elm-pages 3.0.8 β†’ 3.0.9

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.
package/README.md CHANGED
@@ -11,10 +11,10 @@
11
11
 
12
12
  - [elm-pages Docs Site](https://elm-pages.com/docs)
13
13
  - [elm-pages site showcase](https://elm-pages.com/showcase/)
14
- - [elm-pages Elm API Docs](https://package.elm-lang.org/packages/dillonkearns/elm-pages/10.0.1/)
14
+ - [elm-pages Elm API Docs](https://package.elm-lang.org/packages/dillonkearns/elm-pages/10.0.2/)
15
15
  - [Quick start repo](https://github.com/dillonkearns/elm-pages-starter) [(live site hosted here)](https://elm-pages-starter.netlify.com)
16
16
  - [Introducing `elm-pages` blog post](https://elm-pages.com/blog/introducing-elm-pages)
17
- - [`examples` folder](https://github.com/dillonkearns/elm-pages/blob/master/examples/) (includes https://elm-pages.com site source)
17
+ - [`examples` folder](https://github.com/dillonkearns/elm-pages/blob/master/examples/) (includes https://elm-pages.com site source) Use `git clone --recurse-submodules https://github.com/dillonkearns/elm-pages.git` so that there aren't missing files when you try to build the examples.
18
18
 
19
19
  ## Compatibility Key
20
20
 
@@ -34,7 +34,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
34
34
  <tr>
35
35
  <td align="center"><a href="https://github.com/danmarcab"><img src="https://avatars2.githubusercontent.com/u/1517969?v=4" width="100px;" alt=""/><br /><sub><b>Daniel MarΓ­n</b></sub></a><br /><a href="https://github.com/dillonkearns/elm-pages/commits?author=danmarcab" title="Code">πŸ’»</a></td>
36
36
  <td align="center"><a href="https://citric.id"><img src="https://avatars1.githubusercontent.com/u/296665?v=4" width="100px;" alt=""/><br /><sub><b>Steven Vandevelde</b></sub></a><br /><a href="https://github.com/dillonkearns/elm-pages/commits?author=icidasset" title="Code">πŸ’»</a></td>
37
- <td align="center"><a href="https://github.com/Y0hy0h"><img src="https://avatars0.githubusercontent.com/u/11377826?v=4" width="100px;" alt=""/><br /><sub><b>Johannes Maas</b></sub></a><br /><a href="#userTesting-Y0hy0h" title="User Testing">πŸ““</a> <a href="https://github.com/dillonkearns/elm-pages/commits?author=Y0hy0h" title="Code">πŸ’»</a></td>
37
+ <td align="center"><a href="https://github.com/j-maas"><img src="https://avatars0.githubusercontent.com/u/11377826?v=4" width="100px;" alt=""/><br /><sub><b>Johannes Maas</b></sub></a><br /><a href="#userTesting-j-maas" title="User Testing">πŸ““</a> <a href="https://github.com/dillonkearns/elm-pages/commits?author=j-maas" title="Code">πŸ’»</a></td>
38
38
  <td align="center"><a href="https://github.com/vViktorPL"><img src="https://avatars1.githubusercontent.com/u/2961541?v=4" width="100px;" alt=""/><br /><sub><b>Wiktor Toporek</b></sub></a><br /><a href="https://github.com/dillonkearns/elm-pages/commits?author=vViktorPL" title="Code">πŸ’»</a></td>
39
39
  <td align="center"><a href="https://sunrisemovement.com"><img src="https://avatars1.githubusercontent.com/u/1508245?v=4" width="100px;" alt=""/><br /><sub><b>Luke Westby</b></sub></a><br /><a href="https://github.com/dillonkearns/elm-pages/commits?author=lukewestby" title="Code">πŸ’»</a></td>
40
40
  </tr>
@@ -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: 2473597562, fuzzRuns: 100, filter: null };
78
+ const flags = { initialSeed: 1491170149, 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: 2473597562,
85
+ initialSeed: 1491170149,
86
86
  fuzzRuns: 100,
87
87
  mode: "consoleNoColor",
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: 4202591380, fuzzRuns: 100, filter: null };
78
+ const flags = { initialSeed: 3733738384, 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: 4202591380,
85
+ initialSeed: 3733738384,
86
86
  fuzzRuns: 100,
87
87
  mode: "consoleNoColor",
88
88
  verbosity: verbosity,
@@ -221,7 +221,6 @@ export async function render(request) {
221
221
  addWatcher,
222
222
  false
223
223
  );
224
- console.dir(response);
225
224
  if (response.kind === "bytes") {
226
225
  return {
227
226
  body: response.contentDatPayload.buffer,
@@ -103,14 +103,17 @@ function find_anchor(node) {
103
103
  return /** @type {HTMLAnchorElement} */ (node);
104
104
  }
105
105
 
106
- Object.defineProperty(SubmitEvent.prototype, "fields", {
107
- get: function fields() {
108
- let formData = new FormData(this.currentTarget);
109
- if (this.submitter && this.submitter.name) {
110
- formData.append(this.submitter.name, this.submitter.value);
111
- }
112
- return [...formData.entries()];
113
- },
114
- });
106
+ // only run in modern browsers to prevent exception: https://github.com/dillonkearns/elm-pages/issues/427
107
+ if ("SubmitEvent" in window) {
108
+ Object.defineProperty(SubmitEvent.prototype, "fields", {
109
+ get: function fields() {
110
+ let formData = new FormData(this.currentTarget);
111
+ if (this.submitter && this.submitter.name) {
112
+ formData.append(this.submitter.name, this.submitter.value);
113
+ }
114
+ return [...formData.entries()];
115
+ },
116
+ });
117
+ }
115
118
 
116
119
  setup();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "elm-pages",
3
3
  "type": "module",
4
- "version": "3.0.8",
4
+ "version": "3.0.9",
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.13.0",
58
+ "cypress": "^13.3.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
@@ -65,10 +65,11 @@ You define your ApiRoute's in `app/Api.elm`. Here's a simple example:
65
65
 
66
66
  import ApiRoute
67
67
  import BackendTask exposing (BackendTask)
68
+ import FatalError exposing (FatalError)
68
69
  import Server.Request
69
70
 
70
71
  routes :
71
- BackendTask (List Route)
72
+ BackendTask FatalError (List Route)
72
73
  -> (Maybe { indent : Int, newLines : Bool } -> Html Never -> String)
73
74
  -> List (ApiRoute.ApiRoute ApiRoute.Response)
74
75
  routes getStaticRoutes htmlToString =
@@ -35,26 +35,23 @@ we're using `BackendTask.allowFatal` to let the framework treat that as an unexp
35
35
  ```javascript
36
36
  // custom-backend-task.js
37
37
 
38
- module.exports =
39
- /**
40
- * @param { unknown } fromElm
41
- * @returns { Promise<unknown> }
42
- */
43
- {
44
- environmentVariable: async function (name) {
45
- const result = process.env[name];
46
- if (result) {
47
- return result;
48
- } else {
49
- throw `No environment variable called ${name}
38
+ /**
39
+ * @param { string } fromElm
40
+ * @returns { Promise<string> }
41
+ */
42
+ export async function environmentVariable(name) {
43
+ const result = process.env[name];
44
+ if (result) {
45
+ return result;
46
+ } else {
47
+ throw `No environment variable called ${name}
50
48
 
51
49
  Available:
52
50
 
53
51
  ${Object.keys(process.env).join("\n")}
54
52
  `;
55
- }
56
- },
57
- }
53
+ }
54
+ }
58
55
  ```
59
56
 
60
57
 
@@ -167,6 +167,7 @@ bodyWithFrontmatter frontmatterDecoder filePath =
167
167
  { title = "BackendTask.File Decoder Error"
168
168
  , body =
169
169
  "I encountered a Json Decoder error from a call to BackendTask.File.bodyWithFrontmatter.\n\n"
170
+ ++ ("I was trying to process `" ++ filePath ++ "`.\n\n")
170
171
  ++ Decode.errorToString frontmatterDecodeError
171
172
  }
172
173
  |> FatalError.build
@@ -257,6 +258,7 @@ onlyFrontmatter frontmatterDecoder filePath =
257
258
  { title = "BackendTask.File Decoder Error"
258
259
  , body =
259
260
  "I encountered a Json Decoder error from a call to BackendTask.File.onlyFrontmatter.\n\n"
261
+ ++ ("I was trying to process `" ++ filePath ++ "`.\n\n")
260
262
  ++ Decode.errorToString frontmatterDecodeError
261
263
  }
262
264
  |> FatalError.build
@@ -28,7 +28,7 @@ With the `BackendTask.Glob` API, you could get all of those files like so:
28
28
 
29
29
  import BackendTask exposing (BackendTask)
30
30
 
31
- blogPostsGlob : BackendTask (List String)
31
+ blogPostsGlob : BackendTask error (List String)
32
32
  blogPostsGlob =
33
33
  Glob.succeed (\slug -> slug)
34
34
  |> Glob.match (Glob.literal "content/blog/")
@@ -69,7 +69,7 @@ There will be one argument for every `capture` in your pipeline, whereas `match`
69
69
  import BackendTask exposing (BackendTask)
70
70
  import BackendTask.Glob as Glob
71
71
 
72
- blogPostsGlob : BackendTask (List String)
72
+ blogPostsGlob : BackendTask error (List String)
73
73
  blogPostsGlob =
74
74
  Glob.succeed (\slug -> slug)
75
75
  -- no argument from this, but we will only
@@ -97,7 +97,7 @@ Let's try our blogPostsGlob from before, but change every `match` to `capture`.
97
97
  import BackendTask exposing (BackendTask)
98
98
 
99
99
  blogPostsGlob :
100
- BackendTask
100
+ BackendTask error
101
101
  (List
102
102
  { filePath : String
103
103
  , slug : String
@@ -155,20 +155,22 @@ This is my first post!
155
155
  Then we could read that title for our blog post list page using our `blogPosts` `BackendTask` that we defined above.
156
156
 
157
157
  import BackendTask.File
158
+ import FatalError exposing (FatalError)
158
159
  import Json.Decode as Decode exposing (Decoder)
159
160
 
160
- titles : BackendTask (List BlogPost)
161
+ titles : BackendTask FatalError (List BlogPost)
161
162
  titles =
162
163
  blogPosts
163
164
  |> BackendTask.map
164
165
  (List.map
165
166
  (\blogPost ->
166
- BackendTask.File.request
167
+ BackendTask.File.onlyFrontmatter
168
+ blogFrontmatterDecoder
167
169
  blogPost.filePath
168
- (BackendTask.File.frontmatter blogFrontmatterDecoder)
169
170
  )
170
171
  )
171
172
  |> BackendTask.resolve
173
+ |> BackendTask.allowFatal
172
174
 
173
175
  type alias BlogPost =
174
176
  { title : String }
@@ -249,7 +251,7 @@ could use
249
251
  import BackendTask exposing (BackendTask)
250
252
  import BackendTask.Glob as Glob
251
253
 
252
- blogPostsGlob : BackendTask (List String)
254
+ blogPostsGlob : BackendTask error (List String)
253
255
  blogPostsGlob =
254
256
  Glob.succeed (\slug -> slug)
255
257
  |> Glob.match (Glob.literal "content/blog/")
@@ -258,14 +260,14 @@ could use
258
260
  |> Glob.toBackendTask
259
261
 
260
262
  If you want to validate file formats, you can combine that with some `BackendTask` helpers to turn a `Glob (Result String value)` into
261
- a `BackendTask (List value)`.
263
+ a `BackendTask FatalError (List value)`.
262
264
 
263
265
  For example, you could take a date and parse it.
264
266
 
265
267
  import BackendTask exposing (BackendTask)
266
268
  import BackendTask.Glob as Glob
267
269
 
268
- example : BackendTask (List ( String, String ))
270
+ example : BackendTask FatalError (List ( String, String ))
269
271
  example =
270
272
  Glob.succeed
271
273
  (\dateResult slug ->
@@ -281,14 +283,14 @@ For example, you could take a date and parse it.
281
283
  |> BackendTask.map (List.map BackendTask.fromResult)
282
284
  |> BackendTask.resolve
283
285
 
284
- expectDateFormat : List String -> Result String String
286
+ expectDateFormat : List String -> Result FatalError String
285
287
  expectDateFormat dateParts =
286
288
  case dateParts of
287
289
  [ year, month, date ] ->
288
290
  Ok (String.join "-" [ year, month, date ])
289
291
 
290
292
  _ ->
291
- Err "Unexpected date format, expected yyyy/mm/dd folder structure."
293
+ Err <| FatalError.fromString "Unexpected date format, expected yyyy/mm/dd folder structure."
292
294
 
293
295
  -}
294
296
  map : (a -> b) -> Glob a -> Glob b
@@ -322,7 +324,7 @@ fullFilePath =
322
324
  import BackendTask.Glob as Glob
323
325
 
324
326
  blogPosts :
325
- BackendTask
327
+ BackendTask error
326
328
  (List
327
329
  { filePath : String
328
330
  , slug : String
@@ -368,11 +370,11 @@ match 0 or more path parts like, see `recursiveWildcard`.
368
370
  , slug : String
369
371
  }
370
372
 
371
- example : BackendTask (List BlogPost)
373
+ example : BackendTask error (List BlogPost)
372
374
  example =
373
375
  Glob.succeed BlogPost
374
376
  |> Glob.match (Glob.literal "blog/")
375
- |> Glob.match Glob.wildcard
377
+ |> Glob.capture Glob.wildcard
376
378
  |> Glob.match (Glob.literal "-")
377
379
  |> Glob.capture Glob.wildcard
378
380
  |> Glob.match (Glob.literal "-")
@@ -391,7 +393,7 @@ match 0 or more path parts like, see `recursiveWildcard`.
391
393
 
392
394
  That will match to:
393
395
 
394
- results : BackendTask (List BlogPost)
396
+ results : BackendTask error (List BlogPost)
395
397
  results =
396
398
  BackendTask.succeed
397
399
  [ { year = "2021"
@@ -443,7 +445,7 @@ Leading 0's are ignored.
443
445
  import BackendTask exposing (BackendTask)
444
446
  import BackendTask.Glob as Glob
445
447
 
446
- slides : BackendTask (List Int)
448
+ slides : BackendTask error (List Int)
447
449
  slides =
448
450
  Glob.succeed identity
449
451
  |> Glob.match (Glob.literal "slide-")
@@ -472,7 +474,7 @@ With files
472
474
 
473
475
  Yields
474
476
 
475
- matches : BackendTask (List Int)
477
+ matches : BackendTask error (List Int)
476
478
  matches =
477
479
  BackendTask.succeed
478
480
  [ 1
@@ -513,7 +515,7 @@ This is the elm-pages equivalent of `**/*.txt` in standard shell syntax:
513
515
  import BackendTask exposing (BackendTask)
514
516
  import BackendTask.Glob as Glob
515
517
 
516
- example : BackendTask (List ( List String, String ))
518
+ example : BackendTask error (List ( List String, String ))
517
519
  example =
518
520
  Glob.succeed Tuple.pair
519
521
  |> Glob.match (Glob.literal "articles/")
@@ -537,7 +539,7 @@ With these files:
537
539
 
538
540
  We would get the following matches:
539
541
 
540
- matches : BackendTask (List ( List String, String ))
542
+ matches : BackendTask error (List ( List String, String ))
541
543
  matches =
542
544
  BackendTask.succeed
543
545
  [ ( [ "archive", "1977", "06", "10" ], "apple-2-announced" )
@@ -552,14 +554,14 @@ And also note that it matches 0 path parts into an empty list.
552
554
  If we didn't include the `wildcard` after the `recursiveWildcard`, then we would only get
553
555
  a single level of matches because it is followed by a file extension.
554
556
 
555
- example : BackendTask (List String)
557
+ example : BackendTask error (List String)
556
558
  example =
557
559
  Glob.succeed identity
558
560
  |> Glob.match (Glob.literal "articles/")
559
561
  |> Glob.capture Glob.recursiveWildcard
560
562
  |> Glob.match (Glob.literal ".txt")
561
563
 
562
- matches : BackendTask (List String)
564
+ matches : BackendTask error (List String)
563
565
  matches =
564
566
  BackendTask.succeed
565
567
  [ "google-io-2021-recap"
@@ -677,7 +679,7 @@ Exactly the same as `match` except it also captures the matched sub-pattern.
677
679
  , slug : String
678
680
  }
679
681
 
680
- archives : BackendTask ArchivesArticle
682
+ archives : BackendTask error ArchivesArticle
681
683
  archives =
682
684
  Glob.succeed ArchivesArticle
683
685
  |> Glob.match (Glob.literal "archive/")
@@ -693,7 +695,7 @@ Exactly the same as `match` except it also captures the matched sub-pattern.
693
695
 
694
696
  The file `archive/1977/06/10/apple-2-released.md` will give us this match:
695
697
 
696
- matches : List ArchivesArticle
698
+ matches : List error ArchivesArticle
697
699
  matches =
698
700
  BackendTask.succeed
699
701
  [ { year = 1977
@@ -744,7 +746,7 @@ capture (Glob matcherPattern apply1) (Glob pattern apply2) =
744
746
  , extension : String
745
747
  }
746
748
 
747
- dataFiles : BackendTask (List DataFile)
749
+ dataFiles : BackendTask error (List DataFile)
748
750
  dataFiles =
749
751
  Glob.succeed DataFile
750
752
  |> Glob.match (Glob.literal "my-data/")
@@ -768,7 +770,7 @@ If we have the following files
768
770
 
769
771
  That gives us
770
772
 
771
- results : BackendTask (List DataFile)
773
+ results : BackendTask error (List DataFile)
772
774
  results =
773
775
  BackendTask.succeed
774
776
  [ { name = "authors"
@@ -781,7 +783,7 @@ That gives us
781
783
 
782
784
  You could also match an optional file path segment using `oneOf`.
783
785
 
784
- rootFilesMd : BackendTask (List String)
786
+ rootFilesMd : BackendTask error (List String)
785
787
  rootFilesMd =
786
788
  Glob.succeed (\slug -> slug)
787
789
  |> Glob.match (Glob.literal "blog/")
@@ -806,7 +808,7 @@ With these files:
806
808
 
807
809
  This would give us:
808
810
 
809
- results : BackendTask (List String)
811
+ results : BackendTask error (List String)
810
812
  results =
811
813
  BackendTask.succeed
812
814
  [ "first-post"
@@ -965,7 +967,7 @@ encodeOptions options =
965
967
 
966
968
  import BackendTask.Glob as Glob exposing (OnlyFolders, defaultOptions)
967
969
 
968
- matchingFiles : Glob a -> BackendTask (List a)
970
+ matchingFiles : Glob a -> BackendTask error (List a)
969
971
  matchingFiles glob =
970
972
  glob
971
973
  |> Glob.toBackendTaskWithOptions { defaultOptions | include = OnlyFolders }
@@ -1010,12 +1012,12 @@ For example, maybe you can have
1010
1012
  import BackendTask exposing (BackendTask)
1011
1013
  import BackendTask.Glob as Glob
1012
1014
 
1013
- findBlogBySlug : String -> BackendTask String
1015
+ findBlogBySlug : String -> BackendTask FatalError String
1014
1016
  findBlogBySlug slug =
1015
1017
  Glob.succeed identity
1016
1018
  |> Glob.captureFilePath
1017
1019
  |> Glob.match (Glob.literal "blog/")
1018
- |> Glob.capture (Glob.literal slug)
1020
+ |> Glob.match (Glob.literal slug)
1019
1021
  |> Glob.match
1020
1022
  (Glob.oneOf
1021
1023
  ( ( "", () )
@@ -1024,6 +1026,7 @@ For example, maybe you can have
1024
1026
  )
1025
1027
  |> Glob.match (Glob.literal ".md")
1026
1028
  |> Glob.expectUniqueMatch
1029
+ |> BackendTask.allowFatal
1027
1030
 
1028
1031
  If we used `findBlogBySlug "first-post"` with these files:
1029
1032
 
@@ -1035,7 +1038,7 @@ If we used `findBlogBySlug "first-post"` with these files:
1035
1038
 
1036
1039
  This would give us:
1037
1040
 
1038
- results : BackendTask String
1041
+ results : BackendTask FatalError String
1039
1042
  results =
1040
1043
  BackendTask.succeed "blog/first-post/index.md"
1041
1044
 
@@ -88,6 +88,7 @@ Any place in your `elm-pages` app where the framework lets you pass in a value o
88
88
 
89
89
  import FatalError exposing (FatalError)
90
90
  import Json.Encode
91
+ import List.Chunks
91
92
  import Pages.StaticHttpRequest exposing (RawRequest(..))
92
93
 
93
94
 
@@ -140,6 +141,7 @@ resolve =
140
141
  {-| Turn a list of `BackendTask`s into a single one.
141
142
 
142
143
  import BackendTask
144
+ import FatalError exposing (FatalError)
143
145
  import Json.Decode as Decode exposing (Decoder)
144
146
 
145
147
  type alias Pokemon =
@@ -147,7 +149,7 @@ resolve =
147
149
  , sprite : String
148
150
  }
149
151
 
150
- pokemonDetailRequest : BackendTask (List Pokemon)
152
+ pokemonDetailRequest : BackendTask FatalError (List Pokemon)
151
153
  pokemonDetailRequest =
152
154
  BackendTask.Http.getJson
153
155
  "https://pokeapi.co/api/v2/pokemon/?limit=3"
@@ -169,10 +171,31 @@ resolve =
169
171
  )
170
172
  )
171
173
  |> BackendTask.andThen BackendTask.combine
174
+ |> BackendTask.allowFatal
172
175
 
173
176
  -}
174
177
  combine : List (BackendTask error value) -> BackendTask error (List value)
175
178
  combine items =
179
+ items
180
+ |> List.Chunks.chunk 100
181
+ |> List.map combineHelp
182
+ |> List.Chunks.chunk 100
183
+ |> List.map combineHelp
184
+ |> List.Chunks.chunk 100
185
+ |> List.map combineHelp
186
+ |> combineHelp
187
+ |> map (List.concat >> List.concat >> List.concat)
188
+
189
+
190
+ {-| `combineHelp` on its own will overflow the stack with larger lists of tasks
191
+
192
+ dividing the tasks into smaller nested chunks and recombining makes `combine` stack safe
193
+
194
+ There's probably a way of doing this without the Lists but it's a neat trick to safely combine lots of tasks!
195
+
196
+ -}
197
+ combineHelp : List (BackendTask error value) -> BackendTask error (List value)
198
+ combineHelp items =
176
199
  List.foldl (map2 (::)) (succeed []) items |> map List.reverse
177
200
 
178
201
 
@@ -190,11 +213,11 @@ combine items =
190
213
  }
191
214
  )
192
215
  (get
193
- (Secrets.succeed "https://api.github.com/repos/dillonkearns/elm-pages")
216
+ "https://api.github.com/repos/dillonkearns/elm-pages"
194
217
  (Decode.field "stargazers_count" Decode.int)
195
218
  )
196
219
  (get
197
- (Secrets.succeed "https://api.github.com/repos/dillonkearns/elm-markdown")
220
+ "https://api.github.com/repos/dillonkearns/elm-markdown"
198
221
  (Decode.field "stargazers_count" Decode.int)
199
222
  )
200
223
 
@@ -239,17 +262,19 @@ map2 fn request1 request2 =
239
262
  from the previous response to build up the URL, headers, etc. that you send to the subsequent request.
240
263
 
241
264
  import BackendTask
265
+ import FatalError exposing (FatalError)
242
266
  import Json.Decode as Decode exposing (Decoder)
243
267
 
244
- licenseData : BackendTask String
268
+ licenseData : BackendTask FatalError String
245
269
  licenseData =
246
- BackendTask.Http.get
247
- (Secrets.succeed "https://api.github.com/repos/dillonkearns/elm-pages")
270
+ BackendTask.Http.getJson
271
+ "https://api.github.com/repos/dillonkearns/elm-pages"
248
272
  (Decode.at [ "license", "url" ] Decode.string)
249
273
  |> BackendTask.andThen
250
274
  (\licenseUrl ->
251
- BackendTask.Http.get (Secrets.succeed licenseUrl) (Decode.field "description" Decode.string)
275
+ BackendTask.Http.getJson licenseUrl (Decode.field "description" Decode.string)
252
276
  )
277
+ |> BackendTask.allowFatal
253
278
 
254
279
  -}
255
280
  andThen : (a -> BackendTask error b) -> BackendTask error a -> BackendTask error b
@@ -0,0 +1,51 @@
1
+ module List.Chunks exposing (chunk)
2
+
3
+ import Array exposing (Array)
4
+
5
+
6
+ {-| Adapted from <https://package.elm-lang.org/packages/krisajenkins/elm-exts/latest/Exts-List>
7
+
8
+ Split a list into chunks of length `n`.
9
+
10
+ Be aware that the last sub-list may be smaller than `n`-items long.
11
+
12
+ For example `chunk 3 [1..10] => [[1,2,3], [4,5,6], [7,8,9], [10]]`
13
+
14
+ -}
15
+ chunk : Int -> List a -> List (List a)
16
+ chunk n xs =
17
+ if n < 1 then
18
+ List.singleton xs
19
+
20
+ else
21
+ evaluate (chunkInternal n xs Array.empty)
22
+
23
+
24
+ chunkInternal : Int -> List a -> Array (List a) -> Trampoline (List (List a))
25
+ chunkInternal n xs acc =
26
+ -- elm-review: known-unoptimized-recursion
27
+ if List.isEmpty xs then
28
+ Done (Array.toList acc)
29
+
30
+ else
31
+ Jump
32
+ (\_ ->
33
+ chunkInternal n
34
+ (List.drop n xs)
35
+ (Array.push (List.take n xs) acc)
36
+ )
37
+
38
+
39
+ type Trampoline a
40
+ = Done a
41
+ | Jump (() -> Trampoline a)
42
+
43
+
44
+ evaluate : Trampoline a -> a
45
+ evaluate trampoline =
46
+ case trampoline of
47
+ Done value ->
48
+ value
49
+
50
+ Jump f ->
51
+ evaluate (f ())
@@ -27,7 +27,7 @@ Using these functions, you can store and read session data in cookies to maintai
27
27
  type alias Data =
28
28
  { darkMode : Bool }
29
29
 
30
- data : RouteParams -> Request -> BackendTask (Response Data ErrorPage)
30
+ data : RouteParams -> Request -> BackendTask FatalError (Response Data ErrorPage)
31
31
  data routeParams request =
32
32
  request
33
33
  |> Session.withSession
@@ -42,12 +42,15 @@ Using these functions, you can store and read session data in cookies to maintai
42
42
  (session |> Session.get "mode" |> Maybe.withDefault "light")
43
43
  == "dark"
44
44
  in
45
- ( session
46
- , { darkMode = darkMode }
47
- )
45
+ BackendTask.succeed
46
+ ( session
47
+ , Response.render
48
+ { darkMode = darkMode
49
+ }
50
+ )
48
51
  )
49
52
 
50
- The elm-pages framework will manage signing these cookies using the `secrets : BackendTask (List String)` you pass in.
53
+ The elm-pages framework will manage signing these cookies using the `secrets : BackendTask FatalError (List String)` you pass in.
51
54
  That means that the values you set in your session will be directly visible to anyone who has access to the cookie
52
55
  (so don't directly store sensitive data in your session). Since the session cookie is signed using the secret you provide,
53
56
  the cookie will be invalidated if it is tampered with because it won't match when elm-pages verifies that it has been
@@ -56,7 +59,7 @@ signed with your secrets. Of course you need to provide secure secrets and treat
56
59
 
57
60
  ### Rotating Secrets
58
61
 
59
- The first String in `secrets : BackendTask (List String)` will be used to sign sessions, while the remaining String's will
62
+ The first String in `secrets : BackendTask FatalError (List String)` will be used to sign sessions, while the remaining String's will
60
63
  still be used to attempt to "unsign" the cookies. So if you have a single secret:
61
64
 
62
65
  Session.withSession