Skip to content

Commit ecbd921

Browse files
authored
Merge pull request #31 from tinymce/fetch
Fetch support
2 parents 1348f74 + 80433fd commit ecbd921

24 files changed

+1027
-50
lines changed

.gitignore

-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
docs/
55
node_modules/
66
/lib/bs
7-
/lib/js/examples
87
/lib/js/src
98
/lib/js/tests/testHelpers.js
109
npm-debug.log

CHANGELOG.md

+7-6
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
### 1.0 in progress
22

33
Done:
4-
* Change all `send.pipe` externals to `send`, making them "t-first" (#16)
5-
* Remove deprecated APIs
6-
* `Window.getSelection` now returns an option, to better match the definition
7-
* Add scrollToWithOptions to window
8-
* Convert to rescript syntax
9-
* Added IntersectionObserver and IntersectionObserverEntry bindings
4+
* Change all `send.pipe` externals to `send`, making them "t-first" (#8)
5+
* Remove deprecated APIs (#16)
6+
* `Window.getSelection` now returns an option, to better match the definition (#15)
7+
* Added `scrollToWithOptions` to `window` (#26)
8+
* Converted to rescript syntax (#18)
9+
* Added `IntersectionObserver` and `IntersectionObserverEntry` bindings (#27)
10+
* Imported `bs-fetch` and converted it to "t-first" (#31)
1011

1112
Todo:
1213
* Convert more input types to `node_like` and `element_like` to improve usability

LICENSE

+4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
MIT License
22

33
Copyright (c) 2021 Tiny Technologies Inc (https://www.tiny.cloud/)
4+
Forked from https://github.com/reasonml-community/bs-webapi-incubator:
5+
Copyright (c) 2017 reasonml-community
6+
Forked from https://github.com/reasonml-community/bs-fetch:
7+
Copyright (c) 2017
48

59
Permission is hereby granted, free of charge, to any person obtaining a copy
610
of this software and associated documentation files (the "Software"), to deal

bsconfig.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
"number": "+A-105",
2121
"error": "+A"
2222
},
23-
"bs-dependencies": [
24-
"bs-fetch"
23+
"bs-dev-dependencies": [
24+
"@ryyppy/rescript-promise"
2525
]
2626
}

examples/fetch_examples.res

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
open Webapi
2+
open Promise
3+
4+
let _ = {
5+
Fetch.fetch("/api/hellos/1")
6+
->then(Fetch.Response.text)
7+
->then(text => print_endline(text)->resolve)
8+
}
9+
10+
let _ = {
11+
Fetch.fetchWithInit("/api/hello", Fetch.RequestInit.make(~method_=Post, ()))
12+
->then(Fetch.Response.text)
13+
->then(text => print_endline(text)->resolve)
14+
}
15+
16+
let _ = {
17+
Fetch.fetch("/api/fruit")
18+
/* assume server returns `["apple", "banana", "pear", ...]` */
19+
->then(Fetch.Response.json)
20+
->then(json => Js.Json.decodeArray(json)->resolve)
21+
->then(opt => Belt.Option.getExn(opt)->resolve)
22+
->then(items =>
23+
items->Js.Array2.map(item => item->Js.Json.decodeString->Belt.Option.getExn)->resolve
24+
)
25+
}
26+
27+
/* makes a post request with the following json payload { hello: "world" } */
28+
let _ = {
29+
let payload = Js.Dict.empty()
30+
Js.Dict.set(payload, "hello", Js.Json.string("world"))
31+
Fetch.fetchWithInit(
32+
"/api/hello",
33+
Fetch.RequestInit.make(
34+
~method_=Post,
35+
~body=Fetch.BodyInit.make(Js.Json.stringify(Js.Json.object_(payload))),
36+
~headers=Fetch.HeadersInit.make({"Content-Type": "application/json"}),
37+
(),
38+
),
39+
)->then(Fetch.Response.json)
40+
}
41+
42+
let _ = {
43+
let formData = Webapi.FormData.make()
44+
Webapi.FormData.appendObject(
45+
formData,
46+
"image0",
47+
{"type": "image/jpg", "uri": "path/to/it", "name": "image0.jpg"},
48+
(),
49+
)
50+
51+
Fetch.fetchWithInit(
52+
"/api/upload",
53+
Fetch.RequestInit.make(
54+
~method_=Post,
55+
~body=Fetch.BodyInit.makeWithFormData(formData),
56+
~headers=Fetch.HeadersInit.make({"Accept": "*"}),
57+
(),
58+
),
59+
)->then(Fetch.Response.json)
60+
}
61+
62+
let _ = {
63+
let controller = Fetch.AbortController.make()
64+
65+
let _ = Fetch.fetchWithInit("/api/fruit", Fetch.RequestInit.make(~signal=controller.signal, ()))
66+
67+
controller->Fetch.AbortController.abort
68+
}

lib/js/examples/dom_example.js

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
'use strict';
2+
3+
var Belt_Option = require("rescript/lib/js/belt_Option.js");
4+
var Caml_option = require("rescript/lib/js/caml_option.js");
5+
var Webapi__Dom__Element = require("../src/Webapi/Dom/Webapi__Dom__Element.js");
6+
var Webapi__Dom__Document = require("../src/Webapi/Dom/Webapi__Dom__Document.js");
7+
8+
function unwrapUnsafely(x) {
9+
if (x !== undefined) {
10+
return Caml_option.valFromOption(x);
11+
}
12+
throw {
13+
RE_EXN_ID: "Invalid_argument",
14+
_1: "Passed `None` to unwrapUnsafely",
15+
Error: new Error()
16+
};
17+
}
18+
19+
document.createElement("div").className;
20+
21+
Belt_Option.map(Caml_option.nullable_to_opt(document.createElement("div").nextElementSibling), (function (prim) {
22+
return prim.innerText;
23+
}));
24+
25+
Belt_Option.map(Caml_option.nullable_to_opt(document.createElement("div").parentElement), (function (prim) {
26+
return prim.innerText;
27+
}));
28+
29+
var el = unwrapUnsafely(Webapi__Dom__Element.asHtmlElement(document.createElement("div")));
30+
31+
Belt_Option.map(Belt_Option.flatMap(Webapi__Dom__Document.asHtmlDocument(document), (function (prim) {
32+
return Caml_option.nullable_to_opt(prim.body);
33+
})), (function (body) {
34+
body.appendChild(el);
35+
36+
}));
37+
38+
document.createElement("div").addEventListener("mousemove", (function (e) {
39+
console.log([
40+
e.screenX,
41+
e.screenY
42+
]);
43+
44+
}));
45+
46+
exports.unwrapUnsafely = unwrapUnsafely;
47+
exports.el = el;
48+
/* Not a pure module */

lib/js/examples/fetch_examples.js

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
'use strict';
2+
3+
var Js_json = require("rescript/lib/js/js_json.js");
4+
var Belt_Option = require("rescript/lib/js/belt_Option.js");
5+
var Caml_option = require("rescript/lib/js/caml_option.js");
6+
var Webapi__Fetch = require("../src/Webapi/Webapi__Fetch.js");
7+
8+
fetch("/api/hellos/1").then(function (prim) {
9+
return prim.text();
10+
}).then(function (text) {
11+
return Promise.resolve((console.log(text), undefined));
12+
});
13+
14+
fetch("/api/hello", Webapi__Fetch.RequestInit.make(/* Post */2, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined)(undefined)).then(function (prim) {
15+
return prim.text();
16+
}).then(function (text) {
17+
return Promise.resolve((console.log(text), undefined));
18+
});
19+
20+
fetch("/api/fruit").then(function (prim) {
21+
return prim.json();
22+
}).then(function (json) {
23+
return Promise.resolve(Js_json.decodeArray(json));
24+
}).then(function (opt) {
25+
return Promise.resolve(Belt_Option.getExn(opt));
26+
}).then(function (items) {
27+
return Promise.resolve(items.map(function (item) {
28+
return Belt_Option.getExn(Js_json.decodeString(item));
29+
}));
30+
});
31+
32+
var payload = {};
33+
34+
payload["hello"] = "world";
35+
36+
fetch("/api/hello", Webapi__Fetch.RequestInit.make(/* Post */2, {
37+
"Content-Type": "application/json"
38+
}, Caml_option.some(JSON.stringify(payload)), undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined)(undefined)).then(function (prim) {
39+
return prim.json();
40+
});
41+
42+
var formData = new FormData();
43+
44+
formData.append("image0", {
45+
type: "image/jpg",
46+
uri: "path/to/it",
47+
name: "image0.jpg"
48+
}, undefined);
49+
50+
fetch("/api/upload", Webapi__Fetch.RequestInit.make(/* Post */2, {
51+
Accept: "*"
52+
}, Caml_option.some(formData), undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined)(undefined)).then(function (prim) {
53+
return prim.json();
54+
});
55+
56+
var controller = new AbortController();
57+
58+
fetch("/api/fruit", Webapi__Fetch.RequestInit.make(undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, Caml_option.some(controller.signal))(undefined));
59+
60+
controller.abort();
61+
62+
/* Not a pure module */

package.json

+1-3
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,7 @@
2929
"author": "Tiny Technologies Inc",
3030
"license": "MIT",
3131
"devDependencies": {
32+
"@ryyppy/rescript-promise": "^2.1.0",
3233
"rescript": "^9.1.4"
33-
},
34-
"dependencies": {
35-
"bs-fetch": "^0.6.2"
3634
}
3735
}

src/Webapi.res

+3-16
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,12 @@
11
module Base64 = Webapi__Base64
2-
3-
/** @since 0.18.0 */
42
module Blob = Webapi__Blob
53
module Canvas = Webapi__Canvas
64
module Dom = Webapi__Dom
75
module File = Webapi__File
6+
module Fetch = Webapi__Fetch
7+
module FormData = Webapi__FormData
88

9-
/** Re-export from [bs-fetch] for convenience. To use, you will also
10-
need to add the [bs-fetch] package as a dependency.
11-
12-
To get the [FormData] of an HTML form, use
13-
[Webapi.Dom.HtmlFormElement.data].
14-
15-
@since 0.18.0 */
16-
module FormData = Fetch.FormData
17-
18-
/** Re-export from [bs-fetch] for convenience. See also
19-
{!module:FormData}.
20-
21-
@since 0.18.0 */
22-
module Iterator = FormData.Iterator
9+
module Iterator = Webapi__Iterator
2310

2411
module Performance = Webapi__Performance
2512

src/Webapi/Dom/Webapi__Dom__HtmlFormElement.res

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ module Impl = (
3636
@send external checkValidity: t_htmlFormElement => bool = ""
3737
@send external reportValidity: t_htmlFormElement => bool = ""
3838

39-
/** @since 0.18.0 */ @new external data: T.t => Fetch.FormData.t = "FormData"
39+
/** @since 0.18.0 */ @new external data: T.t => Webapi__FormData.t = "FormData"
4040
}
4141

4242
type t = Dom.htmlFormElement
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
type signal
2+
type t = {signal: signal}
3+
4+
@new external make: unit => t = "AbortController"
5+
@send external abort: t => unit = "abort"
6+
7+
@ocaml.doc("For compatibility. Prefer the record field.")
8+
@get external signal: t => signal = "signal"

src/Webapi/Webapi__Blob.res

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ module Impl = (
1616
@get external type_: T.t => string = "type"
1717
}
1818

19-
type t = Fetch.blob
19+
type t
2020

2121
include Impl({
2222
type t = t

0 commit comments

Comments
 (0)