From ce38409f2c1b63bcf75fbc12eef0af97f58fadd6 Mon Sep 17 00:00:00 2001 From: tanner0101 Date: Mon, 12 Mar 2018 23:05:53 -0400 Subject: [PATCH] remove all broken links --- 3.0/docs/crypto/getting-started.md | 2 - 3.0/docs/deploy/getting-started.md | 3 + 3.0/docs/fluent/migrations.md | 2 +- 3.0/docs/fluent/models.md | 3 +- 3.0/docs/fluent/querying.md | 4 +- 3.0/docs/getting-started/application.md | 2 +- 3.0/docs/getting-started/routing.md | 4 +- 3.0/docs/getting-started/services.md | 31 +- 3.0/docs/getting-started/structure.md | 2 +- 3.0/docs/leaf/basics.md | 2 +- 3.0/docs/mysql/fluent.md | 2 +- 3.0/docs/postgresql/fluent.md | 2 +- 3.0/docs/redis/basics.md | 6 +- 3.0/docs/redis/custom-commands.md | 2 - 3.0/docs/redis/pub-sub.md | 2 +- 3.0/docs/routing/getting-started.md | 8 +- 3.0/docs/routing/parameters.md | 2 +- 3.0/docs/routing/router.md | 4 +- 3.0/docs/sqlite/fluent.md | 2 +- 3.0/docs/testing/getting-started.md | 3 + 3.0/mkdocs.yml | 4 + 3.0/site/404.html | 1343 ++++++++++++ 3.0/site/assets/images/favicon.png | Bin 0 -> 521 bytes .../images/icons/bitbucket.4ebea66e.svg | 20 + .../assets/images/icons/github.a4034fb1.svg | 18 + .../assets/images/icons/gitlab.348cdb3a.svg | 38 + .../javascripts/application.8eb9be28.js | 1 + 3.0/site/assets/javascripts/lunr/lunr.da.js | 1 + 3.0/site/assets/javascripts/lunr/lunr.de.js | 1 + 3.0/site/assets/javascripts/lunr/lunr.du.js | 1 + 3.0/site/assets/javascripts/lunr/lunr.es.js | 1 + 3.0/site/assets/javascripts/lunr/lunr.fi.js | 1 + 3.0/site/assets/javascripts/lunr/lunr.fr.js | 1 + 3.0/site/assets/javascripts/lunr/lunr.hu.js | 1 + 3.0/site/assets/javascripts/lunr/lunr.it.js | 1 + 3.0/site/assets/javascripts/lunr/lunr.jp.js | 1 + .../assets/javascripts/lunr/lunr.multi.js | 1 + 3.0/site/assets/javascripts/lunr/lunr.no.js | 1 + 3.0/site/assets/javascripts/lunr/lunr.pt.js | 1 + 3.0/site/assets/javascripts/lunr/lunr.ro.js | 1 + 3.0/site/assets/javascripts/lunr/lunr.ru.js | 1 + .../javascripts/lunr/lunr.stemmer.support.js | 1 + 3.0/site/assets/javascripts/lunr/lunr.sv.js | 1 + 3.0/site/assets/javascripts/lunr/lunr.tr.js | 1 + 3.0/site/assets/javascripts/lunr/tinyseg.js | 1 + .../assets/javascripts/modernizr.1aa3b519.js | 1 + .../application-palette.6079476c.css | 2 + .../stylesheets/application.78aab2dc.css | 2 + 3.0/site/crypto/base64/index.html | 1416 +++++++++++++ 3.0/site/crypto/getting-started/index.html | 1500 +++++++++++++ 3.0/site/crypto/hash/index.html | 1604 ++++++++++++++ 3.0/site/crypto/mac/index.html | 1467 +++++++++++++ 3.0/site/crypto/passwords/index.html | 1607 ++++++++++++++ 3.0/site/crypto/random/index.html | 1535 ++++++++++++++ 3.0/site/deploy/getting-started/index.html | 1416 +++++++++++++ 3.0/site/fluent/database/index.html | 1479 +++++++++++++ 3.0/site/fluent/getting-started/index.html | 1499 +++++++++++++ 3.0/site/fluent/migrations/index.html | 1612 ++++++++++++++ 3.0/site/fluent/models/index.html | 1648 +++++++++++++++ 3.0/site/fluent/pivot/index.html | 1416 +++++++++++++ 3.0/site/fluent/query-builder/index.html | 1663 +++++++++++++++ 3.0/site/fluent/querying/index.html | 1811 ++++++++++++++++ 3.0/site/fluent/relations/index.html | 1479 +++++++++++++ 3.0/site/fluent/schema-builder/index.html | 1511 +++++++++++++ 3.0/site/fluent/transaction/index.html | 1416 +++++++++++++ .../getting-started/application/index.html | 1504 +++++++++++++ 3.0/site/getting-started/cloud/index.html | 1491 +++++++++++++ 3.0/site/getting-started/content/index.html | 1682 +++++++++++++++ .../getting-started/controllers/index.html | 1518 +++++++++++++ 3.0/site/getting-started/futures/index.html | 1677 +++++++++++++++ .../getting-started/hello-world/index.html | 1546 ++++++++++++++ 3.0/site/getting-started/routing/index.html | 1576 ++++++++++++++ 3.0/site/getting-started/services/index.html | 1865 ++++++++++++++++ 3.0/site/getting-started/spm/index.html | 1610 ++++++++++++++ 3.0/site/getting-started/structure/index.html | 1720 +++++++++++++++ 3.0/site/getting-started/toolbox/index.html | 1631 ++++++++++++++ 3.0/site/getting-started/xcode/index.html | 1514 +++++++++++++ 3.0/site/images/droplet-color.svg | 30 + 3.0/site/images/droplet-white.svg | 18 + 3.0/site/index.html | 1551 ++++++++++++++ 3.0/site/install/macos/index.html | 1586 ++++++++++++++ 3.0/site/install/ubuntu/index.html | 1717 +++++++++++++++ 3.0/site/leaf/basics/index.html | 1876 +++++++++++++++++ 3.0/site/leaf/custom-tags/index.html | 1465 +++++++++++++ 3.0/site/leaf/getting-started/index.html | 1623 ++++++++++++++ 3.0/site/mysql/core/index.html | 1707 +++++++++++++++ 3.0/site/mysql/fluent/index.html | 1720 +++++++++++++++ 3.0/site/mysql/getting-started/index.html | 1422 +++++++++++++ 3.0/site/postgresql/core/index.html | 1707 +++++++++++++++ 3.0/site/postgresql/fluent/index.html | 1720 +++++++++++++++ .../postgresql/getting-started/index.html | 1422 +++++++++++++ 3.0/site/redis/basics/index.html | 1608 ++++++++++++++ 3.0/site/redis/custom-commands/index.html | 1470 +++++++++++++ 3.0/site/redis/getting-started/index.html | 1498 +++++++++++++ 3.0/site/redis/pipeline/index.html | 1487 +++++++++++++ 3.0/site/redis/pub-sub/index.html | 1529 ++++++++++++++ 3.0/site/routing/getting-started/index.html | 1523 +++++++++++++ 3.0/site/routing/parameters/index.html | 1506 +++++++++++++ 3.0/site/routing/route/index.html | 1465 +++++++++++++ 3.0/site/routing/router/index.html | 1505 +++++++++++++ 3.0/site/search/search_index.json | 1799 ++++++++++++++++ 3.0/site/sitemap.xml | 416 ++++ 3.0/site/sqlite/core/index.html | 1707 +++++++++++++++ 3.0/site/sqlite/fluent/index.html | 1720 +++++++++++++++ 3.0/site/sqlite/getting-started/index.html | 1422 +++++++++++++ 3.0/site/stylesheets/extra.css | 48 + 3.0/site/testing/getting-started/index.html | 1416 +++++++++++++ 3.0/site/version/1_5/index.html | 1416 +++++++++++++ 3.0/site/version/2_0/index.html | 1416 +++++++++++++ 3.0/site/version/3_0/index.html | 1416 +++++++++++++ 3.0/site/version/support/index.html | 1538 ++++++++++++++ 3.0/site/websocket/websocket/index.html | 1416 +++++++++++++ 112 files changed, 95788 insertions(+), 45 deletions(-) create mode 100644 3.0/docs/deploy/getting-started.md create mode 100644 3.0/docs/testing/getting-started.md create mode 100644 3.0/site/404.html create mode 100644 3.0/site/assets/images/favicon.png create mode 100644 3.0/site/assets/images/icons/bitbucket.4ebea66e.svg create mode 100644 3.0/site/assets/images/icons/github.a4034fb1.svg create mode 100644 3.0/site/assets/images/icons/gitlab.348cdb3a.svg create mode 100644 3.0/site/assets/javascripts/application.8eb9be28.js create mode 100644 3.0/site/assets/javascripts/lunr/lunr.da.js create mode 100644 3.0/site/assets/javascripts/lunr/lunr.de.js create mode 100644 3.0/site/assets/javascripts/lunr/lunr.du.js create mode 100644 3.0/site/assets/javascripts/lunr/lunr.es.js create mode 100644 3.0/site/assets/javascripts/lunr/lunr.fi.js create mode 100644 3.0/site/assets/javascripts/lunr/lunr.fr.js create mode 100644 3.0/site/assets/javascripts/lunr/lunr.hu.js create mode 100644 3.0/site/assets/javascripts/lunr/lunr.it.js create mode 100644 3.0/site/assets/javascripts/lunr/lunr.jp.js create mode 100644 3.0/site/assets/javascripts/lunr/lunr.multi.js create mode 100644 3.0/site/assets/javascripts/lunr/lunr.no.js create mode 100644 3.0/site/assets/javascripts/lunr/lunr.pt.js create mode 100644 3.0/site/assets/javascripts/lunr/lunr.ro.js create mode 100644 3.0/site/assets/javascripts/lunr/lunr.ru.js create mode 100644 3.0/site/assets/javascripts/lunr/lunr.stemmer.support.js create mode 100644 3.0/site/assets/javascripts/lunr/lunr.sv.js create mode 100644 3.0/site/assets/javascripts/lunr/lunr.tr.js create mode 100644 3.0/site/assets/javascripts/lunr/tinyseg.js create mode 100644 3.0/site/assets/javascripts/modernizr.1aa3b519.js create mode 100644 3.0/site/assets/stylesheets/application-palette.6079476c.css create mode 100644 3.0/site/assets/stylesheets/application.78aab2dc.css create mode 100644 3.0/site/crypto/base64/index.html create mode 100644 3.0/site/crypto/getting-started/index.html create mode 100644 3.0/site/crypto/hash/index.html create mode 100644 3.0/site/crypto/mac/index.html create mode 100644 3.0/site/crypto/passwords/index.html create mode 100644 3.0/site/crypto/random/index.html create mode 100644 3.0/site/deploy/getting-started/index.html create mode 100644 3.0/site/fluent/database/index.html create mode 100644 3.0/site/fluent/getting-started/index.html create mode 100644 3.0/site/fluent/migrations/index.html create mode 100644 3.0/site/fluent/models/index.html create mode 100644 3.0/site/fluent/pivot/index.html create mode 100644 3.0/site/fluent/query-builder/index.html create mode 100644 3.0/site/fluent/querying/index.html create mode 100644 3.0/site/fluent/relations/index.html create mode 100644 3.0/site/fluent/schema-builder/index.html create mode 100644 3.0/site/fluent/transaction/index.html create mode 100644 3.0/site/getting-started/application/index.html create mode 100644 3.0/site/getting-started/cloud/index.html create mode 100644 3.0/site/getting-started/content/index.html create mode 100644 3.0/site/getting-started/controllers/index.html create mode 100644 3.0/site/getting-started/futures/index.html create mode 100644 3.0/site/getting-started/hello-world/index.html create mode 100644 3.0/site/getting-started/routing/index.html create mode 100644 3.0/site/getting-started/services/index.html create mode 100644 3.0/site/getting-started/spm/index.html create mode 100644 3.0/site/getting-started/structure/index.html create mode 100644 3.0/site/getting-started/toolbox/index.html create mode 100644 3.0/site/getting-started/xcode/index.html create mode 100644 3.0/site/images/droplet-color.svg create mode 100644 3.0/site/images/droplet-white.svg create mode 100644 3.0/site/index.html create mode 100644 3.0/site/install/macos/index.html create mode 100644 3.0/site/install/ubuntu/index.html create mode 100644 3.0/site/leaf/basics/index.html create mode 100644 3.0/site/leaf/custom-tags/index.html create mode 100644 3.0/site/leaf/getting-started/index.html create mode 100644 3.0/site/mysql/core/index.html create mode 100644 3.0/site/mysql/fluent/index.html create mode 100644 3.0/site/mysql/getting-started/index.html create mode 100644 3.0/site/postgresql/core/index.html create mode 100644 3.0/site/postgresql/fluent/index.html create mode 100644 3.0/site/postgresql/getting-started/index.html create mode 100644 3.0/site/redis/basics/index.html create mode 100644 3.0/site/redis/custom-commands/index.html create mode 100644 3.0/site/redis/getting-started/index.html create mode 100644 3.0/site/redis/pipeline/index.html create mode 100644 3.0/site/redis/pub-sub/index.html create mode 100644 3.0/site/routing/getting-started/index.html create mode 100644 3.0/site/routing/parameters/index.html create mode 100644 3.0/site/routing/route/index.html create mode 100644 3.0/site/routing/router/index.html create mode 100644 3.0/site/search/search_index.json create mode 100644 3.0/site/sitemap.xml create mode 100644 3.0/site/sqlite/core/index.html create mode 100644 3.0/site/sqlite/fluent/index.html create mode 100644 3.0/site/sqlite/getting-started/index.html create mode 100644 3.0/site/stylesheets/extra.css create mode 100644 3.0/site/testing/getting-started/index.html create mode 100644 3.0/site/version/1_5/index.html create mode 100644 3.0/site/version/2_0/index.html create mode 100644 3.0/site/version/3_0/index.html create mode 100644 3.0/site/version/support/index.html create mode 100644 3.0/site/websocket/websocket/index.html diff --git a/3.0/docs/crypto/getting-started.md b/3.0/docs/crypto/getting-started.md index 6e151015..399e4bea 100644 --- a/3.0/docs/crypto/getting-started.md +++ b/3.0/docs/crypto/getting-started.md @@ -2,8 +2,6 @@ Crypto is a library containing all common APIs related to cryptography and security. -This project does **not** support TLS. For that, please see [the TLS package](../tls/getting-started.md). - ## With Vapor This package is included with Vapor by default, just add: diff --git a/3.0/docs/deploy/getting-started.md b/3.0/docs/deploy/getting-started.md new file mode 100644 index 00000000..a2e79bd7 --- /dev/null +++ b/3.0/docs/deploy/getting-started.md @@ -0,0 +1,3 @@ +# Getting Started with Deployments + +Coming soon. \ No newline at end of file diff --git a/3.0/docs/fluent/migrations.md b/3.0/docs/fluent/migrations.md index cfeaf30e..7b7ab2c0 100644 --- a/3.0/docs/fluent/migrations.md +++ b/3.0/docs/fluent/migrations.md @@ -67,7 +67,7 @@ Learn more about creating, updating, and deleting schemas in [Fluent → Sche ### Revert Revert is the opposite of prepare. It's job is to undo anything that was done in prepare. It is used when you boot your -app with the `--revert` option. See [Fluent → Migration](../migration.md#revert) for more information. +app with the `--revert` option. To implement `revert` for our model, we simply use `.delete` to indicate that we would like to delete the schema created for `User`. diff --git a/3.0/docs/fluent/models.md b/3.0/docs/fluent/models.md index 1554fb9d..a2064100 100644 --- a/3.0/docs/fluent/models.md +++ b/3.0/docs/fluent/models.md @@ -84,8 +84,7 @@ extension User: Model { } ``` -You can use any type that conforms to `IDType` as a Fluent ID. See [Fluent → Model → ID](../model.md#id) for more information. -You can also use any property name you'd like for the id. +You can use any type that conforms to `IDType` as a Fluent ID. You can also use any property name you'd like for the id. !!! warning Some databases require certain ID keys. For example, MongoDB requires `_id`. diff --git a/3.0/docs/fluent/querying.md b/3.0/docs/fluent/querying.md index 6b45d666..9eb971bc 100644 --- a/3.0/docs/fluent/querying.md +++ b/3.0/docs/fluent/querying.md @@ -8,7 +8,7 @@ querying your database to create, read, update, and delete data. The first thing you need to query your database, is a connection to it. Luckily, they are easy to get. You can use either the application or an incoming request to create a database connection. You just need -access to the [`DatabaseIdentifier`](provider.md#identifier). +access to the database identifier. ### Request @@ -22,7 +22,7 @@ router.get(...) { req in } ``` -The first parameter is the database's [identifier](provider.md#identifier). The second parameter is a closure +The first parameter is the database's identifier. The second parameter is a closure that accepts a connection to that database. !!! tip diff --git a/3.0/docs/getting-started/application.md b/3.0/docs/getting-started/application.md index 48dacb44..5ca92e81 100644 --- a/3.0/docs/getting-started/application.md +++ b/3.0/docs/getting-started/application.md @@ -50,4 +50,4 @@ let client = try app.make(Client.self) let res = client.get("http://vapor.codes") ``` -Learn more about services in [Services → Getting Started](../services/getting-started.md). +Learn more about services in [Getting Started → Services](services.md). diff --git a/3.0/docs/getting-started/routing.md b/3.0/docs/getting-started/routing.md index 82bb84e0..ddec13be 100644 --- a/3.0/docs/getting-started/routing.md +++ b/3.0/docs/getting-started/routing.md @@ -52,7 +52,7 @@ final class Routes: RouteCollection { ``` You _must_ return a Future containing a `ResponseEncodable` here. -The most common `ResponseEncodable` types are [`Content`](content.md), [`Response`](../http/response.md) amd [`View`](../leaf/view.md). +The most common `ResponseEncodable` types are [`Content`](content.md), Response and [`View`](../leaf/getting-started.md). ## Parameters @@ -73,4 +73,4 @@ Instead of passing a string, pass the _type_ of parameter you expect. In this ca ## After registering your routes -After registering your routes you must register the Router as a [`Service`](../concepts/services.md) +After registering your routes you must register the Router as a [`Service`](../getting-started/services.md) diff --git a/3.0/docs/getting-started/services.md b/3.0/docs/getting-started/services.md index 0a3b60c1..1ca9a150 100644 --- a/3.0/docs/getting-started/services.md +++ b/3.0/docs/getting-started/services.md @@ -2,15 +2,13 @@ Services is a framework for creating things you need in your application in a type-safe fashion with protocol and environment support. -The Services framework is designed to be thread unsafe. The framework aims to guarantee that a service exists on the same [EventLoop](../async/eventloop.md) it was created from and will be used on. +The Services framework is designed to be thread unsafe. The framework aims to guarantee that a service exists on the same EventLoop it was created from and will be used on. -## Glossary +## Container -### Container +Containers are event loops that can create and cache services. -Containers are [EventLoops](../async/eventloop.md) that can create and cache services. - -[`Request`](../http/request.md) is the most common `Container` type, which can be accessed in every [`Route`](../getting-started/routing.md). +Request is the most common `Container` type, which can be accessed in every [`Route`](../getting-started/routing.md). Containers cache instances of a given service (keyed by the requested protocol) on a per-container basis. @@ -23,11 +21,14 @@ There will be as many instances of a normal service per-container as there are u ### EphemeralContainer -EphemeralContainers are containers that are short-lived. -Their cache does not stretch beyond a short lifecycle. -The most common EphemeralContainer is an [HTTP Request](../http/request.md) which lives for the duration of the route handler. +EphemeralContainers are containers that are short-lived. Their cache does not stretch beyond a short lifecycle. The most common EphemeralContainer is an HTTP Request which lives for the duration of the route handler. -### Service +### Environment + +Environments indicate the type of deployment/situation in which an application is ran. Environments can be used to change database credentials or API tokens per environment automatically. + + +## Service Services are a type that can be requested from a Container. They are registered as part of the application setup. @@ -35,11 +36,7 @@ Services are registered to a matching type or protocol it can represent, includi Services are registered to a blueprint before the [`Application`](../getting-started/application.md) is initialized. Together they make up the blueprint that Containers use to create an individual Service. -### Environment - -Environments indicate the type of deployment/situation in which an application is ran. Environments can be used to change database credentials or API tokens per environment automatically. - -## Registering +### Registering Services are registered as a concrete (singleton) type or factories. Singleton types should be a struct, but can be a class. @@ -77,7 +74,7 @@ final class SingletonService { init() {} } -services.instance(isSingleton: true, SingletonService()) +services.instance(SingletonService()) ``` Assuming the above service, you can now make this service from a container. The global container in Vapor is `Application` which *must not* be used within routes. @@ -227,7 +224,7 @@ services.register { container -> BCryptConfig in ## Getting a Service To get a service you need an existing container matching the current EventLoop. -If you're processing a [`Request`](../http/request.md), you should almost always use the Request as a Container type. +If you're processing a Request, you should almost always use the Request as a Container type. ```swift // ErrorLogger diff --git a/3.0/docs/getting-started/structure.md b/3.0/docs/getting-started/structure.md index 1e068c32..714281d7 100644 --- a/3.0/docs/getting-started/structure.md +++ b/3.0/docs/getting-started/structure.md @@ -60,7 +60,7 @@ have many functions that accept a request and return some sort of response. #### Models The `Models` folder is a great place to store your [`Content`](content.md) structs or -Fluent [`Model`](../fluent/getting-started/models.md)s. +Fluent [`Model`](../fluent/models.md)s. #### boot.swift diff --git a/3.0/docs/leaf/basics.md b/3.0/docs/leaf/basics.md index c1cee86d..55ff3816 100644 --- a/3.0/docs/leaf/basics.md +++ b/3.0/docs/leaf/basics.md @@ -40,7 +40,7 @@ struct HomePage: Codable { ### Async -Leaf's engine is completely [reactive](../async/reactive.md), supporting both [streams](../async/streams.md) and [futures](../async/futures.md). One of the only ones of it's kind. +Leaf's engine is completely reactive, supporting both streams and futures. One of the only ones of its kind. When working with Future results, simply pass the `Future` in your template context. Streams that carry an encodable type need to be encoded before they're usable within Leaf. diff --git a/3.0/docs/mysql/fluent.md b/3.0/docs/mysql/fluent.md index 41c15d17..9f6f744d 100644 --- a/3.0/docs/mysql/fluent.md +++ b/3.0/docs/mysql/fluent.md @@ -91,7 +91,7 @@ extension User: Migration { } That's all it takes. Fluent uses Codable to analyze your model and will attempt to create the best possible schema for it. -Take a look at [Fluent → Migration](../fluent/migration.md) if you are interested in customizing this migration. +Take a look at [Fluent → Migration](../fluent/migrations.md) if you are interested in customizing this migration. ### Configure diff --git a/3.0/docs/postgresql/fluent.md b/3.0/docs/postgresql/fluent.md index a8fede31..96778bd8 100644 --- a/3.0/docs/postgresql/fluent.md +++ b/3.0/docs/postgresql/fluent.md @@ -91,7 +91,7 @@ extension User: Migration { } That's all it takes. Fluent uses Codable to analyze your model and will attempt to create the best possible schema for it. -Take a look at [Fluent → Migration](../fluent/migration.md) if you are interested in customizing this migration. +Take a look at [Fluent → Migration](../fluent/migrations.md) if you are interested in customizing this migration. ### Configure diff --git a/3.0/docs/redis/basics.md b/3.0/docs/redis/basics.md index 420595e3..d74f2f73 100644 --- a/3.0/docs/redis/basics.md +++ b/3.0/docs/redis/basics.md @@ -1,15 +1,15 @@ # Redis basic usage To interact with Redis, you first need to construct a Redis client. -The Redis library primarily supports [TCP sockets](../sockets/tcp-client.md). +The Redis library primarily supports TCP sockets. -This requires a hostname, port and [Worker](../async/eventloop.md). The eventloop will be used for Redis' Socket. The hostname and port have a default. The hostname is defaulted to `localhost`, and the port to Redis' default port `6379`. +This requires a hostname, port and worker. The eventloop will be used for Redis' Socket. The hostname and port have a default. The hostname is defaulted to `localhost`, and the port to Redis' default port `6379`. ```swift let client = try RedisClient.connect(on: worker) // Future ``` -The `connect` method will return a [Future](../async/futures.md) containing the TCP based Redis Client. +The `connect` method will return a future containing the TCP based Redis Client. ## Redis Data Types diff --git a/3.0/docs/redis/custom-commands.md b/3.0/docs/redis/custom-commands.md index 47cc04db..cd63e742 100644 --- a/3.0/docs/redis/custom-commands.md +++ b/3.0/docs/redis/custom-commands.md @@ -15,5 +15,3 @@ let future = client.run(command: "GET", arguments: ["my-key"]) // Future`](../http/response.md) conforming type. +The trailing closure receives a request. The route can throw errors and needs to return a future response conforming type. ## Registering a route using Vapor @@ -45,7 +45,7 @@ router.get("components", "in", "path") { request in After registering routes to the Router, you must add the router to your services. ```swift -services.instance(Router.self, router) +services.register(router, as: Router.self) ``` -[More about services here.](../concepts/services.md) +Learn more about services in [Getting Started → Services](../getting-started/services.md) diff --git a/3.0/docs/routing/parameters.md b/3.0/docs/routing/parameters.md index 382cc7ef..7c52994f 100644 --- a/3.0/docs/routing/parameters.md +++ b/3.0/docs/routing/parameters.md @@ -2,7 +2,7 @@ Parameters are a registered type that can be initialized from a String. -They can be part of a [Route](route.md), and be extracted from [Requests](../http/request.md) that are called in that Route. +They can be part of a [Route](route.md), and be extracted from requests that are called in that route. ## Creating custom parameters diff --git a/3.0/docs/routing/router.md b/3.0/docs/routing/router.md index 7fa1b554..59875aca 100644 --- a/3.0/docs/routing/router.md +++ b/3.0/docs/routing/router.md @@ -4,7 +4,7 @@ Router is a protocol that you can conform your own routers to. ## Registering a route -First, create a [Route](route.md) using a [Method](../http/method.md), path and a responder. +First, create a [Route](route.md) using a HTTP method, path and a responder. The following example shows a route with a constant path. @@ -35,7 +35,7 @@ Assuming you have a request, like the following example: let request = Request(method: .get, URI(path: "/hello/world")) ``` -The router should be able to route the [Request](../http/request.md) using +The router should be able to route the HTTP request using ```swift let responder = router.route(request: request) diff --git a/3.0/docs/sqlite/fluent.md b/3.0/docs/sqlite/fluent.md index 6cb01e85..a7cfa32b 100644 --- a/3.0/docs/sqlite/fluent.md +++ b/3.0/docs/sqlite/fluent.md @@ -91,7 +91,7 @@ extension User: Migration { } That's all it takes. Fluent uses Codable to analyze your model and will attempt to create the best possible schema for it. -Take a look at [Fluent → Migration](../fluent/migration.md) if you are interested in customizing this migration. +Take a look at [Fluent → Migration](../fluent/migrations.md) if you are interested in customizing this migration. ### Configure diff --git a/3.0/docs/testing/getting-started.md b/3.0/docs/testing/getting-started.md new file mode 100644 index 00000000..813e1984 --- /dev/null +++ b/3.0/docs/testing/getting-started.md @@ -0,0 +1,3 @@ +# Getting Started with Testing + +Coming soon. diff --git a/3.0/mkdocs.yml b/3.0/mkdocs.yml index fbe5d260..acd0330a 100644 --- a/3.0/mkdocs.yml +++ b/3.0/mkdocs.yml @@ -66,6 +66,10 @@ pages: - 'Message authentication': 'crypto/mac.md' - 'Password hashing': 'crypto/passwords.md' - 'Random': 'crypto/random.md' +- 'Testing': + - 'Getting Started': 'testing/getting-started.md' +- 'Deploy': + - 'Getting Started': 'deploy/getting-started.md' - 'Version (3.0-rc)': - '1.5': 'version/1_5.md' - '2.0': 'version/2_0.md' diff --git a/3.0/site/404.html b/3.0/site/404.html new file mode 100644 index 00000000..4cd94cde --- /dev/null +++ b/3.0/site/404.html @@ -0,0 +1,1343 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + + +
+
+ +

404 - Not found

+ + + + +
+
+
+
+ + +
+ + +
+ +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/assets/images/favicon.png b/3.0/site/assets/images/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..76d17f57ad903c3ea2f1b564cafb95bf9af84ee3 GIT binary patch literal 521 zcmV+k0`~ohP)kdg0005dNkl2WptjAn6@db&Pvy?U$ zv>P|<&rCZfZF0jmq0opf8)91(A<*iIVPPJJT((+JiF~>9KAA3%heFdnI;SaK+~|aU zQ~!x`%y{jX1<~SK2RxN7Db8`yWBbf6p7&07{VXfaam*cUs&eu*Zu(xaIL8rP){;a< zS~$}^Td32Rw+W1TqTd|L{#~jJet4!qwKsb5hq%YXiiUV!yH=ltu0>s|FLsT+Iy7K~ z!6*Z0a@vQ;AiZo!=s{{fqR+ct6YQPzbk+j}*qe7vtu39I7 zrOtZqU}=NnLchJxsU9iY+}3TYDl|BvPsX%E@dlyLgdV%q$UP|Y?DfcGb`}K&$;drd z+hL;zy7UTccUYU+h`ONIU|d=%`(0$=KW4%tVWXj~AE + + diff --git a/3.0/site/assets/images/icons/github.a4034fb1.svg b/3.0/site/assets/images/icons/github.a4034fb1.svg new file mode 100644 index 00000000..f8944b01 --- /dev/null +++ b/3.0/site/assets/images/icons/github.a4034fb1.svg @@ -0,0 +1,18 @@ + + + diff --git a/3.0/site/assets/images/icons/gitlab.348cdb3a.svg b/3.0/site/assets/images/icons/gitlab.348cdb3a.svg new file mode 100644 index 00000000..f4df57c6 --- /dev/null +++ b/3.0/site/assets/images/icons/gitlab.348cdb3a.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/3.0/site/assets/javascripts/application.8eb9be28.js b/3.0/site/assets/javascripts/application.8eb9be28.js new file mode 100644 index 00000000..f8983e6a --- /dev/null +++ b/3.0/site/assets/javascripts/application.8eb9be28.js @@ -0,0 +1 @@ +!function(e,t){for(var n in t)e[n]=t[n]}(window,function(e){function t(r){if(n[r])return n[r].exports;var i=n[r]={i:r,l:!1,exports:{}};return e[r].call(i.exports,i,i.exports,t),i.l=!0,i.exports}var n={};return t.m=e,t.c=n,t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s=6)}([function(e,t,n){"use strict";t.__esModule=!0,t.default={createElement:function(e,t){var n=document.createElement(e);t&&Array.prototype.forEach.call(Object.keys(t),function(e){n.setAttribute(e,t[e])});for(var r=arguments.length,i=Array(r>2?r-2:0),o=2;o pre, pre > code");Array.prototype.forEach.call(n,function(t,n){var r="__code_"+n,i=e.createElement("button",{class:"md-clipboard",title:h("clipboard.copy"),"data-clipboard-target":"#"+r+" pre, #"+r+" code"},e.createElement("span",{class:"md-clipboard__message"})),o=t.parentNode;o.id=r,o.insertBefore(i,t)});new c.default(".md-clipboard").on("success",function(e){var t=e.trigger.querySelector(".md-clipboard__message");if(!(t instanceof HTMLElement))throw new ReferenceError;e.clearSelection(),t.dataset.mdTimer&&clearTimeout(parseInt(t.dataset.mdTimer,10)),t.classList.add("md-clipboard__message--active"),t.innerHTML=h("clipboard.copied"),t.dataset.mdTimer=setTimeout(function(){t.classList.remove("md-clipboard__message--active"),t.dataset.mdTimer=""},2e3).toString()})}if(!Modernizr.details){var r=document.querySelectorAll("details > summary");Array.prototype.forEach.call(r,function(e){e.addEventListener("click",function(e){var t=e.target.parentNode;t.hasAttribute("open")?t.removeAttribute("open"):t.setAttribute("open","")})})}var i=function(){if(document.location.hash){var e=document.getElementById(document.location.hash.substring(1));if(!e)return;for(var t=e.parentNode;t&&!(t instanceof HTMLDetailsElement);)t=t.parentNode;if(t&&!t.open){t.open=!0;var n=location.hash;location.hash=" ",location.hash=n}}};if(window.addEventListener("hashchange",i),i(),Modernizr.ios){var o=document.querySelectorAll("[data-md-scrollfix]");Array.prototype.forEach.call(o,function(e){e.addEventListener("touchstart",function(){var t=e.scrollTop;0===t?e.scrollTop=1:t+e.offsetHeight===e.scrollHeight&&(e.scrollTop=t-1)})})}}).listen(),new f.default.Event.Listener(window,["scroll","resize","orientationchange"],new f.default.Header.Shadow("[data-md-component=container]","[data-md-component=header]")).listen(),new f.default.Event.Listener(window,["scroll","resize","orientationchange"],new f.default.Header.Title("[data-md-component=title]",".md-typeset h1")).listen(),document.querySelector("[data-md-component=hero]")&&new f.default.Event.Listener(window,["scroll","resize","orientationchange"],new f.default.Tabs.Toggle("[data-md-component=hero]")).listen(),document.querySelector("[data-md-component=tabs]")&&new f.default.Event.Listener(window,["scroll","resize","orientationchange"],new f.default.Tabs.Toggle("[data-md-component=tabs]")).listen(),new f.default.Event.MatchMedia("(min-width: 1220px)",new f.default.Event.Listener(window,["scroll","resize","orientationchange"],new f.default.Sidebar.Position("[data-md-component=navigation]","[data-md-component=header]"))),document.querySelector("[data-md-component=toc]")&&new f.default.Event.MatchMedia("(min-width: 960px)",new f.default.Event.Listener(window,["scroll","resize","orientationchange"],new f.default.Sidebar.Position("[data-md-component=toc]","[data-md-component=header]"))),new f.default.Event.MatchMedia("(min-width: 960px)",new f.default.Event.Listener(window,"scroll",new f.default.Nav.Blur("[data-md-component=toc] [href]")));var n=document.querySelectorAll("[data-md-component=collapsible]");Array.prototype.forEach.call(n,function(e){new f.default.Event.MatchMedia("(min-width: 1220px)",new f.default.Event.Listener(e.previousElementSibling,"click",new f.default.Nav.Collapse(e)))}),new f.default.Event.MatchMedia("(max-width: 1219px)",new f.default.Event.Listener("[data-md-component=navigation] [data-md-toggle]","change",new f.default.Nav.Scrolling("[data-md-component=navigation] nav"))),document.querySelector("[data-md-component=search]")&&(new f.default.Event.MatchMedia("(max-width: 959px)",new f.default.Event.Listener("[data-md-toggle=search]","change",new f.default.Search.Lock("[data-md-toggle=search]"))),new f.default.Event.Listener("[data-md-component=query]",["focus","keyup","change"],new f.default.Search.Result("[data-md-component=result]",function(){return fetch(t.url.base+"/"+(t.version<"0.17"?"mkdocs":"search")+"/search_index.json",{credentials:"same-origin"}).then(function(e){return e.json()}).then(function(e){return e.docs.map(function(e){return e.location=t.url.base+e.location,e})})})).listen(),new f.default.Event.Listener("[data-md-component=reset]","click",function(){setTimeout(function(){var e=document.querySelector("[data-md-component=query]");if(!(e instanceof HTMLInputElement))throw new ReferenceError;e.focus()},10)}).listen(),new f.default.Event.Listener("[data-md-toggle=search]","change",function(e){setTimeout(function(e){if(!(e instanceof HTMLInputElement))throw new ReferenceError;if(e.checked){var t=document.querySelector("[data-md-component=query]");if(!(t instanceof HTMLInputElement))throw new ReferenceError;t.focus()}},400,e.target)}).listen(),new f.default.Event.MatchMedia("(min-width: 960px)",new f.default.Event.Listener("[data-md-component=query]","focus",function(){var e=document.querySelector("[data-md-toggle=search]");if(!(e instanceof HTMLInputElement))throw new ReferenceError;e.checked||(e.checked=!0,e.dispatchEvent(new CustomEvent("change")))})),new f.default.Event.Listener(window,"keydown",function(e){var t=document.querySelector("[data-md-toggle=search]");if(!(t instanceof HTMLInputElement))throw new ReferenceError;var n=document.querySelector("[data-md-component=query]");if(!(n instanceof HTMLInputElement))throw new ReferenceError;if(!e.metaKey&&!e.ctrlKey)if(t.checked){if(13===e.keyCode){if(n===document.activeElement){e.preventDefault();var r=document.querySelector("[data-md-component=search] [href][data-md-state=active]");r instanceof HTMLLinkElement&&(window.location=r.getAttribute("href"),t.checked=!1,t.dispatchEvent(new CustomEvent("change")),n.blur())}}else if(9===e.keyCode||27===e.keyCode)t.checked=!1,t.dispatchEvent(new CustomEvent("change")),n.blur();else if(-1!==[8,37,39].indexOf(e.keyCode))n!==document.activeElement&&n.focus();else if(-1!==[38,40].indexOf(e.keyCode)){var i=e.keyCode,o=Array.prototype.slice.call(document.querySelectorAll("[data-md-component=query], [data-md-component=search] [href]")),a=o.find(function(e){if(!(e instanceof HTMLElement))throw new ReferenceError;return"active"===e.dataset.mdState});a&&(a.dataset.mdState="");var s=Math.max(0,(o.indexOf(a)+o.length+(38===i?-1:1))%o.length);return o[s]&&(o[s].dataset.mdState="active",o[s].focus()),e.preventDefault(),e.stopPropagation(),!1}}else document.activeElement&&!document.activeElement.form&&(70!==e.keyCode&&83!==e.keyCode||(n.focus(),e.preventDefault()))}).listen(),new f.default.Event.Listener(window,"keypress",function(){var e=document.querySelector("[data-md-toggle=search]");if(!(e instanceof HTMLInputElement))throw new ReferenceError;if(e.checked){var t=document.querySelector("[data-md-component=query]");if(!(t instanceof HTMLInputElement))throw new ReferenceError;t!==document.activeElement&&t.focus()}}).listen()),new f.default.Event.Listener(document.body,"keydown",function(e){if(9===e.keyCode){var t=document.querySelectorAll("[data-md-component=navigation] .md-nav__link[for]:not([tabindex])");Array.prototype.forEach.call(t,function(e){e.offsetHeight&&(e.tabIndex=0)})}}).listen(),new f.default.Event.Listener(document.body,"mousedown",function(){var e=document.querySelectorAll("[data-md-component=navigation] .md-nav__link[tabindex]");Array.prototype.forEach.call(e,function(e){e.removeAttribute("tabIndex")})}).listen(),document.body.addEventListener("click",function(){"tabbing"===document.body.dataset.mdState&&(document.body.dataset.mdState="")}),new f.default.Event.MatchMedia("(max-width: 959px)",new f.default.Event.Listener("[data-md-component=navigation] [href^='#']","click",function(){var e=document.querySelector("[data-md-toggle=drawer]");if(!(e instanceof HTMLInputElement))throw new ReferenceError;e.checked&&(e.checked=!1,e.dispatchEvent(new CustomEvent("change")))})),function(){var e=document.querySelector("[data-md-source]");if(!e)return a.default.resolve([]);if(!(e instanceof HTMLAnchorElement))throw new ReferenceError;switch(e.dataset.mdSource){case"github":return new f.default.Source.Adapter.GitHub(e).fetch();default:return a.default.resolve([])}}().then(function(e){var t=document.querySelectorAll("[data-md-source]");Array.prototype.forEach.call(t,function(t){new f.default.Source.Repository(t).initialize(e)})})}t.__esModule=!0,t.app=void 0,n(7),n(8),n(9),n(10),n(11),n(12),n(13);var o=n(14),a=r(o),s=n(18),c=r(s),u=n(26),l=r(u),d=n(27),f=r(d);window.Promise=window.Promise||a.default;var h=function(e){var t=document.getElementsByName("lang:"+e)[0];if(!(t instanceof HTMLMetaElement))throw new ReferenceError;return t.content},p={initialize:i};t.app=p}).call(t,n(0))},function(e,t,n){e.exports=n.p+"assets/images/icons/bitbucket.4ebea66e.svg"},function(e,t,n){e.exports=n.p+"assets/images/icons/github.a4034fb1.svg"},function(e,t,n){e.exports=n.p+"assets/images/icons/gitlab.348cdb3a.svg"},function(e,t){},function(e,t){},function(e,t){try{var n=new window.CustomEvent("test");if(n.preventDefault(),!0!==n.defaultPrevented)throw new Error("Could not prevent default")}catch(e){var r=function(e,t){var n,r;return t=t||{bubbles:!1,cancelable:!1,detail:void 0},n=document.createEvent("CustomEvent"),n.initCustomEvent(e,t.bubbles,t.cancelable,t.detail),r=n.preventDefault,n.preventDefault=function(){r.call(this);try{Object.defineProperty(this,"defaultPrevented",{get:function(){return!0}})}catch(e){this.defaultPrevented=!0}},n};r.prototype=window.Event.prototype,window.CustomEvent=r}},function(e,t,n){window.fetch||(window.fetch=n(2).default||n(2))},function(e,t,n){"use strict";(function(t){function n(){}function r(e,t){return function(){e.apply(t,arguments)}}function i(e){if(!(this instanceof i))throw new TypeError("Promises must be constructed via new");if("function"!=typeof e)throw new TypeError("not a function");this._state=0,this._handled=!1,this._value=void 0,this._deferreds=[],l(e,this)}function o(e,t){for(;3===e._state;)e=e._value;if(0===e._state)return void e._deferreds.push(t);e._handled=!0,i._immediateFn(function(){var n=1===e._state?t.onFulfilled:t.onRejected;if(null===n)return void(1===e._state?a:s)(t.promise,e._value);var r;try{r=n(e._value)}catch(e){return void s(t.promise,e)}a(t.promise,r)})}function a(e,t){try{if(t===e)throw new TypeError("A promise cannot be resolved with itself.");if(t&&("object"==typeof t||"function"==typeof t)){var n=t.then;if(t instanceof i)return e._state=3,e._value=t,void c(e);if("function"==typeof n)return void l(r(n,t),e)}e._state=1,e._value=t,c(e)}catch(t){s(e,t)}}function s(e,t){e._state=2,e._value=t,c(e)}function c(e){2===e._state&&0===e._deferreds.length&&i._immediateFn(function(){e._handled||i._unhandledRejectionFn(e._value)});for(var t=0,n=e._deferreds.length;t=0&&(e._idleTimeoutId=setTimeout(function(){e._onTimeout&&e._onTimeout()},t))},n(16),t.setImmediate="undefined"!=typeof self&&self.setImmediate||void 0!==e&&e.setImmediate||this&&this.setImmediate,t.clearImmediate="undefined"!=typeof self&&self.clearImmediate||void 0!==e&&e.clearImmediate||this&&this.clearImmediate}).call(t,n(1))},function(e,t,n){(function(e,t){!function(e,n){"use strict";function r(e){"function"!=typeof e&&(e=new Function(""+e));for(var t=new Array(arguments.length-1),n=0;n1)for(var n=1;n0&&void 0!==arguments[0]?arguments[0]:{};this.action="function"==typeof e.action?e.action:this.defaultAction,this.target="function"==typeof e.target?e.target:this.defaultTarget,this.text="function"==typeof e.text?e.text:this.defaultText,this.container="object"===f(e.container)?e.container:document.body}},{key:"listenClick",value:function(e){var t=this;this.listener=(0,d.default)(e,"click",function(e){return t.onClick(e)})}},{key:"onClick",value:function(e){var t=e.delegateTarget||e.currentTarget;this.clipboardAction&&(this.clipboardAction=null),this.clipboardAction=new u.default({action:this.action(t),target:this.target(t),text:this.text(t),container:this.container,trigger:t,emitter:this})}},{key:"defaultAction",value:function(e){return c("action",e)}},{key:"defaultTarget",value:function(e){var t=c("target",e);if(t)return document.querySelector(t)}},{key:"defaultText",value:function(e){return c("text",e)}},{key:"destroy",value:function(){this.listener.destroy(),this.clipboardAction&&(this.clipboardAction.destroy(),this.clipboardAction=null)}}],[{key:"isSupported",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:["copy","cut"],t="string"==typeof e?[e]:e,n=!!document.queryCommandSupported;return t.forEach(function(e){n=n&&!!document.queryCommandSupported(e)}),n}}]),t}(l.default);e.exports=p})},function(e,t,n){var r,i,o;!function(a,s){i=[e,n(20)],r=s,void 0!==(o="function"==typeof r?r.apply(t,i):r)&&(e.exports=o)}(0,function(e,t){"use strict";function n(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}var r=function(e){return e&&e.__esModule?e:{default:e}}(t),i="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},o=function(){function e(e,t){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:{};this.action=e.action,this.container=e.container,this.emitter=e.emitter,this.target=e.target,this.text=e.text,this.trigger=e.trigger,this.selectedText=""}},{key:"initSelection",value:function(){this.text?this.selectFake():this.target&&this.selectTarget()}},{key:"selectFake",value:function(){var e=this,t="rtl"==document.documentElement.getAttribute("dir");this.removeFake(),this.fakeHandlerCallback=function(){return e.removeFake()},this.fakeHandler=this.container.addEventListener("click",this.fakeHandlerCallback)||!0,this.fakeElem=document.createElement("textarea"),this.fakeElem.style.fontSize="12pt",this.fakeElem.style.border="0",this.fakeElem.style.padding="0",this.fakeElem.style.margin="0",this.fakeElem.style.position="absolute",this.fakeElem.style[t?"right":"left"]="-9999px";var n=window.pageYOffset||document.documentElement.scrollTop;this.fakeElem.style.top=n+"px",this.fakeElem.setAttribute("readonly",""),this.fakeElem.value=this.text,this.container.appendChild(this.fakeElem),this.selectedText=(0,r.default)(this.fakeElem),this.copyText()}},{key:"removeFake",value:function(){this.fakeHandler&&(this.container.removeEventListener("click",this.fakeHandlerCallback),this.fakeHandler=null,this.fakeHandlerCallback=null),this.fakeElem&&(this.container.removeChild(this.fakeElem),this.fakeElem=null)}},{key:"selectTarget",value:function(){this.selectedText=(0,r.default)(this.target),this.copyText()}},{key:"copyText",value:function(){var e=void 0;try{e=document.execCommand(this.action)}catch(t){e=!1}this.handleResult(e)}},{key:"handleResult",value:function(e){this.emitter.emit(e?"success":"error",{action:this.action,text:this.selectedText,trigger:this.trigger,clearSelection:this.clearSelection.bind(this)})}},{key:"clearSelection",value:function(){this.trigger&&this.trigger.focus(),window.getSelection().removeAllRanges()}},{key:"destroy",value:function(){this.removeFake()}},{key:"action",set:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"copy";if(this._action=e,"copy"!==this._action&&"cut"!==this._action)throw new Error('Invalid "action" value, use either "copy" or "cut"')},get:function(){return this._action}},{key:"target",set:function(e){if(void 0!==e){if(!e||"object"!==(void 0===e?"undefined":i(e))||1!==e.nodeType)throw new Error('Invalid "target" value, use a valid Element');if("copy"===this.action&&e.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if("cut"===this.action&&(e.hasAttribute("readonly")||e.hasAttribute("disabled")))throw new Error('Invalid "target" attribute. You can\'t cut text from elements with "readonly" or "disabled" attributes');this._target=e}},get:function(){return this._target}}]),e}();e.exports=a})},function(e,t){function n(e){var t;if("SELECT"===e.nodeName)e.focus(),t=e.value;else if("INPUT"===e.nodeName||"TEXTAREA"===e.nodeName){var n=e.hasAttribute("readonly");n||e.setAttribute("readonly",""),e.select(),e.setSelectionRange(0,e.value.length),n||e.removeAttribute("readonly"),t=e.value}else{e.hasAttribute("contenteditable")&&e.focus();var r=window.getSelection(),i=document.createRange();i.selectNodeContents(e),r.removeAllRanges(),r.addRange(i),t=r.toString()}return t}e.exports=n},function(e,t){function n(){}n.prototype={on:function(e,t,n){var r=this.e||(this.e={});return(r[e]||(r[e]=[])).push({fn:t,ctx:n}),this},once:function(e,t,n){function r(){i.off(e,r),t.apply(n,arguments)}var i=this;return r._=t,this.on(e,r,n)},emit:function(e){var t=[].slice.call(arguments,1),n=((this.e||(this.e={}))[e]||[]).slice(),r=0,i=n.length;for(r;r=0,a=navigator.userAgent.indexOf("Android")>0&&!o,s=/iP(ad|hone|od)/.test(navigator.userAgent)&&!o,c=s&&/OS 4_\d(_\d)?/.test(navigator.userAgent),u=s&&/OS [6-7]_\d/.test(navigator.userAgent),l=navigator.userAgent.indexOf("BB10")>0;i.prototype.needsClick=function(e){switch(e.nodeName.toLowerCase()){case"button":case"select":case"textarea":if(e.disabled)return!0;break;case"input":if(s&&"file"===e.type||e.disabled)return!0;break;case"label":case"iframe":case"video":return!0}return/\bneedsclick\b/.test(e.className)},i.prototype.needsFocus=function(e){switch(e.nodeName.toLowerCase()){case"textarea":return!0;case"select":return!a;case"input":switch(e.type){case"button":case"checkbox":case"file":case"image":case"radio":case"submit":return!1}return!e.disabled&&!e.readOnly;default:return/\bneedsfocus\b/.test(e.className)}},i.prototype.sendClick=function(e,t){var n,r;document.activeElement&&document.activeElement!==e&&document.activeElement.blur(),r=t.changedTouches[0],n=document.createEvent("MouseEvents"),n.initMouseEvent(this.determineEventType(e),!0,!0,window,1,r.screenX,r.screenY,r.clientX,r.clientY,!1,!1,!1,!1,0,null),n.forwardedTouchEvent=!0,e.dispatchEvent(n)},i.prototype.determineEventType=function(e){return a&&"select"===e.tagName.toLowerCase()?"mousedown":"click"},i.prototype.focus=function(e){var t;s&&e.setSelectionRange&&0!==e.type.indexOf("date")&&"time"!==e.type&&"month"!==e.type?(t=e.value.length,e.setSelectionRange(t,t)):e.focus()},i.prototype.updateScrollParent=function(e){var t,n;if(!(t=e.fastClickScrollParent)||!t.contains(e)){n=e;do{if(n.scrollHeight>n.offsetHeight){t=n,e.fastClickScrollParent=n;break}n=n.parentElement}while(n)}t&&(t.fastClickLastScrollTop=t.scrollTop)},i.prototype.getTargetElementFromEventTarget=function(e){return e.nodeType===Node.TEXT_NODE?e.parentNode:e},i.prototype.onTouchStart=function(e){var t,n,r;if(e.targetTouches.length>1)return!0;if(t=this.getTargetElementFromEventTarget(e.target),n=e.targetTouches[0],s){if(r=window.getSelection(),r.rangeCount&&!r.isCollapsed)return!0;if(!c){if(n.identifier&&n.identifier===this.lastTouchIdentifier)return e.preventDefault(),!1;this.lastTouchIdentifier=n.identifier,this.updateScrollParent(t)}}return this.trackingClick=!0,this.trackingClickStart=e.timeStamp,this.targetElement=t,this.touchStartX=n.pageX,this.touchStartY=n.pageY,e.timeStamp-this.lastClickTimen||Math.abs(t.pageY-this.touchStartY)>n},i.prototype.onTouchMove=function(e){return!this.trackingClick||((this.targetElement!==this.getTargetElementFromEventTarget(e.target)||this.touchHasMoved(e))&&(this.trackingClick=!1,this.targetElement=null),!0)},i.prototype.findControl=function(e){return void 0!==e.control?e.control:e.htmlFor?document.getElementById(e.htmlFor):e.querySelector("button, input:not([type=hidden]), keygen, meter, output, progress, select, textarea")},i.prototype.onTouchEnd=function(e){var t,n,r,i,o,l=this.targetElement;if(!this.trackingClick)return!0;if(e.timeStamp-this.lastClickTimethis.tapTimeout)return!0;if(this.cancelNextClick=!1,this.lastClickTime=e.timeStamp,n=this.trackingClickStart,this.trackingClick=!1,this.trackingClickStart=0,u&&(o=e.changedTouches[0],l=document.elementFromPoint(o.pageX-window.pageXOffset,o.pageY-window.pageYOffset)||l,l.fastClickScrollParent=this.targetElement.fastClickScrollParent),"label"===(r=l.tagName.toLowerCase())){if(t=this.findControl(l)){if(this.focus(l),a)return!1;l=t}}else if(this.needsFocus(l))return e.timeStamp-n>100||s&&window.top!==window&&"input"===r?(this.targetElement=null,!1):(this.focus(l),this.sendClick(l,e),s&&"select"===r||(this.targetElement=null,e.preventDefault()),!1);return!(!s||c||!(i=l.fastClickScrollParent)||i.fastClickLastScrollTop===i.scrollTop)||(this.needsClick(l)||(e.preventDefault(),this.sendClick(l,e)),!1)},i.prototype.onTouchCancel=function(){this.trackingClick=!1,this.targetElement=null},i.prototype.onMouse=function(e){return!this.targetElement||(!!e.forwardedTouchEvent||(!e.cancelable||(!(!this.needsClick(this.targetElement)||this.cancelNextClick)||(e.stopImmediatePropagation?e.stopImmediatePropagation():e.propagationStopped=!0,e.stopPropagation(),e.preventDefault(),!1))))},i.prototype.onClick=function(e){var t;return this.trackingClick?(this.targetElement=null,this.trackingClick=!1,!0):"submit"===e.target.type&&0===e.detail||(t=this.onMouse(e),t||(this.targetElement=null),t)},i.prototype.destroy=function(){var e=this.layer;a&&(e.removeEventListener("mouseover",this.onMouse,!0),e.removeEventListener("mousedown",this.onMouse,!0),e.removeEventListener("mouseup",this.onMouse,!0)),e.removeEventListener("click",this.onClick,!0),e.removeEventListener("touchstart",this.onTouchStart,!1),e.removeEventListener("touchmove",this.onTouchMove,!1),e.removeEventListener("touchend",this.onTouchEnd,!1),e.removeEventListener("touchcancel",this.onTouchCancel,!1)},i.notNeeded=function(e){var t,n,r;if(void 0===window.ontouchstart)return!0;if(n=+(/Chrome\/([0-9]+)/.exec(navigator.userAgent)||[,0])[1]){if(!a)return!0;if(t=document.querySelector("meta[name=viewport]")){if(-1!==t.content.indexOf("user-scalable=no"))return!0;if(n>31&&document.documentElement.scrollWidth<=window.outerWidth)return!0}}if(l&&(r=navigator.userAgent.match(/Version\/([0-9]*)\.([0-9]*)/),r[1]>=10&&r[2]>=3&&(t=document.querySelector("meta[name=viewport]")))){if(-1!==t.content.indexOf("user-scalable=no"))return!0;if(document.documentElement.scrollWidth<=window.outerWidth)return!0}return"none"===e.style.msTouchAction||"manipulation"===e.style.touchAction||(!!(+(/Firefox\/([0-9]+)/.exec(navigator.userAgent)||[,0])[1]>=27&&(t=document.querySelector("meta[name=viewport]"))&&(-1!==t.content.indexOf("user-scalable=no")||document.documentElement.scrollWidth<=window.outerWidth))||("none"===e.style.touchAction||"manipulation"===e.style.touchAction))},i.attach=function(e,t){return new i(e,t)},void 0!==(r=function(){return i}.call(t,n,t,e))&&(e.exports=r)}()},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}t.__esModule=!0;var i=n(28),o=r(i),a=n(30),s=r(a),c=n(33),u=r(c),l=n(37),d=r(l),f=n(43),h=r(f),p=n(45),m=r(p),v=n(51),y=r(v);t.default={Event:o.default,Header:s.default,Nav:u.default,Search:d.default,Sidebar:h.default,Source:m.default,Tabs:y.default}},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}t.__esModule=!0;var i=n(3),o=r(i),a=n(29),s=r(a);t.default={Listener:o.default,MatchMedia:s.default}},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}t.__esModule=!0;var i=n(3),o=(function(e){e&&e.__esModule}(i),function e(t,n){r(this,e),this.handler_=function(e){e.matches?n.listen():n.unlisten()};var i=window.matchMedia(t);i.addListener(this.handler_),this.handler_(i)});t.default=o},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}t.__esModule=!0;var i=n(31),o=r(i),a=n(32),s=r(a);t.default={Shadow:o.default,Title:s.default}},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}t.__esModule=!0;var i=function(){function e(t,n){r(this,e);var i="string"==typeof t?document.querySelector(t):t;if(!(i instanceof HTMLElement&&i.parentNode instanceof HTMLElement))throw new ReferenceError;if(this.el_=i.parentNode,!((i="string"==typeof n?document.querySelector(n):n)instanceof HTMLElement))throw new ReferenceError;this.header_=i,this.height_=0,this.active_=!1}return e.prototype.setup=function(){for(var e=this.el_;e=e.previousElementSibling;){if(!(e instanceof HTMLElement))throw new ReferenceError;this.height_+=e.offsetHeight}this.update()},e.prototype.update=function(e){if(!e||"resize"!==e.type&&"orientationchange"!==e.type){var t=window.pageYOffset>=this.height_;t!==this.active_&&(this.header_.dataset.mdState=(this.active_=t)?"shadow":"")}else this.height_=0,this.setup()},e.prototype.reset=function(){this.header_.dataset.mdState="",this.height_=0,this.active_=!1},e}();t.default=i},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}t.__esModule=!0;var i=function(){function e(t,n){r(this,e);var i="string"==typeof t?document.querySelector(t):t;if(!(i instanceof HTMLElement))throw new ReferenceError;if(this.el_=i,!((i="string"==typeof n?document.querySelector(n):n)instanceof HTMLHeadingElement))throw new ReferenceError;this.header_=i,this.active_=!1}return e.prototype.setup=function(){var e=this;Array.prototype.forEach.call(this.el_.children,function(t){t.style.width=e.el_.offsetWidth-20+"px"})},e.prototype.update=function(e){var t=this,n=window.pageYOffset>=this.header_.offsetTop;n!==this.active_&&(this.el_.dataset.mdState=(this.active_=n)?"active":""),"resize"!==e.type&&"orientationchange"!==e.type||Array.prototype.forEach.call(this.el_.children,function(e){e.style.width=t.el_.offsetWidth-20+"px"})},e.prototype.reset=function(){this.el_.dataset.mdState="",this.el_.style.width="",this.active_=!1},e}();t.default=i},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}t.__esModule=!0;var i=n(34),o=r(i),a=n(35),s=r(a),c=n(36),u=r(c);t.default={Blur:o.default,Collapse:s.default,Scrolling:u.default}},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}t.__esModule=!0;var i=function(){function e(t){r(this,e),this.els_="string"==typeof t?document.querySelectorAll(t):t,this.index_=0,this.offset_=window.pageYOffset,this.dir_=!1,this.anchors_=[].reduce.call(this.els_,function(e,t){return e.concat(document.getElementById(t.hash.substring(1))||[])},[])}return e.prototype.setup=function(){this.update()},e.prototype.update=function(){var e=window.pageYOffset,t=this.offset_-e<0;if(this.dir_!==t&&(this.index_=this.index_=t?0:this.els_.length-1),0!==this.anchors_.length){if(this.offset_<=e)for(var n=this.index_+1;n0&&(this.els_[n-1].dataset.mdState="blur"),this.index_=n;else for(var r=this.index_;r>=0;r--){if(!(this.anchors_[r].offsetTop-80>e)){this.index_=r;break}r>0&&(this.els_[r-1].dataset.mdState="")}this.offset_=e,this.dir_=t}},e.prototype.reset=function(){Array.prototype.forEach.call(this.els_,function(e){e.dataset.mdState=""}),this.index_=0,this.offset_=window.pageYOffset},e}();t.default=i},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}t.__esModule=!0;var i=function(){function e(t){r(this,e);var n="string"==typeof t?document.querySelector(t):t;if(!(n instanceof HTMLElement))throw new ReferenceError;this.el_=n}return e.prototype.setup=function(){var e=this.el_.getBoundingClientRect().height;this.el_.style.display=e?"block":"none",this.el_.style.overflow=e?"visible":"hidden"},e.prototype.update=function(){var e=this,t=this.el_.getBoundingClientRect().height;if(this.el_.style.display="block",this.el_.style.overflow="",t)this.el_.style.maxHeight=t+"px",requestAnimationFrame(function(){e.el_.setAttribute("data-md-state","animate"),e.el_.style.maxHeight="0px"});else{this.el_.setAttribute("data-md-state","expand"),this.el_.style.maxHeight="";var n=this.el_.getBoundingClientRect().height;this.el_.removeAttribute("data-md-state"),this.el_.style.maxHeight="0px",requestAnimationFrame(function(){e.el_.setAttribute("data-md-state","animate"),e.el_.style.maxHeight=n+"px"})}var r=function e(n){var r=n.target;if(!(r instanceof HTMLElement))throw new ReferenceError;r.removeAttribute("data-md-state"),r.style.maxHeight="",r.style.display=t?"none":"block",r.style.overflow=t?"hidden":"visible",r.removeEventListener("transitionend",e)};this.el_.addEventListener("transitionend",r,!1)},e.prototype.reset=function(){this.el_.dataset.mdState="",this.el_.style.maxHeight="",this.el_.style.display="",this.el_.style.overflow=""},e}();t.default=i},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}t.__esModule=!0;var i=function(){function e(t){r(this,e);var n="string"==typeof t?document.querySelector(t):t;if(!(n instanceof HTMLElement))throw new ReferenceError;this.el_=n}return e.prototype.setup=function(){this.el_.children[this.el_.children.length-1].style.webkitOverflowScrolling="touch";var e=this.el_.querySelectorAll("[data-md-toggle]");Array.prototype.forEach.call(e,function(e){if(!(e instanceof HTMLInputElement))throw new ReferenceError;if(e.checked){var t=e.nextElementSibling;if(!(t instanceof HTMLElement))throw new ReferenceError;for(;"NAV"!==t.tagName&&t.nextElementSibling;)t=t.nextElementSibling;if(!(e.parentNode instanceof HTMLElement&&e.parentNode.parentNode instanceof HTMLElement))throw new ReferenceError;var n=e.parentNode.parentNode,r=t.children[t.children.length-1];n.style.webkitOverflowScrolling="",r.style.webkitOverflowScrolling="touch"}})},e.prototype.update=function(e){var t=e.target;if(!(t instanceof HTMLElement))throw new ReferenceError;var n=t.nextElementSibling;if(!(n instanceof HTMLElement))throw new ReferenceError;for(;"NAV"!==n.tagName&&n.nextElementSibling;)n=n.nextElementSibling;if(!(t.parentNode instanceof HTMLElement&&t.parentNode.parentNode instanceof HTMLElement))throw new ReferenceError;var r=t.parentNode.parentNode,i=n.children[n.children.length-1];if(r.style.webkitOverflowScrolling="",i.style.webkitOverflowScrolling="",!t.checked){var o=function e(){n instanceof HTMLElement&&(r.style.webkitOverflowScrolling="touch",n.removeEventListener("transitionend",e))};n.addEventListener("transitionend",o,!1)}if(t.checked){var a=function e(){n instanceof HTMLElement&&(i.style.webkitOverflowScrolling="touch",n.removeEventListener("transitionend",e))};n.addEventListener("transitionend",a,!1)}},e.prototype.reset=function(){this.el_.children[1].style.webkitOverflowScrolling="";var e=this.el_.querySelectorAll("[data-md-toggle]");Array.prototype.forEach.call(e,function(e){if(!(e instanceof HTMLInputElement))throw new ReferenceError;if(e.checked){var t=e.nextElementSibling;if(!(t instanceof HTMLElement))throw new ReferenceError;for(;"NAV"!==t.tagName&&t.nextElementSibling;)t=t.nextElementSibling;if(!(e.parentNode instanceof HTMLElement&&e.parentNode.parentNode instanceof HTMLElement))throw new ReferenceError;var n=e.parentNode.parentNode,r=t.children[t.children.length-1];n.style.webkitOverflowScrolling="",r.style.webkitOverflowScrolling=""}})},e}();t.default=i},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}t.__esModule=!0;var i=n(38),o=r(i),a=n(39),s=r(a);t.default={Lock:o.default,Result:s.default}},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}t.__esModule=!0;var i=function(){function e(t){r(this,e);var n="string"==typeof t?document.querySelector(t):t;if(!(n instanceof HTMLInputElement))throw new ReferenceError;if(this.el_=n,!document.body)throw new ReferenceError;this.lock_=document.body}return e.prototype.setup=function(){this.update()},e.prototype.update=function(){var e=this;this.el_.checked?(this.offset_=window.pageYOffset,setTimeout(function(){window.scrollTo(0,0),e.el_.checked&&(e.lock_.dataset.mdState="lock")},400)):(this.lock_.dataset.mdState="",setTimeout(function(){void 0!==e.offset_&&window.scrollTo(0,e.offset_)},100))},e.prototype.reset=function(){"lock"===this.lock_.dataset.mdState&&window.scrollTo(0,this.offset_),this.lock_.dataset.mdState=""},e}();t.default=i},function(e,t,n){"use strict";(function(e){function r(e){return e&&e.__esModule?e:{default:e}}function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}t.__esModule=!0;var o=n(40),a=r(o),s=n(41),c=r(s),u=function(e,t){var n=t;if(e.length>n){for(;" "!==e[n]&&--n>0;);return e.substring(0,n)+"..."}return e},l=function(e){var t=document.getElementsByName("lang:"+e)[0];if(!(t instanceof HTMLMetaElement))throw new ReferenceError;return t.content},d=function(){function t(e,n){i(this,t);var r="string"==typeof e?document.querySelector(e):e;if(!(r instanceof HTMLElement))throw new ReferenceError;this.el_=r;var o=Array.prototype.slice.call(this.el_.children),a=o[0],s=o[1];this.data_=n,this.meta_=a,this.list_=s,this.message_={placeholder:this.meta_.textContent,none:l("search.result.none"),one:l("search.result.one"),other:l("search.result.other")};var u=l("search.tokenizer");u.length&&(c.default.tokenizer.separator=u),this.lang_=l("search.language").split(",").filter(Boolean).map(function(e){return e.trim()})}return t.prototype.update=function(t){var n=this;if("focus"!==t.type||this.index_){if("focus"===t.type||"keyup"===t.type){var r=t.target;if(!(r instanceof HTMLInputElement))throw new ReferenceError;if(!this.index_||r.value===this.value_)return;for(;this.list_.firstChild;)this.list_.removeChild(this.list_.firstChild);if(this.value_=r.value,0===this.value_.length)return void(this.meta_.textContent=this.message_.placeholder);var i=this.index_.query(function(e){n.value_.toLowerCase().split(" ").filter(Boolean).forEach(function(t){e.term(t,{wildcard:c.default.Query.wildcard.TRAILING})})}).reduce(function(e,t){var r=n.docs_.get(t.ref);if(r.parent){var i=r.parent.location;e.set(i,(e.get(i)||[]).concat(t))}else{var o=r.location;e.set(o,e.get(o)||[])}return e},new Map),o=(0,a.default)(this.value_.trim()).replace(new RegExp(c.default.tokenizer.separator,"img"),"|"),s=new RegExp("(^|"+c.default.tokenizer.separator+")("+o+")","img"),d=function(e,t,n){return t+""+n+""};this.stack_=[],i.forEach(function(t,r){var i,o=n.docs_.get(r),a=e.createElement("li",{class:"md-search-result__item"},e.createElement("a",{href:o.location,title:o.title,class:"md-search-result__link",tabindex:"-1"},e.createElement("article",{class:"md-search-result__article md-search-result__article--document"},e.createElement("h1",{class:"md-search-result__title"},{__html:o.title.replace(s,d)}),o.text.length?e.createElement("p",{class:"md-search-result__teaser"},{__html:o.text.replace(s,d)}):{}))),c=t.map(function(t){return function(){var r=n.docs_.get(t.ref);a.appendChild(e.createElement("a",{href:r.location,title:r.title,class:"md-search-result__link","data-md-rel":"anchor",tabindex:"-1"},e.createElement("article",{class:"md-search-result__article"},e.createElement("h1",{class:"md-search-result__title"},{__html:r.title.replace(s,d)}),r.text.length?e.createElement("p",{class:"md-search-result__teaser"},{__html:u(r.text.replace(s,d),400)}):{})))}});(i=n.stack_).push.apply(i,[function(){return n.list_.appendChild(a)}].concat(c))});var f=this.el_.parentNode;if(!(f instanceof HTMLElement))throw new ReferenceError;for(;this.stack_.length&&f.offsetHeight>=f.scrollHeight-16;)this.stack_.shift()();var h=this.list_.querySelectorAll("[data-md-rel=anchor]");switch(Array.prototype.forEach.call(h,function(e){["click","keydown"].forEach(function(t){e.addEventListener(t,function(n){if("keydown"!==t||13===n.keyCode){var r=document.querySelector("[data-md-toggle=search]");if(!(r instanceof HTMLInputElement))throw new ReferenceError;r.checked&&(r.checked=!1,r.dispatchEvent(new CustomEvent("change"))),n.preventDefault(),setTimeout(function(){document.location.href=e.href},100)}})})}),i.size){case 0:this.meta_.textContent=this.message_.none;break;case 1:this.meta_.textContent=this.message_.one;break;default:this.meta_.textContent=this.message_.other.replace("#",i.size)}}}else{var p=function(e){n.docs_=e.reduce(function(e,t){var n=t.location.split("#"),r=n[0];return n[1]&&(t.parent=e.get(r),t.parent&&!t.parent.done&&(t.parent.title=t.title,t.parent.text=t.text,t.parent.done=!0)),t.text=t.text.replace(/\n/g," ").replace(/\s+/g," ").replace(/\s+([,.:;!?])/g,function(e,t){return t}),t.parent&&t.parent.title===t.title||e.set(t.location,t),e},new Map);var t=n.docs_,r=n.lang_;n.stack_=[],n.index_=(0,c.default)(function(){var e,n=this,i={"search.pipeline.trimmer":c.default.trimmer,"search.pipeline.stopwords":c.default.stopWordFilter},o=Object.keys(i).reduce(function(e,t){return l(t).match(/^false$/i)||e.push(i[t]),e},[]);this.pipeline.reset(),o&&(e=this.pipeline).add.apply(e,o),1===r.length&&"en"!==r[0]&&c.default[r[0]]?this.use(c.default[r[0]]):r.length>1&&this.use(c.default.multiLanguage.apply(c.default,r)),this.field("title",{boost:10}),this.field("text"),this.ref("location"),t.forEach(function(e){return n.add(e)})});var i=n.el_.parentNode;if(!(i instanceof HTMLElement))throw new ReferenceError;i.addEventListener("scroll",function(){for(;n.stack_.length&&i.scrollTop+i.offsetHeight>=i.scrollHeight-16;)n.stack_.splice(0,10).forEach(function(e){return e()})})};setTimeout(function(){return"function"==typeof n.data_?n.data_().then(p):p(n.data_)},250)}},t}();t.default=d}).call(t,n(0))},function(e,t,n){"use strict";var r=/[|\\{}()[\]^$+*?.]/g;e.exports=function(e){if("string"!=typeof e)throw new TypeError("Expected a string");return e.replace(r,"\\$&")}},function(e,t,n){(function(t){e.exports=t.lunr=n(42)}).call(t,n(1))},function(e,t,n){var r,i;!function(){var o=function(e){var t=new o.Builder;return t.pipeline.add(o.trimmer,o.stopWordFilter,o.stemmer),t.searchPipeline.add(o.stemmer),e.call(t,t),t.build()};o.version="2.1.5",o.utils={},o.utils.warn=function(e){return function(t){e.console&&console.warn&&console.warn(t)}}(this),o.utils.asString=function(e){return void 0===e||null===e?"":e.toString()},o.FieldRef=function(e,t,n){this.docRef=e,this.fieldName=t,this._stringValue=n},o.FieldRef.joiner="/",o.FieldRef.fromString=function(e){var t=e.indexOf(o.FieldRef.joiner);if(-1===t)throw"malformed field ref string";var n=e.slice(0,t),r=e.slice(t+1);return new o.FieldRef(r,n,e)},o.FieldRef.prototype.toString=function(){return void 0==this._stringValue&&(this._stringValue=this.fieldName+o.FieldRef.joiner+this.docRef),this._stringValue},o.idf=function(e,t){var n=0;for(var r in e)"_index"!=r&&(n+=Object.keys(e[r]).length);var i=(t-n+.5)/(n+.5);return Math.log(1+Math.abs(i))},o.Token=function(e,t){this.str=e||"",this.metadata=t||{}},o.Token.prototype.toString=function(){return this.str},o.Token.prototype.update=function(e){return this.str=e(this.str,this.metadata),this},o.Token.prototype.clone=function(e){return e=e||function(e){return e},new o.Token(e(this.str,this.metadata),this.metadata)},o.tokenizer=function(e){if(null==e||void 0==e)return[];if(Array.isArray(e))return e.map(function(e){return new o.Token(o.utils.asString(e).toLowerCase())});for(var t=e.toString().trim().toLowerCase(),n=t.length,r=[],i=0,a=0;i<=n;i++){var s=t.charAt(i),c=i-a;(s.match(o.tokenizer.separator)||i==n)&&(c>0&&r.push(new o.Token(t.slice(a,i),{position:[a,c],index:r.length})),a=i+1)}return r},o.tokenizer.separator=/[\s\-]+/,o.Pipeline=function(){this._stack=[]},o.Pipeline.registeredFunctions=Object.create(null),o.Pipeline.registerFunction=function(e,t){t in this.registeredFunctions&&o.utils.warn("Overwriting existing registered function: "+t),e.label=t,o.Pipeline.registeredFunctions[e.label]=e},o.Pipeline.warnIfFunctionNotRegistered=function(e){e.label&&e.label in this.registeredFunctions||o.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",e)},o.Pipeline.load=function(e){var t=new o.Pipeline;return e.forEach(function(e){var n=o.Pipeline.registeredFunctions[e];if(!n)throw new Error("Cannot load unregistered function: "+e);t.add(n)}),t},o.Pipeline.prototype.add=function(){Array.prototype.slice.call(arguments).forEach(function(e){o.Pipeline.warnIfFunctionNotRegistered(e),this._stack.push(e)},this)},o.Pipeline.prototype.after=function(e,t){o.Pipeline.warnIfFunctionNotRegistered(t);var n=this._stack.indexOf(e);if(-1==n)throw new Error("Cannot find existingFn");n+=1,this._stack.splice(n,0,t)},o.Pipeline.prototype.before=function(e,t){o.Pipeline.warnIfFunctionNotRegistered(t);var n=this._stack.indexOf(e);if(-1==n)throw new Error("Cannot find existingFn");this._stack.splice(n,0,t)},o.Pipeline.prototype.remove=function(e){var t=this._stack.indexOf(e);-1!=t&&this._stack.splice(t,1)},o.Pipeline.prototype.run=function(e){for(var t=this._stack.length,n=0;n1&&(oe&&(n=i),o!=e);)r=n-t,i=t+Math.floor(r/2),o=this.elements[2*i];return o==e?2*i:o>e?2*i:os?u+=2:a==s&&(t+=n[c+1]*r[u+1],c+=2,u+=2);return t},o.Vector.prototype.similarity=function(e){return this.dot(e)/(this.magnitude()*e.magnitude())},o.Vector.prototype.toArray=function(){for(var e=new Array(this.elements.length/2),t=1,n=0;t0){var a,s=i.str.charAt(0);s in i.node.edges?a=i.node.edges[s]:(a=new o.TokenSet,i.node.edges[s]=a),1==i.str.length?a.final=!0:r.push({node:a,editsRemaining:i.editsRemaining,str:i.str.slice(1)})}if(i.editsRemaining>0&&i.str.length>1){var c,s=i.str.charAt(1);s in i.node.edges?c=i.node.edges[s]:(c=new o.TokenSet,i.node.edges[s]=c),i.str.length<=2?c.final=!0:r.push({node:c,editsRemaining:i.editsRemaining-1,str:i.str.slice(2)})}if(i.editsRemaining>0&&1==i.str.length&&(i.node.final=!0),i.editsRemaining>0&&i.str.length>=1){if("*"in i.node.edges)var u=i.node.edges["*"];else{var u=new o.TokenSet;i.node.edges["*"]=u}1==i.str.length?u.final=!0:r.push({node:u,editsRemaining:i.editsRemaining-1,str:i.str.slice(1)})}if(i.editsRemaining>0){if("*"in i.node.edges)var l=i.node.edges["*"];else{var l=new o.TokenSet;i.node.edges["*"]=l}0==i.str.length?l.final=!0:r.push({node:l,editsRemaining:i.editsRemaining-1,str:i.str})}if(i.editsRemaining>0&&i.str.length>1){var d,f=i.str.charAt(0),h=i.str.charAt(1);h in i.node.edges?d=i.node.edges[h]:(d=new o.TokenSet,i.node.edges[h]=d),1==i.str.length?d.final=!0:r.push({node:d,editsRemaining:i.editsRemaining-1,str:f+i.str.slice(2)})}}return n},o.TokenSet.fromString=function(e){for(var t=new o.TokenSet,n=t,r=!1,i=0,a=e.length;i=e;t--){var n=this.uncheckedNodes[t],r=n.child.toString();r in this.minimizedNodes?n.parent.edges[n.char]=this.minimizedNodes[r]:(n.child._str=r,this.minimizedNodes[r]=n.child),this.uncheckedNodes.pop()}},o.Index=function(e){this.invertedIndex=e.invertedIndex,this.fieldVectors=e.fieldVectors,this.tokenSet=e.tokenSet,this.fields=e.fields,this.pipeline=e.pipeline},o.Index.prototype.search=function(e){return this.query(function(t){new o.QueryParser(e,t).parse()})},o.Index.prototype.query=function(e){var t=new o.Query(this.fields),n=Object.create(null),r=Object.create(null),i=Object.create(null);e.call(t,t);for(var a=0;a1?1:e},o.Builder.prototype.k1=function(e){this._k1=e},o.Builder.prototype.add=function(e){var t=e[this._ref];this.documentCount+=1;for(var n=0;n=this.length)return o.QueryLexer.EOS;var e=this.str.charAt(this.pos);return this.pos+=1,e},o.QueryLexer.prototype.width=function(){return this.pos-this.start},o.QueryLexer.prototype.ignore=function(){this.start==this.pos&&(this.pos+=1),this.start=this.pos},o.QueryLexer.prototype.backup=function(){this.pos-=1},o.QueryLexer.prototype.acceptDigitRun=function(){var e,t;do{e=this.next(),t=e.charCodeAt(0)}while(t>47&&t<58);e!=o.QueryLexer.EOS&&this.backup()},o.QueryLexer.prototype.more=function(){return this.pos1&&(e.backup(),e.emit(o.QueryLexer.TERM)),e.ignore(),e.more())return o.QueryLexer.lexText},o.QueryLexer.lexEditDistance=function(e){return e.ignore(),e.acceptDigitRun(),e.emit(o.QueryLexer.EDIT_DISTANCE),o.QueryLexer.lexText},o.QueryLexer.lexBoost=function(e){return e.ignore(),e.acceptDigitRun(),e.emit(o.QueryLexer.BOOST),o.QueryLexer.lexText},o.QueryLexer.lexEOS=function(e){e.width()>0&&e.emit(o.QueryLexer.TERM)},o.QueryLexer.termSeparator=o.tokenizer.separator,o.QueryLexer.lexText=function(e){for(;;){var t=e.next();if(t==o.QueryLexer.EOS)return o.QueryLexer.lexEOS;if(92!=t.charCodeAt(0)){if(":"==t)return o.QueryLexer.lexField;if("~"==t)return e.backup(),e.width()>0&&e.emit(o.QueryLexer.TERM),o.QueryLexer.lexEditDistance;if("^"==t)return e.backup(),e.width()>0&&e.emit(o.QueryLexer.TERM),o.QueryLexer.lexBoost;if(t.match(o.QueryLexer.termSeparator))return o.QueryLexer.lexTerm}else e.escapeCharacter()}},o.QueryParser=function(e,t){this.lexer=new o.QueryLexer(e),this.query=t,this.currentClause={},this.lexemeIdx=0},o.QueryParser.prototype.parse=function(){this.lexer.run(),this.lexemes=this.lexer.lexemes;for(var e=o.QueryParser.parseFieldOrTerm;e;)e=e(this);return this.query},o.QueryParser.prototype.peekLexeme=function(){return this.lexemes[this.lexemeIdx]},o.QueryParser.prototype.consumeLexeme=function(){var e=this.peekLexeme();return this.lexemeIdx+=1,e},o.QueryParser.prototype.nextClause=function(){var e=this.currentClause;this.query.clause(e),this.currentClause={}},o.QueryParser.parseFieldOrTerm=function(e){var t=e.peekLexeme();if(void 0!=t)switch(t.type){case o.QueryLexer.FIELD:return o.QueryParser.parseField;case o.QueryLexer.TERM:return o.QueryParser.parseTerm;default:var n="expected either a field or a term, found "+t.type;throw t.str.length>=1&&(n+=" with value '"+t.str+"'"),new o.QueryParseError(n,t.start,t.end)}},o.QueryParser.parseField=function(e){var t=e.consumeLexeme();if(void 0!=t){if(-1==e.query.allFields.indexOf(t.str)){var n=e.query.allFields.map(function(e){return"'"+e+"'"}).join(", "),r="unrecognised field '"+t.str+"', possible fields: "+n;throw new o.QueryParseError(r,t.start,t.end)}e.currentClause.fields=[t.str];var i=e.peekLexeme();if(void 0==i){var r="expecting term, found nothing";throw new o.QueryParseError(r,t.start,t.end)}switch(i.type){case o.QueryLexer.TERM:return o.QueryParser.parseTerm;default:var r="expecting term, found '"+i.type+"'";throw new o.QueryParseError(r,i.start,i.end)}}},o.QueryParser.parseTerm=function(e){var t=e.consumeLexeme();if(void 0!=t){e.currentClause.term=t.str.toLowerCase(),-1!=t.str.indexOf("*")&&(e.currentClause.usePipeline=!1);var n=e.peekLexeme();if(void 0==n)return void e.nextClause();switch(n.type){case o.QueryLexer.TERM:return e.nextClause(),o.QueryParser.parseTerm;case o.QueryLexer.FIELD:return e.nextClause(),o.QueryParser.parseField;case o.QueryLexer.EDIT_DISTANCE:return o.QueryParser.parseEditDistance;case o.QueryLexer.BOOST:return o.QueryParser.parseBoost;default:var r="Unexpected lexeme type '"+n.type+"'";throw new o.QueryParseError(r,n.start,n.end)}}},o.QueryParser.parseEditDistance=function(e){var t=e.consumeLexeme();if(void 0!=t){var n=parseInt(t.str,10);if(isNaN(n)){var r="edit distance must be numeric";throw new o.QueryParseError(r,t.start,t.end)}e.currentClause.editDistance=n;var i=e.peekLexeme();if(void 0==i)return void e.nextClause();switch(i.type){case o.QueryLexer.TERM:return e.nextClause(),o.QueryParser.parseTerm;case o.QueryLexer.FIELD:return e.nextClause(),o.QueryParser.parseField;case o.QueryLexer.EDIT_DISTANCE:return o.QueryParser.parseEditDistance;case o.QueryLexer.BOOST:return o.QueryParser.parseBoost;default:var r="Unexpected lexeme type '"+i.type+"'";throw new o.QueryParseError(r,i.start,i.end)}}},o.QueryParser.parseBoost=function(e){var t=e.consumeLexeme();if(void 0!=t){var n=parseInt(t.str,10);if(isNaN(n)){var r="boost must be numeric";throw new o.QueryParseError(r,t.start,t.end)}e.currentClause.boost=n;var i=e.peekLexeme();if(void 0==i)return void e.nextClause();switch(i.type){case o.QueryLexer.TERM:return e.nextClause(),o.QueryParser.parseTerm;case o.QueryLexer.FIELD:return e.nextClause(),o.QueryParser.parseField;case o.QueryLexer.EDIT_DISTANCE:return o.QueryParser.parseEditDistance;case o.QueryLexer.BOOST:return o.QueryParser.parseBoost;default:var r="Unexpected lexeme type '"+i.type+"'";throw new o.QueryParseError(r,i.start,i.end)}}},function(o,a){r=a,void 0!==(i="function"==typeof r?r.call(t,n,t,e):r)&&(e.exports=i)}(0,function(){return o})}()},function(e,t,n){"use strict";t.__esModule=!0;var r=n(44),i=function(e){return e&&e.__esModule?e:{default:e}}(r);t.default={Position:i.default}},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}t.__esModule=!0;var i=function(){function e(t,n){r(this,e);var i="string"==typeof t?document.querySelector(t):t;if(!(i instanceof HTMLElement&&i.parentNode instanceof HTMLElement))throw new ReferenceError;if(this.el_=i,this.parent_=i.parentNode,!((i="string"==typeof n?document.querySelector(n):n)instanceof HTMLElement))throw new ReferenceError;this.header_=i,this.height_=0,this.pad_="fixed"===window.getComputedStyle(this.header_).position}return e.prototype.setup=function(){var e=Array.prototype.reduce.call(this.parent_.children,function(e,t){return Math.max(e,t.offsetTop)},0);this.offset_=e-(this.pad_?this.header_.offsetHeight:0),this.update()},e.prototype.update=function(e){var t=window.pageYOffset,n=window.innerHeight;e&&"resize"===e.type&&this.setup();var r={top:this.pad_?this.header_.offsetHeight:0,bottom:this.parent_.offsetTop+this.parent_.offsetHeight},i=n-r.top-Math.max(0,this.offset_-t)-Math.max(0,t+n-r.bottom);i!==this.height_&&(this.el_.style.height=(this.height_=i)+"px"),t>=this.offset_?"lock"!==this.el_.dataset.mdState&&(this.el_.dataset.mdState="lock"):"lock"===this.el_.dataset.mdState&&(this.el_.dataset.mdState="")},e.prototype.reset=function(){this.el_.dataset.mdState="",this.el_.style.height="",this.height_=0},e}();t.default=i},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}t.__esModule=!0;var i=n(46),o=r(i),a=n(50),s=r(a);t.default={Adapter:o.default,Repository:s.default}},function(e,t,n){"use strict";t.__esModule=!0;var r=n(47),i=function(e){return e&&e.__esModule?e:{default:e}}(r);t.default={GitHub:i.default}},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function i(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function o(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}t.__esModule=!0;var a=n(48),s=function(e){return e&&e.__esModule?e:{default:e}}(a),c=function(e){function t(n){r(this,t);var o=i(this,e.call(this,n)),a=/^.+github\.com\/([^\/]+)\/?([^\/]+)?.*$/.exec(o.base_);if(a&&3===a.length){var s=a[1],c=a[2];o.base_="https://api.github.com/users/"+s+"/repos",o.name_=c}return o}return o(t,e),t.prototype.fetch_=function(){var e=this;return function t(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0;return fetch(e.base_+"?per_page=30&page="+n).then(function(e){return e.json()}).then(function(r){if(!(r instanceof Array))throw new TypeError;if(e.name_){var i=r.find(function(t){return t.name===e.name_});return i||30!==r.length?i?[e.format_(i.stargazers_count)+" Stars",e.format_(i.forks_count)+" Forks"]:[]:t(n+1)}return[r.length+" Repositories"]})}()},t}(s.default);t.default=c},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}t.__esModule=!0;var i=n(49),o=function(e){return e&&e.__esModule?e:{default:e}}(i),a=function(){function e(t){r(this,e);var n="string"==typeof t?document.querySelector(t):t;if(!(n instanceof HTMLAnchorElement))throw new ReferenceError;this.el_=n,this.base_=this.el_.href,this.salt_=this.hash_(this.base_)}return e.prototype.fetch=function(){var e=this;return new Promise(function(t){var n=o.default.getJSON(e.salt_+".cache-source");void 0!==n?t(n):e.fetch_().then(function(n){o.default.set(e.salt_+".cache-source",n,{expires:1/96}),t(n)})})},e.prototype.fetch_=function(){throw new Error("fetch_(): Not implemented")},e.prototype.format_=function(e){return e>1e4?(e/1e3).toFixed(0)+"k":e>1e3?(e/1e3).toFixed(1)+"k":""+e},e.prototype.hash_=function(e){var t=0;if(0===e.length)return t;for(var n=0,r=e.length;n1){if(o=e({path:"/"},r.defaults,o),"number"==typeof o.expires){var s=new Date;s.setMilliseconds(s.getMilliseconds()+864e5*o.expires),o.expires=s}o.expires=o.expires?o.expires.toUTCString():"";try{a=JSON.stringify(i),/^[\{\[]/.test(a)&&(i=a)}catch(e){}i=n.write?n.write(i,t):encodeURIComponent(String(i)).replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g,decodeURIComponent),t=encodeURIComponent(String(t)),t=t.replace(/%(23|24|26|2B|5E|60|7C)/g,decodeURIComponent),t=t.replace(/[\(\)]/g,escape);var c="";for(var u in o)o[u]&&(c+="; "+u,!0!==o[u]&&(c+="="+o[u]));return document.cookie=t+"="+i+c}t||(a={});for(var l=document.cookie?document.cookie.split("; "):[],d=/(%[0-9A-Z]{2})+/g,f=0;f=this.el_.children[0].offsetTop+-43;e!==this.active_&&(this.el_.dataset.mdState=(this.active_=e)?"hidden":"")},e.prototype.reset=function(){this.el_.dataset.mdState="",this.active_=!1},e}();t.default=i}])); \ No newline at end of file diff --git a/3.0/site/assets/javascripts/lunr/lunr.da.js b/3.0/site/assets/javascripts/lunr/lunr.da.js new file mode 100644 index 00000000..3b07b2c1 --- /dev/null +++ b/3.0/site/assets/javascripts/lunr/lunr.da.js @@ -0,0 +1 @@ +!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var r,i,n;e.da=function(){this.pipeline.reset(),this.pipeline.add(e.da.trimmer,e.da.stopWordFilter,e.da.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.da.stemmer))},e.da.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.da.trimmer=e.trimmerSupport.generateTrimmer(e.da.wordCharacters),e.Pipeline.registerFunction(e.da.trimmer,"trimmer-da"),e.da.stemmer=(r=e.stemmerSupport.Among,i=e.stemmerSupport.SnowballProgram,n=new function(){var e,n,t,s=[new r("hed",-1,1),new r("ethed",0,1),new r("ered",-1,1),new r("e",-1,1),new r("erede",3,1),new r("ende",3,1),new r("erende",5,1),new r("ene",3,1),new r("erne",3,1),new r("ere",3,1),new r("en",-1,1),new r("heden",10,1),new r("eren",10,1),new r("er",-1,1),new r("heder",13,1),new r("erer",13,1),new r("s",-1,2),new r("heds",16,1),new r("es",16,1),new r("endes",18,1),new r("erendes",19,1),new r("enes",18,1),new r("ernes",18,1),new r("eres",18,1),new r("ens",16,1),new r("hedens",24,1),new r("erens",24,1),new r("ers",16,1),new r("ets",16,1),new r("erets",28,1),new r("et",-1,1),new r("eret",30,1)],o=[new r("gd",-1,-1),new r("dt",-1,-1),new r("gt",-1,-1),new r("kt",-1,-1)],a=[new r("ig",-1,1),new r("lig",0,1),new r("elig",1,1),new r("els",-1,1),new r("løst",-1,2)],d=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,48,0,128],u=[239,254,42,3,0,0,0,0,0,0,0,0,0,0,0,0,16],c=new i;function l(){var e,r=c.limit-c.cursor;c.cursor>=n&&(e=c.limit_backward,c.limit_backward=n,c.ket=c.cursor,c.find_among_b(o,4)?(c.bra=c.cursor,c.limit_backward=e,c.cursor=c.limit-r,c.cursor>c.limit_backward&&(c.cursor--,c.bra=c.cursor,c.slice_del())):c.limit_backward=e)}this.setCurrent=function(e){c.setCurrent(e)},this.getCurrent=function(){return c.getCurrent()},this.stem=function(){var r,i=c.cursor;return function(){var r,i=c.cursor+3;if(n=c.limit,0<=i&&i<=c.limit){for(e=i;;){if(r=c.cursor,c.in_grouping(d,97,248)){c.cursor=r;break}if(c.cursor=r,r>=c.limit)return;c.cursor++}for(;!c.out_grouping(d,97,248);){if(c.cursor>=c.limit)return;c.cursor++}(n=c.cursor)=n&&(r=c.limit_backward,c.limit_backward=n,c.ket=c.cursor,e=c.find_among_b(s,32),c.limit_backward=r,e))switch(c.bra=c.cursor,e){case 1:c.slice_del();break;case 2:c.in_grouping_b(u,97,229)&&c.slice_del()}}(),c.cursor=c.limit,l(),c.cursor=c.limit,function(){var e,r,i,t=c.limit-c.cursor;if(c.ket=c.cursor,c.eq_s_b(2,"st")&&(c.bra=c.cursor,c.eq_s_b(2,"ig")&&c.slice_del()),c.cursor=c.limit-t,c.cursor>=n&&(r=c.limit_backward,c.limit_backward=n,c.ket=c.cursor,e=c.find_among_b(a,5),c.limit_backward=r,e))switch(c.bra=c.cursor,e){case 1:c.slice_del(),i=c.limit-c.cursor,l(),c.cursor=c.limit-i;break;case 2:c.slice_from("løs")}}(),c.cursor=c.limit,c.cursor>=n&&(r=c.limit_backward,c.limit_backward=n,c.ket=c.cursor,c.out_grouping_b(d,97,248)?(c.bra=c.cursor,t=c.slice_to(t),c.limit_backward=r,c.eq_v_b(t)&&c.slice_del()):c.limit_backward=r),!0}},function(e){return"function"==typeof e.update?e.update(function(e){return n.setCurrent(e),n.stem(),n.getCurrent()}):(n.setCurrent(e),n.stem(),n.getCurrent())}),e.Pipeline.registerFunction(e.da.stemmer,"stemmer-da"),e.da.stopWordFilter=e.generateStopWordFilter("ad af alle alt anden at blev blive bliver da de dem den denne der deres det dette dig din disse dog du efter eller en end er et for fra ham han hans har havde have hende hendes her hos hun hvad hvis hvor i ikke ind jeg jer jo kunne man mange med meget men mig min mine mit mod ned noget nogle nu når og også om op os over på selv sig sin sine sit skal skulle som sådan thi til ud under var vi vil ville vor være været".split(" ")),e.Pipeline.registerFunction(e.da.stopWordFilter,"stopWordFilter-da")}}); \ No newline at end of file diff --git a/3.0/site/assets/javascripts/lunr/lunr.de.js b/3.0/site/assets/javascripts/lunr/lunr.de.js new file mode 100644 index 00000000..ebd78f28 --- /dev/null +++ b/3.0/site/assets/javascripts/lunr/lunr.de.js @@ -0,0 +1 @@ +!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var r,n,i;e.de=function(){this.pipeline.reset(),this.pipeline.add(e.de.trimmer,e.de.stopWordFilter,e.de.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.de.stemmer))},e.de.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.de.trimmer=e.trimmerSupport.generateTrimmer(e.de.wordCharacters),e.Pipeline.registerFunction(e.de.trimmer,"trimmer-de"),e.de.stemmer=(r=e.stemmerSupport.Among,n=e.stemmerSupport.SnowballProgram,i=new function(){var e,i,s,t=[new r("",-1,6),new r("U",0,2),new r("Y",0,1),new r("ä",0,3),new r("ö",0,4),new r("ü",0,5)],o=[new r("e",-1,2),new r("em",-1,1),new r("en",-1,2),new r("ern",-1,1),new r("er",-1,1),new r("s",-1,3),new r("es",5,2)],c=[new r("en",-1,1),new r("er",-1,1),new r("st",-1,2),new r("est",2,1)],u=[new r("ig",-1,1),new r("lich",-1,1)],a=[new r("end",-1,1),new r("ig",-1,2),new r("ung",-1,1),new r("lich",-1,3),new r("isch",-1,2),new r("ik",-1,2),new r("heit",-1,3),new r("keit",-1,4)],d=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,8,0,32,8],l=[117,30,5],m=[117,30,4],h=new n;function w(e,r,n){return!(!h.eq_s(1,e)||(h.ket=h.cursor,!h.in_grouping(d,97,252)))&&(h.slice_from(r),h.cursor=n,!0)}function f(){for(;!h.in_grouping(d,97,252);){if(h.cursor>=h.limit)return!0;h.cursor++}for(;!h.out_grouping(d,97,252);){if(h.cursor>=h.limit)return!0;h.cursor++}return!1}function b(){return s<=h.cursor}function _(){return i<=h.cursor}this.setCurrent=function(e){h.setCurrent(e)},this.getCurrent=function(){return h.getCurrent()},this.stem=function(){var r=h.cursor;return function(){for(var e,r,n,i,s=h.cursor;;)if(e=h.cursor,h.bra=e,h.eq_s(1,"ß"))h.ket=h.cursor,h.slice_from("ss");else{if(e>=h.limit)break;h.cursor=e+1}for(h.cursor=s;;)for(r=h.cursor;;){if(n=h.cursor,h.in_grouping(d,97,252)){if(i=h.cursor,h.bra=i,w("u","U",n))break;if(h.cursor=i,w("y","Y",n))break}if(n>=h.limit)return void(h.cursor=r);h.cursor=n+1}}(),h.cursor=r,function(){s=h.limit,i=s;var r=h.cursor+3;0<=r&&r<=h.limit&&(e=r,f()||((s=h.cursor)=h.limit)return;h.cursor++}}}(),!0}},function(e){return"function"==typeof e.update?e.update(function(e){return i.setCurrent(e),i.stem(),i.getCurrent()}):(i.setCurrent(e),i.stem(),i.getCurrent())}),e.Pipeline.registerFunction(e.de.stemmer,"stemmer-de"),e.de.stopWordFilter=e.generateStopWordFilter("aber alle allem allen aller alles als also am an ander andere anderem anderen anderer anderes anderm andern anderr anders auch auf aus bei bin bis bist da damit dann das dasselbe dazu daß dein deine deinem deinen deiner deines dem demselben den denn denselben der derer derselbe derselben des desselben dessen dich die dies diese dieselbe dieselben diesem diesen dieser dieses dir doch dort du durch ein eine einem einen einer eines einig einige einigem einigen einiger einiges einmal er es etwas euch euer eure eurem euren eurer eures für gegen gewesen hab habe haben hat hatte hatten hier hin hinter ich ihm ihn ihnen ihr ihre ihrem ihren ihrer ihres im in indem ins ist jede jedem jeden jeder jedes jene jenem jenen jener jenes jetzt kann kein keine keinem keinen keiner keines können könnte machen man manche manchem manchen mancher manches mein meine meinem meinen meiner meines mich mir mit muss musste nach nicht nichts noch nun nur ob oder ohne sehr sein seine seinem seinen seiner seines selbst sich sie sind so solche solchem solchen solcher solches soll sollte sondern sonst um und uns unse unsem unsen unser unses unter viel vom von vor war waren warst was weg weil weiter welche welchem welchen welcher welches wenn werde werden wie wieder will wir wird wirst wo wollen wollte während würde würden zu zum zur zwar zwischen über".split(" ")),e.Pipeline.registerFunction(e.de.stopWordFilter,"stopWordFilter-de")}}); \ No newline at end of file diff --git a/3.0/site/assets/javascripts/lunr/lunr.du.js b/3.0/site/assets/javascripts/lunr/lunr.du.js new file mode 100644 index 00000000..375c0e76 --- /dev/null +++ b/3.0/site/assets/javascripts/lunr/lunr.du.js @@ -0,0 +1 @@ +!function(r,e){"function"==typeof define&&define.amd?define(e):"object"==typeof exports?module.exports=e():e()(r.lunr)}(this,function(){return function(r){if(void 0===r)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===r.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var e,i,n;r.du=function(){this.pipeline.reset(),this.pipeline.add(r.du.trimmer,r.du.stopWordFilter,r.du.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(r.du.stemmer))},r.du.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",r.du.trimmer=r.trimmerSupport.generateTrimmer(r.du.wordCharacters),r.Pipeline.registerFunction(r.du.trimmer,"trimmer-du"),r.du.stemmer=(e=r.stemmerSupport.Among,i=r.stemmerSupport.SnowballProgram,n=new function(){var r,n,o,t=[new e("",-1,6),new e("á",0,1),new e("ä",0,1),new e("é",0,2),new e("ë",0,2),new e("í",0,3),new e("ï",0,3),new e("ó",0,4),new e("ö",0,4),new e("ú",0,5),new e("ü",0,5)],s=[new e("",-1,3),new e("I",0,2),new e("Y",0,1)],u=[new e("dd",-1,-1),new e("kk",-1,-1),new e("tt",-1,-1)],c=[new e("ene",-1,2),new e("se",-1,3),new e("en",-1,2),new e("heden",2,1),new e("s",-1,3)],a=[new e("end",-1,1),new e("ig",-1,2),new e("ing",-1,1),new e("lijk",-1,3),new e("baar",-1,4),new e("bar",-1,5)],l=[new e("aa",-1,-1),new e("ee",-1,-1),new e("oo",-1,-1),new e("uu",-1,-1)],m=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,128],d=[1,0,0,17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,128],f=[17,67,16,1,0,0,0,0,0,0,0,0,0,0,0,0,128],_=new i;function w(r){return _.cursor=r,r>=_.limit||(_.cursor++,!1)}function b(){for(;!_.in_grouping(m,97,232);){if(_.cursor>=_.limit)return!0;_.cursor++}for(;!_.out_grouping(m,97,232);){if(_.cursor>=_.limit)return!0;_.cursor++}return!1}function p(){return n<=_.cursor}function g(){return r<=_.cursor}function h(){var r=_.limit-_.cursor;_.find_among_b(u,3)&&(_.cursor=_.limit-r,_.ket=_.cursor,_.cursor>_.limit_backward&&(_.cursor--,_.bra=_.cursor,_.slice_del()))}function k(){var r;o=!1,_.ket=_.cursor,_.eq_s_b(1,"e")&&(_.bra=_.cursor,p()&&(r=_.limit-_.cursor,_.out_grouping_b(m,97,232)&&(_.cursor=_.limit-r,_.slice_del(),o=!0,h())))}function v(){var r;p()&&(r=_.limit-_.cursor,_.out_grouping_b(m,97,232)&&(_.cursor=_.limit-r,_.eq_s_b(3,"gem")||(_.cursor=_.limit-r,_.slice_del(),h())))}this.setCurrent=function(r){_.setCurrent(r)},this.getCurrent=function(){return _.getCurrent()},this.stem=function(){var e=_.cursor;return function(){for(var r,e,i,n=_.cursor;;){if(_.bra=_.cursor,r=_.find_among(t,11))switch(_.ket=_.cursor,r){case 1:_.slice_from("a");continue;case 2:_.slice_from("e");continue;case 3:_.slice_from("i");continue;case 4:_.slice_from("o");continue;case 5:_.slice_from("u");continue;case 6:if(_.cursor>=_.limit)break;_.cursor++;continue}break}for(_.cursor=n,_.bra=n,_.eq_s(1,"y")?(_.ket=_.cursor,_.slice_from("Y")):_.cursor=n;;)if(e=_.cursor,_.in_grouping(m,97,232)){if(i=_.cursor,_.bra=i,_.eq_s(1,"i"))_.ket=_.cursor,_.in_grouping(m,97,232)&&(_.slice_from("I"),_.cursor=e);else if(_.cursor=i,_.eq_s(1,"y"))_.ket=_.cursor,_.slice_from("Y"),_.cursor=e;else if(w(e))break}else if(w(e))break}(),_.cursor=e,n=_.limit,r=n,b()||((n=_.cursor)<3&&(n=3),b()||(r=_.cursor)),_.limit_backward=e,_.cursor=_.limit,function(){var r,e,i,n,t,s,u=_.limit-_.cursor;if(_.ket=_.cursor,r=_.find_among_b(c,5))switch(_.bra=_.cursor,r){case 1:p()&&_.slice_from("heid");break;case 2:v();break;case 3:p()&&_.out_grouping_b(f,97,232)&&_.slice_del()}if(_.cursor=_.limit-u,k(),_.cursor=_.limit-u,_.ket=_.cursor,_.eq_s_b(4,"heid")&&(_.bra=_.cursor,g()&&(e=_.limit-_.cursor,_.eq_s_b(1,"c")||(_.cursor=_.limit-e,_.slice_del(),_.ket=_.cursor,_.eq_s_b(2,"en")&&(_.bra=_.cursor,v())))),_.cursor=_.limit-u,_.ket=_.cursor,r=_.find_among_b(a,6))switch(_.bra=_.cursor,r){case 1:if(g()){if(_.slice_del(),i=_.limit-_.cursor,_.ket=_.cursor,_.eq_s_b(2,"ig")&&(_.bra=_.cursor,g()&&(n=_.limit-_.cursor,!_.eq_s_b(1,"e")))){_.cursor=_.limit-n,_.slice_del();break}_.cursor=_.limit-i,h()}break;case 2:g()&&(t=_.limit-_.cursor,_.eq_s_b(1,"e")||(_.cursor=_.limit-t,_.slice_del()));break;case 3:g()&&(_.slice_del(),k());break;case 4:g()&&_.slice_del();break;case 5:g()&&o&&_.slice_del()}_.cursor=_.limit-u,_.out_grouping_b(d,73,232)&&(s=_.limit-_.cursor,_.find_among_b(l,4)&&_.out_grouping_b(m,97,232)&&(_.cursor=_.limit-s,_.ket=_.cursor,_.cursor>_.limit_backward&&(_.cursor--,_.bra=_.cursor,_.slice_del())))}(),_.cursor=_.limit_backward,function(){for(var r;;)if(_.bra=_.cursor,r=_.find_among(s,3))switch(_.ket=_.cursor,r){case 1:_.slice_from("y");break;case 2:_.slice_from("i");break;case 3:if(_.cursor>=_.limit)return;_.cursor++}}(),!0}},function(r){return"function"==typeof r.update?r.update(function(r){return n.setCurrent(r),n.stem(),n.getCurrent()}):(n.setCurrent(r),n.stem(),n.getCurrent())}),r.Pipeline.registerFunction(r.du.stemmer,"stemmer-du"),r.du.stopWordFilter=r.generateStopWordFilter(" aan al alles als altijd andere ben bij daar dan dat de der deze die dit doch doen door dus een eens en er ge geen geweest haar had heb hebben heeft hem het hier hij hoe hun iemand iets ik in is ja je kan kon kunnen maar me meer men met mij mijn moet na naar niet niets nog nu of om omdat onder ons ook op over reeds te tegen toch toen tot u uit uw van veel voor want waren was wat werd wezen wie wil worden wordt zal ze zelf zich zij zijn zo zonder zou".split(" ")),r.Pipeline.registerFunction(r.du.stopWordFilter,"stopWordFilter-du")}}); \ No newline at end of file diff --git a/3.0/site/assets/javascripts/lunr/lunr.es.js b/3.0/site/assets/javascripts/lunr/lunr.es.js new file mode 100644 index 00000000..4cb634f0 --- /dev/null +++ b/3.0/site/assets/javascripts/lunr/lunr.es.js @@ -0,0 +1 @@ +!function(e,s){"function"==typeof define&&define.amd?define(s):"object"==typeof exports?module.exports=s():s()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var s,r,n;e.es=function(){this.pipeline.reset(),this.pipeline.add(e.es.trimmer,e.es.stopWordFilter,e.es.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.es.stemmer))},e.es.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.es.trimmer=e.trimmerSupport.generateTrimmer(e.es.wordCharacters),e.Pipeline.registerFunction(e.es.trimmer,"trimmer-es"),e.es.stemmer=(s=e.stemmerSupport.Among,r=e.stemmerSupport.SnowballProgram,n=new function(){var e,n,i,a=[new s("",-1,6),new s("á",0,1),new s("é",0,2),new s("í",0,3),new s("ó",0,4),new s("ú",0,5)],t=[new s("la",-1,-1),new s("sela",0,-1),new s("le",-1,-1),new s("me",-1,-1),new s("se",-1,-1),new s("lo",-1,-1),new s("selo",5,-1),new s("las",-1,-1),new s("selas",7,-1),new s("les",-1,-1),new s("los",-1,-1),new s("selos",10,-1),new s("nos",-1,-1)],o=[new s("ando",-1,6),new s("iendo",-1,6),new s("yendo",-1,7),new s("ándo",-1,2),new s("iéndo",-1,1),new s("ar",-1,6),new s("er",-1,6),new s("ir",-1,6),new s("ár",-1,3),new s("ér",-1,4),new s("ír",-1,5)],u=[new s("ic",-1,-1),new s("ad",-1,-1),new s("os",-1,-1),new s("iv",-1,1)],w=[new s("able",-1,1),new s("ible",-1,1),new s("ante",-1,1)],c=[new s("ic",-1,1),new s("abil",-1,1),new s("iv",-1,1)],m=[new s("ica",-1,1),new s("ancia",-1,2),new s("encia",-1,5),new s("adora",-1,2),new s("osa",-1,1),new s("ista",-1,1),new s("iva",-1,9),new s("anza",-1,1),new s("logía",-1,3),new s("idad",-1,8),new s("able",-1,1),new s("ible",-1,1),new s("ante",-1,2),new s("mente",-1,7),new s("amente",13,6),new s("ación",-1,2),new s("ución",-1,4),new s("ico",-1,1),new s("ismo",-1,1),new s("oso",-1,1),new s("amiento",-1,1),new s("imiento",-1,1),new s("ivo",-1,9),new s("ador",-1,2),new s("icas",-1,1),new s("ancias",-1,2),new s("encias",-1,5),new s("adoras",-1,2),new s("osas",-1,1),new s("istas",-1,1),new s("ivas",-1,9),new s("anzas",-1,1),new s("logías",-1,3),new s("idades",-1,8),new s("ables",-1,1),new s("ibles",-1,1),new s("aciones",-1,2),new s("uciones",-1,4),new s("adores",-1,2),new s("antes",-1,2),new s("icos",-1,1),new s("ismos",-1,1),new s("osos",-1,1),new s("amientos",-1,1),new s("imientos",-1,1),new s("ivos",-1,9)],l=[new s("ya",-1,1),new s("ye",-1,1),new s("yan",-1,1),new s("yen",-1,1),new s("yeron",-1,1),new s("yendo",-1,1),new s("yo",-1,1),new s("yas",-1,1),new s("yes",-1,1),new s("yais",-1,1),new s("yamos",-1,1),new s("yó",-1,1)],d=[new s("aba",-1,2),new s("ada",-1,2),new s("ida",-1,2),new s("ara",-1,2),new s("iera",-1,2),new s("ía",-1,2),new s("aría",5,2),new s("ería",5,2),new s("iría",5,2),new s("ad",-1,2),new s("ed",-1,2),new s("id",-1,2),new s("ase",-1,2),new s("iese",-1,2),new s("aste",-1,2),new s("iste",-1,2),new s("an",-1,2),new s("aban",16,2),new s("aran",16,2),new s("ieran",16,2),new s("ían",16,2),new s("arían",20,2),new s("erían",20,2),new s("irían",20,2),new s("en",-1,1),new s("asen",24,2),new s("iesen",24,2),new s("aron",-1,2),new s("ieron",-1,2),new s("arán",-1,2),new s("erán",-1,2),new s("irán",-1,2),new s("ado",-1,2),new s("ido",-1,2),new s("ando",-1,2),new s("iendo",-1,2),new s("ar",-1,2),new s("er",-1,2),new s("ir",-1,2),new s("as",-1,2),new s("abas",39,2),new s("adas",39,2),new s("idas",39,2),new s("aras",39,2),new s("ieras",39,2),new s("ías",39,2),new s("arías",45,2),new s("erías",45,2),new s("irías",45,2),new s("es",-1,1),new s("ases",49,2),new s("ieses",49,2),new s("abais",-1,2),new s("arais",-1,2),new s("ierais",-1,2),new s("íais",-1,2),new s("aríais",55,2),new s("eríais",55,2),new s("iríais",55,2),new s("aseis",-1,2),new s("ieseis",-1,2),new s("asteis",-1,2),new s("isteis",-1,2),new s("áis",-1,2),new s("éis",-1,1),new s("aréis",64,2),new s("eréis",64,2),new s("iréis",64,2),new s("ados",-1,2),new s("idos",-1,2),new s("amos",-1,2),new s("ábamos",70,2),new s("áramos",70,2),new s("iéramos",70,2),new s("íamos",70,2),new s("aríamos",74,2),new s("eríamos",74,2),new s("iríamos",74,2),new s("emos",-1,1),new s("aremos",78,2),new s("eremos",78,2),new s("iremos",78,2),new s("ásemos",78,2),new s("iésemos",78,2),new s("imos",-1,2),new s("arás",-1,2),new s("erás",-1,2),new s("irás",-1,2),new s("ís",-1,2),new s("ará",-1,2),new s("erá",-1,2),new s("irá",-1,2),new s("aré",-1,2),new s("eré",-1,2),new s("iré",-1,2),new s("ió",-1,2)],b=[new s("a",-1,1),new s("e",-1,2),new s("o",-1,1),new s("os",-1,1),new s("á",-1,1),new s("é",-1,2),new s("í",-1,1),new s("ó",-1,1)],f=[17,65,16,0,0,0,0,0,0,0,0,0,0,0,0,0,1,17,4,10],_=new r;function h(){if(_.out_grouping(f,97,252)){for(;!_.in_grouping(f,97,252);){if(_.cursor>=_.limit)return!0;_.cursor++}return!1}return!0}function v(){var e,s=_.cursor;if(function(){if(_.in_grouping(f,97,252)){var e=_.cursor;if(h()){if(_.cursor=e,!_.in_grouping(f,97,252))return!0;for(;!_.out_grouping(f,97,252);){if(_.cursor>=_.limit)return!0;_.cursor++}}return!1}return!0}()){if(_.cursor=s,!_.out_grouping(f,97,252))return;if(e=_.cursor,h()){if(_.cursor=e,!_.in_grouping(f,97,252)||_.cursor>=_.limit)return;_.cursor++}}i=_.cursor}function p(){for(;!_.in_grouping(f,97,252);){if(_.cursor>=_.limit)return!1;_.cursor++}for(;!_.out_grouping(f,97,252);){if(_.cursor>=_.limit)return!1;_.cursor++}return!0}function g(){return i<=_.cursor}function k(){return e<=_.cursor}function y(e,s){if(!k())return!0;_.slice_del(),_.ket=_.cursor;var r=_.find_among_b(e,s);return r&&(_.bra=_.cursor,1==r&&k()&&_.slice_del()),!1}function q(e){return!k()||(_.slice_del(),_.ket=_.cursor,_.eq_s_b(2,e)&&(_.bra=_.cursor,k()&&_.slice_del()),!1)}function C(){var e;if(_.ket=_.cursor,e=_.find_among_b(m,46)){switch(_.bra=_.cursor,e){case 1:if(!k())return!1;_.slice_del();break;case 2:if(q("ic"))return!1;break;case 3:if(!k())return!1;_.slice_from("log");break;case 4:if(!k())return!1;_.slice_from("u");break;case 5:if(!k())return!1;_.slice_from("ente");break;case 6:if(!(n<=_.cursor))return!1;_.slice_del(),_.ket=_.cursor,(e=_.find_among_b(u,4))&&(_.bra=_.cursor,k()&&(_.slice_del(),1==e&&(_.ket=_.cursor,_.eq_s_b(2,"at")&&(_.bra=_.cursor,k()&&_.slice_del()))));break;case 7:if(y(w,3))return!1;break;case 8:if(y(c,3))return!1;break;case 9:if(q("at"))return!1}return!0}return!1}this.setCurrent=function(e){_.setCurrent(e)},this.getCurrent=function(){return _.getCurrent()},this.stem=function(){var s,r=_.cursor;return s=_.cursor,i=_.limit,n=i,e=i,v(),_.cursor=s,p()&&(n=_.cursor,p()&&(e=_.cursor)),_.limit_backward=r,_.cursor=_.limit,function(){var e;if(_.ket=_.cursor,_.find_among_b(t,13)&&(_.bra=_.cursor,(e=_.find_among_b(o,11))&&g()))switch(e){case 1:_.bra=_.cursor,_.slice_from("iendo");break;case 2:_.bra=_.cursor,_.slice_from("ando");break;case 3:_.bra=_.cursor,_.slice_from("ar");break;case 4:_.bra=_.cursor,_.slice_from("er");break;case 5:_.bra=_.cursor,_.slice_from("ir");break;case 6:_.slice_del();break;case 7:_.eq_s_b(1,"u")&&_.slice_del()}}(),_.cursor=_.limit,C()||(_.cursor=_.limit,function(){var e,s;if(_.cursor>=i&&(s=_.limit_backward,_.limit_backward=i,_.ket=_.cursor,e=_.find_among_b(l,12),_.limit_backward=s,e)){if(_.bra=_.cursor,1==e){if(!_.eq_s_b(1,"u"))return!1;_.slice_del()}return!0}return!1}()||(_.cursor=_.limit,function(){var e,s,r,n;if(_.cursor>=i&&(s=_.limit_backward,_.limit_backward=i,_.ket=_.cursor,e=_.find_among_b(d,96),_.limit_backward=s,e))switch(_.bra=_.cursor,e){case 1:r=_.limit-_.cursor,_.eq_s_b(1,"u")?(n=_.limit-_.cursor,_.eq_s_b(1,"g")?_.cursor=_.limit-n:_.cursor=_.limit-r):_.cursor=_.limit-r,_.bra=_.cursor;case 2:_.slice_del()}}())),_.cursor=_.limit,function(){var e,s;if(_.ket=_.cursor,e=_.find_among_b(b,8))switch(_.bra=_.cursor,e){case 1:g()&&_.slice_del();break;case 2:g()&&(_.slice_del(),_.ket=_.cursor,_.eq_s_b(1,"u")&&(_.bra=_.cursor,s=_.limit-_.cursor,_.eq_s_b(1,"g")&&(_.cursor=_.limit-s,g()&&_.slice_del())))}}(),_.cursor=_.limit_backward,function(){for(var e;;){if(_.bra=_.cursor,e=_.find_among(a,6))switch(_.ket=_.cursor,e){case 1:_.slice_from("a");continue;case 2:_.slice_from("e");continue;case 3:_.slice_from("i");continue;case 4:_.slice_from("o");continue;case 5:_.slice_from("u");continue;case 6:if(_.cursor>=_.limit)break;_.cursor++;continue}break}}(),!0}},function(e){return"function"==typeof e.update?e.update(function(e){return n.setCurrent(e),n.stem(),n.getCurrent()}):(n.setCurrent(e),n.stem(),n.getCurrent())}),e.Pipeline.registerFunction(e.es.stemmer,"stemmer-es"),e.es.stopWordFilter=e.generateStopWordFilter("a al algo algunas algunos ante antes como con contra cual cuando de del desde donde durante e el ella ellas ellos en entre era erais eran eras eres es esa esas ese eso esos esta estaba estabais estaban estabas estad estada estadas estado estados estamos estando estar estaremos estará estarán estarás estaré estaréis estaría estaríais estaríamos estarían estarías estas este estemos esto estos estoy estuve estuviera estuvierais estuvieran estuvieras estuvieron estuviese estuvieseis estuviesen estuvieses estuvimos estuviste estuvisteis estuviéramos estuviésemos estuvo está estábamos estáis están estás esté estéis estén estés fue fuera fuerais fueran fueras fueron fuese fueseis fuesen fueses fui fuimos fuiste fuisteis fuéramos fuésemos ha habida habidas habido habidos habiendo habremos habrá habrán habrás habré habréis habría habríais habríamos habrían habrías habéis había habíais habíamos habían habías han has hasta hay haya hayamos hayan hayas hayáis he hemos hube hubiera hubierais hubieran hubieras hubieron hubiese hubieseis hubiesen hubieses hubimos hubiste hubisteis hubiéramos hubiésemos hubo la las le les lo los me mi mis mucho muchos muy más mí mía mías mío míos nada ni no nos nosotras nosotros nuestra nuestras nuestro nuestros o os otra otras otro otros para pero poco por porque que quien quienes qué se sea seamos sean seas seremos será serán serás seré seréis sería seríais seríamos serían serías seáis sido siendo sin sobre sois somos son soy su sus suya suyas suyo suyos sí también tanto te tendremos tendrá tendrán tendrás tendré tendréis tendría tendríais tendríamos tendrían tendrías tened tenemos tenga tengamos tengan tengas tengo tengáis tenida tenidas tenido tenidos teniendo tenéis tenía teníais teníamos tenían tenías ti tiene tienen tienes todo todos tu tus tuve tuviera tuvierais tuvieran tuvieras tuvieron tuviese tuvieseis tuviesen tuvieses tuvimos tuviste tuvisteis tuviéramos tuviésemos tuvo tuya tuyas tuyo tuyos tú un una uno unos vosotras vosotros vuestra vuestras vuestro vuestros y ya yo él éramos".split(" ")),e.Pipeline.registerFunction(e.es.stopWordFilter,"stopWordFilter-es")}}); \ No newline at end of file diff --git a/3.0/site/assets/javascripts/lunr/lunr.fi.js b/3.0/site/assets/javascripts/lunr/lunr.fi.js new file mode 100644 index 00000000..0200b1fc --- /dev/null +++ b/3.0/site/assets/javascripts/lunr/lunr.fi.js @@ -0,0 +1 @@ +!function(i,e){"function"==typeof define&&define.amd?define(e):"object"==typeof exports?module.exports=e():e()(i.lunr)}(this,function(){return function(i){if(void 0===i)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===i.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var e,r,n;i.fi=function(){this.pipeline.reset(),this.pipeline.add(i.fi.trimmer,i.fi.stopWordFilter,i.fi.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(i.fi.stemmer))},i.fi.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",i.fi.trimmer=i.trimmerSupport.generateTrimmer(i.fi.wordCharacters),i.Pipeline.registerFunction(i.fi.trimmer,"trimmer-fi"),i.fi.stemmer=(e=i.stemmerSupport.Among,r=i.stemmerSupport.SnowballProgram,n=new function(){var i,n,t,s,l=[new e("pa",-1,1),new e("sti",-1,2),new e("kaan",-1,1),new e("han",-1,1),new e("kin",-1,1),new e("hän",-1,1),new e("kään",-1,1),new e("ko",-1,1),new e("pä",-1,1),new e("kö",-1,1)],o=[new e("lla",-1,-1),new e("na",-1,-1),new e("ssa",-1,-1),new e("ta",-1,-1),new e("lta",3,-1),new e("sta",3,-1)],a=[new e("llä",-1,-1),new e("nä",-1,-1),new e("ssä",-1,-1),new e("tä",-1,-1),new e("ltä",3,-1),new e("stä",3,-1)],u=[new e("lle",-1,-1),new e("ine",-1,-1)],c=[new e("nsa",-1,3),new e("mme",-1,3),new e("nne",-1,3),new e("ni",-1,2),new e("si",-1,1),new e("an",-1,4),new e("en",-1,6),new e("än",-1,5),new e("nsä",-1,3)],m=[new e("aa",-1,-1),new e("ee",-1,-1),new e("ii",-1,-1),new e("oo",-1,-1),new e("uu",-1,-1),new e("ää",-1,-1),new e("öö",-1,-1)],w=[new e("a",-1,8),new e("lla",0,-1),new e("na",0,-1),new e("ssa",0,-1),new e("ta",0,-1),new e("lta",4,-1),new e("sta",4,-1),new e("tta",4,9),new e("lle",-1,-1),new e("ine",-1,-1),new e("ksi",-1,-1),new e("n",-1,7),new e("han",11,1),new e("den",11,-1,C),new e("seen",11,-1,v),new e("hen",11,2),new e("tten",11,-1,C),new e("hin",11,3),new e("siin",11,-1,C),new e("hon",11,4),new e("hän",11,5),new e("hön",11,6),new e("ä",-1,8),new e("llä",22,-1),new e("nä",22,-1),new e("ssä",22,-1),new e("tä",22,-1),new e("ltä",26,-1),new e("stä",26,-1),new e("ttä",26,9)],_=[new e("eja",-1,-1),new e("mma",-1,1),new e("imma",1,-1),new e("mpa",-1,1),new e("impa",3,-1),new e("mmi",-1,1),new e("immi",5,-1),new e("mpi",-1,1),new e("impi",7,-1),new e("ejä",-1,-1),new e("mmä",-1,1),new e("immä",10,-1),new e("mpä",-1,1),new e("impä",12,-1)],k=[new e("i",-1,-1),new e("j",-1,-1)],b=[new e("mma",-1,1),new e("imma",0,-1)],d=[17,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8],f=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,8,0,32],h=[17,65,16,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,32],p=[17,97,24,1,0,0,0,0,0,0,0,0,0,0,0,0,8,0,32],g=new r;function j(){for(var i;i=g.cursor,!g.in_grouping(f,97,246);){if(g.cursor=i,i>=g.limit)return!0;g.cursor++}for(g.cursor=i;!g.out_grouping(f,97,246);){if(g.cursor>=g.limit)return!0;g.cursor++}return!1}function q(){var i,e;if(g.cursor>=s)if(e=g.limit_backward,g.limit_backward=s,g.ket=g.cursor,i=g.find_among_b(l,10)){switch(g.bra=g.cursor,g.limit_backward=e,i){case 1:if(!g.in_grouping_b(p,97,246))return;break;case 2:if(!(t<=g.cursor))return}g.slice_del()}else g.limit_backward=e}function v(){return g.find_among_b(m,7)}function C(){return g.eq_s_b(1,"i")&&g.in_grouping_b(h,97,246)}this.setCurrent=function(i){g.setCurrent(i)},this.getCurrent=function(){return g.getCurrent()},this.stem=function(){var e,r=g.cursor;return s=g.limit,t=s,j()||(s=g.cursor,j()||(t=g.cursor)),i=!1,g.limit_backward=r,g.cursor=g.limit,q(),g.cursor=g.limit,function(){var i,e,r;if(g.cursor>=s)if(e=g.limit_backward,g.limit_backward=s,g.ket=g.cursor,i=g.find_among_b(c,9))switch(g.bra=g.cursor,g.limit_backward=e,i){case 1:r=g.limit-g.cursor,g.eq_s_b(1,"k")||(g.cursor=g.limit-r,g.slice_del());break;case 2:g.slice_del(),g.ket=g.cursor,g.eq_s_b(3,"kse")&&(g.bra=g.cursor,g.slice_from("ksi"));break;case 3:g.slice_del();break;case 4:g.find_among_b(o,6)&&g.slice_del();break;case 5:g.find_among_b(a,6)&&g.slice_del();break;case 6:g.find_among_b(u,2)&&g.slice_del()}else g.limit_backward=e}(),g.cursor=g.limit,function(){var e,r,n;if(g.cursor>=s)if(r=g.limit_backward,g.limit_backward=s,g.ket=g.cursor,e=g.find_among_b(w,30)){switch(g.bra=g.cursor,g.limit_backward=r,e){case 1:if(!g.eq_s_b(1,"a"))return;break;case 2:case 9:if(!g.eq_s_b(1,"e"))return;break;case 3:if(!g.eq_s_b(1,"i"))return;break;case 4:if(!g.eq_s_b(1,"o"))return;break;case 5:if(!g.eq_s_b(1,"ä"))return;break;case 6:if(!g.eq_s_b(1,"ö"))return;break;case 7:if(n=g.limit-g.cursor,!v()&&(g.cursor=g.limit-n,!g.eq_s_b(2,"ie"))){g.cursor=g.limit-n;break}if(g.cursor=g.limit-n,g.cursor<=g.limit_backward){g.cursor=g.limit-n;break}g.cursor--,g.bra=g.cursor;break;case 8:if(!g.in_grouping_b(f,97,246)||!g.out_grouping_b(f,97,246))return}g.slice_del(),i=!0}else g.limit_backward=r}(),g.cursor=g.limit,function(){var i,e,r;if(g.cursor>=t)if(e=g.limit_backward,g.limit_backward=t,g.ket=g.cursor,i=g.find_among_b(_,14)){if(g.bra=g.cursor,g.limit_backward=e,1==i){if(r=g.limit-g.cursor,g.eq_s_b(2,"po"))return;g.cursor=g.limit-r}g.slice_del()}else g.limit_backward=e}(),g.cursor=g.limit,i?(g.cursor>=s&&(e=g.limit_backward,g.limit_backward=s,g.ket=g.cursor,g.find_among_b(k,2)?(g.bra=g.cursor,g.limit_backward=e,g.slice_del()):g.limit_backward=e),g.cursor=g.limit):(g.cursor=g.limit,function(){var i,e,r,n,l,o;if(g.cursor>=s){if(e=g.limit_backward,g.limit_backward=s,g.ket=g.cursor,g.eq_s_b(1,"t")&&(g.bra=g.cursor,r=g.limit-g.cursor,g.in_grouping_b(f,97,246)&&(g.cursor=g.limit-r,g.slice_del(),g.limit_backward=e,n=g.limit-g.cursor,g.cursor>=t&&(g.cursor=t,l=g.limit_backward,g.limit_backward=g.cursor,g.cursor=g.limit-n,g.ket=g.cursor,i=g.find_among_b(b,2))))){if(g.bra=g.cursor,g.limit_backward=l,1==i){if(o=g.limit-g.cursor,g.eq_s_b(2,"po"))return;g.cursor=g.limit-o}return void g.slice_del()}g.limit_backward=e}}(),g.cursor=g.limit),function(){var i,e,r,t;if(g.cursor>=s){for(i=g.limit_backward,g.limit_backward=s,e=g.limit-g.cursor,v()&&(g.cursor=g.limit-e,g.ket=g.cursor,g.cursor>g.limit_backward&&(g.cursor--,g.bra=g.cursor,g.slice_del())),g.cursor=g.limit-e,g.ket=g.cursor,g.in_grouping_b(d,97,228)&&(g.bra=g.cursor,g.out_grouping_b(f,97,246)&&g.slice_del()),g.cursor=g.limit-e,g.ket=g.cursor,g.eq_s_b(1,"j")&&(g.bra=g.cursor,r=g.limit-g.cursor,g.eq_s_b(1,"o")?g.slice_del():(g.cursor=g.limit-r,g.eq_s_b(1,"u")&&g.slice_del())),g.cursor=g.limit-e,g.ket=g.cursor,g.eq_s_b(1,"o")&&(g.bra=g.cursor,g.eq_s_b(1,"j")&&g.slice_del()),g.cursor=g.limit-e,g.limit_backward=i;;){if(t=g.limit-g.cursor,g.out_grouping_b(f,97,246)){g.cursor=g.limit-t;break}if(g.cursor=g.limit-t,g.cursor<=g.limit_backward)return;g.cursor--}g.ket=g.cursor,g.cursor>g.limit_backward&&(g.cursor--,g.bra=g.cursor,n=g.slice_to(),g.eq_v_b(n)&&g.slice_del())}}(),!0}},function(i){return"function"==typeof i.update?i.update(function(i){return n.setCurrent(i),n.stem(),n.getCurrent()}):(n.setCurrent(i),n.stem(),n.getCurrent())}),i.Pipeline.registerFunction(i.fi.stemmer,"stemmer-fi"),i.fi.stopWordFilter=i.generateStopWordFilter("ei eivät emme en et ette että he heidän heidät heihin heille heillä heiltä heissä heistä heitä hän häneen hänelle hänellä häneltä hänen hänessä hänestä hänet häntä itse ja johon joiden joihin joiksi joilla joille joilta joina joissa joista joita joka joksi jolla jolle jolta jona jonka jos jossa josta jota jotka kanssa keiden keihin keiksi keille keillä keiltä keinä keissä keistä keitä keneen keneksi kenelle kenellä keneltä kenen kenenä kenessä kenestä kenet ketkä ketkä ketä koska kuin kuka kun me meidän meidät meihin meille meillä meiltä meissä meistä meitä mihin miksi mikä mille millä miltä minkä minkä minua minulla minulle minulta minun minussa minusta minut minuun minä minä missä mistä mitkä mitä mukaan mutta ne niiden niihin niiksi niille niillä niiltä niin niin niinä niissä niistä niitä noiden noihin noiksi noilla noille noilta noin noina noissa noista noita nuo nyt näiden näihin näiksi näille näillä näiltä näinä näissä näistä näitä nämä ole olemme olen olet olette oli olimme olin olisi olisimme olisin olisit olisitte olisivat olit olitte olivat olla olleet ollut on ovat poikki se sekä sen siihen siinä siitä siksi sille sillä sillä siltä sinua sinulla sinulle sinulta sinun sinussa sinusta sinut sinuun sinä sinä sitä tai te teidän teidät teihin teille teillä teiltä teissä teistä teitä tuo tuohon tuoksi tuolla tuolle tuolta tuon tuona tuossa tuosta tuota tähän täksi tälle tällä tältä tämä tämän tänä tässä tästä tätä vaan vai vaikka yli".split(" ")),i.Pipeline.registerFunction(i.fi.stopWordFilter,"stopWordFilter-fi")}}); \ No newline at end of file diff --git a/3.0/site/assets/javascripts/lunr/lunr.fr.js b/3.0/site/assets/javascripts/lunr/lunr.fr.js new file mode 100644 index 00000000..3a9b9b17 --- /dev/null +++ b/3.0/site/assets/javascripts/lunr/lunr.fr.js @@ -0,0 +1 @@ +!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var r,s,i;e.fr=function(){this.pipeline.reset(),this.pipeline.add(e.fr.trimmer,e.fr.stopWordFilter,e.fr.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.fr.stemmer))},e.fr.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.fr.trimmer=e.trimmerSupport.generateTrimmer(e.fr.wordCharacters),e.Pipeline.registerFunction(e.fr.trimmer,"trimmer-fr"),e.fr.stemmer=(r=e.stemmerSupport.Among,s=e.stemmerSupport.SnowballProgram,i=new function(){var e,i,n,t=[new r("col",-1,-1),new r("par",-1,-1),new r("tap",-1,-1)],u=[new r("",-1,4),new r("I",0,1),new r("U",0,2),new r("Y",0,3)],o=[new r("iqU",-1,3),new r("abl",-1,3),new r("Ièr",-1,4),new r("ièr",-1,4),new r("eus",-1,2),new r("iv",-1,1)],c=[new r("ic",-1,2),new r("abil",-1,1),new r("iv",-1,3)],a=[new r("iqUe",-1,1),new r("atrice",-1,2),new r("ance",-1,1),new r("ence",-1,5),new r("logie",-1,3),new r("able",-1,1),new r("isme",-1,1),new r("euse",-1,11),new r("iste",-1,1),new r("ive",-1,8),new r("if",-1,8),new r("usion",-1,4),new r("ation",-1,2),new r("ution",-1,4),new r("ateur",-1,2),new r("iqUes",-1,1),new r("atrices",-1,2),new r("ances",-1,1),new r("ences",-1,5),new r("logies",-1,3),new r("ables",-1,1),new r("ismes",-1,1),new r("euses",-1,11),new r("istes",-1,1),new r("ives",-1,8),new r("ifs",-1,8),new r("usions",-1,4),new r("ations",-1,2),new r("utions",-1,4),new r("ateurs",-1,2),new r("ments",-1,15),new r("ements",30,6),new r("issements",31,12),new r("ités",-1,7),new r("ment",-1,15),new r("ement",34,6),new r("issement",35,12),new r("amment",34,13),new r("emment",34,14),new r("aux",-1,10),new r("eaux",39,9),new r("eux",-1,1),new r("ité",-1,7)],l=[new r("ira",-1,1),new r("ie",-1,1),new r("isse",-1,1),new r("issante",-1,1),new r("i",-1,1),new r("irai",4,1),new r("ir",-1,1),new r("iras",-1,1),new r("ies",-1,1),new r("îmes",-1,1),new r("isses",-1,1),new r("issantes",-1,1),new r("îtes",-1,1),new r("is",-1,1),new r("irais",13,1),new r("issais",13,1),new r("irions",-1,1),new r("issions",-1,1),new r("irons",-1,1),new r("issons",-1,1),new r("issants",-1,1),new r("it",-1,1),new r("irait",21,1),new r("issait",21,1),new r("issant",-1,1),new r("iraIent",-1,1),new r("issaIent",-1,1),new r("irent",-1,1),new r("issent",-1,1),new r("iront",-1,1),new r("ît",-1,1),new r("iriez",-1,1),new r("issiez",-1,1),new r("irez",-1,1),new r("issez",-1,1)],w=[new r("a",-1,3),new r("era",0,2),new r("asse",-1,3),new r("ante",-1,3),new r("ée",-1,2),new r("ai",-1,3),new r("erai",5,2),new r("er",-1,2),new r("as",-1,3),new r("eras",8,2),new r("âmes",-1,3),new r("asses",-1,3),new r("antes",-1,3),new r("âtes",-1,3),new r("ées",-1,2),new r("ais",-1,3),new r("erais",15,2),new r("ions",-1,1),new r("erions",17,2),new r("assions",17,3),new r("erons",-1,2),new r("ants",-1,3),new r("és",-1,2),new r("ait",-1,3),new r("erait",23,2),new r("ant",-1,3),new r("aIent",-1,3),new r("eraIent",26,2),new r("èrent",-1,2),new r("assent",-1,3),new r("eront",-1,2),new r("ât",-1,3),new r("ez",-1,2),new r("iez",32,2),new r("eriez",33,2),new r("assiez",33,3),new r("erez",32,2),new r("é",-1,2)],f=[new r("e",-1,3),new r("Ière",0,2),new r("ière",0,2),new r("ion",-1,1),new r("Ier",-1,2),new r("ier",-1,2),new r("ë",-1,4)],m=[new r("ell",-1,-1),new r("eill",-1,-1),new r("enn",-1,-1),new r("onn",-1,-1),new r("ett",-1,-1)],_=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,128,130,103,8,5],b=[1,65,20,0,0,0,0,0,0,0,0,0,0,0,0,0,128],d=new s;function k(e,r,s){return!(!d.eq_s(1,e)||(d.ket=d.cursor,!d.in_grouping(_,97,251)))&&(d.slice_from(r),d.cursor=s,!0)}function p(e,r,s){return!!d.eq_s(1,e)&&(d.ket=d.cursor,d.slice_from(r),d.cursor=s,!0)}function g(){for(;!d.in_grouping(_,97,251);){if(d.cursor>=d.limit)return!0;d.cursor++}for(;!d.out_grouping(_,97,251);){if(d.cursor>=d.limit)return!0;d.cursor++}return!1}function q(){return n<=d.cursor}function v(){return i<=d.cursor}function h(){return e<=d.cursor}function z(){if(!function(){var e,r;if(d.ket=d.cursor,e=d.find_among_b(a,43)){switch(d.bra=d.cursor,e){case 1:if(!h())return!1;d.slice_del();break;case 2:if(!h())return!1;d.slice_del(),d.ket=d.cursor,d.eq_s_b(2,"ic")&&(d.bra=d.cursor,h()?d.slice_del():d.slice_from("iqU"));break;case 3:if(!h())return!1;d.slice_from("log");break;case 4:if(!h())return!1;d.slice_from("u");break;case 5:if(!h())return!1;d.slice_from("ent");break;case 6:if(!q())return!1;if(d.slice_del(),d.ket=d.cursor,e=d.find_among_b(o,6))switch(d.bra=d.cursor,e){case 1:h()&&(d.slice_del(),d.ket=d.cursor,d.eq_s_b(2,"at")&&(d.bra=d.cursor,h()&&d.slice_del()));break;case 2:h()?d.slice_del():v()&&d.slice_from("eux");break;case 3:h()&&d.slice_del();break;case 4:q()&&d.slice_from("i")}break;case 7:if(!h())return!1;if(d.slice_del(),d.ket=d.cursor,e=d.find_among_b(c,3))switch(d.bra=d.cursor,e){case 1:h()?d.slice_del():d.slice_from("abl");break;case 2:h()?d.slice_del():d.slice_from("iqU");break;case 3:h()&&d.slice_del()}break;case 8:if(!h())return!1;if(d.slice_del(),d.ket=d.cursor,d.eq_s_b(2,"at")&&(d.bra=d.cursor,h()&&(d.slice_del(),d.ket=d.cursor,d.eq_s_b(2,"ic")))){d.bra=d.cursor,h()?d.slice_del():d.slice_from("iqU");break}break;case 9:d.slice_from("eau");break;case 10:if(!v())return!1;d.slice_from("al");break;case 11:if(h())d.slice_del();else{if(!v())return!1;d.slice_from("eux")}break;case 12:if(!v()||!d.out_grouping_b(_,97,251))return!1;d.slice_del();break;case 13:return q()&&d.slice_from("ant"),!1;case 14:return q()&&d.slice_from("ent"),!1;case 15:return r=d.limit-d.cursor,d.in_grouping_b(_,97,251)&&q()&&(d.cursor=d.limit-r,d.slice_del()),!1}return!0}return!1}()&&(d.cursor=d.limit,!function(){var e,r;if(d.cursor=n){if(s=d.limit_backward,d.limit_backward=n,d.ket=d.cursor,e=d.find_among_b(f,7))switch(d.bra=d.cursor,e){case 1:if(h()){if(i=d.limit-d.cursor,!d.eq_s_b(1,"s")&&(d.cursor=d.limit-i,!d.eq_s_b(1,"t")))break;d.slice_del()}break;case 2:d.slice_from("i");break;case 3:d.slice_del();break;case 4:d.eq_s_b(2,"gu")&&d.slice_del()}d.limit_backward=s}}();d.cursor=d.limit,d.ket=d.cursor,d.eq_s_b(1,"Y")?(d.bra=d.cursor,d.slice_from("i")):(d.cursor=d.limit,d.eq_s_b(1,"ç")&&(d.bra=d.cursor,d.slice_from("c")))}this.setCurrent=function(e){d.setCurrent(e)},this.getCurrent=function(){return d.getCurrent()},this.stem=function(){var r,s=d.cursor;return function(){for(var e,r;;){if(e=d.cursor,d.in_grouping(_,97,251)){if(d.bra=d.cursor,r=d.cursor,k("u","U",e))continue;if(d.cursor=r,k("i","I",e))continue;if(d.cursor=r,p("y","Y",e))continue}if(d.cursor=e,d.bra=e,!k("y","Y",e)){if(d.cursor=e,d.eq_s(1,"q")&&(d.bra=d.cursor,p("u","U",e)))continue;if(d.cursor=e,e>=d.limit)return;d.cursor++}}}(),d.cursor=s,function(){var r=d.cursor;if(n=d.limit,i=n,e=n,d.in_grouping(_,97,251)&&d.in_grouping(_,97,251)&&d.cursor=d.limit){d.cursor=n;break}d.cursor++}while(!d.in_grouping(_,97,251))}n=d.cursor,d.cursor=r,g()||(i=d.cursor,g()||(e=d.cursor))}(),d.limit_backward=s,d.cursor=d.limit,z(),d.cursor=d.limit,r=d.limit-d.cursor,d.find_among_b(m,5)&&(d.cursor=d.limit-r,d.ket=d.cursor,d.cursor>d.limit_backward&&(d.cursor--,d.bra=d.cursor,d.slice_del())),d.cursor=d.limit,function(){for(var e,r=1;d.out_grouping_b(_,97,251);)r--;if(r<=0){if(d.ket=d.cursor,e=d.limit-d.cursor,!d.eq_s_b(1,"é")&&(d.cursor=d.limit-e,!d.eq_s_b(1,"è")))return;d.bra=d.cursor,d.slice_from("e")}}(),d.cursor=d.limit_backward,function(){for(var e,r;r=d.cursor,d.bra=r,e=d.find_among(u,4);)switch(d.ket=d.cursor,e){case 1:d.slice_from("i");break;case 2:d.slice_from("u");break;case 3:d.slice_from("y");break;case 4:if(d.cursor>=d.limit)return;d.cursor++}}(),!0}},function(e){return"function"==typeof e.update?e.update(function(e){return i.setCurrent(e),i.stem(),i.getCurrent()}):(i.setCurrent(e),i.stem(),i.getCurrent())}),e.Pipeline.registerFunction(e.fr.stemmer,"stemmer-fr"),e.fr.stopWordFilter=e.generateStopWordFilter("ai aie aient aies ait as au aura aurai auraient aurais aurait auras aurez auriez aurions aurons auront aux avaient avais avait avec avez aviez avions avons ayant ayez ayons c ce ceci celà ces cet cette d dans de des du elle en es est et eu eue eues eurent eus eusse eussent eusses eussiez eussions eut eux eûmes eût eûtes furent fus fusse fussent fusses fussiez fussions fut fûmes fût fûtes ici il ils j je l la le les leur leurs lui m ma mais me mes moi mon même n ne nos notre nous on ont ou par pas pour qu que quel quelle quelles quels qui s sa sans se sera serai seraient serais serait seras serez seriez serions serons seront ses soi soient sois soit sommes son sont soyez soyons suis sur t ta te tes toi ton tu un une vos votre vous y à étaient étais était étant étiez étions été étée étées étés êtes".split(" ")),e.Pipeline.registerFunction(e.fr.stopWordFilter,"stopWordFilter-fr")}}); \ No newline at end of file diff --git a/3.0/site/assets/javascripts/lunr/lunr.hu.js b/3.0/site/assets/javascripts/lunr/lunr.hu.js new file mode 100644 index 00000000..fa704a69 --- /dev/null +++ b/3.0/site/assets/javascripts/lunr/lunr.hu.js @@ -0,0 +1 @@ +!function(e,n){"function"==typeof define&&define.amd?define(n):"object"==typeof exports?module.exports=n():n()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var n,r,i;e.hu=function(){this.pipeline.reset(),this.pipeline.add(e.hu.trimmer,e.hu.stopWordFilter,e.hu.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.hu.stemmer))},e.hu.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.hu.trimmer=e.trimmerSupport.generateTrimmer(e.hu.wordCharacters),e.Pipeline.registerFunction(e.hu.trimmer,"trimmer-hu"),e.hu.stemmer=(n=e.stemmerSupport.Among,r=e.stemmerSupport.SnowballProgram,i=new function(){var e,i=[new n("cs",-1,-1),new n("dzs",-1,-1),new n("gy",-1,-1),new n("ly",-1,-1),new n("ny",-1,-1),new n("sz",-1,-1),new n("ty",-1,-1),new n("zs",-1,-1)],a=[new n("á",-1,1),new n("é",-1,2)],t=[new n("bb",-1,-1),new n("cc",-1,-1),new n("dd",-1,-1),new n("ff",-1,-1),new n("gg",-1,-1),new n("jj",-1,-1),new n("kk",-1,-1),new n("ll",-1,-1),new n("mm",-1,-1),new n("nn",-1,-1),new n("pp",-1,-1),new n("rr",-1,-1),new n("ccs",-1,-1),new n("ss",-1,-1),new n("zzs",-1,-1),new n("tt",-1,-1),new n("vv",-1,-1),new n("ggy",-1,-1),new n("lly",-1,-1),new n("nny",-1,-1),new n("tty",-1,-1),new n("ssz",-1,-1),new n("zz",-1,-1)],s=[new n("al",-1,1),new n("el",-1,2)],c=[new n("ba",-1,-1),new n("ra",-1,-1),new n("be",-1,-1),new n("re",-1,-1),new n("ig",-1,-1),new n("nak",-1,-1),new n("nek",-1,-1),new n("val",-1,-1),new n("vel",-1,-1),new n("ul",-1,-1),new n("nál",-1,-1),new n("nél",-1,-1),new n("ból",-1,-1),new n("ról",-1,-1),new n("tól",-1,-1),new n("bõl",-1,-1),new n("rõl",-1,-1),new n("tõl",-1,-1),new n("ül",-1,-1),new n("n",-1,-1),new n("an",19,-1),new n("ban",20,-1),new n("en",19,-1),new n("ben",22,-1),new n("képpen",22,-1),new n("on",19,-1),new n("ön",19,-1),new n("képp",-1,-1),new n("kor",-1,-1),new n("t",-1,-1),new n("at",29,-1),new n("et",29,-1),new n("ként",29,-1),new n("anként",32,-1),new n("enként",32,-1),new n("onként",32,-1),new n("ot",29,-1),new n("ért",29,-1),new n("öt",29,-1),new n("hez",-1,-1),new n("hoz",-1,-1),new n("höz",-1,-1),new n("vá",-1,-1),new n("vé",-1,-1)],w=[new n("án",-1,2),new n("én",-1,1),new n("ánként",-1,3)],o=[new n("stul",-1,2),new n("astul",0,1),new n("ástul",0,3),new n("stül",-1,2),new n("estül",3,1),new n("éstül",3,4)],l=[new n("á",-1,1),new n("é",-1,2)],u=[new n("k",-1,7),new n("ak",0,4),new n("ek",0,6),new n("ok",0,5),new n("ák",0,1),new n("ék",0,2),new n("ök",0,3)],m=[new n("éi",-1,7),new n("áéi",0,6),new n("ééi",0,5),new n("é",-1,9),new n("ké",3,4),new n("aké",4,1),new n("eké",4,1),new n("oké",4,1),new n("áké",4,3),new n("éké",4,2),new n("öké",4,1),new n("éé",3,8)],k=[new n("a",-1,18),new n("ja",0,17),new n("d",-1,16),new n("ad",2,13),new n("ed",2,13),new n("od",2,13),new n("ád",2,14),new n("éd",2,15),new n("öd",2,13),new n("e",-1,18),new n("je",9,17),new n("nk",-1,4),new n("unk",11,1),new n("ánk",11,2),new n("énk",11,3),new n("ünk",11,1),new n("uk",-1,8),new n("juk",16,7),new n("ájuk",17,5),new n("ük",-1,8),new n("jük",19,7),new n("éjük",20,6),new n("m",-1,12),new n("am",22,9),new n("em",22,9),new n("om",22,9),new n("ám",22,10),new n("ém",22,11),new n("o",-1,18),new n("á",-1,19),new n("é",-1,20)],f=[new n("id",-1,10),new n("aid",0,9),new n("jaid",1,6),new n("eid",0,9),new n("jeid",3,6),new n("áid",0,7),new n("éid",0,8),new n("i",-1,15),new n("ai",7,14),new n("jai",8,11),new n("ei",7,14),new n("jei",10,11),new n("ái",7,12),new n("éi",7,13),new n("itek",-1,24),new n("eitek",14,21),new n("jeitek",15,20),new n("éitek",14,23),new n("ik",-1,29),new n("aik",18,26),new n("jaik",19,25),new n("eik",18,26),new n("jeik",21,25),new n("áik",18,27),new n("éik",18,28),new n("ink",-1,20),new n("aink",25,17),new n("jaink",26,16),new n("eink",25,17),new n("jeink",28,16),new n("áink",25,18),new n("éink",25,19),new n("aitok",-1,21),new n("jaitok",32,20),new n("áitok",-1,22),new n("im",-1,5),new n("aim",35,4),new n("jaim",36,1),new n("eim",35,4),new n("jeim",38,1),new n("áim",35,2),new n("éim",35,3)],b=[17,65,16,0,0,0,0,0,0,0,0,0,0,0,0,0,1,17,52,14],d=new r;function g(){return e<=d.cursor}function h(){var e=d.limit-d.cursor;return!!d.find_among_b(t,23)&&(d.cursor=d.limit-e,!0)}function p(){if(d.cursor>d.limit_backward){d.cursor--,d.ket=d.cursor;var e=d.cursor-1;d.limit_backward<=e&&e<=d.limit&&(d.cursor=e,d.bra=e,d.slice_del())}}function _(){d.ket=d.cursor,d.find_among_b(c,44)&&(d.bra=d.cursor,g()&&(d.slice_del(),function(){var e;if(d.ket=d.cursor,(e=d.find_among_b(a,2))&&(d.bra=d.cursor,g()))switch(e){case 1:d.slice_from("a");break;case 2:d.slice_from("e")}}()))}this.setCurrent=function(e){d.setCurrent(e)},this.getCurrent=function(){return d.getCurrent()},this.stem=function(){var n=d.cursor;return function(){var n,r=d.cursor;if(e=d.limit,d.in_grouping(b,97,252))for(;;){if(n=d.cursor,d.out_grouping(b,97,252))return d.cursor=n,d.find_among(i,8)||(d.cursor=n,n=d.limit)return void(e=n);d.cursor++}if(d.cursor=r,d.out_grouping(b,97,252)){for(;!d.in_grouping(b,97,252);){if(d.cursor>=d.limit)return;d.cursor++}e=d.cursor}}(),d.limit_backward=n,d.cursor=d.limit,function(){var e;if(d.ket=d.cursor,(e=d.find_among_b(s,2))&&(d.bra=d.cursor,g())){if((1==e||2==e)&&!h())return;d.slice_del(),p()}}(),d.cursor=d.limit,_(),d.cursor=d.limit,function(){var e;if(d.ket=d.cursor,(e=d.find_among_b(w,3))&&(d.bra=d.cursor,g()))switch(e){case 1:d.slice_from("e");break;case 2:case 3:d.slice_from("a")}}(),d.cursor=d.limit,function(){var e;if(d.ket=d.cursor,(e=d.find_among_b(o,6))&&(d.bra=d.cursor,g()))switch(e){case 1:case 2:d.slice_del();break;case 3:d.slice_from("a");break;case 4:d.slice_from("e")}}(),d.cursor=d.limit,function(){var e;if(d.ket=d.cursor,(e=d.find_among_b(l,2))&&(d.bra=d.cursor,g())){if((1==e||2==e)&&!h())return;d.slice_del(),p()}}(),d.cursor=d.limit,function(){var e;if(d.ket=d.cursor,(e=d.find_among_b(m,12))&&(d.bra=d.cursor,g()))switch(e){case 1:case 4:case 7:case 9:d.slice_del();break;case 2:case 5:case 8:d.slice_from("e");break;case 3:case 6:d.slice_from("a")}}(),d.cursor=d.limit,function(){var e;if(d.ket=d.cursor,(e=d.find_among_b(k,31))&&(d.bra=d.cursor,g()))switch(e){case 1:case 4:case 7:case 8:case 9:case 12:case 13:case 16:case 17:case 18:d.slice_del();break;case 2:case 5:case 10:case 14:case 19:d.slice_from("a");break;case 3:case 6:case 11:case 15:case 20:d.slice_from("e")}}(),d.cursor=d.limit,function(){var e;if(d.ket=d.cursor,(e=d.find_among_b(f,42))&&(d.bra=d.cursor,g()))switch(e){case 1:case 4:case 5:case 6:case 9:case 10:case 11:case 14:case 15:case 16:case 17:case 20:case 21:case 24:case 25:case 26:case 29:d.slice_del();break;case 2:case 7:case 12:case 18:case 22:case 27:d.slice_from("a");break;case 3:case 8:case 13:case 19:case 23:case 28:d.slice_from("e")}}(),d.cursor=d.limit,function(){var e;if(d.ket=d.cursor,(e=d.find_among_b(u,7))&&(d.bra=d.cursor,g()))switch(e){case 1:d.slice_from("a");break;case 2:d.slice_from("e");break;case 3:case 4:case 5:case 6:case 7:d.slice_del()}}(),!0}},function(e){return"function"==typeof e.update?e.update(function(e){return i.setCurrent(e),i.stem(),i.getCurrent()}):(i.setCurrent(e),i.stem(),i.getCurrent())}),e.Pipeline.registerFunction(e.hu.stemmer,"stemmer-hu"),e.hu.stopWordFilter=e.generateStopWordFilter("a abban ahhoz ahogy ahol aki akik akkor alatt amely amelyek amelyekben amelyeket amelyet amelynek ami amikor amit amolyan amíg annak arra arról az azok azon azonban azt aztán azután azzal azért be belül benne bár cikk cikkek cikkeket csak de e ebben eddig egy egyes egyetlen egyik egyre egyéb egész ehhez ekkor el ellen elsõ elég elõ elõször elõtt emilyen ennek erre ez ezek ezen ezt ezzel ezért fel felé hanem hiszen hogy hogyan igen ill ill. illetve ilyen ilyenkor ismét ison itt jobban jó jól kell kellett keressünk keresztül ki kívül között közül legalább legyen lehet lehetett lenne lenni lesz lett maga magát majd majd meg mellett mely melyek mert mi mikor milyen minden mindenki mindent mindig mint mintha mit mivel miért most már más másik még míg nagy nagyobb nagyon ne nekem neki nem nincs néha néhány nélkül olyan ott pedig persze rá s saját sem semmi sok sokat sokkal szemben szerint szinte számára talán tehát teljes tovább továbbá több ugyanis utolsó után utána vagy vagyis vagyok valaki valami valamint való van vannak vele vissza viszont volna volt voltak voltam voltunk által általában át én éppen és így õ õk õket össze úgy új újabb újra".split(" ")),e.Pipeline.registerFunction(e.hu.stopWordFilter,"stopWordFilter-hu")}}); \ No newline at end of file diff --git a/3.0/site/assets/javascripts/lunr/lunr.it.js b/3.0/site/assets/javascripts/lunr/lunr.it.js new file mode 100644 index 00000000..29307338 --- /dev/null +++ b/3.0/site/assets/javascripts/lunr/lunr.it.js @@ -0,0 +1 @@ +!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var r,i,n;e.it=function(){this.pipeline.reset(),this.pipeline.add(e.it.trimmer,e.it.stopWordFilter,e.it.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.it.stemmer))},e.it.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.it.trimmer=e.trimmerSupport.generateTrimmer(e.it.wordCharacters),e.Pipeline.registerFunction(e.it.trimmer,"trimmer-it"),e.it.stemmer=(r=e.stemmerSupport.Among,i=e.stemmerSupport.SnowballProgram,n=new function(){var e,n,o,t=[new r("",-1,7),new r("qu",0,6),new r("á",0,1),new r("é",0,2),new r("í",0,3),new r("ó",0,4),new r("ú",0,5)],s=[new r("",-1,3),new r("I",0,1),new r("U",0,2)],a=[new r("la",-1,-1),new r("cela",0,-1),new r("gliela",0,-1),new r("mela",0,-1),new r("tela",0,-1),new r("vela",0,-1),new r("le",-1,-1),new r("cele",6,-1),new r("gliele",6,-1),new r("mele",6,-1),new r("tele",6,-1),new r("vele",6,-1),new r("ne",-1,-1),new r("cene",12,-1),new r("gliene",12,-1),new r("mene",12,-1),new r("sene",12,-1),new r("tene",12,-1),new r("vene",12,-1),new r("ci",-1,-1),new r("li",-1,-1),new r("celi",20,-1),new r("glieli",20,-1),new r("meli",20,-1),new r("teli",20,-1),new r("veli",20,-1),new r("gli",20,-1),new r("mi",-1,-1),new r("si",-1,-1),new r("ti",-1,-1),new r("vi",-1,-1),new r("lo",-1,-1),new r("celo",31,-1),new r("glielo",31,-1),new r("melo",31,-1),new r("telo",31,-1),new r("velo",31,-1)],u=[new r("ando",-1,1),new r("endo",-1,1),new r("ar",-1,2),new r("er",-1,2),new r("ir",-1,2)],c=[new r("ic",-1,-1),new r("abil",-1,-1),new r("os",-1,-1),new r("iv",-1,1)],w=[new r("ic",-1,1),new r("abil",-1,1),new r("iv",-1,1)],l=[new r("ica",-1,1),new r("logia",-1,3),new r("osa",-1,1),new r("ista",-1,1),new r("iva",-1,9),new r("anza",-1,1),new r("enza",-1,5),new r("ice",-1,1),new r("atrice",7,1),new r("iche",-1,1),new r("logie",-1,3),new r("abile",-1,1),new r("ibile",-1,1),new r("usione",-1,4),new r("azione",-1,2),new r("uzione",-1,4),new r("atore",-1,2),new r("ose",-1,1),new r("ante",-1,1),new r("mente",-1,1),new r("amente",19,7),new r("iste",-1,1),new r("ive",-1,9),new r("anze",-1,1),new r("enze",-1,5),new r("ici",-1,1),new r("atrici",25,1),new r("ichi",-1,1),new r("abili",-1,1),new r("ibili",-1,1),new r("ismi",-1,1),new r("usioni",-1,4),new r("azioni",-1,2),new r("uzioni",-1,4),new r("atori",-1,2),new r("osi",-1,1),new r("anti",-1,1),new r("amenti",-1,6),new r("imenti",-1,6),new r("isti",-1,1),new r("ivi",-1,9),new r("ico",-1,1),new r("ismo",-1,1),new r("oso",-1,1),new r("amento",-1,6),new r("imento",-1,6),new r("ivo",-1,9),new r("ità",-1,8),new r("istà",-1,1),new r("istè",-1,1),new r("istì",-1,1)],m=[new r("isca",-1,1),new r("enda",-1,1),new r("ata",-1,1),new r("ita",-1,1),new r("uta",-1,1),new r("ava",-1,1),new r("eva",-1,1),new r("iva",-1,1),new r("erebbe",-1,1),new r("irebbe",-1,1),new r("isce",-1,1),new r("ende",-1,1),new r("are",-1,1),new r("ere",-1,1),new r("ire",-1,1),new r("asse",-1,1),new r("ate",-1,1),new r("avate",16,1),new r("evate",16,1),new r("ivate",16,1),new r("ete",-1,1),new r("erete",20,1),new r("irete",20,1),new r("ite",-1,1),new r("ereste",-1,1),new r("ireste",-1,1),new r("ute",-1,1),new r("erai",-1,1),new r("irai",-1,1),new r("isci",-1,1),new r("endi",-1,1),new r("erei",-1,1),new r("irei",-1,1),new r("assi",-1,1),new r("ati",-1,1),new r("iti",-1,1),new r("eresti",-1,1),new r("iresti",-1,1),new r("uti",-1,1),new r("avi",-1,1),new r("evi",-1,1),new r("ivi",-1,1),new r("isco",-1,1),new r("ando",-1,1),new r("endo",-1,1),new r("Yamo",-1,1),new r("iamo",-1,1),new r("avamo",-1,1),new r("evamo",-1,1),new r("ivamo",-1,1),new r("eremo",-1,1),new r("iremo",-1,1),new r("assimo",-1,1),new r("ammo",-1,1),new r("emmo",-1,1),new r("eremmo",54,1),new r("iremmo",54,1),new r("immo",-1,1),new r("ano",-1,1),new r("iscano",58,1),new r("avano",58,1),new r("evano",58,1),new r("ivano",58,1),new r("eranno",-1,1),new r("iranno",-1,1),new r("ono",-1,1),new r("iscono",65,1),new r("arono",65,1),new r("erono",65,1),new r("irono",65,1),new r("erebbero",-1,1),new r("irebbero",-1,1),new r("assero",-1,1),new r("essero",-1,1),new r("issero",-1,1),new r("ato",-1,1),new r("ito",-1,1),new r("uto",-1,1),new r("avo",-1,1),new r("evo",-1,1),new r("ivo",-1,1),new r("ar",-1,1),new r("ir",-1,1),new r("erà",-1,1),new r("irà",-1,1),new r("erò",-1,1),new r("irò",-1,1)],f=[17,65,16,0,0,0,0,0,0,0,0,0,0,0,0,128,128,8,2,1],v=[17,65,0,0,0,0,0,0,0,0,0,0,0,0,0,128,128,8,2],b=[17],d=new i;function _(e,r,i){return!(!d.eq_s(1,e)||(d.ket=d.cursor,!d.in_grouping(f,97,249)))&&(d.slice_from(r),d.cursor=i,!0)}function g(e){if(d.cursor=e,!d.in_grouping(f,97,249))return!1;for(;!d.out_grouping(f,97,249);){if(d.cursor>=d.limit)return!1;d.cursor++}return!0}function p(){var e,r=d.cursor;if(!function(){if(d.in_grouping(f,97,249)){var e=d.cursor;if(d.out_grouping(f,97,249)){for(;!d.in_grouping(f,97,249);){if(d.cursor>=d.limit)return g(e);d.cursor++}return!0}return g(e)}return!1}()){if(d.cursor=r,!d.out_grouping(f,97,249))return;if(e=d.cursor,d.out_grouping(f,97,249)){for(;!d.in_grouping(f,97,249);){if(d.cursor>=d.limit)return d.cursor=e,void(d.in_grouping(f,97,249)&&d.cursor=d.limit)return;d.cursor++}o=d.cursor}function k(){for(;!d.in_grouping(f,97,249);){if(d.cursor>=d.limit)return!1;d.cursor++}for(;!d.out_grouping(f,97,249);){if(d.cursor>=d.limit)return!1;d.cursor++}return!0}function h(){return o<=d.cursor}function q(){return e<=d.cursor}function C(){var e;if(d.ket=d.cursor,!(e=d.find_among_b(l,51)))return!1;switch(d.bra=d.cursor,e){case 1:if(!q())return!1;d.slice_del();break;case 2:if(!q())return!1;d.slice_del(),d.ket=d.cursor,d.eq_s_b(2,"ic")&&(d.bra=d.cursor,q()&&d.slice_del());break;case 3:if(!q())return!1;d.slice_from("log");break;case 4:if(!q())return!1;d.slice_from("u");break;case 5:if(!q())return!1;d.slice_from("ente");break;case 6:if(!h())return!1;d.slice_del();break;case 7:if(!(n<=d.cursor))return!1;d.slice_del(),d.ket=d.cursor,(e=d.find_among_b(c,4))&&(d.bra=d.cursor,q()&&(d.slice_del(),1==e&&(d.ket=d.cursor,d.eq_s_b(2,"at")&&(d.bra=d.cursor,q()&&d.slice_del()))));break;case 8:if(!q())return!1;d.slice_del(),d.ket=d.cursor,(e=d.find_among_b(w,3))&&(d.bra=d.cursor,1==e&&q()&&d.slice_del());break;case 9:if(!q())return!1;d.slice_del(),d.ket=d.cursor,d.eq_s_b(2,"at")&&(d.bra=d.cursor,q()&&(d.slice_del(),d.ket=d.cursor,d.eq_s_b(2,"ic")&&(d.bra=d.cursor,q()&&d.slice_del())))}return!0}function z(){var e;e=d.limit-d.cursor,d.ket=d.cursor,d.in_grouping_b(v,97,242)&&(d.bra=d.cursor,h()&&(d.slice_del(),d.ket=d.cursor,d.eq_s_b(1,"i")&&(d.bra=d.cursor,h())))?d.slice_del():d.cursor=d.limit-e,d.ket=d.cursor,d.eq_s_b(1,"h")&&(d.bra=d.cursor,d.in_grouping_b(b,99,103)&&h()&&d.slice_del())}this.setCurrent=function(e){d.setCurrent(e)},this.getCurrent=function(){return d.getCurrent()},this.stem=function(){var r,i,c,w=d.cursor;return function(){for(var e,r,i,n,o=d.cursor;;){if(d.bra=d.cursor,e=d.find_among(t,7))switch(d.ket=d.cursor,e){case 1:d.slice_from("à");continue;case 2:d.slice_from("è");continue;case 3:d.slice_from("ì");continue;case 4:d.slice_from("ò");continue;case 5:d.slice_from("ù");continue;case 6:d.slice_from("qU");continue;case 7:if(d.cursor>=d.limit)break;d.cursor++;continue}break}for(d.cursor=o;;)for(r=d.cursor;;){if(i=d.cursor,d.in_grouping(f,97,249)){if(d.bra=d.cursor,n=d.cursor,_("u","U",i))break;if(d.cursor=n,_("i","I",i))break}if(d.cursor=i,d.cursor>=d.limit)return void(d.cursor=r);d.cursor++}}(),d.cursor=w,r=d.cursor,o=d.limit,n=o,e=o,p(),d.cursor=r,k()&&(n=d.cursor,k()&&(e=d.cursor)),d.limit_backward=w,d.cursor=d.limit,function(){var e;if(d.ket=d.cursor,d.find_among_b(a,37)&&(d.bra=d.cursor,(e=d.find_among_b(u,5))&&h()))switch(e){case 1:d.slice_del();break;case 2:d.slice_from("e")}}(),d.cursor=d.limit,C()||(d.cursor=d.limit,d.cursor>=o&&(c=d.limit_backward,d.limit_backward=o,d.ket=d.cursor,(i=d.find_among_b(m,87))&&(d.bra=d.cursor,1==i&&d.slice_del()),d.limit_backward=c)),d.cursor=d.limit,z(),d.cursor=d.limit_backward,function(){for(var e;d.bra=d.cursor,e=d.find_among(s,3);)switch(d.ket=d.cursor,e){case 1:d.slice_from("i");break;case 2:d.slice_from("u");break;case 3:if(d.cursor>=d.limit)return;d.cursor++}}(),!0}},function(e){return"function"==typeof e.update?e.update(function(e){return n.setCurrent(e),n.stem(),n.getCurrent()}):(n.setCurrent(e),n.stem(),n.getCurrent())}),e.Pipeline.registerFunction(e.it.stemmer,"stemmer-it"),e.it.stopWordFilter=e.generateStopWordFilter("a abbia abbiamo abbiano abbiate ad agl agli ai al all alla alle allo anche avemmo avendo avesse avessero avessi avessimo aveste avesti avete aveva avevamo avevano avevate avevi avevo avrai avranno avrebbe avrebbero avrei avremmo avremo avreste avresti avrete avrà avrò avuta avute avuti avuto c che chi ci coi col come con contro cui da dagl dagli dai dal dall dalla dalle dallo degl degli dei del dell della delle dello di dov dove e ebbe ebbero ebbi ed era erano eravamo eravate eri ero essendo faccia facciamo facciano facciate faccio facemmo facendo facesse facessero facessi facessimo faceste facesti faceva facevamo facevano facevate facevi facevo fai fanno farai faranno farebbe farebbero farei faremmo faremo fareste faresti farete farà farò fece fecero feci fosse fossero fossi fossimo foste fosti fu fui fummo furono gli ha hai hanno ho i il in io l la le lei li lo loro lui ma mi mia mie miei mio ne negl negli nei nel nell nella nelle nello noi non nostra nostre nostri nostro o per perché più quale quanta quante quanti quanto quella quelle quelli quello questa queste questi questo sarai saranno sarebbe sarebbero sarei saremmo saremo sareste saresti sarete sarà sarò se sei si sia siamo siano siate siete sono sta stai stando stanno starai staranno starebbe starebbero starei staremmo staremo stareste staresti starete starà starò stava stavamo stavano stavate stavi stavo stemmo stesse stessero stessi stessimo steste stesti stette stettero stetti stia stiamo stiano stiate sto su sua sue sugl sugli sui sul sull sulla sulle sullo suo suoi ti tra tu tua tue tuo tuoi tutti tutto un una uno vi voi vostra vostre vostri vostro è".split(" ")),e.Pipeline.registerFunction(e.it.stopWordFilter,"stopWordFilter-it")}}); \ No newline at end of file diff --git a/3.0/site/assets/javascripts/lunr/lunr.jp.js b/3.0/site/assets/javascripts/lunr/lunr.jp.js new file mode 100644 index 00000000..a33c3c71 --- /dev/null +++ b/3.0/site/assets/javascripts/lunr/lunr.jp.js @@ -0,0 +1 @@ +!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var r="2"==e.version[0];e.jp=function(){this.pipeline.reset(),this.pipeline.add(e.jp.stopWordFilter,e.jp.stemmer),r?this.tokenizer=e.jp.tokenizer:(e.tokenizer&&(e.tokenizer=e.jp.tokenizer),this.tokenizerFn&&(this.tokenizerFn=e.jp.tokenizer))};var t=new e.TinySegmenter;e.jp.tokenizer=function(n){if(!arguments.length||null==n||null==n)return[];if(Array.isArray(n))return n.map(function(t){return r?new e.Token(t.toLowerCase()):t.toLowerCase()});for(var i=n.toString().toLowerCase().replace(/^\s+/,""),o=i.length-1;o>=0;o--)if(/\S/.test(i.charAt(o))){i=i.substring(0,o+1);break}return t.segment(i).filter(function(e){return!!e}).map(function(t){return r?new e.Token(t):t})},e.jp.stemmer=function(e){return e},e.Pipeline.registerFunction(e.jp.stemmer,"stemmer-jp"),e.jp.wordCharacters="一二三四五六七八九十百千万億兆一-龠々〆ヵヶぁ-んァ-ヴーア-ン゙a-zA-Za-zA-Z0-90-9",e.jp.stopWordFilter=function(t){if(-1===e.jp.stopWordFilter.stopWords.indexOf(r?t.toString():t))return t},e.jp.stopWordFilter=e.generateStopWordFilter("これ それ あれ この その あの ここ そこ あそこ こちら どこ だれ なに なん 何 私 貴方 貴方方 我々 私達 あの人 あのかた 彼女 彼 です あります おります います は が の に を で え から まで より も どの と し それで しかし".split(" ")),e.Pipeline.registerFunction(e.jp.stopWordFilter,"stopWordFilter-jp")}}); \ No newline at end of file diff --git a/3.0/site/assets/javascripts/lunr/lunr.multi.js b/3.0/site/assets/javascripts/lunr/lunr.multi.js new file mode 100644 index 00000000..d3dbc860 --- /dev/null +++ b/3.0/site/assets/javascripts/lunr/lunr.multi.js @@ -0,0 +1 @@ +!function(e,i){"function"==typeof define&&define.amd?define(i):"object"==typeof exports?module.exports=i():i()(e.lunr)}(this,function(){return function(e){e.multiLanguage=function(){for(var i=Array.prototype.slice.call(arguments),t=i.join("-"),r="",n=[],s=[],p=0;p=l.limit)return;l.cursor=r+1}for(;!l.out_grouping(a,97,248);){if(l.cursor>=l.limit)return;l.cursor++}(i=l.cursor)=i&&(r=l.limit_backward,l.limit_backward=i,l.ket=l.cursor,e=l.find_among_b(t,29),l.limit_backward=r,e))switch(l.bra=l.cursor,e){case 1:l.slice_del();break;case 2:n=l.limit-l.cursor,l.in_grouping_b(m,98,122)?l.slice_del():(l.cursor=l.limit-n,l.eq_s_b(1,"k")&&l.out_grouping_b(a,97,248)&&l.slice_del());break;case 3:l.slice_from("er")}}(),l.cursor=l.limit,n=l.limit-l.cursor,l.cursor>=i&&(r=l.limit_backward,l.limit_backward=i,l.ket=l.cursor,l.find_among_b(o,2)?(l.bra=l.cursor,l.limit_backward=r,l.cursor=l.limit-n,l.cursor>l.limit_backward&&(l.cursor--,l.bra=l.cursor,l.slice_del())):l.limit_backward=r),l.cursor=l.limit,l.cursor>=i&&(d=l.limit_backward,l.limit_backward=i,l.ket=l.cursor,(u=l.find_among_b(s,11))?(l.bra=l.cursor,l.limit_backward=d,1==u&&l.slice_del()):l.limit_backward=d),!0}},function(e){return"function"==typeof e.update?e.update(function(e){return i.setCurrent(e),i.stem(),i.getCurrent()}):(i.setCurrent(e),i.stem(),i.getCurrent())}),e.Pipeline.registerFunction(e.no.stemmer,"stemmer-no"),e.no.stopWordFilter=e.generateStopWordFilter("alle at av bare begge ble blei bli blir blitt både båe da de deg dei deim deira deires dem den denne der dere deres det dette di din disse ditt du dykk dykkar då eg ein eit eitt eller elles en enn er et ett etter for fordi fra før ha hadde han hans har hennar henne hennes her hjå ho hoe honom hoss hossen hun hva hvem hver hvilke hvilken hvis hvor hvordan hvorfor i ikke ikkje ikkje ingen ingi inkje inn inni ja jeg kan kom korleis korso kun kunne kva kvar kvarhelst kven kvi kvifor man mange me med medan meg meget mellom men mi min mine mitt mot mykje ned no noe noen noka noko nokon nokor nokre nå når og også om opp oss over på samme seg selv si si sia sidan siden sin sine sitt sjøl skal skulle slik so som som somme somt så sånn til um upp ut uten var vart varte ved vere verte vi vil ville vore vors vort vår være være vært å".split(" ")),e.Pipeline.registerFunction(e.no.stopWordFilter,"stopWordFilter-no")}}); \ No newline at end of file diff --git a/3.0/site/assets/javascripts/lunr/lunr.pt.js b/3.0/site/assets/javascripts/lunr/lunr.pt.js new file mode 100644 index 00000000..51035c96 --- /dev/null +++ b/3.0/site/assets/javascripts/lunr/lunr.pt.js @@ -0,0 +1 @@ +!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var r,s,n;e.pt=function(){this.pipeline.reset(),this.pipeline.add(e.pt.trimmer,e.pt.stopWordFilter,e.pt.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.pt.stemmer))},e.pt.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.pt.trimmer=e.trimmerSupport.generateTrimmer(e.pt.wordCharacters),e.Pipeline.registerFunction(e.pt.trimmer,"trimmer-pt"),e.pt.stemmer=(r=e.stemmerSupport.Among,s=e.stemmerSupport.SnowballProgram,n=new function(){var e,n,i,o=[new r("",-1,3),new r("ã",0,1),new r("õ",0,2)],a=[new r("",-1,3),new r("a~",0,1),new r("o~",0,2)],t=[new r("ic",-1,-1),new r("ad",-1,-1),new r("os",-1,-1),new r("iv",-1,1)],u=[new r("ante",-1,1),new r("avel",-1,1),new r("ível",-1,1)],w=[new r("ic",-1,1),new r("abil",-1,1),new r("iv",-1,1)],m=[new r("ica",-1,1),new r("ância",-1,1),new r("ência",-1,4),new r("ira",-1,9),new r("adora",-1,1),new r("osa",-1,1),new r("ista",-1,1),new r("iva",-1,8),new r("eza",-1,1),new r("logía",-1,2),new r("idade",-1,7),new r("ante",-1,1),new r("mente",-1,6),new r("amente",12,5),new r("ável",-1,1),new r("ível",-1,1),new r("ución",-1,3),new r("ico",-1,1),new r("ismo",-1,1),new r("oso",-1,1),new r("amento",-1,1),new r("imento",-1,1),new r("ivo",-1,8),new r("aça~o",-1,1),new r("ador",-1,1),new r("icas",-1,1),new r("ências",-1,4),new r("iras",-1,9),new r("adoras",-1,1),new r("osas",-1,1),new r("istas",-1,1),new r("ivas",-1,8),new r("ezas",-1,1),new r("logías",-1,2),new r("idades",-1,7),new r("uciones",-1,3),new r("adores",-1,1),new r("antes",-1,1),new r("aço~es",-1,1),new r("icos",-1,1),new r("ismos",-1,1),new r("osos",-1,1),new r("amentos",-1,1),new r("imentos",-1,1),new r("ivos",-1,8)],c=[new r("ada",-1,1),new r("ida",-1,1),new r("ia",-1,1),new r("aria",2,1),new r("eria",2,1),new r("iria",2,1),new r("ara",-1,1),new r("era",-1,1),new r("ira",-1,1),new r("ava",-1,1),new r("asse",-1,1),new r("esse",-1,1),new r("isse",-1,1),new r("aste",-1,1),new r("este",-1,1),new r("iste",-1,1),new r("ei",-1,1),new r("arei",16,1),new r("erei",16,1),new r("irei",16,1),new r("am",-1,1),new r("iam",20,1),new r("ariam",21,1),new r("eriam",21,1),new r("iriam",21,1),new r("aram",20,1),new r("eram",20,1),new r("iram",20,1),new r("avam",20,1),new r("em",-1,1),new r("arem",29,1),new r("erem",29,1),new r("irem",29,1),new r("assem",29,1),new r("essem",29,1),new r("issem",29,1),new r("ado",-1,1),new r("ido",-1,1),new r("ando",-1,1),new r("endo",-1,1),new r("indo",-1,1),new r("ara~o",-1,1),new r("era~o",-1,1),new r("ira~o",-1,1),new r("ar",-1,1),new r("er",-1,1),new r("ir",-1,1),new r("as",-1,1),new r("adas",47,1),new r("idas",47,1),new r("ias",47,1),new r("arias",50,1),new r("erias",50,1),new r("irias",50,1),new r("aras",47,1),new r("eras",47,1),new r("iras",47,1),new r("avas",47,1),new r("es",-1,1),new r("ardes",58,1),new r("erdes",58,1),new r("irdes",58,1),new r("ares",58,1),new r("eres",58,1),new r("ires",58,1),new r("asses",58,1),new r("esses",58,1),new r("isses",58,1),new r("astes",58,1),new r("estes",58,1),new r("istes",58,1),new r("is",-1,1),new r("ais",71,1),new r("eis",71,1),new r("areis",73,1),new r("ereis",73,1),new r("ireis",73,1),new r("áreis",73,1),new r("éreis",73,1),new r("íreis",73,1),new r("ásseis",73,1),new r("ésseis",73,1),new r("ísseis",73,1),new r("áveis",73,1),new r("íeis",73,1),new r("aríeis",84,1),new r("eríeis",84,1),new r("iríeis",84,1),new r("ados",-1,1),new r("idos",-1,1),new r("amos",-1,1),new r("áramos",90,1),new r("éramos",90,1),new r("íramos",90,1),new r("ávamos",90,1),new r("íamos",90,1),new r("aríamos",95,1),new r("eríamos",95,1),new r("iríamos",95,1),new r("emos",-1,1),new r("aremos",99,1),new r("eremos",99,1),new r("iremos",99,1),new r("ássemos",99,1),new r("êssemos",99,1),new r("íssemos",99,1),new r("imos",-1,1),new r("armos",-1,1),new r("ermos",-1,1),new r("irmos",-1,1),new r("ámos",-1,1),new r("arás",-1,1),new r("erás",-1,1),new r("irás",-1,1),new r("eu",-1,1),new r("iu",-1,1),new r("ou",-1,1),new r("ará",-1,1),new r("erá",-1,1),new r("irá",-1,1)],l=[new r("a",-1,1),new r("i",-1,1),new r("o",-1,1),new r("os",-1,1),new r("á",-1,1),new r("í",-1,1),new r("ó",-1,1)],f=[new r("e",-1,1),new r("ç",-1,2),new r("é",-1,1),new r("ê",-1,1)],d=[17,65,16,0,0,0,0,0,0,0,0,0,0,0,0,0,3,19,12,2],v=new s;function p(){if(v.out_grouping(d,97,250)){for(;!v.in_grouping(d,97,250);){if(v.cursor>=v.limit)return!0;v.cursor++}return!1}return!0}function _(){var e,r,s=v.cursor;if(v.in_grouping(d,97,250))if(e=v.cursor,p()){if(v.cursor=e,function(){if(v.in_grouping(d,97,250))for(;!v.out_grouping(d,97,250);){if(v.cursor>=v.limit)return!1;v.cursor++}return i=v.cursor,!0}())return}else i=v.cursor;if(v.cursor=s,v.out_grouping(d,97,250)){if(r=v.cursor,p()){if(v.cursor=r,!v.in_grouping(d,97,250)||v.cursor>=v.limit)return;v.cursor++}i=v.cursor}}function h(){for(;!v.in_grouping(d,97,250);){if(v.cursor>=v.limit)return!1;v.cursor++}for(;!v.out_grouping(d,97,250);){if(v.cursor>=v.limit)return!1;v.cursor++}return!0}function b(){return i<=v.cursor}function g(){return e<=v.cursor}function k(){var e;if(v.ket=v.cursor,!(e=v.find_among_b(m,45)))return!1;switch(v.bra=v.cursor,e){case 1:if(!g())return!1;v.slice_del();break;case 2:if(!g())return!1;v.slice_from("log");break;case 3:if(!g())return!1;v.slice_from("u");break;case 4:if(!g())return!1;v.slice_from("ente");break;case 5:if(!(n<=v.cursor))return!1;v.slice_del(),v.ket=v.cursor,(e=v.find_among_b(t,4))&&(v.bra=v.cursor,g()&&(v.slice_del(),1==e&&(v.ket=v.cursor,v.eq_s_b(2,"at")&&(v.bra=v.cursor,g()&&v.slice_del()))));break;case 6:if(!g())return!1;v.slice_del(),v.ket=v.cursor,(e=v.find_among_b(u,3))&&(v.bra=v.cursor,1==e&&g()&&v.slice_del());break;case 7:if(!g())return!1;v.slice_del(),v.ket=v.cursor,(e=v.find_among_b(w,3))&&(v.bra=v.cursor,1==e&&g()&&v.slice_del());break;case 8:if(!g())return!1;v.slice_del(),v.ket=v.cursor,v.eq_s_b(2,"at")&&(v.bra=v.cursor,g()&&v.slice_del());break;case 9:if(!b()||!v.eq_s_b(1,"e"))return!1;v.slice_from("ir")}return!0}function q(e,r){if(v.eq_s_b(1,e)){v.bra=v.cursor;var s=v.limit-v.cursor;if(v.eq_s_b(1,r))return v.cursor=v.limit-s,b()&&v.slice_del(),!1}return!0}function j(){if(!k()&&(v.cursor=v.limit,!function(){var e,r;if(v.cursor>=i){if(r=v.limit_backward,v.limit_backward=i,v.ket=v.cursor,e=v.find_among_b(c,120))return v.bra=v.cursor,1==e&&v.slice_del(),v.limit_backward=r,!0;v.limit_backward=r}return!1}()))return v.cursor=v.limit,v.ket=v.cursor,void((e=v.find_among_b(l,7))&&(v.bra=v.cursor,1==e&&b()&&v.slice_del()));var e;v.cursor=v.limit,v.ket=v.cursor,v.eq_s_b(1,"i")&&(v.bra=v.cursor,v.eq_s_b(1,"c")&&(v.cursor=v.limit,b()&&v.slice_del()))}this.setCurrent=function(e){v.setCurrent(e)},this.getCurrent=function(){return v.getCurrent()},this.stem=function(){var r,s=v.cursor;return function(){for(var e;;){if(v.bra=v.cursor,e=v.find_among(o,3))switch(v.ket=v.cursor,e){case 1:v.slice_from("a~");continue;case 2:v.slice_from("o~");continue;case 3:if(v.cursor>=v.limit)break;v.cursor++;continue}break}}(),v.cursor=s,r=v.cursor,i=v.limit,n=i,e=i,_(),v.cursor=r,h()&&(n=v.cursor,h()&&(e=v.cursor)),v.limit_backward=s,v.cursor=v.limit,j(),v.cursor=v.limit,function(){var e;if(v.ket=v.cursor,e=v.find_among_b(f,4))switch(v.bra=v.cursor,e){case 1:b()&&(v.slice_del(),v.ket=v.cursor,v.limit,v.cursor,q("u","g")&&q("i","c"));break;case 2:v.slice_from("c")}}(),v.cursor=v.limit_backward,function(){for(var e;;){if(v.bra=v.cursor,e=v.find_among(a,3))switch(v.ket=v.cursor,e){case 1:v.slice_from("ã");continue;case 2:v.slice_from("õ");continue;case 3:if(v.cursor>=v.limit)break;v.cursor++;continue}break}}(),!0}},function(e){return"function"==typeof e.update?e.update(function(e){return n.setCurrent(e),n.stem(),n.getCurrent()}):(n.setCurrent(e),n.stem(),n.getCurrent())}),e.Pipeline.registerFunction(e.pt.stemmer,"stemmer-pt"),e.pt.stopWordFilter=e.generateStopWordFilter("a ao aos aquela aquelas aquele aqueles aquilo as até com como da das de dela delas dele deles depois do dos e ela elas ele eles em entre era eram essa essas esse esses esta estamos estas estava estavam este esteja estejam estejamos estes esteve estive estivemos estiver estivera estiveram estiverem estivermos estivesse estivessem estivéramos estivéssemos estou está estávamos estão eu foi fomos for fora foram forem formos fosse fossem fui fôramos fôssemos haja hajam hajamos havemos hei houve houvemos houver houvera houveram houverei houverem houveremos houveria houveriam houvermos houverá houverão houveríamos houvesse houvessem houvéramos houvéssemos há hão isso isto já lhe lhes mais mas me mesmo meu meus minha minhas muito na nas nem no nos nossa nossas nosso nossos num numa não nós o os ou para pela pelas pelo pelos por qual quando que quem se seja sejam sejamos sem serei seremos seria seriam será serão seríamos seu seus somos sou sua suas são só também te tem temos tenha tenham tenhamos tenho terei teremos teria teriam terá terão teríamos teu teus teve tinha tinham tive tivemos tiver tivera tiveram tiverem tivermos tivesse tivessem tivéramos tivéssemos tu tua tuas tém tínhamos um uma você vocês vos à às éramos".split(" ")),e.Pipeline.registerFunction(e.pt.stopWordFilter,"stopWordFilter-pt")}}); \ No newline at end of file diff --git a/3.0/site/assets/javascripts/lunr/lunr.ro.js b/3.0/site/assets/javascripts/lunr/lunr.ro.js new file mode 100644 index 00000000..155cb562 --- /dev/null +++ b/3.0/site/assets/javascripts/lunr/lunr.ro.js @@ -0,0 +1 @@ +!function(e,i){"function"==typeof define&&define.amd?define(i):"object"==typeof exports?module.exports=i():i()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var i,r,n;e.ro=function(){this.pipeline.reset(),this.pipeline.add(e.ro.trimmer,e.ro.stopWordFilter,e.ro.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.ro.stemmer))},e.ro.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.ro.trimmer=e.trimmerSupport.generateTrimmer(e.ro.wordCharacters),e.Pipeline.registerFunction(e.ro.trimmer,"trimmer-ro"),e.ro.stemmer=(i=e.stemmerSupport.Among,r=e.stemmerSupport.SnowballProgram,n=new function(){var e,n,t,a,o=[new i("",-1,3),new i("I",0,1),new i("U",0,2)],s=[new i("ea",-1,3),new i("aţia",-1,7),new i("aua",-1,2),new i("iua",-1,4),new i("aţie",-1,7),new i("ele",-1,3),new i("ile",-1,5),new i("iile",6,4),new i("iei",-1,4),new i("atei",-1,6),new i("ii",-1,4),new i("ului",-1,1),new i("ul",-1,1),new i("elor",-1,3),new i("ilor",-1,4),new i("iilor",14,4)],c=[new i("icala",-1,4),new i("iciva",-1,4),new i("ativa",-1,5),new i("itiva",-1,6),new i("icale",-1,4),new i("aţiune",-1,5),new i("iţiune",-1,6),new i("atoare",-1,5),new i("itoare",-1,6),new i("ătoare",-1,5),new i("icitate",-1,4),new i("abilitate",-1,1),new i("ibilitate",-1,2),new i("ivitate",-1,3),new i("icive",-1,4),new i("ative",-1,5),new i("itive",-1,6),new i("icali",-1,4),new i("atori",-1,5),new i("icatori",18,4),new i("itori",-1,6),new i("ători",-1,5),new i("icitati",-1,4),new i("abilitati",-1,1),new i("ivitati",-1,3),new i("icivi",-1,4),new i("ativi",-1,5),new i("itivi",-1,6),new i("icităi",-1,4),new i("abilităi",-1,1),new i("ivităi",-1,3),new i("icităţi",-1,4),new i("abilităţi",-1,1),new i("ivităţi",-1,3),new i("ical",-1,4),new i("ator",-1,5),new i("icator",35,4),new i("itor",-1,6),new i("ător",-1,5),new i("iciv",-1,4),new i("ativ",-1,5),new i("itiv",-1,6),new i("icală",-1,4),new i("icivă",-1,4),new i("ativă",-1,5),new i("itivă",-1,6)],u=[new i("ica",-1,1),new i("abila",-1,1),new i("ibila",-1,1),new i("oasa",-1,1),new i("ata",-1,1),new i("ita",-1,1),new i("anta",-1,1),new i("ista",-1,3),new i("uta",-1,1),new i("iva",-1,1),new i("ic",-1,1),new i("ice",-1,1),new i("abile",-1,1),new i("ibile",-1,1),new i("isme",-1,3),new i("iune",-1,2),new i("oase",-1,1),new i("ate",-1,1),new i("itate",17,1),new i("ite",-1,1),new i("ante",-1,1),new i("iste",-1,3),new i("ute",-1,1),new i("ive",-1,1),new i("ici",-1,1),new i("abili",-1,1),new i("ibili",-1,1),new i("iuni",-1,2),new i("atori",-1,1),new i("osi",-1,1),new i("ati",-1,1),new i("itati",30,1),new i("iti",-1,1),new i("anti",-1,1),new i("isti",-1,3),new i("uti",-1,1),new i("işti",-1,3),new i("ivi",-1,1),new i("ităi",-1,1),new i("oşi",-1,1),new i("ităţi",-1,1),new i("abil",-1,1),new i("ibil",-1,1),new i("ism",-1,3),new i("ator",-1,1),new i("os",-1,1),new i("at",-1,1),new i("it",-1,1),new i("ant",-1,1),new i("ist",-1,3),new i("ut",-1,1),new i("iv",-1,1),new i("ică",-1,1),new i("abilă",-1,1),new i("ibilă",-1,1),new i("oasă",-1,1),new i("ată",-1,1),new i("ită",-1,1),new i("antă",-1,1),new i("istă",-1,3),new i("ută",-1,1),new i("ivă",-1,1)],w=[new i("ea",-1,1),new i("ia",-1,1),new i("esc",-1,1),new i("ăsc",-1,1),new i("ind",-1,1),new i("ând",-1,1),new i("are",-1,1),new i("ere",-1,1),new i("ire",-1,1),new i("âre",-1,1),new i("se",-1,2),new i("ase",10,1),new i("sese",10,2),new i("ise",10,1),new i("use",10,1),new i("âse",10,1),new i("eşte",-1,1),new i("ăşte",-1,1),new i("eze",-1,1),new i("ai",-1,1),new i("eai",19,1),new i("iai",19,1),new i("sei",-1,2),new i("eşti",-1,1),new i("ăşti",-1,1),new i("ui",-1,1),new i("ezi",-1,1),new i("âi",-1,1),new i("aşi",-1,1),new i("seşi",-1,2),new i("aseşi",29,1),new i("seseşi",29,2),new i("iseşi",29,1),new i("useşi",29,1),new i("âseşi",29,1),new i("işi",-1,1),new i("uşi",-1,1),new i("âşi",-1,1),new i("aţi",-1,2),new i("eaţi",38,1),new i("iaţi",38,1),new i("eţi",-1,2),new i("iţi",-1,2),new i("âţi",-1,2),new i("arăţi",-1,1),new i("serăţi",-1,2),new i("aserăţi",45,1),new i("seserăţi",45,2),new i("iserăţi",45,1),new i("userăţi",45,1),new i("âserăţi",45,1),new i("irăţi",-1,1),new i("urăţi",-1,1),new i("ârăţi",-1,1),new i("am",-1,1),new i("eam",54,1),new i("iam",54,1),new i("em",-1,2),new i("asem",57,1),new i("sesem",57,2),new i("isem",57,1),new i("usem",57,1),new i("âsem",57,1),new i("im",-1,2),new i("âm",-1,2),new i("ăm",-1,2),new i("arăm",65,1),new i("serăm",65,2),new i("aserăm",67,1),new i("seserăm",67,2),new i("iserăm",67,1),new i("userăm",67,1),new i("âserăm",67,1),new i("irăm",65,1),new i("urăm",65,1),new i("ârăm",65,1),new i("au",-1,1),new i("eau",76,1),new i("iau",76,1),new i("indu",-1,1),new i("ându",-1,1),new i("ez",-1,1),new i("ească",-1,1),new i("ară",-1,1),new i("seră",-1,2),new i("aseră",84,1),new i("seseră",84,2),new i("iseră",84,1),new i("useră",84,1),new i("âseră",84,1),new i("iră",-1,1),new i("ură",-1,1),new i("âră",-1,1),new i("ează",-1,1)],m=[new i("a",-1,1),new i("e",-1,1),new i("ie",1,1),new i("i",-1,1),new i("ă",-1,1)],l=[17,65,16,0,0,0,0,0,0,0,0,0,0,0,0,0,2,32,0,0,4],f=new r;function p(e,i){f.eq_s(1,e)&&(f.ket=f.cursor,f.in_grouping(l,97,259)&&f.slice_from(i))}function d(){if(f.out_grouping(l,97,259)){for(;!f.in_grouping(l,97,259);){if(f.cursor>=f.limit)return!0;f.cursor++}return!1}return!0}function b(){var e,i,r=f.cursor;if(f.in_grouping(l,97,259)){if(e=f.cursor,!d())return void(a=f.cursor);if(f.cursor=e,!function(){if(f.in_grouping(l,97,259))for(;!f.out_grouping(l,97,259);){if(f.cursor>=f.limit)return!0;f.cursor++}return!1}())return void(a=f.cursor)}f.cursor=r,f.out_grouping(l,97,259)&&(i=f.cursor,d()&&(f.cursor=i,f.in_grouping(l,97,259)&&f.cursor=f.limit)return!1;f.cursor++}for(;!f.out_grouping(l,97,259);){if(f.cursor>=f.limit)return!1;f.cursor++}return!0}function _(){return t<=f.cursor}function g(){var i,r=f.limit-f.cursor;if(f.ket=f.cursor,(i=f.find_among_b(c,46))&&(f.bra=f.cursor,_())){switch(i){case 1:f.slice_from("abil");break;case 2:f.slice_from("ibil");break;case 3:f.slice_from("iv");break;case 4:f.slice_from("ic");break;case 5:f.slice_from("at");break;case 6:f.slice_from("it")}return e=!0,f.cursor=f.limit-r,!0}return!1}function k(){var i,r;for(e=!1;;)if(r=f.limit-f.cursor,!g()){f.cursor=f.limit-r;break}if(f.ket=f.cursor,(i=f.find_among_b(u,62))&&(f.bra=f.cursor,n<=f.cursor)){switch(i){case 1:f.slice_del();break;case 2:f.eq_s_b(1,"ţ")&&(f.bra=f.cursor,f.slice_from("t"));break;case 3:f.slice_from("ist")}e=!0}}function h(){var e;f.ket=f.cursor,(e=f.find_among_b(m,5))&&(f.bra=f.cursor,a<=f.cursor&&1==e&&f.slice_del())}this.setCurrent=function(e){f.setCurrent(e)},this.getCurrent=function(){return f.getCurrent()},this.stem=function(){var i,r=f.cursor;return function(){for(var e,i;e=f.cursor,f.in_grouping(l,97,259)&&(i=f.cursor,f.bra=i,p("u","U"),f.cursor=i,p("i","I")),f.cursor=e,!(f.cursor>=f.limit);)f.cursor++}(),f.cursor=r,i=f.cursor,a=f.limit,t=a,n=a,b(),f.cursor=i,v()&&(t=f.cursor,v()&&(n=f.cursor)),f.limit_backward=r,f.cursor=f.limit,function(){var e,i;if(f.ket=f.cursor,(e=f.find_among_b(s,16))&&(f.bra=f.cursor,_()))switch(e){case 1:f.slice_del();break;case 2:f.slice_from("a");break;case 3:f.slice_from("e");break;case 4:f.slice_from("i");break;case 5:i=f.limit-f.cursor,f.eq_s_b(2,"ab")||(f.cursor=f.limit-i,f.slice_from("i"));break;case 6:f.slice_from("at");break;case 7:f.slice_from("aţi")}}(),f.cursor=f.limit,k(),f.cursor=f.limit,e||(f.cursor=f.limit,function(){var e,i,r;if(f.cursor>=a){if(i=f.limit_backward,f.limit_backward=a,f.ket=f.cursor,e=f.find_among_b(w,94))switch(f.bra=f.cursor,e){case 1:if(r=f.limit-f.cursor,!f.out_grouping_b(l,97,259)&&(f.cursor=f.limit-r,!f.eq_s_b(1,"u")))break;case 2:f.slice_del()}f.limit_backward=i}}(),f.cursor=f.limit),h(),f.cursor=f.limit_backward,function(){for(var e;;){if(f.bra=f.cursor,e=f.find_among(o,3))switch(f.ket=f.cursor,e){case 1:f.slice_from("i");continue;case 2:f.slice_from("u");continue;case 3:if(f.cursor>=f.limit)break;f.cursor++;continue}break}}(),!0}},function(e){return"function"==typeof e.update?e.update(function(e){return n.setCurrent(e),n.stem(),n.getCurrent()}):(n.setCurrent(e),n.stem(),n.getCurrent())}),e.Pipeline.registerFunction(e.ro.stemmer,"stemmer-ro"),e.ro.stopWordFilter=e.generateStopWordFilter("acea aceasta această aceea acei aceia acel acela acele acelea acest acesta aceste acestea aceşti aceştia acolo acord acum ai aia aibă aici al ale alea altceva altcineva am ar are asemenea asta astea astăzi asupra au avea avem aveţi azi aş aşadar aţi bine bucur bună ca care caut ce cel ceva chiar cinci cine cineva contra cu cum cumva curând curînd când cât câte câtva câţi cînd cît cîte cîtva cîţi că căci cărei căror cărui către da dacă dar datorită dată dau de deci deja deoarece departe deşi din dinaintea dintr- dintre doi doilea două drept după dă ea ei el ele eram este eu eşti face fata fi fie fiecare fii fim fiu fiţi frumos fără graţie halbă iar ieri la le li lor lui lângă lîngă mai mea mei mele mereu meu mi mie mine mult multă mulţi mulţumesc mâine mîine mă ne nevoie nici nicăieri nimeni nimeri nimic nişte noastre noastră noi noroc nostru nouă noştri nu opt ori oricare orice oricine oricum oricând oricât oricînd oricît oriunde patra patru patrulea pe pentru peste pic poate pot prea prima primul prin puţin puţina puţină până pînă rog sa sale sau se spate spre sub sunt suntem sunteţi sută sînt sîntem sînteţi să săi său ta tale te timp tine toate toată tot totuşi toţi trei treia treilea tu tăi tău un una unde undeva unei uneia unele uneori unii unor unora unu unui unuia unul vi voastre voastră voi vostru vouă voştri vreme vreo vreun vă zece zero zi zice îi îl îmi împotriva în înainte înaintea încotro încât încît între întrucât întrucît îţi ăla ălea ăsta ăstea ăştia şapte şase şi ştiu ţi ţie".split(" ")),e.Pipeline.registerFunction(e.ro.stopWordFilter,"stopWordFilter-ro")}}); \ No newline at end of file diff --git a/3.0/site/assets/javascripts/lunr/lunr.ru.js b/3.0/site/assets/javascripts/lunr/lunr.ru.js new file mode 100644 index 00000000..078609ad --- /dev/null +++ b/3.0/site/assets/javascripts/lunr/lunr.ru.js @@ -0,0 +1 @@ +!function(e,n){"function"==typeof define&&define.amd?define(n):"object"==typeof exports?module.exports=n():n()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var n,r,t;e.ru=function(){this.pipeline.reset(),this.pipeline.add(e.ru.trimmer,e.ru.stopWordFilter,e.ru.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.ru.stemmer))},e.ru.wordCharacters="Ѐ-҄҇-ԯᴫᵸⷠ-ⷿꙀ-ꚟ︮︯",e.ru.trimmer=e.trimmerSupport.generateTrimmer(e.ru.wordCharacters),e.Pipeline.registerFunction(e.ru.trimmer,"trimmer-ru"),e.ru.stemmer=(n=e.stemmerSupport.Among,r=e.stemmerSupport.SnowballProgram,t=new function(){var e,t,w=[new n("в",-1,1),new n("ив",0,2),new n("ыв",0,2),new n("вши",-1,1),new n("ивши",3,2),new n("ывши",3,2),new n("вшись",-1,1),new n("ившись",6,2),new n("ывшись",6,2)],i=[new n("ее",-1,1),new n("ие",-1,1),new n("ое",-1,1),new n("ые",-1,1),new n("ими",-1,1),new n("ыми",-1,1),new n("ей",-1,1),new n("ий",-1,1),new n("ой",-1,1),new n("ый",-1,1),new n("ем",-1,1),new n("им",-1,1),new n("ом",-1,1),new n("ым",-1,1),new n("его",-1,1),new n("ого",-1,1),new n("ему",-1,1),new n("ому",-1,1),new n("их",-1,1),new n("ых",-1,1),new n("ею",-1,1),new n("ою",-1,1),new n("ую",-1,1),new n("юю",-1,1),new n("ая",-1,1),new n("яя",-1,1)],u=[new n("ем",-1,1),new n("нн",-1,1),new n("вш",-1,1),new n("ивш",2,2),new n("ывш",2,2),new n("щ",-1,1),new n("ющ",5,1),new n("ующ",6,2)],s=[new n("сь",-1,1),new n("ся",-1,1)],o=[new n("ла",-1,1),new n("ила",0,2),new n("ыла",0,2),new n("на",-1,1),new n("ена",3,2),new n("ете",-1,1),new n("ите",-1,2),new n("йте",-1,1),new n("ейте",7,2),new n("уйте",7,2),new n("ли",-1,1),new n("или",10,2),new n("ыли",10,2),new n("й",-1,1),new n("ей",13,2),new n("уй",13,2),new n("л",-1,1),new n("ил",16,2),new n("ыл",16,2),new n("ем",-1,1),new n("им",-1,2),new n("ым",-1,2),new n("н",-1,1),new n("ен",22,2),new n("ло",-1,1),new n("ило",24,2),new n("ыло",24,2),new n("но",-1,1),new n("ено",27,2),new n("нно",27,1),new n("ет",-1,1),new n("ует",30,2),new n("ит",-1,2),new n("ыт",-1,2),new n("ют",-1,1),new n("уют",34,2),new n("ят",-1,2),new n("ны",-1,1),new n("ены",37,2),new n("ть",-1,1),new n("ить",39,2),new n("ыть",39,2),new n("ешь",-1,1),new n("ишь",-1,2),new n("ю",-1,2),new n("ую",44,2)],c=[new n("а",-1,1),new n("ев",-1,1),new n("ов",-1,1),new n("е",-1,1),new n("ие",3,1),new n("ье",3,1),new n("и",-1,1),new n("еи",6,1),new n("ии",6,1),new n("ами",6,1),new n("ями",6,1),new n("иями",10,1),new n("й",-1,1),new n("ей",12,1),new n("ией",13,1),new n("ий",12,1),new n("ой",12,1),new n("ам",-1,1),new n("ем",-1,1),new n("ием",18,1),new n("ом",-1,1),new n("ям",-1,1),new n("иям",21,1),new n("о",-1,1),new n("у",-1,1),new n("ах",-1,1),new n("ях",-1,1),new n("иях",26,1),new n("ы",-1,1),new n("ь",-1,1),new n("ю",-1,1),new n("ию",30,1),new n("ью",30,1),new n("я",-1,1),new n("ия",33,1),new n("ья",33,1)],m=[new n("ост",-1,1),new n("ость",-1,1)],l=[new n("ейше",-1,1),new n("н",-1,2),new n("ейш",-1,1),new n("ь",-1,3)],f=[33,65,8,232],a=new r;function p(){for(;!a.in_grouping(f,1072,1103);){if(a.cursor>=a.limit)return!1;a.cursor++}return!0}function d(){for(;!a.out_grouping(f,1072,1103);){if(a.cursor>=a.limit)return!1;a.cursor++}return!0}function _(e,n){var r,t;if(a.ket=a.cursor,r=a.find_among_b(e,n)){switch(a.bra=a.cursor,r){case 1:if(t=a.limit-a.cursor,!a.eq_s_b(1,"а")&&(a.cursor=a.limit-t,!a.eq_s_b(1,"я")))return!1;case 2:a.slice_del()}return!0}return!1}function b(e,n){var r;return a.ket=a.cursor,!!(r=a.find_among_b(e,n))&&(a.bra=a.cursor,1==r&&a.slice_del(),!0)}function h(){return!!b(i,26)&&(_(u,8),!0)}function g(){var n;a.ket=a.cursor,(n=a.find_among_b(m,2))&&(a.bra=a.cursor,e<=a.cursor&&1==n&&a.slice_del())}this.setCurrent=function(e){a.setCurrent(e)},this.getCurrent=function(){return a.getCurrent()},this.stem=function(){return t=a.limit,e=t,p()&&(t=a.cursor,d()&&p()&&d()&&(e=a.cursor)),a.cursor=a.limit,!(a.cursor=i&&t[(e-=i)>>3]&1<<(7&e))return this.cursor++,!0}return!1},in_grouping_b:function(t,i,s){if(this.cursor>this.limit_backward){var e=r.charCodeAt(this.cursor-1);if(e<=s&&e>=i&&t[(e-=i)>>3]&1<<(7&e))return this.cursor--,!0}return!1},out_grouping:function(t,i,s){if(this.cursors||e>3]&1<<(7&e)))return this.cursor++,!0}return!1},out_grouping_b:function(t,i,s){if(this.cursor>this.limit_backward){var e=r.charCodeAt(this.cursor-1);if(e>s||e>3]&1<<(7&e)))return this.cursor--,!0}return!1},eq_s:function(t,i){if(this.limit-this.cursor>1),f=0,l=o0||e==s||c)break;c=!0}}for(;;){if(o>=(_=t[s]).s_size){if(this.cursor=n+_.s_size,!_.method)return _.result;var b=_.method();if(this.cursor=n+_.s_size,b)return _.result}if((s=_.substring_i)<0)return 0}},find_among_b:function(t,i){for(var s=0,e=i,n=this.cursor,u=this.limit_backward,o=0,h=0,c=!1;;){for(var a=s+(e-s>>1),f=0,l=o=0;_--){if(n-l==u){f=-1;break}if(f=r.charCodeAt(n-1-l)-m.s[_])break;l++}if(f<0?(e=a,h=l):(s=a,o=l),e-s<=1){if(s>0||e==s||c)break;c=!0}}for(;;){var m;if(o>=(m=t[s]).s_size){if(this.cursor=n-m.s_size,!m.method)return m.result;var b=m.method();if(this.cursor=n-m.s_size,b)return m.result}if((s=m.substring_i)<0)return 0}},replace_s:function(t,i,s){var e=s.length-(i-t),n=r.substring(0,t),u=r.substring(i);return r=n+s+u,this.limit+=e,this.cursor>=i?this.cursor+=e:this.cursor>t&&(this.cursor=t),e},slice_check:function(){if(this.bra<0||this.bra>this.ket||this.ket>this.limit||this.limit>r.length)throw"faulty slice operation"},slice_from:function(r){this.slice_check(),this.replace_s(this.bra,this.ket,r)},slice_del:function(){this.slice_from("")},insert:function(r,t,i){var s=this.replace_s(r,t,i);r<=this.bra&&(this.bra+=s),r<=this.ket&&(this.ket+=s)},slice_to:function(){return this.slice_check(),r.substring(this.bra,this.ket)},eq_v_b:function(r){return this.eq_s_b(r.length,r)}}}},r.trimmerSupport={generateTrimmer:function(r){var t=new RegExp("^[^"+r+"]+"),i=new RegExp("[^"+r+"]+$");return function(r){return"function"==typeof r.update?r.update(function(r){return r.replace(t,"").replace(i,"")}):r.replace(t,"").replace(i,"")}}}}}); \ No newline at end of file diff --git a/3.0/site/assets/javascripts/lunr/lunr.sv.js b/3.0/site/assets/javascripts/lunr/lunr.sv.js new file mode 100644 index 00000000..4bb0f9f9 --- /dev/null +++ b/3.0/site/assets/javascripts/lunr/lunr.sv.js @@ -0,0 +1 @@ +!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var r,n,t;e.sv=function(){this.pipeline.reset(),this.pipeline.add(e.sv.trimmer,e.sv.stopWordFilter,e.sv.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.sv.stemmer))},e.sv.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.sv.trimmer=e.trimmerSupport.generateTrimmer(e.sv.wordCharacters),e.Pipeline.registerFunction(e.sv.trimmer,"trimmer-sv"),e.sv.stemmer=(r=e.stemmerSupport.Among,n=e.stemmerSupport.SnowballProgram,t=new function(){var e,t,i=[new r("a",-1,1),new r("arna",0,1),new r("erna",0,1),new r("heterna",2,1),new r("orna",0,1),new r("ad",-1,1),new r("e",-1,1),new r("ade",6,1),new r("ande",6,1),new r("arne",6,1),new r("are",6,1),new r("aste",6,1),new r("en",-1,1),new r("anden",12,1),new r("aren",12,1),new r("heten",12,1),new r("ern",-1,1),new r("ar",-1,1),new r("er",-1,1),new r("heter",18,1),new r("or",-1,1),new r("s",-1,2),new r("as",21,1),new r("arnas",22,1),new r("ernas",22,1),new r("ornas",22,1),new r("es",21,1),new r("ades",26,1),new r("andes",26,1),new r("ens",21,1),new r("arens",29,1),new r("hetens",29,1),new r("erns",21,1),new r("at",-1,1),new r("andet",-1,1),new r("het",-1,1),new r("ast",-1,1)],s=[new r("dd",-1,-1),new r("gd",-1,-1),new r("nn",-1,-1),new r("dt",-1,-1),new r("gt",-1,-1),new r("kt",-1,-1),new r("tt",-1,-1)],a=[new r("ig",-1,1),new r("lig",0,1),new r("els",-1,1),new r("fullt",-1,3),new r("löst",-1,2)],o=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,24,0,32],u=[119,127,149],m=new n;this.setCurrent=function(e){m.setCurrent(e)},this.getCurrent=function(){return m.getCurrent()},this.stem=function(){var r,n=m.cursor;return function(){var r,n=m.cursor+3;if(t=m.limit,0<=n||n<=m.limit){for(e=n;;){if(r=m.cursor,m.in_grouping(o,97,246)){m.cursor=r;break}if(m.cursor=r,m.cursor>=m.limit)return;m.cursor++}for(;!m.out_grouping(o,97,246);){if(m.cursor>=m.limit)return;m.cursor++}(t=m.cursor)=t&&(m.limit_backward=t,m.cursor=m.limit,m.ket=m.cursor,e=m.find_among_b(i,37),m.limit_backward=r,e))switch(m.bra=m.cursor,e){case 1:m.slice_del();break;case 2:m.in_grouping_b(u,98,121)&&m.slice_del()}}(),m.cursor=m.limit,r=m.limit_backward,m.cursor>=t&&(m.limit_backward=t,m.cursor=m.limit,m.find_among_b(s,7)&&(m.cursor=m.limit,m.ket=m.cursor,m.cursor>m.limit_backward&&(m.bra=--m.cursor,m.slice_del())),m.limit_backward=r),m.cursor=m.limit,function(){var e,r;if(m.cursor>=t){if(r=m.limit_backward,m.limit_backward=t,m.cursor=m.limit,m.ket=m.cursor,e=m.find_among_b(a,5))switch(m.bra=m.cursor,e){case 1:m.slice_del();break;case 2:m.slice_from("lös");break;case 3:m.slice_from("full")}m.limit_backward=r}}(),!0}},function(e){return"function"==typeof e.update?e.update(function(e){return t.setCurrent(e),t.stem(),t.getCurrent()}):(t.setCurrent(e),t.stem(),t.getCurrent())}),e.Pipeline.registerFunction(e.sv.stemmer,"stemmer-sv"),e.sv.stopWordFilter=e.generateStopWordFilter("alla allt att av blev bli blir blivit de dem den denna deras dess dessa det detta dig din dina ditt du där då efter ej eller en er era ert ett från för ha hade han hans har henne hennes hon honom hur här i icke ingen inom inte jag ju kan kunde man med mellan men mig min mina mitt mot mycket ni nu när någon något några och om oss på samma sedan sig sin sina sitta själv skulle som så sådan sådana sådant till under upp ut utan vad var vara varför varit varje vars vart vem vi vid vilka vilkas vilken vilket vår våra vårt än är åt över".split(" ")),e.Pipeline.registerFunction(e.sv.stopWordFilter,"stopWordFilter-sv")}}); \ No newline at end of file diff --git a/3.0/site/assets/javascripts/lunr/lunr.tr.js b/3.0/site/assets/javascripts/lunr/lunr.tr.js new file mode 100644 index 00000000..c42b349e --- /dev/null +++ b/3.0/site/assets/javascripts/lunr/lunr.tr.js @@ -0,0 +1 @@ +!function(r,i){"function"==typeof define&&define.amd?define(i):"object"==typeof exports?module.exports=i():i()(r.lunr)}(this,function(){return function(r){if(void 0===r)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===r.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var i,e,n;r.tr=function(){this.pipeline.reset(),this.pipeline.add(r.tr.trimmer,r.tr.stopWordFilter,r.tr.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(r.tr.stemmer))},r.tr.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",r.tr.trimmer=r.trimmerSupport.generateTrimmer(r.tr.wordCharacters),r.Pipeline.registerFunction(r.tr.trimmer,"trimmer-tr"),r.tr.stemmer=(i=r.stemmerSupport.Among,e=r.stemmerSupport.SnowballProgram,n=new function(){var r,n=[new i("m",-1,-1),new i("n",-1,-1),new i("miz",-1,-1),new i("niz",-1,-1),new i("muz",-1,-1),new i("nuz",-1,-1),new i("müz",-1,-1),new i("nüz",-1,-1),new i("mız",-1,-1),new i("nız",-1,-1)],t=[new i("leri",-1,-1),new i("ları",-1,-1)],u=[new i("ni",-1,-1),new i("nu",-1,-1),new i("nü",-1,-1),new i("nı",-1,-1)],o=[new i("in",-1,-1),new i("un",-1,-1),new i("ün",-1,-1),new i("ın",-1,-1)],s=[new i("a",-1,-1),new i("e",-1,-1)],c=[new i("na",-1,-1),new i("ne",-1,-1)],l=[new i("da",-1,-1),new i("ta",-1,-1),new i("de",-1,-1),new i("te",-1,-1)],a=[new i("nda",-1,-1),new i("nde",-1,-1)],m=[new i("dan",-1,-1),new i("tan",-1,-1),new i("den",-1,-1),new i("ten",-1,-1)],d=[new i("ndan",-1,-1),new i("nden",-1,-1)],f=[new i("la",-1,-1),new i("le",-1,-1)],b=[new i("ca",-1,-1),new i("ce",-1,-1)],w=[new i("im",-1,-1),new i("um",-1,-1),new i("üm",-1,-1),new i("ım",-1,-1)],_=[new i("sin",-1,-1),new i("sun",-1,-1),new i("sün",-1,-1),new i("sın",-1,-1)],k=[new i("iz",-1,-1),new i("uz",-1,-1),new i("üz",-1,-1),new i("ız",-1,-1)],p=[new i("siniz",-1,-1),new i("sunuz",-1,-1),new i("sünüz",-1,-1),new i("sınız",-1,-1)],g=[new i("lar",-1,-1),new i("ler",-1,-1)],y=[new i("niz",-1,-1),new i("nuz",-1,-1),new i("nüz",-1,-1),new i("nız",-1,-1)],z=[new i("dir",-1,-1),new i("tir",-1,-1),new i("dur",-1,-1),new i("tur",-1,-1),new i("dür",-1,-1),new i("tür",-1,-1),new i("dır",-1,-1),new i("tır",-1,-1)],h=[new i("casına",-1,-1),new i("cesine",-1,-1)],v=[new i("di",-1,-1),new i("ti",-1,-1),new i("dik",-1,-1),new i("tik",-1,-1),new i("duk",-1,-1),new i("tuk",-1,-1),new i("dük",-1,-1),new i("tük",-1,-1),new i("dık",-1,-1),new i("tık",-1,-1),new i("dim",-1,-1),new i("tim",-1,-1),new i("dum",-1,-1),new i("tum",-1,-1),new i("düm",-1,-1),new i("tüm",-1,-1),new i("dım",-1,-1),new i("tım",-1,-1),new i("din",-1,-1),new i("tin",-1,-1),new i("dun",-1,-1),new i("tun",-1,-1),new i("dün",-1,-1),new i("tün",-1,-1),new i("dın",-1,-1),new i("tın",-1,-1),new i("du",-1,-1),new i("tu",-1,-1),new i("dü",-1,-1),new i("tü",-1,-1),new i("dı",-1,-1),new i("tı",-1,-1)],q=[new i("sa",-1,-1),new i("se",-1,-1),new i("sak",-1,-1),new i("sek",-1,-1),new i("sam",-1,-1),new i("sem",-1,-1),new i("san",-1,-1),new i("sen",-1,-1)],C=[new i("miş",-1,-1),new i("muş",-1,-1),new i("müş",-1,-1),new i("mış",-1,-1)],P=[new i("b",-1,1),new i("c",-1,2),new i("d",-1,3),new i("ğ",-1,4)],F=[17,65,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,8,0,0,0,0,0,0,1],S=[1,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,1],W=[65],L=[65],x=[["a",[1,64,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],97,305],["e",[17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,130],101,252],["ı",[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],97,305],["i",[17],101,105],["o",W,111,117],["ö",L,246,252],["u",W,111,117]],A=new e;function E(r,i,e){for(;;){var n=A.limit-A.cursor;if(A.in_grouping_b(r,i,e)){A.cursor=A.limit-n;break}if(A.cursor=A.limit-n,A.cursor<=A.limit_backward)return!1;A.cursor--}return!0}function j(){var r,i;r=A.limit-A.cursor,E(F,97,305);for(var e=0;eA.limit_backward&&(A.cursor--,e=A.limit-A.cursor,i()))?(A.cursor=A.limit-e,!0):(A.cursor=A.limit-n,r()?(A.cursor=A.limit-n,!1):(A.cursor=A.limit-n,!(A.cursor<=A.limit_backward)&&(A.cursor--,!!i()&&(A.cursor=A.limit-n,!0))))}function Z(r){return T(r,function(){return A.in_grouping_b(F,97,305)})}function B(){return Z(function(){return A.eq_s_b(1,"n")})}function D(){return Z(function(){return A.eq_s_b(1,"y")})}function G(){return A.find_among_b(n,10)&&T(function(){return A.in_grouping_b(S,105,305)},function(){return A.out_grouping_b(F,97,305)})}function H(){return j()&&A.in_grouping_b(S,105,305)&&Z(function(){return A.eq_s_b(1,"s")})}function I(){return A.find_among_b(t,2)}function J(){return j()&&A.find_among_b(o,4)&&B()}function K(){return j()&&A.find_among_b(l,4)}function M(){return j()&&A.find_among_b(a,2)}function N(){return j()&&A.find_among_b(w,4)&&D()}function O(){return j()&&A.find_among_b(_,4)}function Q(){return j()&&A.find_among_b(k,4)&&D()}function R(){return A.find_among_b(p,4)}function U(){return j()&&A.find_among_b(g,2)}function V(){return j()&&A.find_among_b(z,8)}function X(){return j()&&A.find_among_b(v,32)&&D()}function Y(){return A.find_among_b(q,8)&&D()}function $(){return j()&&A.find_among_b(C,4)&&D()}function rr(){var r=A.limit-A.cursor;return!($()||(A.cursor=A.limit-r,X()||(A.cursor=A.limit-r,Y()||(A.cursor=A.limit-r,A.eq_s_b(3,"ken")&&D()))))}function ir(){if(A.find_among_b(h,2)){var r=A.limit-A.cursor;if(R()||(A.cursor=A.limit-r,U()||(A.cursor=A.limit-r,N()||(A.cursor=A.limit-r,O()||(A.cursor=A.limit-r,Q()||(A.cursor=A.limit-r))))),$())return!1}return!0}function er(){if(!j()||!A.find_among_b(y,4))return!0;var r=A.limit-A.cursor;return!X()&&(A.cursor=A.limit-r,!Y())}function nr(){var i,e,n,t=A.limit-A.cursor;if(A.ket=A.cursor,r=!0,rr()&&(A.cursor=A.limit-t,ir()&&(A.cursor=A.limit-t,function(){if(U()){A.bra=A.cursor,A.slice_del();var i=A.limit-A.cursor;return A.ket=A.cursor,V()||(A.cursor=A.limit-i,X()||(A.cursor=A.limit-i,Y()||(A.cursor=A.limit-i,$()||(A.cursor=A.limit-i)))),r=!1,!1}return!0}()&&(A.cursor=A.limit-t,er()&&(A.cursor=A.limit-t,n=A.limit-A.cursor,!(R()||(A.cursor=A.limit-n,Q()||(A.cursor=A.limit-n,O()||(A.cursor=A.limit-n,N()))))||(A.bra=A.cursor,A.slice_del(),e=A.limit-A.cursor,A.ket=A.cursor,$()||(A.cursor=A.limit-e),0)))))){if(A.cursor=A.limit-t,!V())return;A.bra=A.cursor,A.slice_del(),A.ket=A.cursor,i=A.limit-A.cursor,R()||(A.cursor=A.limit-i,U()||(A.cursor=A.limit-i,N()||(A.cursor=A.limit-i,O()||(A.cursor=A.limit-i,Q()||(A.cursor=A.limit-i))))),$()||(A.cursor=A.limit-i)}A.bra=A.cursor,A.slice_del()}function tr(){var r,i,e,n;if(A.ket=A.cursor,A.eq_s_b(2,"ki")){if(r=A.limit-A.cursor,K())return A.bra=A.cursor,A.slice_del(),i=A.limit-A.cursor,A.ket=A.cursor,U()?(A.bra=A.cursor,A.slice_del(),tr()):(A.cursor=A.limit-i,G()&&(A.bra=A.cursor,A.slice_del(),A.ket=A.cursor,U()&&(A.bra=A.cursor,A.slice_del(),tr()))),!0;if(A.cursor=A.limit-r,J()){if(A.bra=A.cursor,A.slice_del(),A.ket=A.cursor,e=A.limit-A.cursor,I())A.bra=A.cursor,A.slice_del();else{if(A.cursor=A.limit-e,A.ket=A.cursor,!G()&&(A.cursor=A.limit-e,!H()&&(A.cursor=A.limit-e,!tr())))return!0;A.bra=A.cursor,A.slice_del(),A.ket=A.cursor,U()&&(A.bra=A.cursor,A.slice_del(),tr())}return!0}if(A.cursor=A.limit-r,M()){if(n=A.limit-A.cursor,I())A.bra=A.cursor,A.slice_del();else if(A.cursor=A.limit-n,H())A.bra=A.cursor,A.slice_del(),A.ket=A.cursor,U()&&(A.bra=A.cursor,A.slice_del(),tr());else if(A.cursor=A.limit-n,!tr())return!1;return!0}}return!1}function ur(r){if(A.ket=A.cursor,!M()&&(A.cursor=A.limit-r,!j()||!A.find_among_b(c,2)))return!1;var i=A.limit-A.cursor;if(I())A.bra=A.cursor,A.slice_del();else if(A.cursor=A.limit-i,H())A.bra=A.cursor,A.slice_del(),A.ket=A.cursor,U()&&(A.bra=A.cursor,A.slice_del(),tr());else if(A.cursor=A.limit-i,!tr())return!1;return!0}function or(r){if(A.ket=A.cursor,!(j()&&A.find_among_b(d,2)||(A.cursor=A.limit-r,j()&&A.find_among_b(u,4))))return!1;var i=A.limit-A.cursor;return!(!H()&&(A.cursor=A.limit-i,!I()))&&(A.bra=A.cursor,A.slice_del(),A.ket=A.cursor,U()&&(A.bra=A.cursor,A.slice_del(),tr()),!0)}function sr(){var r,i=A.limit-A.cursor;return A.ket=A.cursor,!!(J()||(A.cursor=A.limit-i,j()&&A.find_among_b(f,2)&&D()))&&(A.bra=A.cursor,A.slice_del(),r=A.limit-A.cursor,A.ket=A.cursor,!(!U()||(A.bra=A.cursor,A.slice_del(),!tr()))||(A.cursor=A.limit-r,A.ket=A.cursor,!(G()||(A.cursor=A.limit-r,H()||(A.cursor=A.limit-r,tr())))||(A.bra=A.cursor,A.slice_del(),A.ket=A.cursor,U()&&(A.bra=A.cursor,A.slice_del(),tr()),!0)))}function cr(){var r,i,e=A.limit-A.cursor;if(A.ket=A.cursor,!(K()||(A.cursor=A.limit-e,j()&&A.in_grouping_b(S,105,305)&&D()||(A.cursor=A.limit-e,j()&&A.find_among_b(s,2)&&D()))))return!1;if(A.bra=A.cursor,A.slice_del(),A.ket=A.cursor,r=A.limit-A.cursor,G())A.bra=A.cursor,A.slice_del(),i=A.limit-A.cursor,A.ket=A.cursor,U()||(A.cursor=A.limit-i);else if(A.cursor=A.limit-r,!U())return!0;return A.bra=A.cursor,A.slice_del(),A.ket=A.cursor,tr(),!0}function lr(){var r,i,e=A.limit-A.cursor;if(A.ket=A.cursor,U())return A.bra=A.cursor,A.slice_del(),void tr();if(A.cursor=A.limit-e,A.ket=A.cursor,j()&&A.find_among_b(b,2)&&B())if(A.bra=A.cursor,A.slice_del(),r=A.limit-A.cursor,A.ket=A.cursor,I())A.bra=A.cursor,A.slice_del();else{if(A.cursor=A.limit-r,A.ket=A.cursor,!G()&&(A.cursor=A.limit-r,!H())){if(A.cursor=A.limit-r,A.ket=A.cursor,!U())return;if(A.bra=A.cursor,A.slice_del(),!tr())return}A.bra=A.cursor,A.slice_del(),A.ket=A.cursor,U()&&(A.bra=A.cursor,A.slice_del(),tr())}else if(A.cursor=A.limit-e,!ur(e)&&(A.cursor=A.limit-e,!or(e))){if(A.cursor=A.limit-e,A.ket=A.cursor,j()&&A.find_among_b(m,4))return A.bra=A.cursor,A.slice_del(),A.ket=A.cursor,i=A.limit-A.cursor,void(G()?(A.bra=A.cursor,A.slice_del(),A.ket=A.cursor,U()&&(A.bra=A.cursor,A.slice_del(),tr())):(A.cursor=A.limit-i,U()?(A.bra=A.cursor,A.slice_del(),tr()):(A.cursor=A.limit-i,tr())));if(A.cursor=A.limit-e,!sr()){if(A.cursor=A.limit-e,I())return A.bra=A.cursor,void A.slice_del();A.cursor=A.limit-e,tr()||(A.cursor=A.limit-e,cr()||(A.cursor=A.limit-e,A.ket=A.cursor,(G()||(A.cursor=A.limit-e,H()))&&(A.bra=A.cursor,A.slice_del(),A.ket=A.cursor,U()&&(A.bra=A.cursor,A.slice_del(),tr()))))}}}function ar(r,i,e){if(A.cursor=A.limit-r,function(){for(;;){var r=A.limit-A.cursor;if(A.in_grouping_b(F,97,305)){A.cursor=A.limit-r;break}if(A.cursor=A.limit-r,A.cursor<=A.limit_backward)return!1;A.cursor--}return!0}()){var n=A.limit-A.cursor;if(!A.eq_s_b(1,i)&&(A.cursor=A.limit-n,!A.eq_s_b(1,e)))return!0;A.cursor=A.limit-r;var t=A.cursor;return A.insert(A.cursor,A.cursor,e),A.cursor=t,!1}return!0}function mr(r,i,e){for(;!A.eq_s(i,e);){if(A.cursor>=A.limit)return!0;A.cursor++}return i!=A.limit||(A.cursor=r,!1)}function dr(){var r,i,e=A.cursor;return!(!mr(r=A.cursor,2,"ad")||(A.cursor=r,!mr(r,5,"soyad")))&&(A.limit_backward=e,A.cursor=A.limit,i=A.limit-A.cursor,(A.eq_s_b(1,"d")||(A.cursor=A.limit-i,A.eq_s_b(1,"g")))&&ar(i,"a","ı")&&ar(i,"e","i")&&ar(i,"o","u")&&ar(i,"ö","ü"),A.cursor=A.limit,function(){var r;if(A.ket=A.cursor,r=A.find_among_b(P,4))switch(A.bra=A.cursor,r){case 1:A.slice_from("p");break;case 2:A.slice_from("ç");break;case 3:A.slice_from("t");break;case 4:A.slice_from("k")}}(),!0)}this.setCurrent=function(r){A.setCurrent(r)},this.getCurrent=function(){return A.getCurrent()},this.stem=function(){return!!(function(){for(var r,i=A.cursor,e=2;;){for(r=A.cursor;!A.in_grouping(F,97,305);){if(A.cursor>=A.limit)return A.cursor=r,!(e>0||(A.cursor=i,0));A.cursor++}e--}}()&&(A.limit_backward=A.cursor,A.cursor=A.limit,nr(),A.cursor=A.limit,r&&(lr(),A.cursor=A.limit_backward,dr())))}},function(r){return"function"==typeof r.update?r.update(function(r){return n.setCurrent(r),n.stem(),n.getCurrent()}):(n.setCurrent(r),n.stem(),n.getCurrent())}),r.Pipeline.registerFunction(r.tr.stemmer,"stemmer-tr"),r.tr.stopWordFilter=r.generateStopWordFilter("acaba altmış altı ama ancak arada aslında ayrıca bana bazı belki ben benden beni benim beri beş bile bin bir biri birkaç birkez birçok birşey birşeyi biz bizden bize bizi bizim bu buna bunda bundan bunlar bunları bunların bunu bunun burada böyle böylece da daha dahi de defa değil diye diğer doksan dokuz dolayı dolayısıyla dört edecek eden ederek edilecek ediliyor edilmesi ediyor elli en etmesi etti ettiği ettiğini eğer gibi göre halen hangi hatta hem henüz hep hepsi her herhangi herkesin hiç hiçbir iki ile ilgili ise itibaren itibariyle için işte kadar karşın katrilyon kendi kendilerine kendini kendisi kendisine kendisini kez ki kim kimden kime kimi kimse kırk milyar milyon mu mü mı nasıl ne neden nedenle nerde nerede nereye niye niçin o olan olarak oldu olduklarını olduğu olduğunu olmadı olmadığı olmak olması olmayan olmaz olsa olsun olup olur olursa oluyor on ona ondan onlar onlardan onları onların onu onun otuz oysa pek rağmen sadece sanki sekiz seksen sen senden seni senin siz sizden sizi sizin tarafından trilyon tüm var vardı ve veya ya yani yapacak yapmak yaptı yaptıkları yaptığı yaptığını yapılan yapılması yapıyor yedi yerine yetmiş yine yirmi yoksa yüz zaten çok çünkü öyle üzere üç şey şeyden şeyi şeyler şu şuna şunda şundan şunları şunu şöyle".split(" ")),r.Pipeline.registerFunction(r.tr.stopWordFilter,"stopWordFilter-tr")}}); \ No newline at end of file diff --git a/3.0/site/assets/javascripts/lunr/tinyseg.js b/3.0/site/assets/javascripts/lunr/tinyseg.js new file mode 100644 index 00000000..f7ec6032 --- /dev/null +++ b/3.0/site/assets/javascripts/lunr/tinyseg.js @@ -0,0 +1 @@ +!function(_,t){"function"==typeof define&&define.amd?define(t):"object"==typeof exports?module.exports=t():t()(_.lunr)}(this,function(){return function(_){function t(){var _={"[一二三四五六七八九十百千万億兆]":"M","[一-龠々〆ヵヶ]":"H","[ぁ-ん]":"I","[ァ-ヴーア-ン゙ー]":"K","[a-zA-Za-zA-Z]":"A","[0-90-9]":"N"};for(var t in this.chartype_=[],_){var H=new RegExp;H.compile(t),this.chartype_.push([H,_[t]])}return this.BIAS__=-332,this.BC1__={HH:6,II:2461,KH:406,OH:-1378},this.BC2__={AA:-3267,AI:2744,AN:-878,HH:-4070,HM:-1711,HN:4012,HO:3761,IA:1327,IH:-1184,II:-1332,IK:1721,IO:5492,KI:3831,KK:-8741,MH:-3132,MK:3334,OO:-2920},this.BC3__={HH:996,HI:626,HK:-721,HN:-1307,HO:-836,IH:-301,KK:2762,MK:1079,MM:4034,OA:-1652,OH:266},this.BP1__={BB:295,OB:304,OO:-125,UB:352},this.BP2__={BO:60,OO:-1762},this.BQ1__={BHH:1150,BHM:1521,BII:-1158,BIM:886,BMH:1208,BNH:449,BOH:-91,BOO:-2597,OHI:451,OIH:-296,OKA:1851,OKH:-1020,OKK:904,OOO:2965},this.BQ2__={BHH:118,BHI:-1159,BHM:466,BIH:-919,BKK:-1720,BKO:864,OHH:-1139,OHM:-181,OIH:153,UHI:-1146},this.BQ3__={BHH:-792,BHI:2664,BII:-299,BKI:419,BMH:937,BMM:8335,BNN:998,BOH:775,OHH:2174,OHM:439,OII:280,OKH:1798,OKI:-793,OKO:-2242,OMH:-2402,OOO:11699},this.BQ4__={BHH:-3895,BIH:3761,BII:-4654,BIK:1348,BKK:-1806,BMI:-3385,BOO:-12396,OAH:926,OHH:266,OHK:-2036,ONN:-973},this.BW1__={",と":660,",同":727,"B1あ":1404,"B1同":542,"、と":660,"、同":727,"」と":1682,"あっ":1505,"いう":1743,"いっ":-2055,"いる":672,"うし":-4817,"うん":665,"から":3472,"がら":600,"こう":-790,"こと":2083,"こん":-1262,"さら":-4143,"さん":4573,"した":2641,"して":1104,"すで":-3399,"そこ":1977,"それ":-871,"たち":1122,"ため":601,"った":3463,"つい":-802,"てい":805,"てき":1249,"でき":1127,"です":3445,"では":844,"とい":-4915,"とみ":1922,"どこ":3887,"ない":5713,"なっ":3015,"など":7379,"なん":-1113,"にし":2468,"には":1498,"にも":1671,"に対":-912,"の一":-501,"の中":741,"ませ":2448,"まで":1711,"まま":2600,"まる":-2155,"やむ":-1947,"よっ":-2565,"れた":2369,"れで":-913,"をし":1860,"を見":731,"亡く":-1886,"京都":2558,"取り":-2784,"大き":-2604,"大阪":1497,"平方":-2314,"引き":-1336,"日本":-195,"本当":-2423,"毎日":-2113,"目指":-724,"B1あ":1404,"B1同":542,"」と":1682},this.BW2__={"..":-11822,11:-669,"――":-5730,"−−":-13175,"いう":-1609,"うか":2490,"かし":-1350,"かも":-602,"から":-7194,"かれ":4612,"がい":853,"がら":-3198,"きた":1941,"くな":-1597,"こと":-8392,"この":-4193,"させ":4533,"され":13168,"さん":-3977,"しい":-1819,"しか":-545,"した":5078,"して":972,"しな":939,"その":-3744,"たい":-1253,"たた":-662,"ただ":-3857,"たち":-786,"たと":1224,"たは":-939,"った":4589,"って":1647,"っと":-2094,"てい":6144,"てき":3640,"てく":2551,"ては":-3110,"ても":-3065,"でい":2666,"でき":-1528,"でし":-3828,"です":-4761,"でも":-4203,"とい":1890,"とこ":-1746,"とと":-2279,"との":720,"とみ":5168,"とも":-3941,"ない":-2488,"なが":-1313,"など":-6509,"なの":2614,"なん":3099,"にお":-1615,"にし":2748,"にな":2454,"によ":-7236,"に対":-14943,"に従":-4688,"に関":-11388,"のか":2093,"ので":-7059,"のに":-6041,"のの":-6125,"はい":1073,"はが":-1033,"はず":-2532,"ばれ":1813,"まし":-1316,"まで":-6621,"まれ":5409,"めて":-3153,"もい":2230,"もの":-10713,"らか":-944,"らし":-1611,"らに":-1897,"りし":651,"りま":1620,"れた":4270,"れて":849,"れば":4114,"ろう":6067,"われ":7901,"を通":-11877,"んだ":728,"んな":-4115,"一人":602,"一方":-1375,"一日":970,"一部":-1051,"上が":-4479,"会社":-1116,"出て":2163,"分の":-7758,"同党":970,"同日":-913,"大阪":-2471,"委員":-1250,"少な":-1050,"年度":-8669,"年間":-1626,"府県":-2363,"手権":-1982,"新聞":-4066,"日新":-722,"日本":-7068,"日米":3372,"曜日":-601,"朝鮮":-2355,"本人":-2697,"東京":-1543,"然と":-1384,"社会":-1276,"立て":-990,"第に":-1612,"米国":-4268,"11":-669},this.BW3__={"あた":-2194,"あり":719,"ある":3846,"い.":-1185,"い。":-1185,"いい":5308,"いえ":2079,"いく":3029,"いた":2056,"いっ":1883,"いる":5600,"いわ":1527,"うち":1117,"うと":4798,"えと":1454,"か.":2857,"か。":2857,"かけ":-743,"かっ":-4098,"かに":-669,"から":6520,"かり":-2670,"が,":1816,"が、":1816,"がき":-4855,"がけ":-1127,"がっ":-913,"がら":-4977,"がり":-2064,"きた":1645,"けど":1374,"こと":7397,"この":1542,"ころ":-2757,"さい":-714,"さを":976,"し,":1557,"し、":1557,"しい":-3714,"した":3562,"して":1449,"しな":2608,"しま":1200,"す.":-1310,"す。":-1310,"する":6521,"ず,":3426,"ず、":3426,"ずに":841,"そう":428,"た.":8875,"た。":8875,"たい":-594,"たの":812,"たり":-1183,"たる":-853,"だ.":4098,"だ。":4098,"だっ":1004,"った":-4748,"って":300,"てい":6240,"てお":855,"ても":302,"です":1437,"でに":-1482,"では":2295,"とう":-1387,"とし":2266,"との":541,"とも":-3543,"どう":4664,"ない":1796,"なく":-903,"など":2135,"に,":-1021,"に、":-1021,"にし":1771,"にな":1906,"には":2644,"の,":-724,"の、":-724,"の子":-1e3,"は,":1337,"は、":1337,"べき":2181,"まし":1113,"ます":6943,"まっ":-1549,"まで":6154,"まれ":-793,"らし":1479,"られ":6820,"るる":3818,"れ,":854,"れ、":854,"れた":1850,"れて":1375,"れば":-3246,"れる":1091,"われ":-605,"んだ":606,"んで":798,"カ月":990,"会議":860,"入り":1232,"大会":2217,"始め":1681,"市":965,"新聞":-5055,"日,":974,"日、":974,"社会":2024,"カ月":990},this.TC1__={AAA:1093,HHH:1029,HHM:580,HII:998,HOH:-390,HOM:-331,IHI:1169,IOH:-142,IOI:-1015,IOM:467,MMH:187,OOI:-1832},this.TC2__={HHO:2088,HII:-1023,HMM:-1154,IHI:-1965,KKH:703,OII:-2649},this.TC3__={AAA:-294,HHH:346,HHI:-341,HII:-1088,HIK:731,HOH:-1486,IHH:128,IHI:-3041,IHO:-1935,IIH:-825,IIM:-1035,IOI:-542,KHH:-1216,KKA:491,KKH:-1217,KOK:-1009,MHH:-2694,MHM:-457,MHO:123,MMH:-471,NNH:-1689,NNO:662,OHO:-3393},this.TC4__={HHH:-203,HHI:1344,HHK:365,HHM:-122,HHN:182,HHO:669,HIH:804,HII:679,HOH:446,IHH:695,IHO:-2324,IIH:321,III:1497,IIO:656,IOO:54,KAK:4845,KKA:3386,KKK:3065,MHH:-405,MHI:201,MMH:-241,MMM:661,MOM:841},this.TQ1__={BHHH:-227,BHHI:316,BHIH:-132,BIHH:60,BIII:1595,BNHH:-744,BOHH:225,BOOO:-908,OAKK:482,OHHH:281,OHIH:249,OIHI:200,OIIH:-68},this.TQ2__={BIHH:-1401,BIII:-1033,BKAK:-543,BOOO:-5591},this.TQ3__={BHHH:478,BHHM:-1073,BHIH:222,BHII:-504,BIIH:-116,BIII:-105,BMHI:-863,BMHM:-464,BOMH:620,OHHH:346,OHHI:1729,OHII:997,OHMH:481,OIHH:623,OIIH:1344,OKAK:2792,OKHH:587,OKKA:679,OOHH:110,OOII:-685},this.TQ4__={BHHH:-721,BHHM:-3604,BHII:-966,BIIH:-607,BIII:-2181,OAAA:-2763,OAKK:180,OHHH:-294,OHHI:2446,OHHO:480,OHIH:-1573,OIHH:1935,OIHI:-493,OIIH:626,OIII:-4007,OKAK:-8156},this.TW1__={"につい":-4681,"東京都":2026},this.TW2__={"ある程":-2049,"いった":-1256,"ころが":-2434,"しょう":3873,"その後":-4430,"だって":-1049,"ていた":1833,"として":-4657,"ともに":-4517,"もので":1882,"一気に":-792,"初めて":-1512,"同時に":-8097,"大きな":-1255,"対して":-2721,"社会党":-3216},this.TW3__={"いただ":-1734,"してい":1314,"として":-4314,"につい":-5483,"にとっ":-5989,"に当た":-6247,"ので,":-727,"ので、":-727,"のもの":-600,"れから":-3752,"十二月":-2287},this.TW4__={"いう.":8576,"いう。":8576,"からな":-2348,"してい":2958,"たが,":1516,"たが、":1516,"ている":1538,"という":1349,"ました":5543,"ません":1097,"ようと":-4258,"よると":5865},this.UC1__={A:484,K:93,M:645,O:-505},this.UC2__={A:819,H:1059,I:409,M:3987,N:5775,O:646},this.UC3__={A:-1370,I:2311},this.UC4__={A:-2643,H:1809,I:-1032,K:-3450,M:3565,N:3876,O:6646},this.UC5__={H:313,I:-1238,K:-799,M:539,O:-831},this.UC6__={H:-506,I:-253,K:87,M:247,O:-387},this.UP1__={O:-214},this.UP2__={B:69,O:935},this.UP3__={B:189},this.UQ1__={BH:21,BI:-12,BK:-99,BN:142,BO:-56,OH:-95,OI:477,OK:410,OO:-2422},this.UQ2__={BH:216,BI:113,OK:1759},this.UQ3__={BA:-479,BH:42,BI:1913,BK:-7198,BM:3160,BN:6427,BO:14761,OI:-827,ON:-3212},this.UW1__={",":156,"、":156,"「":-463,"あ":-941,"う":-127,"が":-553,"き":121,"こ":505,"で":-201,"と":-547,"ど":-123,"に":-789,"の":-185,"は":-847,"も":-466,"や":-470,"よ":182,"ら":-292,"り":208,"れ":169,"を":-446,"ん":-137,"・":-135,"主":-402,"京":-268,"区":-912,"午":871,"国":-460,"大":561,"委":729,"市":-411,"日":-141,"理":361,"生":-408,"県":-386,"都":-718,"「":-463,"・":-135},this.UW2__={",":-829,"、":-829,"〇":892,"「":-645,"」":3145,"あ":-538,"い":505,"う":134,"お":-502,"か":1454,"が":-856,"く":-412,"こ":1141,"さ":878,"ざ":540,"し":1529,"す":-675,"せ":300,"そ":-1011,"た":188,"だ":1837,"つ":-949,"て":-291,"で":-268,"と":-981,"ど":1273,"な":1063,"に":-1764,"の":130,"は":-409,"ひ":-1273,"べ":1261,"ま":600,"も":-1263,"や":-402,"よ":1639,"り":-579,"る":-694,"れ":571,"を":-2516,"ん":2095,"ア":-587,"カ":306,"キ":568,"ッ":831,"三":-758,"不":-2150,"世":-302,"中":-968,"主":-861,"事":492,"人":-123,"会":978,"保":362,"入":548,"初":-3025,"副":-1566,"北":-3414,"区":-422,"大":-1769,"天":-865,"太":-483,"子":-1519,"学":760,"実":1023,"小":-2009,"市":-813,"年":-1060,"強":1067,"手":-1519,"揺":-1033,"政":1522,"文":-1355,"新":-1682,"日":-1815,"明":-1462,"最":-630,"朝":-1843,"本":-1650,"東":-931,"果":-665,"次":-2378,"民":-180,"気":-1740,"理":752,"発":529,"目":-1584,"相":-242,"県":-1165,"立":-763,"第":810,"米":509,"自":-1353,"行":838,"西":-744,"見":-3874,"調":1010,"議":1198,"込":3041,"開":1758,"間":-1257,"「":-645,"」":3145,"ッ":831,"ア":-587,"カ":306,"キ":568},this.UW3__={",":4889,1:-800,"−":-1723,"、":4889,"々":-2311,"〇":5827,"」":2670,"〓":-3573,"あ":-2696,"い":1006,"う":2342,"え":1983,"お":-4864,"か":-1163,"が":3271,"く":1004,"け":388,"げ":401,"こ":-3552,"ご":-3116,"さ":-1058,"し":-395,"す":584,"せ":3685,"そ":-5228,"た":842,"ち":-521,"っ":-1444,"つ":-1081,"て":6167,"で":2318,"と":1691,"ど":-899,"な":-2788,"に":2745,"の":4056,"は":4555,"ひ":-2171,"ふ":-1798,"へ":1199,"ほ":-5516,"ま":-4384,"み":-120,"め":1205,"も":2323,"や":-788,"よ":-202,"ら":727,"り":649,"る":5905,"れ":2773,"わ":-1207,"を":6620,"ん":-518,"ア":551,"グ":1319,"ス":874,"ッ":-1350,"ト":521,"ム":1109,"ル":1591,"ロ":2201,"ン":278,"・":-3794,"一":-1619,"下":-1759,"世":-2087,"両":3815,"中":653,"主":-758,"予":-1193,"二":974,"人":2742,"今":792,"他":1889,"以":-1368,"低":811,"何":4265,"作":-361,"保":-2439,"元":4858,"党":3593,"全":1574,"公":-3030,"六":755,"共":-1880,"円":5807,"再":3095,"分":457,"初":2475,"別":1129,"前":2286,"副":4437,"力":365,"動":-949,"務":-1872,"化":1327,"北":-1038,"区":4646,"千":-2309,"午":-783,"協":-1006,"口":483,"右":1233,"各":3588,"合":-241,"同":3906,"和":-837,"員":4513,"国":642,"型":1389,"場":1219,"外":-241,"妻":2016,"学":-1356,"安":-423,"実":-1008,"家":1078,"小":-513,"少":-3102,"州":1155,"市":3197,"平":-1804,"年":2416,"広":-1030,"府":1605,"度":1452,"建":-2352,"当":-3885,"得":1905,"思":-1291,"性":1822,"戸":-488,"指":-3973,"政":-2013,"教":-1479,"数":3222,"文":-1489,"新":1764,"日":2099,"旧":5792,"昨":-661,"時":-1248,"曜":-951,"最":-937,"月":4125,"期":360,"李":3094,"村":364,"東":-805,"核":5156,"森":2438,"業":484,"氏":2613,"民":-1694,"決":-1073,"法":1868,"海":-495,"無":979,"物":461,"特":-3850,"生":-273,"用":914,"町":1215,"的":7313,"直":-1835,"省":792,"県":6293,"知":-1528,"私":4231,"税":401,"立":-960,"第":1201,"米":7767,"系":3066,"約":3663,"級":1384,"統":-4229,"総":1163,"線":1255,"者":6457,"能":725,"自":-2869,"英":785,"見":1044,"調":-562,"財":-733,"費":1777,"車":1835,"軍":1375,"込":-1504,"通":-1136,"選":-681,"郎":1026,"郡":4404,"部":1200,"金":2163,"長":421,"開":-1432,"間":1302,"関":-1282,"雨":2009,"電":-1045,"非":2066,"駅":1620,"1":-800,"」":2670,"・":-3794,"ッ":-1350,"ア":551,"グ":1319,"ス":874,"ト":521,"ム":1109,"ル":1591,"ロ":2201,"ン":278},this.UW4__={",":3930,".":3508,"―":-4841,"、":3930,"。":3508,"〇":4999,"「":1895,"」":3798,"〓":-5156,"あ":4752,"い":-3435,"う":-640,"え":-2514,"お":2405,"か":530,"が":6006,"き":-4482,"ぎ":-3821,"く":-3788,"け":-4376,"げ":-4734,"こ":2255,"ご":1979,"さ":2864,"し":-843,"じ":-2506,"す":-731,"ず":1251,"せ":181,"そ":4091,"た":5034,"だ":5408,"ち":-3654,"っ":-5882,"つ":-1659,"て":3994,"で":7410,"と":4547,"な":5433,"に":6499,"ぬ":1853,"ね":1413,"の":7396,"は":8578,"ば":1940,"ひ":4249,"び":-4134,"ふ":1345,"へ":6665,"べ":-744,"ほ":1464,"ま":1051,"み":-2082,"む":-882,"め":-5046,"も":4169,"ゃ":-2666,"や":2795,"ょ":-1544,"よ":3351,"ら":-2922,"り":-9726,"る":-14896,"れ":-2613,"ろ":-4570,"わ":-1783,"を":13150,"ん":-2352,"カ":2145,"コ":1789,"セ":1287,"ッ":-724,"ト":-403,"メ":-1635,"ラ":-881,"リ":-541,"ル":-856,"ン":-3637,"・":-4371,"ー":-11870,"一":-2069,"中":2210,"予":782,"事":-190,"井":-1768,"人":1036,"以":544,"会":950,"体":-1286,"作":530,"側":4292,"先":601,"党":-2006,"共":-1212,"内":584,"円":788,"初":1347,"前":1623,"副":3879,"力":-302,"動":-740,"務":-2715,"化":776,"区":4517,"協":1013,"参":1555,"合":-1834,"和":-681,"員":-910,"器":-851,"回":1500,"国":-619,"園":-1200,"地":866,"場":-1410,"塁":-2094,"士":-1413,"多":1067,"大":571,"子":-4802,"学":-1397,"定":-1057,"寺":-809,"小":1910,"屋":-1328,"山":-1500,"島":-2056,"川":-2667,"市":2771,"年":374,"庁":-4556,"後":456,"性":553,"感":916,"所":-1566,"支":856,"改":787,"政":2182,"教":704,"文":522,"方":-856,"日":1798,"時":1829,"最":845,"月":-9066,"木":-485,"来":-442,"校":-360,"業":-1043,"氏":5388,"民":-2716,"気":-910,"沢":-939,"済":-543,"物":-735,"率":672,"球":-1267,"生":-1286,"産":-1101,"田":-2900,"町":1826,"的":2586,"目":922,"省":-3485,"県":2997,"空":-867,"立":-2112,"第":788,"米":2937,"系":786,"約":2171,"経":1146,"統":-1169,"総":940,"線":-994,"署":749,"者":2145,"能":-730,"般":-852,"行":-792,"規":792,"警":-1184,"議":-244,"谷":-1e3,"賞":730,"車":-1481,"軍":1158,"輪":-1433,"込":-3370,"近":929,"道":-1291,"選":2596,"郎":-4866,"都":1192,"野":-1100,"銀":-2213,"長":357,"間":-2344,"院":-2297,"際":-2604,"電":-878,"領":-1659,"題":-792,"館":-1984,"首":1749,"高":2120,"「":1895,"」":3798,"・":-4371,"ッ":-724,"ー":-11870,"カ":2145,"コ":1789,"セ":1287,"ト":-403,"メ":-1635,"ラ":-881,"リ":-541,"ル":-856,"ン":-3637},this.UW5__={",":465,".":-299,1:-514,E2:-32768,"]":-2762,"、":465,"。":-299,"「":363,"あ":1655,"い":331,"う":-503,"え":1199,"お":527,"か":647,"が":-421,"き":1624,"ぎ":1971,"く":312,"げ":-983,"さ":-1537,"し":-1371,"す":-852,"だ":-1186,"ち":1093,"っ":52,"つ":921,"て":-18,"で":-850,"と":-127,"ど":1682,"な":-787,"に":-1224,"の":-635,"は":-578,"べ":1001,"み":502,"め":865,"ゃ":3350,"ょ":854,"り":-208,"る":429,"れ":504,"わ":419,"を":-1264,"ん":327,"イ":241,"ル":451,"ン":-343,"中":-871,"京":722,"会":-1153,"党":-654,"務":3519,"区":-901,"告":848,"員":2104,"大":-1296,"学":-548,"定":1785,"嵐":-1304,"市":-2991,"席":921,"年":1763,"思":872,"所":-814,"挙":1618,"新":-1682,"日":218,"月":-4353,"査":932,"格":1356,"機":-1508,"氏":-1347,"田":240,"町":-3912,"的":-3149,"相":1319,"省":-1052,"県":-4003,"研":-997,"社":-278,"空":-813,"統":1955,"者":-2233,"表":663,"語":-1073,"議":1219,"選":-1018,"郎":-368,"長":786,"間":1191,"題":2368,"館":-689,"1":-514,"E2":-32768,"「":363,"イ":241,"ル":451,"ン":-343},this.UW6__={",":227,".":808,1:-270,E1:306,"、":227,"。":808,"あ":-307,"う":189,"か":241,"が":-73,"く":-121,"こ":-200,"じ":1782,"す":383,"た":-428,"っ":573,"て":-1014,"で":101,"と":-105,"な":-253,"に":-149,"の":-417,"は":-236,"も":-206,"り":187,"る":-135,"を":195,"ル":-673,"ン":-496,"一":-277,"中":201,"件":-800,"会":624,"前":302,"区":1792,"員":-1212,"委":798,"学":-960,"市":887,"広":-695,"後":535,"業":-697,"相":753,"社":-507,"福":974,"空":-822,"者":1811,"連":463,"郎":1082,"1":-270,"E1":306,"ル":-673,"ン":-496},this}t.prototype.ctype_=function(_){for(var t in this.chartype_)if(_.match(this.chartype_[t][0]))return this.chartype_[t][1];return"O"},t.prototype.ts_=function(_){return _||0},t.prototype.segment=function(_){if(null==_||null==_||""==_)return[];var t=[],H=["B3","B2","B1"],s=["O","O","O"],h=_.split("");for(K=0;K0&&(t.push(i),i="",N="B"),I=O,O=B,B=N,i+=H[K]}return t.push(i),t},_.TinySegmenter=t}}); \ No newline at end of file diff --git a/3.0/site/assets/javascripts/modernizr.1aa3b519.js b/3.0/site/assets/javascripts/modernizr.1aa3b519.js new file mode 100644 index 00000000..14e111fc --- /dev/null +++ b/3.0/site/assets/javascripts/modernizr.1aa3b519.js @@ -0,0 +1 @@ +!function(e,t){for(var n in t)e[n]=t[n]}(window,function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,t),o.l=!0,o.exports}var n={};return t.m=e,t.c=n,t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s=4)}({4:function(e,t,n){"use strict";n(5)},5:function(e,t){!function(t){!function(e,t,n){function r(e,t){return typeof e===t}function o(e){var t=_.className,n=C._config.classPrefix||"";if(T&&(t=t.baseVal),C._config.enableJSClass){var r=new RegExp("(^|\\s)"+n+"no-js(\\s|$)");t=t.replace(r,"$1"+n+"js$2")}C._config.enableClasses&&(t+=" "+n+e.join(" "+n),T?_.className.baseVal=t:_.className=t)}function i(e,t){if("object"==typeof e)for(var n in e)b(e,n)&&i(n,e[n]);else{e=e.toLowerCase();var r=e.split("."),s=C[r[0]];if(2==r.length&&(s=s[r[1]]),void 0!==s)return C;t="function"==typeof t?t():t,1==r.length?C[r[0]]=t:(!C[r[0]]||C[r[0]]instanceof Boolean||(C[r[0]]=new Boolean(C[r[0]])),C[r[0]][r[1]]=t),o([(t&&0!=t?"":"no-")+r.join("-")]),C._trigger(e,t)}return C}function s(){return"function"!=typeof t.createElement?t.createElement(arguments[0]):T?t.createElementNS.call(t,"http://www.w3.org/2000/svg",arguments[0]):t.createElement.apply(t,arguments)}function a(){var e=t.body;return e||(e=s(T?"svg":"body"),e.fake=!0),e}function u(e,n,r,o){var i,u,l,f,c="modernizr",d=s("div"),p=a();if(parseInt(r,10))for(;r--;)l=s("div"),l.id=o?o[r]:c+(r+1),d.appendChild(l);return i=s("style"),i.type="text/css",i.id="s"+c,(p.fake?p:d).appendChild(i),p.appendChild(d),i.styleSheet?i.styleSheet.cssText=e:i.appendChild(t.createTextNode(e)),d.id=c,p.fake&&(p.style.background="",p.style.overflow="hidden",f=_.style.overflow,_.style.overflow="hidden",_.appendChild(p)),u=n(d,e),p.fake?(p.parentNode.removeChild(p),_.style.overflow=f,_.offsetHeight):d.parentNode.removeChild(d),!!u}function l(e,t){return!!~(""+e).indexOf(t)}function f(e){return e.replace(/([A-Z])/g,function(e,t){return"-"+t.toLowerCase()}).replace(/^ms-/,"-ms-")}function c(t,n,r){var o;if("getComputedStyle"in e){o=getComputedStyle.call(e,t,n);var i=e.console;if(null!==o)r&&(o=o.getPropertyValue(r));else if(i){var s=i.error?"error":"log";i[s].call(i,"getComputedStyle returning null, its possible modernizr test results are inaccurate")}}else o=!n&&t.currentStyle&&t.currentStyle[r];return o}function d(t,r){var o=t.length;if("CSS"in e&&"supports"in e.CSS){for(;o--;)if(e.CSS.supports(f(t[o]),r))return!0;return!1}if("CSSSupportsRule"in e){for(var i=[];o--;)i.push("("+f(t[o])+":"+r+")");return i=i.join(" or "),u("@supports ("+i+") { #modernizr { position: absolute; } }",function(e){return"absolute"==c(e,null,"position")})}return n}function p(e){return e.replace(/([a-z])-([a-z])/g,function(e,t,n){return t+n.toUpperCase()}).replace(/^-/,"")}function h(e,t,o,i){function a(){f&&(delete j.style,delete j.modElem)}if(i=!r(i,"undefined")&&i,!r(o,"undefined")){var u=d(e,o);if(!r(u,"undefined"))return u}for(var f,c,h,m,v,g=["modernizr","tspan","samp"];!j.style&&g.length;)f=!0,j.modElem=s(g.shift()),j.style=j.modElem.style;for(h=e.length,c=0;h>c;c++)if(m=e[c],v=j.style[m],l(m,"-")&&(m=p(m)),j.style[m]!==n){if(i||r(o,"undefined"))return a(),"pfx"!=t||m;try{j.style[m]=o}catch(e){}if(j.style[m]!=v)return a(),"pfx"!=t||m}return a(),!1}function m(e,t){return function(){return e.apply(t,arguments)}}function v(e,t,n){var o;for(var i in e)if(e[i]in t)return!1===n?e[i]:(o=t[e[i]],r(o,"function")?m(o,n||t):o);return!1}function g(e,t,n,o,i){var s=e.charAt(0).toUpperCase()+e.slice(1),a=(e+" "+k.join(s+" ")+s).split(" ");return r(t,"string")||r(t,"undefined")?h(a,t,o,i):(a=(e+" "+A.join(s+" ")+s).split(" "),v(a,t,n))}function y(e,t,r){return g(e,n,n,t,r)}var w=[],S={_version:"3.5.0",_config:{classPrefix:"",enableClasses:!0,enableJSClass:!0,usePrefixes:!0},_q:[],on:function(e,t){var n=this;setTimeout(function(){t(n[e])},0)},addTest:function(e,t,n){w.push({name:e,fn:t,options:n})},addAsyncTest:function(e){w.push({name:null,fn:e})}},C=function(){};C.prototype=S,C=new C;var b,x=[],_=t.documentElement,T="svg"===_.nodeName.toLowerCase();!function(){var e={}.hasOwnProperty;b=r(e,"undefined")||r(e.call,"undefined")?function(e,t){return t in e&&r(e.constructor.prototype[t],"undefined")}:function(t,n){return e.call(t,n)}}(),S._l={},S.on=function(e,t){this._l[e]||(this._l[e]=[]),this._l[e].push(t),C.hasOwnProperty(e)&&setTimeout(function(){C._trigger(e,C[e])},0)},S._trigger=function(e,t){if(this._l[e]){var n=this._l[e];setTimeout(function(){var e;for(e=0;e.md-nav__link{color:inherit}button[data-md-color-primary=pink]{background-color:#e91e63}[data-md-color-primary=pink] .md-typeset a{color:#e91e63}[data-md-color-primary=pink] .md-header,[data-md-color-primary=pink] .md-hero{background-color:#e91e63}[data-md-color-primary=pink] .md-nav__link--active,[data-md-color-primary=pink] .md-nav__link:active{color:#e91e63}[data-md-color-primary=pink] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=purple]{background-color:#ab47bc}[data-md-color-primary=purple] .md-typeset a{color:#ab47bc}[data-md-color-primary=purple] .md-header,[data-md-color-primary=purple] .md-hero{background-color:#ab47bc}[data-md-color-primary=purple] .md-nav__link--active,[data-md-color-primary=purple] .md-nav__link:active{color:#ab47bc}[data-md-color-primary=purple] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=deep-purple]{background-color:#7e57c2}[data-md-color-primary=deep-purple] .md-typeset a{color:#7e57c2}[data-md-color-primary=deep-purple] .md-header,[data-md-color-primary=deep-purple] .md-hero{background-color:#7e57c2}[data-md-color-primary=deep-purple] .md-nav__link--active,[data-md-color-primary=deep-purple] .md-nav__link:active{color:#7e57c2}[data-md-color-primary=deep-purple] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=indigo]{background-color:#3f51b5}[data-md-color-primary=indigo] .md-typeset a{color:#3f51b5}[data-md-color-primary=indigo] .md-header,[data-md-color-primary=indigo] .md-hero{background-color:#3f51b5}[data-md-color-primary=indigo] .md-nav__link--active,[data-md-color-primary=indigo] .md-nav__link:active{color:#3f51b5}[data-md-color-primary=indigo] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=blue]{background-color:#2196f3}[data-md-color-primary=blue] .md-typeset a{color:#2196f3}[data-md-color-primary=blue] .md-header,[data-md-color-primary=blue] .md-hero{background-color:#2196f3}[data-md-color-primary=blue] .md-nav__link--active,[data-md-color-primary=blue] .md-nav__link:active{color:#2196f3}[data-md-color-primary=blue] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=light-blue]{background-color:#03a9f4}[data-md-color-primary=light-blue] .md-typeset a{color:#03a9f4}[data-md-color-primary=light-blue] .md-header,[data-md-color-primary=light-blue] .md-hero{background-color:#03a9f4}[data-md-color-primary=light-blue] .md-nav__link--active,[data-md-color-primary=light-blue] .md-nav__link:active{color:#03a9f4}[data-md-color-primary=light-blue] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=cyan]{background-color:#00bcd4}[data-md-color-primary=cyan] .md-typeset a{color:#00bcd4}[data-md-color-primary=cyan] .md-header,[data-md-color-primary=cyan] .md-hero{background-color:#00bcd4}[data-md-color-primary=cyan] .md-nav__link--active,[data-md-color-primary=cyan] .md-nav__link:active{color:#00bcd4}[data-md-color-primary=cyan] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=teal]{background-color:#009688}[data-md-color-primary=teal] .md-typeset a{color:#009688}[data-md-color-primary=teal] .md-header,[data-md-color-primary=teal] .md-hero{background-color:#009688}[data-md-color-primary=teal] .md-nav__link--active,[data-md-color-primary=teal] .md-nav__link:active{color:#009688}[data-md-color-primary=teal] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=green]{background-color:#4caf50}[data-md-color-primary=green] .md-typeset a{color:#4caf50}[data-md-color-primary=green] .md-header,[data-md-color-primary=green] .md-hero{background-color:#4caf50}[data-md-color-primary=green] .md-nav__link--active,[data-md-color-primary=green] .md-nav__link:active{color:#4caf50}[data-md-color-primary=green] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=light-green]{background-color:#7cb342}[data-md-color-primary=light-green] .md-typeset a{color:#7cb342}[data-md-color-primary=light-green] .md-header,[data-md-color-primary=light-green] .md-hero{background-color:#7cb342}[data-md-color-primary=light-green] .md-nav__link--active,[data-md-color-primary=light-green] .md-nav__link:active{color:#7cb342}[data-md-color-primary=light-green] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=lime]{background-color:#c0ca33}[data-md-color-primary=lime] .md-typeset a{color:#c0ca33}[data-md-color-primary=lime] .md-header,[data-md-color-primary=lime] .md-hero{background-color:#c0ca33}[data-md-color-primary=lime] .md-nav__link--active,[data-md-color-primary=lime] .md-nav__link:active{color:#c0ca33}[data-md-color-primary=lime] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=yellow]{background-color:#f9a825}[data-md-color-primary=yellow] .md-typeset a{color:#f9a825}[data-md-color-primary=yellow] .md-header,[data-md-color-primary=yellow] .md-hero{background-color:#f9a825}[data-md-color-primary=yellow] .md-nav__link--active,[data-md-color-primary=yellow] .md-nav__link:active{color:#f9a825}[data-md-color-primary=yellow] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=amber]{background-color:#ffa000}[data-md-color-primary=amber] .md-typeset a{color:#ffa000}[data-md-color-primary=amber] .md-header,[data-md-color-primary=amber] .md-hero{background-color:#ffa000}[data-md-color-primary=amber] .md-nav__link--active,[data-md-color-primary=amber] .md-nav__link:active{color:#ffa000}[data-md-color-primary=amber] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=orange]{background-color:#fb8c00}[data-md-color-primary=orange] .md-typeset a{color:#fb8c00}[data-md-color-primary=orange] .md-header,[data-md-color-primary=orange] .md-hero{background-color:#fb8c00}[data-md-color-primary=orange] .md-nav__link--active,[data-md-color-primary=orange] .md-nav__link:active{color:#fb8c00}[data-md-color-primary=orange] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=deep-orange]{background-color:#ff7043}[data-md-color-primary=deep-orange] .md-typeset a{color:#ff7043}[data-md-color-primary=deep-orange] .md-header,[data-md-color-primary=deep-orange] .md-hero{background-color:#ff7043}[data-md-color-primary=deep-orange] .md-nav__link--active,[data-md-color-primary=deep-orange] .md-nav__link:active{color:#ff7043}[data-md-color-primary=deep-orange] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=brown]{background-color:#795548}[data-md-color-primary=brown] .md-typeset a{color:#795548}[data-md-color-primary=brown] .md-header,[data-md-color-primary=brown] .md-hero{background-color:#795548}[data-md-color-primary=brown] .md-nav__link--active,[data-md-color-primary=brown] .md-nav__link:active{color:#795548}[data-md-color-primary=brown] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=grey]{background-color:#757575}[data-md-color-primary=grey] .md-typeset a{color:#757575}[data-md-color-primary=grey] .md-header,[data-md-color-primary=grey] .md-hero{background-color:#757575}[data-md-color-primary=grey] .md-nav__link--active,[data-md-color-primary=grey] .md-nav__link:active{color:#757575}[data-md-color-primary=grey] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=blue-grey]{background-color:#546e7a}[data-md-color-primary=blue-grey] .md-typeset a{color:#546e7a}[data-md-color-primary=blue-grey] .md-header,[data-md-color-primary=blue-grey] .md-hero{background-color:#546e7a}[data-md-color-primary=blue-grey] .md-nav__link--active,[data-md-color-primary=blue-grey] .md-nav__link:active{color:#546e7a}[data-md-color-primary=blue-grey] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=white]{box-shadow:inset 0 0 .1rem rgba(0,0,0,.54)}[data-md-color-primary=white] .md-header,[data-md-color-primary=white] .md-hero,button[data-md-color-primary=white]{background-color:#fff;color:rgba(0,0,0,.87)}[data-md-color-primary=white] .md-hero--expand{border-bottom:.1rem solid rgba(0,0,0,.07)}button[data-md-color-accent=red]{background-color:#ff1744}[data-md-color-accent=red] .md-typeset a:active,[data-md-color-accent=red] .md-typeset a:hover{color:#ff1744}[data-md-color-accent=red] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover,[data-md-color-accent=red] .md-typeset pre code::-webkit-scrollbar-thumb:hover{background-color:#ff1744}[data-md-color-accent=red] .md-nav__link:focus,[data-md-color-accent=red] .md-nav__link:hover,[data-md-color-accent=red] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=red] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=red] .md-typeset .md-clipboard:active:before,[data-md-color-accent=red] .md-typeset .md-clipboard:hover:before,[data-md-color-accent=red] .md-typeset [id] .headerlink:focus,[data-md-color-accent=red] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=red] .md-typeset [id]:target .headerlink{color:#ff1744}[data-md-color-accent=red] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#ff1744}[data-md-color-accent=red] .md-search-result__link:hover,[data-md-color-accent=red] .md-search-result__link[data-md-state=active]{background-color:rgba(255,23,68,.1)}[data-md-color-accent=red] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#ff1744}[data-md-color-accent=red] .md-source-file:hover:before{background-color:#ff1744}button[data-md-color-accent=pink]{background-color:#f50057}[data-md-color-accent=pink] .md-typeset a:active,[data-md-color-accent=pink] .md-typeset a:hover{color:#f50057}[data-md-color-accent=pink] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover,[data-md-color-accent=pink] .md-typeset pre code::-webkit-scrollbar-thumb:hover{background-color:#f50057}[data-md-color-accent=pink] .md-nav__link:focus,[data-md-color-accent=pink] .md-nav__link:hover,[data-md-color-accent=pink] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=pink] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=pink] .md-typeset .md-clipboard:active:before,[data-md-color-accent=pink] .md-typeset .md-clipboard:hover:before,[data-md-color-accent=pink] .md-typeset [id] .headerlink:focus,[data-md-color-accent=pink] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=pink] .md-typeset [id]:target .headerlink{color:#f50057}[data-md-color-accent=pink] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#f50057}[data-md-color-accent=pink] .md-search-result__link:hover,[data-md-color-accent=pink] .md-search-result__link[data-md-state=active]{background-color:rgba(245,0,87,.1)}[data-md-color-accent=pink] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#f50057}[data-md-color-accent=pink] .md-source-file:hover:before{background-color:#f50057}button[data-md-color-accent=purple]{background-color:#e040fb}[data-md-color-accent=purple] .md-typeset a:active,[data-md-color-accent=purple] .md-typeset a:hover{color:#e040fb}[data-md-color-accent=purple] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover,[data-md-color-accent=purple] .md-typeset pre code::-webkit-scrollbar-thumb:hover{background-color:#e040fb}[data-md-color-accent=purple] .md-nav__link:focus,[data-md-color-accent=purple] .md-nav__link:hover,[data-md-color-accent=purple] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=purple] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=purple] .md-typeset .md-clipboard:active:before,[data-md-color-accent=purple] .md-typeset .md-clipboard:hover:before,[data-md-color-accent=purple] .md-typeset [id] .headerlink:focus,[data-md-color-accent=purple] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=purple] .md-typeset [id]:target .headerlink{color:#e040fb}[data-md-color-accent=purple] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#e040fb}[data-md-color-accent=purple] .md-search-result__link:hover,[data-md-color-accent=purple] .md-search-result__link[data-md-state=active]{background-color:rgba(224,64,251,.1)}[data-md-color-accent=purple] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#e040fb}[data-md-color-accent=purple] .md-source-file:hover:before{background-color:#e040fb}button[data-md-color-accent=deep-purple]{background-color:#7c4dff}[data-md-color-accent=deep-purple] .md-typeset a:active,[data-md-color-accent=deep-purple] .md-typeset a:hover{color:#7c4dff}[data-md-color-accent=deep-purple] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover,[data-md-color-accent=deep-purple] .md-typeset pre code::-webkit-scrollbar-thumb:hover{background-color:#7c4dff}[data-md-color-accent=deep-purple] .md-nav__link:focus,[data-md-color-accent=deep-purple] .md-nav__link:hover,[data-md-color-accent=deep-purple] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=deep-purple] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=deep-purple] .md-typeset .md-clipboard:active:before,[data-md-color-accent=deep-purple] .md-typeset .md-clipboard:hover:before,[data-md-color-accent=deep-purple] .md-typeset [id] .headerlink:focus,[data-md-color-accent=deep-purple] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=deep-purple] .md-typeset [id]:target .headerlink{color:#7c4dff}[data-md-color-accent=deep-purple] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#7c4dff}[data-md-color-accent=deep-purple] .md-search-result__link:hover,[data-md-color-accent=deep-purple] .md-search-result__link[data-md-state=active]{background-color:rgba(124,77,255,.1)}[data-md-color-accent=deep-purple] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#7c4dff}[data-md-color-accent=deep-purple] .md-source-file:hover:before{background-color:#7c4dff}button[data-md-color-accent=indigo]{background-color:#536dfe}[data-md-color-accent=indigo] .md-typeset a:active,[data-md-color-accent=indigo] .md-typeset a:hover{color:#536dfe}[data-md-color-accent=indigo] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover,[data-md-color-accent=indigo] .md-typeset pre code::-webkit-scrollbar-thumb:hover{background-color:#536dfe}[data-md-color-accent=indigo] .md-nav__link:focus,[data-md-color-accent=indigo] .md-nav__link:hover,[data-md-color-accent=indigo] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=indigo] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=indigo] .md-typeset .md-clipboard:active:before,[data-md-color-accent=indigo] .md-typeset .md-clipboard:hover:before,[data-md-color-accent=indigo] .md-typeset [id] .headerlink:focus,[data-md-color-accent=indigo] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=indigo] .md-typeset [id]:target .headerlink{color:#536dfe}[data-md-color-accent=indigo] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#536dfe}[data-md-color-accent=indigo] .md-search-result__link:hover,[data-md-color-accent=indigo] .md-search-result__link[data-md-state=active]{background-color:rgba(83,109,254,.1)}[data-md-color-accent=indigo] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#536dfe}[data-md-color-accent=indigo] .md-source-file:hover:before{background-color:#536dfe}button[data-md-color-accent=blue]{background-color:#448aff}[data-md-color-accent=blue] .md-typeset a:active,[data-md-color-accent=blue] .md-typeset a:hover{color:#448aff}[data-md-color-accent=blue] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover,[data-md-color-accent=blue] .md-typeset pre code::-webkit-scrollbar-thumb:hover{background-color:#448aff}[data-md-color-accent=blue] .md-nav__link:focus,[data-md-color-accent=blue] .md-nav__link:hover,[data-md-color-accent=blue] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=blue] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=blue] .md-typeset .md-clipboard:active:before,[data-md-color-accent=blue] .md-typeset .md-clipboard:hover:before,[data-md-color-accent=blue] .md-typeset [id] .headerlink:focus,[data-md-color-accent=blue] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=blue] .md-typeset [id]:target .headerlink{color:#448aff}[data-md-color-accent=blue] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#448aff}[data-md-color-accent=blue] .md-search-result__link:hover,[data-md-color-accent=blue] .md-search-result__link[data-md-state=active]{background-color:rgba(68,138,255,.1)}[data-md-color-accent=blue] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#448aff}[data-md-color-accent=blue] .md-source-file:hover:before{background-color:#448aff}button[data-md-color-accent=light-blue]{background-color:#0091ea}[data-md-color-accent=light-blue] .md-typeset a:active,[data-md-color-accent=light-blue] .md-typeset a:hover{color:#0091ea}[data-md-color-accent=light-blue] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover,[data-md-color-accent=light-blue] .md-typeset pre code::-webkit-scrollbar-thumb:hover{background-color:#0091ea}[data-md-color-accent=light-blue] .md-nav__link:focus,[data-md-color-accent=light-blue] .md-nav__link:hover,[data-md-color-accent=light-blue] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=light-blue] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=light-blue] .md-typeset .md-clipboard:active:before,[data-md-color-accent=light-blue] .md-typeset .md-clipboard:hover:before,[data-md-color-accent=light-blue] .md-typeset [id] .headerlink:focus,[data-md-color-accent=light-blue] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=light-blue] .md-typeset [id]:target .headerlink{color:#0091ea}[data-md-color-accent=light-blue] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#0091ea}[data-md-color-accent=light-blue] .md-search-result__link:hover,[data-md-color-accent=light-blue] .md-search-result__link[data-md-state=active]{background-color:rgba(0,145,234,.1)}[data-md-color-accent=light-blue] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#0091ea}[data-md-color-accent=light-blue] .md-source-file:hover:before{background-color:#0091ea}button[data-md-color-accent=cyan]{background-color:#00b8d4}[data-md-color-accent=cyan] .md-typeset a:active,[data-md-color-accent=cyan] .md-typeset a:hover{color:#00b8d4}[data-md-color-accent=cyan] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover,[data-md-color-accent=cyan] .md-typeset pre code::-webkit-scrollbar-thumb:hover{background-color:#00b8d4}[data-md-color-accent=cyan] .md-nav__link:focus,[data-md-color-accent=cyan] .md-nav__link:hover,[data-md-color-accent=cyan] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=cyan] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=cyan] .md-typeset .md-clipboard:active:before,[data-md-color-accent=cyan] .md-typeset .md-clipboard:hover:before,[data-md-color-accent=cyan] .md-typeset [id] .headerlink:focus,[data-md-color-accent=cyan] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=cyan] .md-typeset [id]:target .headerlink{color:#00b8d4}[data-md-color-accent=cyan] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#00b8d4}[data-md-color-accent=cyan] .md-search-result__link:hover,[data-md-color-accent=cyan] .md-search-result__link[data-md-state=active]{background-color:rgba(0,184,212,.1)}[data-md-color-accent=cyan] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#00b8d4}[data-md-color-accent=cyan] .md-source-file:hover:before{background-color:#00b8d4}button[data-md-color-accent=teal]{background-color:#00bfa5}[data-md-color-accent=teal] .md-typeset a:active,[data-md-color-accent=teal] .md-typeset a:hover{color:#00bfa5}[data-md-color-accent=teal] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover,[data-md-color-accent=teal] .md-typeset pre code::-webkit-scrollbar-thumb:hover{background-color:#00bfa5}[data-md-color-accent=teal] .md-nav__link:focus,[data-md-color-accent=teal] .md-nav__link:hover,[data-md-color-accent=teal] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=teal] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=teal] .md-typeset .md-clipboard:active:before,[data-md-color-accent=teal] .md-typeset .md-clipboard:hover:before,[data-md-color-accent=teal] .md-typeset [id] .headerlink:focus,[data-md-color-accent=teal] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=teal] .md-typeset [id]:target .headerlink{color:#00bfa5}[data-md-color-accent=teal] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#00bfa5}[data-md-color-accent=teal] .md-search-result__link:hover,[data-md-color-accent=teal] .md-search-result__link[data-md-state=active]{background-color:rgba(0,191,165,.1)}[data-md-color-accent=teal] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#00bfa5}[data-md-color-accent=teal] .md-source-file:hover:before{background-color:#00bfa5}button[data-md-color-accent=green]{background-color:#00c853}[data-md-color-accent=green] .md-typeset a:active,[data-md-color-accent=green] .md-typeset a:hover{color:#00c853}[data-md-color-accent=green] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover,[data-md-color-accent=green] .md-typeset pre code::-webkit-scrollbar-thumb:hover{background-color:#00c853}[data-md-color-accent=green] .md-nav__link:focus,[data-md-color-accent=green] .md-nav__link:hover,[data-md-color-accent=green] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=green] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=green] .md-typeset .md-clipboard:active:before,[data-md-color-accent=green] .md-typeset .md-clipboard:hover:before,[data-md-color-accent=green] .md-typeset [id] .headerlink:focus,[data-md-color-accent=green] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=green] .md-typeset [id]:target .headerlink{color:#00c853}[data-md-color-accent=green] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#00c853}[data-md-color-accent=green] .md-search-result__link:hover,[data-md-color-accent=green] .md-search-result__link[data-md-state=active]{background-color:rgba(0,200,83,.1)}[data-md-color-accent=green] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#00c853}[data-md-color-accent=green] .md-source-file:hover:before{background-color:#00c853}button[data-md-color-accent=light-green]{background-color:#64dd17}[data-md-color-accent=light-green] .md-typeset a:active,[data-md-color-accent=light-green] .md-typeset a:hover{color:#64dd17}[data-md-color-accent=light-green] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover,[data-md-color-accent=light-green] .md-typeset pre code::-webkit-scrollbar-thumb:hover{background-color:#64dd17}[data-md-color-accent=light-green] .md-nav__link:focus,[data-md-color-accent=light-green] .md-nav__link:hover,[data-md-color-accent=light-green] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=light-green] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=light-green] .md-typeset .md-clipboard:active:before,[data-md-color-accent=light-green] .md-typeset .md-clipboard:hover:before,[data-md-color-accent=light-green] .md-typeset [id] .headerlink:focus,[data-md-color-accent=light-green] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=light-green] .md-typeset [id]:target .headerlink{color:#64dd17}[data-md-color-accent=light-green] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#64dd17}[data-md-color-accent=light-green] .md-search-result__link:hover,[data-md-color-accent=light-green] .md-search-result__link[data-md-state=active]{background-color:rgba(100,221,23,.1)}[data-md-color-accent=light-green] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#64dd17}[data-md-color-accent=light-green] .md-source-file:hover:before{background-color:#64dd17}button[data-md-color-accent=lime]{background-color:#aeea00}[data-md-color-accent=lime] .md-typeset a:active,[data-md-color-accent=lime] .md-typeset a:hover{color:#aeea00}[data-md-color-accent=lime] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover,[data-md-color-accent=lime] .md-typeset pre code::-webkit-scrollbar-thumb:hover{background-color:#aeea00}[data-md-color-accent=lime] .md-nav__link:focus,[data-md-color-accent=lime] .md-nav__link:hover,[data-md-color-accent=lime] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=lime] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=lime] .md-typeset .md-clipboard:active:before,[data-md-color-accent=lime] .md-typeset .md-clipboard:hover:before,[data-md-color-accent=lime] .md-typeset [id] .headerlink:focus,[data-md-color-accent=lime] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=lime] .md-typeset [id]:target .headerlink{color:#aeea00}[data-md-color-accent=lime] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#aeea00}[data-md-color-accent=lime] .md-search-result__link:hover,[data-md-color-accent=lime] .md-search-result__link[data-md-state=active]{background-color:rgba(174,234,0,.1)}[data-md-color-accent=lime] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#aeea00}[data-md-color-accent=lime] .md-source-file:hover:before{background-color:#aeea00}button[data-md-color-accent=yellow]{background-color:#ffd600}[data-md-color-accent=yellow] .md-typeset a:active,[data-md-color-accent=yellow] .md-typeset a:hover{color:#ffd600}[data-md-color-accent=yellow] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover,[data-md-color-accent=yellow] .md-typeset pre code::-webkit-scrollbar-thumb:hover{background-color:#ffd600}[data-md-color-accent=yellow] .md-nav__link:focus,[data-md-color-accent=yellow] .md-nav__link:hover,[data-md-color-accent=yellow] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=yellow] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=yellow] .md-typeset .md-clipboard:active:before,[data-md-color-accent=yellow] .md-typeset .md-clipboard:hover:before,[data-md-color-accent=yellow] .md-typeset [id] .headerlink:focus,[data-md-color-accent=yellow] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=yellow] .md-typeset [id]:target .headerlink{color:#ffd600}[data-md-color-accent=yellow] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#ffd600}[data-md-color-accent=yellow] .md-search-result__link:hover,[data-md-color-accent=yellow] .md-search-result__link[data-md-state=active]{background-color:rgba(255,214,0,.1)}[data-md-color-accent=yellow] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#ffd600}[data-md-color-accent=yellow] .md-source-file:hover:before{background-color:#ffd600}button[data-md-color-accent=amber]{background-color:#ffab00}[data-md-color-accent=amber] .md-typeset a:active,[data-md-color-accent=amber] .md-typeset a:hover{color:#ffab00}[data-md-color-accent=amber] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover,[data-md-color-accent=amber] .md-typeset pre code::-webkit-scrollbar-thumb:hover{background-color:#ffab00}[data-md-color-accent=amber] .md-nav__link:focus,[data-md-color-accent=amber] .md-nav__link:hover,[data-md-color-accent=amber] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=amber] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=amber] .md-typeset .md-clipboard:active:before,[data-md-color-accent=amber] .md-typeset .md-clipboard:hover:before,[data-md-color-accent=amber] .md-typeset [id] .headerlink:focus,[data-md-color-accent=amber] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=amber] .md-typeset [id]:target .headerlink{color:#ffab00}[data-md-color-accent=amber] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#ffab00}[data-md-color-accent=amber] .md-search-result__link:hover,[data-md-color-accent=amber] .md-search-result__link[data-md-state=active]{background-color:rgba(255,171,0,.1)}[data-md-color-accent=amber] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#ffab00}[data-md-color-accent=amber] .md-source-file:hover:before{background-color:#ffab00}button[data-md-color-accent=orange]{background-color:#ff9100}[data-md-color-accent=orange] .md-typeset a:active,[data-md-color-accent=orange] .md-typeset a:hover{color:#ff9100}[data-md-color-accent=orange] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover,[data-md-color-accent=orange] .md-typeset pre code::-webkit-scrollbar-thumb:hover{background-color:#ff9100}[data-md-color-accent=orange] .md-nav__link:focus,[data-md-color-accent=orange] .md-nav__link:hover,[data-md-color-accent=orange] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=orange] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=orange] .md-typeset .md-clipboard:active:before,[data-md-color-accent=orange] .md-typeset .md-clipboard:hover:before,[data-md-color-accent=orange] .md-typeset [id] .headerlink:focus,[data-md-color-accent=orange] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=orange] .md-typeset [id]:target .headerlink{color:#ff9100}[data-md-color-accent=orange] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#ff9100}[data-md-color-accent=orange] .md-search-result__link:hover,[data-md-color-accent=orange] .md-search-result__link[data-md-state=active]{background-color:rgba(255,145,0,.1)}[data-md-color-accent=orange] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#ff9100}[data-md-color-accent=orange] .md-source-file:hover:before{background-color:#ff9100}button[data-md-color-accent=deep-orange]{background-color:#ff6e40}[data-md-color-accent=deep-orange] .md-typeset a:active,[data-md-color-accent=deep-orange] .md-typeset a:hover{color:#ff6e40}[data-md-color-accent=deep-orange] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover,[data-md-color-accent=deep-orange] .md-typeset pre code::-webkit-scrollbar-thumb:hover{background-color:#ff6e40}[data-md-color-accent=deep-orange] .md-nav__link:focus,[data-md-color-accent=deep-orange] .md-nav__link:hover,[data-md-color-accent=deep-orange] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=deep-orange] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=deep-orange] .md-typeset .md-clipboard:active:before,[data-md-color-accent=deep-orange] .md-typeset .md-clipboard:hover:before,[data-md-color-accent=deep-orange] .md-typeset [id] .headerlink:focus,[data-md-color-accent=deep-orange] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=deep-orange] .md-typeset [id]:target .headerlink{color:#ff6e40}[data-md-color-accent=deep-orange] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#ff6e40}[data-md-color-accent=deep-orange] .md-search-result__link:hover,[data-md-color-accent=deep-orange] .md-search-result__link[data-md-state=active]{background-color:rgba(255,110,64,.1)}[data-md-color-accent=deep-orange] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#ff6e40}[data-md-color-accent=deep-orange] .md-source-file:hover:before{background-color:#ff6e40}@media only screen and (max-width:59.9375em){[data-md-color-primary=red] .md-nav__source{background-color:rgba(190,66,64,.9675)}[data-md-color-primary=pink] .md-nav__source{background-color:rgba(185,24,79,.9675)}[data-md-color-primary=purple] .md-nav__source{background-color:rgba(136,57,150,.9675)}[data-md-color-primary=deep-purple] .md-nav__source{background-color:rgba(100,69,154,.9675)}[data-md-color-primary=indigo] .md-nav__source{background-color:rgba(50,64,144,.9675)}[data-md-color-primary=blue] .md-nav__source{background-color:rgba(26,119,193,.9675)}[data-md-color-primary=light-blue] .md-nav__source{background-color:rgba(2,134,194,.9675)}[data-md-color-primary=cyan] .md-nav__source{background-color:rgba(0,150,169,.9675)}[data-md-color-primary=teal] .md-nav__source{background-color:rgba(0,119,108,.9675)}[data-md-color-primary=green] .md-nav__source{background-color:rgba(60,139,64,.9675)}[data-md-color-primary=light-green] .md-nav__source{background-color:rgba(99,142,53,.9675)}[data-md-color-primary=lime] .md-nav__source{background-color:rgba(153,161,41,.9675)}[data-md-color-primary=yellow] .md-nav__source{background-color:rgba(198,134,29,.9675)}[data-md-color-primary=amber] .md-nav__source{background-color:rgba(203,127,0,.9675)}[data-md-color-primary=orange] .md-nav__source{background-color:rgba(200,111,0,.9675)}[data-md-color-primary=deep-orange] .md-nav__source{background-color:rgba(203,89,53,.9675)}[data-md-color-primary=brown] .md-nav__source{background-color:rgba(96,68,57,.9675)}[data-md-color-primary=grey] .md-nav__source{background-color:rgba(93,93,93,.9675)}[data-md-color-primary=blue-grey] .md-nav__source{background-color:rgba(67,88,97,.9675)}[data-md-color-primary=white] .md-nav__source{background-color:rgba(0,0,0,.07);color:rgba(0,0,0,.87)}}@media only screen and (max-width:76.1875em){html [data-md-color-primary=red] .md-nav--primary .md-nav__title--site{background-color:#ef5350}html [data-md-color-primary=pink] .md-nav--primary .md-nav__title--site{background-color:#e91e63}html [data-md-color-primary=purple] .md-nav--primary .md-nav__title--site{background-color:#ab47bc}html [data-md-color-primary=deep-purple] .md-nav--primary .md-nav__title--site{background-color:#7e57c2}html [data-md-color-primary=indigo] .md-nav--primary .md-nav__title--site{background-color:#3f51b5}html [data-md-color-primary=blue] .md-nav--primary .md-nav__title--site{background-color:#2196f3}html [data-md-color-primary=light-blue] .md-nav--primary .md-nav__title--site{background-color:#03a9f4}html [data-md-color-primary=cyan] .md-nav--primary .md-nav__title--site{background-color:#00bcd4}html [data-md-color-primary=teal] .md-nav--primary .md-nav__title--site{background-color:#009688}html [data-md-color-primary=green] .md-nav--primary .md-nav__title--site{background-color:#4caf50}html [data-md-color-primary=light-green] .md-nav--primary .md-nav__title--site{background-color:#7cb342}html [data-md-color-primary=lime] .md-nav--primary .md-nav__title--site{background-color:#c0ca33}html [data-md-color-primary=yellow] .md-nav--primary .md-nav__title--site{background-color:#f9a825}html [data-md-color-primary=amber] .md-nav--primary .md-nav__title--site{background-color:#ffa000}html [data-md-color-primary=orange] .md-nav--primary .md-nav__title--site{background-color:#fb8c00}html [data-md-color-primary=deep-orange] .md-nav--primary .md-nav__title--site{background-color:#ff7043}html [data-md-color-primary=brown] .md-nav--primary .md-nav__title--site{background-color:#795548}html [data-md-color-primary=grey] .md-nav--primary .md-nav__title--site{background-color:#757575}html [data-md-color-primary=blue-grey] .md-nav--primary .md-nav__title--site{background-color:#546e7a}html [data-md-color-primary=white] .md-nav--primary .md-nav__title--site{background-color:#fff;color:rgba(0,0,0,.87)}[data-md-color-primary=white] .md-hero{border-bottom:.1rem solid rgba(0,0,0,.07)}}@media only screen and (min-width:76.25em){[data-md-color-primary=red] .md-tabs{background-color:#ef5350}[data-md-color-primary=pink] .md-tabs{background-color:#e91e63}[data-md-color-primary=purple] .md-tabs{background-color:#ab47bc}[data-md-color-primary=deep-purple] .md-tabs{background-color:#7e57c2}[data-md-color-primary=indigo] .md-tabs{background-color:#3f51b5}[data-md-color-primary=blue] .md-tabs{background-color:#2196f3}[data-md-color-primary=light-blue] .md-tabs{background-color:#03a9f4}[data-md-color-primary=cyan] .md-tabs{background-color:#00bcd4}[data-md-color-primary=teal] .md-tabs{background-color:#009688}[data-md-color-primary=green] .md-tabs{background-color:#4caf50}[data-md-color-primary=light-green] .md-tabs{background-color:#7cb342}[data-md-color-primary=lime] .md-tabs{background-color:#c0ca33}[data-md-color-primary=yellow] .md-tabs{background-color:#f9a825}[data-md-color-primary=amber] .md-tabs{background-color:#ffa000}[data-md-color-primary=orange] .md-tabs{background-color:#fb8c00}[data-md-color-primary=deep-orange] .md-tabs{background-color:#ff7043}[data-md-color-primary=brown] .md-tabs{background-color:#795548}[data-md-color-primary=grey] .md-tabs{background-color:#757575}[data-md-color-primary=blue-grey] .md-tabs{background-color:#546e7a}[data-md-color-primary=white] .md-tabs{border-bottom:.1rem solid rgba(0,0,0,.07);background-color:#fff;color:rgba(0,0,0,.87)}}@media only screen and (min-width:60em){[data-md-color-primary=white] .md-search__input{background-color:rgba(0,0,0,.07)}[data-md-color-primary=white] .md-search__input::-webkit-input-placeholder{color:rgba(0,0,0,.54)}[data-md-color-primary=white] .md-search__input:-ms-input-placeholder,[data-md-color-primary=white] .md-search__input::-ms-input-placeholder{color:rgba(0,0,0,.54)}[data-md-color-primary=white] .md-search__input::placeholder{color:rgba(0,0,0,.54)}} +/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiIsImZpbGUiOiJhc3NldHMvc3R5bGVzaGVldHMvYXBwbGljYXRpb24tcGFsZXR0ZS42MDc5NDc2Yy5jc3MiLCJzb3VyY2VSb290IjoiIn0=*/ \ No newline at end of file diff --git a/3.0/site/assets/stylesheets/application.78aab2dc.css b/3.0/site/assets/stylesheets/application.78aab2dc.css new file mode 100644 index 00000000..541bf451 --- /dev/null +++ b/3.0/site/assets/stylesheets/application.78aab2dc.css @@ -0,0 +1,2 @@ +html{box-sizing:border-box}*,:after,:before{box-sizing:inherit}html{-webkit-text-size-adjust:none;-moz-text-size-adjust:none;-ms-text-size-adjust:none;text-size-adjust:none}body{margin:0}hr{overflow:visible;box-sizing:content-box}a{-webkit-text-decoration-skip:objects}a,button,input,label{-webkit-tap-highlight-color:transparent}a{color:inherit;text-decoration:none}small,sub,sup{font-size:80%}sub,sup{position:relative;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}table{border-collapse:separate;border-spacing:0}td,th{font-weight:400;vertical-align:top}button{margin:0;padding:0;border:0;outline-style:none;background:transparent;font-size:inherit}input{border:0;outline:0}.md-clipboard:before,.md-icon,.md-nav__button,.md-nav__link:after,.md-nav__title:before,.md-search-result__article--document:before,.md-source-file:before,.md-typeset .admonition>.admonition-title:before,.md-typeset .admonition>summary:before,.md-typeset .critic.comment:before,.md-typeset .footnote-backref,.md-typeset .task-list-control .task-list-indicator:before,.md-typeset details>.admonition-title:before,.md-typeset details>summary:before,.md-typeset summary:after{font-family:Material Icons;font-style:normal;font-variant:normal;font-weight:400;line-height:1;text-transform:none;white-space:nowrap;speak:none;word-wrap:normal;direction:ltr}.md-content__icon,.md-footer-nav__button,.md-header-nav__button,.md-nav__button,.md-nav__title:before,.md-search-result__article--document:before{display:inline-block;margin:.4rem;padding:.8rem;font-size:2.4rem;cursor:pointer}.md-icon--arrow-back:before{content:"\E5C4"}.md-icon--arrow-forward:before{content:"\E5C8"}.md-icon--menu:before{content:"\E5D2"}.md-icon--search:before{content:"\E8B6"}[dir=rtl] .md-icon--arrow-back:before{content:"\E5C8"}[dir=rtl] .md-icon--arrow-forward:before{content:"\E5C4"}body{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}body,input{color:rgba(0,0,0,.87);-webkit-font-feature-settings:"kern","liga";font-feature-settings:"kern","liga";font-family:Helvetica Neue,Helvetica,Arial,sans-serif}code,kbd,pre{color:rgba(0,0,0,.87);-webkit-font-feature-settings:"kern";font-feature-settings:"kern";font-family:Courier New,Courier,monospace}.md-typeset{font-size:1.6rem;line-height:1.6;-webkit-print-color-adjust:exact}.md-typeset blockquote,.md-typeset ol,.md-typeset p,.md-typeset ul{margin:1em 0}.md-typeset h1{margin:0 0 4rem;color:rgba(0,0,0,.54);font-size:3.125rem;line-height:1.3}.md-typeset h1,.md-typeset h2{font-weight:300;letter-spacing:-.01em}.md-typeset h2{margin:4rem 0 1.6rem;font-size:2.5rem;line-height:1.4}.md-typeset h3{margin:3.2rem 0 1.6rem;font-size:2rem;font-weight:400;letter-spacing:-.01em;line-height:1.5}.md-typeset h2+h3{margin-top:1.6rem}.md-typeset h4{font-size:1.6rem}.md-typeset h4,.md-typeset h5,.md-typeset h6{margin:1.6rem 0;font-weight:700;letter-spacing:-.01em}.md-typeset h5,.md-typeset h6{color:rgba(0,0,0,.54);font-size:1.28rem}.md-typeset h5{text-transform:uppercase}.md-typeset hr{margin:1.5em 0;border-bottom:.1rem dotted rgba(0,0,0,.26)}.md-typeset a{color:#3f51b5;word-break:break-word}.md-typeset a,.md-typeset a:before{transition:color .125s}.md-typeset a:active,.md-typeset a:hover{color:#536dfe}.md-typeset code,.md-typeset pre{background-color:hsla(0,0%,93%,.5);color:#37474f;font-size:85%;direction:ltr}.md-typeset code{margin:0 .29412em;padding:.07353em 0;border-radius:.2rem;box-shadow:.29412em 0 0 hsla(0,0%,93%,.5),-.29412em 0 0 hsla(0,0%,93%,.5);word-break:break-word;-webkit-box-decoration-break:clone;box-decoration-break:clone}.md-typeset h1 code,.md-typeset h2 code,.md-typeset h3 code,.md-typeset h4 code,.md-typeset h5 code,.md-typeset h6 code{margin:0;background-color:transparent;box-shadow:none}.md-typeset a>code{margin:inherit;padding:inherit;border-radius:none;background-color:inherit;color:inherit;box-shadow:none}.md-typeset pre{position:relative;margin:1em 0;border-radius:.2rem;line-height:1.4;-webkit-overflow-scrolling:touch}.md-typeset pre>code{display:block;margin:0;padding:1.05rem 1.2rem;background-color:transparent;font-size:inherit;box-shadow:none;-webkit-box-decoration-break:none;box-decoration-break:none;overflow:auto}.md-typeset pre>code::-webkit-scrollbar{width:.4rem;height:.4rem}.md-typeset pre>code::-webkit-scrollbar-thumb{background-color:rgba(0,0,0,.26)}.md-typeset pre>code::-webkit-scrollbar-thumb:hover{background-color:#536dfe}.md-typeset kbd{padding:0 .29412em;border:.1rem solid #c9c9c9;border-radius:.3rem;border-bottom-color:#bcbcbc;background-color:#fcfcfc;color:#555;font-size:85%;box-shadow:0 .1rem 0 #b0b0b0;word-break:break-word}.md-typeset mark{margin:0 .25em;padding:.0625em 0;border-radius:.2rem;background-color:rgba(255,235,59,.5);box-shadow:.25em 0 0 rgba(255,235,59,.5),-.25em 0 0 rgba(255,235,59,.5);word-break:break-word;-webkit-box-decoration-break:clone;box-decoration-break:clone}.md-typeset abbr{border-bottom:.1rem dotted rgba(0,0,0,.54);text-decoration:none;cursor:help}.md-typeset small{opacity:.75}.md-typeset sub,.md-typeset sup{margin-left:.07812em}[dir=rtl] .md-typeset sub,[dir=rtl] .md-typeset sup{margin-right:.07812em;margin-left:0}.md-typeset blockquote{padding-left:1.2rem;border-left:.4rem solid rgba(0,0,0,.26);color:rgba(0,0,0,.54)}[dir=rtl] .md-typeset blockquote{padding-right:1.2rem;padding-left:0;border-right:.4rem solid rgba(0,0,0,.26);border-left:initial}.md-typeset ul{list-style-type:disc}.md-typeset ol,.md-typeset ul{margin-left:.625em;padding:0}[dir=rtl] .md-typeset ol,[dir=rtl] .md-typeset ul{margin-right:.625em;margin-left:0}.md-typeset ol ol,.md-typeset ul ol{list-style-type:lower-alpha}.md-typeset ol ol ol,.md-typeset ul ol ol{list-style-type:lower-roman}.md-typeset ol li,.md-typeset ul li{margin-bottom:.5em;margin-left:1.25em}[dir=rtl] .md-typeset ol li,[dir=rtl] .md-typeset ul li{margin-right:1.25em;margin-left:0}.md-typeset ol li blockquote,.md-typeset ol li p,.md-typeset ul li blockquote,.md-typeset ul li p{margin:.5em 0}.md-typeset ol li:last-child,.md-typeset ul li:last-child{margin-bottom:0}.md-typeset ol li ol,.md-typeset ol li ul,.md-typeset ul li ol,.md-typeset ul li ul{margin:.5em 0 .5em .625em}[dir=rtl] .md-typeset ol li ol,[dir=rtl] .md-typeset ol li ul,[dir=rtl] .md-typeset ul li ol,[dir=rtl] .md-typeset ul li ul{margin-right:.625em;margin-left:0}.md-typeset dd{margin:1em 0 1em 1.875em}[dir=rtl] .md-typeset dd{margin-right:1.875em;margin-left:0}.md-typeset iframe,.md-typeset img,.md-typeset svg{max-width:100%}.md-typeset table:not([class]){box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12),0 3px 1px -2px rgba(0,0,0,.2);display:inline-block;max-width:100%;border-radius:.2rem;font-size:1.28rem;overflow:auto;-webkit-overflow-scrolling:touch}.md-typeset table:not([class])+*{margin-top:1.5em}.md-typeset table:not([class]) td:not([align]),.md-typeset table:not([class]) th:not([align]){text-align:left}[dir=rtl] .md-typeset table:not([class]) td:not([align]),[dir=rtl] .md-typeset table:not([class]) th:not([align]){text-align:right}.md-typeset table:not([class]) th{min-width:10rem;padding:1.2rem 1.6rem;background-color:rgba(0,0,0,.54);color:#fff;vertical-align:top}.md-typeset table:not([class]) td{padding:1.2rem 1.6rem;border-top:.1rem solid rgba(0,0,0,.07);vertical-align:top}.md-typeset table:not([class]) tr:first-child td{border-top:0}.md-typeset table:not([class]) a{word-break:normal}.md-typeset__scrollwrap{margin:1em -1.6rem;overflow-x:auto;-webkit-overflow-scrolling:touch}.md-typeset .md-typeset__table{display:inline-block;margin-bottom:.5em;padding:0 1.6rem}.md-typeset .md-typeset__table table{display:table;width:100%;margin:0;overflow:hidden}html{font-size:62.5%;overflow-x:hidden}body,html{height:100%}body{position:relative}hr{display:block;height:.1rem;padding:0;border:0}.md-svg{display:none}.md-grid{max-width:122rem;margin-right:auto;margin-left:auto}.md-container,.md-main{overflow:auto}.md-container{display:table;width:100%;height:100%;padding-top:4.8rem;table-layout:fixed}.md-main{display:table-row;height:100%}.md-main__inner{height:100%;padding-top:3rem;padding-bottom:.1rem}.md-toggle{display:none}.md-overlay{position:fixed;top:0;width:0;height:0;transition:width 0s .25s,height 0s .25s,opacity .25s;background-color:rgba(0,0,0,.54);opacity:0;z-index:3}.md-flex{display:table}.md-flex__cell{display:table-cell;position:relative;vertical-align:top}.md-flex__cell--shrink{width:0}.md-flex__cell--stretch{display:table;width:100%;table-layout:fixed}.md-flex__ellipsis{display:table-cell;text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.md-skip{position:fixed;width:.1rem;height:.1rem;margin:1rem;padding:.6rem 1rem;clip:rect(.1rem);-webkit-transform:translateY(.8rem);transform:translateY(.8rem);border-radius:.2rem;background-color:rgba(0,0,0,.87);color:#fff;font-size:1.28rem;opacity:0;overflow:hidden}.md-skip:focus{width:auto;height:auto;clip:auto;-webkit-transform:translateX(0);transform:translateX(0);transition:opacity .175s 75ms,-webkit-transform .25s cubic-bezier(.4,0,.2,1);transition:transform .25s cubic-bezier(.4,0,.2,1),opacity .175s 75ms;transition:transform .25s cubic-bezier(.4,0,.2,1),opacity .175s 75ms,-webkit-transform .25s cubic-bezier(.4,0,.2,1);opacity:1;z-index:10}@page{margin:25mm}.md-clipboard{position:absolute;top:.6rem;right:.6rem;width:2.8rem;height:2.8rem;border-radius:.2rem;font-size:1.6rem;cursor:pointer;z-index:1;-webkit-backface-visibility:hidden;backface-visibility:hidden}.md-clipboard:before{transition:color .25s,opacity .25s;color:rgba(0,0,0,.07);content:"\E14D"}.codehilite:hover .md-clipboard:before,.md-typeset .highlight:hover .md-clipboard:before,pre:hover .md-clipboard:before{color:rgba(0,0,0,.54)}.md-clipboard:focus:before,.md-clipboard:hover:before{color:#536dfe}.md-clipboard__message{display:block;position:absolute;top:0;right:3.4rem;padding:.6rem 1rem;-webkit-transform:translateX(.8rem);transform:translateX(.8rem);transition:opacity .175s,-webkit-transform .25s cubic-bezier(.9,.1,.9,0);transition:transform .25s cubic-bezier(.9,.1,.9,0),opacity .175s;transition:transform .25s cubic-bezier(.9,.1,.9,0),opacity .175s,-webkit-transform .25s cubic-bezier(.9,.1,.9,0);border-radius:.2rem;background-color:rgba(0,0,0,.54);color:#fff;font-size:1.28rem;white-space:nowrap;opacity:0;pointer-events:none}.md-clipboard__message--active{-webkit-transform:translateX(0);transform:translateX(0);transition:opacity .175s 75ms,-webkit-transform .25s cubic-bezier(.4,0,.2,1);transition:transform .25s cubic-bezier(.4,0,.2,1),opacity .175s 75ms;transition:transform .25s cubic-bezier(.4,0,.2,1),opacity .175s 75ms,-webkit-transform .25s cubic-bezier(.4,0,.2,1);opacity:1;pointer-events:auto}.md-clipboard__message:before{content:attr(aria-label)}.md-clipboard__message:after{display:block;position:absolute;top:50%;right:-.4rem;width:0;margin-top:-.4rem;border-width:.4rem 0 .4rem .4rem;border-style:solid;border-color:transparent rgba(0,0,0,.54);content:""}.md-content__inner{margin:0 1.6rem 2.4rem;padding-top:1.2rem}.md-content__inner:before{display:block;height:.8rem;content:""}.md-content__inner>:last-child{margin-bottom:0}.md-content__icon{position:relative;margin:.8rem 0;padding:0;float:right}.md-typeset .md-content__icon{color:rgba(0,0,0,.26)}.md-header{position:fixed;top:0;right:0;left:0;height:4.8rem;transition:background-color .25s,color .25s;background-color:#3f51b5;color:#fff;box-shadow:none;z-index:2;-webkit-backface-visibility:hidden;backface-visibility:hidden}.no-js .md-header{transition:none;box-shadow:none}.md-header[data-md-state=shadow]{transition:background-color .25s,color .25s,box-shadow .25s;box-shadow:0 0 .4rem rgba(0,0,0,.1),0 .4rem .8rem rgba(0,0,0,.2)}.md-header-nav{padding:0 .4rem}.md-header-nav__button{position:relative;transition:opacity .25s;z-index:1}.md-header-nav__button:hover{opacity:.7}.md-header-nav__button.md-logo *{display:block}.no-js .md-header-nav__button.md-icon--search{display:none}.md-header-nav__topic{display:block;position:absolute;transition:opacity .15s,-webkit-transform .4s cubic-bezier(.1,.7,.1,1);transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .15s;transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .15s,-webkit-transform .4s cubic-bezier(.1,.7,.1,1);text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.md-header-nav__topic+.md-header-nav__topic{-webkit-transform:translateX(2.5rem);transform:translateX(2.5rem);transition:opacity .15s,-webkit-transform .4s cubic-bezier(1,.7,.1,.1);transition:transform .4s cubic-bezier(1,.7,.1,.1),opacity .15s;transition:transform .4s cubic-bezier(1,.7,.1,.1),opacity .15s,-webkit-transform .4s cubic-bezier(1,.7,.1,.1);opacity:0;z-index:-1;pointer-events:none}[dir=rtl] .md-header-nav__topic+.md-header-nav__topic{-webkit-transform:translateX(-2.5rem);transform:translateX(-2.5rem)}.no-js .md-header-nav__topic{position:static}.no-js .md-header-nav__topic+.md-header-nav__topic{display:none}.md-header-nav__title{padding:0 2rem;font-size:1.8rem;line-height:4.8rem}.md-header-nav__title[data-md-state=active] .md-header-nav__topic{-webkit-transform:translateX(-2.5rem);transform:translateX(-2.5rem);transition:opacity .15s,-webkit-transform .4s cubic-bezier(1,.7,.1,.1);transition:transform .4s cubic-bezier(1,.7,.1,.1),opacity .15s;transition:transform .4s cubic-bezier(1,.7,.1,.1),opacity .15s,-webkit-transform .4s cubic-bezier(1,.7,.1,.1);opacity:0;z-index:-1;pointer-events:none}[dir=rtl] .md-header-nav__title[data-md-state=active] .md-header-nav__topic{-webkit-transform:translateX(2.5rem);transform:translateX(2.5rem)}.md-header-nav__title[data-md-state=active] .md-header-nav__topic+.md-header-nav__topic{-webkit-transform:translateX(0);transform:translateX(0);transition:opacity .15s,-webkit-transform .4s cubic-bezier(.1,.7,.1,1);transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .15s;transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .15s,-webkit-transform .4s cubic-bezier(.1,.7,.1,1);opacity:1;z-index:0;pointer-events:auto}.md-header-nav__source{display:none}.md-hero{transition:background .25s;background-color:#3f51b5;color:#fff;font-size:2rem;overflow:hidden}.md-hero__inner{margin-top:2rem;padding:1.6rem 1.6rem .8rem;transition:opacity .25s,-webkit-transform .4s cubic-bezier(.1,.7,.1,1);transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .25s;transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .25s,-webkit-transform .4s cubic-bezier(.1,.7,.1,1);transition-delay:.1s}[data-md-state=hidden] .md-hero__inner{pointer-events:none;-webkit-transform:translateY(1.25rem);transform:translateY(1.25rem);transition:opacity .1s 0s,-webkit-transform 0s .4s;transition:transform 0s .4s,opacity .1s 0s;transition:transform 0s .4s,opacity .1s 0s,-webkit-transform 0s .4s;opacity:0}.md-hero--expand .md-hero__inner{margin-bottom:2.4rem}.md-footer-nav{background-color:rgba(0,0,0,.87);color:#fff}.md-footer-nav__inner{padding:.4rem;overflow:auto}.md-footer-nav__link{padding-top:2.8rem;padding-bottom:.8rem;transition:opacity .25s}.md-footer-nav__link:hover{opacity:.7}.md-footer-nav__link--prev{width:25%;float:left}[dir=rtl] .md-footer-nav__link--prev{float:right}.md-footer-nav__link--next{width:75%;float:right;text-align:right}[dir=rtl] .md-footer-nav__link--next{float:left;text-align:left}.md-footer-nav__button{transition:background .25s}.md-footer-nav__title{position:relative;padding:0 2rem;font-size:1.8rem;line-height:4.8rem}.md-footer-nav__direction{position:absolute;right:0;left:0;margin-top:-2rem;padding:0 2rem;color:hsla(0,0%,100%,.7);font-size:1.5rem}.md-footer-meta{background-color:rgba(0,0,0,.895)}.md-footer-meta__inner{padding:.4rem;overflow:auto}html .md-footer-meta.md-typeset a{color:hsla(0,0%,100%,.7)}html .md-footer-meta.md-typeset a:focus,html .md-footer-meta.md-typeset a:hover{color:#fff}.md-footer-copyright{margin:0 1.2rem;padding:.8rem 0;color:hsla(0,0%,100%,.3);font-size:1.28rem}.md-footer-copyright__highlight{color:hsla(0,0%,100%,.7)}.md-footer-social{margin:0 .8rem;padding:.4rem 0 1.2rem}.md-footer-social__link{display:inline-block;width:3.2rem;height:3.2rem;font-size:1.6rem;text-align:center}.md-footer-social__link:before{line-height:1.9}.md-nav{font-size:1.4rem;line-height:1.3}.md-nav__title{display:block;padding:0 1.2rem;font-weight:700;text-overflow:ellipsis;overflow:hidden}.md-nav__title:before{display:none;content:"\E5C4"}[dir=rtl] .md-nav__title:before{content:"\E5C8"}.md-nav__title .md-nav__button{display:none}.md-nav__list{margin:0;padding:0;list-style:none}.md-nav__item{padding:0 1.2rem}.md-nav__item:last-child{padding-bottom:1.2rem}.md-nav__item .md-nav__item{padding-right:0}[dir=rtl] .md-nav__item .md-nav__item{padding-right:1.2rem;padding-left:0}.md-nav__item .md-nav__item:last-child{padding-bottom:0}.md-nav__button img{width:100%;height:auto}.md-nav__link{display:block;margin-top:.625em;transition:color .125s;text-overflow:ellipsis;cursor:pointer;overflow:hidden}.md-nav__item--nested>.md-nav__link:after{content:"\E313"}html .md-nav__link[for=toc],html .md-nav__link[for=toc]+.md-nav__link:after,html .md-nav__link[for=toc]~.md-nav{display:none}.md-nav__link[data-md-state=blur]{color:rgba(0,0,0,.54)}.md-nav__link--active,.md-nav__link:active{color:#3f51b5}.md-nav__item--nested>.md-nav__link{color:inherit}.md-nav__link:focus,.md-nav__link:hover{color:#536dfe}.md-nav__source,.no-js .md-search{display:none}.md-search__overlay{opacity:0;z-index:1}.md-search__form{position:relative}.md-search__input{position:relative;padding:0 4.4rem 0 7.2rem;text-overflow:ellipsis;z-index:2}[dir=rtl] .md-search__input{padding:0 7.2rem 0 4.4rem}.md-search__input::-webkit-input-placeholder{transition:color .25s cubic-bezier(.1,.7,.1,1)}.md-search__input:-ms-input-placeholder,.md-search__input::-ms-input-placeholder{transition:color .25s cubic-bezier(.1,.7,.1,1)}.md-search__input::placeholder{transition:color .25s cubic-bezier(.1,.7,.1,1)}.md-search__input::-webkit-input-placeholder,.md-search__input~.md-search__icon{color:rgba(0,0,0,.54)}.md-search__input:-ms-input-placeholder,.md-search__input::-ms-input-placeholder,.md-search__input~.md-search__icon{color:rgba(0,0,0,.54)}.md-search__input::placeholder,.md-search__input~.md-search__icon{color:rgba(0,0,0,.54)}.md-search__input::-ms-clear{display:none}.md-search__icon{position:absolute;transition:color .25s cubic-bezier(.1,.7,.1,1),opacity .25s;font-size:2.4rem;cursor:pointer;z-index:2}.md-search__icon:hover{opacity:.7}.md-search__icon[for=search]{top:.6rem;left:1rem}[dir=rtl] .md-search__icon[for=search]{right:1rem;left:auto}.md-search__icon[for=search]:before{content:"\E8B6"}.md-search__icon[type=reset]{top:.6rem;right:1rem;-webkit-transform:scale(.125);transform:scale(.125);transition:opacity .15s,-webkit-transform .15s cubic-bezier(.1,.7,.1,1);transition:transform .15s cubic-bezier(.1,.7,.1,1),opacity .15s;transition:transform .15s cubic-bezier(.1,.7,.1,1),opacity .15s,-webkit-transform .15s cubic-bezier(.1,.7,.1,1);opacity:0}[dir=rtl] .md-search__icon[type=reset]{right:auto;left:1rem}[data-md-toggle=search]:checked~.md-header .md-search__input:valid~.md-search__icon[type=reset]{-webkit-transform:scale(1);transform:scale(1);opacity:1}[data-md-toggle=search]:checked~.md-header .md-search__input:valid~.md-search__icon[type=reset]:hover{opacity:.7}.md-search__output{position:absolute;width:100%;border-radius:0 0 .2rem .2rem;overflow:hidden;z-index:1}.md-search__scrollwrap{height:100%;background-color:#fff;box-shadow:inset 0 .1rem 0 rgba(0,0,0,.07);overflow-y:auto;-webkit-overflow-scrolling:touch}.md-search-result{color:rgba(0,0,0,.87);word-break:break-word}.md-search-result__meta{padding:0 1.6rem;background-color:rgba(0,0,0,.07);color:rgba(0,0,0,.54);font-size:1.28rem;line-height:3.6rem}.md-search-result__list{margin:0;padding:0;border-top:.1rem solid rgba(0,0,0,.07);list-style:none}.md-search-result__item{box-shadow:0 -.1rem 0 rgba(0,0,0,.07)}.md-search-result__link{display:block;transition:background .25s;outline:0;overflow:hidden}.md-search-result__link:hover,.md-search-result__link[data-md-state=active]{background-color:rgba(83,109,254,.1)}.md-search-result__link:hover .md-search-result__article:before,.md-search-result__link[data-md-state=active] .md-search-result__article:before{opacity:.7}.md-search-result__link:last-child .md-search-result__teaser{margin-bottom:1.2rem}.md-search-result__article{position:relative;padding:0 1.6rem;overflow:auto}.md-search-result__article--document:before{position:absolute;left:0;margin:.2rem;transition:opacity .25s;color:rgba(0,0,0,.54);content:"\E880"}[dir=rtl] .md-search-result__article--document:before{right:0;left:auto}.md-search-result__article--document .md-search-result__title{margin:1.1rem 0;font-size:1.6rem;font-weight:400;line-height:1.4}.md-search-result__title{margin:.5em 0;font-size:1.28rem;font-weight:700;line-height:1.4}.md-search-result__teaser{display:-webkit-box;max-height:3.3rem;margin:.5em 0;color:rgba(0,0,0,.54);font-size:1.28rem;line-height:1.4;text-overflow:ellipsis;overflow:hidden;-webkit-line-clamp:2}.md-search-result em{font-style:normal;font-weight:700;text-decoration:underline}.md-sidebar{position:absolute;width:24.2rem;padding:2.4rem 0;overflow:hidden}.md-sidebar[data-md-state=lock]{position:fixed;top:4.8rem}.md-sidebar--secondary{display:none}.md-sidebar__scrollwrap{max-height:100%;margin:0 .4rem;overflow-y:auto;-webkit-backface-visibility:hidden;backface-visibility:hidden}.md-sidebar__scrollwrap::-webkit-scrollbar{width:.4rem;height:.4rem}.md-sidebar__scrollwrap::-webkit-scrollbar-thumb{background-color:rgba(0,0,0,.26)}.md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#536dfe}@-webkit-keyframes md-source__facts--done{0%{height:0}to{height:1.3rem}}@keyframes md-source__facts--done{0%{height:0}to{height:1.3rem}}@-webkit-keyframes md-source__fact--done{0%{-webkit-transform:translateY(100%);transform:translateY(100%);opacity:0}50%{opacity:0}to{-webkit-transform:translateY(0);transform:translateY(0);opacity:1}}@keyframes md-source__fact--done{0%{-webkit-transform:translateY(100%);transform:translateY(100%);opacity:0}50%{opacity:0}to{-webkit-transform:translateY(0);transform:translateY(0);opacity:1}}.md-source{display:block;padding-right:1.2rem;transition:opacity .25s;font-size:1.3rem;line-height:1.2;white-space:nowrap}[dir=rtl] .md-source{padding-right:0;padding-left:1.2rem}.md-source:hover{opacity:.7}.md-source:after,.md-source__icon{display:inline-block;height:4.8rem;content:"";vertical-align:middle}.md-source__icon{width:4.8rem}.md-source__icon svg{width:2.4rem;height:2.4rem;margin-top:1.2rem;margin-left:1.2rem}[dir=rtl] .md-source__icon svg{margin-right:1.2rem;margin-left:0}.md-source__icon+.md-source__repository{margin-left:-4.4rem;padding-left:4rem}[dir=rtl] .md-source__icon+.md-source__repository{margin-right:-4.4rem;margin-left:0;padding-right:4rem;padding-left:0}.md-source__repository{display:inline-block;max-width:100%;margin-left:1.2rem;font-weight:700;text-overflow:ellipsis;overflow:hidden;vertical-align:middle}.md-source__facts{margin:0;padding:0;font-size:1.1rem;font-weight:700;list-style-type:none;opacity:.75;overflow:hidden}[data-md-state=done] .md-source__facts{-webkit-animation:md-source__facts--done .25s ease-in;animation:md-source__facts--done .25s ease-in}.md-source__fact{float:left}[dir=rtl] .md-source__fact{float:right}[data-md-state=done] .md-source__fact{-webkit-animation:md-source__fact--done .4s ease-out;animation:md-source__fact--done .4s ease-out}.md-source__fact:before{margin:0 .2rem;content:"\B7"}.md-source__fact:first-child:before{display:none}.md-source-file{display:inline-block;margin:1em .5em 1em 0;padding-right:.5rem;border-radius:.2rem;background-color:rgba(0,0,0,.07);font-size:1.28rem;list-style-type:none;cursor:pointer;overflow:hidden}.md-source-file:before{display:inline-block;margin-right:.5rem;padding:.5rem;background-color:rgba(0,0,0,.26);color:#fff;font-size:1.6rem;content:"\E86F";vertical-align:middle}html .md-source-file{transition:background .4s,color .4s,box-shadow .4s cubic-bezier(.4,0,.2,1)}html .md-source-file:before{transition:inherit}html body .md-typeset .md-source-file{color:rgba(0,0,0,.54)}.md-source-file:hover{box-shadow:0 0 8px rgba(0,0,0,.18),0 8px 16px rgba(0,0,0,.36)}.md-source-file:hover:before{background-color:#536dfe}.md-tabs{width:100%;transition:background .25s;background-color:#3f51b5;color:#fff;overflow:auto}.md-tabs__list{margin:0;margin-left:.4rem;padding:0;list-style:none;white-space:nowrap}.md-tabs__item{display:inline-block;height:4.8rem;padding-right:1.2rem;padding-left:1.2rem}.md-tabs__link{display:block;margin-top:1.6rem;transition:opacity .25s,-webkit-transform .4s cubic-bezier(.1,.7,.1,1);transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .25s;transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .25s,-webkit-transform .4s cubic-bezier(.1,.7,.1,1);font-size:1.4rem;opacity:.7}.md-tabs__link--active,.md-tabs__link:hover{color:inherit;opacity:1}.md-tabs__item:nth-child(2) .md-tabs__link{transition-delay:.02s}.md-tabs__item:nth-child(3) .md-tabs__link{transition-delay:.04s}.md-tabs__item:nth-child(4) .md-tabs__link{transition-delay:.06s}.md-tabs__item:nth-child(5) .md-tabs__link{transition-delay:.08s}.md-tabs__item:nth-child(6) .md-tabs__link{transition-delay:.1s}.md-tabs__item:nth-child(7) .md-tabs__link{transition-delay:.12s}.md-tabs__item:nth-child(8) .md-tabs__link{transition-delay:.14s}.md-tabs__item:nth-child(9) .md-tabs__link{transition-delay:.16s}.md-tabs__item:nth-child(10) .md-tabs__link{transition-delay:.18s}.md-tabs__item:nth-child(11) .md-tabs__link{transition-delay:.2s}.md-tabs__item:nth-child(12) .md-tabs__link{transition-delay:.22s}.md-tabs__item:nth-child(13) .md-tabs__link{transition-delay:.24s}.md-tabs__item:nth-child(14) .md-tabs__link{transition-delay:.26s}.md-tabs__item:nth-child(15) .md-tabs__link{transition-delay:.28s}.md-tabs__item:nth-child(16) .md-tabs__link{transition-delay:.3s}.md-tabs[data-md-state=hidden]{pointer-events:none}.md-tabs[data-md-state=hidden] .md-tabs__link{-webkit-transform:translateY(50%);transform:translateY(50%);transition:color .25s,opacity .1s,-webkit-transform 0s .4s;transition:color .25s,transform 0s .4s,opacity .1s;transition:color .25s,transform 0s .4s,opacity .1s,-webkit-transform 0s .4s;opacity:0}.md-typeset .admonition,.md-typeset details{box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12),0 3px 1px -2px rgba(0,0,0,.2);position:relative;margin:1.5625em 0;padding:0 1.2rem;border-left:.4rem solid #448aff;border-radius:.2rem;font-size:1.28rem;overflow:auto}[dir=rtl] .md-typeset .admonition,[dir=rtl] .md-typeset details{border-right:.4rem solid #448aff;border-left:none}html .md-typeset .admonition>:last-child,html .md-typeset details>:last-child{margin-bottom:1.2rem}.md-typeset .admonition .admonition,.md-typeset .admonition details,.md-typeset details .admonition,.md-typeset details details{margin:1em 0}.md-typeset .admonition>.admonition-title,.md-typeset .admonition>summary,.md-typeset details>.admonition-title,.md-typeset details>summary{margin:0 -1.2rem;padding:.8rem 1.2rem .8rem 4rem;border-bottom:.1rem solid rgba(68,138,255,.1);background-color:rgba(68,138,255,.1);font-weight:700}[dir=rtl] .md-typeset .admonition>.admonition-title,[dir=rtl] .md-typeset .admonition>summary,[dir=rtl] .md-typeset details>.admonition-title,[dir=rtl] .md-typeset details>summary{padding:.8rem 4rem .8rem 1.2rem}.md-typeset .admonition>.admonition-title:last-child,.md-typeset .admonition>summary:last-child,.md-typeset details>.admonition-title:last-child,.md-typeset details>summary:last-child{margin-bottom:0}.md-typeset .admonition>.admonition-title:before,.md-typeset .admonition>summary:before,.md-typeset details>.admonition-title:before,.md-typeset details>summary:before{position:absolute;left:1.2rem;color:#448aff;font-size:2rem;content:"\E3C9"}[dir=rtl] .md-typeset .admonition>.admonition-title:before,[dir=rtl] .md-typeset .admonition>summary:before,[dir=rtl] .md-typeset details>.admonition-title:before,[dir=rtl] .md-typeset details>summary:before{right:1.2rem;left:auto}.md-typeset .admonition.abstract,.md-typeset .admonition.summary,.md-typeset .admonition.tldr,.md-typeset details.abstract,.md-typeset details.summary,.md-typeset details.tldr{border-left-color:#00b0ff}[dir=rtl] .md-typeset .admonition.abstract,[dir=rtl] .md-typeset .admonition.summary,[dir=rtl] .md-typeset .admonition.tldr,[dir=rtl] .md-typeset details.abstract,[dir=rtl] .md-typeset details.summary,[dir=rtl] .md-typeset details.tldr{border-right-color:#00b0ff}.md-typeset .admonition.abstract>.admonition-title,.md-typeset .admonition.abstract>summary,.md-typeset .admonition.summary>.admonition-title,.md-typeset .admonition.summary>summary,.md-typeset .admonition.tldr>.admonition-title,.md-typeset .admonition.tldr>summary,.md-typeset details.abstract>.admonition-title,.md-typeset details.abstract>summary,.md-typeset details.summary>.admonition-title,.md-typeset details.summary>summary,.md-typeset details.tldr>.admonition-title,.md-typeset details.tldr>summary{border-bottom-color:.1rem solid rgba(0,176,255,.1);background-color:rgba(0,176,255,.1)}.md-typeset .admonition.abstract>.admonition-title:before,.md-typeset .admonition.abstract>summary:before,.md-typeset .admonition.summary>.admonition-title:before,.md-typeset .admonition.summary>summary:before,.md-typeset .admonition.tldr>.admonition-title:before,.md-typeset .admonition.tldr>summary:before,.md-typeset details.abstract>.admonition-title:before,.md-typeset details.abstract>summary:before,.md-typeset details.summary>.admonition-title:before,.md-typeset details.summary>summary:before,.md-typeset details.tldr>.admonition-title:before,.md-typeset details.tldr>summary:before{color:#00b0ff;content:"\E8D2"}.md-typeset .admonition.info,.md-typeset .admonition.todo,.md-typeset details.info,.md-typeset details.todo{border-left-color:#00b8d4}[dir=rtl] .md-typeset .admonition.info,[dir=rtl] .md-typeset .admonition.todo,[dir=rtl] .md-typeset details.info,[dir=rtl] .md-typeset details.todo{border-right-color:#00b8d4}.md-typeset .admonition.info>.admonition-title,.md-typeset .admonition.info>summary,.md-typeset .admonition.todo>.admonition-title,.md-typeset .admonition.todo>summary,.md-typeset details.info>.admonition-title,.md-typeset details.info>summary,.md-typeset details.todo>.admonition-title,.md-typeset details.todo>summary{border-bottom-color:.1rem solid rgba(0,184,212,.1);background-color:rgba(0,184,212,.1)}.md-typeset .admonition.info>.admonition-title:before,.md-typeset .admonition.info>summary:before,.md-typeset .admonition.todo>.admonition-title:before,.md-typeset .admonition.todo>summary:before,.md-typeset details.info>.admonition-title:before,.md-typeset details.info>summary:before,.md-typeset details.todo>.admonition-title:before,.md-typeset details.todo>summary:before{color:#00b8d4;content:"\E88E"}.md-typeset .admonition.hint,.md-typeset .admonition.important,.md-typeset .admonition.tip,.md-typeset details.hint,.md-typeset details.important,.md-typeset details.tip{border-left-color:#00bfa5}[dir=rtl] .md-typeset .admonition.hint,[dir=rtl] .md-typeset .admonition.important,[dir=rtl] .md-typeset .admonition.tip,[dir=rtl] .md-typeset details.hint,[dir=rtl] .md-typeset details.important,[dir=rtl] .md-typeset details.tip{border-right-color:#00bfa5}.md-typeset .admonition.hint>.admonition-title,.md-typeset .admonition.hint>summary,.md-typeset .admonition.important>.admonition-title,.md-typeset .admonition.important>summary,.md-typeset .admonition.tip>.admonition-title,.md-typeset .admonition.tip>summary,.md-typeset details.hint>.admonition-title,.md-typeset details.hint>summary,.md-typeset details.important>.admonition-title,.md-typeset details.important>summary,.md-typeset details.tip>.admonition-title,.md-typeset details.tip>summary{border-bottom-color:.1rem solid rgba(0,191,165,.1);background-color:rgba(0,191,165,.1)}.md-typeset .admonition.hint>.admonition-title:before,.md-typeset .admonition.hint>summary:before,.md-typeset .admonition.important>.admonition-title:before,.md-typeset .admonition.important>summary:before,.md-typeset .admonition.tip>.admonition-title:before,.md-typeset .admonition.tip>summary:before,.md-typeset details.hint>.admonition-title:before,.md-typeset details.hint>summary:before,.md-typeset details.important>.admonition-title:before,.md-typeset details.important>summary:before,.md-typeset details.tip>.admonition-title:before,.md-typeset details.tip>summary:before{color:#00bfa5;content:"\E80E"}.md-typeset .admonition.check,.md-typeset .admonition.done,.md-typeset .admonition.success,.md-typeset details.check,.md-typeset details.done,.md-typeset details.success{border-left-color:#00c853}[dir=rtl] .md-typeset .admonition.check,[dir=rtl] .md-typeset .admonition.done,[dir=rtl] .md-typeset .admonition.success,[dir=rtl] .md-typeset details.check,[dir=rtl] .md-typeset details.done,[dir=rtl] .md-typeset details.success{border-right-color:#00c853}.md-typeset .admonition.check>.admonition-title,.md-typeset .admonition.check>summary,.md-typeset .admonition.done>.admonition-title,.md-typeset .admonition.done>summary,.md-typeset .admonition.success>.admonition-title,.md-typeset .admonition.success>summary,.md-typeset details.check>.admonition-title,.md-typeset details.check>summary,.md-typeset details.done>.admonition-title,.md-typeset details.done>summary,.md-typeset details.success>.admonition-title,.md-typeset details.success>summary{border-bottom-color:.1rem solid rgba(0,200,83,.1);background-color:rgba(0,200,83,.1)}.md-typeset .admonition.check>.admonition-title:before,.md-typeset .admonition.check>summary:before,.md-typeset .admonition.done>.admonition-title:before,.md-typeset .admonition.done>summary:before,.md-typeset .admonition.success>.admonition-title:before,.md-typeset .admonition.success>summary:before,.md-typeset details.check>.admonition-title:before,.md-typeset details.check>summary:before,.md-typeset details.done>.admonition-title:before,.md-typeset details.done>summary:before,.md-typeset details.success>.admonition-title:before,.md-typeset details.success>summary:before{color:#00c853;content:"\E876"}.md-typeset .admonition.faq,.md-typeset .admonition.help,.md-typeset .admonition.question,.md-typeset details.faq,.md-typeset details.help,.md-typeset details.question{border-left-color:#64dd17}[dir=rtl] .md-typeset .admonition.faq,[dir=rtl] .md-typeset .admonition.help,[dir=rtl] .md-typeset .admonition.question,[dir=rtl] .md-typeset details.faq,[dir=rtl] .md-typeset details.help,[dir=rtl] .md-typeset details.question{border-right-color:#64dd17}.md-typeset .admonition.faq>.admonition-title,.md-typeset .admonition.faq>summary,.md-typeset .admonition.help>.admonition-title,.md-typeset .admonition.help>summary,.md-typeset .admonition.question>.admonition-title,.md-typeset .admonition.question>summary,.md-typeset details.faq>.admonition-title,.md-typeset details.faq>summary,.md-typeset details.help>.admonition-title,.md-typeset details.help>summary,.md-typeset details.question>.admonition-title,.md-typeset details.question>summary{border-bottom-color:.1rem solid rgba(100,221,23,.1);background-color:rgba(100,221,23,.1)}.md-typeset .admonition.faq>.admonition-title:before,.md-typeset .admonition.faq>summary:before,.md-typeset .admonition.help>.admonition-title:before,.md-typeset .admonition.help>summary:before,.md-typeset .admonition.question>.admonition-title:before,.md-typeset .admonition.question>summary:before,.md-typeset details.faq>.admonition-title:before,.md-typeset details.faq>summary:before,.md-typeset details.help>.admonition-title:before,.md-typeset details.help>summary:before,.md-typeset details.question>.admonition-title:before,.md-typeset details.question>summary:before{color:#64dd17;content:"\E887"}.md-typeset .admonition.attention,.md-typeset .admonition.caution,.md-typeset .admonition.warning,.md-typeset details.attention,.md-typeset details.caution,.md-typeset details.warning{border-left-color:#ff9100}[dir=rtl] .md-typeset .admonition.attention,[dir=rtl] .md-typeset .admonition.caution,[dir=rtl] .md-typeset .admonition.warning,[dir=rtl] .md-typeset details.attention,[dir=rtl] .md-typeset details.caution,[dir=rtl] .md-typeset details.warning{border-right-color:#ff9100}.md-typeset .admonition.attention>.admonition-title,.md-typeset .admonition.attention>summary,.md-typeset .admonition.caution>.admonition-title,.md-typeset .admonition.caution>summary,.md-typeset .admonition.warning>.admonition-title,.md-typeset .admonition.warning>summary,.md-typeset details.attention>.admonition-title,.md-typeset details.attention>summary,.md-typeset details.caution>.admonition-title,.md-typeset details.caution>summary,.md-typeset details.warning>.admonition-title,.md-typeset details.warning>summary{border-bottom-color:.1rem solid rgba(255,145,0,.1);background-color:rgba(255,145,0,.1)}.md-typeset .admonition.attention>.admonition-title:before,.md-typeset .admonition.attention>summary:before,.md-typeset .admonition.caution>.admonition-title:before,.md-typeset .admonition.caution>summary:before,.md-typeset .admonition.warning>.admonition-title:before,.md-typeset .admonition.warning>summary:before,.md-typeset details.attention>.admonition-title:before,.md-typeset details.attention>summary:before,.md-typeset details.caution>.admonition-title:before,.md-typeset details.caution>summary:before,.md-typeset details.warning>.admonition-title:before,.md-typeset details.warning>summary:before{color:#ff9100;content:"\E002"}.md-typeset .admonition.fail,.md-typeset .admonition.failure,.md-typeset .admonition.missing,.md-typeset details.fail,.md-typeset details.failure,.md-typeset details.missing{border-left-color:#ff5252}[dir=rtl] .md-typeset .admonition.fail,[dir=rtl] .md-typeset .admonition.failure,[dir=rtl] .md-typeset .admonition.missing,[dir=rtl] .md-typeset details.fail,[dir=rtl] .md-typeset details.failure,[dir=rtl] .md-typeset details.missing{border-right-color:#ff5252}.md-typeset .admonition.fail>.admonition-title,.md-typeset .admonition.fail>summary,.md-typeset .admonition.failure>.admonition-title,.md-typeset .admonition.failure>summary,.md-typeset .admonition.missing>.admonition-title,.md-typeset .admonition.missing>summary,.md-typeset details.fail>.admonition-title,.md-typeset details.fail>summary,.md-typeset details.failure>.admonition-title,.md-typeset details.failure>summary,.md-typeset details.missing>.admonition-title,.md-typeset details.missing>summary{border-bottom-color:.1rem solid rgba(255,82,82,.1);background-color:rgba(255,82,82,.1)}.md-typeset .admonition.fail>.admonition-title:before,.md-typeset .admonition.fail>summary:before,.md-typeset .admonition.failure>.admonition-title:before,.md-typeset .admonition.failure>summary:before,.md-typeset .admonition.missing>.admonition-title:before,.md-typeset .admonition.missing>summary:before,.md-typeset details.fail>.admonition-title:before,.md-typeset details.fail>summary:before,.md-typeset details.failure>.admonition-title:before,.md-typeset details.failure>summary:before,.md-typeset details.missing>.admonition-title:before,.md-typeset details.missing>summary:before{color:#ff5252;content:"\E14C"}.md-typeset .admonition.danger,.md-typeset .admonition.error,.md-typeset details.danger,.md-typeset details.error{border-left-color:#ff1744}[dir=rtl] .md-typeset .admonition.danger,[dir=rtl] .md-typeset .admonition.error,[dir=rtl] .md-typeset details.danger,[dir=rtl] .md-typeset details.error{border-right-color:#ff1744}.md-typeset .admonition.danger>.admonition-title,.md-typeset .admonition.danger>summary,.md-typeset .admonition.error>.admonition-title,.md-typeset .admonition.error>summary,.md-typeset details.danger>.admonition-title,.md-typeset details.danger>summary,.md-typeset details.error>.admonition-title,.md-typeset details.error>summary{border-bottom-color:.1rem solid rgba(255,23,68,.1);background-color:rgba(255,23,68,.1)}.md-typeset .admonition.danger>.admonition-title:before,.md-typeset .admonition.danger>summary:before,.md-typeset .admonition.error>.admonition-title:before,.md-typeset .admonition.error>summary:before,.md-typeset details.danger>.admonition-title:before,.md-typeset details.danger>summary:before,.md-typeset details.error>.admonition-title:before,.md-typeset details.error>summary:before{color:#ff1744;content:"\E3E7"}.md-typeset .admonition.bug,.md-typeset details.bug{border-left-color:#f50057}[dir=rtl] .md-typeset .admonition.bug,[dir=rtl] .md-typeset details.bug{border-right-color:#f50057}.md-typeset .admonition.bug>.admonition-title,.md-typeset .admonition.bug>summary,.md-typeset details.bug>.admonition-title,.md-typeset details.bug>summary{border-bottom-color:.1rem solid rgba(245,0,87,.1);background-color:rgba(245,0,87,.1)}.md-typeset .admonition.bug>.admonition-title:before,.md-typeset .admonition.bug>summary:before,.md-typeset details.bug>.admonition-title:before,.md-typeset details.bug>summary:before{color:#f50057;content:"\E868"}.md-typeset .admonition.example,.md-typeset details.example{border-left-color:#651fff}[dir=rtl] .md-typeset .admonition.example,[dir=rtl] .md-typeset details.example{border-right-color:#651fff}.md-typeset .admonition.example>.admonition-title,.md-typeset .admonition.example>summary,.md-typeset details.example>.admonition-title,.md-typeset details.example>summary{border-bottom-color:.1rem solid rgba(101,31,255,.1);background-color:rgba(101,31,255,.1)}.md-typeset .admonition.example>.admonition-title:before,.md-typeset .admonition.example>summary:before,.md-typeset details.example>.admonition-title:before,.md-typeset details.example>summary:before{color:#651fff;content:"\E242"}.md-typeset .admonition.cite,.md-typeset .admonition.quote,.md-typeset details.cite,.md-typeset details.quote{border-left-color:#9e9e9e}[dir=rtl] .md-typeset .admonition.cite,[dir=rtl] .md-typeset .admonition.quote,[dir=rtl] .md-typeset details.cite,[dir=rtl] .md-typeset details.quote{border-right-color:#9e9e9e}.md-typeset .admonition.cite>.admonition-title,.md-typeset .admonition.cite>summary,.md-typeset .admonition.quote>.admonition-title,.md-typeset .admonition.quote>summary,.md-typeset details.cite>.admonition-title,.md-typeset details.cite>summary,.md-typeset details.quote>.admonition-title,.md-typeset details.quote>summary{border-bottom-color:.1rem solid hsla(0,0%,62%,.1);background-color:hsla(0,0%,62%,.1)}.md-typeset .admonition.cite>.admonition-title:before,.md-typeset .admonition.cite>summary:before,.md-typeset .admonition.quote>.admonition-title:before,.md-typeset .admonition.quote>summary:before,.md-typeset details.cite>.admonition-title:before,.md-typeset details.cite>summary:before,.md-typeset details.quote>.admonition-title:before,.md-typeset details.quote>summary:before{color:#9e9e9e;content:"\E244"}.codehilite .o,.codehilite .ow,.md-typeset .highlight .o,.md-typeset .highlight .ow{color:inherit}.codehilite .ge,.md-typeset .highlight .ge{color:#000}.codehilite .gr,.md-typeset .highlight .gr{color:#a00}.codehilite .gh,.md-typeset .highlight .gh{color:#999}.codehilite .go,.md-typeset .highlight .go{color:#888}.codehilite .gp,.md-typeset .highlight .gp{color:#555}.codehilite .gs,.md-typeset .highlight .gs{color:inherit}.codehilite .gu,.md-typeset .highlight .gu{color:#aaa}.codehilite .gt,.md-typeset .highlight .gt{color:#a00}.codehilite .gd,.md-typeset .highlight .gd{background-color:#fdd}.codehilite .gi,.md-typeset .highlight .gi{background-color:#dfd}.codehilite .k,.md-typeset .highlight .k{color:#3b78e7}.codehilite .kc,.md-typeset .highlight .kc{color:#a71d5d}.codehilite .kd,.codehilite .kn,.md-typeset .highlight .kd,.md-typeset .highlight .kn{color:#3b78e7}.codehilite .kp,.md-typeset .highlight .kp{color:#a71d5d}.codehilite .kr,.codehilite .kt,.md-typeset .highlight .kr,.md-typeset .highlight .kt{color:#3e61a2}.codehilite .c,.codehilite .cm,.md-typeset .highlight .c,.md-typeset .highlight .cm{color:#999}.codehilite .cp,.md-typeset .highlight .cp{color:#666}.codehilite .c1,.codehilite .ch,.codehilite .cs,.md-typeset .highlight .c1,.md-typeset .highlight .ch,.md-typeset .highlight .cs{color:#999}.codehilite .na,.codehilite .nb,.md-typeset .highlight .na,.md-typeset .highlight .nb{color:#c2185b}.codehilite .bp,.md-typeset .highlight .bp{color:#3e61a2}.codehilite .nc,.md-typeset .highlight .nc{color:#c2185b}.codehilite .no,.md-typeset .highlight .no{color:#3e61a2}.codehilite .nd,.codehilite .ni,.md-typeset .highlight .nd,.md-typeset .highlight .ni{color:#666}.codehilite .ne,.codehilite .nf,.md-typeset .highlight .ne,.md-typeset .highlight .nf{color:#c2185b}.codehilite .nl,.md-typeset .highlight .nl{color:#3b5179}.codehilite .nn,.md-typeset .highlight .nn{color:#ec407a}.codehilite .nt,.md-typeset .highlight .nt{color:#3b78e7}.codehilite .nv,.codehilite .vc,.codehilite .vg,.codehilite .vi,.md-typeset .highlight .nv,.md-typeset .highlight .vc,.md-typeset .highlight .vg,.md-typeset .highlight .vi{color:#3e61a2}.codehilite .nx,.md-typeset .highlight .nx{color:#ec407a}.codehilite .il,.codehilite .m,.codehilite .mf,.codehilite .mh,.codehilite .mi,.codehilite .mo,.md-typeset .highlight .il,.md-typeset .highlight .m,.md-typeset .highlight .mf,.md-typeset .highlight .mh,.md-typeset .highlight .mi,.md-typeset .highlight .mo{color:#e74c3c}.codehilite .s,.codehilite .sb,.codehilite .sc,.md-typeset .highlight .s,.md-typeset .highlight .sb,.md-typeset .highlight .sc{color:#0d904f}.codehilite .sd,.md-typeset .highlight .sd{color:#999}.codehilite .s2,.md-typeset .highlight .s2{color:#0d904f}.codehilite .se,.codehilite .sh,.codehilite .si,.codehilite .sx,.md-typeset .highlight .se,.md-typeset .highlight .sh,.md-typeset .highlight .si,.md-typeset .highlight .sx{color:#183691}.codehilite .sr,.md-typeset .highlight .sr{color:#009926}.codehilite .s1,.codehilite .ss,.md-typeset .highlight .s1,.md-typeset .highlight .ss{color:#0d904f}.codehilite .err,.md-typeset .highlight .err{color:#a61717}.codehilite .w,.md-typeset .highlight .w{color:transparent}.codehilite .hll,.md-typeset .highlight .hll{display:block;margin:0 -1.2rem;padding:0 1.2rem;background-color:rgba(255,235,59,.5)}.md-typeset .codehilite,.md-typeset .highlight{position:relative;margin:1em 0;padding:0;border-radius:.2rem;background-color:hsla(0,0%,93%,.5);color:#37474f;line-height:1.4;-webkit-overflow-scrolling:touch}.md-typeset .codehilite code,.md-typeset .codehilite pre,.md-typeset .highlight code,.md-typeset .highlight pre{display:block;margin:0;padding:1.05rem 1.2rem;background-color:transparent;overflow:auto;vertical-align:top}.md-typeset .codehilite code::-webkit-scrollbar,.md-typeset .codehilite pre::-webkit-scrollbar,.md-typeset .highlight code::-webkit-scrollbar,.md-typeset .highlight pre::-webkit-scrollbar{width:.4rem;height:.4rem}.md-typeset .codehilite code::-webkit-scrollbar-thumb,.md-typeset .codehilite pre::-webkit-scrollbar-thumb,.md-typeset .highlight code::-webkit-scrollbar-thumb,.md-typeset .highlight pre::-webkit-scrollbar-thumb{background-color:rgba(0,0,0,.26)}.md-typeset .codehilite code::-webkit-scrollbar-thumb:hover,.md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover,.md-typeset .highlight code::-webkit-scrollbar-thumb:hover,.md-typeset .highlight pre::-webkit-scrollbar-thumb:hover{background-color:#536dfe}.md-typeset pre.codehilite,.md-typeset pre.highlight{overflow:visible}.md-typeset pre.codehilite code,.md-typeset pre.highlight code{display:block;padding:1.05rem 1.2rem;overflow:auto}.md-typeset .codehilitetable{display:block;margin:1em 0;border-radius:.2em;font-size:1.6rem;overflow:hidden}.md-typeset .codehilitetable tbody,.md-typeset .codehilitetable td{display:block;padding:0}.md-typeset .codehilitetable tr{display:flex}.md-typeset .codehilitetable .codehilite,.md-typeset .codehilitetable .highlight,.md-typeset .codehilitetable .linenodiv{margin:0;border-radius:0}.md-typeset .codehilitetable .linenodiv{padding:1.05rem 1.2rem}.md-typeset .codehilitetable .linenos{background-color:rgba(0,0,0,.07);color:rgba(0,0,0,.26);-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.md-typeset .codehilitetable .linenos pre{margin:0;padding:0;background-color:transparent;color:inherit;text-align:right}.md-typeset .codehilitetable .code{flex:1;overflow:hidden}.md-typeset>.codehilitetable{box-shadow:none}.md-typeset [id^="fnref:"]{display:inline-block}.md-typeset [id^="fnref:"]:target{margin-top:-7.6rem;padding-top:7.6rem;pointer-events:none}.md-typeset [id^="fn:"]:before{display:none;height:0;content:""}.md-typeset [id^="fn:"]:target:before{display:block;margin-top:-7rem;padding-top:7rem;pointer-events:none}.md-typeset .footnote{color:rgba(0,0,0,.54);font-size:1.28rem}.md-typeset .footnote ol{margin-left:0}.md-typeset .footnote li{transition:color .25s}.md-typeset .footnote li:target{color:rgba(0,0,0,.87)}.md-typeset .footnote li :first-child{margin-top:0}.md-typeset .footnote li:hover .footnote-backref,.md-typeset .footnote li:target .footnote-backref{-webkit-transform:translateX(0);transform:translateX(0);opacity:1}.md-typeset .footnote li:hover .footnote-backref:hover,.md-typeset .footnote li:target .footnote-backref{color:#536dfe}.md-typeset .footnote-ref{display:inline-block;pointer-events:auto}.md-typeset .footnote-ref:before{display:inline;margin:0 .2em;border-left:.1rem solid rgba(0,0,0,.26);font-size:1.25em;content:"";vertical-align:-.5rem}.md-typeset .footnote-backref{display:inline-block;-webkit-transform:translateX(.5rem);transform:translateX(.5rem);transition:color .25s,opacity .125s .125s,-webkit-transform .25s .125s;transition:transform .25s .125s,color .25s,opacity .125s .125s;transition:transform .25s .125s,color .25s,opacity .125s .125s,-webkit-transform .25s .125s;color:rgba(0,0,0,.26);font-size:0;opacity:0;vertical-align:text-bottom}[dir=rtl] .md-typeset .footnote-backref{-webkit-transform:translateX(-.5rem);transform:translateX(-.5rem)}.md-typeset .footnote-backref:before{display:inline-block;font-size:1.6rem;content:"\E31B"}[dir=rtl] .md-typeset .footnote-backref:before{-webkit-transform:scaleX(-1);transform:scaleX(-1)}.md-typeset .headerlink{display:inline-block;margin-left:1rem;-webkit-transform:translateY(.5rem);transform:translateY(.5rem);transition:color .25s,opacity .125s .25s,-webkit-transform .25s .25s;transition:transform .25s .25s,color .25s,opacity .125s .25s;transition:transform .25s .25s,color .25s,opacity .125s .25s,-webkit-transform .25s .25s;opacity:0}[dir=rtl] .md-typeset .headerlink{margin-right:1rem;margin-left:0}html body .md-typeset .headerlink{color:rgba(0,0,0,.26)}.md-typeset h1[id]:before{display:block;margin-top:-.9rem;padding-top:.9rem;content:""}.md-typeset h1[id]:target:before{margin-top:-6.9rem;padding-top:6.9rem}.md-typeset h1[id] .headerlink:focus,.md-typeset h1[id]:hover .headerlink,.md-typeset h1[id]:target .headerlink{-webkit-transform:translate(0);transform:translate(0);opacity:1}.md-typeset h1[id] .headerlink:focus,.md-typeset h1[id]:hover .headerlink:hover,.md-typeset h1[id]:target .headerlink{color:#536dfe}.md-typeset h2[id]:before{display:block;margin-top:-.8rem;padding-top:.8rem;content:""}.md-typeset h2[id]:target:before{margin-top:-6.8rem;padding-top:6.8rem}.md-typeset h2[id] .headerlink:focus,.md-typeset h2[id]:hover .headerlink,.md-typeset h2[id]:target .headerlink{-webkit-transform:translate(0);transform:translate(0);opacity:1}.md-typeset h2[id] .headerlink:focus,.md-typeset h2[id]:hover .headerlink:hover,.md-typeset h2[id]:target .headerlink{color:#536dfe}.md-typeset h3[id]:before{display:block;margin-top:-.9rem;padding-top:.9rem;content:""}.md-typeset h3[id]:target:before{margin-top:-6.9rem;padding-top:6.9rem}.md-typeset h3[id] .headerlink:focus,.md-typeset h3[id]:hover .headerlink,.md-typeset h3[id]:target .headerlink{-webkit-transform:translate(0);transform:translate(0);opacity:1}.md-typeset h3[id] .headerlink:focus,.md-typeset h3[id]:hover .headerlink:hover,.md-typeset h3[id]:target .headerlink{color:#536dfe}.md-typeset h4[id]:before{display:block;margin-top:-.9rem;padding-top:.9rem;content:""}.md-typeset h4[id]:target:before{margin-top:-6.9rem;padding-top:6.9rem}.md-typeset h4[id] .headerlink:focus,.md-typeset h4[id]:hover .headerlink,.md-typeset h4[id]:target .headerlink{-webkit-transform:translate(0);transform:translate(0);opacity:1}.md-typeset h4[id] .headerlink:focus,.md-typeset h4[id]:hover .headerlink:hover,.md-typeset h4[id]:target .headerlink{color:#536dfe}.md-typeset h5[id]:before{display:block;margin-top:-1.1rem;padding-top:1.1rem;content:""}.md-typeset h5[id]:target:before{margin-top:-7.1rem;padding-top:7.1rem}.md-typeset h5[id] .headerlink:focus,.md-typeset h5[id]:hover .headerlink,.md-typeset h5[id]:target .headerlink{-webkit-transform:translate(0);transform:translate(0);opacity:1}.md-typeset h5[id] .headerlink:focus,.md-typeset h5[id]:hover .headerlink:hover,.md-typeset h5[id]:target .headerlink{color:#536dfe}.md-typeset h6[id]:before{display:block;margin-top:-1.1rem;padding-top:1.1rem;content:""}.md-typeset h6[id]:target:before{margin-top:-7.1rem;padding-top:7.1rem}.md-typeset h6[id] .headerlink:focus,.md-typeset h6[id]:hover .headerlink,.md-typeset h6[id]:target .headerlink{-webkit-transform:translate(0);transform:translate(0);opacity:1}.md-typeset h6[id] .headerlink:focus,.md-typeset h6[id]:hover .headerlink:hover,.md-typeset h6[id]:target .headerlink{color:#536dfe}.md-typeset .MJXc-display{margin:.75em 0;padding:.75em 0;overflow:auto;-webkit-overflow-scrolling:touch}.md-typeset .MathJax_CHTML{outline:0}.md-typeset .critic.comment,.md-typeset del.critic,.md-typeset ins.critic{margin:0 .25em;padding:.0625em 0;border-radius:.2rem;-webkit-box-decoration-break:clone;box-decoration-break:clone}.md-typeset del.critic{background-color:#fdd;box-shadow:.25em 0 0 #fdd,-.25em 0 0 #fdd}.md-typeset ins.critic{background-color:#dfd;box-shadow:.25em 0 0 #dfd,-.25em 0 0 #dfd}.md-typeset .critic.comment{background-color:hsla(0,0%,93%,.5);color:#37474f;box-shadow:.25em 0 0 hsla(0,0%,93%,.5),-.25em 0 0 hsla(0,0%,93%,.5)}.md-typeset .critic.comment:before{padding-right:.125em;color:rgba(0,0,0,.26);content:"\E0B7";vertical-align:-.125em}.md-typeset .critic.block{display:block;margin:1em 0;padding-right:1.6rem;padding-left:1.6rem;box-shadow:none}.md-typeset .critic.block :first-child{margin-top:.5em}.md-typeset .critic.block :last-child{margin-bottom:.5em}.md-typeset details{padding-top:0}.md-typeset details[open]>summary:after{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.md-typeset details:not([open]){padding-bottom:0}.md-typeset details:not([open])>summary{border-bottom:none}.md-typeset details summary{padding-right:4rem}[dir=rtl] .md-typeset details summary{padding-left:4rem}.no-details .md-typeset details:not([open])>*{display:none}.no-details .md-typeset details:not([open]) summary{display:block}.md-typeset summary{display:block;outline:none;cursor:pointer}.md-typeset summary::-webkit-details-marker{display:none}.md-typeset summary:after{position:absolute;top:.8rem;right:1.2rem;color:rgba(0,0,0,.26);font-size:2rem;content:"\E313"}[dir=rtl] .md-typeset summary:after{right:auto;left:1.2rem}.md-typeset .emojione{width:2rem;vertical-align:text-top}.md-typeset code.codehilite,.md-typeset code.highlight{margin:0 .29412em;padding:.07353em 0}.md-typeset .task-list-item{position:relative;list-style-type:none}.md-typeset .task-list-item [type=checkbox]{position:absolute;top:.45em;left:-2em}[dir=rtl] .md-typeset .task-list-item [type=checkbox]{right:-2em;left:auto}.md-typeset .task-list-control .task-list-indicator:before{position:absolute;top:.15em;left:-1.25em;color:rgba(0,0,0,.26);font-size:1.25em;content:"\E835";vertical-align:-.25em}[dir=rtl] .md-typeset .task-list-control .task-list-indicator:before{right:-1.25em;left:auto}.md-typeset .task-list-control [type=checkbox]:checked+.task-list-indicator:before{content:"\E834"}.md-typeset .task-list-control [type=checkbox]{opacity:0;z-index:-1}@media print{.md-typeset a:after{color:rgba(0,0,0,.54);content:" [" attr(href) "]"}.md-typeset code,.md-typeset pre{white-space:pre-wrap}.md-typeset code{box-shadow:none;-webkit-box-decoration-break:initial;box-decoration-break:slice}.md-clipboard,.md-content__icon,.md-footer,.md-header,.md-sidebar,.md-tabs,.md-typeset .headerlink{display:none}}@media only screen and (max-width:44.9375em){.md-typeset pre{margin:1em -1.6rem;border-radius:0}.md-typeset pre>code{padding:1.05rem 1.6rem}.md-footer-nav__link--prev .md-footer-nav__title{display:none}.md-search-result__teaser{max-height:5rem;-webkit-line-clamp:3}.codehilite .hll,.md-typeset .highlight .hll{margin:0 -1.6rem;padding:0 1.6rem}.md-typeset>.codehilite,.md-typeset>.highlight{margin:1em -1.6rem;border-radius:0}.md-typeset>.codehilite code,.md-typeset>.codehilite pre,.md-typeset>.highlight code,.md-typeset>.highlight pre{padding:1.05rem 1.6rem}.md-typeset>.codehilitetable{margin:1em -1.6rem;border-radius:0}.md-typeset>.codehilitetable .codehilite>code,.md-typeset>.codehilitetable .codehilite>pre,.md-typeset>.codehilitetable .highlight>code,.md-typeset>.codehilitetable .highlight>pre,.md-typeset>.codehilitetable .linenodiv{padding:1rem 1.6rem}.md-typeset>p>.MJXc-display{margin:.75em -1.6rem;padding:.25em 1.6rem}}@media only screen and (min-width:100em){html{font-size:68.75%}}@media only screen and (min-width:125em){html{font-size:75%}}@media only screen and (max-width:59.9375em){body[data-md-state=lock]{overflow:hidden}.ios body[data-md-state=lock] .md-container{display:none}html .md-nav__link[for=toc]{display:block;padding-right:4.8rem}html .md-nav__link[for=toc]:after{color:inherit;content:"\E8DE"}html .md-nav__link[for=toc]+.md-nav__link{display:none}html .md-nav__link[for=toc]~.md-nav{display:flex}html [dir=rtl] .md-nav__link{padding-right:1.6rem;padding-left:4.8rem}.md-nav__source{display:block;padding:0 .4rem;background-color:rgba(50,64,144,.9675);color:#fff}.md-search__overlay{position:absolute;top:.4rem;left:.4rem;width:3.6rem;height:3.6rem;-webkit-transform-origin:center;transform-origin:center;transition:opacity .2s .2s,-webkit-transform .3s .1s;transition:transform .3s .1s,opacity .2s .2s;transition:transform .3s .1s,opacity .2s .2s,-webkit-transform .3s .1s;border-radius:2rem;background-color:#fff;overflow:hidden;pointer-events:none}[dir=rtl] .md-search__overlay{right:.4rem;left:auto}[data-md-toggle=search]:checked~.md-header .md-search__overlay{transition:opacity .1s,-webkit-transform .4s;transition:transform .4s,opacity .1s;transition:transform .4s,opacity .1s,-webkit-transform .4s;opacity:1}.md-search__inner{position:fixed;top:0;left:100%;width:100%;height:100%;-webkit-transform:translateX(5%);transform:translateX(5%);transition:right 0s .3s,left 0s .3s,opacity .15s .15s,-webkit-transform .15s cubic-bezier(.4,0,.2,1) .15s;transition:right 0s .3s,left 0s .3s,transform .15s cubic-bezier(.4,0,.2,1) .15s,opacity .15s .15s;transition:right 0s .3s,left 0s .3s,transform .15s cubic-bezier(.4,0,.2,1) .15s,opacity .15s .15s,-webkit-transform .15s cubic-bezier(.4,0,.2,1) .15s;opacity:0;z-index:2}[data-md-toggle=search]:checked~.md-header .md-search__inner{left:0;-webkit-transform:translateX(0);transform:translateX(0);transition:right 0s 0s,left 0s 0s,opacity .15s .15s,-webkit-transform .15s cubic-bezier(.1,.7,.1,1) .15s;transition:right 0s 0s,left 0s 0s,transform .15s cubic-bezier(.1,.7,.1,1) .15s,opacity .15s .15s;transition:right 0s 0s,left 0s 0s,transform .15s cubic-bezier(.1,.7,.1,1) .15s,opacity .15s .15s,-webkit-transform .15s cubic-bezier(.1,.7,.1,1) .15s;opacity:1}[dir=rtl] [data-md-toggle=search]:checked~.md-header .md-search__inner{right:0;left:auto}html [dir=rtl] .md-search__inner{right:100%;left:auto;-webkit-transform:translateX(-5%);transform:translateX(-5%)}.md-search__input{width:100%;height:4.8rem;font-size:1.8rem}.md-search__icon[for=search]{top:1.2rem;left:1.6rem}.md-search__icon[for=search][for=search]:before{content:"\E5C4"}[dir=rtl] .md-search__icon[for=search][for=search]:before{content:"\E5C8"}.md-search__icon[type=reset]{top:1.2rem;right:1.6rem}.md-search__output{top:4.8rem;bottom:0}.md-search-result__article--document:before{display:none}}@media only screen and (max-width:76.1875em){[data-md-toggle=drawer]:checked~.md-overlay{width:100%;height:100%;transition:width 0s,height 0s,opacity .25s;opacity:1}.md-header-nav__button.md-icon--home,.md-header-nav__button.md-logo{display:none}.md-hero__inner{margin-top:4.8rem;margin-bottom:2.4rem}.md-nav{background-color:#fff}.md-nav--primary,.md-nav--primary .md-nav{display:flex;position:absolute;top:0;right:0;left:0;flex-direction:column;height:100%;z-index:1}.md-nav--primary .md-nav__item,.md-nav--primary .md-nav__title{font-size:1.6rem;line-height:1.5}html .md-nav--primary .md-nav__title{position:relative;height:11.2rem;padding:6rem 1.6rem .4rem;background-color:rgba(0,0,0,.07);color:rgba(0,0,0,.54);font-weight:400;line-height:4.8rem;white-space:nowrap;cursor:pointer}html .md-nav--primary .md-nav__title:before{display:block;position:absolute;top:.4rem;left:.4rem;width:4rem;height:4rem;color:rgba(0,0,0,.54)}html .md-nav--primary .md-nav__title~.md-nav__list{background-color:#fff;box-shadow:inset 0 .1rem 0 rgba(0,0,0,.07)}html .md-nav--primary .md-nav__title~.md-nav__list>.md-nav__item:first-child{border-top:0}html .md-nav--primary .md-nav__title--site{position:relative;background-color:#3f51b5;color:#fff}html .md-nav--primary .md-nav__title--site .md-nav__button{display:block;position:absolute;top:.4rem;left:.4rem;width:6.4rem;height:6.4rem;font-size:4.8rem}html .md-nav--primary .md-nav__title--site:before{display:none}html [dir=rtl] .md-nav--primary .md-nav__title--site .md-nav__button,html [dir=rtl] .md-nav--primary .md-nav__title:before{right:.4rem;left:auto}.md-nav--primary .md-nav__list{flex:1;overflow-y:auto}.md-nav--primary .md-nav__item{padding:0;border-top:.1rem solid rgba(0,0,0,.07)}[dir=rtl] .md-nav--primary .md-nav__item{padding:0}.md-nav--primary .md-nav__item--nested>.md-nav__link{padding-right:4.8rem}[dir=rtl] .md-nav--primary .md-nav__item--nested>.md-nav__link{padding-right:1.6rem;padding-left:4.8rem}.md-nav--primary .md-nav__item--nested>.md-nav__link:after{content:"\E315"}[dir=rtl] .md-nav--primary .md-nav__item--nested>.md-nav__link:after{content:"\E314"}.md-nav--primary .md-nav__link{position:relative;margin-top:0;padding:1.2rem 1.6rem}.md-nav--primary .md-nav__link:after{position:absolute;top:50%;right:1.2rem;margin-top:-1.2rem;color:inherit;font-size:2.4rem}[dir=rtl] .md-nav--primary .md-nav__link:after{right:auto;left:1.2rem}.md-nav--primary .md-nav--secondary .md-nav__link{position:static}.md-nav--primary .md-nav--secondary .md-nav{position:static;background-color:transparent}.md-nav--primary .md-nav--secondary .md-nav .md-nav__link{padding-left:2.8rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav__link{padding-right:2.8rem;padding-left:0}.md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav__link{padding-left:4rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav__link{padding-right:4rem;padding-left:0}.md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav__link{padding-left:5.2rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav__link{padding-right:5.2rem;padding-left:0}.md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav .md-nav__link{padding-left:6.4rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav .md-nav__link{padding-right:6.4rem;padding-left:0}.md-nav__toggle~.md-nav{display:flex;-webkit-transform:translateX(100%);transform:translateX(100%);transition:opacity .125s .05s,-webkit-transform .25s cubic-bezier(.8,0,.6,1);transition:transform .25s cubic-bezier(.8,0,.6,1),opacity .125s .05s;transition:transform .25s cubic-bezier(.8,0,.6,1),opacity .125s .05s,-webkit-transform .25s cubic-bezier(.8,0,.6,1);opacity:0}[dir=rtl] .md-nav__toggle~.md-nav{-webkit-transform:translateX(-100%);transform:translateX(-100%)}.no-csstransforms3d .md-nav__toggle~.md-nav{display:none}.md-nav__toggle:checked~.md-nav{-webkit-transform:translateX(0);transform:translateX(0);transition:opacity .125s .125s,-webkit-transform .25s cubic-bezier(.4,0,.2,1);transition:transform .25s cubic-bezier(.4,0,.2,1),opacity .125s .125s;transition:transform .25s cubic-bezier(.4,0,.2,1),opacity .125s .125s,-webkit-transform .25s cubic-bezier(.4,0,.2,1);opacity:1}.no-csstransforms3d .md-nav__toggle:checked~.md-nav{display:flex}.md-sidebar--primary{position:fixed;top:0;left:-24.2rem;width:24.2rem;height:100%;-webkit-transform:translateX(0);transform:translateX(0);transition:box-shadow .25s,-webkit-transform .25s cubic-bezier(.4,0,.2,1);transition:transform .25s cubic-bezier(.4,0,.2,1),box-shadow .25s;transition:transform .25s cubic-bezier(.4,0,.2,1),box-shadow .25s,-webkit-transform .25s cubic-bezier(.4,0,.2,1);background-color:#fff;z-index:3}[dir=rtl] .md-sidebar--primary{right:-24.2rem;left:auto}.no-csstransforms3d .md-sidebar--primary{display:none}[data-md-toggle=drawer]:checked~.md-container .md-sidebar--primary{box-shadow:0 8px 10px 1px rgba(0,0,0,.14),0 3px 14px 2px rgba(0,0,0,.12),0 5px 5px -3px rgba(0,0,0,.4);-webkit-transform:translateX(24.2rem);transform:translateX(24.2rem)}[dir=rtl] [data-md-toggle=drawer]:checked~.md-container .md-sidebar--primary{-webkit-transform:translateX(-24.2rem);transform:translateX(-24.2rem)}.no-csstransforms3d [data-md-toggle=drawer]:checked~.md-container .md-sidebar--primary{display:block}.md-sidebar--primary .md-sidebar__scrollwrap{overflow:hidden;position:absolute;top:0;right:0;bottom:0;left:0;margin:0}.md-tabs{display:none}}@media only screen and (min-width:60em){.md-content{margin-right:24.2rem}[dir=rtl] .md-content{margin-right:0;margin-left:24.2rem}.md-header-nav__button.md-icon--search{display:none}.md-header-nav__source{display:block;width:23rem;max-width:23rem;margin-left:2.8rem;padding-right:1.2rem}[dir=rtl] .md-header-nav__source{margin-right:2.8rem;margin-left:0;padding-right:0;padding-left:1.2rem}.md-search{padding:.4rem}.md-search__overlay{position:fixed;top:0;left:0;width:0;height:0;transition:width 0s .25s,height 0s .25s,opacity .25s;background-color:rgba(0,0,0,.54);cursor:pointer}[dir=rtl] .md-search__overlay{right:0;left:auto}[data-md-toggle=search]:checked~.md-header .md-search__overlay{width:100%;height:100%;transition:width 0s,height 0s,opacity .25s;opacity:1}.md-search__inner{position:relative;width:23rem;padding:.2rem 0;float:right;transition:width .25s cubic-bezier(.1,.7,.1,1)}[dir=rtl] .md-search__inner{float:left}.md-search__form,.md-search__input{border-radius:.2rem}.md-search__input{width:100%;height:3.6rem;padding-left:4.4rem;transition:background-color .25s cubic-bezier(.1,.7,.1,1),color .25s cubic-bezier(.1,.7,.1,1);background-color:rgba(0,0,0,.26);color:inherit;font-size:1.6rem}[dir=rtl] .md-search__input{padding-right:4.4rem}.md-search__input+.md-search__icon{color:inherit}.md-search__input::-webkit-input-placeholder{color:hsla(0,0%,100%,.7)}.md-search__input:-ms-input-placeholder,.md-search__input::-ms-input-placeholder{color:hsla(0,0%,100%,.7)}.md-search__input::placeholder{color:hsla(0,0%,100%,.7)}.md-search__input:hover{background-color:hsla(0,0%,100%,.12)}[data-md-toggle=search]:checked~.md-header .md-search__input{border-radius:.2rem .2rem 0 0;background-color:#fff;color:rgba(0,0,0,.87);text-overflow:none}[data-md-toggle=search]:checked~.md-header .md-search__input+.md-search__icon,[data-md-toggle=search]:checked~.md-header .md-search__input::-webkit-input-placeholder{color:rgba(0,0,0,.54)}[data-md-toggle=search]:checked~.md-header .md-search__input+.md-search__icon,[data-md-toggle=search]:checked~.md-header .md-search__input:-ms-input-placeholder,[data-md-toggle=search]:checked~.md-header .md-search__input::-ms-input-placeholder{color:rgba(0,0,0,.54)}[data-md-toggle=search]:checked~.md-header .md-search__input+.md-search__icon,[data-md-toggle=search]:checked~.md-header .md-search__input::placeholder{color:rgba(0,0,0,.54)}.md-search__output{top:3.8rem;transition:opacity .4s;opacity:0}[data-md-toggle=search]:checked~.md-header .md-search__output{box-shadow:0 6px 10px 0 rgba(0,0,0,.14),0 1px 18px 0 rgba(0,0,0,.12),0 3px 5px -1px rgba(0,0,0,.4);opacity:1}.md-search__scrollwrap{max-height:0}[data-md-toggle=search]:checked~.md-header .md-search__scrollwrap{max-height:75vh}.md-search__scrollwrap::-webkit-scrollbar{width:.4rem;height:.4rem}.md-search__scrollwrap::-webkit-scrollbar-thumb{background-color:rgba(0,0,0,.26)}.md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#536dfe}.md-search-result__meta{padding-left:4.4rem}[dir=rtl] .md-search-result__meta{padding-right:4.4rem;padding-left:0}.md-search-result__article{padding-left:4.4rem}[dir=rtl] .md-search-result__article{padding-right:4.4rem;padding-left:1.6rem}.md-sidebar--secondary{display:block;margin-left:100%;-webkit-transform:translate(-100%);transform:translate(-100%)}[dir=rtl] .md-sidebar--secondary{margin-right:100%;margin-left:0;-webkit-transform:translate(100%);transform:translate(100%)}}@media only screen and (min-width:76.25em){.md-content{margin-left:24.2rem}[dir=rtl] .md-content{margin-right:24.2rem}.md-content__inner{margin-right:2.4rem;margin-left:2.4rem}.md-header-nav__button.md-icon--menu{display:none}.md-nav[data-md-state=animate]{transition:max-height .25s cubic-bezier(.86,0,.07,1)}.md-nav__toggle~.md-nav{max-height:0;overflow:hidden}.no-js .md-nav__toggle~.md-nav{display:none}.md-nav[data-md-state=expand],.md-nav__toggle:checked~.md-nav{max-height:100%}.no-js .md-nav[data-md-state=expand],.no-js .md-nav__toggle:checked~.md-nav{display:block}.md-nav__item--nested>.md-nav>.md-nav__title{display:none}.md-nav__item--nested>.md-nav__link:after{display:inline-block;-webkit-transform-origin:.45em .45em;transform-origin:.45em .45em;-webkit-transform-style:preserve-3d;transform-style:preserve-3d;vertical-align:-.125em}.js .md-nav__item--nested>.md-nav__link:after{transition:-webkit-transform .4s;transition:transform .4s;transition:transform .4s,-webkit-transform .4s}.md-nav__item--nested .md-nav__toggle:checked~.md-nav__link:after{-webkit-transform:rotateX(180deg);transform:rotateX(180deg)}.md-search__scrollwrap,[data-md-toggle=search]:checked~.md-header .md-search__inner{width:68.8rem}.md-sidebar--secondary{margin-left:122rem}[dir=rtl] .md-sidebar--secondary{margin-right:122rem;margin-left:0}.md-tabs~.md-main .md-nav--primary>.md-nav__list>.md-nav__item--nested{font-size:0;visibility:hidden}.md-tabs--active~.md-main .md-nav--primary .md-nav__title{display:block;padding:0}.md-tabs--active~.md-main .md-nav--primary .md-nav__title--site{display:none}.no-js .md-tabs--active~.md-main .md-nav--primary .md-nav{display:block}.md-tabs--active~.md-main .md-nav--primary>.md-nav__list>.md-nav__item{font-size:0;visibility:hidden}.md-tabs--active~.md-main .md-nav--primary>.md-nav__list>.md-nav__item--nested{display:none;font-size:1.4rem;overflow:auto;visibility:visible}.md-tabs--active~.md-main .md-nav--primary>.md-nav__list>.md-nav__item--nested>.md-nav__link{display:none}.md-tabs--active~.md-main .md-nav--primary>.md-nav__list>.md-nav__item--active{display:block}.md-tabs--active~.md-main .md-nav[data-md-level="1"]{max-height:none;overflow:visible}.md-tabs--active~.md-main .md-nav[data-md-level="1"]>.md-nav__list>.md-nav__item{padding-left:0}.md-tabs--active~.md-main .md-nav[data-md-level="1"] .md-nav .md-nav__title{display:none}}@media only screen and (min-width:45em){.md-footer-nav__link{width:50%}.md-footer-copyright{max-width:75%;float:left}[dir=rtl] .md-footer-copyright{float:right}.md-footer-social{padding:1.2rem 0;float:right}[dir=rtl] .md-footer-social{float:left}}@media only screen and (max-width:29.9375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{-webkit-transform:scale(45);transform:scale(45)}}@media only screen and (min-width:30em) and (max-width:44.9375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{-webkit-transform:scale(60);transform:scale(60)}}@media only screen and (min-width:45em) and (max-width:59.9375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{-webkit-transform:scale(75);transform:scale(75)}}@media only screen and (min-width:60em) and (max-width:76.1875em){.md-search__scrollwrap,[data-md-toggle=search]:checked~.md-header .md-search__inner{width:46.8rem}.md-search-result__teaser{max-height:5rem;-webkit-line-clamp:3}} +/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiIsImZpbGUiOiJhc3NldHMvc3R5bGVzaGVldHMvYXBwbGljYXRpb24uNzhhYWIyZGMuY3NzIiwic291cmNlUm9vdCI6IiJ9*/ \ No newline at end of file diff --git a/3.0/site/crypto/base64/index.html b/3.0/site/crypto/base64/index.html new file mode 100644 index 00000000..9a973d8d --- /dev/null +++ b/3.0/site/crypto/base64/index.html @@ -0,0 +1,1416 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Base64 - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

Base64

+

Coming soon

+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/crypto/getting-started/index.html b/3.0/site/crypto/getting-started/index.html new file mode 100644 index 00000000..3f309032 --- /dev/null +++ b/3.0/site/crypto/getting-started/index.html @@ -0,0 +1,1500 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Getting Started - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

Using Crypto

+

Crypto is a library containing all common APIs related to cryptography and security.

+

With Vapor

+

This package is included with Vapor by default, just add:

+
import Crypto
+
+ + +

Without Vapor

+

To include it in your package, add the following to your Package.swift file.

+
// swift-tools-version:4.0
+import PackageDescription
+
+let package = Package(
+    name: "Project",
+    dependencies: [
+        ...
+        .package(url: "https://github.com/vapor/crypto.git", .upToNextMajor(from: "x.0.0")),
+    ],
+    targets: [
+      .target(name: "Project", dependencies: ["Crypto", ... ])
+    ]
+)
+
+ + +

Use import Crypto to access Crypto's APIs.

+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/crypto/hash/index.html b/3.0/site/crypto/hash/index.html new file mode 100644 index 00000000..86ea10be --- /dev/null +++ b/3.0/site/crypto/hash/index.html @@ -0,0 +1,1604 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Hashes - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

Hash

+

Hashes are a one-directional encryption that is commonly used for validating files or one-way securing data such as passwords.

+

Available hashes

+

Crypto currently supports a few hashes.

+
    +
  • MD5
  • +
  • SHA1
  • +
  • SHA2 (all variants)
  • +
+

MD5 and SHA1 are generally used for file validation or legacy (weak) passwords. They're performant and lightweight.

+

Every Hash type has a set of helpers that you can use.

+

Hashing blobs of data

+

Every Hash has a static method called hash that can be used for hashing the entire contents of Foundation.Data, ByteBuffer or String.

+

The result is Data containing the resulting hash. The hash's length is according to spec and defined in the static variable digestSize.

+
// MD5 with `Data`
+let fileData = Data()
+let fileMD5 = MD5.hash(fileData)
+
+// SHA1 with `ByteBuffer`
+let fileBuffer: ByteBuffer = ...
+let fileSHA1 = SHA1.hash(fileBuffer)
+
+// SHA2 variants with String
+let staticUnsafeToken: String = "rsadd14ndmasidfm12i4j"
+
+let tokenHashSHA224 = SHA224.hash(staticUnsafeToken)
+let tokenHashSHA256 = SHA256.hash(staticUnsafeToken)
+let tokenHashSHA384 = SHA384.hash(staticUnsafeToken)
+let tokenHashSHA512 = SHA512.hash(staticUnsafeToken)
+
+ + +

Incremental hashes (manual)

+

To incrementally process hashes you can create an instance of the Hash. This will set up a context.

+

All hash context initializers are empty:

+
// Create an MD5 context
+let md5Context = MD5()
+
+ + +

To process a single chunk of data, you can call the update function on a context using any Sequence of UInt8. That means Array, Data and ByteBuffer work alongside any other sequence of bytes.

+
md5Context.update(data)
+
+ + +

The data data need not be a specific length. Any length works.

+

When you need the result, you can call md5Context.finalize(). This will finish calculating the hash by appending the standard 1 bit, padding and message bitlength.

+

You can optionally provide a last set of data to finalize().

+

After calling finalize(), do not update the hash if you want correct results.

+

Fetching the results

+

The context can then be accessed to extract the resulting Hash.

+
let hash: Data = md5Context.hash
+
+ + +

Streaming hashes (Async)

+

Sometimes you need to hash the contents of a Stream, for example, when processing a file transfer. In this case you can use ByteStreamHasher.

+

First, create a new generic ByteStreamHasher<Hash> where Hash is the hash you want to use. In this case, SHA512.

+
let streamHasher = ByteStreamHasher<SHA512>()
+
+ + +

This stream works like any inputStream by consuming the incoming data and passing the buffers to the hash context.

+

For example, draining a TCP socket.

+
let socket: TCP.Socket = ...
+
+socket.drain(into: streamHasher)
+
+ + +

This will incrementally update the hash using the provided TCP socket's data.

+

When the hash has been completely accumulated, you can complete the hash.

+
let hash = streamHasher.complete() // Foundation `Data`
+
+ + +

This will reset the hash's context to the default configuration, ready to start over.

+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/crypto/mac/index.html b/3.0/site/crypto/mac/index.html new file mode 100644 index 00000000..fd2791a5 --- /dev/null +++ b/3.0/site/crypto/mac/index.html @@ -0,0 +1,1467 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Message authentication - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

Message authentication

+

Message authentication is used for verifying message authenticity and validity.

+

Common use cases are JSON Web Tokens.

+

For message authentication, Vapor only supports HMAC.

+

Using HMAC

+

To use HMAC you first need to select the used hashing algorithm for authentication. This works using generics.

+
let hash = HMAC<SHA224>.authenticate(message, withKey: authenticationKey)
+
+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/crypto/passwords/index.html b/3.0/site/crypto/passwords/index.html new file mode 100644 index 00000000..e2ec8609 --- /dev/null +++ b/3.0/site/crypto/passwords/index.html @@ -0,0 +1,1607 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Password hashing - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

Password hashing

+

Password management is critical for good user security and doesn't need to cost a lot of effort. No software is perfect. Even if your software is perfect, other software on the same server likely isn't. Good password encryption security prevents users' passwords from leaking out in case of a hypothetical future data breach.

+

For password hashing Vapor supports PBKDF2 and BCrypt.

+

We recommend using BCrypt over PBKDF2 for almost all scenarios. Whilst PBKDF2 is a proven standard, it's much more easily brute-forced than BCrypt and is less future-proof.

+

BCrypt

+

BCrypt is an algorithm specifically designed for password hashing. It's easy to store and verify.

+

Deriving a key

+

Unlike PBKDF2 you don't need to generate and store a salt, that's part of the BCrypt hashing and verification process.

+

The output is a combination of the BCrypt "cost" factor, salt and resulting hash. Meaning that the derived output contains all information necessary for verification, simplifying the database access.

+
let result: Data = try BCrypt.make(message: "MyPassword")
+
+guard try BCrypt.verify(message: "MyPassword", matches: result) else {
+    fatalError("This never triggers, since the verification process will always be successful for the same password and conditions")
+}
+
+ + +

The default cost factor is 12, based on the official recommendations.

+

Storing the derived key as a String

+

BCrypt always outputs valid ASCII/UTF-8 for the resulting hash.

+

This means you can convert the output Data to a String as such:

+
guard let string = String(bytes: result, encoding: .utf8) else {
+    // This must never trigger
+}
+
+ + +

PBKDF2

+

PBKDF2 is an algorithm that is almost always (and in Vapor, exclusively) used with HMAC for message authentication.

+

PBKDF2 can be paired up with any hashing algorithm and is simple to implement. PBKDF2 is used all over the world through the WPA2 standard, securing WiFi connections. But we still recommend PBKDF2 above any normal hashing function.

+

For PBKDF2 you also select the Hash using generics.

+

Deriving a key

+

In the following example:

+
    +
  • password is either a String or Data
  • +
  • The salt is Data
  • +
  • Iterations is defaulted to 10_000 iterations
  • +
  • The keySize is equivalent to 1 hash's length.
  • +
+
// Generate a random salt
+let salt: Data = OSRandom().data(count: 32)
+
+let hash = try PBKDF2<SHA256>.derive(fromPassword: password, salt: salt)
+
+ + +

You can optionally configure PBKDF2 to use a different iteration count and output keysize.

+
// Iterates 20'000 times and outputs 100 bytes
+let hash = try PBKDF2<SHA256>.derive(fromPassword: password, salt: salt, iterating: 20_000, derivedKeyLength: 100)
+
+ + +

Storing the results

+

When you're storing the PBKDF2 results, be sure to also store the Salt. Without the original salt, iteration count and other parameters you cannot reproduce the same hash for validation or authentication.

+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/crypto/random/index.html b/3.0/site/crypto/random/index.html new file mode 100644 index 00000000..b70044ac --- /dev/null +++ b/3.0/site/crypto/random/index.html @@ -0,0 +1,1535 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Random - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

Random

+

Crypto has two primary random number generators.

+

OSRandom generates random numbers by calling the operating system's random number generator.

+

URandom generates random numbers by reading from /dev/urandom.

+

Accessing random numbers

+

First, create an instance of the preferred random number generator:

+
let random = OSRandom()
+
+ + +

or

+
let random = try URandom()
+
+ + +

Reading integers

+

For every Swift integer a random number function exists.

+
let int8: Int8 = try random.makeInt8()
+let uint8: UInt8 = try random.makeUInt8()
+let int16: Int16 = try random.makeInt16()
+let uint16: UInt16 = try random.makeUInt16()
+let int32: Int32 = try random.makeInt32()
+let uint32: UInt32 = try random.makeUInt32()
+let int64: Int64 = try random.makeInt64()
+let uint64: UInt64 = try random.makeUInt64()
+let int: Int = try random.makeInt()
+let uint: UInt = try random.makeUInt()
+
+ + +

Reading random data

+

Random buffers of data are useful when, for example, generating tokens or other unique strings/blobs.

+

To generate a buffer of random data:

+
// generates 20 random bytes
+let data: Data = random.data(count: 20)
+
+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/deploy/getting-started/index.html b/3.0/site/deploy/getting-started/index.html new file mode 100644 index 00000000..e747ce4d --- /dev/null +++ b/3.0/site/deploy/getting-started/index.html @@ -0,0 +1,1416 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Getting Started - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

Getting Started with Deployments

+

Coming soon.

+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/fluent/database/index.html b/3.0/site/fluent/database/index.html new file mode 100644 index 00000000..9e156331 --- /dev/null +++ b/3.0/site/fluent/database/index.html @@ -0,0 +1,1479 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Database - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

Fluent Database

+

Coming soon.

+

Connection

+

Coming soon.

+

Logger

+

Coming soon.

+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/fluent/getting-started/index.html b/3.0/site/fluent/getting-started/index.html new file mode 100644 index 00000000..ecb78f2a --- /dev/null +++ b/3.0/site/fluent/getting-started/index.html @@ -0,0 +1,1499 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Getting Started - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

Getting Started with Fluent

+

Fluent (vapor/fluent) is a type-safe, fast, and easy-to-use ORM framework built for Swift. +It takes advantage of Swift's strong type system to provide an elegant foundation for building database integrations.

+

Database

+

Fluent is just a framework for building ORMs, not an ORM itself. To get started using Fluent, pick one of the databases below.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
databaselibrarydriverguide
PostgreSQLvapor/postgresqlvapor/fluent-postgresqlGetting Started →
MySQLvapor/mysqlvapor/fluent-mysqlGetting Started →
SQLitevapor/sqlitevapor/fluent-sqliteGetting Started →
+

After you get started, come back to the other sections in this guide for a more in-depth look at Fluent's features.

+
+

Tip

+

You can also search GitHub for the tag fluent-database for a full list of official and third-party Fluent databases.

+
+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/fluent/migrations/index.html b/3.0/site/fluent/migrations/index.html new file mode 100644 index 00000000..0f6facd9 --- /dev/null +++ b/3.0/site/fluent/migrations/index.html @@ -0,0 +1,1612 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Migrations - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

Getting Started with Migrations

+

Migrations are a way of making organized, testable, and reliable changes to your database's structure-- +even while it's in production!

+

Migrations are often used for preparing a database schema for your models. However, they can also be used to +make normal queries to your database.

+

In this guide we will cover creating both types of migrations.

+

Model Schema

+

Let's take a look at how we can prepare a schema supporting database to accept the +User model from the previous section.

+

Just like we did with the Model protocol, we will conform our User to Migration.

+
import Fluent
+
+extension User: Migration {
+
+}
+
+ + +

Swift will inform us that User does not yet conform. Let's add the required methods!

+

Prepare

+

The first method to implement is prepare. This method is where you make any of your +desired changes to the database.

+

For our User model, we simply want to create a table that can store one or more users. To do this, +we will use the .create(...) function on the supplied database connection.

+
extension User: Migration {
+    /// See Migration.prepare
+    static func prepare(on connection: MySQLConnection) -> Future<Void> {
+        return connection.create(self) { builder in
+            try builder.field(for: \.id)
+            try builder.field(for: \.name)
+            try builder.field(for: \.age)
+        }
+    }
+}
+
+ + +

We pass self (shorthand for User.self since this is a static method) as the first argument to the .create method. This indicates +to Fluent that we would like to create a schema for the User model.

+

Next, we pass a closure that accepts a SchemaBuilder for our User model. +We can then call .fieldon this builder to describe what fields we'd like our table to have.

+

Since we are passing key paths to our User model (indicated by \.), Fluent can see what type those properties are. +For most common types (String, Int, Double, etc) Fluent will automatically be able to determine the best +database field type to use.

+

You can also choose to manually select which database field type to use for a given field.

+
try builder.field(type: .text, for: \.name)
+
+ + +

Learn more about creating, updating, and deleting schemas in Fluent → Schema Builder.

+

Revert

+

Revert is the opposite of prepare. It's job is to undo anything that was done in prepare. It is used when you boot your +app with the --revert option.

+

To implement revert for our model, we simply use .delete to indicate that we would like to delete the schema created for User.

+
extension User: Migration {
+    /// See Migration.revert
+    static func revert(on connection: MySQLConnection) -> Future<Void> {
+        return connection.delete(self)
+    }
+}
+
+ + +

Example

+

We now have a fully functioning model with migration!

+
extension TestUser: Migration {
+    /// See Migration.prepare
+    static func prepare(on connection: SQLiteConnection) -> Future<Void> {
+        return connection.create(self) { builder in
+            try builder.field(for: \.id)
+            try builder.field(for: \.name)
+            try builder.field(for: \.age)
+        }
+    }
+
+    /// See Migration.revert
+    static func revert(on connection: SQLiteConnection) -> Future<Void> {
+        return connection.delete(self)
+    }
+}
+
+ + +

Done

+

Now that you have a working Fluent model and migration, you can move onto querying your model.

+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/fluent/models/index.html b/3.0/site/fluent/models/index.html new file mode 100644 index 00000000..35b5feb6 --- /dev/null +++ b/3.0/site/fluent/models/index.html @@ -0,0 +1,1648 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Models - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

Getting Started with Models

+

Models are the heart of Fluent. Unlike ORMs in other languages, Fluent doesn't return untyped +arrays or dictionaries for queries. Instead, you query the database using models. This allows the +Swift compiler to catch many errors that have burdened ORM users for ages.

+

In this guide, we will cover the creation of a basic User model.

+

Class

+

Every Fluent model starts with a Codable class. You can make any Codable class a Fluent model, +even ones that come from a different module. All you have to do is conform to Model.

+
import Foundation
+import Vapor
+
+final class User: Content {
+    var id: UUID?
+    var name: String
+    var age: Int
+}
+
+ + +

Although it's not necessary, adding final to your Swift classes can make them more performant +and also make adding init methods in extensions easier.

+

Conforming to Model

+

Now that we have our User class, let's conform it to Model.

+
import FluentMySQL
+
+extension User: Model {
+
+}
+
+ + +

Once you add this conformance requirement, Swift will tell you that it does not yet conform. +Let's add the necessary items to make User conform to model.

+
+

Tip

+

We recommend adding Model conformance in an extension to help keep your code clean.

+
+

Database

+

The first step to conforming to Model is to let Fluent know which type of database you plan +on using this model with. This allows Fluent to enable database-specific features wherever you +use this model.

+
import FluentMySQL
+
+extension User: Model {
+    ...
+
+    /// See Model.Database
+    typealias Database = MySQLDatabase
+}
+
+ + +

ID

+

Now we can tell Fluent what type of ID this model uses. In this example, our User model +has an ID property of type UUID named id.

+
import FluentMySQL
+import Foundation
+
+extension User: Model {
+    ...
+
+    /// See Model.ID
+    typealias ID = UUID
+
+    /// See Model.idKey
+    static var idKey: IDKey {
+        return \.id
+    }
+}
+
+ + +

You can use any type that conforms to IDType as a Fluent ID. You can also use any property name you'd like for the id.

+
+

Warning

+

Some databases require certain ID keys. For example, MongoDB requires _id.

+
+

Example

+

We now have a fully-conformed Fluent model!

+
import FluentMySQL
+import Foundation
+import Vapor
+
+final class User: Codable {
+    var id: UUID?
+    var name: String
+    var age: Int
+}
+
+extension User: Model {
+    /// See Model.Database
+    typealias Database = MySQLDatabase
+
+    /// See Model.ID
+    typealias ID = UUID
+
+    /// See Model.idKey
+    static var idKey: IDKey {
+        return \.id
+    }
+}
+
+ + +

Done

+

Now that you have a working Fluent model, you can move onto querying your model. +However, if your database uses schemas, you may need to create a migration for your model first.

+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/fluent/pivot/index.html b/3.0/site/fluent/pivot/index.html new file mode 100644 index 00000000..80154e92 --- /dev/null +++ b/3.0/site/fluent/pivot/index.html @@ -0,0 +1,1416 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Pivot - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

Fluent Pivot

+

Coming soon.

+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/fluent/query-builder/index.html b/3.0/site/fluent/query-builder/index.html new file mode 100644 index 00000000..527841f8 --- /dev/null +++ b/3.0/site/fluent/query-builder/index.html @@ -0,0 +1,1663 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Query Builder - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

Fluent Query Builder

+

Coming soon.

+

Filter

+

Coming soon.

+

Compare

+

Coming soon.

+

Group

+

Coming soon.

+

Subset

+

Coming soon.

+

Join

+

Coming soon.

+

Range

+

Coming soon.

+

Sort

+

Coming soon.

+

Execute

+

Coming soon.

+

Aggregate

+

Coming soon.

+

All

+

Coming soon.

+

First

+

Coming soon.

+

Query

+

Coming soon.

+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/fluent/querying/index.html b/3.0/site/fluent/querying/index.html new file mode 100644 index 00000000..cbb3e166 --- /dev/null +++ b/3.0/site/fluent/querying/index.html @@ -0,0 +1,1811 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Querying - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

Querying Models

+

Once you have a model (and optionally a migration) you can start +querying your database to create, read, update, and delete data.

+

Connection

+

The first thing you need to query your database, is a connection to it. Luckily, they are easy to get.

+

You can use either the application or an incoming request to create a database connection. You just need +access to the database identifier.

+

Request

+

The preferred method for getting access to a database connection is via an incoming request.

+
router.get(...) { req in
+    return req.withConnection(to: .foo) { db in
+        // use the db here
+    }
+}
+
+ + +

The first parameter is the database's identifier. The second parameter is a closure +that accepts a connection to that database.

+
+

Tip

+

Although the closure to .withConnection(to: ...) accepts a database connection, we often use just db for short.

+
+

The closure is expected to return a Future<Void>. When this future is completed, the connection will be released +back into Fluent's connection pool. This is usually acheived by simply returning the query as we will soon see.

+

Application

+

You can also create a database connection using the application. This is useful for cases where you must access +the database from outside a request/response event.

+
let res = app.withConnection(to: .foo) { db in
+    // use the db here
+}
+print(res) // Future<T>
+
+ + +

This is usually done in the boot section of your application.

+
+

Warning

+

Do not use database connections created by the application in a route closure (when responding to a request). +Always use the incoming request to create a connection to avoid threading issues.

+
+

Create

+

To create (save) a model to the database, first initialize an instance of your model, then call .save(on: ).

+
router.post(...) { req in
+    return req.withConnection(to: .foo) { db -> Future<User> in
+        let user = User(name: "Vapor", age: 3)
+        return user.save(on: db).transform(to: user) // Future<User>
+    }
+}
+
+ + +

Response

+

.save(on: ) returns a Future<Void> that completes when the user has finished saving. In this example, we then +map that Future<Void> to a Future<User> by calling .map and passing in the recently-saved user.

+

You can also use .map to return a simple success response.

+
router.post(...) { req in
+    return req.withConnection(to: .foo) { db -> Future<HTTPResponse> in
+        let user = User(name: "Vapor", age: 3)
+        return user.save(on: db).map(to: HTTPResponse.self) {
+            return HTTPResponse(status: .created)
+        }
+    }
+}
+
+ + +

Multiple

+

If you have multiple instances to save, do so using an array. Arrays containing only futures behave like futures.

+
router.post(...) { req in
+    return req.withConnection(to: .foo) { db -> Future<HTTPResponse> in
+        let marie = User(name: "Marie Curie", age: 66)
+        let charles = User(name: "Charles Darwin", age: 73)
+        return [
+            marie.save(on: db),
+            charles.save(on: db)
+        ].map(to: HTTPResponse.self) {
+            return HTTPResponse(status: .created)
+        }
+    }
+}
+
+ + +

Read

+

To read models from the database, use .query() on the database connection to create a QueryBuilder.

+

All

+

Fetch all instances of a model from the database using .all().

+
router.get(...) { req in
+    return req.withConnection(to: .foo) { db -> Future<[User]> in
+        return db.query(User.self).all()
+    }
+}
+
+ + +

Filter

+

Use .filter(...) to apply filters to your query.

+
router.get(...) { req in
+    return req.withConnection(to: .foo) { db -> Future<[User]> in
+        return try db.query(User.self).filter(\User.age > 50).all()
+    }
+}
+
+ + +

First

+

You can also use .first() to just get the first result.

+
router.get(...) { req in
+    return req.withConnection(to: .foo) { db -> Future<User> in
+        return try db.query(User.self).filter(\User.name == "Vapor").first().map(to: User.self) { user in
+            guard let user = user else {
+                throw Abort(.notFound, reason: "Could not find user.")
+            }
+
+            return user
+        }
+    }
+}
+
+ + +

Notice we use .map(to:) here to convert the optional user returned by .first() to a non-optional +user, or we throw an error.

+

Update

+
router.put(...) { req in
+    return req.withConnection(to: .foo) { db -> Future<User> in
+        return db.query(User.self).first().map(to: User.self) { user in
+            guard let user = $0 else {
+                throw Abort(.notFound, reason: "Could not find user.")
+            }
+
+            return user
+        }.flatMap(to: User.self) { user in
+            user.age += 1
+            return user.update(on: db).map(to: User.self) { user }
+        }
+    }
+}
+
+ + +

Notice we use .map(to:) here to convert the optional user returned by .first() to a non-optional +user, or we throw an error.

+

Delete

+
router.delete(...) { req in
+    return req.withConnection(to: .foo) { db -> Future<User> in
+        return db.query(User.self).first().map(to: User.self) { user in
+            guard let user = $0 else {
+                throw Abort(.notFound, reason: "Could not find user.")
+            }
+            return user
+        }.flatMap(to: User.self) { user in
+            return user.delete(on: db).transfom(to: user)
+        }
+    }
+}
+
+ + +

Notice we use .map(to:) here to convert the optional user returned by .first() to a non-optional +user, or we throw an error.

+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/fluent/relations/index.html b/3.0/site/fluent/relations/index.html new file mode 100644 index 00000000..10b7660e --- /dev/null +++ b/3.0/site/fluent/relations/index.html @@ -0,0 +1,1479 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Relations - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

Fluent Relations

+

Coming soon.

+

Parent / Child

+

Coming soon.

+

Siblings

+

Coming soon.

+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/fluent/schema-builder/index.html b/3.0/site/fluent/schema-builder/index.html new file mode 100644 index 00000000..ec71ddcc --- /dev/null +++ b/3.0/site/fluent/schema-builder/index.html @@ -0,0 +1,1511 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Schema Builder - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

Fluent Schema Builder

+

Coming soon.

+

Create

+

Coming soon.

+

Update

+

Coming soon.

+

Delete

+

Coming soon.

+

References

+

Coming soon.

+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/fluent/transaction/index.html b/3.0/site/fluent/transaction/index.html new file mode 100644 index 00000000..5800e2d9 --- /dev/null +++ b/3.0/site/fluent/transaction/index.html @@ -0,0 +1,1416 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Transaction - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

Fluent Transactions

+

Coming soon.

+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/getting-started/application/index.html b/3.0/site/getting-started/application/index.html new file mode 100644 index 00000000..e9d9ed6a --- /dev/null +++ b/3.0/site/getting-started/application/index.html @@ -0,0 +1,1504 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Application - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

Application

+

Every Vapor project has an Application. You use the application to create any services +you might need while developing.

+

The best place to access the application is in your project's boot.swift file.

+
import Vapor
+
+public func boot(_ app: Application) throws {
+    // your code here
+}
+
+ + +

You can also access the application from your routes.swift file. It's stored +as a property there.

+
import Vapor
+
+final class Routes: RouteCollection {
+    ...
+}
+
+ + +

Unlike some other web frameworks, Vapor doesn't support statically accessing the application. +If you need to access it from another class or struct, you should pass through a method or initializer.

+
+

Info

+

Avoiding static access to variables helps make Vapor performant by preventing +the need for thread-safe locks or semaphores.

+
+

Services

+

The application's main function is to make services. For example, you might need a BCryptHasher to hash +some passwords before storing them in a database. You can use the application to create one.

+
import BCrypt
+
+let bcryptHasher = try app.make(BCryptHasher.self)
+
+ + +

Or you might use the application to create an HTTP client.

+
let client = try app.make(Client.self)
+let res = client.get("http://vapor.codes")
+
+ + +

Learn more about services in Getting Started → Services.

+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/getting-started/cloud/index.html b/3.0/site/getting-started/cloud/index.html new file mode 100644 index 00000000..4d1ab13f --- /dev/null +++ b/3.0/site/getting-started/cloud/index.html @@ -0,0 +1,1491 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Deployment - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

Deployment

+

Deploying code is the process of making your Vapor project publically available. +It can be one of the most difficult aspects of web development. Fortunately, there +are services to help.

+

Vapor Cloud

+

The best way to deploy your application is through Vapor Cloud. It's a cloud platform built +specifically for the Vapor web framework. This means it's incredibly easy to deploy your +project quickly and be confident that it will be fast and stable.

+

Deploying your project to Vapor Cloud is simple, it's built right into the Vapor Toolbox. +Just run this command from within the root directory of your project.

+
vapor cloud deploy
+
+ + +

For a detailed guide, visit Vapor Cloud → Quick Start.

+

Other Options

+

Vapor can be deployed anywhere that supports Ubuntu (basically everywhere). To learn more about +deploying your code, checkout Deploy → Getting Started

+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/getting-started/content/index.html b/3.0/site/getting-started/content/index.html new file mode 100644 index 00000000..50660c95 --- /dev/null +++ b/3.0/site/getting-started/content/index.html @@ -0,0 +1,1682 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Content - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

Content

+

In Vapor 3, all content types (JSON, protobuf, FormURLEncoded, Multipart, etc) are treated the same. +All you need to parse and serialize content is a Codable class or struct.

+

For this introduction, we will use JSON as an example. But keep in mind the API is the same for any supported content type.

+

Request

+

Let's take a look at how you would parse the following HTTP request.

+
POST /login HTTP/1.1
+Content-Type: application/json
+
+{
+    "email": "user@vapor.codes",
+    "password": "don't look!"
+}
+
+ + +

Decode Request

+

First, create a struct or class that represents the data you expect.

+
import Foundation
+import Vapor
+
+struct LoginRequest: Content {
+    var email: String
+    var password: String
+}
+
+ + +

Then simply conform this struct or class to Content. +Now we are ready to decode that HTTP request.

+
router.post("login") { req -> Response in
+    let loginRequest = try req.content.decode(LoginRequest.self)
+
+    print(loginRequest.email) // user@vapor.codes
+    print(loginRequest.password) // don't look!
+
+    return Response(status: .ok)
+}
+
+ + +

It's that simple!

+

Other Request Types

+

Since the request in the previous example declared JSON as it's content type, +Vapor knows to use a JSON decoder automatically. +This same method would work just as well for the following request.

+
POST /login HTTP/1.1
+Content-Type: application/x-www-form-urlencoded
+
+email=user@vapor.codes&don't+look!
+
+ + +
+

Tip

+

You can configure which encoders/decoders Vapor uses. Read on to learn more.

+
+

Response

+

Let's take a look at how you would create the following HTTP response.

+
HTTP/1.1 200 OK
+Content-Type: application/json
+
+{
+    "name": "Vapor User",
+    "email": "user@vapor.codes"
+}
+
+ + +

Encode Response

+

Just like decoding, first create a struct or class that represents the data your expect.

+
import Foundation
+import Vapor
+
+struct User: Content {
+    var name: String
+    var email: String
+}
+
+ + +

Then just conform this struct or class to Content. +Now we are ready to encode that HTTP response.

+
router.get("user") { req -> Response in
+    let user = User(
+        name: "Vapor User",
+        email: "user@vapor.codes"
+    )
+
+    let res = Response(status: .ok)
+    try res.content.encode(user, as: .json)
+    return res
+}
+
+ + +

Other Response Types

+

Content will automatically encode as JSON by default. You can always override which content type is used +using the as: parameter.

+
try res.content.encode(user, as: .formURLEncoded)
+
+ + +

You can also change the default media type for any class or struct.

+
struct User: Content {
+    /// See Content.defaultMediaType
+    static let defaultMediaType: MediaType = .formURLEncoded
+
+    ...
+}
+
+ + +

Configuring Content

+

Coming soon.

+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/getting-started/controllers/index.html b/3.0/site/getting-started/controllers/index.html new file mode 100644 index 00000000..d7253117 --- /dev/null +++ b/3.0/site/getting-started/controllers/index.html @@ -0,0 +1,1518 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Controllers - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

Controllers

+

Controllers are a great way to organize your code. They are collections of methods that accept +a request and return a response.

+

A good place to put your controllers is in the Controllers folder.

+

Methods

+

Let's take a look at an example controller.

+
import Vapor
+
+final class HelloController {
+    func greet(_ req: Request) throws -> String {
+        return "Hello!"
+    }
+}
+
+ + +

Controller methods should always accept a Request and return something ResponseRepresentable. +This also includes futures whose expectations are ResponseRepresentable (i.e, Future<String>).

+

To use this controller, we can simply initialize it, then pass the method to a router.

+
let helloController = HelloController()
+router.get("greet", use: helloController.greet)
+
+ + +

Use Services

+

You will probably want to access your application's services from within your controllers. +Luckily this is easy to do. First, declare what services your controller needs in its init method. Then store them +as properties on the controller.

+
final class HelloController {
+    let hasher: BCryptHasher
+
+    init(hasher: BCryptHasher) {
+        self.hasher = hasher
+    }
+
+    ...
+}
+
+ + +

Next, use the application to create these services when you initialize your controller.

+
let helloController = try HelloController(
+    hasher: app.make()
+)
+
+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/getting-started/futures/index.html b/3.0/site/getting-started/futures/index.html new file mode 100644 index 00000000..b914d93c --- /dev/null +++ b/3.0/site/getting-started/futures/index.html @@ -0,0 +1,1677 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Futures - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

Futures

+

You may have noticed some APIs in Vapor expect or return a Future<T> type. +If this is your first time hearing about futures, they might seem a little confusing at first. +But don't worry, Vapor makes them easy to use.

+

Promises and Futures are two strongly related types. Every promise has a future. +A promise is a write-only entity that has the ability to complete (or fail) it's Future counterpart.

+

Futures are a read-only entity that can have a successful or error case. Successful cases are called the "Expectation".

+

Futures can be used to register callbacks to, which will always executed in the order of registration. Promises can only be completed once. If a promise is completed more than once the input will be ignored.

+

Basics

+

Creating a promise is when the result is returned in the future at an unknown time. +For the sake of demonstration, however, the promise will be completed at a predefined point in time and execution.

+

Within the .do block you may not throw an error or return a result.

+
let promise = Promise<String>()
+let future = promise.future // Future<String>
+
+future.do { string in
+    print(string)
+}
+
+promise.complete("Hello")
+
+ + +

The above code prints "Hello" in the console.

+

Errors

+

When running the above code, you may have noticed a warning pop up. This is because the .do block only handles successful completions. If we were to replace the completion with the following code the .do block would never get run:

+
struct MyError: Error {}
+
+promise.fail(MyError())
+
+ + +

Instead, a .catch block will be triggered.

+
future.do { string in
+    print(string)
+}.catch { error in
+    print("Error '\(error)' occurred")
+}
+
+ + +

In this scenario the test "Error 'MyError' occurred" will appear.

+

Within the .catch block you may not throw an error or return a result.

+

Basic Transformations

+

Transformations are one of the more critical parts of Vapor 3's future system. They assist in reducing the complexity of futures and keep code isolated and readable. You can use the .map function to transform the future expectation to another future of the same or a different type. You need to explicitly state which type will be returned in the mapping closure.

+

The mapping closure(s) will only be executed if an expectation has been received in the previous step. If at any point a transformation function throws an error, execution stops there and the .catch block will be executed.

+

If the promise that was mapped failed to begin with, the .catch block will also be executed without triggering any mapping closures.

+
let promise = Promise<Int>()
+
+promise.future.do { int in
+    print(int)
+}.map(to: Int.self) { int in
+    return int + 4
+}.map(to: String.self) { int in
+    return int.description
+}.do { string in
+    print(string)
+}.catch { error in
+    print("Error '\(error)' occurred")
+}
+
+promise.complete(3)
+
+ + +

The above code will print the inputted integer. Then map the input to (integer + 4) == 7. +Then the textual representation of the integer is returned as a String which will be printed.

+

This results in the following console output:

+
3
+7
+
+ + +

Recursive futures

+

In the above map function we returned a new result synchronously. In some situations, however, you'll need to dispatch another asynchronous call based on the result of a previous call.

+

First, let's see how this would work out using map by exaggerating synchronous code as if it were an asynchronous call.

+
+

Warning

+

Do not use this implementation, use the next one instead. This is an unnecessarily complicated way of nesting futures.

+
+
let promise = Promise<Int>()
+
+promise.map(to: Future<Int>.self) { int in
+    return Future(int + 4)
+}.map(to: Future<Future<String>>.self) { futureInt in
+    return futureInt.map(to: Future<String.self>) { int in
+        return Future(int.description)
+    }
+}.do { doubleFutureString in // Future<Future<String>>
+    doubleFutureString.do { futureString in // Future<String>
+      futureString.do { string in
+          print(string)
+      }.catch { error in
+          print("Error '\(error)' occurred")
+      }
+    }.catch { error in
+        print("Error '\(error)' occurred")
+    }
+}.catch { error in
+    print("Error '\(error)' occurred")
+}
+
+promise.complete(3)
+
+ + +

To flatten this asynchronous recursion, instead, we recommend using flatMap. +The type supplied in the to: argument is implied to be wrapped in a Future<>.

+
let promise = Promise<Int>()
+
+promise.flatMap(to: Int.self) { int in
+    return Future<Int>(int + 4)
+}.flatMap(to: String.self) { int in
+    return Future(int.description)
+}.do { string in
+    print(string)
+}.catch { error in
+    print("Error '\(error)' occurred")
+}
+
+promise.complete(3)
+
+ + +

Always

+

Sometimes you want to always execute a function as part of the cleanup phase. +You can use the .always block to execute a block of code after the future has been successfully executed (and mapped if applicable) or when an error occurs. Please do consider that always also will be executed in the order in which it has been registered, like all other closures.

+
var i = 0
+
+let promise = Promise<Int>()
+let future = promise.future // Future<Int>
+
+future.do { int in
+    i += int * 3
+}.do { int in
+    i += (int - 1)
+}.always {
+    print(i)
+    i = 0
+}.catch { _ in
+    i = -1
+}
+
+ + +

At the end of the above function, i will always be 0. If the promise is completed with the successful result i, the number "11" will be printed. On error, "-1" will be printed.

+

Signals

+

Signals, or Future<Void> is a Future that can contain either an Error or Void (the Expectation). Future<Void> is often used to indicate the successful or unsuccessful completion of a task.

+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/getting-started/hello-world/index.html b/3.0/site/getting-started/hello-world/index.html new file mode 100644 index 00000000..999d7b0a --- /dev/null +++ b/3.0/site/getting-started/hello-world/index.html @@ -0,0 +1,1546 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Hello, world - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

Hello, world

+

Now that you've installed Vapor, let's create your first Vapor app! +This guide will take you step by step through creating a new project, building, and running it.

+

New Project

+

The first step is to create a new Vapor project on your computer. +For this guide, we will call the project Hello.

+

Open up your terminal, and use Vapor Toolbox's new command.

+
vapor new Hello
+
+ + +
+

Warning

+

Make sure to add --branch=beta while using Vapor 3 pre-release.

+

If you receive an error that looks like this: 'Cloning Template [Failed]' then the template you are using is not yet ready for the beta branch. Try a different template.

+
+

Once that finishes, change into the newly created directory.

+
cd Hello
+
+ + +

Generate Xcode Project

+

Let's now use the Vapor Toolbox's xcode command to generate an Xcode project. +This will allow us to build and run our app from inside of Xcode, just like an iOS app.

+
vapor xcode
+
+ + +

The toolbox will ask you if you'd like to open Xcode automatically, select yes.

+

Build & Run

+

You should now have Xcode open and running. Select the run scheme from the scheme menu, +then click the play button.

+

You should see the terminal pop up at the bottom of the screen.

+
Server starting on localhost:8080
+
+ + +

Visit Localhost

+

Open your web browser, and visit localhost:8080/hello →

+

You should see the following page.

+
Hello, world!
+
+ + +

Congratulations on creating, building, and running your first Vapor app! 🎉

+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/getting-started/routing/index.html b/3.0/site/getting-started/routing/index.html new file mode 100644 index 00000000..f1a1fa85 --- /dev/null +++ b/3.0/site/getting-started/routing/index.html @@ -0,0 +1,1576 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Routing - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

Routing

+

Routing is the process of finding the appropriate response to an incoming request.

+

Making a Router

+

In Vapor the default Router is the EngineRouter. You can implement custom routers by implementing one conformant to the Router protocol.

+
let router = try EngineRouter.default()
+
+ + +

There are two APIs available, one is supplied by the Routing library and a set of helpers is available in Vapor itself.

+

We recommend using the helpers and will continue to describe those here.

+

Registering a route

+

Imagine you want to return a list of users when someone visits GET /users. +Leaving authorization on the side, that would look something like this.

+
router.get("users") { req in
+    return // fetch the users
+}
+
+ + +

In Vapor, routing is usually done using the .get, .put, .post, .patch and .delete shorthands. +You can supply the path as / or comma-separated strings. We recommend comma separated, as it's more readable.

+
router.get("path", "to", "something") { ... }
+
+ + +

Routes

+

The best place to add routes is in the routes.swift file. +You will find a router there that is ready to use.

+
import Vapor
+
+final class Routes: RouteCollection {
+    ...
+
+    func boot(router: Router) throws {
+        router.get("hello") { req in
+            return "Hello, world!"
+        }
+    }
+}
+
+ + +

You must return a Future containing a ResponseEncodable here. +The most common ResponseEncodable types are Content, Response and View.

+

Parameters

+

Sometimes you may want one of the components of your route path to be dynamic. This is often used when +you want to get an item with a supplied identifier, i.e., GET /users/:id

+
router.get("users", Int.parameter) { req -> Future<String> in
+    let id = try req.parameter(Int.self)
+    return // fetch the user with id
+}
+
+ + +

Instead of passing a string, pass the type of parameter you expect. In this case, our User has an Int ID.

+
+

Tip

+

You can define your own custom parameter types as well.

+
+

After registering your routes

+

After registering your routes you must register the Router as a Service

+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/getting-started/services/index.html b/3.0/site/getting-started/services/index.html new file mode 100644 index 00000000..0fc28378 --- /dev/null +++ b/3.0/site/getting-started/services/index.html @@ -0,0 +1,1865 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Services - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + + + + +
+
+ + + + + +

Services

+

Services is a framework for creating things you need in your application in a type-safe fashion with protocol and environment support.

+

The Services framework is designed to be thread unsafe. The framework aims to guarantee that a service exists on the same EventLoop it was created from and will be used on.

+

Container

+

Containers are event loops that can create and cache services.

+

Request is the most common Container type, which can be accessed in every Route.

+

Containers cache instances of a given service (keyed by the requested protocol) on a per-container basis.

+
    +
  1. Any given container has its own cache. No two containers will ever share a service instance, whether singleton or not.
  2. +
  3. A singleton service is chosen and cached only by which interface(s) it supports and the service tag. +There will only ever be one instance of a singleton service per-container, regardless of what requested it.
  4. +
  5. A normal service is chosen and cached by which interface(s) it supports, the service tag, and the requesting client interface. +There will be as many instances of a normal service per-container as there are unique clients requesting it. +(Remembering that clients are also interface types, not instances - that's the for: parameter to .make())
  6. +
+

EphemeralContainer

+

EphemeralContainers are containers that are short-lived. Their cache does not stretch beyond a short lifecycle. The most common EphemeralContainer is an HTTP Request which lives for the duration of the route handler.

+

Environment

+

Environments indicate the type of deployment/situation in which an application is ran. Environments can be used to change database credentials or API tokens per environment automatically.

+

Service

+

Services are a type that can be requested from a Container. They are registered as part of the application setup.

+

Services are registered to a matching type or protocol it can represent, including it's own concrete type.

+

Services are registered to a blueprint before the Application is initialized. Together they make up the blueprint that Containers use to create an individual Service.

+

Registering

+

Services are registered as a concrete (singleton) type or factories. Singleton types should be a struct, but can be a class.

+

To create an empty list of Services you can call the initializer without parameters

+
var services = Services()
+
+ + +

The Vapor framework has a default setup with the most common (and officially supported) Services already registered.

+
var services = Services.default()
+
+ + +

Concrete implementations

+

A common use case for registering a struct is for registering configurations. +Vapor 3 configurations are always a concrete struct type. Registering a concrete type is simple:

+
struct EmptyService {}
+
+services.instance(EmptyService())
+
+ + +

Singletons

+

Singleton services (which declare themselves, or were registered, as such) are cached on a per-container basis, but the singleton cache ignores which Client is requesting the service (whereas the normal cache does not).

+

Singleton classes must be thread-safe to prevent crashes. If you want your class to be a singleton type (across all threads):

+
final class SingletonService {
+  init() {}
+}
+
+services.instance(SingletonService())
+
+ + +

Assuming the above service, you can now make this service from a container. The global container in Vapor is Application which must not be used within routes.

+
let app = try Application(services: services)
+let emptyService = app.make(EmptyService.self)
+
+ + +

Protocol conforming services

+

Often times when registering a service is conforms to one or more protocols for which it can be used. This is one of the more widely used use cases for Services.

+
enum Level {
+  case verbose, error
+}
+
+protocol Logger {
+  func log(_ message: String, level: Level)
+}
+
+struct PrintLogger: Logger {
+  init() {}
+
+  func log(_ message: String, level: Level) {
+    print(message)
+  }
+}
+
+services.instance(Logger.self, PrintLogger())
+
+ + +

The above can be combined with isSingleton: true

+

Registering multiple conformances

+

A single type can conform to multiple protocols, and you might want to register a single service for all those conforming situations.

+
protocol Console {
+  func write(_ message: String, color: AnsiColor)
+}
+
+struct PrintConsole: Console, Logger {
+  func write(_ message: String, color: AnsiColor) {
+    print(message)
+  }
+
+  func log(_ message: String, level: Level) {
+    print(message)
+  }
+
+  init() {}
+}
+
+services.instance(
+  supports: [Logger.self, Console.self],
+  ErrorLogger()
+)
+
+ + +

Registering for a specific requester

+

Sometimes, the implementation should change depending on the user. A database connector might need to run over a VPN tunnel, redis might use an optimized local loopback whilst the default implementation is a normal TCP socket.

+

Other times, you simply want to change the log destination depending on the type that's logging (such as logging HTTP errors differently from database errors).

+

This comes in useful when changing configurations per situation, too.

+
struct VerboseLogger: Logger {
+  init() {}
+
+  func log(_ message: String, level: Level) {
+    print(message)
+  }
+}
+
+struct ErrorLogger: Logger {
+  init() {}
+
+  func log(_ message: String, level: Level) {
+    if level == .error {
+      print(message)
+    }
+  }
+}
+
+// Only log errors
+services.instance(Logger.self, ErrorLogger())
+
+// Except the router, do log not found errors verbosely
+services.instance(Logger.self, PrintLogger(), for: Router.self)
+
+ + +

Factorized services

+

Some services have dependencies. An extremly useful use case is TLS, where the implementation is separated from the protocol. This allows users to create a TLS socket to connect to another host without relying on a specific implementation. Vapor uses this to better integrate with the operating system by changing the default TLS implementation from OpenSSL on Linux to the Transport Security Framework on macOS and iOS.

+

Factorized services get access to the event loop to factorize dependencies.

+
services.register { container -> GithubClient in
+  // Create an HTTP client for our GithubClient
+  let client = try container.make(Client.self, for: GithubClient.self)
+  try client.connect(hostname: "github.com", ssl: true)
+
+  return GithubClient(using: client)
+}
+
+ + +

Please do note that we explicitly stated that the GithubClient requests an (HTTP) Client. We recommend doing this at all times, so that you leave configuration options open.

+

Environments

+

Vapor 3 supports (custom) environments. By default we recommend (and support) the .production, .development and .testing environments.

+

You can create a custom environment type as .custom(<my-environment-name>).

+
let environment = Environment.custom("staging")
+
+ + +

Containers give access to the current environment, so libraries may change behaviour depending on the environment.

+

Changing configurations per environment

+

For easy of development, some parameters may and should change for easy of debugging. +Password hashes can be made intentionally weaker in development scenarios to compensate for debug compilation performance, or API tokens may change to the correct one for your environment.

+
services.register { container -> BCryptConfig in
+  let cost: Int
+
+  switch container.environment {
+  case .production:
+      cost = 12
+  default:
+      cost = 4
+  }
+
+  return BCryptConfig(cost: cost)
+}
+
+ + +

Getting a Service

+

To get a service you need an existing container matching the current EventLoop. +If you're processing a Request, you should almost always use the Request as a Container type.

+
// ErrorLogger
+let errorLogger = myContainerType.make(Logger.self, for: Request.self)
+
+// PrintLogger
+let printLogger = myContainerType.make(Logger.self, for: Router.self)
+
+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/getting-started/spm/index.html b/3.0/site/getting-started/spm/index.html new file mode 100644 index 00000000..7e28c0ee --- /dev/null +++ b/3.0/site/getting-started/spm/index.html @@ -0,0 +1,1610 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SPM - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

Managing your project

+

The Swift Package Manager (SPM for short) is used for building your project's source code and dependencies. +It's a similar idea to Cocoapods, Ruby gems, and NPM. Most of the time the Vapor Toolbox will +interact with SPM on your behalf. However, it's important to understand the basics.

+
+

Tip

+

Learn more about SPM on Swift.org →

+
+

Package Manifest

+

The first place SPM looks in your project is the package manfiest. This should always be located in the root +directory of your project and named Package.swift.

+

Dependencies

+

Dependencies are other SPM packages that your package relies on. All Vapor applications rely on the Vapor package, +but you can add as many other dependencies as you want.

+
// swift-tools-version:4.0
+import PackageDescription
+
+let package = Package(
+    name: "VaporApp",
+    dependencies: [
+        // 💧 A server-side Swift web framework. 
+        .package(url: "https://github.com/vapor/vapor.git", from: "3.0.0"),
+    ],
+    targets: [ ... ]
+)
+
+ + +

In the above example, you can see vapor/vapor → version 3.0 +or later is a dependency of this package. +When you add a dependency to your package, you must next signal which targets depend on +the newly available modules.

+
+

Warning

+

Anytime you modify the package manifest, call vapor update to effect the changes.

+
+

Targets

+

Targets are all of the modules, executables, and tests that your package contains.

+
// swift-tools-version:4.0
+import PackageDescription
+
+let package = Package(
+    name: "VaporApp",
+    dependencies: [ ... ],
+    targets: [
+        .target(name: "App", dependencies: ["Vapor"]),
+        .target(name: "Run", dependencies: ["App"]),
+        .testTarget(name: "AppTests", dependencies: ["App"]),
+    ]
+)
+
+ + +

Most Vapor apps will have three targets, although you can add as many as you like to organize your code. +Each target declares which modules it depends on. You must add module names here in order to import them in your code. +A target can depend on other targets in your project or any modules exposed by packages you've added to +the main dependencies array.

+
+

Tip

+

Executable targets (targets that contain a main.swift file) cannot be imported by other modules. +This is why Vapor has both an App and a Run target. +Any code you include in App can be tested in the AppTests.

+
+

Folder Structure

+

Below is the typical folder structure for an SPM package.

+
.
+├── Sources
+│   ├── App
+│   │   └── (Source code)
+│   └── Run
+│       └── main.swift
+├── Tests
+│   └── AppTests
+└── Package.swift
+
+ + +

Each .target corresponds to a folder in the Sources folder. +Each .testTarget corresponds to a folder in the Tests folder.

+

Troubleshooting

+

If you are experiencing problems with SPM, sometimes cleaning your project can help.

+
vapor clean
+
+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/getting-started/structure/index.html b/3.0/site/getting-started/structure/index.html new file mode 100644 index 00000000..0087d6d0 --- /dev/null +++ b/3.0/site/getting-started/structure/index.html @@ -0,0 +1,1720 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Folder Structure - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

Structure

+

This section explains the structure of a typical Vapor application to help get +you familiar with where things go.

+

Folder Structure

+

Vapor's folder structure builds on top of SPM's folder structure.

+
.
+├── Public
+├── Sources
+│   ├── App
+│   │   ├── Controllers
+│   │   ├── Models
+│   │   ├── boot.swift
+│   │   ├── configure.swift
+│   │   └── routes.swift
+│   └── Run
+│       └── main.swift
+├── Tests
+│   └── AppTests
+└── Package.swift
+
+ + +

Let's take a look at what each of these folders and files does.

+

Public

+

This folder contains any public files that will be served by your app. +This is usually images, style sheets, and browser scripts.

+

Whenever Vapor responds to a request, it will first check if the requested +item is in this folder. If it is, it skips your application logic and returns +the file immediately.

+

For example, a request to localhost:8080/favicon.ico will check to see +if Public/favicon.ico exists. If it does, Vapor will return it.

+

Sources

+

This folder contains all of the Swift source files for your project. +The top level folders (App and Run) reflect your package's modules, +as declared in the package manifest.

+

App

+

This is the most important folder in your application, it's where all of +the application logic goes!

+

Controllers

+

Controllers are great way of grouping together application logic. Most controllers +have many functions that accept a request and return some sort of response.

+
+

Tip

+

Vapor supports, but does not enforce the MVC pattern

+
+

Models

+

The Models folder is a great place to store your Content structs or +Fluent Models.

+

boot.swift

+

This file contains a function that will be called after your application has booted, +but before it has started running. This is a great place do things that should happen +every time your application starts.

+

You have access to the Application here which you can use to create +any services you might need.

+

configure.swift

+

This file contains a function that receives the config, environment, and services for your +application as input. This is a great place to make changes to your config or register +services to your application.

+

routes.swift

+

This file contains the main route collection for your app. This is where you should assign routes +for your controller methods.

+

You'll notice there's one example route in there that returns the "hello, world" response we saw earlier.

+

You can create as many route collections as you want to further organize your code. Just make sure +to register them in this main route collection.

+

Tests

+

Each non-executable module in your Sources folder should have a corresponding ...Tests folder.

+

AppTests

+

This folder contains the unit tests for code in your App module. +Learn more about testing in Testing → Getting Started.

+

Package.swift

+

Finally is SPM's package manifest.

+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/getting-started/toolbox/index.html b/3.0/site/getting-started/toolbox/index.html new file mode 100644 index 00000000..2ac049ff --- /dev/null +++ b/3.0/site/getting-started/toolbox/index.html @@ -0,0 +1,1631 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Toolbox - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

Install Toolbox

+

Vapor's command line interface provides shortcuts and assistance for common tasks.

+

Vapor Toolbox

+

Help prints useful information about available commands and flags.

+
vapor --help
+
+ + +

You can also run the --help option on any Toolbox command.

+
vapor new --help
+
+ + +

The --help flag should be your goto for learning about the toolbox as it is the most up-to-date.

+

New

+

The Toolbox's most important feature is helping you create a new project.

+
vapor new <name>
+
+ + +

Just pass the name of your project as the first argument to the new command.

+
+

Note

+

Project names should be PascalCase →, like HelloWorld or MyProject.

+
+

Templates

+

By default, Vapor will create your new project from the API template. You can choose +a different template by passing the --template flag.

+ + + + + + + + + + + + + + + + + + + + +
NameFlagDescription
API--template=apiJSON API with Fluent database.
Web--template=webHTML website with Leaf templates.
+
+

Info

+

There are lots of unofficial Vapor templates on GitHub under the vapor + template topcs →. +You can use these by passing the full GitHub URL to the --template option.

+
+

Build & Run

+

You can use the toolbox to build and run your Vapor app.

+
vapor build
+vapor run
+
+ + +
+

Tip

+

We recommend building and running through Xcode if you have a Mac. +It's a bit faster and you can set breakpoints! +Just use vapor xcode to generate an Xcode project.

+
+

Updating

+

The toolbox should be updated by the package manager it was installed with.

+

Homebrew

+
brew upgrade vapor
+
+ + +

APT

+
sudo apt-get update
+sudo apt-get install vapor
+
+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/getting-started/xcode/index.html b/3.0/site/getting-started/xcode/index.html new file mode 100644 index 00000000..fb8c8c9f --- /dev/null +++ b/3.0/site/getting-started/xcode/index.html @@ -0,0 +1,1514 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Xcode - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

Xcode

+

If you're on a Mac, you can develop your Vapor project using Xcode. +You can build, run, and stop your server from within Xcode, as well as use breakpoints and instruments to debug your code.

+

Xcode 9 running Vapor

+

Xcode is a great way to develop Vapor apps, but you can use any text editor you like.

+

Generate Project

+

To use Xcode, you just need to generate an Xcode project using Vapor Toolbox.

+
vapor xcode
+
+ + +
+

Tip

+

Don't worry about comitting the generated Xcode Project to git, just generate a new +one whenever you need it.

+
+

Run

+

To build and run your Vapor app, first make sure you have the Run scheme selected from the schemes menu. +Also make sure to select "My Mac" as the device.

+

Run Scheme

+

Once that's selected, just click the play button or press Command + R on your keyboard.

+

Test

+

To run your unit tests, select the scheme ending in -Package and press Command + U.

+
+

Warning

+

There may be a few extraneous schemes in the dropdown menu. Ignore them!

+
+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/images/droplet-color.svg b/3.0/site/images/droplet-color.svg new file mode 100644 index 00000000..11c540fb --- /dev/null +++ b/3.0/site/images/droplet-color.svg @@ -0,0 +1,30 @@ + + + Vapor + The Vapor droplet logo in pink and blue. + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/images/droplet-white.svg b/3.0/site/images/droplet-white.svg new file mode 100644 index 00000000..9f645196 --- /dev/null +++ b/3.0/site/images/droplet-white.svg @@ -0,0 +1,18 @@ + + + Vapor + The Vapor droplet logo in white. + + + + \ No newline at end of file diff --git a/3.0/site/index.html b/3.0/site/index.html new file mode 100644 index 00000000..4293b93b --- /dev/null +++ b/3.0/site/index.html @@ -0,0 +1,1551 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

Vapor Documentation

+

This is the documentation for Vapor, a Web Framework for Swift that works on iOS, macOS, and Ubuntu; and all of the packages that Vapor offers.

+

Vapor is the most used web framework for Swift. It provides a beautifully expressive and easy to use foundation for your next website or API.

+

Getting Started

+

If this is your first time using Vapor, head to the Install → macOS section to install Swift and Vapor.

+

Once you have Vapor installed, check out Getting Started → Hello, world to create your first Vapor app!

+

Like Vapor?

+

Our small team works hard to make Vapor awesome (and free). Support the framework by starring Vapor on GitHub +or donating $1 monthly—it helps us a lot. Thanks!

+

Other Sources

+

Here are some other great places to find information about Vapor.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
namedescriptionlink
Vapor SlackChat with ~5,000 Vapor developers.visit →
API docsAuto-generated documentation from code comments.visit →
Stack OverflowAsk and answer questions with the vapor tag.visit →
Source CodeLearn how Vapor works under the hood.visit →
GitHub IssuesReport bugs or request features on GitHub.visit →
+

Providers

+

Vapor providers are a convenient way to add functionality to your Vapor projects. +For a full list of providers, check out the vapor-provider tag on GitHub.

+

Authors

+

Tanner Nelson, Logan Wright, Joannis Orlandos, and the hundreds of members of Vapor.

+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/install/macos/index.html b/3.0/site/install/macos/index.html new file mode 100644 index 00000000..84ec271b --- /dev/null +++ b/3.0/site/install/macos/index.html @@ -0,0 +1,1586 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + macOS - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

Install on macOS

+

To use Vapor on macOS, you just need to have Xcode 9 or greater installed.

+
+

Tip

+

You need to install Xcode to install Swift, but after that you can use any text editor +you like to develop Vapor apps.

+
+

Install Xcode

+

Install Xcode 9 or greater from the Mac App Store.

+

Xcode 9.1

+
+

Warning

+

After Xcode has been downloaded, you must open it to finish the installation. This may take a while.

+
+

Verify Installation

+

Double check the installation was successful by opening Terminal and running:

+
swift --version
+
+ + +

You should see output similar to:

+
Apple Swift version 4.0.2 (swiftlang-900.0.69.2 clang-900.0.38)
+Target: x86_64-apple-macosx10.9
+
+ + +

Vapor requires Swift 4.

+

Install Vapor

+

Now that you have Swift 4, let's install the Vapor Toolbox.

+

The toolbox includes all of Vapor's dependencies as well as a handy CLI tool for creating new projects.

+
brew install vapor/tap/vapor
+
+ + +
+

Tip

+

If you don't already have Homebrew installed, install it at brew.sh →

+
+

Verify Installation

+

Double check the installation was successful by opening Terminal and running:

+
vapor --help
+
+ + +

You should see a long list of available commands.

+

Done

+

Now that you have installed Vapor, create your first app in Getting Started → Hello, world.

+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/install/ubuntu/index.html b/3.0/site/install/ubuntu/index.html new file mode 100644 index 00000000..901250cb --- /dev/null +++ b/3.0/site/install/ubuntu/index.html @@ -0,0 +1,1717 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Ubuntu - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

Install on Ubuntu

+

Installing Vapor on Ubuntu only takes a couple of minutes.

+

Supported

+

Vapor supports the same versions of Ubuntu that Swift supports.

+ + + + + + + + + + + + + + + + + + + + + +
VersionCodename
16.10Yakkety Yak
16.04Xenial Xerus
14.04Trusty Tahr
+

APT Repo

+

Add Vapor's APT repo to get access to all of Vapor's Ubuntu packages.

+

Quick Script

+

Easily add Vapor's APT repo with this handy script.

+
eval "$(curl -sL https://apt.vapor.sh)"
+
+ + +
+

Tip

+

This command requires curl which can be installed using sudo apt-get install curl

+
+

Dockerfile

+

When configuring Ubuntu from a Dockerfile, adding the APT repo can be done via this command:

+
RUN /bin/bash -c "$(wget -qO- https://apt.vapor.sh)"
+
+ + +

Manual

+

Or add the repo manually.

+
wget -q https://repo.vapor.codes/apt/keyring.gpg -O- | sudo apt-key add -
+echo "deb https://repo.vapor.codes/apt $(lsb_release -sc) main" | sudo tee /etc/apt/sources.list.d/vapor.list
+sudo apt-get update
+
+ + +

Install Vapor

+

Now that you have added Vapor's APT repo, you can install the required dependencies.

+
sudo apt-get install swift vapor
+
+ + +

Verify Installation

+

Double check everything worked with the following commands.

+

Swift

+
swift --version
+
+ + +

You should see output similar to:

+
Apple Swift version 4.0.2 (swiftlang-900.0.69.2 clang-900.0.38)
+Target: x86_64-apple-macosx10.9
+
+ + +

Vapor requires Swift 4.

+

Vapor Toolbox

+
vapor --help
+
+ + +

You should see a long list of available commands.

+

Done

+

Now that you have installed Vapor, create your first app in Getting Started → Hello, world.

+

Swift.org

+

Check out Swift.org's guide to using downloads if you need more detailed instructions for installing Swift 4.

+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/leaf/basics/index.html b/3.0/site/leaf/basics/index.html new file mode 100644 index 00000000..34bc87b1 --- /dev/null +++ b/3.0/site/leaf/basics/index.html @@ -0,0 +1,1876 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Basics - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

Basics

+

Welcome to Leaf. Leaf's goal is to be a simple templating language that can make generating views easier. There are plenty of great templating languages, so use what's best for you – maybe that's Leaf! The goals of Leaf are:

+
    +
  • Small set of strictly enforced rules
  • +
  • Consistency
  • +
  • Parser first mentality
  • +
  • Extensibility
  • +
  • Asynchronous and reactive
  • +
+

Rendering a template

+

Once you have Leaf installed, you should create a directory called “Resources” inside your project folder, and inside that create another directory called “Views”. This Resources/Views directory is the default location for Leaf templates, although you can change it if you want.

+

Firstly, import Leaf to routes.swift

+
import Leaf
+
+ + +

Then, to render a basic Leaf template from a route, add this code:

+
router.get { req -> Future<View> in
+    let leaf = try req.make(LeafRenderer.self)
+    let context = [String: String]()
+    return try leaf.render("home", context)
+}
+
+ + +

That will load home.leaf in the Resources/Views directory and render it. The context dictionary is there to let you provide custom data to render inside the template, but you might find it easier to use codable structs instead because they provide extra type safety. For example:

+
struct HomePage: Codable {
+    var title: String
+    var content: String
+}
+
+ + +

Async

+

Leaf's engine is completely reactive, supporting both streams and futures. One of the only ones of its kind.

+

When working with Future results, simply pass the Future in your template context. +Streams that carry an encodable type need to be encoded before they're usable within Leaf.

+
struct Profile: Codable {
+    var friends: EncodableStream
+    var currentUser: Future<User>
+}
+
+ + +

In the above context, the currentUser variable in Leaf will behave as being a User type. Leaf will not read the user Future if it's not used during rendering.

+

EncodableStream will behave as an array of LeafData, only with lower memory impact and better performance. It is recommended to use EncodableStream for (large) database queries.

+
Your name is #(currentUser.name).
+
+#for(friend in friends) {
+    #(friend.name) is a friend of you.
+}
+
+ + +

Template syntax

+

Structure

+

Leaf tags are made up of four elements:

+
    +
  • Token: # is the token
  • +
  • Name: A string that identifies the tag
  • +
  • Parameter List: () May accept 0 or more arguments
  • +
  • Body (optional): {} Must be separated from the parameter list by a space
  • +
+

There can be many different usages of these four elements depending on the tag's implementation. Let's look at a few examples of how Leaf's built-in tags might be used:

+
    +
  • #()
  • +
  • #(variable)
  • +
  • #embed("template")
  • +
  • #set("title") { Welcome to Vapor }
  • +
  • #count(friends)
  • +
  • #for(friend in friends) { <li>#(friend.name)</li> }
  • +
+

Working with context

+

In our Swift example from earlier, we used an empty [String: String] dictionary for context, which passes no custom data to Leaf. To try rendering content, use this code instead:

+
let context = ["title": "Welcome", "message": "Vapor and Leaf work hand in hand"]
+return try leaf.make("home", context)
+
+ + +

That will expose title and message to our Leaf template, which can then be used inside tags. For example:

+
<h1>#(title)</h1>
+<p>#(message)</p>
+
+ + +

Checking conditions

+

Leaf is able to evaluate a range of conditions using its #if tag. For example, if you provide a variable it will check that variable exists in its context:

+
#if(title) {
+    The title is #(title)
+} else {
+    No title was provided.
+}
+
+ + +

You can also write comparisons, for example:

+
#if(title == "Welcome") {
+    This is a friendly web page.
+} else {
+    No strangers allowed!
+}
+
+ + +

If you want to use another tag as part of your condition, you should omit the # for the inner tag. For example:

+
#if(lowercase(title) == "welcome") {
+    This is a friendly web page.
+} else {
+    No strangers allowed!
+}
+
+ + +

Loops

+

If you provide an array of items, Leaf can loop over them and let you manipulate each item individually using its #for tag. For example, we could update our Swift code to provide a list of names in a team:

+
let context = ["team": ["Malcolm", "Kaylee", "Jayne"]]
+
+ + +

We could then loop over them in Leaf like this:

+
#for(name in team) {
+    <p>#(name) is in the team.</p>
+}
+
+ + +

Leaf provides some extra variables inside a #for loop to give you more information about the loop's progress:

+
    +
  • The loop.isFirst variable is true when the current iteration is the first one.
  • +
  • The loop.isLast variable is true when it's the last iteration.
  • +
  • The loop.index variable will be set to the number of the current iteration, counting from 0.
  • +
+

Embedding templates

+

Leaf’s #embed tag allows you to copy the contents of one template into another. When use this, you should always omit the template file's .leaf extension.

+

Embedding is useful for copying in a standard piece of content, for example a page footer or advert code:

+
#embed("footer")
+
+ + +

This tag is also useful for building one template on top of another. For example, you might have a master.leaf file that includes all the code required to lay out your website – HTML structure, CSS and JavaScript – with some gaps in place that represent where page content varies.

+

Using this approach, you would construct a child template that fills in its unique content, then embeds the parent template that places the content appropriately.

+

For example, you might create a child.leaf template like this:

+
#set("body") {
+<p>Welcome to Vapor!</p>
+}
+
+#embed("master")
+
+ + +

That configures one item of context, body, but doesn’t display it directly. Instead, it embeds master.leaf, which can render body along with any other context variables passed in from Swift. For example, master.leaf might look like this:

+
<html>
+<head><title>#(title)</title></head>
+<body>#get(body)</body>
+</html>
+
+ + +

When given the context ["title": "Hi there!"], child.leaf will render as follows:

+
<html>
+<head><title>Hi there!</title></head>
+<body><p>Welcome to Vapor!</p></body>
+</html>
+
+ + +

Other tags

+

#capitalize

+

The #capitalize tag uppercases the first letter of any string. For example, “taylor” will become “Taylor”.

+
#capitalize(name)
+
+ + +

#contains

+

The #contains tag accepts an array and a value as its two parameters, and returns true if the array in parameter one contains the value in parameter two. For example, given the array team:

+
#if(contains(team, "Jayne")) {
+    You're all set!
+} else {
+    You need someone to do PR.
+}
+
+ + +

#count

+

The #count tag returns the number of items in an array. For example:

+
Your search matched #count(matches) pages.
+
+ + +

#lowercase

+

The #lowercase tag lowercases all letters in a string. For example, “Taylor” will become “taylor”.

+
#lowercase(name)
+
+ + +

#uppercase

+

The #uppercase tag uppercases all letters in a string. For example, “Taylor” will become “TAYLOR”.

+
#uppercase(name)
+
+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/leaf/custom-tags/index.html b/3.0/site/leaf/custom-tags/index.html new file mode 100644 index 00000000..6dcb2eef --- /dev/null +++ b/3.0/site/leaf/custom-tags/index.html @@ -0,0 +1,1465 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Custom tags - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

Custom Tags

+

You can extend Leaf to provide your own tags that add custom functionality. To demonstrate this, let's look at a basic example by recreating #uppercase together. This tag will take one argument, which is the string to uppercase.

+

When working with custom tags, there are four important things to know:

+
    +
  1. You should call requireParameterCount() with the number of parameters you expect to receive. This will throw an error if your tag is used incorrectly.
  2. +
  3. If you do or do not require a body, you should use either requireBody() or requireNoBody(). Again, this will throw an error if your tag is used incorrectly.
  4. +
  5. You can read individual parameters using the parameters array. Each parameter will be of type LeafData, which you can convert to concrete data types using properties such as .string, .dictionary, and so on.
  6. +
  7. You must return a Future<LeafData?> containing what should be rendered. In the example below we wrap the resulting uppercase string in a LeafData string, then send that back wrapped in a future.
  8. +
+

Here’s example code for a CustomUppercase Leaf tag:

+
import Async
+import Leaf
+
+public final class CustomUppercase: Leaf.LeafTag {
+    public init() {}
+    public func render(parsed: ParsedTag, context: LeafContext, renderer: LeafRenderer) throws -> Future<LeafData?> {
+        // ensure we receive precisely one parameter
+        try parsed.requireParameterCount(1)
+
+        // pull out our lone parameter as a string then uppercase it, or use an empty string
+        let string = parsed.parameters[0].string?.uppercased() ?? ""
+
+        // send it back wrapped in a LeafData
+        return Future(.string(string))
+    }
+}
+
+ + +

We can now register this Tag in our configure.swift file with:

+
services.register { container -> LeafConfig in
+    // take a copy of Leaf's default tags
+    var tags = defaultTags
+
+    // add our custom tag
+    tags["customuppercase"] = CustomUppercase()
+
+    // find the location of our Resources/Views directory
+    let directoryConfig = try container.make(DirectoryConfig.self, for: LeafRenderer.self)
+    let viewsDirectory = directoryConfig.workDir + "Resources/Views"
+
+    // put all that into a new Leaf configuration and return it
+    return LeafConfig(tags: tags, viewsDir: viewsDirectory)
+}
+
+ + +

Once that is complete, you can use #customuppercase(some_variable) to run your custom code.

+
+

Note: Use of non-alphanumeric characters in tag names is strongly discouraged and may be disallowed in future versions of Leaf.

+
+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/leaf/getting-started/index.html b/3.0/site/leaf/getting-started/index.html new file mode 100644 index 00000000..309f99e0 --- /dev/null +++ b/3.0/site/leaf/getting-started/index.html @@ -0,0 +1,1623 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Getting Started - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

Leaf

+

Leaf is a templating language that integrates with Futures, Reactive Streams and Codable. This section outlines how to import the Leaf package into a Vapor project.

+

Example Folder Structure

+
Hello
+├── Package.resolved
+├── Package.swift
+├── Public
+├── Resources
+│   ├── Views
+│   │   └── hello.leaf
+├── Public
+│   ├── images (images resources)
+│   ├── styles (css resources)
+├── Sources
+│   ├── App
+│   │   ├── boot.swift
+│   │   ├── configure.swift
+│   │   └── routes.swift
+│   └── Run
+│       └── main.swift
+├── Tests
+│   ├── AppTests
+│   │   └── AppTests.swift
+│   └── LinuxMain.swift
+└── LICENSE
+
+ + +

Adding Leaf to your project

+

The easiest way to use Leaf with Vapor is to include the Leaf repository as a dependency in Package.swift:

+
// swift-tools-version:4.0
+import PackageDescription
+
+let package = Package(
+    name: "project1",
+    dependencies: [
+        // 💧 A server-side Swift web framework.
+        .package(url: "https://github.com/vapor/vapor.git", .branch("beta")),
+        .package(url: "https://github.com/vapor/leaf.git", .branch("beta")),
+    ],
+    targets: [
+        .target(
+            name: "App",
+            dependencies: ["Vapor", "Leaf"]
+        ),
+        .target(name: "Run", dependencies: ["App"]),
+        .testTarget(name: "AppTests", dependencies: ["App"]),
+    ]
+)
+
+ + +

The Leaf package adds Leaf to your project, but to configure it for use you must modify configure.swift:

+
    +
  1. Add import Leaf to the top of the file so that Leaf is available to use. You will also need to add this to any file that will render templates.
  2. +
  3. Add try services.register(LeafProvider()) to the configure() function so that routes may render Leaf templates as needed.
  4. +
+

Syntax Highlighting

+

You may also wish to install one these third-party packages that provide support for syntax highlighting in Leaf templates.

+

Atom

+

language-leaf by ButkiewiczP

+

Xcode

+

It is not currently possible to implement Leaf Syntax Highlighting in Xcode, however, using Xcode's HTML Syntax Coloring can help a bit. Select one or more Leaf files and then choose Editor > Syntax Coloring > HTML. Your selected Leaf files will now use Xcode's HTML Syntax Coloring. Unfortunately the usefulness of this is limited because this association will be removed when vapor xcode is run.

+

There appears to be a way to make Xcode file associations persist but that requires a bit more kung-fu.

+

VS Code

+

html-leaf by FranciscoAmado

+

CLion & AppCode

+

Some preliminary work has been done to implement a Leaf Plugin for CLion & AppCode but lack of skill and interest in Java has slowed progress! If you have IntelliJ SDK experience and want to help with this, message Tom Holland on Vapor Slack

+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/mysql/core/index.html b/3.0/site/mysql/core/index.html new file mode 100644 index 00000000..a730e8d8 --- /dev/null +++ b/3.0/site/mysql/core/index.html @@ -0,0 +1,1707 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + MySQL Core - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

MySQL Core

+

MySQL (vapor/mysql) is a pure-Swift (no libmysql dependency), event-driven, non-blocking driver for MySQL. It's build on top of the Swift NIO networking library.

+
+

Seealso

+

The higher-level, Fluent MySQL ORM guide is located at MySQL → Fluent

+
+

Using just the MySQL package for your project may be a good idea if any of the following are true.

+
    +
  • You have an existing DB with non-standard structure.
  • +
  • You rely heavily on custom or complex SQL queries.
  • +
  • You just plain don't like ORMs.
  • +
+

MySQL core is built on top of DatabaseKit which provides some conveniences like connection pooling and integrations with Vapor's Services architecture.

+
+

Tip

+

Even if you do choose to use Fluent MySQL, all of the features of MySQL core will be available to you.

+
+

Getting Started

+

Let's take a look at how you can get started using MySQL core.

+

Package

+

The first step to using MySQL core is adding it as a dependency to your project in your SPM package manifest file.

+
// swift-tools-version:4.0
+import PackageDescription
+
+let package = Package(
+    name: "MyApp",
+    dependencies: [
+        /// Any other dependencies ...
+
+        // 🐬 Non-blocking, event-driven Swift client for MySQL.
+        .package(url: "https://github.com/vapor/mysql.git", from: "3.0.0-rc"),
+    ],
+    targets: [
+        .target(name: "App", dependencies: ["MySQL", ...]),
+        .target(name: "Run", dependencies: ["App"]),
+        .testTarget(name: "AppTests", dependencies: ["App"]),
+    ]
+)
+
+ + +

Don't forget to add the module as a dependency in the targets array. Once you have added the dependency, regenerate your Xcode project with the following command:

+
vapor xcode
+
+ + +

Config

+

The next step is to configure the database in your configure.swift file.

+
import MySQL
+
+/// ...
+
+/// Register providers first
+try services.register(MySQLProvider())
+
+ + +

Registering the provider will add all of the services required for MySQL to work properly. It also includes a default database config struct that uses typical development environment credentials.

+

You can of course override this config struct if you have non-standard credentials.

+
/// Register custom MySQL Config
+let mysqlConfig = MySQLDatabaseConfig(hostname: "localhost", port: 3306, username: "vapor")
+services.register(mysqlConfig)
+
+ + +

Query

+

Now that the database is configured, you can make your first query.

+
router.get("mysql-version") { req -> Future<String> in
+    return req.withPooledConnection(to: .mysql) { conn in
+        return try conn.query("select @@version as v;").map(to: String.self) { rows in
+            return try rows[0].firstValue(forColumn: "v")?.decode(String.self) ?? "n/a"
+        }
+    }
+}
+
+ + +

Visiting this route should display your MySQL version.

+

Connection

+

A MySQLConnection is normally created using the Request container and can perform two different types of queries.

+

Create

+

There are two methods for creating a MySQLConnection.

+
return req.withPooledConnection(to: .mysql) { conn in
+    /// ...
+}
+return req.withConnection(to: .mysql) { conn in
+    /// ...
+}
+
+ + +

As the names imply, withPooledConnection(to:) utilizes a connection pool. withConnection(to:) does not. Connection pooling is a great way to ensure your application does not exceed the limits of your database, even under peak load.

+

Simply Query

+

Use .simpleQuery(_:) to perform a query on your MySQL database that does not bind any parameters. Some queries you send to MySQL may actually require that you use the simpleQuery(_:) method instead of the parameterized method.

+
+

Note

+

This method sends and receives data as text-encoded, meaning it is not optimal for transmitting things like integers.

+
+
let rows = req.withPooledConnection(to: .mysql) { conn in
+    return conn.simpleQuery("SELECT * FROM users;")
+}
+print(rows) // Future<[[MySQLColumn: MySQLData]]>
+
+ + +

You can also choose to receive each row in a callback, which is great for conserving memory for large queries.

+
let done = req.withPooledConnection(to: .mysql) { conn in
+    return conn.simpleQuery("SELECT * FROM users;") { row in
+        print(row) // [MySQLColumn: MySQLData]
+    }
+}
+print(done) // Future<Void>
+
+ + +

Parameterized Query

+

MySQL also supports sending parameterized queries (sometimes called prepared statements). This method allows you to insert data placeholders into the SQL string and send the values separately.

+

Data sent via parameterized queries is binary encoded, making it more efficient for sending some data types. In general, you should use parameterized queries where ever possible.

+
let users = req.withPooledConnection(to: .mysql) { conn in
+    return try conn.query("SELECT *  users WHERE name = $1;", ["Vapor"])
+}
+print(users) // Future<[[MySQLColumn: MySQLData]]>
+
+ + +

You can also provide a callback, similar to simple queries, for handling each row individually.

+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/mysql/fluent/index.html b/3.0/site/mysql/fluent/index.html new file mode 100644 index 00000000..c842d8d3 --- /dev/null +++ b/3.0/site/mysql/fluent/index.html @@ -0,0 +1,1720 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Fluent MySQL - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

Fluent MySQL

+

Fluent MySQL (vapor/fluent-mysql) is a type-safe, fast, and easy-to-use ORM for MySQL built on top of Fluent.

+
+

Seealso

+

The Fluent MySQL package is built on top of Fluent and the pure Swift, NIO-based MySQL core. You should refer to their guides for more information about subjects not covered here.

+
+

Getting Started

+

This section will show you how to add Fluent MySQL to your project and create your first MySQLModel.

+

Package

+

The first step to using Fluent MySQL is adding it as a dependency to your project in your SPM package manifest file.

+
// swift-tools-version:4.0
+import PackageDescription
+
+let package = Package(
+    name: "MyApp",
+    dependencies: [
+        /// Any other dependencies ...
+
+        // 🖋🐬 Swift ORM (queries, models, relations, etc) built on MySQL.
+        .package(url: "https://github.com/vapor/fluent-mysql.git", from: "3.0.0-rc"),
+    ],
+    targets: [
+        .target(name: "App", dependencies: ["FluentMySQL", ...]),
+        .target(name: "Run", dependencies: ["App"]),
+        .testTarget(name: "AppTests", dependencies: ["App"]),
+    ]
+)
+
+ + +

Don't forget to add the module as a dependency in the targets array. Once you have added the dependency, regenerate your Xcode project with the following command:

+
vapor xcode
+
+ + +

Model

+

Now let's create our first MySQLModel. Models represent tables in your MySQL database and they are the primary method of interacting with your data.

+
/// A simple user.
+final class User: MySQLModel {
+    /// The unique identifier for this user.
+    var id: Int?
+
+    /// The user's full name.
+    var name: String
+
+    /// The user's current age in years.
+    var age: Int
+
+    /// Creates a new user.
+    init(id: Int? = nil, name: String, age: Int) {
+        self.id = id
+        self.name = name
+        self.age = age
+    }
+}
+
+ + +

The example above shows a MySQLModel for a simple model representing a user. You can make both structs and classes a model. You can even conform types that come from external modules. The only requirement is that these types conform to Codable, which must be declared on the base type for synthesized (automatic) conformance.

+

Standard practice with MySQL databases is using an auto-generated INTEGER for creating and storing unique identifiers in the id column. It's also possible to use UUIDs or even Strings for your identifiers. There are convenience protocol for that.

+ + + + + + + + + + + + + + + + + + + + + + + + + +
protocoltypekey
MySQLModelIntid
MySQLUUIDModelUUIDid
MySQLStringModelStringid
+
+

Seealso

+

Take a look at Fluent → Model for more information on creating models with custom ID types and keys.

+
+

Migration

+

All of your models (with some rare exceptions) should have a corresponding table—or schema—in your database. You can use a Fluent → Migration to automatically generate this schema in a testable, maintainable way. Fluent makes it easy to automatically generate a migration for your model

+
+

Tip

+

If you are creating models to represent an existing table or database, you can skip this step.

+
+
/// Allows `User` to be used as a migration.
+extension User: Migration { }
+
+ + +

That's all it takes. Fluent uses Codable to analyze your model and will attempt to create the best possible schema for it.

+

Take a look at Fluent → Migration if you are interested in customizing this migration.

+

Configure

+

The final step is to configure your database. At a minimum, this requires adding two things to your configure.swift file.

+
    +
  • FluentMySQLProvider
  • +
  • MigrationConfig
  • +
+

Let's take a look.

+
import FluentMySQL
+
+/// ...
+
+/// Register providers first
+try services.register(FluentMySQLProvider())
+
+/// Configure migrations
+var migrations = MigrationConfig()
+migrations.add(model: User.self, database: .mysql)
+services.register(migrations)
+
+/// Other services....
+
+ + +

Registering the provider will add all of the services required for Fluent MySQL to work properly. It also includes a default database config struct that uses typical development environment credentials.

+

You can of course override this config struct if you have non-standard credentials.

+
/// Register custom MySQL Config
+let mysqlConfig = MySQLDatabaseConfig(hostname: "localhost", port: 3306, username: "vapor")
+services.register(mysqlConfig)
+
+ + +

Once you have the MigrationConfig added, you should be able to run your application and see the following:

+
Migrating mysql DB
+Migrations complete
+Server starting on http://localhost:8080
+
+ + +

Query

+

Now that you have created a model and a corresponding schema in your database, let's make your first query.

+
router.get("users") { req in
+    return User.query(on: req).all()
+}
+
+ + +

If you run your app, and query that route, you should see an empty array returned. Now you just need to add some users! Congratulations on getting your first Fluent MySQL model and migration working.

+

Connection

+

With Fluent, you always have access to the underlying database driver. Using this underlying driver to perform a query is sometimes called a "raw query".

+

Let's take a look at a raw MySQL query.

+
router.get("mysql-version") { req -> Future<String> in
+    return req.withPooledConnection(to: .mysql) { conn in
+        return try conn.query("select @@version as v;").map(to: String.self) { rows in
+            return try rows[0].firstValue(forColumn: "v")?.decode(String.self) ?? "n/a"
+        }
+    }
+}
+
+ + +

In the above example, withPooledConnection(to:) is used to create a connection to the database identified by .mysql. This is the default database identifier. See Fluent → Database to learn more.

+

Once we have the MySQLConnection, we can perform a query on it. You can learn more about the methods available in MySQL → Core.

+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/mysql/getting-started/index.html b/3.0/site/mysql/getting-started/index.html new file mode 100644 index 00000000..e0d10fdd --- /dev/null +++ b/3.0/site/mysql/getting-started/index.html @@ -0,0 +1,1422 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Getting Started - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

Getting Started with MySQL

+

MySQL is a widely-used, open-source database. It has an extremely popular wire-protocol that other open-source databases like MariaDB also support.

+

You can use MySQL with Vapor (or any server-side Swift framework) by either:

+ +

We recommend using the ORM since it does a lot of the hard work for you. Check out the respective guides to learn more.

+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/postgresql/core/index.html b/3.0/site/postgresql/core/index.html new file mode 100644 index 00000000..de248f31 --- /dev/null +++ b/3.0/site/postgresql/core/index.html @@ -0,0 +1,1707 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + PostgreSQL Core - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

PostgreSQL Core

+

PostgreSQL (vapor/postgresql) is a pure-Swift (no libpq dependency), event-driven, non-blocking driver for PostgreSQL. It's build on top of the Swift NIO networking library.

+
+

Seealso

+

The higher-level, Fluent PostgreSQL ORM guide is located at PostgreSQL → Fluent

+
+

Using just the PostgreSQL package for your project may be a good idea if any of the following are true.

+
    +
  • You have an existing DB with non-standard structure.
  • +
  • You rely heavily on custom or complex SQL queries.
  • +
  • You just plain don't like ORMs.
  • +
+

PostgreSQL core is built on top of DatabaseKit which provides some conveniences like connection pooling and integrations with Vapor's Services architecture.

+
+

Tip

+

Even if you do choose to use Fluent PostgreSQL, all of the features of PostgreSQL core will be available to you.

+
+

Getting Started

+

Let's take a look at how you can get started using PostgreSQL core.

+

Package

+

The first step to using PostgreSQL core is adding it as a dependency to your project in your SPM package manifest file.

+
// swift-tools-version:4.0
+import PackageDescription
+
+let package = Package(
+    name: "MyApp",
+    dependencies: [
+        /// Any other dependencies ...
+
+        // 🐘 Non-blocking, event-driven Swift client for PostgreSQL.
+        .package(url: "https://github.com/vapor/postgresql.git", from: "1.0.0-rc"),
+    ],
+    targets: [
+        .target(name: "App", dependencies: ["PostgreSQL", ...]),
+        .target(name: "Run", dependencies: ["App"]),
+        .testTarget(name: "AppTests", dependencies: ["App"]),
+    ]
+)
+
+ + +

Don't forget to add the module as a dependency in the targets array. Once you have added the dependency, regenerate your Xcode project with the following command:

+
vapor xcode
+
+ + +

Config

+

The next step is to configure the database in your configure.swift file.

+
import PostgreSQL
+
+/// ...
+
+/// Register providers first
+try services.register(PostgreSQLProvider())
+
+ + +

Registering the provider will add all of the services required for PostgreSQL to work properly. It also includes a default database config struct that uses typical development environment credentials.

+

You can of course override this config struct if you have non-standard credentials.

+
/// Register custom PostgreSQL Config
+let psqlConfig = PostgreSQLDatabaseConfig(hostname: "localhost", port: 5432, username: "vapor")
+services.register(psqlConfig)
+
+ + +

Query

+

Now that the database is configured, you can make your first query.

+
router.get("psql-version") { req -> Future<String> in
+    return req.withPooledConnection(to: .psql) { conn in
+        return try conn.query("select version() as v;").map(to: String.self) { rows in
+            return try rows[0].firstValue(forColumn: "v")?.decode(String.self) ?? "n/a"
+        }
+    }
+}
+
+ + +

Visiting this route should display your PostgreSQL version.

+

Connection

+

A PostgreSQLConnection is normally created using the Request container and can perform two different types of queries.

+

Create

+

There are two methods for creating a PostgreSQLConnection.

+
return req.withPooledConnection(to: .psql) { conn in
+    /// ...
+}
+return req.withConnection(to: .psql) { conn in
+    /// ...
+}
+
+ + +

As the names imply, withPooledConnection(to:) utilizes a connection pool. withConnection(to:) does not. Connection pooling is a great way to ensure your application does not exceed the limits of your database, even under peak load.

+

Simply Query

+

Use .simpleQuery(_:) to perform a query on your PostgreSQL database that does not bind any parameters. Some queries you send to PostgreSQL may actually require that you use the simpleQuery(_:) method instead of the parameterized method.

+
+

Note

+

This method sends and receives data as text-encoded, meaning it is not optimal for transmitting things like integers.

+
+
let rows = req.withPooledConnection(to: .psql) { conn in
+    return conn.simpleQuery("SELECT * FROM users;")
+}
+print(rows) // Future<[[PostgreSQLColumn: PostgreSQLData]]>
+
+ + +

You can also choose to receive each row in a callback, which is great for conserving memory for large queries.

+
let done = req.withPooledConnection(to: .psql) { conn in
+    return conn.simpleQuery("SELECT * FROM users;") { row in
+        print(row) // [PostgreSQLColumn: PostgreSQLData]
+    }
+}
+print(done) // Future<Void>
+
+ + +

Parameterized Query

+

PostgreSQL also supports sending parameterized queries (sometimes called prepared statements). This method allows you to insert data placeholders into the SQL string and send the values separately.

+

Data sent via parameterized queries is binary encoded, making it more efficient for sending some data types. In general, you should use parameterized queries where ever possible.

+
let users = req.withPooledConnection(to: .psql) { conn in
+    return try conn.query("SELECT *  users WHERE name = $1;", ["Vapor"])
+}
+print(users) // Future<[[PostgreSQLColumn: PostgreSQLData]]>
+
+ + +

You can also provide a callback, similar to simple queries, for handling each row individually.

+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/postgresql/fluent/index.html b/3.0/site/postgresql/fluent/index.html new file mode 100644 index 00000000..be2ef377 --- /dev/null +++ b/3.0/site/postgresql/fluent/index.html @@ -0,0 +1,1720 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Fluent PostgreSQL - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

Fluent PostgreSQL

+

Fluent PostgreSQL (vapor/fluent-postgresql) is a type-safe, fast, and easy-to-use ORM for PostgreSQL built on top of Fluent.

+
+

Seealso

+

The Fluent PostgreSQL package is built on top of Fluent and the pure Swift, NIO-based PostgreSQL core. You should refer to their guides for more information about subjects not covered here.

+
+

Getting Started

+

This section will show you how to add Fluent PostgreSQL to your project and create your first PostgreSQLModel.

+

Package

+

The first step to using Fluent PostgreSQL is adding it as a dependency to your project in your SPM package manifest file.

+
// swift-tools-version:4.0
+import PackageDescription
+
+let package = Package(
+    name: "MyApp",
+    dependencies: [
+        /// Any other dependencies ...
+
+        // 🖋🐘 Swift ORM (queries, models, relations, etc) built on PostgreSQL.
+        .package(url: "https://github.com/vapor/fluent-postgresql.git", from: "1.0.0-rc"),
+    ],
+    targets: [
+        .target(name: "App", dependencies: ["FluentPostgreSQL", ...]),
+        .target(name: "Run", dependencies: ["App"]),
+        .testTarget(name: "AppTests", dependencies: ["App"]),
+    ]
+)
+
+ + +

Don't forget to add the module as a dependency in the targets array. Once you have added the dependency, regenerate your Xcode project with the following command:

+
vapor xcode
+
+ + +

Model

+

Now let's create our first PostgreSQLModel. Models represent tables in your PostgreSQL database and they are the primary method of interacting with your data.

+
/// A simple user.
+final class User: PostgreSQLModel {
+    /// The unique identifier for this user.
+    var id: Int?
+
+    /// The user's full name.
+    var name: String
+
+    /// The user's current age in years.
+    var age: Int
+
+    /// Creates a new user.
+    init(id: Int? = nil, name: String, age: Int) {
+        self.id = id
+        self.name = name
+        self.age = age
+    }
+}
+
+ + +

The example above shows a PostgreSQLModel for a simple model representing a user. You can make both structs and classes a model. You can even conform types that come from external modules. The only requirement is that these types conform to Codable, which must be declared on the base type for synthesized (automatic) conformance.

+

Standard practice with PostgreSQL databases is using an auto-generated INTEGER for creating and storing unique identifiers in the id column. It's also possible to use UUIDs or even Strings for your identifiers. There are convenience protocol for that.

+ + + + + + + + + + + + + + + + + + + + + + + + + +
protocoltypekey
PostgreSQLModelIntid
PostgreSQLUUIDModelUUIDid
PostgreSQLStringModelStringid
+
+

Seealso

+

Take a look at Fluent → Model for more information on creating models with custom ID types and keys.

+
+

Migration

+

All of your models (with some rare exceptions) should have a corresponding table—or schema—in your database. You can use a Fluent → Migration to automatically generate this schema in a testable, maintainable way. Fluent makes it easy to automatically generate a migration for your model

+
+

Tip

+

If you are creating models to represent an existing table or database, you can skip this step.

+
+
/// Allows `User` to be used as a migration.
+extension User: Migration { }
+
+ + +

That's all it takes. Fluent uses Codable to analyze your model and will attempt to create the best possible schema for it.

+

Take a look at Fluent → Migration if you are interested in customizing this migration.

+

Configure

+

The final step is to configure your database. At a minimum, this requires adding two things to your configure.swift file.

+
    +
  • FluentPostgreSQLProvider
  • +
  • MigrationConfig
  • +
+

Let's take a look.

+
import FluentPostgreSQL
+
+/// ...
+
+/// Register providers first
+try services.register(FluentPostgreSQLProvider())
+
+/// Configure migrations
+var migrations = MigrationConfig()
+migrations.add(model: User.self, database: .psql)
+services.register(migrations)
+
+/// Other services....
+
+ + +

Registering the provider will add all of the services required for Fluent PostgreSQL to work properly. It also includes a default database config struct that uses typical development environment credentials.

+

You can of course override this config struct if you have non-standard credentials.

+
/// Register custom PostgreSQL Config
+let psqlConfig = PostgreSQLDatabaseConfig(hostname: "localhost", port: 5432, username: "vapor")
+services.register(psqlConfig)
+
+ + +

Once you have the MigrationConfig added, you should be able to run your application and see the following:

+
Migrating psql DB
+Migrations complete
+Server starting on http://localhost:8080
+
+ + +

Query

+

Now that you have created a model and a corresponding schema in your database, let's make your first query.

+
router.get("users") { req in
+    return User.query(on: req).all()
+}
+
+ + +

If you run your app, and query that route, you should see an empty array returned. Now you just need to add some users! Congratulations on getting your first Fluent PostgreSQL model and migration working.

+

Connection

+

With Fluent, you always have access to the underlying database driver. Using this underlying driver to perform a query is sometimes called a "raw query".

+

Let's take a look at a raw PostgreSQL query.

+
router.get("psql-version") { req -> Future<String> in
+    return req.withPooledConnection(to: .psql) { conn in
+        return try conn.query("select version() as v;").map(to: String.self) { rows in
+            return try rows[0].firstValue(forColumn: "v")?.decode(String.self) ?? "n/a"
+        }
+    }
+}
+
+ + +

In the above example, withPooledConnection(to:) is used to create a connection to the database identified by .psql. This is the default database identifier. See Fluent → Database to learn more.

+

Once we have the PostgreSQLConnection, we can perform a query on it. You can learn more about the methods available in PostgreSQL → Core.

+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/postgresql/getting-started/index.html b/3.0/site/postgresql/getting-started/index.html new file mode 100644 index 00000000..540a82de --- /dev/null +++ b/3.0/site/postgresql/getting-started/index.html @@ -0,0 +1,1422 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Getting Started - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

Getting Started with PostgreSQL

+

PostgreSQL is a powerful, open-source database that puts an emphasis on standards compliance. PostgreSQL's strong type-system make it a great fit with Swift, and it is the preferred database for use with Vapor.

+

You can use PostgreSQL with Vapor (or any server-side Swift framework) by either:

+ +

We recommend using the ORM since it does a lot of the hard work for you. Check out the respective guides to learn more.

+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/redis/basics/index.html b/3.0/site/redis/basics/index.html new file mode 100644 index 00000000..83ccf3c6 --- /dev/null +++ b/3.0/site/redis/basics/index.html @@ -0,0 +1,1608 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Basics - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

Redis basic usage

+

To interact with Redis, you first need to construct a Redis client. +The Redis library primarily supports TCP sockets.

+

This requires a hostname, port and worker. The eventloop will be used for Redis' Socket. The hostname and port have a default. The hostname is defaulted to localhost, and the port to Redis' default port 6379.

+
let client = try RedisClient.connect(on: worker) // Future<RedisClient>
+
+ + +

The connect method will return a future containing the TCP based Redis Client.

+

Redis Data Types

+

Redis has 6 data types:

+
    +
  • null
  • +
  • Int
  • +
  • Error
  • +
  • Array
  • +
  • Basic String (used for command names and basic replies only)
  • +
  • Bulk String (used for Strings and binary data blobs)
  • +
+

You can instantiate one from the static functions and variables on RedisData.

+
let null = RedisData.null
+
+let helloWorld = RedisData.bulkString("Hello World")
+
+let three = RedisData.integer(3)
+
+let oneThroughTen = RedisData.array([
+  .integer(1),
+  .integer(2),
+  .integer(3),
+  .integer(4),
+  .integer(5),
+  .integer(6),
+  .integer(7),
+  .integer(8),
+  .integer(9),
+  .integer(10)
+])
+
+ + +

The above is the explicit way of defining Redis Types. You can also use literals in most scenarios:

+
let array = RedisData.array([
+  [
+    1, 2, 3, 4, 5, 6, 7, 8, 9, 10
+  ],
+  "Hello World",
+  "One",
+  "Two",
+  .null,
+  .null,
+  "test"
+])
+
+ + +

CRUD using Redis

+

From here on it is assumed that your client has been successfully created and is available in the variable client as a RedisClient.

+

Creating a record

+

Creating a record is done using a RedisData for a value and a key.

+
client.set("world", forKey: "hello")
+
+ + +

This returns a future that'll indicate successful or unsuccessful insertion.

+

Reading a record

+

Reading a record is similar, only you'll get a warning if you don't use the returned future.

+

The Future<RedisData> for the key "hello" will be "world" if you created the record as shown above.

+
let futureRecord = client.getData(forKey: "hello") // Future<RedisData>
+
+ + +

Deleting a record

+

Deleting a record is similar but allows querying the keys, too.

+
client.delete(keys: ["hello"])
+
+ + +

Where the above command will remove the key "hello", the next command will delete all keys from the Redis database.

+
client.delete(keys: ["*"])
+
+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/redis/custom-commands/index.html b/3.0/site/redis/custom-commands/index.html new file mode 100644 index 00000000..f5effbe3 --- /dev/null +++ b/3.0/site/redis/custom-commands/index.html @@ -0,0 +1,1470 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Custom commands - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

Custom commands

+

Many commands are not (yet) implemented by the driver using a convenience function. This does not mean the feature/command is not usable.

+

(Almost) all functions listed here work out of the box using custom commands.

+

Usage

+

The Redis client has a run function that allows you to run these commands.

+

The following code demonstrates a "custom" implementation for GET.

+
let future = client.run(command: "GET", arguments: ["my-key"]) // Future<RedisData>
+
+ + +

This future will contain the result as specified in the article on the redis command page or an error.

+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/redis/getting-started/index.html b/3.0/site/redis/getting-started/index.html new file mode 100644 index 00000000..55885f52 --- /dev/null +++ b/3.0/site/redis/getting-started/index.html @@ -0,0 +1,1498 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Getting Started - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

Redis

+

Redis is a Redis client library that can communicate with a Redis database.

+

What is Redis?

+

Redis is an in-memory data store used as a database, cache and message broker. It supports most common data structures. Redis is most commonly used for caching data such as sessions and notifications (between multiple servers).

+

Redis works as a key-value store, but allows querying the keys, unlike most databases.

+

With and without Vapor

+

To include it in your package, add the following to your Package.swift file.

+
// swift-tools-version:4.0
+import PackageDescription
+
+let package = Package(
+    name: "Project",
+    dependencies: [
+        ...
+        .package(url: "https://github.com/vapor/redis.git", .upToNextMajor(from: "3.0.0")),
+    ],
+    targets: [
+      .target(name: "Project", dependencies: ["Redis", ... ])
+    ]
+)
+
+ + +

If this is your first time adding a dependency, you should read our introduction to Package.swift.

+

Use import Redis to access Redis' APIs.

+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/redis/pipeline/index.html b/3.0/site/redis/pipeline/index.html new file mode 100644 index 00000000..5558c20a --- /dev/null +++ b/3.0/site/redis/pipeline/index.html @@ -0,0 +1,1487 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Pipeline - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

Pipelining

+

Pipelining is used for sending multiple commands at once. The performance advantages become apparent when sending a large number of queries. Redis' pipelining cuts down latency by reducing the RTT (Round Trip Time) between the client and server. Pipelining also reduces the amount of IO operations Redis has to perform, this increases the amount of queries per second Redis can handle.

+

Use cases

+

Sometimes multiple commands need to be executed at once. Instead of sending those commands individually in a loop, pipelining allows the commands to be batched and sent in one request. A common scenario might be needing to set a key and increment a count, pipelining those commands would be ideal.

+

Enqueuing Commands

+
 let pipeline = connection.makePipeline()
+ let result = try pipeline
+         .enqueue(command: "SET", arguments: ["KEY", "VALUE"])
+         .enqueue(command: "INCR", arguments: ["COUNT"])
+         .execute() // Future<[RedisData]>
+
+ + +

Note: Commands will not be executed until execute is called.

+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/redis/pub-sub/index.html b/3.0/site/redis/pub-sub/index.html new file mode 100644 index 00000000..db3c7c7e --- /dev/null +++ b/3.0/site/redis/pub-sub/index.html @@ -0,0 +1,1529 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Publish and Subscribe - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

Publish & Subscribe

+

Redis' Publish and Subscribe model is really useful for notifications.

+

Use cases

+

Pub/sub is used for notifying subscribers of an event. +A simple and common event for example would be a chat message.

+

A channel consists of a name and group of listeners. Think of it as being [String: [Listener]]. +When you send a notification to a channel you need to provide a payload. +Each listener will get a notification consisting of this payload.

+

Channels must be a string. For chat groups, for example, you could use the database identifier.

+

Publishing

+

You cannot get a list of listeners, but sending a payload will emit the amount of listeners that received the notification. +Sending (publishing) an event is done like so:

+
// Any redis data
+let notification: RedisData = "My-Notification"
+
+client.publish(notification, to: "my-channel")
+
+ + +

If you want access to the listener count:

+
let notifiedCount = client.publish(notification, to: "my-channel") // Future<Int>
+
+ + +

Subscribing

+

To subscribe for notifications you're rendering an entire Redis Client useless in exchange for listening to events.

+

A single client can listen to one or more channels, which is provided using a set of unique channel names. The result of subscribing is a SubscriptionStream.

+
let notifications = client.subscribe(to: ["some-notification-channel", "other-notification-channel"])
+
+ + +

If you try to use the client after subscribing, all operations will fail. These errors are usually emitted through the Future.

+

This stream will receive messages asynchronously from the point of draining. This works like any other async stream.

+

Notifications consist of the channel and payload.

+
notifications.drain { notification in
+  print(notification.channel)
+
+  let payload = notification.payload
+
+  // TODO: Process the payload
+}
+
+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/routing/getting-started/index.html b/3.0/site/routing/getting-started/index.html new file mode 100644 index 00000000..c4ad23b7 --- /dev/null +++ b/3.0/site/routing/getting-started/index.html @@ -0,0 +1,1523 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Getting Started - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

Basics

+

In Vapor the default Router is the EngineRouter. You can implement custom routers by implementing one conformant to the Router protocol.

+
let router = try EngineRouter.default()
+
+ + +

There are two APIs available, one is supplied by the Routing library and a set of helpers is available in Vapor itself.

+

Registering a route using Routing

+

The on function on a AsyncRouter registers a route to the provided path. The following registers a GET /hello/world route.

+

It responds with "Hello world!" using futures.

+
router.on(.get, to: "hello", "world") { request in
+  return try Response(body: "Hello world!") 
+}
+
+ + +

The .get represents the HTTP method you want to use. to: "hello", "world" registers the path /hello/world.

+

For variable path components you can use parameters.

+

The trailing closure receives a request. The route can throw errors and needs to return a future response conforming type.

+

Registering a route using Vapor

+

In Vapor we add support for routes using the .get, .put, .post, .patch and .delete shorthands.

+

For variable path components you can use parameters here, too.

+

Vapor has an added benefit here in that you can return the Response itself in addition to Future<ResponseRepresentable> or Future<Response>.

+
router.get("components", "in", "path") { request in
+  return Response(status: .ok)
+}
+
+ + +

After registering your routes

+

After registering routes to the Router, you must add the router to your services.

+
services.register(router, as: Router.self)
+
+ + +

Learn more about services in Getting Started → Services

+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/routing/parameters/index.html b/3.0/site/routing/parameters/index.html new file mode 100644 index 00000000..1410f7f8 --- /dev/null +++ b/3.0/site/routing/parameters/index.html @@ -0,0 +1,1506 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Parameters - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

Parameters

+

Parameters are a registered type that can be initialized from a String.

+

They can be part of a Route, and be extracted from requests that are called in that route.

+

Creating custom parameters

+

To create a custom parameter type, simply conform to Parameter and implement the conversion function make and a unique slug.

+

In this example, the User class will be initialized from a parameter that represents it's identifier.

+

We recommend prefixing custom Parameter identifiers.

+
class User : Parameter {
+  var username: String
+
+  // The unique (prefixed) identifier for this type
+  static var uniqueSlug = "my-app:user"
+
+  // Creates a new user from the raw `parameter`
+  static func make(for parameter: String, in request: Request) throws -> User {
+    return User(named: parameter)
+  }
+
+  init(named username: String) {
+    self.username = username
+  }
+}
+
+ + +

Using (custom) parameters

+

After conforming a type to Parameter you can access it's static property parameter as part of a path.

+
router.on(.get, to: "users", User.parameter, "profile") { request in
+  let user = try request.parameters.next(User.self)
+
+  // Return the user's Profile sync or async (depending on the router)
+}
+
+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/routing/route/index.html b/3.0/site/routing/route/index.html new file mode 100644 index 00000000..d279a147 --- /dev/null +++ b/3.0/site/routing/route/index.html @@ -0,0 +1,1465 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Route - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

Route

+

Route is an object that contains the essential information of an HTTP Route.

+

It contains the route's Method, path components and responder.

+

Extensions

+

Routes are Extensible using the extend property. This allow storing additional data for use by integrating libraries.

+

The purpose is to allow tools (such as documentation tools) to hook into the Vapor routing process.

+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/routing/router/index.html b/3.0/site/routing/router/index.html new file mode 100644 index 00000000..0e8140b7 --- /dev/null +++ b/3.0/site/routing/router/index.html @@ -0,0 +1,1505 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Router - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

Router

+

Router is a protocol that you can conform your own routers to.

+

Registering a route

+

First, create a Route using a HTTP method, path and a responder.

+

The following example shows a route with a constant path.

+
let responder = BasicAsyncResponder { request in
+  return "Hello world"
+}
+
+let route = Route(method: .get, path: [.constant("hello"), .constant("world")], responder: responder)
+
+ + +

The following example shows a with a Parameter:

+
let responder = BasicSyncResponder { request in
+  let name = try request.parameters.next(String.self)
+  return "Hello \(name)"
+}
+
+let route = Route(method: .get, path: [.constant("greet"), .parameter(String.self)], responder: responder)
+
+ + +

Routing a request through a Router

+

Assuming you have a request, like the following example:

+
let request = Request(method: .get, URI(path: "/hello/world"))
+
+ + +

The router should be able to route the HTTP request using

+
let responder = router.route(request: request)
+
+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/search/search_index.json b/3.0/site/search/search_index.json new file mode 100644 index 00000000..8bf07e8d --- /dev/null +++ b/3.0/site/search/search_index.json @@ -0,0 +1,1799 @@ +{ + "docs": [ + { + "location": "/", + "text": "Vapor Documentation\n\n\nThis is the documentation for Vapor, a Web Framework for Swift that works on iOS, macOS, and Ubuntu; and all of the packages that Vapor offers.\n\n\nVapor is the most used web framework for Swift. It provides a beautifully expressive and easy to use foundation for your next website or API.\n\n\nGetting Started\n\n\nIf this is your first time using Vapor, head to the \nInstall \n macOS\n section to install Swift and Vapor.\n\n\nOnce you have Vapor installed, check out \nGetting Started \n Hello, world\n to create your first Vapor app!\n\n\nLike Vapor?\n\n\nOur small team works hard to make Vapor awesome (and free). Support the framework by \nstarring Vapor on GitHub\n\nor \ndonating $1 monthly\nit helps us a lot. Thanks!\n\n\nOther Sources\n\n\nHere are some other great places to find information about Vapor.\n\n\n\n\n\n\n\n\nname\n\n\ndescription\n\n\nlink\n\n\n\n\n\n\n\n\n\n\nVapor Slack\n\n\nChat with ~5,000 Vapor developers.\n\n\nvisit \n\n\n\n\n\n\nAPI docs\n\n\nAuto-generated documentation from code comments.\n\n\nvisit \n\n\n\n\n\n\nStack Overflow\n\n\nAsk and answer questions with the \nvapor\n tag.\n\n\nvisit \n\n\n\n\n\n\nSource Code\n\n\nLearn how Vapor works under the hood.\n\n\nvisit \n\n\n\n\n\n\nGitHub Issues\n\n\nReport bugs or request features on GitHub.\n\n\nvisit \n\n\n\n\n\n\n\n\nProviders\n\n\nVapor providers are a convenient way to add functionality to your Vapor projects.\nFor a full list of providers, check out the \nvapor-provider\n tag on GitHub.\n\n\nAuthors\n\n\nTanner Nelson\n, \nLogan Wright\n, \nJoannis Orlandos\n, and the hundreds of members of Vapor.", + "title": "Overview" + }, + { + "location": "/#vapor-documentation", + "text": "This is the documentation for Vapor, a Web Framework for Swift that works on iOS, macOS, and Ubuntu; and all of the packages that Vapor offers. Vapor is the most used web framework for Swift. It provides a beautifully expressive and easy to use foundation for your next website or API.", + "title": "Vapor Documentation" + }, + { + "location": "/#getting-started", + "text": "If this is your first time using Vapor, head to the Install macOS section to install Swift and Vapor. Once you have Vapor installed, check out Getting Started Hello, world to create your first Vapor app!", + "title": "Getting Started" + }, + { + "location": "/#like-vapor", + "text": "Our small team works hard to make Vapor awesome (and free). Support the framework by starring Vapor on GitHub \nor donating $1 monthly it helps us a lot. Thanks!", + "title": "Like Vapor?" + }, + { + "location": "/#other-sources", + "text": "Here are some other great places to find information about Vapor. name description link Vapor Slack Chat with ~5,000 Vapor developers. visit API docs Auto-generated documentation from code comments. visit Stack Overflow Ask and answer questions with the vapor tag. visit Source Code Learn how Vapor works under the hood. visit GitHub Issues Report bugs or request features on GitHub. visit", + "title": "Other Sources" + }, + { + "location": "/#providers", + "text": "Vapor providers are a convenient way to add functionality to your Vapor projects.\nFor a full list of providers, check out the vapor-provider tag on GitHub.", + "title": "Providers" + }, + { + "location": "/#authors", + "text": "Tanner Nelson , Logan Wright , Joannis Orlandos , and the hundreds of members of Vapor.", + "title": "Authors" + }, + { + "location": "/install/macos/", + "text": "Install on macOS\n\n\nTo use Vapor on macOS, you just need to have Xcode 9 or greater installed.\n\n\n\n\nTip\n\n\nYou need to install Xcode to install Swift, but after that you can use any text editor \nyou like to develop Vapor apps.\n\n\n\n\nInstall Xcode\n\n\nInstall \nXcode 9 or greater\n from the Mac App Store.\n\n\n\n\n\n\nWarning\n\n\nAfter Xcode has been downloaded, you must open it to finish the installation. This may take a while.\n\n\n\n\nVerify Installation\n\n\nDouble check the installation was successful by opening Terminal and running:\n\n\nswift --version\n\n\n\n\n\nYou should see output similar to:\n\n\nApple Swift version \n4\n.0.2 \n(\nswiftlang-900.0.69.2 clang-900.0.38\n)\n\nTarget: x86_64-apple-macosx10.9\n\n\n\n\n\nVapor requires Swift 4.\n\n\nInstall Vapor\n\n\nNow that you have Swift 4, let's install the \nVapor Toolbox\n.\n\n\nThe toolbox includes all of Vapor's dependencies as well as a handy CLI tool for creating new projects.\n\n\nbrew install vapor/tap/vapor\n\n\n\n\n\n\n\nTip\n\n\nIf you don't already have Homebrew installed, install it at \nbrew.sh \n\n\n\n\nVerify Installation\n\n\nDouble check the installation was successful by opening Terminal and running:\n\n\nvapor --help\n\n\n\n\n\nYou should see a long list of available commands.\n\n\nDone\n\n\nNow that you have installed Vapor, create your first app in \nGetting Started \n Hello, world\n.", + "title": "macOS" + }, + { + "location": "/install/macos/#install-on-macos", + "text": "To use Vapor on macOS, you just need to have Xcode 9 or greater installed. Tip You need to install Xcode to install Swift, but after that you can use any text editor \nyou like to develop Vapor apps.", + "title": "Install on macOS" + }, + { + "location": "/install/macos/#install-xcode", + "text": "Install Xcode 9 or greater from the Mac App Store. Warning After Xcode has been downloaded, you must open it to finish the installation. This may take a while.", + "title": "Install Xcode" + }, + { + "location": "/install/macos/#verify-installation", + "text": "Double check the installation was successful by opening Terminal and running: swift --version You should see output similar to: Apple Swift version 4 .0.2 ( swiftlang-900.0.69.2 clang-900.0.38 ) \nTarget: x86_64-apple-macosx10.9 Vapor requires Swift 4.", + "title": "Verify Installation" + }, + { + "location": "/install/macos/#install-vapor", + "text": "Now that you have Swift 4, let's install the Vapor Toolbox . The toolbox includes all of Vapor's dependencies as well as a handy CLI tool for creating new projects. brew install vapor/tap/vapor Tip If you don't already have Homebrew installed, install it at brew.sh", + "title": "Install Vapor" + }, + { + "location": "/install/macos/#verify-installation_1", + "text": "Double check the installation was successful by opening Terminal and running: vapor --help You should see a long list of available commands.", + "title": "Verify Installation" + }, + { + "location": "/install/macos/#done", + "text": "Now that you have installed Vapor, create your first app in Getting Started Hello, world .", + "title": "Done" + }, + { + "location": "/install/ubuntu/", + "text": "Install on Ubuntu\n\n\nInstalling Vapor on Ubuntu only takes a couple of minutes.\n\n\nSupported\n\n\nVapor supports the same versions of Ubuntu that Swift supports.\n\n\n\n\n\n\n\n\nVersion\n\n\nCodename\n\n\n\n\n\n\n\n\n\n\n16.10\n\n\nYakkety Yak\n\n\n\n\n\n\n16.04\n\n\nXenial Xerus\n\n\n\n\n\n\n14.04\n\n\nTrusty Tahr\n\n\n\n\n\n\n\n\nAPT Repo\n\n\nAdd Vapor's APT repo to get access to all of Vapor's Ubuntu packages.\n\n\nQuick Script\n\n\nEasily add Vapor's APT repo with this handy script.\n\n\neval\n \n$(\ncurl -sL https://apt.vapor.sh\n)\n\n\n\n\n\n\n\n\nTip\n\n\nThis command requires \ncurl\n which can be installed using \nsudo apt-get install curl\n\n\n\n\nDockerfile\n\n\nWhen configuring Ubuntu from a Dockerfile, adding the APT repo can be done via this command:\n\n\nRUN /bin/bash -c \n$(\nwget -qO- https://apt.vapor.sh\n)\n\n\n\n\n\n\nManual\n\n\nOr add the repo manually.\n\n\nwget -q https://repo.vapor.codes/apt/keyring.gpg -O- \n|\n sudo apt-key add -\n\necho\n \ndeb https://repo.vapor.codes/apt \n$(\nlsb_release -sc\n)\n main\n \n|\n sudo tee /etc/apt/sources.list.d/vapor.list\nsudo apt-get update\n\n\n\n\n\nInstall Vapor\n\n\nNow that you have added Vapor's APT repo, you can install the required dependencies.\n\n\nsudo apt-get install swift vapor\n\n\n\n\n\nVerify Installation\n\n\nDouble check everything worked with the following commands.\n\n\nSwift\n\n\nswift --version\n\n\n\n\n\nYou should see output similar to:\n\n\nApple Swift version \n4\n.0.2 \n(\nswiftlang-900.0.69.2 clang-900.0.38\n)\n\nTarget: x86_64-apple-macosx10.9\n\n\n\n\n\nVapor requires Swift 4.\n\n\nVapor Toolbox\n\n\nvapor --help\n\n\n\n\n\nYou should see a long list of available commands.\n\n\nDone\n\n\nNow that you have installed Vapor, create your first app in \nGetting Started \n Hello, world\n.\n\n\nSwift.org\n\n\nCheck out \nSwift.org\n's guide to \nusing downloads\n if you need more detailed instructions for installing Swift 4.", + "title": "Ubuntu" + }, + { + "location": "/install/ubuntu/#install-on-ubuntu", + "text": "Installing Vapor on Ubuntu only takes a couple of minutes.", + "title": "Install on Ubuntu" + }, + { + "location": "/install/ubuntu/#supported", + "text": "Vapor supports the same versions of Ubuntu that Swift supports. Version Codename 16.10 Yakkety Yak 16.04 Xenial Xerus 14.04 Trusty Tahr", + "title": "Supported" + }, + { + "location": "/install/ubuntu/#apt-repo", + "text": "Add Vapor's APT repo to get access to all of Vapor's Ubuntu packages.", + "title": "APT Repo" + }, + { + "location": "/install/ubuntu/#quick-script", + "text": "Easily add Vapor's APT repo with this handy script. eval $( curl -sL https://apt.vapor.sh ) Tip This command requires curl which can be installed using sudo apt-get install curl", + "title": "Quick Script" + }, + { + "location": "/install/ubuntu/#dockerfile", + "text": "When configuring Ubuntu from a Dockerfile, adding the APT repo can be done via this command: RUN /bin/bash -c $( wget -qO- https://apt.vapor.sh )", + "title": "Dockerfile" + }, + { + "location": "/install/ubuntu/#manual", + "text": "Or add the repo manually. wget -q https://repo.vapor.codes/apt/keyring.gpg -O- | sudo apt-key add - echo deb https://repo.vapor.codes/apt $( lsb_release -sc ) main | sudo tee /etc/apt/sources.list.d/vapor.list\nsudo apt-get update", + "title": "Manual" + }, + { + "location": "/install/ubuntu/#install-vapor", + "text": "Now that you have added Vapor's APT repo, you can install the required dependencies. sudo apt-get install swift vapor", + "title": "Install Vapor" + }, + { + "location": "/install/ubuntu/#verify-installation", + "text": "Double check everything worked with the following commands.", + "title": "Verify Installation" + }, + { + "location": "/install/ubuntu/#swift", + "text": "swift --version You should see output similar to: Apple Swift version 4 .0.2 ( swiftlang-900.0.69.2 clang-900.0.38 ) \nTarget: x86_64-apple-macosx10.9 Vapor requires Swift 4.", + "title": "Swift" + }, + { + "location": "/install/ubuntu/#vapor-toolbox", + "text": "vapor --help You should see a long list of available commands.", + "title": "Vapor Toolbox" + }, + { + "location": "/install/ubuntu/#done", + "text": "Now that you have installed Vapor, create your first app in Getting Started Hello, world .", + "title": "Done" + }, + { + "location": "/install/ubuntu/#swiftorg", + "text": "Check out Swift.org 's guide to using downloads if you need more detailed instructions for installing Swift 4.", + "title": "Swift.org" + }, + { + "location": "/getting-started/hello-world/", + "text": "Hello, world\n\n\nNow that you've installed Vapor, let's create your first Vapor app!\nThis guide will take you step by step through creating a new project, building, and running it.\n\n\nNew Project\n\n\nThe first step is to create a new Vapor project on your computer.\nFor this guide, we will call the project \nHello\n.\n\n\nOpen up your terminal, and use \nVapor Toolbox's \nnew\n command.\n\n\nvapor new Hello\n\n\n\n\n\n\n\nWarning\n\n\nMake sure to add \n--branch=beta\n while using Vapor 3 pre-release.\n\n\nIf you receive an error that looks like this: 'Cloning Template [Failed]' then the template you are using is not yet ready for the beta branch. Try a different template. \n\n\n\n\nOnce that finishes, change into the newly created directory.\n\n\ncd\n Hello\n\n\n\n\n\nGenerate Xcode Project\n\n\nLet's now use the \nVapor Toolbox's \nxcode\n command to generate an Xcode project.\nThis will allow us to build and run our app from inside of Xcode, just like an iOS app.\n\n\nvapor xcode\n\n\n\n\n\nThe toolbox will ask you if you'd like to open Xcode automatically, select \nyes\n.\n\n\nBuild \n Run\n\n\nYou should now have Xcode open and running. Select the \nrun scheme\n from the scheme menu,\nthen click the play button.\n\n\nYou should see the terminal pop up at the bottom of the screen.\n\n\nServer starting on localhost:8080\n\n\n\n\n\nVisit Localhost\n\n\nOpen your web browser, and visit \nlocalhost:8080/hello \n\n\nYou should see the following page.\n\n\nHello, world!\n\n\n\n\n\nCongratulations on creating, building, and running your first Vapor app! \ud83c\udf89", + "title": "Hello, world" + }, + { + "location": "/getting-started/hello-world/#hello-world", + "text": "Now that you've installed Vapor, let's create your first Vapor app!\nThis guide will take you step by step through creating a new project, building, and running it.", + "title": "Hello, world" + }, + { + "location": "/getting-started/hello-world/#new-project", + "text": "The first step is to create a new Vapor project on your computer.\nFor this guide, we will call the project Hello . Open up your terminal, and use Vapor Toolbox's new command. vapor new Hello Warning Make sure to add --branch=beta while using Vapor 3 pre-release. If you receive an error that looks like this: 'Cloning Template [Failed]' then the template you are using is not yet ready for the beta branch. Try a different template. Once that finishes, change into the newly created directory. cd Hello", + "title": "New Project" + }, + { + "location": "/getting-started/hello-world/#generate-xcode-project", + "text": "Let's now use the Vapor Toolbox's xcode command to generate an Xcode project.\nThis will allow us to build and run our app from inside of Xcode, just like an iOS app. vapor xcode The toolbox will ask you if you'd like to open Xcode automatically, select yes .", + "title": "Generate Xcode Project" + }, + { + "location": "/getting-started/hello-world/#build-run", + "text": "You should now have Xcode open and running. Select the run scheme from the scheme menu,\nthen click the play button. You should see the terminal pop up at the bottom of the screen. Server starting on localhost:8080", + "title": "Build & Run" + }, + { + "location": "/getting-started/hello-world/#visit-localhost", + "text": "Open your web browser, and visit localhost:8080/hello You should see the following page. Hello, world! Congratulations on creating, building, and running your first Vapor app! \ud83c\udf89", + "title": "Visit Localhost" + }, + { + "location": "/getting-started/toolbox/", + "text": "Install Toolbox\n\n\nVapor's command line interface provides shortcuts and assistance for common tasks.\n\n\n\n\nHelp prints useful information about available commands and flags.\n\n\nvapor --help\n\n\n\n\n\nYou can also run the \n--help\n option on any Toolbox command.\n\n\nvapor new --help\n\n\n\n\n\nThe \n--help\n flag should be your goto for learning about the toolbox as it is the most up-to-date.\n\n\nNew\n\n\nThe Toolbox's most important feature is helping you create a new project.\n\n\nvapor new \nname\n\n\n\n\n\n\nJust pass the name of your project as the first argument to the \nnew\n command.\n\n\n\n\nNote\n\n\nProject names should be \nPascalCase \n, like \nHelloWorld\n or \nMyProject\n.\n\n\n\n\nTemplates\n\n\nBy default, Vapor will create your new project from the API template. You can choose\na different template by passing the \n--template\n flag.\n\n\n\n\n\n\n\n\nName\n\n\nFlag\n\n\nDescription\n\n\n\n\n\n\n\n\n\n\nAPI\n\n\n--template=api\n\n\nJSON API with Fluent database.\n\n\n\n\n\n\nWeb\n\n\n--template=web\n\n\nHTML website with Leaf templates.\n\n\n\n\n\n\n\n\n\n\nInfo\n\n\nThere are lots of unofficial Vapor templates on GitHub under the \nvapor\n + \ntemplate\n topcs \n.\nYou can use these by passing the full GitHub URL to the \n--template\n option.\n\n\n\n\nBuild \n Run\n\n\nYou can use the toolbox to build and run your Vapor app.\n\n\nvapor build\nvapor run\n\n\n\n\n\n\n\nTip\n\n\nWe recommend building and running through \nXcode\n if you have a Mac. \nIt's a bit faster and you can set breakpoints! \nJust use \nvapor xcode\n to generate an Xcode project.\n\n\n\n\nUpdating\n\n\nThe toolbox should be updated by the package manager it was installed with.\n\n\nHomebrew\n\n\nbrew upgrade vapor\n\n\n\n\n\nAPT\n\n\nsudo apt-get update\nsudo apt-get install vapor", + "title": "Toolbox" + }, + { + "location": "/getting-started/toolbox/#install-toolbox", + "text": "Vapor's command line interface provides shortcuts and assistance for common tasks. Help prints useful information about available commands and flags. vapor --help You can also run the --help option on any Toolbox command. vapor new --help The --help flag should be your goto for learning about the toolbox as it is the most up-to-date.", + "title": "Install Toolbox" + }, + { + "location": "/getting-started/toolbox/#new", + "text": "The Toolbox's most important feature is helping you create a new project. vapor new name Just pass the name of your project as the first argument to the new command. Note Project names should be PascalCase , like HelloWorld or MyProject .", + "title": "New" + }, + { + "location": "/getting-started/toolbox/#templates", + "text": "By default, Vapor will create your new project from the API template. You can choose\na different template by passing the --template flag. Name Flag Description API --template=api JSON API with Fluent database. Web --template=web HTML website with Leaf templates. Info There are lots of unofficial Vapor templates on GitHub under the vapor + template topcs .\nYou can use these by passing the full GitHub URL to the --template option.", + "title": "Templates" + }, + { + "location": "/getting-started/toolbox/#build-run", + "text": "You can use the toolbox to build and run your Vapor app. vapor build\nvapor run Tip We recommend building and running through Xcode if you have a Mac. \nIt's a bit faster and you can set breakpoints! \nJust use vapor xcode to generate an Xcode project.", + "title": "Build & Run" + }, + { + "location": "/getting-started/toolbox/#updating", + "text": "The toolbox should be updated by the package manager it was installed with.", + "title": "Updating" + }, + { + "location": "/getting-started/toolbox/#homebrew", + "text": "brew upgrade vapor", + "title": "Homebrew" + }, + { + "location": "/getting-started/toolbox/#apt", + "text": "sudo apt-get update\nsudo apt-get install vapor", + "title": "APT" + }, + { + "location": "/getting-started/spm/", + "text": "Managing your project\n\n\nThe Swift Package Manager (SPM for short) is used for building your project's source code and dependencies. \nIt's a similar idea to Cocoapods, Ruby gems, and NPM. Most of the time the \nVapor Toolbox\n will \ninteract with SPM on your behalf. However, it's important to understand the basics.\n\n\n\n\nTip\n\n\nLearn more about SPM on \nSwift.org \n \n\n\n\n\nPackage Manifest\n\n\nThe first place SPM looks in your project is the package manfiest. This should always be located in the root\ndirectory of your project and named \nPackage.swift\n.\n\n\nDependencies\n\n\nDependencies are other SPM packages that your package relies on. All Vapor applications rely on the Vapor package,\nbut you can add as many other dependencies as you want.\n\n\n// swift-tools-version:4.0\n\n\nimport\n \nPackageDescription\n\n\n\nlet\n \npackage\n \n=\n \nPackage\n(\n\n \nname\n:\n \nVaporApp\n,\n\n \ndependencies\n:\n \n[\n\n \n// \ud83d\udca7 A server-side Swift web framework. \n\n \n.\npackage\n(\nurl\n:\n \nhttps://github.com/vapor/vapor.git\n,\n \nfrom\n:\n \n3.0.0\n),\n\n \n],\n\n \ntargets\n:\n \n[\n \n...\n \n]\n\n\n)\n\n\n\n\n\n\nIn the above example, you can see \nvapor/vapor \n version 3.0\nor later is a dependency of this package.\nWhen you add a dependency to your package, you must next signal which \ntargets\n depend on\nthe newly available modules.\n\n\n\n\nWarning\n\n\nAnytime you modify the package manifest, call \nvapor update\n to effect the changes.\n\n\n\n\nTargets\n\n\nTargets are all of the modules, executables, and tests that your package contains. \n\n\n// swift-tools-version:4.0\n\n\nimport\n \nPackageDescription\n\n\n\nlet\n \npackage\n \n=\n \nPackage\n(\n\n \nname\n:\n \nVaporApp\n,\n\n \ndependencies\n:\n \n[\n \n...\n \n],\n\n \ntargets\n:\n \n[\n\n \n.\ntarget\n(\nname\n:\n \nApp\n,\n \ndependencies\n:\n \n[\nVapor\n]),\n\n \n.\ntarget\n(\nname\n:\n \nRun\n,\n \ndependencies\n:\n \n[\nApp\n]),\n\n \n.\ntestTarget\n(\nname\n:\n \nAppTests\n,\n \ndependencies\n:\n \n[\nApp\n]),\n\n \n]\n\n\n)\n\n\n\n\n\n\nMost Vapor apps will have three targets, although you can add as many as you like to organize your code.\nEach target declares which modules it depends on. You must add module names here in order to \nimport\n them in your code.\nA target can depend on other targets in your project or any modules exposed by packages you've added to\nthe \nmain dependencies\n array.\n\n\n\n\nTip\n\n\nExecutable targets (targets that contain a \nmain.swift\n file) cannot be imported by other modules.\nThis is why Vapor has both an \nApp\n and a \nRun\n target.\nAny code you include in \nApp\n can be tested in the \nAppTests\n.\n\n\n\n\nFolder Structure\n\n\nBelow is the typical folder structure for an SPM package.\n\n\n.\n\u251c\u2500\u2500 Sources\n\u2502 \u251c\u2500\u2500 App\n\u2502 \u2502 \u2514\u2500\u2500 (Source code)\n\u2502 \u2514\u2500\u2500 Run\n\u2502 \u2514\u2500\u2500 main.swift\n\u251c\u2500\u2500 Tests\n\u2502 \u2514\u2500\u2500 AppTests\n\u2514\u2500\u2500 Package.swift\n\n\n\n\n\nEach \n.target\n corresponds to a folder in the \nSources\n folder. \nEach \n.testTarget\n corresponds to a folder in the \nTests\n folder.\n\n\nTroubleshooting\n\n\nIf you are experiencing problems with SPM, sometimes cleaning your project can help.\n\n\nvapor clean", + "title": "SPM" + }, + { + "location": "/getting-started/spm/#managing-your-project", + "text": "The Swift Package Manager (SPM for short) is used for building your project's source code and dependencies. \nIt's a similar idea to Cocoapods, Ruby gems, and NPM. Most of the time the Vapor Toolbox will \ninteract with SPM on your behalf. However, it's important to understand the basics. Tip Learn more about SPM on Swift.org", + "title": "Managing your project" + }, + { + "location": "/getting-started/spm/#package-manifest", + "text": "The first place SPM looks in your project is the package manfiest. This should always be located in the root\ndirectory of your project and named Package.swift .", + "title": "Package Manifest" + }, + { + "location": "/getting-started/spm/#dependencies", + "text": "Dependencies are other SPM packages that your package relies on. All Vapor applications rely on the Vapor package,\nbut you can add as many other dependencies as you want. // swift-tools-version:4.0 import PackageDescription let package = Package ( \n name : VaporApp , \n dependencies : [ \n // \ud83d\udca7 A server-side Swift web framework. \n . package ( url : https://github.com/vapor/vapor.git , from : 3.0.0 ), \n ], \n targets : [ ... ] ) In the above example, you can see vapor/vapor version 3.0\nor later is a dependency of this package.\nWhen you add a dependency to your package, you must next signal which targets depend on\nthe newly available modules. Warning Anytime you modify the package manifest, call vapor update to effect the changes.", + "title": "Dependencies" + }, + { + "location": "/getting-started/spm/#targets", + "text": "Targets are all of the modules, executables, and tests that your package contains. // swift-tools-version:4.0 import PackageDescription let package = Package ( \n name : VaporApp , \n dependencies : [ ... ], \n targets : [ \n . target ( name : App , dependencies : [ Vapor ]), \n . target ( name : Run , dependencies : [ App ]), \n . testTarget ( name : AppTests , dependencies : [ App ]), \n ] ) Most Vapor apps will have three targets, although you can add as many as you like to organize your code.\nEach target declares which modules it depends on. You must add module names here in order to import them in your code.\nA target can depend on other targets in your project or any modules exposed by packages you've added to\nthe main dependencies array. Tip Executable targets (targets that contain a main.swift file) cannot be imported by other modules.\nThis is why Vapor has both an App and a Run target.\nAny code you include in App can be tested in the AppTests .", + "title": "Targets" + }, + { + "location": "/getting-started/spm/#folder-structure", + "text": "Below is the typical folder structure for an SPM package. .\n\u251c\u2500\u2500 Sources\n\u2502 \u251c\u2500\u2500 App\n\u2502 \u2502 \u2514\u2500\u2500 (Source code)\n\u2502 \u2514\u2500\u2500 Run\n\u2502 \u2514\u2500\u2500 main.swift\n\u251c\u2500\u2500 Tests\n\u2502 \u2514\u2500\u2500 AppTests\n\u2514\u2500\u2500 Package.swift Each .target corresponds to a folder in the Sources folder. \nEach .testTarget corresponds to a folder in the Tests folder.", + "title": "Folder Structure" + }, + { + "location": "/getting-started/spm/#troubleshooting", + "text": "If you are experiencing problems with SPM, sometimes cleaning your project can help. vapor clean", + "title": "Troubleshooting" + }, + { + "location": "/getting-started/xcode/", + "text": "Xcode\n\n\nIf you're on a Mac, you can develop your Vapor project using Xcode. \nYou can build, run, and stop your server from within Xcode, as well as use breakpoints and instruments to debug your code.\n\n\n\n\nXcode is a great way to develop Vapor apps, but you can use any text editor you like.\n\n\nGenerate Project\n\n\nTo use Xcode, you just need to generate an Xcode project using \nVapor Toolbox\n.\n\n\nvapor xcode\n\n\n\n\n\n\n\nTip\n\n\nDon't worry about comitting the generated Xcode Project to git, just generate a new\none whenever you need it.\n\n\n\n\nRun\n\n\nTo build and run your Vapor app, first make sure you have the \nRun\n scheme selected from the schemes menu.\nAlso make sure to select \"My Mac\" as the device.\n\n\n\n\nOnce that's selected, just click the play button or press \nCommand + R\n on your keyboard.\n\n\nTest\n\n\nTo run your unit tests, select the scheme ending in \n-Package\n and press \nCommand + U\n.\n\n\n\n\nWarning\n\n\nThere may be a few extraneous schemes in the dropdown menu. Ignore them!", + "title": "Xcode" + }, + { + "location": "/getting-started/xcode/#xcode", + "text": "If you're on a Mac, you can develop your Vapor project using Xcode. \nYou can build, run, and stop your server from within Xcode, as well as use breakpoints and instruments to debug your code. Xcode is a great way to develop Vapor apps, but you can use any text editor you like.", + "title": "Xcode" + }, + { + "location": "/getting-started/xcode/#generate-project", + "text": "To use Xcode, you just need to generate an Xcode project using Vapor Toolbox . vapor xcode Tip Don't worry about comitting the generated Xcode Project to git, just generate a new\none whenever you need it.", + "title": "Generate Project" + }, + { + "location": "/getting-started/xcode/#run", + "text": "To build and run your Vapor app, first make sure you have the Run scheme selected from the schemes menu.\nAlso make sure to select \"My Mac\" as the device. Once that's selected, just click the play button or press Command + R on your keyboard.", + "title": "Run" + }, + { + "location": "/getting-started/xcode/#test", + "text": "To run your unit tests, select the scheme ending in -Package and press Command + U . Warning There may be a few extraneous schemes in the dropdown menu. Ignore them!", + "title": "Test" + }, + { + "location": "/getting-started/structure/", + "text": "Structure\n\n\nThis section explains the structure of a typical Vapor application to help get\nyou familiar with where things go.\n\n\nFolder Structure\n\n\nVapor's folder structure builds on top of \nSPM's folder structure\n.\n\n\n.\n\u251c\u2500\u2500 Public\n\u251c\u2500\u2500 Sources\n\u2502 \u251c\u2500\u2500 App\n\u2502 \u2502 \u251c\u2500\u2500 Controllers\n\u2502 \u2502 \u251c\u2500\u2500 Models\n\u2502 \u2502 \u251c\u2500\u2500 boot.swift\n\u2502 \u2502 \u251c\u2500\u2500 configure.swift\n\u2502 \u2502 \u2514\u2500\u2500 routes.swift\n\u2502 \u2514\u2500\u2500 Run\n\u2502 \u2514\u2500\u2500 main.swift\n\u251c\u2500\u2500 Tests\n\u2502 \u2514\u2500\u2500 AppTests\n\u2514\u2500\u2500 Package.swift\n\n\n\n\n\nLet's take a look at what each of these folders and files does.\n\n\nPublic\n\n\nThis folder contains any public files that will be served by your app.\nThis is usually images, style sheets, and browser scripts.\n\n\nWhenever Vapor responds to a request, it will first check if the requested\nitem is in this folder. If it is, it skips your application logic and returns\nthe file immediately.\n\n\nFor example, a request to \nlocalhost:8080/favicon.ico\n will check to see\nif \nPublic/favicon.ico\n exists. If it does, Vapor will return it.\n\n\nSources\n\n\nThis folder contains all of the Swift source files for your project. \nThe top level folders (\nApp\n and \nRun\n) reflect your package's modules, \nas declared in the \npackage manifest\n.\n\n\nApp\n\n\nThis is the most important folder in your application, it's where all of\nthe application logic goes!\n\n\nControllers\n\n\nControllers are great way of grouping together application logic. Most controllers\nhave many functions that accept a request and return some sort of response.\n\n\n\n\nTip\n\n\nVapor supports, but does not enforce the MVC pattern\n\n\n\n\nModels\n\n\nThe \nModels\n folder is a great place to store your \nContent\n structs or\nFluent \nModel\ns.\n\n\nboot.swift\n\n\nThis file contains a function that will be called \nafter\n your application has booted,\nbut \nbefore\n it has started running. This is a great place do things that should happen \nevery time your application starts.\n\n\nYou have access to the \nApplication\n here which you can use to create\nany \nservices\n you might need.\n\n\nconfigure.swift\n\n\nThis file contains a function that receives the config, environment, and services for your\napplication as input. This is a great place to make changes to your config or register \n\nservices\n to your application.\n\n\nroutes.swift\n\n\nThis file contains the main route collection for your app. This is where you should assign routes\nfor your controller methods.\n\n\nYou'll notice there's one example route in there that returns the \"hello, world\" response we saw earlier.\n\n\nYou can create as many route collections as you want to further organize your code. Just make sure\nto register them in this main route collection. \n\n\nTests\n\n\nEach non-executable module in your \nSources\n folder should have a corresponding \n...Tests\n folder.\n\n\nAppTests\n\n\nThis folder contains the unit tests for code in your \nApp\n module. \nLearn more about testing in \nTesting \n Getting Started\n.\n\n\nPackage.swift\n\n\nFinally is SPM's \npackage manifest\n.", + "title": "Folder Structure" + }, + { + "location": "/getting-started/structure/#structure", + "text": "This section explains the structure of a typical Vapor application to help get\nyou familiar with where things go.", + "title": "Structure" + }, + { + "location": "/getting-started/structure/#folder-structure", + "text": "Vapor's folder structure builds on top of SPM's folder structure . .\n\u251c\u2500\u2500 Public\n\u251c\u2500\u2500 Sources\n\u2502 \u251c\u2500\u2500 App\n\u2502 \u2502 \u251c\u2500\u2500 Controllers\n\u2502 \u2502 \u251c\u2500\u2500 Models\n\u2502 \u2502 \u251c\u2500\u2500 boot.swift\n\u2502 \u2502 \u251c\u2500\u2500 configure.swift\n\u2502 \u2502 \u2514\u2500\u2500 routes.swift\n\u2502 \u2514\u2500\u2500 Run\n\u2502 \u2514\u2500\u2500 main.swift\n\u251c\u2500\u2500 Tests\n\u2502 \u2514\u2500\u2500 AppTests\n\u2514\u2500\u2500 Package.swift Let's take a look at what each of these folders and files does.", + "title": "Folder Structure" + }, + { + "location": "/getting-started/structure/#public", + "text": "This folder contains any public files that will be served by your app.\nThis is usually images, style sheets, and browser scripts. Whenever Vapor responds to a request, it will first check if the requested\nitem is in this folder. If it is, it skips your application logic and returns\nthe file immediately. For example, a request to localhost:8080/favicon.ico will check to see\nif Public/favicon.ico exists. If it does, Vapor will return it.", + "title": "Public" + }, + { + "location": "/getting-started/structure/#sources", + "text": "This folder contains all of the Swift source files for your project. \nThe top level folders ( App and Run ) reflect your package's modules, \nas declared in the package manifest .", + "title": "Sources" + }, + { + "location": "/getting-started/structure/#app", + "text": "This is the most important folder in your application, it's where all of\nthe application logic goes!", + "title": "App" + }, + { + "location": "/getting-started/structure/#controllers", + "text": "Controllers are great way of grouping together application logic. Most controllers\nhave many functions that accept a request and return some sort of response. Tip Vapor supports, but does not enforce the MVC pattern", + "title": "Controllers" + }, + { + "location": "/getting-started/structure/#models", + "text": "The Models folder is a great place to store your Content structs or\nFluent Model s.", + "title": "Models" + }, + { + "location": "/getting-started/structure/#bootswift", + "text": "This file contains a function that will be called after your application has booted,\nbut before it has started running. This is a great place do things that should happen \nevery time your application starts. You have access to the Application here which you can use to create\nany services you might need.", + "title": "boot.swift" + }, + { + "location": "/getting-started/structure/#configureswift", + "text": "This file contains a function that receives the config, environment, and services for your\napplication as input. This is a great place to make changes to your config or register services to your application.", + "title": "configure.swift" + }, + { + "location": "/getting-started/structure/#routesswift", + "text": "This file contains the main route collection for your app. This is where you should assign routes\nfor your controller methods. You'll notice there's one example route in there that returns the \"hello, world\" response we saw earlier. You can create as many route collections as you want to further organize your code. Just make sure\nto register them in this main route collection.", + "title": "routes.swift" + }, + { + "location": "/getting-started/structure/#tests", + "text": "Each non-executable module in your Sources folder should have a corresponding ...Tests folder.", + "title": "Tests" + }, + { + "location": "/getting-started/structure/#apptests", + "text": "This folder contains the unit tests for code in your App module. \nLearn more about testing in Testing Getting Started .", + "title": "AppTests" + }, + { + "location": "/getting-started/structure/#packageswift", + "text": "Finally is SPM's package manifest .", + "title": "Package.swift" + }, + { + "location": "/getting-started/application/", + "text": "Application\n\n\nEvery Vapor project has an \nApplication\n. You use the application to create any services\nyou might need while developing.\n\n\nThe best place to access the application is in your project's \nboot.swift\n file.\n\n\nimport\n \nVapor\n\n\n\npublic\n \nfunc\n \nboot\n(\n_\n \napp\n:\n \nApplication\n)\n \nthrows\n \n{\n\n \n// your code here\n\n\n}\n\n\n\n\n\n\nYou can also access the application from your \nroutes.swift\n file. It's stored\nas a property there.\n\n\nimport\n \nVapor\n\n\n\nfinal\n \nclass\n \nRoutes\n:\n \nRouteCollection\n \n{\n\n \n...\n\n\n}\n\n\n\n\n\n\nUnlike some other web frameworks, Vapor doesn't support statically accessing the application.\nIf you need to access it from another class or struct, you should pass through a method or initializer.\n\n\n\n\nInfo\n\n\nAvoiding static access to variables helps make Vapor performant by preventing\nthe need for thread-safe locks or semaphores.\n\n\n\n\nServices\n\n\nThe application's main function is to make services. For example, you might need a \nBCryptHasher\n to hash\nsome passwords before storing them in a database. You can use the application to create one.\n\n\nimport\n \nBCrypt\n\n\n\nlet\n \nbcryptHasher\n \n=\n \ntry\n \napp\n.\nmake\n(\nBCryptHasher\n.\nself\n)\n\n\n\n\n\n\nOr you might use the application to create an HTTP client.\n\n\nlet\n \nclient\n \n=\n \ntry\n \napp\n.\nmake\n(\nClient\n.\nself\n)\n\n\nlet\n \nres\n \n=\n \nclient\n.\nget\n(\nhttp://vapor.codes\n)\n\n\n\n\n\n\nLearn more about services in \nGetting Started \n Services\n.", + "title": "Application" + }, + { + "location": "/getting-started/application/#application", + "text": "Every Vapor project has an Application . You use the application to create any services\nyou might need while developing. The best place to access the application is in your project's boot.swift file. import Vapor public func boot ( _ app : Application ) throws { \n // your code here } You can also access the application from your routes.swift file. It's stored\nas a property there. import Vapor final class Routes : RouteCollection { \n ... } Unlike some other web frameworks, Vapor doesn't support statically accessing the application.\nIf you need to access it from another class or struct, you should pass through a method or initializer. Info Avoiding static access to variables helps make Vapor performant by preventing\nthe need for thread-safe locks or semaphores.", + "title": "Application" + }, + { + "location": "/getting-started/application/#services", + "text": "The application's main function is to make services. For example, you might need a BCryptHasher to hash\nsome passwords before storing them in a database. You can use the application to create one. import BCrypt let bcryptHasher = try app . make ( BCryptHasher . self ) Or you might use the application to create an HTTP client. let client = try app . make ( Client . self ) let res = client . get ( http://vapor.codes ) Learn more about services in Getting Started Services .", + "title": "Services" + }, + { + "location": "/getting-started/controllers/", + "text": "Controllers\n\n\nControllers are a great way to organize your code. They are collections of methods that accept\na request and return a response.\n\n\nA good place to put your controllers is in the \nControllers\n folder.\n\n\nMethods\n\n\nLet's take a look at an example controller.\n\n\nimport\n \nVapor\n\n\n\nfinal\n \nclass\n \nHelloController\n \n{\n\n \nfunc\n \ngreet\n(\n_\n \nreq\n:\n \nRequest\n)\n \nthrows\n \n-\n \nString\n \n{\n\n \nreturn\n \nHello!\n\n \n}\n\n\n}\n\n\n\n\n\n\nController methods should always accept a \nRequest\n and return something \nResponseRepresentable\n.\nThis also includes \nfutures\n whose expectations are \nResponseRepresentable\n (i.e, \nFuture\nString\n).\n\n\nTo use this controller, we can simply initialize it, then pass the method to a router.\n\n\nlet\n \nhelloController\n \n=\n \nHelloController\n()\n\n\nrouter\n.\nget\n(\ngreet\n,\n \nuse\n:\n \nhelloController\n.\ngreet\n)\n\n\n\n\n\n\nUse Services\n\n\nYou will probably want to access your \napplication's services\n from within your controllers.\nLuckily this is easy to do. First, declare what services your controller needs in its init method. Then store them\nas properties on the controller.\n\n\nfinal\n \nclass\n \nHelloController\n \n{\n\n \nlet\n \nhasher\n:\n \nBCryptHasher\n\n\n \ninit\n(\nhasher\n:\n \nBCryptHasher\n)\n \n{\n\n \nself\n.\nhasher\n \n=\n \nhasher\n\n \n}\n\n\n \n...\n\n\n}\n\n\n\n\n\n\nNext, use the \napplication\n to create these services when you initialize your controller.\n\n\nlet\n \nhelloController\n \n=\n \ntry\n \nHelloController\n(\n\n \nhasher\n:\n \napp\n.\nmake\n()\n\n\n)", + "title": "Controllers" + }, + { + "location": "/getting-started/controllers/#controllers", + "text": "Controllers are a great way to organize your code. They are collections of methods that accept\na request and return a response. A good place to put your controllers is in the Controllers folder.", + "title": "Controllers" + }, + { + "location": "/getting-started/controllers/#methods", + "text": "Let's take a look at an example controller. import Vapor final class HelloController { \n func greet ( _ req : Request ) throws - String { \n return Hello! \n } } Controller methods should always accept a Request and return something ResponseRepresentable .\nThis also includes futures whose expectations are ResponseRepresentable (i.e, Future String ). To use this controller, we can simply initialize it, then pass the method to a router. let helloController = HelloController () router . get ( greet , use : helloController . greet )", + "title": "Methods" + }, + { + "location": "/getting-started/controllers/#use-services", + "text": "You will probably want to access your application's services from within your controllers.\nLuckily this is easy to do. First, declare what services your controller needs in its init method. Then store them\nas properties on the controller. final class HelloController { \n let hasher : BCryptHasher \n\n init ( hasher : BCryptHasher ) { \n self . hasher = hasher \n } \n\n ... } Next, use the application to create these services when you initialize your controller. let helloController = try HelloController ( \n hasher : app . make () )", + "title": "Use Services" + }, + { + "location": "/getting-started/routing/", + "text": "Routing\n\n\nRouting is the process of finding the appropriate response to an incoming request.\n\n\nMaking a Router\n\n\nIn Vapor the default Router is the \nEngineRouter\n. You can implement custom routers by implementing one conformant to the \nRouter\n protocol.\n\n\nlet\n \nrouter\n \n=\n \ntry\n \nEngineRouter\n.\ndefault\n()\n\n\n\n\n\n\nThere are two APIs available, one is supplied by the \nRouting\n library and a set of helpers is available in Vapor itself.\n\n\nWe recommend using the helpers and will continue to describe those here.\n\n\nRegistering a route\n\n\nImagine you want to return a list of users when someone visits \nGET /users\n.\nLeaving authorization on the side, that would look something like this.\n\n\nrouter\n.\nget\n(\nusers\n)\n \n{\n \nreq\n \nin\n\n \nreturn\n \n// fetch the users\n\n\n}\n\n\n\n\n\n\nIn Vapor, routing is usually done using the \n.get\n, \n.put\n, \n.post\n, \n.patch\n and \n.delete\n shorthands.\nYou can supply the path as \n/\n or comma-separated strings. We recommend comma separated, as it's more readable.\n\n\nrouter\n.\nget\n(\npath\n,\n \nto\n,\n \nsomething\n)\n \n{\n \n...\n \n}\n\n\n\n\n\n\nRoutes\n\n\nThe best place to add routes is in the \nroutes.swift\n file.\nYou will find a router there that is ready to use.\n\n\nimport\n \nVapor\n\n\n\nfinal\n \nclass\n \nRoutes\n:\n \nRouteCollection\n \n{\n\n \n...\n\n\n \nfunc\n \nboot\n(\nrouter\n:\n \nRouter\n)\n \nthrows\n \n{\n\n \nrouter\n.\nget\n(\nhello\n)\n \n{\n \nreq\n \nin\n\n \nreturn\n \nHello, world!\n\n \n}\n\n \n}\n\n\n}\n\n\n\n\n\n\nYou \nmust\n return a Future containing a \nResponseEncodable\n here.\nThe most common \nResponseEncodable\n types are \nContent\n, Response and \nView\n.\n\n\nParameters\n\n\nSometimes you may want one of the components of your route path to be dynamic. This is often used when\nyou want to get an item with a supplied identifier, i.e., \nGET /users/:id\n\n\nrouter\n.\nget\n(\nusers\n,\n \nInt\n.\nparameter\n)\n \n{\n \nreq\n \n-\n \nFuture\nString\n \nin\n\n \nlet\n \nid\n \n=\n \ntry\n \nreq\n.\nparameter\n(\nInt\n.\nself\n)\n\n \nreturn\n \n// fetch the user with id\n\n\n}\n\n\n\n\n\n\nInstead of passing a string, pass the \ntype\n of parameter you expect. In this case, our \nUser\n has an \nInt\n ID.\n\n\n\n\nTip\n\n\nYou can define your own \ncustom parameter types\n as well.\n\n\n\n\nAfter registering your routes\n\n\nAfter registering your routes you must register the Router as a \nService", + "title": "Routing" + }, + { + "location": "/getting-started/routing/#routing", + "text": "Routing is the process of finding the appropriate response to an incoming request.", + "title": "Routing" + }, + { + "location": "/getting-started/routing/#making-a-router", + "text": "In Vapor the default Router is the EngineRouter . You can implement custom routers by implementing one conformant to the Router protocol. let router = try EngineRouter . default () There are two APIs available, one is supplied by the Routing library and a set of helpers is available in Vapor itself. We recommend using the helpers and will continue to describe those here.", + "title": "Making a Router" + }, + { + "location": "/getting-started/routing/#registering-a-route", + "text": "Imagine you want to return a list of users when someone visits GET /users .\nLeaving authorization on the side, that would look something like this. router . get ( users ) { req in \n return // fetch the users } In Vapor, routing is usually done using the .get , .put , .post , .patch and .delete shorthands.\nYou can supply the path as / or comma-separated strings. We recommend comma separated, as it's more readable. router . get ( path , to , something ) { ... }", + "title": "Registering a route" + }, + { + "location": "/getting-started/routing/#routes", + "text": "The best place to add routes is in the routes.swift file.\nYou will find a router there that is ready to use. import Vapor final class Routes : RouteCollection { \n ... \n\n func boot ( router : Router ) throws { \n router . get ( hello ) { req in \n return Hello, world! \n } \n } } You must return a Future containing a ResponseEncodable here.\nThe most common ResponseEncodable types are Content , Response and View .", + "title": "Routes" + }, + { + "location": "/getting-started/routing/#parameters", + "text": "Sometimes you may want one of the components of your route path to be dynamic. This is often used when\nyou want to get an item with a supplied identifier, i.e., GET /users/:id router . get ( users , Int . parameter ) { req - Future String in \n let id = try req . parameter ( Int . self ) \n return // fetch the user with id } Instead of passing a string, pass the type of parameter you expect. In this case, our User has an Int ID. Tip You can define your own custom parameter types as well.", + "title": "Parameters" + }, + { + "location": "/getting-started/routing/#after-registering-your-routes", + "text": "After registering your routes you must register the Router as a Service", + "title": "After registering your routes" + }, + { + "location": "/getting-started/content/", + "text": "Content\n\n\nIn Vapor 3, all content types (JSON, protobuf, FormURLEncoded, Multipart, etc) are treated the same.\nAll you need to parse and serialize content is a \nCodable\n class or struct.\n\n\nFor this introduction, we will use JSON as an example. But keep in mind the API is the same for any supported content type.\n\n\nRequest\n\n\nLet's take a look at how you would parse the following HTTP request.\n\n\nPOST\n \n/login\n \nHTTP\n/\n1.1\n\n\nContent-Type\n:\n \napplication/json\n\n\n\n{\n\n \nemail\n:\n \nuser@vapor.codes\n,\n\n \npassword\n:\n \ndon\nt look!\n\n\n}\n\n\n\n\n\n\nDecode Request\n\n\nFirst, create a struct or class that represents the data you expect.\n\n\nimport\n \nFoundation\n\n\nimport\n \nVapor\n\n\n\nstruct\n \nLoginRequest\n:\n \nContent\n \n{\n\n \nvar\n \nemail\n:\n \nString\n\n \nvar\n \npassword\n:\n \nString\n\n\n}\n\n\n\n\n\n\nThen simply conform this struct or class to \nContent\n.\nNow we are ready to decode that HTTP request.\n\n\nrouter\n.\npost\n(\nlogin\n)\n \n{\n \nreq\n \n-\n \nResponse\n \nin\n\n \nlet\n \nloginRequest\n \n=\n \ntry\n \nreq\n.\ncontent\n.\ndecode\n(\nLoginRequest\n.\nself\n)\n\n\n \nprint\n(\nloginRequest\n.\nemail\n)\n \n// user@vapor.codes\n\n \nprint\n(\nloginRequest\n.\npassword\n)\n \n// don\nt look!\n\n\n \nreturn\n \nResponse\n(\nstatus\n:\n \n.\nok\n)\n\n\n}\n\n\n\n\n\n\nIt's that simple!\n\n\nOther Request Types\n\n\nSince the request in the previous example declared JSON as it's content type,\nVapor knows to use a JSON decoder automatically.\nThis same method would work just as well for the following request.\n\n\nPOST\n \n/login\n \nHTTP\n/\n1.1\n\n\nContent-Type\n:\n \napplication/x-www-form-urlencoded\n\n\nemail=user@vapor.codes\ndon\nt+look!\n\n\n\n\n\n\n\nTip\n\n\nYou can configure which encoders/decoders Vapor uses. Read on to learn more.\n\n\n\n\nResponse\n\n\nLet's take a look at how you would create the following HTTP response.\n\n\nHTTP\n/\n1.1\n \n200\n \nOK\n\n\nContent-Type\n:\n \napplication/json\n\n\n\n{\n\n \nname\n:\n \nVapor User\n,\n\n \nemail\n:\n \nuser@vapor.codes\n\n\n}\n\n\n\n\n\n\nEncode Response\n\n\nJust like decoding, first create a struct or class that represents the data your expect.\n\n\nimport\n \nFoundation\n\n\nimport\n \nVapor\n\n\n\nstruct\n \nUser\n:\n \nContent\n \n{\n\n \nvar\n \nname\n:\n \nString\n\n \nvar\n \nemail\n:\n \nString\n\n\n}\n\n\n\n\n\n\nThen just conform this struct or class to \nContent\n.\nNow we are ready to encode that HTTP response.\n\n\nrouter\n.\nget\n(\nuser\n)\n \n{\n \nreq\n \n-\n \nResponse\n \nin\n\n \nlet\n \nuser\n \n=\n \nUser\n(\n\n \nname\n:\n \nVapor User\n,\n\n \nemail\n:\n \nuser@vapor.codes\n\n \n)\n\n\n \nlet\n \nres\n \n=\n \nResponse\n(\nstatus\n:\n \n.\nok\n)\n\n \ntry\n \nres\n.\ncontent\n.\nencode\n(\nuser\n,\n \nas\n:\n \n.\njson\n)\n\n \nreturn\n \nres\n\n\n}\n\n\n\n\n\n\nOther Response Types\n\n\nContent will automatically encode as JSON by default. You can always override which content type is used\nusing the \nas:\n parameter.\n\n\ntry\n \nres\n.\ncontent\n.\nencode\n(\nuser\n,\n \nas\n:\n \n.\nformURLEncoded\n)\n\n\n\n\n\n\nYou can also change the default media type for any class or struct.\n\n\nstruct\n \nUser\n:\n \nContent\n \n{\n\n \n/// See Content.defaultMediaType\n\n \nstatic\n \nlet\n \ndefaultMediaType\n:\n \nMediaType\n \n=\n \n.\nformURLEncoded\n\n\n \n...\n\n\n}\n\n\n\n\n\n\nConfiguring Content\n\n\nComing soon.", + "title": "Content" + }, + { + "location": "/getting-started/content/#content", + "text": "In Vapor 3, all content types (JSON, protobuf, FormURLEncoded, Multipart, etc) are treated the same.\nAll you need to parse and serialize content is a Codable class or struct. For this introduction, we will use JSON as an example. But keep in mind the API is the same for any supported content type.", + "title": "Content" + }, + { + "location": "/getting-started/content/#request", + "text": "Let's take a look at how you would parse the following HTTP request. POST /login HTTP / 1.1 Content-Type : application/json { \n email : user@vapor.codes , \n password : don t look! }", + "title": "Request" + }, + { + "location": "/getting-started/content/#decode-request", + "text": "First, create a struct or class that represents the data you expect. import Foundation import Vapor struct LoginRequest : Content { \n var email : String \n var password : String } Then simply conform this struct or class to Content .\nNow we are ready to decode that HTTP request. router . post ( login ) { req - Response in \n let loginRequest = try req . content . decode ( LoginRequest . self ) \n\n print ( loginRequest . email ) // user@vapor.codes \n print ( loginRequest . password ) // don t look! \n\n return Response ( status : . ok ) } It's that simple!", + "title": "Decode Request" + }, + { + "location": "/getting-started/content/#other-request-types", + "text": "Since the request in the previous example declared JSON as it's content type,\nVapor knows to use a JSON decoder automatically.\nThis same method would work just as well for the following request. POST /login HTTP / 1.1 Content-Type : application/x-www-form-urlencoded \n\nemail=user@vapor.codes don t+look! Tip You can configure which encoders/decoders Vapor uses. Read on to learn more.", + "title": "Other Request Types" + }, + { + "location": "/getting-started/content/#response", + "text": "Let's take a look at how you would create the following HTTP response. HTTP / 1.1 200 OK Content-Type : application/json { \n name : Vapor User , \n email : user@vapor.codes }", + "title": "Response" + }, + { + "location": "/getting-started/content/#encode-response", + "text": "Just like decoding, first create a struct or class that represents the data your expect. import Foundation import Vapor struct User : Content { \n var name : String \n var email : String } Then just conform this struct or class to Content .\nNow we are ready to encode that HTTP response. router . get ( user ) { req - Response in \n let user = User ( \n name : Vapor User , \n email : user@vapor.codes \n ) \n\n let res = Response ( status : . ok ) \n try res . content . encode ( user , as : . json ) \n return res }", + "title": "Encode Response" + }, + { + "location": "/getting-started/content/#other-response-types", + "text": "Content will automatically encode as JSON by default. You can always override which content type is used\nusing the as: parameter. try res . content . encode ( user , as : . formURLEncoded ) You can also change the default media type for any class or struct. struct User : Content { \n /// See Content.defaultMediaType \n static let defaultMediaType : MediaType = . formURLEncoded \n\n ... }", + "title": "Other Response Types" + }, + { + "location": "/getting-started/content/#configuring-content", + "text": "Coming soon.", + "title": "Configuring Content" + }, + { + "location": "/getting-started/futures/", + "text": "Futures\n\n\nYou may have noticed some APIs in Vapor expect or return a \nFuture\nT\n type.\nIf this is your first time hearing about futures, they might seem a little confusing at first.\nBut don't worry, Vapor makes them easy to use.\n\n\nPromises and Futures are two strongly related types. Every promise has a future.\nA promise is a write-only entity that has the ability to complete (or fail) it's Future counterpart.\n\n\nFutures are a read-only entity that can have a successful or error case. Successful cases are called the \"Expectation\".\n\n\nFutures can be used to register callbacks to, which will always executed in the order of registration. Promises can only be completed once. If a promise is completed more than once the input will be \nignored\n.\n\n\nBasics\n\n\nCreating a promise is when the result is returned in the future at an unknown time.\nFor the sake of demonstration, however, the promise will be completed at a predefined point in time and execution.\n\n\nWithin the \n.do\n block you may not throw an error or return a result.\n\n\nlet\n \npromise\n \n=\n \nPromise\nString\n()\n\n\nlet\n \nfuture\n \n=\n \npromise\n.\nfuture\n \n// Future\nString\n\n\n\nfuture\n.\ndo\n \n{\n \nstring\n \nin\n\n \nprint\n(\nstring\n)\n\n\n}\n\n\n\npromise\n.\ncomplete\n(\nHello\n)\n\n\n\n\n\n\nThe above code prints \"Hello\" in the console.\n\n\nErrors\n\n\nWhen running the above code, you may have noticed a warning pop up. This is because the \n.do\n block only handles successful completions. If we were to replace the completion with the following code the \n.do\n block would never get run:\n\n\nstruct\n \nMyError\n:\n \nError\n \n{}\n\n\n\npromise\n.\nfail\n(\nMyError\n())\n\n\n\n\n\n\nInstead, a \n.catch\n block will be triggered.\n\n\nfuture\n.\ndo\n \n{\n \nstring\n \nin\n\n \nprint\n(\nstring\n)\n\n\n}.\ncatch\n \n{\n \nerror\n \nin\n\n \nprint\n(\nError \n\\(\nerror\n)\n occurred\n)\n\n\n}\n\n\n\n\n\n\nIn this scenario the test \"Error 'MyError' occurred\" will appear.\n\n\nWithin the \n.catch\n block you may not throw an error or return a result.\n\n\nBasic Transformations\n\n\nTransformations are one of the more critical parts of Vapor 3's future system. They assist in reducing the complexity of futures and keep code isolated and readable. You can use the \n.map\n function to transform the future expectation to another future of the same or a different type. You need to explicitly state which type will be returned in the mapping closure.\n\n\nThe mapping closure(s) will \nonly\n be executed if an expectation has been received in the previous step. If at any point a transformation function throws an error, execution stops there and the \n.catch\n block will be executed.\n\n\nIf the promise that was mapped failed to begin with, the \n.catch\n block will also be executed \nwithout\n triggering \nany\n mapping closures.\n\n\nlet\n \npromise\n \n=\n \nPromise\nInt\n()\n\n\n\npromise\n.\nfuture\n.\ndo\n \n{\n \nint\n \nin\n\n \nprint\n(\nint\n)\n\n\n}.\nmap\n(\nto\n:\n \nInt\n.\nself\n)\n \n{\n \nint\n \nin\n\n \nreturn\n \nint\n \n+\n \n4\n\n\n}.\nmap\n(\nto\n:\n \nString\n.\nself\n)\n \n{\n \nint\n \nin\n\n \nreturn\n \nint\n.\ndescription\n\n\n}.\ndo\n \n{\n \nstring\n \nin\n\n \nprint\n(\nstring\n)\n\n\n}.\ncatch\n \n{\n \nerror\n \nin\n\n \nprint\n(\nError \n\\(\nerror\n)\n occurred\n)\n\n\n}\n\n\n\npromise\n.\ncomplete\n(\n3\n)\n\n\n\n\n\n\nThe above code will print the inputted integer. Then map the input to \n(integer + 4) == 7\n.\nThen the textual representation of the integer is returned as a \nString\n which will be printed.\n\n\nThis results in the following console output:\n\n\n3\n\n\n7\n\n\n\n\n\n\nRecursive futures\n\n\nIn the above \nmap\n function we returned a new result synchronously. In some situations, however, you'll need to dispatch another asynchronous call based on the result of a previous call.\n\n\nFirst, let's see how this would work out using \nmap\n by exaggerating synchronous code as if it were an asynchronous call.\n\n\n\n\nWarning\n\n\nDo not use this implementation, use the next one instead. This is an unnecessarily complicated way of nesting futures.\n\n\n\n\nlet\n \npromise\n \n=\n \nPromise\nInt\n()\n\n\n\npromise\n.\nmap\n(\nto\n:\n \nFuture\nInt\n.\nself\n)\n \n{\n \nint\n \nin\n\n \nreturn\n \nFuture\n(\nint\n \n+\n \n4\n)\n\n\n}.\nmap\n(\nto\n:\n \nFuture\nFuture\nString\n.\nself\n)\n \n{\n \nfutureInt\n \nin\n\n \nreturn\n \nfutureInt\n.\nmap\n(\nto\n:\n \nFuture\nString\n.\nself\n)\n \n{\n \nint\n \nin\n\n \nreturn\n \nFuture\n(\nint\n.\ndescription\n)\n\n \n}\n\n\n}.\ndo\n \n{\n \ndoubleFutureString\n \nin\n \n// Future\nFuture\nString\n\n \ndoubleFutureString\n.\ndo\n \n{\n \nfutureString\n \nin\n \n// Future\nString\n\n \nfutureString\n.\ndo\n \n{\n \nstring\n \nin\n\n \nprint\n(\nstring\n)\n\n \n}.\ncatch\n \n{\n \nerror\n \nin\n\n \nprint\n(\nError \n\\(\nerror\n)\n occurred\n)\n\n \n}\n\n \n}.\ncatch\n \n{\n \nerror\n \nin\n\n \nprint\n(\nError \n\\(\nerror\n)\n occurred\n)\n\n \n}\n\n\n}.\ncatch\n \n{\n \nerror\n \nin\n\n \nprint\n(\nError \n\\(\nerror\n)\n occurred\n)\n\n\n}\n\n\n\npromise\n.\ncomplete\n(\n3\n)\n\n\n\n\n\n\nTo flatten this asynchronous recursion, instead, we recommend using \nflatMap\n.\nThe type supplied in the \nto:\n argument is implied to be wrapped in a \nFuture\n.\n\n\nlet\n \npromise\n \n=\n \nPromise\nInt\n()\n\n\n\npromise\n.\nflatMap\n(\nto\n:\n \nInt\n.\nself\n)\n \n{\n \nint\n \nin\n\n \nreturn\n \nFuture\nInt\n(\nint\n \n+\n \n4\n)\n\n\n}.\nflatMap\n(\nto\n:\n \nString\n.\nself\n)\n \n{\n \nint\n \nin\n\n \nreturn\n \nFuture\n(\nint\n.\ndescription\n)\n\n\n}.\ndo\n \n{\n \nstring\n \nin\n\n \nprint\n(\nstring\n)\n\n\n}.\ncatch\n \n{\n \nerror\n \nin\n\n \nprint\n(\nError \n\\(\nerror\n)\n occurred\n)\n\n\n}\n\n\n\npromise\n.\ncomplete\n(\n3\n)\n\n\n\n\n\n\nAlways\n\n\nSometimes you want to always execute a function as part of the cleanup phase.\nYou can use the \n.always\n block to execute a block of code after the future has been successfully executed (and mapped if applicable) or when an error occurs. Please do consider that \nalways\n also will be executed in the order in which it has been registered, like all other closures.\n\n\nvar\n \ni\n \n=\n \n0\n\n\n\nlet\n \npromise\n \n=\n \nPromise\nInt\n()\n\n\nlet\n \nfuture\n \n=\n \npromise\n.\nfuture\n \n// Future\nInt\n\n\n\nfuture\n.\ndo\n \n{\n \nint\n \nin\n\n \ni\n \n+=\n \nint\n \n*\n \n3\n\n\n}.\ndo\n \n{\n \nint\n \nin\n\n \ni\n \n+=\n \n(\nint\n \n-\n \n1\n)\n\n\n}.\nalways\n \n{\n\n \nprint\n(\ni\n)\n\n \ni\n \n=\n \n0\n\n\n}.\ncatch\n \n{\n \n_\n \nin\n\n \ni\n \n=\n \n-\n1\n\n\n}\n\n\n\n\n\n\nAt the end of the above function, \ni\n will \nalways\n be 0. If the promise is completed with the successful result \ni\n, the number \"11\" will be printed. On error, \"-1\" will be printed.\n\n\nSignals\n\n\nSignals, or \nFuture\nVoid\n is a Future that can contain either an Error or Void (the Expectation). \nFuture\nVoid\n is often used to indicate the successful or unsuccessful completion of a task.", + "title": "Futures" + }, + { + "location": "/getting-started/futures/#futures", + "text": "You may have noticed some APIs in Vapor expect or return a Future T type.\nIf this is your first time hearing about futures, they might seem a little confusing at first.\nBut don't worry, Vapor makes them easy to use. Promises and Futures are two strongly related types. Every promise has a future.\nA promise is a write-only entity that has the ability to complete (or fail) it's Future counterpart. Futures are a read-only entity that can have a successful or error case. Successful cases are called the \"Expectation\". Futures can be used to register callbacks to, which will always executed in the order of registration. Promises can only be completed once. If a promise is completed more than once the input will be ignored .", + "title": "Futures" + }, + { + "location": "/getting-started/futures/#basics", + "text": "Creating a promise is when the result is returned in the future at an unknown time.\nFor the sake of demonstration, however, the promise will be completed at a predefined point in time and execution. Within the .do block you may not throw an error or return a result. let promise = Promise String () let future = promise . future // Future String future . do { string in \n print ( string ) } promise . complete ( Hello ) The above code prints \"Hello\" in the console.", + "title": "Basics" + }, + { + "location": "/getting-started/futures/#errors", + "text": "When running the above code, you may have noticed a warning pop up. This is because the .do block only handles successful completions. If we were to replace the completion with the following code the .do block would never get run: struct MyError : Error {} promise . fail ( MyError ()) Instead, a .catch block will be triggered. future . do { string in \n print ( string ) }. catch { error in \n print ( Error \\( error ) occurred ) } In this scenario the test \"Error 'MyError' occurred\" will appear. Within the .catch block you may not throw an error or return a result.", + "title": "Errors" + }, + { + "location": "/getting-started/futures/#basic-transformations", + "text": "Transformations are one of the more critical parts of Vapor 3's future system. They assist in reducing the complexity of futures and keep code isolated and readable. You can use the .map function to transform the future expectation to another future of the same or a different type. You need to explicitly state which type will be returned in the mapping closure. The mapping closure(s) will only be executed if an expectation has been received in the previous step. If at any point a transformation function throws an error, execution stops there and the .catch block will be executed. If the promise that was mapped failed to begin with, the .catch block will also be executed without triggering any mapping closures. let promise = Promise Int () promise . future . do { int in \n print ( int ) }. map ( to : Int . self ) { int in \n return int + 4 }. map ( to : String . self ) { int in \n return int . description }. do { string in \n print ( string ) }. catch { error in \n print ( Error \\( error ) occurred ) } promise . complete ( 3 ) The above code will print the inputted integer. Then map the input to (integer + 4) == 7 .\nThen the textual representation of the integer is returned as a String which will be printed. This results in the following console output: 3 7", + "title": "Basic Transformations" + }, + { + "location": "/getting-started/futures/#recursive-futures", + "text": "In the above map function we returned a new result synchronously. In some situations, however, you'll need to dispatch another asynchronous call based on the result of a previous call. First, let's see how this would work out using map by exaggerating synchronous code as if it were an asynchronous call. Warning Do not use this implementation, use the next one instead. This is an unnecessarily complicated way of nesting futures. let promise = Promise Int () promise . map ( to : Future Int . self ) { int in \n return Future ( int + 4 ) }. map ( to : Future Future String . self ) { futureInt in \n return futureInt . map ( to : Future String . self ) { int in \n return Future ( int . description ) \n } }. do { doubleFutureString in // Future Future String \n doubleFutureString . do { futureString in // Future String \n futureString . do { string in \n print ( string ) \n }. catch { error in \n print ( Error \\( error ) occurred ) \n } \n }. catch { error in \n print ( Error \\( error ) occurred ) \n } }. catch { error in \n print ( Error \\( error ) occurred ) } promise . complete ( 3 ) To flatten this asynchronous recursion, instead, we recommend using flatMap .\nThe type supplied in the to: argument is implied to be wrapped in a Future . let promise = Promise Int () promise . flatMap ( to : Int . self ) { int in \n return Future Int ( int + 4 ) }. flatMap ( to : String . self ) { int in \n return Future ( int . description ) }. do { string in \n print ( string ) }. catch { error in \n print ( Error \\( error ) occurred ) } promise . complete ( 3 )", + "title": "Recursive futures" + }, + { + "location": "/getting-started/futures/#always", + "text": "Sometimes you want to always execute a function as part of the cleanup phase.\nYou can use the .always block to execute a block of code after the future has been successfully executed (and mapped if applicable) or when an error occurs. Please do consider that always also will be executed in the order in which it has been registered, like all other closures. var i = 0 let promise = Promise Int () let future = promise . future // Future Int future . do { int in \n i += int * 3 }. do { int in \n i += ( int - 1 ) }. always { \n print ( i ) \n i = 0 }. catch { _ in \n i = - 1 } At the end of the above function, i will always be 0. If the promise is completed with the successful result i , the number \"11\" will be printed. On error, \"-1\" will be printed.", + "title": "Always" + }, + { + "location": "/getting-started/futures/#signals", + "text": "Signals, or Future Void is a Future that can contain either an Error or Void (the Expectation). Future Void is often used to indicate the successful or unsuccessful completion of a task.", + "title": "Signals" + }, + { + "location": "/getting-started/services/", + "text": "Services\n\n\nServices is a framework for creating things you need in your application in a type-safe fashion with protocol and environment support.\n\n\nThe Services framework is designed to be thread unsafe. The framework aims to guarantee that a service exists on the same EventLoop it was created from and will be used on.\n\n\nContainer\n\n\nContainers are event loops that can create and cache services.\n\n\nRequest is the most common \nContainer\n type, which can be accessed in every \nRoute\n.\n\n\nContainers cache instances of a given service (keyed by the requested protocol) on a per-container basis.\n\n\n\n\nAny given container has its own cache. No two containers will ever share a service instance, whether singleton or not.\n\n\nA singleton service is chosen and cached only by which interface(s) it supports and the service tag.\nThere will only ever be one instance of a singleton service per-container, regardless of what requested it.\n\n\nA normal service is chosen and cached by which interface(s) it supports, the service tag, and the requesting client interface.\nThere will be as many instances of a normal service per-container as there are unique clients requesting it.\n(Remembering that clients are also interface types, not instances - that's the \nfor:\n parameter to \n.make()\n)\n\n\n\n\nEphemeralContainer\n\n\nEphemeralContainers are containers that are short-lived. Their cache does not stretch beyond a short lifecycle. The most common EphemeralContainer is an HTTP Request which lives for the duration of the route handler.\n\n\nEnvironment\n\n\nEnvironments indicate the type of deployment/situation in which an application is ran. Environments can be used to change database credentials or API tokens per environment automatically.\n\n\nService\n\n\nServices are a type that can be requested from a Container. They are registered as part of the application setup.\n\n\nServices are registered to a matching type or protocol it can represent, including it's own concrete type.\n\n\nServices are registered to a blueprint before the \nApplication\n is initialized. Together they make up the blueprint that Containers use to create an individual Service.\n\n\nRegistering\n\n\nServices are registered as a concrete (singleton) type or factories. Singleton types should be a struct, but can be a class.\n\n\nTo create an empty list of Services you can call the initializer without parameters\n\n\nvar\n \nservices\n \n=\n \nServices\n()\n\n\n\n\n\n\nThe Vapor framework has a default setup with the most common (and officially supported) Services already registered.\n\n\nvar\n \nservices\n \n=\n \nServices\n.\ndefault\n()\n\n\n\n\n\n\nConcrete implementations\n\n\nA common use case for registering a struct is for registering configurations.\nVapor 3 configurations are \nalways\n a concrete struct type. Registering a concrete type is simple:\n\n\nstruct\n \nEmptyService\n \n{}\n\n\n\nservices\n.\ninstance\n(\nEmptyService\n())\n\n\n\n\n\n\nSingletons\n\n\nSingleton services (which declare themselves, or were registered, as such) are cached on a per-container basis, but the singleton cache ignores which Client is requesting the service (whereas the normal cache does not).\n\n\nSingleton classes \nmust\n be thread-safe to prevent crashes. If you want your class to be a singleton type (across all threads):\n\n\nfinal\n \nclass\n \nSingletonService\n \n{\n\n \ninit\n()\n \n{}\n\n\n}\n\n\n\nservices\n.\ninstance\n(\nSingletonService\n())\n\n\n\n\n\n\nAssuming the above service, you can now make this service from a container. The global container in Vapor is \nApplication\n which \nmust not\n be used within routes.\n\n\nlet\n \napp\n \n=\n \ntry\n \nApplication\n(\nservices\n:\n \nservices\n)\n\n\nlet\n \nemptyService\n \n=\n \napp\n.\nmake\n(\nEmptyService\n.\nself\n)\n\n\n\n\n\n\nProtocol conforming services\n\n\nOften times when registering a service is conforms to one or more protocols for which it can be used. This is one of the more widely used use cases for Services.\n\n\nenum\n \nLevel\n \n{\n\n \ncase\n \nverbose\n,\n \nerror\n\n\n}\n\n\n\nprotocol\n \nLogger\n \n{\n\n \nfunc\n \nlog\n(\n_\n \nmessage\n:\n \nString\n,\n \nlevel\n:\n \nLevel\n)\n\n\n}\n\n\n\nstruct\n \nPrintLogger\n:\n \nLogger\n \n{\n\n \ninit\n()\n \n{}\n\n\n \nfunc\n \nlog\n(\n_\n \nmessage\n:\n \nString\n,\n \nlevel\n:\n \nLevel\n)\n \n{\n\n \nprint\n(\nmessage\n)\n\n \n}\n\n\n}\n\n\n\nservices\n.\ninstance\n(\nLogger\n.\nself\n,\n \nPrintLogger\n())\n\n\n\n\n\n\nThe above can be combined with \nisSingleton: true\n\n\nRegistering multiple conformances\n\n\nA single type can conform to multiple protocols, and you might want to register a single service for all those conforming situations.\n\n\nprotocol\n \nConsole\n \n{\n\n \nfunc\n \nwrite\n(\n_\n \nmessage\n:\n \nString\n,\n \ncolor\n:\n \nAnsiColor\n)\n\n\n}\n\n\n\nstruct\n \nPrintConsole\n:\n \nConsole\n,\n \nLogger\n \n{\n\n \nfunc\n \nwrite\n(\n_\n \nmessage\n:\n \nString\n,\n \ncolor\n:\n \nAnsiColor\n)\n \n{\n\n \nprint\n(\nmessage\n)\n\n \n}\n\n\n \nfunc\n \nlog\n(\n_\n \nmessage\n:\n \nString\n,\n \nlevel\n:\n \nLevel\n)\n \n{\n\n \nprint\n(\nmessage\n)\n\n \n}\n\n\n \ninit\n()\n \n{}\n\n\n}\n\n\n\nservices\n.\ninstance\n(\n\n \nsupports\n:\n \n[\nLogger\n.\nself\n,\n \nConsole\n.\nself\n],\n\n \nErrorLogger\n()\n\n\n)\n\n\n\n\n\n\nRegistering for a specific requester\n\n\nSometimes, the implementation should change depending on the user. A database connector might need to run over a VPN tunnel, redis might use an optimized local loopback whilst the default implementation is a normal TCP socket.\n\n\nOther times, you simply want to change the log destination depending on the type that's logging (such as logging HTTP errors differently from database errors).\n\n\nThis comes in useful when changing configurations per situation, too.\n\n\nstruct\n \nVerboseLogger\n:\n \nLogger\n \n{\n\n \ninit\n()\n \n{}\n\n\n \nfunc\n \nlog\n(\n_\n \nmessage\n:\n \nString\n,\n \nlevel\n:\n \nLevel\n)\n \n{\n\n \nprint\n(\nmessage\n)\n\n \n}\n\n\n}\n\n\n\nstruct\n \nErrorLogger\n:\n \nLogger\n \n{\n\n \ninit\n()\n \n{}\n\n\n \nfunc\n \nlog\n(\n_\n \nmessage\n:\n \nString\n,\n \nlevel\n:\n \nLevel\n)\n \n{\n\n \nif\n \nlevel\n \n==\n \n.\nerror\n \n{\n\n \nprint\n(\nmessage\n)\n\n \n}\n\n \n}\n\n\n}\n\n\n\n// Only log errors\n\n\nservices\n.\ninstance\n(\nLogger\n.\nself\n,\n \nErrorLogger\n())\n\n\n\n// Except the router, do log not found errors verbosely\n\n\nservices\n.\ninstance\n(\nLogger\n.\nself\n,\n \nPrintLogger\n(),\n \nfor\n:\n \nRouter\n.\nself\n)\n\n\n\n\n\n\nFactorized services\n\n\nSome services have dependencies. An extremly useful use case is TLS, where the implementation is separated from the protocol. This allows users to create a TLS socket to connect to another host without relying on a specific implementation. Vapor uses this to better integrate with the operating system by changing the default TLS implementation from OpenSSL on Linux to the Transport Security Framework on macOS and iOS.\n\n\nFactorized services get access to the event loop to factorize dependencies.\n\n\nservices\n.\nregister\n \n{\n \ncontainer\n \n-\n \nGithubClient\n \nin\n\n \n// Create an HTTP client for our GithubClient\n\n \nlet\n \nclient\n \n=\n \ntry\n \ncontainer\n.\nmake\n(\nClient\n.\nself\n,\n \nfor\n:\n \nGithubClient\n.\nself\n)\n\n \ntry\n \nclient\n.\nconnect\n(\nhostname\n:\n \ngithub.com\n,\n \nssl\n:\n \ntrue\n)\n\n\n \nreturn\n \nGithubClient\n(\nusing\n:\n \nclient\n)\n\n\n}\n\n\n\n\n\n\nPlease do note that we explicitly stated that the \nGithubClient\n requests an (HTTP) Client. We recommend doing this at all times, so that you leave configuration options open.\n\n\nEnvironments\n\n\nVapor 3 supports (custom) environments. By default we recommend (and support) the \n.production\n, \n.development\n and \n.testing\n environments.\n\n\nYou can create a custom environment type as \n.custom(\nmy-environment-name\n)\n.\n\n\nlet\n \nenvironment\n \n=\n \nEnvironment\n.\ncustom\n(\nstaging\n)\n\n\n\n\n\n\nContainers give access to the current environment, so libraries may change behaviour depending on the environment.\n\n\nChanging configurations per environment\n\n\nFor easy of development, some parameters may and should change for easy of debugging.\nPassword hashes can be made intentionally weaker in development scenarios to compensate for debug compilation performance, or API tokens may change to the correct one for your environment.\n\n\nservices\n.\nregister\n \n{\n \ncontainer\n \n-\n \nBCryptConfig\n \nin\n\n \nlet\n \ncost\n:\n \nInt\n\n\n \nswitch\n \ncontainer\n.\nenvironment\n \n{\n\n \ncase\n \n.\nproduction\n:\n\n \ncost\n \n=\n \n12\n\n \ndefault\n:\n\n \ncost\n \n=\n \n4\n\n \n}\n\n\n \nreturn\n \nBCryptConfig\n(\ncost\n:\n \ncost\n)\n\n\n}\n\n\n\n\n\n\nGetting a Service\n\n\nTo get a service you need an existing container matching the current EventLoop.\nIf you're processing a Request, you should almost always use the Request as a Container type.\n\n\n// ErrorLogger\n\n\nlet\n \nerrorLogger\n \n=\n \nmyContainerType\n.\nmake\n(\nLogger\n.\nself\n,\n \nfor\n:\n \nRequest\n.\nself\n)\n\n\n\n// PrintLogger\n\n\nlet\n \nprintLogger\n \n=\n \nmyContainerType\n.\nmake\n(\nLogger\n.\nself\n,\n \nfor\n:\n \nRouter\n.\nself\n)", + "title": "Services" + }, + { + "location": "/getting-started/services/#services", + "text": "Services is a framework for creating things you need in your application in a type-safe fashion with protocol and environment support. The Services framework is designed to be thread unsafe. The framework aims to guarantee that a service exists on the same EventLoop it was created from and will be used on.", + "title": "Services" + }, + { + "location": "/getting-started/services/#container", + "text": "Containers are event loops that can create and cache services. Request is the most common Container type, which can be accessed in every Route . Containers cache instances of a given service (keyed by the requested protocol) on a per-container basis. Any given container has its own cache. No two containers will ever share a service instance, whether singleton or not. A singleton service is chosen and cached only by which interface(s) it supports and the service tag.\nThere will only ever be one instance of a singleton service per-container, regardless of what requested it. A normal service is chosen and cached by which interface(s) it supports, the service tag, and the requesting client interface.\nThere will be as many instances of a normal service per-container as there are unique clients requesting it.\n(Remembering that clients are also interface types, not instances - that's the for: parameter to .make() )", + "title": "Container" + }, + { + "location": "/getting-started/services/#ephemeralcontainer", + "text": "EphemeralContainers are containers that are short-lived. Their cache does not stretch beyond a short lifecycle. The most common EphemeralContainer is an HTTP Request which lives for the duration of the route handler.", + "title": "EphemeralContainer" + }, + { + "location": "/getting-started/services/#environment", + "text": "Environments indicate the type of deployment/situation in which an application is ran. Environments can be used to change database credentials or API tokens per environment automatically.", + "title": "Environment" + }, + { + "location": "/getting-started/services/#service", + "text": "Services are a type that can be requested from a Container. They are registered as part of the application setup. Services are registered to a matching type or protocol it can represent, including it's own concrete type. Services are registered to a blueprint before the Application is initialized. Together they make up the blueprint that Containers use to create an individual Service.", + "title": "Service" + }, + { + "location": "/getting-started/services/#registering", + "text": "Services are registered as a concrete (singleton) type or factories. Singleton types should be a struct, but can be a class. To create an empty list of Services you can call the initializer without parameters var services = Services () The Vapor framework has a default setup with the most common (and officially supported) Services already registered. var services = Services . default ()", + "title": "Registering" + }, + { + "location": "/getting-started/services/#concrete-implementations", + "text": "A common use case for registering a struct is for registering configurations.\nVapor 3 configurations are always a concrete struct type. Registering a concrete type is simple: struct EmptyService {} services . instance ( EmptyService ())", + "title": "Concrete implementations" + }, + { + "location": "/getting-started/services/#singletons", + "text": "Singleton services (which declare themselves, or were registered, as such) are cached on a per-container basis, but the singleton cache ignores which Client is requesting the service (whereas the normal cache does not). Singleton classes must be thread-safe to prevent crashes. If you want your class to be a singleton type (across all threads): final class SingletonService { \n init () {} } services . instance ( SingletonService ()) Assuming the above service, you can now make this service from a container. The global container in Vapor is Application which must not be used within routes. let app = try Application ( services : services ) let emptyService = app . make ( EmptyService . self )", + "title": "Singletons" + }, + { + "location": "/getting-started/services/#protocol-conforming-services", + "text": "Often times when registering a service is conforms to one or more protocols for which it can be used. This is one of the more widely used use cases for Services. enum Level { \n case verbose , error } protocol Logger { \n func log ( _ message : String , level : Level ) } struct PrintLogger : Logger { \n init () {} \n\n func log ( _ message : String , level : Level ) { \n print ( message ) \n } } services . instance ( Logger . self , PrintLogger ()) The above can be combined with isSingleton: true", + "title": "Protocol conforming services" + }, + { + "location": "/getting-started/services/#registering-multiple-conformances", + "text": "A single type can conform to multiple protocols, and you might want to register a single service for all those conforming situations. protocol Console { \n func write ( _ message : String , color : AnsiColor ) } struct PrintConsole : Console , Logger { \n func write ( _ message : String , color : AnsiColor ) { \n print ( message ) \n } \n\n func log ( _ message : String , level : Level ) { \n print ( message ) \n } \n\n init () {} } services . instance ( \n supports : [ Logger . self , Console . self ], \n ErrorLogger () )", + "title": "Registering multiple conformances" + }, + { + "location": "/getting-started/services/#registering-for-a-specific-requester", + "text": "Sometimes, the implementation should change depending on the user. A database connector might need to run over a VPN tunnel, redis might use an optimized local loopback whilst the default implementation is a normal TCP socket. Other times, you simply want to change the log destination depending on the type that's logging (such as logging HTTP errors differently from database errors). This comes in useful when changing configurations per situation, too. struct VerboseLogger : Logger { \n init () {} \n\n func log ( _ message : String , level : Level ) { \n print ( message ) \n } } struct ErrorLogger : Logger { \n init () {} \n\n func log ( _ message : String , level : Level ) { \n if level == . error { \n print ( message ) \n } \n } } // Only log errors services . instance ( Logger . self , ErrorLogger ()) // Except the router, do log not found errors verbosely services . instance ( Logger . self , PrintLogger (), for : Router . self )", + "title": "Registering for a specific requester" + }, + { + "location": "/getting-started/services/#factorized-services", + "text": "Some services have dependencies. An extremly useful use case is TLS, where the implementation is separated from the protocol. This allows users to create a TLS socket to connect to another host without relying on a specific implementation. Vapor uses this to better integrate with the operating system by changing the default TLS implementation from OpenSSL on Linux to the Transport Security Framework on macOS and iOS. Factorized services get access to the event loop to factorize dependencies. services . register { container - GithubClient in \n // Create an HTTP client for our GithubClient \n let client = try container . make ( Client . self , for : GithubClient . self ) \n try client . connect ( hostname : github.com , ssl : true ) \n\n return GithubClient ( using : client ) } Please do note that we explicitly stated that the GithubClient requests an (HTTP) Client. We recommend doing this at all times, so that you leave configuration options open.", + "title": "Factorized services" + }, + { + "location": "/getting-started/services/#environments", + "text": "Vapor 3 supports (custom) environments. By default we recommend (and support) the .production , .development and .testing environments. You can create a custom environment type as .custom( my-environment-name ) . let environment = Environment . custom ( staging ) Containers give access to the current environment, so libraries may change behaviour depending on the environment.", + "title": "Environments" + }, + { + "location": "/getting-started/services/#changing-configurations-per-environment", + "text": "For easy of development, some parameters may and should change for easy of debugging.\nPassword hashes can be made intentionally weaker in development scenarios to compensate for debug compilation performance, or API tokens may change to the correct one for your environment. services . register { container - BCryptConfig in \n let cost : Int \n\n switch container . environment { \n case . production : \n cost = 12 \n default : \n cost = 4 \n } \n\n return BCryptConfig ( cost : cost ) }", + "title": "Changing configurations per environment" + }, + { + "location": "/getting-started/services/#getting-a-service", + "text": "To get a service you need an existing container matching the current EventLoop.\nIf you're processing a Request, you should almost always use the Request as a Container type. // ErrorLogger let errorLogger = myContainerType . make ( Logger . self , for : Request . self ) // PrintLogger let printLogger = myContainerType . make ( Logger . self , for : Router . self )", + "title": "Getting a Service" + }, + { + "location": "/getting-started/cloud/", + "text": "Deployment\n\n\nDeploying code is the process of making your Vapor project publically available. \nIt can be one of the most difficult aspects of web development. Fortunately, there\nare services to help.\n\n\nVapor Cloud\n\n\nThe best way to deploy your application is through Vapor Cloud. It's a cloud platform built\nspecifically for the Vapor web framework. This means it's incredibly easy to deploy your\nproject quickly and be confident that it will be fast and stable.\n\n\nDeploying your project to Vapor Cloud is simple, it's built right into the \nVapor Toolbox\n.\nJust run this command from within the root directory of your project.\n\n\nvapor cloud deploy\n\n\n\n\n\nFor a detailed guide, visit \nVapor Cloud \n Quick Start\n.\n\n\nOther Options\n\n\nVapor can be deployed anywhere that supports Ubuntu (basically everywhere). To learn more about\ndeploying your code, checkout \nDeploy \n Getting Started", + "title": "Deployment" + }, + { + "location": "/getting-started/cloud/#deployment", + "text": "Deploying code is the process of making your Vapor project publically available. \nIt can be one of the most difficult aspects of web development. Fortunately, there\nare services to help.", + "title": "Deployment" + }, + { + "location": "/getting-started/cloud/#vapor-cloud", + "text": "The best way to deploy your application is through Vapor Cloud. It's a cloud platform built\nspecifically for the Vapor web framework. This means it's incredibly easy to deploy your\nproject quickly and be confident that it will be fast and stable. Deploying your project to Vapor Cloud is simple, it's built right into the Vapor Toolbox .\nJust run this command from within the root directory of your project. vapor cloud deploy For a detailed guide, visit Vapor Cloud Quick Start .", + "title": "Vapor Cloud" + }, + { + "location": "/getting-started/cloud/#other-options", + "text": "Vapor can be deployed anywhere that supports Ubuntu (basically everywhere). To learn more about\ndeploying your code, checkout Deploy Getting Started", + "title": "Other Options" + }, + { + "location": "/routing/getting-started/", + "text": "Basics\n\n\nIn Vapor the default Router is the \nEngineRouter\n. You can implement custom routers by implementing one conformant to the \nRouter\n protocol.\n\n\nlet\n \nrouter\n \n=\n \ntry\n \nEngineRouter\n.\ndefault\n()\n\n\n\n\n\n\nThere are two APIs available, one is supplied by the \nRouting\n library and a set of helpers is available in Vapor itself.\n\n\nRegistering a route using Routing\n\n\nThe \non\n function on a \nAsyncRouter\n registers a route to the provided path. The following registers a \nGET /hello/world\n route.\n\n\nIt responds with \n\"Hello world!\"\n using futures.\n\n\nrouter\n.\non\n(.\nget\n,\n \nto\n:\n \nhello\n,\n \nworld\n)\n \n{\n \nrequest\n \nin\n\n \nreturn\n \ntry\n \nResponse\n(\nbody\n:\n \nHello world!\n)\n \n\n}\n\n\n\n\n\n\nThe \n.get\n represents the HTTP method you want to use. \nto: \"hello\", \"world\"\n registers the path \n/hello/world\n.\n\n\nFor variable path components you can use \nparameters\n.\n\n\nThe trailing closure receives a request. The route can throw errors and needs to return a future response conforming type.\n\n\nRegistering a route using Vapor\n\n\nIn Vapor we add support for routes using the \n.get\n, \n.put\n, \n.post\n, \n.patch\n and \n.delete\n shorthands.\n\n\nFor variable path components you can use \nparameters\n here, too.\n\n\nVapor has an added benefit here in that you can return the \nResponse\n itself in addition to \nFuture\nResponseRepresentable\n or \nFuture\nResponse\n.\n\n\nrouter\n.\nget\n(\ncomponents\n,\n \nin\n,\n \npath\n)\n \n{\n \nrequest\n \nin\n\n \nreturn\n \nResponse\n(\nstatus\n:\n \n.\nok\n)\n\n\n}\n\n\n\n\n\n\nAfter registering your routes\n\n\nAfter registering routes to the Router, you must add the router to your services.\n\n\nservices\n.\nregister\n(\nrouter\n,\n \nas\n:\n \nRouter\n.\nself\n)\n\n\n\n\n\n\nLearn more about services in \nGetting Started \n Services", + "title": "Getting Started" + }, + { + "location": "/routing/getting-started/#basics", + "text": "In Vapor the default Router is the EngineRouter . You can implement custom routers by implementing one conformant to the Router protocol. let router = try EngineRouter . default () There are two APIs available, one is supplied by the Routing library and a set of helpers is available in Vapor itself.", + "title": "Basics" + }, + { + "location": "/routing/getting-started/#registering-a-route-using-routing", + "text": "The on function on a AsyncRouter registers a route to the provided path. The following registers a GET /hello/world route. It responds with \"Hello world!\" using futures. router . on (. get , to : hello , world ) { request in \n return try Response ( body : Hello world! ) } The .get represents the HTTP method you want to use. to: \"hello\", \"world\" registers the path /hello/world . For variable path components you can use parameters . The trailing closure receives a request. The route can throw errors and needs to return a future response conforming type.", + "title": "Registering a route using Routing" + }, + { + "location": "/routing/getting-started/#registering-a-route-using-vapor", + "text": "In Vapor we add support for routes using the .get , .put , .post , .patch and .delete shorthands. For variable path components you can use parameters here, too. Vapor has an added benefit here in that you can return the Response itself in addition to Future ResponseRepresentable or Future Response . router . get ( components , in , path ) { request in \n return Response ( status : . ok ) }", + "title": "Registering a route using Vapor" + }, + { + "location": "/routing/getting-started/#after-registering-your-routes", + "text": "After registering routes to the Router, you must add the router to your services. services . register ( router , as : Router . self ) Learn more about services in Getting Started Services", + "title": "After registering your routes" + }, + { + "location": "/routing/parameters/", + "text": "Parameters\n\n\nParameters are a registered type that can be initialized from a String.\n\n\nThey can be part of a \nRoute\n, and be extracted from requests that are called in that route.\n\n\nCreating custom parameters\n\n\nTo create a custom parameter type, simply conform to \nParameter\n and implement the conversion function \nmake\n and a unique slug.\n\n\nIn this example, the \nUser\n class will be initialized from a parameter that represents it's identifier.\n\n\nWe recommend prefixing custom Parameter identifiers.\n\n\nclass\n \nUser\n \n:\n \nParameter\n \n{\n\n \nvar\n \nusername\n:\n \nString\n\n\n \n// The unique (prefixed) identifier for this type\n\n \nstatic\n \nvar\n \nuniqueSlug\n \n=\n \nmy-app:user\n\n\n \n// Creates a new user from the raw `parameter`\n\n \nstatic\n \nfunc\n \nmake\n(\nfor\n \nparameter\n:\n \nString\n,\n \nin\n \nrequest\n:\n \nRequest\n)\n \nthrows\n \n-\n \nUser\n \n{\n\n \nreturn\n \nUser\n(\nnamed\n:\n \nparameter\n)\n\n \n}\n\n\n \ninit\n(\nnamed\n \nusername\n:\n \nString\n)\n \n{\n\n \nself\n.\nusername\n \n=\n \nusername\n\n \n}\n\n\n}\n\n\n\n\n\n\nUsing (custom) parameters\n\n\nAfter conforming a type to \nParameter\n you can access it's static property \nparameter\n as part of a path.\n\n\nrouter\n.\non\n(.\nget\n,\n \nto\n:\n \nusers\n,\n \nUser\n.\nparameter\n,\n \nprofile\n)\n \n{\n \nrequest\n \nin\n\n \nlet\n \nuser\n \n=\n \ntry\n \nrequest\n.\nparameters\n.\nnext\n(\nUser\n.\nself\n)\n\n\n \n// Return the user\ns Profile sync or async (depending on the router)\n\n\n}", + "title": "Parameters" + }, + { + "location": "/routing/parameters/#parameters", + "text": "Parameters are a registered type that can be initialized from a String. They can be part of a Route , and be extracted from requests that are called in that route.", + "title": "Parameters" + }, + { + "location": "/routing/parameters/#creating-custom-parameters", + "text": "To create a custom parameter type, simply conform to Parameter and implement the conversion function make and a unique slug. In this example, the User class will be initialized from a parameter that represents it's identifier. We recommend prefixing custom Parameter identifiers. class User : Parameter { \n var username : String \n\n // The unique (prefixed) identifier for this type \n static var uniqueSlug = my-app:user \n\n // Creates a new user from the raw `parameter` \n static func make ( for parameter : String , in request : Request ) throws - User { \n return User ( named : parameter ) \n } \n\n init ( named username : String ) { \n self . username = username \n } }", + "title": "Creating custom parameters" + }, + { + "location": "/routing/parameters/#using-custom-parameters", + "text": "After conforming a type to Parameter you can access it's static property parameter as part of a path. router . on (. get , to : users , User . parameter , profile ) { request in \n let user = try request . parameters . next ( User . self ) \n\n // Return the user s Profile sync or async (depending on the router) }", + "title": "Using (custom) parameters" + }, + { + "location": "/routing/route/", + "text": "Route\n\n\nRoute is an object that contains the essential information of an HTTP Route.\n\n\nIt contains the route's Method, path components and responder.\n\n\nExtensions\n\n\nRoutes are Extensible using the \nextend\n property. This allow storing additional data for use by integrating libraries.\n\n\nThe purpose is to allow tools (such as documentation tools) to hook into the Vapor routing process.", + "title": "Route" + }, + { + "location": "/routing/route/#route", + "text": "Route is an object that contains the essential information of an HTTP Route. It contains the route's Method, path components and responder.", + "title": "Route" + }, + { + "location": "/routing/route/#extensions", + "text": "Routes are Extensible using the extend property. This allow storing additional data for use by integrating libraries. The purpose is to allow tools (such as documentation tools) to hook into the Vapor routing process.", + "title": "Extensions" + }, + { + "location": "/routing/router/", + "text": "Router\n\n\nRouter is a protocol that you can conform your own routers to.\n\n\nRegistering a route\n\n\nFirst, create a \nRoute\n using a HTTP method, path and a responder.\n\n\nThe following example shows a route with a constant path.\n\n\nlet\n \nresponder\n \n=\n \nBasicAsyncResponder\n \n{\n \nrequest\n \nin\n\n \nreturn\n \nHello world\n\n\n}\n\n\n\nlet\n \nroute\n \n=\n \nRoute\n(\nmethod\n:\n \n.\nget\n,\n \npath\n:\n \n[.\nconstant\n(\nhello\n),\n \n.\nconstant\n(\nworld\n)],\n \nresponder\n:\n \nresponder\n)\n\n\n\n\n\n\nThe following example shows a with a \nParameter\n:\n\n\nlet\n \nresponder\n \n=\n \nBasicSyncResponder\n \n{\n \nrequest\n \nin\n\n \nlet\n \nname\n \n=\n \ntry\n \nrequest\n.\nparameters\n.\nnext\n(\nString\n.\nself\n)\n\n \nreturn\n \nHello \n\\(\nname\n)\n\n\n}\n\n\n\nlet\n \nroute\n \n=\n \nRoute\n(\nmethod\n:\n \n.\nget\n,\n \npath\n:\n \n[.\nconstant\n(\ngreet\n),\n \n.\nparameter\n(\nString\n.\nself\n)],\n \nresponder\n:\n \nresponder\n)\n\n\n\n\n\n\nRouting a request through a Router\n\n\nAssuming you have a request, like the following example:\n\n\nlet\n \nrequest\n \n=\n \nRequest\n(\nmethod\n:\n \n.\nget\n,\n \nURI\n(\npath\n:\n \n/hello/world\n))\n\n\n\n\n\n\nThe router should be able to route the HTTP request using\n\n\nlet\n \nresponder\n \n=\n \nrouter\n.\nroute\n(\nrequest\n:\n \nrequest\n)", + "title": "Router" + }, + { + "location": "/routing/router/#router", + "text": "Router is a protocol that you can conform your own routers to.", + "title": "Router" + }, + { + "location": "/routing/router/#registering-a-route", + "text": "First, create a Route using a HTTP method, path and a responder. The following example shows a route with a constant path. let responder = BasicAsyncResponder { request in \n return Hello world } let route = Route ( method : . get , path : [. constant ( hello ), . constant ( world )], responder : responder ) The following example shows a with a Parameter : let responder = BasicSyncResponder { request in \n let name = try request . parameters . next ( String . self ) \n return Hello \\( name ) } let route = Route ( method : . get , path : [. constant ( greet ), . parameter ( String . self )], responder : responder )", + "title": "Registering a route" + }, + { + "location": "/routing/router/#routing-a-request-through-a-router", + "text": "Assuming you have a request, like the following example: let request = Request ( method : . get , URI ( path : /hello/world )) The router should be able to route the HTTP request using let responder = router . route ( request : request )", + "title": "Routing a request through a Router" + }, + { + "location": "/fluent/getting-started/", + "text": "Getting Started with Fluent\n\n\nFluent (\nvapor/fluent\n) is a type-safe, fast, and easy-to-use ORM framework built for Swift.\nIt takes advantage of Swift's strong type system to provide an elegant foundation for building database integrations.\n\n\nDatabase\n\n\nFluent is just a framework for building ORMs, not an ORM itself. To get started using Fluent, pick one of the databases below.\n\n\n\n\n\n\n\n\ndatabase\n\n\nlibrary\n\n\ndriver\n\n\nguide\n\n\n\n\n\n\n\n\n\n\nPostgreSQL\n\n\nvapor/postgresql\n\n\nvapor/fluent-postgresql\n\n\nGetting Started \n\n\n\n\n\n\nMySQL\n\n\nvapor/mysql\n\n\nvapor/fluent-mysql\n\n\nGetting Started \n\n\n\n\n\n\nSQLite\n\n\nvapor/sqlite\n\n\nvapor/fluent-sqlite\n\n\nGetting Started \n\n\n\n\n\n\n\n\nAfter you get started, come back to the other sections in this guide for a more in-depth look at Fluent's features.\n\n\n\n\nTip\n\n\nYou can also search GitHub for the tag \nfluent-database\n for a full list of official and third-party Fluent databases.", + "title": "Getting Started" + }, + { + "location": "/fluent/getting-started/#getting-started-with-fluent", + "text": "Fluent ( vapor/fluent ) is a type-safe, fast, and easy-to-use ORM framework built for Swift.\nIt takes advantage of Swift's strong type system to provide an elegant foundation for building database integrations.", + "title": "Getting Started with Fluent" + }, + { + "location": "/fluent/getting-started/#database", + "text": "Fluent is just a framework for building ORMs, not an ORM itself. To get started using Fluent, pick one of the databases below. database library driver guide PostgreSQL vapor/postgresql vapor/fluent-postgresql Getting Started MySQL vapor/mysql vapor/fluent-mysql Getting Started SQLite vapor/sqlite vapor/fluent-sqlite Getting Started After you get started, come back to the other sections in this guide for a more in-depth look at Fluent's features. Tip You can also search GitHub for the tag fluent-database for a full list of official and third-party Fluent databases.", + "title": "Database" + }, + { + "location": "/fluent/models/", + "text": "Getting Started with Models\n\n\nModels are the heart of Fluent. Unlike ORMs in other languages, Fluent doesn't return untyped\narrays or dictionaries for queries. Instead, you query the database using models. This allows the\nSwift compiler to catch many errors that have burdened ORM users for ages.\n\n\nIn this guide, we will cover the creation of a basic \nUser\n model.\n\n\nClass\n\n\nEvery Fluent model starts with a \nCodable\n class. You can make any \nCodable\n class a Fluent model,\neven ones that come from a different module. All you have to do is conform to \nModel\n.\n\n\nimport\n \nFoundation\n\n\nimport\n \nVapor\n\n\n\nfinal\n \nclass\n \nUser\n:\n \nContent\n \n{\n\n \nvar\n \nid\n:\n \nUUID\n?\n\n \nvar\n \nname\n:\n \nString\n\n \nvar\n \nage\n:\n \nInt\n\n\n}\n\n\n\n\n\n\nAlthough it's not necessary, adding \nfinal\n to your Swift classes can make them more performant\nand also make adding \ninit\n methods in extensions easier.\n\n\nConforming to Model\n\n\nNow that we have our \nUser\n class, let's conform it to \nModel\n.\n\n\nimport\n \nFluentMySQL\n\n\n\nextension\n \nUser\n:\n \nModel\n \n{\n\n\n\n}\n\n\n\n\n\n\nOnce you add this conformance requirement, Swift will tell you that it does not yet conform.\nLet's add the necessary items to make \nUser\n conform to model.\n\n\n\n\nTip\n\n\nWe recommend adding \nModel\n conformance in an extension to help keep your code clean.\n\n\n\n\nDatabase\n\n\nThe first step to conforming to \nModel\n is to let Fluent know which type of database you plan\non using this model with. This allows Fluent to enable database-specific features wherever you\nuse this model.\n\n\nimport\n \nFluentMySQL\n\n\n\nextension\n \nUser\n:\n \nModel\n \n{\n\n \n...\n\n\n \n/// See Model.Database\n\n \ntypealias\n \nDatabase\n \n=\n \nMySQLDatabase\n\n\n}\n\n\n\n\n\n\nID\n\n\nNow we can tell Fluent what type of ID this model uses. In this example, our \nUser\n model\nhas an ID property of type \nUUID\n named \nid\n.\n\n\nimport\n \nFluentMySQL\n\n\nimport\n \nFoundation\n\n\n\nextension\n \nUser\n:\n \nModel\n \n{\n\n \n...\n\n\n \n/// See Model.ID\n\n \ntypealias\n \nID\n \n=\n \nUUID\n\n\n \n/// See Model.idKey\n\n \nstatic\n \nvar\n \nidKey\n:\n \nIDKey\n \n{\n\n \nreturn\n \n\\\n.\nid\n\n \n}\n\n\n}\n\n\n\n\n\n\nYou can use any type that conforms to \nIDType\n as a Fluent ID. You can also use any property name you'd like for the id.\n\n\n\n\nWarning\n\n\nSome databases require certain ID keys. For example, MongoDB requires \n_id\n.\n\n\n\n\nExample\n\n\nWe now have a fully-conformed Fluent model!\n\n\nimport\n \nFluentMySQL\n\n\nimport\n \nFoundation\n\n\nimport\n \nVapor\n\n\n\nfinal\n \nclass\n \nUser\n:\n \nCodable\n \n{\n\n \nvar\n \nid\n:\n \nUUID\n?\n\n \nvar\n \nname\n:\n \nString\n\n \nvar\n \nage\n:\n \nInt\n\n\n}\n\n\n\nextension\n \nUser\n:\n \nModel\n \n{\n\n \n/// See Model.Database\n\n \ntypealias\n \nDatabase\n \n=\n \nMySQLDatabase\n\n\n \n/// See Model.ID\n\n \ntypealias\n \nID\n \n=\n \nUUID\n\n\n \n/// See Model.idKey\n\n \nstatic\n \nvar\n \nidKey\n:\n \nIDKey\n \n{\n\n \nreturn\n \n\\\n.\nid\n\n \n}\n\n\n}\n\n\n\n\n\n\nDone\n\n\nNow that you have a working Fluent model, you can move onto \nquerying\n your model.\nHowever, if your database uses schemas, you may need to create a \nmigration\n for your model first.", + "title": "Models" + }, + { + "location": "/fluent/models/#getting-started-with-models", + "text": "Models are the heart of Fluent. Unlike ORMs in other languages, Fluent doesn't return untyped\narrays or dictionaries for queries. Instead, you query the database using models. This allows the\nSwift compiler to catch many errors that have burdened ORM users for ages. In this guide, we will cover the creation of a basic User model.", + "title": "Getting Started with Models" + }, + { + "location": "/fluent/models/#class", + "text": "Every Fluent model starts with a Codable class. You can make any Codable class a Fluent model,\neven ones that come from a different module. All you have to do is conform to Model . import Foundation import Vapor final class User : Content { \n var id : UUID ? \n var name : String \n var age : Int } Although it's not necessary, adding final to your Swift classes can make them more performant\nand also make adding init methods in extensions easier.", + "title": "Class" + }, + { + "location": "/fluent/models/#conforming-to-model", + "text": "Now that we have our User class, let's conform it to Model . import FluentMySQL extension User : Model { } Once you add this conformance requirement, Swift will tell you that it does not yet conform.\nLet's add the necessary items to make User conform to model. Tip We recommend adding Model conformance in an extension to help keep your code clean.", + "title": "Conforming to Model" + }, + { + "location": "/fluent/models/#database", + "text": "The first step to conforming to Model is to let Fluent know which type of database you plan\non using this model with. This allows Fluent to enable database-specific features wherever you\nuse this model. import FluentMySQL extension User : Model { \n ... \n\n /// See Model.Database \n typealias Database = MySQLDatabase }", + "title": "Database" + }, + { + "location": "/fluent/models/#id", + "text": "Now we can tell Fluent what type of ID this model uses. In this example, our User model\nhas an ID property of type UUID named id . import FluentMySQL import Foundation extension User : Model { \n ... \n\n /// See Model.ID \n typealias ID = UUID \n\n /// See Model.idKey \n static var idKey : IDKey { \n return \\ . id \n } } You can use any type that conforms to IDType as a Fluent ID. You can also use any property name you'd like for the id. Warning Some databases require certain ID keys. For example, MongoDB requires _id .", + "title": "ID" + }, + { + "location": "/fluent/models/#example", + "text": "We now have a fully-conformed Fluent model! import FluentMySQL import Foundation import Vapor final class User : Codable { \n var id : UUID ? \n var name : String \n var age : Int } extension User : Model { \n /// See Model.Database \n typealias Database = MySQLDatabase \n\n /// See Model.ID \n typealias ID = UUID \n\n /// See Model.idKey \n static var idKey : IDKey { \n return \\ . id \n } }", + "title": "Example" + }, + { + "location": "/fluent/models/#done", + "text": "Now that you have a working Fluent model, you can move onto querying your model.\nHowever, if your database uses schemas, you may need to create a migration for your model first.", + "title": "Done" + }, + { + "location": "/fluent/migrations/", + "text": "Getting Started with Migrations\n\n\nMigrations are a way of making organized, testable, and reliable changes to your database's structure--\neven while it's in production!\n\n\nMigrations are often used for preparing a database schema for your models. However, they can also be used to \nmake normal queries to your database.\n\n\nIn this guide we will cover creating both types of migrations.\n\n\nModel Schema\n\n\nLet's take a look at how we can prepare a schema supporting database to accept the \n\nUser\n model from the \nprevious section\n.\n\n\nJust like we did with the \nModel\n protocol, we will conform our \nUser\n to \nMigration\n.\n\n\nimport\n \nFluent\n\n\n\nextension\n \nUser\n:\n \nMigration\n \n{\n\n\n\n}\n\n\n\n\n\n\nSwift will inform us that \nUser\n does not yet conform. Let's add the required methods!\n\n\nPrepare\n\n\nThe first method to implement is \nprepare\n. This method is where you make any of your \ndesired changes to the database.\n\n\nFor our \nUser\n model, we simply want to create a table that can store one or more users. To do this,\nwe will use the \n.create(...)\n function on the supplied database connection.\n\n\nextension\n \nUser\n:\n \nMigration\n \n{\n\n \n/// See Migration.prepare\n\n \nstatic\n \nfunc\n \nprepare\n(\non\n \nconnection\n:\n \nMySQLConnection\n)\n \n-\n \nFuture\nVoid\n \n{\n\n \nreturn\n \nconnection\n.\ncreate\n(\nself\n)\n \n{\n \nbuilder\n \nin\n\n \ntry\n \nbuilder\n.\nfield\n(\nfor\n:\n \n\\\n.\nid\n)\n\n \ntry\n \nbuilder\n.\nfield\n(\nfor\n:\n \n\\\n.\nname\n)\n\n \ntry\n \nbuilder\n.\nfield\n(\nfor\n:\n \n\\\n.\nage\n)\n\n \n}\n\n \n}\n\n\n}\n\n\n\n\n\n\nWe pass \nself\n (shorthand for \nUser.self\n since this is a static method) as the first argument to the \n.create\n method. This indicates\nto Fluent that we would like to create a schema for the \nUser\n model.\n\n\nNext, we pass a closure that accepts a \nSchemaBuilder\n for our \nUser\n model.\nWe can then call \n.field\non this builder to describe what fields we'd like our table to have.\n\n\nSince we are passing key paths to our \nUser\n model (indicated by \n\\.\n), Fluent can see what type those properties are.\nFor most common types (\nString\n, \nInt\n, \nDouble\n, etc) Fluent will automatically be able to determine the best\ndatabase field type to use.\n\n\nYou can also choose to manually select which database field type to use for a given field.\n\n\ntry\n \nbuilder\n.\nfield\n(\ntype\n:\n \n.\ntext\n,\n \nfor\n:\n \n\\\n.\nname\n)\n\n\n\n\n\n\nLearn more about creating, updating, and deleting schemas in \nFluent \n Schema Builder\n.\n\n\nRevert\n\n\nRevert is the opposite of prepare. It's job is to undo anything that was done in prepare. It is used when you boot your \napp with the \n--revert\n option. \n\n\nTo implement \nrevert\n for our model, we simply use \n.delete\n to indicate that we would like to delete the schema created for \nUser\n.\n\n\nextension\n \nUser\n:\n \nMigration\n \n{\n\n \n/// See Migration.revert\n\n \nstatic\n \nfunc\n \nrevert\n(\non\n \nconnection\n:\n \nMySQLConnection\n)\n \n-\n \nFuture\nVoid\n \n{\n\n \nreturn\n \nconnection\n.\ndelete\n(\nself\n)\n\n \n}\n\n\n}\n\n\n\n\n\n\nExample\n\n\nWe now have a fully functioning model with migration!\n\n\nextension\n \nTestUser\n:\n \nMigration\n \n{\n\n \n/// See Migration.prepare\n\n \nstatic\n \nfunc\n \nprepare\n(\non\n \nconnection\n:\n \nSQLiteConnection\n)\n \n-\n \nFuture\nVoid\n \n{\n\n \nreturn\n \nconnection\n.\ncreate\n(\nself\n)\n \n{\n \nbuilder\n \nin\n\n \ntry\n \nbuilder\n.\nfield\n(\nfor\n:\n \n\\\n.\nid\n)\n\n \ntry\n \nbuilder\n.\nfield\n(\nfor\n:\n \n\\\n.\nname\n)\n\n \ntry\n \nbuilder\n.\nfield\n(\nfor\n:\n \n\\\n.\nage\n)\n\n \n}\n\n \n}\n\n\n \n/// See Migration.revert\n\n \nstatic\n \nfunc\n \nrevert\n(\non\n \nconnection\n:\n \nSQLiteConnection\n)\n \n-\n \nFuture\nVoid\n \n{\n\n \nreturn\n \nconnection\n.\ndelete\n(\nself\n)\n\n \n}\n\n\n}\n\n\n\n\n\n\nDone\n\n\nNow that you have a working Fluent model and migration, you can move onto \nquerying\n your model.", + "title": "Migrations" + }, + { + "location": "/fluent/migrations/#getting-started-with-migrations", + "text": "Migrations are a way of making organized, testable, and reliable changes to your database's structure--\neven while it's in production! Migrations are often used for preparing a database schema for your models. However, they can also be used to \nmake normal queries to your database. In this guide we will cover creating both types of migrations.", + "title": "Getting Started with Migrations" + }, + { + "location": "/fluent/migrations/#model-schema", + "text": "Let's take a look at how we can prepare a schema supporting database to accept the User model from the previous section . Just like we did with the Model protocol, we will conform our User to Migration . import Fluent extension User : Migration { } Swift will inform us that User does not yet conform. Let's add the required methods!", + "title": "Model Schema" + }, + { + "location": "/fluent/migrations/#prepare", + "text": "The first method to implement is prepare . This method is where you make any of your \ndesired changes to the database. For our User model, we simply want to create a table that can store one or more users. To do this,\nwe will use the .create(...) function on the supplied database connection. extension User : Migration { \n /// See Migration.prepare \n static func prepare ( on connection : MySQLConnection ) - Future Void { \n return connection . create ( self ) { builder in \n try builder . field ( for : \\ . id ) \n try builder . field ( for : \\ . name ) \n try builder . field ( for : \\ . age ) \n } \n } } We pass self (shorthand for User.self since this is a static method) as the first argument to the .create method. This indicates\nto Fluent that we would like to create a schema for the User model. Next, we pass a closure that accepts a SchemaBuilder for our User model.\nWe can then call .field on this builder to describe what fields we'd like our table to have. Since we are passing key paths to our User model (indicated by \\. ), Fluent can see what type those properties are.\nFor most common types ( String , Int , Double , etc) Fluent will automatically be able to determine the best\ndatabase field type to use. You can also choose to manually select which database field type to use for a given field. try builder . field ( type : . text , for : \\ . name ) Learn more about creating, updating, and deleting schemas in Fluent Schema Builder .", + "title": "Prepare" + }, + { + "location": "/fluent/migrations/#revert", + "text": "Revert is the opposite of prepare. It's job is to undo anything that was done in prepare. It is used when you boot your \napp with the --revert option. To implement revert for our model, we simply use .delete to indicate that we would like to delete the schema created for User . extension User : Migration { \n /// See Migration.revert \n static func revert ( on connection : MySQLConnection ) - Future Void { \n return connection . delete ( self ) \n } }", + "title": "Revert" + }, + { + "location": "/fluent/migrations/#example", + "text": "We now have a fully functioning model with migration! extension TestUser : Migration { \n /// See Migration.prepare \n static func prepare ( on connection : SQLiteConnection ) - Future Void { \n return connection . create ( self ) { builder in \n try builder . field ( for : \\ . id ) \n try builder . field ( for : \\ . name ) \n try builder . field ( for : \\ . age ) \n } \n } \n\n /// See Migration.revert \n static func revert ( on connection : SQLiteConnection ) - Future Void { \n return connection . delete ( self ) \n } }", + "title": "Example" + }, + { + "location": "/fluent/migrations/#done", + "text": "Now that you have a working Fluent model and migration, you can move onto querying your model.", + "title": "Done" + }, + { + "location": "/fluent/querying/", + "text": "Querying Models\n\n\nOnce you have a \nmodel\n (and optionally a \nmigration\n) you can start\nquerying your database to create, read, update, and delete data.\n\n\nConnection\n\n\nThe first thing you need to query your database, is a connection to it. Luckily, they are easy to get.\n\n\nYou can use either the application or an incoming request to create a database connection. You just need\naccess to the database identifier.\n\n\nRequest\n\n\nThe preferred method for getting access to a database connection is via an incoming request.\n\n\nrouter\n.\nget\n(...)\n \n{\n \nreq\n \nin\n\n \nreturn\n \nreq\n.\nwithConnection\n(\nto\n:\n \n.\nfoo\n)\n \n{\n \ndb\n \nin\n\n \n// use the db here\n\n \n}\n\n\n}\n\n\n\n\n\n\nThe first parameter is the database's identifier. The second parameter is a closure\nthat accepts a connection to that database.\n\n\n\n\nTip\n\n\nAlthough the closure to \n.withConnection(to: ...)\n accepts a database \nconnection\n, we often use just \ndb\n for short.\n\n\n\n\nThe closure is expected to return a \nFuture\nVoid\n. When this future is completed, the connection will be released\nback into Fluent's connection pool. This is usually acheived by simply returning the query as we will soon see.\n\n\nApplication\n\n\nYou can also create a database connection using the application. This is useful for cases where you must access\nthe database from outside a request/response event.\n\n\nlet\n \nres\n \n=\n \napp\n.\nwithConnection\n(\nto\n:\n \n.\nfoo\n)\n \n{\n \ndb\n \nin\n\n \n// use the db here\n\n\n}\n\n\nprint\n(\nres\n)\n \n// Future\nT\n\n\n\n\n\n\nThis is usually done in the \nboot section\n of your application.\n\n\n\n\nWarning\n\n\nDo not use database connections created by the application in a route closure (when responding to a request).\nAlways use the incoming request to create a connection to avoid threading issues.\n\n\n\n\nCreate\n\n\nTo create (save) a model to the database, first initialize an instance of your model, then call \n.save(on: )\n.\n\n\nrouter\n.\npost\n(...)\n \n{\n \nreq\n \nin\n\n \nreturn\n \nreq\n.\nwithConnection\n(\nto\n:\n \n.\nfoo\n)\n \n{\n \ndb\n \n-\n \nFuture\nUser\n \nin\n\n \nlet\n \nuser\n \n=\n \nUser\n(\nname\n:\n \nVapor\n,\n \nage\n:\n \n3\n)\n\n \nreturn\n \nuser\n.\nsave\n(\non\n:\n \ndb\n).\ntransform\n(\nto\n:\n \nuser\n)\n \n// Future\nUser\n\n \n}\n\n\n}\n\n\n\n\n\n\nResponse\n\n\n.save(on: )\n returns a \nFuture\nVoid\n that completes when the user has finished saving. In this example, we then\nmap that \nFuture\nVoid\n to a \nFuture\nUser\n by calling \n.map\n and passing in the recently-saved user.\n\n\nYou can also use \n.map\n to return a simple success response.\n\n\nrouter\n.\npost\n(...)\n \n{\n \nreq\n \nin\n\n \nreturn\n \nreq\n.\nwithConnection\n(\nto\n:\n \n.\nfoo\n)\n \n{\n \ndb\n \n-\n \nFuture\nHTTPResponse\n \nin\n\n \nlet\n \nuser\n \n=\n \nUser\n(\nname\n:\n \nVapor\n,\n \nage\n:\n \n3\n)\n\n \nreturn\n \nuser\n.\nsave\n(\non\n:\n \ndb\n).\nmap\n(\nto\n:\n \nHTTPResponse\n.\nself\n)\n \n{\n\n \nreturn\n \nHTTPResponse\n(\nstatus\n:\n \n.\ncreated\n)\n\n \n}\n\n \n}\n\n\n}\n\n\n\n\n\n\nMultiple\n\n\nIf you have multiple instances to save, do so using an array. Arrays containing only futures behave like futures.\n\n\nrouter\n.\npost\n(...)\n \n{\n \nreq\n \nin\n\n \nreturn\n \nreq\n.\nwithConnection\n(\nto\n:\n \n.\nfoo\n)\n \n{\n \ndb\n \n-\n \nFuture\nHTTPResponse\n \nin\n\n \nlet\n \nmarie\n \n=\n \nUser\n(\nname\n:\n \nMarie Curie\n,\n \nage\n:\n \n66\n)\n\n \nlet\n \ncharles\n \n=\n \nUser\n(\nname\n:\n \nCharles Darwin\n,\n \nage\n:\n \n73\n)\n\n \nreturn\n \n[\n\n \nmarie\n.\nsave\n(\non\n:\n \ndb\n),\n\n \ncharles\n.\nsave\n(\non\n:\n \ndb\n)\n\n \n].\nmap\n(\nto\n:\n \nHTTPResponse\n.\nself\n)\n \n{\n\n \nreturn\n \nHTTPResponse\n(\nstatus\n:\n \n.\ncreated\n)\n\n \n}\n\n \n}\n\n\n}\n\n\n\n\n\n\nRead\n\n\nTo read models from the database, use \n.query()\n on the database connection to create a \nQueryBuilder\n.\n\n\nAll\n\n\nFetch all instances of a model from the database using \n.all()\n.\n\n\nrouter\n.\nget\n(...)\n \n{\n \nreq\n \nin\n\n \nreturn\n \nreq\n.\nwithConnection\n(\nto\n:\n \n.\nfoo\n)\n \n{\n \ndb\n \n-\n \nFuture\n[\nUser\n]\n \nin\n\n \nreturn\n \ndb\n.\nquery\n(\nUser\n.\nself\n).\nall\n()\n\n \n}\n\n\n}\n\n\n\n\n\n\nFilter\n\n\nUse \n.filter(...)\n to apply \nfilters\n to your query.\n\n\nrouter\n.\nget\n(...)\n \n{\n \nreq\n \nin\n\n \nreturn\n \nreq\n.\nwithConnection\n(\nto\n:\n \n.\nfoo\n)\n \n{\n \ndb\n \n-\n \nFuture\n[\nUser\n]\n \nin\n\n \nreturn\n \ntry\n \ndb\n.\nquery\n(\nUser\n.\nself\n).\nfilter\n(\n\\\nUser\n.\nage\n \n \n50\n).\nall\n()\n\n \n}\n\n\n}\n\n\n\n\n\n\nFirst\n\n\nYou can also use \n.first()\n to just get the first result.\n\n\nrouter\n.\nget\n(...)\n \n{\n \nreq\n \nin\n\n \nreturn\n \nreq\n.\nwithConnection\n(\nto\n:\n \n.\nfoo\n)\n \n{\n \ndb\n \n-\n \nFuture\nUser\n \nin\n\n \nreturn\n \ntry\n \ndb\n.\nquery\n(\nUser\n.\nself\n).\nfilter\n(\n\\\nUser\n.\nname\n \n==\n \nVapor\n).\nfirst\n().\nmap\n(\nto\n:\n \nUser\n.\nself\n)\n \n{\n \nuser\n \nin\n\n \nguard\n \nlet\n \nuser\n \n=\n \nuser\n \nelse\n \n{\n\n \nthrow\n \nAbort\n(.\nnotFound\n,\n \nreason\n:\n \nCould not find user.\n)\n\n \n}\n\n\n \nreturn\n \nuser\n\n \n}\n\n \n}\n\n\n}\n\n\n\n\n\n\nNotice we use \n.map(to:)\n here to convert the optional user returned by \n.first()\n to a non-optional\nuser, or we throw an error.\n\n\nUpdate\n\n\nrouter\n.\nput\n(...)\n \n{\n \nreq\n \nin\n\n \nreturn\n \nreq\n.\nwithConnection\n(\nto\n:\n \n.\nfoo\n)\n \n{\n \ndb\n \n-\n \nFuture\nUser\n \nin\n\n \nreturn\n \ndb\n.\nquery\n(\nUser\n.\nself\n).\nfirst\n().\nmap\n(\nto\n:\n \nUser\n.\nself\n)\n \n{\n \nuser\n \nin\n\n \nguard\n \nlet\n \nuser\n \n=\n \n$0\n \nelse\n \n{\n\n \nthrow\n \nAbort\n(.\nnotFound\n,\n \nreason\n:\n \nCould not find user.\n)\n\n \n}\n\n\n \nreturn\n \nuser\n\n \n}.\nflatMap\n(\nto\n:\n \nUser\n.\nself\n)\n \n{\n \nuser\n \nin\n\n \nuser\n.\nage\n \n+=\n \n1\n\n \nreturn\n \nuser\n.\nupdate\n(\non\n:\n \ndb\n).\nmap\n(\nto\n:\n \nUser\n.\nself\n)\n \n{\n \nuser\n \n}\n\n \n}\n\n \n}\n\n\n}\n\n\n\n\n\n\nNotice we use \n.map(to:)\n here to convert the optional user returned by \n.first()\n to a non-optional\nuser, or we throw an error.\n\n\nDelete\n\n\nrouter\n.\ndelete\n(...)\n \n{\n \nreq\n \nin\n\n \nreturn\n \nreq\n.\nwithConnection\n(\nto\n:\n \n.\nfoo\n)\n \n{\n \ndb\n \n-\n \nFuture\nUser\n \nin\n\n \nreturn\n \ndb\n.\nquery\n(\nUser\n.\nself\n).\nfirst\n().\nmap\n(\nto\n:\n \nUser\n.\nself\n)\n \n{\n \nuser\n \nin\n\n \nguard\n \nlet\n \nuser\n \n=\n \n$0\n \nelse\n \n{\n\n \nthrow\n \nAbort\n(.\nnotFound\n,\n \nreason\n:\n \nCould not find user.\n)\n\n \n}\n\n \nreturn\n \nuser\n\n \n}.\nflatMap\n(\nto\n:\n \nUser\n.\nself\n)\n \n{\n \nuser\n \nin\n\n \nreturn\n \nuser\n.\ndelete\n(\non\n:\n \ndb\n).\ntransfom\n(\nto\n:\n \nuser\n)\n\n \n}\n\n \n}\n\n\n}\n\n\n\n\n\n\nNotice we use \n.map(to:)\n here to convert the optional user returned by \n.first()\n to a non-optional\nuser, or we throw an error.", + "title": "Querying" + }, + { + "location": "/fluent/querying/#querying-models", + "text": "Once you have a model (and optionally a migration ) you can start\nquerying your database to create, read, update, and delete data.", + "title": "Querying Models" + }, + { + "location": "/fluent/querying/#connection", + "text": "The first thing you need to query your database, is a connection to it. Luckily, they are easy to get. You can use either the application or an incoming request to create a database connection. You just need\naccess to the database identifier.", + "title": "Connection" + }, + { + "location": "/fluent/querying/#request", + "text": "The preferred method for getting access to a database connection is via an incoming request. router . get (...) { req in \n return req . withConnection ( to : . foo ) { db in \n // use the db here \n } } The first parameter is the database's identifier. The second parameter is a closure\nthat accepts a connection to that database. Tip Although the closure to .withConnection(to: ...) accepts a database connection , we often use just db for short. The closure is expected to return a Future Void . When this future is completed, the connection will be released\nback into Fluent's connection pool. This is usually acheived by simply returning the query as we will soon see.", + "title": "Request" + }, + { + "location": "/fluent/querying/#application", + "text": "You can also create a database connection using the application. This is useful for cases where you must access\nthe database from outside a request/response event. let res = app . withConnection ( to : . foo ) { db in \n // use the db here } print ( res ) // Future T This is usually done in the boot section of your application. Warning Do not use database connections created by the application in a route closure (when responding to a request).\nAlways use the incoming request to create a connection to avoid threading issues.", + "title": "Application" + }, + { + "location": "/fluent/querying/#create", + "text": "To create (save) a model to the database, first initialize an instance of your model, then call .save(on: ) . router . post (...) { req in \n return req . withConnection ( to : . foo ) { db - Future User in \n let user = User ( name : Vapor , age : 3 ) \n return user . save ( on : db ). transform ( to : user ) // Future User \n } }", + "title": "Create" + }, + { + "location": "/fluent/querying/#response", + "text": ".save(on: ) returns a Future Void that completes when the user has finished saving. In this example, we then\nmap that Future Void to a Future User by calling .map and passing in the recently-saved user. You can also use .map to return a simple success response. router . post (...) { req in \n return req . withConnection ( to : . foo ) { db - Future HTTPResponse in \n let user = User ( name : Vapor , age : 3 ) \n return user . save ( on : db ). map ( to : HTTPResponse . self ) { \n return HTTPResponse ( status : . created ) \n } \n } }", + "title": "Response" + }, + { + "location": "/fluent/querying/#multiple", + "text": "If you have multiple instances to save, do so using an array. Arrays containing only futures behave like futures. router . post (...) { req in \n return req . withConnection ( to : . foo ) { db - Future HTTPResponse in \n let marie = User ( name : Marie Curie , age : 66 ) \n let charles = User ( name : Charles Darwin , age : 73 ) \n return [ \n marie . save ( on : db ), \n charles . save ( on : db ) \n ]. map ( to : HTTPResponse . self ) { \n return HTTPResponse ( status : . created ) \n } \n } }", + "title": "Multiple" + }, + { + "location": "/fluent/querying/#read", + "text": "To read models from the database, use .query() on the database connection to create a QueryBuilder .", + "title": "Read" + }, + { + "location": "/fluent/querying/#all", + "text": "Fetch all instances of a model from the database using .all() . router . get (...) { req in \n return req . withConnection ( to : . foo ) { db - Future [ User ] in \n return db . query ( User . self ). all () \n } }", + "title": "All" + }, + { + "location": "/fluent/querying/#filter", + "text": "Use .filter(...) to apply filters to your query. router . get (...) { req in \n return req . withConnection ( to : . foo ) { db - Future [ User ] in \n return try db . query ( User . self ). filter ( \\ User . age 50 ). all () \n } }", + "title": "Filter" + }, + { + "location": "/fluent/querying/#first", + "text": "You can also use .first() to just get the first result. router . get (...) { req in \n return req . withConnection ( to : . foo ) { db - Future User in \n return try db . query ( User . self ). filter ( \\ User . name == Vapor ). first (). map ( to : User . self ) { user in \n guard let user = user else { \n throw Abort (. notFound , reason : Could not find user. ) \n } \n\n return user \n } \n } } Notice we use .map(to:) here to convert the optional user returned by .first() to a non-optional\nuser, or we throw an error.", + "title": "First" + }, + { + "location": "/fluent/querying/#update", + "text": "router . put (...) { req in \n return req . withConnection ( to : . foo ) { db - Future User in \n return db . query ( User . self ). first (). map ( to : User . self ) { user in \n guard let user = $0 else { \n throw Abort (. notFound , reason : Could not find user. ) \n } \n\n return user \n }. flatMap ( to : User . self ) { user in \n user . age += 1 \n return user . update ( on : db ). map ( to : User . self ) { user } \n } \n } } Notice we use .map(to:) here to convert the optional user returned by .first() to a non-optional\nuser, or we throw an error.", + "title": "Update" + }, + { + "location": "/fluent/querying/#delete", + "text": "router . delete (...) { req in \n return req . withConnection ( to : . foo ) { db - Future User in \n return db . query ( User . self ). first (). map ( to : User . self ) { user in \n guard let user = $0 else { \n throw Abort (. notFound , reason : Could not find user. ) \n } \n return user \n }. flatMap ( to : User . self ) { user in \n return user . delete ( on : db ). transfom ( to : user ) \n } \n } } Notice we use .map(to:) here to convert the optional user returned by .first() to a non-optional\nuser, or we throw an error.", + "title": "Delete" + }, + { + "location": "/fluent/query-builder/", + "text": "Fluent Query Builder\n\n\nComing soon.\n\n\nFilter\n\n\nComing soon.\n\n\nCompare\n\n\nComing soon.\n\n\nGroup\n\n\nComing soon.\n\n\nSubset\n\n\nComing soon.\n\n\nJoin\n\n\nComing soon.\n\n\nRange\n\n\nComing soon.\n\n\nSort\n\n\nComing soon.\n\n\nExecute\n\n\nComing soon.\n\n\nAggregate\n\n\nComing soon.\n\n\nAll\n\n\nComing soon.\n\n\nFirst\n\n\nComing soon.\n\n\nQuery\n\n\nComing soon.", + "title": "Query Builder" + }, + { + "location": "/fluent/query-builder/#fluent-query-builder", + "text": "Coming soon.", + "title": "Fluent Query Builder" + }, + { + "location": "/fluent/query-builder/#filter", + "text": "Coming soon.", + "title": "Filter" + }, + { + "location": "/fluent/query-builder/#compare", + "text": "Coming soon.", + "title": "Compare" + }, + { + "location": "/fluent/query-builder/#group", + "text": "Coming soon.", + "title": "Group" + }, + { + "location": "/fluent/query-builder/#subset", + "text": "Coming soon.", + "title": "Subset" + }, + { + "location": "/fluent/query-builder/#join", + "text": "Coming soon.", + "title": "Join" + }, + { + "location": "/fluent/query-builder/#range", + "text": "Coming soon.", + "title": "Range" + }, + { + "location": "/fluent/query-builder/#sort", + "text": "Coming soon.", + "title": "Sort" + }, + { + "location": "/fluent/query-builder/#execute", + "text": "Coming soon.", + "title": "Execute" + }, + { + "location": "/fluent/query-builder/#aggregate", + "text": "Coming soon.", + "title": "Aggregate" + }, + { + "location": "/fluent/query-builder/#all", + "text": "Coming soon.", + "title": "All" + }, + { + "location": "/fluent/query-builder/#first", + "text": "Coming soon.", + "title": "First" + }, + { + "location": "/fluent/query-builder/#query", + "text": "Coming soon.", + "title": "Query" + }, + { + "location": "/fluent/schema-builder/", + "text": "Fluent Schema Builder\n\n\nComing soon.\n\n\nCreate\n\n\nComing soon.\n\n\nUpdate\n\n\nComing soon.\n\n\nDelete\n\n\nComing soon.\n\n\nReferences\n\n\nComing soon.", + "title": "Schema Builder" + }, + { + "location": "/fluent/schema-builder/#fluent-schema-builder", + "text": "Coming soon.", + "title": "Fluent Schema Builder" + }, + { + "location": "/fluent/schema-builder/#create", + "text": "Coming soon.", + "title": "Create" + }, + { + "location": "/fluent/schema-builder/#update", + "text": "Coming soon.", + "title": "Update" + }, + { + "location": "/fluent/schema-builder/#delete", + "text": "Coming soon.", + "title": "Delete" + }, + { + "location": "/fluent/schema-builder/#references", + "text": "Coming soon.", + "title": "References" + }, + { + "location": "/fluent/relations/", + "text": "Fluent Relations\n\n\nComing soon.\n\n\nParent / Child\n\n\nComing soon.\n\n\nSiblings\n\n\nComing soon.", + "title": "Relations" + }, + { + "location": "/fluent/relations/#fluent-relations", + "text": "Coming soon.", + "title": "Fluent Relations" + }, + { + "location": "/fluent/relations/#parent-child", + "text": "Coming soon.", + "title": "Parent / Child" + }, + { + "location": "/fluent/relations/#siblings", + "text": "Coming soon.", + "title": "Siblings" + }, + { + "location": "/fluent/pivot/", + "text": "Fluent Pivot\n\n\nComing soon.", + "title": "Pivot" + }, + { + "location": "/fluent/pivot/#fluent-pivot", + "text": "Coming soon.", + "title": "Fluent Pivot" + }, + { + "location": "/fluent/transaction/", + "text": "Fluent Transactions\n\n\nComing soon.", + "title": "Transaction" + }, + { + "location": "/fluent/transaction/#fluent-transactions", + "text": "Coming soon.", + "title": "Fluent Transactions" + }, + { + "location": "/fluent/database/", + "text": "Fluent Database\n\n\nComing soon.\n\n\nConnection\n\n\nComing soon.\n\n\nLogger\n\n\nComing soon.", + "title": "Database" + }, + { + "location": "/fluent/database/#fluent-database", + "text": "Coming soon.", + "title": "Fluent Database" + }, + { + "location": "/fluent/database/#connection", + "text": "Coming soon.", + "title": "Connection" + }, + { + "location": "/fluent/database/#logger", + "text": "Coming soon.", + "title": "Logger" + }, + { + "location": "/postgresql/getting-started/", + "text": "Getting Started with PostgreSQL\n\n\nPostgreSQL\n is a powerful, open-source database that puts an emphasis on standards compliance. PostgreSQL's strong type-system make it a great fit with Swift, and it is the preferred database for use with Vapor.\n\n\nYou can use PostgreSQL with Vapor (or any server-side Swift framework) by either:\n\n\n\n\nUsing \nFluent PostgreSQL\n ORM.\n\n\nUse just \nPostgreSQL core\n.\n\n\n\n\nWe recommend using the ORM since it does a lot of the hard work for you. Check out the respective guides to learn more.", + "title": "Getting Started" + }, + { + "location": "/postgresql/getting-started/#getting-started-with-postgresql", + "text": "PostgreSQL is a powerful, open-source database that puts an emphasis on standards compliance. PostgreSQL's strong type-system make it a great fit with Swift, and it is the preferred database for use with Vapor. You can use PostgreSQL with Vapor (or any server-side Swift framework) by either: Using Fluent PostgreSQL ORM. Use just PostgreSQL core . We recommend using the ORM since it does a lot of the hard work for you. Check out the respective guides to learn more.", + "title": "Getting Started with PostgreSQL" + }, + { + "location": "/postgresql/fluent/", + "text": "Fluent PostgreSQL\n\n\nFluent PostgreSQL (\nvapor/fluent-postgresql\n) is a type-safe, fast, and easy-to-use ORM for PostgreSQL built on top of \nFluent\n.\n\n\n\n\nSeealso\n\n\nThe Fluent PostgreSQL package is built on top of \nFluent\n and the pure Swift, NIO-based \nPostgreSQL core\n. You should refer to their guides for more information about subjects not covered here.\n\n\n\n\nGetting Started\n\n\nThis section will show you how to add Fluent PostgreSQL to your project and create your first \nPostgreSQLModel\n.\n\n\nPackage\n\n\nThe first step to using Fluent PostgreSQL is adding it as a dependency to your project in your SPM package manifest file.\n\n\n// swift-tools-version:4.0\n\n\nimport\n \nPackageDescription\n\n\n\nlet\n \npackage\n \n=\n \nPackage\n(\n\n \nname\n:\n \nMyApp\n,\n\n \ndependencies\n:\n \n[\n\n \n/// Any other dependencies ...\n\n\n \n// \ud83d\udd8b\ud83d\udc18 Swift ORM (queries, models, relations, etc) built on PostgreSQL.\n\n \n.\npackage\n(\nurl\n:\n \nhttps://github.com/vapor/fluent-postgresql.git\n,\n \nfrom\n:\n \n1.0.0-rc\n),\n\n \n],\n\n \ntargets\n:\n \n[\n\n \n.\ntarget\n(\nname\n:\n \nApp\n,\n \ndependencies\n:\n \n[\nFluentPostgreSQL\n,\n \n...]),\n\n \n.\ntarget\n(\nname\n:\n \nRun\n,\n \ndependencies\n:\n \n[\nApp\n]),\n\n \n.\ntestTarget\n(\nname\n:\n \nAppTests\n,\n \ndependencies\n:\n \n[\nApp\n]),\n\n \n]\n\n\n)\n\n\n\n\n\n\nDon't forget to add the module as a dependency in the \ntargets\n array. Once you have added the dependency, regenerate your Xcode project with the following command:\n\n\nvapor xcode\n\n\n\n\n\nModel\n\n\nNow let's create our first \nPostgreSQLModel\n. Models represent tables in your PostgreSQL database and they are the primary method of interacting with your data. \n\n\n/// A simple user.\n\n\nfinal\n \nclass\n \nUser\n:\n \nPostgreSQLModel\n \n{\n\n \n/// The unique identifier for this user.\n\n \nvar\n \nid\n:\n \nInt\n?\n\n\n \n/// The user\ns full name.\n\n \nvar\n \nname\n:\n \nString\n\n\n \n/// The user\ns current age in years.\n\n \nvar\n \nage\n:\n \nInt\n\n\n \n/// Creates a new user.\n\n \ninit\n(\nid\n:\n \nInt\n?\n \n=\n \nnil\n,\n \nname\n:\n \nString\n,\n \nage\n:\n \nInt\n)\n \n{\n\n \nself\n.\nid\n \n=\n \nid\n\n \nself\n.\nname\n \n=\n \nname\n\n \nself\n.\nage\n \n=\n \nage\n\n \n}\n\n\n}\n\n\n\n\n\n\nThe example above shows a \nPostgreSQLModel\n for a simple model representing a user. You can make both \nstruct\ns and \nclass\nes a model. You can even conform types that come from external modules. The only requirement is that these types conform to \nCodable\n, which must be declared on the base type for synthesized (automatic) conformance.\n\n\nStandard practice with PostgreSQL databases is using an auto-generated \nINTEGER\n for creating and storing unique identifiers in the \nid\n column. It's also possible to use \nUUID\ns or even \nString\ns for your identifiers. There are convenience protocol for that. \n\n\n\n\n\n\n\n\nprotocol\n\n\ntype\n\n\nkey\n\n\n\n\n\n\n\n\n\n\nPostgreSQLModel\n\n\nInt\n\n\nid\n\n\n\n\n\n\nPostgreSQLUUIDModel\n\n\nUUID\n\n\nid\n\n\n\n\n\n\nPostgreSQLStringModel\n\n\nString\n\n\nid\n\n\n\n\n\n\n\n\n\n\nSeealso\n\n\nTake a look at \nFluent \n Model\n for more information on creating models with custom ID types and keys.\n\n\n\n\nMigration\n\n\nAll of your models (with some rare exceptions) should have a corresponding table\nor \nschema\nin your database. You can use a \nFluent \n Migration\n to automatically generate this schema in a testable, maintainable way. Fluent makes it easy to automatically generate a migration for your model\n\n\n\n\nTip\n\n\nIf you are creating models to represent an existing table or database, you can skip this step.\n\n\n\n\n/// Allows `User` to be used as a migration.\n\n\nextension\n \nUser\n:\n \nMigration\n \n{\n \n}\n\n\n\n\n\n\nThat's all it takes. Fluent uses Codable to analyze your model and will attempt to create the best possible schema for it.\n\n\nTake a look at \nFluent \n Migration\n if you are interested in customizing this migration.\n\n\nConfigure\n\n\nThe final step is to configure your database. At a minimum, this requires adding two things to your \nconfigure.swift\n file.\n\n\n\n\nFluentPostgreSQLProvider\n\n\nMigrationConfig\n\n\n\n\nLet's take a look.\n\n\nimport\n \nFluentPostgreSQL\n\n\n\n/// ...\n\n\n\n/// Register providers first\n\n\ntry\n \nservices\n.\nregister\n(\nFluentPostgreSQLProvider\n())\n\n\n\n/// Configure migrations\n\n\nvar\n \nmigrations\n \n=\n \nMigrationConfig\n()\n\n\nmigrations\n.\nadd\n(\nmodel\n:\n \nUser\n.\nself\n,\n \ndatabase\n:\n \n.\npsql\n)\n\n\nservices\n.\nregister\n(\nmigrations\n)\n\n\n\n/// Other services....\n\n\n\n\n\n\nRegistering the provider will add all of the services required for Fluent PostgreSQL to work properly. It also includes a default database config struct that uses typical development environment credentials. \n\n\nYou can of course override this config struct if you have non-standard credentials.\n\n\n/// Register custom PostgreSQL Config\n\n\nlet\n \npsqlConfig\n \n=\n \nPostgreSQLDatabaseConfig\n(\nhostname\n:\n \nlocalhost\n,\n \nport\n:\n \n5432\n,\n \nusername\n:\n \nvapor\n)\n\n\nservices\n.\nregister\n(\npsqlConfig\n)\n\n\n\n\n\n\nOnce you have the \nMigrationConfig\n added, you should be able to run your application and see the following:\n\n\nMigrating psql DB\nMigrations \ncomplete\n\nServer starting on http://localhost:8080\n\n\n\n\n\nQuery\n\n\nNow that you have created a model and a corresponding schema in your database, let's make your first query.\n\n\nrouter\n.\nget\n(\nusers\n)\n \n{\n \nreq\n \nin\n\n \nreturn\n \nUser\n.\nquery\n(\non\n:\n \nreq\n).\nall\n()\n\n\n}\n\n\n\n\n\n\nIf you run your app, and query that route, you should see an empty array returned. Now you just need to add some users! Congratulations on getting your first Fluent PostgreSQL model and migration working.\n\n\nConnection\n\n\nWith Fluent, you always have access to the underlying database driver. Using this underlying driver to perform a query is sometimes called a \"raw query\".\n\n\nLet's take a look at a raw PostgreSQL query.\n\n\nrouter\n.\nget\n(\npsql-version\n)\n \n{\n \nreq\n \n-\n \nFuture\nString\n \nin\n\n \nreturn\n \nreq\n.\nwithPooledConnection\n(\nto\n:\n \n.\npsql\n)\n \n{\n \nconn\n \nin\n\n \nreturn\n \ntry\n \nconn\n.\nquery\n(\nselect version() as v;\n).\nmap\n(\nto\n:\n \nString\n.\nself\n)\n \n{\n \nrows\n \nin\n\n \nreturn\n \ntry\n \nrows\n[\n0\n].\nfirstValue\n(\nforColumn\n:\n \nv\n)?.\ndecode\n(\nString\n.\nself\n)\n \n??\n \nn/a\n\n \n}\n\n \n}\n\n\n}\n\n\n\n\n\n\nIn the above example, \nwithPooledConnection(to:)\n is used to create a connection to the database identified by \n.psql\n. This is the default database identifier. See \nFluent \n Database\n to learn more.\n\n\nOnce we have the \nPostgreSQLConnection\n, we can perform a query on it. You can learn more about the methods available in \nPostgreSQL \n Core\n.", + "title": "Fluent PostgreSQL" + }, + { + "location": "/postgresql/fluent/#fluent-postgresql", + "text": "Fluent PostgreSQL ( vapor/fluent-postgresql ) is a type-safe, fast, and easy-to-use ORM for PostgreSQL built on top of Fluent . Seealso The Fluent PostgreSQL package is built on top of Fluent and the pure Swift, NIO-based PostgreSQL core . You should refer to their guides for more information about subjects not covered here.", + "title": "Fluent PostgreSQL" + }, + { + "location": "/postgresql/fluent/#getting-started", + "text": "This section will show you how to add Fluent PostgreSQL to your project and create your first PostgreSQLModel .", + "title": "Getting Started" + }, + { + "location": "/postgresql/fluent/#package", + "text": "The first step to using Fluent PostgreSQL is adding it as a dependency to your project in your SPM package manifest file. // swift-tools-version:4.0 import PackageDescription let package = Package ( \n name : MyApp , \n dependencies : [ \n /// Any other dependencies ... \n\n // \ud83d\udd8b\ud83d\udc18 Swift ORM (queries, models, relations, etc) built on PostgreSQL. \n . package ( url : https://github.com/vapor/fluent-postgresql.git , from : 1.0.0-rc ), \n ], \n targets : [ \n . target ( name : App , dependencies : [ FluentPostgreSQL , ...]), \n . target ( name : Run , dependencies : [ App ]), \n . testTarget ( name : AppTests , dependencies : [ App ]), \n ] ) Don't forget to add the module as a dependency in the targets array. Once you have added the dependency, regenerate your Xcode project with the following command: vapor xcode", + "title": "Package" + }, + { + "location": "/postgresql/fluent/#model", + "text": "Now let's create our first PostgreSQLModel . Models represent tables in your PostgreSQL database and they are the primary method of interacting with your data. /// A simple user. final class User : PostgreSQLModel { \n /// The unique identifier for this user. \n var id : Int ? \n\n /// The user s full name. \n var name : String \n\n /// The user s current age in years. \n var age : Int \n\n /// Creates a new user. \n init ( id : Int ? = nil , name : String , age : Int ) { \n self . id = id \n self . name = name \n self . age = age \n } } The example above shows a PostgreSQLModel for a simple model representing a user. You can make both struct s and class es a model. You can even conform types that come from external modules. The only requirement is that these types conform to Codable , which must be declared on the base type for synthesized (automatic) conformance. Standard practice with PostgreSQL databases is using an auto-generated INTEGER for creating and storing unique identifiers in the id column. It's also possible to use UUID s or even String s for your identifiers. There are convenience protocol for that. protocol type key PostgreSQLModel Int id PostgreSQLUUIDModel UUID id PostgreSQLStringModel String id Seealso Take a look at Fluent Model for more information on creating models with custom ID types and keys.", + "title": "Model" + }, + { + "location": "/postgresql/fluent/#migration", + "text": "All of your models (with some rare exceptions) should have a corresponding table or schema in your database. You can use a Fluent Migration to automatically generate this schema in a testable, maintainable way. Fluent makes it easy to automatically generate a migration for your model Tip If you are creating models to represent an existing table or database, you can skip this step. /// Allows `User` to be used as a migration. extension User : Migration { } That's all it takes. Fluent uses Codable to analyze your model and will attempt to create the best possible schema for it. Take a look at Fluent Migration if you are interested in customizing this migration.", + "title": "Migration" + }, + { + "location": "/postgresql/fluent/#configure", + "text": "The final step is to configure your database. At a minimum, this requires adding two things to your configure.swift file. FluentPostgreSQLProvider MigrationConfig Let's take a look. import FluentPostgreSQL /// ... /// Register providers first try services . register ( FluentPostgreSQLProvider ()) /// Configure migrations var migrations = MigrationConfig () migrations . add ( model : User . self , database : . psql ) services . register ( migrations ) /// Other services.... Registering the provider will add all of the services required for Fluent PostgreSQL to work properly. It also includes a default database config struct that uses typical development environment credentials. You can of course override this config struct if you have non-standard credentials. /// Register custom PostgreSQL Config let psqlConfig = PostgreSQLDatabaseConfig ( hostname : localhost , port : 5432 , username : vapor ) services . register ( psqlConfig ) Once you have the MigrationConfig added, you should be able to run your application and see the following: Migrating psql DB\nMigrations complete \nServer starting on http://localhost:8080", + "title": "Configure" + }, + { + "location": "/postgresql/fluent/#query", + "text": "Now that you have created a model and a corresponding schema in your database, let's make your first query. router . get ( users ) { req in \n return User . query ( on : req ). all () } If you run your app, and query that route, you should see an empty array returned. Now you just need to add some users! Congratulations on getting your first Fluent PostgreSQL model and migration working.", + "title": "Query" + }, + { + "location": "/postgresql/fluent/#connection", + "text": "With Fluent, you always have access to the underlying database driver. Using this underlying driver to perform a query is sometimes called a \"raw query\". Let's take a look at a raw PostgreSQL query. router . get ( psql-version ) { req - Future String in \n return req . withPooledConnection ( to : . psql ) { conn in \n return try conn . query ( select version() as v; ). map ( to : String . self ) { rows in \n return try rows [ 0 ]. firstValue ( forColumn : v )?. decode ( String . self ) ?? n/a \n } \n } } In the above example, withPooledConnection(to:) is used to create a connection to the database identified by .psql . This is the default database identifier. See Fluent Database to learn more. Once we have the PostgreSQLConnection , we can perform a query on it. You can learn more about the methods available in PostgreSQL Core .", + "title": "Connection" + }, + { + "location": "/postgresql/core/", + "text": "PostgreSQL Core\n\n\nPostgreSQL (\nvapor/postgresql\n) is a pure-Swift (no \nlibpq\n dependency), event-driven, non-blocking driver for PostgreSQL. It's build on top of the \nSwift NIO\n networking library.\n\n\n\n\nSeealso\n\n\nThe higher-level, Fluent PostgreSQL ORM guide is located at \nPostgreSQL \n Fluent\n\n\n\n\nUsing just the PostgreSQL package for your project may be a good idea if any of the following are true.\n\n\n\n\nYou have an existing DB with non-standard structure.\n\n\nYou rely heavily on custom or complex SQL queries.\n\n\nYou just plain don't like ORMs.\n\n\n\n\nPostgreSQL core is built on top of DatabaseKit which provides some conveniences like connection pooling and integrations with Vapor's \nServices\n architecture.\n\n\n\n\nTip\n\n\nEven if you do choose to use \nFluent PostgreSQL\n, all of the features of PostgreSQL core will be available to you.\n\n\n\n\nGetting Started\n\n\nLet's take a look at how you can get started using PostgreSQL core.\n\n\nPackage\n\n\nThe first step to using PostgreSQL core is adding it as a dependency to your project in your SPM package manifest file.\n\n\n// swift-tools-version:4.0\n\n\nimport\n \nPackageDescription\n\n\n\nlet\n \npackage\n \n=\n \nPackage\n(\n\n \nname\n:\n \nMyApp\n,\n\n \ndependencies\n:\n \n[\n\n \n/// Any other dependencies ...\n\n\n \n// \ud83d\udc18 Non-blocking, event-driven Swift client for PostgreSQL.\n\n \n.\npackage\n(\nurl\n:\n \nhttps://github.com/vapor/postgresql.git\n,\n \nfrom\n:\n \n1.0.0-rc\n),\n\n \n],\n\n \ntargets\n:\n \n[\n\n \n.\ntarget\n(\nname\n:\n \nApp\n,\n \ndependencies\n:\n \n[\nPostgreSQL\n,\n \n...]),\n\n \n.\ntarget\n(\nname\n:\n \nRun\n,\n \ndependencies\n:\n \n[\nApp\n]),\n\n \n.\ntestTarget\n(\nname\n:\n \nAppTests\n,\n \ndependencies\n:\n \n[\nApp\n]),\n\n \n]\n\n\n)\n\n\n\n\n\n\nDon't forget to add the module as a dependency in the \ntargets\n array. Once you have added the dependency, regenerate your Xcode project with the following command:\n\n\nvapor xcode\n\n\n\n\n\nConfig\n\n\nThe next step is to configure the database in your \nconfigure.swift\n file.\n\n\nimport\n \nPostgreSQL\n\n\n\n/// ...\n\n\n\n/// Register providers first\n\n\ntry\n \nservices\n.\nregister\n(\nPostgreSQLProvider\n())\n\n\n\n\n\n\nRegistering the provider will add all of the services required for PostgreSQL to work properly. It also includes a default database config struct that uses typical development environment credentials. \n\n\nYou can of course override this config struct if you have non-standard credentials.\n\n\n/// Register custom PostgreSQL Config\n\n\nlet\n \npsqlConfig\n \n=\n \nPostgreSQLDatabaseConfig\n(\nhostname\n:\n \nlocalhost\n,\n \nport\n:\n \n5432\n,\n \nusername\n:\n \nvapor\n)\n\n\nservices\n.\nregister\n(\npsqlConfig\n)\n\n\n\n\n\n\nQuery\n\n\nNow that the database is configured, you can make your first query.\n\n\nrouter\n.\nget\n(\npsql-version\n)\n \n{\n \nreq\n \n-\n \nFuture\nString\n \nin\n\n \nreturn\n \nreq\n.\nwithPooledConnection\n(\nto\n:\n \n.\npsql\n)\n \n{\n \nconn\n \nin\n\n \nreturn\n \ntry\n \nconn\n.\nquery\n(\nselect version() as v;\n).\nmap\n(\nto\n:\n \nString\n.\nself\n)\n \n{\n \nrows\n \nin\n\n \nreturn\n \ntry\n \nrows\n[\n0\n].\nfirstValue\n(\nforColumn\n:\n \nv\n)?.\ndecode\n(\nString\n.\nself\n)\n \n??\n \nn/a\n\n \n}\n\n \n}\n\n\n}\n\n\n\n\n\n\nVisiting this route should display your PostgreSQL version.\n\n\nConnection\n\n\nA \nPostgreSQLConnection\n is normally created using the \nRequest\n container and can perform two different types of queries.\n\n\nCreate\n\n\nThere are two methods for creating a \nPostgreSQLConnection\n.\n\n\nreturn\n \nreq\n.\nwithPooledConnection\n(\nto\n:\n \n.\npsql\n)\n \n{\n \nconn\n \nin\n\n \n/// ...\n\n\n}\n\n\nreturn\n \nreq\n.\nwithConnection\n(\nto\n:\n \n.\npsql\n)\n \n{\n \nconn\n \nin\n\n \n/// ...\n\n\n}\n\n\n\n\n\n\nAs the names imply, \nwithPooledConnection(to:)\n utilizes a connection pool. \nwithConnection(to:)\n does not. Connection pooling is a great way to ensure your application does not exceed the limits of your database, even under peak load.\n\n\nSimply Query\n\n\nUse \n.simpleQuery(_:)\n to perform a query on your PostgreSQL database that does not bind any parameters. Some queries you send to PostgreSQL may actually require that you use the \nsimpleQuery(_:)\n method instead of the parameterized method. \n\n\n\n\nNote\n\n\nThis method sends and receives data as text-encoded, meaning it is not optimal for transmitting things like integers.\n\n\n\n\nlet\n \nrows\n \n=\n \nreq\n.\nwithPooledConnection\n(\nto\n:\n \n.\npsql\n)\n \n{\n \nconn\n \nin\n\n \nreturn\n \nconn\n.\nsimpleQuery\n(\nSELECT * FROM users;\n)\n\n\n}\n\n\nprint\n(\nrows\n)\n \n// Future\n[[PostgreSQLColumn: PostgreSQLData]]\n\n\n\n\n\n\nYou can also choose to receive each row in a callback, which is great for conserving memory for large queries.\n\n\nlet\n \ndone\n \n=\n \nreq\n.\nwithPooledConnection\n(\nto\n:\n \n.\npsql\n)\n \n{\n \nconn\n \nin\n\n \nreturn\n \nconn\n.\nsimpleQuery\n(\nSELECT * FROM users;\n)\n \n{\n \nrow\n \nin\n\n \nprint\n(\nrow\n)\n \n// [PostgreSQLColumn: PostgreSQLData]\n\n \n}\n\n\n}\n\n\nprint\n(\ndone\n)\n \n// Future\nVoid\n\n\n\n\n\n\nParameterized Query\n\n\nPostgreSQL also supports sending parameterized queries (sometimes called prepared statements). This method allows you to insert data placeholders into the SQL string and send the values separately.\n\n\nData sent via parameterized queries is binary encoded, making it more efficient for sending some data types. In general, you should use parameterized queries where ever possible.\n\n\nlet\n \nusers\n \n=\n \nreq\n.\nwithPooledConnection\n(\nto\n:\n \n.\npsql\n)\n \n{\n \nconn\n \nin\n\n \nreturn\n \ntry\n \nconn\n.\nquery\n(\nSELECT * users WHERE name = $1;\n,\n \n[\nVapor\n])\n\n\n}\n\n\nprint\n(\nusers\n)\n \n// Future\n[[PostgreSQLColumn: PostgreSQLData]]\n\n\n\n\n\n\nYou can also provide a callback, similar to simple queries, for handling each row individually.", + "title": "PostgreSQL Core" + }, + { + "location": "/postgresql/core/#postgresql-core", + "text": "PostgreSQL ( vapor/postgresql ) is a pure-Swift (no libpq dependency), event-driven, non-blocking driver for PostgreSQL. It's build on top of the Swift NIO networking library. Seealso The higher-level, Fluent PostgreSQL ORM guide is located at PostgreSQL Fluent Using just the PostgreSQL package for your project may be a good idea if any of the following are true. You have an existing DB with non-standard structure. You rely heavily on custom or complex SQL queries. You just plain don't like ORMs. PostgreSQL core is built on top of DatabaseKit which provides some conveniences like connection pooling and integrations with Vapor's Services architecture. Tip Even if you do choose to use Fluent PostgreSQL , all of the features of PostgreSQL core will be available to you.", + "title": "PostgreSQL Core" + }, + { + "location": "/postgresql/core/#getting-started", + "text": "Let's take a look at how you can get started using PostgreSQL core.", + "title": "Getting Started" + }, + { + "location": "/postgresql/core/#package", + "text": "The first step to using PostgreSQL core is adding it as a dependency to your project in your SPM package manifest file. // swift-tools-version:4.0 import PackageDescription let package = Package ( \n name : MyApp , \n dependencies : [ \n /// Any other dependencies ... \n\n // \ud83d\udc18 Non-blocking, event-driven Swift client for PostgreSQL. \n . package ( url : https://github.com/vapor/postgresql.git , from : 1.0.0-rc ), \n ], \n targets : [ \n . target ( name : App , dependencies : [ PostgreSQL , ...]), \n . target ( name : Run , dependencies : [ App ]), \n . testTarget ( name : AppTests , dependencies : [ App ]), \n ] ) Don't forget to add the module as a dependency in the targets array. Once you have added the dependency, regenerate your Xcode project with the following command: vapor xcode", + "title": "Package" + }, + { + "location": "/postgresql/core/#config", + "text": "The next step is to configure the database in your configure.swift file. import PostgreSQL /// ... /// Register providers first try services . register ( PostgreSQLProvider ()) Registering the provider will add all of the services required for PostgreSQL to work properly. It also includes a default database config struct that uses typical development environment credentials. You can of course override this config struct if you have non-standard credentials. /// Register custom PostgreSQL Config let psqlConfig = PostgreSQLDatabaseConfig ( hostname : localhost , port : 5432 , username : vapor ) services . register ( psqlConfig )", + "title": "Config" + }, + { + "location": "/postgresql/core/#query", + "text": "Now that the database is configured, you can make your first query. router . get ( psql-version ) { req - Future String in \n return req . withPooledConnection ( to : . psql ) { conn in \n return try conn . query ( select version() as v; ). map ( to : String . self ) { rows in \n return try rows [ 0 ]. firstValue ( forColumn : v )?. decode ( String . self ) ?? n/a \n } \n } } Visiting this route should display your PostgreSQL version.", + "title": "Query" + }, + { + "location": "/postgresql/core/#connection", + "text": "A PostgreSQLConnection is normally created using the Request container and can perform two different types of queries.", + "title": "Connection" + }, + { + "location": "/postgresql/core/#create", + "text": "There are two methods for creating a PostgreSQLConnection . return req . withPooledConnection ( to : . psql ) { conn in \n /// ... } return req . withConnection ( to : . psql ) { conn in \n /// ... } As the names imply, withPooledConnection(to:) utilizes a connection pool. withConnection(to:) does not. Connection pooling is a great way to ensure your application does not exceed the limits of your database, even under peak load.", + "title": "Create" + }, + { + "location": "/postgresql/core/#simply-query", + "text": "Use .simpleQuery(_:) to perform a query on your PostgreSQL database that does not bind any parameters. Some queries you send to PostgreSQL may actually require that you use the simpleQuery(_:) method instead of the parameterized method. Note This method sends and receives data as text-encoded, meaning it is not optimal for transmitting things like integers. let rows = req . withPooledConnection ( to : . psql ) { conn in \n return conn . simpleQuery ( SELECT * FROM users; ) } print ( rows ) // Future [[PostgreSQLColumn: PostgreSQLData]] You can also choose to receive each row in a callback, which is great for conserving memory for large queries. let done = req . withPooledConnection ( to : . psql ) { conn in \n return conn . simpleQuery ( SELECT * FROM users; ) { row in \n print ( row ) // [PostgreSQLColumn: PostgreSQLData] \n } } print ( done ) // Future Void", + "title": "Simply Query" + }, + { + "location": "/postgresql/core/#parameterized-query", + "text": "PostgreSQL also supports sending parameterized queries (sometimes called prepared statements). This method allows you to insert data placeholders into the SQL string and send the values separately. Data sent via parameterized queries is binary encoded, making it more efficient for sending some data types. In general, you should use parameterized queries where ever possible. let users = req . withPooledConnection ( to : . psql ) { conn in \n return try conn . query ( SELECT * users WHERE name = $1; , [ Vapor ]) } print ( users ) // Future [[PostgreSQLColumn: PostgreSQLData]] You can also provide a callback, similar to simple queries, for handling each row individually.", + "title": "Parameterized Query" + }, + { + "location": "/mysql/getting-started/", + "text": "Getting Started with MySQL\n\n\nMySQL\n is a widely-used, open-source database. It has an extremely popular wire-protocol that other open-source databases like \nMariaDB\n also support.\n\n\nYou can use MySQL with Vapor (or any server-side Swift framework) by either:\n\n\n\n\nUsing \nFluent MySQL\n ORM.\n\n\nUse just \nMySQL core\n.\n\n\n\n\nWe recommend using the ORM since it does a lot of the hard work for you. Check out the respective guides to learn more.", + "title": "Getting Started" + }, + { + "location": "/mysql/getting-started/#getting-started-with-mysql", + "text": "MySQL is a widely-used, open-source database. It has an extremely popular wire-protocol that other open-source databases like MariaDB also support. You can use MySQL with Vapor (or any server-side Swift framework) by either: Using Fluent MySQL ORM. Use just MySQL core . We recommend using the ORM since it does a lot of the hard work for you. Check out the respective guides to learn more.", + "title": "Getting Started with MySQL" + }, + { + "location": "/mysql/fluent/", + "text": "Fluent MySQL\n\n\nFluent MySQL (\nvapor/fluent-mysql\n) is a type-safe, fast, and easy-to-use ORM for MySQL built on top of \nFluent\n.\n\n\n\n\nSeealso\n\n\nThe Fluent MySQL package is built on top of \nFluent\n and the pure Swift, NIO-based \nMySQL core\n. You should refer to their guides for more information about subjects not covered here.\n\n\n\n\nGetting Started\n\n\nThis section will show you how to add Fluent MySQL to your project and create your first \nMySQLModel\n.\n\n\nPackage\n\n\nThe first step to using Fluent MySQL is adding it as a dependency to your project in your SPM package manifest file.\n\n\n// swift-tools-version:4.0\n\n\nimport\n \nPackageDescription\n\n\n\nlet\n \npackage\n \n=\n \nPackage\n(\n\n \nname\n:\n \nMyApp\n,\n\n \ndependencies\n:\n \n[\n\n \n/// Any other dependencies ...\n\n\n \n// \ud83d\udd8b\ud83d\udc2c Swift ORM (queries, models, relations, etc) built on MySQL.\n\n \n.\npackage\n(\nurl\n:\n \nhttps://github.com/vapor/fluent-mysql.git\n,\n \nfrom\n:\n \n3.0.0-rc\n),\n\n \n],\n\n \ntargets\n:\n \n[\n\n \n.\ntarget\n(\nname\n:\n \nApp\n,\n \ndependencies\n:\n \n[\nFluentMySQL\n,\n \n...]),\n\n \n.\ntarget\n(\nname\n:\n \nRun\n,\n \ndependencies\n:\n \n[\nApp\n]),\n\n \n.\ntestTarget\n(\nname\n:\n \nAppTests\n,\n \ndependencies\n:\n \n[\nApp\n]),\n\n \n]\n\n\n)\n\n\n\n\n\n\nDon't forget to add the module as a dependency in the \ntargets\n array. Once you have added the dependency, regenerate your Xcode project with the following command:\n\n\nvapor xcode\n\n\n\n\n\nModel\n\n\nNow let's create our first \nMySQLModel\n. Models represent tables in your MySQL database and they are the primary method of interacting with your data. \n\n\n/// A simple user.\n\n\nfinal\n \nclass\n \nUser\n:\n \nMySQLModel\n \n{\n\n \n/// The unique identifier for this user.\n\n \nvar\n \nid\n:\n \nInt\n?\n\n\n \n/// The user\ns full name.\n\n \nvar\n \nname\n:\n \nString\n\n\n \n/// The user\ns current age in years.\n\n \nvar\n \nage\n:\n \nInt\n\n\n \n/// Creates a new user.\n\n \ninit\n(\nid\n:\n \nInt\n?\n \n=\n \nnil\n,\n \nname\n:\n \nString\n,\n \nage\n:\n \nInt\n)\n \n{\n\n \nself\n.\nid\n \n=\n \nid\n\n \nself\n.\nname\n \n=\n \nname\n\n \nself\n.\nage\n \n=\n \nage\n\n \n}\n\n\n}\n\n\n\n\n\n\nThe example above shows a \nMySQLModel\n for a simple model representing a user. You can make both \nstruct\ns and \nclass\nes a model. You can even conform types that come from external modules. The only requirement is that these types conform to \nCodable\n, which must be declared on the base type for synthesized (automatic) conformance.\n\n\nStandard practice with MySQL databases is using an auto-generated \nINTEGER\n for creating and storing unique identifiers in the \nid\n column. It's also possible to use \nUUID\ns or even \nString\ns for your identifiers. There are convenience protocol for that. \n\n\n\n\n\n\n\n\nprotocol\n\n\ntype\n\n\nkey\n\n\n\n\n\n\n\n\n\n\nMySQLModel\n\n\nInt\n\n\nid\n\n\n\n\n\n\nMySQLUUIDModel\n\n\nUUID\n\n\nid\n\n\n\n\n\n\nMySQLStringModel\n\n\nString\n\n\nid\n\n\n\n\n\n\n\n\n\n\nSeealso\n\n\nTake a look at \nFluent \n Model\n for more information on creating models with custom ID types and keys.\n\n\n\n\nMigration\n\n\nAll of your models (with some rare exceptions) should have a corresponding table\nor \nschema\nin your database. You can use a \nFluent \n Migration\n to automatically generate this schema in a testable, maintainable way. Fluent makes it easy to automatically generate a migration for your model\n\n\n\n\nTip\n\n\nIf you are creating models to represent an existing table or database, you can skip this step.\n\n\n\n\n/// Allows `User` to be used as a migration.\n\n\nextension\n \nUser\n:\n \nMigration\n \n{\n \n}\n\n\n\n\n\n\nThat's all it takes. Fluent uses Codable to analyze your model and will attempt to create the best possible schema for it.\n\n\nTake a look at \nFluent \n Migration\n if you are interested in customizing this migration.\n\n\nConfigure\n\n\nThe final step is to configure your database. At a minimum, this requires adding two things to your \nconfigure.swift\n file.\n\n\n\n\nFluentMySQLProvider\n\n\nMigrationConfig\n\n\n\n\nLet's take a look.\n\n\nimport\n \nFluentMySQL\n\n\n\n/// ...\n\n\n\n/// Register providers first\n\n\ntry\n \nservices\n.\nregister\n(\nFluentMySQLProvider\n())\n\n\n\n/// Configure migrations\n\n\nvar\n \nmigrations\n \n=\n \nMigrationConfig\n()\n\n\nmigrations\n.\nadd\n(\nmodel\n:\n \nUser\n.\nself\n,\n \ndatabase\n:\n \n.\nmysql\n)\n\n\nservices\n.\nregister\n(\nmigrations\n)\n\n\n\n/// Other services....\n\n\n\n\n\n\nRegistering the provider will add all of the services required for Fluent MySQL to work properly. It also includes a default database config struct that uses typical development environment credentials. \n\n\nYou can of course override this config struct if you have non-standard credentials.\n\n\n/// Register custom MySQL Config\n\n\nlet\n \nmysqlConfig\n \n=\n \nMySQLDatabaseConfig\n(\nhostname\n:\n \nlocalhost\n,\n \nport\n:\n \n3306\n,\n \nusername\n:\n \nvapor\n)\n\n\nservices\n.\nregister\n(\nmysqlConfig\n)\n\n\n\n\n\n\nOnce you have the \nMigrationConfig\n added, you should be able to run your application and see the following:\n\n\nMigrating mysql DB\nMigrations \ncomplete\n\nServer starting on http://localhost:8080\n\n\n\n\n\nQuery\n\n\nNow that you have created a model and a corresponding schema in your database, let's make your first query.\n\n\nrouter\n.\nget\n(\nusers\n)\n \n{\n \nreq\n \nin\n\n \nreturn\n \nUser\n.\nquery\n(\non\n:\n \nreq\n).\nall\n()\n\n\n}\n\n\n\n\n\n\nIf you run your app, and query that route, you should see an empty array returned. Now you just need to add some users! Congratulations on getting your first Fluent MySQL model and migration working.\n\n\nConnection\n\n\nWith Fluent, you always have access to the underlying database driver. Using this underlying driver to perform a query is sometimes called a \"raw query\".\n\n\nLet's take a look at a raw MySQL query.\n\n\nrouter\n.\nget\n(\nmysql-version\n)\n \n{\n \nreq\n \n-\n \nFuture\nString\n \nin\n\n \nreturn\n \nreq\n.\nwithPooledConnection\n(\nto\n:\n \n.\nmysql\n)\n \n{\n \nconn\n \nin\n\n \nreturn\n \ntry\n \nconn\n.\nquery\n(\nselect @@version as v;\n).\nmap\n(\nto\n:\n \nString\n.\nself\n)\n \n{\n \nrows\n \nin\n\n \nreturn\n \ntry\n \nrows\n[\n0\n].\nfirstValue\n(\nforColumn\n:\n \nv\n)?.\ndecode\n(\nString\n.\nself\n)\n \n??\n \nn/a\n\n \n}\n\n \n}\n\n\n}\n\n\n\n\n\n\nIn the above example, \nwithPooledConnection(to:)\n is used to create a connection to the database identified by \n.mysql\n. This is the default database identifier. See \nFluent \n Database\n to learn more.\n\n\nOnce we have the \nMySQLConnection\n, we can perform a query on it. You can learn more about the methods available in \nMySQL \n Core\n.", + "title": "Fluent MySQL" + }, + { + "location": "/mysql/fluent/#fluent-mysql", + "text": "Fluent MySQL ( vapor/fluent-mysql ) is a type-safe, fast, and easy-to-use ORM for MySQL built on top of Fluent . Seealso The Fluent MySQL package is built on top of Fluent and the pure Swift, NIO-based MySQL core . You should refer to their guides for more information about subjects not covered here.", + "title": "Fluent MySQL" + }, + { + "location": "/mysql/fluent/#getting-started", + "text": "This section will show you how to add Fluent MySQL to your project and create your first MySQLModel .", + "title": "Getting Started" + }, + { + "location": "/mysql/fluent/#package", + "text": "The first step to using Fluent MySQL is adding it as a dependency to your project in your SPM package manifest file. // swift-tools-version:4.0 import PackageDescription let package = Package ( \n name : MyApp , \n dependencies : [ \n /// Any other dependencies ... \n\n // \ud83d\udd8b\ud83d\udc2c Swift ORM (queries, models, relations, etc) built on MySQL. \n . package ( url : https://github.com/vapor/fluent-mysql.git , from : 3.0.0-rc ), \n ], \n targets : [ \n . target ( name : App , dependencies : [ FluentMySQL , ...]), \n . target ( name : Run , dependencies : [ App ]), \n . testTarget ( name : AppTests , dependencies : [ App ]), \n ] ) Don't forget to add the module as a dependency in the targets array. Once you have added the dependency, regenerate your Xcode project with the following command: vapor xcode", + "title": "Package" + }, + { + "location": "/mysql/fluent/#model", + "text": "Now let's create our first MySQLModel . Models represent tables in your MySQL database and they are the primary method of interacting with your data. /// A simple user. final class User : MySQLModel { \n /// The unique identifier for this user. \n var id : Int ? \n\n /// The user s full name. \n var name : String \n\n /// The user s current age in years. \n var age : Int \n\n /// Creates a new user. \n init ( id : Int ? = nil , name : String , age : Int ) { \n self . id = id \n self . name = name \n self . age = age \n } } The example above shows a MySQLModel for a simple model representing a user. You can make both struct s and class es a model. You can even conform types that come from external modules. The only requirement is that these types conform to Codable , which must be declared on the base type for synthesized (automatic) conformance. Standard practice with MySQL databases is using an auto-generated INTEGER for creating and storing unique identifiers in the id column. It's also possible to use UUID s or even String s for your identifiers. There are convenience protocol for that. protocol type key MySQLModel Int id MySQLUUIDModel UUID id MySQLStringModel String id Seealso Take a look at Fluent Model for more information on creating models with custom ID types and keys.", + "title": "Model" + }, + { + "location": "/mysql/fluent/#migration", + "text": "All of your models (with some rare exceptions) should have a corresponding table or schema in your database. You can use a Fluent Migration to automatically generate this schema in a testable, maintainable way. Fluent makes it easy to automatically generate a migration for your model Tip If you are creating models to represent an existing table or database, you can skip this step. /// Allows `User` to be used as a migration. extension User : Migration { } That's all it takes. Fluent uses Codable to analyze your model and will attempt to create the best possible schema for it. Take a look at Fluent Migration if you are interested in customizing this migration.", + "title": "Migration" + }, + { + "location": "/mysql/fluent/#configure", + "text": "The final step is to configure your database. At a minimum, this requires adding two things to your configure.swift file. FluentMySQLProvider MigrationConfig Let's take a look. import FluentMySQL /// ... /// Register providers first try services . register ( FluentMySQLProvider ()) /// Configure migrations var migrations = MigrationConfig () migrations . add ( model : User . self , database : . mysql ) services . register ( migrations ) /// Other services.... Registering the provider will add all of the services required for Fluent MySQL to work properly. It also includes a default database config struct that uses typical development environment credentials. You can of course override this config struct if you have non-standard credentials. /// Register custom MySQL Config let mysqlConfig = MySQLDatabaseConfig ( hostname : localhost , port : 3306 , username : vapor ) services . register ( mysqlConfig ) Once you have the MigrationConfig added, you should be able to run your application and see the following: Migrating mysql DB\nMigrations complete \nServer starting on http://localhost:8080", + "title": "Configure" + }, + { + "location": "/mysql/fluent/#query", + "text": "Now that you have created a model and a corresponding schema in your database, let's make your first query. router . get ( users ) { req in \n return User . query ( on : req ). all () } If you run your app, and query that route, you should see an empty array returned. Now you just need to add some users! Congratulations on getting your first Fluent MySQL model and migration working.", + "title": "Query" + }, + { + "location": "/mysql/fluent/#connection", + "text": "With Fluent, you always have access to the underlying database driver. Using this underlying driver to perform a query is sometimes called a \"raw query\". Let's take a look at a raw MySQL query. router . get ( mysql-version ) { req - Future String in \n return req . withPooledConnection ( to : . mysql ) { conn in \n return try conn . query ( select @@version as v; ). map ( to : String . self ) { rows in \n return try rows [ 0 ]. firstValue ( forColumn : v )?. decode ( String . self ) ?? n/a \n } \n } } In the above example, withPooledConnection(to:) is used to create a connection to the database identified by .mysql . This is the default database identifier. See Fluent Database to learn more. Once we have the MySQLConnection , we can perform a query on it. You can learn more about the methods available in MySQL Core .", + "title": "Connection" + }, + { + "location": "/mysql/core/", + "text": "MySQL Core\n\n\nMySQL (\nvapor/mysql\n) is a pure-Swift (no \nlibmysql\n dependency), event-driven, non-blocking driver for MySQL. It's build on top of the \nSwift NIO\n networking library.\n\n\n\n\nSeealso\n\n\nThe higher-level, Fluent MySQL ORM guide is located at \nMySQL \n Fluent\n\n\n\n\nUsing just the MySQL package for your project may be a good idea if any of the following are true.\n\n\n\n\nYou have an existing DB with non-standard structure.\n\n\nYou rely heavily on custom or complex SQL queries.\n\n\nYou just plain don't like ORMs.\n\n\n\n\nMySQL core is built on top of DatabaseKit which provides some conveniences like connection pooling and integrations with Vapor's \nServices\n architecture.\n\n\n\n\nTip\n\n\nEven if you do choose to use \nFluent MySQL\n, all of the features of MySQL core will be available to you.\n\n\n\n\nGetting Started\n\n\nLet's take a look at how you can get started using MySQL core.\n\n\nPackage\n\n\nThe first step to using MySQL core is adding it as a dependency to your project in your SPM package manifest file.\n\n\n// swift-tools-version:4.0\n\n\nimport\n \nPackageDescription\n\n\n\nlet\n \npackage\n \n=\n \nPackage\n(\n\n \nname\n:\n \nMyApp\n,\n\n \ndependencies\n:\n \n[\n\n \n/// Any other dependencies ...\n\n\n \n// \ud83d\udc2c Non-blocking, event-driven Swift client for MySQL.\n\n \n.\npackage\n(\nurl\n:\n \nhttps://github.com/vapor/mysql.git\n,\n \nfrom\n:\n \n3.0.0-rc\n),\n\n \n],\n\n \ntargets\n:\n \n[\n\n \n.\ntarget\n(\nname\n:\n \nApp\n,\n \ndependencies\n:\n \n[\nMySQL\n,\n \n...]),\n\n \n.\ntarget\n(\nname\n:\n \nRun\n,\n \ndependencies\n:\n \n[\nApp\n]),\n\n \n.\ntestTarget\n(\nname\n:\n \nAppTests\n,\n \ndependencies\n:\n \n[\nApp\n]),\n\n \n]\n\n\n)\n\n\n\n\n\n\nDon't forget to add the module as a dependency in the \ntargets\n array. Once you have added the dependency, regenerate your Xcode project with the following command:\n\n\nvapor xcode\n\n\n\n\n\nConfig\n\n\nThe next step is to configure the database in your \nconfigure.swift\n file.\n\n\nimport\n \nMySQL\n\n\n\n/// ...\n\n\n\n/// Register providers first\n\n\ntry\n \nservices\n.\nregister\n(\nMySQLProvider\n())\n\n\n\n\n\n\nRegistering the provider will add all of the services required for MySQL to work properly. It also includes a default database config struct that uses typical development environment credentials. \n\n\nYou can of course override this config struct if you have non-standard credentials.\n\n\n/// Register custom MySQL Config\n\n\nlet\n \nmysqlConfig\n \n=\n \nMySQLDatabaseConfig\n(\nhostname\n:\n \nlocalhost\n,\n \nport\n:\n \n3306\n,\n \nusername\n:\n \nvapor\n)\n\n\nservices\n.\nregister\n(\nmysqlConfig\n)\n\n\n\n\n\n\nQuery\n\n\nNow that the database is configured, you can make your first query.\n\n\nrouter\n.\nget\n(\nmysql-version\n)\n \n{\n \nreq\n \n-\n \nFuture\nString\n \nin\n\n \nreturn\n \nreq\n.\nwithPooledConnection\n(\nto\n:\n \n.\nmysql\n)\n \n{\n \nconn\n \nin\n\n \nreturn\n \ntry\n \nconn\n.\nquery\n(\nselect @@version as v;\n).\nmap\n(\nto\n:\n \nString\n.\nself\n)\n \n{\n \nrows\n \nin\n\n \nreturn\n \ntry\n \nrows\n[\n0\n].\nfirstValue\n(\nforColumn\n:\n \nv\n)?.\ndecode\n(\nString\n.\nself\n)\n \n??\n \nn/a\n\n \n}\n\n \n}\n\n\n}\n\n\n\n\n\n\nVisiting this route should display your MySQL version.\n\n\nConnection\n\n\nA \nMySQLConnection\n is normally created using the \nRequest\n container and can perform two different types of queries.\n\n\nCreate\n\n\nThere are two methods for creating a \nMySQLConnection\n.\n\n\nreturn\n \nreq\n.\nwithPooledConnection\n(\nto\n:\n \n.\nmysql\n)\n \n{\n \nconn\n \nin\n\n \n/// ...\n\n\n}\n\n\nreturn\n \nreq\n.\nwithConnection\n(\nto\n:\n \n.\nmysql\n)\n \n{\n \nconn\n \nin\n\n \n/// ...\n\n\n}\n\n\n\n\n\n\nAs the names imply, \nwithPooledConnection(to:)\n utilizes a connection pool. \nwithConnection(to:)\n does not. Connection pooling is a great way to ensure your application does not exceed the limits of your database, even under peak load.\n\n\nSimply Query\n\n\nUse \n.simpleQuery(_:)\n to perform a query on your MySQL database that does not bind any parameters. Some queries you send to MySQL may actually require that you use the \nsimpleQuery(_:)\n method instead of the parameterized method. \n\n\n\n\nNote\n\n\nThis method sends and receives data as text-encoded, meaning it is not optimal for transmitting things like integers.\n\n\n\n\nlet\n \nrows\n \n=\n \nreq\n.\nwithPooledConnection\n(\nto\n:\n \n.\nmysql\n)\n \n{\n \nconn\n \nin\n\n \nreturn\n \nconn\n.\nsimpleQuery\n(\nSELECT * FROM users;\n)\n\n\n}\n\n\nprint\n(\nrows\n)\n \n// Future\n[[MySQLColumn: MySQLData]]\n\n\n\n\n\n\nYou can also choose to receive each row in a callback, which is great for conserving memory for large queries.\n\n\nlet\n \ndone\n \n=\n \nreq\n.\nwithPooledConnection\n(\nto\n:\n \n.\nmysql\n)\n \n{\n \nconn\n \nin\n\n \nreturn\n \nconn\n.\nsimpleQuery\n(\nSELECT * FROM users;\n)\n \n{\n \nrow\n \nin\n\n \nprint\n(\nrow\n)\n \n// [MySQLColumn: MySQLData]\n\n \n}\n\n\n}\n\n\nprint\n(\ndone\n)\n \n// Future\nVoid\n\n\n\n\n\n\nParameterized Query\n\n\nMySQL also supports sending parameterized queries (sometimes called prepared statements). This method allows you to insert data placeholders into the SQL string and send the values separately.\n\n\nData sent via parameterized queries is binary encoded, making it more efficient for sending some data types. In general, you should use parameterized queries where ever possible.\n\n\nlet\n \nusers\n \n=\n \nreq\n.\nwithPooledConnection\n(\nto\n:\n \n.\nmysql\n)\n \n{\n \nconn\n \nin\n\n \nreturn\n \ntry\n \nconn\n.\nquery\n(\nSELECT * users WHERE name = $1;\n,\n \n[\nVapor\n])\n\n\n}\n\n\nprint\n(\nusers\n)\n \n// Future\n[[MySQLColumn: MySQLData]]\n\n\n\n\n\n\nYou can also provide a callback, similar to simple queries, for handling each row individually.", + "title": "MySQL Core" + }, + { + "location": "/mysql/core/#mysql-core", + "text": "MySQL ( vapor/mysql ) is a pure-Swift (no libmysql dependency), event-driven, non-blocking driver for MySQL. It's build on top of the Swift NIO networking library. Seealso The higher-level, Fluent MySQL ORM guide is located at MySQL Fluent Using just the MySQL package for your project may be a good idea if any of the following are true. You have an existing DB with non-standard structure. You rely heavily on custom or complex SQL queries. You just plain don't like ORMs. MySQL core is built on top of DatabaseKit which provides some conveniences like connection pooling and integrations with Vapor's Services architecture. Tip Even if you do choose to use Fluent MySQL , all of the features of MySQL core will be available to you.", + "title": "MySQL Core" + }, + { + "location": "/mysql/core/#getting-started", + "text": "Let's take a look at how you can get started using MySQL core.", + "title": "Getting Started" + }, + { + "location": "/mysql/core/#package", + "text": "The first step to using MySQL core is adding it as a dependency to your project in your SPM package manifest file. // swift-tools-version:4.0 import PackageDescription let package = Package ( \n name : MyApp , \n dependencies : [ \n /// Any other dependencies ... \n\n // \ud83d\udc2c Non-blocking, event-driven Swift client for MySQL. \n . package ( url : https://github.com/vapor/mysql.git , from : 3.0.0-rc ), \n ], \n targets : [ \n . target ( name : App , dependencies : [ MySQL , ...]), \n . target ( name : Run , dependencies : [ App ]), \n . testTarget ( name : AppTests , dependencies : [ App ]), \n ] ) Don't forget to add the module as a dependency in the targets array. Once you have added the dependency, regenerate your Xcode project with the following command: vapor xcode", + "title": "Package" + }, + { + "location": "/mysql/core/#config", + "text": "The next step is to configure the database in your configure.swift file. import MySQL /// ... /// Register providers first try services . register ( MySQLProvider ()) Registering the provider will add all of the services required for MySQL to work properly. It also includes a default database config struct that uses typical development environment credentials. You can of course override this config struct if you have non-standard credentials. /// Register custom MySQL Config let mysqlConfig = MySQLDatabaseConfig ( hostname : localhost , port : 3306 , username : vapor ) services . register ( mysqlConfig )", + "title": "Config" + }, + { + "location": "/mysql/core/#query", + "text": "Now that the database is configured, you can make your first query. router . get ( mysql-version ) { req - Future String in \n return req . withPooledConnection ( to : . mysql ) { conn in \n return try conn . query ( select @@version as v; ). map ( to : String . self ) { rows in \n return try rows [ 0 ]. firstValue ( forColumn : v )?. decode ( String . self ) ?? n/a \n } \n } } Visiting this route should display your MySQL version.", + "title": "Query" + }, + { + "location": "/mysql/core/#connection", + "text": "A MySQLConnection is normally created using the Request container and can perform two different types of queries.", + "title": "Connection" + }, + { + "location": "/mysql/core/#create", + "text": "There are two methods for creating a MySQLConnection . return req . withPooledConnection ( to : . mysql ) { conn in \n /// ... } return req . withConnection ( to : . mysql ) { conn in \n /// ... } As the names imply, withPooledConnection(to:) utilizes a connection pool. withConnection(to:) does not. Connection pooling is a great way to ensure your application does not exceed the limits of your database, even under peak load.", + "title": "Create" + }, + { + "location": "/mysql/core/#simply-query", + "text": "Use .simpleQuery(_:) to perform a query on your MySQL database that does not bind any parameters. Some queries you send to MySQL may actually require that you use the simpleQuery(_:) method instead of the parameterized method. Note This method sends and receives data as text-encoded, meaning it is not optimal for transmitting things like integers. let rows = req . withPooledConnection ( to : . mysql ) { conn in \n return conn . simpleQuery ( SELECT * FROM users; ) } print ( rows ) // Future [[MySQLColumn: MySQLData]] You can also choose to receive each row in a callback, which is great for conserving memory for large queries. let done = req . withPooledConnection ( to : . mysql ) { conn in \n return conn . simpleQuery ( SELECT * FROM users; ) { row in \n print ( row ) // [MySQLColumn: MySQLData] \n } } print ( done ) // Future Void", + "title": "Simply Query" + }, + { + "location": "/mysql/core/#parameterized-query", + "text": "MySQL also supports sending parameterized queries (sometimes called prepared statements). This method allows you to insert data placeholders into the SQL string and send the values separately. Data sent via parameterized queries is binary encoded, making it more efficient for sending some data types. In general, you should use parameterized queries where ever possible. let users = req . withPooledConnection ( to : . mysql ) { conn in \n return try conn . query ( SELECT * users WHERE name = $1; , [ Vapor ]) } print ( users ) // Future [[MySQLColumn: MySQLData]] You can also provide a callback, similar to simple queries, for handling each row individually.", + "title": "Parameterized Query" + }, + { + "location": "/sqlite/getting-started/", + "text": "Getting Started with SQLite\n\n\nSQLite\n is a open-source, embedded database. It's simplistic nature makes it a great candiate for prototyping and testing.\n\n\nYou can use SQLite with Vapor (or any server-side Swift framework) by either:\n\n\n\n\nUsing \nFluent SQLite\n ORM.\n\n\nUse just \nSQLite core\n.\n\n\n\n\nWe recommend using the ORM since it does a lot of the hard work for you. Check out the respective guides to learn more.", + "title": "Getting Started" + }, + { + "location": "/sqlite/getting-started/#getting-started-with-sqlite", + "text": "SQLite is a open-source, embedded database. It's simplistic nature makes it a great candiate for prototyping and testing. You can use SQLite with Vapor (or any server-side Swift framework) by either: Using Fluent SQLite ORM. Use just SQLite core . We recommend using the ORM since it does a lot of the hard work for you. Check out the respective guides to learn more.", + "title": "Getting Started with SQLite" + }, + { + "location": "/sqlite/fluent/", + "text": "Fluent SQLite\n\n\nFluent SQLite (\nvapor/fluent-sqlite\n) is a type-safe, fast, and easy-to-use ORM for SQLite built on top of \nFluent\n.\n\n\n\n\nSeealso\n\n\nThe Fluent SQLite package is built on top of \nFluent\n and the pure Swift, NIO-based \nSQLite core\n. You should refer to their guides for more information about subjects not covered here.\n\n\n\n\nGetting Started\n\n\nThis section will show you how to add Fluent SQLite to your project and create your first \nSQLiteModel\n.\n\n\nPackage\n\n\nThe first step to using Fluent SQLite is adding it as a dependency to your project in your SPM package manifest file.\n\n\n// swift-tools-version:4.0\n\n\nimport\n \nPackageDescription\n\n\n\nlet\n \npackage\n \n=\n \nPackage\n(\n\n \nname\n:\n \nMyApp\n,\n\n \ndependencies\n:\n \n[\n\n \n/// Any other dependencies ...\n\n\n \n// \ud83d\udd8b\ud83d\udc2c Swift ORM (queries, models, relations, etc) built on SQLite.\n\n \n.\npackage\n(\nurl\n:\n \nhttps://github.com/vapor/fluent-sqlite.git\n,\n \nfrom\n:\n \n3.0.0-rc\n),\n\n \n],\n\n \ntargets\n:\n \n[\n\n \n.\ntarget\n(\nname\n:\n \nApp\n,\n \ndependencies\n:\n \n[\nFluentSQLite\n,\n \n...]),\n\n \n.\ntarget\n(\nname\n:\n \nRun\n,\n \ndependencies\n:\n \n[\nApp\n]),\n\n \n.\ntestTarget\n(\nname\n:\n \nAppTests\n,\n \ndependencies\n:\n \n[\nApp\n]),\n\n \n]\n\n\n)\n\n\n\n\n\n\nDon't forget to add the module as a dependency in the \ntargets\n array. Once you have added the dependency, regenerate your Xcode project with the following command:\n\n\nvapor xcode\n\n\n\n\n\nModel\n\n\nNow let's create our first \nSQLiteModel\n. Models represent tables in your SQLite database and they are the primary method of interacting with your data. \n\n\n/// A simple user.\n\n\nfinal\n \nclass\n \nUser\n:\n \nSQLiteModel\n \n{\n\n \n/// The unique identifier for this user.\n\n \nvar\n \nid\n:\n \nInt\n?\n\n\n \n/// The user\ns full name.\n\n \nvar\n \nname\n:\n \nString\n\n\n \n/// The user\ns current age in years.\n\n \nvar\n \nage\n:\n \nInt\n\n\n \n/// Creates a new user.\n\n \ninit\n(\nid\n:\n \nInt\n?\n \n=\n \nnil\n,\n \nname\n:\n \nString\n,\n \nage\n:\n \nInt\n)\n \n{\n\n \nself\n.\nid\n \n=\n \nid\n\n \nself\n.\nname\n \n=\n \nname\n\n \nself\n.\nage\n \n=\n \nage\n\n \n}\n\n\n}\n\n\n\n\n\n\nThe example above shows a \nSQLiteModel\n for a simple model representing a user. You can make both \nstruct\ns and \nclass\nes a model. You can even conform types that come from external modules. The only requirement is that these types conform to \nCodable\n, which must be declared on the base type for synthesized (automatic) conformance.\n\n\nStandard practice with SQLite databases is using an auto-generated \nINTEGER\n for creating and storing unique identifiers in the \nid\n column. It's also possible to use \nUUID\ns or even \nString\ns for your identifiers. There are convenience protocol for that. \n\n\n\n\n\n\n\n\nprotocol\n\n\ntype\n\n\nkey\n\n\n\n\n\n\n\n\n\n\nSQLiteModel\n\n\nInt\n\n\nid\n\n\n\n\n\n\nSQLiteUUIDModel\n\n\nUUID\n\n\nid\n\n\n\n\n\n\nSQLiteStringModel\n\n\nString\n\n\nid\n\n\n\n\n\n\n\n\n\n\nSeealso\n\n\nTake a look at \nFluent \n Model\n for more information on creating models with custom ID types and keys.\n\n\n\n\nMigration\n\n\nAll of your models (with some rare exceptions) should have a corresponding table\nor \nschema\nin your database. You can use a \nFluent \n Migration\n to automatically generate this schema in a testable, maintainable way. Fluent makes it easy to automatically generate a migration for your model\n\n\n\n\nTip\n\n\nIf you are creating models to represent an existing table or database, you can skip this step.\n\n\n\n\n/// Allows `User` to be used as a migration.\n\n\nextension\n \nUser\n:\n \nMigration\n \n{\n \n}\n\n\n\n\n\n\nThat's all it takes. Fluent uses Codable to analyze your model and will attempt to create the best possible schema for it.\n\n\nTake a look at \nFluent \n Migration\n if you are interested in customizing this migration.\n\n\nConfigure\n\n\nThe final step is to configure your database. At a minimum, this requires adding two things to your \nconfigure.swift\n file.\n\n\n\n\nFluentSQLiteProvider\n\n\nMigrationConfig\n\n\n\n\nLet's take a look.\n\n\nimport\n \nFluentSQLite\n\n\n\n/// ...\n\n\n\n/// Register providers first\n\n\ntry\n \nservices\n.\nregister\n(\nFluentSQLiteProvider\n())\n\n\n\n/// Configure migrations\n\n\nvar\n \nmigrations\n \n=\n \nMigrationConfig\n()\n\n\nmigrations\n.\nadd\n(\nmodel\n:\n \nUser\n.\nself\n,\n \ndatabase\n:\n \n.\nsqlite\n)\n\n\nservices\n.\nregister\n(\nmigrations\n)\n\n\n\n/// Other services....\n\n\n\n\n\n\nRegistering the provider will add all of the services required for Fluent SQLite to work properly. It also includes a default database config struct that uses typical development environment credentials. \n\n\nYou can of course override this config struct if you have non-standard credentials.\n\n\n/// Register custom SQLite Config\n\n\nlet\n \nsqliteConfig\n \n=\n \nSQLiteDatabaseConfig\n(\nhostname\n:\n \nlocalhost\n,\n \nport\n:\n \n5432\n,\n \nusername\n:\n \nvapor\n)\n\n\nservices\n.\nregister\n(\nsqliteConfig\n)\n\n\n\n\n\n\nOnce you have the \nMigrationConfig\n added, you should be able to run your application and see the following:\n\n\nMigrating sqlite DB\nMigrations \ncomplete\n\nServer starting on http://localhost:8080\n\n\n\n\n\nQuery\n\n\nNow that you have created a model and a corresponding schema in your database, let's make your first query.\n\n\nrouter\n.\nget\n(\nusers\n)\n \n{\n \nreq\n \nin\n\n \nreturn\n \nUser\n.\nquery\n(\non\n:\n \nreq\n).\nall\n()\n\n\n}\n\n\n\n\n\n\nIf you run your app, and query that route, you should see an empty array returned. Now you just need to add some users! Congratulations on getting your first Fluent SQLite model and migration working.\n\n\nConnection\n\n\nWith Fluent, you always have access to the underlying database driver. Using this underlying driver to perform a query is sometimes called a \"raw query\".\n\n\nLet's take a look at a raw SQLite query.\n\n\nrouter\n.\nget\n(\nsqlite-version\n)\n \n{\n \nreq\n \n-\n \nFuture\nString\n \nin\n\n \nreturn\n \nreq\n.\nwithPooledConnection\n(\nto\n:\n \n.\nsqlite\n)\n \n{\n \nconn\n \nin\n\n \nreturn\n \ntry\n \nconn\n.\nquery\n(\nselect sqlite_version() as v;\n).\nmap\n(\nto\n:\n \nString\n.\nself\n)\n \n{\n \nrows\n \nin\n\n \nreturn\n \ntry\n \nrows\n[\n0\n].\nfirstValue\n(\nforColumn\n:\n \nv\n)?.\ndecode\n(\nString\n.\nself\n)\n \n??\n \nn/a\n\n \n}\n\n \n}\n\n\n}\n\n\n\n\n\n\nIn the above example, \nwithPooledConnection(to:)\n is used to create a connection to the database identified by \n.sqlite\n. This is the default database identifier. See \nFluent \n Database\n to learn more.\n\n\nOnce we have the \nSQLiteConnection\n, we can perform a query on it. You can learn more about the methods available in \nSQLite \n Core\n.", + "title": "Fluent SQLite" + }, + { + "location": "/sqlite/fluent/#fluent-sqlite", + "text": "Fluent SQLite ( vapor/fluent-sqlite ) is a type-safe, fast, and easy-to-use ORM for SQLite built on top of Fluent . Seealso The Fluent SQLite package is built on top of Fluent and the pure Swift, NIO-based SQLite core . You should refer to their guides for more information about subjects not covered here.", + "title": "Fluent SQLite" + }, + { + "location": "/sqlite/fluent/#getting-started", + "text": "This section will show you how to add Fluent SQLite to your project and create your first SQLiteModel .", + "title": "Getting Started" + }, + { + "location": "/sqlite/fluent/#package", + "text": "The first step to using Fluent SQLite is adding it as a dependency to your project in your SPM package manifest file. // swift-tools-version:4.0 import PackageDescription let package = Package ( \n name : MyApp , \n dependencies : [ \n /// Any other dependencies ... \n\n // \ud83d\udd8b\ud83d\udc2c Swift ORM (queries, models, relations, etc) built on SQLite. \n . package ( url : https://github.com/vapor/fluent-sqlite.git , from : 3.0.0-rc ), \n ], \n targets : [ \n . target ( name : App , dependencies : [ FluentSQLite , ...]), \n . target ( name : Run , dependencies : [ App ]), \n . testTarget ( name : AppTests , dependencies : [ App ]), \n ] ) Don't forget to add the module as a dependency in the targets array. Once you have added the dependency, regenerate your Xcode project with the following command: vapor xcode", + "title": "Package" + }, + { + "location": "/sqlite/fluent/#model", + "text": "Now let's create our first SQLiteModel . Models represent tables in your SQLite database and they are the primary method of interacting with your data. /// A simple user. final class User : SQLiteModel { \n /// The unique identifier for this user. \n var id : Int ? \n\n /// The user s full name. \n var name : String \n\n /// The user s current age in years. \n var age : Int \n\n /// Creates a new user. \n init ( id : Int ? = nil , name : String , age : Int ) { \n self . id = id \n self . name = name \n self . age = age \n } } The example above shows a SQLiteModel for a simple model representing a user. You can make both struct s and class es a model. You can even conform types that come from external modules. The only requirement is that these types conform to Codable , which must be declared on the base type for synthesized (automatic) conformance. Standard practice with SQLite databases is using an auto-generated INTEGER for creating and storing unique identifiers in the id column. It's also possible to use UUID s or even String s for your identifiers. There are convenience protocol for that. protocol type key SQLiteModel Int id SQLiteUUIDModel UUID id SQLiteStringModel String id Seealso Take a look at Fluent Model for more information on creating models with custom ID types and keys.", + "title": "Model" + }, + { + "location": "/sqlite/fluent/#migration", + "text": "All of your models (with some rare exceptions) should have a corresponding table or schema in your database. You can use a Fluent Migration to automatically generate this schema in a testable, maintainable way. Fluent makes it easy to automatically generate a migration for your model Tip If you are creating models to represent an existing table or database, you can skip this step. /// Allows `User` to be used as a migration. extension User : Migration { } That's all it takes. Fluent uses Codable to analyze your model and will attempt to create the best possible schema for it. Take a look at Fluent Migration if you are interested in customizing this migration.", + "title": "Migration" + }, + { + "location": "/sqlite/fluent/#configure", + "text": "The final step is to configure your database. At a minimum, this requires adding two things to your configure.swift file. FluentSQLiteProvider MigrationConfig Let's take a look. import FluentSQLite /// ... /// Register providers first try services . register ( FluentSQLiteProvider ()) /// Configure migrations var migrations = MigrationConfig () migrations . add ( model : User . self , database : . sqlite ) services . register ( migrations ) /// Other services.... Registering the provider will add all of the services required for Fluent SQLite to work properly. It also includes a default database config struct that uses typical development environment credentials. You can of course override this config struct if you have non-standard credentials. /// Register custom SQLite Config let sqliteConfig = SQLiteDatabaseConfig ( hostname : localhost , port : 5432 , username : vapor ) services . register ( sqliteConfig ) Once you have the MigrationConfig added, you should be able to run your application and see the following: Migrating sqlite DB\nMigrations complete \nServer starting on http://localhost:8080", + "title": "Configure" + }, + { + "location": "/sqlite/fluent/#query", + "text": "Now that you have created a model and a corresponding schema in your database, let's make your first query. router . get ( users ) { req in \n return User . query ( on : req ). all () } If you run your app, and query that route, you should see an empty array returned. Now you just need to add some users! Congratulations on getting your first Fluent SQLite model and migration working.", + "title": "Query" + }, + { + "location": "/sqlite/fluent/#connection", + "text": "With Fluent, you always have access to the underlying database driver. Using this underlying driver to perform a query is sometimes called a \"raw query\". Let's take a look at a raw SQLite query. router . get ( sqlite-version ) { req - Future String in \n return req . withPooledConnection ( to : . sqlite ) { conn in \n return try conn . query ( select sqlite_version() as v; ). map ( to : String . self ) { rows in \n return try rows [ 0 ]. firstValue ( forColumn : v )?. decode ( String . self ) ?? n/a \n } \n } } In the above example, withPooledConnection(to:) is used to create a connection to the database identified by .sqlite . This is the default database identifier. See Fluent Database to learn more. Once we have the SQLiteConnection , we can perform a query on it. You can learn more about the methods available in SQLite Core .", + "title": "Connection" + }, + { + "location": "/sqlite/core/", + "text": "SQLite Core\n\n\nSQLite (\nvapor/sqlite\n) is a wrapper around the \nlibsqlite\n C-library.\n\n\n\n\nSeealso\n\n\nThe higher-level, Fluent SQLite ORM guide is located at \nSQLite \n Fluent\n\n\n\n\nUsing just the SQLite package for your project may be a good idea if any of the following are true.\n\n\n\n\nYou have an existing DB with non-standard structure.\n\n\nYou rely heavily on custom or complex SQL queries.\n\n\nYou just plain don't like ORMs.\n\n\n\n\nSQLite core is built on top of DatabaseKit which provides some conveniences like connection pooling and integrations with Vapor's \nServices\n architecture.\n\n\n\n\nTip\n\n\nEven if you do choose to use \nFluent SQLite\n, all of the features of SQLite core will be available to you.\n\n\n\n\nGetting Started\n\n\nLet's take a look at how you can get started using SQLite core.\n\n\nPackage\n\n\nThe first step to using SQLite core is adding it as a dependency to your project in your SPM package manifest file.\n\n\n// swift-tools-version:4.0\n\n\nimport\n \nPackageDescription\n\n\n\nlet\n \npackage\n \n=\n \nPackage\n(\n\n \nname\n:\n \nMyApp\n,\n\n \ndependencies\n:\n \n[\n\n \n/// Any other dependencies ...\n\n\n \n// \ud83d\udd35 SQLite 3 wrapper for Swift.\n\n \n.\npackage\n(\nurl\n:\n \nhttps://github.com/vapor/sqlite.git\n,\n \nfrom\n:\n \n3.0.0-rc\n),\n\n \n],\n\n \ntargets\n:\n \n[\n\n \n.\ntarget\n(\nname\n:\n \nApp\n,\n \ndependencies\n:\n \n[\nSQLite\n,\n \n...]),\n\n \n.\ntarget\n(\nname\n:\n \nRun\n,\n \ndependencies\n:\n \n[\nApp\n]),\n\n \n.\ntestTarget\n(\nname\n:\n \nAppTests\n,\n \ndependencies\n:\n \n[\nApp\n]),\n\n \n]\n\n\n)\n\n\n\n\n\n\nDon't forget to add the module as a dependency in the \ntargets\n array. Once you have added the dependency, regenerate your Xcode project with the following command:\n\n\nvapor xcode\n\n\n\n\n\nConfig\n\n\nThe next step is to configure the database in your \nconfigure.swift\n file.\n\n\nimport\n \nSQLite\n\n\n\n/// ...\n\n\n\n/// Register providers first\n\n\ntry\n \nservices\n.\nregister\n(\nSQLiteProvider\n())\n\n\n\n\n\n\nRegistering the provider will add all of the services required for SQLite to work properly. It also includes a default database config struct that uses an in-memory DB.\n\n\nYou can of course override this config struct if you have non-standard credentials.\n\n\n/// Register custom SQLite Config\n\n\nlet\n \nsqliteConfig\n \n=\n \nSQLiteDatabaseConfig\n(\nstorage\n:\n \n.\nmemory\n)\n\n\nservices\n.\nregister\n(\nsqliteConfig\n)\n\n\n\n\n\n\nQuery\n\n\nNow that the database is configured, you can make your first query.\n\n\nrouter\n.\nget\n(\nsqlite-version\n)\n \n{\n \nreq\n \n-\n \nFuture\nString\n \nin\n\n \nreturn\n \nreq\n.\nwithPooledConnection\n(\nto\n:\n \n.\nsqlite\n)\n \n{\n \nconn\n \nin\n\n \nreturn\n \ntry\n \nconn\n.\nquery\n(\nselect sqlite_version() as v;\n).\nmap\n(\nto\n:\n \nString\n.\nself\n)\n \n{\n \nrows\n \nin\n\n \nreturn\n \ntry\n \nrows\n[\n0\n].\nfirstValue\n(\nforColumn\n:\n \nv\n)?.\ndecode\n(\nString\n.\nself\n)\n \n??\n \nn/a\n\n \n}\n\n \n}\n\n\n}\n\n\n\n\n\n\nVisiting this route should display your SQLite version.\n\n\nConnection\n\n\nA \nSQLiteConnection\n is normally created using the \nRequest\n container and can perform two different types of queries.\n\n\nCreate\n\n\nThere are two methods for creating a \nSQLiteConnection\n.\n\n\nreturn\n \nreq\n.\nwithPooledConnection\n(\nto\n:\n \n.\nsqlite\n)\n \n{\n \nconn\n \nin\n\n \n/// ...\n\n\n}\n\n\nreturn\n \nreq\n.\nwithConnection\n(\nto\n:\n \n.\nsqlite\n)\n \n{\n \nconn\n \nin\n\n \n/// ...\n\n\n}\n\n\n\n\n\n\nAs the names imply, \nwithPooledConnection(to:)\n utilizes a connection pool. \nwithConnection(to:)\n does not. Connection pooling is a great way to ensure your application does not exceed the limits of your database, even under peak load.\n\n\nSimply Query\n\n\nUse \n.simpleQuery(_:)\n to perform a query on your SQLite database that does not bind any parameters. Some queries you send to SQLite may actually require that you use the \nsimpleQuery(_:)\n method instead of the parameterized method. \n\n\n\n\nNote\n\n\nThis method sends and receives data as text-encoded, meaning it is not optimal for transmitting things like integers.\n\n\n\n\nlet\n \nrows\n \n=\n \nreq\n.\nwithPooledConnection\n(\nto\n:\n \n.\nsqlite\n)\n \n{\n \nconn\n \nin\n\n \nreturn\n \nconn\n.\nsimpleQuery\n(\nSELECT * FROM users;\n)\n\n\n}\n\n\nprint\n(\nrows\n)\n \n// Future\n[[SQLiteColumn: SQLiteData]]\n\n\n\n\n\n\nYou can also choose to receive each row in a callback, which is great for conserving memory for large queries.\n\n\nlet\n \ndone\n \n=\n \nreq\n.\nwithPooledConnection\n(\nto\n:\n \n.\nsqlite\n)\n \n{\n \nconn\n \nin\n\n \nreturn\n \nconn\n.\nsimpleQuery\n(\nSELECT * FROM users;\n)\n \n{\n \nrow\n \nin\n\n \nprint\n(\nrow\n)\n \n// [SQLiteColumn: SQLiteData]\n\n \n}\n\n\n}\n\n\nprint\n(\ndone\n)\n \n// Future\nVoid\n\n\n\n\n\n\nParameterized Query\n\n\nSQLite also supports sending parameterized queries (sometimes called prepared statements). This method allows you to insert data placeholders into the SQL string and send the values separately.\n\n\nData sent via parameterized queries is binary encoded, making it more efficient for sending some data types. In general, you should use parameterized queries where ever possible.\n\n\nlet\n \nusers\n \n=\n \nreq\n.\nwithPooledConnection\n(\nto\n:\n \n.\nsqlite\n)\n \n{\n \nconn\n \nin\n\n \nreturn\n \ntry\n \nconn\n.\nquery\n(\nSELECT * users WHERE name = $1;\n,\n \n[\nVapor\n])\n\n\n}\n\n\nprint\n(\nusers\n)\n \n// Future\n[[SQLiteColumn: SQLiteData]]\n\n\n\n\n\n\nYou can also provide a callback, similar to simple queries, for handling each row individually.", + "title": "SQLite Core" + }, + { + "location": "/sqlite/core/#sqlite-core", + "text": "SQLite ( vapor/sqlite ) is a wrapper around the libsqlite C-library. Seealso The higher-level, Fluent SQLite ORM guide is located at SQLite Fluent Using just the SQLite package for your project may be a good idea if any of the following are true. You have an existing DB with non-standard structure. You rely heavily on custom or complex SQL queries. You just plain don't like ORMs. SQLite core is built on top of DatabaseKit which provides some conveniences like connection pooling and integrations with Vapor's Services architecture. Tip Even if you do choose to use Fluent SQLite , all of the features of SQLite core will be available to you.", + "title": "SQLite Core" + }, + { + "location": "/sqlite/core/#getting-started", + "text": "Let's take a look at how you can get started using SQLite core.", + "title": "Getting Started" + }, + { + "location": "/sqlite/core/#package", + "text": "The first step to using SQLite core is adding it as a dependency to your project in your SPM package manifest file. // swift-tools-version:4.0 import PackageDescription let package = Package ( \n name : MyApp , \n dependencies : [ \n /// Any other dependencies ... \n\n // \ud83d\udd35 SQLite 3 wrapper for Swift. \n . package ( url : https://github.com/vapor/sqlite.git , from : 3.0.0-rc ), \n ], \n targets : [ \n . target ( name : App , dependencies : [ SQLite , ...]), \n . target ( name : Run , dependencies : [ App ]), \n . testTarget ( name : AppTests , dependencies : [ App ]), \n ] ) Don't forget to add the module as a dependency in the targets array. Once you have added the dependency, regenerate your Xcode project with the following command: vapor xcode", + "title": "Package" + }, + { + "location": "/sqlite/core/#config", + "text": "The next step is to configure the database in your configure.swift file. import SQLite /// ... /// Register providers first try services . register ( SQLiteProvider ()) Registering the provider will add all of the services required for SQLite to work properly. It also includes a default database config struct that uses an in-memory DB. You can of course override this config struct if you have non-standard credentials. /// Register custom SQLite Config let sqliteConfig = SQLiteDatabaseConfig ( storage : . memory ) services . register ( sqliteConfig )", + "title": "Config" + }, + { + "location": "/sqlite/core/#query", + "text": "Now that the database is configured, you can make your first query. router . get ( sqlite-version ) { req - Future String in \n return req . withPooledConnection ( to : . sqlite ) { conn in \n return try conn . query ( select sqlite_version() as v; ). map ( to : String . self ) { rows in \n return try rows [ 0 ]. firstValue ( forColumn : v )?. decode ( String . self ) ?? n/a \n } \n } } Visiting this route should display your SQLite version.", + "title": "Query" + }, + { + "location": "/sqlite/core/#connection", + "text": "A SQLiteConnection is normally created using the Request container and can perform two different types of queries.", + "title": "Connection" + }, + { + "location": "/sqlite/core/#create", + "text": "There are two methods for creating a SQLiteConnection . return req . withPooledConnection ( to : . sqlite ) { conn in \n /// ... } return req . withConnection ( to : . sqlite ) { conn in \n /// ... } As the names imply, withPooledConnection(to:) utilizes a connection pool. withConnection(to:) does not. Connection pooling is a great way to ensure your application does not exceed the limits of your database, even under peak load.", + "title": "Create" + }, + { + "location": "/sqlite/core/#simply-query", + "text": "Use .simpleQuery(_:) to perform a query on your SQLite database that does not bind any parameters. Some queries you send to SQLite may actually require that you use the simpleQuery(_:) method instead of the parameterized method. Note This method sends and receives data as text-encoded, meaning it is not optimal for transmitting things like integers. let rows = req . withPooledConnection ( to : . sqlite ) { conn in \n return conn . simpleQuery ( SELECT * FROM users; ) } print ( rows ) // Future [[SQLiteColumn: SQLiteData]] You can also choose to receive each row in a callback, which is great for conserving memory for large queries. let done = req . withPooledConnection ( to : . sqlite ) { conn in \n return conn . simpleQuery ( SELECT * FROM users; ) { row in \n print ( row ) // [SQLiteColumn: SQLiteData] \n } } print ( done ) // Future Void", + "title": "Simply Query" + }, + { + "location": "/sqlite/core/#parameterized-query", + "text": "SQLite also supports sending parameterized queries (sometimes called prepared statements). This method allows you to insert data placeholders into the SQL string and send the values separately. Data sent via parameterized queries is binary encoded, making it more efficient for sending some data types. In general, you should use parameterized queries where ever possible. let users = req . withPooledConnection ( to : . sqlite ) { conn in \n return try conn . query ( SELECT * users WHERE name = $1; , [ Vapor ]) } print ( users ) // Future [[SQLiteColumn: SQLiteData]] You can also provide a callback, similar to simple queries, for handling each row individually.", + "title": "Parameterized Query" + }, + { + "location": "/leaf/getting-started/", + "text": "Leaf\n\n\nLeaf is a templating language that integrates with Futures, Reactive Streams and Codable. This section outlines how to import the Leaf package into a Vapor project.\n\n\nExample Folder Structure\n\n\nHello\n\u251c\u2500\u2500 Package.resolved\n\u251c\u2500\u2500 Package.swift\n\u251c\u2500\u2500 Public\n\u251c\u2500\u2500 Resources\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 Views\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2514\u2500\u2500 hello.leaf\n\u251c\u2500\u2500 Public\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 images (images resources)\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 styles (css resources)\n\u251c\u2500\u2500 Sources\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 App\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 boot.swift\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 configure.swift\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2514\u2500\u2500 routes.swift\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 Run\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 main.swift\n\u251c\u2500\u2500 Tests\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 AppTests\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2514\u2500\u2500 AppTests.swift\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 LinuxMain.swift\n\u2514\u2500\u2500 LICENSE\n\n\n\n\n\nAdding Leaf to your project\n\n\nThe easiest way to use Leaf with Vapor is to include the Leaf repository as a dependency in Package.swift:\n\n\n// swift-tools-version:4.0\n\n\nimport\n \nPackageDescription\n\n\n\nlet\n \npackage\n \n=\n \nPackage\n(\n\n \nname\n:\n \nproject1\n,\n\n \ndependencies\n:\n \n[\n\n \n// \ud83d\udca7 A server-side Swift web framework.\n\n \n.\npackage\n(\nurl\n:\n \nhttps://github.com/vapor/vapor.git\n,\n \n.\nbranch\n(\nbeta\n)),\n\n \n.\npackage\n(\nurl\n:\n \nhttps://github.com/vapor/leaf.git\n,\n \n.\nbranch\n(\nbeta\n)),\n\n \n],\n\n \ntargets\n:\n \n[\n\n \n.\ntarget\n(\n\n \nname\n:\n \nApp\n,\n\n \ndependencies\n:\n \n[\nVapor\n,\n \nLeaf\n]\n\n \n),\n\n \n.\ntarget\n(\nname\n:\n \nRun\n,\n \ndependencies\n:\n \n[\nApp\n]),\n\n \n.\ntestTarget\n(\nname\n:\n \nAppTests\n,\n \ndependencies\n:\n \n[\nApp\n]),\n\n \n]\n\n\n)\n\n\n\n\n\n\nThe Leaf package adds Leaf to your project, but to configure it for use you must modify configure.swift:\n\n\n\n\nAdd \nimport Leaf\n to the top of the file so that Leaf is available to use. You will also need to add this to any file that will render templates.\n\n\nAdd \ntry services.register(LeafProvider())\n to the \nconfigure()\n function so that routes may render Leaf templates as needed.\n\n\n\n\nSyntax Highlighting\n\n\nYou may also wish to install one these third-party packages that provide support for syntax highlighting in Leaf templates.\n\n\nAtom\n\n\nlanguage-leaf\n by ButkiewiczP\n\n\nXcode\n\n\nIt is not currently possible to implement Leaf Syntax Highlighting in Xcode, however, using Xcode's HTML Syntax Coloring can help a bit. Select one or more Leaf files and then choose Editor \n Syntax Coloring \n HTML. Your selected Leaf files will now use Xcode's HTML Syntax Coloring. Unfortunately the usefulness of this is limited because this association will be removed when \nvapor xcode\n is run.\n\n\nThere appears to be a way to \nmake Xcode file associations persist\n but that requires a bit more kung-fu.\n\n\nVS Code\n\n\nhtml-leaf\n by FranciscoAmado\n\n\nCLion \n AppCode\n\n\nSome preliminary work has been done to implement a Leaf Plugin for CLion \n AppCode but lack of skill and interest in Java has slowed progress! If you have IntelliJ SDK experience and want to help with this, message Tom Holland on \nVapor Slack", + "title": "Getting Started" + }, + { + "location": "/leaf/getting-started/#leaf", + "text": "Leaf is a templating language that integrates with Futures, Reactive Streams and Codable. This section outlines how to import the Leaf package into a Vapor project.", + "title": "Leaf" + }, + { + "location": "/leaf/getting-started/#example-folder-structure", + "text": "Hello\n\u251c\u2500\u2500 Package.resolved\n\u251c\u2500\u2500 Package.swift\n\u251c\u2500\u2500 Public\n\u251c\u2500\u2500 Resources\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 Views\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2514\u2500\u2500 hello.leaf\n\u251c\u2500\u2500 Public\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 images (images resources)\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 styles (css resources)\n\u251c\u2500\u2500 Sources\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 App\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 boot.swift\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 configure.swift\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2514\u2500\u2500 routes.swift\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 Run\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 main.swift\n\u251c\u2500\u2500 Tests\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 AppTests\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2514\u2500\u2500 AppTests.swift\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 LinuxMain.swift\n\u2514\u2500\u2500 LICENSE", + "title": "Example Folder Structure" + }, + { + "location": "/leaf/getting-started/#adding-leaf-to-your-project", + "text": "The easiest way to use Leaf with Vapor is to include the Leaf repository as a dependency in Package.swift: // swift-tools-version:4.0 import PackageDescription let package = Package ( \n name : project1 , \n dependencies : [ \n // \ud83d\udca7 A server-side Swift web framework. \n . package ( url : https://github.com/vapor/vapor.git , . branch ( beta )), \n . package ( url : https://github.com/vapor/leaf.git , . branch ( beta )), \n ], \n targets : [ \n . target ( \n name : App , \n dependencies : [ Vapor , Leaf ] \n ), \n . target ( name : Run , dependencies : [ App ]), \n . testTarget ( name : AppTests , dependencies : [ App ]), \n ] ) The Leaf package adds Leaf to your project, but to configure it for use you must modify configure.swift: Add import Leaf to the top of the file so that Leaf is available to use. You will also need to add this to any file that will render templates. Add try services.register(LeafProvider()) to the configure() function so that routes may render Leaf templates as needed.", + "title": "Adding Leaf to your project" + }, + { + "location": "/leaf/getting-started/#syntax-highlighting", + "text": "You may also wish to install one these third-party packages that provide support for syntax highlighting in Leaf templates.", + "title": "Syntax Highlighting" + }, + { + "location": "/leaf/getting-started/#atom", + "text": "language-leaf by ButkiewiczP", + "title": "Atom" + }, + { + "location": "/leaf/getting-started/#xcode", + "text": "It is not currently possible to implement Leaf Syntax Highlighting in Xcode, however, using Xcode's HTML Syntax Coloring can help a bit. Select one or more Leaf files and then choose Editor Syntax Coloring HTML. Your selected Leaf files will now use Xcode's HTML Syntax Coloring. Unfortunately the usefulness of this is limited because this association will be removed when vapor xcode is run. There appears to be a way to make Xcode file associations persist but that requires a bit more kung-fu.", + "title": "Xcode" + }, + { + "location": "/leaf/getting-started/#vs-code", + "text": "html-leaf by FranciscoAmado", + "title": "VS Code" + }, + { + "location": "/leaf/getting-started/#clion-appcode", + "text": "Some preliminary work has been done to implement a Leaf Plugin for CLion AppCode but lack of skill and interest in Java has slowed progress! If you have IntelliJ SDK experience and want to help with this, message Tom Holland on Vapor Slack", + "title": "CLion & AppCode" + }, + { + "location": "/leaf/basics/", + "text": "Basics\n\n\nWelcome to Leaf. Leaf's goal is to be a simple templating language that can make generating views easier. There are plenty of great templating languages, so use what's best for you \u2013 maybe that's Leaf! The goals of Leaf are:\n\n\n\n\nSmall set of strictly enforced rules\n\n\nConsistency\n\n\nParser first mentality\n\n\nExtensibility\n\n\nAsynchronous and reactive\n\n\n\n\nRendering a template\n\n\nOnce you have Leaf installed, you should create a directory called \u201cResources\u201d inside your project folder, and inside that create another directory called \u201cViews\u201d. This Resources/Views directory is the default location for Leaf templates, although you can change it if you want.\n\n\nFirstly, import Leaf to routes.swift\n\n\nimport\n \nLeaf\n\n\n\n\n\n\nThen, to render a basic Leaf template from a route, add this code:\n\n\nrouter\n.\nget\n \n{\n \nreq\n \n-\n \nFuture\nView\n \nin\n\n \nlet\n \nleaf\n \n=\n \ntry\n \nreq\n.\nmake\n(\nLeafRenderer\n.\nself\n)\n\n \nlet\n \ncontext\n \n=\n \n[\nString\n:\n \nString\n]()\n\n \nreturn\n \ntry\n \nleaf\n.\nrender\n(\nhome\n,\n \ncontext\n)\n\n\n}\n\n\n\n\n\n\nThat will load home.leaf in the Resources/Views directory and render it. The \ncontext\n dictionary is there to let you provide custom data to render inside the template, but you might find it easier to use codable structs instead because they provide extra type safety. For example:\n\n\nstruct\n \nHomePage\n:\n \nCodable\n \n{\n\n \nvar\n \ntitle\n:\n \nString\n\n \nvar\n \ncontent\n:\n \nString\n\n\n}\n\n\n\n\n\n\nAsync\n\n\nLeaf's engine is completely reactive, supporting both streams and futures. One of the only ones of its kind.\n\n\nWhen working with Future results, simply pass the \nFuture\n in your template context.\nStreams that carry an encodable type need to be encoded before they're usable within Leaf.\n\n\nstruct\n \nProfile\n:\n \nCodable\n \n{\n\n \nvar\n \nfriends\n:\n \nEncodableStream\n\n \nvar\n \ncurrentUser\n:\n \nFuture\nUser\n\n\n}\n\n\n\n\n\n\nIn the above context, the \ncurrentUser\n variable in Leaf will behave as being a \nUser\n type. Leaf will not read the user Future if it's not used during rendering.\n\n\nEncodableStream\n will behave as an array of LeafData, only with lower memory impact and better performance. It is recommended to use \nEncodableStream\n for (large) database queries.\n\n\nYour name is #(currentUser.name).\n\n#for(friend in friends) {\n #(friend.name) is a friend of you.\n}\n\n\n\n\n\nTemplate syntax\n\n\nStructure\n\n\nLeaf tags are made up of four elements:\n\n\n\n\nToken: \n#\n is the token\n\n\nName: A \nstring\n that identifies the tag\n\n\nParameter List: \n()\n May accept 0 or more arguments\n\n\nBody (optional): \n{}\n Must be separated from the parameter list by a space\n\n\n\n\nThere can be many different usages of these four elements depending on the tag's implementation. Let's look at a few examples of how Leaf's built-in tags might be used:\n\n\n\n\n#()\n\n\n#(variable)\n\n\n#embed(\"template\")\n\n\n#set(\"title\") { Welcome to Vapor }\n\n\n#count(friends)\n\n\n#for(friend in friends) { \nli\n#(friend.name)\n/li\n }\n\n\n\n\nWorking with context\n\n\nIn our Swift example from earlier, we used an empty \n[String: String]\n dictionary for context, which passes no custom data to Leaf. To try rendering content, use this code instead:\n\n\nlet\n \ncontext\n \n=\n \n[\ntitle\n:\n \nWelcome\n,\n \nmessage\n:\n \nVapor and Leaf work hand in hand\n]\n\n\nreturn\n \ntry\n \nleaf\n.\nmake\n(\nhome\n,\n \ncontext\n)\n\n\n\n\n\n\nThat will expose \ntitle\n and \nmessage\n to our Leaf template, which can then be used inside tags. For example:\n\n\nh1\n#(title)\n/h1\n\n\np\n#(message)\n/p\n\n\n\n\n\n\nChecking conditions\n\n\nLeaf is able to evaluate a range of conditions using its \n#if\n tag. For example, if you provide a variable it will check that variable exists in its context:\n\n\n#if(title) {\n The title is #(title)\n} else {\n No title was provided.\n}\n\n\n\n\n\nYou can also write comparisons, for example:\n\n\n#if(title == \nWelcome\n) {\n This is a friendly web page.\n} else {\n No strangers allowed!\n}\n\n\n\n\n\nIf you want to use another tag as part of your condition, you should omit the \n#\n for the inner tag. For example:\n\n\n#if(lowercase(title) == \nwelcome\n) {\n This is a friendly web page.\n} else {\n No strangers allowed!\n}\n\n\n\n\n\nLoops\n\n\nIf you provide an array of items, Leaf can loop over them and let you manipulate each item individually using its \n#for\n tag. For example, we could update our Swift code to provide a list of names in a team:\n\n\nlet\n \ncontext\n \n=\n \n[\nteam\n:\n \n[\nMalcolm\n,\n \nKaylee\n,\n \nJayne\n]]\n\n\n\n\n\n\nWe could then loop over them in Leaf like this:\n\n\n#for(name in team) {\n \np\n#(name) is in the team.\n/p\n\n}\n\n\n\n\n\nLeaf provides some extra variables inside a \n#for\n loop to give you more information about the loop's progress:\n\n\n\n\nThe \nloop.isFirst\n variable is true when the current iteration is the first one.\n\n\nThe \nloop.isLast\n variable is true when it's the last iteration.\n\n\nThe \nloop.index\n variable will be set to the number of the current iteration, counting from 0.\n\n\n\n\nEmbedding templates\n\n\nLeaf\u2019s \n#embed\n tag allows you to copy the contents of one template into another. When use this, you should always omit the template file's .leaf extension.\n\n\nEmbedding is useful for copying in a standard piece of content, for example a page footer or advert code:\n\n\n#embed(\nfooter\n)\n\n\n\n\n\nThis tag is also useful for building one template on top of another. For example, you might have a master.leaf file that includes all the code required to lay out your website \u2013\u00a0HTML structure, CSS and JavaScript \u2013\u00a0with some gaps in place that represent where page content varies.\n\n\nUsing this approach, you would construct a child template that fills in its unique content, then embeds the parent template that places the content appropriately.\n\n\nFor example, you might create a child.leaf template like this:\n\n\n#set(\nbody\n) {\n\np\nWelcome to Vapor!\n/p\n\n}\n\n#embed(\nmaster\n)\n\n\n\n\n\nThat configures one item of context, \nbody\n, but doesn\u2019t display it directly. Instead, it embeds master.leaf, which can render \nbody\n along with any other context variables passed in from Swift. For example, master.leaf might look like this:\n\n\nhtml\n\n\nhead\ntitle\n#(title)\n/title\n/head\n\n\nbody\n#get(body)\n/body\n\n\n/html\n\n\n\n\n\n\nWhen given the context \n[\"title\": \"Hi there!\"]\n, child.leaf will render as follows:\n\n\nhtml\n\n\nhead\ntitle\nHi there!\n/title\n/head\n\n\nbody\np\nWelcome to Vapor!\n/p\n/body\n\n\n/html\n\n\n\n\n\n\nOther tags\n\n\n#capitalize\n\n\nThe \n#capitalize\n tag uppercases the first letter of any string. For example, \u201ctaylor\u201d will become \u201cTaylor\u201d.\n\n\n#capitalize(name)\n\n\n\n\n\n#contains\n\n\nThe \n#contains\n tag accepts an array and a value as its two parameters, and returns true if the array in parameter one contains the value in parameter two. For example, given the array \nteam\n:\n\n\n#if(contains(team, \nJayne\n)) {\n You\nre all set!\n} else {\n You need someone to do PR.\n}\n\n\n\n\n\n#count\n\n\nThe \n#count\n tag returns the number of items in an array. For example:\n\n\nYour search matched #count(matches) pages.\n\n\n\n\n\n#lowercase\n\n\nThe \n#lowercase\n tag lowercases all letters in a string. For example, \u201cTaylor\u201d will become \u201ctaylor\u201d.\n\n\n#lowercase(name)\n\n\n\n\n\n#uppercase\n\n\nThe \n#uppercase\n tag uppercases all letters in a string. For example, \u201cTaylor\u201d will become \u201cTAYLOR\u201d.\n\n\n#uppercase(name)", + "title": "Basics" + }, + { + "location": "/leaf/basics/#basics", + "text": "Welcome to Leaf. Leaf's goal is to be a simple templating language that can make generating views easier. There are plenty of great templating languages, so use what's best for you \u2013 maybe that's Leaf! The goals of Leaf are: Small set of strictly enforced rules Consistency Parser first mentality Extensibility Asynchronous and reactive", + "title": "Basics" + }, + { + "location": "/leaf/basics/#rendering-a-template", + "text": "Once you have Leaf installed, you should create a directory called \u201cResources\u201d inside your project folder, and inside that create another directory called \u201cViews\u201d. This Resources/Views directory is the default location for Leaf templates, although you can change it if you want. Firstly, import Leaf to routes.swift import Leaf Then, to render a basic Leaf template from a route, add this code: router . get { req - Future View in \n let leaf = try req . make ( LeafRenderer . self ) \n let context = [ String : String ]() \n return try leaf . render ( home , context ) } That will load home.leaf in the Resources/Views directory and render it. The context dictionary is there to let you provide custom data to render inside the template, but you might find it easier to use codable structs instead because they provide extra type safety. For example: struct HomePage : Codable { \n var title : String \n var content : String }", + "title": "Rendering a template" + }, + { + "location": "/leaf/basics/#async", + "text": "Leaf's engine is completely reactive, supporting both streams and futures. One of the only ones of its kind. When working with Future results, simply pass the Future in your template context.\nStreams that carry an encodable type need to be encoded before they're usable within Leaf. struct Profile : Codable { \n var friends : EncodableStream \n var currentUser : Future User } In the above context, the currentUser variable in Leaf will behave as being a User type. Leaf will not read the user Future if it's not used during rendering. EncodableStream will behave as an array of LeafData, only with lower memory impact and better performance. It is recommended to use EncodableStream for (large) database queries. Your name is #(currentUser.name).\n\n#for(friend in friends) {\n #(friend.name) is a friend of you.\n}", + "title": "Async" + }, + { + "location": "/leaf/basics/#template-syntax", + "text": "", + "title": "Template syntax" + }, + { + "location": "/leaf/basics/#structure", + "text": "Leaf tags are made up of four elements: Token: # is the token Name: A string that identifies the tag Parameter List: () May accept 0 or more arguments Body (optional): {} Must be separated from the parameter list by a space There can be many different usages of these four elements depending on the tag's implementation. Let's look at a few examples of how Leaf's built-in tags might be used: #() #(variable) #embed(\"template\") #set(\"title\") { Welcome to Vapor } #count(friends) #for(friend in friends) { li #(friend.name) /li }", + "title": "Structure" + }, + { + "location": "/leaf/basics/#working-with-context", + "text": "In our Swift example from earlier, we used an empty [String: String] dictionary for context, which passes no custom data to Leaf. To try rendering content, use this code instead: let context = [ title : Welcome , message : Vapor and Leaf work hand in hand ] return try leaf . make ( home , context ) That will expose title and message to our Leaf template, which can then be used inside tags. For example: h1 #(title) /h1 p #(message) /p", + "title": "Working with context" + }, + { + "location": "/leaf/basics/#checking-conditions", + "text": "Leaf is able to evaluate a range of conditions using its #if tag. For example, if you provide a variable it will check that variable exists in its context: #if(title) {\n The title is #(title)\n} else {\n No title was provided.\n} You can also write comparisons, for example: #if(title == Welcome ) {\n This is a friendly web page.\n} else {\n No strangers allowed!\n} If you want to use another tag as part of your condition, you should omit the # for the inner tag. For example: #if(lowercase(title) == welcome ) {\n This is a friendly web page.\n} else {\n No strangers allowed!\n}", + "title": "Checking conditions" + }, + { + "location": "/leaf/basics/#loops", + "text": "If you provide an array of items, Leaf can loop over them and let you manipulate each item individually using its #for tag. For example, we could update our Swift code to provide a list of names in a team: let context = [ team : [ Malcolm , Kaylee , Jayne ]] We could then loop over them in Leaf like this: #for(name in team) {\n p #(name) is in the team. /p \n} Leaf provides some extra variables inside a #for loop to give you more information about the loop's progress: The loop.isFirst variable is true when the current iteration is the first one. The loop.isLast variable is true when it's the last iteration. The loop.index variable will be set to the number of the current iteration, counting from 0.", + "title": "Loops" + }, + { + "location": "/leaf/basics/#embedding-templates", + "text": "Leaf\u2019s #embed tag allows you to copy the contents of one template into another. When use this, you should always omit the template file's .leaf extension. Embedding is useful for copying in a standard piece of content, for example a page footer or advert code: #embed( footer ) This tag is also useful for building one template on top of another. For example, you might have a master.leaf file that includes all the code required to lay out your website \u2013\u00a0HTML structure, CSS and JavaScript \u2013\u00a0with some gaps in place that represent where page content varies. Using this approach, you would construct a child template that fills in its unique content, then embeds the parent template that places the content appropriately. For example, you might create a child.leaf template like this: #set( body ) { p Welcome to Vapor! /p \n}\n\n#embed( master ) That configures one item of context, body , but doesn\u2019t display it directly. Instead, it embeds master.leaf, which can render body along with any other context variables passed in from Swift. For example, master.leaf might look like this: html head title #(title) /title /head body #get(body) /body /html When given the context [\"title\": \"Hi there!\"] , child.leaf will render as follows: html head title Hi there! /title /head body p Welcome to Vapor! /p /body /html", + "title": "Embedding templates" + }, + { + "location": "/leaf/basics/#other-tags", + "text": "", + "title": "Other tags" + }, + { + "location": "/leaf/basics/#capitalize", + "text": "The #capitalize tag uppercases the first letter of any string. For example, \u201ctaylor\u201d will become \u201cTaylor\u201d. #capitalize(name)", + "title": "#capitalize" + }, + { + "location": "/leaf/basics/#contains", + "text": "The #contains tag accepts an array and a value as its two parameters, and returns true if the array in parameter one contains the value in parameter two. For example, given the array team : #if(contains(team, Jayne )) {\n You re all set!\n} else {\n You need someone to do PR.\n}", + "title": "#contains" + }, + { + "location": "/leaf/basics/#count", + "text": "The #count tag returns the number of items in an array. For example: Your search matched #count(matches) pages.", + "title": "#count" + }, + { + "location": "/leaf/basics/#lowercase", + "text": "The #lowercase tag lowercases all letters in a string. For example, \u201cTaylor\u201d will become \u201ctaylor\u201d. #lowercase(name)", + "title": "#lowercase" + }, + { + "location": "/leaf/basics/#uppercase", + "text": "The #uppercase tag uppercases all letters in a string. For example, \u201cTaylor\u201d will become \u201cTAYLOR\u201d. #uppercase(name)", + "title": "#uppercase" + }, + { + "location": "/leaf/custom-tags/", + "text": "Custom Tags\n\n\nYou can extend Leaf to provide your own tags that add custom functionality. To demonstrate this, let's look at a basic example by recreating \n#uppercase\n together. This tag will take one argument, which is the string to uppercase.\n\n\nWhen working with custom tags, there are four important things to know:\n\n\n\n\nYou should call \nrequireParameterCount()\n with the number of parameters you expect to receive. This will throw an error if your tag is used incorrectly.\n\n\nIf you do or do not require a body, you should use either \nrequireBody()\n or \nrequireNoBody()\n. Again, this will throw an error if your tag is used incorrectly.\n\n\nYou can read individual parameters using the \nparameters\n array. Each parameter will be of type \nLeafData\n, which you can convert to concrete data types using properties such as \n.string\n, \n.dictionary\n, and so on.\n\n\nYou must return a \nFuture\nLeafData?\n containing what should be rendered. In the example below we wrap the resulting uppercase string in a \nLeafData\n string, then send that back wrapped in a future.\n\n\n\n\nHere\u2019s example code for a \nCustomUppercase\n Leaf tag:\n\n\nimport\n \nAsync\n\n\nimport\n \nLeaf\n\n\n\npublic\n \nfinal\n \nclass\n \nCustomUppercase\n:\n \nLeaf\n.\nLeafTag\n \n{\n\n \npublic\n \ninit\n()\n \n{}\n\n \npublic\n \nfunc\n \nrender\n(\nparsed\n:\n \nParsedTag\n,\n \ncontext\n:\n \nLeafContext\n,\n \nrenderer\n:\n \nLeafRenderer\n)\n \nthrows\n \n-\n \nFuture\nLeafData\n?\n \n{\n\n \n// ensure we receive precisely one parameter\n\n \ntry\n \nparsed\n.\nrequireParameterCount\n(\n1\n)\n\n\n \n// pull out our lone parameter as a string then uppercase it, or use an empty string\n\n \nlet\n \nstring\n \n=\n \nparsed\n.\nparameters\n[\n0\n].\nstring\n?.\nuppercased\n()\n \n??\n \n\n\n \n// send it back wrapped in a LeafData\n\n \nreturn\n \nFuture\n(.\nstring\n(\nstring\n))\n\n \n}\n\n\n}\n\n\n\n\n\n\nWe can now register this Tag in our \nconfigure.swift\n file with:\n\n\nservices\n.\nregister\n \n{\n \ncontainer\n \n-\n \nLeafConfig\n \nin\n\n \n// take a copy of Leaf\ns default tags\n\n \nvar\n \ntags\n \n=\n \ndefaultTags\n\n\n \n// add our custom tag\n\n \ntags\n[\ncustomuppercase\n]\n \n=\n \nCustomUppercase\n()\n\n\n \n// find the location of our Resources/Views directory\n\n \nlet\n \ndirectoryConfig\n \n=\n \ntry\n \ncontainer\n.\nmake\n(\nDirectoryConfig\n.\nself\n,\n \nfor\n:\n \nLeafRenderer\n.\nself\n)\n\n \nlet\n \nviewsDirectory\n \n=\n \ndirectoryConfig\n.\nworkDir\n \n+\n \nResources/Views\n\n\n \n// put all that into a new Leaf configuration and return it\n\n \nreturn\n \nLeafConfig\n(\ntags\n:\n \ntags\n,\n \nviewsDir\n:\n \nviewsDirectory\n)\n\n\n}\n\n\n\n\n\n\nOnce that is complete, you can use \n#customuppercase(some_variable)\n to run your custom code.\n\n\n\n\nNote: Use of non-alphanumeric characters in tag names is \nstrongly discouraged\n and may be disallowed in future versions of Leaf.", + "title": "Custom tags" + }, + { + "location": "/leaf/custom-tags/#custom-tags", + "text": "You can extend Leaf to provide your own tags that add custom functionality. To demonstrate this, let's look at a basic example by recreating #uppercase together. This tag will take one argument, which is the string to uppercase. When working with custom tags, there are four important things to know: You should call requireParameterCount() with the number of parameters you expect to receive. This will throw an error if your tag is used incorrectly. If you do or do not require a body, you should use either requireBody() or requireNoBody() . Again, this will throw an error if your tag is used incorrectly. You can read individual parameters using the parameters array. Each parameter will be of type LeafData , which you can convert to concrete data types using properties such as .string , .dictionary , and so on. You must return a Future LeafData? containing what should be rendered. In the example below we wrap the resulting uppercase string in a LeafData string, then send that back wrapped in a future. Here\u2019s example code for a CustomUppercase Leaf tag: import Async import Leaf public final class CustomUppercase : Leaf . LeafTag { \n public init () {} \n public func render ( parsed : ParsedTag , context : LeafContext , renderer : LeafRenderer ) throws - Future LeafData ? { \n // ensure we receive precisely one parameter \n try parsed . requireParameterCount ( 1 ) \n\n // pull out our lone parameter as a string then uppercase it, or use an empty string \n let string = parsed . parameters [ 0 ]. string ?. uppercased () ?? \n\n // send it back wrapped in a LeafData \n return Future (. string ( string )) \n } } We can now register this Tag in our configure.swift file with: services . register { container - LeafConfig in \n // take a copy of Leaf s default tags \n var tags = defaultTags \n\n // add our custom tag \n tags [ customuppercase ] = CustomUppercase () \n\n // find the location of our Resources/Views directory \n let directoryConfig = try container . make ( DirectoryConfig . self , for : LeafRenderer . self ) \n let viewsDirectory = directoryConfig . workDir + Resources/Views \n\n // put all that into a new Leaf configuration and return it \n return LeafConfig ( tags : tags , viewsDir : viewsDirectory ) } Once that is complete, you can use #customuppercase(some_variable) to run your custom code. Note: Use of non-alphanumeric characters in tag names is strongly discouraged and may be disallowed in future versions of Leaf.", + "title": "Custom Tags" + }, + { + "location": "/redis/getting-started/", + "text": "Redis\n\n\nRedis is a Redis client library that can communicate with a Redis database.\n\n\nWhat is Redis?\n\n\nRedis is an in-memory data store used as a database, cache and message broker. It supports most common data structures. Redis is most commonly used for caching data such as sessions and notifications (between multiple servers).\n\n\nRedis works as a key-value store, but allows querying the keys, unlike most databases.\n\n\nWith and without Vapor\n\n\nTo include it in your package, add the following to your \nPackage.swift\n file.\n\n\n// swift-tools-version:4.0\n\n\nimport\n \nPackageDescription\n\n\n\nlet\n \npackage\n \n=\n \nPackage\n(\n\n \nname\n:\n \nProject\n,\n\n \ndependencies\n:\n \n[\n\n \n...\n\n \n.\npackage\n(\nurl\n:\n \nhttps://github.com/vapor/redis.git\n,\n \n.\nupToNextMajor\n(\nfrom\n:\n \n3.0.0\n)),\n\n \n],\n\n \ntargets\n:\n \n[\n\n \n.\ntarget\n(\nname\n:\n \nProject\n,\n \ndependencies\n:\n \n[\nRedis\n,\n \n...\n \n])\n\n \n]\n\n\n)\n\n\n\n\n\n\nIf this is your first time adding a dependency, you should read our introduction to \nPackage.swift\n.\n\n\nUse \nimport Redis\n to access Redis' APIs.", + "title": "Getting Started" + }, + { + "location": "/redis/getting-started/#redis", + "text": "Redis is a Redis client library that can communicate with a Redis database.", + "title": "Redis" + }, + { + "location": "/redis/getting-started/#what-is-redis", + "text": "Redis is an in-memory data store used as a database, cache and message broker. It supports most common data structures. Redis is most commonly used for caching data such as sessions and notifications (between multiple servers). Redis works as a key-value store, but allows querying the keys, unlike most databases.", + "title": "What is Redis?" + }, + { + "location": "/redis/getting-started/#with-and-without-vapor", + "text": "To include it in your package, add the following to your Package.swift file. // swift-tools-version:4.0 import PackageDescription let package = Package ( \n name : Project , \n dependencies : [ \n ... \n . package ( url : https://github.com/vapor/redis.git , . upToNextMajor ( from : 3.0.0 )), \n ], \n targets : [ \n . target ( name : Project , dependencies : [ Redis , ... ]) \n ] ) If this is your first time adding a dependency, you should read our introduction to Package.swift . Use import Redis to access Redis' APIs.", + "title": "With and without Vapor" + }, + { + "location": "/redis/basics/", + "text": "Redis basic usage\n\n\nTo interact with Redis, you first need to construct a Redis client.\nThe Redis library primarily supports TCP sockets.\n\n\nThis requires a hostname, port and worker. The eventloop will be used for Redis' Socket. The hostname and port have a default. The hostname is defaulted to \nlocalhost\n, and the port to Redis' default port \n6379\n.\n\n\nlet\n \nclient\n \n=\n \ntry\n \nRedisClient\n.\nconnect\n(\non\n:\n \nworker\n)\n \n// Future\nRedisClient\n\n\n\n\n\n\nThe \nconnect\n method will return a future containing the TCP based Redis Client.\n\n\nRedis Data Types\n\n\nRedis has 6 data types:\n\n\n\n\nnull\n\n\nInt\n\n\nError\n\n\nArray\n\n\nBasic String (used for command names and basic replies only)\n\n\nBulk String (used for Strings and binary data blobs)\n\n\n\n\nYou can instantiate one from the static functions and variables on \nRedisData\n.\n\n\nlet\n \nnull\n \n=\n \nRedisData\n.\nnull\n\n\n\nlet\n \nhelloWorld\n \n=\n \nRedisData\n.\nbulkString\n(\nHello World\n)\n\n\n\nlet\n \nthree\n \n=\n \nRedisData\n.\ninteger\n(\n3\n)\n\n\n\nlet\n \noneThroughTen\n \n=\n \nRedisData\n.\narray\n([\n\n \n.\ninteger\n(\n1\n),\n\n \n.\ninteger\n(\n2\n),\n\n \n.\ninteger\n(\n3\n),\n\n \n.\ninteger\n(\n4\n),\n\n \n.\ninteger\n(\n5\n),\n\n \n.\ninteger\n(\n6\n),\n\n \n.\ninteger\n(\n7\n),\n\n \n.\ninteger\n(\n8\n),\n\n \n.\ninteger\n(\n9\n),\n\n \n.\ninteger\n(\n10\n)\n\n\n])\n\n\n\n\n\n\nThe above is the explicit way of defining Redis Types. You can also use literals in most scenarios:\n\n\nlet\n \narray\n \n=\n \nRedisData\n.\narray\n([\n\n \n[\n\n \n1\n,\n \n2\n,\n \n3\n,\n \n4\n,\n \n5\n,\n \n6\n,\n \n7\n,\n \n8\n,\n \n9\n,\n \n10\n\n \n],\n\n \nHello World\n,\n\n \nOne\n,\n\n \nTwo\n,\n\n \n.\nnull\n,\n\n \n.\nnull\n,\n\n \ntest\n\n\n])\n\n\n\n\n\n\nCRUD using Redis\n\n\nFrom here on it is assumed that your client has been successfully created and is available in the variable \nclient\n as a \nRedisClient\n.\n\n\nCreating a record\n\n\nCreating a record is done using a \nRedisData\n for a value and a key.\n\n\nclient\n.\nset\n(\nworld\n,\n \nforKey\n:\n \nhello\n)\n\n\n\n\n\n\nThis returns a future that'll indicate successful or unsuccessful insertion.\n\n\nReading a record\n\n\nReading a record is similar, only you'll get a warning if you don't use the returned future.\n\n\nThe \nFuture\nRedisData\n for the key \"hello\" will be \"world\" if you created the record as shown above.\n\n\nlet\n \nfutureRecord\n \n=\n \nclient\n.\ngetData\n(\nforKey\n:\n \nhello\n)\n \n// Future\nRedisData\n\n\n\n\n\n\nDeleting a record\n\n\nDeleting a record is similar but allows querying the keys, too.\n\n\nclient\n.\ndelete\n(\nkeys\n:\n \n[\nhello\n])\n\n\n\n\n\n\nWhere the above command will remove the key \"hello\", the next command will delete \nall\n keys from the Redis database.\n\n\nclient\n.\ndelete\n(\nkeys\n:\n \n[\n*\n])", + "title": "Basics" + }, + { + "location": "/redis/basics/#redis-basic-usage", + "text": "To interact with Redis, you first need to construct a Redis client.\nThe Redis library primarily supports TCP sockets. This requires a hostname, port and worker. The eventloop will be used for Redis' Socket. The hostname and port have a default. The hostname is defaulted to localhost , and the port to Redis' default port 6379 . let client = try RedisClient . connect ( on : worker ) // Future RedisClient The connect method will return a future containing the TCP based Redis Client.", + "title": "Redis basic usage" + }, + { + "location": "/redis/basics/#redis-data-types", + "text": "Redis has 6 data types: null Int Error Array Basic String (used for command names and basic replies only) Bulk String (used for Strings and binary data blobs) You can instantiate one from the static functions and variables on RedisData . let null = RedisData . null let helloWorld = RedisData . bulkString ( Hello World ) let three = RedisData . integer ( 3 ) let oneThroughTen = RedisData . array ([ \n . integer ( 1 ), \n . integer ( 2 ), \n . integer ( 3 ), \n . integer ( 4 ), \n . integer ( 5 ), \n . integer ( 6 ), \n . integer ( 7 ), \n . integer ( 8 ), \n . integer ( 9 ), \n . integer ( 10 ) ]) The above is the explicit way of defining Redis Types. You can also use literals in most scenarios: let array = RedisData . array ([ \n [ \n 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 \n ], \n Hello World , \n One , \n Two , \n . null , \n . null , \n test ])", + "title": "Redis Data Types" + }, + { + "location": "/redis/basics/#crud-using-redis", + "text": "From here on it is assumed that your client has been successfully created and is available in the variable client as a RedisClient .", + "title": "CRUD using Redis" + }, + { + "location": "/redis/basics/#creating-a-record", + "text": "Creating a record is done using a RedisData for a value and a key. client . set ( world , forKey : hello ) This returns a future that'll indicate successful or unsuccessful insertion.", + "title": "Creating a record" + }, + { + "location": "/redis/basics/#reading-a-record", + "text": "Reading a record is similar, only you'll get a warning if you don't use the returned future. The Future RedisData for the key \"hello\" will be \"world\" if you created the record as shown above. let futureRecord = client . getData ( forKey : hello ) // Future RedisData", + "title": "Reading a record" + }, + { + "location": "/redis/basics/#deleting-a-record", + "text": "Deleting a record is similar but allows querying the keys, too. client . delete ( keys : [ hello ]) Where the above command will remove the key \"hello\", the next command will delete all keys from the Redis database. client . delete ( keys : [ * ])", + "title": "Deleting a record" + }, + { + "location": "/redis/custom-commands/", + "text": "Custom commands\n\n\nMany commands are not (yet) implemented by the driver using a convenience function. This does not mean the feature/command is not usable.\n\n\n(Almost) all functions listed here\n work out of the box using custom commands.\n\n\nUsage\n\n\nThe Redis client has a \nrun\n function that allows you to run these commands.\n\n\nThe following code demonstrates a \"custom\" implementation for \nGET\n.\n\n\nlet\n \nfuture\n \n=\n \nclient\n.\nrun\n(\ncommand\n:\n \nGET\n,\n \narguments\n:\n \n[\nmy-key\n])\n \n// Future\nRedisData\n\n\n\n\n\n\nThis future will contain the result as specified in the article on the redis command page or an error.", + "title": "Custom commands" + }, + { + "location": "/redis/custom-commands/#custom-commands", + "text": "Many commands are not (yet) implemented by the driver using a convenience function. This does not mean the feature/command is not usable. (Almost) all functions listed here work out of the box using custom commands.", + "title": "Custom commands" + }, + { + "location": "/redis/custom-commands/#usage", + "text": "The Redis client has a run function that allows you to run these commands. The following code demonstrates a \"custom\" implementation for GET . let future = client . run ( command : GET , arguments : [ my-key ]) // Future RedisData This future will contain the result as specified in the article on the redis command page or an error.", + "title": "Usage" + }, + { + "location": "/redis/pub-sub/", + "text": "Publish \n Subscribe\n\n\nRedis' Publish and Subscribe model is really useful for notifications.\n\n\nUse cases\n\n\nPub/sub is used for notifying subscribers of an event.\nA simple and common event for example would be a chat message.\n\n\nA channel consists of a name and group of listeners. Think of it as being \n[String: [Listener]]\n.\nWhen you send a notification to a channel you need to provide a payload.\nEach listener will get a notification consisting of this payload.\n\n\nChannels must be a string. For chat groups, for example, you could use the database identifier.\n\n\nPublishing\n\n\nYou cannot get a list of listeners, but sending a payload will emit the amount of listeners that received the notification.\nSending (publishing) an event is done like so:\n\n\n// Any redis data\n\n\nlet\n \nnotification\n:\n \nRedisData\n \n=\n \nMy-Notification\n\n\n\nclient\n.\npublish\n(\nnotification\n,\n \nto\n:\n \nmy-channel\n)\n\n\n\n\n\n\nIf you want access to the listener count:\n\n\nlet\n \nnotifiedCount\n \n=\n \nclient\n.\npublish\n(\nnotification\n,\n \nto\n:\n \nmy-channel\n)\n \n// Future\nInt\n\n\n\n\n\n\nSubscribing\n\n\nTo subscribe for notifications you're rendering an entire Redis Client useless in exchange for listening to events.\n\n\nA single client can listen to one or more channels, which is provided using a set of unique channel names. The result of subscribing is a \nSubscriptionStream\n.\n\n\nlet\n \nnotifications\n \n=\n \nclient\n.\nsubscribe\n(\nto\n:\n \n[\nsome-notification-channel\n,\n \nother-notification-channel\n])\n\n\n\n\n\n\nIf you try to use the client after subscribing, all operations will fail. These errors are usually emitted through the Future.\n\n\nThis stream will receive messages asynchronously from the point of \ndraining\n. This works like any other async stream.\n\n\nNotifications consist of the channel and payload.\n\n\nnotifications\n.\ndrain\n \n{\n \nnotification\n \nin\n\n \nprint\n(\nnotification\n.\nchannel\n)\n\n\n \nlet\n \npayload\n \n=\n \nnotification\n.\npayload\n\n\n \n// \nTODO:\n Process the payload\n\n\n}", + "title": "Publish and Subscribe" + }, + { + "location": "/redis/pub-sub/#publish-subscribe", + "text": "Redis' Publish and Subscribe model is really useful for notifications.", + "title": "Publish & Subscribe" + }, + { + "location": "/redis/pub-sub/#use-cases", + "text": "Pub/sub is used for notifying subscribers of an event.\nA simple and common event for example would be a chat message. A channel consists of a name and group of listeners. Think of it as being [String: [Listener]] .\nWhen you send a notification to a channel you need to provide a payload.\nEach listener will get a notification consisting of this payload. Channels must be a string. For chat groups, for example, you could use the database identifier.", + "title": "Use cases" + }, + { + "location": "/redis/pub-sub/#publishing", + "text": "You cannot get a list of listeners, but sending a payload will emit the amount of listeners that received the notification.\nSending (publishing) an event is done like so: // Any redis data let notification : RedisData = My-Notification client . publish ( notification , to : my-channel ) If you want access to the listener count: let notifiedCount = client . publish ( notification , to : my-channel ) // Future Int", + "title": "Publishing" + }, + { + "location": "/redis/pub-sub/#subscribing", + "text": "To subscribe for notifications you're rendering an entire Redis Client useless in exchange for listening to events. A single client can listen to one or more channels, which is provided using a set of unique channel names. The result of subscribing is a SubscriptionStream . let notifications = client . subscribe ( to : [ some-notification-channel , other-notification-channel ]) If you try to use the client after subscribing, all operations will fail. These errors are usually emitted through the Future. This stream will receive messages asynchronously from the point of draining . This works like any other async stream. Notifications consist of the channel and payload. notifications . drain { notification in \n print ( notification . channel ) \n\n let payload = notification . payload \n\n // TODO: Process the payload }", + "title": "Subscribing" + }, + { + "location": "/redis/pipeline/", + "text": "Pipelining\n\n\nPipelining is used for sending multiple commands at once. The performance advantages become apparent when sending a large number of queries. Redis' pipelining cuts down latency by reducing the RTT (Round Trip Time) between the client and server. Pipelining also reduces the amount of IO operations Redis has to perform, this increases the amount of queries per second Redis can handle. \n\n\nUse cases\n\n\nSometimes multiple commands need to be executed at once. Instead of sending those commands individually in a loop, pipelining allows the commands to be batched and sent in one request. A common scenario might be needing to set a key and increment a count, pipelining those commands would be ideal.\n\n\nEnqueuing Commands\n\n\n \nlet\n \npipeline\n \n=\n \nconnection\n.\nmakePipeline\n()\n\n \nlet\n \nresult\n \n=\n \ntry\n \npipeline\n\n \n.\nenqueue\n(\ncommand\n:\n \nSET\n,\n \narguments\n:\n \n[\nKEY\n,\n \nVALUE\n])\n\n \n.\nenqueue\n(\ncommand\n:\n \nINCR\n,\n \narguments\n:\n \n[\nCOUNT\n])\n\n \n.\nexecute\n()\n \n// Future\n[RedisData]\n\n\n\n\n\n\nNote: Commands will not be executed until execute is called.", + "title": "Pipeline" + }, + { + "location": "/redis/pipeline/#pipelining", + "text": "Pipelining is used for sending multiple commands at once. The performance advantages become apparent when sending a large number of queries. Redis' pipelining cuts down latency by reducing the RTT (Round Trip Time) between the client and server. Pipelining also reduces the amount of IO operations Redis has to perform, this increases the amount of queries per second Redis can handle.", + "title": "Pipelining" + }, + { + "location": "/redis/pipeline/#use-cases", + "text": "Sometimes multiple commands need to be executed at once. Instead of sending those commands individually in a loop, pipelining allows the commands to be batched and sent in one request. A common scenario might be needing to set a key and increment a count, pipelining those commands would be ideal.", + "title": "Use cases" + }, + { + "location": "/redis/pipeline/#enqueuing-commands", + "text": "let pipeline = connection . makePipeline () \n let result = try pipeline \n . enqueue ( command : SET , arguments : [ KEY , VALUE ]) \n . enqueue ( command : INCR , arguments : [ COUNT ]) \n . execute () // Future [RedisData] Note: Commands will not be executed until execute is called.", + "title": "Enqueuing Commands" + }, + { + "location": "/websocket/websocket/", + "text": "WebSocket\n\n\nComing soon.", + "title": "Getting Started" + }, + { + "location": "/websocket/websocket/#websocket", + "text": "Coming soon.", + "title": "WebSocket" + }, + { + "location": "/crypto/getting-started/", + "text": "Using Crypto\n\n\nCrypto is a library containing all common APIs related to cryptography and security.\n\n\nWith Vapor\n\n\nThis package is included with Vapor by default, just add:\n\n\nimport\n \nCrypto\n\n\n\n\n\n\nWithout Vapor\n\n\nTo include it in your package, add the following to your \nPackage.swift\n file.\n\n\n// swift-tools-version:4.0\n\n\nimport\n \nPackageDescription\n\n\n\nlet\n \npackage\n \n=\n \nPackage\n(\n\n \nname\n:\n \nProject\n,\n\n \ndependencies\n:\n \n[\n\n \n...\n\n \n.\npackage\n(\nurl\n:\n \nhttps://github.com/vapor/crypto.git\n,\n \n.\nupToNextMajor\n(\nfrom\n:\n \nx.0.0\n)),\n\n \n],\n\n \ntargets\n:\n \n[\n\n \n.\ntarget\n(\nname\n:\n \nProject\n,\n \ndependencies\n:\n \n[\nCrypto\n,\n \n...\n \n])\n\n \n]\n\n\n)\n\n\n\n\n\n\nUse \nimport Crypto\n to access Crypto's APIs.", + "title": "Getting Started" + }, + { + "location": "/crypto/getting-started/#using-crypto", + "text": "Crypto is a library containing all common APIs related to cryptography and security.", + "title": "Using Crypto" + }, + { + "location": "/crypto/getting-started/#with-vapor", + "text": "This package is included with Vapor by default, just add: import Crypto", + "title": "With Vapor" + }, + { + "location": "/crypto/getting-started/#without-vapor", + "text": "To include it in your package, add the following to your Package.swift file. // swift-tools-version:4.0 import PackageDescription let package = Package ( \n name : Project , \n dependencies : [ \n ... \n . package ( url : https://github.com/vapor/crypto.git , . upToNextMajor ( from : x.0.0 )), \n ], \n targets : [ \n . target ( name : Project , dependencies : [ Crypto , ... ]) \n ] ) Use import Crypto to access Crypto's APIs.", + "title": "Without Vapor" + }, + { + "location": "/crypto/base64/", + "text": "Base64\n\n\nComing soon", + "title": "Base64" + }, + { + "location": "/crypto/base64/#base64", + "text": "Coming soon", + "title": "Base64" + }, + { + "location": "/crypto/hash/", + "text": "Hash\n\n\nHashes are a one-directional encryption that is commonly used for validating files or one-way securing data such as passwords.\n\n\nAvailable hashes\n\n\nCrypto currently supports a few hashes.\n\n\n\n\nMD5\n\n\nSHA1\n\n\nSHA2 (all variants)\n\n\n\n\nMD5 and SHA1 are generally used for file validation or legacy (weak) passwords. They're performant and lightweight.\n\n\nEvery Hash type has a set of helpers that you can use.\n\n\nHashing blobs of data\n\n\nEvery \nHash\n has a static method called \nhash\n that can be used for hashing the entire contents of \nFoundation.Data\n, \nByteBuffer\n or \nString\n.\n\n\nThe result is \nData\n containing the resulting hash. The hash's length is according to spec and defined in the static variable \ndigestSize\n.\n\n\n// MD5 with `Data`\n\n\nlet\n \nfileData\n \n=\n \nData\n()\n\n\nlet\n \nfileMD5\n \n=\n \nMD5\n.\nhash\n(\nfileData\n)\n\n\n\n// SHA1 with `ByteBuffer`\n\n\nlet\n \nfileBuffer\n:\n \nByteBuffer\n \n=\n \n...\n\n\nlet\n \nfileSHA1\n \n=\n \nSHA1\n.\nhash\n(\nfileBuffer\n)\n\n\n\n// SHA2 variants with String\n\n\nlet\n \nstaticUnsafeToken\n:\n \nString\n \n=\n \nrsadd14ndmasidfm12i4j\n\n\n\nlet\n \ntokenHashSHA224\n \n=\n \nSHA224\n.\nhash\n(\nstaticUnsafeToken\n)\n\n\nlet\n \ntokenHashSHA256\n \n=\n \nSHA256\n.\nhash\n(\nstaticUnsafeToken\n)\n\n\nlet\n \ntokenHashSHA384\n \n=\n \nSHA384\n.\nhash\n(\nstaticUnsafeToken\n)\n\n\nlet\n \ntokenHashSHA512\n \n=\n \nSHA512\n.\nhash\n(\nstaticUnsafeToken\n)\n\n\n\n\n\n\nIncremental hashes (manual)\n\n\nTo incrementally process hashes you can create an instance of the Hash. This will set up a context.\n\n\nAll hash context initializers are empty:\n\n\n// Create an MD5 context\n\n\nlet\n \nmd5Context\n \n=\n \nMD5\n()\n\n\n\n\n\n\nTo process a single chunk of data, you can call the \nupdate\n function on a context using any \nSequence\n of \nUInt8\n. That means \nArray\n, \nData\n and \nByteBuffer\n work alongside any other sequence of bytes.\n\n\nmd5Context\n.\nupdate\n(\ndata\n)\n\n\n\n\n\n\nThe data data need not be a specific length. Any length works.\n\n\nWhen you need the result, you can call \nmd5Context.finalize()\n. This will finish calculating the hash by appending the standard \n1\n bit, padding and message bitlength.\n\n\nYou can optionally provide a last set of data to \nfinalize()\n.\n\n\nAfter calling \nfinalize()\n, do not update the hash if you want correct results.\n\n\nFetching the results\n\n\nThe context can then be accessed to extract the resulting Hash.\n\n\nlet\n \nhash\n:\n \nData\n \n=\n \nmd5Context\n.\nhash\n\n\n\n\n\n\nStreaming hashes (Async)\n\n\nSometimes you need to hash the contents of a Stream, for example, when processing a file transfer. In this case you can use \nByteStreamHasher\n.\n\n\nFirst, create a new generic \nByteStreamHasher\nHash\n where \nHash\n is the hash you want to use. In this case, SHA512.\n\n\nlet\n \nstreamHasher\n \n=\n \nByteStreamHasher\nSHA512\n()\n\n\n\n\n\n\nThis stream works like any \ninputStream\n by consuming the incoming data and passing the buffers to the hash context.\n\n\nFor example, draining a TCP socket.\n\n\nlet\n \nsocket\n:\n \nTCP\n.\nSocket\n \n=\n \n...\n\n\n\nsocket\n.\ndrain\n(\ninto\n:\n \nstreamHasher\n)\n\n\n\n\n\n\nThis will incrementally update the hash using the provided TCP socket's data.\n\n\nWhen the hash has been completely accumulated, you can \ncomplete\n the hash.\n\n\nlet\n \nhash\n \n=\n \nstreamHasher\n.\ncomplete\n()\n \n// Foundation `Data`\n\n\n\n\n\n\nThis will reset the hash's context to the default configuration, ready to start over.", + "title": "Hashes" + }, + { + "location": "/crypto/hash/#hash", + "text": "Hashes are a one-directional encryption that is commonly used for validating files or one-way securing data such as passwords.", + "title": "Hash" + }, + { + "location": "/crypto/hash/#available-hashes", + "text": "Crypto currently supports a few hashes. MD5 SHA1 SHA2 (all variants) MD5 and SHA1 are generally used for file validation or legacy (weak) passwords. They're performant and lightweight. Every Hash type has a set of helpers that you can use.", + "title": "Available hashes" + }, + { + "location": "/crypto/hash/#hashing-blobs-of-data", + "text": "Every Hash has a static method called hash that can be used for hashing the entire contents of Foundation.Data , ByteBuffer or String . The result is Data containing the resulting hash. The hash's length is according to spec and defined in the static variable digestSize . // MD5 with `Data` let fileData = Data () let fileMD5 = MD5 . hash ( fileData ) // SHA1 with `ByteBuffer` let fileBuffer : ByteBuffer = ... let fileSHA1 = SHA1 . hash ( fileBuffer ) // SHA2 variants with String let staticUnsafeToken : String = rsadd14ndmasidfm12i4j let tokenHashSHA224 = SHA224 . hash ( staticUnsafeToken ) let tokenHashSHA256 = SHA256 . hash ( staticUnsafeToken ) let tokenHashSHA384 = SHA384 . hash ( staticUnsafeToken ) let tokenHashSHA512 = SHA512 . hash ( staticUnsafeToken )", + "title": "Hashing blobs of data" + }, + { + "location": "/crypto/hash/#incremental-hashes-manual", + "text": "To incrementally process hashes you can create an instance of the Hash. This will set up a context. All hash context initializers are empty: // Create an MD5 context let md5Context = MD5 () To process a single chunk of data, you can call the update function on a context using any Sequence of UInt8 . That means Array , Data and ByteBuffer work alongside any other sequence of bytes. md5Context . update ( data ) The data data need not be a specific length. Any length works. When you need the result, you can call md5Context.finalize() . This will finish calculating the hash by appending the standard 1 bit, padding and message bitlength. You can optionally provide a last set of data to finalize() . After calling finalize() , do not update the hash if you want correct results.", + "title": "Incremental hashes (manual)" + }, + { + "location": "/crypto/hash/#fetching-the-results", + "text": "The context can then be accessed to extract the resulting Hash. let hash : Data = md5Context . hash", + "title": "Fetching the results" + }, + { + "location": "/crypto/hash/#streaming-hashes-async", + "text": "Sometimes you need to hash the contents of a Stream, for example, when processing a file transfer. In this case you can use ByteStreamHasher . First, create a new generic ByteStreamHasher Hash where Hash is the hash you want to use. In this case, SHA512. let streamHasher = ByteStreamHasher SHA512 () This stream works like any inputStream by consuming the incoming data and passing the buffers to the hash context. For example, draining a TCP socket. let socket : TCP . Socket = ... socket . drain ( into : streamHasher ) This will incrementally update the hash using the provided TCP socket's data. When the hash has been completely accumulated, you can complete the hash. let hash = streamHasher . complete () // Foundation `Data` This will reset the hash's context to the default configuration, ready to start over.", + "title": "Streaming hashes (Async)" + }, + { + "location": "/crypto/mac/", + "text": "Message authentication\n\n\nMessage authentication is used for verifying message authenticity and validity.\n\n\nCommon use cases are JSON Web Tokens.\n\n\nFor message authentication, Vapor only supports HMAC.\n\n\nUsing HMAC\n\n\nTo use HMAC you first need to select the used hashing algorithm for authentication. This works using generics.\n\n\nlet\n \nhash\n \n=\n \nHMAC\nSHA224\n.\nauthenticate\n(\nmessage\n,\n \nwithKey\n:\n \nauthenticationKey\n)", + "title": "Message authentication" + }, + { + "location": "/crypto/mac/#message-authentication", + "text": "Message authentication is used for verifying message authenticity and validity. Common use cases are JSON Web Tokens. For message authentication, Vapor only supports HMAC.", + "title": "Message authentication" + }, + { + "location": "/crypto/mac/#using-hmac", + "text": "To use HMAC you first need to select the used hashing algorithm for authentication. This works using generics. let hash = HMAC SHA224 . authenticate ( message , withKey : authenticationKey )", + "title": "Using HMAC" + }, + { + "location": "/crypto/passwords/", + "text": "Password hashing\n\n\nPassword management is critical for good user security and doesn't need to cost a lot of effort. No software is perfect. Even if your software is perfect, other software on the same server likely isn't. Good password encryption security prevents users' passwords from leaking out in case of a hypothetical future data breach.\n\n\nFor password hashing Vapor supports PBKDF2 and BCrypt.\n\n\nWe recommend using BCrypt over PBKDF2 for almost all scenarios. Whilst PBKDF2 is a proven standard, it's much more easily brute-forced than BCrypt and is less future-proof.\n\n\nBCrypt\n\n\nBCrypt is an algorithm specifically designed for password hashing. It's easy to store and verify.\n\n\nDeriving a key\n\n\nUnlike PBKDF2 you don't need to generate and store a salt, that's part of the BCrypt hashing and verification process.\n\n\nThe output is a combination of the BCrypt \"cost\" factor, salt and resulting hash. Meaning that the derived output contains all information necessary for verification, simplifying the database access.\n\n\nlet\n \nresult\n:\n \nData\n \n=\n \ntry\n \nBCrypt\n.\nmake\n(\nmessage\n:\n \nMyPassword\n)\n\n\n\nguard\n \ntry\n \nBCrypt\n.\nverify\n(\nmessage\n:\n \nMyPassword\n,\n \nmatches\n:\n \nresult\n)\n \nelse\n \n{\n\n \nfatalError\n(\nThis never triggers, since the verification process will always be successful for the same password and conditions\n)\n\n\n}\n\n\n\n\n\n\nThe default cost factor is \n12\n, based on the official recommendations.\n\n\nStoring the derived key as a String\n\n\nBCrypt always outputs valid ASCII/UTF-8 for the resulting hash.\n\n\nThis means you can convert the output \nData\n to a \nString\n as such:\n\n\nguard\n \nlet\n \nstring\n \n=\n \nString\n(\nbytes\n:\n \nresult\n,\n \nencoding\n:\n \n.\nutf8\n)\n \nelse\n \n{\n\n \n// This must never trigger\n\n\n}\n\n\n\n\n\n\nPBKDF2\n\n\nPBKDF2 is an algorithm that is almost always (and in Vapor, exclusively) used with HMAC for message authentication.\n\n\nPBKDF2 can be paired up with any hashing algorithm and is simple to implement. PBKDF2 is used all over the world through the WPA2 standard, securing WiFi connections. But we still recommend PBKDF2 above any normal hashing function.\n\n\nFor PBKDF2 you also select the Hash using generics.\n\n\nDeriving a key\n\n\nIn the following example:\n\n\n\n\npassword\n is either a \nString\n or \nData\n\n\nThe \nsalt\n is \nData\n\n\nIterations is defaulted to \n10_000\n iterations\n\n\nThe keySize is equivalent to 1 hash's length.\n\n\n\n\n// Generate a random salt\n\n\nlet\n \nsalt\n:\n \nData\n \n=\n \nOSRandom\n().\ndata\n(\ncount\n:\n \n32\n)\n\n\n\nlet\n \nhash\n \n=\n \ntry\n \nPBKDF2\nSHA256\n.\nderive\n(\nfromPassword\n:\n \npassword\n,\n \nsalt\n:\n \nsalt\n)\n\n\n\n\n\n\nYou can optionally configure PBKDF2 to use a different iteration count and output keysize.\n\n\n// Iterates 20\n000 times and outputs 100 bytes\n\n\nlet\n \nhash\n \n=\n \ntry\n \nPBKDF2\nSHA256\n.\nderive\n(\nfromPassword\n:\n \npassword\n,\n \nsalt\n:\n \nsalt\n,\n \niterating\n:\n \n20_000\n,\n \nderivedKeyLength\n:\n \n100\n)\n\n\n\n\n\n\nStoring the results\n\n\nWhen you're storing the PBKDF2 results, be sure to also store the Salt. Without the original salt, iteration count and other parameters you cannot reproduce the same hash for validation or authentication.", + "title": "Password hashing" + }, + { + "location": "/crypto/passwords/#password-hashing", + "text": "Password management is critical for good user security and doesn't need to cost a lot of effort. No software is perfect. Even if your software is perfect, other software on the same server likely isn't. Good password encryption security prevents users' passwords from leaking out in case of a hypothetical future data breach. For password hashing Vapor supports PBKDF2 and BCrypt. We recommend using BCrypt over PBKDF2 for almost all scenarios. Whilst PBKDF2 is a proven standard, it's much more easily brute-forced than BCrypt and is less future-proof.", + "title": "Password hashing" + }, + { + "location": "/crypto/passwords/#bcrypt", + "text": "BCrypt is an algorithm specifically designed for password hashing. It's easy to store and verify.", + "title": "BCrypt" + }, + { + "location": "/crypto/passwords/#deriving-a-key", + "text": "Unlike PBKDF2 you don't need to generate and store a salt, that's part of the BCrypt hashing and verification process. The output is a combination of the BCrypt \"cost\" factor, salt and resulting hash. Meaning that the derived output contains all information necessary for verification, simplifying the database access. let result : Data = try BCrypt . make ( message : MyPassword ) guard try BCrypt . verify ( message : MyPassword , matches : result ) else { \n fatalError ( This never triggers, since the verification process will always be successful for the same password and conditions ) } The default cost factor is 12 , based on the official recommendations.", + "title": "Deriving a key" + }, + { + "location": "/crypto/passwords/#storing-the-derived-key-as-a-string", + "text": "BCrypt always outputs valid ASCII/UTF-8 for the resulting hash. This means you can convert the output Data to a String as such: guard let string = String ( bytes : result , encoding : . utf8 ) else { \n // This must never trigger }", + "title": "Storing the derived key as a String" + }, + { + "location": "/crypto/passwords/#pbkdf2", + "text": "PBKDF2 is an algorithm that is almost always (and in Vapor, exclusively) used with HMAC for message authentication. PBKDF2 can be paired up with any hashing algorithm and is simple to implement. PBKDF2 is used all over the world through the WPA2 standard, securing WiFi connections. But we still recommend PBKDF2 above any normal hashing function. For PBKDF2 you also select the Hash using generics.", + "title": "PBKDF2" + }, + { + "location": "/crypto/passwords/#deriving-a-key_1", + "text": "In the following example: password is either a String or Data The salt is Data Iterations is defaulted to 10_000 iterations The keySize is equivalent to 1 hash's length. // Generate a random salt let salt : Data = OSRandom (). data ( count : 32 ) let hash = try PBKDF2 SHA256 . derive ( fromPassword : password , salt : salt ) You can optionally configure PBKDF2 to use a different iteration count and output keysize. // Iterates 20 000 times and outputs 100 bytes let hash = try PBKDF2 SHA256 . derive ( fromPassword : password , salt : salt , iterating : 20_000 , derivedKeyLength : 100 )", + "title": "Deriving a key" + }, + { + "location": "/crypto/passwords/#storing-the-results", + "text": "When you're storing the PBKDF2 results, be sure to also store the Salt. Without the original salt, iteration count and other parameters you cannot reproduce the same hash for validation or authentication.", + "title": "Storing the results" + }, + { + "location": "/crypto/random/", + "text": "Random\n\n\nCrypto has two primary random number generators.\n\n\nOSRandom generates random numbers by calling the operating system's random number generator.\n\n\nURandom generates random numbers by reading from \n/dev/urandom\n.\n\n\nAccessing random numbers\n\n\nFirst, create an instance of the preferred random number generator:\n\n\nlet\n \nrandom\n \n=\n \nOSRandom\n()\n\n\n\n\n\n\nor\n\n\nlet\n \nrandom\n \n=\n \ntry\n \nURandom\n()\n\n\n\n\n\n\nReading integers\n\n\nFor every Swift integer a random number function exists.\n\n\nlet\n \nint8\n:\n \nInt8\n \n=\n \ntry\n \nrandom\n.\nmakeInt8\n()\n\n\nlet\n \nuint8\n:\n \nUInt8\n \n=\n \ntry\n \nrandom\n.\nmakeUInt8\n()\n\n\nlet\n \nint16\n:\n \nInt16\n \n=\n \ntry\n \nrandom\n.\nmakeInt16\n()\n\n\nlet\n \nuint16\n:\n \nUInt16\n \n=\n \ntry\n \nrandom\n.\nmakeUInt16\n()\n\n\nlet\n \nint32\n:\n \nInt32\n \n=\n \ntry\n \nrandom\n.\nmakeInt32\n()\n\n\nlet\n \nuint32\n:\n \nUInt32\n \n=\n \ntry\n \nrandom\n.\nmakeUInt32\n()\n\n\nlet\n \nint64\n:\n \nInt64\n \n=\n \ntry\n \nrandom\n.\nmakeInt64\n()\n\n\nlet\n \nuint64\n:\n \nUInt64\n \n=\n \ntry\n \nrandom\n.\nmakeUInt64\n()\n\n\nlet\n \nint\n:\n \nInt\n \n=\n \ntry\n \nrandom\n.\nmakeInt\n()\n\n\nlet\n \nuint\n:\n \nUInt\n \n=\n \ntry\n \nrandom\n.\nmakeUInt\n()\n\n\n\n\n\n\nReading random data\n\n\nRandom buffers of data are useful when, for example, generating tokens or other unique strings/blobs.\n\n\nTo generate a buffer of random data:\n\n\n// generates 20 random bytes\n\n\nlet\n \ndata\n:\n \nData\n \n=\n \nrandom\n.\ndata\n(\ncount\n:\n \n20\n)", + "title": "Random" + }, + { + "location": "/crypto/random/#random", + "text": "Crypto has two primary random number generators. OSRandom generates random numbers by calling the operating system's random number generator. URandom generates random numbers by reading from /dev/urandom .", + "title": "Random" + }, + { + "location": "/crypto/random/#accessing-random-numbers", + "text": "First, create an instance of the preferred random number generator: let random = OSRandom () or let random = try URandom ()", + "title": "Accessing random numbers" + }, + { + "location": "/crypto/random/#reading-integers", + "text": "For every Swift integer a random number function exists. let int8 : Int8 = try random . makeInt8 () let uint8 : UInt8 = try random . makeUInt8 () let int16 : Int16 = try random . makeInt16 () let uint16 : UInt16 = try random . makeUInt16 () let int32 : Int32 = try random . makeInt32 () let uint32 : UInt32 = try random . makeUInt32 () let int64 : Int64 = try random . makeInt64 () let uint64 : UInt64 = try random . makeUInt64 () let int : Int = try random . makeInt () let uint : UInt = try random . makeUInt ()", + "title": "Reading integers" + }, + { + "location": "/crypto/random/#reading-random-data", + "text": "Random buffers of data are useful when, for example, generating tokens or other unique strings/blobs. To generate a buffer of random data: // generates 20 random bytes let data : Data = random . data ( count : 20 )", + "title": "Reading random data" + }, + { + "location": "/testing/getting-started/", + "text": "Getting Started with Testing\n\n\nComing soon.", + "title": "Getting Started" + }, + { + "location": "/testing/getting-started/#getting-started-with-testing", + "text": "Coming soon.", + "title": "Getting Started with Testing" + }, + { + "location": "/deploy/getting-started/", + "text": "Getting Started with Deployments\n\n\nComing soon.", + "title": "Getting Started" + }, + { + "location": "/deploy/getting-started/#getting-started-with-deployments", + "text": "Coming soon.", + "title": "Getting Started with Deployments" + }, + { + "location": "/version/1_5/", + "text": "Redirecting...", + "title": "1.5" + }, + { + "location": "/version/1_5/#redirecting", + "text": "", + "title": "Redirecting..." + }, + { + "location": "/version/2_0/", + "text": "Redirecting...", + "title": "2.0" + }, + { + "location": "/version/2_0/#redirecting", + "text": "", + "title": "Redirecting..." + }, + { + "location": "/version/3_0/", + "text": "Redirecting...", + "title": "3.0-rc" + }, + { + "location": "/version/3_0/#redirecting", + "text": "", + "title": "Redirecting..." + }, + { + "location": "/version/support/", + "text": "Version Support\n\n\n\n\nVapor 3.0 is currently in beta.\n\n\nVapor 2.0 is currently active.\n\n\nVapor 1.5 is being maintained until November 2017.\n\n\n\n\nWarning\n\n\nDashed blocks and lines represent the team's goals and are not yet guaranteed. \nVapor 3's release and the beginning of Vapor 2's maintenance phase have not yet been decided.\n\n\n\n\nCore\n\n\nAll packages in the \nVapor GitHub\n are maintained according to the following rules.\n\n\nActive\n\n\nWhile a version is active, reported security issues and bugs are fixed.\n\n\nAdditionally, new features and optimizations may be added. If new API is added, the minor version number will be incremented. At no point can existing API be removed or broken during an active version. Semver is strictly followed and tested.\n\n\nMaintenance\n\n\nWhen a new version of Vapor is released, the previous version will enter a maintenance phase which lasts for six months. During this phase, critical security issues and bugs will be fixed. No new features will be added.\n\n\n\n\nNote\n\n\nOnly the latest minor version will be maintained.\n\n\n\n\nUnstable\n\n\nThe master branch is the latest development version of Vapor and is constantly changing. Before a new version of Vapor is released, there may be unstable alpha and beta phases in which you are welcome to test and provide feedback on the changes.\n\n\nCommunity\n\n\nAll packages in the \nVapor Community GitHub\n are maintained in strict accordance of semver. Violations of semver should be reported as issues to the offending package's GitHub page.", + "title": "Support" + }, + { + "location": "/version/support/#version-support", + "text": "Vapor 3.0 is currently in beta. Vapor 2.0 is currently active. Vapor 1.5 is being maintained until November 2017. Warning Dashed blocks and lines represent the team's goals and are not yet guaranteed. \nVapor 3's release and the beginning of Vapor 2's maintenance phase have not yet been decided.", + "title": "Version Support" + }, + { + "location": "/version/support/#core", + "text": "All packages in the Vapor GitHub are maintained according to the following rules.", + "title": "Core" + }, + { + "location": "/version/support/#active", + "text": "While a version is active, reported security issues and bugs are fixed. Additionally, new features and optimizations may be added. If new API is added, the minor version number will be incremented. At no point can existing API be removed or broken during an active version. Semver is strictly followed and tested.", + "title": "Active" + }, + { + "location": "/version/support/#maintenance", + "text": "When a new version of Vapor is released, the previous version will enter a maintenance phase which lasts for six months. During this phase, critical security issues and bugs will be fixed. No new features will be added. Note Only the latest minor version will be maintained.", + "title": "Maintenance" + }, + { + "location": "/version/support/#unstable", + "text": "The master branch is the latest development version of Vapor and is constantly changing. Before a new version of Vapor is released, there may be unstable alpha and beta phases in which you are welcome to test and provide feedback on the changes.", + "title": "Unstable" + }, + { + "location": "/version/support/#community", + "text": "All packages in the Vapor Community GitHub are maintained in strict accordance of semver. Violations of semver should be reported as issues to the offending package's GitHub page.", + "title": "Community" + } + ] +} \ No newline at end of file diff --git a/3.0/site/sitemap.xml b/3.0/site/sitemap.xml new file mode 100644 index 00000000..c8076625 --- /dev/null +++ b/3.0/site/sitemap.xml @@ -0,0 +1,416 @@ + + + + + + / + 2018-03-12 + daily + + + + + + + /install/macos/ + 2018-03-12 + daily + + + + /install/ubuntu/ + 2018-03-12 + daily + + + + + + + + /getting-started/hello-world/ + 2018-03-12 + daily + + + + /getting-started/toolbox/ + 2018-03-12 + daily + + + + /getting-started/spm/ + 2018-03-12 + daily + + + + /getting-started/xcode/ + 2018-03-12 + daily + + + + /getting-started/structure/ + 2018-03-12 + daily + + + + /getting-started/application/ + 2018-03-12 + daily + + + + /getting-started/controllers/ + 2018-03-12 + daily + + + + /getting-started/routing/ + 2018-03-12 + daily + + + + /getting-started/content/ + 2018-03-12 + daily + + + + /getting-started/futures/ + 2018-03-12 + daily + + + + /getting-started/services/ + 2018-03-12 + daily + + + + /getting-started/cloud/ + 2018-03-12 + daily + + + + + + + + /routing/getting-started/ + 2018-03-12 + daily + + + + /routing/parameters/ + 2018-03-12 + daily + + + + /routing/route/ + 2018-03-12 + daily + + + + /routing/router/ + 2018-03-12 + daily + + + + + + + + /fluent/getting-started/ + 2018-03-12 + daily + + + + /fluent/models/ + 2018-03-12 + daily + + + + /fluent/migrations/ + 2018-03-12 + daily + + + + /fluent/querying/ + 2018-03-12 + daily + + + + /fluent/query-builder/ + 2018-03-12 + daily + + + + /fluent/schema-builder/ + 2018-03-12 + daily + + + + /fluent/relations/ + 2018-03-12 + daily + + + + /fluent/pivot/ + 2018-03-12 + daily + + + + /fluent/transaction/ + 2018-03-12 + daily + + + + /fluent/database/ + 2018-03-12 + daily + + + + + + + + /postgresql/getting-started/ + 2018-03-12 + daily + + + + /postgresql/fluent/ + 2018-03-12 + daily + + + + /postgresql/core/ + 2018-03-12 + daily + + + + + + + + /mysql/getting-started/ + 2018-03-12 + daily + + + + /mysql/fluent/ + 2018-03-12 + daily + + + + /mysql/core/ + 2018-03-12 + daily + + + + + + + + /sqlite/getting-started/ + 2018-03-12 + daily + + + + /sqlite/fluent/ + 2018-03-12 + daily + + + + /sqlite/core/ + 2018-03-12 + daily + + + + + + + + /leaf/getting-started/ + 2018-03-12 + daily + + + + /leaf/basics/ + 2018-03-12 + daily + + + + /leaf/custom-tags/ + 2018-03-12 + daily + + + + + + + + /redis/getting-started/ + 2018-03-12 + daily + + + + /redis/basics/ + 2018-03-12 + daily + + + + /redis/custom-commands/ + 2018-03-12 + daily + + + + /redis/pub-sub/ + 2018-03-12 + daily + + + + /redis/pipeline/ + 2018-03-12 + daily + + + + + + + + /websocket/websocket/ + 2018-03-12 + daily + + + + + + + + /crypto/getting-started/ + 2018-03-12 + daily + + + + /crypto/base64/ + 2018-03-12 + daily + + + + /crypto/hash/ + 2018-03-12 + daily + + + + /crypto/mac/ + 2018-03-12 + daily + + + + /crypto/passwords/ + 2018-03-12 + daily + + + + /crypto/random/ + 2018-03-12 + daily + + + + + + + + /testing/getting-started/ + 2018-03-12 + daily + + + + + + + + /deploy/getting-started/ + 2018-03-12 + daily + + + + + + + + /version/1_5/ + 2018-03-12 + daily + + + + /version/2_0/ + 2018-03-12 + daily + + + + /version/3_0/ + 2018-03-12 + daily + + + + /version/support/ + 2018-03-12 + daily + + + + + \ No newline at end of file diff --git a/3.0/site/sqlite/core/index.html b/3.0/site/sqlite/core/index.html new file mode 100644 index 00000000..67282614 --- /dev/null +++ b/3.0/site/sqlite/core/index.html @@ -0,0 +1,1707 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SQLite Core - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

SQLite Core

+

SQLite (vapor/sqlite) is a wrapper around the libsqlite C-library.

+
+

Seealso

+

The higher-level, Fluent SQLite ORM guide is located at SQLite → Fluent

+
+

Using just the SQLite package for your project may be a good idea if any of the following are true.

+
    +
  • You have an existing DB with non-standard structure.
  • +
  • You rely heavily on custom or complex SQL queries.
  • +
  • You just plain don't like ORMs.
  • +
+

SQLite core is built on top of DatabaseKit which provides some conveniences like connection pooling and integrations with Vapor's Services architecture.

+
+

Tip

+

Even if you do choose to use Fluent SQLite, all of the features of SQLite core will be available to you.

+
+

Getting Started

+

Let's take a look at how you can get started using SQLite core.

+

Package

+

The first step to using SQLite core is adding it as a dependency to your project in your SPM package manifest file.

+
// swift-tools-version:4.0
+import PackageDescription
+
+let package = Package(
+    name: "MyApp",
+    dependencies: [
+        /// Any other dependencies ...
+
+        // 🔵 SQLite 3 wrapper for Swift.
+        .package(url: "https://github.com/vapor/sqlite.git", from: "3.0.0-rc"),
+    ],
+    targets: [
+        .target(name: "App", dependencies: ["SQLite", ...]),
+        .target(name: "Run", dependencies: ["App"]),
+        .testTarget(name: "AppTests", dependencies: ["App"]),
+    ]
+)
+
+ + +

Don't forget to add the module as a dependency in the targets array. Once you have added the dependency, regenerate your Xcode project with the following command:

+
vapor xcode
+
+ + +

Config

+

The next step is to configure the database in your configure.swift file.

+
import SQLite
+
+/// ...
+
+/// Register providers first
+try services.register(SQLiteProvider())
+
+ + +

Registering the provider will add all of the services required for SQLite to work properly. It also includes a default database config struct that uses an in-memory DB.

+

You can of course override this config struct if you have non-standard credentials.

+
/// Register custom SQLite Config
+let sqliteConfig = SQLiteDatabaseConfig(storage: .memory)
+services.register(sqliteConfig)
+
+ + +

Query

+

Now that the database is configured, you can make your first query.

+
router.get("sqlite-version") { req -> Future<String> in
+    return req.withPooledConnection(to: .sqlite) { conn in
+        return try conn.query("select sqlite_version() as v;").map(to: String.self) { rows in
+            return try rows[0].firstValue(forColumn: "v")?.decode(String.self) ?? "n/a"
+        }
+    }
+}
+
+ + +

Visiting this route should display your SQLite version.

+

Connection

+

A SQLiteConnection is normally created using the Request container and can perform two different types of queries.

+

Create

+

There are two methods for creating a SQLiteConnection.

+
return req.withPooledConnection(to: .sqlite) { conn in
+    /// ...
+}
+return req.withConnection(to: .sqlite) { conn in
+    /// ...
+}
+
+ + +

As the names imply, withPooledConnection(to:) utilizes a connection pool. withConnection(to:) does not. Connection pooling is a great way to ensure your application does not exceed the limits of your database, even under peak load.

+

Simply Query

+

Use .simpleQuery(_:) to perform a query on your SQLite database that does not bind any parameters. Some queries you send to SQLite may actually require that you use the simpleQuery(_:) method instead of the parameterized method.

+
+

Note

+

This method sends and receives data as text-encoded, meaning it is not optimal for transmitting things like integers.

+
+
let rows = req.withPooledConnection(to: .sqlite) { conn in
+    return conn.simpleQuery("SELECT * FROM users;")
+}
+print(rows) // Future<[[SQLiteColumn: SQLiteData]]>
+
+ + +

You can also choose to receive each row in a callback, which is great for conserving memory for large queries.

+
let done = req.withPooledConnection(to: .sqlite) { conn in
+    return conn.simpleQuery("SELECT * FROM users;") { row in
+        print(row) // [SQLiteColumn: SQLiteData]
+    }
+}
+print(done) // Future<Void>
+
+ + +

Parameterized Query

+

SQLite also supports sending parameterized queries (sometimes called prepared statements). This method allows you to insert data placeholders into the SQL string and send the values separately.

+

Data sent via parameterized queries is binary encoded, making it more efficient for sending some data types. In general, you should use parameterized queries where ever possible.

+
let users = req.withPooledConnection(to: .sqlite) { conn in
+    return try conn.query("SELECT *  users WHERE name = $1;", ["Vapor"])
+}
+print(users) // Future<[[SQLiteColumn: SQLiteData]]>
+
+ + +

You can also provide a callback, similar to simple queries, for handling each row individually.

+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/sqlite/fluent/index.html b/3.0/site/sqlite/fluent/index.html new file mode 100644 index 00000000..4a206613 --- /dev/null +++ b/3.0/site/sqlite/fluent/index.html @@ -0,0 +1,1720 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Fluent SQLite - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

Fluent SQLite

+

Fluent SQLite (vapor/fluent-sqlite) is a type-safe, fast, and easy-to-use ORM for SQLite built on top of Fluent.

+
+

Seealso

+

The Fluent SQLite package is built on top of Fluent and the pure Swift, NIO-based SQLite core. You should refer to their guides for more information about subjects not covered here.

+
+

Getting Started

+

This section will show you how to add Fluent SQLite to your project and create your first SQLiteModel.

+

Package

+

The first step to using Fluent SQLite is adding it as a dependency to your project in your SPM package manifest file.

+
// swift-tools-version:4.0
+import PackageDescription
+
+let package = Package(
+    name: "MyApp",
+    dependencies: [
+        /// Any other dependencies ...
+
+        // 🖋🐬 Swift ORM (queries, models, relations, etc) built on SQLite.
+        .package(url: "https://github.com/vapor/fluent-sqlite.git", from: "3.0.0-rc"),
+    ],
+    targets: [
+        .target(name: "App", dependencies: ["FluentSQLite", ...]),
+        .target(name: "Run", dependencies: ["App"]),
+        .testTarget(name: "AppTests", dependencies: ["App"]),
+    ]
+)
+
+ + +

Don't forget to add the module as a dependency in the targets array. Once you have added the dependency, regenerate your Xcode project with the following command:

+
vapor xcode
+
+ + +

Model

+

Now let's create our first SQLiteModel. Models represent tables in your SQLite database and they are the primary method of interacting with your data.

+
/// A simple user.
+final class User: SQLiteModel {
+    /// The unique identifier for this user.
+    var id: Int?
+
+    /// The user's full name.
+    var name: String
+
+    /// The user's current age in years.
+    var age: Int
+
+    /// Creates a new user.
+    init(id: Int? = nil, name: String, age: Int) {
+        self.id = id
+        self.name = name
+        self.age = age
+    }
+}
+
+ + +

The example above shows a SQLiteModel for a simple model representing a user. You can make both structs and classes a model. You can even conform types that come from external modules. The only requirement is that these types conform to Codable, which must be declared on the base type for synthesized (automatic) conformance.

+

Standard practice with SQLite databases is using an auto-generated INTEGER for creating and storing unique identifiers in the id column. It's also possible to use UUIDs or even Strings for your identifiers. There are convenience protocol for that.

+ + + + + + + + + + + + + + + + + + + + + + + + + +
protocoltypekey
SQLiteModelIntid
SQLiteUUIDModelUUIDid
SQLiteStringModelStringid
+
+

Seealso

+

Take a look at Fluent → Model for more information on creating models with custom ID types and keys.

+
+

Migration

+

All of your models (with some rare exceptions) should have a corresponding table—or schema—in your database. You can use a Fluent → Migration to automatically generate this schema in a testable, maintainable way. Fluent makes it easy to automatically generate a migration for your model

+
+

Tip

+

If you are creating models to represent an existing table or database, you can skip this step.

+
+
/// Allows `User` to be used as a migration.
+extension User: Migration { }
+
+ + +

That's all it takes. Fluent uses Codable to analyze your model and will attempt to create the best possible schema for it.

+

Take a look at Fluent → Migration if you are interested in customizing this migration.

+

Configure

+

The final step is to configure your database. At a minimum, this requires adding two things to your configure.swift file.

+
    +
  • FluentSQLiteProvider
  • +
  • MigrationConfig
  • +
+

Let's take a look.

+
import FluentSQLite
+
+/// ...
+
+/// Register providers first
+try services.register(FluentSQLiteProvider())
+
+/// Configure migrations
+var migrations = MigrationConfig()
+migrations.add(model: User.self, database: .sqlite)
+services.register(migrations)
+
+/// Other services....
+
+ + +

Registering the provider will add all of the services required for Fluent SQLite to work properly. It also includes a default database config struct that uses typical development environment credentials.

+

You can of course override this config struct if you have non-standard credentials.

+
/// Register custom SQLite Config
+let sqliteConfig = SQLiteDatabaseConfig(hostname: "localhost", port: 5432, username: "vapor")
+services.register(sqliteConfig)
+
+ + +

Once you have the MigrationConfig added, you should be able to run your application and see the following:

+
Migrating sqlite DB
+Migrations complete
+Server starting on http://localhost:8080
+
+ + +

Query

+

Now that you have created a model and a corresponding schema in your database, let's make your first query.

+
router.get("users") { req in
+    return User.query(on: req).all()
+}
+
+ + +

If you run your app, and query that route, you should see an empty array returned. Now you just need to add some users! Congratulations on getting your first Fluent SQLite model and migration working.

+

Connection

+

With Fluent, you always have access to the underlying database driver. Using this underlying driver to perform a query is sometimes called a "raw query".

+

Let's take a look at a raw SQLite query.

+
router.get("sqlite-version") { req -> Future<String> in
+    return req.withPooledConnection(to: .sqlite) { conn in
+        return try conn.query("select sqlite_version() as v;").map(to: String.self) { rows in
+            return try rows[0].firstValue(forColumn: "v")?.decode(String.self) ?? "n/a"
+        }
+    }
+}
+
+ + +

In the above example, withPooledConnection(to:) is used to create a connection to the database identified by .sqlite. This is the default database identifier. See Fluent → Database to learn more.

+

Once we have the SQLiteConnection, we can perform a query on it. You can learn more about the methods available in SQLite → Core.

+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/sqlite/getting-started/index.html b/3.0/site/sqlite/getting-started/index.html new file mode 100644 index 00000000..4eb114b7 --- /dev/null +++ b/3.0/site/sqlite/getting-started/index.html @@ -0,0 +1,1422 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Getting Started - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

Getting Started with SQLite

+

SQLite is a open-source, embedded database. It's simplistic nature makes it a great candiate for prototyping and testing.

+

You can use SQLite with Vapor (or any server-side Swift framework) by either:

+ +

We recommend using the ORM since it does a lot of the hard work for you. Check out the respective guides to learn more.

+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/stylesheets/extra.css b/3.0/site/stylesheets/extra.css new file mode 100644 index 00000000..27fff6f2 --- /dev/null +++ b/3.0/site/stylesheets/extra.css @@ -0,0 +1,48 @@ +.md-header { + box-shadow: 0px 0px 8px #657590!important; +} + +.md-main__inner { + padding-top: 0; +} + +.md-nav__button img { + width: 90%; +} + +.md-logo { + padding: 6px; +} + +.md-sidebar__inner .md-logo { + width: 60px!important; +} + +.md-header-nav__title { + line-height: 4.6rem; +} + +@media only screen and (min-width:75em) { + .md-header-nav__button img { + width: 28px!important; + height: 28px!important; + } + + .md-main__inner { + padding-top: 1rem; + } +} + +@media only screen and (min-width:100em) { + .md-header-nav__button img { + width: 32px!important; + height: 32px!important; + } +} + +@media only screen and (min-width:125em) { + .md-header-nav__button img { + width: 38px!important; + height: 38px!important; + } +} diff --git a/3.0/site/testing/getting-started/index.html b/3.0/site/testing/getting-started/index.html new file mode 100644 index 00000000..f4367863 --- /dev/null +++ b/3.0/site/testing/getting-started/index.html @@ -0,0 +1,1416 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Getting Started - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

Getting Started with Testing

+

Coming soon.

+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/version/1_5/index.html b/3.0/site/version/1_5/index.html new file mode 100644 index 00000000..5a1d303a --- /dev/null +++ b/3.0/site/version/1_5/index.html @@ -0,0 +1,1416 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1.5 - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

Redirecting...

+

+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/version/2_0/index.html b/3.0/site/version/2_0/index.html new file mode 100644 index 00000000..bd74232b --- /dev/null +++ b/3.0/site/version/2_0/index.html @@ -0,0 +1,1416 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 2.0 - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

Redirecting...

+

+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/version/3_0/index.html b/3.0/site/version/3_0/index.html new file mode 100644 index 00000000..647b5d51 --- /dev/null +++ b/3.0/site/version/3_0/index.html @@ -0,0 +1,1416 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 3.0-rc - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

Redirecting...

+

+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/version/support/index.html b/3.0/site/version/support/index.html new file mode 100644 index 00000000..5db2008e --- /dev/null +++ b/3.0/site/version/support/index.html @@ -0,0 +1,1538 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Support - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

Version Support

+

Support Matrix

+

Vapor 3.0 is currently in beta.

+

Vapor 2.0 is currently active.

+

Vapor 1.5 is being maintained until November 2017.

+
+

Warning

+

Dashed blocks and lines represent the team's goals and are not yet guaranteed. +Vapor 3's release and the beginning of Vapor 2's maintenance phase have not yet been decided.

+
+

Core

+

All packages in the Vapor GitHub are maintained according to the following rules.

+

Active

+

While a version is active, reported security issues and bugs are fixed.

+

Additionally, new features and optimizations may be added. If new API is added, the minor version number will be incremented. At no point can existing API be removed or broken during an active version. Semver is strictly followed and tested.

+

Maintenance

+

When a new version of Vapor is released, the previous version will enter a maintenance phase which lasts for six months. During this phase, critical security issues and bugs will be fixed. No new features will be added.

+
+

Note

+

Only the latest minor version will be maintained.

+
+

Unstable

+

The master branch is the latest development version of Vapor and is constantly changing. Before a new version of Vapor is released, there may be unstable alpha and beta phases in which you are welcome to test and provide feedback on the changes.

+

Community

+

All packages in the Vapor Community GitHub are maintained in strict accordance of semver. Violations of semver should be reported as issues to the offending package's GitHub page.

+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/3.0/site/websocket/websocket/index.html b/3.0/site/websocket/websocket/index.html new file mode 100644 index 00000000..bfa67642 --- /dev/null +++ b/3.0/site/websocket/websocket/index.html @@ -0,0 +1,1416 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Getting Started - Vapor Docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + +
+ +
+ +
+ + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + +

WebSocket

+

Coming soon.

+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + + + \ No newline at end of file