├── glues
├── ServeStatic
│ ├── README.md
│ ├── tests
│ │ ├── files
│ │ │ └── users
│ │ │ │ ├── tobi.txt
│ │ │ │ └── index.html
│ │ ├── Tests.ServeStatic.fsproj
│ │ └── Tests.ServeStatic.fs
│ ├── CHANGELOG.md
│ └── src
│ │ ├── Glutinum.ServeStatic.fsproj
│ │ └── Glutinum.ServeStatic.fs
├── Express
│ ├── tests
│ │ ├── fixtures
│ │ │ ├── .name
│ │ │ ├── empty.txt
│ │ │ ├── broken.send
│ │ │ ├── name.txt
│ │ │ ├── % of dogs.txt
│ │ │ ├── nums.txt
│ │ │ ├── snow ☃
│ │ │ │ └── .gitkeep
│ │ │ ├── todo.txt
│ │ │ ├── name.tmpl
│ │ │ ├── pets
│ │ │ │ └── names.txt
│ │ │ ├── users
│ │ │ │ ├── tobi.txt
│ │ │ │ └── index.html
│ │ │ ├── blog
│ │ │ │ ├── index.html
│ │ │ │ └── post
│ │ │ │ │ └── index.tmpl
│ │ │ ├── todo.html
│ │ │ ├── user.html
│ │ │ ├── user.tmpl
│ │ │ ├── email.tmpl
│ │ │ ├── default_layout
│ │ │ │ ├── name.tmpl
│ │ │ │ └── user.tmpl
│ │ │ └── local_layout
│ │ │ │ └── user.tmpl
│ │ ├── Tests.Express.Port.App.fs
│ │ ├── Tests.Express.Port.App.Listen.fs
│ │ ├── Tests.Express.Port.App.Del.fs
│ │ ├── support
│ │ │ └── tmpl.js
│ │ ├── Tests.Express.Port.App.Locals.fs
│ │ ├── Tests.Express.Port.App.All.fs
│ │ ├── Tests.Express.Port.App.Response.fs
│ │ ├── Tests.Express.fsproj
│ │ ├── Tests.Express.Port.App.Route.fs
│ │ ├── Tests.Express.Port.App.Head.fs
│ │ ├── Tests.Express.Port.App.Routes.Error.fs
│ │ ├── Tests.Express.Port.App.Request.fs
│ │ ├── Tests.Express.Port.App.Engine.fs
│ │ ├── Tests.Express.Port.App.Options.fs
│ │ ├── Tests.Express.Port.App.Render.fs
│ │ └── Tests.Express.Port.App.Params.fs
│ ├── CHANGELOG.md
│ ├── README.md
│ └── src
│ │ ├── Glutinum.Express.fsproj
│ │ └── Glutinum.Express.fs
├── ExpressServeStaticCore
│ ├── README.md
│ ├── CHANGELOG.md
│ └── src
│ │ └── Glutinum.ExpressServeStaticCore.fsproj
├── Connect
│ ├── src
│ │ ├── Glutinum.Connect.Ext.fs
│ │ ├── Glutinum.Connect.fsproj
│ │ └── Glutinum.Connect.fs
│ ├── README.md
│ ├── CHANGELOG.md
│ └── tests
│ │ ├── Tests.Connect.fsproj
│ │ └── Tests.Connect.fs
├── After
│ ├── src
│ │ ├── Glutinum.After.fs
│ │ └── Glutinum.After.fsproj
│ ├── CHANGELOG.md
│ └── README.md
├── RangeParser.Extensions
│ ├── CHANGELOG.md
│ ├── src
│ │ ├── Glutinum.RangeParser.Extensions.fs
│ │ └── Glutinum.RangeParser.Extensions.fsproj
│ └── tests
│ │ ├── Tests.RangeParser.fsproj
│ │ └── Tests.RangeParser.fs
├── Chalk
│ ├── CHANGELOG.md
│ ├── tests
│ │ ├── Tests.Chalk.fsproj
│ │ ├── Tests.Chalk.Instance.fs
│ │ ├── Tests.Chalk.Level.fs
│ │ └── Tests.Chalk.ChalkJS.fs
│ ├── src
│ │ ├── Glutinum.Chalk.fsproj
│ │ └── Glutinum.Chalk.fs
│ └── README.md
├── Methods
│ ├── CHANGELOG.md
│ ├── README.md
│ └── src
│ │ ├── Glutinum.Methods.fsproj
│ │ └── Glutinum.Methods.fs
├── SuperAgent
│ ├── CHANGELOG.md
│ ├── README.md
│ └── src
│ │ ├── Glutinum.SuperAgent.fsproj
│ │ └── Glutinum.SuperAgent.fs
├── SuperTest
│ ├── CHANGELOG.md
│ ├── README.md
│ └── src
│ │ ├── Glutinum.SuperTest.fsproj
│ │ └── Glutinum.SuperTest.fs
├── BodyParser
│ ├── README.md
│ ├── CHANGELOG.md
│ ├── tests
│ │ ├── Tests.BodyParser.fsproj
│ │ ├── Tests.BodyParser.Text.fs
│ │ └── Tests.BodyParser.Json.fs
│ └── src
│ │ ├── Glutinum.BodyParser.fsproj
│ │ └── Glutinum.BodyParser.fs
├── Qs
│ ├── README.md
│ ├── CHANGELOG.md
│ ├── tests
│ │ ├── Tests.Qs.fsproj
│ │ └── Tests.Qs.fs
│ └── src
│ │ ├── Glutinum.Qs.fsproj
│ │ └── Glutinum.Qs.fs
├── Mime
│ ├── README.md
│ ├── CHANGELOG.md
│ ├── tests
│ │ ├── Tests.Mime.fsproj
│ │ └── Tests.Mime.fs
│ └── src
│ │ ├── Glutinum.Mime.fsproj
│ │ └── Glutinum.Mime.fs
└── RangeParser
│ ├── src
│ ├── Glutinum.RangeParser.fsproj
│ └── Glutinum.RangeParser.fs
│ ├── README.md
│ └── CHANGELOG.md
├── tests-shared
├── mocha.env.js
├── Fable.Core.JsInterop.fs
├── Mocha.fs
├── Tests.Shared.props
├── Globals.fs
└── Node.Assert.fs
├── global.json
├── .editorconfig
├── Settings.FSharpLint
├── .config
└── dotnet-tools.json
├── .github
├── ISSUE_TEMPLATE
│ └── typescript-to-f--structure-conversion.md
└── FUNDING.yml
├── scripts
├── await-spawn.js
└── init-glue-templates.js
├── LICENSE
├── package.json
├── README.md
└── .gitignore
/glues/ServeStatic/README.md:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/glues/Express/tests/fixtures/.name:
--------------------------------------------------------------------------------
1 | tobi
--------------------------------------------------------------------------------
/glues/Express/tests/fixtures/empty.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/glues/ExpressServeStaticCore/README.md:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/glues/Express/tests/fixtures/broken.send:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/glues/Express/tests/fixtures/name.txt:
--------------------------------------------------------------------------------
1 | tobi
--------------------------------------------------------------------------------
/glues/Express/tests/fixtures/% of dogs.txt:
--------------------------------------------------------------------------------
1 | 20%
--------------------------------------------------------------------------------
/glues/Express/tests/fixtures/nums.txt:
--------------------------------------------------------------------------------
1 | 123456789
--------------------------------------------------------------------------------
/glues/Express/tests/fixtures/snow ☃/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/glues/Express/tests/fixtures/todo.txt:
--------------------------------------------------------------------------------
1 | - groceries
--------------------------------------------------------------------------------
/glues/Express/tests/fixtures/name.tmpl:
--------------------------------------------------------------------------------
1 |
$name
--------------------------------------------------------------------------------
/glues/Express/tests/fixtures/pets/names.txt:
--------------------------------------------------------------------------------
1 | tobi,loki
--------------------------------------------------------------------------------
/glues/Express/tests/fixtures/users/tobi.txt:
--------------------------------------------------------------------------------
1 | ferret
--------------------------------------------------------------------------------
/glues/ServeStatic/tests/files/users/tobi.txt:
--------------------------------------------------------------------------------
1 | ferret
--------------------------------------------------------------------------------
/glues/Express/tests/fixtures/blog/index.html:
--------------------------------------------------------------------------------
1 | index
--------------------------------------------------------------------------------
/glues/Express/tests/fixtures/todo.html:
--------------------------------------------------------------------------------
1 | groceries
--------------------------------------------------------------------------------
/glues/Express/tests/fixtures/user.html:
--------------------------------------------------------------------------------
1 | {{user.name}}
--------------------------------------------------------------------------------
/glues/Express/tests/fixtures/user.tmpl:
--------------------------------------------------------------------------------
1 | $user.name
--------------------------------------------------------------------------------
/glues/Express/tests/fixtures/email.tmpl:
--------------------------------------------------------------------------------
1 | This is an email
--------------------------------------------------------------------------------
/tests-shared/mocha.env.js:
--------------------------------------------------------------------------------
1 | process.env.NODE_ENV = 'test';
2 |
--------------------------------------------------------------------------------
/glues/Express/tests/fixtures/blog/post/index.tmpl:
--------------------------------------------------------------------------------
1 | blog post
--------------------------------------------------------------------------------
/glues/Express/tests/fixtures/default_layout/name.tmpl:
--------------------------------------------------------------------------------
1 | $name
--------------------------------------------------------------------------------
/glues/Express/tests/fixtures/users/index.html:
--------------------------------------------------------------------------------
1 | tobi, loki, jane
--------------------------------------------------------------------------------
/glues/Express/tests/fixtures/default_layout/user.tmpl:
--------------------------------------------------------------------------------
1 | $user.name
--------------------------------------------------------------------------------
/glues/ServeStatic/tests/files/users/index.html:
--------------------------------------------------------------------------------
1 | tobi, loki, jane
--------------------------------------------------------------------------------
/glues/Express/tests/fixtures/local_layout/user.tmpl:
--------------------------------------------------------------------------------
1 | $user.name
--------------------------------------------------------------------------------
/global.json:
--------------------------------------------------------------------------------
1 | {
2 | "sdk": {
3 | "version": "8.0.100",
4 | "rollForward": "minor"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | end_of_line = lf
5 | insert_final_newline = true
6 | indent_style = space
7 | indent_size = 4
8 |
--------------------------------------------------------------------------------
/tests-shared/Fable.Core.JsInterop.fs:
--------------------------------------------------------------------------------
1 | module Fable.Core.JsInterop
2 |
3 | open Fable.Core
4 |
5 | []
6 | let inline jsDelete<'T> (v : 'T) : unit = jsNative
7 |
--------------------------------------------------------------------------------
/Settings.FSharpLint:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | False
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/glues/Connect/src/Glutinum.Connect.Ext.fs:
--------------------------------------------------------------------------------
1 | []
2 | module Glutinum.Connect.Ext
3 |
4 | open Fable.Core
5 |
6 | type Node.Http.IExports with
7 | []
8 | member __.createServer(_ : Connect.CreateServer.Server) : Node.Http.Server = jsNative
9 |
--------------------------------------------------------------------------------
/.config/dotnet-tools.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 1,
3 | "isRoot": true,
4 | "tools": {
5 | "fable": {
6 | "version": "4.19.3",
7 | "commands": [
8 | "fable"
9 | ]
10 | },
11 | "femto": {
12 | "version": "0.9.0",
13 | "commands": [
14 | "femto"
15 | ]
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/glues/Connect/README.md:
--------------------------------------------------------------------------------
1 | # Glutinum.Connect
2 |
3 | Binding for [connect](https://www.npmjs.com/package/connect)
4 |
5 | # Usage
6 |
7 | ```fs
8 | open Connect
9 | let app = connect()
10 |
11 | // We need to help the compiler with a type hint
12 | app.``use``(fun req res ->
13 | res.``end``("Hello, world!")
14 | ) |> ignore
15 | ```
16 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/typescript-to-f--structure-conversion.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: TypeScript to F# structure conversion
3 | about: Use this template to ask supporting a new TypeScript to F# conversion
4 | title: ''
5 | labels: TS to F# conversion
6 | assignees: ''
7 |
8 | ---
9 |
10 | ```ts
11 |
12 | ```
13 |
14 | should translates to
15 |
16 | ```fs
17 |
18 | ```
19 |
--------------------------------------------------------------------------------
/glues/After/src/Glutinum.After.fs:
--------------------------------------------------------------------------------
1 | // ts2fable 0.8.0
2 | module rec Glutinum.After
3 |
4 | open Fable.Core
5 |
6 | []
7 | let after : IExports = jsNative
8 |
9 | type [] IExports =
10 | []
11 | abstract Invoke : count : int * callback : 'Callback * ?errCallback : (obj option -> unit) -> 'T
12 |
--------------------------------------------------------------------------------
/tests-shared/Mocha.fs:
--------------------------------------------------------------------------------
1 | module Mocha
2 |
3 | open Fable.Core
4 |
5 | let [] describe (name: string) (f: unit->unit) : unit = jsNative
6 | let [] it (msg: string) (f: unit->unit) : unit = jsNative
7 | let [] itAsync (msg: string) (f: (obj->unit)->unit) : unit = jsNative
8 | let [] beforeEach (f: unit->unit) : unit = jsNative
9 |
10 |
--------------------------------------------------------------------------------
/glues/RangeParser.Extensions/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 | All notable changes to this project will be documented in this file.
3 |
4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6 |
7 | ## Unreleased
8 |
9 | ## 1.0.0 - 2021-03-17
10 |
11 | ### Added
12 |
13 | * Initial release
14 |
--------------------------------------------------------------------------------
/glues/Chalk/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 | All notable changes to this project will be documented in this file.
3 |
4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6 |
7 | ## Unreleased
8 |
9 | ## 1.0.0 - 2021-07-26
10 |
11 | ### Added
12 |
13 | * Release 1.0
14 |
15 | ## 0.1.0-alpha-001 - 2021-03-17
16 |
17 | * Initial release
18 |
--------------------------------------------------------------------------------
/glues/After/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | All notable changes to this project will be documented in this file.
4 |
5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7 |
8 | ## Unreleased
9 |
10 | ## 1.0.0 - 2021-07-26
11 |
12 | ### Added
13 |
14 | * Release 1.0
15 |
16 | ## 0.1.0-alpha-001 - 2021-03-17
17 |
18 | * Initial release
19 |
--------------------------------------------------------------------------------
/glues/Connect/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 | All notable changes to this project will be documented in this file.
3 |
4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6 |
7 | ## Unreleased
8 |
9 | ## 0.1.0-alpha-002 - 2021-03-17
10 |
11 | * Fix package description
12 |
13 | ## 0.1.0-alpha-001 - 2021-03-17
14 |
15 | * Initial release
16 |
--------------------------------------------------------------------------------
/glues/Methods/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 | All notable changes to this project will be documented in this file.
3 |
4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6 |
7 | ## Unreleased
8 |
9 | ## 0.1.0-alpha-002 - 2021-03-17
10 |
11 | * Fix package description
12 |
13 | ## 0.1.0-alpha-001 - 2021-03-17
14 |
15 | * Initial release
16 |
--------------------------------------------------------------------------------
/glues/ServeStatic/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 | All notable changes to this project will be documented in this file.
3 |
4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6 |
7 | ## Unreleased
8 |
9 | ## 1.0.0 - 2021-07-26
10 |
11 | ### Added
12 |
13 | * Release 1.0
14 |
15 | ## 0.1.0-alpha-001 - 2021-03-17
16 |
17 | * Initial release
18 |
--------------------------------------------------------------------------------
/glues/SuperAgent/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 | All notable changes to this project will be documented in this file.
3 |
4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6 |
7 | ## Unreleased
8 |
9 | ## 0.1.0-alpha-002 - 2021-03-17
10 |
11 | * Fix package description
12 |
13 | ## 0.1.0-alpha-001 - 2021-03-17
14 |
15 | * Initial release
16 |
--------------------------------------------------------------------------------
/glues/SuperTest/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 | All notable changes to this project will be documented in this file.
3 |
4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6 |
7 | ## Unreleased
8 |
9 | ## 0.1.0-alpha-002 - 2021-03-17
10 |
11 | * Fix package description
12 |
13 | ## 0.1.0-alpha-001 - 2021-03-17
14 |
15 | * Initial release
16 |
--------------------------------------------------------------------------------
/glues/After/README.md:
--------------------------------------------------------------------------------
1 | # BodyParser
2 |
3 | Binding for [body-parser](https://www.npmjs.com/package/body-parser)
4 |
5 | # Usage
6 |
7 | ```fs
8 | open BodyParser
9 |
10 | let bodyParser =
11 | let options =
12 | jsOptions(fun o ->
13 | o.limit <- !^ "1kb"
14 | )
15 |
16 | bodyParser.json(options)
17 |
18 | // If used with Express
19 | open Express
20 |
21 | let app = express.express ()
22 | app.``use``(bodyParser)
23 | ```
24 |
--------------------------------------------------------------------------------
/glues/BodyParser/README.md:
--------------------------------------------------------------------------------
1 | # BodyParser
2 |
3 | Binding for [body-parser](https://www.npmjs.com/package/body-parser)
4 |
5 | # Usage
6 |
7 | ```fs
8 | open BodyParser
9 |
10 | let bodyParser =
11 | let options =
12 | jsOptions(fun o ->
13 | o.limit <- !^ "1kb"
14 | )
15 |
16 | bodyParser.json(options)
17 |
18 | // If used with Express
19 | open Express
20 |
21 | let app = express.express ()
22 | app.``use``(bodyParser)
23 | ```
24 |
--------------------------------------------------------------------------------
/glues/Methods/README.md:
--------------------------------------------------------------------------------
1 | # BodyParser
2 |
3 | Binding for [body-parser](https://www.npmjs.com/package/body-parser)
4 |
5 | # Usage
6 |
7 | ```fs
8 | open BodyParser
9 |
10 | let bodyParser =
11 | let options =
12 | jsOptions(fun o ->
13 | o.limit <- !^ "1kb"
14 | )
15 |
16 | bodyParser.json(options)
17 |
18 | // If used with Express
19 | open Express
20 |
21 | let app = express.express ()
22 | app.``use``(bodyParser)
23 | ```
24 |
--------------------------------------------------------------------------------
/glues/SuperAgent/README.md:
--------------------------------------------------------------------------------
1 | # BodyParser
2 |
3 | Binding for [body-parser](https://www.npmjs.com/package/body-parser)
4 |
5 | # Usage
6 |
7 | ```fs
8 | open BodyParser
9 |
10 | let bodyParser =
11 | let options =
12 | jsOptions(fun o ->
13 | o.limit <- !^ "1kb"
14 | )
15 |
16 | bodyParser.json(options)
17 |
18 | // If used with Express
19 | open Express
20 |
21 | let app = express.express ()
22 | app.``use``(bodyParser)
23 | ```
24 |
--------------------------------------------------------------------------------
/glues/SuperTest/README.md:
--------------------------------------------------------------------------------
1 | # BodyParser
2 |
3 | Binding for [body-parser](https://www.npmjs.com/package/body-parser)
4 |
5 | # Usage
6 |
7 | ```fs
8 | open BodyParser
9 |
10 | let bodyParser =
11 | let options =
12 | jsOptions(fun o ->
13 | o.limit <- !^ "1kb"
14 | )
15 |
16 | bodyParser.json(options)
17 |
18 | // If used with Express
19 | open Express
20 |
21 | let app = express.express ()
22 | app.``use``(bodyParser)
23 | ```
24 |
--------------------------------------------------------------------------------
/glues/Qs/README.md:
--------------------------------------------------------------------------------
1 | # Qs
2 |
3 | Binding for [qs](https://www.npmjs.com/package/qs)
4 |
5 | # Usage
6 |
7 | ```fs
8 | open Qs
9 |
10 | let res = qs.parse("0=foo")
11 |
12 | // You can access the value store at index 0 like that
13 | res.[0]
14 | // Return: Some (U4.Case "foo")
15 |
16 | // You can also pass options to the 'parse' function
17 | qs.parse(
18 | "a[0]=b&a[1]=c",
19 | jsOptions(fun o ->
20 | o.arrayFormat <- Qs.IArrayFormat.Brackets
21 | )
22 | )
23 | ```
24 |
--------------------------------------------------------------------------------
/tests-shared/Tests.Shared.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/glues/Mime/README.md:
--------------------------------------------------------------------------------
1 | # Mime
2 |
3 | Binding for [mime](https://www.npmjs.com/package/mime)
4 |
5 | # Usage
6 |
7 | ```fs
8 | open Mime
9 |
10 | // You can create a custom Mime instance
11 | let typeMap = createEmpty
12 | typeMap.["text/a"] <- ResizeArray(["a"; "a1"])
13 | typeMap.["text/b"] <- ResizeArray(["b"; "b1"])
14 |
15 | let myMime = mime.Mime(typeMap)
16 |
17 | // You can also use the default settings
18 | mime.getType("txt")
19 | // Returns: Some "text/plain"
20 |
21 | mime.getExtension("text/html")
22 | // Returns: Some "html"
23 | ```
24 |
--------------------------------------------------------------------------------
/glues/Mime/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 | All notable changes to this project will be documented in this file.
3 |
4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6 |
7 | ## Unreleased
8 |
9 | ## 2.0.0 - 2024-08-14
10 |
11 | ### Changed
12 |
13 | * Updated to support `Mime@4`
14 |
15 | ## 1.0.0 - 2021-07-26
16 |
17 | ### Added
18 |
19 | * Release 1.0
20 |
21 | ## 0.1.0-alpha-001 - 2021-03-17
22 |
23 | ### Added
24 |
25 | * Initial release
26 |
--------------------------------------------------------------------------------
/glues/Qs/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 | All notable changes to this project will be documented in this file.
3 |
4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6 |
7 | ## Unreleased
8 |
9 | ## 1.0.0 - 2021-07-26
10 |
11 | ### Added
12 |
13 | * Release 1.0
14 |
15 | ## 0.1.0-alpha-002 - 2021-03-17
16 |
17 | ### Changed
18 |
19 | * Fix package description
20 |
21 | ## 0.1.0-alpha-001 - 2021-03-17
22 |
23 | ### Added
24 |
25 | * Initial release
26 |
--------------------------------------------------------------------------------
/glues/Express/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 | All notable changes to this project will be documented in this file.
3 |
4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6 |
7 | ## Unreleased
8 |
9 | ## 0.2.0 - 2024-08-14
10 |
11 | * Release to force update of `ExpressServeStaticCore` dependency to `0.2.0`
12 |
13 | ## 0.1.0-alpha-002 - 2021-03-17
14 |
15 | * Fix package description
16 |
17 | ## 0.1.0-alpha-001 - 2021-03-17
18 |
19 | * Initial release
20 |
--------------------------------------------------------------------------------
/glues/Express/tests/Tests.Express.Port.App.fs:
--------------------------------------------------------------------------------
1 | module Tests.Express.Port.Express.App.App
2 |
3 | open Mocha
4 | open Glutinum.Express
5 |
6 | describe "app" (fun _ ->
7 |
8 | itAsync "should inherit from event emitter" (fun d ->
9 | let app = express.express ()
10 |
11 | app.on("foo", fun _ -> d()) |> ignore
12 | app.emit("foo") |> ignore
13 | )
14 |
15 | itAsync "should be callable" (fun d ->
16 | request(express.express())
17 | .get("/")
18 | .expect(404, d)
19 | |> ignore
20 | )
21 |
22 | )
23 |
--------------------------------------------------------------------------------
/glues/Express/README.md:
--------------------------------------------------------------------------------
1 | # Glutinum.Express
2 |
3 | Binding for [Express](https://github.com/expressjs/express)
4 |
5 | # Usage
6 |
7 | ```fs
8 | open Fable.Core
9 | open Glutinum.Express
10 | open Glutinum.ExpressServeStaticCore
11 |
12 | let port = 2700
13 | let app = express.express ()
14 |
15 | app.get (
16 | "/",
17 | fun (req: Request) (res: Response) ->
18 | JS.console.log ($"New request, %s{req.hostname}")
19 | res.send ("Hello world")
20 | )
21 |
22 | app.listen (
23 | port,
24 | fun () -> JS.console.log $"Started listening on http://127.0.0.1:%i{port}"
25 | )
26 | |> ignore
27 | ```
28 |
--------------------------------------------------------------------------------
/glues/ExpressServeStaticCore/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 | All notable changes to this project will be documented in this file.
3 |
4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6 |
7 | ## Unreleased
8 |
9 | ## 0.2.0 - 2024-08-14
10 |
11 | * add deprecation message to Request#param (by @joprice)
12 | * fix return type of 'is' function (by @joprice)
13 |
14 | ## 0.1.0-alpha-002 - 2021-03-17
15 |
16 | * Fix package description
17 |
18 | ## 0.1.0-alpha-001 - 2021-03-17
19 |
20 | * Initial release
21 |
--------------------------------------------------------------------------------
/scripts/await-spawn.js:
--------------------------------------------------------------------------------
1 | import { spawn } from 'child_process'
2 |
3 | export default (...args) => {
4 | const child = spawn(...args)
5 |
6 | const promise = new Promise((resolve, reject) => {
7 | child.on('error', reject)
8 |
9 | child.on('exit', code => {
10 | if (code === 0) {
11 | resolve()
12 | } else {
13 | const err = new Error(`child exited with code ${code}`)
14 | err.code = code
15 | reject(err)
16 | }
17 | })
18 | })
19 |
20 | promise.child = child
21 |
22 | return promise
23 | }
24 |
--------------------------------------------------------------------------------
/glues/BodyParser/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 | All notable changes to this project will be documented in this file.
3 |
4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6 |
7 | ## Unreleased
8 |
9 | ## 1.0.0 - 2021-07-26
10 |
11 | ### Added
12 |
13 | * Release 1.0
14 |
15 | ### Fixed
16 |
17 | * Fix #8: body-parser import doesn't work when targeting es module in node
18 |
19 | ## 0.1.0-alpha-002 - 2021-03-17
20 |
21 | * Fix package description
22 |
23 | ## 0.1.0-alpha-001 - 2021-03-17
24 |
25 | * Initial release
26 |
27 | ## 1.0.0 - 2021-07-26
28 |
--------------------------------------------------------------------------------
/glues/Express/tests/Tests.Express.Port.App.Listen.fs:
--------------------------------------------------------------------------------
1 | module Tests.Express.Port.App.Listen
2 |
3 | open Node
4 | open Mocha
5 | open Glutinum.ExpressServeStaticCore
6 | open Glutinum.Express
7 |
8 | #nowarn "40"
9 |
10 | describe "app.listen()" (fun _ ->
11 |
12 | itAsync "should wrap with an HTTP server" (fun d ->
13 | let app = express.express ()
14 |
15 | app.del("/tobi", fun req (res : Response<_,_>) next ->
16 | res.``end``("deleted tobi")
17 | )
18 |
19 | let rec server : Http.Server =
20 | app.listen(9999, fun _ ->
21 | server.close() |> ignore
22 | d()
23 | )
24 |
25 | ()
26 | )
27 |
28 | )
29 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | patreon: MangelMaxime
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
13 |
--------------------------------------------------------------------------------
/glues/RangeParser/src/Glutinum.RangeParser.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1.0.1
5 | netstandard2.0
6 | true
7 | Maxime Mangel
8 |
9 | Fable bindings for npm https://github.com/jshttp/range-parser package
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/glues/RangeParser/README.md:
--------------------------------------------------------------------------------
1 | # RangeParser
2 |
3 | Binding for [range-parser](https://www.npmjs.com/package/range-parser)
4 |
5 | # Usage
6 |
7 | ```fs
8 | open RangeParser
9 |
10 | let range = rangeParser.rangeParser(1000, "bytes=0-499")
11 |
12 | // The binding include a custom active pattern making it easier/safer to work with the library
13 |
14 | match range with
15 | | ParseRangeResult.UnkownError _
16 | | ParseRangeResult.ResultInvalid
17 | | ParseRangeResult.ResultUnsatisfiable ->
18 | // Handle error case
19 |
20 | | ParseRangeResult.Range range ->
21 | // Here you can access your successful result
22 | range.``type`` // Returns: "bytes"
23 | range.Count // Returns: 1
24 | range.[0].start // Returns: 0
25 | range.[0].``end`` // Returns: 499
26 | ```
27 |
--------------------------------------------------------------------------------
/glues/RangeParser/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 | All notable changes to this project will be documented in this file.
3 |
4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6 |
7 | ## Unreleased
8 |
9 | ## 1.0.1 - 2021-07-26
10 |
11 | ### Fixed
12 |
13 | * Do not publish the code under `.fable` directory, this package is now no code.
14 |
15 | ## 1.0.0 - 2021-07-26
16 |
17 | ### Changed
18 |
19 | * Make this package no-code, the `Ext` module has been extracted to from `Glutinum.RangeParser.Extensions`
20 |
21 | ## 0.1.0-alpha-002 - 2021-03-17
22 |
23 | ### Changed
24 |
25 | * Fix package description
26 |
27 | ## 0.1.0-alpha-001 - 2021-03-17
28 |
29 | ### Added
30 |
31 | * Initial release
32 |
--------------------------------------------------------------------------------
/glues/Qs/tests/Tests.Qs.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 | true
6 | ../../../tests-shared
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/glues/Mime/tests/Tests.Mime.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 | true
6 | ../../../tests-shared
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/glues/RangeParser.Extensions/src/Glutinum.RangeParser.Extensions.fs:
--------------------------------------------------------------------------------
1 | namespace Glutinum.RangeParser
2 |
3 | open Fable.Core
4 |
5 | module internal Interop =
6 |
7 | []
8 | let internal jsTypeOf _ : string = jsNative
9 |
10 | []
11 | module ParseRangeResult =
12 |
13 | let (|UnkownError|ResultInvalid|ResultUnsatisfiable|Range|) (result : RangeParser.ParseRangeResult) : Choice =
14 | if Interop.jsTypeOf result = "number" then
15 | let error = unbox result
16 |
17 | if error = -1 then
18 | ResultUnsatisfiable
19 | else if error = -2 then
20 | ResultInvalid
21 | else
22 | UnkownError (unbox result)
23 | else
24 | Range (unbox result)
25 |
--------------------------------------------------------------------------------
/glues/Connect/tests/Tests.Connect.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 | true
6 | ../../../tests-shared
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/glues/Chalk/tests/Tests.Chalk.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 | true
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/glues/ServeStatic/tests/Tests.ServeStatic.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 | true
6 | ../../../tests-shared
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/glues/RangeParser.Extensions/tests/Tests.RangeParser.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 | true
6 | ../../../tests-shared
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/tests-shared/Globals.fs:
--------------------------------------------------------------------------------
1 | []
2 | module Globals
3 |
4 | open Fable.Core
5 | open Fable.Core.JS
6 | open Glutinum.SuperTest
7 |
8 | []
9 | let Assert: Node.Assert.IExports = jsNative
10 |
11 | let request (app : #obj) = supertest.supertest app
12 |
13 | type Node.Http.ServerResponse with
14 | []
15 | member __.setHeader(name: string, value: string) : unit = jsNative
16 | []
17 | member __.setHeader(name: string, value: ResizeArray) : unit = jsNative
18 |
19 | []
20 | let inline jsUndefined<'T> : 'T = jsNative
21 |
22 | []
23 | let inline returnNothingHack<'T> : 'T = jsNative
24 |
25 | type NumberConstructor with
26 | []
27 | member __.Create(v : obj) = jsNative
28 |
29 | []
30 | let internal jsTypeOf _ : string = jsNative
31 |
--------------------------------------------------------------------------------
/glues/Qs/src/Glutinum.Qs.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1.0.0
5 | netstandard2.0
6 | true
7 | Maxime Mangel
8 |
9 | Fable bindings for npm https://github.com/ljharb/qs package
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
20 |
23 |
24 |
--------------------------------------------------------------------------------
/glues/Chalk/src/Glutinum.Chalk.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1.0.0
5 | netstandard2.0
6 | true
7 | Maxime Mangel
8 |
9 | Fable bindings for npm https://github.com/chalk/chalk package
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
20 |
23 |
24 |
--------------------------------------------------------------------------------
/glues/Mime/src/Glutinum.Mime.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 2.0.0
5 | netstandard2.0
6 | true
7 | Maxime Mangel
8 |
9 | Fable bindings for npm https://github.com/broofa/mime package
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
20 |
23 |
24 |
--------------------------------------------------------------------------------
/glues/After/src/Glutinum.After.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1.0.0
5 | netstandard2.0
6 | true
7 | Maxime Mangel
8 |
9 | Fable bindings for https://www.npmjs.com/package/after package
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
20 |
23 |
24 |
--------------------------------------------------------------------------------
/glues/BodyParser/tests/Tests.BodyParser.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 | true
6 | ../../../tests-shared
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/glues/Methods/src/Glutinum.Methods.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 0.1.0-alpha-002
5 | netstandard2.0
6 | true
7 | Maxime Mangel
8 |
9 | Fable bindings for npm https://github.com/jshttp/methods package
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
20 |
23 |
24 |
--------------------------------------------------------------------------------
/glues/Express/tests/Tests.Express.Port.App.Del.fs:
--------------------------------------------------------------------------------
1 | module Tests.Express.Port.App.Del
2 |
3 | open Mocha
4 | open Glutinum.ExpressServeStaticCore
5 | open Glutinum.Express
6 |
7 |
8 | describe "app.delete()" (fun _ ->
9 |
10 | itAsync "app.delete() works" (fun d ->
11 | let app = express.express ()
12 |
13 | app.delete("/tobi", fun req (res : Response<_,_>) next ->
14 | res.``end``("deleted tobi")
15 | )
16 |
17 | request(app)
18 | .del("/tobi")
19 | .expect("deleted tobi", d)
20 | |> ignore
21 | )
22 |
23 | itAsync "app.del() should alias app.delete()" (fun d ->
24 | let app = express.express ()
25 |
26 | app.del("/tobi", fun req (res : Response<_,_>) next ->
27 | res.``end``("deleted tobi")
28 | )
29 |
30 | request(app)
31 | .del("/tobi")
32 | .expect("deleted tobi", d)
33 | |> ignore
34 | )
35 |
36 | )
37 |
--------------------------------------------------------------------------------
/glues/Express/tests/support/tmpl.js:
--------------------------------------------------------------------------------
1 | import { readFile } from 'fs';
2 |
3 | var variableRegExp = /\$([0-9a-zA-Z\.]+)/g;
4 |
5 | export default function renderFile(fileName, options, callback) {
6 | function onReadFile(err, str) {
7 | if (err) {
8 | callback(err);
9 | return;
10 | }
11 |
12 | try {
13 | str = str.replace(variableRegExp, generateVariableLookup(options));
14 | } catch (e) {
15 | err = e;
16 | err.name = 'RenderError'
17 | }
18 |
19 | callback(err, str);
20 | }
21 |
22 | readFile(fileName, 'utf8', onReadFile);
23 | };
24 |
25 | function generateVariableLookup(data) {
26 | return function variableLookup(str, path) {
27 | var parts = path.split('.');
28 | var value = data;
29 |
30 | for (var i = 0; i < parts.length; i++) {
31 | value = value[parts[i]];
32 | }
33 |
34 | return value;
35 | };
36 | }
37 |
--------------------------------------------------------------------------------
/glues/RangeParser.Extensions/src/Glutinum.RangeParser.Extensions.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1.0.0
5 | netstandard2.0
6 | true
7 | Maxime Mangel
8 |
9 | Extensions for Glutinum.RangeParser glues
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/glues/BodyParser/src/Glutinum.BodyParser.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1.0.0
5 | netstandard2.0
6 | true
7 | Maxime Mangel
8 |
9 | Fable bindings for npm https://github.com/expressjs/body-parser package
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
21 |
24 |
25 |
--------------------------------------------------------------------------------
/glues/Connect/src/Glutinum.Connect.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 0.1.0-alpha-002
5 | netstandard2.0
6 | true
7 | Maxime Mangel
8 |
9 | Fable bindings for npm https://github.com/senchalabs/connect package
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
22 |
25 |
26 |
--------------------------------------------------------------------------------
/glues/SuperAgent/src/Glutinum.SuperAgent.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 0.1.0-alpha-002
5 | netstandard2.0
6 | true
7 | Maxime Mangel
8 |
9 | Fable bindings for npm https://github.com/visionmedia/superagent package
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
22 |
25 |
26 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Mangel Maxime
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/glues/Mime/src/Glutinum.Mime.fs:
--------------------------------------------------------------------------------
1 | module rec Glutinum.Mime
2 |
3 | // Exported from: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/0893371fea43bfdf1777b6d835424961ba0d1dbb/types/mime/index.d.ts
4 |
5 | open Fable.Core
6 |
7 | []
8 | let mime : Mime.IExports = jsNative
9 |
10 | module Mime =
11 |
12 | type [] IExports =
13 | []
14 | abstract Mime: mimes: TypeMap -> Mime
15 | abstract getType: path: string -> string option
16 | abstract getExtension: mime: string -> string option
17 | abstract define: mimes: TypeMap * ?force: bool -> unit
18 |
19 | type [] Mime =
20 | abstract getType: path: string -> string option
21 | abstract getExtension: mime: string -> string option
22 | abstract define: mimes: TypeMap * ?force: bool -> unit
23 |
24 | type [] MimeStatic =
25 | [] abstract Create: mimes: TypeMap -> Mime
26 |
27 | type [] TypeMap =
28 | [] abstract Item: key: string -> ResizeArray with get, set
29 |
--------------------------------------------------------------------------------
/glues/ServeStatic/src/Glutinum.ServeStatic.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1.0.0
5 | netstandard2.0
6 | true
7 | Maxime Mangel
8 |
9 | Fable bindings for npm https://github.com/expressjs/serve-static package
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
24 |
27 |
28 |
--------------------------------------------------------------------------------
/glues/ExpressServeStaticCore/src/Glutinum.ExpressServeStaticCore.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 0.2.0
5 | netstandard2.0
6 | true
7 | Maxime Mangel
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
23 |
26 |
27 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "fable-express",
3 | "version": "0.0.0",
4 | "private": true,
5 | "description": "This repo is a meta repo for working on the binding for Express ecosystem",
6 | "author": "Mangel Maxime ",
7 | "license": "MIT",
8 | "type": "module",
9 | "scripts": {
10 | "postinstall": "dotnet tool restore"
11 | },
12 | "dependencies": {
13 | "after": "^0.8.2",
14 | "body-parser": "^1.19.0",
15 | "connect": "^3.7.0",
16 | "cookie-parser": "^1.4.5",
17 | "dirname-filename-esm": "^1.1.2",
18 | "express": "^4.17.1",
19 | "mime": "^4.0.4",
20 | "qs": "^6.9.6",
21 | "range-parser": "^1.2.1",
22 | "serve-static": "^1.14.1",
23 | "supertest": "6.0.1"
24 | },
25 | "devDependencies": {
26 | "chalk": "^4.1.0",
27 | "changelog-parser": "^2.8.0",
28 | "concurrently": "^6.0.0",
29 | "del-cli": "^3.0.1",
30 | "esm": "^3.2.25",
31 | "fast-glob": "^3.3.2",
32 | "mocha": "^10.7.3",
33 | "nodemon": "^2.0.7",
34 | "prompts": "^2.4.0",
35 | "yargs": "^16.2.0"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/glues/Chalk/tests/Tests.Chalk.Instance.fs:
--------------------------------------------------------------------------------
1 | module Tests.Chalk.Instance
2 |
3 | open Mocha
4 | open Fable.Core
5 | open Fable.Core.JsInterop
6 | open Glutinum.Chalk
7 |
8 | // Test ported from https://github.com/chalk/chalk/blob/4dab5e1fb6f42c6c9fdacbe34b9dafd24359208e/test/instance.js
9 |
10 | []
11 | let Assert: Node.Assert.IExports = jsNative
12 |
13 | describe "Chalk" (fun _ ->
14 |
15 | describe "Instance" (fun _ ->
16 |
17 | it "create an isolated context where colors can be disabled (by level)" (fun _ ->
18 | let instance = chalk.Instance(jsOptions(fun o ->
19 | o.level <- Some Chalk.Level.N0
20 | ))
21 |
22 | Assert.strictEqual(
23 | instance.red.Invoke("foo"),
24 | "foo"
25 | )
26 |
27 | Assert.strictEqual(
28 | chalk.red.Invoke("foo"),
29 | "\u001B[31mfoo\u001B[39m"
30 | )
31 |
32 | instance.level <- Chalk.Level.N2
33 |
34 |
35 | Assert.strictEqual(
36 | instance.red.Invoke("foo"),
37 | "\u001B[31mfoo\u001B[39m"
38 | )
39 |
40 | )
41 |
42 | )
43 |
44 | )
45 |
--------------------------------------------------------------------------------
/glues/SuperTest/src/Glutinum.SuperTest.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 0.1.0-alpha-002
5 | netstandard2.0
6 | true
7 | Maxime Mangel
8 |
9 | Fable bindings for npm https://github.com/visionmedia/supertest package
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
25 |
28 |
29 |
--------------------------------------------------------------------------------
/glues/Express/tests/Tests.Express.Port.App.Locals.fs:
--------------------------------------------------------------------------------
1 | module Tests.Express.Port.App.Locals
2 |
3 | open Fable.Core.JS
4 | open Fable.Core.JsInterop
5 | open Mocha
6 | open Glutinum.ExpressServeStaticCore
7 | open Glutinum.Express
8 |
9 | describe "app" (fun _ ->
10 |
11 | describe ".locals(obj" (fun _ ->
12 |
13 | it "should merge locals" (fun _ ->
14 | let app = express.express ()
15 |
16 | Assert.deepStrictEqual(Constructors.Object.keys(app.locals), ResizeArray ["settings"])
17 | app.locals.["user"] <- "tobi"
18 | app.locals.["age"] <- 2
19 |
20 | Assert.deepStrictEqual(Constructors.Object.keys(app.locals), ResizeArray ["settings"; "user"; "age"])
21 | Assert.strictEqual(app.locals.["user"], box "tobi")
22 | Assert.strictEqual(app.locals.["age"], box 2)
23 | )
24 |
25 | )
26 |
27 | describe ".locals.settings" (fun _ ->
28 | it "should expose app settings" (fun _ ->
29 | let app = express.express ()
30 | app.set("title", "House of Manny") |> ignore
31 |
32 | let o = app.locals.["settings"]
33 | Assert.strictEqual(o?env, "test")
34 | Assert.strictEqual(o?title, "House of Manny")
35 | )
36 | )
37 |
38 | )
39 |
--------------------------------------------------------------------------------
/glues/Express/tests/Tests.Express.Port.App.All.fs:
--------------------------------------------------------------------------------
1 | module Tests.Express.Port.App.All
2 |
3 | open Mocha
4 | open Glutinum.ExpressServeStaticCore
5 | open Glutinum.Express
6 |
7 | describe "app.all()" (fun _ ->
8 |
9 | itAsync "should add a router per method" (fun d ->
10 | let app = express.express ()
11 |
12 | app.all("/tobi", fun (req : Request) (res : Response) next ->
13 | res.``end``(req.``method``)
14 | )
15 |
16 | request(app)
17 | .put("/tobi")
18 | .expect(
19 | "PUT",
20 | fun _ ->
21 | request(app)
22 | .get("/tobi")
23 | .expect("GET", d)
24 | |> ignore
25 | )
26 | |> ignore
27 | )
28 |
29 | itAsync "should run the callback for a method just once" (fun d ->
30 | let app = express.express ()
31 | let mutable n = 0
32 |
33 | app.all("/*", fun (req : Request) (res : Response) (next : NextFunction) ->
34 | if n > 0 then
35 | d(System.Exception("DELETE called several times"))
36 | n <- n + 1
37 | next.Invoke()
38 | )
39 |
40 | request(app)
41 | .del("/tobi")
42 | .expect(404, d)
43 | |> ignore
44 | )
45 |
46 | )
47 |
--------------------------------------------------------------------------------
/glues/Chalk/tests/Tests.Chalk.Level.fs:
--------------------------------------------------------------------------------
1 | module Tests.Chalk.Level
2 |
3 | open Mocha
4 | open Fable.Core
5 | open Glutinum.Chalk
6 |
7 | // Test ported from https://github.com/chalk/chalk/blob/4dab5e1fb6f42c6c9fdacbe34b9dafd24359208e/test/instance.js
8 |
9 | []
10 | let Assert: Node.Assert.IExports = jsNative
11 |
12 | describe "Chalk" (fun _ ->
13 |
14 | describe "Level" (fun _ ->
15 |
16 | it "don't output colors when manually disabled" (fun _ ->
17 | let oldLevel = chalk.level
18 | chalk.level <- Chalk.Level.N0
19 |
20 | Assert.strictEqual(
21 | chalk.red.Invoke("foo"),
22 | "foo"
23 | )
24 |
25 | chalk.level <- oldLevel
26 | )
27 |
28 | it "enable/disable colors based on overall chalk .level property, not individual instances" (fun _ ->
29 | let oldLevel = chalk.level
30 |
31 | chalk.level <- Chalk.Level.N1
32 | let red = chalk.red
33 |
34 | Assert.strictEqual(
35 | red.level,
36 | Chalk.Level.N1
37 | )
38 |
39 | chalk.level <- Chalk.Level.N0
40 |
41 | Assert.strictEqual(
42 | red.level,
43 | chalk.level
44 | )
45 |
46 | chalk.level <- oldLevel
47 | )
48 | )
49 | )
50 |
--------------------------------------------------------------------------------
/glues/Express/src/Glutinum.Express.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 0.2.0
5 | netstandard2.0
6 | true
7 | Maxime Mangel
8 |
9 | Fable bindings for npm https://github.com/expressjs/express package
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
27 |
30 |
31 |
--------------------------------------------------------------------------------
/glues/Mime/tests/Tests.Mime.fs:
--------------------------------------------------------------------------------
1 | module Tests.Mime
2 |
3 | open Mocha
4 | open Fable.Core
5 | open Fable.Core.Testing
6 | open Fable.Core.JsInterop
7 | open Glutinum.Mime
8 |
9 | // Code adapted from: https://github.com/broofa/mime/blob/9847c9f9ee077a8d6e17d0b738b1b28c030a9a89/src/test.js
10 |
11 |
12 | describe "Mime" (fun _ ->
13 |
14 | it "Mime new constructor works" (fun _ ->
15 | let typeMap = createEmpty
16 | typeMap.["text/a"] <- ResizeArray(["a"; "a1"])
17 | typeMap.["text/b"] <- ResizeArray(["b"; "b1"])
18 |
19 | let mime = mime.Mime(typeMap)
20 |
21 | Assert.AreEqual(mime.getType("a"), Some "text/a")
22 | )
23 |
24 | it "define works" (fun _ ->
25 | let typeMap = createEmpty
26 | typeMap.["text/a"] <- ResizeArray(["a"; "a1"])
27 | typeMap.["text/b"] <- ResizeArray(["b"; "b1"])
28 |
29 | let mime = mime.Mime(typeMap)
30 |
31 | let subTypeMap = createEmpty
32 | subTypeMap.["text/c"] <- ResizeArray(["c"])
33 |
34 | mime.define(subTypeMap)
35 |
36 | Assert.AreEqual(mime.getType("c"), Some "text/c")
37 | )
38 |
39 | it "getType() works" (fun _ ->
40 | Assert.AreEqual(mime.getType("txt"), Some "text/plain")
41 | )
42 |
43 | it "getExtension() works" (fun _ ->
44 | Assert.AreEqual(mime.getExtension("text/html"), Some "html")
45 | )
46 |
47 | )
48 |
--------------------------------------------------------------------------------
/glues/Express/tests/Tests.Express.Port.App.Response.fs:
--------------------------------------------------------------------------------
1 | module Tests.Express.Port.App.Response
2 |
3 | open Glutinum.ExpressServeStaticCore
4 | open Glutinum.Express
5 | open Fable.Core.JsInterop
6 | open Mocha
7 |
8 | describe "app" (fun _ ->
9 | describe ".response" (fun _ ->
10 | itAsync "should extend the response prototype" (fun d ->
11 | let app = express.express ()
12 |
13 | emitJsStatement app """
14 | $0.response.shout = function(str){
15 | this.send(str.toUpperCase());
16 | };
17 | """
18 |
19 | app.``use``(fun req res ->
20 | res?shout("hey")
21 | )
22 |
23 | request(app)
24 | .get("/")
25 | .expect("HEY", d)
26 | |> ignore
27 | )
28 |
29 | itAsync "should not be influenced by other app protos" (fun d ->
30 | let app = express.express ()
31 | let app2 = express.express()
32 |
33 | emitJsStatement app """
34 | $0.response.shout = function(str){
35 | this.send(str.toUpperCase());
36 | };
37 | """
38 |
39 | emitJsStatement app2 """
40 | $0.response.shout = function(str){
41 | this.send(str);
42 | };
43 | """
44 |
45 | app.``use``(fun req res ->
46 | res?shout("hey")
47 | )
48 |
49 | request(app)
50 | .get("/")
51 | .expect("HEY", d)
52 | |> ignore
53 | )
54 | )
55 | )
56 |
--------------------------------------------------------------------------------
/glues/Chalk/README.md:
--------------------------------------------------------------------------------
1 | # Glutinum.Chalk
2 |
3 | Binding for [https://github.com/chalk/chalk](chalk)
4 |
5 | ## Limitations
6 |
7 | This package doesn't support template literal style notation from chalk.
8 |
9 | ## Usage
10 |
11 | *If you want to compare the F# code with JavaScript the section below is a port of [Chalk - Usage](https://github.com/chalk/chalk/tree/4dab5e1fb6f42c6c9fdacbe34b9dafd24359208e#usage)*
12 |
13 | ```fs
14 | open Glutinum.Chalk
15 | open Fable.Core
16 |
17 | let log x = JS.console.log(x)
18 |
19 | // Combine styled and normal strings
20 | log(chalk.blue.Invoke("Hello") + " World" + chalk.red.Invoke("!"))
21 |
22 | // Compose multiples styles using the chainable API
23 | log(chalk.blue.bgRed.bold.Invoke("Hello world!"))
24 |
25 | // Pass in multiple arguments
26 | log(chalk.blue.Invoke("Hello", "World!", "Foo", "bar", "biz", "baz"))
27 |
28 | // Nest styles
29 | log(chalk.red.Invoke("Hello", chalk.underline.bgBlue.Invoke("world") + "!"));
30 |
31 | // Nest styles of the same type even (color, underline, background)
32 | log(
33 | chalk.green.Invoke(
34 | "I am a green line " +
35 | chalk.blue.underline.bold.Invoke("with a blue substring") +
36 | " that becomes green again!"
37 | ));
38 |
39 | // Use RGB colors in terminal emulators that support it.
40 | log(chalk.keyword("orange").Invoke("Yay for orange colored text!"));
41 | log(chalk.rgb(123, 45, 67).underline.Invoke("Underlined reddish color"));
42 | log(chalk.hex("#DEADED").bold.Invoke("Bold gray!"));
43 | ```
44 |
--------------------------------------------------------------------------------
/glues/Connect/tests/Tests.Connect.fs:
--------------------------------------------------------------------------------
1 | module Tests.Connect
2 |
3 | open Mocha
4 | open Fable.Core.JsInterop
5 | open Node
6 | open Glutinum.Connect
7 |
8 | // Code adapted from: https://github.com/senchalabs/connect/blob/52cf21b211272519caeef3bb5064c3430f4feb43/test/server.js
9 |
10 | describe "Connect" (fun _ ->
11 |
12 | let mutable app : Connect.CreateServer.Server = Unchecked.defaultof<_>
13 |
14 | beforeEach (fun _ ->
15 | app <- connect()
16 | )
17 |
18 | itAsync "should inherit from event emitter" (fun ok ->
19 | app.on("foo", ok) |> ignore
20 | app.emit("foo") |> ignore
21 | )
22 |
23 | itAsync "should work in http.createServer" (fun ok ->
24 | let app = connect()
25 |
26 | // We need to help the compiler with a type hint
27 | app.``use``(fun req res ->
28 | res.``end``("Hello, world!")
29 | ) |> ignore
30 |
31 | let server = http.createServer(app)
32 |
33 | request(box server)
34 | .get("/")
35 | .expect(200, "Hello, world!", fun err _ -> ok err)
36 | |> ignore
37 | )
38 |
39 | describe "error handler" (fun _ ->
40 |
41 | itAsync "should use custom error code" (fun ok ->
42 | let app = connect()
43 |
44 | app.``use``(fun req res next ->
45 | let err = new System.Exception("boom!")
46 | err?status <- 503
47 | raise err |> ignore
48 | ) |> ignore
49 |
50 | request(box app)
51 | .get("/")
52 | .expect(503, fun err _ -> ok err)
53 | |> ignore
54 | )
55 |
56 | )
57 |
58 | )
59 |
--------------------------------------------------------------------------------
/glues/ServeStatic/tests/Tests.ServeStatic.fs:
--------------------------------------------------------------------------------
1 | module Tests.ServeStatic
2 |
3 | open Mocha
4 | open Fable.Core
5 | open Fable.Core.Testing
6 | open Fable.Core.JsInterop
7 | open Glutinum.ServeStatic
8 | open Node
9 | open Glutinum.Connect
10 |
11 | // Code adapted from: https://github.com/expressjs/serve-static/blob/94feedb81682f4503ed9f8dc6d51a5c1b9bfa091/test/test.js
12 |
13 | let __dirname =
14 | emitJsExpr () """
15 | import { dirname } from 'dirname-filename-esm';
16 | dirname(import.meta);
17 | """
18 |
19 | let private createServer opts =
20 | let dir = path.join(__dirname, "files")
21 |
22 | let serve = serveStatic.serveStatic(dir, opts)
23 |
24 | http.createServer(fun req res ->
25 | // let req = req :?> Types.Connect.CreateServer.IncomingMessage
26 |
27 | let next =
28 | Connect.CreateServer.NextFunction(fun error ->
29 | res.statusCode <-
30 | if isNull error then
31 | 404
32 | else
33 | if isNull error?status then
34 | 500
35 | else
36 | error?status |> unbox
37 | if isNull error then
38 | res.``end``("sorry!")
39 | else
40 | res.``end``(error?``stack``)
41 | )
42 |
43 | serve.Invoke(req, res, next)
44 | |> ignore
45 |
46 | )
47 |
48 |
49 | describe "ServeStatic" (fun _ ->
50 |
51 | itAsync "should support nesting" (fun d ->
52 | request(box (createServer null))
53 | .get("/users/tobi.txt")
54 | .expect(200, "ferret", d)
55 | |> ignore
56 | )
57 |
58 | itAsync "should support index.html" (fun d ->
59 | request(box (createServer null))
60 | .get("/users/")
61 | .expect(200, "tobi, loki, jane
", d)
62 | |> ignore
63 | )
64 |
65 | )
66 |
--------------------------------------------------------------------------------
/glues/Express/tests/Tests.Express.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 | true
6 | ../../../tests-shared
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/glues/Express/tests/Tests.Express.Port.App.Route.fs:
--------------------------------------------------------------------------------
1 | module Tests.Express.Port.App.Route
2 |
3 | open Glutinum.ExpressServeStaticCore
4 | open Glutinum.Express
5 | open Mocha
6 |
7 | describe "app.route" (fun _ ->
8 | itAsync "should return a new route" (fun d ->
9 | let app = express.express ()
10 |
11 | app.route("/foo")
12 | .get(fun (req : Request) (res : Response) ->
13 | res.send("get")
14 | )
15 | .post(fun (req : Request) (res : Response) ->
16 | res.send("post")
17 | )
18 | |> ignore
19 |
20 | request(app)
21 | .post("/foo")
22 | .expect("post", d)
23 | |> ignore
24 | )
25 |
26 | itAsync "should all .VERB after .all" (fun d ->
27 | let app = express.express ()
28 |
29 | app.route("/foo")
30 | .all(fun req res (next : NextFunction) ->
31 | next.Invoke()
32 | )
33 | .get(fun req (res : Response) ->
34 | res.send("get")
35 | )
36 | .post(fun (req : Request) (res : Response) ->
37 | res.send("post")
38 | )
39 | |> ignore
40 |
41 | request(app)
42 | .post("/foo")
43 | .expect("post", d)
44 | |> ignore
45 | )
46 |
47 | itAsync "should support dynamic routes" (fun d ->
48 | let app = express.express ()
49 |
50 | app.route("/:foo")
51 | .get(fun (req : Request) (res : Response) ->
52 | res.send(req.``params``.["foo"])
53 | )
54 | |> ignore
55 |
56 | request(app)
57 | .get("/test")
58 | .expect("test", d)
59 | |> ignore
60 | )
61 |
62 | itAsync "should not error on empty routes" (fun d ->
63 | let app = express.express ()
64 |
65 | app.route("/:foo") |> ignore
66 |
67 | request(app)
68 | .get("/test")
69 | .expect(404, d)
70 | |> ignore
71 | )
72 |
73 | )
74 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Glutinum
2 |
3 | Glutinum is a tentative to bring an equivalent to [DefinitelyTyped](https://github.com/DefinitelyTyped/DefinitelyTyped) for Fable ecosystem.
4 |
5 | Right now Glutinum contains bindings generated using [ts2fable](https://github.com/fable-compiler/ts2fable/) and improved manually.
6 |
7 | In the future, Glutinum will try to use it's own converter to experiment with a new way to write bindings.
8 |
9 | ## Can I use the packages from this repository?
10 |
11 | Yes, you can.
12 |
13 | Most of the package already have a battery of tests to check regression and if they works.
14 |
15 | ## Structure of a glue
16 |
17 | A glue, is always no-code bindings between F# and JavaScript code.
18 |
19 | If there are code addition made to improve the user experience when working with the package then it goes into another package `Glutinum.XXX.Extensions`.
20 |
21 | Example: `Glutinum.RangeParser.Extensions`
22 |
23 | This is so we don't have to make all the packages includes their source code under `.fable` directory and should improve Fable memory consumption and performance.
24 |
25 | ## Tests status
26 |
27 |
28 |
29 | | Binding | Number of tests |
30 | |---------|-----------------|
31 | | BodyParser | 5 |
32 | | Chalk | 30 |
33 | | Connect | 3 |
34 | | Express | 424 |
35 | | Mime | 4 |
36 | | Qs | 5 |
37 | | RangeParser | 5 |
38 | | ServeStatic | 2 |
39 |
40 |
41 |
42 | ## How to use this repository?
43 |
44 | If you are on Windows using the standard terminal you need to run `node build.js`.
45 |
46 | If you are on Linux, OSX or Windows using a bash-like terminal you can run `build.js` directly.
47 |
48 | From now, I will always use `build.js` in the command as it is shorter.
49 |
50 | Run `build.js --help` for more information about which command are supported.
51 |
52 | ### Getting completion from Linux, OSX, Windows bash-like
53 |
54 | It is possible to have `TAB` completion support from your terminal.
55 |
56 | Run `./build.js completion` and follow the instruction.
57 |
58 | ### Why this name?
59 |
60 | It comes from the ideas that this project is trying to **glue** together F# and TypeScript.
61 |
62 | Glutinum is from the Latin word gluten ("glue") with the suffix -um.
63 |
--------------------------------------------------------------------------------
/glues/Methods/src/Glutinum.Methods.fs:
--------------------------------------------------------------------------------
1 | // ts2fable 0.8.0
2 | module rec Glutinum.Methods
3 |
4 | open System
5 | open Fable.Core
6 | open Fable.Core.JS
7 |
8 | let [] methods: ResizeArray = jsNative
9 |
10 | type [] [] Method =
11 | | [] ACL
12 | | [] BIND
13 | | [] CHECKOUT
14 | | [] CONNECT
15 | | [] COPY
16 | | [] DELETE
17 | | [] GET
18 | | [] HEAD
19 | | [] LINK
20 | | [] LOCK
21 | | [] MSEARCH
22 | | [] MERGE
23 | | [] MKACTIVITY
24 | | [] MKCALENDAR
25 | | [] MKCOL
26 | | [] MOVE
27 | | [] NOTIFY
28 | | [] OPTIONS
29 | | [] PATCH
30 | | [] POST
31 | | [] PROPFIND
32 | | [] PROPPATCH
33 | | [] PURGE
34 | | [] PUT
35 | | [] REBIND
36 | | [] REPORT
37 | | [] SEARCH
38 | | [] SOURCE
39 | | [] SUBSCRIBE
40 | | [] TRACE
41 | | [] UNBIND
42 | | [] UNLINK
43 | | [] UNLOCK
44 | | [] UNSUBSCRIBE
45 | | Acl
46 | | Bind
47 | | Checkout
48 | | Connect
49 | | Copy
50 | | Delete
51 | | Get
52 | | Head
53 | | Link
54 | | Lock
55 | | [] MSearch
56 | | Merge
57 | | Mkactivity
58 | | Mkcalendar
59 | | Mkcol
60 | | Move
61 | | Notify
62 | | Options
63 | | Patch
64 | | Post
65 | | Propfind
66 | | Proppatch
67 | | Purge
68 | | Put
69 | | Rebind
70 | | Report
71 | | Search
72 | | Source
73 | | Subscribe
74 | | Trace
75 | | Unbind
76 | | Unlink
77 | | Unlock
78 | | Unsubscribe
79 |
--------------------------------------------------------------------------------
/glues/BodyParser/tests/Tests.BodyParser.Text.fs:
--------------------------------------------------------------------------------
1 | module Tests.BodyParser.Text
2 |
3 | open Mocha
4 | open Node
5 | open Fable.Core
6 | open Fable.Core.JsInterop
7 | open Glutinum.BodyParser
8 | open Glutinum.Connect
9 | open Glutinum.SuperTest
10 |
11 | // Code adapted from: https://github.com/expressjs/body-parser/blob/480b1cfe29af19c070f4ae96e0d598c099f42a12/test/text.js
12 |
13 | let private createServer (opts : BodyParser.OptionsText) =
14 | let bodyParser = bodyParser.text(opts)
15 |
16 | http.createServer(fun req res ->
17 | let req = req :?> Connect.CreateServer.IncomingMessage
18 |
19 | let next =
20 | Connect.CreateServer.NextFunction(fun error ->
21 | res.statusCode <-
22 | if isNull error then
23 | 200
24 | else
25 | if isNull error?status then
26 | 500
27 | else
28 | error?status |> unbox
29 | if isNull error then
30 | res.``end``(JS.JSON.stringify(req?body))
31 | else
32 | res.``end``(error?message)
33 | )
34 |
35 | bodyParser.Invoke(req, res, next)
36 | )
37 |
38 |
39 |
40 | describe "bodyParser.text()" (fun _ ->
41 |
42 | itAsync "should parse text/plain" (fun d ->
43 | let test =
44 | request(box (createServer null))
45 | .post("/")
46 | .set("Content-Type", "text/plain")
47 | .send("user is tobi")
48 | :?> SuperTest.Test
49 |
50 | test.expect(200, "\"user is tobi\"", d)
51 | |> ignore
52 | )
53 |
54 | describe "with limit option" (fun _ ->
55 |
56 | itAsync "should 413 when over limit with Content-Length" (fun d ->
57 | let buf = buffer.Buffer.alloc(1028, ".")
58 |
59 | let server =
60 | createServer(jsOptions(fun o ->
61 | o.limit <- !^ "1kb"
62 | ))
63 |
64 | let test =
65 | request(box server)
66 | .post("/")
67 | .set("Content-Type", "text/plain")
68 | .set("Content-Length", "1028")
69 | .send(buf.toString())
70 | :?> SuperTest.Test
71 |
72 | test.expect(413, d)
73 | |> ignore
74 | )
75 | )
76 | )
77 |
78 |
--------------------------------------------------------------------------------
/glues/Express/tests/Tests.Express.Port.App.Head.fs:
--------------------------------------------------------------------------------
1 | module Tests.Express.Port.App.Head
2 |
3 | open Mocha
4 | open Glutinum.ExpressServeStaticCore
5 | open Glutinum.Express
6 | open Fable.Core.JsInterop
7 |
8 | describe "HEAD" (fun _ ->
9 |
10 | itAsync "should default to GET" (fun d ->
11 | let app = express.express ()
12 |
13 | app.get("/tobi", fun (req : Request) (res : Response) (next : NextFunction) ->
14 | res.send("tobi")
15 | )
16 |
17 | request(app)
18 | .head("/tobi")
19 | .expect(200, d)
20 | |> ignore
21 | )
22 |
23 | itAsync "should output the same headers as GET requests" (fun d ->
24 | let app = express.express ()
25 |
26 | app.get("/tobi", fun req (res : Response) next ->
27 | res.send("tobi")
28 | )
29 |
30 | request(app)
31 | .get("/tobi")
32 | .expect(
33 | 200,
34 | (fun err res ->
35 | if err.IsSome then
36 | d err
37 |
38 | let headers = res.headers
39 |
40 | request(app)
41 | .get("/tobi")
42 | .expect(
43 | 200,
44 | (fun err res ->
45 | if err.IsSome then
46 | d err
47 |
48 | jsDelete headers.Value?date
49 | jsDelete res.headers.Value?date
50 |
51 | Assert.deepStrictEqual(res.headers, headers)
52 | d()
53 | )
54 | )
55 | |> ignore
56 | )
57 | )
58 | |> ignore
59 | )
60 |
61 | )
62 |
63 | describe "app.head()" (fun _ ->
64 |
65 | itAsync "should override" (fun d ->
66 |
67 | let app = express.express ()
68 | let mutable called = false
69 |
70 | app.head("/tobi", fun req (res : Response<_,_>) next ->
71 | called <- true
72 | res.``end``()
73 | )
74 |
75 | app.get("/tobi", fun req (res : Response) next ->
76 | Assert.fail("should not call GET")
77 | res.send("tobi")
78 | )
79 |
80 | request(app)
81 | .head("/tobi")
82 | .expect(
83 | 200,
84 | (fun err _ ->
85 | Assert.strictEqual(called, true)
86 | d ()
87 | )
88 | )
89 | |> ignore
90 |
91 | )
92 |
93 | )
94 |
--------------------------------------------------------------------------------
/glues/BodyParser/tests/Tests.BodyParser.Json.fs:
--------------------------------------------------------------------------------
1 | module Tests.BodyParser.Json
2 |
3 | open Mocha
4 | open Node
5 | open Fable.Core
6 | open Fable.Core.JsInterop
7 | open Glutinum.BodyParser
8 | open Glutinum.Connect
9 | open Glutinum.SuperTest
10 |
11 | // Code adapted from: https://github.com/expressjs/body-parser/blob/480b1cfe29af19c070f4ae96e0d598c099f42a12/test/json.js
12 |
13 | let private createServer opts =
14 | let bodyParser = bodyParser.json(opts)
15 |
16 | http.createServer(fun req res ->
17 | let req = req :?> Connect.CreateServer.IncomingMessage
18 |
19 | let next =
20 | Connect.CreateServer.NextFunction(fun error ->
21 | res.statusCode <-
22 | if isNull error then
23 | 200
24 | else
25 | if isNull error?status then
26 | 500
27 | else
28 | error?status |> unbox
29 | if isNull error then
30 | res.``end``(JS.JSON.stringify(req?body))
31 | else
32 | res.``end``(error?message)
33 | )
34 |
35 | bodyParser.Invoke(req, res, next)
36 | )
37 |
38 |
39 |
40 | describe "bodyParser.json()" (fun _ ->
41 |
42 | itAsync "should default to {}" (fun d ->
43 | request(box (createServer null))
44 | .post("/")
45 | .expect(200, "{}", d)
46 | |> ignore
47 | )
48 |
49 | itAsync "should parse JSON" (fun ok ->
50 | let test =
51 | request(box (createServer null))
52 | .post("/")
53 | .set("Content-Type", "application/json")
54 | .send("""{"user":"tobi"}""")
55 | :?> SuperTest.Test
56 |
57 | test.expect(200, """{"user":"tobi"}""", ok)
58 | |> ignore
59 | )
60 |
61 | describe "with limit option" (fun _ ->
62 |
63 | itAsync "should 413 when over limit with Content-Length" (fun d ->
64 | let buf = buffer.Buffer.alloc(1024, ".")
65 |
66 | let server =
67 | createServer(jsOptions(fun o ->
68 | o.limit <- !^ "1kb"
69 | ))
70 |
71 | let test =
72 | request(box server)
73 | .post("/")
74 | .set("Content-Type", "application/json")
75 | .set("Content-Length", "1034")
76 | .send(JS.JSON.stringify(
77 | createObj [
78 | "str" ==> buf.toString()
79 | ]
80 | )
81 | )
82 | :?> SuperTest.Test
83 |
84 | test.expect(413, d)
85 | |> ignore
86 | )
87 | )
88 | )
89 |
--------------------------------------------------------------------------------
/glues/Express/tests/Tests.Express.Port.App.Routes.Error.fs:
--------------------------------------------------------------------------------
1 | module Tests.Express.Port.App.Routes.Error
2 |
3 | open System.Text.RegularExpressions
4 | open Glutinum.ExpressServeStaticCore
5 | open Glutinum.Express
6 | open Fable.Core.JsInterop
7 | open Mocha
8 | open Node
9 | open Fable.Core
10 |
11 | describe "app" (fun () ->
12 |
13 | describe ".VERB()" (fun () ->
14 |
15 | itAsync "should not get invoked without error handler on error" (fun d ->
16 | let app = express.express ()
17 |
18 | app.``use``(fun (req : Request) (res : Response) (next : NextFunction) ->
19 | next.Invoke(System.Exception("boom!"))
20 | )
21 |
22 | app.get("/bar", fun (req : Request) (res : Response) ->
23 | res.send("hello, world!")
24 | )
25 |
26 | request(app)
27 | .post("/bar")
28 | .expect(500, JS.Constructors.RegExp.Create("Error: boom!"), d)
29 | |> ignore
30 | )
31 |
32 | itAsync "should only call an error handling routing callback when an error is propagated" (fun ``done`` ->
33 | let app = express.express ()
34 |
35 | let mutable a = false
36 | let mutable b = false
37 | let mutable c = false
38 | let mutable d = false
39 |
40 | app.get("/",
41 | fun (req : Request) (res : Response) (next : NextFunction) ->
42 | next.Invoke(System.Exception("fabricated error"))
43 | |> Adapter.RequestHandler,
44 | fun (req : Request) (res : Response) (next : NextFunction) ->
45 | a <- true
46 | next.Invoke()
47 | |> Adapter.RequestHandler,
48 | fun (err : Error option) (req : Request) (res : Response) (next : NextFunction) ->
49 | b <- true
50 | Assert.strictEqual(err.Value.Message, "fabricated error")
51 | next.Invoke(err)
52 | |> Adapter.RequestHandler,
53 | fun (err : Error option) (req : Request) (res : Response) (next : NextFunction) ->
54 | c <- true
55 | Assert.strictEqual(err.Value.Message, "fabricated error")
56 | next.Invoke()
57 | |> Adapter.RequestHandler,
58 | fun (err : Error option) (req : Request) (res : Response) (next : NextFunction) ->
59 | d <- true
60 | next.Invoke()
61 | |> Adapter.RequestHandler,
62 | fun (req : Request) (res : Response) ->
63 | Assert.strictEqual(a, false)
64 | Assert.strictEqual(b, true)
65 | Assert.strictEqual(c, true)
66 | Assert.strictEqual(d, false)
67 | res.send(204)
68 | |> Adapter.RequestHandler
69 | )
70 |
71 | request(app)
72 | .get("/")
73 | .expect(204, ``done``)
74 | |> ignore
75 | )
76 | )
77 | )
78 |
--------------------------------------------------------------------------------
/scripts/init-glue-templates.js:
--------------------------------------------------------------------------------
1 | export const initialChangelog = () => {
2 | return `
3 | # Changelog
4 | All notable changes to this project will be documented in this file.
5 |
6 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
7 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
8 |
9 | ## Unreleased
10 | `.trimStart()
11 | }
12 |
13 | export const initialReadme = (glueName, npmPackageName, npmPackageUrl) => {
14 | return `
15 | # Glutinum.${glueName}
16 |
17 | Binding for [${npmPackageName}](${npmPackageUrl})
18 |
19 | ## Usage
20 | `.trimStart()
21 | }
22 |
23 | export const initialGlueFsproj = (glueName, authors, npmPackageUrl) => {
24 | return `
25 |
26 |
27 |
28 | 0.0.0
29 | netstandard2.0
30 | true
31 | ${authors}
32 |
33 | Fable bindings for npm ${npmPackageUrl} package
34 |
35 |
36 |
37 |
38 |
39 |
41 |
44 |
45 | `.trimStart()
46 | }
47 |
48 | const lowerFirstLetter = (txt) => {
49 | return txt.charAt(0).toLowerCase() + txt.slice(1)
50 | }
51 |
52 | export const initialGlueFsharpFile = (glueName, npmPackageName) => {
53 | return `
54 | module rec Glutinum.${glueName}
55 |
56 | open Fable.Core
57 |
58 | []
59 | let ${lowerFirstLetter(glueName)} : ${glueName}.IExports = jsNative
60 |
61 | module ${glueName} =
62 |
63 | type [] IExports =
64 | class end
65 | `.trimStart()
66 | }
67 |
68 | export const initialGlueTestFsproj = (glueName) => {
69 | return `
70 |
71 |
72 |
73 | netstandard2.0
74 | true
75 | ../../../tests-shared
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 | `.trimStart()
86 | }
87 |
88 | export const initialGlueTestFsharpFile = (glueName) => {
89 | return `
90 | module Tests.${glueName}
91 |
92 | open Mocha
93 | open Fable.Core
94 | open Glutinum.${glueName}
95 |
96 | describe "${glueName}" (fun _ ->
97 |
98 | it "Mime new constructor works" (fun _ ->
99 | failwith "Tests should go here"
100 | )
101 | )
102 | `.trimStart()
103 | }
104 |
--------------------------------------------------------------------------------
/glues/SuperTest/src/Glutinum.SuperTest.fs:
--------------------------------------------------------------------------------
1 | module rec Glutinum.SuperTest
2 |
3 | open System
4 | open Fable.Core
5 | open Fable.Core.JS
6 |
7 | []
8 | let supertest : IExports = jsNative
9 |
10 | type IExports =
11 | []
12 | abstract supertest : app : obj -> SuperTest.SuperTest
13 | abstract agent: ?app: obj * ?options: SuperTest.AgentOptions -> SuperTest.SuperAgentTest
14 |
15 | //let supertest (_app : obj) : Supertest.Test = jsNative
16 | //
17 | //type Supertest() =
18 | // []
19 | // abstract agent: ?app: obj * ?options: Supertest.AgentOptions -> Supertest.SuperAgentTest
20 |
21 |
22 | type RegExp = System.Text.RegularExpressions.Regex
23 |
24 | module SuperTest =
25 |
26 | // type [] IExports =
27 | // abstract agent: ?app: obj * ?options: AgentOptions -> SuperAgentTest
28 |
29 | type [] Response =
30 | inherit SuperAgent.Request.Response
31 |
32 | type [] Request =
33 | inherit SuperAgent.Request.SuperAgentRequest
34 |
35 | type CallbackHandler =
36 | Action
37 | // [] abstract Invoke: err: obj option * res: Response -> unit
38 |
39 | type [] Test =
40 | inherit SuperAgent.Request.SuperAgentRequest
41 | abstract app: obj option with get, set
42 | abstract url: string with get, set
43 | abstract serverAddress: app: obj option * path: string -> string
44 | abstract expect: status: int -> Test
45 | abstract expect: body : string -> Test
46 | abstract expect: status: int * callback: Func -> Test
47 | // abstract expect: status: int * body: obj * callback: CallbackHandler -> Test
48 | abstract expect: status: int * body: obj -> Test
49 | abstract expect: status : int * callback: Func -> Test
50 | abstract expect: status: int * body: obj * callback: Func -> Test
51 | abstract expect: status: int * body: obj * callback: Func -> Test
52 | abstract expect: status: string * body: obj * callback: Func -> Test
53 | abstract expect: body: obj * callback: Func -> Test
54 | abstract expect: body: obj * callback: Func -> Test
55 |
56 | abstract expect: checker: (Response -> obj option) * ?callback: CallbackHandler -> Test
57 | abstract expect: body: string * ?callback: CallbackHandler -> Test
58 | abstract expect: body: RegExp * ?callback: CallbackHandler -> Test
59 | abstract expect: body: Object * ?callback: CallbackHandler -> Test
60 | abstract expect: field: string * ``val``: string -> Test
61 | abstract expect: field: string * ``val``: string * callback: Func -> Test
62 | abstract expect: field: string * ``val``: RegExp * ?callback: Func -> Test
63 | abstract ``end``: ?callback: CallbackHandler -> Test
64 |
65 | type [] AgentOptions =
66 | abstract ca: obj option with get, set
67 |
68 | type [] SuperAgentTest =
69 | interface end
70 |
71 | type [] SuperTest<'T when 'T :> SuperAgent.Request.SuperAgentRequest> =
72 | inherit SuperAgent.Request.SuperAgent<'T>
73 |
--------------------------------------------------------------------------------
/glues/Express/tests/Tests.Express.Port.App.Request.fs:
--------------------------------------------------------------------------------
1 | module Tests.Express.Port.App.Request
2 |
3 | open Glutinum.Express
4 | open Fable.Core.JsInterop
5 | open Mocha
6 | open Glutinum.SuperTest
7 | open Fable.Core
8 |
9 | // this ensures the two types are the same to asset that the is method below has the expected return type
10 | let expectEqual (actual: 'T, expected: 'T) =
11 | Assert.strictEqual(actual, expected)
12 |
13 | let expectEqualNonStrict (actual: 'T, expected: 'T) =
14 | Assert.notStrictEqual(actual, expected)
15 |
16 | let url : obj = import "*" "url"
17 |
18 | describe "app" (fun _ ->
19 | describe ".request" (fun _ ->
20 | itAsync "should extend the request prototype" (fun d ->
21 | let app = express.express ()
22 |
23 | emitJsStatement (app, url) """
24 | $0.request.querystring = function(){
25 | return $1.parse(this.url).query;
26 | };
27 | """
28 |
29 | app.``use``(fun req res ->
30 | res.``end``(req?querystring())
31 | )
32 |
33 | request(app)
34 | .get("/foo?name=tobi")
35 | .expect("name=tobi", d)
36 | |> ignore
37 | )
38 | )
39 |
40 | describe ".is"(fun _ ->
41 |
42 | itAsync "should return null when body is missing" (fun d ->
43 | let app = express.express ()
44 | app.``use``(fun req res ->
45 | let json: U2 = req.is(!^"json")
46 | //NOTE: not testing strict equality here since the string option returns a null, whereas
47 | // the runtime representation of None is undefined
48 | expectEqualNonStrict(json, !^None)
49 | //NOTE: converting this to a boolean, since providing the option to the send method
50 | // will result in `some` checking for null, and the response body being {},
51 | // which is indistinguishable from some other empty object
52 | let json =
53 | match json with
54 | | U2.Case1 (Some value) -> false
55 | | U2.Case1 None -> true
56 | | U2.Case2 _ -> false
57 | res.``send``(json)
58 | )
59 | request(app)
60 | .get("/")
61 | .expect("true", d)
62 | |> ignore
63 | )
64 |
65 | itAsync "should return false when missing application/json content type" (fun d ->
66 | let app = express.express ()
67 | app.``use``(fun req res ->
68 | let json = req.is(!^"json")
69 | expectEqual(json, !^false)
70 | res.``send``(json)
71 | )
72 | let test =
73 | request(app)
74 | .post("/")
75 | .send "{}"
76 | :?> SuperTest.Test
77 | test.expect("false", d)
78 | |> ignore
79 | )
80 |
81 | itAsync "should return 'json' when body and content type is present" (fun d ->
82 | let app = express.express ()
83 | app.``use``(fun req res ->
84 | let json = req.is(!^"json")
85 | expectEqual(json, !^(Some "json"))
86 | res.``send``(json)
87 | )
88 | let test =
89 | request(app)
90 | .post("/")
91 | .set("Content-Type", "application/json")
92 | .send "{}"
93 | :?> SuperTest.Test
94 | test.expect("json", d)
95 | |> ignore
96 | )
97 | )
98 | )
99 |
--------------------------------------------------------------------------------
/glues/Express/tests/Tests.Express.Port.App.Engine.fs:
--------------------------------------------------------------------------------
1 | module Tests.Express.Port.App.Engine
2 |
3 | open Mocha
4 | open Glutinum.ExpressServeStaticCore
5 | open Glutinum.Express
6 | open Node
7 | open Fable.Core.JsInterop
8 |
9 | let __dirname =
10 | emitJsExpr () """
11 | import { dirname } from 'dirname-filename-esm';
12 | dirname(import.meta);
13 | """
14 |
15 | type RenderOption =
16 | {
17 | user :
18 | {|
19 | name : string
20 | |}
21 | }
22 |
23 | let render path (options : RenderOption) (fn : EngineRenderFunc) =
24 | fs.readFile(path, "utf8", fun (err : Base.ErrnoException option) (str : string) ->
25 | if err.IsSome then
26 | fn.Invoke(err, None)
27 | else
28 | let str = str.Replace("{{user.name}}", options.user.name)
29 | fn.Invoke(null, Some str)
30 | )
31 |
32 |
33 | describe "app.engine(ext, fn)" (fun _ ->
34 |
35 | itAsync "should map a template engine" (fun d ->
36 | let app = express.express ()
37 |
38 | app.set("views", path.join(__dirname, "fixtures")) |> ignore
39 | app.engine(".html", render) |> ignore
40 | app.locals.["user"] <-
41 | {|
42 | name = "tobi"
43 | |}
44 |
45 | app.render("user.html", fun err str ->
46 | if err.IsSome then
47 | d err
48 | else
49 | Assert.strictEqual(str, "tobi
")
50 | d()
51 | )
52 | )
53 |
54 | it "should throw when the callback is missing" (fun _ ->
55 | let app = express.express ()
56 |
57 | Assert.throws(
58 | (fun () ->
59 | app.engine(".html", unbox null)
60 | )
61 | )
62 | )
63 |
64 | itAsync """should work without leading "." """ (fun d ->
65 | let app = express.express ()
66 |
67 | app.set("views", path.join(__dirname, "fixtures")) |> ignore
68 | app.engine("html", render) |> ignore
69 | app.locals.["user"] <-
70 | {|
71 | name = "tobi"
72 | |}
73 |
74 | app.render("user.html", fun err str ->
75 | if err.IsSome then
76 | d err
77 | else
78 | Assert.strictEqual(str, "tobi
")
79 | d()
80 | )
81 |
82 | )
83 |
84 | itAsync """should work "view engine" setting""" (fun d ->
85 | let app = express.express ()
86 |
87 | app.set("views", path.join(__dirname, "fixtures")) |> ignore
88 | app.engine("html", render) |> ignore
89 | app.set("view engine","html") |> ignore
90 | app.locals.["user"] <-
91 | {|
92 | name = "tobi"
93 | |}
94 |
95 | app.render("user.html", fun err str ->
96 | if err.IsSome then
97 | d err
98 | else
99 | Assert.strictEqual(str, "tobi
")
100 | d()
101 | )
102 | )
103 |
104 | itAsync """should work "view engine" with leading "." """ (fun d ->
105 | let app = express.express ()
106 |
107 | app.set("views", path.join(__dirname, "fixtures")) |> ignore
108 | app.engine(".html", render) |> ignore
109 | app.set("view engine",".html") |> ignore
110 | app.locals.["user"] <-
111 | {|
112 | name = "tobi"
113 | |}
114 |
115 | app.render("user.html", fun err str ->
116 | if err.IsSome then
117 | d err
118 | else
119 | Assert.strictEqual(str, "tobi
")
120 | d()
121 | )
122 |
123 | )
124 |
125 | )
126 |
--------------------------------------------------------------------------------
/tests-shared/Node.Assert.fs:
--------------------------------------------------------------------------------
1 | module rec Node.Assert
2 |
3 | open System
4 | open Fable.Core
5 | open Fable.Core.JS
6 |
7 | type Error = System.Exception
8 | type Function = System.Action
9 | type RegExp = System.Text.RegularExpressions.Regex
10 |
11 | type [] IExports =
12 | abstract AssertionError: AssertionErrorStatic
13 | abstract fail: ?message: Error -> unit
14 | abstract fail: ?message: string -> unit
15 | []
16 | abstract fail: actual: obj * expected: obj option * ?message: U2 * ?operator: string * ?stackStartFn: Function -> unit
17 | abstract ok: value: obj * ?message: U2 -> unit
18 | []
19 | abstract equal: actual: obj * expected: obj option * ?message: U2 -> unit
20 | []
21 | abstract notEqual: actual: obj * expected: obj option * ?message: U2 -> unit
22 | []
23 | abstract deepEqual: actual: obj * expected: obj option * ?message: U2 -> unit
24 | []
25 | abstract notDeepEqual: actual: obj * expected: obj option * ?message: U2 -> unit
26 | abstract strictEqual: actual: obj * expected: 'T * ?message: U2 -> unit
27 | abstract notStrictEqual: actual: obj * expected: 'T * ?message: U2 -> unit
28 | abstract deepStrictEqual: actual: obj * expected: 'T * ?message: U2 -> unit
29 | abstract notDeepStrictEqual: actual: obj * expected: obj option * ?message: U2 -> unit
30 | abstract throws: block: (unit -> 'T) * ?message: string -> unit
31 | abstract throws: block: (unit -> obj) * ?message: Error -> unit
32 | abstract throws: block: (unit -> obj) -> unit
33 | abstract throws: block: (unit -> obj option) * error: AssertPredicate * ?message: U2 -> unit
34 | abstract doesNotThrow: block: (unit -> obj option) * ?message: U2 -> unit
35 | abstract doesNotThrow: block: (unit -> obj option) * error: U2 * ?message: U2 -> unit
36 | abstract ifError: value: obj option -> bool
37 | abstract rejects: block: U2<(unit -> Promise), Promise> * ?message: U2 -> Promise
38 | abstract rejects: block: U2<(unit -> Promise), Promise> * error: AssertPredicate * ?message: U2 -> Promise
39 | abstract doesNotReject: block: U2<(unit -> Promise), Promise> * ?message: U2 -> Promise
40 | abstract doesNotReject: block: U2<(unit -> Promise), Promise> * error: U2 * ?message: U2 -> Promise
41 | abstract ``match``: value: string * regExp: RegExp * ?message: U2 -> unit
42 | abstract doesNotMatch: value: string * regExp: RegExp * ?message: U2 -> unit
43 | abstract strict: obj
44 |
45 | type [] AssertionError =
46 | inherit Error
47 | abstract name: string with get, set
48 | abstract message: string with get, set
49 | abstract actual: obj with get, set
50 | abstract expected: obj option with get, set
51 | abstract operator: string with get, set
52 | abstract generatedMessage: bool with get, set
53 | abstract code: string with get, set
54 |
55 | type [] AssertionErrorStatic =
56 | [] abstract Create: ?options: AssertionErrorStaticOptions -> AssertionError
57 |
58 | type [] AssertionErrorStaticOptions =
59 | abstract message: string option with get, set
60 | abstract actual: obj with get, set
61 | abstract expected: obj option with get, set
62 | abstract operator: string option with get, set
63 | abstract stackStartFn: Function option with get, set
64 |
65 | type AssertPredicate =
66 | U5 bool), obj, Error>
67 |
--------------------------------------------------------------------------------
/glues/RangeParser/src/Glutinum.RangeParser.fs:
--------------------------------------------------------------------------------
1 | namespace rec Glutinum.RangeParser
2 |
3 | open System
4 | open Fable.Core
5 |
6 | []
7 | module Api =
8 | []
9 | let rangeParser : IExports = jsNative
10 |
11 | type IExports =
12 | ///
13 | /// When ranges are returned, the array has a "type" property which is the type of
14 | /// range that is required (most commonly, "bytes"). Each array element is an object
15 | /// with a "start" and "end" property for the portion of the range.
16 | ///
17 | /// Use ParseRangeResult pattern matcher against the result of parseString.Invoke to get a typed result.
18 | ///
19 | ///
20 | /// let range = parseRange.Invoke(1000, "bytes=0-499")
21 | ///
22 | /// match range with
23 | /// | ParseRangeResult.UnkownError error ->
24 | /// printfn "Result of parseRange.Invoke is an unkown error value... If this happen a fix is probably needed in the binding"
25 | ///
26 | /// | ParseRangeResult.ResultInvalid error ->
27 | /// printfn "Result of parseRange.Invoke is an error of type ResultInvalid"
28 | ///
29 | /// | ParseRangeResult.ResultUnsatisfiable error ->
30 | /// printfn "Result of parseRange.Invoke is an error of type ResultUnsatisfiable"
31 | ///
32 | /// | ParseRangeResult.Range range ->
33 | /// // Here you can access your successful result
34 | /// Expect.equal range.``type "bytes" ""
35 | /// Expect.equal range.Count 1 ""
36 | /// Expect.equal range.[0].start 0 ""
37 | /// Expect.equal range.[0].``end 499 ""
38 | ///
39 | ///
40 | []
41 | abstract rangeParser : size: int * str: string * ?options: RangeParser.Options -> RangeParser.ParseRangeResult
42 |
43 | ///
44 | /// Equivalent to
45 | ///
46 | /// npm.parseRange(
47 | /// size,
48 | /// str,
49 | /// jsOptions<RangeParser.Types.Options>(fun o ->
50 | /// o.combine <- true
51 | /// )
52 | /// )
53 | ///
54 | ///
55 | []
56 | abstract rangeParser : size: int * str: string * combine : bool -> RangeParser.ParseRangeResult
57 |
58 | module RangeParser =
59 |
60 | type Array<'T> = Collections.Generic.IList<'T>
61 |
62 | type [] Ranges =
63 | inherit Array
64 | abstract ``type``: string with get, set
65 |
66 | type [] Range =
67 | abstract start: int with get, set
68 | abstract ``end``: int with get, set
69 |
70 | type [] Options =
71 | /// The "combine" option can be set to true and overlapping & adjacent ranges
72 | /// will be combined into a single range.
73 | abstract combine: bool with get, set
74 |
75 | ///
76 | /// Alias type representing an error case. The runtime representation of this type is always equal to -1
77 | ///
78 | /// Please use ParseRangeResult.ResultUnsatisfiable pattern matcher againt the result of parseString.Invoke to get a typed result
79 | ///
80 | type ResultUnsatisfiable =
81 | int
82 |
83 | ///
84 | /// Alias type representing an error case. The runtime representation of this type is always equal to -2
85 | ///
86 | /// Please use ParseRangeResult.ResultUnsatisfiable pattern matcher againt the result of parseString.Invoke to get a typed result
87 | ///
88 | type ResultInvalid =
89 | int
90 |
91 | ///
92 | /// Alias type representing an error case
93 | ///
94 | type Errored =
95 | U2
96 |
97 | ///
98 | /// Alias type representing the result of parseString.Invoke
99 | ///
100 | type ParseRangeResult =
101 | U2
102 |
--------------------------------------------------------------------------------
/glues/Qs/src/Glutinum.Qs.fs:
--------------------------------------------------------------------------------
1 | module rec Glutinum.Qs
2 |
3 | open System
4 | open Fable.Core
5 | open Fable.Core.JS
6 |
7 | []
8 | let qs : Qs.IExports = jsNative
9 |
10 | module Qs =
11 |
12 | type Array<'T> = Collections.Generic.IList<'T>
13 | type RegExp = Text.RegularExpressions.Regex
14 |
15 | type [] IExports =
16 | abstract stringify: obj: obj * ?options: IStringifyOptions -> string
17 | // abstract parse: str: string -> ParsedQs
18 | abstract parse: str: string * ?options: IParseOptions -> ParsedQs
19 | // abstract parse: str: string * ?options: IParseOptions -> ParseReturn
20 |
21 | type [] ParseReturn =
22 | [] abstract Item: key: string -> obj with get, set
23 |
24 | type [] defaultEncoder =
25 | [] abstract Invoke: str: obj * ?defaultEncoder: obj * ?charset: string -> string
26 |
27 | type [] defaultDecoder =
28 | [] abstract Invoke: str: string * ?decoder: obj * ?charset: string -> string
29 |
30 | type [] IStringifyOptions =
31 | abstract delimiter: string with get, set
32 | abstract strictNullHandling: bool with get, set
33 | abstract skipNulls: bool with get, set
34 | abstract encode: bool with get, set
35 | abstract encoder: Func with get, set
36 | abstract filter: U2>, (string -> obj option -> obj option)> with get, set
37 | abstract arrayFormat: IArrayFormat with get, set
38 | abstract indices: bool with get, set
39 | abstract sort: (obj option -> obj option -> float) with get, set
40 | abstract serializeDate: (DateTime -> string) with get, set
41 | abstract format: IStringifyOptionsFormat with get, set
42 | abstract encodeValuesOnly: bool with get, set
43 | abstract addQueryPrefix: bool with get, set
44 | abstract allowDots: bool with get, set
45 | abstract charset: IStringifyOptionsCharset with get, set
46 | abstract charsetSentinel: bool with get, set
47 |
48 | type [] IParseOptions =
49 | abstract comma: bool with get, set
50 | abstract delimiter: U2 with get, set
51 | abstract depth: float with get, set
52 | abstract decoder: Func with get, set
53 | abstract arrayLimit: float with get, set
54 | abstract arrayFormat: IArrayFormat with get, set
55 | abstract parseArrays: bool with get, set
56 | abstract allowDots: bool with get, set
57 | abstract plainObjects: bool with get, set
58 | abstract allowPrototypes: bool with get, set
59 | abstract parameterLimit: float with get, set
60 | abstract strictNullHandling: bool with get, set
61 | abstract ignoreQueryPrefix: bool with get, set
62 | abstract charset: IStringifyOptionsCharset with get, set
63 | abstract charsetSentinel: bool with get, set
64 | abstract interpretNumericEntities: bool with get, set
65 |
66 | type [] ParsedQs =
67 | [] abstract Item: key: string -> U4, ParsedQs, ResizeArray> option with get, set
68 | [] abstract Item: key: int -> U4, ParsedQs, ResizeArray> option with get, set
69 |
70 | type [] [] IStringifyOptionsEncoder =
71 | | Key
72 | | Value
73 |
74 | type [] [] IArrayFormat =
75 | | Indices
76 | | Brackets
77 | | Repeat
78 | | Comma
79 |
80 | type [] [] IStringifyOptionsFormat =
81 | | [] RFC1738
82 | | [] RFC3986
83 |
84 | type [] [] IStringifyOptionsCharset =
85 | | [] Utf8
86 | | [] Iso88591
87 |
--------------------------------------------------------------------------------
/glues/Express/tests/Tests.Express.Port.App.Options.fs:
--------------------------------------------------------------------------------
1 | module Tests.Express.Port.App.Options
2 |
3 | open Mocha
4 | open Glutinum.ExpressServeStaticCore
5 | open Glutinum.Express
6 |
7 | describe "OPTIONS" (fun _ ->
8 |
9 | itAsync "should default to the routes defined" (fun d ->
10 | let app = express.express ()
11 |
12 | app.del("/", fun (_ : Request) (_ : Response) -> ())
13 | app.get("/users", fun _ _ -> ())
14 | app.put("/users", fun _ _ -> ())
15 |
16 | request(app)
17 | .options("/users")
18 | .expect("Allow", "GET,HEAD,PUT")
19 | .expect(200, "GET,HEAD,PUT", d)
20 | |> ignore
21 | )
22 |
23 | itAsync "should only include each method once" (fun d ->
24 | let app = express.express ()
25 |
26 | app.del("/", fun (_ : Request) (_ : Response) -> ())
27 | app.get("/users", fun _ _ -> ())
28 | app.put("/users", fun _ _ -> ())
29 | app.get("/users", fun _ _ -> ())
30 |
31 | request(app)
32 | .options("/users")
33 | .expect("Allow", "GET,HEAD,PUT")
34 | .expect(200, "GET,HEAD,PUT", d)
35 | |> ignore
36 |
37 | )
38 |
39 | itAsync "should not be affected by app.all" (fun d ->
40 | let app = express.express ()
41 |
42 | app.del("/", fun (_ : Request) (_ : Response) -> ())
43 | app.get("/users", fun _ _ -> ())
44 | app.put("/users", fun _ _ -> ())
45 | app.all("/users", fun (req : Request) (res : Response) (next : NextFunction) ->
46 | res.setHeader("x-hit", "1")
47 | next.Invoke()
48 | )
49 |
50 | request(app)
51 | .options("/users")
52 | .expect("x-hit", "1")
53 | .expect("Allow", "GET,HEAD,PUT")
54 | .expect(200, "GET,HEAD,PUT", d)
55 | |> ignore
56 |
57 | )
58 |
59 | itAsync "should not respond if the path is not defined" (fun d ->
60 | let app = express.express ()
61 |
62 | app.get("/users", fun _ _ -> ())
63 |
64 | request(app)
65 | .options("/other")
66 | .expect(404, d)
67 | |> ignore
68 | )
69 |
70 | itAsync "should forward requests down the middleware chain" (fun d ->
71 | let app = express.express ()
72 | let router = express.Router()
73 |
74 | router.get("/users", fun _ _ -> ())
75 | app.``use``(router)
76 | app.get("/other", fun _ _ -> ())
77 |
78 | request(app)
79 | .options("/other")
80 | .expect("Allow", "GET,HEAD")
81 | .expect(200, "GET,HEAD", d)
82 | |> ignore
83 |
84 | )
85 |
86 | describe "when error occurs in response handler" (fun _ ->
87 |
88 | itAsync "should pass error to callback" (fun d ->
89 | let app = express.express ()
90 | let router = express.Router()
91 |
92 | router.get("/users", fun _ _ -> ())
93 |
94 | app.``use``(fun (req : Request) (res : Response) (next : NextFunction) ->
95 | res.writeHead(200)
96 | next.Invoke()
97 | )
98 |
99 | app.``use``(router)
100 | app.``use``(fun err req (res : Response) next ->
101 | res.``end``("true")
102 | )
103 |
104 | request(app)
105 | .options("/users")
106 | .expect(200, "true", d)
107 | |> ignore
108 | )
109 |
110 | )
111 | )
112 |
113 | describe "app.options()" (fun _ ->
114 |
115 | itAsync "should override the default behaviour" (fun d ->
116 | let app = express.express ()
117 |
118 | app.options("/users", fun (req : Request) (res : Response) ->
119 | res.set("Allow", "GET") |> ignore
120 | res.send("GET")
121 | )
122 |
123 | app.get("/users", fun _ _ -> ())
124 | app.put("/users", fun _ _ -> ())
125 |
126 | request(app)
127 | .options("/users")
128 | .expect("GET")
129 | .expect("Allow", "GET", d)
130 | |> ignore
131 |
132 | )
133 |
134 | )
135 |
--------------------------------------------------------------------------------
/glues/Qs/tests/Tests.Qs.fs:
--------------------------------------------------------------------------------
1 | module Tests.Qs
2 |
3 | open Mocha
4 | open Fable.Core
5 | open Fable.Core.Testing
6 | open Fable.Core.JsInterop
7 | open Glutinum.Qs
8 |
9 | // Code adapted from: https://github.com/ljharb/qs/tree/b04febd9cb1c94b466aa2bd81b6452b44712414e
10 |
11 |
12 | []
13 | let jsTypeOf _ = jsNative
14 |
15 | []
16 | let JsBigInt _ : bigint = jsNative
17 |
18 | let private parseApi () =
19 | describe "qs.parse()" (fun _ ->
20 |
21 | it "parses a simple string" (fun _ ->
22 | let res = qs.parse("0=foo")
23 |
24 | Assert.AreEqual(res.[0], Some (!^ "foo"))
25 | )
26 |
27 | it "arrayFormat: brackets allows only explicit arrays" (fun _ ->
28 | let res =
29 | qs.parse(
30 | "a[0]=b&a[1]=c",
31 | jsOptions(fun o ->
32 | o.arrayFormat <- Qs.IArrayFormat.Brackets
33 | )
34 | )
35 |
36 | Assert.AreEqual(res.["a"], Some (!^ ResizeArray(["b"; "c"])))
37 | )
38 |
39 | it "allows for decoding keys and values differently" (fun _ ->
40 | let decoder =
41 | // Code copied from Qs test but it seems typ is always undefined so it doesn't work /shrug
42 | // So for now, I am using some stupid decoder just to test the API
43 | // System.Func<_,_,_,_,_>(
44 | // fun (str : string) (defaultDecoder : Types.defaultDecoder) (charset : string) (typ : Types.IStringifyOptionsEncoder) ->
45 |
46 | // if typ = Types.IStringifyOptionsEncoder.Key then
47 | // defaultDecoder.Invoke(str, defaultDecoder, charset).ToLower() |> box
48 |
49 | // else if typ = Types.IStringifyOptionsEncoder.Value then
50 | // defaultDecoder.Invoke(str, defaultDecoder, charset).ToUpper() |> box
51 |
52 | // else
53 | // failwithf "this should never happen! type: %A" typ
54 | // )
55 |
56 | System.Func<_,_,_,_,_>(
57 | fun (str : string) (defaultDecoder : Qs.defaultDecoder) (charset : string) (_: Qs.IStringifyOptionsEncoder) ->
58 | if str = "KeY" then
59 | box "key"
60 |
61 | else if str = "vAlUe" then
62 | box "VALUE"
63 |
64 | else
65 | failwithf "this should never happen! type: %A" str
66 | )
67 |
68 | let res =
69 | qs.parse(
70 | "KeY=vAlUe",
71 | jsOptions(fun o ->
72 | o.decoder <- decoder
73 | )
74 | )
75 |
76 | Assert.AreEqual(res.["key"], Some (!^ "VALUE"))
77 |
78 | )
79 |
80 | )
81 |
82 | let private stringifyApi () =
83 | describe "qs.stringify()" (fun _ ->
84 | it "stringifies a querystring object" (fun _ ->
85 | let res =
86 | qs.stringify({| a = "b" |})
87 |
88 | Assert.AreEqual(res, "a=b")
89 |
90 | )
91 |
92 | it "stringifies using encoder" (fun _->
93 | let encoder =
94 | System.Func<_,Qs.defaultEncoder,_,_,_> (fun value defaultEncoder charset _ ->
95 | let result = defaultEncoder.Invoke(value, defaultEncoder, charset)
96 |
97 | if jsTypeOf value = "bigint" then
98 | result + "n"
99 | else
100 | result
101 | )
102 |
103 | let res =
104 | qs.stringify(
105 | createObj [
106 | "a" ==> JsBigInt 2
107 | ],
108 | jsOptions(fun o ->
109 | o.encoder <- encoder
110 | )
111 | )
112 |
113 | Assert.AreEqual(res, "a=2n")
114 | )
115 | )
116 |
117 |
118 | describe "Qs" (fun _ ->
119 | parseApi ()
120 | stringifyApi ()
121 | )
122 |
--------------------------------------------------------------------------------
/glues/RangeParser.Extensions/tests/Tests.RangeParser.fs:
--------------------------------------------------------------------------------
1 | module Tests.RangeParser
2 |
3 | open Mocha
4 | open Fable.Core.Testing
5 | open Fable.Core.JsInterop
6 | open Glutinum.RangeParser
7 |
8 | // Code adapted from: https://github.com/jshttp/range-parser/tree/5f48dfc7996b18242dfa1fbddcc03f39b42a4554
9 |
10 |
11 | describe "RangeParser" (fun _ ->
12 |
13 | describe "parseRange(len, str)" (fun _ ->
14 |
15 | itAsync "should return -2 (aka ResultInvalid) for invalid str" (fun ok ->
16 | let range = rangeParser.rangeParser(200, "malformed")
17 |
18 | match range with
19 | | ParseRangeResult.ResultInvalid ->
20 | ok()
21 |
22 | | ParseRangeResult.UnkownError _
23 | | ParseRangeResult.ResultUnsatisfiable
24 | | ParseRangeResult.Range _ ->
25 | failwith "Should not happen"
26 | )
27 |
28 | itAsync "should return -1 if all specified ranges are invalid" (fun ok ->
29 | let range = rangeParser.rangeParser(200, "bytes=500-20")
30 |
31 | match range with
32 | | ParseRangeResult.ResultUnsatisfiable ->
33 | ok()
34 |
35 | | ParseRangeResult.UnkownError _
36 | | ParseRangeResult.ResultInvalid
37 | | ParseRangeResult.Range _ ->
38 | failwith "Should not happen"
39 | )
40 |
41 | it "should parse str" (fun _ ->
42 | let range = rangeParser.rangeParser(1000, "bytes=0-499")
43 |
44 | match range with
45 | | ParseRangeResult.UnkownError _
46 | | ParseRangeResult.ResultInvalid
47 | | ParseRangeResult.ResultUnsatisfiable ->
48 | failwith "Should not happen"
49 |
50 | | ParseRangeResult.Range range ->
51 | // Here you can access your successful result
52 | Assert.AreEqual(range.``type``, "bytes")
53 | Assert.AreEqual(range.Count, 1)
54 | Assert.AreEqual(range.[0].start, 0)
55 | Assert.AreEqual(range.[0].``end``, 499)
56 | )
57 |
58 | )
59 |
60 | describe "when combine: true" (fun _ ->
61 |
62 | it "should combine overlapping ranges" (fun _ ->
63 | let range = rangeParser.rangeParser(
64 | 150,
65 | "bytes=0-4,90-99,5-75,100-199,101-102",
66 | jsOptions(fun o ->
67 | o.combine <- true
68 | )
69 | )
70 |
71 | match range with
72 | | ParseRangeResult.UnkownError _
73 | | ParseRangeResult.ResultInvalid
74 | | ParseRangeResult.ResultUnsatisfiable ->
75 | failwith "Should not happen"
76 |
77 | | ParseRangeResult.Range range ->
78 | // Here you can access your successful result
79 | Assert.AreEqual(range.``type``, "bytes")
80 | Assert.AreEqual(range.Count, 2)
81 | Assert.AreEqual(range.[0].start, 0)
82 | Assert.AreEqual(range.[0].``end``, 75 )
83 | Assert.AreEqual(range.[1].start, 90)
84 | Assert.AreEqual(range.[1].``end``, 149)
85 | )
86 |
87 | it "overload npm.rangeParser with direct combine argument works" (fun _ ->
88 | let range =
89 | rangeParser.rangeParser(
90 | 150,
91 | "bytes=0-4,90-99,5-75,100-199,101-102",
92 | true
93 | )
94 |
95 | match range with
96 | | ParseRangeResult.UnkownError _
97 | | ParseRangeResult.ResultInvalid
98 | | ParseRangeResult.ResultUnsatisfiable ->
99 | failwith "Should not happen"
100 |
101 | | ParseRangeResult.Range range ->
102 | // Here you can access your successful result
103 | Assert.AreEqual(range.``type``, "bytes")
104 | Assert.AreEqual(range.Count, 2)
105 | Assert.AreEqual(range.[0].start, 0)
106 | Assert.AreEqual(range.[0].``end``, 75 )
107 | Assert.AreEqual(range.[1].start, 90)
108 | Assert.AreEqual(range.[1].``end``, 149)
109 | )
110 |
111 | )
112 |
113 | )
114 |
--------------------------------------------------------------------------------
/glues/BodyParser/src/Glutinum.BodyParser.fs:
--------------------------------------------------------------------------------
1 | module rec Glutinum.BodyParser
2 |
3 | open System
4 | open Fable.Core
5 | open Node
6 |
7 | []
8 | let bodyParser : BodyParser.IExports = jsNative
9 |
10 | module BodyParser =
11 |
12 | type [] IExports =
13 | ///
14 | /// Returns middleware that only parses json and only looks at requests
15 | /// where the Content-Type header matches the type option.
16 | ///
17 | abstract json: ?options: OptionsJson -> Func, unit>
18 | ///
19 | /// Returns middleware that parses all bodies as a Buffer and only looks at requests
20 | /// where the Content-Type header matches the type option.
21 | ///
22 | abstract raw: ?options: Options -> Func, unit>
23 | ///
24 | /// Returns middleware that parses all bodies as a string and only looks at requests
25 | /// where the Content-Type header matches the type option.
26 | ///
27 | abstract text: ?options: OptionsText -> Func, unit>
28 | ///
29 | /// Returns middleware that only parses urlencoded bodies and only looks at requests
30 | /// where the Content-Type header matches the type option
31 | ///
32 | abstract urlencoded: ?options: OptionsUrlencoded -> Func, unit>
33 |
34 | type [] Options =
35 | ///
36 | /// When set to true, then deflated (compressed) bodies will be inflated; when false, deflated bodies are rejected. Defaults to true.
37 | ///
38 | abstract inflate: bool with get, set
39 | ///
40 | /// Controls the maximum request body size. If this is a number,
41 | /// then the value specifies the number of bytes; if it is a string,
42 | /// the value is passed to the bytes library for parsing. Defaults to '100kb'.
43 | ///
44 | abstract limit: U2 with get, set
45 | ///
46 | /// The type option is used to determine what media type the middleware will parse
47 | ///
48 | abstract ``type``: U3, (Http.IncomingMessage -> obj option)> with get, set
49 | ///
50 | /// The verify option, if supplied, is called as verify(req, res, buf, encoding),
51 | /// where buf is a Buffer of the raw request body and encoding is the encoding of the request.
52 | ///
53 | abstract verify: req: Http.IncomingMessage * res: Http.ServerResponse * buf: Buffer * encoding: string -> unit
54 |
55 | type [] OptionsJson =
56 | inherit Options
57 | ///
58 | /// The reviver option is passed directly to JSON.parse as the second argument.
59 | ///
60 | abstract reviver: key: string * value: obj option -> obj option
61 | ///
62 | /// When set to true, will only accept arrays and objects;
63 | /// when `false` will accept anything JSON.parse accepts. Defaults to true.
64 | ///
65 | abstract strict: bool with get, set
66 |
67 | type [] OptionsText =
68 | inherit Options
69 | ///
70 | /// Specify the default character set for the text content if the charset
71 | /// is not specified in the Content-Type header of the request.
72 | /// Defaults to utf-8.
73 | ///
74 | abstract defaultCharset: string with get, set
75 |
76 | type [] OptionsUrlencoded =
77 | inherit Options
78 | ///
79 | /// The extended option allows to choose between parsing the URL-encoded data
80 | /// with the querystring library (when `false`) or the qs library (when true).
81 | ///
82 | abstract extended: bool with get, set
83 | ///
84 | /// The parameterLimit option controls the maximum number of parameters
85 | /// that are allowed in the URL-encoded data. If a request contains more parameters than this value,
86 | /// a 413 will be returned to the client. Defaults to 1000.
87 | ///
88 | abstract parameterLimit: float with get, set
89 |
--------------------------------------------------------------------------------
/glues/ServeStatic/src/Glutinum.ServeStatic.fs:
--------------------------------------------------------------------------------
1 | module rec Glutinum.ServeStatic
2 |
3 | // Exported from: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/0893371fea43bfdf1777b6d835424961ba0d1dbb/types/mime/index.d.ts
4 |
5 | open Fable.Core
6 | open Node
7 | open Mime
8 | open Glutinum.Connect
9 |
10 | []
11 | let serveStatic : IExports = jsNative
12 |
13 | type [] IExports =
14 | /// Create a new middleware function to serve files from within a given root directory.
15 | /// The file to serve will be determined by combining req.url with the provided root directory.
16 | /// When a file is not found, instead of sending a 404 response, this module will instead call next() to move on to the next middleware, allowing for stacking and fall-backs.
17 | []
18 | abstract serveStatic: root: string * ?options: ServeStatic.ServeStaticOptions<'R> -> ServeStatic.RequestHandler<'R> when 'R :> Http.ServerResponse
19 |
20 | abstract mime : Mime.IExports
21 |
22 | type ServeStaticOptions =
23 | ServeStaticOptions
24 |
25 | type [] ServeStaticOptions<'R when 'R :> Http.ServerResponse> =
26 | /// Enable or disable setting Cache-Control response header, defaults to true.
27 | /// Disabling this will ignore the immutable and maxAge options.
28 | abstract cacheControl: bool with get, set
29 | /// Set how "dotfiles" are treated when encountered. A dotfile is a file or directory that begins with a dot (".").
30 | /// Note this check is done on the path itself without checking if the path actually exists on the disk.
31 | /// If root is specified, only the dotfiles above the root are checked (i.e. the root itself can be within a dotfile when when set to "deny").
32 | /// The default value is 'ignore'.
33 | /// 'allow' No special treatment for dotfiles
34 | /// 'deny' Send a 403 for any request for a dotfile
35 | /// 'ignore' Pretend like the dotfile does not exist and call next()
36 | abstract dotfiles: string with get, set
37 | /// Enable or disable etag generation, defaults to true.
38 | abstract etag: bool with get, set
39 | /// Set file extension fallbacks. When set, if a file is not found, the given extensions will be added to the file name and search for.
40 | /// The first that exists will be served. Example: ['html', 'htm'].
41 | /// The default value is false.
42 | abstract extensions: ResizeArray with get, set
43 | /// Let client errors fall-through as unhandled requests, otherwise forward a client error.
44 | /// The default value is true.
45 | abstract fallthrough: bool with get, set
46 | /// Enable or disable the immutable directive in the Cache-Control response header.
47 | /// If enabled, the maxAge option should also be specified to enable caching. The immutable directive will prevent supported clients from making conditional requests during the life of the maxAge option to check if the file has changed.
48 | abstract immutable: bool with get, set
49 | /// By default this module will send "index.html" files in response to a request on a directory.
50 | /// To disable this set false or to supply a new index pass a string or an array in preferred order.
51 | abstract index: U3> with get, set
52 | /// Enable or disable Last-Modified header, defaults to true. Uses the file system's last modified value.
53 | abstract lastModified: bool with get, set
54 | /// Provide a max-age in milliseconds for http caching, defaults to 0. This can also be a string accepted by the ms module.
55 | abstract maxAge: U2 with get, set
56 | /// Redirect to trailing "/" when the pathname is a dir. Defaults to true.
57 | abstract redirect: bool with get, set
58 | /// Function to set custom headers on response. Alterations to the headers need to occur synchronously.
59 | /// The function is called as fn(res, path, stat), where the arguments are:
60 | /// res the response object
61 | /// path the file path that is being sent
62 | /// stat the stat object of the file that is being sent
63 | abstract setHeaders: ('R -> string -> obj option -> obj option) with get, set
64 |
65 | type [] RequestHandler<'R when 'R :> Http.ServerResponse> =
66 | [] abstract Invoke: request: Http.IncomingMessage * response: 'R * next: Connect.CreateServer.NextFunction -> obj option
67 |
68 | type [] RequestHandlerConstructor<'R when 'R :> Http.ServerResponse> =
69 | [] abstract Invoke: root: string * ?options: ServeStaticOptions<'R> -> RequestHandler<'R>
70 | abstract mime: obj with get, set
71 |
--------------------------------------------------------------------------------
/glues/Connect/src/Glutinum.Connect.fs:
--------------------------------------------------------------------------------
1 | namespace rec Glutinum.Connect
2 |
3 | open System
4 | open Fable.Core
5 | open Fable.Core.JS
6 | open Node
7 |
8 | []
9 | module Api =
10 | []
11 | let connect () : Connect.CreateServer.Server = jsNative
12 |
13 | type Function = System.Action
14 |
15 | module Connect =
16 | module CreateServer =
17 |
18 | type ServerHandle =
19 | U2
20 |
21 | type [] IncomingMessage =
22 | inherit Http.IncomingMessage
23 | abstract originalUrl: Http.IncomingMessage option with get, set
24 |
25 | type [] IncomingMessageStatic =
26 | [] abstract Create: unit -> IncomingMessage
27 |
28 | // Try with private constructor ?
29 | // type NextFunction private (?err : obj) =
30 | // class end
31 |
32 | type NextFunction =
33 | Func
34 | type SimpleHandleFunction =
35 | Func
36 |
37 | type NextHandleFunction =
38 | Func, unit>
39 |
40 | type ErrorHandleFunction =
41 | Func
42 |
43 | // type [] NextFunction =
44 | // [] abstract Invoke: ?err: obj -> unit
45 |
46 | // type [] SimpleHandleFunction =
47 | // [] abstract Invoke: req: IncomingMessage * res: Http.ServerResponse -> unit
48 |
49 | // type [] NextHandleFunction =
50 | // [] abstract Invoke: req: IncomingMessage * res: Http.ServerResponse * next: NextFunction -> unit
51 |
52 | // type [] ErrorHandleFunction =
53 | // [] abstract Invoke: err: obj option * req: IncomingMessage * res: Http.ServerResponse * next: NextFunction -> unit
54 |
55 | type HandleFunction =
56 | U3
57 |
58 | type [] ServerStackItem =
59 | abstract route: string with get, set
60 | abstract handle: ServerHandle with get, set
61 |
62 | type [] Server =
63 | inherit Events.EventEmitter
64 | [