diff options
author | Juan Carlos <juancarlospaco@gmail.com> | 2021-03-07 01:57:16 -0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-03-06 20:57:16 -0800 |
commit | b8c04bdb929f72ed8214122b255e88db9e713774 (patch) | |
tree | 4dfd2120bd980f3c6dad43710a1f7d1490715bf6 /lib/std | |
parent | 07df4fe560850b036e84750aced746daa58e2427 (diff) | |
download | Nim-b8c04bdb929f72ed8214122b255e88db9e713774.tar.gz |
Add module jsfetch (#12531)
* 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>
Diffstat (limited to 'lib/std')
-rw-r--r-- | lib/std/jsfetch.nim | 198 | ||||
-rw-r--r-- | lib/std/jsformdata.nim | 65 | ||||
-rw-r--r-- | lib/std/jsheaders.nim | 83 |
3 files changed, 346 insertions, 0 deletions
diff --git a/lib/std/jsfetch.nim b/lib/std/jsfetch.nim new file mode 100644 index 000000000..2ff894985 --- /dev/null +++ b/lib/std/jsfetch.nim @@ -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() diff --git a/lib/std/jsformdata.nim b/lib/std/jsformdata.nim new file mode 100644 index 000000000..2728fbdd0 --- /dev/null +++ b/lib/std/jsformdata.nim @@ -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 diff --git a/lib/std/jsheaders.nim b/lib/std/jsheaders.nim new file mode 100644 index 000000000..a67e54ef7 --- /dev/null +++ b/lib/std/jsheaders.nim @@ -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 |