@webqit/fetch-plus 0.1.1

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/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@webqit/fetch-plus",
3
+ "publishConfig": {
4
+ "access": "public"
5
+ },
6
+ "title": "Fetch+",
7
+ "description": "Upgraded fetch and the LiveResponse API",
8
+ "keywords": [
9
+ "fetch",
10
+ "LiveResponse"
11
+ ],
12
+ "homepage": "https://fetch-plus.netlify.app/",
13
+ "icon": "https://webqit.io/icon.svg",
14
+ "version": "0.1.1",
15
+ "license": "MIT",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/webqit/fetch-plus.git"
19
+ },
20
+ "bugs": {
21
+ "url": "https://github.com/webqit/fetch-plus/issues"
22
+ },
23
+ "type": "module",
24
+ "exports": {
25
+ ".": "./src/index.js"
26
+ },
27
+ "scripts": {
28
+ "test": "mocha --extension .test.js --recursive --timeout 5000 --exit",
29
+ "build": "esbuild main=src/index.browser.js --bundle --minify --sourcemap --outdir=dist",
30
+ "preversion": "npm run test && npm run build && git add -A dist",
31
+ "postversion": "git push && git push --tags",
32
+ "version:next": "npm version prerelease --preid=next"
33
+ },
34
+ "dependencies": {
35
+ "@webqit/util": "^0.8.16"
36
+ },
37
+ "peerDependencies": {
38
+ "@webqit/observer": "^3.8.14",
39
+ "@webqit/port-plus": "^0.1.6"
40
+ },
41
+ "devDependencies": {
42
+ "@webqit/observer": "^3.8.14",
43
+ "@webqit/port-plus": "^0.1.6",
44
+ "chai": "^4.3.4",
45
+ "chai-as-promised": "^7.1.1",
46
+ "esbuild": "^0.20.2",
47
+ "mocha": "^10.3.0"
48
+ },
49
+ "author": "Oxford Harrison <oxharris.dev@gmail.com>",
50
+ "maintainers": [
51
+ "Oxford Harrison <oxharris.dev@gmail.com>"
52
+ ],
53
+ "contributors": []
54
+ }
@@ -0,0 +1,57 @@
1
+ import { _before } from '@webqit/util/str/index.js';
2
+ import { _isNumeric } from '@webqit/util/js/index.js';
3
+ import { URLSearchParamsPlus } from './URLSearchParamsPlus.js';
4
+ import { dataType, _meta, _wq } from './core.js';
5
+
6
+ export class FormDataPlus extends FormData {
7
+
8
+ static upgradeInPlace(formData) {
9
+ Object.setPrototypeOf(formData, FormDataPlus.prototype);
10
+ }
11
+
12
+ static json(data = {}, { recursive = true, getIsJsonfiable = false } = {}) {
13
+ const formData = new FormDataPlus;
14
+ let isJsonfiable = true;
15
+
16
+ URLSearchParamsPlus.reduceValue(data, '', (value, contextPath, suggestedKeys = undefined) => {
17
+ if (suggestedKeys) {
18
+ const isJson = dataType(value) === 'json';
19
+ isJsonfiable = isJsonfiable && isJson;
20
+ return isJson && suggestedKeys;
21
+ }
22
+
23
+ if (recursive && [true, false, null].includes(value)) {
24
+ value = new Blob([value + ''], { type: 'application/json' });
25
+ }
26
+
27
+ formData.append(contextPath, value);
28
+ });
29
+
30
+ if (getIsJsonfiable) return [formData, isJsonfiable];
31
+ return formData;
32
+ }
33
+
34
+ async json({ recursive = true, getIsJsonfiable = false } = {}) {
35
+ let isJsonfiable = true;
36
+ let json;
37
+
38
+ for (let [name, value] of this.entries()) {
39
+ if (!json) json = _isNumeric(_before(name, '[')) ? [] : {};
40
+
41
+ let type = dataType(value);
42
+ if (recursive && ['Blob', 'File'].includes(type) && value.type === 'application/json') {
43
+ let _value = await value.text();
44
+ value = JSON.parse(_value);
45
+ type = 'json';
46
+ }
47
+
48
+ isJsonfiable = isJsonfiable && type === 'json';
49
+ URLSearchParamsPlus.set(json, name, value);
50
+ }
51
+
52
+ if (getIsJsonfiable) return [json, isJsonfiable];
53
+ return json;
54
+ }
55
+ }
56
+
57
+
@@ -0,0 +1,152 @@
1
+ import { _isObject, _isTypeObject } from '@webqit/util/js/index.js';
2
+ import { _from as _arrFrom } from '@webqit/util/arr/index.js';
3
+ import { _after } from '@webqit/util/str/index.js';
4
+
5
+ export class HeadersPlus extends upgradeMixin(Headers) {
6
+
7
+ static upgradeInPlace(headers) {
8
+ Object.setPrototypeOf(headers, HeadersPlus.prototype);
9
+ }
10
+
11
+ set(name, value) {
12
+ // Format "Set-Cookie" response header
13
+ if (/^Set-Cookie$/i.test(name) && _isObject(value)) {
14
+ value = renderCookieObjToString(value);
15
+ }
16
+
17
+ // Format "Cookie" request header
18
+ if (/Cookie/i.test(name) && _isTypeObject(value)) {
19
+ value = [].concat(value).map(renderCookieObjToString).join(';');
20
+ }
21
+
22
+ // Format "Content-Range" response header?
23
+ if (/^Content-Range$/i.test(name) && Array.isArray(value)) {
24
+ if (value.length < 2 || !value[0].includes('-')) {
25
+ throw new Error(`A Content-Range array must be in the format: [ 'start-end', 'total' ]`);
26
+ }
27
+ value = `bytes ${value.join('/')}`;
28
+ }
29
+
30
+ // Format "Range" request header?
31
+ if (/^Range$/i.test(name)) {
32
+ let rangeArr = [];
33
+ _arrFrom(value).forEach((range, i) => {
34
+ let rangeStr = Array.isArray(range) ? range.join('-') : range + '';
35
+ if (i === 0 && !rangeStr.includes('bytes=')) {
36
+ rangeStr = `bytes=${rangeStr}`;
37
+ }
38
+ rangeArr.push(rangeStr);
39
+ });
40
+ value = rangeArr.join(', ');
41
+ }
42
+
43
+ // Format "Accept" request header?
44
+ if (/^Accept$/i.test(name) && Array.isArray(value)) {
45
+ value = value.join(',');
46
+ }
47
+
48
+ return super.set(name, value);
49
+ }
50
+
51
+ append(name, value) {
52
+ // Format "Set-Cookie" response header
53
+ if (/^Set-Cookie$/i.test(name) && _isObject(value)) {
54
+ value = renderCookieObjToString(value);
55
+ }
56
+ return super.append(name, value);
57
+ }
58
+
59
+ get(name, parsed = false) {
60
+ let value = super.get(name);
61
+
62
+ // Parse "Set-Cookie" response header
63
+ if (/^Set-Cookie$/i.test(name) && parsed) {
64
+ value = this.getSetCookie()/*IMPORTANT*/.map((str) => {
65
+ const [cookieDefinition, attrsStr] = str.split(';');
66
+ const [name, value] = cookieDefinition.split('=').map((s) => s.trim());
67
+ const cookieObj = { name, value: /*decodeURIComponent*/(value), };
68
+ attrsStr && attrsStr.split(/\;/g).map(attrStr => attrStr.trim().split('=')).forEach(attrsArr => {
69
+ cookieObj[attrsArr[0][0].toLowerCase() + attrsArr[0].substring(1).replace('-', '')] = attrsArr.length === 1 ? true : attrsArr[1];
70
+ });
71
+ return cookieObj;
72
+ });
73
+ }
74
+
75
+ // Parse "Cookie" request header
76
+ if (/^Cookie$/i.test(name) && parsed) {
77
+ value = value?.split(';').map((str) => {
78
+ const [name, value] = str.split('=').map((s) => s.trim());
79
+ return { name, value: /*decodeURIComponent*/(value), };
80
+ }) || [];
81
+ }
82
+
83
+ // Parse "Content-Range" response header?
84
+ if (/^Content-Range$/i.test(name) && value && parsed) {
85
+ value = _after(value, 'bytes ').split('/');
86
+ }
87
+
88
+ // Parse "Range" request header?
89
+ if (/^Range$/i.test(name) && parsed) {
90
+ value = !value ? [] : _after(value, 'bytes=').split(',').map((rangeStr) => {
91
+ const range = rangeStr.trim().split('-').map((s) => s ? parseInt(s, 10) : null);
92
+ range.render = (totalLength) => {
93
+ if (range[1] === null) {
94
+ range[1] = totalLength - 1;
95
+ }
96
+ if (range[0] === null) {
97
+ range[0] = range[1] ? totalLength - range[1] - 1 : 0;
98
+ }
99
+ return range
100
+ };
101
+ range.isValid = (currentStart, totalLength) => {
102
+ // Start higher than end or vice versa?
103
+ if (range[0] > range[1] || range[1] < range[0]) return false;
104
+ // Stretching beyond valid start/end?
105
+ if (range[0] < currentStart || range[1] > totalLength) return false;
106
+ return true;
107
+ };
108
+ return range;
109
+ });
110
+ }
111
+
112
+ // Parse "Accept" request header?
113
+ if (/^Accept$/i.test(name) && value && parsed) {
114
+ const parseSpec = (spec) => {
115
+ const [mime, q] = spec.trim().split(';').map((s) => s.trim());
116
+ return [mime, parseFloat((q || 'q=1').replace('q=', ''))];
117
+ };
118
+ const list = value.split(',')
119
+ .map((spec) => parseSpec(spec))
120
+ .sort((a, b) => a[1] > b[1] ? -1 : 1) || [];
121
+ const $value = value;
122
+ value = {
123
+ match(mime) {
124
+ if (!mime) return 0;
125
+ const splitMime = (mime) => mime.split('/').map((s) => s.trim());
126
+ const $mime = splitMime(mime + '');
127
+ return list.reduce((prev, [entry, q]) => {
128
+ if (prev) return prev;
129
+ const $entry = splitMime(entry);
130
+ return [0, 1].every((i) => (($mime[i] === $entry[i]) || $mime[i] === '*' || $entry[i] === '*')) ? q : 0;
131
+ }, 0);
132
+ },
133
+ toString() {
134
+ return $value;
135
+ }
136
+ };
137
+ }
138
+
139
+ return value;
140
+ }
141
+ }
142
+
143
+ export function renderCookieObjToString(cookieObj) {
144
+ const attrsArr = [`${cookieObj.name}=${/*encodeURIComponent*/(cookieObj.value)}`];
145
+ for (const attrName in cookieObj) {
146
+ if (['name', 'value'].includes(attrName)) continue;
147
+ let _attrName = attrName[0].toUpperCase() + attrName.substring(1);
148
+ if (_attrName === 'MaxAge') { _attrName = 'Max-Age' };
149
+ attrsArr.push(cookieObj[attrName] === true ? _attrName : `${_attrName}=${cookieObj[attrName]}`);
150
+ }
151
+ return attrsArr.join('; ');
152
+ }