@gershy/util-http 0.0.1 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/cmp/cjs/main.d.ts CHANGED
@@ -1,4 +1,58 @@
1
1
  import '../sideEffects.js';
2
- import '@gershy/clearing';
3
- declare const _default: null;
2
+ export type NetProc = {
3
+ proto: 'ws' | 'wss' | 'http' | 'https';
4
+ addr: string;
5
+ port: number;
6
+ };
7
+ type Built<T> = T | {
8
+ [k: string]: Built<T>;
9
+ };
10
+ export type HttpReq = {
11
+ path: string[];
12
+ method: 'head' | 'get' | 'post' | 'put' | 'patch' | 'delete' | 'sokt';
13
+ cookies: Obj<Built<string>>;
14
+ query?: Obj<Built<string>>;
15
+ body?: Json;
16
+ };
17
+ export type HttpRes = {
18
+ code: number;
19
+ headers?: {
20
+ [key: string]: string | string[];
21
+ };
22
+ body: Json | Buffer;
23
+ };
24
+ export type HttpArgs<Req extends HttpReq, Res extends HttpRes> = {} & {
25
+ $req: Req;
26
+ $res: Res;
27
+ } & {
28
+ netProc: NetProc;
29
+ } & Omit<Req, 'cookies'> & {
30
+ headers?: Obj<string>;
31
+ };
32
+ export type HttpResult<Res extends HttpRes> = {
33
+ reqArgs: {
34
+ url: string;
35
+ method: string;
36
+ headers: [string, string][];
37
+ body: string | null;
38
+ };
39
+ code: number;
40
+ body: Res['body'];
41
+ };
42
+ export type HttpRet<Res extends HttpRes> = Promise<HttpResult<Res>> & {
43
+ end: () => void;
44
+ };
45
+ declare const _default: <Args extends HttpArgs<HttpReq, HttpRes>>(args: Args, params: Pick<Args, "query" | "body">) => Promise<{
46
+ reqArgs: ({
47
+ method: "HEAD" | "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "SOKT";
48
+ headers: [string, string][];
49
+ body: string | null;
50
+ } & {
51
+ url: string;
52
+ });
53
+ code: number;
54
+ body: Args["$res"]["body"];
55
+ }> & {
56
+ end: () => void;
57
+ };
4
58
  export default _default;
package/cmp/cjs/main.js CHANGED
@@ -1,4 +1,65 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- require("@gershy/clearing");
4
- exports.default = null;
3
+ const clearing_1 = require("@gershy/clearing");
4
+ exports.default = (args, params) => {
5
+ // Note this function is sovereign - can't reference jargon/http for `formatNetProc` :(
6
+ const { netProc, path, headers = {} } = args;
7
+ const { query = {}, body: reqBody = null } = params;
8
+ const defPorts = { http: 80, https: 443 };
9
+ const url = [
10
+ // E.g. "http://pasta.com"
11
+ `${netProc.proto}:/${''}/${netProc.addr}`,
12
+ // E.g. "http://pasta.com:3000"
13
+ netProc.port !== defPorts[netProc.proto] ? `:${netProc.port.toString(10)}` : null,
14
+ // E.g. "http://pasta.com:3000/path/to/resource"
15
+ path ? `/${path.join('/')}` : null,
16
+ (() => {
17
+ if (query[empty]())
18
+ return null;
19
+ const chains = function* (val, chain = []) {
20
+ if (!(0, clearing_1.isCls)(val, Object))
21
+ return yield { chain, val };
22
+ for (const [k, v] of val)
23
+ yield* chains(v, [...chain, k]);
24
+ };
25
+ // E.g. "http://pasta.com:3000/path/to/resource?query=spaghetti&offset=10"
26
+ return [...chains(query)][map](({ chain, val }) => `${encodeURIComponent(chain.join('.'))}=${encodeURIComponent(val)}`)
27
+ .join('&');
28
+ })()
29
+ ].filter(Boolean).join('');
30
+ const reqArgs = {
31
+ method: args.method[upper](),
32
+ headers: headers[toArr]((v, k) => [k.replace(/([A-Z])/g, '-$1')[lower](), v]), // Avoid `camelCase` util - want to keep this sovereign
33
+ body: [Object, Array].some(C => (0, clearing_1.isCls)(reqBody, C)) ? JSON.stringify(reqBody) : reqBody !== null ? `${reqBody}` : null
34
+ };
35
+ const abort = new AbortController();
36
+ const prm = fetch(url, { ...reqArgs, signal: abort.signal }).then(async (res) => {
37
+ const resBody = await (async () => {
38
+ const t = await res.text();
39
+ try {
40
+ return JSON.parse(t);
41
+ }
42
+ catch (err) { }
43
+ return t;
44
+ })();
45
+ const http = {
46
+ reqArgs: Object.assign(reqArgs, { url, body: reqBody }),
47
+ code: res.status,
48
+ body: resBody
49
+ };
50
+ if (res.status >= 500)
51
+ throw Error('http glitch')[mod](http);
52
+ if (res.status >= 400)
53
+ throw Error('http reject')[mod](http);
54
+ return http; // TODO: Return something like `{ ...http.body, http: { status: res.status } }`? Works as long as `http.body` is Json and not a Buffer
55
+ }, err => {
56
+ while ((0, clearing_1.isCls)(err.cause, Error))
57
+ err = err.cause; // `fetch` natively wraps errors - pretty annoying; unwrap them
58
+ if (err.code === 'ENOTFOUND')
59
+ return err[fire]({ retry: false });
60
+ throw err;
61
+ });
62
+ // Note that fetch abortion errors are suppressed!! By default we short-circuit any logic
63
+ // which depended on the http return value.
64
+ return Object.assign(prm, { end: () => abort.abort(Error('fetch aborted')[suppress]()) });
65
+ };
package/cmp/mjs/main.d.ts CHANGED
@@ -1,4 +1,58 @@
1
1
  import '../sideEffects.js';
2
- import '@gershy/clearing';
3
- declare const _default: null;
2
+ export type NetProc = {
3
+ proto: 'ws' | 'wss' | 'http' | 'https';
4
+ addr: string;
5
+ port: number;
6
+ };
7
+ type Built<T> = T | {
8
+ [k: string]: Built<T>;
9
+ };
10
+ export type HttpReq = {
11
+ path: string[];
12
+ method: 'head' | 'get' | 'post' | 'put' | 'patch' | 'delete' | 'sokt';
13
+ cookies: Obj<Built<string>>;
14
+ query?: Obj<Built<string>>;
15
+ body?: Json;
16
+ };
17
+ export type HttpRes = {
18
+ code: number;
19
+ headers?: {
20
+ [key: string]: string | string[];
21
+ };
22
+ body: Json | Buffer;
23
+ };
24
+ export type HttpArgs<Req extends HttpReq, Res extends HttpRes> = {} & {
25
+ $req: Req;
26
+ $res: Res;
27
+ } & {
28
+ netProc: NetProc;
29
+ } & Omit<Req, 'cookies'> & {
30
+ headers?: Obj<string>;
31
+ };
32
+ export type HttpResult<Res extends HttpRes> = {
33
+ reqArgs: {
34
+ url: string;
35
+ method: string;
36
+ headers: [string, string][];
37
+ body: string | null;
38
+ };
39
+ code: number;
40
+ body: Res['body'];
41
+ };
42
+ export type HttpRet<Res extends HttpRes> = Promise<HttpResult<Res>> & {
43
+ end: () => void;
44
+ };
45
+ declare const _default: <Args extends HttpArgs<HttpReq, HttpRes>>(args: Args, params: Pick<Args, "query" | "body">) => Promise<{
46
+ reqArgs: ({
47
+ method: "HEAD" | "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "SOKT";
48
+ headers: [string, string][];
49
+ body: string | null;
50
+ } & {
51
+ url: string;
52
+ });
53
+ code: number;
54
+ body: Args["$res"]["body"];
55
+ }> & {
56
+ end: () => void;
57
+ };
4
58
  export default _default;
package/cmp/mjs/main.js CHANGED
@@ -1,2 +1,63 @@
1
- import '@gershy/clearing';
2
- export default null;
1
+ import { isCls } from '@gershy/clearing';
2
+ export default (args, params) => {
3
+ // Note this function is sovereign - can't reference jargon/http for `formatNetProc` :(
4
+ const { netProc, path, headers = {} } = args;
5
+ const { query = {}, body: reqBody = null } = params;
6
+ const defPorts = { http: 80, https: 443 };
7
+ const url = [
8
+ // E.g. "http://pasta.com"
9
+ `${netProc.proto}:/${''}/${netProc.addr}`,
10
+ // E.g. "http://pasta.com:3000"
11
+ netProc.port !== defPorts[netProc.proto] ? `:${netProc.port.toString(10)}` : null,
12
+ // E.g. "http://pasta.com:3000/path/to/resource"
13
+ path ? `/${path.join('/')}` : null,
14
+ (() => {
15
+ if (query[empty]())
16
+ return null;
17
+ const chains = function* (val, chain = []) {
18
+ if (!isCls(val, Object))
19
+ return yield { chain, val };
20
+ for (const [k, v] of val)
21
+ yield* chains(v, [...chain, k]);
22
+ };
23
+ // E.g. "http://pasta.com:3000/path/to/resource?query=spaghetti&offset=10"
24
+ return [...chains(query)][map](({ chain, val }) => `${encodeURIComponent(chain.join('.'))}=${encodeURIComponent(val)}`)
25
+ .join('&');
26
+ })()
27
+ ].filter(Boolean).join('');
28
+ const reqArgs = {
29
+ method: args.method[upper](),
30
+ headers: headers[toArr]((v, k) => [k.replace(/([A-Z])/g, '-$1')[lower](), v]), // Avoid `camelCase` util - want to keep this sovereign
31
+ body: [Object, Array].some(C => isCls(reqBody, C)) ? JSON.stringify(reqBody) : reqBody !== null ? `${reqBody}` : null
32
+ };
33
+ const abort = new AbortController();
34
+ const prm = fetch(url, { ...reqArgs, signal: abort.signal }).then(async (res) => {
35
+ const resBody = await (async () => {
36
+ const t = await res.text();
37
+ try {
38
+ return JSON.parse(t);
39
+ }
40
+ catch (err) { }
41
+ return t;
42
+ })();
43
+ const http = {
44
+ reqArgs: Object.assign(reqArgs, { url, body: reqBody }),
45
+ code: res.status,
46
+ body: resBody
47
+ };
48
+ if (res.status >= 500)
49
+ throw Error('http glitch')[mod](http);
50
+ if (res.status >= 400)
51
+ throw Error('http reject')[mod](http);
52
+ return http; // TODO: Return something like `{ ...http.body, http: { status: res.status } }`? Works as long as `http.body` is Json and not a Buffer
53
+ }, err => {
54
+ while (isCls(err.cause, Error))
55
+ err = err.cause; // `fetch` natively wraps errors - pretty annoying; unwrap them
56
+ if (err.code === 'ENOTFOUND')
57
+ return err[fire]({ retry: false });
58
+ throw err;
59
+ });
60
+ // Note that fetch abortion errors are suppressed!! By default we short-circuit any logic
61
+ // which depended on the http return value.
62
+ return Object.assign(prm, { end: () => abort.abort(Error('fetch aborted')[suppress]()) });
63
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gershy/util-http",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "description": "TODO",
5
5
  "keywords": [
6
6
  "TODO"
@@ -16,7 +16,7 @@
16
16
  "homepage": "https://github.com/gershy/utilHttp#readme",
17
17
  "license": "ISC",
18
18
  "peerDependencies": {
19
- "@gershy/clearing": "^0.0.24"
19
+ "@gershy/clearing": "^0.0.28"
20
20
  },
21
21
  "devDependencies": {
22
22
  "@types/node": "^24.10.1",