Upgrade to Pro — share decks privately, control downloads, hide ads and more …

プラグインで拡張される Context をtype-safe にする難しさと設計判断

プラグインで拡張される Context をtype-safe にする難しさと設計判断

Avatar for kazupon

kazupon

May 22, 2026

More Decks by kazupon

Other Decks in Programming

Transcript

  1. Vue Fes Japan Organizer Vue.js Japan User Group Organizer Vue.js

    Core Team Vite+ Team Nuxt Ecosystem Team Vue I18n, Intlify and Gunshi author Plaid, inc. @kazupon kazupon kazupon
  2. ͜ͷൃදʹ͍ͭͯ • Gunshi ͱ Context ͱ͸Կ͔ • ίϚϯυఆ͔ٛΒ Context ͕ఏڙ͢Δ஋ͷܕ͕Ͳ͏ਪ࿦͞ΕΔͷ͔

    • ϓϥάΠϯʹΑͬͯ Context Λ֦ு͢ΔͱԿ͕೉͍͠ͷ͔ • Gunshi Runtime ʹ֦ு͞Εͨ஋ͱϓϥάΠϯ͕ఏڙ͢Δ Extension ܕΛͲ͏ରԠ͚ͮΔͷ͔ • Gunshi ͕બΜͩઃܭΞϓϩʔνͱͦͷτϨʔυΦϑ Gunshi ͷϓϥάΠϯγεςϜΛ୊ࡐʹɺ ʹ͍ͭͯ࿩͍ͯ͘͠
  3. Gunshi ͱίϚϯυ࣮ߦ࠷খίʔυ • Gunshi: Ϟμϯͳ JavaScript CLI ϥΠϒϥϦ • ໊শ͸

    “Commander” ͔ΒΠϯεύΠΞ • ಛ௃ • એݴϕʔεͳίϚϯυఆٛ • type-safe ͳ ίϚϯυ arguments • ϓϥάΠϯ • … ͳͲͳͲ https://gunshi.dev
  4. Context ͱ͸ʁ • Context ͱ͸Կ౳͔ͷॲཧͷ࣮ߦʹඞ ཁͳ৘ใΛ·ͱΊͯ౉ͨ͢ΊͷϋίΈ ͍ͨͳ΋ͷ • ࣮ࡍྫ: •

    Hono: ϋϯυϥʹ౉͞ΕΔ `c` • Express: ϛυϧ΢ΣΞؒͰڞ༗͢Δ `res.locals` • Context Λ࢖͏ͱɺ࣮ߦର৅ͷೖྗɺ ؀ڥɺڞ༗ঢ়ଶΛ1ͭͷೖޱ͔Βѻ͑Δ
  5. Gunshi ͷ CommandContext ͱ͸ʁ • ࣮ߦͨ͠ίϚϯυ໊ɺίϚϯυ args ఆٛɺͦ ࣮ͯ͠ߦલʹ args

    ͕ղੳ (parse) ͞Εͯղܾ ͨ͠ values ͳͲͷ৘ใΛؚΜͩΦϒδΣΫτ • ίϚϯυ࣮ߦϩδοΫͰ͋Δ CommandRunner ͷϋϯυϥؔ਺ͷύϥϝʔ λͱͯ͠౉͞ΕΔ • CommandContext ʹ͸ɺίϚϯυ࣮ߦ͞Εͨ ͱ͖ͷॲཧʹඞཁͳ৘ใ͕ू໿͞ΕΔ
  6. CommandContext ͷ type-safe • Gunshi Ͱ͸ɺίϚϯυΛఆٛ͢Δ de fi ne() Λհͯ͠ɺ

    CommandContext ͷܕ͕ܾ·Δ • ctx.args ΋ ctx.values ΋ɺίϚϯ υͰએݴͨ͠ args ʹج͍ͮͯ type-safe ʹͳΔ
  7. de fi ne ͔Β run(ctx) ·Ͱܕ͕ಧ͘࢓૊Έ • CommandContext ͷܕ͸ɺde fi

    ne() ʹ౉ͨ͠ίϚϯυఆ͔ٛΒਪ࿦͞ΕΔ • ͜͜Ͱ `A` ͸ɺde fi nition.args ͔Βਪ࿦͞ΕΔ • de fi ne() Ͱਪ࿦͞Εͨ `A` ͸ɺGunshiParams Λ௨ͯ͠ Command ͷܕʹ ͳΔ
  8. de fi ne ͔Β run(ctx) ·Ͱܕ͕ಧ͘࢓૊Έ Command Ҏ߱͸ɺ Command ->

    CommandRunner -> CommandContext -> ExtractArgs, ArgValues ͱ͍͏ܕͷܕύϥϝʔλͷόέπϦϨʔͰ ctx.args ͱ ctx.values ͷܕ͕ਪ࿦͞ΕΔ
  9. ϓϥάΠϯ͸ CommandContext Λ֦ு͢Δ • ۩ମతʹ͸ɺCommandRunner ͷύϥϝʔ λͰ౉Δ CommandContext ͷ extensions

    (ͭ·Γctx.extensions) ʹ஋΍ϩδοΫΛಈ తʹ௥Ճ͢Δ • ͔͠͠ɺJSϨϕϧͰ͸֦ு͞ΕΔ͕ɺTSͷ ܕγεςϜͱͯ͠͸ run(ctx) ͷ ctx.extensions ʹܕͷԸܙΛड͚Εͳ͍ • ϓϥάΠϯ͸ɺίϚϯυ࣮ߦ࣌ͷ CommandContext ΁ܕΛެ։͢Δ࢓૊Έ͕ ඞཁ
  10. ֦ு͞Εͨ ctx.extensions Λ type-safe ʹ͍ͨ͠ • ͨͩɺϓϥάΠϯʹΑ֦ͬͯு͞Εͨ ctx.extensions ΋ɺطଘͷ argsɺ

    values ͷΑ͏ʹ type-safe ʹͯ͠࢖͍͍ͨ • ཉ͍͠΋ͷ: • ϓϥάΠϯ ID ʹΑΔิ׬ • ϓϥάΠϯ extension API ͷิ׬ • ଘࡏ͠ͳ͍ϝιουͷܕΤϥʔ
  11. ೉͠͞ 1: ϓϥάΠϯID • ctx.extensions ͸ɺϓϥάΠϯIDΛ key ʹͨ͠ΦϒδΣΫτ • ͭ·ΓɺϓϥάΠϯID͸

    Gunshi ͕ϓ ϥάΠϯΛద༻͢Δͱ͖ͷࣝผࢠͰ ͋ΓɺTypeScript ্ͷ property key Ͱ΋͋Δ • ͜͜ͰϓϥάΠϯ ID ͷܕ͕ `string` ͩͱɺtype-safe ͳ key ͱͯ͠ѻ͑ͳ ͍
  12. ೉͠͞ 2: ϓϥάΠϯಉ࢜΋ Context Λ࢖͏ • ίϚϯυ͚ͩͰͳ͘ɺϓϥάΠϯ΋ଞ ͷϓϥάΠϯ͕௥Ճͨ͠ extension Λ

    ࢖͏͜ͱ͕Ͱ͖Δ • extension(ctx) ϑΝΫτϦͰ͸ɺίϚ ϯυ࣮ߦ࣌ͱಉ͡Α͏ʹ ctx.extensions ΛࢀরͰ͖Δ CommandContext Λड͚औΔ • ྫ: auth ϓϥάΠϯͷதͰ΋logger ϓ ϥάΠϯͷ Extension ܕ͕ඞཁʹͳΔ
  13. ೉͠͞ 2: ϓϥάΠϯಉ࢜΋ Context Λ࢖͏ • ͜͜ͰͷϙΠϯτ͸ɺϓϥάΠϯ ࣮૷தͷ extension(ctx) ϑΝΫτ

    ϦͰ΋ɺଞͷϓϥάΠϯ͕ఏڙ͠ ͨ ctx.extensions Λ type-safe ʹ ѻ͍͍ͨ͜ͱ • ͭ·ΓɺContext ֦ுͷܕ͸ίϚ ϯυଆ͚ͩͰͳ͘ɺϓϥάΠϯ࡞ ੒ऀଆʹ΋ඞཁ
  14. ೉͠͞ 3: ίϚϯυଆ͔Βొ࿥ࡁΈϓϥάΠϯ͕ݟ͑ͳ͍ • CommandRunner ଆͰ extension Λ࢖͑Δ Α͏ʹ͢Δඞཁ͕͋Δ •

    Gunshi Ͱ͸ɺίϚϯυఆٛΛผϑΝΠ ϧʹ෼཭Ͱ͖ɺίϚϯυϞδϡʔϧԽͰ ͖Δ • ͔͠͠ɺίϚϯυϞδϡʔϧ͸ϓϥάΠ ϯΛొ࿥͢Δίʔυͱ͸ผͷ৔ॴʹ͋Δ • ͦͷͨΊɺίϚϯυଆʹ͸ʮͲͷϓϥάΠ ϯ extension ͕࢖͑Δͷ͔ʯΛܕͱͯ͠౉ ͢ඞཁ͕͋Δ
  15. ͜͜·Ͱ࿩ͨ͠೉͠͞Λ੔ཧ͢Δͱ… 1. Gunshi Runtime ্Ͱ͸ɺϓϥάΠ ϯ ID Λ key ʹͯ͠

    ctx.extensions ͷ஋Λ࡞Δඞཁ͕͋Δ 2. ϓϥάΠϯ࣮૷ऀͱίϚϯυఆٛ ऀͷ྆ํͰ͸ɺ࢖༻͢Δ ctx.extensions ΛϓϥάΠϯ ID ͷ key ͱ Extension ܕͰ type-safe ʹ ͢Δඞཁ͕͋Δ ϓϥάΠϯ࡞ऀ Gunshi Runtime ϓϥάΠϯ࣮૷ऀ ίϚϯυఆٛऀ
  16. ղ͖ํͷީิΛݕ౼͢Δ • ͜ͷ໰୊Λղͨ͘Ίʹ͸ɺ͍͔ͭ͘ͷઃܭΞϓϩʔν͕͋Δ 1. Module Augmentation 2. Fluent Interface 3.

    Explicit Context Type 4. Compositional Type Augmentation ʢ஫ҙ: ͜ΕΒͷઃܭΞϓϩʔνͷ໊শ͸ɺࣗ෼໊͕෇͚͍ͯΔ΋ͷ͕͋Γ·͢ʣ
  17. 1. Module Augmentation • طଘͷ interface ΛϞδϡʔϧશମͰ֦ ு͢Δ • ͜ͷΞϓϩʔνΛ࠾༻͍ͯ͠ΔϥΠϒϥ

    Ϧ/ϑϨʔϜϫʔΫྫ: • h3 / Nitro: `event.context` • Astro: `App.Locals`
  18. 1. Module Augmentation • Pros: • ΞϓϦɺϞδϡʔϧશମͰಉ͡ܕΛڞ༗͠΍͍͢ • ϑϨʔϜϫʔΫ͕ఏڙ͢Δطଘ Context

    ͱ૬ੑ͕Α͍ • Cons: • Ұ౓֦ு͢Δͱɺಉ͡ module ͷܕΛ࢖͏৔ॴ͢΂ͯʹ൓ө͞ΕΔ • ܕͷείʔϓ͸ΞϓϦશମ·ͨ͸ϓϩδΣΫτશମʹدΓ΍͍͢
  19. 2. Fluent Interface • ϝιουνΣΠϯΛ௨ͯ͠ɺContext ʹ ܕΛஈ֊తʹ஝ੵ͢Δ • ͜ͷΞϓϩʔνΛ࠾༻͍ͯ͠ΔϥΠϒϥ Ϧ/ϑϨʔϜϫʔΫྫ:

    • Elysia: `.decorate()` / `.resolve()` • TanStack Router: `beforeLoad` https://martinfowler.com/bliki/FluentInterface.html
  20. 2. Fluent Interface • Pros: • API Λͭͳ͙͚ͩͰܕ͕஝ੵ͞ΕΔ • ิ׬͕ڧ͍

    • ߏஙॱংͱܕͷมԽ͕Ұக͠΍͍͢ • લஈͰ௥Ճͨ͠ܕΛɺޙஈͷϋϯυϥͳͲͷॲཧͰ࢖͑Δ • Cons: • ϝιουνΣΠϯʹ৐Δ API ઃܭ͕લఏʹͳΔ • Ϟδϡʔϧ͕෼ࢄ͢ΔͱܕΛӡͼʹ͍͘
  21. 3. Explicit Context Type • Context ʹೖΔ஋ͷ shape Λɺܕύϥ ϝʔλͰ໌ࣔతͳܕͱͯ͠౉͢

    • ͜ͷΞϓϩʔνΛ࠾༻͍ͯ͠ΔϥΠϒϥ Ϧ/ϑϨʔϜϫʔΫྫ: • Hono: `Hono<{ Variables: ... }>` • tRPC: `initTRPC.context<Context>()`
  22. 3. Explicit Context Type • Pros: • Context ͷ shape

    ͕໌ࣔతʹݟ͑Δ • module augmentation ʹཔΒͳͯ͘Α͍ • Ϟδϡʔϧ୯ҐͰܕͷείʔϓΛ෼͚΍͍͢ • ܕΛ export ͯ͠ผϞδϡʔϧ͔Β࠶ར༻͠΍͍͢ • Cons: • Context ʹԿ͕ೖΔ͔Λར༻ऀ͕ܕͱͯ͠ॻ͘ඞཁ͕͋Δ • ஋Λ௥Ճ͢Δ৔ॴͱܕఆٛΛἧ͑Δඞཁ͕͋Δ • ܕύζϧͰෳࡶʹͳΓ͕ͪ
  23. Explicit Context Type • Gunshi Ͱ͸ɺExplicit Context Type Λ ϕʔεΛ࠾༻

    • Gunshi ʹ͓͍ͯɺܕͷ໌ࣔ͸ Context શମͰ͸ͳ͘ɺctx.extensions ͷܕ • ϓϥάΠϯIDͱ Extension ܕͷϖΞΛ Map ͱͯ͠දݱ͢Δ
  24. ࠾༻ཧ༝ • Gunshi ͸ʮએݴϕʔεͷΠϯλʔϑΣʔεʯͰίϚϯυ΍ϓϥάΠϯΛఆ ٛ͢Δ • ίϚϯυϞδϡʔϧͱϓϥάΠϯ͕ผʑͷ৔ॴʹଘࡏ͢Δ • ϓϥάΠϯ IDͱ

    Extension ܕͷରԠΛ ctx.extensions ͷ key/value ͱͯ͠ API ͱͯ͠࢒ͤΔ • ϓϥάΠϯ࡞ऀ͕ϓϥάΠϯ IDͱ Extension ܕΛ export ͯ͠ڞ༗Ͱ͖Δ
  25. ϓϥάΠϯ࡞ऀ͕ export ͨ͠ϞϊΛ૊ΈࠐΉ ϓϥάΠϯ͕ެ։͢Δ৘ใ extension(ctx) Ͱґଘ͢Δ ϓϥάΠϯextensionΛ࢖༻ ctx.extensions ͷ ࣮ࡍͷ஋Λ࡞Δͷʹ࢖༻

    run(ctx) Ͱ ظ଴͢ΔϓϥάΠϯ exntensionΛ࢖༻ ϓϥάΠϯID Extension ܕ extension ϑΝΫτϦ ϓϥάΠϯID Extension ܕ ϓϥάΠϯID ϓϥάΠϯID Extension ܕ ϓϥάΠϯ࣮૷ऀ Gunshi Runtime ίϚϯυఆٛऀ extension ϑΝΫτϦ ϓϥάΠϯ࡞ऀ
  26. GunshiParams ʹΑΔܕͷ఻ൖ • Gunshi Ͱ͸ɺܕ৘ใΛ GunshiParams ܦ ༝Ͱ CommandContext ·Ͱ౉͍ͯ͠Δ

    • ϓϥάΠϯʹΑΔ Context ֦ுʹରͯ͠ ΋ɺಉ͡࢓૊ΈͰɺextensions ΋ GunshiParams ʹ࣋ͨͤͯղܾ͍ͯ͠Δ • CommandContext ͸ɺܕύϥϝʔλܦ༝ Ͱ ctx.extensions ͷܕ΋औΓग़͠ɺϓϥ άΠϯͷܕΛ఻೻ͤ͞Δ
  27. ϓϥάΠϯ࡞ऀଆ͕ެ։͢Δ৘ใ • ϓϥάΠϯ࡞ऀ͸ɺctx.extensions Ͱ key ʹͳΔϓϥάΠϯIDͱɺ extension ϑΝΫτϦ͕ฦ͢ Extension ͷܕΛηοτͰެ։͢

    Δ • Logger ϓϥάΠϯͷέʔεྫ: • loggerId ͸ ctx.extensions ͷkey • LoggerExtension ͸ logger ͕ఏ ڙ͢Δϩάؔ਺܊
  28. ϓϥάΠϯ࡞ऀଆ͕ެ։͢Δ৘ใ • ϓϥάΠϯ͕ఏڙ͢ΔػೳΛ࣮ ૷ͨ͠ extension ϑΝΫτϦΛ ఏڙ͢Δ • ۩ମతʹ͸ pluign()

    ʹͦͷϑΝ ΫτϦΛࢦఆͯ͠ɺreturn ͢Δ ॲཧΛ࣋ͬͨϓϥάΠϯఆٛؔ ਺Λ export ͢Δ logger()ͷ ໭Γ஋ͷܕ͸ԼهͰਪ࿦
  29. ϓϥάΠϯ࣮૷ଆͰଞͷϓϥάΠϯΛ࢖͏৔߹ • ϓϥάΠϯ͸ɺଞͷϓϥάΠϯ extension Λ࢖͏͜ͱ͕͋Δ • ϓϥάΠϯIDͱ Extension ܕΛ Map

    ͨ͠΋ͷΛ plugin() ͷܕύϥϝʔλͷ ୈ1Ҿ਺ʹࢦఆ͢Δ • ͦΕʹΑΓɺplugin()ଆͰɺextension ϑΝΫτϦʹ౉Δ ctx.extensions ʹґ ଘ͢ΔϓϥάΠϯͷܕ͕ਪ࿦͞ΕΔ ϓϥάΠϯ ID ͱ Extension ܕΛ Map ͨ͠΋ͷΛࢦఆ͢Δ
  30. Gunshi Runtime Ͱ͸ ctx.extensions Λ࡞Δ • Πϯετʔϧ͞ΕͨϓϥάΠϯͷ extension ϑΝΫτϦΛ࣮ߦ͢Δ loggerExtension

    LoggerExtension Type authExtension AuthExtension Type apiExtension ApiExtension Type extension() extension() extension() ctx.extensions [loggerId] loggerExtension [authId] authExtension [apiId] apiExtension … … cli() ಺෦Ͱ ϓϥάΠϯID͝ͱʹू໿
  31. ίϚϯυఆٛऀ͕ϓϥάΠϯͷܕΛ౉͢ • ίϚϯυϞδϡʔϧ͸ϓϥάΠϯొ࿥৔ॴ ͔Β཭Ε͍ͯΔ • ϓϥάΠϯIDͱ Extension ܕΛ Map ͨ͠΋

    ͷΛ plugin() ͷܕύϥϝʔλͷୈ1Ҿ਺ʹࢦ ఆ͢Δ • ͦΕʹΑΓɺde fi neWithTypes()ଆͰɺ extensionϑΝΫτϦʹ౉Δ ctx.extensions ʹґଘ͢ΔϓϥάΠϯͷܕ͕ਪ࿦͞ΕΔ
  32. de fi neWithTypes() ʹΑΔ ctx.extensions ͕ type-safeʹͳΔྲྀΕ CommandDe fi nitionResult

    -> Command -> CommanRunner ͱ͍͏ܕͷόέπϦϨʔͰ࠷ऴత ʹ ctx.extensions ͷܕ͕ܾ·Δ
  33. ࠓ೔࿩ͨ͜͠ͱ 1.Gunshi ͷ CommandContext ͸ de fi ne() ͔Βܕਪ࿦͞ΕΔ 2.

    ϓϥάΠϯಋೖʹΑΓɺCommandContext ʹ΋֦ுϙΠϯτ͕ඞཁʹͳͬ ͨ 3. ՝୊͸ɺίϚϯυ࣮ߦ࣌ʹ࡞ΒΕΔ ctx.extensions ͷ஋ͱɺϓϥάΠϯ࣮૷ ࣌ɺίϚϯυఆٛ࣌ʹͦΕΛ type-safe ʹ͢Δ͜ͱ 4. ͍͔ͭ͘ͷΞϓϩʔνΛൺֱ͠ɺGunshi ͸ Explicit Context Type Λ࠾༻ 5. ϓϥάΠϯ IDͱ Extension ܕΛ export ͠ɺͦΕΒΛ key/value ͷϖΞͱͳ Δ Map Ͱܕύϥϝʔλܦ༝Ͱ ctx.extensions ͷܕ͕ਪ࿦͞ΕΔΑ͏ʹઃܭ
  34. Explicit Context Type ΛબΜͩ͜ͱʹΑΔτϨʔυΦϑ • ಘͨ΋ͷ: • ϓϥάΠϯڥքͷ໌ࣔੑ: ϓϥάΠϯ ID

    ͕ key ʹ࢒ΓɺͲͷϓϥάΠϯ༝དྷͷ extension ͔෼͔Δ • ܕܖ໿ͷެ։ ֎෦ϓϥάΠϯ͕ϓϥάΠϯ ID ͱ Extension ܕΛ export Ͱ͖Δ • ෼ࢄίϚϯυͰͷܕڞ༗: ϓϥάΠϯొ࿥৔ॴ͔Β཭ΕͨίϚϯυϞδϡʔϧʹ΋ܕΛ౉ͤΔ
  35. Explicit Context Type ΛબΜͩ͜ͱʹΑΔτϨʔυΦϑ • ҰํͰɺ੍໿ͱͯ͠ड͚ೖΕͨ΋ͷ: • ϓϥάΠϯ࡞ऀͷެ։੹຿: ϓϥάΠϯ࡞ऀ͸ ϓϥάΠϯ

    IDͱ Extension ܕΛެ։͢Δඞཁ͕͋Δ • ίϚϯυଆͷ໌ࣔࢦఆ: ίϚϯυఆٛଆ͸ظ଴͢ΔϓϥάΠϯͷ extension Λ໌ࣔ͢Δඞཁ͕͋Δ • ొ࿥ͱܕࢦఆͷ੔߹ੑ: ϓϥάΠϯొ࿥ͱίϚϯυ࣮૷ଆͷܕࢦఆΛἧ͑Δඞཁ͕͋Δ
  36. Ԡ༻Ͱ͖Δ஌ݟ • Context ͷΑ͏ͳಈతʹ஋Λ௥ՃͰ͖Δߏ଄Λઃܭ͢Δͱ͖΋ɺಉ͡؍఺Ͱ ൑அ͢Δ͜ͱʹͳΔ • ௥Ճ͞Εͨ஋ͷ༝དྷΛ API ʹ࢒͔͢ •

    ܕΛ global ʹ޿͛Δ͔ɺscope Λݶఆ͢Δ͔ • ࣗಈਪ࿦Λ༏ઌ͢Δ͔ɺ໌ࣔతͳܖ໿Λ༏ઌ͢Δ͔ • ར༻ऀͷॻ͖΍͢͞ͱɺ֦ு͢Δଆͷ੹຿ΛͲ͏෼͚Δ͔ • ಈతͳ֦ுΛ type-safe ʹ͢ΔઃܭͰ͸ɺܕͷදݱ͚ͩͰͳ͘ɺڥքͱ੹຿ͷ ஔ͖ํ͕ॏཁʹͳΔ