The response has been limited to 50k tokens of the smallest files in the repo. You can remove this limitation by removing the max tokens filter.
├── .babelrc
├── .codecov.yml
├── .editorconfig
├── .eslintignore
├── .eslintrc.js
├── .github
    ├── PULL_REQUEST_TEMPLATE
    ├── dependabot.yml
    └── workflows
    │   ├── build.yml
    │   └── tests.yml
├── .gitignore
├── .npmignore
├── .prettierignore
├── .prettierrc.js
├── CHANGELOG.md
├── LICENSE
├── README.md
├── ambient.d.ts
├── gulpfile.js
├── jest.config.js
├── jest.es5.config.js
├── jest.perf.config.js
├── jsdoc.conf.json
├── package-lock.json
├── package.json
├── src
    ├── Model.d.ts
    ├── Model.js
    ├── ORM.d.ts
    ├── ORM.js
    ├── QuerySet.d.ts
    ├── QuerySet.js
    ├── Session.d.ts
    ├── Session.js
    ├── constants.d.ts
    ├── constants.js
    ├── db
    │   ├── Database.d.ts
    │   ├── Database.js
    │   ├── Table.d.ts
    │   ├── Table.js
    │   ├── index.d.ts
    │   └── index.js
    ├── descriptors.js
    ├── fields
    │   ├── Attribute.js
    │   ├── DefaultFieldInstaller.js
    │   ├── Field.js
    │   ├── FieldInstallerTemplate.js
    │   ├── ForeignKey.js
    │   ├── ManyToMany.js
    │   ├── OneToOne.js
    │   ├── RelationalField.js
    │   ├── index.d.ts
    │   └── index.js
    ├── helpers.d.ts
    ├── index.d.ts
    ├── index.js
    ├── memoize.js
    ├── redux-orm-tests.ts
    ├── redux.d.ts
    ├── redux.js
    ├── selectors
    │   ├── FieldSelectorSpec.js
    │   ├── MapSelectorSpec.js
    │   ├── ModelBasedSelectorSpec.js
    │   ├── ModelSelectorSpec.js
    │   ├── SelectorSpec.js
    │   ├── idArgSelector.js
    │   └── index.js
    ├── test
    │   ├── .eslintrc.js
    │   ├── ReferencedModel.js
    │   ├── ReferencingModel.js
    │   ├── ThroughModel.js
    │   ├── functional
    │   │   ├── concurrentSessions.js
    │   │   ├── deprecations.js
    │   │   ├── es5.js
    │   │   ├── fieldDeclarations.js
    │   │   ├── immutableSessions.js
    │   │   ├── many2many.js
    │   │   ├── mutableSessions.js
    │   │   ├── performance.js
    │   │   └── serialization.js
    │   ├── helpers.js
    │   ├── integration
    │   │   ├── redux.js
    │   │   ├── reduxPersist.js
    │   │   └── selectors.js
    │   └── unit
    │   │   ├── Database.js
    │   │   ├── Model.js
    │   │   ├── ORM.js
    │   │   ├── QuerySet.js
    │   │   ├── Session.js
    │   │   ├── Table.js
    │   │   ├── fields.js
    │   │   └── utils.js
    └── utils.js
├── standalone-docs
    ├── FAQ.md
    ├── LearningResources.md
    ├── README.md
    ├── Troubleshooting.md
    ├── advanced
    │   ├── ComplexSelectors.md
    │   └── CustomReducer.md
    ├── api
    │   ├── Model.md
    │   ├── ORM.md
    │   ├── QuerySet.md
    │   ├── README.md
    │   ├── Session.md
    │   ├── index.md
    │   └── redux.md
    ├── assets
    │   └── plantuml-theme.iuml
    ├── basics
    │   ├── Fields.md
    │   ├── README.md
    │   ├── React.md
    │   ├── Reducers.md
    │   └── Selectors.md
    ├── faq
    │   └── Performance.md
    ├── introduction
    │   ├── CoreConcepts.md
    │   ├── GettingStarted.md
    │   ├── Installation.md
    │   └── README.md
    └── recipes
    │   ├── README.md
    │   └── WritingTests.md
├── tasks
    └── api-docs
    │   ├── build.js
    │   ├── clean.js
    │   ├── config.js
    │   ├── helper
    │       └── docusaurus-helpers.js
    │   ├── index.js
    │   └── partial
    │       ├── defaultvalue.hbs
    │       ├── docusaurus-header.hbs
    │       ├── header.hbs
    │       ├── link.hbs
    │       ├── member-index-grouped.hbs
    │       ├── module-index-table.hbs
    │       ├── module-index.hbs
    │       ├── params-table.hbs
    │       ├── properties-list.hbs
    │       ├── properties-table.hbs
    │       ├── returns.hbs
    │       ├── see.hbs
    │       ├── separator.hbs
    │       ├── sig-link-parent.hbs
    │       ├── sig-link.hbs
    │       ├── sig-name.hbs
    │       └── template.hbs
├── tsconfig.json
├── webpack.common.js
├── webpack.dev.js
├── webpack.prod.js
└── website
    ├── _redirects
    ├── core
        └── Footer.js
    ├── i18n
        └── en.json
    ├── package-lock.json
    ├── package.json
    ├── pages
        └── en
        │   ├── 404.js
        │   └── index.js
    ├── plantuml-plugin
        └── index.js
    ├── sidebars.json
    ├── siteConfig.js
    └── static
        ├── css
            ├── 404.css
            ├── code-block-buttons.css
            ├── codeblock.css
            └── custom.css
        ├── img
            ├── favicon
            │   └── favicon.ico
            ├── flexible.svg
            ├── github-brands.svg
            ├── lightweight.svg
            ├── redux-orm-white.svg
            ├── redux-orm.svg
            ├── safe.svg
            └── stable.svg
        └── scripts
            ├── code-block-buttons.js
            ├── codeblock.js
            └── sidebarScroll.js


/.babelrc:
--------------------------------------------------------------------------------
 1 | {
 2 |     "presets": [["@babel/preset-typescript", { "allExtensions": true }]],
 3 |     "plugins": [
 4 |         "@babel/plugin-transform-typescript",
 5 |         "@babel/plugin-transform-runtime",
 6 |         "@babel/plugin-proposal-class-properties",
 7 |         [
 8 |             "@babel/plugin-transform-classes",
 9 |             {
10 |                 "loose": true
11 |             }
12 |         ],
13 |         "@babel/plugin-syntax-dynamic-import",
14 |         "@babel/plugin-syntax-import-meta",
15 |         "@babel/plugin-proposal-json-strings",
16 |         [
17 |             "@babel/plugin-proposal-decorators",
18 |             {
19 |                 "legacy": true
20 |             }
21 |         ],
22 |         "@babel/plugin-proposal-function-sent",
23 |         "@babel/plugin-proposal-export-namespace-from",
24 |         "@babel/plugin-proposal-numeric-separator",
25 |         "@babel/plugin-proposal-throw-expressions"
26 |     ],
27 |     "env": {
28 |         "es": {
29 |             "presets": [
30 |                 [
31 |                     "@babel/preset-env",
32 |                     {
33 |                         "modules": false
34 |                     }
35 |                 ]
36 |             ]
37 |         },
38 |         "cjs": {
39 |             "presets": ["@babel/preset-env"]
40 |         },
41 |         "jsnext": {
42 |             "presets": [
43 |                 [
44 |                     "@babel/preset-env",
45 |                     {
46 |                         "modules": "commonjs"
47 |                     }
48 |                 ],
49 |                 ["@babel/preset-typescript", { "allExtensions": true }]
50 |             ]
51 |         },
52 |         "test": {
53 |             "plugins": ["@babel/plugin-transform-modules-commonjs"]
54 |         }
55 |     }
56 | }
57 | 


--------------------------------------------------------------------------------
/.codecov.yml:
--------------------------------------------------------------------------------
1 | codecov:
2 |     #branch: master
3 | 


--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
 1 | root = true
 2 | 
 3 | [*]
 4 | indent_size = 4
 5 | indent_style = space
 6 | trim_trailing_whitespace = true
 7 | insert_final_newline = true
 8 | 
 9 | [*.json]
10 | indent_size = 2
11 | 


--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | */node_modules/*
2 | website
3 | tasks
4 | 


--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
 1 | module.exports = {
 2 |     extends: ["airbnb-base", "plugin:prettier/recommended"],
 3 |     parser: "babel-eslint",
 4 |     rules: {
 5 |         "max-len": 0,
 6 |         "id-length": 0,
 7 |         "consistent-return": 1,
 8 |         "class-methods-use-this": 0,
 9 |         "no-unused-vars": ["error", { args: "none" }],
10 |         "no-underscore-dangle": ["warn", { allowAfterThis: true }],
11 |         "no-param-reassign": ["error", { props: false }],
12 |         "no-prototype-builtins": 0,
13 |         "no-plusplus": 0,
14 |         "no-console": 0,
15 |         "prefer-rest-params": 0,
16 |         "import/no-named-as-default": 0,
17 |         "function-paren-newline": 0,
18 |         "operator-linebreak": 0,
19 |         "max-classes-per-file": ["warn", 1],
20 |     },
21 |     parserOptions: {
22 |         ecmaFeatures: {
23 |             ecmaVersion: 6,
24 |         },
25 |     },
26 | };
27 | 


--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE:
--------------------------------------------------------------------------------
1 | First of all, thanks for your contribution! :-)
2 | 
3 | Please makes sure these boxes are checked before submitting your PR, thank you!
4 | 
5 | * [ ] Make sure you propose PR to correct branch: hotfix for `master`, feature for latest active branch `feature/x`.
6 | * [ ] Run `npm run lint` and fix those errors before submitting in order to keep consistent code style.
7 | * [ ] Rebase before creating a PR to keep commit history clear.
8 | * [ ] Add some descriptions and refer relative issues for you PR.
9 | 


--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
  1 | version: 2
  2 | updates:
  3 | - package-ecosystem: npm
  4 |   directory: "/"
  5 |   schedule:
  6 |     interval: daily
  7 |   open-pull-requests-limit: 10
  8 |   labels:
  9 |   - "type: Chore"
 10 |   ignore:
 11 |   - dependency-name: "@babel/preset-env"
 12 |     versions:
 13 |     - 7.13.15
 14 |   - dependency-name: "@babel/runtime"
 15 |     versions:
 16 |     - 7.13.17
 17 |   - dependency-name: "@babel/cli"
 18 |     versions:
 19 |     - 7.13.10
 20 |     - 7.13.14
 21 |     - 7.13.16
 22 |   - dependency-name: ssri
 23 |     versions:
 24 |     - 6.0.2
 25 |   - dependency-name: eslint-config-prettier
 26 |     versions:
 27 |     - 8.1.0
 28 |     - 8.2.0
 29 |   - dependency-name: eslint-plugin-jest
 30 |     versions:
 31 |     - 24.1.10
 32 |     - 24.1.5
 33 |     - 24.1.6
 34 |     - 24.1.7
 35 |     - 24.1.8
 36 |     - 24.1.9
 37 |     - 24.2.0
 38 |     - 24.2.1
 39 |     - 24.3.0
 40 |     - 24.3.1
 41 |     - 24.3.2
 42 |     - 24.3.3
 43 |     - 24.3.4
 44 |     - 24.3.5
 45 |   - dependency-name: "@babel/plugin-transform-runtime"
 46 |     versions:
 47 |     - 7.13.10
 48 |     - 7.13.15
 49 |   - dependency-name: jsdoc-to-markdown
 50 |     versions:
 51 |     - 7.0.0
 52 |   - dependency-name: webpack-cli
 53 |     versions:
 54 |     - 4.5.0
 55 |   - dependency-name: "@babel/core"
 56 |     versions:
 57 |     - 7.12.10
 58 |     - 7.12.13
 59 |     - 7.12.16
 60 |     - 7.12.17
 61 |     - 7.13.0
 62 |     - 7.13.1
 63 |     - 7.13.8
 64 |   - dependency-name: "@babel/register"
 65 |     versions:
 66 |     - 7.12.10
 67 |     - 7.12.13
 68 |     - 7.13.0
 69 |     - 7.13.8
 70 |   - dependency-name: lodash
 71 |     versions:
 72 |     - 4.17.20
 73 |     - 4.17.21
 74 |   - dependency-name: eslint
 75 |     versions:
 76 |     - 7.18.0
 77 |     - 7.19.0
 78 |     - 7.20.0
 79 |     - 7.21.0
 80 |   - dependency-name: webpack-merge
 81 |     versions:
 82 |     - 5.7.3
 83 |   - dependency-name: ini
 84 |     versions:
 85 |     - 1.3.8
 86 |   - dependency-name: prettier
 87 |     versions:
 88 |     - 2.2.1
 89 |   - dependency-name: jest
 90 |     versions:
 91 |     - 26.6.3
 92 |   - dependency-name: babel-jest
 93 |     versions:
 94 |     - 26.6.3
 95 |   - dependency-name: jest-cli
 96 |     versions:
 97 |     - 26.6.3
 98 |   - dependency-name: yargs-parser
 99 |     versions:
100 |     - 5.0.1
101 | - package-ecosystem: npm
102 |   directory: "/website"
103 |   schedule:
104 |     interval: daily
105 |   open-pull-requests-limit: 10
106 |   labels:
107 |   - "type: Chore"
108 |   versioning-strategy: increase
109 |   ignore:
110 |   - dependency-name: "@babel/plugin-proposal-class-properties"
111 |     versions:
112 |     - 7.12.1
113 |     - 7.12.13
114 |   - dependency-name: "@babel/preset-react"
115 |     versions:
116 |     - 7.12.10
117 |     - 7.12.13
118 |   - dependency-name: ini
119 |     versions:
120 |     - 1.3.7
121 | 


--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
 1 | name: Build
 2 | 
 3 | on:
 4 |     push:
 5 |         branches: [master]
 6 |     pull_request:
 7 |         branches: [master]
 8 | 
 9 | jobs:
10 |     build:
11 |         name: Test Suite
12 |         runs-on: ubuntu-latest
13 | 
14 |         steps:
15 |             - name: Set up Node
16 |               uses: actions/setup-node@v1
17 |               with:
18 |                   node-version: 14.x
19 | 
20 |             - name: Checkout code
21 |               uses: actions/checkout@v2
22 | 
23 |             - name: Cache dependencies
24 |               uses: actions/cache@v2
25 |               with:
26 |                   path: ~/.npm
27 |                   key: ${{ runner.OS }}-npm-${{ hashFiles('**/package-lock.json') }}
28 |                   restore-keys: |
29 |                       ${{ runner.OS }}-npm-
30 |                       ${{ runner.OS }}-
31 | 
32 |             - name: Install dependencies
33 |               run: npm ci
34 | 
35 |             - name: Build package
36 |               run: npm run build
37 | 


--------------------------------------------------------------------------------
/.github/workflows/tests.yml:
--------------------------------------------------------------------------------
 1 | name: Tests
 2 | 
 3 | on:
 4 |     push:
 5 |         branches: [master]
 6 |     pull_request:
 7 |         branches: [master]
 8 | 
 9 | jobs:
10 |     build:
11 |         name: Test Suite
12 |         runs-on: ubuntu-latest
13 | 
14 |         steps:
15 |             - name: Set up Node
16 |               uses: actions/setup-node@v1
17 |               with:
18 |                   node-version: 14.x
19 | 
20 |             - name: Checkout code
21 |               uses: actions/checkout@v2
22 | 
23 |             - name: Cache dependencies
24 |               uses: actions/cache@v2
25 |               with:
26 |                   path: ~/.npm
27 |                   key: ${{ runner.OS }}-npm-${{ hashFiles('**/package-lock.json') }}
28 |                   restore-keys: |
29 |                       ${{ runner.OS }}-npm-
30 |                       ${{ runner.OS }}-
31 | 
32 |             - name: Install dependencies
33 |               run: npm ci
34 | 
35 |             - name: Lint code
36 |               run: npm run lint
37 | 
38 |             - name: Transpile to ES5
39 |               run: npm run transpile:cjs
40 | 
41 |             - name: ES5 tests
42 |               run: npm run test:es5
43 | 
44 |             - name: Normal tests
45 |               run: npm run test
46 | 
47 |             - name: Performance tests
48 |               run: npm run test:perf
49 | 
50 |             - name: Collect coverage
51 |               run: npx codecov
52 | 


--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
 1 | node_modules/
 2 | docs-current/
 3 | *.sublime-project
 4 | *.sublime-workspace
 5 | .DS_Store
 6 | lib/
 7 | es/
 8 | jsnext/
 9 | types/
10 | .publish
11 | dist/
12 | min
13 | coverage/
14 | .idea/
15 | docs/
16 | build/
17 | 


--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
 1 | node_modules
 2 | docs
 3 | coverage
 4 | src/test
 5 | website
 6 | standalone-docs
 7 | tasks
 8 | .publish
 9 | .github
10 | .codecov.yml
11 | .travis.yml
12 | gulpfile.js
13 | jest.config.js
14 | jest.es5.config.js
15 | jest.perf.config.js
16 | jsdoc.conf.json
17 | webpack.common.js
18 | webpack.dev.js
19 | webpack.prod.js
20 | .eslintrc
21 | .eslintignore
22 | 


--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | standalone-docs/
2 | 


--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
 1 | module.exports = {
 2 |     tabWidth: 4,
 3 |     semi: true,
 4 |     singleQuote: false,
 5 |     overrides: [
 6 |         {
 7 |             files: ["*.js", "*.mjs"],
 8 |             options: {
 9 |                 trailingComma: "es5",
10 |             },
11 |         },
12 |         {
13 |             files: "*.json",
14 |             options: {
15 |                 tabWidth: 2,
16 |             },
17 |         },
18 |     ],
19 | };
20 | 


--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
 1 | The MIT License (MIT)
 2 | 
 3 | Copyright (c) 2015 Tommi Kaikkonen
 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 | 


--------------------------------------------------------------------------------
/ambient.d.ts:
--------------------------------------------------------------------------------
1 | declare module "immutable-ops";
2 | declare module "lodash/filter";
3 | declare module "lodash/orderBy";
4 | declare module "lodash/reject";
5 | declare module "lodash/sortBy";
6 | 


--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
 1 | const { task, series, watch } = require("gulp");
 2 | 
 3 | const apiDocs = require("./tasks/api-docs");
 4 | 
 5 | task("api-docs:clean", apiDocs.clean);
 6 | task("api-docs:build", apiDocs.build);
 7 | const apiDocsAll = series("api-docs:clean", "api-docs:build");
 8 | 
 9 | function apiDocsWatch() {
10 |     watch(["src/**/*.js", "tasks/api-docs/**/*"], apiDocsAll);
11 | }
12 | 
13 | exports["api-docs"] = apiDocsAll;
14 | exports["api-docs:watch"] = apiDocsWatch;
15 | 


--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
 1 | const path = require("path");
 2 | 
 3 | module.exports = {
 4 |     rootDir: path.resolve("./src/"),
 5 |     testRegex: "test/(.*)/(.*)\\.(js)",
 6 |     moduleFileExtensions: ["js", "json"],
 7 |     testPathIgnorePatterns: [
 8 |         "test/functional/es5\\.(js)",
 9 |         "test/functional/performance\\.(js)",
10 |     ],
11 |     transform: {
12 |         "\\.js
quot;: "babel-jest",
13 |     },
14 |     coverageDirectory: "../coverage/",
15 |     collectCoverage: true,
16 |     collectCoverageFrom: ["*.js", "*/*.js"],
17 |     coveragePathIgnorePatterns: ["test/*"],
18 |     coverageThreshold: {
19 |         global: {
20 |             branches: 98,
21 |             functions: 100,
22 |             lines: 100,
23 |             statements: 99,
24 |         },
25 |     },
26 | };
27 | 


--------------------------------------------------------------------------------
/jest.es5.config.js:
--------------------------------------------------------------------------------
 1 | const path = require("path");
 2 | 
 3 | module.exports = {
 4 |     rootDir: path.resolve("./src/"),
 5 |     testRegex: "test/functional/es5\\.(js)",
 6 |     moduleFileExtensions: ["js", "json"],
 7 |     transform: {},
 8 |     collectCoverage: false,
 9 | };
10 | 


--------------------------------------------------------------------------------
/jest.perf.config.js:
--------------------------------------------------------------------------------
 1 | const path = require("path");
 2 | 
 3 | module.exports = {
 4 |     rootDir: path.resolve("./src/"),
 5 |     testRegex: "test/functional/performance\\.(js)",
 6 |     moduleFileExtensions: ["js", "json"],
 7 |     transform: {
 8 |         "\\.js
quot;: "babel-jest",
 9 |     },
10 |     collectCoverage: false,
11 |     verbose: true,
12 | };
13 | 


--------------------------------------------------------------------------------
/jsdoc.conf.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "tags": {
 3 |     "allowUnknownTags": true,
 4 |     "dictionaries": ["jsdoc", "closure"]
 5 |   },
 6 |   "source": {
 7 |     "includePattern": ".+\\.js(doc)?
quot;,
 8 |     "excludePattern": "test"
 9 |   },
10 |   "markdown": {
11 |     "hardwrap": true
12 |   },
13 |   "plugins": ["plugins/markdown"],
14 |   "templates": {
15 |     "cleverLinks": true,
16 |     "monospaceLinks": false
17 |   }
18 | }
19 | 


--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
  1 | {
  2 |   "name": "redux-orm",
  3 |   "version": "0.16.2",
  4 |   "description": "Simple ORM to manage and query your state trees",
  5 |   "main": "lib/index.js",
  6 |   "module": "es/index.js",
  7 |   "jsnext:main": "jsnext/index.js",
  8 |   "types": "types/index.d.ts",
  9 |   "sideEffects": false,
 10 |   "scripts": {
 11 |     "test": "jest",
 12 |     "test:watch": "jest --watch",
 13 |     "test:es5": "jest --config=jest.es5.config.js",
 14 |     "test:perf": "jest --config=jest.perf.config.js",
 15 |     "transpile": "run-p transpile:*",
 16 |     "transpile:cjs": "cross-env BABEL_ENV=cjs babel src --out-dir lib --extensions .js,.ts --ignore */test,**/*.d.ts",
 17 |     "transpile:es": "cross-env BABEL_ENV=es babel src --out-dir es --extensions .js,.ts --ignore */test,**/*.d.ts",
 18 |     "transpile:jsnext": "cross-env BABEL_ENV=jsnext babel src --out-dir jsnext --extensions .js,.ts --ignore */test,**/*.d.ts",
 19 |     "transpile:types": "cross-env BABEL_ENV=cjs babel src --out-dir types --extensions .ts --ignore */test --keep-file-extension",
 20 |     "build:umd": "webpack --config webpack.dev.js",
 21 |     "build:umd:min": "webpack --config webpack.prod.js",
 22 |     "build:docs": "jsdoc src -d docs -r -c ./jsdoc.conf.json --verbose",
 23 |     "build:api-docs": "gulp api-docs",
 24 |     "build": "run-s clean transpile && run-p build:umd build:umd:min",
 25 |     "pub": "run-s ensure-clean-tree ensure-version-not-published lint test build test:es5 && npm publish && git push origin && npm run clean",
 26 |     "pub:next": "run-s ensure-clean-tree ensure-version-not-published lint test build test:es5 && npm publish --tag next && git push origin && npm run clean",
 27 |     "ensure-version-not-published": "[ -z \"$(npm info $(npm list | head -n 1 | cut -d ' ' -f1) 2> /dev/null)\" ] || (echo \"Error: $(npm list | head -n 1 | cut -d ' ' -f1) has already been published.\" && false)",
 28 |     "ensure-clean-tree": "[ -z \"$(git status -s)\" ] || (echo \"Error: There are uncommitted changes in the git repostory.\" && false)",
 29 |     "lint": "run-p lint:*",
 30 |     "lint:es": "eslint src",
 31 |     "clean": "rimraf lib es jsnext types dist docs coverage"
 32 |   },
 33 |   "keywords": [
 34 |     "redux",
 35 |     "orm",
 36 |     "state",
 37 |     "immutable"
 38 |   ],
 39 |   "author": "Tommi Kaikkonen <tommi.kaikkonen@aalto.fi>",
 40 |   "repository": {
 41 |     "type": "git",
 42 |     "url": "https://github.com/redux-orm/redux-orm.git"
 43 |   },
 44 |   "bugs": {
 45 |     "url": "https://github.com/redux-orm/redux-orm/issues"
 46 |   },
 47 |   "homepage": "https://redux-orm.github.io/redux-orm",
 48 |   "license": "MIT",
 49 |   "devDependencies": {
 50 |     "@babel/cli": "^7.13.0",
 51 |     "@babel/core": "^7.13.8",
 52 |     "@babel/plugin-proposal-class-properties": "^7.13.0",
 53 |     "@babel/plugin-proposal-decorators": "^7.13.5",
 54 |     "@babel/plugin-proposal-export-namespace-from": "^7.12.13",
 55 |     "@babel/plugin-proposal-function-sent": "^7.12.13",
 56 |     "@babel/plugin-proposal-json-strings": "^7.13.8",
 57 |     "@babel/plugin-proposal-numeric-separator": "^7.12.13",
 58 |     "@babel/plugin-proposal-throw-expressions": "^7.12.13",
 59 |     "@babel/plugin-syntax-dynamic-import": "^7.8.3",
 60 |     "@babel/plugin-syntax-import-meta": "^7.10.4",
 61 |     "@babel/plugin-transform-classes": "^7.13.0",
 62 |     "@babel/plugin-transform-destructuring": "^7.13.0",
 63 |     "@babel/plugin-transform-modules-commonjs": "^7.13.8",
 64 |     "@babel/plugin-transform-runtime": "^7.13.9",
 65 |     "@babel/preset-env": "^7.13.9",
 66 |     "@babel/preset-typescript": "^7.13.0",
 67 |     "@babel/register": "^7.13.8",
 68 |     "@types/jsdoc-to-markdown": "^6.0.0",
 69 |     "babel-core": "^7.0.0-bridge.0",
 70 |     "babel-eslint": "^10.1.0",
 71 |     "babel-jest": "^26.6.3",
 72 |     "babel-loader": "^8.2.2",
 73 |     "babel-plugin-lodash": "^3.3.4",
 74 |     "codecov": "^3.8.1",
 75 |     "cross-env": "^7.0.3",
 76 |     "deep-freeze": "0.0.1",
 77 |     "eslint": "^7.21.0",
 78 |     "eslint-config-airbnb-base": "^14.2.1",
 79 |     "eslint-config-prettier": "^6.15.0",
 80 |     "eslint-plugin-import": "^2.22.1",
 81 |     "eslint-plugin-jest": "^23.20.0",
 82 |     "eslint-plugin-prettier": "^3.3.1",
 83 |     "gulp": "^4.0.2",
 84 |     "gulp-clean": "^0.4.0",
 85 |     "jest": "^26.6.3",
 86 |     "jest-cli": "^26.6.3",
 87 |     "jsdoc": "^3.6.6",
 88 |     "jsdoc-to-markdown": "^6.0.1",
 89 |     "lodash-webpack-plugin": "^0.11.6",
 90 |     "npm-run-all": "^4.1.5",
 91 |     "prettier": "^2.2.1",
 92 |     "redux": "^4.0.5",
 93 |     "redux-persist": "^6.0.0",
 94 |     "rimraf": "^3.0.2",
 95 |     "typescript": "^4.2.2",
 96 |     "webpack": "^4.46.0",
 97 |     "webpack-cli": "^3.3.12",
 98 |     "webpack-merge": "^5.7.3"
 99 |   },
100 |   "dependencies": {
101 |     "@babel/runtime": "^7.13.9",
102 |     "immutable-ops": "^0.7.0",
103 |     "lodash": "^4.17.21",
104 |     "re-reselect": "^4.0.0",
105 |     "reselect": "^3.0.1"
106 |   }
107 | }
108 | 


--------------------------------------------------------------------------------
/src/ORM.d.ts:
--------------------------------------------------------------------------------
  1 | import { Database, DatabaseCreator } from "./db";
  2 | import { TableState } from "./db/Table";
  3 | import { AnyModel } from "./Model";
  4 | import Session, { OrmSession } from "./Session";
  5 | 
  6 | /**
  7 |  * A `{typeof Model[modelName]: typeof Model}` map defining:
  8 |  *
  9 |  * - database schema
 10 |  * - {@link Session} bound Model classes
 11 |  * - ORM branch state type
 12 |  */
 13 | type IndexedModelClasses<
 14 |     T extends { [k in keyof T]: typeof AnyModel } = {},
 15 |     K extends keyof T = Extract<keyof T, T[keyof T]["modelName"]>
 16 | > = { [k in K]: T[K] };
 17 | 
 18 | /**
 19 |  * A mapped type capable of inferring ORM branch state type based on schema {@link Model}s.
 20 |  */
 21 | type OrmState<MClassMap extends IndexedModelClasses<any>> = {
 22 |     [K in keyof MClassMap]: TableState<MClassMap[K]>;
 23 | };
 24 | 
 25 | /**
 26 |  * ORM instantiation opts.
 27 |  *
 28 |  * Enables customization of database creation.
 29 |  */
 30 | interface ORMOpts<MClassMap> {
 31 |     stateSelector?: (state: any) => OrmState<MClassMap>;
 32 |     createDatabase?: DatabaseCreator;
 33 | }
 34 | 
 35 | /**
 36 |  * ORM - the Object Relational Mapper.
 37 |  *
 38 |  * Use instances of this class to:
 39 |  *
 40 |  * - Register your {@link Model} classes using {@link ORM#register}
 41 |  * - Get the empty state for the underlying database with {@link ORM#getEmptyState}
 42 |  * - Start an immutable database session with {@link ORM#session}
 43 |  * - Start a mutating database session with {@link ORM#mutableSession}
 44 |  *
 45 |  * Internally, this class handles generating a schema specification from models
 46 |  * to the database.
 47 |  */
 48 | declare class ORM<
 49 |     I extends IndexedModelClasses<any>,
 50 |     ModelNames extends keyof I = keyof I
 51 | > {
 52 |     /**
 53 |      * Creates a new ORM instance.
 54 |      */
 55 |     constructor(opts?: ORMOpts<I>);
 56 | 
 57 |     /**
 58 |      * Registers a {@link Model} class to the ORM.
 59 |      *
 60 |      * If the model has declared any ManyToMany fields, their
 61 |      * through models will be generated and registered with
 62 |      * this call, unless a custom through model has been specified.
 63 |      *
 64 |      * @param  model - a {@link Model} class to register
 65 |      */
 66 |     register(...model: ReadonlyArray<I[ModelNames]>): void;
 67 | 
 68 |     /**
 69 |      * Gets a {@link Model} class by its name from the registry.
 70 |      *
 71 |      * @param  modelName - the name of the {@link Model} class to get
 72 |      *
 73 |      * @throws If {@link Model} class is not found.
 74 |      *
 75 |      * @return the {@link Model} class, if found
 76 |      */
 77 |     get<K extends ModelNames>(modelName: K): I[K];
 78 | 
 79 |     /**
 80 |      * Returns the empty database state.
 81 |      *
 82 |      * @see {@link OrmState}
 83 |      *
 84 |      * @return empty state
 85 |      */
 86 |     getEmptyState(): OrmState<I>;
 87 | 
 88 |     /**
 89 |      * Begins an immutable database session.
 90 |      *
 91 |      * @see {@link OrmState}
 92 |      * @see {@link SessionType}
 93 |      *
 94 |      * @param  state  - the state the database manages
 95 |      *
 96 |      * @return a new {@link Session} instance
 97 |      */
 98 |     session(state?: OrmState<I>): OrmSession<I>;
 99 | 
100 |     /**
101 |      * Begins an mutable database session.
102 |      *
103 |      * @see {@link OrmState}
104 |      * @see {@link SessionType}
105 |      *
106 |      * @param  state  - the state the database manages
107 |      *
108 |      * @return a new {@link Session} instance
109 |      */
110 |     mutableSession(state: OrmState<I>): OrmSession<I>;
111 | 
112 |     /**
113 |      * Acquire database reference.
114 |      *
115 |      * If no database exists, an instance is created using either default or supplied implementation of {@link DatabaseCreator}.
116 |      *
117 |      * @return A {@link Database} instance structured according to registered schema.
118 |      */
119 |     getDatabase(): Database<I>;
120 | }
121 | 


--------------------------------------------------------------------------------
/src/Session.d.ts:
--------------------------------------------------------------------------------
 1 | import { IndexedModelClasses, ORM, OrmState } from "./ORM";
 2 | import { Database, QueryResult, QuerySpec, UpdateSpec } from "./db";
 3 | import { Assign } from "./helpers";
 4 | 
 5 | export declare type BatchToken = any;
 6 | 
 7 | export declare class Session<I extends IndexedModelClasses<any>> {
 8 |     /**
 9 |      * list of bound {@link Model} classes bound to this session, bootstrapped during {@link @ORM.register}.
10 |      *
11 |      * @see {@link Model}
12 |      */
13 |     readonly sessionBoundModels: ReadonlyArray<I[keyof I]>;
14 | 
15 |     /**
16 |      * Current {@link OrmState}, specific to registered schema
17 |      */
18 |     readonly state: OrmState<I>;
19 | 
20 |     /**
21 |      * Creates a new Session.
22 |      *
23 |      * @param schema - {@Link ORM} instance, with bootstrapped {@link Model} prototypes.
24 |      * @param  db - a {@link Database} instance
25 |      * @param  state - the database {@link OrmState}
26 |      * @param  withMutations? - whether the session should mutate data
27 |      * @param  batchToken? - a {@link BatchToken} used by the backend to identify objects that can be
28 |      *                                 mutated. If none is provided a default of `Symbol('ownerId')` will be created.
29 |      *
30 |      */
31 |     constructor(
32 |         schema: ORM<I>,
33 |         db: Database<I>,
34 |         state: OrmState<I>,
35 |         withMutations?: boolean,
36 |         batchToken?: BatchToken
37 |     );
38 | 
39 |     /**
40 |      * Executes query against model state.
41 |      *
42 |      * @param query - the query command object.
43 |      *
44 |      * @returns query result.
45 |      *
46 |      * @see {@link QueryType}
47 |      * @see {@link QueryClause}
48 |      * @see {@link QuerySpec}
49 |      * @see {@link QueryResult}
50 |      */
51 |     query(query: QuerySpec): QueryResult;
52 | 
53 |     /**
54 |      * Applies update to a model state.
55 |      *
56 |      * @param  update - the update command object.
57 |      *
58 |      * @returns query result.
59 |      *
60 |      * @see {@link DbAction}
61 |      * @see {@link UpdateSpec}
62 |      * @see {@link DbActionResult}
63 |      * @see {@link UpdateResult}
64 |      */
65 |     applyUpdate<P>(update: UpdateSpec<P>): P;
66 | }
67 | 
68 | /**
69 |  * An {@link ORM}-bound {@link Session} instance, extended with a set of {@link Model.constructor} properties.
70 |  *
71 |  * Extension is a map of {@link Model} class accessible under keys within a set of {@link Model#modelName} values
72 |  * for registered {@link Model} classes.
73 |  */
74 | export declare type OrmSession<I extends IndexedModelClasses<any>> = Assign<
75 |     Session<I>,
76 |     { [K in keyof I]: I[K] }
77 | >;
78 | export default Session;
79 | 


--------------------------------------------------------------------------------
/src/constants.d.ts:
--------------------------------------------------------------------------------
 1 | export declare const UPDATE: string;
 2 | export declare const DELETE: string;
 3 | export declare const CREATE: string;
 4 | 
 5 | export declare const FILTER: string;
 6 | export declare const EXCLUDE: string;
 7 | export declare const ORDER_BY: string;
 8 | 
 9 | export declare const SUCCESS: string;
10 | export declare const FAILURE: string;
11 | 


--------------------------------------------------------------------------------
/src/constants.js:
--------------------------------------------------------------------------------
 1 | export const UPDATE = "REDUX_ORM_UPDATE";
 2 | export const DELETE = "REDUX_ORM_DELETE";
 3 | export const CREATE = "REDUX_ORM_CREATE";
 4 | 
 5 | export const FILTER = "REDUX_ORM_FILTER";
 6 | export const EXCLUDE = "REDUX_ORM_EXCLUDE";
 7 | export const ORDER_BY = "REDUX_ORM_ORDER_BY";
 8 | 
 9 | export const SUCCESS = "SUCCESS";
10 | export const FAILURE = "FAILURE";
11 | 
12 | // for detecting ORM state objects
13 | export const STATE_FLAG = "@@_______REDUX_ORM_STATE_FLAG";
14 | 
15 | // for caching selectors based on their ID argument
16 | export const ALL_INSTANCES = Symbol("REDUX_ORM_ALL_INSTANCES");
17 | export const ID_ARG_KEY_SELECTOR = (_state, idArg) =>
18 |     typeof idArg === "undefined" ? ALL_INSTANCES : idArg;
19 | 


--------------------------------------------------------------------------------
/src/db/Database.d.ts:
--------------------------------------------------------------------------------
  1 | import { IndexedModelClasses, OrmState } from "../ORM";
  2 | import {
  3 |     CREATE,
  4 |     DELETE,
  5 |     EXCLUDE,
  6 |     FAILURE,
  7 |     FILTER,
  8 |     ORDER_BY,
  9 |     SUCCESS,
 10 |     UPDATE,
 11 | } from "../constants";
 12 | import { ModelTableOpts, Table } from "./Table";
 13 | import { Serializable } from "../Model";
 14 | import { BatchToken } from "../Session";
 15 | 
 16 | /**
 17 |  * A type of {@link QueryClause}.
 18 |  */
 19 | type QueryType = typeof FILTER | typeof EXCLUDE | typeof ORDER_BY;
 20 | 
 21 | /**
 22 |  * A single `QueryClause`.
 23 |  * Multiple `QueryClause`s can be combined into a {@link Query}.
 24 |  */
 25 | interface QueryClause<Payload extends object = {}> {
 26 |     type: QueryType;
 27 |     payload: Payload;
 28 | }
 29 | 
 30 | /**
 31 |  * Query definition, contains target table and a collection of {@link QueryClause}.
 32 |  */
 33 | interface Query {
 34 |     table: string;
 35 |     clauses: QueryClause[];
 36 | }
 37 | 
 38 | /**
 39 |  * Query wrapper definition, wraps {@link Query}.
 40 |  */
 41 | interface QuerySpec {
 42 |     query: Query;
 43 | }
 44 | 
 45 | /**
 46 |  * Query result.
 47 |  */
 48 | interface QueryResult<Row extends Record<string, Serializable> = {}> {
 49 |     rows: ReadonlyArray<Row>;
 50 | }
 51 | 
 52 | /**
 53 |  * A type of data update to perform.
 54 |  */
 55 | type UpdateType = typeof CREATE | typeof UPDATE | typeof DELETE;
 56 | 
 57 | /**
 58 |  * A status of data update operation.
 59 |  */
 60 | type UpdateStatus = typeof SUCCESS | typeof FAILURE;
 61 | 
 62 | /**
 63 |  * Data update definition
 64 |  */
 65 | interface UpdateSpec<Payload = any> {
 66 |     action: UpdateType;
 67 |     payload?: Payload;
 68 |     query?: Query;
 69 | }
 70 | 
 71 | /**
 72 |  * Data update result.
 73 |  */
 74 | interface UpdateResult<
 75 |     I extends IndexedModelClasses<any>,
 76 |     Payload extends object = {}
 77 | > {
 78 |     status: UpdateStatus;
 79 |     state: OrmState<I>;
 80 |     payload: Payload;
 81 | }
 82 | 
 83 | /**
 84 |  * Transactions aggregate batches of operations.
 85 |  */
 86 | interface Transaction {
 87 |     batchToken: BatchToken;
 88 |     withMutations: boolean;
 89 | }
 90 | 
 91 | /**
 92 |  * Schema specification, required for default database creator.
 93 |  *
 94 |  * @see {@link DatabaseCreator}
 95 |  * @see {@link ModelTableOpts}
 96 |  * @see {@link Table}
 97 |  */
 98 | interface SchemaSpec<I extends IndexedModelClasses<any>> {
 99 |     tables: { [K in keyof I]: ModelTableOpts<I[K]> };
100 | }
101 | 
102 | /**
103 |  * A Database parametrized by schema made of {@link Model} classes.
104 |  *
105 |  * @see {@link SchemaSpec}
106 |  * @see {@link TableSpec}
107 |  * @see {@link Table}
108 |  */
109 | interface Database<
110 |     I extends IndexedModelClasses<any>,
111 |     Tables = { [K in keyof I]: Table<I[K]> }
112 | > {
113 |     /**
114 |      * Returns the empty database state.
115 |      *
116 |      * @see {@link OrmState}
117 |      *
118 |      * @return empty state
119 |      */
120 |     getEmptyState(): OrmState<I>;
121 | 
122 |     /**
123 |      * Execute a query against a given state.
124 |      *
125 |      * @param querySpec - a query definition.
126 |      * @param state - the state to query against.
127 |      *
128 |      * @see {@link QuerySpec}
129 |      * @see {@link OrmState}
130 |      * @see {@link OrmState}
131 |      *
132 |      * @return a {@link QueryResult} containing 0 to many {@link QueryResult.rows}.
133 |      */
134 |     query(querySpec: QuerySpec, state: OrmState<I>): QueryResult;
135 | 
136 |     /**
137 |      * Apply an update to a given state.
138 |      *
139 |      * @param updateSpec - a data update definition.
140 |      * @param tx - a transaction for batches of operations.
141 |      * @param state - the state to apply update to.
142 |      *
143 |      * @see {@link UpdateSpec}
144 |      * @see {@link Transaction}
145 |      * @see {@link OrmState}
146 |      *
147 |      * @return a {@link UpdateResult} containing 0 to many {@link QueryResult.rows}.
148 |      */
149 | 
150 |     update(
151 |         updateSpec: UpdateSpec,
152 |         tx: Transaction,
153 |         state: OrmState<I>
154 |     ): UpdateResult<I>;
155 | 
156 |     /**
157 |      * Return a {@link Table} structure based on provided table name.
158 |      * @param tableName - the name of the {@link Table} to describe
159 |      *
160 |      * @return a {@link Table} instance matching given `tableName` or `undefined` if no such table exists.
161 |      */
162 |     describe<K extends keyof Tables>(tableName: K): Tables[K];
163 | }
164 | 
165 | /**
166 |  * Database creation function type.
167 |  */
168 | type DatabaseCreator = typeof createDatabase;
169 | 
170 | /**
171 |  * Default database creation procedure handle.
172 |  *
173 |  * @param schemaSpec - a {@link SchemaSpec} to built the {@link Database} from.
174 |  *
175 |  *  @return a {@Link Database} instance, ready for query and data update operation.
176 |  */
177 | declare function createDatabase<I extends IndexedModelClasses<any>>(
178 |     schemaSpec: SchemaSpec<I>
179 | ): Database<I>;
180 | 


--------------------------------------------------------------------------------
/src/db/Database.js:
--------------------------------------------------------------------------------
  1 | import ops from "immutable-ops";
  2 | 
  3 | import { CREATE, UPDATE, DELETE, SUCCESS, STATE_FLAG } from "../constants";
  4 | 
  5 | import Table from "./Table";
  6 | 
  7 | const BASE_EMPTY_STATE = {};
  8 | Object.defineProperty(BASE_EMPTY_STATE, STATE_FLAG, {
  9 |     enumerable: true,
 10 |     value: true,
 11 | });
 12 | 
 13 | /** @private */
 14 | function replaceTableState(tableName, newTableState, tx, state) {
 15 |     const { batchToken, withMutations } = tx;
 16 | 
 17 |     if (withMutations) {
 18 |         state[tableName] = newTableState;
 19 |         return state;
 20 |     }
 21 | 
 22 |     return ops.batch.set(batchToken, tableName, newTableState, state);
 23 | }
 24 | 
 25 | /** @private */
 26 | function query(tables, querySpec, state) {
 27 |     const { table: tableName, clauses } = querySpec;
 28 |     const table = tables[tableName];
 29 |     const rows = table.query(state[tableName], clauses);
 30 |     return {
 31 |         rows,
 32 |     };
 33 | }
 34 | 
 35 | /** @private */
 36 | function update(tables, updateSpec, tx, state) {
 37 |     const { action, payload } = updateSpec;
 38 | 
 39 |     let tableName;
 40 |     let nextTableState;
 41 |     let resultPayload;
 42 | 
 43 |     if (action === CREATE) {
 44 |         ({ table: tableName } = updateSpec);
 45 |         const table = tables[tableName];
 46 |         const currTableState = state[tableName];
 47 |         const result = table.insert(tx, currTableState, payload);
 48 |         nextTableState = result.state;
 49 |         resultPayload = result.created;
 50 |     } else {
 51 |         const { query: querySpec } = updateSpec;
 52 |         ({ table: tableName } = querySpec);
 53 |         const { rows } = query(tables, querySpec, state);
 54 | 
 55 |         const table = tables[tableName];
 56 |         const currTableState = state[tableName];
 57 | 
 58 |         if (action === UPDATE) {
 59 |             nextTableState = table.update(tx, currTableState, rows, payload);
 60 |             // return updated rows
 61 |             resultPayload = query(tables, querySpec, state).rows;
 62 |         } else if (action === DELETE) {
 63 |             nextTableState = table.delete(tx, currTableState, rows);
 64 |             // return original rows that we just deleted
 65 |             resultPayload = rows;
 66 |         } else {
 67 |             throw new Error(`Database received unknown update type: ${action}`);
 68 |         }
 69 |     }
 70 | 
 71 |     const nextDBState = replaceTableState(tableName, nextTableState, tx, state);
 72 |     return {
 73 |         status: SUCCESS,
 74 |         state: nextDBState,
 75 |         payload: resultPayload,
 76 |     };
 77 | }
 78 | 
 79 | /**
 80 |  * @memberof db
 81 |  * @param {Object} schemaSpec
 82 |  * @return Object database
 83 |  */
 84 | export function createDatabase(schemaSpec) {
 85 |     const { tables: tableSpecs } = schemaSpec;
 86 |     const tables = Object.entries(tableSpecs).reduce(
 87 |         (map, [tableName, tableSpec]) => ({
 88 |             ...map,
 89 |             [tableName]: new Table(tableSpec),
 90 |         }),
 91 |         {}
 92 |     );
 93 | 
 94 |     const getEmptyState = () =>
 95 |         Object.entries(tables).reduce(
 96 |             (map, [tableName, table]) => ({
 97 |                 ...map,
 98 |                 [tableName]: table.getEmptyState(),
 99 |             }),
100 |             BASE_EMPTY_STATE
101 |         );
102 | 
103 |     return {
104 |         getEmptyState,
105 |         query: query.bind(null, tables),
106 |         update: update.bind(null, tables),
107 |         // Used to inspect the schema.
108 |         describe: (tableName) => tables[tableName],
109 |     };
110 | }
111 | 
112 | export default createDatabase;
113 | 


--------------------------------------------------------------------------------
/src/db/Table.d.ts:
--------------------------------------------------------------------------------
  1 | import { AnyModel, FieldSpecKeys, IdType, Ref } from "../Model";
  2 | import { Field, ForeignKey, OneToOne } from "../fields";
  3 | 
  4 | /**
  5 |  * {@link TableOpts} used for {@link Table} customization.
  6 |  *
  7 |  * Supplied via {@link Model#options}.
  8 |  *
  9 |  * If no customizations were provided, the table uses following default options:
 10 |  * <br/>
 11 |  * ```typescript
 12 |  *  {
 13 |  *      idAttribute: 'id',
 14 |  *      arrName:     'items',
 15 |  *      mapName:     'itemsById'
 16 |  *  }
 17 |  * ```
 18 |  * <br/>
 19 |  *  @see {@link Model}
 20 |  *  @see {@link Model#options}
 21 |  *  @see {@link OrmState}
 22 |  */
 23 | export interface TableOpts {
 24 |     readonly idAttribute?: string;
 25 |     readonly arrName?: string;
 26 |     readonly mapName?: string;
 27 |     readonly fields?: { [K: string]: Field };
 28 | }
 29 | 
 30 | /**
 31 |  * @internal
 32 |  */
 33 | type ExtractModelOption<
 34 |     MClass extends typeof AnyModel,
 35 |     K extends keyof TableOpts,
 36 |     DefaultValue extends string
 37 | > = MClass["options"] extends () => { [P in K]: infer R }
 38 |     ? R extends string
 39 |         ? R
 40 |         : DefaultValue
 41 |     : MClass["options"] extends { [P in K]: infer R }
 42 |     ? R extends string
 43 |         ? R
 44 |         : DefaultValue
 45 |     : DefaultValue;
 46 | 
 47 | /**
 48 |  * Model idAttribute option extraction helper.
 49 |  *
 50 |  * Falls back to `'id'` if not specified explicitly via {@link Model.options}.
 51 |  */
 52 | export type IdAttribute<MClass extends typeof AnyModel> = ExtractModelOption<
 53 |     MClass,
 54 |     "idAttribute",
 55 |     "id"
 56 | >;
 57 | 
 58 | /**
 59 |  * Model arrName option extraction helper.
 60 |  *
 61 |  * Falls back to `'items'` if not specified explicitly via {@link Model.options}.
 62 |  */
 63 | type ArrName<MClass extends typeof AnyModel> = ExtractModelOption<
 64 |     MClass,
 65 |     "arrName",
 66 |     "items"
 67 | >;
 68 | 
 69 | /**
 70 |  * Model mapName option extraction helper.
 71 |  *
 72 |  * Falls back to `'itemsById'` if not specified explicitly via {@link Model.options}.
 73 |  */
 74 | export type MapName<MClass extends typeof AnyModel> = ExtractModelOption<
 75 |     MClass,
 76 |     "mapName",
 77 |     "itemsById"
 78 | >;
 79 | 
 80 | /**
 81 |  * Unbox {@link Model#options} or fallback to default for others.
 82 |  *
 83 |  * @internal
 84 |  */
 85 | export interface ModelTableOpts<MClass extends typeof AnyModel> {
 86 |     readonly idAttribute: IdAttribute<MClass>;
 87 |     readonly arrName: ArrName<MClass>;
 88 |     readonly mapName: MapName<MClass>;
 89 |     readonly fields: MClass["fields"];
 90 | }
 91 | 
 92 | /**
 93 |  * Handles the underlying data structure for a {@link Model} class.
 94 |  */
 95 | export declare class Table<MClass extends typeof AnyModel> {
 96 |     /**
 97 |      * Creates a new {@link Table} instance.
 98 |      *
 99 |      * @param   userOpts - options to use.
100 |      * @param   [userOpts.idAttribute=DefaultTableOpts.idAttribute] - the id attribute of the entity.
101 |      * @param   [userOpts.arrName=DefaultTableOpts.arrName] - the state attribute where an array of
102 |      *                                             entity id's are stored
103 |      * @param   [userOpts.mapName=DefaultTableOpts.mapName] - the state attribute where the entity objects
104 |      *                                                 are stored in a id to entity object
105 |      *                                                 map.
106 |      * @param   [userOpts.fields=DefaultTableOpts.fields] - mapping of field key to {@link Field} object
107 |      */
108 |     constructor(userOpts?: ModelTableOpts<MClass>);
109 | 
110 |     getEmptyState(): TableState<MClass>;
111 | }
112 | 
113 | /**
114 |  * Type of {@link Model} state's branch `meta` field.
115 |  */
116 | interface DefaultMeta<MIdType> {
117 |     maxId: MIdType extends number ? number : null | number;
118 | }
119 | 
120 | export type TableIndexes<MClass extends typeof AnyModel> = {
121 |     [K in FieldSpecKeys<InstanceType<MClass>, OneToOne | ForeignKey>]: string;
122 | };
123 | 
124 | /**
125 |  * A mapped type parametrized by specific {@link Model} class.
126 |  *
127 |  * Infers actual state of the ORM branch based on the {@link Model} class provided.
128 |  */
129 | export declare type TableState<MClass extends typeof AnyModel> = {
130 |     readonly meta: DefaultMeta<IdType<InstanceType<MClass>>>;
131 |     readonly indexes: TableIndexes<MClass>;
132 | } & {
133 |     readonly [K in ArrName<MClass>]: ReadonlyArray<
134 |         IdType<InstanceType<MClass>>
135 |     >;
136 | } &
137 |     {
138 |         readonly [K in MapName<MClass>]: {
139 |             readonly [K: string]: Ref<InstanceType<MClass>>;
140 |         };
141 |     };
142 | 
143 | export default Table;
144 | 


--------------------------------------------------------------------------------
/src/db/index.d.ts:
--------------------------------------------------------------------------------
1 | export * from "./Database";
2 | 


--------------------------------------------------------------------------------
/src/db/index.js:
--------------------------------------------------------------------------------
 1 | /**
 2 |  * @module db
 3 |  * @desc Internal implementation of data storage, fetching and optimizations.
 4 |  * @private
 5 |  */
 6 | 
 7 | import createDatabase from "./Database";
 8 | import Table from "./Table";
 9 | 
10 | export { createDatabase, Table };
11 | 
12 | export default createDatabase;
13 | 


--------------------------------------------------------------------------------
/src/fields/Attribute.js:
--------------------------------------------------------------------------------
 1 | import Field from "./Field";
 2 | 
 3 | import { attrDescriptor } from "../descriptors";
 4 | 
 5 | /**
 6 |  * @memberof module:fields
 7 |  */
 8 | export class Attribute extends Field {
 9 |     constructor(opts) {
10 |         super();
11 |         this.opts = opts || {};
12 | 
13 |         if (this.opts.hasOwnProperty("getDefault")) {
14 |             this.getDefault = this.opts.getDefault;
15 |         }
16 |     }
17 | 
18 |     createForwardsDescriptor(fieldName, model) {
19 |         return attrDescriptor(fieldName);
20 |     }
21 | }
22 | 
23 | export default Attribute;
24 | 


--------------------------------------------------------------------------------
/src/fields/DefaultFieldInstaller.js:
--------------------------------------------------------------------------------
 1 | import FieldInstallerTemplate from "./FieldInstallerTemplate";
 2 | 
 3 | import { reverseFieldErrorMessage } from "../utils";
 4 | 
 5 | /**
 6 |  * Default implementation for the template method in FieldInstallerTemplate.
 7 |  * @private
 8 |  * @memberof module:fields
 9 |  */
10 | export class DefaultFieldInstaller extends FieldInstallerTemplate {
11 |     installForwardsDescriptor() {
12 |         Object.defineProperty(
13 |             this.model.prototype,
14 |             this.fieldName,
15 |             this.field.createForwardsDescriptor(
16 |                 this.fieldName,
17 |                 this.model,
18 |                 this.toModel,
19 |                 this.throughModel
20 |             )
21 |         );
22 |     }
23 | 
24 |     installForwardsVirtualField() {
25 |         this.model.virtualFields[
26 |             this.fieldName
27 |         ] = this.field.createForwardsVirtualField(
28 |             this.fieldName,
29 |             this.model,
30 |             this.toModel,
31 |             this.throughModel
32 |         );
33 |     }
34 | 
35 |     installBackwardsDescriptor() {
36 |         const backwardsDescriptor = Object.getOwnPropertyDescriptor(
37 |             this.toModel.prototype,
38 |             this.backwardsFieldName
39 |         );
40 |         if (backwardsDescriptor) {
41 |             throw new Error(
42 |                 reverseFieldErrorMessage(
43 |                     this.model.modelName,
44 |                     this.fieldName,
45 |                     this.toModel.modelName,
46 |                     this.backwardsFieldName
47 |                 )
48 |             );
49 |         }
50 | 
51 |         // install backwards descriptor
52 |         Object.defineProperty(
53 |             this.toModel.prototype,
54 |             this.backwardsFieldName,
55 |             this.field.createBackwardsDescriptor(
56 |                 this.fieldName,
57 |                 this.model,
58 |                 this.toModel,
59 |                 this.throughModel
60 |             )
61 |         );
62 |     }
63 | 
64 |     installBackwardsVirtualField() {
65 |         this.toModel.virtualFields[
66 |             this.backwardsFieldName
67 |         ] = this.field.createBackwardsVirtualField(
68 |             this.fieldName,
69 |             this.model,
70 |             this.toModel,
71 |             this.throughModel
72 |         );
73 |     }
74 | }
75 | 
76 | export default DefaultFieldInstaller;
77 | 


--------------------------------------------------------------------------------
/src/fields/Field.js:
--------------------------------------------------------------------------------
 1 | import DefaultFieldInstaller from "./DefaultFieldInstaller";
 2 | 
 3 | /**
 4 |  * @private
 5 |  * @memberof module:fields
 6 |  */
 7 | export class Field {
 8 |     get installerClass() {
 9 |         return DefaultFieldInstaller;
10 |     }
11 | 
12 |     getClass() {
13 |         return this.constructor;
14 |     }
15 | 
16 |     references(model) {
17 |         return false;
18 |     }
19 | 
20 |     getThroughModelName(fieldName, model) {
21 |         return null;
22 |     }
23 | 
24 |     get installsForwardsVirtualField() {
25 |         return false;
26 |     }
27 | 
28 |     get installsBackwardsDescriptor() {
29 |         return false;
30 |     }
31 | 
32 |     get installsBackwardsVirtualField() {
33 |         return false;
34 |     }
35 | 
36 |     get index() {
37 |         return false;
38 |     }
39 | }
40 | 
41 | export default Field;
42 | 


--------------------------------------------------------------------------------
/src/fields/FieldInstallerTemplate.js:
--------------------------------------------------------------------------------
 1 | /**
 2 |  * Defines algorithm for installing a field onto a model and related models.
 3 |  * Conforms to the template method behavioral design pattern.
 4 |  * @private
 5 |  * @memberof module:fields
 6 |  */
 7 | export class FieldInstallerTemplate {
 8 |     constructor(opts) {
 9 |         this.field = opts.field;
10 |         this.fieldName = opts.fieldName;
11 |         this.model = opts.model;
12 |         this.orm = opts.orm;
13 |         /**
14 |          * the field itself has no knowledge of the model
15 |          * it is being installed upon; we need to inform it
16 |          * that it is a self-referencing field for the field
17 |          * to be able to make better informed decisions
18 |          */
19 |         if (this.field.references(this.model)) {
20 |             this.field.toModelName = "this";
21 |         }
22 |     }
23 | 
24 |     get toModel() {
25 |         if (typeof this._toModel === "undefined") {
26 |             const { toModelName } = this.field;
27 |             if (!toModelName) {
28 |                 this._toModel = null;
29 |             } else if (toModelName === "this") {
30 |                 this._toModel = this.model;
31 |             } else {
32 |                 this._toModel = this.orm.get(toModelName);
33 |             }
34 |         }
35 |         return this._toModel;
36 |     }
37 | 
38 |     get throughModel() {
39 |         if (typeof this._throughModel === "undefined") {
40 |             const throughModelName = this.field.getThroughModelName(
41 |                 this.fieldName,
42 |                 this.model
43 |             );
44 |             if (!throughModelName) {
45 |                 this._throughModel = null;
46 |             } else {
47 |                 this._throughModel = this.orm.get(throughModelName);
48 |             }
49 |         }
50 |         return this._throughModel;
51 |     }
52 | 
53 |     get backwardsFieldName() {
54 |         return this.field.getBackwardsFieldName(this.model);
55 |     }
56 | 
57 |     run() {
58 |         this.installForwardsDescriptor();
59 |         if (this.field.installsForwardsVirtualField) {
60 |             this.installForwardsVirtualField();
61 |         }
62 |         /**
63 |          * Install a backwards field on a model as a consequence
64 |          * of having installed the forwards field on another model.
65 |          */
66 |         if (this.field.installsBackwardsDescriptor) {
67 |             this.installBackwardsDescriptor();
68 |         }
69 |         if (this.field.installsBackwardsVirtualField) {
70 |             this.installBackwardsVirtualField();
71 |         }
72 |     }
73 | }
74 | 
75 | export default FieldInstallerTemplate;
76 | 


--------------------------------------------------------------------------------
/src/fields/ForeignKey.js:
--------------------------------------------------------------------------------
 1 | import RelationalField from "./RelationalField";
 2 | 
 3 | import {
 4 |     forwardsManyToOneDescriptor,
 5 |     backwardsManyToOneDescriptor,
 6 | } from "../descriptors";
 7 | 
 8 | /**
 9 |  * @memberof module:fields
10 |  */
11 | export class ForeignKey extends RelationalField {
12 |     createForwardsDescriptor(fieldName, model, toModel, throughModel) {
13 |         return forwardsManyToOneDescriptor(fieldName, toModel.modelName);
14 |     }
15 | 
16 |     createBackwardsDescriptor(fieldName, model, toModel, throughModel) {
17 |         return backwardsManyToOneDescriptor(fieldName, model.modelName);
18 |     }
19 | 
20 |     get index() {
21 |         return true;
22 |     }
23 | }
24 | 
25 | export default ForeignKey;
26 | 


--------------------------------------------------------------------------------
/src/fields/ManyToMany.js:
--------------------------------------------------------------------------------
  1 | import RelationalField from "./RelationalField";
  2 | 
  3 | import { manyToManyDescriptor } from "../descriptors";
  4 | 
  5 | import { m2mName, m2mToFieldName, m2mFromFieldName } from "../utils";
  6 | 
  7 | /**
  8 |  * @memberof module:fields
  9 |  */
 10 | export class ManyToMany extends RelationalField {
 11 |     getDefault() {
 12 |         return [];
 13 |     }
 14 | 
 15 |     getThroughModelName(fieldName, model) {
 16 |         return this.through || m2mName(model.modelName, fieldName);
 17 |     }
 18 | 
 19 |     createForwardsDescriptor(fieldName, model, toModel, throughModel) {
 20 |         return manyToManyDescriptor(
 21 |             model.modelName,
 22 |             toModel.modelName,
 23 |             throughModel.modelName,
 24 |             this.getThroughFields(fieldName, model, toModel, throughModel),
 25 |             false
 26 |         );
 27 |     }
 28 | 
 29 |     createBackwardsDescriptor(fieldName, model, toModel, throughModel) {
 30 |         return manyToManyDescriptor(
 31 |             model.modelName,
 32 |             toModel.modelName,
 33 |             throughModel.modelName,
 34 |             this.getThroughFields(fieldName, model, toModel, throughModel),
 35 |             true
 36 |         );
 37 |     }
 38 | 
 39 |     createBackwardsVirtualField(fieldName, model, toModel, throughModel) {
 40 |         const ThisField = this.getClass();
 41 |         return new ThisField({
 42 |             to: model.modelName,
 43 |             relatedName: fieldName,
 44 |             through: throughModel.modelName,
 45 |             throughFields: this.getThroughFields(
 46 |                 fieldName,
 47 |                 model,
 48 |                 toModel,
 49 |                 throughModel
 50 |             ),
 51 |         });
 52 |     }
 53 | 
 54 |     createForwardsVirtualField(fieldName, model, toModel, throughModel) {
 55 |         const ThisField = this.getClass();
 56 |         return new ThisField({
 57 |             to: toModel.modelName,
 58 |             relatedName: fieldName,
 59 |             through: this.through,
 60 |             throughFields: this.getThroughFields(
 61 |                 fieldName,
 62 |                 model,
 63 |                 toModel,
 64 |                 throughModel
 65 |             ),
 66 |             as: this.as,
 67 |         });
 68 |     }
 69 | 
 70 |     get installsForwardsVirtualField() {
 71 |         return true;
 72 |     }
 73 | 
 74 |     getThroughFields(fieldName, model, toModel, throughModel) {
 75 |         if (this.throughFields) {
 76 |             const [fieldAName, fieldBName] = this.throughFields;
 77 |             const fieldA = throughModel.fields[fieldAName];
 78 |             return {
 79 |                 to: fieldA.references(toModel) ? fieldAName : fieldBName,
 80 |                 from: fieldA.references(toModel) ? fieldBName : fieldAName,
 81 |             };
 82 |         }
 83 | 
 84 |         if (model.modelName === toModel.modelName) {
 85 |             /**
 86 |              * we have no way of determining the relationship's
 87 |              * direction here, so we need to assume that the user
 88 |              * did not use a custom through model
 89 |              * see ORM#registerManyToManyModelsFor
 90 |              */
 91 |             return {
 92 |                 to: m2mToFieldName(toModel.modelName),
 93 |                 from: m2mFromFieldName(model.modelName),
 94 |             };
 95 |         }
 96 | 
 97 |         /**
 98 |          * determine which field references which model
 99 |          * and infer the directions from that
100 |          */
101 |         const throughModelFieldReferencing = (otherModel) =>
102 |             Object.keys(throughModel.fields).find((someFieldName) =>
103 |                 throughModel.fields[someFieldName].references(otherModel)
104 |             );
105 | 
106 |         return {
107 |             to: throughModelFieldReferencing(toModel),
108 |             from: throughModelFieldReferencing(model),
109 |         };
110 |     }
111 | }
112 | 
113 | export default ManyToMany;
114 | 


--------------------------------------------------------------------------------
/src/fields/OneToOne.js:
--------------------------------------------------------------------------------
 1 | import RelationalField from "./RelationalField";
 2 | 
 3 | import {
 4 |     forwardsOneToOneDescriptor,
 5 |     backwardsOneToOneDescriptor,
 6 | } from "../descriptors";
 7 | 
 8 | /**
 9 |  * @memberof module:fields
10 |  */
11 | export class OneToOne extends RelationalField {
12 |     getBackwardsFieldName(model) {
13 |         return this.relatedName || model.modelName.toLowerCase();
14 |     }
15 | 
16 |     createForwardsDescriptor(fieldName, model, toModel, throughModel) {
17 |         return forwardsOneToOneDescriptor(fieldName, toModel.modelName);
18 |     }
19 | 
20 |     createBackwardsDescriptor(fieldName, model, toModel, throughModel) {
21 |         return backwardsOneToOneDescriptor(fieldName, model.modelName);
22 |     }
23 | }
24 | 
25 | export default OneToOne;
26 | 


--------------------------------------------------------------------------------
/src/fields/RelationalField.js:
--------------------------------------------------------------------------------
 1 | /* eslint-disable max-classes-per-file */
 2 | import Field from "./Field";
 3 | import DefaultFieldInstaller from "./DefaultFieldInstaller";
 4 | 
 5 | import { reverseFieldName, normalizeModelReference } from "../utils";
 6 | 
 7 | /**
 8 |  * @private
 9 |  * @memberof module:fields
10 |  */
11 | export class RelationalField extends Field {
12 |     constructor(...args) {
13 |         super();
14 |         if (args.length === 1 && typeof args[0] === "object") {
15 |             const opts = args[0];
16 |             this.toModelName = normalizeModelReference(opts.to);
17 |             this.relatedName = opts.relatedName;
18 |             this.through = normalizeModelReference(opts.through);
19 |             this.throughFields = opts.throughFields;
20 |             this.as = opts.as;
21 |         } else {
22 |             [this.toModelName, this.relatedName] = [
23 |                 normalizeModelReference(args[0]),
24 |                 args[1],
25 |             ];
26 |         }
27 |     }
28 | 
29 |     getBackwardsFieldName(model) {
30 |         return this.relatedName || reverseFieldName(model.modelName);
31 |     }
32 | 
33 |     createBackwardsVirtualField(fieldName, model, toModel, throughModel) {
34 |         const ThisField = this.getClass();
35 |         return new ThisField(model.modelName, fieldName);
36 |     }
37 | 
38 |     get installsBackwardsVirtualField() {
39 |         return true;
40 |     }
41 | 
42 |     get installsBackwardsDescriptor() {
43 |         return true;
44 |     }
45 | 
46 |     references(model) {
47 |         return this.toModelName === model.modelName;
48 |     }
49 | 
50 |     get installerClass() {
51 |         return class AliasedForwardsDescriptorInstaller extends DefaultFieldInstaller {
52 |             installForwardsDescriptor() {
53 |                 Object.defineProperty(
54 |                     this.model.prototype,
55 |                     this.field.as || this.fieldName, // use supplied name if possible
56 |                     this.field.createForwardsDescriptor(
57 |                         this.fieldName,
58 |                         this.model,
59 |                         this.toModel,
60 |                         this.throughModel
61 |                     )
62 |                 );
63 |             }
64 |         };
65 |     }
66 | }
67 | 
68 | export default RelationalField;
69 | 


--------------------------------------------------------------------------------
/src/fields/index.d.ts:
--------------------------------------------------------------------------------
 1 | export declare class Field {
 2 |     readonly index: boolean;
 3 | }
 4 | 
 5 | interface AttributeOpts {
 6 |     getDefault?: () => any;
 7 | }
 8 | 
 9 | export declare class Attribute extends Field {
10 |     constructor(opts?: AttributeOpts);
11 |     ["type"]: "attr";
12 | }
13 | 
14 | interface AttributeWithDefault extends Attribute {
15 |     getDefault(): any;
16 | }
17 | 
18 | interface RelationalFieldOpts {
19 |     to: string;
20 |     relatedName?: string;
21 |     through?: string;
22 |     throughFields?: {
23 |         to: string;
24 |         from: string;
25 |     };
26 |     as?: string;
27 | }
28 | 
29 | export declare class RelationalField extends Field {
30 |     constructor(toModelName: string, relatedName?: string);
31 |     constructor(opts: RelationalFieldOpts);
32 | }
33 | 
34 | export declare class OneToOne extends RelationalField {
35 |     ["type"]: "oneToOne";
36 | }
37 | 
38 | export declare class ForeignKey extends RelationalField {
39 |     readonly index: true;
40 |     ["type"]: "fk";
41 | }
42 | 
43 | export declare class ManyToMany extends RelationalField {
44 |     readonly index: false;
45 |     ["type"]: "many";
46 | }
47 | 
48 | interface AttrCreator {
49 |     (): Attribute;
50 |     (opts: AttributeOpts): AttributeWithDefault;
51 | }
52 | 
53 | interface FkCreator {
54 |     (toModelName: string, relatedName?: string): ForeignKey;
55 |     (opts: RelationalFieldOpts): ForeignKey;
56 | }
57 | 
58 | interface ManyCreator {
59 |     (toModelName: string, relatedName?: string): ManyToMany;
60 |     (opts: RelationalFieldOpts): ManyToMany;
61 | }
62 | 
63 | interface OneToOneCreator {
64 |     (toModelName: string, relatedName?: string): OneToOne;
65 |     (opts: RelationalFieldOpts): OneToOne;
66 | }
67 | 
68 | export declare const attr: AttrCreator;
69 | 
70 | export declare const oneToOne: OneToOneCreator;
71 | 
72 | export declare const fk: FkCreator;
73 | 
74 | export declare const many: ManyCreator;
75 | 
76 | interface FieldSpecMap {
77 |     [K: string]: Attribute | ForeignKey | ManyToMany | OneToOne;
78 | }
79 | 


--------------------------------------------------------------------------------
/src/helpers.d.ts:
--------------------------------------------------------------------------------
 1 | /**
 2 |  * Credits to Piotr Witek (http://piotrwitek.github.io) and utility-type project (https://github.com/piotrwitek/utility-types)
 3 |  */
 4 | 
 5 | export declare type Assign<
 6 |     T extends object,
 7 |     U extends object,
 8 |     I = Diff<T, U> & Intersection<U, T> & Diff<U, T>
 9 | > = Pick<I, keyof I>;
10 | 
11 | export declare type Diff<T extends object, U extends object> = Pick<
12 |     T,
13 |     Exclude<keyof T, keyof U>
14 | >;
15 | 
16 | export declare type PickByValue<T, ValueType> = Pick<
17 |     T,
18 |     { [Key in keyof T]: T[Key] extends ValueType ? Key : never }[keyof T]
19 | >;
20 | 
21 | export type Intersection<T extends object, U extends object> = Pick<
22 |     T,
23 |     Extract<keyof T, keyof U> & Extract<keyof U, keyof T>
24 | >;
25 | 
26 | export declare type OptionalKeys<T> = {
27 |     [K in keyof T]-?: {} extends Pick<T, K> ? K : never;
28 | }[keyof T];
29 | 
30 | export declare type KnownKeys<T> = {
31 |     [K in keyof T]: string extends K ? never : number extends K ? never : K;
32 | } extends { [_ in keyof T]: infer U }
33 |     ? U
34 |     : never;
35 | 


--------------------------------------------------------------------------------
/src/index.d.ts:
--------------------------------------------------------------------------------
 1 | import { ORM } from "./ORM";
 2 | import Model from "./Model";
 3 | import QuerySet from "./QuerySet";
 4 | import { createDatabase } from "./db";
 5 | import { attr, fk, many, oneToOne } from "./fields";
 6 | import { createReducer, createSelector } from "./redux";
 7 | 
 8 | import type {
 9 |     CreateProps,
10 |     CustomInstanceProps,
11 |     IdKey,
12 |     IdOrModelLike,
13 |     IdType,
14 |     ModelField,
15 |     ModelFieldMap,
16 |     Ref,
17 |     RefPropOrSimple,
18 |     SessionBoundModel,
19 |     UpdateProps,
20 |     UpsertProps,
21 | } from "./Model";
22 | import type { MutableQuerySet } from "./QuerySet";
23 | import type { OrmSession } from "./Session";
24 | import type { ORMOpts, OrmState } from "./ORM";
25 | import type { TableOpts, TableState } from "./db/Table";
26 | import type { defaultUpdater, ORMReducer, ORMSelector } from "./redux";
27 | import type {
28 |     Attribute,
29 |     FieldSpecMap,
30 |     ForeignKey,
31 |     ManyToMany,
32 |     OneToOne,
33 | } from "./fields";
34 | 
35 | export type {
36 |     OrmState,
37 |     OrmSession,
38 |     ORMOpts,
39 |     Attribute,
40 |     OneToOne,
41 |     ForeignKey,
42 |     ManyToMany,
43 |     FieldSpecMap,
44 |     TableOpts,
45 |     RefPropOrSimple,
46 |     ModelFieldMap,
47 |     CustomInstanceProps,
48 |     UpsertProps,
49 |     CreateProps,
50 |     UpdateProps,
51 |     ModelField,
52 |     OrmSession as Session,
53 |     MutableQuerySet,
54 |     ORMSelector,
55 |     ORMReducer,
56 |     IdOrModelLike,
57 |     defaultUpdater,
58 |     TableState,
59 |     Ref,
60 |     SessionBoundModel,
61 |     IdKey,
62 |     IdType,
63 | };
64 | 
65 | export {
66 |     createDatabase,
67 |     createSelector,
68 |     createReducer,
69 |     ORM,
70 |     Model,
71 |     QuerySet,
72 |     attr,
73 |     oneToOne,
74 |     fk,
75 |     many,
76 | };
77 | 
78 | export default Model;
79 | 


--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
 1 | import QuerySet from "./QuerySet";
 2 | import Model from "./Model";
 3 | import { DeprecatedSchema, ORM } from "./ORM";
 4 | import Session from "./Session";
 5 | import { createReducer, createSelector } from "./redux";
 6 | import ForeignKey from "./fields/ForeignKey";
 7 | import ManyToMany from "./fields/ManyToMany";
 8 | import OneToOne from "./fields/OneToOne";
 9 | import Attribute from "./fields/Attribute";
10 | import { fk, many, oneToOne, attr } from "./fields";
11 | 
12 | const Schema = DeprecatedSchema;
13 | 
14 | const Backend = function RemovedBackend() {
15 |     throw new Error(
16 |         "Having a custom Backend instance is now unsupported. " +
17 |             "Documentation for database customization is upcoming, for now " +
18 |             "please look at the db folder in the source."
19 |     );
20 | };
21 | 
22 | export {
23 |     Attribute,
24 |     QuerySet,
25 |     Model,
26 |     ORM,
27 |     Schema,
28 |     Backend,
29 |     Session,
30 |     ForeignKey,
31 |     ManyToMany,
32 |     OneToOne,
33 |     fk,
34 |     many,
35 |     attr,
36 |     oneToOne,
37 |     createReducer,
38 |     createSelector,
39 | };
40 | 
41 | export default Model;
42 | 


--------------------------------------------------------------------------------
/src/redux.d.ts:
--------------------------------------------------------------------------------
 1 | import { IndexedModelClasses, ORM, OrmState } from "./ORM";
 2 | import { OrmSession } from "./Session";
 3 | 
 4 | interface ORMReducer<I extends IndexedModelClasses, TAction extends any = any> {
 5 |     (state: OrmState<I> | undefined, action: TAction): OrmState<I>;
 6 | }
 7 | 
 8 | type defaultUpdater<
 9 |     I extends IndexedModelClasses,
10 |     TAction extends any = any
11 | > = (session: OrmSession<I>, action: TAction) => void;
12 | 
13 | declare function createReducer<
14 |     I extends IndexedModelClasses,
15 |     TAction extends any = any
16 | >(orm: ORM<I>, updater?: defaultUpdater<I, TAction>): ORMReducer<I, TAction>;
17 | 
18 | type Selector<S, R> = (state: S) => R;
19 | 
20 | interface ORMSelector<I extends IndexedModelClasses, Args extends any[], R> {
21 |     (session: OrmSession<I>, ...args: Args): R;
22 | }
23 | 
24 | declare function createSelector<S, I, R1, R2, R3, R4, R5, R6, R>(
25 |     orm: ORM<I>,
26 |     ormStateSelector: Selector<S, OrmState<I>>,
27 |     selector1: Selector<S, R1>,
28 |     selector2: Selector<S, R2>,
29 |     selector3: Selector<S, R3>,
30 |     selector4: Selector<S, R4>,
31 |     selector5: Selector<S, R5>,
32 |     selector6: Selector<S, R6>,
33 |     ormSelector: ORMSelector<I, [R1, R2, R3, R4, R5, R6], R>
34 | ): Selector<S, R>;
35 | 
36 | declare function createSelector<S, I, R1, R2, R3, R4, R5, R>(
37 |     orm: ORM<I>,
38 |     ormStateSelector: Selector<S, OrmState<I>>,
39 |     selector1: Selector<S, R1>,
40 |     selector2: Selector<S, R2>,
41 |     selector3: Selector<S, R3>,
42 |     selector4: Selector<S, R4>,
43 |     selector5: Selector<S, R5>,
44 |     ormSelector: ORMSelector<I, [R1, R2, R3, R4, R5], R>
45 | ): Selector<S, R>;
46 | 
47 | declare function createSelector<S, I, R1, R2, R3, R4, R>(
48 |     orm: ORM<I>,
49 |     ormStateSelector: Selector<S, OrmState<I>>,
50 |     selector1: Selector<S, R1>,
51 |     selector2: Selector<S, R2>,
52 |     selector3: Selector<S, R3>,
53 |     selector4: Selector<S, R4>,
54 |     ormSelector: ORMSelector<I, [R1, R2, R3, R4], R>
55 | ): Selector<S, R>;
56 | 
57 | declare function createSelector<S, I, R1, R2, R3, R>(
58 |     orm: ORM<I>,
59 |     ormStateSelector: Selector<S, OrmState<I>>,
60 |     selector1: Selector<S, R1>,
61 |     selector2: Selector<S, R2>,
62 |     selector3: Selector<S, R3>,
63 |     ormSelector: ORMSelector<I, [R1, R2, R3], R>
64 | ): Selector<S, R>;
65 | 
66 | declare function createSelector<S, I, R1, R2, R>(
67 |     orm: ORM<I>,
68 |     ormStateSelector: Selector<S, OrmState<I>>,
69 |     selector1: Selector<S, R1>,
70 |     selector2: Selector<S, R2>,
71 |     ormSelector: ORMSelector<I, [R1, R2], R>
72 | ): Selector<S, R>;
73 | 
74 | declare function createSelector<S, I, R1, R>(
75 |     orm: ORM<I>,
76 |     ormStateSelector: Selector<S, OrmState<I>>,
77 |     selector1: Selector<S, R1>,
78 |     ormSelector: ORMSelector<I, [R1], R>
79 | ): Selector<S, R>;
80 | 
81 | declare function createSelector<S, I, R>(
82 |     orm: ORM<I>,
83 |     ormStateSelector: Selector<S, OrmState<I>>,
84 |     ormSelector: ORMSelector<I, [], R>
85 | ): Selector<S, R>;
86 | 
87 | declare function createSelector<I, R>(
88 |     orm: ORM<I>,
89 |     ormSelector: ORMSelector<I, [], R>
90 | ): Selector<OrmState<I>, R>;
91 | 


--------------------------------------------------------------------------------
/src/selectors/FieldSelectorSpec.js:
--------------------------------------------------------------------------------
  1 | import MapSelectorSpec from "./MapSelectorSpec";
  2 | import ModelSelectorSpec from "./ModelSelectorSpec";
  3 | import ModelBasedSelectorSpec from "./ModelBasedSelectorSpec";
  4 | import idArgSelector from "./idArgSelector";
  5 | 
  6 | import QuerySet from "../QuerySet";
  7 | import Model from "../Model";
  8 | 
  9 | import ForeignKey from "../fields/ForeignKey";
 10 | import ManyToMany from "../fields/ManyToMany";
 11 | 
 12 | export default class FieldSelectorSpec extends ModelBasedSelectorSpec {
 13 |     constructor({ field, fieldModel, accessorName, isVirtual, ...other }) {
 14 |         super(other);
 15 |         this._field = field;
 16 |         this._fieldModel = fieldModel;
 17 |         this._accessorName = accessorName;
 18 |         this._isVirtual = isVirtual;
 19 |     }
 20 | 
 21 |     get key() {
 22 |         return this._accessorName;
 23 |     }
 24 | 
 25 |     get dependencies() {
 26 |         return [this._orm, idArgSelector];
 27 |     }
 28 | 
 29 |     valueForInstance(instance, session) {
 30 |         if (!instance) {
 31 |             return null;
 32 |         }
 33 |         let value;
 34 |         if (this._parent instanceof ModelSelectorSpec) {
 35 |             /* orm.Model.field */
 36 |             value = instance[this._accessorName];
 37 |         } else {
 38 |             /* orm.Model.field1.field2..fieldN.field */
 39 |             const { [this._parent.toModelName]: ParentToModel } = session;
 40 |             const parentRef = this._parent.valueForInstance(instance, session);
 41 |             const parentInstance = parentRef
 42 |                 ? new ParentToModel(parentRef)
 43 |                 : null;
 44 |             value = parentInstance ? parentInstance[this._accessorName] : null;
 45 |         }
 46 |         if (value instanceof Model) {
 47 |             return value.ref;
 48 |         }
 49 |         if (value instanceof QuerySet) {
 50 |             return value.toRefArray();
 51 |         }
 52 |         return value;
 53 |     }
 54 | 
 55 |     map(selector) {
 56 |         if (selector instanceof ModelSelectorSpec) {
 57 |             if (this.toModelName === selector.model.modelName) {
 58 |                 throw new Error(
 59 |                     `Cannot select models in a \`map()\` call. If you just want the \`${this._accessorName}\` as a ref array then you can simply drop the \`map()\`. Otherwise make sure you're passing a field selector of the form \`${this.toModelName}.<field>\` or a custom selector instead.`
 60 |                 );
 61 |             } else {
 62 |                 throw new Error(
 63 |                     `Cannot select \`${selector.model.modelName}\` models in this \`map()\` call. Make sure you're passing a field selector of the form \`${this.toModelName}.<field>\` or a custom selector instead.`
 64 |                 );
 65 |             }
 66 |         } else if (
 67 |             selector instanceof FieldSelectorSpec ||
 68 |             selector instanceof MapSelectorSpec
 69 |         ) {
 70 |             if (this.toModelName !== selector.model.modelName) {
 71 |                 throw new Error(
 72 |                     `Cannot select fields of the \`${selector.model.modelName}\` model in this \`map()\` call. Make sure you're passing a field selector of the form \`${this.toModelName}.<field>\` or a custom selector instead.`
 73 |                 );
 74 |             }
 75 |         } else if (
 76 |             !selector ||
 77 |             typeof selector !== "function" ||
 78 |             !selector.recomputations
 79 |         ) {
 80 |             throw new Error(
 81 |                 `\`map()\` requires a selector as an input. Received: ${JSON.stringify(
 82 |                     selector
 83 |                 )} of type ${typeof selector}`
 84 |             );
 85 |         }
 86 |         if (
 87 |             !(this._field instanceof ForeignKey) &&
 88 |             !(this._field instanceof ManyToMany)
 89 |         ) {
 90 |             throw new Error("Cannot map selectors for non-collection fields");
 91 |         }
 92 |         return new MapSelectorSpec({
 93 |             parent: this,
 94 |             model: this._model,
 95 |             orm: this._orm,
 96 |             field: this._field,
 97 |             selector,
 98 |         });
 99 |     }
100 | 
101 |     get toModelName() {
102 |         return this._field.toModelName === "this"
103 |             ? this._fieldModel.modelName
104 |             : this._field.toModelName;
105 |     }
106 | 
107 |     get toModel() {
108 |         const db = this._orm.getDatabase();
109 |         return db.describe(this.toModelName);
110 |     }
111 | }
112 | 


--------------------------------------------------------------------------------
/src/selectors/MapSelectorSpec.js:
--------------------------------------------------------------------------------
 1 | import ModelBasedSelectorSpec from "./ModelBasedSelectorSpec";
 2 | import idArgSelector from "./idArgSelector";
 3 | 
 4 | export default class MapSelectorSpec extends ModelBasedSelectorSpec {
 5 |     constructor({ field, selector, ...other }) {
 6 |         super(other);
 7 |         this._field = field;
 8 |         this._selector = selector;
 9 |     }
10 | 
11 |     createResultFunc(parentSelector) {
12 |         const { idAttribute } = this._parent.toModel;
13 |         return (state, ...other) => {
14 |             /**
15 |              * The parent selector should return a ref array
16 |              * in case of a single ID being passed.
17 |              * Otherwise it should return an array of ref arrays.
18 |              */
19 |             const parentResult = parentSelector(state, ...other);
20 |             const idArg = idArgSelector(state, ...other);
21 |             const single = (refArray) => {
22 |                 if (refArray === null) {
23 |                     // an intermediate field could not be resolved
24 |                     return null;
25 |                 }
26 |                 return refArray.map((ref) =>
27 |                     this._selector(state, ref[idAttribute])
28 |                 );
29 |             };
30 |             if (typeof idArg === "undefined" || Array.isArray(idArg)) {
31 |                 return parentResult.map(single);
32 |             }
33 |             return single(parentResult);
34 |         };
35 |     }
36 | 
37 |     get selector() {
38 |         return this._selector;
39 |     }
40 | 
41 |     set selector(selector) {
42 |         this._selector = selector;
43 |     }
44 | 
45 |     get key() {
46 |         return this._selector;
47 |     }
48 | }
49 | 


--------------------------------------------------------------------------------
/src/selectors/ModelBasedSelectorSpec.js:
--------------------------------------------------------------------------------
 1 | import SelectorSpec from "./SelectorSpec";
 2 | 
 3 | export default class ModelBasedSelectorSpec extends SelectorSpec {
 4 |     constructor({ model, ...other }) {
 5 |         super(other);
 6 |         this._model = model;
 7 |     }
 8 | 
 9 |     get resultFunc() {
10 |         return (session, idArg, ...other) => {
11 |             const { [this._model.modelName]: ModelClass } = session;
12 |             if (typeof idArg === "undefined") {
13 |                 return ModelClass.all()
14 |                     .toModelArray()
15 |                     .map((instance) =>
16 |                         this.valueForInstance(instance, session, ...other)
17 |                     );
18 |             }
19 |             if (Array.isArray(idArg)) {
20 |                 return idArg.map((id) =>
21 |                     this.valueForInstance(
22 |                         ModelClass.withId(id),
23 |                         session,
24 |                         ...other
25 |                     )
26 |                 );
27 |             }
28 |             return this.valueForInstance(
29 |                 ModelClass.withId(idArg),
30 |                 session,
31 |                 ...other
32 |             );
33 |         };
34 |     }
35 | 
36 |     get model() {
37 |         return this._model;
38 |     }
39 | }
40 | 


--------------------------------------------------------------------------------
/src/selectors/ModelSelectorSpec.js:
--------------------------------------------------------------------------------
 1 | import SelectorSpec from "./SelectorSpec";
 2 | import idArgSelector from "./idArgSelector";
 3 | 
 4 | export default class ModelSelectorSpec extends SelectorSpec {
 5 |     constructor({ model, ...other }) {
 6 |         super(other);
 7 |         this._model = model;
 8 |     }
 9 | 
10 |     get key() {
11 |         return this._model.modelName;
12 |     }
13 | 
14 |     get dependencies() {
15 |         return [this._orm, idArgSelector];
16 |     }
17 | 
18 |     get resultFunc() {
19 |         return ({ [this._model.modelName]: ModelClass }, idArg) => {
20 |             if (typeof idArg === "undefined") {
21 |                 return ModelClass.all().toRefArray();
22 |             }
23 |             if (Array.isArray(idArg)) {
24 |                 return idArg.map((id) => {
25 |                     const instance = ModelClass.withId(id);
26 |                     return instance ? instance.ref : null;
27 |                 });
28 |             }
29 |             const instance = ModelClass.withId(idArg);
30 |             return instance ? instance.ref : null;
31 |         };
32 |     }
33 | 
34 |     get model() {
35 |         return this._model;
36 |     }
37 | }
38 | 


--------------------------------------------------------------------------------
/src/selectors/SelectorSpec.js:
--------------------------------------------------------------------------------
 1 | import { ID_ARG_KEY_SELECTOR } from "../constants";
 2 | 
 3 | export default class SelectorSpec {
 4 |     constructor({ parent, orm }) {
 5 |         this._parent = parent;
 6 |         this._orm = orm;
 7 |         this.keySelector = ID_ARG_KEY_SELECTOR;
 8 |     }
 9 | 
10 |     get cachePath() {
11 |         const basePath = this._parent ? this._parent.cachePath : [];
12 |         return [...basePath, this.key];
13 |     }
14 | 
15 |     get orm() {
16 |         return this._orm;
17 |     }
18 | 
19 |     get parent() {
20 |         return this._parent;
21 |     }
22 | }
23 | 


--------------------------------------------------------------------------------
/src/selectors/idArgSelector.js:
--------------------------------------------------------------------------------
1 | export default function idArgSelector(state, idArg) {
2 |     return idArg;
3 | }
4 | 


--------------------------------------------------------------------------------
/src/selectors/index.js:
--------------------------------------------------------------------------------
  1 | import ForeignKey from "../fields/ForeignKey";
  2 | import ManyToMany from "../fields/ManyToMany";
  3 | import RelationalField from "../fields/RelationalField";
  4 | 
  5 | import FieldSelectorSpec from "./FieldSelectorSpec";
  6 | import ModelSelectorSpec from "./ModelSelectorSpec";
  7 | 
  8 | /**
  9 |  * @module selectors
 10 |  * @private
 11 |  */
 12 | 
 13 | export function createFieldSelectorSpec({
 14 |     parent,
 15 |     model,
 16 |     field,
 17 |     fieldModel,
 18 |     accessorName,
 19 |     orm,
 20 |     isVirtual,
 21 | }) {
 22 |     const fieldSelectorSpec = new FieldSelectorSpec({
 23 |         parent,
 24 |         model,
 25 |         field,
 26 |         fieldModel,
 27 |         accessorName,
 28 |         orm,
 29 |         isVirtual,
 30 |     });
 31 |     /* Do not even try to create field selectors below attributes. */
 32 |     if (!(field instanceof RelationalField)) {
 33 |         // "orm.Author.name.publisher" would be nonsense
 34 |         return fieldSelectorSpec;
 35 |     }
 36 |     /* Prevent field selectors below collections. */
 37 |     if (parent instanceof FieldSelectorSpec) {
 38 |         /* eslint-disable no-underscore-dangle */
 39 |         if (
 40 |             // "orm.Author.books.publisher" would be nonsense
 41 |             (parent._field instanceof ForeignKey && parent._isVirtual) ||
 42 |             // "orm.Genre.books.publisher" would be nonsense
 43 |             parent._field instanceof ManyToMany
 44 |         ) {
 45 |             throw new Error(
 46 |                 `Cannot create a selector for \`${parent._accessorName}.${accessorName}\` because \`${parent._accessorName}\` is a collection field.`
 47 |             );
 48 |         }
 49 |     }
 50 |     const { toModelName } = field;
 51 |     const toModel = orm.get(
 52 |         toModelName === "this" ? model.modelName : toModelName
 53 |     );
 54 |     Object.entries(toModel.fields).forEach(
 55 |         ([relatedFieldName, relatedField]) => {
 56 |             const fieldAccessorName = relatedField.as || relatedFieldName;
 57 |             Object.defineProperty(fieldSelectorSpec, fieldAccessorName, {
 58 |                 get: () =>
 59 |                     createFieldSelectorSpec({
 60 |                         parent: fieldSelectorSpec,
 61 |                         model,
 62 |                         fieldModel: toModel,
 63 |                         field: relatedField,
 64 |                         accessorName: fieldAccessorName,
 65 |                         orm,
 66 |                         isVirtual: false,
 67 |                     }),
 68 |             });
 69 |         }
 70 |     );
 71 |     Object.entries(toModel.virtualFields).forEach(
 72 |         ([relatedFieldName, relatedField]) => {
 73 |             const fieldAccessorName = relatedField.as || relatedFieldName;
 74 |             if (fieldSelectorSpec.hasOwnProperty(fieldAccessorName)) {
 75 |                 return;
 76 |             }
 77 |             Object.defineProperty(fieldSelectorSpec, fieldAccessorName, {
 78 |                 get: () =>
 79 |                     createFieldSelectorSpec({
 80 |                         parent: fieldSelectorSpec,
 81 |                         model,
 82 |                         fieldModel: toModel,
 83 |                         field: relatedField,
 84 |                         accessorName: fieldAccessorName,
 85 |                         orm,
 86 |                         isVirtual: true,
 87 |                     }),
 88 |             });
 89 |         }
 90 |     );
 91 |     return fieldSelectorSpec;
 92 | }
 93 | 
 94 | export function createModelSelectorSpec({ model, orm }) {
 95 |     const modelSelectorSpec = new ModelSelectorSpec({
 96 |         parent: null,
 97 |         orm,
 98 |         model,
 99 |     });
100 | 
101 |     Object.entries(model.fields).forEach(([fieldName, field]) => {
102 |         const fieldAccessorName = field.as || fieldName;
103 |         Object.defineProperty(modelSelectorSpec, fieldAccessorName, {
104 |             get: () =>
105 |                 createFieldSelectorSpec({
106 |                     parent: modelSelectorSpec,
107 |                     model,
108 |                     fieldModel: model,
109 |                     field,
110 |                     accessorName: fieldAccessorName,
111 |                     orm,
112 |                     isVirtual: false,
113 |                 }),
114 |         });
115 |     });
116 | 
117 |     Object.entries(model.virtualFields).forEach(([fieldName, field]) => {
118 |         const fieldAccessorName = field.as || fieldName;
119 |         if (modelSelectorSpec.hasOwnProperty(fieldAccessorName)) {
120 |             return;
121 |         }
122 |         Object.defineProperty(modelSelectorSpec, fieldAccessorName, {
123 |             get: () =>
124 |                 createFieldSelectorSpec({
125 |                     parent: modelSelectorSpec,
126 |                     model,
127 |                     fieldModel: model,
128 |                     field,
129 |                     accessorName: fieldAccessorName,
130 |                     orm,
131 |                     isVirtual: true,
132 |                 }),
133 |         });
134 |     });
135 | 
136 |     return modelSelectorSpec;
137 | }
138 | 


--------------------------------------------------------------------------------
/src/test/.eslintrc.js:
--------------------------------------------------------------------------------
 1 | module.exports = {
 2 |     extends: [
 3 |         "airbnb-base",
 4 |         "plugin:prettier/recommended",
 5 |         "plugin:jest/recommended",
 6 |     ],
 7 |     env: {
 8 |         jest: true,
 9 |     },
10 |     plugins: ["jest"],
11 |     parser: "babel-eslint",
12 |     rules: {
13 |         "no-unused-vars": 0,
14 |         "id-length": 0,
15 |         "no-underscore-dangle": 0,
16 |         "import/no-named-as-default": 0,
17 |         "import/no-extraneous-dependencies": 0,
18 |         "max-len": 0,
19 |         "comma-dangle": 0,
20 |         "arrow-parens": 0,
21 |         "no-plusplus": 0,
22 |         "no-console": 0,
23 |         "no-unsafe-negation": 0,
24 |         "function-paren-newline": 0,
25 |         "prefer-rest-params": 0,
26 |         "max-classes-per-file": 0,
27 |     },
28 |     parserOptions: {
29 |         ecmaFeatures: {
30 |             ecmaVersion: 6,
31 |         },
32 |     },
33 | };
34 | 


--------------------------------------------------------------------------------
/src/test/ReferencedModel.js:
--------------------------------------------------------------------------------
1 | import { Model } from "../index";
2 | 
3 | export default class ReferencedModel extends Model {
4 |     static get modelName() {
5 |         return "ReferencedModel";
6 |     }
7 | }
8 | 


--------------------------------------------------------------------------------
/src/test/ReferencingModel.js:
--------------------------------------------------------------------------------
 1 | import { fk, many, Model, oneToOne } from "../index";
 2 | import ReferencedModel from "./ReferencedModel";
 3 | // eslint-disable-next-line import/no-cycle
 4 | import ThroughModel from "./ThroughModel";
 5 | 
 6 | export default class ReferencingModel extends Model {
 7 |     static get modelName() {
 8 |         return "ReferencingModel";
 9 |     }
10 | 
11 |     static get fields() {
12 |         return {
13 |             fkField: fk({
14 |                 to: ReferencedModel,
15 |                 relatedName: "reverseFkField",
16 |             }),
17 |             oneToOneField: oneToOne(ReferencedModel, "reverseOneToOneField"),
18 |             manyField: many({
19 |                 to: ReferencedModel,
20 |                 relatedName: "reverseManyField",
21 |                 through: ThroughModel,
22 |                 throughFields: ["referencingModel", "referencedModel"],
23 |             }),
24 |         };
25 |     }
26 | }
27 | 


--------------------------------------------------------------------------------
/src/test/ThroughModel.js:
--------------------------------------------------------------------------------
 1 | import { fk, Model } from "../index";
 2 | import ReferencedModel from "./ReferencedModel";
 3 | // eslint-disable-next-line import/no-cycle
 4 | import ReferencingModel from "./ReferencingModel";
 5 | 
 6 | export default class ThroughModel extends Model {
 7 |     static get modelName() {
 8 |         return "ThroughModel";
 9 |     }
10 | 
11 |     static get fields() {
12 |         return {
13 |             referencedModel: fk(ReferencedModel),
14 |             referencingModel: fk(ReferencingModel),
15 |         };
16 |     }
17 | }
18 | 


--------------------------------------------------------------------------------
/src/test/functional/concurrentSessions.js:
--------------------------------------------------------------------------------
 1 | import { createTestSessionWithData } from "../helpers";
 2 | 
 3 | describe("Multiple concurrent sessions", () => {
 4 |     let session;
 5 |     let orm;
 6 |     let state;
 7 | 
 8 |     beforeEach(() => {
 9 |         ({ session, orm, state } = createTestSessionWithData());
10 |     });
11 | 
12 |     it("separate sessions can manage separate data stores", () => {
13 |         const firstSession = session;
14 |         const secondSession = orm.session(state);
15 | 
16 |         expect(firstSession.Book.count()).toBe(3);
17 |         expect(secondSession.Book.count()).toBe(3);
18 | 
19 |         const newBookProps = {
20 |             name: "New Book",
21 |             author: 0,
22 |             releaseYear: 2015,
23 |             genres: [0, 1],
24 |         };
25 | 
26 |         firstSession.Book.create(newBookProps);
27 | 
28 |         expect(firstSession.Book.count()).toBe(4);
29 |         expect(secondSession.Book.count()).toBe(3);
30 |     });
31 | 
32 |     it("separate sessions have different session bound models", () => {
33 |         const firstSession = orm.session();
34 |         const secondSession = orm.session();
35 | 
36 |         expect(firstSession.Book).not.toBe(secondSession.Book);
37 |         expect(firstSession.Author).not.toBe(secondSession.Author);
38 |         expect(firstSession.Genre).not.toBe(secondSession.Genre);
39 |         expect(firstSession.Tag).not.toBe(secondSession.Tag);
40 |         expect(firstSession.Cover).not.toBe(secondSession.Cover);
41 |         expect(firstSession.Publisher).not.toBe(secondSession.Publisher);
42 |     });
43 | });
44 | 


--------------------------------------------------------------------------------
/src/test/functional/es5.js:
--------------------------------------------------------------------------------
 1 | const { Model, ORM, createSelector, fk } = require("../../../lib"); // eslint-disable-line import/no-unresolved
 2 | 
 3 | describe("ES5 library code", () => {
 4 |     describe("With ES6 client code", () => {
 5 |         let orm;
 6 |         let emptyState;
 7 |         let ormState;
 8 |         const stateSelector = () => ormState;
 9 |         let reducer;
10 | 
11 |         const CREATE_BOOK = "CREATE_BOOK";
12 |         const CREATE_AUTHOR = "CREATE_AUTHOR";
13 | 
14 |         beforeEach(() => {
15 |             class Book extends Model {}
16 |             Book.fields = {
17 |                 authorId: fk({
18 |                     to: "Author",
19 |                     as: "author",
20 |                     relatedName: "books",
21 |                 }),
22 |             };
23 |             Book.modelName = "Book";
24 |             class Author extends Model {}
25 |             Author.modelName = "Author";
26 |             orm = new ORM({ stateSelector });
27 |             orm.register(Book, Author);
28 |             reducer = (state, action) => {
29 |                 const session = orm.session(state || orm.getEmptyState());
30 |                 switch (action.type) {
31 |                     case CREATE_AUTHOR:
32 |                         session.Author.create(action.payload);
33 |                         break;
34 |                     case CREATE_BOOK:
35 |                         session.Book.create(action.payload);
36 |                         break;
37 |                     default:
38 |                         break;
39 |                 }
40 |                 return session.state;
41 |             };
42 |             emptyState = orm.getEmptyState();
43 |             ormState = emptyState;
44 |         });
45 | 
46 |         it("Model CRUD works", () => {
47 |             const session = orm.session();
48 |             let book;
49 |             expect(() => {
50 |                 book = session.Book.create({ id: 1, title: "title" });
51 |             }).not.toThrow();
52 |             expect(() => {
53 |                 book.update({ id: 1, title: "new title" });
54 |             }).not.toThrow();
55 |         });
56 | 
57 |         it("Selectors can be created from selector specs", () => {
58 |             const bookAuthor = createSelector(orm.Book.author);
59 |             expect(bookAuthor(ormState, 1)).toBe(null);
60 |             ormState = reducer(ormState, {
61 |                 type: CREATE_BOOK,
62 |                 payload: {
63 |                     id: 1,
64 |                     title: "title",
65 |                     authorId: 123,
66 |                 },
67 |             });
68 |             expect(bookAuthor(ormState, 1)).toBe(null);
69 |             ormState = reducer(ormState, {
70 |                 type: CREATE_AUTHOR,
71 |                 payload: {
72 |                     id: 123,
73 |                 },
74 |             });
75 |             expect(bookAuthor(ormState, 1)).toEqual({ id: 123 });
76 |         });
77 |     });
78 | });
79 | 


--------------------------------------------------------------------------------
/src/test/functional/fieldDeclarations.js:
--------------------------------------------------------------------------------
 1 | /* eslint-disable no-shadow */
 2 | import { Model, ORM } from "../../index";
 3 | import ReferencedModel from "../ReferencedModel";
 4 | import ReferencingModel from "../ReferencingModel";
 5 | import ThroughModel from "../ThroughModel";
 6 | 
 7 | describe("Field declarations", () => {
 8 |     describe("Common mistakes", () => {
 9 |         it("fields need to be instances of the Field class", () => {
10 |             class MyModel extends Model {}
11 |             MyModel.modelName = "MyModel";
12 |             MyModel.fields = {
13 |                 test: "myTestField",
14 |             };
15 | 
16 |             const orm = new ORM();
17 |             orm.register(MyModel);
18 |             expect(() => orm.session()).toThrow(
19 |                 'MyModel.test is of type "string" but must be an instance of Field. Please use the `attr`, `fk`, `oneToOne` and `many` functions to define fields.'
20 |             );
21 |         });
22 |     });
23 | 
24 |     describe("specifying relations using Model classes works", () => {
25 |         const orm = new ORM();
26 |         orm.register(ReferencedModel, ReferencingModel, ThroughModel);
27 |         let session;
28 | 
29 |         beforeEach(() => {
30 |             session = orm.session();
31 |         });
32 | 
33 |         it("correctly handles Model class provided to descriptor function as first argument", () => {
34 |             const { ReferencedModel, ReferencingModel } = session;
35 | 
36 |             ReferencedModel.create({ id: 1 });
37 |             ReferencingModel.create({ id: 1, oneToOneField: 1 });
38 | 
39 |             expect(ReferencedModel.withId(1).reverseOneToOneField.ref).toEqual({
40 |                 id: 1,
41 |                 oneToOneField: 1,
42 |             });
43 |             expect(ReferencingModel.withId(1).oneToOneField.ref).toEqual({
44 |                 id: 1,
45 |             });
46 |         });
47 | 
48 |         it("correctly handles Model class provided to descriptor function within the opts object", () => {
49 |             const { ReferencedModel, ReferencingModel } = session;
50 | 
51 |             ReferencedModel.create({ id: 1 });
52 |             ReferencingModel.create({ id: 1, fkField: 1 });
53 |             ReferencingModel.create({ id: 2, fkField: 1 });
54 | 
55 |             expect(ReferencedModel.withId(1).reverseFkField.count()).toEqual(2);
56 |         });
57 | 
58 |         it("correctly handles Model class provided as an explicit through model", () => {
59 |             const { ReferencedModel, ReferencingModel, ThroughModel } = session;
60 | 
61 |             ReferencedModel.create({ id: 1 });
62 |             ReferencedModel.create({ id: 2 });
63 |             ReferencingModel.create({ id: 1, manyField: [1, 2] });
64 |             ReferencingModel.create({ id: 2, manyField: [1, 2] });
65 | 
66 |             expect(ThroughModel.count()).toEqual(4);
67 |             expect(ReferencedModel.withId(1).reverseManyField.count()).toEqual(
68 |                 2
69 |             );
70 |         });
71 |     });
72 | });
73 | 


--------------------------------------------------------------------------------
/src/test/functional/mutableSessions.js:
--------------------------------------------------------------------------------
 1 | import { createTestSessionWithData } from "../helpers";
 2 | 
 3 | describe("Mutating session", () => {
 4 |     let session;
 5 |     let orm;
 6 |     let state;
 7 | 
 8 |     beforeEach(() => {
 9 |         ({ session, orm, state } = createTestSessionWithData());
10 |     });
11 | 
12 |     it("works", () => {
13 |         const mutating = orm.mutableSession(state);
14 |         const { Book, Cover } = mutating;
15 | 
16 |         const cover = Cover.create({ src: "somecover.png" });
17 |         const coverId = cover.getId();
18 | 
19 |         const book = Book.first();
20 |         const bookRef = book.ref;
21 |         const bookId = book.getId();
22 |         expect(state.Book.itemsById[bookId]).toBe(bookRef);
23 |         const newName = "New Name";
24 | 
25 |         book.name = newName;
26 | 
27 |         expect(book.name).toBe(newName);
28 | 
29 |         const nextState = mutating.state;
30 |         expect(nextState).toBe(state);
31 |         expect(state.Book.itemsById[bookId]).toBe(bookRef);
32 |         expect(bookRef.name).toBe(newName);
33 |         expect(state.Cover.itemsById[coverId].src).toBe("somecover.png");
34 |     });
35 | });
36 | 


--------------------------------------------------------------------------------
/src/test/functional/serialization.js:
--------------------------------------------------------------------------------
 1 | import { createTestORM } from "../helpers";
 2 | 
 3 | let orm;
 4 | let emptyState;
 5 | 
 6 | beforeEach(() => {
 7 |     orm = createTestORM();
 8 |     emptyState = orm.getEmptyState();
 9 | });
10 | 
11 | it("can serialize and deserialize the state", () => {
12 |     const serializedState = JSON.stringify(emptyState);
13 |     const deserializedState = JSON.parse(serializedState);
14 | 
15 |     expect(emptyState).toStrictEqual(deserializedState);
16 | });
17 | 


--------------------------------------------------------------------------------
/src/test/integration/reduxPersist.js:
--------------------------------------------------------------------------------
  1 | import { createStore } from "redux";
  2 | 
  3 | import { persistCombineReducers, persistStore } from "redux-persist";
  4 | import storage from "redux-persist/lib/storage";
  5 | import hardSet from "redux-persist/lib/stateReconciler/hardSet";
  6 | 
  7 | import {
  8 |     ORM,
  9 |     Session,
 10 |     Model as OrmModel,
 11 |     createReducer,
 12 |     createSelector,
 13 | } from "../..";
 14 | 
 15 | import { createTestModels } from "../helpers";
 16 | 
 17 | describe("Redux Persist integration", () => {
 18 |     let orm;
 19 |     let Book;
 20 |     let Cover;
 21 |     let Genre;
 22 |     let Tag;
 23 |     let Author;
 24 |     let Publisher;
 25 |     let Movie;
 26 |     const stateSelector = (state) => state.orm;
 27 |     let emptyState;
 28 | 
 29 |     const CREATE_MOVIE = "CREATE_MOVIE";
 30 |     const UPSERT_MOVIE = "UPSERT_MOVIE";
 31 |     const CREATE_PUBLISHER = "CREATE_PUBLISHER";
 32 | 
 33 |     const createModelReducers = () => {
 34 |         Author.reducer = jest.fn();
 35 |         Book.reducer = jest.fn();
 36 |         Cover.reducer = jest.fn();
 37 |         Genre.reducer = jest.fn();
 38 |         Tag.reducer = jest.fn();
 39 |         Movie.reducer = jest.fn((action, Model, _session) => {
 40 |             switch (action.type) {
 41 |                 case CREATE_MOVIE:
 42 |                     Model.create(action.payload);
 43 |                     break;
 44 |                 case UPSERT_MOVIE:
 45 |                     Model.upsert(action.payload);
 46 |                     break;
 47 |                 default:
 48 |                     break;
 49 |             }
 50 |         });
 51 |         Publisher.reducer = jest.fn((action, Model, _session) => {
 52 |             switch (action.type) {
 53 |                 case CREATE_PUBLISHER:
 54 |                     Model.create(action.payload);
 55 |                     break;
 56 |                 default:
 57 |                     break;
 58 |             }
 59 |         });
 60 |     };
 61 | 
 62 |     const createPersistedReducer = () => {
 63 |         const reducer = createReducer(orm);
 64 |         const persistConfig = {
 65 |             key: "root",
 66 |             storage,
 67 |             whitelist: ["orm"],
 68 |             stateReconciler: hardSet,
 69 |         };
 70 |         return persistCombineReducers(persistConfig, {
 71 |             orm: reducer,
 72 |         });
 73 |     };
 74 | 
 75 |     beforeEach(() => {
 76 |         // eslint-disable-next-line no-undef
 77 |         localStorage.clear(); // prevents persistence between tests
 78 | 
 79 |         ({
 80 |             Book,
 81 |             Cover,
 82 |             Genre,
 83 |             Tag,
 84 |             Author,
 85 |             Movie,
 86 |             Publisher,
 87 |         } = createTestModels());
 88 |         orm = new ORM({ stateSelector });
 89 |         orm.register(Book, Cover, Genre, Tag, Author, Movie, Publisher);
 90 |         emptyState = orm.getEmptyState();
 91 |         createModelReducers();
 92 |     });
 93 | 
 94 |     it("creates correct empty state by default", async () => {
 95 |         await new Promise((resolve) => {
 96 |             const store = createStore(createPersistedReducer());
 97 |             persistStore(store, null, async () => {
 98 |                 expect(store.getState().orm).toStrictEqual(emptyState);
 99 | 
100 |                 const store2 = createStore(createPersistedReducer());
101 |                 const persistor2 = persistStore(store2);
102 |                 persistor2.subscribe(() => {
103 |                     expect(store2.getState().orm).toStrictEqual(emptyState);
104 |                     resolve();
105 |                 });
106 |             });
107 |         });
108 |     });
109 | 
110 |     it("keeps selector results consistent after persistence", async () => {
111 |         await new Promise((resolve) => {
112 |             const store = createStore(createPersistedReducer());
113 |             const persistor = persistStore(store, null, async () => {
114 |                 store.dispatch({
115 |                     type: CREATE_MOVIE,
116 |                     payload: { id: 123 },
117 |                 });
118 | 
119 |                 const movies = createSelector(orm.Movie);
120 |                 expect(movies(store.getState())).toStrictEqual([{ id: 123 }]);
121 | 
122 |                 return persistor.flush().then(() => {
123 |                     expect(movies(store.getState())).toStrictEqual([
124 |                         { id: 123 },
125 |                     ]);
126 | 
127 |                     const store2 = createStore(createPersistedReducer());
128 |                     const persistor2 = persistStore(store2);
129 |                     persistor2.subscribe(() => {
130 |                         expect(movies(store2.getState())).toStrictEqual([
131 |                             { id: 123 },
132 |                         ]);
133 | 
134 |                         resolve();
135 |                     });
136 |                 });
137 |             });
138 |         });
139 |     });
140 | 
141 |     it("keeps state consistent after persistence", async () => {
142 |         await new Promise((resolve) => {
143 |             const store = createStore(createPersistedReducer());
144 |             const persistor = persistStore(store, null, async () => {
145 |                 store.dispatch({
146 |                     type: CREATE_MOVIE,
147 |                     payload: { id: 123 },
148 |                 });
149 |                 return persistor.flush().then(() => {
150 |                     const session = orm.session(store.getState().orm);
151 |                     expect(session.Movie.all().toRefArray()).toStrictEqual([
152 |                         { id: 123 },
153 |                     ]);
154 | 
155 |                     const store2 = createStore(createPersistedReducer());
156 |                     const persistor2 = persistStore(store2);
157 |                     persistor2.subscribe(() => {
158 |                         expect(store2.getState()).toStrictEqual(
159 |                             store.getState()
160 |                         );
161 | 
162 |                         const session2 = orm.session(store2.getState().orm);
163 |                         expect(
164 |                             session2.Movie.all().toRefArray()
165 |                         ).toStrictEqual([{ id: 123 }]);
166 |                         resolve();
167 |                     });
168 |                 });
169 |             });
170 |         });
171 |     });
172 | });
173 | 


--------------------------------------------------------------------------------
/src/test/unit/QuerySet.js:
--------------------------------------------------------------------------------
  1 | import { Model, ORM, QuerySet } from "../..";
  2 | import { createTestModels, createTestSessionWithData } from "../helpers";
  3 | 
  4 | describe("QuerySet tests", () => {
  5 |     let session;
  6 |     let bookQs;
  7 |     let genreQs;
  8 |     let tagQs;
  9 |     beforeEach(() => {
 10 |         ({ session } = createTestSessionWithData());
 11 |         bookQs = session.Book.getQuerySet();
 12 |         genreQs = session.Genre.getQuerySet();
 13 |         tagQs = session.Tag.getQuerySet();
 14 |     });
 15 | 
 16 |     it("count works correctly", () => {
 17 |         expect(bookQs.count()).toBe(3);
 18 |         expect(genreQs.count()).toBe(4);
 19 |         expect(tagQs.count()).toBe(4);
 20 |     });
 21 | 
 22 |     it("exists works correctly", () => {
 23 |         expect(bookQs.exists()).toBe(true);
 24 | 
 25 |         const emptyQs = new QuerySet(session.Book, []).filter(() => false);
 26 | 
 27 |         expect(emptyQs.exists()).toBe(false);
 28 |     });
 29 | 
 30 |     it("at works correctly", () => {
 31 |         expect(bookQs.at(0)).toBeInstanceOf(Model);
 32 |         expect(bookQs.toRefArray()[0]).toBe(session.Book.withId(0).ref);
 33 |     });
 34 | 
 35 |     it("at doesn't return a Model instance if index is out of bounds", () => {
 36 |         expect(bookQs.at(-1)).toBeUndefined();
 37 |         const len = bookQs.count();
 38 |         expect(bookQs.at(len)).toBeUndefined();
 39 |     });
 40 | 
 41 |     it("first works correctly", () => {
 42 |         expect(bookQs.first()).toEqual(bookQs.at(0));
 43 |     });
 44 | 
 45 |     it("last works correctly", () => {
 46 |         const lastIndex = bookQs.count() - 1;
 47 |         expect(bookQs.last()).toEqual(bookQs.at(lastIndex));
 48 |     });
 49 | 
 50 |     it("all works correctly", () => {
 51 |         const all = bookQs.all();
 52 | 
 53 |         // Force evaluation of QuerySets
 54 |         bookQs.toRefArray();
 55 |         all.toRefArray();
 56 | 
 57 |         expect(all).not.toBe(bookQs);
 58 |         expect(all.rows).toHaveLength(bookQs.rows.length);
 59 | 
 60 |         for (let i = 0; i < all.rows.length; i++) {
 61 |             expect(all.rows[i]).toBe(bookQs.rows[i]);
 62 |         }
 63 |     });
 64 | 
 65 |     it("filter works correctly with object argument", () => {
 66 |         const filtered = bookQs.filter({ name: "Clean Code" });
 67 |         expect(filtered.count()).toBe(1);
 68 |         expect(filtered.first().ref).toBe(session.Book.withId(1).ref);
 69 |     });
 70 | 
 71 |     it("filter works correctly with object argument, with model instance value", () => {
 72 |         const filtered = bookQs.filter({
 73 |             author: session.Author.withId(0),
 74 |         });
 75 |         expect(filtered.count()).toBe(1);
 76 |         expect(filtered.first().ref).toBe(session.Book.withId(0).ref);
 77 |     });
 78 | 
 79 |     it("orderBy works correctly with prop argument", () => {
 80 |         const ordered = bookQs.orderBy(["releaseYear"]);
 81 |         const idArr = ordered.toRefArray().map((row) => row.id);
 82 |         expect(idArr).toEqual([1, 2, 0]);
 83 |     });
 84 | 
 85 |     it("orderBy works correctly with function argument", () => {
 86 |         const ordered = bookQs.orderBy([(book) => book.releaseYear]);
 87 |         const idArr = ordered.toRefArray().map((row) => row.id);
 88 |         expect(idArr).toEqual([1, 2, 0]);
 89 |     });
 90 | 
 91 |     it("exclude works correctly with object argument", () => {
 92 |         const excluded = bookQs.exclude({ name: "Clean Code" });
 93 |         expect(excluded.count()).toBe(2);
 94 | 
 95 |         const idArr = excluded.toRefArray().map((row) => row.id);
 96 |         expect(idArr).toEqual([0, 2]);
 97 |     });
 98 | 
 99 |     it("exclude works correctly with object argument, with model instance value", () => {
100 |         const excluded = bookQs.exclude({
101 |             author: session.Author.withId(1),
102 |         });
103 |         expect(excluded.count()).toBe(2);
104 | 
105 |         const idArr = excluded.toRefArray().map((row) => row.id);
106 |         expect(idArr).toEqual([0, 2]);
107 |     });
108 | 
109 |     it("exclude works correctly with function argument", () => {
110 |         const excluded = bookQs.exclude(({ author }) => author === 1);
111 |         expect(excluded.count()).toBe(2);
112 | 
113 |         const idArr = excluded.toRefArray().map((row) => row.id);
114 |         expect(idArr).toEqual([0, 2]);
115 |     });
116 | 
117 |     it("update records a update", () => {
118 |         const mergeObj = { name: "Updated Book Name" };
119 |         bookQs.update(mergeObj);
120 | 
121 |         bookQs
122 |             .toRefArray()
123 |             .forEach((row) => expect(row.name).toBe("Updated Book Name"));
124 |     });
125 | 
126 |     it("delete records a update", () => {
127 |         bookQs.delete();
128 |         expect(bookQs.count()).toBe(0);
129 |     });
130 | 
131 |     it("toString returns evaluated models", () => {
132 |         const firstTwoBooks = bookQs.filter(({ id }) => [0, 1].includes(id));
133 |         expect(firstTwoBooks.toString()).toBe(`QuerySet contents:
134 |     - Book: {id: 0, name: Tommi Kaikkonen - an Autobiography, releaseYear: 2050, author: 0, cover: 0, genres: [0, 1], tags: [Technology, Literary], publisher: 1}
135 |     - Book: {id: 1, name: Clean Code, releaseYear: 2008, author: 1, cover: 1, genres: [2], tags: [Technology], publisher: 0}`);
136 |     });
137 | 
138 |     it("custom methods works", () => {
139 |         const {
140 |             Book,
141 |             Genre,
142 |             Tag,
143 |             Cover,
144 |             Author,
145 |             Publisher,
146 |             Movie,
147 |         } = createTestModels();
148 | 
149 |         const currentYear = 2015;
150 |         class CustomQuerySet extends QuerySet {
151 |             unreleased() {
152 |                 return this.filter((book) => book.releaseYear > currentYear);
153 |             }
154 |         }
155 |         CustomQuerySet.addSharedMethod("unreleased");
156 | 
157 |         Book.querySetClass = CustomQuerySet;
158 | 
159 |         const orm = new ORM();
160 |         orm.register(Book, Genre, Tag, Cover, Author, Publisher, Movie);
161 |         const { session: sess } = createTestSessionWithData(orm);
162 | 
163 |         const customQs = sess.Book.getQuerySet();
164 | 
165 |         expect(customQs).toBeInstanceOf(CustomQuerySet);
166 | 
167 |         const unreleased = customQs.unreleased();
168 |         expect(unreleased.count()).toBe(1);
169 | 
170 |         expect(unreleased.first().ref).toEqual({
171 |             id: 0,
172 |             name: "Tommi Kaikkonen - an Autobiography",
173 |             author: 0,
174 |             cover: 0,
175 |             releaseYear: 2050,
176 |             publisher: 1,
177 |         });
178 |         expect(sess.Book.unreleased().count()).toBe(1);
179 |         expect(sess.Book.filter({ name: "Clean Code" }).count()).toBe(1);
180 |     });
181 | 
182 |     it("should throw a custom error when user try to interact with database without a session", () => {
183 |         const { Book } = createTestModels();
184 |         const errorMessage =
185 |             'Tried to query the Book model\'s table without a session. Create a session using `session = orm.session()` and use `session["Book"]` for querying instead.';
186 |         expect(() => Book.getQuerySet().count()).toThrow(errorMessage);
187 |         expect(() => Book.getQuerySet().exists()).toThrow(errorMessage);
188 |         expect(() => Book.getQuerySet().at(0)).toThrow(errorMessage);
189 |         expect(() => Book.getQuerySet().first()).toThrow(errorMessage);
190 |         expect(() => Book.getQuerySet().last()).toThrow(errorMessage);
191 |     });
192 | });
193 | 


--------------------------------------------------------------------------------
/src/test/unit/Session.js:
--------------------------------------------------------------------------------
  1 | import { ORM } from "../..";
  2 | import { createTestModels, isSubclass } from "../helpers";
  3 | import { CREATE } from "../../constants";
  4 | 
  5 | describe("Session", () => {
  6 |     let orm;
  7 |     let Book;
  8 |     let Cover;
  9 |     let Genre;
 10 |     let Tag;
 11 |     let Author;
 12 |     let Publisher;
 13 |     let emptyState;
 14 |     beforeEach(() => {
 15 |         ({ Book, Cover, Genre, Tag, Author, Publisher } = createTestModels());
 16 |         orm = new ORM();
 17 |         orm.register(Book, Cover, Genre, Tag, Author, Publisher);
 18 |         emptyState = orm.getEmptyState();
 19 |     });
 20 | 
 21 |     it("connects models", () => {
 22 |         expect(Book.session).toBeUndefined();
 23 |         expect(Cover.session).toBeUndefined();
 24 |         expect(Genre.session).toBeUndefined();
 25 |         expect(Tag.session).toBeUndefined();
 26 |         expect(Cover.session).toBeUndefined();
 27 |         expect(Publisher.session).toBeUndefined();
 28 | 
 29 |         const session = orm.session();
 30 | 
 31 |         expect(session.Book.session).toBe(session);
 32 |         expect(session.Cover.session).toBe(session);
 33 |         expect(session.Genre.session).toBe(session);
 34 |         expect(session.Tag.session).toBe(session);
 35 |         expect(session.Cover.session).toBe(session);
 36 |         expect(session.Publisher.session).toBe(session);
 37 |     });
 38 | 
 39 |     it("exposes models as getter properties", () => {
 40 |         const session = orm.session();
 41 |         expect(isSubclass(session.Book, Book)).toBe(true);
 42 |         expect(isSubclass(session.Author, Author)).toBe(true);
 43 |         expect(isSubclass(session.Cover, Cover)).toBe(true);
 44 |         expect(isSubclass(session.Genre, Genre)).toBe(true);
 45 |         expect(isSubclass(session.Tag, Tag)).toBe(true);
 46 |         expect(isSubclass(session.Publisher, Publisher)).toBe(true);
 47 |     });
 48 | 
 49 |     it("marks models when full table scan has been performed", () => {
 50 |         const session = orm.session();
 51 |         expect(session.fullTableScannedModels).toHaveLength(0);
 52 | 
 53 |         session.markFullTableScanned(Book.modelName);
 54 |         expect(session.fullTableScannedModels).toHaveLength(1);
 55 |         expect(session.fullTableScannedModels[0]).toBe("Book");
 56 | 
 57 |         session.markFullTableScanned(Book.modelName);
 58 | 
 59 |         expect(session.fullTableScannedModels[0]).toBe("Book");
 60 |     });
 61 | 
 62 |     it("marks accessed model instances", () => {
 63 |         const session = orm.session();
 64 |         expect(session.accessedModelInstances).toEqual({});
 65 | 
 66 |         session.markAccessed(Book.modelName, [0]);
 67 | 
 68 |         expect(session.accessedModelInstances).toEqual({
 69 |             Book: {
 70 |                 0: true,
 71 |             },
 72 |         });
 73 | 
 74 |         session.markAccessed(Book.modelName, [1]);
 75 |         expect(session.accessedModelInstances).toEqual({
 76 |             Book: {
 77 |                 0: true,
 78 |                 1: true,
 79 |             },
 80 |         });
 81 |     });
 82 | 
 83 |     it("throws when failing to apply updates", () => {
 84 |         const session = orm.session();
 85 |         session.db = {
 86 |             update() {
 87 |                 return {
 88 |                     payload: 123,
 89 |                     status: "failed",
 90 |                     state: {},
 91 |                 };
 92 |             },
 93 |         };
 94 |         expect(() => session.applyUpdate({})).toThrow(
 95 |             "Applying update failed with status failed. Payload: 123"
 96 |         );
 97 |     });
 98 | 
 99 |     describe("gets the next state", () => {
100 |         it("without any updates, the same state is returned", () => {
101 |             const session = orm.session();
102 |             expect(session.state).toEqual(emptyState);
103 |         });
104 | 
105 |         it("with updates, a new state is returned", () => {
106 |             const session = orm.session(emptyState);
107 | 
108 |             session.applyUpdate({
109 |                 table: Author.modelName,
110 |                 action: CREATE,
111 |                 payload: {
112 |                     id: 0,
113 |                     name: "Caesar",
114 |                 },
115 |             });
116 | 
117 |             const nextState = session.state;
118 | 
119 |             expect(nextState).not.toBe(emptyState);
120 | 
121 |             expect(nextState[Author.modelName]).not.toBe(
122 |                 emptyState[Author.modelName]
123 |             );
124 | 
125 |             // All other model states should stay equal.
126 |             expect(nextState[Book.modelName]).toBe(emptyState[Book.modelName]);
127 |             expect(nextState[Cover.modelName]).toBe(
128 |                 emptyState[Cover.modelName]
129 |             );
130 |             expect(nextState[Genre.modelName]).toBe(
131 |                 emptyState[Genre.modelName]
132 |             );
133 |             expect(nextState[Tag.modelName]).toBe(emptyState[Tag.modelName]);
134 |             expect(nextState[Publisher.modelName]).toBe(
135 |                 emptyState[Publisher.modelName]
136 |             );
137 |         });
138 |     });
139 | });
140 | 


--------------------------------------------------------------------------------
/src/test/unit/fields.js:
--------------------------------------------------------------------------------
 1 | import ManyToMany from "../../fields/ManyToMany";
 2 | 
 3 | describe("Fields", () => {
 4 |     describe("ManyToMany", () => {
 5 |         describe("getDefault", () => {
 6 |             it("returns empty array", () => {
 7 |                 const m2m = new ManyToMany();
 8 |                 expect(m2m.getDefault()).toEqual([]);
 9 |             });
10 |         });
11 |     });
12 | });
13 | 


--------------------------------------------------------------------------------
/standalone-docs/FAQ.md:
--------------------------------------------------------------------------------
 1 | ---
 2 | id: faq
 3 | title: Index
 4 | sidebar_label: FAQ Index
 5 | hide_title: true
 6 | ---
 7 | 
 8 | # Redux-ORM FAQ
 9 | 
10 | ## Table of Contents
11 | 
12 | - **[Performance](faq/Performance.md)**
13 | 


--------------------------------------------------------------------------------
/standalone-docs/LearningResources.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: learning-resources
3 | title: Learning Resources
4 | sidebar_label: Learning Resources
5 | hide_title: true
6 | ---
7 | 
8 | # Learning Resources
9 | 


--------------------------------------------------------------------------------
/standalone-docs/README.md:
--------------------------------------------------------------------------------
 1 | 
 2 | # Table of Contents
 3 | 
 4 | - [Read Me](../README.md)
 5 | - [Introduction](introduction/README.md)
 6 |   - [Core Concepts](introduction/CoreConcepts.md)
 7 |   - [Related Projects](introduction/RelatedProjects.md)
 8 | - [Basics](basics/README.md)
 9 | - [Advanced](advanced/README.md)
10 | - [Recipes](recipes/README.md)
11 |   - [Writing Tests](recipes/WritingTests.md)
12 | - [FAQ](FAQ.md)
13 |   - [Performance](faq/Performance.md)
14 | - [Troubleshooting](Troubleshooting.md)
15 | - [Learning Resources](LearningResources.md)
16 | - [API Reference](api/README.md)
17 |   - [ORM](api/ORM.md)
18 |   - [Model](api/Model.md)
19 |   - [QuerySet](api/QuerySet.md)
20 |   - [Session](api/Session.md)
21 |   - [Descriptors](api/descriptors.md)
22 |     - [attr](api/attr.md)
23 |     - [oneToOne](api/oneToOne.md)
24 |     - [fk](api/fk.md)
25 |     - [many](api/many.md)
26 |   - [Redux Integration](api/reduxintegration.md)
27 |     - [createReducer](api/createReducer.md)
28 |     - [createSelector](api/createSelector.md)
29 | - [Change Log](../CHANGELOG.md)
30 | 


--------------------------------------------------------------------------------
/standalone-docs/Troubleshooting.md:
--------------------------------------------------------------------------------
 1 | ---
 2 | id: troubleshooting
 3 | title: Troubleshooting
 4 | sidebar_label: Troubleshooting
 5 | hide_title: true
 6 | ---
 7 | 
 8 | # Troubleshooting
 9 | 
10 | This is a place to share common problems and solutions to them.
11 | 


--------------------------------------------------------------------------------
/standalone-docs/advanced/ComplexSelectors.md:
--------------------------------------------------------------------------------
 1 | ---
 2 | id: complex-selectors
 3 | title: Complex Selectors
 4 | sidebar_label: Complex Selectors
 5 | hide_title: true
 6 | ---
 7 | 
 8 | # Complex Selectors
 9 | 
10 | Please read up on selectors in the official [reselect](https://github.com/reduxjs/reselect) repository before programming complex calculations using selectors. You should understand the basics of how they work first.
11 | 
12 | ## Custom result functions
13 | 
14 | As with regular selectors, Redux-ORM selectors can combine the results of multiple selectors.
15 | 
16 | So the last argument you pass to `createSelector` can be a custom result function that will receive the result of previous selectors as arguments.
17 | 
18 | ```js
19 | const publisherDescription = createSelector(
20 |     orm.Publisher.name,
21 |     orm.Publisher.movies,
22 |     (publisher, movies) => `${publisher} has published ${movies.length} movies.`
23 | );
24 | publisherDescription(state, 1) // Warner Bros. has published 10000 movies.
25 | ```
26 | 
27 | ### `SelectorSpec`
28 | 
29 | In this example, `orm.Publisher.name` and `orm.Publisher.movies` are interpreted as input selectors, although in fact they are so-called `SelectorSpec` objects created by Redux-ORM.
30 | 
31 | > Don't forget that both of them will receive the `state` and `1` as arguments as well.
32 | 
33 | `createSelector` will automatically try to turn anything beginning with `orm` into a selector.
34 | 
35 | ### `ORM` instances become sessions
36 | 
37 | Passing your `ORM` instance at any position of `createSelector` will create a session at the corresponding position in the result function's argument list:
38 | ```js
39 | const someSelector = createSelector(
40 |     [ a,  b,     orm,  d], // a, b and d being selectors
41 |     (_a, _b, session, _d) => session.User.count()
42 | );
43 | ```
44 | 
45 | This is important because if you want to access Model classes within the result function, you need to get them using a session (like `session.User` above).
46 | 
47 | > You always need to pass at least one selector beginning with `orm` to `createSelector()`.
48 | 
49 | ## The `QuerySet` class
50 | 
51 | Where does the `User.count()` call above come from, you're asking?
52 | 
53 | There are many functions like `count()` defined on the [`QuerySet`](../api/QuerySet) class. Most of them are copied over to Model classes when a session is created.
54 | 
55 | Instances of `QuerySet` are automatically created when you access a relationship that potentially returns multiple model instances, like `book.authors`. To get the corresponding database objects, you would write `book.authors.toRefArray()`. In some cases you need to wrap them in Model instances, e.g. in order to access other related models. To do that, use `book.authors.toModelArray()` instead.
56 | 
57 | ## Complex selector example
58 | 
59 | Let's apply everything we've learned so far into a single useful computation.
60 | ```js
61 | // this is just a helper that gets the average value in an array
62 | const avg = arr => arr.reduce((a, b) => a + b, 0) / arr.length;
63 | 
64 | const publisherAverageRating = createSelector(
65 |     orm.Publisher.movies.map(orm.Movie.rating),
66 |     ratings => ratings && (ratings.length ? avg(ratings) : 'no movies')
67 | );
68 | publisherAverageRating(state, 1) // 3.5
69 | ```
70 | 
71 | ## Argument types for custom result functions
72 | 
73 | Different values of the primary key argument need to be handled if you want to support them ([#282](https://github.com/redux-orm/redux-orm/issues/282)):
74 | ```js
75 | const publisherAverageRating = createSelector(
76 |     orm.Publisher.movies.map(orm.Movie.rating),
77 |     (state, idArg) => idArg,
78 |     (ratingsArg, idArg) => {
79 |         if (typeof idArg === 'undefined' || Array.isArray(idArg)) {
80 |             // only state was passed, or an array of IDs
81 |             return ratingsArg.map(
82 |                 ratings => ratings && ratings.length ? avg(ratings) : 'no movies'
83 |             );
84 |         }
85 |         // single publisher ID was passed
86 |         return ratingsArg && (ratingsArg.length ? avg(ratingsArg) : 'no movies');
87 |     }
88 | );
89 | ```
90 | 
91 | ## Migrating from versions before 0.14.0
92 | 
93 | Remove the second argument that you previously needed to pass to `createSelector` and pass it to the `ORM` constructor instead (as described [in the previous section](../basics/Selectors.md#prerequisites)).
94 | ```diff
95 | -const someSelector = createSelector(orm, state => state.orm, …);
96 | +const someSelector = createSelector(orm, …);
97 | ```
98 | 


--------------------------------------------------------------------------------
/standalone-docs/advanced/CustomReducer.md:
--------------------------------------------------------------------------------
 1 | ---
 2 | id: custom-reducer
 3 | title: Custom Reducer
 4 | sidebar_label: Custom Reducer
 5 | hide_title: true
 6 | ---
 7 | 
 8 | # Custom Reducer
 9 | 
10 | ## Updating the store state
11 | 
12 | If you wish to integrate Redux-ORM with Redux by yourself, you could define your own reducer that instantiates a session from the database state held in the Redux state slice.
13 | 
14 | Then when you've applied all of your updates, you return the next state from the session.
15 | 
16 | ```javascript
17 | function ormReducer(dbState, action) {
18 |     const session = orm.session(dbState);
19 | 
20 |     // Session-specific Models are available
21 |     // as properties on the Session instance.
22 |     const { Book } = session;
23 | 
24 |     switch (action.type) {
25 |     case 'CREATE_BOOK':
26 |         Book.create(action.payload);
27 |         break;
28 |     case 'UPDATE_BOOK':
29 |         Book.withId(action.payload.id).update(action.payload);
30 |         break;
31 |     case 'REMOVE_BOOK':
32 |         Book.withId(action.payload.id).delete();
33 |         break;
34 |     case 'ADD_AUTHOR_TO_BOOK':
35 |         Book.withId(action.payload.bookId).authors.add(action.payload.author);
36 |         break;
37 |     case 'REMOVE_AUTHOR_FROM_BOOK':
38 |         Book.withId(action.payload.bookId).authors.remove(action.payload.authorId);
39 |         break;
40 |     case 'ASSIGN_PUBLISHER':
41 |         Book.withId(action.payload.bookId).publisherId = action.payload.publisherId;
42 |         break;
43 |     }
44 | 
45 |     // The state property of session always points to the current database.
46 |     // Updates don't mutate the original state, so if you changed something
47 |     // this reference will differ from `dbState` that was an argument to this reducer.
48 |     return session.state;
49 | }
50 | ```
51 | 
52 | ## `createStore`
53 | 
54 | Of course then you need to pass this reducer to Redux' `createStore` function instead.
55 | 
56 | ```js
57 | import { createStore, combineReducers } from "redux";
58 | import { createReducer } from "redux-orm";
59 | 
60 | const rootReducer = combineReducers({
61 |     orm: ormReducer,
62 |     // … potentially other reducers
63 | });
64 | const store = createStore(rootReducer);
65 | ```
66 | 


--------------------------------------------------------------------------------
/standalone-docs/api/ORM.md:
--------------------------------------------------------------------------------
  1 | ---
  2 | id: ORM
  3 | title: ORM
  4 | sidebar_label: ORM
  5 | hide_title: true
  6 | ---
  7 | 
  8 | <a name="ORM"></a>
  9 | 
 10 | #  ORM
 11 | 
 12 | <p>ORM - the Object Relational Mapper.</p>
 13 | <p>Use instances of this class to:</p>
 14 | <ul>
 15 | <li>Register your [Model](Model) classes using [register](#ORM+register)</li>
 16 | <li>Get the empty state for the underlying database with [getEmptyState](#ORM+getEmptyState)</li>
 17 | <li>Start an immutable database session with [session](#ORM+session)</li>
 18 | <li>Start a mutating database session with [mutableSession](#ORM+mutableSession)</li>
 19 | </ul>
 20 | <p>Internally, this class handles generating a schema specification from models<br>
 21 | to the database.</p>
 22 | 
 23 | **Kind**: global class  
 24 | 
 25 | * [ORM](#.ORM)
 26 |     * [`new ORM([opts])`](#.ORM)
 27 |     * [`register(...models)`](#orm+register) ⇒ undefined
 28 |     * [`get(modelName)`](#orm+get) ⇒ Model
 29 |     * [`getEmptyState()`](#orm+getEmptyState) ⇒ Object
 30 |     * [`session(state)`](#orm+session) ⇒ Session
 31 |     * [`mutableSession(state)`](#orm+mutableSession) ⇒ Session
 32 |     * ~~[`withMutations()`](#orm+withMutations)~~
 33 |     * ~~[`from()`](#orm+from)~~
 34 |     * ~~[`getDefaultState()`](#orm+getDefaultState)~~
 35 |     * ~~[`define()`](#orm+define)~~
 36 | 
 37 | 
 38 | <a name="ORM"></a>
 39 | 
 40 | ## `new  ORM([opts])`
 41 | 
 42 | <p>Creates a new ORM instance.</p>
 43 | 
 44 | 
 45 | | Param | Type | Description |
 46 | | --- | --- | --- |
 47 | | [opts] | Object |  |
 48 | | [opts.stateSelector] | function | <p>function that given a Redux state tree<br> will return the ORM state's subtree,<br> e.g. <code>state =&gt; state.orm</code><br> (necessary if you want to use selectors)</p> |
 49 | | [opts.createDatabase] | function | <p>function that creates a database</p> |
 50 | 
 51 | 
 52 | <a name="orm+register"></a>
 53 | 
 54 | ## ` register(...models)`⇒ undefined 
 55 | 
 56 | <p>Registers a [Model](Model) class to the ORM.</p>
 57 | <p>If the model has declared any ManyToMany fields, their<br>
 58 | through models will be generated and registered with<br>
 59 | this call, unless a custom through model has been specified.</p>
 60 | 
 61 | **Kind**: instance method of [ORM](#.ORM)  
 62 | 
 63 | | Param | Type | Description |
 64 | | --- | --- | --- |
 65 | | ...models | [Model](#.Model) | <p>a [Model](Model) class to register</p> |
 66 | 
 67 | 
 68 | <a name="orm+get"></a>
 69 | 
 70 | ## ` get(modelName)`⇒ Model 
 71 | 
 72 | <p>Gets a [Model](Model) class by its name from the registry.</p>
 73 | 
 74 | **Kind**: instance method of [ORM](#.ORM)  
 75 | **Returns**: [Model](#.Model) - <p>the [Model](Model) class, if found</p>  
 76 | **Throws**:
 77 | 
 78 | - <p>If [Model](Model) class is not found.</p>
 79 | 
 80 | 
 81 | | Param | Type | Description |
 82 | | --- | --- | --- |
 83 | | modelName | string | <p>the name of the [Model](Model) class to get</p> |
 84 | 
 85 | 
 86 | <a name="orm+getEmptyState"></a>
 87 | 
 88 | ## ` getEmptyState()`⇒ Object 
 89 | 
 90 | <p>Returns the empty database state.</p>
 91 | 
 92 | **Kind**: instance method of [ORM](#.ORM)  
 93 | **Returns**: Object - <p>the empty state</p>  
 94 | 
 95 | <a name="orm+session"></a>
 96 | 
 97 | ## ` session(state)`⇒ Session 
 98 | 
 99 | <p>Begins an immutable database session.</p>
100 | 
101 | **Kind**: instance method of [ORM](#.ORM)  
102 | **Returns**: Session - <p>a new [Session](Session) instance</p>  
103 | 
104 | | Param | Type | Description |
105 | | --- | --- | --- |
106 | | state | Object | <p>the state the database manages</p> |
107 | 
108 | 
109 | <a name="orm+mutableSession"></a>
110 | 
111 | ## ` mutableSession(state)`⇒ Session 
112 | 
113 | <p>Begins a mutable database session.</p>
114 | 
115 | **Kind**: instance method of [ORM](#.ORM)  
116 | **Returns**: Session - <p>a new [Session](Session) instance</p>  
117 | 
118 | | Param | Type | Description |
119 | | --- | --- | --- |
120 | | state | Object | <p>the state the database manages</p> |
121 | 
122 | 
123 | <a name="orm+withMutations"></a>
124 | 
125 | ## ~~` withMutations()`~~
126 | 
127 | ***Deprecated***
128 | 
129 | **Kind**: instance method of [ORM](#.ORM)  
130 | 
131 | <a name="orm+from"></a>
132 | 
133 | ## ~~` from()`~~
134 | 
135 | ***Deprecated***
136 | 
137 | **Kind**: instance method of [ORM](#.ORM)  
138 | 
139 | <a name="orm+getDefaultState"></a>
140 | 
141 | ## ~~` getDefaultState()`~~
142 | 
143 | ***Deprecated***
144 | 
145 | **Kind**: instance method of [ORM](#.ORM)  
146 | 
147 | <a name="orm+define"></a>
148 | 
149 | ## ~~` define()`~~
150 | 
151 | ***Deprecated***
152 | 
153 | **Kind**: instance method of [ORM](#.ORM)  
154 | 
155 | 


--------------------------------------------------------------------------------
/standalone-docs/api/README.md:
--------------------------------------------------------------------------------
 1 | ---
 2 | id: api-reference
 3 | title: API Reference
 4 | sidebar_label: API Reference
 5 | hide_title: true
 6 | ---
 7 | 
 8 | # API Reference
 9 | 
10 | This section documents the complete **Redux-ORM** API.
11 | 
12 | ### Classes
13 | 
14 | - [ORM](ORM)
15 | - [Model](Model)
16 | - [QuerySet](QuerySet)
17 | - [Session](Session)
18 | 
19 | ### Descriptors    
20 | 
21 | - [attr](module_fields#attr)
22 | - [oneToOne](module_fields#oneToOne)
23 | - [fk](module_fields#fk)
24 | - [many](module_fields#many)
25 | 
26 | ### Redux Integration    
27 | 
28 | - [createReducer](module_redux#createReducer)
29 | - [createSelector](module_redux#createSelector)
30 | 


--------------------------------------------------------------------------------
/standalone-docs/api/Session.md:
--------------------------------------------------------------------------------
 1 | ---
 2 | id: Session
 3 | title: Session
 4 | sidebar_label: Session
 5 | hide_title: true
 6 | ---
 7 | 
 8 | <a name="Session"></a>
 9 | 
10 | #  Session
11 | 
12 | **Kind**: global class  
13 | 
14 | * [Session](#.Session)
15 |     * [`new Session(db, state, [withMutations], [batchToken])`](#.Session)
16 |     * ~~[`getNextState()`](#session+getNextState)~~
17 |     * ~~[`reduce()`](#session+reduce)~~
18 | 
19 | 
20 | <a name="Session"></a>
21 | 
22 | ## `new  Session(db, state, [withMutations], [batchToken])`
23 | 
24 | <p>Creates a new Session.</p>
25 | 
26 | 
27 | | Param | Type | Description |
28 | | --- | --- | --- |
29 | | db | Database | <p>a [Database](Database) instance</p> |
30 | | state | Object | <p>the database state</p> |
31 | | [withMutations] | Boolean | <p>whether the session should mutate data</p> |
32 | | [batchToken] | Object | <p>used by the backend to identify objects that can be<br> mutated.</p> |
33 | 
34 | 
35 | <a name="session+getNextState"></a>
36 | 
37 | ## ~~` getNextState()`~~
38 | 
39 | ***Deprecated***
40 | 
41 | **Kind**: instance method of [Session](#.Session)  
42 | 
43 | <a name="session+reduce"></a>
44 | 
45 | ## ~~` reduce()`~~
46 | 
47 | ***Deprecated***
48 | 
49 | **Kind**: instance method of [Session](#.Session)  
50 | 
51 | 


--------------------------------------------------------------------------------
/standalone-docs/api/redux.md:
--------------------------------------------------------------------------------
  1 | ---
  2 | id: module_redux
  3 | title: Redux
  4 | sidebar_label: Redux
  5 | hide_title: true
  6 | ---
  7 | 
  8 | <a name="module:redux"></a>
  9 | 
 10 | #  redux
 11 | 
 12 | <p>Provides functions for integration with Redux.</p>
 13 | 
 14 | 
 15 | <a name="module:redux~level"></a>
 16 | 
 17 | ## ` level`
 18 | 
 19 | <p>Drill down into selector map by cachePath.</p>
 20 | <p>The selector itself is stored under a special SELECTOR_KEY<br>
 21 | so that we can store selectors below it as well.</p>
 22 | 
 23 | **Kind**: inner property of redux  
 24 | 
 25 | <a name="defaultUpdater"></a>
 26 | 
 27 | # ` defaultUpdater()`⇒ undefined 
 28 | 
 29 | <p>Calls all models' reducers if they exist.</p>
 30 | 
 31 | **Kind**: global function  
 32 | 
 33 | <a name="createReducer"></a>
 34 | 
 35 | # ` createReducer(orm, [updater])`⇒ function 
 36 | 
 37 | <p>Call the returned function to pass actions to Redux-ORM.</p>
 38 | 
 39 | **Kind**: global function  
 40 | **Returns**: function - <p>reducer that will update the ORM state.</p>  
 41 | 
 42 | | Param | Type | Description |
 43 | | --- | --- | --- |
 44 | | orm | [ORM](#.ORM) | <p>the ORM instance.</p> |
 45 | | [updater] | function | <p>the function updating the ORM state based on the given action.</p> |
 46 | 
 47 | 
 48 | <a name="createSelector"></a>
 49 | 
 50 | # ` createSelector(...args)`⇒ function 
 51 | 
 52 | <p>Returns a memoized selector based on passed arguments.<br>
 53 | This is similar to <code>reselect</code>'s <code>createSelector</code>,<br>
 54 | except you can also pass a single function to be memoized.</p>
 55 | <p>If you pass multiple functions, the format will be the<br>
 56 | same as in <code>reselect</code>. The last argument is the selector<br>
 57 | function and the previous are input selectors.</p>
 58 | <p>When you use this method to create a selector, the returned selector<br>
 59 | expects the whole <code>redux-orm</code> state branch as input. In the selector<br>
 60 | function that you pass as the last argument, any of the arguments<br>
 61 | you pass first will be considered selectors and mapped<br>
 62 | to their outputs, like in <code>reselect</code>.</p>
 63 | <p>Here are some example selectors:</p>
 64 | <pre class="prettyprint source lang-javascript"><code>// orm is an instance of ORM
 65 | // reduxState is the state of a Redux store
 66 | const books = createSelector(orm.Book);
 67 | books(reduxState) // array of book refs
 68 | 
 69 | const bookAuthors = createSelector(orm.Book.authors);
 70 | bookAuthors(reduxState) // two-dimensional array of author refs for each book
 71 | </code></pre>
 72 | <p>Selectors can easily be applied to related models:</p>
 73 | <pre class="prettyprint source lang-javascript"><code>const bookAuthorNames = createSelector(
 74 |     orm.Book.authors.map(orm.Author.name),
 75 | );
 76 | bookAuthorNames(reduxState, 8) // names of all authors of book with ID 8
 77 | bookAuthorNames(reduxState, [8, 9]) // 2D array of names of all authors of books with IDs 8 and 9
 78 | </code></pre>
 79 | <p>Also note that <code>orm.Author.name</code> did not need to be wrapped in another <code>createSelector</code> call,<br>
 80 | although that would be possible.</p>
 81 | <p>For more complex calculations you can access<br>
 82 | entire session objects by passing an ORM instance.</p>
 83 | <pre class="prettyprint source lang-javascript"><code>const freshBananasCost = createSelector(
 84 |     orm,
 85 |     session => {
 86 |        const banana = session.Product.get({
 87 |            name: &quot;Banana&quot;,
 88 |        });
 89 |        // amount of fresh bananas in shopping cart
 90 |        const amount = session.ShoppingCart.filter({
 91 |            product_id: banana.id,
 92 |            is_fresh: true,
 93 |        }).count();
 94 |        return `USD ${amount * banana.price}`;
 95 |     }
 96 | );
 97 | </code></pre>
 98 | <p>redux-orm uses a special memoization function to avoid recomputations.</p>
 99 | <p>Everytime a selector runs, this function records which instances<br>
100 | of your <code>Model</code>s were accessed.<br><br>
101 | On subsequent runs, the selector first checks if the previously<br>
102 | accessed instances or <code>args</code> have changed in any way:</p>
103 | <ul>
104 |     <li>If yes, the selector calls the function you passed to it.</li>
105 |     <li>If not, it just returns the previous result
106 |         (unless you call it for the first time).</li>
107 | </ul>
108 | <p>This way you can use pure rendering in your React components<br>
109 | for performance gains.</p>
110 | 
111 | **Kind**: global function  
112 | **Returns**: function - <p>memoized selector</p>  
113 | 
114 | | Param | Type | Description |
115 | | --- | --- | --- |
116 | | ...args | function | <p>zero or more input selectors<br> and the selector function.</p> |
117 | 
118 | 
119 | 


--------------------------------------------------------------------------------
/standalone-docs/assets/plantuml-theme.iuml:
--------------------------------------------------------------------------------
 1 | hide circle
 2 | hide empty members
 3 | 
 4 | !$headingFontSize = 18
 5 | !$fontSize = 16
 6 | !$accent = 'fff'
 7 | !$accentDark = 'fff'
 8 | !$primary = 'bc001c'
 9 | !$secondary = 'fff'
10 | !$arrowColor = 'fff'
11 | !$arrowFontColor = 'fff'
12 | !$borderColor = 'bc001c'
13 | !$boxBg = 'fefefe'
14 | 
15 | !function $font_style()
16 |   fontColor $primary
17 |   fontName $fontName
18 |   fontSize $fontSize
19 |   stereotypeFontColor $secondary
20 |   stereotypeFontSize $fontSize
21 | !endfunction
22 | 
23 | !function $basic_style()
24 |   backgroundColor $boxBg
25 |   borderColor $borderColor
26 | !endfunction
27 | 
28 | !function $accent_style()
29 |   backgroundColor $accent
30 |   borderColor $accentDark
31 | !endfunction
32 | 
33 | !function $arrow_style()
34 |   arrowColor $arrowColor
35 |   arrowFontName $fontName
36 |   arrowBorderThickness 33
37 |   arrowFontColor $arrowFontColor
38 |   arrowFontSize $fontSize
39 | !endfunction
40 | 
41 | skinparam class {
42 |   backgroundColor red
43 |   $basic_style()
44 |   $font_style()
45 |   $arrow_style()
46 |   attributeFontColor $primary
47 |   attributeFontSize $fontSize
48 |   attributeIconSize 14
49 |   attributeIconColor red
50 |   borderThickness 3
51 | }
52 | 
53 | skinparam entity {
54 |   backgroundColor red
55 |   $basic_style()
56 |   $font_style()
57 |   $arrow_style()
58 |   attributeFontColor $primary
59 |   attributeFontSize $fontSize
60 |   attributeIconSize 22
61 |   attributeIconColor red
62 |   borderThickness 3
63 | }
64 | 
65 | skinparam interface {
66 |   $accent_style()
67 |   $font_style()
68 | }
69 | 
70 | skinparam component {
71 |   $basic_style()
72 |   $font_style()
73 | }
74 | 
75 | skinparam node {
76 |   $basic_style()
77 |   $font_style()
78 | }
79 | 
80 | skinparam database {
81 |   $basic_style()
82 |   $font_style()
83 | }
84 | skinparam note {
85 |   $accent_style()
86 |   $font_style()
87 | }
88 | 
89 | skinparam rectangle {
90 |   $basic_style()
91 |   $font_style()
92 | }
93 | 


--------------------------------------------------------------------------------
/standalone-docs/basics/Fields.md:
--------------------------------------------------------------------------------
 1 | ---
 2 | id: fields
 3 | title: Relational Fields
 4 | sidebar_label: Relational Fields
 5 | hide_title: true
 6 | ---
 7 | 
 8 | # Relational Fields
 9 | 
10 | Our goal is to have each model instance be stored exactly once, and we want to retrieve related model instances easily.
11 | 
12 | ## One-to-many
13 | For this purpose we can define our model to have fields that reference other models. For instance, let's assume that every book is written by a single author that we want to represent using a **foreign key**.
14 | 
15 | ```js
16 | import { fk } from 'redux-orm';
17 | Book.fields = {
18 |     authorId: fk({
19 |         to: 'Author',
20 |         as: 'author',
21 |         relatedName: 'books',
22 |     }),
23 | };
24 | ```
25 | Now any `book` returned by Redux-ORM will have a `book.author` accessor that can be used to retrieve its `Author` model instance. In turn, each `author` will receive an `author.books` accessor.
26 | 
27 | All we need to do is pass an `authorId` when creating each book. APIs commonly return these keys.
28 | 
29 | ## Many-to-many
30 | 
31 | But if we're realistic, a book could have several authors, and every author can write multiple books. Here's the way to specify such a many-to-many relationship.
32 | 
33 | ```js
34 | import { many } from 'redux-orm';
35 | Book.fields = {
36 |     author_ids: many({
37 |         to: 'Author',
38 |         as: 'authors',
39 |         relatedName: 'books',
40 |     }),
41 | };
42 | ```
43 | `author_ids` should be an array of author **primary keys** (by default looked up at `author.id`).
44 | 
45 | > Never use `many` to specify a backwards foreign key field, only for many-to-many relationships.
46 | 
47 | ## One-to-one
48 | 
49 | Each `book` has **exactly one** cover. This can be expressed similarly to one-to-many relationships.
50 | 
51 | ```js
52 | import { oneToOne } from 'redux-orm';
53 | Book.fields = {
54 |     coverId: oneToOne({
55 |         to: 'Cover',
56 |         as: 'cover',
57 |         relatedName: 'book',
58 |     }),
59 | };
60 | ```
61 | 
62 | ## Shorthand definitions
63 | 
64 | At times we don't need to keep access to the original `authorId` of a book. Then it's a little easier.
65 | ```js
66 | Book.fields = {
67 |     authorId: fk('Author', 'books'),
68 | };
69 | ```
70 | The second argument can be omitted. For `fk`, you can then access the reverse relation through `author.bookSet`, where the related name is `${modelName}Set`. Same goes for `many`. For `oneToOne`, the reverse relation can be accessed by just the model name the field was declared on: `author.book`.
71 | 
72 | ## Attributes
73 | 
74 | It is recommended but not required to list all the possible other fields a model could have.
75 | ```js
76 | import { attr, many, oneToOne } from 'redux-orm';
77 | Book.fields = {
78 |     id: attr(),
79 |     title: attr(),
80 |     author_ids: many({
81 |         to: 'Author',
82 |         as: 'authors',
83 |         relatedName: 'books',
84 |     }),
85 |     cover: oneToOne('Cover'),
86 | };
87 | ```
88 | ### idAttribute
89 | Sometimes backends store use natural keys as primary keys or call them differently. If that's the case we need to change the model's ID attribute.
90 | ```js
91 | Book.options = {
92 |   idAttribute: 'title'; // Default: 'id' 
93 | }
94 | ```
95 | 


--------------------------------------------------------------------------------
/standalone-docs/basics/README.md:
--------------------------------------------------------------------------------
 1 | ---
 2 | id: quick-start
 3 | title: Quick Start
 4 | sidebar_label: Quick Start
 5 | hide_title: true
 6 | ---
 7 | 
 8 | # Quick Start
 9 | 
10 | This page shows you the bare minimum to be able to use Redux-ORM in your app.
11 | 
12 | ## Defining a schema
13 | 
14 | You probably came here because your application's state is quite complex. While Redux by itself helps a lot, it lacks the capabilities for specifying which relationships there are within your data.
15 | 
16 | This is where Redux-ORM comes in. It lets us define **`Model`** classes …
17 | 
18 | ```js
19 | import { Model, ORM } from "redux-orm";
20 | 
21 | class Book extends Model {}
22 | Book.modelName = 'Book';
23 | 
24 | class Author extends Model {}
25 | Author.modelName = 'Author';
26 | ```
27 | … which we then combine into a schema that we call **`ORM`**:
28 | ```js
29 | const orm = new ORM;
30 | orm.register(Book, Author);
31 | ```
32 | 
33 | ## Connecting a schema to Redux
34 | 
35 | We need to make the Redux store aware of our models so that it can save them.
36 | ```js
37 | import { createStore, combineReducers } from "redux";
38 | import { createReducer } from "redux-orm";
39 | 
40 | const rootReducer = combineReducers({
41 |     orm: createReducer(orm), // This will be the Redux-ORM state.
42 |     // … potentially other reducers
43 | });
44 | const store = createStore(rootReducer);
45 | ```
46 | We have now created a new branch within the Redux state tree at the key `orm`.
47 | 
48 | ## Writing to the store
49 | 
50 | To actually create some model instances, we need to start a **`Session`** and access the models below it.
51 | ```js
52 | const session = orm.session();
53 | 
54 | session.Book.create({
55 |     id: 1,
56 |     title: 'Don Quixote',
57 | });
58 | ```
59 | 
60 | This is necessary so that you can re-use Model classes across different schemas.
61 | 
62 | ## Reading from the store
63 | 
64 | Reading data also works using sessions, at least under the hood.
65 | ```js
66 | const book = session.Book.first();
67 | console.log(book.title); // 'Don Quixote'
68 | ```
69 | Most of the times you won't need to do this, though. More on that later.
70 | 


--------------------------------------------------------------------------------
/standalone-docs/basics/React.md:
--------------------------------------------------------------------------------
 1 | ---
 2 | id: react
 3 | title: Usage with React
 4 | sidebar_label: Usage with React
 5 | hide_title: true
 6 | ---
 7 | 
 8 | # Usage with React
 9 | 
10 | Here is an example of how Redux-ORM can help when using React.
11 | 
12 | Let's imagine we have a file at `src/orm.js` where we have set up an `ORM` with a `Book` and an `Author` model. Each book is written by a single author, indicated by a foreign key. Our task is to implement a small author profile that shows an author's name and how many books they have written.
13 | 
14 | We first define a selector to retrieve one or more authors and another to retrieve their books.
15 | 
16 | **`src/selectors.js`**:
17 | ```jsx
18 | import { createSelector } from "redux-orm";
19 | import orm from "./orm";
20 | 
21 | export const authors = createSelector(orm.Author);
22 | export const authorBooks = createSelector(orm.Author.books);
23 | ```
24 | 
25 | Then, we proceed to implement the author profile component.
26 | We make use of the `useSelector` hook that allows us to easily retrieve
27 | the stored author by their ID and the books they have written.
28 | 
29 | **`src/components/AuthorProfile.js`**:
30 | ```jsx
31 | import React from "react";
32 | import { useSelector } from "react-redux";
33 | import { authors, authorBooks } from "../selectors";
34 | 
35 | export function AuthorProfile(props) {
36 |   const author = useSelector(state => authors(state, props.authorId));
37 |   const books = useSelector(state => authorBooks(state, props.authorId));
38 |   return (
39 |     <div>{author.name} has written {books.length} books!</div>
40 |   );
41 | };
42 | ```
43 | 
44 | To use this component, we simply pass an author's primary key to it.
45 | ```jsx
46 | <AuthorProfile authorId={1} />
47 | ```
48 | 
49 | 


--------------------------------------------------------------------------------
/standalone-docs/basics/Reducers.md:
--------------------------------------------------------------------------------
 1 | ---
 2 | id: reducers
 3 | title: Reducers
 4 | sidebar_label: Reducers
 5 | hide_title: true
 6 | ---
 7 | 
 8 | # Reducers
 9 | 
10 | The default reducer that you passed to the Redux store allows you to update your models based on Redux actions.
11 | 
12 | ## `Model.reducer`
13 | 
14 | Your model classes' static `reducer` function will automatically be called when a Redux action is dispatched. Override them to add your own behavior.
15 | 
16 | ```js
17 | class Book extends Model {
18 |     static reducer(action, Book, session) {
19 |         let book;
20 |         switch (action.type) {
21 |         case 'CREATE_BOOK':
22 |             Book.create(action.payload);
23 |             break;
24 |         case 'UPDATE_BOOK':
25 |             book = Book.withId(action.payload.id);
26 |             book.update(action.payload);
27 |             break;
28 |         case 'REMOVE_BOOK':
29 |             book = Book.withId(action.payload);
30 |             book.delete();
31 |             break;
32 |         case 'ADD_AUTHOR_TO_BOOK':
33 |             book = Book.withId(action.payload.bookId);
34 |             book.authors.add(action.payload.author);
35 |             break;
36 |         case 'REMOVE_AUTHOR_FROM_BOOK':
37 |             book = Book.withId(action.payload.bookId);
38 |             book.authors.remove(action.payload.authorId);
39 |             break;
40 |         case 'ASSIGN_PUBLISHER':
41 |             book = Book.withId(action.payload.bookId);
42 |             book.publisherId = action.payload.publisherId;
43 |             break;
44 |         }
45 |         // Return value is ignored.
46 |     }
47 | }
48 | ```
49 | 
50 | ## Static methods
51 | 
52 | To find out which methods you can call on your model class [take a look at the API reference](../api/Model). The most useful ones are as follows:
53 | 
54 | * `create(props)`: creates a new Model instance with `props`. If you don't supply an id, the new `id` will be `Math.max(...allOtherIds) + 1`.
55 | * `upsert(props)`: either creates a new Model instance with `props` or, in case an instance with the same id already exists, updates that one - in other words it's **create or update** behaviour.
56 | * `withId(id)`: gets the Model instance with id `id`.
57 | * `get(matchObj)`: gets a Model instance based on matching properties in `matchObj` (if you are sure there is only one matching instance).
58 | 
59 | ## Instance methods
60 | 
61 | Once you have a model instance, you may want to call one of the following methods:
62 | 
63 | * `update(mergeObj)`: merges `mergeObj` with the Model instance properties. Returns `undefined`.
64 | * `delete()`: deletes the record for this Model instance in the database. Returns `undefined`.
65 | 


--------------------------------------------------------------------------------
/standalone-docs/basics/Selectors.md:
--------------------------------------------------------------------------------
 1 | ---
 2 | id: selectors
 3 | title: Selectors
 4 | sidebar_label: Selectors
 5 | hide_title: true
 6 | ---
 7 | 
 8 | # Selectors
 9 | 
10 | Selectors are functions that cache their result after they are executed. When you call them again with the same parameters, the result will be returned immediately. This can drastically improve the performance of web frontends by preventing unnecessary re-rendering of UI components.
11 | 
12 | Redux-ORM provides a simple API for creating model related selectors. For example, given Movie is a model class registered to `orm`, we can write:
13 | 
14 | ```js
15 | import { createSelector } from 'redux-orm';
16 | const movies = createSelector(orm.Movie);
17 | ```
18 | 
19 | ## Argument types
20 | 
21 | Pass a single primary key, an array of primary keys or nothing as an argument.
22 | 
23 | ```js
24 | movies(state);             // array of all movies
25 | movies(state, 1);          // movie with ID 1
26 | movies(state, [1, 2, 3]);  // array of movies with ID 1, 2 and 3
27 | ```
28 | 
29 | > Arguments are compared shallowly so make sure not to create new references during each call.
30 | 
31 | ## Mapping models to their fields
32 | 
33 | A selector can directly retrieve related model references for you.
34 | 
35 | ```js
36 | const moviePublisher = createSelector(orm.Movie.publisher);
37 | 
38 | moviePublisher(state);            // array of each movie's publisher
39 | moviePublisher(state, 1);         // the first movie's publisher
40 | moviePublisher(state, [1, 2, 3]); // array of each of the movies with ID 1, 2 and 3
41 | ```
42 | 
43 | ### Chaining relationship accessors
44 | 
45 | For relationships, this works in a chained way as well.
46 | 
47 | ```js
48 | const coverBookAuthors = createSelector(orm.Cover.book.authors);
49 | ```
50 | 
51 | ### The default is `null`
52 | 
53 | If the cover or its book don't exist, `null` is returned.
54 | 
55 | ```js
56 | coverBookAuthors(state);  // []
57 |                           // or [null, null, …] if there are covers but no authors
58 | coverBookAuthors(state, 1);         // null
59 | coverBookAuthors(state, [1, 2, 3]); // [null, null, null]
60 | ```
61 | 
62 | ## Map over collections using `.map()`
63 | 
64 | `map()` is a Redux-ORM method which returns a selector that will be called for all instances in a collection. For example, we might want a selector that returns all titles of books within a certain genre.
65 | 
66 | ```js
67 | const genreTitles = createSelector(
68 |     orm.Genre.books.map(orm.Book.title)
69 | );
70 | 
71 | genreTitles(state, 'Realism');
72 | // ['The Adventures of Huckleberry Finn', 'The Portrait of a Lady']
73 | ```
74 | 
75 | ## Prerequisites
76 | 
77 | You must tell your `ORM` how its selectors can retrieve the Redux branch. You do this by passing a `stateSelector` function to the `ORM` constructor.
78 | 
79 | ```diff
80 | -const orm = new ORM();
81 | +const orm = new ORM({
82 | +    stateSelector: state => state.orm,
83 | +});
84 | ```
85 | 


--------------------------------------------------------------------------------
/standalone-docs/faq/Performance.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: performance
3 | title: Performance
4 | sidebar_label: Performance
5 | hide_title: true
6 | ---
7 | 
8 | # Redux-ORM FAQ: Performance
9 | 


--------------------------------------------------------------------------------
/standalone-docs/introduction/CoreConcepts.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: core-concepts
3 | title: Core Concepts
4 | sidebar_label: Core Concepts
5 | hide_title: true
6 | ---
7 | 
8 | # Core Concepts `todo!`
9 | 


--------------------------------------------------------------------------------
/standalone-docs/introduction/GettingStarted.md:
--------------------------------------------------------------------------------
 1 | ---
 2 | id: getting-started
 3 | title: Getting Started with Redux-ORM
 4 | sidebar_label: Getting Started
 5 | hide_title: true
 6 | ---
 7 | 
 8 | # Getting Started with Redux-ORM
 9 | 
10 | Redux-ORM helps you manage relational data in Redux.
11 | 
12 | ## Installation
13 | 
14 | The library is available as a package on NPM for use with a module bundler or in a Node application:
15 | 
16 | ```bash
17 | npm install redux-orm
18 | ```
19 | 
20 | ### Polyfill
21 | 
22 | Redux-ORM uses some <abbr title="ECMAScript">ES</abbr>6+ features, such as `Set` and `Symbol`.
23 | 
24 | So if you are dealing with a pre-ES6 environment, you should load a polyfill like [`babel-polyfill`](https://babeljs.io/docs/usage/polyfill/) before using Redux-ORM.
25 | 
26 | ### Details
27 | 
28 | For more details, see the [Installation](Installation.md) page. Or [skip ahead to the Quick Start](basics/README.md).
29 | 


--------------------------------------------------------------------------------
/standalone-docs/introduction/Installation.md:
--------------------------------------------------------------------------------
 1 | ---
 2 | id: installation
 3 | title: Installation
 4 | sidebar_label: Installation
 5 | hide_title: true
 6 | ---
 7 | 
 8 | # Installation
 9 | 
10 | As is typical for modern JavaScript libraries, the installation is straightforward.
11 | 
12 | ## Package managers
13 | 
14 | Install with `npm`:
15 | 
16 | ```bash
17 | npm install redux-orm
18 | ```
19 | 
20 | Install with `yarn`:
21 | 
22 | ```bash
23 | yarn add redux-orm
24 | ```
25 | 
26 | ## Browser script
27 | 
28 | Or use our precompiled <abbr title="Universal Module Definition">UMD</abbr> module. Adding the following tag exposes a global called `ReduxOrm`:
29 | 
30 | ```html
31 | <script src="https://unpkg.com/redux-orm/dist/redux-orm.min.js"></script>
32 | ```
33 | 
34 | ## Downloads
35 | 
36 | * [Latest stable browser build (minimized)](https://unpkg.com/redux-orm/dist/redux-orm.min.js)
37 |   * [Source Map](https://unpkg.com/redux-orm/dist/redux-orm.min.js.map)
38 | * [Latest stable browser build](https://unpkg.com/redux-orm/dist/redux-orm.js) (only ever use if size does not matter)
39 | 


--------------------------------------------------------------------------------
/standalone-docs/introduction/README.md:
--------------------------------------------------------------------------------
1 | # Introduction
2 | 
3 | - [Core Concepts](CoreConcepts.md)
4 | - [Learning Resources](LearningResources.md)
5 | 


--------------------------------------------------------------------------------
/standalone-docs/recipes/README.md:
--------------------------------------------------------------------------------
 1 | ---
 2 | id: recipe-index
 3 | title: Recipes: Index
 4 | sidebar_label: Recipes: Index
 5 | hide_title: true
 6 | ---
 7 | 
 8 | # Recipes
 9 | 
10 | These are some use cases and code snippets to get you started with **Redux-ORM** in a real app. They assume you understand the topics in [basic](../basics/README.md) and [advanced](../advanced/README.md) tutorials.
11 | 
12 | - [Writing Tests](WritingTests.md)
13 | 


--------------------------------------------------------------------------------
/standalone-docs/recipes/WritingTests.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: writing-tests
3 | title: Writing Tests
4 | sidebar_label: Writing Tests
5 | hide_title: true
6 | ---
7 | 
8 | # Writing Tests
9 | 


--------------------------------------------------------------------------------
/tasks/api-docs/build.js:
--------------------------------------------------------------------------------
 1 | const jsdoc2md = require("jsdoc-to-markdown");
 2 | const fs = require("fs");
 3 | const path = require("path");
 4 | const config = require("./config");
 5 | const util = require("util");
 6 | const readFile = util.promisify(fs.readFile);
 7 | const writeFile = util.promisify(fs.writeFile);
 8 | 
 9 | module.exports = buildApiDocs;
10 | 
11 | async function buildApiDocs() {
12 |     const {
13 |         dmdOpts,
14 |         src,
15 |         dest,
16 |         partial,
17 |         helper,
18 |         include,
19 |         rootTemplate,
20 |         configure,
21 |     } = config;
22 | 
23 |     const template = await readFile(path.resolve(rootTemplate));
24 | 
25 |     async function toMarkdown(file) {
26 |         const data = await jsdoc2md.getTemplateData({ configure, files: file });
27 |         const markdown = await jsdoc2md.render({
28 |             data,
29 |             configure,
30 |             template: template.toString(),
31 |             partial,
32 |             helper,
33 |             ...dmdOpts,
34 |         });
35 | 
36 |         const outputPath = file =>
37 |             path.resolve(dest, path.basename(file).replace(/\.js$/, ".md"));
38 | 
39 |         return writeFile(outputPath(file), markdown);
40 |     }
41 | 
42 |     const promisedDocs = include
43 |         .map(fileName => `${src}/${fileName}.js`)
44 |         .map(fileName => toMarkdown(fileName));
45 | 
46 |     return Promise.all(promisedDocs);
47 | }
48 | 


--------------------------------------------------------------------------------
/tasks/api-docs/clean.js:
--------------------------------------------------------------------------------
1 | const gulp = require("gulp");
2 | const clean = require("gulp-clean");
3 | const config = require("./config");
4 | 
5 | module.exports = () =>
6 |     gulp
7 |         .src([`${config.dest}/**/*.md`, `!${config.dest}/**/README.md`])
8 |         .pipe(clean({ force: true }));
9 | 


--------------------------------------------------------------------------------
/tasks/api-docs/config.js:
--------------------------------------------------------------------------------
 1 | module.exports = {
 2 |     src: "src",
 3 |     dest: "standalone-docs/api",
 4 |     partial: "tasks/api-docs/partial/*.hbs",
 5 |     helper: "tasks/api-docs/helper/*.js",
 6 |     rootTemplate: "tasks/api-docs/partial/template.hbs",
 7 |     configure: "jsdoc.conf.json",
 8 |     include: ["Model", "ORM", "fields/index", "QuerySet", "Session", "redux"],
 9 |     dmdOpts: {
10 |         "no-cache": true,
11 |         "name-format": true,
12 |         "no-gfm": false,
13 |         "heading-depth": 1, // number
14 |         "member-index-format": "grouped", // none|grouped|table|dl
15 |         "module-index-format": "dl", // none|grouped|table|dl
16 |         "param-list-format": "table", // list|table
17 |         "property-list-format": "table", // list|table
18 |     },
19 | };
20 | 


--------------------------------------------------------------------------------
/tasks/api-docs/helper/docusaurus-helpers.js:
--------------------------------------------------------------------------------
 1 | const links = {};
 2 | 
 3 | function getLink(parent, name, link) {
 4 |     if (parent === "event") return name;
 5 |     let sig = `${parent}#${name}`;
 6 |     let out = links[sig];
 7 | 
 8 |     if (link) {
 9 |         links[sig] = link;
10 |         out = link;
11 |     } else if (!out) {
12 |         sig = `null#${name}`;
13 |         out = links[sig];
14 |     }
15 | 
16 |     return out;
17 | }
18 | 
19 | function docusaurusLink(
20 |     parent,
21 |     prefix,
22 |     accessSymbol,
23 |     name,
24 |     methodSign,
25 |     returnTypes,
26 |     scope
27 | ) {
28 |     const parentAndSymbol =
29 |         scope === "instance" ? [parent.toLowerCase(), "+"] : [parent, "."];
30 | 
31 |     const link = ["#", ...parentAndSymbol, name].join("");
32 | 
33 |     return getLink(parent, name, link);
34 | }
35 | 
36 | function docusaurusGuessLink(
37 |     parent,
38 |     prefix,
39 |     accessSymbol,
40 |     name,
41 |     methodSign,
42 |     returnTypes
43 | ) {
44 |     const link = getLink(parent, name);
45 |     if (link) {
46 |         return `[${name}](${link})`;
47 |     }
48 |     return name;
49 | }
50 | 
51 | function docusaurusParseLink(roughName) {
52 |     if (!roughName) return;
53 |     if (roughName.indexOf("http") >= 0) {
54 |         return `[${roughName}](${roughName})`;
55 |     }
56 |     const parts = roughName.split(/[#\.:]/);
57 |     const link = getLink(parts[0], parts[1]);
58 |     if (link) {
59 |         return `[${roughName}](${link})`;
60 |     }
61 |     return roughName;
62 | }
63 | 
64 | function sanitize(returnTypes) {
65 |     if (Array.isArray(returnTypes)) {
66 |         return returnTypes.join("|");
67 |     }
68 |     return returnTypes;
69 | }
70 | 
71 | function docusaurusAnchor(longname, scope) {
72 |     return scope === "instance"
73 |         ? `${longname
74 |               .substr(0, longname.indexOf("#"))
75 |               .toLowerCase()}+${longname.substr(longname.indexOf("#") + 1)}`
76 |         : longname;
77 | }
78 | 
79 | function isStatic() {
80 |     return this.scope === "static" ? "static " : "";
81 | }
82 | 
83 | function capitalize(str) {
84 |     return str.charAt(0).toUpperCase() + str.substr(1);
85 | }
86 | 
87 | exports.isStatic = isStatic;
88 | exports.sanitize = sanitize;
89 | exports.capitalize = capitalize;
90 | exports.docusaurusAnchor = docusaurusAnchor;
91 | exports.docusaurusGuessLink = docusaurusGuessLink;
92 | exports.docusaurusParseLink = docusaurusParseLink;
93 | exports.docusaurusLink = docusaurusLink;
94 | 


--------------------------------------------------------------------------------
/tasks/api-docs/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 |     clean: require("./clean"),
3 |     build: require("./build"),
4 | };
5 | 


--------------------------------------------------------------------------------
/tasks/api-docs/partial/defaultvalue.hbs:
--------------------------------------------------------------------------------
1 | {{#unless (equal defaultvalue undefined)}}`{{#if equals}} = {{/if}}{{#if (equal type.names.[0] "string")}}{{{json-stringify defaultvalue}}}{{else}}{{{defaultvalue}}}{{/if}}`{{/unless}}


--------------------------------------------------------------------------------
/tasks/api-docs/partial/docusaurus-header.hbs:
--------------------------------------------------------------------------------
1 | ---
2 | id: {{anchorName}}
3 | title: {{capitalize name}}
4 | sidebar_label: {{capitalize name}}
5 | hide_title: true
6 | ---
7 | 


--------------------------------------------------------------------------------
/tasks/api-docs/partial/header.hbs:
--------------------------------------------------------------------------------
1 | 
2 | <a name="{{{docusaurusAnchor longname scope}}}"></a>
3 | 
4 | {{>heading-indent}}{{>sig-name}}
5 | 
6 | 


--------------------------------------------------------------------------------
/tasks/api-docs/partial/link.hbs:
--------------------------------------------------------------------------------
1 | {{! usage: link to="namepath" html=true/false caption="optional caption"~}}
2 | 
3 | {{#link to~}}
4 | {{{docusaurusGuessLink @parent @prefix @accessSymbol name @methodSign @returnTypes}}}{{/link~}}
5 | 


--------------------------------------------------------------------------------
/tasks/api-docs/partial/member-index-grouped.hbs:
--------------------------------------------------------------------------------
1 | {{string-repeat "    " (add level baseLevel)}}* {{#unless (equal _title undefined)}}_{{_title}}_{{else}}{{>sig-link}}{{/unless}}
2 | {{#groupBy (option "group-by")~}}
3 | {{>member-index-grouped baseLevel=(add ../level ../baseLevel 1)~}}
4 | {{/groupBy~}}
5 | 


--------------------------------------------------------------------------------
/tasks/api-docs/partial/module-index-table.hbs:
--------------------------------------------------------------------------------
 1 | {{#modules~}}
 2 | {{#if @first~}}
 3 | {{>heading-indent}}Modules
 4 | Module | Description
 5 | ------ | -----------
 6 | {{/if}}
 7 | {{>sig-link}} | {{{inlineLinks description}}}
 8 | {{/modules}}
 9 | 
10 | 


--------------------------------------------------------------------------------
/tasks/api-docs/partial/module-index.hbs:
--------------------------------------------------------------------------------
1 | {{#unless (optionEquals "module-index-format" "none")~}}
2 | {{>module-index-table ~}}
3 | {{/unless~}}
4 | 


--------------------------------------------------------------------------------
/tasks/api-docs/partial/params-table.hbs:
--------------------------------------------------------------------------------
 1 | {{#if params}}
 2 | 
 3 | {{tableHead params "name|Param" "type|Type" "defaultvalue|Default" "description|Description" ~}}
 4 | 
 5 | {{#tableRow params "name" "type" "defaultvalue" "description" ~}}
 6 | | {{#if @col1}}{{>param-table-name}} | {{/if~}}
 7 | {{#if @col2}}{{>linked-type-list types=type.names delimiter=" ⎮ " }} | {{/if~}}
 8 | {{#if @col3}}{{>defaultvalue}} | {{/if~}}
 9 | {{#if @col4}}{{{stripNewlines (inlineLinks description)}}} |{{/if}}
10 | {{/tableRow}}
11 | 
12 | {{/if}}


--------------------------------------------------------------------------------
/tasks/api-docs/partial/properties-list.hbs:
--------------------------------------------------------------------------------
1 | {{#if properties}}**Properties**
2 | 
3 | {{#each properties~}}
4 | {{#if (regexp-test name "\w+\.\w+")}}  {{/if}}- {{{name}}} {{>linked-type-list types=type.names delimiter=" ⎮ " ~}}{{#if description}} - {{{inlineLinks description}}}{{/if}}  
5 | {{/each}}
6 | 
7 | {{/if~}}


--------------------------------------------------------------------------------
/tasks/api-docs/partial/properties-table.hbs:
--------------------------------------------------------------------------------
 1 | {{#if properties}}**Properties**
 2 | 
 3 | {{tableHead properties "name|Name" "type|Type" "defaultvalue|Default" "description|Description" ~}}
 4 | 
 5 | {{#tableRow properties "name" "type" "defaultvalue" "description" ~}}
 6 | | {{#if @col1}}{{name}} | {{/if~}}
 7 | {{#if @col2}}{{>linked-type-list types=type.names delimiter=" ⎮ " }} | {{/if~}}
 8 | {{#if @col3}}{{>defaultvalue}} | {{/if~}}
 9 | {{#if @col4}}{{{stripNewlines (inlineLinks description)}}} |{{/if}}
10 | {{/tableRow}}
11 | 
12 | {{/if}}


--------------------------------------------------------------------------------
/tasks/api-docs/partial/returns.hbs:
--------------------------------------------------------------------------------
 1 | {{#if returns}}
 2 | {{#if returns.[0].description~}}
 3 | **Returns**: {{#each returns~}}
 4 |   {{#if type~}}
 5 |   {{#if type.names}}{{>linked-type-list types=type.names delimiter=" ⎮ " ~}}{{/if}}
 6 |   {{~#if description}} - {{{inlineLinks description}}}{{/if~}}
 7 |   {{else~}}
 8 |   {{{inlineLinks description}~}}
 9 |   {{/if~}}
10 | {{~/each}}
11 |   
12 | {{/if}}{{/if}}


--------------------------------------------------------------------------------
/tasks/api-docs/partial/see.hbs:
--------------------------------------------------------------------------------
 1 | {{#if see~}}
 2 | 
 3 | {{#if (equal see.length 1)~}}
 4 | **See**: {{{docusaurusParseLink see.[0]}}}
 5 | {{else~}}
 6 | **See**
 7 | 
 8 | {{#each see}}- {{{docusaurusParseLink this}}}
 9 | {{/each}}
10 | 
11 | {{/if~}}
12 | {{/if~}}
13 | 


--------------------------------------------------------------------------------
/tasks/api-docs/partial/separator.hbs:
--------------------------------------------------------------------------------
1 | {{#if (option "separators")}}
2 | 
3 | * * *
4 | 
5 | {{/if}}


--------------------------------------------------------------------------------
/tasks/api-docs/partial/sig-link-parent.hbs:
--------------------------------------------------------------------------------
 1 | {{#if name}}{{#sig~}}
 2 | {{{@depOpen}~}}
 3 | [{{{@codeOpen}~}}
 4 | {{#if @prefix}}{{@prefix}} {{/if~}}
 5 | {{#if (isClassMember)}}{{@parent~}}{{/if~}}
 6 | {{@accessSymbol}}{{#if (isEvent)}}"{{{name}}}"{{else}}{{{name}}}{{/if~}}
 7 | {{~#if @methodSign}}{{#if (isEvent)}} {{@methodSign}}{{else}}{{@methodSign}}{{/if}}{{/if~}}
 8 | {{{@codeClose}}}](#{{{anchorName}}})
 9 | {{~#if @returnSymbol}} {{@returnSymbol}}{{/if~}}
10 | {{#if @returnTypes}} {{>linked-type-list types=@returnTypes delimiter=" ⎮ " }}{{/if~}}
11 | {{#if @suffix}} {{@suffix}}{{/if~}}
12 | {{{@depClose}~}}
13 | {{~/sig}}{{/if~}}
14 | 


--------------------------------------------------------------------------------
/tasks/api-docs/partial/sig-link.hbs:
--------------------------------------------------------------------------------
 1 | {{#if virtual}}*{{/if}}{{#with (parentObject)}}{{#if virtual}}*{{/if~}}{{/with~}}
 2 | {{#if name}}{{#sig~}}
 3 | {{{@depOpen}~}}
 4 | [{{{@codeOpen}~}}
 5 | {{#if @prefix}}{{@prefix}} {{/if~}}
 6 | {{#if (isEvent)}}"{{{name}}}"{{else}}{{{name}}}{{/if~}}
 7 | {{~#if @methodSign}}{{#if (isEvent)}} {{@methodSign}}{{else}}{{@methodSign}}{{/if}}{{/if~}}
 8 | {{{@codeClose}}}]({{{ docusaurusLink @parent @prefix @accessSymbol name @methodSign @returnTypes scope }}})
 9 | {{~#if @returnSymbol}} {{@returnSymbol}}{{/if~}}
10 | {{#if @returnTypes}} {{>linked-type-list types=@returnTypes delimiter=" ⎮ " }}{{/if~}}
11 | {{#if @suffix}} {{@suffix}}{{/if~}}
12 | {{{@depClose}~}}
13 | {{~/sig}}{{/if~}}
14 | {{#if virtual}}*{{/if}}{{#with (parentObject)}}{{#if virtual}}*{{/if~}}{{/with~}}
15 | 


--------------------------------------------------------------------------------
/tasks/api-docs/partial/sig-name.hbs:
--------------------------------------------------------------------------------
 1 | {{#if virtual}}*{{/if}}{{#with (parentObject)}}{{#if virtual}}*{{/if~}}{{/with~}}
 2 | {{#if name}}{{#sig~}}
 3 | {{{@depOpen}~}}
 4 | {{{@codeOpen}~}}
 5 | {{#if @prefix}}{{@prefix}} {{/if~}}
 6 | {{isStatic}} {{#if (isEvent)}}"{{{name}}}"{{else}}{{{name}}}{{/if~}}
 7 | {{#if @methodSign}}{{#if (isEvent)}} {{@methodSign}}{{else}}{{@methodSign}}{{/if}}{{/if~}}
 8 | {{{@codeClose}~}}
 9 | {{#if @returnSymbol}}{{@returnSymbol}}{{/if~}}
10 | {{#if @returnTypes}} {{@returnTypes}} {{/if~}}
11 | {{#if @suffix}} {{@suffix}}{{/if~}}
12 | {{{@depClose}~}}
13 | {{~/sig}}{{/if~}}
14 | {{#if virtual}}*{{/if}}{{#with (parentObject)}}{{#if virtual}}*{{/if~}}{{/with~}}
15 | 


--------------------------------------------------------------------------------
/tasks/api-docs/partial/template.hbs:
--------------------------------------------------------------------------------
1 | {{#modules}}
2 | {{>docusaurus-header}}{{>all-docs}}
3 | {{/modules}}
4 | {{#classes}}
5 | {{>docusaurus-header}}{{>all-docs}}
6 | {{/classes}}
7 | 


--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "compilerOptions": {
 3 |     "module": "es6",
 4 |     "moduleResolution": "node",
 5 |     "lib": ["es6"],
 6 |     "target": "es6",
 7 |     "noImplicitAny": true,
 8 |     "noImplicitThis": true,
 9 |     "strictNullChecks": true,
10 |     "strictFunctionTypes": true,
11 |     "types": [],
12 |     "noEmit": true,
13 |     "forceConsistentCasingInFileNames": true
14 |   },
15 |   "include": ["src/**/*"],
16 |   "files": ["src/index.d.ts", "src/redux-orm-tests.ts"]
17 | }
18 | 


--------------------------------------------------------------------------------
/webpack.common.js:
--------------------------------------------------------------------------------
 1 | /* eslint-disable import/no-extraneous-dependencies */
 2 | const path = require("path");
 3 | 
 4 | const LodashModuleReplacementPlugin = require("lodash-webpack-plugin");
 5 | 
 6 | module.exports = {
 7 |     entry: "./src/index.js",
 8 |     resolve: {
 9 |         modules: [path.resolve("./src"), "node_modules"],
10 |         extensions: [".js", ".ts"],
11 |     },
12 |     module: {
13 |         rules: [
14 |             {
15 |                 test: /\.(js|ts)$/,
16 |                 loader: "babel-loader",
17 |                 exclude: /node_modules/,
18 |                 options: {
19 |                     plugins: ["lodash"],
20 |                 },
21 |             },
22 |         ],
23 |     },
24 |     plugins: [new LodashModuleReplacementPlugin()],
25 | };
26 | 


--------------------------------------------------------------------------------
/webpack.dev.js:
--------------------------------------------------------------------------------
 1 | /* eslint-disable import/no-extraneous-dependencies */
 2 | const path = require("path");
 3 | 
 4 | const { merge } = require("webpack-merge");
 5 | const common = require("./webpack.common.js");
 6 | 
 7 | module.exports = merge(common, {
 8 |     mode: "development",
 9 |     output: {
10 |         path: path.resolve("./dist"),
11 |         filename: "redux-orm.js",
12 |         library: "ReduxOrm",
13 |         libraryTarget: "umd",
14 |         umdNamedDefine: true,
15 |     },
16 |     devtool: "eval-source-map",
17 | });
18 | 


--------------------------------------------------------------------------------
/webpack.prod.js:
--------------------------------------------------------------------------------
 1 | /* eslint-disable import/no-extraneous-dependencies */
 2 | const path = require("path");
 3 | 
 4 | const { merge } = require("webpack-merge");
 5 | const common = require("./webpack.common.js");
 6 | 
 7 | module.exports = merge(common, {
 8 |     mode: "production",
 9 |     output: {
10 |         path: path.resolve("./dist"),
11 |         filename: "redux-orm.min.js",
12 |         library: "ReduxOrm",
13 |         libraryTarget: "umd",
14 |         umdNamedDefine: true,
15 |     },
16 |     devtool: "source-map",
17 | });
18 | 


--------------------------------------------------------------------------------
/website/_redirects:
--------------------------------------------------------------------------------
 1 | # Redirect all of our current Gitbook-generated URLs to the corresponding Docusaurus URLs
 2 | # We will omit any URLs that directly match already, like /api/store
 3 | 
 4 | # Note that more general URLs need to go later, so we'll put them at the end of each section
 5 | 
 6 | /basics       /basics/basic-tutorial:splat
 7 | /advanced     /advanced/advanced-tutorial:splat
 8 | /api         /api/api-reference
 9 | /api/api-reference/*        /api/api-reference:splat
10 | /api/*        /api/api-reference:splat
11 | /api/index.html        /api/api-reference
12 | /faq/performance'*         /faq/performance:splat
13 | /introduction/coreconcepts*         /introduction/core-concepts:splat
14 | /introduction/learningresources*         /introduction/learning-resources:splat
15 | /introduction         /introduction/getting-started
16 | /recipes/writingtests*         /recipes/writing-tests:splat
17 | /recipes         /recipes/recipe-index
18 | /docs/advanced/index.html        /advanced/advanced-tutorial
19 | /docs/advanced/        /advanced/advanced-tutorial
20 | /docs/faq/Performance.html*        /faq/performance:splat
21 | /docs/FAQ.html*        /faq:splat
22 | /docs/recipes/WritingTests.html*        /recipes/writing-tests:splat
23 | /docs/Troubleshooting.html*        /troubleshooting:splat
24 | 


--------------------------------------------------------------------------------
/website/core/Footer.js:
--------------------------------------------------------------------------------
 1 | /**
 2 |  * Copyright (c) 2020, Redux-ORM
 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 | const React = require('react');
 9 | 
10 | const docUrl = (doc, baseUrl) => `${baseUrl}${doc}`;
11 | 
12 | const Footer = props => (
13 |     <footer className="nav-footer" id="footer">
14 |         <section className="sitemap">
15 |             <a href={props.config.baseUrl} className="nav-home">
16 |                 {props.config.footerIcon && (
17 |                     <img
18 |                         src={props.config.baseUrl + props.config.footerIcon}
19 |                         alt={props.config.title}
20 |                         width="66"
21 |                         height="58"
22 |                     />
23 |                 )}
24 |             </a>
25 |             <div>
26 |                 <h5>Docs</h5>
27 |                 <a
28 |                     href={docUrl(
29 |                         'introduction/getting-started',
30 |                         props.config.baseUrl
31 |                     )}
32 |                 >
33 |                     Getting Started
34 |                 </a>
35 |                 {/* <a href={docUrl("introduction/core-concepts", props.config.baseUrl)}>
36 |           Core Concepts
37 |         </a> */}
38 |                 <a href={docUrl('basics/quick-start', props.config.baseUrl)}>
39 |                     Quick Start
40 |                 </a>
41 |                 <a
42 |                     href={docUrl(
43 |                         'advanced/complex-selectors',
44 |                         props.config.baseUrl
45 |                     )}
46 |                 >
47 |                     Advanced scenarios
48 |                 </a>
49 |             </div>
50 |             <div>
51 |                 <h5>Community</h5>
52 |                 <a
53 |                     href="https://stackoverflow.com/questions/tagged/redux-orm"
54 |                     target="_blank"
55 |                     rel="noreferrer noopener"
56 |                 >
57 |                     Stack Overflow
58 |                 </a>
59 |                 <a
60 |                     href="https://gitter.im/redux-orm/Lobby"
61 |                     target="_blank"
62 |                     rel="noreferrer noopener"
63 |                 >
64 |                     Gitter
65 |                 </a>
66 |             </div>
67 |             <div>
68 |                 <h5>More</h5>
69 |                 <a href="https://github.com/redux-orm/redux-orm/">GitHub</a>
70 |                 <a
71 |                     className="github-button"
72 |                     href={props.config.repoUrl}
73 |                     data-icon="octicon-star"
74 |                     data-count-href="/redux-orm/redux-orm/stargazers"
75 |                     data-show-count="true"
76 |                     data-count-aria-label="# stargazers on GitHub"
77 |                     aria-label="Star this project on GitHub"
78 |                 >
79 |                     Star
80 |                 </a>
81 |             </div>
82 |         </section>
83 |         <section className="copyright">
84 |             {props.config.copyright}
85 |             <br />
86 |             Some icons copyright{' '}
87 |             <a
88 |                 href="https://fontawesome.com/license/free"
89 |                 style={{ color: 'white' }}
90 |             >
91 |                 Font Awesome.
92 |             </a>
93 |         </section>
94 |     </footer>
95 | );
96 | 
97 | module.exports = Footer;
98 | 


--------------------------------------------------------------------------------
/website/i18n/en.json:
--------------------------------------------------------------------------------
  1 | {
  2 |   "_comment": "This file is auto-generated by write-translations.js",
  3 |   "localized-strings": {
  4 |     "next": "Next",
  5 |     "previous": "Previous",
  6 |     "tagline": "A small, simple and immutable ORM to manage relational data in your Redux store",
  7 |     "docs": {
  8 |       "advanced/complex-selectors": {
  9 |         "title": "Complex Selectors",
 10 |         "sidebar_label": "Complex Selectors"
 11 |       },
 12 |       "advanced/custom-reducer": {
 13 |         "title": "Custom Reducer",
 14 |         "sidebar_label": "Custom Reducer"
 15 |       },
 16 |       "api/module_fields": {
 17 |         "title": "Fields",
 18 |         "sidebar_label": "Fields"
 19 |       },
 20 |       "api/Model": {
 21 |         "title": "Model",
 22 |         "sidebar_label": "Model"
 23 |       },
 24 |       "api/ORM": {
 25 |         "title": "ORM",
 26 |         "sidebar_label": "ORM"
 27 |       },
 28 |       "api/QuerySet": {
 29 |         "title": "QuerySet",
 30 |         "sidebar_label": "QuerySet"
 31 |       },
 32 |       "api/api-reference": {
 33 |         "title": "API Reference",
 34 |         "sidebar_label": "API Reference"
 35 |       },
 36 |       "api/module_redux": {
 37 |         "title": "Redux",
 38 |         "sidebar_label": "Redux"
 39 |       },
 40 |       "api/Session": {
 41 |         "title": "Session",
 42 |         "sidebar_label": "Session"
 43 |       },
 44 |       "basics/fields": {
 45 |         "title": "Relational Fields",
 46 |         "sidebar_label": "Relational Fields"
 47 |       },
 48 |       "basics/react": {
 49 |         "title": "Usage with React",
 50 |         "sidebar_label": "Usage with React"
 51 |       },
 52 |       "basics/quick-start": {
 53 |         "title": "Quick Start",
 54 |         "sidebar_label": "Quick Start"
 55 |       },
 56 |       "basics/reducers": {
 57 |         "title": "Reducers",
 58 |         "sidebar_label": "Reducers"
 59 |       },
 60 |       "basics/selectors": {
 61 |         "title": "Selectors",
 62 |         "sidebar_label": "Selectors"
 63 |       },
 64 |       "faq": {
 65 |         "title": "Index",
 66 |         "sidebar_label": "FAQ Index"
 67 |       },
 68 |       "faq/performance": {
 69 |         "title": "Performance",
 70 |         "sidebar_label": "Performance"
 71 |       },
 72 |       "introduction/core-concepts": {
 73 |         "title": "Core Concepts",
 74 |         "sidebar_label": "Core Concepts"
 75 |       },
 76 |       "introduction/getting-started": {
 77 |         "title": "Getting Started with Redux-ORM",
 78 |         "sidebar_label": "Getting Started"
 79 |       },
 80 |       "introduction/installation": {
 81 |         "title": "Installation",
 82 |         "sidebar_label": "Installation"
 83 |       },
 84 |       "introduction/README": {
 85 |         "title": "introduction/README"
 86 |       },
 87 |       "learning-resources": {
 88 |         "title": "Learning Resources",
 89 |         "sidebar_label": "Learning Resources"
 90 |       },
 91 |       "README": {
 92 |         "title": "README"
 93 |       },
 94 |       "recipes/recipe-index": {
 95 |         "title": "Recipes: Index",
 96 |         "sidebar_label": "Recipes: Index"
 97 |       },
 98 |       "recipes/writing-tests": {
 99 |         "title": "Writing Tests",
100 |         "sidebar_label": "Writing Tests"
101 |       },
102 |       "troubleshooting": {
103 |         "title": "Troubleshooting",
104 |         "sidebar_label": "Troubleshooting"
105 |       }
106 |     },
107 |     "links": {
108 |       "Getting Started": "Getting Started",
109 |       "Docs": "Docs",
110 |       "API": "API",
111 |       "GitHub": "GitHub"
112 |     },
113 |     "categories": {
114 |       "Introduction": "Introduction",
115 |       "Basics": "Basics",
116 |       "Advanced": "Advanced",
117 |       "API Reference": "API Reference"
118 |     }
119 |   },
120 |   "pages-strings": {
121 |     "Help Translate|recruit community translators for your project": "Help Translate",
122 |     "Edit this Doc|recruitment message asking to edit the doc source": "Edit",
123 |     "Translate this Doc|recruitment message asking to translate the docs": "Translate"
124 |   }
125 | }
126 | 


--------------------------------------------------------------------------------
/website/package.json:
--------------------------------------------------------------------------------
 1 | {
 2 |     "scripts": {
 3 |         "start": "docusaurus-start",
 4 |         "build": "docusaurus-build",
 5 |         "publish-gh-pages": "docusaurus-publish",
 6 |         "write-translations": "docusaurus-write-translations",
 7 |         "version": "docusaurus-version",
 8 |         "rename-version": "docusaurus-rename-version"
 9 |     },
10 |     "devDependencies": {
11 |         "@babel/plugin-proposal-class-properties": "^7.10.4",
12 |         "@babel/preset-react": "^7.10.4"
13 |     },
14 |     "dependencies": {
15 |         "docusaurus": "^1.14.6",
16 |         "plantuml-encoder": "^1.4.0"
17 |     }
18 | }
19 | 


--------------------------------------------------------------------------------
/website/pages/en/404.js:
--------------------------------------------------------------------------------
 1 | /**
 2 |  * Copyright (c) 2017-present, Redux-ORM.
 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 | const React = require('react');
 9 | 
10 | const siteConfig = require(`${process.cwd()}/siteConfig.js`);
11 | 
12 | const getTrackingScript = () => {
13 |     if (!siteConfig.gaTrackingId) {
14 |         return null;
15 |     }
16 | 
17 |     return {
18 |         __html: `
19 |       ga('create', "${siteConfig.gaTrackingId}");
20 |       ga('send', {
21 |         hitType: 'event',
22 |         eventCategory: '404 Response',
23 |         eventAction: window.location.href,
24 |         eventLabel: document.referrer
25 |       });`,
26 |     };
27 | };
28 | 
29 | const errorPage = () => (
30 |     <div className="error-page">
31 |         {getTrackingScript() && (
32 |             <script dangerouslySetInnerHTML={getTrackingScript()} />
33 |         )}
34 |         <div className="error-message">
35 |             <div className=" error-message-container container">
36 |                 <span>404 </span>
37 |                 <p>Page Not Found.</p>
38 |                 <a href="/">Return to the front page</a>
39 |             </div>
40 |         </div>
41 |     </div>
42 | );
43 | 
44 | errorPage.title = 'Page Not Found';
45 | 
46 | module.exports = errorPage;
47 | 


--------------------------------------------------------------------------------
/website/pages/en/index.js:
--------------------------------------------------------------------------------
  1 | /**
  2 |  * Copyright (c) 2017-present, Redux-ORM.
  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 | const React = require('react');
  9 | 
 10 | const CompLibrary = require('../../core/CompLibrary.js');
 11 | 
 12 | const { MarkdownBlock, GridBlock, Container } = CompLibrary;
 13 | 
 14 | const siteConfig = require(`${process.cwd()}/siteConfig.js`);
 15 | 
 16 | function docUrl(doc, language) {
 17 |     return `${siteConfig.baseUrl}${language ? `${language}/` : ''}${doc}`;
 18 | }
 19 | 
 20 | function imgUrl(img) {
 21 |     return `${siteConfig.baseUrl}img/${img}`;
 22 | }
 23 | 
 24 | class Button extends React.Component {
 25 |     render() {
 26 |         return (
 27 |             <div className="pluginWrapper buttonWrapper">
 28 |                 <a
 29 |                     className="button hero"
 30 |                     href={this.props.href}
 31 |                     target={this.props.target}
 32 |                 >
 33 |                     {this.props.children}
 34 |                 </a>
 35 |             </div>
 36 |         );
 37 |     }
 38 | }
 39 | 
 40 | Button.defaultProps = {
 41 |     target: '_self',
 42 | };
 43 | 
 44 | const SplashContainer = props => (
 45 |     <div className="homeContainer">
 46 |         <div className="homeSplashFade">
 47 |             <div className="wrapper homeWrapper">{props.children}</div>
 48 |         </div>
 49 |     </div>
 50 | );
 51 | 
 52 | const ProjectTitle = () => (
 53 |     <React.Fragment>
 54 |         <div
 55 |             style={{
 56 |                 display: 'flex',
 57 |                 justifyContent: 'center',
 58 |                 alignItems: 'center',
 59 |             }}
 60 |         >
 61 |             <img
 62 |                 src={imgUrl('redux-orm.svg')}
 63 |                 alt="Redux-ORM logo"
 64 |                 width={100}
 65 |                 height={100}
 66 |             />
 67 |             <h1 className="projectTitle">{siteConfig.title}</h1>
 68 |         </div>
 69 | 
 70 |         <h2 style={{ marginTop: '0.5em' }}>{siteConfig.tagline}</h2>
 71 |     </React.Fragment>
 72 | );
 73 | 
 74 | const PromoSection = props => (
 75 |     <div className="section promoSection">
 76 |         <div className="promoRow">
 77 |             <div className="pluginRowBlock">{props.children}</div>
 78 |         </div>
 79 |     </div>
 80 | );
 81 | 
 82 | class HomeSplash extends React.Component {
 83 |     render() {
 84 |         const language = this.props.language || '';
 85 |         return (
 86 |             <SplashContainer>
 87 |                 <div className="inner">
 88 |                     <ProjectTitle />
 89 |                     <PromoSection>
 90 |                         <Button
 91 |                             href={docUrl(
 92 |                                 'introduction/getting-started',
 93 |                                 language,
 94 |                             )}
 95 |                         >
 96 |                             Get Started
 97 |                         </Button>
 98 |                     </PromoSection>
 99 |                 </div>
100 |             </SplashContainer>
101 |         );
102 |     }
103 | }
104 | 
105 | const Installation = () => (
106 |     <div className="productShowcaseSection" style={{ textAlign: 'center' }}>
107 |         <h2 style={{ marginTop: 10, marginBottom: 5 }}>Installation</h2>
108 |         <MarkdownBlock>``` npm install --save redux ```</MarkdownBlock>
109 |     </div>
110 | );
111 | 
112 | const Block = props => (
113 |     <Container
114 |         id={props.id}
115 |         background={props.background}
116 |         className={props.className}
117 |     >
118 |         <GridBlock
119 |             align="center"
120 |             contents={props.children}
121 |             layout={props.layout}
122 |         />
123 |     </Container>
124 | );
125 | 
126 | const FeaturesTop = props => (
127 |     <Block layout="fourColumn" className="rowContainer featureBlock">
128 |         {[
129 |             {
130 |                 content:
131 |                     'Redux-ORM is fast. **All queries are lazily evaluated** and come with a built-in **support for memoization**.',
132 |                 image: imgUrl('lightweight.svg'),
133 |                 imageAlign: 'top',
134 |                 title: 'Lightweight',
135 |             },
136 |             {
137 |                 content:
138 |                     'It provides a clean abstraction over low-level updates, **protecting the state from accidental mutations.**',
139 |                 image: imgUrl('safe.svg'),
140 |                 imageAlign: 'top',
141 |                 title: 'Reliable',
142 |             },
143 |             {
144 |                 content:
145 |                     'With nearly **100% branch coverage**, the library is thoroughly tested to ensure **rock solid code quality**.',
146 |                 image: imgUrl('stable.svg'),
147 |                 imageAlign: 'top',
148 |                 title: 'Stable',
149 |             },
150 |             {
151 |                 content:
152 |                     'No matter where your data comes from, Redux-ORM will stay out of its way. Even a **custom database layer** is possible.',
153 |                 image: imgUrl('flexible.svg'),
154 |                 imageAlign: 'top',
155 |                 title: 'Flexible',
156 |             },
157 |         ]}
158 |     </Block>
159 | );
160 | 
161 | class Index extends React.Component {
162 |     render() {
163 |         const language = this.props.language || '';
164 | 
165 |         return (
166 |             <div>
167 |                 <HomeSplash language={language} />
168 |                 <div className="mainContainer">
169 |                     <div className="productShowcaseSection">
170 |                         <Container background="light">
171 |                             <FeaturesTop />
172 |                         </Container>
173 |                         <Container background="light" />
174 |                     </div>
175 |                 </div>
176 |             </div>
177 |         );
178 |     }
179 | }
180 | 
181 | module.exports = Index;
182 | 


--------------------------------------------------------------------------------
/website/plantuml-plugin/index.js:
--------------------------------------------------------------------------------
 1 | const encoder = require('plantuml-encoder');
 2 | 
 3 | function isUmlToken({ type, params }) {
 4 |     return type === 'fence' && params === 'uml';
 5 | }
 6 | 
 7 | function getTokenTransformer(url, theme) {
 8 |     return ({ tokens }) =>
 9 |         tokens.filter(isUmlToken).forEach(token =>
10 |             transformToken({
11 |                 token,
12 |                 url,
13 |                 theme,
14 |             }),
15 |         );
16 | }
17 | 
18 | function transformToken({ token, url, theme }) {
19 |     const themed = token.content.replace('@startuml', `@startuml\n${theme}`);
20 |     const diagramUrl = url + encoder.encode(themed);
21 | 
22 |     token.type = 'inline';
23 |     token.content = `![](${diagramUrl})`;
24 |     token.children = [];
25 | }
26 | 
27 | function extractTheme({ theme }) {
28 |     if (typeof theme === 'string') {
29 |         return theme;
30 |     } else if (typeof theme === 'function') {
31 |         return theme();
32 |     } else {
33 |         throw Error(
34 |             'supplied [theme] option is invalid - provide either a plantuml theme string or a function returning one',
35 |         );
36 |     }
37 | }
38 | 
39 | function plantumlPlugin(opts) {
40 |     return md =>
41 |         md.core.ruler.after(
42 |             'block',
43 |             'uml',
44 |             getTokenTransformer(opts.backendUrl, extractTheme(opts)),
45 |         );
46 | }
47 | 
48 | module.exports = plantumlPlugin;
49 | 


--------------------------------------------------------------------------------
/website/sidebars.json:
--------------------------------------------------------------------------------
 1 | {
 2 |     "docs": {
 3 |         "Introduction": [
 4 |             "introduction/getting-started",
 5 |             "introduction/installation"
 6 |         ],
 7 |         "Basics": [
 8 |             "basics/quick-start",
 9 |             "basics/fields",
10 |             "basics/reducers",
11 |             "basics/selectors",
12 |             "basics/react"
13 |         ],
14 |         "Advanced": ["advanced/complex-selectors", "advanced/custom-reducer"],
15 |         "API Reference": [
16 |             "api/api-reference",
17 |             "api/ORM",
18 |             "api/Model",
19 |             "api/QuerySet",
20 |             "api/Session",
21 |             "api/module_fields",
22 |             "api/module_redux"
23 |         ]
24 |     }
25 | }
26 | 


--------------------------------------------------------------------------------
/website/siteConfig.js:
--------------------------------------------------------------------------------
 1 | const fs = require('fs');
 2 | const plantumlPlugin = require('./plantuml-plugin');
 3 | const umlMdPlugin = plantumlPlugin({
 4 |     backendUrl: 'http://www.plantuml.com/plantuml/svg/',
 5 |     theme: () =>
 6 |         fs.readFileSync('../standalone-docs/assets/plantuml-theme.iuml'),
 7 | });
 8 | 
 9 | const baseUrl = '/redux-orm/';
10 | const siteConfig = {
11 |     title: 'Redux-ORM',
12 |     tagline:
13 |         'A small, simple and immutable ORM to manage relational data in your Redux store',
14 |     url: 'https://github.com/redux-orm/redux-orm',
15 |     baseUrl,
16 |     docsUrl: '',
17 |     // todo: add search once finished
18 |     // algolia: {
19 |     //     apiKey: '',
20 |     //     indexName: 'redux-orm',
21 |     //     algoliaOptions: {},
22 |     // },
23 | 
24 |     projectName: 'redux-orm',
25 |     organizationName: 'redux-orm',
26 | 
27 |     headerLinks: [
28 |         { doc: 'introduction/getting-started', label: 'Getting Started' },
29 |         { doc: 'basics/quick-start', label: 'Docs' },
30 |         { doc: 'api/api-reference', label: 'API' },
31 |         //         { doc: 'faq', label: 'FAQ' },
32 |         { href: 'https://github.com/redux-orm/redux-orm', label: 'GitHub' },
33 |     ],
34 | 
35 |     fonts: {
36 |         logoFont: [
37 |             'https://fonts.googleapis.com/css?family=Lato:900&display=swap',
38 |             'sans-serif',
39 |             '/css/custom.css',
40 |         ],
41 |     },
42 |     usePrism: ['jsx', 'javascript', 'tsx', 'typescript'],
43 |     headerIcon: 'img/redux-orm-white.svg',
44 |     footerIcon: 'img/redux-orm-white.svg',
45 |     favicon: '/redux-orm/img/favicon/favicon.ico',
46 |     customDocsPath: 'standalone-docs',
47 |     colors: {
48 |         primaryColor: '#bc001c',
49 |         secondaryColor: '#56000c',
50 |         accentColor1: '#ffabb0',
51 |         accentColor2: '#F3EAFF',
52 |         accentColor3: '#ffabb0',
53 |     },
54 | 
55 |     copyright: 'Copyright © 2020 Redux-ORM documentation authors.',
56 | 
57 |     highlight: {
58 |         theme: 'darcula',
59 |     },
60 | 
61 |     scripts: [
62 |         `${baseUrl}scripts/sidebarScroll.js`,
63 |         `${baseUrl}scripts/codeblock.js`,
64 |         `${baseUrl}scripts/code-block-buttons.js`,
65 |         'https://buttons.github.io/buttons.js',
66 |         'https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/2.0.0/clipboard.min.js',
67 |     ],
68 |     stylesheets: [`${baseUrl}code-block-buttons.css`],
69 |     enableUpdateTime: true,
70 |     onPageNav: 'separate',
71 |     cleanUrl: true,
72 |     docsSideNavCollapsible: true,
73 |     repoUrl: 'https://github.com/redux-orm/redux-orm',
74 |     markdownPlugins: [umlMdPlugin],
75 |     //todo add tracking id
76 |     gaTrackingId: '',
77 | };
78 | 
79 | module.exports = siteConfig;
80 | 


--------------------------------------------------------------------------------
/website/static/css/404.css:
--------------------------------------------------------------------------------
 1 | .error-page .error-message-container {
 2 |     margin-left: auto;
 3 |     margin-right: auto;
 4 |     padding-top: 40px;
 5 |     max-width: 1400px;
 6 |     width: 87%;
 7 | }
 8 |  .error-page .error-message {
 9 |     min-height: 90vh;
10 |     background: white;
11 | }
12 |  .error-page .error-message span {
13 |     color: #764ABC;
14 |     font-size: 8.8em;
15 |     font-weight: 700;
16 |     display: inline-block;
17 |     margin-top: 10vh;
18 |     text-align: center;
19 |     display: block;
20 | }
21 |  .error-page .error-message p {
22 |     color: black;
23 |     margin-top: 50px;
24 |     font-size: 1.6em;
25 |     text-align: center;
26 | }
27 | 
28 | .error-page .error-message a {
29 |    margin-bottom: 50px;
30 |    font-size: 1.6em;
31 |    text-align: center;
32 |    display: block;
33 |    text-decoration: underline;
34 | }
35 | 


--------------------------------------------------------------------------------
/website/static/css/code-block-buttons.css:
--------------------------------------------------------------------------------
 1 | 
 2 | /* "Copy" code block button */
 3 | pre {
 4 |     position: relative;
 5 | }
 6 | 
 7 | pre .btnIcon {
 8 |     position: absolute;
 9 |     top: 4px;
10 |     z-index: 2;
11 |     cursor: pointer;
12 |     border: 1px solid transparent;
13 |     padding: 0;
14 |     color: #fff;
15 |     background-color: transparent;
16 |     height: 30px;
17 |     transition: all .25s ease-out;
18 | }
19 | 
20 | pre .btnIcon:hover {
21 |     text-decoration: none;
22 | }
23 | 
24 | .btnIcon__body {
25 |     align-items: center;
26 |     display: flex;
27 | }
28 | 
29 | .btnIcon svg {
30 |     fill: currentColor;
31 |     margin-right: .4em;
32 | }
33 | 
34 | .btnIcon__label {
35 |     font-size: 11px;
36 | }
37 | 
38 | .btnClipboard {
39 |     right: 10px;
40 | }
41 | 


--------------------------------------------------------------------------------
/website/static/css/codeblock.css:
--------------------------------------------------------------------------------
 1 | /* "Copy" code block button */
 2 | pre {
 3 |   position: relative;
 4 | }
 5 | 
 6 | pre .btnIcon {
 7 |   position: absolute;
 8 |   top: 4px;
 9 |   z-index: 2;
10 |   cursor: pointer;
11 |   border: 1px solid transparent;
12 |   padding: 0;
13 |   color: #fff;
14 |   background-color: transparent;
15 |   height: 30px;
16 |   transition: all .25s ease-out;
17 | }
18 | 
19 | pre .btnIcon:hover {
20 |   text-decoration: none;
21 | }
22 | 
23 | .btnIcon__body {
24 |   align-items: center;
25 |   display: flex;
26 | }
27 | 
28 | .btnIcon svg {
29 |   fill: currentColor;
30 |   margin-right: .4em;
31 | }
32 | 
33 | .btnIcon__label {
34 |   font-size: 11px;
35 | }
36 | 
37 | .btnClipboard {
38 |   right: 10px;
39 | }


--------------------------------------------------------------------------------
/website/static/css/custom.css:
--------------------------------------------------------------------------------
  1 | /* your custom css */
  2 | 
  3 | .logo {
  4 |   font-family: $logoFont
  5 | }
  6 | 
  7 | header.postHeader:empty {
  8 |   display: none;
  9 | }
 10 | 
 11 | header.postHeader:empty + article h1 {
 12 |   margin-top: 0;
 13 | }
 14 | 
 15 | .homeContainer .homeWrapper {
 16 |   padding: 4em 10px 1em;
 17 | }
 18 | 
 19 | .post article a {
 20 |   /* add underlines to links in blocks of text for a11y */
 21 |   color: $primaryColor;
 22 |   text-decoration: underline;
 23 |   overflow: hidden;
 24 |   position: relative;
 25 |   transition: outline-offset 0.2s ease-in-out;
 26 | }
 27 | 
 28 | .post article a:hover,
 29 | .post article a:focus,
 30 | .post article a:active {
 31 |   /* change color and bg to display state change in more than one way for a11y */
 32 |   background-color: $secondaryColor;
 33 |   color: white;
 34 |   box-shadow: 0 0 0 2px $secondaryColor;
 35 |   outline: none;
 36 |   text-decoration: underline;
 37 |   overflow: hidden;
 38 |   transition: outline-offset 0.2s ease-in-out;
 39 | }
 40 | 
 41 | .post article blockquote {
 42 |   color: black;
 43 |   background-color: $accentColor4;
 44 |   border-left: 8px solid $accentColor5;
 45 | }
 46 | 
 47 | .post article blockquote a {
 48 |   /* add underlines to links in blocks of text for a11y */
 49 |   color: black;
 50 |   text-decoration: underline;
 51 |   overflow: hidden;
 52 |   position: relative;
 53 |   transition: outline-offset 0.2s ease-in-out;
 54 | }
 55 | 
 56 | .post article blockquote a:hover,
 57 | .post article blockquote a:focus,
 58 | .post article blockquote a:active {
 59 |   /* change color and bg to display state change in more than one way for a11y */
 60 |   background-color: $accentColor6;
 61 |   color: white;
 62 |   box-shadow: 0 0 0 2px $accentColor6;
 63 |   outline: none;
 64 |   text-decoration: underline;
 65 | }
 66 | 
 67 | .post article blockquote {
 68 |   color: black;
 69 |   background-color: $accentColor4;
 70 |   border-left: 8px solid $accentColor5;
 71 | }
 72 | 
 73 | .post article blockquote a {
 74 |   /* add underlines to links in blocks of text for a11y */
 75 |   color: black;
 76 |   text-decoration: underline;
 77 |   overflow: hidden;
 78 |   position: relative;
 79 |   transition: outline-offset 0.2s ease-in-out;
 80 | }
 81 | 
 82 | .post article blockquote a:hover,
 83 | .post article blockquote a:focus,
 84 | .post article blockquote a:active {
 85 |   /* change color and bg to display state change in more than one way for a11y */
 86 |   background-color: $accentColor6;
 87 |   color: white;
 88 |   box-shadow: 0 0 0 2px $accentColor6;
 89 |   outline: none;
 90 |   text-decoration: underline;
 91 | }
 92 | 
 93 | .post article .hash-link {
 94 |   /* add underlines to links in blocks of text for a11y */
 95 |   color: $accentColor1;
 96 |   transition: outline-offset 0.2s ease-in-out;
 97 |   opacity: 1;
 98 |   position: absolute;
 99 | }
100 | 
101 | .post article .hash-link:hover,
102 | .post article .hash-link:focus,
103 | .post article .hash-link:active {
104 |   /* change color and bg to display state change in more than one way for a11y */
105 |   color: $secondaryColor;
106 |   background-color: white;
107 |   box-shadow: 0 0 0 0 $accentColor6;
108 |   transition: outline-offset 0.2s ease-in-out;
109 | }
110 | 
111 | .hash-link .hash-link-icon {
112 |   fill: currentColor;
113 | }
114 | 
115 | 
116 | .fixedHeaderContainer header .headerTitleWithLogo {
117 |   color : white;
118 |   display: block !important;
119 | }
120 | 
121 | .navigationSlider .slidingNav ul.nav-site a {
122 |   color : white;
123 |   font-weight: 400;
124 | }
125 | 
126 | .navigationSlider .slidingNav ul.nav-site a:hover,
127 | .navigationSlider .slidingNav ul.nav-site a:focus,
128 | .navigationSlider .slidingNav ul.nav-site a:active {
129 |   color : white;
130 |   font-weight: 400;
131 |   text-decoration: underline;
132 | }
133 | 
134 | .navigationSlider .slidingNav ul a[href*="github"] {
135 |   font-size: 0;
136 | }
137 | 
138 | .navigationSlider .slidingNav ul a[href*="github"]::before {
139 |   content : "";
140 |   width : 32px;
141 |   height : 32px;
142 |   background: url("/redux-orm/img/github-brands.svg");
143 | }
144 | 
145 | .nav-footer .copyright,
146 | .nav-footer .sitemap a {
147 |   /* increase link contrast */
148 |   color: white;
149 | }
150 | 
151 | .button.hero {
152 |   background: $primaryColor;
153 |   color: #fff;
154 |   font-size: 30px;
155 |   padding: .5em 1.25em;
156 |   font-weight: bold;
157 | }
158 | 
159 | .button.hero:visited {
160 |   background: $primaryColor;
161 |   color: #fff;
162 | }
163 | 
164 | .button.hero:hover {
165 |   background: #fff;
166 |   color: $primaryColor;
167 | }
168 | 
169 | 
170 | .productShowcaseSection .rowContainer {
171 |   padding-top: 30px;
172 |   padding-bottom: 30px;
173 | }
174 | 
175 | .productShowcaseSection .featureBlock img {
176 |   width: 60px;
177 |   height: 60px;
178 |   max-width: 60px;
179 |   max-height: 60px;
180 | }
181 | 
182 | .featureBlock .blockContent > div p,
183 | .docsSurvey .blockContent > div p {
184 |   text-align: left;
185 | }
186 | 
187 | .libBlock > div p {
188 |   text-align: center;
189 | }
190 | 
191 | .libBlock h2 a,
192 | .libBlock h2 a:visited {
193 |   color : black;
194 |   display: flex;
195 |   justify-content: center;
196 |   align-items: center;
197 | }
198 | 
199 | .libBlock img {
200 |   margin-left: 5px;
201 |   max-width: 16px !important;
202 |   max-height: 16px !important;
203 | }
204 | 
205 | .container .wrapper .alignLeft h2 {
206 |   margin-top: 10px;
207 |   margin-bottom: 5px;
208 | }
209 | 
210 | .libsContainer {
211 |   display: flex;
212 |   justify-content: center;
213 | }
214 | 
215 | .libsContainer .docsSurvey h2 {
216 |   margin-top : 0;
217 |   margin-bottom: 30px;
218 | }
219 | 
220 | @media only screen and (max-device-width: 480px) {
221 |   .productShowcaseSection .featureBlock {
222 |     padding-top: 30px;
223 |     padding-bottom: 5px;
224 |   }
225 | 
226 |   .featureBlock .imageAlignTop .blockImage {
227 |     margin-bottom: 0;
228 |   }
229 | 
230 |   .featureBlock .blockContent h2 {
231 |     margin-top: 0;
232 |     margin-bottom: 5px;
233 |   }
234 | 
235 |   .featureBlock .blockContent {
236 |     margin-bottom: 25px;
237 |   }
238 | 
239 |   .homeSplashFade img {
240 |     width : 60px;
241 |     height : 60px;
242 |   }
243 | 
244 |   .homeSplashFade h1 {
245 |     margin-top: 0;
246 |     margin-bottom: 0;
247 |   }
248 | 
249 | }
250 | 
251 | 
252 | @media only screen and (min-device-width: 360px) and (max-device-width: 736px) {
253 | }
254 | 
255 | @media only screen and (min-width: 1024px) {
256 |   .reactNavSearchWrapper input#search_input_react {
257 |     height: 100%;
258 |     padding-top: 8px;
259 |     padding-bottom: 8px;
260 |     padding-left: 38px;
261 |   }
262 | 
263 |   .navSearchWrapper:before {
264 |     left: 24px;
265 |   }
266 | 
267 |   .navSearchWrapper:after {
268 |     left: 35px;
269 |   }
270 | }
271 | 
272 | @media only screen and (max-width: 1024px) {
273 |   .reactNavSearchWrapper input#search_input_react {
274 |     background: rgba(0, 0, 0, 0.2);
275 |   }
276 | }
277 | 
278 | @media only screen and (max-width: 1023px) {
279 |   .reactNavSearchWrapper input#search_input_react {
280 |     padding-left: 38px;
281 |   }
282 | 
283 |   .navSearchWrapper:before {
284 |     left: 24px;
285 |   }
286 | 
287 |   .navSearchWrapper:after {
288 |     left: 35px;
289 |   }
290 | 
291 |   .libsContainer {
292 |     flex-direction: column;
293 |   }
294 | }
295 | 
296 | @media only screen and (min-width: 1400px) {
297 | }
298 | 
299 | @media only screen and (min-width: 1500px) {
300 | }
301 | 


--------------------------------------------------------------------------------
/website/static/img/favicon/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redux-orm/redux-orm/32e9caa6940ee663726ad883b3d81bc672afd3fe/website/static/img/favicon/favicon.ico


--------------------------------------------------------------------------------
/website/static/img/flexible.svg:
--------------------------------------------------------------------------------
1 | <svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="cogs" class="svg-inline--fa fa-cogs fa-w-20" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path fill="currentColor" d="M512.1 191l-8.2 14.3c-3 5.3-9.4 7.5-15.1 5.4-11.8-4.4-22.6-10.7-32.1-18.6-4.6-3.8-5.8-10.5-2.8-15.7l8.2-14.3c-6.9-8-12.3-17.3-15.9-27.4h-16.5c-6 0-11.2-4.3-12.2-10.3-2-12-2.1-24.6 0-37.1 1-6 6.2-10.4 12.2-10.4h16.5c3.6-10.1 9-19.4 15.9-27.4l-8.2-14.3c-3-5.2-1.9-11.9 2.8-15.7 9.5-7.9 20.4-14.2 32.1-18.6 5.7-2.1 12.1.1 15.1 5.4l8.2 14.3c10.5-1.9 21.2-1.9 31.7 0L552 6.3c3-5.3 9.4-7.5 15.1-5.4 11.8 4.4 22.6 10.7 32.1 18.6 4.6 3.8 5.8 10.5 2.8 15.7l-8.2 14.3c6.9 8 12.3 17.3 15.9 27.4h16.5c6 0 11.2 4.3 12.2 10.3 2 12 2.1 24.6 0 37.1-1 6-6.2 10.4-12.2 10.4h-16.5c-3.6 10.1-9 19.4-15.9 27.4l8.2 14.3c3 5.2 1.9 11.9-2.8 15.7-9.5 7.9-20.4 14.2-32.1 18.6-5.7 2.1-12.1-.1-15.1-5.4l-8.2-14.3c-10.4 1.9-21.2 1.9-31.7 0zm-10.5-58.8c38.5 29.6 82.4-14.3 52.8-52.8-38.5-29.7-82.4 14.3-52.8 52.8zM386.3 286.1l33.7 16.8c10.1 5.8 14.5 18.1 10.5 29.1-8.9 24.2-26.4 46.4-42.6 65.8-7.4 8.9-20.2 11.1-30.3 5.3l-29.1-16.8c-16 13.7-34.6 24.6-54.9 31.7v33.6c0 11.6-8.3 21.6-19.7 23.6-24.6 4.2-50.4 4.4-75.9 0-11.5-2-20-11.9-20-23.6V418c-20.3-7.2-38.9-18-54.9-31.7L74 403c-10 5.8-22.9 3.6-30.3-5.3-16.2-19.4-33.3-41.6-42.2-65.7-4-10.9.4-23.2 10.5-29.1l33.3-16.8c-3.9-20.9-3.9-42.4 0-63.4L12 205.8c-10.1-5.8-14.6-18.1-10.5-29 8.9-24.2 26-46.4 42.2-65.8 7.4-8.9 20.2-11.1 30.3-5.3l29.1 16.8c16-13.7 34.6-24.6 54.9-31.7V57.1c0-11.5 8.2-21.5 19.6-23.5 24.6-4.2 50.5-4.4 76-.1 11.5 2 20 11.9 20 23.6v33.6c20.3 7.2 38.9 18 54.9 31.7l29.1-16.8c10-5.8 22.9-3.6 30.3 5.3 16.2 19.4 33.2 41.6 42.1 65.8 4 10.9.1 23.2-10 29.1l-33.7 16.8c3.9 21 3.9 42.5 0 63.5zm-117.6 21.1c59.2-77-28.7-164.9-105.7-105.7-59.2 77 28.7 164.9 105.7 105.7zm243.4 182.7l-8.2 14.3c-3 5.3-9.4 7.5-15.1 5.4-11.8-4.4-22.6-10.7-32.1-18.6-4.6-3.8-5.8-10.5-2.8-15.7l8.2-14.3c-6.9-8-12.3-17.3-15.9-27.4h-16.5c-6 0-11.2-4.3-12.2-10.3-2-12-2.1-24.6 0-37.1 1-6 6.2-10.4 12.2-10.4h16.5c3.6-10.1 9-19.4 15.9-27.4l-8.2-14.3c-3-5.2-1.9-11.9 2.8-15.7 9.5-7.9 20.4-14.2 32.1-18.6 5.7-2.1 12.1.1 15.1 5.4l8.2 14.3c10.5-1.9 21.2-1.9 31.7 0l8.2-14.3c3-5.3 9.4-7.5 15.1-5.4 11.8 4.4 22.6 10.7 32.1 18.6 4.6 3.8 5.8 10.5 2.8 15.7l-8.2 14.3c6.9 8 12.3 17.3 15.9 27.4h16.5c6 0 11.2 4.3 12.2 10.3 2 12 2.1 24.6 0 37.1-1 6-6.2 10.4-12.2 10.4h-16.5c-3.6 10.1-9 19.4-15.9 27.4l8.2 14.3c3 5.2 1.9 11.9-2.8 15.7-9.5 7.9-20.4 14.2-32.1 18.6-5.7 2.1-12.1-.1-15.1-5.4l-8.2-14.3c-10.4 1.9-21.2 1.9-31.7 0zM501.6 431c38.5 29.6 82.4-14.3 52.8-52.8-38.5-29.6-82.4 14.3-52.8 52.8z"></path></svg>


--------------------------------------------------------------------------------
/website/static/img/github-brands.svg:
--------------------------------------------------------------------------------
1 | <svg aria-hidden="true" data-prefix="fab" data-icon="github" class="svg-inline--fa fa-github fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 496"><path fill="white" d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"></path></svg>
2 | 


--------------------------------------------------------------------------------
/website/static/img/lightweight.svg:
--------------------------------------------------------------------------------
1 | <svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="feather-alt" class="svg-inline--fa fa-feather-alt fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M512 0C460.22 3.56 96.44 38.2 71.01 287.61c-3.09 26.66-4.84 53.44-5.99 80.24l178.87-178.69c6.25-6.25 16.4-6.25 22.65 0s6.25 16.38 0 22.63L7.04 471.03c-9.38 9.37-9.38 24.57 0 33.94 9.38 9.37 24.59 9.37 33.98 0l57.13-57.07c42.09-.14 84.15-2.53 125.96-7.36 53.48-5.44 97.02-26.47 132.58-56.54H255.74l146.79-48.88c11.25-14.89 21.37-30.71 30.45-47.12h-81.14l106.54-53.21C500.29 132.86 510.19 26.26 512 0z"></path></svg>


--------------------------------------------------------------------------------
/website/static/img/redux-orm-white.svg:
--------------------------------------------------------------------------------
 1 | <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 2 | <svg
 3 |    xmlns:dc="http://purl.org/dc/elements/1.1/"
 4 |    xmlns:cc="http://creativecommons.org/ns#"
 5 |    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
 6 |    xmlns:svg="http://www.w3.org/2000/svg"
 7 |    xmlns="http://www.w3.org/2000/svg"
 8 |    xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
 9 |    xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
10 |    version="1.1"
11 |    height="128.0"
12 |    width="128.0"
13 |    id="svg13"
14 |    sodipodi:docname="redux-orm22.svg"
15 |    inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
16 |   <metadata
17 |      id="metadata19">
18 |     <rdf:RDF>
19 |       <cc:Work
20 |          rdf:about="">
21 |         <dc:format>image/svg+xml</dc:format>
22 |         <dc:type
23 |            rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
24 |         <dc:title></dc:title>
25 |       </cc:Work>
26 |     </rdf:RDF>
27 |   </metadata>
28 |   <defs
29 |      id="defs17" />
30 |   <sodipodi:namedview
31 |      pagecolor="#ffffff"
32 |      bordercolor="#666666"
33 |      borderopacity="1"
34 |      objecttolerance="10"
35 |      gridtolerance="10"
36 |      guidetolerance="10"
37 |      inkscape:pageopacity="0"
38 |      inkscape:pageshadow="2"
39 |      inkscape:window-width="1920"
40 |      inkscape:window-height="1017"
41 |      id="namedview15"
42 |      showgrid="false"
43 |      showguides="true"
44 |      inkscape:guide-bbox="true"
45 |      inkscape:snap-bbox="true"
46 |      inkscape:bbox-paths="true"
47 |      inkscape:bbox-nodes="true"
48 |      inkscape:snap-bbox-edge-midpoints="true"
49 |      inkscape:snap-bbox-midpoints="true"
50 |      inkscape:object-paths="true"
51 |      inkscape:snap-intersection-paths="true"
52 |      inkscape:snap-smooth-nodes="true"
53 |      inkscape:snap-midpoints="true"
54 |      inkscape:snap-page="true"
55 |      inkscape:snap-text-baseline="true"
56 |      inkscape:snap-center="true"
57 |      inkscape:snap-object-midpoints="true"
58 |      inkscape:zoom="2.8284271"
59 |      inkscape:cx="-25.136248"
60 |      inkscape:cy="8.3060888"
61 |      inkscape:window-x="-8"
62 |      inkscape:window-y="-8"
63 |      inkscape:window-maximized="1"
64 |      inkscape:current-layer="SvgjsG2331">
65 |     <sodipodi:guide
66 |        position="-41,133"
67 |        orientation="1,0"
68 |        id="guide878"
69 |        inkscape:locked="false" />
70 |     <sodipodi:guide
71 |        position="161.75,128.75"
72 |        orientation="1,0"
73 |        id="guide880"
74 |        inkscape:locked="false" />
75 |   </sodipodi:namedview>
76 |   <g
77 |      id="SvgjsG2331"
78 |      rel="mainfill"
79 |      name="symbol"
80 |      transform="scale(1.2)"
81 |      fill="#ff0014">
82 |     <path
83 |        style="fill:#ffffff;fill-opacity:1;stroke-width:1"
84 |        d="m 53.814311,9.8925778 c -23.605,0 -42.740885,4.4292062 -42.740885,9.8942062 0,0.134 0.02032,0.267392 0.04232,0.40039 h -0.0033 L 14.07143,39.95931 c 1.459,1.119 3.610104,2.157682 6.315104,3.082682 l 5.392253,1.523438 c 7.596998,1.779 17.713125,2.86621 28.828125,2.86621 17.174,0 31.972531,-2.591125 38.769531,-6.328125 L 95.59324,26.29069 c -7.598,2.573999 -20.596956,4.270833 -35.362956,4.270833 -23.43,0 -42.423502,-4.272643 -42.423502,-9.542643 0,-5.271 18.993502,-9.542644 42.423502,-9.542643 10.436,0 19.984674,0.849232 27.374675,2.254232 C 79.783,11.397469 67.56131,9.8925778 53.814311,9.8925778 Z M 19.618347,43.365885 c -2.913274,1.254611 -4.588216,2.684158 -4.588216,4.207357 0,0.121 0.01806,0.241328 0.03906,0.361328 h -0.0033 l 2.672526,17.827149 c 11.572677,5.876795 25.602332,7.74082 36.608072,7.79297 15.511,0 28.877021,-3.391067 35.013021,-6.76107 l 2.003581,-14.93815 c -6.861,2.321 -18.601849,5.43457 -31.936849,5.43457 -21.160999,0 -38.315429,-3.851516 -38.315429,-8.603516 8.4e-4,-1.255599 1.2087,-2.44629 3.35612,-3.520508 z m 4.217123,25.652669 c 0,0 -5.091146,2.018385 -5.638021,4.969076 0.810663,5.012957 1.732167,10.202935 2.475589,14.5166 4.674,3.578 17.887052,8.269859 33.470052,8.269859 14.18,0 25.871118,-3.191159 31.481119,-6.271159 l 2.358399,-13.79395 c -6.273005,2.122 -20.172909,6.160484 -32.364909,6.160484 -19.345,0 -31.861979,-3.522209 -31.861979,-7.866209 0,-1.453675 1.879066,-2.971808 4.944662,-4.140625 -1.645186,-0.55165 -3.266366,-1.170185 -4.864912,-1.844076 z"
85 |        id="path10"
86 |        inkscape:connector-curvature="0"
87 |        sodipodi:nodetypes="sscccccsccssscscsccccccsccccccsccsscc" />
88 |   </g>
89 | </svg>
90 | 


--------------------------------------------------------------------------------
/website/static/img/redux-orm.svg:
--------------------------------------------------------------------------------
 1 | <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 2 | <svg
 3 |    xmlns:dc="http://purl.org/dc/elements/1.1/"
 4 |    xmlns:cc="http://creativecommons.org/ns#"
 5 |    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
 6 |    xmlns:svg="http://www.w3.org/2000/svg"
 7 |    xmlns="http://www.w3.org/2000/svg"
 8 |    xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
 9 |    xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
10 |    version="1.1"
11 |    height="128.0"
12 |    width="128.0"
13 |    id="svg13"
14 |    sodipodi:docname="redux-orm2.svg"
15 |    inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
16 |   <metadata
17 |      id="metadata19">
18 |     <rdf:RDF>
19 |       <cc:Work
20 |          rdf:about="">
21 |         <dc:format>image/svg+xml</dc:format>
22 |         <dc:type
23 |            rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
24 |         <dc:title></dc:title>
25 |       </cc:Work>
26 |     </rdf:RDF>
27 |   </metadata>
28 |   <defs
29 |      id="defs17" />
30 |   <sodipodi:namedview
31 |      pagecolor="#ffffff"
32 |      bordercolor="#666666"
33 |      borderopacity="1"
34 |      objecttolerance="10"
35 |      gridtolerance="10"
36 |      guidetolerance="10"
37 |      inkscape:pageopacity="0"
38 |      inkscape:pageshadow="2"
39 |      inkscape:window-width="1920"
40 |      inkscape:window-height="1017"
41 |      id="namedview15"
42 |      showgrid="false"
43 |      showguides="true"
44 |      inkscape:guide-bbox="true"
45 |      inkscape:snap-bbox="true"
46 |      inkscape:bbox-paths="true"
47 |      inkscape:bbox-nodes="true"
48 |      inkscape:snap-bbox-edge-midpoints="true"
49 |      inkscape:snap-bbox-midpoints="true"
50 |      inkscape:object-paths="true"
51 |      inkscape:snap-intersection-paths="true"
52 |      inkscape:snap-smooth-nodes="true"
53 |      inkscape:snap-midpoints="true"
54 |      inkscape:snap-page="true"
55 |      inkscape:snap-text-baseline="true"
56 |      inkscape:snap-center="true"
57 |      inkscape:snap-object-midpoints="true"
58 |      inkscape:zoom="2.8284271"
59 |      inkscape:cx="-25.136248"
60 |      inkscape:cy="8.3060888"
61 |      inkscape:window-x="-8"
62 |      inkscape:window-y="-8"
63 |      inkscape:window-maximized="1"
64 |      inkscape:current-layer="SvgjsG2331">
65 |     <sodipodi:guide
66 |        position="-41,133"
67 |        orientation="1,0"
68 |        id="guide878"
69 |        inkscape:locked="false" />
70 |     <sodipodi:guide
71 |        position="161.75,128.75"
72 |        orientation="1,0"
73 |        id="guide880"
74 |        inkscape:locked="false" />
75 |   </sodipodi:namedview>
76 |   <g
77 |      id="SvgjsG2331"
78 |      rel="mainfill"
79 |      name="symbol"
80 |      transform="scale(1.2)"
81 |      fill="#ff0014">
82 |     <path
83 |        style="fill:#bc001c;fill-opacity:1;stroke-width:1"
84 |        d="m 53.814311,9.8925778 c -23.605,0 -42.740885,4.4292062 -42.740885,9.8942062 0,0.134 0.02032,0.267392 0.04232,0.40039 h -0.0033 L 14.07143,39.95931 c 1.459,1.119 3.610104,2.157682 6.315104,3.082682 l 5.392253,1.523438 c 7.596998,1.779 17.713125,2.86621 28.828125,2.86621 17.174,0 31.972531,-2.591125 38.769531,-6.328125 L 95.59324,26.29069 c -7.598,2.573999 -20.596956,4.270833 -35.362956,4.270833 -23.43,0 -42.423502,-4.272643 -42.423502,-9.542643 0,-5.271 18.993502,-9.542644 42.423502,-9.542643 10.436,0 19.984674,0.849232 27.374675,2.254232 C 79.783,11.397469 67.56131,9.8925778 53.814311,9.8925778 Z M 19.618347,43.365885 c -2.913274,1.254611 -4.588216,2.684158 -4.588216,4.207357 0,0.121 0.01806,0.241328 0.03906,0.361328 h -0.0033 l 2.672526,17.827149 c 11.572677,5.876795 25.602332,7.74082 36.608072,7.79297 15.511,0 28.877021,-3.391067 35.013021,-6.76107 l 2.003581,-14.93815 c -6.861,2.321 -18.601849,5.43457 -31.936849,5.43457 -21.160999,0 -38.315429,-3.851516 -38.315429,-8.603516 8.4e-4,-1.255599 1.2087,-2.44629 3.35612,-3.520508 z m 4.217123,25.652669 c 0,0 -5.091146,2.018385 -5.638021,4.969076 0.810663,5.012957 1.732167,10.202935 2.475589,14.5166 4.674,3.578 17.887052,8.269859 33.470052,8.269859 14.18,0 25.871118,-3.191159 31.481119,-6.271159 l 2.358399,-13.79395 c -6.273005,2.122 -20.172909,6.160484 -32.364909,6.160484 -19.345,0 -31.861979,-3.522209 -31.861979,-7.866209 0,-1.453675 1.879066,-2.971808 4.944662,-4.140625 -1.645186,-0.55165 -3.266366,-1.170185 -4.864912,-1.844076 z"
85 |        id="path10"
86 |        inkscape:connector-curvature="0"
87 |        sodipodi:nodetypes="sscccccsccssscscsccccccsccccccsccsscc" />
88 |   </g>
89 | </svg>
90 | 


--------------------------------------------------------------------------------
/website/static/img/safe.svg:
--------------------------------------------------------------------------------
1 | <svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="shield-alt" class="svg-inline--fa fa-shield-alt fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M466.5 83.7l-192-80a48.15 48.15 0 0 0-36.9 0l-192 80C27.7 91.1 16 108.6 16 128c0 198.5 114.5 335.7 221.5 380.3 11.8 4.9 25.1 4.9 36.9 0C360.1 472.6 496 349.3 496 128c0-19.4-11.7-36.9-29.5-44.3zM256.1 446.3l-.1-381 175.9 73.3c-3.3 151.4-82.1 261.1-175.8 307.7z"></path></svg>


--------------------------------------------------------------------------------
/website/static/img/stable.svg:
--------------------------------------------------------------------------------
1 | <svg aria-hidden="true" focusable="false" data-prefix="far" data-icon="check-circle" class="svg-inline--fa fa-check-circle fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M256 8C119.033 8 8 119.033 8 256s111.033 248 248 248 248-111.033 248-248S392.967 8 256 8zm0 48c110.532 0 200 89.451 200 200 0 110.532-89.451 200-200 200-110.532 0-200-89.451-200-200 0-110.532 89.451-200 200-200m140.204 130.267l-22.536-22.718c-4.667-4.705-12.265-4.736-16.97-.068L215.346 303.697l-59.792-60.277c-4.667-4.705-12.265-4.736-16.97-.069l-22.719 22.536c-4.705 4.667-4.736 12.265-.068 16.971l90.781 91.516c4.667 4.705 12.265 4.736 16.97.068l172.589-171.204c4.704-4.668 4.734-12.266.067-16.971z"></path></svg>


--------------------------------------------------------------------------------
/website/static/scripts/code-block-buttons.js:
--------------------------------------------------------------------------------
 1 | // Turn off ESLint for this file because it's sent down to users as-is.
 2 | /* eslint-disable */
 3 | window.addEventListener('load', function() {
 4 |   function button(label, ariaLabel, icon, className) {
 5 |     const btn = document.createElement('button');
 6 |     btn.classList.add('btnIcon', className);
 7 |     btn.setAttribute('type', 'button');
 8 |     btn.setAttribute('aria-label', ariaLabel);
 9 |     btn.innerHTML =
10 |         '<div class="btnIcon__body">' +
11 |         icon +
12 |         '<strong class="btnIcon__label">' +
13 |         label +
14 |         '</strong>' +
15 |         '</div>';
16 |     return btn;
17 |   }
18 | 
19 |   function addButtons(codeBlockSelector, btn) {
20 |     document.querySelectorAll(codeBlockSelector).forEach(function(code) {
21 |       code.parentNode.appendChild(btn.cloneNode(true));
22 |     });
23 |   }
24 | 
25 |   const copyIcon =
26 |             '<svg width="12" height="12" viewBox="340 364 14 15" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M342 375.974h4v.998h-4v-.998zm5-5.987h-5v.998h5v-.998zm2 2.994v-1.995l-3 2.993 3 2.994v-1.996h5v-1.995h-5zm-4.5-.997H342v.998h2.5v-.997zm-2.5 2.993h2.5v-.998H342v.998zm9 .998h1v1.996c-.016.28-.11.514-.297.702-.187.187-.422.28-.703.296h-10c-.547 0-1-.452-1-.998v-10.976c0-.546.453-.998 1-.998h3c0-1.107.89-1.996 2-1.996 1.11 0 2 .89 2 1.996h3c.547 0 1 .452 1 .998v4.99h-1v-2.995h-10v8.98h10v-1.996zm-9-7.983h8c0-.544-.453-.996-1-.996h-1c-.547 0-1-.453-1-.998 0-.546-.453-.998-1-.998-.547 0-1 .452-1 .998 0 .545-.453.998-1 .998h-1c-.547 0-1 .452-1 .997z" fill-rule="evenodd"/></svg>';
27 | 
28 |   addButtons(
29 |       '.hljs',
30 |       button('Copy', 'Copy code to clipboard', copyIcon, 'btnClipboard'),
31 |   );
32 | 
33 |   const clipboard = new ClipboardJS('.btnClipboard', {
34 |     target: function(trigger) {
35 |       return trigger.parentNode.querySelector('code');
36 |     },
37 |   });
38 | 
39 |   clipboard.on('success', function(event) {
40 |     event.clearSelection();
41 |     const textEl = event.trigger.querySelector('.btnIcon__label');
42 |     textEl.textContent = 'Copied';
43 |     setTimeout(function() {
44 |       textEl.textContent = 'Copy';
45 |     }, 2000);
46 |   });
47 | });
48 | 


--------------------------------------------------------------------------------
/website/static/scripts/codeblock.js:
--------------------------------------------------------------------------------
 1 | window.addEventListener('load', function() {
 2 |   function button(label, ariaLabel, icon, className) {
 3 |     const btn = document.createElement('button');
 4 |     btn.classList.add('btnIcon', className);
 5 |     btn.setAttribute('type', 'button');
 6 |     btn.setAttribute('aria-label', ariaLabel);
 7 |     btn.innerHTML =
 8 |       '<div class="btnIcon__body">' +
 9 |       icon +
10 |       '<strong class="btnIcon__label">' +
11 |       label +
12 |       '</strong>' +
13 |       '</div>';
14 |     return btn;
15 |   }
16 | 
17 |   function addButtons(codeBlockSelector, btn) {
18 |     document.querySelectorAll(codeBlockSelector).forEach(function(code) {
19 |       code.parentNode.appendChild(btn.cloneNode(true));
20 |     });
21 |   }
22 | 
23 |   const copyIcon =
24 |     '<svg width="12" height="12" viewBox="340 364 14 15" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M342 375.974h4v.998h-4v-.998zm5-5.987h-5v.998h5v-.998zm2 2.994v-1.995l-3 2.993 3 2.994v-1.996h5v-1.995h-5zm-4.5-.997H342v.998h2.5v-.997zm-2.5 2.993h2.5v-.998H342v.998zm9 .998h1v1.996c-.016.28-.11.514-.297.702-.187.187-.422.28-.703.296h-10c-.547 0-1-.452-1-.998v-10.976c0-.546.453-.998 1-.998h3c0-1.107.89-1.996 2-1.996 1.11 0 2 .89 2 1.996h3c.547 0 1 .452 1 .998v4.99h-1v-2.995h-10v8.98h10v-1.996zm-9-7.983h8c0-.544-.453-.996-1-.996h-1c-.547 0-1-.453-1-.998 0-.546-.453-.998-1-.998-.547 0-1 .452-1 .998 0 .545-.453.998-1 .998h-1c-.547 0-1 .452-1 .997z" fill-rule="evenodd"/></svg>';
25 | 
26 |   addButtons(
27 |     '.hljs',
28 |     button('Copy', 'Copy code to clipboard', copyIcon, 'btnClipboard'),
29 |   );
30 | 
31 |   const clipboard = new ClipboardJS('.btnClipboard', {
32 |     target: function(trigger) {
33 |       return trigger.parentNode.querySelector('code');
34 |     },
35 |   });
36 | 
37 |   clipboard.on('success', function(event) {
38 |     event.clearSelection();
39 |     const textEl = event.trigger.querySelector('.btnIcon__label');
40 |     textEl.textContent = 'Copied';
41 |     setTimeout(function() {
42 |       textEl.textContent = 'Copy';
43 |     }, 2000);
44 |   });
45 | });


--------------------------------------------------------------------------------
/website/static/scripts/sidebarScroll.js:
--------------------------------------------------------------------------------
 1 | document.addEventListener('DOMContentLoaded', () => {
 2 |   // Find the active nav item in the sidebar
 3 |   const item = document.getElementsByClassName('navListItemActive')[0];
 4 |   if (!item) { return; }
 5 |   const bounding = item.getBoundingClientRect();
 6 |   if (
 7 |     bounding.top >= 0 &&
 8 |     bounding.bottom <= (window.innerHeight || document.documentElement.clientHeight)
 9 |   ) {
10 |     // Already visible.  Do nothing.
11 |   } else {
12 |     // Not visible.  Scroll sidebar.
13 |     item.scrollIntoView({block: 'center', inline: 'nearest'});
14 |     document.body.scrollTop = document.documentElement.scrollTop = 0;
15 |   }
16 | });
17 | 


--------------------------------------------------------------------------------