@jacraig/request 1.0.0

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.
@@ -0,0 +1,475 @@
1
+ (function (global, factory) {
2
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@jacraig/woodchuck')) :
3
+ typeof define === 'function' && define.amd ? define(['exports', '@jacraig/woodchuck'], factory) :
4
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.request = {}, global.woodchuck));
5
+ })(this, (function (exports, woodchuck) { 'use strict';
6
+
7
+ class DatabaseConnection {
8
+ constructor(dbName, tables, version) {
9
+ this.dbName = dbName;
10
+ this.tables = tables;
11
+ this.version = version;
12
+ const request = indexedDB.open(dbName, version);
13
+ request.onupgradeneeded = (ev) => {
14
+ this.database = ev.target.result;
15
+ if (!this.database) {
16
+ return;
17
+ }
18
+ for (const table of tables) {
19
+ if (this.database.objectStoreNames.contains(table)) {
20
+ this.database.deleteObjectStore(table);
21
+ }
22
+ this.database.createObjectStore(table);
23
+ }
24
+ };
25
+ request.onsuccess = (ev) => {
26
+ this.database = ev.target.result;
27
+ };
28
+ request.onerror = (ev) => {
29
+ woodchuck.Logger.error('Failed to open the database:', ev.target.error);
30
+ };
31
+ }
32
+ openDatabase() {
33
+ return new Promise((resolve, reject) => {
34
+ const request = indexedDB.open(this.dbName, this.version);
35
+ request.onsuccess = (ev) => {
36
+ this.database = ev.target.result;
37
+ resolve(this);
38
+ };
39
+ request.onerror = (ev) => {
40
+ woodchuck.Logger.error('Failed to open the database:', ev.target.error);
41
+ reject(new Error('Failed to open the database:' + ev.target.error));
42
+ };
43
+ });
44
+ }
45
+ add(table, obj, key) {
46
+ return new Promise((resolve, reject) => {
47
+ if (!this.database) {
48
+ woodchuck.Logger.error('Database connection is not open');
49
+ reject(new Error('Database connection is not open'));
50
+ return;
51
+ }
52
+ const transaction = this.database.transaction(table, 'readwrite');
53
+ const objectStore = transaction.objectStore(table);
54
+ const request = objectStore.put(obj, key);
55
+ request.onsuccess = () => {
56
+ resolve();
57
+ };
58
+ request.onerror = (ev) => {
59
+ woodchuck.Logger.error('Failed to add an object to the database: ', ev.target.error);
60
+ reject(new Error('Failed to add an object to the database: ' + ev.target.error));
61
+ };
62
+ });
63
+ }
64
+ remove(table, key) {
65
+ return new Promise((resolve, reject) => {
66
+ if (!this.database) {
67
+ woodchuck.Logger.error('Database connection is not open');
68
+ reject(new Error('Database connection is not open'));
69
+ return;
70
+ }
71
+ const transaction = this.database.transaction(table, 'readwrite');
72
+ const objectStore = transaction.objectStore(table);
73
+ const request = objectStore.delete(key);
74
+ request.onsuccess = () => {
75
+ resolve();
76
+ };
77
+ request.onerror = (ev) => {
78
+ woodchuck.Logger.error('Failed to remove an object from the database: ', ev.target.error);
79
+ reject(new Error('Failed to remove an object from the database: ' + ev.target.error));
80
+ };
81
+ });
82
+ }
83
+ getByKey(table, key) {
84
+ return new Promise((resolve, reject) => {
85
+ if (!this.database) {
86
+ woodchuck.Logger.error('Database connection is not open');
87
+ reject(new Error('Database connection is not open'));
88
+ return;
89
+ }
90
+ const transaction = this.database.transaction(table, 'readwrite');
91
+ const objectStore = transaction.objectStore(table);
92
+ const request = objectStore.get(key);
93
+ request.onsuccess = (ev) => {
94
+ resolve(ev.target.result);
95
+ };
96
+ request.onerror = (ev) => {
97
+ woodchuck.Logger.error('Failed to retrieve an object from the database: ', ev.target.error);
98
+ reject(new Error('Failed to retrieve an object from the database: ' + ev.target.error));
99
+ };
100
+ });
101
+ }
102
+ getKeys(table) {
103
+ return new Promise((resolve, reject) => {
104
+ if (!this.database) {
105
+ woodchuck.Logger.error('Database connection is not open');
106
+ reject(new Error('Database connection is not open'));
107
+ return;
108
+ }
109
+ const transaction = this.database.transaction(table, 'readwrite');
110
+ const objectStore = transaction.objectStore(table);
111
+ const request = objectStore.getAllKeys();
112
+ request.onsuccess = (ev) => {
113
+ resolve(ev.target.result);
114
+ };
115
+ request.onerror = (ev) => {
116
+ woodchuck.Logger.error('Failed to retrieve an object from the database: ', ev.target.error);
117
+ reject(new Error('Failed to retrieve keys from the database: ' + ev.target.error));
118
+ };
119
+ });
120
+ }
121
+ get(table, query) {
122
+ return new Promise((resolve, reject) => {
123
+ if (!this.database) {
124
+ woodchuck.Logger.error('Database connection is not open');
125
+ reject(new Error('Database connection is not open'));
126
+ return;
127
+ }
128
+ const transaction = this.database.transaction(table, 'readwrite');
129
+ const objectStore = transaction.objectStore(table);
130
+ const request = objectStore.get(query);
131
+ request.onsuccess = (ev) => {
132
+ resolve(ev.target.result);
133
+ };
134
+ request.onerror = (ev) => {
135
+ woodchuck.Logger.error('Failed to retrieve an object from the database: ', ev.target.error);
136
+ reject(new Error('Failed to retrieve an object from the database: ' + ev.target.error));
137
+ };
138
+ });
139
+ }
140
+ getAll(table, query) {
141
+ return new Promise((resolve, reject) => {
142
+ if (!this.database) {
143
+ woodchuck.Logger.error('Database connection is not open');
144
+ reject(new Error('Database connection is not open'));
145
+ return;
146
+ }
147
+ const transaction = this.database.transaction(table, 'readwrite');
148
+ const objectStore = transaction.objectStore(table);
149
+ const request = objectStore.getAll(query);
150
+ request.onsuccess = (ev) => {
151
+ resolve(ev.target.result);
152
+ };
153
+ request.onerror = (ev) => {
154
+ woodchuck.Logger.error('Failed to retrieve an object from the database: ', ev.target.error);
155
+ reject(new Error('Failed to retrieve objects from the database: ' + ev.target.error));
156
+ };
157
+ });
158
+ }
159
+ }
160
+
161
+ class IndexedDbStorageProvider {
162
+ constructor() {
163
+ this.database = new DatabaseConnection("cacheStore", ["cache", "cacheEntryOptions"], 1);
164
+ }
165
+ async clear() {
166
+ await this.database.openDatabase();
167
+ let cacheEntryOptions = await this.database.getAll("cacheEntryOptions");
168
+ let cacheEntries = await this.database.getAll("cache");
169
+ for (let i = 0; i < cacheEntryOptions.length; i++) {
170
+ let cacheEntry = cacheEntries[i];
171
+ await this.remove(cacheEntry.key);
172
+ }
173
+ return this;
174
+ }
175
+ async getOptions(key) {
176
+ await this.database.openDatabase();
177
+ return this.database.getByKey("cacheEntryOptions", key);
178
+ }
179
+ async add(obj, key, options) {
180
+ await this.database.openDatabase();
181
+ await this.database.add("cache", obj, key);
182
+ await this.database.add("cacheEntryOptions", options, key);
183
+ return this;
184
+ }
185
+ async remove(key) {
186
+ await this.database.openDatabase();
187
+ await this.database.remove("cache", key);
188
+ await this.database.remove("cacheEntryOptions", key);
189
+ return this;
190
+ }
191
+ async get(key) {
192
+ await this.database.openDatabase();
193
+ return this.database.getByKey("cache", key);
194
+ }
195
+ async compact() {
196
+ await this.database.openDatabase();
197
+ let cacheEntryOptions = await this.database.getAll("cacheEntryOptions");
198
+ let cacheEntries = await this.database.getAll("cache");
199
+ let now = new Date().getTime();
200
+ for (let i = 0; i < cacheEntryOptions.length; i++) {
201
+ let cacheEntryOption = cacheEntryOptions[i];
202
+ let cacheEntry = cacheEntries[i];
203
+ if (cacheEntryOption.expirationTime == 0) {
204
+ continue;
205
+ }
206
+ if (cacheEntryOption.expirationTime < now) {
207
+ await this.remove(cacheEntry.key);
208
+ }
209
+ }
210
+ return this;
211
+ }
212
+ }
213
+ class Cache {
214
+ constructor() { }
215
+ static configure(storageProvider = new IndexedDbStorageProvider()) {
216
+ this.storageProvider ??= globalThis.StorageProvider || storageProvider || new IndexedDbStorageProvider();
217
+ globalThis.StorageProvider = this.storageProvider;
218
+ }
219
+ static async set(key, value, entryOptions = { expirationTime: 0, slidingExpirationTime: 0, sliding: false }) {
220
+ woodchuck.Logger.debug("Setting object in cache: ", { "key": key, "value": value, "entryOptions": entryOptions });
221
+ this.configure();
222
+ await this.storageProvider.add(value, key, entryOptions);
223
+ }
224
+ static async get(key) {
225
+ woodchuck.Logger.debug("Getting object from cache: " + key);
226
+ this.configure();
227
+ await this.storageProvider.compact();
228
+ let returnValue = await this.storageProvider.get(key);
229
+ if (returnValue == null) {
230
+ return returnValue;
231
+ }
232
+ let entryOptions = await this.storageProvider.getOptions(key);
233
+ if (entryOptions.sliding) {
234
+ await this.set(key, returnValue, {
235
+ expirationTime: new Date().getTime() + entryOptions.slidingExpirationTime,
236
+ slidingExpirationTime: entryOptions.slidingExpirationTime,
237
+ sliding: true
238
+ });
239
+ }
240
+ return returnValue;
241
+ }
242
+ static async remove(key) {
243
+ woodchuck.Logger.debug("Removing object from cache: " + key);
244
+ this.configure();
245
+ await this.storageProvider.remove(key);
246
+ }
247
+ static async clear() {
248
+ woodchuck.Logger.debug("Clearing cache");
249
+ this.configure();
250
+ await this.storageProvider.clear();
251
+ }
252
+ }
253
+
254
+ class CancellationToken {
255
+ constructor() {
256
+ this.canceled = false;
257
+ }
258
+ }
259
+ class Request {
260
+ constructor(options) {
261
+ this.options = {
262
+ method: "GET",
263
+ url: "",
264
+ headers: {},
265
+ credentials: "same-origin",
266
+ serializer: JSON.stringify,
267
+ parser: (response) => response.json(),
268
+ success: (response) => { woodchuck.Logger.debug("Request response from " + this.options.url + ":", response); },
269
+ error: (reason) => { woodchuck.Logger.error("Request error from " + this.options.url + ":", reason); },
270
+ retry: (attempt) => { woodchuck.Logger.debug("Request retry on " + this.options.url + ":", { "attempt": attempt }); },
271
+ storageMode: exports.StorageMode.NetworkFirst,
272
+ cacheKey: "",
273
+ timeout: 60000,
274
+ retryAttempts: 3,
275
+ retryDelay: 1000
276
+ };
277
+ this.abortController = null;
278
+ this.options = { ...this.options, ...options };
279
+ }
280
+ static get(url, data) {
281
+ return new Request({ method: "GET", url, data, cacheKey: url + JSON.stringify(data) })
282
+ .withHeaders({
283
+ "Accept": "application/json"
284
+ });
285
+ }
286
+ static post(url, data) {
287
+ return new Request({ method: "POST", url, data, cacheKey: url + JSON.stringify(data), storageMode: exports.StorageMode.NetworkOnly })
288
+ .withHeaders({
289
+ "Content-Type": "application/json",
290
+ "Accept": "application/json"
291
+ });
292
+ }
293
+ static put(url, data) {
294
+ return new Request({ method: "PUT", url, data, cacheKey: url + JSON.stringify(data), storageMode: exports.StorageMode.NetworkOnly })
295
+ .withHeaders({
296
+ "Content-Type": "application/json",
297
+ "Accept": "application/json"
298
+ });
299
+ }
300
+ static delete(url, data) {
301
+ return new Request({ method: "DELETE", url, data, cacheKey: url + JSON.stringify(data), storageMode: exports.StorageMode.NetworkOnly })
302
+ .withHeaders({
303
+ "Accept": "application/json"
304
+ });
305
+ }
306
+ static ofType(method, url, data) {
307
+ return new Request({ method, url, data, cacheKey: url + JSON.stringify(data) })
308
+ .withHeaders({
309
+ "Accept": "application/json"
310
+ });
311
+ }
312
+ withAuthenticationProvider(authenticationProvider) {
313
+ this.options.authenticationProvider = authenticationProvider;
314
+ return this;
315
+ }
316
+ withHeaders(headers) {
317
+ this.options.headers = { ...this.options.headers, ...headers };
318
+ return this;
319
+ }
320
+ withCancellationToken(cancellationToken) {
321
+ this.options.cancellationToken = cancellationToken;
322
+ return this;
323
+ }
324
+ withCredentials(credentials) {
325
+ this.options.credentials = credentials;
326
+ return this;
327
+ }
328
+ withSerializer(serializer) {
329
+ this.options.serializer = serializer;
330
+ return this;
331
+ }
332
+ withParser(parser) {
333
+ this.options.parser = parser;
334
+ return this;
335
+ }
336
+ onSuccess(callback) {
337
+ this.options.success = callback ?? ((response) => { woodchuck.Logger.debug("Request response:", response); });
338
+ return this;
339
+ }
340
+ onError(callback) {
341
+ this.options.error = callback ?? ((reason) => { woodchuck.Logger.error("Request error:", reason); });
342
+ return this;
343
+ }
344
+ onRetry(callback) {
345
+ this.options.retry = callback ?? ((attempt) => { woodchuck.Logger.debug("Request retry:", { "attempt": attempt }); });
346
+ return this;
347
+ }
348
+ withStorageMode(storageMode) {
349
+ this.options.storageMode = storageMode;
350
+ return this;
351
+ }
352
+ withCacheKey(cacheKey) {
353
+ this.options.cacheKey = cacheKey;
354
+ return this;
355
+ }
356
+ withTimeout(timeout) {
357
+ this.options.timeout = timeout ?? 60000;
358
+ return this;
359
+ }
360
+ withRetryAttempts(retryAttempts) {
361
+ this.options.retryAttempts = retryAttempts;
362
+ return this;
363
+ }
364
+ withRetryDelay(retryDelay) {
365
+ this.options.retryDelay = retryDelay;
366
+ return this;
367
+ }
368
+ abort() {
369
+ if (this.abortController == null || this.options.error == null) {
370
+ return this;
371
+ }
372
+ this.abortController.abort();
373
+ this.options.error(new Error("The request was aborted."));
374
+ return this;
375
+ }
376
+ async send() {
377
+ const { authenticationProvider, method, url, data, headers, credentials, serializer, parser, success, error, storageMode, cacheKey, timeout, retryAttempts, retryDelay, retry, cancellationToken } = this.options;
378
+ const abortController = new AbortController();
379
+ this.abortController = abortController;
380
+ let attempts = 0;
381
+ let lastError = null;
382
+ let successMethod = success || ((response) => { woodchuck.Logger.debug("Request response from " + url + ":", response); });
383
+ let errorMethod = error || ((reason) => { woodchuck.Logger.error("Request error from " + url + ":", reason); });
384
+ let serializerMethod = serializer || JSON.stringify;
385
+ let parserMethod = parser || ((response) => response.json());
386
+ let retryMethod = retry || ((attempt) => { woodchuck.Logger.debug("Request retry on " + url + ":", { "attempt": attempt }); });
387
+ const sendRequest = async () => {
388
+ if (storageMode === exports.StorageMode.StorageFirst || storageMode === exports.StorageMode.StorageAndUpdate) {
389
+ const cachedValue = await Cache.get(cacheKey || "");
390
+ if (cachedValue !== undefined) {
391
+ successMethod(cachedValue);
392
+ if (storageMode === exports.StorageMode.StorageFirst) {
393
+ return cachedValue;
394
+ }
395
+ }
396
+ }
397
+ if (!navigator.onLine) {
398
+ if (storageMode === exports.StorageMode.NetworkFirst) {
399
+ const cachedValue = await Cache.get(cacheKey || "");
400
+ if (cachedValue !== undefined) {
401
+ successMethod(cachedValue);
402
+ return cachedValue;
403
+ }
404
+ }
405
+ const errorMessage = new Error("System is offline");
406
+ errorMethod(errorMessage);
407
+ return Promise.reject(errorMessage);
408
+ }
409
+ try {
410
+ const serializedData = serializerMethod(data);
411
+ await authenticationProvider?.authenticate(this);
412
+ const response = await Promise.race([
413
+ fetch(url, {
414
+ method,
415
+ credentials,
416
+ headers,
417
+ body: serializedData,
418
+ signal: abortController.signal,
419
+ }),
420
+ this.handleTimeout(timeout)
421
+ ]);
422
+ if (cancellationToken?.canceled) {
423
+ return Promise.reject(new Error("The request was canceled."));
424
+ }
425
+ if (response.status >= 200 && response.status < 300) {
426
+ const parsedResponse = await parserMethod(response);
427
+ successMethod(parsedResponse);
428
+ if (storageMode !== exports.StorageMode.NetworkOnly) {
429
+ await Cache.set(cacheKey || "", parsedResponse);
430
+ }
431
+ return parsedResponse;
432
+ }
433
+ lastError = new Error(response.statusText);
434
+ if (response.status === 401) {
435
+ await authenticationProvider?.authenticationFailed(this, response);
436
+ }
437
+ }
438
+ catch (err) {
439
+ lastError = err;
440
+ }
441
+ if (attempts < (retryAttempts || 0)) {
442
+ ++attempts;
443
+ await new Promise(resolve => setTimeout(resolve, retryDelay));
444
+ retryMethod(attempts);
445
+ return sendRequest();
446
+ }
447
+ errorMethod(lastError);
448
+ return Promise.reject(lastError);
449
+ };
450
+ return sendRequest();
451
+ }
452
+ async handleTimeout(timeout) {
453
+ timeout ??= 60000;
454
+ await new Promise((_, reject) => {
455
+ setTimeout(() => {
456
+ reject(new Error('Request timeout'));
457
+ }, timeout);
458
+ });
459
+ throw new Error('Request timeout');
460
+ }
461
+ }
462
+ exports.StorageMode = void 0;
463
+ (function (StorageMode) {
464
+ StorageMode[StorageMode["NetworkFirst"] = 0] = "NetworkFirst";
465
+ StorageMode[StorageMode["StorageFirst"] = 1] = "StorageFirst";
466
+ StorageMode[StorageMode["NetworkOnly"] = 2] = "NetworkOnly";
467
+ StorageMode[StorageMode["StorageAndUpdate"] = 3] = "StorageAndUpdate";
468
+ })(exports.StorageMode || (exports.StorageMode = {}));
469
+
470
+ exports.Cache = Cache;
471
+ exports.CancellationToken = CancellationToken;
472
+ exports.IndexedDbStorageProvider = IndexedDbStorageProvider;
473
+ exports.Request = Request;
474
+
475
+ }));
@@ -0,0 +1 @@
1
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("@jacraig/woodchuck")):"function"==typeof define&&define.amd?define(["exports","@jacraig/woodchuck"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).request={},e.woodchuck)}(this,(function(e,t){"use strict";class r{constructor(e,r,a){this.dbName=e,this.tables=r,this.version=a;const o=indexedDB.open(e,a);o.onupgradeneeded=e=>{if(this.database=e.target.result,this.database)for(const e of r)this.database.objectStoreNames.contains(e)&&this.database.deleteObjectStore(e),this.database.createObjectStore(e)},o.onsuccess=e=>{this.database=e.target.result},o.onerror=e=>{t.Logger.error("Failed to open the database:",e.target.error)}}openDatabase(){return new Promise(((e,r)=>{const a=indexedDB.open(this.dbName,this.version);a.onsuccess=t=>{this.database=t.target.result,e(this)},a.onerror=e=>{t.Logger.error("Failed to open the database:",e.target.error),r(new Error("Failed to open the database:"+e.target.error))}}))}add(e,r,a){return new Promise(((o,s)=>{if(!this.database)return t.Logger.error("Database connection is not open"),void s(new Error("Database connection is not open"));const i=this.database.transaction(e,"readwrite").objectStore(e).put(r,a);i.onsuccess=()=>{o()},i.onerror=e=>{t.Logger.error("Failed to add an object to the database: ",e.target.error),s(new Error("Failed to add an object to the database: "+e.target.error))}}))}remove(e,r){return new Promise(((a,o)=>{if(!this.database)return t.Logger.error("Database connection is not open"),void o(new Error("Database connection is not open"));const s=this.database.transaction(e,"readwrite").objectStore(e).delete(r);s.onsuccess=()=>{a()},s.onerror=e=>{t.Logger.error("Failed to remove an object from the database: ",e.target.error),o(new Error("Failed to remove an object from the database: "+e.target.error))}}))}getByKey(e,r){return new Promise(((a,o)=>{if(!this.database)return t.Logger.error("Database connection is not open"),void o(new Error("Database connection is not open"));const s=this.database.transaction(e,"readwrite").objectStore(e).get(r);s.onsuccess=e=>{a(e.target.result)},s.onerror=e=>{t.Logger.error("Failed to retrieve an object from the database: ",e.target.error),o(new Error("Failed to retrieve an object from the database: "+e.target.error))}}))}getKeys(e){return new Promise(((r,a)=>{if(!this.database)return t.Logger.error("Database connection is not open"),void a(new Error("Database connection is not open"));const o=this.database.transaction(e,"readwrite").objectStore(e).getAllKeys();o.onsuccess=e=>{r(e.target.result)},o.onerror=e=>{t.Logger.error("Failed to retrieve an object from the database: ",e.target.error),a(new Error("Failed to retrieve keys from the database: "+e.target.error))}}))}get(e,r){return new Promise(((a,o)=>{if(!this.database)return t.Logger.error("Database connection is not open"),void o(new Error("Database connection is not open"));const s=this.database.transaction(e,"readwrite").objectStore(e).get(r);s.onsuccess=e=>{a(e.target.result)},s.onerror=e=>{t.Logger.error("Failed to retrieve an object from the database: ",e.target.error),o(new Error("Failed to retrieve an object from the database: "+e.target.error))}}))}getAll(e,r){return new Promise(((a,o)=>{if(!this.database)return t.Logger.error("Database connection is not open"),void o(new Error("Database connection is not open"));const s=this.database.transaction(e,"readwrite").objectStore(e).getAll(r);s.onsuccess=e=>{a(e.target.result)},s.onerror=e=>{t.Logger.error("Failed to retrieve an object from the database: ",e.target.error),o(new Error("Failed to retrieve objects from the database: "+e.target.error))}}))}}class a{constructor(){this.database=new r("cacheStore",["cache","cacheEntryOptions"],1)}async clear(){await this.database.openDatabase();let e=await this.database.getAll("cacheEntryOptions"),t=await this.database.getAll("cache");for(let r=0;r<e.length;r++){let e=t[r];await this.remove(e.key)}return this}async getOptions(e){return await this.database.openDatabase(),this.database.getByKey("cacheEntryOptions",e)}async add(e,t,r){return await this.database.openDatabase(),await this.database.add("cache",e,t),await this.database.add("cacheEntryOptions",r,t),this}async remove(e){return await this.database.openDatabase(),await this.database.remove("cache",e),await this.database.remove("cacheEntryOptions",e),this}async get(e){return await this.database.openDatabase(),this.database.getByKey("cache",e)}async compact(){await this.database.openDatabase();let e=await this.database.getAll("cacheEntryOptions"),t=await this.database.getAll("cache"),r=(new Date).getTime();for(let a=0;a<e.length;a++){let o=e[a],s=t[a];0!=o.expirationTime&&(o.expirationTime<r&&await this.remove(s.key))}return this}}class o{constructor(){}static configure(e=new a){this.storageProvider??=globalThis.StorageProvider||e||new a,globalThis.StorageProvider=this.storageProvider}static async set(e,r,a={expirationTime:0,slidingExpirationTime:0,sliding:!1}){t.Logger.debug("Setting object in cache: ",{key:e,value:r,entryOptions:a}),this.configure(),await this.storageProvider.add(r,e,a)}static async get(e){t.Logger.debug("Getting object from cache: "+e),this.configure(),await this.storageProvider.compact();let r=await this.storageProvider.get(e);if(null==r)return r;let a=await this.storageProvider.getOptions(e);return a.sliding&&await this.set(e,r,{expirationTime:(new Date).getTime()+a.slidingExpirationTime,slidingExpirationTime:a.slidingExpirationTime,sliding:!0}),r}static async remove(e){t.Logger.debug("Removing object from cache: "+e),this.configure(),await this.storageProvider.remove(e)}static async clear(){t.Logger.debug("Clearing cache"),this.configure(),await this.storageProvider.clear()}}class s{constructor(r){this.options={method:"GET",url:"",headers:{},credentials:"same-origin",serializer:JSON.stringify,parser:e=>e.json(),success:e=>{t.Logger.debug("Request response from "+this.options.url+":",e)},error:e=>{t.Logger.error("Request error from "+this.options.url+":",e)},retry:e=>{t.Logger.debug("Request retry on "+this.options.url+":",{attempt:e})},storageMode:e.StorageMode.NetworkFirst,cacheKey:"",timeout:6e4,retryAttempts:3,retryDelay:1e3},this.abortController=null,this.options={...this.options,...r}}static get(e,t){return new s({method:"GET",url:e,data:t,cacheKey:e+JSON.stringify(t)}).withHeaders({Accept:"application/json"})}static post(t,r){return new s({method:"POST",url:t,data:r,cacheKey:t+JSON.stringify(r),storageMode:e.StorageMode.NetworkOnly}).withHeaders({"Content-Type":"application/json",Accept:"application/json"})}static put(t,r){return new s({method:"PUT",url:t,data:r,cacheKey:t+JSON.stringify(r),storageMode:e.StorageMode.NetworkOnly}).withHeaders({"Content-Type":"application/json",Accept:"application/json"})}static delete(t,r){return new s({method:"DELETE",url:t,data:r,cacheKey:t+JSON.stringify(r),storageMode:e.StorageMode.NetworkOnly}).withHeaders({Accept:"application/json"})}static ofType(e,t,r){return new s({method:e,url:t,data:r,cacheKey:t+JSON.stringify(r)}).withHeaders({Accept:"application/json"})}withAuthenticationProvider(e){return this.options.authenticationProvider=e,this}withHeaders(e){return this.options.headers={...this.options.headers,...e},this}withCancellationToken(e){return this.options.cancellationToken=e,this}withCredentials(e){return this.options.credentials=e,this}withSerializer(e){return this.options.serializer=e,this}withParser(e){return this.options.parser=e,this}onSuccess(e){return this.options.success=e??(e=>{t.Logger.debug("Request response:",e)}),this}onError(e){return this.options.error=e??(e=>{t.Logger.error("Request error:",e)}),this}onRetry(e){return this.options.retry=e??(e=>{t.Logger.debug("Request retry:",{attempt:e})}),this}withStorageMode(e){return this.options.storageMode=e,this}withCacheKey(e){return this.options.cacheKey=e,this}withTimeout(e){return this.options.timeout=e??6e4,this}withRetryAttempts(e){return this.options.retryAttempts=e,this}withRetryDelay(e){return this.options.retryDelay=e,this}abort(){return null==this.abortController||null==this.options.error||(this.abortController.abort(),this.options.error(new Error("The request was aborted."))),this}async send(){const{authenticationProvider:r,method:a,url:s,data:i,headers:n,credentials:c,serializer:d,parser:h,success:g,error:l,storageMode:u,cacheKey:b,timeout:p,retryAttempts:w,retryDelay:m,retry:y,cancellationToken:f}=this.options,v=new AbortController;this.abortController=v;let S=0,j=null,E=g||(e=>{t.Logger.debug("Request response from "+s+":",e)}),T=l||(e=>{t.Logger.error("Request error from "+s+":",e)}),P=d||JSON.stringify,D=h||(e=>e.json()),L=y||(e=>{t.Logger.debug("Request retry on "+s+":",{attempt:e})});const O=async()=>{if(u===e.StorageMode.StorageFirst||u===e.StorageMode.StorageAndUpdate){const t=await o.get(b||"");if(void 0!==t&&(E(t),u===e.StorageMode.StorageFirst))return t}if(!navigator.onLine){if(u===e.StorageMode.NetworkFirst){const e=await o.get(b||"");if(void 0!==e)return E(e),e}const t=new Error("System is offline");return T(t),Promise.reject(t)}try{const t=P(i);await(r?.authenticate(this));const d=await Promise.race([fetch(s,{method:a,credentials:c,headers:n,body:t,signal:v.signal}),this.handleTimeout(p)]);if(f?.canceled)return Promise.reject(new Error("The request was canceled."));if(d.status>=200&&d.status<300){const t=await D(d);return E(t),u!==e.StorageMode.NetworkOnly&&await o.set(b||"",t),t}j=new Error(d.statusText),401===d.status&&await(r?.authenticationFailed(this,d))}catch(e){j=e}return S<(w||0)?(++S,await new Promise((e=>setTimeout(e,m))),L(S),O()):(T(j),Promise.reject(j))};return O()}async handleTimeout(e){throw e??=6e4,await new Promise(((t,r)=>{setTimeout((()=>{r(new Error("Request timeout"))}),e)})),new Error("Request timeout")}}var i;e.StorageMode=void 0,(i=e.StorageMode||(e.StorageMode={}))[i.NetworkFirst=0]="NetworkFirst",i[i.StorageFirst=1]="StorageFirst",i[i.NetworkOnly=2]="NetworkOnly",i[i.StorageAndUpdate=3]="StorageAndUpdate",e.Cache=o,e.CancellationToken=class{constructor(){this.canceled=!1}},e.IndexedDbStorageProvider=a,e.Request=s}));
@@ -0,0 +1,33 @@
1
+ export interface CacheEntryOptions {
2
+ expirationTime: number;
3
+ slidingExpirationTime: number;
4
+ sliding: boolean;
5
+ }
6
+ export interface StorageProvider {
7
+ add(obj: any, key: string, options: CacheEntryOptions): Promise<StorageProvider>;
8
+ remove(key: string): Promise<StorageProvider>;
9
+ get(key: string): Promise<any>;
10
+ getOptions(key: string): Promise<CacheEntryOptions>;
11
+ compact(): Promise<StorageProvider>;
12
+ clear(): Promise<StorageProvider>;
13
+ }
14
+ export declare class IndexedDbStorageProvider implements StorageProvider {
15
+ private database;
16
+ constructor();
17
+ clear(): Promise<StorageProvider>;
18
+ getOptions(key: string): Promise<CacheEntryOptions>;
19
+ add(obj: any, key: string, options: CacheEntryOptions): Promise<StorageProvider>;
20
+ remove(key: string): Promise<StorageProvider>;
21
+ get(key: string): Promise<any>;
22
+ compact(): Promise<StorageProvider>;
23
+ }
24
+ export declare class Cache {
25
+ private constructor();
26
+ static configure(storageProvider?: StorageProvider): void;
27
+ private static storageProvider;
28
+ static set(key: string, value: any, entryOptions?: CacheEntryOptions): Promise<void>;
29
+ static get(key: string): Promise<any>;
30
+ static remove(key: string): Promise<void>;
31
+ static clear(): Promise<void>;
32
+ }
33
+ //# sourceMappingURL=Cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Cache.d.ts","sourceRoot":"","sources":["../../src/Cache.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,iBAAiB;IAE9B,cAAc,EAAE,MAAM,CAAC;IAEvB,qBAAqB,EAAE,MAAM,CAAC;IAE9B,OAAO,EAAE,OAAO,CAAC;CACpB;AAGD,MAAM,WAAW,eAAe;IAM5B,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;IAKjF,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;IAK9C,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IAK/B,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAKpD,OAAO,IAAI,OAAO,CAAC,eAAe,CAAC,CAAC;IAIpC,KAAK,IAAI,OAAO,CAAC,eAAe,CAAC,CAAC;CACrC;AAGD,qBAAa,wBAAyB,YAAW,eAAe;IAE5D,OAAO,CAAC,QAAQ,CAAqB;;IASxB,KAAK,IAAI,OAAO,CAAC,eAAe,CAAC;IAcjC,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC;IASnD,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,eAAe,CAAC;IAShF,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAU7C,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC;IAQ9B,OAAO,IAAI,OAAO,CAAC,eAAe,CAAC;CAiBnD;AASD,qBAAa,KAAK;IAEd,OAAO;WAGO,SAAS,CAAC,eAAe,GAAE,eAAgD,GAAG,IAAI;IAMhG,OAAO,CAAC,MAAM,CAAC,eAAe,CAAkB;WAO5B,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,YAAY,GAAE,iBAAmF,GAAG,OAAO,CAAC,IAAI,CAAC;WAS9I,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC;WAsB9B,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;WAQlC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAK7C"}
@@ -0,0 +1,15 @@
1
+ export declare class DatabaseConnection {
2
+ private dbName;
3
+ private tables;
4
+ private version;
5
+ private database;
6
+ constructor(dbName: string, tables: string[], version: number);
7
+ openDatabase(): Promise<DatabaseConnection>;
8
+ add(table: string, obj: any, key: IDBValidKey): Promise<void>;
9
+ remove(table: string, key: IDBValidKey): Promise<void>;
10
+ getByKey(table: string, key: IDBValidKey): Promise<any>;
11
+ getKeys(table: string): Promise<IDBValidKey[]>;
12
+ get(table: string, query: string): Promise<any>;
13
+ getAll(table: string, query?: string): Promise<any[]>;
14
+ }
15
+ //# sourceMappingURL=Database.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Database.d.ts","sourceRoot":"","sources":["../../src/Database.ts"],"names":[],"mappings":"AAGA,qBAAa,kBAAkB;IAQf,OAAO,CAAC,MAAM;IAAU,OAAO,CAAC,MAAM;IAAY,OAAO,CAAC,OAAO;IAN7E,OAAO,CAAC,QAAQ,CAAwB;gBAMpB,MAAM,EAAE,MAAM,EAAU,MAAM,EAAE,MAAM,EAAE,EAAU,OAAO,EAAE,MAAM;IA4B9E,YAAY,IAAI,OAAO,CAAC,kBAAkB,CAAC;IAoB3C,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAyB7D,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAyBtD,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC;IAyBvD,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAyB9C,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC;IA0B/C,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;CAqB/D"}
@@ -0,0 +1,62 @@
1
+ import { Cache, CacheEntryOptions, StorageProvider, IndexedDbStorageProvider } from "./Cache";
2
+ declare class CancellationToken {
3
+ canceled: boolean;
4
+ }
5
+ interface AuthenticationProvider {
6
+ authenticate(request: Request): Promise<void>;
7
+ authenticationFailed(request: Request, reason: Response): Promise<void>;
8
+ }
9
+ interface RequestOptions {
10
+ authenticationProvider?: AuthenticationProvider;
11
+ cacheKey?: string;
12
+ cancellationToken?: CancellationToken;
13
+ credentials?: RequestCredentials;
14
+ data?: any;
15
+ error?: (reason: any) => void;
16
+ headers?: Record<string, string>;
17
+ method: string;
18
+ parser?: (response: Response) => Promise<any>;
19
+ retry?: (attempt: number) => void;
20
+ retryAttempts?: number;
21
+ retryDelay?: number;
22
+ serializer?: (data: any) => string;
23
+ storageMode?: StorageMode;
24
+ success?: (response: any) => void;
25
+ timeout?: number;
26
+ url: string;
27
+ }
28
+ declare class Request {
29
+ private options;
30
+ private abortController;
31
+ constructor(options: RequestOptions);
32
+ static get(url: string, data?: any): Request;
33
+ static post(url: string, data?: any): Request;
34
+ static put(url: string, data?: any): Request;
35
+ static delete(url: string, data?: any): Request;
36
+ static ofType(method: string, url: string, data?: any): Request;
37
+ withAuthenticationProvider(authenticationProvider: AuthenticationProvider): this;
38
+ withHeaders(headers: Record<string, string>): this;
39
+ withCancellationToken(cancellationToken: CancellationToken): this;
40
+ withCredentials(credentials: RequestCredentials): this;
41
+ withSerializer(serializer: (data: any) => string): this;
42
+ withParser(parser: (response: Response) => Promise<any>): this;
43
+ onSuccess(callback: (response: any) => void): this;
44
+ onError(callback: (reason: any) => void): this;
45
+ onRetry(callback: (attempt: number) => void): this;
46
+ withStorageMode(storageMode: StorageMode): this;
47
+ withCacheKey(cacheKey: string): this;
48
+ withTimeout(timeout?: number): this;
49
+ withRetryAttempts(retryAttempts: number): this;
50
+ withRetryDelay(retryDelay: number): this;
51
+ abort(): this;
52
+ send(): Promise<any>;
53
+ private handleTimeout;
54
+ }
55
+ declare enum StorageMode {
56
+ NetworkFirst = 0,
57
+ StorageFirst = 1,
58
+ NetworkOnly = 2,
59
+ StorageAndUpdate = 3
60
+ }
61
+ export { CancellationToken, AuthenticationProvider, RequestOptions, Request, StorageMode, Cache, CacheEntryOptions, StorageProvider, IndexedDbStorageProvider };
62
+ //# sourceMappingURL=Request.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Request.d.ts","sourceRoot":"","sources":["../../src/Request.ts"],"names":[],"mappings":"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"}