@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.
@@ -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
@@ -0,0 +1,2 @@
1
+ declare const request: (method: string, url: string, config?: any, aborter?: any) => any;
2
+ export default request;
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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bbn/bbn",
3
- "version": "1.0.477",
3
+ "version": "1.0.479",
4
4
  "description": "Javascript toolkit",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",