@webqit/fetch-plus 0.1.2 → 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.2",
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.16",
39
- "@webqit/port-plus": "^0.1.7"
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.16",
43
- "@webqit/port-plus": "^0.1.7",
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,7 +1,7 @@
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
 
@@ -10,47 +10,55 @@ export class FormDataPlus extends FormData {
10
10
  return Object.setPrototypeOf(formData, FormDataPlus.prototype);
11
11
  }
12
12
 
13
- static json(data = {}, { recursive = true, getIsJsonfiable = false } = {}) {
13
+ static json(data = {}, { encodeLiterals = true, meta = false } = {}) {
14
14
  const formData = new FormDataPlus;
15
- let isJsonfiable = true;
15
+ let isDirectlySerializable = true;
16
16
 
17
17
  URLSearchParamsPlus.reduceValue(data, '', (value, contextPath, suggestedKeys = undefined) => {
18
18
  if (suggestedKeys) {
19
19
  const isJson = dataType(value) === 'json';
20
- isJsonfiable = isJsonfiable && isJson;
20
+ isDirectlySerializable = isDirectlySerializable && isJson;
21
21
  return isJson && suggestedKeys;
22
22
  }
23
23
 
24
- if (recursive && [true, false, null].includes(value)) {
24
+ if (encodeLiterals && [true, false, null].includes(value)) {
25
25
  value = new Blob([value + ''], { type: 'application/json' });
26
26
  }
27
27
 
28
28
  formData.append(contextPath, value);
29
29
  });
30
30
 
31
- if (getIsJsonfiable) return [formData, isJsonfiable];
31
+ if (meta) return { result: formData, isDirectlySerializable };
32
32
  return formData;
33
33
  }
34
34
 
35
- async json({ recursive = true, getIsJsonfiable = false } = {}) {
36
- let isJsonfiable = true;
35
+ async json({ decodeLiterals = true, meta = false } = {}) {
36
+ let isDirectlySerializable = true;
37
37
  let json;
38
38
 
39
39
  for (let [name, value] of this.entries()) {
40
40
  if (!json) json = _isNumeric(_before(name, '[')) ? [] : {};
41
41
 
42
42
  let type = dataType(value);
43
- if (recursive && ['Blob', 'File'].includes(type) && value.type === 'application/json') {
44
- let _value = await value.text();
45
- value = JSON.parse(_value);
46
- 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
+ }
47
52
  }
48
53
 
49
- isJsonfiable = isJsonfiable && type === 'json';
54
+ isDirectlySerializable = isDirectlySerializable && type === 'json';
55
+ if (/^[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?$/.test(value)) {
56
+ value = Number(value);
57
+ }
50
58
  URLSearchParamsPlus.set(json, name, value);
51
59
  }
52
60
 
53
- if (getIsJsonfiable) return [json, isJsonfiable];
61
+ if (meta) return { result: json, isDirectlySerializable };
54
62
  return json;
55
63
  }
56
64
  }
@@ -2,7 +2,7 @@ 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
8
  if (headers instanceof HeadersPlus) return headers;
@@ -11,39 +11,35 @@ export class HeadersPlus extends upgradeMixin(Headers) {
11
11
 
12
12
  set(name, value) {
13
13
  // Format "Set-Cookie" response header
14
- if (/^Set-Cookie$/i.test(name) && _isObject(value)) {
15
- 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
+ }
16
23
  }
17
24
 
18
25
  // Format "Cookie" request header
19
- if (/Cookie/i.test(name) && _isTypeObject(value)) {
20
- value = [].concat(value).map(renderCookieObjToString).join(';');
26
+ if (/Cookie/i.test(name)) {
27
+ value = renderCookieInput(value);
21
28
  }
22
29
 
23
30
  // Format "Content-Range" response header?
24
- if (/^Content-Range$/i.test(name) && Array.isArray(value)) {
25
- if (value.length < 2 || !value[0].includes('-')) {
26
- throw new Error(`A Content-Range array must be in the format: [ 'start-end', 'total' ]`);
27
- }
28
- value = `bytes ${value.join('/')}`;
31
+ if (/^Content-Range$/i.test(name)) {
32
+ value = renderContentRangeInput(value);
29
33
  }
30
34
 
31
35
  // Format "Range" request header?
32
36
  if (/^Range$/i.test(name)) {
33
- let rangeArr = [];
34
- _arrFrom(value).forEach((range, i) => {
35
- let rangeStr = Array.isArray(range) ? range.join('-') : range + '';
36
- if (i === 0 && !rangeStr.includes('bytes=')) {
37
- rangeStr = `bytes=${rangeStr}`;
38
- }
39
- rangeArr.push(rangeStr);
40
- });
41
- value = rangeArr.join(', ');
37
+ value = renderRangeInput(value);
42
38
  }
43
39
 
44
40
  // Format "Accept" request header?
45
- if (/^Accept$/i.test(name) && Array.isArray(value)) {
46
- value = value.join(',');
41
+ if (/^Accept$/i.test(name)) {
42
+ value = renderAcceptInput(value);
47
43
  }
48
44
 
49
45
  return super.set(name, value);
@@ -51,22 +47,49 @@ export class HeadersPlus extends upgradeMixin(Headers) {
51
47
 
52
48
  append(name, value) {
53
49
  // Format "Set-Cookie" response header
54
- if (/^Set-Cookie$/i.test(name) && _isObject(value)) {
55
- 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);
56
78
  }
79
+
57
80
  return super.append(name, value);
58
81
  }
59
82
 
60
- get(name, parsed = false) {
83
+ get(name, structured = false) {
61
84
  let value = super.get(name);
62
85
 
63
86
  // Parse "Set-Cookie" response header
64
- if (/^Set-Cookie$/i.test(name) && parsed) {
87
+ if (/^Set-Cookie$/i.test(name) && structured) {
65
88
  value = this.getSetCookie()/*IMPORTANT*/.map((str) => {
66
- const [cookieDefinition, attrsStr] = str.split(';');
89
+ const [cookieDefinition, ...attrs] = str.split(';');
67
90
  const [name, value] = cookieDefinition.split('=').map((s) => s.trim());
68
91
  const cookieObj = { name, value: /*decodeURIComponent*/(value), };
69
- attrsStr && attrsStr.split(/\;/g).map(attrStr => attrStr.trim().split('=')).forEach(attrsArr => {
92
+ attrs.map((attrStr) => attrStr.trim().split('=')).forEach(attrsArr => {
70
93
  cookieObj[attrsArr[0][0].toLowerCase() + attrsArr[0].substring(1).replace('-', '')] = attrsArr.length === 1 ? true : attrsArr[1];
71
94
  });
72
95
  return cookieObj;
@@ -74,7 +97,7 @@ export class HeadersPlus extends upgradeMixin(Headers) {
74
97
  }
75
98
 
76
99
  // Parse "Cookie" request header
77
- if (/^Cookie$/i.test(name) && parsed) {
100
+ if (/^Cookie$/i.test(name) && structured) {
78
101
  value = value?.split(';').map((str) => {
79
102
  const [name, value] = str.split('=').map((s) => s.trim());
80
103
  return { name, value: /*decodeURIComponent*/(value), };
@@ -82,58 +105,66 @@ export class HeadersPlus extends upgradeMixin(Headers) {
82
105
  }
83
106
 
84
107
  // Parse "Content-Range" response header?
85
- if (/^Content-Range$/i.test(name) && value && parsed) {
108
+ if (/^Content-Range$/i.test(name) && value && structured) {
86
109
  value = _after(value, 'bytes ').split('/');
87
110
  }
88
111
 
89
112
  // Parse "Range" request header?
90
- if (/^Range$/i.test(name) && parsed) {
113
+ if (/^Range$/i.test(name) && structured) {
91
114
  value = !value ? [] : _after(value, 'bytes=').split(',').map((rangeStr) => {
92
115
  const range = rangeStr.trim().split('-').map((s) => s ? parseInt(s, 10) : null);
93
- range.render = (totalLength) => {
94
- if (range[1] === null) {
95
- 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;
96
122
  }
97
- if (range[0] === null) {
98
- range[0] = range[1] ? totalLength - range[1] - 1 : 0;
123
+ if (offsets[0] === null) {
124
+ offsets[0] = offsets[1] ? totalLength - offsets[1] - 1 : 0;
99
125
  }
100
- return range
126
+ return offsets;
101
127
  };
102
- 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
+ ];
103
133
  // Start higher than end or vice versa?
104
- if (range[0] > range[1] || range[1] < range[0]) return false;
134
+ if (offsets[0] > offsets[1]) return false;
105
135
  // Stretching beyond valid start/end?
106
- if (range[0] < currentStart || range[1] > totalLength) return false;
136
+ if (offsets[0] < currentStart || offsets[1] >= totalLength) return false;
107
137
  return true;
108
138
  };
139
+ range.toString = () => {
140
+ return rangeStr;
141
+ };
109
142
  return range;
110
143
  });
111
144
  }
112
145
 
113
146
  // Parse "Accept" request header?
114
- if (/^Accept$/i.test(name) && value && parsed) {
147
+ if (/^Accept$/i.test(name) && value && structured) {
115
148
  const parseSpec = (spec) => {
116
149
  const [mime, q] = spec.trim().split(';').map((s) => s.trim());
117
150
  return [mime, parseFloat((q || 'q=1').replace('q=', ''))];
118
151
  };
119
- const list = value.split(',')
152
+ const $value = value;
153
+ value = value.split(',')
120
154
  .map((spec) => parseSpec(spec))
121
155
  .sort((a, b) => a[1] > b[1] ? -1 : 1) || [];
122
- const $value = value;
123
- value = {
124
- match(mime) {
125
- if (!mime) return 0;
126
- const splitMime = (mime) => mime.split('/').map((s) => s.trim());
127
- const $mime = splitMime(mime + '');
128
- return list.reduce((prev, [entry, q]) => {
129
- if (prev) return prev;
130
- const $entry = splitMime(entry);
131
- return [0, 1].every((i) => (($mime[i] === $entry[i]) || $mime[i] === '*' || $entry[i] === '*')) ? q : 0;
132
- }, 0);
133
- },
134
- toString() {
135
- return $value;
136
- }
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;
137
168
  };
138
169
  }
139
170
 
@@ -151,3 +182,44 @@ export function renderCookieObjToString(cookieObj) {
151
182
  }
152
183
  return attrsArr.join('; ');
153
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
+