From d2f3ff10bae5d37c791e9ec720809be3e73190d6 Mon Sep 17 00:00:00 2001 From: Ryan Bas Date: Mon, 11 May 2026 23:11:51 -0600 Subject: [PATCH 01/23] chore: add .worktrees/ to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index e26b234..dbb17f6 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ packged/ out-tsc/ .direnv/ .corepack/ +.worktrees/ From 465c0fc0bf0477543808f6c150c49c5570d03392 Mon Sep 17 00:00:00 2001 From: Ryan Bas Date: Mon, 11 May 2026 23:19:28 -0600 Subject: [PATCH 02/23] feat(docs-site): scaffold elm-pages v3 project Initialize elm-pages v3.5.1 scaffold under packages/docs-site with monorepo-appropriate package.json. Adds lamdera, elm-review, and elm-codegen as required devDependencies. Adds lamdera to root pnpm onlyBuiltDependencies to allow its postinstall binary download. Co-Authored-By: Claude Opus 4.6 (1M context) --- package.json | 3 +- packages/docs-site/.gitignore | 5 + packages/docs-site/README.md | 1 + packages/docs-site/app/Api.elm | 26 + packages/docs-site/app/Effect.elm | 190 ++ packages/docs-site/app/ErrorPage.elm | 84 + packages/docs-site/app/Route/Blog/Slug_.elm | 86 + packages/docs-site/app/Route/Greet.elm | 107 + packages/docs-site/app/Route/Hello.elm | 119 + packages/docs-site/app/Route/Index.elm | 88 + packages/docs-site/app/Shared.elm | 120 + packages/docs-site/app/Site.elm | 21 + packages/docs-site/app/View.elm | 69 + packages/docs-site/codegen/elm.codegen.json | 14 + packages/docs-site/custom-backend-task.js | 3 + packages/docs-site/custom-backend-task.ts | 3 + packages/docs-site/elm-pages.config.mjs | 18 + packages/docs-site/elm.json | 69 + packages/docs-site/index.js | 10 + packages/docs-site/index.ts | 16 + packages/docs-site/netlify.toml | 13 + packages/docs-site/package.json | 16 + packages/docs-site/public/favicon.ico | Bin 0 -> 450 bytes .../docs-site/script/custom-backend-task.js | 3 + .../docs-site/script/custom-backend-task.ts | 3 + packages/docs-site/script/elm.json | 62 + packages/docs-site/script/src/AddRoute.elm | 326 +++ .../docs-site/script/src/AddStaticRoute.elm | 89 + packages/docs-site/script/src/Stars.elm | 42 + packages/docs-site/src/.gitkeep | 0 packages/docs-site/style.css | 5 + pnpm-lock.yaml | 2408 ++++++++++++++++- 32 files changed, 3949 insertions(+), 70 deletions(-) create mode 100644 packages/docs-site/.gitignore create mode 100644 packages/docs-site/README.md create mode 100644 packages/docs-site/app/Api.elm create mode 100644 packages/docs-site/app/Effect.elm create mode 100644 packages/docs-site/app/ErrorPage.elm create mode 100644 packages/docs-site/app/Route/Blog/Slug_.elm create mode 100644 packages/docs-site/app/Route/Greet.elm create mode 100644 packages/docs-site/app/Route/Hello.elm create mode 100644 packages/docs-site/app/Route/Index.elm create mode 100644 packages/docs-site/app/Shared.elm create mode 100644 packages/docs-site/app/Site.elm create mode 100644 packages/docs-site/app/View.elm create mode 100644 packages/docs-site/codegen/elm.codegen.json create mode 100644 packages/docs-site/custom-backend-task.js create mode 100644 packages/docs-site/custom-backend-task.ts create mode 100644 packages/docs-site/elm-pages.config.mjs create mode 100644 packages/docs-site/elm.json create mode 100644 packages/docs-site/index.js create mode 100644 packages/docs-site/index.ts create mode 100644 packages/docs-site/netlify.toml create mode 100644 packages/docs-site/package.json create mode 100644 packages/docs-site/public/favicon.ico create mode 100644 packages/docs-site/script/custom-backend-task.js create mode 100644 packages/docs-site/script/custom-backend-task.ts create mode 100644 packages/docs-site/script/elm.json create mode 100644 packages/docs-site/script/src/AddRoute.elm create mode 100644 packages/docs-site/script/src/AddStaticRoute.elm create mode 100644 packages/docs-site/script/src/Stars.elm create mode 100644 packages/docs-site/src/.gitkeep create mode 100644 packages/docs-site/style.css diff --git a/package.json b/package.json index 1925ff1..58c1599 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,8 @@ }, "pnpm": { "onlyBuiltDependencies": [ - "esbuild" + "esbuild", + "lamdera" ] } } diff --git a/packages/docs-site/.gitignore b/packages/docs-site/.gitignore new file mode 100644 index 0000000..7f8423c --- /dev/null +++ b/packages/docs-site/.gitignore @@ -0,0 +1,5 @@ +node_modules/ +elm-stuff/ +dist/ +.elm-pages/ +functions/ diff --git a/packages/docs-site/README.md b/packages/docs-site/README.md new file mode 100644 index 0000000..7e59600 --- /dev/null +++ b/packages/docs-site/README.md @@ -0,0 +1 @@ +# README diff --git a/packages/docs-site/app/Api.elm b/packages/docs-site/app/Api.elm new file mode 100644 index 0000000..86aa52b --- /dev/null +++ b/packages/docs-site/app/Api.elm @@ -0,0 +1,26 @@ +module Api exposing (routes) + +import ApiRoute exposing (ApiRoute) +import BackendTask exposing (BackendTask) +import FatalError exposing (FatalError) +import Html exposing (Html) +import Pages.Manifest as Manifest +import Route exposing (Route) + + +routes : + BackendTask FatalError (List Route) + -> (Maybe { indent : Int, newLines : Bool } -> Html Never -> String) + -> List (ApiRoute ApiRoute.Response) +routes getStaticRoutes htmlToString = + [] + + +manifest : Manifest.Config +manifest = + Manifest.init + { name = "Site Name" + , description = "Description" + , startUrl = Route.Index |> Route.toPath + , icons = [] + } diff --git a/packages/docs-site/app/Effect.elm b/packages/docs-site/app/Effect.elm new file mode 100644 index 0000000..c307576 --- /dev/null +++ b/packages/docs-site/app/Effect.elm @@ -0,0 +1,190 @@ +module Effect exposing (Effect(..), batch, fromCmd, map, none, perform, testPerform) + +{-| + +@docs Effect, batch, fromCmd, map, none, perform, testPerform + +-} + +import Browser.Navigation +import Form +import Http +import Json.Decode as Decode +import Pages.Fetcher +import Test.PagesProgram.SimulatedEffect as SimulatedEffect exposing (SimulatedEffect) +import Url exposing (Url) + + +{-| -} +type Effect msg + = None + | Cmd (Cmd msg) + | Batch (List (Effect msg)) + | GetStargazers (Result Http.Error Int -> msg) + | SetField { formId : String, name : String, value : String } + | FetchRouteData + { data : Maybe FormData + , toMsg : Result Http.Error Url -> msg + } + | Submit + { values : FormData + , toMsg : Result Http.Error Url -> msg + } + | SubmitFetcher (Pages.Fetcher.Fetcher msg) + + +{-| -} +type alias RequestInfo = + { contentType : String + , body : String + } + + +{-| -} +none : Effect msg +none = + None + + +{-| -} +batch : List (Effect msg) -> Effect msg +batch = + Batch + + +{-| -} +fromCmd : Cmd msg -> Effect msg +fromCmd = + Cmd + + +{-| -} +map : (a -> b) -> Effect a -> Effect b +map fn effect = + case effect of + None -> + None + + Cmd cmd -> + Cmd (Cmd.map fn cmd) + + Batch list -> + Batch (List.map (map fn) list) + + GetStargazers toMsg -> + GetStargazers (toMsg >> fn) + + FetchRouteData fetchInfo -> + FetchRouteData + { data = fetchInfo.data + , toMsg = fetchInfo.toMsg >> fn + } + + Submit fetchInfo -> + Submit + { values = fetchInfo.values + , toMsg = fetchInfo.toMsg >> fn + } + + SetField info -> + SetField info + + SubmitFetcher fetcher -> + fetcher + |> Pages.Fetcher.map fn + |> SubmitFetcher + + +{-| -} +perform : + { fetchRouteData : + { data : Maybe FormData + , toMsg : Result Http.Error Url -> pageMsg + } + -> Cmd msg + , submit : + { values : FormData + , toMsg : Result Http.Error Url -> pageMsg + } + -> Cmd msg + , runFetcher : + Pages.Fetcher.Fetcher pageMsg + -> Cmd msg + , fromPageMsg : pageMsg -> msg + , key : Browser.Navigation.Key + , setField : { formId : String, name : String, value : String } -> Cmd msg + } + -> Effect pageMsg + -> Cmd msg +perform ({ fromPageMsg, key } as helpers) effect = + case effect of + None -> + Cmd.none + + Cmd cmd -> + Cmd.map fromPageMsg cmd + + SetField info -> + helpers.setField info + + Batch list -> + Cmd.batch (List.map (perform helpers) list) + + GetStargazers toMsg -> + Http.get + { url = + "https://api.github.com/repos/dillonkearns/elm-pages" + , expect = Http.expectJson (toMsg >> fromPageMsg) (Decode.field "stargazers_count" Decode.int) + } + + FetchRouteData fetchInfo -> + helpers.fetchRouteData + fetchInfo + + Submit record -> + helpers.submit record + + SubmitFetcher record -> + helpers.runFetcher record + + +{-| Decompose an Effect into a SimulatedEffect for the test framework. +Maintain this function alongside `perform` when adding custom Effect variants. +-} +testPerform : Effect msg -> SimulatedEffect msg +testPerform effect = + case effect of + None -> + SimulatedEffect.none + + Cmd _ -> + SimulatedEffect.none + + Batch list -> + SimulatedEffect.batch (List.map testPerform list) + + GetStargazers toMsg -> + -- In tests, provide a mock value for the stargazers count. + -- Alternatively, return SimulatedEffect.none and use + -- PagesProgram.simulateMsg to provide the result manually. + SimulatedEffect.dispatchMsg (toMsg (Ok 0)) + + SetField info -> + SimulatedEffect.setField info + + FetchRouteData _ -> + SimulatedEffect.none + + Submit _ -> + SimulatedEffect.none + + SubmitFetcher fetcher -> + SimulatedEffect.submitFetcher fetcher + + +type alias FormData = + { fields : List ( String, String ) + , method : Form.Method + , action : String + , id : Maybe String + } diff --git a/packages/docs-site/app/ErrorPage.elm b/packages/docs-site/app/ErrorPage.elm new file mode 100644 index 0000000..3f6e45d --- /dev/null +++ b/packages/docs-site/app/ErrorPage.elm @@ -0,0 +1,84 @@ +module ErrorPage exposing (ErrorPage(..), Model, Msg, head, init, internalError, notFound, statusCode, update, view) + +import Effect exposing (Effect) +import Head +import Html exposing (Html) +import View exposing (View) + + +type Msg + = Increment + + +type alias Model = + { count : Int + } + + +init : ErrorPage -> ( Model, Effect Msg ) +init errorPage = + ( { count = 0 } + , Effect.none + ) + + +update : ErrorPage -> Msg -> Model -> ( Model, Effect Msg ) +update errorPage msg model = + case msg of + Increment -> + ( { model | count = model.count + 1 }, Effect.none ) + + +head : ErrorPage -> List Head.Tag +head errorPage = + [] + + +type ErrorPage + = NotFound + | InternalError String + + +notFound : ErrorPage +notFound = + NotFound + + +internalError : String -> ErrorPage +internalError = + InternalError + + +view : ErrorPage -> Model -> View Msg +view error model = + { body = + [ Html.div [] + [ Html.p [] + [ Html.text <| + case error of + NotFound -> + "Page not found. Maybe try another URL?" + + InternalError string -> + "Something went wrong.\n" ++ string + ] + ] + ] + , title = + case error of + NotFound -> + "Page Not Found" + + InternalError string -> + "Unexpected Error" + } + + +statusCode : ErrorPage -> number +statusCode error = + case error of + NotFound -> + 404 + + InternalError _ -> + 500 diff --git a/packages/docs-site/app/Route/Blog/Slug_.elm b/packages/docs-site/app/Route/Blog/Slug_.elm new file mode 100644 index 0000000..bfbedcf --- /dev/null +++ b/packages/docs-site/app/Route/Blog/Slug_.elm @@ -0,0 +1,86 @@ +module Route.Blog.Slug_ exposing (ActionData, Data, Model, Msg, route) + +import BackendTask exposing (BackendTask) +import FatalError exposing (FatalError) +import Head +import Head.Seo as Seo +import Html +import Pages.Url +import PagesMsg exposing (PagesMsg) +import RouteBuilder exposing (App, StatelessRoute) +import Shared +import View exposing (View) + + +type alias Model = + {} + + +type alias Msg = + () + + +type alias RouteParams = + { slug : String } + + +route : StatelessRoute RouteParams Data ActionData +route = + RouteBuilder.preRender + { head = head + , pages = pages + , data = data + } + |> RouteBuilder.buildNoState { view = view } + + +pages : BackendTask FatalError (List RouteParams) +pages = + BackendTask.succeed + [ { slug = "hello" } + ] + + +type alias Data = + { something : String + } + + +type alias ActionData = + {} + + +data : RouteParams -> BackendTask FatalError Data +data routeParams = + BackendTask.map Data + (BackendTask.succeed "Hi") + + +head : + App Data ActionData RouteParams + -> List Head.Tag +head app = + Seo.summary + { canonicalUrlOverride = Nothing + , siteName = "elm-pages" + , image = + { url = Pages.Url.external "TODO" + , alt = "elm-pages logo" + , dimensions = Nothing + , mimeType = Nothing + } + , description = "TODO" + , locale = Nothing + , title = "TODO title" -- metadata.title -- TODO + } + |> Seo.website + + +view : + App Data ActionData RouteParams + -> Shared.Model + -> View (PagesMsg Msg) +view app sharedModel = + { title = "Placeholder - Blog.Slug_" + , body = [ Html.text "You're on the page Blog.Slug_" ] + } diff --git a/packages/docs-site/app/Route/Greet.elm b/packages/docs-site/app/Route/Greet.elm new file mode 100644 index 0000000..5434b62 --- /dev/null +++ b/packages/docs-site/app/Route/Greet.elm @@ -0,0 +1,107 @@ +module Route.Greet exposing (ActionData, Data, Model, Msg, route) + +import BackendTask exposing (BackendTask) +import BackendTask.Http +import ErrorPage exposing (ErrorPage) +import FatalError exposing (FatalError) +import Head +import Head.Seo as Seo +import Html +import Json.Decode as Decode +import Pages.Url +import PagesMsg exposing (PagesMsg) +import RouteBuilder exposing (App, StatefulRoute, StatelessRoute) +import Server.Request as Request exposing (Request) +import Server.Response as Response exposing (Response) +import Shared +import View exposing (View) + + +type alias Model = + {} + + +type alias Msg = + () + + +type alias RouteParams = + {} + + +route : StatelessRoute RouteParams Data ActionData +route = + RouteBuilder.serverRender + { head = head + , data = data + , action = \_ _ -> BackendTask.fail (FatalError.fromString "No action.") + } + |> RouteBuilder.buildNoState { view = view } + + +type alias Data = + { name : Maybe String + } + + +type alias ActionData = + {} + + +data : RouteParams -> Request -> BackendTask FatalError (Response Data ErrorPage) +data routeParams request = + case request |> Request.queryParam "name" of + Just name -> + BackendTask.Http.getJson "http://worldtimeapi.org/api/timezone/America/Los_Angeles" + (Decode.field "utc_datetime" Decode.string) + |> BackendTask.allowFatal + |> BackendTask.map + (\dateTimeString -> + Response.render + { name = Just dateTimeString } + ) + + Nothing -> + BackendTask.succeed + (Response.render + { name = Nothing } + ) + + +head : + App Data ActionData RouteParams + -> List Head.Tag +head app = + Seo.summary + { canonicalUrlOverride = Nothing + , siteName = "elm-pages" + , image = + { url = Pages.Url.external "TODO" + , alt = "elm-pages logo" + , dimensions = Nothing + , mimeType = Nothing + } + , description = "TODO" + , locale = Nothing + , title = "TODO title" -- metadata.title -- TODO + } + |> Seo.website + + +view : + App Data ActionData RouteParams + -> Shared.Model + -> View (PagesMsg Msg) +view app shared = + { title = "Greetings" + , body = + [ Html.div [] + [ case app.data.name of + Just name -> + Html.text ("Hello " ++ name) + + Nothing -> + Html.text "Hello, I didn't find your name" + ] + ] + } diff --git a/packages/docs-site/app/Route/Hello.elm b/packages/docs-site/app/Route/Hello.elm new file mode 100644 index 0000000..3119a43 --- /dev/null +++ b/packages/docs-site/app/Route/Hello.elm @@ -0,0 +1,119 @@ +module Route.Hello exposing (ActionData, Data, Model, Msg(..), RouteParams, action, data, route) + +import BackendTask exposing (BackendTask) +import BackendTask.Http +import Effect exposing (Effect) +import ErrorPage exposing (ErrorPage) +import FatalError exposing (FatalError) +import Head +import Html +import Json.Decode as Decode +import PagesMsg exposing (PagesMsg) +import RouteBuilder exposing (App) +import Server.Request exposing (Request) +import Server.Response +import Shared +import UrlPath exposing (UrlPath) +import View exposing (View) + + +type alias Model = + {} + + +type Msg + = NoOp + + +type alias RouteParams = + {} + + +route = + RouteBuilder.serverRender { data = data, action = action, head = head } + |> RouteBuilder.buildWithLocalState + { view = view + , subscriptions = subscriptions + , update = update + , init = init + } + + +init : + App Data ActionData RouteParams + -> Shared.Model + -> ( Model, Effect Msg ) +init app shared = + ( {}, Effect.none ) + + +update : + App Data ActionData RouteParams + -> Shared.Model + -> Msg + -> Model + -> ( Model, Effect Msg ) +update app shared msg model = + case msg of + NoOp -> + ( model, Effect.none ) + + +subscriptions : + RouteParams + -> UrlPath + -> Shared.Model + -> Model + -> Sub Msg +subscriptions routeParams path shared model = + Sub.none + + +type alias Data = + { stars : Int + } + + +type alias ActionData = + {} + + +data : + RouteParams + -> Request + -> BackendTask FatalError (Server.Response.Response Data ErrorPage) +data routeParams request = + BackendTask.Http.getWithOptions + { url = "https://api.github.com/repos/dillonkearns/elm-pages" + , expect = BackendTask.Http.expectJson (Decode.field "stargazers_count" Decode.int) + , headers = [] + , cacheStrategy = Just BackendTask.Http.IgnoreCache + , retries = Nothing + , timeoutInMs = Nothing + , cachePath = Nothing + } + |> BackendTask.allowFatal + |> BackendTask.map + (\stars -> Server.Response.render { stars = stars }) + + +head : App Data ActionData RouteParams -> List Head.Tag +head app = + [] + + +view : + App Data ActionData RouteParams + -> Shared.Model + -> Model + -> View (PagesMsg Msg) +view app shared model = + { title = "Hello", body = [ Html.text (String.fromInt app.data.stars) ] } + + +action : + RouteParams + -> Request + -> BackendTask.BackendTask FatalError.FatalError (Server.Response.Response ActionData ErrorPage.ErrorPage) +action routeParams request = + BackendTask.succeed (Server.Response.render {}) diff --git a/packages/docs-site/app/Route/Index.elm b/packages/docs-site/app/Route/Index.elm new file mode 100644 index 0000000..e930c85 --- /dev/null +++ b/packages/docs-site/app/Route/Index.elm @@ -0,0 +1,88 @@ +module Route.Index exposing (ActionData, Data, Model, Msg, route) + +import BackendTask exposing (BackendTask) +import FatalError exposing (FatalError) +import Head +import Head.Seo as Seo +import Html +import Pages.Url +import PagesMsg exposing (PagesMsg) +import UrlPath +import Route +import RouteBuilder exposing (App, StatelessRoute) +import Shared +import View exposing (View) + + +type alias Model = + {} + + +type alias Msg = + () + + +type alias RouteParams = + {} + + +type alias Data = + { message : String + } + + +type alias ActionData = + {} + + +route : StatelessRoute RouteParams Data ActionData +route = + RouteBuilder.single + { head = head + , data = data + } + |> RouteBuilder.buildNoState { view = view } + + +data : BackendTask FatalError Data +data = + BackendTask.succeed Data + |> BackendTask.andMap + (BackendTask.succeed "Hello!") + + +head : + App Data ActionData RouteParams + -> List Head.Tag +head app = + Seo.summary + { canonicalUrlOverride = Nothing + , siteName = "elm-pages" + , image = + { url = [ "images", "icon-png.png" ] |> UrlPath.join |> Pages.Url.fromPath + , alt = "elm-pages logo" + , dimensions = Nothing + , mimeType = Nothing + } + , description = "Welcome to elm-pages!" + , locale = Nothing + , title = "elm-pages is running" + } + |> Seo.website + + +view : + App Data ActionData RouteParams + -> Shared.Model + -> View (PagesMsg Msg) +view app shared = + { title = "elm-pages is running" + , body = + [ Html.h1 [] [ Html.text "elm-pages is up and running!" ] + , Html.p [] + [ Html.text <| "The message is: " ++ app.data.message + ] + , Route.Blog__Slug_ { slug = "hello" } + |> Route.link [] [ Html.text "My blog post" ] + ] + } diff --git a/packages/docs-site/app/Shared.elm b/packages/docs-site/app/Shared.elm new file mode 100644 index 0000000..b567255 --- /dev/null +++ b/packages/docs-site/app/Shared.elm @@ -0,0 +1,120 @@ +module Shared exposing (Data, Model, Msg(..), SharedMsg(..), template) + +import BackendTask exposing (BackendTask) +import Effect exposing (Effect) +import FatalError exposing (FatalError) +import Html exposing (Html) +import Html.Events +import Pages.Flags +import Pages.PageUrl exposing (PageUrl) +import UrlPath exposing (UrlPath) +import Route exposing (Route) +import SharedTemplate exposing (SharedTemplate) +import View exposing (View) + + +template : SharedTemplate Msg Model Data msg +template = + { init = init + , update = update + , view = view + , data = data + , subscriptions = subscriptions + , onPageChange = Nothing + } + + +type Msg + = SharedMsg SharedMsg + | MenuClicked + + +type alias Data = + () + + +type SharedMsg + = NoOp + + +type alias Model = + { showMenu : Bool + } + + +init : + Pages.Flags.Flags + -> + Maybe + { path : + { path : UrlPath + , query : Maybe String + , fragment : Maybe String + } + , metadata : route + , pageUrl : Maybe PageUrl + } + -> ( Model, Effect Msg ) +init flags maybePagePath = + ( { showMenu = False } + , Effect.none + ) + + +update : Msg -> Model -> ( Model, Effect Msg ) +update msg model = + case msg of + SharedMsg globalMsg -> + ( model, Effect.none ) + + MenuClicked -> + ( { model | showMenu = not model.showMenu }, Effect.none ) + + +subscriptions : UrlPath -> Model -> Sub Msg +subscriptions _ _ = + Sub.none + + +data : BackendTask FatalError Data +data = + BackendTask.succeed () + + +view : + Data + -> + { path : UrlPath + , route : Maybe Route + } + -> Model + -> (Msg -> msg) + -> View msg + -> { body : List (Html msg), title : String } +view sharedData page model toMsg pageView = + { body = + [ Html.nav [] + [ Html.button + [ Html.Events.onClick MenuClicked ] + [ Html.text + (if model.showMenu then + "Close Menu" + + else + "Open Menu" + ) + ] + , if model.showMenu then + Html.ul [] + [ Html.li [] [ Html.text "Menu item 1" ] + , Html.li [] [ Html.text "Menu item 2" ] + ] + + else + Html.text "" + ] + |> Html.map toMsg + , Html.main_ [] pageView.body + ] + , title = pageView.title + } diff --git a/packages/docs-site/app/Site.elm b/packages/docs-site/app/Site.elm new file mode 100644 index 0000000..7d6af63 --- /dev/null +++ b/packages/docs-site/app/Site.elm @@ -0,0 +1,21 @@ +module Site exposing (config) + +import BackendTask exposing (BackendTask) +import FatalError exposing (FatalError) +import Head +import SiteConfig exposing (SiteConfig) + + +config : SiteConfig +config = + { canonicalUrl = "https://elm-pages.com" + , head = head + } + + +head : BackendTask FatalError (List Head.Tag) +head = + [ Head.metaName "viewport" (Head.raw "width=device-width,initial-scale=1") + , Head.sitemapLink "/sitemap.xml" + ] + |> BackendTask.succeed diff --git a/packages/docs-site/app/View.elm b/packages/docs-site/app/View.elm new file mode 100644 index 0000000..cdca20e --- /dev/null +++ b/packages/docs-site/app/View.elm @@ -0,0 +1,69 @@ +module View exposing (View, map, freeze, freezableToHtml, htmlToFreezable) + +{-| View module for elm-pages with frozen view support. + +@docs View, map, freeze, freezableToHtml, htmlToFreezable + +-} + +import Html exposing (Html) + + +{-| -} +type alias View msg = + { title : String + , body : List (Html msg) + } + + +{-| -} +map : (msg1 -> msg2) -> View msg1 -> View msg2 +map fn doc = + { title = doc.title + , body = List.map (Html.map fn) doc.body + } + + +{-| The type of content that can be frozen. Must produce no messages (Never). +For plain Html, this is just Html Never. +-} +type alias Freezable = + Html Never + + +{-| Convert Freezable content to plain Html for server-side rendering. +For plain Html, this is identity. +-} +freezableToHtml : Freezable -> Html Never +freezableToHtml = + identity + + +{-| Convert plain Html back to Freezable for client-side adoption. +For plain Html, this is identity. +-} +htmlToFreezable : Html Never -> Freezable +htmlToFreezable = + identity + + +{-| Freeze a view so its content is rendered at build time and adopted on the client. +Use this for static content that doesn't need interactivity. + +Frozen content is: + + - Rendered at build time and included in the HTML + - Adopted by the client without re-rendering + - Eligible for dead-code elimination (rendering code removed from client bundle) + +At build time, the server codemod wraps the content with a `data-frozen` attribute for extraction. +The elm-review codemod then transforms `freeze` calls to lazy thunks on the client, +which adopt the pre-rendered DOM without re-rendering. + +-} +freeze : Freezable -> Html msg +freeze content = + content + |> freezableToHtml + |> htmlToFreezable + |> Html.map never diff --git a/packages/docs-site/codegen/elm.codegen.json b/packages/docs-site/codegen/elm.codegen.json new file mode 100644 index 0000000..17cfd2e --- /dev/null +++ b/packages/docs-site/codegen/elm.codegen.json @@ -0,0 +1,14 @@ +{ + "elm-codegen-version": "0.6.0", + "codegen-helpers": { + "packages": { + "elm/core": "1.0.5", + "dillonkearns/elm-form": "3.1.0", + "elm/html": "1.0.0", + "rtfeldman/elm-css": "18.0.0", + "dillonkearns/elm-pages": "12.3.0", + "elm/json": "1.1.3" + }, + "local": [".elm-pages/", "app/", "src/"] + } +} diff --git a/packages/docs-site/custom-backend-task.js b/packages/docs-site/custom-backend-task.js new file mode 100644 index 0000000..8a59bea --- /dev/null +++ b/packages/docs-site/custom-backend-task.js @@ -0,0 +1,3 @@ +export async function hello(name) { + return `Hello ${name}!`; +} diff --git a/packages/docs-site/custom-backend-task.ts b/packages/docs-site/custom-backend-task.ts new file mode 100644 index 0000000..8a59bea --- /dev/null +++ b/packages/docs-site/custom-backend-task.ts @@ -0,0 +1,3 @@ +export async function hello(name) { + return `Hello ${name}!`; +} diff --git a/packages/docs-site/elm-pages.config.mjs b/packages/docs-site/elm-pages.config.mjs new file mode 100644 index 0000000..491b738 --- /dev/null +++ b/packages/docs-site/elm-pages.config.mjs @@ -0,0 +1,18 @@ +import { defineConfig } from 'vite'; +import adapter from 'elm-pages/adapter/netlify.js'; + +export default { + vite: defineConfig({}), + adapter, + headTagsTemplate(context) { + return ` + + +`; + }, + preloadTagForFile(file) { + // add preload directives for JS assets and font assets, etc., skip for CSS files + // this function will be called with each file that is processed by Vite, including any files in your headTagsTemplate in your config + return !file.endsWith('.css'); + }, +}; diff --git a/packages/docs-site/elm.json b/packages/docs-site/elm.json new file mode 100644 index 0000000..563df0a --- /dev/null +++ b/packages/docs-site/elm.json @@ -0,0 +1,69 @@ +{ + "type": "application", + "source-directories": ["src", "app", ".elm-pages"], + "elm-version": "0.19.1", + "dependencies": { + "direct": { + "avh4/elm-color": "1.0.0", + "danyx23/elm-mimetype": "4.0.1", + "dillonkearns/elm-bcp47-language-tag": "2.0.0", + "dillonkearns/elm-form": "3.1.0", + "dillonkearns/elm-markdown": "7.0.1", + "dillonkearns/elm-pages": "12.3.0", + "elm/browser": "1.0.2", + "elm/bytes": "1.0.8", + "elm/core": "1.0.5", + "elm/html": "1.0.1", + "elm/http": "2.0.0", + "elm/json": "1.1.4", + "elm/parser": "1.1.0", + "elm/regex": "1.0.0", + "elm/time": "1.0.0", + "elm/url": "1.0.0", + "elm/virtual-dom": "1.0.5", + "elm-community/dict-extra": "2.4.0", + "elm-community/list-extra": "8.7.0", + "elm-community/result-extra": "2.4.0", + "jluckyiv/elm-utc-date-strings": "1.0.0", + "justinmimbs/date": "4.1.0", + "mdgriffith/elm-codegen": "6.0.3", + "miniBill/elm-codec": "2.3.0", + "noahzgordon/elm-color-extra": "1.0.2", + "robinheghan/fnv1a": "1.0.0", + "rtfeldman/elm-css": "18.0.0", + "the-sett/elm-syntax-dsl": "6.0.5", + "turboMaCk/non-empty-list-alias": "1.4.0", + "vito/elm-ansi": "12.0.0" + }, + "indirect": { + "BrianHicks/elm-string-graphemes": "1.0.4", + "BrianHicks/elm-trend": "2.1.3", + "Chadtech/elm-bool-extra": "2.4.2", + "MaybeJustJames/yaml": "2.1.7", + "dillonkearns/elm-cli-options-parser": "5.0.1", + "dillonkearns/elm-date-or-date-time": "2.0.0", + "dillonkearns/elm-ts-json": "2.1.2", + "elm/file": "1.0.5", + "elm/random": "1.0.0", + "elm-explorations/benchmark": "1.0.2", + "elm-explorations/test": "2.2.1", + "elmcraft/core-extra": "2.3.0", + "fredcy/elm-parseint": "2.0.1", + "mdgriffith/style-elements": "5.0.2", + "miniBill/elm-unicode": "1.1.1", + "pithub/elm-parser-bug-workaround": "1.0.0", + "pithub/elm-parser-extra": "1.0.0", + "robinheghan/murmur3": "1.0.0", + "rtfeldman/elm-hex": "1.0.0", + "rtfeldman/elm-iso8601-date-strings": "1.1.4", + "stil4m/elm-syntax": "7.3.9", + "stil4m/structured-writer": "1.0.3", + "the-sett/elm-pretty-printer": "3.3.1", + "wolfadex/elm-ansi": "3.0.1" + } + }, + "test-dependencies": { + "direct": {}, + "indirect": {} + } +} diff --git a/packages/docs-site/index.js b/packages/docs-site/index.js new file mode 100644 index 0000000..c573f3d --- /dev/null +++ b/packages/docs-site/index.js @@ -0,0 +1,10 @@ +const config = { + load: async function (elmLoaded) { + const app = await elmLoaded; + console.log('App loaded', app); + }, + flags: function () { + return 'You can decode this in Shared.elm using Json.Decode.string!'; + }, +}; +export default config; diff --git a/packages/docs-site/index.ts b/packages/docs-site/index.ts new file mode 100644 index 0000000..c191234 --- /dev/null +++ b/packages/docs-site/index.ts @@ -0,0 +1,16 @@ +type ElmPagesInit = { + load: (elmLoaded: Promise) => Promise; + flags: unknown; +}; + +const config: ElmPagesInit = { + load: async function (elmLoaded) { + const app = await elmLoaded; + console.log('App loaded', app); + }, + flags: function () { + return 'You can decode this in Shared.elm using Json.Decode.string!'; + }, +}; + +export default config; diff --git a/packages/docs-site/netlify.toml b/packages/docs-site/netlify.toml new file mode 100644 index 0000000..978159e --- /dev/null +++ b/packages/docs-site/netlify.toml @@ -0,0 +1,13 @@ +[build] + functions = "functions/" + publish = "dist/" + command = "export ELM_HOME=\"$NETLIFY_BUILD_BASE/cache/elm\" && npm install && npm run build" + +[dev] + command = "npm start" + targetPort = 1234 + autoLaunch = true + framework = "#custom" + +[functions] + node_bundler = "esbuild" diff --git a/packages/docs-site/package.json b/packages/docs-site/package.json new file mode 100644 index 0000000..7e0ffe5 --- /dev/null +++ b/packages/docs-site/package.json @@ -0,0 +1,16 @@ +{ + "name": "@wolfcola/docs-site", + "version": "0.0.0", + "private": true, + "type": "module", + "scripts": { + "dev": "elm-pages dev", + "build": "elm-pages build" + }, + "devDependencies": { + "elm-codegen": "^0.6.3", + "elm-pages": "3.5.1", + "elm-review": "^2.13.5", + "lamdera": "^0.19.1-1.4.0" + } +} diff --git a/packages/docs-site/public/favicon.ico b/packages/docs-site/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..30b118264e5d5ceb2a224d8cb2ec646054db7e2d GIT binary patch literal 450 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dys{?#OT-^(NfK1lXXDrqC88}#g zYz7Wi5D5m1*XtPWl`%ZcW85{H;Y~W@`BvunyO@`4Vr{;`(tMq{^8(YMDNHAOnGQ~2 z-m;i!^J1nGJ*-_9SSFlcnRt?A#zE%k2btHbVqUwNLEn8TBd*shCN|SbCxsgsbHMl&bYsurSY)b z#F8aIS84Av8htFD5QkFcgbX{2S>>*=b)?wDU9DItY;^)>?s~jnuQ@68Gq~`wlO$saSG+fxn o@c*rTo%V(Wec??VU*FB!&0H<7o}R7Y&;<%}Pgg&ebxsLQ09-MvasU7T literal 0 HcmV?d00001 diff --git a/packages/docs-site/script/custom-backend-task.js b/packages/docs-site/script/custom-backend-task.js new file mode 100644 index 0000000..8a59bea --- /dev/null +++ b/packages/docs-site/script/custom-backend-task.js @@ -0,0 +1,3 @@ +export async function hello(name) { + return `Hello ${name}!`; +} diff --git a/packages/docs-site/script/custom-backend-task.ts b/packages/docs-site/script/custom-backend-task.ts new file mode 100644 index 0000000..8a59bea --- /dev/null +++ b/packages/docs-site/script/custom-backend-task.ts @@ -0,0 +1,3 @@ +export async function hello(name) { + return `Hello ${name}!`; +} diff --git a/packages/docs-site/script/elm.json b/packages/docs-site/script/elm.json new file mode 100644 index 0000000..2c44d24 --- /dev/null +++ b/packages/docs-site/script/elm.json @@ -0,0 +1,62 @@ +{ + "type": "application", + "source-directories": ["src", "../codegen"], + "elm-version": "0.19.1", + "dependencies": { + "direct": { + "dillonkearns/elm-cli-options-parser": "5.0.1", + "dillonkearns/elm-pages": "12.3.0", + "elm/bytes": "1.0.8", + "elm/core": "1.0.5", + "elm/html": "1.0.1", + "elm/json": "1.1.4", + "mdgriffith/elm-codegen": "6.0.3" + }, + "indirect": { + "BrianHicks/elm-string-graphemes": "1.0.4", + "Chadtech/elm-bool-extra": "2.4.2", + "MaybeJustJames/yaml": "2.1.7", + "avh4/elm-color": "1.0.0", + "danyx23/elm-mimetype": "4.0.1", + "dillonkearns/elm-bcp47-language-tag": "2.0.0", + "dillonkearns/elm-date-or-date-time": "2.0.0", + "dillonkearns/elm-form": "3.1.0", + "dillonkearns/elm-ts-json": "2.1.2", + "elm/browser": "1.0.2", + "elm/file": "1.0.5", + "elm/http": "2.0.0", + "elm/parser": "1.1.0", + "elm/random": "1.0.0", + "elm/regex": "1.0.0", + "elm/time": "1.0.0", + "elm/url": "1.0.0", + "elm/virtual-dom": "1.0.5", + "elm-community/dict-extra": "2.4.0", + "elm-community/list-extra": "8.7.0", + "elm-explorations/test": "2.2.1", + "elmcraft/core-extra": "2.3.0", + "fredcy/elm-parseint": "2.0.1", + "jluckyiv/elm-utc-date-strings": "1.0.0", + "justinmimbs/date": "4.1.0", + "miniBill/elm-codec": "2.3.1", + "miniBill/elm-unicode": "1.1.1", + "noahzgordon/elm-color-extra": "1.0.2", + "pithub/elm-parser-bug-workaround": "1.0.0", + "pithub/elm-parser-extra": "1.0.0", + "robinheghan/fnv1a": "1.0.0", + "robinheghan/murmur3": "1.0.0", + "rtfeldman/elm-css": "18.0.0", + "rtfeldman/elm-hex": "1.0.0", + "rtfeldman/elm-iso8601-date-strings": "1.1.4", + "stil4m/elm-syntax": "7.3.9", + "stil4m/structured-writer": "1.0.3", + "the-sett/elm-pretty-printer": "3.3.1", + "the-sett/elm-syntax-dsl": "6.0.5", + "wolfadex/elm-ansi": "3.0.1" + } + }, + "test-dependencies": { + "direct": {}, + "indirect": {} + } +} diff --git a/packages/docs-site/script/src/AddRoute.elm b/packages/docs-site/script/src/AddRoute.elm new file mode 100644 index 0000000..8faf75d --- /dev/null +++ b/packages/docs-site/script/src/AddRoute.elm @@ -0,0 +1,326 @@ +module AddRoute exposing (run) + +import BackendTask +import Cli.Option as Option +import Cli.OptionsParser as OptionsParser +import Cli.Program as Program +import Elm +import Elm.Annotation as Type +import Elm.Arg +import Elm.Case +import Elm.Declare exposing (Function, fn2) +import Elm.Let +import Elm.Op +import Gen.BackendTask +import Gen.Effect as Effect +import Gen.FatalError +import Gen.Form as Form +import Gen.Form.FieldView as FieldView +import Gen.Html as Html +import Gen.Html.Attributes as Attr +import Gen.Json.Encode +import Gen.List +import Gen.Maybe +import Gen.Pages.Form as PagesForm +import Gen.Pages.Script +import Gen.Platform.Sub +import Gen.Server.Request as Request +import Gen.Server.Response as Response +import Gen.View +import Pages.Script as Script exposing (Script) +import Scaffold.Form +import Scaffold.Route exposing (Type(..)) + + +type alias CliOptions = + { moduleName : List String + , fields : List ( String, Scaffold.Form.Kind ) + } + + +run : Script +run = + Script.withCliOptions program + (\cliOptions -> + cliOptions + |> createFile + |> Script.writeFile + |> BackendTask.allowFatal + ) + + +program : Program.Config CliOptions +program = + Program.config + |> Program.add + (OptionsParser.build CliOptions + |> OptionsParser.with (Option.requiredPositionalArg "module" |> Scaffold.Route.moduleNameCliArg) + |> OptionsParser.withRestArgs Scaffold.Form.restArgsParser + ) + + +createFile : CliOptions -> { path : String, body : String } +createFile { moduleName, fields } = + let + formHelpers : + Maybe + { formHandlers : Elm.Expression + , form : Elm.Expression + , declarations : List Elm.Declaration + } + formHelpers = + Scaffold.Form.provide + { fields = fields + , elmCssView = False + , view = + \{ formState, params } -> + Elm.Let.letIn + (\fieldView -> + Elm.list + ((params + |> List.map + (\{ name, kind, param } -> + fieldView (Elm.string name) param + ) + ) + ++ [ Elm.ifThen formState.submitting + (Html.button + [ Attr.disabled True + ] + [ Html.text "Submitting..." + ] + ) + (Html.button [] + [ Html.text "Submit" + ] + ) + ] + ) + ) + |> Elm.Let.fn2 "fieldView" + (Elm.Arg.var "label") + (Elm.Arg.var "field") + (\label field -> + Html.div [] + [ Html.label [] + [ Html.call_.text (Elm.Op.append label (Elm.string " ")) + , field |> FieldView.input [] + , errorsView.call formState.errors field + ] + ] + ) + |> Elm.Let.toExpression + } + in + Scaffold.Route.serverRender + { moduleName = moduleName + , action = + ( Alias + (Type.record + (case formHelpers of + Just _ -> + [ ( "errors", Type.namedWith [ "Form" ] "ServerResponse" [ Type.string ] ) + ] + + Nothing -> + [] + ) + ) + , \routeParams request -> + formHelpers + |> Maybe.map + (\justFormHelp -> + Request.formData justFormHelp.formHandlers request + |> Gen.Maybe.call_.map + (Elm.fn (Elm.Arg.var "formData") + (\formData -> + Elm.Case.custom + formData + Type.unit + [ Elm.Case.branch + (Elm.Arg.tuple + (Elm.Arg.var "response") + (Elm.Arg.var "parsedForm") + ) + (\( response, parsedForm ) -> + Elm.Case.custom parsedForm + Type.int + [ Elm.Case.branch + (Elm.Arg.customType "Form.Valid" identity + |> Elm.Arg.item (Elm.Arg.var "validatedForm") + ) + (\validatedForm -> + Elm.Case.custom validatedForm + Type.int + [ Elm.Case.branch + (Elm.Arg.customType "Action" identity + |> Elm.Arg.item (Elm.Arg.var "parsed") + ) + (\parsed -> + Scaffold.Form.recordEncoder parsed fields + |> Gen.Json.Encode.encode 2 + |> Gen.Pages.Script.call_.log + |> Gen.BackendTask.call_.map + (Elm.fn Elm.Arg.ignore + (\_ -> + Response.render + (Elm.record + [ ( "errors", response ) + ] + ) + ) + ) + ) + ] + ) + , Elm.Case.branch + (Elm.Arg.customType + "Form.Invalid" + Tuple.pair + |> Elm.Arg.item (Elm.Arg.var "parsed") + |> Elm.Arg.item (Elm.Arg.var "error") + ) + (\( _, _ ) -> + "Form validations did not succeed!" + |> Gen.Pages.Script.log + |> Gen.BackendTask.call_.map + (Elm.fn Elm.Arg.ignore + (\_ -> + Response.render + (Elm.record + [ ( "errors", response ) + ] + ) + ) + ) + ) + ] + ) + ] + ) + ) + |> Gen.Maybe.withDefault + (Gen.BackendTask.fail + (Gen.FatalError.fromString "Expected form post") + ) + ) + |> Maybe.withDefault + (Gen.BackendTask.succeed + (Response.render + (Elm.record []) + ) + ) + ) + , data = + ( Alias (Type.record []) + , \routeParams request -> + Gen.BackendTask.succeed + (Response.render + (Elm.record []) + ) + ) + , head = \app -> Elm.list [] + } + |> Scaffold.Route.addDeclarations + (formHelpers + |> Maybe.map .declarations + |> Maybe.map ((::) errorsView.declaration) + |> Maybe.withDefault [] + ) + |> Scaffold.Route.buildWithLocalState + { view = + \{ shared, model, app } -> + Gen.View.make_.view + { title = moduleName |> String.join "." |> Elm.string + , body = + Elm.list + (case formHelpers of + Just justFormHelp -> + [ Html.h2 [] [ Html.text "Form" ] + , justFormHelp.form + |> PagesForm.call_.renderHtml + (Elm.list []) + (Form.options "form" + |> Form.withServerResponse + (app + |> Elm.get "action" + |> Gen.Maybe.map (Elm.get "errors") + ) + ) + app + ] + + Nothing -> + [ Html.h2 [] [ Html.text "New Page" ] + ] + ) + } + , update = + \{ shared, app, msg, model } -> + Elm.Case.custom msg + (Type.named [] "Msg") + [ Elm.Case.branch (Elm.Arg.customType "NoOp" ()) + (\() -> + Elm.tuple model + Effect.none + ) + ] + , init = + \{ shared, app } -> + Elm.tuple (Elm.record []) Effect.none + , subscriptions = + \{ routeParams, path, shared, model } -> + Gen.Platform.Sub.none + , model = + Alias (Type.record []) + , msg = + Custom [ Elm.variant "NoOp" ] + } + + +errorsView : Function (Elm.Expression -> Elm.Expression -> Elm.Expression) +errorsView = + fn2 "errorsView" + (Elm.Arg.varWith "errors" + (Type.namedWith [ "Form" ] "Errors" [ Type.string ]) + ) + (Elm.Arg.varWith "field" + (Type.namedWith [ "Form", "Validation" ] + "Field" + [ Type.string + , Type.var "parsed" + , Type.var "kind" + ] + ) + ) + (\errors field -> + Elm.ifThen + (Gen.List.call_.isEmpty (Form.errorsForField field errors)) + (Html.div [] []) + (Html.div + [] + [ Html.call_.ul (Elm.list []) + (Gen.List.call_.map + (Elm.fn (Elm.Arg.var "error") + (\error -> + Html.li + [ Attr.style "color" "red" + ] + [ Html.call_.text error + ] + ) + ) + (Form.errorsForField field errors) + ) + ] + ) + |> Elm.withType + (Type.namedWith [ "Html" ] + "Html" + [ Type.namedWith + [ "PagesMsg" ] + "PagesMsg" + [ Type.named [] "Msg" ] + ] + ) + ) diff --git a/packages/docs-site/script/src/AddStaticRoute.elm b/packages/docs-site/script/src/AddStaticRoute.elm new file mode 100644 index 0000000..907df23 --- /dev/null +++ b/packages/docs-site/script/src/AddStaticRoute.elm @@ -0,0 +1,89 @@ +module AddStaticRoute exposing (run) + +import BackendTask +import Cli.Option as Option +import Cli.OptionsParser as OptionsParser +import Cli.Program as Program +import Elm +import Elm.Annotation as Type +import Elm.Arg +import Elm.Case +import Gen.BackendTask +import Gen.Effect as Effect +import Gen.Html as Html +import Gen.Platform.Sub +import Gen.View +import Pages.Script as Script exposing (Script) +import Scaffold.Route exposing (Type(..)) + + +type alias CliOptions = + { moduleName : List String + } + + +run : Script +run = + Script.withCliOptions program + (\cliOptions -> + cliOptions + |> createFile + |> Script.writeFile + |> BackendTask.allowFatal + ) + + +program : Program.Config CliOptions +program = + Program.config + |> Program.add + (OptionsParser.build CliOptions + |> OptionsParser.with (Option.requiredPositionalArg "module" |> Scaffold.Route.moduleNameCliArg) + ) + + +createFile : CliOptions -> { path : String, body : String } +createFile { moduleName } = + Scaffold.Route.preRender + { moduleName = moduleName + , pages = + Gen.BackendTask.succeed + (Elm.list []) + , data = + ( Alias (Type.record []) + , \routeParams -> + Gen.BackendTask.succeed (Elm.record []) + ) + , head = \app -> Elm.list [] + } + |> Scaffold.Route.buildWithLocalState + { view = + \{ shared, model, app } -> + Gen.View.make_.view + { title = moduleName |> String.join "." |> Elm.string + , body = + Elm.list + [ Html.h2 [] [ Html.text "New Page" ] + ] + } + , update = + \{ shared, app, msg, model } -> + Elm.Case.custom msg + (Type.named [] "Msg") + [ Elm.Case.branch (Elm.Arg.customType "NoOp" ()) + (\() -> + Elm.tuple model + Effect.none + ) + ] + , init = + \{ shared, app } -> + Elm.tuple (Elm.record []) Effect.none + , subscriptions = + \{ routeParams, path, shared, model } -> + Gen.Platform.Sub.none + , model = + Alias (Type.record []) + , msg = + Custom [ Elm.variant "NoOp" ] + } diff --git a/packages/docs-site/script/src/Stars.elm b/packages/docs-site/script/src/Stars.elm new file mode 100644 index 0000000..80ecee8 --- /dev/null +++ b/packages/docs-site/script/src/Stars.elm @@ -0,0 +1,42 @@ +module Stars exposing (run) + +import BackendTask exposing (BackendTask) +import BackendTask.Http +import Cli.Option as Option +import Cli.OptionsParser as OptionsParser +import Cli.Program as Program +import Json.Decode as Decode +import Pages.Script as Script exposing (Script) + + +run : Script +run = + Script.withCliOptions program + (\{ username, repo } -> + BackendTask.Http.getJson + ("https://api.github.com/repos/dillonkearns/" ++ repo) + (Decode.field "stargazers_count" Decode.int) + |> BackendTask.allowFatal + |> BackendTask.andThen + (\stars -> + Script.log (String.fromInt stars) + ) + ) + + +type alias CliOptions = + { username : String + , repo : String + } + + +program : Program.Config CliOptions +program = + Program.config + |> Program.add + (OptionsParser.build CliOptions + |> OptionsParser.with + (Option.optionalKeywordArg "username" |> Option.withDefault "dillonkearns") + |> OptionsParser.with + (Option.optionalKeywordArg "repo" |> Option.withDefault "elm-pages") + ) diff --git a/packages/docs-site/src/.gitkeep b/packages/docs-site/src/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/packages/docs-site/style.css b/packages/docs-site/style.css new file mode 100644 index 0000000..96bb337 --- /dev/null +++ b/packages/docs-site/style.css @@ -0,0 +1,5 @@ +body { + font-family: + -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif, + 'Apple Color Emoji', 'Segoe UI Emoji'; +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dc76c6c..ea48638 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -171,6 +171,21 @@ importers: specifier: ^5.47.1 version: 5.47.1 + packages/docs-site: + devDependencies: + elm-codegen: + specifier: ^0.6.3 + version: 0.6.3 + elm-pages: + specifier: 3.5.1 + version: 3.5.1(@types/node@22.19.18)(tslib@2.8.1)(yaml@2.9.0) + elm-review: + specifier: ^2.13.5 + version: 2.13.5 + lamdera: + specifier: ^0.19.1-1.4.0 + version: 0.19.1-1.4.0 + packages/eslint-plugin-treeshake: dependencies: '@typescript-eslint/parser': @@ -884,6 +899,10 @@ packages: '@forgerock/storage@2.0.0': resolution: {integrity: sha512-0/1AL8s1UUGQov93n2i3KRalIIZ6P/KZ2pGgg3Ki3LpjVej80BGZkMp05WfQ8S74XkM81S+VmUdWUILzhLtaTA==} + '@gar/promise-retry@1.0.3': + resolution: {integrity: sha512-GmzA9ckNokPypTg10pgpeHNQe7ph+iIKKmhKu3Ob9ANkswreCx7R3cKmY781K8QK3AqVL3xVh9A42JvIAbkkSA==} + engines: {node: ^20.17.0 || >=22.9.0} + '@hono/node-server@1.19.14': resolution: {integrity: sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw==} engines: {node: '>=18.14.1'} @@ -939,6 +958,151 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + '@jsonjoy.com/base64@1.1.2': + resolution: {integrity: sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/base64@17.67.0': + resolution: {integrity: sha512-5SEsJGsm15aP8TQGkDfJvz9axgPwAEm98S5DxOuYe8e1EbfajcDmgeXXzccEjh+mLnjqEKrkBdjHWS5vFNwDdw==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/buffers@1.2.1': + resolution: {integrity: sha512-12cdlDwX4RUM3QxmUbVJWqZ/mrK6dFQH4Zxq6+r1YXKXYBNgZXndx2qbCJwh3+WWkCSn67IjnlG3XYTvmvYtgA==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/buffers@17.67.0': + resolution: {integrity: sha512-tfExRpYxBvi32vPs9ZHaTjSP4fHAfzSmcahOfNxtvGHcyJel+aibkPlGeBB+7AoC6hL7lXIE++8okecBxx7lcw==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/codegen@1.0.0': + resolution: {integrity: sha512-E8Oy+08cmCf0EK/NMxpaJZmOxPqM+6iSe2S4nlSBrPZOORoDJILxtbSUEDKQyTamm/BVAhIGllOBNU79/dwf0g==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/codegen@17.67.0': + resolution: {integrity: sha512-idnkUplROpdBOV0HMcwhsCUS5TRUi9poagdGs70A6S4ux9+/aPuKbh8+UYRTLYQHtXvAdNfQWXDqZEx5k4Dj2Q==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-core@4.57.2': + resolution: {integrity: sha512-SVjwklkpIV5wrynpYtuYnfYH1QF4/nDuLBX7VXdb+3miglcAgBVZb/5y0cOsehRV/9Vb+3UqhkMq3/NR3ztdkQ==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-fsa@4.57.2': + resolution: {integrity: sha512-fhO8+iR2I+OCw668ISDJdn1aArc9zx033sWejIyzQ8RBeXa9bDSaUeA3ix0poYOfrj1KdOzytmYNv2/uLDfV6g==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-node-builtins@4.57.2': + resolution: {integrity: sha512-xhiegylRmhw43Ki2HO1ZBL7DQ5ja/qpRsL29VtQ2xuUHiuDGbgf2uD4p9Qd8hJI5P6RCtGYD50IXHXVq/Ocjcg==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-node-to-fsa@4.57.2': + resolution: {integrity: sha512-18LmWTSONhoAPW+IWRuf8w/+zRolPFGPeGwMxlAhhfY11EKzX+5XHDBPAw67dBF5dxDErHJbl40U+3IXSDRXSQ==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-node-utils@4.57.2': + resolution: {integrity: sha512-rsPSJgekz43IlNbLyAM/Ab+ouYLWGp5DDBfYBNNEqDaSpsbXfthBn29Q4muFA9L0F+Z3mKo+CWlgSCXrf+mOyQ==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-node@4.57.2': + resolution: {integrity: sha512-nX2AdL6cOFwLdju9G4/nbRnYevmCJbh7N7hvR3gGm97Cs60uEjyd0rpR+YBS7cTg175zzl22pGKXR5USaQMvKg==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-print@4.57.2': + resolution: {integrity: sha512-wK9NSow48i4DbDl9F1CQE5TqnyZOJ04elU3WFG5aJ76p+YxO/ulyBBQvKsessPxdo381Bc2pcEoyPujMOhcRqQ==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-snapshot@4.57.2': + resolution: {integrity: sha512-GdduDZuoP5V/QCgJkx9+BZ6SC0EZ/smXAdTS7PfMqgMTGXLlt/bH/FqMYaqB9JmLf05sJPtO0XRbAwwkEEPbVw==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/json-pack@1.21.0': + resolution: {integrity: sha512-+AKG+R2cfZMShzrF2uQw34v3zbeDYUqnQ+jg7ORic3BGtfw9p/+N6RJbq/kkV8JmYZaINknaEQ2m0/f693ZPpg==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/json-pack@17.67.0': + resolution: {integrity: sha512-t0ejURcGaZsn1ClbJ/3kFqSOjlryd92eQY465IYrezsXmPcfHPE/av4twRSxf6WE+TkZgLY+71vCZbiIiFKA/w==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/json-pointer@1.0.2': + resolution: {integrity: sha512-Fsn6wM2zlDzY1U+v4Nc8bo3bVqgfNTGcn6dMgs6FjrEnt4ZCe60o6ByKRjOGlI2gow0aE/Q41QOigdTqkyK5fg==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/json-pointer@17.67.0': + resolution: {integrity: sha512-+iqOFInH+QZGmSuaybBUNdh7yvNrXvqR+h3wjXm0N/3JK1EyyFAeGJvqnmQL61d1ARLlk/wJdFKSL+LHJ1eaUA==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/util@1.9.0': + resolution: {integrity: sha512-pLuQo+VPRnN8hfPqUTLTHk126wuYdXVxE6aDmjSeV4NCAgyxWbiOIeNJVtID3h1Vzpoi9m4jXezf73I6LgabgQ==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/util@17.67.0': + resolution: {integrity: sha512-6+8xBaz1rLSohlGh68D1pdw3AwDi9xydm8QNlAFkvnavCJYSze+pxoW2VKP8p308jtlMRLs5NTHfPlZLd4w7ew==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@lamdera/compiler-darwin-arm64@0.19.1-1.4.0': + resolution: {integrity: sha512-zLqkIm5wMtuWyFnWlpgXgN0/nfEYRqvcSXEg9R/SlDL5yb8cle3sJCkR5S7zcqBc3zknjb24gWgpWzd0BSkkZg==} + cpu: [arm64] + os: [darwin] + + '@lamdera/compiler-darwin-x64@0.19.1-1.4.0': + resolution: {integrity: sha512-hMG/uKWeL7hp8EeodmSo9Tg9X1P8Xzshai8E/RNtn2LpLCZ6yeRsIjzxpKqV8cEkVCI9yamnT6QTMywexPceYg==} + cpu: [x64] + os: [darwin] + + '@lamdera/compiler-linux-arm64@0.19.1-1.4.0': + resolution: {integrity: sha512-ZLO4Z1jCW4O/9Zoa9LXME+DklUGaOOv6JW7ulWYxSdgvu68Lgkfx47HZidM96R9tehX0Q1wZ+fBmZQkU/a7biQ==} + cpu: [arm64] + os: [linux] + + '@lamdera/compiler-linux-x64@0.19.1-1.4.0': + resolution: {integrity: sha512-r7tx1p/+dSw9SLUL3K7dSj1RBb/1Y5kmE27i9jarOlTeEUr1pycUqt3FSomD0EbTp8Rcp7jRCaNWvb5IWFathg==} + cpu: [x64] + os: [linux] + + '@lamdera/compiler-win32-x64@0.19.1-1.4.0': + resolution: {integrity: sha512-hLpi1eikdY9BA87ipqhTP8gY0l75yih7kjckD30CrIOMMqEWbgaK/0yr2JxTQJiSt6D2A1VZiUbqjHMiSTbPZg==} + cpu: [x64] + os: [win32] + '@manypkg/find-root@1.1.0': resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} @@ -987,6 +1151,18 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} + '@npmcli/agent@4.0.0': + resolution: {integrity: sha512-kAQTcEN9E8ERLVg5AsGwLNoFb+oEG6engbqAU2P43gD4JEIkNGMHdVQ096FsOAAYpZPB0RSt0zgInKIAS1l5QA==} + engines: {node: ^20.17.0 || >=22.9.0} + + '@npmcli/fs@5.0.0': + resolution: {integrity: sha512-7OsC1gNORBEawOa5+j2pXN9vsicaIOH5cPXxoR6fJOmH6/EXpJB2CajXOu1fPRFun2m1lktEFX11+P89hqO/og==} + engines: {node: ^20.17.0 || >=22.9.0} + + '@npmcli/redact@4.0.0': + resolution: {integrity: sha512-gOBg5YHMfZy+TfHArfVogwgfBeQnKbbGo3pSUyK/gSI0AVu+pEiDVcKlQb0D8Mg1LNRZILZ6XG8I5dJ4KuAd9Q==} + engines: {node: ^20.17.0 || >=22.9.0} + '@parcel/watcher-android-arm64@2.5.6': resolution: {integrity: sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==} engines: {node: '>= 10.0.0'} @@ -1078,6 +1254,18 @@ packages: engines: {node: '>=18'} hasBin: true + '@pnpm/config.env-replace@1.1.0': + resolution: {integrity: sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==} + engines: {node: '>=12.22.0'} + + '@pnpm/network.ca-file@1.0.2': + resolution: {integrity: sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==} + engines: {node: '>=12.22.0'} + + '@pnpm/npm-conf@3.0.2': + resolution: {integrity: sha512-h104Kh26rR8tm+a3Qkc5S4VLYint3FE48as7+/5oCEcKR2idC/pF1G6AhIXKI+eHPJa/3J9i5z0Al47IeGHPkA==} + engines: {node: '>=12'} + '@reduxjs/toolkit@2.11.2': resolution: {integrity: sha512-Kd6kAHTA6/nUpp8mySPqj3en3dm0tdMIgbttnQ1xFMVpufoj+ADi8pXLBsd4xzTRHQa7t/Jv8W5UnCuW4kuWMQ==} peerDependencies: @@ -1183,6 +1371,11 @@ packages: cpu: [s390x] os: [linux] + '@rollup/rollup-linux-x64-gnu@4.55.1': + resolution: {integrity: sha512-a8G4wiQxQG2BAvo+gU6XrReRRqj+pLS2NGXKm8io19goR+K8lw269eTrPkSdDTALwMmJp4th2Uh0D8J9bEV1vg==} + cpu: [x64] + os: [linux] + '@rollup/rollup-linux-x64-gnu@4.60.3': resolution: {integrity: sha512-DAZDBHQfG2oQuhY7mc6I3/qB4LU2fQCjRvxbDwd/Jdvb9fypP4IJ4qmtu6lNjes6B531AI8cg1aKC2di97bUxA==} cpu: [x64] @@ -1271,16 +1464,28 @@ packages: resolution: {integrity: sha512-Nqc90v4lWCXyakD6xNyNACBJNJ0tNCwj2WNk/7ivyacYHxiITVgmLUFXTBOeCdy79iz6HtN9Y31uw/jbLrdOAg==} engines: {node: '>=20.0.0'} + '@sindresorhus/is@4.6.0': + resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==} + engines: {node: '>=10'} + '@sindresorhus/merge-streams@2.3.0': resolution: {integrity: sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==} engines: {node: '>=18'} + '@sindresorhus/merge-streams@4.0.0': + resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} + engines: {node: '>=18'} + '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} '@standard-schema/utils@0.3.0': resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==} + '@szmarczak/http-timer@4.0.6': + resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==} + engines: {node: '>=10'} + '@textlint/ast-node-types@15.6.0': resolution: {integrity: sha512-CxZHFbYAU7J0A4izz31wV2ZZfySR6aVj2OSR6/3tppZm7VV6hM7nA7sutsLwIiBL/v4lsB1RM79l4Dc/VrH4qw==} @@ -1296,12 +1501,21 @@ packages: '@textlint/types@15.6.0': resolution: {integrity: sha512-CvgYb1PiqF4BGyoZebGWzAJCZ4ChJAZ9gtWjpQIMKE4Xe2KlSwDA8m8MsiZIV321f5Ibx38BMjC1Z/2ZYP2GQg==} + '@types/cacheable-request@6.0.3': + resolution: {integrity: sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==} + '@types/chai@5.2.3': resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} '@types/chrome@0.1.42': resolution: {integrity: sha512-tdT2roFqGecZZDjA9fUEAINb2STxSPifHMDvY6EfRjNRCjdrs/0FwKt5RCIA9MKMd1arAYZZL3nwEkp6ZLZu2w==} + '@types/configstore@2.1.1': + resolution: {integrity: sha512-YY+hm3afkDHeSM2rsFXxeZtu0garnusBWNG1+7MknmDWQHqcH2w21/xOU9arJUi8ch4qyFklidANLCu3ihhVwQ==} + + '@types/debug@0.0.30': + resolution: {integrity: sha512-orGL5LXERPYsLov6CWs3Fh6203+dXzJkR7OnddIr2514Hsecwc8xRpzCapshBbKFImCsvS/mk6+FWiN5LyZJAQ==} + '@types/deep-eql@4.0.2': resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} @@ -1317,27 +1531,64 @@ packages: '@types/filewriter@0.0.33': resolution: {integrity: sha512-xFU8ZXTw4gd358lb2jw25nxY9QAgqn2+bKKjKOYfNCzN4DKCFetK7sPtrlpg66Ywe3vWY9FNxprZawAh9wfJ3g==} + '@types/get-port@3.2.0': + resolution: {integrity: sha512-TiNg8R1kjDde5Pub9F9vCwZA/BNW9HeXP5b9j7Qucqncy/McfPZ6xze/EyBdXS5FhMIGN6Fx3vg75l5KHy3V1Q==} + + '@types/glob@5.0.38': + resolution: {integrity: sha512-rTtf75rwyP9G2qO5yRpYtdJ6aU1QqEhWbtW55qEgquEDa6bXW0s2TWZfDm02GuppjEozOWG/F2UnPq5hAQb+gw==} + '@types/har-format@1.2.16': resolution: {integrity: sha512-fluxdy7ryD3MV6h8pTfTYpy/xQzCFC7m89nOH9y94cNqJ1mDIDPut7MnRHI3F6qRmh/cT2fUjG1MLdCNb4hE9A==} + '@types/http-cache-semantics@4.2.0': + resolution: {integrity: sha512-L3LgimLHXtGkWikKnsPg0/VFx9OGZaC+eN1u4r+OB1XRqH3meBIAVC2zr1WdMH+RHmnRkqliQAOHNJ/E0j/e0Q==} + + '@types/jest@27.5.2': + resolution: {integrity: sha512-mpT8LJJ4CMeeahobofYWIjFo0xonRS/HfxnVEPMPFSQdGUt1uHCnoPT7Zhb+sjDU2wz0oKV0OLUR0WzrHNgfeA==} + '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} '@types/json5@0.0.29': resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + '@types/keyv@3.1.4': + resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} + + '@types/lodash@4.17.24': + resolution: {integrity: sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ==} + + '@types/minimatch@6.0.0': + resolution: {integrity: sha512-zmPitbQ8+6zNutpwgcQuLcsEpn/Cj54Kbn7L5pX0Os5kdWplB7xPgEh/g+SWOB/qmows2gpuCaPyduq8ZZRnxA==} + deprecated: This is a stub types definition. minimatch provides its own type definitions, so you do not need this installed. + + '@types/mkdirp@0.5.2': + resolution: {integrity: sha512-U5icWpv7YnZYGsN4/cmh3WD2onMY0aJIiTE6+51TwJCttdHvtCYmkBNOobHlXwrJRL0nkH9jH4kD+1FAdMN4Tg==} + '@types/node@12.20.55': resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} '@types/node@22.19.18': resolution: {integrity: sha512-9v00a+dn2yWVsYDEunWC4g/TcRKVq3r8N5FuZp7u0SGrPvdN9c2yXI9bBuf5Fl0hNCb+QTIePTn5pJs2pwBOQQ==} + '@types/node@8.10.66': + resolution: {integrity: sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw==} + '@types/normalize-package-data@2.4.4': resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} + '@types/responselike@1.0.3': + resolution: {integrity: sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==} + + '@types/rimraf@2.0.5': + resolution: {integrity: sha512-YyP+VfeaqAyFmXoTh3HChxOQMyjByRMsHU7kc5KOJkSlXudhMhQIALbYV7rHh/l8d2lX3VUQzprrcAgWdRuU8g==} + '@types/sarif@2.1.7': resolution: {integrity: sha512-kRz0VEkJqWLf1LLVN4pT1cg1Z9wAuvI6L97V3m2f5B76Tg8d413ddvLBPTEHAZJlnn4XSvu0FkZtViCQGVyrXQ==} + '@types/tmp@0.0.33': + resolution: {integrity: sha512-gVC1InwyVrO326wbBZw+AO3u2vRXz/iRWq9jYhpG4W8LXyIgDv3ZmcLQ5Q4Gs+gFMyqx+viFoFT+l3p61QFCmQ==} + '@types/vscode@1.118.0': resolution: {integrity: sha512-Ah6eTlqDcwIMELEVwQMO++rJAFBRz/oLluLD/vWdYrH1KuI9kfpaM+7pg0OvvascgcJy+ghLCERAYouM4QbzGw==} @@ -1496,6 +1747,10 @@ packages: engines: {node: '>= 20'} hasBin: true + accepts@1.3.8: + resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} + engines: {node: '>= 0.6'} + acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -1520,6 +1775,10 @@ packages: resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} engines: {node: '>=6'} + ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + ansi-escapes@7.3.0: resolution: {integrity: sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==} engines: {node: '>=18'} @@ -1536,6 +1795,17 @@ packages: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} + ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + application-config-path@0.1.1: + resolution: {integrity: sha512-zy9cHePtMP0YhwG+CfHm0bgwdnga2X3gZexpdCwEj//dpb+TKajtiC8REEUJUSq6Ab4f9cgNy2l8ObXzCXFkEw==} + argparse@1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} @@ -1546,6 +1816,9 @@ packages: resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} engines: {node: '>= 0.4'} + array-flatten@1.1.1: + resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} + array-includes@3.1.9: resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==} engines: {node: '>= 0.4'} @@ -1602,10 +1875,17 @@ packages: base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + batch@0.6.1: + resolution: {integrity: sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==} + better-path-resolve@1.0.0: resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} engines: {node: '>=4'} + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + binaryextensions@6.11.0: resolution: {integrity: sha512-sXnYK/Ij80TO3lcqZVV2YgfKN5QjUWIRk/XSm2J/4bd/lPko3lvk0O4ZppH6m+6hB2/GTu+ptNwVFe1xh+QLQw==} engines: {node: '>=4'} @@ -1613,6 +1893,10 @@ packages: bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + body-parser@1.20.5: + resolution: {integrity: sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} @@ -1646,10 +1930,30 @@ packages: resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} engines: {node: '>=18'} + busboy@1.6.0: + resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} + engines: {node: '>=10.16.0'} + + bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + cac@6.7.14: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} + cacache@20.0.4: + resolution: {integrity: sha512-M3Lab8NPYlZU2exsL3bMVvMrMqgwCnMWfdZbK28bn3pK6APT/Te/I8hjRPNu1uwORY9a1eEQoifXbKPQMfMTOA==} + engines: {node: ^20.17.0 || >=22.9.0} + + cacheable-lookup@5.0.4: + resolution: {integrity: sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==} + engines: {node: '>=10.6.0'} + + cacheable-request@7.0.4: + resolution: {integrity: sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==} + engines: {node: '>=8'} + call-bind-apply-helpers@1.0.2: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} @@ -1692,9 +1996,36 @@ packages: resolution: {integrity: sha512-WDrybc/gKFpTYQutKIK6UvfcuxijIZfMfXaYm8NMsPQxSYvf+13fXUJ4rztGGbJcBQ/GF55gvrZ0Bc0bj/mqvg==} engines: {node: '>=20.18.1'} + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + + chokidar@5.0.0: + resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} + engines: {node: '>= 20.19.0'} + chownr@1.1.4: resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} + cli-cursor@3.1.0: + resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} + engines: {node: '>=8'} + + cli-cursor@5.0.0: + resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} + engines: {node: '>=18'} + + cli-spinners@2.9.2: + resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} + engines: {node: '>=6'} + + clone-response@1.0.3: + resolution: {integrity: sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==} + + clone@1.0.4: + resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} + engines: {node: '>=0.8'} + cockatiel@3.2.1: resolution: {integrity: sha512-gfrHV6ZPkquExvMh9IOkKsBzNDk6sDuZ6DdBGUBkvFnTCqCxzpuq48RySgP0AnaqQkw2zynOFj9yly6T1Q2G5Q==} engines: {node: '>=16'} @@ -1710,16 +2041,61 @@ packages: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} + command-exists@1.2.9: + resolution: {integrity: sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==} + commander@12.1.0: resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} engines: {node: '>=18'} + commander@14.0.3: + resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==} + engines: {node: '>=20'} + commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + commander@6.2.1: + resolution: {integrity: sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==} + engines: {node: '>= 6'} + + commander@8.3.0: + resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} + engines: {node: '>= 12'} + concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + config-chain@1.1.13: + resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} + + connect@3.7.0: + resolution: {integrity: sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==} + engines: {node: '>= 0.10.0'} + + content-disposition@0.5.4: + resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} + engines: {node: '>= 0.6'} + + content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + + cookie-signature@1.0.7: + resolution: {integrity: sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==} + + cookie-signature@1.2.2: + resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} + engines: {node: '>=6.6.0'} + + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + engines: {node: '>= 0.6'} + + cross-spawn@6.0.5: + resolution: {integrity: sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==} + engines: {node: '>=4.8'} + cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -1754,6 +2130,14 @@ packages: dataloader@1.4.0: resolution: {integrity: sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw==} + debug@2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + debug@3.2.7: resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} peerDependencies: @@ -1762,6 +2146,15 @@ packages: supports-color: optional: true + debug@4.3.7: + resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + debug@4.4.3: resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} engines: {node: '>=6.0'} @@ -1797,6 +2190,13 @@ packages: resolution: {integrity: sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==} engines: {node: '>=18'} + defaults@1.0.4: + resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} + + defer-to-connect@2.0.1: + resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==} + engines: {node: '>=10'} + define-data-property@1.1.4: resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} engines: {node: '>= 0.4'} @@ -1813,6 +2213,18 @@ packages: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} + depd@1.1.2: + resolution: {integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==} + engines: {node: '>= 0.6'} + + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + + destroy@1.2.0: + resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + detect-indent@6.1.0: resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} engines: {node: '>=8'} @@ -1821,6 +2233,13 @@ packages: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} + devcert@1.2.3: + resolution: {integrity: sha512-vmLo0hDNHmZ47HED1ZiouJ7cAcamL8HY7qa9YdmCBkXxHEVtdDgT9pN/Xy3ZkcF3pFjF0sqq8WMV93HF2nmHHw==} + + diff-sequences@27.5.1: + resolution: {integrity: sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dir-glob@3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} @@ -1857,9 +2276,41 @@ packages: resolution: {integrity: sha512-UgGlf8IW75je7HZjNDpJdCv4cGJWIi6yumFdZ0R7A8/CIhQiWUjyGLCxdHpd8bmyD1gnkfUNK0oeOXqUS2cpfQ==} engines: {ecmascript: '>= es5', node: '>=4'} + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + effect@3.21.2: resolution: {integrity: sha512-rXd2FGDM8KdjSIrc+mqEELo7ScW7xTVxEf1iInmPSpIde9/nyGuFM710cjTo7/EreGXiUX2MOonPpprbz2XHCg==} + elm-codegen@0.6.3: + resolution: {integrity: sha512-X9S7ltwF78gjOmL5Rl79sMszMJlsBh44VwSXBgUah8vkobkRog2yfyMmMzyZmac/hzoRTP6Z98FFJY4A/xtUpQ==} + hasBin: true + + elm-doc-preview@6.0.1: + resolution: {integrity: sha512-G/xgVRKpbLuqliQmBINQFE6I0YAwqET2+SabiyXE47kaVChkhIz91HlZudUvCzTG/CV2ci7AERtAwYzQ4bjOKA==} + engines: {node: '>=20'} + hasBin: true + + elm-hot@1.1.6: + resolution: {integrity: sha512-zYZJlfs7Gt4BdjA+D+857K+XAWzwwySJmXCgFpHW1dIEfaHSZCIPYPf7/jinZBLfKRkOAlKzI32AA84DY50g7Q==} + + elm-optimize-level-2@0.3.5: + resolution: {integrity: sha512-t1xl8zR9UBspdmEMuLBE/qTLP+Ew3L4PzKLhSY/PsO21HU3jZ1ULmKxDtZmJPVG4ciqsY1JSl/GFbaSdQ1sX9Q==} + engines: {node: '>=16'} + hasBin: true + + elm-pages@3.5.1: + resolution: {integrity: sha512-EWEVXHsAZcFJPOduzn1MCYixwmf7QgIY7+ABJKNI+eqibJDTDhzoyj/glBhZBDJ5MtXNaqxQSqUQHZ8xWv7BrA==} + hasBin: true + + elm-review@2.13.5: + resolution: {integrity: sha512-Iw3YztGjmuGyn4nIEVOxqx+dacJialprpB0ZVrBHA0sBBOQ9+QhBaQnQ0tp0G8U8UoIAush0M7XoBqzUN1F/+A==} + engines: {node: 14 >=14.21 || 16 >=16.20 || 18 || 20 || >=22} + hasBin: true + + elm-solve-deps-wasm@2.0.0: + resolution: {integrity: sha512-11OV8FgB9qsth/F94q2SJjb1MoEgbworSyNM1L+YlxVoaxp7wtWPyA8cNcPEkSoIKG1B8Tqg68ED1P6dVamHSg==} + elm-tooling@1.17.0: resolution: {integrity: sha512-Y6umJYX7w/tV08pgmNF95nkvPq+xOvhirJz6LIt4YUj2I9V2W0V4Yb1E0IUZ/9X9yUgiEcFpKyObav4QMWCPrg==} hasBin: true @@ -1867,6 +2318,14 @@ packages: emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + encodeurl@1.0.2: + resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} + engines: {node: '>= 0.8'} + + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + encoding-sniffer@0.2.1: resolution: {integrity: sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==} @@ -1893,6 +2352,9 @@ packages: resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} engines: {node: '>=18'} + eol@0.9.1: + resolution: {integrity: sha512-Ds/TEoZjwggRoz/Q2O7SE3i4Jm66mqTDfmdHdq/7DKVk3bro9Q8h6WdXKdPqFLMoqxrDK5SVRzHVPOS6uuGtrg==} + es-abstract@1.24.2: resolution: {integrity: sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==} engines: {node: '>= 0.4'} @@ -1934,6 +2396,9 @@ packages: engines: {node: '>=18'} hasBin: true + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} @@ -2046,6 +2511,10 @@ packages: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + expand-template@2.0.3: resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} engines: {node: '>=6'} @@ -2054,6 +2523,20 @@ packages: resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} engines: {node: '>=12.0.0'} + express-ws@5.0.2: + resolution: {integrity: sha512-0uvmuk61O9HXgLhGl3QhNSEtRsQevtmbL94/eILaliEADZBHZOQUAiHFrGPrgsjikohyrmSG5g+sCfASTt0lkQ==} + engines: {node: '>=4.5.0'} + peerDependencies: + express: ^4.0.0 || ^5.0.0-alpha.1 + + express@4.22.2: + resolution: {integrity: sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==} + engines: {node: '>= 0.10.0'} + + extend-shallow@2.0.1: + resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==} + engines: {node: '>=0.10.0'} + extendable-error@0.1.7: resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} @@ -2080,6 +2563,10 @@ packages: fast-uri@3.1.2: resolution: {integrity: sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==} + fastest-levenshtein@1.0.16: + resolution: {integrity: sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==} + engines: {node: '>= 4.9.1'} + fastq@1.20.1: resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} @@ -2100,6 +2587,19 @@ packages: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} + finalhandler@1.1.2: + resolution: {integrity: sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==} + engines: {node: '>= 0.8'} + + finalhandler@1.3.2: + resolution: {integrity: sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==} + engines: {node: '>= 0.8'} + + find-elm-dependencies@2.0.4: + resolution: {integrity: sha512-x/4w4fVmlD2X4PD9oQ+yh9EyaQef6OtEULdMGBTuWx0Nkppvo2Z/bAiQioW2n+GdRYKypME2b9OmYTw5tw5qDg==} + engines: {node: '>=4.0.0'} + hasBin: true + find-my-way-ts@0.1.6: resolution: {integrity: sha512-a85L9ZoXtNAey3Y6Z+eBWW658kO/MwR7zIafkIUPUMf3isZG0NCs2pjW2wtjxAKuJPxMAsHUIP4ZPGv0o5gyTA==} @@ -2111,6 +2611,10 @@ packages: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} + firstline@1.3.1: + resolution: {integrity: sha512-ycwgqtoxujz1dm0kjkBFOPQMESxB9uKc/PlD951dQDIG+tBXRpYZC2UmJb0gDxopQ1ZX6oyRQN3goRczYu7Deg==} + engines: {node: '>=6.4.0'} + flat-cache@4.0.1: resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} engines: {node: '>=16'} @@ -2118,6 +2622,11 @@ packages: flatted@3.4.2: resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} + folder-hash@3.3.4: + resolution: {integrity: sha512-ygx41kOst5mOBJbcMq+BTtadOIPxLBtj0CAI9QVY8DUSW2WcKQd6yD/i49cL3qEwI5iJryoQ1+kzZE3FyJX4ug==} + engines: {node: '>=6.0.0'} + hasBin: true + for-each@0.3.5: resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} engines: {node: '>= 0.4'} @@ -2130,6 +2639,18 @@ packages: resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} engines: {node: '>= 6'} + forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + + fresh@0.5.2: + resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} + engines: {node: '>= 0.6'} + + fresh@2.0.0: + resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} + engines: {node: '>= 0.8'} + fs-constants@1.0.0: resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} @@ -2145,6 +2666,13 @@ packages: resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} engines: {node: '>=6 <7 || >=8'} + fs-minipass@3.0.3: + resolution: {integrity: sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + fsevents@2.3.2: resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -2173,10 +2701,18 @@ packages: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} + get-port@3.2.0: + resolution: {integrity: sha512-x5UJKlgeUiNT8nyo/AcnwLnZuZNcSjSw0kogRB+Whd1fjjFq4B1hySFxSFWWSn4mIBzg3sRNUDFYc4g5gjPoLg==} + engines: {node: '>=4'} + get-proto@1.0.1: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} + get-stream@5.2.0: + resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} + engines: {node: '>=8'} + get-symbol-description@1.1.0: resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} engines: {node: '>= 0.4'} @@ -2192,12 +2728,26 @@ packages: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} + glob-to-regex.js@1.2.0: + resolution: {integrity: sha512-QMwlOQKU/IzqMUOAZWubUOT8Qft+Y0KQWnX9nK3ch0CJg0tTp4TvGZsTfudYKv2NzoQSyPcnA6TYeIQ3jGichQ==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + glob@11.1.0: resolution: {integrity: sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==} engines: {node: 20 || >=22} deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me hasBin: true + glob@13.0.6: + resolution: {integrity: sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==} + engines: {node: 18 || 20 || >=22} + + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + globals@14.0.0: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} @@ -2214,13 +2764,28 @@ packages: resolution: {integrity: sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==} engines: {node: '>=18'} + globby@16.2.0: + resolution: {integrity: sha512-QrJia2qDf5BB/V6HYlDTs0I0lBahyjLzpGQg3KT7FnCdTonAyPy2RtY802m2k4ALx6Dp752f82WsOczEVr3l6Q==} + engines: {node: '>=20'} + gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} + got@11.8.6: + resolution: {integrity: sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==} + engines: {node: '>=10.19.0'} + + graceful-fs@4.2.10: + resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} + graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + gray-matter@4.0.3: + resolution: {integrity: sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==} + engines: {node: '>=6.0'} + has-bigints@1.1.0: resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} engines: {node: '>= 0.4'} @@ -2267,10 +2832,25 @@ packages: htmlparser2@10.1.0: resolution: {integrity: sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==} + http-cache-semantics@4.2.0: + resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==} + + http-errors@1.8.1: + resolution: {integrity: sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==} + engines: {node: '>= 0.6'} + + http-errors@2.0.1: + resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} + engines: {node: '>= 0.8'} + http-proxy-agent@7.0.2: resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} engines: {node: '>= 14'} + http2-wrapper@1.0.3: + resolution: {integrity: sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==} + engines: {node: '>=10.19.0'} + https-proxy-agent@7.0.6: resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} engines: {node: '>= 14'} @@ -2279,6 +2859,14 @@ packages: resolution: {integrity: sha512-tsYlhAYpjCKa//8rXZ9DqKEawhPoSytweBC2eNvcaDK+57RZLHGqNs3PZTQO6yekLFSuvA6AlnAfrw1uBvtb+Q==} hasBin: true + hyperdyperid@1.2.0: + resolution: {integrity: sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==} + engines: {node: '>=10.18'} + + iconv-lite@0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + iconv-lite@0.6.3: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} @@ -2316,6 +2904,10 @@ packages: resolution: {integrity: sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==} engines: {node: '>=18'} + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} @@ -2330,6 +2922,14 @@ packages: resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} + ip-address@10.2.0: + resolution: {integrity: sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==} + engines: {node: '>= 12'} + + ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + is-array-buffer@3.0.5: resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} engines: {node: '>= 0.4'} @@ -2342,6 +2942,10 @@ packages: resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} engines: {node: '>= 0.4'} + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + is-boolean-object@1.2.2: resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} engines: {node: '>= 0.4'} @@ -2367,6 +2971,10 @@ packages: engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} hasBin: true + is-extendable@0.1.1: + resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==} + engines: {node: '>=0.10.0'} + is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -2392,6 +3000,10 @@ packages: engines: {node: '>=14.16'} hasBin: true + is-interactive@1.0.0: + resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} + engines: {node: '>=8'} + is-map@2.0.3: resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} engines: {node: '>= 0.4'} @@ -2408,6 +3020,10 @@ packages: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} + is-path-inside@4.0.0: + resolution: {integrity: sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==} + engines: {node: '>=12'} + is-potential-custom-element-name@1.0.1: resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} @@ -2439,6 +3055,13 @@ packages: resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} engines: {node: '>= 0.4'} + is-unicode-supported@0.1.0: + resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} + engines: {node: '>=10'} + + is-valid-domain@0.1.6: + resolution: {integrity: sha512-ZKtq737eFkZr71At8NxOFcP9O1K89gW3DkdrGMpp1upr/ueWjj+Weh4l9AI4rN0Gt8W2M1w7jrG2b/Yv83Ljpg==} + is-weakmap@2.0.2: resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} engines: {node: '>= 0.4'} @@ -2465,6 +3088,10 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + isexe@4.0.0: + resolution: {integrity: sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==} + engines: {node: '>=20'} + istextorbinary@9.5.0: resolution: {integrity: sha512-5mbUj3SiZXCuRf9fT3ibzbSSEWiy63gFfksmGfdOzujPjW3k+z8WvIBxcJHBoQNlaZaiyB25deviif2+osLmLw==} engines: {node: '>=4'} @@ -2473,6 +3100,18 @@ packages: resolution: {integrity: sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg==} engines: {node: 20 || >=22} + jest-diff@27.5.1: + resolution: {integrity: sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + jest-get-type@27.5.1: + resolution: {integrity: sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + jest-matcher-utils@27.5.1: + resolution: {integrity: sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -2542,8 +3181,32 @@ packages: keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - kubernetes-types@1.30.0: - resolution: {integrity: sha512-Dew1okvhM/SQcIa2rcgujNndZwU8VnSapDgdxlYoB84ZlpAD43U6KLAFqYo17ykSFGHNPrg0qry0bP+GJd9v7Q==} + kind-of@6.0.3: + resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} + engines: {node: '>=0.10.0'} + + kleur@3.0.3: + resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} + engines: {node: '>=6'} + + kleur@4.1.5: + resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} + engines: {node: '>=6'} + + kubernetes-types@1.30.0: + resolution: {integrity: sha512-Dew1okvhM/SQcIa2rcgujNndZwU8VnSapDgdxlYoB84ZlpAD43U6KLAFqYo17ykSFGHNPrg0qry0bP+GJd9v7Q==} + + ky@1.14.3: + resolution: {integrity: sha512-9zy9lkjac+TR1c2tG+mkNSVlyOpInnWdSMiue4F+kq8TwJSgv6o8jhLRg8Ho6SnZ9wOYUq/yozts9qQCfk7bIw==} + engines: {node: '>=18'} + + lamdera@0.19.1-1.4.0: + resolution: {integrity: sha512-F5HqnNJPXulkr6/0XCNhaRhESjewG2iTHc+EKg8mJLj76E2PscuUOVhnBEjPqeFrEZF6RT53qSeWtSYG61fhpQ==} + hasBin: true + + latest-version@9.0.0: + resolution: {integrity: sha512-7W0vV3rqv5tokqkBAFV1LbR7HPOWzXQDpDgEuib/aJ1jsZZx6x3c2mBI+TJhJzOhkGeaLbCKEHXEXLfirtG2JA==} + engines: {node: '>=18'} lefthook-darwin-arm64@2.1.6: resolution: {integrity: sha512-hyB7eeiX78BS66f70byTJacDLC/xV1vgMv9n+idFUsrM7J3Udd/ag9Ag5NP3t0eN0EqQqAtrNnt35EH01lxnRQ==} @@ -2651,9 +3314,17 @@ packages: lodash@4.18.1: resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} + log-symbols@4.1.0: + resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} + engines: {node: '>=10'} + loupe@3.2.1: resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} + lowercase-keys@2.0.0: + resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==} + engines: {node: '>=8'} + lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} @@ -2668,6 +3339,10 @@ packages: magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + make-fetch-happen@15.0.5: + resolution: {integrity: sha512-uCbIa8jWWmQZt4dSnEStkVC6gdakiinAm4PiGsywIkguF0eWMdcjDz0ECYhUolFU3pFLOev9VNPCEygydXnddg==} + engines: {node: ^20.17.0 || >=22.9.0} + markdown-it@14.1.1: resolution: {integrity: sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==} hasBin: true @@ -2679,10 +3354,26 @@ packages: mdurl@2.0.0: resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} + media-typer@0.3.0: + resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} + engines: {node: '>= 0.6'} + + memfs@4.57.2: + resolution: {integrity: sha512-2nWzSsJzrukurSDna4Z0WywuScK4Id3tSKejgu74u8KCdW4uNrseKRSIDg75C6Yw5ZRqBe0F0EtMNlTbUq8bAQ==} + peerDependencies: + tslib: '2' + + merge-descriptors@1.0.3: + resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==} + merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} + methods@1.1.2: + resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} + engines: {node: '>= 0.6'} + micromatch@4.0.8: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} @@ -2691,10 +3382,18 @@ packages: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} + mime-db@1.54.0: + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} + engines: {node: '>= 0.6'} + mime-types@2.1.35: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} + mime-types@3.0.2: + resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==} + engines: {node: '>=18'} + mime@1.6.0: resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} engines: {node: '>=4'} @@ -2705,6 +3404,18 @@ packages: engines: {node: '>=10.0.0'} hasBin: true + mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + + mimic-function@5.0.1: + resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} + engines: {node: '>=18'} + + mimic-response@1.0.1: + resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==} + engines: {node: '>=4'} + mimic-response@3.1.0: resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} engines: {node: '>=10'} @@ -2719,17 +3430,52 @@ packages: minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + minipass-collect@2.0.1: + resolution: {integrity: sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==} + engines: {node: '>=16 || 14 >=14.17'} + + minipass-fetch@5.0.2: + resolution: {integrity: sha512-2d0q2a8eCi2IRg/IGubCNRJoYbA1+YPXAzQVRFmB45gdGZafyivnZ5YSEfo3JikbjGxOdntGFvBQGqaSMXlAFQ==} + engines: {node: ^20.17.0 || >=22.9.0} + + minipass-flush@1.0.7: + resolution: {integrity: sha512-TbqTz9cUwWyHS2Dy89P3ocAGUGxKjjLuR9z8w4WUTGAVgEj17/4nhgo2Du56i0Fm3Pm30g4iA8Lcqctc76jCzA==} + engines: {node: '>= 8'} + + minipass-pipeline@1.2.4: + resolution: {integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==} + engines: {node: '>=8'} + + minipass-sized@2.0.0: + resolution: {integrity: sha512-zSsHhto5BcUVM2m1LurnXY6M//cGhVaegT71OfOXoprxT6o780GZd792ea6FfrQkuU4usHZIUczAQMRUE2plzA==} + engines: {node: '>=8'} + + minipass@3.3.6: + resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} + engines: {node: '>=8'} + minipass@7.1.3: resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} engines: {node: '>=16 || 14 >=14.17'} + minizlib@3.1.0: + resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==} + engines: {node: '>= 18'} + mkdirp-classic@0.5.3: resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} + mkdirp@0.5.6: + resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} + hasBin: true + mri@1.2.0: resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} engines: {node: '>=4'} + ms@2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -2757,6 +3503,17 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + negotiator@0.6.3: + resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} + engines: {node: '>= 0.6'} + + negotiator@1.0.0: + resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} + engines: {node: '>= 0.6'} + + nice-try@1.0.5: + resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==} + node-abi@3.92.0: resolution: {integrity: sha512-KdHvFWZjEKDf0cakgFjebl371GPsISX2oZHcuyKqM7DtogIsHrqKeLTo8wBHxaXRAQlY2PsPlZmfo+9ZCxEREQ==} engines: {node: '>=10'} @@ -2767,6 +3524,10 @@ packages: node-addon-api@7.1.1: resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} + node-elm-compiler@5.0.6: + resolution: {integrity: sha512-DWTRQR8b54rvschcZRREdsz7K84lnS8A6YJu8du3QLQ8f204SJbyTaA6NzYYbfUG97OTRKRv/0KZl82cTfpLhA==} + engines: {node: '>=4.0.0'} + node-exports-info@1.6.0: resolution: {integrity: sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw==} engines: {node: '>= 0.4'} @@ -2792,6 +3553,14 @@ packages: resolution: {integrity: sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==} engines: {node: ^16.14.0 || >=18.0.0} + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + normalize-url@6.1.0: + resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==} + engines: {node: '>=10'} + nth-check@2.1.1: resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} @@ -2826,9 +3595,25 @@ packages: resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} engines: {node: '>= 0.4'} + on-finished@2.3.0: + resolution: {integrity: sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==} + engines: {node: '>= 0.8'} + + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + + onetime@7.0.0: + resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} + engines: {node: '>=18'} + open@10.2.0: resolution: {integrity: sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==} engines: {node: '>=18'} @@ -2837,6 +3622,14 @@ packages: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} + ora@5.4.1: + resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} + engines: {node: '>=10'} + + os-tmpdir@1.0.2: + resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} + engines: {node: '>=0.10.0'} + outdent@0.5.0: resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} @@ -2844,6 +3637,10 @@ packages: resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} engines: {node: '>= 0.4'} + p-cancelable@2.1.1: + resolution: {integrity: sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==} + engines: {node: '>=8'} + p-filter@2.1.0: resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} engines: {node: '>=8'} @@ -2879,6 +3676,10 @@ packages: package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + package-json@10.0.1: + resolution: {integrity: sha512-ua1L4OgXSBdsu1FPb7F3tYH0F48a6kxvod4pLUlGY9COeJAJQNX/sNH2IiEmsxw7lqYiAwrdHMjz1FctOsyDQg==} + engines: {node: '>=18'} + package-manager-detector@0.2.11: resolution: {integrity: sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ==} @@ -2902,10 +3703,25 @@ packages: parse5@7.3.0: resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} + parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + + password-prompt@1.1.3: + resolution: {integrity: sha512-HkrjG2aJlvF0t2BMH0e2LB/EHf3Lcq3fNMzy4GYHcQblAvOl+QQji1Lx7WRBMqpVK8p+KR7bCg7oqAMXtdgqyw==} + path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + + path-key@2.0.1: + resolution: {integrity: sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==} + engines: {node: '>=4'} + path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} @@ -2917,6 +3733,9 @@ packages: resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} engines: {node: 18 || 20 || >=22} + path-to-regexp@0.1.13: + resolution: {integrity: sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==} + path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} @@ -2999,6 +3818,25 @@ packages: engines: {node: '>=14'} hasBin: true + pretty-format@27.5.1: + resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + proc-log@6.1.0: + resolution: {integrity: sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==} + engines: {node: ^20.17.0 || >=22.9.0} + + prompts@2.4.2: + resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} + engines: {node: '>= 6'} + + proto-list@1.2.4: + resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} + + proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + pump@3.0.4: resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==} @@ -3023,6 +3861,18 @@ packages: queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + quick-lru@5.1.1: + resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} + engines: {node: '>=10'} + + range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + + raw-body@2.5.3: + resolution: {integrity: sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==} + engines: {node: '>= 0.8'} + rc-config-loader@4.1.4: resolution: {integrity: sha512-3GiwEzklkbXTDp52UR5nT8iXgYAx1V9ZG/kDZT7p60u2GCv2XTwQq4NzinMoMpNtXhmt3WkhYXcj6HH8HdwCEQ==} @@ -3030,6 +3880,9 @@ packages: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} hasBin: true + react-is@17.0.2: + resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + read-pkg@9.0.1: resolution: {integrity: sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA==} engines: {node: '>=18'} @@ -3046,6 +3899,14 @@ packages: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + + readdirp@5.0.0: + resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==} + engines: {node: '>= 20.19.0'} + redux-thunk@3.1.0: resolution: {integrity: sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==} peerDependencies: @@ -3062,6 +3923,14 @@ packages: resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} engines: {node: '>= 0.4'} + registry-auth-token@5.1.1: + resolution: {integrity: sha512-P7B4+jq8DeD2nMsAcdfaqHbssgHtZ7Z5+++a5ask90fvmJ8p5je4mOa+wzu+DB4vQ5tdJV/xywY+UnVFeQLV5Q==} + engines: {node: '>=14'} + + registry-url@6.0.1: + resolution: {integrity: sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q==} + engines: {node: '>=12'} + require-from-string@2.0.2: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} @@ -3069,6 +3938,9 @@ packages: reselect@5.1.1: resolution: {integrity: sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==} + resolve-alpn@1.2.1: + resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} + resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -3082,10 +3954,31 @@ packages: engines: {node: '>= 0.4'} hasBin: true + responselike@2.0.1: + resolution: {integrity: sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==} + + restore-cursor@3.1.0: + resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} + engines: {node: '>=8'} + + restore-cursor@5.1.0: + resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} + engines: {node: '>=18'} + reusify@1.1.0: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + rimraf@2.6.3: + resolution: {integrity: sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + + rimraf@2.7.1: + resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + rollup@4.60.3: resolution: {integrity: sha512-pAQK9HalE84QSm4Po3EmWIZPd3FnjkShVkiMlz1iligWYkWQ7wHYd1PF/T7QZ5TVSD6uSTon5gBVMSM4JfBV+A==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -3132,6 +4025,10 @@ packages: engines: {node: '>=20.0.0'} hasBin: true + section-matter@1.0.0: + resolution: {integrity: sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==} + engines: {node: '>=4'} + semver@5.7.2: resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} hasBin: true @@ -3145,6 +4042,26 @@ packages: engines: {node: '>=10'} hasBin: true + send@0.19.2: + resolution: {integrity: sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==} + engines: {node: '>= 0.8.0'} + + send@1.2.1: + resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} + engines: {node: '>= 18'} + + serve-index@1.9.2: + resolution: {integrity: sha512-KDj11HScOaLmrPxl70KYNW1PksP4Nb/CLL2yvC+Qd2kHMPEEpfc4Re2e4FOay+bC/+XQl/7zAcWON3JVo5v3KQ==} + engines: {node: '>= 0.8.0'} + + serve-static@1.16.3: + resolution: {integrity: sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==} + engines: {node: '>= 0.8.0'} + + serve-static@2.2.1: + resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==} + engines: {node: '>= 18'} + set-function-length@1.2.2: resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} engines: {node: '>= 0.4'} @@ -3157,10 +4074,21 @@ packages: resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} engines: {node: '>= 0.4'} + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + + shebang-command@1.2.0: + resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} + engines: {node: '>=0.10.0'} + shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} + shebang-regex@1.0.0: + resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==} + engines: {node: '>=0.10.0'} + shebang-regex@3.0.0: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} @@ -3184,6 +4112,9 @@ packages: siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + signal-exit@4.1.0: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} @@ -3194,6 +4125,9 @@ packages: simple-get@4.0.1: resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==} + sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + slash@3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} @@ -3206,6 +4140,18 @@ packages: resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} engines: {node: '>=10'} + smart-buffer@4.2.0: + resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} + engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} + + socks-proxy-agent@8.0.5: + resolution: {integrity: sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==} + engines: {node: '>= 14'} + + socks@2.8.9: + resolution: {integrity: sha512-LJhUYUvItdQ0LkJTmPeaEObWXAqFyfmP85x0tch/ez9cahmhlBBLbIqDFnvBnUJGagb0JbIQrkBs1wJ+yRYpEw==} + engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} + source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} @@ -3235,9 +4181,21 @@ packages: sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + ssri@13.0.1: + resolution: {integrity: sha512-QUiRf1+u9wPTL/76GTYlKttDEBWV1ga9ZXW8BG6kfdeyyM8LGPix9gROyg9V2+P0xNyF3X2Go526xKFdMZrHSQ==} + engines: {node: ^20.17.0 || >=22.9.0} + stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + statuses@1.5.0: + resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==} + engines: {node: '>= 0.6'} + + statuses@2.0.2: + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} + engines: {node: '>= 0.8'} + std-env@3.10.0: resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} @@ -3245,6 +4203,10 @@ packages: resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} engines: {node: '>= 0.4'} + streamsearch@1.1.0: + resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} + engines: {node: '>=10.0.0'} + string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -3272,6 +4234,10 @@ packages: resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} engines: {node: '>=12'} + strip-bom-string@1.0.0: + resolution: {integrity: sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==} + engines: {node: '>=0.10.0'} + strip-bom@3.0.0: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'} @@ -3290,10 +4256,18 @@ packages: structured-source@4.0.0: resolution: {integrity: sha512-qGzRFNJDjFieQkl/sVOI2dUjHKRyL9dAJi2gCPGJLbJHBIkyOHxjuocpIEfbLioX+qSJpvbYdT49/YCdMznKxA==} + sudo-prompt@8.2.5: + resolution: {integrity: sha512-rlBo3HU/1zAJUrkY6jNxDOC9eVYliG6nS4JA8u8KAshITd07tafMc/Br7xQwCSseXwJ2iCcHCE8SNWX3q8Z+kw==} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. + supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} + supports-hyperlinks@2.3.0: + resolution: {integrity: sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==} + engines: {node: '>=8'} + supports-hyperlinks@3.2.0: resolution: {integrity: sha512-zFObLMyZeEwzAoKCyu1B91U79K2t7ApXuQfo8OuxwXLDgcKxuwM+YvcbIhm6QWqz7mHUH1TVytR1PwVVjEuMig==} engines: {node: '>=14.18'} @@ -3365,10 +4339,18 @@ packages: resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} engines: {node: '>=6'} + temp@0.9.4: + resolution: {integrity: sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==} + engines: {node: '>=6.0.0'} + term-size@2.2.1: resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} engines: {node: '>=8'} + terminal-link@2.1.1: + resolution: {integrity: sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==} + engines: {node: '>=8'} + terminal-link@4.0.0: resolution: {integrity: sha512-lk+vH+MccxNqgVqSnkMVKx4VLJfnLjDBGzH16JVZjKE2DoxP57s6/vt6JmXV5I3jBcfGrxNrYtC+mPtU7WJztA==} engines: {node: '>=18'} @@ -3385,6 +4367,12 @@ packages: resolution: {integrity: sha512-tXJwSr9355kFJI3lbCkPpUH5cP8/M0GGy2xLO34aZCjMXBaK3SoPnZwr/oWmo1FdCnELcs4npdCIOFtq9W3ruQ==} engines: {node: '>=4'} + thingies@2.6.0: + resolution: {integrity: sha512-rMHRjmlFLM1R96UYPvpmnc3LYtdFrT33JIB7L9hetGue1qAPfn1N2LJeEjxUSidu1Iku+haLZXDuEXUHNGO/lg==} + engines: {node: '>=10.18'} + peerDependencies: + tslib: ^2 + tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} @@ -3414,6 +4402,14 @@ packages: resolution: {integrity: sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==} hasBin: true + tmp@0.0.33: + resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} + engines: {node: '>=0.6.0'} + + tmp@0.2.3: + resolution: {integrity: sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==} + engines: {node: '>=14.14'} + tmp@0.2.5: resolution: {integrity: sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==} engines: {node: '>=14.14'} @@ -3422,6 +4418,10 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + toml@3.0.0: resolution: {integrity: sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==} @@ -3436,15 +4436,27 @@ packages: resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==} engines: {node: '>=18'} + tree-dump@1.1.0: + resolution: {integrity: sha512-rMuvhU4MCDbcbnleZTFezWsaZXRFemSqAM+7jPnzUl1fo9w3YEKOxAeui0fz3OI4EU4hf23iyA7uQRVko+UaBA==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + ts-api-utils@2.5.0: resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} engines: {node: '>=18.12'} peerDependencies: typescript: '>=4.8.4' + ts-union@2.3.0: + resolution: {integrity: sha512-OP+W9WoYvGlOMjc90D6nYz60jU1zQlXAg3VBtuSoMDejY94PaORkya9HtHjaaqqwA4I5/hN38fmKK0nSWj7jPg==} + tsconfig-paths@3.15.0: resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + tslib@1.14.1: + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} @@ -3459,10 +4471,18 @@ packages: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} + type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + type-fest@4.41.0: resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} engines: {node: '>=16'} + type-is@1.6.18: + resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} + engines: {node: '>= 0.6'} + typed-array-buffer@1.0.3: resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} engines: {node: '>= 0.4'} @@ -3482,6 +4502,11 @@ packages: typed-rest-client@1.8.11: resolution: {integrity: sha512-5UvfMpd1oelmUPRbbaVnq+rHP7ng2cE4qoQkQeAqxRL6PklkxsM0g32/HL0yfvruK6ojQ5x8EE+HF4YV6DtuCA==} + typescript@4.9.5: + resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==} + engines: {node: '>=4.2.0'} + hasBin: true + typescript@5.8.3: resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} engines: {node: '>=14.17'} @@ -3512,6 +4537,10 @@ packages: resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} engines: {node: '>=18'} + unicorn-magic@0.4.0: + resolution: {integrity: sha512-wH590V9VNgYH9g3lH9wWjTrUoKsjLF6sGLjhR4sH1LWpLmCOH0Zf7PukhDA8BiS7KHe4oPNkcTHqYkj7SOGUOw==} + engines: {node: '>=20'} + universalify@0.1.2: resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} engines: {node: '>= 4.0.0'} @@ -3520,6 +4549,10 @@ packages: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} + unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} @@ -3529,6 +4562,10 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + utils-merge@1.0.1: + resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} + engines: {node: '>= 0.4.0'} + uuid@11.1.1: resolution: {integrity: sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ==} hasBin: true @@ -3536,6 +4573,10 @@ packages: validate-npm-package-license@3.0.4: resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + version-range@4.15.0: resolution: {integrity: sha512-Ck0EJbAGxHwprkzFO966t4/5QkRuzh+/I1RxhLgUKKwEn+Cd8NwM60mE3AqBZg5gYODoXW0EFsQvbZjRlvdqbg==} engines: {node: '>=4'} @@ -3617,6 +4658,9 @@ packages: resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} engines: {node: '>=18'} + wcwidth@1.0.1: + resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} @@ -3656,11 +4700,20 @@ packages: resolution: {integrity: sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==} engines: {node: '>= 0.4'} + which@1.3.1: + resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} + hasBin: true + which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} hasBin: true + which@6.0.1: + resolution: {integrity: sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==} + engines: {node: ^20.17.0 || >=22.9.0} + hasBin: true + why-is-node-running@2.3.0: resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} engines: {node: '>=8'} @@ -3670,9 +4723,25 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + ws@7.5.10: + resolution: {integrity: sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==} + engines: {node: '>=8.3.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + ws@8.20.0: resolution: {integrity: sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==} engines: {node: '>=10.0.0'} @@ -4353,6 +5422,8 @@ snapshots: dependencies: '@forgerock/sdk-types': 2.0.0 + '@gar/promise-retry@1.0.3': {} + '@hono/node-server@1.19.14(hono@4.12.18)': dependencies: hono: 4.12.18 @@ -4401,63 +5472,221 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 - '@manypkg/find-root@1.1.0': + '@jsonjoy.com/base64@1.1.2(tslib@2.8.1)': dependencies: - '@babel/runtime': 7.29.2 - '@types/node': 12.20.55 - find-up: 4.1.0 - fs-extra: 8.1.0 + tslib: 2.8.1 - '@manypkg/get-packages@1.1.3': + '@jsonjoy.com/base64@17.67.0(tslib@2.8.1)': dependencies: - '@babel/runtime': 7.29.2 - '@changesets/types': 4.1.0 - '@manypkg/find-root': 1.1.0 - fs-extra: 8.1.0 - globby: 11.1.0 - read-yaml-file: 1.1.0 - - '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3': - optional: true + tslib: 2.8.1 - '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3': - optional: true + '@jsonjoy.com/buffers@1.2.1(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 - '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3': - optional: true + '@jsonjoy.com/buffers@17.67.0(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 - '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3': - optional: true + '@jsonjoy.com/codegen@1.0.0(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 - '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3': - optional: true + '@jsonjoy.com/codegen@17.67.0(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 - '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3': - optional: true + '@jsonjoy.com/fs-core@4.57.2(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/fs-node-builtins': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-node-utils': 4.57.2(tslib@2.8.1) + thingies: 2.6.0(tslib@2.8.1) + tslib: 2.8.1 - '@nodelib/fs.scandir@2.1.5': + '@jsonjoy.com/fs-fsa@4.57.2(tslib@2.8.1)': dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 + '@jsonjoy.com/fs-core': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-node-builtins': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-node-utils': 4.57.2(tslib@2.8.1) + thingies: 2.6.0(tslib@2.8.1) + tslib: 2.8.1 - '@nodelib/fs.stat@2.0.5': {} + '@jsonjoy.com/fs-node-builtins@4.57.2(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 - '@nodelib/fs.walk@1.2.8': + '@jsonjoy.com/fs-node-to-fsa@4.57.2(tslib@2.8.1)': dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.20.1 + '@jsonjoy.com/fs-fsa': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-node-builtins': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-node-utils': 4.57.2(tslib@2.8.1) + tslib: 2.8.1 - '@parcel/watcher-android-arm64@2.5.6': - optional: true + '@jsonjoy.com/fs-node-utils@4.57.2(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/fs-node-builtins': 4.57.2(tslib@2.8.1) + tslib: 2.8.1 - '@parcel/watcher-darwin-arm64@2.5.6': - optional: true + '@jsonjoy.com/fs-node@4.57.2(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/fs-core': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-node-builtins': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-node-utils': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-print': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-snapshot': 4.57.2(tslib@2.8.1) + glob-to-regex.js: 1.2.0(tslib@2.8.1) + thingies: 2.6.0(tslib@2.8.1) + tslib: 2.8.1 - '@parcel/watcher-darwin-x64@2.5.6': - optional: true + '@jsonjoy.com/fs-print@4.57.2(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/fs-node-utils': 4.57.2(tslib@2.8.1) + tree-dump: 1.1.0(tslib@2.8.1) + tslib: 2.8.1 - '@parcel/watcher-freebsd-x64@2.5.6': - optional: true + '@jsonjoy.com/fs-snapshot@4.57.2(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/buffers': 17.67.0(tslib@2.8.1) + '@jsonjoy.com/fs-node-utils': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/json-pack': 17.67.0(tslib@2.8.1) + '@jsonjoy.com/util': 17.67.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/json-pack@1.21.0(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/base64': 1.1.2(tslib@2.8.1) + '@jsonjoy.com/buffers': 1.2.1(tslib@2.8.1) + '@jsonjoy.com/codegen': 1.0.0(tslib@2.8.1) + '@jsonjoy.com/json-pointer': 1.0.2(tslib@2.8.1) + '@jsonjoy.com/util': 1.9.0(tslib@2.8.1) + hyperdyperid: 1.2.0 + thingies: 2.6.0(tslib@2.8.1) + tree-dump: 1.1.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/json-pack@17.67.0(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/base64': 17.67.0(tslib@2.8.1) + '@jsonjoy.com/buffers': 17.67.0(tslib@2.8.1) + '@jsonjoy.com/codegen': 17.67.0(tslib@2.8.1) + '@jsonjoy.com/json-pointer': 17.67.0(tslib@2.8.1) + '@jsonjoy.com/util': 17.67.0(tslib@2.8.1) + hyperdyperid: 1.2.0 + thingies: 2.6.0(tslib@2.8.1) + tree-dump: 1.1.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/json-pointer@1.0.2(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/codegen': 1.0.0(tslib@2.8.1) + '@jsonjoy.com/util': 1.9.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/json-pointer@17.67.0(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/util': 17.67.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/util@1.9.0(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/buffers': 1.2.1(tslib@2.8.1) + '@jsonjoy.com/codegen': 1.0.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/util@17.67.0(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/buffers': 17.67.0(tslib@2.8.1) + '@jsonjoy.com/codegen': 17.67.0(tslib@2.8.1) + tslib: 2.8.1 + + '@lamdera/compiler-darwin-arm64@0.19.1-1.4.0': + optional: true + + '@lamdera/compiler-darwin-x64@0.19.1-1.4.0': + optional: true + + '@lamdera/compiler-linux-arm64@0.19.1-1.4.0': + optional: true + + '@lamdera/compiler-linux-x64@0.19.1-1.4.0': + optional: true + + '@lamdera/compiler-win32-x64@0.19.1-1.4.0': + optional: true + + '@manypkg/find-root@1.1.0': + dependencies: + '@babel/runtime': 7.29.2 + '@types/node': 12.20.55 + find-up: 4.1.0 + fs-extra: 8.1.0 + + '@manypkg/get-packages@1.1.3': + dependencies: + '@babel/runtime': 7.29.2 + '@changesets/types': 4.1.0 + '@manypkg/find-root': 1.1.0 + fs-extra: 8.1.0 + globby: 11.1.0 + read-yaml-file: 1.1.0 + + '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3': + optional: true + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.20.1 + + '@npmcli/agent@4.0.0': + dependencies: + agent-base: 7.1.4 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + lru-cache: 11.3.6 + socks-proxy-agent: 8.0.5 + transitivePeerDependencies: + - supports-color + + '@npmcli/fs@5.0.0': + dependencies: + semver: 7.8.0 + + '@npmcli/redact@4.0.0': {} + + '@parcel/watcher-android-arm64@2.5.6': + optional: true + + '@parcel/watcher-darwin-arm64@2.5.6': + optional: true + + '@parcel/watcher-darwin-x64@2.5.6': + optional: true + + '@parcel/watcher-freebsd-x64@2.5.6': + optional: true '@parcel/watcher-linux-arm-glibc@2.5.6': optional: true @@ -4513,6 +5742,18 @@ snapshots: dependencies: playwright: 1.59.1 + '@pnpm/config.env-replace@1.1.0': {} + + '@pnpm/network.ca-file@1.0.2': + dependencies: + graceful-fs: 4.2.10 + + '@pnpm/npm-conf@3.0.2': + dependencies: + '@pnpm/config.env-replace': 1.1.0 + '@pnpm/network.ca-file': 1.0.2 + config-chain: 1.1.13 + '@reduxjs/toolkit@2.11.2': dependencies: '@standard-schema/spec': 1.1.0 @@ -4577,6 +5818,9 @@ snapshots: '@rollup/rollup-linux-s390x-gnu@4.60.3': optional: true + '@rollup/rollup-linux-x64-gnu@4.55.1': + optional: true + '@rollup/rollup-linux-x64-gnu@4.60.3': optional: true @@ -4677,12 +5921,20 @@ snapshots: '@secretlint/types@10.2.2': {} + '@sindresorhus/is@4.6.0': {} + '@sindresorhus/merge-streams@2.3.0': {} + '@sindresorhus/merge-streams@4.0.0': {} + '@standard-schema/spec@1.1.0': {} '@standard-schema/utils@0.3.0': {} + '@szmarczak/http-timer@4.0.6': + dependencies: + defer-to-connect: 2.0.1 + '@textlint/ast-node-types@15.6.0': {} '@textlint/linter-formatter@15.6.0': @@ -4712,6 +5964,13 @@ snapshots: dependencies: '@textlint/ast-node-types': 15.6.0 + '@types/cacheable-request@6.0.3': + dependencies: + '@types/http-cache-semantics': 4.2.0 + '@types/keyv': 3.1.4 + '@types/node': 22.19.18 + '@types/responselike': 1.0.3 + '@types/chai@5.2.3': dependencies: '@types/deep-eql': 4.0.2 @@ -4722,6 +5981,10 @@ snapshots: '@types/filesystem': 0.0.36 '@types/har-format': 1.2.16 + '@types/configstore@2.1.1': {} + + '@types/debug@0.0.30': {} + '@types/deep-eql@4.0.2': {} '@types/estree@1.0.8': {} @@ -4734,22 +5997,63 @@ snapshots: '@types/filewriter@0.0.33': {} + '@types/get-port@3.2.0': {} + + '@types/glob@5.0.38': + dependencies: + '@types/minimatch': 6.0.0 + '@types/node': 22.19.18 + '@types/har-format@1.2.16': {} + '@types/http-cache-semantics@4.2.0': {} + + '@types/jest@27.5.2': + dependencies: + jest-matcher-utils: 27.5.1 + pretty-format: 27.5.1 + '@types/json-schema@7.0.15': {} '@types/json5@0.0.29': {} + '@types/keyv@3.1.4': + dependencies: + '@types/node': 22.19.18 + + '@types/lodash@4.17.24': {} + + '@types/minimatch@6.0.0': + dependencies: + minimatch: 10.2.5 + + '@types/mkdirp@0.5.2': + dependencies: + '@types/node': 22.19.18 + '@types/node@12.20.55': {} '@types/node@22.19.18': dependencies: undici-types: 6.21.0 + '@types/node@8.10.66': {} + '@types/normalize-package-data@2.4.4': {} + '@types/responselike@1.0.3': + dependencies: + '@types/node': 22.19.18 + + '@types/rimraf@2.0.5': + dependencies: + '@types/glob': 5.0.38 + '@types/node': 22.19.18 + '@types/sarif@2.1.7': {} + '@types/tmp@0.0.33': {} + '@types/vscode@1.118.0': {} '@types/ws@8.18.1': @@ -4986,6 +6290,11 @@ snapshots: transitivePeerDependencies: - supports-color + accepts@1.3.8: + dependencies: + mime-types: 2.1.35 + negotiator: 0.6.3 + acorn-jsx@5.3.2(acorn@8.16.0): dependencies: acorn: 8.16.0 @@ -5010,6 +6319,10 @@ snapshots: ansi-colors@4.1.3: {} + ansi-escapes@4.3.2: + dependencies: + type-fest: 0.21.3 + ansi-escapes@7.3.0: dependencies: environment: 1.1.0 @@ -5022,6 +6335,15 @@ snapshots: dependencies: color-convert: 2.0.1 + ansi-styles@5.2.0: {} + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.2 + + application-config-path@0.1.1: {} + argparse@1.0.10: dependencies: sprintf-js: 1.0.3 @@ -5033,6 +6355,8 @@ snapshots: call-bound: 1.0.4 is-array-buffer: 3.0.5 + array-flatten@1.1.1: {} + array-includes@3.1.9: dependencies: call-bind: 1.0.9 @@ -5101,13 +6425,16 @@ snapshots: balanced-match@4.0.4: {} - base64-js@1.5.1: - optional: true + base64-js@1.5.1: {} + + batch@0.6.1: {} better-path-resolve@1.0.0: dependencies: is-windows: 1.0.2 + binary-extensions@2.3.0: {} + binaryextensions@6.11.0: dependencies: editions: 6.22.0 @@ -5117,7 +6444,23 @@ snapshots: buffer: 5.7.1 inherits: 2.0.4 readable-stream: 3.6.2 - optional: true + + body-parser@1.20.5: + 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 + transitivePeerDependencies: + - supports-color boolbase@1.0.0: {} @@ -5146,14 +6489,44 @@ snapshots: dependencies: base64-js: 1.5.1 ieee754: 1.2.1 - optional: true bundle-name@4.1.0: dependencies: run-applescript: 7.1.0 + busboy@1.6.0: + dependencies: + streamsearch: 1.1.0 + + bytes@3.1.2: {} + cac@6.7.14: {} + cacache@20.0.4: + dependencies: + '@npmcli/fs': 5.0.0 + fs-minipass: 3.0.3 + glob: 13.0.6 + lru-cache: 11.3.6 + minipass: 7.1.3 + minipass-collect: 2.0.1 + minipass-flush: 1.0.7 + minipass-pipeline: 1.2.4 + p-map: 7.0.4 + ssri: 13.0.1 + + cacheable-lookup@5.0.4: {} + + cacheable-request@7.0.4: + dependencies: + clone-response: 1.0.3 + get-stream: 5.2.0 + http-cache-semantics: 4.2.0 + keyv: 4.5.4 + lowercase-keys: 2.0.0 + normalize-url: 6.1.0 + responselike: 2.0.1 + call-bind-apply-helpers@1.0.2: dependencies: es-errors: 1.3.0 @@ -5215,9 +6588,41 @@ snapshots: undici: 7.25.0 whatwg-mimetype: 4.0.0 + chokidar@3.6.0: + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + + chokidar@5.0.0: + dependencies: + readdirp: 5.0.0 + chownr@1.1.4: optional: true + cli-cursor@3.1.0: + dependencies: + restore-cursor: 3.1.0 + + cli-cursor@5.0.0: + dependencies: + restore-cursor: 5.1.0 + + cli-spinners@2.9.2: {} + + clone-response@1.0.3: + dependencies: + mimic-response: 1.0.1 + + clone@1.0.4: {} + cockatiel@3.2.1: {} color-convert@2.0.1: @@ -5230,12 +6635,54 @@ snapshots: dependencies: delayed-stream: 1.0.0 + command-exists@1.2.9: {} + commander@12.1.0: {} + commander@14.0.3: {} + commander@2.20.3: {} + commander@6.2.1: {} + + commander@8.3.0: {} + concat-map@0.0.1: {} + config-chain@1.1.13: + dependencies: + ini: 1.3.8 + proto-list: 1.2.4 + + connect@3.7.0: + dependencies: + debug: 2.6.9 + finalhandler: 1.1.2 + parseurl: 1.3.3 + utils-merge: 1.0.1 + transitivePeerDependencies: + - supports-color + + content-disposition@0.5.4: + dependencies: + safe-buffer: 5.2.1 + + content-type@1.0.5: {} + + cookie-signature@1.0.7: {} + + cookie-signature@1.2.2: {} + + cookie@0.7.2: {} + + cross-spawn@6.0.5: + dependencies: + nice-try: 1.0.5 + path-key: 2.0.1 + semver: 5.7.2 + shebang-command: 1.2.0 + which: 1.3.1 + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -5282,10 +6729,18 @@ snapshots: dataloader@1.4.0: {} + debug@2.6.9: + dependencies: + ms: 2.0.0 + debug@3.2.7: dependencies: ms: 2.1.3 + debug@4.3.7: + dependencies: + ms: 2.1.3 + debug@4.4.3: dependencies: ms: 2.1.3 @@ -5295,12 +6750,10 @@ snapshots: decompress-response@6.0.0: dependencies: mimic-response: 3.1.0 - optional: true deep-eql@5.0.2: {} - deep-extend@0.6.0: - optional: true + deep-extend@0.6.0: {} deep-is@0.1.4: {} @@ -5311,6 +6764,12 @@ snapshots: bundle-name: 4.1.0 default-browser-id: 5.0.1 + defaults@1.0.4: + dependencies: + clone: 1.0.4 + + defer-to-connect@2.0.1: {} + define-data-property@1.1.4: dependencies: es-define-property: 1.0.1 @@ -5327,10 +6786,46 @@ snapshots: delayed-stream@1.0.0: {} + depd@1.1.2: {} + + depd@2.0.0: {} + + destroy@1.2.0: {} + detect-indent@6.1.0: {} detect-libc@2.1.2: {} + devcert@1.2.3: + dependencies: + '@types/configstore': 2.1.1 + '@types/debug': 0.0.30 + '@types/get-port': 3.2.0 + '@types/glob': 5.0.38 + '@types/lodash': 4.17.24 + '@types/mkdirp': 0.5.2 + '@types/node': 8.10.66 + '@types/rimraf': 2.0.5 + '@types/tmp': 0.0.33 + application-config-path: 0.1.1 + command-exists: 1.2.9 + debug: 3.2.7 + eol: 0.9.1 + get-port: 3.2.0 + glob: 7.2.3 + is-valid-domain: 0.1.6 + lodash: 4.18.1 + mkdirp: 0.5.6 + password-prompt: 1.1.3 + rimraf: 2.7.1 + sudo-prompt: 8.2.5 + tmp: 0.0.33 + tslib: 1.14.1 + transitivePeerDependencies: + - supports-color + + diff-sequences@27.5.1: {} + dir-glob@3.0.1: dependencies: path-type: 4.0.0 @@ -5373,15 +6868,128 @@ snapshots: dependencies: version-range: 4.15.0 + ee-first@1.1.1: {} + effect@3.21.2: dependencies: '@standard-schema/spec': 1.1.0 fast-check: 3.23.2 + elm-codegen@0.6.3: + dependencies: + chalk: 4.1.2 + chokidar: 3.6.0 + commander: 8.3.0 + node-elm-compiler: 5.0.6 + + elm-doc-preview@6.0.1: + dependencies: + chalk: 5.6.2 + chokidar: 3.6.0 + commander: 12.1.0 + cross-spawn: 7.0.6 + express: 4.22.2 + express-ws: 5.0.2(express@4.22.2) + glob: 11.1.0 + latest-version: 9.0.0 + open: 10.2.0 + serve-index: 1.9.2 + serve-static: 1.16.3 + tmp: 0.2.3 + ws: 8.20.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + elm-hot@1.1.6: {} + + elm-optimize-level-2@0.3.5: + dependencies: + '@types/jest': 27.5.2 + chalk: 4.1.2 + commander: 6.2.1 + node-elm-compiler: 5.0.6 + ts-union: 2.3.0 + typescript: 4.9.5 + + elm-pages@3.5.1(@types/node@22.19.18)(tslib@2.8.1)(yaml@2.9.0): + dependencies: + '@sindresorhus/merge-streams': 4.0.0 + busboy: 1.6.0 + chokidar: 5.0.0 + cli-cursor: 5.0.0 + commander: 14.0.3 + connect: 3.7.0 + cookie-signature: 1.2.2 + cross-spawn: 7.0.6 + devcert: 1.2.3 + elm-doc-preview: 6.0.1 + elm-hot: 1.1.6 + elm-optimize-level-2: 0.3.5 + esbuild: 0.27.7 + fs-extra: 11.3.5 + globby: 16.2.0 + gray-matter: 4.0.3 + kleur: 4.1.5 + make-fetch-happen: 15.0.5 + memfs: 4.57.2(tslib@2.8.1) + micromatch: 4.0.8 + serve-static: 2.2.1 + terser: 5.47.1 + vite: 7.3.3(@types/node@22.19.18)(terser@5.47.1)(yaml@2.9.0) + which: 6.0.1 + optionalDependencies: + '@rollup/rollup-linux-x64-gnu': 4.55.1 + transitivePeerDependencies: + - '@types/node' + - bufferutil + - jiti + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - tslib + - tsx + - utf-8-validate + - yaml + + elm-review@2.13.5: + dependencies: + chalk: 4.1.2 + chokidar: 3.6.0 + cross-spawn: 7.0.6 + elm-solve-deps-wasm: 2.0.0 + fastest-levenshtein: 1.0.16 + find-up: 5.0.0 + folder-hash: 3.3.4 + got: 11.8.6 + graceful-fs: 4.2.11 + minimist: 1.2.8 + ora: 5.4.1 + path-key: 3.1.1 + prompts: 2.4.2 + strip-ansi: 6.0.1 + terminal-link: 2.1.1 + tinyglobby: 0.2.16 + which: 2.0.2 + wrap-ansi: 7.0.0 + transitivePeerDependencies: + - supports-color + + elm-solve-deps-wasm@2.0.0: {} + elm-tooling@1.17.0: {} emoji-regex@8.0.0: {} + encodeurl@1.0.2: {} + + encodeurl@2.0.0: {} + encoding-sniffer@0.2.1: dependencies: iconv-lite: 0.6.3 @@ -5390,7 +6998,6 @@ snapshots: end-of-stream@1.4.5: dependencies: once: 1.4.0 - optional: true enquirer@2.4.1: dependencies: @@ -5405,6 +7012,8 @@ snapshots: environment@1.1.0: {} + eol@0.9.1: {} + es-abstract@1.24.2: dependencies: array-buffer-byte-length: 1.0.2 @@ -5547,6 +7156,8 @@ snapshots: '@esbuild/win32-ia32': 0.28.0 '@esbuild/win32-x64': 0.28.0 + escape-html@1.0.3: {} + escape-string-regexp@4.0.0: {} eslint-config-prettier@10.1.8(eslint@9.39.4): @@ -5683,11 +7294,61 @@ snapshots: esutils@2.0.3: {} + etag@1.8.1: {} + expand-template@2.0.3: optional: true expect-type@1.3.0: {} + express-ws@5.0.2(express@4.22.2): + dependencies: + express: 4.22.2 + ws: 7.5.10 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + express@4.22.2: + dependencies: + accepts: 1.3.8 + array-flatten: 1.1.1 + body-parser: 1.20.5 + content-disposition: 0.5.4 + content-type: 1.0.5 + cookie: 0.7.2 + cookie-signature: 1.0.7 + debug: 2.6.9 + depd: 2.0.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 1.3.2 + fresh: 0.5.2 + http-errors: 2.0.1 + merge-descriptors: 1.0.3 + methods: 1.1.2 + on-finished: 2.4.1 + parseurl: 1.3.3 + path-to-regexp: 0.1.13 + proxy-addr: 2.0.7 + qs: 6.15.1 + range-parser: 1.2.1 + safe-buffer: 5.2.1 + send: 0.19.2 + serve-static: 1.16.3 + setprototypeof: 1.2.0 + statuses: 2.0.2 + type-is: 1.6.18 + utils-merge: 1.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + extend-shallow@2.0.1: + dependencies: + is-extendable: 0.1.1 + extendable-error@0.1.7: {} fast-check@3.23.2: @@ -5712,6 +7373,8 @@ snapshots: fast-uri@3.1.2: {} + fastest-levenshtein@1.0.16: {} + fastq@1.20.1: dependencies: reusify: 1.1.0 @@ -5728,6 +7391,35 @@ snapshots: dependencies: to-regex-range: 5.0.1 + finalhandler@1.1.2: + dependencies: + debug: 2.6.9 + encodeurl: 1.0.2 + escape-html: 1.0.3 + on-finished: 2.3.0 + parseurl: 1.3.3 + statuses: 1.5.0 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + + finalhandler@1.3.2: + 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 + transitivePeerDependencies: + - supports-color + + find-elm-dependencies@2.0.4: + dependencies: + firstline: 1.3.1 + lodash: 4.18.1 + find-my-way-ts@0.1.6: {} find-up@4.1.0: @@ -5740,6 +7432,8 @@ snapshots: locate-path: 6.0.0 path-exists: 4.0.0 + firstline@1.3.1: {} + flat-cache@4.0.1: dependencies: flatted: 3.4.2 @@ -5747,6 +7441,14 @@ snapshots: flatted@3.4.2: {} + folder-hash@3.3.4: + dependencies: + debug: 4.3.7 + graceful-fs: 4.2.11 + minimatch: 3.1.5 + transitivePeerDependencies: + - supports-color + for-each@0.3.5: dependencies: is-callable: 1.2.7 @@ -5764,6 +7466,12 @@ snapshots: hasown: 2.0.3 mime-types: 2.1.35 + forwarded@0.2.0: {} + + fresh@0.5.2: {} + + fresh@2.0.0: {} + fs-constants@1.0.0: optional: true @@ -5785,6 +7493,12 @@ snapshots: jsonfile: 4.0.0 universalify: 0.1.2 + fs-minipass@3.0.3: + dependencies: + minipass: 7.1.3 + + fs.realpath@1.0.0: {} + fsevents@2.3.2: optional: true @@ -5819,11 +7533,17 @@ snapshots: hasown: 2.0.3 math-intrinsics: 1.1.0 + get-port@3.2.0: {} + get-proto@1.0.1: dependencies: dunder-proto: 1.0.1 es-object-atoms: 1.1.1 + get-stream@5.2.0: + dependencies: + pump: 3.0.4 + get-symbol-description@1.1.0: dependencies: call-bound: 1.0.4 @@ -5841,6 +7561,10 @@ snapshots: dependencies: is-glob: 4.0.3 + glob-to-regex.js@1.2.0(tslib@2.8.1): + dependencies: + tslib: 2.8.1 + glob@11.1.0: dependencies: foreground-child: 3.3.1 @@ -5850,6 +7574,21 @@ snapshots: package-json-from-dist: 1.0.1 path-scurry: 2.0.2 + glob@13.0.6: + dependencies: + minimatch: 10.2.5 + minipass: 7.1.3 + path-scurry: 2.0.2 + + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.5 + once: 1.4.0 + path-is-absolute: 1.0.1 + globals@14.0.0: {} globalthis@1.0.4: @@ -5875,10 +7614,42 @@ snapshots: slash: 5.1.0 unicorn-magic: 0.3.0 + globby@16.2.0: + dependencies: + '@sindresorhus/merge-streams': 4.0.0 + fast-glob: 3.3.3 + ignore: 7.0.5 + is-path-inside: 4.0.0 + slash: 5.1.0 + unicorn-magic: 0.4.0 + gopd@1.2.0: {} + got@11.8.6: + dependencies: + '@sindresorhus/is': 4.6.0 + '@szmarczak/http-timer': 4.0.6 + '@types/cacheable-request': 6.0.3 + '@types/responselike': 1.0.3 + cacheable-lookup: 5.0.4 + cacheable-request: 7.0.4 + decompress-response: 6.0.0 + http2-wrapper: 1.0.3 + lowercase-keys: 2.0.0 + p-cancelable: 2.1.1 + responselike: 2.0.1 + + graceful-fs@4.2.10: {} + graceful-fs@4.2.11: {} + gray-matter@4.0.3: + dependencies: + js-yaml: 3.14.2 + kind-of: 6.0.3 + section-matter: 1.0.0 + strip-bom-string: 1.0.0 + has-bigints@1.1.0: {} has-flag@4.0.0: {} @@ -5922,6 +7693,24 @@ snapshots: domutils: 3.2.2 entities: 7.0.1 + http-cache-semantics@4.2.0: {} + + http-errors@1.8.1: + dependencies: + depd: 1.1.2 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 1.5.0 + toidentifier: 1.0.1 + + http-errors@2.0.1: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.2 + toidentifier: 1.0.1 + http-proxy-agent@7.0.2: dependencies: agent-base: 7.1.4 @@ -5929,6 +7718,11 @@ snapshots: transitivePeerDependencies: - supports-color + http2-wrapper@1.0.3: + dependencies: + quick-lru: 5.1.1 + resolve-alpn: 1.2.1 + https-proxy-agent@7.0.6: dependencies: agent-base: 7.1.4 @@ -5938,6 +7732,12 @@ snapshots: human-id@4.1.3: {} + hyperdyperid@1.2.0: {} + + iconv-lite@0.4.24: + dependencies: + safer-buffer: 2.1.2 + iconv-lite@0.6.3: dependencies: safer-buffer: 2.1.2 @@ -5946,8 +7746,7 @@ snapshots: dependencies: safer-buffer: 2.1.2 - ieee754@1.2.1: - optional: true + ieee754@1.2.1: {} ignore@5.3.2: {} @@ -5966,11 +7765,14 @@ snapshots: index-to-position@1.2.0: {} - inherits@2.0.4: - optional: true + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 - ini@1.3.8: - optional: true + inherits@2.0.4: {} + + ini@1.3.8: {} ini@4.1.3: {} @@ -5980,6 +7782,10 @@ snapshots: hasown: 2.0.3 side-channel: 1.1.0 + ip-address@10.2.0: {} + + ipaddr.js@1.9.1: {} + is-array-buffer@3.0.5: dependencies: call-bind: 1.0.9 @@ -5998,6 +7804,10 @@ snapshots: dependencies: has-bigints: 1.1.0 + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + is-boolean-object@1.2.2: dependencies: call-bound: 1.0.4 @@ -6022,6 +7832,8 @@ snapshots: is-docker@3.0.0: {} + is-extendable@0.1.1: {} + is-extglob@2.1.1: {} is-finalizationregistry@1.1.1: @@ -6046,6 +7858,8 @@ snapshots: dependencies: is-docker: 3.0.0 + is-interactive@1.0.0: {} + is-map@2.0.3: {} is-negative-zero@2.0.3: {} @@ -6057,6 +7871,8 @@ snapshots: is-number@7.0.0: {} + is-path-inside@4.0.0: {} + is-potential-custom-element-name@1.0.1: {} is-regex@1.2.1: @@ -6091,6 +7907,12 @@ snapshots: dependencies: which-typed-array: 1.1.20 + is-unicode-supported@0.1.0: {} + + is-valid-domain@0.1.6: + dependencies: + punycode: 2.3.1 + is-weakmap@2.0.2: {} is-weakref@1.1.1: @@ -6112,6 +7934,8 @@ snapshots: isexe@2.0.0: {} + isexe@4.0.0: {} + istextorbinary@9.5.0: dependencies: binaryextensions: 6.11.0 @@ -6122,6 +7946,22 @@ snapshots: dependencies: '@isaacs/cliui': 9.0.0 + jest-diff@27.5.1: + dependencies: + chalk: 4.1.2 + diff-sequences: 27.5.1 + jest-get-type: 27.5.1 + pretty-format: 27.5.1 + + jest-get-type@27.5.1: {} + + jest-matcher-utils@27.5.1: + dependencies: + chalk: 4.1.2 + jest-diff: 27.5.1 + jest-get-type: 27.5.1 + pretty-format: 27.5.1 + js-tokens@4.0.0: {} js-tokens@9.0.1: {} @@ -6222,8 +8062,28 @@ snapshots: dependencies: json-buffer: 3.0.1 + kind-of@6.0.3: {} + + kleur@3.0.3: {} + + kleur@4.1.5: {} + kubernetes-types@1.30.0: {} + ky@1.14.3: {} + + lamdera@0.19.1-1.4.0: + optionalDependencies: + '@lamdera/compiler-darwin-arm64': 0.19.1-1.4.0 + '@lamdera/compiler-darwin-x64': 0.19.1-1.4.0 + '@lamdera/compiler-linux-arm64': 0.19.1-1.4.0 + '@lamdera/compiler-linux-x64': 0.19.1-1.4.0 + '@lamdera/compiler-win32-x64': 0.19.1-1.4.0 + + latest-version@9.0.0: + dependencies: + package-json: 10.0.1 + lefthook-darwin-arm64@2.1.6: optional: true @@ -6308,8 +8168,15 @@ snapshots: lodash@4.18.1: {} + log-symbols@4.1.0: + dependencies: + chalk: 4.1.2 + is-unicode-supported: 0.1.0 + loupe@3.2.1: {} + lowercase-keys@2.0.0: {} + lru-cache@10.4.3: {} lru-cache@11.3.6: {} @@ -6322,6 +8189,23 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 + make-fetch-happen@15.0.5: + dependencies: + '@gar/promise-retry': 1.0.3 + '@npmcli/agent': 4.0.0 + '@npmcli/redact': 4.0.0 + cacache: 20.0.4 + http-cache-semantics: 4.2.0 + minipass: 7.1.3 + minipass-fetch: 5.0.2 + minipass-flush: 1.0.7 + minipass-pipeline: 1.2.4 + negotiator: 1.0.0 + proc-log: 6.1.0 + ssri: 13.0.1 + transitivePeerDependencies: + - supports-color + markdown-it@14.1.1: dependencies: argparse: 2.0.1 @@ -6335,8 +8219,31 @@ snapshots: mdurl@2.0.0: {} + media-typer@0.3.0: {} + + memfs@4.57.2(tslib@2.8.1): + dependencies: + '@jsonjoy.com/fs-core': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-fsa': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-node': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-node-builtins': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-node-to-fsa': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-node-utils': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-print': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-snapshot': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/json-pack': 1.21.0(tslib@2.8.1) + '@jsonjoy.com/util': 1.9.0(tslib@2.8.1) + glob-to-regex.js: 1.2.0(tslib@2.8.1) + thingies: 2.6.0(tslib@2.8.1) + tree-dump: 1.1.0(tslib@2.8.1) + tslib: 2.8.1 + + merge-descriptors@1.0.3: {} + merge2@1.4.1: {} + methods@1.1.2: {} + micromatch@4.0.8: dependencies: braces: 3.0.3 @@ -6344,16 +8251,27 @@ snapshots: mime-db@1.52.0: {} + mime-db@1.54.0: {} + mime-types@2.1.35: dependencies: mime-db: 1.52.0 + mime-types@3.0.2: + dependencies: + mime-db: 1.54.0 + mime@1.6.0: {} mime@3.0.0: {} - mimic-response@3.1.0: - optional: true + mimic-fn@2.1.0: {} + + mimic-function@5.0.1: {} + + mimic-response@1.0.1: {} + + mimic-response@3.1.0: {} minimatch@10.2.5: dependencies: @@ -6365,13 +8283,51 @@ snapshots: minimist@1.2.8: {} + minipass-collect@2.0.1: + dependencies: + minipass: 7.1.3 + + minipass-fetch@5.0.2: + dependencies: + minipass: 7.1.3 + minipass-sized: 2.0.0 + minizlib: 3.1.0 + optionalDependencies: + iconv-lite: 0.7.2 + + minipass-flush@1.0.7: + dependencies: + minipass: 3.3.6 + + minipass-pipeline@1.2.4: + dependencies: + minipass: 3.3.6 + + minipass-sized@2.0.0: + dependencies: + minipass: 7.1.3 + + minipass@3.3.6: + dependencies: + yallist: 4.0.0 + minipass@7.1.3: {} + minizlib@3.1.0: + dependencies: + minipass: 7.1.3 + mkdirp-classic@0.5.3: optional: true + mkdirp@0.5.6: + dependencies: + minimist: 1.2.8 + mri@1.2.0: {} + ms@2.0.0: {} + ms@2.1.3: {} msgpackr-extract@3.0.3: @@ -6401,6 +8357,12 @@ snapshots: natural-compare@1.4.0: {} + negotiator@0.6.3: {} + + negotiator@1.0.0: {} + + nice-try@1.0.5: {} + node-abi@3.92.0: dependencies: semver: 7.8.0 @@ -6411,6 +8373,13 @@ snapshots: node-addon-api@7.1.1: {} + node-elm-compiler@5.0.6: + dependencies: + cross-spawn: 6.0.5 + find-elm-dependencies: 2.0.4 + lodash: 4.18.1 + temp: 0.9.4 + node-exports-info@1.6.0: dependencies: array.prototype.flatmap: 1.3.3 @@ -6438,6 +8407,10 @@ snapshots: semver: 7.8.0 validate-npm-package-license: 3.0.4 + normalize-path@3.0.0: {} + + normalize-url@6.1.0: {} + nth-check@2.1.1: dependencies: boolbase: 1.0.0 @@ -6484,10 +8457,25 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.1.1 + on-finished@2.3.0: + dependencies: + ee-first: 1.1.1 + + on-finished@2.4.1: + dependencies: + ee-first: 1.1.1 + once@1.4.0: dependencies: wrappy: 1.0.2 - optional: true + + onetime@5.1.2: + dependencies: + mimic-fn: 2.1.0 + + onetime@7.0.0: + dependencies: + mimic-function: 5.0.1 open@10.2.0: dependencies: @@ -6505,6 +8493,20 @@ snapshots: type-check: 0.4.0 word-wrap: 1.2.5 + ora@5.4.1: + dependencies: + bl: 4.1.0 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-spinners: 2.9.2 + is-interactive: 1.0.0 + is-unicode-supported: 0.1.0 + log-symbols: 4.1.0 + strip-ansi: 6.0.1 + wcwidth: 1.0.1 + + os-tmpdir@1.0.2: {} + outdent@0.5.0: {} own-keys@1.0.1: @@ -6513,6 +8515,8 @@ snapshots: object-keys: 1.1.1 safe-push-apply: 1.0.0 + p-cancelable@2.1.1: {} + p-filter@2.1.0: dependencies: p-map: 2.1.0 @@ -6541,6 +8545,13 @@ snapshots: package-json-from-dist@1.0.1: {} + package-json@10.0.1: + dependencies: + ky: 1.14.3 + registry-auth-token: 5.1.1 + registry-url: 6.0.1 + semver: 7.8.0 + package-manager-detector@0.2.11: dependencies: quansync: 0.2.11 @@ -6572,8 +8583,19 @@ snapshots: dependencies: entities: 6.0.1 + parseurl@1.3.3: {} + + password-prompt@1.1.3: + dependencies: + ansi-escapes: 4.3.2 + cross-spawn: 7.0.6 + path-exists@4.0.0: {} + path-is-absolute@1.0.1: {} + + path-key@2.0.1: {} + path-key@3.1.1: {} path-parse@1.0.7: {} @@ -6583,6 +8605,8 @@ snapshots: lru-cache: 11.3.6 minipass: 7.1.3 + path-to-regexp@0.1.13: {} + path-type@4.0.0: {} path-type@6.0.0: {} @@ -6647,11 +8671,30 @@ snapshots: prettier@3.8.3: {} + pretty-format@27.5.1: + dependencies: + ansi-regex: 5.0.1 + ansi-styles: 5.2.0 + react-is: 17.0.2 + + proc-log@6.1.0: {} + + prompts@2.4.2: + dependencies: + kleur: 3.0.3 + sisteransi: 1.0.5 + + proto-list@1.2.4: {} + + proxy-addr@2.0.7: + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + pump@3.0.4: dependencies: end-of-stream: 1.4.5 once: 1.4.0 - optional: true punycode.js@2.3.1: {} @@ -6667,6 +8710,17 @@ snapshots: queue-microtask@1.2.3: {} + quick-lru@5.1.1: {} + + range-parser@1.2.1: {} + + raw-body@2.5.3: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.1 + iconv-lite: 0.4.24 + unpipe: 1.0.0 + rc-config-loader@4.1.4: dependencies: debug: 4.4.3 @@ -6682,7 +8736,8 @@ snapshots: ini: 1.3.8 minimist: 1.2.8 strip-json-comments: 2.0.1 - optional: true + + react-is@17.0.2: {} read-pkg@9.0.1: dependencies: @@ -6708,7 +8763,12 @@ snapshots: inherits: 2.0.4 string_decoder: 1.3.0 util-deprecate: 1.0.2 - optional: true + + readdirp@3.6.0: + dependencies: + picomatch: 2.3.2 + + readdirp@5.0.0: {} redux-thunk@3.1.0(redux@5.0.1): dependencies: @@ -6736,10 +8796,20 @@ snapshots: gopd: 1.2.0 set-function-name: 2.0.2 + registry-auth-token@5.1.1: + dependencies: + '@pnpm/npm-conf': 3.0.2 + + registry-url@6.0.1: + dependencies: + rc: 1.2.8 + require-from-string@2.0.2: {} reselect@5.1.1: {} + resolve-alpn@1.2.1: {} + resolve-from@4.0.0: {} resolve-from@5.0.0: {} @@ -6753,8 +8823,30 @@ snapshots: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 + responselike@2.0.1: + dependencies: + lowercase-keys: 2.0.0 + + restore-cursor@3.1.0: + dependencies: + onetime: 5.1.2 + signal-exit: 3.0.7 + + restore-cursor@5.1.0: + dependencies: + onetime: 7.0.0 + signal-exit: 4.1.0 + reusify@1.1.0: {} + rimraf@2.6.3: + dependencies: + glob: 7.2.3 + + rimraf@2.7.1: + dependencies: + glob: 7.2.3 + rollup@4.60.3: dependencies: '@types/estree': 1.0.8 @@ -6835,12 +8927,81 @@ snapshots: transitivePeerDependencies: - supports-color + section-matter@1.0.0: + dependencies: + extend-shallow: 2.0.1 + kind-of: 6.0.3 + semver@5.7.2: {} semver@6.3.1: {} semver@7.8.0: {} + send@0.19.2: + 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 + transitivePeerDependencies: + - supports-color + + send@1.2.1: + dependencies: + debug: 4.4.3 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 2.0.0 + http-errors: 2.0.1 + mime-types: 3.0.2 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + + serve-index@1.9.2: + dependencies: + accepts: 1.3.8 + batch: 0.6.1 + debug: 2.6.9 + escape-html: 1.0.3 + http-errors: 1.8.1 + mime-types: 2.1.35 + parseurl: 1.3.3 + transitivePeerDependencies: + - supports-color + + serve-static@1.16.3: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 0.19.2 + transitivePeerDependencies: + - supports-color + + serve-static@2.2.1: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 1.2.1 + transitivePeerDependencies: + - supports-color + set-function-length@1.2.2: dependencies: define-data-property: 1.1.4 @@ -6863,10 +9024,18 @@ snapshots: es-errors: 1.3.0 es-object-atoms: 1.1.1 + setprototypeof@1.2.0: {} + + shebang-command@1.2.0: + dependencies: + shebang-regex: 1.0.0 + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 + shebang-regex@1.0.0: {} + shebang-regex@3.0.0: {} side-channel-list@1.0.1: @@ -6899,6 +9068,8 @@ snapshots: siginfo@2.0.0: {} + signal-exit@3.0.7: {} + signal-exit@4.1.0: {} simple-concat@1.0.1: @@ -6911,6 +9082,8 @@ snapshots: simple-concat: 1.0.1 optional: true + sisteransi@1.0.5: {} + slash@3.0.0: {} slash@5.1.0: {} @@ -6921,6 +9094,21 @@ snapshots: astral-regex: 2.0.0 is-fullwidth-code-point: 3.0.0 + smart-buffer@4.2.0: {} + + socks-proxy-agent@8.0.5: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + socks: 2.8.9 + transitivePeerDependencies: + - supports-color + + socks@2.8.9: + dependencies: + ip-address: 10.2.0 + smart-buffer: 4.2.0 + source-map-js@1.2.1: {} source-map-support@0.5.21: @@ -6951,8 +9139,16 @@ snapshots: sprintf-js@1.0.3: {} + ssri@13.0.1: + dependencies: + minipass: 7.1.3 + stackback@0.0.2: {} + statuses@1.5.0: {} + + statuses@2.0.2: {} + std-env@3.10.0: {} stop-iteration-iterator@1.1.0: @@ -6960,6 +9156,8 @@ snapshots: es-errors: 1.3.0 internal-slot: 1.1.0 + streamsearch@1.1.0: {} + string-width@4.2.3: dependencies: emoji-regex: 8.0.0 @@ -6992,7 +9190,6 @@ snapshots: string_decoder@1.3.0: dependencies: safe-buffer: 5.2.1 - optional: true strip-ansi@6.0.1: dependencies: @@ -7002,10 +9199,11 @@ snapshots: dependencies: ansi-regex: 6.2.2 + strip-bom-string@1.0.0: {} + strip-bom@3.0.0: {} - strip-json-comments@2.0.1: - optional: true + strip-json-comments@2.0.1: {} strip-json-comments@3.1.1: {} @@ -7017,10 +9215,17 @@ snapshots: dependencies: boundary: 2.0.0 + sudo-prompt@8.2.5: {} + supports-color@7.2.0: dependencies: has-flag: 4.0.0 + supports-hyperlinks@2.3.0: + dependencies: + has-flag: 4.0.0 + supports-color: 7.2.0 + supports-hyperlinks@3.2.0: dependencies: has-flag: 4.0.0 @@ -7094,8 +9299,18 @@ snapshots: readable-stream: 3.6.2 optional: true + temp@0.9.4: + dependencies: + mkdirp: 0.5.6 + rimraf: 2.6.3 + term-size@2.2.1: {} + terminal-link@2.1.1: + dependencies: + ansi-escapes: 4.3.2 + supports-hyperlinks: 2.3.0 + terminal-link@4.0.0: dependencies: ansi-escapes: 7.3.0 @@ -7114,6 +9329,10 @@ snapshots: dependencies: editions: 6.22.0 + thingies@2.6.0(tslib@2.8.1): + dependencies: + tslib: 2.8.1 + tinybench@2.9.0: {} tinyexec@0.3.2: {} @@ -7135,12 +9354,20 @@ snapshots: dependencies: tldts-core: 6.1.86 + tmp@0.0.33: + dependencies: + os-tmpdir: 1.0.2 + + tmp@0.2.3: {} + tmp@0.2.5: {} to-regex-range@5.0.1: dependencies: is-number: 7.0.0 + toidentifier@1.0.1: {} + toml@3.0.0: {} tough-cookie@5.1.2: @@ -7153,10 +9380,16 @@ snapshots: dependencies: punycode: 2.3.1 + tree-dump@1.1.0(tslib@2.8.1): + dependencies: + tslib: 2.8.1 + ts-api-utils@2.5.0(typescript@5.8.3): dependencies: typescript: 5.8.3 + ts-union@2.3.0: {} + tsconfig-paths@3.15.0: dependencies: '@types/json5': 0.0.29 @@ -7164,6 +9397,8 @@ snapshots: minimist: 1.2.8 strip-bom: 3.0.0 + tslib@1.14.1: {} + tslib@2.8.1: {} tunnel-agent@0.6.0: @@ -7177,8 +9412,15 @@ snapshots: dependencies: prelude-ls: 1.2.1 + type-fest@0.21.3: {} + type-fest@4.41.0: {} + type-is@1.6.18: + dependencies: + media-typer: 0.3.0 + mime-types: 2.1.35 + typed-array-buffer@1.0.3: dependencies: call-bound: 1.0.4 @@ -7218,6 +9460,8 @@ snapshots: tunnel: 0.0.6 underscore: 1.13.8 + typescript@4.9.5: {} + typescript@5.8.3: {} uc.micro@2.1.0: {} @@ -7239,18 +9483,23 @@ snapshots: unicorn-magic@0.3.0: {} + unicorn-magic@0.4.0: {} + universalify@0.1.2: {} universalify@2.0.1: {} + unpipe@1.0.0: {} + uri-js@4.4.1: dependencies: punycode: 2.3.1 url-join@4.0.1: {} - util-deprecate@1.0.2: - optional: true + util-deprecate@1.0.2: {} + + utils-merge@1.0.1: {} uuid@11.1.1: {} @@ -7259,6 +9508,8 @@ snapshots: spdx-correct: 3.2.0 spdx-expression-parse: 3.0.1 + vary@1.1.2: {} + version-range@4.15.0: {} vite-node@3.2.4(@types/node@22.19.18)(terser@5.47.1)(yaml@2.9.0): @@ -7342,6 +9593,10 @@ snapshots: dependencies: xml-name-validator: 5.0.0 + wcwidth@1.0.1: + dependencies: + defaults: 1.0.4 + webidl-conversions@3.0.1: {} webidl-conversions@7.0.0: {} @@ -7403,10 +9658,18 @@ snapshots: gopd: 1.2.0 has-tostringtag: 1.0.2 + which@1.3.1: + dependencies: + isexe: 2.0.0 + which@2.0.2: dependencies: isexe: 2.0.0 + which@6.0.1: + dependencies: + isexe: 4.0.0 + why-is-node-running@2.3.0: dependencies: siginfo: 2.0.0 @@ -7414,8 +9677,15 @@ snapshots: word-wrap@1.2.5: {} - wrappy@1.0.2: - optional: true + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrappy@1.0.2: {} + + ws@7.5.10: {} ws@8.20.0: {} From e39bf3474c3cba00a23c705ec168f92fda75fa8c Mon Sep 17 00:00:00 2001 From: Ryan Bas Date: Mon, 11 May 2026 23:25:38 -0600 Subject: [PATCH 03/23] feat(docs-site): add site layout with header, sidebar, and content area Modify the elm-pages scaffold to include a sticky header with nav links, a collapsible sidebar with navigation sections, and a styled main content area. Replace default CSS with full docs site stylesheet supporting light/dark mode, responsive breakpoints, and typography. Co-Authored-By: Claude Opus 4.6 (1M context) --- packages/docs-site/app/Shared.elm | 119 +++++++++++---- packages/docs-site/app/View.elm | 66 ++------ packages/docs-site/style.css | 242 +++++++++++++++++++++++++++++- 3 files changed, 340 insertions(+), 87 deletions(-) diff --git a/packages/docs-site/app/Shared.elm b/packages/docs-site/app/Shared.elm index b567255..2cd8c56 100644 --- a/packages/docs-site/app/Shared.elm +++ b/packages/docs-site/app/Shared.elm @@ -4,12 +4,13 @@ import BackendTask exposing (BackendTask) import Effect exposing (Effect) import FatalError exposing (FatalError) import Html exposing (Html) +import Html.Attributes as Attr import Html.Events import Pages.Flags import Pages.PageUrl exposing (PageUrl) -import UrlPath exposing (UrlPath) import Route exposing (Route) import SharedTemplate exposing (SharedTemplate) +import UrlPath exposing (UrlPath) import View exposing (View) @@ -26,7 +27,7 @@ template = type Msg = SharedMsg SharedMsg - | MenuClicked + | ToggleSidebar type alias Data = @@ -38,7 +39,7 @@ type SharedMsg type alias Model = - { showMenu : Bool + { sidebarOpen : Bool } @@ -56,7 +57,7 @@ init : } -> ( Model, Effect Msg ) init flags maybePagePath = - ( { showMenu = False } + ( { sidebarOpen = True } , Effect.none ) @@ -64,11 +65,11 @@ init flags maybePagePath = update : Msg -> Model -> ( Model, Effect Msg ) update msg model = case msg of - SharedMsg globalMsg -> + SharedMsg _ -> ( model, Effect.none ) - MenuClicked -> - ( { model | showMenu = not model.showMenu }, Effect.none ) + ToggleSidebar -> + ( { model | sidebarOpen = not model.sidebarOpen }, Effect.none ) subscriptions : UrlPath -> Model -> Sub Msg @@ -93,28 +94,90 @@ view : -> { body : List (Html msg), title : String } view sharedData page model toMsg pageView = { body = - [ Html.nav [] - [ Html.button - [ Html.Events.onClick MenuClicked ] - [ Html.text - (if model.showMenu then - "Close Menu" - - else - "Open Menu" - ) - ] - , if model.showMenu then - Html.ul [] - [ Html.li [] [ Html.text "Menu item 1" ] - , Html.li [] [ Html.text "Menu item 2" ] - ] - - else - Html.text "" + [ viewHeader model toMsg + , Html.div [ Attr.class "layout" ] + [ viewSidebar model toMsg + , Html.main_ [ Attr.class "content" ] + pageView.body ] - |> Html.map toMsg - , Html.main_ [] pageView.body ] , title = pageView.title } + + +viewHeader : Model -> (Msg -> msg) -> Html msg +viewHeader model toMsg = + Html.header [ Attr.class "header" ] + [ Html.button + [ Attr.class "sidebar-toggle" + , Html.Events.onClick (toMsg ToggleSidebar) + ] + [ Html.text + (if model.sidebarOpen then + "\u{2630}" + + else + "\u{2630}" + ) + ] + , Html.a + [ Attr.class "logo" + , Attr.href "/" + ] + [ Html.text "wolfcola devtools" ] + , Html.nav [ Attr.class "header-nav" ] + [ Html.a [ Attr.href "/packages" ] [ Html.text "Packages" ] + , Html.a [ Attr.href "/guides" ] [ Html.text "Guides" ] + , Html.a [ Attr.href "/api" ] [ Html.text "API" ] + , Html.a [ Attr.href "/architecture" ] [ Html.text "Architecture" ] + , Html.a [ Attr.href "/contributing" ] [ Html.text "Contributing" ] + ] + ] + + +viewSidebar : Model -> (Msg -> msg) -> Html msg +viewSidebar model toMsg = + Html.aside + [ Attr.class + (if model.sidebarOpen then + "sidebar" + + else + "sidebar sidebar--closed" + ) + ] + [ viewSidebarSection "Packages" + [ ( "/packages/treeshake-check", "treeshake-check" ) + , ( "/packages/eslint-plugin-treeshake", "eslint-plugin-treeshake" ) + , ( "/packages/devtools-bridge", "devtools-bridge" ) + , ( "/packages/devtools-types", "devtools-types" ) + ] + , viewSidebarSection "Guides" + [ ( "/docs/getting-started", "Getting Started" ) + , ( "/docs/devtools-extension", "DevTools Extension" ) + , ( "/docs/vscode-extension", "VS Code Extension" ) + , ( "/docs/tree-shaking", "Tree-Shaking" ) + , ( "/docs/davinci-integration", "DaVinci Integration" ) + ] + , viewSidebarSection "Contributing" + [ ( "/contributing/development-setup", "Development Setup" ) + , ( "/contributing/repository-structure", "Repository Structure" ) + , ( "/contributing/code-style", "Code Style" ) + , ( "/contributing/release-process", "Release Process" ) + ] + ] + + +viewSidebarSection : String -> List ( String, String ) -> Html msg +viewSidebarSection heading links = + Html.div [ Attr.class "sidebar-section" ] + [ Html.h3 [ Attr.class "sidebar-heading" ] [ Html.text heading ] + , Html.ul [ Attr.class "sidebar-links" ] + (List.map + (\( href, label ) -> + Html.li [] + [ Html.a [ Attr.href href ] [ Html.text label ] ] + ) + links + ) + ] diff --git a/packages/docs-site/app/View.elm b/packages/docs-site/app/View.elm index cdca20e..1f2f078 100644 --- a/packages/docs-site/app/View.elm +++ b/packages/docs-site/app/View.elm @@ -1,69 +1,23 @@ -module View exposing (View, map, freeze, freezableToHtml, htmlToFreezable) - -{-| View module for elm-pages with frozen view support. - -@docs View, map, freeze, freezableToHtml, htmlToFreezable - --} +module View exposing (View, map, placeholder) import Html exposing (Html) -{-| -} type alias View msg = { title : String , body : List (Html msg) } -{-| -} -map : (msg1 -> msg2) -> View msg1 -> View msg2 -map fn doc = - { title = doc.title - , body = List.map (Html.map fn) doc.body +map : (a -> b) -> View a -> View b +map fn view = + { title = view.title + , body = List.map (Html.map fn) view.body } -{-| The type of content that can be frozen. Must produce no messages (Never). -For plain Html, this is just Html Never. --} -type alias Freezable = - Html Never - - -{-| Convert Freezable content to plain Html for server-side rendering. -For plain Html, this is identity. --} -freezableToHtml : Freezable -> Html Never -freezableToHtml = - identity - - -{-| Convert plain Html back to Freezable for client-side adoption. -For plain Html, this is identity. --} -htmlToFreezable : Html Never -> Freezable -htmlToFreezable = - identity - - -{-| Freeze a view so its content is rendered at build time and adopted on the client. -Use this for static content that doesn't need interactivity. - -Frozen content is: - - - Rendered at build time and included in the HTML - - Adopted by the client without re-rendering - - Eligible for dead-code elimination (rendering code removed from client bundle) - -At build time, the server codemod wraps the content with a `data-frozen` attribute for extraction. -The elm-review codemod then transforms `freeze` calls to lazy thunks on the client, -which adopt the pre-rendered DOM without re-rendering. - --} -freeze : Freezable -> Html msg -freeze content = - content - |> freezableToHtml - |> htmlToFreezable - |> Html.map never +placeholder : String -> View msg +placeholder moduleName = + { title = moduleName + , body = [ Html.text moduleName ] + } diff --git a/packages/docs-site/style.css b/packages/docs-site/style.css index 96bb337..a333ecc 100644 --- a/packages/docs-site/style.css +++ b/packages/docs-site/style.css @@ -1,5 +1,241 @@ +:root { + --bg: #ffffff; + --bg-sidebar: #f8f9fa; + --text: #1a1a2e; + --text-muted: #6b7280; + --border: #e5e7eb; + --accent: #3b82f6; + --accent-hover: #2563eb; + --header-height: 56px; + --sidebar-width: 260px; + --content-max: 800px; + --font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + --font-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, monospace; +} + +@media (prefers-color-scheme: dark) { + :root { + --bg: #0f172a; + --bg-sidebar: #1e293b; + --text: #e2e8f0; + --text-muted: #94a3b8; + --border: #334155; + --accent: #60a5fa; + --accent-hover: #93bbfd; + } +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + body { - font-family: - -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif, - 'Apple Color Emoji', 'Segoe UI Emoji'; + font-family: var(--font-sans); + color: var(--text); + background: var(--bg); + line-height: 1.6; +} + +a { + color: var(--accent); + text-decoration: none; +} +a:hover { + color: var(--accent-hover); +} + +/* Header */ +.header { + position: sticky; + top: 0; + z-index: 100; + display: flex; + align-items: center; + gap: 1rem; + height: var(--header-height); + padding: 0 1.5rem; + background: var(--bg); + border-bottom: 1px solid var(--border); +} + +.logo { + font-weight: 700; + font-size: 1.1rem; + color: var(--text); +} + +.header-nav { + display: flex; + gap: 1.5rem; + margin-left: auto; +} +.header-nav a { + color: var(--text-muted); + font-size: 0.9rem; +} +.header-nav a:hover { + color: var(--text); +} + +.sidebar-toggle { + background: none; + border: 1px solid var(--border); + border-radius: 4px; + padding: 0.25rem 0.5rem; + cursor: pointer; + color: var(--text); + font-size: 1rem; +} + +/* Layout */ +.layout { + display: flex; + min-height: calc(100vh - var(--header-height)); +} + +/* Sidebar */ +.sidebar { + width: var(--sidebar-width); + padding: 1.5rem 1rem; + background: var(--bg-sidebar); + border-right: 1px solid var(--border); + overflow-y: auto; + position: sticky; + top: var(--header-height); + height: calc(100vh - var(--header-height)); + transition: margin-left 0.2s; +} + +.sidebar--closed { + margin-left: calc(-1 * var(--sidebar-width)); +} + +.sidebar-heading { + font-size: 0.75rem; + text-transform: uppercase; + letter-spacing: 0.05em; + color: var(--text-muted); + margin-bottom: 0.5rem; + margin-top: 1.5rem; +} + +.sidebar-section:first-child .sidebar-heading { + margin-top: 0; +} + +.sidebar-links { + list-style: none; + padding: 0; +} +.sidebar-links li { + margin-bottom: 0.25rem; +} + +.sidebar-links a { + display: block; + padding: 0.3rem 0.75rem; + border-radius: 4px; + color: var(--text); + font-size: 0.875rem; +} + +.sidebar-links a:hover { + background: var(--border); +} + +/* Content */ +.content { + flex: 1; + max-width: var(--content-max); + margin: 0 auto; + padding: 2rem 2rem 4rem; +} + +.content h1 { + font-size: 2rem; + margin-bottom: 0.5rem; +} +.content h2 { + font-size: 1.5rem; + margin-top: 2rem; + margin-bottom: 0.75rem; +} +.content h3 { + font-size: 1.25rem; + margin-top: 1.5rem; + margin-bottom: 0.5rem; +} +.content p { + margin-bottom: 1rem; +} +.content ul, +.content ol { + margin-bottom: 1rem; + padding-left: 1.5rem; +} +.content li { + margin-bottom: 0.25rem; +} + +.content pre { + background: var(--bg-sidebar); + border: 1px solid var(--border); + border-radius: 6px; + padding: 1rem; + overflow-x: auto; + margin-bottom: 1rem; + font-family: var(--font-mono); + font-size: 0.875rem; + line-height: 1.5; +} + +.content code { + font-family: var(--font-mono); + font-size: 0.875em; + background: var(--bg-sidebar); + padding: 0.15rem 0.35rem; + border-radius: 3px; +} + +.content pre code { + background: none; + padding: 0; +} + +/* Callouts */ +.callout { + border-left: 4px solid var(--accent); + padding: 1rem 1.25rem; + margin-bottom: 1rem; + border-radius: 0 6px 6px 0; + background: var(--bg-sidebar); +} + +.callout--warning { + border-left-color: #f59e0b; +} +.callout--info { + border-left-color: var(--accent); +} + +/* Responsive */ +@media (max-width: 768px) { + .header-nav { + display: none; + } + .sidebar { + position: fixed; + z-index: 50; + top: var(--header-height); + left: 0; + height: calc(100vh - var(--header-height)); + } + .sidebar--closed { + margin-left: calc(-1 * var(--sidebar-width)); + } + .content { + padding: 1.5rem 1rem 3rem; + } } From b1d003bb72c3b8e3a35b0021dc56926bd94a45e3 Mon Sep 17 00:00:00 2001 From: Ryan Bas Date: Mon, 11 May 2026 23:29:29 -0600 Subject: [PATCH 04/23] feat(docs-site): add landing page with package cards Co-Authored-By: Claude Opus 4.6 (1M context) --- packages/docs-site/app/Route/Index.elm | 100 +++++++++++++++++++++---- packages/docs-site/style.css | 68 +++++++++++++++++ 2 files changed, 152 insertions(+), 16 deletions(-) diff --git a/packages/docs-site/app/Route/Index.elm b/packages/docs-site/app/Route/Index.elm index e930c85..1647436 100644 --- a/packages/docs-site/app/Route/Index.elm +++ b/packages/docs-site/app/Route/Index.elm @@ -5,6 +5,7 @@ import FatalError exposing (FatalError) import Head import Head.Seo as Seo import Html +import Html.Attributes as Attr import Pages.Url import PagesMsg exposing (PagesMsg) import UrlPath @@ -27,8 +28,7 @@ type alias RouteParams = type alias Data = - { message : String - } + {} type alias ActionData = @@ -46,9 +46,7 @@ route = data : BackendTask FatalError Data data = - BackendTask.succeed Data - |> BackendTask.andMap - (BackendTask.succeed "Hello!") + BackendTask.succeed {} head : @@ -57,16 +55,16 @@ head : head app = Seo.summary { canonicalUrlOverride = Nothing - , siteName = "elm-pages" + , siteName = "wolfcola devtools" , image = { url = [ "images", "icon-png.png" ] |> UrlPath.join |> Pages.Url.fromPath - , alt = "elm-pages logo" + , alt = "wolfcola devtools logo" , dimensions = Nothing , mimeType = Nothing } - , description = "Welcome to elm-pages!" + , description = "Developer tools for OIDC debugging and tree-shake verification" , locale = Nothing - , title = "elm-pages is running" + , title = "wolfcola devtools" } |> Seo.website @@ -76,13 +74,83 @@ view : -> Shared.Model -> View (PagesMsg Msg) view app shared = - { title = "elm-pages is running" + { title = "wolfcola devtools" , body = - [ Html.h1 [] [ Html.text "elm-pages is up and running!" ] - , Html.p [] - [ Html.text <| "The message is: " ++ app.data.message - ] - , Route.Blog__Slug_ { slug = "hello" } - |> Route.link [] [ Html.text "My blog post" ] + [ viewHero + , viewPackageGrid + , viewQuickLinks ] } + + +viewHero : Html.Html (PagesMsg Msg) +viewHero = + Html.section [ Attr.class "hero" ] + [ Html.h1 [] [ Html.text "wolfcola devtools" ] + , Html.p [ Attr.class "hero-subtitle" ] + [ Html.text "Developer tools for OIDC debugging and tree-shake verification" ] + ] + + +viewPackageGrid : Html.Html (PagesMsg Msg) +viewPackageGrid = + Html.div [ Attr.class "package-grid" ] + [ viewPackageCard + { name = "@wolfcola/treeshake-check" + , description = "CLI & library to verify packages are tree-shakeable by Rollup" + , href = "/packages/treeshake-check" + , tag = "Published" + } + , viewPackageCard + { name = "@wolfcola/eslint-plugin-treeshake" + , description = "ESLint plugin that flags tree-breaking patterns" + , href = "/packages/eslint-plugin-treeshake" + , tag = "Published" + } + , viewPackageCard + { name = "@wolfcola/devtools-bridge" + , description = "SDK adapter for emitting events from DaVinci, Journey, OIDC clients" + , href = "/packages/devtools-bridge" + , tag = "Published" + } + , viewPackageCard + { name = "@wolfcola/devtools-types" + , description = "Effect Schema definitions for AuthEvent and FlowState" + , href = "/packages/devtools-types" + , tag = "Published" + } + ] + + +viewPackageCard : + { name : String, description : String, href : String, tag : String } + -> Html.Html (PagesMsg Msg) +viewPackageCard pkg = + Html.a [ Attr.class "package-card", Attr.href pkg.href ] + [ Html.div [ Attr.class "package-card-header" ] + [ Html.h3 [] [ Html.text pkg.name ] + , Html.span [ Attr.class "package-tag" ] [ Html.text pkg.tag ] + ] + , Html.p [] [ Html.text pkg.description ] + ] + + +viewQuickLinks : Html.Html (PagesMsg Msg) +viewQuickLinks = + Html.section [ Attr.class "quick-links" ] + [ Html.h2 [] [ Html.text "Quick Links" ] + , Html.ul [] + [ Html.li [] + [ Html.a [ Attr.href "/docs/getting-started" ] + [ Html.text "Installation & Setup" ] + ] + , Html.li [] + [ Html.a [ Attr.href "/architecture" ] + [ Html.text "Architecture Overview" ] + ] + , Html.li [] + [ Html.a [ Attr.href "/contributing/development-setup" ] + [ Html.text "Contributing" ] + ] + ] + ] diff --git a/packages/docs-site/style.css b/packages/docs-site/style.css index a333ecc..e23a717 100644 --- a/packages/docs-site/style.css +++ b/packages/docs-site/style.css @@ -239,3 +239,71 @@ a:hover { padding: 1.5rem 1rem 3rem; } } + +/* Landing Page */ +.hero { + text-align: center; + padding: 3rem 0 2rem; +} +.hero h1 { + font-size: 2.5rem; +} +.hero-subtitle { + font-size: 1.2rem; + color: var(--text-muted); + margin-top: 0.5rem; +} + +.package-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); + gap: 1rem; + margin: 2rem 0; +} + +.package-card { + display: block; + padding: 1.25rem; + border: 1px solid var(--border); + border-radius: 8px; + color: var(--text); + transition: border-color 0.15s; +} + +.package-card:hover { + border-color: var(--accent); +} + +.package-card-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 0.5rem; +} + +.package-card h3 { + font-size: 1rem; +} + +.package-tag { + font-size: 0.7rem; + text-transform: uppercase; + letter-spacing: 0.05em; + padding: 0.15rem 0.5rem; + border-radius: 999px; + background: var(--accent); + color: white; +} + +.package-card p { + font-size: 0.875rem; + color: var(--text-muted); + margin: 0; +} + +.quick-links { + margin-top: 3rem; +} +.quick-links h2 { + margin-bottom: 1rem; +} From a2028555fe5c8de41f9028ba266dfb59dee3dea0 Mon Sep 17 00:00:00 2001 From: Ryan Bas Date: Mon, 11 May 2026 23:34:16 -0600 Subject: [PATCH 05/23] feat(docs-site): add Markdown renderer with callout support Co-Authored-By: Claude Opus 4.6 (1M context) --- .../docs-site/content/docs/getting-started.md | 37 +++ packages/docs-site/src/MarkdownRenderer.elm | 268 ++++++++++++++++++ 2 files changed, 305 insertions(+) create mode 100644 packages/docs-site/content/docs/getting-started.md create mode 100644 packages/docs-site/src/MarkdownRenderer.elm diff --git a/packages/docs-site/content/docs/getting-started.md b/packages/docs-site/content/docs/getting-started.md new file mode 100644 index 0000000..b268ee8 --- /dev/null +++ b/packages/docs-site/content/docs/getting-started.md @@ -0,0 +1,37 @@ +--- +title: 'Getting Started' +description: 'Install and configure wolfcola devtools' +section: guides +order: 1 +--- + +# Getting Started + +Install the wolfcola devtools packages you need. + +## Tree-Shake Verification + +```bash +npm install -D @wolfcola/treeshake-check +``` + +Run the CLI: + +```bash +npx treeshake-check your-package +``` + +## OIDC DevTools + +Install the bridge SDK to emit events from your OIDC client: + +```bash +npm install @wolfcola/devtools-bridge +``` + +The devtools bridge is framework-agnostic and works with any OIDC client. + +## Next Steps + +- Read the [Tree-Shaking Guide](/docs/tree-shaking) +- Explore the [Architecture](/architecture) diff --git a/packages/docs-site/src/MarkdownRenderer.elm b/packages/docs-site/src/MarkdownRenderer.elm new file mode 100644 index 0000000..478ca07 --- /dev/null +++ b/packages/docs-site/src/MarkdownRenderer.elm @@ -0,0 +1,268 @@ +module MarkdownRenderer exposing (renderer) + +{-| Custom Markdown renderer for wolfcola devtools documentation. + +Supports standard Markdown elements plus a custom `` HTML element +with an optional `type` attribute (info/warning). + +Code blocks emit `class="language-"` on the `` element for +Prism.js syntax highlighting. + +-} + +import Html exposing (Html) +import Html.Attributes as Attr +import Markdown.Block as Block exposing (ListItem) +import Markdown.Html +import Markdown.Renderer exposing (Renderer) + + +{-| The custom renderer for documentation pages. +-} +renderer : Renderer (Html msg) +renderer = + { heading = viewHeading + , paragraph = Html.p [] + , blockQuote = Html.blockquote [] + , html = htmlRenderer + , text = Html.text + , codeSpan = + \content -> Html.code [] [ Html.text content ] + , strong = + \children -> Html.strong [] children + , emphasis = + \children -> Html.em [] children + , strikethrough = + \children -> Html.del [] children + , hardLineBreak = Html.br [] [] + , link = viewLink + , image = viewImage + , unorderedList = viewUnorderedList + , orderedList = viewOrderedList + , codeBlock = viewCodeBlock + , thematicBreak = Html.hr [] [] + , table = Html.table [] + , tableHeader = Html.thead [] + , tableBody = Html.tbody [] + , tableRow = Html.tr [] + , tableCell = viewTableCell + , tableHeaderCell = viewTableHeaderCell + } + + + +-- Headings + + +viewHeading : { level : Block.HeadingLevel, rawText : String, children : List (Html msg) } -> Html msg +viewHeading { level, rawText, children } = + let + slug : String + slug = + rawText + |> String.toLower + |> String.replace " " "-" + |> String.filter (\c -> Char.isAlphaNum c || c == '-') + + tag : List (Html.Attribute msg) -> List (Html msg) -> Html msg + tag = + case level of + Block.H1 -> + Html.h1 + + Block.H2 -> + Html.h2 + + Block.H3 -> + Html.h3 + + Block.H4 -> + Html.h4 + + Block.H5 -> + Html.h5 + + Block.H6 -> + Html.h6 + in + tag [ Attr.id slug ] children + + + +-- Links + + +viewLink : { title : Maybe String, destination : String } -> List (Html msg) -> Html msg +viewLink link children = + case link.title of + Just title -> + Html.a + [ Attr.href link.destination + , Attr.title title + ] + children + + Nothing -> + Html.a [ Attr.href link.destination ] children + + + +-- Images + + +viewImage : { alt : String, src : String, title : Maybe String } -> Html msg +viewImage imageInfo = + case imageInfo.title of + Just title -> + Html.img + [ Attr.src imageInfo.src + , Attr.alt imageInfo.alt + , Attr.title title + ] + [] + + Nothing -> + Html.img + [ Attr.src imageInfo.src + , Attr.alt imageInfo.alt + ] + [] + + + +-- Lists + + +viewUnorderedList : List (ListItem (Html msg)) -> Html msg +viewUnorderedList items = + Html.ul [] + (items + |> List.map + (\item -> + case item of + Block.ListItem task children -> + let + checkbox : Html msg + checkbox = + case task of + Block.NoTask -> + Html.text "" + + Block.IncompleteTask -> + Html.input + [ Attr.disabled True + , Attr.checked False + , Attr.type_ "checkbox" + ] + [] + + Block.CompletedTask -> + Html.input + [ Attr.disabled True + , Attr.checked True + , Attr.type_ "checkbox" + ] + [] + in + Html.li [] (checkbox :: children) + ) + ) + + +viewOrderedList : Int -> List (List (Html msg)) -> Html msg +viewOrderedList startingIndex items = + Html.ol + (if startingIndex /= 1 then + [ Attr.start startingIndex ] + + else + [] + ) + (items + |> List.map (\itemBlocks -> Html.li [] itemBlocks) + ) + + + +-- Code blocks + + +viewCodeBlock : { body : String, language : Maybe String } -> Html msg +viewCodeBlock { body, language } = + let + codeAttrs : List (Html.Attribute msg) + codeAttrs = + case Maybe.map String.words language of + Just (lang :: _) -> + [ Attr.class ("language-" ++ lang) ] + + _ -> + [] + in + Html.pre [] + [ Html.code codeAttrs + [ Html.text body ] + ] + + + +-- Table cells + + +viewTableHeaderCell : Maybe Block.Alignment -> List (Html msg) -> Html msg +viewTableHeaderCell maybeAlignment = + Html.th (alignmentAttrs maybeAlignment) + + +viewTableCell : Maybe Block.Alignment -> List (Html msg) -> Html msg +viewTableCell maybeAlignment = + Html.td (alignmentAttrs maybeAlignment) + + +alignmentAttrs : Maybe Block.Alignment -> List (Html.Attribute msg) +alignmentAttrs maybeAlignment = + case maybeAlignment of + Just alignment -> + [ Attr.align + (case alignment of + Block.AlignLeft -> + "left" + + Block.AlignCenter -> + "center" + + Block.AlignRight -> + "right" + ) + ] + + Nothing -> + [] + + + +-- Custom HTML elements + + +htmlRenderer : Markdown.Html.Renderer (List (Html msg) -> Html msg) +htmlRenderer = + Markdown.Html.oneOf + [ Markdown.Html.tag "callout" + (\maybeType children -> + let + className : String + className = + case maybeType of + Just "warning" -> + "callout callout--warning" + + Just "info" -> + "callout callout--info" + + _ -> + "callout" + in + Html.div [ Attr.class className ] children + ) + |> Markdown.Html.withOptionalAttribute "type" + ] From 3abbc797a30467e780c0cdcf924db2ce3dfa25c9 Mon Sep 17 00:00:00 2001 From: Ryan Bas Date: Mon, 11 May 2026 23:39:56 -0600 Subject: [PATCH 06/23] feat(docs-site): add docs route for Markdown guide pages Co-Authored-By: Claude Opus 4.6 (1M context) --- packages/docs-site/app/Route/Docs/Slug_.elm | 121 ++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 packages/docs-site/app/Route/Docs/Slug_.elm diff --git a/packages/docs-site/app/Route/Docs/Slug_.elm b/packages/docs-site/app/Route/Docs/Slug_.elm new file mode 100644 index 0000000..2faac3e --- /dev/null +++ b/packages/docs-site/app/Route/Docs/Slug_.elm @@ -0,0 +1,121 @@ +module Route.Docs.Slug_ exposing (ActionData, Data, Model, Msg, route) + +import BackendTask exposing (BackendTask) +import BackendTask.File as File +import BackendTask.Glob as Glob +import FatalError exposing (FatalError) +import Head +import Head.Seo as Seo +import Html +import Html.Attributes as Attr +import Json.Decode as Decode exposing (Decoder) +import Markdown.Parser +import Markdown.Renderer +import MarkdownRenderer +import Pages.Url +import PagesMsg exposing (PagesMsg) +import RouteBuilder exposing (App, StatelessRoute) +import Shared +import View exposing (View) + + +type alias Model = + {} + + +type alias Msg = + () + + +type alias RouteParams = + { slug : String } + + +route : StatelessRoute RouteParams Data ActionData +route = + RouteBuilder.preRender + { head = head + , pages = pages + , data = data + } + |> RouteBuilder.buildNoState { view = view } + + +pages : BackendTask FatalError (List RouteParams) +pages = + Glob.succeed (\slug -> { slug = slug }) + |> Glob.match (Glob.literal "content/docs/") + |> Glob.capture Glob.wildcard + |> Glob.match (Glob.literal ".md") + |> Glob.toBackendTask + + +type alias Data = + { title : String + , description : String + , markdownBody : String + } + + +type alias ActionData = + {} + + +data : RouteParams -> BackendTask FatalError Data +data routeParams = + File.bodyWithFrontmatter + (\markdownBody -> + Decode.map2 + (\title description -> + { title = title + , description = description + , markdownBody = markdownBody + } + ) + (Decode.field "title" Decode.string) + (Decode.field "description" Decode.string) + ) + ("content/docs/" ++ routeParams.slug ++ ".md") + |> BackendTask.allowFatal + + +head : + App Data ActionData RouteParams + -> List Head.Tag +head app = + Seo.summary + { canonicalUrlOverride = Nothing + , siteName = "wolfcola devtools" + , image = + { url = Pages.Url.external "" + , alt = "wolfcola devtools" + , dimensions = Nothing + , mimeType = Nothing + } + , description = app.data.description + , locale = Nothing + , title = app.data.title + } + |> Seo.website + + +renderMarkdown : String -> List (Html.Html msg) +renderMarkdown markdownBody = + markdownBody + |> Markdown.Parser.parse + |> Result.mapError (\_ -> "Markdown parsing error.") + |> Result.andThen (Markdown.Renderer.render MarkdownRenderer.renderer) + |> Result.withDefault [] + + +view : + App Data ActionData RouteParams + -> Shared.Model + -> View (PagesMsg Msg) +view app _ = + { title = app.data.title + , body = + [ Html.article [ Attr.class "markdown-content" ] + (renderMarkdown app.data.markdownBody) + ] + } From df162b9d5ff7e1cf44c28982b0dee4541a272508 Mon Sep 17 00:00:00 2001 From: Ryan Bas Date: Mon, 11 May 2026 23:40:00 -0600 Subject: [PATCH 07/23] feat(docs-site): add packages route for package overview pages Co-Authored-By: Claude Opus 4.6 (1M context) --- .../docs-site/app/Route/Packages/Slug_.elm | 121 ++++++++++++++++++ .../content/packages/treeshake-check.md | 26 ++++ 2 files changed, 147 insertions(+) create mode 100644 packages/docs-site/app/Route/Packages/Slug_.elm create mode 100644 packages/docs-site/content/packages/treeshake-check.md diff --git a/packages/docs-site/app/Route/Packages/Slug_.elm b/packages/docs-site/app/Route/Packages/Slug_.elm new file mode 100644 index 0000000..97f9195 --- /dev/null +++ b/packages/docs-site/app/Route/Packages/Slug_.elm @@ -0,0 +1,121 @@ +module Route.Packages.Slug_ exposing (ActionData, Data, Model, Msg, route) + +import BackendTask exposing (BackendTask) +import BackendTask.File as File +import BackendTask.Glob as Glob +import FatalError exposing (FatalError) +import Head +import Head.Seo as Seo +import Html +import Html.Attributes as Attr +import Json.Decode as Decode exposing (Decoder) +import Markdown.Parser +import Markdown.Renderer +import MarkdownRenderer +import Pages.Url +import PagesMsg exposing (PagesMsg) +import RouteBuilder exposing (App, StatelessRoute) +import Shared +import View exposing (View) + + +type alias Model = + {} + + +type alias Msg = + () + + +type alias RouteParams = + { slug : String } + + +route : StatelessRoute RouteParams Data ActionData +route = + RouteBuilder.preRender + { head = head + , pages = pages + , data = data + } + |> RouteBuilder.buildNoState { view = view } + + +pages : BackendTask FatalError (List RouteParams) +pages = + Glob.succeed (\slug -> { slug = slug }) + |> Glob.match (Glob.literal "content/packages/") + |> Glob.capture Glob.wildcard + |> Glob.match (Glob.literal ".md") + |> Glob.toBackendTask + + +type alias Data = + { title : String + , description : String + , markdownBody : String + } + + +type alias ActionData = + {} + + +data : RouteParams -> BackendTask FatalError Data +data routeParams = + File.bodyWithFrontmatter + (\markdownBody -> + Decode.map2 + (\title description -> + { title = title + , description = description + , markdownBody = markdownBody + } + ) + (Decode.field "title" Decode.string) + (Decode.field "description" Decode.string) + ) + ("content/packages/" ++ routeParams.slug ++ ".md") + |> BackendTask.allowFatal + + +head : + App Data ActionData RouteParams + -> List Head.Tag +head app = + Seo.summary + { canonicalUrlOverride = Nothing + , siteName = "wolfcola devtools" + , image = + { url = Pages.Url.external "" + , alt = "wolfcola devtools" + , dimensions = Nothing + , mimeType = Nothing + } + , description = app.data.description + , locale = Nothing + , title = app.data.title + } + |> Seo.website + + +renderMarkdown : String -> List (Html.Html msg) +renderMarkdown markdownBody = + markdownBody + |> Markdown.Parser.parse + |> Result.mapError (\_ -> "Markdown parsing error.") + |> Result.andThen (Markdown.Renderer.render MarkdownRenderer.renderer) + |> Result.withDefault [] + + +view : + App Data ActionData RouteParams + -> Shared.Model + -> View (PagesMsg Msg) +view app _ = + { title = app.data.title + , body = + [ Html.article [ Attr.class "markdown-content" ] + (renderMarkdown app.data.markdownBody) + ] + } diff --git a/packages/docs-site/content/packages/treeshake-check.md b/packages/docs-site/content/packages/treeshake-check.md new file mode 100644 index 0000000..66ec25e --- /dev/null +++ b/packages/docs-site/content/packages/treeshake-check.md @@ -0,0 +1,26 @@ +--- +title: '@wolfcola/treeshake-check' +description: 'CLI & library to verify packages are tree-shakeable by Rollup' +section: packages +order: 1 +--- + +# @wolfcola/treeshake-check + +Verify that your npm packages are properly tree-shakeable. + +## Installation + +```bash +npm install -D @wolfcola/treeshake-check +``` + +## CLI Usage + +```bash +npx treeshake-check +``` + +Checks whether importing individual exports from a package produces minimal bundles via Rollup. + +This tool uses Rollup internally. Ensure Rollup is available in your project. From c99bd5c05777e3fc0f866ec65d039731a3b982df Mon Sep 17 00:00:00 2001 From: Ryan Bas Date: Mon, 11 May 2026 23:40:06 -0600 Subject: [PATCH 08/23] feat(docs-site): add contributing route for contributor docs Co-Authored-By: Claude Opus 4.6 (1M context) --- .../app/Route/Contributing/Slug_.elm | 121 ++++++++++++++++++ .../content/contributing/development-setup.md | 35 +++++ 2 files changed, 156 insertions(+) create mode 100644 packages/docs-site/app/Route/Contributing/Slug_.elm create mode 100644 packages/docs-site/content/contributing/development-setup.md diff --git a/packages/docs-site/app/Route/Contributing/Slug_.elm b/packages/docs-site/app/Route/Contributing/Slug_.elm new file mode 100644 index 0000000..4665572 --- /dev/null +++ b/packages/docs-site/app/Route/Contributing/Slug_.elm @@ -0,0 +1,121 @@ +module Route.Contributing.Slug_ exposing (ActionData, Data, Model, Msg, route) + +import BackendTask exposing (BackendTask) +import BackendTask.File as File +import BackendTask.Glob as Glob +import FatalError exposing (FatalError) +import Head +import Head.Seo as Seo +import Html +import Html.Attributes as Attr +import Json.Decode as Decode exposing (Decoder) +import Markdown.Parser +import Markdown.Renderer +import MarkdownRenderer +import Pages.Url +import PagesMsg exposing (PagesMsg) +import RouteBuilder exposing (App, StatelessRoute) +import Shared +import View exposing (View) + + +type alias Model = + {} + + +type alias Msg = + () + + +type alias RouteParams = + { slug : String } + + +route : StatelessRoute RouteParams Data ActionData +route = + RouteBuilder.preRender + { head = head + , pages = pages + , data = data + } + |> RouteBuilder.buildNoState { view = view } + + +pages : BackendTask FatalError (List RouteParams) +pages = + Glob.succeed (\slug -> { slug = slug }) + |> Glob.match (Glob.literal "content/contributing/") + |> Glob.capture Glob.wildcard + |> Glob.match (Glob.literal ".md") + |> Glob.toBackendTask + + +type alias Data = + { title : String + , description : String + , markdownBody : String + } + + +type alias ActionData = + {} + + +data : RouteParams -> BackendTask FatalError Data +data routeParams = + File.bodyWithFrontmatter + (\markdownBody -> + Decode.map2 + (\title description -> + { title = title + , description = description + , markdownBody = markdownBody + } + ) + (Decode.field "title" Decode.string) + (Decode.field "description" Decode.string) + ) + ("content/contributing/" ++ routeParams.slug ++ ".md") + |> BackendTask.allowFatal + + +head : + App Data ActionData RouteParams + -> List Head.Tag +head app = + Seo.summary + { canonicalUrlOverride = Nothing + , siteName = "wolfcola devtools" + , image = + { url = Pages.Url.external "" + , alt = "wolfcola devtools" + , dimensions = Nothing + , mimeType = Nothing + } + , description = app.data.description + , locale = Nothing + , title = app.data.title + } + |> Seo.website + + +renderMarkdown : String -> List (Html.Html msg) +renderMarkdown markdownBody = + markdownBody + |> Markdown.Parser.parse + |> Result.mapError (\_ -> "Markdown parsing error.") + |> Result.andThen (Markdown.Renderer.render MarkdownRenderer.renderer) + |> Result.withDefault [] + + +view : + App Data ActionData RouteParams + -> Shared.Model + -> View (PagesMsg Msg) +view app _ = + { title = app.data.title + , body = + [ Html.article [ Attr.class "markdown-content" ] + (renderMarkdown app.data.markdownBody) + ] + } diff --git a/packages/docs-site/content/contributing/development-setup.md b/packages/docs-site/content/contributing/development-setup.md new file mode 100644 index 0000000..55b0736 --- /dev/null +++ b/packages/docs-site/content/contributing/development-setup.md @@ -0,0 +1,35 @@ +--- +title: 'Development Setup' +description: 'Set up your local development environment for wolfcola-devtools' +section: contributing +order: 1 +--- + +# Development Setup + +## Prerequisites + +- Node.js 20, 22, or 24 +- pnpm 10.21+ +- Nix (recommended, provides all tools automatically) + +## Using Nix + +```bash +git clone https://github.com/ryanbas21/devtools.git +cd devtools +nix develop +``` + +The Nix dev shell provides Node.js, pnpm, Elm, and lefthook automatically. + +## Without Nix + +```bash +git clone https://github.com/ryanbas21/devtools.git +cd devtools +corepack enable +pnpm install +``` + +The repository uses Effect TypeScript extensively. See the Code Style guide for Effect conventions. From dd043207ceb1e906f582274094a7c1fcd20bc935 Mon Sep 17 00:00:00 2001 From: Ryan Bas Date: Mon, 11 May 2026 23:52:21 -0600 Subject: [PATCH 09/23] feat(docs-site): add API reference route with treeshake-check docs Add hand-written Elm modules for API documentation: - ApiDocs.Types: data types for API docs (ApiModule, TypeDoc, FunctionDoc) - ApiDocs.TreeshakeCheck: treeshake-check package documentation - Route.Api.Package_.ModuleName_: pre-rendered API reference pages - API reference and architecture CSS styles - Fix pre-existing ephemeral field disagreement in Blog, Docs, Contributing, and Packages routes by referencing description fields in view functions Co-Authored-By: Claude Opus 4.6 (1M context) --- .../app/Route/Api/Package_/ModuleName_.elm | 179 ++++++++++++++++++ packages/docs-site/app/Route/Blog/Slug_.elm | 2 +- .../app/Route/Contributing/Slug_.elm | 4 +- packages/docs-site/app/Route/Docs/Slug_.elm | 4 +- .../docs-site/app/Route/Packages/Slug_.elm | 4 +- .../docs-site/src/ApiDocs/TreeshakeCheck.elm | 28 +++ packages/docs-site/src/ApiDocs/Types.elm | 24 +++ packages/docs-site/style.css | 52 +++++ 8 files changed, 293 insertions(+), 4 deletions(-) create mode 100644 packages/docs-site/app/Route/Api/Package_/ModuleName_.elm create mode 100644 packages/docs-site/src/ApiDocs/TreeshakeCheck.elm create mode 100644 packages/docs-site/src/ApiDocs/Types.elm diff --git a/packages/docs-site/app/Route/Api/Package_/ModuleName_.elm b/packages/docs-site/app/Route/Api/Package_/ModuleName_.elm new file mode 100644 index 0000000..28da43a --- /dev/null +++ b/packages/docs-site/app/Route/Api/Package_/ModuleName_.elm @@ -0,0 +1,179 @@ +module Route.Api.Package_.ModuleName_ exposing (ActionData, Data, Model, Msg, route) + +import ApiDocs.TreeshakeCheck +import ApiDocs.Types exposing (ApiModule, FunctionDoc, TypeDoc) +import BackendTask exposing (BackendTask) +import FatalError exposing (FatalError) +import Head +import Head.Seo as Seo +import Html +import Html.Attributes as Attr +import Pages.Url +import PagesMsg exposing (PagesMsg) +import RouteBuilder exposing (App, StatelessRoute) +import Shared +import View exposing (View) + + +type alias Model = + {} + + +type alias Msg = + () + + +type alias RouteParams = + { package : String + , moduleName : String + } + + +type alias Data = + { apiModule : ApiModule + } + + +type alias ActionData = + {} + + +route : StatelessRoute RouteParams Data ActionData +route = + RouteBuilder.preRender + { head = head + , pages = pages + , data = data + } + |> RouteBuilder.buildNoState { view = view } + + +allModules : List ( String, String, ApiModule ) +allModules = + List.map (\m -> ( "treeshake-check", m.name, m )) ApiDocs.TreeshakeCheck.modules + + +pages : BackendTask FatalError (List RouteParams) +pages = + allModules + |> List.map (\( pkg, modName, _ ) -> { package = pkg, moduleName = modName }) + |> BackendTask.succeed + + +data : RouteParams -> BackendTask FatalError Data +data routeParams = + case findModule routeParams.package routeParams.moduleName of + Just apiMod -> + BackendTask.succeed { apiModule = apiMod } + + Nothing -> + BackendTask.fail + (FatalError.fromString + ("API module not found: " ++ routeParams.package ++ "/" ++ routeParams.moduleName) + ) + + +findModule : String -> String -> Maybe ApiModule +findModule pkg modName = + allModules + |> List.filter (\( p, n, _ ) -> p == pkg && n == modName) + |> List.head + |> Maybe.map (\( _, _, m ) -> m) + + +head : + App Data ActionData RouteParams + -> List Head.Tag +head app = + Seo.summary + { canonicalUrlOverride = Nothing + , siteName = "wolfcola devtools" + , image = + { url = Pages.Url.external "" + , alt = "wolfcola devtools" + , dimensions = Nothing + , mimeType = Nothing + } + , description = app.data.apiModule.description + , locale = Nothing + , title = app.data.apiModule.name ++ " - API Reference" + } + |> Seo.website + + +view : + App Data ActionData RouteParams + -> Shared.Model + -> View (PagesMsg Msg) +view app _ = + let + apiMod = + app.data.apiModule + in + { title = apiMod.name ++ " - API Reference" + , body = + [ Html.div [ Attr.class "api-reference" ] + ([ Html.h1 [] [ Html.text apiMod.name ] + , Html.p [ Attr.class "api-description" ] [ Html.text apiMod.description ] + ] + ++ viewTypes apiMod.types + ++ viewFunctions apiMod.functions + ) + ] + } + + +viewTypes : List TypeDoc -> List (Html.Html (PagesMsg Msg)) +viewTypes types = + if List.isEmpty types then + [] + + else + Html.h2 [] [ Html.text "Types" ] + :: List.map viewType types + + +viewType : TypeDoc -> Html.Html (PagesMsg Msg) +viewType typeDoc = + Html.div [ Attr.class "api-item" ] + [ Html.h3 [ Attr.class "api-item-name" ] [ Html.text typeDoc.name ] + , Html.pre [ Attr.class "api-signature" ] + [ Html.code [] [ Html.text typeDoc.signature ] ] + , Html.p [] [ Html.text typeDoc.description ] + ] + + +viewFunctions : List FunctionDoc -> List (Html.Html (PagesMsg Msg)) +viewFunctions functions = + if List.isEmpty functions then + [] + + else + Html.h2 [] [ Html.text "Functions" ] + :: List.map viewFunction functions + + +viewFunction : FunctionDoc -> Html.Html (PagesMsg Msg) +viewFunction funcDoc = + Html.div [ Attr.class "api-item" ] + ([ Html.h3 [ Attr.class "api-item-name" ] [ Html.text funcDoc.name ] + , Html.pre [ Attr.class "api-signature" ] + [ Html.code [] [ Html.text funcDoc.signature ] ] + , Html.p [] [ Html.text funcDoc.description ] + ] + ++ viewExample funcDoc.example + ) + + +viewExample : Maybe String -> List (Html.Html (PagesMsg Msg)) +viewExample maybeExample = + case maybeExample of + Just example -> + [ Html.div [ Attr.class "api-example" ] + [ Html.h4 [] [ Html.text "Example" ] + , Html.pre [] [ Html.code [] [ Html.text example ] ] + ] + ] + + Nothing -> + [] diff --git a/packages/docs-site/app/Route/Blog/Slug_.elm b/packages/docs-site/app/Route/Blog/Slug_.elm index bfbedcf..b823c97 100644 --- a/packages/docs-site/app/Route/Blog/Slug_.elm +++ b/packages/docs-site/app/Route/Blog/Slug_.elm @@ -82,5 +82,5 @@ view : -> View (PagesMsg Msg) view app sharedModel = { title = "Placeholder - Blog.Slug_" - , body = [ Html.text "You're on the page Blog.Slug_" ] + , body = [ Html.text app.data.something ] } diff --git a/packages/docs-site/app/Route/Contributing/Slug_.elm b/packages/docs-site/app/Route/Contributing/Slug_.elm index 4665572..b38d614 100644 --- a/packages/docs-site/app/Route/Contributing/Slug_.elm +++ b/packages/docs-site/app/Route/Contributing/Slug_.elm @@ -116,6 +116,8 @@ view app _ = { title = app.data.title , body = [ Html.article [ Attr.class "markdown-content" ] - (renderMarkdown app.data.markdownBody) + (Html.p [ Attr.class "page-description" ] [ Html.text app.data.description ] + :: renderMarkdown app.data.markdownBody + ) ] } diff --git a/packages/docs-site/app/Route/Docs/Slug_.elm b/packages/docs-site/app/Route/Docs/Slug_.elm index 2faac3e..261cdf4 100644 --- a/packages/docs-site/app/Route/Docs/Slug_.elm +++ b/packages/docs-site/app/Route/Docs/Slug_.elm @@ -116,6 +116,8 @@ view app _ = { title = app.data.title , body = [ Html.article [ Attr.class "markdown-content" ] - (renderMarkdown app.data.markdownBody) + (Html.p [ Attr.class "page-description" ] [ Html.text app.data.description ] + :: renderMarkdown app.data.markdownBody + ) ] } diff --git a/packages/docs-site/app/Route/Packages/Slug_.elm b/packages/docs-site/app/Route/Packages/Slug_.elm index 97f9195..a68128e 100644 --- a/packages/docs-site/app/Route/Packages/Slug_.elm +++ b/packages/docs-site/app/Route/Packages/Slug_.elm @@ -116,6 +116,8 @@ view app _ = { title = app.data.title , body = [ Html.article [ Attr.class "markdown-content" ] - (renderMarkdown app.data.markdownBody) + (Html.p [ Attr.class "page-description" ] [ Html.text app.data.description ] + :: renderMarkdown app.data.markdownBody + ) ] } diff --git a/packages/docs-site/src/ApiDocs/TreeshakeCheck.elm b/packages/docs-site/src/ApiDocs/TreeshakeCheck.elm new file mode 100644 index 0000000..74ad90e --- /dev/null +++ b/packages/docs-site/src/ApiDocs/TreeshakeCheck.elm @@ -0,0 +1,28 @@ +module ApiDocs.TreeshakeCheck exposing (modules) + +import ApiDocs.Types exposing (ApiModule) + + +modules : List ApiModule +modules = + [ { name = "treeshake-check" + , description = "CLI & library to verify packages are tree-shakeable by Rollup" + , types = + [ { name = "TreeshakeResult" + , signature = "type TreeshakeResult = { passed: boolean; exports: ExportResult[] }" + , description = "The result of checking a package for tree-shaking support." + } + , { name = "ExportResult" + , signature = "type ExportResult = { name: string; bundleSize: number; treeshakeable: boolean }" + , description = "Result for an individual export." + } + ] + , functions = + [ { name = "checkTreeShaking" + , signature = "(packageName: string, options?: CheckOptions) => Effect.Effect" + , description = "Check whether a package's exports are tree-shakeable." + , example = Just "import { checkTreeShaking } from \"@wolfcola/treeshake-check\"\n\nconst result = yield* checkTreeShaking(\"my-package\")" + } + ] + } + ] diff --git a/packages/docs-site/src/ApiDocs/Types.elm b/packages/docs-site/src/ApiDocs/Types.elm new file mode 100644 index 0000000..13b5365 --- /dev/null +++ b/packages/docs-site/src/ApiDocs/Types.elm @@ -0,0 +1,24 @@ +module ApiDocs.Types exposing (ApiModule, FunctionDoc, TypeDoc) + + +type alias ApiModule = + { name : String + , description : String + , types : List TypeDoc + , functions : List FunctionDoc + } + + +type alias TypeDoc = + { name : String + , signature : String + , description : String + } + + +type alias FunctionDoc = + { name : String + , signature : String + , description : String + , example : Maybe String + } diff --git a/packages/docs-site/style.css b/packages/docs-site/style.css index e23a717..803d87f 100644 --- a/packages/docs-site/style.css +++ b/packages/docs-site/style.css @@ -307,3 +307,55 @@ a:hover { .quick-links h2 { margin-bottom: 1rem; } + +/* API Reference */ +.api-reference { + max-width: var(--content-max); +} +.api-description { + font-size: 1.1rem; + color: var(--text-muted); + margin-bottom: 2rem; +} +.api-item { + padding: 1.25rem 0; + border-bottom: 1px solid var(--border); +} +.api-item:last-child { + border-bottom: none; +} +.api-item-name { + font-family: var(--font-mono); + font-size: 1.1rem; + color: var(--accent); + margin-bottom: 0.5rem; +} +.api-signature { + background: var(--bg-sidebar); + border: 1px solid var(--border); + border-radius: 6px; + padding: 0.75rem 1rem; + font-size: 0.85rem; + margin-bottom: 0.75rem; +} +.api-example { + margin-top: 0.75rem; +} +.api-example h4 { + font-size: 0.8rem; + text-transform: uppercase; + letter-spacing: 0.05em; + color: var(--text-muted); + margin-bottom: 0.5rem; +} + +/* Architecture */ +.architecture-diagram { + display: flex; + justify-content: center; + margin: 2rem 0; + padding: 1.5rem; + background: var(--bg-sidebar); + border-radius: 8px; + border: 1px solid var(--border); +} From 3f5079c5383fb92d70a7f010c1f3c39fb1b77a87 Mon Sep 17 00:00:00 2001 From: Ryan Bas Date: Mon, 11 May 2026 23:52:35 -0600 Subject: [PATCH 10/23] feat(docs-site): add architecture page with SVG package diagram Add static Architecture route showing how wolfcola devtools packages relate to each other. Includes: - SVG diagram with package boxes grouped by tool family (OIDC DevTools and Tree-Shake Tools) with dependency arrows - Package relationships description list - Helper functions for SVG primitives (svgBox, svgGroup, svgArrow) - elm/svg dependency added to elm.json Co-Authored-By: Claude Opus 4.6 (1M context) --- packages/docs-site/app/Route/Architecture.elm | 219 ++++++++++++++++++ packages/docs-site/elm.json | 1 + pnpm-lock.yaml | 26 --- 3 files changed, 220 insertions(+), 26 deletions(-) create mode 100644 packages/docs-site/app/Route/Architecture.elm diff --git a/packages/docs-site/app/Route/Architecture.elm b/packages/docs-site/app/Route/Architecture.elm new file mode 100644 index 0000000..8239a82 --- /dev/null +++ b/packages/docs-site/app/Route/Architecture.elm @@ -0,0 +1,219 @@ +module Route.Architecture exposing (ActionData, Data, Model, Msg, route) + +import BackendTask exposing (BackendTask) +import FatalError exposing (FatalError) +import Head +import Head.Seo as Seo +import Html +import Html.Attributes as Attr +import Pages.Url +import PagesMsg exposing (PagesMsg) +import RouteBuilder exposing (App, StatelessRoute) +import Shared +import Svg +import Svg.Attributes as SvgAttr +import View exposing (View) + + +type alias Model = + {} + + +type alias Msg = + () + + +type alias RouteParams = + {} + + +type alias Data = + {} + + +type alias ActionData = + {} + + +route : StatelessRoute RouteParams Data ActionData +route = + RouteBuilder.single + { head = head + , data = data + } + |> RouteBuilder.buildNoState { view = view } + + +data : BackendTask FatalError Data +data = + BackendTask.succeed {} + + +head : + App Data ActionData RouteParams + -> List Head.Tag +head app = + Seo.summary + { canonicalUrlOverride = Nothing + , siteName = "wolfcola devtools" + , image = + { url = Pages.Url.external "" + , alt = "wolfcola devtools" + , dimensions = Nothing + , mimeType = Nothing + } + , description = "Architecture overview of wolfcola devtools packages" + , locale = Nothing + , title = "Architecture - wolfcola devtools" + } + |> Seo.website + + +view : + App Data ActionData RouteParams + -> Shared.Model + -> View (PagesMsg Msg) +view app _ = + { title = "Architecture - wolfcola devtools" + , body = + [ Html.h1 [] [ Html.text "Architecture" ] + , Html.p [] + [ Html.text "wolfcola devtools is organized into two tool families: " + , Html.strong [] [ Html.text "OIDC DevTools" ] + , Html.text " for debugging authentication flows, and " + , Html.strong [] [ Html.text "Tree-Shake Tools" ] + , Html.text " for verifying that packages are tree-shakeable by bundlers like Rollup." + ] + , Html.div [ Attr.class "architecture-diagram" ] + [ viewDiagram ] + , Html.h2 [] [ Html.text "Package Relationships" ] + , Html.dl [] + [ Html.dt [] [ Html.text "@wolfcola/devtools-types" ] + , Html.dd [] + [ Html.text "Effect Schema definitions for AuthEvent and FlowState. Shared foundation used by devtools-bridge." ] + , Html.dt [] [ Html.text "@wolfcola/devtools-bridge" ] + , Html.dd [] + [ Html.text "SDK adapter for emitting events from DaVinci, Journey, and OIDC clients. Depends on devtools-types." ] + , Html.dt [] [ Html.text "@wolfcola/treeshake-check" ] + , Html.dd [] + [ Html.text "CLI & library to verify packages are tree-shakeable by Rollup. Standalone package." ] + , Html.dt [] [ Html.text "@wolfcola/eslint-plugin-treeshake" ] + , Html.dd [] + [ Html.text "ESLint plugin that flags tree-breaking patterns. Can use treeshake-check for verification." ] + ] + ] + } + + +viewDiagram : Html.Html (PagesMsg Msg) +viewDiagram = + Svg.svg + [ SvgAttr.viewBox "0 0 700 360" + , SvgAttr.width "700" + , SvgAttr.height "360" + , SvgAttr.style "max-width: 100%; height: auto;" + ] + [ -- OIDC DevTools group + svgGroup 20 20 320 300 "OIDC DevTools" + , svgBox 60 80 240 60 "devtools-types" + , svgBox 60 200 240 60 "devtools-bridge" + + -- Tree-Shake Tools group + , svgGroup 370 20 310 300 "Tree-Shake Tools" + , svgBox 400 80 240 60 "treeshake-check" + , svgBox 400 200 240 60 "eslint-plugin-treeshake" + + -- Arrows + , svgArrow 180 140 180 200 + + -- devtools-types -> devtools-bridge + , svgArrow 520 160 520 200 + + -- treeshake-check -> eslint-plugin-treeshake (optional dependency) + ] + + +svgBox : Float -> Float -> Float -> Float -> String -> Svg.Svg msg +svgBox x y w h label = + Svg.g [] + [ Svg.rect + [ SvgAttr.x (String.fromFloat x) + , SvgAttr.y (String.fromFloat y) + , SvgAttr.width (String.fromFloat w) + , SvgAttr.height (String.fromFloat h) + , SvgAttr.rx "8" + , SvgAttr.fill "var(--bg, #ffffff)" + , SvgAttr.stroke "var(--accent, #3b82f6)" + , SvgAttr.strokeWidth "2" + ] + [] + , Svg.text_ + [ SvgAttr.x (String.fromFloat (x + w / 2)) + , SvgAttr.y (String.fromFloat (y + h / 2 + 5)) + , SvgAttr.textAnchor "middle" + , SvgAttr.fill "var(--text, #1a1a2e)" + , SvgAttr.fontFamily "var(--font-mono, monospace)" + , SvgAttr.fontSize "13" + ] + [ Svg.text label ] + ] + + +svgGroup : Float -> Float -> Float -> Float -> String -> Svg.Svg msg +svgGroup x y w h label = + Svg.g [] + [ Svg.rect + [ SvgAttr.x (String.fromFloat x) + , SvgAttr.y (String.fromFloat y) + , SvgAttr.width (String.fromFloat w) + , SvgAttr.height (String.fromFloat h) + , SvgAttr.rx "12" + , SvgAttr.fill "none" + , SvgAttr.stroke "var(--border, #e5e7eb)" + , SvgAttr.strokeWidth "2" + , SvgAttr.strokeDasharray "8 4" + ] + [] + , Svg.text_ + [ SvgAttr.x (String.fromFloat (x + 16)) + , SvgAttr.y (String.fromFloat (y + 16)) + , SvgAttr.fill "var(--text-muted, #6b7280)" + , SvgAttr.fontSize "12" + , SvgAttr.fontWeight "600" + , SvgAttr.textAnchor "start" + , SvgAttr.dominantBaseline "hanging" + ] + [ Svg.text label ] + ] + + +svgArrow : Float -> Float -> Float -> Float -> Svg.Svg msg +svgArrow x1 y1 x2 y2 = + Svg.g [] + [ Svg.line + [ SvgAttr.x1 (String.fromFloat x1) + , SvgAttr.y1 (String.fromFloat y1) + , SvgAttr.x2 (String.fromFloat x2) + , SvgAttr.y2 (String.fromFloat y2) + , SvgAttr.stroke "var(--text-muted, #6b7280)" + , SvgAttr.strokeWidth "2" + , SvgAttr.markerEnd "url(#arrowhead)" + ] + [] + , Svg.defs [] + [ Svg.marker + [ SvgAttr.id "arrowhead" + , SvgAttr.markerWidth "10" + , SvgAttr.markerHeight "7" + , SvgAttr.refX "10" + , SvgAttr.refY "3.5" + , SvgAttr.orient "auto" + ] + [ Svg.polygon + [ SvgAttr.points "0 0, 10 3.5, 0 7" + , SvgAttr.fill "var(--text-muted, #6b7280)" + ] + [] + ] + ] + ] diff --git a/packages/docs-site/elm.json b/packages/docs-site/elm.json index 563df0a..6daa067 100644 --- a/packages/docs-site/elm.json +++ b/packages/docs-site/elm.json @@ -18,6 +18,7 @@ "elm/json": "1.1.4", "elm/parser": "1.1.0", "elm/regex": "1.0.0", + "elm/svg": "1.0.1", "elm/time": "1.0.0", "elm/url": "1.0.0", "elm/virtual-dom": "1.0.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ea48638..483eeaa 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,32 +4,6 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false -catalogs: - effect: - '@effect/cli': - specifier: ^0.75.1 - version: 0.75.1 - '@effect/platform': - specifier: ^0.96.0 - version: 0.96.1 - '@effect/platform-node': - specifier: ^0.106.0 - version: 0.106.0 - '@effect/vitest': - specifier: ^0.29.0 - version: 0.29.0 - effect: - specifier: ^3.21.1 - version: 3.21.2 - vite: - vite: - specifier: ^7.3.2 - version: 7.3.3 - vitest: - vitest: - specifier: ^3.2.0 - version: 3.2.4 - importers: .: From cd46c49ce4ef25b0b9d4e35f422a517432559ab1 Mon Sep 17 00:00:00 2001 From: Ryan Bas Date: Tue, 12 May 2026 00:00:19 -0600 Subject: [PATCH 11/23] feat(docs-site): add client-side search over content index Co-Authored-By: Claude Opus 4.6 (1M context) --- packages/docs-site/app/Shared.elm | 122 ++++++++++++++++++++++++++++-- packages/docs-site/src/Search.elm | 48 ++++++++++++ packages/docs-site/style.css | 56 ++++++++++++++ 3 files changed, 220 insertions(+), 6 deletions(-) create mode 100644 packages/docs-site/src/Search.elm diff --git a/packages/docs-site/app/Shared.elm b/packages/docs-site/app/Shared.elm index 2cd8c56..fcacbb3 100644 --- a/packages/docs-site/app/Shared.elm +++ b/packages/docs-site/app/Shared.elm @@ -1,14 +1,18 @@ module Shared exposing (Data, Model, Msg(..), SharedMsg(..), template) import BackendTask exposing (BackendTask) +import BackendTask.File as File +import BackendTask.Glob as Glob import Effect exposing (Effect) import FatalError exposing (FatalError) import Html exposing (Html) import Html.Attributes as Attr import Html.Events +import Json.Decode as Decode import Pages.Flags import Pages.PageUrl exposing (PageUrl) import Route exposing (Route) +import Search exposing (SearchEntry, SearchIndex) import SharedTemplate exposing (SharedTemplate) import UrlPath exposing (UrlPath) import View exposing (View) @@ -28,10 +32,12 @@ template = type Msg = SharedMsg SharedMsg | ToggleSidebar + | SearchInput String type alias Data = - () + { searchIndex : SearchIndex + } type SharedMsg @@ -40,6 +46,7 @@ type SharedMsg type alias Model = { sidebarOpen : Bool + , searchQuery : String } @@ -57,7 +64,9 @@ init : } -> ( Model, Effect Msg ) init flags maybePagePath = - ( { sidebarOpen = True } + ( { sidebarOpen = True + , searchQuery = "" + } , Effect.none ) @@ -71,6 +80,9 @@ update msg model = ToggleSidebar -> ( { model | sidebarOpen = not model.sidebarOpen }, Effect.none ) + SearchInput query -> + ( { model | searchQuery = query }, Effect.none ) + subscriptions : UrlPath -> Model -> Sub Msg subscriptions _ _ = @@ -79,7 +91,66 @@ subscriptions _ _ = data : BackendTask FatalError Data data = - BackendTask.succeed () + BackendTask.map3 + (\docsEntries packageEntries contributingEntries -> + { searchIndex = + Search.buildIndex + (docsEntries ++ packageEntries ++ contributingEntries) + } + ) + (globEntries "content/docs/" "docs") + (globEntries "content/packages/" "packages") + (globEntries "content/contributing/" "contributing") + + +globEntries : String -> String -> BackendTask FatalError (List SearchEntry) +globEntries dir section = + Glob.succeed (\slug -> slug) + |> Glob.match (Glob.literal dir) + |> Glob.capture Glob.wildcard + |> Glob.match (Glob.literal ".md") + |> Glob.toBackendTask + |> BackendTask.andThen + (\slugs -> + slugs + |> List.map + (\slug -> + File.onlyFrontmatter + (Decode.map2 + (\title description -> + { title = title + , url = "/" ++ sectionToUrlPrefix section ++ "/" ++ slug + , section = section + , excerpt = description + } + ) + (Decode.field "title" Decode.string) + (Decode.field "description" Decode.string) + ) + (dir ++ slug ++ ".md") + |> BackendTask.allowFatal + ) + |> BackendTask.combine + ) + + +sectionToUrlPrefix : String -> String +sectionToUrlPrefix section = + case section of + "guides" -> + "docs" + + "docs" -> + "docs" + + "packages" -> + "packages" + + "contributing" -> + "contributing" + + _ -> + section view : @@ -94,7 +165,7 @@ view : -> { body : List (Html msg), title : String } view sharedData page model toMsg pageView = { body = - [ viewHeader model toMsg + [ viewHeader sharedData model toMsg , Html.div [ Attr.class "layout" ] [ viewSidebar model toMsg , Html.main_ [ Attr.class "content" ] @@ -105,8 +176,8 @@ view sharedData page model toMsg pageView = } -viewHeader : Model -> (Msg -> msg) -> Html msg -viewHeader model toMsg = +viewHeader : Data -> Model -> (Msg -> msg) -> Html msg +viewHeader sharedData model toMsg = Html.header [ Attr.class "header" ] [ Html.button [ Attr.class "sidebar-toggle" @@ -125,6 +196,7 @@ viewHeader model toMsg = , Attr.href "/" ] [ Html.text "wolfcola devtools" ] + , viewSearch sharedData model toMsg , Html.nav [ Attr.class "header-nav" ] [ Html.a [ Attr.href "/packages" ] [ Html.text "Packages" ] , Html.a [ Attr.href "/guides" ] [ Html.text "Guides" ] @@ -135,6 +207,44 @@ viewHeader model toMsg = ] +viewSearch : Data -> Model -> (Msg -> msg) -> Html msg +viewSearch sharedData model toMsg = + let + results = + Search.search model.searchQuery sharedData.searchIndex + in + Html.div [ Attr.class "search-wrapper" ] + ([ Html.input + [ Attr.class "search-input" + , Attr.type_ "text" + , Attr.placeholder "Search docs..." + , Attr.value model.searchQuery + , Html.Events.onInput (\val -> toMsg (SearchInput val)) + ] + [] + ] + ++ (if List.isEmpty results then + [] + + else + [ Html.div [ Attr.class "search-results" ] + (List.map viewSearchResult results) + ] + ) + ) + + +viewSearchResult : Search.SearchResult -> Html msg +viewSearchResult result = + Html.a + [ Attr.class "search-result" + , Attr.href result.url + ] + [ Html.span [ Attr.class "search-result-section" ] [ Html.text result.section ] + , Html.span [ Attr.class "search-result-title" ] [ Html.text result.title ] + ] + + viewSidebar : Model -> (Msg -> msg) -> Html msg viewSidebar model toMsg = Html.aside diff --git a/packages/docs-site/src/Search.elm b/packages/docs-site/src/Search.elm new file mode 100644 index 0000000..20c71eb --- /dev/null +++ b/packages/docs-site/src/Search.elm @@ -0,0 +1,48 @@ +module Search exposing (SearchEntry, SearchIndex, SearchResult, buildIndex, search) + + +type alias SearchEntry = + { title : String + , url : String + , section : String + , excerpt : String + } + + +type alias SearchIndex = + List SearchEntry + + +type alias SearchResult = + SearchEntry + + +buildIndex : List SearchEntry -> SearchIndex +buildIndex entries = + entries + + +search : String -> SearchIndex -> List SearchResult +search query index = + if String.length query < 2 then + [] + + else + let + lowerQuery = + String.toLower query + in + index + |> List.filter + (\entry -> + String.contains lowerQuery (String.toLower entry.title) + || String.contains lowerQuery (String.toLower entry.excerpt) + ) + |> List.sortBy + (\entry -> + if String.contains lowerQuery (String.toLower entry.title) then + 0 + + else + 1 + ) diff --git a/packages/docs-site/style.css b/packages/docs-site/style.css index 803d87f..c01ce8c 100644 --- a/packages/docs-site/style.css +++ b/packages/docs-site/style.css @@ -350,6 +350,62 @@ a:hover { } /* Architecture */ +/* Search */ +.search-wrapper { + position: relative; + margin-left: 1rem; +} +.search-input { + width: 220px; + padding: 0.4rem 0.75rem; + border: 1px solid var(--border); + border-radius: 6px; + background: var(--bg); + color: var(--text); + font-size: 0.875rem; + font-family: var(--font-sans); +} +.search-input:focus { + outline: none; + border-color: var(--accent); +} +.search-results { + position: absolute; + top: 100%; + left: 0; + right: 0; + margin-top: 0.25rem; + background: var(--bg); + border: 1px solid var(--border); + border-radius: 6px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); + max-height: 300px; + overflow-y: auto; + z-index: 200; +} +.search-result { + display: flex; + flex-direction: column; + padding: 0.5rem 0.75rem; + border-bottom: 1px solid var(--border); + color: var(--text); +} +.search-result:last-child { + border-bottom: none; +} +.search-result:hover { + background: var(--bg-sidebar); +} +.search-result-section { + font-size: 0.7rem; + text-transform: uppercase; + letter-spacing: 0.05em; + color: var(--text-muted); +} +.search-result-title { + font-size: 0.875rem; +} + .architecture-diagram { display: flex; justify-content: center; From 00e1794f5feebde7f33dbb0736b6c1994bd7c5d0 Mon Sep 17 00:00:00 2001 From: Ryan Bas Date: Tue, 12 May 2026 00:00:50 -0600 Subject: [PATCH 12/23] feat(docs-site): add all content pages for guides, packages, and contributing Co-Authored-By: Claude Opus 4.6 (1M context) --- .../content/contributing/code-style.md | 138 ++++++++++++++++++ .../content/contributing/release-process.md | 119 +++++++++++++++ .../contributing/repository-structure.md | 80 ++++++++++ .../content/docs/davinci-integration.md | 101 +++++++++++++ .../content/docs/devtools-extension.md | 80 ++++++++++ .../docs-site/content/docs/tree-shaking.md | 125 ++++++++++++++++ .../content/docs/vscode-extension.md | 86 +++++++++++ .../content/packages/devtools-bridge.md | 108 ++++++++++++++ .../content/packages/devtools-types.md | 128 ++++++++++++++++ .../packages/eslint-plugin-treeshake.md | 116 +++++++++++++++ 10 files changed, 1081 insertions(+) create mode 100644 packages/docs-site/content/contributing/code-style.md create mode 100644 packages/docs-site/content/contributing/release-process.md create mode 100644 packages/docs-site/content/contributing/repository-structure.md create mode 100644 packages/docs-site/content/docs/davinci-integration.md create mode 100644 packages/docs-site/content/docs/devtools-extension.md create mode 100644 packages/docs-site/content/docs/tree-shaking.md create mode 100644 packages/docs-site/content/docs/vscode-extension.md create mode 100644 packages/docs-site/content/packages/devtools-bridge.md create mode 100644 packages/docs-site/content/packages/devtools-types.md create mode 100644 packages/docs-site/content/packages/eslint-plugin-treeshake.md diff --git a/packages/docs-site/content/contributing/code-style.md b/packages/docs-site/content/contributing/code-style.md new file mode 100644 index 0000000..71849b2 --- /dev/null +++ b/packages/docs-site/content/contributing/code-style.md @@ -0,0 +1,138 @@ +--- +title: 'Code Style' +description: 'Coding conventions for Effect TypeScript and Elm in this repository' +section: contributing +order: 3 +--- + +# Code Style + +This repository uses two primary languages: Effect TypeScript for all runtime packages and Elm for the documentation site. Each has its own conventions. + +## Effect TypeScript Conventions + +### Prefer `Effect.fnUntraced` Over `Effect.gen` Wrappers + +Instead of writing a function that returns `Effect.gen`: + +```typescript +// Avoid +const fetchUser = (id: string) => + Effect.gen(function* () { + const client = yield* HttpClient; + return yield* client.get(`/users/${id}`); + }); +``` + +Use `Effect.fnUntraced`: + +```typescript +// Preferred +const fetchUser = Effect.fnUntraced(function* (id: string) { + const client = yield* HttpClient; + return yield* client.get(`/users/${id}`); +}); +``` + +### Use `Context.Service` Class Syntax + +Define services using the class-based syntax: + +```typescript +import { Context } from 'effect'; + +class UserRepository extends Context.Service< + UserRepository, + { + readonly findById: (id: string) => Effect.Effect; + readonly save: (user: User) => Effect.Effect; + } +>()('UserRepository') {} +``` + +### Never Use `async`/`await` or `try`/`catch` + +All asynchronous and error-handling code must use Effect APIs: + +```typescript +// Bad +const data = await fetch(url); + +// Good +const data = yield * Effect.tryPromise(() => fetch(url)); +``` + +### Never Use `Date.now()` or `new Date()` + +Use the Effect `Clock` module for time operations. In tests, use `TestClock` to control time: + +```typescript +import { Clock } from 'effect'; + +const now = yield * Clock.currentTimeMillis; +``` + +### Use `Schema.TaggedStruct` for Discriminated Unions + +```typescript +const AuthEvent = Schema.TaggedStruct('AuthEvent', { + type: Schema.String, + timestamp: Schema.Number, + data: Schema.Unknown, +}); +``` + +## Testing Conventions + +### Use `it.effect` for All Effect Tests + +```typescript +import { assert, describe, it } from '@effect/vitest'; + +describe('UserRepository', () => { + it.effect('finds a user by id', () => + Effect.gen(function* () { + const repo = yield* UserRepository; + const user = yield* repo.findById('123'); + assert.strictEqual(user.name, 'Alice'); + }), + ); +}); +``` + +### Use `assert` Instead of `expect` + +The `@effect/vitest` package provides `assert` methods. Never import `expect` from vitest in Effect tests: + +```typescript +// Bad +expect(result).toBe(42); + +// Good +assert.strictEqual(result, 42); +``` + +### Test File Location + +Test files live in `packages//test/` directories, mirroring the source structure. + +## Elm Conventions + +The docs site follows standard Elm conventions: + +- **elm-format** — All Elm files must be formatted with `elm-format`. The pre-commit hook runs this automatically. +- **Module naming** — Modules use PascalCase and match their file path (e.g. `ApiDocs.TreeshakeCheck` lives at `src/ApiDocs/TreeshakeCheck.elm`). +- **Type annotations** — Every top-level function must have a type annotation. +- **No partial functions** — Never use `List.head`, `Maybe.withDefault` without documenting why, or any function that can crash. Use pattern matching instead. + +## Changesets + +Every pull request that changes a publishable package must include a changeset: + +```bash +pnpm changeset +``` + +Follow the prompts to select the affected packages and the semver bump level. Write a concise description of what changed and why. + +Look at existing code in the repository before writing new code. The patterns directory (`.patterns/`) contains additional examples. diff --git a/packages/docs-site/content/contributing/release-process.md b/packages/docs-site/content/contributing/release-process.md new file mode 100644 index 0000000..31c0d48 --- /dev/null +++ b/packages/docs-site/content/contributing/release-process.md @@ -0,0 +1,119 @@ +--- +title: 'Release Process' +description: 'How to version and publish wolfcola-devtools packages' +section: contributing +order: 4 +--- + +# Release Process + +The wolfcola-devtools monorepo uses [Changesets](https://github.com/changesets/changesets) for versioning and publishing packages to npm. + +## Overview + +The release workflow is: + +1. Contributors add changesets to their pull requests +2. A "Version Packages" PR is automatically maintained by the Changesets GitHub Action +3. When the Version Packages PR is merged, packages are published to npm + +## Adding a Changeset + +Every pull request that modifies a publishable package must include a changeset. Run: + +```bash +pnpm changeset +``` + +The interactive prompt will ask you to: + +1. **Select packages** — choose which packages are affected by your change +2. **Select bump type** — `patch` for bug fixes, `minor` for new features, `major` for breaking changes +3. **Write a summary** — describe what changed and why + +This creates a Markdown file in `.changeset/` with the change metadata: + +```markdown +--- +'@wolfcola/treeshake-check': patch +--- + +Fixed false positive when checking packages with conditional exports. +``` + +### Multiple Packages + +If your change affects multiple packages, select all of them in the prompt. Each can have a different bump level: + +```markdown +--- +'@wolfcola/devtools-types': minor +'@wolfcola/devtools-bridge': patch +--- + +Added new FlowError type to devtools-types. Updated devtools-bridge to use it. +``` + +### Changeset Guidelines + +- Write in imperative mood ("Add feature" not "Added feature") +- Include context about why the change was made, not just what +- If the change is a breaking change, describe the migration path +- One changeset per logical change — split unrelated changes into separate PRs + +## Version Packages PR + +The Changesets GitHub Action runs on every push to `main`. It: + +1. Collects all changeset files in `.changeset/` +2. Calculates the new version for each affected package +3. Updates `package.json` versions and `CHANGELOG.md` files +4. Opens (or updates) a "Version Packages" pull request + +The Version Packages PR shows all pending changes and the resulting version bumps. Review it to make sure the versions look correct. + +## Publishing + +When the Version Packages PR is merged: + +1. The CI workflow detects the version bump commits +2. It runs the full test suite +3. It builds all affected packages +4. It publishes updated packages to npm with `pnpm publish -r` + +### Manual Publishing + +In rare cases, you may need to publish manually: + +```bash +pnpm changeset version # Apply version bumps +pnpm install # Update lockfile +pnpm -r build # Build all packages +pnpm changeset publish # Publish to npm +``` + +Manual publishing should be a last resort. Always prefer the automated workflow via the Version Packages PR. + +## Pre-releases + +For testing unreleased versions: + +```bash +pnpm changeset pre enter next +pnpm changeset version +pnpm changeset publish +``` + +This publishes packages with a `next` dist-tag (e.g. `1.2.0-next.0`) so they do not affect the `latest` tag. + +To exit pre-release mode: + +```bash +pnpm changeset pre exit +``` + +## Version Policy + +- **Patch** (`0.0.x`) — Bug fixes, documentation updates, internal refactors +- **Minor** (`0.x.0`) — New features, new exports, new optional fields in schemas +- **Major** (`x.0.0`) — Breaking changes to public APIs, removed exports, changed schema shapes diff --git a/packages/docs-site/content/contributing/repository-structure.md b/packages/docs-site/content/contributing/repository-structure.md new file mode 100644 index 0000000..207dae0 --- /dev/null +++ b/packages/docs-site/content/contributing/repository-structure.md @@ -0,0 +1,80 @@ +--- +title: 'Repository Structure' +description: 'How the wolfcola-devtools monorepo is organized' +section: contributing +order: 2 +--- + +# Repository Structure + +The wolfcola-devtools repository is a pnpm workspace monorepo. All publishable packages live under the `packages/` directory, and shared tooling lives at the repository root. + +## Package Map + +| Package | Directory | Description | +| ----------------------------------- | ---------------------------------- | --------------------------------------------------------- | +| `@wolfcola/treeshake-check` | `packages/treeshake-check` | CLI and library to verify tree-shaking support via Rollup | +| `@wolfcola/eslint-plugin-treeshake` | `packages/eslint-plugin-treeshake` | ESLint plugin that flags patterns breaking tree-shaking | +| `@wolfcola/devtools-bridge` | `packages/devtools-bridge` | SDK adapter for emitting AuthEvent objects to DevTools | +| `@wolfcola/devtools-types` | `packages/devtools-types` | Effect Schema definitions for AuthEvent and FlowState | +| `@wolfcola/devtools-extension` | `packages/devtools-extension` | Browser extension for Chrome and Firefox | +| `@wolfcola/devtools-vscode` | `packages/devtools-vscode` | VS Code extension with CDP connection | +| `@wolfcola/docs-site` | `packages/docs-site` | This documentation site (elm-pages) | + +## Root Files + +| File | Purpose | +| --------------------- | -------------------------------------------------- | +| `flake.nix` | Nix flake providing the development shell | +| `pnpm-workspace.yaml` | Defines the workspace packages | +| `package.json` | Root scripts for build, test, and lint | +| `tsconfig.json` | Base TypeScript configuration extended by packages | +| `.changeset/` | Changeset configuration and pending changesets | +| `CLAUDE.md` | Agent instructions for Claude Code | +| `CONTEXT.md` | Domain context for AI assistants | +| `docs/adr/` | Architecture Decision Records | + +## Build System + +The monorepo uses the following tools: + +- **pnpm** — Package manager with workspace support. All packages share a single lockfile. +- **TypeScript** — Used by all packages except the docs site. Each package has its own `tsconfig.json` that extends the root config. +- **Vitest** — Test runner for all TypeScript packages. Tests use `@effect/vitest` helpers. +- **Elm** — Used by the docs site. Compiled by elm-pages during the site build. +- **Nix** — Optional but recommended. The `flake.nix` provides Node.js, pnpm, Elm, and lefthook. +- **Lefthook** — Git hooks for pre-commit linting and pre-push testing. +- **Changesets** — Versioning and changelog generation for all publishable packages. + +## Dependency Graph + +The packages have the following dependency relationships: + +``` +devtools-extension + └── devtools-types + +devtools-bridge + └── devtools-types + +devtools-vscode + └── devtools-types + +treeshake-check + (standalone) + +eslint-plugin-treeshake + (standalone) +``` + +The `devtools-types` package is the shared foundation. It defines the `AuthEvent` and `FlowState` schemas that the bridge, browser extension, and VS Code extension all depend on. + +## Adding a New Package + +1. Create a directory under `packages/` +2. Add a `package.json` with the `@wolfcola` scope +3. Add the package to `pnpm-workspace.yaml` if not using a glob pattern +4. Add a `tsconfig.json` extending the root config +5. Add the package to the sidebar in `packages/docs-site/app/Shared.elm` +6. Create a content page in `packages/docs-site/content/packages/` +7. Create an API docs module in `packages/docs-site/src/ApiDocs/` diff --git a/packages/docs-site/content/docs/davinci-integration.md b/packages/docs-site/content/docs/davinci-integration.md new file mode 100644 index 0000000..16f4b30 --- /dev/null +++ b/packages/docs-site/content/docs/davinci-integration.md @@ -0,0 +1,101 @@ +--- +title: 'DaVinci Integration' +description: 'Integrate wolfcola devtools with ForgeRock DaVinci flows' +section: guides +order: 5 +--- + +# DaVinci Integration + +ForgeRock DaVinci is an orchestration platform for identity flows. The wolfcola devtools suite provides first-class support for instrumenting DaVinci flows through the `@wolfcola/devtools-bridge` package. + +## Overview + +DaVinci flows consist of a series of nodes that the user progresses through. Each node may involve user interaction (login forms, MFA challenges), server-side decisions (risk evaluation, policy checks), or external service calls (social login providers, identity verification). + +The devtools bridge captures every node transition as an `AuthEvent` and the overall flow progress as a `FlowState`, giving you full visibility into what is happening during authentication. + +## Setup + +### Install the Bridge + +```bash +npm install @wolfcola/devtools-bridge +``` + +### Create a DaVinci Bridge + +```typescript +import { createBridge } from '@wolfcola/devtools-bridge'; +import { davinci } from '@wolfcola/devtools-bridge/adapters/davinci'; + +// Pass your DaVinci client instance to the adapter +const bridge = createBridge(davinci(daVinciClient)); +``` + +The `davinci` adapter hooks into the DaVinci SDK's event system and translates its internal events into the standard `AuthEvent` schema that the devtools panel understands. + +## What Gets Captured + +With the DaVinci adapter active, the bridge emits events for: + +- **Flow start** — when `daVinciClient.start()` is called +- **Node transitions** — each time the flow advances to a new node +- **User submissions** — form data submitted at each node (passwords are redacted) +- **Collector callbacks** — interactions with individual collectors within a node +- **Errors** — authentication failures, network errors, timeout errors +- **Flow completion** — successful authentication with token issuance + +## Flow State Tracking + +The bridge also maintains a `FlowState` object that represents the current position in the DaVinci flow: + +```typescript +// FlowState is updated automatically +// Access it from the DevTools panel or programmatically: +bridge.getFlowState(); +// => { step: "username-password", tokens: null, error: null } +``` + +The DevTools extension visualizes this as a node graph in the Flow view, where each step is a node and transitions are edges. + +## Advanced Configuration + +### Filtering Events + +You can filter which events are emitted to the devtools panel: + +```typescript +const bridge = createBridge(davinci(daVinciClient), { + filter: (event) => event.type !== 'collector_callback', +}); +``` + +### Custom Metadata + +Attach custom metadata to every event for debugging: + +```typescript +const bridge = createBridge(davinci(daVinciClient), { + metadata: { + environment: 'staging', + flowId: 'login-v2', + }, +}); +``` + +### Cleanup + +When the component unmounts or the flow completes, destroy the bridge to stop event capture and release resources: + +```typescript +bridge.destroy(); +``` + +Always call `bridge.destroy()` when you are done. Failing to do so may cause memory leaks from lingering event listeners. + +## Troubleshooting + +- **No events appearing** — Verify that the DaVinci SDK version is compatible. The adapter supports `@forgerock/davinci-client` v1.x and v2.x. +- **Missing node transitions** — Some custom DaVinci nodes may not emit standard events. Contact the node author to ensure compatibility. +- **Redacted fields showing as empty** — The bridge redacts sensitive fields by default. To see raw values during development, pass `redact: false` in the bridge options (never do this in production). diff --git a/packages/docs-site/content/docs/devtools-extension.md b/packages/docs-site/content/docs/devtools-extension.md new file mode 100644 index 0000000..cd653ee --- /dev/null +++ b/packages/docs-site/content/docs/devtools-extension.md @@ -0,0 +1,80 @@ +--- +title: 'DevTools Extension' +description: 'Use the OIDC DevTools browser extension for Chrome and Firefox' +section: guides +order: 2 +--- + +# DevTools Extension + +The wolfcola DevTools browser extension adds a dedicated panel to your browser's developer tools for inspecting OIDC authentication flows in real time. + +## Installation + +### Chrome + +Install from the [Chrome Web Store](https://chrome.google.com/webstore). Search for "wolfcola devtools" or follow the direct link from the repository README. + +### Firefox + +Install from [Firefox Add-ons](https://addons.mozilla.org). The extension supports Firefox 115+ (ESR) and all modern Firefox releases. + +### Manual Installation (Development) + +For local development or testing unreleased builds: + +```bash +git clone https://github.com/ryanbas21/devtools.git +cd devtools +pnpm install +pnpm --filter @wolfcola/devtools-extension build +``` + +Then load the unpacked extension from `packages/devtools-extension/dist/` in your browser's extension management page. + +## Using the DevTools Panel + +Once installed, open your browser DevTools (F12 or Cmd+Opt+I) and look for the **OIDC DevTools** tab. The panel activates automatically when the inspected page includes the `@wolfcola/devtools-bridge` SDK. + +## Views + +The extension provides three views, accessible via tabs at the top of the panel. + +### Timeline + +The Timeline view shows a chronological list of every `AuthEvent` emitted by the bridge. Each event displays: + +- **Event type** (e.g. `authorize`, `token_exchange`, `refresh`) +- **Timestamp** relative to the page load +- **Payload** expandable JSON tree with the full event data + +Use the filter bar at the top to narrow events by type or search within payloads. + +### Flow + +The Flow view renders the current OIDC flow as a state diagram. Each node represents a `FlowState`, and edges represent the transitions triggered by `AuthEvent` objects. This is especially useful for visualizing complex DaVinci orchestration flows that involve multiple steps and decision nodes. + +Color coding indicates the state of each node: + +- **Blue** — completed successfully +- **Yellow** — in progress +- **Red** — errored + +### Learn + +The Learn view provides contextual documentation for the OIDC concepts involved in the current flow. When you select an event in the Timeline or a node in the Flow view, the Learn panel shows: + +- What the event or state represents in the OIDC spec +- Common issues and troubleshooting tips +- Links to the relevant RFC sections + +The Learn view content is bundled with the extension and works offline. + +## Troubleshooting + +If the OIDC DevTools tab does not appear: + +1. Make sure the extension is enabled in your browser's extension manager +2. Verify that the inspected page includes the `@wolfcola/devtools-bridge` package +3. Close and reopen DevTools — the panel registers on DevTools initialization +4. Check the browser console for errors from the extension background script diff --git a/packages/docs-site/content/docs/tree-shaking.md b/packages/docs-site/content/docs/tree-shaking.md new file mode 100644 index 0000000..df3e3c6 --- /dev/null +++ b/packages/docs-site/content/docs/tree-shaking.md @@ -0,0 +1,125 @@ +--- +title: 'Tree-Shaking Your Packages' +description: 'Verify and improve tree-shaking support in your npm packages' +section: guides +order: 4 +--- + +# Tree-Shaking Your Packages + +Tree-shaking is the process by which bundlers like Rollup, Webpack, and esbuild eliminate unused code from your final bundle. The wolfcola devtools suite includes two packages to help you verify and enforce tree-shaking support: `@wolfcola/treeshake-check` (a CLI and library) and `@wolfcola/eslint-plugin-treeshake` (a linting plugin). + +## Why Tree-Shaking Matters + +If your package is not tree-shakeable, consumers pay the cost of importing your entire library even when they only use a single function. This directly impacts: + +- **Bundle size** — larger downloads for end users +- **Parse time** — browsers must parse unused code +- **Memory usage** — unused modules still occupy memory in some runtimes + +## Using treeshake-check + +The `treeshake-check` CLI verifies that each export from your package produces a minimal bundle when imported individually through Rollup. + +### Installation + +```bash +npm install -D @wolfcola/treeshake-check +``` + +### Running the Check + +```bash +npx treeshake-check your-package-name +``` + +The tool will: + +1. Discover all named exports from your package +2. Create a minimal Rollup bundle importing each export individually +3. Compare the bundle size against a threshold +4. Report which exports are tree-shakeable and which are not + +### CI Integration + +Add a check to your CI pipeline to catch tree-shaking regressions: + +```json +{ + "scripts": { + "check:treeshake": "treeshake-check your-package-name" + } +} +``` + +The CLI exits with code 1 if any export fails the tree-shaking check. + +## Using eslint-plugin-treeshake + +The ESLint plugin catches common patterns that break tree-shaking at the source level, before you even publish. + +### Installation + +```bash +npm install -D @wolfcola/eslint-plugin-treeshake +``` + +### Configuration + +With ESLint flat config: + +```javascript +import treeshake from '@wolfcola/eslint-plugin-treeshake'; + +export default [treeshake.configs.recommended]; +``` + +### Rules + +The plugin includes rules that flag: + +- **Top-level side effects** — code that runs at module evaluation time prevents the export from being eliminated +- **Mutable module-scope variables** — `let` or `var` at module scope that are read by exported functions create implicit dependencies +- **Class static initializers with side effects** — static blocks or property initializers that call external functions +- **Barrel file anti-patterns** — re-export-all patterns (`export * from`) that defeat bundler analysis + +The `treeshake-check` CLI and `eslint-plugin-treeshake` complement each other. The CLI tests your built output; the ESLint plugin catches problems in source code. + +## Common Fixes + +### Avoid Top-Level Side Effects + +```typescript +// Bad - side effect at module scope +const registry = new Map(); +registry.set('default', createDefault()); + +// Good - lazy initialization +const getRegistry = () => { + const registry = new Map(); + registry.set('default', createDefault()); + return registry; +}; +``` + +### Use Named Exports + +```typescript +// Bad - default export of object +export default { foo, bar, baz }; + +// Good - named exports +export { foo, bar, baz }; +``` + +### Mark Packages as Side-Effect-Free + +Add to your `package.json`: + +```json +{ + "sideEffects": false +} +``` + +This tells bundlers that any unused import from your package can be safely removed. diff --git a/packages/docs-site/content/docs/vscode-extension.md b/packages/docs-site/content/docs/vscode-extension.md new file mode 100644 index 0000000..7bd7fde --- /dev/null +++ b/packages/docs-site/content/docs/vscode-extension.md @@ -0,0 +1,86 @@ +--- +title: 'VS Code Extension' +description: 'Use the OIDC DevTools VS Code extension with CDP support' +section: guides +order: 3 +--- + +# VS Code Extension + +The wolfcola DevTools VS Code extension brings OIDC flow inspection directly into your editor. It connects to a running browser via the Chrome DevTools Protocol (CDP) and streams `AuthEvent` data into a VS Code panel. + +## Installation + +Install from the VS Code Marketplace: + +1. Open VS Code +2. Go to Extensions (Ctrl+Shift+X / Cmd+Shift+X) +3. Search for "wolfcola devtools" +4. Click Install + +Alternatively, install from the command line: + +```bash +code --install-extension wolfcola.devtools-vscode +``` + +## CDP WebSocket Connection + +The extension requires a WebSocket connection to a browser running with remote debugging enabled. + +### Starting Chrome with CDP + +```bash +google-chrome --remote-debugging-port=9222 +``` + +### Starting Edge with CDP + +```bash +msedge --remote-debugging-port=9222 +``` + +### Configuring the Extension + +Open VS Code settings and set the CDP endpoint: + +```json +{ + "wolfcola.devtools.cdpEndpoint": "ws://localhost:9222" +} +``` + +The extension will auto-discover available pages and connect to the first one that has the `@wolfcola/devtools-bridge` SDK active. + +## Features + +### Live Event Stream + +The extension sidebar shows a live feed of `AuthEvent` objects as they are emitted. Events are color-coded by type and can be expanded to view the full JSON payload. + +### Flow State Visualization + +A webview panel renders the current `FlowState` as an interactive diagram, similar to the browser extension's Flow view. You can pan, zoom, and click nodes to inspect their data. + +### CodeLens Integration + +When the extension detects that your workspace contains `@wolfcola/devtools-bridge` import statements, it adds CodeLens annotations above `createBridge()` calls showing the connection status and last event received. + +### Diagnostics + +The extension reports issues as VS Code diagnostics: + +- Missing or misconfigured bridge initialization +- Schema validation failures on captured events +- Connection drops or CDP endpoint issues + +The CDP connection requires the browser to be started with the `--remote-debugging-port` flag. Without it, the extension cannot connect. + +## Commands + +The extension contributes the following commands to the Command Palette: + +- **OIDC DevTools: Connect** — Connect to the configured CDP endpoint +- **OIDC DevTools: Disconnect** — Close the CDP connection +- **OIDC DevTools: Show Flow** — Open the flow visualization panel +- **OIDC DevTools: Clear Events** — Clear the event stream diff --git a/packages/docs-site/content/packages/devtools-bridge.md b/packages/docs-site/content/packages/devtools-bridge.md new file mode 100644 index 0000000..e92da5c --- /dev/null +++ b/packages/docs-site/content/packages/devtools-bridge.md @@ -0,0 +1,108 @@ +--- +title: '@wolfcola/devtools-bridge' +description: 'SDK adapter for emitting events from OIDC clients' +section: packages +order: 3 +--- + +# @wolfcola/devtools-bridge + +The devtools bridge is a lightweight SDK adapter that connects your OIDC client to the wolfcola DevTools panel. It captures authentication events and flow state, then forwards them to the browser extension or VS Code extension for inspection. + +## Installation + +```bash +npm install @wolfcola/devtools-bridge +``` + +## Quick Start + +```typescript +import { createBridge } from '@wolfcola/devtools-bridge'; +import { davinci } from '@wolfcola/devtools-bridge/adapters/davinci'; + +const bridge = createBridge(davinci(myDaVinciClient)); + +// Events are now forwarded to the DevTools panel automatically. +// When done: +bridge.destroy(); +``` + +## Adapters + +Adapters translate SDK-specific events into the standard `AuthEvent` schema. Each adapter is a separate entry point so unused adapters are tree-shaken from your bundle. + +### DaVinci Adapter + +```typescript +import { davinci } from '@wolfcola/devtools-bridge/adapters/davinci'; + +const bridge = createBridge(davinci(daVinciClient)); +``` + +Supports `@forgerock/davinci-client` v1.x and v2.x. Captures node transitions, collector callbacks, and flow completion. + +### Journey Adapter + +```typescript +import { journey } from '@wolfcola/devtools-bridge/adapters/journey'; + +const bridge = createBridge(journey(journeyConfig)); +``` + +Supports ForgeRock Journey/Tree-based authentication. Captures callbacks, step transitions, and session token issuance. + +### Generic OIDC Adapter + +```typescript +import { oidc } from '@wolfcola/devtools-bridge/adapters/oidc'; + +const bridge = createBridge( + oidc({ + onAuthorize: (cb) => myClient.on('authorize', cb), + onToken: (cb) => myClient.on('token', cb), + onError: (cb) => myClient.on('error', cb), + }), +); +``` + +Use the generic adapter when your OIDC client does not have a dedicated adapter. You provide hook functions that the bridge calls to subscribe to events. + +## Bridge API + +### `createBridge(adapter, options?)` + +Creates a new bridge instance. + +**Options:** + +| Option | Type | Default | Description | +| ------------ | ------------------------------- | ------------ | ------------------------------------------------------- | +| `filter` | `(event: AuthEvent) => boolean` | `() => true` | Filter which events are emitted | +| `metadata` | `Record` | `{}` | Custom metadata attached to every event | +| `redact` | `boolean` | `true` | Redact sensitive fields like passwords and tokens | +| `bufferSize` | `number` | `1000` | Maximum number of events to keep in the internal buffer | + +### `bridge.emit(event)` + +Manually emit an `AuthEvent`. Useful for custom events that the adapter does not capture automatically. + +### `bridge.destroy()` + +Disconnect from the OIDC client and stop emitting events. Always call this when the bridge is no longer needed to prevent memory leaks. + +### `bridge.getFlowState()` + +Returns the current `FlowState` object, which represents the latest known position in the authentication flow. + +The bridge uses `postMessage` to communicate with the browser extension. It has zero runtime dependencies beyond `@wolfcola/devtools-types`. + +## Bundle Impact + +The bridge is designed to be lightweight. When using a specific adapter, only that adapter's code is included in your bundle: + +| Import | Minified + gzipped | +| -------------------------- | ------------------ | +| `createBridge` + `davinci` | ~1.2 kB | +| `createBridge` + `journey` | ~1.0 kB | +| `createBridge` + `oidc` | ~0.8 kB | diff --git a/packages/docs-site/content/packages/devtools-types.md b/packages/docs-site/content/packages/devtools-types.md new file mode 100644 index 0000000..5add38d --- /dev/null +++ b/packages/docs-site/content/packages/devtools-types.md @@ -0,0 +1,128 @@ +--- +title: '@wolfcola/devtools-types' +description: 'Effect Schema definitions for AuthEvent and FlowState' +section: packages +order: 4 +--- + +# @wolfcola/devtools-types + +This package provides the shared type definitions and runtime validators for the wolfcola devtools ecosystem. All types are defined using [Effect Schema](https://effect.website/docs/schema/introduction), giving you both TypeScript types and runtime validation in a single definition. + +## Installation + +```bash +npm install @wolfcola/devtools-types +``` + +This package has `effect` as a peer dependency. Make sure Effect is installed in your project: + +```bash +npm install effect +``` + +## Key Types + +### AuthEvent + +Represents a single authentication event captured during an OIDC flow. + +```typescript +import { Schema } from 'effect'; + +const AuthEvent = Schema.TaggedStruct('AuthEvent', { + type: Schema.String, + timestamp: Schema.Number, + data: Schema.Unknown, +}); +``` + +The `type` field identifies the kind of event (e.g. `"authorize"`, `"token_exchange"`, `"refresh"`, `"error"`). The `timestamp` is milliseconds since the epoch. The `data` field contains event-specific payload. + +### FlowState + +Represents the state of an OIDC authentication flow at a point in time. + +```typescript +const FlowState = Schema.TaggedStruct('FlowState', { + step: Schema.String, + tokens: Schema.NullOr(TokenSet), + error: Schema.NullOr(FlowError), +}); +``` + +A `FlowState` tracks which step the user is on, whether tokens have been issued, and whether an error has occurred. + +### TokenSet + +```typescript +const TokenSet = Schema.Struct({ + accessToken: Schema.String, + idToken: Schema.OptionFromNullishOr(Schema.String), + refreshToken: Schema.OptionFromNullishOr(Schema.String), + expiresAt: Schema.Number, +}); +``` + +### FlowError + +```typescript +const FlowError = Schema.Struct({ + code: Schema.String, + message: Schema.String, + details: Schema.optional(Schema.Unknown), +}); +``` + +## Usage with Effect + +### Decoding Events + +Use the provided decode functions to validate raw data at runtime: + +```typescript +import { Effect } from 'effect'; +import { decodeAuthEvent } from '@wolfcola/devtools-types'; + +const program = Effect.gen(function* () { + const rawData = yield* getEventFromExtension(); + const event = yield* decodeAuthEvent(rawData); + + // event is now fully typed as AuthEvent + console.log(event.type, event.timestamp); +}); +``` + +### Encoding Events + +Encode typed events back to plain objects for serialization: + +```typescript +import { encodeAuthEvent } from '@wolfcola/devtools-types'; + +const plain = yield * encodeAuthEvent(event); +// plain is a plain JavaScript object safe to send over postMessage +``` + +### Custom Event Types + +Extend the base `AuthEvent` schema for domain-specific events: + +```typescript +import { Schema } from 'effect'; +import { AuthEvent } from '@wolfcola/devtools-types'; + +const DaVinciEvent = Schema.extend( + AuthEvent, + Schema.Struct({ + nodeId: Schema.String, + flowId: Schema.String, + }), +); +``` + +This package uses `Schema.TaggedStruct` for discriminated unions. Make sure you are on Effect 3.10 or later, which includes the tagged struct API. + +## Versioning + +The schema definitions follow semantic versioning. Breaking changes to the shape of `AuthEvent` or `FlowState` will result in a major version bump. Additive changes (new optional fields) are minor versions. diff --git a/packages/docs-site/content/packages/eslint-plugin-treeshake.md b/packages/docs-site/content/packages/eslint-plugin-treeshake.md new file mode 100644 index 0000000..cd5a855 --- /dev/null +++ b/packages/docs-site/content/packages/eslint-plugin-treeshake.md @@ -0,0 +1,116 @@ +--- +title: '@wolfcola/eslint-plugin-treeshake' +description: 'ESLint plugin that flags tree-breaking patterns' +section: packages +order: 2 +--- + +# @wolfcola/eslint-plugin-treeshake + +An ESLint plugin that statically analyzes your source code for patterns known to break tree-shaking in JavaScript bundlers. + +## Installation + +```bash +npm install -D @wolfcola/eslint-plugin-treeshake +``` + +## Configuration + +### ESLint Flat Config (recommended) + +```javascript +import treeshake from '@wolfcola/eslint-plugin-treeshake'; + +export default [treeshake.configs.recommended]; +``` + +### Legacy `.eslintrc` + +```json +{ + "plugins": ["@wolfcola/treeshake"], + "extends": ["plugin:@wolfcola/treeshake/recommended"] +} +``` + +## Rules + +### `treeshake/no-top-level-side-effects` + +Disallows expressions at module scope that produce side effects. Side effects at the top level prevent bundlers from removing the module even when none of its exports are used. + +**Bad:** + +```typescript +// This runs when the module is imported, even if nothing is used +console.log('module loaded'); +const el = document.createElement('div'); +``` + +**Good:** + +```typescript +// Side effects are deferred to function calls +export const init = () => { + console.log('module loaded'); + const el = document.createElement('div'); + return el; +}; +``` + +### `treeshake/no-mutable-module-scope` + +Flags `let` and `var` declarations at module scope that are read by exported functions. Mutable bindings create implicit dependencies that bundlers cannot safely eliminate. + +**Bad:** + +```typescript +let count = 0; +export const increment = () => ++count; +``` + +**Good:** + +```typescript +export const createCounter = () => { + let count = 0; + return { increment: () => ++count }; +}; +``` + +### `treeshake/no-export-star` + +Warns against `export * from "..."` patterns. Barrel files that re-export everything from sub-modules make it difficult for bundlers to determine which exports are actually used. + +**Bad:** + +```typescript +export * from './utils'; +export * from './helpers'; +``` + +**Good:** + +```typescript +export { formatDate, parseDate } from './utils'; +export { capitalize } from './helpers'; +``` + +### `treeshake/no-class-side-effects` + +Detects class declarations with static initializers that call external functions. Static blocks and property initializers run at class definition time, creating side effects. + +All rules are enabled by the `recommended` config preset. You can disable individual rules in your ESLint config if needed. + +## Programmatic API + +The plugin exports its rules for use in custom ESLint configurations: + +```javascript +import treeshake from '@wolfcola/eslint-plugin-treeshake'; + +// Access individual rules +treeshake.rules['no-top-level-side-effects']; +treeshake.rules['no-mutable-module-scope']; +``` From 6ccfcbcd4384c36dd70a59a65f91af6128525ebe Mon Sep 17 00:00:00 2001 From: Ryan Bas Date: Tue, 12 May 2026 00:01:01 -0600 Subject: [PATCH 13/23] feat(docs-site): add API reference docs for all published packages Co-Authored-By: Claude Opus 4.6 (1M context) --- .../app/Route/Api/Package_/ModuleName_.elm | 6 ++++ .../docs-site/src/ApiDocs/DevtoolsBridge.elm | 24 ++++++++++++++++ .../docs-site/src/ApiDocs/DevtoolsTypes.elm | 28 +++++++++++++++++++ .../src/ApiDocs/EslintPluginTreeshake.elm | 19 +++++++++++++ 4 files changed, 77 insertions(+) create mode 100644 packages/docs-site/src/ApiDocs/DevtoolsBridge.elm create mode 100644 packages/docs-site/src/ApiDocs/DevtoolsTypes.elm create mode 100644 packages/docs-site/src/ApiDocs/EslintPluginTreeshake.elm diff --git a/packages/docs-site/app/Route/Api/Package_/ModuleName_.elm b/packages/docs-site/app/Route/Api/Package_/ModuleName_.elm index 28da43a..ae4644e 100644 --- a/packages/docs-site/app/Route/Api/Package_/ModuleName_.elm +++ b/packages/docs-site/app/Route/Api/Package_/ModuleName_.elm @@ -1,5 +1,8 @@ module Route.Api.Package_.ModuleName_ exposing (ActionData, Data, Model, Msg, route) +import ApiDocs.DevtoolsBridge +import ApiDocs.DevtoolsTypes +import ApiDocs.EslintPluginTreeshake import ApiDocs.TreeshakeCheck import ApiDocs.Types exposing (ApiModule, FunctionDoc, TypeDoc) import BackendTask exposing (BackendTask) @@ -51,6 +54,9 @@ route = allModules : List ( String, String, ApiModule ) allModules = List.map (\m -> ( "treeshake-check", m.name, m )) ApiDocs.TreeshakeCheck.modules + ++ List.map (\m -> ( "eslint-plugin-treeshake", m.name, m )) ApiDocs.EslintPluginTreeshake.modules + ++ List.map (\m -> ( "devtools-bridge", m.name, m )) ApiDocs.DevtoolsBridge.modules + ++ List.map (\m -> ( "devtools-types", m.name, m )) ApiDocs.DevtoolsTypes.modules pages : BackendTask FatalError (List RouteParams) diff --git a/packages/docs-site/src/ApiDocs/DevtoolsBridge.elm b/packages/docs-site/src/ApiDocs/DevtoolsBridge.elm new file mode 100644 index 0000000..70a4556 --- /dev/null +++ b/packages/docs-site/src/ApiDocs/DevtoolsBridge.elm @@ -0,0 +1,24 @@ +module ApiDocs.DevtoolsBridge exposing (modules) + +import ApiDocs.Types exposing (ApiModule) + + +modules : List ApiModule +modules = + [ { name = "devtools-bridge" + , description = "SDK adapter for emitting events from DaVinci, Journey, OIDC clients" + , types = + [ { name = "Bridge" + , signature = "type Bridge = { emit: (event: AuthEvent) => void; destroy: () => void }" + , description = "A bridge instance that emits AuthEvent objects to the DevTools panel." + } + ] + , functions = + [ { name = "createBridge" + , signature = "(adapter: Adapter) => Bridge" + , description = "Create a bridge instance from an SDK adapter." + , example = Just "import { createBridge } from \"@wolfcola/devtools-bridge\"\nimport { davinci } from \"@wolfcola/devtools-bridge/adapters/davinci\"\n\nconst bridge = createBridge(davinci(client))" + } + ] + } + ] diff --git a/packages/docs-site/src/ApiDocs/DevtoolsTypes.elm b/packages/docs-site/src/ApiDocs/DevtoolsTypes.elm new file mode 100644 index 0000000..f80fd90 --- /dev/null +++ b/packages/docs-site/src/ApiDocs/DevtoolsTypes.elm @@ -0,0 +1,28 @@ +module ApiDocs.DevtoolsTypes exposing (modules) + +import ApiDocs.Types exposing (ApiModule) + + +modules : List ApiModule +modules = + [ { name = "devtools-types" + , description = "Effect Schema definitions for AuthEvent and FlowState" + , types = + [ { name = "AuthEvent" + , signature = "Schema.TaggedStruct<\"AuthEvent\", { type: string; timestamp: number; data: unknown }>" + , description = "Represents a single authentication event captured during an OIDC flow." + } + , { name = "FlowState" + , signature = "Schema.TaggedStruct<\"FlowState\", { step: string; tokens: TokenSet | null; error: FlowError | null }>" + , description = "Represents the state of an OIDC authentication flow at a point in time." + } + ] + , functions = + [ { name = "decodeAuthEvent" + , signature = "(input: unknown) => Effect.Effect" + , description = "Decode and validate raw data into a typed AuthEvent using Effect Schema." + , example = Just "import { decodeAuthEvent } from \"@wolfcola/devtools-types\"\n\nconst event = yield* decodeAuthEvent(rawData)" + } + ] + } + ] diff --git a/packages/docs-site/src/ApiDocs/EslintPluginTreeshake.elm b/packages/docs-site/src/ApiDocs/EslintPluginTreeshake.elm new file mode 100644 index 0000000..2954e73 --- /dev/null +++ b/packages/docs-site/src/ApiDocs/EslintPluginTreeshake.elm @@ -0,0 +1,19 @@ +module ApiDocs.EslintPluginTreeshake exposing (modules) + +import ApiDocs.Types exposing (ApiModule) + + +modules : List ApiModule +modules = + [ { name = "eslint-plugin-treeshake" + , description = "ESLint plugin that flags tree-breaking patterns" + , types = [] + , functions = + [ { name = "configs.recommended" + , signature = "Linter.FlatConfig" + , description = "Recommended flat config preset that enables all tree-shaking rules." + , example = Just "import treeshake from \"@wolfcola/eslint-plugin-treeshake\"\n\nexport default [treeshake.configs.recommended]" + } + ] + } + ] From d3227c636b1ca8e1a56a958a5113f7cdefa804e5 Mon Sep 17 00:00:00 2001 From: Ryan Bas Date: Tue, 12 May 2026 00:04:33 -0600 Subject: [PATCH 14/23] feat(docs-site): add dark/light mode toggle with system preference detection Co-Authored-By: Claude Opus 4.6 (1M context) --- packages/docs-site/app/Shared.elm | 52 +++++++++++++++++++++++++++---- packages/docs-site/index.ts | 4 ++- packages/docs-site/style.css | 33 ++++++++++++++------ 3 files changed, 72 insertions(+), 17 deletions(-) diff --git a/packages/docs-site/app/Shared.elm b/packages/docs-site/app/Shared.elm index fcacbb3..54aac8d 100644 --- a/packages/docs-site/app/Shared.elm +++ b/packages/docs-site/app/Shared.elm @@ -6,7 +6,7 @@ import BackendTask.Glob as Glob import Effect exposing (Effect) import FatalError exposing (FatalError) import Html exposing (Html) -import Html.Attributes as Attr +import Html.Attributes as Attr exposing (attribute) import Html.Events import Json.Decode as Decode import Pages.Flags @@ -32,6 +32,7 @@ template = type Msg = SharedMsg SharedMsg | ToggleSidebar + | ToggleTheme | SearchInput String @@ -47,6 +48,7 @@ type SharedMsg type alias Model = { sidebarOpen : Bool , searchQuery : String + , darkMode : Bool } @@ -64,8 +66,21 @@ init : } -> ( Model, Effect Msg ) init flags maybePagePath = + let + darkMode = + case flags of + Pages.Flags.BrowserFlags value -> + Decode.decodeValue + (Decode.field "darkMode" Decode.bool) + value + |> Result.withDefault False + + Pages.Flags.PreRenderFlags -> + False + in ( { sidebarOpen = True , searchQuery = "" + , darkMode = darkMode } , Effect.none ) @@ -80,6 +95,9 @@ update msg model = ToggleSidebar -> ( { model | sidebarOpen = not model.sidebarOpen }, Effect.none ) + ToggleTheme -> + ( { model | darkMode = not model.darkMode }, Effect.none ) + SearchInput query -> ( { model | searchQuery = query }, Effect.none ) @@ -165,11 +183,21 @@ view : -> { body : List (Html msg), title : String } view sharedData page model toMsg pageView = { body = - [ viewHeader sharedData model toMsg - , Html.div [ Attr.class "layout" ] - [ viewSidebar model toMsg - , Html.main_ [ Attr.class "content" ] - pageView.body + [ Html.div + [ attribute "data-theme" + (if model.darkMode then + "dark" + + else + "light" + ) + ] + [ viewHeader sharedData model toMsg + , Html.div [ Attr.class "layout" ] + [ viewSidebar model toMsg + , Html.main_ [ Attr.class "content" ] + pageView.body + ] ] ] , title = pageView.title @@ -204,6 +232,18 @@ viewHeader sharedData model toMsg = , Html.a [ Attr.href "/architecture" ] [ Html.text "Architecture" ] , Html.a [ Attr.href "/contributing" ] [ Html.text "Contributing" ] ] + , Html.button + [ Attr.class "theme-toggle" + , Html.Events.onClick (toMsg ToggleTheme) + ] + [ Html.text + (if model.darkMode then + "Light" + + else + "Dark" + ) + ] ] diff --git a/packages/docs-site/index.ts b/packages/docs-site/index.ts index c191234..a7dc118 100644 --- a/packages/docs-site/index.ts +++ b/packages/docs-site/index.ts @@ -9,7 +9,9 @@ const config: ElmPagesInit = { console.log('App loaded', app); }, flags: function () { - return 'You can decode this in Shared.elm using Json.Decode.string!'; + return { + darkMode: window.matchMedia('(prefers-color-scheme: dark)').matches, + }; }, }; diff --git a/packages/docs-site/style.css b/packages/docs-site/style.css index c01ce8c..0988df9 100644 --- a/packages/docs-site/style.css +++ b/packages/docs-site/style.css @@ -13,16 +13,14 @@ --font-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, monospace; } -@media (prefers-color-scheme: dark) { - :root { - --bg: #0f172a; - --bg-sidebar: #1e293b; - --text: #e2e8f0; - --text-muted: #94a3b8; - --border: #334155; - --accent: #60a5fa; - --accent-hover: #93bbfd; - } +[data-theme='dark'] { + --bg: #0f172a; + --bg-sidebar: #1e293b; + --text: #e2e8f0; + --text-muted: #94a3b8; + --border: #334155; + --accent: #60a5fa; + --accent-hover: #93bbfd; } * { @@ -89,6 +87,21 @@ a:hover { font-size: 1rem; } +.theme-toggle { + background: none; + border: 1px solid var(--border); + border-radius: 4px; + padding: 0.25rem 0.6rem; + cursor: pointer; + color: var(--text-muted); + font-size: 0.8rem; + font-family: var(--font-sans); +} +.theme-toggle:hover { + color: var(--text); + border-color: var(--text-muted); +} + /* Layout */ .layout { display: flex; From a81157b6190714fd8647fb5f63180eac651d0309 Mon Sep 17 00:00:00 2001 From: Ryan Bas Date: Tue, 12 May 2026 00:05:46 -0600 Subject: [PATCH 15/23] feat(docs-site): add Prism.js syntax highlighting for code blocks Co-Authored-By: Claude Opus 4.6 (1M context) --- packages/docs-site/index.ts | 14 ++++++++++ packages/docs-site/package.json | 3 +++ packages/docs-site/style.css | 48 +++++++++++++++++++++++++++++++++ pnpm-lock.yaml | 36 +++++++++++++++++++++++++ 4 files changed, 101 insertions(+) diff --git a/packages/docs-site/index.ts b/packages/docs-site/index.ts index a7dc118..f568ed2 100644 --- a/packages/docs-site/index.ts +++ b/packages/docs-site/index.ts @@ -1,3 +1,11 @@ +import Prism from 'prismjs'; +import 'prismjs/components/prism-typescript'; +import 'prismjs/components/prism-bash'; +import 'prismjs/components/prism-json'; +import 'prismjs/components/prism-yaml'; +import 'prismjs/components/prism-elm'; +import 'prismjs/components/prism-javascript'; + type ElmPagesInit = { load: (elmLoaded: Promise) => Promise; flags: unknown; @@ -7,6 +15,12 @@ const config: ElmPagesInit = { load: async function (elmLoaded) { const app = await elmLoaded; console.log('App loaded', app); + + // Highlight code blocks after elm-pages renders each page + const observer = new MutationObserver(() => { + Prism.highlightAll(); + }); + observer.observe(document.body, { childList: true, subtree: true }); }, flags: function () { return { diff --git a/packages/docs-site/package.json b/packages/docs-site/package.json index 7e0ffe5..cf2de0b 100644 --- a/packages/docs-site/package.json +++ b/packages/docs-site/package.json @@ -12,5 +12,8 @@ "elm-pages": "3.5.1", "elm-review": "^2.13.5", "lamdera": "^0.19.1-1.4.0" + }, + "dependencies": { + "prismjs": "^1.30.0" } } diff --git a/packages/docs-site/style.css b/packages/docs-site/style.css index 0988df9..300aa9d 100644 --- a/packages/docs-site/style.css +++ b/packages/docs-site/style.css @@ -428,3 +428,51 @@ a:hover { border-radius: 8px; border: 1px solid var(--border); } + +/* Prism Syntax Highlighting */ +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: var(--text-muted); + font-style: italic; +} +.token.punctuation { + color: var(--text); +} +.token.property, +.token.tag, +.token.boolean, +.token.number, +.token.constant, +.token.symbol, +.token.deleted { + color: #e06c75; +} +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + color: #98c379; +} +.token.operator, +.token.entity, +.token.url { + color: #56b6c2; +} +.token.atrule, +.token.attr-value, +.token.keyword { + color: #c678dd; +} +.token.function, +.token.class-name { + color: #61afef; +} +.token.regex, +.token.important, +.token.variable { + color: #d19a66; +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 483eeaa..3f3ba57 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,6 +4,32 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +catalogs: + effect: + '@effect/cli': + specifier: ^0.75.1 + version: 0.75.1 + '@effect/platform': + specifier: ^0.96.0 + version: 0.96.1 + '@effect/platform-node': + specifier: ^0.106.0 + version: 0.106.0 + '@effect/vitest': + specifier: ^0.29.0 + version: 0.29.0 + effect: + specifier: ^3.21.1 + version: 3.21.2 + vite: + vite: + specifier: ^7.3.2 + version: 7.3.3 + vitest: + vitest: + specifier: ^3.2.0 + version: 3.2.4 + importers: .: @@ -146,6 +172,10 @@ importers: version: 5.47.1 packages/docs-site: + dependencies: + prismjs: + specifier: ^1.30.0 + version: 1.30.0 devDependencies: elm-codegen: specifier: ^0.6.3 @@ -3796,6 +3826,10 @@ packages: resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + prismjs@1.30.0: + resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} + engines: {node: '>=6'} + proc-log@6.1.0: resolution: {integrity: sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==} engines: {node: ^20.17.0 || >=22.9.0} @@ -8651,6 +8685,8 @@ snapshots: ansi-styles: 5.2.0 react-is: 17.0.2 + prismjs@1.30.0: {} + proc-log@6.1.0: {} prompts@2.4.2: From 2fd104161dc9e4c3f0f8cb021e1fe4bac46cfddf Mon Sep 17 00:00:00 2001 From: Ryan Bas Date: Tue, 12 May 2026 00:07:32 -0600 Subject: [PATCH 16/23] ci: add GitHub Pages deployment workflow for docs site Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/docs.yml | 49 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 .github/workflows/docs.yml diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..5c47f79 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,49 @@ +name: Deploy Docs + +on: + push: + branches: [main] + paths: + - 'packages/docs-site/**' + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: 'pages' + cancel-in-progress: false + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v4 + + - uses: actions/setup-node@v4 + with: + node-version: '22' + cache: 'pnpm' + + - run: pnpm install --frozen-lockfile + + - name: Build docs site + run: pnpm --filter docs-site build + + - uses: actions/upload-pages-artifact@v3 + with: + path: packages/docs-site/dist + + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 From be778d4179dbe223bd83c680e53d7ba9f0797d11 Mon Sep 17 00:00:00 2001 From: Ryan Bas Date: Tue, 12 May 2026 00:17:44 -0600 Subject: [PATCH 17/23] fix(docs-site): rename ForgeRock to Ping Identity, add Journey and OIDC integration guides Co-Authored-By: Claude Opus 4.6 (1M context) --- packages/docs-site/app/Shared.elm | 2 + .../content/docs/davinci-integration.md | 6 +- .../content/docs/journey-integration.md | 86 ++++++++++++++ .../content/docs/oidc-integration.md | 105 ++++++++++++++++++ .../content/packages/devtools-bridge.md | 4 +- 5 files changed, 198 insertions(+), 5 deletions(-) create mode 100644 packages/docs-site/content/docs/journey-integration.md create mode 100644 packages/docs-site/content/docs/oidc-integration.md diff --git a/packages/docs-site/app/Shared.elm b/packages/docs-site/app/Shared.elm index 54aac8d..bee2c5a 100644 --- a/packages/docs-site/app/Shared.elm +++ b/packages/docs-site/app/Shared.elm @@ -308,6 +308,8 @@ viewSidebar model toMsg = , ( "/docs/vscode-extension", "VS Code Extension" ) , ( "/docs/tree-shaking", "Tree-Shaking" ) , ( "/docs/davinci-integration", "DaVinci Integration" ) + , ( "/docs/journey-integration", "Journey Integration" ) + , ( "/docs/oidc-integration", "Generic OIDC Integration" ) ] , viewSidebarSection "Contributing" [ ( "/contributing/development-setup", "Development Setup" ) diff --git a/packages/docs-site/content/docs/davinci-integration.md b/packages/docs-site/content/docs/davinci-integration.md index 16f4b30..e10c649 100644 --- a/packages/docs-site/content/docs/davinci-integration.md +++ b/packages/docs-site/content/docs/davinci-integration.md @@ -1,13 +1,13 @@ --- title: 'DaVinci Integration' -description: 'Integrate wolfcola devtools with ForgeRock DaVinci flows' +description: 'Integrate wolfcola devtools with Ping Identity DaVinci flows' section: guides order: 5 --- # DaVinci Integration -ForgeRock DaVinci is an orchestration platform for identity flows. The wolfcola devtools suite provides first-class support for instrumenting DaVinci flows through the `@wolfcola/devtools-bridge` package. +Ping Identity DaVinci is an orchestration platform for identity flows. The wolfcola devtools suite provides first-class support for instrumenting DaVinci flows through the `@wolfcola/devtools-bridge` package. ## Overview @@ -96,6 +96,6 @@ bridge.destroy(); ## Troubleshooting -- **No events appearing** — Verify that the DaVinci SDK version is compatible. The adapter supports `@forgerock/davinci-client` v1.x and v2.x. +- **No events appearing** — Verify that the DaVinci SDK version is compatible. The adapter supports `@ping-identity/davinci-client`. - **Missing node transitions** — Some custom DaVinci nodes may not emit standard events. Contact the node author to ensure compatibility. - **Redacted fields showing as empty** — The bridge redacts sensitive fields by default. To see raw values during development, pass `redact: false` in the bridge options (never do this in production). diff --git a/packages/docs-site/content/docs/journey-integration.md b/packages/docs-site/content/docs/journey-integration.md new file mode 100644 index 0000000..997cea3 --- /dev/null +++ b/packages/docs-site/content/docs/journey-integration.md @@ -0,0 +1,86 @@ +--- +title: 'Journey Integration' +description: 'Integrate wolfcola devtools with Ping Identity Journey/Tree-based authentication' +section: guides +order: 6 +--- + +# Journey Integration + +Ping Identity Journey (formerly Tree-based authentication) uses a series of callbacks to guide users through authentication. The wolfcola devtools bridge provides a dedicated adapter for instrumenting Journey flows. + +## Setup + +### Install the Bridge + +```bash +npm install @wolfcola/devtools-bridge +``` + +### Create a Journey Bridge + +```typescript +import { createBridge } from '@wolfcola/devtools-bridge'; +import { journey } from '@wolfcola/devtools-bridge/adapters/journey'; + +const bridge = createBridge(journey(journeyConfig)); +``` + +The `journey` adapter hooks into the Journey SDK's callback mechanism and translates each step into the standard `AuthEvent` schema. + +## What Gets Captured + +With the Journey adapter active, the bridge emits events for: + +- **Flow start** -- when the Journey tree begins +- **Callback transitions** -- each time the flow presents a new set of callbacks +- **User submissions** -- data submitted at each callback step (passwords are redacted) +- **Step transitions** -- progression through the authentication tree +- **Session token issuance** -- successful authentication with token delivery +- **Errors** -- authentication failures and timeout errors + +## Flow State Tracking + +The bridge maintains a `FlowState` object representing the current position in the Journey tree: + +```typescript +bridge.getFlowState(); +// => { step: "username-password", tokens: null, error: null } +``` + +The DevTools extension visualizes this in the Flow view, showing each callback step as a node in the tree. + +## Advanced Configuration + +### Filtering Events + +```typescript +const bridge = createBridge(journey(journeyConfig), { + filter: (event) => event.type !== 'callback_transition', +}); +``` + +### Custom Metadata + +```typescript +const bridge = createBridge(journey(journeyConfig), { + metadata: { + environment: 'staging', + tree: 'login-v2', + }, +}); +``` + +### Cleanup + +```typescript +bridge.destroy(); +``` + +Always call `bridge.destroy()` when you are done. Failing to do so may cause memory leaks from lingering event listeners. + +## Troubleshooting + +- **No events appearing** -- Verify that the Journey SDK is correctly configured and the adapter receives the config object. +- **Missing step transitions** -- Some custom callback handlers may not emit standard events. Ensure callbacks follow the Ping Identity SDK conventions. +- **Redacted fields showing as empty** -- The bridge redacts sensitive fields by default. Pass `redact: false` in bridge options during development only. diff --git a/packages/docs-site/content/docs/oidc-integration.md b/packages/docs-site/content/docs/oidc-integration.md new file mode 100644 index 0000000..4b0d0d9 --- /dev/null +++ b/packages/docs-site/content/docs/oidc-integration.md @@ -0,0 +1,105 @@ +--- +title: 'Generic OIDC Integration' +description: 'Integrate wolfcola devtools with any OIDC client using the generic adapter' +section: guides +order: 7 +--- + +# Generic OIDC Integration + +If your OIDC client does not have a dedicated adapter (like DaVinci or Journey), you can use the generic OIDC adapter. It works with any client library by letting you provide hook functions that the bridge calls to subscribe to events. + +## Setup + +### Install the Bridge + +```bash +npm install @wolfcola/devtools-bridge +``` + +### Create a Generic OIDC Bridge + +```typescript +import { createBridge } from '@wolfcola/devtools-bridge'; +import { oidc } from '@wolfcola/devtools-bridge/adapters/oidc'; + +const bridge = createBridge( + oidc({ + onAuthorize: (cb) => myClient.on('authorize', cb), + onToken: (cb) => myClient.on('token', cb), + onError: (cb) => myClient.on('error', cb), + }), +); +``` + +You provide three hook functions that subscribe to your client's events. The adapter translates these into standard `AuthEvent` objects. + +## Hook Functions + +| Hook | Called when | Event type emitted | +| ------------- | ----------------------------------------- | ------------------ | +| `onAuthorize` | Authorization request starts or completes | `authorize` | +| `onToken` | Tokens are issued or refreshed | `token` | +| `onError` | An authentication error occurs | `error` | + +### Optional Hooks + +You can also provide additional hooks for more detailed event capture: + +```typescript +const bridge = createBridge( + oidc({ + onAuthorize: (cb) => myClient.on('authorize', cb), + onToken: (cb) => myClient.on('token', cb), + onError: (cb) => myClient.on('error', cb), + onRedirect: (cb) => myClient.on('redirect', cb), + onLogout: (cb) => myClient.on('logout', cb), + }), +); +``` + +## What Gets Captured + +With the generic OIDC adapter active, the bridge emits events for: + +- **Authorization start** -- when the auth flow begins +- **Token issuance** -- access tokens, refresh tokens, ID tokens +- **Token refresh** -- automatic or manual token renewal +- **Errors** -- failed auth requests, expired tokens, network errors +- **Redirects** -- OIDC redirect events (if `onRedirect` hook provided) +- **Logout** -- session termination (if `onLogout` hook provided) + +## Advanced Configuration + +### Filtering Events + +```typescript +const bridge = createBridge(oidc({ onAuthorize, onToken, onError }), { + filter: (event) => event.type !== 'redirect', +}); +``` + +### Custom Metadata + +```typescript +const bridge = createBridge(oidc({ onAuthorize, onToken, onError }), { + metadata: { + provider: 'auth0', + environment: 'staging', + }, +}); +``` + +### Cleanup + +```typescript +bridge.destroy(); +``` + +The generic adapter has the smallest bundle footprint (~0.8 kB minified + gzipped) since it delegates all event subscription to your hook functions. + +## Troubleshooting + +- **No events appearing** -- Verify that your hook functions are correctly subscribing to your client's events. The callbacks must be invoked when events fire. +- **Missing event types** -- The generic adapter only captures what you wire up. If you need redirect or logout events, provide the optional hooks. +- **Redacted fields showing as empty** -- The bridge redacts sensitive fields by default. Pass `redact: false` during development only. diff --git a/packages/docs-site/content/packages/devtools-bridge.md b/packages/docs-site/content/packages/devtools-bridge.md index e92da5c..bb56bcd 100644 --- a/packages/docs-site/content/packages/devtools-bridge.md +++ b/packages/docs-site/content/packages/devtools-bridge.md @@ -40,7 +40,7 @@ import { davinci } from '@wolfcola/devtools-bridge/adapters/davinci'; const bridge = createBridge(davinci(daVinciClient)); ``` -Supports `@forgerock/davinci-client` v1.x and v2.x. Captures node transitions, collector callbacks, and flow completion. +Supports `@ping-identity/davinci-client`. Captures node transitions, collector callbacks, and flow completion. ### Journey Adapter @@ -50,7 +50,7 @@ import { journey } from '@wolfcola/devtools-bridge/adapters/journey'; const bridge = createBridge(journey(journeyConfig)); ``` -Supports ForgeRock Journey/Tree-based authentication. Captures callbacks, step transitions, and session token issuance. +Supports Ping Identity Journey/Tree-based authentication. Captures callbacks, step transitions, and session token issuance. ### Generic OIDC Adapter From bded136ae13cd49987d9b28445adf1a0c8980197 Mon Sep 17 00:00:00 2001 From: Ryan Bas Date: Tue, 12 May 2026 00:19:41 -0600 Subject: [PATCH 18/23] fix(docs-site): fix dark mode covering full viewport, fix dead nav links - Dark mode now applies background/color on the [data-theme] wrapper which spans min-height: 100vh, so the entire page goes dark - Header nav links now point to actual existing pages instead of nonexistent index routes (/packages -> /packages/treeshake-check, etc.) Co-Authored-By: Claude Opus 4.6 (1M context) --- packages/docs-site/app/Shared.elm | 8 ++++---- packages/docs-site/style.css | 12 ++++++++++++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/packages/docs-site/app/Shared.elm b/packages/docs-site/app/Shared.elm index bee2c5a..9bd8123 100644 --- a/packages/docs-site/app/Shared.elm +++ b/packages/docs-site/app/Shared.elm @@ -226,11 +226,11 @@ viewHeader sharedData model toMsg = [ Html.text "wolfcola devtools" ] , viewSearch sharedData model toMsg , Html.nav [ Attr.class "header-nav" ] - [ Html.a [ Attr.href "/packages" ] [ Html.text "Packages" ] - , Html.a [ Attr.href "/guides" ] [ Html.text "Guides" ] - , Html.a [ Attr.href "/api" ] [ Html.text "API" ] + [ Html.a [ Attr.href "/packages/treeshake-check" ] [ Html.text "Packages" ] + , Html.a [ Attr.href "/docs/getting-started" ] [ Html.text "Guides" ] + , Html.a [ Attr.href "/api/treeshake-check/treeshake-check" ] [ Html.text "API" ] , Html.a [ Attr.href "/architecture" ] [ Html.text "Architecture" ] - , Html.a [ Attr.href "/contributing" ] [ Html.text "Contributing" ] + , Html.a [ Attr.href "/contributing/development-setup" ] [ Html.text "Contributing" ] ] , Html.button [ Attr.class "theme-toggle" diff --git a/packages/docs-site/style.css b/packages/docs-site/style.css index 300aa9d..b14b1b0 100644 --- a/packages/docs-site/style.css +++ b/packages/docs-site/style.css @@ -21,6 +21,18 @@ --border: #334155; --accent: #60a5fa; --accent-hover: #93bbfd; + color-scheme: dark; + background: var(--bg); + color: var(--text); +} + +[data-theme='light'] { + background: var(--bg); + color: var(--text); +} + +[data-theme] { + min-height: 100vh; } * { From f08e61da49083bc3a99b412c468ef754eecfc780 Mon Sep 17 00:00:00 2001 From: Ryan Bas Date: Tue, 12 May 2026 00:27:33 -0600 Subject: [PATCH 19/23] fix(docs-site): remove fabricated API reference, fix all content to match real code - Delete all ApiDocs Elm modules (Types, TreeshakeCheck, EslintPluginTreeshake, DevtoolsBridge, DevtoolsTypes) and the API route (Route.Api.Package_.ModuleName_) - Remove API link from header nav in Shared.elm - Remove .api-reference, .api-item, .api-signature, .api-example CSS rules - Rewrite devtools-bridge package page with real exports: attachDaVinciBridge, attachJourneyBridge, attachOidcBridge, emitAuthEvent, emitConfigEvent, DEVTOOLS_EVENT_NAME, BridgeHandle, DevtoolsOptions - Rewrite devtools-types package page with real schemas: AuthEventSchema, NetworkDataSchema, SdkDataSchema, SdkConfigDataSchema, DomDataSchema, SessionDataSchema, JourneyDataSchema, OidcDataSchema, FlowStateSchema, FlowSummarySchema, FlowExportSchema, OIDC semantic schemas - Rewrite treeshake-check package page with real CLI options and programmatic API: checkPackage, analyzeTreeshakeability, getEntryFromPackageJson, error classes - Rewrite eslint-plugin-treeshake package page with real single rule (no-treeshake-hazard) and its check categories, configs (recommended/strict) - Rewrite DaVinci integration guide: attachDaVinciBridge with Subscribable interface - Rewrite Journey integration guide: attachJourneyBridge with RTK Query state - Rewrite OIDC integration guide: attachOidcBridge with endpoint-to-phase mapping - Fix stale ApiDocs references in contributing docs Co-Authored-By: Claude Opus 4.6 (1M context) --- .../app/Route/Api/Package_/ModuleName_.elm | 185 ------------------ packages/docs-site/app/Shared.elm | 2 +- .../content/contributing/code-style.md | 2 +- .../contributing/repository-structure.md | 2 +- .../content/docs/davinci-integration.md | 97 ++++----- .../content/docs/journey-integration.md | 81 ++++---- .../content/docs/oidc-integration.md | 116 +++++------ .../content/packages/devtools-bridge.md | 138 +++++++------ .../content/packages/devtools-types.md | 178 ++++++++++------- .../packages/eslint-plugin-treeshake.md | 116 +++++------ .../content/packages/treeshake-check.md | 87 +++++++- .../docs-site/src/ApiDocs/DevtoolsBridge.elm | 24 --- .../docs-site/src/ApiDocs/DevtoolsTypes.elm | 28 --- .../src/ApiDocs/EslintPluginTreeshake.elm | 19 -- .../docs-site/src/ApiDocs/TreeshakeCheck.elm | 28 --- packages/docs-site/src/ApiDocs/Types.elm | 24 --- packages/docs-site/style.css | 41 ---- 17 files changed, 460 insertions(+), 708 deletions(-) delete mode 100644 packages/docs-site/app/Route/Api/Package_/ModuleName_.elm delete mode 100644 packages/docs-site/src/ApiDocs/DevtoolsBridge.elm delete mode 100644 packages/docs-site/src/ApiDocs/DevtoolsTypes.elm delete mode 100644 packages/docs-site/src/ApiDocs/EslintPluginTreeshake.elm delete mode 100644 packages/docs-site/src/ApiDocs/TreeshakeCheck.elm delete mode 100644 packages/docs-site/src/ApiDocs/Types.elm diff --git a/packages/docs-site/app/Route/Api/Package_/ModuleName_.elm b/packages/docs-site/app/Route/Api/Package_/ModuleName_.elm deleted file mode 100644 index ae4644e..0000000 --- a/packages/docs-site/app/Route/Api/Package_/ModuleName_.elm +++ /dev/null @@ -1,185 +0,0 @@ -module Route.Api.Package_.ModuleName_ exposing (ActionData, Data, Model, Msg, route) - -import ApiDocs.DevtoolsBridge -import ApiDocs.DevtoolsTypes -import ApiDocs.EslintPluginTreeshake -import ApiDocs.TreeshakeCheck -import ApiDocs.Types exposing (ApiModule, FunctionDoc, TypeDoc) -import BackendTask exposing (BackendTask) -import FatalError exposing (FatalError) -import Head -import Head.Seo as Seo -import Html -import Html.Attributes as Attr -import Pages.Url -import PagesMsg exposing (PagesMsg) -import RouteBuilder exposing (App, StatelessRoute) -import Shared -import View exposing (View) - - -type alias Model = - {} - - -type alias Msg = - () - - -type alias RouteParams = - { package : String - , moduleName : String - } - - -type alias Data = - { apiModule : ApiModule - } - - -type alias ActionData = - {} - - -route : StatelessRoute RouteParams Data ActionData -route = - RouteBuilder.preRender - { head = head - , pages = pages - , data = data - } - |> RouteBuilder.buildNoState { view = view } - - -allModules : List ( String, String, ApiModule ) -allModules = - List.map (\m -> ( "treeshake-check", m.name, m )) ApiDocs.TreeshakeCheck.modules - ++ List.map (\m -> ( "eslint-plugin-treeshake", m.name, m )) ApiDocs.EslintPluginTreeshake.modules - ++ List.map (\m -> ( "devtools-bridge", m.name, m )) ApiDocs.DevtoolsBridge.modules - ++ List.map (\m -> ( "devtools-types", m.name, m )) ApiDocs.DevtoolsTypes.modules - - -pages : BackendTask FatalError (List RouteParams) -pages = - allModules - |> List.map (\( pkg, modName, _ ) -> { package = pkg, moduleName = modName }) - |> BackendTask.succeed - - -data : RouteParams -> BackendTask FatalError Data -data routeParams = - case findModule routeParams.package routeParams.moduleName of - Just apiMod -> - BackendTask.succeed { apiModule = apiMod } - - Nothing -> - BackendTask.fail - (FatalError.fromString - ("API module not found: " ++ routeParams.package ++ "/" ++ routeParams.moduleName) - ) - - -findModule : String -> String -> Maybe ApiModule -findModule pkg modName = - allModules - |> List.filter (\( p, n, _ ) -> p == pkg && n == modName) - |> List.head - |> Maybe.map (\( _, _, m ) -> m) - - -head : - App Data ActionData RouteParams - -> List Head.Tag -head app = - Seo.summary - { canonicalUrlOverride = Nothing - , siteName = "wolfcola devtools" - , image = - { url = Pages.Url.external "" - , alt = "wolfcola devtools" - , dimensions = Nothing - , mimeType = Nothing - } - , description = app.data.apiModule.description - , locale = Nothing - , title = app.data.apiModule.name ++ " - API Reference" - } - |> Seo.website - - -view : - App Data ActionData RouteParams - -> Shared.Model - -> View (PagesMsg Msg) -view app _ = - let - apiMod = - app.data.apiModule - in - { title = apiMod.name ++ " - API Reference" - , body = - [ Html.div [ Attr.class "api-reference" ] - ([ Html.h1 [] [ Html.text apiMod.name ] - , Html.p [ Attr.class "api-description" ] [ Html.text apiMod.description ] - ] - ++ viewTypes apiMod.types - ++ viewFunctions apiMod.functions - ) - ] - } - - -viewTypes : List TypeDoc -> List (Html.Html (PagesMsg Msg)) -viewTypes types = - if List.isEmpty types then - [] - - else - Html.h2 [] [ Html.text "Types" ] - :: List.map viewType types - - -viewType : TypeDoc -> Html.Html (PagesMsg Msg) -viewType typeDoc = - Html.div [ Attr.class "api-item" ] - [ Html.h3 [ Attr.class "api-item-name" ] [ Html.text typeDoc.name ] - , Html.pre [ Attr.class "api-signature" ] - [ Html.code [] [ Html.text typeDoc.signature ] ] - , Html.p [] [ Html.text typeDoc.description ] - ] - - -viewFunctions : List FunctionDoc -> List (Html.Html (PagesMsg Msg)) -viewFunctions functions = - if List.isEmpty functions then - [] - - else - Html.h2 [] [ Html.text "Functions" ] - :: List.map viewFunction functions - - -viewFunction : FunctionDoc -> Html.Html (PagesMsg Msg) -viewFunction funcDoc = - Html.div [ Attr.class "api-item" ] - ([ Html.h3 [ Attr.class "api-item-name" ] [ Html.text funcDoc.name ] - , Html.pre [ Attr.class "api-signature" ] - [ Html.code [] [ Html.text funcDoc.signature ] ] - , Html.p [] [ Html.text funcDoc.description ] - ] - ++ viewExample funcDoc.example - ) - - -viewExample : Maybe String -> List (Html.Html (PagesMsg Msg)) -viewExample maybeExample = - case maybeExample of - Just example -> - [ Html.div [ Attr.class "api-example" ] - [ Html.h4 [] [ Html.text "Example" ] - , Html.pre [] [ Html.code [] [ Html.text example ] ] - ] - ] - - Nothing -> - [] diff --git a/packages/docs-site/app/Shared.elm b/packages/docs-site/app/Shared.elm index 9bd8123..b0a5f76 100644 --- a/packages/docs-site/app/Shared.elm +++ b/packages/docs-site/app/Shared.elm @@ -228,7 +228,7 @@ viewHeader sharedData model toMsg = , Html.nav [ Attr.class "header-nav" ] [ Html.a [ Attr.href "/packages/treeshake-check" ] [ Html.text "Packages" ] , Html.a [ Attr.href "/docs/getting-started" ] [ Html.text "Guides" ] - , Html.a [ Attr.href "/api/treeshake-check/treeshake-check" ] [ Html.text "API" ] + , Html.a [ Attr.href "/architecture" ] [ Html.text "Architecture" ] , Html.a [ Attr.href "/contributing/development-setup" ] [ Html.text "Contributing" ] ] diff --git a/packages/docs-site/content/contributing/code-style.md b/packages/docs-site/content/contributing/code-style.md index 71849b2..ef2b695 100644 --- a/packages/docs-site/content/contributing/code-style.md +++ b/packages/docs-site/content/contributing/code-style.md @@ -121,7 +121,7 @@ Test files live in `packages//test/` directories, mirroring the source str The docs site follows standard Elm conventions: - **elm-format** — All Elm files must be formatted with `elm-format`. The pre-commit hook runs this automatically. -- **Module naming** — Modules use PascalCase and match their file path (e.g. `ApiDocs.TreeshakeCheck` lives at `src/ApiDocs/TreeshakeCheck.elm`). +- **Module naming** — Modules use PascalCase and match their file path (e.g. `Search` lives at `src/Search.elm`). - **Type annotations** — Every top-level function must have a type annotation. - **No partial functions** — Never use `List.head`, `Maybe.withDefault` without documenting why, or any function that can crash. Use pattern matching instead. diff --git a/packages/docs-site/content/contributing/repository-structure.md b/packages/docs-site/content/contributing/repository-structure.md index 207dae0..dcd5ce2 100644 --- a/packages/docs-site/content/contributing/repository-structure.md +++ b/packages/docs-site/content/contributing/repository-structure.md @@ -77,4 +77,4 @@ The `devtools-types` package is the shared foundation. It defines the `AuthEvent 4. Add a `tsconfig.json` extending the root config 5. Add the package to the sidebar in `packages/docs-site/app/Shared.elm` 6. Create a content page in `packages/docs-site/content/packages/` -7. Create an API docs module in `packages/docs-site/src/ApiDocs/` +7. Create a content page in `packages/docs-site/content/docs/` for integration guides diff --git a/packages/docs-site/content/docs/davinci-integration.md b/packages/docs-site/content/docs/davinci-integration.md index e10c649..eb06c52 100644 --- a/packages/docs-site/content/docs/davinci-integration.md +++ b/packages/docs-site/content/docs/davinci-integration.md @@ -13,7 +13,7 @@ Ping Identity DaVinci is an orchestration platform for identity flows. The wolfc DaVinci flows consist of a series of nodes that the user progresses through. Each node may involve user interaction (login forms, MFA challenges), server-side decisions (risk evaluation, policy checks), or external service calls (social login providers, identity verification). -The devtools bridge captures every node transition as an `AuthEvent` and the overall flow progress as a `FlowState`, giving you full visibility into what is happening during authentication. +The devtools bridge monitors a DaVinci client's `Subscribable` interface and emits an `AuthEvent` on every node status transition, giving you full visibility into what is happening during authentication. ## Setup @@ -23,79 +23,68 @@ The devtools bridge captures every node transition as an `AuthEvent` and the ove npm install @wolfcola/devtools-bridge ``` -### Create a DaVinci Bridge +### Attach the DaVinci Bridge ```typescript -import { createBridge } from '@wolfcola/devtools-bridge'; -import { davinci } from '@wolfcola/devtools-bridge/adapters/davinci'; +import { attachDaVinciBridge } from '@wolfcola/devtools-bridge'; -// Pass your DaVinci client instance to the adapter -const bridge = createBridge(davinci(daVinciClient)); -``` - -The `davinci` adapter hooks into the DaVinci SDK's event system and translates its internal events into the standard `AuthEvent` schema that the devtools panel understands. - -## What Gets Captured - -With the DaVinci adapter active, the bridge emits events for: +// Pass your DaVinci client instance +const handle = attachDaVinciBridge(daVinciClient); -- **Flow start** — when `daVinciClient.start()` is called -- **Node transitions** — each time the flow advances to a new node -- **User submissions** — form data submitted at each node (passwords are redacted) -- **Collector callbacks** — interactions with individual collectors within a node -- **Errors** — authentication failures, network errors, timeout errors -- **Flow completion** — successful authentication with token issuance +// Optionally pass SDK config and devtools options +const handle = attachDaVinciBridge( + daVinciClient, + { + clientId: 'my-app', + redirectUri: 'https://example.com/callback', + }, + { consoleLog: true }, +); +``` -## Flow State Tracking +The bridge subscribes to the client via `client.subscribe()` and reads node state via `client.getNode()`. It decodes each node using an internal schema and only emits events when the node status actually changes. -The bridge also maintains a `FlowState` object that represents the current position in the DaVinci flow: +### Cleanup ```typescript -// FlowState is updated automatically -// Access it from the DevTools panel or programmatically: -bridge.getFlowState(); -// => { step: "username-password", tokens: null, error: null } +handle.detach(); ``` -The DevTools extension visualizes this as a node graph in the Flow view, where each step is a node and transitions are edges. - -## Advanced Configuration +Always call `handle.detach()` when you are done. Failing to do so may cause memory leaks from lingering event listeners. -### Filtering Events +## What Gets Captured -You can filter which events are emitted to the devtools panel: +With the DaVinci bridge attached, the following events are emitted: -```typescript -const bridge = createBridge(davinci(daVinciClient), { - filter: (event) => event.type !== 'collector_callback', -}); -``` +- **`sdk:config`** -- Emitted once on the first node transition (when `config` is provided). Contains the SDK configuration object. +- **`sdk:node-change`** -- Emitted on every node status transition. Contains `SdkData` with `nodeStatus`, `previousStatus`, `interactionId`, `interactionToken`, `nodeId`, `nodeName`, `nodeDescription`, `eventName`, `httpStatus`, `collectors`, `error`, `authorization`, `session`, and `responseBody`. +- **`session:cookie`** -- Emitted when `document.cookie` changes between node transitions. +- **`session:storage`** -- Emitted when `localStorage` values change between node transitions. -### Custom Metadata +## How It Works -Attach custom metadata to every event for debugging: +The bridge uses the `Subscribable` interface expected by the DaVinci SDK: ```typescript -const bridge = createBridge(davinci(daVinciClient), { - metadata: { - environment: 'staging', - flowId: 'login-v2', - }, -}); +interface Subscribable { + subscribe: (listener: () => void) => () => void; + getNode: () => unknown; + cache?: { + getCache: (requestId: string) => unknown; + }; +} ``` -### Cleanup - -When the component unmounts or the flow completes, destroy the bridge to stop event capture and release resources: - -```typescript -bridge.destroy(); -``` +On each subscription callback: -Always call `bridge.destroy()` when you are done. Failing to do so may cause memory leaks from lingering event listeners. +1. The bridge calls `client.getNode()` and decodes the result with `Schema.decodeUnknownOption` +2. If the node status matches the previous status, the event is skipped (deduplication) +3. If `window.__PING_DEVTOOLS_EXTENSION__` is not present, the event is dropped (no extension installed) +4. The node data is mapped to `SdkData` via the pure `nodeToSdkData` function +5. Session snapshots (cookies + localStorage) are taken before and after, with diffs emitted as separate events ## Troubleshooting -- **No events appearing** — Verify that the DaVinci SDK version is compatible. The adapter supports `@ping-identity/davinci-client`. -- **Missing node transitions** — Some custom DaVinci nodes may not emit standard events. Contact the node author to ensure compatibility. -- **Redacted fields showing as empty** — The bridge redacts sensitive fields by default. To see raw values during development, pass `redact: false` in the bridge options (never do this in production). +- **No events appearing** -- Verify that `window.__PING_DEVTOOLS_EXTENSION__` exists. The bridge only emits events when the Ping DevTools extension is detected. +- **Missing node transitions** -- The bridge deduplicates by `nodeStatus`. If two consecutive nodes have the same status string, only the first is emitted. +- **Optional peer dependency** -- The bridge has `@forgerock/davinci-client` as an optional peer dependency. You do not need to install it separately if you are already using the DaVinci SDK. diff --git a/packages/docs-site/content/docs/journey-integration.md b/packages/docs-site/content/docs/journey-integration.md index 997cea3..d111280 100644 --- a/packages/docs-site/content/docs/journey-integration.md +++ b/packages/docs-site/content/docs/journey-integration.md @@ -17,70 +17,63 @@ Ping Identity Journey (formerly Tree-based authentication) uses a series of call npm install @wolfcola/devtools-bridge ``` -### Create a Journey Bridge +### Attach the Journey Bridge ```typescript -import { createBridge } from '@wolfcola/devtools-bridge'; -import { journey } from '@wolfcola/devtools-bridge/adapters/journey'; +import { attachJourneyBridge } from '@wolfcola/devtools-bridge'; -const bridge = createBridge(journey(journeyConfig)); -``` - -The `journey` adapter hooks into the Journey SDK's callback mechanism and translates each step into the standard `AuthEvent` schema. - -## What Gets Captured +const handle = attachJourneyBridge(journeyClient); -With the Journey adapter active, the bridge emits events for: - -- **Flow start** -- when the Journey tree begins -- **Callback transitions** -- each time the flow presents a new set of callbacks -- **User submissions** -- data submitted at each callback step (passwords are redacted) -- **Step transitions** -- progression through the authentication tree -- **Session token issuance** -- successful authentication with token delivery -- **Errors** -- authentication failures and timeout errors +// Optionally pass SDK config and devtools options +const handle = attachJourneyBridge(journeyClient, sdkConfig, { consoleLog: true }); +``` -## Flow State Tracking +The bridge subscribes to the client via `client.subscribe()` and reads state via `client.getState()`. -The bridge maintains a `FlowState` object representing the current position in the Journey tree: +### Cleanup ```typescript -bridge.getFlowState(); -// => { step: "username-password", tokens: null, error: null } +handle.detach(); ``` -The DevTools extension visualizes this in the Flow view, showing each callback step as a node in the tree. +Always call `handle.detach()` when you are done. Failing to do so may cause memory leaks from lingering event listeners. -## Advanced Configuration +## What Gets Captured -### Filtering Events +With the Journey bridge attached, the following events are emitted: -```typescript -const bridge = createBridge(journey(journeyConfig), { - filter: (event) => event.type !== 'callback_transition', -}); -``` +- **`sdk:config`** -- Emitted once on the first mutation (when `config` is provided). Contains the SDK configuration object. +- **`sdk:journey-step`** -- Emitted for each fulfilled or rejected mutation. Contains `JourneyData` with the step type and associated fields. -### Custom Metadata +## How It Works + +The bridge monitors RTK Query state at `journeyReducer.mutations`. It expects a `JourneySubscribable` interface: ```typescript -const bridge = createBridge(journey(journeyConfig), { - metadata: { - environment: 'staging', - tree: 'login-v2', - }, -}); +interface JourneySubscribable { + subscribe: (listener: () => void) => () => void; + getState: () => unknown; +} ``` -### Cleanup +On each subscription callback: -```typescript -bridge.destroy(); -``` +1. The bridge decodes the state with `Schema.decodeUnknownOption` looking for `journeyReducer.mutations` +2. For each mutation entry not yet emitted, it checks if `status` is `'fulfilled'` or `'rejected'` +3. **Fulfilled mutations:** The step payload is decoded and mapped to `JourneyData`. The `stepType` is determined by: `authId` present = `'Step'`, `successUrl` present = `'LoginSuccess'`, otherwise `'LoginFailure'` +4. **Rejected mutations:** A `LoginFailure` event is emitted with the extracted error message +5. Stale mutation IDs no longer in the cache are automatically pruned from the deduplication set + +### JourneyData Fields -Always call `bridge.destroy()` when you are done. Failing to do so may cause memory leaks from lingering event listeners. +- `stepType`: `'Step'` | `'LoginSuccess'` | `'LoginFailure'` +- `callbacks`: Array of callback objects from the step +- `authId`, `tokenId`, `successUrl`: Step identifiers +- `realm`, `stage`, `header`, `description`: Step metadata +- `errorCode`, `errorMessage`, `errorReason`: Error details (for `LoginFailure`) ## Troubleshooting -- **No events appearing** -- Verify that the Journey SDK is correctly configured and the adapter receives the config object. -- **Missing step transitions** -- Some custom callback handlers may not emit standard events. Ensure callbacks follow the Ping Identity SDK conventions. -- **Redacted fields showing as empty** -- The bridge redacts sensitive fields by default. Pass `redact: false` in bridge options during development only. +- **No events appearing** -- Verify that `window.__PING_DEVTOOLS_EXTENSION__` exists. The bridge only emits events when the Ping DevTools extension is detected. +- **Missing step transitions** -- The bridge deduplicates by mutation request ID. Each mutation is only emitted once. +- **Pending mutations ignored** -- Only `fulfilled` and `rejected` mutations are emitted. Pending mutations are skipped. diff --git a/packages/docs-site/content/docs/oidc-integration.md b/packages/docs-site/content/docs/oidc-integration.md index 4b0d0d9..440e17c 100644 --- a/packages/docs-site/content/docs/oidc-integration.md +++ b/packages/docs-site/content/docs/oidc-integration.md @@ -1,13 +1,13 @@ --- title: 'Generic OIDC Integration' -description: 'Integrate wolfcola devtools with any OIDC client using the generic adapter' +description: 'Integrate wolfcola devtools with OIDC clients using the OIDC bridge adapter' section: guides order: 7 --- # Generic OIDC Integration -If your OIDC client does not have a dedicated adapter (like DaVinci or Journey), you can use the generic OIDC adapter. It works with any client library by letting you provide hook functions that the bridge calls to subscribe to events. +The OIDC bridge adapter instruments OIDC clients that expose their state via an RTK Query-style subscribable store. It maps mutation endpoint results to OIDC protocol phases. ## Setup @@ -17,89 +17,81 @@ If your OIDC client does not have a dedicated adapter (like DaVinci or Journey), npm install @wolfcola/devtools-bridge ``` -### Create a Generic OIDC Bridge +### Attach the OIDC Bridge ```typescript -import { createBridge } from '@wolfcola/devtools-bridge'; -import { oidc } from '@wolfcola/devtools-bridge/adapters/oidc'; - -const bridge = createBridge( - oidc({ - onAuthorize: (cb) => myClient.on('authorize', cb), - onToken: (cb) => myClient.on('token', cb), - onError: (cb) => myClient.on('error', cb), - }), -); -``` - -You provide three hook functions that subscribe to your client's events. The adapter translates these into standard `AuthEvent` objects. +import { attachOidcBridge } from '@wolfcola/devtools-bridge'; -## Hook Functions +const handle = attachOidcBridge(oidcClient); -| Hook | Called when | Event type emitted | -| ------------- | ----------------------------------------- | ------------------ | -| `onAuthorize` | Authorization request starts or completes | `authorize` | -| `onToken` | Tokens are issued or refreshed | `token` | -| `onError` | An authentication error occurs | `error` | +// Optionally pass config with clientId and devtools options +const handle = attachOidcBridge(oidcClient, { clientId: 'my-app' }, { consoleLog: true }); +``` -### Optional Hooks +The bridge subscribes to the client via `client.subscribe()` and reads state via `client.getState()`. -You can also provide additional hooks for more detailed event capture: +### Cleanup ```typescript -const bridge = createBridge( - oidc({ - onAuthorize: (cb) => myClient.on('authorize', cb), - onToken: (cb) => myClient.on('token', cb), - onError: (cb) => myClient.on('error', cb), - onRedirect: (cb) => myClient.on('redirect', cb), - onLogout: (cb) => myClient.on('logout', cb), - }), -); +handle.detach(); ``` +Always call `handle.detach()` when you are done. Failing to do so may cause memory leaks from lingering event listeners. + ## What Gets Captured -With the generic OIDC adapter active, the bridge emits events for: +With the OIDC bridge attached, the following events are emitted: -- **Authorization start** -- when the auth flow begins -- **Token issuance** -- access tokens, refresh tokens, ID tokens -- **Token refresh** -- automatic or manual token renewal -- **Errors** -- failed auth requests, expired tokens, network errors -- **Redirects** -- OIDC redirect events (if `onRedirect` hook provided) -- **Logout** -- session termination (if `onLogout` hook provided) +- **`sdk:config`** -- Emitted once on the first mutation (when `config` is provided). Contains the SDK configuration object. +- **`sdk:oidc-state`** -- Emitted for each fulfilled or rejected mutation that maps to a known OIDC endpoint. Contains `OidcData` with the protocol phase, status, and error details. -## Advanced Configuration +## How It Works -### Filtering Events +The bridge monitors RTK Query state at `oidc.mutations`. It expects an `OidcSubscribable` interface: ```typescript -const bridge = createBridge(oidc({ onAuthorize, onToken, onError }), { - filter: (event) => event.type !== 'redirect', -}); +interface OidcSubscribable { + subscribe: (listener: () => void) => () => void; + getState: () => unknown; +} ``` -### Custom Metadata +### Endpoint-to-Phase Mapping -```typescript -const bridge = createBridge(oidc({ onAuthorize, onToken, onError }), { - metadata: { - provider: 'auth0', - environment: 'staging', - }, -}); -``` +The bridge maps RTK Query mutation endpoint names to OIDC protocol phases: -### Cleanup +| Endpoint | Phase | +| ----------------- | ----------- | +| `authorizeFetch` | `authorize` | +| `authorizeIframe` | `authorize` | +| `exchange` | `exchange` | +| `revoke` | `revoke` | +| `userInfo` | `userinfo` | +| `endSession` | `logout` | -```typescript -bridge.destroy(); -``` +Mutations with endpoint names not in this map are silently ignored. + +### Processing + +On each subscription callback: + +1. The bridge decodes the state with `Schema.decodeUnknownOption` looking for `oidc.mutations` +2. For each mutation entry not yet emitted, it checks if `status` is `'fulfilled'` or `'rejected'` +3. The `endpointName` is mapped to an OIDC phase +4. **Fulfilled mutations:** An `OidcData` event is emitted with `status: 'success'` +5. **Rejected mutations:** An `OidcData` event is emitted with `status: 'error'`, including `errorCode` and `errorMessage` extracted from the error payload +6. Stale mutation IDs no longer in the cache are automatically pruned from the deduplication set + +### OidcData Fields -The generic adapter has the smallest bundle footprint (~0.8 kB minified + gzipped) since it delegates all event subscription to your hook functions. +- `phase`: `'authorize'` | `'exchange'` | `'revoke'` | `'userinfo'` | `'logout'` +- `status`: `'success'` | `'error'` +- `clientId`: From the config object (if provided) +- `errorCode`: Extracted from `error.data.error` or HTTP status +- `errorMessage`: Extracted from `error.data.error_description`, `error.data.message`, or `error.message` ## Troubleshooting -- **No events appearing** -- Verify that your hook functions are correctly subscribing to your client's events. The callbacks must be invoked when events fire. -- **Missing event types** -- The generic adapter only captures what you wire up. If you need redirect or logout events, provide the optional hooks. -- **Redacted fields showing as empty** -- The bridge redacts sensitive fields by default. Pass `redact: false` during development only. +- **No events appearing** -- Verify that `window.__PING_DEVTOOLS_EXTENSION__` exists. The bridge only emits events when the Ping DevTools extension is detected. +- **Missing OIDC events** -- Only mutations with recognized endpoint names are emitted. Custom endpoints not in the mapping table are ignored. +- **Pending mutations ignored** -- Only `fulfilled` and `rejected` mutations are emitted. Pending mutations are skipped. diff --git a/packages/docs-site/content/packages/devtools-bridge.md b/packages/docs-site/content/packages/devtools-bridge.md index bb56bcd..860daea 100644 --- a/packages/docs-site/content/packages/devtools-bridge.md +++ b/packages/docs-site/content/packages/devtools-bridge.md @@ -1,13 +1,13 @@ --- title: '@wolfcola/devtools-bridge' -description: 'SDK adapter for emitting events from OIDC clients' +description: 'SDK adapter for emitting auth events to the Ping DevTools extension' section: packages order: 3 --- # @wolfcola/devtools-bridge -The devtools bridge is a lightweight SDK adapter that connects your OIDC client to the wolfcola DevTools panel. It captures authentication events and flow state, then forwards them to the browser extension or VS Code extension for inspection. +The devtools bridge connects your OIDC client to the Ping DevTools browser extension. It monitors SDK state changes and emits `AuthEvent` objects via `CustomEvent` on the window, where the extension picks them up. ## Installation @@ -15,94 +15,116 @@ The devtools bridge is a lightweight SDK adapter that connects your OIDC client npm install @wolfcola/devtools-bridge ``` +**Dependencies:** `@wolfcola/devtools-types`, `effect` + +**Optional peer dependency:** `@forgerock/davinci-client` (only needed for the DaVinci bridge) + ## Quick Start ```typescript -import { createBridge } from '@wolfcola/devtools-bridge'; -import { davinci } from '@wolfcola/devtools-bridge/adapters/davinci'; +import { attachDaVinciBridge } from '@wolfcola/devtools-bridge'; -const bridge = createBridge(davinci(myDaVinciClient)); +const handle = attachDaVinciBridge(daVinciClient, sdkConfig); -// Events are now forwarded to the DevTools panel automatically. +// Events are now forwarded to the DevTools extension automatically. // When done: -bridge.destroy(); +handle.detach(); ``` -## Adapters - -Adapters translate SDK-specific events into the standard `AuthEvent` schema. Each adapter is a separate entry point so unused adapters are tree-shaken from your bundle. +## Bridge Functions -### DaVinci Adapter +All bridge functions share the same signature pattern: ```typescript -import { davinci } from '@wolfcola/devtools-bridge/adapters/davinci'; - -const bridge = createBridge(davinci(daVinciClient)); +function attachXxxBridge( + client: Subscribable, + config?: object, + devtoolsOptions?: DevtoolsOptions, +): BridgeHandle; ``` -Supports `@ping-identity/davinci-client`. Captures node transitions, collector callbacks, and flow completion. +They return a `BridgeHandle` with a single `detach()` method. When called outside a browser environment (SSR), they return a no-op handle. -### Journey Adapter +### `attachDaVinciBridge(client, config?, devtoolsOptions?)` -```typescript -import { journey } from '@wolfcola/devtools-bridge/adapters/journey'; +Attaches to a DaVinci `Subscribable` client. The client must expose `subscribe(listener)` and `getNode()` methods. On each subscription callback the bridge: -const bridge = createBridge(journey(journeyConfig)); -``` +1. Decodes the node using an internal `DaVinciNodeSchema` (via `Schema.decodeUnknownOption`) +2. Skips if the node status has not changed from the previous emission +3. Emits the SDK config once (on first node transition) as an `sdk:config` event via `emitConfigEvent` +4. Emits an `sdk:node-change` event with `SdkData` including `nodeStatus`, `previousStatus`, `interactionId`, `interactionToken`, `nodeId`, `nodeName`, `collectors`, `error`, `authorization`, etc. +5. Snapshots `document.cookie` and `localStorage` before and after, emitting `session:cookie` and `session:storage` diff events -Supports Ping Identity Journey/Tree-based authentication. Captures callbacks, step transitions, and session token issuance. +The bridge only emits events when `window.__PING_DEVTOOLS_EXTENSION__` is present. -### Generic OIDC Adapter +### `attachJourneyBridge(client, config?, devtoolsOptions?)` -```typescript -import { oidc } from '@wolfcola/devtools-bridge/adapters/oidc'; - -const bridge = createBridge( - oidc({ - onAuthorize: (cb) => myClient.on('authorize', cb), - onToken: (cb) => myClient.on('token', cb), - onError: (cb) => myClient.on('error', cb), - }), -); -``` +Attaches to a Journey `Subscribable` client that exposes `subscribe(listener)` and `getState()`. Monitors RTK Query state at `journeyReducer.mutations`. For each fulfilled or rejected mutation entry that has not been emitted yet: + +- **Fulfilled:** Decodes the step payload and maps it to `JourneyData` with `stepType` of `'Step'`, `'LoginSuccess'`, or `'LoginFailure'` based on the presence of `authId`, `successUrl`, or neither +- **Rejected:** Emits a `LoginFailure` event with the extracted error message + +Emits `sdk:journey-step` events. Automatically trims stale mutation IDs from the deduplication set. + +### `attachOidcBridge(client, config?, devtoolsOptions?)` -Use the generic adapter when your OIDC client does not have a dedicated adapter. You provide hook functions that the bridge calls to subscribe to events. +Attaches to an OIDC `Subscribable` client that exposes `subscribe(listener)` and `getState()`. Monitors RTK Query state at `oidc.mutations`. Maps mutation endpoint names to OIDC phases: -## Bridge API +| Endpoint | Phase | +| ----------------- | ----------- | +| `authorizeFetch` | `authorize` | +| `authorizeIframe` | `authorize` | +| `exchange` | `exchange` | +| `revoke` | `revoke` | +| `userInfo` | `userinfo` | +| `endSession` | `logout` | -### `createBridge(adapter, options?)` +Emits `sdk:oidc-state` events with `OidcData` containing `phase`, `status` (`'success'` or `'error'`), `clientId`, and error details when applicable. -Creates a new bridge instance. +## Event Emission -**Options:** +### `emitAuthEvent(event, options?)` -| Option | Type | Default | Description | -| ------------ | ------------------------------- | ------------ | ------------------------------------------------------- | -| `filter` | `(event: AuthEvent) => boolean` | `() => true` | Filter which events are emitted | -| `metadata` | `Record` | `{}` | Custom metadata attached to every event | -| `redact` | `boolean` | `true` | Redact sensitive fields like passwords and tokens | -| `bufferSize` | `number` | `1000` | Maximum number of events to keep in the internal buffer | +Manually emits an `AuthEvent`. The event is: -### `bridge.emit(event)` +1. Pushed to `window.__PING_DEVTOOLS_STATE__` (capped at 500 entries, oldest trimmed) +2. Optionally logged to the console if `options.consoleLog` is `true` +3. Dispatched as `window.dispatchEvent(new CustomEvent('pingDevtools', { detail: event }))` -Manually emit an `AuthEvent`. Useful for custom events that the adapter does not capture automatically. +No-ops when `window` is undefined (SSR-safe). -### `bridge.destroy()` +### `emitConfigEvent(config, options?)` -Disconnect from the OIDC client and stop emitting events. Always call this when the bridge is no longer needed to prevent memory leaks. +Emits an SDK configuration event with type `'sdk:config'` and `SdkConfigData` (`_tag: 'sdk-config'`). Internally calls `emitAuthEvent`. -### `bridge.getFlowState()` +## Constants and Types -Returns the current `FlowState` object, which represents the latest known position in the authentication flow. +### `DEVTOOLS_EVENT_NAME` + +The `CustomEvent` name: `'pingDevtools'`. + +### `DevtoolsOptions` + +```typescript +interface DevtoolsOptions { + consoleLog?: boolean; +} +``` + +When `consoleLog` is `true`, every emitted event is also logged via `console.log`. + +### `BridgeHandle` + +```typescript +interface BridgeHandle { + detach: () => void; +} +``` -The bridge uses `postMessage` to communicate with the browser extension. It has zero runtime dependencies beyond `@wolfcola/devtools-types`. +Call `detach()` to unsubscribe the bridge from the SDK client. -## Bundle Impact +## Event Storage -The bridge is designed to be lightweight. When using a specific adapter, only that adapter's code is included in your bundle: +Events are stored on `window.__PING_DEVTOOLS_STATE__` as an array of `AuthEvent` objects. The array is capped at 500 entries; when the limit is exceeded, the oldest entries are removed via `splice`. -| Import | Minified + gzipped | -| -------------------------- | ------------------ | -| `createBridge` + `davinci` | ~1.2 kB | -| `createBridge` + `journey` | ~1.0 kB | -| `createBridge` + `oidc` | ~0.8 kB | +Always call `handle.detach()` when you are done. Failing to do so may cause memory leaks from lingering subscription listeners. diff --git a/packages/docs-site/content/packages/devtools-types.md b/packages/docs-site/content/packages/devtools-types.md index 5add38d..36afda7 100644 --- a/packages/docs-site/content/packages/devtools-types.md +++ b/packages/docs-site/content/packages/devtools-types.md @@ -1,13 +1,13 @@ --- title: '@wolfcola/devtools-types' -description: 'Effect Schema definitions for AuthEvent and FlowState' +description: 'Effect Schema definitions for AuthEvent, FlowState, and related types' section: packages order: 4 --- # @wolfcola/devtools-types -This package provides the shared type definitions and runtime validators for the wolfcola devtools ecosystem. All types are defined using [Effect Schema](https://effect.website/docs/schema/introduction), giving you both TypeScript types and runtime validation in a single definition. +This package provides the shared type definitions and runtime validators for the wolfcola devtools ecosystem. All types are defined using [Effect Schema](https://effect.website/docs/schema/introduction), giving you both TypeScript types and runtime validation from a single source of truth. ## Installation @@ -15,113 +15,153 @@ This package provides the shared type definitions and runtime validators for the npm install @wolfcola/devtools-types ``` -This package has `effect` as a peer dependency. Make sure Effect is installed in your project: +This package has `effect` as a peer dependency: ```bash npm install effect ``` -## Key Types +## Auth Event Schemas -### AuthEvent +### `AuthEventSchema` -Represents a single authentication event captured during an OIDC flow. +The top-level event schema. Every event captured by the devtools bridge conforms to this shape. ```typescript -import { Schema } from 'effect'; - -const AuthEvent = Schema.TaggedStruct('AuthEvent', { - type: Schema.String, +Schema.Struct({ + id: Schema.String, timestamp: Schema.Number, - data: Schema.Unknown, + type: AuthEventTypeSchema, + source: Schema.Union('network', 'sdk', 'dom', 'session'), + flowId: Schema.NullOr(Schema.String), + causedBy: Schema.NullOr(Schema.String), + data: Schema.Union( + NetworkDataSchema, + SdkDataSchema, + SdkConfigDataSchema, + DomDataSchema, + SessionDataSchema, + JourneyDataSchema, + OidcDataSchema, + ), + flags: AuthEventFlagsSchema, + oidcSemantics: Schema.optional(OidcSemanticsSchema), }); ``` -The `type` field identifies the kind of event (e.g. `"authorize"`, `"token_exchange"`, `"refresh"`, `"error"`). The `timestamp` is milliseconds since the epoch. The `data` field contains event-specific payload. +### `AuthEventTypeSchema` + +A union of all valid event type strings: -### FlowState +`'network:request'` | `'network:response'` | `'network:cors-flag'` | `'sdk:node-change'` | `'sdk:action'` | `'sdk:config'` | `'sdk:journey-step'` | `'sdk:oidc-state'` | `'dom:form-submit'` | `'dom:redirect'` | `'session:cookie'` | `'session:storage'` -Represents the state of an OIDC authentication flow at a point in time. +### `AuthEventFlagsSchema` ```typescript -const FlowState = Schema.TaggedStruct('FlowState', { - step: Schema.String, - tokens: Schema.NullOr(TokenSet), - error: Schema.NullOr(FlowError), +Schema.Struct({ + isCors: Schema.Boolean, + isError: Schema.Boolean, + isAuthRelated: Schema.Boolean, }); ``` -A `FlowState` tracks which step the user is on, whether tokens have been issued, and whether an error has occurred. +## Data Schemas (TaggedStruct) -### TokenSet +Each event data variant uses `Schema.TaggedStruct` for discriminated unions via the `_tag` field. -```typescript -const TokenSet = Schema.Struct({ - accessToken: Schema.String, - idToken: Schema.OptionFromNullishOr(Schema.String), - refreshToken: Schema.OptionFromNullishOr(Schema.String), - expiresAt: Schema.Number, -}); -``` +### `NetworkDataSchema` (tag: `'network'`) -### FlowError +Network request/response data with URL, method, status, headers, duration, optional CORS flag, and optional request/response bodies. -```typescript -const FlowError = Schema.Struct({ - code: Schema.String, - message: Schema.String, - details: Schema.optional(Schema.Unknown), -}); -``` +### `SdkDataSchema` (tag: `'sdk'`) -## Usage with Effect +SDK node change data including `nodeStatus`, `previousStatus`, `interactionId`, `interactionToken`, `nodeId`, `requestId`, `nodeName`, `nodeDescription`, `eventName`, `httpStatus`, `collectors`, `error` (via `SdkErrorSchema`), `authorization` (via `SdkAuthorizationSchema`), `session`, and `responseBody`. -### Decoding Events +### `SdkConfigDataSchema` (tag: `'sdk-config'`) -Use the provided decode functions to validate raw data at runtime: +SDK configuration data containing a single `config: Schema.Unknown` field. -```typescript -import { Effect } from 'effect'; -import { decodeAuthEvent } from '@wolfcola/devtools-types'; +### `DomDataSchema` (tag: `'dom'`) -const program = Effect.gen(function* () { - const rawData = yield* getEventFromExtension(); - const event = yield* decodeAuthEvent(rawData); +DOM event data with optional `element` and `url` fields. - // event is now fully typed as AuthEvent - console.log(event.type, event.timestamp); -}); -``` +### `SessionDataSchema` (tag: `'session'`) -### Encoding Events +Session change data with `key`, optional `before`, and optional `after` fields for tracking cookie and localStorage mutations. -Encode typed events back to plain objects for serialization: +### `JourneyDataSchema` (tag: `'journey'`) -```typescript -import { encodeAuthEvent } from '@wolfcola/devtools-types'; +Journey/Tree authentication data with `stepType` (`'Step'` | `'LoginSuccess'` | `'LoginFailure'`), optional `callbacks`, `authId`, `tokenId`, `successUrl`, `realm`, `stage`, `header`, `description`, `errorCode`, `errorMessage`, and `errorReason`. -const plain = yield * encodeAuthEvent(event); -// plain is a plain JavaScript object safe to send over postMessage -``` +### `OidcDataSchema` (tag: `'oidc'`) + +OIDC state data with `phase` (`'authorize'` | `'exchange'` | `'revoke'` | `'userinfo'` | `'logout'`), `status` (`'success'` | `'error'`), and optional `clientId`, `errorCode`, `errorMessage`. + +## CORS Schema + +### `CorsFlagSchema` + +Describes a detected CORS issue with `url`, `reason` (one of `'status-zero'`, `'missing-allow-origin'`, `'credentials-mismatch'`, `'wildcard-with-credentials'`, `'preflight-failed'`), `method`, and optional `preflightStatus`, `allowOrigin`, `allowCredentials`. + +## OIDC Semantic Schemas + +Fine-grained OIDC protocol details attached optionally to any `AuthEvent`. + +### `OidcSemanticsSchema` (tag: `'oidc-semantics'`) -### Custom Event Types +Contains `oidcPhase` (`'discovery'` | `'authorize'` | `'par'` | `'token'` | `'userinfo'` | `'revocation'` | `'introspection'` | `'end-session'` | `'jwks'`), and optional fields: `grantType`, `pkce` (via `OidcPkceSchema`), `dpop` (via `OidcDpopSchema`), `par` (via `OidcParSchema`), `state`, `nonce`, `clientId`, `tokens` (via `OidcTokensSchema`), `error` (via `OidcErrorSchema`). -Extend the base `AuthEvent` schema for domain-specific events: +### `OidcPkceSchema` + +PKCE details: optional `challengeMethod` and required `hasVerifier: boolean`. + +### `OidcDpopSchema` + +DPoP details: optional `proofJwt`, `tokenType`, `nonce`. + +### `OidcParSchema` + +Pushed Authorization Request details: optional `requestUri` and `expiresIn`. + +### `OidcTokensSchema` + +Token presence indicators: optional booleans for `accessToken`, `refreshToken`, `idToken`, plus optional `tokenType` and `expiresIn`. + +### `OidcErrorSchema` + +OIDC error with required `error: string` and optional `errorDescription`. + +## Flow State Schemas + +### `FlowStateSchema` + +Represents the state of a captured authentication flow. ```typescript -import { Schema } from 'effect'; -import { AuthEvent } from '@wolfcola/devtools-types'; - -const DaVinciEvent = Schema.extend( - AuthEvent, - Schema.Struct({ - nodeId: Schema.String, - flowId: Schema.String, - }), -); +Schema.Struct({ + flowId: Schema.NullOr(Schema.String), + capturedAt: Schema.String, + events: Schema.Array(AuthEventSchema), + summary: FlowSummarySchema, + lastSdkEventId: Schema.optionalWith(Schema.NullOr(Schema.String), { default: () => null }), +}); ``` -This package uses `Schema.TaggedStruct` for discriminated unions. Make sure you are on Effect 3.10 or later, which includes the tagged struct API. +### `FlowSummarySchema` + +Aggregate statistics for a flow: `nodeCount`, `errorCount`, `corsFlags` (array of `CorsFlagSchema`), `duration`, and `sdkConnected`. + +### `FlowExportSchema` + +Wraps a `FlowState` for export/import with `version: 1`, `exportedAt`, `redacted`, and `flow`. + +## Derived Types + +All TypeScript types are derived from their schemas using `Schema.Schema.Type`: + +`AuthEvent`, `AuthEventType`, `AuthEventFlags`, `NetworkData`, `SdkData`, `SdkConfigData`, `DomData`, `SessionData`, `JourneyData`, `OidcData`, `CorsFlag`, `OidcSemantics`, `FlowState`, `FlowSummary`, `FlowExport` + +This package uses `Schema.TaggedStruct` for discriminated unions. Make sure you are on Effect 3.10 or later. ## Versioning diff --git a/packages/docs-site/content/packages/eslint-plugin-treeshake.md b/packages/docs-site/content/packages/eslint-plugin-treeshake.md index cd5a855..19e6b00 100644 --- a/packages/docs-site/content/packages/eslint-plugin-treeshake.md +++ b/packages/docs-site/content/packages/eslint-plugin-treeshake.md @@ -1,6 +1,6 @@ --- title: '@wolfcola/eslint-plugin-treeshake' -description: 'ESLint plugin that flags tree-breaking patterns' +description: 'ESLint plugin that flags tree-shaking hazards in your code' section: packages order: 2 --- @@ -17,100 +17,86 @@ npm install -D @wolfcola/eslint-plugin-treeshake ## Configuration -### ESLint Flat Config (recommended) +### Flat Config (recommended) ```javascript import treeshake from '@wolfcola/eslint-plugin-treeshake'; +// Warns on all hazard patterns export default [treeshake.configs.recommended]; ``` -### Legacy `.eslintrc` +### Strict Config -```json -{ - "plugins": ["@wolfcola/treeshake"], - "extends": ["plugin:@wolfcola/treeshake/recommended"] -} -``` - -## Rules - -### `treeshake/no-top-level-side-effects` - -Disallows expressions at module scope that produce side effects. Side effects at the top level prevent bundlers from removing the module even when none of its exports are used. - -**Bad:** +```javascript +import treeshake from '@wolfcola/eslint-plugin-treeshake'; -```typescript -// This runs when the module is imported, even if nothing is used -console.log('module loaded'); -const el = document.createElement('div'); +// Errors on all hazard patterns + enables bundle check +export default [treeshake.configs.strict]; ``` -**Good:** - -```typescript -// Side effects are deferred to function calls -export const init = () => { - console.log('module loaded'); - const el = document.createElement('div'); - return el; -}; -``` +The `strict` config sets the rule to `error` level and enables `bundleCheck: true`, which runs `treeshake-check --json` as part of linting to cross-reference static findings with actual bundle analysis. -### `treeshake/no-mutable-module-scope` +## Rule: `no-treeshake-hazard` -Flags `let` and `var` declarations at module scope that are read by exported functions. Mutable bindings create implicit dependencies that bundlers cannot safely eliminate. +A single rule that detects multiple categories of tree-shaking hazards. Each category can be individually toggled. -**Bad:** +### Detected Patterns -```typescript -let count = 0; -export const increment = () => ++count; -``` +| Pattern | Option | Default | Description | +| --------------------- | ------------------------ | ------- | ----------------------------------------------------------------------- | +| Enum declarations | `checkEnums` | `true` | TypeScript `enum` compiles to an IIFE that bundlers cannot remove | +| Unannotated calls | `checkUnannotatedCalls` | `true` | Top-level function calls without `/*#__PURE__*/` annotation | +| Prototype mutations | `checkPrototypeMutation` | `true` | `Object.defineProperty`, `Object.setPrototypeOf`, `X.prototype.y = ...` | +| Global assignments | `checkGlobalAssignment` | `true` | Assignments to `window`, `globalThis`, `self`, `global` | +| CommonJS patterns | `checkCjsPatterns` | `true` | `require()`, `module.exports`, `exports.x` | +| Missing `sideEffects` | `checkSideEffectsField` | `true` | Warns when nearest `package.json` lacks a `sideEffects` field | +| Bundle check (opt-in) | `bundleCheck` | `false` | Runs `treeshake-check --json` and maps results to source locations | -**Good:** +### Rule Options -```typescript -export const createCounter = () => { - let count = 0; - return { increment: () => ++count }; -}; +```javascript +'wolfcola/no-treeshake-hazard': ['warn', { + checkEnums: true, + checkUnannotatedCalls: true, + checkPrototypeMutation: true, + checkGlobalAssignment: true, + checkCjsPatterns: true, + checkSideEffectsField: true, + additionalPureFunctions: ['myPureHelper'], + bundleCheck: false, + bundleCheckCwd: undefined, +}] ``` -### `treeshake/no-export-star` - -Warns against `export * from "..."` patterns. Barrel files that re-export everything from sub-modules make it difficult for bundlers to determine which exports are actually used. +### `additionalPureFunctions` -**Bad:** +An array of function names that should be treated as pure (side-effect-free). Calls to these functions at module scope will not trigger the `unannotatedCall` warning. -```typescript -export * from './utils'; -export * from './helpers'; -``` +### Auto-fix and Suggestions -**Good:** +- **Enum declarations:** Provides a suggestion to replace `enum` with an `as const` object and type alias +- **Unannotated calls:** Auto-fixes by inserting `/*#__PURE__*/` before the call -```typescript -export { formatDate, parseDate } from './utils'; -export { capitalize } from './helpers'; -``` +### Bundle Check Mode -### `treeshake/no-class-side-effects` +When `bundleCheck: true`, the rule runs `npx treeshake-check --json` (with a 60-second timeout) and maps the bundle analysis results back to source file locations. Static findings are deduplicated against bundle check results to avoid double-reporting. -Detects class declarations with static initializers that call external functions. Static blocks and property initializers run at class definition time, creating side effects. +## Configs -All rules are enabled by the `recommended` config preset. You can disable individual rules in your ESLint config if needed. +| Config | Rule Level | `bundleCheck` | +| ------------- | ---------- | ------------- | +| `recommended` | `warn` | `false` | +| `strict` | `error` | `true` | ## Programmatic API -The plugin exports its rules for use in custom ESLint configurations: - ```javascript import treeshake from '@wolfcola/eslint-plugin-treeshake'; -// Access individual rules -treeshake.rules['no-top-level-side-effects']; -treeshake.rules['no-mutable-module-scope']; +// Access the rule directly +treeshake.rules['no-treeshake-hazard']; + +// Access the named export +import { noTreeshakeHazard } from '@wolfcola/eslint-plugin-treeshake'; ``` diff --git a/packages/docs-site/content/packages/treeshake-check.md b/packages/docs-site/content/packages/treeshake-check.md index 66ec25e..d8c9b9a 100644 --- a/packages/docs-site/content/packages/treeshake-check.md +++ b/packages/docs-site/content/packages/treeshake-check.md @@ -7,7 +7,7 @@ order: 1 # @wolfcola/treeshake-check -Verify that your npm packages are properly tree-shakeable. +Verify that your npm packages are properly tree-shakeable. Uses Rollup to bundle a synthetic consumer that imports your package entry point, then analyzes which modules survive tree-shaking and why. ## Installation @@ -18,9 +18,88 @@ npm install -D @wolfcola/treeshake-check ## CLI Usage ```bash -npx treeshake-check +npx treeshake-check [options] ``` -Checks whether importing individual exports from a package produces minimal bundles via Rollup. +By default, the CLI reads `package.json` from the current directory, resolves the entry point from the `module`, `main`, or `exports` field, and runs the analysis. -This tool uses Rollup internally. Ensure Rollup is available in your project. +### Options + +| Option | Alias | Description | +| --------- | ----- | ------------------------------------------------------------------------------- | +| `--cwd` | `-C` | Directory containing the package.json to analyze (defaults to cwd) | +| `--entry` | `-e` | Analyze a specific entry file directly, skipping package.json resolution | +| `--json` | | Emit machine-readable JSON instead of human-readable output | +| `--quiet` | `-q` | Suppress all output; rely on exit code only (exit 1 = not fully tree-shakeable) | +| `--top` | | Show only the N modules with the largest surviving byte count | + +### Examples + +```bash +# Check the package in the current directory +npx treeshake-check + +# Check a specific directory +npx treeshake-check --cwd packages/my-lib + +# Analyze a specific file +npx treeshake-check --entry dist/index.mjs + +# CI gate (fails with exit code 1 if not tree-shakeable) +npx treeshake-check --quiet + +# Machine-readable output +npx treeshake-check --json + +# Show top 5 worst offenders +npx treeshake-check --top 5 +``` + +## Programmatic API + +### `checkPackage(cwd?)` + +Full pipeline: reads `package.json` from `cwd` (defaults to `process.cwd()`), resolves the entry point, and analyzes tree-shakeability. Returns `Effect.Effect`. + +```typescript +import { checkPackage } from '@wolfcola/treeshake-check'; +import { Effect } from 'effect'; +import { NodeContext } from '@effect/platform-node'; + +const result = await Effect.runPromise( + checkPackage('./packages/my-lib').pipe(Effect.provide(NodeContext.layer)), +); +``` + +### `analyzeTreeshakeability(entry, pkg?)` + +Analyzes a single entry file. Creates a virtual Rollup bundle that imports the entry, enables tree-shaking, and inspects what survives. Returns `Effect.Effect`. + +### `getEntryFromPackageJson(cwd?)` + +Reads and validates `package.json`, resolving the entry point from `module`, `main`, or `exports` fields. Returns the entry path and parsed package metadata. + +## Result Types + +### `TreeshakeResult` + +A discriminated union (Effect `Schema.Union` of `TaggedStruct`): + +- **`FullyTreeshakeable`** -- All modules rendered to zero bytes. Includes `hints` with package-level recommendations. +- **`HasSideEffects`** -- Some modules survived tree-shaking. Includes `totalOriginalBytes`, `totalRenderedBytes`, `modules` (array of per-module analysis), `warnings` (Rollup warnings), `hints`, and `unshakenCode`. + +Each module in the `HasSideEffects` result includes `id`, `originalLength`, `renderedLength`, `shakingRatio`, `renderedExports`, `removedExports`, `survivingCode`, and `suspectedCauses`. + +### Suspected Causes + +The analyzer classifies surviving code into categories: `TopLevelSideEffect`, `PrototypeMutation`, `GlobalAssignment`, `CommonJsContamination`, `UnannotatedCall`, `EnumPattern`, `Unknown`. The CLI provides explanations and fix suggestions for each. + +## Error Types + +| Error | Description | +| --------------------- | ---------------------------------------------------------- | +| `PackageJsonNotFound` | No `package.json` found at the specified path | +| `MissingEntryPoint` | `package.json` has no `module`, `main`, or `exports` entry | +| `BundleFailed` | Rollup bundling failed (wraps the underlying cause) | + +This tool uses Rollup internally. Rollup is bundled as a dependency. diff --git a/packages/docs-site/src/ApiDocs/DevtoolsBridge.elm b/packages/docs-site/src/ApiDocs/DevtoolsBridge.elm deleted file mode 100644 index 70a4556..0000000 --- a/packages/docs-site/src/ApiDocs/DevtoolsBridge.elm +++ /dev/null @@ -1,24 +0,0 @@ -module ApiDocs.DevtoolsBridge exposing (modules) - -import ApiDocs.Types exposing (ApiModule) - - -modules : List ApiModule -modules = - [ { name = "devtools-bridge" - , description = "SDK adapter for emitting events from DaVinci, Journey, OIDC clients" - , types = - [ { name = "Bridge" - , signature = "type Bridge = { emit: (event: AuthEvent) => void; destroy: () => void }" - , description = "A bridge instance that emits AuthEvent objects to the DevTools panel." - } - ] - , functions = - [ { name = "createBridge" - , signature = "(adapter: Adapter) => Bridge" - , description = "Create a bridge instance from an SDK adapter." - , example = Just "import { createBridge } from \"@wolfcola/devtools-bridge\"\nimport { davinci } from \"@wolfcola/devtools-bridge/adapters/davinci\"\n\nconst bridge = createBridge(davinci(client))" - } - ] - } - ] diff --git a/packages/docs-site/src/ApiDocs/DevtoolsTypes.elm b/packages/docs-site/src/ApiDocs/DevtoolsTypes.elm deleted file mode 100644 index f80fd90..0000000 --- a/packages/docs-site/src/ApiDocs/DevtoolsTypes.elm +++ /dev/null @@ -1,28 +0,0 @@ -module ApiDocs.DevtoolsTypes exposing (modules) - -import ApiDocs.Types exposing (ApiModule) - - -modules : List ApiModule -modules = - [ { name = "devtools-types" - , description = "Effect Schema definitions for AuthEvent and FlowState" - , types = - [ { name = "AuthEvent" - , signature = "Schema.TaggedStruct<\"AuthEvent\", { type: string; timestamp: number; data: unknown }>" - , description = "Represents a single authentication event captured during an OIDC flow." - } - , { name = "FlowState" - , signature = "Schema.TaggedStruct<\"FlowState\", { step: string; tokens: TokenSet | null; error: FlowError | null }>" - , description = "Represents the state of an OIDC authentication flow at a point in time." - } - ] - , functions = - [ { name = "decodeAuthEvent" - , signature = "(input: unknown) => Effect.Effect" - , description = "Decode and validate raw data into a typed AuthEvent using Effect Schema." - , example = Just "import { decodeAuthEvent } from \"@wolfcola/devtools-types\"\n\nconst event = yield* decodeAuthEvent(rawData)" - } - ] - } - ] diff --git a/packages/docs-site/src/ApiDocs/EslintPluginTreeshake.elm b/packages/docs-site/src/ApiDocs/EslintPluginTreeshake.elm deleted file mode 100644 index 2954e73..0000000 --- a/packages/docs-site/src/ApiDocs/EslintPluginTreeshake.elm +++ /dev/null @@ -1,19 +0,0 @@ -module ApiDocs.EslintPluginTreeshake exposing (modules) - -import ApiDocs.Types exposing (ApiModule) - - -modules : List ApiModule -modules = - [ { name = "eslint-plugin-treeshake" - , description = "ESLint plugin that flags tree-breaking patterns" - , types = [] - , functions = - [ { name = "configs.recommended" - , signature = "Linter.FlatConfig" - , description = "Recommended flat config preset that enables all tree-shaking rules." - , example = Just "import treeshake from \"@wolfcola/eslint-plugin-treeshake\"\n\nexport default [treeshake.configs.recommended]" - } - ] - } - ] diff --git a/packages/docs-site/src/ApiDocs/TreeshakeCheck.elm b/packages/docs-site/src/ApiDocs/TreeshakeCheck.elm deleted file mode 100644 index 74ad90e..0000000 --- a/packages/docs-site/src/ApiDocs/TreeshakeCheck.elm +++ /dev/null @@ -1,28 +0,0 @@ -module ApiDocs.TreeshakeCheck exposing (modules) - -import ApiDocs.Types exposing (ApiModule) - - -modules : List ApiModule -modules = - [ { name = "treeshake-check" - , description = "CLI & library to verify packages are tree-shakeable by Rollup" - , types = - [ { name = "TreeshakeResult" - , signature = "type TreeshakeResult = { passed: boolean; exports: ExportResult[] }" - , description = "The result of checking a package for tree-shaking support." - } - , { name = "ExportResult" - , signature = "type ExportResult = { name: string; bundleSize: number; treeshakeable: boolean }" - , description = "Result for an individual export." - } - ] - , functions = - [ { name = "checkTreeShaking" - , signature = "(packageName: string, options?: CheckOptions) => Effect.Effect" - , description = "Check whether a package's exports are tree-shakeable." - , example = Just "import { checkTreeShaking } from \"@wolfcola/treeshake-check\"\n\nconst result = yield* checkTreeShaking(\"my-package\")" - } - ] - } - ] diff --git a/packages/docs-site/src/ApiDocs/Types.elm b/packages/docs-site/src/ApiDocs/Types.elm deleted file mode 100644 index 13b5365..0000000 --- a/packages/docs-site/src/ApiDocs/Types.elm +++ /dev/null @@ -1,24 +0,0 @@ -module ApiDocs.Types exposing (ApiModule, FunctionDoc, TypeDoc) - - -type alias ApiModule = - { name : String - , description : String - , types : List TypeDoc - , functions : List FunctionDoc - } - - -type alias TypeDoc = - { name : String - , signature : String - , description : String - } - - -type alias FunctionDoc = - { name : String - , signature : String - , description : String - , example : Maybe String - } diff --git a/packages/docs-site/style.css b/packages/docs-site/style.css index b14b1b0..e2b9f49 100644 --- a/packages/docs-site/style.css +++ b/packages/docs-site/style.css @@ -333,47 +333,6 @@ a:hover { margin-bottom: 1rem; } -/* API Reference */ -.api-reference { - max-width: var(--content-max); -} -.api-description { - font-size: 1.1rem; - color: var(--text-muted); - margin-bottom: 2rem; -} -.api-item { - padding: 1.25rem 0; - border-bottom: 1px solid var(--border); -} -.api-item:last-child { - border-bottom: none; -} -.api-item-name { - font-family: var(--font-mono); - font-size: 1.1rem; - color: var(--accent); - margin-bottom: 0.5rem; -} -.api-signature { - background: var(--bg-sidebar); - border: 1px solid var(--border); - border-radius: 6px; - padding: 0.75rem 1rem; - font-size: 0.85rem; - margin-bottom: 0.75rem; -} -.api-example { - margin-top: 0.75rem; -} -.api-example h4 { - font-size: 0.8rem; - text-transform: uppercase; - letter-spacing: 0.05em; - color: var(--text-muted); - margin-bottom: 0.5rem; -} - /* Architecture */ /* Search */ .search-wrapper { From 5ca22489cdade70ab7282c875650371af3f6e50e Mon Sep 17 00:00:00 2001 From: Ryan Bas Date: Tue, 12 May 2026 08:39:19 -0600 Subject: [PATCH 20/23] fix(docs-site): fix inaccuracies from content audit - Fix treeshake-check CLI syntax (no positional args, use --cwd) - Fix event type examples in devtools-extension.md (use real schema types) - Fix unpacked extension path (manifest.json is in package root) - Fix vscode extension ID and name (oidc-devtools by ryanbasmajian) - Fix createBridge reference to attachDaVinciBridge in vscode docs - Fix test file location (co-located in src/, not in test/) - Fix eslint-plugin docs (add missing Object.defineProperties) - Fix repository-structure.md (correct vscode path, add devtools-core/ui) - Fix tree-shaking.md (remove barrel file claim, accurate tool description) - Remove scaffold leftover routes (Greet, Hello, Blog) Co-Authored-By: Claude Opus 4.6 (1M context) --- packages/docs-site/app/Route/Blog/Slug_.elm | 86 ------------- packages/docs-site/app/Route/Greet.elm | 107 ---------------- packages/docs-site/app/Route/Hello.elm | 119 ------------------ .../content/contributing/code-style.md | 2 +- .../contributing/repository-structure.md | 13 +- .../content/docs/devtools-extension.md | 4 +- .../docs-site/content/docs/getting-started.md | 11 +- .../docs-site/content/docs/tree-shaking.md | 28 +++-- .../content/docs/vscode-extension.md | 6 +- .../packages/eslint-plugin-treeshake.md | 18 +-- 10 files changed, 52 insertions(+), 342 deletions(-) delete mode 100644 packages/docs-site/app/Route/Blog/Slug_.elm delete mode 100644 packages/docs-site/app/Route/Greet.elm delete mode 100644 packages/docs-site/app/Route/Hello.elm diff --git a/packages/docs-site/app/Route/Blog/Slug_.elm b/packages/docs-site/app/Route/Blog/Slug_.elm deleted file mode 100644 index b823c97..0000000 --- a/packages/docs-site/app/Route/Blog/Slug_.elm +++ /dev/null @@ -1,86 +0,0 @@ -module Route.Blog.Slug_ exposing (ActionData, Data, Model, Msg, route) - -import BackendTask exposing (BackendTask) -import FatalError exposing (FatalError) -import Head -import Head.Seo as Seo -import Html -import Pages.Url -import PagesMsg exposing (PagesMsg) -import RouteBuilder exposing (App, StatelessRoute) -import Shared -import View exposing (View) - - -type alias Model = - {} - - -type alias Msg = - () - - -type alias RouteParams = - { slug : String } - - -route : StatelessRoute RouteParams Data ActionData -route = - RouteBuilder.preRender - { head = head - , pages = pages - , data = data - } - |> RouteBuilder.buildNoState { view = view } - - -pages : BackendTask FatalError (List RouteParams) -pages = - BackendTask.succeed - [ { slug = "hello" } - ] - - -type alias Data = - { something : String - } - - -type alias ActionData = - {} - - -data : RouteParams -> BackendTask FatalError Data -data routeParams = - BackendTask.map Data - (BackendTask.succeed "Hi") - - -head : - App Data ActionData RouteParams - -> List Head.Tag -head app = - Seo.summary - { canonicalUrlOverride = Nothing - , siteName = "elm-pages" - , image = - { url = Pages.Url.external "TODO" - , alt = "elm-pages logo" - , dimensions = Nothing - , mimeType = Nothing - } - , description = "TODO" - , locale = Nothing - , title = "TODO title" -- metadata.title -- TODO - } - |> Seo.website - - -view : - App Data ActionData RouteParams - -> Shared.Model - -> View (PagesMsg Msg) -view app sharedModel = - { title = "Placeholder - Blog.Slug_" - , body = [ Html.text app.data.something ] - } diff --git a/packages/docs-site/app/Route/Greet.elm b/packages/docs-site/app/Route/Greet.elm deleted file mode 100644 index 5434b62..0000000 --- a/packages/docs-site/app/Route/Greet.elm +++ /dev/null @@ -1,107 +0,0 @@ -module Route.Greet exposing (ActionData, Data, Model, Msg, route) - -import BackendTask exposing (BackendTask) -import BackendTask.Http -import ErrorPage exposing (ErrorPage) -import FatalError exposing (FatalError) -import Head -import Head.Seo as Seo -import Html -import Json.Decode as Decode -import Pages.Url -import PagesMsg exposing (PagesMsg) -import RouteBuilder exposing (App, StatefulRoute, StatelessRoute) -import Server.Request as Request exposing (Request) -import Server.Response as Response exposing (Response) -import Shared -import View exposing (View) - - -type alias Model = - {} - - -type alias Msg = - () - - -type alias RouteParams = - {} - - -route : StatelessRoute RouteParams Data ActionData -route = - RouteBuilder.serverRender - { head = head - , data = data - , action = \_ _ -> BackendTask.fail (FatalError.fromString "No action.") - } - |> RouteBuilder.buildNoState { view = view } - - -type alias Data = - { name : Maybe String - } - - -type alias ActionData = - {} - - -data : RouteParams -> Request -> BackendTask FatalError (Response Data ErrorPage) -data routeParams request = - case request |> Request.queryParam "name" of - Just name -> - BackendTask.Http.getJson "http://worldtimeapi.org/api/timezone/America/Los_Angeles" - (Decode.field "utc_datetime" Decode.string) - |> BackendTask.allowFatal - |> BackendTask.map - (\dateTimeString -> - Response.render - { name = Just dateTimeString } - ) - - Nothing -> - BackendTask.succeed - (Response.render - { name = Nothing } - ) - - -head : - App Data ActionData RouteParams - -> List Head.Tag -head app = - Seo.summary - { canonicalUrlOverride = Nothing - , siteName = "elm-pages" - , image = - { url = Pages.Url.external "TODO" - , alt = "elm-pages logo" - , dimensions = Nothing - , mimeType = Nothing - } - , description = "TODO" - , locale = Nothing - , title = "TODO title" -- metadata.title -- TODO - } - |> Seo.website - - -view : - App Data ActionData RouteParams - -> Shared.Model - -> View (PagesMsg Msg) -view app shared = - { title = "Greetings" - , body = - [ Html.div [] - [ case app.data.name of - Just name -> - Html.text ("Hello " ++ name) - - Nothing -> - Html.text "Hello, I didn't find your name" - ] - ] - } diff --git a/packages/docs-site/app/Route/Hello.elm b/packages/docs-site/app/Route/Hello.elm deleted file mode 100644 index 3119a43..0000000 --- a/packages/docs-site/app/Route/Hello.elm +++ /dev/null @@ -1,119 +0,0 @@ -module Route.Hello exposing (ActionData, Data, Model, Msg(..), RouteParams, action, data, route) - -import BackendTask exposing (BackendTask) -import BackendTask.Http -import Effect exposing (Effect) -import ErrorPage exposing (ErrorPage) -import FatalError exposing (FatalError) -import Head -import Html -import Json.Decode as Decode -import PagesMsg exposing (PagesMsg) -import RouteBuilder exposing (App) -import Server.Request exposing (Request) -import Server.Response -import Shared -import UrlPath exposing (UrlPath) -import View exposing (View) - - -type alias Model = - {} - - -type Msg - = NoOp - - -type alias RouteParams = - {} - - -route = - RouteBuilder.serverRender { data = data, action = action, head = head } - |> RouteBuilder.buildWithLocalState - { view = view - , subscriptions = subscriptions - , update = update - , init = init - } - - -init : - App Data ActionData RouteParams - -> Shared.Model - -> ( Model, Effect Msg ) -init app shared = - ( {}, Effect.none ) - - -update : - App Data ActionData RouteParams - -> Shared.Model - -> Msg - -> Model - -> ( Model, Effect Msg ) -update app shared msg model = - case msg of - NoOp -> - ( model, Effect.none ) - - -subscriptions : - RouteParams - -> UrlPath - -> Shared.Model - -> Model - -> Sub Msg -subscriptions routeParams path shared model = - Sub.none - - -type alias Data = - { stars : Int - } - - -type alias ActionData = - {} - - -data : - RouteParams - -> Request - -> BackendTask FatalError (Server.Response.Response Data ErrorPage) -data routeParams request = - BackendTask.Http.getWithOptions - { url = "https://api.github.com/repos/dillonkearns/elm-pages" - , expect = BackendTask.Http.expectJson (Decode.field "stargazers_count" Decode.int) - , headers = [] - , cacheStrategy = Just BackendTask.Http.IgnoreCache - , retries = Nothing - , timeoutInMs = Nothing - , cachePath = Nothing - } - |> BackendTask.allowFatal - |> BackendTask.map - (\stars -> Server.Response.render { stars = stars }) - - -head : App Data ActionData RouteParams -> List Head.Tag -head app = - [] - - -view : - App Data ActionData RouteParams - -> Shared.Model - -> Model - -> View (PagesMsg Msg) -view app shared model = - { title = "Hello", body = [ Html.text (String.fromInt app.data.stars) ] } - - -action : - RouteParams - -> Request - -> BackendTask.BackendTask FatalError.FatalError (Server.Response.Response ActionData ErrorPage.ErrorPage) -action routeParams request = - BackendTask.succeed (Server.Response.render {}) diff --git a/packages/docs-site/content/contributing/code-style.md b/packages/docs-site/content/contributing/code-style.md index ef2b695..19971ec 100644 --- a/packages/docs-site/content/contributing/code-style.md +++ b/packages/docs-site/content/contributing/code-style.md @@ -114,7 +114,7 @@ assert.strictEqual(result, 42); ### Test File Location -Test files live in `packages//test/` directories, mirroring the source structure. +Test files are co-located with source files in `packages//src/` (e.g. `davinci-bridge.test.ts` next to `davinci-bridge.ts`). ## Elm Conventions diff --git a/packages/docs-site/content/contributing/repository-structure.md b/packages/docs-site/content/contributing/repository-structure.md index dcd5ce2..5bea3b1 100644 --- a/packages/docs-site/content/contributing/repository-structure.md +++ b/packages/docs-site/content/contributing/repository-structure.md @@ -17,8 +17,10 @@ The wolfcola-devtools repository is a pnpm workspace monorepo. All publishable p | `@wolfcola/eslint-plugin-treeshake` | `packages/eslint-plugin-treeshake` | ESLint plugin that flags patterns breaking tree-shaking | | `@wolfcola/devtools-bridge` | `packages/devtools-bridge` | SDK adapter for emitting AuthEvent objects to DevTools | | `@wolfcola/devtools-types` | `packages/devtools-types` | Effect Schema definitions for AuthEvent and FlowState | +| `@wolfcola/devtools-core` | `packages/devtools-core` | Shared annotators, diagnosis engine, event store | +| `@wolfcola/devtools-ui` | `packages/devtools-ui` | Elm UI components for Timeline, Flow, and Learn views | | `@wolfcola/devtools-extension` | `packages/devtools-extension` | Browser extension for Chrome and Firefox | -| `@wolfcola/devtools-vscode` | `packages/devtools-vscode` | VS Code extension with CDP connection | +| `oidc-devtools` | `packages/vscode-extension` | VS Code extension with CDP connection | | `@wolfcola/docs-site` | `packages/docs-site` | This documentation site (elm-pages) | ## Root Files @@ -52,19 +54,24 @@ The packages have the following dependency relationships: ``` devtools-extension + ├── devtools-core + ├── devtools-ui (Elm) └── devtools-types devtools-bridge └── devtools-types -devtools-vscode +vscode-extension + └── devtools-types + +devtools-core └── devtools-types treeshake-check (standalone) eslint-plugin-treeshake - (standalone) + (standalone, optional dep on treeshake-check) ``` The `devtools-types` package is the shared foundation. It defines the `AuthEvent` and `FlowState` schemas that the bridge, browser extension, and VS Code extension all depend on. diff --git a/packages/docs-site/content/docs/devtools-extension.md b/packages/docs-site/content/docs/devtools-extension.md index cd653ee..2aedf6d 100644 --- a/packages/docs-site/content/docs/devtools-extension.md +++ b/packages/docs-site/content/docs/devtools-extension.md @@ -30,7 +30,7 @@ pnpm install pnpm --filter @wolfcola/devtools-extension build ``` -Then load the unpacked extension from `packages/devtools-extension/dist/` in your browser's extension management page. +Then load the unpacked extension from `packages/devtools-extension/` in your browser's extension management page (the `manifest.json` is in the package root). ## Using the DevTools Panel @@ -44,7 +44,7 @@ The extension provides three views, accessible via tabs at the top of the panel. The Timeline view shows a chronological list of every `AuthEvent` emitted by the bridge. Each event displays: -- **Event type** (e.g. `authorize`, `token_exchange`, `refresh`) +- **Event type** (e.g. `sdk:node-change`, `session:cookie`, `sdk:oidc-state`) - **Timestamp** relative to the page load - **Payload** expandable JSON tree with the full event data diff --git a/packages/docs-site/content/docs/getting-started.md b/packages/docs-site/content/docs/getting-started.md index b268ee8..3d53841 100644 --- a/packages/docs-site/content/docs/getting-started.md +++ b/packages/docs-site/content/docs/getting-started.md @@ -15,10 +15,16 @@ Install the wolfcola devtools packages you need. npm install -D @wolfcola/treeshake-check ``` -Run the CLI: +Run the CLI from your package directory: ```bash -npx treeshake-check your-package +npx treeshake-check +``` + +Or point it at a specific package: + +```bash +npx treeshake-check --cwd packages/my-lib ``` ## OIDC DevTools @@ -35,3 +41,4 @@ npm install @wolfcola/devtools-bridge - Read the [Tree-Shaking Guide](/docs/tree-shaking) - Explore the [Architecture](/architecture) +- Learn about the [DevTools Extension](/docs/devtools-extension) diff --git a/packages/docs-site/content/docs/tree-shaking.md b/packages/docs-site/content/docs/tree-shaking.md index df3e3c6..329647a 100644 --- a/packages/docs-site/content/docs/tree-shaking.md +++ b/packages/docs-site/content/docs/tree-shaking.md @@ -30,15 +30,21 @@ npm install -D @wolfcola/treeshake-check ### Running the Check ```bash -npx treeshake-check your-package-name +npx treeshake-check +``` + +Run from inside your package directory. Or specify a path: + +```bash +npx treeshake-check --cwd packages/my-lib ``` The tool will: -1. Discover all named exports from your package -2. Create a minimal Rollup bundle importing each export individually -3. Compare the bundle size against a threshold -4. Report which exports are tree-shakeable and which are not +1. Read `package.json` to find the entry point (`module`, `main`, or `exports`) +2. Bundle the entry through Rollup as the sole import +3. Analyze the output — if rendered bytes are zero, the package is fully tree-shakeable +4. Report which modules survived shaking, with per-file diagnostics and suggested fixes ### CI Integration @@ -47,7 +53,7 @@ Add a check to your CI pipeline to catch tree-shaking regressions: ```json { "scripts": { - "check:treeshake": "treeshake-check your-package-name" + "check:treeshake": "treeshake-check" } } ``` @@ -78,10 +84,12 @@ export default [treeshake.configs.recommended]; The plugin includes rules that flag: -- **Top-level side effects** — code that runs at module evaluation time prevents the export from being eliminated -- **Mutable module-scope variables** — `let` or `var` at module scope that are read by exported functions create implicit dependencies -- **Class static initializers with side effects** — static blocks or property initializers that call external functions -- **Barrel file anti-patterns** — re-export-all patterns (`export * from`) that defeat bundler analysis +- **Enum declarations** — TypeScript `enum` compiles to an IIFE that bundlers cannot eliminate +- **Unannotated top-level calls** — function calls at module scope without `/*#__PURE__*/` annotation +- **Prototype mutations** — `Object.defineProperty`, `Object.defineProperties`, `Object.setPrototypeOf`, and `.prototype` assignments +- **Global assignments** — writes to `window`, `globalThis`, `self`, or `global` +- **CommonJS patterns** — `require()`, `module.exports`, and `exports` usage +- **Missing `sideEffects` field** — warns when `package.json` lacks `"sideEffects": false` The `treeshake-check` CLI and `eslint-plugin-treeshake` complement each other. The CLI tests your built output; the ESLint plugin catches problems in source code. diff --git a/packages/docs-site/content/docs/vscode-extension.md b/packages/docs-site/content/docs/vscode-extension.md index 7bd7fde..fced252 100644 --- a/packages/docs-site/content/docs/vscode-extension.md +++ b/packages/docs-site/content/docs/vscode-extension.md @@ -15,13 +15,13 @@ Install from the VS Code Marketplace: 1. Open VS Code 2. Go to Extensions (Ctrl+Shift+X / Cmd+Shift+X) -3. Search for "wolfcola devtools" +3. Search for "OIDC DevTools" 4. Click Install Alternatively, install from the command line: ```bash -code --install-extension wolfcola.devtools-vscode +code --install-extension ryanbasmajian.oidc-devtools ``` ## CDP WebSocket Connection @@ -64,7 +64,7 @@ A webview panel renders the current `FlowState` as an interactive diagram, simil ### CodeLens Integration -When the extension detects that your workspace contains `@wolfcola/devtools-bridge` import statements, it adds CodeLens annotations above `createBridge()` calls showing the connection status and last event received. +When the extension detects that your workspace contains `@wolfcola/devtools-bridge` import statements, it adds CodeLens annotations above bridge attachment calls (e.g. `attachDaVinciBridge()`) showing the connection status and last event received. ### Diagnostics diff --git a/packages/docs-site/content/packages/eslint-plugin-treeshake.md b/packages/docs-site/content/packages/eslint-plugin-treeshake.md index 19e6b00..a61effa 100644 --- a/packages/docs-site/content/packages/eslint-plugin-treeshake.md +++ b/packages/docs-site/content/packages/eslint-plugin-treeshake.md @@ -43,15 +43,15 @@ A single rule that detects multiple categories of tree-shaking hazards. Each cat ### Detected Patterns -| Pattern | Option | Default | Description | -| --------------------- | ------------------------ | ------- | ----------------------------------------------------------------------- | -| Enum declarations | `checkEnums` | `true` | TypeScript `enum` compiles to an IIFE that bundlers cannot remove | -| Unannotated calls | `checkUnannotatedCalls` | `true` | Top-level function calls without `/*#__PURE__*/` annotation | -| Prototype mutations | `checkPrototypeMutation` | `true` | `Object.defineProperty`, `Object.setPrototypeOf`, `X.prototype.y = ...` | -| Global assignments | `checkGlobalAssignment` | `true` | Assignments to `window`, `globalThis`, `self`, `global` | -| CommonJS patterns | `checkCjsPatterns` | `true` | `require()`, `module.exports`, `exports.x` | -| Missing `sideEffects` | `checkSideEffectsField` | `true` | Warns when nearest `package.json` lacks a `sideEffects` field | -| Bundle check (opt-in) | `bundleCheck` | `false` | Runs `treeshake-check --json` and maps results to source locations | +| Pattern | Option | Default | Description | +| --------------------- | ------------------------ | ------- | -------------------------------------------------------------------------------------------------- | +| Enum declarations | `checkEnums` | `true` | TypeScript `enum` compiles to an IIFE that bundlers cannot remove | +| Unannotated calls | `checkUnannotatedCalls` | `true` | Top-level function calls without `/*#__PURE__*/` annotation | +| Prototype mutations | `checkPrototypeMutation` | `true` | `Object.defineProperty`, `Object.defineProperties`, `Object.setPrototypeOf`, `X.prototype.y = ...` | +| Global assignments | `checkGlobalAssignment` | `true` | Assignments to `window`, `globalThis`, `self`, `global` | +| CommonJS patterns | `checkCjsPatterns` | `true` | `require()`, `module.exports`, `exports.x` | +| Missing `sideEffects` | `checkSideEffectsField` | `true` | Warns when nearest `package.json` lacks a `sideEffects` field | +| Bundle check (opt-in) | `bundleCheck` | `false` | Runs `treeshake-check --json` and maps results to source locations | ### Rule Options From 811d3bfffa13d8b887941fe0702f89961cd97a0a Mon Sep 17 00:00:00 2001 From: Ryan Bas Date: Tue, 12 May 2026 08:43:31 -0600 Subject: [PATCH 21/23] =?UTF-8?q?fix(docs-site):=20correct=20bridge=20desc?= =?UTF-8?q?ription=20=E2=80=94=20not=20framework-agnostic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The bridge requires Ping Identity SDK Subscribable interfaces, not arbitrary OIDC clients. Updated callout to list the actual supported SDKs. Co-Authored-By: Claude Opus 4.6 (1M context) --- packages/docs-site/content/docs/getting-started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/docs-site/content/docs/getting-started.md b/packages/docs-site/content/docs/getting-started.md index 3d53841..7305130 100644 --- a/packages/docs-site/content/docs/getting-started.md +++ b/packages/docs-site/content/docs/getting-started.md @@ -35,7 +35,7 @@ Install the bridge SDK to emit events from your OIDC client: npm install @wolfcola/devtools-bridge ``` -The devtools bridge is framework-agnostic and works with any OIDC client. +The bridge provides adapters for Ping Identity SDKs: DaVinci (`@forgerock/davinci-client`), Journey, and OIDC (`@forgerock/oidc-client`). See the integration guides for setup details. ## Next Steps From a13070c423b47054009417e2830b156afb45e2bc Mon Sep 17 00:00:00 2001 From: Ryan Bas Date: Tue, 12 May 2026 08:58:54 -0600 Subject: [PATCH 22/23] fix: exclude docs-site from ESLint to prevent CI hang The elm-pages docs-site contains Elm files and generated JS that the TypeScript ESLint parser cannot handle, causing ESLint to hang indefinitely in CI. Exclude the entire package since it uses Elm (not TypeScript) and has its own tooling. Co-Authored-By: Claude Opus 4.6 (1M context) --- eslint.config.mjs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/eslint.config.mjs b/eslint.config.mjs index c14a67d..08e756a 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -21,6 +21,10 @@ export default [ '**/vite.config.*.timestamp*', '**/__fixtures__/**', '**/out-tsc/**', + '**/.elm-pages/**', + '**/functions/**', + '**/codegen/**', + 'packages/docs-site/**', ], }, ...compat.extends('plugin:@typescript-eslint/recommended', 'plugin:prettier/recommended'), From 1173aa2590596d72ad2fc7dc45eff07b868a6db5 Mon Sep 17 00:00:00 2001 From: Ryan Bas Date: Tue, 12 May 2026 09:03:36 -0600 Subject: [PATCH 23/23] refactor: move docs-site from packages/ to apps/docs/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The docs site is a deployable Elm app, not a publishable TypeScript library. Moving it to apps/ separates it from the package pipeline (lint, test, typecheck) and avoids ESLint/TypeScript tooling conflicts. - Move packages/docs-site → apps/docs - Add apps/* to pnpm-workspace.yaml - Update ESLint ignores, CI workflow paths, and content references Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/docs.yml | 4 +- {packages/docs-site => apps/docs}/.gitignore | 0 {packages/docs-site => apps/docs}/README.md | 0 {packages/docs-site => apps/docs}/app/Api.elm | 0 .../docs-site => apps/docs}/app/Effect.elm | 0 .../docs-site => apps/docs}/app/ErrorPage.elm | 0 .../docs}/app/Route/Architecture.elm | 0 .../docs}/app/Route/Contributing/Slug_.elm | 0 .../docs}/app/Route/Docs/Slug_.elm | 0 .../docs}/app/Route/Index.elm | 0 .../docs}/app/Route/Packages/Slug_.elm | 0 .../docs-site => apps/docs}/app/Shared.elm | 0 .../docs-site => apps/docs}/app/Site.elm | 0 .../docs-site => apps/docs}/app/View.elm | 0 .../docs}/codegen/elm.codegen.json | 0 .../docs}/content/contributing/code-style.md | 0 .../content/contributing/development-setup.md | 0 .../content/contributing/release-process.md | 0 .../contributing/repository-structure.md | 8 ++-- .../docs}/content/docs/davinci-integration.md | 0 .../docs}/content/docs/devtools-extension.md | 0 .../docs}/content/docs/getting-started.md | 0 .../docs}/content/docs/journey-integration.md | 0 .../docs}/content/docs/oidc-integration.md | 0 .../docs}/content/docs/tree-shaking.md | 0 .../docs}/content/docs/vscode-extension.md | 0 .../docs}/content/packages/devtools-bridge.md | 0 .../docs}/content/packages/devtools-types.md | 0 .../packages/eslint-plugin-treeshake.md | 0 .../docs}/content/packages/treeshake-check.md | 0 .../docs}/custom-backend-task.js | 0 .../docs}/custom-backend-task.ts | 0 .../docs}/elm-pages.config.mjs | 0 {packages/docs-site => apps/docs}/elm.json | 0 {packages/docs-site => apps/docs}/index.js | 0 {packages/docs-site => apps/docs}/index.ts | 0 .../docs-site => apps/docs}/netlify.toml | 0 .../docs-site => apps/docs}/package.json | 0 .../docs}/public/favicon.ico | Bin .../docs}/script/custom-backend-task.js | 0 .../docs}/script/custom-backend-task.ts | 0 .../docs-site => apps/docs}/script/elm.json | 0 .../docs}/script/src/AddRoute.elm | 0 .../docs}/script/src/AddStaticRoute.elm | 0 .../docs}/script/src/Stars.elm | 0 .../docs-site => apps/docs}/src/.gitkeep | 0 .../docs}/src/MarkdownRenderer.elm | 0 .../docs-site => apps/docs}/src/Search.elm | 0 {packages/docs-site => apps/docs}/style.css | 0 eslint.config.mjs | 2 +- pnpm-lock.yaml | 38 +++++++++--------- pnpm-workspace.yaml | 1 + 52 files changed, 27 insertions(+), 26 deletions(-) rename {packages/docs-site => apps/docs}/.gitignore (100%) rename {packages/docs-site => apps/docs}/README.md (100%) rename {packages/docs-site => apps/docs}/app/Api.elm (100%) rename {packages/docs-site => apps/docs}/app/Effect.elm (100%) rename {packages/docs-site => apps/docs}/app/ErrorPage.elm (100%) rename {packages/docs-site => apps/docs}/app/Route/Architecture.elm (100%) rename {packages/docs-site => apps/docs}/app/Route/Contributing/Slug_.elm (100%) rename {packages/docs-site => apps/docs}/app/Route/Docs/Slug_.elm (100%) rename {packages/docs-site => apps/docs}/app/Route/Index.elm (100%) rename {packages/docs-site => apps/docs}/app/Route/Packages/Slug_.elm (100%) rename {packages/docs-site => apps/docs}/app/Shared.elm (100%) rename {packages/docs-site => apps/docs}/app/Site.elm (100%) rename {packages/docs-site => apps/docs}/app/View.elm (100%) rename {packages/docs-site => apps/docs}/codegen/elm.codegen.json (100%) rename {packages/docs-site => apps/docs}/content/contributing/code-style.md (100%) rename {packages/docs-site => apps/docs}/content/contributing/development-setup.md (100%) rename {packages/docs-site => apps/docs}/content/contributing/release-process.md (100%) rename {packages/docs-site => apps/docs}/content/contributing/repository-structure.md (93%) rename {packages/docs-site => apps/docs}/content/docs/davinci-integration.md (100%) rename {packages/docs-site => apps/docs}/content/docs/devtools-extension.md (100%) rename {packages/docs-site => apps/docs}/content/docs/getting-started.md (100%) rename {packages/docs-site => apps/docs}/content/docs/journey-integration.md (100%) rename {packages/docs-site => apps/docs}/content/docs/oidc-integration.md (100%) rename {packages/docs-site => apps/docs}/content/docs/tree-shaking.md (100%) rename {packages/docs-site => apps/docs}/content/docs/vscode-extension.md (100%) rename {packages/docs-site => apps/docs}/content/packages/devtools-bridge.md (100%) rename {packages/docs-site => apps/docs}/content/packages/devtools-types.md (100%) rename {packages/docs-site => apps/docs}/content/packages/eslint-plugin-treeshake.md (100%) rename {packages/docs-site => apps/docs}/content/packages/treeshake-check.md (100%) rename {packages/docs-site => apps/docs}/custom-backend-task.js (100%) rename {packages/docs-site => apps/docs}/custom-backend-task.ts (100%) rename {packages/docs-site => apps/docs}/elm-pages.config.mjs (100%) rename {packages/docs-site => apps/docs}/elm.json (100%) rename {packages/docs-site => apps/docs}/index.js (100%) rename {packages/docs-site => apps/docs}/index.ts (100%) rename {packages/docs-site => apps/docs}/netlify.toml (100%) rename {packages/docs-site => apps/docs}/package.json (100%) rename {packages/docs-site => apps/docs}/public/favicon.ico (100%) rename {packages/docs-site => apps/docs}/script/custom-backend-task.js (100%) rename {packages/docs-site => apps/docs}/script/custom-backend-task.ts (100%) rename {packages/docs-site => apps/docs}/script/elm.json (100%) rename {packages/docs-site => apps/docs}/script/src/AddRoute.elm (100%) rename {packages/docs-site => apps/docs}/script/src/AddStaticRoute.elm (100%) rename {packages/docs-site => apps/docs}/script/src/Stars.elm (100%) rename {packages/docs-site => apps/docs}/src/.gitkeep (100%) rename {packages/docs-site => apps/docs}/src/MarkdownRenderer.elm (100%) rename {packages/docs-site => apps/docs}/src/Search.elm (100%) rename {packages/docs-site => apps/docs}/style.css (100%) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 5c47f79..7824d19 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -4,7 +4,7 @@ on: push: branches: [main] paths: - - 'packages/docs-site/**' + - 'apps/docs/**' permissions: contents: read @@ -35,7 +35,7 @@ jobs: - uses: actions/upload-pages-artifact@v3 with: - path: packages/docs-site/dist + path: apps/docs/dist deploy: environment: diff --git a/packages/docs-site/.gitignore b/apps/docs/.gitignore similarity index 100% rename from packages/docs-site/.gitignore rename to apps/docs/.gitignore diff --git a/packages/docs-site/README.md b/apps/docs/README.md similarity index 100% rename from packages/docs-site/README.md rename to apps/docs/README.md diff --git a/packages/docs-site/app/Api.elm b/apps/docs/app/Api.elm similarity index 100% rename from packages/docs-site/app/Api.elm rename to apps/docs/app/Api.elm diff --git a/packages/docs-site/app/Effect.elm b/apps/docs/app/Effect.elm similarity index 100% rename from packages/docs-site/app/Effect.elm rename to apps/docs/app/Effect.elm diff --git a/packages/docs-site/app/ErrorPage.elm b/apps/docs/app/ErrorPage.elm similarity index 100% rename from packages/docs-site/app/ErrorPage.elm rename to apps/docs/app/ErrorPage.elm diff --git a/packages/docs-site/app/Route/Architecture.elm b/apps/docs/app/Route/Architecture.elm similarity index 100% rename from packages/docs-site/app/Route/Architecture.elm rename to apps/docs/app/Route/Architecture.elm diff --git a/packages/docs-site/app/Route/Contributing/Slug_.elm b/apps/docs/app/Route/Contributing/Slug_.elm similarity index 100% rename from packages/docs-site/app/Route/Contributing/Slug_.elm rename to apps/docs/app/Route/Contributing/Slug_.elm diff --git a/packages/docs-site/app/Route/Docs/Slug_.elm b/apps/docs/app/Route/Docs/Slug_.elm similarity index 100% rename from packages/docs-site/app/Route/Docs/Slug_.elm rename to apps/docs/app/Route/Docs/Slug_.elm diff --git a/packages/docs-site/app/Route/Index.elm b/apps/docs/app/Route/Index.elm similarity index 100% rename from packages/docs-site/app/Route/Index.elm rename to apps/docs/app/Route/Index.elm diff --git a/packages/docs-site/app/Route/Packages/Slug_.elm b/apps/docs/app/Route/Packages/Slug_.elm similarity index 100% rename from packages/docs-site/app/Route/Packages/Slug_.elm rename to apps/docs/app/Route/Packages/Slug_.elm diff --git a/packages/docs-site/app/Shared.elm b/apps/docs/app/Shared.elm similarity index 100% rename from packages/docs-site/app/Shared.elm rename to apps/docs/app/Shared.elm diff --git a/packages/docs-site/app/Site.elm b/apps/docs/app/Site.elm similarity index 100% rename from packages/docs-site/app/Site.elm rename to apps/docs/app/Site.elm diff --git a/packages/docs-site/app/View.elm b/apps/docs/app/View.elm similarity index 100% rename from packages/docs-site/app/View.elm rename to apps/docs/app/View.elm diff --git a/packages/docs-site/codegen/elm.codegen.json b/apps/docs/codegen/elm.codegen.json similarity index 100% rename from packages/docs-site/codegen/elm.codegen.json rename to apps/docs/codegen/elm.codegen.json diff --git a/packages/docs-site/content/contributing/code-style.md b/apps/docs/content/contributing/code-style.md similarity index 100% rename from packages/docs-site/content/contributing/code-style.md rename to apps/docs/content/contributing/code-style.md diff --git a/packages/docs-site/content/contributing/development-setup.md b/apps/docs/content/contributing/development-setup.md similarity index 100% rename from packages/docs-site/content/contributing/development-setup.md rename to apps/docs/content/contributing/development-setup.md diff --git a/packages/docs-site/content/contributing/release-process.md b/apps/docs/content/contributing/release-process.md similarity index 100% rename from packages/docs-site/content/contributing/release-process.md rename to apps/docs/content/contributing/release-process.md diff --git a/packages/docs-site/content/contributing/repository-structure.md b/apps/docs/content/contributing/repository-structure.md similarity index 93% rename from packages/docs-site/content/contributing/repository-structure.md rename to apps/docs/content/contributing/repository-structure.md index 5bea3b1..938cfbf 100644 --- a/packages/docs-site/content/contributing/repository-structure.md +++ b/apps/docs/content/contributing/repository-structure.md @@ -21,7 +21,7 @@ The wolfcola-devtools repository is a pnpm workspace monorepo. All publishable p | `@wolfcola/devtools-ui` | `packages/devtools-ui` | Elm UI components for Timeline, Flow, and Learn views | | `@wolfcola/devtools-extension` | `packages/devtools-extension` | Browser extension for Chrome and Firefox | | `oidc-devtools` | `packages/vscode-extension` | VS Code extension with CDP connection | -| `@wolfcola/docs-site` | `packages/docs-site` | This documentation site (elm-pages) | +| `@wolfcola/docs-site` | `apps/docs` | This documentation site (elm-pages) | ## Root Files @@ -82,6 +82,6 @@ The `devtools-types` package is the shared foundation. It defines the `AuthEvent 2. Add a `package.json` with the `@wolfcola` scope 3. Add the package to `pnpm-workspace.yaml` if not using a glob pattern 4. Add a `tsconfig.json` extending the root config -5. Add the package to the sidebar in `packages/docs-site/app/Shared.elm` -6. Create a content page in `packages/docs-site/content/packages/` -7. Create a content page in `packages/docs-site/content/docs/` for integration guides +5. Add the package to the sidebar in `apps/docs/app/Shared.elm` +6. Create a content page in `apps/docs/content/packages/` +7. Create a content page in `apps/docs/content/docs/` for integration guides diff --git a/packages/docs-site/content/docs/davinci-integration.md b/apps/docs/content/docs/davinci-integration.md similarity index 100% rename from packages/docs-site/content/docs/davinci-integration.md rename to apps/docs/content/docs/davinci-integration.md diff --git a/packages/docs-site/content/docs/devtools-extension.md b/apps/docs/content/docs/devtools-extension.md similarity index 100% rename from packages/docs-site/content/docs/devtools-extension.md rename to apps/docs/content/docs/devtools-extension.md diff --git a/packages/docs-site/content/docs/getting-started.md b/apps/docs/content/docs/getting-started.md similarity index 100% rename from packages/docs-site/content/docs/getting-started.md rename to apps/docs/content/docs/getting-started.md diff --git a/packages/docs-site/content/docs/journey-integration.md b/apps/docs/content/docs/journey-integration.md similarity index 100% rename from packages/docs-site/content/docs/journey-integration.md rename to apps/docs/content/docs/journey-integration.md diff --git a/packages/docs-site/content/docs/oidc-integration.md b/apps/docs/content/docs/oidc-integration.md similarity index 100% rename from packages/docs-site/content/docs/oidc-integration.md rename to apps/docs/content/docs/oidc-integration.md diff --git a/packages/docs-site/content/docs/tree-shaking.md b/apps/docs/content/docs/tree-shaking.md similarity index 100% rename from packages/docs-site/content/docs/tree-shaking.md rename to apps/docs/content/docs/tree-shaking.md diff --git a/packages/docs-site/content/docs/vscode-extension.md b/apps/docs/content/docs/vscode-extension.md similarity index 100% rename from packages/docs-site/content/docs/vscode-extension.md rename to apps/docs/content/docs/vscode-extension.md diff --git a/packages/docs-site/content/packages/devtools-bridge.md b/apps/docs/content/packages/devtools-bridge.md similarity index 100% rename from packages/docs-site/content/packages/devtools-bridge.md rename to apps/docs/content/packages/devtools-bridge.md diff --git a/packages/docs-site/content/packages/devtools-types.md b/apps/docs/content/packages/devtools-types.md similarity index 100% rename from packages/docs-site/content/packages/devtools-types.md rename to apps/docs/content/packages/devtools-types.md diff --git a/packages/docs-site/content/packages/eslint-plugin-treeshake.md b/apps/docs/content/packages/eslint-plugin-treeshake.md similarity index 100% rename from packages/docs-site/content/packages/eslint-plugin-treeshake.md rename to apps/docs/content/packages/eslint-plugin-treeshake.md diff --git a/packages/docs-site/content/packages/treeshake-check.md b/apps/docs/content/packages/treeshake-check.md similarity index 100% rename from packages/docs-site/content/packages/treeshake-check.md rename to apps/docs/content/packages/treeshake-check.md diff --git a/packages/docs-site/custom-backend-task.js b/apps/docs/custom-backend-task.js similarity index 100% rename from packages/docs-site/custom-backend-task.js rename to apps/docs/custom-backend-task.js diff --git a/packages/docs-site/custom-backend-task.ts b/apps/docs/custom-backend-task.ts similarity index 100% rename from packages/docs-site/custom-backend-task.ts rename to apps/docs/custom-backend-task.ts diff --git a/packages/docs-site/elm-pages.config.mjs b/apps/docs/elm-pages.config.mjs similarity index 100% rename from packages/docs-site/elm-pages.config.mjs rename to apps/docs/elm-pages.config.mjs diff --git a/packages/docs-site/elm.json b/apps/docs/elm.json similarity index 100% rename from packages/docs-site/elm.json rename to apps/docs/elm.json diff --git a/packages/docs-site/index.js b/apps/docs/index.js similarity index 100% rename from packages/docs-site/index.js rename to apps/docs/index.js diff --git a/packages/docs-site/index.ts b/apps/docs/index.ts similarity index 100% rename from packages/docs-site/index.ts rename to apps/docs/index.ts diff --git a/packages/docs-site/netlify.toml b/apps/docs/netlify.toml similarity index 100% rename from packages/docs-site/netlify.toml rename to apps/docs/netlify.toml diff --git a/packages/docs-site/package.json b/apps/docs/package.json similarity index 100% rename from packages/docs-site/package.json rename to apps/docs/package.json diff --git a/packages/docs-site/public/favicon.ico b/apps/docs/public/favicon.ico similarity index 100% rename from packages/docs-site/public/favicon.ico rename to apps/docs/public/favicon.ico diff --git a/packages/docs-site/script/custom-backend-task.js b/apps/docs/script/custom-backend-task.js similarity index 100% rename from packages/docs-site/script/custom-backend-task.js rename to apps/docs/script/custom-backend-task.js diff --git a/packages/docs-site/script/custom-backend-task.ts b/apps/docs/script/custom-backend-task.ts similarity index 100% rename from packages/docs-site/script/custom-backend-task.ts rename to apps/docs/script/custom-backend-task.ts diff --git a/packages/docs-site/script/elm.json b/apps/docs/script/elm.json similarity index 100% rename from packages/docs-site/script/elm.json rename to apps/docs/script/elm.json diff --git a/packages/docs-site/script/src/AddRoute.elm b/apps/docs/script/src/AddRoute.elm similarity index 100% rename from packages/docs-site/script/src/AddRoute.elm rename to apps/docs/script/src/AddRoute.elm diff --git a/packages/docs-site/script/src/AddStaticRoute.elm b/apps/docs/script/src/AddStaticRoute.elm similarity index 100% rename from packages/docs-site/script/src/AddStaticRoute.elm rename to apps/docs/script/src/AddStaticRoute.elm diff --git a/packages/docs-site/script/src/Stars.elm b/apps/docs/script/src/Stars.elm similarity index 100% rename from packages/docs-site/script/src/Stars.elm rename to apps/docs/script/src/Stars.elm diff --git a/packages/docs-site/src/.gitkeep b/apps/docs/src/.gitkeep similarity index 100% rename from packages/docs-site/src/.gitkeep rename to apps/docs/src/.gitkeep diff --git a/packages/docs-site/src/MarkdownRenderer.elm b/apps/docs/src/MarkdownRenderer.elm similarity index 100% rename from packages/docs-site/src/MarkdownRenderer.elm rename to apps/docs/src/MarkdownRenderer.elm diff --git a/packages/docs-site/src/Search.elm b/apps/docs/src/Search.elm similarity index 100% rename from packages/docs-site/src/Search.elm rename to apps/docs/src/Search.elm diff --git a/packages/docs-site/style.css b/apps/docs/style.css similarity index 100% rename from packages/docs-site/style.css rename to apps/docs/style.css diff --git a/eslint.config.mjs b/eslint.config.mjs index 08e756a..869ea05 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -24,7 +24,7 @@ export default [ '**/.elm-pages/**', '**/functions/**', '**/codegen/**', - 'packages/docs-site/**', + 'apps/**', ], }, ...compat.extends('plugin:@typescript-eslint/recommended', 'plugin:prettier/recommended'), diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3f3ba57..f6285f2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -92,6 +92,25 @@ importers: specifier: catalog:vitest version: 3.2.4(@types/node@22.19.18)(jsdom@26.1.0)(terser@5.47.1)(yaml@2.9.0) + apps/docs: + dependencies: + prismjs: + specifier: ^1.30.0 + version: 1.30.0 + devDependencies: + elm-codegen: + specifier: ^0.6.3 + version: 0.6.3 + elm-pages: + specifier: 3.5.1 + version: 3.5.1(@types/node@22.19.18)(tslib@2.8.1)(yaml@2.9.0) + elm-review: + specifier: ^2.13.5 + version: 2.13.5 + lamdera: + specifier: ^0.19.1-1.4.0 + version: 0.19.1-1.4.0 + e2e: devDependencies: '@hono/node-server': @@ -171,25 +190,6 @@ importers: specifier: ^5.47.1 version: 5.47.1 - packages/docs-site: - dependencies: - prismjs: - specifier: ^1.30.0 - version: 1.30.0 - devDependencies: - elm-codegen: - specifier: ^0.6.3 - version: 0.6.3 - elm-pages: - specifier: 3.5.1 - version: 3.5.1(@types/node@22.19.18)(tslib@2.8.1)(yaml@2.9.0) - elm-review: - specifier: ^2.13.5 - version: 2.13.5 - lamdera: - specifier: ^0.19.1-1.4.0 - version: 0.19.1-1.4.0 - packages/eslint-plugin-treeshake: dependencies: '@typescript-eslint/parser': diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 8ed4113..cb34666 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,5 +1,6 @@ packages: - 'packages/*' + - 'apps/*' - 'e2e' catalogs: