elm-ssr 0.1.0
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 +67 -0
- package/bin/elm-ssr.mjs +102 -0
- package/elm-src/ElmSsr/Action.elm +210 -0
- package/elm-src/ElmSsr/Document/Encode.elm +83 -0
- package/elm-src/ElmSsr/Document/Events.elm +125 -0
- package/elm-src/ElmSsr/Document.elm +26 -0
- package/elm-src/ElmSsr/Html/Attributes.elm +344 -0
- package/elm-src/ElmSsr/Html/Events.elm +95 -0
- package/elm-src/ElmSsr/Html.elm +706 -0
- package/elm-src/ElmSsr/Island/Shared.elm +38 -0
- package/elm-src/ElmSsr/Island.elm +49 -0
- package/elm-src/ElmSsr/Loader.elm +297 -0
- package/elm-src/ElmSsr/Page.elm +102 -0
- package/elm-src/ElmSsr/Route.elm +136 -0
- package/elm-src/ElmSsr/Runtime.elm +170 -0
- package/elm-src/ElmSsr/Svg/Attributes.elm +1208 -0
- package/elm-src/ElmSsr/Svg.elm +309 -0
- package/lib/build.mjs +256 -0
- package/lib/migrate.mjs +146 -0
- package/lib/scaffold.mjs +472 -0
- package/lib/workspace.mjs +21 -0
- package/package.json +60 -0
- package/src/app.ts +74 -0
- package/src/backends.ts +116 -0
- package/src/client-runtime/islands.ts +247 -0
- package/src/effects.ts +267 -0
- package/src/http.ts +86 -0
- package/src/middleware.ts +104 -0
- package/src/migrations.ts +225 -0
- package/src/protocol.ts +119 -0
- package/src/render.ts +111 -0
- package/src/request-handler.ts +208 -0
- package/src/response-headers.ts +18 -0
- package/src/serialize.ts +47 -0
- package/src/tasks.ts +139 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
module ElmSsr.Runtime exposing
|
|
2
|
+
( Config, Ports
|
|
3
|
+
, State, Msg
|
|
4
|
+
, program
|
|
5
|
+
)
|
|
6
|
+
|
|
7
|
+
{-| The engine that renders routes.
|
|
8
|
+
|
|
9
|
+
Every route is a stateless page: the runtime decodes the request, runs the
|
|
10
|
+
matched route's loader (pumping its effects through the Worker), and renders the
|
|
11
|
+
resulting document. There is no document-level update loop — all interactivity
|
|
12
|
+
lives in islands ([`ElmSsr.Island`](./Island.elm)).
|
|
13
|
+
|
|
14
|
+
Application authors never call this directly; the generated `Main` wires the
|
|
15
|
+
ports and the file-based router to [`program`](#program).
|
|
16
|
+
|
|
17
|
+
@docs Config, Ports
|
|
18
|
+
@docs State, Msg
|
|
19
|
+
@docs program
|
|
20
|
+
|
|
21
|
+
-}
|
|
22
|
+
|
|
23
|
+
import ElmSsr.Action as Action exposing (Action)
|
|
24
|
+
import ElmSsr.Document as Document exposing (Document)
|
|
25
|
+
import ElmSsr.Document.Encode as Encode
|
|
26
|
+
import ElmSsr.Loader as Loader exposing (Loader)
|
|
27
|
+
import ElmSsr.Page as Page
|
|
28
|
+
import ElmSsr.Route as Route exposing (Request)
|
|
29
|
+
import Json.Decode as Decode
|
|
30
|
+
import Json.Encode as Json
|
|
31
|
+
import Platform
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
{-| The ports the generated `Main` connects to the JS runtime. -}
|
|
35
|
+
type alias Ports =
|
|
36
|
+
{ effectRequest : Json.Value -> Cmd Msg
|
|
37
|
+
, effectResult : (Decode.Value -> Msg) -> Sub Msg
|
|
38
|
+
, rendered : Json.Value -> Cmd Msg
|
|
39
|
+
, start : (Decode.Value -> Msg) -> Sub Msg
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
{-| The file-based router plus the ports. -}
|
|
44
|
+
type alias Config =
|
|
45
|
+
{ router : Request -> Loader (Document Never)
|
|
46
|
+
, action : Request -> Action (Document Never)
|
|
47
|
+
, ports : Ports
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
{-| Runtime state for one request. -}
|
|
52
|
+
type State
|
|
53
|
+
= AwaitingStart Request
|
|
54
|
+
| LoadingPage (Decode.Value -> Loader (Document Never))
|
|
55
|
+
| PerformingAction (Decode.Value -> Action (Document Never))
|
|
56
|
+
| Rendered
|
|
57
|
+
| Aborted Int String
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
{-| Runtime messages. -}
|
|
61
|
+
type Msg
|
|
62
|
+
= StartRequested
|
|
63
|
+
| EffectResolved Decode.Value
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
{-| Build the `Program` for an app's routes. -}
|
|
67
|
+
program : Config -> Program Decode.Value State Msg
|
|
68
|
+
program config =
|
|
69
|
+
Platform.worker
|
|
70
|
+
{ init = init
|
|
71
|
+
, update = update config
|
|
72
|
+
, subscriptions = subscriptions config
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
init : Decode.Value -> ( State, Cmd Msg )
|
|
77
|
+
init flags =
|
|
78
|
+
case Decode.decodeValue Route.decoder flags of
|
|
79
|
+
Ok request ->
|
|
80
|
+
( AwaitingStart request, Cmd.none )
|
|
81
|
+
|
|
82
|
+
Err decodeError ->
|
|
83
|
+
( Aborted 400 ("Invalid request flags: " ++ Decode.errorToString decodeError), Cmd.none )
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
update : Config -> Msg -> State -> ( State, Cmd Msg )
|
|
87
|
+
update config msg state =
|
|
88
|
+
case msg of
|
|
89
|
+
StartRequested ->
|
|
90
|
+
case state of
|
|
91
|
+
AwaitingStart request ->
|
|
92
|
+
if Route.method request == "GET" || Route.method request == "HEAD" then
|
|
93
|
+
advance config (config.router request)
|
|
94
|
+
|
|
95
|
+
else
|
|
96
|
+
advanceAction config (config.action request)
|
|
97
|
+
|
|
98
|
+
Aborted status message ->
|
|
99
|
+
( state, renderError config status message )
|
|
100
|
+
|
|
101
|
+
_ ->
|
|
102
|
+
( state, Cmd.none )
|
|
103
|
+
|
|
104
|
+
EffectResolved value ->
|
|
105
|
+
case state of
|
|
106
|
+
LoadingPage continue ->
|
|
107
|
+
advance config (continue value)
|
|
108
|
+
|
|
109
|
+
PerformingAction continue ->
|
|
110
|
+
advanceAction config (continue value)
|
|
111
|
+
|
|
112
|
+
_ ->
|
|
113
|
+
( state, Cmd.none )
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
advance : Config -> Loader (Document Never) -> ( State, Cmd Msg )
|
|
117
|
+
advance config loader =
|
|
118
|
+
case Loader.step loader of
|
|
119
|
+
Loader.Resolved document ->
|
|
120
|
+
( Rendered, render config document )
|
|
121
|
+
|
|
122
|
+
Loader.Errored status message ->
|
|
123
|
+
( Aborted status message, renderError config status message )
|
|
124
|
+
|
|
125
|
+
Loader.Await effect continue ->
|
|
126
|
+
( LoadingPage continue, config.ports.effectRequest (Loader.encodeEffect effect) )
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
advanceAction : Config -> Action (Document Never) -> ( State, Cmd Msg )
|
|
130
|
+
advanceAction config action =
|
|
131
|
+
case Action.step action of
|
|
132
|
+
Action.Resolved document ->
|
|
133
|
+
( Rendered, render config document )
|
|
134
|
+
|
|
135
|
+
Action.Errored status message ->
|
|
136
|
+
( Aborted status message, renderError config status message )
|
|
137
|
+
|
|
138
|
+
Action.Moved url ->
|
|
139
|
+
( Rendered, config.ports.rendered (Action.encodeStep (always Json.null) (Action.Moved url)) )
|
|
140
|
+
|
|
141
|
+
Action.SentJson value ->
|
|
142
|
+
( Rendered, config.ports.rendered (Action.encodeStep (always Json.null) (Action.SentJson value)) )
|
|
143
|
+
|
|
144
|
+
Action.Await effect continue ->
|
|
145
|
+
( PerformingAction continue, config.ports.effectRequest (Loader.encodeEffect effect) )
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
render : Config -> Document Never -> Cmd Msg
|
|
149
|
+
render config document =
|
|
150
|
+
config.ports.rendered (Action.encodeStep Encode.encode (Action.Resolved (Document.map never document)))
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
renderError : Config -> Int -> String -> Cmd Msg
|
|
154
|
+
renderError config status message =
|
|
155
|
+
config.ports.rendered
|
|
156
|
+
(Json.object
|
|
157
|
+
[ ( "kind", Json.string "errored" )
|
|
158
|
+
, ( "status", Json.int status )
|
|
159
|
+
, ( "message", Json.string message )
|
|
160
|
+
, ( "value", Encode.encode (Document.map never (Page.error status message)) )
|
|
161
|
+
]
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
subscriptions : Config -> State -> Sub Msg
|
|
166
|
+
subscriptions config _ =
|
|
167
|
+
Sub.batch
|
|
168
|
+
[ config.ports.start (\_ -> StartRequested)
|
|
169
|
+
, config.ports.effectResult EffectResolved
|
|
170
|
+
]
|