├── .babelrc ├── .eslintignore ├── .eslintrc.json ├── .flowconfig ├── .gitignore ├── .npmignore ├── README.md ├── bin └── async-storage-repl ├── example ├── .babelrc ├── .flowconfig ├── .gitignore ├── .watchmanconfig ├── App.js ├── App.test.js ├── README.md ├── app.json ├── async-storage-repl └── package.json ├── flow-typed └── global.js ├── package.json ├── src ├── node │ ├── RNAsyncStorage.js │ ├── preload.js │ └── server.js └── rn │ ├── AsyncStorageREPL.js │ └── WebSocketClient.js └── test.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "stage-2"], 3 | "plugins": [ 4 | ["flow-runtime", { 5 | "assert": true, 6 | "annotate": true 7 | }], 8 | "transform-decorators-legacy", 9 | "transform-flow-strip-types" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | flow-typed/* 2 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "extends": "airbnb-base", 4 | "globals": { 5 | "_ayncStorageWebSocketServer": true, 6 | "WebSocket": true, 7 | "MessageEvent": true, 8 | "CloseEvent": true, 9 | "describe": true, 10 | "beforeEach": true, 11 | "context": true, 12 | "it": true 13 | }, 14 | "rules": { 15 | "import/no-extraneous-dependencies": 0, 16 | "no-underscore-dangle": 0, 17 | "no-plusplus": 0, 18 | "no-prototype-builtins": 0, 19 | "import/no-unresolved": 0, 20 | "import/extensions": 0, 21 | "no-console": ["error", { "allow": ["info", "warn", "error"] }] 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | .*/dist/.* 3 | .*/example/src/.* 4 | .*/node_modules/react-native/.* 5 | .*/node_modules/babel-plugin-flow-runtime/.* 6 | .*/node_modules/reqwest/tests/.* 7 | 8 | [include] 9 | 10 | [libs] 11 | 12 | [options] 13 | 14 | [version] 15 | ^0.61.0 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | yarn.lock 2 | node_modules 3 | sandbox 4 | dist 5 | example/src 6 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .babelrc 3 | src 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AsyncStorageREPL 2 | 3 | AsyncStorageRepl provides you to access remote ReactNative application's AsyncStorage from your node REPL. 4 | 5 | ![gif](https://cloud.githubusercontent.com/assets/4954534/26222189/09924ee2-3c54-11e7-8247-ed15afa7cd31.gif) 6 | 7 | ``` 8 | npm i async-storage-repl -D 9 | ``` 10 | 11 | # BasicUsage 12 | 13 | 1. Write bellow code in your ReactNative Application. 14 | 15 | ```js 16 | import AsyncStorageREPL from 'async-storage-repl'; 17 | AsyncStorageREPL().connect(); 18 | ``` 19 | 20 | 2. Start node REPL. 21 | 22 | ```sh 23 | ./node_modules/.bin/async-storage-repl 24 | ``` 25 | 26 | 3. Let's get access your ReactNative application's storage from your node REPL. 27 | 28 | ```sh 29 | $ ./node_modules/.bin/async-storage-repl 30 | > RNAsyncStorage.getItem('item1') 31 | null 32 | > RNAsyncStorage.setItem('item1', 'nice value!') 33 | null 34 | > RNAsyncStorage.getItem('item1') 35 | 'nice value!' 36 | > RNAsyncStorage.getAllKeys() 37 | [ 'reduxPersist:timeline', 38 | 'item1', 39 | 'reduxPersist:auth', 40 | 'reduxPersist:nav' ] 41 | ``` 42 | 43 | # Example 44 | 45 | [example](/example) 46 | 47 | # API 48 | 49 | ## AsyncStorage APIs 50 | AsyncStorageREPL provides RNAsyncStorage on your node REPL as a global object. 51 | You can access [AsyncStorage's all APIs](https://facebook.github.io/react-native/docs/asyncstorage.html) via this object. 52 | 53 | - getAllKeys(): string[] 54 | - getItem(key: string) 55 | - setItem(key: string, value: string) 56 | - removeItem(key: string) 57 | - mergeItem(key: string, value: string) 58 | - clear() 59 | - flushGetRequests() 60 | - multiGet(keys: string[]) 61 | - multiSet(keyValuePairs: string[][]) 62 | - multiRemove(keys: string[]) 63 | - multiMerge(keyValuePairs: string[][]) 64 | 65 | AsyncStorageREPL's methods args are guaranteed type-safe by [flow-runtime](https://codemix.github.io/flow-runtime/). 66 | 67 | ```sh 68 | > RNAsyncStorage.getItem(1) 69 | RuntimeTypeError: key must be a string 70 | 71 | Expected: string 72 | 73 | Actual: number 74 | 75 | ``` 76 | 77 | ## dump & load 78 | 79 | You can save & load RN Application AsyncStorage data. 80 | 81 | `dump()` provides you getting dump. 82 | `load(string[][])` provides you loading dump. 83 | 84 | ```sh 85 | > const data = RNAsyncStorage.dump() 86 | undefined 87 | > data 88 | [ [ 'comments', '["foo","bar","baz"]' ] ] 89 | > fs.writeSync('./dump1.txt', JSON.stringify(data)) 90 | ``` 91 | 92 | ```sh 93 | > const data = JSON.parse(fs.readSync('./dump1.txt')) 94 | > RNAsyncStorage.load(data) 95 | null 96 | ``` 97 | # Advanced Usage 98 | 99 | ## ReactNative side 100 | 101 | AsyncStorageREPL() accepts an object with a host and port key. 102 | Port key must be matched REPL side. 103 | You don't need specify a host in case of running on a simulator. 104 | but in case of runnning on a real device, specify your computer host. 105 | 106 | ```js 107 | AsyncStorageREPL({ host: 'localhost', port: 8080 }) // default 108 | .connect(); 109 | ``` 110 | 111 | ## REPL side 112 | 113 | You can specify portNo --port option. 114 | 115 | ```sh 116 | async-storage-repl --port 8080 117 | ``` 118 | -------------------------------------------------------------------------------- /bin/async-storage-repl: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env node 2 | const path = require('path'); 3 | 4 | function getArg(paramName) { 5 | const index = process.argv.indexOf(paramName); 6 | if (index < 0) { 7 | return null; 8 | } 9 | return process.argv[index + 1]; 10 | } 11 | 12 | const port = getArg('--port') || ''; 13 | require('child_process').spawn('node', ['-r', path.join(__dirname, '../dist/node/preload.js')], { 14 | stdio: [0, 1, 2], 15 | env: Object.assign({}, process.env, { asrport: port }) 16 | }); 17 | -------------------------------------------------------------------------------- /example/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["babel-preset-expo"], 3 | "env": { 4 | "development": { 5 | "plugins": ["transform-react-jsx-source"] 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /example/.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | ; We fork some components by platform 3 | .*/*[.]android.js 4 | 5 | ; Ignore templates for 'react-native init' 6 | /node_modules/react-native/local-cli/templates/.* 7 | 8 | ; Ignore RN jest 9 | /node_modules/react-native/jest/.* 10 | 11 | ; Ignore RNTester 12 | /node_modules/react-native/RNTester/.* 13 | 14 | ; Ignore the website subdir 15 | /node_modules/react-native/website/.* 16 | 17 | ; Ignore the Dangerfile 18 | /node_modules/react-native/danger/dangerfile.js 19 | 20 | ; Ignore Fbemitter 21 | /node_modules/fbemitter/.* 22 | 23 | ; Ignore "BUCK" generated dirs 24 | /node_modules/react-native/\.buckd/ 25 | 26 | ; Ignore unexpected extra "@providesModule" 27 | .*/node_modules/.*/node_modules/fbjs/.* 28 | 29 | ; Ignore polyfills 30 | /node_modules/react-native/Libraries/polyfills/.* 31 | 32 | ; Ignore various node_modules 33 | /node_modules/react-native-gesture-handler/.* 34 | /node_modules/expo/.* 35 | /node_modules/react-navigation/.* 36 | /node_modules/xdl/.* 37 | /node_modules/reqwest/.* 38 | /node_modules/metro-bundler/.* 39 | 40 | [include] 41 | 42 | [libs] 43 | node_modules/react-native/Libraries/react-native/react-native-interface.js 44 | node_modules/react-native/flow/ 45 | node_modules/expo/flow/ 46 | 47 | [options] 48 | emoji=true 49 | 50 | module.system=haste 51 | 52 | module.file_ext=.js 53 | module.file_ext=.jsx 54 | module.file_ext=.json 55 | module.file_ext=.ios.js 56 | 57 | munge_underscores=true 58 | 59 | module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub' 60 | 61 | suppress_type=$FlowIssue 62 | suppress_type=$FlowFixMe 63 | suppress_type=$FlowFixMeProps 64 | suppress_type=$FlowFixMeState 65 | suppress_type=$FixMe 66 | 67 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(5[0-6]\\|[1-4][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native_oss[a-z,_]*\\)?)\\) 68 | suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(5[0-6]\\|[1-4][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native_oss[a-z,_]*\\)?)\\)?:? #[0-9]+ 69 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy 70 | suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError 71 | 72 | unsafe.enable_getters_and_setters=true 73 | 74 | [version] 75 | ^0.56.0 76 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # expo 4 | .expo/ 5 | 6 | # dependencies 7 | /node_modules 8 | 9 | # misc 10 | .env.local 11 | .env.development.local 12 | .env.test.local 13 | .env.production.local 14 | 15 | npm-debug.log* 16 | yarn-debug.log* 17 | yarn-error.log* 18 | -------------------------------------------------------------------------------- /example/.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /example/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { StyleSheet, Text, TextInput, View, Button, AsyncStorage } from 'react-native'; 3 | import AsyncStorageREPL from './src/rn/AsyncStorageREPL'; 4 | 5 | AsyncStorageREPL().connect(); 6 | 7 | export default class App extends React.Component { 8 | constructor(props) { 9 | super(props); 10 | this.state = { inputText: '', comments: [] }; 11 | this._handlePost = this.handlePost.bind(this); 12 | } 13 | 14 | componentDidMount() { 15 | AsyncStorage.getItem('comments', (_err, result) => { 16 | this.setState({ comments: JSON.parse(result) || [] }); 17 | }); 18 | } 19 | 20 | handlePost() { 21 | const comments = this.state.comments.concat(this.state.inputText); 22 | AsyncStorage.setItem('comments', JSON.stringify(comments), (_err) => { 23 | this.setState({ comments, inputText: '' }); 24 | }); 25 | } 26 | 27 | render() { 28 | const comments = this.state.comments.map((comment, idx) => 29 | {comment}); 30 | return ( 31 | 32 | 33 | 34 | { this.setState({ inputText: text }); }} 38 | /> 39 | 40 |