├── .editorconfig ├── .gitattributes ├── .gitignore ├── .pnp.cjs ├── .travis.yml ├── .vscode ├── extensions.json └── settings.json ├── README.md ├── package-lock.json ├── package.json └── packages ├── arkhamjs-devtools-extension ├── .eslintrc ├── .gitignore ├── LICENSE ├── README.md ├── jest.setup.js ├── package.json ├── src │ ├── actions │ │ ├── InspectorActions.ts │ │ └── index.ts │ ├── app.html │ ├── app.tsx │ ├── components │ │ ├── ActionItem │ │ │ └── ActionItem.tsx │ │ ├── TabBar │ │ │ └── TabBar.tsx │ │ └── index.ts │ ├── constants │ │ ├── InspectorConstants.ts │ │ └── index.ts │ ├── contentScripts.ts │ ├── devtoolsBackground.html │ ├── devtoolsBackground.ts │ ├── manifest.json │ ├── types │ │ └── inspector.ts │ └── views │ │ ├── ActionsView │ │ └── ActionsView.tsx │ │ ├── InfoView │ │ └── InfoView.tsx │ │ ├── InspectorView │ │ └── InspectorView.tsx │ │ ├── LayoutView │ │ └── LayoutView.tsx │ │ ├── StateView │ │ └── StateView.tsx │ │ └── index.ts ├── tsconfig.json └── webpack.config.ts ├── arkhamjs-example-ts-react ├── .eslintrc ├── .gitignore ├── .npmignore ├── .travis.yml ├── .vscode │ ├── extensions.json │ └── settings.json ├── LICENSE ├── README.md ├── lex.config.js ├── package.json ├── src │ ├── actions │ │ └── AppActions │ │ │ ├── AppActions.test.ts │ │ │ └── AppActions.ts │ ├── app.css │ ├── components │ │ └── Icon │ │ │ ├── Icon.test.tsx │ │ │ ├── Icon.tsx │ │ │ └── Icon.types.ts │ ├── config │ │ ├── config.types.ts │ │ └── index.ts │ ├── constants │ │ └── AppConstants.ts │ ├── errors │ │ └── UserError.ts │ ├── fonts │ │ └── readme.txt │ ├── icons │ │ ├── pencil.svg │ │ └── readme.txt │ ├── img │ │ ├── arkhamjs-logo.png │ │ └── readme.txt │ ├── index.html │ ├── index.tsx │ ├── services │ │ ├── StringService.test.ts │ │ └── StringService.ts │ ├── stores │ │ └── AppStore │ │ │ ├── appStore.test.ts │ │ │ ├── appStore.ts │ │ │ └── appStore.types.ts │ └── views │ │ ├── AppView.test.tsx │ │ ├── AppView.tsx │ │ ├── HomeView.test.tsx │ │ └── HomeView.tsx └── tsconfig.json ├── arkhamjs-middleware-devtools ├── .eslintrc ├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── index.js ├── jest.setup.js ├── package.json ├── src │ ├── index.ts │ └── middleware │ │ └── DevTools.ts └── tsconfig.json ├── arkhamjs-middleware-logger ├── .eslintrc ├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── index.js ├── lex.config.js ├── package.json ├── src │ ├── Logger │ │ ├── Logger.test.ts │ │ └── Logger.ts │ ├── index.ts │ └── types │ │ └── main.ts └── tsconfig.json ├── arkhamjs-middleware-redux ├── .eslintrc ├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── index.js ├── lex.config.js ├── package.json ├── src │ ├── createArkhamStore.ts │ ├── index.ts │ ├── middleware │ │ ├── ReduxMiddleware.ts │ │ ├── arkhamMiddleware.test.ts │ │ └── arkhamMiddleware.ts │ └── types │ │ └── main.ts └── tsconfig.json ├── arkhamjs-storage-browser ├── .eslintrc ├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── index.js ├── lex.config.js ├── package.json ├── src │ ├── BrowserStorage │ │ ├── BrowserStorage.test.ts │ │ └── BrowserStorage.ts │ ├── index.ts │ └── types │ │ └── main.ts └── tsconfig.json ├── arkhamjs-storage-native ├── .eslintrc ├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── index.js ├── lex.config.js ├── package.json ├── src │ ├── NativeStorage │ │ ├── NativeStorage.test.ts │ │ ├── NativeStorage.ts │ │ └── NativeStorage.types.ts │ └── index.ts └── tsconfig.json ├── arkhamjs-storage-node ├── .eslintrc ├── .gitignore ├── .npmignore ├── .travis.yml ├── LICENSE ├── README.md ├── index.js ├── jest.setup.js ├── lex.config.js ├── package.json ├── src │ ├── NodeStorage │ │ ├── NodeStorage.test.ts │ │ └── NodeStorage.ts │ └── index.ts └── tsconfig.json ├── arkhamjs-utils-react ├── .eslintignore ├── .eslintrc ├── .gitignore ├── .npmignore ├── LICENSE ├── README ├── index.js ├── lex.config.js ├── package.json ├── src │ ├── FluxContext.ts │ ├── FluxProvider.test.tsx │ ├── FluxProvider.tsx │ ├── FluxProvider.types.ts │ ├── ResizeObserver.d.ts │ ├── index.ts │ ├── useComponentSize.ts │ ├── useFlux.ts │ ├── useFluxDispatch.ts │ ├── useFluxListener.ts │ ├── useFluxState.ts │ ├── useFluxValue.ts │ ├── useRefSize.ts │ ├── useState.ts │ ├── useWindowSize.ts │ └── useWindowSize.types.ts └── tsconfig.json └── arkhamjs ├── .eslintrc ├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── index.js ├── lex.config.js ├── package.json ├── src ├── Flux │ ├── Flux.test.ts │ ├── Flux.ts │ └── Flux.types.ts ├── constants │ └── ArkhamConstants.ts └── index.ts └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | 7 | [*.{js,json,yml}] 8 | charset = utf-8 9 | indent_style = space 10 | indent_size = 2 11 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | /.yarn/** linguist-vendored 2 | /.yarn/releases/* binary 3 | /.yarn/plugins/**/* binary 4 | /.pnp.* binary linguist-generated 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | .DS_Store 3 | *.bash_profile 4 | *.log 5 | 6 | # node.js 7 | node_modules 8 | 9 | # Client Editor 10 | build 11 | .idea 12 | 13 | # App 14 | coverage 15 | lib 16 | docs 17 | tmp 18 | 19 | # Docusaurus 20 | lib/core/metadata.js 21 | lib/core/MetadataBlog.js 22 | 23 | website/translated_docs 24 | website/build/ 25 | website/node_modules 26 | website/i18n/* 27 | /packages/arkhamjs-example-ts-react/dist 28 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "20" 4 | install: 5 | - npm install 6 | - lex versions 7 | script: 8 | - npm run lint 9 | - npm run build 10 | - npm run test 11 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "enabled": [ 3 | "streetsidesoftware.code-spell-checker", 4 | "bierner.markdown-preview-github-styles", 5 | "DavidAnson.vscode-markdownlint", 6 | "ricard.postcss", 7 | "shinnn.stylelint" 8 | ], 9 | "recommendations": [ 10 | "dbaeumer.vscode-eslint", 11 | "streetsidesoftware.code-spell-checker", 12 | "bierner.markdown-preview-github-styles", 13 | "DavidAnson.vscode-markdownlint", 14 | "ricard.postcss", 15 | "shinnn.stylelint", 16 | "flowtype.flow-for-vscode", 17 | "rbbit.typescript-hero", 18 | "Equinusocio.vsc-material-theme", 19 | "PKief.material-icon-theme" 20 | ] 21 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": ["arkham", "arkhamjs", "nitrogenlabs", "nlabs", "transpiler"], 3 | "css.validate": false, 4 | "editor.autoClosingBrackets": "never", 5 | "editor.detectIndentation": false, 6 | "editor.formatOnSave": true, 7 | "editor.rulers": [120], 8 | "editor.tabCompletion": "onlySnippets", 9 | "editor.tabSize": 2, 10 | "editor.wordWrap": "off", 11 | "editor.wordWrapColumn": 120, 12 | "editor.wrappingIndent": "indent", 13 | "emmet.includeLanguages": { 14 | "postcss": "css" 15 | }, 16 | "emmet.syntaxProfiles": { 17 | "postcss": "css" 18 | }, 19 | "eslint.codeActionsOnSave": true, 20 | "eslint.alwaysShowStatus": true, 21 | "eslint.enable": true, 22 | "eslint.validate": [ 23 | "javascript", 24 | "javascriptreact", 25 | "typescript", 26 | "typescriptreact" 27 | ], 28 | "explorer.confirmDragAndDrop": false, 29 | "explorer.decorations.badges": false, 30 | "files.trimTrailingWhitespace": true, 31 | "flow.runOnAllFiles": true, 32 | "html.format.indentInnerHtml": true, 33 | "html.format.indentHandlebars": true, 34 | "html.format.extraLiners": "", 35 | "html.format.enable": false, 36 | "html.format.endWithNewline": true, 37 | "javascript.format.enable": false, 38 | "javascript.format.insertSpaceAfterFunctionKeywordForAnonymousFunctions": false, 39 | "javascript.format.insertSpaceAfterKeywordsInControlFlowStatements": false, 40 | "javascript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces": false, 41 | "javascript.validate.enable": false, 42 | "typescript.format.insertSpaceAfterKeywordsInControlFlowStatements": false, 43 | "typescript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces": false, 44 | "typescriptHero.imports.insertSpaceBeforeAndAfterImportBraces": false, 45 | "typescriptHero.imports.multiLineTrailingComma": false, 46 | "typescriptHero.imports.multiLineWrapThreshold": 120, 47 | "typescriptHero.imports.organizeOnSave": true, 48 | "typescript.format.insertSpaceAfterFunctionKeywordForAnonymousFunctions": false, 49 | "tslint.enable": true, 50 | "tslint.packageManager": "npm", 51 | "tslint.autoFixOnSave": true, 52 | "tslint.configFile": "./tslint.json", 53 | "editor.codeActionsOnSave": { 54 | "source.fixAll.eslint": "explicit" 55 | }, 56 | "editor.autoClosingQuotes": "never", 57 | "editor.autoSurround": "never", 58 | "eslint.workingDirectories": ["./node_modules/@nlabs/lex/"] 59 | } 60 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ArkhamJS 2 | 3 | 4 | 5 | [![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lernajs.io/) 6 | [![npm version](https://img.shields.io/npm/v/@nlabs/arkhamjs.svg?style=flat-square)](https://www.npmjs.com/package/arkhamjs) 7 | [![Issues](http://img.shields.io/github/issues/nitrogenlabs/arkhamjs.svg?style=flat-square)](https://github.com/nitrogenlabs/arkhamjs/issues) 8 | [![TypeScript](https://badges.frapsoft.com/typescript/version/typescript-next.svg?v=101)](https://github.com/ellerbrock/typescript-badges/) 9 | [![MIT license](http://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](http://opensource.org/licenses/MIT) 10 | [![Chat](https://img.shields.io/discord/446122412715802649.svg)](https://discord.gg/Ttgev58) 11 | 12 | ## Flux Framework 13 | 14 | ArkhamJS is a lightweight framework that can accommodate a project of any size, small or large. From small start-up ideas to large enterprise projects. A simple, flexible framework. Consisting of a singular state tree with a unidirectional data flow. 15 | 16 | ## Lightweight 17 | 18 | The framework is small. The bulk of your app should lay within your code, not the framework. While larger frameworks come with lots of "magic", they become very limited when new features arise within your project. 19 | 20 | ## Typescript 21 | 22 | Compatible with typescript. Definitions are included to support your Typescript project. 23 | 24 | ## Single Store 25 | 26 | All data is stored within a single store. The data can be accessed through all your views and components. Data is organized into multiple stores within the single store. 27 | 28 | ## Immutability 29 | 30 | To prevent object referencing, we use immutable objects. When the state changes, the state's property is not the only item that is changed, the item it references is also updated. To prevent passing around an object between different scopes, immutable objects give your data a one way update path. You may also have returned values converted into ImmutableJS objects. 31 | 32 | ## Debugger 33 | 34 | The most important factor in choosing a framework is how easy it is to build with it. And with building comes debugging. A state debugger can be added with the middleware, [@nlabs/arkhamjs-middleware-logger](https://github.com/nitrogenlabs/arkhamjs-middleware-logger). When turned on, it will display any actions and state changes that come through the framework. Making the previous and next state visible to the developer. Great way to make your data transparent! Supported browsers: Chrome, Firefox, and Safari. 35 | 36 | ## Cache Storage 37 | 38 | An app state is clears after a browser refresh. Keeping the state after a reload can be very useful when requiring a persistent state. 39 | 40 | If you plan to persist data, you will need to add a storage to the framework: 41 | 42 | - React [@nlabs/arkhamjs-storage-browser](https://github.com/nitrogenlabs/arkhamjs-storage-browser) 43 | - React Native [@nlabs/arkhamjs-storage-native](https://github.com/nitrogenlabs/arkhamjs-storage-native) 44 | - NodeJS [@nlabs/arkhamjs-storage-node](https://github.com/nitrogenlabs/arkhamjs-storage-node) 45 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "arkhamjs", 3 | "scripts": { 4 | "build": "npm run build --workspaces", 5 | "lint": "npm run lint --workspaces", 6 | "clean": "npm run clean --workspaces && npm run clean:root", 7 | "clean:root": "rm -rf *.log node_modules *.lock package-lock.json", 8 | "publish:major": "npm run publish:major --workspaces && npm run publish:tags", 9 | "publish:minor": "npm run publish:minor --workspaces && npm run publish:tags", 10 | "publish:patch": "npm run publish:patch --workspaces && npm run publish:tags", 11 | "publish:tags": "git push --tags && git push origin HEAD", 12 | "test": "jest", 13 | "update": "npm run update --workspaces" 14 | }, 15 | "devDependencies": { 16 | "@nlabs/lex": "^1.21.2", 17 | "npm-check-updates": "^17.1.14" 18 | }, 19 | "workspaces": [ 20 | "packages/arkhamjs", 21 | "packages/arkhamjs-middleware-logger", 22 | "packages/arkhamjs-middleware-redux", 23 | "packages/arkhamjs-storage-browser", 24 | "packages/arkhamjs-storage-native", 25 | "packages/arkhamjs-storage-node", 26 | "packages/arkhamjs-utils-react" 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /packages/arkhamjs-devtools-extension/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "styleguidejs/typescript" 3 | } -------------------------------------------------------------------------------- /packages/arkhamjs-devtools-extension/.gitignore: -------------------------------------------------------------------------------- 1 | coverage 2 | lib 3 | node_modules 4 | *.log -------------------------------------------------------------------------------- /packages/arkhamjs-devtools-extension/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Nitrogen Labs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/arkhamjs-devtools-extension/README.md: -------------------------------------------------------------------------------- 1 | # arkhamjs-devtools-extension 2 | 3 | ArkhamJS Development Tools Browser Extension 4 | 5 | [![Travis](https://img.shields.io/travis/nitrogenlabs/arkhamjs.svg?style=flat-square)](https://travis-ci.org/nitrogenlabs/arkhamjs) 6 | [![Issues](https://img.shields.io/github/issues/nitrogenlabs/arkhamjs.svg?style=flat-square)](https://github.com/nitrogenlabs/arkhamjs/issues) 7 | [![TypeScript](https://badges.frapsoft.com/typescript/version/typescript-next.svg?v=101)](https://github.com/ellerbrock/typescript-badges/) 8 | [![MIT license](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](http://opensource.org/licenses/MIT) 9 | [![Chat](https://img.shields.io/discord/446122412715802649.svg)](https://discord.gg/Ttgev58) 10 | 11 | -------------------------------------------------------------------------------- /packages/arkhamjs-devtools-extension/jest.setup.js: -------------------------------------------------------------------------------- 1 | global.requestAnimationFrame = function(callback) { 2 | setTimeout(callback, 0); 3 | }; 4 | -------------------------------------------------------------------------------- /packages/arkhamjs-devtools-extension/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@nlabs/arkhamjs-devtools-extension", 3 | "version": "3.11.8", 4 | "publishConfig": { 5 | "access": "public" 6 | }, 7 | "description": "ArkhamJS DevTools Extension", 8 | "license": "MIT", 9 | "main": "index.js", 10 | "types": "./dist/index.d.ts", 11 | "keywords": [ 12 | "arkhamjs", 13 | "chrome", 14 | "devtools", 15 | "firefox", 16 | "nitrogenlabs", 17 | "react" 18 | ], 19 | "author": { 20 | "name": "Giraldo Rosales", 21 | "email": "giraldo@nitrogenlabs.com", 22 | "url": "http://nitrogenlabs.com" 23 | }, 24 | "repository": { 25 | "type": "git", 26 | "url": "nitrogenlabs/arkhamjs.git" 27 | }, 28 | "homepage": "https://arkhamjs.io", 29 | "bugs": { 30 | "url": "https://github.com/nitrogenlabs/arkhamjs/issues" 31 | }, 32 | "scripts": { 33 | "clean": "lex clean", 34 | "compile": "NODE_ENV=production tsc && webpack", 35 | "development": "NODE_ENV=development webpack -w", 36 | "lint": "eslint ./src --ext .ts,.tsx", 37 | "prepublishOnly": "npm run build", 38 | "publish:major": "npm version major && npm publish", 39 | "publish:minor": "npm version minor && npm publish", 40 | "publish:patch": "npm version patch && npm publish", 41 | "pretest": "npm run lint", 42 | "start": "npm run development", 43 | "test": "", 44 | "update": "npm-check-updates --interactive" 45 | }, 46 | "dependencies": { 47 | "@nlabs/arkhamjs": "^3.11.8", 48 | "@nlabs/arkhamjs-storage-browser": "^3.11.8", 49 | "@nlabs/arkhamjs-views-react": "^1.2.2", 50 | "history": "^4.7.2", 51 | "lodash": "^4.17.20", 52 | "rc-tabs": "^9.4.1", 53 | "react": "^19.0.0", 54 | "react-dom": "^19.0.0", 55 | "react-router": "^4.2.0", 56 | "react-router-dom": "^4.2.2" 57 | }, 58 | "devDependencies": { 59 | "@types/chrome": "^0.0.73", 60 | "@types/jest": "^23.3.2", 61 | "@types/node": "^10.9.4", 62 | "@types/react": "^16.4.14", 63 | "@types/react-dom": "^16.0.7", 64 | "@types/react-router-dom": "^4.3.0", 65 | "clean-webpack-plugin": "^0.1.19", 66 | "copy-webpack-plugin": "^4.5.1", 67 | "del": "^3.0.0", 68 | "eslint": "^5.5.0", 69 | "eslint-config-styleguidejs": "^0.7.9", 70 | "exports-loader": "^0.7.0", 71 | "file-loader": "^2.0.0", 72 | "fs": "0.0.2", 73 | "html-webpack-plugin": "^3.2.0", 74 | "imports-loader": "^0.8.0", 75 | "json-d-ts": "^1.0.1", 76 | "react-addons-test-utils": "^15.6.2", 77 | "react-test-renderer": "^16.5.0", 78 | "react-transform-catch-errors": "^1.0.2", 79 | "react-transform-hmr": "^1.0.4", 80 | "redbox-react": "^1.5.0", 81 | "regenerator-runtime": "^0.12.1", 82 | "source-map-loader": "^0.2.4", 83 | "source-map-support": "^0.5.9", 84 | "typescript": "^3.0.3", 85 | "webpack": "^4.18.0", 86 | "webpack-cli": "^3.1.0", 87 | "webpack-dev-middleware": "^3.3.0", 88 | "webpack-hot-middleware": "^2.23.1" 89 | }, 90 | "gitHead": "ca196f147316f6590794c49f53965ab6770b2dd6" 91 | } 92 | -------------------------------------------------------------------------------- /packages/arkhamjs-devtools-extension/src/actions/InspectorActions.ts: -------------------------------------------------------------------------------- 1 | import {FluxAction} from '@nlabs/arkhamjs'; 2 | import {createBrowserHistory, History} from 'history'; 3 | 4 | export class InspectorActions { 5 | static dispatch(action: FluxAction): void { 6 | this.message({ 7 | _arkhamCall: { 8 | args: [action], 9 | method: 'dispatch' 10 | } 11 | }); 12 | } 13 | 14 | static goto(routePath: string): History { 15 | const history = createBrowserHistory(); 16 | history.push(`/${routePath}`); 17 | return history; 18 | } 19 | 20 | static onDispatch(data): void { 21 | const {action, duration, state, stack} = data; 22 | const formatDuration: string = InspectorActions.msToTime(duration); 23 | console.log('InspectorActions::action', action); 24 | console.log('InspectorActions::state', state); 25 | console.log('InspectorActions::stack', stack); 26 | console.log('InspectorActions::formatDuration', formatDuration); 27 | } 28 | 29 | static onInfo(data): void { 30 | console.log('InspectorActions::onInfo::data', data); 31 | } 32 | 33 | static message(data): void { 34 | window.postMessage(data, '*'); 35 | } 36 | 37 | static msToTime(duration: number): string { 38 | const milliseconds: number = Math.floor(duration % 1000); 39 | const seconds: number = Math.floor((duration / 1000) % 60); 40 | const minutes: number = Math.floor((duration / (1000 * 60)) % 60); 41 | const hours: number = Math.floor((duration / (1000 * 60 * 60)) % 24); 42 | 43 | const formatHours: string = (hours < 10) ? `0${hours}` : hours.toString(); 44 | const formatMinutes: string = (minutes < 10) ? `0${minutes}` : minutes.toString(); 45 | const formatSeconds: string = (seconds < 10) ? `0${seconds}` : seconds.toString(); 46 | 47 | return `${formatHours}:${formatMinutes}:${formatSeconds}.${milliseconds.toString()}`; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /packages/arkhamjs-devtools-extension/src/actions/index.ts: -------------------------------------------------------------------------------- 1 | export {InspectorActions} from './InspectorActions'; 2 | -------------------------------------------------------------------------------- /packages/arkhamjs-devtools-extension/src/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ArkhamJS DevTools 6 | 7 | 8 |
9 | 10 | -------------------------------------------------------------------------------- /packages/arkhamjs-devtools-extension/src/app.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | 4 | import {InspectorView} from './views/InspectorView/InspectorView'; 5 | 6 | const target = document.getElementById('app'); 7 | 8 | console.log('target', target); 9 | // Render initial inspector panel 10 | ReactDOM.render(, target); 11 | -------------------------------------------------------------------------------- /packages/arkhamjs-devtools-extension/src/components/ActionItem/ActionItem.tsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitrogenlabs/arkhamjs/9428e95a52f4a7323609da35ad9c29223eb8eafa/packages/arkhamjs-devtools-extension/src/components/ActionItem/ActionItem.tsx -------------------------------------------------------------------------------- /packages/arkhamjs-devtools-extension/src/components/TabBar/TabBar.tsx: -------------------------------------------------------------------------------- 1 | import Tabs, {TabPane} from 'rc-tabs'; 2 | import ScrollableInkTabBar from 'rc-tabs/lib/ScrollableInkTabBar'; 3 | import TabContent from 'rc-tabs/lib/TabContent'; 4 | import * as React from 'react'; 5 | 6 | import {InspectorActions} from '../../actions'; 7 | import {ActionsView, InfoView, StateView} from '../../views'; 8 | 9 | export class TabBar extends React.Component { 10 | data; 11 | 12 | constructor(props) { 13 | super(props); 14 | 15 | // Set tab data 16 | this.data = [ 17 | {component: , key: 'actions'}, 18 | {component: , key: 'stateTree'}, 19 | {component: , key: 'appDetails'} 20 | ]; 21 | 22 | // Methods 23 | this.onSelect = this.onSelect.bind(this); 24 | } 25 | 26 | onSelect(key): void { 27 | console.log('key', key); 28 | InspectorActions.goto(key); 29 | } 30 | 31 | render(): JSX.Element { 32 | let activeKey: string = 'actions'; 33 | const {children} = this.props; 34 | 35 | if(children) { 36 | this.data.forEach((dataItem) => { 37 | const typeProp: string = 'type'; 38 | if(dataItem.component.type === children[typeProp]) { 39 | // for demo, better immutable 40 | dataItem.component = children; 41 | activeKey = dataItem.key; 42 | } 43 | }); 44 | } 45 | 46 | const tabs: TabPane[] = this.data.map( 47 | (dataItem) => {dataItem.component} 48 | ); 49 | 50 | return ( 51 | } 55 | renderTabContent={() => }> 56 | {tabs} 57 | 58 | ); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /packages/arkhamjs-devtools-extension/src/components/index.ts: -------------------------------------------------------------------------------- 1 | export {TabBar} from './TabBar/TabBar'; 2 | -------------------------------------------------------------------------------- /packages/arkhamjs-devtools-extension/src/constants/InspectorConstants.ts: -------------------------------------------------------------------------------- 1 | export class InspectorConstants { 2 | static DISPATCH: string = 'INPECTOR_DISPATCH'; 3 | } 4 | -------------------------------------------------------------------------------- /packages/arkhamjs-devtools-extension/src/constants/index.ts: -------------------------------------------------------------------------------- 1 | export {InspectorConstants} from './InspectorConstants'; 2 | -------------------------------------------------------------------------------- /packages/arkhamjs-devtools-extension/src/contentScripts.ts: -------------------------------------------------------------------------------- 1 | // chrome.runtime.onInstalled.addListener(function() { 2 | // // Replace all rules ... 3 | // chrome.declarativeContent.onPageChanged.removeRules(undefined, function() { 4 | // // With a new rule ... 5 | // chrome.declarativeContent.onPageChanged.addRules([ 6 | // { 7 | // // That fires when a page's URL contains a 'g' ... 8 | // conditions: [ 9 | // new chrome.declarativeContent.PageStateMatcher({ 10 | // pageUrl: { urlContains: 'g' }, 11 | // }) 12 | // ], 13 | // // And shows the extension's page action. 14 | // actions: [ new chrome.declarativeContent.ShowPageAction() ] 15 | // } 16 | // ]); 17 | // }); 18 | // }); 19 | // const win = (window); 20 | 21 | // console.log('win', win); 22 | 23 | // win.addEventListener('load', loadEvent => { 24 | // let window = loadEvent.currentTarget; 25 | // console.log('window', window); 26 | // console.log('loadEvent', loadEvent); 27 | // window.document.title = 'You changed me!'; 28 | // }); 29 | 30 | // win.addEventListener('ARKHAMJS_INIT', () => { 31 | // const Flux = win.arkhamjs; 32 | 33 | // console.log('ArkhamJS DevTools', Flux.getState()); 34 | 35 | // Flux.on('APP_UPDATE_CONTENT', () => { 36 | // console.log('ArkhamJS::Content', Flux.getState()); 37 | // }); 38 | // }); 39 | 40 | // chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) { 41 | // if(request.cmd == "any command") { 42 | // sendResponse({result: "any response from background"}); 43 | // } else { 44 | // sendResponse({result: "error", message: `Invalid 'cmd'`}); 45 | // } 46 | // // Note: Returning true is required here! 47 | // // ref: http://stackoverflow.com/questions/20077487/chrome-extension-message-passing-response-not-sent 48 | // return true; 49 | // }); 50 | console.log('load::bg'); 51 | 52 | window.addEventListener('load', () => { 53 | window.postMessage({_arkhamCall: {method: 'storeClasses'}}, '*'); 54 | }); 55 | 56 | // chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { 57 | // // port.postMessage({greeting:"hello"}); 58 | // console.log('chrome::onMessage::request', request); 59 | // }); 60 | window.addEventListener('message', (event) => { 61 | const {data = {}, source} = event; 62 | 63 | // We only accept messages from ourselves 64 | if(source !== window) { 65 | return; 66 | } 67 | 68 | chrome.runtime.sendMessage(data); 69 | }); 70 | -------------------------------------------------------------------------------- /packages/arkhamjs-devtools-extension/src/devtoolsBackground.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Background 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /packages/arkhamjs-devtools-extension/src/devtoolsBackground.ts: -------------------------------------------------------------------------------- 1 | // import {Flux} from '@nlabs/arkhamjs'; 2 | const {devtools: {panels}} = chrome || {}; 3 | 4 | // Create panel 5 | if(panels) { 6 | panels.create('ArkhamJS', null, 'app.html', () => {}); 7 | } 8 | console.log('load::devtoolsbg'); 9 | -------------------------------------------------------------------------------- /packages/arkhamjs-devtools-extension/src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ArkhamJS DevTools", 3 | "description": "ArkhamJS development tool used to debug apps", 4 | "devtools_page": "devtoolsBackground.html", 5 | "version": "0.1.0", 6 | "manifest_version": 2, 7 | "minimum_chrome_version": "50", 8 | "permissions": [ 9 | "storage", 10 | "tabs", 11 | "background", 12 | "activeTab", 13 | "" 14 | ], 15 | "content_scripts": [ 16 | { 17 | "matches": [ 18 | "" 19 | ], 20 | "js": [ 21 | "contentScripts.js" 22 | ] 23 | } 24 | ], 25 | "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'" 26 | } -------------------------------------------------------------------------------- /packages/arkhamjs-devtools-extension/src/types/inspector.ts: -------------------------------------------------------------------------------- 1 | import {FluxAction} from '@nlabs/arkhamjs'; 2 | 3 | export interface InspectorStackType { 4 | readonly columnNumber: number; 5 | readonly fileName: string; 6 | readonly functionName: string; 7 | readonly lineNumber: number; 8 | readonly source: string; 9 | } 10 | 11 | export interface InspectorDispatchType { 12 | readonly action: FluxAction; 13 | readonly duration: number; 14 | readonly state: any; 15 | readonly stack: InspectorStackType[]; 16 | } 17 | -------------------------------------------------------------------------------- /packages/arkhamjs-devtools-extension/src/views/ActionsView/ActionsView.tsx: -------------------------------------------------------------------------------- 1 | import {ViewBase, ViewProps} from '@nlabs/arkhamjs-views-react'; 2 | import * as React from 'react'; 3 | 4 | export interface ActionsViewProps extends ViewProps { 5 | } 6 | 7 | export class ActionsView extends ViewBase { 8 | render(): JSX.Element { 9 | return ( 10 |

Actions

11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/arkhamjs-devtools-extension/src/views/InfoView/InfoView.tsx: -------------------------------------------------------------------------------- 1 | import {ViewBase, ViewProps} from '@nlabs/arkhamjs-views-react'; 2 | import * as React from 'react'; 3 | 4 | export interface InfoViewProps extends ViewProps { 5 | } 6 | 7 | export class InfoView extends ViewBase { 8 | render(): JSX.Element { 9 | return ( 10 |

Info

11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/arkhamjs-devtools-extension/src/views/InspectorView/InspectorView.tsx: -------------------------------------------------------------------------------- 1 | import {Flux, FluxOptions} from '@nlabs/arkhamjs'; 2 | import {BrowserStorage} from '@nlabs/arkhamjs-storage-browser'; 3 | import * as React from 'react'; 4 | import {HashRouter, Route} from 'react-router-dom'; 5 | 6 | import {InspectorActions} from '../../actions'; 7 | import {TabBar} from '../../components'; 8 | import {InspectorDispatchType} from '../../types/inspector'; 9 | import {ActionsView} from '../ActionsView/ActionsView'; 10 | import {InfoView} from '../InfoView/InfoView'; 11 | import {StateView} from '../StateView/StateView'; 12 | 13 | export class InspectorView extends React.Component<{}, {}> { 14 | constructor(props) { 15 | super(props); 16 | 17 | // ArkhamJS Configuration 18 | const storage = new BrowserStorage({type: 'session'}); 19 | Flux.init({ 20 | storage 21 | }); 22 | 23 | // Methods 24 | this.onData = this.onData.bind(this); 25 | } 26 | 27 | componentDidMount(): void { 28 | chrome.runtime.onMessage.addListener(this.onData); 29 | } 30 | 31 | componentWillUnmount(): void { 32 | chrome.runtime.onMessage.removeListener(this.onData); 33 | } 34 | 35 | onData(eventData): void { 36 | console.log('app::message::eventData', eventData); 37 | const {_arkhamDispatch, _arkhamInfo} = eventData; 38 | 39 | if(_arkhamDispatch) { 40 | const dispatchData: InspectorDispatchType = JSON.parse(_arkhamDispatch); 41 | InspectorActions.onDispatch(dispatchData); 42 | } 43 | 44 | if(_arkhamInfo) { 45 | const infoData: FluxOptions = JSON.parse(_arkhamInfo); 46 | InspectorActions.onInfo(infoData); 47 | } 48 | } 49 | 50 | render(): JSX.Element { 51 | return ( 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | ); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /packages/arkhamjs-devtools-extension/src/views/LayoutView/LayoutView.tsx: -------------------------------------------------------------------------------- 1 | import {ViewBase, ViewContainer, ViewProps} from '@nlabs/arkhamjs-views-react'; 2 | import * as React from 'react'; 3 | import {RouteProps} from 'react-router'; 4 | 5 | import {ActionsView, InfoView, StateView} from '../../views'; 6 | 7 | export interface LayoutProps extends ViewProps { 8 | readonly children?: React.ReactNode; 9 | } 10 | 11 | export class LayoutView extends ViewBase { 12 | routes: RouteProps[]; 13 | 14 | constructor(props) { 15 | super(props); 16 | this.routes = [ 17 | {component: ActionsView, path: '/'}, 18 | {component: StateView, path: '/state'}, 19 | {component: InfoView, path: '/info'} 20 | ]; 21 | } 22 | 23 | render(): JSX.Element { 24 | return ( 25 | 26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/arkhamjs-devtools-extension/src/views/StateView/StateView.tsx: -------------------------------------------------------------------------------- 1 | import {ViewBase, ViewProps} from '@nlabs/arkhamjs-views-react'; 2 | import * as React from 'react'; 3 | 4 | export interface StateViewProps extends ViewProps { 5 | } 6 | 7 | export class StateView extends ViewBase { 8 | render(): JSX.Element { 9 | return ( 10 |

State

11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/arkhamjs-devtools-extension/src/views/index.ts: -------------------------------------------------------------------------------- 1 | export {ActionsView} from './ActionsView/ActionsView'; 2 | export {InfoView} from './InfoView/InfoView'; 3 | export {LayoutView} from './LayoutView/LayoutView'; 4 | export {StateView} from './StateView/StateView'; 5 | -------------------------------------------------------------------------------- /packages/arkhamjs-devtools-extension/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "baseUrl": "./src", 5 | "declaration": true, 6 | "emitDeclarationOnly": true, 7 | "esModuleInterop": true, 8 | "inlineSourceMap": true, 9 | "jsx": "react-jsx", 10 | "lib": [ 11 | "esnext", 12 | "dom" 13 | ], 14 | "module": "commonjs", 15 | "moduleResolution": "node", 16 | "noImplicitReturns": true, 17 | "noImplicitThis": true, 18 | "noUnusedLocals": true, 19 | "outDir": "./dist", 20 | "pretty": true, 21 | "skipLibCheck": true, 22 | "target": "ES2018" 23 | }, 24 | "include": [ 25 | "./src/**/*" 26 | ], 27 | "exclude": [ 28 | "**/*.test.*", 29 | "./dist", 30 | "./node_modules", 31 | "./test" 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /packages/arkhamjs-devtools-extension/webpack.config.ts: -------------------------------------------------------------------------------- 1 | import {CheckerPlugin} from 'awesome-typescript-loader'; 2 | import * as path from 'path'; 3 | import * as webpack from 'webpack'; 4 | import * as CopyWebpackPlugin from 'copy-webpack-plugin'; 5 | import * as HtmlWebpackPlugin from 'html-webpack-plugin'; 6 | import * as CleanWebpackPlugin from 'clean-webpack-plugin'; 7 | const {name, version} = require('./package.json'); 8 | 9 | module.exports = { 10 | mode: 'production', 11 | devtool: process.env.NODE_ENV === 'development' ? 'cheap-module-eval-source-map' : null, 12 | entry: { 13 | app: './src/app.tsx', 14 | contentScripts: './src/contentScripts.ts', 15 | devToolsBackground: './src/devToolsBackground.ts' 16 | }, 17 | output: { 18 | path: path.resolve(__dirname, 'lib'), 19 | filename: '[name].js' 20 | }, 21 | resolve: { 22 | extensions: ['.ts', '.tsx', '.js'], 23 | modules: [path.resolve(__dirname, 'src'), 'node_modules'] 24 | }, 25 | plugins: [ 26 | new CleanWebpackPlugin(['lib']), 27 | new webpack.DefinePlugin({ 28 | 'process.env.NODE_ENV': process.env.NODE_ENV ? JSON.stringify(process.env.NODE_ENV) : 'development' 29 | }), 30 | new CheckerPlugin(), 31 | new CopyWebpackPlugin([{from: './src/*.+(json)', to: './', flatten: true}]), 32 | new HtmlWebpackPlugin({ 33 | template: path.join(__dirname, 'src', 'app.html'), 34 | filename: 'app.html', 35 | chunks: ["app"] 36 | }), 37 | new HtmlWebpackPlugin({ 38 | template: path.join(__dirname, 'src', 'devToolsBackground.html'), 39 | filename: 'devToolsBackground.html', 40 | chunks: ["devToolsBackground"] 41 | }), 42 | ], 43 | module: { 44 | rules: [ 45 | { 46 | test: /\.tsx?$/, 47 | loader: 'awesome-typescript-loader', 48 | exclude: /node_modules/, 49 | query: { 50 | declaration: false 51 | } 52 | }, 53 | { 54 | test: /\.(json)$/, 55 | loader: 'file-loader' 56 | } 57 | ] 58 | } 59 | } -------------------------------------------------------------------------------- /packages/arkhamjs-example-ts-react/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "styleguidejs/typescript" 3 | } -------------------------------------------------------------------------------- /packages/arkhamjs-example-ts-react/.gitignore: -------------------------------------------------------------------------------- 1 | coverage 2 | lib 3 | node_modules 4 | *.log -------------------------------------------------------------------------------- /packages/arkhamjs-example-ts-react/.npmignore: -------------------------------------------------------------------------------- 1 | coverage 2 | dist 3 | docs 4 | node_modules 5 | package-lock.json 6 | -------------------------------------------------------------------------------- /packages/arkhamjs-example-ts-react/.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "20" 4 | before_install: 5 | - export CHROME_BIN=chromium-browser 6 | - export DISPLAY=:99.0 7 | - sh -e /etc/init.d/xvfb start 8 | install: 9 | - npm install 10 | - lex versions 11 | script: 12 | - npm run build 13 | - npm run test 14 | -------------------------------------------------------------------------------- /packages/arkhamjs-example-ts-react/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "enabled": [ 3 | "streetsidesoftware.code-spell-checker", 4 | "bierner.markdown-preview-github-styles", 5 | "DavidAnson.vscode-markdownlint", 6 | "ricard.postcss", 7 | "shinnn.stylelint" 8 | ], 9 | "recommendations": [ 10 | "dbaeumer.vscode-eslint", 11 | "streetsidesoftware.code-spell-checker", 12 | "bierner.markdown-preview-github-styles", 13 | "DavidAnson.vscode-markdownlint", 14 | "ricard.postcss", 15 | "shinnn.stylelint", 16 | "flowtype.flow-for-vscode", 17 | "rbbit.typescript-hero", 18 | "Equinusocio.vsc-material-theme", 19 | "PKief.material-icon-theme" 20 | ] 21 | } -------------------------------------------------------------------------------- /packages/arkhamjs-example-ts-react/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": [ 3 | "arkham", 4 | "arkhamjs", 5 | "nitrogenlabs", 6 | "nlabs", 7 | "transpiler" 8 | ], 9 | "css.validate": false, 10 | "editor.autoClosingBrackets": false, 11 | "editor.detectIndentation": false, 12 | "editor.formatOnSave": true, 13 | "editor.rulers": [ 14 | 120 15 | ], 16 | "editor.tabCompletion": true, 17 | "editor.tabSize": 2, 18 | "editor.wordWrap": "off", 19 | "editor.wordWrapColumn": 120, 20 | "editor.wrappingIndent": "indent", 21 | "emmet.includeLanguages": { 22 | "postcss": "css" 23 | }, 24 | "emmet.syntaxProfiles": { 25 | "postcss": "css" 26 | }, 27 | "eslint.codeActionsOnSave": true, 28 | "eslint.alwaysShowStatus": true, 29 | "eslint.enable": true, 30 | "eslint.validate": [ 31 | "javascript", 32 | "javascriptreact", 33 | "typescript", 34 | "typescriptreact" 35 | ], 36 | "explorer.confirmDragAndDrop": false, 37 | "explorer.decorations.badges": false, 38 | "files.trimTrailingWhitespace": true, 39 | "flow.runOnAllFiles": true, 40 | "html.format.indentInnerHtml": true, 41 | "html.format.indentHandlebars": true, 42 | "html.format.extraLiners": "", 43 | "html.format.enable": false, 44 | "html.format.endWithNewline": true, 45 | "javascript.format.enable": false, 46 | "javascript.format.insertSpaceAfterFunctionKeywordForAnonymousFunctions": false, 47 | "javascript.format.insertSpaceAfterKeywordsInControlFlowStatements": false, 48 | "javascript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces": false, 49 | "javascript.validate.enable": false, 50 | "typescript.format.insertSpaceAfterKeywordsInControlFlowStatements": false, 51 | "typescript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces": false, 52 | "typescriptHero.imports.insertSpaceBeforeAndAfterImportBraces": false, 53 | "typescriptHero.imports.multiLineTrailingComma": false, 54 | "typescriptHero.imports.multiLineWrapThreshold": 120, 55 | "typescriptHero.imports.organizeOnSave": true, 56 | "typescript.format.insertSpaceAfterFunctionKeywordForAnonymousFunctions": false, 57 | "tslint.enable": true, 58 | "tslint.packageManager": "npm", 59 | "tslint.autoFixOnSave": true, 60 | "tslint.configFile": "./tslint.json" 61 | } 62 | -------------------------------------------------------------------------------- /packages/arkhamjs-example-ts-react/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Nitrogen Labs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/arkhamjs-example-ts-react/README.md: -------------------------------------------------------------------------------- 1 | # @nlabs/arkhamjs-example-ts-react 2 | 3 | An ArkhamJS React TypeScript example. A simple base application to start you off on your ReactJS project. Uses the following modules: 4 | 5 | - [react](https://www.npmjs.com/package/react) - A declarative, efficient, and flexible JavaScript library for building user interfaces. 6 | - [@nlabs/arkhamjs](https://www.npmjs.com/package/arkhamjs) - A clean, simple Flux framework. 7 | - [@nlabs/lex](https://www.npmjs.com/package/@nlabs/lex) - CLI tool to assist in development. Initialize, test, and compile your apps with zero setup. Using [Jest](https://facebook.github.io/jest/), [Webpack](https://webpack.js.org/), and [Typescript](http://www.typescriptlang.org/). 8 | - [@nlabs/arkhamjs-storage-browser](https://www.npmjs.com/package/@nlabs/arkhamjs-storage-browser) - ArkhamJS browser storage. Caches state in session or local storage. 9 | - [@nlabs/arkhamjs-middleware-logger](https://www.npmjs.com/package/@nlabs/arkhamjs-middleware-logger) - ArkhamJS console log middleware. 10 | 11 | [![npm version](https://img.shields.io/npm/v/@nlabs/arkhamjs-example-ts-react.svg?style=flat-square)](https://www.npmjs.com/package/@nlabs/arkhamjs-example-ts-react) 12 | [![npm downloads](https://img.shields.io/npm/dm/@nlabs/arkhamjs-example-ts-react.svg?style=flat-square)](https://www.npmjs.com/package/@nlabs/arkhamjs-example-ts-react) 13 | [![Travis](https://img.shields.io/travis/nitrogenlabs/arkhamjs.svg?style=flat-square)](https://travis-ci.org/nitrogenlabs/arkhamjs) 14 | [![Issues](https://img.shields.io/github/issues/nitrogenlabs/arkhamjs.svg?style=flat-square)](https://github.com/nitrogenlabs/arkhamjs/issues) 15 | [![TypeScript](https://badges.frapsoft.com/typescript/version/typescript-next.svg?v=101)](https://github.com/ellerbrock/typescript-badges/) 16 | [![MIT license](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](http://opensource.org/licenses/MIT) 17 | [![Chat](https://img.shields.io/discord/446122412715802649.svg)](https://discord.gg/Ttgev58) 18 | 19 | ## Getting Started 20 | 21 | --------------- 22 | 23 | - Clone the repo and install the necessary node modules: 24 | 25 | ```shell 26 | # Install Lex globally 27 | $ npm install -g @nlabs/lex 28 | 29 | # Download example app package and install dependencies (may take awhile the first time) 30 | $ lex init exampleApp @nlabs/arkhamjs-example-ts-react -i 31 | ``` 32 | 33 | ## Usage 34 | 35 | --------------- 36 | 37 | ### `npm run start` also `npm run development` 38 | 39 | Runs the webpack build system to compile scripts on the fly. Also runs a local development web server which can be found at `localhost:9000`. The port can be changed in the config. 40 | 41 | ### `npm run build` 42 | 43 | Compile your application and copy static files for a production environment. 44 | 45 | ### `npm run clean` 46 | 47 | Clean your app directory. Removes *coverage*, *node_modules*, *npm-debug.log*, *package-lock.log*. 48 | 49 | ### `npm run lint` 50 | 51 | Lint your app with tslint. 52 | 53 | ### `npm run test` 54 | 55 | Runs all unit tests within the app with Jest. 56 | 57 | ### `npm run production` 58 | 59 | Run tests and then, on success, compile your application for a production environment. 60 | 61 | ## Configuration 62 | 63 | --------------- 64 | 65 | See [@nlabs/lex](https://www.npmjs.com/package/@nlabs/lex) for documentation on custom configuration. 66 | 67 | ## Structure 68 | 69 | --------------- 70 | 71 | The folder structure provided is only meant to serve as a guide, it is by no means prescriptive. It is something that has worked very well for me and my team, but use only what makes sense to you. 72 | 73 | ```shell 74 | . 75 | ├── coverage # Unit test coverage reports 76 | ├── dist # Compiled files ready to be deployed 77 | ├── src # Application source code 78 | │ ├── actions # ArkhamJS Flux actions 79 | │ ├── components # React components 80 | │ ├── config # App configuration 81 | │ ├── constants # App constants 82 | │ ├── errors # Custom errors 83 | │ ├── fonts # Font files 84 | │ ├── icons # SVG files 85 | │ ├── img # Images 86 | │ ├── services # Helpers and utilities 87 | │ ├── stores # ArkhamJS store configurations 88 | │ ├── styles # CSS styles 89 | │ ├── views # React components/views that live at a route 90 | │ ├── app.css # Entry CSS file 91 | │ ├── index.html # Entry HTML file 92 | │ └── index.tsx # Entry JS to bootstrap and render 93 | ├── .eslintrc # ESLint rules 94 | ├── .travis.yml # Travis-CI configuration 95 | ├── lex.config.js # Optional Lex configuration 96 | ├── LICENSE # License details 97 | ├── package.json # Package dependencies and configuration 98 | ├── README.md # Readme file to detail the app and configurations 99 | └── tsconfig.json # Typescript configuration (only used for definitions) 100 | ``` 101 | 102 | ### Components vs. Views vs. Layouts 103 | 104 | **TL;DR:** They're all components. 105 | 106 | This distinction may not be important for you, but as an explanation: A **Layout** is something that describes an entire page structure, such as a fixed navigation, viewport, sidebar, and footer. Most applications will probably only have one layout, but keeping these components separate makes their intent clear. **Views** are components that live at routes, and are generally rendered within a **Layout**. What this ends up meaning is that, with this structure, nearly everything inside of **Components** ends up being a dumb component. 107 | 108 | ## Styles 109 | 110 | --------------- 111 | 112 | All `.css` imports will be run through postcss and cssnext, extracted and compiled during builds. CSS features included are nested classes and SASS-like variables. Styles must be imported either directly within the js file or via another stylesheet which has already been imported. 113 | 114 | ```js 115 | // JS 116 | import `./component.css`; 117 | ``` 118 | 119 | ## Testing 120 | 121 | --------------- 122 | 123 | To add a unit test, simply create a `*.test.ts` or `*.test.tsx` file within the `/src` directory. Jest will look for these for and test these files. 124 | 125 | ## Troubleshooting 126 | 127 | --------------- 128 | 129 | Nothing yet. Having an issue? Report it and We'll get to it as soon as possible! 130 | -------------------------------------------------------------------------------- /packages/arkhamjs-example-ts-react/lex.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'web', 3 | useTypescript: true 4 | }; 5 | -------------------------------------------------------------------------------- /packages/arkhamjs-example-ts-react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@nlabs/arkhamjs-example-ts-react", 3 | "version": "3.28.0", 4 | "publishConfig": { 5 | "access": "public" 6 | }, 7 | "description": "ArkhamJS React Typescript Example App", 8 | "license": "MIT", 9 | "keywords": [ 10 | "arkhamjs", 11 | "example", 12 | "nitrogenlabs", 13 | "react", 14 | "typescript", 15 | "skeleton", 16 | "lex" 17 | ], 18 | "author": { 19 | "name": "Giraldo Rosales", 20 | "email": "giraldo@nitrogenlabs.com", 21 | "url": "http://nitrogenlabs.com" 22 | }, 23 | "repository": { 24 | "type": "git", 25 | "url": "nitrogenlabs/arkhamjs.git" 26 | }, 27 | "homepage": "https://arkhamjs.io", 28 | "bugs": { 29 | "url": "https://github.com/nitrogenlabs/arkhamjs/issues" 30 | }, 31 | "scripts": { 32 | "build": "lex build --remove", 33 | "clean": "lex clean", 34 | "dev": "lex dev", 35 | "lint": "eslint ./src --ext .ts,.tsx", 36 | "prepublishOnly": "npm run build", 37 | "publish:major": "npm version major && npm publish", 38 | "publish:minor": "npm version minor && npm publish", 39 | "publish:patch": "npm version patch && npm publish", 40 | "pretest": "npm run lint", 41 | "prod": "npm run test && npm run build", 42 | "start": "lex dev --open", 43 | "test": "lex test", 44 | "update": "npm-check-updates --interactive" 45 | }, 46 | "dependencies": { 47 | "@nlabs/arkhamjs": "*", 48 | "@nlabs/arkhamjs-middleware-logger": "*", 49 | "@nlabs/arkhamjs-storage-browser": "*", 50 | "@nlabs/arkhamjs-utils-react": "*", 51 | "bootstrap": "^5.3.2", 52 | "lodash": "^4.17.21", 53 | "react": "^19.0.0", 54 | "react-dom": "^19.0.0", 55 | "react-jss": "^10.10.0" 56 | }, 57 | "devDependencies": { 58 | "@types/jest": "^29.5.11", 59 | "@types/node": "^20.11.5", 60 | "@types/react": "^18.2.48", 61 | "@types/react-dom": "^18.2.18", 62 | "eslint": "^8.56.0", 63 | "eslint-config-styleguidejs": "^3.2.1", 64 | "react-test-renderer": "^19.0.0", 65 | "typescript": "^5.3.3" 66 | }, 67 | "gitHead": "fc371e1e28fe0ae35d40d29a217d5f0e990ec32a" 68 | } 69 | -------------------------------------------------------------------------------- /packages/arkhamjs-example-ts-react/src/actions/AppActions/AppActions.test.ts: -------------------------------------------------------------------------------- 1 | import {AppConstants} from '../../constants/AppConstants'; 2 | import {updateContent} from './AppActions'; 3 | 4 | describe('AppActions', () => { 5 | const content: string = 'test'; 6 | 7 | describe('updateContent', () => { 8 | let action; 9 | 10 | beforeAll(async () => { 11 | // Method 12 | action = await updateContent(content); 13 | }); 14 | 15 | it('should dispatch AppConstants.UPDATE_CONTENT', () => { 16 | expect(action.type).toBe(AppConstants.UPDATE_CONTENT); 17 | }); 18 | 19 | it('should contain content in action', () => { 20 | expect(action.content).toBe(content); 21 | }); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /packages/arkhamjs-example-ts-react/src/actions/AppActions/AppActions.ts: -------------------------------------------------------------------------------- 1 | import {Flux, FluxAction} from '@nlabs/arkhamjs'; 2 | 3 | import {AppConstants} from '../../constants/AppConstants'; 4 | 5 | export const updateContent = (content: string): Promise => 6 | Flux.dispatch({content, type: AppConstants.UPDATE_CONTENT}); 7 | -------------------------------------------------------------------------------- /packages/arkhamjs-example-ts-react/src/app.css: -------------------------------------------------------------------------------- 1 | /* Base */ 2 | @import 'bootstrap/dist/css/bootstrap.css'; 3 | 4 | body, p, h1 { 5 | font-family: 'Open Sans', sans-serif; 6 | } -------------------------------------------------------------------------------- /packages/arkhamjs-example-ts-react/src/components/Icon/Icon.test.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as renderer from 'react-test-renderer'; 3 | 4 | import {Icon} from './Icon'; 5 | 6 | describe('Icon', () => { 7 | let rendered; 8 | 9 | beforeAll(() => { 10 | rendered = renderer.create(); 11 | }); 12 | 13 | it('should render', () => expect(rendered).toBeDefined()); 14 | }); 15 | -------------------------------------------------------------------------------- /packages/arkhamjs-example-ts-react/src/components/Icon/Icon.tsx: -------------------------------------------------------------------------------- 1 | import {createUseStyles} from 'react-jss'; 2 | 3 | import {IconProps, IconSize} from './Icon.types'; 4 | 5 | const useStyles = createUseStyles({ 6 | icon: ({iconSize}) => ({ 7 | backgroundColor: 'transparent', 8 | color: 'inherit', 9 | display: 'block', 10 | fill: 'currentColor', 11 | height: iconSize, 12 | verticalAlign: 'middle', 13 | width: iconSize, 14 | 15 | '& svg': { 16 | fill: 'inherit', 17 | height: '100%', 18 | width: '100%', 19 | 20 | '& symbol path': { 21 | all: 'inherit' 22 | } 23 | } 24 | }) 25 | }); 26 | 27 | export const Icon = (props: IconProps): JSX.Element => { 28 | const { 29 | className = '', 30 | name = 'icon', 31 | size: propSize = '' 32 | } = props; 33 | const size: IconSize = propSize.toLowerCase().trim() as IconSize; 34 | 35 | // Icon sizes 36 | let iconSize; 37 | 38 | switch(size) { 39 | case 'md': 40 | iconSize = 32; 41 | break; 42 | case 'lg': 43 | iconSize = 64; 44 | break; 45 | case 'xl': 46 | iconSize = 128; 47 | break; 48 | case 'xx': 49 | iconSize = 256; 50 | break; 51 | default: 52 | iconSize = 16; 53 | break; 54 | } 55 | 56 | // Styles 57 | const classes = useStyles({iconSize}); 58 | const styleClasses: string[] = [name, classes.icon, className]; 59 | 60 | // SVG 61 | const useTag: string = ``; 62 | return ; 63 | }; 64 | -------------------------------------------------------------------------------- /packages/arkhamjs-example-ts-react/src/components/Icon/Icon.types.ts: -------------------------------------------------------------------------------- 1 | export type IconSize = 'sm' | 'md' | 'lg' | 'xl' | 'xx'; 2 | 3 | export interface IconProps { 4 | readonly className?: string; 5 | readonly name: string; 6 | readonly size?: IconSize; 7 | } 8 | -------------------------------------------------------------------------------- /packages/arkhamjs-example-ts-react/src/config/config.types.ts: -------------------------------------------------------------------------------- 1 | export interface AppConfig { 2 | readonly appId?: string; 3 | readonly appName?: string; 4 | readonly env?: string; 5 | } 6 | 7 | export interface EnvConfig { 8 | readonly default: AppConfig; 9 | readonly development: AppConfig; 10 | readonly preprod: AppConfig; 11 | readonly production: AppConfig; 12 | readonly test: AppConfig; 13 | } 14 | -------------------------------------------------------------------------------- /packages/arkhamjs-example-ts-react/src/config/index.ts: -------------------------------------------------------------------------------- 1 | import get from 'lodash/get'; 2 | import merge from 'lodash/merge'; 3 | 4 | import {EnvConfig} from './config.types'; 5 | 6 | const {NODE_ENV} = process.env; 7 | 8 | export class Config { 9 | static values: EnvConfig = { 10 | default: { 11 | appId: 'arkhamjs-skeleton', 12 | env: NODE_ENV 13 | }, 14 | development: { 15 | appName: 'Arkham Skeleton Development' 16 | }, 17 | preprod: { 18 | appName: 'Arkham Skeleton Pre-Production' 19 | }, 20 | production: { 21 | appName: 'Arkham Skeleton Production' 22 | }, 23 | test: { 24 | appName: 'Arkham Skeleton Test' 25 | } 26 | }; 27 | 28 | static get(path: string | string[]): any { 29 | const environment: string = NODE_ENV || 'development'; 30 | const configValues: object = merge(this.values.default, this.values[environment], {environment}); 31 | return get(configValues, path); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/arkhamjs-example-ts-react/src/constants/AppConstants.ts: -------------------------------------------------------------------------------- 1 | export class AppConstants { 2 | static readonly UPDATE_CONTENT: string = 'APP_UPDATE_CONTENT'; 3 | } 4 | -------------------------------------------------------------------------------- /packages/arkhamjs-example-ts-react/src/errors/UserError.ts: -------------------------------------------------------------------------------- 1 | export class UserError extends Error { 2 | errors: string[]; 3 | 4 | constructor(msg: string, errors: string[] = []) { 5 | super(msg); 6 | this.errors = errors || []; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/arkhamjs-example-ts-react/src/fonts/readme.txt: -------------------------------------------------------------------------------- 1 | Any font files to be imported would be placed in the fonts directory. -------------------------------------------------------------------------------- /packages/arkhamjs-example-ts-react/src/icons/pencil.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /packages/arkhamjs-example-ts-react/src/icons/readme.txt: -------------------------------------------------------------------------------- 1 | Any SVG icons to be imported would be placed in the icons directory. They will be consolidated into one file, icons.svg. -------------------------------------------------------------------------------- /packages/arkhamjs-example-ts-react/src/img/arkhamjs-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitrogenlabs/arkhamjs/9428e95a52f4a7323609da35ad9c29223eb8eafa/packages/arkhamjs-example-ts-react/src/img/arkhamjs-logo.png -------------------------------------------------------------------------------- /packages/arkhamjs-example-ts-react/src/img/readme.txt: -------------------------------------------------------------------------------- 1 | Any images to be imported would be placed in the img directory. -------------------------------------------------------------------------------- /packages/arkhamjs-example-ts-react/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ArkhamJS 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | -------------------------------------------------------------------------------- /packages/arkhamjs-example-ts-react/src/index.tsx: -------------------------------------------------------------------------------- 1 | import './app.css'; 2 | 3 | import * as ReactDOM from 'react-dom'; 4 | 5 | import {AppView} from './views/AppView'; 6 | 7 | const target = document.getElementById('app'); 8 | 9 | // Render initial ReactJS code 10 | ReactDOM.render(, target); 11 | -------------------------------------------------------------------------------- /packages/arkhamjs-example-ts-react/src/services/StringService.test.ts: -------------------------------------------------------------------------------- 1 | import {capitalize, uppercaseWords} from './StringService'; 2 | 3 | describe('StringService', () => { 4 | describe('.uppercaseWords', () => { 5 | it('should uppercase words', () => { 6 | const str = 'test string'; 7 | const expected = 'Test String'; 8 | return expect(uppercaseWords(str)).toBe(expected); 9 | }); 10 | }); 11 | 12 | describe('.capitalize', () => { 13 | it('should capitalize word', () => { 14 | const str = 'test'; 15 | const expected = 'Test'; 16 | return expect(capitalize(str)).toBe(expected); 17 | }); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /packages/arkhamjs-example-ts-react/src/services/StringService.ts: -------------------------------------------------------------------------------- 1 | 2 | export const capitalize = (str: string = ''): string => { 3 | if(str.length > 1) { 4 | return `${str.charAt(0).toUpperCase()}${str.substr(1).toLowerCase()}`; 5 | } 6 | 7 | return str.toUpperCase(); 8 | }; 9 | export const uppercaseWords = (str: string = ''): string => (str || '').replace(/\w\S*/g, (txt: string) => capitalize(txt)); 10 | -------------------------------------------------------------------------------- /packages/arkhamjs-example-ts-react/src/stores/AppStore/appStore.test.ts: -------------------------------------------------------------------------------- 1 | import {AppConstants} from '../../constants/AppConstants'; 2 | import {app, initialState} from './appStore'; 3 | 4 | describe('app', () => { 5 | describe('action', () => { 6 | it('should listen for default', () => { 7 | const content = 'test'; 8 | const data = {content}; 9 | const state = app('default', data, initialState); 10 | return expect(state).toBe(initialState); 11 | }); 12 | 13 | it('should listen for AppConstants.UPDATE_CONTENT', () => { 14 | const content = 'test'; 15 | const data = {content}; 16 | const state = app(AppConstants.UPDATE_CONTENT, data, initialState); 17 | return expect(state.content).toBe(content); 18 | }); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /packages/arkhamjs-example-ts-react/src/stores/AppStore/appStore.ts: -------------------------------------------------------------------------------- 1 | import {AppConstants} from '../../constants/AppConstants'; 2 | import {AppState} from './appStore.types'; 3 | 4 | export const initialState: AppState = { 5 | content: 'Hello World' 6 | }; 7 | 8 | export const app = (type: string, data: any, state: AppState = initialState): AppState => { 9 | switch(type) { 10 | case AppConstants.UPDATE_CONTENT: { 11 | const {content} = data; 12 | return {...state, content}; 13 | } 14 | default: 15 | return state; 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /packages/arkhamjs-example-ts-react/src/stores/AppStore/appStore.types.ts: -------------------------------------------------------------------------------- 1 | export interface AppState { 2 | readonly content: string; 3 | } 4 | -------------------------------------------------------------------------------- /packages/arkhamjs-example-ts-react/src/views/AppView.test.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as renderer from 'react-test-renderer'; 3 | 4 | import {AppView} from './AppView'; 5 | 6 | describe('AppView', () => { 7 | let rendered; 8 | 9 | beforeAll(() => { 10 | // Render 11 | rendered = renderer.create(); 12 | }); 13 | 14 | it('should render', () => expect(rendered).toBeDefined()); 15 | }); 16 | -------------------------------------------------------------------------------- /packages/arkhamjs-example-ts-react/src/views/AppView.tsx: -------------------------------------------------------------------------------- 1 | import {Flux} from '@nlabs/arkhamjs'; 2 | import {Logger, LoggerDebugLevel} from '@nlabs/arkhamjs-middleware-logger'; 3 | import {BrowserStorage} from '@nlabs/arkhamjs-storage-browser'; 4 | import {FluxProvider} from '@nlabs/arkhamjs-utils-react'; 5 | import {useEffect, useState} from 'react'; 6 | 7 | import {Config} from '../config'; 8 | import {app} from '../stores/appStore/appStore'; 9 | import {HomeView} from './HomeView'; 10 | 11 | export const onUpdateContent = (setContent) => (): void => { 12 | const content = Flux.getState('app.content', ''); 13 | setContent(content); 14 | }; 15 | 16 | export const AppView = (): JSX.Element => { 17 | // ArkhamJS Middleware 18 | const env: string = Config.get('environment'); 19 | const logger: Logger = new Logger({ 20 | debugLevel: env === 'development' ? LoggerDebugLevel.DISPATCH : LoggerDebugLevel.DISABLED 21 | }); 22 | 23 | const [content, setContent] = useState(Flux.getState('app.content', '')); 24 | 25 | // ArkhamJS Configuration 26 | Flux.init({ 27 | middleware: [logger], 28 | name: 'arkhamExampleReact', 29 | storage: new BrowserStorage({type: 'session'}), 30 | stores: [app] 31 | }); 32 | 33 | useEffect(() => { 34 | const onUpdate = onUpdateContent(setContent); 35 | 36 | // When app initializes and gets any data from persistent storage 37 | Flux.onInit(onUpdate); 38 | 39 | return () => { 40 | Flux.offInit(onUpdate); 41 | }; 42 | }); 43 | 44 | return ( 45 | 46 | 47 | 48 | ); 49 | }; 50 | -------------------------------------------------------------------------------- /packages/arkhamjs-example-ts-react/src/views/HomeView.test.tsx: -------------------------------------------------------------------------------- 1 | import {FluxProvider} from '@nlabs/arkhamjs-utils-react'; 2 | import * as React from 'react'; 3 | import * as renderer from 'react-test-renderer'; 4 | 5 | import {HomeView} from './HomeView'; 6 | 7 | describe('HomeView', () => { 8 | let rendered; 9 | 10 | beforeAll(() => { 11 | const fluxMock: any = jest.fn(); 12 | 13 | // Render 14 | rendered = renderer.create(); 15 | }); 16 | 17 | it('should render', () => expect(rendered).toBeDefined()); 18 | }); 19 | -------------------------------------------------------------------------------- /packages/arkhamjs-example-ts-react/src/views/HomeView.tsx: -------------------------------------------------------------------------------- 1 | import {useFluxListener} from '@nlabs/arkhamjs-utils-react'; 2 | import {useRef, useState} from 'react'; 3 | import {createUseStyles} from 'react-jss'; 4 | 5 | import {updateContent} from '../actions/AppActions/AppActions'; 6 | import {Icon} from '../components/Icon/Icon'; 7 | import {AppConstants} from '../constants/AppConstants'; 8 | import {uppercaseWords} from '../services/StringService'; 9 | 10 | const useStyles = createUseStyles({ 11 | logo: { 12 | display: 'flex', 13 | justifyContent: 'center', 14 | marginBottom: 100 15 | }, 16 | logoImg: { 17 | height: 94, 18 | width: 403 19 | }, 20 | helloTxt: { 21 | fontSize: 30, 22 | fontStyle: 'italic', 23 | fontWeight: 100, 24 | textAlign: 'center' 25 | }, 26 | form: { 27 | display: 'flex', 28 | flexDirection: 'column' 29 | }, 30 | input: { 31 | alignSelf: 'stretch', 32 | border: '1px solid #ccc', 33 | padding: '10px 15px', 34 | margin: '30px 0' 35 | }, 36 | button: { 37 | alignSelf: 'flex-end', 38 | alignContent: 'center', 39 | display: 'flex', 40 | flexDirection: 'row', 41 | justifyContent: 'center' 42 | 43 | }, 44 | btnIcon: { 45 | alignSelf: 'center', 46 | marginRight: 5 47 | } 48 | }); 49 | 50 | export const onChange = (inputRef) => () => { 51 | if(inputRef.current) { 52 | const {value} = inputRef.current; 53 | updateContent(value); 54 | } 55 | }; 56 | 57 | export const onUpdateContent = (setContent) => ({content}) => { 58 | setContent(content); 59 | }; 60 | 61 | export const HomeView = ({initialContent}) => { 62 | // State 63 | const [content, setContent] = useState(initialContent); 64 | const inputRef = useRef(); 65 | 66 | useFluxListener(AppConstants.UPDATE_CONTENT, onUpdateContent(setContent)); 67 | 68 | // Styles 69 | const classes = useStyles(); 70 | 71 | return ( 72 |
73 |
74 |
75 |
76 | 77 | 78 | 79 |
80 |
{uppercaseWords(content)}
81 |
82 | 83 | 87 |
88 |
89 |
90 |
91 | ); 92 | }; 93 | -------------------------------------------------------------------------------- /packages/arkhamjs-example-ts-react/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "baseUrl": "./src", 5 | "declaration": true, 6 | "emitDeclarationOnly": true, 7 | "esModuleInterop": true, 8 | "inlineSourceMap": true, 9 | "jsx": "react-jsx", 10 | "lib": [ 11 | "esnext", 12 | "dom" 13 | ], 14 | "module": "commonjs", 15 | "moduleResolution": "node", 16 | "noImplicitReturns": true, 17 | "noImplicitThis": true, 18 | "noUnusedLocals": true, 19 | "outDir": "lib", 20 | "pretty": true, 21 | "skipLibCheck": true, 22 | "target": "ES2018" 23 | }, 24 | "include": [ 25 | "./src/**/*" 26 | ], 27 | "exclude": [ 28 | "**/*.test.*", 29 | "lib", 30 | "./node_modules", 31 | "./test" 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /packages/arkhamjs-middleware-devtools/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "styleguidejs/typescript", 3 | "rules": { 4 | "import/no-extraneous-dependencies": 0 5 | } 6 | } -------------------------------------------------------------------------------- /packages/arkhamjs-middleware-devtools/.gitignore: -------------------------------------------------------------------------------- 1 | coverage 2 | lib 3 | node_modules 4 | *.log -------------------------------------------------------------------------------- /packages/arkhamjs-middleware-devtools/.npmignore: -------------------------------------------------------------------------------- 1 | coverage 2 | docs 3 | node_modules 4 | src 5 | .eslintrc 6 | jest.setup.js 7 | package-lock.json 8 | tsconfig.json 9 | -------------------------------------------------------------------------------- /packages/arkhamjs-middleware-devtools/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Nitrogen Labs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/arkhamjs-middleware-devtools/README.md: -------------------------------------------------------------------------------- 1 | # @nlabs/arkhamjs-middleware-devtools 2 | 3 | ArkhamJS DevTools Middleware 4 | 5 | [![npm version](https://img.shields.io/npm/v/@nlabs/arkhamjs-middleware-devtools.svg?style=flat-square)](https://www.npmjs.com/package/@nlabs/arkhamjs-middleware-devtools) 6 | [![npm downloads](https://img.shields.io/npm/dm/@nlabs/arkhamjs-middleware-devtools.svg?style=flat-square)](https://www.npmjs.com/package/@nlabs/arkhamjs-middleware-devtools) 7 | [![Travis](https://img.shields.io/travis/nitrogenlabs/arkhamjs.svg?style=flat-square)](https://travis-ci.org/nitrogenlabs/arkhamjs) 8 | [![Issues](https://img.shields.io/github/issues/nitrogenlabs/arkhamjs.svg?style=flat-square)](https://github.com/nitrogenlabs/arkhamjs/issues) 9 | [![TypeScript](https://badges.frapsoft.com/typescript/version/typescript-next.svg?v=101)](https://github.com/ellerbrock/typescript-badges/) 10 | [![MIT license](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](http://opensource.org/licenses/MIT) 11 | [![Chat](https://img.shields.io/discord/446122412715802649.svg)](https://discord.gg/Ttgev58) 12 | 13 | ## Installation 14 | 15 | Using [npm](https://www.npmjs.com/): 16 | 17 | ```shell 18 | npm i --save @nlabs/arkhamjs-middleware-devtools 19 | ``` 20 | 21 | ## Documentation 22 | 23 | For detailed [Documentation](https://arkhamjs.io) and additional options. 24 | -------------------------------------------------------------------------------- /packages/arkhamjs-middleware-devtools/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2018-Present, Nitrogen Labs, Inc. 3 | * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. 4 | */ 5 | const lib = require('./lib'); 6 | 7 | // ArkhamJS middleware 8 | exports.DevTools = lib.DevTools; 9 | -------------------------------------------------------------------------------- /packages/arkhamjs-middleware-devtools/jest.setup.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitrogenlabs/arkhamjs/9428e95a52f4a7323609da35ad9c29223eb8eafa/packages/arkhamjs-middleware-devtools/jest.setup.js -------------------------------------------------------------------------------- /packages/arkhamjs-middleware-devtools/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@nlabs/arkhamjs-middleware-devtools", 3 | "version": "3.11.8", 4 | "publishConfig": { 5 | "access": "public" 6 | }, 7 | "description": "Add devtools to ArkhamJS app", 8 | "license": "MIT", 9 | "main": "./lib/index.js", 10 | "module": "./lib/index.js", 11 | "browser": "./lib/index.js", 12 | "types": "./lib/index.d.ts", 13 | "keywords": [ 14 | "arkhamjs", 15 | "flux", 16 | "immutable", 17 | "middleware", 18 | "nitrogenlabs", 19 | "devtools" 20 | ], 21 | "author": { 22 | "name": "Giraldo Rosales", 23 | "email": "giraldo@nitrogenlabs.com", 24 | "url": "http://nitrogenlabs.com" 25 | }, 26 | "repository": { 27 | "type": "git", 28 | "url": "nitrogenlabs/arkhamjs.git" 29 | }, 30 | "homepage": "https://arkhamjs.io", 31 | "bugs": { 32 | "url": "https://github.com/nitrogenlabs/arkhamjs/issues" 33 | }, 34 | "scripts": { 35 | "build": "lex compile --remove", 36 | "clean": "lex clean", 37 | "lint": "eslint ./src --ext .ts,.tsx", 38 | "prepublishOnly": "npm run build", 39 | "publish:major": "npm version major && npm publish", 40 | "publish:minor": "npm version minor && npm publish", 41 | "publish:patch": "npm version patch && npm publish", 42 | "pretest": "npm run lint", 43 | "test": "lex test", 44 | "update": "npm-check-updates --interactive" 45 | }, 46 | "peerDependencies": { 47 | "@nlabs/arkhamjs": "^3.23", 48 | "redux": "^3.0" 49 | }, 50 | "devDependencies": { 51 | "@nlabs/arkhamjs": "^3.22.0", 52 | "@types/jest": "^27.4.0", 53 | "@types/node": "^17.0.17", 54 | "eslint": "^7.32.0", 55 | "eslint-config-styleguidejs": "^1.5.4", 56 | "typescript": "^4.5.5" 57 | }, 58 | "gitHead": "ca196f147316f6590794c49f53965ab6770b2dd6" 59 | } 60 | -------------------------------------------------------------------------------- /packages/arkhamjs-middleware-devtools/src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2018-Present, Nitrogen Labs, Inc. 3 | * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. 4 | */ 5 | 6 | // Middleware for ArkhamJS 7 | export {DevTools} from './middleware/DevTools'; 8 | -------------------------------------------------------------------------------- /packages/arkhamjs-middleware-devtools/src/middleware/DevTools.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2018-Present, Nitrogen Labs, Inc. 3 | * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. 4 | */ 5 | import {Flux, FluxAction} from '@nlabs/arkhamjs'; 6 | 7 | export class DevTools { 8 | name: string = 'DevTools'; 9 | mode: string = 'development'; 10 | 11 | constructor(options) { 12 | // Methods 13 | this.postDispatch = this.postDispatch.bind(this); 14 | this.postMessage = this.postMessage.bind(this); 15 | this.setMode = this.setMode.bind(this); 16 | 17 | // Set options 18 | Flux.onInit(() => { 19 | const arkhamOptions: string = JSON.stringify(Flux.getOptions(), null, 0); 20 | this.postMessage({_arkhamInfo: arkhamOptions}); 21 | }); 22 | 23 | // Set initial mode 24 | const {mode = 'development'} = options; 25 | this.setMode(mode); 26 | } 27 | 28 | get isActive(): boolean { 29 | return this.mode === 'development'; 30 | } 31 | 32 | setMode(mode: string = 'development'): void { 33 | this.mode = mode; 34 | 35 | if(mode === 'development') { 36 | window.addEventListener('message', this.sendMethod); 37 | } else { 38 | window.removeEventListener('message', this.sendMethod); 39 | } 40 | } 41 | 42 | sendMethod(event): void { 43 | const {_arkhamCall: {method = '', args = []} = {}} = event.data; 44 | console.log('middleware::method', method); 45 | 46 | if(method !== '') { 47 | switch(method) { 48 | case 'dispatch': 49 | Flux[method](...args); 50 | break; 51 | case 'storeClasses': { 52 | const stores = Flux[method]; 53 | const storeDetails = Object.keys(stores).map((storeName: string) => { 54 | const store = stores[storeName]; 55 | return {initialState: store.initialState(), name: store.name}; 56 | }); 57 | console.log('storeClasses', storeDetails); 58 | break; 59 | } 60 | } 61 | } 62 | } 63 | 64 | postMessage(data): void { 65 | window.postMessage(data, '*'); 66 | } 67 | 68 | postDispatch(action, state, {duration, options, stack}): Promise { 69 | if(this.isActive) { 70 | const dispatchData = {action, duration, stack, state}; 71 | const data: string = JSON.stringify(dispatchData, null, 0); 72 | const optionsData: string = JSON.stringify(options, null, 0); 73 | this.postMessage({_arkhamDispatch: data, _arkhamInfo: optionsData}); 74 | } 75 | 76 | return Promise.resolve(action); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /packages/arkhamjs-middleware-devtools/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "baseUrl": "./src", 5 | "declaration": true, 6 | "emitDeclarationOnly": true, 7 | "esModuleInterop": true, 8 | "inlineSourceMap": true, 9 | "jsx": "react-jsx", 10 | "lib": [ 11 | "esnext", 12 | "dom" 13 | ], 14 | "module": "commonjs", 15 | "moduleResolution": "node", 16 | "noImplicitReturns": true, 17 | "noImplicitThis": true, 18 | "noUnusedLocals": true, 19 | "outDir": "lib", 20 | "pretty": true, 21 | "skipLibCheck": true, 22 | "target": "ES2018", 23 | "typeRoots": [ 24 | "node_modules/@types", 25 | "node_modules/json-d-ts" 26 | ] 27 | }, 28 | "include": [ 29 | "./src/**/*" 30 | ], 31 | "exclude": [ 32 | "**/*.test.*", 33 | "lib", 34 | "./node_modules", 35 | "./test" 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /packages/arkhamjs-middleware-logger/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "styleguidejs/typescript", 3 | "rules": { 4 | "import/no-extraneous-dependencies": 0 5 | } 6 | } -------------------------------------------------------------------------------- /packages/arkhamjs-middleware-logger/.gitignore: -------------------------------------------------------------------------------- 1 | coverage 2 | lib 3 | node_modules 4 | *.log -------------------------------------------------------------------------------- /packages/arkhamjs-middleware-logger/.npmignore: -------------------------------------------------------------------------------- 1 | coverage 2 | docs 3 | node_modules 4 | src 5 | .eslintrc 6 | lex.config.js 7 | package-lock.json 8 | tsconfig.json 9 | -------------------------------------------------------------------------------- /packages/arkhamjs-middleware-logger/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Nitrogen Labs, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/arkhamjs-middleware-logger/README.md: -------------------------------------------------------------------------------- 1 | # @nlabs/arkhamjs-middleware-logger 2 | 3 | Add console logging for ArkhamJS 4 | 5 | [![npm version](https://img.shields.io/npm/v/@nlabs/arkhamjs-middleware-logger.svg?style=flat-square)](https://www.npmjs.com/package/@nlabs/arkhamjs-middleware-logger) 6 | [![npm downloads](https://img.shields.io/npm/dm/@nlabs/arkhamjs-middleware-logger.svg?style=flat-square)](https://www.npmjs.com/package/@nlabs/arkhamjs-middleware-logger) 7 | [![Travis](https://img.shields.io/travis/nitrogenlabs/arkhamjs.svg?style=flat-square)](https://travis-ci.org/nitrogenlabs/arkhamjs) 8 | [![Issues](https://img.shields.io/github/issues/nitrogenlabs/arkhamjs.svg?style=flat-square)](https://github.com/nitrogenlabs/arkhamjs/issues) 9 | [![TypeScript](https://badges.frapsoft.com/typescript/version/typescript-next.svg?v=101)](https://github.com/ellerbrock/typescript-badges/) 10 | [![MIT license](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](http://opensource.org/licenses/MIT) 11 | [![Chat](https://img.shields.io/discord/446122412715802649.svg)](https://discord.gg/Ttgev58) 12 | 13 | ## Installation 14 | 15 | Using [npm](https://www.npmjs.com/): 16 | 17 | ```shell 18 | npm i --save @nlabs/arkhamjs-middleware-logger 19 | ``` 20 | 21 | ## Documentation 22 | 23 | For detailed [Documentation](https://arkhamjs.io) and additional options. 24 | -------------------------------------------------------------------------------- /packages/arkhamjs-middleware-logger/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2018-Present, Nitrogen Labs, Inc. 3 | * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. 4 | */ 5 | module.exports = require('./lib'); 6 | -------------------------------------------------------------------------------- /packages/arkhamjs-middleware-logger/lex.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | outputPath: 'lib', 3 | preset: 'web', 4 | useTypescript: true 5 | }; 6 | -------------------------------------------------------------------------------- /packages/arkhamjs-middleware-logger/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@nlabs/arkhamjs-middleware-logger", 3 | "version": "3.28.5", 4 | "publishConfig": { 5 | "access": "public" 6 | }, 7 | "description": "Add console logging for ArkhamJS", 8 | "license": "MIT", 9 | "main": "./lib/index.js", 10 | "module": "./lib/index.js", 11 | "browser": "./lib/index.js", 12 | "types": "./lib/index.d.ts", 13 | "keywords": [ 14 | "arkhamjs", 15 | "flux", 16 | "logging", 17 | "nitrogenlabs" 18 | ], 19 | "author": { 20 | "name": "Giraldo Rosales", 21 | "email": "giraldo@nitrogenlabs.com", 22 | "url": "http://nitrogenlabs.com" 23 | }, 24 | "repository": { 25 | "type": "git", 26 | "url": "nitrogenlabs/arkhamjs.git" 27 | }, 28 | "homepage": "https://arkhamjs.io", 29 | "bugs": { 30 | "url": "https://github.com/nitrogenlabs/arkhamjs/issues" 31 | }, 32 | "scripts": { 33 | "build": "lex compile --remove", 34 | "clean": "lex clean", 35 | "lint": "eslint ./src --ext .ts,.tsx", 36 | "prepublishOnly": "npm run build", 37 | "publish:major": "npm version major && npm publish", 38 | "publish:minor": "npm version minor && npm publish", 39 | "publish:patch": "npm version patch && npm publish", 40 | "pretest": "lex lint", 41 | "test": "lex test", 42 | "update": "npm-check-updates --interactive" 43 | }, 44 | "peerDependencies": { 45 | "@nlabs/arkhamjs": "*", 46 | "lodash": "^4.17" 47 | }, 48 | "devDependencies": { 49 | "@nlabs/arkhamjs": "*", 50 | "@types/jest": "^29.5.14", 51 | "@types/node": "^22.13.1", 52 | "eslint": "^9.19.0", 53 | "eslint-config-styleguidejs": "^3.2.1", 54 | "lodash": "^4.17.21", 55 | "typescript": "^5.7.3" 56 | }, 57 | "gitHead": "fc371e1e28fe0ae35d40d29a217d5f0e990ec32a" 58 | } 59 | -------------------------------------------------------------------------------- /packages/arkhamjs-middleware-logger/src/Logger/Logger.test.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2018-Present, Nitrogen Labs, Inc. 3 | * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. 4 | */ 5 | import {Flux, FluxAction} from '@nlabs/arkhamjs'; 6 | import set from 'lodash/set'; 7 | 8 | import {LoggerDebugLevel, LoggerOptions} from '../types/main'; 9 | import {Logger} from './Logger'; 10 | 11 | const test = (type: string, data, state = {hello: 'world'}) => { 12 | switch(type) { 13 | case 'TEST_EVENT': 14 | return set(state, 'testAction', data.testVar); 15 | default: 16 | return state; 17 | } 18 | }; 19 | 20 | describe('Logger', () => { 21 | const cfg: LoggerOptions = {debugLevel: LoggerDebugLevel.DISPATCH}; 22 | const logger: Logger = new Logger(cfg); 23 | const testAction: FluxAction = {hello: 'world', type: 'TEST_EVENT'}; 24 | 25 | beforeAll(async () => { 26 | await Flux.init({ 27 | stores: [test] 28 | }); 29 | }); 30 | 31 | describe('#config', () => { 32 | // Vars 33 | const opts: LoggerOptions = { 34 | debugLevel: 0 35 | }; 36 | 37 | beforeAll(() => { 38 | // Method 39 | logger.config(opts); 40 | }); 41 | 42 | afterAll(() => { 43 | logger.config(cfg); 44 | }); 45 | 46 | it('should set debugLevel', () => { 47 | const privateProperty: string = 'options'; 48 | expect(logger[privateProperty].debugLevel).toEqual(opts.debugLevel); 49 | }); 50 | }); 51 | 52 | describe('#debugError', () => { 53 | let consoleSpy; 54 | const msg: string = 'test'; 55 | 56 | beforeAll(() => { 57 | // Spy 58 | consoleSpy = jest.spyOn(console, 'error'); 59 | 60 | // Method 61 | logger.debugError(msg); 62 | }); 63 | 64 | afterAll(() => { 65 | consoleSpy.mockRestore(); 66 | }); 67 | 68 | it('should send data to console.error', () => { 69 | expect(consoleSpy.mock.calls[0][0]).toEqual(msg); 70 | }); 71 | }); 72 | 73 | describe('#debugInfo', () => { 74 | let consoleSpy; 75 | const msg: string = 'test'; 76 | 77 | beforeAll(() => { 78 | // Spy 79 | consoleSpy = jest.spyOn(console, 'info'); 80 | 81 | // Method 82 | logger.debugInfo(msg); 83 | }); 84 | 85 | afterAll(() => { 86 | consoleSpy.mockRestore(); 87 | }); 88 | 89 | it('should send data to console.info', () => { 90 | expect(consoleSpy.mock.calls[0][0]).toEqual(msg); 91 | }); 92 | }); 93 | 94 | describe('#debugLog', () => { 95 | let consoleSpy; 96 | const msg: string = 'test'; 97 | 98 | beforeAll(() => { 99 | // Spy 100 | consoleSpy = jest.spyOn(console, 'log'); 101 | 102 | // Method 103 | logger.debugLog(msg); 104 | }); 105 | 106 | afterAll(() => { 107 | consoleSpy.mockRestore(); 108 | }); 109 | 110 | it('should send data to console.log', () => { 111 | expect(consoleSpy.mock.calls[0][0]).toEqual(msg); 112 | }); 113 | }); 114 | 115 | describe('#enableDebugger', () => { 116 | it('should disable debugger', () => { 117 | logger.enableDebugger(LoggerDebugLevel.DISABLED); 118 | const options: LoggerOptions = logger.getOptions(); 119 | expect(options.debugLevel).toEqual(0); 120 | }); 121 | 122 | it('should enable debugger for logs', () => { 123 | logger.enableDebugger(LoggerDebugLevel.LOGS); 124 | const options: LoggerOptions = logger.getOptions(); 125 | expect(options.debugLevel).toEqual(1); 126 | }); 127 | 128 | it('should enable debugger for dispatch actions', () => { 129 | logger.enableDebugger(LoggerDebugLevel.DISPATCH); 130 | const options: LoggerOptions = logger.getOptions(); 131 | expect(options.debugLevel).toEqual(2); 132 | }); 133 | }); 134 | 135 | describe('#preDispatch', () => { 136 | it('should update the previous store', () => { 137 | const testStore = {test: 'test'}; 138 | const privateProperty: string = 'previousStore'; 139 | logger.preDispatch(testAction, testStore); 140 | expect(JSON.stringify(logger[privateProperty])).toEqual(JSON.stringify(testStore)); 141 | }); 142 | }); 143 | 144 | describe('#postDispatch', () => { 145 | let consoleSpy; 146 | const prevStore = {test: 'previous'}; 147 | const nextStore = {test: 'next'}; 148 | 149 | beforeEach(() => { 150 | // Spy 151 | consoleSpy = jest.spyOn(console, 'log'); 152 | }); 153 | 154 | afterEach(() => { 155 | consoleSpy.mockRestore(); 156 | }); 157 | 158 | it('should dispatch logs for a changed state', () => { 159 | // Method 160 | logger.preDispatch(testAction, prevStore); 161 | logger.postDispatch(testAction, nextStore); 162 | 163 | // expect(consoleSpy.mock.calls[0][0]).toEqual('%c Action: '); 164 | // expect(consoleSpy.mock.calls[0][2]).toEqual(testAction); 165 | // expect(consoleSpy.mock.calls[1][0]).toEqual('%c Last State: '); 166 | // expect(consoleSpy.mock.calls[1][2]).toEqual(prevStore); 167 | expect(consoleSpy.mock.calls[2][0]).toEqual('%c Changed State: '); 168 | expect(consoleSpy.mock.calls[2][2]).toEqual(nextStore); 169 | }); 170 | 171 | // it('should dispatch logs for an unchanged state', () => { 172 | // // Method 173 | // logger.preDispatch(testAction, prevStore); 174 | // logger.postDispatch(testAction, prevStore); 175 | 176 | // expect(consoleSpy.mock.calls[0][0]).toEqual('%c Action: '); 177 | // expect(consoleSpy.mock.calls[0][2]).toEqual(testAction); 178 | // expect(consoleSpy.mock.calls[1][0]).toEqual('%c Last State: '); 179 | // expect(consoleSpy.mock.calls[1][2]).toEqual(prevStore); 180 | // expect(consoleSpy.mock.calls[2][0]).toEqual('%c Unchanged State: '); 181 | // expect(consoleSpy.mock.calls[2][2]).toEqual(prevStore); 182 | // }); 183 | }); 184 | }); 185 | -------------------------------------------------------------------------------- /packages/arkhamjs-middleware-logger/src/Logger/Logger.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2018-Present, Nitrogen Labs, Inc. 3 | * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. 4 | */ 5 | 6 | import {FluxAction} from '@nlabs/arkhamjs'; 7 | import cloneDeep from 'lodash/cloneDeep'; 8 | import isEqual from 'lodash/isEqual'; 9 | 10 | import {LoggerDebugLevel, LoggerOptions} from '../types/main'; 11 | 12 | /** 13 | * FluxLogger 14 | * @type {EventEmitter} 15 | */ 16 | export class Logger { 17 | // Public properties 18 | name: string = 'Logger'; 19 | 20 | // Private properties 21 | private previousStore: any = {}; 22 | private defaultOptions: LoggerOptions = { 23 | debugLevel: LoggerDebugLevel.DISABLED 24 | }; 25 | private options: LoggerOptions = this.defaultOptions; 26 | 27 | /** 28 | * Create a new instance of the FluxLogger. 29 | * 30 | * @constructor 31 | * @this {FluxLogger} 32 | */ 33 | constructor(options: LoggerOptions) { 34 | // Methods 35 | this.config = this.config.bind(this); 36 | this.debugError = this.debugError.bind(this); 37 | this.debugInfo = this.debugInfo.bind(this); 38 | this.debugLog = this.debugLog.bind(this); 39 | this.enableDebugger = this.enableDebugger.bind(this); 40 | this.getOptions = this.getOptions.bind(this); 41 | this.postDispatch = this.postDispatch.bind(this); 42 | this.preDispatch = this.preDispatch.bind(this); 43 | 44 | // Configuration 45 | this.config(options); 46 | } 47 | 48 | /** 49 | * Set configuration options. 50 | * 51 | * @param {object} options Configuration options. 52 | */ 53 | config(options: LoggerOptions): void { 54 | this.options = {...this.defaultOptions, ...options}; 55 | } 56 | 57 | /** 58 | * Logs errors in the console. Will also call the debugErrorFnc method set in the config. 59 | * 60 | * @param {object} obj A list of JavaScript objects to output. The string representations of each of these objects 61 | * are appended together in the order listed and output. 62 | */ 63 | debugError(...obj): void { 64 | const {debugErrorFnc, debugLevel} = this.options; 65 | 66 | if(debugLevel) { 67 | console.error(...obj); 68 | } 69 | 70 | if(debugErrorFnc) { 71 | debugErrorFnc(debugLevel, ...obj); 72 | } 73 | } 74 | 75 | /** 76 | * Logs informational messages to the console. Will also call the debugInfoFnc method set in the config. 77 | * 78 | * @param {object} obj A list of JavaScript objects to output. The string representations of each of these objects 79 | * are appended together in the order listed and output. 80 | */ 81 | debugInfo(...obj): void { 82 | const {debugInfoFnc, debugLevel} = this.options; 83 | 84 | if(debugLevel) { 85 | console.info(...obj); 86 | } 87 | 88 | if(debugInfoFnc) { 89 | debugInfoFnc(debugLevel, ...obj); 90 | } 91 | } 92 | 93 | /** 94 | * Logs data in the console. Only logs when in debug mode. Will also call the debugLogFnc method set in the config. 95 | * 96 | * @param {object} obj A list of JavaScript objects to output. The string representations of each of these objects 97 | * are appended together in the order listed and output. 98 | */ 99 | debugLog(...obj): void { 100 | const {debugLogFnc, debugLevel} = this.options; 101 | 102 | if(debugLevel) { 103 | console.log(...obj); 104 | } 105 | 106 | if(debugLogFnc) { 107 | debugLogFnc(debugLevel, ...obj); 108 | } 109 | } 110 | 111 | /** 112 | * Enables the console debugger. 113 | * 114 | * @param {number} level Enable or disable the debugger. Uses the constants: 115 | * LoggerDebugLevel.DISABLED (0) - Disable. 116 | * LoggerDebugLevel.LOGS (1) - Enable console logs. 117 | * LoggerDebugLevel.DISPATCH (2) - Enable console logs and dispatch action data (default). 118 | */ 119 | enableDebugger(level: number = LoggerDebugLevel.DISPATCH): void { 120 | this.options = {...this.options, debugLevel: level}; 121 | } 122 | 123 | /** 124 | * Get the current FluxLogger options. 125 | * 126 | * @returns {LoggerOptions} the FluxLogger options object. 127 | */ 128 | getOptions(): LoggerOptions { 129 | return this.options; 130 | } 131 | 132 | preDispatch(action: FluxAction, store): Promise { 133 | this.previousStore = store; 134 | return Promise.resolve(action); 135 | } 136 | 137 | postDispatch(action: FluxAction, store: object): Promise { 138 | const {debugLevel} = this.options; 139 | 140 | if(debugLevel > LoggerDebugLevel.LOGS) { 141 | const {type} = action; 142 | const hasChanged = !isEqual(store, this.previousStore); 143 | const updatedLabel = hasChanged ? 'Changed State' : 'Unchanged State'; 144 | const updatedColor = hasChanged ? '#00d484' : '#959595'; 145 | const updatedStore = cloneDeep(store); 146 | 147 | if(console.groupCollapsed) { 148 | console.groupCollapsed(`FLUX DISPATCH: ${type}`); 149 | console.log('%c Action: ', 'color: #00C4FF', action); 150 | console.log('%c Last State: ', 'color: #959595', this.previousStore); 151 | console.log(`%c ${updatedLabel}: `, `color: ${updatedColor}`, updatedStore); 152 | console.groupEnd(); 153 | } else { 154 | console.log(`FLUX DISPATCH: ${type}`); 155 | console.log('Action: ', action); 156 | console.log('Last State: ', this.previousStore); 157 | console.log(`${updatedLabel}: `, updatedStore); 158 | } 159 | } 160 | 161 | return Promise.resolve(action); 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /packages/arkhamjs-middleware-logger/src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2018-Present, Nitrogen Labs, Inc. 3 | * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. 4 | */ 5 | 6 | import {Logger} from './Logger/Logger'; 7 | 8 | export * from './types/main'; 9 | export {Logger}; 10 | -------------------------------------------------------------------------------- /packages/arkhamjs-middleware-logger/src/types/main.ts: -------------------------------------------------------------------------------- 1 | export enum LoggerDebugLevel { 2 | DISABLED = 0, 3 | LOGS = 1, 4 | DISPATCH = 2 5 | } 6 | 7 | export interface LoggerOptions { 8 | readonly debugLevel?: LoggerDebugLevel; 9 | readonly debugErrorFnc?: (debugLevel: number, ...args) => void; 10 | readonly debugInfoFnc?: (debugLevel: number, ...args) => void; 11 | readonly debugLogFnc?: (debugLevel: number, ...args) => void; 12 | } 13 | -------------------------------------------------------------------------------- /packages/arkhamjs-middleware-logger/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "baseUrl": "./src", 5 | "declaration": true, 6 | "emitDeclarationOnly": true, 7 | "esModuleInterop": true, 8 | "inlineSourceMap": true, 9 | "jsx": "react-jsx", 10 | "lib": [ 11 | "esnext", 12 | "dom" 13 | ], 14 | "module": "commonjs", 15 | "moduleResolution": "node", 16 | "noImplicitReturns": true, 17 | "noImplicitThis": true, 18 | "noUnusedLocals": true, 19 | "outDir": "lib", 20 | "pretty": true, 21 | "skipLibCheck": true, 22 | "target": "ES2018" 23 | }, 24 | "include": [ 25 | "./src/**/*" 26 | ], 27 | "exclude": [ 28 | "**/*.test.*", 29 | "lib", 30 | "./node_modules", 31 | "./test" 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /packages/arkhamjs-middleware-redux/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "styleguidejs/typescript" 4 | ], 5 | "rules": { 6 | "no-underscore-dangle": 0 7 | } 8 | } -------------------------------------------------------------------------------- /packages/arkhamjs-middleware-redux/.gitignore: -------------------------------------------------------------------------------- 1 | coverage 2 | lib 3 | node_modules 4 | *.log -------------------------------------------------------------------------------- /packages/arkhamjs-middleware-redux/.npmignore: -------------------------------------------------------------------------------- 1 | coverage 2 | docs 3 | node_modules 4 | src 5 | .eslintrc 6 | lex.config.js 7 | package-lock.json 8 | tsconfig.json 9 | -------------------------------------------------------------------------------- /packages/arkhamjs-middleware-redux/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Nitrogen Labs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/arkhamjs-middleware-redux/README.md: -------------------------------------------------------------------------------- 1 | # @nlabs/arkhamjs-middleware-redux 2 | 3 | ArkhamJS Redux middleware integrates ArkhamJS into existing redux applications to provide access to ArkhamJS features and/or provide a simple migration path to ArkhamJS. 4 | 5 | [![npm version](https://img.shields.io/npm/v/@nlabs/arkhamjs-middleware-redux.svg?style=flat-square)](https://www.npmjs.com/package/@nlabs/arkhamjs-middleware-redux) 6 | [![npm downloads](https://img.shields.io/npm/dm/@nlabs/arkhamjs-middleware-redux.svg?style=flat-square)](https://www.npmjs.com/package/@nlabs/arkhamjs-middleware-redux) 7 | [![Travis](https://img.shields.io/travis/nitrogenlabs/arkhamjs.svg?style=flat-square)](https://travis-ci.org/nitrogenlabs/arkhamjs) 8 | [![Issues](https://img.shields.io/github/issues/nitrogenlabs/arkhamjs.svg?style=flat-square)](https://github.com/nitrogenlabs/arkhamjs/issues) 9 | [![TypeScript](https://badges.frapsoft.com/typescript/version/typescript-next.svg?v=101)](https://github.com/ellerbrock/typescript-badges/) 10 | [![MIT license](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](http://opensource.org/licenses/MIT) 11 | [![Chat](https://img.shields.io/discord/446122412715802649.svg)](https://discord.gg/Ttgev58) 12 | 13 | ## Installation 14 | 15 | ```shell 16 | npm i @nlabs/arkhamjs-middleware-redux 17 | ``` 18 | 19 | ## Usage 20 | 21 | ### `createArkhamStore` 22 | 23 | Create a Redux store that creates a two-way binding with ArkhamJS. 24 | 25 | ```javascript 26 | // Create Redux store 27 | const store = createArkhamStore({ 28 | arkhamMiddleware, 29 | devTools, 30 | flux, 31 | reducers, 32 | reduxMiddleware, 33 | statePath 34 | }); 35 | ``` 36 | 37 | A quick example on usage. 38 | 39 | ```javascript 40 | import {createArkhamStore} from '@nlabs/arkhamjs-middleware-redux'; 41 | import {Flux} from '@nlabs/arkhamjs'; 42 | import * as React from 'react'; 43 | import {render} from 'react-dom'; 44 | import {Provider} from 'react-redux'; 45 | import {App} from './components/App'; 46 | import {reducers} from './reducers'; 47 | 48 | // Initialize ArkhamJS 49 | Flux.init({name: 'reduxTodos'}); 50 | 51 | // Create Redux store 52 | const store = createArkhamStore({ 53 | flux: Flux, 54 | reducers, 55 | statePath: 'todos' 56 | }); 57 | 58 | // Render root component with store 59 | render( 60 | 61 | 62 | , 63 | document.getElementById('root') 64 | ); 65 | ``` 66 | 67 | #### Options 68 | 69 | - **flux** - *(Flux)* The Flux object you initialized in your app. 70 | - **reducers** - *(Reducer)* Redux root reducer. The reducer created by `combineReducers()`. 71 | - **statePath** - *(string[] | string)* State tree path where to set this branch of the store's state tree. 72 | - **arkhamMiddleware** - *(any[])* (optional) ArkhamJS options. Use only if intending to initialize a new instance. 73 | - **devTools** - *(boolean)* (optional) Enable/disable Redux dev tools. Default: false. 74 | - **reduxMiddleware** - *(Middleware[])* (optional) Redux middleware. Any additional Redux middleware used in the app. 75 | 76 | ### `ReduxMiddleware` 77 | 78 | ArkhamJS middleware to relay dispatched actions to Redux. 79 | 80 | ```javascript 81 | // Create ArkhamJS middleware 82 | const reduxMiddleware = new ReduxMiddleware(store, name); 83 | ``` 84 | 85 | A simple usage example: 86 | 87 | ```javascript 88 | import {ReduxMiddleware} from '@nlabs/arkhamjs-middleware-redux'; 89 | import {createStore} from 'redux'; 90 | import {reducers} from './reducers'; 91 | 92 | const store = createStore(reducers); 93 | const middleware = [new ReduxMiddleware(store, 'myApp')]; 94 | 95 | Flux.init({middleware}); 96 | ``` 97 | 98 | - **store** - *(Store)* Redux root store. The store created by `createStore()`. 99 | - **name** - *(string)* (optional) Middleware name. Should be unique if integrating with multiple Redux stores. 100 | 101 | ### `arkhamMiddleware` 102 | 103 | Redux middleware to relay Redux action dispatches to ArkhamJS. 104 | 105 | ```javascript 106 | // Create Redux middleware 107 | const middleware = arkhamMiddleware(statePath); 108 | ``` 109 | 110 | A simple usage example: 111 | 112 | ```javascript 113 | import {arkhamMiddleware} from '@nlabs/arkhamjs-middleware-redux'; 114 | import {applyMiddleware, createStore} from 'redux'; 115 | import {reducers} from './reducers'; 116 | 117 | const store = createStore(reducers, applyMiddleware(arkhamMiddleware('myApp'))); 118 | ``` 119 | 120 | - **statePath** - *(string[] | string)* State tree path where to set this branch of the store's state tree. 121 | 122 | ## Additional Documentation 123 | 124 | Additional details may be found in the [ArkhamJS Documentation](https://docs.arkhamjs.io). 125 | 126 | ## Redux Todo example 127 | 128 | The following is a full example using the [Todo example](https://github.com/reactjs/redux/tree/master/examples/todos) within the Redux repository. The middleware will be added using the `Flux.addMiddleware()` automatically via the `createArkhamStore()` function. 129 | 130 | ```javascript 131 | import {Logger, LoggerDebugLevel} from '@nlabs/arkhamjs-middleware-logger'; 132 | import {createArkhamStore} from '@nlabs/arkhamjs-middleware-redux'; 133 | import {BrowserStorage} from '@nlabs/arkhamjs-storage-browser'; 134 | import {Flux} from '@nlabs/arkhamjs'; 135 | import * as React from 'react'; 136 | import {render} from 'react-dom'; 137 | import {Provider} from 'react-redux'; 138 | import App from './components/App'; 139 | import rootReducer from './reducers'; 140 | 141 | // Add a console logger for Arkham (optional). 142 | const logger = new Logger({ 143 | debugLevel: LoggerDebugLevel.DISPATCH 144 | }); 145 | 146 | // Initialize ArkhamJS. 147 | const Flux.init({ 148 | name: 'reduxDemo', // Optional name of application, defaults to 'arkhamjs' 149 | storage: new BrowserStorage(), // Optional persistent storage cache 150 | middleware: [logger] // Optional console logger 151 | }); 152 | 153 | // Create an ArkhamJS store for Redux 154 | const store = createArkhamStore({ 155 | flux: Flux, 156 | reducers: rootReducer, 157 | statePath: 'demo' 158 | }); 159 | 160 | render( 161 | 162 | 163 | , 164 | document.getElementById('root') 165 | ); 166 | ``` 167 | -------------------------------------------------------------------------------- /packages/arkhamjs-middleware-redux/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2018-Present, Nitrogen Labs, Inc. 3 | * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. 4 | */ 5 | 6 | module.exports = require('./lib'); 7 | -------------------------------------------------------------------------------- /packages/arkhamjs-middleware-redux/lex.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | outputPath: 'lib', 3 | preset: 'web', 4 | useTypescript: true 5 | }; 6 | -------------------------------------------------------------------------------- /packages/arkhamjs-middleware-redux/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@nlabs/arkhamjs-middleware-redux", 3 | "version": "3.28.4", 4 | "publishConfig": { 5 | "access": "public" 6 | }, 7 | "description": "Integrate ArkhamJS state management within Redux", 8 | "license": "MIT", 9 | "main": "./lib/index.js", 10 | "module": "./lib/index.js", 11 | "browser": "./lib/index.js", 12 | "types": "./lib/index.d.ts", 13 | "keywords": [ 14 | "arkhamjs", 15 | "flux", 16 | "immutable", 17 | "middleware", 18 | "nitrogenlabs", 19 | "redux" 20 | ], 21 | "author": { 22 | "name": "Giraldo Rosales", 23 | "email": "giraldo@nitrogenlabs.com", 24 | "url": "http://nitrogenlabs.com" 25 | }, 26 | "repository": { 27 | "type": "git", 28 | "url": "nitrogenlabs/arkhamjs.git" 29 | }, 30 | "homepage": "https://arkhamjs.io", 31 | "bugs": { 32 | "url": "https://github.com/nitrogenlabs/arkhamjs/issues" 33 | }, 34 | "scripts": { 35 | "build": "lex compile --remove", 36 | "clean": "lex clean", 37 | "lint": "eslint ./src --ext .ts,.tsx", 38 | "prepublishOnly": "npm run build", 39 | "publish:major": "npm version major && npm publish", 40 | "publish:minor": "npm version minor && npm publish", 41 | "publish:patch": "npm version patch && npm publish", 42 | "pretest": "npm run lint", 43 | "test": "lex test", 44 | "update": "npm-check-updates --interactive" 45 | }, 46 | "peerDependencies": { 47 | "@nlabs/arkhamjs": "^3.23", 48 | "redux": "^3.0" 49 | }, 50 | "dependencies": { 51 | "lodash": "^4.17.21" 52 | }, 53 | "devDependencies": { 54 | "@nlabs/arkhamjs": "*", 55 | "@types/jest": "^29.5.14", 56 | "@types/node": "^22.13.1", 57 | "@types/redux": "^3.6.31", 58 | "@types/webpack": "^5.28.5", 59 | "eslint": "^9.19.0", 60 | "eslint-config-styleguidejs": "^3.2.1", 61 | "redux": "^5.0.1", 62 | "typescript": "^5.7.3" 63 | }, 64 | "gitHead": "fc371e1e28fe0ae35d40d29a217d5f0e990ec32a" 65 | } 66 | -------------------------------------------------------------------------------- /packages/arkhamjs-middleware-redux/src/createArkhamStore.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2018-Present, Nitrogen Labs, Inc. 3 | * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. 4 | */ 5 | import {Flux} from '@nlabs/arkhamjs'; 6 | import isPlainObject from 'lodash/isPlainObject'; 7 | import merge from 'lodash/merge'; 8 | import {applyMiddleware, createStore, Store} from 'redux'; 9 | 10 | import {arkhamMiddleware} from './middleware/arkhamMiddleware'; 11 | import {ReduxMiddleware} from './middleware/ReduxMiddleware'; 12 | import {ArkhamReduxStoreType} from './types/main'; 13 | 14 | export const createArkhamStore = (configuration: ArkhamReduxStoreType): Store => { 15 | const { 16 | arkhamMiddleware: middleware = [], 17 | flux, 18 | reducers, 19 | sagas, 20 | statePath = '', 21 | reduxMiddleware = [], 22 | devTools 23 | } = configuration; 24 | 25 | // Save initial state tree 26 | const {storage} = Flux.getOptions(); 27 | let store: Store; 28 | 29 | if(storage) { 30 | const cachedState = Flux.getState(statePath); 31 | 32 | if(devTools) { 33 | store = createStore( 34 | devTools(reducers, cachedState), 35 | applyMiddleware(...reduxMiddleware, arkhamMiddleware(statePath, flux))); 36 | } else { 37 | store = createStore( 38 | reducers, 39 | cachedState, 40 | applyMiddleware(...reduxMiddleware, arkhamMiddleware(statePath, flux))); 41 | } 42 | 43 | if(cachedState === undefined) { 44 | const stateTree = store.getState(); 45 | const updatedState = isPlainObject(stateTree) ? merge(stateTree, cachedState) : stateTree; 46 | Flux.setState(statePath, updatedState); 47 | } 48 | } else { 49 | store = createStore( 50 | reducers, 51 | devTools, 52 | applyMiddleware(...reduxMiddleware, arkhamMiddleware(statePath, flux)) 53 | ); 54 | 55 | Flux.setState(statePath, store.getState()); 56 | } 57 | 58 | // If saga is being added, run. 59 | reduxMiddleware.every((item: any) => { 60 | if(sagas) { 61 | item.run(sagas); 62 | return false; 63 | } 64 | 65 | return true; 66 | }); 67 | 68 | // Add redux middleware to Arkham to relay dispatches to Redux 69 | middleware.push(new ReduxMiddleware(store, statePath)); 70 | 71 | // Initialize ArkhamJS 72 | Flux.addMiddleware(middleware); 73 | 74 | return store; 75 | }; 76 | -------------------------------------------------------------------------------- /packages/arkhamjs-middleware-redux/src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2018-Present, Nitrogen Labs, Inc. 3 | * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. 4 | */ 5 | import {createArkhamStore} from './createArkhamStore'; 6 | import {arkhamMiddleware} from './middleware/arkhamMiddleware'; 7 | import {ReduxMiddleware} from './middleware/ReduxMiddleware'; 8 | 9 | export * from './types/main'; 10 | export {ReduxMiddleware, arkhamMiddleware, createArkhamStore}; 11 | -------------------------------------------------------------------------------- /packages/arkhamjs-middleware-redux/src/middleware/ReduxMiddleware.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2018-Present, Nitrogen Labs, Inc. 3 | * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. 4 | */ 5 | import {FluxAction} from '@nlabs/arkhamjs'; 6 | import {Store} from 'redux'; 7 | 8 | export class ReduxMiddleware { 9 | name: string; 10 | reduxStore: Store; 11 | 12 | constructor(reduxStore: Store, name: string) { 13 | this.name = name ? `reduxMiddleware-${name}` : 'reduxMiddleware'; 14 | this.reduxStore = reduxStore; 15 | 16 | // Methods 17 | this.postDispatch = this.postDispatch.bind(this); 18 | } 19 | 20 | postDispatch(action): Promise { 21 | // ... Alter action if needed 22 | const {__ARKHAMJS_DISPATCH: isArkhamJs} = action; 23 | 24 | if(!isArkhamJs) { 25 | action.__ARKHAMJS_DISPATCH = true; 26 | this.reduxStore.dispatch(action); 27 | } 28 | 29 | return Promise.resolve(action); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/arkhamjs-middleware-redux/src/middleware/arkhamMiddleware.test.ts: -------------------------------------------------------------------------------- 1 | describe('arkhamMiddleware', () => { 2 | it('should emit altered data', () => { 3 | expect(true).toBe(true); 4 | }); 5 | }); 6 | -------------------------------------------------------------------------------- /packages/arkhamjs-middleware-redux/src/middleware/arkhamMiddleware.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2018-Present, Nitrogen Labs, Inc. 3 | * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. 4 | */ 5 | export const arkhamMiddleware = (statePath: string, Flux) => (store) => (next) => (action) => { 6 | const {__ARKHAMJS_DISPATCH: isArkhamJs} = action; 7 | delete action.__ARKHAMJS_DISPATCH; 8 | 9 | // Run the action through the redux reducers 10 | next(action); 11 | 12 | // Save the new, altered state within ArkhamJS 13 | Flux.setState(statePath, store.getState()); 14 | 15 | // Make sure we emit the event through ArkhamJS for any listeners. 16 | if(!isArkhamJs) { 17 | action.__ARKHAMJS_DISPATCH = true; 18 | Flux.dispatch(action); 19 | } 20 | 21 | return null; 22 | }; 23 | -------------------------------------------------------------------------------- /packages/arkhamjs-middleware-redux/src/types/main.ts: -------------------------------------------------------------------------------- 1 | import {AnyAction, Dispatch, Middleware} from 'redux'; 2 | 3 | export interface ArkhamReduxStoreType { 4 | arkhamMiddleware: any[]; 5 | devTools: any; 6 | flux: any; 7 | reducers: Dispatch; 8 | reduxMiddleware: Middleware[]; 9 | sagas: any; 10 | statePath: string; 11 | } 12 | -------------------------------------------------------------------------------- /packages/arkhamjs-middleware-redux/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "baseUrl": "./src", 5 | "declaration": true, 6 | "emitDeclarationOnly": true, 7 | "esModuleInterop": true, 8 | "inlineSourceMap": true, 9 | "jsx": "react-jsx", 10 | "lib": [ 11 | "esnext", 12 | "dom" 13 | ], 14 | "module": "commonjs", 15 | "moduleResolution": "node", 16 | "noImplicitReturns": true, 17 | "noImplicitThis": true, 18 | "noUnusedLocals": true, 19 | "outDir": "lib", 20 | "pretty": true, 21 | "skipLibCheck": true, 22 | "target": "ES2018" 23 | }, 24 | "include": [ 25 | "./src/**/*" 26 | ], 27 | "exclude": [ 28 | "**/*.test.*", 29 | "lib", 30 | "./node_modules", 31 | "./test" 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /packages/arkhamjs-storage-browser/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "styleguidejs/typescript" 3 | } -------------------------------------------------------------------------------- /packages/arkhamjs-storage-browser/.gitignore: -------------------------------------------------------------------------------- 1 | coverage 2 | lib 3 | node_modules 4 | *.log -------------------------------------------------------------------------------- /packages/arkhamjs-storage-browser/.npmignore: -------------------------------------------------------------------------------- 1 | converage 2 | docs 3 | node_modules 4 | src 5 | .eslintrc 6 | lex.config.js 7 | package-lock.json 8 | tsconfig.json 9 | -------------------------------------------------------------------------------- /packages/arkhamjs-storage-browser/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Nitrogen Labs, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/arkhamjs-storage-browser/README.md: -------------------------------------------------------------------------------- 1 | # @nlabs/arkhamjs-storage-browser 2 | 3 | [![npm version](https://img.shields.io/npm/v/@nlabs/arkhamjs-storage-browser.svg?style=flat-square)](https://www.npmjs.com/package/@nlabs/arkhamjs-storage-browser) 4 | [![npm downloads](https://img.shields.io/npm/dm/@nlabs/arkhamjs-storage-browser.svg?style=flat-square)](https://www.npmjs.com/package/@nlabs/arkhamjs-storage-browser) 5 | [![Travis](https://img.shields.io/travis/nitrogenlabs/arkhamjs.svg?style=flat-square)](https://travis-ci.org/nitrogenlabs/arkhamjs) 6 | [![Issues](https://img.shields.io/github/issues/nitrogenlabs/arkhamjs.svg?style=flat-square)](https://github.com/nitrogenlabs/arkhamjs/issues) 7 | [![TypeScript](https://badges.frapsoft.com/typescript/version/typescript-next.svg?v=101)](https://github.com/ellerbrock/typescript-badges/) 8 | [![MIT license](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](http://opensource.org/licenses/MIT) 9 | [![Chat](https://img.shields.io/discord/446122412715802649.svg)](https://discord.gg/Ttgev58) 10 | 11 | ## Installation 12 | 13 | Using [npm](https://www.npmjs.com/): 14 | 15 | ```shell 16 | npm i --save @nlabs/arkhamjs-storage-browser 17 | ``` 18 | 19 | ## Documentation 20 | 21 | For detailed [Documentation](https://arkhamjs.io) and additional options. 22 | -------------------------------------------------------------------------------- /packages/arkhamjs-storage-browser/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2018-Present, Nitrogen Labs, Inc. 3 | * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. 4 | */ 5 | 6 | module.exports = require('./lib'); 7 | -------------------------------------------------------------------------------- /packages/arkhamjs-storage-browser/lex.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | outputPath: 'lib', 3 | preset: 'web', 4 | useTypescript: true 5 | }; 6 | -------------------------------------------------------------------------------- /packages/arkhamjs-storage-browser/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@nlabs/arkhamjs-storage-browser", 3 | "version": "3.28.3", 4 | "publishConfig": { 5 | "access": "public" 6 | }, 7 | "description": "Browser storage for ArkhamJS", 8 | "license": "MIT", 9 | "main": "./lib/index.js", 10 | "module": "./lib/index.js", 11 | "browser": "./lib/index.js", 12 | "types": "./lib/index.d.ts", 13 | "keywords": [ 14 | "arkhamjs", 15 | "flux", 16 | "browser", 17 | "nitrogenlabs", 18 | "storage" 19 | ], 20 | "author": { 21 | "name": "Giraldo Rosales", 22 | "email": "giraldo@nitrogenlabs.com", 23 | "url": "http://nitrogenlabs.com" 24 | }, 25 | "repository": { 26 | "type": "git", 27 | "url": "nitrogenlabs/arkhamjs.git" 28 | }, 29 | "homepage": "https://arkhamjs.io", 30 | "bugs": { 31 | "url": "https://github.com/nitrogenlabs/arkhamjs/issues" 32 | }, 33 | "scripts": { 34 | "build": "lex compile --remove", 35 | "clean": "lex clean", 36 | "lint": "eslint ./src --ext .ts,.tsx", 37 | "prepublishOnly": "npm run build", 38 | "publish:major": "npm version major && npm publish", 39 | "publish:minor": "npm version minor && npm publish", 40 | "publish:patch": "npm version patch && npm publish", 41 | "pretest": "npm run lint", 42 | "reset": "lex clean", 43 | "test": "lex test", 44 | "update": "npm-check-updates --interactive" 45 | }, 46 | "peerDependencies": { 47 | "@nlabs/arkhamjs": "^3.26.0" 48 | }, 49 | "devDependencies": { 50 | "@nlabs/arkhamjs": "*", 51 | "@types/jest": "^29.5.14", 52 | "@types/node": "^22.13.1", 53 | "eslint": "^9.19.0", 54 | "eslint-config-styleguidejs": "^3.2.1", 55 | "typescript": "^5.7.3" 56 | }, 57 | "gitHead": "fc371e1e28fe0ae35d40d29a217d5f0e990ec32a" 58 | } 59 | -------------------------------------------------------------------------------- /packages/arkhamjs-storage-browser/src/BrowserStorage/BrowserStorage.test.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2018-Present, Nitrogen Labs, Inc. 3 | * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. 4 | */ 5 | import {BrowserStorage} from './BrowserStorage'; 6 | 7 | 8 | describe('BrowserStorage', () => { 9 | let localSpy; 10 | let sessionSpy; 11 | const val: string = 'hello_world'; 12 | const key: string = 'test'; 13 | const storage: BrowserStorage = new BrowserStorage({type: 'session'}); 14 | BrowserStorage.window = {localStorage: jest.fn(), sessionStorage: jest.fn()}; 15 | 16 | beforeAll(() => { 17 | // Mock storage 18 | const storageMock = () => { 19 | const storage: object = {}; 20 | 21 | return { 22 | getItem: (storageGetKey: string) => storage[storageGetKey] || null, 23 | removeItem: (storageRemoveKey: string) => { 24 | delete storage[storageRemoveKey]; 25 | }, 26 | setItem: (storageSetKey, storageSetValue) => { 27 | storage[storageSetKey] = storageSetValue || ''; 28 | } 29 | }; 30 | }; 31 | 32 | // Vars 33 | BrowserStorage.window.sessionStorage = storageMock(); 34 | BrowserStorage.window.localStorage = storageMock(); 35 | 36 | localSpy = jest.spyOn(BrowserStorage.window.localStorage, 'setItem'); 37 | sessionSpy = jest.spyOn(BrowserStorage.window.sessionStorage, 'setItem'); 38 | }); 39 | 40 | afterEach(() => { 41 | localSpy.mockClear(); 42 | sessionSpy.mockClear(); 43 | }); 44 | 45 | afterAll(() => { 46 | localSpy.mockRestore(); 47 | sessionSpy.mockRestore(); 48 | }); 49 | 50 | describe('.delLocalData', () => { 51 | it('should remove local data', () => { 52 | // Method 53 | BrowserStorage.delLocalData(key); 54 | const testVal: string = BrowserStorage.getLocalData(key); 55 | expect(testVal).toEqual(null); 56 | }); 57 | }); 58 | 59 | describe('.delSessionData', () => { 60 | it('should remove session data', () => { 61 | // Method 62 | BrowserStorage.delSessionData(key); 63 | const testVal: string = BrowserStorage.getSessionData(key); 64 | expect(testVal).toEqual(null); 65 | }); 66 | }); 67 | 68 | describe('#getLocalData', () => { 69 | it('should get local data', () => { 70 | // Set data 71 | BrowserStorage.setLocalData(key, val); 72 | 73 | // Method 74 | const testVal: string = BrowserStorage.getLocalData(key); 75 | expect(testVal).toEqual(val); 76 | }); 77 | }); 78 | 79 | describe('.getLocalStorage', () => { 80 | it('check if localStorage exists', () => { 81 | const localStorage = BrowserStorage.getLocalStorage(); 82 | expect(BrowserStorage.window.localStorage).toBe(localStorage); 83 | }); 84 | }); 85 | 86 | describe('.getSessionData', () => { 87 | it('should get session data', () => { 88 | // Method 89 | BrowserStorage.setSessionData(key, val); 90 | const testVal: string = BrowserStorage.getSessionData(key); 91 | expect(testVal).toEqual(val); 92 | }); 93 | }); 94 | 95 | describe('.getSessionStorage', () => { 96 | it('check if sessionStorage exists', () => { 97 | const sessionStorage = BrowserStorage.getSessionStorage(); 98 | expect(BrowserStorage.window.sessionStorage).toBe(sessionStorage); 99 | }); 100 | }); 101 | 102 | describe('#getStorageData', () => { 103 | it('should get storage data', async () => { 104 | // Method 105 | BrowserStorage.setSessionData(key, val); 106 | const testVal: string = await storage.getStorageData(key); 107 | expect(testVal).toEqual(val); 108 | }); 109 | }); 110 | 111 | describe('#setLocalData', () => { 112 | it('should set local data', () => { 113 | // Method 114 | BrowserStorage.setLocalData(key, val); 115 | expect(localSpy.mock.calls.length).toEqual(1); 116 | }); 117 | }); 118 | 119 | describe('#setSessionData', () => { 120 | it('should set session data', () => { 121 | // Method 122 | BrowserStorage.setSessionData(key, val); 123 | expect(sessionSpy.mock.calls.length).toEqual(1); 124 | }); 125 | }); 126 | 127 | describe('#setStorageData', () => { 128 | it('should set storage data', () => { 129 | // Method 130 | storage.setStorageData(key, val); 131 | expect(sessionSpy.mock.calls.length).toEqual(1); 132 | }); 133 | }); 134 | }); 135 | -------------------------------------------------------------------------------- /packages/arkhamjs-storage-browser/src/BrowserStorage/BrowserStorage.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2018-Present, Nitrogen Labs, Inc. 3 | * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. 4 | */ 5 | import {BrowserStorageOptions} from '../types/main'; 6 | 7 | export class BrowserStorage { 8 | static window: any = window || {}; 9 | private options: BrowserStorageOptions = { 10 | type: 'session' 11 | }; 12 | 13 | constructor(options: BrowserStorageOptions = {}) { 14 | // Methods 15 | this.getStorageData = this.getStorageData.bind(this); 16 | this.setStorageData = this.setStorageData.bind(this); 17 | 18 | // Configuration 19 | this.options = {...this.options, ...options}; 20 | } 21 | 22 | /** 23 | * Removes a key from localStorage. 24 | * 25 | * @param {string} key Key associated with the data to remove. 26 | * @returns {boolean} Whether data was successfully removed. 27 | */ 28 | static delLocalData(key: string): boolean { 29 | const localStorage = BrowserStorage.getLocalStorage(); 30 | 31 | if(localStorage) { 32 | try { 33 | localStorage.removeItem(key); 34 | return true; 35 | } catch(error) { 36 | return false; 37 | } 38 | } else { 39 | return false; 40 | } 41 | } 42 | 43 | /** 44 | * Removes a key from sessionStorage. 45 | * 46 | * @param {string} key Key associated with the data to remove. 47 | * @returns {boolean} Whether data was successfully removed. 48 | */ 49 | static delSessionData(key: string): boolean { 50 | const sessionStorage = BrowserStorage.getSessionStorage(); 51 | 52 | if(sessionStorage) { 53 | try { 54 | sessionStorage.removeItem(key); 55 | return true; 56 | } catch(error) { 57 | return false; 58 | } 59 | } else { 60 | return false; 61 | } 62 | } 63 | 64 | /** 65 | * Get a key value from localStorage. 66 | * 67 | * @param {string} key The key for data. 68 | * @returns {any} the data object associated with the key. 69 | */ 70 | static getLocalData(key: string): any { 71 | const localStorage = BrowserStorage.getLocalStorage(); 72 | 73 | if(localStorage) { 74 | try { 75 | const item = localStorage.getItem(key); 76 | 77 | if(item) { 78 | return JSON.parse(item); 79 | } 80 | 81 | return null; 82 | } catch(error) { 83 | return null; 84 | } 85 | } else { 86 | return null; 87 | } 88 | } 89 | 90 | /** 91 | * Get localStorage from global window object. 92 | * 93 | * @param {string} key Key to store data. 94 | * @param {any} value Data to store. 95 | * @returns {any} window.localStorage. 96 | */ 97 | static getLocalStorage(): any { 98 | const {localStorage} = BrowserStorage.window; 99 | return localStorage; 100 | } 101 | 102 | /** 103 | * Get a key value from sessionStorage. 104 | * 105 | * @param {string} key The key for data. 106 | * @returns {any} the data object associated with the key. 107 | */ 108 | static getSessionData(key: string): any { 109 | const sessionStorage = BrowserStorage.getSessionStorage(); 110 | 111 | if(sessionStorage) { 112 | try { 113 | const item = sessionStorage.getItem(key); 114 | 115 | if(item) { 116 | return item ? JSON.parse(item) : null; 117 | } 118 | 119 | return null; 120 | } catch(error) { 121 | return null; 122 | } 123 | } else { 124 | return null; 125 | } 126 | } 127 | 128 | /** 129 | * Get sessionStorage from global window object. 130 | * 131 | * @param {string} key Key to store data. 132 | * @param {any} value Data to store. 133 | * @returns {any} window.sessionStorage. 134 | */ 135 | static getSessionStorage(): any { 136 | const {sessionStorage} = BrowserStorage.window; 137 | 138 | return sessionStorage; 139 | } 140 | 141 | /** 142 | * Saves data to localStorage. 143 | * 144 | * @param {string} key Key to store data. 145 | * @param {any} value Data to store. 146 | * @returns {boolean} Whether data was successfully saved. 147 | */ 148 | static setLocalData(key: string, value): boolean { 149 | const localStorage = BrowserStorage.getLocalStorage(); 150 | 151 | if(localStorage) { 152 | try { 153 | localStorage.setItem(key, JSON.stringify(value)); 154 | return true; 155 | } catch(error) { 156 | return false; 157 | } 158 | } else { 159 | return false; 160 | } 161 | } 162 | 163 | /** 164 | * Saves data to sessionStorage. 165 | * 166 | * @param {string} key Key to store data. 167 | * @param {any} value Data to store. 168 | * @returns {boolean} Whether data was successfully saved. 169 | */ 170 | static setSessionData(key: string, value): boolean { 171 | const sessionStorage = BrowserStorage.getSessionStorage(); 172 | 173 | if(sessionStorage) { 174 | try { 175 | sessionStorage.setItem(key, JSON.stringify(value)); 176 | return true; 177 | } catch(error) { 178 | return false; 179 | } 180 | } else { 181 | return false; 182 | } 183 | } 184 | 185 | /** 186 | * Get a key value from storage. 187 | * 188 | * @param {string} key The key for data. 189 | * @returns {Promise} the data object associated with the key. 190 | */ 191 | getStorageData(key: string): Promise { 192 | const {type} = this.options; 193 | const results = type === 'local' ? BrowserStorage.getLocalData(key) : BrowserStorage.getSessionData(key); 194 | return Promise.resolve(results); 195 | } 196 | 197 | /** 198 | * Saves data to storage. 199 | * 200 | * @param {string} key Key to store data. 201 | * @param {any} value Data to store. 202 | * @returns {Promise} Whether data was successfully saved. 203 | */ 204 | setStorageData(key: string, value): Promise { 205 | const {type} = this.options; 206 | const results: boolean = type === 'local' ? 207 | BrowserStorage.setLocalData(key, value) : 208 | BrowserStorage.setSessionData(key, value); 209 | return Promise.resolve(results); 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /packages/arkhamjs-storage-browser/src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2018-Present, Nitrogen Labs, Inc. 3 | * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. 4 | */ 5 | import {BrowserStorage} from './BrowserStorage/BrowserStorage'; 6 | 7 | // Storage 8 | export * from './types/main'; 9 | export {BrowserStorage}; 10 | -------------------------------------------------------------------------------- /packages/arkhamjs-storage-browser/src/types/main.ts: -------------------------------------------------------------------------------- 1 | export interface BrowserStorageOptions { 2 | readonly type?: 'local' | 'session'; 3 | } 4 | -------------------------------------------------------------------------------- /packages/arkhamjs-storage-browser/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "baseUrl": "./src", 5 | "declaration": true, 6 | "emitDeclarationOnly": true, 7 | "esModuleInterop": true, 8 | "inlineSourceMap": true, 9 | "jsx": "react-jsx", 10 | "lib": [ 11 | "esnext", 12 | "dom" 13 | ], 14 | "module": "commonjs", 15 | "moduleResolution": "node", 16 | "noImplicitReturns": true, 17 | "noImplicitThis": true, 18 | "noUnusedLocals": true, 19 | "outDir": "lib", 20 | "pretty": true, 21 | "skipLibCheck": true, 22 | "target": "ES2018" 23 | }, 24 | "include": [ 25 | "./src/**/*" 26 | ], 27 | "exclude": [ 28 | "**/*.test.*", 29 | "lib", 30 | "./node_modules", 31 | "./test" 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /packages/arkhamjs-storage-native/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "styleguidejs/typescript", 3 | "rules": { 4 | "import/no-deprecated": 0, 5 | "import/no-named-as-default": 0, 6 | "import/no-named-as-default-member": 0 7 | } 8 | } -------------------------------------------------------------------------------- /packages/arkhamjs-storage-native/.gitignore: -------------------------------------------------------------------------------- 1 | coverage 2 | lib 3 | node_modules 4 | *.log -------------------------------------------------------------------------------- /packages/arkhamjs-storage-native/.npmignore: -------------------------------------------------------------------------------- 1 | coverage 2 | docs 3 | node_modules 4 | src 5 | .eslintrc 6 | lex.config.js 7 | package-lock.json 8 | tsconfig.json 9 | -------------------------------------------------------------------------------- /packages/arkhamjs-storage-native/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Nitrogen Labs, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/arkhamjs-storage-native/README.md: -------------------------------------------------------------------------------- 1 | # @nlabs/arkhamjs-storage-native 2 | 3 | [![npm version](https://img.shields.io/npm/v/@nlabs/arkhamjs-storage-native.svg?style=flat-square)](https://www.npmjs.com/package/@nlabs/arkhamjs-storage-native) 4 | [![npm downloads](https://img.shields.io/npm/dm/@nlabs/arkhamjs-storage-native.svg?style=flat-square)](https://www.npmjs.com/package/@nlabs/arkhamjs-storage-native) 5 | [![Travis](https://img.shields.io/travis/nitrogenlabs/arkhamjs.svg?style=flat-square)](https://travis-ci.org/nitrogenlabs/arkhamjs) 6 | [![Issues](https://img.shields.io/github/issues/nitrogenlabs/arkhamjs.svg?style=flat-square)](https://github.com/nitrogenlabs/arkhamjs/issues) 7 | [![TypeScript](https://badges.frapsoft.com/typescript/version/typescript-next.svg?v=101)](https://github.com/ellerbrock/typescript-badges/) 8 | [![MIT license](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](http://opensource.org/licenses/MIT) 9 | [![Chat](https://img.shields.io/discord/446122412715802649.svg)](https://discord.gg/Ttgev58) 10 | 11 | ## Installation 12 | 13 | Using [npm](https://www.npmjs.com/): 14 | 15 | ```shell 16 | npm install --save @nlabs/arkhamjs-storage-native 17 | ``` 18 | 19 | ## Documentation 20 | 21 | For detailed [Documentation](https://arkhamjs.io) and additional options. 22 | -------------------------------------------------------------------------------- /packages/arkhamjs-storage-native/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2018-Present, Nitrogen Labs, Inc. 3 | * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. 4 | */ 5 | 6 | module.exports = require('./lib'); 7 | -------------------------------------------------------------------------------- /packages/arkhamjs-storage-native/lex.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | outputPath: 'lib', 3 | preset: 'react-native', 4 | useTypescript: true 5 | }; 6 | -------------------------------------------------------------------------------- /packages/arkhamjs-storage-native/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@nlabs/arkhamjs-storage-native", 3 | "version": "3.29.0", 4 | "publishConfig": { 5 | "access": "public" 6 | }, 7 | "description": "React Native storage for ArkhamJS", 8 | "license": "MIT", 9 | "main": "./lib/index.js", 10 | "module": "./lib/index.js", 11 | "browser": "./lib/index.js", 12 | "types": "./lib/index.d.ts", 13 | "keywords": [ 14 | "arkhamjs", 15 | "flux", 16 | "native", 17 | "nitrogenlabs", 18 | "storage" 19 | ], 20 | "author": { 21 | "name": "Giraldo Rosales", 22 | "email": "giraldo@nitrogenlabs.com", 23 | "url": "http://nitrogenlabs.com" 24 | }, 25 | "repository": { 26 | "type": "git", 27 | "url": "nitrogenlabs/arkhamjs.git" 28 | }, 29 | "homepage": "https://arkhamjs.io", 30 | "bugs": { 31 | "url": "https://github.com/nitrogenlabs/arkhamjs/issues" 32 | }, 33 | "scripts": { 34 | "build": "lex compile --remove", 35 | "clean": "lex clean", 36 | "deploy": "lex build --remove && s3-deploy './lib/static/**' --cwd './lib/static/' --region us-east-1 --bucket arkhamjs.io/versions --gzip", 37 | "lint": "eslint ./src --ext .ts,.tsx", 38 | "prepublishOnly": "npm run build", 39 | "publish:major": "npm version major && npm publish", 40 | "publish:minor": "npm version minor && npm publish", 41 | "publish:patch": "npm version patch && npm publish", 42 | "pretest": "npm run lint", 43 | "test": "lex test", 44 | "update": "npm-check-updates --interactive" 45 | }, 46 | "peerDependencies": { 47 | "@nlabs/arkhamjs": "^3.26.0", 48 | "react": "^18.0.0 || ^19.0.0" 49 | }, 50 | "devDependencies": { 51 | "@nlabs/arkhamjs": "*", 52 | "@types/jest": "^29.5.14", 53 | "@types/node": "^22.13.1", 54 | "eslint": "^9.19.0", 55 | "eslint-config-styleguidejs": "^3.2.1", 56 | "lodash": "^4.17.21", 57 | "typescript": "^5.7.3" 58 | }, 59 | "gitHead": "fc371e1e28fe0ae35d40d29a217d5f0e990ec32a", 60 | "dependencies": { 61 | "@react-native-async-storage/async-storage": "^2.1.1" 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /packages/arkhamjs-storage-native/src/NativeStorage/NativeStorage.test.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2018-Present, Nitrogen Labs, Inc. 3 | * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. 4 | */ 5 | import {NativeStorage} from './NativeStorage'; 6 | 7 | describe('NativeStorage', () => { 8 | it('should set async data', async () => { 9 | const hasExistingData = await NativeStorage.getAsyncData('test'); 10 | expect(hasExistingData).toBe(undefined); 11 | 12 | const result = await NativeStorage.setAsyncData('test', 'hello world'); 13 | expect(result).toBe(true); 14 | 15 | const hasNewData = await NativeStorage.getAsyncData('test'); 16 | expect(hasNewData).toBe('hello world'); 17 | }); 18 | 19 | it('should delete async data', async () => { 20 | const hasExistingData = await NativeStorage.getAsyncData('test'); 21 | expect(hasExistingData).toBe(undefined); 22 | 23 | const result = await NativeStorage.delAsyncData('test'); 24 | expect(result).toBe(true); 25 | 26 | const hasNewData = await NativeStorage.getAsyncData('test'); 27 | expect(hasNewData).toBe(undefined); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /packages/arkhamjs-storage-native/src/NativeStorage/NativeStorage.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2018-Present, Nitrogen Labs, Inc. 3 | * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. 4 | */ 5 | import AsyncStorage from '@react-native-async-storage/async-storage'; 6 | 7 | import {NativeStorageOptions} from './NativeStorage.types'; 8 | 9 | export class NativeStorage { 10 | private options: NativeStorageOptions = {}; 11 | 12 | constructor(options: NativeStorageOptions = {}) { 13 | // Methods 14 | this.getStorageData = this.getStorageData.bind(this); 15 | this.setStorageData = this.setStorageData.bind(this); 16 | 17 | // Configuration 18 | this.options = {...this.options, ...options}; 19 | } 20 | 21 | /** 22 | * Removes a key from AsyncStorage. 23 | * 24 | * @param {string} key Key associated with the data to remove. 25 | * @returns {Promise} Whether data was successfully removed. 26 | */ 27 | static delAsyncData(key: string): Promise { 28 | try { 29 | return AsyncStorage.removeItem(key) 30 | .then(() => Promise.resolve(true)) 31 | .catch(() => Promise.resolve(false)); 32 | } catch(error) { 33 | return Promise.resolve(false); 34 | } 35 | } 36 | 37 | /** 38 | * Get a key value from AsyncStorage. 39 | * 40 | * @param {string} key The key for data. 41 | * @returns {Promise} the data object associated with the key. 42 | */ 43 | static getAsyncData(key: string): Promise { 44 | try { 45 | return AsyncStorage.getItem(key) 46 | .then((value: string) => { 47 | const updatedValue = value ? JSON.parse(value) : null; 48 | return updatedValue; 49 | }) 50 | .catch(() => Promise.resolve(null)); 51 | } catch(error) { 52 | return Promise.resolve(null); 53 | } 54 | } 55 | 56 | /** 57 | * Saves data to AsyncStorage. 58 | * 59 | * @param {string} key Key to store data. 60 | * @param {any} value Data to store. 61 | * @returns {Promise} Whether data was successfully saved. 62 | */ 63 | static setAsyncData(key: string, value): Promise { 64 | const updatedValue = value !== undefined && JSON.stringify(value); 65 | 66 | try { 67 | return AsyncStorage.setItem(key, updatedValue) 68 | .then(() => true) 69 | .catch(() => Promise.resolve(false)); 70 | } catch(error) { 71 | return Promise.resolve(false); 72 | } 73 | } 74 | 75 | /** 76 | * Get a key value from storage. 77 | * 78 | * @param {string} key The key for data. 79 | * @returns {Promise} the data object associated with the key. 80 | */ 81 | getStorageData(key: string): Promise { 82 | return NativeStorage.getAsyncData(key); 83 | } 84 | 85 | /** 86 | * Saves data to storage. 87 | * 88 | * @param {string} key Key to store data. 89 | * @param {any} value Data to store. 90 | * @returns {Promise} Whether data was successfully saved. 91 | */ 92 | setStorageData(key: string, value): Promise { 93 | return NativeStorage.setAsyncData(key, value); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /packages/arkhamjs-storage-native/src/NativeStorage/NativeStorage.types.ts: -------------------------------------------------------------------------------- 1 | export interface NativeStorageOptions {} 2 | -------------------------------------------------------------------------------- /packages/arkhamjs-storage-native/src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2018-Present, Nitrogen Labs, Inc. 3 | * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. 4 | */ 5 | import {NativeStorage} from './NativeStorage/NativeStorage'; 6 | 7 | 8 | // Storage 9 | export * from './NativeStorage/NativeStorage.types'; 10 | 11 | export {NativeStorage}; 12 | -------------------------------------------------------------------------------- /packages/arkhamjs-storage-native/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "allowSyntheticDefaultImports": true, 5 | "baseUrl": "./src", 6 | "declaration": true, 7 | "emitDeclarationOnly": true, 8 | "esModuleInterop": true, 9 | "inlineSourceMap": true, 10 | "jsx": "react-jsx", 11 | "lib": [ 12 | "esnext", 13 | "dom" 14 | ], 15 | "module": "commonjs", 16 | "moduleResolution": "node", 17 | "noImplicitReturns": true, 18 | "noImplicitThis": true, 19 | "noUnusedLocals": true, 20 | "outDir": "lib", 21 | "pretty": true, 22 | "skipLibCheck": true, 23 | "target": "ES2018" 24 | }, 25 | "include": [ 26 | "./src/**/*" 27 | ], 28 | "exclude": [ 29 | "**/*.test.*", 30 | "lib", 31 | "./node_modules", 32 | "./test" 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /packages/arkhamjs-storage-node/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "styleguidejs/typescript" 3 | } -------------------------------------------------------------------------------- /packages/arkhamjs-storage-node/.gitignore: -------------------------------------------------------------------------------- 1 | coverage 2 | lib 3 | node_modules 4 | *.log -------------------------------------------------------------------------------- /packages/arkhamjs-storage-node/.npmignore: -------------------------------------------------------------------------------- 1 | .node-persist 2 | coverage 3 | docs 4 | node_modules 5 | src 6 | .eslintrc 7 | .travis.yml 8 | jest.setup.js 9 | lex.config.js 10 | package-lock.json 11 | tsconfig.json 12 | -------------------------------------------------------------------------------- /packages/arkhamjs-storage-node/.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "20" 4 | install: 5 | - npm i -g @nlabs/lex 6 | - lex versions 7 | script: 8 | - npm run build 9 | - npm run test 10 | -------------------------------------------------------------------------------- /packages/arkhamjs-storage-node/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Nitrogen Labs, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/arkhamjs-storage-node/README.md: -------------------------------------------------------------------------------- 1 | # @nlabs/arkhamjs-storage-node 2 | 3 | [![npm version](https://img.shields.io/npm/v/@nlabs/arkhamjs-storage-node.svg?style=flat-square)](https://www.npmjs.com/package/@nlabs/arkhamjs-storage-node) 4 | [![npm downloads](https://img.shields.io/npm/dm/@nlabs/arkhamjs-storage-node.svg?style=flat-square)](https://www.npmjs.com/package/@nlabs/arkhamjs-storage-node) 5 | [![Travis](https://img.shields.io/travis/nitrogenlabs/arkhamjs.svg?style=flat-square)](https://travis-ci.org/nitrogenlabs/arkhamjs) 6 | [![Issues](https://img.shields.io/github/issues/nitrogenlabs/arkhamjs.svg?style=flat-square)](https://github.com/nitrogenlabs/arkhamjs/issues) 7 | [![TypeScript](https://badges.frapsoft.com/typescript/version/typescript-next.svg?v=101)](https://github.com/ellerbrock/typescript-badges/) 8 | [![MIT license](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](http://opensource.org/licenses/MIT) 9 | [![Chat](https://img.shields.io/discord/446122412715802649.svg)](https://discord.gg/Ttgev58) 10 | 11 | ## Installation 12 | 13 | Using [npm](https://www.npmjs.com/): 14 | 15 | ```shell 16 | npm i --save @nlabs/arkhamjs-storage-node 17 | ``` 18 | 19 | ## Documentation 20 | 21 | For detailed [Documentation](https://arkhamjs.io) and additional options. 22 | -------------------------------------------------------------------------------- /packages/arkhamjs-storage-node/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2018-Present, Nitrogen Labs, Inc. 3 | * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. 4 | */ 5 | 6 | module.exports = require('./lib'); 7 | -------------------------------------------------------------------------------- /packages/arkhamjs-storage-node/jest.setup.js: -------------------------------------------------------------------------------- 1 | jest.mock('node-persist', () => ({ 2 | init: (options) => jest.fn(), 3 | getItem: (key) => Promise.resolve(jest.fn()), 4 | removeItem: (key) => Promise.resolve(jest.fn()), 5 | setItem: (key, val) => Promise.resolve(jest.fn()) 6 | })); 7 | -------------------------------------------------------------------------------- /packages/arkhamjs-storage-node/lex.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | outputPath: 'lib', 3 | preset: 'node', 4 | useTypescript: true 5 | }; 6 | -------------------------------------------------------------------------------- /packages/arkhamjs-storage-node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@nlabs/arkhamjs-storage-node", 3 | "version": "3.28.3", 4 | "publishConfig": { 5 | "access": "public" 6 | }, 7 | "description": "NodeJS storage for ArkhamJS", 8 | "license": "MIT", 9 | "main": "./lib/index.js", 10 | "module": "./lib/index.js", 11 | "browser": "./lib/index.js", 12 | "types": "./lib/index.d.ts", 13 | "keywords": [ 14 | "arkhamjs", 15 | "flux", 16 | "node", 17 | "nitrogenlabs", 18 | "storage" 19 | ], 20 | "author": { 21 | "name": "Giraldo Rosales", 22 | "email": "giraldo@nitrogenlabs.com", 23 | "url": "http://nitrogenlabs.com" 24 | }, 25 | "repository": { 26 | "type": "git", 27 | "url": "nitrogenlabs/arkhamjs.git" 28 | }, 29 | "homepage": "https://arkhamjs.io", 30 | "bugs": { 31 | "url": "https://github.com/nitrogenlabs/arkhamjs/issues" 32 | }, 33 | "scripts": { 34 | "build": "lex compile --remove", 35 | "clean": "lex clean", 36 | "lint": "eslint ./src --ext .ts,.tsx", 37 | "prepublishOnly": "npm run build", 38 | "publish:major": "npm version major && npm publish", 39 | "publish:minor": "npm version minor && npm publish", 40 | "publish:patch": "npm version patch && npm publish", 41 | "pretest": "npm run lint", 42 | "test": "lex test --setup ./jest.setup.js", 43 | "update": "npm-check-updates --interactive" 44 | }, 45 | "dependencies": { 46 | "node-persist": "^4.0.4" 47 | }, 48 | "peerDependencies": { 49 | "@nlabs/arkhamjs": "^3.26.0" 50 | }, 51 | "devDependencies": { 52 | "@nlabs/arkhamjs": "*", 53 | "@types/jest": "^29.5.14", 54 | "@types/node": "^22.13.1", 55 | "@types/node-persist": "^3.1.8", 56 | "eslint": "^9.19.0", 57 | "eslint-config-styleguidejs": "^3.2.1", 58 | "typescript": "^5.7.3" 59 | }, 60 | "gitHead": "fc371e1e28fe0ae35d40d29a217d5f0e990ec32a" 61 | } 62 | -------------------------------------------------------------------------------- /packages/arkhamjs-storage-node/src/NodeStorage/NodeStorage.test.ts: -------------------------------------------------------------------------------- 1 | import PersistStorage from 'node-persist'; 2 | 3 | import {NodeStorage} from './NodeStorage'; 4 | 5 | /** 6 | * Copyright (c) 2018-Present, Nitrogen Labs, Inc. 7 | * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. 8 | */ 9 | 10 | describe('NodeStorage', () => { 11 | beforeAll(() => { 12 | new NodeStorage(); 13 | }); 14 | 15 | describe('.delPersistData', () => { 16 | let storageSpy; 17 | 18 | beforeAll(() => { 19 | // Spy 20 | storageSpy = jest.spyOn(PersistStorage, 'removeItem'); 21 | }); 22 | 23 | afterAll(() => { 24 | storageSpy.mockRestore(); 25 | }); 26 | 27 | it('should delete async data', async () => { 28 | await NodeStorage.delPersistData('test'); 29 | expect(storageSpy.mock.calls.length).toBe(1); 30 | }); 31 | }); 32 | 33 | describe('.getPersistData', () => { 34 | let storageSpy; 35 | 36 | beforeAll(() => { 37 | // Spy 38 | storageSpy = jest.spyOn(PersistStorage, 'getItem'); 39 | }); 40 | 41 | afterAll(() => { 42 | storageSpy.mockRestore(); 43 | }); 44 | 45 | it('should delete async data', async () => { 46 | await NodeStorage.getPersistData('test'); 47 | expect(storageSpy.mock.calls.length).toBe(1); 48 | }); 49 | }); 50 | 51 | describe('.setPersistData', () => { 52 | let storageSpy; 53 | 54 | beforeAll(() => { 55 | // Spy 56 | storageSpy = jest.spyOn(PersistStorage, 'setItem'); 57 | }); 58 | 59 | afterAll(() => { 60 | storageSpy.mockRestore(); 61 | }); 62 | 63 | it('should delete async data', async () => { 64 | await NodeStorage.setPersistData('test', 'hello world'); 65 | expect(storageSpy.mock.calls.length).toBe(1); 66 | }); 67 | }); 68 | }); 69 | -------------------------------------------------------------------------------- /packages/arkhamjs-storage-node/src/NodeStorage/NodeStorage.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2018-Present, Nitrogen Labs, Inc. 3 | * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. 4 | */ 5 | import PersistStorage, {InitOptions} from 'node-persist'; 6 | 7 | export class NodeStorage { 8 | private options: InitOptions = { 9 | dir: '/tmp', 10 | encoding: 'utf8', 11 | expiredInterval: 3 * 60 * 1000, 12 | forgiveParseErrors: false, 13 | logging: false, 14 | parse: JSON.parse, 15 | stringify: JSON.stringify 16 | }; 17 | 18 | constructor(options: InitOptions = {}) { 19 | // Methods 20 | this.clearStorageData = this.clearStorageData.bind(this); 21 | this.getStorageData = this.getStorageData.bind(this); 22 | this.setStorageData = this.setStorageData.bind(this); 23 | 24 | // Configuration 25 | this.options = {...this.options, ...options}; 26 | PersistStorage.init(this.options); 27 | } 28 | 29 | /** 30 | * Removes all keys from persistent data. 31 | * 32 | * @returns {Promise} Whether data was successfully removed. 33 | */ 34 | static clearPersistData(): Promise { 35 | try { 36 | return new Promise((resolve) => { 37 | PersistStorage.clear() 38 | .then(() => resolve(true)) 39 | .catch(() => resolve(false)); 40 | }); 41 | } catch(error) { 42 | return Promise.resolve(false); 43 | } 44 | } 45 | 46 | /** 47 | * Removes a key from persistent data. 48 | * 49 | * @param {string} key Key associated with the data to remove. 50 | * @returns {Promise} Whether data was successfully removed. 51 | */ 52 | static delPersistData(key: string): Promise { 53 | try { 54 | return new Promise((resolve) => { 55 | PersistStorage.removeItem(key) 56 | .then(() => resolve(true)) 57 | .catch(() => resolve(false)); 58 | }); 59 | } catch(error) { 60 | return Promise.resolve(false); 61 | } 62 | } 63 | 64 | /** 65 | * Get a key value from persistent data. 66 | * 67 | * @param {string} key The key for data. 68 | * @returns {Promise} the data object associated with the key. 69 | */ 70 | static getPersistData(key: string): Promise { 71 | try { 72 | return new Promise((resolve) => { 73 | PersistStorage.getItem(key) 74 | .then((value: string) => { 75 | const updatedValue = value ? JSON.parse(value) : null; 76 | resolve(updatedValue); 77 | }) 78 | .catch(() => resolve(null)); 79 | }); 80 | } catch(error) { 81 | return Promise.resolve(null); 82 | } 83 | } 84 | 85 | /** 86 | * Saves data to persistent data. 87 | * 88 | * @param {string} key Key to store data. 89 | * @param {any} value Data to store. 90 | * @returns {Promise} Whether data was successfully saved. 91 | */ 92 | static setPersistData(key: string, value): Promise { 93 | try { 94 | return new Promise((resolve) => { 95 | PersistStorage.setItem(key, value) 96 | .then(() => resolve(true)) 97 | .catch(() => resolve(false)); 98 | }); 99 | } catch(error) { 100 | return Promise.resolve(false); 101 | } 102 | } 103 | 104 | /** 105 | * Clears all data from storage. 106 | * 107 | * @returns {Promise} Whether data was successfully saved. 108 | */ 109 | clearStorageData(): Promise { 110 | return NodeStorage.clearPersistData(); 111 | } 112 | 113 | /** 114 | * Get a key value from storage. 115 | * 116 | * @param {string} key The key for data. 117 | * @returns {Promise} the data object associated with the key. 118 | */ 119 | getStorageData(key: string): Promise { 120 | return NodeStorage.getPersistData(key); 121 | } 122 | 123 | /** 124 | * Saves data to storage. 125 | * 126 | * @param {string} key Key to store data. 127 | * @param {any} value Data to store. 128 | * @returns {Promise} Whether data was successfully saved. 129 | */ 130 | setStorageData(key: string, value): Promise { 131 | return NodeStorage.setPersistData(key, value); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /packages/arkhamjs-storage-node/src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2018-Present, Nitrogen Labs, Inc. 3 | * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. 4 | */ 5 | import {NodeStorage} from './NodeStorage/NodeStorage'; 6 | 7 | // Storage 8 | export {NodeStorage}; 9 | -------------------------------------------------------------------------------- /packages/arkhamjs-storage-node/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "baseUrl": "./src", 5 | "declaration": true, 6 | "emitDeclarationOnly": true, 7 | "esModuleInterop": true, 8 | "inlineSourceMap": true, 9 | "jsx": "react-jsx", 10 | "lib": [ 11 | "esnext", 12 | "dom" 13 | ], 14 | "module": "commonjs", 15 | "moduleResolution": "node", 16 | "noImplicitReturns": true, 17 | "noImplicitThis": true, 18 | "noUnusedLocals": true, 19 | "outDir": "lib", 20 | "pretty": true, 21 | "skipLibCheck": true, 22 | "target": "ES2018", 23 | "typeRoots": [ 24 | "node_modules/@types", 25 | "node_modules/json-d-ts" 26 | ] 27 | }, 28 | "include": [ 29 | "./src/**/*" 30 | ], 31 | "exclude": [ 32 | "**/*.test.*", 33 | "lib", 34 | "./node_modules", 35 | "./test" 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /packages/arkhamjs-utils-react/.eslintignore: -------------------------------------------------------------------------------- 1 | ResizeObserver.d.ts -------------------------------------------------------------------------------- /packages/arkhamjs-utils-react/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "styleguidejs/typescript" 3 | } -------------------------------------------------------------------------------- /packages/arkhamjs-utils-react/.gitignore: -------------------------------------------------------------------------------- 1 | coverage 2 | lib 3 | node_modules 4 | *.log -------------------------------------------------------------------------------- /packages/arkhamjs-utils-react/.npmignore: -------------------------------------------------------------------------------- 1 | coverage 2 | docs 3 | node_modules 4 | src 5 | .eslintrc 6 | lex.config.js 7 | package-lock.json 8 | tsconfig.json 9 | -------------------------------------------------------------------------------- /packages/arkhamjs-utils-react/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Nitrogen Labs, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/arkhamjs-utils-react/README: -------------------------------------------------------------------------------- 1 | # arkhamjs-utils-react 2 | -------------------------------------------------------------------------------- /packages/arkhamjs-utils-react/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2018-Present, Nitrogen Labs, Inc. 3 | * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. 4 | */ 5 | 6 | module.exports = require('./lib'); 7 | -------------------------------------------------------------------------------- /packages/arkhamjs-utils-react/lex.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | outputPath: 'lib', 3 | preset: 'web', 4 | useTypescript: true 5 | }; 6 | -------------------------------------------------------------------------------- /packages/arkhamjs-utils-react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@nlabs/arkhamjs-utils-react", 3 | "version": "3.29.0", 4 | "publishConfig": { 5 | "access": "public" 6 | }, 7 | "description": "ArkhamJS React Utilities", 8 | "license": "MIT", 9 | "main": "./lib/index.js", 10 | "browser": "./lib/index.js", 11 | "types": "./lib/index.d.js", 12 | "keywords": [ 13 | "arkhamjs", 14 | "flux", 15 | "global state", 16 | "nitrogenlabs", 17 | "state", 18 | "state management", 19 | "useFlux", 20 | "useComponentSize", 21 | "useRefSize", 22 | "useWindowSize" 23 | ], 24 | "contributors": [ 25 | { 26 | "name": "Giraldo Rosales", 27 | "email": "giraldo@nitrogenlabs.com", 28 | "url": "http://nitrogenlabs.com" 29 | }, 30 | { 31 | "name": "Sartaj Chowdhury", 32 | "email": "sartaj@sartaj.me", 33 | "url": "http://github.com/Sartaj" 34 | } 35 | ], 36 | "repository": { 37 | "type": "git", 38 | "url": "nitrogenlabs/arkhamjs.git" 39 | }, 40 | "homepage": "https://arkhamjs.io", 41 | "bugs": { 42 | "url": "https://github.com/nitrogenlabs/arkhamjs/issues" 43 | }, 44 | "scripts": { 45 | "build": "lex compile --remove", 46 | "clean": "lex clean", 47 | "lint": "eslint ./src --ext .ts,.tsx", 48 | "prepublishOnly": "npm run build", 49 | "publish:major": "npm version major && npm publish", 50 | "publish:minor": "npm version minor && npm publish", 51 | "publish:patch": "npm version patch && npm publish", 52 | "pretest": "npm run lint", 53 | "test": "lex test", 54 | "update": "npm-check-updates --interactive" 55 | }, 56 | "dependencies": { 57 | "lodash": "^4.17.21" 58 | }, 59 | "devDependencies": { 60 | "@nlabs/arkhamjs": "*", 61 | "@types/jest": "^29.5.14", 62 | "@types/lodash": "^4.17.15", 63 | "@types/node": "^22.13.1", 64 | "eslint": "^9.19.0", 65 | "eslint-config-styleguidejs": "^3.2.1", 66 | "react": "^19.0.0", 67 | "react-test-renderer": "^19.0.0", 68 | "typescript": "^5.7.3" 69 | }, 70 | "peerDependencies": { 71 | "@nlabs/arkhamjs": "^3.26.0", 72 | "react": "^18.0.0 || ^19.0.0" 73 | }, 74 | "gitHead": "fc371e1e28fe0ae35d40d29a217d5f0e990ec32a" 75 | } 76 | -------------------------------------------------------------------------------- /packages/arkhamjs-utils-react/src/FluxContext.ts: -------------------------------------------------------------------------------- 1 | import {createContext} from 'react'; 2 | 3 | export const FluxContext = createContext(null); 4 | -------------------------------------------------------------------------------- /packages/arkhamjs-utils-react/src/FluxProvider.test.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2018-Present, Nitrogen Labs, Inc. 3 | * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. 4 | */ 5 | import {Flux} from '@nlabs/arkhamjs'; 6 | 7 | import {FluxProvider} from './FluxProvider'; 8 | 9 | describe('FluxProvider', () => { 10 | let children: any[]; 11 | let provider; 12 | 13 | beforeAll(() => { 14 | Flux.init(); 15 | children = []; 16 | const props = {Flux, children}; 17 | provider = ; 18 | }); 19 | 20 | it('should create a provider with Flux', () => { 21 | expect(provider.props.Flux).toEqual(Flux); 22 | }); 23 | 24 | it('should create a provider with children', () => { 25 | expect(provider.props.children).toEqual(children); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /packages/arkhamjs-utils-react/src/FluxProvider.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2018-Present, Nitrogen Labs, Inc. 3 | * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. 4 | */ 5 | import isEqual from 'lodash/isEqual'; 6 | import {FC, useEffect, useRef, useState} from 'react'; 7 | 8 | import {FluxContext} from './FluxContext'; 9 | import {FluxProviderProps} from './FluxProvider.types'; 10 | 11 | export const FluxProvider: FC = ({children, flux}) => { 12 | const [state, setState] = useState(flux.getState()); 13 | const ref = useRef(state); 14 | 15 | useEffect(() => { 16 | const updateState = (newState) => { 17 | if(!isEqual(newState, ref.current)) { 18 | ref.current = newState; 19 | setState(newState); 20 | } 21 | }; 22 | flux.addListener('arkhamjs', updateState); 23 | 24 | return () => { 25 | flux.removeListener('arkhamjs', updateState); 26 | }; 27 | }, []); 28 | 29 | return {children}; 30 | }; 31 | -------------------------------------------------------------------------------- /packages/arkhamjs-utils-react/src/FluxProvider.types.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2018-Present, Nitrogen Labs, Inc. 3 | * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. 4 | */ 5 | import {FluxFramework} from '@nlabs/arkhamjs'; 6 | 7 | export interface FluxProviderProps { 8 | children?: any; 9 | flux: FluxFramework; 10 | state?: any; 11 | } 12 | -------------------------------------------------------------------------------- /packages/arkhamjs-utils-react/src/ResizeObserver.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * The **ResizeObserver** interface reports changes to the dimensions of an 3 | * [Element](https://developer.mozilla.org/en-US/docs/Web/API/Element)'s content 4 | * or border box, or the bounding box of an 5 | * [SVGElement](https://developer.mozilla.org/en-US/docs/Web/API/SVGElement). 6 | * 7 | * > **Note**: The content box is the box in which content can be placed, 8 | * > meaning the border box minus the padding and border width. The border box 9 | * > encompasses the content, padding, and border. See 10 | * > [The box model](https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/The_box_model) 11 | * > for further explanation. 12 | * 13 | * `ResizeObserver` avoids infinite callback loops and cyclic dependencies that 14 | * are often created when resizing via a callback function. It does this by only 15 | * processing elements deeper in the DOM in subsequent frames. Implementations 16 | * should, if they follow the specification, invoke resize events before paint 17 | * and after layout. 18 | * 19 | * @see https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver 20 | */ 21 | class ResizeObserver { 22 | /** 23 | * The **ResizeObserver** constructor creates a new `ResizeObserver` object, 24 | * which can be used to report changes to the content or border box of an 25 | * `Element` or the bounding box of an `SVGElement`. 26 | * 27 | * @example 28 | * var ResizeObserver = new ResizeObserver(callback) 29 | * 30 | * @param callback 31 | * The function called whenever an observed resize occurs. The function is 32 | * called with two parameters: 33 | * * **entries** 34 | * An array of 35 | * [ResizeObserverEntry](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserverEntry) 36 | * objects that can be used to access the new dimensions of the element 37 | * after each change. 38 | * * **observer** 39 | * A reference to the `ResizeObserver` itself, so it will definitely be 40 | * accessible from inside the callback, should you need it. This could be 41 | * used for example to automatically unobserve the observer when a certain 42 | * condition is reached, but you can omit it if you don't need it. 43 | * 44 | * The callback will generally follow a pattern along the lines of: 45 | * ```js 46 | * function(entries, observer) { 47 | * for (let entry of entries) { 48 | * // Do something to each entry 49 | * // and possibly something to the observer itself 50 | * } 51 | * } 52 | * ``` 53 | * 54 | * The following snippet is taken from the 55 | * [resize-observer-text.html](https://mdn.github.io/dom-examples/resize-observer/resize-observer-text.html) 56 | * ([see source](https://github.com/mdn/dom-examples/blob/master/resize-observer/resize-observer-text.html)) 57 | * example: 58 | * @example 59 | * const resizeObserver = new ResizeObserver(entries => { 60 | * for (let entry of entries) { 61 | * if(entry.contentBoxSize) { 62 | * h1Elem.style.fontSize = Math.max(1.5, entry.contentBoxSize.inlineSize/200) + 'rem'; 63 | * pElem.style.fontSize = Math.max(1, entry.contentBoxSize.inlineSize/600) + 'rem'; 64 | * } else { 65 | * h1Elem.style.fontSize = Math.max(1.5, entry.contentRect.width/200) + 'rem'; 66 | * pElem.style.fontSize = Math.max(1, entry.contentRect.width/600) + 'rem'; 67 | * } 68 | * } 69 | * }); 70 | * 71 | * resizeObserver.observe(divElem); 72 | */ 73 | constructor(callback: ResizeObserverCallback); 74 | 75 | /** 76 | * The **disconnect()** method of the 77 | * [ResizeObserver](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver) 78 | * interface unobserves all observed 79 | * [Element](https://developer.mozilla.org/en-US/docs/Web/API/Element) or 80 | * [SVGElement](https://developer.mozilla.org/en-US/docs/Web/API/SVGElement) 81 | * targets. 82 | */ 83 | disconnect: () => void; 84 | 85 | /** 86 | * The `observe()` method of the 87 | * [ResizeObserver](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver) 88 | * interface starts observing the specified 89 | * [Element](https://developer.mozilla.org/en-US/docs/Web/API/Element) or 90 | * [SVGElement](https://developer.mozilla.org/en-US/docs/Web/API/SVGElement). 91 | * 92 | * @example 93 | * resizeObserver.observe(target, options); 94 | * 95 | * @param target 96 | * A reference to an 97 | * [Element](https://developer.mozilla.org/en-US/docs/Web/API/Element) or 98 | * [SVGElement](https://developer.mozilla.org/en-US/docs/Web/API/SVGElement) 99 | * to be observed. 100 | * 101 | * @param options 102 | * An options object allowing you to set options for the observation. 103 | * Currently this only has one possible option that can be set. 104 | */ 105 | observe: (target: Element, options?: ResizeObserverObserveOptions) => void; 106 | 107 | /** 108 | * The **unobserve()** method of the 109 | * [ResizeObserver](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver) 110 | * interface ends the observing of a specified 111 | * [Element](https://developer.mozilla.org/en-US/docs/Web/API/Element) or 112 | * [SVGElement](https://developer.mozilla.org/en-US/docs/Web/API/SVGElement). 113 | */ 114 | unobserve: (target: Element) => void; 115 | } 116 | 117 | interface ResizeObserverObserveOptions { 118 | /** 119 | * Sets which box model the observer will observe changes to. Possible values 120 | * are `content-box` (the default), and `border-box`. 121 | * 122 | * @default "content-box" 123 | */ 124 | box?: "content-box" | "border-box"; 125 | } 126 | 127 | /** 128 | * The function called whenever an observed resize occurs. The function is 129 | * called with two parameters: 130 | * 131 | * @param entries 132 | * An array of 133 | * [ResizeObserverEntry](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserverEntry) 134 | * objects that can be used to access the new dimensions of the element after 135 | * each change. 136 | * 137 | * @param observer 138 | * A reference to the `ResizeObserver` itself, so it will definitely be 139 | * accessible from inside the callback, should you need it. This could be used 140 | * for example to automatically unobserve the observer when a certain condition 141 | * is reached, but you can omit it if you don't need it. 142 | * 143 | * The callback will generally follow a pattern along the lines of: 144 | * @example 145 | * function(entries, observer) { 146 | * for (let entry of entries) { 147 | * // Do something to each entry 148 | * // and possibly something to the observer itself 149 | * } 150 | * } 151 | * 152 | * @example 153 | * const resizeObserver = new ResizeObserver(entries => { 154 | * for (let entry of entries) { 155 | * if(entry.contentBoxSize) { 156 | * h1Elem.style.fontSize = Math.max(1.5, entry.contentBoxSize.inlineSize/200) + 'rem'; 157 | * pElem.style.fontSize = Math.max(1, entry.contentBoxSize.inlineSize/600) + 'rem'; 158 | * } else { 159 | * h1Elem.style.fontSize = Math.max(1.5, entry.contentRect.width/200) + 'rem'; 160 | * pElem.style.fontSize = Math.max(1, entry.contentRect.width/600) + 'rem'; 161 | * } 162 | * } 163 | * }); 164 | * 165 | * resizeObserver.observe(divElem); 166 | */ 167 | type ResizeObserverCallback = ( 168 | entries: ResizeObserverEntry[], 169 | observer: ResizeObserver, 170 | ) => void; 171 | 172 | /** 173 | * The **ResizeObserverEntry** interface represents the object passed to the 174 | * [ResizeObserver()](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver/ResizeObserver) 175 | * constructor's callback function, which allows you to access the new 176 | * dimensions of the 177 | * [Element](https://developer.mozilla.org/en-US/docs/Web/API/Element) or 178 | * [SVGElement](https://developer.mozilla.org/en-US/docs/Web/API/SVGElement) 179 | * being observed. 180 | */ 181 | interface ResizeObserverEntry { 182 | /** 183 | * An object containing the new border box size of the observed element when 184 | * the callback is run. 185 | */ 186 | readonly borderBoxSize: ResizeObserverEntryBoxSize; 187 | 188 | /** 189 | * An object containing the new content box size of the observed element when 190 | * the callback is run. 191 | */ 192 | readonly contentBoxSize: ResizeObserverEntryBoxSize; 193 | 194 | /** 195 | * A [DOMRectReadOnly](https://developer.mozilla.org/en-US/docs/Web/API/DOMRectReadOnly) 196 | * object containing the new size of the observed element when the callback is 197 | * run. Note that this is better supported than the above two properties, but 198 | * it is left over from an earlier implementation of the Resize Observer API, 199 | * is still included in the spec for web compat reasons, and may be deprecated 200 | * in future versions. 201 | */ 202 | // node_modules/typescript/lib/lib.dom.d.ts 203 | readonly contentRect: DOMRectReadOnly; 204 | 205 | /** 206 | * A reference to the 207 | * [Element](https://developer.mozilla.org/en-US/docs/Web/API/Element) or 208 | * [SVGElement](https://developer.mozilla.org/en-US/docs/Web/API/SVGElement) 209 | * being observed. 210 | */ 211 | readonly target: Element; 212 | } 213 | 214 | /** 215 | * The **borderBoxSize** read-only property of the 216 | * [ResizeObserverEntry](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserverEntry) 217 | * interface returns an object containing the new border box size of the 218 | * observed element when the callback is run. 219 | */ 220 | interface ResizeObserverEntryBoxSize { 221 | /** 222 | * The length of the observed element's border box in the block dimension. For 223 | * boxes with a horizontal 224 | * [writing-mode](https://developer.mozilla.org/en-US/docs/Web/CSS/writing-mode), 225 | * this is the vertical dimension, or height; if the writing-mode is vertical, 226 | * this is the horizontal dimension, or width. 227 | */ 228 | blockSize: number; 229 | 230 | /** 231 | * The length of the observed element's border box in the inline dimension. 232 | * For boxes with a horizontal 233 | * [writing-mode](https://developer.mozilla.org/en-US/docs/Web/CSS/writing-mode), 234 | * this is the horizontal dimension, or width; if the writing-mode is 235 | * vertical, this is the vertical dimension, or height. 236 | */ 237 | inlineSize: number; 238 | } 239 | 240 | interface Window { 241 | ResizeObserver: ResizeObserver; 242 | } -------------------------------------------------------------------------------- /packages/arkhamjs-utils-react/src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2018-Present, Nitrogen Labs, Inc. 3 | * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. 4 | */ 5 | export {FluxContext} from './FluxContext'; 6 | export {FluxProvider} from './FluxProvider'; 7 | export {useComponentSize} from './useComponentSize'; 8 | export {useFlux} from './useFlux'; 9 | export {useFluxDispatch} from './useFluxDispatch'; 10 | export {useFluxListener} from './useFluxListener'; 11 | export {useFluxState} from './useFluxState'; 12 | export {useFluxValue} from './useFluxValue'; 13 | export {useRefSize} from './useRefSize'; 14 | export {useState} from './useState'; 15 | export {useWindowSize} from './useWindowSize'; 16 | 17 | export * from './FluxProvider.types'; 18 | export * from './useWindowSize.types'; 19 | -------------------------------------------------------------------------------- /packages/arkhamjs-utils-react/src/useComponentSize.ts: -------------------------------------------------------------------------------- 1 | import {useCallback, useLayoutEffect, useState} from 'react'; 2 | 3 | export const getComponentSize = (element) => { 4 | if(!element) { 5 | return {height: undefined, width: undefined}; 6 | } 7 | 8 | const {offsetHeight: height, offsetWidth: width} = element; 9 | return {height, width}; 10 | }; 11 | 12 | export const useComponentSize = (component) => { 13 | const [componentSize, setComponentSize] = useState(getComponentSize(component)); 14 | const onResize = useCallback(() => { 15 | if(component) { 16 | setComponentSize(getComponentSize(component)); 17 | } 18 | }, [component]); 19 | 20 | useLayoutEffect(() => { 21 | if(!component) { 22 | return () => {}; 23 | } 24 | 25 | onResize(); 26 | 27 | if(typeof ResizeObserver === 'function') { 28 | let resizeObserver = new ResizeObserver(onResize); 29 | resizeObserver.observe(component); 30 | 31 | return () => { 32 | resizeObserver.disconnect(); 33 | resizeObserver = null; 34 | }; 35 | } 36 | 37 | window.addEventListener('resize', onResize); 38 | return () => window.removeEventListener('resize', onResize); 39 | }, [component]); 40 | 41 | return componentSize; 42 | }; 43 | -------------------------------------------------------------------------------- /packages/arkhamjs-utils-react/src/useFlux.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2022-Present, Nitrogen Labs, Inc. 3 | * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. 4 | */ 5 | import {useContext} from 'react'; 6 | 7 | import {FluxContext} from './FluxContext'; 8 | 9 | export const useFlux = (): any => { 10 | const {flux} = useContext(FluxContext); 11 | return flux; 12 | }; 13 | -------------------------------------------------------------------------------- /packages/arkhamjs-utils-react/src/useFluxDispatch.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2020-Present, Nitrogen Labs, Inc. 3 | * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. 4 | */ 5 | import {useContext} from 'react'; 6 | 7 | import {FluxContext} from './FluxContext'; 8 | 9 | export const useFluxDispatch = (type: string, data: any = {}, silent: boolean = false) => { 10 | const {flux} = useContext(FluxContext); 11 | return flux.dispatch({...data, type}, silent); 12 | }; 13 | -------------------------------------------------------------------------------- /packages/arkhamjs-utils-react/src/useFluxListener.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2020-Present, Nitrogen Labs, Inc. 3 | * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. 4 | */ 5 | import {useContext, useEffect} from 'react'; 6 | 7 | import {FluxContext} from './FluxContext'; 8 | 9 | export const useFluxListener = (type: string, handler: (data?: any) => any) => { 10 | const {flux} = useContext(FluxContext); 11 | 12 | useEffect(() => { 13 | flux.on(type, handler); 14 | 15 | return () => { 16 | flux.off(type, handler); 17 | }; 18 | }, []); 19 | 20 | return handler; 21 | }; 22 | -------------------------------------------------------------------------------- /packages/arkhamjs-utils-react/src/useFluxState.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2020-Present, Nitrogen Labs, Inc. 3 | * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. 4 | */ 5 | import isEqual from 'lodash/isEqual'; 6 | import {useContext, useEffect, useRef, useState} from 'react'; 7 | 8 | import {FluxContext} from './FluxContext'; 9 | 10 | export const useFluxState = (key: string | string[], defaultValue?: any): any => { 11 | const {flux, state} = useContext(FluxContext); 12 | const ref = useRef(); 13 | const value = flux.getState(key, defaultValue); 14 | const [updatedValue, setValue] = useState(value); 15 | 16 | useEffect(() => { 17 | if(!isEqual(value, ref.current)) { 18 | ref.current = value; 19 | setValue(value); 20 | } 21 | }, [state]); 22 | 23 | return updatedValue; 24 | }; 25 | -------------------------------------------------------------------------------- /packages/arkhamjs-utils-react/src/useFluxValue.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021-Present, Nitrogen Labs, Inc. 3 | * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. 4 | */ 5 | import {useContext} from 'react'; 6 | 7 | import {FluxContext} from './FluxContext'; 8 | 9 | export const useFluxValue = (key: string | string[], defaultValue?: any): any => { 10 | const {flux} = useContext(FluxContext); 11 | return flux.getState(key, defaultValue); 12 | }; 13 | -------------------------------------------------------------------------------- /packages/arkhamjs-utils-react/src/useRefSize.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2019-Present, Nitrogen Labs, Inc. 3 | * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. 4 | */ 5 | import {useComponentSize} from './useComponentSize'; 6 | 7 | export const useRefSize = (ref) => { 8 | const {current: component = {}} = ref; 9 | return useComponentSize(component); 10 | }; 11 | -------------------------------------------------------------------------------- /packages/arkhamjs-utils-react/src/useState.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2019-Present, Nitrogen Labs, Inc. 3 | * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. 4 | */ 5 | import isPlainObject from 'lodash/isPlainObject'; 6 | import merge from 'lodash/merge'; 7 | import {useState as useReactState} from 'react'; 8 | 9 | export const useState = (initialState: any) => { 10 | const [state, setNewState] = useReactState( 11 | (prevState: any) => { 12 | if(isPlainObject(initialState)) { 13 | return merge(prevState, initialState); 14 | } 15 | 16 | return initialState; 17 | }); 18 | 19 | return [ 20 | state, 21 | setNewState 22 | ]; 23 | }; 24 | -------------------------------------------------------------------------------- /packages/arkhamjs-utils-react/src/useWindowSize.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2019-Present, Nitrogen Labs, Inc. 3 | * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. 4 | */ 5 | import {useEffect, useState} from 'react'; 6 | 7 | import {WindowSize} from './useWindowSize.types'; 8 | 9 | export const isClient = typeof window === 'object'; 10 | 11 | export const getWindowSize = () => { 12 | if(isClient) { 13 | const {innerHeight: height, innerWidth: width} = window; 14 | return {height, width}; 15 | } 16 | 17 | return {height: undefined, width: undefined}; 18 | }; 19 | 20 | export const useWindowSize = (): WindowSize => { 21 | const [size, setSize] = useState(getWindowSize()); 22 | 23 | useEffect(() => { 24 | if(!isClient) { 25 | return () => {}; 26 | } 27 | 28 | const onResize = () => setSize(getWindowSize()); 29 | window.addEventListener('resize', onResize); 30 | return () => window.removeEventListener('resize', onResize); 31 | }, []); 32 | 33 | return size; 34 | }; 35 | -------------------------------------------------------------------------------- /packages/arkhamjs-utils-react/src/useWindowSize.types.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2019-Present, Nitrogen Labs, Inc. 3 | * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. 4 | */ 5 | export interface WindowSize { 6 | readonly height: number; 7 | readonly width: number; 8 | } 9 | -------------------------------------------------------------------------------- /packages/arkhamjs-utils-react/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "baseUrl": "./src", 5 | "declaration": true, 6 | "emitDeclarationOnly": true, 7 | "esModuleInterop": true, 8 | "inlineSourceMap": true, 9 | "jsx": "react-jsx", 10 | "lib": [ 11 | "esnext", 12 | "dom" 13 | ], 14 | "module": "commonjs", 15 | "moduleResolution": "node", 16 | "noImplicitReturns": true, 17 | "noImplicitThis": true, 18 | "noUnusedLocals": true, 19 | "outDir": "lib", 20 | "pretty": true, 21 | "skipLibCheck": true, 22 | "target": "ES2018", 23 | "typeRoots": [ 24 | "node_modules/@types", 25 | "node_modules/json-d-ts" 26 | ] 27 | }, 28 | "include": [ 29 | "./src/**/*" 30 | ], 31 | "exclude": [ 32 | "**/*.test.*", 33 | "lib", 34 | "./node_modules", 35 | "./test" 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /packages/arkhamjs/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "styleguidejs/typescript" 3 | } -------------------------------------------------------------------------------- /packages/arkhamjs/.gitignore: -------------------------------------------------------------------------------- 1 | coverage 2 | lib 3 | node_modules 4 | *.log -------------------------------------------------------------------------------- /packages/arkhamjs/.npmignore: -------------------------------------------------------------------------------- 1 | coverage 2 | docs 3 | node_modules 4 | src 5 | .eslintrc 6 | lex.config.js 7 | package-lock.json 8 | tsconfig.json 9 | -------------------------------------------------------------------------------- /packages/arkhamjs/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Nitrogen Labs, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/arkhamjs/README.md: -------------------------------------------------------------------------------- 1 | # @nlabs/arkhamjs 2 | 3 | 4 | 5 | [![npm version](https://img.shields.io/npm/v/@nlabs/arkhamjs.svg?style=flat-square)](https://www.npmjs.com/package/@nlabs/arkhamjs) 6 | [![npm downloads](https://img.shields.io/npm/dm/@nlabs/arkhamjs.svg?style=flat-square)](https://www.npmjs.com/package/@nlabs/arkhamjs) 7 | [![Travis](https://img.shields.io/travis/nitrogenlabs/arkhamjs.svg?style=flat-square)](https://travis-ci.org/nitrogenlabs/arkhamjs) 8 | [![Issues](https://img.shields.io/github/issues/nitrogenlabs/arkhamjs.svg?style=flat-square)](https://github.com/nitrogenlabs/arkhamjs/issues) 9 | [![TypeScript](https://badges.frapsoft.com/typescript/version/typescript-next.svg?v=101)](https://github.com/ellerbrock/typescript-badges/) 10 | [![MIT license](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](http://opensource.org/licenses/MIT) 11 | [![Chat](https://img.shields.io/discord/446122412715802649.svg)](https://discord.gg/Ttgev58) 12 | 13 | ## Features 14 | 15 | ### Flux Framework 16 | 17 | ArkhamJS is a lightweight framework that can accommodate a project of any size, small or large. From small start-up ideas to large enterprise projects. A simple, flexible framework. Consisting of a singular state tree with a unidirectional data flow. 18 | 19 | ### Lightweight 20 | 21 | The framework is small. The bulk of your app should lay within your code, not the framework. While larger frameworks come with lots of "magic", they become very limited when new features arise within your project. 22 | 23 | ### Typescript 24 | 25 | Compatible with typescript. Definitions are included to support your Typescript project. 26 | 27 | ### Single Store 28 | 29 | All data is stored within a single store. The data can be accessed through all your views and components. Data is organized into multiple stores within the single store. 30 | 31 | ### Immutability 32 | 33 | To prevent object referencing, we use immutable objects. When the state changes, the state's property is not the only item that is changed, the item it references is also updated. To prevent passing around an object between different scopes, immutable objects give your data a one way update path. You may also have returned values converted into ImmutableJS objects. 34 | 35 | ### Debugger 36 | 37 | The most important factor in choosing a framework is how easy it is to build with it. And with building comes debugging. A state debugger can be added with the middleware, [@nlabs/arkhamjs-middleware-logger](https://github.com/nitrogenlabs/arkhamjs-middleware-logger). When turned on, it will display any actions and state changes that come through the framework. Making the previous and next state visible to the developer. Great way to make your data transparent! Supported browsers: Chrome, Firefox, and Safari. 38 | 39 | ### Cache Storage 40 | 41 | An app state is clears after a browser refresh. Keeping the state after a reload can be very useful when requiring a persistent state. 42 | 43 | If you plan to persist data, you will need to add a storage to the framework: 44 | 45 | - React [@nlabs/arkhamjs-storage-browser](https://github.com/nitrogenlabs/arkhamjs-storage-browser) 46 | - React Native [@nlabs/arkhamjs-storage-native](https://github.com/nitrogenlabs/arkhamjs-storage-native) 47 | - NodeJS [@nlabs/arkhamjs-storage-node](https://github.com/nitrogenlabs/arkhamjs-storage-node) 48 | 49 | ## Installation 50 | 51 | Using [npm](https://www.npmjs.com/): 52 | 53 | ```shell 54 | npm install --save @nlabs/arkhamjs 55 | ``` 56 | 57 | ## Documentation 58 | 59 | For detailed [Documentation](https://arkhamjs.io) and additional options. 60 | 61 | ## Demo 62 | 63 | Try tinkering with a simplified demo in [JSFiddle](https://jsfiddle.net/nitrog7/j3k762vd/)! 64 | 65 | ## Examples 66 | 67 | ### React Typescript Example 68 | 69 | For a complete example of a React setup using Typescript, feel free to start your project with [arkhamjs-example-ts-react](https://github.com/nitrogenlabs/arkhamjs-example-ts-react). 70 | -------------------------------------------------------------------------------- /packages/arkhamjs/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021-Present, Nitrogen Labs, Inc. 3 | * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. 4 | */ 5 | 6 | module.exports = require('./lib'); 7 | -------------------------------------------------------------------------------- /packages/arkhamjs/lex.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | outputPath: 'lib', 3 | preset: 'web', 4 | remove: true, 5 | useTypescript: true 6 | }; 7 | -------------------------------------------------------------------------------- /packages/arkhamjs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@nlabs/arkhamjs", 3 | "version": "3.28.5", 4 | "publishConfig": { 5 | "access": "public" 6 | }, 7 | "description": "Javascript Flux Library", 8 | "license": "MIT", 9 | "main": "./index.js", 10 | "module": "./index.js", 11 | "browser": "./index.js", 12 | "types": "./lib/index.d.js", 13 | "keywords": [ 14 | "arkhamjs", 15 | "flux", 16 | "global state", 17 | "immutable", 18 | "nitrogenlabs", 19 | "state", 20 | "state management" 21 | ], 22 | "author": { 23 | "name": "Giraldo Rosales", 24 | "email": "giraldo@nitrogenlabs.com", 25 | "url": "http://nitrogenlabs.com" 26 | }, 27 | "repository": { 28 | "type": "git", 29 | "url": "nitrogenlabs/arkhamjs.git" 30 | }, 31 | "homepage": "https://arkhamjs.io", 32 | "bugs": { 33 | "url": "https://github.com/nitrogenlabs/arkhamjs/issues" 34 | }, 35 | "scripts": { 36 | "build": "lex compile --remove", 37 | "clean": "lex clean", 38 | "lint": "eslint ./src --ext .ts,.tsx", 39 | "prepublishOnly": "npm run build", 40 | "publish:major": "npm version major && npm publish", 41 | "publish:minor": "npm version minor && npm publish", 42 | "publish:patch": "npm version patch && npm publish", 43 | "pretest": "npm run lint", 44 | "test": "lex test", 45 | "update": "npm-check-updates --interactive" 46 | }, 47 | "dependencies": { 48 | "error-stack-parser": "^2.1.4", 49 | "events": "^3.3.0", 50 | "lodash": "^4.17.21" 51 | }, 52 | "devDependencies": { 53 | "@types/events": "^3.0.3", 54 | "@types/jest": "^29.5.14", 55 | "@types/lodash": "^4.17.15", 56 | "@types/node": "^22.13.1", 57 | "eslint": "^9.19.0", 58 | "eslint-config-styleguidejs": "^3.2.1", 59 | "typescript": "^5.7.3" 60 | }, 61 | "gitHead": "fc371e1e28fe0ae35d40d29a217d5f0e990ec32a" 62 | } 63 | -------------------------------------------------------------------------------- /packages/arkhamjs/src/Flux/Flux.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2018-Present, Nitrogen Labs, Inc. 3 | * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. 4 | */ 5 | import ErrorStackParser from 'error-stack-parser'; 6 | import {EventEmitter} from 'events'; 7 | import debounce from 'lodash/debounce'; 8 | import cloneDeep from 'lodash/fp/cloneDeep'; 9 | import get from 'lodash/fp/get'; 10 | import isEmpty from 'lodash/fp/isEmpty'; 11 | import merge from 'lodash/fp/merge'; 12 | import set from 'lodash/fp/set'; 13 | 14 | import {ArkhamConstants} from '../constants/ArkhamConstants'; 15 | import {FluxAction, FluxMiddlewareType, FluxOptions, FluxPluginType, FluxStore} from './Flux.types'; 16 | 17 | /** 18 | * FluxFramework 19 | * @type {EventEmitter} 20 | */ 21 | export class FluxFramework extends EventEmitter { 22 | static initFlux: boolean = false; 23 | isInit: boolean = false; 24 | // Public properties 25 | pluginTypes: string[] = ['preDispatch', 'postDispatch']; 26 | // Private properties 27 | private state: any = {}; 28 | private storeActions: any = {}; 29 | private defaultOptions: FluxOptions = { 30 | name: 'arkhamjs', 31 | routerType: 'browser', 32 | scrollToTop: true, 33 | state: null, 34 | storage: null, 35 | storageWait: 300, 36 | stores: [], 37 | title: 'ArkhamJS' 38 | }; 39 | private middleware: any = {}; 40 | private options: FluxOptions = this.defaultOptions; 41 | /** 42 | * Create a new instance of Flux. Note that the Flux object 43 | * is a Singleton pattern, so only one should ever exist. 44 | * 45 | * @constructor 46 | * @this {FluxFramework} 47 | */ 48 | constructor() { 49 | super(); 50 | 51 | // Methods 52 | this.addMiddleware = this.addMiddleware.bind(this); 53 | this.addStores = this.addStores.bind(this); 54 | this.clearAppData = this.clearAppData.bind(this); 55 | this.clearMiddleware = this.clearMiddleware.bind(this); 56 | this.deregister = this.deregister.bind(this); 57 | this.dispatch = this.dispatch.bind(this); 58 | this.getOptions = this.getOptions.bind(this); 59 | this.getState = this.getState.bind(this); 60 | this.getStore = this.getStore.bind(this); 61 | this.init = this.init.bind(this); 62 | this.off = this.off.bind(this); 63 | this.register = this.register.bind(this); 64 | this.removeMiddleware = this.removeMiddleware.bind(this); 65 | this.removeStores = this.removeStores.bind(this); 66 | this.reset = this.reset.bind(this); 67 | this.setState = this.setState.bind(this); 68 | 69 | // Add middleware plugin types 70 | this.pluginTypes.forEach((type: string) => this.middleware[`${type}List`] = []); 71 | } 72 | 73 | /** 74 | * Add middleware to framework. 75 | * 76 | * @param {array} middleware An array of middleware to add to the framework. 77 | */ 78 | addMiddleware(middleware: FluxMiddlewareType[]): void { 79 | middleware.forEach((middleObj: FluxMiddlewareType) => { 80 | // Make sure middleware is either a class or object. 81 | if(!!middleObj && ((typeof middleObj === 'function') || (typeof middleObj === 'object'))) { 82 | const middleName: string = middleObj.name || ''; 83 | 84 | if(!middleName) { 85 | throw Error('Unknown middleware is not configured properly. Requires name property. Cannot add to Flux.'); 86 | } 87 | 88 | // Sort middleware plugins for efficiency 89 | this.pluginTypes.forEach((type: string) => { 90 | const method = middleObj[type]; 91 | const plugin: FluxPluginType = {method, name: middleName}; 92 | this.middleware[`${type}List`] = this.addPlugin(type, plugin); 93 | }); 94 | } else { 95 | throw Error('Unknown middleware is not configured properly. Cannot add to Flux.'); 96 | } 97 | }); 98 | } 99 | 100 | /** 101 | * Remove all app data from storage. 102 | * 103 | * @returns {Promise} Whether app data was successfully removed. 104 | */ 105 | clearAppData(): Promise { 106 | // Set all store data to initial state 107 | Object 108 | .keys(this.storeActions) 109 | .forEach((storeName: string) => { 110 | const storeFn = this.storeActions[storeName]; 111 | this.state[storeFn.name] = cloneDeep(storeFn.initialState); 112 | }); 113 | 114 | const {name, storage} = this.options; 115 | 116 | if(storage?.setStorageData) { 117 | return storage.setStorageData(name, this.state); 118 | } 119 | 120 | return Promise.resolve(true); 121 | } 122 | 123 | /** 124 | * Remove all middleware. 125 | * 126 | * @returns {boolean} Whether middleware was successfully removed. 127 | */ 128 | clearMiddleware(): boolean { 129 | // Set all store data to initial state 130 | Object 131 | .keys(this.middleware) 132 | .forEach((pluginType: string) => { 133 | this.middleware[pluginType] = []; 134 | }); 135 | 136 | return true; 137 | } 138 | 139 | /** 140 | * De-registers named stores. 141 | * 142 | * @param {array} storeNames An array of store names to remove from the framework. 143 | */ 144 | removeStores(storeNames: string[]): void { 145 | storeNames.forEach((name: string) => this.deregister(name)); 146 | } 147 | 148 | /** 149 | * Dispatches an action to all stores. 150 | * 151 | * @param {object} action to dispatch to all the stores. 152 | * @param {boolean} silent To silence any events. 153 | * @returns {Promise} The promise is resolved when and if the app saves data to storage, returning 154 | * the action. 155 | */ 156 | async dispatch(action: FluxAction, silent: boolean = false): Promise { 157 | if(!action) { 158 | throw new Error('ArkhamJS Error: Flux.dispatch requires an action.'); 159 | } 160 | 161 | let clonedAction: FluxAction = cloneDeep(action); 162 | 163 | // Log duration of dispatch 164 | const startTime: number = Date.now(); 165 | 166 | // Get stack 167 | let stack = []; 168 | 169 | try { 170 | const stackProperty: string = 'stackTraceLimit'; 171 | const {stackTraceLimit}: any = Error; 172 | Error[stackProperty] = Infinity; 173 | stack = ErrorStackParser.parse(new Error()); 174 | Error[stackProperty] = stackTraceLimit; 175 | } catch(error) {} 176 | 177 | // Get options 178 | const options = cloneDeep(this.options); 179 | 180 | // App info 181 | const appInfo = {duration: 0, options, stack}; 182 | 183 | // Apply middleware before the action is processed 184 | const {postDispatchList = [], preDispatchList = []} = this.middleware; 185 | 186 | if(preDispatchList.length) { 187 | clonedAction = await Promise 188 | .all( 189 | preDispatchList.map((plugin: FluxPluginType) => plugin.method( 190 | cloneDeep(clonedAction), cloneDeep(this.state), appInfo) 191 | ) 192 | ) 193 | .then( 194 | (actions) => actions.reduce((updatedAction, action) => 195 | merge(updatedAction, action), clonedAction) as FluxAction 196 | ) 197 | .catch((error) => { 198 | throw error; 199 | }); 200 | } 201 | 202 | const {type, ...data} = clonedAction; 203 | 204 | // Require a type 205 | if(!type || type === '') { 206 | console.warn('ArkhamJS Warning: Flux.dispatch is missing an action type for the payload:', data); 207 | return Promise.resolve(clonedAction); 208 | } 209 | 210 | // When an action comes in, it must be completely handled by all stores 211 | Object 212 | .keys(this.storeActions) 213 | .forEach((storeName: string) => { 214 | const storeFn = this.storeActions[storeName]; 215 | const state = cloneDeep(this.state[storeName]) || cloneDeep(storeFn.initialState) || {}; 216 | this.state[storeName] = cloneDeep(storeFn.action(type, data, state)) || state; 217 | }); 218 | 219 | // Save cache in storage 220 | const {storage} = this.options; 221 | 222 | if(storage && this.updateStorage) { 223 | try { 224 | await this.updateStorage(); 225 | } catch(error) {} 226 | } 227 | 228 | const endTime: number = +(new Date()); 229 | const duration: number = endTime - startTime; 230 | appInfo.duration = duration; 231 | 232 | if(postDispatchList.length) { 233 | clonedAction = await Promise 234 | .all( 235 | postDispatchList.map( 236 | async (plugin: FluxPluginType) => plugin.method(cloneDeep(clonedAction), cloneDeep(this.state), appInfo) 237 | ) 238 | ) 239 | .then( 240 | (actions) => actions.reduce((updatedAction, action) => 241 | merge(updatedAction, action), clonedAction) as FluxAction 242 | ) 243 | .catch((error) => { 244 | throw error; 245 | }); 246 | } 247 | 248 | if(!silent) { 249 | this.emit(type, clonedAction); 250 | this.emit('arkhamjs', this.state); 251 | } 252 | 253 | return Promise.resolve(clonedAction); 254 | } 255 | 256 | /** 257 | * Get the current Flux options. 258 | * 259 | * @returns {FluxOptions} the Flux options object. 260 | */ 261 | getOptions(): FluxOptions { 262 | return this.options; 263 | } 264 | 265 | /** 266 | * Get the current state object. 267 | * 268 | * @param {string|array} [name] (optional) The name of the store for an object, otherwise it will return all store 269 | * objects. You can also use an array to specify a property path within the object. 270 | * @param {any} [defaultValue] (optional) A default value to return if null. 271 | * @returns {any} the state object or a property value within. 272 | */ 273 | getState(path: string | string[] = '', defaultValue?): any { 274 | let storeValue; 275 | 276 | if(!path) { 277 | storeValue = this.state || {}; 278 | } else { 279 | storeValue = get(path, this.state); 280 | } 281 | 282 | const value = storeValue ? cloneDeep(storeValue) : storeValue; 283 | return value === undefined ? defaultValue : value; 284 | } 285 | 286 | /** 287 | * Get a store object registered with Flux. 288 | * 289 | * @param {string} name The name of the store. 290 | * @returns {FluxStore} the store object. 291 | */ 292 | getStore(name: string = ''): FluxStore { 293 | return this.storeActions[name]; 294 | } 295 | 296 | /** 297 | * Initialize and set configuration options. 298 | * 299 | * @param {object} options Configuration options. 300 | */ 301 | async init(options: FluxOptions = {}, reset: boolean = false): Promise { 302 | // Should reset previous params 303 | if(reset) { 304 | this.isInit = false; 305 | this.reset(false); 306 | } 307 | 308 | // Set options 309 | const updatedOptions = {...options}; 310 | 311 | if(this.isInit) { 312 | // Remove the name from options if already initialized, otherwise the root app will not be able to access 313 | // the state tree 314 | updatedOptions.name = this.options.name; 315 | } 316 | 317 | this.options = {...this.defaultOptions, ...updatedOptions}; 318 | const {debug, middleware, name, stores} = this.options; 319 | 320 | // Update default store 321 | try { 322 | await this.useStorage(name); 323 | } catch(error) { 324 | console.error('Arkham Error: There was an error while using storage.', name); 325 | throw error; 326 | } 327 | 328 | if(!!stores && stores.length) { 329 | try { 330 | await this.addStores(stores); 331 | } catch(error) { 332 | console.error('Arkham Error: There was an error while adding stores.', stores); 333 | throw error; 334 | } 335 | } 336 | 337 | if(!!middleware && middleware.length) { 338 | this.addMiddleware(middleware); 339 | } 340 | 341 | const windowProperty: string = 'arkhamjs'; 342 | 343 | if(debug) { 344 | window[windowProperty] = this; 345 | } else { 346 | delete window[windowProperty]; 347 | } 348 | 349 | this.isInit = true; 350 | this.emit(ArkhamConstants.INIT); 351 | 352 | return this; 353 | } 354 | 355 | /** 356 | * Adds an initialization listener. 357 | * 358 | * @param {function} [listener] The callback associated with the subscribed event. 359 | */ 360 | onInit(listener: (...args: any[]) => void): void { 361 | this.on(ArkhamConstants.INIT, listener); 362 | 363 | if(this.isInit) { 364 | listener(); 365 | } 366 | } 367 | 368 | /** 369 | * Removes the initialization listener. 370 | * 371 | * @param {function} [listener] The callback associated with the subscribed event. 372 | */ 373 | offInit(listener: (...args: any[]) => void): void { 374 | this.off(ArkhamConstants.INIT, listener); 375 | } 376 | 377 | /** 378 | * Removes an event listener. 379 | * 380 | * @param {string} [eventType] Event to unsubscribe. 381 | * @param {function} [listener] The callback associated with the subscribed event. 382 | */ 383 | off(eventType: string, listener: (...args: any[]) => void): this { 384 | return this.removeListener(eventType, listener); 385 | } 386 | 387 | /** 388 | * Adds an event listener. 389 | * 390 | * @param {string} [eventType] Event to subscribe. 391 | * @param {function} [listener] The callback associated with the subscribed event. 392 | */ 393 | on(eventType: string, listener: (...args: any[]) => void): this { 394 | return this.addListener(eventType, listener); 395 | } 396 | 397 | /** 398 | * Registers new Stores. 399 | * 400 | * @param {array} stores Store class. 401 | * @returns {Promise} the class object(s). 402 | */ 403 | async addStores(stores: any[]): Promise { 404 | const registeredStores: FluxStore[] = stores.map((store: FluxStore) => this.register(store)); 405 | 406 | // Save cache in session storage 407 | const {name, storage} = this.options; 408 | 409 | if(storage?.setStorageData) { 410 | try { 411 | await storage.setStorageData(name, this.state); 412 | } catch(error) { 413 | throw error; 414 | } 415 | } 416 | 417 | // Return classes 418 | return registeredStores; 419 | } 420 | 421 | /** 422 | * Remove middleware from framework. 423 | * 424 | * @param {array} string middleware names to remove. 425 | * @returns {Promise} the class object(s). 426 | */ 427 | removeMiddleware(names: string[]): void { 428 | names.forEach((name: string) => { 429 | // Remove middleware plugins 430 | this.pluginTypes.forEach((type: string) => { 431 | this.middleware[`${type}List`] = this.removePlugin(type, name); 432 | }); 433 | }); 434 | } 435 | 436 | /** 437 | * Reset framework. 438 | * 439 | * @param {array} string middleware names to remove. 440 | * @returns {Promise} the class object(s). 441 | */ 442 | async reset(clearStorage: boolean = true): Promise { 443 | const {name, storage} = this.options; 444 | 445 | // Clear persistent cache 446 | if(storage && clearStorage) { 447 | try { 448 | await storage.setStorageData(name, {}); 449 | } catch(error) { 450 | throw error; 451 | } 452 | } 453 | 454 | // Clear all properties 455 | this.middleware = {}; 456 | this.options = {...this.defaultOptions}; 457 | this.state = {}; 458 | this.storeActions = {}; 459 | this.isInit = false; 460 | } 461 | 462 | /** 463 | * Sets the current state object. 464 | * 465 | * @param {string|array} [name] The name of the store to set. You can also use an array to specify a property path 466 | * within the object. 467 | * @param {any} [value] The value to set. 468 | */ 469 | setState(path: string | string[] = '', value): Promise { 470 | if(!!path) { 471 | this.state = set(path, cloneDeep(value), this.state); 472 | } 473 | 474 | // Update persistent cache 475 | const {storage} = this.options; 476 | 477 | if(storage && this.updateStorage) { 478 | return this.updateStorage(); 479 | } 480 | 481 | return Promise.resolve(false); 482 | } 483 | 484 | private addPlugin(type: string, plugin: FluxPluginType): FluxPluginType[] { 485 | const list = this.middleware[`${type}List`] || []; 486 | const {method, name} = plugin; 487 | 488 | if(method && typeof method === 'function') { 489 | // Check if plugin already exists 490 | const exists: boolean = !!list.filter((obj: FluxPluginType) => obj.name === name).length; 491 | 492 | // Do not add duplicate plugins 493 | if(!exists) { 494 | list.push({method, name}); 495 | } 496 | } else if(method !== undefined) { 497 | throw Error(`${plugin.name} middleware is not configured properly. Method is not a function.`); 498 | } 499 | 500 | return list; 501 | } 502 | 503 | private deregister(name: string = ''): void { 504 | delete this.storeActions[name]; 505 | delete this.state[name]; 506 | } 507 | 508 | private register(storeFn): FluxStore { 509 | if(!storeFn) { 510 | throw Error('Store is undefined. Cannot register with Flux.'); 511 | } 512 | 513 | const isFnc: boolean = typeof storeFn === 'function'; 514 | 515 | if(!isFnc) { 516 | throw Error(`${storeFn} is not a store function. Cannot register with Flux.`); 517 | } 518 | 519 | // Create store object 520 | const {name} = storeFn; 521 | const initialState: any = storeFn(); 522 | const storeAction = { 523 | action: storeFn, 524 | initialState: storeFn(), 525 | name 526 | }; 527 | 528 | if(!isEmpty(name) && !this.storeActions[name]) { 529 | // Save store object 530 | this.storeActions[name] = storeAction; 531 | 532 | // Get default values 533 | if(!this.state[name]) { 534 | if(initialState) { 535 | this.state[name] = cloneDeep(initialState); 536 | } else { 537 | this.state[name] = {}; 538 | } 539 | } 540 | } 541 | 542 | // Return store class 543 | return this.storeActions[name]; 544 | } 545 | 546 | private removePlugin(type: string, name: string): FluxPluginType[] { 547 | const list = this.middleware[`${type}List`] || []; 548 | 549 | // remove all occurrences of the plugin 550 | return list.filter((obj: FluxPluginType) => obj.name !== name); 551 | } 552 | 553 | private updateStorage = () => Promise.resolve(false); 554 | 555 | private async useStorage(name: string): Promise { 556 | const {storage, state, storageWait} = this.options; 557 | 558 | // Cache 559 | if(storage) { 560 | try { 561 | this.state = state || await storage.getStorageData(name) || {}; 562 | this.updateStorage = debounce( 563 | () => storage.setStorageData(name, this.state), 564 | storageWait, 565 | {leading: true, trailing: true} 566 | ); 567 | } catch(error) { 568 | console.error(`ArkhamJS Error: Using storage, "${name}".`); 569 | throw error; 570 | } 571 | } else { 572 | this.state = state || {}; 573 | } 574 | 575 | return null; 576 | } 577 | } 578 | 579 | export const Flux: FluxFramework = new FluxFramework(); 580 | -------------------------------------------------------------------------------- /packages/arkhamjs/src/Flux/Flux.types.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2018-Present, Nitrogen Labs, Inc. 3 | * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. 4 | */ 5 | export interface FluxOptions { 6 | readonly basename?: string; 7 | readonly context?: object; 8 | readonly debug?: boolean; 9 | readonly getUserConfirmation?: () => void; 10 | readonly hashType?: 'slash' | 'noslash' | 'hashbang'; 11 | readonly history?: object; 12 | readonly initialEntries?: any[]; 13 | readonly initialIndex?: number; 14 | readonly keyLength?: number; 15 | readonly location?: string | object; 16 | readonly middleware?: FluxMiddlewareType[]; 17 | readonly name?: string; 18 | readonly routerType?: string; 19 | readonly scrollToTop?: boolean; 20 | readonly state?: any; 21 | readonly storage?: FluxStorageType; 22 | readonly storageWait?: number; 23 | readonly stores?: any[]; 24 | readonly title?: string; 25 | } 26 | 27 | export interface FluxAction { 28 | readonly [key: string]: any; 29 | readonly type: string; 30 | } 31 | 32 | export interface FluxStorageType { 33 | readonly getStorageData: (key: string) => Promise; 34 | readonly setStorageData: (key: string, value: any) => Promise; 35 | } 36 | 37 | export interface FluxStore { 38 | readonly action: (type: string, data: any, state: any) => any; 39 | readonly name: string; 40 | readonly initialState: any; 41 | } 42 | 43 | export type FluxPluginMethodType = (action: FluxAction, store: object, appData?: object) => Promise; 44 | 45 | export interface FluxMiddlewareType { 46 | readonly name: string; 47 | readonly preDispatch?: FluxPluginMethodType; 48 | readonly postDispatch?: FluxPluginMethodType; 49 | } 50 | 51 | export interface FluxPluginType { 52 | readonly name: string; 53 | readonly method: FluxPluginMethodType; 54 | } 55 | 56 | export interface ErrorConstructor { 57 | captureStackTrace(thisArg: any, func: any): void 58 | } 59 | -------------------------------------------------------------------------------- /packages/arkhamjs/src/constants/ArkhamConstants.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2018-Present, Nitrogen Labs, Inc. 3 | * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. 4 | */ 5 | 6 | export class ArkhamConstants { 7 | static readonly GO_BACK: string = 'ARKHAM_GO_BACK'; 8 | static readonly GO_REPLACE: string = 'ARKHAM_GO_REPLACE'; 9 | static readonly GOTO: string = 'ARKHAM_GOTO'; 10 | static readonly INIT: string = 'ARKHAM_INIT'; 11 | static readonly UPDATE_TITLE: string = 'ARKHAM_UPDATE_TITLE'; 12 | static readonly UPDATE_VIEW: string = 'ARKHAM_UPDATE_VIEW'; 13 | } 14 | -------------------------------------------------------------------------------- /packages/arkhamjs/src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2018-Present, Nitrogen Labs, Inc. 3 | * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. 4 | */ 5 | import {ArkhamConstants} from './constants/ArkhamConstants'; 6 | import {Flux, FluxFramework} from './Flux/Flux'; 7 | 8 | export * from './Flux/Flux.types'; 9 | export {ArkhamConstants, Flux, FluxFramework}; 10 | -------------------------------------------------------------------------------- /packages/arkhamjs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": false, 4 | "allowSyntheticDefaultImports": true, 5 | "baseUrl": "./src", 6 | "declaration": true, 7 | "emitDeclarationOnly": true, 8 | "esModuleInterop": true, 9 | "inlineSourceMap": true, 10 | "jsx": "react-jsx", 11 | "lib": [ 12 | "esnext", 13 | "dom" 14 | ], 15 | "module": "commonjs", 16 | "moduleResolution": "node", 17 | "noImplicitReturns": true, 18 | "noImplicitThis": true, 19 | "noUnusedLocals": true, 20 | "outDir": "lib", 21 | "pretty": true, 22 | "skipLibCheck": true, 23 | "target": "ES2018", 24 | }, 25 | "include": [ 26 | "./src/**/*" 27 | ], 28 | "exclude": [ 29 | "**/*.test.*", 30 | "lib", 31 | "./node_modules", 32 | "./test" 33 | ] 34 | } 35 | --------------------------------------------------------------------------------