elm-pages 3.0.0-beta.14 → 3.0.0-beta.15
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.js +4 -7
- 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/compatibility-key.js +1 -1
- package/generator/src/error-formatter.js +7 -3
- package/generator/src/render.js +8 -17
- package/generator/src/request-cache.js +34 -4
- package/generator/static-code/hmr.js +16 -2
- package/package.json +1 -1
- package/src/ApiRoute.elm +1 -4
- package/src/BackendTask/Env.elm +10 -8
- package/src/BackendTask/File.elm +10 -10
- package/src/BackendTask/Glob.elm +2 -2
- package/src/BackendTask/Http.elm +49 -13
- package/src/BackendTask/Port.elm +50 -46
- package/src/BackendTask.elm +5 -5
- package/src/Exception.elm +70 -12
- package/src/Form.elm +3 -2
- package/src/Pages/Generate.elm +299 -102
- package/src/Pages/Internal/Platform/Cli.elm +17 -37
- package/src/Pages/Internal/Platform/CompatibilityKey.elm +1 -1
- package/src/Pages/Internal/Platform/GeneratorApplication.elm +17 -41
- package/src/Pages/Internal/Platform/StaticResponses.elm +11 -25
- package/src/Pages/Script.elm +2 -2
- package/src/Pages/StaticHttpRequest.elm +1 -23
- package/src/Server/Request.elm +3 -2
- package/src/MultiDict.elm +0 -49
- package/src/PairingHeap.elm +0 -137
- package/src/Parser/Extra/String.elm +0 -33
- package/src/Parser/Extra.elm +0 -69
- package/src/ProgramTest/ComplexQuery.elm +0 -360
- package/src/ProgramTest/EffectSimulation.elm +0 -122
- package/src/ProgramTest/Failure.elm +0 -367
- package/src/ProgramTest/HtmlHighlighter.elm +0 -116
- package/src/ProgramTest/HtmlParserHacks.elm +0 -58
- package/src/ProgramTest/HtmlRenderer.elm +0 -73
- package/src/ProgramTest/Program.elm +0 -30
- package/src/ProgramTest/StringLines.elm +0 -26
- package/src/ProgramTest/TestHtmlHacks.elm +0 -132
- package/src/ProgramTest/TestHtmlParser.elm +0 -201
- package/src/ProgramTest.elm +0 -2339
- package/src/Query/Extra.elm +0 -55
- package/src/SimulatedEffect/Cmd.elm +0 -69
- package/src/SimulatedEffect/Http.elm +0 -330
- package/src/SimulatedEffect/Navigation.elm +0 -69
- package/src/SimulatedEffect/Ports.elm +0 -62
- package/src/SimulatedEffect/Process.elm +0 -24
- package/src/SimulatedEffect/Sub.elm +0 -48
- package/src/SimulatedEffect/Task.elm +0 -252
- package/src/SimulatedEffect/Time.elm +0 -25
- package/src/SimulatedEffect.elm +0 -42
- package/src/String/Extra.elm +0 -6
- package/src/Test/Http.elm +0 -145
- package/src/TestResult.elm +0 -35
- package/src/TestState.elm +0 -305
- package/src/Url/Extra.elm +0 -100
- package/src/Vendored/Diff.elm +0 -321
- package/src/Vendored/Failure.elm +0 -217
- package/src/Vendored/FormatMonochrome.elm +0 -44
- package/src/Vendored/Highlightable.elm +0 -53
package/src/BackendTask/Port.elm
CHANGED
|
@@ -3,47 +3,36 @@ module BackendTask.Port exposing
|
|
|
3
3
|
, Error(..)
|
|
4
4
|
)
|
|
5
5
|
|
|
6
|
-
{-|
|
|
7
|
-
|
|
8
|
-
@docs get
|
|
9
|
-
|
|
10
|
-
@docs Error
|
|
11
|
-
|
|
12
|
-
-}
|
|
13
|
-
|
|
14
|
-
import BackendTask
|
|
15
|
-
import BackendTask.Http
|
|
16
|
-
import BackendTask.Internal.Request
|
|
17
|
-
import Exception exposing (Catchable)
|
|
18
|
-
import Json.Decode as Decode exposing (Decoder)
|
|
19
|
-
import Json.Encode as Encode
|
|
20
|
-
import TerminalText
|
|
6
|
+
{-| In a vanilla Elm application, ports let you either send or receive JSON data between your Elm application and the JavaScript context in the user's browser at runtime.
|
|
21
7
|
|
|
8
|
+
With `BackendTask.Port`, you send and receive JSON to JavaScript running in NodeJS. As with any `BackendTask`, Port BackendTask's are either run at build-time (for pre-rendered routes) or at request-time (for server-rendered routes). See [`BackendTask`](BackendTask) for more about the
|
|
9
|
+
lifecycle of `BackendTask`'s.
|
|
22
10
|
|
|
23
|
-
|
|
11
|
+
This means that you can call shell scripts, run NPM packages that are installed, or anything else you could do with NodeJS to perform custom side-effects, get some data, or both.
|
|
24
12
|
|
|
25
|
-
|
|
13
|
+
A `BackendTask.Port` will call an async JavaScript function with the given name from the definition in a file called `port-data-source.js` in your project's root directory. The function receives the input JSON value, and the Decoder is used to decode the return value of the async function.
|
|
26
14
|
|
|
27
|
-
|
|
15
|
+
@docs get
|
|
28
16
|
|
|
29
|
-
Here is the Elm code and corresponding JavaScript definition for getting an environment variable (or
|
|
17
|
+
Here is the Elm code and corresponding JavaScript definition for getting an environment variable (or an `Exception BackendTask.Port.Error` if it isn't found). In this example,
|
|
18
|
+
we're using `BackendTask.throw` to let the framework treat that as an unexpected exception, but we could also handle the possible failures of the `Exception` (see [`Exception`](Exception)).
|
|
30
19
|
|
|
31
20
|
import BackendTask exposing (BackendTask)
|
|
32
21
|
import BackendTask.Port
|
|
33
22
|
import Json.Encode
|
|
34
23
|
import OptimizedDecoder as Decode
|
|
35
24
|
|
|
36
|
-
data : BackendTask String
|
|
25
|
+
data : BackendTask Throwable String
|
|
37
26
|
data =
|
|
38
27
|
BackendTask.Port.get "environmentVariable"
|
|
39
28
|
(Json.Encode.string "EDITOR")
|
|
40
29
|
Decode.string
|
|
30
|
+
|> BackendTask.throw
|
|
41
31
|
|
|
42
32
|
-- will resolve to "VIM" if you run `EDITOR=vim elm-pages dev`
|
|
43
33
|
|
|
44
34
|
```javascript
|
|
45
|
-
|
|
46
|
-
|
|
35
|
+
// port-data-source.js
|
|
47
36
|
|
|
48
37
|
module.exports =
|
|
49
38
|
/**
|
|
@@ -56,32 +45,47 @@ module.exports =
|
|
|
56
45
|
if (result) {
|
|
57
46
|
return result;
|
|
58
47
|
} else {
|
|
59
|
-
throw `No environment variable called ${
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
48
|
+
throw `No environment variable called ${name}
|
|
49
|
+
|
|
50
|
+
Available:
|
|
51
|
+
|
|
52
|
+
${Object.keys(process.env).join("\n")}
|
|
53
|
+
`;
|
|
64
54
|
}
|
|
65
55
|
},
|
|
66
56
|
}
|
|
67
57
|
```
|
|
68
58
|
|
|
69
59
|
|
|
70
|
-
##
|
|
60
|
+
## Performance
|
|
71
61
|
|
|
72
|
-
`
|
|
62
|
+
As with any JavaScript or NodeJS code, avoid doing blocking IO operations. For example, avoid using `fs.readFileSync`, because blocking IO can slow down your elm-pages builds and dev server. `elm-pages` performances all `BackendTask`'s in parallel whenever possible.
|
|
63
|
+
So if you do `BackendTask.map2 Tuple.pair myHttpBackendTask myPortBackendTask`, it will resolve those two in parallel. NodeJS performs best when you take advantage of its ability to do non-blocking I/O (file reads, HTTP requests, etc.). If you use `BackendTask.andThen`,
|
|
64
|
+
it will need to resolve them in sequence rather than in parallel, but it's still best to avoid blocking IO operations in your BackendTask Port definitions.
|
|
73
65
|
|
|
74
|
-
Any time you throw an exception from a BackendTask.Port definition, it will result in a build error in your `elm-pages build` or dev server. In the example above, if the environment variable
|
|
75
|
-
is not found it will result in a build failure. Notice that the NPM package `kleur` is being used in this example to add color to the output for that build error. You can use any tool you
|
|
76
|
-
prefer to add ANSI color codes within the error string in an exception and it will show up with color output in the build output and dev server.
|
|
77
66
|
|
|
67
|
+
## Error Handling
|
|
78
68
|
|
|
79
|
-
|
|
69
|
+
There are a few different things that can go wrong when running a port-data-source. These possible errors are captured in the `BackendTask.Port.Error` type.
|
|
80
70
|
|
|
81
|
-
|
|
71
|
+
@docs Error
|
|
72
|
+
|
|
73
|
+
Any time you throw a JavaScript exception from a BackendTask.Port definition, it will give you a `PortCallException`. It's usually easier to add a `try`/`catch` in your JavaScript code in `port-data-source.js`
|
|
74
|
+
to handle possible errors, but you can throw a JSON value and handle it in Elm in the `PortCallException` call error.
|
|
82
75
|
|
|
83
76
|
-}
|
|
84
|
-
|
|
77
|
+
|
|
78
|
+
import BackendTask
|
|
79
|
+
import BackendTask.Http
|
|
80
|
+
import BackendTask.Internal.Request
|
|
81
|
+
import Exception exposing (Exception)
|
|
82
|
+
import Json.Decode as Decode exposing (Decoder)
|
|
83
|
+
import Json.Encode as Encode
|
|
84
|
+
import TerminalText
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
{-| -}
|
|
88
|
+
get : String -> Encode.Value -> Decoder b -> BackendTask.BackendTask (Exception Error) b
|
|
85
89
|
get portName input decoder =
|
|
86
90
|
BackendTask.Internal.Request.request
|
|
87
91
|
{ name = "port"
|
|
@@ -97,7 +101,7 @@ get portName input decoder =
|
|
|
97
101
|
|> Decode.andThen
|
|
98
102
|
(\errorKind ->
|
|
99
103
|
if errorKind == "PortNotDefined" then
|
|
100
|
-
Exception.
|
|
104
|
+
Exception.Exception (PortNotDefined { name = portName })
|
|
101
105
|
{ title = "Port Error"
|
|
102
106
|
, body =
|
|
103
107
|
[ TerminalText.text "Something went wrong in a call to BackendTask.Port.get. I expected to find a port named `"
|
|
@@ -114,7 +118,7 @@ get portName input decoder =
|
|
|
114
118
|
|> Decode.map (Maybe.withDefault "")
|
|
115
119
|
|> Decode.map
|
|
116
120
|
(\incorrectPortType ->
|
|
117
|
-
Exception.
|
|
121
|
+
Exception.Exception ExportIsNotFunction
|
|
118
122
|
{ title = "Port Error"
|
|
119
123
|
, body =
|
|
120
124
|
[ TerminalText.text "Something went wrong in a call to BackendTask.Port.get. I found an export called `"
|
|
@@ -127,7 +131,7 @@ get portName input decoder =
|
|
|
127
131
|
)
|
|
128
132
|
|
|
129
133
|
else if errorKind == "MissingPortsFile" then
|
|
130
|
-
Exception.
|
|
134
|
+
Exception.Exception MissingPortsFile
|
|
131
135
|
{ title = "Port Error"
|
|
132
136
|
, body =
|
|
133
137
|
[ TerminalText.text "Something went wrong in a call to BackendTask.Port.get. I couldn't find your port-data-source file. Be sure to create a 'port-data-source.ts' or 'port-data-source.js' file."
|
|
@@ -142,7 +146,7 @@ get portName input decoder =
|
|
|
142
146
|
|> Decode.map (Maybe.withDefault "")
|
|
143
147
|
|> Decode.map
|
|
144
148
|
(\errorMessage ->
|
|
145
|
-
Exception.
|
|
149
|
+
Exception.Exception
|
|
146
150
|
ErrorInPortsFile
|
|
147
151
|
{ title = "Port Error"
|
|
148
152
|
, body =
|
|
@@ -154,26 +158,26 @@ get portName input decoder =
|
|
|
154
158
|
}
|
|
155
159
|
)
|
|
156
160
|
|
|
157
|
-
else if errorKind == "
|
|
161
|
+
else if errorKind == "PortCallException" then
|
|
158
162
|
Decode.field "error" Decode.value
|
|
159
163
|
|> Decode.maybe
|
|
160
164
|
|> Decode.map (Maybe.withDefault Encode.null)
|
|
161
165
|
|> Decode.map
|
|
162
166
|
(\portCallError ->
|
|
163
|
-
Exception.
|
|
164
|
-
(
|
|
167
|
+
Exception.Exception
|
|
168
|
+
(PortCallException portCallError)
|
|
165
169
|
{ title = "Port Error"
|
|
166
170
|
, body =
|
|
167
|
-
[ TerminalText.text "Something went wrong in a call to BackendTask.Port.get. I
|
|
171
|
+
[ TerminalText.text "Something went wrong in a call to BackendTask.Port.get. I was able to import the port definitions file, but when running it I encountered this exception:\n\n"
|
|
168
172
|
, TerminalText.red (Encode.encode 2 portCallError)
|
|
169
|
-
, TerminalText.text "\n\
|
|
173
|
+
, TerminalText.text "\n\nYou could add a `try`/`catch` in your `port-data-source` JavaScript code to handle that error."
|
|
170
174
|
]
|
|
171
175
|
|> TerminalText.toString
|
|
172
176
|
}
|
|
173
177
|
)
|
|
174
178
|
|
|
175
179
|
else
|
|
176
|
-
Exception.
|
|
180
|
+
Exception.Exception ErrorInPortsFile
|
|
177
181
|
{ title = "Port Error"
|
|
178
182
|
, body =
|
|
179
183
|
[ TerminalText.text "Something went wrong in a call to BackendTask.Port.get. I expected to find a port named `"
|
|
@@ -198,5 +202,5 @@ type Error
|
|
|
198
202
|
| ErrorInPortsFile
|
|
199
203
|
| MissingPortsFile
|
|
200
204
|
| PortNotDefined { name : String }
|
|
201
|
-
|
|
|
205
|
+
| PortCallException Decode.Value
|
|
202
206
|
| ExportIsNotFunction
|
package/src/BackendTask.elm
CHANGED
|
@@ -86,7 +86,7 @@ Any place in your `elm-pages` app where the framework lets you pass in a value o
|
|
|
86
86
|
|
|
87
87
|
-}
|
|
88
88
|
|
|
89
|
-
import Exception exposing (
|
|
89
|
+
import Exception exposing (Exception(..), Throwable)
|
|
90
90
|
import Json.Encode
|
|
91
91
|
import Pages.StaticHttpRequest exposing (RawRequest(..))
|
|
92
92
|
|
|
@@ -524,26 +524,26 @@ map9 combineFn request1 request2 request3 request4 request5 request6 request7 re
|
|
|
524
524
|
|
|
525
525
|
|
|
526
526
|
{-| -}
|
|
527
|
-
catch : BackendTask (
|
|
527
|
+
catch : BackendTask (Exception error) value -> BackendTask error value
|
|
528
528
|
catch ds =
|
|
529
529
|
ds
|
|
530
530
|
|> onError
|
|
531
531
|
(\exception ->
|
|
532
532
|
case exception of
|
|
533
|
-
|
|
533
|
+
Exception error _ ->
|
|
534
534
|
fail error
|
|
535
535
|
)
|
|
536
536
|
|
|
537
537
|
|
|
538
538
|
{-| -}
|
|
539
|
-
throw : BackendTask (
|
|
539
|
+
throw : BackendTask (Exception error) data -> BackendTask Throwable data
|
|
540
540
|
throw backendTask =
|
|
541
541
|
backendTask
|
|
542
542
|
|> onError (Exception.throw >> fail)
|
|
543
543
|
|
|
544
544
|
|
|
545
545
|
{-| -}
|
|
546
|
-
toResult : BackendTask (
|
|
546
|
+
toResult : BackendTask (Exception error) data -> BackendTask noError (Result error data)
|
|
547
547
|
toResult backendTask =
|
|
548
548
|
backendTask
|
|
549
549
|
|> catch
|
package/src/Exception.elm
CHANGED
|
@@ -1,37 +1,95 @@
|
|
|
1
|
-
module Exception exposing (Throwable,
|
|
1
|
+
module Exception exposing (Throwable, Exception(..), fromString, fromStringWithValue, throw, unwrap)
|
|
2
2
|
|
|
3
|
-
{-|
|
|
3
|
+
{-| The Elm language doesn't have the concept of exceptions or special control flow for errors. It just has
|
|
4
|
+
Custom Types, and by convention types like `Result` and the `Err` variant are used to represent possible failure states
|
|
5
|
+
and combine together different error states.
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
`elm-pages` doesn't change that, Elm still doesn't have special exception control flow at the language level. It does have
|
|
8
|
+
a type, which is just a regular old Elm type, called `Exception`. Why? Because this plain old Elm type does have one
|
|
9
|
+
special characteristic - the `elm-pages` framework knows how to turn it into an error message. This becomes interesting
|
|
10
|
+
because an `elm-pages` app has several places that accept a value of type `BackendTask Exception.Throwable value`.
|
|
11
|
+
This design lets the `elm-pages` framework do some of the work for you.
|
|
12
|
+
|
|
13
|
+
For example, if you wanted to handle possible errors to present them to the user
|
|
14
|
+
|
|
15
|
+
type alias Data =
|
|
16
|
+
String
|
|
17
|
+
|
|
18
|
+
data : RouteParams -> BackendTask Throwable Data
|
|
19
|
+
data routeParams =
|
|
20
|
+
BackendTask.Http.getJson "https://api.github.com/repos/dillonkearns/elm-pages"
|
|
21
|
+
(Decode.field "description" Decode.string)
|
|
22
|
+
|> BackendTask.onError
|
|
23
|
+
(\error ->
|
|
24
|
+
case Exception.unwrap error of
|
|
25
|
+
BackendTask.Http.BadStatus metadata string ->
|
|
26
|
+
if metadata.statusCode == 401 || metadata.statusCode == 403 || metadata.statusCode == 404 then
|
|
27
|
+
BackendTask.succeed "Either this repo doesn't exist or you don't have access to it."
|
|
28
|
+
|
|
29
|
+
else
|
|
30
|
+
-- we're only handling these expected error cases. In the case of an HTTP timeout,
|
|
31
|
+
-- we'll let the error propagate as a Throwable
|
|
32
|
+
BackendTask.fail error |> BackendTask.throw
|
|
33
|
+
|
|
34
|
+
_ ->
|
|
35
|
+
BackendTask.fail error |> BackendTask.throw
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
This can be a lot of work for all possible errors, though. If you don't expect this kind of error (it's an _exceptional_ case),
|
|
39
|
+
you can let the framework handle it if the error ever does unexpectedly occur.
|
|
40
|
+
|
|
41
|
+
data : RouteParams -> BackendTask Throwable Data
|
|
42
|
+
data routeParams =
|
|
43
|
+
BackendTask.Http.getJson "https://api.github.com/repos/dillonkearns/elm-pages"
|
|
44
|
+
(Decode.field "description" Decode.string)
|
|
45
|
+
|> BackendTask.throw
|
|
46
|
+
|
|
47
|
+
This is especially useful for pages generated at build-time (`RouteBuilder.preRender`) where you want the build
|
|
48
|
+
to fail if anything unexpected happens. With pre-rendered routes, you know that these error cases won't
|
|
49
|
+
be seen by users, so it's often a great idea to just let the framework handle these unexpected errors so a developer can
|
|
50
|
+
debug them and see what went wrong. In the example above, maybe we are only pre-rendering pages for a set of known
|
|
51
|
+
GitHub Repositories, so a Not Found or Unauthorized HTTP error would be unexpected and should stop the build so we can fix the
|
|
52
|
+
issue.
|
|
53
|
+
|
|
54
|
+
In the case of server-rendered Routes (`RouteBuilder.serverRender`), `elm-pages` will show your 500 error page
|
|
55
|
+
when these errors occur.
|
|
56
|
+
|
|
57
|
+
@docs Throwable, Exception, fromString, fromStringWithValue, throw, unwrap
|
|
6
58
|
|
|
7
59
|
-}
|
|
8
60
|
|
|
9
61
|
|
|
10
62
|
{-| -}
|
|
11
63
|
type alias Throwable =
|
|
12
|
-
|
|
64
|
+
Exception ()
|
|
13
65
|
|
|
14
66
|
|
|
15
67
|
{-| -}
|
|
16
|
-
type
|
|
17
|
-
=
|
|
68
|
+
type Exception error
|
|
69
|
+
= Exception error { title : String, body : String }
|
|
18
70
|
|
|
19
71
|
|
|
20
72
|
{-| -}
|
|
21
|
-
fromString : String ->
|
|
73
|
+
fromString : String -> Exception ()
|
|
22
74
|
fromString string =
|
|
23
75
|
fromStringWithValue string ()
|
|
24
76
|
|
|
25
77
|
|
|
26
78
|
{-| -}
|
|
27
|
-
fromStringWithValue : String -> value ->
|
|
79
|
+
fromStringWithValue : String -> value -> Exception value
|
|
28
80
|
fromStringWithValue string value =
|
|
29
|
-
|
|
81
|
+
Exception value { title = "Custom Error", body = string }
|
|
30
82
|
|
|
31
83
|
|
|
32
84
|
{-| -}
|
|
33
|
-
throw :
|
|
85
|
+
throw : Exception error -> Exception ()
|
|
34
86
|
throw exception =
|
|
35
87
|
case exception of
|
|
36
|
-
|
|
37
|
-
|
|
88
|
+
Exception _ string ->
|
|
89
|
+
Exception () string
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
{-| -}
|
|
93
|
+
unwrap : Exception error -> error
|
|
94
|
+
unwrap (Exception error _) =
|
|
95
|
+
error
|
package/src/Form.elm
CHANGED
|
@@ -269,6 +269,7 @@ Totally customizable. Uses [`Form.FieldView`](Form-FieldView) to render all of t
|
|
|
269
269
|
|
|
270
270
|
import BackendTask exposing (BackendTask)
|
|
271
271
|
import Dict exposing (Dict)
|
|
272
|
+
import Exception exposing (Throwable)
|
|
272
273
|
import Form.Field as Field exposing (Field(..))
|
|
273
274
|
import Form.FieldStatus as FieldStatus exposing (FieldStatus)
|
|
274
275
|
import Form.FieldView
|
|
@@ -681,7 +682,7 @@ toServerForm :
|
|
|
681
682
|
->
|
|
682
683
|
Form
|
|
683
684
|
error
|
|
684
|
-
{ combine : Validation.Validation error (BackendTask
|
|
685
|
+
{ combine : Validation.Validation error (BackendTask Throwable (Validation.Validation error combined kind constraints)) kind constraints
|
|
685
686
|
, view : viewFn
|
|
686
687
|
}
|
|
687
688
|
data
|
|
@@ -694,7 +695,7 @@ toServerForm (Form a b c) =
|
|
|
694
695
|
{ result : Dict String (List error)
|
|
695
696
|
, isMatchCandidate : Bool
|
|
696
697
|
, combineAndView :
|
|
697
|
-
{ combine : Validation.Validation error (BackendTask
|
|
698
|
+
{ combine : Validation.Validation error (BackendTask Throwable (Validation.Validation error combined kind constraints)) kind constraints
|
|
698
699
|
, view : viewFn
|
|
699
700
|
}
|
|
700
701
|
}
|