Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
node_modules
*.js.map
lcov.info
20 changes: 15 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ A PokeAPI wrapper intended for browsers. Comes fully asynchronous, zero dependen
- [Example requests](#example-requests)
- [Configuration](#configuration)
- [Caching images](#caching-images)
- [Caching methods](#caching-methods)
- [Tests](#tests)
- [Endpoints](#endpoints)
- [Root Endpoints list](#root-endpoints-list)
Expand All @@ -31,7 +32,7 @@ console.log(await pokedex.getPokemonsList())
```html
<!-- Included in some HTML -->
<script type="module">
import {Pokedex} from "https://cdn.jsdelivr.net/gh/pokeapi/pokeapi-js-wrapper@beta/src/index.js"
import {Pokedex} from "https://cdn.jsdelivr.net/gh/pokeapi/pokeapi-js-wrapper@2.0.2/src/index.js"
const pokedex = await Pokedex.init();
const version = await pokedex.getVersionByName(1)
console.log(version)
Expand Down Expand Up @@ -65,7 +66,7 @@ pokedex.resource([
## Configuration

Pass an Object to `Pokedex.init()` in order to configure it. Available options: `protocol`, `hostName`, `versionPath`, `cache`, `timeout`(ms), and `cacheImages`.
Any option is optional :smile:. All the default values can be found [here](https://github.com/PokeAPI/pokeapi-js-wrapper/blob/master/src/config.js#L3-L10)
Any option is optional :smile:. All the default values can be found [here](https://github.com/PokeAPI/pokeapi-js-wrapper/blob/master/src/config.js#L3-L11)

```js
const customOptions = {
Expand All @@ -74,7 +75,8 @@ const customOptions = {
versionPath: "/api/v2/",
cache: true,
timeout: 5 * 1000, // 5s
cacheImages: true
cacheImages: true,
swLocation: '/'
}
const pokedex = await Pokedex.init(customOptions);
```
Expand All @@ -86,14 +88,22 @@ Pokeapi.co serves its Pokemon images through [Github](https://github.com/PokeAPI
`pokeapi-js-wrapper` enables browsers to cache all these images by:

1. enabling the config parameter `cacheImages`
2. serving [our service worker](https://raw.githubusercontent.com/PokeAPI/pokeapi-js-wrapper/master/src/pokeapi-js-wrapper-sw.js) from the root of your project
2. serving [our service worker](https://raw.githubusercontent.com/PokeAPI/pokeapi-js-wrapper/master/test/pokeapi-js-wrapper-sw.js) from the root of your project

In this way when `pokeapi-js-wrapper`'s `Pokedex` is initialized it will install and start the Service Worker you are serving at the root of your server. The Service Worker will intercept all the calls your HTML/CSS/JS are making to get PokeAPI's images and will cache them.

It's fundamental that you download the Service Worker [we provide](https://raw.githubusercontent.com/PokeAPI/pokeapi-js-wrapper/master/src/pokeapi-js-wrapper-sw.js)_(Right Click + Save As)_ and you serve it from the root of your project/server. Service Workers in fact cannot be installed from a domain different than yours.

A [basic example](https://github.com/PokeAPI/pokeapi-js-wrapper/blob/master/test/example-sw.html) is hosted [here](https://pokeapi.github.io/pokeapi-js-wrapper/test/example-sw.html).

### Caching methods

```js
await P.getCacheLength() // Get how many objects are cached
await P.clearCache() // Remove all entries
await P.invalidateCache() // Remove only stale entries
```

## Tests

`pokeapi-js-wrapper` can be tested using two strategies. One is with Node, since this package works with Node, and the other with a browser.
Expand All @@ -102,7 +112,7 @@ A [basic example](https://github.com/PokeAPI/pokeapi-js-wrapper/blob/master/test
npm test
```

Or open `/test/test.html` in your browser. A live version can be found at [`gh-pages`](https://pokeapi.github.io/pokeapi-js-wrapper/test/test.html)
Or open `test/test.html` in your browser after serving the project with `npm run serve`. A live version can be found on the project [`gh-pages`](https://pokeapi.github.io/pokeapi-js-wrapper/test/test.html)

## Endpoints

Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "pokeapi-js-wrapper",
"version": "2.0.1",
"version": "2.0.2",
"description": "An API wrapper for PokeAPI",
"main": "src/index.js",
"type": "module",
Expand Down
4 changes: 4 additions & 0 deletions src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ class Config {
this.timeout = 10 * 1000 // 2 seconds
this.cache = true
this.cacheImages = false
this.swLocation = '/'

if (config.hasOwnProperty('protocol')) {
this.protocol = config.protocol
Expand All @@ -33,6 +34,9 @@ class Config {
if (config.hasOwnProperty('cacheImages')) {
this.cacheImages = config.cacheImages
}
if (config.hasOwnProperty('swLocation')) {
this.swLocation = config.swLocation
}
}
}
export { Config }
76 changes: 61 additions & 15 deletions src/getter.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,34 @@ import { log, canUseCache } from './utils.js'

var db

function openDB(config) {
function openCache(config) {
if (config.cache && typeof window !== 'undefined') {
const request = window.indexedDB.open("pokeapi-js-wrapper", 3);
const request = window.indexedDB.open("pokeapi-js-wrapper", 8);
return new Promise((resolve, reject) => {
request.onerror = (event) => {
log('IndexedDB not available')
reject()
}
request.onupgradeneeded = (event) => {
db = event.target.result;
log('db opened and cache created')
db.createObjectStore("cache", { autoIncrement: false });
resolve(db)
const db = event.target.result;
const transaction = event.target.transaction;
let objectStore;

if (!db.objectStoreNames.contains('cache')) {
objectStore = db.createObjectStore("cache", { autoIncrement: false });
log('Object store "cache" created');
} else {
objectStore = transaction.objectStore("cache");
}

if (!objectStore.indexNames.contains("deploy_date_index")) {
objectStore.createIndex("deploy_date_index", "meta.deploy_date", { unique: false });
log('Index "deploy_date_index" created');
}
}
request.onsuccess = (event) => {
log('db opened')
db = event.target.result;
log('db opened')
resolve(db)
}
request.onversionchange = (event) => {
Expand All @@ -43,8 +54,11 @@ function getFromDB(objectStore, url) {

async function loadResource(config, url) {
if (! url.includes('://')) {
url = url.replace(/^\//, '');
url = `${config.protocol}://${config.hostName}/${url}`
if (url.startsWith('/api/v2/')) {
url = `${config.protocol}://${config.hostName}${url}`
} else if (!url.includes('://')) {
url = `${config.protocol}://${config.hostName}${config.versionPath}${url}`
}
}
if (canUseCache(config, db)) {
const transaction = db.transaction("cache", "readonly");
Expand All @@ -66,10 +80,14 @@ async function loadUrl(config, url) {
const body = await response.json()
if (response.status === 200) {
if (canUseCache(config, db)) {
const deploy_date = parseInt(response.headers.get('X-PokeAPI-Deploy-Date'))
body.meta = { deploy_date }
const transaction = db.transaction("cache", "readwrite");
const objectStore = transaction.objectStore("cache");
const request = objectStore.add(body, url)
request.onsuccess = () => log(`object cached ${url}`);
request.onsuccess = () => {
log(`object cached ${url}`);
}
request.onerror = () => {
log(request.error)
}
Expand All @@ -79,7 +97,7 @@ async function loadUrl(config, url) {
return body
}

function sizeDB(config) {
function sizeCache(config) {
if (canUseCache(config, db)) {
return new Promise((resolve, reject) => {
const transaction = db.transaction("cache", "readwrite");
Expand All @@ -89,11 +107,39 @@ function sizeDB(config) {
request.onerror = () => reject(request.error);
});
} else {
return Promise.reject()
return Promise.reject(new Error('Cache not available'))
}
}

async function invalidateCache(config) {
if (canUseCache(config, db)) {
const meta = await loadResource({ ...config, cache: false }, 'meta');
const upstream_deploy_date = parseInt(meta.deploy_date);

return new Promise((resolve, reject) => {
const transaction = db.transaction("cache", "readwrite");
const objectStore = transaction.objectStore("cache");
const index = objectStore.index("deploy_date_index");

const range = IDBKeyRange.upperBound(upstream_deploy_date, true);
const request = index.getAllKeys(range);

request.onsuccess = () => {
const keys = request.result;
keys.forEach(pk => {
objectStore.delete(pk);
log(`invalidated ${pk}`);
});
resolve(true);
};
request.onerror = () => reject(new Error(request.error));
});
} else {
throw new Error('cache not available');
}
}

function clearDB(config) {
function clearCache(config) {
if (canUseCache(config, db)) {
return new Promise((resolve, reject) => {
const transaction = db.transaction("cache", "readwrite");
Expand All @@ -103,8 +149,8 @@ function clearDB(config) {
request.onerror = () => reject(request.error);
});
} else {
return Promise.reject()
return Promise.reject(new Error('Cache not available'))
}
}

export { loadResource, openDB, sizeDB, clearDB }
export { loadResource, openCache, sizeCache, clearCache, invalidateCache }
20 changes: 12 additions & 8 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import endpoints from './endpoints.json' with { type: "json" }
import rootEndpoints from './rootEndpoints.json' with { type: "json" }
import { loadResource, openDB, sizeDB, clearDB } from './getter.js'
import { loadResource, openCache, sizeCache, clearCache, invalidateCache } from './getter.js'
import { Config } from './config.js'

export class Pokedex {
Expand All @@ -18,7 +18,7 @@ export class Pokedex {

// if the user has submitted a Name or an ID, return the JSON promise
if (typeof input === 'number' || typeof input === 'string') {
return loadResource(this.config, `${this.config.versionPath}${endpoint[2].replace(':id', input)}`)
return loadResource(this.config, `${endpoint[2].replace(':id', input)}`)
}

// if the user has submitted an Array
Expand All @@ -44,19 +44,19 @@ export class Pokedex {
limit = config.limit
}
}
return loadResource(this.config, `${this.config.versionPath}${rootEndpoint[1]}?limit=${limit}&offset=${offset}`)
return loadResource(this.config, `${rootEndpoint[1]}?limit=${limit}&offset=${offset}`)
}
this[rootEndpoint[0]] = this[rootEndpointFullName]
})

if (this.config.cacheImages) {
import('./installSW.js').then(module=>module.installSW())
import('./installSW.js').then(module=>module.installSW(this.config.swLocation))
}
}

static async init(config) {
config = new Config(config)
await openDB(config)
await openCache(config)
return new Pokedex(config)
}

Expand All @@ -65,11 +65,15 @@ export class Pokedex {
}

getCacheLength() {
return sizeDB(this.config)
return sizeCache(this.config)
}

clearCache() {
return clearDB(this.config)
return clearCache(this.config)
}

invalidateCache() {
return invalidateCache(this.config)
}

resource(path) {
Expand All @@ -85,7 +89,7 @@ export class Pokedex {

function mapResources(config, endpoint, inputs) {
return inputs.map(input => {
return loadResource(config, `${config.versionPath}${endpoint[2].replace(':id', input)}`)
return loadResource(config, `${endpoint[2].replace(':id', input)}`)
})
}

Expand Down
4 changes: 2 additions & 2 deletions src/installSW.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { log } from './utils.js'

export function installSW() {
export function installSW(swLocation) {
if (navigator && window && 'serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('./pokeapi-js-wrapper-sw.js', { scope: './' })
navigator.serviceWorker.register(`${swLocation}pokeapi-js-wrapper-sw.js`, { scope: './' })
.catch(error => {
log('SW installation failed with the following error:')
log(error)
Expand Down
2 changes: 1 addition & 1 deletion test/cdn.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
// var PJSW_DEBUG=1
</script>
<script type="module">
//import {Pokedex} from "https://cdn.jsdelivr.net/gh/pokeapi/pokeapi-js-wrapper@beta/src/index.js"
//import {Pokedex} from "https://cdn.jsdelivr.net/gh/pokeapi/pokeapi-js-wrapper/src/index.js"
import {Pokedex} from "../src/index.js"
const pokedex = await Pokedex.init();
const version = await pokedex.getVersionByName(1)
Expand Down
2 changes: 1 addition & 1 deletion test/example-sw.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<title>SW test</title>
<script type="module">
import {Pokedex} from '../src/index.js'
const P = await Pokedex.init({ cacheImages: true });
const P = await Pokedex.init({ cacheImages: true, swLocation: '/pokeapi-js-wrapper/test/' });
</script>
</head>

Expand Down
File renamed without changes.
Loading