elm-pages 2.1.7 → 2.1.11

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 (75) hide show
  1. package/generator/review/elm.json +34 -0
  2. package/generator/review/src/ReviewConfig.elm +10 -0
  3. package/generator/src/basepath-middleware.js +15 -9
  4. package/generator/src/build.js +77 -4
  5. package/generator/src/cli.js +13 -9
  6. package/generator/src/compile-elm.js +43 -0
  7. package/generator/src/dev-server.js +63 -11
  8. package/generator/src/error-formatter.js +62 -9
  9. package/generator/src/generate-template-module-connector.js +17 -4
  10. package/generator/src/init.js +4 -0
  11. package/generator/src/pre-render-html.js +19 -12
  12. package/generator/src/render-worker.js +0 -1
  13. package/generator/src/render.js +1 -2
  14. package/generator/src/seo-renderer.js +21 -2
  15. package/generator/static-code/hmr.js +43 -6
  16. package/generator/template/elm.json +13 -5
  17. package/generator/template/package.json +3 -2
  18. package/package.json +14 -8
  19. package/src/ApiRoute.elm +178 -0
  20. package/src/AriaLiveAnnouncer.elm +36 -0
  21. package/src/BuildError.elm +60 -0
  22. package/src/DataSource/File.elm +288 -0
  23. package/src/DataSource/Glob.elm +1050 -0
  24. package/src/DataSource/Http.elm +467 -0
  25. package/src/DataSource/Internal/Glob.elm +74 -0
  26. package/src/DataSource/Port.elm +87 -0
  27. package/src/DataSource/ServerRequest.elm +60 -0
  28. package/src/DataSource.elm +801 -0
  29. package/src/Head/Seo.elm +516 -0
  30. package/src/Head/Twitter.elm +109 -0
  31. package/src/Head.elm +452 -0
  32. package/src/HtmlPrinter.elm +27 -0
  33. package/src/Internal/ApiRoute.elm +89 -0
  34. package/src/Internal/OptimizedDecoder.elm +18 -0
  35. package/src/KeepOrDiscard.elm +6 -0
  36. package/src/OptimizedDecoder/Pipeline.elm +335 -0
  37. package/src/OptimizedDecoder.elm +818 -0
  38. package/src/Pages/ContentCache.elm +248 -0
  39. package/src/Pages/Flags.elm +26 -0
  40. package/src/Pages/Http.elm +10 -0
  41. package/src/Pages/Internal/ApplicationType.elm +6 -0
  42. package/src/Pages/Internal/NotFoundReason.elm +256 -0
  43. package/src/Pages/Internal/Platform/Cli.elm +1015 -0
  44. package/src/Pages/Internal/Platform/Effect.elm +14 -0
  45. package/src/Pages/Internal/Platform/StaticResponses.elm +540 -0
  46. package/src/Pages/Internal/Platform/ToJsPayload.elm +138 -0
  47. package/src/Pages/Internal/Platform.elm +745 -0
  48. package/src/Pages/Internal/RoutePattern.elm +122 -0
  49. package/src/Pages/Internal/Router.elm +116 -0
  50. package/src/Pages/Internal/StaticHttpBody.elm +54 -0
  51. package/src/Pages/Internal/String.elm +39 -0
  52. package/src/Pages/Manifest/Category.elm +240 -0
  53. package/src/Pages/Manifest.elm +412 -0
  54. package/src/Pages/PageUrl.elm +38 -0
  55. package/src/Pages/ProgramConfig.elm +73 -0
  56. package/src/Pages/Review/NoContractViolations.elm +397 -0
  57. package/src/Pages/Secrets.elm +83 -0
  58. package/src/Pages/SiteConfig.elm +13 -0
  59. package/src/Pages/StaticHttp/Request.elm +42 -0
  60. package/src/Pages/StaticHttpRequest.elm +320 -0
  61. package/src/Pages/Url.elm +60 -0
  62. package/src/Path.elm +96 -0
  63. package/src/QueryParams.elm +216 -0
  64. package/src/RenderRequest.elm +163 -0
  65. package/src/RequestsAndPending.elm +20 -0
  66. package/src/Secrets.elm +111 -0
  67. package/src/SecretsDict.elm +45 -0
  68. package/src/StructuredData.elm +236 -0
  69. package/src/TerminalText.elm +242 -0
  70. package/src/Test/Html/Internal/ElmHtml/Constants.elm +53 -0
  71. package/src/Test/Html/Internal/ElmHtml/Helpers.elm +17 -0
  72. package/src/Test/Html/Internal/ElmHtml/InternalTypes.elm +529 -0
  73. package/src/Test/Html/Internal/ElmHtml/Markdown.elm +56 -0
  74. package/src/Test/Html/Internal/ElmHtml/ToString.elm +197 -0
  75. package/src/Test/Internal/KernelConstants.elm +34 -0
@@ -0,0 +1,163 @@
1
+ module RenderRequest exposing
2
+ ( IncludeHtml(..)
3
+ , RenderRequest(..)
4
+ , RequestPayload(..)
5
+ , decoder
6
+ , default
7
+ , maybeRequestPayload
8
+ )
9
+
10
+ import ApiRoute
11
+ import HtmlPrinter
12
+ import Internal.ApiRoute
13
+ import Json.Decode as Decode
14
+ import Json.Encode as Encode
15
+ import Pages.ProgramConfig exposing (ProgramConfig)
16
+ import Path exposing (Path)
17
+ import Regex
18
+ import Url exposing (Url)
19
+
20
+
21
+ type RequestPayload route
22
+ = Page { path : Path, frontmatter : route }
23
+ | Api ( String, ApiRoute.ApiRoute ApiRoute.Response )
24
+ | NotFound Path
25
+
26
+
27
+ type RenderRequest route
28
+ = SinglePage IncludeHtml (RequestPayload route) Decode.Value
29
+
30
+
31
+ default : RenderRequest route
32
+ default =
33
+ SinglePage
34
+ HtmlAndJson
35
+ (NotFound (Path.fromString "/error"))
36
+ Encode.null
37
+
38
+
39
+ maybeRequestPayload : RenderRequest route -> Maybe Decode.Value
40
+ maybeRequestPayload renderRequest =
41
+ case renderRequest of
42
+ SinglePage _ _ rawJson ->
43
+ Just rawJson
44
+
45
+
46
+ type IncludeHtml
47
+ = HtmlAndJson
48
+ | OnlyJson
49
+
50
+
51
+ decoder :
52
+ ProgramConfig userMsg userModel (Maybe route) siteData pageData sharedData
53
+ -> Decode.Decoder (RenderRequest (Maybe route))
54
+ decoder config =
55
+ Decode.field "request"
56
+ (Decode.map3
57
+ (\includeHtml requestThing payload ->
58
+ SinglePage includeHtml requestThing payload
59
+ )
60
+ (Decode.field "kind" Decode.string
61
+ |> Decode.andThen
62
+ (\kind ->
63
+ case kind of
64
+ "single-page" ->
65
+ Decode.field "jsonOnly" Decode.bool
66
+ |> Decode.map
67
+ (\jsonOnly ->
68
+ if jsonOnly then
69
+ OnlyJson
70
+
71
+ else
72
+ HtmlAndJson
73
+ )
74
+
75
+ _ ->
76
+ Decode.fail "Unhandled"
77
+ )
78
+ )
79
+ (requestPayloadDecoder config)
80
+ (Decode.field "payload" Decode.value)
81
+ )
82
+
83
+
84
+
85
+ {-
86
+ payload: modifiedRequest,
87
+ kind: "single-page",
88
+ jsonOnly: isJson,
89
+ -}
90
+
91
+
92
+ requestPayloadDecoder :
93
+ ProgramConfig userMsg userModel (Maybe route) siteData pageData sharedData
94
+ -> Decode.Decoder (RequestPayload (Maybe route))
95
+ requestPayloadDecoder config =
96
+ (Decode.string
97
+ |> Decode.map
98
+ (\rawPath ->
99
+ let
100
+ path : String
101
+ path =
102
+ rawPath
103
+ |> dropTrailingIndexHtml
104
+
105
+ route : Maybe route
106
+ route =
107
+ pathToUrl path |> config.urlToRoute
108
+
109
+ apiRoute : Maybe (ApiRoute.ApiRoute ApiRoute.Response)
110
+ apiRoute =
111
+ Internal.ApiRoute.firstMatch (String.dropLeft 1 path)
112
+ (config.apiRoutes HtmlPrinter.htmlToString)
113
+ in
114
+ case route of
115
+ Just _ ->
116
+ if isFile rawPath then
117
+ case apiRoute of
118
+ Just justApi ->
119
+ ( path, justApi ) |> Api
120
+
121
+ Nothing ->
122
+ NotFound (Path.fromString path)
123
+
124
+ else
125
+ Page
126
+ { frontmatter = route
127
+ , path = config.routeToPath route |> Path.join
128
+ }
129
+
130
+ Nothing ->
131
+ case apiRoute of
132
+ Just justApi ->
133
+ ( path, justApi ) |> Api
134
+
135
+ Nothing ->
136
+ NotFound (Path.fromString path)
137
+ )
138
+ )
139
+ |> Decode.field "path"
140
+ |> Decode.field "payload"
141
+
142
+
143
+ isFile : String -> Bool
144
+ isFile rawPath =
145
+ rawPath
146
+ |> String.contains "."
147
+
148
+
149
+ pathToUrl : String -> Url
150
+ pathToUrl path =
151
+ { protocol = Url.Https
152
+ , host = "TODO"
153
+ , port_ = Nothing
154
+ , path = path
155
+ , query = Nothing
156
+ , fragment = Nothing
157
+ }
158
+
159
+
160
+ dropTrailingIndexHtml : String -> String
161
+ dropTrailingIndexHtml =
162
+ Regex.replace (Regex.fromString "/index\\.html$" |> Maybe.withDefault Regex.never)
163
+ (\_ -> "")
@@ -0,0 +1,20 @@
1
+ module RequestsAndPending exposing (RequestsAndPending, decoder, get)
2
+
3
+ import Dict exposing (Dict)
4
+ import Json.Decode as Decode
5
+
6
+
7
+ type alias RequestsAndPending =
8
+ Dict String (Maybe String)
9
+
10
+
11
+ get : String -> RequestsAndPending -> Maybe String
12
+ get key requestsAndPending =
13
+ requestsAndPending
14
+ |> Dict.get key
15
+ |> Maybe.andThen identity
16
+
17
+
18
+ decoder : Decode.Decoder RequestsAndPending
19
+ decoder =
20
+ Decode.dict (Decode.string |> Decode.map Just)
@@ -0,0 +1,111 @@
1
+ module Secrets exposing
2
+ ( Value
3
+ , lookup
4
+ , map
5
+ , maskedLookup
6
+ , succeed
7
+ , with
8
+ )
9
+
10
+ import BuildError exposing (BuildError)
11
+ import Fuzzy
12
+ import SecretsDict exposing (SecretsDict)
13
+ import TerminalText as Terminal
14
+
15
+
16
+ type Value value
17
+ = Value (SecretsDict -> Result (List BuildError) value)
18
+
19
+
20
+ lookup : SecretsDict -> Value a -> Result (List BuildError) a
21
+ lookup secrets (Value lookupSecrets) =
22
+ lookupSecrets secrets
23
+
24
+
25
+ maskedLookup : Value value -> value
26
+ maskedLookup (Value lookupSecrets) =
27
+ case lookupSecrets SecretsDict.masked of
28
+ Ok value ->
29
+ value
30
+
31
+ Err _ ->
32
+ -- crash
33
+ maskedLookup (Value lookupSecrets)
34
+
35
+
36
+ succeed : value -> Value value
37
+ succeed value =
38
+ Value (\_ -> Ok value)
39
+
40
+
41
+ buildError : String -> SecretsDict -> BuildError
42
+ buildError secretName secretsDict =
43
+ let
44
+ availableEnvironmentVariables : List String
45
+ availableEnvironmentVariables =
46
+ SecretsDict.available secretsDict
47
+ in
48
+ { title = "Missing Secret"
49
+ , message =
50
+ [ Terminal.text "I expected to find this Secret in your environment variables but didn't find a match:\n\nSecrets.get \""
51
+ , Terminal.text secretName
52
+ , Terminal.text "\"\n "
53
+ , Terminal.red <| underlineText (secretName |> String.length)
54
+ , Terminal.text "\n\nSo maybe "
55
+ , Terminal.yellow <| secretName
56
+ , Terminal.text " should be "
57
+ , Terminal.green <| (sortMatches secretName availableEnvironmentVariables |> List.head |> Maybe.withDefault "")
58
+ ]
59
+ , path = "" -- TODO wire in path here?
60
+ , fatal = True
61
+ }
62
+
63
+
64
+ underlineText : Int -> String
65
+ underlineText length =
66
+ String.repeat length "^"
67
+
68
+
69
+ sortMatches : String -> List String -> List String
70
+ sortMatches missingSecret availableSecrets =
71
+ let
72
+ simpleMatch : List Fuzzy.Config -> List String -> String -> String -> Int
73
+ simpleMatch config separators needle hay =
74
+ Fuzzy.match config separators needle hay |> .score
75
+ in
76
+ List.sortBy (simpleMatch [] [] missingSecret) availableSecrets
77
+
78
+
79
+ map : (valueA -> valueB) -> Value valueA -> Value valueB
80
+ map mapFunction (Value lookupSecrets) =
81
+ Value
82
+ (\secrets ->
83
+ lookupSecrets secrets
84
+ |> Result.map mapFunction
85
+ )
86
+
87
+
88
+ with : String -> Value (String -> value) -> Value value
89
+ with newSecret (Value lookupSecrets) =
90
+ Value <|
91
+ \secrets ->
92
+ case lookupSecrets secrets of
93
+ Ok value ->
94
+ case SecretsDict.get newSecret secrets of
95
+ Just newValue ->
96
+ value newValue |> Ok
97
+
98
+ Nothing ->
99
+ Err [ buildError newSecret secrets ]
100
+
101
+ Err error ->
102
+ case SecretsDict.get newSecret secrets of
103
+ Just _ ->
104
+ Err error
105
+
106
+ Nothing ->
107
+ -- TODO add more errors
108
+ Err
109
+ (buildError newSecret secrets
110
+ :: error
111
+ )
@@ -0,0 +1,45 @@
1
+ module SecretsDict exposing (SecretsDict, available, decoder, get, masked, unmasked)
2
+
3
+ import Dict exposing (Dict)
4
+ import Json.Decode as Decode exposing (Decoder)
5
+
6
+
7
+ available : SecretsDict -> List String
8
+ available secretsDict =
9
+ case secretsDict of
10
+ Masked ->
11
+ []
12
+
13
+ Unmasked dict ->
14
+ Dict.keys dict
15
+
16
+
17
+ decoder : Decoder SecretsDict
18
+ decoder =
19
+ Decode.dict Decode.string
20
+ |> Decode.map Unmasked
21
+
22
+
23
+ unmasked : Dict String String -> SecretsDict
24
+ unmasked dict =
25
+ Unmasked dict
26
+
27
+
28
+ masked : SecretsDict
29
+ masked =
30
+ Masked
31
+
32
+
33
+ get : String -> SecretsDict -> Maybe String
34
+ get secretName secretsDict =
35
+ case secretsDict of
36
+ Masked ->
37
+ Just <| "<" ++ secretName ++ ">"
38
+
39
+ Unmasked dict ->
40
+ dict |> Dict.get secretName
41
+
42
+
43
+ type SecretsDict
44
+ = Masked
45
+ | Unmasked (Dict String String)
@@ -0,0 +1,236 @@
1
+ module StructuredData exposing (StructuredData(..), additionalName, article, article_, computerLanguage, elmLang, encode, episode, person, series, softwareSourceCode)
2
+
3
+ import Json.Encode as Encode
4
+ import Pages.Url
5
+
6
+
7
+ {-| <https://schema.org/SoftwareSourceCode>
8
+ -}
9
+ softwareSourceCode :
10
+ { codeRepositoryUrl : String
11
+ , description : String
12
+ , author : String
13
+ , programmingLanguage : Encode.Value
14
+ }
15
+ -> Encode.Value
16
+ softwareSourceCode info =
17
+ Encode.object
18
+ [ ( "@type", Encode.string "SoftwareSourceCode" )
19
+ , ( "codeRepository", Encode.string info.codeRepositoryUrl )
20
+ , ( "description", Encode.string info.description )
21
+ , ( "author", Encode.string info.author )
22
+ , ( "programmingLanguage", info.programmingLanguage )
23
+ ]
24
+
25
+
26
+ {-| <https://schema.org/ComputerLanguage>
27
+ -}
28
+ computerLanguage : { url : String, name : String, imageUrl : String, identifier : String } -> Encode.Value
29
+ computerLanguage info =
30
+ Encode.object
31
+ [ ( "@type", Encode.string "ComputerLanguage" )
32
+ , ( "url", Encode.string info.url )
33
+ , ( "name", Encode.string info.name )
34
+ , ( "image", Encode.string info.imageUrl )
35
+ , ( "identifier", Encode.string info.identifier )
36
+ ]
37
+
38
+
39
+ elmLang : Encode.Value
40
+ elmLang =
41
+ computerLanguage
42
+ { url = "http://elm-lang.org/"
43
+ , name = "Elm"
44
+ , imageUrl = "http://elm-lang.org/"
45
+ , identifier = "http://elm-lang.org/"
46
+ }
47
+
48
+
49
+ {-| <https://schema.org/Article>
50
+ -}
51
+ article :
52
+ { title : String
53
+ , description : String
54
+ , author : StructuredData { authorMemberOf | personOrOrganization : () } authorPossibleFields
55
+ , publisher : StructuredData { publisherMemberOf | personOrOrganization : () } publisherPossibleFields
56
+ , url : String
57
+ , imageUrl : Pages.Url.Url
58
+ , datePublished : String
59
+ , mainEntityOfPage : Encode.Value
60
+ }
61
+ -> Encode.Value
62
+ article info =
63
+ Encode.object
64
+ [ ( "@context", Encode.string "http://schema.org/" )
65
+ , ( "@type", Encode.string "Article" )
66
+ , ( "headline", Encode.string info.title )
67
+ , ( "description", Encode.string info.description )
68
+ , ( "image", Encode.string (Pages.Url.toString info.imageUrl) )
69
+ , ( "author", encode info.author )
70
+ , ( "publisher", encode info.publisher )
71
+ , ( "url", Encode.string info.url )
72
+ , ( "datePublished", Encode.string info.datePublished )
73
+ , ( "mainEntityOfPage", info.mainEntityOfPage )
74
+ ]
75
+
76
+
77
+ type StructuredData memberOf possibleFields
78
+ = StructuredData String (List ( String, Encode.Value ))
79
+
80
+
81
+ {-| <https://schema.org/Person>
82
+ -}
83
+ person :
84
+ { name : String
85
+ }
86
+ ->
87
+ StructuredData
88
+ { personOrOrganization : () }
89
+ { additionalName : ()
90
+ , address : ()
91
+ , affiliation : ()
92
+ }
93
+ person info =
94
+ StructuredData "Person" [ ( "name", Encode.string info.name ) ]
95
+
96
+
97
+ additionalName : String -> StructuredData memberOf { possibleFields | additionalName : () } -> StructuredData memberOf possibleFields
98
+ additionalName value (StructuredData typeName fields) =
99
+ StructuredData typeName (( "additionalName", Encode.string value ) :: fields)
100
+
101
+
102
+ {-| <https://schema.org/Article>
103
+ -}
104
+ article_ :
105
+ { title : String
106
+ , description : String
107
+ , author : String
108
+ , publisher : StructuredData { personOrOrganization : () } possibleFieldsPublisher
109
+ , url : String
110
+ , imageUrl : String
111
+ , datePublished : String
112
+ , mainEntityOfPage : Encode.Value
113
+ }
114
+ -> Encode.Value
115
+ article_ info =
116
+ Encode.object
117
+ [ ( "@context", Encode.string "http://schema.org/" )
118
+ , ( "@type", Encode.string "Article" )
119
+ , ( "headline", Encode.string info.title )
120
+ , ( "description", Encode.string info.description )
121
+ , ( "image", Encode.string info.imageUrl )
122
+ , ( "author", Encode.string info.author )
123
+ , ( "publisher", encode info.publisher )
124
+ , ( "url", Encode.string info.url )
125
+ , ( "datePublished", Encode.string info.datePublished )
126
+ , ( "mainEntityOfPage", info.mainEntityOfPage )
127
+ ]
128
+
129
+
130
+ encode : StructuredData memberOf possibleFieldsPublisher -> Encode.Value
131
+ encode (StructuredData typeName fields) =
132
+ Encode.object
133
+ (( "@type", Encode.string typeName ) :: fields)
134
+
135
+
136
+
137
+ --example :
138
+ -- StructuredData
139
+ -- { personOrOrganization : () }
140
+ -- { address : ()
141
+ -- , affiliation : ()
142
+ -- }
143
+ --example =
144
+ -- person { name = "Dillon Kearns" }
145
+ -- |> additionalName "Cornelius"
146
+ --organization :
147
+ -- {}
148
+ -- -> StructuredDataHelper { personOrOrganization : () }
149
+ --organization info =
150
+ -- StructuredDataHelper "Organization" []
151
+ --needsPersonOrOrg : StructuredDataHelper {}
152
+ --needsPersonOrOrg =
153
+ -- StructuredDataHelper "" []
154
+
155
+
156
+ {-|
157
+
158
+ ```json
159
+ {
160
+ "@context": "http://schema.org/",
161
+ "@type": "PodcastSeries",
162
+ "image": "https://www.relay.fm/inquisitive_artwork.png",
163
+ "url": "http://www.relay.fm/inquisitive",
164
+ "name": "Inquisitive",
165
+ "description": "Inquisitive is a show for the naturally curious. Each week, Myke Hurley takes a look at what makes creative people successful and what steps they have taken to get there.",
166
+ "webFeed": "http://www.relay.fm//inquisitive/feed",
167
+ "author": {
168
+ "@type": "Person",
169
+ "name": "Myke Hurley"
170
+ }
171
+ }
172
+ }
173
+ ```
174
+
175
+ -}
176
+ series : Encode.Value
177
+ series =
178
+ Encode.object
179
+ [ ( "@context", Encode.string "http://schema.org/" )
180
+ , ( "@type", Encode.string "PodcastSeries" )
181
+ , ( "image", Encode.string "TODO" )
182
+ , ( "url", Encode.string "http://elm-radio.com/episode/getting-started-with-elm-pages" )
183
+ , ( "name", Encode.string "Elm Radio" )
184
+ , ( "description", Encode.string "TODO" )
185
+ , ( "webFeed", Encode.string "https://elm-radio.com/feed.xml" )
186
+ ]
187
+
188
+
189
+ {-|
190
+
191
+ ```json
192
+ {
193
+ "@context": "http://schema.org/",
194
+ "@type": "PodcastEpisode",
195
+ "url": "http://elm-radio.com/episode/getting-started-with-elm-pages",
196
+ "name": "001: Getting Started with elm-pages",
197
+ "datePublished": "2015-02-18",
198
+ "timeRequired": "PT37M",
199
+ "description": "In the first episode of “Behind the App”, a special series of Inquisitive, we take a look at the beginnings of iOS app development, by focusing on the introduction of the iPhone and the App Store.",
200
+ "associatedMedia": {
201
+ "@type": "MediaObject",
202
+ "contentUrl": "https://cdn.simplecast.com/audio/6a206b/6a206baa-9c8e-4c25-9037-2b674204ba84/ca009f6e-1710-4518-b869-ca34cb0b7d17/001-getting-started-elm-pages_tc.mp3 "
203
+ },
204
+ "partOfSeries": {
205
+ "@type": "PodcastSeries",
206
+ "name": "Elm Radio",
207
+ "url": "https://elm-radio.com"
208
+ }
209
+ }
210
+ ```
211
+
212
+ -}
213
+ episode : Encode.Value
214
+ episode =
215
+ Encode.object
216
+ [ ( "@context", Encode.string "http://schema.org/" )
217
+ , ( "@type", Encode.string "PodcastEpisode" )
218
+ , ( "url", Encode.string "http://elm-radio.com/episode/getting-started-with-elm-pages" )
219
+ , ( "name", Encode.string "Getting Started with elm-pages" )
220
+ , ( "datePublished", Encode.string "2015-02-18" )
221
+ , ( "timeRequired", Encode.string "PT37M" )
222
+ , ( "description", Encode.string "TODO" )
223
+ , ( "associatedMedia"
224
+ , Encode.object
225
+ [ ( "@type", Encode.string "MediaObject" )
226
+ , ( "contentUrl", Encode.string "https://cdn.simplecast.com/audio/6a206b/6a206baa-9c8e-4c25-9037-2b674204ba84/ca009f6e-1710-4518-b869-ca34cb0b7d17/001-getting-started-elm-pages_tc.mp3" )
227
+ ]
228
+ )
229
+ , ( "partOfSeries"
230
+ , Encode.object
231
+ [ ( "@type", Encode.string "PodcastSeries" )
232
+ , ( "name", Encode.string "Elm Radio" )
233
+ , ( "url", Encode.string "https://elm-radio.com" )
234
+ ]
235
+ )
236
+ ]