@webqit/fetch-plus 0.1.1 → 0.1.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/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  ],
12
12
  "homepage": "https://fetch-plus.netlify.app/",
13
13
  "icon": "https://webqit.io/icon.svg",
14
- "version": "0.1.1",
14
+ "version": "0.1.3",
15
15
  "license": "MIT",
16
16
  "repository": {
17
17
  "type": "git",
@@ -21,9 +21,7 @@
21
21
  "url": "https://github.com/webqit/fetch-plus/issues"
22
22
  },
23
23
  "type": "module",
24
- "exports": {
25
- ".": "./src/index.js"
26
- },
24
+ "main": "./src/index.js",
27
25
  "scripts": {
28
26
  "test": "mocha --extension .test.js --recursive --timeout 5000 --exit",
29
27
  "build": "esbuild main=src/index.browser.js --bundle --minify --sourcemap --outdir=dist",
@@ -35,12 +33,14 @@
35
33
  "@webqit/util": "^0.8.16"
36
34
  },
37
35
  "peerDependencies": {
38
- "@webqit/observer": "^3.8.14",
39
- "@webqit/port-plus": "^0.1.6"
36
+ "@webqit/observer": "^3.8.17",
37
+ "@webqit/port-plus": "^0.1.9",
38
+ "@webqit/url-plus": "^0.1.3"
40
39
  },
41
40
  "devDependencies": {
42
- "@webqit/observer": "^3.8.14",
43
- "@webqit/port-plus": "^0.1.6",
41
+ "@webqit/observer": "^3.8.17",
42
+ "@webqit/port-plus": "^0.1.9",
43
+ "@webqit/url-plus": "^0.1.3",
44
44
  "chai": "^4.3.4",
45
45
  "chai-as-promised": "^7.1.1",
46
46
  "esbuild": "^0.20.2",
@@ -1,55 +1,64 @@
1
1
  import { _before } from '@webqit/util/str/index.js';
2
2
  import { _isNumeric } from '@webqit/util/js/index.js';
3
- import { URLSearchParamsPlus } from './URLSearchParamsPlus.js';
4
- import { dataType, _meta, _wq } from './core.js';
3
+ import { URLSearchParamsPlus } from '@webqit/url-plus';
4
+ import { dataType, _meta, _wq } from './messageParserMixin.js';
5
5
 
6
6
  export class FormDataPlus extends FormData {
7
7
 
8
8
  static upgradeInPlace(formData) {
9
- Object.setPrototypeOf(formData, FormDataPlus.prototype);
9
+ if (formData instanceof FormDataPlus) return formData;
10
+ return Object.setPrototypeOf(formData, FormDataPlus.prototype);
10
11
  }
11
12
 
12
- static json(data = {}, { recursive = true, getIsJsonfiable = false } = {}) {
13
+ static json(data = {}, { encodeLiterals = true, meta = false } = {}) {
13
14
  const formData = new FormDataPlus;
14
- let isJsonfiable = true;
15
+ let isDirectlySerializable = true;
15
16
 
16
17
  URLSearchParamsPlus.reduceValue(data, '', (value, contextPath, suggestedKeys = undefined) => {
17
18
  if (suggestedKeys) {
18
19
  const isJson = dataType(value) === 'json';
19
- isJsonfiable = isJsonfiable && isJson;
20
+ isDirectlySerializable = isDirectlySerializable && isJson;
20
21
  return isJson && suggestedKeys;
21
22
  }
22
23
 
23
- if (recursive && [true, false, null].includes(value)) {
24
+ if (encodeLiterals && [true, false, null].includes(value)) {
24
25
  value = new Blob([value + ''], { type: 'application/json' });
25
26
  }
26
27
 
27
28
  formData.append(contextPath, value);
28
29
  });
29
30
 
30
- if (getIsJsonfiable) return [formData, isJsonfiable];
31
+ if (meta) return { result: formData, isDirectlySerializable };
31
32
  return formData;
32
33
  }
33
34
 
34
- async json({ recursive = true, getIsJsonfiable = false } = {}) {
35
- let isJsonfiable = true;
35
+ async json({ decodeLiterals = true, meta = false } = {}) {
36
+ let isDirectlySerializable = true;
36
37
  let json;
37
38
 
38
39
  for (let [name, value] of this.entries()) {
39
40
  if (!json) json = _isNumeric(_before(name, '[')) ? [] : {};
40
41
 
41
42
  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';
43
+ if (decodeLiterals
44
+ && ['Blob', 'File'].includes(type)
45
+ && value.type === 'application/json'
46
+ && [4, 5].includes(value.size)) {
47
+ let _value = JSON.parse(await value.text());
48
+ if ([null, true, false].includes(_value)) {
49
+ value = _value;
50
+ type = 'json';
51
+ }
46
52
  }
47
53
 
48
- isJsonfiable = isJsonfiable && type === 'json';
54
+ isDirectlySerializable = isDirectlySerializable && type === 'json';
55
+ if (/^[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?$/.test(value)) {
56
+ value = Number(value);
57
+ }
49
58
  URLSearchParamsPlus.set(json, name, value);
50
59
  }
51
60
 
52
- if (getIsJsonfiable) return [json, isJsonfiable];
61
+ if (meta) return { result: json, isDirectlySerializable };
53
62
  return json;
54
63
  }
55
64
  }
@@ -2,47 +2,44 @@ import { _isObject, _isTypeObject } from '@webqit/util/js/index.js';
2
2
  import { _from as _arrFrom } from '@webqit/util/arr/index.js';
3
3
  import { _after } from '@webqit/util/str/index.js';
4
4
 
5
- export class HeadersPlus extends upgradeMixin(Headers) {
5
+ export class HeadersPlus extends Headers {
6
6
 
7
7
  static upgradeInPlace(headers) {
8
- Object.setPrototypeOf(headers, HeadersPlus.prototype);
8
+ if (headers instanceof HeadersPlus) return headers;
9
+ return Object.setPrototypeOf(headers, HeadersPlus.prototype);
9
10
  }
10
11
 
11
12
  set(name, value) {
12
13
  // Format "Set-Cookie" response header
13
- if (/^Set-Cookie$/i.test(name) && _isObject(value)) {
14
- value = renderCookieObjToString(value);
14
+ if (/^Set-Cookie$/i.test(name)) {
15
+ if (Array.isArray(value)) {
16
+ this.delete(name); // IMPORTANT
17
+ for (const v of value) this.append(name, v);
18
+ return;
19
+ }
20
+ if (_isObject(value)) {
21
+ value = renderCookieObjToString(value);
22
+ }
15
23
  }
16
24
 
17
25
  // Format "Cookie" request header
18
- if (/Cookie/i.test(name) && _isTypeObject(value)) {
19
- value = [].concat(value).map(renderCookieObjToString).join(';');
26
+ if (/Cookie/i.test(name)) {
27
+ value = renderCookieInput(value);
20
28
  }
21
29
 
22
30
  // 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('/')}`;
31
+ if (/^Content-Range$/i.test(name)) {
32
+ value = renderContentRangeInput(value);
28
33
  }
29
34
 
30
35
  // Format "Range" request header?
31
36
  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(', ');
37
+ value = renderRangeInput(value);
41
38
  }
42
39
 
43
40
  // Format "Accept" request header?
44
- if (/^Accept$/i.test(name) && Array.isArray(value)) {
45
- value = value.join(',');
41
+ if (/^Accept$/i.test(name)) {
42
+ value = renderAcceptInput(value);
46
43
  }
47
44
 
48
45
  return super.set(name, value);
@@ -50,22 +47,49 @@ export class HeadersPlus extends upgradeMixin(Headers) {
50
47
 
51
48
  append(name, value) {
52
49
  // Format "Set-Cookie" response header
53
- if (/^Set-Cookie$/i.test(name) && _isObject(value)) {
54
- value = renderCookieObjToString(value);
50
+ if (/^Set-Cookie$/i.test(name)) {
51
+ if (Array.isArray(value)) {
52
+ for (const v of value) this.append(name, v);
53
+ return;
54
+ }
55
+ if (_isObject(value)) {
56
+ value = renderCookieObjToString(value);
57
+ }
58
+ }
59
+
60
+ // Format "Cookie" request header
61
+ if (/Cookie/i.test(name)) {
62
+ value = renderCookieInput(value);
63
+ }
64
+
65
+ // Format "Content-Range" response header?
66
+ if (/^Content-Range$/i.test(name)) {
67
+ value = renderContentRangeInput(value);
68
+ }
69
+
70
+ // Format "Range" request header?
71
+ if (/^Range$/i.test(name)) {
72
+ value = renderRangeInput(value);
73
+ }
74
+
75
+ // Format "Accept" request header?
76
+ if (/^Accept$/i.test(name)) {
77
+ value = renderAcceptInput(value);
55
78
  }
79
+
56
80
  return super.append(name, value);
57
81
  }
58
82
 
59
- get(name, parsed = false) {
83
+ get(name, structured = false) {
60
84
  let value = super.get(name);
61
85
 
62
86
  // Parse "Set-Cookie" response header
63
- if (/^Set-Cookie$/i.test(name) && parsed) {
87
+ if (/^Set-Cookie$/i.test(name) && structured) {
64
88
  value = this.getSetCookie()/*IMPORTANT*/.map((str) => {
65
- const [cookieDefinition, attrsStr] = str.split(';');
89
+ const [cookieDefinition, ...attrs] = str.split(';');
66
90
  const [name, value] = cookieDefinition.split('=').map((s) => s.trim());
67
91
  const cookieObj = { name, value: /*decodeURIComponent*/(value), };
68
- attrsStr && attrsStr.split(/\;/g).map(attrStr => attrStr.trim().split('=')).forEach(attrsArr => {
92
+ attrs.map((attrStr) => attrStr.trim().split('=')).forEach(attrsArr => {
69
93
  cookieObj[attrsArr[0][0].toLowerCase() + attrsArr[0].substring(1).replace('-', '')] = attrsArr.length === 1 ? true : attrsArr[1];
70
94
  });
71
95
  return cookieObj;
@@ -73,7 +97,7 @@ export class HeadersPlus extends upgradeMixin(Headers) {
73
97
  }
74
98
 
75
99
  // Parse "Cookie" request header
76
- if (/^Cookie$/i.test(name) && parsed) {
100
+ if (/^Cookie$/i.test(name) && structured) {
77
101
  value = value?.split(';').map((str) => {
78
102
  const [name, value] = str.split('=').map((s) => s.trim());
79
103
  return { name, value: /*decodeURIComponent*/(value), };
@@ -81,58 +105,66 @@ export class HeadersPlus extends upgradeMixin(Headers) {
81
105
  }
82
106
 
83
107
  // Parse "Content-Range" response header?
84
- if (/^Content-Range$/i.test(name) && value && parsed) {
108
+ if (/^Content-Range$/i.test(name) && value && structured) {
85
109
  value = _after(value, 'bytes ').split('/');
86
110
  }
87
111
 
88
112
  // Parse "Range" request header?
89
- if (/^Range$/i.test(name) && parsed) {
113
+ if (/^Range$/i.test(name) && structured) {
90
114
  value = !value ? [] : _after(value, 'bytes=').split(',').map((rangeStr) => {
91
115
  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;
116
+ range.resolveAgainst = (totalLength) => {
117
+ const offsets = [...range];
118
+ if (offsets[1] === null) {
119
+ offsets[1] = totalLength - 1;
120
+ } else {
121
+ offsets[1] = Math.min(offsets[1], totalLength) - 1;
95
122
  }
96
- if (range[0] === null) {
97
- range[0] = range[1] ? totalLength - range[1] - 1 : 0;
123
+ if (offsets[0] === null) {
124
+ offsets[0] = offsets[1] ? totalLength - offsets[1] - 1 : 0;
98
125
  }
99
- return range
126
+ return offsets;
100
127
  };
101
- range.isValid = (currentStart, totalLength) => {
128
+ range.canResolveAgainst = (currentStart, totalLength) => {
129
+ const offsets = [
130
+ typeof range[0] === 'number' ? range[0] : currentStart,
131
+ typeof range[1] === 'number' ? range[1] : totalLength - 1
132
+ ];
102
133
  // Start higher than end or vice versa?
103
- if (range[0] > range[1] || range[1] < range[0]) return false;
134
+ if (offsets[0] > offsets[1]) return false;
104
135
  // Stretching beyond valid start/end?
105
- if (range[0] < currentStart || range[1] > totalLength) return false;
136
+ if (offsets[0] < currentStart || offsets[1] >= totalLength) return false;
106
137
  return true;
107
138
  };
139
+ range.toString = () => {
140
+ return rangeStr;
141
+ };
108
142
  return range;
109
143
  });
110
144
  }
111
145
 
112
146
  // Parse "Accept" request header?
113
- if (/^Accept$/i.test(name) && value && parsed) {
147
+ if (/^Accept$/i.test(name) && value && structured) {
114
148
  const parseSpec = (spec) => {
115
149
  const [mime, q] = spec.trim().split(';').map((s) => s.trim());
116
150
  return [mime, parseFloat((q || 'q=1').replace('q=', ''))];
117
151
  };
118
- const list = value.split(',')
152
+ const $value = value;
153
+ value = value.split(',')
119
154
  .map((spec) => parseSpec(spec))
120
155
  .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
- }
156
+ value.match = (mime) => {
157
+ if (!mime) return 0;
158
+ const splitMime = (mime) => mime.split('/').map((s) => s.trim());
159
+ const $mime = splitMime(mime + '');
160
+ return value.reduce((prev, [entry, q]) => {
161
+ if (prev) return prev;
162
+ const $entry = splitMime(entry);
163
+ return [0, 1].every((i) => (($mime[i] === $entry[i]) || $mime[i] === '*' || $entry[i] === '*')) ? q : 0;
164
+ }, 0);
165
+ };
166
+ value.toString = () => {
167
+ return $value;
136
168
  };
137
169
  }
138
170
 
@@ -150,3 +182,44 @@ export function renderCookieObjToString(cookieObj) {
150
182
  }
151
183
  return attrsArr.join('; ');
152
184
  }
185
+
186
+ function renderCookieInput(value) {
187
+ if (_isTypeObject(value)) {
188
+ value = [].concat(value).map(renderCookieObjToString).join('; ');
189
+ }
190
+ return value;
191
+ }
192
+
193
+ function renderRangeInput(value) {
194
+ let rangeArr = [];
195
+ _arrFrom(value).forEach((range, i) => {
196
+ let rangeStr = Array.isArray(range) ? range.map((n) => [null, undefined].includes(n) ? '' : n).join('-') : range + '';
197
+ if (i === 0 && !rangeStr.includes('bytes=')) {
198
+ rangeStr = `bytes=${rangeStr}`;
199
+ }
200
+ rangeArr.push(rangeStr);
201
+ });
202
+ return rangeArr.join(', ');
203
+ }
204
+
205
+ function renderContentRangeInput(value) {
206
+ if (Array.isArray(value)) {
207
+ if (value.length < 2 || !value[0].includes('-')) {
208
+ throw new Error(`A Content-Range array must be in the format: [ 'start-end', 'total' ]`);
209
+ }
210
+ value = `bytes ${value.join('/')}`;
211
+ }
212
+ return value;
213
+ }
214
+
215
+ function renderAcceptInput(value) {
216
+ if (Array.isArray(value)) {
217
+ value = value.map(
218
+ (s) => Array.isArray(s) ? s.map(
219
+ (s, i) => i === 1 && (s = parseFloat(s), true) ? (s === 1 ? '' : `;q=${s}`) : s.trim()
220
+ ).join('') : s.trim()
221
+ ).join(',');
222
+ }
223
+ return value;
224
+ }
225
+