forked from nim-lang/Nim
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add module jsfetch for fetch support for JavaScript target https://developer.mozilla.org/docs/Web/API/Fetch_API * Update lib/std/jsheaders.nim * Update lib/std/jsformdata.nim * Update lib/std/jsfetch.nim Co-authored-by: Timothee Cour <timothee.cour2@gmail.com> Co-authored-by: flywind <xzsflywind@gmail.com>
- Loading branch information
1 parent
e00f49c
commit c1ca6c3
Showing
5 changed files
with
351 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,198 @@ | ||
## - Fetch for the JavaScript target: https://developer.mozilla.org/docs/Web/API/Fetch_API | ||
## .. Note:: jsfetch is Experimental. jsfetch module requires `-d:nimExperimentalJsfetch` | ||
when not defined(js): | ||
{.fatal: "Module jsfetch is designed to be used with the JavaScript backend.".} | ||
|
||
when defined(nimExperimentalJsfetch) or defined(nimdoc): | ||
import std/[asyncjs, jsheaders, jsformdata] | ||
from std/httpcore import HttpMethod | ||
from std/jsffi import JsObject | ||
|
||
type | ||
FetchOptions* = ref object of JsRoot ## Options for Fetch API. | ||
keepalive*: bool | ||
metod* {.importjs: "method".}: cstring | ||
body*, integrity*, referrer*, mode*, credentials*, cache*, redirect*, referrerPolicy*: cstring | ||
|
||
FetchModes* = enum ## Mode options. | ||
fmCors = "cors" | ||
fmNoCors = "no-cors" | ||
fmSameOrigin = "same-origin" | ||
|
||
FetchCredentials* = enum ## Credential options. See https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials | ||
fcInclude = "include" | ||
fcSameOrigin = "same-origin" | ||
fcOmit = "omit" | ||
|
||
FetchCaches* = enum ## https://developer.mozilla.org/docs/Web/API/Request/cache | ||
fchDefault = "default" | ||
fchNoStore = "no-store" | ||
fchReload = "reload" | ||
fchNoCache = "no-cache" | ||
fchForceCache = "force-cache" | ||
|
||
FetchRedirects* = enum ## Redirects options. | ||
frFollow = "follow" | ||
frError = "error" | ||
frManual = "manual" | ||
|
||
FetchReferrerPolicies* = enum ## Referrer Policy options. | ||
frpNoReferrer = "no-referrer" | ||
frpNoReferrerWhenDowngrade = "no-referrer-when-downgrade" | ||
frpOrigin = "origin" | ||
frpOriginWhenCrossOrigin = "origin-when-cross-origin" | ||
frpUnsafeUrl = "unsafe-url" | ||
|
||
Body* = ref object of JsRoot ## https://developer.mozilla.org/en-US/docs/Web/API/Body | ||
bodyUsed*: bool | ||
|
||
Response* = ref object of JsRoot ## https://developer.mozilla.org/en-US/docs/Web/API/Response | ||
bodyUsed*, ok*, redirected*: bool | ||
typ* {.importjs: "type".}: cstring | ||
url*, statusText*: cstring | ||
status*: cint | ||
headers*: Headers | ||
body*: Body | ||
|
||
Request* = ref object of JsRoot ## https://developer.mozilla.org/en-US/docs/Web/API/Request | ||
bodyUsed*, ok*, redirected*: bool | ||
typ* {.importjs: "type".}: cstring | ||
url*, statusText*: cstring | ||
status*: cint | ||
headers*: Headers | ||
body*: Body | ||
|
||
|
||
func newResponse*(body: cstring | FormData): Response {.importjs: "(new Response(#))".} | ||
## Constructor for `Response`. This does *not* call `fetch()`. Same as `new Response()`. | ||
|
||
func newRequest*(url: cstring): Request {.importjs: "(new Request(#))".} | ||
## Constructor for `Request`. This does *not* call `fetch()`. Same as `new Request()`. | ||
|
||
func clone*(self: Response | Request): Response {.importjs: "#.$1()".} | ||
## https://developer.mozilla.org/en-US/docs/Web/API/Response/clone | ||
|
||
proc text*(self: Response): Future[cstring] {.importjs: "#.$1()".} | ||
## https://developer.mozilla.org/en-US/docs/Web/API/Body/text | ||
|
||
proc json*(self: Response): Future[JsObject] {.importjs: "#.$1()".} | ||
## https://developer.mozilla.org/en-US/docs/Web/API/Body/json | ||
|
||
proc formData*(self: Body): Future[FormData] {.importjs: "#.$1()".} | ||
## https://developer.mozilla.org/en-US/docs/Web/API/Body/formData | ||
|
||
proc unsafeNewFetchOptions*(metod, body, mode, credentials, cache, referrerPolicy: cstring; | ||
keepalive: bool; redirect = "follow".cstring; referrer = "client".cstring; integrity = "".cstring): FetchOptions {.importjs: | ||
"{method: #, body: #, mode: #, credentials: #, cache: #, referrerPolicy: #, keepalive: #, redirect: #, referrer: #, integrity: #}".} | ||
## .. Warning:: Unsafe `newfetchOptions`. | ||
|
||
func newfetchOptions*(metod: HttpMethod; body: cstring; | ||
mode: FetchModes; credentials: FetchCredentials; cache: FetchCaches; referrerPolicy: FetchReferrerPolicies; | ||
keepalive: bool; redirect = frFollow; referrer = "client".cstring; integrity = "".cstring): FetchOptions = | ||
## Constructor for `FetchOptions`. | ||
result = FetchOptions( | ||
body: body, mode: $mode, credentials: $credentials, cache: $cache, referrerPolicy: $referrerPolicy, | ||
keepalive: keepalive, redirect: $redirect, referrer: referrer, integrity: integrity, | ||
metod: (case metod | ||
of HttpHead: "HEAD".cstring | ||
of HttpGet: "GET".cstring | ||
of HttpPost: "POST".cstring | ||
of HttpPut: "PUT".cstring | ||
of HttpDelete: "DELETE".cstring | ||
of HttpPatch: "PATCH".cstring | ||
else: "GET".cstring | ||
) | ||
) | ||
|
||
proc fetch*(url: cstring | Request): Future[Response] {.importjs: "$1(#)".} | ||
## `fetch()` API, simple `GET` only, returns a `Future[Response]`. | ||
|
||
proc fetch*(url: cstring | Request; options: FetchOptions): Future[Response] {.importjs: "$1(#, #)".} | ||
## `fetch()` API that takes a `FetchOptions`, returns a `Future[Response]`. | ||
|
||
func toCstring*(self: Request | Response | Body | FetchOptions): cstring {.importjs: "JSON.stringify(#)".} | ||
|
||
func `$`*(self: Request | Response | Body | FetchOptions): string = $toCstring(self) | ||
|
||
|
||
runnableExamples("-d:nimExperimentalJsfetch -r:off"): | ||
import std/[asyncjs, jsconsole, jsheaders, jsformdata] | ||
from std/httpcore import HttpMethod | ||
from std/jsffi import JsObject | ||
from std/sugar import `=>` | ||
|
||
block: | ||
let options0: FetchOptions = unsafeNewFetchOptions( | ||
metod = "POST".cstring, | ||
body = """{"key": "value"}""".cstring, | ||
mode = "no-cors".cstring, | ||
credentials = "omit".cstring, | ||
cache = "no-cache".cstring, | ||
referrerPolicy = "no-referrer".cstring, | ||
keepalive = false, | ||
redirect = "follow".cstring, | ||
referrer = "client".cstring, | ||
integrity = "".cstring | ||
) | ||
assert options0.keepalive == false | ||
assert options0.metod == "POST".cstring | ||
assert options0.body == """{"key": "value"}""".cstring | ||
assert options0.mode == "no-cors".cstring | ||
assert options0.credentials == "omit".cstring | ||
assert options0.cache == "no-cache".cstring | ||
assert options0.referrerPolicy == "no-referrer".cstring | ||
assert options0.redirect == "follow".cstring | ||
assert options0.referrer == "client".cstring | ||
assert options0.integrity == "".cstring | ||
|
||
block: | ||
let options1: FetchOptions = newFetchOptions( | ||
metod = HttpPost, | ||
body = """{"key": "value"}""".cstring, | ||
mode = fmNoCors, | ||
credentials = fcOmit, | ||
cache = fchNoCache, | ||
referrerPolicy = frpNoReferrer, | ||
keepalive = false, | ||
redirect = frFollow, | ||
referrer = "client".cstring, | ||
integrity = "".cstring | ||
) | ||
assert options1.keepalive == false | ||
assert options1.metod == $HttpPost | ||
assert options1.body == """{"key": "value"}""".cstring | ||
assert options1.mode == $fmNoCors | ||
assert options1.credentials == $fcOmit | ||
assert options1.cache == $fchNoCache | ||
assert options1.referrerPolicy == $frpNoReferrer | ||
assert options1.redirect == $frFollow | ||
assert options1.referrer == "client".cstring | ||
assert options1.integrity == "".cstring | ||
|
||
block: | ||
let response: Response = newResponse(body = "-. .. --".cstring) | ||
let request: Request = newRequest(url = "http://nim-lang.org".cstring) | ||
|
||
if not defined(nodejs): | ||
block: | ||
proc doFetch(): Future[Response] {.async.} = | ||
fetch "https://httpbin.org/get".cstring | ||
|
||
proc example() {.async.} = | ||
let response: Response = await doFetch() | ||
assert response.ok | ||
assert response.status == 200.cint | ||
assert response.headers is Headers | ||
assert response.body is Body | ||
|
||
discard example() | ||
|
||
when defined(nimExperimentalAsyncjsThen): | ||
block: | ||
proc example2 {.async.} = | ||
await fetch("https://api.github.com/users/torvalds".cstring) | ||
.then((response: Response) => response.json()) | ||
.then((json: JsObject) => console.log(json)) | ||
.catch((err: Error) => console.log("Request Failed", err)) | ||
|
||
discard example2() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
## - `FormData` for the JavaScript target: https://developer.mozilla.org/en-US/docs/Web/API/FormData | ||
when not defined(js): | ||
{.fatal: "Module jsformdata is designed to be used with the JavaScript backend.".} | ||
|
||
type FormData* = ref object of JsRoot ## FormData API. | ||
|
||
func newFormData*(): FormData {.importjs: "new FormData()".} | ||
|
||
func add*(self: FormData; name: cstring; value: SomeNumber | bool | cstring) {.importjs: "#.append(#, #)".} | ||
## https://developer.mozilla.org/en-US/docs/Web/API/FormData/append | ||
## Duplicate keys are allowed and order is preserved. | ||
|
||
func add*(self: FormData; name: cstring; value: SomeNumber | bool | cstring, filename: cstring) {.importjs: "#.append(#, #, #)".} | ||
## https://developer.mozilla.org/en-US/docs/Web/API/FormData/append | ||
## Duplicate keys are allowed and order is preserved. | ||
|
||
func delete*(self: FormData; name: cstring) {.importjs: "#.$1(#)".} | ||
## https://developer.mozilla.org/en-US/docs/Web/API/FormData/delete | ||
## | ||
## .. Warning:: Deletes *all items* with the same key name. | ||
|
||
func getAll*(self: FormData; name: cstring): seq[cstring] {.importjs: "#.$1(#)".} | ||
## https://developer.mozilla.org/en-US/docs/Web/API/FormData/getAll | ||
|
||
func hasKey*(self: FormData; name: cstring): bool {.importjs: "#.has(#)".} | ||
## https://developer.mozilla.org/en-US/docs/Web/API/FormData/has | ||
|
||
func keys*(self: FormData): seq[cstring] {.importjs: "Array.from(#.$1())".} | ||
## https://developer.mozilla.org/en-US/docs/Web/API/FormData/keys | ||
|
||
func values*(self: FormData): seq[cstring] {.importjs: "Array.from(#.$1())".} | ||
## https://developer.mozilla.org/en-US/docs/Web/API/FormData/values | ||
|
||
func pairs*(self: FormData): seq[tuple[key, val: cstring]] {.importjs: "Array.from(#.entries())".} | ||
## https://developer.mozilla.org/en-US/docs/Web/API/FormData/entries | ||
|
||
func put*(self: FormData; name, value, filename: cstring) {.importjs: "#.set(#, #, #)".} | ||
## https://developer.mozilla.org/en-US/docs/Web/API/FormData/set | ||
|
||
func `[]=`*(self: FormData; name, value: cstring) {.importjs: "#.set(#, #)".} | ||
## https://developer.mozilla.org/en-US/docs/Web/API/FormData/set | ||
|
||
func `[]`*(self: FormData; name: cstring): cstring {.importjs: "#.get(#)".} | ||
## https://developer.mozilla.org/en-US/docs/Web/API/FormData/get | ||
|
||
func clear*(self: FormData) {.importjs: | ||
"(() => { const frmdt = #; Array.from(frmdt.keys()).forEach((key) => frmdt.delete(key)) })()".} | ||
## Convenience func to delete all items from `FormData`. | ||
|
||
func toCstring*(self: FormData): cstring {.importjs: "JSON.stringify(#)".} | ||
|
||
func `$`*(self: FormData): string = $toCstring(self) | ||
|
||
func len*(self: FormData): int {.importjs: "Array.from(#.entries()).length".} | ||
|
||
|
||
runnableExamples("-r:off"): | ||
let data: FormData = newFormData() | ||
data["key0"] = "value0".cstring | ||
data.add("key1".cstring, "value1".cstring) | ||
data.delete("key1") | ||
assert data.hasKey("key0") | ||
assert data["key0"] == "value0".cstring | ||
data.clear() | ||
assert data.len == 0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
## - HTTP Headers for the JavaScript target: https://developer.mozilla.org/en-US/docs/Web/API/Headers | ||
when not defined(js): | ||
{.fatal: "Module jsheaders is designed to be used with the JavaScript backend.".} | ||
|
||
type Headers* = ref object of JsRoot ## HTTP Headers API. | ||
|
||
func newHeaders*(): Headers {.importjs: "new Headers()".} | ||
## https://developer.mozilla.org/en-US/docs/Web/API/Headers | ||
|
||
func add*(self: Headers; key: cstring; value: cstring) {.importjs: "#.append(#, #)".} | ||
## Allows duplicated keys. | ||
## https://developer.mozilla.org/en-US/docs/Web/API/Headers/append | ||
|
||
func delete*(self: Headers; key: cstring) {.importjs: "#.$1(#)".} | ||
## https://developer.mozilla.org/en-US/docs/Web/API/Headers/delete | ||
## | ||
## .. Warning:: Delete *all* items with `key` from the headers, including duplicated keys. | ||
|
||
func hasKey*(self: Headers; key: cstring): bool {.importjs: "#.has(#)".} | ||
## https://developer.mozilla.org/en-US/docs/Web/API/Headers/has | ||
|
||
func keys*(self: Headers): seq[cstring] {.importjs: "Array.from(#.$1())".} | ||
## https://developer.mozilla.org/en-US/docs/Web/API/Headers/keys | ||
|
||
func values*(self: Headers): seq[cstring] {.importjs: "Array.from(#.$1())".} | ||
## https://developer.mozilla.org/en-US/docs/Web/API/Headers/values | ||
|
||
func entries*(self: Headers): seq[tuple[key, value: cstring]] {.importjs: "Array.from(#.$1())".} | ||
## https://developer.mozilla.org/en-US/docs/Web/API/Headers/entries | ||
|
||
func `[]`*(self: Headers; key: cstring): cstring {.importjs: "#.get(#)".} | ||
## Get *all* items with `key` from the headers, including duplicated values. | ||
## https://developer.mozilla.org/en-US/docs/Web/API/Headers/get | ||
|
||
func `[]=`*(self: Headers; key: cstring; value: cstring) {.importjs: "#.set(#, #)".} | ||
## Do *not* allow duplicated keys, overwrites duplicated keys. | ||
## https://developer.mozilla.org/en-US/docs/Web/API/Headers/set | ||
|
||
func clear*(self: Headers) {.importjs: | ||
"(() => { const header = #; Array.from(header.keys()).forEach((key) => header.delete(key)) })()".} | ||
## Convenience func to delete all items from `Headers`. | ||
|
||
func toCstring*(self: Headers): cstring {.importjs: "JSON.stringify(Array.from(#.entries()))".} | ||
## Returns a `cstring` representation of `Headers`. | ||
|
||
func `$`*(self: Headers): string = $toCstring(self) | ||
|
||
func len*(self: Headers): int {.importjs: "Array.from(#.entries()).length".} | ||
|
||
|
||
runnableExamples("-r:off"): | ||
|
||
block: | ||
let header: Headers = newHeaders() | ||
header.add("key", "value") | ||
assert header.hasKey("key") | ||
assert header.keys() == @["key".cstring] | ||
assert header.values() == @["value".cstring] | ||
assert header["key"] == "value".cstring | ||
header["other"] = "another".cstring | ||
assert header["other"] == "another".cstring | ||
assert header.entries() == @[("key".cstring, "value".cstring), ("other".cstring, "another".cstring)] | ||
assert header.toCstring() == """[["key","value"],["other","another"]]""".cstring | ||
header.delete("other") | ||
assert header.entries() == @[("key".cstring, "value".cstring)] | ||
header.clear() | ||
assert header.entries() == @[] | ||
assert header.len == 0 | ||
|
||
block: | ||
let header: Headers = newHeaders() | ||
header.add("key", "a") | ||
header.add("key", "b") ## Duplicated. | ||
header.add("key", "c") ## Duplicated. | ||
assert header["key"] == "a, b, c".cstring | ||
header["key"] = "value".cstring | ||
assert header["key"] == "value".cstring | ||
|
||
block: | ||
let header: Headers = newHeaders() | ||
header["key"] = "a" | ||
header["key"] = "b" ## Overwrites. | ||
assert header["key"] == "b".cstring |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters