elm-pages 3.0.0-beta.40 → 3.0.0-beta.42

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/README.md +1 -1
  2. package/codegen/elm-pages-codegen.cjs +251 -285
  3. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/d.dat +0 -0
  4. package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/node_runner.js +1 -1
  5. package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/node_supervisor.js +1 -1
  6. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/d.dat +0 -0
  7. package/generator/review/elm-stuff/tests-0.19.1/js/node_runner.js +1 -1
  8. package/generator/review/elm-stuff/tests-0.19.1/js/node_supervisor.js +1 -1
  9. package/generator/src/RouteBuilder.elm +19 -60
  10. package/generator/src/SharedTemplate.elm +5 -5
  11. package/generator/src/compatibility-key.js +2 -2
  12. package/package.json +2 -2
  13. package/src/ApiRoute.elm +3 -31
  14. package/src/BackendTask.elm +18 -24
  15. package/src/FormData.elm +21 -1
  16. package/src/Head/Seo.elm +4 -4
  17. package/src/Internal/Request.elm +84 -4
  18. package/src/Pages/ConcurrentSubmission.elm +127 -0
  19. package/src/Pages/Form.elm +151 -40
  20. package/src/Pages/FormData.elm +19 -0
  21. package/src/Pages/Internal/NotFoundReason.elm +4 -4
  22. package/src/Pages/Internal/Platform/Cli.elm +30 -17
  23. package/src/Pages/Internal/Platform/CompatibilityKey.elm +1 -1
  24. package/src/Pages/Internal/Platform.elm +39 -38
  25. package/src/Pages/Internal/ResponseSketch.elm +2 -2
  26. package/src/Pages/Manifest.elm +23 -7
  27. package/src/Pages/Navigation.elm +85 -0
  28. package/src/Pages/PageUrl.elm +3 -3
  29. package/src/Pages/ProgramConfig.elm +13 -11
  30. package/src/Pages/Script.elm +64 -7
  31. package/src/Pages/Url.elm +3 -3
  32. package/src/PagesMsg.elm +9 -3
  33. package/src/RenderRequest.elm +7 -7
  34. package/src/Scaffold/Form.elm +28 -5
  35. package/src/Scaffold/Route.elm +82 -53
  36. package/src/Server/Request.elm +446 -952
  37. package/src/Server/Session.elm +141 -91
  38. package/src/Server/SetCookie.elm +71 -31
  39. package/src/{Path.elm → UrlPath.elm} +21 -21
  40. package/src/Pages/Transition.elm +0 -79
@@ -75,7 +75,7 @@ console.elmlog = (str) => logs.push(str + "\n");
75
75
  const { Elm } = require("./Runner.elm.js");
76
76
 
77
77
  // Start the Elm app
78
- const flags = { initialSeed: 2220181744, fuzzRuns: 100, filter: null };
78
+ const flags = { initialSeed: 848138408, fuzzRuns: 100, filter: null };
79
79
  const app = Elm.Runner.init({ flags: flags });
80
80
 
81
81
  // Record the timing at which we received the last "runTest" message
@@ -82,7 +82,7 @@ const verbosity = 0;
82
82
  // Create a long lived reporter worker
83
83
  const { Elm } = require("./Reporter.elm.js");
84
84
  const flags = {
85
- initialSeed: 2220181744,
85
+ initialSeed: 848138408,
86
86
  fuzzRuns: 100,
87
87
  mode: "consoleColor",
88
88
  verbosity: verbosity,
@@ -75,7 +75,7 @@ console.elmlog = (str) => logs.push(str + "\n");
75
75
  const { Elm } = require("./Runner.elm.js");
76
76
 
77
77
  // Start the Elm app
78
- const flags = { initialSeed: 1451258136, fuzzRuns: 100, filter: null };
78
+ const flags = { initialSeed: 2748899408, fuzzRuns: 100, filter: null };
79
79
  const app = Elm.Runner.init({ flags: flags });
80
80
 
81
81
  // Record the timing at which we received the last "runTest" message
@@ -82,7 +82,7 @@ const verbosity = 0;
82
82
  // Create a long lived reporter worker
83
83
  const { Elm } = require("./Reporter.elm.js");
84
84
  const flags = {
85
- initialSeed: 1451258136,
85
+ initialSeed: 2748899408,
86
86
  fuzzRuns: 100,
87
87
  mode: "consoleColor",
88
88
  verbosity: verbosity,
@@ -94,23 +94,24 @@ import Form
94
94
  import Head
95
95
  import Http
96
96
  import Json.Decode
97
+ import Pages.ConcurrentSubmission
97
98
  import Pages.Fetcher
98
99
  import Pages.Internal.NotFoundReason exposing (NotFoundReason)
99
100
  import Pages.Internal.RoutePattern exposing (RoutePattern)
101
+ import Pages.Navigation
100
102
  import Pages.PageUrl exposing (PageUrl)
101
- import Pages.Transition
102
103
  import PagesMsg exposing (PagesMsg)
103
- import Path exposing (Path)
104
104
  import Server.Request
105
105
  import Server.Response
106
106
  import Shared
107
+ import UrlPath exposing (UrlPath)
107
108
  import View exposing (View)
108
109
 
109
110
 
110
111
  {-| -}
111
112
  type alias StatefulRoute routeParams data action model msg =
112
- { data : Json.Decode.Value -> routeParams -> BackendTask FatalError (Server.Response.Response data ErrorPage)
113
- , action : Json.Decode.Value -> routeParams -> BackendTask FatalError (Server.Response.Response action ErrorPage)
113
+ { data : Server.Request.Request -> routeParams -> BackendTask FatalError (Server.Response.Response data ErrorPage)
114
+ , action : Server.Request.Request -> routeParams -> BackendTask FatalError (Server.Response.Response action ErrorPage)
114
115
  , staticRoutes : BackendTask FatalError (List routeParams)
115
116
  , view :
116
117
  Shared.Model
@@ -122,7 +123,7 @@ type alias StatefulRoute routeParams data action model msg =
122
123
  -> List Head.Tag
123
124
  , init : Shared.Model -> App data action routeParams -> ( model, Effect msg )
124
125
  , update : App data action routeParams -> msg -> model -> Shared.Model -> ( model, Effect msg, Maybe Shared.Msg )
125
- , subscriptions : routeParams -> Path -> model -> Shared.Model -> Sub msg
126
+ , subscriptions : routeParams -> UrlPath -> model -> Shared.Model -> Sub msg
126
127
  , handleRoute : { moduleName : List String, routePattern : RoutePattern } -> (routeParams -> List ( String, String )) -> routeParams -> BackendTask FatalError (Maybe NotFoundReason)
127
128
  , kind : String
128
129
  , onAction : Maybe (action -> msg)
@@ -139,14 +140,14 @@ type alias App data action routeParams =
139
140
  { data : data
140
141
  , sharedData : Shared.Data
141
142
  , routeParams : routeParams
142
- , path : Path
143
+ , path : UrlPath
143
144
  , url : Maybe PageUrl
144
145
  , action : Maybe action
145
146
  , submit :
146
147
  { fields : List ( String, String ), headers : List ( String, String ) }
147
148
  -> Pages.Fetcher.Fetcher (Result Http.Error action)
148
- , transition : Maybe Pages.Transition.Transition
149
- , fetchers : Dict String (Pages.Transition.FetcherState (Maybe action))
149
+ , navigation : Maybe Pages.Navigation.Navigation
150
+ , concurrentSubmissions : Dict String (Pages.ConcurrentSubmission.ConcurrentSubmission (Maybe action))
150
151
  , pageFormState : Form.Model
151
152
  }
152
153
 
@@ -154,8 +155,8 @@ type alias App data action routeParams =
154
155
  {-| -}
155
156
  type Builder routeParams data action
156
157
  = WithData
157
- { data : Json.Decode.Value -> routeParams -> BackendTask FatalError (Server.Response.Response data ErrorPage)
158
- , action : Json.Decode.Value -> routeParams -> BackendTask FatalError (Server.Response.Response action ErrorPage)
158
+ { data : Server.Request.Request -> routeParams -> BackendTask FatalError (Server.Response.Response data ErrorPage)
159
+ , action : Server.Request.Request -> routeParams -> BackendTask FatalError (Server.Response.Response action ErrorPage)
159
160
  , staticRoutes : BackendTask FatalError (List routeParams)
160
161
  , head :
161
162
  App data action routeParams
@@ -213,7 +214,7 @@ buildWithLocalState :
213
214
  -> View (PagesMsg msg)
214
215
  , init : App data action routeParams -> Shared.Model -> ( model, Effect msg )
215
216
  , update : App data action routeParams -> Shared.Model -> msg -> model -> ( model, Effect msg )
216
- , subscriptions : routeParams -> Path -> Shared.Model -> model -> Sub msg
217
+ , subscriptions : routeParams -> UrlPath -> Shared.Model -> model -> Sub msg
217
218
  }
218
219
  -> Builder routeParams data action
219
220
  -> StatefulRoute routeParams data action model msg
@@ -253,7 +254,7 @@ buildWithSharedState :
253
254
  -> View (PagesMsg msg)
254
255
  , init : App data action routeParams -> Shared.Model -> ( model, Effect msg )
255
256
  , update : App data action routeParams -> Shared.Model -> msg -> model -> ( model, Effect msg, Maybe Shared.Msg )
256
- , subscriptions : routeParams -> Path -> Shared.Model -> model -> Sub msg
257
+ , subscriptions : routeParams -> UrlPath -> Shared.Model -> model -> Sub msg
257
258
  }
258
259
  -> Builder routeParams data action
259
260
  -> StatefulRoute routeParams data action model msg
@@ -361,61 +362,19 @@ preRenderWithFallback { data, head, pages } =
361
362
 
362
363
  {-| -}
363
364
  serverRender :
364
- { data : routeParams -> Server.Request.Parser (BackendTask FatalError (Server.Response.Response data ErrorPage))
365
- , action : routeParams -> Server.Request.Parser (BackendTask FatalError (Server.Response.Response action ErrorPage))
365
+ { data : routeParams -> Server.Request.Request -> BackendTask FatalError (Server.Response.Response data ErrorPage)
366
+ , action : routeParams -> Server.Request.Request -> BackendTask FatalError (Server.Response.Response action ErrorPage)
366
367
  , head : App data action routeParams -> List Head.Tag
367
368
  }
368
369
  -> Builder routeParams data action
369
370
  serverRender { data, action, head } =
370
371
  WithData
371
372
  { data =
372
- \requestPayload routeParams ->
373
- (routeParams
374
- |> data
375
- |> Server.Request.getDecoder
376
- |> (\decoder ->
377
- Json.Decode.decodeValue decoder requestPayload
378
- |> Result.mapError Json.Decode.errorToString
379
- |> BackendTask.fromResult
380
- -- TODO include title and better error context and formatting
381
- |> BackendTask.onError (\error -> BackendTask.fail (FatalError.fromString error))
382
- )
383
- )
384
- |> BackendTask.andThen
385
- (\rendered ->
386
- case rendered of
387
- Ok okRendered ->
388
- okRendered
389
-
390
- Err error ->
391
- Server.Request.errorsToString error
392
- |> FatalError.fromString
393
- |> BackendTask.fail
394
- )
373
+ \request routeParams ->
374
+ data routeParams request
395
375
  , action =
396
- \requestPayload routeParams ->
397
- (routeParams
398
- |> action
399
- |> Server.Request.getDecoder
400
- |> (\decoder ->
401
- Json.Decode.decodeValue decoder requestPayload
402
- |> Result.mapError Json.Decode.errorToString
403
- |> BackendTask.fromResult
404
- -- TODO include title and better error context and formatting
405
- |> BackendTask.onError (\error -> BackendTask.fail (FatalError.fromString error))
406
- )
407
- )
408
- |> BackendTask.andThen
409
- (\rendered ->
410
- case rendered of
411
- Ok okRendered ->
412
- okRendered
413
-
414
- Err error ->
415
- Server.Request.errorsToString error
416
- |> FatalError.fromString
417
- |> BackendTask.fail
418
- )
376
+ \request routeParams ->
377
+ action routeParams request
419
378
  , staticRoutes = BackendTask.succeed []
420
379
  , head = head
421
380
  , serverless = True
@@ -6,7 +6,7 @@ import FatalError exposing (FatalError)
6
6
  import Html exposing (Html)
7
7
  import Pages.Flags exposing (Flags)
8
8
  import Pages.PageUrl exposing (PageUrl)
9
- import Path exposing (Path)
9
+ import UrlPath exposing (UrlPath)
10
10
  import Route exposing (Route)
11
11
  import View exposing (View)
12
12
 
@@ -17,7 +17,7 @@ type alias SharedTemplate msg sharedModel sharedData mappedMsg =
17
17
  ->
18
18
  Maybe
19
19
  { path :
20
- { path : Path
20
+ { path : UrlPath
21
21
  , query : Maybe String
22
22
  , fragment : Maybe String
23
23
  }
@@ -29,7 +29,7 @@ type alias SharedTemplate msg sharedModel sharedData mappedMsg =
29
29
  , view :
30
30
  sharedData
31
31
  ->
32
- { path : Path
32
+ { path : UrlPath
33
33
  , route : Maybe Route
34
34
  }
35
35
  -> sharedModel
@@ -37,10 +37,10 @@ type alias SharedTemplate msg sharedModel sharedData mappedMsg =
37
37
  -> View mappedMsg
38
38
  -> { body : List (Html mappedMsg), title : String }
39
39
  , data : BackendTask.BackendTask FatalError sharedData
40
- , subscriptions : Path -> sharedModel -> Sub msg
40
+ , subscriptions : UrlPath -> sharedModel -> Sub msg
41
41
  , onPageChange :
42
42
  Maybe
43
- ({ path : Path
43
+ ({ path : UrlPath
44
44
  , query : Maybe String
45
45
  , fragment : Maybe String
46
46
  }
@@ -1,3 +1,3 @@
1
- export const compatibilityKey = 17;
1
+ export const compatibilityKey = 19;
2
2
 
3
- export const packageVersion = "3.0.0-beta.40";
3
+ export const packageVersion = "3.0.0-beta.42";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "elm-pages",
3
3
  "type": "module",
4
- "version": "3.0.0-beta.40",
4
+ "version": "3.0.0-beta.42",
5
5
  "homepage": "https://elm-pages.com",
6
6
  "moduleResolution": "node",
7
7
  "description": "Type-safe static sites, written in pure elm with your own custom elm-markup syntax.",
@@ -55,7 +55,7 @@
55
55
  "@types/micromatch": "^4.0.2",
56
56
  "@types/node": "^20.1.0",
57
57
  "@types/serve-static": "^1.15.1",
58
- "cypress": "^12.11.0",
58
+ "cypress": "^12.13.0",
59
59
  "elm-codegen": "^0.3.0",
60
60
  "elm-optimize-level-2": "^0.3.5",
61
61
  "elm-review": "^2.10.2",
package/src/ApiRoute.elm CHANGED
@@ -176,6 +176,7 @@ import BackendTask exposing (BackendTask)
176
176
  import FatalError exposing (FatalError)
177
177
  import Head
178
178
  import Internal.ApiRoute exposing (ApiRoute(..), ApiRouteBuilder(..))
179
+ import Internal.Request
179
180
  import Json.Decode as Decode
180
181
  import Json.Encode
181
182
  import Pattern
@@ -199,7 +200,7 @@ single handler =
199
200
 
200
201
 
201
202
  {-| -}
202
- serverRender : ApiRouteBuilder (Server.Request.Parser (BackendTask FatalError (Server.Response.Response Never Never))) constructor -> ApiRoute Response
203
+ serverRender : ApiRouteBuilder (Server.Request.Request -> BackendTask FatalError (Server.Response.Response Never Never)) constructor -> ApiRoute Response
203
204
  serverRender ((ApiRouteBuilder patterns pattern _ _ _) as fullHandler) =
204
205
  ApiRoute
205
206
  { regex = Regex.fromString ("^" ++ pattern ++ "$") |> Maybe.withDefault Regex.never
@@ -208,36 +209,7 @@ serverRender ((ApiRouteBuilder patterns pattern _ _ _) as fullHandler) =
208
209
  Internal.ApiRoute.tryMatch path fullHandler
209
210
  |> Maybe.map
210
211
  (\toBackendTask ->
211
- Server.Request.getDecoder toBackendTask
212
- |> (\decoder ->
213
- Decode.decodeValue decoder serverRequest
214
- |> Result.mapError Decode.errorToString
215
- |> BackendTask.fromResult
216
- |> BackendTask.map Just
217
- )
218
- |> BackendTask.onError
219
- (\stringError ->
220
- -- TODO make error with title and better context/formatting
221
- FatalError.fromString stringError |> BackendTask.fail
222
- )
223
- |> BackendTask.andThen
224
- (\rendered ->
225
- case rendered of
226
- Just (Ok okRendered) ->
227
- okRendered
228
-
229
- Just (Err errors) ->
230
- errors
231
- |> Server.Request.errorsToString
232
- |> Server.Response.plainText
233
- |> Server.Response.withStatusCode 400
234
- |> BackendTask.succeed
235
-
236
- Nothing ->
237
- Server.Response.plainText "No matching request handler"
238
- |> Server.Response.withStatusCode 400
239
- |> BackendTask.succeed
240
- )
212
+ toBackendTask (Internal.Request.toRequest serverRequest)
241
213
  )
242
214
  |> Maybe.map (BackendTask.map (Server.Response.toJson >> Just))
243
215
  |> Maybe.withDefault
@@ -98,28 +98,19 @@ type alias BackendTask error value =
98
98
  RawRequest error value
99
99
 
100
100
 
101
- {-| Transform a request into an arbitrary value. The same underlying HTTP requests will be performed during the build
102
- step, but mapping allows you to change the resulting values by applying functions to the results.
103
-
104
- A common use for this is to map your data into your elm-pages view:
101
+ {-| Transform a request into an arbitrary value. The same underlying task will be performed,
102
+ but mapping allows you to change the resulting values by applying functions to the results.
105
103
 
106
104
  import BackendTask
105
+ import BackendTask.Http
107
106
  import Json.Decode as Decode exposing (Decoder)
108
107
 
109
- view =
110
- BackendTask.Http.get
111
- (Secrets.succeed "https://api.github.com/repos/dillonkearns/elm-pages")
108
+ starsMessage =
109
+ BackendTask.Http.getJson
110
+ "https://api.github.com/repos/dillonkearns/elm-pages"
112
111
  (Decode.field "stargazers_count" Decode.int)
113
112
  |> BackendTask.map
114
- (\stars ->
115
- { view =
116
- \model viewForPage ->
117
- { title = "Current stars: " ++ String.fromInt stars
118
- , body = Html.text <| "⭐️ " ++ String.fromInt stars
119
- , head = []
120
- }
121
- }
122
- )
113
+ (\stars -> "⭐️ " ++ String.fromInt stars)
123
114
 
124
115
  -}
125
116
  map : (a -> b) -> BackendTask error a -> BackendTask error b
@@ -146,7 +137,7 @@ resolve =
146
137
  andThen combine
147
138
 
148
139
 
149
- {-| Turn a list of `StaticHttp.Request`s into a single one.
140
+ {-| Turn a list of `BackendTask`s into a single one.
150
141
 
151
142
  import BackendTask
152
143
  import Json.Decode as Decode exposing (Decoder)
@@ -156,10 +147,10 @@ resolve =
156
147
  , sprite : String
157
148
  }
158
149
 
159
- pokemonDetailRequest : StaticHttp.Request (List Pokemon)
150
+ pokemonDetailRequest : BackendTask (List Pokemon)
160
151
  pokemonDetailRequest =
161
- StaticHttp.get
162
- (Secrets.succeed "https://pokeapi.co/api/v2/pokemon/?limit=3")
152
+ BackendTask.Http.getJson
153
+ "https://pokeapi.co/api/v2/pokemon/?limit=3"
163
154
  (Decode.field "results"
164
155
  (Decode.list
165
156
  (Decode.map2 Tuple.pair
@@ -167,7 +158,7 @@ resolve =
167
158
  (Decode.field "url" Decode.string)
168
159
  |> Decode.map
169
160
  (\( name, url ) ->
170
- StaticHttp.get (Secrets.succeed url)
161
+ BackendTask.Http.getJson url
171
162
  (Decode.at
172
163
  [ "sprites", "front_default" ]
173
164
  Decode.string
@@ -177,7 +168,7 @@ resolve =
177
168
  )
178
169
  )
179
170
  )
180
- |> StaticHttp.andThen StaticHttp.combine
171
+ |> BackendTask.andThen BackendTask.combine
181
172
 
182
173
  -}
183
174
  combine : List (BackendTask error value) -> BackendTask error (List value)
@@ -353,7 +344,7 @@ fail error =
353
344
  ApiRoute (Err error)
354
345
 
355
346
 
356
- {-| Turn an Err into a BackendTask failure.
347
+ {-| Turn `Ok` into `BackendTask.succeed` and `Err` into `BackendTask.fail`.
357
348
  -}
358
349
  fromResult : Result error value -> BackendTask error value
359
350
  fromResult result =
@@ -523,7 +514,10 @@ map9 combineFn request1 request2 request3 request4 request5 request6 request7 re
523
514
  |> map2 (|>) request9
524
515
 
525
516
 
526
- {-| -}
517
+ {-| Ignore any recoverable error data and propagate the `FatalError`. Similar to a `Cmd` in The Elm Architecture,
518
+ a `FatalError` will not do anything except if it is returned at the top-level of your application. Read more
519
+ in the [`FatalError` docs](FatalError).
520
+ -}
527
521
  allowFatal : BackendTask { error | fatal : FatalError } data -> BackendTask FatalError data
528
522
  allowFatal backendTask =
529
523
  mapError .fatal backendTask
package/src/FormData.elm CHANGED
@@ -1,4 +1,4 @@
1
- module FormData exposing (encode, parse)
1
+ module FormData exposing (encode, parse, parseToList)
2
2
 
3
3
  import Dict exposing (Dict)
4
4
  import List.NonEmpty exposing (NonEmpty)
@@ -34,6 +34,26 @@ parse rawString =
34
34
  Dict.empty
35
35
 
36
36
 
37
+ parseToList : String -> List ( String, String )
38
+ parseToList rawString =
39
+ rawString
40
+ |> String.split "&"
41
+ |> List.concatMap
42
+ (\entry ->
43
+ case entry |> String.split "=" of
44
+ [ key, value ] ->
45
+ let
46
+ newValue : String
47
+ newValue =
48
+ value |> decode
49
+ in
50
+ [ ( key, newValue ) ]
51
+
52
+ _ ->
53
+ []
54
+ )
55
+
56
+
37
57
  decode : String -> String
38
58
  decode string =
39
59
  string
package/src/Head/Seo.elm CHANGED
@@ -1,13 +1,13 @@
1
1
  module Head.Seo exposing (Common, Image, article, audioPlayer, book, profile, song, summary, summaryLarge, videoPlayer, website)
2
2
 
3
- {-| <https://ogp.me/#>
4
- <https://developers.facebook.com/docs/sharing/opengraph>
3
+ {-| <https://developers.facebook.com/docs/sharing/opengraph>
5
4
 
6
5
  This module encapsulates some of the best practices for SEO for your site.
7
6
 
8
- `elm-pages` will pre-render each of the static pages (in your `content` directory) so that
7
+ `elm-pages` pre-renders the HTML for your pages (either at build-time or server-render time) so that
9
8
  web crawlers can efficiently and accurately process it. The functions in this module are for use
10
- with the `head` function that you pass to your Pages config (`Pages.application`).
9
+ with the `head` function in your `Route` modules to help you build up a set of `<meta>` tags that
10
+ includes common meta tags used for rich link previews, namely [OpenGraph tags](https://ogp.me/) and [Twitter card tags](https://developer.twitter.com/en/docs/twitter-for-websites/cards/overview/abouts-cards).
11
11
 
12
12
  import Date
13
13
  import Head
@@ -1,7 +1,87 @@
1
- module Internal.Request exposing (Parser(..))
1
+ module Internal.Request exposing (Request(..), RequestRecord, fakeRequest, toRequest)
2
2
 
3
- import Json.Decode
3
+ import CookieParser
4
+ import Dict exposing (Dict)
5
+ import Json.Decode as Decode
6
+ import Time
4
7
 
5
8
 
6
- type Parser decodesTo validationError
7
- = Parser (Json.Decode.Decoder ( Result validationError decodesTo, List validationError ))
9
+ type Request
10
+ = Request RequestRecord
11
+
12
+
13
+ type alias RequestRecord =
14
+ { time : Time.Posix
15
+ , method : String
16
+ , body : Maybe String
17
+ , rawUrl : String
18
+ , rawHeaders : Dict String String
19
+ , cookies : Dict String String
20
+ }
21
+
22
+
23
+ toRequest : Decode.Value -> Request
24
+ toRequest value =
25
+ Decode.decodeValue requestDecoder value
26
+ |> Result.map Request
27
+ |> Result.withDefault fakeRequest
28
+
29
+
30
+ fakeRequest : Request
31
+ fakeRequest =
32
+ Request
33
+ { time = Time.millisToPosix 0
34
+ , method = "ERROR"
35
+ , body = Just "ERROR"
36
+ , rawUrl = "ERROR"
37
+ , rawHeaders = Dict.empty
38
+ , cookies = Dict.empty
39
+ }
40
+
41
+
42
+ requestDecoder : Decode.Decoder RequestRecord
43
+ requestDecoder =
44
+ Decode.succeed RequestRecord
45
+ |> andMap
46
+ (Decode.field "requestTime"
47
+ (Decode.int |> Decode.map Time.millisToPosix)
48
+ )
49
+ |> andMap (Decode.field "method" Decode.string)
50
+ |> andMap (Decode.field "body" (Decode.nullable Decode.string))
51
+ |> andMap
52
+ (Decode.string
53
+ |> Decode.field "rawUrl"
54
+ )
55
+ |> andMap (Decode.field "headers" (Decode.dict Decode.string))
56
+ |> andMap
57
+ (Decode.field "headers"
58
+ (optionalField "cookie" Decode.string
59
+ |> Decode.map
60
+ (Maybe.map CookieParser.parse
61
+ >> Maybe.withDefault Dict.empty
62
+ )
63
+ )
64
+ )
65
+
66
+
67
+ andMap : Decode.Decoder a -> Decode.Decoder (a -> b) -> Decode.Decoder b
68
+ andMap =
69
+ Decode.map2 (|>)
70
+
71
+
72
+ optionalField : String -> Decode.Decoder a -> Decode.Decoder (Maybe a)
73
+ optionalField fieldName decoder_ =
74
+ let
75
+ finishDecoding : Decode.Value -> Decode.Decoder (Maybe a)
76
+ finishDecoding json =
77
+ case Decode.decodeValue (Decode.field fieldName Decode.value) json of
78
+ Ok _ ->
79
+ -- The field is present, so run the decoder on it.
80
+ Decode.map Just (Decode.field fieldName decoder_)
81
+
82
+ Err _ ->
83
+ -- The field was missing, which is fine!
84
+ Decode.succeed Nothing
85
+ in
86
+ Decode.value
87
+ |> Decode.andThen finishDecoding