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

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 (140) hide show
  1. package/README.md +10 -1
  2. package/adapter/netlify.js +207 -0
  3. package/codegen/{elm-pages-codegen.js → elm-pages-codegen.cjs} +2678 -2725
  4. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateData.elmi +0 -0
  5. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateData.elmo +0 -0
  6. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateDataTest.elmo +0 -0
  7. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/d.dat +0 -0
  8. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/i.dat +0 -0
  9. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/o.dat +0 -0
  10. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm.json +1 -1
  11. package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/Reporter.elm.js +1447 -342
  12. package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/Runner.elm.js +17004 -13817
  13. package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/node_runner.js +1 -1
  14. package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/node_supervisor.js +4 -4
  15. package/generator/dead-code-review/elm.json +9 -7
  16. package/generator/dead-code-review/src/Pages/Review/DeadCodeEliminateData.elm +59 -10
  17. package/generator/dead-code-review/tests/Pages/Review/DeadCodeEliminateDataTest.elm +52 -36
  18. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Internal-RoutePattern.elmi +0 -0
  19. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Internal-RoutePattern.elmo +0 -0
  20. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-NoContractViolations.elmi +0 -0
  21. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-NoContractViolations.elmo +0 -0
  22. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/d.dat +0 -0
  23. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/i.dat +0 -0
  24. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/o.dat +0 -0
  25. package/generator/review/elm-stuff/tests-0.19.1/elm.json +1 -1
  26. package/generator/review/elm-stuff/tests-0.19.1/js/Reporter.elm.js +1447 -342
  27. package/generator/review/elm-stuff/tests-0.19.1/js/Runner.elm.js +25025 -21739
  28. package/generator/review/elm-stuff/tests-0.19.1/js/node_runner.js +1 -1
  29. package/generator/review/elm-stuff/tests-0.19.1/js/node_supervisor.js +4 -4
  30. package/generator/review/elm.json +10 -10
  31. package/generator/src/RouteBuilder.elm +115 -109
  32. package/generator/src/SharedTemplate.elm +3 -2
  33. package/generator/src/SiteConfig.elm +3 -2
  34. package/generator/src/basepath-middleware.js +3 -3
  35. package/generator/src/build.js +209 -92
  36. package/generator/src/cli.js +292 -88
  37. package/generator/src/codegen.js +29 -27
  38. package/generator/src/compatibility-key.js +3 -0
  39. package/generator/src/compile-elm.js +43 -26
  40. package/generator/src/config.js +39 -0
  41. package/generator/src/copy-dir.js +2 -2
  42. package/generator/src/dev-server.js +176 -138
  43. package/generator/src/dir-helpers.js +9 -26
  44. package/generator/src/elm-codegen.js +5 -4
  45. package/generator/src/elm-file-constants.js +2 -3
  46. package/generator/src/error-formatter.js +12 -11
  47. package/generator/src/file-helpers.js +3 -4
  48. package/generator/src/generate-template-module-connector.js +23 -23
  49. package/generator/src/init.js +9 -8
  50. package/generator/src/pre-render-html.js +39 -28
  51. package/generator/src/render-test.js +109 -0
  52. package/generator/src/render-worker.js +25 -28
  53. package/generator/src/render.js +321 -142
  54. package/generator/src/request-cache.js +265 -162
  55. package/generator/src/resolve-elm-module.js +64 -0
  56. package/generator/src/rewrite-client-elm-json.js +6 -5
  57. package/generator/src/rewrite-elm-json-help.js +56 -0
  58. package/generator/src/rewrite-elm-json.js +17 -7
  59. package/generator/src/route-codegen-helpers.js +16 -31
  60. package/generator/src/seo-renderer.js +12 -7
  61. package/generator/src/vite-utils.js +77 -0
  62. package/generator/static-code/elm-pages.js +10 -0
  63. package/generator/static-code/hmr.js +79 -13
  64. package/generator/template/app/Api.elm +6 -5
  65. package/generator/template/app/Effect.elm +123 -0
  66. package/generator/template/app/ErrorPage.elm +37 -6
  67. package/generator/template/app/Route/Index.elm +17 -10
  68. package/generator/template/app/Shared.elm +24 -47
  69. package/generator/template/app/Site.elm +19 -6
  70. package/generator/template/app/View.elm +1 -8
  71. package/generator/template/elm-tooling.json +0 -3
  72. package/generator/template/elm.json +32 -24
  73. package/generator/template/package.json +10 -4
  74. package/package.json +30 -27
  75. package/src/ApiRoute.elm +199 -61
  76. package/src/BackendTask/Custom.elm +325 -0
  77. package/src/BackendTask/Env.elm +90 -0
  78. package/src/{DataSource → BackendTask}/File.elm +171 -56
  79. package/src/{DataSource → BackendTask}/Glob.elm +136 -125
  80. package/src/BackendTask/Http.elm +679 -0
  81. package/src/{DataSource → BackendTask}/Internal/Glob.elm +1 -1
  82. package/src/BackendTask/Internal/Request.elm +69 -0
  83. package/src/BackendTask/Random.elm +79 -0
  84. package/src/BackendTask/Time.elm +47 -0
  85. package/src/BackendTask.elm +537 -0
  86. package/src/FatalError.elm +90 -0
  87. package/src/Head.elm +237 -7
  88. package/src/HtmlPrinter.elm +7 -3
  89. package/src/Internal/ApiRoute.elm +7 -5
  90. package/src/PageServerResponse.elm +6 -1
  91. package/src/Pages/Form.elm +229 -0
  92. package/src/Pages/GeneratorProgramConfig.elm +15 -0
  93. package/src/Pages/Internal/FatalError.elm +5 -0
  94. package/src/Pages/Internal/Msg.elm +93 -0
  95. package/src/Pages/Internal/Platform/Cli.elm +612 -763
  96. package/src/Pages/Internal/Platform/CompatibilityKey.elm +6 -0
  97. package/src/Pages/Internal/Platform/Effect.elm +1 -2
  98. package/src/Pages/Internal/Platform/GeneratorApplication.elm +379 -0
  99. package/src/Pages/Internal/Platform/StaticResponses.elm +65 -276
  100. package/src/Pages/Internal/Platform/ToJsPayload.elm +6 -9
  101. package/src/Pages/Internal/Platform.elm +327 -194
  102. package/src/Pages/Internal/Script.elm +17 -0
  103. package/src/Pages/Internal/StaticHttpBody.elm +35 -1
  104. package/src/Pages/Manifest.elm +29 -4
  105. package/src/Pages/PageUrl.elm +23 -9
  106. package/src/Pages/ProgramConfig.elm +26 -15
  107. package/src/Pages/Script.elm +109 -0
  108. package/src/Pages/SiteConfig.elm +3 -2
  109. package/src/Pages/StaticHttp/Request.elm +2 -2
  110. package/src/Pages/StaticHttpRequest.elm +23 -99
  111. package/src/Pages/Transition.elm +12 -3
  112. package/src/PagesMsg.elm +82 -0
  113. package/src/Path.elm +16 -19
  114. package/src/QueryParams.elm +21 -172
  115. package/src/RequestsAndPending.elm +37 -20
  116. package/src/Result/Extra.elm +26 -0
  117. package/src/Scaffold/Form.elm +546 -0
  118. package/src/Scaffold/Route.elm +1402 -0
  119. package/src/Server/Request.elm +73 -72
  120. package/src/Server/Session.elm +62 -42
  121. package/src/Server/SetCookie.elm +12 -4
  122. package/src/Stub.elm +53 -0
  123. package/src/Test/Html/Internal/ElmHtml/ToString.elm +8 -9
  124. package/src/DataSource/Env.elm +0 -38
  125. package/src/DataSource/Http.elm +0 -446
  126. package/src/DataSource/Internal/Request.elm +0 -20
  127. package/src/DataSource/Port.elm +0 -90
  128. package/src/DataSource.elm +0 -538
  129. package/src/Form/Field.elm +0 -717
  130. package/src/Form/FieldStatus.elm +0 -36
  131. package/src/Form/FieldView.elm +0 -417
  132. package/src/Form/FormData.elm +0 -22
  133. package/src/Form/Validation.elm +0 -391
  134. package/src/Form/Value.elm +0 -118
  135. package/src/Form.elm +0 -1683
  136. package/src/FormDecoder.elm +0 -102
  137. package/src/Pages/FormState.elm +0 -256
  138. package/src/Pages/Generate.elm +0 -800
  139. package/src/Pages/Internal/Form.elm +0 -17
  140. package/src/Pages/Msg.elm +0 -79
@@ -20,8 +20,7 @@ import BuildError exposing (BuildError)
20
20
  import Bytes exposing (Bytes)
21
21
  import Bytes.Decode
22
22
  import Dict exposing (Dict)
23
- import Form.FormData exposing (FormData, Method(..))
24
- import FormDecoder
23
+ import Form
25
24
  import Html exposing (Html)
26
25
  import Html.Attributes as Attr
27
26
  import Http
@@ -30,14 +29,14 @@ import Json.Encode
30
29
  import Pages.ContentCache as ContentCache
31
30
  import Pages.Fetcher
32
31
  import Pages.Flags
33
- import Pages.FormState
32
+ import Pages.Internal.Msg
34
33
  import Pages.Internal.NotFoundReason exposing (NotFoundReason)
35
34
  import Pages.Internal.ResponseSketch as ResponseSketch exposing (ResponseSketch)
36
35
  import Pages.Internal.String as String
37
- import Pages.Msg
38
36
  import Pages.ProgramConfig exposing (ProgramConfig)
39
37
  import Pages.StaticHttpRequest as StaticHttpRequest
40
38
  import Pages.Transition
39
+ import PagesMsg exposing (PagesMsg)
41
40
  import Path exposing (Path)
42
41
  import QueryParams
43
42
  import Task
@@ -45,11 +44,6 @@ import Time
45
44
  import Url exposing (Url)
46
45
 
47
46
 
48
- type Transition
49
- = Loading Int Path
50
- | Submitting FormData
51
-
52
-
53
47
  {-| -}
54
48
  type alias Program userModel userMsg pageData actionData sharedData errorPage =
55
49
  Platform.Program Flags (Model userModel pageData actionData sharedData) (Msg userMsg pageData actionData sharedData errorPage)
@@ -58,7 +52,7 @@ type alias Program userModel userMsg pageData actionData sharedData errorPage =
58
52
  mainView :
59
53
  ProgramConfig userMsg userModel route pageData actionData sharedData effect (Msg userMsg pageData actionData sharedData errorPage) errorPage
60
54
  -> Model userModel pageData actionData sharedData
61
- -> { title : String, body : List (Html (Pages.Msg.Msg userMsg)) }
55
+ -> { title : String, body : List (Html (PagesMsg userMsg)) }
62
56
  mainView config model =
63
57
  case model.notFound of
64
58
  Just info ->
@@ -230,7 +224,7 @@ init config flags url key =
230
224
  , host = url.host
231
225
  , port_ = url.port_
232
226
  , path = pagePath
233
- , query = url.query |> Maybe.map QueryParams.fromString
227
+ , query = url.query |> Maybe.map QueryParams.fromString |> Maybe.withDefault Dict.empty
234
228
  , fragment = url.fragment
235
229
  }
236
230
  }
@@ -274,7 +268,7 @@ init config flags url key =
274
268
  , url = url
275
269
  , currentPath = url.path
276
270
  , pageData = Err "Not found"
277
- , ariaNavigationAnnouncement = "Error" -- TODO use error page title for announcement?
271
+ , ariaNavigationAnnouncement = "Page Not Found" -- TODO use error page title for announcement?
278
272
  , userFlags = flags
279
273
  , notFound = Just info
280
274
  , transition = Nothing
@@ -313,16 +307,23 @@ init config flags url key =
313
307
  type Msg userMsg pageData actionData sharedData errorPage
314
308
  = LinkClicked Browser.UrlRequest
315
309
  | UrlChanged Url
316
- | UserMsg (Pages.Msg.Msg userMsg)
317
- | SetField { formId : String, name : String, value : String }
310
+ -- TODO rename to PagesMsg
311
+ | UserMsg (PagesMsg userMsg)
312
+ --| SetField { formId : String, name : String, value : String }
313
+ | FormMsg (Form.Msg (Msg userMsg pageData actionData sharedData errorPage))
318
314
  | UpdateCacheAndUrlNew Bool Url (Maybe userMsg) (Result Http.Error ( Url, ResponseSketch pageData actionData sharedData ))
319
- | FetcherComplete Bool String Int (Result Http.Error ( Maybe userMsg, Maybe actionData ))
315
+ | FetcherComplete Bool String Int (Result Http.Error ( Maybe userMsg, ActionDataOrRedirect actionData ))
320
316
  | FetcherStarted String Int FormData Time.Posix
321
317
  | PageScrollComplete
322
318
  | HotReloadCompleteNew Bytes
323
319
  | ProcessFetchResponse Int (Result Http.Error ( Url, ResponseSketch pageData actionData sharedData )) (Result Http.Error ( Url, ResponseSketch pageData actionData sharedData ) -> Msg userMsg pageData actionData sharedData errorPage)
324
320
 
325
321
 
322
+ type ActionDataOrRedirect action
323
+ = ActionResponse (Maybe action)
324
+ | RedirectResponse String
325
+
326
+
326
327
  {-| -}
327
328
  type alias Model userModel pageData actionData sharedData =
328
329
  { key : Maybe Browser.Navigation.Key
@@ -342,7 +343,7 @@ type alias Model userModel pageData actionData sharedData =
342
343
  , transition : Maybe ( Int, Pages.Transition.Transition )
343
344
  , nextTransitionKey : Int
344
345
  , inFlightFetchers : Dict String ( Int, Pages.Transition.FetcherState actionData )
345
- , pageFormState : Pages.FormState.PageFormState
346
+ , pageFormState : Form.Model
346
347
  , pendingRedirect : Bool
347
348
  , pendingData : Maybe ( pageData, sharedData, Maybe actionData )
348
349
  }
@@ -361,6 +362,7 @@ type Effect userMsg pageData actionData sharedData userEffect errorPage
361
362
  | Batch (List (Effect userMsg pageData actionData sharedData userEffect errorPage))
362
363
  | UserCmd userEffect
363
364
  | CancelRequest Int
365
+ | RunCmd (Cmd (Msg userMsg pageData actionData sharedData errorPage))
364
366
 
365
367
 
366
368
  {-| -}
@@ -371,6 +373,18 @@ update :
371
373
  -> ( Model userModel pageData actionData sharedData, Effect userMsg pageData actionData sharedData userEffect errorPage )
372
374
  update config appMsg model =
373
375
  case appMsg of
376
+ FormMsg formMsg ->
377
+ let
378
+ -- TODO trigger formCmd
379
+ ( newModel, formCmd ) =
380
+ Form.update formMsg model.pageFormState
381
+ in
382
+ ( { model
383
+ | pageFormState = newModel
384
+ }
385
+ , RunCmd formCmd
386
+ )
387
+
374
388
  LinkClicked urlRequest ->
375
389
  case urlRequest of
376
390
  Browser.Internal url ->
@@ -396,11 +410,6 @@ update config appMsg model =
396
410
  , BrowserLoadUrl href
397
411
  )
398
412
 
399
- SetField info ->
400
- ( { model | pageFormState = Pages.FormState.setField info model.pageFormState }
401
- , NoEffect
402
- )
403
-
404
413
  UrlChanged url ->
405
414
  case model.pendingData of
406
415
  Just ( newPageData, newSharedData, newActionData ) ->
@@ -414,7 +423,7 @@ update config appMsg model =
414
423
  model
415
424
 
416
425
  Nothing ->
417
- if model.url.path == url.path then
426
+ if model.url.path == url.path && model.url.query == url.query then
418
427
  ( { model
419
428
  | -- update the URL in case query params or fragment changed
420
429
  url = url
@@ -423,46 +432,57 @@ update config appMsg model =
423
432
  )
424
433
 
425
434
  else
426
- ( { model
427
- | url = url
428
- }
435
+ ( model
429
436
  , NoEffect
430
437
  )
431
438
  -- TODO is it reasonable to always re-fetch route data if you re-navigate to the current route? Might be a good
432
439
  -- parallel to the browser behavior
433
440
  |> startNewGetLoad url (UpdateCacheAndUrlNew True url Nothing)
434
441
 
435
- FetcherComplete forPageReload fetcherKey transitionId___ userMsgResult ->
442
+ FetcherComplete _ fetcherKey _ userMsgResult ->
436
443
  case userMsgResult of
437
- Ok ( userMsg, maybeFetcherDoneActionData ) ->
438
- ( { model
439
- | inFlightFetchers =
440
- model.inFlightFetchers
441
- |> Dict.update fetcherKey
442
- (Maybe.map
443
- (\( transitionId, fetcherState ) ->
444
- ( transitionId
445
- , { fetcherState
446
- | status =
447
- maybeFetcherDoneActionData
448
- |> Maybe.map Pages.Transition.FetcherReloading
449
- -- TODO remove this bad default, FetcherSubmitting is incorrect
450
- |> Maybe.withDefault Pages.Transition.FetcherSubmitting
451
- }
444
+ Ok ( userMsg, actionOrRedirect ) ->
445
+ case actionOrRedirect of
446
+ ActionResponse maybeFetcherDoneActionData ->
447
+ ( { model
448
+ | inFlightFetchers =
449
+ model.inFlightFetchers
450
+ |> Dict.update fetcherKey
451
+ (Maybe.map
452
+ (\( transitionId, fetcherState ) ->
453
+ ( transitionId
454
+ , { fetcherState
455
+ | status =
456
+ maybeFetcherDoneActionData
457
+ |> Maybe.map Pages.Transition.FetcherReloading
458
+ -- TODO remove this bad default, FetcherSubmitting is incorrect
459
+ |> Maybe.withDefault Pages.Transition.FetcherSubmitting
460
+ }
461
+ )
462
+ )
452
463
  )
453
- )
454
- )
455
- }
456
- , NoEffect
457
- )
458
- |> (case userMsg of
459
- Just justUserMsg ->
460
- performUserMsg justUserMsg config
461
-
462
- Nothing ->
463
- identity
464
- )
465
- |> startNewGetLoad (currentUrlWithPath model.url.path model) (UpdateCacheAndUrlNew False model.url Nothing)
464
+ }
465
+ , NoEffect
466
+ )
467
+ |> (case userMsg of
468
+ Just justUserMsg ->
469
+ performUserMsg justUserMsg config
470
+
471
+ Nothing ->
472
+ identity
473
+ )
474
+ |> startNewGetLoad (currentUrlWithPath model.url.path model) (UpdateCacheAndUrlNew False model.url Nothing)
475
+
476
+ RedirectResponse redirectTo ->
477
+ ( { model
478
+ | inFlightFetchers =
479
+ model.inFlightFetchers
480
+ |> Dict.remove fetcherKey
481
+ , pendingRedirect = True
482
+ }
483
+ , NoEffect
484
+ )
485
+ |> startNewGetLoad (currentUrlWithPath redirectTo model) (UpdateCacheAndUrlNew False model.url Nothing)
466
486
 
467
487
  Err _ ->
468
488
  -- TODO how to handle error?
@@ -480,74 +500,70 @@ update config appMsg model =
480
500
 
481
501
  UserMsg userMsg_ ->
482
502
  case userMsg_ of
483
- Pages.Msg.UserMsg userMsg ->
503
+ Pages.Internal.Msg.UserMsg userMsg ->
484
504
  ( model, NoEffect )
485
505
  |> performUserMsg userMsg config
486
506
 
487
- Pages.Msg.Submit fields ->
488
- ( { model
489
- | transition =
490
- Just
491
- ( -- TODO remove hardcoded number
492
- -1
493
- , Pages.Transition.Submitting fields
494
- )
495
- }
496
- , Submit fields
497
- )
498
-
499
- Pages.Msg.SubmitIfValid formId fields isValid ->
500
- if isValid then
501
- ( { model
502
- -- TODO should I setSubmitAttempted here, too?
503
- | transition =
504
- Just
505
- ( -- TODO remove hardcoded number
506
- -1
507
- , Pages.Transition.Submitting fields
508
- )
509
- }
510
- , Submit fields
511
- )
512
-
513
- else
514
- ( { model
515
- | pageFormState =
516
- model.pageFormState
517
- |> Pages.FormState.setSubmitAttempted formId
518
- }
519
- , NoEffect
520
- )
521
-
522
- Pages.Msg.SubmitFetcher fetcherKey fields isValid maybeUserMsg ->
523
- if isValid then
524
- -- TODO should I setSubmitAttempted here, too?
525
- ( { model | nextTransitionKey = model.nextTransitionKey + 1 }
526
- , SubmitFetcher fetcherKey model.nextTransitionKey fields
527
- )
528
- |> (case maybeUserMsg of
529
- Just justUserMsg ->
530
- performUserMsg justUserMsg config
507
+ Pages.Internal.Msg.Submit fields ->
508
+ if fields.valid then
509
+ let
510
+ payload : { fields : List ( String, String ), method : Form.Method, action : String, id : Maybe String }
511
+ payload =
512
+ { fields = fields.fields
513
+ , method = fields.method
514
+ , action = fields.action
515
+ , id = Just fields.id
516
+ }
517
+ in
518
+ if fields.useFetcher then
519
+ ( { model | nextTransitionKey = model.nextTransitionKey + 1 }
520
+ , SubmitFetcher fields.id model.nextTransitionKey payload
521
+ )
522
+ |> (case fields.msg of
523
+ Just justUserMsg ->
524
+ performUserMsg justUserMsg config
525
+
526
+ Nothing ->
527
+ identity
528
+ )
529
+
530
+ else
531
+ ( { model
532
+ -- TODO should I setSubmitAttempted here, too?
533
+ | transition =
534
+ Just
535
+ ( -- TODO remove hardcoded number
536
+ -1
537
+ , Pages.Transition.Submitting payload
538
+ )
539
+ }
540
+ , Submit payload
541
+ )
542
+ |> (case fields.msg of
543
+ Just justUserMsg ->
544
+ performUserMsg justUserMsg config
531
545
 
532
- Nothing ->
533
- identity
534
- )
546
+ Nothing ->
547
+ identity
548
+ )
535
549
 
536
550
  else
537
- ( { model
538
- | pageFormState =
539
- model.pageFormState
540
- |> Pages.FormState.setSubmitAttempted fetcherKey
541
- }
542
- , NoEffect
543
- )
551
+ -- TODO should the user msg still be run if the form is invalid?
552
+ ( model, NoEffect )
544
553
 
545
- Pages.Msg.FormFieldEvent value ->
554
+ Pages.Internal.Msg.FormMsg formMsg ->
546
555
  -- TODO when init is called for a new page, also need to clear out client-side `pageFormState`
547
- ( { model | pageFormState = Pages.FormState.update value model.pageFormState }
548
- , NoEffect
556
+ let
557
+ ( formModel, formCmd ) =
558
+ Form.update formMsg model.pageFormState
559
+ in
560
+ ( { model | pageFormState = formModel }
561
+ , RunCmd (Cmd.map UserMsg formCmd)
549
562
  )
550
563
 
564
+ Pages.Internal.Msg.NoOp ->
565
+ ( model, NoEffect )
566
+
551
567
  UpdateCacheAndUrlNew scrollToTopWhenDone urlWithoutRedirectResolution maybeUserMsg updateResult ->
552
568
  -- TODO remove all fetchers that are in the state `FetcherReloading` here -- I think that's the right logic?
553
569
  case
@@ -605,27 +621,32 @@ update config appMsg model =
605
621
  , actionData = newActionData
606
622
  }
607
623
 
608
- ( userModel, _ ) =
624
+ ( userModel, userEffect ) =
609
625
  -- TODO if urlWithoutRedirectResolution is different from the url with redirect resolution, then
610
626
  -- instead of calling update, call pushUrl (I think?)
611
627
  -- TODO include user Cmd
612
- config.update model.pageFormState
613
- (model.inFlightFetchers |> toFetcherState)
614
- (model.transition |> Maybe.map Tuple.second)
615
- newSharedData
616
- newPageData
617
- model.key
618
- (config.onPageChange
619
- { protocol = model.url.protocol
620
- , host = model.url.host
621
- , port_ = model.url.port_
622
- , path = urlPathToPath urlWithoutRedirectResolution
623
- , query = urlWithoutRedirectResolution.query
624
- , fragment = urlWithoutRedirectResolution.fragment
625
- , metadata = config.urlToRoute urlWithoutRedirectResolution
626
- }
627
- )
628
- previousPageData.userModel
628
+ if stayingOnSamePath then
629
+ ( previousPageData.userModel, NoEffect )
630
+
631
+ else
632
+ config.update model.pageFormState
633
+ (model.inFlightFetchers |> toFetcherState)
634
+ (model.transition |> Maybe.map Tuple.second)
635
+ newSharedData
636
+ newPageData
637
+ model.key
638
+ (config.onPageChange
639
+ { protocol = model.url.protocol
640
+ , host = model.url.host
641
+ , port_ = model.url.port_
642
+ , path = urlPathToPath urlWithoutRedirectResolution
643
+ , query = urlWithoutRedirectResolution.query
644
+ , fragment = urlWithoutRedirectResolution.fragment
645
+ , metadata = config.urlToRoute urlWithoutRedirectResolution
646
+ }
647
+ )
648
+ previousPageData.userModel
649
+ |> Tuple.mapSecond UserCmd
629
650
 
630
651
  updatedModel : Model userModel pageData actionData sharedData
631
652
  updatedModel =
@@ -656,10 +677,13 @@ update config appMsg model =
656
677
  , currentPath = newUrl.path
657
678
  }
658
679
  , if not stayingOnSamePath && scrollToTopWhenDone then
659
- ScrollToTop
680
+ Batch
681
+ [ ScrollToTop
682
+ , userEffect
683
+ ]
660
684
 
661
685
  else
662
- NoEffect
686
+ userEffect
663
687
  )
664
688
  |> (case maybeUserMsg of
665
689
  Just userMsg ->
@@ -679,10 +703,10 @@ update config appMsg model =
679
703
  Err _ ->
680
704
  {-
681
705
  When there is an error loading the content.dat, we are either
682
- 1) in the dev server, and should show the relevant DataSource error for the page
706
+ 1) in the dev server, and should show the relevant BackendTask error for the page
683
707
  we're navigating to. This could be done more cleanly, but it's simplest to just
684
708
  do a fresh page load and use the code path for presenting an error for a fresh page.
685
- 2) In a production app. That means we had a successful build, so there were no DataSource failures,
709
+ 2) In a production app. That means we had a successful build, so there were no BackendTask failures,
686
710
  so the app must be stale (unless it's in some unexpected state from a bug). In the future,
687
711
  it probably makes sense to include some sort of hash of the app version we are fetching, match
688
712
  it with the current version that's running, and perform this logic when we see there is a mismatch.
@@ -745,7 +769,86 @@ update config appMsg model =
745
769
  _ ->
746
770
  ( model, NoEffect )
747
771
  )
748
- |> Result.withDefault ( model, NoEffect )
772
+ |> Result.withDefault
773
+ (let
774
+ pageDataResult : Maybe (InitKind sharedData pageData actionData errorPage)
775
+ pageDataResult =
776
+ case Bytes.Decode.decode config.decodeResponse pageDataBytes of
777
+ Just (ResponseSketch.RenderPage _ _) ->
778
+ Nothing
779
+
780
+ Just (ResponseSketch.HotUpdate pageData shared actionData) ->
781
+ OkPage shared pageData actionData
782
+ |> Just
783
+
784
+ Just (ResponseSketch.NotFound notFound) ->
785
+ NotFound notFound
786
+ |> Just
787
+
788
+ _ ->
789
+ Nothing
790
+ in
791
+ case pageDataResult of
792
+ Just (OkPage sharedData pageData actionData) ->
793
+ let
794
+ urls : { currentUrl : Url, basePath : List String }
795
+ urls =
796
+ { currentUrl = model.url
797
+ , basePath = config.basePath
798
+ }
799
+
800
+ pagePath : Path
801
+ pagePath =
802
+ urlsToPagePath urls
803
+
804
+ userFlags : Pages.Flags.Flags
805
+ userFlags =
806
+ model.userFlags
807
+ |> Decode.decodeValue
808
+ (Decode.field "userFlags" Decode.value)
809
+ |> Result.withDefault Json.Encode.null
810
+ |> Pages.Flags.BrowserFlags
811
+
812
+ ( userModel, userCmd ) =
813
+ Just
814
+ { path =
815
+ { path = pagePath
816
+ , query = model.url.query
817
+ , fragment = model.url.fragment
818
+ }
819
+ , metadata = config.urlToRoute model.url
820
+ , pageUrl =
821
+ Just
822
+ { protocol = model.url.protocol
823
+ , host = model.url.host
824
+ , port_ = model.url.port_
825
+ , path = pagePath
826
+ , query = model.url.query |> Maybe.map QueryParams.fromString |> Maybe.withDefault Dict.empty
827
+ , fragment = model.url.fragment
828
+ }
829
+ }
830
+ |> config.init userFlags sharedData pageData actionData
831
+
832
+ cmd : Effect userMsg pageData actionData sharedData userEffect errorPage
833
+ cmd =
834
+ UserCmd userCmd
835
+ in
836
+ ( { model
837
+ | pageData =
838
+ Ok
839
+ { userModel = userModel
840
+ , sharedData = sharedData
841
+ , pageData = pageData
842
+ , actionData = actionData
843
+ }
844
+ , notFound = Nothing
845
+ }
846
+ , cmd
847
+ )
848
+
849
+ _ ->
850
+ ( model, NoEffect )
851
+ )
749
852
 
750
853
  FetcherStarted fetcherKey transitionId fetcherData initiatedAt ->
751
854
  ( { model
@@ -766,7 +869,7 @@ update config appMsg model =
766
869
  toFetcherState : Dict String ( Int, Pages.Transition.FetcherState actionData ) -> Dict String (Pages.Transition.FetcherState actionData)
767
870
  toFetcherState inFlightFetchers =
768
871
  inFlightFetchers
769
- |> Dict.map (\_ ( index, fetcherState ) -> fetcherState)
872
+ |> Dict.map (\_ ( _, fetcherState ) -> fetcherState)
770
873
 
771
874
 
772
875
  performUserMsg :
@@ -800,6 +903,9 @@ perform config model effect =
800
903
  NoEffect ->
801
904
  Cmd.none
802
905
 
906
+ RunCmd cmd ->
907
+ cmd
908
+
803
909
  Batch effects ->
804
910
  effects
805
911
  |> List.map (perform config model)
@@ -828,10 +934,10 @@ perform config model effect =
828
934
  |> Maybe.withDefault Cmd.none
829
935
 
830
936
  FetchPageData transitionKey maybeRequestInfo url toMsg ->
831
- fetchRouteData True transitionKey toMsg config url maybeRequestInfo
937
+ fetchRouteData transitionKey toMsg config url maybeRequestInfo
832
938
 
833
939
  Submit fields ->
834
- if fields.method == Get then
940
+ if fields.method == Form.Get then
835
941
  model.key
836
942
  |> Maybe.map (\key -> Browser.Navigation.pushUrl key (appendFormQueryParams fields))
837
943
  |> Maybe.withDefault Cmd.none
@@ -843,7 +949,7 @@ perform config model effect =
843
949
  -- TODO add optional path parameter to Submit variant to allow submitting to other routes
844
950
  model.url
845
951
  in
846
- fetchRouteData False -1 (UpdateCacheAndUrlNew False model.url Nothing) config urlToSubmitTo (Just fields)
952
+ fetchRouteData -1 (UpdateCacheAndUrlNew False model.url Nothing) config urlToSubmitTo (Just fields)
847
953
 
848
954
  SubmitFetcher fetcherKey transitionId formData ->
849
955
  startFetcher2 config False fetcherKey transitionId formData model
@@ -863,7 +969,7 @@ perform config model effect =
863
969
  |> config.perform
864
970
  { fetchRouteData =
865
971
  \fetchInfo ->
866
- fetchRouteData False
972
+ fetchRouteData
867
973
  -1
868
974
  (prepare fetchInfo.toMsg)
869
975
  config
@@ -873,15 +979,19 @@ perform config model effect =
873
979
  ---- TODO map the Msg with the wrapper type (like in the PR branch)
874
980
  , submit =
875
981
  \fetchInfo ->
876
- fetchRouteData False -1 (prepare fetchInfo.toMsg) config (fetchInfo.values.action |> Url.fromString |> Maybe.withDefault model.url) (Just fetchInfo.values)
982
+ fetchRouteData -1 (prepare fetchInfo.toMsg) config (fetchInfo.values.action |> Url.fromString |> Maybe.withDefault model.url) (Just fetchInfo.values)
877
983
  , runFetcher =
878
984
  \(Pages.Fetcher.Fetcher options) ->
879
985
  -- TODO need to get the fetcherId here
880
986
  -- TODO need to increment and pass in the transitionId
881
987
  startFetcher "TODO" -1 options model
882
- , fromPageMsg = Pages.Msg.UserMsg >> UserMsg
988
+ , fromPageMsg = Pages.Internal.Msg.UserMsg >> UserMsg
883
989
  , key = key
884
- , setField = \info -> Task.succeed (SetField info) |> Task.perform identity
990
+ , setField =
991
+ \_ ->
992
+ --Task.succeed (SetField info) |> Task.perform identity
993
+ -- TODO
994
+ Cmd.none
885
995
  }
886
996
 
887
997
  Nothing ->
@@ -896,21 +1006,12 @@ startFetcher fetcherKey transitionId options model =
896
1006
  let
897
1007
  encodedBody : String
898
1008
  encodedBody =
899
- FormDecoder.encodeFormData
900
- { fields = options.fields
901
-
902
- -- TODO remove hardcoding
903
- , action = ""
1009
+ encodeFormData options.fields
904
1010
 
905
- -- TODO remove hardcoding
906
- , method = Post
907
- , id = Nothing
908
- }
909
-
910
- formData : { method : Method, action : String, fields : List ( String, String ), id : Maybe String }
1011
+ formData : { method : Form.Method, action : String, fields : List ( String, String ), id : Maybe String }
911
1012
  formData =
912
1013
  { -- TODO remove hardcoding
913
- method = Get
1014
+ method = Form.Get
914
1015
 
915
1016
  -- TODO pass FormData directly
916
1017
  , action = options.url |> Maybe.withDefault model.url.path
@@ -927,10 +1028,10 @@ startFetcher fetcherKey transitionId options model =
927
1028
  Http.expectBytesResponse (FetcherComplete False fetcherKey model.nextTransitionKey)
928
1029
  (\bytes ->
929
1030
  case bytes of
930
- Http.GoodStatus_ metadata bytesBody ->
1031
+ Http.GoodStatus_ _ bytesBody ->
931
1032
  ( options.decoder (Ok bytesBody)
932
1033
  |> Just
933
- , Nothing
1034
+ , ActionResponse Nothing
934
1035
  )
935
1036
  |> Ok
936
1037
 
@@ -943,7 +1044,7 @@ startFetcher fetcherKey transitionId options model =
943
1044
  Http.NetworkError_ ->
944
1045
  Err <| Http.NetworkError
945
1046
 
946
- Http.BadStatus_ metadata body ->
1047
+ Http.BadStatus_ metadata _ ->
947
1048
  Err <| Http.BadStatus metadata.statusCode
948
1049
  )
949
1050
  , tracker = Nothing
@@ -968,13 +1069,13 @@ startFetcher2 config fromPageReload fetcherKey transitionId formData model =
968
1069
  let
969
1070
  encodedBody : String
970
1071
  encodedBody =
971
- FormDecoder.encodeFormData formData
1072
+ encodeFormData formData.fields
972
1073
  in
973
1074
  -- TODO make sure that `actionData` isn't updated in Model for fetchers
974
1075
  Cmd.batch
975
1076
  [ cancelStaleFetchers model
976
1077
  , case Dict.get fetcherKey model.inFlightFetchers of
977
- Just ( inFlightId, inFlightFetcher ) ->
1078
+ Just ( inFlightId, _ ) ->
978
1079
  Http.cancel (String.fromInt inFlightId)
979
1080
 
980
1081
  Nothing ->
@@ -985,22 +1086,26 @@ startFetcher2 config fromPageReload fetcherKey transitionId formData model =
985
1086
  Http.expectBytesResponse (FetcherComplete fromPageReload fetcherKey model.nextTransitionKey)
986
1087
  (\bytes ->
987
1088
  case bytes of
988
- Http.GoodStatus_ metadata bytesBody ->
1089
+ Http.GoodStatus_ _ bytesBody ->
989
1090
  let
990
- decodedAction : Maybe actionData
1091
+ decodedAction : ActionDataOrRedirect actionData
991
1092
  decodedAction =
992
1093
  case Bytes.Decode.decode config.decodeResponse bytesBody of
1094
+ -- @@@
1095
+ Just (ResponseSketch.Redirect redirectTo) ->
1096
+ RedirectResponse redirectTo
1097
+
993
1098
  Just (ResponseSketch.RenderPage _ maybeAction) ->
994
- maybeAction
1099
+ ActionResponse maybeAction
995
1100
 
996
- Just (ResponseSketch.HotUpdate pageData shared maybeAction) ->
997
- maybeAction
1101
+ Just (ResponseSketch.HotUpdate _ _ maybeAction) ->
1102
+ ActionResponse maybeAction
998
1103
 
999
- Just (ResponseSketch.NotFound notFound) ->
1000
- Nothing
1104
+ Just (ResponseSketch.NotFound _) ->
1105
+ ActionResponse Nothing
1001
1106
 
1002
1107
  _ ->
1003
- Nothing
1108
+ ActionResponse Nothing
1004
1109
  in
1005
1110
  -- TODO maybe have an optional way to pass the bytes through?
1006
1111
  Ok ( Nothing, decodedAction )
@@ -1014,7 +1119,7 @@ startFetcher2 config fromPageReload fetcherKey transitionId formData model =
1014
1119
  Http.NetworkError_ ->
1015
1120
  Err <| Http.NetworkError
1016
1121
 
1017
- Http.BadStatus_ metadata body ->
1122
+ Http.BadStatus_ metadata _ ->
1018
1123
  Err <| Http.BadStatus metadata.statusCode
1019
1124
  )
1020
1125
  , tracker = Just (String.fromInt transitionId)
@@ -1025,7 +1130,7 @@ startFetcher2 config fromPageReload fetcherKey transitionId formData model =
1025
1130
 
1026
1131
  -- TODO use formData.method to do either query params or POST body
1027
1132
  , url = formData.action |> Url.fromString |> Maybe.map (\{ path } -> Path.join [ path, "content.dat" ] |> Path.toAbsolute) |> Maybe.withDefault "/"
1028
- , method = formData.method |> FormDecoder.methodToString
1133
+ , method = formData.method |> methodToString
1029
1134
  , timeout = Nothing
1030
1135
  }
1031
1136
  ]
@@ -1036,7 +1141,7 @@ cancelStaleFetchers model =
1036
1141
  model.inFlightFetchers
1037
1142
  |> Dict.toList
1038
1143
  |> List.filterMap
1039
- (\( fetcherKey, ( id, fetcher ) ) ->
1144
+ (\( _, ( id, fetcher ) ) ->
1040
1145
  case fetcher.status of
1041
1146
  Pages.Transition.FetcherReloading _ ->
1042
1147
  Http.cancel (String.fromInt id)
@@ -1059,10 +1164,10 @@ appendFormQueryParams fields =
1059
1164
  |> Maybe.withDefault "/"
1060
1165
  )
1061
1166
  ++ (case fields.method of
1062
- Get ->
1063
- "?" ++ FormDecoder.encodeFormData fields
1167
+ Form.Get ->
1168
+ "?" ++ encodeFormData fields.fields
1064
1169
 
1065
- Post ->
1170
+ Form.Post ->
1066
1171
  ""
1067
1172
  )
1068
1173
 
@@ -1104,7 +1209,7 @@ application config =
1104
1209
  [ config.subscriptions (model.url |> config.urlToRoute)
1105
1210
  (urls.currentUrl |> config.urlToRoute |> config.routeToPath |> Path.join)
1106
1211
  pageData.userModel
1107
- |> Sub.map (Pages.Msg.UserMsg >> UserMsg)
1212
+ |> Sub.map (Pages.Internal.Msg.UserMsg >> UserMsg)
1108
1213
  , config.hotReloadData
1109
1214
  |> Sub.map HotReloadCompleteNew
1110
1215
  ]
@@ -1157,14 +1262,13 @@ urlPathToPath urls =
1157
1262
 
1158
1263
 
1159
1264
  fetchRouteData :
1160
- Bool
1161
- -> Int
1265
+ Int
1162
1266
  -> (Result Http.Error ( Url, ResponseSketch pageData actionData sharedData ) -> Msg userMsg pageData actionData sharedData errorPage)
1163
1267
  -> ProgramConfig userMsg userModel route pageData actionData sharedData effect (Msg userMsg pageData actionData sharedData errorPage) errorPage
1164
1268
  -> Url
1165
1269
  -> Maybe FormData
1166
1270
  -> Cmd (Msg userMsg pageData actionData sharedData errorPage)
1167
- fetchRouteData forPageDataReload transitionKey toMsg config url details =
1271
+ fetchRouteData transitionKey toMsg config url details =
1168
1272
  {-
1169
1273
  TODO:
1170
1274
  - [X] `toMsg` needs a parameter for the callback Msg so it can pass it on if there is a Redirect response
@@ -1178,14 +1282,14 @@ fetchRouteData forPageDataReload transitionKey toMsg config url details =
1178
1282
 
1179
1283
  -}
1180
1284
  let
1181
- formMethod : Method
1285
+ formMethod : Form.Method
1182
1286
  formMethod =
1183
1287
  details
1184
1288
  |> Maybe.map .method
1185
- |> Maybe.withDefault Get
1289
+ |> Maybe.withDefault Form.Get
1186
1290
  in
1187
1291
  Http.request
1188
- { method = details |> Maybe.map (.method >> FormDecoder.methodToString) |> Maybe.withDefault "GET"
1292
+ { method = details |> Maybe.map (.method >> methodToString) |> Maybe.withDefault "GET"
1189
1293
  , headers = []
1190
1294
  , url =
1191
1295
  "/"
@@ -1203,34 +1307,34 @@ fetchRouteData forPageDataReload transitionKey toMsg config url details =
1203
1307
  |> String.join "/"
1204
1308
  )
1205
1309
  ++ (case formMethod of
1206
- Post ->
1310
+ Form.Post ->
1207
1311
  "/"
1208
1312
 
1209
- Get ->
1313
+ Form.Get ->
1210
1314
  details
1211
- |> Maybe.map FormDecoder.encodeFormData
1315
+ |> Maybe.map (.fields >> encodeFormData)
1212
1316
  |> Maybe.map (\encoded -> "?" ++ encoded)
1213
1317
  |> Maybe.withDefault ""
1214
1318
  )
1215
1319
  ++ (case formMethod of
1216
1320
  -- TODO extract this to something unit testable
1217
1321
  -- TODO make states mutually exclusive for submissions and direct URL requests (shouldn't be possible to append two query param strings)
1218
- Post ->
1322
+ Form.Post ->
1219
1323
  ""
1220
1324
 
1221
- Get ->
1325
+ Form.Get ->
1222
1326
  url.query
1223
1327
  |> Maybe.map (\encoded -> "?" ++ encoded)
1224
1328
  |> Maybe.withDefault ""
1225
1329
  )
1226
1330
  , body =
1227
1331
  case formMethod of
1228
- Post ->
1332
+ Form.Post ->
1229
1333
  let
1230
1334
  urlEncodedFields : Maybe String
1231
1335
  urlEncodedFields =
1232
1336
  details
1233
- |> Maybe.map FormDecoder.encodeFormData
1337
+ |> Maybe.map (.fields >> encodeFormData)
1234
1338
  in
1235
1339
  urlEncodedFields
1236
1340
  |> Maybe.map (\encoded -> Http.stringBody "application/x-www-form-urlencoded" encoded)
@@ -1251,7 +1355,7 @@ fetchRouteData forPageDataReload transitionKey toMsg config url details =
1251
1355
  Http.NetworkError_ ->
1252
1356
  Err Http.NetworkError
1253
1357
 
1254
- Http.BadStatus_ metadata body ->
1358
+ Http.BadStatus_ _ body ->
1255
1359
  body
1256
1360
  |> Bytes.Decode.decode config.decodeResponse
1257
1361
  |> Result.fromMaybe "Decoding error"
@@ -1303,7 +1407,7 @@ startNewGetLoad urlToGet toMsg ( model, effect ) =
1303
1407
  cancelIfStale : Effect userMsg pageData actionData sharedData userEffect errorPage
1304
1408
  cancelIfStale =
1305
1409
  case model.transition of
1306
- Just ( transitionKey, Pages.Transition.Loading path loadingKind ) ->
1410
+ Just ( transitionKey, Pages.Transition.Loading _ _ ) ->
1307
1411
  CancelRequest transitionKey
1308
1412
 
1309
1413
  _ ->
@@ -1314,13 +1418,13 @@ startNewGetLoad urlToGet toMsg ( model, effect ) =
1314
1418
  , transition =
1315
1419
  ( model.nextTransitionKey
1316
1420
  , case model.transition of
1317
- Just ( transitionKey, Pages.Transition.LoadAfterSubmit submitData _ _ ) ->
1421
+ Just ( _, Pages.Transition.LoadAfterSubmit submitData _ _ ) ->
1318
1422
  Pages.Transition.LoadAfterSubmit
1319
1423
  submitData
1320
1424
  (urlToGet.path |> Path.fromString)
1321
1425
  Pages.Transition.Load
1322
1426
 
1323
- Just ( transitionKey, Pages.Transition.Submitting submitData ) ->
1427
+ Just ( _, Pages.Transition.Submitting submitData ) ->
1324
1428
  Pages.Transition.LoadAfterSubmit
1325
1429
  submitData
1326
1430
  (urlToGet.path |> Path.fromString)
@@ -1392,6 +1496,7 @@ loadDataAndUpdateUrl ( newPageData, newSharedData, newActionData ) maybeUserMsg
1392
1496
  , actionData = newActionData
1393
1497
  }
1394
1498
 
1499
+ -- TODO use userEffect here?
1395
1500
  ( userModel, _ ) =
1396
1501
  -- TODO if urlWithoutRedirectResolution is different from the url with redirect resolution, then
1397
1502
  -- instead of calling update, call pushUrl (I think?)
@@ -1467,10 +1572,10 @@ loadDataAndUpdateUrl ( newPageData, newSharedData, newActionData ) maybeUserMsg
1467
1572
  Err _ ->
1468
1573
  {-
1469
1574
  When there is an error loading the content.dat, we are either
1470
- 1) in the dev server, and should show the relevant DataSource error for the page
1575
+ 1) in the dev server, and should show the relevant BackendTask error for the page
1471
1576
  we're navigating to. This could be done more cleanly, but it's simplest to just
1472
1577
  do a fresh page load and use the code path for presenting an error for a fresh page.
1473
- 2) In a production app. That means we had a successful build, so there were no DataSource failures,
1578
+ 2) In a production app. That means we had a successful build, so there were no BackendTask failures,
1474
1579
  so the app must be stale (unless it's in some unexpected state from a bug). In the future,
1475
1580
  it probably makes sense to include some sort of hash of the app version we are fetching, match
1476
1581
  it with the current version that's running, and perform this logic when we see there is a mismatch.
@@ -1483,3 +1588,31 @@ loadDataAndUpdateUrl ( newPageData, newSharedData, newActionData ) maybeUserMsg
1483
1588
  |> Url.toString
1484
1589
  |> BrowserLoadUrl
1485
1590
  )
1591
+
1592
+
1593
+ methodToString : Form.Method -> String
1594
+ methodToString method =
1595
+ case method of
1596
+ Form.Get ->
1597
+ "GET"
1598
+
1599
+ Form.Post ->
1600
+ "POST"
1601
+
1602
+
1603
+ encodeFormData : List ( String, String ) -> String
1604
+ encodeFormData fields =
1605
+ fields
1606
+ |> List.map
1607
+ (\( name, value ) ->
1608
+ Url.percentEncode name ++ "=" ++ Url.percentEncode value
1609
+ )
1610
+ |> String.join "&"
1611
+
1612
+
1613
+ type alias FormData =
1614
+ { fields : List ( String, String )
1615
+ , method : Form.Method
1616
+ , action : String
1617
+ , id : Maybe String
1618
+ }