├── .editorconfig ├── .gitignore ├── .travis.yml ├── .vscode ├── launch.json └── settings.json ├── CNAME ├── LICENSE ├── README.md ├── core ├── .eslintignore ├── .eslintrc.js ├── README.md ├── jest.config.js ├── package.json ├── project.json ├── replace-in-typedoc.js ├── rollup.config.js ├── src │ ├── __tests__ │ │ ├── Array.tsx │ │ ├── Complex.tsx │ │ ├── Effect.tsx │ │ ├── Error.tsx │ │ ├── Extension.tsx │ │ ├── Merge.tsx │ │ ├── Object.tsx │ │ ├── Primitive.tsx │ │ ├── Promised.tsx │ │ ├── Scoped.tsx │ │ └── Typing.tsx │ ├── index.ts │ ├── is-shallow-equal.ts │ └── react-app-env.d.ts ├── tsconfig.json └── tsconfig.prod.json ├── docs ├── demos │ ├── strictmode │ │ ├── .gitignore │ │ ├── README.md │ │ ├── hookstate-performance-demo.png │ │ ├── package.json │ │ ├── project.json │ │ ├── public │ │ │ ├── favicon.ico │ │ │ ├── index.html │ │ │ ├── logo192.png │ │ │ ├── logo512.png │ │ │ ├── manifest.json │ │ │ └── robots.txt │ │ ├── src │ │ │ ├── App.css │ │ │ ├── App.test.tsx │ │ │ ├── App.tsx │ │ │ ├── index.css │ │ │ ├── index.tsx │ │ │ ├── logo.svg │ │ │ ├── react-app-env.d.ts │ │ │ ├── reportWebVitals.ts │ │ │ └── setupTests.ts │ │ └── tsconfig.json │ └── todolist │ │ ├── LICENSE │ │ ├── README.md │ │ ├── package.json │ │ ├── project.json │ │ ├── public │ │ ├── favicon-192.png │ │ ├── favicon-32.png │ │ ├── index.html │ │ ├── manifest.json │ │ └── robots.txt │ │ ├── src │ │ ├── App.tsx │ │ ├── components │ │ │ ├── SettingsState.ts │ │ │ ├── SettingsViewer.tsx │ │ │ ├── TasksState.ts │ │ │ ├── TasksTotal.tsx │ │ │ └── TasksViewer.tsx │ │ ├── index.css │ │ ├── index.tsx │ │ ├── react-app-env.d.ts │ │ └── serviceWorker.ts │ │ ├── tsconfig.json │ │ └── tsconfig.lib.json ├── index │ ├── .gitignore │ ├── .netlify-headers │ ├── .netlify-redirects │ ├── README.md │ ├── babel.config.js │ ├── blog │ │ ├── 2020-03-10-new-hookstate-website.md │ │ ├── 2020-05-29-cool-performance-demo-from-moonpiano.md │ │ └── 2020-07-17-hookstate-docsearch.md │ ├── docs │ │ ├── 01-getting-started.md │ │ ├── 02-global-state.md │ │ ├── 03-local-state.md │ │ ├── 04-nested-state.md │ │ ├── 04b-scoped-state.md │ │ ├── 04c-nullable-state.md │ │ ├── 05-async-state.md │ │ ├── 06-recursive-state.md │ │ ├── 07-exporting-state.md │ │ ├── 08-using-without-statehook.md │ │ ├── 09-state-with-use-effect.md │ │ ├── 10-migrating-to-v4.md │ │ ├── 11-server-side-rendering.md │ │ ├── 12-using-with-non-json.md │ │ ├── 21-performance-intro.md │ │ ├── 22-performance-large-state.md │ │ ├── 23-performance-batched-updates.md │ │ ├── 23-performance-frequent-updates.md │ │ ├── 24-performance-preact.md │ │ ├── 30x-writing-an-extension.md │ │ ├── 31-extensions-overview.md │ │ ├── 32-extensions-snapshotable.md │ │ ├── 35-extensions-validation.md │ │ ├── 36-extensions-localstored.md │ │ ├── 37-extensions-identifiable.md │ │ ├── 38-extensions-broadcasted.md │ │ ├── 39-extensions-subscribable.md │ │ ├── 40-devtools-overview.md │ │ ├── 50-exceptions.md │ │ └── typedoc-hookstate-core.md │ ├── docusaurus.config.js │ ├── package.json │ ├── project.json │ ├── sidebars.js │ ├── src │ │ ├── PreviewSample.tsx │ │ ├── css │ │ │ └── custom.css │ │ ├── examples │ │ │ ├── Index.tsx │ │ │ ├── global-getting-started-interface.tsx │ │ │ ├── global-getting-started.tsx │ │ │ ├── global-multiple-consumers-statefragment.tsx │ │ │ ├── local-async-state.tsx │ │ │ ├── local-complex-from-documentation.tsx │ │ │ ├── local-complex-tree-structure.tsx │ │ │ ├── local-getting-started.tsx │ │ │ ├── performance-demo-large-form.tsx │ │ │ ├── plugin-broadcasted.tsx │ │ │ ├── plugin-custom.tsx │ │ │ ├── plugin-identifiable.tsx │ │ │ ├── plugin-localstored.tsx │ │ │ ├── plugin-snapshotable.tsx │ │ │ ├── plugin-subscribable.tsx │ │ │ ├── plugin-validation.tsx │ │ │ └── with-use-effect.tsx │ │ └── pages │ │ │ ├── index.js │ │ │ └── styles.module.css │ ├── static │ │ ├── BingSiteAuth.xml │ │ └── img │ │ │ ├── blog │ │ │ └── 2020-07-17-picture.png │ │ │ ├── favicon-128.png │ │ │ ├── favicon-152.png │ │ │ ├── favicon-167.png │ │ │ ├── favicon-180.png │ │ │ ├── favicon-192.png │ │ │ ├── favicon-196.png │ │ │ ├── favicon-32.png │ │ │ ├── favicon.ico │ │ │ └── logo.svg │ └── tsconfig.json ├── logo-square.gvdesign └── logo.gvdesign ├── jest.config.js ├── netlify.toml ├── nodemon.json ├── nx.json ├── package.json ├── plugins ├── broadcasted │ ├── LICENSE │ ├── README.md │ ├── jest.config.js │ ├── package.json │ ├── project.json │ ├── rollup.config.js │ ├── src │ │ ├── broadcasted.ts │ │ ├── index.ts │ │ └── unit.test.ts │ ├── tsconfig.json │ └── tsconfig.prod.json ├── clonable │ ├── LICENSE │ ├── README.md │ ├── jest.config.js │ ├── package.json │ ├── project.json │ ├── rollup.config.js │ ├── src │ │ ├── clonable.ts │ │ ├── index.ts │ │ └── unit.test.ts │ ├── tsconfig.json │ └── tsconfig.prod.json ├── comparable │ ├── LICENSE │ ├── README.md │ ├── jest.config.js │ ├── package.json │ ├── project.json │ ├── rollup.config.js │ ├── src │ │ ├── comparable.ts │ │ ├── index.ts │ │ └── unit.test.ts │ ├── tsconfig.json │ └── tsconfig.prod.json ├── devtools │ ├── .env │ ├── LICENSE │ ├── README.md │ ├── jest.config.js │ ├── package.json │ ├── project.json │ ├── rollup.config.js │ ├── src │ │ ├── devtools.ts │ │ ├── index.ts │ │ └── unit.test.ts │ ├── tsconfig.json │ └── tsconfig.prod.json ├── identifiable │ ├── LICENSE │ ├── README.md │ ├── jest.config.js │ ├── package.json │ ├── project.json │ ├── rollup.config.js │ ├── src │ │ ├── identifiable.ts │ │ ├── index.ts │ │ └── unit.test.ts │ ├── tsconfig.json │ └── tsconfig.prod.json ├── initializable │ ├── LICENSE │ ├── README.md │ ├── jest.config.js │ ├── package.json │ ├── project.json │ ├── rollup.config.js │ ├── src │ │ ├── index.ts │ │ ├── initializable.ts │ │ └── unit.test.ts │ ├── tsconfig.json │ └── tsconfig.prod.json ├── localstored │ ├── LICENSE │ ├── README.md │ ├── jest.config.js │ ├── package.json │ ├── project.json │ ├── rollup.config.js │ ├── src │ │ ├── index.ts │ │ ├── localstorage.ts │ │ └── unit.test.ts │ ├── tsconfig.json │ └── tsconfig.prod.json ├── logged │ ├── LICENSE │ ├── README.md │ ├── jest.config.js │ ├── package.json │ ├── project.json │ ├── rollup.config.js │ ├── src │ │ ├── index.ts │ │ ├── logged.ts │ │ └── unit.test.ts │ ├── tsconfig.json │ └── tsconfig.prod.json ├── serializable │ ├── LICENSE │ ├── README.md │ ├── jest.config.js │ ├── package.json │ ├── project.json │ ├── rollup.config.js │ ├── src │ │ ├── index.ts │ │ ├── serializable.ts │ │ └── unit.test.ts │ ├── tsconfig.json │ └── tsconfig.prod.json ├── snapshotable │ ├── LICENSE │ ├── README.md │ ├── jest.config.js │ ├── package.json │ ├── project.json │ ├── rollup.config.js │ ├── src │ │ ├── index.ts │ │ ├── snapshotable.ts │ │ └── unit.test.ts │ ├── tsconfig.json │ └── tsconfig.prod.json ├── subscribable │ ├── LICENSE │ ├── README.md │ ├── jest.config.js │ ├── package.json │ ├── project.json │ ├── rollup.config.js │ ├── src │ │ ├── index.ts │ │ ├── subscribable.ts │ │ └── unit.test.ts │ ├── tsconfig.json │ └── tsconfig.prod.json └── validation │ ├── LICENSE │ ├── README.md │ ├── jest.config.js │ ├── package.json │ ├── project.json │ ├── rollup.config.js │ ├── src │ ├── index.ts │ ├── unit.test.ts │ └── validation.ts │ ├── tsconfig.json │ └── tsconfig.prod.json ├── pnpm-lock.yaml ├── pnpm-workspace.yaml └── workspace.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 4 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | end_of_line = lf 11 | 12 | [{*.ts,*.tsx,*.js,*.jsx,*.css,*scss,*.graphql}] 13 | indent_size = 4 14 | 15 | [*.md] 16 | max_line_length = off 17 | trim_trailing_whitespace = false 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/node_modules 2 | **/node_modules.bak 3 | **/coverage 4 | 5 | npm-debug.log* 6 | yarn-debug.log* 7 | yarn-error.log* 8 | 9 | .DS_Store 10 | 11 | **/.idea 12 | **/.netlify 13 | **/.firebase 14 | 15 | **/build 16 | **/dist 17 | .pnpm-debug.log 18 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: focal 2 | language: node_js 3 | node_js: 4 | - 18 5 | cache: 6 | npm: false 7 | directories: 8 | - "~/.pnpm-store" 9 | before_install: 10 | - curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm@7.18.2 11 | - pnpm config set store-dir ~/.pnpm-store 12 | install: 13 | - pnpm install 14 | scripts: 15 | - pnpm test:ci 16 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "configurations": [ 4 | { 5 | "name": "Test Core", 6 | "request": "launch", 7 | "runtimeArgs": [ 8 | "nx", 9 | "test", 10 | "core" 11 | ], 12 | "runtimeExecutable": "pnpm", 13 | "skipFiles": [ 14 | "/**" 15 | ], 16 | "type": "node" 17 | } 18 | ] 19 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "peacock.color": "#42b883", 3 | "workbench.colorCustomizations": { 4 | "activityBar.background": "#65c89b", 5 | "activityBar.activeBackground": "#65c89b", 6 | "activityBar.activeBorder": "#945bc4", 7 | "activityBar.foreground": "#15202b", 8 | "activityBar.inactiveForeground": "#15202b99", 9 | "activityBarBadge.background": "#945bc4", 10 | "activityBarBadge.foreground": "#e7e7e7", 11 | "titleBar.activeBackground": "#42b883", 12 | "titleBar.inactiveBackground": "#42b88399", 13 | "titleBar.activeForeground": "#15202b", 14 | "titleBar.inactiveForeground": "#15202b99", 15 | "statusBar.background": "#42b883", 16 | "statusBarItem.hoverBackground": "#359268", 17 | "statusBar.foreground": "#15202b", 18 | "sash.hoverBorder": "#65c89b", 19 | "statusBarItem.remoteBackground": "#42b883", 20 | "statusBarItem.remoteForeground": "#15202b", 21 | "commandCenter.border": "#15202b99" 22 | }, 23 | "peacock.remoteColor": "#42b883", 24 | "cSpell.words": [ 25 | "core", 26 | "fullfilled", 27 | "hookstate", 28 | "unmount", 29 | "untracked" 30 | ], 31 | "jest.jestCommandLine": "pnpm jest" 32 | } -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | hookstate.js.org -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2022 Andrey Konstantinov 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 | -------------------------------------------------------------------------------- /core/.eslintignore: -------------------------------------------------------------------------------- 1 | src/__tests__ 2 | dist 3 | node_modules -------------------------------------------------------------------------------- /core/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parser: '@typescript-eslint/parser', 4 | plugins: [ 5 | '@typescript-eslint', 6 | ], 7 | extends: [ 8 | 'eslint:recommended', 9 | 'plugin:@typescript-eslint/recommended', 10 | ], 11 | }; -------------------------------------------------------------------------------- /core/README.md: -------------------------------------------------------------------------------- 1 |

2 | Hookstate 3 |

4 | 5 |

6 | The most straightforward, extensible and incredibly fast state management that is based on React state hook. 7 |

8 |
9 | 10 |

11 | Why? • 12 | Docs / Samples • 13 | Demo application • 14 | Extensions • 15 | Release notes 16 |

17 | 18 |

19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 |

41 | 42 | ## Support 43 | 44 | **Any questions? Just ask by raising a github ticket.** 45 | -------------------------------------------------------------------------------- /core/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'ts-jest', 3 | testEnvironment: 'node' 4 | }; -------------------------------------------------------------------------------- /core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hookstate/core", 3 | "version": "4.0.2", 4 | "description": "The flexible, fast and extendable state management for React that is based on hooks and state usage tracking.", 5 | "license": "MIT", 6 | "author": { 7 | "name": "Andrey Konstantinov" 8 | }, 9 | "repository": { 10 | "url": "https://github.com/avkonst/hookstate" 11 | }, 12 | "bugs": { 13 | "url": "https://github.com/avkonst/hookstate/issues" 14 | }, 15 | "homepage": "https://github.com/avkonst/hookstate", 16 | "main": "dist/index.js", 17 | "module": "dist/index.es.js", 18 | "jsnext:main": "dist/index.es.js", 19 | "types": "dist/index.d.ts", 20 | "scripts": { 21 | "build": "rollup -c", 22 | "build:w": "rollup -c -w", 23 | "build:docs": "typedoc --plugin typedoc-plugin-markdown --hideBreadcrumbs --tsconfig ./tsconfig.json --exclude \"dist/**.js\" --gitRevision master --excludeExternals --categorizeByGroup false --readme none --hideGenerator --out dist/docs ./src/index.ts && concat-md --decrease-title-levels --dir-name-as-title dist/docs > dist/typedoc.md && rimraf dist/docs && node ./replace-in-typedoc.js && mv dist/typedoc.md ../docs/index/docs/typedoc-hookstate-core.md", 24 | "clean": "rimraf dist", 25 | "test": "jest --env=jsdom", 26 | "test:ci": "jest --env=jsdom --coverage && codecov -e TRAVIS_NODE_VERSION", 27 | "test:w": "jest --env=jsdom --watch", 28 | "release": "npm publish --access public", 29 | "update:deps": "ncu -u" 30 | }, 31 | "peerDependencies": { 32 | "react": "^16.8.6 || ^17.0.0 || ^18.0.0" 33 | }, 34 | "devDependencies": { 35 | "@babel/core": "7.18.6", 36 | "@babel/runtime": "7.18.6", 37 | "@rollup/plugin-babel": "5.3.1", 38 | "@rollup/plugin-commonjs": "22.0.1", 39 | "@rollup/plugin-node-resolve": "13.3.0", 40 | "@rollup/plugin-url": "7.0.0", 41 | "@testing-library/react": "13.3.0", 42 | "@testing-library/react-hooks": "8.0.1", 43 | "@types/jest": "28.1.4", 44 | "@types/node": "^18.0.3", 45 | "@types/react": "18.0.15", 46 | "@types/react-dom": "18.0.6", 47 | "codecov": "3.8.3", 48 | "concat-md": "0.4.0", 49 | "coverage": "0.4.1", 50 | "cross-env": "7.0.3", 51 | "jest": "28.1.2", 52 | "jest-environment-jsdom": "28.1.2", 53 | "npm-check-updates": "15.2.1", 54 | "react": "18.2.0", 55 | "react-dom": "18.2.0", 56 | "react-test-renderer": "18.2.0", 57 | "replace": "1.2.1", 58 | "rimraf": "3.0.2", 59 | "rollup": "2.76.0", 60 | "rollup-plugin-peer-deps-external": "2.2.4", 61 | "rollup-plugin-typescript2": "0.32.1", 62 | "ts-jest": "28.0.5", 63 | "tslib": "^2.4.0", 64 | "typedoc": "0.23.6", 65 | "typedoc-plugin-markdown": "3.13.3", 66 | "typescript": "4.7.4" 67 | }, 68 | "files": [ 69 | "dist" 70 | ] 71 | } 72 | -------------------------------------------------------------------------------- /core/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": "core", 3 | "tags": [] 4 | } 5 | -------------------------------------------------------------------------------- /core/replace-in-typedoc.js: -------------------------------------------------------------------------------- 1 | let replace = require('replace') 2 | 3 | replace({ 4 | regex: '# @hookstate/core', 5 | replacement: "", 6 | paths: ['dist/typedoc.md'], 7 | recursive: false, 8 | silent: false, 9 | }) 10 | 11 | // 'Ƭ [*][*]([A-Za-z0-9]+)[*][*]: [*](.*)[*]' 'Ƭ **$1**: *`$2`*' dist/typedoc.md && replace 'Ƭ [*][*]State[*][*]: [*](.*)[*]' 'Ƭ **State**: *[StateMixin](#interfacesstatemixinmd) & `S extends object` ? `{ readonly [K in keyof Required]: State }` : [StateMethods](#interfacesstatemethodsmd)*' dist/typedoc.md && replace '[(]statemethods.md#\\[self\\][)]' '(#self)' dist/typedoc.md && replace '[(]statemixin.md#\\[self\\][)]' '(#self-1)' dist/typedoc.md && replace '[(]statemixindestroy.md#\\[self\\][)]' '(#self-2)' dist/typedoc.md && replace '# @hookstate/core' '' dist/typedoc.md && replace '' '\n---\nid: typedoc-hookstate-core\ntitle: API @hookstate/core\n---' dist/typedoc.md && replace '\n\n(---)' '$1' dist/typedoc.md && mv dist/typedoc.md ../docs/index/docs/typedoc-hookstate-core.md", 12 | 13 | replace({ 14 | regex: '>', 15 | replacement: "/>", 16 | paths: ['dist/typedoc.md'], 17 | recursive: false, 18 | silent: false, 19 | }) 20 | 21 | 22 | replace({ 23 | regex: '', 24 | replacement: "\n---\nid: typedoc-hookstate-core\ntitle: API @hookstate/core\n---", 25 | paths: ['dist/typedoc.md'], 26 | recursive: false, 27 | silent: false, 28 | }) 29 | 30 | replace({ 31 | regex: '\n\n(---)', 32 | replacement: "$1", 33 | paths: ['dist/typedoc.md'], 34 | recursive: false, 35 | silent: false, 36 | }) 37 | 38 | replace({ 39 | regex: '[(][a-zA-Z_]+[.]md#', 40 | replacement: "(#", 41 | paths: ['dist/typedoc.md'], 42 | recursive: false, 43 | silent: false, 44 | }) 45 | -------------------------------------------------------------------------------- /core/rollup.config.js: -------------------------------------------------------------------------------- 1 | import typescript from 'rollup-plugin-typescript2' 2 | import commonjs from '@rollup/plugin-commonjs' 3 | import external from 'rollup-plugin-peer-deps-external' 4 | import resolve from '@rollup/plugin-node-resolve' 5 | import url from '@rollup/plugin-url' 6 | 7 | import pkg from './package.json' 8 | 9 | export default { 10 | input: 'src/index.ts', 11 | output: [ 12 | { 13 | file: pkg.main, 14 | format: 'cjs', 15 | exports: 'named', 16 | sourcemap: true 17 | }, 18 | { 19 | file: pkg.module, 20 | format: 'es', 21 | exports: 'named', 22 | sourcemap: true 23 | } 24 | ], 25 | plugins: [ 26 | external(), 27 | url(), 28 | resolve(), 29 | typescript({ 30 | tsconfig: 'tsconfig.prod.json', 31 | rollupCommonJSResolveHack: true, 32 | clean: true 33 | }), 34 | commonjs() 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /core/src/__tests__/Error.tsx: -------------------------------------------------------------------------------- 1 | import { useHookstate } from '../'; 2 | 3 | import { renderHook } from '@testing-library/react-hooks'; 4 | 5 | test('error: should not allow set to another state value', async () => { 6 | const state1 = renderHook(() => { 7 | return useHookstate({ prop1: [0, 0] }) 8 | }); 9 | 10 | const state2 = renderHook(() => { 11 | return useHookstate({ prop2: [0, 0] }) 12 | }); 13 | 14 | expect(() => { 15 | state2.result.current.prop2.set(p => state1.result.current.get().prop1); 16 | // tslint:disable-next-line: max-line-length 17 | }).toThrow(`Error: HOOKSTATE-102 [path: /prop2]. See https://hookstate.js.org/docs/exceptions#hookstate-102`); 18 | }); 19 | 20 | test('error: should not allow create state from another state value', async () => { 21 | const state1 = renderHook(() => { 22 | return useHookstate({ prop1: [0, 0] }) 23 | }); 24 | 25 | const state2 = renderHook(() => { 26 | return useHookstate(state1.result.current.get().prop1) 27 | }) 28 | 29 | expect(state2.result.error?.message) 30 | // tslint:disable-next-line: max-line-length 31 | .toEqual(`Error: HOOKSTATE-101 [path: /]. See https://hookstate.js.org/docs/exceptions#hookstate-101`) 32 | }); 33 | 34 | test('error: should not allow create state from another state value (nested)', async () => { 35 | const state1 = renderHook(() => { 36 | return useHookstate({ prop1: [0, 0] }) 37 | }); 38 | 39 | const state2 = renderHook(() => { 40 | return useHookstate(state1.result.current) 41 | }) 42 | 43 | const state3 = renderHook(() => { 44 | return useHookstate(state2.result.current.prop1.get()) 45 | }) 46 | 47 | expect(state3.result.error?.message) 48 | // tslint:disable-next-line: max-line-length 49 | .toEqual(`Error: HOOKSTATE-101 [path: /]. See https://hookstate.js.org/docs/exceptions#hookstate-101`) 50 | }); 51 | 52 | test('error: should not allow serialization of statelink', async () => { 53 | const state1 = renderHook(() => { 54 | return useHookstate({ prop1: [0, 0] }) 55 | }); 56 | 57 | expect(() => JSON.stringify(state1)) 58 | .toThrow('Error: HOOKSTATE-109 [path: /]. See https://hookstate.js.org/docs/exceptions#hookstate-109') 59 | }); 60 | -------------------------------------------------------------------------------- /core/src/__tests__/Typing.tsx: -------------------------------------------------------------------------------- 1 | import { hookstate, none, State, useHookstate } from '../'; 2 | 3 | import { renderHook, act } from '@testing-library/react-hooks'; 4 | 5 | test('check assignability on typescript level', async () => { 6 | { 7 | // assign state to state of reduced value 8 | let a = hookstate<{ a: string, b?: string }>({ a: 'a', b: 'b' }) 9 | let b: State<{ a: string }> = a; 10 | } 11 | 12 | { 13 | //assign state with extension method to a state without 14 | interface Inf { 15 | m(): void, 16 | } 17 | let a = hookstate<{ a: string, b?: string }, Inf>({ a: 'a', b: 'b' }, () => ({ 18 | onCreate: () => ({ 19 | m: () => () => {} 20 | }) 21 | })) 22 | let b: State<{ a: string }> = a; 23 | } 24 | }); -------------------------------------------------------------------------------- /core/src/is-shallow-equal.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copied from fbjs is-shallow-equal 3 | */ 4 | 5 | const hasOwnProperty = Object.prototype.hasOwnProperty; 6 | 7 | /** 8 | * inlined Object.is polyfill to avoid requiring consumers ship their own 9 | * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is 10 | */ 11 | function is(x: any, y: any): boolean { 12 | // SameValue algorithm 13 | if (x === y) { // Steps 1-5, 7-10 14 | // Steps 6.b-6.e: +0 != -0 15 | // Added the nonzero y check to make Flow happy, but it is redundant 16 | return x !== 0 || y !== 0 || 1 / x === 1 / y; 17 | } else { 18 | // Step 6.a: NaN == NaN 19 | return x !== x && y !== y; 20 | } 21 | } 22 | 23 | /** 24 | * Performs equality by iterating through keys on an object and returning false 25 | * when any key has values which are not strictly equal between the arguments. 26 | * Returns true when the values of all keys are strictly equal. 27 | */ 28 | export function shallowEqual(objA: any, objB: any): boolean { 29 | if (is(objA, objB)) { 30 | return true; 31 | } 32 | 33 | if (typeof objA !== 'object' || objA === null || 34 | typeof objB !== 'object' || objB === null) { 35 | return false; 36 | } 37 | 38 | const keysA = Object.keys(objA); 39 | const keysB = Object.keys(objB); 40 | 41 | if (keysA.length !== keysB.length) { 42 | return false; 43 | } 44 | 45 | // Test for A's keys different from B. 46 | for (let i = 0; i < keysA.length; i++) { 47 | if ( 48 | !hasOwnProperty.call(objB, keysA[i]) || 49 | !is(objA[keysA[i]], objB[keysA[i]]) 50 | ) { 51 | return false; 52 | } 53 | } 54 | 55 | return true; 56 | } -------------------------------------------------------------------------------- /core/src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /core/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "dist", 4 | "target": "es5", 5 | "lib": [ 6 | "dom", 7 | "dom.iterable", 8 | "esnext" 9 | ], 10 | "sourceMap": true, 11 | "noImplicitReturns": true, 12 | "noImplicitThis": true, 13 | "noImplicitAny": true, 14 | "strictNullChecks": true, 15 | "noUnusedLocals": false, 16 | "experimentalDecorators": true, 17 | "newLine": "LF", 18 | "allowJs": true, 19 | "skipLibCheck": true, 20 | "esModuleInterop": true, 21 | "allowSyntheticDefaultImports": true, 22 | "strict": true, 23 | "forceConsistentCasingInFileNames": true, 24 | "suppressImplicitAnyIndexErrors": true, 25 | "module": "esnext", 26 | "moduleResolution": "node", 27 | "resolveJsonModule": true, 28 | "isolatedModules": true, 29 | "noEmit": false, 30 | "jsx": "preserve" 31 | }, 32 | "include": [ 33 | "src", "src/__tests__" 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /core/tsconfig.prod.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": [ 4 | "src" 5 | ], 6 | "compilerOptions": { 7 | "lib": ["DOM"], 8 | "jsx": "react", 9 | "allowJs": false, 10 | "declaration": true, 11 | "declarationMap": true, 12 | "isolatedModules": false 13 | }, 14 | "exclude": [ 15 | "src/__tests__" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /docs/demos/strictmode/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /docs/demos/strictmode/README.md: -------------------------------------------------------------------------------- 1 | # Strict mode and performance application demo 2 | 3 | Frequent state updates demo. 10K cells table updating 1 cell every millisecond(*). 4 | 5 | * - results maybe different depending on a machine performance 6 | 7 | ## Workflow 8 | 9 | From the repository root directory: 10 | 11 | - `pnpm install` 12 | - `pnpm nx start strictmode` - slow performance demo, for debugging only 13 | - `pnpm nx build strictmode` - build in release mode 14 | - `pnpm nx serve strictmode` - open in browser and see it running 15 | 16 | ## Screenshot 17 | 18 | ![frequent state updates](hookstate-performance-demo.png "Hookstate frequent state updates demo") 19 | -------------------------------------------------------------------------------- /docs/demos/strictmode/hookstate-performance-demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avkonst/hookstate/e3118d674a4727c60fc3c682008211b44774f187/docs/demos/strictmode/hookstate-performance-demo.png -------------------------------------------------------------------------------- /docs/demos/strictmode/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hookstate/strictmode", 3 | "version": "4.0.0-rc3", 4 | "private": true, 5 | "dependencies": { 6 | "@hookstate/core": "workspace:*", 7 | "@testing-library/jest-dom": "^5.16.4", 8 | "@testing-library/react": "^13.3.0", 9 | "@testing-library/user-event": "^14.2.1", 10 | "@types/jest": "^28.1.4", 11 | "@types/node": "^18.0.3", 12 | "@types/react": "^18.0.15", 13 | "@types/react-dom": "^18.0.6", 14 | "npm-check-updates": "15.2.1", 15 | "react": "^18.2.0", 16 | "react-dom": "^18.2.0", 17 | "react-scripts": "5.0.1", 18 | "serve": "14.1.2", 19 | "typescript": "^4.7.4", 20 | "web-vitals": "^2.1.4" 21 | }, 22 | "scripts": { 23 | "start": "react-scripts start", 24 | "build": "react-scripts build", 25 | "serve": "serve -s build", 26 | "test": "react-scripts test", 27 | "eject": "react-scripts eject", 28 | "update:deps": "ncu -u" 29 | }, 30 | "eslintConfig": { 31 | "extends": [ 32 | "react-app", 33 | "react-app/jest" 34 | ] 35 | }, 36 | "browserslist": { 37 | "production": [ 38 | ">0.2%", 39 | "not dead", 40 | "not op_mini all" 41 | ], 42 | "development": [ 43 | "last 1 chrome version", 44 | "last 1 firefox version", 45 | "last 1 safari version" 46 | ] 47 | } 48 | } -------------------------------------------------------------------------------- /docs/demos/strictmode/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": "docs/demos/strictmode", 3 | "tags": [] 4 | } 5 | -------------------------------------------------------------------------------- /docs/demos/strictmode/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avkonst/hookstate/e3118d674a4727c60fc3c682008211b44774f187/docs/demos/strictmode/public/favicon.ico -------------------------------------------------------------------------------- /docs/demos/strictmode/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /docs/demos/strictmode/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avkonst/hookstate/e3118d674a4727c60fc3c682008211b44774f187/docs/demos/strictmode/public/logo192.png -------------------------------------------------------------------------------- /docs/demos/strictmode/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avkonst/hookstate/e3118d674a4727c60fc3c682008211b44774f187/docs/demos/strictmode/public/logo512.png -------------------------------------------------------------------------------- /docs/demos/strictmode/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /docs/demos/strictmode/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /docs/demos/strictmode/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /docs/demos/strictmode/src/App.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render, screen } from '@testing-library/react'; 3 | import { App } from './App'; 4 | 5 | test('renders learn react link', () => { 6 | render(); 7 | }); 8 | -------------------------------------------------------------------------------- /docs/demos/strictmode/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /docs/demos/strictmode/src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import './index.css'; 4 | import { App } from './App'; 5 | import reportWebVitals from './reportWebVitals'; 6 | 7 | const root = ReactDOM.createRoot( 8 | document.getElementById('root') as HTMLElement 9 | ); 10 | root.render( 11 | 12 | 13 | 14 | ); 15 | 16 | // If you want to start measuring performance in your app, pass a function 17 | // to log results (for example: reportWebVitals(console.log)) 18 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 19 | reportWebVitals(); 20 | -------------------------------------------------------------------------------- /docs/demos/strictmode/src/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/demos/strictmode/src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /docs/demos/strictmode/src/reportWebVitals.ts: -------------------------------------------------------------------------------- 1 | import { ReportHandler } from 'web-vitals'; 2 | 3 | const reportWebVitals = (onPerfEntry?: ReportHandler) => { 4 | if (onPerfEntry && onPerfEntry instanceof Function) { 5 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 6 | getCLS(onPerfEntry); 7 | getFID(onPerfEntry); 8 | getFCP(onPerfEntry); 9 | getLCP(onPerfEntry); 10 | getTTFB(onPerfEntry); 11 | }); 12 | } 13 | }; 14 | 15 | export default reportWebVitals; 16 | -------------------------------------------------------------------------------- /docs/demos/strictmode/src/setupTests.ts: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /docs/demos/strictmode/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "noFallthroughCasesInSwitch": true, 16 | "module": "esnext", 17 | "moduleResolution": "node", 18 | "resolveJsonModule": true, 19 | "isolatedModules": true, 20 | "noEmit": true, 21 | "jsx": "react-jsx" 22 | }, 23 | "include": [ 24 | "src" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /docs/demos/todolist/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Andrey 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 | -------------------------------------------------------------------------------- /docs/demos/todolist/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Hookstate Sample Application 3 | 4 | This is an example application, which uses the core library of [Hookstate](https://github.com/avkonst/hookstate) to manage various types of states. 5 | 6 | This project can be used as a 'starter repo'. 7 | The source code demonstrates the usage of Hookstate in more realistic, large application than shorter [code samples and demos available online](https://hookstate.js.org/). 8 | We advise you to look through the shorter samples online, if you discovered the Hookstate for the first time, before studying this application. 9 | 10 | [See this application running online](https://hookstate.js.org/docs/getting-started) 11 | 12 | ## Workflow 13 | 14 | From the repository root directory: 15 | 16 | - `pnpm install` 17 | - `pnpm nx build todolist` 18 | - `pnpm nx start todolist` 19 | - `pnpm nx test todolist` 20 | -------------------------------------------------------------------------------- /docs/demos/todolist/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hookstate/todolist", 3 | "version": "4.0.0-rc3", 4 | "private": true, 5 | "homepage": ".", 6 | "main": "build/App.js", 7 | "module": "build/App.es.js", 8 | "jsnext:main": "build/App.es.js", 9 | "types": "build/App.d.ts", 10 | "dependencies": { 11 | "@hookstate/core": "workspace:*", 12 | "@hookstate/devtools": "workspace:*", 13 | "@types/jest": "28.1.4", 14 | "@types/node": "18.0.3", 15 | "@types/react": "18.0.15", 16 | "@types/react-dom": "18.0.6", 17 | "npm-check-updates": "^15.2.1", 18 | "react": "18.2.0", 19 | "react-dom": "18.2.0", 20 | "react-scripts": "5.0.1", 21 | "tslib": "^2.4.0", 22 | "typescript": "4.7.4", 23 | "serve": "13.0.4" 24 | }, 25 | "scripts": { 26 | "start": "react-scripts start", 27 | "build": "tsc -p tsconfig.lib.json", 28 | "build:app": "react-scripts build", 29 | "serve": "serve build", 30 | "update:deps": "ncu -u" 31 | }, 32 | "browserslist": { 33 | "production": [ 34 | ">0.2%", 35 | "not dead", 36 | "not op_mini all" 37 | ], 38 | "development": [ 39 | "last 1 chrome version", 40 | "last 1 firefox version", 41 | "last 1 safari version" 42 | ] 43 | } 44 | } -------------------------------------------------------------------------------- /docs/demos/todolist/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": "docs/demos/todolist", 3 | "tags": [] 4 | } 5 | -------------------------------------------------------------------------------- /docs/demos/todolist/public/favicon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avkonst/hookstate/e3118d674a4727c60fc3c682008211b44774f187/docs/demos/todolist/public/favicon-192.png -------------------------------------------------------------------------------- /docs/demos/todolist/public/favicon-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avkonst/hookstate/e3118d674a4727c60fc3c682008211b44774f187/docs/demos/todolist/public/favicon-32.png -------------------------------------------------------------------------------- /docs/demos/todolist/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 13 | 14 | 18 | 19 | 28 | Hookstate: Example Application 29 | 30 | 31 | 32 |
33 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/demos/todolist/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon-32.png", 7 | "sizes": "32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "favicon-192.png", 12 | "sizes": "192x192 128x128 64x64", 13 | "type": "image/x-icon" 14 | } 15 | ], 16 | "start_url": ".", 17 | "display": "standalone", 18 | "theme_color": "#000000", 19 | "background_color": "#ffffff" 20 | } 21 | -------------------------------------------------------------------------------- /docs/demos/todolist/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | -------------------------------------------------------------------------------- /docs/demos/todolist/src/App.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { TasksViewer } from './components/TasksViewer'; 3 | import { SettingsViewer } from './components/SettingsViewer'; 4 | import { TasksTotal } from './components/TasksTotal'; 5 | 6 | const App: React.FC = () => { 7 | return ( 8 |
9 |
10 |
18 |
19 |
20 | This is Hookstate demo application. 26 | Source code is on GitHub. 32 |
33 | 34 | 35 | 37 | Loading initial state asynchronously... 38 |
39 | }> 40 | 41 | 42 |
43 |
44 |
45 |
46 | ); 47 | } 48 | 49 | export default App; 50 | -------------------------------------------------------------------------------- /docs/demos/todolist/src/components/SettingsState.ts: -------------------------------------------------------------------------------- 1 | import { hookstate, useHookstate } from '@hookstate/core'; 2 | import { devtools } from '@hookstate/devtools'; 3 | 4 | const settingsState = hookstate({ 5 | isEditableInline: true, 6 | isScopedUpdateEnabled: true, 7 | isHighlightUpdatesEnabled: true 8 | }, devtools({ key: 'settings' })) 9 | 10 | export function useSettingsState() { 11 | const state = useHookstate(settingsState) 12 | 13 | // This function wraps the state by an interface, 14 | // i.e. the state link is not accessible directly outside of this module. 15 | // The state for tasks in TasksState.ts exposes the state directly. 16 | // Both options are valid and you need to use one or another, 17 | // depending on your circumstances. Apply your engineering judgement 18 | // to choose the best option. If unsure, exposing the state directly 19 | // like it is done in the TasksState.ts is a safe bet. 20 | return ({ 21 | get isEditableInline() { 22 | return state.isEditableInline.get() 23 | }, 24 | toggleEditableInline() { 25 | state.isEditableInline.set(p => !p) 26 | }, 27 | get isScopedUpdateEnabled() { 28 | return state.isScopedUpdateEnabled.get() 29 | }, 30 | toggleScopedUpdate() { 31 | state.isScopedUpdateEnabled.set(p => !p) 32 | }, 33 | get isHighlightUpdateEnabled() { 34 | return state.isHighlightUpdatesEnabled.get() 35 | }, 36 | toggleHighlightUpdate() { 37 | state.isHighlightUpdatesEnabled.set(p => !p) 38 | } 39 | }) 40 | } 41 | -------------------------------------------------------------------------------- /docs/demos/todolist/src/components/SettingsViewer.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useSettingsState } from './SettingsState'; 3 | 4 | export function SettingsViewer() { 5 | const settingsState = useSettingsState(); 6 | 7 | return
15 |
16 |
17 | settingsState.toggleEditableInline()} 22 | /> 23 |
24 |
25 | edit inline 26 |
27 |
28 |
29 |
30 | settingsState.toggleScopedUpdate()} 35 | /> 36 |
37 |
38 | use scoped state 39 |
40 |
41 |
42 |
43 | settingsState.toggleHighlightUpdate()} 48 | /> 49 |
50 |
51 | highlight updates 52 |
53 |
54 |
55 | } -------------------------------------------------------------------------------- /docs/demos/todolist/src/components/TasksState.ts: -------------------------------------------------------------------------------- 1 | import { hookstate, useHookstate } from '@hookstate/core'; 2 | import { devtools } from '@hookstate/devtools'; 3 | 4 | export interface Task { 5 | id: string; 6 | name: string; 7 | done: boolean; 8 | } 9 | 10 | const state = hookstate(new Promise((resolve, reject) => { 11 | // Emulate asynchronous loading of the initial state data. 12 | // The real application would run some fetch request, 13 | // to get the initial data from a server. 14 | setTimeout(() => resolve([ 15 | { 16 | id: '1', 17 | name: 'Discover Hookstate', 18 | done: true, 19 | }, { 20 | id: '2', 21 | name: 'Replace Redux by Hookstate', 22 | done: false, 23 | }, { 24 | id: '3', 25 | name: 'Enjoy simpler code and faster application', 26 | done: false, 27 | } 28 | ]), 3000) 29 | }), devtools({ key: 'tasks' })) 30 | 31 | export function useTasksState() { 32 | // This function exposes the state directly. 33 | // i.e. the state is accessible directly outside of this module. 34 | // The state for settings in SettingsState.ts wraps the state by an interface. 35 | // Both options are valid and you need to use one or another, 36 | // depending on your circumstances. Apply your engineering judgement 37 | // to choose the best option. If unsure, exposing the state directly 38 | // like it is done below is a safe bet. 39 | return useHookstate(state) 40 | } 41 | 42 | // for example purposes, let's update the state outside of a React component 43 | setTimeout(() => state[state.length].set({ 44 | id: '100', 45 | name: 'Spread few words about Hookstate', 46 | done: false 47 | }), 10000) -------------------------------------------------------------------------------- /docs/demos/todolist/src/components/TasksTotal.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useTasksState } from './TasksState'; 3 | import { useSettingsState } from './SettingsState'; 4 | 5 | export function TasksTotal() { 6 | // Use both global stores in the same component. 7 | // Note: in fact, it could be even one state object 8 | // with functions accessing different nested segments of the state data. 9 | // It would perform equally well. 10 | const tasksState = useTasksState(); 11 | const settingsState = useSettingsState(); 12 | 13 | // This is the trick to obtain different color on every run of this function 14 | var colors = ['#ff0000', '#00ff00', '#0000ff']; 15 | const color = React.useRef(0) 16 | color.current += 1 17 | var nextColor = colors[color.current % colors.length]; 18 | 19 | return
24 | {settingsState.isHighlightUpdateEnabled && 25 |
32 | } 33 | {tasksState.promised ? 34 | <> : 35 |
40 |
Total tasks: {tasksState.length}
41 |
Done: {tasksState.filter(i => i.done.value).length}
42 |
Remaining: {tasksState.filter(i => !i.done.value).length}
43 |
44 | } 45 |
46 | } -------------------------------------------------------------------------------- /docs/demos/todolist/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | background-color: #282c34; 9 | } 10 | 11 | code { 12 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 13 | monospace; 14 | } 15 | -------------------------------------------------------------------------------- /docs/demos/todolist/src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { createRoot } from 'react-dom/client'; 3 | import './index.css'; 4 | 5 | // optional, defaults are almost always right 6 | import { configure } from '@hookstate/core'; 7 | configure({ 8 | interceptDependencyListsMode: "always", 9 | isDevelopmentMode: process.env.NODE_ENV === 'development' 10 | }) 11 | 12 | import App from './App'; 13 | import * as serviceWorker from './serviceWorker'; 14 | 15 | const container = document.getElementById('root'); 16 | const root = createRoot(container!); 17 | root.render(); 18 | 19 | // If you want your app to work offline and load faster, you can change 20 | // unregister() to register() below. Note this comes with some pitfalls. 21 | // Learn more about service workers: https://bit.ly/CRA-PWA 22 | serviceWorker.unregister(); 23 | -------------------------------------------------------------------------------- /docs/demos/todolist/src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /docs/demos/todolist/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "module": "esnext", 16 | "moduleResolution": "node", 17 | "resolveJsonModule": true, 18 | "isolatedModules": true, 19 | "noEmit": true, 20 | "jsx": "react" 21 | }, 22 | "include": [ 23 | "src" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /docs/demos/todolist/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "noEmit": false, 5 | "outDir": "./build", 6 | "declaration": true, 7 | "declarationMap": true, 8 | } 9 | } -------------------------------------------------------------------------------- /docs/index/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | 4 | # Production 5 | /build 6 | 7 | # Generated files 8 | .docusaurus 9 | .cache-loader 10 | 11 | # Misc 12 | .DS_Store 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | -------------------------------------------------------------------------------- /docs/index/.netlify-headers: -------------------------------------------------------------------------------- 1 | # see format of the file here: https://www.netlify.com/docs/headers-and-basic-auth/#custom-headers 2 | 3 | /checkline 4 | # Headers for that path: 5 | Cache-Control: no-cache 6 | 7 | /version.json 8 | # Headers for that path: 9 | Cache-Control: no-cache 10 | 11 | /* 12 | # required for https://hstspreload.org, see https://www.netlify.com/docs/ssl/#forcing-ssl 13 | Strict-Transport-Security: max-age=63072000; includeSubDomains; preload 14 | -------------------------------------------------------------------------------- /docs/index/.netlify-redirects: -------------------------------------------------------------------------------- 1 | # The following is neccessary to reduce site duplication and improve SEO result 2 | # Redirect default Netlify subdomain to primary domain 3 | https://hookstate.netlify.com/* https://hookstate.js.org/:splat 301! 4 | 5 | # version.json is cached in the bundle, but checkline path is unknown for the bundle 6 | # this path is used by the app to check internet connectivity 7 | /checkline /version.json 200 8 | 9 | /demo-todolist/* /docs/getting-started 200​ 10 | /demo/* /docs/getting-started 200​ 11 | 12 | # route old code sample links: 13 | /global-getting-started /docs/global-state 14 | /global-getting-started-interface /docs/exporting-state 15 | /local-getting-started /docs/local-state 16 | /local-complex-from-documentation /docs/scoped-state 17 | /local-async-state /docs/asynchronous-state 18 | /local-complex-tree-structure /docs/recursive-state 19 | /performance-demo-large-table /docs/performance-frequent-updates 20 | /performance-demo-large-form /docs/performance-large-state 21 | /global-multiple-consumers-statefragment /docs/using-without-statehook 22 | /plugin-initial /docs/extensions-snapshotable 23 | /plugin-initial-statefragment /docs/extensions-snapshotable 24 | /plugin-touched /docs/extensions-snapshotable 25 | /plugin-persistence /docs/extensions-persistence 26 | /docs/extensions-persistence /docs/extensions-localstored 27 | /plugin-validation /docs/extensions-validation 28 | /plugin-untracked /docs/extensions 29 | /docs/extensions-touched /docs/extensions 30 | 31 | # the application handles routes internally, including non-existing pages 32 | /* /index.html 200​ 33 | -------------------------------------------------------------------------------- /docs/index/README.md: -------------------------------------------------------------------------------- 1 | # Website 2 | 3 | This website is built using [Docusaurus 2](https://v2.docusaurus.io/), a modern static website generator. 4 | 5 | ## Workflow 6 | 7 | From the repository root directory: 8 | 9 | - `pnpm install` 10 | - `pnpm nx build docs` 11 | - `pnpm nx start docs` 12 | -------------------------------------------------------------------------------- /docs/index/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')], 3 | }; 4 | -------------------------------------------------------------------------------- /docs/index/blog/2020-03-10-new-hookstate-website.md: -------------------------------------------------------------------------------- 1 | --- 2 | slug: new-hookstate-website 3 | title: Hookstate website rework 4 | author: avkonst 5 | # author_title: Front End Engineer @ Facebook 6 | author_url: https://github.com/avkonst 7 | # author_image_url: https://avatars0.githubusercontent.com/u/1315101?s=400&v=4 8 | tags: [hookstate, docusaurus, news] 9 | --- 10 | 11 | Hookstate project receives new website built with Docusaurus 2. 12 | 13 | * Now live code samples are built-in to the documentation. 14 | * Typedoc generated [API reference](/docs/typedoc-hookstate-core) is served by the website as well. 15 | -------------------------------------------------------------------------------- /docs/index/blog/2020-05-29-cool-performance-demo-from-moonpiano.md: -------------------------------------------------------------------------------- 1 | --- 2 | slug: cool-hookstate-performance-demo-from-moonpiano-application 3 | title: Performance demo from the Moonpiano application 4 | author: avkonst 5 | # author_title: Front End Engineer @ Facebook 6 | author_url: https://github.com/avkonst 7 | # author_image_url: https://avatars0.githubusercontent.com/u/1315101?s=400&v=4 8 | tags: [hookstate, performance, applications] 9 | --- 10 | 11 | import ReactPlayer from 'react-player' 12 | 13 | Thanks to [@praisethemoon](https://github.com/praisethemoon) for creating such an impressive performance demo of the [Moonpiano application](https://moonpiano.praisethemoon.org/) powered by Hookstate. Here is the [scoped state](https://hookstate.js.org/docs/scoped-state) and [state usage tracking](https://hookstate.js.org/docs/performance-intro) technologies of the Hookstate library acting in full power: 14 | 15 | 16 | 17 | ### 18 | 19 | You can read [the full story](https://praisethemoon.org/hookstate-how-one-small-react-library-saved-moonpiano/) about boosting the performance of frequent state changes in the Moonpiano application, which is based on React for rendering and Hookstate for state management. -------------------------------------------------------------------------------- /docs/index/blog/2020-07-17-hookstate-docsearch.md: -------------------------------------------------------------------------------- 1 | --- 2 | slug: hookstate-docsearch 3 | title: Algolia search bar for Hookstate documentation 4 | author: avkonst 5 | # author_title: Front End Engineer @ Facebook 6 | author_url: https://github.com/avkonst 7 | # author_image_url: https://avatars0.githubusercontent.com/u/1315101?s=400&v=4 8 | tags: [hookstate, news, algolia] 9 | --- 10 | 11 | Thanks to Algolia for providing search functionality for Hookstate documentation. Free for open source projects! Very much appreciated. Works well! 12 | 13 | ![Hookstate Search Bar](/img/blog/2020-07-17-picture.png 'Hookstate Search Bar') -------------------------------------------------------------------------------- /docs/index/docs/02-global-state.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: global-state 3 | title: Global state 4 | sidebar_label: Global state 5 | --- 6 | 7 | import { PreviewSample } from '../src/PreviewSample' 8 | 9 | ## Creating and using global state 10 | 11 | Create the state and use it within and outside a React component. Few lines of code. No boilerplate! 12 | 13 | 14 | 15 | The state is created by [hookstate](typedoc-hookstate-core#hookstate). The first argument is the initial state value. The result value is an instance of [State](typedoc-hookstate-core#state), 16 | which **can be** used directly to get and set the state value outside a React component. 17 | 18 | When you need to use the state in a functional React component, 19 | pass the created state to [useHookstate](typedoc-hookstate-core#usehookstate) function 20 | and use the returned result in the component's logic. 21 | The returned result is an instance of [State](typedoc-hookstate-core#state) too, 22 | which **must be** used within a React component (during rendering 23 | or in effects) and/or it's children components. 24 | 25 | Read more about [hookstate](typedoc-hookstate-core#hookstate) and [useHookstate](typedoc-hookstate-core#usehookstate) in the [API reference](typedoc-hookstate-core). 26 | -------------------------------------------------------------------------------- /docs/index/docs/03-local-state.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: local-state 3 | title: Local state 4 | sidebar_label: Local state 5 | --- 6 | 7 | import { PreviewSample } from '../src/PreviewSample' 8 | 9 | ## Creating and using local state 10 | 11 | When a state is used by only one component, and maybe its children, 12 | it is recommended to use *local* state instead of [*global* state](global-state). 13 | In this case [useHookstate](typedoc-hookstate-core#usehookstate) behaves similarly to `React.useState`, but the 14 | returned instance of [State](typedoc-hookstate-core#state) has more features. 15 | 16 | 17 | 18 | Read more about [useHookstate](typedoc-hookstate-core#usehookstate) and [State](typedoc-hookstate-core#state) in the [API reference](typedoc-hookstate-core). 19 | -------------------------------------------------------------------------------- /docs/index/docs/04c-nullable-state.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: nullable-state 3 | title: Nullable state 4 | sidebar_label: Nullable state 5 | --- 6 | 7 | import { PreviewSample } from '../src/PreviewSample' 8 | 9 | ## Dealing with nullable state 10 | 11 | If a state can be missing (e.g. nested property is `undefined`) or `null`, checking for null state value is essential before diving into the nested states. 12 | 13 | Typescript will fail a compilation if you attempt to work with nested states of a state, which might have `null`/`undefined` state value. For example: 14 | 15 | ```tsx 16 | interface Task { name: string, priority?: number } 17 | 18 | const MyComponent = () => { 19 | const state = useHookstate(null) 20 | 21 | // JS - runtime error, TS - compilation error 22 | state.name.value 23 | // JS - runtime error, TS - compilation error 24 | state.value.name 25 | } 26 | ``` 27 | 28 | Here is the recommended way to check for `null`/`undefined` before unfolding nested states: 29 | 30 | ```tsx 31 | // type is for clarity, it is inferred by the compiler 32 | const stateOrNull: State | null = state.ornull 33 | if (stateOrNull) { 34 | // neither compilation nor runtime errors 35 | stateOrNull.name.value 36 | 37 | // neither compilation nor runtime errors 38 | stateOrNull.value.name 39 | } 40 | ``` 41 | 42 | [State.ornull](typedoc-hookstate-core.md#ornull) property is a very convenient way to deal in those cases. Here is an example of a component, which receives a state whose value might be `null`. 43 | 44 | ```tsx 45 | const MyInputField = (props: { state: State}) => { 46 | const state: State | null = props.state.ornull; 47 | // state is either null or an instance of State: 48 | if (!state) { 49 | // state value was null, do not render form field 50 | return <>; 51 | } 52 | // state value is an instance of string, can not be null here: 53 | return state.set(v.target.value)} /> 54 | } 55 | ``` 56 | 57 | [State.ornull](typedoc-hookstate-core.md#ornull) property is just a convenience. Traditional `||` may also work depending on a case. Here is an example of a component, which receives a state whose value might be `null`, but still proceeds with rendering 'state editor': 58 | 59 | ```tsx 60 | const MyInputField = (props: { state: State}) => { 61 | // state value is an instance of string or null here: 62 | return state.set(v.target.value)} /> 63 | } 64 | ``` 65 | -------------------------------------------------------------------------------- /docs/index/docs/05-async-state.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: asynchronous-state 3 | title: Asynchronous state 4 | sidebar_label: Asynchronous state 5 | --- 6 | 7 | import { PreviewSample } from '../src/PreviewSample' 8 | 9 | The root state can be set to a promise value, either as an initial value for [hookstate](typedoc-hookstate-core#hookstate)/[useHookstate](typedoc-hookstate-core#usehookstate) or as a subsequent value via [State.set](typedoc-hookstate-core#set) method. 10 | 11 | ## Checking if state is loading 12 | 13 | While a promise is not resolved or rejected almost any operation will result in an exception. To check if underlying promise is resolved or rejected, use [State.promised](typedoc-hookstate-core#readonly-promised). 14 | To check if underlying promise is rejected, use [State.error](typedoc-hookstate-core#readonly-error). For example: 15 | 16 | 17 | 18 | ## Executing an action when state is loaded 19 | 20 | It is also possible to access the underlying promise and add a value handling on the promise resolution: 21 | 22 | ```tsx 23 | const state = hookstate(new Promise(...)); 24 | state.promise.then(() => {}) 25 | ``` 26 | 27 | ```tsx 28 | const state = hookstate(none); 29 | state.promise.then(() => {}) 30 | setTimeout(() => state.set(...), 1000) 31 | ``` 32 | 33 | ## Suspending rendering until asynchronous state is loaded 34 | 35 | Suspend is a React 18 feature. Hookstate provides integration with it in 2 ways: 36 | - the [suspend](typedoc-hookstate-core#suspend) function 37 | - and the `suspend` option of the `StateFragment` component. 38 | 39 | Both methods work with local, global and scoped states. 40 | 41 | Example: 42 | 43 | ```tsx 44 | function MyComponent() { 45 | let state = useHookstate(new Promise(...)) 46 | return suspend(state) ??

State is loaded: {state.value}

47 | } 48 | ``` 49 | -------------------------------------------------------------------------------- /docs/index/docs/06-recursive-state.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: recursive-state 3 | title: Recursive state 4 | sidebar_label: Recursive state 5 | --- 6 | 7 | import { PreviewSample } from '../src/PreviewSample' 8 | 9 | The following example demonstrates how Hookstate can be efficiently used to manage tree-like data structure. It combines [nested state](./nested-state) with [scoped state](./scoped-state) technique for scalable rendering. 10 | 11 | 12 | -------------------------------------------------------------------------------- /docs/index/docs/07-exporting-state.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: exporting-state 3 | title: Exporting state 4 | sidebar_label: Exporting state 5 | --- 6 | 7 | import { PreviewSample } from '../src/PreviewSample' 8 | 9 | If you would like to implement and expose a custom global state without exposing Hookstate details, 10 | you can wrap a state object by a custom interface. For example: 11 | 12 | 13 | 14 | The other more complex example can be found in [the demo application](https://github.com/avkonst/hookstate/tree/master/docs/demos/todolist), where settings state is mapped directly to a hook which returns state access interface when used. 15 | -------------------------------------------------------------------------------- /docs/index/docs/08-using-without-statehook.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: state-without-usestate 3 | title: Using state without useHookstate hook 4 | sidebar_label: Using state without useHookstate 5 | --- 6 | 7 | import { PreviewSample } from '../src/PreviewSample' 8 | 9 | You can use Hookstate without a hook. It is particularly useful for integration with old class-based React components. 10 | It works with [global](./global-state), [local](./local-state), [nested](./nested-state) and [scoped](./scoped-state) states the same way. 11 | 12 | The following example demonstrates how to use a global state without [useHookstate](typedoc-hookstate-core#useHookstate) hook: 13 | 14 | 15 | 16 | And the following components are identical in behavior: 17 | 18 | Functional component: 19 | 20 | ```tsx 21 | const globalState = hookstate(''); 22 | 23 | const MyComponent = () => { 24 | const state = useHookstate(globalState); 25 | return state.set(e.target.value)} />; 27 | } 28 | ``` 29 | 30 | Functional component without a hook: 31 | 32 | ```tsx 33 | const globalState = hookstate(''); 34 | 35 | const MyComponent = () => { 36 | state => state.set(e.target.value)}> 38 | } 39 | ``` 40 | 41 | Class-based component: 42 | 43 | ```tsx 44 | const globalState = hookstate(''); 45 | 46 | class MyComponent extends React.Component { 47 | render() { 48 | return { 49 | state => state.set(e.target.value)}> 51 | } 52 | } 53 | } 54 | ``` 55 | -------------------------------------------------------------------------------- /docs/index/docs/09-state-with-use-effect.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: state-with-useeffect 3 | title: Using state with useEffect 4 | sidebar_label: Using state with useEffect 5 | --- 6 | 7 | import { PreviewSample } from '../src/PreviewSample' 8 | 9 | 10 | -------------------------------------------------------------------------------- /docs/index/docs/11-server-side-rendering.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: server-side-rendering 3 | title: Server Side Rendering with Hookstate 4 | sidebar_label: Using state with SSR 5 | --- 6 | 7 | As Hookstate is built on top of the standard `React.useState` and `React.useEffect` hooks, it should support SSR as the standard React hooks would support. There are many frameworks with SSR support, each has their own rules and tools for SSR. Follow the specific guidelines for SSR in relation to `React.useState` for the framework you use. The same should apply to `useHookstate` hook. 8 | 9 | We know a number of cases where a Hookstate is used with NextJS SSR. 10 | -------------------------------------------------------------------------------- /docs/index/docs/12-using-with-non-json.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: state-with-non-json-objects 3 | title: Using state with complex non-JSON serializable data 4 | sidebar_label: Using state with non-JSON data 5 | --- 6 | 7 | It is possible to have states (root level or nested) to hold instances of custom classes, including standard classes like Date. Accessing these values, will automatically enable `noproxy` option for the [State.get](typedoc-hookstate-core#get) function. It means these values will be tracked by Hookstate for rerendering purpose as whole instances, ie. using one property of such a value, means the entire object is used. 8 | 9 | If a state value holds a function, but the value is an instance of Object class, then it is required to set noproxy explicitly before accessing the function, for example: 10 | 11 | ```tsx 12 | let state = useHookstate({ callback: () => {} }) 13 | state.get({ noproxy: true }).callback() 14 | ``` 15 | 16 | If you use extensions, such as `localstored`, which requires to serialize and deserialize the state value, you may need to add [serializable extension](/docs/extensions-overview) to the state and define how a custom class value should be serialized and deserialized. 17 | -------------------------------------------------------------------------------- /docs/index/docs/21-performance-intro.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: performance-intro 3 | title: Performance overview 4 | sidebar_label: Overview 5 | --- 6 | 7 | import { PreviewSample } from '../src/PreviewSample' 8 | 9 | Performance is one of the main goals of the Hookstate project alongside with simple and flexible API. Hookstate has got two technologies built-in, which make it stand out and deliver incredible performance for applications: 10 | 11 | 1. Hookstate does Proxy-based state value usage tracking to identify what components require rerendering when a state is changed. 12 | 2. Hookstate has got [scoped state](./scoped-state) feature which multiplies the effect of the first, particularly for the cases involving [large states](./performance-large-state) and [frequent updates](./performance-frequent-updates). 13 | 14 | -------------------------------------------------------------------------------- /docs/index/docs/22-performance-large-state.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: performance-large-state 3 | title: Large state / Large forms 4 | sidebar_label: Large states 5 | --- 6 | 7 | import { PreviewSample } from '../src/PreviewSample' 8 | 9 | Here is an example of very responsive form state with 5000 fields. 10 | 11 | > Warning: it takes a bit of time to render the first time. 12 | 13 | 14 | -------------------------------------------------------------------------------- /docs/index/docs/23-performance-batched-updates.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: performance-batched-updates 3 | title: Batching state updates 4 | sidebar_label: Batching updates 5 | --- 6 | 7 | import { PreviewSample } from '../src/PreviewSample' 8 | 9 | If you have got an event handler, which results in multiple state update actions 10 | (for the same nested part of a state or different), React will batch state updates and will minimize rerendering natively. 11 | -------------------------------------------------------------------------------- /docs/index/docs/23-performance-frequent-updates.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: performance-frequent-updates 3 | title: Frequent state updates 4 | sidebar_label: Frequent updates 5 | --- 6 | 7 | import { PreviewSample } from '../src/PreviewSample' 8 | 9 | Here is an example of 10000 cells table which achieves an update rate 1+ cells per every millisecond. 10 | 11 | Unfortunately, this example cannot run embedded within the documentation. Check it out [here](https://github.com/avkonst/hookstate/tree/master/docs/demos/strictmode). 12 | 13 | 14 | -------------------------------------------------------------------------------- /docs/index/docs/24-performance-preact.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: performance-preact 3 | title: Using with preact 4 | sidebar_label: Using with Preact 5 | --- 6 | 7 | import { PreviewSample } from '../src/PreviewSample' 8 | 9 | Some people find that Preact improves the performance of an application. 10 | We verified that the Hookstate works with Preact just out of the box. 11 | -------------------------------------------------------------------------------- /docs/index/docs/30x-writing-an-extension.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: writing-extension 3 | title: Writing your own extension 4 | sidebar_label: Writing an extension 5 | --- 6 | 7 | import { PreviewSample } from '../src/PreviewSample' 8 | 9 | An extension is effectively a factory of a set of callbacks. All callbacks are optional. 10 | 11 | `onCreate` callback returns implementation for extension methods and properties which are added to a State object, where this extension is activated. If your extension does not add any properties or methods, do not provide `onCreate` callback or return `{}` from it. 12 | 13 | Here is an example of an extension which has got all possible callbacks and prints console logs when callbacks are called. It also defines an extension method and an extension property. The example is relatively long, because we provided extensive comments and mentioned other available capabilities, 14 | which we did not use in this instance. 15 | 16 | For more information, check out how the existing standard plugins are implemented. In case of any issues, just raise a ticket on Github. 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /docs/index/docs/32-extensions-snapshotable.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: extensions-snapshotable 3 | title: State snapshotting and restoring 4 | sidebar_label: Snapshotable state 5 | --- 6 | 7 | import { PreviewSample } from '../src/PreviewSample' 8 | 9 | The `@hookstate/snapshotable` extension allows to take state snapshots and restore from it. 10 | 11 | 12 | -------------------------------------------------------------------------------- /docs/index/docs/35-extensions-validation.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: extensions-validation 3 | title: State validation 4 | sidebar_label: Validated state 5 | --- 6 | 7 | import { PreviewSample } from '../src/PreviewSample' 8 | 9 | The `Validation` extension allows to validate a state. 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /docs/index/docs/36-extensions-localstored.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: extensions-localstored 3 | title: Localstored state 4 | sidebar_label: Localstored state 5 | --- 6 | 7 | import { PreviewSample } from '../src/PreviewSample' 8 | 9 | Simple extension which enables local storage persistence for a state. 10 | - It works same way for local and global states 11 | - An application can provide storage engine instance to allow for storing the data elsewhere. By default it stores it in a local browser storage. 12 | - Setting a state to `none` will delete the persisted data from the storage. 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /docs/index/docs/37-extensions-identifiable.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: extensions-identifiable 3 | title: Identifiable state 4 | sidebar_label: Identifiable state 5 | --- 6 | 7 | import { PreviewSample } from '../src/PreviewSample' 8 | 9 | A simple extension which allows to associate string metadata with a state. 10 | 11 | 12 | -------------------------------------------------------------------------------- /docs/index/docs/38-extensions-broadcasted.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: extensions-broadcasted 3 | title: State synchronized across tabs 4 | sidebar_label: Broadcasted state 5 | --- 6 | 7 | import { PreviewSample } from '../src/PreviewSample' 8 | 9 | An extension which enables storage synchronization across multiple browser tabs. 10 | Open the same example in multiple tabs to see the plugin at work. 11 | 12 | -------------------------------------------------------------------------------- /docs/index/docs/39-extensions-subscribable.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: extensions-subscribable 3 | title: Subscribable state 4 | sidebar_label: Subscribable state 5 | --- 6 | 7 | import { PreviewSample } from '../src/PreviewSample' 8 | 9 | An extension which allows registering a callback to observe state updates. The extension `subscribe` method returns a function, which should be called when you want to unsubscribe. 10 | 11 | 12 | -------------------------------------------------------------------------------- /docs/index/docs/40-devtools-overview.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: devtools 3 | title: Development Tools Overview 4 | sidebar_label: Overview 5 | --- 6 | 7 | ## Setting up 8 | 9 | * Devtools is an extension. Hookstate-4 requires it to be added explicitly to a state, by providing it as a second argument to `hookstate` or `useHookstate` functions. 10 | * If a state does not have `identifiable` extension attached as well, `devtools` extension should be initialized with the `key` option. 11 | 12 | ```tsx 13 | import { devtools } from '@hookstate/devtools' 14 | let state = hookstate(value, devtools({ key: 'my-state-label' })) 15 | ``` 16 | 17 | ```tsx 18 | import { identifiable } from '@hookstate/identifiable' 19 | import { devtools } from '@hookstate/devtools' 20 | let state = hookstate(value, extend(identifiable('my-state-label'), devtools())) 21 | ``` 22 | 23 | * Install [Chrome browser's extension](https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd?hl=en) and reload your app. 24 | 25 | There is no impact on performance in production. Development tools are activated only when the browser's extension is opened. 26 | 27 | ## Demo 28 | 29 | [Demo application](https://github.com/avkonst/hookstate/tree/master/docs/demos/todolist) has got DevTools integrated. Try it out! 30 | 31 | ## Set state value from the development tools 32 | 33 | You can set new value for a state at root or at a specific path using the development tools. 34 | Put content for an action in the 'Dispatch' form and click 'Dispatch' button. 35 | 36 | The easiest way to learn the content of a dispatch action is to inspect an action data for the state update, triggered within an application. 37 | 38 | ## Toggle breakpoint on state update 39 | 40 | Trigger a dispatch action from the Redux development tools with the following content: 41 | 42 | ```tsx 43 | { 44 | type: 'BREAKPOINT', 45 | } 46 | ``` 47 | 48 | Now any event, which sets a state, will trigger a breakpoint in the browser. 49 | 50 | To disable the breakpoint, repeat the same dispatch action again. 51 | 52 | ## Pausing/Unpausing monitoring 53 | 54 | Click 'Pause recording' button (bottom row) in the development tools. 55 | 56 | ## Persist state on page reload 57 | 58 | Click 'Persist' button (bottom row) in the development tools. 59 | -------------------------------------------------------------------------------- /docs/index/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hookstate/docs", 3 | "version": "4.0.0-rc3", 4 | "private": true, 5 | "scripts": { 6 | "start": "docusaurus start --no-open --host 0.0.0.0", 7 | "build": "docusaurus build && cp .netlify-headers build/_headers && cp .netlify-redirects build/_redirects", 8 | "swizzle": "docusaurus swizzle", 9 | "deploy": "docusaurus deploy", 10 | "update:deps": "ncu -u" 11 | }, 12 | "dependencies": { 13 | "@docusaurus/core": "2.0.0-beta.22", 14 | "@docusaurus/preset-classic": "2.0.0-beta.22", 15 | "@hookstate/core": "workspace:*", 16 | "@hookstate/clonable": "workspace:*", 17 | "@hookstate/comparable": "workspace:*", 18 | "@hookstate/initializable": "workspace:*", 19 | "@hookstate/identifiable": "workspace:*", 20 | "@hookstate/serializable": "workspace:*", 21 | "@hookstate/snapshotable": "workspace:*", 22 | "@hookstate/subscribable": "workspace:*", 23 | "@hookstate/localstored": "workspace:*", 24 | "@hookstate/broadcasted": "workspace:*", 25 | "@hookstate/validation": "workspace:*", 26 | "@hookstate/logged": "workspace:*", 27 | "@hookstate/todolist": "workspace:*", 28 | "@material-ui/core": "4.12.4", 29 | "@material-ui/icons": "4.11.3", 30 | "@mdx-js/react": "1.6.22", 31 | "@types/lodash.clonedeep": "4.5.7", 32 | "@types/lodash.isequal": "4.5.6", 33 | "broadcast-channel": "4.13.0", 34 | "classnames": "2.3.1", 35 | "lodash.clonedeep": "4.5.0", 36 | "lodash.isequal": "4.5.0", 37 | "prism-react-renderer": "1.3.5", 38 | "react": "18.2.0", 39 | "react-dom": "18.2.0", 40 | "react-player": "2.10.1", 41 | "clsx": "^1.2.1" 42 | }, 43 | "browserslist": { 44 | "production": [ 45 | ">0.2%", 46 | "not dead", 47 | "not op_mini all" 48 | ], 49 | "development": [ 50 | "last 1 chrome version", 51 | "last 1 firefox version", 52 | "last 1 safari version" 53 | ] 54 | }, 55 | "devDependencies": { 56 | "@docusaurus/module-type-aliases": "2.0.0-beta.22", 57 | "@tsconfig/docusaurus": "^1.0.6", 58 | "npm-check-updates": "^15.2.1", 59 | "typescript": "^4.7.4" 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /docs/index/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": "docs/index", 3 | "tags": [] 4 | } 5 | -------------------------------------------------------------------------------- /docs/index/sidebars.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017-present, Facebook, Inc. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | module.exports = { 9 | someSidebar: { 10 | Introduction: [ 11 | 'getting-started', 12 | 'migrating-to-v4', 13 | 'global-state', 14 | 'local-state', 15 | 'nested-state', 16 | 'scoped-state', 17 | 'nullable-state', 18 | 'asynchronous-state', 19 | 'recursive-state', 20 | 'exporting-state', 21 | 'state-with-non-json-objects', 22 | 'state-with-useeffect', 23 | 'state-without-usestate', 24 | 'server-side-rendering', 25 | ], 26 | Performance: [ 27 | 'performance-intro', 28 | 'performance-large-state', 29 | 'performance-frequent-updates', 30 | 'performance-batched-updates', 31 | 'performance-preact', 32 | ], 33 | Extensions: [ 34 | 'extensions-overview', 35 | 'extensions-validation', 36 | 'extensions-snapshotable', 37 | 'extensions-broadcasted', 38 | 'extensions-localstored', 39 | 'extensions-identifiable', 40 | 'extensions-subscribable', 41 | 'writing-extension' 42 | ], 43 | 'Development Tools': ['devtools'], 44 | 'API Reference': [ 45 | 'typedoc-hookstate-core', 46 | 'exceptions' 47 | ], 48 | }, 49 | }; 50 | -------------------------------------------------------------------------------- /docs/index/src/css/custom.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Any CSS included here will be global. The classic template 3 | * bundles Infima by default. Infima is a CSS framework designed to 4 | * work well for content-centric websites. 5 | */ 6 | 7 | /* You can override the default Infima variables here. */ 8 | :root { 9 | --ifm-color-primary: rgb(0, 120, 190); 10 | --ifm-color-primary-dark: rgb(0, 111, 175); 11 | --ifm-color-primary-darker: rgb(0, 94, 148); 12 | --ifm-color-primary-darkest: rgb(0, 80, 126); 13 | --ifm-color-primary-light: rgb(7, 135, 209); 14 | --ifm-color-primary-lighter: rgb(30, 163, 240); 15 | --ifm-color-primary-lightest: rgb(49, 179, 255); 16 | --ifm-code-font-size: 95%; 17 | } 18 | 19 | .docusaurus-highlight-code-line { 20 | background-color: rgb(72, 77, 91); 21 | display: block; 22 | margin: 0 calc(-1 * var(--ifm-pre-padding)); 23 | padding: 0 var(--ifm-pre-padding); 24 | } -------------------------------------------------------------------------------- /docs/index/src/examples/Index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { ExampleComponent as ExampleGlobalPrimitive } from './global-getting-started'; 4 | import { ExampleComponent as ExampleGlobalPrimitiveInterface } from './global-getting-started-interface'; 5 | import { ExampleComponent as ExampleLocalPrimitive } from './local-getting-started'; 6 | import { ExampleComponent as ExampleAsyncState } from './local-async-state'; 7 | import { ExampleComponent as ExampleLocalComplexFromDocumentation } from './local-complex-from-documentation'; 8 | import { ExampleComponent as ExampleLocalComplexTreeStructure } from './local-complex-tree-structure'; 9 | import { ExampleComponent as ExamplePerformanceLargeForm } from './performance-demo-large-form'; 10 | import { ExampleComponent as ExampleGlobalMultipleConsumersStateFragment } from './global-multiple-consumers-statefragment'; 11 | 12 | import { ExampleComponent as ExamplePluginIdentifiable } from './plugin-identifiable'; 13 | import { ExampleComponent as ExamplePluginSubscribable } from './plugin-subscribable'; 14 | import { ExampleComponent as ExamplePluginSnapshotable } from './plugin-snapshotable'; 15 | import { ExampleComponent as ExamplePluginLocalstored } from './plugin-localstored'; 16 | import { ExampleComponent as ExamplePluginBroadcasted } from './plugin-broadcasted'; 17 | import { ExampleComponent as ExamplePluginValidation } from './plugin-validation'; 18 | import { ExampleComponent as ExamplePluginCustom } from './plugin-custom'; 19 | 20 | import { ExampleComponent as ExampleWithUseEffect } from './with-use-effect'; 21 | 22 | const baseUrl = 'https://raw.githubusercontent.com/avkonst/hookstate/master/docs/index/src/examples/' 23 | 24 | export const ExampleCodeUrl = (id: string) => `${baseUrl}${id}.tsx`; 25 | 26 | export const ExamplesRepo: Map = new Map(); 27 | ExamplesRepo.set('global-getting-started', ); 28 | ExamplesRepo.set('global-getting-started-interface', ); 29 | ExamplesRepo.set('local-getting-started', ); 30 | ExamplesRepo.set('local-complex-from-documentation', ); 31 | ExamplesRepo.set('local-async-state', ); 32 | ExamplesRepo.set('local-complex-tree-structure', ); 33 | ExamplesRepo.set('performance-demo-large-form', ); 34 | ExamplesRepo.set('global-multiple-consumers-statefragment', ); 35 | 36 | ExamplesRepo.set('plugin-identifiable', ); 37 | ExamplesRepo.set('plugin-subscribable', ); 38 | ExamplesRepo.set('plugin-snapshotable', ); 39 | ExamplesRepo.set('plugin-validation', ); 40 | ExamplesRepo.set('plugin-localstored', ); 41 | ExamplesRepo.set('plugin-broadcasted', ); 42 | 43 | ExamplesRepo.set('plugin-custom', ); 44 | 45 | ExamplesRepo.set('with-use-effect', ); 46 | -------------------------------------------------------------------------------- /docs/index/src/examples/global-getting-started-interface.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { hookstate, useHookstate, State } from '@hookstate/core'; 3 | 4 | // internal variables 5 | const globalState = hookstate(0); 6 | const wrapState = (s: State) => ({ 7 | get: () => s.value, 8 | increment: () => s.set(p => p + 1) 9 | }) 10 | 11 | // The following 2 functions can be exported now: 12 | export const accessGlobalState = () => wrapState(globalState) 13 | export const useGlobalState = () => wrapState(useHookstate(globalState)) 14 | 15 | // And here is how it can be used outside of a component ... 16 | setInterval(() => accessGlobalState().increment(), 3000) 17 | // ... and inside of a component 18 | export const ExampleComponent = () => { 19 | const state = useGlobalState(); 20 | return

21 | Counter value: {state.get()} (watch +1 every 3 seconds) 22 | 23 |

24 | } 25 | -------------------------------------------------------------------------------- /docs/index/src/examples/global-getting-started.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { hookstate, useHookstate } from '@hookstate/core'; 3 | 4 | const globalState = hookstate(0); 5 | 6 | setInterval(() => globalState.set(p => p + 1), 3000) 7 | 8 | export const ExampleComponent = () => { 9 | const state = useHookstate(globalState); 10 | return <> 11 | Counter value: {state.get()} (watch +1 every 3 seconds) {' '} 12 | 13 | 14 | } -------------------------------------------------------------------------------- /docs/index/src/examples/global-multiple-consumers-statefragment.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { hookstate, StateFragment } from '@hookstate/core'; 3 | 4 | const state = hookstate(0); 5 | 6 | export const ExampleComponent = () => {s => 7 | Current state: {s.value} 8 | 9 | } 10 | -------------------------------------------------------------------------------- /docs/index/src/examples/local-async-state.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useHookstate } from '@hookstate/core'; 3 | 4 | export const ExampleComponent = () => { 5 | const resourcePath = 'https://raw.githubusercontent.com/avkonst/hookstate/master/CNAME'; 6 | const fetchResource = () => fetch(resourcePath) 7 | .then(r => r.text()) 8 | const state = useHookstate(fetchResource); 9 | 10 | if (state.promised) { 11 | return

Loading {resourcePath}

; 12 | } 13 | 14 | if (state.error) { 15 | return

Failed to load {resourcePath}
16 | {state.error.toString()}
17 | 18 |

19 | } 20 | 21 | return

Loaded {resourcePath}
22 | {state.value}
23 | 24 |

25 | } 26 | -------------------------------------------------------------------------------- /docs/index/src/examples/local-complex-from-documentation.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useHookstate, State } from '@hookstate/core'; 3 | 4 | interface Task { name: string; priority?: number } 5 | 6 | export const ExampleComponent = () => { 7 | const state: State = useHookstate([{ name: 'First Task' }] as Task[]); 8 | return <> 9 | {state.map((taskState: State, taskIndex) => 10 | 11 | )} 12 | 13 | 14 | } 15 | 16 | function TaskEditor(props: { taskState: State }) { 17 | const taskState = props.taskState; 18 | return

taskState.name.set(e.target.value)} 21 | />

22 | } 23 | -------------------------------------------------------------------------------- /docs/index/src/examples/local-complex-tree-structure.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useHookstate, State } from '@hookstate/core'; 3 | 4 | interface Node { name: string; children?: Node[] } 5 | 6 | export const ExampleComponent = () => { 7 | const state = useHookstate([ 8 | { 9 | name: 'Node 1', 10 | children: [ 11 | { name: 'Node 1.1' }, 12 | { name: 'Node 1.2' } 13 | ] 14 | }, 15 | { 16 | name: 'Node 2' 17 | } 18 | ]); 19 | return <> 20 | 21 | 22 | 23 | } 24 | 25 | function NodeNameEditor(props: { nameState: State }) { 26 | // scoped state is optional for performance 27 | // could have used props.nameState everywhere instead 28 | const state = useHookstate(props.nameState); 29 | return <> 30 |

31 | state.set(e.target.value)} 34 | /> Last render at: {(new Date()).toISOString()} 35 |

36 | 37 | } 38 | 39 | function NodeListEditor(props: { nodes: State }) { 40 | // scoped state is optional for performance 41 | // could have used props.nodes everywhere instead 42 | const state = useHookstate(props.nodes); 43 | return
44 | {state.ornull && state.ornull.map((nodeState: State, i) => 45 |
46 | 47 | 48 |
49 | )} 50 |

53 |
54 | } 55 | 56 | function JsonDump(props: { state: State }) { 57 | // scoped state is optional for performance 58 | // could have used props.state everywhere instead 59 | const state = useHookstate(props.state); 60 | return

61 | Current state: {JSON.stringify(state.value)}
62 | Last render at: {(new Date()).toISOString()} 63 |

64 | } 65 | -------------------------------------------------------------------------------- /docs/index/src/examples/local-getting-started.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useHookstate } from '@hookstate/core'; 3 | 4 | export const ExampleComponent = () => { 5 | const state = useHookstate(0); 6 | return <> 7 | Counter value: {state.get()} 8 | 9 | 10 | } 11 | -------------------------------------------------------------------------------- /docs/index/src/examples/performance-demo-large-form.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useHookstate, State } from '@hookstate/core'; 3 | 4 | export const ExampleComponent = () => { 5 | const state = useHookstate(Array.from(Array(5000).keys()).map(i => `Field #${i + 1} value`)); 6 | return <> 7 | 8 | {state.map((taskState, taskIndex) => 9 | 10 | )} 11 | 12 | } 13 | 14 | function FieldEditor(props: { fieldState: State }) { 15 | const scopedState = useHookstate(props.fieldState); 16 | return

17 | Last render at: {(new Date()).toISOString()} scopedState.set(e.target.value)} 20 | /> 21 |

22 | } 23 | 24 | function JsonDump(props: { state: State }) { 25 | const scopedState = useHookstate(props.state); 26 | return

27 | Last render at: {(new Date()).toISOString()} (JSON dump of the first 10 fields) 28 | :
{JSON.stringify(scopedState.get().slice(0, 10), undefined, 4)} 29 |

30 | } 31 | -------------------------------------------------------------------------------- /docs/index/src/examples/plugin-broadcasted.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useHookstate } from '@hookstate/core'; 3 | import { broadcasted } from '@hookstate/broadcasted'; 4 | 5 | export const ExampleComponent = () => { 6 | const state = useHookstate([{ name: 'First Task' }], 7 | broadcasted({ 8 | // topic is optional, 9 | // if it is not defined, the extension requires and 10 | // uses the identifier from the @hookstate/identifiable 11 | topic: 'my-sync-channel-topic', 12 | onLeader: () => { // optional 13 | window.console.log('This tab is a leader now') 14 | // attach persistence, remote synchronization plugins, 15 | // or any other actions which needs to be done with a state 16 | // only by one tab 17 | } 18 | })) 19 | 20 | return <> 21 | {state.map((taskState, taskIndex) => { 22 | return

23 | taskState.name.set(e.target.value)} 26 | /> 27 |

28 | })} 29 |

32 | 33 | } 34 | -------------------------------------------------------------------------------- /docs/index/src/examples/plugin-identifiable.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useHookstate } from '@hookstate/core'; 3 | import { identifiable } from '@hookstate/identifiable'; 4 | 5 | export const ExampleComponent = () => { 6 | const state = useHookstate(1, identifiable('my-counter')) 7 | return <> 8 |

Counter: {state.value}

9 |

12 | 13 | } 14 | 15 | -------------------------------------------------------------------------------- /docs/index/src/examples/plugin-localstored.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useHookstate } from '@hookstate/core'; 3 | import { localstored } from '@hookstate/localstored'; 4 | 5 | export const ExampleComponent = () => { 6 | const state = useHookstate([{ name: 'First Task' }], 7 | localstored({ 8 | // key is optional, 9 | // if it is not defined, the extension requires and 10 | // uses the identifier from the @hookstate/identifiable 11 | key: 'state-key' 12 | })) 13 | 14 | return <> 15 | {state.map((taskState, taskIndex) => { 16 | return

17 | taskState.name.set(e.target.value)} 20 | /> 21 |

22 | })} 23 |

26 | 27 | } 28 | -------------------------------------------------------------------------------- /docs/index/src/examples/plugin-subscribable.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useHookstate } from '@hookstate/core'; 3 | import { subscribable } from '@hookstate/subscribable'; 4 | 5 | export const ExampleComponent = () => { 6 | const state = useHookstate({ a: 1, b: 1 }, subscribable()) 7 | 8 | React.useEffect(() => state.subscribe( 9 | (v) => console.log('updated any counter', JSON.stringify(v))), []) 10 | React.useEffect(() => state.a.subscribe( 11 | (v) => console.log('updated counter A', JSON.stringify(v))), []) 12 | React.useEffect(() => state.b.subscribe( 13 | (v) => console.log('updated counter B', JSON.stringify(v))), []) 14 | 15 | return <> 16 |

Open console to see the subscription effect

17 |

Counter A: {state.a.value}

18 |

Counter B: {state.b.value}

19 |

22 |

25 | 26 | } 27 | 28 | -------------------------------------------------------------------------------- /docs/index/src/examples/plugin-validation.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { State, useHookstate } from '@hookstate/core'; 3 | import { Validation, validation } from '@hookstate/validation'; 4 | 5 | interface Task { name: string } 6 | 7 | export const ExampleComponent = () => { 8 | const state: State = useHookstate([{ name: 'First Task' }, { name: 'Second Task' }], validation()) 9 | 10 | // configure rules 11 | state.validate(tasks => tasks.length >= 3, 12 | 'There should be at least 3 tasks in the list'); 13 | state.validate(tasks => tasks.length < 4, 14 | 'There are too many tasks', 15 | 'warning') 16 | 17 | return <> 18 |

19 | Is this task list valid?: {state.valid().toString()}
20 | Is this task list valid (ignoring nested errors)?: 21 | {state.valid({ depth: 1 }).toString()}
22 | What are the errors and warnings?: {JSON.stringify(state.errors(), null, 4)}
23 | What is the first error or warning?: {JSON.stringify(state.firstError(), null, 4)}
24 | What is the first error (ignoring warnings and nested errors)?: { 25 | JSON.stringify(state.firstError( 26 | i => i.severity === 'error', 1), null, 4)}
27 |

28 | {state.map((taskState, taskIndex) => { 29 | // attaching validation to any element in the array applies it to every 30 | taskState.name.validate( 31 | taskName => taskName.length > 0, 'Task name should not be empty') 32 | return

33 | Is this task valid?: {taskState.valid().toString()}
34 | Is the name of the task valid?: {taskState.name.valid().toString()}
35 | This task validation errors and warnings: {JSON.stringify(taskState.errors())}
36 | taskState.name.set(e.target.value)} 39 | /> 40 |

41 | })} 42 |

45 | 46 | } 47 | -------------------------------------------------------------------------------- /docs/index/src/examples/with-use-effect.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useHookstate } from '@hookstate/core'; 3 | 4 | export const ExampleComponent = () => { 5 | const state = useHookstate<{ field1: boolean, field2: boolean, field3?: {} }>({ 6 | field1: false, 7 | field2: false, 8 | field3: undefined 9 | }); 10 | React.useEffect(() => { 11 | console.log('#1 effect called') 12 | if (state.field1.value && state.field3.value) { 13 | console.log('#1 effect called: if => true') 14 | } 15 | }, [state.field1, state.field3]) 16 | 17 | React.useEffect(() => { 18 | console.log('#2 effect called') 19 | if (state.field2.value && state.field3.value) { 20 | console.log('#2 effect called: if => true') 21 | } 22 | }, [state.field2, state.field3]) 23 | 24 | let hide = { stealth: true } 25 | console.log(`rendered: field1: ${state.field1.get(hide)}, field2: ${state.field2.get(hide)}, field3: ${state.field3.get(hide)}`) 26 | 27 | return <> 28 |

Checkout console logs when the effect callback runs

29 |
30 |
31 | 32 | 33 | } -------------------------------------------------------------------------------- /docs/index/src/pages/styles.module.css: -------------------------------------------------------------------------------- 1 | /** 2 | * CSS files with the .module.css suffix will be treated as CSS modules 3 | * and scoped locally. 4 | */ 5 | 6 | .heroTitle { 7 | text-transform: uppercase; 8 | padding: 2rem 0; 9 | padding-bottom: 1rem; 10 | } 11 | 12 | .heroBanner { 13 | /* background-size: 100%; 14 | background-position: 0; 15 | background-image: linear-gradient( rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.5) ), url('https://static.vecteezy.com/system/resources/previews/000/642/562/non_2x/abstract-futuristic-hexagon-shape-pattern-connection-in-gradient-blue-technology-background-vector.jpg'); */ 16 | background-color: rgb(11, 57, 102); 17 | padding: 4rem 0; 18 | text-align: center; 19 | position: relative; 20 | overflow: hidden; 21 | } 22 | 23 | @media screen and (max-width: 966px) { 24 | .heroBanner { 25 | padding: 2rem; 26 | } 27 | } 28 | 29 | .buttons { 30 | display: flex; 31 | align-items: center; 32 | justify-content: center; 33 | } 34 | 35 | .features { 36 | display: flex; 37 | align-items: center; 38 | padding: 2rem 0; 39 | width: 100%; 40 | } 41 | 42 | .featureImage { 43 | height: 200px; 44 | width: 200px; 45 | } 46 | 47 | .getStarted { 48 | background-color: lightgreen; 49 | } -------------------------------------------------------------------------------- /docs/index/static/BingSiteAuth.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 3AD0DAE0936710F6322E01F22038C5AB 4 | -------------------------------------------------------------------------------- /docs/index/static/img/blog/2020-07-17-picture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avkonst/hookstate/e3118d674a4727c60fc3c682008211b44774f187/docs/index/static/img/blog/2020-07-17-picture.png -------------------------------------------------------------------------------- /docs/index/static/img/favicon-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avkonst/hookstate/e3118d674a4727c60fc3c682008211b44774f187/docs/index/static/img/favicon-128.png -------------------------------------------------------------------------------- /docs/index/static/img/favicon-152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avkonst/hookstate/e3118d674a4727c60fc3c682008211b44774f187/docs/index/static/img/favicon-152.png -------------------------------------------------------------------------------- /docs/index/static/img/favicon-167.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avkonst/hookstate/e3118d674a4727c60fc3c682008211b44774f187/docs/index/static/img/favicon-167.png -------------------------------------------------------------------------------- /docs/index/static/img/favicon-180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avkonst/hookstate/e3118d674a4727c60fc3c682008211b44774f187/docs/index/static/img/favicon-180.png -------------------------------------------------------------------------------- /docs/index/static/img/favicon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avkonst/hookstate/e3118d674a4727c60fc3c682008211b44774f187/docs/index/static/img/favicon-192.png -------------------------------------------------------------------------------- /docs/index/static/img/favicon-196.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avkonst/hookstate/e3118d674a4727c60fc3c682008211b44774f187/docs/index/static/img/favicon-196.png -------------------------------------------------------------------------------- /docs/index/static/img/favicon-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avkonst/hookstate/e3118d674a4727c60fc3c682008211b44774f187/docs/index/static/img/favicon-32.png -------------------------------------------------------------------------------- /docs/index/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avkonst/hookstate/e3118d674a4727c60fc3c682008211b44774f187/docs/index/static/img/favicon.ico -------------------------------------------------------------------------------- /docs/index/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // This file is not used in compilation. It is here just for a nice editor experience. 3 | "extends": "@tsconfig/docusaurus/tsconfig.json", 4 | "compilerOptions": { 5 | "baseUrl": "." 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /docs/logo-square.gvdesign: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avkonst/hookstate/e3118d674a4727c60fc3c682008211b44774f187/docs/logo-square.gvdesign -------------------------------------------------------------------------------- /docs/logo.gvdesign: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avkonst/hookstate/e3118d674a4727c60fc3c682008211b44774f187/docs/logo.gvdesign -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | projects: [ 3 | '/core/jest.config.js', 4 | ] 5 | }; 6 | -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [build.environment] 2 | NODE_VERSION = "16" 3 | NPM_FLAGS = "--version" # prevent Netlify npm install 4 | [build] 5 | publish = "docs/index/build" 6 | command = "pnpm nx build:docs core && pnpm nx build docs && pnpm nx build todolist" 7 | # ignore = "git diff --quiet $CACHED_COMMIT_REF $COMMIT_REF docs package.json pnpm-lock.yaml netlify.toml" -------------------------------------------------------------------------------- /nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "verbose": true, 3 | "ignore": ["node_modules", "./**/node_modules", "./**/dist"], 4 | "ext": "js,ts,tsx,json,html" 5 | } 6 | -------------------------------------------------------------------------------- /nx.json: -------------------------------------------------------------------------------- 1 | { 2 | "implicitDependencies": { 3 | "package.json": { 4 | "dependencies": "*", 5 | "devDependencies": "*" 6 | }, 7 | "nx.json": "*" 8 | }, 9 | "npmScope": "@hookstate", 10 | "tasksRunnerOptions": { 11 | "default": { 12 | "runner": "@nrwl/workspace/tasks-runners/default", 13 | "options": { 14 | "cacheableOperations": ["build", "test", "lint", "e2e"], 15 | "parallel": 1 16 | } 17 | } 18 | }, 19 | "affected": { 20 | "defaultBase": "master" 21 | }, 22 | "targetDependencies": { 23 | "build": [ 24 | { 25 | "target": "build", 26 | "projects": "dependencies" 27 | } 28 | ], 29 | "test": [ 30 | { 31 | "target": "build", 32 | "projects": "dependencies" 33 | } 34 | ], 35 | "test:ci": [ 36 | { 37 | "target": "build", 38 | "projects": "dependencies" 39 | } 40 | ], 41 | "start": [ 42 | { 43 | "target": "build", 44 | "projects": "dependencies" 45 | } 46 | ], 47 | "release": [ 48 | { 49 | "target": "build", 50 | "projects": "self" 51 | } 52 | ] 53 | }, 54 | "defaultProject": "core" 55 | } 56 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hookstate/workspace", 3 | "version": "4.0.0", 4 | "description": "The workspace for @hookstate.", 5 | "license": "MIT", 6 | "author": { 7 | "name": "Andrey Konstantinov" 8 | }, 9 | "repository": { 10 | "url": "https://github.com/avkonst/hookstate" 11 | }, 12 | "bugs": { 13 | "url": "https://github.com/avkonst/hookstate/issues" 14 | }, 15 | "homepage": "https://github.com/avkonst/hookstate", 16 | "engines": { 17 | "node": ">=16", 18 | "pnpm": ">=7.18.2" 19 | }, 20 | "scripts": { 21 | "build": "nx run-many --target=build --all", 22 | "test": "nx run-many --target=test --all", 23 | "test:ci": "nx run-many --target=test:ci --all", 24 | "update:deps": "nx run-many --target=update:deps --all", 25 | "clean": "nx run-many --target=clean --all", 26 | "release": "nx run-many --target=release --all", 27 | "nx:w": "nodemon node_modules/@nrwl/cli/bin/nx.js" 28 | }, 29 | "devDependencies": { 30 | "@nrwl/cli": "15.3.3", 31 | "@nrwl/workspace": "15.3.3", 32 | "jest": "28.1.2", 33 | "nodemon": "^2.0.16", 34 | "nx": "15.3.3", 35 | "typescript": "4.6.2" 36 | } 37 | } -------------------------------------------------------------------------------- /plugins/broadcasted/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2022 Andrey Konstantinov 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 | -------------------------------------------------------------------------------- /plugins/broadcasted/README.md: -------------------------------------------------------------------------------- 1 | # @hookstate/broadcasted 2 | 3 | [![license](https://img.shields.io/github/license/avkonst/hookstate)](https://img.shields.io/github/license/avkonst/hookstate) [![npm version](https://img.shields.io/npm/v/@hookstate/broadcasted.svg?maxAge=300&label=version&colorB=007ec6)](https://www.npmjs.com/package/@hookstate/broadcasted) 4 | 5 | Plugin for @hookstate/core to enable synchronization of a state across browser tabs. 6 | 7 | [Learn more...](https://hookstate.js.org/docs/extensions-broadcasted) 8 | 9 | -------------------------------------------------------------------------------- /plugins/broadcasted/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'ts-jest', 3 | testEnvironment: 'node' 4 | }; -------------------------------------------------------------------------------- /plugins/broadcasted/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hookstate/broadcasted", 3 | "version": "4.0.0", 4 | "description": "Plugin for @hookstate/core to enable state synchronization across browser tabs.", 5 | "license": "MIT", 6 | "author": { 7 | "name": "Andrey Konstantinov" 8 | }, 9 | "repository": { 10 | "url": "https://github.com/avkonst/hookstate" 11 | }, 12 | "bugs": { 13 | "url": "https://github.com/avkonst/hookstate/issues" 14 | }, 15 | "homepage": "https://github.com/avkonst/hookstate", 16 | "main": "dist/index.js", 17 | "module": "dist/index.es.js", 18 | "jsnext:main": "dist/index.es.js", 19 | "types": "dist/index.d.ts", 20 | "scripts": { 21 | "build": "rollup -c", 22 | "build:w": "rollup -c -w", 23 | "clean": "rimraf dist", 24 | "test": "jest --env=jsdom", 25 | "test:ci": "jest --env=jsdom", 26 | "test:w": "jest --env=jsdom --watch", 27 | "release": "npm publish --access public", 28 | "update:deps": "ncu -u" 29 | }, 30 | "dependencies": {}, 31 | "peerDependencies": { 32 | "@hookstate/core": "^4.0.0", 33 | "broadcast-channel": "^4.10.0" 34 | }, 35 | "devDependencies": { 36 | "@babel/core": "7.18.6", 37 | "@babel/runtime": "7.18.6", 38 | "@hookstate/core": "workspace:*", 39 | "@rollup/plugin-babel": "5.3.1", 40 | "@rollup/plugin-commonjs": "22.0.1", 41 | "@rollup/plugin-node-resolve": "13.3.0", 42 | "@rollup/plugin-url": "7.0.0", 43 | "@testing-library/react": "13.3.0", 44 | "@testing-library/react-hooks": "8.0.1", 45 | "@types/jest": "28.1.4", 46 | "@types/lodash.clonedeep": "4.5.7", 47 | "@types/lodash.isequal": "4.5.6", 48 | "@types/node": "^18.0.3", 49 | "@types/react": "18.0.15", 50 | "@types/react-dom": "18.0.6", 51 | "broadcast-channel": "4.13.0", 52 | "codecov": "3.8.3", 53 | "coverage": "0.4.1", 54 | "cross-env": "7.0.3", 55 | "jest": "28.1.2", 56 | "jest-environment-jsdom": "28.1.2", 57 | "npm-check-updates": "15.2.1", 58 | "react": "18.2.0", 59 | "react-dom": "18.2.0", 60 | "react-test-renderer": "18.2.0", 61 | "rimraf": "3.0.2", 62 | "rollup": "2.76.0", 63 | "rollup-plugin-peer-deps-external": "2.2.4", 64 | "rollup-plugin-typescript2": "0.32.1", 65 | "tslib": "^2.4.0", 66 | "typescript": "4.7.4" 67 | }, 68 | "files": [ 69 | "dist" 70 | ] 71 | } -------------------------------------------------------------------------------- /plugins/broadcasted/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": "plugins/broadcasted", 3 | "tags": [] 4 | } 5 | -------------------------------------------------------------------------------- /plugins/broadcasted/rollup.config.js: -------------------------------------------------------------------------------- 1 | import typescript from 'rollup-plugin-typescript2' 2 | import commonjs from '@rollup/plugin-commonjs' 3 | import external from 'rollup-plugin-peer-deps-external' 4 | import resolve from '@rollup/plugin-node-resolve' 5 | import url from '@rollup/plugin-url' 6 | 7 | import pkg from './package.json' 8 | 9 | export default { 10 | input: 'src/index.ts', 11 | output: [ 12 | { 13 | file: pkg.main, 14 | format: 'cjs', 15 | exports: 'named', 16 | sourcemap: true 17 | }, 18 | { 19 | file: pkg.module, 20 | format: 'es', 21 | exports: 'named', 22 | sourcemap: true 23 | } 24 | ], 25 | plugins: [ 26 | external(), 27 | url(), 28 | resolve(), 29 | typescript({ 30 | tsconfig: 'tsconfig.prod.json', 31 | rollupCommonJSResolveHack: true, 32 | clean: true 33 | }), 34 | commonjs() 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /plugins/broadcasted/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './broadcasted'; 2 | -------------------------------------------------------------------------------- /plugins/broadcasted/src/unit.test.ts: -------------------------------------------------------------------------------- 1 | 2 | import { renderHook, act } from '@testing-library/react-hooks'; 3 | 4 | test('placeholder: develop tests', async () => {}); 5 | 6 | -------------------------------------------------------------------------------- /plugins/broadcasted/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "dist", 4 | "target": "es5", 5 | "lib": [ 6 | "dom", 7 | "dom.iterable", 8 | "esnext" 9 | ], 10 | "sourceMap": true, 11 | "noImplicitReturns": true, 12 | "noImplicitThis": true, 13 | "noImplicitAny": true, 14 | "strictNullChecks": true, 15 | "noUnusedLocals": false, 16 | "experimentalDecorators": true, 17 | "newLine": "LF", 18 | "allowJs": true, 19 | "skipLibCheck": true, 20 | "esModuleInterop": true, 21 | "allowSyntheticDefaultImports": true, 22 | "strict": true, 23 | "forceConsistentCasingInFileNames": true, 24 | "suppressImplicitAnyIndexErrors": true, 25 | "module": "esnext", 26 | "moduleResolution": "node", 27 | "resolveJsonModule": true, 28 | "isolatedModules": true, 29 | "noEmit": false, 30 | "jsx": "preserve" 31 | }, 32 | "include": [ 33 | "src", "src/__tests__" 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /plugins/broadcasted/tsconfig.prod.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": [ 4 | "src" 5 | ], 6 | "compilerOptions": { 7 | "lib": ["DOM"], 8 | "jsx": "react", 9 | "allowJs": false, 10 | "declaration": true, 11 | "declarationMap": true, 12 | "isolatedModules": false 13 | }, 14 | "exclude": [ 15 | "src/__tests__" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /plugins/clonable/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2022 Andrey Konstantinov 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 | -------------------------------------------------------------------------------- /plugins/clonable/README.md: -------------------------------------------------------------------------------- 1 | # @hookstate/clonable 2 | 3 | [![license](https://img.shields.io/github/license/avkonst/hookstate)](https://img.shields.io/github/license/avkonst/hookstate) [![npm version](https://img.shields.io/npm/v/@hookstate/clonable.svg?maxAge=300&label=version&colorB=007ec6)](https://www.npmjs.com/package/@hookstate/clonable) 4 | 5 | Plugin for @hookstate/core to define state cloning capabilities. 6 | 7 | [Learn more...](https://hookstate.js.org/docs/extensions-clonable) 8 | 9 | -------------------------------------------------------------------------------- /plugins/clonable/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'ts-jest', 3 | testEnvironment: 'node' 4 | }; -------------------------------------------------------------------------------- /plugins/clonable/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hookstate/clonable", 3 | "version": "4.0.0", 4 | "description": "Plugin for @hookstate/core to define state cloning capabilities.", 5 | "license": "MIT", 6 | "author": { 7 | "name": "Andrey Konstantinov" 8 | }, 9 | "repository": { 10 | "url": "https://github.com/avkonst/hookstate" 11 | }, 12 | "bugs": { 13 | "url": "https://github.com/avkonst/hookstate/issues" 14 | }, 15 | "homepage": "https://github.com/avkonst/hookstate", 16 | "main": "dist/index.js", 17 | "module": "dist/index.es.js", 18 | "jsnext:main": "dist/index.es.js", 19 | "types": "dist/index.d.ts", 20 | "scripts": { 21 | "build": "rollup -c", 22 | "build:w": "rollup -c -w", 23 | "clean": "rimraf dist", 24 | "test": "jest --env=jsdom", 25 | "test:ci": "jest --env=jsdom", 26 | "test:w": "jest --env=jsdom --watch", 27 | "release": "npm publish --access public", 28 | "update:deps": "ncu -u" 29 | }, 30 | "dependencies": {}, 31 | "peerDependencies": { 32 | "@hookstate/core": "^4.0.0" 33 | }, 34 | "devDependencies": { 35 | "@babel/core": "7.18.6", 36 | "@babel/runtime": "7.18.6", 37 | "@hookstate/core": "workspace:*", 38 | "@rollup/plugin-babel": "5.3.1", 39 | "@rollup/plugin-commonjs": "22.0.1", 40 | "@rollup/plugin-node-resolve": "13.3.0", 41 | "@rollup/plugin-url": "7.0.0", 42 | "@testing-library/react": "13.3.0", 43 | "@testing-library/react-hooks": "8.0.1", 44 | "@types/jest": "28.1.4", 45 | "@types/lodash.clonedeep": "4.5.7", 46 | "@types/lodash.isequal": "4.5.6", 47 | "@types/node": "^18.0.3", 48 | "@types/react": "18.0.15", 49 | "@types/react-dom": "18.0.6", 50 | "codecov": "3.8.3", 51 | "coverage": "0.4.1", 52 | "cross-env": "7.0.3", 53 | "jest": "28.1.2", 54 | "jest-environment-jsdom": "28.1.2", 55 | "npm-check-updates": "15.2.1", 56 | "react": "18.2.0", 57 | "react-dom": "18.2.0", 58 | "react-test-renderer": "18.2.0", 59 | "rimraf": "3.0.2", 60 | "rollup": "2.76.0", 61 | "rollup-plugin-peer-deps-external": "2.2.4", 62 | "rollup-plugin-typescript2": "0.32.1", 63 | "tslib": "^2.4.0", 64 | "typescript": "4.7.4" 65 | }, 66 | "files": [ 67 | "dist" 68 | ] 69 | } -------------------------------------------------------------------------------- /plugins/clonable/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": "plugins/clonable", 3 | "tags": [] 4 | } 5 | -------------------------------------------------------------------------------- /plugins/clonable/rollup.config.js: -------------------------------------------------------------------------------- 1 | import typescript from 'rollup-plugin-typescript2' 2 | import commonjs from '@rollup/plugin-commonjs' 3 | import external from 'rollup-plugin-peer-deps-external' 4 | import resolve from '@rollup/plugin-node-resolve' 5 | import url from '@rollup/plugin-url' 6 | 7 | import pkg from './package.json' 8 | 9 | export default { 10 | input: 'src/index.ts', 11 | output: [ 12 | { 13 | file: pkg.main, 14 | format: 'cjs', 15 | exports: 'named', 16 | sourcemap: true 17 | }, 18 | { 19 | file: pkg.module, 20 | format: 'es', 21 | exports: 'named', 22 | sourcemap: true 23 | } 24 | ], 25 | plugins: [ 26 | external(), 27 | url(), 28 | resolve(), 29 | typescript({ 30 | tsconfig: 'tsconfig.prod.json', 31 | rollupCommonJSResolveHack: true, 32 | clean: true 33 | }), 34 | commonjs() 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /plugins/clonable/src/clonable.ts: -------------------------------------------------------------------------------- 1 | import { InferStateValueType, StateValueAtPath, ExtensionFactory, hookstate, State } from '@hookstate/core'; 2 | 3 | export interface Clonable { 4 | clone>(options?: { stealth?: boolean }): S 5 | } 6 | 7 | export function clonable(snapshot: (v: StateValueAtPath) => StateValueAtPath): ExtensionFactory { 8 | return () => ({ 9 | onCreate: () => ({ 10 | clone: (s) => (o) => snapshot(s.get({ noproxy: true, stealth: o?.stealth })) 11 | }) 12 | }) 13 | } 14 | -------------------------------------------------------------------------------- /plugins/clonable/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './clonable'; 2 | -------------------------------------------------------------------------------- /plugins/clonable/src/unit.test.ts: -------------------------------------------------------------------------------- 1 | 2 | import { hookstate, State } from '@hookstate/core'; 3 | import { renderHook, act } from '@testing-library/react-hooks'; 4 | import { Clonable, clonable } from '../src' 5 | 6 | test('check typescript assignability', async () => { 7 | interface Task { 8 | name: string, 9 | snapshot: number 10 | } 11 | 12 | let a = hookstate([{ name: 'string', snapshot: 1 }], clonable(v => v)) 13 | let b: State = a[0] 14 | let c: Task = b.clone() 15 | let c2: Task = a[0].clone() 16 | }); 17 | 18 | -------------------------------------------------------------------------------- /plugins/clonable/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "dist", 4 | "target": "es5", 5 | "lib": [ 6 | "dom", 7 | "dom.iterable", 8 | "esnext" 9 | ], 10 | "sourceMap": true, 11 | "noImplicitReturns": true, 12 | "noImplicitThis": true, 13 | "noImplicitAny": true, 14 | "strictNullChecks": true, 15 | "noUnusedLocals": false, 16 | "experimentalDecorators": true, 17 | "newLine": "LF", 18 | "allowJs": true, 19 | "skipLibCheck": true, 20 | "esModuleInterop": true, 21 | "allowSyntheticDefaultImports": true, 22 | "strict": true, 23 | "forceConsistentCasingInFileNames": true, 24 | "suppressImplicitAnyIndexErrors": true, 25 | "module": "esnext", 26 | "moduleResolution": "node", 27 | "resolveJsonModule": true, 28 | "isolatedModules": true, 29 | "noEmit": false, 30 | "jsx": "preserve" 31 | }, 32 | "include": [ 33 | "src", "src/__tests__" 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /plugins/clonable/tsconfig.prod.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": [ 4 | "src" 5 | ], 6 | "compilerOptions": { 7 | "lib": ["DOM"], 8 | "jsx": "react", 9 | "allowJs": false, 10 | "declaration": true, 11 | "declarationMap": true, 12 | "isolatedModules": false 13 | }, 14 | "exclude": [ 15 | "src/__tests__" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /plugins/comparable/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2022 Andrey Konstantinov 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 | -------------------------------------------------------------------------------- /plugins/comparable/README.md: -------------------------------------------------------------------------------- 1 | # @hookstate/comparable 2 | 3 | [![license](https://img.shields.io/github/license/avkonst/hookstate)](https://img.shields.io/github/license/avkonst/hookstate) [![npm version](https://img.shields.io/npm/v/@hookstate/comparable.svg?maxAge=300&label=version&colorB=007ec6)](https://www.npmjs.com/package/@hookstate/comparable) 4 | 5 | Plugin for @hookstate/core to enable state values comparison. 6 | 7 | [Learn more...](https://hookstate.js.org/docs/extensions-comparable) 8 | 9 | -------------------------------------------------------------------------------- /plugins/comparable/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'ts-jest', 3 | testEnvironment: 'node' 4 | }; -------------------------------------------------------------------------------- /plugins/comparable/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hookstate/comparable", 3 | "version": "4.0.0", 4 | "description": "Plugin for @hookstate/core to enable state values comparison.", 5 | "license": "MIT", 6 | "author": { 7 | "name": "Andrey Konstantinov" 8 | }, 9 | "repository": { 10 | "url": "https://github.com/avkonst/hookstate" 11 | }, 12 | "bugs": { 13 | "url": "https://github.com/avkonst/hookstate/issues" 14 | }, 15 | "homepage": "https://github.com/avkonst/hookstate", 16 | "main": "dist/index.js", 17 | "module": "dist/index.es.js", 18 | "jsnext:main": "dist/index.es.js", 19 | "types": "dist/index.d.ts", 20 | "scripts": { 21 | "build": "rollup -c", 22 | "build:w": "rollup -c -w", 23 | "clean": "rimraf dist", 24 | "test": "jest --env=jsdom", 25 | "test:ci": "jest --env=jsdom", 26 | "test:w": "jest --env=jsdom --watch", 27 | "release": "npm publish --access public", 28 | "update:deps": "ncu -u" 29 | }, 30 | "dependencies": {}, 31 | "peerDependencies": { 32 | "@hookstate/core": "^4.0.0" 33 | }, 34 | "devDependencies": { 35 | "@babel/core": "7.18.6", 36 | "@babel/runtime": "7.18.6", 37 | "@hookstate/core": "workspace:*", 38 | "@rollup/plugin-babel": "5.3.1", 39 | "@rollup/plugin-commonjs": "22.0.1", 40 | "@rollup/plugin-node-resolve": "13.3.0", 41 | "@rollup/plugin-url": "7.0.0", 42 | "@testing-library/react": "13.3.0", 43 | "@testing-library/react-hooks": "8.0.1", 44 | "@types/jest": "28.1.4", 45 | "@types/lodash.clonedeep": "4.5.7", 46 | "@types/lodash.isequal": "4.5.6", 47 | "@types/node": "^18.0.3", 48 | "@types/react": "18.0.15", 49 | "@types/react-dom": "18.0.6", 50 | "codecov": "3.8.3", 51 | "coverage": "0.4.1", 52 | "cross-env": "7.0.3", 53 | "jest": "28.1.2", 54 | "jest-environment-jsdom": "28.1.2", 55 | "npm-check-updates": "15.2.1", 56 | "react": "18.2.0", 57 | "react-dom": "18.2.0", 58 | "react-test-renderer": "18.2.0", 59 | "rimraf": "3.0.2", 60 | "rollup": "2.76.0", 61 | "rollup-plugin-peer-deps-external": "2.2.4", 62 | "rollup-plugin-typescript2": "0.32.1", 63 | "tslib": "^2.4.0", 64 | "typescript": "4.7.4" 65 | }, 66 | "files": [ 67 | "dist" 68 | ] 69 | } -------------------------------------------------------------------------------- /plugins/comparable/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": "plugins/comparable", 3 | "tags": [] 4 | } 5 | -------------------------------------------------------------------------------- /plugins/comparable/rollup.config.js: -------------------------------------------------------------------------------- 1 | import typescript from 'rollup-plugin-typescript2' 2 | import commonjs from '@rollup/plugin-commonjs' 3 | import external from 'rollup-plugin-peer-deps-external' 4 | import resolve from '@rollup/plugin-node-resolve' 5 | import url from '@rollup/plugin-url' 6 | 7 | import pkg from './package.json' 8 | 9 | export default { 10 | input: 'src/index.ts', 11 | output: [ 12 | { 13 | file: pkg.main, 14 | format: 'cjs', 15 | exports: 'named', 16 | sourcemap: true 17 | }, 18 | { 19 | file: pkg.module, 20 | format: 'es', 21 | exports: 'named', 22 | sourcemap: true 23 | } 24 | ], 25 | plugins: [ 26 | external(), 27 | url(), 28 | resolve(), 29 | typescript({ 30 | tsconfig: 'tsconfig.prod.json', 31 | rollupCommonJSResolveHack: true, 32 | clean: true 33 | }), 34 | commonjs() 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /plugins/comparable/src/comparable.ts: -------------------------------------------------------------------------------- 1 | import { ExtensionFactory, InferStateValueType, StateValueAtPath } from '@hookstate/core'; 2 | 3 | export interface Comparable { 4 | compare>(other: S): number 5 | equals>(other: S): boolean 6 | } 7 | 8 | export function comparable(compare: (v1: StateValueAtPath, v2: StateValueAtPath) => number): ExtensionFactory { 9 | return () => ({ 10 | onCreate: () => ({ 11 | compare: (s) => (other) => compare(s.get({ noproxy: true }), other), 12 | equals: (s) => (other) => compare(s.get({ noproxy: true }), other) === 0, 13 | }) 14 | }) 15 | } 16 | -------------------------------------------------------------------------------- /plugins/comparable/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './comparable'; 2 | -------------------------------------------------------------------------------- /plugins/comparable/src/unit.test.ts: -------------------------------------------------------------------------------- 1 | 2 | import { renderHook, act } from '@testing-library/react-hooks'; 3 | 4 | test('placeholder: develop tests', async () => {}); 5 | 6 | -------------------------------------------------------------------------------- /plugins/comparable/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "dist", 4 | "target": "es5", 5 | "lib": [ 6 | "dom", 7 | "dom.iterable", 8 | "esnext" 9 | ], 10 | "sourceMap": true, 11 | "noImplicitReturns": true, 12 | "noImplicitThis": true, 13 | "noImplicitAny": true, 14 | "strictNullChecks": true, 15 | "noUnusedLocals": false, 16 | "experimentalDecorators": true, 17 | "newLine": "LF", 18 | "allowJs": true, 19 | "skipLibCheck": true, 20 | "esModuleInterop": true, 21 | "allowSyntheticDefaultImports": true, 22 | "strict": true, 23 | "forceConsistentCasingInFileNames": true, 24 | "suppressImplicitAnyIndexErrors": true, 25 | "module": "esnext", 26 | "moduleResolution": "node", 27 | "resolveJsonModule": true, 28 | "isolatedModules": true, 29 | "noEmit": false, 30 | "jsx": "preserve" 31 | }, 32 | "include": [ 33 | "src", "src/__tests__" 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /plugins/comparable/tsconfig.prod.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": [ 4 | "src" 5 | ], 6 | "compilerOptions": { 7 | "lib": ["DOM"], 8 | "jsx": "react", 9 | "allowJs": false, 10 | "declaration": true, 11 | "declarationMap": true, 12 | "isolatedModules": false 13 | }, 14 | "exclude": [ 15 | "src/__tests__" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /plugins/devtools/.env: -------------------------------------------------------------------------------- 1 | SKIP_PREFLIGHT_CHECK=true 2 | -------------------------------------------------------------------------------- /plugins/devtools/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2022 Andrey Konstantinov 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 | -------------------------------------------------------------------------------- /plugins/devtools/README.md: -------------------------------------------------------------------------------- 1 | # @hookstate/devtools 2 | 3 | [![license](https://img.shields.io/github/license/avkonst/hookstate)](https://img.shields.io/github/license/avkonst/hookstate) [![npm version](https://img.shields.io/npm/v/@hookstate/devtools.svg?maxAge=300&label=version&colorB=007ec6)](https://www.npmjs.com/package/@hookstate/devtools) 4 | 5 | Development tools for @hookstate/core. 6 | 7 | [Learn more...](https://hookstate.js.org/docs/devtools) -------------------------------------------------------------------------------- /plugins/devtools/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'ts-jest', 3 | testEnvironment: 'node', 4 | }; -------------------------------------------------------------------------------- /plugins/devtools/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hookstate/devtools", 3 | "version": "4.0.3", 4 | "description": "Development tools plugin for @hookstate/core.", 5 | "license": "MIT", 6 | "author": { 7 | "name": "Andrey Konstantinov" 8 | }, 9 | "repository": { 10 | "url": "https://github.com/avkonst/hookstate" 11 | }, 12 | "bugs": { 13 | "url": "https://github.com/avkonst/hookstate/issues" 14 | }, 15 | "homepage": "https://github.com/avkonst/hookstate", 16 | "main": "dist/index.js", 17 | "module": "dist/index.es.js", 18 | "jsnext:main": "dist/index.es.js", 19 | "types": "dist/index.d.ts", 20 | "scripts": { 21 | "build": "rollup -c", 22 | "build:w": "rollup -c -w", 23 | "clean": "rimraf dist", 24 | "test": "jest --env=jsdom", 25 | "test:ci": "jest --env=jsdom", 26 | "test:w": "jest --env=jsdom --watch", 27 | "release": "npm publish --access public", 28 | "update:deps": "ncu -u" 29 | }, 30 | "dependencies": { 31 | "redux": "4.2.0", 32 | "redux-devtools-extension": "2.13.9" 33 | }, 34 | "peerDependencies": { 35 | "@hookstate/core": "^4.0.0" 36 | }, 37 | "devDependencies": { 38 | "@babel/core": "7.18.6", 39 | "@babel/runtime": "7.18.6", 40 | "@hookstate/core": "workspace:*", 41 | "@rollup/plugin-babel": "5.3.1", 42 | "@rollup/plugin-commonjs": "22.0.1", 43 | "@rollup/plugin-node-resolve": "13.3.0", 44 | "@rollup/plugin-url": "7.0.0", 45 | "@testing-library/react": "13.3.0", 46 | "@testing-library/react-hooks": "8.0.1", 47 | "@types/jest": "28.1.4", 48 | "@types/lodash.clonedeep": "4.5.7", 49 | "@types/lodash.isequal": "4.5.6", 50 | "@types/node": "^18.0.3", 51 | "@types/react": "18.0.15", 52 | "@types/react-dom": "18.0.6", 53 | "codecov": "3.8.3", 54 | "coverage": "0.4.1", 55 | "cross-env": "7.0.3", 56 | "jest-mock-console": "^2.0.0", 57 | "jest": "28.1.2", 58 | "jest-environment-jsdom": "28.1.2", 59 | "npm-check-updates": "15.2.1", 60 | "react": "18.2.0", 61 | "react-dom": "18.2.0", 62 | "react-test-renderer": "18.2.0", 63 | "rimraf": "3.0.2", 64 | "rollup": "2.76.0", 65 | "rollup-plugin-peer-deps-external": "2.2.4", 66 | "rollup-plugin-typescript2": "0.32.1", 67 | "ts-jest": "28.0.5", 68 | "tslib": "^2.4.0", 69 | "typescript": "4.7.4" 70 | }, 71 | "files": [ 72 | "dist" 73 | ] 74 | } 75 | -------------------------------------------------------------------------------- /plugins/devtools/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": "plugins/devtools", 3 | "tags": [] 4 | } 5 | -------------------------------------------------------------------------------- /plugins/devtools/rollup.config.js: -------------------------------------------------------------------------------- 1 | import typescript from 'rollup-plugin-typescript2' 2 | import commonjs from '@rollup/plugin-commonjs' 3 | import external from 'rollup-plugin-peer-deps-external' 4 | import resolve from '@rollup/plugin-node-resolve' 5 | import url from '@rollup/plugin-url' 6 | 7 | import pkg from './package.json' 8 | 9 | export default { 10 | input: 'src/index.ts', 11 | output: [ 12 | { 13 | file: pkg.main, 14 | format: 'cjs', 15 | exports: 'named', 16 | sourcemap: true 17 | }, 18 | { 19 | file: pkg.module, 20 | format: 'es', 21 | exports: 'named', 22 | sourcemap: true 23 | } 24 | ], 25 | plugins: [ 26 | external(), 27 | url(), 28 | resolve(), 29 | typescript({ 30 | tsconfig: 'tsconfig.prod.json', 31 | rollupCommonJSResolveHack: true, 32 | clean: true 33 | }), 34 | commonjs() 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /plugins/devtools/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './devtools'; 2 | -------------------------------------------------------------------------------- /plugins/devtools/src/unit.test.ts: -------------------------------------------------------------------------------- 1 | 2 | import { renderHook, act } from '@testing-library/react-hooks'; 3 | 4 | test('placeholder: develop tests', async () => {}); 5 | 6 | -------------------------------------------------------------------------------- /plugins/devtools/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "dist", 4 | "target": "es5", 5 | "lib": [ 6 | "dom", 7 | "dom.iterable", 8 | "esnext" 9 | ], 10 | "sourceMap": true, 11 | "noImplicitReturns": true, 12 | "noImplicitThis": true, 13 | "noImplicitAny": true, 14 | "strictNullChecks": true, 15 | "noUnusedLocals": false, 16 | "experimentalDecorators": true, 17 | "newLine": "LF", 18 | "allowJs": true, 19 | "skipLibCheck": true, 20 | "esModuleInterop": true, 21 | "allowSyntheticDefaultImports": true, 22 | "strict": true, 23 | "forceConsistentCasingInFileNames": true, 24 | "suppressImplicitAnyIndexErrors": true, 25 | "module": "esnext", 26 | "moduleResolution": "node", 27 | "resolveJsonModule": true, 28 | "isolatedModules": true, 29 | "noEmit": false, 30 | "jsx": "preserve" 31 | }, 32 | "include": [ 33 | "src", "src/__tests__" 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /plugins/devtools/tsconfig.prod.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": [ 4 | "src" 5 | ], 6 | "compilerOptions": { 7 | "lib": ["DOM"], 8 | "jsx": "react", 9 | "allowJs": false, 10 | "declaration": true, 11 | "declarationMap": true, 12 | "isolatedModules": false 13 | }, 14 | "exclude": [ 15 | "src/__tests__" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /plugins/identifiable/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2022 Andrey Konstantinov 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 | -------------------------------------------------------------------------------- /plugins/identifiable/README.md: -------------------------------------------------------------------------------- 1 | # @hookstate/identifiable 2 | 3 | [![license](https://img.shields.io/github/license/avkonst/hookstate)](https://img.shields.io/github/license/avkonst/hookstate) [![npm version](https://img.shields.io/npm/v/@hookstate/identifiable.svg?maxAge=300&label=version&colorB=007ec6)](https://www.npmjs.com/package/@hookstate/identifiable) 4 | 5 | Plugin for @hookstate/core to enable state labeling and identification by name. 6 | 7 | [Learn more...](https://hookstate.js.org/docs/extensions-identifiable) 8 | 9 | -------------------------------------------------------------------------------- /plugins/identifiable/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'ts-jest', 3 | testEnvironment: 'node' 4 | }; -------------------------------------------------------------------------------- /plugins/identifiable/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hookstate/identifiable", 3 | "version": "4.0.0", 4 | "description": "Plugin for @hookstate/core to enable state labeling and identification by name.", 5 | "license": "MIT", 6 | "author": { 7 | "name": "Andrey Konstantinov" 8 | }, 9 | "repository": { 10 | "url": "https://github.com/avkonst/hookstate" 11 | }, 12 | "bugs": { 13 | "url": "https://github.com/avkonst/hookstate/issues" 14 | }, 15 | "homepage": "https://github.com/avkonst/hookstate", 16 | "main": "dist/index.js", 17 | "module": "dist/index.es.js", 18 | "jsnext:main": "dist/index.es.js", 19 | "types": "dist/index.d.ts", 20 | "scripts": { 21 | "build": "rollup -c", 22 | "build:w": "rollup -c -w", 23 | "clean": "rimraf dist", 24 | "test": "jest --env=jsdom", 25 | "test:ci": "jest --env=jsdom", 26 | "test:w": "jest --env=jsdom --watch", 27 | "release": "npm publish --access public", 28 | "update:deps": "ncu -u" 29 | }, 30 | "dependencies": {}, 31 | "peerDependencies": { 32 | "@hookstate/core": "^4.0.0" 33 | }, 34 | "devDependencies": { 35 | "@babel/core": "7.18.6", 36 | "@babel/runtime": "7.18.6", 37 | "@hookstate/core": "workspace:*", 38 | "@rollup/plugin-babel": "5.3.1", 39 | "@rollup/plugin-commonjs": "22.0.1", 40 | "@rollup/plugin-node-resolve": "13.3.0", 41 | "@rollup/plugin-url": "7.0.0", 42 | "@testing-library/react": "13.3.0", 43 | "@testing-library/react-hooks": "8.0.1", 44 | "@types/jest": "28.1.4", 45 | "@types/lodash.clonedeep": "4.5.7", 46 | "@types/lodash.isequal": "4.5.6", 47 | "@types/node": "^18.0.3", 48 | "@types/react": "18.0.15", 49 | "@types/react-dom": "18.0.6", 50 | "codecov": "3.8.3", 51 | "coverage": "0.4.1", 52 | "cross-env": "7.0.3", 53 | "jest": "28.1.2", 54 | "jest-environment-jsdom": "28.1.2", 55 | "npm-check-updates": "15.2.1", 56 | "react": "18.2.0", 57 | "react-dom": "18.2.0", 58 | "react-test-renderer": "18.2.0", 59 | "rimraf": "3.0.2", 60 | "rollup": "2.76.0", 61 | "rollup-plugin-peer-deps-external": "2.2.4", 62 | "rollup-plugin-typescript2": "0.32.1", 63 | "tslib": "^2.4.0", 64 | "typescript": "4.7.4" 65 | }, 66 | "files": [ 67 | "dist" 68 | ] 69 | } -------------------------------------------------------------------------------- /plugins/identifiable/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": "plugins/identifiable", 3 | "tags": [] 4 | } 5 | -------------------------------------------------------------------------------- /plugins/identifiable/rollup.config.js: -------------------------------------------------------------------------------- 1 | import typescript from 'rollup-plugin-typescript2' 2 | import commonjs from '@rollup/plugin-commonjs' 3 | import external from 'rollup-plugin-peer-deps-external' 4 | import resolve from '@rollup/plugin-node-resolve' 5 | import url from '@rollup/plugin-url' 6 | 7 | import pkg from './package.json' 8 | 9 | export default { 10 | input: 'src/index.ts', 11 | output: [ 12 | { 13 | file: pkg.main, 14 | format: 'cjs', 15 | exports: 'named', 16 | sourcemap: true 17 | }, 18 | { 19 | file: pkg.module, 20 | format: 'es', 21 | exports: 'named', 22 | sourcemap: true 23 | } 24 | ], 25 | plugins: [ 26 | external(), 27 | url(), 28 | resolve(), 29 | typescript({ 30 | tsconfig: 'tsconfig.prod.json', 31 | rollupCommonJSResolveHack: true, 32 | clean: true 33 | }), 34 | commonjs() 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /plugins/identifiable/src/identifiable.ts: -------------------------------------------------------------------------------- 1 | import { ExtensionFactory } from '@hookstate/core'; 2 | 3 | export interface Identifiable { 4 | readonly identifier: string 5 | } 6 | 7 | export function identifiable(identifier: string): ExtensionFactory { 8 | return () => ({ 9 | onCreate: () => ({ 10 | identifier: _ => identifier 11 | }) 12 | }) 13 | } 14 | -------------------------------------------------------------------------------- /plugins/identifiable/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './identifiable'; 2 | -------------------------------------------------------------------------------- /plugins/identifiable/src/unit.test.ts: -------------------------------------------------------------------------------- 1 | 2 | import { renderHook, act } from '@testing-library/react-hooks'; 3 | 4 | test('placeholder: develop tests', async () => {}); 5 | 6 | -------------------------------------------------------------------------------- /plugins/identifiable/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "dist", 4 | "target": "es5", 5 | "lib": [ 6 | "dom", 7 | "dom.iterable", 8 | "esnext" 9 | ], 10 | "sourceMap": true, 11 | "noImplicitReturns": true, 12 | "noImplicitThis": true, 13 | "noImplicitAny": true, 14 | "strictNullChecks": true, 15 | "noUnusedLocals": false, 16 | "experimentalDecorators": true, 17 | "newLine": "LF", 18 | "allowJs": true, 19 | "skipLibCheck": true, 20 | "esModuleInterop": true, 21 | "allowSyntheticDefaultImports": true, 22 | "strict": true, 23 | "forceConsistentCasingInFileNames": true, 24 | "suppressImplicitAnyIndexErrors": true, 25 | "module": "esnext", 26 | "moduleResolution": "node", 27 | "resolveJsonModule": true, 28 | "isolatedModules": true, 29 | "noEmit": false, 30 | "jsx": "preserve" 31 | }, 32 | "include": [ 33 | "src", "src/__tests__" 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /plugins/identifiable/tsconfig.prod.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": [ 4 | "src" 5 | ], 6 | "compilerOptions": { 7 | "lib": ["DOM"], 8 | "jsx": "react", 9 | "allowJs": false, 10 | "declaration": true, 11 | "declarationMap": true, 12 | "isolatedModules": false 13 | }, 14 | "exclude": [ 15 | "src/__tests__" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /plugins/initializable/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2022 Andrey Konstantinov 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 | -------------------------------------------------------------------------------- /plugins/initializable/README.md: -------------------------------------------------------------------------------- 1 | # @hookstate/initializable 2 | 3 | [![license](https://img.shields.io/github/license/avkonst/hookstate)](https://img.shields.io/github/license/avkonst/hookstate) [![npm version](https://img.shields.io/npm/v/@hookstate/initializable.svg?maxAge=300&label=version&colorB=007ec6)](https://www.npmjs.com/package/@hookstate/initializable) 4 | 5 | Plugin for @hookstate/core to enable one off initialization callback. 6 | 7 | [Learn more...](https://hookstate.js.org/docs/extensions-initializable) 8 | 9 | -------------------------------------------------------------------------------- /plugins/initializable/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'ts-jest', 3 | testEnvironment: 'node' 4 | }; -------------------------------------------------------------------------------- /plugins/initializable/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hookstate/initializable", 3 | "version": "4.0.0", 4 | "description": "Plugin for @hookstate/core to enable one off initialization callback.", 5 | "license": "MIT", 6 | "author": { 7 | "name": "Andrey Konstantinov" 8 | }, 9 | "repository": { 10 | "url": "https://github.com/avkonst/hookstate" 11 | }, 12 | "bugs": { 13 | "url": "https://github.com/avkonst/hookstate/issues" 14 | }, 15 | "homepage": "https://github.com/avkonst/hookstate", 16 | "main": "dist/index.js", 17 | "module": "dist/index.es.js", 18 | "jsnext:main": "dist/index.es.js", 19 | "types": "dist/index.d.ts", 20 | "scripts": { 21 | "build": "rollup -c", 22 | "build:w": "rollup -c -w", 23 | "clean": "rimraf dist", 24 | "test": "jest --env=jsdom", 25 | "test:ci": "jest --env=jsdom", 26 | "test:w": "jest --env=jsdom --watch", 27 | "release": "npm publish --access public", 28 | "update:deps": "ncu -u" 29 | }, 30 | "dependencies": {}, 31 | "peerDependencies": { 32 | "@hookstate/core": "^4.0.0" 33 | }, 34 | "devDependencies": { 35 | "@babel/core": "7.18.6", 36 | "@babel/runtime": "7.18.6", 37 | "@hookstate/core": "workspace:*", 38 | "@rollup/plugin-babel": "5.3.1", 39 | "@rollup/plugin-commonjs": "22.0.1", 40 | "@rollup/plugin-node-resolve": "13.3.0", 41 | "@rollup/plugin-url": "7.0.0", 42 | "@testing-library/react": "13.3.0", 43 | "@testing-library/react-hooks": "8.0.1", 44 | "@types/jest": "28.1.4", 45 | "@types/lodash.clonedeep": "4.5.7", 46 | "@types/lodash.isequal": "4.5.6", 47 | "@types/node": "^18.0.3", 48 | "@types/react": "18.0.15", 49 | "@types/react-dom": "18.0.6", 50 | "codecov": "3.8.3", 51 | "coverage": "0.4.1", 52 | "cross-env": "7.0.3", 53 | "jest": "28.1.2", 54 | "jest-environment-jsdom": "28.1.2", 55 | "npm-check-updates": "15.2.1", 56 | "react": "18.2.0", 57 | "react-dom": "18.2.0", 58 | "react-test-renderer": "18.2.0", 59 | "rimraf": "3.0.2", 60 | "rollup": "2.76.0", 61 | "rollup-plugin-peer-deps-external": "2.2.4", 62 | "rollup-plugin-typescript2": "0.32.1", 63 | "tslib": "^2.4.0", 64 | "typescript": "4.7.4" 65 | }, 66 | "files": [ 67 | "dist" 68 | ] 69 | } -------------------------------------------------------------------------------- /plugins/initializable/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": "plugins/initializable", 3 | "tags": [] 4 | } 5 | -------------------------------------------------------------------------------- /plugins/initializable/rollup.config.js: -------------------------------------------------------------------------------- 1 | import typescript from 'rollup-plugin-typescript2' 2 | import commonjs from '@rollup/plugin-commonjs' 3 | import external from 'rollup-plugin-peer-deps-external' 4 | import resolve from '@rollup/plugin-node-resolve' 5 | import url from '@rollup/plugin-url' 6 | 7 | import pkg from './package.json' 8 | 9 | export default { 10 | input: 'src/index.ts', 11 | output: [ 12 | { 13 | file: pkg.main, 14 | format: 'cjs', 15 | exports: 'named', 16 | sourcemap: true 17 | }, 18 | { 19 | file: pkg.module, 20 | format: 'es', 21 | exports: 'named', 22 | sourcemap: true 23 | } 24 | ], 25 | plugins: [ 26 | external(), 27 | url(), 28 | resolve(), 29 | typescript({ 30 | tsconfig: 'tsconfig.prod.json', 31 | rollupCommonJSResolveHack: true, 32 | clean: true 33 | }), 34 | commonjs() 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /plugins/initializable/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './initializable'; 2 | -------------------------------------------------------------------------------- /plugins/initializable/src/initializable.ts: -------------------------------------------------------------------------------- 1 | import { ExtensionFactory, State } from '@hookstate/core'; 2 | 3 | export interface Initializable {} 4 | 5 | export function initializable(initializer: (s: State) => (void | ((s: State) => void))): 6 | ExtensionFactory { 7 | let uninitializer: ((s: State) => void) | void = undefined; 8 | return () => ({ 9 | onInit: (s) => { 10 | uninitializer = initializer(s as unknown as State) 11 | }, 12 | onDestroy: (s) => { 13 | if (uninitializer) { 14 | uninitializer(s as unknown as State) 15 | } 16 | } 17 | }) 18 | } 19 | -------------------------------------------------------------------------------- /plugins/initializable/src/unit.test.ts: -------------------------------------------------------------------------------- 1 | 2 | import { renderHook, act } from '@testing-library/react-hooks'; 3 | 4 | test('placeholder: develop tests', async () => {}); 5 | 6 | -------------------------------------------------------------------------------- /plugins/initializable/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "dist", 4 | "target": "es5", 5 | "lib": [ 6 | "dom", 7 | "dom.iterable", 8 | "esnext" 9 | ], 10 | "sourceMap": true, 11 | "noImplicitReturns": true, 12 | "noImplicitThis": true, 13 | "noImplicitAny": true, 14 | "strictNullChecks": true, 15 | "noUnusedLocals": false, 16 | "experimentalDecorators": true, 17 | "newLine": "LF", 18 | "allowJs": true, 19 | "skipLibCheck": true, 20 | "esModuleInterop": true, 21 | "allowSyntheticDefaultImports": true, 22 | "strict": true, 23 | "forceConsistentCasingInFileNames": true, 24 | "suppressImplicitAnyIndexErrors": true, 25 | "module": "esnext", 26 | "moduleResolution": "node", 27 | "resolveJsonModule": true, 28 | "isolatedModules": true, 29 | "noEmit": false, 30 | "jsx": "preserve" 31 | }, 32 | "include": [ 33 | "src", "src/__tests__" 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /plugins/initializable/tsconfig.prod.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": [ 4 | "src" 5 | ], 6 | "compilerOptions": { 7 | "lib": ["DOM"], 8 | "jsx": "react", 9 | "allowJs": false, 10 | "declaration": true, 11 | "declarationMap": true, 12 | "isolatedModules": false 13 | }, 14 | "exclude": [ 15 | "src/__tests__" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /plugins/localstored/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2022 Andrey Konstantinov 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 | -------------------------------------------------------------------------------- /plugins/localstored/README.md: -------------------------------------------------------------------------------- 1 | # @hookstate/localstored 2 | 3 | [![license](https://img.shields.io/github/license/avkonst/hookstate)](https://img.shields.io/github/license/avkonst/hookstate) [![npm version](https://img.shields.io/npm/v/@hookstate/localstored.svg?maxAge=300&label=version&colorB=007ec6)](https://www.npmjs.com/package/@hookstate/localstored) 4 | 5 | Plugin for @hookstate/core to enable persistence of managed data states to browser's local storage. 6 | 7 | [Learn more...](https://hookstate.js.org/docs/extensions-localstored) 8 | 9 | -------------------------------------------------------------------------------- /plugins/localstored/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'ts-jest', 3 | testEnvironment: 'node' 4 | }; -------------------------------------------------------------------------------- /plugins/localstored/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hookstate/localstored", 3 | "version": "4.0.2", 4 | "description": "Plugin for @hookstate/core to enable persistence of managed data states to browser's local storage.", 5 | "license": "MIT", 6 | "author": { 7 | "name": "Andrey Konstantinov" 8 | }, 9 | "repository": { 10 | "url": "https://github.com/avkonst/hookstate" 11 | }, 12 | "bugs": { 13 | "url": "https://github.com/avkonst/hookstate/issues" 14 | }, 15 | "homepage": "https://github.com/avkonst/hookstate", 16 | "main": "dist/index.js", 17 | "module": "dist/index.es.js", 18 | "jsnext:main": "dist/index.es.js", 19 | "types": "dist/index.d.ts", 20 | "scripts": { 21 | "build": "rollup -c", 22 | "build:w": "rollup -c -w", 23 | "clean": "rimraf dist", 24 | "test": "jest --env=jsdom", 25 | "test:ci": "jest --env=jsdom", 26 | "test:w": "jest --env=jsdom --watch", 27 | "release": "npm publish --access public", 28 | "update:deps": "ncu -u" 29 | }, 30 | "dependencies": {}, 31 | "peerDependencies": { 32 | "@hookstate/core": "^4.0.0" 33 | }, 34 | "devDependencies": { 35 | "@babel/core": "7.18.6", 36 | "@babel/runtime": "7.18.6", 37 | "@hookstate/core": "workspace:*", 38 | "@rollup/plugin-babel": "5.3.1", 39 | "@rollup/plugin-commonjs": "22.0.1", 40 | "@rollup/plugin-node-resolve": "13.3.0", 41 | "@rollup/plugin-url": "7.0.0", 42 | "@testing-library/react": "13.3.0", 43 | "@testing-library/react-hooks": "8.0.1", 44 | "@types/jest": "28.1.4", 45 | "@types/lodash.clonedeep": "4.5.7", 46 | "@types/lodash.isequal": "4.5.6", 47 | "@types/node": "^18.0.3", 48 | "@types/react": "18.0.15", 49 | "@types/react-dom": "18.0.6", 50 | "codecov": "3.8.3", 51 | "coverage": "0.4.1", 52 | "cross-env": "7.0.3", 53 | "jest": "28.1.2", 54 | "jest-environment-jsdom": "28.1.2", 55 | "npm-check-updates": "15.2.1", 56 | "react": "18.2.0", 57 | "react-dom": "18.2.0", 58 | "react-test-renderer": "18.2.0", 59 | "rimraf": "3.0.2", 60 | "rollup": "2.76.0", 61 | "rollup-plugin-peer-deps-external": "2.2.4", 62 | "rollup-plugin-typescript2": "0.32.1", 63 | "tslib": "^2.4.0", 64 | "typescript": "4.7.4" 65 | }, 66 | "files": [ 67 | "dist" 68 | ] 69 | } -------------------------------------------------------------------------------- /plugins/localstored/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": "plugins/localstored", 3 | "tags": [] 4 | } 5 | -------------------------------------------------------------------------------- /plugins/localstored/rollup.config.js: -------------------------------------------------------------------------------- 1 | import typescript from 'rollup-plugin-typescript2' 2 | import commonjs from '@rollup/plugin-commonjs' 3 | import external from 'rollup-plugin-peer-deps-external' 4 | import resolve from '@rollup/plugin-node-resolve' 5 | import url from '@rollup/plugin-url' 6 | 7 | import pkg from './package.json' 8 | 9 | export default { 10 | input: 'src/index.ts', 11 | output: [ 12 | { 13 | file: pkg.main, 14 | format: 'cjs', 15 | exports: 'named', 16 | sourcemap: true 17 | }, 18 | { 19 | file: pkg.module, 20 | format: 'es', 21 | exports: 'named', 22 | sourcemap: true 23 | } 24 | ], 25 | plugins: [ 26 | external(), 27 | url(), 28 | resolve(), 29 | typescript({ 30 | tsconfig: 'tsconfig.prod.json', 31 | rollupCommonJSResolveHack: true, 32 | clean: true 33 | }), 34 | commonjs() 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /plugins/localstored/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './localstorage'; 2 | -------------------------------------------------------------------------------- /plugins/localstored/src/unit.test.ts: -------------------------------------------------------------------------------- 1 | 2 | import { renderHook, act } from '@testing-library/react-hooks'; 3 | 4 | test('placeholder: develop tests', async () => {}); 5 | 6 | -------------------------------------------------------------------------------- /plugins/localstored/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "dist", 4 | "target": "es5", 5 | "lib": [ 6 | "dom", 7 | "dom.iterable", 8 | "esnext" 9 | ], 10 | "sourceMap": true, 11 | "noImplicitReturns": true, 12 | "noImplicitThis": true, 13 | "noImplicitAny": true, 14 | "strictNullChecks": true, 15 | "noUnusedLocals": false, 16 | "experimentalDecorators": true, 17 | "newLine": "LF", 18 | "allowJs": true, 19 | "skipLibCheck": true, 20 | "esModuleInterop": true, 21 | "allowSyntheticDefaultImports": true, 22 | "strict": true, 23 | "forceConsistentCasingInFileNames": true, 24 | "suppressImplicitAnyIndexErrors": true, 25 | "module": "esnext", 26 | "moduleResolution": "node", 27 | "resolveJsonModule": true, 28 | "isolatedModules": true, 29 | "noEmit": false, 30 | "jsx": "preserve" 31 | }, 32 | "include": [ 33 | "src", "src/__tests__" 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /plugins/localstored/tsconfig.prod.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": [ 4 | "src" 5 | ], 6 | "compilerOptions": { 7 | "lib": ["DOM"], 8 | "jsx": "react", 9 | "allowJs": false, 10 | "declaration": true, 11 | "declarationMap": true, 12 | "isolatedModules": false 13 | }, 14 | "exclude": [ 15 | "src/__tests__" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /plugins/logged/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2022 Andrey Konstantinov 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 | -------------------------------------------------------------------------------- /plugins/logged/README.md: -------------------------------------------------------------------------------- 1 | # @hookstate/localstored 2 | 3 | [![license](https://img.shields.io/github/license/avkonst/hookstate)](https://img.shields.io/github/license/avkonst/hookstate) [![npm version](https://img.shields.io/npm/v/@hookstate/logged.svg?maxAge=300&label=version&colorB=007ec6)](https://www.npmjs.com/package/@hookstate/logged) 4 | 5 | Plugin for @hookstate/core to enable logging into console when state is created/updated/destroyed. 6 | 7 | [Learn more...](https://hookstate.js.org/docs/extensions-logged) 8 | 9 | -------------------------------------------------------------------------------- /plugins/logged/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'ts-jest', 3 | testEnvironment: 'node' 4 | }; -------------------------------------------------------------------------------- /plugins/logged/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hookstate/logged", 3 | "version": "4.0.0", 4 | "description": "Plugin for @hookstate/core to enable logging into console when state is created/updated/destroyed.", 5 | "license": "MIT", 6 | "author": { 7 | "name": "Andrey Konstantinov" 8 | }, 9 | "repository": { 10 | "url": "https://github.com/avkonst/hookstate" 11 | }, 12 | "bugs": { 13 | "url": "https://github.com/avkonst/hookstate/issues" 14 | }, 15 | "homepage": "https://github.com/avkonst/hookstate", 16 | "main": "dist/index.js", 17 | "module": "dist/index.es.js", 18 | "jsnext:main": "dist/index.es.js", 19 | "types": "dist/index.d.ts", 20 | "scripts": { 21 | "build": "rollup -c", 22 | "build:w": "rollup -c -w", 23 | "clean": "rimraf dist", 24 | "test": "jest --env=jsdom", 25 | "test:ci": "jest --env=jsdom", 26 | "test:w": "jest --env=jsdom --watch", 27 | "release": "npm publish --access public", 28 | "update:deps": "ncu -u" 29 | }, 30 | "dependencies": {}, 31 | "peerDependencies": { 32 | "@hookstate/core": "^4.0.0" 33 | }, 34 | "devDependencies": { 35 | "@babel/core": "7.18.6", 36 | "@babel/runtime": "7.18.6", 37 | "@hookstate/core": "workspace:*", 38 | "@rollup/plugin-babel": "5.3.1", 39 | "@rollup/plugin-commonjs": "22.0.1", 40 | "@rollup/plugin-node-resolve": "13.3.0", 41 | "@rollup/plugin-url": "7.0.0", 42 | "@testing-library/react": "13.3.0", 43 | "@testing-library/react-hooks": "8.0.1", 44 | "@types/jest": "28.1.4", 45 | "@types/lodash.clonedeep": "4.5.7", 46 | "@types/lodash.isequal": "4.5.6", 47 | "@types/node": "^18.0.3", 48 | "@types/react": "18.0.15", 49 | "@types/react-dom": "18.0.6", 50 | "codecov": "3.8.3", 51 | "coverage": "0.4.1", 52 | "cross-env": "7.0.3", 53 | "jest": "28.1.2", 54 | "jest-environment-jsdom": "28.1.2", 55 | "npm-check-updates": "15.2.1", 56 | "react": "18.2.0", 57 | "react-dom": "18.2.0", 58 | "react-test-renderer": "18.2.0", 59 | "rimraf": "3.0.2", 60 | "rollup": "2.76.0", 61 | "rollup-plugin-peer-deps-external": "2.2.4", 62 | "rollup-plugin-typescript2": "0.32.1", 63 | "tslib": "^2.4.0", 64 | "typescript": "4.7.4" 65 | }, 66 | "files": [ 67 | "dist" 68 | ] 69 | } -------------------------------------------------------------------------------- /plugins/logged/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": "plugins/logged", 3 | "tags": [] 4 | } 5 | -------------------------------------------------------------------------------- /plugins/logged/rollup.config.js: -------------------------------------------------------------------------------- 1 | import typescript from 'rollup-plugin-typescript2' 2 | import commonjs from '@rollup/plugin-commonjs' 3 | import external from 'rollup-plugin-peer-deps-external' 4 | import resolve from '@rollup/plugin-node-resolve' 5 | import url from '@rollup/plugin-url' 6 | 7 | import pkg from './package.json' 8 | 9 | export default { 10 | input: 'src/index.ts', 11 | output: [ 12 | { 13 | file: pkg.main, 14 | format: 'cjs', 15 | exports: 'named', 16 | sourcemap: true 17 | }, 18 | { 19 | file: pkg.module, 20 | format: 'es', 21 | exports: 'named', 22 | sourcemap: true 23 | } 24 | ], 25 | plugins: [ 26 | external(), 27 | url(), 28 | resolve(), 29 | typescript({ 30 | tsconfig: 'tsconfig.prod.json', 31 | rollupCommonJSResolveHack: true, 32 | clean: true 33 | }), 34 | commonjs() 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /plugins/logged/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './logged'; 2 | -------------------------------------------------------------------------------- /plugins/logged/src/logged.ts: -------------------------------------------------------------------------------- 1 | import { ExtensionFactory, State, StateValueAtPath } from '@hookstate/core'; 2 | 3 | export interface Logged { 4 | log(): void 5 | } 6 | 7 | export function logged(options?: { 8 | identifier?: string, 9 | logger?: (...args: any[]) => void 10 | }): ExtensionFactory { 11 | return () => { 12 | let identifier: string; 13 | let serializer: (s: State) => (opts?: { stealth?: boolean }) => string; 14 | let logger = options?.logger ?? console.log 15 | return { 16 | onCreate: () => ({ 17 | log: (s) => () => { 18 | if (s.promise) { 19 | logger(`[hookstate][${identifier}] logged: promised`) 20 | } else { 21 | logger(`[hookstate][${identifier}] logged: ${serializer(s)({ stealth: true })}`) 22 | } 23 | } 24 | }), 25 | onInit: (state, extensionMethods) => { 26 | if (options?.identifier === undefined) { 27 | if (extensionMethods['identifier'] === undefined) { 28 | identifier = 'untitled' 29 | } else { 30 | identifier = extensionMethods['identifier'](state) 31 | } 32 | } else { 33 | identifier = options.identifier 34 | } 35 | if (extensionMethods['serialize'] !== undefined) { 36 | serializer = extensionMethods['serialize'] 37 | } else { 38 | serializer = (s) => (opts) => JSON.stringify(s.get({ noproxy: true, stealth: opts?.stealth })) 39 | } 40 | if (state.promise) { 41 | logger(`[hookstate][${identifier}] initialized: promised`) 42 | } else { 43 | logger(`[hookstate][${identifier}] initialized: ${serializer(state)({ stealth: true })}`) 44 | } 45 | }, 46 | onSet: (s, d) => { 47 | if (s.promised) { 48 | logger(`[hookstate][${identifier}] set to: promised`) 49 | } else if (s.error !== undefined) { 50 | logger(`[hookstate][${identifier}] set to: error: ${s.error}`) 51 | } else { 52 | if (d.actions) { 53 | logger(`[hookstate][${identifier}] set at path ${s.path} to: ${serializer(s)({ stealth: true })} with update actions: ${JSON.stringify(d)}`) 54 | } else { 55 | logger(`[hookstate][${identifier}] set at path ${s.path} to: ${serializer(s)({ stealth: true })}`) 56 | } 57 | } 58 | }, 59 | onDestroy: (s) => { 60 | if (s.promise) { 61 | logger(`[hookstate][${identifier}] destroyed: promised`) 62 | } else { 63 | logger(`[hookstate][${identifier}] destroyed: ${serializer(s)({ stealth: true })}`) 64 | } 65 | } 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /plugins/logged/src/unit.test.ts: -------------------------------------------------------------------------------- 1 | 2 | import { renderHook, act } from '@testing-library/react-hooks'; 3 | 4 | test('placeholder: develop tests', async () => {}); 5 | 6 | -------------------------------------------------------------------------------- /plugins/logged/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "dist", 4 | "target": "es5", 5 | "lib": [ 6 | "dom", 7 | "dom.iterable", 8 | "esnext" 9 | ], 10 | "sourceMap": true, 11 | "noImplicitReturns": true, 12 | "noImplicitThis": true, 13 | "noImplicitAny": true, 14 | "strictNullChecks": true, 15 | "noUnusedLocals": false, 16 | "experimentalDecorators": true, 17 | "newLine": "LF", 18 | "allowJs": true, 19 | "skipLibCheck": true, 20 | "esModuleInterop": true, 21 | "allowSyntheticDefaultImports": true, 22 | "strict": true, 23 | "forceConsistentCasingInFileNames": true, 24 | "suppressImplicitAnyIndexErrors": true, 25 | "module": "esnext", 26 | "moduleResolution": "node", 27 | "resolveJsonModule": true, 28 | "isolatedModules": true, 29 | "noEmit": false, 30 | "jsx": "preserve" 31 | }, 32 | "include": [ 33 | "src", "src/__tests__" 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /plugins/logged/tsconfig.prod.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": [ 4 | "src" 5 | ], 6 | "compilerOptions": { 7 | "lib": ["DOM"], 8 | "jsx": "react", 9 | "allowJs": false, 10 | "declaration": true, 11 | "declarationMap": true, 12 | "isolatedModules": false 13 | }, 14 | "exclude": [ 15 | "src/__tests__" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /plugins/serializable/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2022 Andrey Konstantinov 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 | -------------------------------------------------------------------------------- /plugins/serializable/README.md: -------------------------------------------------------------------------------- 1 | # @hookstate/serializable 2 | 3 | [![license](https://img.shields.io/github/license/avkonst/hookstate)](https://img.shields.io/github/license/avkonst/hookstate) [![npm version](https://img.shields.io/npm/v/@hookstate/serializable.svg?maxAge=300&label=version&colorB=007ec6)](https://www.npmjs.com/package/@hookstate/serializable) 4 | 5 | Plugin for @hookstate/core to enable state serialization and deserialization. 6 | 7 | [Learn more...](https://hookstate.js.org/docs/extensions-serializable) 8 | 9 | -------------------------------------------------------------------------------- /plugins/serializable/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'ts-jest', 3 | testEnvironment: 'node' 4 | }; -------------------------------------------------------------------------------- /plugins/serializable/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hookstate/serializable", 3 | "version": "4.0.0", 4 | "description": "Plugin for @hookstate/core to enable state serialization and deserialization.", 5 | "license": "MIT", 6 | "author": { 7 | "name": "Andrey Konstantinov" 8 | }, 9 | "repository": { 10 | "url": "https://github.com/avkonst/hookstate" 11 | }, 12 | "bugs": { 13 | "url": "https://github.com/avkonst/hookstate/issues" 14 | }, 15 | "homepage": "https://github.com/avkonst/hookstate", 16 | "main": "dist/index.js", 17 | "module": "dist/index.es.js", 18 | "jsnext:main": "dist/index.es.js", 19 | "types": "dist/index.d.ts", 20 | "scripts": { 21 | "build": "rollup -c", 22 | "build:w": "rollup -c -w", 23 | "clean": "rimraf dist", 24 | "test": "jest --env=jsdom", 25 | "test:ci": "jest --env=jsdom", 26 | "test:w": "jest --env=jsdom --watch", 27 | "release": "npm publish --access public", 28 | "update:deps": "ncu -u" 29 | }, 30 | "dependencies": {}, 31 | "peerDependencies": { 32 | "@hookstate/core": "^4.0.0" 33 | }, 34 | "devDependencies": { 35 | "@babel/core": "7.18.6", 36 | "@babel/runtime": "7.18.6", 37 | "@hookstate/core": "workspace:*", 38 | "@rollup/plugin-babel": "5.3.1", 39 | "@rollup/plugin-commonjs": "22.0.1", 40 | "@rollup/plugin-node-resolve": "13.3.0", 41 | "@rollup/plugin-url": "7.0.0", 42 | "@testing-library/react": "13.3.0", 43 | "@testing-library/react-hooks": "8.0.1", 44 | "@types/jest": "28.1.4", 45 | "@types/lodash.clonedeep": "4.5.7", 46 | "@types/lodash.isequal": "4.5.6", 47 | "@types/node": "^18.0.3", 48 | "@types/react": "18.0.15", 49 | "@types/react-dom": "18.0.6", 50 | "codecov": "3.8.3", 51 | "coverage": "0.4.1", 52 | "cross-env": "7.0.3", 53 | "jest": "28.1.2", 54 | "jest-environment-jsdom": "28.1.2", 55 | "npm-check-updates": "15.2.1", 56 | "react": "18.2.0", 57 | "react-dom": "18.2.0", 58 | "react-test-renderer": "18.2.0", 59 | "rimraf": "3.0.2", 60 | "rollup": "2.76.0", 61 | "rollup-plugin-peer-deps-external": "2.2.4", 62 | "rollup-plugin-typescript2": "0.32.1", 63 | "tslib": "^2.4.0", 64 | "typescript": "4.7.4" 65 | }, 66 | "files": [ 67 | "dist" 68 | ] 69 | } -------------------------------------------------------------------------------- /plugins/serializable/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": "plugins/serializable", 3 | "tags": [] 4 | } 5 | -------------------------------------------------------------------------------- /plugins/serializable/rollup.config.js: -------------------------------------------------------------------------------- 1 | import typescript from 'rollup-plugin-typescript2' 2 | import commonjs from '@rollup/plugin-commonjs' 3 | import external from 'rollup-plugin-peer-deps-external' 4 | import resolve from '@rollup/plugin-node-resolve' 5 | import url from '@rollup/plugin-url' 6 | 7 | import pkg from './package.json' 8 | 9 | export default { 10 | input: 'src/index.ts', 11 | output: [ 12 | { 13 | file: pkg.main, 14 | format: 'cjs', 15 | exports: 'named', 16 | sourcemap: true 17 | }, 18 | { 19 | file: pkg.module, 20 | format: 'es', 21 | exports: 'named', 22 | sourcemap: true 23 | } 24 | ], 25 | plugins: [ 26 | external(), 27 | url(), 28 | resolve(), 29 | typescript({ 30 | tsconfig: 'tsconfig.prod.json', 31 | rollupCommonJSResolveHack: true, 32 | clean: true 33 | }), 34 | commonjs() 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /plugins/serializable/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './serializable'; 2 | -------------------------------------------------------------------------------- /plugins/serializable/src/serializable.ts: -------------------------------------------------------------------------------- 1 | import { ExtensionFactory, Path, StateValueAtPath } from '@hookstate/core'; 2 | 3 | export interface Serializable { 4 | serialize: (options?: { stealth?: boolean }) => string, 5 | deserialize: (v: string) => void, 6 | } 7 | 8 | export function serializable( 9 | serialize: (path: Path, v: StateValueAtPath) => string, 10 | deserialize: (path: Path, v: string) => void 11 | ): ExtensionFactory { 12 | return () => ({ 13 | onCreate: () => ({ 14 | serialize: s => (options) => serialize(s.path, s.get({ noproxy: true, stealth: options?.stealth })), 15 | deserialize: s => (v) => s.set(deserialize(s.path, v)), 16 | }) 17 | }) 18 | } 19 | -------------------------------------------------------------------------------- /plugins/serializable/src/unit.test.ts: -------------------------------------------------------------------------------- 1 | 2 | import { renderHook, act } from '@testing-library/react-hooks'; 3 | 4 | test('placeholder: develop tests', async () => {}); 5 | 6 | -------------------------------------------------------------------------------- /plugins/serializable/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "dist", 4 | "target": "es5", 5 | "lib": [ 6 | "dom", 7 | "dom.iterable", 8 | "esnext" 9 | ], 10 | "sourceMap": true, 11 | "noImplicitReturns": true, 12 | "noImplicitThis": true, 13 | "noImplicitAny": true, 14 | "strictNullChecks": true, 15 | "noUnusedLocals": false, 16 | "experimentalDecorators": true, 17 | "newLine": "LF", 18 | "allowJs": true, 19 | "skipLibCheck": true, 20 | "esModuleInterop": true, 21 | "allowSyntheticDefaultImports": true, 22 | "strict": true, 23 | "forceConsistentCasingInFileNames": true, 24 | "suppressImplicitAnyIndexErrors": true, 25 | "module": "esnext", 26 | "moduleResolution": "node", 27 | "resolveJsonModule": true, 28 | "isolatedModules": true, 29 | "noEmit": false, 30 | "jsx": "preserve" 31 | }, 32 | "include": [ 33 | "src", "src/__tests__" 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /plugins/serializable/tsconfig.prod.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": [ 4 | "src" 5 | ], 6 | "compilerOptions": { 7 | "lib": ["DOM"], 8 | "jsx": "react", 9 | "allowJs": false, 10 | "declaration": true, 11 | "declarationMap": true, 12 | "isolatedModules": false 13 | }, 14 | "exclude": [ 15 | "src/__tests__" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /plugins/snapshotable/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2022 Andrey Konstantinov 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 | -------------------------------------------------------------------------------- /plugins/snapshotable/README.md: -------------------------------------------------------------------------------- 1 | # @hookstate/snapshotable 2 | 3 | [![license](https://img.shields.io/github/license/avkonst/hookstate)](https://img.shields.io/github/license/avkonst/hookstate) [![npm version](https://img.shields.io/npm/v/@hookstate/snapshotable.svg?maxAge=300&label=version&colorB=007ec6)](https://www.npmjs.com/package/@hookstate/snapshotable) 4 | 5 | Plugin for @hookstate/core to enable access to initial state value and to modified status. 6 | 7 | [Learn more...](https://hookstate.js.org/docs/extensions-snapshotable) 8 | -------------------------------------------------------------------------------- /plugins/snapshotable/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'ts-jest', 3 | testEnvironment: 'node' 4 | }; -------------------------------------------------------------------------------- /plugins/snapshotable/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hookstate/snapshotable", 3 | "version": "4.0.0", 4 | "description": "Extension for @hookstate/core to enable state snapshoting and reverting to the snapshoted / previous values.", 5 | "license": "MIT", 6 | "author": { 7 | "name": "Andrey Konstantinov" 8 | }, 9 | "repository": { 10 | "url": "https://github.com/avkonst/hookstate" 11 | }, 12 | "bugs": { 13 | "url": "https://github.com/avkonst/hookstate/issues" 14 | }, 15 | "homepage": "https://github.com/avkonst/hookstate", 16 | "main": "dist/index.js", 17 | "module": "dist/index.es.js", 18 | "jsnext:main": "dist/index.es.js", 19 | "types": "dist/index.d.ts", 20 | "scripts": { 21 | "build": "rollup -c", 22 | "build:w": "rollup -c -w", 23 | "clean": "rimraf dist", 24 | "test": "jest --env=jsdom", 25 | "test:ci": "jest --env=jsdom", 26 | "test:w": "jest --env=jsdom --watch", 27 | "release": "npm publish --access public", 28 | "update:deps": "ncu -u" 29 | }, 30 | "peerDependencies": { 31 | "@hookstate/core": "^4.0.0" 32 | }, 33 | "devDependencies": { 34 | "@babel/core": "7.18.6", 35 | "@babel/runtime": "7.18.6", 36 | "@hookstate/core": "workspace:*", 37 | "@hookstate/clonable": "workspace:*", 38 | "@hookstate/comparable": "workspace:*", 39 | "@rollup/plugin-babel": "5.3.1", 40 | "@rollup/plugin-commonjs": "22.0.1", 41 | "@rollup/plugin-node-resolve": "13.3.0", 42 | "@rollup/plugin-url": "7.0.0", 43 | "@testing-library/react": "13.3.0", 44 | "@testing-library/react-hooks": "8.0.1", 45 | "@types/jest": "28.1.4", 46 | "@types/node": "^18.0.3", 47 | "@types/react": "18.0.15", 48 | "@types/react-dom": "18.0.6", 49 | "codecov": "3.8.3", 50 | "coverage": "0.4.1", 51 | "cross-env": "7.0.3", 52 | "jest": "28.1.2", 53 | "jest-environment-jsdom": "28.1.2", 54 | "npm-check-updates": "15.2.1", 55 | "react": "18.2.0", 56 | "react-dom": "18.2.0", 57 | "react-test-renderer": "18.2.0", 58 | "rimraf": "3.0.2", 59 | "rollup": "2.76.0", 60 | "rollup-plugin-peer-deps-external": "2.2.4", 61 | "rollup-plugin-typescript2": "0.32.1", 62 | "ts-jest": "28.0.5", 63 | "tslib": "^2.4.0", 64 | "typescript": "4.7.4" 65 | }, 66 | "files": [ 67 | "dist" 68 | ] 69 | } -------------------------------------------------------------------------------- /plugins/snapshotable/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": "plugins/snapshotable", 3 | "tags": [] 4 | } 5 | -------------------------------------------------------------------------------- /plugins/snapshotable/rollup.config.js: -------------------------------------------------------------------------------- 1 | import typescript from 'rollup-plugin-typescript2' 2 | import commonjs from '@rollup/plugin-commonjs' 3 | import external from 'rollup-plugin-peer-deps-external' 4 | import resolve from '@rollup/plugin-node-resolve' 5 | import url from '@rollup/plugin-url' 6 | 7 | import pkg from './package.json' 8 | 9 | export default { 10 | input: 'src/index.ts', 11 | output: [ 12 | { 13 | file: pkg.main, 14 | format: 'cjs', 15 | exports: 'named', 16 | sourcemap: true 17 | }, 18 | { 19 | file: pkg.module, 20 | format: 'es', 21 | exports: 'named', 22 | sourcemap: true 23 | } 24 | ], 25 | plugins: [ 26 | external(), 27 | url(), 28 | resolve(), 29 | typescript({ 30 | tsconfig: 'tsconfig.prod.json', 31 | rollupCommonJSResolveHack: true, 32 | clean: true 33 | }), 34 | commonjs() 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /plugins/snapshotable/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './snapshotable'; 2 | -------------------------------------------------------------------------------- /plugins/snapshotable/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "dist", 4 | "target": "es5", 5 | "lib": [ 6 | "dom", 7 | "dom.iterable", 8 | "esnext" 9 | ], 10 | "sourceMap": true, 11 | "noImplicitReturns": true, 12 | "noImplicitThis": true, 13 | "noImplicitAny": true, 14 | "strictNullChecks": true, 15 | "noUnusedLocals": false, 16 | "experimentalDecorators": true, 17 | "newLine": "LF", 18 | "allowJs": true, 19 | "skipLibCheck": true, 20 | "esModuleInterop": true, 21 | "allowSyntheticDefaultImports": true, 22 | "strict": true, 23 | "forceConsistentCasingInFileNames": true, 24 | "suppressImplicitAnyIndexErrors": true, 25 | "module": "esnext", 26 | "moduleResolution": "node", 27 | "resolveJsonModule": true, 28 | "isolatedModules": true, 29 | "noEmit": false, 30 | "jsx": "preserve" 31 | }, 32 | "include": [ 33 | "src", "src/__tests__" 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /plugins/snapshotable/tsconfig.prod.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": [ 4 | "src" 5 | ], 6 | "compilerOptions": { 7 | "lib": ["DOM"], 8 | "jsx": "react", 9 | "allowJs": false, 10 | "declaration": true, 11 | "declarationMap": true, 12 | "isolatedModules": false 13 | }, 14 | "exclude": [ 15 | "src/__tests__" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /plugins/subscribable/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2022 Andrey Konstantinov 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 | -------------------------------------------------------------------------------- /plugins/subscribable/README.md: -------------------------------------------------------------------------------- 1 | # @hookstate/localstored 2 | 3 | [![license](https://img.shields.io/github/license/avkonst/hookstate)](https://img.shields.io/github/license/avkonst/hookstate) [![npm version](https://img.shields.io/npm/v/@hookstate/subscribable.svg?maxAge=300&label=version&colorB=007ec6)](https://www.npmjs.com/package/@hookstate/subscribable) 4 | 5 | Plugin for @hookstate/core to make it easier to subscribe a custom callback to state changes. 6 | 7 | [Learn more...](https://hookstate.js.org/docs/extensions-subscribable) 8 | 9 | -------------------------------------------------------------------------------- /plugins/subscribable/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'ts-jest', 3 | testEnvironment: 'node' 4 | }; -------------------------------------------------------------------------------- /plugins/subscribable/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hookstate/subscribable", 3 | "version": "4.0.0", 4 | "description": "Plugin for @hookstate/core to make it easier to subscribe a custom callback to state changes.", 5 | "license": "MIT", 6 | "author": { 7 | "name": "Andrey Konstantinov" 8 | }, 9 | "repository": { 10 | "url": "https://github.com/avkonst/hookstate" 11 | }, 12 | "bugs": { 13 | "url": "https://github.com/avkonst/hookstate/issues" 14 | }, 15 | "homepage": "https://github.com/avkonst/hookstate", 16 | "main": "dist/index.js", 17 | "module": "dist/index.es.js", 18 | "jsnext:main": "dist/index.es.js", 19 | "types": "dist/index.d.ts", 20 | "scripts": { 21 | "build": "rollup -c", 22 | "build:w": "rollup -c -w", 23 | "clean": "rimraf dist", 24 | "test": "jest --env=jsdom", 25 | "test:ci": "jest --env=jsdom", 26 | "test:w": "jest --env=jsdom --watch", 27 | "release": "npm publish --access public", 28 | "update:deps": "ncu -u" 29 | }, 30 | "dependencies": {}, 31 | "peerDependencies": { 32 | "@hookstate/core": "^4.0.0" 33 | }, 34 | "devDependencies": { 35 | "@babel/core": "7.18.6", 36 | "@babel/runtime": "7.18.6", 37 | "@hookstate/core": "workspace:*", 38 | "@rollup/plugin-babel": "5.3.1", 39 | "@rollup/plugin-commonjs": "22.0.1", 40 | "@rollup/plugin-node-resolve": "13.3.0", 41 | "@rollup/plugin-url": "7.0.0", 42 | "@testing-library/react": "13.3.0", 43 | "@testing-library/react-hooks": "8.0.1", 44 | "@types/jest": "28.1.4", 45 | "@types/lodash.clonedeep": "4.5.7", 46 | "@types/lodash.isequal": "4.5.6", 47 | "@types/node": "^18.0.3", 48 | "@types/react": "18.0.15", 49 | "@types/react-dom": "18.0.6", 50 | "codecov": "3.8.3", 51 | "coverage": "0.4.1", 52 | "cross-env": "7.0.3", 53 | "jest": "28.1.2", 54 | "jest-environment-jsdom": "28.1.2", 55 | "npm-check-updates": "15.2.1", 56 | "react": "18.2.0", 57 | "react-dom": "18.2.0", 58 | "react-test-renderer": "18.2.0", 59 | "rimraf": "3.0.2", 60 | "rollup": "2.76.0", 61 | "rollup-plugin-peer-deps-external": "2.2.4", 62 | "rollup-plugin-typescript2": "0.32.1", 63 | "tslib": "^2.4.0", 64 | "typescript": "4.7.4" 65 | }, 66 | "files": [ 67 | "dist" 68 | ] 69 | } -------------------------------------------------------------------------------- /plugins/subscribable/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": "plugins/subscribable", 3 | "tags": [] 4 | } 5 | -------------------------------------------------------------------------------- /plugins/subscribable/rollup.config.js: -------------------------------------------------------------------------------- 1 | import typescript from 'rollup-plugin-typescript2' 2 | import commonjs from '@rollup/plugin-commonjs' 3 | import external from 'rollup-plugin-peer-deps-external' 4 | import resolve from '@rollup/plugin-node-resolve' 5 | import url from '@rollup/plugin-url' 6 | 7 | import pkg from './package.json' 8 | 9 | export default { 10 | input: 'src/index.ts', 11 | output: [ 12 | { 13 | file: pkg.main, 14 | format: 'cjs', 15 | exports: 'named', 16 | sourcemap: true 17 | }, 18 | { 19 | file: pkg.module, 20 | format: 'es', 21 | exports: 'named', 22 | sourcemap: true 23 | } 24 | ], 25 | plugins: [ 26 | external(), 27 | url(), 28 | resolve(), 29 | typescript({ 30 | tsconfig: 'tsconfig.prod.json', 31 | rollupCommonJSResolveHack: true, 32 | clean: true 33 | }), 34 | commonjs() 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /plugins/subscribable/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './subscribable'; 2 | -------------------------------------------------------------------------------- /plugins/subscribable/src/unit.test.ts: -------------------------------------------------------------------------------- 1 | 2 | import { renderHook, act } from '@testing-library/react-hooks'; 3 | 4 | test('placeholder: develop tests', async () => {}); 5 | 6 | -------------------------------------------------------------------------------- /plugins/subscribable/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "dist", 4 | "target": "es5", 5 | "lib": [ 6 | "dom", 7 | "dom.iterable", 8 | "esnext" 9 | ], 10 | "sourceMap": true, 11 | "noImplicitReturns": true, 12 | "noImplicitThis": true, 13 | "noImplicitAny": true, 14 | "strictNullChecks": true, 15 | "noUnusedLocals": false, 16 | "experimentalDecorators": true, 17 | "newLine": "LF", 18 | "allowJs": true, 19 | "skipLibCheck": true, 20 | "esModuleInterop": true, 21 | "allowSyntheticDefaultImports": true, 22 | "strict": true, 23 | "forceConsistentCasingInFileNames": true, 24 | "suppressImplicitAnyIndexErrors": true, 25 | "module": "esnext", 26 | "moduleResolution": "node", 27 | "resolveJsonModule": true, 28 | "isolatedModules": true, 29 | "noEmit": false, 30 | "jsx": "preserve" 31 | }, 32 | "include": [ 33 | "src", "src/__tests__" 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /plugins/subscribable/tsconfig.prod.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": [ 4 | "src" 5 | ], 6 | "compilerOptions": { 7 | "lib": ["DOM"], 8 | "jsx": "react", 9 | "allowJs": false, 10 | "declaration": true, 11 | "declarationMap": true, 12 | "isolatedModules": false 13 | }, 14 | "exclude": [ 15 | "src/__tests__" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /plugins/validation/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2022 Andrey Konstantinov 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 | -------------------------------------------------------------------------------- /plugins/validation/README.md: -------------------------------------------------------------------------------- 1 | # @hookstate/validation 2 | 3 | [![license](https://img.shields.io/github/license/avkonst/hookstate)](https://img.shields.io/github/license/avkonst/hookstate) [![npm version](https://img.shields.io/npm/v/@hookstate/validation.svg?maxAge=300&label=version&colorB=007ec6)](https://www.npmjs.com/package/@hookstate/validation) 4 | 5 | Plugin for @hookstate/core to enable validation of data state. 6 | 7 | [Learn more...](https://hookstate.js.org/docs/extensions-validation) 8 | -------------------------------------------------------------------------------- /plugins/validation/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'ts-jest', 3 | testEnvironment: 'node' 4 | }; -------------------------------------------------------------------------------- /plugins/validation/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hookstate/validation", 3 | "version": "4.0.1", 4 | "description": "Plugin for @hookstate/core to enable validation of data state.", 5 | "license": "MIT", 6 | "author": { 7 | "name": "Andrey Konstantinov" 8 | }, 9 | "repository": { 10 | "url": "https://github.com/avkonst/hookstate" 11 | }, 12 | "bugs": { 13 | "url": "https://github.com/avkonst/hookstate/issues" 14 | }, 15 | "homepage": "https://github.com/avkonst/hookstate", 16 | "main": "dist/index.js", 17 | "module": "dist/index.es.js", 18 | "jsnext:main": "dist/index.es.js", 19 | "types": "dist/index.d.ts", 20 | "scripts": { 21 | "build": "rollup -c", 22 | "build:w": "rollup -c -w", 23 | "clean": "rimraf dist", 24 | "test": "jest --env=jsdom", 25 | "test:ci": "jest --env=jsdom", 26 | "test:w": "jest --env=jsdom --watch", 27 | "release": "npm publish --access public", 28 | "update:deps": "ncu -u" 29 | }, 30 | "dependencies": {}, 31 | "peerDependencies": { 32 | "@hookstate/core": "^4.0.0" 33 | }, 34 | "devDependencies": { 35 | "@babel/core": "7.18.6", 36 | "@babel/runtime": "7.18.6", 37 | "@hookstate/core": "workspace:*", 38 | "@rollup/plugin-babel": "5.3.1", 39 | "@rollup/plugin-commonjs": "22.0.1", 40 | "@rollup/plugin-node-resolve": "13.3.0", 41 | "@rollup/plugin-url": "7.0.0", 42 | "@testing-library/react": "13.3.0", 43 | "@testing-library/react-hooks": "8.0.1", 44 | "@types/jest": "28.1.4", 45 | "@types/lodash.clonedeep": "4.5.7", 46 | "@types/lodash.isequal": "4.5.6", 47 | "@types/node": "^18.0.3", 48 | "@types/react": "18.0.15", 49 | "@types/react-dom": "18.0.6", 50 | "codecov": "3.8.3", 51 | "coverage": "0.4.1", 52 | "cross-env": "7.0.3", 53 | "jest": "28.1.2", 54 | "jest-environment-jsdom": "28.1.2", 55 | "npm-check-updates": "15.2.1", 56 | "react": "18.2.0", 57 | "react-dom": "18.2.0", 58 | "react-test-renderer": "18.2.0", 59 | "rimraf": "3.0.2", 60 | "rollup": "2.76.0", 61 | "rollup-plugin-peer-deps-external": "2.2.4", 62 | "rollup-plugin-typescript2": "0.32.1", 63 | "tslib": "^2.4.0", 64 | "typescript": "4.7.4" 65 | }, 66 | "files": [ 67 | "dist" 68 | ] 69 | } 70 | -------------------------------------------------------------------------------- /plugins/validation/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": "plugins/validation", 3 | "tags": [] 4 | } 5 | -------------------------------------------------------------------------------- /plugins/validation/rollup.config.js: -------------------------------------------------------------------------------- 1 | import typescript from 'rollup-plugin-typescript2' 2 | import commonjs from '@rollup/plugin-commonjs' 3 | import external from 'rollup-plugin-peer-deps-external' 4 | import resolve from '@rollup/plugin-node-resolve' 5 | import url from '@rollup/plugin-url' 6 | 7 | import pkg from './package.json' 8 | 9 | export default { 10 | input: 'src/index.ts', 11 | output: [ 12 | { 13 | file: pkg.main, 14 | format: 'cjs', 15 | exports: 'named', 16 | sourcemap: true 17 | }, 18 | { 19 | file: pkg.module, 20 | format: 'es', 21 | exports: 'named', 22 | sourcemap: true 23 | } 24 | ], 25 | plugins: [ 26 | external(), 27 | url(), 28 | resolve(), 29 | typescript({ 30 | tsconfig: 'tsconfig.prod.json', 31 | rollupCommonJSResolveHack: true, 32 | clean: true 33 | }), 34 | commonjs() 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /plugins/validation/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './validation'; 2 | -------------------------------------------------------------------------------- /plugins/validation/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "dist", 4 | "target": "es5", 5 | "lib": [ 6 | "dom", 7 | "dom.iterable", 8 | "esnext" 9 | ], 10 | "sourceMap": true, 11 | "noImplicitReturns": true, 12 | "noImplicitThis": true, 13 | "noImplicitAny": true, 14 | "strictNullChecks": true, 15 | "noUnusedLocals": false, 16 | "experimentalDecorators": true, 17 | "newLine": "LF", 18 | "allowJs": true, 19 | "skipLibCheck": true, 20 | "esModuleInterop": true, 21 | "allowSyntheticDefaultImports": true, 22 | "strict": true, 23 | "forceConsistentCasingInFileNames": true, 24 | "suppressImplicitAnyIndexErrors": true, 25 | "module": "esnext", 26 | "moduleResolution": "node", 27 | "resolveJsonModule": true, 28 | "isolatedModules": true, 29 | "noEmit": false, 30 | "jsx": "preserve" 31 | }, 32 | "include": [ 33 | "src", "src/__tests__" 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /plugins/validation/tsconfig.prod.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": [ 4 | "src" 5 | ], 6 | "compilerOptions": { 7 | "lib": ["DOM"], 8 | "jsx": "react", 9 | "allowJs": false, 10 | "declaration": true, 11 | "declarationMap": true, 12 | "isolatedModules": false 13 | }, 14 | "exclude": [ 15 | "src/__tests__" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - 'core/**' 3 | - 'plugins/**' 4 | - 'docs/**' 5 | -------------------------------------------------------------------------------- /workspace.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 2, 3 | "projects": { 4 | "core": "core", 5 | "clonable": "plugins/clonable", 6 | "comparable": "plugins/comparable", 7 | "initializable": "plugins/initializable", 8 | "identifiable": "plugins/identifiable", 9 | "serializable": "plugins/serializable", 10 | "subscribable": "plugins/subscribable", 11 | "devtools": "plugins/devtools", 12 | "logged": "plugins/logged", 13 | "snapshotable": "plugins/snapshotable", 14 | "validation": "plugins/validation", 15 | "localstored": "plugins/localstored", 16 | "broadcasted": "plugins/broadcasted", 17 | "docs": "docs/index", 18 | "todolist": "docs/demos/todolist", 19 | "strictmode": "docs/demos/strictmode" 20 | } 21 | } --------------------------------------------------------------------------------