@bbn/bbn 1.0.477 → 1.0.479
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/dist/bbn.js +1 -1
- package/dist/bbn.js.map +1 -1
- package/dist/fn/ajax/ajax.js +12 -0
- package/dist/index.js +3 -1
- package/dist/req.d.ts +2 -0
- package/dist/req.js +330 -0
- package/package.json +1 -1
package/dist/fn/ajax/ajax.js
CHANGED
|
@@ -292,6 +292,18 @@ export default function ajax(url, datatype = null, data = null, success = null,
|
|
|
292
292
|
"Content-Type": "text/javascript",
|
|
293
293
|
};
|
|
294
294
|
}
|
|
295
|
+
else if (datatype === "json") {
|
|
296
|
+
options['headers'] = {
|
|
297
|
+
accept: "application/json",
|
|
298
|
+
"Content-Type": "application/json",
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
else if (datatype === "html") {
|
|
302
|
+
options['headers'] = {
|
|
303
|
+
accept: "text/html",
|
|
304
|
+
"Content-Type": "text/html",
|
|
305
|
+
};
|
|
306
|
+
}
|
|
295
307
|
let method = "get";
|
|
296
308
|
if (isObject(data)) {
|
|
297
309
|
if (numProperties(data) > 0) {
|
package/dist/index.js
CHANGED
|
@@ -39,6 +39,7 @@ import $ from './$.js';
|
|
|
39
39
|
import lng from './lng.js';
|
|
40
40
|
import vars from './vars.js';
|
|
41
41
|
import env from './env.js';
|
|
42
|
+
import req from './req.js';
|
|
42
43
|
import db from './db.js';
|
|
43
44
|
import fn from './fn.js';
|
|
44
45
|
import date from './date.js';
|
|
@@ -52,10 +53,11 @@ const bbn = {
|
|
|
52
53
|
$,
|
|
53
54
|
lng,
|
|
54
55
|
var: vars,
|
|
56
|
+
date,
|
|
57
|
+
req,
|
|
55
58
|
env,
|
|
56
59
|
db,
|
|
57
60
|
fn,
|
|
58
|
-
date,
|
|
59
61
|
info: [
|
|
60
62
|
{
|
|
61
63
|
value: 'ajax',
|
package/dist/req.d.ts
ADDED
package/dist/req.js
ADDED
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
class Cancel extends Error {
|
|
11
|
+
constructor(message) {
|
|
12
|
+
super(message || 'Request canceled');
|
|
13
|
+
this.name = 'Cancel';
|
|
14
|
+
this.__BBN_CANCEL__ = true;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
const isCancel = (value) => {
|
|
18
|
+
return !!(value && value.__BBN_CANCEL__ === true);
|
|
19
|
+
};
|
|
20
|
+
const parseXhrHeaders = (raw) => {
|
|
21
|
+
const headers = {};
|
|
22
|
+
if (!raw)
|
|
23
|
+
return headers;
|
|
24
|
+
raw.trim().split(/[\r\n]+/).forEach(line => {
|
|
25
|
+
const parts = line.split(': ');
|
|
26
|
+
const key = parts.shift();
|
|
27
|
+
const value = parts.join(': ');
|
|
28
|
+
if (key) {
|
|
29
|
+
headers[key.toLowerCase()] = value;
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
return headers;
|
|
33
|
+
};
|
|
34
|
+
const methodsWithBody = ['POST', 'PUT', 'PATCH', 'DELETE'];
|
|
35
|
+
const buildURL = (url, params) => {
|
|
36
|
+
if (!params)
|
|
37
|
+
return url;
|
|
38
|
+
const usp = new URLSearchParams(params).toString();
|
|
39
|
+
return usp ? url + (url.includes('?') ? '&' : '?') + usp : url;
|
|
40
|
+
};
|
|
41
|
+
const normalizeDataAndHeaders = (data, headersObj) => {
|
|
42
|
+
const headers = new Headers(headersObj || {});
|
|
43
|
+
let body = data;
|
|
44
|
+
if (data != null && !(data instanceof FormData) && typeof data === 'object') {
|
|
45
|
+
headers.set('Content-Type', 'application/json');
|
|
46
|
+
body = JSON.stringify(data);
|
|
47
|
+
}
|
|
48
|
+
return { body, headers };
|
|
49
|
+
};
|
|
50
|
+
const fetchRequest = (method, url, config = {}, aborter) => {
|
|
51
|
+
};
|
|
52
|
+
const xhrRequest = (method, url, config = {}, aborter) => {
|
|
53
|
+
const xhr = new XMLHttpRequest();
|
|
54
|
+
const hasBody = methodsWithBody.includes(method);
|
|
55
|
+
const promise = new Promise((resolve, reject) => {
|
|
56
|
+
xhr.open(method, url, true);
|
|
57
|
+
// Set headers
|
|
58
|
+
const { body, headers } = normalizeDataAndHeaders(config.data, config.headers);
|
|
59
|
+
headers.forEach((value, key) => {
|
|
60
|
+
xhr.setRequestHeader(key, value);
|
|
61
|
+
});
|
|
62
|
+
// Upload progress
|
|
63
|
+
if (typeof config.onUploadProgress === 'function' && xhr.upload) {
|
|
64
|
+
xhr.upload.addEventListener('progress', (event) => {
|
|
65
|
+
const total = event.lengthComputable ? event.total : undefined;
|
|
66
|
+
config.onUploadProgress({
|
|
67
|
+
loaded: event.loaded,
|
|
68
|
+
total,
|
|
69
|
+
progress: total ? event.loaded / total : undefined
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
xhr.onreadystatechange = () => {
|
|
74
|
+
if (xhr.readyState !== 4)
|
|
75
|
+
return;
|
|
76
|
+
const headersObj = parseXhrHeaders(xhr.getAllResponseHeaders());
|
|
77
|
+
const status = xhr.status === 1223 ? 204 : xhr.status; // IE fix, mostly irrelevant now
|
|
78
|
+
const statusText = xhr.statusText || '';
|
|
79
|
+
let data = xhr.responseText;
|
|
80
|
+
const contentType = headersObj['content-type'] || '';
|
|
81
|
+
if (contentType.includes('application/json')) {
|
|
82
|
+
try {
|
|
83
|
+
data = JSON.parse(xhr.responseText);
|
|
84
|
+
}
|
|
85
|
+
catch (e) {
|
|
86
|
+
// leave as text if JSON parse fails
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
const response = {
|
|
90
|
+
data,
|
|
91
|
+
status,
|
|
92
|
+
statusText,
|
|
93
|
+
headers: headersObj,
|
|
94
|
+
config,
|
|
95
|
+
request: xhr
|
|
96
|
+
};
|
|
97
|
+
if (status >= 200 && status < 300) {
|
|
98
|
+
resolve(response);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
const err = new Error('Request failed with status code ' + status);
|
|
102
|
+
err.response = response;
|
|
103
|
+
err.config = config;
|
|
104
|
+
err.request = xhr;
|
|
105
|
+
reject(err);
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
xhr.onerror = () => {
|
|
109
|
+
const err = new Error('Network Error');
|
|
110
|
+
err.config = config;
|
|
111
|
+
err.request = xhr;
|
|
112
|
+
reject(err);
|
|
113
|
+
};
|
|
114
|
+
xhr.onabort = () => {
|
|
115
|
+
reject(new Cancel('Request canceled'));
|
|
116
|
+
};
|
|
117
|
+
if (hasBody) {
|
|
118
|
+
xhr.send(body);
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
xhr.send();
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
promise.cancel = () => xhr.abort();
|
|
125
|
+
return promise;
|
|
126
|
+
};
|
|
127
|
+
/**
|
|
128
|
+
* Upload a file in chunks with progress and cancel support.
|
|
129
|
+
*
|
|
130
|
+
* @param {Object} options
|
|
131
|
+
* @param {string} options.url - Upload endpoint
|
|
132
|
+
* @param {File|Blob} options.file - File to upload
|
|
133
|
+
* @param {number} [options.chunkSize=5MB] - Chunk size in bytes
|
|
134
|
+
* @param {function} [options.onProgress] - Global progress callback
|
|
135
|
+
* onProgress({ loaded, total, progress, chunkIndex, totalChunks })
|
|
136
|
+
* @param {function} [options.onChunkProgress]- Per-chunk progress callback
|
|
137
|
+
* onChunkProgress({ loaded, total, progress, chunkIndex })
|
|
138
|
+
* @param {Object} [options.headers] - Extra headers
|
|
139
|
+
* @param {string} [options.uploadId] - Optional upload ID (for resume)
|
|
140
|
+
* @returns {Promise} - Promise with a .cancel() method
|
|
141
|
+
*/
|
|
142
|
+
const uploadFileInChunks = ({ url, file, chunkSize = 5 * 1024 * 1024, // 5 MB
|
|
143
|
+
onProgress, onChunkProgress, headers = {}, uploadId }) => {
|
|
144
|
+
if (!file) {
|
|
145
|
+
throw new Error('No file provided');
|
|
146
|
+
}
|
|
147
|
+
const totalSize = file.size;
|
|
148
|
+
const totalChunks = Math.ceil(totalSize / chunkSize);
|
|
149
|
+
const finalUploadId = uploadId ||
|
|
150
|
+
(window.crypto && crypto.randomUUID
|
|
151
|
+
? crypto.randomUUID()
|
|
152
|
+
: Date.now().toString(36) + Math.random().toString(36).slice(2));
|
|
153
|
+
let uploadedBytes = 0;
|
|
154
|
+
let aborted = false;
|
|
155
|
+
let currentXhr = null;
|
|
156
|
+
function createChunkRequest(chunk, chunkIndex) {
|
|
157
|
+
return new Promise((resolve, reject) => {
|
|
158
|
+
const xhr = new XMLHttpRequest();
|
|
159
|
+
currentXhr = xhr;
|
|
160
|
+
// Build URL with query params describing the chunk
|
|
161
|
+
const params = new URLSearchParams({
|
|
162
|
+
uploadId: finalUploadId,
|
|
163
|
+
chunkIndex: String(chunkIndex),
|
|
164
|
+
totalChunks: String(totalChunks),
|
|
165
|
+
fileName: file.name,
|
|
166
|
+
fileSize: String(file.size)
|
|
167
|
+
});
|
|
168
|
+
const requestUrl = url + (url.includes('?') ? '&' : '?') + params.toString();
|
|
169
|
+
xhr.open('POST', requestUrl, true);
|
|
170
|
+
// Set extra headers
|
|
171
|
+
Object.entries(headers).forEach(([key, value]) => {
|
|
172
|
+
xhr.setRequestHeader(key, value);
|
|
173
|
+
});
|
|
174
|
+
// Per-chunk upload progress
|
|
175
|
+
xhr.upload.onprogress = (event) => {
|
|
176
|
+
if (!event.lengthComputable)
|
|
177
|
+
return;
|
|
178
|
+
const chunkLoaded = event.loaded;
|
|
179
|
+
const chunkTotal = event.total;
|
|
180
|
+
const chunkProgress = chunkLoaded / chunkTotal;
|
|
181
|
+
if (typeof onChunkProgress === 'function') {
|
|
182
|
+
onChunkProgress({
|
|
183
|
+
loaded: chunkLoaded,
|
|
184
|
+
total: chunkTotal,
|
|
185
|
+
progress: chunkProgress,
|
|
186
|
+
chunkIndex,
|
|
187
|
+
totalChunks
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
// Global progress: bytes before this chunk + current loaded
|
|
191
|
+
const globalLoaded = uploadedBytes + chunkLoaded;
|
|
192
|
+
const globalProgress = globalLoaded / totalSize;
|
|
193
|
+
if (typeof onProgress === 'function') {
|
|
194
|
+
onProgress({
|
|
195
|
+
loaded: globalLoaded,
|
|
196
|
+
total: totalSize,
|
|
197
|
+
progress: globalProgress,
|
|
198
|
+
chunkIndex,
|
|
199
|
+
totalChunks
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
xhr.onreadystatechange = () => {
|
|
204
|
+
if (xhr.readyState !== 4)
|
|
205
|
+
return;
|
|
206
|
+
// Reset currentXhr when completed
|
|
207
|
+
if (currentXhr === xhr) {
|
|
208
|
+
currentXhr = null;
|
|
209
|
+
}
|
|
210
|
+
if (xhr.status >= 200 && xhr.status < 300) {
|
|
211
|
+
// This chunk is done, add its full size to uploadedBytes
|
|
212
|
+
uploadedBytes += chunk.size;
|
|
213
|
+
resolve(xhr.responseText);
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
const err = new Error('Chunk upload failed with status ' + xhr.status);
|
|
217
|
+
err.status = xhr.status;
|
|
218
|
+
err.responseText = xhr.responseText;
|
|
219
|
+
reject(err);
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
xhr.onerror = () => {
|
|
223
|
+
if (currentXhr === xhr) {
|
|
224
|
+
currentXhr = null;
|
|
225
|
+
}
|
|
226
|
+
reject(new Error('Network error during chunk upload'));
|
|
227
|
+
};
|
|
228
|
+
xhr.onabort = () => {
|
|
229
|
+
if (aborted) {
|
|
230
|
+
reject(new Error('Upload canceled'));
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
reject(new Error('Chunk upload aborted'));
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
// Send the chunk as the request body
|
|
237
|
+
xhr.send(chunk);
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
const promise = (() => __awaiter(void 0, void 0, void 0, function* () {
|
|
241
|
+
try {
|
|
242
|
+
let lastResponse = null;
|
|
243
|
+
for (let chunkIndex = 0; chunkIndex < totalChunks; chunkIndex++) {
|
|
244
|
+
if (aborted) {
|
|
245
|
+
throw new Error('Upload canceled');
|
|
246
|
+
}
|
|
247
|
+
const start = chunkIndex * chunkSize;
|
|
248
|
+
const end = Math.min(start + chunkSize, totalSize);
|
|
249
|
+
const chunk = file.slice(start, end);
|
|
250
|
+
lastResponse = yield createChunkRequest(chunk, chunkIndex);
|
|
251
|
+
}
|
|
252
|
+
// All chunks uploaded
|
|
253
|
+
return {
|
|
254
|
+
uploadId: finalUploadId,
|
|
255
|
+
response: lastResponse
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
finally {
|
|
259
|
+
currentXhr = null;
|
|
260
|
+
}
|
|
261
|
+
}))();
|
|
262
|
+
// Attach cancel method to the promise
|
|
263
|
+
promise.cancel = () => {
|
|
264
|
+
aborted = true;
|
|
265
|
+
if (currentXhr) {
|
|
266
|
+
currentXhr.abort();
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
return promise;
|
|
270
|
+
};
|
|
271
|
+
const request = (method, url, config = {}, aborter) => {
|
|
272
|
+
const fetchConfig = {
|
|
273
|
+
method,
|
|
274
|
+
headers: new Headers(config.headers || {}),
|
|
275
|
+
signal: aborter === null || aborter === void 0 ? void 0 : aborter.signal,
|
|
276
|
+
};
|
|
277
|
+
const hasBody = methodsWithBody.includes(method);
|
|
278
|
+
if (config.data != null && hasBody) {
|
|
279
|
+
// You can get fancier here (JSON, FormData, etc.)
|
|
280
|
+
if (typeof config.data === 'object' && !(config.data instanceof FormData)) {
|
|
281
|
+
fetchConfig.headers.set('Content-Type', 'application/json');
|
|
282
|
+
fetchConfig.body = JSON.stringify(config.data);
|
|
283
|
+
}
|
|
284
|
+
else {
|
|
285
|
+
fetchConfig.body = config.data;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
// Add query params support if needed
|
|
289
|
+
if (config.params) {
|
|
290
|
+
const usp = new URLSearchParams(config.params).toString();
|
|
291
|
+
url += (url.includes('?') ? '&' : '?') + usp;
|
|
292
|
+
}
|
|
293
|
+
const fetchPromise = fetch(url, fetchConfig).then((res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
294
|
+
let data;
|
|
295
|
+
const contentType = res.headers.get('content-type') || '';
|
|
296
|
+
if (contentType.includes('application/json')) {
|
|
297
|
+
data = yield res.json();
|
|
298
|
+
}
|
|
299
|
+
else {
|
|
300
|
+
data = yield res.text();
|
|
301
|
+
}
|
|
302
|
+
const response = {
|
|
303
|
+
data,
|
|
304
|
+
status: res.status,
|
|
305
|
+
statusText: res.statusText,
|
|
306
|
+
headers: Object.fromEntries(res.headers.entries()),
|
|
307
|
+
config,
|
|
308
|
+
request: res,
|
|
309
|
+
};
|
|
310
|
+
if (!res.ok) {
|
|
311
|
+
// axios rejects for 4xx/5xx
|
|
312
|
+
const error = new Error('Request failed with status code ' + res.status);
|
|
313
|
+
error.response = response;
|
|
314
|
+
error.config = config;
|
|
315
|
+
error.request = res;
|
|
316
|
+
throw error;
|
|
317
|
+
}
|
|
318
|
+
return response;
|
|
319
|
+
})).catch((err) => {
|
|
320
|
+
if (err.name === 'AbortError') {
|
|
321
|
+
throw new Cancel('Request canceled');
|
|
322
|
+
}
|
|
323
|
+
throw err;
|
|
324
|
+
});
|
|
325
|
+
// axios usually gives you the cancel token separately;
|
|
326
|
+
// here we return both for convenience.
|
|
327
|
+
fetchPromise.cancel = () => aborter.abort();
|
|
328
|
+
return fetchPromise;
|
|
329
|
+
};
|
|
330
|
+
export default request;
|