From 680d8b761caae57035babb2d897082dd8ae54570 Mon Sep 17 00:00:00 2001 From: Minh Vu Date: Sat, 27 Jun 2026 00:39:19 +0700 Subject: [PATCH 01/20] docs: update SEO agent guidance --- AGENTS.md | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index 09b310a..7e284ff 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -94,6 +94,8 @@ The application includes these important routes: Do not break existing route behavior unless explicitly requested. +Canonical public SEO URLs should use trailing slashes, for example `/content/`, `/content/:topicId/`, `/roadmap/`, `/about-us/`, `/privacy/`, and `/terms/`. Non-trailing variants may continue to work for user convenience, but they should not be used in sitemap URLs, canonical tags, Open Graph URLs, structured data URLs, or internal generated SEO links. + ### Content System The curriculum is Markdown-driven. @@ -309,7 +311,7 @@ Important behavior: * The progress section should be hidden. * The "On This Page" section should not show "Interview Practice". * `/content/:topicId` displays the selected curriculum topic. -* Invalid topic IDs should redirect to the first available topic. +* Invalid topic IDs should render a clear Not Found state with `noindex`, not redirect to the first available topic. * The left panel contains the curriculum navigation. * The main area renders Markdown content. * The right panel contains "On This Page", topic progress, and interview practice navigation when applicable. @@ -562,6 +564,35 @@ When a task affects routing, check: * Navigation links * GitHub Pages or production base path compatibility +## SEO and Pre-rendering Rules + +Follow the SEO plan in `docs/plans/dev-net-core-seo-improvement-plan.md` when working on metadata, routing, sitemap output, pre-rendering, or production deployment behavior. + +Important SEO route rules: + +* `/content/:topicId/` is the primary searchable landing page for each curriculum topic. +* `/practice/:topicId/`, `/simulation/*`, `/bug-report/`, and utility/user-flow routes are `noindex` and excluded from the sitemap by default. +* `/practice/:topicId/` should stay out of the sitemap unless it is later redesigned as a standalone searchable page with unique visible content. +* `/simulation/` and `/changelog/` are optional sitemap routes only if they have enough unique visible source HTML content and are intended to rank. +* Invalid content or practice topic IDs should render Not Found + `noindex`; do not redirect them to another valid topic. +* Do not block `noindex` pages in `robots.txt`; crawlers must be able to read the `noindex` directive. + +Sitemap and static output rules: + +* Every sitemap URL must be absolute, canonical, trailing-slash, indexable, direct-loadable, and production validated. +* A sitemap URL must return `200 OK` on direct production load and must not redirect. +* A sitemap URL must have route-specific source HTML metadata and visible source HTML content. +* A sitemap URL must have matching static/pre-rendered output and must not depend on the GitHub Pages `404.html` SPA fallback. +* Do not include routes in `sitemap.xml` until their production static/pre-rendered output is validated. +* Submit the sitemap to Google Search Console only after the production validation gate passes. +* If `dist/404.html` is generated, it should be a dedicated Not Found + `noindex` document, not a blind copy of `dist/index.html`. + +Build and metadata ownership rules: + +* Markdown frontmatter and the generated curriculum/SEO manifests are the source of truth for content metadata. +* Shared SEO metadata should drive sitemap generation, pre-rendered HTML, React route metadata, Open Graph tags, Twitter/X tags, and JSON-LD. +* SEO postbuild output should preserve Vite-generated hashed assets and deploy only public `dist` output; do not deploy private server/static build outputs such as `dist-server`. + ## GitHub Pages and Domain Notes The production website is: From 6d21ec284980a0bd7f14e05bfff32559aaaa1caa Mon Sep 17 00:00:00 2001 From: Minh Vu Date: Sat, 27 Jun 2026 00:40:30 +0700 Subject: [PATCH 02/20] feat: add basic SEO foundation --- index.html | 40 ++++++++++++++++++++++++++++++++- public/og/default-og-image.png | Bin 0 -> 80367 bytes public/robots.txt | 2 ++ 3 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 public/og/default-og-image.png create mode 100644 public/robots.txt diff --git a/index.html b/index.html index 3da1d83..ad1c2c7 100644 --- a/index.html +++ b/index.html @@ -4,7 +4,45 @@ - dev-net-core + DEV_NET_CORE | Developer Interview Preparation + + + + + + + + + + + + + +
diff --git a/public/og/default-og-image.png b/public/og/default-og-image.png new file mode 100644 index 0000000000000000000000000000000000000000..6ed49e785c1c39b04afa44b36781c573c0615025 GIT binary patch literal 80367 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

KPH?=X*&g+wd%gj~q-A1=h6UDL=uwq+C z8h0go9b-X!_xniJ(VL5*BD-HSLm$JQ0(j%kjkEaRv_uOz`la#2$^_FfYgt%H4;`;( z1haiBdH_VRM9sV?=bZKR)M%JZ9ct?%|I}NV6kVAUQlfL7hAAe;zaOeNP=V0*mvhUVnvdK>hiKi=+Vv&4P&uy zl_Rpz_ti!xr*TaZ5U@bS`t}OOyQni+L3-|J{QdvVn-rKwv6P6O_-}glf6ws3AaBL4 zb)9omF*=cLe1hSy`qN=LjX(A(a>JV#3Re{L>fOhsYusgi$%n;lWWC7^rk95IE!svW zF}qe6IsrRAyA_h9Nt^w1uizX~mT^GfXaxkZ_D*l|-S7IB$Aem6yp`c%faX2ahA6Da zk~;Fv<{lRS8gRj$W$q}|Zr9Vo)`_5ZiMO~_>2Nz(m;-h7?I_9Hzp*=rwi#wF6ykfH zg()_VKL;2YhlvlPg>(zULRU4iawwlXR9gbScv!u0#I}{D(+&Iq1-2q0VbbJAM%`iP z1^WS|PWhC!h2nJyF6qMtyh~X;A*}mwJYd?<^LRV3+efIrUIaA6qkO-1$O2pYh7yOp zfs@{vwM5UZNs)m#b&%h*uvqM9aI@i zvE8SHbu^xmNoY_F{>Kw@SWTs;p`NS)G}891wGJbF;Jx#&kxE}=a+(7xTB;kR{ZJ)XIF|nA&I>o(n!GH@3Y_}0>(~wZ*<;*<~zNg1- z@EmlF7(yFEwl5HuH|)_oVVCc$;P$P%OTkWO5;WE!^52>JQ%4N`B( zd4rt7pDURO^|eXO?lm3NFcw7zg$XY-Tue#v*Rp#(Et%U>0Z}`~m(8 z?XV;d%1v=t{b0c>y)|<%IYWQv$O`T0LT=oLHu~yaaZg?QC+`eL&^o-0e5D7)>;>CM z6=um0e*egdF}^LWLp;p9#PnD=O9)gqt9R*)(yJ*6?qz!Vb@SbZtGFLi>*}@5-LFkU zPR8uDb`%KbA&J#jzAafMbhi^v>f)m5-?zkQ(H_!ht~8c;?9Ruh7K5FoYY%TSC_&#W z$STtWO7R01!SUo0WkEL^-(%dyNN443keZr}#%JE^(Ndp3C<$4n+3-@y?VrS^yMeW8 znh{qlzt@N|Ac8OHBZXDr59Mz2x5sJfNBrRk^rJ zubGytSp85yR~*NZsSE9pfvV-^mtEU%*RY+YdMkSGNzsmS+?3>fltN(%^jsb9K9tTa zU(o*aV?Qmz=Pu|qD8rT|1Y`bVF@q{^z(v;>Z)#W%$O%hyLyOtHV%?^C|nrAo*)Zu*JiGpl2B+pw;9ri+5cWs^4RSt+LjuaOi}D zR6ZB7F>=#`Al;3(iM;+yM(EUk#bz^&t|-632S-*k)PAU;NY`_bemYxDKsY3gB-h$& zrmJt;)v_?ipQ0$rvkxdh0E-iK9t6* zT=bn8Qw`X!XlA!syAR*zd$b^R_?~ZHnTw6{J^GI>=zaZ<6G-ys$iOXwBsdZNgrngR z(ESn*zyDW&O~lGa`~gjcpd0Nw!`RWgWOKAL$T4hWUl_f4de}sMq^}DiuLWk%NAki9aFlnRaqH3xUzvWi4=AY1 zMf1Q$3DY#=XAi50NGLD;-8ZkcEg$*c5x2$Hh8D}^!jxArBbOD1JJwrZJ+D#tSCd;F zodn<1o`)3EGUA)Qy~9ChR2GM~^eD^2TiNcVFwfL-Gr~wgwhv$ZQOZz`O8jd5a%5>5 z%5fR`ly!%c(52A5JEYt?17B`+R)*j@2;wt6UJZ`a;>d{$B8k$o8w>F~;nNNc|Agm)SwoYYr#Soh zC1ZcyN?K!x{S>CVXlT4R|fQlLoKh@3NJ9v=$=NyebxK= z%@a-@nrt%P6wMTkfd<+U(h-;5UoS42*>F7NXId#xQc{_DPI$gns*{~7ZK%Vag&sZQ z@IUz>XZ;)P5edAh(SMpH&hTD}Mr%d{(@3G;tclpQfI>fn;{DFp?$Va!MmitevDsY*SrTRXToKne*EIdEOQ(v2%wl%8)jgxLj z=5~?w9baZB!4-=7p|pE=A3~9FQn=5jX|L6Ae&`>>7{5ELjF+GyP&-7LW2N(%9bh3i z*CkG}!AZ&p&8@(V&qlZ6#4SQg!sYk~JN;8`c_6M{O5BF4rD^*g6F7ZfN3ouX>7q1M zlz&-$Y+oi|(Cjc-sh?<|pRNzQ(n*yfBWr7Ucz7nYw6q8b2^ort?(b1jIgKTJpL-?? z263pxn;cBQlg-_o6eRfdB0E5B`YY{oOv5A4fofdzaA&!kU#XG=?A zOJO0_M5_)mdCRZiF{$79e zM2e~IkqYo@$K)_KNi1DqLlMLcIA!V;4>Q*$HIm2rbex4D#Bx{(rNe>3dag8hS7lkM zDjPmJaB%b!Pb9UnWPVa^?zq3Nr1DMJATSQHfUvZT9HFsQteS zb5Rt1OzmRQ$hp10Y>E`>hTl5!=4xtbEf#}dDyf`~{9O0@q0NJ_lyq0ci6!c$%Z=1U zGn&kIL$a{0^A&v6=Ixz5;7!DAMQap3=rnzhI&eEUow(U${^>D1+C%gF2ilqWI2Pn^ zsSz>!v1|4Qde$Lm{-}m}_EGX~KN=BqEgZ=7>fu_(gMQNHXtw_7OK@kq?@`u|97cER zPybu_<2rn7(^v4zsHhWBpDO)!^Zt=Wgwrx!X?A(?b;HcdC-`f!g=Wu^Hy0P~7OCmu zHWjlOX?7|aD$bhSIeQwm6Iq6>HmjQqY;%>%dfM9gN_*$);#^{It{X`0^2rXc2nkV8 z!dqMYb*qiUvpsjW%cdjVP_K5{Li2hRSy@?I6IbbQ)~&> zFd~_WwB_a3+mOY>>d+|M%xOc+KlrNV4C0XOum^;H#(cp}#77JKKV-zjWp*;kEP*>sVwxy0KZA8VR0KO4jQ0n zjTh`#@rV1_OTL~mYw>Di6}-1=b;Ww25KGbK)BkAS{rKMNTq>D=pH5$&6t?OK&)B)L z)qwk(Ate6T7Yq+=gwEF(TwleT-X1Pu-#ME^#>cli_q5E8?yB8Gh35I~2|SY7X(fVz z&!2VOk2G;qbU$4d5RGSgR5URUoEe7*GL8gD_)Q2&9}LLo9}FSLUP-(La;pG#*lrS5 zxP2EoeG6PRG{s9tXIB#K7>)qOsn9y*I+7*=u7=Lo$d-xK*S{ zwoe8dj%ViAHUIMVPu91Nmnz$sns1#@zy2#Il(I;=q^NY0!6O1;d~cF7)qQ7I9Qv|Q zEi*+DahBO1ZR345Cmds+fLj;T?@;Xg)lwmTU2|wnyUIWt*ho8)W=q$?=HCQ_;@k5~ zd?@9lx20+f1voQWS{8ht&vt-|GW0i|-jIxcNGGUf*ec-eYx^GLz7e!MCsOJlm^lay z+KQ6g_3CPp-Ph?OAB-{TveP-z;rOlgj>uu5>c#TaU-2zu?v{#w1QUevC$Ta-=I+XP zJvT4QpRqD#shu55bf(HbM;}ZFKf?lD>FN6-9K~x&OS8m%yB?1eeaVV@&hv13-J8Ty`%V3O6;m82N`!Z?eLc*8dz74*=Unb5C z3^|LM`|>k6ndhn*A>oBE&NHOsZY8=8;ysekBQqfV<$_+*}!pQjRAp?TYH zjv1DunB6m!|Kp$UmuI+KtYI1Biipe3<$aZ5#0j|$UPR{anbGQH^~EFqi>|;iX4Iia z%Ztm7j>mJl%pbiG{PW{lp~fwf4<;a$LS2YUHzeaNMM=XuC6y|kzz0ZYfETv%A!Fh} z(Oz!Q*6bdwmBvz3e0=8P^t{OFX9XSw=vyoHBrra=Am?ngOu`XsA45TpEYW(*yLY9y zeQU^a$BzvZD%Ah~Mn6Y*F8{Ww7S+jj~Q`NSJfIh_@3LLTTD|0%J57{Y4IV z_rgpAPtz-_6s3$^A)Z3;zNE$(10&C(YKDx((^lPn?Koq-+#QmQFYyS=%riVF*R8qz z+l{~>r?$wWs^{|2&9lTs?yr`y)=Ai4eXU4S7)<@nUM4<2|Bj}#j9lKr(h_`kdbR9I zPMsp~g{5I^2u~JBnKf^zKu>P>p?|`oiFKWwB=ifyMBUXp z7aYn8r2NF-2j%HIo=fYM1uclgjorS^pRUfp>+?mz_>g+B*ny9l-@?^dk>_H*V(R8? z_ZJUUbJCnC&pl-`qq2J?I8pqvZRG1pQ!Q?W2JIBr7WCG<8_N=IuG2nsA7Eh_?hNqR z``lBYKNmfbBW}7;J4*YGO9Fu`E&XOj0SFfCB$YZG7yGSD*Tb=7m4P5%oJ72&2O^e2 z13GDZ+6fnQlEGWWI}oErlE*=#d97Ev^qILah@b~%7BHgsU3*^@52d%d`Ikctx+$5L z_s8?Ua988bjCQ}_OE*@y=N?z3adWLNS8JM(&Gq*kHk}HtjotA~LHjYch4}_j&z|P` zZO;oA_)E!hCc(jp#ccy3xe}nbAQ}oU$^D}Q>Nb$Ir*_ye7F^+Og)g4V zv^GPVEJ>lTqfPLPUZOISxv@fHCwzqD>g!?|(-)GV zCS&kV(4u}LKM{>5Lx%W}{H7x5BGXh>QC6-t85j53A6oJzN5H#TG$i33@g|`nCd+%P zFbOgD&7``)B_|AM@Ka=l?+I#BIGBAL!j<6f?&Ng5E{*g_{kt0Dbm@;?lHF_eNljVZ z459Yj2RvD;2E*g<*!fhlMm?9di-7x@K(U>n$t_FQ)~kWcGrd}es>j(SG#twN*nKao|Ek_rtzE9eSU08B75UCu|D zE-fZ|G7ZZo_b4c~keub;_E)}=>SUm4XIX_f%P6$ngf}NkVJf|UiweufIUNTed+uwa ziu!^WucmF}iZLA4?|^3ex4V5!#7ae!G3p=CguM^1nR0_y8{uD=asw`oTdy#4{?%Du zMNoAxWsP(f7FtP+I&P8oS;UTTu2mb0SX&j2cC^&hdE93C<4qCe1|RYa$C@l)M6ArP zre)V~&~Aj=CLgQ)w*?S?3+Ae!h3^l%$)7vyAB}qzS1%!Rl&V6vr?Z_v@*jJBe+$kD zxJ~}~^DXM8xQEl-zM)Rd>Yt1`J`Hg)a&r8YfM(EOHP4odq)&``4eQMs}+xt*y2&H2inCyt>SP8ViQLpsI4?T0UMLU9W+@O+10VhAdP2@2^!> zj=7cNsMad9`=0$wF_2Zm;G;CHb+~D}BIZ>1&|8^mytnHvwBEtQ|Nh@O%H@79ta$E& zdFRpc8NwIQt3gDZTB^HV18%}5_rBWF==8gBs2(%S0YC7}eUf-SKH}=W*ZLaJ-_Ovz zdh@U8q&@9)MqxJ3I8%oslTRFclW~K1UgzsSOioEzb3dc=_rF*5U6r`7nd1mD7}_jiOAG1i z8yNT&$kw~29gRWes+$*b`HD*ym`NqfP7Qdt{|!3^(?PWh~7y_++SDd$F$E>5{(-Bs-oj#;mVNc#x^<3 zu8`Iwi-QXkc`Avsg+UoH{NPLzPDZR#J*ob)72--wK%g@+Q_{_9s=)`o!--z%&Hf z1|T7spqZ$}9z2(kTFgrhW*8U0&%E#}yaP?E`1I-@jjpUB!?T@C9>9DDo<=#T{}HrlHNY;M{Jbr`AgH*C@&FOZVbaH=Wd7tUQIDXi@!D2+-SJUIp2U+3Ch zK-G3h2gQ|f!oV;V^-W5(#tygtd|oy!yun>B#s0c0e20(L++3!ou1NFFBH`Tf)p81; zf_J}tnag*e^e;5p#w*nWCxv)BH|NTDxOTjVso6qlgxTSoRUei~pvjkqHSFU15F-pw z2{vuk-}fuo=sC_sM<2#S8Xdil7vcmkim&!<#~#Jm$yaqv5hQMhq{PJVJ4>7}m2u2; zVatWIW{nyuCui_8HL-qA;?RI3>cLVG9eK|7lHI_0M}%$<_KrlNmx??CwGQUPux{Ao8Le*z|x!3(3K=(NQx zhwu4*3i(`dd_-ltPVJ73Ly7 zn{T#3laG&&kghwyb!+^A_$uRyVrd)Rjrb6AdVne7Fyyyxvu^v@G~ns;Bg<&DqG@zp z2t^THd34j7Exp=xx=r2R>WR}YSK*5@{(vLopjw>E^`HMSE z*4VIP0@iu$T*`1jJ&Ffc9(jWGM*B<3n<6Cz#2Hf3iH~7c6!&68loPN*t;5NBj z-}LU(urGA%oNY0we{;NoZs6P< z!D8_tWVMnhrk7?M()<02#AvF`2j$`e)$L`SX9W>HKvOzJDam<;qgWl5?AC5E0clRB zJGg0#DSdo5g(DMjzh_EOpKDj2E!>zpTF?dG*wZxuMIWSmu9!DzapRdqtoYNGnRFUJ zCB%ujl8-)7jMtfX{a0D8d{S0Xk-WQ$hCxBH4|O~R5#Wni7#xR+*Gb{6a}yua?PXcD z_>~X4{Q&d}5(0)924{*K0U&5<9;ib0a{MVoi7y6X zvOB&EDUwC_iN=NCbJy!su@@iVP)d}ClSSZATefu-uT0zB&-vRyo5sh=5EY+4R!@3( zOsGFtv-)5xq}z#gBQ5&A9Ob%<7keS7OG|_Mtun2kz_H*{RLHP89J zRzAk@Oz|S~(16R_^UVrZA|fJrP3RXrI|{~6zk>(}IH*xt=`kXVf~gE>M_Jd_xg031jm)t&(XWYaf$Tt+i8u~;$j<3k_zDx~tqeRl6$ zx*(4LMAbtoRq&C-6OR5sx@YNuP4`>uW64gz=Vs)gjKTUZFYv_LC}6l`0Sn0{Tmk;e zDL=1lF=&g^=1%qQ>n7C%G6Gy+Y5W{vLD@11}mfq zf^Hpa#F(x&c?uMfk$V7qIh-q;EebZrKD+xmK%jiEdf%AI-0v#0uHF^xYwCL z8!Bz-dg+Ii$a7#6|Y@LOzj9=&jiVMdV5h)@+S4lrM<;T*=`c%rZ-Z)G$C^a-g+CccxK2S*TqluQ9Z+L;-WOx&^~Cxjyfj^7JqK zA*;{|L_K6S#m7}|mV%B!Adjl+>*rd<#A%|8t%E&7ax^8ymQpijP-i?TlZPFZDS zVb{dH_G`R0x^@tq?v?c#nb*bC&aC4c>}2}mL_cGq{dFmxDd?(T0p-7qh>DYt`w)}v z$)Jk4M24?dkzcQ9JvD-yGkbFalXw8l5gdpDvILLQkT(_)ht|r67gE&+Av(r?OL#n3 zROjO4GBHtk;neDG{itEqJ3A9bAFGy$g|bK4SQ|7OJ36wGJFQFckQ1v7yAK+LpwN2X z?udXa9Nd4_=h{ycx%a5p>uXL7Og)PD6;WbliK5|cjXWF1^r zB`WT7JSjy{-M#Dy+DZr&SS!}f9hrLT8odm{n)U4GXkkR2+3JovuY7_0;(NHZXf4tU zO;K+R0gl?*1Mz?v3hwpuB$m`B=jk4TslCZPYKa8!HVthbLG}w9HlE*~9G4n;4P7(x z^O3=gsj1Fwh}|ApLrN69k0nT{_xgjr&|%S_yX&bUlsL7knG0}|rf5+Ta5=m|TX(AH z-hJCeZxTE~g{L`kO4Js-pJ%05>lsFayx(%}cM$lI+;_>$C3RGD&3la8&(~1<1Nc>9%a;S&{ED zsXX3ikXh;vaz16*+>1Z&$6CF2*?iiwXIFS<{y_=>)yQ$)`QZVMs5YsWc5mc=RiUZ> z4cn+Jj9Nxm$co{L8tMDPb4gDo7proV^`cr|)LhHmWxmX0uD7|&rq+cU4z1^tdZ}^A z$tYKm@6o9q%x}9H%CF4UX($wFx_Ts!eM%LhrjqzeT4tz8H@D*&#yDu}Y2KN-dhe|c02~9)691)3aF6@ zErHj6nZ1l56%TnSXFC!5C+-@cLQdzKONQxp*c}#rJui?~BfJmC&`Aw@+HI$1j2L3F zwN<^m^!4@K`2Li8dVM0O5YpJSOt0o*71Tt4MfS$FG`d<`dUKCwzbP1b<^@1W7gMfp zA*gTeWOhK~d}A?)K;#u!X5DfD#~+iOwp$p@^Hlu&6{eE)18fKQe^S~k)KAX-3;Uc4iA5jVh1!zMZM%28Rzwqjw5mQ zgozy!(l$3Y-S}SO%6zc5Kj`p}-iFf4fuzutl$DQ}mZwSX2>O1D84(QSbi(%@ySUfG zH6)%j3AVxr?YUR58Geh$c+(jL$(%P2|30jo+&x*)YZ518U?>_W!xpILEkQhNU7LcP zpiKg)Q3TIwicrzs>9}8ZDzwapBhp&kICVAGW-?MVubJH&9faDha!k4>_8(_`hdB%~ zp61|7(Vn9dC(Wtb7izizop!^me5uU{dGwc#uk~Wdwvm2~V{y?7Ix*kE`#H#)x*w9@ z7LEq8*z;Yl`sOQ>XWLD47qON)9sq3D7TVvlQrPRRlRoT6q2uCWqSxq#Q=n!k3^xY= zV+S!vSTdeOV!|Zm{NK;tdE3hV=Po?P`^8+tFxV5OT729Pqhov$}9C%PnI7X& zn99lTsESlP@cWZN(C&3H+Q2efmZMqau(#SIhKQYYT-?O*vSht0qHd+aR)>95zhi$S z?&N0`tK6Il^GU_gOj*Cz6Hw6v5kf{Gu2T)6c&aV_qtL;)+@Wei#fg3aM;q_1rmX?B zOpq$Xi|VO?w4N>z6@zwABkOTz{-9Z{MgJjgkgDr!zKjnG6BGMqXvgCta2(pBTd zgIqh0MQ|i+<+}$DOx1VI8&m^d(`RuCLdO<98yQKAlE$r;osiWeg?7kXS zZLR)1_UYYZ$pPpyIsZI|6)9Ae7qXyjNN zC1sdQzuy-Y%l7X11f(R3v2lk zqSIUrmtj^`GT1$+_=dNR_|7P?Jq!lP5#x=F>OXuI~W z(>>p%qf%b>>q4PEM=gDFWkU*0VPT5YZLmf}AXgUlSA1?l;g!xnuIU?N5#Qf>V*n2= zJ$CYp6p;oLti^;dk%TOxPR`MkuYfS+x%Zxnbo{>?D!(4CQNQR^&oDDKX17}2|C<&m z@aA}=jqki@@43Flg|uHu`HTyMwUdkUZ_pttB|aBiCgjqQXZJ6|PQ_(bc{3!7Vd8h#z>3tuX6@ixa(-O+P0xUU=vwgK&wOfg zGYSf#a+3#~!B68a!R)SZuxp9;36+s=ZSi#954vyM#@)VWT|c?s`^Ze$O&9YWACYfK ze;Lmda-M3*8zYaW5PzPBF1rc-%*=!VQ9f(=+kZYJTbteD*Re^p5;Sw7rx)3*1AY%wLZy{}tR{nY5ND6}?nGN=UF<8<;s z$YOZcW0zV5q;kGRw%}qDZ};G#4NDsXs)GU+)1$VpjS*423X=!;UF&-WPH0;ys;k_qfPUnl6`dNd9*L-9R2JFo%yeS7MYT#$W+fZo7J$NW$?fEHy zfswIoJ%KiB%q!)4@y0Dxk6{=5Vh><-l}+&}DWA5taKhhTt$V4kO##H#a$_L%G>1?e zDXHomipRmFb+L08z;OVw<3`2ApXbhWTA~h*yvF0T`m+aerfP%E9PAuJf$sa_$?q%y zjq+A9X!vz!`+Ow$3dpz_1qJn}Zw~J+Y>|j+>@JR@XAK(#>zWRJ9?3&o^^CRjq|kW= zb@-M@t(%UIi4XxY$3Wj_ek^R|Q@|pr6*Mc&T)h1rS1V%BT$OMlb{CdGxK(*me_W+;#MEI(-?jj(NR((}|O zDq01|Y@=UNW64F!gP)Fvcy$-l8}54hMrM!Fs;MQe+sKwEwrLc;JPX?hYh~lgD7>cZ z(`qa&EtN)k;9ad-Gc`ZC|J~BEeTqh98~gjAhc6x|tndi3MJQtUx432P`0`jPqfvh@ zH#lECWFnaGLk*A*+o0Rq zhZKGe z@4t|6^bRGEro%p=d(3p`wwhH?aQ83#Y$p8$$ZD9kiKe&r+v{x-WBY}Kud@}piC~bl z`NlUj!w_v(EUf5L&^bo&=fA_lvuCaN1*7{bB0n&^tz=MiQ7wATk!1`+QDaZLN|q6f z&7i9jgI)!_&G$7b?84yNb8xqTKruAmMa>g-vU|5Z6Gu7r7LLp;Uhnm_U?SZz@wN4p z4UV|g=mZh`X5SSd&I1>+rEHKp!u?>gynMz^?b+KjpWE4DjkM8ul4lT|!ja?e@`rj< z1a#>?euSFo?|Sl2TEY*(F_#!uqLyp9H@+}HaX44G>UR2wl1}Uk*b4x6RzZ3kb(VxE z|9HvgOpcmTKSVFY`gJ4jw?+=_;?GOGr)wazH`Kx-UZbnND@pb6ZecNKG$TpS$9 zV9=*eGoY+Ev;R(YF*l*iriyt!T2fPCzx|;S)1X1GQ6$(ZCuJ2TN;-E~I_@2xx!y~2 zCmWky(X+$H+2(AC`;Lj5f`UY0E_9a8po1YU$;wv$^PY*=rq0`$qHZB~ChUWo{M05F z&Q`ZLT#G0rdPXF9k>UNt`U&(&4ENJ6<7HFO)aNJFxdsKZIl2z#gJd>EM#S%jftOhA z{A(9ri1AuexMzh>=5(94D|}8ADW%d5P}It@+oircS%ELYOHhsHa}QFYqDH8#vT$*! zLXE7gcPbY>aOjc0WV({^c@0UW&l3};hOLhnjz`=tdhSMOnMoB&;}Oj8`buh~4|sLX zIy~(^303J-T#A*2uH@%$qJY^g+jdTuUB@@^j1P**tGkPp=!y-C(E^j6>Q|oNYMSB;>*8`v`@a**v8$6TId;8wg0*KsbR;+l}Ef< zZiCC_5K8u;D`;a900eiH^2jO##62(Z3w?hq37nd6Ih5Yz*B;nc<@g&A&|_i=in}>l zm+>h_2o&Y8D0e4674Q4W{JL|R_)9He=np!RU(GxTINPacZ@yCNe@m?EIP}|55>bxG zXBf}*6DhOj+q%_$0TGAy_2R6J25e=uUO}>iRo*G2W% zebogFgHqFBE1dUzr~-7fvB!{C#fD3b_BRumTr3-ZqyYJB>neT?+iwvJehydK0Hk&t z#1oj+c!!kqpr-Ts^)yF``X0L>FzHU9tgvu<3({rYwv!{uMBg`f^cnJU*~JfL{rdhD z5+CjNV8R|LJK&-n&v*BtQDf_MH0)q;F*e(e`Vv~moBxdz{wTBp+IqZQ1WSSttpcMf z8$8*J{D$L^2U_Xl`Hqb*sS-YRT91}G%ocuetG)VzvE@XAjNjnDAs_h>4dH*f5mGR5 zvTkEoE-XEdFHtFQs5tLpsa8g=+fGHR>2-MbTq9v5(QvdW;d~o(2lhw1y;S2ADN^V( z9Dt==tDKS6ldpPf80o0V3Zw%W2?~ZESjMkF0xaF#FGSBk1X@VT04nx9`5CyG54pIW zdh2i@e?vZ>%5K=Yb5`{OOL9XnbSf~)NK^0bYP+n`H(z#dCH2)?CrE&9r#Z@g%dP8a0eR=qYVRX407OYUS#>fTWs+KsADmODA`@j>!fLJyRCR1*+i+tH_?DA18mnFTB$mT zF(BMt^W2h|Xc(Q7;b(Q}%e1HFOJwvZOQ6*Smo;Y5p{dAL!>@Fbj;k0(?h+M87e+uK z-U6HUO?`9x+siBhl-sH6c8v>_!>>CM@L$jC>gL*ic+&88B_U5H*v8J)iMHMh%havW zd++z}1qz@x^*0N%<4yHie#k8T`}YFSw+-8a@uz;JggW01NP@fV&X*B2QZdk=->Hrua; z98?3QlgGaO;ZJ(juhQE9_&4erY{kB^Yhyw$IB6{9LC!^l$c~|i)4JrYLL@;KV@DFy5OrqrPI#h|wv>u!e z#CJKU4bN@9{u6V$N*^p>7Bt!S4VxV59+8JyJWyXPgtJ>Z^9?!V{#B2Kit_0U zonXC|)3{^hEU&5-Pgp_+QlKyRd|*?eDi!A>VErgQN}X~q)9yy6%1{E<^QC`OH26+w z?#tKWQoC$V<7V{}u=w-8ft%5_z`YwXfut5&-_w<+{#0nzs6?3rZR>f8-;fGNJruoauD`>FpuxPjzT?D(~`+CDv^^x}pxKphkBTT{1KF`Od zOU>4z^A7#xjgE89R-f;7_HrK~qBVSZ>Q4dQ;9QWpp@e_8vEl)z0c2C#Hg8oECz$n=wL96Xv)PEG2%qpZ zRd$)Kojd1g{1y{8UOCJquMz+;IS>#0OhA;QU;B8SkCOVl)C2qU`o;ICC_r3SQYy?i;XS%&yVBH{!t*Plz;qu>KjyZd4%3MmB_3C z?v1t1HcKfjU~=N+^7SHJ1kUMfB5B~^JS690Spx{?<1QRgWzwEkr#(0ND`3$Z75mut zYGKmhRZ7?6gQ&``Mtq!2pz-_XUGjjN%nRx2!+2+Z8JSB(K%75E{5Au}|0 zA_|_CWUXswy$?TOV_~`9ZAWz7t>9l$->!pvA6+kJ%wjBPvLbMuSNPLl5rX*I_lsyU zrDBf$ru5#?D>JcJVLF){^`?l7P_&W;N66FnR|fk27Z7jYrkCurQ(v5AlR@QAw|qNR zt2B4#kc9i{*9{K_>CX%KXvnV7+Jz7;a%C+lZH41$rx2~77sfsmBBts0_&w3g*?#wq z07=m1x16Cude@Q*=}mb?~Epx>F^&_7iLmN)^0* zT1eko(E#9Hz^Ptj1}=+PquCg{Sp6|YPJ@qW74qXcW*}_Ml>804nvSDFu7m&@j?&p$ zZ{OQ;>0Ledi<;Py-SPL0ZcXQ3FJBaw`l&eO3lAu$+AyJMi?h~Vf5it51h-OiT$q_M zpt20a03h|BhG`Cv)AWC7T}UsA49z5{rr5dt$m^u9siV);NikjNc;4K-SvCcWG<;4!kl_(}LfL=i=1=w}g| zr{~o+M68g(t7jP5KNztF&wWkG|IdO4pP-WC*aOfQ;8Xz~OG#%1N$Ru?h4fJFfIjR3 z#eJL3GjPUAyX`#Sfn=Wc>EWeuoQIT=*iha-m%CN2S&|w)iHb{->4de<3a;E3{bW#up z$$Hn)pjW$aDAcl>r}!{#_{sK~WNYECOLO=66#2)J@u+iQ7k&N36)3TXzlFbi|8Xmp z4D*?E5}jhaqT8x0*)|Q>$Yix1=c`bPli_(16zUG*@?~2q=zX=cdLH zAcR_fC2XAMEHygBkqS^`R=hM23oF2ry5(RGM0{yiLNc;vr%^pyU3RE<4_coU7xn3c zjpUz>>%<*d#fh!h$S%S{j9`p+NE_7OV#7FX0gaO6qHi z|JZ5w^mL2z=lg$hzD7EZu>oe0g29eNb|iEE#p9DQFc8WB5R=V#r=z0kD^ebBwmbg= zCvK31#BPlWX7=?GJd(YbC;)_uo(1oBBjDj-)#q3#;@KZ7Cjp;Nszon_g7l`3ksA7@N~}=9F6c=M9iW+=h0#bOHznG; zUOgsK###PB`j=@ZJJz--oNma4tJmvQCB)-p>@06Gn>;uB%8mwOa5j?jJ9cWin<<4o zRG&PHfhzB=gUl{VBJg%agZ4@JRY@3Xg1#rQYRlEjx#>fJb*WZO@6*TdA3~}N?Pq3g zC$~_g?x&-JREqXbR!F&BR_NQAX9!*G9$a_>KnLLUqMA?3Grs1Ly|R^=Z12TH$BLs` zg57tgn#{W&e7Oyj^dAIK1$<87;7Pe7(;8l!D>P78mx|DH7K|)-M5h&2$=aJl10eyk z->P>$YD7N;c$_vQdX@+avx=-4bgI9f^^1@p@c2S(FUR(?R#ghimF4&pns7dGA)@)pN@P`S*byo6(e-9u|qjpojkE zznLQiVZ&A~vxhfygR1X+9t-ovm^So9eNP`t$(bsb&uulm>X%Q>p7y4RO@gNpUKEQJ z^4>i?Sj1dUyvfey_m((Na_x=i9~iQuuMFN}cmqo4i=O;xrZ+)X7nkO%L@@hKxMa=V z33O}l>tsqxQt6?0!B2`&L=2%aQ*x??_$52Osd#Z+x#}N1Cwf}s!v;Lh&i}ic1WMCLmF1SLzxFLb7>p3sW zy0o)csX!VDZqBE`_?m@r#&yFE9myP!Tq1%WEoS$-qT2*k?XHO?@ZvEJFyo0NKAWiKoRM^B-6{A?B=jZbV!d#vg3OJr`{48Y45~)5KG*^on z`PDKvE;ow>H;XPs2{0@t7@`Tokbxtnf`d+^wQDxTK1!YD1&|&FZBl!R}RTMj~tY-dt>72{aHF|@Bf5AQ+aNce)6Z;gSji{@o;reOQoFRddX z7ag3=HYMP!tsE7i0&kl*4G)D$H+|OvNQ|$)uP^(5V(lqEs>gH7_YV}#)e0P&InMAf zR&l0PDNKX|gto$9oVM}W+wsllS2L8tb!RisrOsQ2+fprW#kk{#`@M@sM+_6kxHy;l zv1toA0HWh?xtQ#A@JjS2l)Q16fBZ|3ZPsY5z%<{y1VH)=HtZgPRR;0ZR}!}@8Qh^88>&Q zFE#LZ0L`V(IyzLMJ{I$*;$TFl^Xh|IAdHfRxlZ)oJr;5I)m#No>0#zfu&LWyy%|7c zJ}!e`YbF&oL^>ZU=?^~_CrxDVfp1q(#M`{PPUhT^9$-xdE#o0TQ-h@I#-?d-W-H_XLVN-Zp>{*=`lWyVbac(Yi*#Lxf8?A^V{TX zQI8bP&N@)w!A^p{H9e*2_2t}n938$Ik5ZT^I)$f~S8BYZ`Vi53E{Os~U(cALx;{ta zVC}7e8dc1DT0Ohi0yjnpR=0pA>zctA%-2gQp|E)mhHEbQzC zEq22sx1r@HKJXa*E4Ta_y^+yT#MQp7tecD08Q^_hR)v6jA?bYD3Bh=f@CQEor ziylh62#~MK1&$DUbsjfMHw+9LZgryoqbTaohkKv3QVYs-F&1Glp@@FFs5+#f@sHu+ zMYG@2YYcBu;5iKBeN&yqYR$8*zbH_mZyFNSJ_ zNH;JgkFh>oTlvL{UlZe7l+Pb;uBFU(fJ0kM85x~e zv{VoRZ{yXP{>Kt!HG)G02Gm4XyPsKWwdnpvH+P-y|1b@9>oWc@QunFGdQ8Cxky4wa zDKi{tTB50pQ52>PL)#=7U|~plpBZsQu03l|3cg7Hx!edL^E0-AK5NaEm+5+(RP|2O z7}~3q+t&@aXMDSN_7^0<4H=eu>RCJxbqxRh8;3aY>%}n=qw+~c@t>ulkD>x^G|HNA z&Z{B8w-^pZw+$K!0R~4pC_It%Nu@tuou;d#s;_XhS~)1iLy)6FAP2R&zd`CU30MUguuwI8NppL=(lH zwIfC*C`)!BqU{#?i?bDf?Q&ig23!%n<^Le|CTl(LmAQ;_84~Sb43)3Nnvr8U-yhYd zTA`@P^>k~jRepddZ2nY1%G(%L&TRB$ThO^4)hKnb8|+ml*v{}*N&~}D8b8lYU;Krk z;?O>To9^pXAY3MG+Q)g{cq3L_pv?%g{pwp}D4rfUn$|vt{OgPUM7$rqc;d+%6xQSI zj97}#KDsR__83`ErM)ex(mnHxK)ekk0WfXf2<-$$DF*DPeM|YO~eo|KAaY3 zXEkT3o{cbSR?MguvxZG$hhSqH#7iD~`;NpSV#Jc3*CBHLo88%8j7a==Hj-10$=G&VFYT6Y++l^Ei%6L0uC$1?)Ppz!>`O8Q2Yj;*#=Wez(GW#6M zbXD1$ig5AL@1AUC569!XMKblwk$K$@-MQJlq5?11glu5Q2Hb6)lmc4271oV~an%WZ z1Io1s(-dB_k4DN!GFrv)EI~nMMEFvdF{FcH->v&E>-cp~kAZKtZ)MESblfTd2s`iVRE_F)UT-Cj)z`-39T7YHkfH%n5e&Re zY#5>Gj@0hxo2@VayK=ry({mQ#g9Yx-b9imlvyTyG5Zd{_@pz;SooqH};JLpUIpY5_ zUmZsjZj9KUNT>@G20+kK(g^z8swJ27%IFkd^(VLV^^L@5%Avuq7q+pm&x`DDNnX&V zVbQjn*A#oF$t5RDo_A8bPqT<0<&vK@`OG;~#x|D!rjVGgytDcka6!}ZxO|;_A7r2xb~V_!5@vM2Swi= zazb1NVuBffRL@sjQHC8p_st&l#GE&3)xR{jA17BHF_ORhqEG58S|0gzLhqAQcxRtA*i?qPJKMS zgR$CL6{0X06eF1#rT|)~83Pm3PdO&N(Nvp6vp1wb7K%`pF6&S3ANjv91-ZXaHIr0l zax#N$M+bzO-M^ z+w=TV-I0?e);eeKPIASczV*oZysg#NH;mS(^g7D`E7!P>$K0|&o>y5Ns}?;h7y*?X zkY>p7eBx1M#*HOoI5%JvboL*bLev*94F*wIm($zO3CDp_sxFlz0wzCTAeGqoD9^Sv$Ca<1a^$=coHzpYYJ87n43yB*Na+QDq5g>VJp4iBKxgr ztVaB^ztOxL0Dw@tBw|_NOYDw+x9}st?{DcjPf`;7-OaR!ug}Gt@k%9hx$&xkSfdSA z>Gn9r`#h?;kI_Ym=eJB0h<{b>><3xIMAkacAay4j{T{;MztMLGD8%0c9tf+2-r)$L^klAd8uqyfR&zuhr@0^Rp^hbR3gH zoyA3&`VF;9y}0@W=#!T~4RnIdjZ*Q8O7Z823hRg8X|n}fUZiXSp0_)hC5(G2>;j;l zn89XR?aV?`gx_<`UC?8KJ@AucNIy+Aekd81-RMyt^~S3wnutE*M50_z8$~fxs^oD&S~A+ zDRY05yr?%-v~(({NQ@_m`Az>`8I(j{$hbdICGM2j$6H7|;T(7>ZzIZW$UfvjTfxfG z;de3HPQtjS)Y0xyXLcNq{U(2~{3Rp*mv@BaWT~#Bj(wZ|fyVH5C3%`;>VMHkDh`lm z;E`o~l#A?W|7((Ns;{F5x|`kvcL_dR@n3ixFSHCC$Ix zx%W)X?iOiBIH zf{OdVGrQ8#Fk&nH{bNXDUuwSN?=OZsi%)GlzxZc<%MJAg2Z>^Ge?GXeIiP##RRMFw z$2PVN4+UA)4gfZkNa57#dy>`NO>JJ!w(L#7<@lQFS_u#g0&oF%a;4m(KJ-r%y|JpV zl^myj!XS6NtPN1X`n!Z}b?#w#ILvKdJn}2P&{618ZXWO zcuWcBNhK+EW9ibmDepQm>94Zz%4k7Zsr+5n4?~HKj(KH_8F_hFAEhNska1FizXE|O zT3bdi8}sK*z3bue3cz@H+O1s!sf|#xHHV1Cf97NU?Qhak{U@&`|1f<++I`zG#81dA zm3V(b>-0_mO-Y6u^Hse6CnZa?``hB*LH-|&FG$M#Z8g~?#zI~uva;miegShNh)J+} zOXPp+A!n&&hn)nztej^vj6Xjx=|i>F ziHO=QGMN;8arX?#Y=jG!t&N)QRHgMxim>0%)7M@TPA!uXo0@1*3+HpE#d5!$xnKUN z$IW6OII81jR4XW!rjw|ZytATs#TtRFI+_MDAD=x)oL#1MDtCKqKB`!8Q{w8#AjCT& zF`Zo}oIh$9;iMq=-Ce*|2W7Ekx608e_E5+6;Qli#Vq*KhBcuB{pi zUneY?#LKdiGxRox&bqOtEV2$`0FzBD0EA=ifJS8D zB`UBfK|u4$T#S}_TP}o2?xh}SZXzRlPn4aLlA^%EN6+K5ye*}v3mvmkat(p;(<&621vJP_GD)2)OqxqcmY2K)i5pqEUJ;Qy$WK>3`RWq#?s`-%p-L>o!b zQL1Y>89*gu!hFTVv^-lrDV#7FoE0QMqw!v!4GTC8OAUP;^~4vyhil7H#@S5Wt?%9` z`bmyS&;k)q-3FEi5?_(?9mQ1%jNX6;rRfO9DLUT-F z{vuuzjb$tRx`aePFVDzDTGwAcI}jX-znL*|^_A1fKo-C-iOfQjZ8)&n-IjeUn!+Nu zFEL$e{`Odn{NCaxbOyUyeRzY>*{2+SvZ`>`pG2L^k0=n=V24m#XLe9 zp2L8jke#$x?x<16R}_fKQd7V%3ZA_eC+(#fUr|?!O;fKnQpZbGt1f+JPhUEyjTvg5 z$>j5m{~L2_qg)_wZx$1WiRtgF08Mc~U)tqEqU1t=sHC5F!)1&yHt;`?pS2@@wP z?(vp>?0dfW+li5?AxZ77f-Dh#q2?T+hKg*CV_9QTs&S72Ueq+9In}YTKZ+@cTbI-rGT^V))!U? z)qO!Op&rbi_999K2vhXlwF>x;XLz9w??em7yO`+Fv{fX1 zodm}w#3`^N zF#1hhhL~Q<{uiX3Kg>t4{Hs!KY_Gh=EUFuQvfYmvCZgKc779a`$c*@6#Z}J+oWN=g zB|Z9!*VXSnofFb5mW2IDVkm#7;UD#jO2iA7(yg4dPz$+9kQD#%Csg9JPhTccNU?&& zE;14=U%(&-_@N%oH52_Pf#rn-r_cY213ow5Rp_qynAnUQd`z%UV`@uy-)hZTZYM+X z>ETxlXn?36miJHR_!)Gdn@Lq7+`IVG2Wc!SuGi?BNLBrn8xz{2Q#$uRYoT09X&cwJ z=b{vy+E9szN54f(N`Zg&L(_*%fDP#1-Tn9z^6Lz&RX*Yp)CZ_eTQuQIe)Sus<6pcd zYIhpM&C8J+uD}%+u-%=OXHALj^v|=F7kO{H8xOpT?DUsh{J2YI8pvha4}6gP!Fe6W zKCU>a6X7s@DbB!-%J*q&lzWPg3)&u04K!&y*th!C)2+s|XrSI>I#d8_;`(l}q`o!jmGyY({pPZR&< zNDvZ+OcYE?F)h0aWQA&f!pue;V+w&BE+>|nQ~;H6GU2_05AOJ+l23_#fxZgE5Srp} zGwSe&=zxQylvsXv_jvAAPGruDs4SqHV`N#w_W;?=AolWgHYVZdYT3ZWx`%^eJr;sqah&obInz2 zbIiwP1^5QSs8l@qRW-a8EDY%%spqp;;go&+tHr1ub%hvJplo5PB{H+w^Io5fGcQ@c=-c*}eMuXtbboX2)AhjMt*qtlzJPo* z@N@t}n&a3@(s_r|eeV>YNvwmol*^5W6QU>wc4iKu@xR0YBxxt3a$oI^7-x)*UbUGR zbqpeKr~sX%8wf7)$Y0DpeTJ|Ja`;{ve*CNvI?MHMEzC8wd0WvzQX*9`(cy;ykZ(F! zLi4t2;xz`ml5%TvWwLOpTi?eY$|Jf!^-aM;25kWlG;%aUoybJt$KzkQxlLpVb@;&2 zd4Cb5BqyF9o}pEqLS{I_8!i)m0$(WR^07>X;(@!_btWRov(0(i=PHBqn9oiQGwu2= zMbUaJ&iiYdlQ9YwSoK=+Yn73Y!wEaGFXnlJLuHobL3j}=a~p?bNze2ABsW3P~dC0^R34yL!Q+G z*Qe!2*;gD=2A+FoKbTL)Kolm;nb_k4G_Vm=bXmrl<3Qzc*w9)DH%u#RK7wlKpUf z&-@>uu}#_DAXK#RW!a<6OrR^t=^Ai#=7-0jygRMQ6wA7b@}BdXT}Tt;ylzI&2Y>HudoGuhehj}ioVLj5u+8I+lV=d1L|y2KoFzkv7F%Vyodik!iFRrP-j1=@^XaEEa(B5fKiMex- zdq})1e2Pi)P3a3JX+a_y=1dONYtms+Im{9!Rxdxfkzvb#lw8RltM1!TUhB3s24$V4 zJDKiH|2;YfoQn-o?<6N)*8?xa!iPg1zk}K(HxzuxMxshuYG7@{9vkf}^15xe`<|kF zE1C50kav1U_~wMmZYy-c#CQ8Pw>KEKiJo2*fnZ&zciKh%1}P}O5)hqvtp`99Vr>(Gi;SWH0k8CDTXWX{h< zmt)BWSG^Y~@RJgLT~l%&%!fe%8&G0bWNT}T0O~j^zMJ(VT>)33VQV>ortL!? zBXY2xXaA{R&&I!{?mOZqsRg+{jEOz$)#N*anez5kf7y)Vd(#_qApROUABtMW7C909 zqG#aXV`r^%1DhmqLS?OPn`ZN{h7GrpFX!dpKXZO8fwc; z?8BFjulc?Yx(FXNV!^x4GbfA&HK*;ZJ1*}SVd)=kP|OS*&^uhzX}Fyjzu4p23bSpA z-E~a(FMnh&oG!szQ7Bv;=xs*b**vj4pTYI#ZvK3SJ?PhJk}#m}VfPC{mJ&PJsA$74 z#UhWWi@|UtGuyMQ5xGC}0=??Q`E<_AB82$u3r)X0kiy3$5kMenyw*1brC^z9QGw^k zZvLO1xA_cfAk@{w)7m51X?-%6$DbKzUAlQ+zs8w_RJ9!s4T{=?iB%~gUXtZ;hpQ6| zj8qSVahJQs^UX$@W(&I=`BT0 zSbMgEDtf=h2+3AO!YIU-mRdh30#U^vfdEM&Q=hlcho%+&u5j3nc$GU#x=A0Hv+ zc-qbnzud*@xF@)A!@Q3nmB$op_gW7&Euvg(VdaFgG+cf76eF<^)eIvV+@Fq*-*pRI zueFAx5ilw$MkJK#s+OB{tnn%vcpd2(8L_3m?ii;z34Fwq{2S+gzb~xh3M;i?%2Vm^ z{fD%-ud*vvbjD4+zrZO6e?81bUh^3X;dWUWNt&B$xnEm2wt2RY*DL1E8Da*E2~cyf+|2i}DdtPEr3A$kzN@Xzjpt$lCnhHju}Da@UGG^b_WxST)Lh;hhgvOQV;n zjiZRCNGyMR>-xUWQJ2@sP0|aZ7FbnW_FAK2(dX?IEi^uW_~v#&&tXc(im^wt zEPS(Rb*H=+zS;tyV=Q=9?}_Zqsp)%?Gw*+XY&b57Mec{ZGD~{D%*+ zlDXrSO zZv-|rL7BGwLc`T!8`Px%9-e(_e5ys-fA0B_nvmr^-u-|DPwsX|2$u1&EEddC(nze7 z{}sO1weDxqYun^mwTkPVxO`yKTJ3~hMC-<%wt3mFcaYK0iSxt%c0K}u$x4^Sez$R3 znsx72%iihT_4cdmJ|uQOJ{gtQaWAfb7 zIvJn|a|3}tF2qPn%dR6+dEP!2OmrG?+c&~@HY~@(6Ub63ybot^X8_rw>1hxyMf3=C9X`Z*_(0NfQjJ$eK?9}(Y{%kYfd8-$qGg+gu8Jl2m07@%n14s<76uJ2QcyB{Q}ilcu6Oh!h7 zV~5+wI<^L^ZoufZ{ex^HFLJy3*=?u3;T~E~aa6fiCjl7_jv^$2J1YmBx#wJGHJaNPK8n^ z9v9G(n@IV7{Sc!#vEqN-?zh=vAK*XcrP%YFLeEQ zq3brTOtnq7RFyWAEuCwot;5s^dH=8-hT~fg=M!#v_+B@L`5Ktys(vpdh4GkjIHBHT5!FXC@*&ZOd5UYsclWML)t}e4C?-NCa*_wg40o6 zzM|^%1L7upBJdWuUsnXgh!#NL*sk&K%y6mviZKAqYQP~Mc~9x-t6xn0;shtX_ND## z^{(eT74j-BhZ%&^IA~nmM3VVET9r}j^_7ijEB9jWRZ$?H0~-qy zb!b6BAy%cnGQj>?&1=c`aMEiGwwMmG+rUd0`h>~M!gPB%<^|zlY%&t@LaxBqy!Sg7)CH+WERL@7?xWpk3B}Rt56) zWP@g6Dc2C^bg8MAm$`hR({8~k>OT(q^2=Na>G|7FqogQ@MJ;VI!v@0gsHk*d`)wIl zRCvsFv(vZUi(0^%)d#!nCCfiVCU(Pi}Pp}I&!b^I^tWg(IpX*q8qdzJw z0G_>S{jTX}_ImU6wWGaT(L*b+=W5>8Joh@&7jq8UR<{3jb4n-sUrDJnW}5JwF8`}i zdAa`Lqp6|}`h3;nUfyFNm$Z+1fci1SGc$-vs^LTOyrf4eUjNs2j=4Y zhgV|zuXNr#rT7er}$rBCA%`^EBp>H3j7b zTKVnuos9|Gy#u?f5MjDktoA6qE%9cd0%hWT&-}Sx$XM^5$}W_v)}J3^>7ao1a(~0Y zu%s*Ei0=p^v3)PzPSY4VqMwXhs%+tWl@Na*%G8PNzO1bk*R68(BPvgWqEWUXk^94i zisT8&$$O`n*7{AB`au35c;N)nxP2~WlYic-nCP z4@US{Ii8wN4G*d7aOlxB;=?weqS3fRymm~!%?|bs9{Ss)uJ&w6)iOT%OM7$U%)U)X zpgR^``FJlRc5+h|g}=4t*1Q+fk8dH9gjDXh7WY4Cws_+XOC%i3lOU#Q^d#6`b6j~m z{#?gbL2(AcIa#e>n1q7M=Kq%TnrFmf(b%W_Oo#LeC|bU8nz8S5xZN+EyS#Q7`w-m* zrw|BkU@0^B`pH@{qOhgRNe*Va@&s-h8Vk5~yM?t^!u``fUiZACG2$N9VA!ZJpM7IL zYjFzb%tZ(viPRx)cfeS&Cl>^epIbjjjAVz0gVqWq!f~-%zu+;wF9`APhS zh*H#La#O~l5&fIRT-p{LX_I@v-Kei~o%N)s@F9;oCQX566{UC}qGh6>)IM+lTl5T; z7mC*;7Lkz5{mWjZ@z}XUJGu>tXzS)x#-v$#x5;tyQ#PQgynMCIUv^)tgIx3D1pQC) zItF#YBwU+-cQqLtD7(sSDJ7%KS~}743nEkdTN`Ugyj4dS4nwH>$=vq-L?;s~`&E0- ztI*noU-*)8v?)l{t}-H2LtMv{K+N|9ZpII6#c)&FSP&5c0IIcUB=$p)-^OuCrzzfd6psNe)nUPrTSPlV!7 zeV)4u9UnUwZ1;L2l-|I&NllI|wb4x0-aOv>ZJZ(`;KP9{(@(rt@pw5Bs?4m+5J1!$ zKzXY?@jy)LFxBl7@OT;{-s(RCW|^oO>G0F1-L=@-$}`Uto>!p~cX~iDSVtVBrs^%^ z9O#wN%H~;EP|Rd4-g^IH{rzx9ULsXLo+VcC60;*gfHv(@bRa$p&p|+)3vx8AespxP zx!Dua>TvlpVyardF4BM(i-x|xKd{1hLey3rkcn#?18RQB$e!>#U)2OYJy-E6r5*Z#$LQSuAj5=u~=!{jgmux0O-Wbu?-+QGJ9v zK=z*YG&+*McmX)4hT3C%-TKtLo*uyQE!eLXm&oR*;}UP-@P7A2Dn*wIu|;#~3c_Q1 zfVYvBh9ejAGrYlreFJOAD%sYXV^#~Zh$K=BbnS}N#YJInx&ozkME%SqqTWs((d4xr zUcblVLHJbCKXuaPlnDbJU+I7Zl-OL27cJaKh;A7J-U$D$a_QPb^HW%)GyokSC#_gnUgRg~G2lwf;&D zktcLd1GLj6)}7}$_4$B+J~S5k1OX7c;Xx|!jpR9df03~EcpL#5*0}nLl`rOQ0%Kv=`w_qq*~PVqhDnwN1UD@oDEu*{f+rk>Odpu9#(~gPnB8^ z&(>tTm5+6zT~|BEXZ_VSfe`SR*J(#j2L#MFI^xwJ=hOu7w!isRZNw-89&0q$uGOv( zVWqpF+x|N~j(LdclRpV(52geQLO8Zu^2yEaQcu1W3D6f*m+4Lx^$*|^Zlis#A&{iD zY^uj4h-ZQ4tcdPX^X-QdZXm*^EfNAf-9!IPUu?q@l}yFP%Q;hxp8TDsd!zvq(Rhlj z^`4z2?^#lLPjHt8=pkk3zwua?u-O0Jr96uo`JqA3unvd`?B@hUheQGfs}vuzAz>@; zUH50Uoj!uR>}IQ;?>sgn`=9$McZgUwr=p^x)WzpsBz|)E_A(mGlqG>iP}uXDc%I!D ziq=C!vD}8c(5T0@aC3OtYL=E>kT#~Fbuu=Zhq;r@6W-Aho2@0S3o)UKZ&=~T6t!LwVk^1@C7Ap0 zHW8d9u70NZx)?6Lg(dOnNFfU)NLYRU=w|ZK52d{{3pCpfmx6zeh$TcTr+s=<-Xu|; zWeklRv{4Y5h<_P&W2B|FVWO3wr@S(=cIs*K^2y=%kHOAA6(b^6H|@yPFN1%Z__ecy z5EHfHhEy6?QS!VcCemrn3n;R(k~c~-srxN?bEEK1lRKOZO5#3pmq{+mgkHC_TIftQ znBpj8nqgM=T9PBHt9pU+i&#}bzlnVF*p^G(&+z+&_En?LZ9{dXzS~jG^G6G`aqK`8 zJ^wjT4D2>wOePHX=#2jYY0+#Slfi5OaP3;f%5DsL#5M8To7E99iW(V;5eZca;q0?g z?|U!kS91J~kKMm3@i>9NR@W9>Kgy=>p;v(Qmruob4y4f7z!uavzcjZ}p4oMt#h9&M z_vPY3D(tG#gq@SMu8rX(R(SZ)acY~PAy?q$imFK6GA+$8FU^HmMQPbYF>{q_Te_l} z+71>7(+YlyyvHgdiU2h~?=S|2xOcrN2yM=r6zFt!3Zu<_@gwvh0H-Q{=#%kCTNZH; zbRG@th3$zojDJq0A;Fiw))l<)LcMDu5z~RtJ*o$(()}J7*!pQu!1PseGNEFIPGR%9 ztju7aUVdI*^33cTtRl}Z6WP|M%lMMZ9>=ZHB3ex@A^GWLjHUqQJ;v-@sL_o9Ft_C> zrf9%{T8KT+hy1~QVQ&KpF`qS1^@Dds(%}e8VgjEHP*AG`-~*YPr7Yf%>{_RxX&`O< zTC(-k32JauKN>e0%*STPaj>NP}6l?r7H24^Q|a z_`M7x5djRV#Qh2cw>@oi(pW3X4SfkZNW}vNNfW<>^;59yu+XWbP2c6{}?ElY2&;G6o|l zg9e8UAsVwLxerr7E=NIu=w`s{aG}URp*Eiup4Q-NS%C{LC$SsYTq9+fGJrJ=0MOTa zc0lN&F?2pF>-pwwrOEH0F}1=#(_KW{Qk8re5xbFW|E(6;ATRK&oVI$D#Koy#4o!@S zdMG&>RoeT)3CntcldDWVDer~8wccuw{&O|dFVF)56>L}7Bu(YzWkC$Ae7>8Fsi;&; z_3zT_(CWm6{yYiYsV6-A-RjF~0dDlL*XxaZz2~MyrW_K>`&3d)iK*HzQBC~QwcaO| z|3o?gExw;U_MeO&e;cP*<(Vi-ovsD8K7SDhE9@io>Y$#qq5;q4qQCeQo?|g-6VCLC zhLjw29+K|_)?=ra-bydn*||6I9tStCZi%J-Twocat=x{3Sp~gF^)oFfDky@$A9&2+ zl?K(5uVu&08y=1;_zQ@YBCJ2f)0NGV1=a;71_Sw#`)psCj@LS)rh2g{vY1>MW2&83 zzx%NNcpLTrC8ec8Q>qD0T~2q(v*h?>&>%s~-SJYf&($>B%I$+zZsX+?IMSUAnQj zYrFR|VlAul(~l(W$2jn5W-1^qcDaAvihHu&qDm#J)J}U3-_H~%v7PS01vmY9&B4h* zv)N4yu9MM!w39pXpAbF0{a4croPTkDVQy|g<&D%2%p5P(^nD0{3yPhsUc&p`_B%98 zH`E>15aQvyHNed!J8R8iw*P@2<5U#=Mc-S0J8BtL`$S)A=w&u&L59EBoouno4ZK#l z?7b0b=9t*mlSGLSzpzGobZ*wvLjk#Mfi1Mb{z2T<&o62EoSM#7I(rFfR?DPh*ZI1o zBklO{gpST1ft8tt@z^g=qP~^kpmsIGN=8Xoh_}5D?jm_vhKmHak7p@S8;`*tlTMeZ zrTv$r8&zc$2GiTl?JPIxk@A(z_(z*@c%S9vR21VXms$7Nuc0%3FFw%o!a8k$Ktac-X&>UJiA+`os$4bU}Sqs z$9(PwhanNoVcd}UXvE%(IC-(m!Q$g(5VoasZsFO%QY z7jS`IUI1n=((C>!g_Sck=`LdxUho+LmD5L==;pIK8@qy{Hd~je*+Vhh6631u?aB6Z z*ip97*AGB|<2w>mBqlWw<>4S?%IE78P^tBVD}x=#UDAgZSXuFXS-##%tGn~@VMq|n zx7Ml3m4XIt>Z=Ar9^vBI&&hNWQ}&Hpj+#XQg1|zo#03@>0N?4Q`#lxP=oxM1E9V;R z_kn$7Q1m$jvR1^RZAE7&KpVZ9%D#64Sv@j#yEs{TSQSZF%2ekmTZrxMJbJ7HhB5Th z@Iwwh^k=r*wfLZIn5wf3k8oynO_u25O3FF-`Bl+`XiRI23=bv$m za9w2Ih6W&ptQ}WQ7Tf#Xq%}VW1OQq~R!6JF&C0F*c9IRVl$U!zDxpWOs* z^xHrNeUb2ORaJ7aEn|KsmqBZwLhP{6pa|sLO`zJo(pCx}gEN{Cs4vuZFgX{PRKmIL zw|}%dGJc8fHP7K~dD@z&CELJUIc!iA-8%qrhijtj=4JkrN(~6T(c^=A9eT3Nq3{$i z4|zss6l|n5(ioE!fUN^m8yG?}g*^QLNFM-EY1|e_NMG_$rFi315rDz|B?_l^AgbfO zB#_sB#$cNCs-WUO?S`7wZc3owJj2aH-r$O?qHLI|@`sV|^-bB9o7S%Kct=X1qA3xK zMTN;JNlAonbaez^&T}Qb)vgw2pi9%S<@z<|X&E4(9e0}ji5IWcK}xf85EP=g{{YoD z)8#ZK<-U1Vw{+6{4X@T6dM1lYuGZM>^>>j=+e3h-&Uk?%n!&89TuEN>SahfLKHHy# zW@@jv*R%sxdA5X)CgnuF&DR!!=rgDx-EMx-q%Z+Y`&>2h{Vp{jVFKzrEVQw+{*jO1 zwOxA0%ky}&%W73f@2N0jZw-69h(hjX50x8__+J3lYJtP#$cA2DTahVOqb-YuMv%R~ zt$_n>TVdjF@Y@+Y?{9{BZgPGwBX)5*V|l}}6h-P&4{spf6Cc2XA@pY2Z<=)oiAuI8 z>=QfMR|RgmMMEQQ$wE8xLUlf8_wpYQNX+u*FnKugKILrB3-FVJa0vuy1f_fz3p+bx z@nrG)_B81NCnVw`&xMeJ<$GKuBuy)nPfF^~(+A^lt(*)0(7M;<=iR1(SLXMLWAV)k1%aQ8F%gH!%GSN$Vy|=T+pQzK$HQB znEVi5Vl=(-d9mIfHza&o$kVO>m~#H!7;r}!LO<>`N-p9Y*&^^7x&xLkcyGWq&({e;95B5Eju^buxK(U!Miy zXFfU`9R6L-GO!#IxZE%01@+C1Efr|{}5{FS5fU7#>9?1j81 zoRtX*da_#BB1F&bcmk=MCKoE0P_upnQu}MZf6d_FY%)wp6&MG)wjIr>P&%JjN;;6~ z(QIsv}CG%-{3R5Ua+r9R7dP|mEaE5_#RZNfqJzNb&y`zs=-5eyHAis)!DQN{!wXfR0IFBA`r1+h^>crmz3+pTbL<0#UxIzQwXn zGolw_qN&w_Yh8}&aZSDT5vw~7tE;x_kJUgW0PL$6p}6LANfL-ENHCblyxV9=;m}rh z)$X86y!GG>fJ9^cc}h>;enS zg3BoBT%t0ee|rB0K*n=>UlffII#+1dZZsWIbzUF&3|&bZ&Fj(IMxhxCas9_7LEqqf zJtK3V=Aou#ON*|!_n|i2P6@?vJhQM~zD&uc6=J1R0o2aL{nWK3zZynV@dfxV zyK!P1%^-yjud4bGp`|QPF1I%y!iQT5&;61zs^MXYf7>*)*wm+zzr+M`aj~KNs)}wD zE`C|S?ukzkXbg%zpF3SYE|WTImZ%oOxs0HxnF^wO3?+;|IjIUeyY8Rkyor9T;J=WK zI3#ciy+PK7}`JI$JPonA#@fB#)e3ov?IxY-^K7!nR2XYETbnbSKPdw}FHXFt7I zb3LmZpQs^zKlyF$&~H4A7}{C{ROTr_?h~bkj6w{2;_#!A{6Rhr)0WSNwE*;p@%H!Z z9ACs-srsCz7MWIR-7zwb}e1O=P5&~ z$`%S-H9TS>@s5BNg(JvH;LbUYXq(Mk2^j@hLLlsahpE&94s!_{27IfcQLJ3z?!N}m zCMhzS+z+NY<{Bu-c!$nS_8|!Il7@gq!l`rK!C>7Vzd7kKXca#w$cBqgS#2TJi zN@t`_M6n5e%H2E_*X>jx-Mk2M&s77)Ak-BF|GptW&j+5EQ1ndfAca3C3>^p}( z`GF|mG10~s0k!@!ds!JMAmeIytU~cG5f2c-A{9~j_i$%mWh)HKk8Yf%V*U@I7BBPx zn;w)U9B$+KEnMwByTWjYf}m_QWOn>>Y~D>vLXiWcxdtBFJ+{(NlDt*iOYDV&!E0Nt z#xq5j`M&vx(d00!pa9YPWB`Ld=f+85S!qcKSZe*Od6pZJ(d4LE@uMlbNaY35aIUd$ zcGuO~fB7$qxJ8mzcn~7ZDVj zn7^bHHDT642ynfuugw1lGCfSE_&@M6-=L-8$i!~OYZQ=vua#7A6p3Oi;#rn zM2t8dew1B;(UaGer^j`NT|&p_6!4!KU@?Ro(Y+0i$aUfu`}keykV(|rm)^l^;+haZ zCm|_i`TuD8=CC^d|KYP*SS{OjEic=)me#UuoV2vuvTMt>-Lh@lc0G5W@9*h4|8(8w zK5xA22L+w#x(skan!s2N4LmZ79VZ8I4__c}5k&|NwvH#%3M5SF4?<*5oOPs#wh3wP z0{(_8B--ZNAxnmJjrPL(VoZzQF(05P(soSvLf0|EZyGwgO96Hwh;73&ri71h=GINaYDwf?y86;VQaq#XyD+a?Xdkw(Zp?h zyn+Zrrgwjy2R=uk(&qhv4HW9=q9y&)_N7(uRH!^=gs1}ofhWsc_wS^sEEB&E(>n6TRPH`*sxTtnEAo=J%oM*3{WwG?xm|Xmr3#j{v7E`pf>8`Rzo*-7oTAYZAvmZ|QP=lBk zI*@E#G2GS?Bcmn!9!+07(9+ZOi9qc?JP8~Q($UtumAH-}2j|8oQ2+tA-LY{0*qVX|x z5c0Dl|H3gOH=85H$qMI;QnW8n9 zX2Wbc(nQ`|K;1$z9!ip93Rp<^>^2-2eglE24V7}TyCl~!b3E!s8a9*Wds5nED_=GR%=XoOei9ecG?mKX)Y;fxG ze>&f6&q}&;8y3I54xj$xBRM7cEvA$eSI-Sc}^B6IWB$v$dHc@9xunzA^XFw7pf*U-5DhA`F9& zfppf&E!f>prOJ_ZE;t5NbB2&ivYadut^u1Fgyh4>5X4nz#6C2A{Sm$(2^#}hY>47~ zqGe)vqfJglljFhR^_}mqA3)eMAl$B5#CR@$22lZ+eWhCuui;2i%kewX)&}(VMb`D0 zN|tuBStLTZtVoz#{X5|{hmDgVnXi8+nN;p~Aw&4ak^F!i$E-X4M9_Z z+Be8DRNDVpC#Dk_0%SKSCOZz{;#4qf?~h~(8!cRC>6YyMb3W@V*$=PItmJ(o1;Zaw zx^HC|l)`ZDHzeZf&yHU^0zAH~-Ss9?+O91;Ex1Q&&a&?NA=Mbt?XTO6lX`@w6Osqb z$%DT4#)YnqX`Ef0lYL^fA4roz5>oIETCJe`O9Om=bf2(N-&yD`h=oho5QeKOyJ7EkmYh1m_lA&w8rI;K@E#B ztP)APL3+a}$ns%u)=sXNwm~w7>MDXm1O|b-{dny7Zgoa))=v1$N$E&_t=kPMI3hsY zG0CN5HSrn{ny=RyYcj%pzF6%3_FDXG{rzm!oUvt}5WnPJ^zit2 zdt(C!1B2D)`6dA7w)I+B*S_+eL3z4gsil=9XWxD+3jCTZ=~2vSZP08NQE%5kuj0QU z7lbWhzprT0+Fj|7$7bygEduL{fayvBi6Cn?+5friaM)B*w zfb5qs?M719oxwx@UlB7iWgkA+batjIdqUq0SzU~k1|krqy&hVkCYMwe9bPL4VA?Dw zJ3dtP3=B~5^P6<_6innodgdE@x4I3zoffyc$ZccNm~@Gf7IA`CdA@w{IG#t-_sM1+ zAQ(lnP{LyM5!P<_r^&BFP+{9i2NYM5>Xbq|!m@5N1&?N%A9o|9@cfhAY6qqTg1$P& zme5ic3=hTk8x7p8otb4@l$6kOJJ~(uB5CM1iHcTo$Wca$;B;&e;9(`r28Tp~Ul%&X zY=7hPeA8E_w6!W}bohIJzM--PuD|Jo`BJ7^thq|Y+cNfxQOc<5uuBHdWYzw#`Ha@l zG4<#Oued0zs``#mr(*BtLrLQTA15bpJ!zspPJ6yWt$oq;LPbr|Y1?~nm#eKU`;hL> z%Bpl~rm~khk6ZRI#y&IK1pV>j-B8WZXy-XTZcX_M3`l&XD!6+XkE%QWDbOu}#st=H z-UHWXe1?#~{pw;&;Oy>SP6%F5=Icdpl}bK71H{P94HVw2YqEQOb$z{rX2T*IjUy6b zMCMlpYO-!V^51ci7GLz+TD+GIGJUM588!6e4-awEGElhPDr|pX)n^eWr)qm^X&qc_ zVZFY}EVVkmgE4;{X-GB0oCYIjDbF{iELZv>v>4@00|mx5pv zp@EiG;#V7VYmME#W}roZZgQr6g@Fi=4$HhnrMFNiVq97Cz$hApQ*6jH6pUn9R7#OE z0e;;fM0HrN)woiBDXxiogdji$T{agy-|EEPikKxBKowA|J^#W-_A=!KezR zITOyQ37??hVRLtKQ8-%EnDRD~*Z_jbk^51gEx2R1Zh|2ilA0D9aIC{eI zh*%A}fM%U+Wo+l&QT~&UX|D%;Dgs`ac1Pjkevo{^+DO)K{G<->aHqV^7Iq#%o@NE2 zl*Nr6wa2RRtL<-kJQeycU-XlUwY$QXx~i*qyShROHoJbp54{cOR_Yj6wlTJ$b2+G< z4r0JywF-%Dhy#b9?Z;)v#bQ9}Ca$eGhM5x6Nh5Z~>kwneR2^=f-&4p{cLEkFMKMsR7&Gs(uR zc0B0=Hqk&G??LLS-rE@F2-;F%zh6Y4AD8-g@&O7{J>GN^+h-C`o#@7{97P}C`8->F z>Ev1>^41(y<_i5YG|XdYleUeEtQemO-aIOYoGGc8U8zUd!D$gHpR%KT6OW9Yd$mIyMusSsjBL#s*-;>f4LarS6eWj-e+#j zNT0JAQku|HnusBjux@ZPc}Sju_-Sb}+%s#(@LFVdW#aAapiA%;>}sWBY*S_f-aXzz zv+qU_bu#Y`x@0v4CQkv}rB@;Kbo&5@!##{SsDezPUy5A!Gzo|G;@|AnOOxG@iO3zX zvkNevJ1oROql7r8C4R{8UlbAnQ%CQb{!6EfNiPGZ(LItt161uw^ng7+ir8nOjN4}~ zIW8^}AW})U_eT%Mp>H@T6iu6~ZhkyODm{%%BAh&DIo_*5{T*i(+XL(xpcYO zC%yep+9+fBN%B!Ja8Fj(bkZ=>hvj~1%ztt!;4x2ssa~T-V%G6g1z>nK%(Uq|cb$s5$8Gj*3T}Ng@>&M!e)=OEAuM`nswc$dn|f<4#1kxq7ToVo3@+=GCnXh3 z7>+(a$6dz4=;^wx&-|n<=5hBh@vfbxqb;%<+Ral}vBn1g#>T;h|8yPq>u7cs1LMkd z2P*IHjrYM$LfW&7)WF3S0~gnx!GD*7Y2C1eC?`qSL) z?FKIU^7YwZMvw2 z{}hNDEn-mFEmT#kPhyY+=;v#)D|5P7xtsdZf&(TWdlXVLnh)22%dI(inaqUFL{<>fyWz}zUwRoTg!#;k*u?k}#Z$cU{u zTAuh^&uZg*d9esw278A~*rs!){MfSUrt1J}Ol zHMa6oyS^U}FLVnKsNK~;F5vTQgEcRn=e|sU-C+dyR!1qw7!3|O*;Z%Ql-$5T*&4ve zOhqLs4JX=0J$FXn3Tx zMrxt8+IL#Dynvc?(C3aoj_ZLA=DfGRy#oh(jol+e7 zjyRcVvIV`jW`e`|W>Va!=LsJ9af>~h@sJld-t-V0VUVcZSLSo+CqRlamg%Cgd&t(m zt$@w1C~vSLi`8$#=H%^AU$N{RRdn4#u~ldc9$*3wqyB}C1cim6>3e0gd#F9-(EbcL z9Fo?y-LtG;n^egU>8BzQdJwrAA8!~?0aP0|(ZHc)zTcUD501CGrT%)ULVEl`R`NDC zAE?aT{j8CFu*Io~Igx8NpOhUlL5j zp{#>YB!|bd+h#-TRv)bcBQ^gvBM|~#EC!#C@P$&TT!P!u2|zV$Gtk zISfJ313N4nt|(B8MtsT|^|ureg@3~jw2?9QCsw4U`&12)WRrG2Qs-&QIF{W=VTd|i z7BiCBV4vdP-~eptr}5AttyrWb)(0`UwaehPi;cyv9XKepkFk^R;1>)Y_bOlx8Wy-T zg+W&4)oktH=H&@~wp=BY_ALz>OTRs}?u-;;r!+OTu~8~L{bBCnCnD~$Yp3vz1ZP18 zj;_xAgW#Mmh_t9t$4n9bvs~9>Y%DzZ?QcVH1-s6#+J6!}BhEKsFi4mLy#0H6J?9&I znLgsXyR%mVjT$b;W+f$71!j${A|6y%}3I?N3|kKC8Z^k;%W% z`Rqkt#N$&b*_6+wUALMqxs-ApEGEQxh#sHKr`oX4|0vxPN#FRkfLn&7-PK!rG#n8h zPg~sZc$L0c#edSvNMdWY&KVg(6CBAt6dFPv3n&9)&L>-IF1RYz==B}5oVGIrrdaNV zxv>sXeOrmQ=|$vWbwtYO`anZ9O|9&L$CEj9bI`g%>f64b*4ayPFn+D1dQ0`$Kd11UlX&+glBXDuzv6BQ!t5 z_41reNx|{yNKiHnsd7FX?i!ZG&CK(niA#Y~7Mm|^G)i-**jDo>G^?rNyi0$GdyR$R zpse{yqtt>&i0R*N1xwDL1q~OzrJbvle~3|Lql6zjeA_^!*I&Z zgXOE^<*l^$vs7}kr=rK2|IHDGE94_N`K(T3k)d;Dk0@y@2fNztE;E-UvtL+vEUOB) z>#gV6EGAZ;eA@mdkz#zJ>t$+h;%9eQYsEFERr?!{#|1qFfnozIM@M*~uk@}v((z;7 z%}xhbo4149uVu4_EKA#n>Bvp#~Y{lJw|S^M>LD7UWb9qI#GRpcJeALo31 z@6D%vdNhSb=LYIzT#am|*1U%rS-kugcs?DV&WPI~NUlEN; z&H4em;_v_1{!%wRbF-WYPJ7yNWq@ka(RTjgY(JrIpi7e(N5!$ix^$h{^o^{J+;jl7 z`lqE=vz>W6UO}x&VCV+#)Z(3jP1D_JBo?N8<0tp7ngd(?if_h6!G$kA0mXc!M@3mn zb%-fA4NaQM#DISHgS*#`0Q^VRTk9Y>)92XZ%==UQ8p-aR0<2_jqa&A0n^fDI->6Fd z?w7abO|Ky%jf)K{(n~2xr;Dp%Mg|J;9`gvox6QD@7B37ANl_v1DK&l?evd$ou-6q& zCpSEUAqu;+2E8_Na|VS}3(Js#?3R}A-H-h-xuiDOKD+suHLY-r2LG((_4GuYUeDTx zGY^9{HjnVQ?T}GU(w?^Cb$Mp6&Lcw!UZ=)B(9q~jOvJmpGvubHYQNR5*(2;b8PAQ9 z5w(G`J*4?Od=eh zDjuRKxVTN37^-2QxQj^)EHAFu9s_uVPBjVKN7n^A_N=i)yyb4++rLJ|{c5)BA_;`?J=O-a$h zDCcFGkJ+`R5Bp(PhEcBbcL>U5hQM6<H=}2oQLYZ520iaKCLnLpJo0(hc^h<)o(@TIiDiJg$JS zXZGspK;pj)jk2XH8Ox-|Sr5MkihpsO+_OU^)6!9G+YPVRhEy`$SU7Vte`RI@4owCi zF2U-!4nrgsg(o38cY8+1;~b{ncDUIK36E8=YcduRvLWz1Z@ep8zHe4oT@9F}f)*Bw zS65H1uR(zCsL9QKO@=Y~)t|qU4sNv`-DV}nEi;4rnGQc!B5DILd=HuTVxm7l9)|sL z=G%1Ml~l4S|I{CQGz1C6UOwW`v}s>BoG|bbkyp(wOgK>HkD9KS4){ z0UQ!YUT=_@qZeI-5MTyDI}h>vj3T~Y>>NZMM6?p?x=IJ{0Q|zT)d`DZDV1S8(xG{@P(& z^!V0>#MjZ3fw5birFPpf$E!&_FB)QYc2;&Z@6sawUvNB5yL*`cu*eD&)&M)9SSL9h z@R;^2ohfQv9G2-m>Fdq|F^<)Dr-*mVp-X&5j*I5qakK98i^wBVLyNWL^!|h>;96ta zEpKQ4((ZN7^oo<3_|>+l%27h{?E`?(KonsPvpZJ;;FIVgg$>wq=nCn8&(p!~?E~N?Q|71-x~yv2 zjIEl=FSA-J^Ge;RR1hfSbp2v;s0v^qdCW8xmcD@7`S6&)pw7xP0tw1axtB_R=2^}MlIC{%GTR!_*Z_I%RPRhpQEA(BjCry~Y3#OKPl1~B~^>RiwMHJ^Sy zUTIbt(<3z4`11NXy1ELuXMoUBV#Z)&je*EUUFjj3H{hGl_8ginsN52ULA(xCSl`3Y z_A%CMyj(vj53O##8j~|p?jQ;&g7DB zNTbyh!)ECl@5ydMDkY4mN!`E3ma>M1Z2yf@2LeEn@={wM5Px zhR4|I?&=CPPc90deUXeejh#bdcSl#J{`NT&eC_S-mkl#t1eUflX-B+))D*&pUe40< zWZ&M{Pt(v5;PE^O)Rqjn6tMiVlTyXr;?gDU5zZEv<2hW3;=ySkY=HM*=y%I<{^A$T z+!q$59BGd#_2~a|qdN%25QyS9*pJ|{^O<;-aG7E~ShWz2@;(?w^o=`Ww`cWj+-MqN zGi!rQ5z_^vCC*lBSD;{>x%Ib~vhl3EPXeizi8r!YKp>vr4Fm5-L@ik$b~!jSheSvo zDdTzS&Sz^>YcXJ8EBBo$xNAJCL+Pnfm!jS9&dB7%G>ipJoz+Vh8!)l;aB?byE3F%` z2k`J*1+JVP7FdCBqR%hP3)iFU+m4G|PUwyp<@5~}C6!`0lWz=N;dJ`pb^#1BTZFRVGKu#la$DSkvG>(fywtr=y9(dQl@Acyqt+nc{K>fwpTV+mlHo zA32FO_ED6H^O&+LkMH-~W$_@$nMQf-0to7TmFf{w#yxw*eQ7MA8hw;rnm>hbu|wWHSMDLncwXAr*`9@)bWQdEd*zrjM8b|u zTQceE$8%9mtJFIGue;C&upb1qA`2k2&jXpD5%J*T9bn8s>p^zsb^-qQ{*#CAI~8YjG+1PYt?TYL|}|23=3}vDI}WmjlcG|2LaUtxSOBB-$@H@72UH)h0W;o zZ%7L4ykY5%>ZwMm#pR;P>G1 z4+6w9i1km_%DA$u7Ao3Rk>>0wCWA`MA{u~q9|WR+{J?0yk%3$$QEman zjH+Uay2J_rDj_D_2H8Z1NJR^S2v)uZ_$3kdA#?CeXB(i9fvlQ6ajk{z5UYV+F;8*>JPPa+i-siVCa^b{k0!a17=f}JH;%2LIr>Y^phE#v z1cBJns4vEqNOGurj>7)2=lKdwgOFE6KzP5BqtOD4I)_Br8-%*vgH+(Kp79^Mtb7nw zeLIM_bNvMd-ylIw`0zmpHsfhF(eE+fTf4HJvzy&WdXuXJxvbi4~S$Ge4i!LjO%F7K;Kcq8 z_NzJW$y)dypPHW-K<|$IJ9-~dLl%DP_6P}#O@Mg_FLBwPf@Hk~C08o3B;viNk$kr` zReMIZP3|Y1{)Z%!b|OW|R?j6(-Ge@Fc!PwjTH0r}5i0bT&!y%Az`xdE0n)0PMjR_A{lV zH)FMlW&*A}cLDReWd`y<7#Rp!&A<#A1VinA?uZyCCj3n(ZiSfDp`3*>acFs24BaR0 zMpAcWG2$~N|M!687O3niG!H9jB+IE>p+t;7VI2I66xv_GL!b3k^OkkT|~&}6@oh7Xl8(ekVS zGkWo~ODS>-8_m)y93NsND;NjcpjwSg-I~$F$}@Q+vgiQS8C;*TXQ5HbLWw0ZHmMNx zmrgxgzphY%KDOug80mO-38x-x>e#nL^P+OwyRpS@D)L40r-TvgGtzWgZa);A33OQC zd|LzP7U+red2K#w6|=09;`Gcj;S-u#ki7)6)nXy*qjB|jGWq@Jl#R+|vOxx5&_`Xe z@u_rE581^)+65n>!iFovB%m1NvSv0M__xHNFU!s*`dug}z^;E1)BP~7@sZiU%+@_B zsAjzpvJQoRyeZfy3N0WE2}lyMa~gvfKK-P`0mt;sk&}|RZBji`&JLDB5PoS(iFryL zHReCW*F)AMA2s`+NllQ;>HzuINrq69k17e14>a)0kk#6mvcD|3(vo%M62K)prg8{V z2mm2rO<@&7AwhIz=5&&ja-UiJ*en+j0pdOn?C{S(7Hiic`)mpe0-gKqa`SqoV!zWD z27Au;zxSl~{t-uM=|n_nBo_AjALE5IYod+#3flE_2|#(|LrTJBbrGRN9AY5 zPrD!x=yO$_*RU+-hgrM-(L^wZf6^b+fGcsj4$Lkb2+%sD^*mhch}>sdbzk66m{2w! zGmz`XPX|f1Q9(?ioDNGB{ziw@rs1lyv13TivDoJ#5jFZ4Ra`Oyd;1m6HnlF`nn zwWcQ{N~^F>xk1TJ*nlMY&g4f`qckkX^w0H$Z}^4EUETKP<=Kl$(>KD3kEW zffO*%GVy;8_dC#h4E>|3n=p*HWk~`2`u{JPDa`+X`swNR8K77ON+t0g6#E}xbq5}t zf9U^v5!AuWgu43@Xj0{W(h0mr3NiJ+3KF7#JMzW*Z>OL zp-J!$#SeNs{~ZqaA<87q4_}b~x~Px;p1G;>=LIl95dFIcApU`kKU<~qTTX-Nw8Ima zs{Nm46u!P1VahL)%KgH7wxx@ zP&f%TWUs*9NbM2kD}>f?)1zyx+}RTJF1QpHx&mW4$hz4oYCwCdfSL550AFm-iC&M~ zfqfotls|r)SDd7s8XwoxQ*z(cSxq>3QZHRl*4Bs;{E+3JAN@e6d!=YZHH>*pE8Ai$8(T`(O?AGgT8yWuzyeP z;KBk;MI87oT<z+&NmW~=^n?) z9CfGj3Zm%{&o;>{&;}oT_hO#T>>OeTc8+86^`7p35jo0dmKT;gfHe1rqRS2#bGC|SO;_rS4CWeDYd{nQ_59{_MP5J(ip(CXq2d=9=-IQ!`f z5)?hPDQi`3Mp3$*h>z)IL)k3Ss$?U&>ls6}9%-);1NTcLA8)&CR1tlp=Ci8*J zBDuLhtKQajoy(;}86OqBtd<;$9s5l*oKaB$^iOAS}~YwFWDubMHdg)9G} zMx{?;rfE79DC6`PX&c=R2J=cizgn&8>M0dgRaD1K_1Jd@Ask>4VeOpkXz8lNrMj9Q zjlQ(M)#l~psrRAa(9RR9LDxoB6%`$(44_HKW3#f+lK$SJ=4iJY2|8(K&@NpTq*ZX# zdev^`iQMIXo87hcd}t2{;Fe2eq0Ci~7S#+XE_T#%H@3H5D6Gc%YO~Plxn>X%@dZ79 zZ1)lz_|bJzAU9k_l@CvHtRaGc%dPWSBE$^9sNG%tDotFckn*UxVVt<2&--|&%Y}J$ zr^AgcYj2OXQ?q<8tRCLiw^yQ0K(XY4gai6e`}1a+9W>~9 z7GVm5wC{N9^?I$PKn*6u!g~Ok&|bBFM$BZ=e(0f&u{%ltZ*862Jn37mdcD!XJ8^qQ zH#QyA_%A+3>gmePOy8VY9QO-Wu=Sz=^oNj{E9%*5trQ@N1x(oyx&nU41JJ_TRz zq%3mtMGXy2ZN=~HeI^{&c<2r}j|M)&*BRa(uHe(1yZH97u+LBHBkoEl*Z z+ayEbm4xls-OYssLYAY)x2WqH!uHoji@sBlS-w_H$|?G(g%J<>c(s{lA|OwS0yR{p zp|f{k;UR#7NU80u!uE{N+rwG^em8$e!}{{>>g7ItubI4uw(Vr9*fUdaMIXHP%Xnxm zxjZ$M^At8dlTr)HFX-!3Z*eATYHI47)jYrZW{6*M>&uImv^4wkAd~$u)9ZB8j~U9& zos$Ky(YY`Gg}oARj9v=sZWZxwVVflx8MB5fdFf&00ONRc^F*0gg_P)FRb`)QWqpEc0KD z6o9ujgN3d$cIO{ zzwGp#dESdzdwN~_@hHbOx7>l(E~kiT20}Z9(tl$mU9~Sg{>^WDnutZvc1cW#5B>@L zVtZQ?(+%w;6V`5f%RuCH+3Ld{G4{>^hIXh@^{>FFVokomk#tN!Etlh5TTeEr&$lTW8H zkTb_Puc#(oBd?uCl$N~AzR?=h{w{9Z-HnYz_e(jVQrTXE0!Js#S}%r=VKVpV`XPfO z?{IjX0in6a=T3wo*Rs>uMK?Fyx>L-^zq@IKpRGYKUN@Ad-K>C1WbS#hx? zlzFWLVMD8B;wb#uv(?h*i<%nyN|TF^&FPChGs;!Q^IFD>gueHM5|M5B13dq7Ek|=* zS(&Ny+=Idz5ir&KE@pwpg{aA6M5nxgsetDwnWXEs1aX4Zot{{4i_5X^Gyvds(b9hn z#Svc*<}ixd%^a8SC$vPJuI~@v-J$&NURs~DWp%|f(RcEv|J+HDUMy zV2Z?{JHX2kn=KtTgnF%Zvs>>Eh*M7Ldb!k&%qsAU2n#U>5B}1J+Z6<_7SvRPLr?v zH(}g*jV0m45%KKiAc5&7j(%^ZdOjOh+h#I z1789i0%U$_FkjEtvrTfj9 zucKVf8*i)WXQ!vP*IReP?he_HZP!><1+-+}@)<@&SkA!5e*+e%?!g(&(Qk*^nQE%4 zqiM{Ap7nuJTejnGd(VKty9}{p#$ms#VNX7Kab{c=VeUOAg6?ER3{TXXmj=+=TXxtU%%fpm}ynNNz zDtKaO=&G)HPp?@o$86A4-|Oy)am~ra1y_Ju#=g0HJqch@?9a*m#3HFoxd91|yASYK zTR+ICA_$O0EITP zoQC;+;KHsK*55UTCEH04N=xnCwYNI6X8d1!_3i3(LBq3Gv`>~h9;BDoc2QBk4i$rB zDbx9#=>`6I&3hCFAnNGqqPKg~!ihq8-V*86+f$(cnH#t`ggM{fbHA0Y<=F+m z@#{a+>C?z(a0WDu}aFanA|!prNN1sRLlX**8B7m_ja(p0pRib4?M$_#|z;5O4GIV{1HGghDO@I z8uw=9_jr1!eWTaAosw{Tb=6kv^(LsLUse%9k{Wr zDau+2tm4l`kKsI+*st7(B?BUJJuO^Vy=R8XD~`TZpU! zTi)g#nRQF}9^_OF&CN5@v))(TERAVDgFe!F{H?B586%YX!&Yn7n=L(K$V?fCXZ5y> zWwLzYKit6h?jxa(McsOS@d`1!;T}Pnp9i!Yu%XB|#u>jluz0yfhUb0#nZSJ2V{mIK zcuzmO8GJ-^dTJwzK0lzaB+p^JG_|o`_PVj)=>AMZ(&5-h$EUIM;Ict-tSQNO}M;WWJV zcMq$~I^!T8Z)Pivv9L_7^>zE3|7fdi7$e-tsH)YrL@uwa)OK$v^U`fjAHYBMknqws z7+bYPm)rYQ?`i)#t{DHG3x!uS*f4AU%kfk!!ZLlVRZyOjNByTa z_bek>kiS9C_^>}By?hgYhvJFm0lDkg-6 zxT^oucs-RtY6VP6AQ0WJzIhinY}rgBKmybjvY)AjH%w(a_L@Ma@pL1_2i_okX8_Wr zj=r9ELRX1vk32>O@7uJCw=Dc+4gN@4n*)!{FHj8)Y*gCHBT$ATDxJ6x}cwetdWs-P4 zT@~w-@gl(rDGGX0<_il~4}eaJixI584p~3lkpP#@YZ@&Ck3eVdzpnp0!l!O zFf>}$=!7$;E+8y-i78v*&#(~khz$xBX5JxeuB~P_Sg?KN&hf1ONcyePqdKFze&X_epktLU zEe-ijI;81kx}zu~8A&m1E!KPwQoT}!Bu*rVy4 z@!7rboc2w^%j)+f!66)a{S%g6n*}qV3r9+8qw6%~yt-S-e$+2EZ@Dh+hHwS6K49(&x~VVHxD~B}56o z4W8P`X=tS6EPExJB#(Xx(wmx|_S)6a`%VSk+%qWCY(m0~VNJky4Ls4>Bx=5SquBtY z`Lg@cA7H50dX~IKIffiQM>r9SddaGhjeGMQQHK4H8;3?B;Xvp!s~AQ?d3Gi9)BZp> ze<7eyhy*}VphhmX`;9{f(@xo8^RB8FRw2s{bT_gE;64V)7Hud(ldYO4A6jRg=P z(xf*51rmFK>c=-@Whu z)_d<}ot3qcHD}JAJ+s?y@0s(!H}Gaz$Rl-G#^&;JZ8502*y{~a&yhi>tYA;gjM$2A zonFMf0feeaGobyUEFcnfPJv|r0}?Gf*!W2;@EmE`Dj0VgZAAKcsI=csPwruFlEJ-Q;8A;Vu|IxqzFt?$WE$S%qX{w+@xL4dqTv zb3;3Do$guc9(gX7Qgn}>@xUf^+Ag+Fl}JiYb%jT{`B{D9e?F57&?FEWik8{^RuJNS z3Hvnw>p}uBVAO4DpnjJQB~1)%pRSQ~VoR!V#MaS;3)O{6R0_%UA6{nS4(zOX=Dcfu zi9(SsLYTkU$*YEs0(Rn*cC5n>*R#}>Jdq0S<{)f%@Q}Ql<@3wKkpr?#NvFFtdU7SJ zwW>#}cPZLcdT>24ba~0W_icY%hg@m`c+NsxZyE^iLHKq8^kM!$;SqqEa5%U!nUkk< z62R&_+?-aUN9{*jSJ$mSweXd@FV)Acj8JkaGpVZSlr1HJiWzFBi))O+?&BjunM=~I zcm$+;Jx5C%_EzR<4t5q3KE#V{_A*XQrA8eeudYX!fjzR~4jVy%7nnuT_0{Y*byhu5 z&yI@=qA%f+XfD&O4G*8(_w_rjwmX{1?E{FxLo!z9G2OC9SDz~?qBb_7RPg{wv2AN& z*r@CAuY&0Ov69@XR6)Tz#`{>?!$qR71hKRY!cwZ+bJKR5;Wk2M!_G7C?#SG}y#r5Y zt0a*k&j+0&2bWl-9CNbDOUl!OIW7`kViL$ZVgALn5t+0>kaYm^CUQVEC)u=~>wN;5 z!bdMZnMDOaq?_dad-J`FhX*<~u*av0E>8L2RyHz18i72HaH24n9>&u-C4 zNwXMuV%F1$WqC|7!KU06dtZ*4T0^IFRS5Yq#h~+;quR}J$}F-|x0#RkhMK#>tcBi7 z?RQB9q9j_d_VHZI%Sy#Yq_Tzw1ruo74cjcOsiRc+1XJOKq z`GuyHSTV=>=>U$B>Vo*RQsj`H%TxrPwBGpKqDRwKz~SSlx#{dpzOVb;d}Cj^eT}Rw zEL@FOa!F>T?>OHbJEpJRH`{uueRpQL(qtxbM|!)3$hre82E18y8WnlGHw4Pm03a5r zZ!=Jr6KtMPx_z|d29{z)SX<&jv&BqjXMC%h;b`1V>spMS`WM7<6aYU7O*5cv3Tp0B zJ9}j!)|5=fX~i7`b0sFcH^Q|_2LhVRn1ESQ@D!3T}w+{nTg1gUh9*oRbd9! zTuWF^4l*%j$8>B|@)Snz{;cN3%2BDO{#3=rWOKWEu}7~q!L!I_KS6m|>~%i?uO_Eb ztM5o65D5ECKFfik)t#E&3%|u)%WBXMieE$^7g^jVBXz78gdDNs&1))3bu%-x`Pwx{ zWhS}8mzDv{v$PdEJn?4BeHIfi>|bo)Es36rF)I0x+7=ZDeBO2;5uAVv&c{c5{! zvC;^`fZ!yjeB{15-6_5qS{}d#y=BKfw}c&HjlFI3^ZgkQS=qD5MO;UG>%mchFr&!o zQ>V<98Eo)?a{E^!JJSc9wUCpR9NZLF=kf011^~qt%hj5%%=nsqvi(-3fJdipa2Ldj zEr0ts&3j{nifYp6?4i56hX?S`a5%;%Pg**3%gd40+n;_9ap&f_yotBH&0*lQ!Y>P4 zfI7qb>%wSN5q_BguEX~RB^z^QCtV_zvgPUqPx7ox zFr<>(Sxp3-a-N9yFQqf7H$f11M>e!5s-r%Ab%G*g9Smtup}cgr`~|T z1-wT5Z{CmYqR?<5{p4iK-Ju0@3Xn7X^C2Jgq%S5mWw|j(8(uQ}#jNS%%oSJ7oka;i zE(OtPoAp1W(;0oCHr{tPZF9ud^r0_&2j0q`&oe@pW>?H|%vQA_aa9ZpMAhr@pWfGv z>b8aGEfq{ zpIG!y7XtWamkqzpWII-Te?x}SN>kU=2Qk^}0_?eMp8g6q^@jhctZ5b9&?(vtV zx3#YhTX&b~j;oJP9Z%age`zY}?%qlsR8NS_I1lar^0oC(CCBMq!S>#t+>=9|$-LIa z7X}fZW?a2etW>hVgXbT}c%!b=@T`DzYgM8R$bie0IPCMtox|T+yP^5sU(+lDcBTz2xIvJ$cG zqd;zN*8ee?6C|q(&*=DP|6yT}whTLBl`y30)<-9Ax%6p52R#Tvo4eVQ2y|)be2@Jt z>@-(gm`8o9Bj@ES?|X{G7flKFPP{?NoYRtxtnGA`ycReMKllDLCm^u#W=G?}%>F{y zw5o{T>A-8pTb{%b-+H4d+FT+f-zYAnve}1k5^x(!q{RNhuEKds=Q&wlI7{e{SYk$U zI4{?Ww(y}=1y`}+g@x;$JaLawTUif!zT3iJEp$|#?px~IAGV)nU7M^=l74Iawqc`* z4(-ZkUc1+2fNo5G^R9_6JC~c4r`<|L0q`w7-wHeo()1=49voM7=E|pAg$P-}+@lsq z-b_JquN?YG2YC@X56*cLZP)D=_0qw+QSN7z=6fe^AAC~Vg3W?0KIOXlmF#iC6dJ_4 z+mSIDnsV!=a`m>9jJDpvx1`iQ#um%OK9*lQE$@NofZCb*3DO=SE2aMSGh#GZ@8B>* z!?pMu+0-?R#!rrOsu@dWKN>X2UmrHG*M)&)Ui&QpVum9$AX)CB4@^@PaJfJRL8~A+ z(RLI$CJ4m+6+&${&mJ5ha`F6acd96yGnAU4^^af6{EpD6L zlY0M_)%%5L;?i-#*gE}e$@>oqB4h7%3+BlPSK@kC(&^Y@WHKfTNFBTt%Dw1^KMf9Fp#WdG>!QbI#vMUG&8hPq8Q zcxd&Wm_dip-aEY&Dp<{KR?0}Av1(^w?=38@`4&H<#esWuK5T7lJnVq7K@7Z=B^A^j z3MpGGe}@XDcsD8NSM@*z(V5Lz^!Iv)d63`~uj>z(B7QeNe=a;5+x^2DN8`itA%(~_ zx#@jMW0$SE!a7@{ZT#}$#QV;uwih7|Dg8N!xK^{E>$G__Bqhuw37%V{LDxI{S{O*X zj3_2>N7Yk8UT>5k!i#%dRdew;bC4$UYu{C%OFU9ySPKy9!haCzL$C zc8KHo!@%?}1$R_j_)_P?$oll|p>p63o(%yjr3k9wE5#2iEEk@r-sim4*GJ4O&pAIC z{Ah>~um2#mz7}i~F6notnmY&f*0uNV(jP`DW}DC`T}0M&j`VYMX~BvSoc#%m#>U$p zO$vg@&?|^T?ZBSflF>%>sY;?5<5FS|>rjLqBv zSXf){>4d>$zux8|ogh8KlbZODr0FbLQw_PsGC|ldO&Ci*s6BRH15~EIMB){_L0IW~ zn;SR0?&EcaYPPTgt&R7jjkjyfqxZ4gqClr9qOP1)MM8*93j%2KA8M4as(s8$$%Dd;bO663iz59J530iKj(rSP9~o)IpqT5=i>XoaYtI+*Me>QHJ zM?4qNK^bkFmP50V-lU*HBI;iyoCFt=eDC|xH88jv(}l(tH$n<^ba$xwg#QlD^Ogb|2@hh}8YmPN5 zc6BGVzo4SQm{rouOe4#8vtQ=O7DD=IA&5Otjo;7pzaDe={g%B%P%#$+! z6?`EAd`VzLGva}r@&W?X^tAOGG*A~NH1(4KFFG@SdVxf;`4xXX4~S`E&nZ4@Na8tT zj(|;`8yddC@zZ83`LisH4U@LgqA}xH`LM~T8IXQNC}MhKW+GU0;mjHY&<|8XN@<#H zlO$(Kbziw5?s~%`TuwI>ZnpNv_5WQ|Wa9je9D?_SNIXsa5FxiYzvatBw#MyA!$*=M zIY>__b^JqVkw3inYbg{MV^mu2uB^aIK^20}nax=Lt6q-Ir$LGkoggPKtUMVTPe6JH zg1v_d+2b}H#;NG03l%E=6q6c(cZk%s>>IrzRP{c z3lTNfM$^y}+$s;gWWR%G@I7ORnXG^A z17O)eY3^5QF;fjo9W9~BLHR7|YIyI8w349@qw(7%T}(}vc!~cQ^nj*V)arKt zEq!UW%W%OfhXE1N$;!!#CXF?ap~}kBG_2shKaw`}yPQ|O@46wCRaXa;jT3||wF2=e z$&o~dY&dn`+-6Zn5SIipVi$O}7+@9+&<|Ts%KR+WMP2WwOm7?SD*Dm zb1DeG6B07od2d@^=;0!sz(+wHT?)T1-qnDjO`pm$`SlM5z|%UidhNYoaA@UPSLtvv zwq5Z-eZW%e@Y}_St=;-A*fUAFu5*(3%jlY?zjJbqFFYuBM$Y;|IPrd>NQeruo%{Z6 zS>>JOkt=35YK*EFuRbg{J{>Tm?b*D+M%RNybujl6&*%)15(w>sUumcc8Hdc|lda_T zb|^SA4IKEqh93gYGT93XRs0ad!2(OzA18#36UHHtw+lMXF&oF&LN7htOW#_ABPtZFnvBpG8Sg>vZxb~v=Zn}nxx1kSm2O&s%?n{tTx7~Z3t(S? z+Fm{Ei@oCLz$K*#%8r}LrSDkg-{3EuUmP#&WJgylO~Yz!DOIb%>nd4VJBwl{S-0B#I8M?Z4^8 zMa`vZjN;)$uFF1+#QM)AKqy-)KED5Su|ftvi^Ahe#_Lm5-y>7gcN$Vl-9Bec$~L)@ zvE2bW12VMccL_LVHpZYfy`Put)zCdz`&I?93YYogcjsJ_ftya`Dwo-$9^F9yxv9FS zwDBfEqNyY}bFr6JL$t3)yQX@0gc13w;UNx(c#LMoEhKMH!BGLO=e3ZUk`JlgX z5l~cmKCk$u=CqZH`MD%y!@w!RJ0%Q)65*yoM%Nr9hoG@{*tEBnD(%1ykJQQCM0DY8*@ZmTvI1))y|S=0}7CPPaI^ff0Dct3C-y_l^ML)^cv|!Uw5!areFLjZcE886%Sd=0tW}o z_C8a6fokdOIus~_zt|jjh5ocfXSE&k&4oMkxI;mmJJFpc6Hesxr>@zH!>Cb9Ii@J; zXJ+1<`18yWS($VzE0|MIPw!x@WUQl3reK1;098UFtUii9Q{ev1L9 zlQ)PqKY|A?6hVGv^qRZ9Am(~W;*cYfX?c$XKAn;n6#}DDLDl!mvjb++;QeO^3QeaS_>X)fKY$}70|*U5$E$VN6FdmTYxeQ=xO0cFseoYj#3qF1~lH`0L*^o zkecROnx$poX3Yp%^dv{Im8vX?{q5k2FRtFMAoF1YZVeIp0q)y>59OU3Nny+GRwalJ z{r+729}>5Fh`Ba)la=1VrKo?m>J)A5gkl314>_V+T_jUo!h-U5W1@(oq@?3yQK!#^ zFtrsuy85)91_6jo`8)iq&p=+mZ)UB(BUF)>!J7>Ogupp#oxdb`!7rZP(<}3r&k*!w z;3{H-sfa_AuzfZ~wem)x9e|nz=_Roa<-#S9; zpkvO>$hc0_b>#K%-D#II!pRzs=@4{f+JcOa&zlBntyz!a&1e`{LxS` z6IQvOQ7L{9L}p~ij<7;d;^yB=pW5~USDq6c^o?}^Klmm(8o&mT(u3GQp`-?(rc`Bj zp6uSOZAwj4#b%QVy0_iT0d2Ub56nEE)rQ9=f%<=AyNx$k1kTosf@cuSx+qfFw2NUi z)m_qJYneOJ?YCdatxxz!SS!}NxIp1iF^&o{>*SV75}6xaS$N5K{Jdx{$x~6g?_&a@ z{>OmJczL+w{_k~?C9H~;eoR~$vc0ix*qxb(W#pRFly6>EeMjP`o??h%A1^h_DB9^H zW4T=soCUHxcQAcWAb<6P@?4GM?UU5~mBhW8vgglrJ>6F$)CL|pJlsvun)8#pf4IHS zw`bO2HXTJ}tRZZ;<+Z0F8?zxP>iA@cBT`$?X-$1Yt5 z9i$eD7dO15DuPw6J~O-?as2I#?T^PTqj~mrS`Dlhts8BhKkwrO#|p`X1}x%g*t3__ znMKKVFo_;_Zi#>S93pA5K7wg*S}onIfigtTiCMJ;#|b@zB3lh&c?6|+!6hs-g(;KH zD?sI5hez^bL%r8hBfoxCW@e&JrjlT-TDcY?8S-_CJHrdpOX>YVDi4hV$E{vxkGN(cF1N)g zn6yhBzdm|E8nPI*)zBKmIp$<$H#Tb7PRr_aKt*=6u^!E}&06>P9HjizB?FoatqZg@ z&X+%PweZ_Tp~mQjGi)+5*FJ-85-?mhW@qlDag0>oSzN?j?9k`8)jg*fOsY1JEly?H zejy~(!ovLd=3-LQHd9ssiXeggb9Q%|J*YNGBu+AE`RPP~2&h)F9+sgWztx4_e?JYXLB;>#35N|1vvKO3G3-!3FuejCaFyudzmxY49s1BlU~g zv*xvIQOAV=qg^ax0j-{D(Srq1(i;`79wYkPou;g+uQPHJ@oSYMLs4m|TQSXmy@f8( zp?a;wQ~u<-n3D{9WQOrJv#`e#73cY*f;N&1#DhhyBX3|5ErCzo*2!{>I_Nx8z{b$4 zDYG=222WI-f#gsDOH!N@`6#{JAHQ~8Y9)Pg;-zf^1!yj_;AV!N>Nu^8M*4p#Tjj0R zyF75acc9_(%;xk}_DY+Lk}7Sh$;$l#y10*7U(~3@-<@|-R^~{h9y_iw8=1ugwq!Im z9tzEU_1N?d{3-Z*MzX5fSAj`hPF{N7@Ou(>KCRwM3-c@WA~EG-WG zNbv?1mAo5L4T~~Xrr>;zyRZ3G$Lsvtu2#9Rc_7YcWZQ*-GPt>9HLQ>VZJnhEu?h+&W z#`0zI%PfHrgpe$yzXqoF!N?WT6hcI9F5jH4y0ZwP`qd_Q>|EH~Ba<&Nyu>X@-1?O- zxA5GJ{&-isfOh$3F;SE4=f0pXP9<)%`m!EJ+n#+nd}C;*oQPtz7gomuXo~gO$FnvX{HGjA;#g5*%7O zLqOI24JLui&5e})A>k3Kk8HyIZb{OwaerqmRa2`Z_7dWSJO9@#T4JG;;lX0;N(zB; z$#aJR@r5y29U~ai)nQqt28YF;{ax=px2rP4oZ!8+d0G+7Fn=HOS+FsGpa24G;4~KJjM1 zC!epc<^Fp4=S-9sH_ZBw0&SRMpp zvSeHRWr>c!t=!&yrjGite60{JG#B;_f%??RfDJ6K2}+G#U}h!Brp~H9uZ9di6u66O z-k=xqVGwh=GgliIw>7~4ML?ZiPDAO~tMpOnm$mCP`7)D0iz5=38p z`ndC${*X&`y$D#a5NOCD`rZC7Y=0Iy%%;&?=#rMGD0>bnr?;B!WGyaG5PIgLXzN~6 zZFza+O;0@y?H7h6FrH@*)8@^2Y@wYX%hf~t*G;*H;#^aqhfXI*fx6@q4Fwe^6U{}^>{atW5+VG~DzScvz zhmNZUN5u{M=jXCF` zEjK;8(EaAapg8-+b`Sa80QEWypB}0rthw&(8AvI~=nx^^i3Zm`*y#(sC30({z{Ou) zDbP~Dhik8(gIW3qFyD5x4U(_Ldo{El>{LD|$U5uEq5tev`bIjHva>?T{)QGW4Y{ht z(^R<}EkUNyin59dup^L%hqL7e*bKO;K6>p_8XIYkY5f#0-yAKu>MKLDScSeYj{DIC z;AlDH@_j=J+oGr0AN}*G^=J(oWKKd0Y?PTZ3Am-?^zrjUj zEGjJQvbRqf%deT#tgfY**dg^quA1n#^2$)_K+or@^69!CYP049vS{I6QS$KIxZGn* z^KIwyitHcXTajx%0+p$sJU$A{E=(l|+>zh251sN=VrV*8`;gm5AkCgjLipDiLWzUO z0*=9Nq=irLwJJP%?C5Aau{t>=-6`piQBhH$16K}^d-`eyasP34zJo4QM=Oass6bas zujnL;k>>TzUfrsclcS{z-~H~H+@@`oE2#m+R;_f2GZQn{B7US&ig&v13s`$fJD{mQ zn;c5C_>J5;o;@&o$km7`uWT_E98)?YpiAkiTw&XRPw(f+PP2Dj8$g{ka`D#NH^jmh3kGur4kMi0}CGct`ca@})}^TE!Z zP>o0}=Y40u1S%uH(p5oyS-NzXTT^22g!V*Hci7jxz&aMs-6i=-ry|>di)4HSQrhR- zplwUS{AzpLuPf2QIr1LB;8tD@E_eCqAIF6rs?+BVTa_>m<)HL70J-GKZ?l8&06(sO z0@18@Y_}*Yp1)?Bd-b&7@>7i5=5RnnmFO==zNGF&T=C>y{4eIfg6y1QTu&KMDDyy? zK5LEA8R&0l^yY>WYUG)XlvWy>aWD#Of$5e!_~{1?c>H8-eTQQ*r=_(uFtC$F!ez3S zO(UUwWrCWQ2-0zmq|drE`v7_YK-NW3&Ra)@+34~}WM_YYif zH!PqhAZCig7yjgA_t^uK%JQ|3@YdmP>r%DMArvF!n0f73yEv^%;jf(uE9Y7Pvf7cfO?KfL3>7BoE zwT*YGCU^KF3Z4JKZ4o_&ge+EFmyP9^D7ZKu|Hab)CY^>6pO9l=RYWul>G?MGe%dp- zfvziwwn4zcrG2x|ucf$iBb}WC58cg|^-Q0+#pk3teggve+ZF0PfMUo_Sq;Hv|1kzC{{gc64}aG}Q={yh@zFSbLjNEz zr}6n|ulSQb0CoQrGLrZAlHU1u(l2Gw{2l8*aK>N$OyG}H0yq8}J5hsZ^B)xE-|iRv z5h#+mNdEJ9oEp*wor3%6{bSoyxM_^z)$!ZFS!9%0691i0$Bd?p8jUzgR{JbreK-2d zUie;`B1&vi`;I-&etpvigrYUyi=Iyso}?Pc5qC|kpmllCKR85TC>~ZmwNDQ<|1j%x zT!9oruBTYUKSZl@{ligFbAG|lDX~zhuq%HO0pR@DqJ?H zgv-x{Ng)~{I(yr7WQSVeh!7;-X14Id!ap|mw^qDx`-0VRI8kMAqV K<=-=S`@aC(B+S78 literal 0 HcmV?d00001 diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 0000000..c2a49f4 --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Allow: / From cadab8defd169e1806f5ff3d3c05510285829183 Mon Sep 17 00:00:00 2001 From: Minh Vu Date: Sat, 27 Jun 2026 09:00:16 +0700 Subject: [PATCH 03/20] feat: extend curriculum manifest with SEO fields --- scripts/generate-curriculum-manifest.mjs | 132 +- .../curriculumSeoManifest.generated.json | 3099 +++++++++++++++++ src/contents/curriculumManifest.generated.ts | 1338 +++++++ .../async-and-await-semantics-in-csharp.md | 3 + 4 files changed, 4569 insertions(+), 3 deletions(-) create mode 100644 shared/content/curriculumSeoManifest.generated.json diff --git a/scripts/generate-curriculum-manifest.mjs b/scripts/generate-curriculum-manifest.mjs index dcee7f8..fd30fb7 100644 --- a/scripts/generate-curriculum-manifest.mjs +++ b/scripts/generate-curriculum-manifest.mjs @@ -11,7 +11,16 @@ const outputPath = path.join( "contents", "curriculumManifest.generated.ts" ); +const seoOutputPath = path.join( + projectRoot, + "shared", + "content", + "curriculumSeoManifest.generated.json" +); const outputRelativePath = toPosix(path.relative(projectRoot, outputPath)); +const seoOutputRelativePath = toPosix( + path.relative(projectRoot, seoOutputPath) +); const v1ContentRoot = "/src/contents/v1/"; const specialLabels = { @@ -53,6 +62,7 @@ const markdownFiles = collectMarkdownFiles(contentRoot) .sort((left, right) => left.relativePath.localeCompare(right.relativePath)); const usedTopicIds = new Set(); +const usedCanonicalPaths = new Map(); const entries = markdownFiles.map(({ absolutePath, relativePath }) => { const markdown = fs.readFileSync(absolutePath, "utf8"); const segments = relativePath.split("/"); @@ -80,6 +90,14 @@ const entries = markdownFiles.map(({ absolutePath, relativePath }) => { usedTopicIds, relativePath ); + const customCanonicalPath = frontmatter.canonicalPath?.trim(); + const customSeoDescription = frontmatter.seoDescription?.trim(); + const customSeoTitle = frontmatter.seoTitle?.trim(); + const canonicalPath = customCanonicalPath || `/content/${id}/`; + + validateCanonicalPath(canonicalPath, relativePath); + registerCanonicalPath(canonicalPath, relativePath, usedCanonicalPaths); + const physicalFolderSegments = segments.slice(0, -1); const folders = physicalFolderSegments.map((folderSegment, index) => { const folderPath = physicalFolderSegments.slice(0, index + 1).join("/"); @@ -97,6 +115,8 @@ const entries = markdownFiles.map(({ absolutePath, relativePath }) => { const questionLevelCounts = getQuestionLevelCounts(questions); return { + canonicalPath, + canonicalPathSource: customCanonicalPath ? "frontmatter" : "generated", category, contentPath: `${v1ContentRoot}${relativePath}`, folders, @@ -108,6 +128,14 @@ const entries = markdownFiles.map(({ absolutePath, relativePath }) => { ), questionLevelCounts, sampleQuestions: getSampleQuestions(questions), + seoDescription: + customSeoDescription || + `Learn ${subtopicTitle} for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.`, + seoDescriptionSource: customSeoDescription ? "frontmatter" : "fallback", + seoTitle: + customSeoTitle || + `${subtopicTitle} Interview Questions | DEV_NET_CORE`, + seoTitleSource: customSeoTitle ? "frontmatter" : "fallback", subtopic: subtopicTitle, title: subtopicTitle, topic: topicTitle, @@ -115,9 +143,30 @@ const entries = markdownFiles.map(({ absolutePath, relativePath }) => { }; }); -const output = `// This file is generated by scripts/generate-curriculum-manifest.mjs. +const missingSeoTitleCount = entries.filter( + (entry) => entry.seoTitleSource === "fallback" +).length; +const missingSeoDescriptionCount = entries.filter( + (entry) => entry.seoDescriptionSource === "fallback" +).length; + +if (missingSeoTitleCount > 0 || missingSeoDescriptionCount > 0) { + console.warn( + "SEO metadata transition fallbacks are active: " + + `${missingSeoTitleCount} title(s), ` + + `${missingSeoDescriptionCount} description(s).` + ); +} + +const typescriptOutput = `// This file is generated by scripts/generate-curriculum-manifest.mjs. // Do not edit it by hand. +export type CurriculumManifestCanonicalPathSource = + | "frontmatter" + | "generated"; + +export type CurriculumManifestSeoTextSource = "fallback" | "frontmatter"; + export type CurriculumManifestFolder = { path: string; title: string; @@ -137,6 +186,8 @@ export type CurriculumManifestSampleQuestion = { }; export type CurriculumManifestEntry = { + canonicalPath: string; + canonicalPathSource: CurriculumManifestCanonicalPathSource; category: string; contentPath: string; folders: CurriculumManifestFolder[]; @@ -145,6 +196,10 @@ export type CurriculumManifestEntry = { questionCount: number; questionLevelCounts: CurriculumManifestQuestionLevelCount[]; sampleQuestions: CurriculumManifestSampleQuestion[]; + seoDescription: string; + seoDescriptionSource: CurriculumManifestSeoTextSource; + seoTitle: string; + seoTitleSource: CurriculumManifestSeoTextSource; subtopic: string; title: string; topic: string; @@ -158,9 +213,37 @@ export const curriculumManifest: CurriculumManifestEntry[] = ${JSON.stringify( )}; `; +const seoOutput = `${JSON.stringify( + { + schemaVersion: 1, + entries: entries.map((entry) => ({ + canonicalPath: entry.canonicalPath, + canonicalPathSource: entry.canonicalPathSource, + category: entry.category, + contentPath: entry.contentPath.replace(/^\/+/, ""), + id: entry.id, + seoDescription: entry.seoDescription, + seoDescriptionSource: entry.seoDescriptionSource, + seoTitle: entry.seoTitle, + seoTitleSource: entry.seoTitleSource, + subtopic: entry.subtopic, + title: entry.title, + topic: entry.topic, + })), + }, + null, + 2 +)} +`; + fs.mkdirSync(path.dirname(outputPath), { recursive: true }); -fs.writeFileSync(outputPath, output); +fs.writeFileSync(outputPath, typescriptOutput); +fs.mkdirSync(path.dirname(seoOutputPath), { recursive: true }); +fs.writeFileSync(seoOutputPath, seoOutput); console.log(`Generated ${entries.length} curriculum entries in ${outputRelativePath}`); +console.log( + `Generated ${entries.length} curriculum SEO entries in ${seoOutputRelativePath}` +); function collectMarkdownFiles(directory) { if (!fs.existsSync(directory)) { @@ -202,7 +285,10 @@ function parseMarkdownFrontmatter(markdown) { key === "id" || key === "topic" || key === "subtopic" || - key === "category" + key === "category" || + key === "seoTitle" || + key === "seoDescription" || + key === "canonicalPath" ) { frontmatter[key] = value; } @@ -560,6 +646,46 @@ function getUniqueTopicId(requestedId, fallbackId, usedTopicIds, contentPath) { return uniqueId; } +function validateCanonicalPath(canonicalPath, contentPath) { + if (!canonicalPath.startsWith("/")) { + throw new Error( + `Canonical path "${canonicalPath}" in ${contentPath} must start with "/".` + ); + } + + if (!canonicalPath.endsWith("/")) { + throw new Error( + `Canonical path "${canonicalPath}" in ${contentPath} must use a trailing slash.` + ); + } + + if ( + canonicalPath.includes("?") || + canonicalPath.includes("#") || + canonicalPath.includes("\\") || + canonicalPath.includes("//") || + !/^\/content\/[a-z0-9][a-z0-9-]*\/$/.test(canonicalPath) + ) { + throw new Error( + `Canonical path "${canonicalPath}" in ${contentPath} must match ` + + '"/content/topic-slug/".' + ); + } +} + +function registerCanonicalPath(canonicalPath, contentPath, usedCanonicalPaths) { + const existingContentPath = usedCanonicalPaths.get(canonicalPath); + + if (existingContentPath) { + throw new Error( + `Duplicate canonical path "${canonicalPath}" found in ` + + `${existingContentPath} and ${contentPath}.` + ); + } + + usedCanonicalPaths.set(canonicalPath, contentPath); +} + function slugify(value) { return ( value diff --git a/shared/content/curriculumSeoManifest.generated.json b/shared/content/curriculumSeoManifest.generated.json new file mode 100644 index 0000000..34cfa94 --- /dev/null +++ b/shared/content/curriculumSeoManifest.generated.json @@ -0,0 +1,3099 @@ +{ + "schemaVersion": 1, + "entries": [ + { + "canonicalPath": "/content/alert-rules-action-groups-and-incident-response-workflows/", + "canonicalPathSource": "generated", + "category": "Azure", + "contentPath": "src/contents/v1/Azure/alert-rules-action-groups-and-incident-response-workflows.md", + "id": "alert-rules-action-groups-and-incident-response-workflows", + "seoDescription": "Learn Alert rules, action groups, and incident response workflows for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Alert rules, action groups, and incident response workflows Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Alert rules, action groups, and incident response workflows", + "title": "Alert rules, action groups, and incident response workflows", + "topic": "Monitoring, tracing, and incident response on Azure" + }, + { + "canonicalPath": "/content/app-service-plans-paas-web-hosting-fit/", + "canonicalPathSource": "generated", + "category": "Azure", + "contentPath": "src/contents/v1/Azure/app-service-plans-paas-web-hosting-fit.md", + "id": "app-service-plans-paas-web-hosting-fit", + "seoDescription": "Learn App Service plans and PaaS web hosting fit for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "App Service plans and PaaS web hosting fit Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "App Service plans and PaaS web hosting fit", + "title": "App Service plans and PaaS web hosting fit", + "topic": "Azure compute choices and hosting models" + }, + { + "canonicalPath": "/content/application-gateway-and-waf-concepts/", + "canonicalPathSource": "generated", + "category": "Azure", + "contentPath": "src/contents/v1/Azure/application-gateway-and-waf-concepts.md", + "id": "application-gateway-and-waf-concepts", + "seoDescription": "Learn Application Gateway and WAF concepts for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Application Gateway and WAF concepts Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Application Gateway and WAF concepts", + "title": "Application Gateway and WAF concepts", + "topic": "Networking, API edge, and secure connectivity" + }, + { + "canonicalPath": "/content/application-insights-and-opentelemetry-aligned-observability/", + "canonicalPathSource": "generated", + "category": "Azure", + "contentPath": "src/contents/v1/Azure/application-insights-and-opentelemetry-aligned-observability.md", + "id": "application-insights-and-opentelemetry-aligned-observability", + "seoDescription": "Learn Application Insights and OpenTelemetry-aligned observability for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Application Insights and OpenTelemetry-aligned observability Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Application Insights and OpenTelemetry-aligned observability", + "title": "Application Insights and OpenTelemetry-aligned observability", + "topic": "Monitoring, tracing, and incident response on Azure" + }, + { + "canonicalPath": "/content/azure-api-management-and-policy-based-gateways/", + "canonicalPathSource": "generated", + "category": "Azure", + "contentPath": "src/contents/v1/Azure/azure-api-management-and-policy-based-gateways.md", + "id": "azure-api-management-and-policy-based-gateways", + "seoDescription": "Learn Azure API Management and policy-based gateways for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Azure API Management and policy-based gateways Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Azure API Management and policy-based gateways", + "title": "Azure API Management and policy-based gateways", + "topic": "Networking, API edge, and secure connectivity" + }, + { + "canonicalPath": "/content/azure-cache-for-redis-low-latency-reads-and-session-state-scenarios/", + "canonicalPathSource": "generated", + "category": "Azure", + "contentPath": "src/contents/v1/Azure/azure-cache-for-redis-low-latency-reads-and-session-state-scenarios.md", + "id": "azure-cache-for-redis-low-latency-reads-and-session-state-scenarios", + "seoDescription": "Learn Azure Cache for Redis for low-latency reads and session/state scenarios for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Azure Cache for Redis for low-latency reads and session/state scenarios Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Azure Cache for Redis for low-latency reads and session/state scenarios", + "title": "Azure Cache for Redis for low-latency reads and session/state scenarios", + "topic": "Azure data, storage, and caching services" + }, + { + "canonicalPath": "/content/azure-container-apps-for-containerized-apis-and-background-workloads/", + "canonicalPathSource": "generated", + "category": "Azure", + "contentPath": "src/contents/v1/Azure/azure-container-apps-for-containerized-apis-and-background-workloads.md", + "id": "azure-container-apps-for-containerized-apis-and-background-workloads", + "seoDescription": "Learn Azure Container Apps for containerized APIs and background workloads for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Azure Container Apps for containerized APIs and background workloads Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Azure Container Apps for containerized APIs and background workloads", + "title": "Azure Container Apps for containerized APIs and background workloads", + "topic": "Azure compute choices and hosting models" + }, + { + "canonicalPath": "/content/azure-functions-hosting-options-and-current-scale-guidance/", + "canonicalPathSource": "generated", + "category": "Azure", + "contentPath": "src/contents/v1/Azure/azure-functions-hosting-options-and-current-scale-guidance.md", + "id": "azure-functions-hosting-options-and-current-scale-guidance", + "seoDescription": "Learn Azure Functions Hosting Options and Current Scale Guidance for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Azure Functions Hosting Options and Current Scale Guidance Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Azure Functions Hosting Options and Current Scale Guidance", + "title": "Azure Functions Hosting Options and Current Scale Guidance", + "topic": "Azure compute choices and hosting models" + }, + { + "canonicalPath": "/content/azure-key-vault-for-secrets-certificates-and-connection-settings/", + "canonicalPathSource": "generated", + "category": "Azure", + "contentPath": "src/contents/v1/Azure/azure-key-vault-for-secrets-certificates-and-connection-settings.md", + "id": "azure-key-vault-for-secrets-certificates-and-connection-settings", + "seoDescription": "Learn Azure Key Vault for secrets, certificates, and connection settings for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Azure Key Vault for secrets, certificates, and connection settings Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Azure Key Vault for secrets, certificates, and connection settings", + "title": "Azure Key Vault for secrets, certificates, and connection settings", + "topic": "Identity, secrets, and access control" + }, + { + "canonicalPath": "/content/azure-sql-database-tiers-scaling-serverless-options-and-failover-groups/", + "canonicalPathSource": "generated", + "category": "Azure", + "contentPath": "src/contents/v1/Azure/azure-sql-database-tiers-scaling-serverless-options-and-failover-groups.md", + "id": "azure-sql-database-tiers-scaling-serverless-options-and-failover-groups", + "seoDescription": "Learn Azure SQL Database tiers, scaling, serverless options, and failover groups for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Azure SQL Database tiers, scaling, serverless options, and failover groups Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Azure SQL Database tiers, scaling, serverless options, and failover groups", + "title": "Azure SQL Database tiers, scaling, serverless options, and failover groups", + "topic": "Azure data, storage, and caching services" + }, + { + "canonicalPath": "/content/bicep-fundamentals-modules-and-reusable-environment-definitions/", + "canonicalPathSource": "generated", + "category": "Azure", + "contentPath": "src/contents/v1/Azure/bicep-fundamentals-modules-and-reusable-environment-definitions.md", + "id": "bicep-fundamentals-modules-and-reusable-environment-definitions", + "seoDescription": "Learn Bicep fundamentals, modules, and reusable environment definitions for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Bicep fundamentals, modules, and reusable environment definitions Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Bicep fundamentals, modules, and reusable environment definitions", + "title": "Bicep fundamentals, modules, and reusable environment definitions", + "topic": "Delivery, infrastructure as code, scaling, and cost control" + }, + { + "canonicalPath": "/content/bindings-connection-configuration-dependency-injection-logging-and-local-development/", + "canonicalPathSource": "generated", + "category": "Azure", + "contentPath": "src/contents/v1/Azure/bindings-connection-configuration-dependency-injection-logging-and-local-development.md", + "id": "bindings-connection-configuration-dependency-injection-logging-and-local-development", + "seoDescription": "Learn Bindings, connection configuration, dependency injection, logging, and local development for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Bindings, connection configuration, dependency injection, logging, and local development Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Bindings, connection configuration, dependency injection, logging, and local development", + "title": "Bindings, connection configuration, dependency injection, logging, and local development", + "topic": "Azure Functions and Durable Functions" + }, + { + "canonicalPath": "/content/blob-storage-access-tiers-lifecycle-management-and-immutability/", + "canonicalPathSource": "generated", + "category": "Azure", + "contentPath": "src/contents/v1/Azure/blob-storage-access-tiers-lifecycle-management-and-immutability.md", + "id": "blob-storage-access-tiers-lifecycle-management-and-immutability", + "seoDescription": "Learn Blob Storage, access tiers, lifecycle management, and immutability for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Blob Storage, access tiers, lifecycle management, and immutability Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Blob Storage, access tiers, lifecycle management, and immutability", + "title": "Blob Storage, access tiers, lifecycle management, and immutability", + "topic": "Azure data, storage, and caching services" + }, + { + "canonicalPath": "/content/block-blobs-append-blobs-and-page-blobs/", + "canonicalPathSource": "generated", + "category": "Azure", + "contentPath": "src/contents/v1/Azure/block-blobs-append-blobs-and-page-blobs.md", + "id": "block-blobs-append-blobs-and-page-blobs", + "seoDescription": "Learn Block blobs, append blobs, and page blobs for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Block blobs, append blobs, and page blobs Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Block blobs, append blobs, and page blobs", + "title": "Block blobs, append blobs, and page blobs", + "topic": "Azure Blob Storage and file handling" + }, + { + "canonicalPath": "/content/choosing-queues-vs-topics-vs-event-notifications/", + "canonicalPathSource": "generated", + "category": "Azure", + "contentPath": "src/contents/v1/Azure/choosing-queues-vs-topics-vs-event-notifications.md", + "id": "choosing-queues-vs-topics-vs-event-notifications", + "seoDescription": "Learn Choosing queues vs topics vs event notifications for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Choosing queues vs topics vs event notifications Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Choosing queues vs topics vs event notifications", + "title": "Choosing queues vs topics vs event notifications", + "topic": "Messaging and event-driven Azure integration" + }, + { + "canonicalPath": "/content/cold-starts-hosting-plan-choices-timeout-configuration-and-scaling-behavior/", + "canonicalPathSource": "generated", + "category": "Azure", + "contentPath": "src/contents/v1/Azure/cold-starts-hosting-plan-choices-timeout-configuration-and-scaling-behavior.md", + "id": "cold-starts-hosting-plan-choices-timeout-configuration-and-scaling-behavior", + "seoDescription": "Learn Cold starts, hosting plan choices, timeout configuration, and scaling behavior for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Cold starts, hosting plan choices, timeout configuration, and scaling behavior Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Cold starts, hosting plan choices, timeout configuration, and scaling behavior", + "title": "Cold starts, hosting plan choices, timeout configuration, and scaling behavior", + "topic": "Azure Functions and Durable Functions" + }, + { + "canonicalPath": "/content/deployment-slots-rollout-safety-autoscaling-availability-zones-and-cost-aware-tiering/", + "canonicalPathSource": "generated", + "category": "Azure", + "contentPath": "src/contents/v1/Azure/deployment-slots-rollout-safety-autoscaling-availability-zones-and-cost-aware-tiering.md", + "id": "deployment-slots-rollout-safety-autoscaling-availability-zones-and-cost-aware-tiering", + "seoDescription": "Learn Deployment slots, rollout safety, autoscaling, availability zones, and cost-aware tiering for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Deployment slots, rollout safety, autoscaling, availability zones, and cost-aware tiering Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Deployment slots, rollout safety, autoscaling, availability zones, and cost-aware tiering", + "title": "Deployment slots, rollout safety, autoscaling, availability zones, and cost-aware tiering", + "topic": "Delivery, infrastructure as code, scaling, and cost control" + }, + { + "canonicalPath": "/content/dotnet-blob-clients-direct-browser-upload-and-separating-metadata-from-binary-content/", + "canonicalPathSource": "generated", + "category": "Azure", + "contentPath": "src/contents/v1/Azure/dotnet-blob-clients-direct-browser-upload-and-separating-metadata-from-binary-content.md", + "id": "dotnet-blob-clients-direct-browser-upload-and-separating-metadata-from-binary-content", + "seoDescription": "Learn .NET Blob clients, direct browser upload patterns, and storing metadata separately from binary content for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": ".NET Blob clients, direct browser upload patterns, and storing metadata separately from binary content Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": ".NET Blob clients, direct browser upload patterns, and storing metadata separately from binary content", + "title": ".NET Blob clients, direct browser upload patterns, and storing metadata separately from binary content", + "topic": "Azure Blob Storage and file handling" + }, + { + "canonicalPath": "/content/duplicate-detection-retries-and-poison-message-handling/", + "canonicalPathSource": "generated", + "category": "Azure", + "contentPath": "src/contents/v1/Azure/duplicate-detection-retries-and-poison-message-handling.md", + "id": "duplicate-detection-retries-and-poison-message-handling", + "seoDescription": "Learn Duplicate detection, retries, and poison-message handling for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Duplicate detection, retries, and poison-message handling Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Duplicate detection, retries, and poison-message handling", + "title": "Duplicate detection, retries, and poison-message handling", + "topic": "Messaging and event-driven Azure integration" + }, + { + "canonicalPath": "/content/durable-functions-orchestrators-activities-timers-external-events-and-entities/", + "canonicalPathSource": "generated", + "category": "Azure", + "contentPath": "src/contents/v1/Azure/durable-functions-orchestrators-activities-timers-external-events-and-entities.md", + "id": "durable-functions-orchestrators-activities-timers-external-events-and-entities", + "seoDescription": "Learn Durable Functions orchestrators, activity functions, durable timers, external events, and durable entities for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Durable Functions orchestrators, activity functions, durable timers, external events, and durable entities Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Durable Functions orchestrators, activity functions, durable timers, external events, and durable entities", + "title": "Durable Functions orchestrators, activity functions, durable timers, external events, and durable entities", + "topic": "Azure Functions and Durable Functions" + }, + { + "canonicalPath": "/content/event-grid-for-event-publication-and-fan-out/", + "canonicalPathSource": "generated", + "category": "Azure", + "contentPath": "src/contents/v1/Azure/event-grid-for-event-publication-and-fan-out.md", + "id": "event-grid-for-event-publication-and-fan-out", + "seoDescription": "Learn Event Grid for event publication and fan-out for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Event Grid for event publication and fan-out Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Event Grid for event publication and fan-out", + "title": "Event Grid for event publication and fan-out", + "topic": "Messaging and event-driven Azure integration" + }, + { + "canonicalPath": "/content/function-chaining-fan-out-fan-in-async-workflow-state-replay-and-compensation/", + "canonicalPathSource": "generated", + "category": "Azure", + "contentPath": "src/contents/v1/Azure/function-chaining-fan-out-fan-in-async-workflow-state-replay-and-compensation.md", + "id": "function-chaining-fan-out-fan-in-async-workflow-state-replay-and-compensation", + "seoDescription": "Learn Function chaining, fan-out/fan-in, async workflow state, replay-safe orchestrator code, and compensation for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Function chaining, fan-out/fan-in, async workflow state, replay-safe orchestrator code, and compensation Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Function chaining, fan-out/fan-in, async workflow state, replay-safe orchestrator code, and compensation", + "title": "Function chaining, fan-out/fan-in, async workflow state, replay-safe orchestrator code, and compensation", + "topic": "Azure Functions and Durable Functions" + }, + { + "canonicalPath": "/content/github-actions-or-azure-devops-for-build-test-deploy-and-infrastructure-steps/", + "canonicalPathSource": "generated", + "category": "Azure", + "contentPath": "src/contents/v1/Azure/github-actions-or-azure-devops-for-build-test-deploy-and-infrastructure-steps.md", + "id": "github-actions-or-azure-devops-for-build-test-deploy-and-infrastructure-steps", + "seoDescription": "Learn GitHub Actions or Azure DevOps for build, test, deploy, and infrastructure steps for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "GitHub Actions or Azure DevOps for build, test, deploy, and infrastructure steps Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "GitHub Actions or Azure DevOps for build, test, deploy, and infrastructure steps", + "title": "GitHub Actions or Azure DevOps for build, test, deploy, and infrastructure steps", + "topic": "Delivery, infrastructure as code, scaling, and cost control" + }, + { + "canonicalPath": "/content/http-timer-queue-service-bus-event-grid-and-blob-triggers/", + "canonicalPathSource": "generated", + "category": "Azure", + "contentPath": "src/contents/v1/Azure/http-timer-queue-service-bus-event-grid-and-blob-triggers.md", + "id": "http-timer-queue-service-bus-event-grid-and-blob-triggers", + "seoDescription": "Learn HTTP, timer, queue, Service Bus, Event Grid, and Blob triggers for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "HTTP, timer, queue, Service Bus, Event Grid, and Blob triggers Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "HTTP, timer, queue, Service Bus, Event Grid, and Blob triggers", + "title": "HTTP, timer, queue, Service Bus, Event Grid, and Blob triggers", + "topic": "Azure Functions and Durable Functions" + }, + { + "canonicalPath": "/content/in-process-vs-isolated-worker-model-for-dotnet-functions/", + "canonicalPathSource": "generated", + "category": "Azure", + "contentPath": "src/contents/v1/Azure/in-process-vs-isolated-worker-model-for-dotnet-functions.md", + "id": "in-process-vs-isolated-worker-model-for-dotnet-functions", + "seoDescription": "Learn In-process vs isolated worker model for .NET Functions for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "In-process vs isolated worker model for .NET Functions Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "In-process vs isolated worker model for .NET Functions", + "title": "In-process vs isolated worker model for .NET Functions", + "topic": "Azure Functions and Durable Functions" + }, + { + "canonicalPath": "/content/large-file-uploads-block-upload-retries-checksums-and-resumable-patterns/", + "canonicalPathSource": "generated", + "category": "Azure", + "contentPath": "src/contents/v1/Azure/large-file-uploads-block-upload-retries-checksums-and-resumable-patterns.md", + "id": "large-file-uploads-block-upload-retries-checksums-and-resumable-patterns", + "seoDescription": "Learn Large file uploads, block upload, retry behavior, checksums, and resumable upload patterns for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Large file uploads, block upload, retry behavior, checksums, and resumable upload patterns Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Large file uploads, block upload, retry behavior, checksums, and resumable upload patterns", + "title": "Large file uploads, block upload, retry behavior, checksums, and resumable upload patterns", + "topic": "Azure Blob Storage and file handling" + }, + { + "canonicalPath": "/content/lifecycle-management-soft-delete-versioning-immutability-encryption-and-retention/", + "canonicalPathSource": "generated", + "category": "Azure", + "contentPath": "src/contents/v1/Azure/lifecycle-management-soft-delete-versioning-immutability-encryption-and-retention.md", + "id": "lifecycle-management-soft-delete-versioning-immutability-encryption-and-retention", + "seoDescription": "Learn Lifecycle management, soft delete, versioning, immutability, encryption, and retention for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Lifecycle management, soft delete, versioning, immutability, encryption, and retention Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Lifecycle management, soft delete, versioning, immutability, encryption, and retention", + "title": "Lifecycle management, soft delete, versioning, immutability, encryption, and retention", + "topic": "Azure Blob Storage and file handling" + }, + { + "canonicalPath": "/content/log-analytics-queries-dashboards-and-availability-tests/", + "canonicalPathSource": "generated", + "category": "Azure", + "contentPath": "src/contents/v1/Azure/log-analytics-queries-dashboards-and-availability-tests.md", + "id": "log-analytics-queries-dashboards-and-availability-tests", + "seoDescription": "Learn Log Analytics queries, dashboards, and availability tests for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Log Analytics queries, dashboards, and availability tests Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Log Analytics queries, dashboards, and availability tests", + "title": "Log Analytics queries, dashboards, and availability tests", + "topic": "Monitoring, tracing, and incident response on Azure" + }, + { + "canonicalPath": "/content/matching-storage-choices-to-relational-object-and-cache-workloads/", + "canonicalPathSource": "generated", + "category": "Azure", + "contentPath": "src/contents/v1/Azure/matching-storage-choices-to-relational-object-and-cache-workloads.md", + "id": "matching-storage-choices-to-relational-object-and-cache-workloads", + "seoDescription": "Learn Matching storage choices to relational, object, and cache workloads for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Matching storage choices to relational, object, and cache workloads Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Matching storage choices to relational, object, and cache workloads", + "title": "Matching storage choices to relational, object, and cache workloads", + "topic": "Azure data, storage, and caching services" + }, + { + "canonicalPath": "/content/metrics-vs-logs-vs-traces/", + "canonicalPathSource": "generated", + "category": "Azure", + "contentPath": "src/contents/v1/Azure/metrics-vs-logs-vs-traces.md", + "id": "metrics-vs-logs-vs-traces", + "seoDescription": "Learn Metrics vs logs vs traces for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Metrics vs logs vs traces Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Metrics vs logs vs traces", + "title": "Metrics vs logs vs traces", + "topic": "Monitoring, tracing, and incident response on Azure" + }, + { + "canonicalPath": "/content/microsoft-entra-app-registrations-scopes-and-auth-code-flow-basics/", + "canonicalPathSource": "generated", + "category": "Azure", + "contentPath": "src/contents/v1/Azure/microsoft-entra-app-registrations-scopes-and-auth-code-flow-basics.md", + "id": "microsoft-entra-app-registrations-scopes-and-auth-code-flow-basics", + "seoDescription": "Learn Microsoft Entra app registrations, scopes, and auth-code flow basics for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Microsoft Entra app registrations, scopes, and auth-code flow basics Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Microsoft Entra app registrations, scopes, and auth-code flow basics", + "title": "Microsoft Entra app registrations, scopes, and auth-code flow basics", + "topic": "Identity, secrets, and access control" + }, + { + "canonicalPath": "/content/oidc-based-azure-authentication-in-pipelines/", + "canonicalPathSource": "generated", + "category": "Azure", + "contentPath": "src/contents/v1/Azure/oidc-based-azure-authentication-in-pipelines.md", + "id": "oidc-based-azure-authentication-in-pipelines", + "seoDescription": "Learn OIDC-based Azure authentication in pipelines for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "OIDC-based Azure authentication in pipelines Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "OIDC-based Azure authentication in pipelines", + "title": "OIDC-based Azure authentication in pipelines", + "topic": "Delivery, infrastructure as code, scaling, and cost control" + }, + { + "canonicalPath": "/content/rate-limiting-quotas-auth-offload-and-request-transformation-at-the-edge/", + "canonicalPathSource": "generated", + "category": "Azure", + "contentPath": "src/contents/v1/Azure/rate-limiting-quotas-auth-offload-and-request-transformation-at-the-edge.md", + "id": "rate-limiting-quotas-auth-offload-and-request-transformation-at-the-edge", + "seoDescription": "Learn Rate limiting, quotas, auth offload, and request transformation at the edge for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Rate limiting, quotas, auth offload, and request transformation at the edge Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Rate limiting, quotas, auth offload, and request transformation at the edge", + "title": "Rate limiting, quotas, auth offload, and request transformation at the edge", + "topic": "Networking, API edge, and secure connectivity" + }, + { + "canonicalPath": "/content/rbac-least-privilege-and-separating-human-vs-workload-identities/", + "canonicalPathSource": "generated", + "category": "Azure", + "contentPath": "src/contents/v1/Azure/rbac-least-privilege-and-separating-human-vs-workload-identities.md", + "id": "rbac-least-privilege-and-separating-human-vs-workload-identities", + "seoDescription": "Learn RBAC, least privilege, and separating human vs workload identities for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "RBAC, least privilege, and separating human vs workload identities Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "RBAC, least privilege, and separating human vs workload identities", + "title": "RBAC, least privilege, and separating human vs workload identities", + "topic": "Identity, secrets, and access control" + }, + { + "canonicalPath": "/content/service-bus-queues-topics-subscriptions-and-dead-letter-queues/", + "canonicalPathSource": "generated", + "category": "Azure", + "contentPath": "src/contents/v1/Azure/service-bus-queues-topics-subscriptions-and-dead-letter-queues.md", + "id": "service-bus-queues-topics-subscriptions-and-dead-letter-queues", + "seoDescription": "Learn Service Bus queues, topics, subscriptions, and dead-letter queues for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Service Bus queues, topics, subscriptions, and dead-letter queues Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Service Bus queues, topics, subscriptions, and dead-letter queues", + "title": "Service Bus queues, topics, subscriptions, and dead-letter queues", + "topic": "Messaging and event-driven Azure integration" + }, + { + "canonicalPath": "/content/service-tags-private-networking-and-reducing-public-attack-surface/", + "canonicalPathSource": "generated", + "category": "Azure", + "contentPath": "src/contents/v1/Azure/service-tags-private-networking-and-reducing-public-attack-surface.md", + "id": "service-tags-private-networking-and-reducing-public-attack-surface", + "seoDescription": "Learn Service tags, private networking, and reducing public attack surface for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Service tags, private networking, and reducing public attack surface Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Service tags, private networking, and reducing public attack surface", + "title": "Service tags, private networking, and reducing public attack surface", + "topic": "Networking, API edge, and secure connectivity" + }, + { + "canonicalPath": "/content/shared-access-signatures-managed-identity-rbac-stored-access-policies-and-public-access-risks/", + "canonicalPathSource": "generated", + "category": "Azure", + "contentPath": "src/contents/v1/Azure/shared-access-signatures-managed-identity-rbac-stored-access-policies-and-public-access-risks.md", + "id": "shared-access-signatures-managed-identity-rbac-stored-access-policies-and-public-access-risks", + "seoDescription": "Learn Shared access signatures, managed identity, RBAC, stored access policies, and public access risks for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Shared access signatures, managed identity, RBAC, stored access policies, and public access risks Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Shared access signatures, managed identity, RBAC, stored access policies, and public access risks", + "title": "Shared access signatures, managed identity, RBAC, stored access policies, and public access risks", + "topic": "Azure Blob Storage and file handling" + }, + { + "canonicalPath": "/content/storage-accounts-containers-blobs-virtual-folders-metadata-and-access-tiers/", + "canonicalPathSource": "generated", + "category": "Azure", + "contentPath": "src/contents/v1/Azure/storage-accounts-containers-blobs-virtual-folders-metadata-and-access-tiers.md", + "id": "storage-accounts-containers-blobs-virtual-folders-metadata-and-access-tiers", + "seoDescription": "Learn Storage accounts, containers, blobs, virtual folders, metadata, and access tiers for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Storage accounts, containers, blobs, virtual folders, metadata, and access tiers Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Storage accounts, containers, blobs, virtual folders, metadata, and access tiers", + "title": "Storage accounts, containers, blobs, virtual folders, metadata, and access tiers", + "topic": "Azure Blob Storage and file handling" + }, + { + "canonicalPath": "/content/system-assigned-and-user-assigned-managed-identities/", + "canonicalPathSource": "generated", + "category": "Azure", + "contentPath": "src/contents/v1/Azure/system-assigned-and-user-assigned-managed-identities.md", + "id": "system-assigned-and-user-assigned-managed-identities", + "seoDescription": "Learn System-assigned and user-assigned managed identities for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "System-assigned and user-assigned managed identities Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "System-assigned and user-assigned managed identities", + "title": "System-assigned and user-assigned managed identities", + "topic": "Identity, secrets, and access control" + }, + { + "canonicalPath": "/content/trade-offs-among-simplicity-portability-autoscaling-and-operational-control/", + "canonicalPathSource": "generated", + "category": "Azure", + "contentPath": "src/contents/v1/Azure/trade-offs-among-simplicity-portability-autoscaling-and-operational-control.md", + "id": "trade-offs-among-simplicity-portability-autoscaling-and-operational-control", + "seoDescription": "Learn Trade-offs among simplicity, portability, autoscaling, and operational control for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Trade-offs among simplicity, portability, autoscaling, and operational control Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Trade-offs among simplicity, portability, autoscaling, and operational control", + "title": "Trade-offs among simplicity, portability, autoscaling, and operational control", + "topic": "Azure compute choices and hosting models" + }, + { + "canonicalPath": "/content/adrs-coding-conventions-and-team-level-design-communication/", + "canonicalPathSource": "generated", + "category": "Design & Architecture", + "contentPath": "src/contents/v1/Design & Architecture/adrs-coding-conventions-and-team-level-design-communication.md", + "id": "adrs-coding-conventions-and-team-level-design-communication", + "seoDescription": "Learn ADRs, coding conventions, and team-level design communication for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "ADRs, coding conventions, and team-level design communication Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "ADRs, coding conventions, and team-level design communication", + "title": "ADRs, coding conventions, and team-level design communication", + "topic": "Testing strategy, maintainability, and technical leadership" + }, + { + "canonicalPath": "/content/api-gateway-and-bff-decisions-for-web-clients-and-microservices/", + "canonicalPathSource": "generated", + "category": "Design & Architecture", + "contentPath": "src/contents/v1/Design & Architecture/api-gateway-and-bff-decisions-for-web-clients-and-microservices.md", + "id": "api-gateway-and-bff-decisions-for-web-clients-and-microservices", + "seoDescription": "Learn API gateway and BFF decisions for web clients and microservices for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "API gateway and BFF decisions for web clients and microservices Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "API gateway and BFF decisions for web clients and microservices", + "title": "API gateway and BFF decisions for web clients and microservices", + "topic": "API design and integration contracts" + }, + { + "canonicalPath": "/content/assumptions-constraints-risks-failure-modes/", + "canonicalPathSource": "generated", + "category": "Design & Architecture", + "contentPath": "src/contents/v1/Design & Architecture/assumptions-constraints-risks-failure-modes.md", + "id": "assumptions-constraints-risks-failure-modes", + "seoDescription": "Learn Assumptions, constraints, risks, and failure modes for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Assumptions, constraints, risks, and failure modes Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Assumptions, constraints, risks, and failure modes", + "title": "Assumptions, constraints, risks, and failure modes", + "topic": "Requirements decomposition and system trade-offs" + }, + { + "canonicalPath": "/content/cache-aside-read-caching-and-invalidation-trade-offs/", + "canonicalPathSource": "generated", + "category": "Design & Architecture", + "contentPath": "src/contents/v1/Design & Architecture/cache-aside-read-caching-and-invalidation-trade-offs.md", + "id": "cache-aside-read-caching-and-invalidation-trade-offs", + "seoDescription": "Learn Cache-aside, read caching, and invalidation trade-offs for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Cache-aside, read caching, and invalidation trade-offs Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Cache-aside, read caching, and invalidation trade-offs", + "title": "Cache-aside, read caching, and invalidation trade-offs", + "topic": "Scalability, resilience, caching, and observability design" + }, + { + "canonicalPath": "/content/capacity-planning-and-identifying-likely-bottlenecks/", + "canonicalPathSource": "generated", + "category": "Design & Architecture", + "contentPath": "src/contents/v1/Design & Architecture/capacity-planning-and-identifying-likely-bottlenecks.md", + "id": "capacity-planning-and-identifying-likely-bottlenecks", + "seoDescription": "Learn Capacity Planning and Identifying Likely Bottlenecks for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Capacity Planning and Identifying Likely Bottlenecks Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Capacity Planning and Identifying Likely Bottlenecks", + "title": "Capacity Planning and Identifying Likely Bottlenecks", + "topic": "Requirements decomposition and system trade-offs" + }, + { + "canonicalPath": "/content/command-injection-file-upload-risks-path-traversal-secrets-exposure-and-dos-ddos-basics/", + "canonicalPathSource": "generated", + "category": "Design & Architecture", + "contentPath": "src/contents/v1/Design & Architecture/command-injection-file-upload-risks-path-traversal-secrets-exposure-and-dos-ddos-basics.md", + "id": "command-injection-file-upload-risks-path-traversal-secrets-exposure-and-dos-ddos-basics", + "seoDescription": "Learn Command injection, file upload risks, path traversal, secrets exposure, and DoS/DDoS basics for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Command injection, file upload risks, path traversal, secrets exposure, and DoS/DDoS basics Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Command injection, file upload risks, path traversal, secrets exposure, and DoS/DDoS basics", + "title": "Command injection, file upload risks, path traversal, secrets exposure, and DoS/DDoS basics", + "topic": "Web application security threat modeling and attack patterns" + }, + { + "canonicalPath": "/content/correlation-ids-traces-health-checks-and-alertable-telemetry/", + "canonicalPathSource": "generated", + "category": "Design & Architecture", + "contentPath": "src/contents/v1/Design & Architecture/correlation-ids-traces-health-checks-and-alertable-telemetry.md", + "id": "correlation-ids-traces-health-checks-and-alertable-telemetry", + "seoDescription": "Learn Correlation IDs, traces, health checks, and alertable telemetry for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Correlation IDs, traces, health checks, and alertable telemetry Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Correlation IDs, traces, health checks, and alertable telemetry", + "title": "Correlation IDs, traces, health checks, and alertable telemetry", + "topic": "Scalability, resilience, caching, and observability design" + }, + { + "canonicalPath": "/content/cqrs-and-when-separate-read-and-write-models-make-sense/", + "canonicalPathSource": "generated", + "category": "Design & Architecture", + "contentPath": "src/contents/v1/Design & Architecture/cqrs-and-when-separate-read-and-write-models-make-sense.md", + "id": "cqrs-and-when-separate-read-and-write-models-make-sense", + "seoDescription": "Learn CQRS and when separate read and write models make sense for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "CQRS and when separate read and write models make sense Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "CQRS and when separate read and write models make sense", + "title": "CQRS and when separate read and write models make sense", + "topic": "Distributed systems patterns" + }, + { + "canonicalPath": "/content/cross-site-request-forgery-and-cookie-based-authentication/", + "canonicalPathSource": "generated", + "category": "Design & Architecture", + "contentPath": "src/contents/v1/Design & Architecture/cross-site-request-forgery-and-cookie-based-authentication.md", + "id": "cross-site-request-forgery-and-cookie-based-authentication", + "seoDescription": "Learn Cross-Site Request Forgery and how cookie-based authentication changes the risk model for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Cross-Site Request Forgery and how cookie-based authentication changes the risk model Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Cross-Site Request Forgery and how cookie-based authentication changes the risk model", + "title": "Cross-Site Request Forgery and how cookie-based authentication changes the risk model", + "topic": "Web application security threat modeling and attack patterns" + }, + { + "canonicalPath": "/content/cross-site-scripting-output-encoding-dangerous-html-rendering-and-content-security/", + "canonicalPathSource": "generated", + "category": "Design & Architecture", + "contentPath": "src/contents/v1/Design & Architecture/cross-site-scripting-output-encoding-dangerous-html-rendering-and-content-security.md", + "id": "cross-site-scripting-output-encoding-dangerous-html-rendering-and-content-security", + "seoDescription": "Learn Cross-Site Scripting, output encoding, dangerous HTML rendering, and content security controls for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Cross-Site Scripting, output encoding, dangerous HTML rendering, and content security controls Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Cross-Site Scripting, output encoding, dangerous HTML rendering, and content security controls", + "title": "Cross-Site Scripting, output encoding, dangerous HTML rendering, and content security controls", + "topic": "Web application security threat modeling and attack patterns" + }, + { + "canonicalPath": "/content/dependency-inversion-and-inward-facing-dependencies/", + "canonicalPathSource": "generated", + "category": "Design & Architecture", + "contentPath": "src/contents/v1/Design & Architecture/dependency-inversion-and-inward-facing-dependencies.md", + "id": "dependency-inversion-and-inward-facing-dependencies", + "seoDescription": "Learn Dependency inversion and inward-facing dependencies for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Dependency inversion and inward-facing dependencies Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Dependency inversion and inward-facing dependencies", + "title": "Dependency inversion and inward-facing dependencies", + "topic": "Clean Architecture and modular boundaries" + }, + { + "canonicalPath": "/content/domain-services-and-domain-events/", + "canonicalPathSource": "generated", + "category": "Design & Architecture", + "contentPath": "src/contents/v1/Design & Architecture/domain-services-and-domain-events.md", + "id": "domain-services-and-domain-events", + "seoDescription": "Learn Domain services and domain events for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Domain services and domain events Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Domain services and domain events", + "title": "Domain services and domain events", + "topic": "Domain modeling and Domain-Driven Design" + }, + { + "canonicalPath": "/content/entities-value-objects-aggregates-and-invariants/", + "canonicalPathSource": "generated", + "category": "Design & Architecture", + "contentPath": "src/contents/v1/Design & Architecture/entities-value-objects-aggregates-and-invariants.md", + "id": "entities-value-objects-aggregates-and-invariants", + "seoDescription": "Learn Entities, value objects, aggregates, and invariants for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Entities, value objects, aggregates, and invariants Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Entities, value objects, aggregates, and invariants", + "title": "Entities, value objects, aggregates, and invariants", + "topic": "Domain modeling and Domain-Driven Design" + }, + { + "canonicalPath": "/content/event-driven-communication-and-asynchronous-request-reply/", + "canonicalPathSource": "generated", + "category": "Design & Architecture", + "contentPath": "src/contents/v1/Design & Architecture/event-driven-communication-and-asynchronous-request-reply.md", + "id": "event-driven-communication-and-asynchronous-request-reply", + "seoDescription": "Learn Event-driven communication and asynchronous request-reply for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Event-driven communication and asynchronous request-reply Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Event-driven communication and asynchronous request-reply", + "title": "Event-driven communication and asynchronous request-reply", + "topic": "Distributed systems patterns" + }, + { + "canonicalPath": "/content/factory-builder-strategy-adapter-decorator-facade-proxy-and-chain-of-responsibility/", + "canonicalPathSource": "generated", + "category": "Design & Architecture", + "contentPath": "src/contents/v1/Design & Architecture/factory-builder-strategy-adapter-decorator-facade-proxy-and-chain-of-responsibility.md", + "id": "factory-builder-strategy-adapter-decorator-facade-proxy-and-chain-of-responsibility", + "seoDescription": "Learn Factory, Builder, Strategy, Adapter, Decorator, Facade, Proxy, and Chain of Responsibility for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Factory, Builder, Strategy, Adapter, Decorator, Facade, Proxy, and Chain of Responsibility Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Factory, Builder, Strategy, Adapter, Decorator, Facade, Proxy, and Chain of Responsibility", + "title": "Factory, Builder, Strategy, Adapter, Decorator, Facade, Proxy, and Chain of Responsibility", + "topic": "Software design principles and common .NET patterns" + }, + { + "canonicalPath": "/content/functional-requirements-vs-nonfunctional-requirements/", + "canonicalPathSource": "generated", + "category": "Design & Architecture", + "contentPath": "src/contents/v1/Design & Architecture/functional-requirements-vs-nonfunctional-requirements.md", + "id": "functional-requirements-vs-nonfunctional-requirements", + "seoDescription": "Learn Functional Requirements vs Nonfunctional Requirements for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Functional Requirements vs Nonfunctional Requirements Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Functional Requirements vs Nonfunctional Requirements", + "title": "Functional Requirements vs Nonfunctional Requirements", + "topic": "Requirements decomposition and system trade-offs" + }, + { + "canonicalPath": "/content/horizontal-scaling-stateless-services-and-backpressure/", + "canonicalPathSource": "generated", + "category": "Design & Architecture", + "contentPath": "src/contents/v1/Design & Architecture/horizontal-scaling-stateless-services-and-backpressure.md", + "id": "horizontal-scaling-stateless-services-and-backpressure", + "seoDescription": "Learn Horizontal scaling, stateless services, and backpressure for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Horizontal scaling, stateless services, and backpressure Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Horizontal scaling, stateless services, and backpressure", + "title": "Horizontal scaling, stateless services, and backpressure", + "topic": "Scalability, resilience, caching, and observability design" + }, + { + "canonicalPath": "/content/idempotent-consumers-and-duplicate-message-handling/", + "canonicalPathSource": "generated", + "category": "Design & Architecture", + "contentPath": "src/contents/v1/Design & Architecture/idempotent-consumers-and-duplicate-message-handling.md", + "id": "idempotent-consumers-and-duplicate-message-handling", + "seoDescription": "Learn Idempotent consumers and duplicate-message handling for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Idempotent consumers and duplicate-message handling Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Idempotent consumers and duplicate-message handling", + "title": "Idempotent consumers and duplicate-message handling", + "topic": "Distributed systems patterns" + }, + { + "canonicalPath": "/content/insecure-direct-object-reference-broken-access-control-and-object-level-authorization/", + "canonicalPathSource": "generated", + "category": "Design & Architecture", + "contentPath": "src/contents/v1/Design & Architecture/insecure-direct-object-reference-broken-access-control-and-object-level-authorization.md", + "id": "insecure-direct-object-reference-broken-access-control-and-object-level-authorization", + "seoDescription": "Learn Insecure Direct Object Reference, broken access control, and object-level authorization for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Insecure Direct Object Reference, broken access control, and object-level authorization Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Insecure Direct Object Reference, broken access control, and object-level authorization", + "title": "Insecure Direct Object Reference, broken access control, and object-level authorization", + "topic": "Web application security threat modeling and attack patterns" + }, + { + "canonicalPath": "/content/kiss-dry-yagni-separation-of-concerns-cohesion-and-coupling/", + "canonicalPathSource": "generated", + "category": "Design & Architecture", + "contentPath": "src/contents/v1/Design & Architecture/kiss-dry-yagni-separation-of-concerns-cohesion-and-coupling.md", + "id": "kiss-dry-yagni-separation-of-concerns-cohesion-and-coupling", + "seoDescription": "Learn KISS, DRY, YAGNI, Separation of Concerns, Cohesion, and Coupling for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "KISS, DRY, YAGNI, Separation of Concerns, Cohesion, and Coupling Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "KISS, DRY, YAGNI, Separation of Concerns, Cohesion, and Coupling", + "title": "KISS, DRY, YAGNI, Separation of Concerns, Cohesion, and Coupling", + "topic": "Software design principles and common .NET patterns" + }, + { + "canonicalPath": "/content/layered-architecture-vs-clean-architecture-vs-ports-and-adapters/", + "canonicalPathSource": "generated", + "category": "Design & Architecture", + "contentPath": "src/contents/v1/Design & Architecture/layered-architecture-vs-clean-architecture-vs-ports-and-adapters.md", + "id": "layered-architecture-vs-clean-architecture-vs-ports-and-adapters", + "seoDescription": "Learn Layered architecture vs Clean Architecture vs ports-and-adapters for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Layered architecture vs Clean Architecture vs ports-and-adapters Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Layered architecture vs Clean Architecture vs ports-and-adapters", + "title": "Layered architecture vs Clean Architecture vs ports-and-adapters", + "topic": "Clean Architecture and modular boundaries" + }, + { + "canonicalPath": "/content/mapping-domain-models-to-persistence-without-corrupting-the-model/", + "canonicalPathSource": "generated", + "category": "Design & Architecture", + "contentPath": "src/contents/v1/Design & Architecture/mapping-domain-models-to-persistence-without-corrupting-the-model.md", + "id": "mapping-domain-models-to-persistence-without-corrupting-the-model", + "seoDescription": "Learn Mapping domain models to persistence without corrupting the model for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Mapping domain models to persistence without corrupting the model Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Mapping domain models to persistence without corrupting the model", + "title": "Mapping domain models to persistence without corrupting the model", + "topic": "Domain modeling and Domain-Driven Design" + }, + { + "canonicalPath": "/content/mentoring-ownership-and-incremental-modernization/", + "canonicalPathSource": "generated", + "category": "Design & Architecture", + "contentPath": "src/contents/v1/Design & Architecture/mentoring-ownership-and-incremental-modernization.md", + "id": "mentoring-ownership-and-incremental-modernization", + "seoDescription": "Learn Mentoring, ownership, and incremental modernization for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Mentoring, ownership, and incremental modernization Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Mentoring, ownership, and incremental modernization", + "title": "Mentoring, ownership, and incremental modernization", + "topic": "Testing strategy, maintainability, and technical leadership" + }, + { + "canonicalPath": "/content/modular-monolith-structure-and-feature-based-organization/", + "canonicalPathSource": "generated", + "category": "Design & Architecture", + "contentPath": "src/contents/v1/Design & Architecture/modular-monolith-structure-and-feature-based-organization.md", + "id": "modular-monolith-structure-and-feature-based-organization", + "seoDescription": "Learn Modular monolith structure and feature-based organization for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Modular monolith structure and feature-based organization Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Modular monolith structure and feature-based organization", + "title": "Modular monolith structure and feature-based organization", + "topic": "Clean Architecture and modular boundaries" + }, + { + "canonicalPath": "/content/openapi-contracts-and-consumer-facing-documentation/", + "canonicalPathSource": "generated", + "category": "Design & Architecture", + "contentPath": "src/contents/v1/Design & Architecture/openapi-contracts-and-consumer-facing-documentation.md", + "id": "openapi-contracts-and-consumer-facing-documentation", + "seoDescription": "Learn OpenAPI contracts and consumer-facing documentation for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "OpenAPI contracts and consumer-facing documentation Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "OpenAPI contracts and consumer-facing documentation", + "title": "OpenAPI contracts and consumer-facing documentation", + "topic": "API design and integration contracts" + }, + { + "canonicalPath": "/content/recognizing-when-a-pattern-improves-maintainability-vs-when-it-adds-unnecessary-complexity/", + "canonicalPathSource": "generated", + "category": "Design & Architecture", + "contentPath": "src/contents/v1/Design & Architecture/recognizing-when-a-pattern-improves-maintainability-vs-when-it-adds-unnecessary-complexity.md", + "id": "recognizing-when-a-pattern-improves-maintainability-vs-when-it-adds-unnecessary-complexity", + "seoDescription": "Learn Recognizing when a pattern improves maintainability vs when it adds unnecessary complexity for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Recognizing when a pattern improves maintainability vs when it adds unnecessary complexity Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Recognizing when a pattern improves maintainability vs when it adds unnecessary complexity", + "title": "Recognizing when a pattern improves maintainability vs when it adds unnecessary complexity", + "topic": "Software design principles and common .NET patterns" + }, + { + "canonicalPath": "/content/refactoring-strategy-code-review-standards-and-technical-debt-management/", + "canonicalPathSource": "generated", + "category": "Design & Architecture", + "contentPath": "src/contents/v1/Design & Architecture/refactoring-strategy-code-review-standards-and-technical-debt-management.md", + "id": "refactoring-strategy-code-review-standards-and-technical-debt-management", + "seoDescription": "Learn Refactoring strategy, code review standards, and technical debt management for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Refactoring strategy, code review standards, and technical debt management Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Refactoring strategy, code review standards, and technical debt management", + "title": "Refactoring strategy, code review standards, and technical debt management", + "topic": "Testing strategy, maintainability, and technical leadership" + }, + { + "canonicalPath": "/content/repository-unit-of-work-mediator-specification-dotnet-patterns/", + "canonicalPathSource": "generated", + "category": "Design & Architecture", + "contentPath": "src/contents/v1/Design & Architecture/repository-unit-of-work-mediator-specification-dotnet-patterns.md", + "id": "repository-unit-of-work-mediator-specification-dotnet-patterns", + "seoDescription": "Learn Repository, Unit of Work, Mediator, and Specification Patterns in .NET for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Repository, Unit of Work, Mediator, and Specification Patterns in .NET Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Repository, Unit of Work, Mediator, and Specification Patterns in .NET", + "title": "Repository, Unit of Work, Mediator, and Specification Patterns in .NET", + "topic": "Software design principles and common .NET patterns" + }, + { + "canonicalPath": "/content/resource-modeling-rest-semantics-and-rpc-style-endpoints/", + "canonicalPathSource": "generated", + "category": "Design & Architecture", + "contentPath": "src/contents/v1/Design & Architecture/resource-modeling-rest-semantics-and-rpc-style-endpoints.md", + "id": "resource-modeling-rest-semantics-and-rpc-style-endpoints", + "seoDescription": "Learn Resource modeling, REST semantics, and when RPC-style endpoints are acceptable for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Resource modeling, REST semantics, and when RPC-style endpoints are acceptable Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Resource modeling, REST semantics, and when RPC-style endpoints are acceptable", + "title": "Resource modeling, REST semantics, and when RPC-style endpoints are acceptable", + "topic": "API design and integration contracts" + }, + { + "canonicalPath": "/content/retry-circuit-breaker-bulkhead-and-queue-based-load-leveling/", + "canonicalPathSource": "generated", + "category": "Design & Architecture", + "contentPath": "src/contents/v1/Design & Architecture/retry-circuit-breaker-bulkhead-and-queue-based-load-leveling.md", + "id": "retry-circuit-breaker-bulkhead-and-queue-based-load-leveling", + "seoDescription": "Learn Retry, circuit breaker, bulkhead, and queue-based load leveling for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Retry, circuit breaker, bulkhead, and queue-based load leveling Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Retry, circuit breaker, bulkhead, and queue-based load leveling", + "title": "Retry, circuit breaker, bulkhead, and queue-based load leveling", + "topic": "Scalability, resilience, caching, and observability design" + }, + { + "canonicalPath": "/content/saga-and-compensating-transaction-patterns/", + "canonicalPathSource": "generated", + "category": "Design & Architecture", + "contentPath": "src/contents/v1/Design & Architecture/saga-and-compensating-transaction-patterns.md", + "id": "saga-and-compensating-transaction-patterns", + "seoDescription": "Learn Saga and compensating transaction patterns for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Saga and compensating transaction patterns Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Saga and compensating transaction patterns", + "title": "Saga and compensating transaction patterns", + "topic": "Distributed systems patterns" + }, + { + "canonicalPath": "/content/session-hijacking-credential-stuffing-brute-force-protection-lockout-and-multi-factor-authentication/", + "canonicalPathSource": "generated", + "category": "Design & Architecture", + "contentPath": "src/contents/v1/Design & Architecture/session-hijacking-credential-stuffing-brute-force-protection-lockout-and-multi-factor-authentication.md", + "id": "session-hijacking-credential-stuffing-brute-force-protection-lockout-and-multi-factor-authentication", + "seoDescription": "Learn Session hijacking, credential stuffing, brute-force protection, lockout, and multi-factor authentication for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Session hijacking, credential stuffing, brute-force protection, lockout, and multi-factor authentication Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Session hijacking, credential stuffing, brute-force protection, lockout, and multi-factor authentication", + "title": "Session hijacking, credential stuffing, brute-force protection, lockout, and multi-factor authentication", + "topic": "Web application security threat modeling and attack patterns" + }, + { + "canonicalPath": "/content/solid-principles-in-dotnet-design/", + "canonicalPathSource": "generated", + "category": "Design & Architecture", + "contentPath": "src/contents/v1/Design & Architecture/solid-principles-in-dotnet-design.md", + "id": "solid-principles-in-dotnet-design", + "seoDescription": "Learn SOLID Principles in .NET Design for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "SOLID Principles in .NET Design Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "SOLID Principles in .NET Design", + "title": "SOLID Principles in .NET Design", + "topic": "Software design principles and common .NET patterns" + }, + { + "canonicalPath": "/content/sql-injection-parameterized-queries-orm-safety-and-unsafe-dynamic-sql/", + "canonicalPathSource": "generated", + "category": "Design & Architecture", + "contentPath": "src/contents/v1/Design & Architecture/sql-injection-parameterized-queries-orm-safety-and-unsafe-dynamic-sql.md", + "id": "sql-injection-parameterized-queries-orm-safety-and-unsafe-dynamic-sql", + "seoDescription": "Learn SQL Injection, parameterized queries, ORM safety, and unsafe dynamic SQL for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "SQL Injection, parameterized queries, ORM safety, and unsafe dynamic SQL Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "SQL Injection, parameterized queries, ORM safety, and unsafe dynamic SQL", + "title": "SQL Injection, parameterized queries, ORM safety, and unsafe dynamic SQL", + "topic": "Web application security threat modeling and attack patterns" + }, + { + "canonicalPath": "/content/test-pyramid-contract-tests-and-architecture-fitness-checks/", + "canonicalPathSource": "generated", + "category": "Design & Architecture", + "contentPath": "src/contents/v1/Design & Architecture/test-pyramid-contract-tests-and-architecture-fitness-checks.md", + "id": "test-pyramid-contract-tests-and-architecture-fitness-checks", + "seoDescription": "Learn Test pyramid, contract tests, and architecture fitness checks for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Test pyramid, contract tests, and architecture fitness checks Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Test pyramid, contract tests, and architecture fitness checks", + "title": "Test pyramid, contract tests, and architecture fitness checks", + "topic": "Testing strategy, maintainability, and technical leadership" + }, + { + "canonicalPath": "/content/threat-modeling-user-input-trust-boundaries-authentication-flows-and-admin-only-operations/", + "canonicalPathSource": "generated", + "category": "Design & Architecture", + "contentPath": "src/contents/v1/Design & Architecture/threat-modeling-user-input-trust-boundaries-authentication-flows-and-admin-only-operations.md", + "id": "threat-modeling-user-input-trust-boundaries-authentication-flows-and-admin-only-operations", + "seoDescription": "Learn Threat modeling user input, trust boundaries, authentication flows, and admin-only operations for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Threat modeling user input, trust boundaries, authentication flows, and admin-only operations Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Threat modeling user input, trust boundaries, authentication flows, and admin-only operations", + "title": "Threat modeling user input, trust boundaries, authentication flows, and admin-only operations", + "topic": "Web application security threat modeling and attack patterns" + }, + { + "canonicalPath": "/content/throughput-latency-concurrency-availability-consistency-cost-targets/", + "canonicalPathSource": "generated", + "category": "Design & Architecture", + "contentPath": "src/contents/v1/Design & Architecture/throughput-latency-concurrency-availability-consistency-cost-targets.md", + "id": "throughput-latency-concurrency-availability-consistency-cost-targets", + "seoDescription": "Learn Throughput, latency, concurrency, availability, consistency, and cost targets for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Throughput, latency, concurrency, availability, consistency, and cost targets Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Throughput, latency, concurrency, availability, consistency, and cost targets", + "title": "Throughput, latency, concurrency, availability, consistency, and cost targets", + "topic": "Requirements decomposition and system trade-offs" + }, + { + "canonicalPath": "/content/ubiquitous-language-and-bounded-contexts/", + "canonicalPathSource": "generated", + "category": "Design & Architecture", + "contentPath": "src/contents/v1/Design & Architecture/ubiquitous-language-and-bounded-contexts.md", + "id": "ubiquitous-language-and-bounded-contexts", + "seoDescription": "Learn Ubiquitous language and bounded contexts for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Ubiquitous language and bounded contexts Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Ubiquitous language and bounded contexts", + "title": "Ubiquitous language and bounded contexts", + "topic": "Domain modeling and Domain-Driven Design" + }, + { + "canonicalPath": "/content/versioning-idempotency-pagination-filtering-and-sorting/", + "canonicalPathSource": "generated", + "category": "Design & Architecture", + "contentPath": "src/contents/v1/Design & Architecture/versioning-idempotency-pagination-filtering-and-sorting.md", + "id": "versioning-idempotency-pagination-filtering-and-sorting", + "seoDescription": "Learn Versioning, idempotency, pagination, filtering, and sorting for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Versioning, idempotency, pagination, filtering, and sorting Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Versioning, idempotency, pagination, filtering, and sorting", + "title": "Versioning, idempotency, pagination, filtering, and sorting", + "topic": "API design and integration contracts" + }, + { + "canonicalPath": "/content/when-not-to-over-architect-a-simple-application/", + "canonicalPathSource": "generated", + "category": "Design & Architecture", + "contentPath": "src/contents/v1/Design & Architecture/when-not-to-over-architect-a-simple-application.md", + "id": "when-not-to-over-architect-a-simple-application", + "seoDescription": "Learn When not to over-architect a simple application for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "When not to over-architect a simple application Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "When not to over-architect a simple application", + "title": "When not to over-architect a simple application", + "topic": "Clean Architecture and modular boundaries" + }, + { + "canonicalPath": "/content/api-controller-behavior-automatic-model-validation-binding-source-inference-and-validation-responses/", + "canonicalPathSource": "generated", + "category": ".NET", + "contentPath": "src/contents/v1/NET/api-controller-behavior-automatic-model-validation-binding-source-inference-and-validation-responses.md", + "id": "api-controller-behavior-automatic-model-validation-binding-source-inference-and-validation-responses", + "seoDescription": "Learn [ApiController] Behavior for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "[ApiController] Behavior Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "[ApiController] Behavior", + "title": "[ApiController] Behavior", + "topic": "API design and implementation" + }, + { + "canonicalPath": "/content/aspnet-core-filters-and-middleware-boundaries/", + "canonicalPathSource": "generated", + "category": ".NET", + "contentPath": "src/contents/v1/NET/aspnet-core-filters-and-middleware-boundaries.md", + "id": "aspnet-core-filters-and-middleware-boundaries", + "seoDescription": "Learn ASP.NET Core filters and middleware boundaries for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "ASP.NET Core filters and middleware boundaries Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "ASP.NET Core filters and middleware boundaries", + "title": "ASP.NET Core filters and middleware boundaries", + "topic": "API design and implementation" + }, + { + "canonicalPath": "/content/async-and-await-semantics-in-csharp/", + "canonicalPathSource": "frontmatter", + "category": ".NET", + "contentPath": "src/contents/v1/NET/async-and-await-semantics-in-csharp.md", + "id": "async-and-await-semantics-in-csharp", + "seoDescription": "Learn async and await in C# for technical interviews, including Task, cancellation, exception handling, and common deadlock risks.", + "seoDescriptionSource": "frontmatter", + "seoTitle": "Async and Await in C# Interview Questions", + "seoTitleSource": "frontmatter", + "subtopic": "Async and Await Semantics", + "title": "Async and Await Semantics", + "topic": "Async programming, tasks, cancellation, and concurrency" + }, + { + "canonicalPath": "/content/authentication-vs-authorization-in-csharp/", + "canonicalPathSource": "generated", + "category": ".NET", + "contentPath": "src/contents/v1/NET/authentication-vs-authorization-in-csharp.md", + "id": "authentication-vs-authorization-in-csharp", + "seoDescription": "Learn Authentication vs Authorization in C# for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Authentication vs Authorization in C# Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Authentication vs Authorization in C#", + "title": "Authentication vs Authorization in C#", + "topic": "Authentication, authorization, and web security" + }, + { + "canonicalPath": "/content/cancellationtoken-propagation-in-csharp/", + "canonicalPathSource": "generated", + "category": ".NET", + "contentPath": "src/contents/v1/NET/cancellationtoken-propagation-in-csharp.md", + "id": "cancellationtoken-propagation-in-csharp", + "seoDescription": "Learn CancellationToken Propagation for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "CancellationToken Propagation Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "CancellationToken Propagation", + "title": "CancellationToken Propagation", + "topic": "Async programming, tasks, cancellation, and concurrency" + }, + { + "canonicalPath": "/content/classes-structs-records/", + "canonicalPathSource": "generated", + "category": ".NET", + "contentPath": "src/contents/v1/NET/classes-structs-records-object-oriented-fundamentals.md", + "id": "classes-structs-records", + "seoDescription": "Learn Classes, structs, records for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Classes, structs, records Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Classes, structs, records", + "title": "Classes, structs, records", + "topic": "C# Language Foundations" + }, + { + "canonicalPath": "/content/code-coverage-useful-assertions-flaky-test-prevention-and-ci-test-execution/", + "canonicalPathSource": "generated", + "category": ".NET", + "contentPath": "src/contents/v1/NET/code-coverage-useful-assertions-flaky-test-prevention-and-ci-test-execution.md", + "id": "code-coverage-useful-assertions-flaky-test-prevention-and-ci-test-execution", + "seoDescription": "Learn Code Coverage, Useful Assertions, Flaky Test Prevention, and CI Test Execution for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Code Coverage, Useful Assertions, Flaky Test Prevention, and CI Test Execution Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Code Coverage, Useful Assertions, Flaky Test Prevention, and CI Test Execution", + "title": "Code Coverage, Useful Assertions, Flaky Test Prevention, and CI Test Execution", + "topic": "Testing strategy and integration testing" + }, + { + "canonicalPath": "/content/collection-choices-in-csharp/", + "canonicalPathSource": "generated", + "category": ".NET", + "contentPath": "src/contents/v1/NET/collection-choices-in-csharp.md", + "id": "collection-choices-in-csharp", + "seoDescription": "Learn Collection Choices in C# for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Collection Choices in C# Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Collection Choices in C#", + "title": "Collection Choices in C#", + "topic": "C# Language Foundations" + }, + { + "canonicalPath": "/content/common-bcl-types-in-csharp/", + "canonicalPathSource": "generated", + "category": ".NET", + "contentPath": "src/contents/v1/NET/common-bcl-types-in-csharp.md", + "id": "common-bcl-types-in-csharp", + "seoDescription": "Learn Common BCL Types in C# for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Common BCL Types in C# Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Common BCL Types in C#", + "title": "Common BCL Types in C#", + "topic": "C# Language Foundations" + }, + { + "canonicalPath": "/content/configuration-sources-and-the-options-pattern/", + "canonicalPathSource": "generated", + "category": ".NET", + "contentPath": "src/contents/v1/NET/configuration-sources-and-the-options-pattern.md", + "id": "configuration-sources-and-the-options-pattern", + "seoDescription": "Learn Configuration Sources and the Options Pattern for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Configuration Sources and the Options Pattern Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Configuration Sources and the Options Pattern", + "title": "Configuration Sources and the Options Pattern", + "topic": "Dependency injection, configuration, middleware, and logging" + }, + { + "canonicalPath": "/content/content-negotiation-status-codes-dtos-request-response-contracts/", + "canonicalPathSource": "generated", + "category": ".NET", + "contentPath": "src/contents/v1/NET/content-negotiation-status-codes-dtos-request-response-contracts.md", + "id": "content-negotiation-status-codes-dtos-request-response-contracts", + "seoDescription": "Learn Content negotiation, status codes and request/response contracts for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Content negotiation, status codes and request/response contracts Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Content negotiation, status codes and request/response contracts", + "title": "Content negotiation, status codes and request/response contracts", + "topic": "API design and implementation" + }, + { + "canonicalPath": "/content/controllers-vs-minimal-apis-when-to-choose-each/", + "canonicalPathSource": "generated", + "category": ".NET", + "contentPath": "src/contents/v1/NET/controllers-vs-minimal-apis-when-to-choose-each.md", + "id": "controllers-vs-minimal-apis-when-to-choose-each", + "seoDescription": "Learn Controllers vs Minimal APIs for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Controllers vs Minimal APIs Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Controllers vs Minimal APIs", + "title": "Controllers vs Minimal APIs", + "topic": "API design and implementation" + }, + { + "canonicalPath": "/content/conventions-fluent-api-owned-complex-data-and-relationship-mapping/", + "canonicalPathSource": "generated", + "category": ".NET", + "contentPath": "src/contents/v1/NET/conventions-fluent-api-owned-complex-data-and-relationship-mapping.md", + "id": "conventions-fluent-api-owned-complex-data-and-relationship-mapping", + "seoDescription": "Learn Conventions, Fluent API, Owned/Complex Data, and Relationship Mapping for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Conventions, Fluent API, Owned/Complex Data, and Relationship Mapping Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Conventions, Fluent API, Owned/Complex Data, and Relationship Mapping", + "title": "Conventions, Fluent API, Owned/Complex Data, and Relationship Mapping", + "topic": "Entity Framework" + }, + { + "canonicalPath": "/content/cookie-behavior-csrf-browser-security-concerns/", + "canonicalPathSource": "generated", + "category": ".NET", + "contentPath": "src/contents/v1/NET/cookie-behavior-csrf-browser-security-concerns.md", + "id": "cookie-behavior-csrf-browser-security-concerns", + "seoDescription": "Learn Cookie behavior, CSRF, and browser-based security concerns for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Cookie behavior, CSRF, and browser-based security concerns Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Cookie behavior, CSRF, and browser-based security concerns", + "title": "Cookie behavior, CSRF, and browser-based security concerns", + "topic": "Authentication, authorization, and web security" + }, + { + "canonicalPath": "/content/coordinating-multiple-tasks-in-csharp/", + "canonicalPathSource": "generated", + "category": ".NET", + "contentPath": "src/contents/v1/NET/coordinating-multiple-tasks-in-csharp.md", + "id": "coordinating-multiple-tasks-in-csharp", + "seoDescription": "Learn Coordinating Multiple Tasks in C# for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Coordinating Multiple Tasks in C# Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Coordinating Multiple Tasks in C#", + "title": "Coordinating Multiple Tasks in C#", + "topic": "Async programming, tasks, cancellation, and concurrency" + }, + { + "canonicalPath": "/content/cors-secure-headers-secret-handling-and-least-privilege/", + "canonicalPathSource": "generated", + "category": "Design & Architecture", + "contentPath": "src/contents/v1/NET/cors-secure-headers-secret-handling-least-privilege.md", + "id": "cors-secure-headers-secret-handling-and-least-privilege", + "seoDescription": "Learn CORS, secure headers, secret handling, and least privilege for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "CORS, secure headers, secret handling, and least privilege Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "CORS, secure headers, secret handling, and least privilege", + "title": "CORS, secure headers, secret handling, and least privilege", + "topic": "Authentication, authorization, and web security" + }, + { + "canonicalPath": "/content/cpu-bound-vs-io-bound-work-and-task-in-csharp/", + "canonicalPathSource": "generated", + "category": ".NET", + "contentPath": "src/contents/v1/NET/cpu-bound-vs-io-bound-work-and-task-in-csharp.md", + "id": "cpu-bound-vs-io-bound-work-and-task-in-csharp", + "seoDescription": "Learn CPU-bound vs I/O-bound Work and Task for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "CPU-bound vs I/O-bound Work and Task Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "CPU-bound vs I/O-bound Work and Task", + "title": "CPU-bound vs I/O-bound Work and Task", + "topic": "Async programming, tasks, cancellation, and concurrency" + }, + { + "canonicalPath": "/content/data-seeding-choices-including-hasdata-vs-runtime-seed-logic/", + "canonicalPathSource": "generated", + "category": ".NET", + "contentPath": "src/contents/v1/NET/data-seeding-choices-including-hasdata-vs-runtime-seed-logic.md", + "id": "data-seeding-choices-including-hasdata-vs-runtime-seed-logic", + "seoDescription": "Learn Data Seeding Choices, Including HasData vs Runtime Seed Logic for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Data Seeding Choices, Including HasData vs Runtime Seed Logic Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Data Seeding Choices, Including HasData vs Runtime Seed Logic", + "title": "Data Seeding Choices, Including HasData vs Runtime Seed Logic", + "topic": "Entity Framework" + }, + { + "canonicalPath": "/content/dbcontext-change-tracking-and-entity-state-management-in-ef-core/", + "canonicalPathSource": "generated", + "category": ".NET", + "contentPath": "src/contents/v1/NET/dbcontext-change-tracking-and-entity-state-management-in-ef-core.md", + "id": "dbcontext-change-tracking-and-entity-state-management-in-ef-core", + "seoDescription": "Learn DbContext change tracking and entity state management in EF Core for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "DbContext change tracking and entity state management in EF Core Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "DbContext change tracking and entity state management in EF Core", + "title": "DbContext change tracking and entity state management in EF Core", + "topic": "Entity Framework" + }, + { + "canonicalPath": "/content/dbcontext-lifetime-thread-safety-connection-usage-and-when-to-avoid-sharing-a-context/", + "canonicalPathSource": "generated", + "category": ".NET", + "contentPath": "src/contents/v1/NET/dbcontext-lifetime-thread-safety-connection-usage-and-when-to-avoid-sharing-a-context.md", + "id": "dbcontext-lifetime-thread-safety-connection-usage-and-when-to-avoid-sharing-a-context", + "seoDescription": "Learn DbContext Lifetime, Thread Safety, Connection Usage, and When to Avoid Sharing a Context for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "DbContext Lifetime, Thread Safety, Connection Usage, and When to Avoid Sharing a Context Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "DbContext Lifetime, Thread Safety, Connection Usage, and When to Avoid Sharing a Context", + "title": "DbContext Lifetime, Thread Safety, Connection Usage, and When to Avoid Sharing a Context", + "topic": "Entity Framework" + }, + { + "canonicalPath": "/content/deferred-execution-in-csharp/", + "canonicalPathSource": "generated", + "category": ".NET", + "contentPath": "src/contents/v1/NET/deferred-execution-in-csharp.md", + "id": "deferred-execution-in-csharp", + "seoDescription": "Learn Deferred Execution in C# for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Deferred Execution in C# Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Deferred Execution in C#", + "title": "Deferred Execution in C#", + "topic": "Modern C# patterns" + }, + { + "canonicalPath": "/content/delegates-in-csharp/", + "canonicalPathSource": "generated", + "category": ".NET", + "contentPath": "src/contents/v1/NET/delegates-in-csharp.md", + "id": "delegates-in-csharp", + "seoDescription": "Learn Delegates for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Delegates Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Delegates", + "title": "Delegates", + "topic": "Modern C# patterns" + }, + { + "canonicalPath": "/content/di-di-container-basics-lifetimes-and-constructor-selection-in-csharp/", + "canonicalPathSource": "generated", + "category": ".NET", + "contentPath": "src/contents/v1/NET/di-di-container-basics-lifetimes-and-constructor-selection-in-csharp.md", + "id": "di-di-container-basics-lifetimes-and-constructor-selection-in-csharp", + "seoDescription": "Learn DI, DI Container Basics for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "DI, DI Container Basics Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "DI, DI Container Basics", + "title": "DI, DI Container Basics", + "topic": "Dependency injection, configuration, middleware, and logging" + }, + { + "canonicalPath": "/content/eager-explicit-and-lazy-loading-including-the-n-plus-one-query-problem/", + "canonicalPathSource": "generated", + "category": ".NET", + "contentPath": "src/contents/v1/NET/eager-explicit-and-lazy-loading-including-the-n-plus-one-query-problem.md", + "id": "eager-explicit-and-lazy-loading-including-the-n-plus-one-query-problem", + "seoDescription": "Learn Eager, Explicit, and Lazy Loading, Including the N+1 Query Problem for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Eager, Explicit, and Lazy Loading, Including the N+1 Query Problem Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Eager, Explicit, and Lazy Loading, Including the N+1 Query Problem", + "title": "Eager, Explicit, and Lazy Loading, Including the N+1 Query Problem", + "topic": "Entity Framework" + }, + { + "canonicalPath": "/content/ef-core-inmemory-provider-caveats-and-when-sqlite-docker-databases-or-testcontainers-are-safer/", + "canonicalPathSource": "generated", + "category": ".NET", + "contentPath": "src/contents/v1/NET/ef-core-inmemory-provider-caveats-and-when-sqlite-docker-databases-or-testcontainers-are-safer.md", + "id": "ef-core-inmemory-provider-caveats-and-when-sqlite-docker-databases-or-testcontainers-are-safer", + "seoDescription": "Learn EF Core InMemory Provider Caveats and When SQLite, Docker Databases, or Testcontainers Are Safer for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "EF Core InMemory Provider Caveats and When SQLite, Docker Databases, or Testcontainers Are Safer Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "EF Core InMemory Provider Caveats and When SQLite, Docker Databases, or Testcontainers Are Safer", + "title": "EF Core InMemory Provider Caveats and When SQLite, Docker Databases, or Testcontainers Are Safer", + "topic": "Testing strategy and integration testing" + }, + { + "canonicalPath": "/content/ef-core-migrations-and-production-migration-safety/", + "canonicalPathSource": "generated", + "category": ".NET", + "contentPath": "src/contents/v1/NET/ef-core-migrations-and-production-migration-safety.md", + "id": "ef-core-migrations-and-production-migration-safety", + "seoDescription": "Learn EF Core migrations and production migration safety for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "EF Core migrations and production migration safety Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "EF Core migrations and production migration safety", + "title": "EF Core migrations and production migration safety", + "topic": "Entity Framework" + }, + { + "canonicalPath": "/content/endpoint-routing-and-route-matching-in-aspnet-core/", + "canonicalPathSource": "generated", + "category": ".NET", + "contentPath": "src/contents/v1/NET/endpoint-routing-and-route-matching-in-aspnet-core.md", + "id": "endpoint-routing-and-route-matching-in-aspnet-core", + "seoDescription": "Learn Endpoint routing and route matching for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Endpoint routing and route matching Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Endpoint routing and route matching", + "title": "Endpoint routing and route matching", + "topic": "API design and implementation" + }, + { + "canonicalPath": "/content/exception-handling-in-csharp/", + "canonicalPathSource": "generated", + "category": ".NET", + "contentPath": "src/contents/v1/NET/exception-handling-in-csharp.md", + "id": "exception-handling-in-csharp", + "seoDescription": "Learn Exception Handling in C# for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Exception Handling in C# Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Exception Handling in C#", + "title": "Exception Handling in C#", + "topic": "Async programming, tasks, cancellation, and concurrency" + }, + { + "canonicalPath": "/content/exceptions-in-csharp/", + "canonicalPathSource": "generated", + "category": ".NET", + "contentPath": "src/contents/v1/NET/exceptions-in-csharp.md", + "id": "exceptions-in-csharp", + "seoDescription": "Learn Exceptions for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Exceptions Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Exceptions", + "title": "Exceptions", + "topic": "C# Language Foundations" + }, + { + "canonicalPath": "/content/generic-type-constraints-and-reusable-components-csharp/", + "canonicalPathSource": "generated", + "category": ".NET", + "contentPath": "src/contents/v1/NET/generic-type-constraints-and-reusable-components-csharp.md", + "id": "generic-type-constraints-and-reusable-components-csharp", + "seoDescription": "Learn Generic Type Constraints and Reusable Components for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Generic Type Constraints and Reusable Components Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Generic Type Constraints and Reusable Components", + "title": "Generic Type Constraints and Reusable Components", + "topic": "Modern C# patterns" + }, + { + "canonicalPath": "/content/ienumerable-vs-iqueryable-in-csharp/", + "canonicalPathSource": "generated", + "category": ".NET", + "contentPath": "src/contents/v1/NET/ienumerable-vs-iqueryable-in-csharp.md", + "id": "ienumerable-vs-iqueryable-in-csharp", + "seoDescription": "Learn IEnumerable vs IQueryable in C# for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "IEnumerable vs IQueryable in C# Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "IEnumerable vs IQueryable in C#", + "title": "IEnumerable vs IQueryable in C#", + "topic": "Modern C# patterns" + }, + { + "canonicalPath": "/content/ihttpclientfactory-and-resilient-outbound-http/", + "canonicalPathSource": "generated", + "category": ".NET", + "contentPath": "src/contents/v1/NET/ihttpclientfactory-and-resilient-outbound-http.md", + "id": "ihttpclientfactory-and-resilient-outbound-http", + "seoDescription": "Learn IHttpClientFactory and Resilient Outbound HTTP for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "IHttpClientFactory and Resilient Outbound HTTP Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "IHttpClientFactory and Resilient Outbound HTTP", + "title": "IHttpClientFactory and Resilient Outbound HTTP", + "topic": "Performance, scalability, and caching" + }, + { + "canonicalPath": "/content/in-memory-distributed-hybrid-output-caching/", + "canonicalPathSource": "generated", + "category": ".NET", + "contentPath": "src/contents/v1/NET/in-memory-distributed-hybrid-output-caching.md", + "id": "in-memory-distributed-hybrid-output-caching", + "seoDescription": "Learn In-memory, distributed, hybrid, and output caching for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "In-memory, distributed, hybrid, and output caching Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "In-memory, distributed, hybrid, and output caching", + "title": "In-memory, distributed, hybrid, and output caching", + "topic": "Performance, scalability, and caching" + }, + { + "canonicalPath": "/content/jwt-bearer-auth-claims-scopes-and-policy-based-authorization/", + "canonicalPathSource": "generated", + "category": ".NET", + "contentPath": "src/contents/v1/NET/jwt-bearer-auth-claims-scopes-and-policy-based-authorization.md", + "id": "jwt-bearer-auth-claims-scopes-and-policy-based-authorization", + "seoDescription": "Learn JWT Bearer Auth, Claims, Scopes, and Policy-Based Authorization for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "JWT Bearer Auth, Claims, Scopes, and Policy-Based Authorization Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "JWT Bearer Auth, Claims, Scopes, and Policy-Based Authorization", + "title": "JWT Bearer Auth, Claims, Scopes, and Policy-Based Authorization", + "topic": "Authentication, authorization, and web security" + }, + { + "canonicalPath": "/content/linq-querying-in-csharp/", + "canonicalPathSource": "generated", + "category": ".NET", + "contentPath": "src/contents/v1/NET/linq-querying-in-csharp.md", + "id": "linq-querying-in-csharp", + "seoDescription": "Learn LINQ Querying for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "LINQ Querying Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "LINQ Querying", + "title": "LINQ Querying", + "topic": "Modern C# patterns" + }, + { + "canonicalPath": "/content/middleware-ordering-and-cross-cutting-behavior/", + "canonicalPathSource": "generated", + "category": ".NET", + "contentPath": "src/contents/v1/NET/middleware-ordering-and-cross-cutting-behavior.md", + "id": "middleware-ordering-and-cross-cutting-behavior", + "seoDescription": "Learn Middleware Ordering and Cross-Cutting Behavior for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Middleware Ordering and Cross-Cutting Behavior Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Middleware Ordering and Cross-Cutting Behavior", + "title": "Middleware Ordering and Cross-Cutting Behavior", + "topic": "Dependency injection, configuration, middleware, and logging" + }, + { + "canonicalPath": "/content/nullable-reference-types-and-null-safety-habits-in-csharp/", + "canonicalPathSource": "generated", + "category": ".NET", + "contentPath": "src/contents/v1/NET/nullable-reference-types-and-null-safety-habits-in-csharp.md", + "id": "nullable-reference-types-and-null-safety-habits-in-csharp", + "seoDescription": "Learn Nullable reference C# for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Nullable reference C# Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Nullable reference C#", + "title": "Nullable reference C#", + "topic": "C# Language Foundations" + }, + { + "canonicalPath": "/content/observer-style-communication-in-csharp/", + "canonicalPathSource": "generated", + "category": ".NET", + "contentPath": "src/contents/v1/NET/observer-style-communication-in-csharp.md", + "id": "observer-style-communication-in-csharp", + "seoDescription": "Learn Observer-Style Communication in C# for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Observer-Style Communication in C# Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Observer-Style Communication in C#", + "title": "Observer-Style Communication in C#", + "topic": "Modern C# patterns" + }, + { + "canonicalPath": "/content/object-oriented-fundamentals/", + "canonicalPathSource": "generated", + "category": ".NET", + "contentPath": "src/contents/v1/NET/oop.md", + "id": "object-oriented-fundamentals", + "seoDescription": "Learn Object-Oriented Programming for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Object-Oriented Programming Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Object-Oriented Programming", + "title": "Object-Oriented Programming", + "topic": "C# Language Foundations" + }, + { + "canonicalPath": "/content/openapi-generation-endpoint-metadata-api-discoverability/", + "canonicalPathSource": "generated", + "category": ".NET", + "contentPath": "src/contents/v1/NET/openapi-generation-endpoint-metadata-api-discoverability.md", + "id": "openapi-generation-endpoint-metadata-api-discoverability", + "seoDescription": "Learn OpenAPI generation and API discoverability for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "OpenAPI generation and API discoverability Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "OpenAPI generation and API discoverability", + "title": "OpenAPI generation and API discoverability", + "topic": "API design and implementation" + }, + { + "canonicalPath": "/content/optimistic-concurrency-transactions-savepoints-conflict-handling/", + "canonicalPathSource": "generated", + "category": ".NET", + "contentPath": "src/contents/v1/NET/optimistic-concurrency-transactions-savepoints-conflict-handling.md", + "id": "optimistic-concurrency-transactions-savepoints-conflict-handling", + "seoDescription": "Learn Optimistic concurrency, transactions, savepoints, and conflict handling for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Optimistic concurrency, transactions, savepoints, and conflict handling Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Optimistic concurrency, transactions, savepoints, and conflict handling", + "title": "Optimistic concurrency, transactions, savepoints, and conflict handling", + "topic": "Entity Framework" + }, + { + "canonicalPath": "/content/overriding-services-and-configuration-for-tests/", + "canonicalPathSource": "generated", + "category": ".NET", + "contentPath": "src/contents/v1/NET/overriding-services-and-configuration-for-tests.md", + "id": "overriding-services-and-configuration-for-tests", + "seoDescription": "Learn Overriding services and configuration in .NET tests for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Overriding services and configuration in .NET tests Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Overriding services and configuration in .NET tests", + "title": "Overriding services and configuration in .NET tests", + "topic": "Testing strategy and integration testing" + }, + { + "canonicalPath": "/content/parameter-binding-from-route-query-string-body-form-header-and-services/", + "canonicalPathSource": "generated", + "category": ".NET", + "contentPath": "src/contents/v1/NET/parameter-binding-from-route-query-string-body-form-header-and-services.md", + "id": "parameter-binding-from-route-query-string-body-form-header-and-services", + "seoDescription": "Learn Parameter Binding for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Parameter Binding Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Parameter Binding", + "title": "Parameter Binding", + "topic": "API design and implementation" + }, + { + "canonicalPath": "/content/pattern-matching-in-csharp/", + "canonicalPathSource": "generated", + "category": ".NET", + "contentPath": "src/contents/v1/NET/pattern-matching-in-csharp.md", + "id": "pattern-matching-in-csharp", + "seoDescription": "Learn Pattern Matching in C# for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Pattern Matching in C# Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Pattern Matching in C#", + "title": "Pattern Matching in C#", + "topic": "Modern C# patterns" + }, + { + "canonicalPath": "/content/rate-limiting-memory-gc-awareness-runtime-diagnostics/", + "canonicalPathSource": "generated", + "category": ".NET", + "contentPath": "src/contents/v1/NET/rate-limiting-memory-gc-awareness-runtime-diagnostics.md", + "id": "rate-limiting-memory-gc-awareness-runtime-diagnostics", + "seoDescription": "Learn Rate limiting, memory/GC awareness, and runtime diagnostics for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Rate limiting, memory/GC awareness, and runtime diagnostics Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Rate limiting, memory/GC awareness, and runtime diagnostics", + "title": "Rate limiting, memory/GC awareness, and runtime diagnostics", + "topic": "Performance, scalability, and caching" + }, + { + "canonicalPath": "/content/records-in-csharp/", + "canonicalPathSource": "generated", + "category": ".NET", + "contentPath": "src/contents/v1/NET/records-in-csharp.md", + "id": "records-in-csharp", + "seoDescription": "Learn Records in C# for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Records in C# Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Records in C#", + "title": "Records in C#", + "topic": "Modern C# patterns" + }, + { + "canonicalPath": "/content/structured-logging-and-correlation/", + "canonicalPathSource": "generated", + "category": ".NET", + "contentPath": "src/contents/v1/NET/structured-logging-and-correlation.md", + "id": "structured-logging-and-correlation", + "seoDescription": "Learn Structured Logging and Correlation for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Structured Logging and Correlation Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Structured Logging and Correlation", + "title": "Structured Logging and Correlation", + "topic": "Dependency injection, configuration, middleware, and logging" + }, + { + "canonicalPath": "/content/switch-expressions-in-csharp/", + "canonicalPathSource": "generated", + "category": ".NET", + "contentPath": "src/contents/v1/NET/switch-expressions-in-csharp.md", + "id": "switch-expressions-in-csharp", + "seoDescription": "Learn Switch Expressions in C# for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Switch Expressions in C# Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Switch Expressions in C#", + "title": "Switch Expressions in C#", + "topic": "Modern C# patterns" + }, + { + "canonicalPath": "/content/test-doubles-mocks-stubs-fakes-mocking-integration-problems/", + "canonicalPathSource": "generated", + "category": ".NET", + "contentPath": "src/contents/v1/NET/test-doubles-mocks-stubs-fakes-mocking-integration-problems.md", + "id": "test-doubles-mocks-stubs-fakes-mocking-integration-problems", + "seoDescription": "Learn Test doubles, mocking boundaries, and integration risks in .NET for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Test doubles, mocking boundaries, and integration risks in .NET Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Test doubles, mocking boundaries, and integration risks in .NET", + "title": "Test doubles, mocking boundaries, and integration risks in .NET", + "topic": "Testing strategy and integration testing" + }, + { + "canonicalPath": "/content/testing-authentication-authorization-middleware-filters-validation-error-responses/", + "canonicalPathSource": "generated", + "category": ".NET", + "contentPath": "src/contents/v1/NET/testing-authentication-authorization-middleware-filters-validation-error-responses.md", + "id": "testing-authentication-authorization-middleware-filters-validation-error-responses", + "seoDescription": "Learn Testing ASP.NET Core security, pipeline behavior, validation, and error responses for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Testing ASP.NET Core security, pipeline behavior, validation, and error responses Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Testing ASP.NET Core security, pipeline behavior, validation, and error responses", + "title": "Testing ASP.NET Core security, pipeline behavior, validation, and error responses", + "topic": "Testing strategy and integration testing" + }, + { + "canonicalPath": "/content/timeouts-in-csharp/", + "canonicalPathSource": "generated", + "category": ".NET", + "contentPath": "src/contents/v1/NET/timeouts-in-csharp.md", + "id": "timeouts-in-csharp", + "seoDescription": "Learn Timeouts in C# for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Timeouts in C# Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Timeouts in C#", + "title": "Timeouts in C#", + "topic": "Async programming, tasks, cancellation, and concurrency" + }, + { + "canonicalPath": "/content/tracking-vs-no-tracking-queries-and-identity-resolution/", + "canonicalPathSource": "generated", + "category": ".NET", + "contentPath": "src/contents/v1/NET/tracking-vs-no-tracking-queries-and-identity-resolution.md", + "id": "tracking-vs-no-tracking-queries-and-identity-resolution", + "seoDescription": "Learn Tracking vs no-tracking queries and identity resolution for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Tracking vs no-tracking queries and identity resolution Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Tracking vs no-tracking queries and identity resolution", + "title": "Tracking vs no-tracking queries and identity resolution", + "topic": "Entity Framework" + }, + { + "canonicalPath": "/content/unit-tests-integration-tests-and-hosted-services-for-background-jobs/", + "canonicalPathSource": "generated", + "category": ".NET", + "contentPath": "src/contents/v1/NET/unit-tests-integration-tests-and-hosted-services-for-background-jobs.md", + "id": "unit-tests-integration-tests-and-hosted-services-for-background-jobs", + "seoDescription": "Learn Unit Tests, Integration Tests, and Hosted Services for Background Jobs for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Unit Tests, Integration Tests, and Hosted Services for Background Jobs Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Unit Tests, Integration Tests, and Hosted Services for Background Jobs", + "title": "Unit Tests, Integration Tests, and Hosted Services for Background Jobs", + "topic": "Performance, scalability, and caching" + }, + { + "canonicalPath": "/content/unit-tests-vs-integration-tests-vs-end-to-end-tests/", + "canonicalPathSource": "generated", + "category": ".NET", + "contentPath": "src/contents/v1/NET/unit-tests-vs-integration-tests-vs-end-to-end-tests.md", + "id": "unit-tests-vs-integration-tests-vs-end-to-end-tests", + "seoDescription": "Learn Unit Tests vs Integration Tests vs End-to-End Tests for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Unit Tests vs Integration Tests vs End-to-End Tests Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Unit Tests vs Integration Tests vs End-to-End Tests", + "title": "Unit Tests vs Integration Tests vs End-to-End Tests", + "topic": "Testing strategy and integration testing" + }, + { + "canonicalPath": "/content/value-types-vs-reference-types/", + "canonicalPathSource": "generated", + "category": ".NET", + "contentPath": "src/contents/v1/NET/value-types-vs-reference-types.md", + "id": "value-types-vs-reference-types", + "seoDescription": "Learn Value Types vs Reference Types for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Value Types vs Reference Types Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Value Types vs Reference Types", + "title": "Value Types vs Reference Types", + "topic": "C# Language Foundations" + }, + { + "canonicalPath": "/content/webapplicationfactory-testserver-httpclient-and-full-aspnet-core-pipeline-testing/", + "canonicalPathSource": "generated", + "category": ".NET", + "contentPath": "src/contents/v1/NET/webapplicationfactory-testserver-httpclient-and-full-aspnet-core-pipeline-testing.md", + "id": "webapplicationfactory-testserver-httpclient-and-full-aspnet-core-pipeline-testing", + "seoDescription": "Learn WebApplicationFactory, TestServer, HttpClient, and Full ASP.NET Core Pipeline Testing for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "WebApplicationFactory, TestServer, HttpClient, and Full ASP.NET Core Pipeline Testing Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "WebApplicationFactory, TestServer, HttpClient, and Full ASP.NET Core Pipeline Testing", + "title": "WebApplicationFactory, TestServer, HttpClient, and Full ASP.NET Core Pipeline Testing", + "topic": "Testing strategy and integration testing" + }, + { + "canonicalPath": "/content/xunit-nunit-mstest-test-naming-aaa-test-data-setup/", + "canonicalPathSource": "generated", + "category": ".NET", + "contentPath": "src/contents/v1/NET/xunit-nunit-mstest-test-naming-aaa-test-data-setup.md", + "id": "xunit-nunit-mstest-test-naming-aaa-test-data-setup", + "seoDescription": "Learn Unit testing frameworks, naming, AAA, and test data setup in .NET for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Unit testing frameworks, naming, AAA, and test data setup in .NET Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Unit testing frameworks, naming, AAA, and test data setup in .NET", + "title": "Unit testing frameworks, naming, AAA, and test data setup in .NET", + "topic": "Testing strategy and integration testing" + }, + { + "canonicalPath": "/content/access-tokens-refresh-tokens-expiration-handling-retrying-original-requests-and-refresh-token-queue-patterns/", + "canonicalPathSource": "generated", + "category": "React", + "contentPath": "src/contents/v1/React/access-tokens-refresh-tokens-expiration-handling-retrying-original-requests-and-refresh-token-queue-patterns.md", + "id": "access-tokens-refresh-tokens-expiration-handling-retrying-original-requests-and-refresh-token-queue-patterns", + "seoDescription": "Learn Access tokens, refresh tokens, expiration handling, retrying original requests, and refresh-token queue patterns for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Access tokens, refresh tokens, expiration handling, retrying original requests, and refresh-token queue patterns Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Access tokens, refresh tokens, expiration handling, retrying original requests, and refresh-token queue patterns", + "title": "Access tokens, refresh tokens, expiration handling, retrying original requests, and refresh-token queue patterns", + "topic": "Production data access, API clients, and frontend auth" + }, + { + "canonicalPath": "/content/axios-request-and-response-interceptors-for-auth-headers-global-errors-logging-and-retry-behavior/", + "canonicalPathSource": "generated", + "category": "React", + "contentPath": "src/contents/v1/React/axios-request-and-response-interceptors-for-auth-headers-global-errors-logging-and-retry-behavior.md", + "id": "axios-request-and-response-interceptors-for-auth-headers-global-errors-logging-and-retry-behavior", + "seoDescription": "Learn Axios request and response interceptors for auth headers, global errors, logging, and retry behavior for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Axios request and response interceptors for auth headers, global errors, logging, and retry behavior Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Axios request and response interceptors for auth headers, global errors, logging, and retry behavior", + "title": "Axios request and response interceptors for auth headers, global errors, logging, and retry behavior", + "topic": "Production data access, API clients, and frontend auth" + }, + { + "canonicalPath": "/content/browser-storage-trade-offs-localstorage-sessionstorage-memory-storage-and-cookies/", + "canonicalPathSource": "generated", + "category": "React", + "contentPath": "src/contents/v1/React/browser-storage-trade-offs-localstorage-sessionstorage-memory-storage-and-cookies.md", + "id": "browser-storage-trade-offs-localstorage-sessionstorage-memory-storage-and-cookies", + "seoDescription": "Learn Browser storage trade-offs: localStorage, sessionStorage, memory storage, and cookies for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Browser storage trade-offs: localStorage, sessionStorage, memory storage, and cookies Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Browser storage trade-offs: localStorage, sessionStorage, memory storage, and cookies", + "title": "Browser storage trade-offs: localStorage, sessionStorage, memory storage, and cookies", + "topic": "Production data access, API clients, and frontend auth" + }, + { + "canonicalPath": "/content/built-in-validation-async-validation-and-schema-resolvers-with-yup-or-zod/", + "canonicalPathSource": "generated", + "category": "React", + "contentPath": "src/contents/v1/React/built-in-validation-async-validation-and-schema-resolvers-with-yup-or-zod.md", + "id": "built-in-validation-async-validation-and-schema-resolvers-with-yup-or-zod", + "seoDescription": "Learn Built-in validation, async validation, and schema resolvers with Yup or Zod for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Built-in validation, async validation, and schema resolvers with Yup or Zod Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Built-in validation, async validation, and schema resolvers with Yup or Zod", + "title": "Built-in validation, async validation, and schema resolvers with Yup or Zod", + "topic": "Forms, validation, and frontend performance in production" + }, + { + "canonicalPath": "/content/centralized-api-clients-with-fetch-axios-rtk-query-or-tanstack-query/", + "canonicalPathSource": "generated", + "category": "React", + "contentPath": "src/contents/v1/React/centralized-api-clients-with-fetch-axios-rtk-query-or-tanstack-query.md", + "id": "centralized-api-clients-with-fetch-axios-rtk-query-or-tanstack-query", + "seoDescription": "Learn Centralized API clients with `fetch`, Axios, RTK Query, or TanStack Query for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Centralized API clients with `fetch`, Axios, RTK Query, or TanStack Query Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Centralized API clients with `fetch`, Axios, RTK Query, or TanStack Query", + "title": "Centralized API clients with `fetch`, Axios, RTK Query, or TanStack Query", + "topic": "Production data access, API clients, and frontend auth" + }, + { + "canonicalPath": "/content/closures-lexical-scope/", + "canonicalPathSource": "generated", + "category": "React", + "contentPath": "src/contents/v1/React/closures-lexical-scope.md", + "id": "closures-lexical-scope", + "seoDescription": "Learn Closures and lexical scope for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Closures and lexical scope Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Closures and lexical scope", + "title": "Closures and lexical scope", + "topic": "JavaScript fundamentals" + }, + { + "canonicalPath": "/content/code-splitting-lazy-loading-bundle-analysis-and-route-level-loading/", + "canonicalPathSource": "generated", + "category": "React", + "contentPath": "src/contents/v1/React/code-splitting-lazy-loading-bundle-analysis-and-route-level-loading.md", + "id": "code-splitting-lazy-loading-bundle-analysis-and-route-level-loading", + "seoDescription": "Learn Code splitting, lazy loading, bundle analysis, and route-level loading for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Code splitting, lazy loading, bundle analysis, and route-level loading Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Code splitting, lazy loading, bundle analysis, and route-level loading", + "title": "Code splitting, lazy loading, bundle analysis, and route-level loading", + "topic": "Forms, validation, and frontend performance in production" + }, + { + "canonicalPath": "/content/context-plus-reducer-vs-external-stores/", + "canonicalPathSource": "generated", + "category": "React", + "contentPath": "src/contents/v1/React/context-plus-reducer-vs-external-stores.md", + "id": "context-plus-reducer-vs-external-stores", + "seoDescription": "Learn Context plus reducer vs external stores for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Context plus reducer vs external stores Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Context plus reducer vs external stores", + "title": "Context plus reducer vs external stores", + "topic": "State management, performance, and rendering optimization" + }, + { + "canonicalPath": "/content/controlled-inputs-and-event-handling/", + "canonicalPathSource": "generated", + "category": "React", + "contentPath": "src/contents/v1/React/controlled-inputs-and-event-handling.md", + "id": "controlled-inputs-and-event-handling", + "seoDescription": "Learn Controlled inputs and event handling for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Controlled inputs and event handling Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Controlled inputs and event handling", + "title": "Controlled inputs and event handling", + "topic": "Components, props, state, and rendering behavior" + }, + { + "canonicalPath": "/content/debounce-and-throttle-for-search-filtering-autosave-and-expensive-ui-updates/", + "canonicalPathSource": "generated", + "category": "React", + "contentPath": "src/contents/v1/React/debounce-and-throttle-for-search-filtering-autosave-and-expensive-ui-updates.md", + "id": "debounce-and-throttle-for-search-filtering-autosave-and-expensive-ui-updates", + "seoDescription": "Learn Debounce and throttle for search, filtering, autosave, and expensive UI updates for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Debounce and throttle for search, filtering, autosave, and expensive UI updates Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Debounce and throttle for search, filtering, autosave, and expensive UI updates", + "title": "Debounce and throttle for search, filtering, autosave, and expensive UI updates", + "topic": "Forms, validation, and frontend performance in production" + }, + { + "canonicalPath": "/content/debugging-rendering-hydration-and-interaction-issues/", + "canonicalPathSource": "generated", + "category": "React", + "contentPath": "src/contents/v1/React/debugging-rendering-hydration-and-interaction-issues.md", + "id": "debugging-rendering-hydration-and-interaction-issues", + "seoDescription": "Learn Debugging rendering, hydration, and interaction issues for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Debugging rendering, hydration, and interaction issues Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Debugging rendering, hydration, and interaction issues", + "title": "Debugging rendering, hydration, and interaction issues", + "topic": "Testing, accessibility, and frontend debugging" + }, + { + "canonicalPath": "/content/error-states-and-retry-ux-for-failed-requests/", + "canonicalPathSource": "generated", + "category": "React", + "contentPath": "src/contents/v1/React/error-states-and-retry-ux-for-failed-requests.md", + "id": "error-states-and-retry-ux-for-failed-requests", + "seoDescription": "Learn Error states and retry UX for failed requests for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Error states and retry UX for failed requests Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Error states and retry UX for failed requests", + "title": "Error states and retry UX for failed requests", + "topic": "Routing, forms, and server communication" + }, + { + "canonicalPath": "/content/forms-validation-optimistic-updates-and-mutation-states/", + "canonicalPathSource": "generated", + "category": "React", + "contentPath": "src/contents/v1/React/forms-validation-optimistic-updates-and-mutation-states.md", + "id": "forms-validation-optimistic-updates-and-mutation-states", + "seoDescription": "Learn Forms, validation, optimistic updates, and mutation states for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Forms, validation, optimistic updates, and mutation states Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Forms, validation, optimistic updates, and mutation states", + "title": "Forms, validation, optimistic updates, and mutation states", + "topic": "Routing, forms, and server communication" + }, + { + "canonicalPath": "/content/functional-components-and-jsx-composition/", + "canonicalPathSource": "generated", + "category": "React", + "contentPath": "src/contents/v1/React/functional-components-and-jsx-composition.md", + "id": "functional-components-and-jsx-composition", + "seoDescription": "Learn Functional components and JSX composition for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Functional components and JSX composition Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Functional components and JSX composition", + "title": "Functional components and JSX composition", + "topic": "Components, props, state, and rendering behavior" + }, + { + "canonicalPath": "/content/global-loading-error-unauthorized-and-network-failure-ux-patterns/", + "canonicalPathSource": "generated", + "category": "React", + "contentPath": "src/contents/v1/React/global-loading-error-unauthorized-and-network-failure-ux-patterns.md", + "id": "global-loading-error-unauthorized-and-network-failure-ux-patterns", + "seoDescription": "Learn Global loading, error, unauthorized, and network-failure UX patterns for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Global loading, error, unauthorized, and network-failure UX patterns Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Global loading, error, unauthorized, and network-failure UX patterns", + "title": "Global loading, error, unauthorized, and network-failure UX patterns", + "topic": "Production data access, API clients, and frontend auth" + }, + { + "canonicalPath": "/content/keyboard-accessibility-semantic-html-aria-and-labels-ids/", + "canonicalPathSource": "generated", + "category": "React", + "contentPath": "src/contents/v1/React/keyboard-accessibility-semantic-html-aria-and-labels-ids.md", + "id": "keyboard-accessibility-semantic-html-aria-and-labels-ids", + "seoDescription": "Learn Keyboard accessibility, semantic HTML, ARIA, and labels/IDs for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Keyboard accessibility, semantic HTML, ARIA, and labels/IDs Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Keyboard accessibility, semantic HTML, ARIA, and labels/IDs", + "title": "Keyboard accessibility, semantic HTML, ARIA, and labels/IDs", + "topic": "Testing, accessibility, and frontend debugging" + }, + { + "canonicalPath": "/content/memoization-and-avoiding-needless-rerenders/", + "canonicalPathSource": "generated", + "category": "React", + "contentPath": "src/contents/v1/React/memoization-and-avoiding-needless-rerenders.md", + "id": "memoization-and-avoiding-needless-rerenders", + "seoDescription": "Learn Memoization and avoiding needless rerenders for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Memoization and avoiding needless rerenders Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Memoization and avoiding needless rerenders", + "title": "Memoization and avoiding needless rerenders", + "topic": "State management, performance, and rendering optimization" + }, + { + "canonicalPath": "/content/memoization-with-usememo-and-dependency-correctness/", + "canonicalPathSource": "generated", + "category": "React", + "contentPath": "src/contents/v1/React/memoization-with-usememo-and-dependency-correctness.md", + "id": "memoization-with-usememo-and-dependency-correctness", + "seoDescription": "Learn Memoization with useMemo and dependency correctness for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Memoization with useMemo and dependency correctness Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Memoization with useMemo and dependency correctness", + "title": "Memoization with useMemo and dependency correctness", + "topic": "Hooks, effects, and custom hooks" + }, + { + "canonicalPath": "/content/modules-and-import-export-behavior/", + "canonicalPathSource": "generated", + "category": "React", + "contentPath": "src/contents/v1/React/modules-and-import-export-behavior.md", + "id": "modules-and-import-export-behavior", + "seoDescription": "Learn Modules and import/export behavior for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Modules and import/export behavior Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Modules and import/export behavior", + "title": "Modules and import/export behavior", + "topic": "JavaScript fundamentals" + }, + { + "canonicalPath": "/content/narrowing-and-control-flow-analysis/", + "canonicalPathSource": "generated", + "category": "React", + "contentPath": "src/contents/v1/React/narrowing-and-control-flow-analysis.md", + "id": "narrowing-and-control-flow-analysis", + "seoDescription": "Learn Narrowing and control-flow analysis for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Narrowing and control-flow analysis Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Narrowing and control-flow analysis", + "title": "Narrowing and control-flow analysis", + "topic": "TypeScript for React" + }, + { + "canonicalPath": "/content/nested-routes-layouts-params-and-route-boundaries/", + "canonicalPathSource": "generated", + "category": "React", + "contentPath": "src/contents/v1/React/nested-routes-layouts-params-and-route-boundaries.md", + "id": "nested-routes-layouts-params-and-route-boundaries", + "seoDescription": "Learn Nested routes, layouts, params, and route boundaries for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Nested routes, layouts, params, and route boundaries Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Nested routes, layouts, params, and route boundaries", + "title": "Nested routes, layouts, params, and route boundaries", + "topic": "Routing, forms, and server communication" + }, + { + "canonicalPath": "/content/preventing-duplicate-requests-canceling-stale-requests-and-avoiding-race-conditions/", + "canonicalPathSource": "generated", + "category": "React", + "contentPath": "src/contents/v1/React/preventing-duplicate-requests-canceling-stale-requests-and-avoiding-race-conditions.md", + "id": "preventing-duplicate-requests-canceling-stale-requests-and-avoiding-race-conditions", + "seoDescription": "Learn Preventing duplicate requests, canceling stale requests, and avoiding race conditions for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Preventing duplicate requests, canceling stale requests, and avoiding race conditions Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Preventing duplicate requests, canceling stale requests, and avoiding race conditions", + "title": "Preventing duplicate requests, canceling stale requests, and avoiding race conditions", + "topic": "Forms, validation, and frontend performance in production" + }, + { + "canonicalPath": "/content/promises-asynchronous-javascript/", + "canonicalPathSource": "generated", + "category": "React", + "contentPath": "src/contents/v1/React/promises-asynchronous-javascript.md", + "id": "promises-asynchronous-javascript", + "seoDescription": "Learn Promises and asynchronous JavaScript for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Promises and asynchronous JavaScript Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Promises and asynchronous JavaScript", + "title": "Promises and asynchronous JavaScript", + "topic": "JavaScript fundamentals" + }, + { + "canonicalPath": "/content/proper-use-effect-usage-and-cleanup/", + "canonicalPathSource": "generated", + "category": "React", + "contentPath": "src/contents/v1/React/proper-use-effect-usage-and-cleanup.md", + "id": "proper-use-effect-usage-and-cleanup", + "seoDescription": "Learn Proper useEffect usage and cleanup for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Proper useEffect usage and cleanup Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Proper useEffect usage and cleanup", + "title": "Proper useEffect usage and cleanup", + "topic": "Hooks, effects, and custom hooks" + }, + { + "canonicalPath": "/content/props-flow-local-state-and-lifting-state-up/", + "canonicalPathSource": "generated", + "category": "React", + "contentPath": "src/contents/v1/React/props-flow-local-state-and-lifting-state-up.md", + "id": "props-flow-local-state-and-lifting-state-up", + "seoDescription": "Learn Props flow, local state, and lifting state up for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Props flow, local state, and lifting state up Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Props flow, local state, and lifting state up", + "title": "Props flow, local state, and lifting state up", + "topic": "Components, props, state, and rendering behavior" + }, + { + "canonicalPath": "/content/query-strategy-and-avoiding-implementation-detail-assertions/", + "canonicalPathSource": "generated", + "category": "React", + "contentPath": "src/contents/v1/React/query-strategy-and-avoiding-implementation-detail-assertions.md", + "id": "query-strategy-and-avoiding-implementation-detail-assertions", + "seoDescription": "Learn Query strategy and avoiding implementation-detail assertions for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Query strategy and avoiding implementation-detail assertions Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Query strategy and avoiding implementation-detail assertions", + "title": "Query strategy and avoiding implementation-detail assertions", + "topic": "Testing, accessibility, and frontend debugging" + }, + { + "canonicalPath": "/content/react-hook-form-fundamentals-uncontrolled-inputs-controller-and-form-state/", + "canonicalPathSource": "generated", + "category": "React", + "contentPath": "src/contents/v1/React/react-hook-form-fundamentals-uncontrolled-inputs-controller-and-form-state.md", + "id": "react-hook-form-fundamentals-uncontrolled-inputs-controller-and-form-state", + "seoDescription": "Learn React Hook Form fundamentals, uncontrolled inputs, `Controller`, and form state for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "React Hook Form fundamentals, uncontrolled inputs, `Controller`, and form state Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "React Hook Form fundamentals, uncontrolled inputs, `Controller`, and form state", + "title": "React Hook Form fundamentals, uncontrolled inputs, `Controller`, and form state", + "topic": "Forms, validation, and frontend performance in production" + }, + { + "canonicalPath": "/content/reducing-state-complexity-and-avoiding-unnecessary-rerenders-in-form-heavy-pages/", + "canonicalPathSource": "generated", + "category": "React", + "contentPath": "src/contents/v1/React/reducing-state-complexity-and-avoiding-unnecessary-rerenders-in-form-heavy-pages.md", + "id": "reducing-state-complexity-and-avoiding-unnecessary-rerenders-in-form-heavy-pages", + "seoDescription": "Learn Reducing state complexity and avoiding unnecessary rerenders in form-heavy pages for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Reducing state complexity and avoiding unnecessary rerenders in form-heavy pages Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Reducing state complexity and avoiding unnecessary rerenders in form-heavy pages", + "title": "Reducing state complexity and avoiding unnecessary rerenders in form-heavy pages", + "topic": "Forms, validation, and frontend performance in production" + }, + { + "canonicalPath": "/content/rerender-triggers-derived-state-and-avoiding-duplicated-state/", + "canonicalPathSource": "generated", + "category": "React", + "contentPath": "src/contents/v1/React/rerender-triggers-derived-state-and-avoiding-duplicated-state.md", + "id": "rerender-triggers-derived-state-and-avoiding-duplicated-state", + "seoDescription": "Learn Rerender triggers, derived state, and avoiding duplicated state for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Rerender triggers, derived state, and avoiding duplicated state Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Rerender triggers, derived state, and avoiding duplicated state", + "title": "Rerender triggers, derived state, and avoiding duplicated state", + "topic": "Components, props, state, and rendering behavior" + }, + { + "canonicalPath": "/content/route-loaders-actions-or-equivalent-data-loading-patterns/", + "canonicalPathSource": "generated", + "category": "React", + "contentPath": "src/contents/v1/React/route-loaders-actions-or-equivalent-data-loading-patterns.md", + "id": "route-loaders-actions-or-equivalent-data-loading-patterns", + "seoDescription": "Learn Route loaders/actions or equivalent data-loading patterns for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Route loaders/actions or equivalent data-loading patterns Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Route loaders/actions or equivalent data-loading patterns", + "title": "Route loaders/actions or equivalent data-loading patterns", + "topic": "Routing, forms, and server communication" + }, + { + "canonicalPath": "/content/rtk-query-custom-base-queries-including-axios-based-basequery-patterns/", + "canonicalPathSource": "generated", + "category": "React", + "contentPath": "src/contents/v1/React/rtk-query-custom-base-queries-including-axios-based-basequery-patterns.md", + "id": "rtk-query-custom-base-queries-including-axios-based-basequery-patterns", + "seoDescription": "Learn RTK Query custom base queries, including Axios-based baseQuery patterns for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "RTK Query custom base queries, including Axios-based baseQuery patterns Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "RTK Query custom base queries, including Axios-based baseQuery patterns", + "title": "RTK Query custom base queries, including Axios-based baseQuery patterns", + "topic": "Production data access, API clients, and frontend auth" + }, + { + "canonicalPath": "/content/secure-cookie-flags-httponly-secure-samesite-path-domain-and-expiration/", + "canonicalPathSource": "generated", + "category": "React", + "contentPath": "src/contents/v1/React/secure-cookie-flags-httponly-secure-samesite-path-domain-and-expiration.md", + "id": "secure-cookie-flags-httponly-secure-samesite-path-domain-and-expiration", + "seoDescription": "Learn Secure cookie flags: `HttpOnly`, `Secure`, `SameSite`, path, domain, and expiration for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Secure cookie flags: `HttpOnly`, `Secure`, `SameSite`, path, domain, and expiration Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Secure cookie flags: `HttpOnly`, `Secure`, `SameSite`, path, domain, and expiration", + "title": "Secure cookie flags: `HttpOnly`, `Secure`, `SameSite`, path, domain, and expiration", + "topic": "Production data access, API clients, and frontend auth" + }, + { + "canonicalPath": "/content/strict-equality-reference-identity-and-immutability-implications/", + "canonicalPathSource": "generated", + "category": "React", + "contentPath": "src/contents/v1/React/strict-equality-reference-identity-and-immutability-implications.md", + "id": "strict-equality-reference-identity-and-immutability-implications", + "seoDescription": "Learn Strict equality, reference identity, and immutability implications for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Strict equality, reference identity, and immutability implications Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Strict equality, reference identity, and immutability implications", + "title": "Strict equality, reference identity, and immutability implications", + "topic": "JavaScript fundamentals" + }, + { + "canonicalPath": "/content/suspense-transitions-and-rendering-priority-concepts/", + "canonicalPathSource": "generated", + "category": "React", + "contentPath": "src/contents/v1/React/suspense-transitions-and-rendering-priority-concepts.md", + "id": "suspense-transitions-and-rendering-priority-concepts", + "seoDescription": "Learn Suspense, transitions, and rendering priority concepts for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Suspense, transitions, and rendering priority concepts Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Suspense, transitions, and rendering priority concepts", + "title": "Suspense, transitions, and rendering priority concepts", + "topic": "State management, performance, and rendering optimization" + }, + { + "canonicalPath": "/content/tsconfig-basics-strict-mode-module-settings/", + "canonicalPathSource": "generated", + "category": "React", + "contentPath": "src/contents/v1/React/tsconfig-basics-strict-mode-module-settings.md", + "id": "tsconfig-basics-strict-mode-module-settings", + "seoDescription": "Learn tsconfig basics, strict mode, and module settings for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "tsconfig basics, strict mode, and module settings Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "tsconfig basics, strict mode, and module settings", + "title": "tsconfig basics, strict mode, and module settings", + "topic": "TypeScript for React" + }, + { + "canonicalPath": "/content/types-unions-intersections-discriminated-unions/", + "canonicalPathSource": "generated", + "category": "React", + "contentPath": "src/contents/v1/React/types-unions-intersections-discriminated-unions.md", + "id": "types-unions-intersections-discriminated-unions", + "seoDescription": "Learn Types, unions, intersections, and discriminated unions for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Types, unions, intersections, and discriminated unions Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Types, unions, intersections, and discriminated unions", + "title": "Types, unions, intersections, and discriminated unions", + "topic": "TypeScript for React" + }, + { + "canonicalPath": "/content/use-state-use-reducer-use-context-and-custom-hooks/", + "canonicalPathSource": "generated", + "category": "React", + "contentPath": "src/contents/v1/React/use-state-use-reducer-use-context-and-custom-hooks.md", + "id": "use-state-use-reducer-use-context-and-custom-hooks", + "seoDescription": "Learn useState, useReducer, useContext, and custom hooks for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "useState, useReducer, useContext, and custom hooks Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "useState, useReducer, useContext, and custom hooks", + "title": "useState, useReducer, useContext, and custom hooks", + "topic": "Hooks, effects, and custom hooks" + }, + { + "canonicalPath": "/content/use-sync-external-store-and-subscribing-to-external-state/", + "canonicalPathSource": "generated", + "category": "React", + "contentPath": "src/contents/v1/React/use-sync-external-store-and-subscribing-to-external-state.md", + "id": "use-sync-external-store-and-subscribing-to-external-state", + "seoDescription": "Learn useSyncExternalStore and subscribing to external state for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "useSyncExternalStore and subscribing to external state Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "useSyncExternalStore and subscribing to external state", + "title": "useSyncExternalStore and subscribing to external state", + "topic": "State management, performance, and rendering optimization" + }, + { + "canonicalPath": "/content/user-centric-testing-with-react-testing-library/", + "canonicalPathSource": "generated", + "category": "React", + "contentPath": "src/contents/v1/React/user-centric-testing-with-react-testing-library.md", + "id": "user-centric-testing-with-react-testing-library", + "seoDescription": "Learn User-centric testing with React Testing Library for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "User-centric testing with React Testing Library Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "User-centric testing with React Testing Library", + "title": "User-centric testing with React Testing Library", + "topic": "Testing, accessibility, and frontend debugging" + }, + { + "canonicalPath": "/content/utility-types-and-conditional-types/", + "canonicalPathSource": "generated", + "category": "React", + "contentPath": "src/contents/v1/React/utility-types-and-conditional-types.md", + "id": "utility-types-and-conditional-types", + "seoDescription": "Learn Utility types and conditional types for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Utility types and conditional types Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Utility types and conditional types", + "title": "Utility types and conditional types", + "topic": "TypeScript for React" + }, + { + "canonicalPath": "/content/watch-vs-usewatch-form-render-isolation-and-large-form-performance/", + "canonicalPathSource": "generated", + "category": "React", + "contentPath": "src/contents/v1/React/watch-vs-usewatch-form-render-isolation-and-large-form-performance.md", + "id": "watch-vs-usewatch-form-render-isolation-and-large-form-performance", + "seoDescription": "Learn `watch` vs `useWatch`, form render isolation, and large-form performance for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "`watch` vs `useWatch`, form render isolation, and large-form performance Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "`watch` vs `useWatch`, form render isolation, and large-form performance", + "title": "`watch` vs `useWatch`, form render isolation, and large-form performance", + "topic": "Forms, validation, and frontend performance in production" + }, + { + "canonicalPath": "/content/you-might-not-need-an-effect-patterns/", + "canonicalPathSource": "generated", + "category": "React", + "contentPath": "src/contents/v1/React/you-might-not-need-an-effect-patterns.md", + "id": "you-might-not-need-an-effect-patterns", + "seoDescription": "Learn “You might not need an effect” patterns for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "“You might not need an effect” patterns Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "“You might not need an effect” patterns", + "title": "“You might not need an effect” patterns", + "topic": "Hooks, effects, and custom hooks" + }, + { + "canonicalPath": "/content/acid-basics-and-transaction-scope/", + "canonicalPathSource": "generated", + "category": "SQL", + "contentPath": "src/contents/v1/SQL/acid-basics-and-transaction-scope.md", + "id": "acid-basics-and-transaction-scope", + "seoDescription": "Learn ACID basics and transaction scope for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "ACID basics and transaction scope Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "ACID basics and transaction scope", + "title": "ACID basics and transaction scope", + "topic": "Transactions, isolation, locking, and deadlocks" + }, + { + "canonicalPath": "/content/backup-types-restore-strategy-and-recovery-objectives/", + "canonicalPathSource": "generated", + "category": "SQL", + "contentPath": "src/contents/v1/SQL/backup-types-restore-strategy-and-recovery-objectives.md", + "id": "backup-types-restore-strategy-and-recovery-objectives", + "seoDescription": "Learn Backup types, restore strategy, and recovery objectives for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Backup types, restore strategy, and recovery objectives Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Backup types, restore strategy, and recovery objectives", + "title": "Backup types, restore strategy, and recovery objectives", + "topic": "Backup, recovery, HA/DR, security, and temporal data" + }, + { + "canonicalPath": "/content/baselines-workload-comparisons-and-targeted-tuning/", + "canonicalPathSource": "generated", + "category": "SQL", + "contentPath": "src/contents/v1/SQL/baselines-workload-comparisons-and-targeted-tuning.md", + "id": "baselines-workload-comparisons-and-targeted-tuning", + "seoDescription": "Learn Baselines, workload comparisons, and targeted tuning for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Baselines, workload comparisons, and targeted tuning Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Baselines, workload comparisons, and targeted tuning", + "title": "Baselines, workload comparisons, and targeted tuning", + "topic": "Query tuning, Query Store, and plan stability" + }, + { + "canonicalPath": "/content/char-vs-varchar-vs-nvarchar-and-unicode-storage-choices/", + "canonicalPathSource": "generated", + "category": "SQL", + "contentPath": "src/contents/v1/SQL/char-vs-varchar-vs-nvarchar-and-unicode-storage-choices.md", + "id": "char-vs-varchar-vs-nvarchar-and-unicode-storage-choices", + "seoDescription": "Learn CHAR vs VARCHAR vs NVARCHAR and Unicode storage choices for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "CHAR vs VARCHAR vs NVARCHAR and Unicode storage choices Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "CHAR vs VARCHAR vs NVARCHAR and Unicode storage choices", + "title": "CHAR vs VARCHAR vs NVARCHAR and Unicode storage choices", + "topic": "SQL practical interview comparisons and SQL Server-specific features" + }, + { + "canonicalPath": "/content/clustered-vs-nonclustered-indexes/", + "canonicalPathSource": "generated", + "category": "SQL", + "contentPath": "src/contents/v1/SQL/clustered-vs-nonclustered-indexes.md", + "id": "clustered-vs-nonclustered-indexes", + "seoDescription": "Learn Clustered vs nonclustered indexes for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Clustered vs nonclustered indexes Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Clustered vs nonclustered indexes", + "title": "Clustered vs nonclustered indexes", + "topic": "Indexes, statistics, and execution plans" + }, + { + "canonicalPath": "/content/computed-columns-persisted-computed-columns-and-indexed-views/", + "canonicalPathSource": "generated", + "category": "SQL", + "contentPath": "src/contents/v1/SQL/computed-columns-persisted-computed-columns-and-indexed-views.md", + "id": "computed-columns-persisted-computed-columns-and-indexed-views", + "seoDescription": "Learn Computed columns, persisted computed columns, and indexed views for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Computed columns, persisted computed columns, and indexed views Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Computed columns, persisted computed columns, and indexed views", + "title": "Computed columns, persisted computed columns, and indexed views", + "topic": "SQL practical interview comparisons and SQL Server-specific features" + }, + { + "canonicalPath": "/content/data-types-nullability-and-business-rule-enforcement/", + "canonicalPathSource": "generated", + "category": "SQL", + "contentPath": "src/contents/v1/SQL/data-types-nullability-and-business-rule-enforcement.md", + "id": "data-types-nullability-and-business-rule-enforcement", + "seoDescription": "Learn Data types, nullability, and business-rule enforcement for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Data types, nullability, and business-rule enforcement Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Data types, nullability, and business-rule enforcement", + "title": "Data types, nullability, and business-rule enforcement", + "topic": "Relational modeling and normalization" + }, + { + "canonicalPath": "/content/ddl-vs-dml-vs-dcl-and-how-schema-data-and-permissions-changes-differ/", + "canonicalPathSource": "generated", + "category": "SQL", + "contentPath": "src/contents/v1/SQL/ddl-vs-dml-vs-dcl-and-how-schema-data-and-permissions-changes-differ.md", + "id": "ddl-vs-dml-vs-dcl-and-how-schema-data-and-permissions-changes-differ", + "seoDescription": "Learn DDL vs DML vs DCL and how schema, data, and permissions changes differ for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "DDL vs DML vs DCL and how schema, data, and permissions changes differ Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "DDL vs DML vs DCL and how schema, data, and permissions changes differ", + "title": "DDL vs DML vs DCL and how schema, data, and permissions changes differ", + "topic": "SQL practical interview comparisons and SQL Server-specific features" + }, + { + "canonicalPath": "/content/deadlocks-detection-and-mitigation/", + "canonicalPathSource": "generated", + "category": "SQL", + "contentPath": "src/contents/v1/SQL/deadlocks-detection-and-mitigation.md", + "id": "deadlocks-detection-and-mitigation", + "seoDescription": "Learn Deadlocks, detection, and mitigation for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Deadlocks, detection, and mitigation Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Deadlocks, detection, and mitigation", + "title": "Deadlocks, detection, and mitigation", + "topic": "Transactions, isolation, locking, and deadlocks" + }, + { + "canonicalPath": "/content/delete-vs-truncate-including-logging-identity-reset-rollback-behavior-and-foreign-key-limitations/", + "canonicalPathSource": "generated", + "category": "SQL", + "contentPath": "src/contents/v1/SQL/delete-vs-truncate-including-logging-identity-reset-rollback-behavior-and-foreign-key-limitations.md", + "id": "delete-vs-truncate-including-logging-identity-reset-rollback-behavior-and-foreign-key-limitations", + "seoDescription": "Learn DELETE vs TRUNCATE, including logging, identity reset, rollback behavior, and foreign key limitations for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "DELETE vs TRUNCATE, including logging, identity reset, rollback behavior, and foreign key limitations Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "DELETE vs TRUNCATE, including logging, identity reset, rollback behavior, and foreign key limitations", + "title": "DELETE vs TRUNCATE, including logging, identity reset, rollback behavior, and foreign key limitations", + "topic": "SQL practical interview comparisons and SQL Server-specific features" + }, + { + "canonicalPath": "/content/estimated-vs-actual-execution-plans/", + "canonicalPathSource": "generated", + "category": "SQL", + "contentPath": "src/contents/v1/SQL/estimated-vs-actual-execution-plans.md", + "id": "estimated-vs-actual-execution-plans", + "seoDescription": "Learn Estimated vs actual execution plans for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Estimated vs actual execution plans Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Estimated vs actual execution plans", + "title": "Estimated vs actual execution plans", + "topic": "Indexes, statistics, and execution plans" + }, + { + "canonicalPath": "/content/group-by-and-aggregate-functions/", + "canonicalPathSource": "generated", + "category": "SQL", + "contentPath": "src/contents/v1/SQL/group-by-and-aggregate-functions.md", + "id": "group-by-and-aggregate-functions", + "seoDescription": "Learn GROUP BY and aggregate functions for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "GROUP BY and aggregate functions Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "GROUP BY and aggregate functions", + "title": "GROUP BY and aggregate functions", + "topic": "Core querying and data retrieval" + }, + { + "canonicalPath": "/content/high-availability-and-disaster-recovery-basics/", + "canonicalPathSource": "generated", + "category": "SQL", + "contentPath": "src/contents/v1/SQL/high-availability-and-disaster-recovery-basics.md", + "id": "high-availability-and-disaster-recovery-basics", + "seoDescription": "Learn High availability and disaster recovery basics for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "High availability and disaster recovery basics Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "High availability and disaster recovery basics", + "title": "High availability and disaster recovery basics", + "topic": "Backup, recovery, HA/DR, security, and temporal data" + }, + { + "canonicalPath": "/content/inner-left-other-join-patterns/", + "canonicalPathSource": "generated", + "category": "SQL", + "contentPath": "src/contents/v1/SQL/inner-left-other-join-patterns.md", + "id": "inner-left-other-join-patterns", + "seoDescription": "Learn INNER, LEFT, and other join patterns for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "INNER, LEFT, and other join patterns Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "INNER, LEFT, and other join patterns", + "title": "INNER, LEFT, and other join patterns", + "topic": "Relational modeling and normalization" + }, + { + "canonicalPath": "/content/isolation-levels-and-row-versioning/", + "canonicalPathSource": "generated", + "category": "SQL", + "contentPath": "src/contents/v1/SQL/isolation-levels-and-row-versioning.md", + "id": "isolation-levels-and-row-versioning", + "seoDescription": "Learn Isolation levels and row versioning for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Isolation levels and row versioning Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Isolation levels and row versioning", + "title": "Isolation levels and row versioning", + "topic": "Transactions, isolation, locking, and deadlocks" + }, + { + "canonicalPath": "/content/knowing-when-to-change-sql-indexes-or-schema-instead-of-forcing-hints/", + "canonicalPathSource": "generated", + "category": "SQL", + "contentPath": "src/contents/v1/SQL/knowing-when-to-change-sql-indexes-or-schema-instead-of-forcing-hints.md", + "id": "knowing-when-to-change-sql-indexes-or-schema-instead-of-forcing-hints", + "seoDescription": "Learn Knowing when to change SQL, indexes, or schema instead of forcing hints for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Knowing when to change SQL, indexes, or schema instead of forcing hints Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Knowing when to change SQL, indexes, or schema instead of forcing hints", + "title": "Knowing when to change SQL, indexes, or schema instead of forcing hints", + "topic": "Query tuning, Query Store, and plan stability" + }, + { + "canonicalPath": "/content/least-privilege-roles-row-level-security-and-masking/", + "canonicalPathSource": "generated", + "category": "SQL", + "contentPath": "src/contents/v1/SQL/least-privilege-roles-row-level-security-and-masking.md", + "id": "least-privilege-roles-row-level-security-and-masking", + "seoDescription": "Learn Least privilege, roles, row-level security, and masking for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Least privilege, roles, row-level security, and masking Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Least privilege, roles, row-level security, and masking", + "title": "Least privilege, roles, row-level security, and masking", + "topic": "Backup, recovery, HA/DR, security, and temporal data" + }, + { + "canonicalPath": "/content/local-vs-global-temporary-tables-and-table-variables/", + "canonicalPathSource": "generated", + "category": "SQL", + "contentPath": "src/contents/v1/SQL/local-vs-global-temporary-tables-and-table-variables.md", + "id": "local-vs-global-temporary-tables-and-table-variables", + "seoDescription": "Learn Local vs global temporary tables and table variables for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Local vs global temporary tables and table variables Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Local vs global temporary tables and table variables", + "title": "Local vs global temporary tables and table variables", + "topic": "SQL practical interview comparisons and SQL Server-specific features" + }, + { + "canonicalPath": "/content/locking-behavior-and-blocking/", + "canonicalPathSource": "generated", + "category": "SQL", + "contentPath": "src/contents/v1/SQL/locking-behavior-and-blocking.md", + "id": "locking-behavior-and-blocking", + "seoDescription": "Learn Locking behavior and blocking for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Locking behavior and blocking Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Locking behavior and blocking", + "title": "Locking behavior and blocking", + "topic": "Transactions, isolation, locking, and deadlocks" + }, + { + "canonicalPath": "/content/merge-and-upsert-patterns-including-concurrency-cautions/", + "canonicalPathSource": "generated", + "category": "SQL", + "contentPath": "src/contents/v1/SQL/merge-and-upsert-patterns-including-concurrency-cautions.md", + "id": "merge-and-upsert-patterns-including-concurrency-cautions", + "seoDescription": "Learn MERGE and upsert patterns, including concurrency cautions for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "MERGE and upsert patterns, including concurrency cautions Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "MERGE and upsert patterns, including concurrency cautions", + "title": "MERGE and upsert patterns, including concurrency cautions", + "topic": "SQL practical interview comparisons and SQL Server-specific features" + }, + { + "canonicalPath": "/content/normalization-and-when-denormalization-is-justified/", + "canonicalPathSource": "generated", + "category": "SQL", + "contentPath": "src/contents/v1/SQL/normalization-and-when-denormalization-is-justified.md", + "id": "normalization-and-when-denormalization-is-justified", + "seoDescription": "Learn Normalization and when denormalization is justified for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Normalization and when denormalization is justified Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Normalization and when denormalization is justified", + "title": "Normalization and when denormalization is justified", + "topic": "Relational modeling and normalization" + }, + { + "canonicalPath": "/content/null-handling-and-common-filtering-mistakes/", + "canonicalPathSource": "generated", + "category": "SQL", + "contentPath": "src/contents/v1/SQL/null-handling-and-common-filtering-mistakes.md", + "id": "null-handling-and-common-filtering-mistakes", + "seoDescription": "Learn NULL handling and common filtering mistakes for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "NULL handling and common filtering mistakes Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "NULL handling and common filtering mistakes", + "title": "NULL handling and common filtering mistakes", + "topic": "Core querying and data retrieval" + }, + { + "canonicalPath": "/content/one-to-one-one-to-many-many-to-many-relationships/", + "canonicalPathSource": "generated", + "category": "SQL", + "contentPath": "src/contents/v1/SQL/one-to-one-one-to-many-many-to-many-relationships.md", + "id": "one-to-one-one-to-many-many-to-many-relationships", + "seoDescription": "Learn One-to-one, one-to-many, and many-to-many relationships for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "One-to-one, one-to-many, and many-to-many relationships Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "One-to-one, one-to-many, and many-to-many relationships", + "title": "One-to-one, one-to-many, and many-to-many relationships", + "topic": "Core querying and data retrieval" + }, + { + "canonicalPath": "/content/over-partition-by-row-number-rank-and-running-aggregates/", + "canonicalPathSource": "generated", + "category": "SQL", + "contentPath": "src/contents/v1/SQL/over-partition-by-row-number-rank-and-running-aggregates.md", + "id": "over-partition-by-row-number-rank-and-running-aggregates", + "seoDescription": "Learn OVER, PARTITION BY, ROW_NUMBER, RANK, and running aggregates for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "OVER, PARTITION BY, ROW_NUMBER, RANK, and running aggregates Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "OVER, PARTITION BY, ROW_NUMBER, RANK, and running aggregates", + "title": "OVER, PARTITION BY, ROW_NUMBER, RANK, and running aggregates", + "topic": "Advanced querying with window functions and CTEs" + }, + { + "canonicalPath": "/content/parameter-sensitive-plans-and-skewed-data-distributions/", + "canonicalPathSource": "generated", + "category": "SQL", + "contentPath": "src/contents/v1/SQL/parameter-sensitive-plans-and-skewed-data-distributions.md", + "id": "parameter-sensitive-plans-and-skewed-data-distributions", + "seoDescription": "Learn Parameter-sensitive plans and skewed data distributions for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Parameter-sensitive plans and skewed data distributions Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Parameter-sensitive plans and skewed data distributions", + "title": "Parameter-sensitive plans and skewed data distributions", + "topic": "Query tuning, Query Store, and plan stability" + }, + { + "canonicalPath": "/content/primary-key-vs-unique-constraint-candidate-keys-foreign-keys-and-constraint-design/", + "canonicalPathSource": "generated", + "category": "SQL", + "contentPath": "src/contents/v1/SQL/primary-key-vs-unique-constraint-candidate-keys-foreign-keys-and-constraint-design.md", + "id": "primary-key-vs-unique-constraint-candidate-keys-foreign-keys-and-constraint-design", + "seoDescription": "Learn Primary key vs unique constraint, candidate keys, foreign keys, and constraint design for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Primary key vs unique constraint, candidate keys, foreign keys, and constraint design Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Primary key vs unique constraint, candidate keys, foreign keys, and constraint design", + "title": "Primary key vs unique constraint, candidate keys, foreign keys, and constraint design", + "topic": "SQL practical interview comparisons and SQL Server-specific features" + }, + { + "canonicalPath": "/content/primary-keys-foreign-keys-constraints/", + "canonicalPathSource": "generated", + "category": "SQL", + "contentPath": "src/contents/v1/SQL/primary-keys-foreign-keys-constraints.md", + "id": "primary-keys-foreign-keys-constraints", + "seoDescription": "Learn Primary keys, foreign keys, and constraints for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Primary keys, foreign keys, and constraints Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Primary keys, foreign keys, and constraints", + "title": "Primary keys, foreign keys, and constraints", + "topic": "Core querying and data retrieval" + }, + { + "canonicalPath": "/content/query-store-for-regression-detection-and-plan-history/", + "canonicalPathSource": "generated", + "category": "SQL", + "contentPath": "src/contents/v1/SQL/query-store-for-regression-detection-and-plan-history.md", + "id": "query-store-for-regression-detection-and-plan-history", + "seoDescription": "Learn Query Store for regression detection and plan history for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Query Store for regression detection and plan history Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Query Store for regression detection and plan history", + "title": "Query Store for regression detection and plan history", + "topic": "Query tuning, Query Store, and plan stability" + }, + { + "canonicalPath": "/content/replacing-brittle-subqueries-with-clearer-query-structure/", + "canonicalPathSource": "generated", + "category": "SQL", + "contentPath": "src/contents/v1/SQL/replacing-brittle-subqueries-with-clearer-query-structure.md", + "id": "replacing-brittle-subqueries-with-clearer-query-structure", + "seoDescription": "Learn Replacing brittle subqueries with clearer query structure for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Replacing brittle subqueries with clearer query structure Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Replacing brittle subqueries with clearer query structure", + "title": "Replacing brittle subqueries with clearer query structure", + "topic": "Advanced querying with window functions and CTEs" + }, + { + "canonicalPath": "/content/schema-migrations-source-control-reviewable-sql-scripts-and-release-safety/", + "canonicalPathSource": "generated", + "category": "SQL", + "contentPath": "src/contents/v1/SQL/schema-migrations-source-control-reviewable-sql-scripts-and-release-safety.md", + "id": "schema-migrations-source-control-reviewable-sql-scripts-and-release-safety", + "seoDescription": "Learn Schema migrations, source control, reviewable SQL scripts, and release safety for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Schema migrations, source control, reviewable SQL scripts, and release safety Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Schema migrations, source control, reviewable SQL scripts, and release safety", + "title": "Schema migrations, source control, reviewable SQL scripts, and release safety", + "topic": "Database programmability and schema evolution" + }, + { + "canonicalPath": "/content/select-where-order-by-top-offset-fetch/", + "canonicalPathSource": "generated", + "category": "SQL", + "contentPath": "src/contents/v1/SQL/select-where-order-by-top-offset-fetch.md", + "id": "select-where-order-by-top-offset-fetch", + "seoDescription": "Learn SELECT, WHERE, ORDER BY, TOP/OFFSET-FETCH for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "SELECT, WHERE, ORDER BY, TOP/OFFSET-FETCH Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "SELECT, WHERE, ORDER BY, TOP/OFFSET-FETCH", + "title": "SELECT, WHERE, ORDER BY, TOP/OFFSET-FETCH", + "topic": "Relational modeling and normalization" + }, + { + "canonicalPath": "/content/selectivity-covering-indexes-and-index-maintenance-trade-offs/", + "canonicalPathSource": "generated", + "category": "SQL", + "contentPath": "src/contents/v1/SQL/selectivity-covering-indexes-and-index-maintenance-trade-offs.md", + "id": "selectivity-covering-indexes-and-index-maintenance-trade-offs", + "seoDescription": "Learn Selectivity, covering indexes, and index maintenance trade-offs for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Selectivity, covering indexes, and index maintenance trade-offs Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Selectivity, covering indexes, and index maintenance trade-offs", + "title": "Selectivity, covering indexes, and index maintenance trade-offs", + "topic": "Indexes, statistics, and execution plans" + }, + { + "canonicalPath": "/content/standard-and-recursive-ctes/", + "canonicalPathSource": "generated", + "category": "SQL", + "contentPath": "src/contents/v1/SQL/standard-and-recursive-ctes.md", + "id": "standard-and-recursive-ctes", + "seoDescription": "Learn Standard and recursive CTEs for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Standard and recursive CTEs Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Standard and recursive CTEs", + "title": "Standard and recursive CTEs", + "topic": "Advanced querying with window functions and CTEs" + }, + { + "canonicalPath": "/content/statistics-and-how-the-optimizer-uses-them/", + "canonicalPathSource": "generated", + "category": "SQL", + "contentPath": "src/contents/v1/SQL/statistics-and-how-the-optimizer-uses-them.md", + "id": "statistics-and-how-the-optimizer-uses-them", + "seoDescription": "Learn Statistics and how the optimizer uses them for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Statistics and how the optimizer uses them Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Statistics and how the optimizer uses them", + "title": "Statistics and how the optimizer uses them", + "topic": "Indexes, statistics, and execution plans" + }, + { + "canonicalPath": "/content/stored-procedure-transaction-patterns-with-try-catch-output-parameters-and-error-handling/", + "canonicalPathSource": "generated", + "category": "SQL", + "contentPath": "src/contents/v1/SQL/stored-procedure-transaction-patterns-with-try-catch-output-parameters-and-error-handling.md", + "id": "stored-procedure-transaction-patterns-with-try-catch-output-parameters-and-error-handling", + "seoDescription": "Learn Stored procedure transaction patterns with `TRY...CATCH`, output parameters, and error handling for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Stored procedure transaction patterns with `TRY...CATCH`, output parameters, and error handling Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Stored procedure transaction patterns with `TRY...CATCH`, output parameters, and error handling", + "title": "Stored procedure transaction patterns with `TRY...CATCH`, output parameters, and error handling", + "topic": "SQL practical interview comparisons and SQL Server-specific features" + }, + { + "canonicalPath": "/content/stored-procedures-output-parameters-and-encapsulated-database-logic/", + "canonicalPathSource": "generated", + "category": "SQL", + "contentPath": "src/contents/v1/SQL/stored-procedures-output-parameters-and-encapsulated-database-logic.md", + "id": "stored-procedures-output-parameters-and-encapsulated-database-logic", + "seoDescription": "Learn Stored procedures, output parameters, and encapsulated database logic for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Stored procedures, output parameters, and encapsulated database logic Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Stored procedures, output parameters, and encapsulated database logic", + "title": "Stored procedures, output parameters, and encapsulated database logic", + "topic": "Database programmability and schema evolution" + }, + { + "canonicalPath": "/content/temporal-or-reporting-style-query-patterns/", + "canonicalPathSource": "generated", + "category": "SQL", + "contentPath": "src/contents/v1/SQL/temporal-or-reporting-style-query-patterns.md", + "id": "temporal-or-reporting-style-query-patterns", + "seoDescription": "Learn Temporal or reporting-style query patterns for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Temporal or reporting-style query patterns Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Temporal or reporting-style query patterns", + "title": "Temporal or reporting-style query patterns", + "topic": "Advanced querying with window functions and CTEs" + }, + { + "canonicalPath": "/content/temporal-tables-historical-retention-and-lifecycle-management/", + "canonicalPathSource": "generated", + "category": "SQL", + "contentPath": "src/contents/v1/SQL/temporal-tables-historical-retention-and-lifecycle-management.md", + "id": "temporal-tables-historical-retention-and-lifecycle-management", + "seoDescription": "Learn Temporal tables, historical retention, and lifecycle management for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Temporal tables, historical retention, and lifecycle management Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Temporal tables, historical retention, and lifecycle management", + "title": "Temporal tables, historical retention, and lifecycle management", + "topic": "Backup, recovery, HA/DR, security, and temporal data" + }, + { + "canonicalPath": "/content/triggers-and-their-trade-offs/", + "canonicalPathSource": "generated", + "category": "SQL", + "contentPath": "src/contents/v1/SQL/triggers-and-their-trade-offs.md", + "id": "triggers-and-their-trade-offs", + "seoDescription": "Learn Triggers and their trade-offs for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Triggers and their trade-offs Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Triggers and their trade-offs", + "title": "Triggers and their trade-offs", + "topic": "Database programmability and schema evolution" + }, + { + "canonicalPath": "/content/union-vs-union-all-except-intersect-and-duplicate-handling/", + "canonicalPathSource": "generated", + "category": "SQL", + "contentPath": "src/contents/v1/SQL/union-vs-union-all-except-intersect-and-duplicate-handling.md", + "id": "union-vs-union-all-except-intersect-and-duplicate-handling", + "seoDescription": "Learn UNION vs UNION ALL, EXCEPT, INTERSECT, and duplicate handling for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "UNION vs UNION ALL, EXCEPT, INTERSECT, and duplicate handling Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "UNION vs UNION ALL, EXCEPT, INTERSECT, and duplicate handling", + "title": "UNION vs UNION ALL, EXCEPT, INTERSECT, and duplicate handling", + "topic": "SQL practical interview comparisons and SQL Server-specific features" + }, + { + "canonicalPath": "/content/views-and-user-defined-functions/", + "canonicalPathSource": "generated", + "category": "SQL", + "contentPath": "src/contents/v1/SQL/views-and-user-defined-functions.md", + "id": "views-and-user-defined-functions", + "seoDescription": "Learn Views and user-defined functions for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Views and user-defined functions Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "Views and user-defined functions", + "title": "Views and user-defined functions", + "topic": "Database programmability and schema evolution" + }, + { + "canonicalPath": "/content/where-vs-having-and-filtering-before-vs-after-aggregation/", + "canonicalPathSource": "generated", + "category": "SQL", + "contentPath": "src/contents/v1/SQL/where-vs-having-and-filtering-before-vs-after-aggregation.md", + "id": "where-vs-having-and-filtering-before-vs-after-aggregation", + "seoDescription": "Learn WHERE vs HAVING and filtering before vs after aggregation for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "WHERE vs HAVING and filtering before vs after aggregation Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", + "subtopic": "WHERE vs HAVING and filtering before vs after aggregation", + "title": "WHERE vs HAVING and filtering before vs after aggregation", + "topic": "SQL practical interview comparisons and SQL Server-specific features" + } + ] +} diff --git a/src/contents/curriculumManifest.generated.ts b/src/contents/curriculumManifest.generated.ts index 785c079..9152ba4 100644 --- a/src/contents/curriculumManifest.generated.ts +++ b/src/contents/curriculumManifest.generated.ts @@ -1,6 +1,12 @@ // This file is generated by scripts/generate-curriculum-manifest.mjs. // Do not edit it by hand. +export type CurriculumManifestCanonicalPathSource = + | "frontmatter" + | "generated"; + +export type CurriculumManifestSeoTextSource = "fallback" | "frontmatter"; + export type CurriculumManifestFolder = { path: string; title: string; @@ -20,6 +26,8 @@ export type CurriculumManifestSampleQuestion = { }; export type CurriculumManifestEntry = { + canonicalPath: string; + canonicalPathSource: CurriculumManifestCanonicalPathSource; category: string; contentPath: string; folders: CurriculumManifestFolder[]; @@ -28,6 +36,10 @@ export type CurriculumManifestEntry = { questionCount: number; questionLevelCounts: CurriculumManifestQuestionLevelCount[]; sampleQuestions: CurriculumManifestSampleQuestion[]; + seoDescription: string; + seoDescriptionSource: CurriculumManifestSeoTextSource; + seoTitle: string; + seoTitleSource: CurriculumManifestSeoTextSource; subtopic: string; title: string; topic: string; @@ -36,6 +48,8 @@ export type CurriculumManifestEntry = { export const curriculumManifest: CurriculumManifestEntry[] = [ { + "canonicalPath": "/content/alert-rules-action-groups-and-incident-response-workflows/", + "canonicalPathSource": "generated", "category": "Azure", "contentPath": "/src/contents/v1/Azure/alert-rules-action-groups-and-incident-response-workflows.md", "folders": [ @@ -70,12 +84,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How do you design an actionable alert?" } ], + "seoDescription": "Learn Alert rules, action groups, and incident response workflows for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Alert rules, action groups, and incident response workflows Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Alert rules, action groups, and incident response workflows", "title": "Alert rules, action groups, and incident response workflows", "topic": "Monitoring, tracing, and incident response on Azure", "topicFolderPath": "Azure/monitoring-tracing-and-incident-response-on-azure" }, { + "canonicalPath": "/content/app-service-plans-paas-web-hosting-fit/", + "canonicalPathSource": "generated", "category": "Azure", "contentPath": "/src/contents/v1/Azure/app-service-plans-paas-web-hosting-fit.md", "folders": [ @@ -110,12 +130,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "When is Azure App Service the right hosting choice?" } ], + "seoDescription": "Learn App Service plans and PaaS web hosting fit for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "App Service plans and PaaS web hosting fit Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "App Service plans and PaaS web hosting fit", "title": "App Service plans and PaaS web hosting fit", "topic": "Azure compute choices and hosting models", "topicFolderPath": "Azure/azure-compute-choices-and-hosting-models" }, { + "canonicalPath": "/content/application-gateway-and-waf-concepts/", + "canonicalPathSource": "generated", "category": "Azure", "contentPath": "/src/contents/v1/Azure/application-gateway-and-waf-concepts.md", "folders": [ @@ -150,12 +176,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How would you configure TLS from the client through Application Gateway to the backend?" } ], + "seoDescription": "Learn Application Gateway and WAF concepts for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Application Gateway and WAF concepts Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Application Gateway and WAF concepts", "title": "Application Gateway and WAF concepts", "topic": "Networking, API edge, and secure connectivity", "topicFolderPath": "Azure/networking-api-edge-and-secure-connectivity" }, { + "canonicalPath": "/content/application-insights-and-opentelemetry-aligned-observability/", + "canonicalPathSource": "generated", "category": "Azure", "contentPath": "/src/contents/v1/Azure/application-insights-and-opentelemetry-aligned-observability.md", "folders": [ @@ -190,12 +222,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "What should be automatically instrumented versus manually instrumented?" } ], + "seoDescription": "Learn Application Insights and OpenTelemetry-aligned observability for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Application Insights and OpenTelemetry-aligned observability Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Application Insights and OpenTelemetry-aligned observability", "title": "Application Insights and OpenTelemetry-aligned observability", "topic": "Monitoring, tracing, and incident response on Azure", "topicFolderPath": "Azure/monitoring-tracing-and-incident-response-on-azure" }, { + "canonicalPath": "/content/azure-api-management-and-policy-based-gateways/", + "canonicalPathSource": "generated", "category": "Azure", "contentPath": "/src/contents/v1/Azure/azure-api-management-and-policy-based-gateways.md", "folders": [ @@ -230,12 +268,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How do APIM policy scopes and the base element work?" } ], + "seoDescription": "Learn Azure API Management and policy-based gateways for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Azure API Management and policy-based gateways Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Azure API Management and policy-based gateways", "title": "Azure API Management and policy-based gateways", "topic": "Networking, API edge, and secure connectivity", "topicFolderPath": "Azure/networking-api-edge-and-secure-connectivity" }, { + "canonicalPath": "/content/azure-cache-for-redis-low-latency-reads-and-session-state-scenarios/", + "canonicalPathSource": "generated", "category": "Azure", "contentPath": "/src/contents/v1/Azure/azure-cache-for-redis-low-latency-reads-and-session-state-scenarios.md", "folders": [ @@ -270,12 +314,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How would you prevent a cache stampede on a popular key?" } ], + "seoDescription": "Learn Azure Cache for Redis for low-latency reads and session/state scenarios for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Azure Cache for Redis for low-latency reads and session/state scenarios Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Azure Cache for Redis for low-latency reads and session/state scenarios", "title": "Azure Cache for Redis for low-latency reads and session/state scenarios", "topic": "Azure data, storage, and caching services", "topicFolderPath": "Azure/azure-data-storage-and-caching-services" }, { + "canonicalPath": "/content/azure-container-apps-for-containerized-apis-and-background-workloads/", + "canonicalPathSource": "generated", "category": "Azure", "contentPath": "/src/contents/v1/Azure/azure-container-apps-for-containerized-apis-and-background-workloads.md", "folders": [ @@ -310,12 +360,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How would you host an API and a queue worker in Azure Container Apps?" } ], + "seoDescription": "Learn Azure Container Apps for containerized APIs and background workloads for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Azure Container Apps for containerized APIs and background workloads Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Azure Container Apps for containerized APIs and background workloads", "title": "Azure Container Apps for containerized APIs and background workloads", "topic": "Azure compute choices and hosting models", "topicFolderPath": "Azure/azure-compute-choices-and-hosting-models" }, { + "canonicalPath": "/content/azure-functions-hosting-options-and-current-scale-guidance/", + "canonicalPathSource": "generated", "category": "Azure", "contentPath": "/src/contents/v1/Azure/azure-functions-hosting-options-and-current-scale-guidance.md", "folders": [ @@ -350,12 +406,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How does Azure Functions scale?" } ], + "seoDescription": "Learn Azure Functions Hosting Options and Current Scale Guidance for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Azure Functions Hosting Options and Current Scale Guidance Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Azure Functions Hosting Options and Current Scale Guidance", "title": "Azure Functions Hosting Options and Current Scale Guidance", "topic": "Azure compute choices and hosting models", "topicFolderPath": "Azure/azure-compute-choices-and-hosting-models" }, { + "canonicalPath": "/content/azure-key-vault-for-secrets-certificates-and-connection-settings/", + "canonicalPathSource": "generated", "category": "Azure", "contentPath": "/src/contents/v1/Azure/azure-key-vault-for-secrets-certificates-and-connection-settings.md", "folders": [ @@ -390,12 +452,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "What is the difference between Key Vault control-plane and data-plane access?" } ], + "seoDescription": "Learn Azure Key Vault for secrets, certificates, and connection settings for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Azure Key Vault for secrets, certificates, and connection settings Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Azure Key Vault for secrets, certificates, and connection settings", "title": "Azure Key Vault for secrets, certificates, and connection settings", "topic": "Identity, secrets, and access control", "topicFolderPath": "Azure/identity-secrets-and-access-control" }, { + "canonicalPath": "/content/azure-sql-database-tiers-scaling-serverless-options-and-failover-groups/", + "canonicalPathSource": "generated", "category": "Azure", "contentPath": "/src/contents/v1/Azure/azure-sql-database-tiers-scaling-serverless-options-and-failover-groups.md", "folders": [ @@ -430,12 +498,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "What happens to application connections when an Azure SQL database is scaled?" } ], + "seoDescription": "Learn Azure SQL Database tiers, scaling, serverless options, and failover groups for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Azure SQL Database tiers, scaling, serverless options, and failover groups Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Azure SQL Database tiers, scaling, serverless options, and failover groups", "title": "Azure SQL Database tiers, scaling, serverless options, and failover groups", "topic": "Azure data, storage, and caching services", "topicFolderPath": "Azure/azure-data-storage-and-caching-services" }, { + "canonicalPath": "/content/bicep-fundamentals-modules-and-reusable-environment-definitions/", + "canonicalPathSource": "generated", "category": "Azure", "contentPath": "/src/contents/v1/Azure/bicep-fundamentals-modules-and-reusable-environment-definitions.md", "folders": [ @@ -470,12 +544,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How would you structure Bicep for dev, test, and production environments?" } ], + "seoDescription": "Learn Bicep fundamentals, modules, and reusable environment definitions for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Bicep fundamentals, modules, and reusable environment definitions Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Bicep fundamentals, modules, and reusable environment definitions", "title": "Bicep fundamentals, modules, and reusable environment definitions", "topic": "Delivery, infrastructure as code, scaling, and cost control", "topicFolderPath": "Azure/delivery-infrastructure-as-code-scaling-and-cost-control" }, { + "canonicalPath": "/content/bindings-connection-configuration-dependency-injection-logging-and-local-development/", + "canonicalPathSource": "generated", "category": "Azure", "contentPath": "/src/contents/v1/Azure/bindings-connection-configuration-dependency-injection-logging-and-local-development.md", "folders": [ @@ -510,12 +590,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How does an identity-based binding connection work?" } ], + "seoDescription": "Learn Bindings, connection configuration, dependency injection, logging, and local development for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Bindings, connection configuration, dependency injection, logging, and local development Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Bindings, connection configuration, dependency injection, logging, and local development", "title": "Bindings, connection configuration, dependency injection, logging, and local development", "topic": "Azure Functions and Durable Functions", "topicFolderPath": "Azure/azure-functions-and-durable-functions" }, { + "canonicalPath": "/content/blob-storage-access-tiers-lifecycle-management-and-immutability/", + "canonicalPathSource": "generated", "category": "Azure", "contentPath": "/src/contents/v1/Azure/blob-storage-access-tiers-lifecycle-management-and-immutability.md", "folders": [ @@ -550,12 +636,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How would you select an access tier using total cost rather than storage price alone?" } ], + "seoDescription": "Learn Blob Storage, access tiers, lifecycle management, and immutability for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Blob Storage, access tiers, lifecycle management, and immutability Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Blob Storage, access tiers, lifecycle management, and immutability", "title": "Blob Storage, access tiers, lifecycle management, and immutability", "topic": "Azure data, storage, and caching services", "topicFolderPath": "Azure/azure-data-storage-and-caching-services" }, { + "canonicalPath": "/content/block-blobs-append-blobs-and-page-blobs/", + "canonicalPathSource": "generated", "category": "Azure", "contentPath": "/src/contents/v1/Azure/block-blobs-append-blobs-and-page-blobs.md", "folders": [ @@ -590,12 +682,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How would you make a large block blob upload resumable?" } ], + "seoDescription": "Learn Block blobs, append blobs, and page blobs for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Block blobs, append blobs, and page blobs Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Block blobs, append blobs, and page blobs", "title": "Block blobs, append blobs, and page blobs", "topic": "Azure Blob Storage and file handling", "topicFolderPath": "Azure/azure-blob-storage-and-file-handling" }, { + "canonicalPath": "/content/choosing-queues-vs-topics-vs-event-notifications/", + "canonicalPathSource": "generated", "category": "Azure", "contentPath": "/src/contents/v1/Azure/choosing-queues-vs-topics-vs-event-notifications.md", "folders": [ @@ -630,12 +728,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "Why is one queue wrong for multiple independent subscribers?" } ], + "seoDescription": "Learn Choosing queues vs topics vs event notifications for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Choosing queues vs topics vs event notifications Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Choosing queues vs topics vs event notifications", "title": "Choosing queues vs topics vs event notifications", "topic": "Messaging and event-driven Azure integration", "topicFolderPath": "Azure/messaging-and-event-driven-azure-integration" }, { + "canonicalPath": "/content/cold-starts-hosting-plan-choices-timeout-configuration-and-scaling-behavior/", + "canonicalPathSource": "generated", "category": "Azure", "contentPath": "/src/contents/v1/Azure/cold-starts-hosting-plan-choices-timeout-configuration-and-scaling-behavior.md", "folders": [ @@ -670,12 +774,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How would you reduce cold-start latency for a .NET isolated Function app?" } ], + "seoDescription": "Learn Cold starts, hosting plan choices, timeout configuration, and scaling behavior for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Cold starts, hosting plan choices, timeout configuration, and scaling behavior Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Cold starts, hosting plan choices, timeout configuration, and scaling behavior", "title": "Cold starts, hosting plan choices, timeout configuration, and scaling behavior", "topic": "Azure Functions and Durable Functions", "topicFolderPath": "Azure/azure-functions-and-durable-functions" }, { + "canonicalPath": "/content/deployment-slots-rollout-safety-autoscaling-availability-zones-and-cost-aware-tiering/", + "canonicalPathSource": "generated", "category": "Azure", "contentPath": "/src/contents/v1/Azure/deployment-slots-rollout-safety-autoscaling-availability-zones-and-cost-aware-tiering.md", "folders": [ @@ -710,12 +820,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "What settings should be slot-specific?" } ], + "seoDescription": "Learn Deployment slots, rollout safety, autoscaling, availability zones, and cost-aware tiering for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Deployment slots, rollout safety, autoscaling, availability zones, and cost-aware tiering Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Deployment slots, rollout safety, autoscaling, availability zones, and cost-aware tiering", "title": "Deployment slots, rollout safety, autoscaling, availability zones, and cost-aware tiering", "topic": "Delivery, infrastructure as code, scaling, and cost control", "topicFolderPath": "Azure/delivery-infrastructure-as-code-scaling-and-cost-control" }, { + "canonicalPath": "/content/dotnet-blob-clients-direct-browser-upload-and-separating-metadata-from-binary-content/", + "canonicalPathSource": "generated", "category": "Azure", "contentPath": "/src/contents/v1/Azure/dotnet-blob-clients-direct-browser-upload-and-separating-metadata-from-binary-content.md", "folders": [ @@ -750,12 +866,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How should Azure Blob SDK clients be registered in dependency injection?" } ], + "seoDescription": "Learn .NET Blob clients, direct browser upload patterns, and storing metadata separately from binary content for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": ".NET Blob clients, direct browser upload patterns, and storing metadata separately from binary content Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": ".NET Blob clients, direct browser upload patterns, and storing metadata separately from binary content", "title": ".NET Blob clients, direct browser upload patterns, and storing metadata separately from binary content", "topic": "Azure Blob Storage and file handling", "topicFolderPath": "Azure/azure-blob-storage-and-file-handling" }, { + "canonicalPath": "/content/duplicate-detection-retries-and-poison-message-handling/", + "canonicalPathSource": "generated", "category": "Azure", "contentPath": "/src/contents/v1/Azure/duplicate-detection-retries-and-poison-message-handling.md", "folders": [ @@ -790,12 +912,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How do you choose a duplicate detection window?" } ], + "seoDescription": "Learn Duplicate detection, retries, and poison-message handling for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Duplicate detection, retries, and poison-message handling Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Duplicate detection, retries, and poison-message handling", "title": "Duplicate detection, retries, and poison-message handling", "topic": "Messaging and event-driven Azure integration", "topicFolderPath": "Azure/messaging-and-event-driven-azure-integration" }, { + "canonicalPath": "/content/durable-functions-orchestrators-activities-timers-external-events-and-entities/", + "canonicalPathSource": "generated", "category": "Azure", "contentPath": "/src/contents/v1/Azure/durable-functions-orchestrators-activities-timers-external-events-and-entities.md", "folders": [ @@ -830,12 +958,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "Why must orchestrator code be deterministic?" } ], + "seoDescription": "Learn Durable Functions orchestrators, activity functions, durable timers, external events, and durable entities for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Durable Functions orchestrators, activity functions, durable timers, external events, and durable entities Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Durable Functions orchestrators, activity functions, durable timers, external events, and durable entities", "title": "Durable Functions orchestrators, activity functions, durable timers, external events, and durable entities", "topic": "Azure Functions and Durable Functions", "topicFolderPath": "Azure/azure-functions-and-durable-functions" }, { + "canonicalPath": "/content/event-grid-for-event-publication-and-fan-out/", + "canonicalPathSource": "generated", "category": "Azure", "contentPath": "/src/contents/v1/Azure/event-grid-for-event-publication-and-fan-out.md", "folders": [ @@ -870,12 +1004,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How should you design Event Grid event payloads?" } ], + "seoDescription": "Learn Event Grid for event publication and fan-out for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Event Grid for event publication and fan-out Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Event Grid for event publication and fan-out", "title": "Event Grid for event publication and fan-out", "topic": "Messaging and event-driven Azure integration", "topicFolderPath": "Azure/messaging-and-event-driven-azure-integration" }, { + "canonicalPath": "/content/function-chaining-fan-out-fan-in-async-workflow-state-replay-and-compensation/", + "canonicalPathSource": "generated", "category": "Azure", "contentPath": "/src/contents/v1/Azure/function-chaining-fan-out-fan-in-async-workflow-state-replay-and-compensation.md", "folders": [ @@ -910,12 +1050,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How would you limit a fan-out that processes thousands of items?" } ], + "seoDescription": "Learn Function chaining, fan-out/fan-in, async workflow state, replay-safe orchestrator code, and compensation for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Function chaining, fan-out/fan-in, async workflow state, replay-safe orchestrator code, and compensation Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Function chaining, fan-out/fan-in, async workflow state, replay-safe orchestrator code, and compensation", "title": "Function chaining, fan-out/fan-in, async workflow state, replay-safe orchestrator code, and compensation", "topic": "Azure Functions and Durable Functions", "topicFolderPath": "Azure/azure-functions-and-durable-functions" }, { + "canonicalPath": "/content/github-actions-or-azure-devops-for-build-test-deploy-and-infrastructure-steps/", + "canonicalPathSource": "generated", "category": "Azure", "contentPath": "/src/contents/v1/Azure/github-actions-or-azure-devops-for-build-test-deploy-and-infrastructure-steps.md", "folders": [ @@ -950,12 +1096,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How should infrastructure deployment fit into a CI/CD pipeline?" } ], + "seoDescription": "Learn GitHub Actions or Azure DevOps for build, test, deploy, and infrastructure steps for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "GitHub Actions or Azure DevOps for build, test, deploy, and infrastructure steps Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "GitHub Actions or Azure DevOps for build, test, deploy, and infrastructure steps", "title": "GitHub Actions or Azure DevOps for build, test, deploy, and infrastructure steps", "topic": "Delivery, infrastructure as code, scaling, and cost control", "topicFolderPath": "Azure/delivery-infrastructure-as-code-scaling-and-cost-control" }, { + "canonicalPath": "/content/http-timer-queue-service-bus-event-grid-and-blob-triggers/", + "canonicalPathSource": "generated", "category": "Azure", "contentPath": "/src/contents/v1/Azure/http-timer-queue-service-bus-event-grid-and-blob-triggers.md", "folders": [ @@ -990,12 +1142,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How should a queue-triggered function handle duplicate messages?" } ], + "seoDescription": "Learn HTTP, timer, queue, Service Bus, Event Grid, and Blob triggers for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "HTTP, timer, queue, Service Bus, Event Grid, and Blob triggers Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "HTTP, timer, queue, Service Bus, Event Grid, and Blob triggers", "title": "HTTP, timer, queue, Service Bus, Event Grid, and Blob triggers", "topic": "Azure Functions and Durable Functions", "topicFolderPath": "Azure/azure-functions-and-durable-functions" }, { + "canonicalPath": "/content/in-process-vs-isolated-worker-model-for-dotnet-functions/", + "canonicalPathSource": "generated", "category": "Azure", "contentPath": "/src/contents/v1/Azure/in-process-vs-isolated-worker-model-for-dotnet-functions.md", "folders": [ @@ -1030,12 +1188,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "What package and attribute changes are required during migration?" } ], + "seoDescription": "Learn In-process vs isolated worker model for .NET Functions for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "In-process vs isolated worker model for .NET Functions Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "In-process vs isolated worker model for .NET Functions", "title": "In-process vs isolated worker model for .NET Functions", "topic": "Azure Functions and Durable Functions", "topicFolderPath": "Azure/azure-functions-and-durable-functions" }, { + "canonicalPath": "/content/large-file-uploads-block-upload-retries-checksums-and-resumable-patterns/", + "canonicalPathSource": "generated", "category": "Azure", "contentPath": "/src/contents/v1/Azure/large-file-uploads-block-upload-retries-checksums-and-resumable-patterns.md", "folders": [ @@ -1070,12 +1234,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How do you select block size and concurrency?" } ], + "seoDescription": "Learn Large file uploads, block upload, retry behavior, checksums, and resumable upload patterns for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Large file uploads, block upload, retry behavior, checksums, and resumable upload patterns Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Large file uploads, block upload, retry behavior, checksums, and resumable upload patterns", "title": "Large file uploads, block upload, retry behavior, checksums, and resumable upload patterns", "topic": "Azure Blob Storage and file handling", "topicFolderPath": "Azure/azure-blob-storage-and-file-handling" }, { + "canonicalPath": "/content/lifecycle-management-soft-delete-versioning-immutability-encryption-and-retention/", + "canonicalPathSource": "generated", "category": "Azure", "contentPath": "/src/contents/v1/Azure/lifecycle-management-soft-delete-versioning-immutability-encryption-and-retention.md", "folders": [ @@ -1110,12 +1280,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How would you control the cost of Blob Storage versioning and snapshots?" } ], + "seoDescription": "Learn Lifecycle management, soft delete, versioning, immutability, encryption, and retention for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Lifecycle management, soft delete, versioning, immutability, encryption, and retention Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Lifecycle management, soft delete, versioning, immutability, encryption, and retention", "title": "Lifecycle management, soft delete, versioning, immutability, encryption, and retention", "topic": "Azure Blob Storage and file handling", "topicFolderPath": "Azure/azure-blob-storage-and-file-handling" }, { + "canonicalPath": "/content/log-analytics-queries-dashboards-and-availability-tests/", + "canonicalPathSource": "generated", "category": "Azure", "contentPath": "/src/contents/v1/Azure/log-analytics-queries-dashboards-and-availability-tests.md", "folders": [ @@ -1150,12 +1326,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How would you write a KQL query to find failing API routes?" } ], + "seoDescription": "Learn Log Analytics queries, dashboards, and availability tests for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Log Analytics queries, dashboards, and availability tests Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Log Analytics queries, dashboards, and availability tests", "title": "Log Analytics queries, dashboards, and availability tests", "topic": "Monitoring, tracing, and incident response on Azure", "topicFolderPath": "Azure/monitoring-tracing-and-incident-response-on-azure" }, { + "canonicalPath": "/content/matching-storage-choices-to-relational-object-and-cache-workloads/", + "canonicalPathSource": "generated", "category": "Azure", "contentPath": "/src/contents/v1/Azure/matching-storage-choices-to-relational-object-and-cache-workloads.md", "folders": [ @@ -1190,12 +1372,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How would you store uploaded documents and their metadata?" } ], + "seoDescription": "Learn Matching storage choices to relational, object, and cache workloads for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Matching storage choices to relational, object, and cache workloads Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Matching storage choices to relational, object, and cache workloads", "title": "Matching storage choices to relational, object, and cache workloads", "topic": "Azure data, storage, and caching services", "topicFolderPath": "Azure/azure-data-storage-and-caching-services" }, { + "canonicalPath": "/content/metrics-vs-logs-vs-traces/", + "canonicalPathSource": "generated", "category": "Azure", "contentPath": "/src/contents/v1/Azure/metrics-vs-logs-vs-traces.md", "folders": [ @@ -1230,12 +1418,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How do metric dimensions differ from log fields?" } ], + "seoDescription": "Learn Metrics vs logs vs traces for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Metrics vs logs vs traces Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Metrics vs logs vs traces", "title": "Metrics vs logs vs traces", "topic": "Monitoring, tracing, and incident response on Azure", "topicFolderPath": "Azure/monitoring-tracing-and-incident-response-on-azure" }, { + "canonicalPath": "/content/microsoft-entra-app-registrations-scopes-and-auth-code-flow-basics/", + "canonicalPathSource": "generated", "category": "Azure", "contentPath": "/src/contents/v1/Azure/microsoft-entra-app-registrations-scopes-and-auth-code-flow-basics.md", "folders": [ @@ -1270,12 +1464,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How do PKCE, state, and nonce differ?" } ], + "seoDescription": "Learn Microsoft Entra app registrations, scopes, and auth-code flow basics for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Microsoft Entra app registrations, scopes, and auth-code flow basics Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Microsoft Entra app registrations, scopes, and auth-code flow basics", "title": "Microsoft Entra app registrations, scopes, and auth-code flow basics", "topic": "Identity, secrets, and access control", "topicFolderPath": "Azure/identity-secrets-and-access-control" }, { + "canonicalPath": "/content/oidc-based-azure-authentication-in-pipelines/", + "canonicalPathSource": "generated", "category": "Azure", "contentPath": "/src/contents/v1/Azure/oidc-based-azure-authentication-in-pipelines.md", "folders": [ @@ -1310,12 +1510,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How does the OIDC token exchange flow work?" } ], + "seoDescription": "Learn OIDC-based Azure authentication in pipelines for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "OIDC-based Azure authentication in pipelines Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "OIDC-based Azure authentication in pipelines", "title": "OIDC-based Azure authentication in pipelines", "topic": "Delivery, infrastructure as code, scaling, and cost control", "topicFolderPath": "Azure/delivery-infrastructure-as-code-scaling-and-cost-control" }, { + "canonicalPath": "/content/rate-limiting-quotas-auth-offload-and-request-transformation-at-the-edge/", + "canonicalPathSource": "generated", "category": "Azure", "contentPath": "/src/contents/v1/Azure/rate-limiting-quotas-auth-offload-and-request-transformation-at-the-edge.md", "folders": [ @@ -1350,12 +1556,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How should you choose a rate-limit counter key?" } ], + "seoDescription": "Learn Rate limiting, quotas, auth offload, and request transformation at the edge for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Rate limiting, quotas, auth offload, and request transformation at the edge Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Rate limiting, quotas, auth offload, and request transformation at the edge", "title": "Rate limiting, quotas, auth offload, and request transformation at the edge", "topic": "Networking, API edge, and secure connectivity", "topicFolderPath": "Azure/networking-api-edge-and-secure-connectivity" }, { + "canonicalPath": "/content/rbac-least-privilege-and-separating-human-vs-workload-identities/", + "canonicalPathSource": "generated", "category": "Azure", "contentPath": "/src/contents/v1/Azure/rbac-least-privilege-and-separating-human-vs-workload-identities.md", "folders": [ @@ -1390,12 +1602,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "Why should privileged human roles use PIM?" } ], + "seoDescription": "Learn RBAC, least privilege, and separating human vs workload identities for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "RBAC, least privilege, and separating human vs workload identities Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "RBAC, least privilege, and separating human vs workload identities", "title": "RBAC, least privilege, and separating human vs workload identities", "topic": "Identity, secrets, and access control", "topicFolderPath": "Azure/identity-secrets-and-access-control" }, { + "canonicalPath": "/content/service-bus-queues-topics-subscriptions-and-dead-letter-queues/", + "canonicalPathSource": "generated", "category": "Azure", "contentPath": "/src/contents/v1/Azure/service-bus-queues-topics-subscriptions-and-dead-letter-queues.md", "folders": [ @@ -1430,12 +1648,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How does peek-lock processing work?" } ], + "seoDescription": "Learn Service Bus queues, topics, subscriptions, and dead-letter queues for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Service Bus queues, topics, subscriptions, and dead-letter queues Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Service Bus queues, topics, subscriptions, and dead-letter queues", "title": "Service Bus queues, topics, subscriptions, and dead-letter queues", "topic": "Messaging and event-driven Azure integration", "topicFolderPath": "Azure/messaging-and-event-driven-azure-integration" }, { + "canonicalPath": "/content/service-tags-private-networking-and-reducing-public-attack-surface/", + "canonicalPathSource": "generated", "category": "Azure", "contentPath": "/src/contents/v1/Azure/service-tags-private-networking-and-reducing-public-attack-surface.md", "folders": [ @@ -1470,12 +1694,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "Compare private endpoints and virtual network service endpoints." } ], + "seoDescription": "Learn Service tags, private networking, and reducing public attack surface for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Service tags, private networking, and reducing public attack surface Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Service tags, private networking, and reducing public attack surface", "title": "Service tags, private networking, and reducing public attack surface", "topic": "Networking, API edge, and secure connectivity", "topicFolderPath": "Azure/networking-api-edge-and-secure-connectivity" }, { + "canonicalPath": "/content/shared-access-signatures-managed-identity-rbac-stored-access-policies-and-public-access-risks/", + "canonicalPathSource": "generated", "category": "Azure", "contentPath": "/src/contents/v1/Azure/shared-access-signatures-managed-identity-rbac-stored-access-policies-and-public-access-risks.md", "folders": [ @@ -1510,12 +1740,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "Compare user delegation, service, and account SAS." } ], + "seoDescription": "Learn Shared access signatures, managed identity, RBAC, stored access policies, and public access risks for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Shared access signatures, managed identity, RBAC, stored access policies, and public access risks Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Shared access signatures, managed identity, RBAC, stored access policies, and public access risks", "title": "Shared access signatures, managed identity, RBAC, stored access policies, and public access risks", "topic": "Azure Blob Storage and file handling", "topicFolderPath": "Azure/azure-blob-storage-and-file-handling" }, { + "canonicalPath": "/content/storage-accounts-containers-blobs-virtual-folders-metadata-and-access-tiers/", + "canonicalPathSource": "generated", "category": "Azure", "contentPath": "/src/contents/v1/Azure/storage-accounts-containers-blobs-virtual-folders-metadata-and-access-tiers.md", "folders": [ @@ -1550,12 +1786,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How would you design blob names for a multi-tenant document system?" } ], + "seoDescription": "Learn Storage accounts, containers, blobs, virtual folders, metadata, and access tiers for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Storage accounts, containers, blobs, virtual folders, metadata, and access tiers Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Storage accounts, containers, blobs, virtual folders, metadata, and access tiers", "title": "Storage accounts, containers, blobs, virtual folders, metadata, and access tiers", "topic": "Azure Blob Storage and file handling", "topicFolderPath": "Azure/azure-blob-storage-and-file-handling" }, { + "canonicalPath": "/content/system-assigned-and-user-assigned-managed-identities/", + "canonicalPathSource": "generated", "category": "Azure", "contentPath": "/src/contents/v1/Azure/system-assigned-and-user-assigned-managed-identities.md", "folders": [ @@ -1590,12 +1832,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "When would you choose a user-assigned identity over a system-assigned identity?" } ], + "seoDescription": "Learn System-assigned and user-assigned managed identities for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "System-assigned and user-assigned managed identities Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "System-assigned and user-assigned managed identities", "title": "System-assigned and user-assigned managed identities", "topic": "Identity, secrets, and access control", "topicFolderPath": "Azure/identity-secrets-and-access-control" }, { + "canonicalPath": "/content/trade-offs-among-simplicity-portability-autoscaling-and-operational-control/", + "canonicalPathSource": "generated", "category": "Azure", "contentPath": "/src/contents/v1/Azure/trade-offs-among-simplicity-portability-autoscaling-and-operational-control.md", "folders": [ @@ -1630,12 +1878,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How would you choose between Azure Container Apps and AKS?" } ], + "seoDescription": "Learn Trade-offs among simplicity, portability, autoscaling, and operational control for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Trade-offs among simplicity, portability, autoscaling, and operational control Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Trade-offs among simplicity, portability, autoscaling, and operational control", "title": "Trade-offs among simplicity, portability, autoscaling, and operational control", "topic": "Azure compute choices and hosting models", "topicFolderPath": "Azure/azure-compute-choices-and-hosting-models" }, { + "canonicalPath": "/content/adrs-coding-conventions-and-team-level-design-communication/", + "canonicalPathSource": "generated", "category": "Design & Architecture", "contentPath": "/src/contents/v1/Design & Architecture/adrs-coding-conventions-and-team-level-design-communication.md", "folders": [ @@ -1670,12 +1924,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "What should happen when an accepted ADR is no longer appropriate?" } ], + "seoDescription": "Learn ADRs, coding conventions, and team-level design communication for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "ADRs, coding conventions, and team-level design communication Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "ADRs, coding conventions, and team-level design communication", "title": "ADRs, coding conventions, and team-level design communication", "topic": "Testing strategy, maintainability, and technical leadership", "topicFolderPath": "Design & Architecture/testing-strategy-maintainability-and-technical-leadership" }, { + "canonicalPath": "/content/api-gateway-and-bff-decisions-for-web-clients-and-microservices/", + "canonicalPathSource": "generated", "category": "Design & Architecture", "contentPath": "/src/contents/v1/Design & Architecture/api-gateway-and-bff-decisions-for-web-clients-and-microservices.md", "folders": [ @@ -1710,12 +1970,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "Which responsibilities should remain out of an API gateway?" } ], + "seoDescription": "Learn API gateway and BFF decisions for web clients and microservices for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "API gateway and BFF decisions for web clients and microservices Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "API gateway and BFF decisions for web clients and microservices", "title": "API gateway and BFF decisions for web clients and microservices", "topic": "API design and integration contracts", "topicFolderPath": "Design & Architecture/api-design-and-integration-contracts" }, { + "canonicalPath": "/content/assumptions-constraints-risks-failure-modes/", + "canonicalPathSource": "generated", "category": "Design & Architecture", "contentPath": "/src/contents/v1/Design & Architecture/assumptions-constraints-risks-failure-modes.md", "folders": [ @@ -1750,12 +2016,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How would you identify risks in a proposed architecture?" } ], + "seoDescription": "Learn Assumptions, constraints, risks, and failure modes for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Assumptions, constraints, risks, and failure modes Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Assumptions, constraints, risks, and failure modes", "title": "Assumptions, constraints, risks, and failure modes", "topic": "Requirements decomposition and system trade-offs", "topicFolderPath": "Design & Architecture/requirements-decomposition-and-system-trade-offs" }, { + "canonicalPath": "/content/cache-aside-read-caching-and-invalidation-trade-offs/", + "canonicalPathSource": "generated", "category": "Design & Architecture", "contentPath": "/src/contents/v1/Design & Architecture/cache-aside-read-caching-and-invalidation-trade-offs.md", "folders": [ @@ -1790,12 +2062,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How would you choose a cache TTL?" } ], + "seoDescription": "Learn Cache-aside, read caching, and invalidation trade-offs for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Cache-aside, read caching, and invalidation trade-offs Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Cache-aside, read caching, and invalidation trade-offs", "title": "Cache-aside, read caching, and invalidation trade-offs", "topic": "Scalability, resilience, caching, and observability design", "topicFolderPath": "Design & Architecture/scalability-resilience-caching-and-observability-design" }, { + "canonicalPath": "/content/capacity-planning-and-identifying-likely-bottlenecks/", + "canonicalPathSource": "generated", "category": "Design & Architecture", "contentPath": "/src/contents/v1/Design & Architecture/capacity-planning-and-identifying-likely-bottlenecks.md", "folders": [ @@ -1830,12 +2108,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How would you estimate how many API instances are needed?" } ], + "seoDescription": "Learn Capacity Planning and Identifying Likely Bottlenecks for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Capacity Planning and Identifying Likely Bottlenecks Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Capacity Planning and Identifying Likely Bottlenecks", "title": "Capacity Planning and Identifying Likely Bottlenecks", "topic": "Requirements decomposition and system trade-offs", "topicFolderPath": "Design & Architecture/requirements-decomposition-and-system-trade-offs" }, { + "canonicalPath": "/content/command-injection-file-upload-risks-path-traversal-secrets-exposure-and-dos-ddos-basics/", + "canonicalPathSource": "generated", "category": "Design & Architecture", "contentPath": "/src/contents/v1/Design & Architecture/command-injection-file-upload-risks-path-traversal-secrets-exposure-and-dos-ddos-basics.md", "folders": [ @@ -1870,12 +2154,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How would you design a secure file-upload service?" } ], + "seoDescription": "Learn Command injection, file upload risks, path traversal, secrets exposure, and DoS/DDoS basics for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Command injection, file upload risks, path traversal, secrets exposure, and DoS/DDoS basics Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Command injection, file upload risks, path traversal, secrets exposure, and DoS/DDoS basics", "title": "Command injection, file upload risks, path traversal, secrets exposure, and DoS/DDoS basics", "topic": "Web application security threat modeling and attack patterns", "topicFolderPath": "Design & Architecture/web-application-security-threat-modeling-and-attack-patterns" }, { + "canonicalPath": "/content/correlation-ids-traces-health-checks-and-alertable-telemetry/", + "canonicalPathSource": "generated", "category": "Design & Architecture", "contentPath": "/src/contents/v1/Design & Architecture/correlation-ids-traces-health-checks-and-alertable-telemetry.md", "folders": [ @@ -1910,12 +2200,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How would you correlate logs, metrics, and traces?" } ], + "seoDescription": "Learn Correlation IDs, traces, health checks, and alertable telemetry for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Correlation IDs, traces, health checks, and alertable telemetry Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Correlation IDs, traces, health checks, and alertable telemetry", "title": "Correlation IDs, traces, health checks, and alertable telemetry", "topic": "Scalability, resilience, caching, and observability design", "topicFolderPath": "Design & Architecture/scalability-resilience-caching-and-observability-design" }, { + "canonicalPath": "/content/cqrs-and-when-separate-read-and-write-models-make-sense/", + "canonicalPathSource": "generated", "category": "Design & Architecture", "contentPath": "/src/contents/v1/Design & Architecture/cqrs-and-when-separate-read-and-write-models-make-sense.md", "folders": [ @@ -1950,12 +2246,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "What changes when CQRS uses separate read and write databases?" } ], + "seoDescription": "Learn CQRS and when separate read and write models make sense for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "CQRS and when separate read and write models make sense Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "CQRS and when separate read and write models make sense", "title": "CQRS and when separate read and write models make sense", "topic": "Distributed systems patterns", "topicFolderPath": "Design & Architecture/distributed-systems-patterns" }, { + "canonicalPath": "/content/cross-site-request-forgery-and-cookie-based-authentication/", + "canonicalPathSource": "generated", "category": "Design & Architecture", "contentPath": "/src/contents/v1/Design & Architecture/cross-site-request-forgery-and-cookie-based-authentication.md", "folders": [ @@ -1990,12 +2292,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "What are the limitations of SameSite cookies as a CSRF defense?" } ], + "seoDescription": "Learn Cross-Site Request Forgery and how cookie-based authentication changes the risk model for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Cross-Site Request Forgery and how cookie-based authentication changes the risk model Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Cross-Site Request Forgery and how cookie-based authentication changes the risk model", "title": "Cross-Site Request Forgery and how cookie-based authentication changes the risk model", "topic": "Web application security threat modeling and attack patterns", "topicFolderPath": "Design & Architecture/web-application-security-threat-modeling-and-attack-patterns" }, { + "canonicalPath": "/content/cross-site-scripting-output-encoding-dangerous-html-rendering-and-content-security/", + "canonicalPathSource": "generated", "category": "Design & Architecture", "contentPath": "/src/contents/v1/Design & Architecture/cross-site-scripting-output-encoding-dangerous-html-rendering-and-content-security.md", "folders": [ @@ -2030,12 +2338,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "Why must output encoding match the browser context?" } ], + "seoDescription": "Learn Cross-Site Scripting, output encoding, dangerous HTML rendering, and content security controls for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Cross-Site Scripting, output encoding, dangerous HTML rendering, and content security controls Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Cross-Site Scripting, output encoding, dangerous HTML rendering, and content security controls", "title": "Cross-Site Scripting, output encoding, dangerous HTML rendering, and content security controls", "topic": "Web application security threat modeling and attack patterns", "topicFolderPath": "Design & Architecture/web-application-security-threat-modeling-and-attack-patterns" }, { + "canonicalPath": "/content/dependency-inversion-and-inward-facing-dependencies/", + "canonicalPathSource": "generated", "category": "Design & Architecture", "contentPath": "/src/contents/v1/Design & Architecture/dependency-inversion-and-inward-facing-dependencies.md", "folders": [ @@ -2070,12 +2384,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "Who should own an interface used to invert a dependency?" } ], + "seoDescription": "Learn Dependency inversion and inward-facing dependencies for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Dependency inversion and inward-facing dependencies Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Dependency inversion and inward-facing dependencies", "title": "Dependency inversion and inward-facing dependencies", "topic": "Clean Architecture and modular boundaries", "topicFolderPath": "Design & Architecture/clean-architecture-and-modular-boundaries" }, { + "canonicalPath": "/content/domain-services-and-domain-events/", + "canonicalPathSource": "generated", "category": "Design & Architecture", "contentPath": "/src/contents/v1/Design & Architecture/domain-services-and-domain-events.md", "folders": [ @@ -2110,12 +2430,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "When should behavior remain on an entity instead of moving to a domain service?" } ], + "seoDescription": "Learn Domain services and domain events for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Domain services and domain events Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Domain services and domain events", "title": "Domain services and domain events", "topic": "Domain modeling and Domain-Driven Design", "topicFolderPath": "Design & Architecture/domain-modeling-and-domain-driven-design" }, { + "canonicalPath": "/content/entities-value-objects-aggregates-and-invariants/", + "canonicalPathSource": "generated", "category": "Design & Architecture", "contentPath": "/src/contents/v1/Design & Architecture/entities-value-objects-aggregates-and-invariants.md", "folders": [ @@ -2150,12 +2476,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How do you choose an aggregate boundary?" } ], + "seoDescription": "Learn Entities, value objects, aggregates, and invariants for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Entities, value objects, aggregates, and invariants Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Entities, value objects, aggregates, and invariants", "title": "Entities, value objects, aggregates, and invariants", "topic": "Domain modeling and Domain-Driven Design", "topicFolderPath": "Design & Architecture/domain-modeling-and-domain-driven-design" }, { + "canonicalPath": "/content/event-driven-communication-and-asynchronous-request-reply/", + "canonicalPathSource": "generated", "category": "Design & Architecture", "contentPath": "/src/contents/v1/Design & Architecture/event-driven-communication-and-asynchronous-request-reply.md", "folders": [ @@ -2190,12 +2522,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How should an HTTP API represent a long-running operation?" } ], + "seoDescription": "Learn Event-driven communication and asynchronous request-reply for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Event-driven communication and asynchronous request-reply Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Event-driven communication and asynchronous request-reply", "title": "Event-driven communication and asynchronous request-reply", "topic": "Distributed systems patterns", "topicFolderPath": "Design & Architecture/distributed-systems-patterns" }, { + "canonicalPath": "/content/factory-builder-strategy-adapter-decorator-facade-proxy-and-chain-of-responsibility/", + "canonicalPathSource": "generated", "category": "Design & Architecture", "contentPath": "/src/contents/v1/Design & Architecture/factory-builder-strategy-adapter-decorator-facade-proxy-and-chain-of-responsibility.md", "folders": [ @@ -2230,12 +2568,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How do Factory and Strategy work together?" } ], + "seoDescription": "Learn Factory, Builder, Strategy, Adapter, Decorator, Facade, Proxy, and Chain of Responsibility for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Factory, Builder, Strategy, Adapter, Decorator, Facade, Proxy, and Chain of Responsibility Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Factory, Builder, Strategy, Adapter, Decorator, Facade, Proxy, and Chain of Responsibility", "title": "Factory, Builder, Strategy, Adapter, Decorator, Facade, Proxy, and Chain of Responsibility", "topic": "Software design principles and common .NET patterns", "topicFolderPath": "Design & Architecture/software-design-principles-and-common-net-patterns" }, { + "canonicalPath": "/content/functional-requirements-vs-nonfunctional-requirements/", + "canonicalPathSource": "generated", "category": "Design & Architecture", "contentPath": "/src/contents/v1/Design & Architecture/functional-requirements-vs-nonfunctional-requirements.md", "folders": [ @@ -2270,12 +2614,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How do nonfunctional requirements influence architecture?" } ], + "seoDescription": "Learn Functional Requirements vs Nonfunctional Requirements for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Functional Requirements vs Nonfunctional Requirements Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Functional Requirements vs Nonfunctional Requirements", "title": "Functional Requirements vs Nonfunctional Requirements", "topic": "Requirements decomposition and system trade-offs", "topicFolderPath": "Design & Architecture/requirements-decomposition-and-system-trade-offs" }, { + "canonicalPath": "/content/horizontal-scaling-stateless-services-and-backpressure/", + "canonicalPathSource": "generated", "category": "Design & Architecture", "contentPath": "/src/contents/v1/Design & Architecture/horizontal-scaling-stateless-services-and-backpressure.md", "folders": [ @@ -2310,12 +2660,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How would you choose autoscaling signals?" } ], + "seoDescription": "Learn Horizontal scaling, stateless services, and backpressure for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Horizontal scaling, stateless services, and backpressure Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Horizontal scaling, stateless services, and backpressure", "title": "Horizontal scaling, stateless services, and backpressure", "topic": "Scalability, resilience, caching, and observability design", "topicFolderPath": "Design & Architecture/scalability-resilience-caching-and-observability-design" }, { + "canonicalPath": "/content/idempotent-consumers-and-duplicate-message-handling/", + "canonicalPathSource": "generated", "category": "Design & Architecture", "contentPath": "/src/contents/v1/Design & Architecture/idempotent-consumers-and-duplicate-message-handling.md", "folders": [ @@ -2350,12 +2706,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How does an inbox table prevent duplicate processing?" } ], + "seoDescription": "Learn Idempotent consumers and duplicate-message handling for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Idempotent consumers and duplicate-message handling Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Idempotent consumers and duplicate-message handling", "title": "Idempotent consumers and duplicate-message handling", "topic": "Distributed systems patterns", "topicFolderPath": "Design & Architecture/distributed-systems-patterns" }, { + "canonicalPath": "/content/insecure-direct-object-reference-broken-access-control-and-object-level-authorization/", + "canonicalPathSource": "generated", "category": "Design & Architecture", "contentPath": "/src/contents/v1/Design & Architecture/insecure-direct-object-reference-broken-access-control-and-object-level-authorization.md", "folders": [ @@ -2390,12 +2752,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How would you fix an endpoint that retrieves an order by ID?" } ], + "seoDescription": "Learn Insecure Direct Object Reference, broken access control, and object-level authorization for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Insecure Direct Object Reference, broken access control, and object-level authorization Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Insecure Direct Object Reference, broken access control, and object-level authorization", "title": "Insecure Direct Object Reference, broken access control, and object-level authorization", "topic": "Web application security threat modeling and attack patterns", "topicFolderPath": "Design & Architecture/web-application-security-threat-modeling-and-attack-patterns" }, { + "canonicalPath": "/content/kiss-dry-yagni-separation-of-concerns-cohesion-and-coupling/", + "canonicalPathSource": "generated", "category": "Design & Architecture", "contentPath": "/src/contents/v1/Design & Architecture/kiss-dry-yagni-separation-of-concerns-cohesion-and-coupling.md", "folders": [ @@ -2430,12 +2798,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How can DRY be harmful when applied incorrectly?" } ], + "seoDescription": "Learn KISS, DRY, YAGNI, Separation of Concerns, Cohesion, and Coupling for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "KISS, DRY, YAGNI, Separation of Concerns, Cohesion, and Coupling Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "KISS, DRY, YAGNI, Separation of Concerns, Cohesion, and Coupling", "title": "KISS, DRY, YAGNI, Separation of Concerns, Cohesion, and Coupling", "topic": "Software design principles and common .NET patterns", "topicFolderPath": "Design & Architecture/software-design-principles-and-common-net-patterns" }, { + "canonicalPath": "/content/layered-architecture-vs-clean-architecture-vs-ports-and-adapters/", + "canonicalPathSource": "generated", "category": "Design & Architecture", "contentPath": "/src/contents/v1/Design & Architecture/layered-architecture-vs-clean-architecture-vs-ports-and-adapters.md", "folders": [ @@ -2470,12 +2844,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "What is the main difference between traditional layering and Clean Architecture?" } ], + "seoDescription": "Learn Layered architecture vs Clean Architecture vs ports-and-adapters for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Layered architecture vs Clean Architecture vs ports-and-adapters Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Layered architecture vs Clean Architecture vs ports-and-adapters", "title": "Layered architecture vs Clean Architecture vs ports-and-adapters", "topic": "Clean Architecture and modular boundaries", "topicFolderPath": "Design & Architecture/clean-architecture-and-modular-boundaries" }, { + "canonicalPath": "/content/mapping-domain-models-to-persistence-without-corrupting-the-model/", + "canonicalPathSource": "generated", "category": "Design & Architecture", "contentPath": "/src/contents/v1/Design & Architecture/mapping-domain-models-to-persistence-without-corrupting-the-model.md", "folders": [ @@ -2510,12 +2890,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "When should you use a separate persistence model instead of directly mapping domain entities?" } ], + "seoDescription": "Learn Mapping domain models to persistence without corrupting the model for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Mapping domain models to persistence without corrupting the model Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Mapping domain models to persistence without corrupting the model", "title": "Mapping domain models to persistence without corrupting the model", "topic": "Domain modeling and Domain-Driven Design", "topicFolderPath": "Design & Architecture/domain-modeling-and-domain-driven-design" }, { + "canonicalPath": "/content/mentoring-ownership-and-incremental-modernization/", + "canonicalPathSource": "generated", "category": "Design & Architecture", "contentPath": "/src/contents/v1/Design & Architecture/mentoring-ownership-and-incremental-modernization.md", "folders": [ @@ -2550,12 +2936,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How do you mentor an engineer without becoming a bottleneck?" } ], + "seoDescription": "Learn Mentoring, ownership, and incremental modernization for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Mentoring, ownership, and incremental modernization Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Mentoring, ownership, and incremental modernization", "title": "Mentoring, ownership, and incremental modernization", "topic": "Testing strategy, maintainability, and technical leadership", "topicFolderPath": "Design & Architecture/testing-strategy-maintainability-and-technical-leadership" }, { + "canonicalPath": "/content/modular-monolith-structure-and-feature-based-organization/", + "canonicalPathSource": "generated", "category": "Design & Architecture", "contentPath": "/src/contents/v1/Design & Architecture/modular-monolith-structure-and-feature-based-organization.md", "folders": [ @@ -2590,12 +2982,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How should modules communicate inside a modular monolith?" } ], + "seoDescription": "Learn Modular monolith structure and feature-based organization for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Modular monolith structure and feature-based organization Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Modular monolith structure and feature-based organization", "title": "Modular monolith structure and feature-based organization", "topic": "Clean Architecture and modular boundaries", "topicFolderPath": "Design & Architecture/clean-architecture-and-modular-boundaries" }, { + "canonicalPath": "/content/openapi-contracts-and-consumer-facing-documentation/", + "canonicalPathSource": "generated", "category": "Design & Architecture", "contentPath": "/src/contents/v1/Design & Architecture/openapi-contracts-and-consumer-facing-documentation.md", "folders": [ @@ -2630,12 +3028,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "What are the trade-offs between contract-first and code-first OpenAPI workflows?" } ], + "seoDescription": "Learn OpenAPI contracts and consumer-facing documentation for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "OpenAPI contracts and consumer-facing documentation Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "OpenAPI contracts and consumer-facing documentation", "title": "OpenAPI contracts and consumer-facing documentation", "topic": "API design and integration contracts", "topicFolderPath": "Design & Architecture/api-design-and-integration-contracts" }, { + "canonicalPath": "/content/recognizing-when-a-pattern-improves-maintainability-vs-when-it-adds-unnecessary-complexity/", + "canonicalPathSource": "generated", "category": "Design & Architecture", "contentPath": "/src/contents/v1/Design & Architecture/recognizing-when-a-pattern-improves-maintainability-vs-when-it-adds-unnecessary-complexity.md", "folders": [ @@ -2670,12 +3074,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "When would you replace a switch statement with the Strategy pattern?" } ], + "seoDescription": "Learn Recognizing when a pattern improves maintainability vs when it adds unnecessary complexity for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Recognizing when a pattern improves maintainability vs when it adds unnecessary complexity Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Recognizing when a pattern improves maintainability vs when it adds unnecessary complexity", "title": "Recognizing when a pattern improves maintainability vs when it adds unnecessary complexity", "topic": "Software design principles and common .NET patterns", "topicFolderPath": "Design & Architecture/software-design-principles-and-common-net-patterns" }, { + "canonicalPath": "/content/refactoring-strategy-code-review-standards-and-technical-debt-management/", + "canonicalPathSource": "generated", "category": "Design & Architecture", "contentPath": "/src/contents/v1/Design & Architecture/refactoring-strategy-code-review-standards-and-technical-debt-management.md", "folders": [ @@ -2710,12 +3120,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How would you safely refactor legacy code with few tests?" } ], + "seoDescription": "Learn Refactoring strategy, code review standards, and technical debt management for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Refactoring strategy, code review standards, and technical debt management Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Refactoring strategy, code review standards, and technical debt management", "title": "Refactoring strategy, code review standards, and technical debt management", "topic": "Testing strategy, maintainability, and technical leadership", "topicFolderPath": "Design & Architecture/testing-strategy-maintainability-and-technical-leadership" }, { + "canonicalPath": "/content/repository-unit-of-work-mediator-specification-dotnet-patterns/", + "canonicalPathSource": "generated", "category": "Design & Architecture", "contentPath": "/src/contents/v1/Design & Architecture/repository-unit-of-work-mediator-specification-dotnet-patterns.md", "folders": [ @@ -2750,12 +3166,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "Is Repository still needed when using EF Core?" } ], + "seoDescription": "Learn Repository, Unit of Work, Mediator, and Specification Patterns in .NET for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Repository, Unit of Work, Mediator, and Specification Patterns in .NET Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Repository, Unit of Work, Mediator, and Specification Patterns in .NET", "title": "Repository, Unit of Work, Mediator, and Specification Patterns in .NET", "topic": "Software design principles and common .NET patterns", "topicFolderPath": "Design & Architecture/software-design-principles-and-common-net-patterns" }, { + "canonicalPath": "/content/resource-modeling-rest-semantics-and-rpc-style-endpoints/", + "canonicalPathSource": "generated", "category": "Design & Architecture", "contentPath": "/src/contents/v1/Design & Architecture/resource-modeling-rest-semantics-and-rpc-style-endpoints.md", "folders": [ @@ -2790,12 +3212,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "When is an RPC-style action endpoint acceptable?" } ], + "seoDescription": "Learn Resource modeling, REST semantics, and when RPC-style endpoints are acceptable for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Resource modeling, REST semantics, and when RPC-style endpoints are acceptable Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Resource modeling, REST semantics, and when RPC-style endpoints are acceptable", "title": "Resource modeling, REST semantics, and when RPC-style endpoints are acceptable", "topic": "API design and integration contracts", "topicFolderPath": "Design & Architecture/api-design-and-integration-contracts" }, { + "canonicalPath": "/content/retry-circuit-breaker-bulkhead-and-queue-based-load-leveling/", + "canonicalPathSource": "generated", "category": "Design & Architecture", "contentPath": "/src/contents/v1/Design & Architecture/retry-circuit-breaker-bulkhead-and-queue-based-load-leveling.md", "folders": [ @@ -2830,12 +3258,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How would you configure retries for an HTTP dependency?" } ], + "seoDescription": "Learn Retry, circuit breaker, bulkhead, and queue-based load leveling for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Retry, circuit breaker, bulkhead, and queue-based load leveling Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Retry, circuit breaker, bulkhead, and queue-based load leveling", "title": "Retry, circuit breaker, bulkhead, and queue-based load leveling", "topic": "Scalability, resilience, caching, and observability design", "topicFolderPath": "Design & Architecture/scalability-resilience-caching-and-observability-design" }, { + "canonicalPath": "/content/saga-and-compensating-transaction-patterns/", + "canonicalPathSource": "generated", "category": "Design & Architecture", "contentPath": "/src/contents/v1/Design & Architecture/saga-and-compensating-transaction-patterns.md", "folders": [ @@ -2870,12 +3304,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How should saga steps be classified and ordered?" } ], + "seoDescription": "Learn Saga and compensating transaction patterns for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Saga and compensating transaction patterns Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Saga and compensating transaction patterns", "title": "Saga and compensating transaction patterns", "topic": "Distributed systems patterns", "topicFolderPath": "Design & Architecture/distributed-systems-patterns" }, { + "canonicalPath": "/content/session-hijacking-credential-stuffing-brute-force-protection-lockout-and-multi-factor-authentication/", + "canonicalPathSource": "generated", "category": "Design & Architecture", "contentPath": "/src/contents/v1/Design & Architecture/session-hijacking-credential-stuffing-brute-force-protection-lockout-and-multi-factor-authentication.md", "folders": [ @@ -2910,12 +3350,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How would you design rate limiting for a public login endpoint?" } ], + "seoDescription": "Learn Session hijacking, credential stuffing, brute-force protection, lockout, and multi-factor authentication for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Session hijacking, credential stuffing, brute-force protection, lockout, and multi-factor authentication Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Session hijacking, credential stuffing, brute-force protection, lockout, and multi-factor authentication", "title": "Session hijacking, credential stuffing, brute-force protection, lockout, and multi-factor authentication", "topic": "Web application security threat modeling and attack patterns", "topicFolderPath": "Design & Architecture/web-application-security-threat-modeling-and-attack-patterns" }, { + "canonicalPath": "/content/solid-principles-in-dotnet-design/", + "canonicalPathSource": "generated", "category": "Design & Architecture", "contentPath": "/src/contents/v1/Design & Architecture/solid-principles-in-dotnet-design.md", "folders": [ @@ -2950,12 +3396,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How are dependency inversion and dependency injection different?" } ], + "seoDescription": "Learn SOLID Principles in .NET Design for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "SOLID Principles in .NET Design Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "SOLID Principles in .NET Design", "title": "SOLID Principles in .NET Design", "topic": "Software design principles and common .NET patterns", "topicFolderPath": "Design & Architecture/software-design-principles-and-common-net-patterns" }, { + "canonicalPath": "/content/sql-injection-parameterized-queries-orm-safety-and-unsafe-dynamic-sql/", + "canonicalPathSource": "generated", "category": "Design & Architecture", "contentPath": "/src/contents/v1/Design & Architecture/sql-injection-parameterized-queries-orm-safety-and-unsafe-dynamic-sql.md", "folders": [ @@ -2990,12 +3442,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How do you safely implement a user-selected sort column?" } ], + "seoDescription": "Learn SQL Injection, parameterized queries, ORM safety, and unsafe dynamic SQL for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "SQL Injection, parameterized queries, ORM safety, and unsafe dynamic SQL Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "SQL Injection, parameterized queries, ORM safety, and unsafe dynamic SQL", "title": "SQL Injection, parameterized queries, ORM safety, and unsafe dynamic SQL", "topic": "Web application security threat modeling and attack patterns", "topicFolderPath": "Design & Architecture/web-application-security-threat-modeling-and-attack-patterns" }, { + "canonicalPath": "/content/test-pyramid-contract-tests-and-architecture-fitness-checks/", + "canonicalPathSource": "generated", "category": "Design & Architecture", "contentPath": "/src/contents/v1/Design & Architecture/test-pyramid-contract-tests-and-architecture-fitness-checks.md", "folders": [ @@ -3030,12 +3488,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How would you test an ASP.NET Core API effectively?" } ], + "seoDescription": "Learn Test pyramid, contract tests, and architecture fitness checks for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Test pyramid, contract tests, and architecture fitness checks Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Test pyramid, contract tests, and architecture fitness checks", "title": "Test pyramid, contract tests, and architecture fitness checks", "topic": "Testing strategy, maintainability, and technical leadership", "topicFolderPath": "Design & Architecture/testing-strategy-maintainability-and-technical-leadership" }, { + "canonicalPath": "/content/threat-modeling-user-input-trust-boundaries-authentication-flows-and-admin-only-operations/", + "canonicalPathSource": "generated", "category": "Design & Architecture", "contentPath": "/src/contents/v1/Design & Architecture/threat-modeling-user-input-trust-boundaries-authentication-flows-and-admin-only-operations.md", "folders": [ @@ -3070,12 +3534,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How would you threat-model an authentication flow?" } ], + "seoDescription": "Learn Threat modeling user input, trust boundaries, authentication flows, and admin-only operations for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Threat modeling user input, trust boundaries, authentication flows, and admin-only operations Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Threat modeling user input, trust boundaries, authentication flows, and admin-only operations", "title": "Threat modeling user input, trust boundaries, authentication flows, and admin-only operations", "topic": "Web application security threat modeling and attack patterns", "topicFolderPath": "Design & Architecture/web-application-security-threat-modeling-and-attack-patterns" }, { + "canonicalPath": "/content/throughput-latency-concurrency-availability-consistency-cost-targets/", + "canonicalPathSource": "generated", "category": "Design & Architecture", "contentPath": "/src/contents/v1/Design & Architecture/throughput-latency-concurrency-availability-consistency-cost-targets.md", "folders": [ @@ -3110,12 +3580,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How would you decompose a vague requirement like \"the system must handle one million users\"?" } ], + "seoDescription": "Learn Throughput, latency, concurrency, availability, consistency, and cost targets for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Throughput, latency, concurrency, availability, consistency, and cost targets Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Throughput, latency, concurrency, availability, consistency, and cost targets", "title": "Throughput, latency, concurrency, availability, consistency, and cost targets", "topic": "Requirements decomposition and system trade-offs", "topicFolderPath": "Design & Architecture/requirements-decomposition-and-system-trade-offs" }, { + "canonicalPath": "/content/ubiquitous-language-and-bounded-contexts/", + "canonicalPathSource": "generated", "category": "Design & Architecture", "contentPath": "/src/contents/v1/Design & Architecture/ubiquitous-language-and-bounded-contexts.md", "folders": [ @@ -3150,12 +3626,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How do you discover a Ubiquitous Language with domain experts?" } ], + "seoDescription": "Learn Ubiquitous language and bounded contexts for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Ubiquitous language and bounded contexts Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Ubiquitous language and bounded contexts", "title": "Ubiquitous language and bounded contexts", "topic": "Domain modeling and Domain-Driven Design", "topicFolderPath": "Design & Architecture/domain-modeling-and-domain-driven-design" }, { + "canonicalPath": "/content/versioning-idempotency-pagination-filtering-and-sorting/", + "canonicalPathSource": "generated", "category": "Design & Architecture", "contentPath": "/src/contents/v1/Design & Architecture/versioning-idempotency-pagination-filtering-and-sorting.md", "folders": [ @@ -3190,12 +3672,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "What are the trade-offs among path, query, header, and media-type versioning?" } ], + "seoDescription": "Learn Versioning, idempotency, pagination, filtering, and sorting for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Versioning, idempotency, pagination, filtering, and sorting Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Versioning, idempotency, pagination, filtering, and sorting", "title": "Versioning, idempotency, pagination, filtering, and sorting", "topic": "API design and integration contracts", "topicFolderPath": "Design & Architecture/api-design-and-integration-contracts" }, { + "canonicalPath": "/content/when-not-to-over-architect-a-simple-application/", + "canonicalPathSource": "generated", "category": "Design & Architecture", "contentPath": "/src/contents/v1/Design & Architecture/when-not-to-over-architect-a-simple-application.md", "folders": [ @@ -3230,12 +3718,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How do you decide whether an abstraction is justified?" } ], + "seoDescription": "Learn When not to over-architect a simple application for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "When not to over-architect a simple application Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "When not to over-architect a simple application", "title": "When not to over-architect a simple application", "topic": "Clean Architecture and modular boundaries", "topicFolderPath": "Design & Architecture/clean-architecture-and-modular-boundaries" }, { + "canonicalPath": "/content/api-controller-behavior-automatic-model-validation-binding-source-inference-and-validation-responses/", + "canonicalPathSource": "generated", "category": ".NET", "contentPath": "/src/contents/v1/NET/api-controller-behavior-automatic-model-validation-binding-source-inference-and-validation-responses.md", "folders": [ @@ -3270,12 +3764,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "Explain binding-source inference with [ApiController]." } ], + "seoDescription": "Learn [ApiController] Behavior for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "[ApiController] Behavior Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "[ApiController] Behavior", "title": "[ApiController] Behavior", "topic": "API design and implementation", "topicFolderPath": "NET/api-design-and-implementation" }, { + "canonicalPath": "/content/aspnet-core-filters-and-middleware-boundaries/", + "canonicalPathSource": "generated", "category": ".NET", "contentPath": "/src/contents/v1/NET/aspnet-core-filters-and-middleware-boundaries.md", "folders": [ @@ -3310,12 +3810,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "When would you use a resource filter instead of an action filter?" } ], + "seoDescription": "Learn ASP.NET Core filters and middleware boundaries for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "ASP.NET Core filters and middleware boundaries Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "ASP.NET Core filters and middleware boundaries", "title": "ASP.NET Core filters and middleware boundaries", "topic": "API design and implementation", "topicFolderPath": "NET/api-design-and-implementation" }, { + "canonicalPath": "/content/async-and-await-semantics-in-csharp/", + "canonicalPathSource": "frontmatter", "category": ".NET", "contentPath": "/src/contents/v1/NET/async-and-await-semantics-in-csharp.md", "folders": [ @@ -3350,12 +3856,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "What is the difference between .Result, .Wait(), and await?" } ], + "seoDescription": "Learn async and await in C# for technical interviews, including Task, cancellation, exception handling, and common deadlock risks.", + "seoDescriptionSource": "frontmatter", + "seoTitle": "Async and Await in C# Interview Questions", + "seoTitleSource": "frontmatter", "subtopic": "Async and Await Semantics", "title": "Async and Await Semantics", "topic": "Async programming, tasks, cancellation, and concurrency", "topicFolderPath": "NET/async-programming-tasks-cancellation-and-concurrency" }, { + "canonicalPath": "/content/authentication-vs-authorization-in-csharp/", + "canonicalPathSource": "generated", "category": ".NET", "contentPath": "/src/contents/v1/NET/authentication-vs-authorization-in-csharp.md", "folders": [ @@ -3390,12 +3902,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "What is an authentication scheme?" } ], + "seoDescription": "Learn Authentication vs Authorization in C# for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Authentication vs Authorization in C# Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Authentication vs Authorization in C#", "title": "Authentication vs Authorization in C#", "topic": "Authentication, authorization, and web security", "topicFolderPath": "NET/authentication-authorization-and-web-security" }, { + "canonicalPath": "/content/cancellationtoken-propagation-in-csharp/", + "canonicalPathSource": "generated", "category": ".NET", "contentPath": "/src/contents/v1/NET/cancellationtoken-propagation-in-csharp.md", "folders": [ @@ -3430,12 +3948,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How do you use CancellationToken in an ASP.NET Core API?" } ], + "seoDescription": "Learn CancellationToken Propagation for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "CancellationToken Propagation Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "CancellationToken Propagation", "title": "CancellationToken Propagation", "topic": "Async programming, tasks, cancellation, and concurrency", "topicFolderPath": "NET/async-programming-tasks-cancellation-and-concurrency" }, { + "canonicalPath": "/content/classes-structs-records/", + "canonicalPathSource": "generated", "category": ".NET", "contentPath": "/src/contents/v1/NET/classes-structs-records-object-oriented-fundamentals.md", "folders": [ @@ -3470,12 +3994,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "Why are mutable structs considered dangerous?" } ], + "seoDescription": "Learn Classes, structs, records for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Classes, structs, records Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Classes, structs, records", "title": "Classes, structs, records", "topic": "C# Language Foundations", "topicFolderPath": "NET/c-language-foundations" }, { + "canonicalPath": "/content/code-coverage-useful-assertions-flaky-test-prevention-and-ci-test-execution/", + "canonicalPathSource": "generated", "category": ".NET", "contentPath": "/src/contents/v1/NET/code-coverage-useful-assertions-flaky-test-prevention-and-ci-test-execution.md", "folders": [ @@ -3510,12 +4040,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "What is the difference between line coverage and branch coverage?" } ], + "seoDescription": "Learn Code Coverage, Useful Assertions, Flaky Test Prevention, and CI Test Execution for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Code Coverage, Useful Assertions, Flaky Test Prevention, and CI Test Execution Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Code Coverage, Useful Assertions, Flaky Test Prevention, and CI Test Execution", "title": "Code Coverage, Useful Assertions, Flaky Test Prevention, and CI Test Execution", "topic": "Testing strategy and integration testing", "topicFolderPath": "NET/testing-strategy-and-integration-testing" }, { + "canonicalPath": "/content/collection-choices-in-csharp/", + "canonicalPathSource": "generated", "category": ".NET", "contentPath": "/src/contents/v1/NET/collection-choices-in-csharp.md", "folders": [ @@ -3550,12 +4086,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How do you choose between List and HashSet?" } ], + "seoDescription": "Learn Collection Choices in C# for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Collection Choices in C# Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Collection Choices in C#", "title": "Collection Choices in C#", "topic": "C# Language Foundations", "topicFolderPath": "NET/c-language-foundations" }, { + "canonicalPath": "/content/common-bcl-types-in-csharp/", + "canonicalPathSource": "generated", "category": ".NET", "contentPath": "/src/contents/v1/NET/common-bcl-types-in-csharp.md", "folders": [ @@ -3590,12 +4132,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "When should you use decimal instead of double?" } ], + "seoDescription": "Learn Common BCL Types in C# for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Common BCL Types in C# Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Common BCL Types in C#", "title": "Common BCL Types in C#", "topic": "C# Language Foundations", "topicFolderPath": "NET/c-language-foundations" }, { + "canonicalPath": "/content/configuration-sources-and-the-options-pattern/", + "canonicalPathSource": "generated", "category": ".NET", "contentPath": "/src/contents/v1/NET/configuration-sources-and-the-options-pattern.md", "folders": [ @@ -3630,12 +4178,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How does configuration provider ordering work?" } ], + "seoDescription": "Learn Configuration Sources and the Options Pattern for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Configuration Sources and the Options Pattern Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Configuration Sources and the Options Pattern", "title": "Configuration Sources and the Options Pattern", "topic": "Dependency injection, configuration, middleware, and logging", "topicFolderPath": "NET/dependency-injection-configuration-middleware-and-logging" }, { + "canonicalPath": "/content/content-negotiation-status-codes-dtos-request-response-contracts/", + "canonicalPathSource": "generated", "category": ".NET", "contentPath": "/src/contents/v1/NET/content-negotiation-status-codes-dtos-request-response-contracts.md", "folders": [ @@ -3670,12 +4224,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "Why should APIs avoid returning EF Core entities directly?" } ], + "seoDescription": "Learn Content negotiation, status codes and request/response contracts for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Content negotiation, status codes and request/response contracts Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Content negotiation, status codes and request/response contracts", "title": "Content negotiation, status codes and request/response contracts", "topic": "API design and implementation", "topicFolderPath": "NET/api-design-and-implementation" }, { + "canonicalPath": "/content/controllers-vs-minimal-apis-when-to-choose-each/", + "canonicalPathSource": "generated", "category": ".NET", "contentPath": "/src/contents/v1/NET/controllers-vs-minimal-apis-when-to-choose-each.md", "folders": [ @@ -3710,12 +4270,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "When would you choose Minimal APIs over controllers?" } ], + "seoDescription": "Learn Controllers vs Minimal APIs for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Controllers vs Minimal APIs Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Controllers vs Minimal APIs", "title": "Controllers vs Minimal APIs", "topic": "API design and implementation", "topicFolderPath": "NET/api-design-and-implementation" }, { + "canonicalPath": "/content/conventions-fluent-api-owned-complex-data-and-relationship-mapping/", + "canonicalPathSource": "generated", "category": ".NET", "contentPath": "/src/contents/v1/NET/conventions-fluent-api-owned-complex-data-and-relationship-mapping.md", "folders": [ @@ -3750,12 +4316,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "When should you use Fluent API instead of conventions or data annotations?" } ], + "seoDescription": "Learn Conventions, Fluent API, Owned/Complex Data, and Relationship Mapping for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Conventions, Fluent API, Owned/Complex Data, and Relationship Mapping Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Conventions, Fluent API, Owned/Complex Data, and Relationship Mapping", "title": "Conventions, Fluent API, Owned/Complex Data, and Relationship Mapping", "topic": "Entity Framework", "topicFolderPath": "NET/entity-framework" }, { + "canonicalPath": "/content/cookie-behavior-csrf-browser-security-concerns/", + "canonicalPathSource": "generated", "category": ".NET", "contentPath": "/src/contents/v1/NET/cookie-behavior-csrf-browser-security-concerns.md", "folders": [ @@ -3790,12 +4362,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How do anti-forgery tokens prevent CSRF?" } ], + "seoDescription": "Learn Cookie behavior, CSRF, and browser-based security concerns for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Cookie behavior, CSRF, and browser-based security concerns Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Cookie behavior, CSRF, and browser-based security concerns", "title": "Cookie behavior, CSRF, and browser-based security concerns", "topic": "Authentication, authorization, and web security", "topicFolderPath": "NET/authentication-authorization-and-web-security" }, { + "canonicalPath": "/content/coordinating-multiple-tasks-in-csharp/", + "canonicalPathSource": "generated", "category": ".NET", "contentPath": "/src/contents/v1/NET/coordinating-multiple-tasks-in-csharp.md", "folders": [ @@ -3830,12 +4408,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How do exceptions behave with Task.WhenAll?" } ], + "seoDescription": "Learn Coordinating Multiple Tasks in C# for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Coordinating Multiple Tasks in C# Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Coordinating Multiple Tasks in C#", "title": "Coordinating Multiple Tasks in C#", "topic": "Async programming, tasks, cancellation, and concurrency", "topicFolderPath": "NET/async-programming-tasks-cancellation-and-concurrency" }, { + "canonicalPath": "/content/cors-secure-headers-secret-handling-and-least-privilege/", + "canonicalPathSource": "generated", "category": "Design & Architecture", "contentPath": "/src/contents/v1/NET/cors-secure-headers-secret-handling-least-privilege.md", "folders": [ @@ -3870,12 +4454,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "Why is AllowAnyOrigin dangerous for authenticated APIs?" } ], + "seoDescription": "Learn CORS, secure headers, secret handling, and least privilege for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "CORS, secure headers, secret handling, and least privilege Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "CORS, secure headers, secret handling, and least privilege", "title": "CORS, secure headers, secret handling, and least privilege", "topic": "Authentication, authorization, and web security", "topicFolderPath": "NET/authentication-authorization-and-web-security" }, { + "canonicalPath": "/content/cpu-bound-vs-io-bound-work-and-task-in-csharp/", + "canonicalPathSource": "generated", "category": ".NET", "contentPath": "/src/contents/v1/NET/cpu-bound-vs-io-bound-work-and-task-in-csharp.md", "folders": [ @@ -3910,12 +4500,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "Why should you not usually wrap I/O-bound work in Task.Run?" } ], + "seoDescription": "Learn CPU-bound vs I/O-bound Work and Task for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "CPU-bound vs I/O-bound Work and Task Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "CPU-bound vs I/O-bound Work and Task", "title": "CPU-bound vs I/O-bound Work and Task", "topic": "Async programming, tasks, cancellation, and concurrency", "topicFolderPath": "NET/async-programming-tasks-cancellation-and-concurrency" }, { + "canonicalPath": "/content/data-seeding-choices-including-hasdata-vs-runtime-seed-logic/", + "canonicalPathSource": "generated", "category": ".NET", "contentPath": "/src/contents/v1/NET/data-seeding-choices-including-hasdata-vs-runtime-seed-logic.md", "folders": [ @@ -3950,12 +4546,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "What is the difference between HasData and runtime seed logic?" } ], + "seoDescription": "Learn Data Seeding Choices, Including HasData vs Runtime Seed Logic for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Data Seeding Choices, Including HasData vs Runtime Seed Logic Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Data Seeding Choices, Including HasData vs Runtime Seed Logic", "title": "Data Seeding Choices, Including HasData vs Runtime Seed Logic", "topic": "Entity Framework", "topicFolderPath": "NET/entity-framework" }, { + "canonicalPath": "/content/dbcontext-change-tracking-and-entity-state-management-in-ef-core/", + "canonicalPathSource": "generated", "category": ".NET", "contentPath": "/src/contents/v1/NET/dbcontext-change-tracking-and-entity-state-management-in-ef-core.md", "folders": [ @@ -3990,12 +4592,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "What is the difference between Add, Attach, and Update?" } ], + "seoDescription": "Learn DbContext change tracking and entity state management in EF Core for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "DbContext change tracking and entity state management in EF Core Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "DbContext change tracking and entity state management in EF Core", "title": "DbContext change tracking and entity state management in EF Core", "topic": "Entity Framework", "topicFolderPath": "NET/entity-framework" }, { + "canonicalPath": "/content/dbcontext-lifetime-thread-safety-connection-usage-and-when-to-avoid-sharing-a-context/", + "canonicalPathSource": "generated", "category": ".NET", "contentPath": "/src/contents/v1/NET/dbcontext-lifetime-thread-safety-connection-usage-and-when-to-avoid-sharing-a-context.md", "folders": [ @@ -4030,12 +4638,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "When should you use IDbContextFactory?" } ], + "seoDescription": "Learn DbContext Lifetime, Thread Safety, Connection Usage, and When to Avoid Sharing a Context for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "DbContext Lifetime, Thread Safety, Connection Usage, and When to Avoid Sharing a Context Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "DbContext Lifetime, Thread Safety, Connection Usage, and When to Avoid Sharing a Context", "title": "DbContext Lifetime, Thread Safety, Connection Usage, and When to Avoid Sharing a Context", "topic": "Entity Framework", "topicFolderPath": "NET/entity-framework" }, { + "canonicalPath": "/content/deferred-execution-in-csharp/", + "canonicalPathSource": "generated", "category": ".NET", "contentPath": "/src/contents/v1/NET/deferred-execution-in-csharp.md", "folders": [ @@ -4070,12 +4684,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "Why can multiple enumeration be a problem?" } ], + "seoDescription": "Learn Deferred Execution in C# for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Deferred Execution in C# Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Deferred Execution in C#", "title": "Deferred Execution in C#", "topic": "Modern C# patterns", "topicFolderPath": "NET/modern-c-patterns" }, { + "canonicalPath": "/content/delegates-in-csharp/", + "canonicalPathSource": "generated", "category": ".NET", "contentPath": "/src/contents/v1/NET/delegates-in-csharp.md", "folders": [ @@ -4110,12 +4730,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "What is a multicast delegate?" } ], + "seoDescription": "Learn Delegates for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Delegates Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Delegates", "title": "Delegates", "topic": "Modern C# patterns", "topicFolderPath": "NET/modern-c-patterns" }, { + "canonicalPath": "/content/di-di-container-basics-lifetimes-and-constructor-selection-in-csharp/", + "canonicalPathSource": "generated", "category": ".NET", "contentPath": "/src/contents/v1/NET/di-di-container-basics-lifetimes-and-constructor-selection-in-csharp.md", "folders": [ @@ -4150,12 +4776,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "What is the difference between transient, scoped, and singleton services?" } ], + "seoDescription": "Learn DI, DI Container Basics for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "DI, DI Container Basics Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "DI, DI Container Basics", "title": "DI, DI Container Basics", "topic": "Dependency injection, configuration, middleware, and logging", "topicFolderPath": "NET/dependency-injection-configuration-middleware-and-logging" }, { + "canonicalPath": "/content/eager-explicit-and-lazy-loading-including-the-n-plus-one-query-problem/", + "canonicalPathSource": "generated", "category": ".NET", "contentPath": "/src/contents/v1/NET/eager-explicit-and-lazy-loading-including-the-n-plus-one-query-problem.md", "folders": [ @@ -4190,12 +4822,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "When would you use eager loading instead of explicit or lazy loading?" } ], + "seoDescription": "Learn Eager, Explicit, and Lazy Loading, Including the N+1 Query Problem for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Eager, Explicit, and Lazy Loading, Including the N+1 Query Problem Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Eager, Explicit, and Lazy Loading, Including the N+1 Query Problem", "title": "Eager, Explicit, and Lazy Loading, Including the N+1 Query Problem", "topic": "Entity Framework", "topicFolderPath": "NET/entity-framework" }, { + "canonicalPath": "/content/ef-core-inmemory-provider-caveats-and-when-sqlite-docker-databases-or-testcontainers-are-safer/", + "canonicalPathSource": "generated", "category": ".NET", "contentPath": "/src/contents/v1/NET/ef-core-inmemory-provider-caveats-and-when-sqlite-docker-databases-or-testcontainers-are-safer.md", "folders": [ @@ -4230,12 +4868,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "What database behaviors can InMemory fail to catch?" } ], + "seoDescription": "Learn EF Core InMemory Provider Caveats and When SQLite, Docker Databases, or Testcontainers Are Safer for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "EF Core InMemory Provider Caveats and When SQLite, Docker Databases, or Testcontainers Are Safer Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "EF Core InMemory Provider Caveats and When SQLite, Docker Databases, or Testcontainers Are Safer", "title": "EF Core InMemory Provider Caveats and When SQLite, Docker Databases, or Testcontainers Are Safer", "topic": "Testing strategy and integration testing", "topicFolderPath": "NET/testing-strategy-and-integration-testing" }, { + "canonicalPath": "/content/ef-core-migrations-and-production-migration-safety/", + "canonicalPathSource": "generated", "category": ".NET", "contentPath": "/src/contents/v1/NET/ef-core-migrations-and-production-migration-safety.md", "folders": [ @@ -4270,12 +4914,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "Why should production migrations usually use reviewed SQL scripts?" } ], + "seoDescription": "Learn EF Core migrations and production migration safety for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "EF Core migrations and production migration safety Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "EF Core migrations and production migration safety", "title": "EF Core migrations and production migration safety", "topic": "Entity Framework", "topicFolderPath": "NET/entity-framework" }, { + "canonicalPath": "/content/endpoint-routing-and-route-matching-in-aspnet-core/", + "canonicalPathSource": "generated", "category": ".NET", "contentPath": "/src/contents/v1/NET/endpoint-routing-and-route-matching-in-aspnet-core.md", "folders": [ @@ -4310,12 +4960,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How does ASP.NET Core choose between multiple matching routes?" } ], + "seoDescription": "Learn Endpoint routing and route matching for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Endpoint routing and route matching Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Endpoint routing and route matching", "title": "Endpoint routing and route matching", "topic": "API design and implementation", "topicFolderPath": "NET/api-design-and-implementation" }, { + "canonicalPath": "/content/exception-handling-in-csharp/", + "canonicalPathSource": "generated", "category": ".NET", "contentPath": "/src/contents/v1/NET/exception-handling-in-csharp.md", "folders": [ @@ -4350,12 +5006,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How should exceptions be handled in async methods?" } ], + "seoDescription": "Learn Exception Handling in C# for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Exception Handling in C# Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Exception Handling in C#", "title": "Exception Handling in C#", "topic": "Async programming, tasks, cancellation, and concurrency", "topicFolderPath": "NET/async-programming-tasks-cancellation-and-concurrency" }, { + "canonicalPath": "/content/exceptions-in-csharp/", + "canonicalPathSource": "generated", "category": ".NET", "contentPath": "/src/contents/v1/NET/exceptions-in-csharp.md", "folders": [ @@ -4390,12 +5052,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How do you choose which exception type to throw?" } ], + "seoDescription": "Learn Exceptions for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Exceptions Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Exceptions", "title": "Exceptions", "topic": "C# Language Foundations", "topicFolderPath": "NET/c-language-foundations" }, { + "canonicalPath": "/content/generic-type-constraints-and-reusable-components-csharp/", + "canonicalPathSource": "generated", "category": ".NET", "contentPath": "/src/contents/v1/NET/generic-type-constraints-and-reusable-components-csharp.md", "folders": [ @@ -4430,12 +5098,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "What is the difference between class, class?, and notnull constraints?" } ], + "seoDescription": "Learn Generic Type Constraints and Reusable Components for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Generic Type Constraints and Reusable Components Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Generic Type Constraints and Reusable Components", "title": "Generic Type Constraints and Reusable Components", "topic": "Modern C# patterns", "topicFolderPath": "NET/modern-c-patterns" }, { + "canonicalPath": "/content/ienumerable-vs-iqueryable-in-csharp/", + "canonicalPathSource": "generated", "category": ".NET", "contentPath": "/src/contents/v1/NET/ienumerable-vs-iqueryable-in-csharp.md", "folders": [ @@ -4470,12 +5144,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "Why can using IEnumerable with EF Core cause performance problems?" } ], + "seoDescription": "Learn IEnumerable vs IQueryable in C# for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "IEnumerable vs IQueryable in C# Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "IEnumerable vs IQueryable in C#", "title": "IEnumerable vs IQueryable in C#", "topic": "Modern C# patterns", "topicFolderPath": "NET/modern-c-patterns" }, { + "canonicalPath": "/content/ihttpclientfactory-and-resilient-outbound-http/", + "canonicalPathSource": "generated", "category": ".NET", "contentPath": "/src/contents/v1/NET/ihttpclientfactory-and-resilient-outbound-http.md", "folders": [ @@ -4510,12 +5190,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How does IHttpClientFactory help with socket exhaustion and DNS changes?" } ], + "seoDescription": "Learn IHttpClientFactory and Resilient Outbound HTTP for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "IHttpClientFactory and Resilient Outbound HTTP Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "IHttpClientFactory and Resilient Outbound HTTP", "title": "IHttpClientFactory and Resilient Outbound HTTP", "topic": "Performance, scalability, and caching", "topicFolderPath": "NET/performance-scalability-and-caching" }, { + "canonicalPath": "/content/in-memory-distributed-hybrid-output-caching/", + "canonicalPathSource": "generated", "category": ".NET", "contentPath": "/src/contents/v1/NET/in-memory-distributed-hybrid-output-caching.md", "folders": [ @@ -4550,12 +5236,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How do you choose between in-memory, distributed, hybrid, and output caching?" } ], + "seoDescription": "Learn In-memory, distributed, hybrid, and output caching for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "In-memory, distributed, hybrid, and output caching Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "In-memory, distributed, hybrid, and output caching", "title": "In-memory, distributed, hybrid, and output caching", "topic": "Performance, scalability, and caching", "topicFolderPath": "NET/performance-scalability-and-caching" }, { + "canonicalPath": "/content/jwt-bearer-auth-claims-scopes-and-policy-based-authorization/", + "canonicalPathSource": "generated", "category": ".NET", "contentPath": "/src/contents/v1/NET/jwt-bearer-auth-claims-scopes-and-policy-based-authorization.md", "folders": [ @@ -4590,12 +5282,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "What is the difference between scopes and roles?" } ], + "seoDescription": "Learn JWT Bearer Auth, Claims, Scopes, and Policy-Based Authorization for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "JWT Bearer Auth, Claims, Scopes, and Policy-Based Authorization Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "JWT Bearer Auth, Claims, Scopes, and Policy-Based Authorization", "title": "JWT Bearer Auth, Claims, Scopes, and Policy-Based Authorization", "topic": "Authentication, authorization, and web security", "topicFolderPath": "NET/authentication-authorization-and-web-security" }, { + "canonicalPath": "/content/linq-querying-in-csharp/", + "canonicalPathSource": "generated", "category": ".NET", "contentPath": "/src/contents/v1/NET/linq-querying-in-csharp.md", "folders": [ @@ -4630,12 +5328,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "What is the difference between IEnumerable and IQueryable?" } ], + "seoDescription": "Learn LINQ Querying for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "LINQ Querying Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "LINQ Querying", "title": "LINQ Querying", "topic": "Modern C# patterns", "topicFolderPath": "NET/modern-c-patterns" }, { + "canonicalPath": "/content/middleware-ordering-and-cross-cutting-behavior/", + "canonicalPathSource": "generated", "category": ".NET", "contentPath": "/src/contents/v1/NET/middleware-ordering-and-cross-cutting-behavior.md", "folders": [ @@ -4670,12 +5374,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "What is the correct order for authentication and authorization middleware?" } ], + "seoDescription": "Learn Middleware Ordering and Cross-Cutting Behavior for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Middleware Ordering and Cross-Cutting Behavior Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Middleware Ordering and Cross-Cutting Behavior", "title": "Middleware Ordering and Cross-Cutting Behavior", "topic": "Dependency injection, configuration, middleware, and logging", "topicFolderPath": "NET/dependency-injection-configuration-middleware-and-logging" }, { + "canonicalPath": "/content/nullable-reference-types-and-null-safety-habits-in-csharp/", + "canonicalPathSource": "generated", "category": ".NET", "contentPath": "/src/contents/v1/NET/nullable-reference-types-and-null-safety-habits-in-csharp.md", "folders": [ @@ -4710,12 +5420,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How does C# null-state analysis work?" } ], + "seoDescription": "Learn Nullable reference C# for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Nullable reference C# Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Nullable reference C#", "title": "Nullable reference C#", "topic": "C# Language Foundations", "topicFolderPath": "NET/c-language-foundations" }, { + "canonicalPath": "/content/observer-style-communication-in-csharp/", + "canonicalPathSource": "generated", "category": ".NET", "contentPath": "/src/contents/v1/NET/observer-style-communication-in-csharp.md", "folders": [ @@ -4750,12 +5466,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "What problem does the observer pattern solve?" } ], + "seoDescription": "Learn Observer-Style Communication in C# for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Observer-Style Communication in C# Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Observer-Style Communication in C#", "title": "Observer-Style Communication in C#", "topic": "Modern C# patterns", "topicFolderPath": "NET/modern-c-patterns" }, { + "canonicalPath": "/content/object-oriented-fundamentals/", + "canonicalPathSource": "generated", "category": ".NET", "contentPath": "/src/contents/v1/NET/oop.md", "folders": [ @@ -4790,12 +5512,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "What is abstraction in C#?" } ], + "seoDescription": "Learn Object-Oriented Programming for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Object-Oriented Programming Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Object-Oriented Programming", "title": "Object-Oriented Programming", "topic": "C# Language Foundations", "topicFolderPath": "NET/c-language-foundations" }, { + "canonicalPath": "/content/openapi-generation-endpoint-metadata-api-discoverability/", + "canonicalPathSource": "generated", "category": ".NET", "contentPath": "/src/contents/v1/NET/openapi-generation-endpoint-metadata-api-discoverability.md", "folders": [ @@ -4830,12 +5558,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How do Minimal APIs provide metadata for OpenAPI generation?" } ], + "seoDescription": "Learn OpenAPI generation and API discoverability for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "OpenAPI generation and API discoverability Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "OpenAPI generation and API discoverability", "title": "OpenAPI generation and API discoverability", "topic": "API design and implementation", "topicFolderPath": "NET/api-design-and-implementation" }, { + "canonicalPath": "/content/optimistic-concurrency-transactions-savepoints-conflict-handling/", + "canonicalPathSource": "generated", "category": ".NET", "contentPath": "/src/contents/v1/NET/optimistic-concurrency-transactions-savepoints-conflict-handling.md", "folders": [ @@ -4870,12 +5604,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How does EF Core use a rowversion column to detect concurrency conflicts?" } ], + "seoDescription": "Learn Optimistic concurrency, transactions, savepoints, and conflict handling for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Optimistic concurrency, transactions, savepoints, and conflict handling Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Optimistic concurrency, transactions, savepoints, and conflict handling", "title": "Optimistic concurrency, transactions, savepoints, and conflict handling", "topic": "Entity Framework", "topicFolderPath": "NET/entity-framework" }, { + "canonicalPath": "/content/overriding-services-and-configuration-for-tests/", + "canonicalPathSource": "generated", "category": ".NET", "contentPath": "/src/contents/v1/NET/overriding-services-and-configuration-for-tests.md", "folders": [ @@ -4910,12 +5650,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "What is the difference between ConfigureServices and ConfigureTestServices?" } ], + "seoDescription": "Learn Overriding services and configuration in .NET tests for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Overriding services and configuration in .NET tests Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Overriding services and configuration in .NET tests", "title": "Overriding services and configuration in .NET tests", "topic": "Testing strategy and integration testing", "topicFolderPath": "NET/testing-strategy-and-integration-testing" }, { + "canonicalPath": "/content/parameter-binding-from-route-query-string-body-form-header-and-services/", + "canonicalPathSource": "generated", "category": ".NET", "contentPath": "/src/contents/v1/NET/parameter-binding-from-route-query-string-body-form-header-and-services.md", "folders": [ @@ -4950,12 +5696,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How does [ApiController] affect parameter binding in controller APIs?" } ], + "seoDescription": "Learn Parameter Binding for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Parameter Binding Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Parameter Binding", "title": "Parameter Binding", "topic": "API design and implementation", "topicFolderPath": "NET/api-design-and-implementation" }, { + "canonicalPath": "/content/pattern-matching-in-csharp/", + "canonicalPathSource": "generated", "category": ".NET", "contentPath": "/src/contents/v1/NET/pattern-matching-in-csharp.md", "folders": [ @@ -4990,12 +5742,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "What are property patterns and when would you use them?" } ], + "seoDescription": "Learn Pattern Matching in C# for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Pattern Matching in C# Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Pattern Matching in C#", "title": "Pattern Matching in C#", "topic": "Modern C# patterns", "topicFolderPath": "NET/modern-c-patterns" }, { + "canonicalPath": "/content/rate-limiting-memory-gc-awareness-runtime-diagnostics/", + "canonicalPathSource": "generated", "category": ".NET", "contentPath": "/src/contents/v1/NET/rate-limiting-memory-gc-awareness-runtime-diagnostics.md", "folders": [ @@ -5030,12 +5788,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How would you configure rate limiting in ASP.NET Core?" } ], + "seoDescription": "Learn Rate limiting, memory/GC awareness, and runtime diagnostics for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Rate limiting, memory/GC awareness, and runtime diagnostics Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Rate limiting, memory/GC awareness, and runtime diagnostics", "title": "Rate limiting, memory/GC awareness, and runtime diagnostics", "topic": "Performance, scalability, and caching", "topicFolderPath": "NET/performance-scalability-and-caching" }, { + "canonicalPath": "/content/records-in-csharp/", + "canonicalPathSource": "generated", "category": ".NET", "contentPath": "/src/contents/v1/NET/records-in-csharp.md", "folders": [ @@ -5070,12 +5834,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "When should you use a record instead of a class?" } ], + "seoDescription": "Learn Records in C# for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Records in C# Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Records in C#", "title": "Records in C#", "topic": "Modern C# patterns", "topicFolderPath": "NET/modern-c-patterns" }, { + "canonicalPath": "/content/structured-logging-and-correlation/", + "canonicalPathSource": "generated", "category": ".NET", "contentPath": "/src/contents/v1/NET/structured-logging-and-correlation.md", "folders": [ @@ -5110,12 +5880,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "What is ILogger.BeginScope and when would you use it?" } ], + "seoDescription": "Learn Structured Logging and Correlation for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Structured Logging and Correlation Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Structured Logging and Correlation", "title": "Structured Logging and Correlation", "topic": "Dependency injection, configuration, middleware, and logging", "topicFolderPath": "NET/dependency-injection-configuration-middleware-and-logging" }, { + "canonicalPath": "/content/switch-expressions-in-csharp/", + "canonicalPathSource": "generated", "category": ".NET", "contentPath": "/src/contents/v1/NET/switch-expressions-in-csharp.md", "folders": [ @@ -5150,12 +5926,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How does arm ordering affect a switch expression?" } ], + "seoDescription": "Learn Switch Expressions in C# for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Switch Expressions in C# Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Switch Expressions in C#", "title": "Switch Expressions in C#", "topic": "Modern C# patterns", "topicFolderPath": "NET/modern-c-patterns" }, { + "canonicalPath": "/content/test-doubles-mocks-stubs-fakes-mocking-integration-problems/", + "canonicalPathSource": "generated", "category": ".NET", "contentPath": "/src/contents/v1/NET/test-doubles-mocks-stubs-fakes-mocking-integration-problems.md", "folders": [ @@ -5190,12 +5972,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "When should you use a mock instead of a fake?" } ], + "seoDescription": "Learn Test doubles, mocking boundaries, and integration risks in .NET for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Test doubles, mocking boundaries, and integration risks in .NET Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Test doubles, mocking boundaries, and integration risks in .NET", "title": "Test doubles, mocking boundaries, and integration risks in .NET", "topic": "Testing strategy and integration testing", "topicFolderPath": "NET/testing-strategy-and-integration-testing" }, { + "canonicalPath": "/content/testing-authentication-authorization-middleware-filters-validation-error-responses/", + "canonicalPathSource": "generated", "category": ".NET", "contentPath": "/src/contents/v1/NET/testing-authentication-authorization-middleware-filters-validation-error-responses.md", "folders": [ @@ -5230,12 +6018,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How can you test a protected endpoint without calling a real identity provider?" } ], + "seoDescription": "Learn Testing ASP.NET Core security, pipeline behavior, validation, and error responses for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Testing ASP.NET Core security, pipeline behavior, validation, and error responses Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Testing ASP.NET Core security, pipeline behavior, validation, and error responses", "title": "Testing ASP.NET Core security, pipeline behavior, validation, and error responses", "topic": "Testing strategy and integration testing", "topicFolderPath": "NET/testing-strategy-and-integration-testing" }, { + "canonicalPath": "/content/timeouts-in-csharp/", + "canonicalPathSource": "generated", "category": ".NET", "contentPath": "/src/contents/v1/NET/timeouts-in-csharp.md", "folders": [ @@ -5270,12 +6064,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "What is the difference between Task.WaitAsync and CancellationTokenSource.CancelAfter?" } ], + "seoDescription": "Learn Timeouts in C# for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Timeouts in C# Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Timeouts in C#", "title": "Timeouts in C#", "topic": "Async programming, tasks, cancellation, and concurrency", "topicFolderPath": "NET/async-programming-tasks-cancellation-and-concurrency" }, { + "canonicalPath": "/content/tracking-vs-no-tracking-queries-and-identity-resolution/", + "canonicalPathSource": "generated", "category": ".NET", "contentPath": "/src/contents/v1/NET/tracking-vs-no-tracking-queries-and-identity-resolution.md", "folders": [ @@ -5310,12 +6110,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "What is identity resolution in EF Core?" } ], + "seoDescription": "Learn Tracking vs no-tracking queries and identity resolution for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Tracking vs no-tracking queries and identity resolution Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Tracking vs no-tracking queries and identity resolution", "title": "Tracking vs no-tracking queries and identity resolution", "topic": "Entity Framework", "topicFolderPath": "NET/entity-framework" }, { + "canonicalPath": "/content/unit-tests-integration-tests-and-hosted-services-for-background-jobs/", + "canonicalPathSource": "generated", "category": ".NET", "contentPath": "/src/contents/v1/NET/unit-tests-integration-tests-and-hosted-services-for-background-jobs.md", "folders": [ @@ -5350,12 +6156,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How do you test a BackgroundService without testing an infinite loop?" } ], + "seoDescription": "Learn Unit Tests, Integration Tests, and Hosted Services for Background Jobs for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Unit Tests, Integration Tests, and Hosted Services for Background Jobs Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Unit Tests, Integration Tests, and Hosted Services for Background Jobs", "title": "Unit Tests, Integration Tests, and Hosted Services for Background Jobs", "topic": "Performance, scalability, and caching", "topicFolderPath": "NET/performance-scalability-and-caching" }, { + "canonicalPath": "/content/unit-tests-vs-integration-tests-vs-end-to-end-tests/", + "canonicalPathSource": "generated", "category": ".NET", "contentPath": "/src/contents/v1/NET/unit-tests-vs-integration-tests-vs-end-to-end-tests.md", "folders": [ @@ -5390,12 +6202,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "What is the testing pyramid?" } ], + "seoDescription": "Learn Unit Tests vs Integration Tests vs End-to-End Tests for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Unit Tests vs Integration Tests vs End-to-End Tests Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Unit Tests vs Integration Tests vs End-to-End Tests", "title": "Unit Tests vs Integration Tests vs End-to-End Tests", "topic": "Testing strategy and integration testing", "topicFolderPath": "NET/testing-strategy-and-integration-testing" }, { + "canonicalPath": "/content/value-types-vs-reference-types/", + "canonicalPathSource": "generated", "category": ".NET", "contentPath": "/src/contents/v1/NET/value-types-vs-reference-types.md", "folders": [ @@ -5430,12 +6248,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "What is boxing and unboxing?" } ], + "seoDescription": "Learn Value Types vs Reference Types for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Value Types vs Reference Types Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Value Types vs Reference Types", "title": "Value Types vs Reference Types", "topic": "C# Language Foundations", "topicFolderPath": "NET/c-language-foundations" }, { + "canonicalPath": "/content/webapplicationfactory-testserver-httpclient-and-full-aspnet-core-pipeline-testing/", + "canonicalPathSource": "generated", "category": ".NET", "contentPath": "/src/contents/v1/NET/webapplicationfactory-testserver-httpclient-and-full-aspnet-core-pipeline-testing.md", "folders": [ @@ -5470,12 +6294,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How do you customize WebApplicationFactory for integration tests?" } ], + "seoDescription": "Learn WebApplicationFactory, TestServer, HttpClient, and Full ASP.NET Core Pipeline Testing for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "WebApplicationFactory, TestServer, HttpClient, and Full ASP.NET Core Pipeline Testing Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "WebApplicationFactory, TestServer, HttpClient, and Full ASP.NET Core Pipeline Testing", "title": "WebApplicationFactory, TestServer, HttpClient, and Full ASP.NET Core Pipeline Testing", "topic": "Testing strategy and integration testing", "topicFolderPath": "NET/testing-strategy-and-integration-testing" }, { + "canonicalPath": "/content/xunit-nunit-mstest-test-naming-aaa-test-data-setup/", + "canonicalPathSource": "generated", "category": ".NET", "contentPath": "/src/contents/v1/NET/xunit-nunit-mstest-test-naming-aaa-test-data-setup.md", "folders": [ @@ -5510,12 +6340,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How do setup and teardown differ between xUnit, NUnit, and MSTest?" } ], + "seoDescription": "Learn Unit testing frameworks, naming, AAA, and test data setup in .NET for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Unit testing frameworks, naming, AAA, and test data setup in .NET Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Unit testing frameworks, naming, AAA, and test data setup in .NET", "title": "Unit testing frameworks, naming, AAA, and test data setup in .NET", "topic": "Testing strategy and integration testing", "topicFolderPath": "NET/testing-strategy-and-integration-testing" }, { + "canonicalPath": "/content/access-tokens-refresh-tokens-expiration-handling-retrying-original-requests-and-refresh-token-queue-patterns/", + "canonicalPathSource": "generated", "category": "React", "contentPath": "/src/contents/v1/React/access-tokens-refresh-tokens-expiration-handling-retrying-original-requests-and-refresh-token-queue-patterns.md", "folders": [ @@ -5550,12 +6386,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How do you retry the original request after refreshing a token?" } ], + "seoDescription": "Learn Access tokens, refresh tokens, expiration handling, retrying original requests, and refresh-token queue patterns for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Access tokens, refresh tokens, expiration handling, retrying original requests, and refresh-token queue patterns Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Access tokens, refresh tokens, expiration handling, retrying original requests, and refresh-token queue patterns", "title": "Access tokens, refresh tokens, expiration handling, retrying original requests, and refresh-token queue patterns", "topic": "Production data access, API clients, and frontend auth", "topicFolderPath": "React/production-data-access-api-clients-and-frontend-auth" }, { + "canonicalPath": "/content/axios-request-and-response-interceptors-for-auth-headers-global-errors-logging-and-retry-behavior/", + "canonicalPathSource": "generated", "category": "React", "contentPath": "/src/contents/v1/React/axios-request-and-response-interceptors-for-auth-headers-global-errors-logging-and-retry-behavior.md", "folders": [ @@ -5590,12 +6432,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How would you add an auth header with an Axios request interceptor?" } ], + "seoDescription": "Learn Axios request and response interceptors for auth headers, global errors, logging, and retry behavior for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Axios request and response interceptors for auth headers, global errors, logging, and retry behavior Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Axios request and response interceptors for auth headers, global errors, logging, and retry behavior", "title": "Axios request and response interceptors for auth headers, global errors, logging, and retry behavior", "topic": "Production data access, API clients, and frontend auth", "topicFolderPath": "React/production-data-access-api-clients-and-frontend-auth" }, { + "canonicalPath": "/content/browser-storage-trade-offs-localstorage-sessionstorage-memory-storage-and-cookies/", + "canonicalPathSource": "generated", "category": "React", "contentPath": "/src/contents/v1/React/browser-storage-trade-offs-localstorage-sessionstorage-memory-storage-and-cookies.md", "folders": [ @@ -5630,12 +6478,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "Why is storing tokens in localStorage risky?" } ], + "seoDescription": "Learn Browser storage trade-offs: localStorage, sessionStorage, memory storage, and cookies for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Browser storage trade-offs: localStorage, sessionStorage, memory storage, and cookies Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Browser storage trade-offs: localStorage, sessionStorage, memory storage, and cookies", "title": "Browser storage trade-offs: localStorage, sessionStorage, memory storage, and cookies", "topic": "Production data access, API clients, and frontend auth", "topicFolderPath": "React/production-data-access-api-clients-and-frontend-auth" }, { + "canonicalPath": "/content/built-in-validation-async-validation-and-schema-resolvers-with-yup-or-zod/", + "canonicalPathSource": "generated", "category": "React", "contentPath": "/src/contents/v1/React/built-in-validation-async-validation-and-schema-resolvers-with-yup-or-zod.md", "folders": [ @@ -5670,12 +6524,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "When would you use built-in rules instead of a schema resolver?" } ], + "seoDescription": "Learn Built-in validation, async validation, and schema resolvers with Yup or Zod for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Built-in validation, async validation, and schema resolvers with Yup or Zod Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Built-in validation, async validation, and schema resolvers with Yup or Zod", "title": "Built-in validation, async validation, and schema resolvers with Yup or Zod", "topic": "Forms, validation, and frontend performance in production", "topicFolderPath": "React/forms-validation-and-frontend-performance-in-production" }, { + "canonicalPath": "/content/centralized-api-clients-with-fetch-axios-rtk-query-or-tanstack-query/", + "canonicalPathSource": "generated", "category": "React", "contentPath": "/src/contents/v1/React/centralized-api-clients-with-fetch-axios-rtk-query-or-tanstack-query.md", "folders": [ @@ -5710,12 +6570,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "When would you choose a simple fetch wrapper instead of TanStack Query or RTK Query?" } ], + "seoDescription": "Learn Centralized API clients with `fetch`, Axios, RTK Query, or TanStack Query for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Centralized API clients with `fetch`, Axios, RTK Query, or TanStack Query Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Centralized API clients with `fetch`, Axios, RTK Query, or TanStack Query", "title": "Centralized API clients with `fetch`, Axios, RTK Query, or TanStack Query", "topic": "Production data access, API clients, and frontend auth", "topicFolderPath": "React/production-data-access-api-clients-and-frontend-auth" }, { + "canonicalPath": "/content/closures-lexical-scope/", + "canonicalPathSource": "generated", "category": "React", "contentPath": "/src/contents/v1/React/closures-lexical-scope.md", "folders": [ @@ -5750,12 +6616,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "What is a stale closure?" } ], + "seoDescription": "Learn Closures and lexical scope for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Closures and lexical scope Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Closures and lexical scope", "title": "Closures and lexical scope", "topic": "JavaScript fundamentals", "topicFolderPath": "React/javascript-fundamentals" }, { + "canonicalPath": "/content/code-splitting-lazy-loading-bundle-analysis-and-route-level-loading/", + "canonicalPathSource": "generated", "category": "React", "contentPath": "/src/contents/v1/React/code-splitting-lazy-loading-bundle-analysis-and-route-level-loading.md", "folders": [ @@ -5790,12 +6662,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How should Suspense boundaries be placed for route loading?" } ], + "seoDescription": "Learn Code splitting, lazy loading, bundle analysis, and route-level loading for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Code splitting, lazy loading, bundle analysis, and route-level loading Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Code splitting, lazy loading, bundle analysis, and route-level loading", "title": "Code splitting, lazy loading, bundle analysis, and route-level loading", "topic": "Forms, validation, and frontend performance in production", "topicFolderPath": "React/forms-validation-and-frontend-performance-in-production" }, { + "canonicalPath": "/content/context-plus-reducer-vs-external-stores/", + "canonicalPathSource": "generated", "category": "React", "contentPath": "/src/contents/v1/React/context-plus-reducer-vs-external-stores.md", "folders": [ @@ -5830,12 +6708,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "When would you use context plus reducer?" } ], + "seoDescription": "Learn Context plus reducer vs external stores for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Context plus reducer vs external stores Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Context plus reducer vs external stores", "title": "Context plus reducer vs external stores", "topic": "State management, performance, and rendering optimization", "topicFolderPath": "React/state-management-performance-and-rendering-optimization" }, { + "canonicalPath": "/content/controlled-inputs-and-event-handling/", + "canonicalPathSource": "generated", "category": "React", "contentPath": "/src/contents/v1/React/controlled-inputs-and-event-handling.md", "folders": [ @@ -5870,12 +6754,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "Why should form submission usually be handled with onSubmit?" } ], + "seoDescription": "Learn Controlled inputs and event handling for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Controlled inputs and event handling Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Controlled inputs and event handling", "title": "Controlled inputs and event handling", "topic": "Components, props, state, and rendering behavior", "topicFolderPath": "React/components-props-state-and-rendering-behavior" }, { + "canonicalPath": "/content/debounce-and-throttle-for-search-filtering-autosave-and-expensive-ui-updates/", + "canonicalPathSource": "generated", "category": "React", "contentPath": "/src/contents/v1/React/debounce-and-throttle-for-search-filtering-autosave-and-expensive-ui-updates.md", "folders": [ @@ -5910,12 +6800,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How do you avoid stale search results with debounced requests?" } ], + "seoDescription": "Learn Debounce and throttle for search, filtering, autosave, and expensive UI updates for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Debounce and throttle for search, filtering, autosave, and expensive UI updates Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Debounce and throttle for search, filtering, autosave, and expensive UI updates", "title": "Debounce and throttle for search, filtering, autosave, and expensive UI updates", "topic": "Forms, validation, and frontend performance in production", "topicFolderPath": "React/forms-validation-and-frontend-performance-in-production" }, { + "canonicalPath": "/content/debugging-rendering-hydration-and-interaction-issues/", + "canonicalPathSource": "generated", "category": "React", "contentPath": "/src/contents/v1/React/debugging-rendering-hydration-and-interaction-issues.md", "folders": [ @@ -5950,12 +6846,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How would you debug a component that loses input state after sorting a list?" } ], + "seoDescription": "Learn Debugging rendering, hydration, and interaction issues for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Debugging rendering, hydration, and interaction issues Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Debugging rendering, hydration, and interaction issues", "title": "Debugging rendering, hydration, and interaction issues", "topic": "Testing, accessibility, and frontend debugging", "topicFolderPath": "React/testing-accessibility-and-frontend-debugging" }, { + "canonicalPath": "/content/error-states-and-retry-ux-for-failed-requests/", + "canonicalPathSource": "generated", "category": "React", "contentPath": "/src/contents/v1/React/error-states-and-retry-ux-for-failed-requests.md", "folders": [ @@ -5990,12 +6892,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How should route error boundaries be used for failed requests?" } ], + "seoDescription": "Learn Error states and retry UX for failed requests for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Error states and retry UX for failed requests Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Error states and retry UX for failed requests", "title": "Error states and retry UX for failed requests", "topic": "Routing, forms, and server communication", "topicFolderPath": "React/routing-forms-and-server-communication" }, { + "canonicalPath": "/content/forms-validation-optimistic-updates-and-mutation-states/", + "canonicalPathSource": "generated", "category": "React", "contentPath": "/src/contents/v1/React/forms-validation-optimistic-updates-and-mutation-states.md", "folders": [ @@ -6030,12 +6938,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How should expected validation errors be returned and displayed?" } ], + "seoDescription": "Learn Forms, validation, optimistic updates, and mutation states for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Forms, validation, optimistic updates, and mutation states Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Forms, validation, optimistic updates, and mutation states", "title": "Forms, validation, optimistic updates, and mutation states", "topic": "Routing, forms, and server communication", "topicFolderPath": "React/routing-forms-and-server-communication" }, { + "canonicalPath": "/content/functional-components-and-jsx-composition/", + "canonicalPathSource": "generated", "category": "React", "contentPath": "/src/contents/v1/React/functional-components-and-jsx-composition.md", "folders": [ @@ -6070,12 +6984,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "What are the most important JSX rules to remember?" } ], + "seoDescription": "Learn Functional components and JSX composition for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Functional components and JSX composition Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Functional components and JSX composition", "title": "Functional components and JSX composition", "topic": "Components, props, state, and rendering behavior", "topicFolderPath": "React/components-props-state-and-rendering-behavior" }, { + "canonicalPath": "/content/global-loading-error-unauthorized-and-network-failure-ux-patterns/", + "canonicalPathSource": "generated", "category": "React", "contentPath": "/src/contents/v1/React/global-loading-error-unauthorized-and-network-failure-ux-patterns.md", "folders": [ @@ -6110,12 +7030,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How should background refetch failures be displayed?" } ], + "seoDescription": "Learn Global loading, error, unauthorized, and network-failure UX patterns for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Global loading, error, unauthorized, and network-failure UX patterns Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Global loading, error, unauthorized, and network-failure UX patterns", "title": "Global loading, error, unauthorized, and network-failure UX patterns", "topic": "Production data access, API clients, and frontend auth", "topicFolderPath": "React/production-data-access-api-clients-and-frontend-auth" }, { + "canonicalPath": "/content/keyboard-accessibility-semantic-html-aria-and-labels-ids/", + "canonicalPathSource": "generated", "category": "React", "contentPath": "/src/contents/v1/React/keyboard-accessibility-semantic-html-aria-and-labels-ids.md", "folders": [ @@ -6150,12 +7076,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "When should you use ARIA in React?" } ], + "seoDescription": "Learn Keyboard accessibility, semantic HTML, ARIA, and labels/IDs for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Keyboard accessibility, semantic HTML, ARIA, and labels/IDs Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Keyboard accessibility, semantic HTML, ARIA, and labels/IDs", "title": "Keyboard accessibility, semantic HTML, ARIA, and labels/IDs", "topic": "Testing, accessibility, and frontend debugging", "topicFolderPath": "React/testing-accessibility-and-frontend-debugging" }, { + "canonicalPath": "/content/memoization-and-avoiding-needless-rerenders/", + "canonicalPathSource": "generated", "category": "React", "contentPath": "/src/contents/v1/React/memoization-and-avoiding-needless-rerenders.md", "folders": [ @@ -6190,12 +7122,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "Why can memo fail when props include objects, arrays, or functions?" } ], + "seoDescription": "Learn Memoization and avoiding needless rerenders for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Memoization and avoiding needless rerenders Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Memoization and avoiding needless rerenders", "title": "Memoization and avoiding needless rerenders", "topic": "State management, performance, and rendering optimization", "topicFolderPath": "React/state-management-performance-and-rendering-optimization" }, { + "canonicalPath": "/content/memoization-with-usememo-and-dependency-correctness/", + "canonicalPathSource": "generated", "category": "React", "contentPath": "/src/contents/v1/React/memoization-with-usememo-and-dependency-correctness.md", "folders": [ @@ -6230,12 +7168,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "Why can object dependencies defeat memoization?" } ], + "seoDescription": "Learn Memoization with useMemo and dependency correctness for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Memoization with useMemo and dependency correctness Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Memoization with useMemo and dependency correctness", "title": "Memoization with useMemo and dependency correctness", "topic": "Hooks, effects, and custom hooks", "topicFolderPath": "React/hooks-effects-and-custom-hooks" }, { + "canonicalPath": "/content/modules-and-import-export-behavior/", + "canonicalPathSource": "generated", "category": "React", "contentPath": "/src/contents/v1/React/modules-and-import-export-behavior.md", "folders": [ @@ -6270,12 +7214,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "What does it mean that ES module imports are live bindings?" } ], + "seoDescription": "Learn Modules and import/export behavior for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Modules and import/export behavior Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Modules and import/export behavior", "title": "Modules and import/export behavior", "topic": "JavaScript fundamentals", "topicFolderPath": "React/javascript-fundamentals" }, { + "canonicalPath": "/content/narrowing-and-control-flow-analysis/", + "canonicalPathSource": "generated", "category": "React", "contentPath": "/src/contents/v1/React/narrowing-and-control-flow-analysis.md", "folders": [ @@ -6310,12 +7260,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How do discriminated unions help React UI state?" } ], + "seoDescription": "Learn Narrowing and control-flow analysis for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Narrowing and control-flow analysis Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Narrowing and control-flow analysis", "title": "Narrowing and control-flow analysis", "topic": "TypeScript for React", "topicFolderPath": "React/typescript-for-react" }, { + "canonicalPath": "/content/nested-routes-layouts-params-and-route-boundaries/", + "canonicalPathSource": "generated", "category": "React", "contentPath": "/src/contents/v1/React/nested-routes-layouts-params-and-route-boundaries.md", "folders": [ @@ -6350,12 +7306,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How do layout routes differ from prefix routes?" } ], + "seoDescription": "Learn Nested routes, layouts, params, and route boundaries for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Nested routes, layouts, params, and route boundaries Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Nested routes, layouts, params, and route boundaries", "title": "Nested routes, layouts, params, and route boundaries", "topic": "Routing, forms, and server communication", "topicFolderPath": "React/routing-forms-and-server-communication" }, { + "canonicalPath": "/content/preventing-duplicate-requests-canceling-stale-requests-and-avoiding-race-conditions/", + "canonicalPathSource": "generated", "category": "React", "contentPath": "/src/contents/v1/React/preventing-duplicate-requests-canceling-stale-requests-and-avoiding-race-conditions.md", "folders": [ @@ -6390,12 +7352,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How should a React effect fetch data safely when a prop changes?" } ], + "seoDescription": "Learn Preventing duplicate requests, canceling stale requests, and avoiding race conditions for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Preventing duplicate requests, canceling stale requests, and avoiding race conditions Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Preventing duplicate requests, canceling stale requests, and avoiding race conditions", "title": "Preventing duplicate requests, canceling stale requests, and avoiding race conditions", "topic": "Forms, validation, and frontend performance in production", "topicFolderPath": "React/forms-validation-and-frontend-performance-in-production" }, { + "canonicalPath": "/content/promises-asynchronous-javascript/", + "canonicalPathSource": "generated", "category": "React", "contentPath": "/src/contents/v1/React/promises-asynchronous-javascript.md", "folders": [ @@ -6430,12 +7398,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "What is the difference between Promise.all and Promise.allSettled?" } ], + "seoDescription": "Learn Promises and asynchronous JavaScript for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Promises and asynchronous JavaScript Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Promises and asynchronous JavaScript", "title": "Promises and asynchronous JavaScript", "topic": "JavaScript fundamentals", "topicFolderPath": "React/javascript-fundamentals" }, { + "canonicalPath": "/content/proper-use-effect-usage-and-cleanup/", + "canonicalPathSource": "generated", "category": "React", "contentPath": "/src/contents/v1/React/proper-use-effect-usage-and-cleanup.md", "folders": [ @@ -6470,12 +7444,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "Why does Strict Mode run effects twice in development?" } ], + "seoDescription": "Learn Proper useEffect usage and cleanup for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Proper useEffect usage and cleanup Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Proper useEffect usage and cleanup", "title": "Proper useEffect usage and cleanup", "topic": "Hooks, effects, and custom hooks", "topicFolderPath": "React/hooks-effects-and-custom-hooks" }, { + "canonicalPath": "/content/props-flow-local-state-and-lifting-state-up/", + "canonicalPathSource": "generated", "category": "React", "contentPath": "/src/contents/v1/React/props-flow-local-state-and-lifting-state-up.md", "folders": [ @@ -6510,12 +7490,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How do you decide whether state should be local or lifted?" } ], + "seoDescription": "Learn Props flow, local state, and lifting state up for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Props flow, local state, and lifting state up Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Props flow, local state, and lifting state up", "title": "Props flow, local state, and lifting state up", "topic": "Components, props, state, and rendering behavior", "topicFolderPath": "React/components-props-state-and-rendering-behavior" }, { + "canonicalPath": "/content/query-strategy-and-avoiding-implementation-detail-assertions/", + "canonicalPathSource": "generated", "category": "React", "contentPath": "/src/contents/v1/React/query-strategy-and-avoiding-implementation-detail-assertions.md", "folders": [ @@ -6550,12 +7536,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "Why is getByRole(\"button\", { name: /save/i }) better than getByText(/save/i) for a button?" } ], + "seoDescription": "Learn Query strategy and avoiding implementation-detail assertions for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Query strategy and avoiding implementation-detail assertions Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Query strategy and avoiding implementation-detail assertions", "title": "Query strategy and avoiding implementation-detail assertions", "topic": "Testing, accessibility, and frontend debugging", "topicFolderPath": "React/testing-accessibility-and-frontend-debugging" }, { + "canonicalPath": "/content/react-hook-form-fundamentals-uncontrolled-inputs-controller-and-form-state/", + "canonicalPathSource": "generated", "category": "React", "contentPath": "/src/contents/v1/React/react-hook-form-fundamentals-uncontrolled-inputs-controller-and-form-state.md", "folders": [ @@ -6590,12 +7582,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "When should you use Controller?" } ], + "seoDescription": "Learn React Hook Form fundamentals, uncontrolled inputs, `Controller`, and form state for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "React Hook Form fundamentals, uncontrolled inputs, `Controller`, and form state Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "React Hook Form fundamentals, uncontrolled inputs, `Controller`, and form state", "title": "React Hook Form fundamentals, uncontrolled inputs, `Controller`, and form state", "topic": "Forms, validation, and frontend performance in production", "topicFolderPath": "React/forms-validation-and-frontend-performance-in-production" }, { + "canonicalPath": "/content/reducing-state-complexity-and-avoiding-unnecessary-rerenders-in-form-heavy-pages/", + "canonicalPathSource": "generated", "category": "React", "contentPath": "/src/contents/v1/React/reducing-state-complexity-and-avoiding-unnecessary-rerenders-in-form-heavy-pages.md", "folders": [ @@ -6630,12 +7628,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How should server state and form draft state be separated?" } ], + "seoDescription": "Learn Reducing state complexity and avoiding unnecessary rerenders in form-heavy pages for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Reducing state complexity and avoiding unnecessary rerenders in form-heavy pages Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Reducing state complexity and avoiding unnecessary rerenders in form-heavy pages", "title": "Reducing state complexity and avoiding unnecessary rerenders in form-heavy pages", "topic": "Forms, validation, and frontend performance in production", "topicFolderPath": "React/forms-validation-and-frontend-performance-in-production" }, { + "canonicalPath": "/content/rerender-triggers-derived-state-and-avoiding-duplicated-state/", + "canonicalPathSource": "generated", "category": "React", "contentPath": "/src/contents/v1/React/rerender-triggers-derived-state-and-avoiding-duplicated-state.md", "folders": [ @@ -6670,12 +7674,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "Why should you avoid mirroring props in state?" } ], + "seoDescription": "Learn Rerender triggers, derived state, and avoiding duplicated state for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Rerender triggers, derived state, and avoiding duplicated state Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Rerender triggers, derived state, and avoiding duplicated state", "title": "Rerender triggers, derived state, and avoiding duplicated state", "topic": "Components, props, state, and rendering behavior", "topicFolderPath": "React/components-props-state-and-rendering-behavior" }, { + "canonicalPath": "/content/route-loaders-actions-or-equivalent-data-loading-patterns/", + "canonicalPathSource": "generated", "category": "React", "contentPath": "/src/contents/v1/React/route-loaders-actions-or-equivalent-data-loading-patterns.md", "folders": [ @@ -6710,12 +7720,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "Why are route loaders often better than fetching in useEffect?" } ], + "seoDescription": "Learn Route loaders/actions or equivalent data-loading patterns for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Route loaders/actions or equivalent data-loading patterns Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Route loaders/actions or equivalent data-loading patterns", "title": "Route loaders/actions or equivalent data-loading patterns", "topic": "Routing, forms, and server communication", "topicFolderPath": "React/routing-forms-and-server-communication" }, { + "canonicalPath": "/content/rtk-query-custom-base-queries-including-axios-based-basequery-patterns/", + "canonicalPathSource": "generated", "category": "React", "contentPath": "/src/contents/v1/React/rtk-query-custom-base-queries-including-axios-based-basequery-patterns.md", "folders": [ @@ -6750,12 +7766,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "When should you write a custom base query instead of using fetchBaseQuery?" } ], + "seoDescription": "Learn RTK Query custom base queries, including Axios-based baseQuery patterns for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "RTK Query custom base queries, including Axios-based baseQuery patterns Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "RTK Query custom base queries, including Axios-based baseQuery patterns", "title": "RTK Query custom base queries, including Axios-based baseQuery patterns", "topic": "Production data access, API clients, and frontend auth", "topicFolderPath": "React/production-data-access-api-clients-and-frontend-auth" }, { + "canonicalPath": "/content/secure-cookie-flags-httponly-secure-samesite-path-domain-and-expiration/", + "canonicalPathSource": "generated", "category": "React", "contentPath": "/src/contents/v1/React/secure-cookie-flags-httponly-secure-samesite-path-domain-and-expiration.md", "folders": [ @@ -6790,12 +7812,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "Why is omitting Domain often safer for auth cookies?" } ], + "seoDescription": "Learn Secure cookie flags: `HttpOnly`, `Secure`, `SameSite`, path, domain, and expiration for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Secure cookie flags: `HttpOnly`, `Secure`, `SameSite`, path, domain, and expiration Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Secure cookie flags: `HttpOnly`, `Secure`, `SameSite`, path, domain, and expiration", "title": "Secure cookie flags: `HttpOnly`, `Secure`, `SameSite`, path, domain, and expiration", "topic": "Production data access, API clients, and frontend auth", "topicFolderPath": "React/production-data-access-api-clients-and-frontend-auth" }, { + "canonicalPath": "/content/strict-equality-reference-identity-and-immutability-implications/", + "canonicalPathSource": "generated", "category": "React", "contentPath": "/src/contents/v1/React/strict-equality-reference-identity-and-immutability-implications.md", "folders": [ @@ -6830,12 +7858,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How does React decide whether state or Hook dependencies changed?" } ], + "seoDescription": "Learn Strict equality, reference identity, and immutability implications for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Strict equality, reference identity, and immutability implications Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Strict equality, reference identity, and immutability implications", "title": "Strict equality, reference identity, and immutability implications", "topic": "JavaScript fundamentals", "topicFolderPath": "React/javascript-fundamentals" }, { + "canonicalPath": "/content/suspense-transitions-and-rendering-priority-concepts/", + "canonicalPathSource": "generated", "category": "React", "contentPath": "/src/contents/v1/React/suspense-transitions-and-rendering-priority-concepts.md", "folders": [ @@ -6870,12 +7904,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How can you prevent already visible content from being replaced by a Suspense fallback during navigation?" } ], + "seoDescription": "Learn Suspense, transitions, and rendering priority concepts for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Suspense, transitions, and rendering priority concepts Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Suspense, transitions, and rendering priority concepts", "title": "Suspense, transitions, and rendering priority concepts", "topic": "State management, performance, and rendering optimization", "topicFolderPath": "React/state-management-performance-and-rendering-optimization" }, { + "canonicalPath": "/content/tsconfig-basics-strict-mode-module-settings/", + "canonicalPathSource": "generated", "category": "React", "contentPath": "/src/contents/v1/React/tsconfig-basics-strict-mode-module-settings.md", "folders": [ @@ -6910,12 +7950,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "What is the difference between module and moduleResolution?" } ], + "seoDescription": "Learn tsconfig basics, strict mode, and module settings for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "tsconfig basics, strict mode, and module settings Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "tsconfig basics, strict mode, and module settings", "title": "tsconfig basics, strict mode, and module settings", "topic": "TypeScript for React", "topicFolderPath": "React/typescript-for-react" }, { + "canonicalPath": "/content/types-unions-intersections-discriminated-unions/", + "canonicalPathSource": "generated", "category": "React", "contentPath": "/src/contents/v1/React/types-unions-intersections-discriminated-unions.md", "folders": [ @@ -6950,12 +7996,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How do discriminated unions prevent invalid UI states?" } ], + "seoDescription": "Learn Types, unions, intersections, and discriminated unions for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Types, unions, intersections, and discriminated unions Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Types, unions, intersections, and discriminated unions", "title": "Types, unions, intersections, and discriminated unions", "topic": "TypeScript for React", "topicFolderPath": "React/typescript-for-react" }, { + "canonicalPath": "/content/use-state-use-reducer-use-context-and-custom-hooks/", + "canonicalPathSource": "generated", "category": "React", "contentPath": "/src/contents/v1/React/use-state-use-reducer-use-context-and-custom-hooks.md", "folders": [ @@ -6990,12 +8042,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "When would you choose useReducer instead of useState?" } ], + "seoDescription": "Learn useState, useReducer, useContext, and custom hooks for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "useState, useReducer, useContext, and custom hooks Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "useState, useReducer, useContext, and custom hooks", "title": "useState, useReducer, useContext, and custom hooks", "topic": "Hooks, effects, and custom hooks", "topicFolderPath": "React/hooks-effects-and-custom-hooks" }, { + "canonicalPath": "/content/use-sync-external-store-and-subscribing-to-external-state/", + "canonicalPathSource": "generated", "category": "React", "contentPath": "/src/contents/v1/React/use-sync-external-store-and-subscribing-to-external-state.md", "folders": [ @@ -7030,12 +8088,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "Why should getSnapshot not return a new object every time?" } ], + "seoDescription": "Learn useSyncExternalStore and subscribing to external state for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "useSyncExternalStore and subscribing to external state Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "useSyncExternalStore and subscribing to external state", "title": "useSyncExternalStore and subscribing to external state", "topic": "State management, performance, and rendering optimization", "topicFolderPath": "React/state-management-performance-and-rendering-optimization" }, { + "canonicalPath": "/content/user-centric-testing-with-react-testing-library/", + "canonicalPathSource": "generated", "category": "React", "contentPath": "/src/contents/v1/React/user-centric-testing-with-react-testing-library.md", "folders": [ @@ -7070,12 +8134,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How would you test a form validation flow with React Testing Library?" } ], + "seoDescription": "Learn User-centric testing with React Testing Library for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "User-centric testing with React Testing Library Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "User-centric testing with React Testing Library", "title": "User-centric testing with React Testing Library", "topic": "Testing, accessibility, and frontend debugging", "topicFolderPath": "React/testing-accessibility-and-frontend-debugging" }, { + "canonicalPath": "/content/utility-types-and-conditional-types/", + "canonicalPathSource": "generated", "category": "React", "contentPath": "/src/contents/v1/React/utility-types-and-conditional-types.md", "folders": [ @@ -7110,12 +8180,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "What are conditional types?" } ], + "seoDescription": "Learn Utility types and conditional types for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Utility types and conditional types Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Utility types and conditional types", "title": "Utility types and conditional types", "topic": "TypeScript for React", "topicFolderPath": "React/typescript-for-react" }, { + "canonicalPath": "/content/watch-vs-usewatch-form-render-isolation-and-large-form-performance/", + "canonicalPathSource": "generated", "category": "React", "contentPath": "/src/contents/v1/React/watch-vs-usewatch-form-render-isolation-and-large-form-performance.md", "folders": [ @@ -7150,12 +8226,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "When would you choose watch instead of useWatch?" } ], + "seoDescription": "Learn `watch` vs `useWatch`, form render isolation, and large-form performance for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "`watch` vs `useWatch`, form render isolation, and large-form performance Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "`watch` vs `useWatch`, form render isolation, and large-form performance", "title": "`watch` vs `useWatch`, form render isolation, and large-form performance", "topic": "Forms, validation, and frontend performance in production", "topicFolderPath": "React/forms-validation-and-frontend-performance-in-production" }, { + "canonicalPath": "/content/you-might-not-need-an-effect-patterns/", + "canonicalPathSource": "generated", "category": "React", "contentPath": "/src/contents/v1/React/you-might-not-need-an-effect-patterns.md", "folders": [ @@ -7190,12 +8272,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How would you replace an effect that filters a list into state?" } ], + "seoDescription": "Learn “You might not need an effect” patterns for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "“You might not need an effect” patterns Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "“You might not need an effect” patterns", "title": "“You might not need an effect” patterns", "topic": "Hooks, effects, and custom hooks", "topicFolderPath": "React/hooks-effects-and-custom-hooks" }, { + "canonicalPath": "/content/acid-basics-and-transaction-scope/", + "canonicalPathSource": "generated", "category": "SQL", "contentPath": "/src/contents/v1/SQL/acid-basics-and-transaction-scope.md", "folders": [ @@ -7230,12 +8318,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "Why are nested transactions in SQL Server not independently committed?" } ], + "seoDescription": "Learn ACID basics and transaction scope for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "ACID basics and transaction scope Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "ACID basics and transaction scope", "title": "ACID basics and transaction scope", "topic": "Transactions, isolation, locking, and deadlocks", "topicFolderPath": "SQL/transactions-isolation-locking-and-deadlocks" }, { + "canonicalPath": "/content/backup-types-restore-strategy-and-recovery-objectives/", + "canonicalPathSource": "generated", "category": "SQL", "contentPath": "/src/contents/v1/SQL/backup-types-restore-strategy-and-recovery-objectives.md", "folders": [ @@ -7270,12 +8364,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How do recovery models affect backup strategy?" } ], + "seoDescription": "Learn Backup types, restore strategy, and recovery objectives for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Backup types, restore strategy, and recovery objectives Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Backup types, restore strategy, and recovery objectives", "title": "Backup types, restore strategy, and recovery objectives", "topic": "Backup, recovery, HA/DR, security, and temporal data", "topicFolderPath": "SQL/backup-recovery-ha-dr-security-and-temporal-data" }, { + "canonicalPath": "/content/baselines-workload-comparisons-and-targeted-tuning/", + "canonicalPathSource": "generated", "category": "SQL", "contentPath": "/src/contents/v1/SQL/baselines-workload-comparisons-and-targeted-tuning.md", "folders": [ @@ -7310,12 +8410,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How would you use Query Store to compare performance before and after a deployment?" } ], + "seoDescription": "Learn Baselines, workload comparisons, and targeted tuning for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Baselines, workload comparisons, and targeted tuning Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Baselines, workload comparisons, and targeted tuning", "title": "Baselines, workload comparisons, and targeted tuning", "topic": "Query tuning, Query Store, and plan stability", "topicFolderPath": "SQL/query-tuning-query-store-and-plan-stability" }, { + "canonicalPath": "/content/char-vs-varchar-vs-nvarchar-and-unicode-storage-choices/", + "canonicalPathSource": "generated", "category": "SQL", "contentPath": "/src/contents/v1/SQL/char-vs-varchar-vs-nvarchar-and-unicode-storage-choices.md", "folders": [ @@ -7350,12 +8456,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "Does VARCHAR(50) always mean 50 characters?" } ], + "seoDescription": "Learn CHAR vs VARCHAR vs NVARCHAR and Unicode storage choices for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "CHAR vs VARCHAR vs NVARCHAR and Unicode storage choices Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "CHAR vs VARCHAR vs NVARCHAR and Unicode storage choices", "title": "CHAR vs VARCHAR vs NVARCHAR and Unicode storage choices", "topic": "SQL practical interview comparisons and SQL Server-specific features", "topicFolderPath": "SQL/sql-practical-interview-comparisons-and-sql-server-specific-features" }, { + "canonicalPath": "/content/clustered-vs-nonclustered-indexes/", + "canonicalPathSource": "generated", "category": "SQL", "contentPath": "/src/contents/v1/SQL/clustered-vs-nonclustered-indexes.md", "folders": [ @@ -7390,12 +8502,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "Why should clustered index keys usually be narrow and stable?" } ], + "seoDescription": "Learn Clustered vs nonclustered indexes for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Clustered vs nonclustered indexes Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Clustered vs nonclustered indexes", "title": "Clustered vs nonclustered indexes", "topic": "Indexes, statistics, and execution plans", "topicFolderPath": "SQL/indexes-statistics-and-execution-plans" }, { + "canonicalPath": "/content/computed-columns-persisted-computed-columns-and-indexed-views/", + "canonicalPathSource": "generated", "category": "SQL", "contentPath": "/src/contents/v1/SQL/computed-columns-persisted-computed-columns-and-indexed-views.md", "folders": [ @@ -7430,12 +8548,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "What requirements matter when indexing a computed column?" } ], + "seoDescription": "Learn Computed columns, persisted computed columns, and indexed views for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Computed columns, persisted computed columns, and indexed views Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Computed columns, persisted computed columns, and indexed views", "title": "Computed columns, persisted computed columns, and indexed views", "topic": "SQL practical interview comparisons and SQL Server-specific features", "topicFolderPath": "SQL/sql-practical-interview-comparisons-and-sql-server-specific-features" }, { + "canonicalPath": "/content/data-types-nullability-and-business-rule-enforcement/", + "canonicalPathSource": "generated", "category": "SQL", "contentPath": "/src/contents/v1/SQL/data-types-nullability-and-business-rule-enforcement.md", "folders": [ @@ -7470,12 +8594,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "Why should money usually use DECIMAL instead of FLOAT?" } ], + "seoDescription": "Learn Data types, nullability, and business-rule enforcement for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Data types, nullability, and business-rule enforcement Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Data types, nullability, and business-rule enforcement", "title": "Data types, nullability, and business-rule enforcement", "topic": "Relational modeling and normalization", "topicFolderPath": "SQL/relational-modeling-and-normalization" }, { + "canonicalPath": "/content/ddl-vs-dml-vs-dcl-and-how-schema-data-and-permissions-changes-differ/", + "canonicalPathSource": "generated", "category": "SQL", "contentPath": "/src/contents/v1/SQL/ddl-vs-dml-vs-dcl-and-how-schema-data-and-permissions-changes-differ.md", "folders": [ @@ -7510,12 +8640,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "Why is TRUNCATE TABLE often classified with DDL even though it removes data?" } ], + "seoDescription": "Learn DDL vs DML vs DCL and how schema, data, and permissions changes differ for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "DDL vs DML vs DCL and how schema, data, and permissions changes differ Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "DDL vs DML vs DCL and how schema, data, and permissions changes differ", "title": "DDL vs DML vs DCL and how schema, data, and permissions changes differ", "topic": "SQL practical interview comparisons and SQL Server-specific features", "topicFolderPath": "SQL/sql-practical-interview-comparisons-and-sql-server-specific-features" }, { + "canonicalPath": "/content/deadlocks-detection-and-mitigation/", + "canonicalPathSource": "generated", "category": "SQL", "contentPath": "/src/contents/v1/SQL/deadlocks-detection-and-mitigation.md", "folders": [ @@ -7550,12 +8686,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How does SQL Server choose a deadlock victim?" } ], + "seoDescription": "Learn Deadlocks, detection, and mitigation for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Deadlocks, detection, and mitigation Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Deadlocks, detection, and mitigation", "title": "Deadlocks, detection, and mitigation", "topic": "Transactions, isolation, locking, and deadlocks", "topicFolderPath": "SQL/transactions-isolation-locking-and-deadlocks" }, { + "canonicalPath": "/content/delete-vs-truncate-including-logging-identity-reset-rollback-behavior-and-foreign-key-limitations/", + "canonicalPathSource": "generated", "category": "SQL", "contentPath": "/src/contents/v1/SQL/delete-vs-truncate-including-logging-identity-reset-rollback-behavior-and-foreign-key-limitations.md", "folders": [ @@ -7590,12 +8732,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "Why is TRUNCATE TABLE usually faster than DELETE for clearing a table?" } ], + "seoDescription": "Learn DELETE vs TRUNCATE, including logging, identity reset, rollback behavior, and foreign key limitations for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "DELETE vs TRUNCATE, including logging, identity reset, rollback behavior, and foreign key limitations Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "DELETE vs TRUNCATE, including logging, identity reset, rollback behavior, and foreign key limitations", "title": "DELETE vs TRUNCATE, including logging, identity reset, rollback behavior, and foreign key limitations", "topic": "SQL practical interview comparisons and SQL Server-specific features", "topicFolderPath": "SQL/sql-practical-interview-comparisons-and-sql-server-specific-features" }, { + "canonicalPath": "/content/estimated-vs-actual-execution-plans/", + "canonicalPathSource": "generated", "category": "SQL", "contentPath": "/src/contents/v1/SQL/estimated-vs-actual-execution-plans.md", "folders": [ @@ -7630,12 +8778,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "Are scans always bad in an execution plan?" } ], + "seoDescription": "Learn Estimated vs actual execution plans for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Estimated vs actual execution plans Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Estimated vs actual execution plans", "title": "Estimated vs actual execution plans", "topic": "Indexes, statistics, and execution plans", "topicFolderPath": "SQL/indexes-statistics-and-execution-plans" }, { + "canonicalPath": "/content/group-by-and-aggregate-functions/", + "canonicalPathSource": "generated", "category": "SQL", "contentPath": "/src/contents/v1/SQL/group-by-and-aggregate-functions.md", "folders": [ @@ -7670,12 +8824,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "Why does SQL require selected columns to be grouped or aggregated?" } ], + "seoDescription": "Learn GROUP BY and aggregate functions for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "GROUP BY and aggregate functions Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "GROUP BY and aggregate functions", "title": "GROUP BY and aggregate functions", "topic": "Core querying and data retrieval", "topicFolderPath": "SQL/core-querying-and-data-retrieval" }, { + "canonicalPath": "/content/high-availability-and-disaster-recovery-basics/", + "canonicalPathSource": "generated", "category": "SQL", "contentPath": "/src/contents/v1/SQL/high-availability-and-disaster-recovery-basics.md", "folders": [ @@ -7710,12 +8870,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "What is the difference between synchronous and asynchronous commit?" } ], + "seoDescription": "Learn High availability and disaster recovery basics for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "High availability and disaster recovery basics Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "High availability and disaster recovery basics", "title": "High availability and disaster recovery basics", "topic": "Backup, recovery, HA/DR, security, and temporal data", "topicFolderPath": "SQL/backup-recovery-ha-dr-security-and-temporal-data" }, { + "canonicalPath": "/content/inner-left-other-join-patterns/", + "canonicalPathSource": "generated", "category": "SQL", "contentPath": "/src/contents/v1/SQL/inner-left-other-join-patterns.md", "folders": [ @@ -7750,12 +8916,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "Why can a LEFT JOIN accidentally behave like an INNER JOIN?" } ], + "seoDescription": "Learn INNER, LEFT, and other join patterns for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "INNER, LEFT, and other join patterns Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "INNER, LEFT, and other join patterns", "title": "INNER, LEFT, and other join patterns", "topic": "Relational modeling and normalization", "topicFolderPath": "SQL/relational-modeling-and-normalization" }, { + "canonicalPath": "/content/isolation-levels-and-row-versioning/", + "canonicalPathSource": "generated", "category": "SQL", "contentPath": "/src/contents/v1/SQL/isolation-levels-and-row-versioning.md", "folders": [ @@ -7790,12 +8962,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How does READ COMMITTED differ with READCOMMITTEDSNAPSHOT ON?" } ], + "seoDescription": "Learn Isolation levels and row versioning for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Isolation levels and row versioning Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Isolation levels and row versioning", "title": "Isolation levels and row versioning", "topic": "Transactions, isolation, locking, and deadlocks", "topicFolderPath": "SQL/transactions-isolation-locking-and-deadlocks" }, { + "canonicalPath": "/content/knowing-when-to-change-sql-indexes-or-schema-instead-of-forcing-hints/", + "canonicalPathSource": "generated", "category": "SQL", "contentPath": "/src/contents/v1/SQL/knowing-when-to-change-sql-indexes-or-schema-instead-of-forcing-hints.md", "folders": [ @@ -7830,12 +9008,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How do you decide whether a slow query needs SQL rewrite, index change, statistics update, or a hint?" } ], + "seoDescription": "Learn Knowing when to change SQL, indexes, or schema instead of forcing hints for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Knowing when to change SQL, indexes, or schema instead of forcing hints Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Knowing when to change SQL, indexes, or schema instead of forcing hints", "title": "Knowing when to change SQL, indexes, or schema instead of forcing hints", "topic": "Query tuning, Query Store, and plan stability", "topicFolderPath": "SQL/query-tuning-query-store-and-plan-stability" }, { + "canonicalPath": "/content/least-privilege-roles-row-level-security-and-masking/", + "canonicalPathSource": "generated", "category": "SQL", "contentPath": "/src/contents/v1/SQL/least-privilege-roles-row-level-security-and-masking.md", "folders": [ @@ -7870,12 +9054,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "Why should applications avoid using dbowner?" } ], + "seoDescription": "Learn Least privilege, roles, row-level security, and masking for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Least privilege, roles, row-level security, and masking Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Least privilege, roles, row-level security, and masking", "title": "Least privilege, roles, row-level security, and masking", "topic": "Backup, recovery, HA/DR, security, and temporal data", "topicFolderPath": "SQL/backup-recovery-ha-dr-security-and-temporal-data" }, { + "canonicalPath": "/content/local-vs-global-temporary-tables-and-table-variables/", + "canonicalPathSource": "generated", "category": "SQL", "contentPath": "/src/contents/v1/SQL/local-vs-global-temporary-tables-and-table-variables.md", "folders": [ @@ -7910,12 +9100,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How do temp tables and table variables differ for query optimization?" } ], + "seoDescription": "Learn Local vs global temporary tables and table variables for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Local vs global temporary tables and table variables Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Local vs global temporary tables and table variables", "title": "Local vs global temporary tables and table variables", "topic": "SQL practical interview comparisons and SQL Server-specific features", "topicFolderPath": "SQL/sql-practical-interview-comparisons-and-sql-server-specific-features" }, { + "canonicalPath": "/content/locking-behavior-and-blocking/", + "canonicalPathSource": "generated", "category": "SQL", "contentPath": "/src/contents/v1/SQL/locking-behavior-and-blocking.md", "folders": [ @@ -7950,12 +9146,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How do you find the head blocker?" } ], + "seoDescription": "Learn Locking behavior and blocking for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Locking behavior and blocking Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Locking behavior and blocking", "title": "Locking behavior and blocking", "topic": "Transactions, isolation, locking, and deadlocks", "topicFolderPath": "SQL/transactions-isolation-locking-and-deadlocks" }, { + "canonicalPath": "/content/merge-and-upsert-patterns-including-concurrency-cautions/", + "canonicalPathSource": "generated", "category": "SQL", "contentPath": "/src/contents/v1/SQL/merge-and-upsert-patterns-including-concurrency-cautions.md", "folders": [ @@ -7990,12 +9192,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "Why can MERGE be risky with duplicate source rows?" } ], + "seoDescription": "Learn MERGE and upsert patterns, including concurrency cautions for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "MERGE and upsert patterns, including concurrency cautions Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "MERGE and upsert patterns, including concurrency cautions", "title": "MERGE and upsert patterns, including concurrency cautions", "topic": "SQL practical interview comparisons and SQL Server-specific features", "topicFolderPath": "SQL/sql-practical-interview-comparisons-and-sql-server-specific-features" }, { + "canonicalPath": "/content/normalization-and-when-denormalization-is-justified/", + "canonicalPathSource": "generated", "category": "SQL", "contentPath": "/src/contents/v1/SQL/normalization-and-when-denormalization-is-justified.md", "folders": [ @@ -8030,12 +9238,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How do second and third normal form differ?" } ], + "seoDescription": "Learn Normalization and when denormalization is justified for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Normalization and when denormalization is justified Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Normalization and when denormalization is justified", "title": "Normalization and when denormalization is justified", "topic": "Relational modeling and normalization", "topicFolderPath": "SQL/relational-modeling-and-normalization" }, { + "canonicalPath": "/content/null-handling-and-common-filtering-mistakes/", + "canonicalPathSource": "generated", "category": "SQL", "contentPath": "/src/contents/v1/SQL/null-handling-and-common-filtering-mistakes.md", "folders": [ @@ -8070,12 +9284,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "Why can NOT IN return no rows when the subquery contains NULL?" } ], + "seoDescription": "Learn NULL handling and common filtering mistakes for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "NULL handling and common filtering mistakes Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "NULL handling and common filtering mistakes", "title": "NULL handling and common filtering mistakes", "topic": "Core querying and data retrieval", "topicFolderPath": "SQL/core-querying-and-data-retrieval" }, { + "canonicalPath": "/content/one-to-one-one-to-many-many-to-many-relationships/", + "canonicalPathSource": "generated", "category": "SQL", "contentPath": "/src/contents/v1/SQL/one-to-one-one-to-many-many-to-many-relationships.md", "folders": [ @@ -8110,12 +9330,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How do you implement a 1-to-1 relationship in SQL?" } ], + "seoDescription": "Learn One-to-one, one-to-many, and many-to-many relationships for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "One-to-one, one-to-many, and many-to-many relationships Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "One-to-one, one-to-many, and many-to-many relationships", "title": "One-to-one, one-to-many, and many-to-many relationships", "topic": "Core querying and data retrieval", "topicFolderPath": "SQL/core-querying-and-data-retrieval" }, { + "canonicalPath": "/content/over-partition-by-row-number-rank-and-running-aggregates/", + "canonicalPathSource": "generated", "category": "SQL", "contentPath": "/src/contents/v1/SQL/over-partition-by-row-number-rank-and-running-aggregates.md", "folders": [ @@ -8150,12 +9376,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How do you calculate a running total in SQL Server?" } ], + "seoDescription": "Learn OVER, PARTITION BY, ROW_NUMBER, RANK, and running aggregates for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "OVER, PARTITION BY, ROW_NUMBER, RANK, and running aggregates Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "OVER, PARTITION BY, ROW_NUMBER, RANK, and running aggregates", "title": "OVER, PARTITION BY, ROW_NUMBER, RANK, and running aggregates", "topic": "Advanced querying with window functions and CTEs", "topicFolderPath": "SQL/advanced-querying-with-window-functions-and-ctes" }, { + "canonicalPath": "/content/parameter-sensitive-plans-and-skewed-data-distributions/", + "canonicalPathSource": "generated", "category": "SQL", "contentPath": "/src/contents/v1/SQL/parameter-sensitive-plans-and-skewed-data-distributions.md", "folders": [ @@ -8190,12 +9422,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How does parameter sniffing interact with plan reuse?" } ], + "seoDescription": "Learn Parameter-sensitive plans and skewed data distributions for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Parameter-sensitive plans and skewed data distributions Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Parameter-sensitive plans and skewed data distributions", "title": "Parameter-sensitive plans and skewed data distributions", "topic": "Query tuning, Query Store, and plan stability", "topicFolderPath": "SQL/query-tuning-query-store-and-plan-stability" }, { + "canonicalPath": "/content/primary-key-vs-unique-constraint-candidate-keys-foreign-keys-and-constraint-design/", + "canonicalPathSource": "generated", "category": "SQL", "contentPath": "/src/contents/v1/SQL/primary-key-vs-unique-constraint-candidate-keys-foreign-keys-and-constraint-design.md", "folders": [ @@ -8230,12 +9468,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "Why use a surrogate primary key plus a unique business key?" } ], + "seoDescription": "Learn Primary key vs unique constraint, candidate keys, foreign keys, and constraint design for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Primary key vs unique constraint, candidate keys, foreign keys, and constraint design Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Primary key vs unique constraint, candidate keys, foreign keys, and constraint design", "title": "Primary key vs unique constraint, candidate keys, foreign keys, and constraint design", "topic": "SQL practical interview comparisons and SQL Server-specific features", "topicFolderPath": "SQL/sql-practical-interview-comparisons-and-sql-server-specific-features" }, { + "canonicalPath": "/content/primary-keys-foreign-keys-constraints/", + "canonicalPathSource": "generated", "category": "SQL", "contentPath": "/src/contents/v1/SQL/primary-keys-foreign-keys-constraints.md", "folders": [ @@ -8270,12 +9514,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "Does a foreign key automatically create an index?" } ], + "seoDescription": "Learn Primary keys, foreign keys, and constraints for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Primary keys, foreign keys, and constraints Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Primary keys, foreign keys, and constraints", "title": "Primary keys, foreign keys, and constraints", "topic": "Core querying and data retrieval", "topicFolderPath": "SQL/core-querying-and-data-retrieval" }, { + "canonicalPath": "/content/query-store-for-regression-detection-and-plan-history/", + "canonicalPathSource": "generated", "category": "SQL", "contentPath": "/src/contents/v1/SQL/query-store-for-regression-detection-and-plan-history.md", "folders": [ @@ -8310,12 +9560,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How would you use Query Store to find a regressed query?" } ], + "seoDescription": "Learn Query Store for regression detection and plan history for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Query Store for regression detection and plan history Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Query Store for regression detection and plan history", "title": "Query Store for regression detection and plan history", "topic": "Query tuning, Query Store, and plan stability", "topicFolderPath": "SQL/query-tuning-query-store-and-plan-stability" }, { + "canonicalPath": "/content/replacing-brittle-subqueries-with-clearer-query-structure/", + "canonicalPathSource": "generated", "category": "SQL", "contentPath": "/src/contents/v1/SQL/replacing-brittle-subqueries-with-clearer-query-structure.md", "folders": [ @@ -8350,12 +9606,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "Why can NOT IN be dangerous with subqueries?" } ], + "seoDescription": "Learn Replacing brittle subqueries with clearer query structure for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Replacing brittle subqueries with clearer query structure Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Replacing brittle subqueries with clearer query structure", "title": "Replacing brittle subqueries with clearer query structure", "topic": "Advanced querying with window functions and CTEs", "topicFolderPath": "SQL/advanced-querying-with-window-functions-and-ctes" }, { + "canonicalPath": "/content/schema-migrations-source-control-reviewable-sql-scripts-and-release-safety/", + "canonicalPathSource": "generated", "category": "SQL", "contentPath": "/src/contents/v1/SQL/schema-migrations-source-control-reviewable-sql-scripts-and-release-safety.md", "folders": [ @@ -8390,12 +9652,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "What is the difference between migration-based and state-based database deployment?" } ], + "seoDescription": "Learn Schema migrations, source control, reviewable SQL scripts, and release safety for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Schema migrations, source control, reviewable SQL scripts, and release safety Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Schema migrations, source control, reviewable SQL scripts, and release safety", "title": "Schema migrations, source control, reviewable SQL scripts, and release safety", "topic": "Database programmability and schema evolution", "topicFolderPath": "SQL/database-programmability-and-schema-evolution" }, { + "canonicalPath": "/content/select-where-order-by-top-offset-fetch/", + "canonicalPathSource": "generated", "category": "SQL", "contentPath": "/src/contents/v1/SQL/select-where-order-by-top-offset-fetch.md", "folders": [ @@ -8430,12 +9698,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "Why should TOP usually be used with ORDER BY?" } ], + "seoDescription": "Learn SELECT, WHERE, ORDER BY, TOP/OFFSET-FETCH for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "SELECT, WHERE, ORDER BY, TOP/OFFSET-FETCH Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "SELECT, WHERE, ORDER BY, TOP/OFFSET-FETCH", "title": "SELECT, WHERE, ORDER BY, TOP/OFFSET-FETCH", "topic": "Relational modeling and normalization", "topicFolderPath": "SQL/relational-modeling-and-normalization" }, { + "canonicalPath": "/content/selectivity-covering-indexes-and-index-maintenance-trade-offs/", + "canonicalPathSource": "generated", "category": "SQL", "contentPath": "/src/contents/v1/SQL/selectivity-covering-indexes-and-index-maintenance-trade-offs.md", "folders": [ @@ -8470,12 +9744,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How do key columns differ from included columns?" } ], + "seoDescription": "Learn Selectivity, covering indexes, and index maintenance trade-offs for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Selectivity, covering indexes, and index maintenance trade-offs Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Selectivity, covering indexes, and index maintenance trade-offs", "title": "Selectivity, covering indexes, and index maintenance trade-offs", "topic": "Indexes, statistics, and execution plans", "topicFolderPath": "SQL/indexes-statistics-and-execution-plans" }, { + "canonicalPath": "/content/standard-and-recursive-ctes/", + "canonicalPathSource": "generated", "category": "SQL", "contentPath": "/src/contents/v1/SQL/standard-and-recursive-ctes.md", "folders": [ @@ -8510,12 +9790,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "Are CTEs materialized in SQL Server?" } ], + "seoDescription": "Learn Standard and recursive CTEs for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Standard and recursive CTEs Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Standard and recursive CTEs", "title": "Standard and recursive CTEs", "topic": "Advanced querying with window functions and CTEs", "topicFolderPath": "SQL/advanced-querying-with-window-functions-and-ctes" }, { + "canonicalPath": "/content/statistics-and-how-the-optimizer-uses-them/", + "canonicalPathSource": "generated", "category": "SQL", "contentPath": "/src/contents/v1/SQL/statistics-and-how-the-optimizer-uses-them.md", "folders": [ @@ -8550,12 +9836,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How does the optimizer use statistics to choose a plan?" } ], + "seoDescription": "Learn Statistics and how the optimizer uses them for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Statistics and how the optimizer uses them Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Statistics and how the optimizer uses them", "title": "Statistics and how the optimizer uses them", "topic": "Indexes, statistics, and execution plans", "topicFolderPath": "SQL/indexes-statistics-and-execution-plans" }, { + "canonicalPath": "/content/stored-procedure-transaction-patterns-with-try-catch-output-parameters-and-error-handling/", + "canonicalPathSource": "generated", "category": "SQL", "contentPath": "/src/contents/v1/SQL/stored-procedure-transaction-patterns-with-try-catch-output-parameters-and-error-handling.md", "folders": [ @@ -8590,12 +9882,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "What is XACTSTATE used for?" } ], + "seoDescription": "Learn Stored procedure transaction patterns with `TRY...CATCH`, output parameters, and error handling for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Stored procedure transaction patterns with `TRY...CATCH`, output parameters, and error handling Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Stored procedure transaction patterns with `TRY...CATCH`, output parameters, and error handling", "title": "Stored procedure transaction patterns with `TRY...CATCH`, output parameters, and error handling", "topic": "SQL practical interview comparisons and SQL Server-specific features", "topicFolderPath": "SQL/sql-practical-interview-comparisons-and-sql-server-specific-features" }, { + "canonicalPath": "/content/stored-procedures-output-parameters-and-encapsulated-database-logic/", + "canonicalPathSource": "generated", "category": "SQL", "contentPath": "/src/contents/v1/SQL/stored-procedures-output-parameters-and-encapsulated-database-logic.md", "folders": [ @@ -8630,12 +9928,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "What are the benefits and drawbacks of encapsulating logic in stored procedures?" } ], + "seoDescription": "Learn Stored procedures, output parameters, and encapsulated database logic for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Stored procedures, output parameters, and encapsulated database logic Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Stored procedures, output parameters, and encapsulated database logic", "title": "Stored procedures, output parameters, and encapsulated database logic", "topic": "Database programmability and schema evolution", "topicFolderPath": "SQL/database-programmability-and-schema-evolution" }, { + "canonicalPath": "/content/temporal-or-reporting-style-query-patterns/", + "canonicalPathSource": "generated", "category": "SQL", "contentPath": "/src/contents/v1/SQL/temporal-or-reporting-style-query-patterns.md", "folders": [ @@ -8670,12 +9974,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How do system-versioned temporal tables differ from event tables?" } ], + "seoDescription": "Learn Temporal or reporting-style query patterns for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Temporal or reporting-style query patterns Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Temporal or reporting-style query patterns", "title": "Temporal or reporting-style query patterns", "topic": "Advanced querying with window functions and CTEs", "topicFolderPath": "SQL/advanced-querying-with-window-functions-and-ctes" }, { + "canonicalPath": "/content/temporal-tables-historical-retention-and-lifecycle-management/", + "canonicalPathSource": "generated", "category": "SQL", "contentPath": "/src/contents/v1/SQL/temporal-tables-historical-retention-and-lifecycle-management.md", "folders": [ @@ -8710,12 +10020,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "How do temporal tables differ from audit tables?" } ], + "seoDescription": "Learn Temporal tables, historical retention, and lifecycle management for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Temporal tables, historical retention, and lifecycle management Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Temporal tables, historical retention, and lifecycle management", "title": "Temporal tables, historical retention, and lifecycle management", "topic": "Backup, recovery, HA/DR, security, and temporal data", "topicFolderPath": "SQL/backup-recovery-ha-dr-security-and-temporal-data" }, { + "canonicalPath": "/content/triggers-and-their-trade-offs/", + "canonicalPathSource": "generated", "category": "SQL", "contentPath": "/src/contents/v1/SQL/triggers-and-their-trade-offs.md", "folders": [ @@ -8750,12 +10066,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "When should you use a trigger instead of a constraint?" } ], + "seoDescription": "Learn Triggers and their trade-offs for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Triggers and their trade-offs Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Triggers and their trade-offs", "title": "Triggers and their trade-offs", "topic": "Database programmability and schema evolution", "topicFolderPath": "SQL/database-programmability-and-schema-evolution" }, { + "canonicalPath": "/content/union-vs-union-all-except-intersect-and-duplicate-handling/", + "canonicalPathSource": "generated", "category": "SQL", "contentPath": "/src/contents/v1/SQL/union-vs-union-all-except-intersect-and-duplicate-handling.md", "folders": [ @@ -8790,12 +10112,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "Why can UNION produce incorrect reports for transaction data?" } ], + "seoDescription": "Learn UNION vs UNION ALL, EXCEPT, INTERSECT, and duplicate handling for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "UNION vs UNION ALL, EXCEPT, INTERSECT, and duplicate handling Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "UNION vs UNION ALL, EXCEPT, INTERSECT, and duplicate handling", "title": "UNION vs UNION ALL, EXCEPT, INTERSECT, and duplicate handling", "topic": "SQL practical interview comparisons and SQL Server-specific features", "topicFolderPath": "SQL/sql-practical-interview-comparisons-and-sql-server-specific-features" }, { + "canonicalPath": "/content/views-and-user-defined-functions/", + "canonicalPathSource": "generated", "category": "SQL", "contentPath": "/src/contents/v1/SQL/views-and-user-defined-functions.md", "folders": [ @@ -8830,12 +10158,18 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "What is SCHEMABINDING and why would you use it?" } ], + "seoDescription": "Learn Views and user-defined functions for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "Views and user-defined functions Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "Views and user-defined functions", "title": "Views and user-defined functions", "topic": "Database programmability and schema evolution", "topicFolderPath": "SQL/database-programmability-and-schema-evolution" }, { + "canonicalPath": "/content/where-vs-having-and-filtering-before-vs-after-aggregation/", + "canonicalPathSource": "generated", "category": "SQL", "contentPath": "/src/contents/v1/SQL/where-vs-having-and-filtering-before-vs-after-aggregation.md", "folders": [ @@ -8870,6 +10204,10 @@ export const curriculumManifest: CurriculumManifestEntry[] = [ "question": "Why is using HAVING Status = 'Completed' usually worse than using WHERE Status = 'Completed'?" } ], + "seoDescription": "Learn WHERE vs HAVING and filtering before vs after aggregation for technical interviews, including core concepts, practical examples, common mistakes, and interview questions.", + "seoDescriptionSource": "fallback", + "seoTitle": "WHERE vs HAVING and filtering before vs after aggregation Interview Questions | DEV_NET_CORE", + "seoTitleSource": "fallback", "subtopic": "WHERE vs HAVING and filtering before vs after aggregation", "title": "WHERE vs HAVING and filtering before vs after aggregation", "topic": "SQL practical interview comparisons and SQL Server-specific features", diff --git a/src/contents/v1/NET/async-and-await-semantics-in-csharp.md b/src/contents/v1/NET/async-and-await-semantics-in-csharp.md index 03b0322..2a61756 100644 --- a/src/contents/v1/NET/async-and-await-semantics-in-csharp.md +++ b/src/contents/v1/NET/async-and-await-semantics-in-csharp.md @@ -3,6 +3,9 @@ id: async-and-await-semantics-in-csharp topic: Async programming, tasks, cancellation, and concurrency subtopic: Async and Await Semantics category: .NET +seoTitle: Async and Await in C# Interview Questions +seoDescription: Learn async and await in C# for technical interviews, including Task, cancellation, exception handling, and common deadlock risks. +canonicalPath: /content/async-and-await-semantics-in-csharp/ --- ## Overview From 282f0a6ebb9975b9b80570c97abde2aa4cd0a7e7 Mon Sep 17 00:00:00 2001 From: Minh Vu Date: Sat, 27 Jun 2026 09:11:08 +0700 Subject: [PATCH 04/20] feat: add shared SEO route config --- shared/seo/seoRoutes.d.mts | 15 ++++ shared/seo/seoRoutes.mjs | 159 +++++++++++++++++++++++++++++++++++++ 2 files changed, 174 insertions(+) create mode 100644 shared/seo/seoRoutes.d.mts create mode 100644 shared/seo/seoRoutes.mjs diff --git a/shared/seo/seoRoutes.d.mts b/shared/seo/seoRoutes.d.mts new file mode 100644 index 0000000..e60618c --- /dev/null +++ b/shared/seo/seoRoutes.d.mts @@ -0,0 +1,15 @@ +export type StaticSeoRoute = Readonly<{ + canonicalPath: string; + index: boolean; + path: string; + prerender: boolean; + sitemap: boolean; +}>; + +export const staticSeoRoutes: readonly StaticSeoRoute[]; + +export function getStaticSeoRoute(path: string): StaticSeoRoute | undefined; + +export function validateStaticSeoRoutes( + routes: readonly StaticSeoRoute[] +): void; diff --git a/shared/seo/seoRoutes.mjs b/shared/seo/seoRoutes.mjs new file mode 100644 index 0000000..38b174b --- /dev/null +++ b/shared/seo/seoRoutes.mjs @@ -0,0 +1,159 @@ +const routeDefinitions = [ + { + path: "/", + canonicalPath: "/", + index: true, + sitemap: true, + prerender: true, + }, + { + path: "/home/", + canonicalPath: "/", + index: false, + sitemap: false, + prerender: false, + }, + { + path: "/content/", + canonicalPath: "/content/", + index: true, + sitemap: true, + prerender: true, + }, + { + path: "/practice/", + canonicalPath: "/practice/", + index: false, + sitemap: false, + prerender: false, + }, + { + path: "/simulation/", + canonicalPath: "/simulation/", + index: false, + sitemap: false, + prerender: false, + }, + { + path: "/simulation/setup/", + canonicalPath: "/simulation/setup/", + index: false, + sitemap: false, + prerender: false, + }, + { + path: "/roadmap/", + canonicalPath: "/roadmap/", + index: true, + sitemap: true, + prerender: true, + }, + { + path: "/about-us/", + canonicalPath: "/about-us/", + index: true, + sitemap: true, + prerender: true, + }, + { + path: "/changelog/", + canonicalPath: "/changelog/", + index: false, + sitemap: false, + prerender: false, + }, + { + path: "/bug-report/", + canonicalPath: "/bug-report/", + index: false, + sitemap: false, + prerender: false, + }, + { + path: "/privacy/", + canonicalPath: "/privacy/", + index: true, + sitemap: true, + prerender: true, + }, + { + path: "/terms/", + canonicalPath: "/terms/", + index: true, + sitemap: true, + prerender: true, + }, +]; + +export function validateStaticSeoRoutes(routes) { + if (!Array.isArray(routes)) { + throw new TypeError("Static SEO routes must be an array."); + } + + const seenPaths = new Set(); + + routes.forEach((route, index) => { + const label = route?.path || `entry ${index}`; + + if (!route || typeof route !== "object" || Array.isArray(route)) { + throw new TypeError(`Static SEO route ${label} must be an object.`); + } + + if (!isCanonicalPath(route.path)) { + throw new Error( + `Static SEO route path "${route.path}" must be "/" or a lowercase trailing-slash path.` + ); + } + + if (seenPaths.has(route.path)) { + throw new Error(`Duplicate static SEO route path "${route.path}".`); + } + + seenPaths.add(route.path); + + if (!isCanonicalPath(route.canonicalPath)) { + throw new Error( + `Canonical path "${route.canonicalPath}" for ${label} must be "/" or a lowercase trailing-slash path.` + ); + } + + for (const flag of ["index", "sitemap", "prerender"]) { + if (typeof route[flag] !== "boolean") { + throw new TypeError(`Static SEO route ${label} must define boolean ${flag}.`); + } + } + + if (route.index && route.canonicalPath !== route.path) { + throw new Error(`Indexable static SEO route ${label} must be self-canonical.`); + } + + if ( + route.sitemap && + (!route.index || + !route.prerender || + route.canonicalPath !== route.path) + ) { + throw new Error( + `Sitemap route ${label} must be indexable, pre-rendered, and self-canonical.` + ); + } + }); +} + +function isCanonicalPath(value) { + return ( + value === "/" || + (typeof value === "string" && + /^\/(?:[a-z0-9]+(?:-[a-z0-9]+)*\/)+$/.test(value)) + ); +} + +validateStaticSeoRoutes(routeDefinitions); + +export const staticSeoRoutes = Object.freeze( + routeDefinitions.map((route) => Object.freeze({ ...route })) +); + +export function getStaticSeoRoute(path) { + return staticSeoRoutes.find((route) => route.path === path); +} From 62b9d90e12ca89a0f259c6d0df5e1a55c68e4828 Mon Sep 17 00:00:00 2001 From: Minh Vu Date: Sat, 27 Jun 2026 09:18:43 +0700 Subject: [PATCH 05/20] feat: add shared SEO metadata builder --- package.json | 1 + shared/seo/buildSeoMetadata.d.mts | 53 +++++++ shared/seo/buildSeoMetadata.mjs | 206 +++++++++++++++++++++++++++ shared/seo/buildSeoMetadata.test.mjs | 123 ++++++++++++++++ shared/seo/seoRoutes.d.mts | 2 + shared/seo/seoRoutes.mjs | 63 ++++++++ 6 files changed, 448 insertions(+) create mode 100644 shared/seo/buildSeoMetadata.d.mts create mode 100644 shared/seo/buildSeoMetadata.mjs create mode 100644 shared/seo/buildSeoMetadata.test.mjs diff --git a/package.json b/package.json index ac1010f..ba54c77 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "prebuild": "npm run generate:curriculum", "build": "tsc -b && vite build", "lint": "eslint .", + "test:seo": "node --test shared/seo/*.test.mjs", "preview": "vite preview" }, "dependencies": { diff --git a/shared/seo/buildSeoMetadata.d.mts b/shared/seo/buildSeoMetadata.d.mts new file mode 100644 index 0000000..e282be6 --- /dev/null +++ b/shared/seo/buildSeoMetadata.d.mts @@ -0,0 +1,53 @@ +export type SeoContentEntry = Readonly<{ + canonicalPath: string; + id: string; + seoDescription: string; + seoTitle: string; + subtopic?: string; + title?: string; +}>; + +export type OpenGraphMetadata = Readonly<{ + description: string; + image: string; + imageAlt: string; + imageHeight: number; + imageWidth: number; + title: string; + type: "website"; + url: string | null; +}>; + +export type TwitterMetadata = Readonly<{ + card: "summary_large_image"; + description: string; + image: string; + title: string; +}>; + +export type SeoMetadata = Readonly<{ + canonicalPath: string | null; + canonicalUrl: string | null; + description: string; + index: boolean; + openGraph: OpenGraphMetadata; + robots: "index,follow" | "noindex,follow"; + title: string; + twitter: TwitterMetadata; +}>; + +export type BuildSeoMetadataInput = Readonly<{ + contentEntry?: SeoContentEntry; + pathname: string; +}>; + +export const SEO_SITE_NAME: "DEV_NET_CORE"; +export const SEO_SITE_ORIGIN: "https://www.dev-net-core.com"; +export const DEFAULT_OG_IMAGE_URL: string; +export const DEFAULT_OG_IMAGE_WIDTH: 1200; +export const DEFAULT_OG_IMAGE_HEIGHT: 630; +export const DEFAULT_OG_IMAGE_ALT: string; + +export function buildSeoMetadata( + input: BuildSeoMetadataInput +): SeoMetadata; diff --git a/shared/seo/buildSeoMetadata.mjs b/shared/seo/buildSeoMetadata.mjs new file mode 100644 index 0000000..8322586 --- /dev/null +++ b/shared/seo/buildSeoMetadata.mjs @@ -0,0 +1,206 @@ +import { getStaticSeoRoute } from "./seoRoutes.mjs"; + +export const SEO_SITE_NAME = "DEV_NET_CORE"; +export const SEO_SITE_ORIGIN = "https://www.dev-net-core.com"; +export const DEFAULT_OG_IMAGE_URL = + `${SEO_SITE_ORIGIN}/og/default-og-image.png`; +export const DEFAULT_OG_IMAGE_WIDTH = 1200; +export const DEFAULT_OG_IMAGE_HEIGHT = 630; +export const DEFAULT_OG_IMAGE_ALT = + "DEV_NET_CORE developer interview preparation"; + +const NOT_FOUND_TITLE = `Page Not Found | ${SEO_SITE_NAME}`; +const NOT_FOUND_DESCRIPTION = + "The requested DEV_NET_CORE page could not be found."; + +export function buildSeoMetadata(input = {}) { + const pathname = + input && typeof input === "object" ? input.pathname : undefined; + const contentEntry = + input && typeof input === "object" ? input.contentEntry : undefined; + const normalizedPath = normalizePathname(pathname); + + if (!normalizedPath) { + return buildNotFoundMetadata(); + } + + const staticRoute = getStaticSeoRoute(normalizedPath); + + if (staticRoute) { + return createMetadata({ + canonicalPath: staticRoute.canonicalPath, + description: staticRoute.seoDescription, + index: staticRoute.index, + title: staticRoute.seoTitle, + }); + } + + const contentMatch = normalizedPath.match( + /^\/content\/([a-z0-9][a-z0-9-]*)\/$/ + ); + + if (contentMatch) { + return buildContentMetadata( + normalizedPath, + contentMatch[1], + contentEntry + ); + } + + const practiceMatch = normalizedPath.match( + /^\/practice\/([a-z0-9][a-z0-9-]*)\/$/ + ); + + if (practiceMatch) { + return buildPracticeMetadata( + normalizedPath, + practiceMatch[1], + contentEntry + ); + } + + if ( + /^\/simulation\/session\/[a-z0-9-]+\/$/.test(normalizedPath) + ) { + return createMetadata({ + canonicalPath: null, + description: + "Complete a private DEV_NET_CORE mock interview session one question at a time.", + index: false, + title: `Mock Interview Session | ${SEO_SITE_NAME}`, + }); + } + + if (/^\/simulation\/result\/[a-z0-9-]+\/$/.test(normalizedPath)) { + return createMetadata({ + canonicalPath: null, + description: + "Review answers, expected responses, and self-evaluation results from a DEV_NET_CORE mock interview session.", + index: false, + title: `Mock Interview Results | ${SEO_SITE_NAME}`, + }); + } + + return buildNotFoundMetadata(); +} + +function buildContentMetadata(normalizedPath, topicId, contentEntry) { + if ( + !isValidContentEntry(contentEntry) || + (contentEntry.id !== topicId && + contentEntry.canonicalPath !== normalizedPath) + ) { + return buildNotFoundMetadata(); + } + + return createMetadata({ + canonicalPath: contentEntry.canonicalPath, + description: contentEntry.seoDescription, + index: true, + title: contentEntry.seoTitle, + }); +} + +function buildPracticeMetadata(normalizedPath, topicId, contentEntry) { + if (!isValidContentEntry(contentEntry) || contentEntry.id !== topicId) { + return buildNotFoundMetadata(); + } + + const topicName = + nonEmptyString(contentEntry.subtopic) || + nonEmptyString(contentEntry.title) || + topicId; + + return createMetadata({ + canonicalPath: normalizedPath, + description: + `Practice ${topicName} interview questions with expected answers, ` + + "key points, and topic progress tracking.", + index: false, + title: `Practice ${topicName} Interview Questions | ${SEO_SITE_NAME}`, + }); +} + +function buildNotFoundMetadata() { + return createMetadata({ + canonicalPath: null, + description: NOT_FOUND_DESCRIPTION, + index: false, + title: NOT_FOUND_TITLE, + }); +} + +function createMetadata({ canonicalPath, description, index, title }) { + const canonicalUrl = canonicalPath + ? `${SEO_SITE_ORIGIN}${canonicalPath}` + : null; + const openGraph = Object.freeze({ + description, + image: DEFAULT_OG_IMAGE_URL, + imageAlt: DEFAULT_OG_IMAGE_ALT, + imageHeight: DEFAULT_OG_IMAGE_HEIGHT, + imageWidth: DEFAULT_OG_IMAGE_WIDTH, + title, + type: "website", + url: canonicalUrl, + }); + const twitter = Object.freeze({ + card: "summary_large_image", + description, + image: DEFAULT_OG_IMAGE_URL, + title, + }); + + return Object.freeze({ + canonicalPath, + canonicalUrl, + description, + index, + openGraph, + robots: index ? "index,follow" : "noindex,follow", + title, + twitter, + }); +} + +function isValidContentEntry(contentEntry) { + return ( + contentEntry && + typeof contentEntry === "object" && + Boolean(nonEmptyString(contentEntry.id)) && + Boolean(nonEmptyString(contentEntry.seoTitle)) && + Boolean(nonEmptyString(contentEntry.seoDescription)) && + typeof contentEntry.canonicalPath === "string" && + /^\/content\/[a-z0-9][a-z0-9-]*\/$/.test( + contentEntry.canonicalPath + ) + ); +} + +function normalizePathname(pathname) { + if (typeof pathname !== "string") { + return undefined; + } + + const trimmedPath = pathname.trim(); + + if ( + !trimmedPath.startsWith("/") || + trimmedPath.includes("?") || + trimmedPath.includes("#") || + trimmedPath.includes("\\") || + trimmedPath.includes("//") + ) { + return undefined; + } + + if (trimmedPath === "/") { + return "/"; + } + + return trimmedPath.endsWith("/") ? trimmedPath : `${trimmedPath}/`; +} + +function nonEmptyString(value) { + return typeof value === "string" && value.trim() ? value.trim() : undefined; +} diff --git a/shared/seo/buildSeoMetadata.test.mjs b/shared/seo/buildSeoMetadata.test.mjs new file mode 100644 index 0000000..c884e08 --- /dev/null +++ b/shared/seo/buildSeoMetadata.test.mjs @@ -0,0 +1,123 @@ +import assert from "node:assert/strict"; +import fs from "node:fs"; +import test from "node:test"; + +import { + DEFAULT_OG_IMAGE_URL, + buildSeoMetadata, +} from "./buildSeoMetadata.mjs"; +import { + staticSeoRoutes, + validateStaticSeoRoutes, +} from "./seoRoutes.mjs"; + +const seoManifest = JSON.parse( + fs.readFileSync( + new URL("../content/curriculumSeoManifest.generated.json", import.meta.url), + "utf8" + ) +); +const asyncAwaitEntry = seoManifest.entries.find( + (entry) => entry.id === "async-and-await-semantics-in-csharp" +); + +test("builds self-canonical metadata for an indexable static route", () => { + const metadata = buildSeoMetadata({ pathname: "/content/" }); + + assert.equal(metadata.index, true); + assert.equal(metadata.robots, "index,follow"); + assert.equal( + metadata.canonicalUrl, + "https://www.dev-net-core.com/content/" + ); + assert.equal(metadata.openGraph.url, metadata.canonicalUrl); + assert.equal(metadata.openGraph.image, DEFAULT_OG_IMAGE_URL); + assert.equal(metadata.twitter.image, DEFAULT_OG_IMAGE_URL); +}); + +test("canonicalizes the home alias while keeping it out of the index", () => { + const metadata = buildSeoMetadata({ pathname: "/home" }); + + assert.equal(metadata.index, false); + assert.equal(metadata.robots, "noindex,follow"); + assert.equal(metadata.canonicalUrl, "https://www.dev-net-core.com/"); + assert.equal( + metadata.title, + "DEV_NET_CORE | Developer Interview Preparation" + ); +}); + +test("uses curriculum metadata for a valid content route", () => { + assert.ok(asyncAwaitEntry); + + const metadata = buildSeoMetadata({ + contentEntry: asyncAwaitEntry, + pathname: "/content/async-and-await-semantics-in-csharp", + }); + + assert.equal(metadata.index, true); + assert.equal(metadata.title, asyncAwaitEntry.seoTitle); + assert.equal(metadata.description, asyncAwaitEntry.seoDescription); + assert.equal( + metadata.canonicalPath, + "/content/async-and-await-semantics-in-csharp/" + ); +}); + +test("keeps a valid practice topic noindex and self-canonical", () => { + assert.ok(asyncAwaitEntry); + + const metadata = buildSeoMetadata({ + contentEntry: asyncAwaitEntry, + pathname: "/practice/async-and-await-semantics-in-csharp/", + }); + + assert.equal(metadata.index, false); + assert.equal(metadata.robots, "noindex,follow"); + assert.equal( + metadata.canonicalUrl, + "https://www.dev-net-core.com/practice/async-and-await-semantics-in-csharp/" + ); + assert.match(metadata.title, /^Practice Async and Await Semantics/); +}); + +test("keeps session-specific simulation routes noindex without canonicals", () => { + const metadata = buildSeoMetadata({ + pathname: "/simulation/session/01234567-89ab-cdef-0123-456789abcdef", + }); + + assert.equal(metadata.index, false); + assert.equal(metadata.canonicalPath, null); + assert.equal(metadata.canonicalUrl, null); + assert.equal(metadata.openGraph.url, null); + assert.equal(metadata.title, "Mock Interview Session | DEV_NET_CORE"); +}); + +test("treats an invalid content topic as not found", () => { + const metadata = buildSeoMetadata({ + pathname: "/content/not-a-real-topic/", + }); + + assert.equal(metadata.index, false); + assert.equal(metadata.canonicalPath, null); + assert.equal(metadata.canonicalUrl, null); + assert.equal(metadata.title, "Page Not Found | DEV_NET_CORE"); +}); + +test("rejects malformed static route configurations", () => { + assert.throws(() => + validateStaticSeoRoutes([ + { + canonicalPath: "/unsafe/", + index: false, + path: "/unsafe/", + prerender: false, + seoDescription: "", + seoTitle: "Unsafe", + sitemap: true, + }, + ]) + ); + + assert.equal(staticSeoRoutes.length, 12); +}); diff --git a/shared/seo/seoRoutes.d.mts b/shared/seo/seoRoutes.d.mts index e60618c..9f90c18 100644 --- a/shared/seo/seoRoutes.d.mts +++ b/shared/seo/seoRoutes.d.mts @@ -3,6 +3,8 @@ export type StaticSeoRoute = Readonly<{ index: boolean; path: string; prerender: boolean; + seoDescription: string; + seoTitle: string; sitemap: boolean; }>; diff --git a/shared/seo/seoRoutes.mjs b/shared/seo/seoRoutes.mjs index 38b174b..8eefd5f 100644 --- a/shared/seo/seoRoutes.mjs +++ b/shared/seo/seoRoutes.mjs @@ -5,6 +5,9 @@ const routeDefinitions = [ index: true, sitemap: true, prerender: true, + seoDescription: + "Structured interview preparation for .NET, React, SQL, Azure, architecture, and modern software engineering topics.", + seoTitle: "DEV_NET_CORE | Developer Interview Preparation", }, { path: "/home/", @@ -12,6 +15,9 @@ const routeDefinitions = [ index: false, sitemap: false, prerender: false, + seoDescription: + "Structured interview preparation for .NET, React, SQL, Azure, architecture, and modern software engineering topics.", + seoTitle: "DEV_NET_CORE | Developer Interview Preparation", }, { path: "/content/", @@ -19,6 +25,9 @@ const routeDefinitions = [ index: true, sitemap: true, prerender: true, + seoDescription: + "Explore structured interview preparation across .NET, architecture, SQL, React, Azure, and modern software engineering topics.", + seoTitle: "Software Engineering Interview Curriculum | DEV_NET_CORE", }, { path: "/practice/", @@ -26,6 +35,9 @@ const routeDefinitions = [ index: false, sitemap: false, prerender: false, + seoDescription: + "Practice topic-based software engineering interview questions with expected answers, key points, and progress tracking.", + seoTitle: "Technical Interview Practice | DEV_NET_CORE", }, { path: "/simulation/", @@ -33,6 +45,9 @@ const routeDefinitions = [ index: false, sitemap: false, prerender: false, + seoDescription: + "Practice a realistic technical interview session by selecting focus topics, difficulty level, and question count.", + seoTitle: "Mock Interview Simulation | DEV_NET_CORE", }, { path: "/simulation/setup/", @@ -40,6 +55,9 @@ const routeDefinitions = [ index: false, sitemap: false, prerender: false, + seoDescription: + "Configure focus topics, difficulty, and question count for a DEV_NET_CORE mock interview session.", + seoTitle: "Set Up a Mock Interview | DEV_NET_CORE", }, { path: "/roadmap/", @@ -47,6 +65,9 @@ const routeDefinitions = [ index: true, sitemap: true, prerender: true, + seoDescription: + "Explore the DEV_NET_CORE roadmap for structured technical learning, interview practice, and simulation improvements.", + seoTitle: "Developer Interview Preparation Roadmap | DEV_NET_CORE", }, { path: "/about-us/", @@ -54,6 +75,9 @@ const routeDefinitions = [ index: true, sitemap: true, prerender: true, + seoDescription: + "Learn how DEV_NET_CORE helps software engineers prepare for technical interviews with structured content and realistic practice.", + seoTitle: "About DEV_NET_CORE | Developer Interview Preparation", }, { path: "/changelog/", @@ -61,6 +85,9 @@ const routeDefinitions = [ index: false, sitemap: false, prerender: false, + seoDescription: + "Review updates and improvements to DEV_NET_CORE interview preparation content, practice, and simulations.", + seoTitle: "DEV_NET_CORE Changelog", }, { path: "/bug-report/", @@ -68,6 +95,9 @@ const routeDefinitions = [ index: false, sitemap: false, prerender: false, + seoDescription: + "Report a problem with DEV_NET_CORE content, navigation, interview practice, or simulation workflows.", + seoTitle: "Report a Bug | DEV_NET_CORE", }, { path: "/privacy/", @@ -75,6 +105,9 @@ const routeDefinitions = [ index: true, sitemap: true, prerender: true, + seoDescription: + "Review the DEV_NET_CORE privacy policy for the developer interview preparation website.", + seoTitle: "Privacy Policy | DEV_NET_CORE", }, { path: "/terms/", @@ -82,6 +115,9 @@ const routeDefinitions = [ index: true, sitemap: true, prerender: true, + seoDescription: + "Review the terms of use for the DEV_NET_CORE developer interview preparation website.", + seoTitle: "Terms of Use | DEV_NET_CORE", }, ]; @@ -91,6 +127,8 @@ export function validateStaticSeoRoutes(routes) { } const seenPaths = new Set(); + const seenIndexableDescriptions = new Set(); + const seenIndexableTitles = new Set(); routes.forEach((route, index) => { const label = route?.path || `entry ${index}`; @@ -123,10 +161,35 @@ export function validateStaticSeoRoutes(routes) { } } + for (const field of ["seoTitle", "seoDescription"]) { + if (typeof route[field] !== "string" || !route[field].trim()) { + throw new TypeError( + `Static SEO route ${label} must define non-empty ${field}.` + ); + } + } + if (route.index && route.canonicalPath !== route.path) { throw new Error(`Indexable static SEO route ${label} must be self-canonical.`); } + if (route.index) { + if (seenIndexableTitles.has(route.seoTitle)) { + throw new Error( + `Indexable static SEO route ${label} must have a unique seoTitle.` + ); + } + + if (seenIndexableDescriptions.has(route.seoDescription)) { + throw new Error( + `Indexable static SEO route ${label} must have a unique seoDescription.` + ); + } + + seenIndexableTitles.add(route.seoTitle); + seenIndexableDescriptions.add(route.seoDescription); + } + if ( route.sitemap && (!route.index || From 5597626ead0b873a1ecdcd4ea4a50286558d9dba Mon Sep 17 00:00:00 2001 From: Minh Vu Date: Sat, 27 Jun 2026 15:43:31 +0700 Subject: [PATCH 06/20] feat: apply route-aware SEO metadata --- src/App.tsx | 2 + src/components/seo/Seo.tsx | 120 +++++++++++++++++++++++++++++++++++++ 2 files changed, 122 insertions(+) create mode 100644 src/components/seo/Seo.tsx diff --git a/src/App.tsx b/src/App.tsx index 2c82343..6e2afdd 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,5 +1,6 @@ import { Route, Routes } from "react-router-dom"; import "./App.css"; +import { Seo } from "./components/seo/Seo"; import AboutUs from "./pages/AboutUs"; import BugReport from "./pages/BugReport"; import Changelog from "./pages/Changelog"; @@ -18,6 +19,7 @@ import { MainLayout } from "./layouts/MainLayout"; function App() { return ( <> + }> } /> diff --git a/src/components/seo/Seo.tsx b/src/components/seo/Seo.tsx new file mode 100644 index 0000000..3e891ad --- /dev/null +++ b/src/components/seo/Seo.tsx @@ -0,0 +1,120 @@ +import { useEffect, useMemo } from "react"; +import { useLocation } from "react-router-dom"; +import { + buildSeoMetadata, + type SeoMetadata, +} from "../../../shared/seo/buildSeoMetadata.mjs"; +import { curriculumManifest } from "../../contents/curriculumManifest.generated"; + +const curriculumEntriesById = new Map( + curriculumManifest.map((entry) => [entry.id, entry]) +); + +export function Seo() { + const { pathname } = useLocation(); + const metadata = useMemo( + () => + buildSeoMetadata({ + contentEntry: findCurriculumEntry(pathname), + pathname, + }), + [pathname] + ); + + useEffect(() => { + applySeoMetadata(metadata); + }, [metadata]); + + return null; +} + +function findCurriculumEntry(pathname: string) { + const topicId = pathname.match( + /^\/(?:content|practice)\/([a-z0-9][a-z0-9-]*)\/?$/ + )?.[1]; + + return topicId ? curriculumEntriesById.get(topicId) : undefined; +} + +function applySeoMetadata(metadata: SeoMetadata) { + document.title = metadata.title; + + upsertMetaByName("description", metadata.description); + upsertMetaByName("robots", metadata.robots); + + upsertMetaByProperty("og:title", metadata.openGraph.title); + upsertMetaByProperty("og:description", metadata.openGraph.description); + upsertMetaByProperty("og:type", metadata.openGraph.type); + upsertMetaByProperty("og:image", metadata.openGraph.image); + upsertMetaByProperty( + "og:image:width", + String(metadata.openGraph.imageWidth) + ); + upsertMetaByProperty( + "og:image:height", + String(metadata.openGraph.imageHeight) + ); + upsertMetaByProperty("og:image:alt", metadata.openGraph.imageAlt); + + upsertMetaByName("twitter:card", metadata.twitter.card); + upsertMetaByName("twitter:title", metadata.twitter.title); + upsertMetaByName("twitter:description", metadata.twitter.description); + upsertMetaByName("twitter:image", metadata.twitter.image); + + if (metadata.canonicalUrl) { + upsertCanonical(metadata.canonicalUrl); + } else { + removeHeadElements('link[rel="canonical"]'); + } + + if (metadata.openGraph.url) { + upsertMetaByProperty("og:url", metadata.openGraph.url); + } else { + removeHeadElements('meta[property="og:url"]'); + } +} + +function upsertMetaByName(name: string, content: string) { + upsertHeadElement("meta", `meta[name="${name}"]`, { + content, + name, + }); +} + +function upsertMetaByProperty(property: string, content: string) { + upsertHeadElement("meta", `meta[property="${property}"]`, { + content, + property, + }); +} + +function upsertCanonical(href: string) { + upsertHeadElement("link", 'link[rel="canonical"]', { + href, + rel: "canonical", + }); +} + +function upsertHeadElement( + tagName: "link" | "meta", + selector: string, + attributes: Record +) { + const existingElement = document.head.querySelector(selector); + const element = existingElement ?? document.createElement(tagName); + + Object.entries(attributes).forEach(([name, value]) => { + element.setAttribute(name, value); + }); + element.setAttribute("data-dev-net-core-seo", "true"); + + if (!existingElement) { + document.head.append(element); + } +} + +function removeHeadElements(selector: string) { + document.head.querySelectorAll(selector).forEach((element) => { + element.remove(); + }); +} From 3df4213c7779341bd723cd381debca02d8a47a5c Mon Sep 17 00:00:00 2001 From: Minh Vu Date: Sat, 27 Jun 2026 15:58:18 +0700 Subject: [PATCH 07/20] fix: render not found for invalid topics --- shared/seo/buildSeoMetadata.test.mjs | 11 +++++++++++ src/pages/Content.tsx | 18 +++--------------- src/pages/Practice.tsx | 3 ++- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/shared/seo/buildSeoMetadata.test.mjs b/shared/seo/buildSeoMetadata.test.mjs index c884e08..727d937 100644 --- a/shared/seo/buildSeoMetadata.test.mjs +++ b/shared/seo/buildSeoMetadata.test.mjs @@ -104,6 +104,17 @@ test("treats an invalid content topic as not found", () => { assert.equal(metadata.title, "Page Not Found | DEV_NET_CORE"); }); +test("treats an invalid practice topic as not found", () => { + const metadata = buildSeoMetadata({ + pathname: "/practice/not-a-real-topic/", + }); + + assert.equal(metadata.index, false); + assert.equal(metadata.canonicalPath, null); + assert.equal(metadata.canonicalUrl, null); + assert.equal(metadata.title, "Page Not Found | DEV_NET_CORE"); +}); + test("rejects malformed static route configurations", () => { assert.throws(() => validateStaticSeoRoutes([ diff --git a/src/pages/Content.tsx b/src/pages/Content.tsx index 7aef3c0..82d6d48 100644 --- a/src/pages/Content.tsx +++ b/src/pages/Content.tsx @@ -1,8 +1,7 @@ import { useEffect, useState } from "react"; -import { Navigate, useNavigate, useParams } from "react-router-dom"; +import { useNavigate, useParams } from "react-router-dom"; import { findCurriculumSubTopicById, - getFirstCurriculumSubTopic, getMarkdownBody, type CurriculumSubTopicNode, } from "../components/content/CurriculumTreeView"; @@ -14,6 +13,7 @@ import welcomeMarkdown from "../contents/resources/welcome.md?raw"; import { useAppDispatch, useAppSelector } from "../lib/redux/hooks/hooks"; import { selectSelectedContentTopicId } from "../lib/redux/selectors/contentSelectors"; import { setSelectedTopicId } from "../lib/redux/slices/contentSlice"; +import NotFound from "./NotFound"; const welcomeContent = getMarkdownBody(welcomeMarkdown); @@ -29,7 +29,6 @@ export default function Content() { const dispatch = useAppDispatch(); const selectedTopicId = useAppSelector(selectSelectedContentTopicId); const isWelcomeContent = !topicId; - const firstTopic = getFirstCurriculumSubTopic(); const selectedTopic = topicId ? findCurriculumSubTopicById(topicId) : undefined; const routeSelectedTopicId = selectedTopic?.id; const [loadedContent, setLoadedContent] = useState({ @@ -112,19 +111,8 @@ export default function Content() { ); } - if (!firstTopic) { - return ( - undefined} - selectedTopic={undefined} - /> - ); - } - if (!selectedTopic) { - return ; + return ; } return ( diff --git a/src/pages/Practice.tsx b/src/pages/Practice.tsx index 7ee2a9d..1c4a010 100644 --- a/src/pages/Practice.tsx +++ b/src/pages/Practice.tsx @@ -31,6 +31,7 @@ import { setSubTopicProgress, type ContentSubTopicProgress, } from "../lib/redux/slices/contentSlice"; +import NotFound from "./NotFound"; const practiceLevels = ["Beginner", "Intermediate", "Advanced"]; const emptyCompletedQuestions: number[] = []; @@ -1070,7 +1071,7 @@ export default function Practice() { } if (!topic) { - return navigate(-1)} />; + return ; } if (!topicPractice) { From 7e1d21d0f976449a09b1430a6ef274031a2371bd Mon Sep 17 00:00:00 2001 From: Minh Vu Date: Sat, 27 Jun 2026 16:01:10 +0700 Subject: [PATCH 08/20] docs: add structured data rules --- docs/plans/structured-data-rules.md | 153 ++++++++++++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 docs/plans/structured-data-rules.md diff --git a/docs/plans/structured-data-rules.md b/docs/plans/structured-data-rules.md new file mode 100644 index 0000000..075b063 --- /dev/null +++ b/docs/plans/structured-data-rules.md @@ -0,0 +1,153 @@ +# DEV_NET_CORE Structured Data Rules + +This note defines the rules DEV_NET_CORE must follow when implementing and +validating structured data. It is the implementation guardrail for Task 8 of +the SEO improvement plan. + +## Purpose + +Structured data should help search engines understand content that is already +visible on the page. It must not introduce claims, entities, dates, authorship, +ratings, or other facts that the page does not support. + +Valid structured data can make a page eligible for enhanced search features, +but it does not guarantee that Google will display a rich result. + +## General Rules + +- Use JSON-LD. +- Structured data must accurately represent the main visible page content. +- Put structured data only on the page that it describes. +- Use the most specific Google-supported schema type that accurately describes + the page. +- Do not mark up hidden, irrelevant, misleading, or incomplete information. +- Do not invent author, publisher, publication date, modification date, rating, + review, FAQ, organization, or ownership information. +- Do not emit properties with empty, `null`, or `undefined` values. +- Do not emit structured data on invalid, Not Found, or `noindex` routes. +- Do not block an indexable structured-data page through `robots.txt`. +- Treat successful validation as a correctness check, not a promise of a rich + result. + +## Approved Initial Types + +### Homepage + +Use one `WebSite` object on `/`. + +Initial properties: + +- `@context` +- `@type` +- `name` +- `url` +- `description` + +The URL must be the canonical production URL: + +```txt +https://www.dev-net-core.com/ +``` + +Do not add an `Organization`, publisher, owner, or alternate site name until +the corresponding identity is explicitly defined and supported by visible +site content. + +### Content Topics + +Use one `Article` object on each valid `/content/:topicId/` page because these +routes contain the long-form educational content for a curriculum topic. + +Initial properties: + +- `@context` +- `@type` +- `headline` +- `description` +- `url` + +Build these values from the same Markdown frontmatter, generated manifest, and +shared metadata builder used for title, description, and canonical metadata. +The Article URL must exactly match the page's canonical trailing-slash URL. + +Omit these properties until their values are verified and visible: + +- `author` +- `publisher` +- `datePublished` +- `dateModified` +- `image` + +The default Open Graph image is a site-wide branding fallback. Do not use it as +an Article image unless it is determined to be genuinely representative of the +specific article. A future topic-specific image must be crawlable, indexable, +and relevant to the marked-up content. + +## Deferred Types + +Do not add the following types during the initial implementation: + +- `FAQPage` +- `QAPage` +- `Course` +- `HowTo` +- `Review` +- `AggregateRating` +- `Person` +- `Organization` +- `BreadcrumbList` + +These types require a separate content and eligibility review. The presence of +interview questions in Markdown does not by itself justify FAQ, Q&A, rating, or +review markup. + +## Ownership and Consistency + +Structured data must follow the existing metadata source-of-truth chain: + +```txt +Markdown frontmatter + -> generated curriculum/SEO manifest + -> shared SEO metadata builder + -> pre-rendered source HTML + -> React route metadata +``` + +The shared metadata builder should produce the JSON-LD data. The SEO postbuild +pipeline must inject it into pre-rendered source HTML. The React SEO component +may mirror the same object after hydration, but it must not maintain a separate +hardcoded version. + +Differences between pre-rendered JSON-LD and hydrated JSON-LD are bugs. + +## Validation Checklist + +Before deployment: + +- Parse every generated JSON-LD block as JSON. +- Confirm each page contains only the intended structured-data objects. +- Confirm every structured-data value matches visible page content. +- Confirm each URL is absolute, canonical, and uses the trailing-slash format. +- Confirm invalid and `noindex` pages have no structured data. +- Confirm pre-rendered sitemap routes contain JSON-LD in source HTML. +- Run representative pages through Google's Rich Results Test. +- Run representative JSON-LD through Schema.org's Schema Markup Validator. +- Remove or defer any property that cannot be verified. + +Representative validation routes: + +```txt +/ +/content/async-and-await-semantics-in-csharp/ +/content/not-a-real-topic/ +/practice/async-and-await-semantics-in-csharp/ +``` + +## References + +- [Google: General structured data guidelines](https://developers.google.com/search/docs/appearance/structured-data/sd-policies) +- [Google: Article structured data](https://developers.google.com/search/docs/appearance/structured-data/article) +- [Google: Rich Results Test](https://search.google.com/test/rich-results) +- [Schema.org: Article](https://schema.org/Article) +- [Schema.org: WebSite](https://schema.org/WebSite) +- [Schema.org: Schema Markup Validator](https://validator.schema.org/) From ff6fc9b22af6ccf8cfa2c8a45584a103fcf6d9ec Mon Sep 17 00:00:00 2001 From: Minh Vu Date: Sat, 27 Jun 2026 16:07:54 +0700 Subject: [PATCH 09/20] feat: add route structured data --- shared/seo/buildSeoMetadata.d.mts | 21 ++++++++++++ shared/seo/buildSeoMetadata.mjs | 51 +++++++++++++++++++++++++++- shared/seo/buildSeoMetadata.test.mjs | 25 ++++++++++++++ src/components/seo/Seo.tsx | 26 ++++++++++++++ 4 files changed, 122 insertions(+), 1 deletion(-) diff --git a/shared/seo/buildSeoMetadata.d.mts b/shared/seo/buildSeoMetadata.d.mts index e282be6..12c2806 100644 --- a/shared/seo/buildSeoMetadata.d.mts +++ b/shared/seo/buildSeoMetadata.d.mts @@ -25,6 +25,26 @@ export type TwitterMetadata = Readonly<{ title: string; }>; +export type WebSiteStructuredData = Readonly<{ + "@context": "https://schema.org"; + "@type": "WebSite"; + description: string; + name: string; + url: string; +}>; + +export type ArticleStructuredData = Readonly<{ + "@context": "https://schema.org"; + "@type": "Article"; + description: string; + headline: string; + url: string; +}>; + +export type SeoStructuredData = + | ArticleStructuredData + | WebSiteStructuredData; + export type SeoMetadata = Readonly<{ canonicalPath: string | null; canonicalUrl: string | null; @@ -32,6 +52,7 @@ export type SeoMetadata = Readonly<{ index: boolean; openGraph: OpenGraphMetadata; robots: "index,follow" | "noindex,follow"; + structuredData: SeoStructuredData | null; title: string; twitter: TwitterMetadata; }>; diff --git a/shared/seo/buildSeoMetadata.mjs b/shared/seo/buildSeoMetadata.mjs index 8322586..4a950d4 100644 --- a/shared/seo/buildSeoMetadata.mjs +++ b/shared/seo/buildSeoMetadata.mjs @@ -31,6 +31,7 @@ export function buildSeoMetadata(input = {}) { canonicalPath: staticRoute.canonicalPath, description: staticRoute.seoDescription, index: staticRoute.index, + structuredDataType: normalizedPath === "/" ? "website" : null, title: staticRoute.seoTitle, }); } @@ -97,6 +98,7 @@ function buildContentMetadata(normalizedPath, topicId, contentEntry) { canonicalPath: contentEntry.canonicalPath, description: contentEntry.seoDescription, index: true, + structuredDataType: "article", title: contentEntry.seoTitle, }); } @@ -130,7 +132,13 @@ function buildNotFoundMetadata() { }); } -function createMetadata({ canonicalPath, description, index, title }) { +function createMetadata({ + canonicalPath, + description, + index, + structuredDataType, + title, +}) { const canonicalUrl = canonicalPath ? `${SEO_SITE_ORIGIN}${canonicalPath}` : null; @@ -158,11 +166,52 @@ function createMetadata({ canonicalPath, description, index, title }) { index, openGraph, robots: index ? "index,follow" : "noindex,follow", + structuredData: createStructuredData({ + canonicalUrl, + description, + index, + title, + type: structuredDataType, + }), title, twitter, }); } +function createStructuredData({ + canonicalUrl, + description, + index, + title, + type, +}) { + if (!index || !canonicalUrl) { + return null; + } + + if (type === "website") { + return Object.freeze({ + "@context": "https://schema.org", + "@type": "WebSite", + description, + name: SEO_SITE_NAME, + url: canonicalUrl, + }); + } + + if (type === "article") { + return Object.freeze({ + "@context": "https://schema.org", + "@type": "Article", + description, + headline: title, + url: canonicalUrl, + }); + } + + return null; +} + function isValidContentEntry(contentEntry) { return ( contentEntry && diff --git a/shared/seo/buildSeoMetadata.test.mjs b/shared/seo/buildSeoMetadata.test.mjs index 727d937..378457e 100644 --- a/shared/seo/buildSeoMetadata.test.mjs +++ b/shared/seo/buildSeoMetadata.test.mjs @@ -33,6 +33,19 @@ test("builds self-canonical metadata for an indexable static route", () => { assert.equal(metadata.openGraph.url, metadata.canonicalUrl); assert.equal(metadata.openGraph.image, DEFAULT_OG_IMAGE_URL); assert.equal(metadata.twitter.image, DEFAULT_OG_IMAGE_URL); + assert.equal(metadata.structuredData, null); +}); + +test("builds WebSite structured data for the canonical homepage", () => { + const metadata = buildSeoMetadata({ pathname: "/" }); + + assert.deepEqual(metadata.structuredData, { + "@context": "https://schema.org", + "@type": "WebSite", + description: metadata.description, + name: "DEV_NET_CORE", + url: "https://www.dev-net-core.com/", + }); }); test("canonicalizes the home alias while keeping it out of the index", () => { @@ -45,6 +58,7 @@ test("canonicalizes the home alias while keeping it out of the index", () => { metadata.title, "DEV_NET_CORE | Developer Interview Preparation" ); + assert.equal(metadata.structuredData, null); }); test("uses curriculum metadata for a valid content route", () => { @@ -62,6 +76,15 @@ test("uses curriculum metadata for a valid content route", () => { metadata.canonicalPath, "/content/async-and-await-semantics-in-csharp/" ); + assert.deepEqual(metadata.structuredData, { + "@context": "https://schema.org", + "@type": "Article", + description: asyncAwaitEntry.seoDescription, + headline: asyncAwaitEntry.seoTitle, + url: + "https://www.dev-net-core.com/content/" + + "async-and-await-semantics-in-csharp/", + }); }); test("keeps a valid practice topic noindex and self-canonical", () => { @@ -79,6 +102,7 @@ test("keeps a valid practice topic noindex and self-canonical", () => { "https://www.dev-net-core.com/practice/async-and-await-semantics-in-csharp/" ); assert.match(metadata.title, /^Practice Async and Await Semantics/); + assert.equal(metadata.structuredData, null); }); test("keeps session-specific simulation routes noindex without canonicals", () => { @@ -102,6 +126,7 @@ test("treats an invalid content topic as not found", () => { assert.equal(metadata.canonicalPath, null); assert.equal(metadata.canonicalUrl, null); assert.equal(metadata.title, "Page Not Found | DEV_NET_CORE"); + assert.equal(metadata.structuredData, null); }); test("treats an invalid practice topic as not found", () => { diff --git a/src/components/seo/Seo.tsx b/src/components/seo/Seo.tsx index 3e891ad..0bfa4e4 100644 --- a/src/components/seo/Seo.tsx +++ b/src/components/seo/Seo.tsx @@ -3,9 +3,13 @@ import { useLocation } from "react-router-dom"; import { buildSeoMetadata, type SeoMetadata, + type SeoStructuredData, } from "../../../shared/seo/buildSeoMetadata.mjs"; import { curriculumManifest } from "../../contents/curriculumManifest.generated"; +const STRUCTURED_DATA_SELECTOR = + 'script[type="application/ld+json"][data-dev-net-core-seo="structured-data"]'; + const curriculumEntriesById = new Map( curriculumManifest.map((entry) => [entry.id, entry]) ); @@ -72,6 +76,12 @@ function applySeoMetadata(metadata: SeoMetadata) { } else { removeHeadElements('meta[property="og:url"]'); } + + if (metadata.structuredData) { + upsertStructuredData(metadata.structuredData); + } else { + removeHeadElements(STRUCTURED_DATA_SELECTOR); + } } function upsertMetaByName(name: string, content: string) { @@ -95,6 +105,22 @@ function upsertCanonical(href: string) { }); } +function upsertStructuredData(structuredData: SeoStructuredData) { + const existingElement = + document.head.querySelector( + STRUCTURED_DATA_SELECTOR + ); + const element = existingElement ?? document.createElement("script"); + + element.type = "application/ld+json"; + element.dataset.devNetCoreSeo = "structured-data"; + element.textContent = JSON.stringify(structuredData); + + if (!existingElement) { + document.head.append(element); + } +} + function upsertHeadElement( tagName: "link" | "meta", selector: string, From 4c677fa59d97eb02082bd5a056394d4449a7813c Mon Sep 17 00:00:00 2001 From: Minh Vu Date: Sat, 27 Jun 2026 20:58:17 +0700 Subject: [PATCH 10/20] feat: make internal navigation crawlable --- src/components/AppFooter.tsx | 8 ++-- src/components/AppHeader.tsx | 42 +++++++++-------- src/components/content/CurriculumTreeView.tsx | 10 ++-- src/pages/Content.tsx | 5 +- src/pages/Home.tsx | 47 +++++++------------ src/pages/Practice.tsx | 2 +- 6 files changed, 52 insertions(+), 62 deletions(-) diff --git a/src/components/AppFooter.tsx b/src/components/AppFooter.tsx index 14b189f..425d97c 100644 --- a/src/components/AppFooter.tsx +++ b/src/components/AppFooter.tsx @@ -20,10 +20,10 @@ const FOOTER_LINKS: FooterLink[] = [ href: "https://github.com/CircleQMinh/dev-net-core", external: true, }, - { label: "Changelog", to: "/changelog", external: false }, - { label: "Bug report", to: "/bug-report", external: false }, - { label: "Terms", to: "/terms", external: false }, - { label: "Privacy", to: "/privacy", external: false }, + { label: "Changelog", to: "/changelog/", external: false }, + { label: "Bug report", to: "/bug-report/", external: false }, + { label: "Terms", to: "/terms/", external: false }, + { label: "Privacy", to: "/privacy/", external: false }, ]; function scrollToPageTop() { diff --git a/src/components/AppHeader.tsx b/src/components/AppHeader.tsx index 3ddb670..b18bf28 100644 --- a/src/components/AppHeader.tsx +++ b/src/components/AppHeader.tsx @@ -4,19 +4,18 @@ import MenuIcon from "@mui/icons-material/Menu"; import { Box, Button, IconButton, Menu, MenuItem, Tooltip } from "@mui/material"; import { useState } from "react"; import type { MouseEvent } from "react"; -import { useNavigate } from "react-router-dom"; +import { Link as RouterLink } from "react-router-dom"; import { useAppThemeMode } from "../theme/themeMode"; const NAV_ITEMS = { - "Learning": "/content#dotnet", - "Practice": "/practice", - "Simulation": "/simulation", - "Roadmap": "/roadmap", - "About Us": "/about-us", + "Learning": "/content/#dotnet", + "Practice": "/practice/", + "Simulation": "/simulation/", + "Roadmap": "/roadmap/", + "About Us": "/about-us/", }; export function AppHeader() { - const navigate = useNavigate(); const { mode, tokens, toggleMode } = useAppThemeMode(); const [menuAnchor, setMenuAnchor] = useState(null); @@ -35,16 +34,11 @@ export function AppHeader() { }); }; - const navigateFromHeader = (path: string) => { + const handleMenuNavigation = () => { closeMenu(); - navigate(path); scrollToPageTop(); }; - const goHome = () => { - navigateFromHeader("/"); - }; - return ( @@ -104,9 +100,10 @@ export function AppHeader() { > {Object.entries(NAV_ITEMS).map(([label, path]) => ( @@ -158,8 +155,9 @@ export function AppHeader() { @@ -219,8 +217,9 @@ export function AppHeader() { > {Object.entries(NAV_ITEMS).map(([label, path]) => ( navigateFromHeader(path)} + onClick={handleMenuNavigation} sx={{ color: tokens.onSurfaceVariant, fontFamily: '"Space Grotesk", "Inter", sans-serif', @@ -232,12 +231,14 @@ export function AppHeader() { color: tokens.onSurface, }, }} + to={path} > {label} ))} navigateFromHeader("/content")} + component={RouterLink} + onClick={handleMenuNavigation} sx={{ color: tokens.primaryContainer, display: { xs: "flex", sm: "none" }, @@ -247,6 +248,7 @@ export function AppHeader() { letterSpacing: "0.05em", textTransform: "uppercase", }} + to="/content/" > GET_STARTED diff --git a/src/components/content/CurriculumTreeView.tsx b/src/components/content/CurriculumTreeView.tsx index 0ab3411..091935d 100644 --- a/src/components/content/CurriculumTreeView.tsx +++ b/src/components/content/CurriculumTreeView.tsx @@ -7,6 +7,7 @@ import FolderOpenOutlinedIcon from "@mui/icons-material/FolderOpenOutlined"; import FolderOutlinedIcon from "@mui/icons-material/FolderOutlined"; import RestartAltOutlinedIcon from "@mui/icons-material/RestartAltOutlined"; import { useEffect, useMemo, useState } from "react"; +import { Link as RouterLink } from "react-router-dom"; import { useAppDispatch, useAppSelector } from "../../lib/redux/hooks/hooks"; import { selectContentProgress } from "../../lib/redux/selectors/contentSelectors"; import { @@ -587,8 +588,9 @@ function TreeNodeRow({ const isActive = activeTopicId === node.id; return ( - + ); } diff --git a/src/pages/Content.tsx b/src/pages/Content.tsx index 82d6d48..72268c3 100644 --- a/src/pages/Content.tsx +++ b/src/pages/Content.tsx @@ -1,5 +1,5 @@ import { useEffect, useState } from "react"; -import { useNavigate, useParams } from "react-router-dom"; +import { useParams } from "react-router-dom"; import { findCurriculumSubTopicById, getMarkdownBody, @@ -25,7 +25,6 @@ type LoadedContentState = { export default function Content() { const { topicId } = useParams<{ topicId?: string }>(); - const navigate = useNavigate(); const dispatch = useAppDispatch(); const selectedTopicId = useAppSelector(selectSelectedContentTopicId); const isWelcomeContent = !topicId; @@ -96,8 +95,6 @@ export default function Content() { if (selectedTopicId !== topic.id) { dispatch(setSelectedTopicId(topic.id)); } - - navigate(`/content/${topic.id}/`); }; if (isWelcomeContent) { diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index 2b87c08..b2f6d44 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -13,7 +13,7 @@ import TerminalOutlinedIcon from "@mui/icons-material/TerminalOutlined"; import { Box, Button, Container, Stack } from "@mui/material"; import { useMemo } from "react"; import type { ReactNode } from "react"; -import { useNavigate } from "react-router-dom"; +import { Link as RouterLink } from "react-router-dom"; import { collectSubTopicNodes, generatedCurriculumTree, @@ -103,7 +103,7 @@ function buildLearningPaths(): LearningPath[] { className: learningPathClassNames[categoryNode.title] ?? "lg:col-span-4 lg:min-h-[217px]", - to: subTopics[0] ? `/content/${subTopics[0].id}/` : "/content", + to: subTopics[0] ? `/content/${subTopics[0].id}/` : "/content/", }; }); } @@ -269,20 +269,21 @@ function PageSection({ function CyberButton({ children, intent = "primary", - onClick, + to, }: { children: ReactNode; intent?: "primary" | "secondary"; - onClick?: () => void; + to: string; }) { const isPrimary = intent === "primary"; return ( @@ -407,10 +408,8 @@ function CodePreview() { function LearningPathCard({ path, - onSelect, }: { path: LearningPath; - onSelect: (to: string) => void; }) { const isFeatured = path.tags.length > 0; @@ -418,17 +417,18 @@ function LearningPathCard({ onSelect(path.to)} + component={RouterLink} + onClick={scrollToPageTop} sx={{ appearance: "none", color: "inherit", cursor: "pointer", font: "inherit", textAlign: "left", + textDecoration: "none", width: "100%", }} - type="button" + to={path.to} >

@@ -530,14 +530,8 @@ function RoadmapStepCard({ step }: { step: RoadmapStep }) { } export default function Home() { - const navigate = useNavigate(); const drills = useMemo(() => getRandomDrills(), []); - const navigateFromHome = (path: string) => { - navigate(path); - scrollToPageTop(); - }; - return ( @@ -571,13 +565,10 @@ export default function Home() { spacing={2} className="pt-2" > - navigateFromHome("/content")}> + Start Learning Now - navigateFromHome("/practice")} - > + Explore Question Bank @@ -594,11 +585,7 @@ export default function Home() {
{learningPaths.map((path) => ( - + ))}
@@ -622,9 +609,10 @@ export default function Home() { subtitle="Common technical assessment benchmarks" /> @@ -677,7 +666,7 @@ export default function Home() { interview performance with our platform.

- navigateFromHome("/content")}> + Get Started
diff --git a/src/pages/Practice.tsx b/src/pages/Practice.tsx index 1c4a010..f3ac1b6 100644 --- a/src/pages/Practice.tsx +++ b/src/pages/Practice.tsx @@ -787,7 +787,7 @@ function PracticeIndexCta({ firstTopic }: { firstTopic?: CurriculumSubTopicNode component={RouterLink} endIcon={} sx={{ px: 5, py: 1.5 }} - to={`/simulation`} + to="/simulation/" variant="contained" > Start simulation From 6e7eb9c3d030323ba938398ea7c83b197693f4c9 Mon Sep 17 00:00:00 2001 From: Minh Vu Date: Sat, 27 Jun 2026 21:14:02 +0700 Subject: [PATCH 11/20] feat: normalize canonical route paths --- src/App.tsx | 111 ++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 87 insertions(+), 24 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 6e2afdd..f040ff3 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,4 +1,9 @@ -import { Route, Routes } from "react-router-dom"; +import { + Navigate, + Route, + Routes, + useLocation, +} from "react-router-dom"; import "./App.css"; import { Seo } from "./components/seo/Seo"; import AboutUs from "./pages/AboutUs"; @@ -16,35 +21,93 @@ import SimulationSession from "./pages/SimulationSession"; import Terms from "./pages/Terms"; import { MainLayout } from "./layouts/MainLayout"; +const trailingSlashRouteAliases = new Set([ + "/about-us", + "/bug-report", + "/changelog", + "/content", + "/practice", + "/privacy", + "/roadmap", + "/simulation", + "/simulation/setup", + "/terms", +]); + function App() { return ( <> - - }> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - - + ); } +function AppRoutes() { + const location = useLocation(); + const canonicalPathname = getCanonicalPathname(location.pathname); + + if (canonicalPathname) { + return ( + + ); + } + + return ( + + }> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } + /> + } + /> + } /> + + + ); +} + +function getCanonicalPathname(pathname: string) { + if (pathname === "/home" || pathname === "/home/") { + return "/"; + } + + if (pathname.endsWith("/")) { + return undefined; + } + + if ( + trailingSlashRouteAliases.has(pathname) || + /^\/(?:content|practice)\/[^/]+$/.test(pathname) + ) { + return `${pathname}/`; + } + + return undefined; +} + export default App; From 7a5047ae750e799d7539380bd2536da2ff39779a Mon Sep 17 00:00:00 2001 From: Minh Vu Date: Sat, 27 Jun 2026 21:42:41 +0700 Subject: [PATCH 12/20] chore: validate React Router SSG approach --- .gitignore | 2 + docs/spikes/react-router-7-ssg-spike.md | 116 ++ eslint.config.js | 8 +- package-lock.json | 1678 ++++++++++++++++- package.json | 7 +- react-router.config.ts | 30 + spikes/react-router-7-ssg/README.md | 29 + .../react-router-7-ssg/app/entry.server.tsx | 36 + .../app/lib/content.server.ts | 60 + spikes/react-router-7-ssg/app/root.tsx | 30 + spikes/react-router-7-ssg/app/routes.ts | 7 + .../app/routes/content-topic.tsx | 44 + .../react-router-7-ssg/app/routes/content.tsx | 27 + spikes/react-router-7-ssg/app/routes/home.tsx | 18 + spikes/react-router-7-ssg/package.json | 5 + spikes/react-router-7-ssg/vite.config.ts | 8 + 16 files changed, 2012 insertions(+), 93 deletions(-) create mode 100644 docs/spikes/react-router-7-ssg-spike.md create mode 100644 react-router.config.ts create mode 100644 spikes/react-router-7-ssg/README.md create mode 100644 spikes/react-router-7-ssg/app/entry.server.tsx create mode 100644 spikes/react-router-7-ssg/app/lib/content.server.ts create mode 100644 spikes/react-router-7-ssg/app/root.tsx create mode 100644 spikes/react-router-7-ssg/app/routes.ts create mode 100644 spikes/react-router-7-ssg/app/routes/content-topic.tsx create mode 100644 spikes/react-router-7-ssg/app/routes/content.tsx create mode 100644 spikes/react-router-7-ssg/app/routes/home.tsx create mode 100644 spikes/react-router-7-ssg/package.json create mode 100644 spikes/react-router-7-ssg/vite.config.ts diff --git a/.gitignore b/.gitignore index ea4b6a9..2f073aa 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,8 @@ lerna-debug.log* node_modules dist dist-ssr +/.react-router/ +/spikes/react-router-7-ssg/build/ *.local /.playwright-cli/ diff --git a/docs/spikes/react-router-7-ssg-spike.md b/docs/spikes/react-router-7-ssg-spike.md new file mode 100644 index 0000000..0713a61 --- /dev/null +++ b/docs/spikes/react-router-7-ssg-spike.md @@ -0,0 +1,116 @@ +# React Router 7 SSG Spike Result + +Date: 2026-06-27 + +## Decision + +Proceed toward React Router framework-mode SSG with `ssr: false`. + +The spike proved that the project can generate GitHub Pages-compatible static +HTML for canonical trailing-slash routes while retaining client hydration and +route-loader navigation. The migration cost is moderate to high, but the +framework removes more custom rendering and hydration risk than it introduces. + +Keep a small SEO finalization script for sitemap generation, output validation, +and GitHub Pages `404.html` handling. Do not build a separate custom Vite SSR +renderer unless the framework migration later fails on the real app tree. + +## Proof + +The isolated proof uses React Router 7.18.0 and: + +- `ssr: false` +- `routeDiscovery: { mode: "initial" }` +- `future.v8_trailingSlashAwareDataRequests: true` +- dynamic `prerender` paths from + `shared/content/curriculumSeoManifest.generated.json` +- Node build-time Markdown loading by manifest `contentPath` +- the existing shared SEO metadata builder +- route loader data shared by pre-rendering and hydration + +The build generated: + +```txt +build/client/index.html +build/client/content/index.html +build/client/content/_.data +build/client/content//index.html +build/client/content//_.data +build/client/__spa-fallback.html +``` + +The temporary server bundle was removed automatically because runtime SSR is +disabled. + +## Questions Answered + +| Question | Result | +|---|---| +| Can the app use framework mode? | Yes, but routes and the app entry must migrate to route modules. | +| Can static hosting use `ssr: false`? | Yes. No deployed Node server is required. | +| Can all sitemap paths be pre-rendered? | Yes. Static paths and manifest-generated topic paths are supported. | +| Can dynamic topic paths come from the SEO manifest? | Yes. The config reads the JSON manifest directly in Node. | +| Can loaders receive Markdown and metadata at build time? | Yes. The topic loader read the Markdown file and shared metadata before rendering. | +| Does hydration receive the same route data? | Yes. React Router generated `.data` output and hydrated without mismatch warnings. | +| Is GitHub Pages output compatible? | Yes for pre-rendered trailing-slash routes. A small fallback adapter is still required. | +| Does this eliminate the SEO postbuild step? | No. It reduces it to sitemap, validation, fallback, and output finalization work. | + +## Browser Validation + +Playwright verified: + +- direct topic load returned the pre-rendered document +- the source document contained topic title, description, canonical, Open Graph, + JSON-LD, and visible Markdown +- hydration completed with zero console errors +- client navigation from `/content/` to the dynamic topic loaded the generated + topic `.data` file +- no request was made to `/__manifest` + +For the sample topic, output included approximately 52 KB of HTML and 25 KB of +route data. Full Markdown is therefore duplicated between HTML and `.data`; +output-size budgets remain necessary before pre-rendering every topic. + +## Required Migration Work + +1. Replace `BrowserRouter`, component ``, and `createRoot` ownership with + framework route modules and framework entries. +2. Move shared providers into the framework root while preserving the current + layout and navigation. +3. Create a Redux store factory and keep persistence subscriptions client-only. +4. Make theme and SEO behavior safe during build-time rendering. +5. Replace the content page's post-mount Markdown fetch with loader-provided + initial route data. +6. Ensure the client hydrates from the same Markdown and curriculum metadata + used during pre-rendering. +7. Convert route metadata to framework `meta` output backed by the shared + metadata builder. +8. Generate every sitemap-eligible path through `prerender`. + +## GitHub Pages Caveats + +- Deploy `build/client` rather than a runtime server build. +- Keep canonical sitemap routes as `route/index.html` static artifacts. +- React Router emits `__spa-fallback.html`, while GitHub Pages uses `404.html`. + The finalizer must create a dedicated `404.html` with `noindex`; it must not + count as valid output for sitemap routes. +- Keep `routeDiscovery` in `initial` mode because GitHub Pages cannot serve a + runtime `/__manifest` endpoint. +- Enable `future.v8_trailingSlashAwareDataRequests`. React Router 7.18 otherwise + removes the trailing slash from generated data requests and rejects canonical + paths such as `/content/` during pre-rendering. +- Pin React Router framework packages to the same version. Mixed framework + package versions caused peer-resolution conflicts during the spike. +- Install `react-router` as a direct framework CLI peer alongside + `react-router-dom`; relying only on the nested DOM package copy did not satisfy + the 7.18 CLI. + +## Rejected Alternative + +Do not proceed with a fully custom Vite SSR/SSG renderer as the primary plan. +It would need to duplicate route matching, loader execution, hydration data, +metadata coordination, and asset handling already provided by React Router. + +Retain custom Node scripts only for responsibilities React Router does not own: +sitemap generation, static-output validation, GitHub Pages fallback generation, +and deployment checks. diff --git a/eslint.config.js b/eslint.config.js index 5e6b472..46380f8 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -6,7 +6,7 @@ import tseslint from 'typescript-eslint' import { defineConfig, globalIgnores } from 'eslint/config' export default defineConfig([ - globalIgnores(['dist']), + globalIgnores(['.react-router', 'dist']), { files: ['**/*.{ts,tsx}'], extends: [ @@ -20,4 +20,10 @@ export default defineConfig([ globals: globals.browser, }, }, + { + files: ['spikes/react-router-7-ssg/app/routes/**/*.tsx'], + rules: { + 'react-refresh/only-export-components': 'off', + }, + }, ]) diff --git a/package-lock.json b/package-lock.json index ddbce5a..724c53e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,13 +20,16 @@ "react-hook-form": "^7.71.1", "react-markdown": "^10.1.0", "react-redux": "^9.2.0", - "react-router-dom": "^7.12.0", + "react-router-dom": "7.18.0", "rehype-highlight": "^7.0.2", "remark-gfm": "^4.0.1", "yup": "^1.7.1" }, "devDependencies": { "@eslint/js": "^9.39.1", + "@react-router/dev": "7.18.0", + "@react-router/node": "7.18.0", + "@react-router/serve": "7.18.0", "@tailwindcss/vite": "^4.3.0", "@types/node": "^24.10.1", "@types/react": "^19.2.5", @@ -36,6 +39,7 @@ "eslint-plugin-react-hooks": "^7.0.1", "eslint-plugin-react-refresh": "^0.4.24", "globals": "^16.5.0", + "react-router": "7.18.0", "tailwindcss": "^4.3.0", "typescript": "~5.9.3", "typescript-eslint": "^8.46.4", @@ -43,12 +47,12 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.28.6.tgz", - "integrity": "sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.28.5", + "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" }, @@ -98,13 +102,13 @@ } }, "node_modules/@babel/generator": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.6.tgz", - "integrity": "sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz", + "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.28.6", - "@babel/types": "^7.28.6", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -113,6 +117,19 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.29.7.tgz", + "integrity": "sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-compilation-targets": { "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", @@ -130,38 +147,74 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.29.7.tgz", + "integrity": "sha512-IY3ZD9Tmooqr3TUhc3DUWxiuo8xx1DWLhd5M7hQ+ZWJamqM2BbalrBJb2MisSLoYorOj75U03qULCxQTY9r3hg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-member-expression-to-functions": "^7.29.7", + "@babel/helper-optimise-call-expression": "^7.29.7", + "@babel/helper-replace-supers": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", + "@babel/traverse": "^7.29.7", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, "node_modules/@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.29.7.tgz", + "integrity": "sha512-j+7JYmk1JYDtACIGj0QJqqWZjoUpMoEikQGADMaHgCMCSDqd2+P32rfcibUNrGOMWrlzK1WJBdxrB3JJQZwWtg==", + "dev": true, "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", - "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", "license": "MIT", "dependencies": { - "@babel/traverse": "^7.28.6", - "@babel/types": "^7.28.6" + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", - "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.28.6", - "@babel/helper-validator-identifier": "^7.28.5", - "@babel/traverse": "^7.28.6" + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -170,38 +223,83 @@ "@babel/core": "^7.0.0" } }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.29.7.tgz", + "integrity": "sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", - "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", + "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.29.7.tgz", + "integrity": "sha512-atfGXWSeCiF4DnKZIfmJfQRkSw9b9gNNXR1kqKjbhG4pGYCOnkp8OcTB8E3NXjBu8NpheSnOeNKz8KT7UNFTmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.29.7", + "@babel/helper-optimise-call-expression": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.29.7.tgz", + "integrity": "sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==", "dev": true, "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", "dev": true, "license": "MIT", "engines": { @@ -223,12 +321,12 @@ } }, "node_modules/@babel/parser": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.6.tgz", - "integrity": "sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", + "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", "license": "MIT", "dependencies": { - "@babel/types": "^7.28.6" + "@babel/types": "^7.29.7" }, "bin": { "parser": "bin/babel-parser.js" @@ -237,6 +335,55 @@ "node": ">=6.0.0" } }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.29.7.tgz", + "integrity": "sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.29.7.tgz", + "integrity": "sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.29.7.tgz", + "integrity": "sha512-j0vCldybPC5b5dwCQOJ21uKtHzt7hxLygJTg9eF1ScfaikEDNfzn94XoW5Fi+seBR0nCyL23xaBFFkq7dTM8XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-transform-react-jsx-self": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", @@ -269,6 +416,46 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.29.7.tgz", + "integrity": "sha512-jK52h8LaLc7JarhQV2ofeFMts4H7vnOXnqZNA6fYglBTZewRBE51KWt3BUltW1P+KoPsYkHoJeXePuz4zo2LMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", + "@babel/plugin-syntax-typescript": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-typescript": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.29.7.tgz", + "integrity": "sha512-/Foi8vKY2EVbed/1eZx0gJEEwHAIxogrySI7rULcRIvhZzbvoE/b5qG5Ghc0WKAFKOHA9SD1x7RsFlOYdutIiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "@babel/plugin-syntax-jsx": "^7.29.7", + "@babel/plugin-transform-modules-commonjs": "^7.29.7", + "@babel/plugin-transform-typescript": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/runtime": { "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz", @@ -279,31 +466,31 @@ } }, "node_modules/@babel/template": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", - "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.28.6", - "@babel/parser": "^7.28.6", - "@babel/types": "^7.28.6" + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.6.tgz", - "integrity": "sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz", + "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==", "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.28.6", - "@babel/generator": "^7.28.6", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.6", - "@babel/template": "^7.28.6", - "@babel/types": "^7.28.6", + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7", "debug": "^4.3.1" }, "engines": { @@ -311,13 +498,13 @@ } }, "node_modules/@babel/types": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.6.tgz", - "integrity": "sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", + "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5" + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1184,6 +1371,13 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@mjackson/node-fetch-server": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@mjackson/node-fetch-server/-/node-fetch-server-0.2.0.tgz", + "integrity": "sha512-EMlH1e30yzmTpGLQjlFmaDAjyOeZhng1/XCd7DExR8PNAnG/G1tyruZxEoUe11ClnwGhGrtsdnyyUx1frSzjng==", + "dev": true, + "license": "MIT" + }, "node_modules/@mui/core-downloads-tracker": { "version": "7.3.7", "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-7.3.7.tgz", @@ -1427,6 +1621,182 @@ "url": "https://opencollective.com/popperjs" } }, + "node_modules/@react-router/dev": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@react-router/dev/-/dev-7.18.0.tgz", + "integrity": "sha512-GVTFvul0xlZHZyVXyRpiJv54Xfyj4eDOAlGYrzi7kDmN7n40rsrUqX+hvU0fy/41SCDMtckht59R3iGR94703g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.27.7", + "@babel/generator": "^7.27.5", + "@babel/parser": "^7.27.7", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/preset-typescript": "^7.27.1", + "@babel/traverse": "^7.27.7", + "@babel/types": "^7.27.7", + "@react-router/node": "7.18.0", + "@remix-run/node-fetch-server": "^0.13.0", + "arg": "^5.0.1", + "babel-dead-code-elimination": "^1.0.6", + "chokidar": "^4.0.0", + "dedent": "^1.5.3", + "es-module-lexer": "^1.3.1", + "exit-hook": "2.2.1", + "isbot": "^5.1.11", + "jsesc": "3.0.2", + "lodash": "^4.17.21", + "p-map": "^7.0.3", + "pathe": "^1.1.2", + "picocolors": "^1.1.1", + "pkg-types": "^2.3.0", + "prettier": "^3.6.2", + "react-refresh": "^0.14.0", + "semver": "^7.3.7", + "tinyglobby": "^0.2.14", + "valibot": "^1.2.0", + "vite-node": "^3.2.2" + }, + "bin": { + "react-router": "bin.js" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@react-router/serve": "^7.18.0", + "@vitejs/plugin-rsc": "~0.5.21", + "react-router": "^7.18.0", + "react-server-dom-webpack": "^19.2.3", + "typescript": "^5.1.0 || ^6.0.0", + "vite": "^5.1.0 || ^6.0.0 || ^7.0.0 || ^8.0.0", + "wrangler": "^3.28.2 || ^4.0.0" + }, + "peerDependenciesMeta": { + "@react-router/serve": { + "optional": true + }, + "@vitejs/plugin-rsc": { + "optional": true + }, + "react-server-dom-webpack": { + "optional": true + }, + "typescript": { + "optional": true + }, + "wrangler": { + "optional": true + } + } + }, + "node_modules/@react-router/dev/node_modules/jsesc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@react-router/dev/node_modules/react-refresh": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", + "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@react-router/dev/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@react-router/express": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@react-router/express/-/express-7.18.0.tgz", + "integrity": "sha512-7+6oZJYdJ7ha7ASV0XkYZkmnTUYtRkzjzjpZh3W9tPdaUY1zJoDFiMlUJZoAg0mHw8BNTAwVWCCf8NFV1xlLZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@react-router/node": "7.18.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "express": "^4.17.1 || ^5", + "react-router": "7.18.0", + "typescript": "^5.1.0 || ^6.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@react-router/node": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@react-router/node/-/node-7.18.0.tgz", + "integrity": "sha512-pRXJahLrdVfuVbaTpWsZ89mBuGiYH3Z4y+y1UidwxmJFKk6NjMyUvkJl3FjDWdD+nSlgFPSESUZS0hF560MUUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@mjackson/node-fetch-server": "^0.2.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react-router": "7.18.0", + "typescript": "^5.1.0 || ^6.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@react-router/serve": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@react-router/serve/-/serve-7.18.0.tgz", + "integrity": "sha512-IrF0cLcJNGBBavnRBm3HxaEGwRrLrLF8E4EzQFuCpkgP1sRli1x2xEOOTJl4zBgUbyIn0ey4TAD6ytg45MAUBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@mjackson/node-fetch-server": "^0.2.0", + "@react-router/express": "7.18.0", + "@react-router/node": "7.18.0", + "compression": "^1.8.1", + "express": "^4.19.2", + "get-port": "5.1.1", + "morgan": "^1.10.1", + "source-map-support": "^0.5.21" + }, + "bin": { + "react-router-serve": "bin.js" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react-router": "7.18.0" + } + }, "node_modules/@reduxjs/toolkit": { "version": "2.11.2", "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.11.2.tgz", @@ -1453,6 +1823,13 @@ } } }, + "node_modules/@remix-run/node-fetch-server": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/@remix-run/node-fetch-server/-/node-fetch-server-0.13.3.tgz", + "integrity": "sha512-UfjOXed/DQteaM5VyTfqTeGpHwyL2J5aoRGY6cydip4tt1ehNNeSwuXCC7AEGE0RWBs/7bgKxYkL/B/+UDe4AA==", + "dev": true, + "license": "MIT" + }, "node_modules/@rolldown/pluginutils": { "version": "1.0.0-beta.53", "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.53.tgz", @@ -2286,6 +2663,7 @@ "version": "19.2.9", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.9.tgz", "integrity": "sha512-Lpo8kgb/igvMIPeNV2rsYKTgaORYdO1XGVZ4Qz3akwOj0ySGYMPlQWa8BaLn0G63D1aSaAQ5ldR06wCpChQCjA==", + "dev": true, "license": "MIT", "dependencies": { "csstype": "^3.2.2" @@ -2618,6 +2996,30 @@ "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/accepts/node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/acorn": { "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", @@ -2686,6 +3088,13 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true, + "license": "MIT" + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -2693,6 +3102,13 @@ "dev": true, "license": "Python-2.0" }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "dev": true, + "license": "MIT" + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -2711,6 +3127,19 @@ "proxy-from-env": "^2.1.0" } }, + "node_modules/babel-dead-code-elimination": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/babel-dead-code-elimination/-/babel-dead-code-elimination-1.0.12.tgz", + "integrity": "sha512-GERT7L2TiYcYDtYk1IpD+ASAYXjKbLTDPhBtYj7X1NuRMDTMtAx9kyBenub1Ev41lo91OHCKdmP+egTDmfQ7Ig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.23.7", + "@babel/parser": "^7.23.6", + "@babel/traverse": "^7.23.7", + "@babel/types": "^7.23.6" + } + }, "node_modules/babel-plugin-macros": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", @@ -2753,7 +3182,69 @@ "baseline-browser-mapping": "dist/cli.js" } }, - "node_modules/brace-expansion": { + "node_modules/basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/basic-auth/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/body-parser": { + "version": "1.20.5", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.5.tgz", + "integrity": "sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.15.1", + "raw-body": "~2.5.3", + "type-is": "~1.6.18", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", @@ -2798,6 +3289,33 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/call-bind-apply-helpers": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", @@ -2811,6 +3329,23 @@ "node": ">= 0.4" } }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -2908,6 +3443,22 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/clsx": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", @@ -2959,6 +3510,55 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz", + "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "compressible": "~2.0.18", + "debug": "2.6.9", + "negotiator": "~0.6.4", + "on-headers": "~1.1.0", + "safe-buffer": "5.2.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -2966,6 +3566,36 @@ "dev": true, "license": "MIT" }, + "node_modules/confbox": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.4.tgz", + "integrity": "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -2986,6 +3616,13 @@ "url": "https://opencollective.com/express" } }, + "node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", + "dev": true, + "license": "MIT" + }, "node_modules/cosmiconfig": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", @@ -3062,6 +3699,21 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/dedent": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.2.tgz", + "integrity": "sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -3078,6 +3730,16 @@ "node": ">=0.4.0" } }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", @@ -3087,6 +3749,17 @@ "node": ">=6" } }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", @@ -3134,6 +3807,13 @@ "node": ">= 0.4" } }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true, + "license": "MIT" + }, "node_modules/electron-to-chromium": { "version": "1.5.277", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.277.tgz", @@ -3141,6 +3821,16 @@ "dev": true, "license": "ISC" }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/enhanced-resolve": { "version": "5.21.2", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.21.2.tgz", @@ -3182,6 +3872,13 @@ "node": ">= 0.4" } }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" + }, "node_modules/es-object-atoms": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", @@ -3261,6 +3958,13 @@ "node": ">=6" } }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true, + "license": "MIT" + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -3467,6 +4171,110 @@ "node": ">=0.10.0" } }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/exit-hook": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-2.2.1.tgz", + "integrity": "sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/express": { + "version": "4.22.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz", + "integrity": "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "~1.20.5", + "content-disposition": "~0.5.4", + "content-type": "~1.0.4", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "~0.1.12", + "proxy-addr": "~2.0.7", + "qs": "~6.15.1", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "~0.19.0", + "serve-static": "~1.16.2", + "setprototypeof": "1.2.0", + "statuses": "~2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express/node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/exsolve": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.1.0.tgz", + "integrity": "sha512-D+42+T12DdIlJM3uepa55qGiL3sYdLBOxIl2ifQCzCHz4c7eiolaHsi3BIqEr7JxBzxv2pYZQX9kw16ziMcEmw==", + "dev": true, + "license": "MIT" + }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -3525,6 +4333,42 @@ "node": ">=16.0.0" } }, + "node_modules/finalhandler": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "statuses": "~2.0.2", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, "node_modules/find-root": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", @@ -3605,6 +4449,26 @@ "node": ">= 6" } }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -3663,6 +4527,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-port": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", + "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", @@ -3890,6 +4767,27 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/https-proxy-agent": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", @@ -3903,14 +4801,27 @@ "node": ">= 6" } }, - "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, "engines": { - "node": ">= 4" + "node": ">=0.10.0" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" } }, "node_modules/immer": { @@ -3949,12 +4860,29 @@ "node": ">=0.8.19" } }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, "node_modules/inline-style-parser": { "version": "0.2.7", "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz", "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==", "license": "MIT" }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/is-alphabetical": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", @@ -4055,6 +4983,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/isbot": { + "version": "5.1.44", + "resolved": "https://registry.npmjs.org/isbot/-/isbot-5.1.44.tgz", + "integrity": "sha512-PGEHtwMnKbZpeSEXW2Utx+/JWed7dp6DiH0WWg33vGSDA7RUvpUeJSVlLrVkQ1RCpvDOUc/eH9ql7VsdbBZ8pA==", + "dev": true, + "license": "Unlicense", + "engines": { + "node": ">=18" + } + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -4450,6 +5388,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -4815,6 +5760,36 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/micromark": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", @@ -5378,6 +6353,19 @@ ], "license": "MIT" }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -5412,6 +6400,44 @@ "node": "*" } }, + "node_modules/morgan": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.11.0.tgz", + "integrity": "sha512-zSkVu3t18r39pw4ixfBKvfZi3y2UOqr7d4WYwcj3m8nXpEQK4rPO6GLzs/CExoRgmX3y9EjmmcXqv6jq0SK46g==", + "dev": true, + "license": "MIT", + "dependencies": { + "basic-auth": "~2.0.1", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-finished": "~2.4.1", + "on-headers": "~1.1.0" + }, + "engines": { + "node": ">= 0.8.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/morgan/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/morgan/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -5444,6 +6470,16 @@ "dev": true, "license": "MIT" }, + "node_modules/negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/node-releases": { "version": "2.0.27", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", @@ -5460,6 +6496,42 @@ "node": ">=0.10.0" } }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -5510,6 +6582,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-map": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.4.tgz", + "integrity": "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -5565,6 +6650,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -5591,6 +6686,13 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "license": "MIT" }, + "node_modules/path-to-regexp": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", + "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", + "dev": true, + "license": "MIT" + }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -5600,6 +6702,13 @@ "node": ">=8" } }, + "node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true, + "license": "MIT" + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -5619,6 +6728,25 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pkg-types": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.1.tgz", + "integrity": "sha512-y+ichcgc2LrADuhLNAx8DFjVfgz91pRxfZdI3UDhxHvcVEZsenLO+7XaU5vOp0u/7V/wZ+plyuQxtrDlZJ+yeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.2.4", + "exsolve": "^1.0.8", + "pathe": "^2.0.3" + } + }, + "node_modules/pkg-types/node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, "node_modules/postcss": { "version": "8.5.6", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", @@ -5658,6 +6786,22 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.0.tgz", + "integrity": "sha512-LjIqSIC5VYLzs9WedVmJ2ljNAGnU+DteIClbahu4L/DBeWjZ6iT/k1lAYyu9JUh+1xINxWadaPw/Pl63y/agAw==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -5691,6 +6835,20 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/proxy-from-env": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", @@ -5710,6 +6868,49 @@ "node": ">=6" } }, + "node_modules/qs": { + "version": "6.15.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz", + "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "es-define-property": "^1.0.1", + "side-channel": "^1.1.1" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/react": { "version": "19.2.3", "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz", @@ -5814,9 +7015,9 @@ } }, "node_modules/react-router": { - "version": "7.12.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.12.0.tgz", - "integrity": "sha512-kTPDYPFzDVGIIGNLS5VJykK0HfHLY5MF3b+xj0/tTyNYL1gF1qs7u67Z9jEhQk2sQ98SUaHxlG31g1JtF7IfVw==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.18.0.tgz", + "integrity": "sha512-pTTGt8J+ji1NOmYnjzT+bAJy/1zD+Jp4ziO6cL7T3ZLvXKtusO7BpFqlRXitqpcPVqllsIXFHRMt+2/k3Xn6HQ==", "license": "MIT", "dependencies": { "cookie": "^1.0.1", @@ -5836,12 +7037,12 @@ } }, "node_modules/react-router-dom": { - "version": "7.12.0", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.12.0.tgz", - "integrity": "sha512-pfO9fiBcpEfX4Tx+iTYKDtPbrSLLCbwJ5EqP+SPYQu1VYCXdy79GSj0wttR0U4cikVdlImZuEZ/9ZNCgoaxwBA==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.18.0.tgz", + "integrity": "sha512-Fi0yY6kgtKae/Th2xibdWK0KSdYZ4B53Gyf6wRtomOKWgpNm7H7+DyfDhncdz9FKbpS+1jmDhg3F4WoGJ+yFOA==", "license": "MIT", "dependencies": { - "react-router": "7.12.0" + "react-router": "7.18.0" }, "engines": { "node": ">=20.0.0" @@ -5867,6 +7068,20 @@ "react-dom": ">=16.6.0" } }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/redux": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", @@ -6045,6 +7260,34 @@ "fsevents": "~2.3.2" } }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, "node_modules/scheduler": { "version": "0.27.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", @@ -6061,12 +7304,77 @@ "semver": "bin/semver.js" } }, + "node_modules/send": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.4.1", + "range-parser": "~1.2.1", + "statuses": "~2.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/serve-static": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "~0.19.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/set-cookie-parser": { "version": "2.7.2", "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", "license": "MIT" }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true, + "license": "ISC" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -6090,6 +7398,82 @@ "node": ">=8" } }, + "node_modules/side-channel": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -6109,6 +7493,27 @@ "node": ">=0.10.0" } }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/space-separated-tokens": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", @@ -6119,6 +7524,16 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/stringify-entities": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", @@ -6239,6 +7654,16 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, "node_modules/toposort": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", @@ -6303,6 +7728,20 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/typescript": { "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", @@ -6449,6 +7888,16 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/update-browserslist-db": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", @@ -6499,6 +7948,41 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/valibot": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/valibot/-/valibot-1.4.1.tgz", + "integrity": "sha512-klCmFTz2jeDluy9RwX+F884TCiogtdBJ/YaxSx1EOBYXa3NXNWj8kR1jjN8rzluwojJVWWaHJ4r1U5LfICnM3g==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "typescript": ">=5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/vfile": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", @@ -6602,6 +8086,36 @@ } } }, + "node_modules/vite-node": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", + "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.4.1", + "es-module-lexer": "^1.7.0", + "pathe": "^2.0.3", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vite-node/node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -6635,24 +8149,6 @@ "dev": true, "license": "ISC" }, - "node_modules/yaml": { - "version": "2.8.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", - "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", - "dev": true, - "license": "ISC", - "optional": true, - "peer": true, - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14.6" - }, - "funding": { - "url": "https://github.com/sponsors/eemeli" - } - }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index ba54c77..49fed79 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "prebuild": "npm run generate:curriculum", "build": "tsc -b && vite build", "lint": "eslint .", + "spike:ssg:build": "react-router build . --config spikes/react-router-7-ssg/vite.config.ts", "test:seo": "node --test shared/seo/*.test.mjs", "preview": "vite preview" }, @@ -26,13 +27,16 @@ "react-hook-form": "^7.71.1", "react-markdown": "^10.1.0", "react-redux": "^9.2.0", - "react-router-dom": "^7.12.0", + "react-router-dom": "7.18.0", "rehype-highlight": "^7.0.2", "remark-gfm": "^4.0.1", "yup": "^1.7.1" }, "devDependencies": { "@eslint/js": "^9.39.1", + "@react-router/dev": "7.18.0", + "@react-router/node": "7.18.0", + "@react-router/serve": "7.18.0", "@tailwindcss/vite": "^4.3.0", "@types/node": "^24.10.1", "@types/react": "^19.2.5", @@ -42,6 +46,7 @@ "eslint-plugin-react-hooks": "^7.0.1", "eslint-plugin-react-refresh": "^0.4.24", "globals": "^16.5.0", + "react-router": "7.18.0", "tailwindcss": "^4.3.0", "typescript": "~5.9.3", "typescript-eslint": "^8.46.4", diff --git a/react-router.config.ts b/react-router.config.ts new file mode 100644 index 0000000..9591959 --- /dev/null +++ b/react-router.config.ts @@ -0,0 +1,30 @@ +import fs from "node:fs"; +import { fileURLToPath } from "node:url"; +import type { Config } from "@react-router/dev/config"; + +type SeoManifest = { + entries: Array<{ + canonicalPath: string; + }>; +}; + +const manifestPath = fileURLToPath( + new URL("./shared/content/curriculumSeoManifest.generated.json", import.meta.url) +); +const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8")) as SeoManifest; +const topicPath = manifest.entries[0]?.canonicalPath; + +if (!topicPath) { + throw new Error("The curriculum SEO manifest must contain at least one topic."); +} + +export default { + appDirectory: "spikes/react-router-7-ssg/app", + buildDirectory: "spikes/react-router-7-ssg/build", + future: { + v8_trailingSlashAwareDataRequests: true, + }, + prerender: ["/", "/content/", topicPath], + routeDiscovery: { mode: "initial" }, + ssr: false, +} satisfies Config; diff --git a/spikes/react-router-7-ssg/README.md b/spikes/react-router-7-ssg/README.md new file mode 100644 index 0000000..9f8c0f9 --- /dev/null +++ b/spikes/react-router-7-ssg/README.md @@ -0,0 +1,29 @@ +# React Router 7 SSG Spike + +This isolated proof tests React Router framework-mode pre-rendering without +changing the production Vite SPA. + +The findings and implementation recommendation are recorded in +[`docs/spikes/react-router-7-ssg-spike.md`](../../docs/spikes/react-router-7-ssg-spike.md). + +Run it from the repository root: + +```bash +npm run spike:ssg:build +``` + +The spike intentionally proves only the architecture boundaries: + +- `ssr: false` static-file output +- initial route discovery without a runtime `/__manifest` endpoint +- dynamic pre-render paths read from the Node-readable curriculum SEO manifest +- Markdown and metadata loaded before rendering +- trailing-slash static output +- hydration data emitted alongside HTML + +React Router requires `react-router.config.ts` at the framework project root. +The repository-level config points back to this isolated app directory; the +normal Vite build does not use it. + +The generated output is written to `build/client` inside this directory and is +ignored by Git. diff --git a/spikes/react-router-7-ssg/app/entry.server.tsx b/spikes/react-router-7-ssg/app/entry.server.tsx new file mode 100644 index 0000000..95df630 --- /dev/null +++ b/spikes/react-router-7-ssg/app/entry.server.tsx @@ -0,0 +1,36 @@ +import { PassThrough } from "node:stream"; +import { createReadableStreamFromReadable } from "@react-router/node"; +import type { EntryContext } from "react-router"; +import { ServerRouter } from "react-router"; +import { renderToPipeableStream } from "react-dom/server"; + +export default function handleRequest( + request: Request, + responseStatusCode: number, + responseHeaders: Headers, + routerContext: EntryContext +) { + return new Promise((resolve, reject) => { + const { pipe } = renderToPipeableStream( + , + { + onAllReady() { + const body = new PassThrough(); + const stream = createReadableStreamFromReadable(body); + + responseHeaders.set("Content-Type", "text/html"); + pipe(body); + resolve( + new Response(stream, { + headers: responseHeaders, + status: responseStatusCode, + }) + ); + }, + onShellError(error) { + reject(error); + }, + } + ); + }); +} diff --git a/spikes/react-router-7-ssg/app/lib/content.server.ts b/spikes/react-router-7-ssg/app/lib/content.server.ts new file mode 100644 index 0000000..9460823 --- /dev/null +++ b/spikes/react-router-7-ssg/app/lib/content.server.ts @@ -0,0 +1,60 @@ +import fs from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { buildSeoMetadata } from "../../../../shared/seo/buildSeoMetadata.mjs"; + +type SeoEntry = { + canonicalPath: string; + contentPath: string; + id: string; + seoDescription: string; + seoTitle: string; + subtopic: string; +}; + +type SeoManifest = { + entries: SeoEntry[]; +}; + +const repositoryRoot = fileURLToPath(new URL("../../../../", import.meta.url)); +const manifestPath = path.join( + repositoryRoot, + "shared/content/curriculumSeoManifest.generated.json" +); +const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8")) as SeoManifest; + +export function loadTopic(topicId: string) { + const entry = manifest.entries.find((item) => item.id === topicId); + + if (!entry) { + throw new Response("Not Found", { status: 404 }); + } + + const source = fs.readFileSync( + path.join(repositoryRoot, entry.contentPath), + "utf8" + ); + + return { + entry, + markdown: stripFrontmatter(source), + metadata: buildSeoMetadata({ + contentEntry: entry, + pathname: entry.canonicalPath, + }), + }; +} + +export function getFirstTopicPath() { + const canonicalPath = manifest.entries[0]?.canonicalPath; + + if (!canonicalPath) { + throw new Error("The curriculum SEO manifest must contain at least one topic."); + } + + return canonicalPath; +} + +function stripFrontmatter(markdown: string) { + return markdown.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/, "").trim(); +} diff --git a/spikes/react-router-7-ssg/app/root.tsx b/spikes/react-router-7-ssg/app/root.tsx new file mode 100644 index 0000000..bc07e4b --- /dev/null +++ b/spikes/react-router-7-ssg/app/root.tsx @@ -0,0 +1,30 @@ +import { + Links, + Meta, + Outlet, + Scripts, + ScrollRestoration, +} from "react-router"; + +export function Layout({ children }: { children: React.ReactNode }) { + return ( + + + + + + + + + + {children} + + + + + ); +} + +export default function Root() { + return ; +} diff --git a/spikes/react-router-7-ssg/app/routes.ts b/spikes/react-router-7-ssg/app/routes.ts new file mode 100644 index 0000000..d2bde1f --- /dev/null +++ b/spikes/react-router-7-ssg/app/routes.ts @@ -0,0 +1,7 @@ +import { index, route, type RouteConfig } from "@react-router/dev/routes"; + +export default [ + index("./routes/home.tsx"), + route("content/", "./routes/content.tsx"), + route("content/:topicId/", "./routes/content-topic.tsx"), +] satisfies RouteConfig; diff --git a/spikes/react-router-7-ssg/app/routes/content-topic.tsx b/spikes/react-router-7-ssg/app/routes/content-topic.tsx new file mode 100644 index 0000000..1242ac1 --- /dev/null +++ b/spikes/react-router-7-ssg/app/routes/content-topic.tsx @@ -0,0 +1,44 @@ +import type { LoaderFunctionArgs, MetaFunction } from "react-router"; +import { Link, useLoaderData } from "react-router"; +import { loadTopic } from "../lib/content.server"; + +export function loader({ params }: LoaderFunctionArgs) { + return loadTopic(params.topicId ?? ""); +} + +export const meta: MetaFunction = ({ data }) => { + if (!data) { + return [{ title: "Page Not Found | DEV_NET_CORE" }]; + } + + return [ + { title: data.metadata.title }, + { name: "description", content: data.metadata.description }, + { tagName: "link", rel: "canonical", href: data.metadata.canonicalUrl }, + { property: "og:title", content: data.metadata.openGraph.title }, + { + property: "og:description", + content: data.metadata.openGraph.description, + }, + { property: "og:url", content: data.metadata.openGraph.url }, + { property: "og:image", content: data.metadata.openGraph.image }, + ]; +}; + +export default function ContentTopic() { + const { entry, markdown, metadata } = useLoaderData(); + + return ( +
+ Curriculum +

{entry.subtopic}

+
{markdown}
+ DEV_NET_CORE | Developer Interview Preparation route.sitemap).map((route) => route.path), + ...contentEntries.map((entry) => entry.canonicalPath), + ]; + const uniquePaths = new Set(paths); + + assert.equal( + uniquePaths.size, + paths.length, + "Sitemap paths must be unique." + ); + + for (const pathname of paths) { + assert.ok( + isCanonicalPath(pathname), + `Sitemap path "${pathname}" must be canonical and trailing-slash.` + ); + } + + return paths; +} + +export function createSitemapXml(paths) { + const urlEntries = paths + .map( + (pathname) => + ` \n ${escapeXml( + `${SEO_SITE_ORIGIN}${pathname}` + )}\n ` + ) + .join("\n"); + + return ( + '\n' + + '\n' + + `${urlEntries}\n` + + "\n" + ); +} + +export function createRobotsTxt() { + return ( + "User-agent: *\n" + + "Allow: /\n\n" + + `Sitemap: ${SEO_SITE_ORIGIN}/sitemap.xml\n` + ); +} + +export function addNotFoundMetadata(html) { + const startMarker = ""; + const endMarker = ""; + const managedBlockPattern = new RegExp( + `${escapeRegExp(startMarker)}[\\s\\S]*?${escapeRegExp(endMarker)}`, + "g" + ); + const withoutManagedBlock = html.replace(managedBlockPattern, ""); + const metadataBlock = + `${startMarker}` + + "Page Not Found | DEV_NET_CORE" + + '' + + '' + + `${endMarker}`; + + assert.ok( + withoutManagedBlock.includes(""), + "Framework fallback output must contain a closing head tag." + ); + assert.ok( + withoutManagedBlock.includes("404 - Page Not Found"), + "Framework fallback output must contain visible Not Found content." + ); + assert.equal( + /]*\brel=["']canonical["']/i.test(withoutManagedBlock), + false, + "Framework fallback must not contain a canonical link." + ); + assert.equal( + withoutManagedBlock.includes('type="application/ld+json"'), + false, + "Framework fallback must not contain structured data." + ); + + return withoutManagedBlock.replace( + "", + `${metadataBlock}` + ); +} + +function getRouteHtmlPath(pathname) { + return pathname === "/" + ? path.join(clientRoot, "index.html") + : path.join(clientRoot, ...pathname.split("/").filter(Boolean), "index.html"); +} + +function isCanonicalPath(value) { + return ( + value === "/" || + (typeof value === "string" && + /^\/(?:[a-z0-9]+(?:-[a-z0-9]+)*\/)+$/.test(value)) + ); +} + +function escapeXml(value) { + return String(value) + .replaceAll("&", "&") + .replaceAll("<", "<") + .replaceAll(">", ">") + .replaceAll('"', """) + .replaceAll("'", "'"); +} + +function escapeRegExp(value) { + return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); +} diff --git a/scripts/framework-output-budgets.mjs b/scripts/framework-output-budgets.mjs new file mode 100644 index 0000000..764eeb8 --- /dev/null +++ b/scripts/framework-output-budgets.mjs @@ -0,0 +1,7 @@ +export const frameworkOutputBudgets = Object.freeze({ + maximumBuildDurationSeconds: 60, + maximumHtmlBytes: 512 * 1024, + maximumRouteCount: 250, + maximumRouteDataBytes: 100 * 1024, + maximumTotalBytes: 80 * 1024 * 1024, +}); diff --git a/scripts/preview-framework.mjs b/scripts/preview-framework.mjs index cc9b311..12d7290 100644 --- a/scripts/preview-framework.mjs +++ b/scripts/preview-framework.mjs @@ -3,7 +3,8 @@ import http from "node:http"; import path from "node:path"; const clientRoot = path.resolve("build-framework", "client"); -const fallbackPath = path.join(clientRoot, "__spa-fallback.html"); +const frameworkFallbackPath = path.join(clientRoot, "__spa-fallback.html"); +const notFoundPath = path.join(clientRoot, "404.html"); const requestedPort = Number(process.env.PORT || process.argv[2] || 4176); const contentTypes = new Map([ [".css", "text/css; charset=utf-8"], @@ -18,16 +19,16 @@ const contentTypes = new Map([ [".webp", "image/webp"], ]); -if (!fs.existsSync(fallbackPath)) { +if (!fs.existsSync(frameworkFallbackPath)) { throw new Error("Run npm run build:framework before previewing."); } const server = http.createServer((request, response) => { const requestUrl = new URL(request.url || "/", "http://localhost"); const requestPath = decodeURIComponent(requestUrl.pathname); - const filePath = resolveRequestPath(requestPath); + const { filePath, statusCode } = resolveRequestPath(requestPath); - response.statusCode = 200; + response.statusCode = statusCode; response.setHeader( "Content-Type", contentTypes.get(path.extname(filePath).toLowerCase()) || @@ -53,11 +54,18 @@ function resolveRequestPath(requestPath) { fs.existsSync(candidate) && fs.statSync(candidate).isFile() ) { - return candidate; + return { filePath: candidate, statusCode: 200 }; } } - return fallbackPath; + const fallbackPath = fs.existsSync(notFoundPath) + ? notFoundPath + : frameworkFallbackPath; + + return { + filePath: fallbackPath, + statusCode: fs.existsSync(notFoundPath) ? 404 : 200, + }; } function isInsideClientRoot(candidate) { diff --git a/scripts/theme-bootstrap.test.mjs b/scripts/theme-bootstrap.test.mjs new file mode 100644 index 0000000..2e56d6a --- /dev/null +++ b/scripts/theme-bootstrap.test.mjs @@ -0,0 +1,79 @@ +import assert from "node:assert/strict"; +import fs from "node:fs"; +import test from "node:test"; +import vm from "node:vm"; + +const bootstrapSource = fs.readFileSync( + new URL("../public/theme-bootstrap.js", import.meta.url), + "utf8" +); + +test("uses the stable dark default without a stored preference", () => { + const result = runBootstrap(); + + assert.equal(result.dataset.theme, "dark"); + assert.deepEqual(result.classes, ["dark"]); + assert.equal(result.style.colorScheme, "dark"); +}); + +test("restores an explicit light preference", () => { + const result = runBootstrap({ storedMode: "light" }); + + assert.equal(result.dataset.theme, "light"); + assert.deepEqual(result.classes, ["light"]); + assert.equal(result.style.colorScheme, "light"); +}); + +test("ignores invalid stored values", () => { + const result = runBootstrap({ storedMode: "system" }); + + assert.equal(result.dataset.theme, "dark"); + assert.deepEqual(result.classes, ["dark"]); +}); + +test("keeps dark mode when browser storage is unavailable", () => { + const result = runBootstrap({ storageUnavailable: true }); + + assert.equal(result.dataset.theme, "dark"); + assert.deepEqual(result.classes, ["dark"]); +}); + +function runBootstrap({ + storedMode = null, + storageUnavailable = false, +} = {}) { + const classes = new Set(); + const root = { + classList: { + toggle(className, force) { + if (force) { + classes.add(className); + } else { + classes.delete(className); + } + }, + }, + dataset: {}, + style: {}, + }; + const localStorage = { + getItem() { + if (storageUnavailable) { + throw new Error("Storage is unavailable."); + } + + return storedMode; + }, + }; + + vm.runInNewContext(bootstrapSource, { + document: { documentElement: root }, + window: { localStorage }, + }); + + return { + classes: [...classes].sort(), + dataset: root.dataset, + style: root.style, + }; +} diff --git a/scripts/validate-framework-build.mjs b/scripts/validate-framework-build.mjs index 9147518..37633fc 100644 --- a/scripts/validate-framework-build.mjs +++ b/scripts/validate-framework-build.mjs @@ -6,9 +6,14 @@ import { SEO_SITE_ORIGIN, } from "../shared/seo/buildSeoMetadata.mjs"; import { staticSeoRoutes } from "../shared/seo/seoRoutes.mjs"; +import { frameworkOutputBudgets } from "./framework-output-budgets.mjs"; const repositoryRoot = process.cwd(); const clientRoot = path.join(repositoryRoot, "build-framework", "client"); +const frameworkFallbackPath = path.join(clientRoot, "__spa-fallback.html"); +const notFoundPath = path.join(clientRoot, "404.html"); +const robotsPath = path.join(clientRoot, "robots.txt"); +const sitemapPath = path.join(clientRoot, "sitemap.xml"); const manifest = JSON.parse( fs.readFileSync( path.join( @@ -32,13 +37,31 @@ const expectedRoutes = [ })), ]; -assert.ok(fs.existsSync(path.join(repositoryRoot, "dist", "index.html"))); -assert.ok(fs.existsSync(path.join(clientRoot, "__spa-fallback.html"))); +const spaIndexPath = path.join(repositoryRoot, "dist", "index.html"); + +assert.ok(fs.existsSync(spaIndexPath)); +assert.ok(fs.existsSync(frameworkFallbackPath)); +assert.ok(fs.existsSync(path.join(clientRoot, "theme-bootstrap.js"))); +assert.ok(fs.existsSync(path.join(repositoryRoot, "dist", "theme-bootstrap.js"))); +assert.ok( + fs.readFileSync(spaIndexPath, "utf8").includes( + '' + ), + "The SPA output must load the static theme bootstrap." +); +assert.ok( + fs.readFileSync(frameworkFallbackPath, "utf8").includes( + '' + ), + "The framework SPA fallback must load the static theme bootstrap." +); assert.equal( fs.existsSync(path.join(repositoryRoot, "build-framework", "server")), false ); +validateFinalizedOutput(); + for (const route of expectedRoutes) { validateRoute(route); } @@ -58,6 +81,8 @@ for (const routePath of [ } const generatedIndexFiles = findFiles(clientRoot, "index.html"); +const generatedRouteDataFiles = findFiles(clientRoot, "_.data"); +const generatedClientFiles = findAllFiles(clientRoot); assert.equal( generatedIndexFiles.length, @@ -65,6 +90,12 @@ assert.equal( "Framework output contains an unexpected number of static route artifacts." ); +validateOutputBudgets({ + generatedClientFiles, + generatedIndexFiles, + generatedRouteDataFiles, +}); + console.log( `Validated ${expectedRoutes.length} framework route artifacts ` + `(${manifest.entries.length} curriculum topics).` @@ -115,6 +146,10 @@ function validateRoute({ contentEntry, pathname }) { html.includes("'), + `Missing static theme bootstrap for ${pathname}.` + ); assert.equal( html.includes("Loading content..."), false, @@ -136,6 +171,120 @@ function validateRoute({ contentEntry, pathname }) { } } +function validateFinalizedOutput() { + assert.ok(fs.existsSync(sitemapPath), "Missing finalized sitemap.xml."); + assert.ok(fs.existsSync(robotsPath), "Missing finalized robots.txt."); + assert.ok(fs.existsSync(notFoundPath), "Missing dedicated 404.html."); + + const sitemap = fs.readFileSync(sitemapPath, "utf8"); + const robots = fs.readFileSync(robotsPath, "utf8"); + const expectedUrls = expectedRoutes.map( + ({ pathname }) => `${SEO_SITE_ORIGIN}${pathname}` + ); + const sitemapUrls = Array.from( + sitemap.matchAll(/([^<]+)<\/loc>/g), + (match) => match[1] + ); + + assert.ok( + sitemap.startsWith(''), + "Sitemap must declare UTF-8 XML." + ); + assert.ok( + sitemap.includes( + '' + ), + "Sitemap must use the standard sitemap namespace." + ); + assert.equal(sitemap.includes(""), false); + assert.equal(sitemap.includes(""), false); + assert.equal(sitemap.includes(""), false); + assert.equal( + new Set(sitemapUrls).size, + sitemapUrls.length, + "Sitemap URLs must be unique." + ); + assert.deepEqual( + sitemapUrls, + expectedUrls, + "Sitemap URLs must exactly match the validated static route set." + ); + + for (const sitemapUrl of sitemapUrls) { + const url = new URL(sitemapUrl); + + assert.equal(url.origin, SEO_SITE_ORIGIN); + assert.ok( + url.pathname === "/" || url.pathname.endsWith("/"), + `Sitemap URL ${sitemapUrl} must use a trailing slash.` + ); + assert.ok( + fs.existsSync(getRouteHtmlPath(url.pathname)), + `Sitemap URL ${sitemapUrl} has no static HTML artifact.` + ); + } + + for (const excludedPath of [ + "/bug-report/", + "/changelog/", + "/practice/", + "/simulation/", + "/simulation/setup/", + ]) { + assert.equal( + sitemapUrls.includes(`${SEO_SITE_ORIGIN}${excludedPath}`), + false, + `${excludedPath} must remain excluded from the sitemap.` + ); + } + + assert.ok( + robots.includes(`Sitemap: ${SEO_SITE_ORIGIN}/sitemap.xml`), + "Candidate robots.txt must advertise the finalized sitemap." + ); + assert.equal( + fs + .readFileSync( + path.join(repositoryRoot, "public", "robots.txt"), + "utf8" + ) + .includes("Sitemap:"), + false, + "The legacy SPA robots source must not advertise the candidate sitemap." + ); + + for (const artifactPath of [frameworkFallbackPath, notFoundPath]) { + const html = fs.readFileSync(artifactPath, "utf8"); + + assert.ok(html.includes("Page Not Found | DEV_NET_CORE")); + assert.ok(html.includes('')); + assert.ok(html.includes("404 - Page Not Found")); + assert.ok(html.includes('')); + assert.ok(html.includes("window.__reactRouterContext")); + assert.equal( + /]*\brel=["']canonical["']/i.test(html), + false, + `${path.basename(artifactPath)} must not contain a canonical link.` + ); + assert.equal( + html.includes('property="og:url"'), + false, + `${path.basename(artifactPath)} must not contain an Open Graph URL.` + ); + assert.equal( + html.includes('type="application/ld+json"'), + false, + `${path.basename(artifactPath)} must not contain structured data.` + ); + } + + assert.notEqual( + fs.readFileSync(notFoundPath, "utf8"), + fs.readFileSync(path.join(clientRoot, "index.html"), "utf8"), + "Dedicated 404.html must not be a copy of the homepage." + ); +} + function getRouteHtmlPath(pathname) { return pathname === "/" ? path.join(clientRoot, "index.html") @@ -160,6 +309,72 @@ function findFiles(directory, fileName) { }); } +function findAllFiles(directory) { + return fs.readdirSync(directory, { withFileTypes: true }).flatMap((entry) => { + const entryPath = path.join(directory, entry.name); + + return entry.isDirectory() ? findAllFiles(entryPath) : [entryPath]; + }); +} + +function validateOutputBudgets({ + generatedClientFiles, + generatedIndexFiles, + generatedRouteDataFiles, +}) { + const totalBytes = sumFileSizes(generatedClientFiles); + const largestHtmlBytes = maximumFileSize(generatedIndexFiles); + const largestRouteDataBytes = maximumFileSize(generatedRouteDataFiles); + + assert.ok( + generatedIndexFiles.length <= frameworkOutputBudgets.maximumRouteCount, + `Framework route count ${generatedIndexFiles.length} exceeds the ` + + `${frameworkOutputBudgets.maximumRouteCount} route budget.` + ); + assert.ok( + totalBytes <= frameworkOutputBudgets.maximumTotalBytes, + `Framework client output ${formatMiB(totalBytes)} exceeds the ` + + `${formatMiB(frameworkOutputBudgets.maximumTotalBytes)} budget.` + ); + assert.ok( + largestHtmlBytes <= frameworkOutputBudgets.maximumHtmlBytes, + `Largest route HTML ${formatKiB(largestHtmlBytes)} exceeds the ` + + `${formatKiB(frameworkOutputBudgets.maximumHtmlBytes)} budget.` + ); + assert.ok( + largestRouteDataBytes <= frameworkOutputBudgets.maximumRouteDataBytes, + `Largest route data ${formatKiB(largestRouteDataBytes)} exceeds the ` + + `${formatKiB(frameworkOutputBudgets.maximumRouteDataBytes)} budget.` + ); + + console.log( + "Framework output budgets: " + + `${generatedIndexFiles.length}/${frameworkOutputBudgets.maximumRouteCount} routes, ` + + `${formatMiB(totalBytes)}/${formatMiB(frameworkOutputBudgets.maximumTotalBytes)} total, ` + + `${formatKiB(largestHtmlBytes)}/${formatKiB(frameworkOutputBudgets.maximumHtmlBytes)} max HTML, ` + + `${formatKiB(largestRouteDataBytes)}/${formatKiB(frameworkOutputBudgets.maximumRouteDataBytes)} max route data.` + ); +} + +function sumFileSizes(files) { + return files.reduce((total, filePath) => total + fs.statSync(filePath).size, 0); +} + +function maximumFileSize(files) { + return files.reduce( + (maximum, filePath) => Math.max(maximum, fs.statSync(filePath).size), + 0 + ); +} + +function formatKiB(bytes) { + return `${(bytes / 1024).toFixed(2)} KiB`; +} + +function formatMiB(bytes) { + return `${(bytes / 1024 / 1024).toFixed(2)} MiB`; +} + function escapeHtml(value) { return String(value) .replaceAll("&", "&") diff --git a/src/index.css b/src/index.css index c799e6b..7131c85 100644 --- a/src/index.css +++ b/src/index.css @@ -13,6 +13,82 @@ -moz-osx-font-smoothing: grayscale; } +html[data-theme="dark"] { + color-scheme: dark; + --color-background: #111318; + --color-on-background: #e2e2e8; + --color-surface: #111318; + --color-surface-container-lowest: #0c0e12; + --color-surface-container-low: #1a1c20; + --color-surface-container: #1e2024; + --color-surface-container-high: #282a2e; + --color-on-surface: #e2e2e8; + --color-on-surface-variant: #b9cacb; + --color-outline: #849495; + --color-outline-variant: #3b494b; + --color-primary: #dbfcff; + --color-on-primary: #00363a; + --color-primary-container: #00f0ff; + --color-on-primary-container: #006970; + --color-secondary: #d8b9ff; + --color-tertiary: #d1fff5; + --color-error: #ffb4ab; + --color-glass: rgba(18, 23, 33, 0.8); + --color-card-border: rgba(255, 255, 255, 0.1); + --color-accent-border: rgba(0, 240, 255, 0.2); + --color-accent-soft: rgba(0, 240, 255, 0.1); + --color-accent-hover: rgba(0, 240, 255, 0.08); + --color-card-hover-border: rgba(0, 240, 255, 0.45); + --color-chip-background: rgba(255, 255, 255, 0.05); + --color-chip-text: #94a3b8; + --color-code-surface: #0c0e12; + --color-feature-band: rgba(26, 28, 32, 0.5); + --color-grid-line: rgba(0, 240, 255, 0.05); + --color-subtle-text: #64748b; + --color-warning: rgba(234, 179, 8, 0.5); + --shadow-accent-glow: 0 0 20px rgba(0, 240, 255, 0.3); + --shadow-card-hover: 0 0 15px rgba(0, 240, 255, 0.1); + --shadow-panel-glow: 0 0 20px rgba(0, 240, 255, 0.3); +} + +html[data-theme="light"] { + color-scheme: light; + --color-background: #f9f9ff; + --color-on-background: #1a1c20; + --color-surface: #f9f9ff; + --color-surface-container-lowest: #ffffff; + --color-surface-container-low: #f3f3f9; + --color-surface-container: #ededf3; + --color-surface-container-high: #e8e8ee; + --color-on-surface: #1a1c20; + --color-on-surface-variant: #3b494b; + --color-outline: #6a7a7b; + --color-outline-variant: #b9cacb; + --color-primary: #006970; + --color-on-primary: #ffffff; + --color-primary-container: #ff0000; + --color-on-primary-container: #006970; + --color-secondary: #7b24dc; + --color-tertiary: #006b5f; + --color-error: #ba1a1a; + --color-glass: rgba(255, 255, 255, 0.72); + --color-card-border: rgba(106, 122, 123, 0.24); + --color-accent-border: rgba(0, 240, 255, 0.3); + --color-accent-soft: rgba(0, 240, 255, 0.15); + --color-accent-hover: rgba(0, 240, 255, 0.14); + --color-card-hover-border: rgba(0, 105, 112, 0.4); + --color-chip-background: rgba(0, 105, 112, 0.08); + --color-chip-text: #3b494b; + --color-code-surface: #ffffff; + --color-feature-band: rgba(243, 243, 249, 0.75); + --color-grid-line: rgba(0, 105, 112, 0.06); + --color-subtle-text: #6a7a7b; + --color-warning: rgba(202, 138, 4, 0.55); + --shadow-accent-glow: 0 0 20px rgba(0, 240, 255, 0.18); + --shadow-card-hover: 0 0 18px rgba(0, 240, 255, 0.14); + --shadow-panel-glow: 0 0 20px rgba(0, 240, 255, 0.16); +} + html { background: var(--color-background, #111318); } diff --git a/src/theme/ThemeModeProvider.tsx b/src/theme/ThemeModeProvider.tsx index fff39ae..9b094a9 100644 --- a/src/theme/ThemeModeProvider.tsx +++ b/src/theme/ThemeModeProvider.tsx @@ -1,10 +1,18 @@ -import { useEffect, useMemo, useState } from "react"; +import { + useCallback, + useLayoutEffect, + useMemo, + useRef, + useState, +} from "react"; import type { ReactNode } from "react"; import { CssBaseline, ThemeProvider } from "@mui/material"; import { appThemeTokens, applyThemeVariables, buildMuiTheme, + persistThemeMode, + readBootstrappedThemeMode, ThemeModeContext, type AppThemeMode, } from "./themeMode"; @@ -18,12 +26,32 @@ export function AppThemeModeProvider({ children, initialMode = "dark", }: AppThemeModeProviderProps) { - const [mode, setMode] = useState(initialMode); + const [mode, setModeState] = useState(initialMode); + const hasReadBootstrappedMode = useRef(false); const tokens = appThemeTokens[mode]; - useEffect(() => { + useLayoutEffect(() => { + if (!hasReadBootstrappedMode.current) { + hasReadBootstrappedMode.current = true; + const bootstrappedMode = readBootstrappedThemeMode(initialMode); + + if (bootstrappedMode !== mode) { + // The bootstrap is an external pre-hydration input. Reconcile it in a + // layout effect so server markup hydrates as dark before the saved + // choice is applied, while still updating before the browser paints. + // eslint-disable-next-line react-hooks/set-state-in-effect + setModeState(bootstrappedMode); + return; + } + } + applyThemeVariables(tokens); - }, [tokens]); + }, [initialMode, mode, tokens]); + + const setMode = useCallback((nextMode: AppThemeMode) => { + persistThemeMode(nextMode); + setModeState(nextMode); + }, []); const muiTheme = useMemo(() => buildMuiTheme(mode, tokens), [mode, tokens]); @@ -32,10 +60,9 @@ export function AppThemeModeProvider({ mode, tokens, setMode, - toggleMode: () => - setMode((current) => (current === "dark" ? "light" : "dark")), + toggleMode: () => setMode(mode === "dark" ? "light" : "dark"), }), - [mode, tokens] + [mode, setMode, tokens] ); return ( diff --git a/src/theme/themeMode.ts b/src/theme/themeMode.ts index 9055d9f..a6b6919 100644 --- a/src/theme/themeMode.ts +++ b/src/theme/themeMode.ts @@ -3,6 +3,8 @@ import { createTheme, type PaletteMode } from "@mui/material"; export type AppThemeMode = "dark" | "light"; +export const THEME_MODE_STORAGE_KEY = "dev-net-core:theme"; + export type AppThemeTokens = { mode: AppThemeMode; background: string; @@ -145,6 +147,7 @@ export function applyThemeVariables(tokens: AppThemeTokens) { root.dataset.theme = tokens.mode; root.classList.toggle("dark", tokens.mode === "dark"); root.classList.toggle("light", tokens.mode === "light"); + root.style.colorScheme = tokens.mode; root.style.setProperty("--color-background", tokens.background); root.style.setProperty("--color-on-background", tokens.onBackground); @@ -197,6 +200,42 @@ export function applyThemeVariables(tokens: AppThemeTokens) { root.style.setProperty("--shadow-panel-glow", tokens.panelGlow); } +export function readBootstrappedThemeMode( + fallback: AppThemeMode +): AppThemeMode { + if (typeof document === "undefined") { + return fallback; + } + + const documentMode = document.documentElement.dataset.theme; + + if (documentMode === "dark" || documentMode === "light") { + return documentMode; + } + + try { + const storedMode = window.localStorage.getItem(THEME_MODE_STORAGE_KEY); + + return storedMode === "dark" || storedMode === "light" + ? storedMode + : fallback; + } catch { + return fallback; + } +} + +export function persistThemeMode(mode: AppThemeMode) { + if (typeof window === "undefined") { + return; + } + + try { + window.localStorage.setItem(THEME_MODE_STORAGE_KEY, mode); + } catch { + // Theme changes still work for the current page when storage is unavailable. + } +} + export function buildMuiTheme(mode: PaletteMode, tokens: AppThemeTokens) { return createTheme({ palette: { From 02a57e3523e4b95ba54b3866cb5910f6cbd4c470 Mon Sep 17 00:00:00 2001 From: Minh Vu Date: Sun, 28 Jun 2026 12:29:09 +0700 Subject: [PATCH 17/20] docs: add SEO improvement plan --- .../dev-net-core-seo-improvement-plan.md | 3405 +++++++++++++++++ 1 file changed, 3405 insertions(+) create mode 100644 docs/plans/dev-net-core-seo-improvement-plan.md diff --git a/docs/plans/dev-net-core-seo-improvement-plan.md b/docs/plans/dev-net-core-seo-improvement-plan.md new file mode 100644 index 0000000..349d099 --- /dev/null +++ b/docs/plans/dev-net-core-seo-improvement-plan.md @@ -0,0 +1,3405 @@ +# DEV_NET_CORE SEO Improvement Plan + +## Purpose + +This plan documents the steps needed to improve SEO for DEV_NET_CORE so the website and its Markdown-based content can be discovered, crawled, indexed, and ranked by Google and other search engines. + +Website: + +```txt +https://www.dev-net-core.com/ +``` + +Repository: + +```txt +https://github.com/CircleQMinh/dev-net-core +``` + +Recommended repo location for this file: + +```txt +docs/plans/seo-improvement-plan.md +``` + +## Current Context + +DEV_NET_CORE is a React + TypeScript + Vite single-page application. + +The website has valuable technical interview preparation content, especially under content routes such as: + +```txt +/content/ +/content/:topicId/ +/practice/ +/simulation/ +/roadmap/ +/about-us/ +``` + +The project uses Markdown content and a generated curriculum manifest. This is good for SEO because each Markdown topic can become a searchable landing page if it has: + +- A stable URL +- Real crawlable links +- Unique metadata +- A sitemap entry +- Search-friendly page content +- Ideally pre-rendered HTML + +The most important SEO goal is to make each curriculum topic searchable as its own page. + +Example search targets: + +```txt +C# async await interview questions +Entity Framework change tracker interview questions +React useEffect interview questions +SQL isolation levels interview questions +ASP.NET Core middleware interview questions +Azure Functions interview questions +Clean Architecture .NET interview questions +``` + +## Goals + +The SEO work should achieve these goals: + +- Make the website discoverable by Google and other search engines. +- Make every important content page available as an indexable URL. +- Generate a sitemap from the curriculum content. +- Add `robots.txt`. +- Add unique metadata for important routes. +- Add canonical URLs. +- Add Open Graph metadata for sharing. +- Add structured data where useful. +- Improve Markdown content so each page has strong search intent. +- Add pre-rendered/static HTML for every sitemap-included route before submitting sitemap URLs. +- Set up Google Search Console and Bing Webmaster Tools after production validation passes. + +## Non-Goals + +This plan does not include: + +- Adding paid SEO tools. +- Rebuilding the entire site in Next.js or Astro immediately. +- Adding a backend. +- Adding ads. +- Adding analytics beyond what is needed for SEO validation. +- Rewriting all curriculum content at once. + +## High-Level Recommendation + +Implement SEO in this order: + +```txt +1. Add robots.txt +2. Extend the curriculum manifest for SEO fields and generate sitemap.xml from it +3. Add safe site-wide default metadata and a default social preview asset +4. Add dynamic SPA metadata with react-helmet-async as an interim improvement +5. Add crawlable internal links, canonical URLs, and explicit trailing-slash route support +6. Add a shared SEO route config and complete the pre-rendering architecture decision gate +7. Run the React Router 7 SSG spike before choosing the final pre-rendering approach +8. Complete the SSR/SSG readiness refactor +9. Add content route data loading and hydration seeding +10. Add pre-render/static generation for all sitemap-eligible routes +11. Validate production sitemap URLs and source HTML metadata +12. Submit the sitemap to Google Search Console and Bing Webmaster Tools +13. Monitor indexing and optionally add scheduled production validation +``` + +The highest-impact items are: + +```txt +manifest-driven sitemap + real links + default metadata + shared route config + pre-rendering architecture gate + SSG-ready app structure + route data hydration + pre-rendered sitemap-eligible routes + production validation + Search Console +``` + +## Phase 1: Basic Crawlability + +### 1. Add `robots.txt` + +Create: + +```txt +public/robots.txt +``` + +Content: + +```txt +User-agent: * +Allow: / + +Sitemap: https://www.dev-net-core.com/sitemap.xml +``` + +### Purpose + +This tells crawlers that the site can be crawled and where to find the sitemap. + +Important timing caveat: if `robots.txt` is deployed before the full production sitemap is ready, do not expose unvalidated URLs through the `Sitemap:` line yet. Either omit the `Sitemap:` line temporarily or expose only a limited sitemap containing URLs that already pass the production validation gate. + +Because DEV_NET_CORE is already in production, treat sitemap exposure as a production change. A public `sitemap.xml` or `robots.txt` `Sitemap:` line can be discovered before Search Console submission, so expose only validated URLs until the full SEO postbuild output is ready. + +### Acceptance Criteria + +- `https://www.dev-net-core.com/robots.txt` is accessible. +- The file points to `https://www.dev-net-core.com/sitemap.xml` only when the sitemap URLs are ready for production discovery. +- No important assets, pages, or routes are blocked. + +### Decision: `noindex` vs `robots.txt` + +Do not block `noindex` pages in `robots.txt`. + +Crawlers must be able to access the page in order to see the `noindex` meta tag or `X-Robots-Tag` header. + +Use `robots.txt` only to control crawling of areas that should not be crawled. Use `noindex` to prevent specific pages from appearing in search results. + +## Phase 2: Sitemap Generation + +### 2. Generate `sitemap.xml` + +Create a sitemap that includes important static routes and curriculum routes only after those routes have production-ready static/pre-rendered output. + +Required sitemap-eligible routes for the first submission: + +```txt +/ +/content/ +/content/:topicId/ +/roadmap/ +/about-us/ +/privacy/ +/terms/ +``` + +Optional sitemap-eligible routes: + +```txt +/changelog/ +/simulation/ +``` + +Do not include optional routes unless they have enough unique visible content, are intended to rank, and pass the same static/pre-rendered production validation as required routes. + +The sitemap should be generated from the existing curriculum manifest so new Markdown topics are automatically included. + +### Recommended Implementation + +Add a script such as: + +```txt +scripts/seo-postbuild.mjs +``` + +The script should: + +1. Run after Vite has produced `dist`. +2. Load the Node-readable SEO manifest generated by the curriculum generation step. +3. Build a list of sitemap-eligible static routes. +4. Build content routes from each entry's `canonicalPath`, falling back to `/content/[id]/` during transition. +5. Generate static/pre-rendered HTML artifacts for every sitemap URL. +6. Inject metadata from the shared metadata builder. +7. Write the final production sitemap to `dist/sitemap.xml`. +8. Validate that every sitemap URL has a matching static artifact. +9. Do not include `/practice/:topicId/` routes in the first SEO sitemap pass. +10. Do not include `` unless a reliable automated source can provide an accurate value. +11. Do not output `` or ``. + +### Decision: SEO Postbuild Output Ownership + +Use a custom SEO `postbuild` script as the owner of pre-rendered HTML and sitemap output. + +The script is responsible for: + +- Building the list of sitemap-eligible routes. +- Generating static HTML artifacts for those routes. +- Injecting metadata from the shared metadata builder. +- Injecting safe serialized route data for hydrated pages. +- Writing `dist/sitemap.xml`. +- Validating that every sitemap URL has a matching static artifact. +- Ensuring no sitemap route depends on `404.html`. + +Postbuild failure policy in strict mode: + +- Fail the build if any sitemap URL is missing a static/pre-rendered artifact. +- Fail the build if any sitemap URL is missing route-specific title, description, canonical URL, Open Graph URL, or `og:image`. +- Fail the build if any sitemap URL has no visible source HTML content. +- Fail the build if any sitemap URL contains `noindex`. +- Fail the build if any sitemap URL can only be served through `dist/404.html`. +- Treat SEO postbuild errors as release-blocking, not warnings. + +Staged postbuild activation: + +- Use `SEO_POSTBUILD_MODE=off` only during early scaffolding when the script or route list does not exist yet. +- Use `SEO_POSTBUILD_MODE=warn` while manifest SEO fields, metadata, route config, and pre-rendering support are being built. +- Use `SEO_POSTBUILD_MODE=strict` after the pre-render proof of concept passes and before exposing a full production sitemap. +- Do not make strict SEO postbuild validation block unrelated early PRs before the SSG/pre-render proof of concept proves the route output and hydration path. +- Once the production sitemap includes sitemap-eligible routes, strict mode should be required in CI/deployment. + +The deployment workflow may still create `dist/404.html` for SPA fallback, but that fallback is not considered valid static output for any sitemap URL. + +During early local work, sitemap generation may be tested locally, but do not deploy a full production sitemap or a `robots.txt` `Sitemap:` line until every sitemap URL has validated static/pre-rendered output. + +### Decision: Build Boundary for Vite, Curriculum Generation, and SEO Postbuild + +Keep the build responsibilities separated: + +```txt +Vite -> JavaScript/CSS bundling and hashed assets +Curriculum generation -> TypeScript app manifest + Node-readable SEO manifest +SEO postbuild -> static route HTML, metadata injection, route data injection, sitemap, and SEO validation +React -> hydration after the static page loads +``` + +Rules: + +- Vite owns JavaScript and CSS bundling, hashed asset filenames, and the production `dist/index.html` app template. +- The curriculum generation step must output a Node-readable SEO manifest, such as JSON, in addition to the React/TypeScript curriculum manifest. +- The SEO manifest must include enough data to generate static route HTML, sitemap URLs, route metadata, and content route data. +- The SEO postbuild script must use `dist/index.html` as the base HTML template after Vite has built the app. +- The SEO postbuild script must preserve Vite-generated script, CSS, preload, modulepreload, and asset references. +- The SEO postbuild script owns static route HTML generation, metadata injection, route data injection, sitemap generation, and SEO validation. +- The SEO postbuild script must not depend on Vite-only imports, TypeScript runtime execution, `import.meta.glob`, or React app internals. +- The SEO postbuild script may import only the built server/static render entry as the React rendering boundary. +- The SEO postbuild script must not rely on the SPA `404.html` fallback as valid output for sitemap routes. +- The server/static render bundle should be a build artifact outside the public `dist` deploy output, such as `dist-server/entry-server.js`. + +### Decision: HTML Template and Asset Preservation + +The SEO postbuild script should read `dist/index.html` and use it as the base template for every generated static route. + +Rules: + +- Preserve Vite-generated hashed script and stylesheet references. +- Preserve modulepreload/preload links and public asset references. +- Replace or inject only the route-specific head tags, root app markup, and safe route data script. +- Do not manually guess hashed JS or CSS filenames. +- Validate generated nested routes load the same JS and CSS assets as the root app shell. +- Prefer explicit managed template slots over broad string replacement. +- If template slots are used, define stable markers for the SEO head block, root app markup, and serialized route data. +- If an HTML parser is used instead of markers, restrict mutation to the same three areas. +- Fail or warn according to `SEO_POSTBUILD_MODE` if the expected template markers or nodes are missing. +- Do not rewrite unrelated `head`, script, style, preload, modulepreload, favicon, manifest, or public asset tags. + +Suggested managed slots: + +```html + + + +
+ + + +``` + +### Decision: Initial Head Metadata Ownership + +Initial source HTML metadata should come from the shared metadata builder and be injected by `scripts/seo-postbuild.mjs`. + +`react-helmet-async` may still mirror or update the same metadata after hydration, but it should not be the source of truth for pre-rendered HTML. + +Rules: + +- Initial HTML metadata is owned by SEO postbuild. +- Hydrated route metadata should match the same shared metadata builder output. +- Mismatches between source HTML metadata and hydrated React metadata are bugs. + +### Decision: Server/Static Render Entry API + +The server/static render entry should expose a small, explicit API for the SEO postbuild script. + +Suggested API: + +```ts +export async function renderRoute(input: { + url: string; + routeData?: unknown; + preloadedState?: unknown; +}): Promise<{ + appHtml: string; + statusCode?: number; + diagnostics?: string[]; +}>; +``` + +Rules: + +- `src/entry-server.tsx` should be the React rendering boundary consumed by SEO postbuild. +- The built server/static render entry should render app markup only; it should not write files, generate sitemap URLs, or mutate `dist`. +- The SEO postbuild script should own file output, metadata injection, route-data injection, sitemap generation, and validation. +- The server/static render entry should receive the canonical URL path and any route data before rendering. +- The server/static render entry should create an isolated app instance per route render. +- Do not import the browser singleton Redux store into the server/static render entry. +- Do not depend on browser globals or process-wide mutable render state. +- Metadata may be calculated for React route components, but initial source HTML metadata is still injected by SEO postbuild from the shared metadata builder. + +### Decision: Per-Render Store and Client-Only Persistence + +Static rendering must not reuse the browser singleton Redux store across route renders. + +Rules: + +- Extract a store factory such as `createAppStore(preloadedState?)` from `src/lib/redux/store.ts`. +- The server/static render entry must create a fresh Redux store for every `renderRoute` call. +- The client entry may create the browser store once, but it should also support preloaded route data or preloaded state when present. +- Browser persistence subscriptions for Redux/localStorage should be attached only in the client entry or behind explicit browser guards. +- Do not run localStorage, sessionStorage, or storage-subscription side effects at module load in code imported by `entry-server`. +- Server/static rendering must never read from or write to localStorage. +- Route-derived data must win over persisted selected-topic state during hydration. + +### Decision: Client Hydration Bootstrap + +The client entry should choose hydration or fresh mounting based on the generated HTML it receives. + +Rules: + +- Read serialized route data before the first React render. +- If the root element already contains pre-rendered app markup, use `hydrateRoot`. +- If the root element is empty or contains only a generic non-pre-rendered shell, use `createRoot`. +- Pass initial route data or preloaded state into shared app providers before rendering. +- Ignore or clear serialized route data if it does not match the current URL. +- After hydration, normal client navigation can use existing route loading behavior. +- Hydration mismatches between generated source HTML and the first client render should be treated as bugs. + +### Decision: Sitemap URL Discipline + +Every sitemap URL must be: + +- Absolute +- Canonical +- Indexable +- Direct-loadable +- Intended to appear in search results +- Returning `200 OK` +- Not redirected +- Not duplicated with another slash/non-slash format +- Not blocked by `robots.txt` +- Not marked `noindex` + +Use only canonical URLs in the sitemap. + +Example: + +```xml +https://www.dev-net-core.com/content/async-await/ +``` + +Do not use: + +```xml +/content/async-await/ +``` + +Do not include invalid routes, duplicate routes, utility-only routes, or pages that should not rank. + +Submitting a sitemap helps discovery, but it does not guarantee that Google will crawl or index every URL. + +### Decision: Static Output Rule for Sitemap URLs + +Every route marked `Sitemap = Yes` must have a matching production static output or pre-rendered artifact. + +A route must not be included in `sitemap.xml` if it only works because the React SPA catches the route after loading a generic app shell. + +For every sitemap URL, production must provide: + +- A direct-loadable URL +- `200 OK` status +- No redirect +- Canonical trailing-slash URL +- Route-specific source HTML metadata +- Route-specific or fallback Open Graph metadata in source HTML +- Visible page content in source HTML +- No `noindex` +- No `robots.txt` block + +This rule applies to all sitemap routes, not only `/content/:topicId/`. + +Examples of routes that need static/pre-rendered output if included in the sitemap: + +- `/` +- `/content/` +- `/content/:topicId/` +- `/roadmap/` +- `/about-us/` +- `/privacy/` +- `/terms/` + +Do not include a route in the sitemap until its production static output is validated. + +| Route type | Sitemap? | Static/pre-render required? | +|---|---:|---:| +| `/` | Yes | Yes | +| `/content/` | Yes | Yes | +| `/content/:topicId/` | Yes | Yes | +| `/roadmap/` | Yes | Yes | +| `/about-us/` | Yes | Yes | +| `/privacy/` | Yes | Yes | +| `/terms/` | Yes | Yes | +| `/practice/` | No by default | No, unless later indexed | +| `/practice/:topicId/` | No by default | No, unless later indexed | +| `/simulation/setup/` | No | No | +| `/simulation/session/:sessionId/` | No | No | +| `/simulation/result/:sessionId/` | No | No | +| `/bug-report/` | No | No | +| Invalid routes | No | Prefer real 404 or noindex | + +### Example Sitemap Shape + +```xml + + + + https://www.dev-net-core.com/ + + + https://www.dev-net-core.com/content/ + + + https://www.dev-net-core.com/roadmap/ + + + https://www.dev-net-core.com/about-us/ + + + https://www.dev-net-core.com/privacy/ + + + https://www.dev-net-core.com/terms/ + + + https://www.dev-net-core.com/content/classes-structs-records/ + + +``` + +### Package Script Update + +Consider updating `package.json`: + +```json +{ + "scripts": { + "generate:curriculum": "node scripts/generate-curriculum-manifest.mjs", + "generate:seo-manifest": "node scripts/generate-seo-manifest.mjs", + "predev": "npm run generate:curriculum", + "build:client": "tsc -b && vite build", + "build:ssg": "vite build --ssr src/entry-server.tsx --outDir dist-server", + "seo:postbuild": "node scripts/seo-postbuild.mjs", + "fallback:404": "node scripts/seo-404.mjs", + "seo:validate": "node scripts/validate-seo-build.mjs", + "build": "npm run generate:curriculum && npm run generate:seo-manifest && npm run build:client && npm run build:ssg && npm run seo:postbuild && npm run fallback:404 && npm run seo:validate" + } +} +``` + +Only change the scripts after checking the current `package.json`. + +Do not make `predev` generate the production sitemap. Local development can rely on app routes; production sitemap output belongs to the SEO postbuild step. + +If the main `build` script explicitly runs `generate:curriculum`, remove or adjust the existing `prebuild` script so curriculum generation does not run twice. + +The exact `generate:seo-manifest` implementation can be folded into `generate:curriculum` as long as the generated ownership is clear, the build script does not duplicate work unnecessarily, and the SEO manifest is produced before sitemap/pre-render work begins. + +If `fallback:404` remains separate from `seo:postbuild`, final SEO validation should run after `fallback:404` so the dedicated Not Found + `noindex` file is included in validation. + +The exact script names may be adjusted during implementation, but the required production build order is: + +1. Generate the curriculum manifest. +2. Generate the Node-readable SEO manifest, either as part of curriculum generation or as an explicit step. +3. Build the client assets. +4. Build the server/static render entry or framework-mode pre-render output. +5. Run the SEO postbuild script that writes route HTML artifacts and `dist/sitemap.xml`. +6. Generate or validate the dedicated `dist/404.html` Not Found + `noindex` fallback. +7. Run local SEO build validation when available. + +Only `dist` should be deployed to GitHub Pages. Do not upload `dist-server` or other private server/static build outputs. + +## Pre-rendering Architecture Decisions Before Implementation + +Before implementing SEO pre-rendering, decide these items explicitly. + +### Decision: Canonical URL Shape for Static Hosting + +DEV_NET_CORE should keep trailing-slash URLs as the canonical static-output format. + +Reason: + +```txt +dist/content/topic/index.html -> https://www.dev-net-core.com/content/topic/ +``` + +Rules: + +- Sitemap URLs must use trailing-slash canonical URLs. +- Canonical tags, Open Graph URLs, Twitter/X URLs, structured data URLs, and internal generated links must use trailing-slash canonical URLs. +- Non-trailing URLs may continue to work for user convenience, but they are not the canonical SEO target. +- On GitHub Pages, do not assume non-trailing URLs can return true `301` redirects unless Cloudflare redirect rules are explicitly enabled and validated. +- If a non-trailing URL is kept as an accepted direct-load URL, production must prove that it returns `200 OK` directly without relying on a generic SPA fallback. +- Non-trailing URLs must not appear in `sitemap.xml`. + +### Decision: Rendering Strategy Spike Before Implementation + +Do not assume the custom SEO postbuild approach is the only possible path. + +Run a spike to compare: + +- React Router framework-mode SSG/pre-rendering. +- Custom Vite SSR/SSG build plus SEO postbuild generation. + +React Router framework docs support static pre-rendering through `react-router.config.ts`, including `ssr: false` for static-file deployments and a `prerender()` list/function for generated paths. They also support build-time loaders during pre-rendering. + +However, DEV_NET_CORE is currently a Vite SPA using `BrowserRouter`, `Routes`, and `createRoot`, so React Router framework-mode SSG may require a larger routing/build migration. + +The spike must answer: + +- Can DEV_NET_CORE migrate safely to React Router framework mode? +- Can it generate `/content/:topicId/` paths from the curriculum manifest? +- Can route loaders receive Markdown content and curriculum metadata at build time? +- Can the generated output deploy cleanly to GitHub Pages with the Cloudflare-managed custom domain? +- How much current routing, layout, provider, and navigation code must change? +- Does framework-mode SSG reduce or increase complexity compared with the custom SEO postbuild approach? + +Proceed with framework-mode SSG only if the migration cost is acceptable. Otherwise, continue with the custom SEO postbuild approach already described in this plan. + +References: + +- `https://reactrouter.com/start/framework/rendering` +- `https://reactrouter.com/how-to/pre-rendering` + +### Decision: JSON-LD Ownership + +Structured data should be owned by the same metadata pipeline as title, description, canonical URL, Open Graph, and Twitter/X metadata. + +Rule: + +```txt +Markdown frontmatter -> generated manifest -> shared metadata builder -> sitemap + pre-render HTML + React route metadata +``` + +The shared metadata builder should produce: + +- Canonical metadata +- Open Graph metadata +- Twitter/X metadata +- JSON-LD structured data + +SEO postbuild should inject JSON-LD into source HTML for sitemap URLs. React may mirror the same JSON-LD after hydration, but React runtime output should not be the source of truth for structured data. + +### Decision: Static Theme Bootstrap + +Pre-rendered HTML should not flash the wrong theme before React hydrates. + +Rules: + +- Add a small before-paint theme bootstrap if needed. +- The bootstrap may read a safe theme preference from localStorage and/or system preference. +- It should set the root `data-theme`, `dark`/`light` class, and any critical theme variables needed before paint. +- Keep the script tiny and dependency-free. +- Do not let the bootstrap create markup that conflicts with React hydration. +- If localStorage is unavailable, default to the production default theme. + +Example placement: + +```html + +``` + +### Decision: Build Script Ownership + +Keep production build steps explicit while the SEO pipeline is being stabilized. + +Preferred conceptual steps: + +```txt +generate:curriculum +generate:seo-manifest +build:client +build:ssg or framework prerender +seo:postbuild +fallback:404 +seo:validate +``` + +Rules: + +- Avoid hiding too much behind `prebuild` and `postbuild` until the flow is stable. +- If the main `build` script explicitly runs `generate:curriculum`, remove or adjust the existing `prebuild` script so generation does not run twice. +- `generate:seo-manifest` may be a separate command or part of `generate:curriculum`, but ownership must be clear. +- `fallback:404` must generate a dedicated Not Found + `noindex` document, not blindly copy `dist/index.html`. +- If `fallback:404` is separate from `seo:postbuild`, run final SEO validation after the fallback file exists. +- CI/GitHub Actions should use the same ordered build steps as local production validation. + +### Decision: Pre-render Output Budgets + +Define output and build-time budgets before scaling from the POC to every sitemap-eligible route. + +Initial budgets to choose during the POC: + +- Maximum total `dist` size. +- Maximum HTML file size per route. +- Maximum serialized route-data JSON size per route. +- Maximum number of pre-rendered routes in the first sitemap release. +- Maximum production build time. + +Important risk: + +If each content route includes full rendered HTML and also serializes full Markdown as route data, output size can grow quickly. Prefer the smallest route data needed for hydration, and measure the POC before generating every topic page. + +### Acceptance Criteria + +- `https://www.dev-net-core.com/sitemap.xml` is accessible. +- The sitemap includes all required sitemap-eligible routes that have validated static/pre-rendered production output. +- Every curriculum topic has a `/content/:topicId/` entry. +- `/practice/:topicId/` routes are intentionally excluded from the first SEO sitemap pass. +- Sitemap URLs are absolute canonical URLs, not relative paths. +- Sitemap URLs use the trailing-slash format. +- Sitemap URLs are indexable, direct-loadable, not redirected, not blocked by `robots.txt`, and not marked `noindex`. +- Every sitemap URL has route-specific source HTML metadata and visible page content in source HTML. +- Every sitemap-included content URL receives Markdown and curriculum metadata before pre-rendering. +- Every sitemap-included content URL hydrates with the same initial route data used during pre-rendering. +- No sitemap-included content URL renders `Loading content...` as its primary source HTML content. +- Sitemap generation uses manifest `canonicalPath` values for content pages. +- Sitemap and pre-render generation use the Node-readable SEO manifest, not the TypeScript app manifest. +- Generated route HTML is based on `dist/index.html` and preserves Vite-generated hashed asset references. +- Generated route HTML includes safe serialized route data only when needed for hydration. +- The private server/static render build output is not deployed to GitHub Pages. +- Sitemap generation omits `` unless a reliable automated source can provide an accurate value. +- Sitemap generation does not include `` or ``. +- SEO postbuild output does not break `npm run build`. +- Local development does not require production sitemap generation. + +### Decision: Practice Topic Indexing + +For the first SEO pass, do not pre-render or index `/practice/:topicId/` pages. + +Use `/content/:topicId/` as the primary SEO landing page and source of truth for each curriculum topic. Treat `/practice/:topicId/` as an interactive app workflow route for users who want to practice after reading the topic content. + +Initial indexing strategy: + +```txt +Index and include in sitemap after static/pre-render validation: +/ +/content/ +/content/:topicId/ +/roadmap/ +/about-us/ +/privacy/ +/terms/ + +Optional index and sitemap only if intentionally designed to rank: +/changelog/ +/simulation/ + +Do not include in sitemap initially: +/practice/ +/practice/:topicId/ +/simulation/* +/bug-report/ + +Add noindex initially: +/practice/:topicId/ +/simulation/* +/bug-report/ +``` + +Rules: + +- `/content/:topicId/` pages should be the canonical searchable landing pages. +- `/practice/:topicId/` pages should be linked from their corresponding content pages for user flow. +- `/practice/:topicId/` pages should not be included in the sitemap until they are intentionally designed as standalone searchable Q&A landing pages. +- `/practice/:topicId/` pages should use `noindex` initially to avoid thin-content or duplicate-intent indexing risk. +- `/practice/` itself should not be included in the first sitemap pass unless it has enough visible source HTML content and is intended to rank as a practice landing page. +- `/simulation/` itself may remain indexable only if it has enough visible source HTML content and is intended to rank as a stable public tool landing route. +- `/simulation/*` session, result, or user-flow routes should use `noindex` and should not be included in the sitemap. + +Only consider indexing `/practice/:topicId/` later if those pages become strong standalone pages with: + +- Unique title and description +- Enough meaningful initial HTML content +- Clear question/answer content visible without relying only on interaction +- Distinct purpose from the corresponding content page +- Self-canonical URL +- Structured data only if it accurately matches the visible content and Google guidelines + +## Phase 3: Crawlable Internal Links + +### 3. Ensure Topic Links Use Real Anchors + +Search engines discover links through real HTML anchor tags. + +Ensure navigation to topic pages renders links with valid `href` values. + +Good: + +```tsx + + Classes, Structs, and Records + +``` + +Also good: + +```tsx +
+ Classes, Structs, and Records + +``` + +Avoid: + +```tsx + +``` + +Buttons are fine for app interactions, but important SEO navigation should also be available through real links. + +### Decision: Curriculum Link Semantics + +Follow the crawlable-link plan with a targeted navigation semantics change, not a redesign. + +Keep curriculum folder rows as `