elm-pages 3.0.0-beta.41 → 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.
- package/README.md +1 -1
- package/codegen/elm-pages-codegen.cjs +47 -23
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/d.dat +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/node_runner.js +1 -1
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/node_supervisor.js +1 -1
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/d.dat +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/js/node_runner.js +1 -1
- package/generator/review/elm-stuff/tests-0.19.1/js/node_supervisor.js +1 -1
- package/generator/src/RouteBuilder.elm +12 -54
- package/generator/src/compatibility-key.js +2 -2
- package/package.json +2 -2
- package/src/ApiRoute.elm +3 -31
- package/src/FormData.elm +21 -1
- package/src/Internal/Request.elm +84 -4
- package/src/Pages/FormData.elm +2 -1
- package/src/Pages/Internal/Platform/Cli.elm +15 -2
- package/src/Pages/Internal/Platform/CompatibilityKey.elm +1 -1
- package/src/Pages/Navigation.elm +0 -2
- package/src/Pages/ProgramConfig.elm +3 -2
- package/src/Scaffold/Route.elm +70 -50
- package/src/Server/Request.elm +445 -952
- package/src/Server/Session.elm +140 -90
package/src/Server/Session.elm
CHANGED
|
@@ -4,43 +4,48 @@ module Server.Session exposing
|
|
|
4
4
|
, Session, empty, get, insert, remove, update, withFlash
|
|
5
5
|
)
|
|
6
6
|
|
|
7
|
-
{-| You can manage server state with HTTP cookies using this Server.Session API. Server-rendered
|
|
8
|
-
|
|
7
|
+
{-| You can manage server state with HTTP cookies using this Server.Session API. Server-rendered routes have a `Server.Request.Request`
|
|
8
|
+
argument that lets you inspect the incoming HTTP request, and return a response using the `Server.Response.Response` type.
|
|
9
9
|
|
|
10
|
+
This API provides a higher-level abstraction for extracting data from the HTTP request, and setting data in the HTTP response.
|
|
11
|
+
It manages the session through key-value data stored in cookies, and lets you [`insert`](#insert), [`update`](#update), and [`remove`](#remove)
|
|
12
|
+
values from the Session. It also provides an abstraction for flash session values through [`withFlash`](#withFlash).
|
|
10
13
|
|
|
11
|
-
|
|
14
|
+
|
|
15
|
+
## Using Sessions
|
|
12
16
|
|
|
13
17
|
Using these functions, you can store and read session data in cookies to maintain state between requests.
|
|
14
|
-
For example, TODO:
|
|
15
|
-
|
|
16
|
-
action : RouteParams -> Request.Parser (BackendTask (Response ActionData ErrorPage))
|
|
17
|
-
action routeParams =
|
|
18
|
-
MySession.withSession
|
|
19
|
-
(Request.formDataWithServerValidation (form |> Form.initCombinedServer identity))
|
|
20
|
-
(\nameResultData session ->
|
|
21
|
-
nameResultData
|
|
22
|
-
|> BackendTask.map
|
|
23
|
-
(\nameResult ->
|
|
24
|
-
case nameResult of
|
|
25
|
-
Err errors ->
|
|
26
|
-
( session
|
|
27
|
-
|> Result.withDefault Nothing
|
|
28
|
-
|> Maybe.withDefault Session.empty
|
|
29
|
-
, Response.render
|
|
30
|
-
{ errors = errors
|
|
31
|
-
}
|
|
32
|
-
)
|
|
33
18
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
19
|
+
import Server.Session as Session
|
|
20
|
+
|
|
21
|
+
secrets : BackendTask FatalError (List String)
|
|
22
|
+
secrets =
|
|
23
|
+
Env.expect "SESSION_SECRET"
|
|
24
|
+
|> BackendTask.allowFatal
|
|
25
|
+
|> BackendTask.map List.singleton
|
|
26
|
+
|
|
27
|
+
type alias Data =
|
|
28
|
+
{ darkMode : Bool }
|
|
29
|
+
|
|
30
|
+
data : RouteParams -> Request -> BackendTask (Response Data ErrorPage)
|
|
31
|
+
data routeParams request =
|
|
32
|
+
request
|
|
33
|
+
|> Session.withSession
|
|
34
|
+
{ name = "mysession"
|
|
35
|
+
, secrets = secrets
|
|
36
|
+
, options = Nothing
|
|
37
|
+
}
|
|
38
|
+
(\session ->
|
|
39
|
+
let
|
|
40
|
+
darkMode : Bool
|
|
41
|
+
darkMode =
|
|
42
|
+
(session |> Session.get "mode" |> Maybe.withDefault "light")
|
|
43
|
+
== "dark"
|
|
44
|
+
in
|
|
45
|
+
( session
|
|
46
|
+
, { darkMode = darkMode }
|
|
47
|
+
)
|
|
48
|
+
)
|
|
44
49
|
|
|
45
50
|
The elm-pages framework will manage signing these cookies using the `secrets : BackendTask (List String)` you pass in.
|
|
46
51
|
That means that the values you set in your session will be directly visible to anyone who has access to the cookie
|
|
@@ -113,12 +118,17 @@ import BackendTask.Internal.Request
|
|
|
113
118
|
import Dict exposing (Dict)
|
|
114
119
|
import Json.Decode
|
|
115
120
|
import Json.Encode
|
|
116
|
-
import Server.Request
|
|
121
|
+
import Server.Request exposing (Request)
|
|
117
122
|
import Server.Response exposing (Response)
|
|
118
123
|
import Server.SetCookie as SetCookie
|
|
119
124
|
|
|
120
125
|
|
|
121
|
-
{-| -
|
|
126
|
+
{-| Represents a Session with key-value Strings.
|
|
127
|
+
|
|
128
|
+
Use with `withSession` to read in the `Session`, and encode any changes you make to the `Session` back through cookie storage
|
|
129
|
+
via the outgoing HTTP response.
|
|
130
|
+
|
|
131
|
+
-}
|
|
122
132
|
type Session
|
|
123
133
|
= Session (Dict String Value)
|
|
124
134
|
|
|
@@ -130,7 +140,12 @@ type Value
|
|
|
130
140
|
| NewFlash String
|
|
131
141
|
|
|
132
142
|
|
|
133
|
-
{-|
|
|
143
|
+
{-| Flash session values are values that are only available for the next request.
|
|
144
|
+
|
|
145
|
+
session
|
|
146
|
+
|> Session.withFlash "message" "Your payment was successful!"
|
|
147
|
+
|
|
148
|
+
-}
|
|
134
149
|
withFlash : String -> String -> Session -> Session
|
|
135
150
|
withFlash key value (Session session) =
|
|
136
151
|
session
|
|
@@ -138,7 +153,12 @@ withFlash key value (Session session) =
|
|
|
138
153
|
|> Session
|
|
139
154
|
|
|
140
155
|
|
|
141
|
-
{-|
|
|
156
|
+
{-| Insert a value under the given key in the `Session`.
|
|
157
|
+
|
|
158
|
+
session
|
|
159
|
+
|> Session.insert "mode" "dark"
|
|
160
|
+
|
|
161
|
+
-}
|
|
142
162
|
insert : String -> String -> Session -> Session
|
|
143
163
|
insert key value (Session session) =
|
|
144
164
|
session
|
|
@@ -146,7 +166,15 @@ insert key value (Session session) =
|
|
|
146
166
|
|> Session
|
|
147
167
|
|
|
148
168
|
|
|
149
|
-
{-|
|
|
169
|
+
{-| Retrieve a String value from the session for the given key (or `Nothing` if the key is not present).
|
|
170
|
+
|
|
171
|
+
(session
|
|
172
|
+
|> Session.get "mode"
|
|
173
|
+
|> Maybe.withDefault "light"
|
|
174
|
+
)
|
|
175
|
+
== "dark"
|
|
176
|
+
|
|
177
|
+
-}
|
|
150
178
|
get : String -> Session -> Maybe String
|
|
151
179
|
get key (Session session) =
|
|
152
180
|
session
|
|
@@ -168,7 +196,25 @@ unwrap value =
|
|
|
168
196
|
string
|
|
169
197
|
|
|
170
198
|
|
|
171
|
-
{-|
|
|
199
|
+
{-| Update the `Session`, given a `Maybe String` of the current value for the given key, and returning a `Maybe String`.
|
|
200
|
+
|
|
201
|
+
If you return `Nothing`, the key-value pair will be removed from the `Session` (or left out if it didn't exist in the first place).
|
|
202
|
+
|
|
203
|
+
session
|
|
204
|
+
|> Session.update "mode"
|
|
205
|
+
(\mode ->
|
|
206
|
+
case mode of
|
|
207
|
+
Just "dark" ->
|
|
208
|
+
Just "light"
|
|
209
|
+
|
|
210
|
+
Just "light" ->
|
|
211
|
+
Just "dark"
|
|
212
|
+
|
|
213
|
+
Nothing ->
|
|
214
|
+
Just "dark"
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
-}
|
|
172
218
|
update : String -> (Maybe String -> Maybe String) -> Session -> Session
|
|
173
219
|
update key updateFn (Session session) =
|
|
174
220
|
session
|
|
@@ -192,7 +238,8 @@ update key updateFn (Session session) =
|
|
|
192
238
|
|> Session
|
|
193
239
|
|
|
194
240
|
|
|
195
|
-
{-|
|
|
241
|
+
{-| Remove a key from the `Session`.
|
|
242
|
+
-}
|
|
196
243
|
remove : String -> Session -> Session
|
|
197
244
|
remove key (Session session) =
|
|
198
245
|
session
|
|
@@ -200,13 +247,15 @@ remove key (Session session) =
|
|
|
200
247
|
|> Session
|
|
201
248
|
|
|
202
249
|
|
|
203
|
-
{-| -
|
|
250
|
+
{-| An empty `Session` with no key-value pairs.
|
|
251
|
+
-}
|
|
204
252
|
empty : Session
|
|
205
253
|
empty =
|
|
206
254
|
Session Dict.empty
|
|
207
255
|
|
|
208
256
|
|
|
209
|
-
{-|
|
|
257
|
+
{-| [`withSessionResult`](#withSessionResult) will return a `Result` with this type if it can't load a session.
|
|
258
|
+
-}
|
|
210
259
|
type NotLoadedReason
|
|
211
260
|
= NoSessionCookie
|
|
212
261
|
| InvalidSessionCookie
|
|
@@ -239,65 +288,67 @@ flashPrefix =
|
|
|
239
288
|
"__flash__"
|
|
240
289
|
|
|
241
290
|
|
|
242
|
-
{-| -
|
|
291
|
+
{-| The main function for using sessions. If you need more fine-grained control over cases where a session can't be loaded, see
|
|
292
|
+
[`withSessionResult`](#withSessionResult).
|
|
293
|
+
-}
|
|
243
294
|
withSession :
|
|
244
295
|
{ name : String
|
|
245
296
|
, secrets : BackendTask error (List String)
|
|
246
297
|
, options : Maybe SetCookie.Options
|
|
247
298
|
}
|
|
248
|
-
-> (
|
|
249
|
-
->
|
|
250
|
-
->
|
|
251
|
-
withSession config toRequest
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
299
|
+
-> (Session -> BackendTask error ( Session, Response data errorPage ))
|
|
300
|
+
-> Request
|
|
301
|
+
-> BackendTask error (Response data errorPage)
|
|
302
|
+
withSession config toRequest request_ =
|
|
303
|
+
request_
|
|
304
|
+
|> withSessionResult config
|
|
305
|
+
(\session ->
|
|
306
|
+
session
|
|
256
307
|
|> Result.withDefault empty
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
userRequest
|
|
308
|
+
|> toRequest
|
|
309
|
+
)
|
|
260
310
|
|
|
261
311
|
|
|
262
|
-
{-|
|
|
312
|
+
{-| Same as `withSession`, but gives you an `Err` with the reason why the Session couldn't be loaded instead of
|
|
313
|
+
using `Session.empty` as a default in the cases where there is an error loading the session.
|
|
314
|
+
|
|
315
|
+
A session won't load if there is no session, or if it cannot be unsigned with your secrets. This could be because the cookie was tampered with
|
|
316
|
+
or otherwise corrupted, or because the cookie was signed with a secret that is no longer in the rotation.
|
|
317
|
+
|
|
318
|
+
-}
|
|
263
319
|
withSessionResult :
|
|
264
320
|
{ name : String
|
|
265
321
|
, secrets : BackendTask error (List String)
|
|
266
322
|
, options : Maybe SetCookie.Options
|
|
267
323
|
}
|
|
268
|
-
-> (
|
|
269
|
-
->
|
|
270
|
-
->
|
|
271
|
-
withSessionResult config
|
|
272
|
-
|
|
273
|
-
(
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
Err InvalidSessionCookie
|
|
289
|
-
)
|
|
324
|
+
-> (Result NotLoadedReason Session -> BackendTask error ( Session, Response data errorPage ))
|
|
325
|
+
-> Request
|
|
326
|
+
-> BackendTask error (Response data errorPage)
|
|
327
|
+
withSessionResult config toTask request =
|
|
328
|
+
let
|
|
329
|
+
unsigned : BackendTask error (Result NotLoadedReason Session)
|
|
330
|
+
unsigned =
|
|
331
|
+
case Server.Request.cookie config.name request of
|
|
332
|
+
Just sessionCookie ->
|
|
333
|
+
sessionCookie
|
|
334
|
+
|> unsignCookie config
|
|
335
|
+
|> BackendTask.map
|
|
336
|
+
(\unsignResult ->
|
|
337
|
+
case unsignResult of
|
|
338
|
+
Ok decoded ->
|
|
339
|
+
Ok decoded
|
|
340
|
+
|
|
341
|
+
Err () ->
|
|
342
|
+
Err InvalidSessionCookie
|
|
343
|
+
)
|
|
290
344
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
)
|
|
299
|
-
(Server.Request.cookie config.name)
|
|
300
|
-
userRequest
|
|
345
|
+
Nothing ->
|
|
346
|
+
Err NoSessionCookie
|
|
347
|
+
|> BackendTask.succeed
|
|
348
|
+
in
|
|
349
|
+
unsigned
|
|
350
|
+
|> BackendTask.andThen
|
|
351
|
+
(encodeSessionUpdate config toTask)
|
|
301
352
|
|
|
302
353
|
|
|
303
354
|
encodeSessionUpdate :
|
|
@@ -305,13 +356,12 @@ encodeSessionUpdate :
|
|
|
305
356
|
, secrets : BackendTask error (List String)
|
|
306
357
|
, options : Maybe SetCookie.Options
|
|
307
358
|
}
|
|
308
|
-
-> (
|
|
309
|
-
-> c
|
|
359
|
+
-> (d -> BackendTask error ( Session, Response data errorPage ))
|
|
310
360
|
-> d
|
|
311
361
|
-> BackendTask error (Response data errorPage)
|
|
312
|
-
encodeSessionUpdate config toRequest
|
|
362
|
+
encodeSessionUpdate config toRequest sessionResult =
|
|
313
363
|
sessionResult
|
|
314
|
-
|> toRequest
|
|
364
|
+
|> toRequest
|
|
315
365
|
|> BackendTask.andThen
|
|
316
366
|
(\( sessionUpdate, response ) ->
|
|
317
367
|
BackendTask.map
|