-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit ab9b477
Showing
11 changed files
with
9,073 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
name: CI | ||
on: [push] | ||
jobs: | ||
build: | ||
name: Build, lint, and test on Node ${{ matrix.node }} and ${{ matrix.os }} | ||
|
||
runs-on: ${{ matrix.os }} | ||
strategy: | ||
matrix: | ||
node: ['10.x', '12.x', '14.x'] | ||
os: [ubuntu-latest, windows-latest, macOS-latest] | ||
|
||
steps: | ||
- name: Checkout repo | ||
uses: actions/checkout@v2 | ||
|
||
- name: Use Node ${{ matrix.node }} | ||
uses: actions/setup-node@v1 | ||
with: | ||
node-version: ${{ matrix.node }} | ||
|
||
- name: Install deps and build (with cache) | ||
uses: bahmutov/npm-install@v1 | ||
|
||
- name: Lint | ||
run: yarn lint | ||
|
||
- name: Test | ||
run: yarn test --ci --coverage --maxWorkers=2 | ||
|
||
- name: Build | ||
run: yarn build |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
name: size | ||
on: [pull_request] | ||
jobs: | ||
size: | ||
runs-on: ubuntu-latest | ||
env: | ||
CI_JOB_NUMBER: 1 | ||
steps: | ||
- uses: actions/checkout@v1 | ||
- uses: andresz1/size-limit-action@v1 | ||
with: | ||
github_token: ${{ secrets.GITHUB_TOKEN }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
*.log | ||
.DS_Store | ||
node_modules | ||
dist |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
MIT License | ||
|
||
Copyright (c) 2021 Peter Olom | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
# Hookstate Persist | ||
|
||
Simple and configurable peristence plugin for [Hookstate state management](https://hookstate.js.org/). | ||
|
||
> Works on React Native and Web. | ||
|
||
## Installation | ||
|
||
To install, run: | ||
|
||
```bash | ||
yarn add hookstate-persist | ||
``` | ||
|
||
```bash | ||
npm install hookstate-persist | ||
``` | ||
|
||
|
||
## Usage | ||
```Typescript | ||
// React Native | ||
import {createState} from '@hookstate/core'; | ||
import CreatePersistor, {PersistorWrapper} from 'hookstate-persist'; | ||
import AsyncStorage from '@react-native-async-storage/async-storage'; | ||
import {Product} from '../utils/types'; | ||
|
||
// for map/tree like state, wrap root state with Persist wrapper | ||
// this gives you the benefit of validating the hydrateTime value | ||
const wrapped = PersistorWrapper({ | ||
cart: [] as Array<Product>, | ||
user: {} as User, | ||
location: '', | ||
}); | ||
const store = createState(wrapped); | ||
|
||
// create the peristor pluging | ||
const persistor = CreatePersistor({ | ||
key: '@myStore', // store name | ||
engine: AsyncStorage, // storage engine which implements getItem & setItem | ||
}); | ||
|
||
// attach plugin and you're done | ||
store.attach(persistor); | ||
|
||
|
||
``` | ||
|
||
|
||
### Peristor options | ||
|
||
```js | ||
CreatePersistor { | ||
key: string, // key in storage | ||
engine: StoreEngine, // engine; any type of storage that implements "getItem" and "setItem" eg AsyncStorage or localStorage | ||
whitelist?: Array<string>, // property names of items in state you want to persist (optional) | ||
blacklist?: Array<string>, // property names of items in state you want excluded from storage(optional) | ||
} | ||
|
||
``` | ||
|
||
## API | ||
|
||
```js | ||
export default store; | ||
CreatePersistor(config: ICreatePersistor) // creates the peristence plugin | ||
PersistorWrapper(state: State<S>) // Wrapps the root state. Do not use if root state is not map/tree like | ||
``` | ||
|
||
## Contiributions | ||
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. | ||
|
||
## License | ||
|
||
[MIT](https://choosealicense.com/licenses/mit/) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
{ | ||
"version": "0.1.0", | ||
"license": "MIT", | ||
"main": "dist/index.js", | ||
"typings": "dist/index.d.ts", | ||
"files": [ | ||
"dist", | ||
"src" | ||
], | ||
"engines": { | ||
"node": ">=10" | ||
}, | ||
"scripts": { | ||
"start": "tsdx watch", | ||
"build": "tsdx build", | ||
"test": "tsdx test", | ||
"lint": "tsdx lint", | ||
"prepare": "tsdx build", | ||
"size": "size-limit", | ||
"analyze": "size-limit --why" | ||
}, | ||
"peerDependencies": {}, | ||
"husky": { | ||
"hooks": { | ||
"pre-commit": "tsdx lint" | ||
} | ||
}, | ||
"prettier": { | ||
"printWidth": 80, | ||
"semi": true, | ||
"singleQuote": true, | ||
"trailingComma": "es5" | ||
}, | ||
"keywords": [ | ||
"react-native", | ||
"persist", | ||
"persistence", | ||
"hookstate", | ||
"hydrate", | ||
"store", | ||
"plugin", | ||
"redux" | ||
], | ||
"name": "hookstate-persist", | ||
"author": "Peter Olom", | ||
"homepage": "https://github.com/peter-olom/hookstate-persist", | ||
"repository": "https://github.com/peter-olom/hookstate-persist", | ||
"module": "dist/hookstate-persist.esm.js", | ||
"size-limit": [ | ||
{ | ||
"path": "dist/hookstate-persist.cjs.production.min.js", | ||
"limit": "10 KB" | ||
}, | ||
{ | ||
"path": "dist/hookstate-persist.esm.js", | ||
"limit": "10 KB" | ||
} | ||
], | ||
"devDependencies": { | ||
"@size-limit/preset-small-lib": "^4.9.2", | ||
"husky": "^4.3.8", | ||
"react": "^17.0.1", | ||
"size-limit": "^4.9.2", | ||
"tsdx": "^0.14.1", | ||
"tslib": "^2.1.0", | ||
"typescript": "^4.1.3" | ||
}, | ||
"dependencies": { | ||
"@hookstate/core": "^3.0.4" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
import {StateValueAtRoot, State} from '@hookstate/core'; | ||
|
||
// should be global variable | ||
const PersistPluginId = Symbol('PersistPluginId'); | ||
|
||
export interface getItem { | ||
(key: string): Promise<string | null> | string; | ||
} | ||
export interface setItem { | ||
(key: string, value: string): Promise<void> | void; | ||
} | ||
|
||
export interface StoreEngine { | ||
getItem: getItem; | ||
setItem: setItem; | ||
} | ||
|
||
export interface ICreatePersistor { | ||
key: string; | ||
engine: StoreEngine; | ||
blacklist?: Array<string>; | ||
whitelist?: Array<string>; | ||
} | ||
function CreatePersistor({key, engine, blacklist, whitelist}: ICreatePersistor) { | ||
return function PersistPlugin() { | ||
return { | ||
id: PersistPluginId, | ||
init: (s: State<StateValueAtRoot>) => { | ||
const engineResponse = engine.getItem(key); | ||
// hydate the state from async storage | ||
Promise.resolve(engineResponse).then((res) => { | ||
if (res) { | ||
const store = JSON.parse(res); | ||
s.merge(store); | ||
} | ||
s.merge({hydateTime: Date.now()}); | ||
}); | ||
|
||
return { | ||
onSet: (data: any) => { | ||
// copy data.state and operate on it | ||
const state = {...data.state}; | ||
if (typeof state == 'object') { | ||
if (blacklist) { | ||
// delete said keys before peristing only if object | ||
blacklist.forEach((item) => { | ||
delete state[item]; | ||
}); | ||
} else if (whitelist) { | ||
// create a blacklist and delete them | ||
const manualBlacklist = Object.keys(state).filter((item) => whitelist.indexOf(item) < 0); | ||
manualBlacklist.forEach((item) => { | ||
delete state[item]; | ||
}); | ||
} | ||
delete state['hydateTime']; | ||
} | ||
engine.setItem(key, JSON.stringify(state)); | ||
}, | ||
}; | ||
}, | ||
}; | ||
}; | ||
} | ||
|
||
function PersistorWrapper<S>(state: S) { | ||
if (typeof state == 'object') { | ||
return { | ||
...state, | ||
hydateTime: null, | ||
}; | ||
} | ||
throw new Error('This helper only works on object trees/maps'); | ||
} | ||
|
||
export default CreatePersistor; | ||
|
||
export {PersistorWrapper}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import {createState} from '@hookstate/core'; | ||
import CreatePersistor, {PersistorWrapper} from '../src/index'; | ||
import storageEngine from './storageEngine'; | ||
|
||
// create store and test plugin | ||
const wrapped = PersistorWrapper({ user: {}, location: '' }); | ||
const store = createState(wrapped); | ||
|
||
const persistor = CreatePersistor({ | ||
key: 'testState', | ||
engine: storageEngine, | ||
}); | ||
store.attach(persistor); | ||
|
||
const hydrator = () => { | ||
return new Promise<Record<string, unknown>>((resolve, reject) => { | ||
setTimeout(() => { | ||
const state = store.get(); | ||
if (state.hydateTime) { | ||
resolve(state); | ||
} else { | ||
reject('Failed to hydrate state'); | ||
} | ||
}, 1000) | ||
}); | ||
} | ||
|
||
|
||
describe('The store was loaded from storage ', () => { | ||
it('loaded storage correctly', async () => { | ||
const res = await hydrator(); | ||
expect(res).toMatchObject({user:{name:"Peter Olom",languages:["TypeScript","C#"]},location:"Earth"}); | ||
}); | ||
}); | ||
|
||
describe('The hydration time exists', () => { | ||
it('hydrate time check', async () => { | ||
const res = await hydrator(); | ||
expect(res).toHaveProperty('hydateTime'); | ||
}); | ||
}); | ||
|
||
describe('The storage was updated', () => { | ||
it('Location updated to Mars', async () => { | ||
// update location | ||
store.location.set('Mars'); | ||
// read storage directly | ||
const res = JSON.parse(storageEngine.getItem('testState')); | ||
expect(res.location).toBe('Mars'); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
const storage: Record<string, any> = { | ||
"testState": '{"user":{"name":"Peter Olom","languages":["TypeScript","C#"]},"location":"Earth"}' | ||
}; | ||
const storageEngine = { | ||
storage: storage, | ||
getItem: function (key: string) { | ||
return this.storage[key]; | ||
}, | ||
setItem: function (key: string, val: string) { | ||
this.storage[key] = val; | ||
|
||
} | ||
}; | ||
|
||
export default storageEngine; |
Oops, something went wrong.