@brandup/ui-ajax 1.0.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/README.md +76 -0
- package/dist/cjs/index.js +392 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/mjs/index.js +387 -0
- package/dist/mjs/index.js.map +1 -0
- package/dist/types.d.ts +65 -0
- package/package.json +47 -0
package/README.md
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# brandup-ui-ajax
|
|
2
|
+
|
|
3
|
+
[]()
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Install NPM package [@brandup/ui-ajax](https://www.npmjs.com/package/@brandup/ui-ajax).
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
npm i @brandup/ui-ajax@latest
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## AJAX request
|
|
14
|
+
|
|
15
|
+
Simplify async ajax request method.
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
import { request } from "@brandup/ui-ajax";
|
|
19
|
+
|
|
20
|
+
await request({
|
|
21
|
+
url?: string | null;
|
|
22
|
+
query?: QueryData | null;
|
|
23
|
+
method?: AJAXMethod | null;
|
|
24
|
+
timeout?: number | null;
|
|
25
|
+
headers?: { [key: string]: string } | null;
|
|
26
|
+
type?: AJAXReqestType | null;
|
|
27
|
+
data?: string | object | Blob | FormData | HTMLFormElement | null;
|
|
28
|
+
success?: ResponseDelegate | null;
|
|
29
|
+
error?: ErrorDelegate | null;
|
|
30
|
+
disableCache?: boolean | null;
|
|
31
|
+
state?: TState | null;
|
|
32
|
+
})
|
|
33
|
+
.then(response => {
|
|
34
|
+
// response.status: number;
|
|
35
|
+
// response.redirected: boolean;
|
|
36
|
+
// response.url: string | null;
|
|
37
|
+
// response.type: "none" | "json" | "blob" | "text" | "html";
|
|
38
|
+
// response.contentType: string | null;
|
|
39
|
+
// response.data: TData | null;
|
|
40
|
+
// response.state?: TState | null;
|
|
41
|
+
})
|
|
42
|
+
.catch(reason => console.error(reason));
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Request cancellation
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
import { request } from "@brandup/ui-ajax";
|
|
49
|
+
|
|
50
|
+
const cancellation = new AbortController();
|
|
51
|
+
|
|
52
|
+
await request({ }, cancellation.signal)
|
|
53
|
+
.catch(reason => console.error(reason));
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Queue requests
|
|
57
|
+
|
|
58
|
+
Sequential execution of AJAX requests.
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
import { AjaxQueue } from "@brandup/ui-ajax";
|
|
62
|
+
|
|
63
|
+
const queue = new AjaxQueue({
|
|
64
|
+
canRequest?: (request: AjaxRequest) => void | boolean;
|
|
65
|
+
successRequest?: (request: AjaxRequest, response: AjaxResponse) => void;
|
|
66
|
+
errorRequest?: (response: AjaxRequest, reason?: any) => void;
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
queue.push({ request options });
|
|
70
|
+
|
|
71
|
+
queue.reset(); // clear queue without abort current request
|
|
72
|
+
|
|
73
|
+
queue.reset(true); // abort current request and clear queue
|
|
74
|
+
|
|
75
|
+
queue.destroy();
|
|
76
|
+
```
|
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const DEFAULT_TIMEOUT = 30000;
|
|
4
|
+
const FORM_URL = "application/x-www-form-urlencoded";
|
|
5
|
+
const FORM_DATA = "multipart/form-data";
|
|
6
|
+
const urlEncode = (data, rfc3986 = true) => {
|
|
7
|
+
data = encodeURIComponent(data);
|
|
8
|
+
data = data.replace(/%20/g, '+');
|
|
9
|
+
if (rfc3986) {
|
|
10
|
+
data = data.replace(/[!'()*]/g, function (c) {
|
|
11
|
+
return '%' + c.charCodeAt(0).toString(16);
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
return data;
|
|
15
|
+
};
|
|
16
|
+
const ajaxRequest = (options) => {
|
|
17
|
+
let url = options.url || location.href;
|
|
18
|
+
let { query } = options;
|
|
19
|
+
if (options.disableCache) {
|
|
20
|
+
if (!query)
|
|
21
|
+
query = {};
|
|
22
|
+
query["_"] = new Date().getTime().toString();
|
|
23
|
+
}
|
|
24
|
+
url = extendUrl(url, query);
|
|
25
|
+
const method = options.method ? options.method : "GET";
|
|
26
|
+
if (options.data && method === "GET")
|
|
27
|
+
throw new Error("GET method is not support request with data.");
|
|
28
|
+
detectRequestType(options);
|
|
29
|
+
const prepared = prepareRequest(options, options.data);
|
|
30
|
+
const xhr = new XMLHttpRequest();
|
|
31
|
+
xhr.withCredentials = true;
|
|
32
|
+
if (options.timeout === 0 || options.timeout)
|
|
33
|
+
xhr.timeout = options.timeout;
|
|
34
|
+
xhr.onreadystatechange = (e) => {
|
|
35
|
+
switch (xhr.readyState) {
|
|
36
|
+
case XMLHttpRequest.DONE: {
|
|
37
|
+
if (options.success) {
|
|
38
|
+
let responseData = null;
|
|
39
|
+
let responseType = "none";
|
|
40
|
+
const contentType = xhr.getResponseHeader("Content-Type");
|
|
41
|
+
if (xhr.response) {
|
|
42
|
+
if (contentType) {
|
|
43
|
+
if (contentType.includes("json")) {
|
|
44
|
+
responseType = "json";
|
|
45
|
+
responseData = JSON.parse(xhr.responseText);
|
|
46
|
+
}
|
|
47
|
+
else if (contentType.includes("text/plain")) {
|
|
48
|
+
responseType = "text";
|
|
49
|
+
responseData = xhr.responseText;
|
|
50
|
+
}
|
|
51
|
+
else if (contentType.includes("text/html")) {
|
|
52
|
+
responseType = "html";
|
|
53
|
+
responseData = xhr.responseText;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
const headers = {
|
|
58
|
+
get(name) {
|
|
59
|
+
return xhr.getResponseHeader(name);
|
|
60
|
+
},
|
|
61
|
+
has(name) {
|
|
62
|
+
return !!xhr.getResponseHeader(name);
|
|
63
|
+
},
|
|
64
|
+
forEach(callbackfn, thisArg) {
|
|
65
|
+
const headers = xhr.getAllResponseHeaders();
|
|
66
|
+
// Convert the header string into an array
|
|
67
|
+
// of individual headers
|
|
68
|
+
const arr = headers.trim().split(/[\r\n]+/);
|
|
69
|
+
// Create a map of header names to values
|
|
70
|
+
arr.forEach((line) => {
|
|
71
|
+
const parts = line.split(": ");
|
|
72
|
+
const header = parts.shift() || "";
|
|
73
|
+
const value = parts.join(": ");
|
|
74
|
+
callbackfn.call(thisArg, value, header.toLowerCase(), {});
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
options.success({
|
|
79
|
+
status: xhr.status,
|
|
80
|
+
url: xhr.responseURL,
|
|
81
|
+
redirected: false,
|
|
82
|
+
type: responseType,
|
|
83
|
+
contentType,
|
|
84
|
+
headers,
|
|
85
|
+
data: responseData,
|
|
86
|
+
state: options.state
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
xhr.onabort = (e) => {
|
|
94
|
+
if (options.error)
|
|
95
|
+
options.error(options, "Request aborted");
|
|
96
|
+
};
|
|
97
|
+
xhr.open(method, url, true);
|
|
98
|
+
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
|
|
99
|
+
for (const key in prepared.headers) {
|
|
100
|
+
const value = prepared.headers[key];
|
|
101
|
+
if (!value)
|
|
102
|
+
continue;
|
|
103
|
+
xhr.setRequestHeader(key, value);
|
|
104
|
+
}
|
|
105
|
+
if (method === "GET")
|
|
106
|
+
xhr.send();
|
|
107
|
+
else
|
|
108
|
+
xhr.send(prepared.body);
|
|
109
|
+
return xhr;
|
|
110
|
+
};
|
|
111
|
+
const request = async (options, abortSignal) => {
|
|
112
|
+
let url = options.url || location.href;
|
|
113
|
+
url = extendUrl(url, options.query);
|
|
114
|
+
const method = options.method ? options.method.toUpperCase() : "GET";
|
|
115
|
+
let body = options.data;
|
|
116
|
+
if (body && method === "GET")
|
|
117
|
+
throw new Error("GET method is not support request with data.");
|
|
118
|
+
detectRequestType(options);
|
|
119
|
+
const prepared = prepareRequest(options, body);
|
|
120
|
+
const abortSignals = [AbortSignal.timeout(options.timeout ?? DEFAULT_TIMEOUT)];
|
|
121
|
+
if (abortSignal)
|
|
122
|
+
abortSignals.push(abortSignal);
|
|
123
|
+
try {
|
|
124
|
+
const response = await fetch(url, {
|
|
125
|
+
method,
|
|
126
|
+
headers: new Headers(prepared.headers),
|
|
127
|
+
cache: options.disableCache ? "no-cache" : "default",
|
|
128
|
+
redirect: "follow",
|
|
129
|
+
signal: AbortSignal.any(abortSignals),
|
|
130
|
+
body: prepared.body
|
|
131
|
+
});
|
|
132
|
+
let result;
|
|
133
|
+
switch (response.type) {
|
|
134
|
+
case "basic":
|
|
135
|
+
case "default": {
|
|
136
|
+
let responseData = null;
|
|
137
|
+
let responseType = "none";
|
|
138
|
+
let contentType = response.headers.get("content-type");
|
|
139
|
+
if (!response.redirected && response.body) {
|
|
140
|
+
if (contentType) {
|
|
141
|
+
const ctSplitIndex = contentType.indexOf(";");
|
|
142
|
+
if (ctSplitIndex > 0)
|
|
143
|
+
contentType = contentType.substring(0, ctSplitIndex);
|
|
144
|
+
if (contentType.includes("json")) {
|
|
145
|
+
responseType = "json";
|
|
146
|
+
responseData = await response.json();
|
|
147
|
+
}
|
|
148
|
+
else if (contentType.includes("text/html")) {
|
|
149
|
+
responseType = "html";
|
|
150
|
+
responseData = await response.text();
|
|
151
|
+
}
|
|
152
|
+
else if (contentType.includes("text/plain")) {
|
|
153
|
+
responseType = "text";
|
|
154
|
+
responseData = await response.text();
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
responseType = "blob";
|
|
158
|
+
responseData = await response.blob();
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
result = {
|
|
163
|
+
status: response.status,
|
|
164
|
+
url: response.url,
|
|
165
|
+
redirected: response.redirected,
|
|
166
|
+
type: responseType,
|
|
167
|
+
contentType,
|
|
168
|
+
headers: response.headers,
|
|
169
|
+
data: responseData,
|
|
170
|
+
state: options.state
|
|
171
|
+
};
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
174
|
+
case "opaqueredirect":
|
|
175
|
+
throw new Error("Not supported opaqueredirect.");
|
|
176
|
+
case "error":
|
|
177
|
+
throw new Error("Response error.");
|
|
178
|
+
case "cors":
|
|
179
|
+
throw new Error("Response type cors is not supported.");
|
|
180
|
+
case "opaque":
|
|
181
|
+
throw new Error("Response type opaque is not supported.");
|
|
182
|
+
default:
|
|
183
|
+
throw new Error(`Unknown response type: ${response.type}`);
|
|
184
|
+
}
|
|
185
|
+
if (options.success)
|
|
186
|
+
options.success(result);
|
|
187
|
+
return result;
|
|
188
|
+
}
|
|
189
|
+
catch (error) {
|
|
190
|
+
if (options.error)
|
|
191
|
+
options.error(options, error);
|
|
192
|
+
throw new Error(`Error ajax request: ${(error && error.message) ? error.message : error}`);
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
const detectRequestType = (options) => {
|
|
196
|
+
if (!options.type && options.data) {
|
|
197
|
+
const body = options.data;
|
|
198
|
+
if (body instanceof Blob)
|
|
199
|
+
options.type = "BLOB";
|
|
200
|
+
else if (body instanceof FormData)
|
|
201
|
+
options.type = null;
|
|
202
|
+
else if (body instanceof HTMLFormElement)
|
|
203
|
+
options.type = "FORM";
|
|
204
|
+
else if (body instanceof Object)
|
|
205
|
+
options.type = "JSON";
|
|
206
|
+
else if (typeof body === "string")
|
|
207
|
+
options.type = "TEXT";
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
const prepareRequest = (options, body) => {
|
|
211
|
+
const headers = {};
|
|
212
|
+
if (options.headers) {
|
|
213
|
+
for (const key in options.headers) {
|
|
214
|
+
const value = options.headers[key];
|
|
215
|
+
if (!value)
|
|
216
|
+
continue;
|
|
217
|
+
headers[key] = value;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
if (options.type) {
|
|
221
|
+
let contentType = null;
|
|
222
|
+
let accept = null;
|
|
223
|
+
switch (options.type) {
|
|
224
|
+
case "XML":
|
|
225
|
+
contentType = "application/xml; charset=utf-8";
|
|
226
|
+
accept = "application/xml, text/xml, */*; q=0.01";
|
|
227
|
+
break;
|
|
228
|
+
case "JSON":
|
|
229
|
+
contentType = "application/json; charset=utf-8";
|
|
230
|
+
accept = "application/json, text/json, */*; q=0.01";
|
|
231
|
+
body = JSON.stringify(body);
|
|
232
|
+
break;
|
|
233
|
+
case "FORM":
|
|
234
|
+
if (body instanceof HTMLFormElement) {
|
|
235
|
+
//const form = <HTMLFormElement>body;
|
|
236
|
+
//contentType = form.enctype ?? FORM_URL;
|
|
237
|
+
body = new FormData(body);
|
|
238
|
+
}
|
|
239
|
+
else if (body instanceof FormData)
|
|
240
|
+
contentType = FORM_URL;
|
|
241
|
+
if (contentType == FORM_URL)
|
|
242
|
+
body = encodeForm(body);
|
|
243
|
+
else if (contentType == FORM_DATA)
|
|
244
|
+
contentType = null;
|
|
245
|
+
break;
|
|
246
|
+
case "FORMDATA":
|
|
247
|
+
break;
|
|
248
|
+
case "TEXT":
|
|
249
|
+
contentType = "text/plain";
|
|
250
|
+
break;
|
|
251
|
+
}
|
|
252
|
+
if (accept)
|
|
253
|
+
headers["Accept"] = accept;
|
|
254
|
+
if (contentType)
|
|
255
|
+
headers['Content-Type'] = contentType;
|
|
256
|
+
}
|
|
257
|
+
return {
|
|
258
|
+
headers,
|
|
259
|
+
body
|
|
260
|
+
};
|
|
261
|
+
};
|
|
262
|
+
const createSearchParams = (query) => {
|
|
263
|
+
const urlParams = new URLSearchParams();
|
|
264
|
+
if (!query)
|
|
265
|
+
return urlParams;
|
|
266
|
+
for (const key in query) {
|
|
267
|
+
const val = query[key];
|
|
268
|
+
if (val === null)
|
|
269
|
+
continue;
|
|
270
|
+
if (Array.isArray(val))
|
|
271
|
+
val.forEach(v => urlParams.append(key, v));
|
|
272
|
+
else
|
|
273
|
+
urlParams.append(key, val);
|
|
274
|
+
}
|
|
275
|
+
return urlParams;
|
|
276
|
+
};
|
|
277
|
+
const extendUrl = (url, query) => {
|
|
278
|
+
if (query) {
|
|
279
|
+
const urlParams = createSearchParams(query);
|
|
280
|
+
if (urlParams.size) {
|
|
281
|
+
if (url.indexOf("?") === -1)
|
|
282
|
+
url += "?";
|
|
283
|
+
else
|
|
284
|
+
url += "&";
|
|
285
|
+
url += urlParams.toString();
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
return url;
|
|
289
|
+
};
|
|
290
|
+
const encodeForm = (data) => {
|
|
291
|
+
const query = new URLSearchParams();
|
|
292
|
+
data.forEach((value, key) => {
|
|
293
|
+
if (!key)
|
|
294
|
+
return;
|
|
295
|
+
query.append(key, value.toString());
|
|
296
|
+
});
|
|
297
|
+
return query.toString();
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
class AjaxQueue {
|
|
301
|
+
_options;
|
|
302
|
+
_requests = [];
|
|
303
|
+
_curent = null;
|
|
304
|
+
_destroyed = false;
|
|
305
|
+
constructor(options) {
|
|
306
|
+
this._options = options ? options : {};
|
|
307
|
+
}
|
|
308
|
+
get length() { return this._requests.length; }
|
|
309
|
+
get isFree() { return !this._requests.length && !this._curent; }
|
|
310
|
+
get isEmpty() { return !this._requests.length; }
|
|
311
|
+
push(request) {
|
|
312
|
+
if (this._destroyed)
|
|
313
|
+
throw new Error("AjaxQueue is destroyed.");
|
|
314
|
+
this._requests.push({ request, abort: new AbortController() });
|
|
315
|
+
if (!this._curent)
|
|
316
|
+
this.__execute();
|
|
317
|
+
}
|
|
318
|
+
enque(request) {
|
|
319
|
+
const { success, error } = request;
|
|
320
|
+
return new Promise((resolve, reject) => {
|
|
321
|
+
request.success = (response) => {
|
|
322
|
+
if (success)
|
|
323
|
+
success(response);
|
|
324
|
+
resolve(response);
|
|
325
|
+
};
|
|
326
|
+
request.error = (response, reason) => {
|
|
327
|
+
if (error)
|
|
328
|
+
error(request, reason);
|
|
329
|
+
reject(reason);
|
|
330
|
+
};
|
|
331
|
+
this.push(request);
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
reset(cancelCurrentRequest = false) {
|
|
335
|
+
this._requests = [];
|
|
336
|
+
const current = this._curent;
|
|
337
|
+
this._curent = null;
|
|
338
|
+
if (cancelCurrentRequest && current)
|
|
339
|
+
current.abort?.abort("ResetAjaxQueue");
|
|
340
|
+
}
|
|
341
|
+
destroy() {
|
|
342
|
+
if (this._destroyed)
|
|
343
|
+
return;
|
|
344
|
+
this._destroyed = true;
|
|
345
|
+
this._requests = [];
|
|
346
|
+
if (this._curent) {
|
|
347
|
+
this._curent.abort?.abort("DestroyAjaxQueue");
|
|
348
|
+
this._curent = null;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
__execute() {
|
|
352
|
+
if (this._destroyed)
|
|
353
|
+
return;
|
|
354
|
+
if (this._curent)
|
|
355
|
+
throw new Error("AjaxQueue currently is executing.");
|
|
356
|
+
const task = this._curent = this._requests.shift() ?? null;
|
|
357
|
+
if (task) {
|
|
358
|
+
if (this._options.canRequest && this._options.canRequest(task.request) === false) {
|
|
359
|
+
this.__next();
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
task.abort = new AbortController();
|
|
363
|
+
task.xhr = request(task.request, task.abort.signal);
|
|
364
|
+
task.xhr
|
|
365
|
+
.then(response => {
|
|
366
|
+
if (this._destroyed)
|
|
367
|
+
return;
|
|
368
|
+
if (this._options.successRequest)
|
|
369
|
+
this._options.successRequest(task.request, response);
|
|
370
|
+
})
|
|
371
|
+
.catch(reason => {
|
|
372
|
+
if (this._destroyed)
|
|
373
|
+
return;
|
|
374
|
+
if (this._options.errorRequest)
|
|
375
|
+
this._options.errorRequest(task.request, reason);
|
|
376
|
+
})
|
|
377
|
+
.finally(() => this.__next());
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
__next() {
|
|
381
|
+
if (this._destroyed)
|
|
382
|
+
return;
|
|
383
|
+
this._curent = null;
|
|
384
|
+
this.__execute();
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
exports.AjaxQueue = AjaxQueue;
|
|
389
|
+
exports.ajaxRequest = ajaxRequest;
|
|
390
|
+
exports.request = request;
|
|
391
|
+
exports.urlEncode = urlEncode;
|
|
392
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
const DEFAULT_TIMEOUT = 30000;
|
|
2
|
+
const FORM_URL = "application/x-www-form-urlencoded";
|
|
3
|
+
const FORM_DATA = "multipart/form-data";
|
|
4
|
+
const urlEncode = (data, rfc3986 = true) => {
|
|
5
|
+
data = encodeURIComponent(data);
|
|
6
|
+
data = data.replace(/%20/g, '+');
|
|
7
|
+
if (rfc3986) {
|
|
8
|
+
data = data.replace(/[!'()*]/g, function (c) {
|
|
9
|
+
return '%' + c.charCodeAt(0).toString(16);
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
return data;
|
|
13
|
+
};
|
|
14
|
+
const ajaxRequest = (options) => {
|
|
15
|
+
let url = options.url || location.href;
|
|
16
|
+
let { query } = options;
|
|
17
|
+
if (options.disableCache) {
|
|
18
|
+
if (!query)
|
|
19
|
+
query = {};
|
|
20
|
+
query["_"] = new Date().getTime().toString();
|
|
21
|
+
}
|
|
22
|
+
url = extendUrl(url, query);
|
|
23
|
+
const method = options.method ? options.method : "GET";
|
|
24
|
+
if (options.data && method === "GET")
|
|
25
|
+
throw new Error("GET method is not support request with data.");
|
|
26
|
+
detectRequestType(options);
|
|
27
|
+
const prepared = prepareRequest(options, options.data);
|
|
28
|
+
const xhr = new XMLHttpRequest();
|
|
29
|
+
xhr.withCredentials = true;
|
|
30
|
+
if (options.timeout === 0 || options.timeout)
|
|
31
|
+
xhr.timeout = options.timeout;
|
|
32
|
+
xhr.onreadystatechange = (e) => {
|
|
33
|
+
switch (xhr.readyState) {
|
|
34
|
+
case XMLHttpRequest.DONE: {
|
|
35
|
+
if (options.success) {
|
|
36
|
+
let responseData = null;
|
|
37
|
+
let responseType = "none";
|
|
38
|
+
const contentType = xhr.getResponseHeader("Content-Type");
|
|
39
|
+
if (xhr.response) {
|
|
40
|
+
if (contentType) {
|
|
41
|
+
if (contentType.includes("json")) {
|
|
42
|
+
responseType = "json";
|
|
43
|
+
responseData = JSON.parse(xhr.responseText);
|
|
44
|
+
}
|
|
45
|
+
else if (contentType.includes("text/plain")) {
|
|
46
|
+
responseType = "text";
|
|
47
|
+
responseData = xhr.responseText;
|
|
48
|
+
}
|
|
49
|
+
else if (contentType.includes("text/html")) {
|
|
50
|
+
responseType = "html";
|
|
51
|
+
responseData = xhr.responseText;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
const headers = {
|
|
56
|
+
get(name) {
|
|
57
|
+
return xhr.getResponseHeader(name);
|
|
58
|
+
},
|
|
59
|
+
has(name) {
|
|
60
|
+
return !!xhr.getResponseHeader(name);
|
|
61
|
+
},
|
|
62
|
+
forEach(callbackfn, thisArg) {
|
|
63
|
+
const headers = xhr.getAllResponseHeaders();
|
|
64
|
+
// Convert the header string into an array
|
|
65
|
+
// of individual headers
|
|
66
|
+
const arr = headers.trim().split(/[\r\n]+/);
|
|
67
|
+
// Create a map of header names to values
|
|
68
|
+
arr.forEach((line) => {
|
|
69
|
+
const parts = line.split(": ");
|
|
70
|
+
const header = parts.shift() || "";
|
|
71
|
+
const value = parts.join(": ");
|
|
72
|
+
callbackfn.call(thisArg, value, header.toLowerCase(), {});
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
options.success({
|
|
77
|
+
status: xhr.status,
|
|
78
|
+
url: xhr.responseURL,
|
|
79
|
+
redirected: false,
|
|
80
|
+
type: responseType,
|
|
81
|
+
contentType,
|
|
82
|
+
headers,
|
|
83
|
+
data: responseData,
|
|
84
|
+
state: options.state
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
xhr.onabort = (e) => {
|
|
92
|
+
if (options.error)
|
|
93
|
+
options.error(options, "Request aborted");
|
|
94
|
+
};
|
|
95
|
+
xhr.open(method, url, true);
|
|
96
|
+
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
|
|
97
|
+
for (const key in prepared.headers) {
|
|
98
|
+
const value = prepared.headers[key];
|
|
99
|
+
if (!value)
|
|
100
|
+
continue;
|
|
101
|
+
xhr.setRequestHeader(key, value);
|
|
102
|
+
}
|
|
103
|
+
if (method === "GET")
|
|
104
|
+
xhr.send();
|
|
105
|
+
else
|
|
106
|
+
xhr.send(prepared.body);
|
|
107
|
+
return xhr;
|
|
108
|
+
};
|
|
109
|
+
const request = async (options, abortSignal) => {
|
|
110
|
+
let url = options.url || location.href;
|
|
111
|
+
url = extendUrl(url, options.query);
|
|
112
|
+
const method = options.method ? options.method.toUpperCase() : "GET";
|
|
113
|
+
let body = options.data;
|
|
114
|
+
if (body && method === "GET")
|
|
115
|
+
throw new Error("GET method is not support request with data.");
|
|
116
|
+
detectRequestType(options);
|
|
117
|
+
const prepared = prepareRequest(options, body);
|
|
118
|
+
const abortSignals = [AbortSignal.timeout(options.timeout ?? DEFAULT_TIMEOUT)];
|
|
119
|
+
if (abortSignal)
|
|
120
|
+
abortSignals.push(abortSignal);
|
|
121
|
+
try {
|
|
122
|
+
const response = await fetch(url, {
|
|
123
|
+
method,
|
|
124
|
+
headers: new Headers(prepared.headers),
|
|
125
|
+
cache: options.disableCache ? "no-cache" : "default",
|
|
126
|
+
redirect: "follow",
|
|
127
|
+
signal: AbortSignal.any(abortSignals),
|
|
128
|
+
body: prepared.body
|
|
129
|
+
});
|
|
130
|
+
let result;
|
|
131
|
+
switch (response.type) {
|
|
132
|
+
case "basic":
|
|
133
|
+
case "default": {
|
|
134
|
+
let responseData = null;
|
|
135
|
+
let responseType = "none";
|
|
136
|
+
let contentType = response.headers.get("content-type");
|
|
137
|
+
if (!response.redirected && response.body) {
|
|
138
|
+
if (contentType) {
|
|
139
|
+
const ctSplitIndex = contentType.indexOf(";");
|
|
140
|
+
if (ctSplitIndex > 0)
|
|
141
|
+
contentType = contentType.substring(0, ctSplitIndex);
|
|
142
|
+
if (contentType.includes("json")) {
|
|
143
|
+
responseType = "json";
|
|
144
|
+
responseData = await response.json();
|
|
145
|
+
}
|
|
146
|
+
else if (contentType.includes("text/html")) {
|
|
147
|
+
responseType = "html";
|
|
148
|
+
responseData = await response.text();
|
|
149
|
+
}
|
|
150
|
+
else if (contentType.includes("text/plain")) {
|
|
151
|
+
responseType = "text";
|
|
152
|
+
responseData = await response.text();
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
responseType = "blob";
|
|
156
|
+
responseData = await response.blob();
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
result = {
|
|
161
|
+
status: response.status,
|
|
162
|
+
url: response.url,
|
|
163
|
+
redirected: response.redirected,
|
|
164
|
+
type: responseType,
|
|
165
|
+
contentType,
|
|
166
|
+
headers: response.headers,
|
|
167
|
+
data: responseData,
|
|
168
|
+
state: options.state
|
|
169
|
+
};
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
case "opaqueredirect":
|
|
173
|
+
throw new Error("Not supported opaqueredirect.");
|
|
174
|
+
case "error":
|
|
175
|
+
throw new Error("Response error.");
|
|
176
|
+
case "cors":
|
|
177
|
+
throw new Error("Response type cors is not supported.");
|
|
178
|
+
case "opaque":
|
|
179
|
+
throw new Error("Response type opaque is not supported.");
|
|
180
|
+
default:
|
|
181
|
+
throw new Error(`Unknown response type: ${response.type}`);
|
|
182
|
+
}
|
|
183
|
+
if (options.success)
|
|
184
|
+
options.success(result);
|
|
185
|
+
return result;
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
if (options.error)
|
|
189
|
+
options.error(options, error);
|
|
190
|
+
throw new Error(`Error ajax request: ${(error && error.message) ? error.message : error}`);
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
const detectRequestType = (options) => {
|
|
194
|
+
if (!options.type && options.data) {
|
|
195
|
+
const body = options.data;
|
|
196
|
+
if (body instanceof Blob)
|
|
197
|
+
options.type = "BLOB";
|
|
198
|
+
else if (body instanceof FormData)
|
|
199
|
+
options.type = null;
|
|
200
|
+
else if (body instanceof HTMLFormElement)
|
|
201
|
+
options.type = "FORM";
|
|
202
|
+
else if (body instanceof Object)
|
|
203
|
+
options.type = "JSON";
|
|
204
|
+
else if (typeof body === "string")
|
|
205
|
+
options.type = "TEXT";
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
const prepareRequest = (options, body) => {
|
|
209
|
+
const headers = {};
|
|
210
|
+
if (options.headers) {
|
|
211
|
+
for (const key in options.headers) {
|
|
212
|
+
const value = options.headers[key];
|
|
213
|
+
if (!value)
|
|
214
|
+
continue;
|
|
215
|
+
headers[key] = value;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
if (options.type) {
|
|
219
|
+
let contentType = null;
|
|
220
|
+
let accept = null;
|
|
221
|
+
switch (options.type) {
|
|
222
|
+
case "XML":
|
|
223
|
+
contentType = "application/xml; charset=utf-8";
|
|
224
|
+
accept = "application/xml, text/xml, */*; q=0.01";
|
|
225
|
+
break;
|
|
226
|
+
case "JSON":
|
|
227
|
+
contentType = "application/json; charset=utf-8";
|
|
228
|
+
accept = "application/json, text/json, */*; q=0.01";
|
|
229
|
+
body = JSON.stringify(body);
|
|
230
|
+
break;
|
|
231
|
+
case "FORM":
|
|
232
|
+
if (body instanceof HTMLFormElement) {
|
|
233
|
+
//const form = <HTMLFormElement>body;
|
|
234
|
+
//contentType = form.enctype ?? FORM_URL;
|
|
235
|
+
body = new FormData(body);
|
|
236
|
+
}
|
|
237
|
+
else if (body instanceof FormData)
|
|
238
|
+
contentType = FORM_URL;
|
|
239
|
+
if (contentType == FORM_URL)
|
|
240
|
+
body = encodeForm(body);
|
|
241
|
+
else if (contentType == FORM_DATA)
|
|
242
|
+
contentType = null;
|
|
243
|
+
break;
|
|
244
|
+
case "FORMDATA":
|
|
245
|
+
break;
|
|
246
|
+
case "TEXT":
|
|
247
|
+
contentType = "text/plain";
|
|
248
|
+
break;
|
|
249
|
+
}
|
|
250
|
+
if (accept)
|
|
251
|
+
headers["Accept"] = accept;
|
|
252
|
+
if (contentType)
|
|
253
|
+
headers['Content-Type'] = contentType;
|
|
254
|
+
}
|
|
255
|
+
return {
|
|
256
|
+
headers,
|
|
257
|
+
body
|
|
258
|
+
};
|
|
259
|
+
};
|
|
260
|
+
const createSearchParams = (query) => {
|
|
261
|
+
const urlParams = new URLSearchParams();
|
|
262
|
+
if (!query)
|
|
263
|
+
return urlParams;
|
|
264
|
+
for (const key in query) {
|
|
265
|
+
const val = query[key];
|
|
266
|
+
if (val === null)
|
|
267
|
+
continue;
|
|
268
|
+
if (Array.isArray(val))
|
|
269
|
+
val.forEach(v => urlParams.append(key, v));
|
|
270
|
+
else
|
|
271
|
+
urlParams.append(key, val);
|
|
272
|
+
}
|
|
273
|
+
return urlParams;
|
|
274
|
+
};
|
|
275
|
+
const extendUrl = (url, query) => {
|
|
276
|
+
if (query) {
|
|
277
|
+
const urlParams = createSearchParams(query);
|
|
278
|
+
if (urlParams.size) {
|
|
279
|
+
if (url.indexOf("?") === -1)
|
|
280
|
+
url += "?";
|
|
281
|
+
else
|
|
282
|
+
url += "&";
|
|
283
|
+
url += urlParams.toString();
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return url;
|
|
287
|
+
};
|
|
288
|
+
const encodeForm = (data) => {
|
|
289
|
+
const query = new URLSearchParams();
|
|
290
|
+
data.forEach((value, key) => {
|
|
291
|
+
if (!key)
|
|
292
|
+
return;
|
|
293
|
+
query.append(key, value.toString());
|
|
294
|
+
});
|
|
295
|
+
return query.toString();
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
class AjaxQueue {
|
|
299
|
+
_options;
|
|
300
|
+
_requests = [];
|
|
301
|
+
_curent = null;
|
|
302
|
+
_destroyed = false;
|
|
303
|
+
constructor(options) {
|
|
304
|
+
this._options = options ? options : {};
|
|
305
|
+
}
|
|
306
|
+
get length() { return this._requests.length; }
|
|
307
|
+
get isFree() { return !this._requests.length && !this._curent; }
|
|
308
|
+
get isEmpty() { return !this._requests.length; }
|
|
309
|
+
push(request) {
|
|
310
|
+
if (this._destroyed)
|
|
311
|
+
throw new Error("AjaxQueue is destroyed.");
|
|
312
|
+
this._requests.push({ request, abort: new AbortController() });
|
|
313
|
+
if (!this._curent)
|
|
314
|
+
this.__execute();
|
|
315
|
+
}
|
|
316
|
+
enque(request) {
|
|
317
|
+
const { success, error } = request;
|
|
318
|
+
return new Promise((resolve, reject) => {
|
|
319
|
+
request.success = (response) => {
|
|
320
|
+
if (success)
|
|
321
|
+
success(response);
|
|
322
|
+
resolve(response);
|
|
323
|
+
};
|
|
324
|
+
request.error = (response, reason) => {
|
|
325
|
+
if (error)
|
|
326
|
+
error(request, reason);
|
|
327
|
+
reject(reason);
|
|
328
|
+
};
|
|
329
|
+
this.push(request);
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
reset(cancelCurrentRequest = false) {
|
|
333
|
+
this._requests = [];
|
|
334
|
+
const current = this._curent;
|
|
335
|
+
this._curent = null;
|
|
336
|
+
if (cancelCurrentRequest && current)
|
|
337
|
+
current.abort?.abort("ResetAjaxQueue");
|
|
338
|
+
}
|
|
339
|
+
destroy() {
|
|
340
|
+
if (this._destroyed)
|
|
341
|
+
return;
|
|
342
|
+
this._destroyed = true;
|
|
343
|
+
this._requests = [];
|
|
344
|
+
if (this._curent) {
|
|
345
|
+
this._curent.abort?.abort("DestroyAjaxQueue");
|
|
346
|
+
this._curent = null;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
__execute() {
|
|
350
|
+
if (this._destroyed)
|
|
351
|
+
return;
|
|
352
|
+
if (this._curent)
|
|
353
|
+
throw new Error("AjaxQueue currently is executing.");
|
|
354
|
+
const task = this._curent = this._requests.shift() ?? null;
|
|
355
|
+
if (task) {
|
|
356
|
+
if (this._options.canRequest && this._options.canRequest(task.request) === false) {
|
|
357
|
+
this.__next();
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
task.abort = new AbortController();
|
|
361
|
+
task.xhr = request(task.request, task.abort.signal);
|
|
362
|
+
task.xhr
|
|
363
|
+
.then(response => {
|
|
364
|
+
if (this._destroyed)
|
|
365
|
+
return;
|
|
366
|
+
if (this._options.successRequest)
|
|
367
|
+
this._options.successRequest(task.request, response);
|
|
368
|
+
})
|
|
369
|
+
.catch(reason => {
|
|
370
|
+
if (this._destroyed)
|
|
371
|
+
return;
|
|
372
|
+
if (this._options.errorRequest)
|
|
373
|
+
this._options.errorRequest(task.request, reason);
|
|
374
|
+
})
|
|
375
|
+
.finally(() => this.__next());
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
__next() {
|
|
379
|
+
if (this._destroyed)
|
|
380
|
+
return;
|
|
381
|
+
this._curent = null;
|
|
382
|
+
this.__execute();
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
export { AjaxQueue, ajaxRequest, request, urlEncode };
|
|
387
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
type AJAXMethod = "GET" | "POST" | "PUT" | "DELETE" | "PATCH" | string;
|
|
2
|
+
type AJAXReqestType = "NONE" | "JSON" | "XML" | "FORM" | "FORMDATA" | "TEXT" | "BLOB";
|
|
3
|
+
type ResponseTye = "none" | "json" | "blob" | "text" | "html";
|
|
4
|
+
type ResponseDelegate = (response: AjaxResponse) => void;
|
|
5
|
+
type ErrorDelegate = (request: AjaxRequest, reason?: any) => void;
|
|
6
|
+
type QueryData = {
|
|
7
|
+
[key: string]: string | string[];
|
|
8
|
+
};
|
|
9
|
+
interface AjaxRequest<TState = any> {
|
|
10
|
+
url?: string | null;
|
|
11
|
+
query?: QueryData | null;
|
|
12
|
+
method?: AJAXMethod | null;
|
|
13
|
+
timeout?: number | null;
|
|
14
|
+
headers?: {
|
|
15
|
+
[key: string]: string;
|
|
16
|
+
} | null;
|
|
17
|
+
type?: AJAXReqestType | null;
|
|
18
|
+
data?: string | object | Blob | FormData | HTMLFormElement | null;
|
|
19
|
+
success?: ResponseDelegate | null;
|
|
20
|
+
error?: ErrorDelegate | null;
|
|
21
|
+
disableCache?: boolean | null;
|
|
22
|
+
state?: TState | null;
|
|
23
|
+
}
|
|
24
|
+
interface AjaxResponse<TData = any, TState = any> {
|
|
25
|
+
status: number;
|
|
26
|
+
redirected: boolean;
|
|
27
|
+
url: string | null;
|
|
28
|
+
type: ResponseTye;
|
|
29
|
+
contentType: string | null;
|
|
30
|
+
headers: ResponseHeaders;
|
|
31
|
+
data: TData | null;
|
|
32
|
+
state?: TState | null;
|
|
33
|
+
}
|
|
34
|
+
interface ResponseHeaders {
|
|
35
|
+
get(name: string): string | null;
|
|
36
|
+
has(name: string): boolean;
|
|
37
|
+
forEach(callbackfn: (value: string, key: string, parent: ResponseHeaders) => void, thisArg?: any): void;
|
|
38
|
+
}
|
|
39
|
+
declare const urlEncode: (data: string, rfc3986?: boolean) => string;
|
|
40
|
+
declare const ajaxRequest: (options: AjaxRequest) => XMLHttpRequest;
|
|
41
|
+
declare const request: (options: AjaxRequest, abortSignal?: AbortSignal) => Promise<AjaxResponse>;
|
|
42
|
+
|
|
43
|
+
interface AjaxQueueOptions {
|
|
44
|
+
canRequest?: (request: AjaxRequest) => void | boolean;
|
|
45
|
+
successRequest?: (request: AjaxRequest, response: AjaxResponse) => void;
|
|
46
|
+
errorRequest?: (response: AjaxRequest, reason?: any) => void;
|
|
47
|
+
}
|
|
48
|
+
declare class AjaxQueue {
|
|
49
|
+
private _options;
|
|
50
|
+
private _requests;
|
|
51
|
+
private _curent;
|
|
52
|
+
private _destroyed;
|
|
53
|
+
constructor(options?: AjaxQueueOptions);
|
|
54
|
+
get length(): number;
|
|
55
|
+
get isFree(): boolean;
|
|
56
|
+
get isEmpty(): boolean;
|
|
57
|
+
push(request: AjaxRequest): void;
|
|
58
|
+
enque(request: AjaxRequest): Promise<AjaxResponse<any, any>>;
|
|
59
|
+
reset(cancelCurrentRequest?: boolean): void;
|
|
60
|
+
destroy(): void;
|
|
61
|
+
private __execute;
|
|
62
|
+
private __next;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export { type AJAXMethod, type AJAXReqestType, AjaxQueue, type AjaxQueueOptions, type AjaxRequest, type AjaxResponse, type ErrorDelegate, type QueryData, type ResponseDelegate, type ResponseHeaders, type ResponseTye, ajaxRequest, request, urlEncode };
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@brandup/ui-ajax",
|
|
3
|
+
"description": "Basic AJAX framework.",
|
|
4
|
+
"keywords": [
|
|
5
|
+
"brandup",
|
|
6
|
+
"typescript",
|
|
7
|
+
"ui",
|
|
8
|
+
"ajax"
|
|
9
|
+
],
|
|
10
|
+
"author": {
|
|
11
|
+
"name": "Dmitry Kovyazin",
|
|
12
|
+
"email": "it@brandup.online"
|
|
13
|
+
},
|
|
14
|
+
"homepage": "https://github.com/brandup-online/brandup-ui/npm/brandup-ui-ajax",
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "https://github.com/brandup-online/brandup-ui.git"
|
|
18
|
+
},
|
|
19
|
+
"bugs": {
|
|
20
|
+
"url": "https://github.com/brandup-online/brandup-ui/issues",
|
|
21
|
+
"email": "it@brandup.online"
|
|
22
|
+
},
|
|
23
|
+
"license": "Apache-2.0",
|
|
24
|
+
"version": "1.0.1",
|
|
25
|
+
"main": "dist/cjs/index.js",
|
|
26
|
+
"module": "dist/mjs/index.js",
|
|
27
|
+
"types": "dist/types.d.ts",
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@rollup/plugin-commonjs": "^25.0.8",
|
|
30
|
+
"@rollup/plugin-node-resolve": "^15.2.3",
|
|
31
|
+
"@rollup/plugin-terser": "^0.4.4",
|
|
32
|
+
"rollup": "^4.19.0",
|
|
33
|
+
"rollup-plugin-dts": "^6.1.1",
|
|
34
|
+
"rollup-plugin-peer-deps-external": "^2.2.4",
|
|
35
|
+
"rollup-plugin-typescript2": "^0.36.0",
|
|
36
|
+
"tslib": "^2.6.3",
|
|
37
|
+
"typescript": "^5.5.3"
|
|
38
|
+
},
|
|
39
|
+
"files": [
|
|
40
|
+
"dist",
|
|
41
|
+
"README.md"
|
|
42
|
+
],
|
|
43
|
+
"scripts": {
|
|
44
|
+
"build": "rollup -c --bundleConfigAsCjs",
|
|
45
|
+
"watch": "rollup -c -w --bundleConfigAsCjs"
|
|
46
|
+
}
|
|
47
|
+
}
|