diff --git a/.gitignore b/.gitignore index ddc75f9..28239ad 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,4 @@ node_modules/ *.log demos/web/.next yarn.lock -demos/web/package-lock.json +demos/web/package-lock.json \ No newline at end of file diff --git a/README.md b/README.md index daedafd..a1561ea 100644 --- a/README.md +++ b/README.md @@ -215,6 +215,7 @@ Key | Type | Description | Example `ignoreHeadersWhenCaching` | `boolean` | Optional, your requests will be cached independently from the headers you sent. | `true` (defaults to `false`) `capServices` | `boolean` | Optional, enable capping for every service, defaults to `false`, see [limiting the size of your cache](#limiting-the-size-of-your-cache) | `true` (defaults to `false`) `capLimit` | `number` | Optional quantity of cached items for each service, see [limiting the size of your cache](#limiting-the-size-of-your-cache) | `100` (defaults to `50`) +`networkFirst` | `boolean` | Optional, changes the flow by performing the network call before checking the cache | `true` (defaults to `false`) ## Services options @@ -234,6 +235,7 @@ Key | Type | Description | Example `capLimit` | `number` | Optional quantity of cached items for this specific service, defaults to `50`, see [limiting the size of your cache](#limiting-the-size-of-your-cache) | `42` (defaults to `50`) `ignoreHeadersWhenCaching` | `boolean` | Optional, your requests will be cached independently from the headers you sent. | `true` (defaults to `false`) `rawData` | `boolean` | Disables JSON parsing from your network requests, useful if you want to fetch XML or anything else from your api | `true` (defaults to `false`) +`networkFirst` | `boolean` | Optional, changes the flow by performing the network call before checking the cache. It overrides the one set in your API option | `true` (defaults to `false`) ## Fetch options diff --git a/dist/drivers/sqlite.js b/dist/drivers/sqlite.js index eff21fa..3929a02 100644 --- a/dist/drivers/sqlite.js +++ b/dist/drivers/sqlite.js @@ -22,8 +22,8 @@ var __generator = (this && this.__generator) || function (thisArg, body) { function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { - if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t; - if (y = 0, t) op = [0, t.value]; + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; @@ -44,7 +44,7 @@ var __generator = (this && this.__generator) || function (thisArg, body) { }; var _this = this; Object.defineProperty(exports, "__esModule", { value: true }); -exports.default = function (SQLite, options) { return __awaiter(_this, void 0, void 0, function () { +exports.default = (function (SQLite, options) { return __awaiter(_this, void 0, void 0, function () { var db, err_1; return __generator(this, function (_a) { switch (_a.label) { @@ -71,7 +71,7 @@ exports.default = function (SQLite, options) { return __awaiter(_this, void 0, v case 4: return [2 /*return*/]; } }); -}); }; +}); }); function getItem(db) { return function (key) { return new Promise(function (resolve, reject) { diff --git a/dist/index.js b/dist/index.js index 4878185..7a3c7ee 100644 --- a/dist/index.js +++ b/dist/index.js @@ -22,8 +22,8 @@ var __generator = (this && this.__generator) || function (thisArg, body) { function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { - if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t; - if (y = 0, t) op = [0, t.value]; + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; @@ -57,7 +57,7 @@ var DEFAULT_API_OPTIONS = { cachePrefix: 'APIPelineCache', ignoreHeadersWhenCaching: false, capServices: false, - capLimit: 50 + capLimit: 50, }; var DEFAULT_SERVICE_OPTIONS = { method: 'GET', @@ -66,7 +66,7 @@ var DEFAULT_SERVICE_OPTIONS = { }; var HTTP_METHODS = ['GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'CONNECT', 'OPTIONS', 'TRACE']; exports.drivers = { sqliteDriver: sqlite_1.default }; -var APIpeline = (function () { +var APIpeline = /** @class */ (function () { function APIpeline(options, services, driver) { this._APIServices = {}; this._warnedAboutMissingDriver = false; @@ -114,7 +114,7 @@ var APIpeline = (function () { return [4 /*yield*/, this._getCachedData(service, requestId, fullPath, shouldUseCache)]; case 3: cachedData = _c.sent(); - if (cachedData.success && cachedData.fresh) { + if ((!this._APIOptions.networkFirst && !options.networkFirst && !serviceDefinition.networkFirst) && cachedData.success && cachedData.fresh) { this._log("Using fresh cache for " + fullPath); return [2 /*return*/, cachedData.data]; } @@ -129,7 +129,7 @@ var APIpeline = (function () { // If the network request fails, return the cached data if it's valid, a throw an error if (!res.success) { if (cachedData.success && cachedData.data) { - this._log("Using stale cache for " + fullPath + " (network request failed)"); + this._log("Using " + (cachedData.fresh ? 'fresh' : 'stale') + " cache for " + fullPath + " (network request failed)"); return [2 /*return*/, cachedData.data]; } else { @@ -192,8 +192,8 @@ var APIpeline = (function () { }; APIpeline.prototype.clearCache = function (service) { return __awaiter(this, void 0, void 0, function () { - var _this = this; var keysToRemove, _a, _b, _i, serviceName, keysForService, err_3; + var _this = this; return __generator(this, function (_c) { switch (_c.label) { case 0: @@ -506,8 +506,8 @@ var APIpeline = (function () { */ APIpeline.prototype._getAllKeysForService = function (service) { return __awaiter(this, void 0, void 0, function () { - var _this = this; var keys, serviceDictionaryKey, dictionary, dictionaryKeys, err_8; + var _this = this; return __generator(this, function (_a) { switch (_a.label) { case 0: @@ -588,7 +588,7 @@ var APIpeline = (function () { }); }; APIpeline.prototype._buildRequestId = function (serviceDefinition, fullPath, fetchHeaders, mergedOptions, // fully merged options - fetchOptions // fetch options + fetchOptions // fetch options ) { var ignoreHeadersWhenCaching = this._APIOptions.ignoreHeadersWhenCaching || serviceDefinition.ignoreHeadersWhenCaching || diff --git a/package.json b/package.json index 01e303e..59e8a16 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "apipeline", - "version": "3.0.1", + "version": "3.1.0", "description": "Feature-rich and pluggable offline-first API wrapper for the browser, node.js and react-native. Easily wire-up your API and make your app work offline in minutes !", "main": "./dist/index.js", "types": "./src/index.d.ts", diff --git a/src/index.ts b/src/index.ts index fd3cdf7..7c98c46 100644 --- a/src/index.ts +++ b/src/index.ts @@ -26,7 +26,7 @@ const DEFAULT_API_OPTIONS = { cachePrefix: 'APIPelineCache', ignoreHeadersWhenCaching: false, capServices: false, - capLimit: 50 + capLimit: 50, }; const DEFAULT_SERVICE_OPTIONS: IAPIService = { @@ -100,7 +100,7 @@ export default class APIpeline { } const cachedData = await this._getCachedData(service, requestId, fullPath, shouldUseCache); - if (cachedData.success && cachedData.fresh) { + if ((!this._APIOptions.networkFirst && !options.networkFirst && !serviceDefinition.networkFirst) && cachedData.success && cachedData.fresh) { this._log(`Using fresh cache for ${fullPath}`); return cachedData.data; } @@ -115,7 +115,7 @@ export default class APIpeline { // If the network request fails, return the cached data if it's valid, a throw an error if (!res.success) { if (cachedData.success && cachedData.data) { - this._log(`Using stale cache for ${fullPath} (network request failed)`); + this._log(`Using ${cachedData.fresh ? 'fresh' : 'stale'} cache for ${fullPath} (network request failed)`); return cachedData.data; } else { throw new Error(`Cannot fetch data for ${service} online, no cache either.`); diff --git a/src/interfaces.ts b/src/interfaces.ts index a70d577..82cf379 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -15,6 +15,7 @@ export interface IAPIOptions { ignoreHeadersWhenCaching?: boolean; capServices?: boolean; capLimit?: number; + networkFirst?: boolean; } export interface IAPIService { @@ -30,6 +31,7 @@ export interface IAPIService { capService?: boolean; capLimit?: number; rawData?: boolean; + networkFirst?: boolean; } export interface IAPIServices {