http.cjs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  1. 'use strict';
  2. var tauri = require('./helpers/tauri.cjs');
  3. // Copyright 2019-2023 Tauri Programme within The Commons Conservancy
  4. // SPDX-License-Identifier: Apache-2.0
  5. // SPDX-License-Identifier: MIT
  6. /**
  7. * Access the HTTP client written in Rust.
  8. *
  9. * This package is also accessible with `window.__TAURI__.http` when [`build.withGlobalTauri`](https://tauri.app/v1/api/config/#buildconfig.withglobaltauri) in `tauri.conf.json` is set to `true`.
  10. *
  11. * The APIs must be allowlisted on `tauri.conf.json`:
  12. * ```json
  13. * {
  14. * "tauri": {
  15. * "allowlist": {
  16. * "http": {
  17. * "all": true, // enable all http APIs
  18. * "request": true // enable HTTP request API
  19. * }
  20. * }
  21. * }
  22. * }
  23. * ```
  24. * It is recommended to allowlist only the APIs you use for optimal bundle size and security.
  25. *
  26. * ## Security
  27. *
  28. * This API has a scope configuration that forces you to restrict the URLs and paths that can be accessed using glob patterns.
  29. *
  30. * For instance, this scope configuration only allows making HTTP requests to the GitHub API for the `tauri-apps` organization:
  31. * ```json
  32. * {
  33. * "tauri": {
  34. * "allowlist": {
  35. * "http": {
  36. * "scope": ["https://api.github.com/repos/tauri-apps/*"]
  37. * }
  38. * }
  39. * }
  40. * }
  41. * ```
  42. * Trying to execute any API with a URL not configured on the scope results in a promise rejection due to denied access.
  43. *
  44. * @module
  45. */
  46. /**
  47. * @since 1.0.0
  48. */
  49. exports.ResponseType = void 0;
  50. (function (ResponseType) {
  51. ResponseType[ResponseType["JSON"] = 1] = "JSON";
  52. ResponseType[ResponseType["Text"] = 2] = "Text";
  53. ResponseType[ResponseType["Binary"] = 3] = "Binary";
  54. })(exports.ResponseType || (exports.ResponseType = {}));
  55. async function formBody(data) {
  56. const form = {};
  57. const append = async (key, v) => {
  58. if (v !== null) {
  59. let r;
  60. if (typeof v === 'string') {
  61. r = v;
  62. }
  63. else if (v instanceof Uint8Array || Array.isArray(v)) {
  64. r = Array.from(v);
  65. }
  66. else if (v instanceof File) {
  67. r = {
  68. file: Array.from(new Uint8Array(await v.arrayBuffer())),
  69. mime: v.type,
  70. fileName: v.name
  71. };
  72. }
  73. else if (typeof v.file === 'string') {
  74. r = { file: v.file, mime: v.mime, fileName: v.fileName };
  75. }
  76. else {
  77. r = { file: Array.from(v.file), mime: v.mime, fileName: v.fileName };
  78. }
  79. form[String(key)] = r;
  80. }
  81. };
  82. if (data instanceof FormData) {
  83. for (const [key, value] of data) {
  84. await append(key, value);
  85. }
  86. }
  87. else {
  88. for (const [key, value] of Object.entries(data)) {
  89. await append(key, value);
  90. }
  91. }
  92. return form;
  93. }
  94. /**
  95. * The body object to be used on POST and PUT requests.
  96. *
  97. * @since 1.0.0
  98. */
  99. class Body {
  100. /** @ignore */
  101. constructor(type, payload) {
  102. this.type = type;
  103. this.payload = payload;
  104. }
  105. /**
  106. * Creates a new form data body. The form data is an object where each key is the entry name,
  107. * and the value is either a string or a file object.
  108. *
  109. * By default it sets the `application/x-www-form-urlencoded` Content-Type header,
  110. * but you can set it to `multipart/form-data` if the Cargo feature `http-multipart` is enabled.
  111. *
  112. * Note that a file path must be allowed in the `fs` allowlist scope.
  113. *
  114. * @example
  115. * ```typescript
  116. * import { Body } from "@tauri-apps/api/http"
  117. * const body = Body.form({
  118. * key: 'value',
  119. * image: {
  120. * file: '/path/to/file', // either a path or an array buffer of the file contents
  121. * mime: 'image/jpeg', // optional
  122. * fileName: 'image.jpg' // optional
  123. * }
  124. * });
  125. *
  126. * // alternatively, use a FormData:
  127. * const form = new FormData();
  128. * form.append('key', 'value');
  129. * form.append('image', file, 'image.png');
  130. * const formBody = Body.form(form);
  131. * ```
  132. *
  133. * @param data The body data.
  134. *
  135. * @returns The body object ready to be used on the POST and PUT requests.
  136. */
  137. static form(data) {
  138. return new Body('Form', data);
  139. }
  140. /**
  141. * Creates a new JSON body.
  142. * @example
  143. * ```typescript
  144. * import { Body } from "@tauri-apps/api/http"
  145. * Body.json({
  146. * registered: true,
  147. * name: 'tauri'
  148. * });
  149. * ```
  150. *
  151. * @param data The body JSON object.
  152. *
  153. * @returns The body object ready to be used on the POST and PUT requests.
  154. */
  155. static json(data) {
  156. return new Body('Json', data);
  157. }
  158. /**
  159. * Creates a new UTF-8 string body.
  160. * @example
  161. * ```typescript
  162. * import { Body } from "@tauri-apps/api/http"
  163. * Body.text('The body content as a string');
  164. * ```
  165. *
  166. * @param value The body string.
  167. *
  168. * @returns The body object ready to be used on the POST and PUT requests.
  169. */
  170. static text(value) {
  171. return new Body('Text', value);
  172. }
  173. /**
  174. * Creates a new byte array body.
  175. * @example
  176. * ```typescript
  177. * import { Body } from "@tauri-apps/api/http"
  178. * Body.bytes(new Uint8Array([1, 2, 3]));
  179. * ```
  180. *
  181. * @param bytes The body byte array.
  182. *
  183. * @returns The body object ready to be used on the POST and PUT requests.
  184. */
  185. static bytes(bytes) {
  186. // stringifying Uint8Array doesn't return an array of numbers, so we create one here
  187. return new Body('Bytes', Array.from(bytes instanceof ArrayBuffer ? new Uint8Array(bytes) : bytes));
  188. }
  189. }
  190. /**
  191. * Response object.
  192. *
  193. * @since 1.0.0
  194. * */
  195. class Response {
  196. /** @ignore */
  197. constructor(response) {
  198. this.url = response.url;
  199. this.status = response.status;
  200. this.ok = this.status >= 200 && this.status < 300;
  201. this.headers = response.headers;
  202. this.rawHeaders = response.rawHeaders;
  203. this.data = response.data;
  204. }
  205. }
  206. /**
  207. * @since 1.0.0
  208. */
  209. class Client {
  210. /** @ignore */
  211. constructor(id) {
  212. this.id = id;
  213. }
  214. /**
  215. * Drops the client instance.
  216. * @example
  217. * ```typescript
  218. * import { getClient } from '@tauri-apps/api/http';
  219. * const client = await getClient();
  220. * await client.drop();
  221. * ```
  222. */
  223. async drop() {
  224. return tauri.invokeTauriCommand({
  225. __tauriModule: 'Http',
  226. message: {
  227. cmd: 'dropClient',
  228. client: this.id
  229. }
  230. });
  231. }
  232. /**
  233. * Makes an HTTP request.
  234. * @example
  235. * ```typescript
  236. * import { getClient } from '@tauri-apps/api/http';
  237. * const client = await getClient();
  238. * const response = await client.request({
  239. * method: 'GET',
  240. * url: 'http://localhost:3003/users',
  241. * });
  242. * ```
  243. */
  244. async request(options) {
  245. var _a;
  246. const jsonResponse = !options.responseType || options.responseType === exports.ResponseType.JSON;
  247. if (jsonResponse) {
  248. options.responseType = exports.ResponseType.Text;
  249. }
  250. if (((_a = options.body) === null || _a === void 0 ? void 0 : _a.type) === 'Form') {
  251. options.body.payload = await formBody(options.body.payload);
  252. }
  253. return tauri.invokeTauriCommand({
  254. __tauriModule: 'Http',
  255. message: {
  256. cmd: 'httpRequest',
  257. client: this.id,
  258. options
  259. }
  260. }).then((res) => {
  261. const response = new Response(res);
  262. if (jsonResponse) {
  263. /* eslint-disable */
  264. try {
  265. response.data = JSON.parse(response.data);
  266. }
  267. catch (e) {
  268. if (response.ok && response.data === '') {
  269. response.data = {};
  270. }
  271. else if (response.ok) {
  272. throw Error(`Failed to parse response \`${response.data}\` as JSON: ${e};
  273. try setting the \`responseType\` option to \`ResponseType.Text\` or \`ResponseType.Binary\` if the API does not return a JSON response.`);
  274. }
  275. }
  276. /* eslint-enable */
  277. return response;
  278. }
  279. return response;
  280. });
  281. }
  282. /**
  283. * Makes a GET request.
  284. * @example
  285. * ```typescript
  286. * import { getClient, ResponseType } from '@tauri-apps/api/http';
  287. * const client = await getClient();
  288. * const response = await client.get('http://localhost:3003/users', {
  289. * timeout: 30,
  290. * // the expected response type
  291. * responseType: ResponseType.JSON
  292. * });
  293. * ```
  294. */
  295. async get(url, options) {
  296. return this.request({
  297. method: 'GET',
  298. url,
  299. ...options
  300. });
  301. }
  302. /**
  303. * Makes a POST request.
  304. * @example
  305. * ```typescript
  306. * import { getClient, Body, ResponseType } from '@tauri-apps/api/http';
  307. * const client = await getClient();
  308. * const response = await client.post('http://localhost:3003/users', {
  309. * body: Body.json({
  310. * name: 'tauri',
  311. * password: 'awesome'
  312. * }),
  313. * // in this case the server returns a simple string
  314. * responseType: ResponseType.Text,
  315. * });
  316. * ```
  317. */
  318. async post(url, body, options) {
  319. return this.request({
  320. method: 'POST',
  321. url,
  322. body,
  323. ...options
  324. });
  325. }
  326. /**
  327. * Makes a PUT request.
  328. * @example
  329. * ```typescript
  330. * import { getClient, Body } from '@tauri-apps/api/http';
  331. * const client = await getClient();
  332. * const response = await client.put('http://localhost:3003/users/1', {
  333. * body: Body.form({
  334. * file: {
  335. * file: '/home/tauri/avatar.png',
  336. * mime: 'image/png',
  337. * fileName: 'avatar.png'
  338. * }
  339. * })
  340. * });
  341. * ```
  342. */
  343. async put(url, body, options) {
  344. return this.request({
  345. method: 'PUT',
  346. url,
  347. body,
  348. ...options
  349. });
  350. }
  351. /**
  352. * Makes a PATCH request.
  353. * @example
  354. * ```typescript
  355. * import { getClient, Body } from '@tauri-apps/api/http';
  356. * const client = await getClient();
  357. * const response = await client.patch('http://localhost:3003/users/1', {
  358. * body: Body.json({ email: 'contact@tauri.app' })
  359. * });
  360. * ```
  361. */
  362. async patch(url, options) {
  363. return this.request({
  364. method: 'PATCH',
  365. url,
  366. ...options
  367. });
  368. }
  369. /**
  370. * Makes a DELETE request.
  371. * @example
  372. * ```typescript
  373. * import { getClient } from '@tauri-apps/api/http';
  374. * const client = await getClient();
  375. * const response = await client.delete('http://localhost:3003/users/1');
  376. * ```
  377. */
  378. async delete(url, options) {
  379. return this.request({
  380. method: 'DELETE',
  381. url,
  382. ...options
  383. });
  384. }
  385. }
  386. /**
  387. * Creates a new client using the specified options.
  388. * @example
  389. * ```typescript
  390. * import { getClient } from '@tauri-apps/api/http';
  391. * const client = await getClient();
  392. * ```
  393. *
  394. * @param options Client configuration.
  395. *
  396. * @returns A promise resolving to the client instance.
  397. *
  398. * @since 1.0.0
  399. */
  400. async function getClient(options) {
  401. return tauri.invokeTauriCommand({
  402. __tauriModule: 'Http',
  403. message: {
  404. cmd: 'createClient',
  405. options
  406. }
  407. }).then((id) => new Client(id));
  408. }
  409. /** @internal */
  410. let defaultClient = null;
  411. /**
  412. * Perform an HTTP request using the default client.
  413. * @example
  414. * ```typescript
  415. * import { fetch } from '@tauri-apps/api/http';
  416. * const response = await fetch('http://localhost:3003/users/2', {
  417. * method: 'GET',
  418. * timeout: 30,
  419. * });
  420. * ```
  421. */
  422. async function fetch(url, options) {
  423. var _a;
  424. if (defaultClient === null) {
  425. defaultClient = await getClient();
  426. }
  427. return defaultClient.request({
  428. url,
  429. method: (_a = options === null || options === void 0 ? void 0 : options.method) !== null && _a !== void 0 ? _a : 'GET',
  430. ...options
  431. });
  432. }
  433. exports.Body = Body;
  434. exports.Client = Client;
  435. exports.Response = Response;
  436. exports.fetch = fetch;
  437. exports.getClient = getClient;