From cd43865bd464cc85c871f32000d09837d47e2d70 Mon Sep 17 00:00:00 2001 From: Maxim Dymchenko Date: Mon, 26 Aug 2024 10:24:02 -0400 Subject: [PATCH] add md by npx ai-digest --- codebase.md | 48659 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48659 insertions(+) create mode 100644 codebase.md diff --git a/codebase.md b/codebase.md new file mode 100644 index 000000000..2ffd1d2ae --- /dev/null +++ b/codebase.md @@ -0,0 +1,48659 @@ +# values.secret.json + +This is a binary file of the type: Binary + +# values-stage.yaml + +This is a binary file of the type: Binary + +# values-prod.yaml + +This is a binary file of the type: Binary + +# values-dev.yaml + +This is a binary file of the type: Binary + +# tsconfig.json + +```json +{ + "compilerOptions": { + "target": "es2017", + "module": "commonjs", + "moduleResolution": "node", + "jsx": "react", + "declaration": true, + "experimentalDecorators": true, + "preserveConstEnums": true, + "sourceMap": true, + "noImplicitAny": false, + "allowSyntheticDefaultImports": false, + "pretty": true, + "removeComments": false, + "lib": [ + "es2017", + "dom", + "esnext.asynciterable" + ], + "types": [ + "@types/node", + "@types/jest" + ] + }, + "include": [ + "./typings/*.d.ts" + ] +} +``` + +# transform.js + +```js +const config = { + babelrc: false, + presets: ["@babel/preset-env", "@babel/preset-react", + // ["@babel/preset-typescript", { isTSX: true, allExtensions: true }] + ], + plugins: [ + ["@babel/plugin-proposal-class-properties", { loose: true }], + "@babel/plugin-transform-runtime", + "@babel/plugin-syntax-dynamic-import", + "@babel/plugin-proposal-object-rest-spread", + ], + }; + module.exports = require("babel-jest").createTransformer(config); + +``` + +# prettier.config.js + +```js +module.exports = { + bracketSpacing: true, + printWidth: 120, + singleQuote: true, + tabWidth: 4, + trailingComma: 'all', + endOfLine: 'lf', +}; + +``` + +# package.json + +```json +{ + "name": "sample-stack", + "version": "0.9.12", + "private": true, + "homepage": "https://github.com/cdmbase/fullstack-pro#readme", + "bugs": { + "url": "https://github.com/cdmbase/fullstack-pro/issues" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/cdmbase/fullstack-pro.git" + }, + "license": "MIT", + "author": { + "name": "CDMBase LLC", + "email": "none@cdmbase.com" + }, + "workspaces": { + "packages": [ + "portable-devices/*", + "packages-modules/**", + "packages/**", + "servers/*" + ] + }, + "scripts": { + "prebootstrap": "lerna run prepare", + "bootstrap": "yarn && yarn lerna", + "postbootstrap": "yarn build", + "build": "yarn build:packages", + "build:clean": "lerna exec yarn build:clean", + "build:packages": "lerna run build --ignore *server --ignore *device --ignore *browser-extension", + "build:packages:watch": "lerna run build:lib:watch --ignore *server --ignore *device --ignore *browser-extension --stream", + "check-updates": "lerna exec ./node_modules/.bin/npm-check-updates -- -u", + "clean": "lerna clean --yes && yarn build:clean && rimraf node_modules", + "clean:force": "rimraf package-lock.json yarn.lock && yarn clean", + "clean:install": "yarn clean:force && yarn git:pull && yarn bootstrap", + "cli": "node tools/cli", + "commit": "git cz", + "compile": "lerna exec -- yarn compile", + "coverage": "jest --coverage", + "postcoverage": "remap-istanbul --input coverage/coverage.raw.json --type lcovonly --output coverage/lcov.info", + "db:migrate": "knex migrate:latest --cwd . --knexfile ./servers/backend-server/knexfile.js", + "db:migrate:rollback": "knex migrate:rollback --cwd . --knexfile ./servers/backend-server/knexfile.js", + "db:seed": "yarn db:migrate && knex seed:run --cwd . --knexfile ./servers/backend-server/knexfile.js", + "predevpublish": "git checkout devpublish && git pull origin devpublish && git merge -s recursive -X theirs develop -m 'merge from develop' && yarn gitcommit && node tools/update-dependency-version.js && yarn gitcommit", + "devpublish": "lerna publish prerelease --ignore-scripts --exact", + "postdevpublish": "git checkout develop", + "devpublish:auto": "yarn devpublish -- --yes", + "devpublish:force": "yarn devpublish:forceManual -- --yes", + "devpublish:forceManual": "yarn devpublish -- --force-publish=*", + "devpublish:push": "yarn predevpublish && git push origin devpublish && yarn postdevpublish", + "format": "yarn lint --fix", + "format:md": "yarn lint:md --fix", + "generateGraphql": "graphql-codegen", + "generateGraphql:watch": "yarn generateGraphql -- --watch", + "git:pull": "git pull origin $(git rev-parse --abbrev-ref HEAD)", + "gitcommit": "git add -A && git diff --staged --quiet || git commit -am 'auto publish [skip ci] \r\n'", + "husky-skip": "cross-env HUSKY_SKIP_HOOKS=1", + "jest": "./node_modules/.bin/jest", + "lerna": "lerna bootstrap", + "prelernapublish": "git checkout publish && git pull origin publish && git merge -s recursive -X theirs master -m 'merge from master' && yarn gitcommit && node tools/update-dependency-version.js && yarn gitcommit", + "lernapublish": "lerna publish --ignore-scripts --cd-version=patch", + "postlernapublish": "git checkout master", + "lint": "eslint --ext js --ext ts --ext md", + "lint:ci": "yarn lint . --format junit", + "lint:fix": "yarn lint -- --fix", + "lint:md": "markdownlint", + "lintx": "yarn lint ./packages/**/src/**/*.ts ./packages-modules/**/src/**/*.ts", + "prodBuild": "cross-env NODE_ENV=production babel-node --presets es2015 tools/webpack.run", + "publish": "yarn lernapublish", + "publish:auto": "yarn lernapublish --yes", + "publish:force": "yarn publish:forceManual --yes", + "publish:forceManual": "yarn lernapublish --force-publish=*", + "publish:push": "yarn prelernapublish && git push origin publish && yarn postlernapublish", + "start": "cross-env NODE_ENV=development ENV_FILE=../../config/development/dev.env yarn zen:watch", + "start:envSSR": "cross-env SSR=true NODE_ENV=development ENV_FILE=../../config/development/dev.env yarn zen:watch", + "start:test": "cross-env NODE_ENV=test ENV_FILE=../../config/test/test.env zen watch", + "test": "cross-env ENV_FILE=config/test/test.env jest", + "posttest": "yarn lint", + "test:watch": "npm test -- --watch", + "pretravis": "yarn compile", + "travis": "istanbul cover -x \"*.test.js\" _mocha -- --timeout 5000 --full-trace ./test/tests.js", + "posttravis": "yarn lint", + "watch": "lerna exec --no-sort --ignore *server --ignore *device --ignore *browser-extension --stream --parallel -- webpack --watch", + "watch-packages": "lerna exec --no-sort --scope @sample-stack/platform* --scope @sample-stack/react-shared-components --scope @sample-stack/core --stream --parallel 'webpack --watch'", + "zen:build": "cross-env NODE_ENV=production zen build", + "zen:exp": "zen exp", + "zen:watch": "zen watch -x", + "zen:watch:debug": "yarn zen:watch -- -v" + }, + "husky": { + "hooks": { + "pre-commit": "lint-staged", + "commit-msg": "commitlint -E HUSKY_GIT_PARAMS" + } + }, + "lint-staged": { + "*.md": [ + "yarn format:md", + "git add" + ], + "*.{js,jsx,ts,tsx}": [ + "eslint --fix", + "git add" + ] + }, + "resolutions": { + "@apollo/client": "~3.3.21", + "chokidar": "^3.4.0", + "react": "17.0.1", + "react-dom": "17.0.1" + }, + "dependencies": { + "dataloader": "^2.0.0", + "graphql": "^14.7.0", + "graphql-tag": "^2.11.0" + }, + "devDependencies": { + "@babel/cli": "^7.7.7", + "@babel/core": "^7.7.7", + "@babel/plugin-proposal-class-properties": "^7.7.4", + "@babel/plugin-proposal-decorators": "^7.7.4", + "@babel/plugin-proposal-export-default-from": "^7.7.4", + "@babel/plugin-proposal-export-namespace-from": "^7.7.4", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.7.4", + "@babel/plugin-proposal-object-rest-spread": "^7.7.7", + "@babel/plugin-proposal-optional-chaining": "^7.7.5", + "@babel/plugin-proposal-pipeline-operator": "^7.7.7", + "@babel/plugin-syntax-dynamic-import": "^7.7.4", + "@babel/plugin-syntax-export-default-from": "^7.7.4", + "@babel/plugin-syntax-export-namespace-from": "^7.7.4", + "@babel/plugin-syntax-import-meta": "^7.7.4", + "@babel/plugin-syntax-numeric-separator": "^7.7.4", + "@babel/plugin-transform-destructuring": "^7.7.4", + "@babel/plugin-transform-for-of": "^7.13.0", + "@babel/plugin-transform-modules-commonjs": "^7.7.5", + "@babel/plugin-transform-regenerator": "^7.7.5", + "@babel/plugin-transform-runtime": "^7.7.6", + "@babel/polyfill": "7.7.0", + "@babel/preset-env": "^7.7.7", + "@babel/preset-flow": "^7.7.4", + "@babel/preset-react": "^7.7.4", + "@babel/preset-typescript": "^7.7.7", + "@babel/register": "^7.7.7", + "@babel/runtime": "^7.7.7", + "@graphql-codegen/add": "^2.0.2", + "@graphql-codegen/cli": "^1.21.8", + "@graphql-codegen/fragment-matcher": "^2.0.1", + "@graphql-codegen/import-types-preset": "^1.18.6", + "@graphql-codegen/near-operation-file-preset": "^1.18.6", + "@graphql-codegen/typescript": "^1.23.0", + "@graphql-codegen/typescript-graphql-files-modules": "^1.18.1", + "@graphql-codegen/typescript-operations": "^1.18.4", + "@graphql-codegen/typescript-react-apollo": "^2.3.1", + "@graphql-codegen/typescript-resolvers": "^1.20.0", + "@hot-loader/react-dom": "^17.0.0", + "@larix/zen": "0.1.37", + "@open-wc/building-rollup": "^1.10.0", + "@redux-devtools/core": "^3.9.0", + "@redux-devtools/dock-monitor": "^1.4.0", + "@redux-devtools/log-monitor": "^2.3.0", + "@rollup/plugin-graphql": "1.0.0", + "@rollup/plugin-image": "^2.0.6", + "@rollup/plugin-typescript": "^6.1.0", + "@shelf/jest-mongodb": "^1.1.5", + "@types/async": "^3.0.3", + "@types/body-parser": "1.17.1", + "@types/bunyan": "^1.8.6", + "@types/classnames": "^2.2.9", + "@types/cors": "2.8.6", + "@types/enzyme": "^3.10.4", + "@types/express": "^4.17.2", + "@types/hoist-non-react-statics": "^3.3.1", + "@types/ioredis": "^4.14.4", + "@types/isomorphic-fetch": "0.0.35", + "@types/jest": "^26.0.24", + "@types/lodash-es": "^4.17.3", + "@types/minimist": "^1.2.0", + "@types/node": "13.1.0", + "@types/prop-types": "^15.7.3", + "@types/react": "^17.0.35", + "@types/react-dom": "^17.0.11", + "@types/react-helmet": "^5.0.14", + "@types/react-loadable": "^5.5.3", + "@types/react-redux": "^7.1.5", + "@types/react-router": "^5.1.3", + "@types/react-router-config": "^5.0.2", + "@types/react-router-dom": "^5.1.3", + "@types/react-test-renderer": "^17.0.1", + "@types/redux-logger": "^3.0.9", + "@types/semver": "^6.2.0", + "@types/sinon": "^7.5.1", + "@types/webpack": "^4.41.0", + "@types/webpack-env": "^1.14.1", + "@types/zen-observable": "^0.8.0", + "@typescript-eslint/eslint-plugin": "^4.20.0", + "@typescript-eslint/eslint-plugin-tslint": "^4.20.0", + "@typescript-eslint/parser": "^4.20.0", + "apollo": "^2.21.2", + "apollo-server-testing": "^2.21.1", + "autoprefixer": "^9.7.3", + "awesome-typescript-loader": "^5.2.1", + "babel-core": "^7.0.0-bridge.0", + "babel-eslint": "^10.0.3", + "babel-jest": "^26.3.0", + "babel-loader": "^8.0.6", + "babel-plugin-import": "^1.13.3", + "caporal": "^1.3.0", + "chokidar": "^3.4.0", + "clean-webpack-plugin": "^2.0.0", + "commitizen": "^4.2.3", + "concurrently": "^5.0.2", + "connect": "^3.7.0", + "copy-webpack-plugin": "^6.4.0", + "cross-env": "^6.0.3", + "css-loader": "^3.4.0", + "csstype": "^2.5.5", + "dotenv-safe": "^8.2.0", + "dotenv-webpack": "^6.0.0", + "envalid": "^7.2.2", + "enzyme": "^3.11.0", + "enzyme-adapter-react-16": "^1.15.2", + "eslint": "^7.23.0", + "eslint-config-airbnb-typescript": "^12.3.1", + "eslint-config-prettier": "^8.1.0", + "eslint-loader": "^4.0.2", + "eslint-plugin-graphql": "^4.0.0", + "eslint-plugin-import": "^2.22.1", + "eslint-plugin-jest": "^24.3.3", + "eslint-plugin-jsdoc": "^32.3.0", + "eslint-plugin-jsx-a11y": "^6.4.1", + "eslint-plugin-markdown": "^2.0.1", + "eslint-plugin-no-null": "^1.0.2", + "eslint-plugin-prettier": "^3.3.1", + "eslint-plugin-react": "^7.23.1", + "eslint-plugin-react-hooks": "^4.2.0", + "eslint-plugin-simple-import-sort": "^7.0.0", + "express": "^4.17.1", + "extract-text-webpack-plugin": "^4.0.0-beta.0", + "file-loader": "^5.0.2", + "freeport-async": "^2.0.0", + "fs-extra": "^8.1.0", + "html-loader": "^0.5.5", + "html-webpack-plugin": "^3.2.0", + "http-proxy-middleware": "^0.20.0", + "husky": "^5.0.9", + "ignore-loader": "^0.1.2", + "image-size": "^0.8.3", + "ip": "^1.1.5", + "isomorphic-style-loader": "^5.1.0", + "istanbul": "1.0.0-alpha.2", + "jest": "^26.0.0", + "jest-css-modules-transform": "^3.1.0", + "jest-dom": "^4.0.0", + "jest-junit": "^12.2.0", + "jest-matcher-utils": "^26.0.0", + "jest-raw-loader": "^1.0.1", + "jest-transform-graphql": "^2.1.0", + "jsdom": "^15.2.1", + "lerna": "^4.0.0", + "lint-staged": "^9.5.0", + "lodash": "^4.17.15", + "lodash-es": "^4.17.15", + "lodash-webpack-plugin": "^0.11.5", + "markdownlint-cli": "^0.27.0", + "merge": "^1.2.1", + "mime": "^2.4.4", + "mini-css-extract-plugin": "^0.9.0", + "minilog": "^3.1.0", + "mkdirp": "^0.5.1", + "mocha": "^6.2.2", + "mocha-steps": "^1.3.0", + "module": "^1.2.5", + "morgan": "^1.9.1", + "nock": "^11.7.0", + "node-dev": "^4.0.0", + "node-sass": "^4.13.0", + "nodemon": "^2.0.2", + "openurl": "^1.1.1", + "pm2": "^4.2.1", + "postcss-loader": "^3.0.0", + "prettier": "^2.2.1", + "qrcode-terminal": "^0.12.0", + "raw-loader": "^4.0.0", + "react-addons-test-utils": "^16.0.0-alpha.3", + "react-hot-loader": "^4.12.18", + "react-test-renderer": "^16.12.0", + "redux-devtools-extension": "^2.13.8", + "redux-mock-store": "^1.5.4", + "remap-istanbul": "^0.13.0", + "resolve-url-loader": "^3.1.1", + "rimraf": "^3.0.0", + "rollup": "^2.53.2", + "rollup-plugin-string": "^3.0.0", + "sass-loader": "^8.0.0", + "shelljs": "^0.8.3", + "simple-git": "^2.42.0", + "sinon": "^8.0.1", + "source-list-map": "^2.0.1", + "source-map-loader": "^0.2.4", + "source-map-support": "^0.5.16", + "standard-version": "^7.0.1", + "style-loader": "^1.1.1", + "svg-url-loader": "^3.0.3", + "tcomb": "^3.2.29", + "ts-jest": "^26.0.0", + "ts-loader": "^6.2.1", + "ts-node": "^8.5.4", + "tslib": "^1.10.0", + "tslint": "^6.1.3", + "typedoc": "^0.15.5", + "typescript": "4.2.3", + "uglify-es": "^3.3.9", + "uglifyjs-webpack-plugin": "^2.2.0", + "url-loader": "^3.0.0", + "wait-on": "^4.0.0", + "webpack": "4.46.0", + "webpack-bundle-analyzer": "^4.5.0", + "webpack-cli": "^3.3.10", + "webpack-dev-middleware": "^4.1.0", + "webpack-dev-server": "^3.11.2", + "webpack-hot-middleware": "^2.25.0", + "webpack-manifest-plugin": "^2.2.0", + "webpack-merge": "^5.4.0", + "webpack-node-externals": "^3.0.0", + "webpack-sources": "^1.4.3", + "webpack-virtual-modules": "^0.4.3", + "ws": "^7.2.1" + }, + "engines": { + "node": ">=12.18.4 < 15.0.0", + "yarn": ">=1.22" + }, + "cacheDirectories": [ + ".cache" + ], + "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" +} + +``` + +# lint-staged.config.js + +```js +module.exports = { + '*.{js,jsx,ts,tsx,json,md}': ['prettier --write', 'git add'], + // '*.{ts,tsx}': ['eslint --fix'], // this can be tested +}; +``` + +# lerna.json + +```json +{ + "changelog": { + "repo": "cdmbase/lerna-bootstrap", + "labels": { + "tag: breaking change": ":boom: Breaking Change", + "tag: new feature": ":rocket: New Feature", + "tag: bug fix": ":bug: Bug Fix", + "tag: polish": ":nail_care: Polish", + "tag: documentation": "Documentation", + "tag: internal": ":house: Internal" + } + }, + "command": { + "publish": { + "registry": "https://registry.npmjs.org", + "graphType": "all", + "allowBranch": [ + "publish", + "devpublish" + ], + "message": "chore(release): publish", + "ignoreChanges": [ + "**/__fixtures__/**", + "**/__tests__/**", + "**/*.md", + "**/example/**" + ] + }, + "version": { + "allowBranch": [ + "master", + "develop", + "publish", + "devpublish" + ], + "conventionalCommits": true, + "message": "chore: release package(s)" + } + }, + "npmClient": "yarn", + "useWorkspaces": true, + "packages": [ + "packages-modules/**", + "packages/**", + "servers/*", + "portable-devices/*" + ], + "version": "0.0.0" +} +``` + +# jest.config.mongodb.js + +```js +module.exports = { + preset: '@shelf/jest-mongodb', +}; + +``` + +# jest.config.js + +```js +const merge = require('merge') +const baseConfig = require('./jest.config.base'); +const mongodbConfig = require('./jest.config.mongodb') +module.exports = merge.recursive( + baseConfig, + mongodbConfig, + { + globals: { + + } + }, + // https://baltuta.eu/posts/typescript-lerna-monorepo-more-tools + // { + // roots: [''], + // projects: [ + // '/packages/ui', + // '/packages/api', + // '/packages/diceroll' + // ], + // } + +); +``` + +# jest.config.base.js + +```js +/* eslint-disable import/no-extraneous-dependencies */ +/* eslint-disable @typescript-eslint/no-var-requires */ +const { defaults } = require('jest-config'); + +module.exports = { + testEnvironment: 'node', + setupFiles: [ + // needed for UI to mock canvas load + // "jest-canvas-mock" + ], + preset: 'ts-jest', + testMatch: null, + testRegex: '.*test*\\.(ts|tsx|js)$', + testPathIgnorePatterns: ['/node_modules/', '/dist/'], + transform: { + '\\.(gql)$': 'jest-transform-graphql', + '\\.(graphql|graphqls)$': 'jest-raw-loader', + '\\.(ts|tsx)$': 'ts-jest', + // Use our custom transformer only for the *.js and *.jsx files + '\\.(js|jsx)?$': './transform.js', + // future need to test with + // "^.+\\.(js|jsx|ts|tsx)$": "./transform.js", + '.+\\.(css|styl|less|sass|scss)$': 'jest-css-modules-transform', + }, + roots: ['packages', 'packages-modules', 'servers'], + moduleFileExtensions: [ + 'tsx', // TODO can be removed as default extension includes + 'ts', // TODO can be removed as default extension includes + ...defaults.moduleFileExtensions, + 'js', // TODO can be removed as default extension includes + 'jsx', // TODO can be removed as default extension includes + 'json', + 'gql', + 'graphql', + ], + moduleNameMapper: { + '^__mocks__/(.*)$': '/../../__mocks__/$1', + // we'll use commonjs version of lodash for tests 👌 + // because we don't need to use any kind of tree shaking right?! + '^lodash-es$': '/node_modules/lodash/index.js', + }, + transformIgnorePatterns: ['/node_modules/(?!(babel-runtime|antd)).*/', '/node_modules/(?!lodash-es/.*)'], + clearMocks: true, + verbose: true, + // projects: [''], // TODO need to test with it https://github.com/bryan-hunter/yarn-workspace-lerna-monorepo/blob/master/jest.config.base.js + coverageDirectory: '/coverage/', + coveragePathIgnorePatterns: ['/build/', '/lib/', '/dist/', '/node_modules/'], + globals: { + __BACKEND_URL__: 'http://localhost:3010', + __GRAPHQL_URL__: 'http://localhost:8085/graphql', + 'ts-jest': { + // tsConfig: "/src/__tests__/tsconfig.json", + // https://github.com/kulshekhar/ts-jest/issues/766 + diagnostics: { + warnOnly: true, + }, + // "skipBabel": true + }, + }, +}; + +``` + +# jest-transform-i18next.js + +```js +const fs = require('fs'); +const path = require('path'); + +module.exports = { + process() { + const pathname = arguments[1]; + const dir = path.dirname(pathname); + const locales = fs.readdirSync(dir); + const result = {}; + for (const locale of locales) { + if (fs.statSync(path.join(dir, locale)).isDirectory()) { + const localeFiles = fs.readdirSync(path.join(dir, locale)); + for (const localeFile of localeFiles) { + if (localeFile.indexOf('.json') >= 0) { + result[locale] = JSON.parse(fs.readFileSync(path.join(dir, locale, localeFile), 'utf8')); + } + } + } + } + return { + code: `module.exports = ${JSON.stringify(result)};`, + }; + }, +}; + +``` + +# jest-mongodb-config.js + +```js +module.exports = { + mongodbMemoryServerOptions: { + instance: { + dbName: 'jest' + }, + binary: { + version: '4.0.12', // Version of MongoDB + skipMD5: true + }, + autoStart: false + } + }; +``` + +# jenkins_variables.groovy + +```groovy +import groovy.json.JsonSlurper + +def getVersion(json_file_path){ + def inputFile = readFile(json_file_path) + def InputJSON = new JsonSlurper().parseText(inputFile) + def version = InputJSON.version +return version +} + +def getName(json_file_path){ + def inputFile = readFile(json_file_path) + def InputJSON = new JsonSlurper().parseText(inputFile) + def name = InputJSON.name +return name +} + +def getSecrets(json_file_path, env, var){ + def inputFile = new File(json_file_path) + def InputJSON = new JsonSlurper().parse(inputFile) + def secret = InputJSON."${env}"."${var}" +return secret +} + +``` + +# husky.config.js + +```js +module.exports = { + hooks: { + 'pre-commit': 'lint-staged', + //'pre-push': 'yarn test', // when production ready + }, +}; +``` + +# commitlint.config.js + +```js +module.exports = { + extends: ['@commitlint/config-conventional'], + rules: { + 'header-max-length': [0, 'always', 100], // corresponding to maxHeaderWidth of commitizen + }, +}; + +``` + +# combined_repo.md + +```md +# Repo as one markdown file + +This file was generated by the repo-to-one-file package, find it here: + + [GitHub](https://github.com/tonypls/repo-to-one-file-cli) + + [NPM](https://www.npmjs.com/package/repo-to-one-file) + +# Directory Structure + +\`\`\` +.browserslistrc +.eslintignore +.eslintrc.js +.markdownlint.json +.markdownlintignore +.npmrc +.travis.yml +.vscode/ + extensions.json + launch.json + settings.json +CHANGELOG.md +CODE_OF_CONDUCT.md +Dockerfile +ISSUES.md +Jenkinsfile +LICENSE +README.md +ScreenShot.png +babel.config.js +codegen.yml +commitlint.config.js +config/ + development/ + settings.json + production/ + staging/ + test/ +docs/ + Moleculer.md + References.md + development/ + CodeContribution/ + Adding_New_Modules.md + Desktop_Setup.md + DoAndDont.md + GitHooks.md + HowToContribute.md + How_to_Run_Various_Options.md + Known_Issues.md + Lint_And_Formatter.md + Project_Setup.md + React-Patterns/ + Dynamically_Render_Components.md + HOC_With_Render_Props.md + Mongoose_Connection.md + React_Componet_Extensions.md + RxJS_notes.md + Styles_With_Type.md + Tutorials.md + faq.md + installation_issues.md + lerna-yarn-workspaces.md + Database/ + mongodb-test.md + Deployment/ + How_To_Setup_Jenkins.md + JenkinsDeployment.md + Mobile/ + How_To_Make_Expo_Wrok_With_Monorepos.md + React-Native-FAQ.md + Run_mobile.md +husky.config.js +jenkins_variables.groovy +jest-mongodb-config.js +jest-transform-i18next.js +jest.config.base.js +jest.config.js +jest.config.mongodb.js +lerna.json +lint-staged.config.js +package.json +packages/ + sample-core/ + .npmignore + README.md + jest.config.js + package.json + src/ + index.ts + tsconfig.json + webpack.config.js + sample-platform/ + browser/ + jest.config.js + package.json + src/ + api.ts + components/ + Counter.tsx + NavBar.tsx + index.ts + containers/ + Clock.tsx + Counter.tsx + Loading.tsx + PersonList.tsx + ServerCounter.tsx + __tests__/ + ApolloProvider.test.tsx + Counter.test.tsx + PersonList.test.tsx + __snapshots__/ + Counter.test.tsx.snap + PersonList.test.tsx.snap + redux.test.tsx + setup.ts + utils.ts + index.ts + graphql/ + index.ts + mutations/ + addCount.graphql + addPerson.graphql + index.ts + queries/ + count.graphql + index.ts + person.graphql + persons.graphql + subscriptions/ + count.graphql + index.ts + index.ts + inversify-containers/ + index.ts + module.ts + module.ts + redux/ + __mocks__/ + api.ts + actions/ + __tests__/ + sampleActions.test.ts + index.ts + sampleActions.ts + index.ts + reducers/ + Store.ts + __tests__/ + sampleReducers.test.ts + index.ts + sampleReducers.ts + services/ + index.ts + tsconfig.json + webpack.config.js + server/ + .npmignore + README.md + jest.config.js + package.json + src/ + index.ts + tsconfig.json + webpack.config.js + sample-store/ + .npmignore + README.md + package.json + src/ + __tests__/ + counter-hemera-repository.test.ts + counter-repository.test.ts + db/ + config.json + migrations/ + counter.ts + seeds/ + counter.ts + constants/ + constants.ts + index.ts + container/ + index.ts + module.ts + database-store/ + migrations/ + counter.ts + index.ts + seeds/ + counter.ts + index.ts + db-helpers/ + abstract-repository.ts + db-config.ts + entity.ts + index.ts + repository.ts + index.ts + models/ + counter.ts + index.ts + interfaces/ + count-model.ts + index.ts + repository/ + counter-hemera-repository.ts + counter-repository.ts + index.ts + interfaces/ + counter-repository.ts + index.ts + tsconfig.json + webpack.config.js +packages-modules/ + counter/ + browser/ + jest.config.js + package.json + rollup.config.js + src/ + apollo-server-n-client/ + components/ + CounterView.tsx + compute.tsx + containers/ + Counter.tsx + generated-model.tsx + graphql/ + __tests__/ + apollo-client-test-helper.ts + apollo-client.test.ts + id-generation.ts + index.ts + mutations/ + AddCounter.client.gql + AddCounter.gql + AddCounter_WS.gql + SyncCachedCounter.gql + queries/ + CounterCacheQuery_WS.gql + CounterQuery.client.gql + CounterQuery.gql + resolvers/ + index.ts + resolvers.ts + schema/ + counter-state.graphql + subscriptions/ + CounterSubscription.gql + index.ts + module.tsx + redux/ + index.ts + reducers/ + index.ts + common/ + components/ + Dashboard.tsx + Home.tsx + compute.tsx + generated-models.ts + index.ts + interfaces/ + context.ts + module.tsx + connected-react-router/ + README.md + __tests__/ + __snapshots__/ + connected-react-router-module.test.ts.snap + connected-react-router-module.test.ts + components/ + Counter.tsx + Hello.tsx + HelloChild.tsx + Home.tsx + NavBar.tsx + NoMatch.tsx + compute.tsx + constants/ + action-types.ts + index.ts + routes-types.ts + electron-module.tsx + index.electron.ts + index.ts + interfaces/ + index.ts + state.ts + module.tsx + redux/ + actions/ + counter.ts + index.ts + index.ts + reducers/ + counter.ts + index.ts + fela/ + README.md + components/ + CompledWithTheme.tsx + ComplexComponent.tsx + index.ts + compute.tsx + index.ts + interfaces/ + index.ts + theme.ts + module.tsx + theme.ts + generated-models.ts + index.ts + utils/ + index.ts + menu.ts + tsconfig.json + typings/ + graphql.d.ts + webpack.config.js + electron/ + jest.config.js + package.json + rollup.config.js + src/ + epics/ + count-tray-updater.ts + index.ts + index.ts + tsconfig.json + typings/ + graphql.d.ts + webpack.config.js + mobile/ + jest.config.js + package.json + src/ + common/ + components/ + Dashboard.tsx + Home.tsx + compute.tsx + generated-models.ts + index.ts + interfaces/ + context.ts + module.tsx + connected-react-router/ + README.md + components/ + Counter.tsx + Hello.tsx + compute.tsx + constants/ + action-types.ts + index.ts + routes-types.ts + index.ts + interfaces/ + index.ts + state.ts + module.tsx + redux/ + actions/ + counter.ts + index.ts + index.ts + reducers/ + counter.ts + index.ts + index.ts + utils/ + index.ts + menu.ts + tsconfig.json + typings/ + graphql.d.ts + server/ + jest.config.js + package.json + src/ + config/ + config.ts + index.ts + constants/ + constants.ts + index.ts + containers/ + containers.ts + index.ts + dataloader/ + cache.ts + counter-dataloader.ts + index.ts + generated-models.ts + graphqlTypes/ + index.ts + resolvers.ts + schema.graphql + index.ts + interfaces/ + context.ts + counter-service.ts + index.ts + module.ts + resolvers/ + index.ts + resolver.ts + schema/ + schema.graphql + services/ + counter-mock-microservice.ts + counter-mock-moleculer-service.ts + counter-mock-proxy-service.ts + counter-mock-service.ts + index.ts + tsconfig.json + webpack.config.js +portable-devices/ + desktop/ + assets/ + entitlements.mac.plist + html/ + about-page.html + main-page.html + tray-page.html + icon.icns + icon.ico + icon.png + icon.svg + icons/ + 1024x1024.png + 128x128.png + 16x16.png + 24x24.png + 256x256.png + 32x32.png + 48x48.png + 512x512.png + 64x64.png + 96x96.png + icon-22.png + icon-256.png + preload.js + electron-webpack.json + jest.config.js + package.json + src/ + common/ + channel.ts + config/ + base-apollo-client.ts + base-redux-config.ts + config.ts + constants/ + index.ts + ipcEvents.ts + index.ts + utils/ + index.ts + is.ts + logger.ts + main/ + app/ + App.ts + Service.ts + View.ts + index.ts + bootstrap.ts + config/ + client.service.ts + epic-config.ts + redux-electron-config.ts + index.ts + interfaces/ + index.ts + tray-icon.ts + tray-window.ts + ioc/ + index.ts + inversify.config.ts + loader.ts + types.ts + menu-template.ts + models/ + User.ts + index.ts + modules/ + index.ts + module.ts + services/ + Logger.ts + System.ts + User.ts + index.ts + utils/ + AutoUpdater.ts + createProtocol.ts + index.ts + ioc.ts + logger/ + customLogger.ts + index.ts + logDecorator.ts + sqlite/ + connection.ts + window/ + devTools.ts + index.ts + protocol.ts + views/ + about.ts + index.ts + main.ts + tray.ts + renderer/ + about.tsx + app/ + 500.tsx + About.tsx + ErrorBoundary.tsx + Main.tsx + ServerError.tsx + Tray.tsx + components/ + WindowHeader.tsx + layout/ + components/ + SideMenu.tsx + index.ts + index.ts + config/ + config.ts + index.ts + main/ + client.service.ts + epic-config.ts + fela-renderer.ts + redux-config.ts + public-config.ts + router-history.ts + tray/ + client.service.ts + epic-config.ts + fela-renderer.ts + redux-config.ts + main.tsx + modules/ + main/ + index.ts + module.tsx + tray/ + index.ts + module.tsx + tray-main.tsx + tools/ + esm-wrapper.js + utils.js + tsconfig.json + webpack.main.additions.js + webpack.renderer.additions.js + mobile/ + .exprc + App.tsx + __generated__/ + AppEntry.js + app.json + assets/ + fonts/ + SpaceMono-Regular.ttf + images/ + adaptive-icon.png + favicon.png + icon.png + splash.png + babel.config.js + eas.json + index.js + jest.config.js + metro.config.js + package.json + src/ + App.tsx + assets/ + fonts/ + SpaceMono-Regular.ttf + images/ + adaptive-icon.png + favicon.png + icon.png + splash.png + components/ + __tests__/ + StyledText-test.js + layout/ + Drawer.tsx + Header.tsx + Layout.tsx + NativeBaseIcon.tsx + NativeBaseSample.tsx + SideBar.tsx + module.ts + root-navigation.tsx + config/ + base-apollo-client.ts + base-redux-config.ts + client.service.ts + config.ts + epic-config.ts + fela-renderer.ts + index.ts + public-config.ts + redux-config.ts + router-history.ts + constants/ + Colors.ts + Layout.ts + hooks/ + useCachedResources.ts + useColorScheme.ts + useColorScheme.web.ts + modules/ + index.ts + modules.tsx + render.tsx + pages/ + dashboard.tsx + hello.tsx + index.ts + screens/ + NotFoundScreen.tsx + tsconfig.json + types.tsx + webpack.config.js +prettier.config.js +servers/ + backend-server/ + .dockerignore + .npmignore + .npmrc + .zenrc.js + CHANGELOG.md + Dockerfile + LICENSE + README.md + generated-schema.graphql + knexfile.js + package.json + src/ + api/ + remote-config.ts + resolver.ts + root-schema.graphqls + scalar.ts + config/ + config.ts + index.ts + moleculer.config.ts + connectors/ + connection-broker.ts + graphql-pubsub-connector.ts + mongo-connector.ts + nats-connector.ts + redis-connector.ts + constants/ + ENDPOINTS.ts + index.ts + express-app.ts + index.ts + interfaces/ + index.ts + module-interface.ts + main.spec.ts + middleware/ + cors.ts + error.ts + moleculer-inter-namespace.ts + persistedQuery.ts + services.ts + modules/ + index.ts + module.ts + server-setup/ + graphql-server.ts + graphql-subscription-server.ts + websocket-multipath-update.ts + service.ts + stack-server.ts + utils/ + migrations.ts + tsconfig.json + webpack.config.lint.js + frontend-server/ + .browserslistrc + .dockerignore + .npmignore + .npmrc + .zenrc.js + Dockerfile + package.json + src/ + app/ + 500.tsx + ErrorBoundary.tsx + Main.tsx + ServerError.tsx + backend/ + app.ts + middlewares/ + cors.ts + error.ts + modules/ + index.ts + modules.ts + server.ts + ssr/ + html.tsx + website.tsx + config/ + __mocks__/ + mockFetch.ts + mockWatchQuery.ts + __tests__/ + apollo-client-subscribe-to-more.ts + base-apollo-client.ts + base-redux-config.ts + client.service.ts + config.ts + epic-config.ts + fela-renderer.ts + index.ts + public-config.ts + redux-config.ts + router-history.ts + index.tsx + modules/ + index.ts + layout/ + components/ + SideMenu.tsx + index.ts + index.ts + module.tsx + postcss.config.js + tools/ + webpackAppConfig.js + tsconfig.json + webpack.config.lint.js + moleculer-server/ + .dockerignore + .draft-tasks.toml + .draftignore + .npmrc + .vscode/ + launch.json + Dockerfile + README.md + charts/ + chart/ + .helmignore + Chart.yaml + templates/ + NOTES.txt + _helpers.tpl + deployment.yaml + ingress.yaml + mongodb-external-service.yaml + nats-external-service.yaml + redis-external-service.yaml + service.yaml + values-dev.yaml + values-prod.yaml + values-stage.yaml + values.yaml + draft.toml + package.json + src/ + config/ + config.ts + index.ts + moleculer.config.ts + connectors/ + connection-broker.ts + graphql-pubsub-connector.ts + mongo-connector.ts + nats-connector.ts + redis-connector.ts + index.ts + modules/ + index.ts + module.ts + stack-server.ts + test/ + unit/ + greeter.spec.ts + tsconfig.json + webpack.config.js +tools/ + .eslintrc + cli/ + command-invoker.js + commands/ + add-module.js + delete-module.js + config.js + helpers/ + util.js + cli.js + esm-wrapper.js + get-symlinked-modules.js + html-plugin-template.ejs + templates/ + module/ + browser/ + package.json + src/ + constants/ + constants.ts + index.ts + graphql/ + index.ts + link/ + index.ts + mutations/ + index.ts + queries/ + index.ts + module-query.gql + schema.ts + index.tsx + locales/ + en/ + translations.json + index.ts + ru/ + translations.json + module.tsx + selectors/ + index.ts + utils/ + index.ts + tsconfig.json + webpack.config.js + server/ + package.json + src/ + config/ + index.ts + constants/ + index.ts + types.ts + containers/ + index.ts + module.ts + index.ts + interfaces/ + index.ts + module.ts + plugin/ + index.ts + services/ + index.ts + tsconfig.json + webpack.config.js + update-dependency-version.js + webpack-util.js + webpackAppConfig.js +transform.js +tsconfig.json +typings/ + graphql.d.ts + index.d.ts +values-dev.yaml +values-prod.yaml +values-stage.yaml +values.secret.json +\`\`\` + +## README.md + +\`\`\`md +# Full Stack Packages + +*Fullstack packages to develop and test end to end; to use as packages or work independently.* + +Purpose: +--- +The idea is to create modules for each package so it can work independently as well as integrated to another project as packages. + +## Screenshot +![screencast](./ScreenShot.png) + + +Useful commands: +--- +|command|Description| +|--------------------------|-----------| +|`lerna clean`| - removes the node_modules directory from all packages. | +|`npm start`| - starts the web server and backend server. Or Use `yarn zen:watch`| +|`yarn zen:watch`| - starts the web server and backend server in watch mode.| +|`yarn zen:watch:debug`| - starts the web server and backend server in debug and watch mode.| +|`yarn watch`| - build the packages in watchmode (Useful for development)| +|`yarn lerna`| - install external dependencies at the repo root so they're |`lable to all packages.| +|`yarn build`| - build all the packages| +|`yarn install`| - runs `lerna` and `build`| +|`lerna publish`| - publishes packages in the current Lerna project. | + +Files explained: +--- +It uses `lerna.json` for creating the packages structure. Under packages you can create different modules based on its usage. For example: + + packages - Has the packages to organize the codebase into multi-package repositories. + sample-core - Core interfaces of the packages which can be shared between server and client. + sample-platform/server - Core platform interfaces and its implementation code for Server. + sample-platform/browser - Core platform browser State related code which consists of Redux, Graphql Gql and UI Components. + sample-platform/react-shared-components - React pure components and containers are defined. + packages-modules - Has the server and browser side packages designed for a specific module feature. + servers - Has the servers to organize the codebase into multi-package repositories. + frontend-server - Frontend Client Server. This is useful to show demo of this package. + backend-server - Backend apollo server. + + +## [Click here for Project Setup](docs/development/CodeContribution/Project_Setup.md) + +In Order to get started with the development you need to go through the +documentation first + +- [Getting Started with lerna](./docs/development/CodeContribution/lerna-build-tools.md) +- [Running the servers](./docs/development/CodeContribution/How_to_Run_Various_Options.md) +- [Dos and Dont](./docs/development/CodeContribution/DoAndDont.md) + + +\`\`\` + +## package.json + +\`\`\`json +{ + "name": "sample-stack", + "version": "0.9.12", + "private": true, + "homepage": "https://github.com/cdmbase/fullstack-pro#readme", + "bugs": { + "url": "https://github.com/cdmbase/fullstack-pro/issues" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/cdmbase/fullstack-pro.git" + }, + "license": "MIT", + "author": { + "name": "CDMBase LLC", + "email": "none@cdmbase.com" + }, + "workspaces": { + "packages": [ + "portable-devices/*", + "packages-modules/**", + "packages/**", + "servers/*" + ] + }, + "scripts": { + "prebootstrap": "lerna run prepare", + "bootstrap": "yarn && yarn lerna", + "postbootstrap": "yarn build", + "build": "yarn build:packages", + "build:clean": "lerna exec yarn build:clean", + "build:packages": "lerna run build --ignore *server --ignore *device --ignore *browser-extension", + "build:packages:watch": "lerna run build:lib:watch --ignore *server --ignore *device --ignore *browser-extension --stream", + "check-updates": "lerna exec ./node_modules/.bin/npm-check-updates -- -u", + "clean": "lerna clean --yes && yarn build:clean && rimraf node_modules", + "clean:force": "rimraf package-lock.json yarn.lock && yarn clean", + "clean:install": "yarn clean:force && yarn git:pull && yarn bootstrap", + "cli": "node tools/cli", + "commit": "git cz", + "compile": "lerna exec -- yarn compile", + "coverage": "jest --coverage", + "postcoverage": "remap-istanbul --input coverage/coverage.raw.json --type lcovonly --output coverage/lcov.info", + "db:migrate": "knex migrate:latest --cwd . --knexfile ./servers/backend-server/knexfile.js", + "db:migrate:rollback": "knex migrate:rollback --cwd . --knexfile ./servers/backend-server/knexfile.js", + "db:seed": "yarn db:migrate && knex seed:run --cwd . --knexfile ./servers/backend-server/knexfile.js", + "predevpublish": "git checkout devpublish && git pull origin devpublish && git merge -s recursive -X theirs develop -m 'merge from develop' && yarn gitcommit && node tools/update-dependency-version.js && yarn gitcommit", + "devpublish": "lerna publish prerelease --ignore-scripts --exact", + "postdevpublish": "git checkout develop", + "devpublish:auto": "yarn devpublish -- --yes", + "devpublish:force": "yarn devpublish:forceManual -- --yes", + "devpublish:forceManual": "yarn devpublish -- --force-publish=*", + "devpublish:push": "yarn predevpublish && git push origin devpublish && yarn postdevpublish", + "format": "yarn lint --fix", + "format:md": "yarn lint:md --fix", + "generateGraphql": "graphql-codegen", + "generateGraphql:watch": "yarn generateGraphql -- --watch", + "git:pull": "git pull origin $(git rev-parse --abbrev-ref HEAD)", + "gitcommit": "git add -A && git diff --staged --quiet || git commit -am 'auto publish [skip ci] \r\n'", + "husky-skip": "cross-env HUSKY_SKIP_HOOKS=1", + "jest": "./node_modules/.bin/jest", + "lerna": "lerna bootstrap", + "prelernapublish": "git checkout publish && git pull origin publish && git merge -s recursive -X theirs master -m 'merge from master' && yarn gitcommit && node tools/update-dependency-version.js && yarn gitcommit", + "lernapublish": "lerna publish --ignore-scripts --cd-version=patch", + "postlernapublish": "git checkout master", + "lint": "eslint --ext js --ext ts --ext md", + "lint:ci": "yarn lint . --format junit", + "lint:fix": "yarn lint -- --fix", + "lint:md": "markdownlint", + "lintx": "yarn lint ./packages/**/src/**/*.ts ./packages-modules/**/src/**/*.ts", + "prodBuild": "cross-env NODE_ENV=production babel-node --presets es2015 tools/webpack.run", + "publish": "yarn lernapublish", + "publish:auto": "yarn lernapublish --yes", + "publish:force": "yarn publish:forceManual --yes", + "publish:forceManual": "yarn lernapublish --force-publish=*", + "publish:push": "yarn prelernapublish && git push origin publish && yarn postlernapublish", + "start": "cross-env NODE_ENV=development ENV_FILE=../../config/development/dev.env yarn zen:watch", + "start:envSSR": "cross-env SSR=true NODE_ENV=development ENV_FILE=../../config/development/dev.env yarn zen:watch", + "start:test": "cross-env NODE_ENV=test ENV_FILE=../../config/test/test.env zen watch", + "test": "cross-env ENV_FILE=config/test/test.env jest", + "posttest": "yarn lint", + "test:watch": "npm test -- --watch", + "pretravis": "yarn compile", + "travis": "istanbul cover -x \"*.test.js\" _mocha -- --timeout 5000 --full-trace ./test/tests.js", + "posttravis": "yarn lint", + "watch": "lerna exec --no-sort --ignore *server --ignore *device --ignore *browser-extension --stream --parallel -- webpack --watch", + "watch-packages": "lerna exec --no-sort --scope @sample-stack/platform* --scope @sample-stack/react-shared-components --scope @sample-stack/core --stream --parallel 'webpack --watch'", + "zen:build": "cross-env NODE_ENV=production zen build", + "zen:exp": "zen exp", + "zen:watch": "zen watch -x", + "zen:watch:debug": "yarn zen:watch -- -v" + }, + "husky": { + "hooks": { + "pre-commit": "lint-staged", + "commit-msg": "commitlint -E HUSKY_GIT_PARAMS" + } + }, + "lint-staged": { + "*.md": [ + "yarn format:md", + "git add" + ], + "*.{js,jsx,ts,tsx}": [ + "eslint --fix", + "git add" + ] + }, + "resolutions": { + "@apollo/client": "~3.3.21", + "chokidar": "^3.4.0", + "react": "17.0.1", + "react-dom": "17.0.1" + }, + "dependencies": { + "dataloader": "^2.0.0", + "graphql": "^14.7.0", + "graphql-tag": "^2.11.0" + }, + "devDependencies": { + "@babel/cli": "^7.7.7", + "@babel/core": "^7.7.7", + "@babel/plugin-proposal-class-properties": "^7.7.4", + "@babel/plugin-proposal-decorators": "^7.7.4", + "@babel/plugin-proposal-export-default-from": "^7.7.4", + "@babel/plugin-proposal-export-namespace-from": "^7.7.4", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.7.4", + "@babel/plugin-proposal-object-rest-spread": "^7.7.7", + "@babel/plugin-proposal-optional-chaining": "^7.7.5", + "@babel/plugin-proposal-pipeline-operator": "^7.7.7", + "@babel/plugin-syntax-dynamic-import": "^7.7.4", + "@babel/plugin-syntax-export-default-from": "^7.7.4", + "@babel/plugin-syntax-export-namespace-from": "^7.7.4", + "@babel/plugin-syntax-import-meta": "^7.7.4", + "@babel/plugin-syntax-numeric-separator": "^7.7.4", + "@babel/plugin-transform-destructuring": "^7.7.4", + "@babel/plugin-transform-for-of": "^7.13.0", + "@babel/plugin-transform-modules-commonjs": "^7.7.5", + "@babel/plugin-transform-regenerator": "^7.7.5", + "@babel/plugin-transform-runtime": "^7.7.6", + "@babel/polyfill": "7.7.0", + "@babel/preset-env": "^7.7.7", + "@babel/preset-flow": "^7.7.4", + "@babel/preset-react": "^7.7.4", + "@babel/preset-typescript": "^7.7.7", + "@babel/register": "^7.7.7", + "@babel/runtime": "^7.7.7", + "@graphql-codegen/add": "^2.0.2", + "@graphql-codegen/cli": "^1.21.8", + "@graphql-codegen/fragment-matcher": "^2.0.1", + "@graphql-codegen/import-types-preset": "^1.18.6", + "@graphql-codegen/near-operation-file-preset": "^1.18.6", + "@graphql-codegen/typescript": "^1.23.0", + "@graphql-codegen/typescript-graphql-files-modules": "^1.18.1", + "@graphql-codegen/typescript-operations": "^1.18.4", + "@graphql-codegen/typescript-react-apollo": "^2.3.1", + "@graphql-codegen/typescript-resolvers": "^1.20.0", + "@hot-loader/react-dom": "^17.0.0", + "@larix/zen": "0.1.37", + "@open-wc/building-rollup": "^1.10.0", + "@redux-devtools/core": "^3.9.0", + "@redux-devtools/dock-monitor": "^1.4.0", + "@redux-devtools/log-monitor": "^2.3.0", + "@rollup/plugin-graphql": "1.0.0", + "@rollup/plugin-image": "^2.0.6", + "@rollup/plugin-typescript": "^6.1.0", + "@shelf/jest-mongodb": "^1.1.5", + "@types/async": "^3.0.3", + "@types/body-parser": "1.17.1", + "@types/bunyan": "^1.8.6", + "@types/classnames": "^2.2.9", + "@types/cors": "2.8.6", + "@types/enzyme": "^3.10.4", + "@types/express": "^4.17.2", + "@types/hoist-non-react-statics": "^3.3.1", + "@types/ioredis": "^4.14.4", + "@types/isomorphic-fetch": "0.0.35", + "@types/jest": "^26.0.24", + "@types/lodash-es": "^4.17.3", + "@types/minimist": "^1.2.0", + "@types/node": "13.1.0", + "@types/prop-types": "^15.7.3", + "@types/react": "^17.0.35", + "@types/react-dom": "^17.0.11", + "@types/react-helmet": "^5.0.14", + "@types/react-loadable": "^5.5.3", + "@types/react-redux": "^7.1.5", + "@types/react-router": "^5.1.3", + "@types/react-router-config": "^5.0.2", + "@types/react-router-dom": "^5.1.3", + "@types/react-test-renderer": "^17.0.1", + "@types/redux-logger": "^3.0.9", + "@types/semver": "^6.2.0", + "@types/sinon": "^7.5.1", + "@types/webpack": "^4.41.0", + "@types/webpack-env": "^1.14.1", + "@types/zen-observable": "^0.8.0", + "@typescript-eslint/eslint-plugin": "^4.20.0", + "@typescript-eslint/eslint-plugin-tslint": "^4.20.0", + "@typescript-eslint/parser": "^4.20.0", + "apollo": "^2.21.2", + "apollo-server-testing": "^2.21.1", + "autoprefixer": "^9.7.3", + "awesome-typescript-loader": "^5.2.1", + "babel-core": "^7.0.0-bridge.0", + "babel-eslint": "^10.0.3", + "babel-jest": "^26.3.0", + "babel-loader": "^8.0.6", + "babel-plugin-import": "^1.13.3", + "caporal": "^1.3.0", + "chokidar": "^3.4.0", + "clean-webpack-plugin": "^2.0.0", + "commitizen": "^4.2.3", + "concurrently": "^5.0.2", + "connect": "^3.7.0", + "copy-webpack-plugin": "^6.4.0", + "cross-env": "^6.0.3", + "css-loader": "^3.4.0", + "csstype": "^2.5.5", + "dotenv-safe": "^8.2.0", + "dotenv-webpack": "^6.0.0", + "envalid": "^7.2.2", + "enzyme": "^3.11.0", + "enzyme-adapter-react-16": "^1.15.2", + "eslint": "^7.23.0", + "eslint-config-airbnb-typescript": "^12.3.1", + "eslint-config-prettier": "^8.1.0", + "eslint-loader": "^4.0.2", + "eslint-plugin-graphql": "^4.0.0", + "eslint-plugin-import": "^2.22.1", + "eslint-plugin-jest": "^24.3.3", + "eslint-plugin-jsdoc": "^32.3.0", + "eslint-plugin-jsx-a11y": "^6.4.1", + "eslint-plugin-markdown": "^2.0.1", + "eslint-plugin-no-null": "^1.0.2", + "eslint-plugin-prettier": "^3.3.1", + "eslint-plugin-react": "^7.23.1", + "eslint-plugin-react-hooks": "^4.2.0", + "eslint-plugin-simple-import-sort": "^7.0.0", + "express": "^4.17.1", + "extract-text-webpack-plugin": "^4.0.0-beta.0", + "file-loader": "^5.0.2", + "freeport-async": "^2.0.0", + "fs-extra": "^8.1.0", + "html-loader": "^0.5.5", + "html-webpack-plugin": "^3.2.0", + "http-proxy-middleware": "^0.20.0", + "husky": "^5.0.9", + "ignore-loader": "^0.1.2", + "image-size": "^0.8.3", + "ip": "^1.1.5", + "isomorphic-style-loader": "^5.1.0", + "istanbul": "1.0.0-alpha.2", + "jest": "^26.0.0", + "jest-css-modules-transform": "^3.1.0", + "jest-dom": "^4.0.0", + "jest-junit": "^12.2.0", + "jest-matcher-utils": "^26.0.0", + "jest-raw-loader": "^1.0.1", + "jest-transform-graphql": "^2.1.0", + "jsdom": "^15.2.1", + "lerna": "^4.0.0", + "lint-staged": "^9.5.0", + "lodash": "^4.17.15", + "lodash-es": "^4.17.15", + "lodash-webpack-plugin": "^0.11.5", + "markdownlint-cli": "^0.27.0", + "merge": "^1.2.1", + "mime": "^2.4.4", + "mini-css-extract-plugin": "^0.9.0", + "minilog": "^3.1.0", + "mkdirp": "^0.5.1", + "mocha": "^6.2.2", + "mocha-steps": "^1.3.0", + "module": "^1.2.5", + "morgan": "^1.9.1", + "nock": "^11.7.0", + "node-dev": "^4.0.0", + "node-sass": "^4.13.0", + "nodemon": "^2.0.2", + "openurl": "^1.1.1", + "pm2": "^4.2.1", + "postcss-loader": "^3.0.0", + "prettier": "^2.2.1", + "qrcode-terminal": "^0.12.0", + "raw-loader": "^4.0.0", + "react-addons-test-utils": "^16.0.0-alpha.3", + "react-hot-loader": "^4.12.18", + "react-test-renderer": "^16.12.0", + "redux-devtools-extension": "^2.13.8", + "redux-mock-store": "^1.5.4", + "remap-istanbul": "^0.13.0", + "resolve-url-loader": "^3.1.1", + "rimraf": "^3.0.0", + "rollup": "^2.53.2", + "rollup-plugin-string": "^3.0.0", + "sass-loader": "^8.0.0", + "shelljs": "^0.8.3", + "simple-git": "^2.42.0", + "sinon": "^8.0.1", + "source-list-map": "^2.0.1", + "source-map-loader": "^0.2.4", + "source-map-support": "^0.5.16", + "standard-version": "^7.0.1", + "style-loader": "^1.1.1", + "svg-url-loader": "^3.0.3", + "tcomb": "^3.2.29", + "ts-jest": "^26.0.0", + "ts-loader": "^6.2.1", + "ts-node": "^8.5.4", + "tslib": "^1.10.0", + "tslint": "^6.1.3", + "typedoc": "^0.15.5", + "typescript": "4.2.3", + "uglify-es": "^3.3.9", + "uglifyjs-webpack-plugin": "^2.2.0", + "url-loader": "^3.0.0", + "wait-on": "^4.0.0", + "webpack": "4.46.0", + "webpack-bundle-analyzer": "^4.5.0", + "webpack-cli": "^3.3.10", + "webpack-dev-middleware": "^4.1.0", + "webpack-dev-server": "^3.11.2", + "webpack-hot-middleware": "^2.25.0", + "webpack-manifest-plugin": "^2.2.0", + "webpack-merge": "^5.4.0", + "webpack-node-externals": "^3.0.0", + "webpack-sources": "^1.4.3", + "webpack-virtual-modules": "^0.4.3", + "ws": "^7.2.1" + }, + "engines": { + "node": ">=12.18.4 < 15.0.0", + "yarn": ">=1.22" + }, + "cacheDirectories": [ + ".cache" + ] +} +\`\`\` + +## packages/sample-core/README.md + +\`\`\`md + + +## To run tests + + `npm link` + `npm link @xtermstack/xterm-core` + `yarn install` + `tsc` + `yarn test` + +\`\`\` + +## packages/sample-core/package.json + +\`\`\`json +{ + "name": "@sample-stack/core", + "version": "0.0.1", + "description": "Sample core for higher packages to depend on", + "license": "ISC", + "author": "CDMBase LLC", + "main": "lib/index.js", + "typings": "lib/index.d.ts", + "scripts": { + "build": "yarn build:clean && yarn build:lib", + "build:clean": "rimraf lib", + "build:lib": "webpack", + "build:lib:watch": "yarn build:lib -- --watch", + "jest": "./node_modules/.bin/jest", + "prepublish": "yarn build", + "test": "cross-env ENV_FILE=../../config/test/test.env jest", + "test:debug": "npm test -- --runInBand", + "test:watch": "npm test -- --watch", + "watch": "yarn build:lib:watch" + }, + "publishConfig": { + "access": "public" + }, + "typescript": { + "definition": "lib/index.d.ts" + } +} + +\`\`\` + +## packages/sample-platform/browser/package.json + +\`\`\`json +{ + "name": "@sample-stack/platform-browser", + "version": "0.0.1", + "description": "Sample core for higher packages to depend on", + "license": "ISC", + "author": "CDMBase LLC", + "main": "lib/index.js", + "typings": "lib/index.d.ts", + "scripts": { + "build": "yarn build:clean && yarn build:lib", + "build:clean": "rimraf lib", + "build:lib": "webpack", + "build:lib:watch": "yarn build:lib -- --watch", + "jest": "./node_modules/.bin/jest", + "prepublish": "yarn build", + "test": "jest", + "test:debug": "npm test -- --runInBand", + "test:watch": "npm test -- --watch", + "watch": "yarn build:lib:watch" + }, + "jest": { + "moduleFileExtensions": [ + "ts", + "tsx", + "js", + "json" + ], + "modulePaths": [ + "node_modules" + ], + "roots": [ + "src" + ], + "testEnvironment": "node", + "testRegex": "/__tests__/.*test*\\.(ts|tsx|js)$", + "transform": { + "\\.(ts|tsx)$": "/../../node_modules/ts-jest/preprocessor.js" + } + }, + "dependencies": { + "@sample-stack/core": "link:../../sample-core" + }, + "publishConfig": { + "access": "public" + }, + "typescript": { + "definition": "lib/index.d.ts" + } +} + +\`\`\` + +## packages/sample-platform/server/README.md + +\`\`\`md + + +## To run tests + + `npm link` + `npm link @xtermstack/xterm-core` + `yarn install` + `tsc` + `yarn test` + +\`\`\` + +## packages/sample-platform/server/package.json + +\`\`\`json +{ + "name": "@sample-stack/platform-server", + "version": "0.0.1", + "description": "Sample core for higher packages to depend on", + "license": "ISC", + "author": "CDMBase LLC", + "main": "lib/index.js", + "typings": "lib/index.d.ts", + "scripts": { + "build": "yarn build:clean && yarn build:lib", + "build:clean": "rimraf lib", + "build:lib": "webpack", + "build:lib:watch": "yarn build:lib -- --watch", + "jest": "./node_modules/.bin/jest", + "prepublish": "yarn build", + "test": "jest", + "test:debug": "npm test -- --runInBand", + "test:watch": "npm test -- --watch", + "watch": "yarn build:lib:watch" + }, + "jest": { + "moduleFileExtensions": [ + "ts", + "tsx", + "js", + "json" + ], + "modulePaths": [ + "node_modules" + ], + "roots": [ + "src" + ], + "testEnvironment": "node", + "testRegex": "/__tests__/.*test*\\.(ts|tsx|js)$", + "transform": { + "\\.(ts|tsx)$": "/../../node_modules/ts-jest/preprocessor.js" + } + }, + "dependencies": { + "@sample-stack/core": "link:../../sample-core" + }, + "publishConfig": { + "access": "public" + }, + "typescript": { + "definition": "lib/index.d.ts" + } +} + +\`\`\` + +## packages/sample-store/README.md + +\`\`\`md + + +## To run tests + + `npm link` + `npm link @xtermstack/xterm-core` + `yarn install` + `tsc` + `yarn test` + +\`\`\` + +## packages/sample-store/package.json + +\`\`\`json +{ + "name": "@sample-stack/store", + "version": "0.0.1", + "description": "Sample core for higher packages to depend on", + "license": "ISC", + "author": "CDMBase LLC", + "main": "lib/index.js", + "typings": "lib/index.d.ts", + "scripts": { + "build": "yarn build:clean && yarn build:lib", + "build:clean": "rimraf lib", + "build:lib": "webpack", + "build:lib:watch": "yarn build:lib -- --watch", + "jest": "./node_modules/.bin/jest", + "prepublish": "yarn build", + "test": "cross-env ENV_FILE=../../config/test/test.env jest", + "test:debug": "npm test -- --runInBand", + "test:watch": "npm test -- --watch", + "watch": "yarn build:lib:watch" + }, + "jest": { + "moduleFileExtensions": [ + "ts", + "tsx", + "js", + "json" + ], + "modulePaths": [ + "node_modules" + ], + "roots": [ + "src" + ], + "testEnvironment": "node", + "testRegex": "/__tests__/.*test*\\.(ts|tsx|js)$", + "transform": { + "\\.(ts|tsx)$": "/../../node_modules/ts-jest/preprocessor.js" + } + }, + "dependencies": { + "@sample-stack/core": "link:../sample-core", + "sequelize": "^5.21.3" + }, + "peerDependencies": { + "inversify": "*" + }, + "publishConfig": { + "access": "public" + }, + "typescript": { + "definition": "lib/index.d.ts" + } +} + +\`\`\` + +## packages-modules/counter/browser/package.json + +\`\`\`json +{ + "name": "@sample-stack/counter-module-browser", + "version": "0.0.1", + "description": "Sample core for higher packages to depend on", + "license": "ISC", + "author": "CDMBase LLC", + "main": "lib/index.js", + "typings": "lib/index.d.ts", + "scripts": { + "build": "yarn build:clean && yarn build:lib", + "build:clean": "rimraf lib", + "build:lib": "rollup -c rollup.config.js", + "build:lib:watch": "yarn build:lib -- --watch", + "jest": "./node_modules/.bin/jest", + "prepublish": "yarn build", + "test": "cross-env ENV_FILE=../../config/test/test.env jest", + "test:debug": "npm test -- --runInBand", + "test:watch": "npm test -- --watch", + "watch": "yarn build:lib:watch" + }, + "jest": { + "moduleFileExtensions": [ + "ts", + "tsx", + "js", + "json" + ], + "modulePaths": [ + "node_modules" + ], + "roots": [ + "src" + ], + "testEnvironment": "node", + "testRegex": "/__tests__/.*test*\\.(ts|tsx|js)$", + "transform": { + "\\.(ts|tsx)$": "/../../node_modules/ts-jest/preprocessor.js" + } + }, + "dependencies": { + "@sample-stack/platform-browser": "link:../../../packages/sample-platform/browser", + "antd": "~4.15.3" + }, + "devDependencies": {}, + "peerDependencies": { + "@common-stack/client-react": "*", + "@rollup/plugin-graphql": "*", + "@rollup/plugin-image": "*", + "@rollup/plugin-typescript": "*", + "react": "*", + "react-native": "*", + "react-redux": "*", + "react-router": "*", + "react-router-dom": "*", + "redux": "*", + "redux-observable": "*", + "rollup-plugin-string": "*", + "rxjs": "*" + }, + "publishConfig": { + "access": "public" + }, + "typescript": { + "definition": "lib/index.d.ts" + } +} +\`\`\` + +## packages-modules/counter/browser/src/connected-react-router/README.md + +\`\`\`md +# Connected React Router TypeScript Example + +You can try changing counter value and editing some components. Components will be updated while preserving counter state. + +In Hello link, you will see that the HelloChild component can access router state (URL path) without passing as props via its parent. + + +Reference: + +https://github.com/supasate/connected-react-router/tree/master/examples/typescript +\`\`\` + +## packages-modules/counter/browser/src/fela/README.md + +\`\`\`md +

Fela

+ +Fela is a small, high-performant and framework-agnostic toolbelt to handle state-driven styling in JavaScript.
+It is dynamic by design and renders your styles depending on your application state. + +Code Reference: + +https://github.com/rofrischmann/fela/tree/master/examples/example-typescript +\`\`\` + +## packages-modules/counter/electron/package.json + +\`\`\`json +{ + "name": "@sample-stack/counter-module-electron", + "version": "0.0.1", + "description": "Sample core for higher packages to depend on", + "license": "ISC", + "author": "CDMBase LLC", + "main": "lib/index.js", + "typings": "lib/index.d.ts", + "scripts": { + "build": "yarn build:clean && yarn build:lib", + "build:clean": "rimraf lib", + "build:lib": "webpack", + "build:lib:watch": "yarn build:lib -- --watch", + "jest": "./node_modules/.bin/jest", + "prepublish": "yarn build", + "test": "cross-env ENV_FILE=../../config/test/test.env jest", + "test:debug": "npm test -- --runInBand", + "test:watch": "npm test -- --watch", + "watch": "yarn build:lib:watch" + }, + "jest": { + "moduleFileExtensions": [ + "ts", + "tsx", + "js", + "json" + ], + "modulePaths": [ + "node_modules" + ], + "roots": [ + "src" + ], + "testEnvironment": "node", + "testRegex": "/__tests__/.*test*\\.(ts|tsx|js)$", + "transform": { + "\\.(ts|tsx)$": "/../../node_modules/ts-jest/preprocessor.js" + } + }, + "dependencies": { + "@sample-stack/counter-module-browser": "link:../browser" + }, + "devDependencies": { + "@open-wc/building-rollup": "^1.10.0", + "@rollup/plugin-graphql": "1.0.0", + "@rollup/plugin-image": "^2.0.6", + "@rollup/plugin-typescript": "^6.1.0", + "rollup": "latest" + }, + "peerDependencies": { + "@common-stack/client-react": "*", + "native-base": "*", + "react": "*", + "react-native": "*", + "react-redux": "*", + "react-router": "*", + "react-router-dom": "*", + "redux": "*", + "redux-observable": "*", + "rxjs": "*" + }, + "publishConfig": { + "access": "public" + }, + "typescript": { + "definition": "lib/index.d.ts" + } +} +\`\`\` + +## packages-modules/counter/mobile/package.json + +\`\`\`json +{ + "name": "@sample-stack/counter-module-mobile", + "version": "0.0.1", + "description": "Sample core for higher packages to depend on", + "license": "ISC", + "author": "CDMBase LLC", + "main": "lib/index.js", + "typings": "lib/index.d.ts", + "scripts": { + "build": "yarn build:clean && yarn build:lib", + "build:clean": "rimraf lib", + "build:lib": "tsc", + "build:lib:watch": "yarn build:lib -- --watch", + "jest": "./node_modules/.bin/jest", + "prepublish": "yarn build", + "test": "cross-env ENV_FILE=../../config/test/test.env jest", + "test:debug": "npm test -- --runInBand", + "test:watch": "npm test -- --watch", + "watch": "yarn build:lib:watch" + }, + "jest": { + "moduleFileExtensions": [ + "ts", + "tsx", + "js", + "json" + ], + "modulePaths": [ + "node_modules" + ], + "roots": [ + "src" + ], + "testEnvironment": "node", + "testRegex": "/__tests__/.*test*\\.(ts|tsx|js)$", + "transform": { + "\\.(ts|tsx)$": "/../../node_modules/ts-jest/preprocessor.js" + } + }, + "dependencies": { + "@sample-stack/platform-browser": "link:../../../packages/sample-platform/browser", + "antd": "~4.15.3" + }, + "peerDependencies": { + "@common-stack/client-react": "*", + "native-base": "*", + "react": "*", + "react-native": "*", + "react-redux": "*", + "react-router": "*", + "react-router-dom": "*", + "redux": "*" + }, + "publishConfig": { + "access": "public" + }, + "typescript": { + "definition": "lib/index.d.ts" + } +} +\`\`\` + +## packages-modules/counter/mobile/src/connected-react-router/README.md + +\`\`\`md +# Connected React Router TypeScript Example + +You can try changing counter value and editing some components. Components will be updated while preserving counter state. + +In Hello link, you will see that the HelloChild component can access router state (URL path) without passing as props via its parent. + + +Reference: + +https://github.com/supasate/connected-react-router/tree/master/examples/typescript +\`\`\` + +## packages-modules/counter/server/package.json + +\`\`\`json +{ + "name": "@sample-stack/counter-module-server", + "version": "0.0.1", + "description": "Sample core for higher packages to depend on", + "license": "ISC", + "author": "CDMBase LLC", + "main": "lib/index.js", + "typings": "lib/index.d.ts", + "scripts": { + "build": "yarn build:clean && yarn build:lib", + "build:clean": "rimraf lib", + "build:lib": "webpack", + "build:lib:watch": "yarn build:lib -- --watch", + "jest": "./node_modules/.bin/jest", + "prepublish": "yarn build", + "test": "cross-env ENV_FILE=../../config/test/test.env jest", + "test:debug": "npm test -- --runInBand", + "test:watch": "npm test -- --watch", + "watch": "yarn build:lib:watch" + }, + "jest": { + "moduleFileExtensions": [ + "ts", + "tsx", + "js", + "json" + ], + "modulePaths": [ + "node_modules" + ], + "roots": [ + "src" + ], + "testEnvironment": "node", + "testRegex": "/__tests__/.*test*\\.(ts|tsx|js)$", + "transform": { + "\\.(ts|tsx)$": "/../../node_modules/ts-jest/preprocessor.js" + } + }, + "peerDependencies": { + "@cdm-logger/server": "*", + "@common-stack/core": "*", + "@common-stack/server-core": "*", + "apollo-server-caching": "*", + "dataloader": "*", + "inversify": "*", + "moleculer": "*" + }, + "publishConfig": { + "access": "public" + }, + "typescript": { + "definition": "lib/index.d.ts" + } +} + +\`\`\` + +## portable-devices/desktop/package.json + +\`\`\`json +{ + "name": "sample-stack-desktop-device", + "version": "0.0.1", + "private": true, + "description": "App is based on Electron, React, Redux and NodeJS as a back end", + "homepage": "https://github.com/cdmbase/fullstack-pro#readme", + "bugs": { + "url": "https://github.com/cdmbase/fullstack-pro/issues" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/cdmbase/fullstack-pro.git" + }, + "license": "MIT", + "author": { + "name": "CDMBase LLC", + "email": "jteidforyou@gmail.com" + }, + "main": "index.js", + "scripts": { + "build": "cross-env NODE_OPTIONS='--max_old_space_size=4096' NODE_ENV=production electron-webpack", + "build:clean": "rimraf dist", + "electron": "electron dist/main/main.js", + "package": "electron-builder", + "release": "yarn build && electron-builder build --mac", + "release:linux": "yarn build && electron-builder build --linux", + "release:mac": "yarn build && electron-builder build --mac", + "release:win": "electron-builder build --win", + "start": "cross-env NODE_ENV=production electron dist/main/main.js", + "start:dev": "cross-env NODE_ENV=development ENV_FILE=../../config/development/dev.env electron dist/main/main.js", + "start:staging": "cross-env NODE_ENV=staging ENV_FILE=../../config/staging/staging.env electron dist/main/main.js", + "start:prod": "cross-env NODE_ENV=production ENV_FILE=../../config/development/dev.env electron dist/main/main.js", + "test": "echo Skipped.", + "watch": "cross-env NODE_ENV=development ENV_FILE=../../config/development/dev.env electron-webpack dev", + "watch:staging": "cross-env ENV_FILE=../../config/staging/staging.env electron-webpack dev" + }, + "resolutions": { + "webpack-sources": "^1.1.0" + }, + "dependencies": { + "@ant-design/compatible": "^1.0.5", + "@ant-design/icons": "^4.2.2", + "@cdm-logger/client": "^7.0.8", + "@cdm-logger/server": "^7.0.7", + "@cdm-logger/electron": "^7.0.7", + "@common-stack/client-core": "0.1.11", + "@common-stack/client-react": "0.1.11", + "@common-stack/core": "0.1.11", + "@common-stack/server-core": "0.1.11", + "@sample-stack/core": "link:../../packages/sample-core", + "@sample-stack/counter-module-browser": "link:../../packages-modules/counter/browser", + "@sample-stack/counter-module-electron": "link:../../packages-modules/counter/electron", + "@sample-stack/platform-browser": "link:../../packages/sample-platform/browser", + "antd": "~4.15.3", + "@apollo/client": "~3.3.21", + "apollo-link-debounce": "^2.1.0", + "apollo-link-logger": "^1.2.3", + "apollo-logger": "^0.3.3", + "aws-sdk": "^2.880.0", + "browser-bunyan": "^1.6.3", + "check-internet-connected": "^2.0.5", + "classnames": "^2.2.6", + "connected-react-router": "^6.9.1", + "cors": "^2.8.5", + "cross-env": "^6.0.3", + "dotenv": "^8.2.0", + "electron-json-storage": "^2.0.0", + "electron-positioner": "^4.1.0", + "electron-redux": "^1.5.4", + "electron-updater": "^4.3.9", + "envalid": "^7.2.2", + "esm": "^3.2.25", + "fela": "11.6.0", + "fela-beautifier": "^11.6.0", + "fela-dom": "11.6.0", + "fela-font-renderer": "^5.0.25", + "fela-perf": "^11.6.0", + "fela-plugin-fallback-value": "^11.6.0", + "fela-plugin-logger": "^11.6.0", + "fela-plugin-lvha": "^5.0.16", + "fela-plugin-prefixer": "^11.6.0", + "fela-plugin-unit": "^11.6.0", + "fela-plugin-validator": "^11.6.0", + "fela-preset-web": "^11.6.0", + "graphql": "^14.7.0", + "graphql-tag": "^2.11.0", + "history": "^4.10.1", + "immutability-helper": "^3.0.1", + "inversify": "^5.0.1", + "inversify-binding-decorators": "^4.0.0", + "isomorphic-fetch": "^2.2.1", + "js-cookie": "^2.2.1", + "lodash": "^4.17.15", + "os-name": "^4.0.0", + "pify": "^2.3.0", + "ramda": "^0.26.1", + "react": "17.0.1", + "react-dom": "17.0.1", + "react-fela": "11.6.0", + "react-helmet": "^6.1.0", + "react-loadable": "^5.5.0", + "react-redux": "^7.1.3", + "react-router": "^5.2.1", + "react-router-config": "^5.1.1", + "react-router-dom": "^5.3.0", + "react-transition-group": "^4.3.0", + "redux": "^4.0.5", + "redux-logger": "^3.0.6", + "redux-observable": "^1.2.0", + "redux-persist": "^6.0.0", + "redux-thunk": "^2.3.0", + "reflect-metadata": "^0.1.13", + "reselect": "^4.0.0", + "rxjs": "^6.5.3", + "rxjs-compat": "^6.5.3", + "rxjs-hooks": "^0.5.2", + "source-map-support": "^0.5.19", + "sqlite3": "^5.0.2", + "subscriptions-transport-ws": "0.9.18", + "typeorm": "^0.2.32" + }, + "devDependencies": { + "@jest-runner/electron": "^3.0.1", + "cross-env": "^6.0.3", + "electron": "11.4.10", + "electron-builder": "^22.11.7", + "electron-debug": "^3.1.0", + "electron-devtools-installer": "^3.2.0", + "electron-is": "^3.0.0", + "electron-log": "^4.3.5", + "electron-webpack": "^2.8.2", + "electron-webpack-ts": "^4.0.1", + "pm2": "^4.2.1", + "rimraf": "^3.0.0", + "workspaces-utils": "^1.2.1" + }, + "buildAbout": { + "appName": "Fullstack-Pro", + "apiApp": "https://time-tracker-api.herokuapp.com/" + } +} +\`\`\` + +## portable-devices/mobile/package.json + +\`\`\`json +{ + "name": "sample-stack-mobile-device", + "version": "0.0.1", + "private": true, + "main": "index.js", + "scripts": { + "android": "expo run:android", + "build": "yarn easBuild --profile development", + "build:all": "yarn build -p all", + "build:android": "yarn build -p android --clear-cache", + "build:auto": "yarn build:all --non-interactive", + "build:clean": "rimraf build .expo .tmp", + "build:configure": "eas build:configure", + "build:ios": "yarn build -p ios --clear-cache", + "build:preview": "yarn easBuild --profile preview -p all --non-interactive", + "cli": "node ../../tools/cli", + "eas-build-post-install": "lerna exec --scope=@sample-stack/core --scope=@sample-stack/counter-module-mobile yarn build", + "easBuild": "eas build", + "eslint": "eslint --fix --ext js --ext jsx --ext json src", + "exp-login": "cross-env NODE_ENV=production expo login -u $EXP_USERNAME -p $EXP_PASSWORD --non-interactive", + "exp-publish": "yarn exp-login && yarn expo p --non-interactive", + "ios": "expo run:ios", + "lint": "yarn eslint && yarn tslint", + "start": "expo start --dev-client", + "submit": "eas submit", + "test": "yarn tests && yarn lint", + "tests": "jest", + "tests:watch": "jest --watch", + "tslint": "tslint --fix -p tsconfig.json -c ../../tslint.json", + "watch": "cross-env NODE_ENV=development ENV_FILE=../../config/development/dev.env expo start --clear", + "watch:android": "expo start --android", + "watch:ios": "expo start --ios" + }, + "jest": { + "preset": "jest-expo" + }, + "dependencies": { + "@apollo/client": "~3.3.21", + "@cdm-logger/client": "^7.0.8", + "@codler/react-native-keyboard-aware-scroll-view": "1.0.0", + "@common-stack/client-core": "^0.1.11", + "@common-stack/client-react": "^0.1.11", + "@common-stack/core": "^0.1.11", + "@expo/vector-icons": "^12.0.0", + "@react-native-async-storage/async-storage": "~1.15.0", + "@react-native-community/cameraroll": "^4.0.4", + "@react-native-community/masked-view": "^0.1.10", + "@react-native-community/netinfo": "6.0.2", + "@react-native-community/picker": "^1.8.1", + "@react-native-community/segmented-control": "^2.2.2", + "@react-native-community/slider": "^4.1.7", + "@react-native-community/viewpager": "^5.0.11", + "@sample-stack/counter-module-mobile": "link:../../packages-modules/counter/mobile", + "apollo-link-debounce": "^2.1.0", + "apollo-link-logger": "^1.2.3", + "apollo-logger": "^0.3.3", + "browser-bunyan": "^1.6.3", + "connected-react-router": "^6.9.1", + "eas-cli": "^0.36.1", + "expo": "~43.0.3", + "expo-app-loading": "~1.2.1", + "expo-asset": "~8.4.3", + "expo-cli": "^4.13.0", + "expo-constants": "~12.1.3", + "expo-file-system": "~13.0.3", + "expo-font": "~10.0.3", + "expo-image-picker": "~11.0.3", + "expo-keep-awake": "~10.0.0", + "expo-linking": "~2.4.2", + "expo-localization": "~11.0.0", + "expo-notifications": "~0.13.3", + "expo-permissions": "~13.0.3", + "expo-random": "12.0.1", + "expo-secure-store": "~11.0.3", + "expo-splash-screen": "~0.13.5", + "expo-status-bar": "~1.1.0", + "expo-web-browser": "~10.0.3", + "fela": "11.6.0", + "fela-beautifier": "^11.6.0", + "fela-dom": "11.6.0", + "fela-font-renderer": "^5.0.25", + "fela-native": "^11.6.1", + "fela-perf": "^11.6.0", + "fela-plugin-fallback-value": "^11.6.0", + "fela-plugin-logger": "^11.6.0", + "fela-plugin-lvha": "^5.0.16", + "fela-plugin-prefixer": "^11.6.0", + "fela-plugin-unit": "^11.6.0", + "fela-plugin-validator": "^11.6.0", + "fela-preset-web": "^11.6.0", + "history": "^4.10.1", + "immutability-helper": "^3.0.1", + "inversify": "^5.0.1", + "isomorphic-fetch": "^2.2.1", + "lodash": "^4.17.4", + "metro-minify-terser": "^0.56.0", + "minilog": "^3.1.0", + "native-base": "~3.2.2", + "prop-types": "^15.6.0", + "ramda": "^0.26.1", + "react": "17.0.1", + "react-dom": "17.0.1", + "react-fela": "11.6.0", + "react-helmet": "^6.1.0", + "react-loadable": "^5.5.0", + "react-native": "0.64.3", + "react-native-dotenv": "^3.3.0", + "react-native-gesture-handler": "~2.1.0", + "react-native-keyboard-aware-scroll-view": "^0.9.3", + "react-native-keyboard-spacer": "^0.4.1", + "react-native-mime-types": "^2.3.0", + "react-native-modal": "^11.6.1", + "react-native-reanimated": "^2.2.0", + "react-native-safe-area-context": "3.3.2", + "react-native-screens": "~3.8.0", + "react-native-simple-picker": "^3.1.2", + "react-native-svg": "~12.1.1", + "react-native-swipe-list-view": "^3.2.6", + "react-native-web": "0.17.1", + "react-redux": "^7.1.3", + "react-router": "^5.2.1", + "react-router-config": "^5.1.1", + "react-router-dom": "^5.3.0", + "react-router-native": "^5.2.1", + "redux": "^4.0.5", + "redux-logger": "^3.0.6", + "redux-observable": "^1.2.0", + "redux-persist": "^6.0.0", + "redux-thunk": "^2.3.0", + "reselect": "^4.0.0", + "rxjs": "^6.5.3", + "rxjs-compat": "^6.5.3", + "rxjs-hooks": "^0.5.2", + "sentry-expo": "~4.0.0", + "subscriptions-transport-ws": "0.9.18" + }, + "devDependencies": { + "@babel/core": "^7.12.9", + "@testing-library/react-native": "^5.0.3", + "@types/expo": "^33.0.1", + "@types/react": "^17.0.35", + "@types/react-native": "~0.64.12", + "@types/react-native-dotenv": "^0.2.0", + "@types/react-native-keyboard-spacer": "^0.4.1", + "@types/react-router-native": "^5.1.0", + "babel-preset-expo": "8.5.1", + "cross-env": "^6.0.3", + "eslint-plugin-react-native": "^3.8.1", + "expo-dev-client": "^0.6.3", + "jest-expo": "^43.0.0", + "lerna": "^4.0.0" + }, + "peerDependencies": { + "webpack": "*" + } +} + +\`\`\` + +## servers/backend-server/README.md + +\`\`\`md +# webpack-apollo-server +Starter kit for apollo server using webpack and typescript + +What does it include: +---- + 1. exported schema as example for GraphQL Schema + 2. Working Apollo Server (webpack + tslint + tsloader) + 3. Typescript 2.0.0 => ES6 + 4. Dockerfile to make the apollo-server a container. + 5. unit testing (mocha-webpack+chai) + coverage report (mocha-istanbul-spec+istanbul). + 6. working with graphql-tools + 7. standard-version for auto SemVer. + +Notes +---- +Please note that you will need to rename the library name in some files: + + 1. package.json (ofcourse ;)) + +Useful commands: +---- + yarn build - build the library files (Required for start:watch) + yarn build:watch - build the library files in watchmode (Useful for development) + npm test - run tests once + yarn test:watch - run tests in watchmode (Useful for development) + yarn test:growl - run tests in watchmode with growl notification (even more useful for development) + +How to run it: +---- +\`\`\`bash + npm start +\`\`\` + +Files explained: +---- + 1. src - directory is used for typescript code that is part of the project + 1a. main.ts - Main server file. (Starting Apollo server) + 1b. main.spec.ts - Tests file for main + 1c. schema - Module used to build schema + - index.ts - simple logic to merge all modules into a schema using graphql-tools + - modules/ - directory for modules to be used with graphql-tools + 1c. schema.spec.ts - Basic test for schema. + 1c. main.test.ts - Main for tests runner. + 3. package.json - file is used to describe the library + 4. tsconfig.json - configuration file for the library compilation + 6. tslint.json - configuration file for the linter + 7. typings.json - typings needed for the server + 8. webpack.config.js - configuration file of the compilation automation process for the library + 9. webpack.config.test.js - configuration file of the compilation when testing + 10. Dockerfile - Dockerfile used to describe how to make a container out of apollo server + 11. mocha-webpack.opts - Options file for mocha-webpack + +Output files explained: +---- + 1. node_modules - directory npm creates with all the dependencies of the module (result of yarn install) + 2. dist - directory contains the compiled server (javascript) + 3. html-report - output of npm test, code coverage html report. + + +Database +---- +Connects to memory database through Knex + +Database configuration is set in `db-config.json` + +Setup +---- +Create a database and fill the data +\`\`\` +yarn db:seed +\`\`\` +What files to be customized? +---- +Customizable files for a specific project would be the following +\`\`\` +src/container +src/middleware/graphql.ts +\`\`\` + +The Person type - dynamic/parametrized query and drill down: +---- +The person type was added to demonstrate a database like access, parametrized queries, resolvers and drill down. +The data is currently hard coded but simulates a storage. Each person has an id, name and sex. It also has a dynamic +field called matches. For demonstration purposes, this field will retrieve all members of the other sex by using a +resolver. + +Since this is a computed field the query can be infinitely nested, for example, try in the graphiql editor this query: + + { + getPerson(id: "1") { + id, + name + sex + matches { + id + name + sex + matches { + id + name + sex + matches { + id + name + sex + } + } + } + } + } + +It will return a nested, alternating male/femal results. + +To list all persons, use the `persons` query: + + { + persons { + id + name + } + } + +There is also an example of a mutation - `addPerson(name: String, sex: String)`, to use it: + + mutation { + addPerson(name: "kuku", sex: "male"){ + id + name + } + } + +Note that the query generates a random id and that the added persons are transient, +i.e. not persisted and will be gone once you shut down the server. +\`\`\` + +## servers/backend-server/package.json + +\`\`\`json +{ + "name": "sample-stack-backend-server", + "version": "0.0.1", + "private": true, + "description": "Starter kit for apollo server using webpack and typescript", + "keywords": [ + "apollo", + "apollo-server", + "backend", + "express", + "graphiql", + "graphql", + "typescript", + "webpack" + ], + "homepage": "https://github.com/cdmbase/fullstack-pro#readme", + "bugs": { + "url": "https://github.com/cdmbase/fullstack-pro/issues" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/cdmbase/fullstack-pro.git" + }, + "license": "MIT", + "author": "CDMBase LLC", + "main": "dist/index.js", + "typings": "dist/main.d.ts", + "scripts": { + "build": "cross-env NODE_ENV=production zen build -x", + "build:clean": "rimraf dist .awcache", + "build:debug": "cross-env NODE_ENV=development zen build -x", + "db:migrate": "knex -- migrate:latest --cwd . --knexfile ./knexfile.js", + "db:migrate:rollback": "knex -- migrate:rollback --cwd . --knexfile ./knexfile.js", + "db:seed": "yarn db:migrate && knex -- seed:run --cwd . --knexfile ./knexfile.js", + "docker:build": "yarn build && docker build . -t $npm_package_name:$npm_package_version", + "docker:build:debug": "yarn build:debug && docker build . -t $npm_package_name:$npm_package_version", + "docker:run": "docker run --env-file ../../config/staging/docker-staging.env -p 8080:8080 -it $npm_package_name:$npm_package_version", + "docker:run:debug": "cross-env NODE_ENV=development docker run --env-file ../../config/staging/docker-staging.env -p 8080:8080 -it $npm_package_name:$npm_package_version", + "proddb:migrate": "NODE_ENV=production yarn db:migrate", + "proddb:migrate:rollback": "NODE_ENV=production yarn db:migrate:rollback", + "proddb:seed": "NODE_ENV=production yarn db:seed", + "prepublish": "yarn build:clean", + "stagedb:migrate": "cross-env ENV_FILE=../../config/test/test.env NODE_ENV=test yarn db:migrate", + "stagedb:migrate:rollback": "cross-env ENV_FILE=../../config/test/test.env NODE_ENV=test yarn db:migrate:rollback", + "stagedb:seed": "cross-env ENV_FILE=../../config/test/test.env NODE_ENV=test yarn db:seed", + "start": "cross-env NODE_ENV=production pm2-runtime dist/index.js", + "start:staging": "cross-env NODE_ENV=staging ENV_FILE=../../config/staging/staging.env node --harmony dist", + "start:test": "cross-env NODE_ENV=test ENV_FILE=../../config/test/test.env node --harmony dist", + "test": "jest", + "test:notify": "yarn test:watch -- --notify", + "test:watch": "npm test -- --watch", + "preupver": "npm test", + "upver": "standard-version", + "watch": "cross-env NODE_ENV=development ENV_FILE=../../config/development/dev.env yarn zen:watch", + "watch:debug": "cross-env NODE_ENV=development ENV_FILE=../../config/development/dev.env yarn zen:watch -- -v", + "watch:staging": "cross-env NODE_ENV=test ENV_FILE=../../config/staging/staging.env yarn zen:watch", + "watch:test": "cross-env NODE_ENV=test ENV_FILE=../../config/test/test.env yarn zen:watch", + "zen:watch": "zen watch -x" + }, + "dependencies": { + "@apollo/client": "~3.3.21", + "@cdm-logger/server": "^7.0.7", + "@common-stack/core": "0.1.11", + "@common-stack/server-core": "0.1.11", + "@graphql-tools/links": "^7.0.0", + "@sample-stack/core": "link:../../packages/sample-core", + "@sample-stack/counter-module-server": "link:../../packages-modules/counter/server", + "@sample-stack/platform-server": "link:../../packages/sample-platform/server", + "@sample-stack/store": "link:../../packages/sample-store", + "apollo-datasource": "^0.7.0", + "apollo-datasource-rest": "^0.8.0", + "apollo-errors": "^1.9.0", + "apollo-logger": "^0.3.3", + "apollo-server-cache-memcached": "^0.6.7", + "apollo-server-cache-redis": "^1.2.3", + "apollo-server-caching": "^0.5.3", + "apollo-server-core": "^2.21.1", + "apollo-server-express": "^2.21.1", + "apollo-server-plugin-response-cache": "^0.6.0", + "app-root-path": "^3.0.0", + "body-parser": "^1.19.0", + "cors": "^2.8.5", + "dataloader": "^2.0.0", + "dotenv": "^8.2.0", + "envalid": "^7.2.2", + "esm": "^3.2.25", + "express": "^4.17.1", + "graphql": "^14.7.0", + "graphql-bigint": "^1.0.0", + "graphql-nats-subscriptions": "^1.5.0", + "graphql-subscriptions": "^1.2.0", + "graphql-tools": "^6.0.0", + "graphql-type-json": "^0.3.1", + "inversify": "^5.0.1", + "ioredis": "^4.14.0", + "isomorphic-fetch": "^2.2.1", + "knex": "^0.20.4", + "lodash": "^4.17.15", + "moleculer": "^0.14.2", + "moleculer-zipkin": "0.2.2", + "mongoose": "^5.13.5", + "morgan": "^1.9.1", + "nats": "^1.3.2", + "react": "17.0.1", + "reflect-metadata": "^0.1.13", + "rxjs": "^6.5.3", + "rxjs-compat": "^6.5.3", + "subscriptions-transport-ws": "0.9.18", + "universal-cookie-express": "^4.0.1", + "ws": "^7.2.1" + }, + "devDependencies": { + "cross-env": "^6.0.3", + "pm2": "^4.2.1", + "rimraf": "^3.0.0" + }, + "peerDependencies": { + "@cdm-logger/core": "*", + "apollo-server-errors": "*", + "mongodb": "*" + }, + "typescript": { + "definition": "dist/main.d.ts" + } +} +\`\`\` + +## servers/frontend-server/package.json + +\`\`\`json +{ + "name": "sample-stack-frontend-server", + "version": "0.0.1", + "private": true, + "description": "Sample Client server", + "homepage": "https://github.com/cdmbase/fullstack-pro#readme", + "bugs": { + "url": "https://github.com/cdmbase/fullstack-pro/issues" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/cdmbase/fullstack-pro.git" + }, + "license": "MIT", + "author": "CDMBase LLC", + "main": "index.js", + "scripts": { + "prebuild": "yarn build:clean", + "build": "cross-env NODE_ENV=production zen build", + "build:clean": "rimraf dist .awcache", + "build:debug": "cross-env DEBUGGING=true NODE_ENV=production zen build", + "build:debug:verbose": "yarn build:debug -- -v", + "build:dev": "cross-env NODE_ENV=development zen build", + "docker:build": "cross-env NODE_OPTIONS='--max_old_space_size=4096' yarn build && docker build . -t $npm_package_name:$npm_package_version", + "docker:build:debug": "yarn build:debug && docker build . -t $npm_package_name:$npm_package_version", + "docker:run": "docker run --env-file ../../config/staging/docker-staging.env -p 3010:3010 -it $npm_package_name:$npm_package_version", + "jest": "./node_modules/.bin/jest", + "start": "cross-env NODE_ENV=production pm2-runtime dist/index.js", + "start:dev": "cross-env NODE_ENV=development ENV_FILE=../../config/development/dev.env node --harmony dist", + "start:staging": "cross-env NODE_ENV=staging ENV_FILE=../../config/staging/staging.env node --harmony dist", + "start:test": "cross-env NODE_ENV=test ENV_FILE=../../config/test/test.env node --harmony dist", + "test": "jest", + "test:debug": "npm test -- --runInBand", + "test:watch": "npm test -- --watch", + "watch": "cross-env NODE_ENV=development ENV_FILE=../../config/development/dev.env yarn zen:watch", + "watch:debug": "cross-env NODE_ENV=development ENV_FILE=../../config/development/dev.env yarn zen:watch -- -v", + "watch:ssr": "cross-env SSR=true && yarn watch", + "watch:staging": "cross-env ENV_FILE=../../config/staging/staging.env yarn zen:watch", + "watch:test": "cross-env ENV_FILE=../../config/test/test.env yarn zen:watch", + "zen:watch": "zen watch -x" + }, + "jest": { + "moduleFileExtensions": [ + "ts", + "tsx", + "js" + ], + "testRegex": "/__tests__/.*\\.(ts|tsx|js)$", + "transform": { + "\\.(ts|tsx)$": "/../../node_modules/ts-jest/preprocessor.js" + } + }, + "dependencies": { + "@apollo/client": "~3.3.21", + "@apollo/react-components": "^4.0.0", + "@apollo/react-hoc": "^4.0.0", + "@cdm-logger/client": "^7.0.8", + "@cdm-logger/server": "^7.0.7", + "@common-stack/client-core": "0.1.11", + "@common-stack/client-react": "0.1.11", + "@common-stack/core": "0.1.11", + "@common-stack/server-core": "0.1.11", + "@sample-stack/core": "link:../../packages/sample-core", + "@sample-stack/counter-module-browser": "link:../../packages-modules/counter/browser", + "@sample-stack/platform-browser": "link:../../packages/sample-platform/browser", + "apollo-link-debounce": "^2.1.0", + "apollo-link-logger": "^1.2.3", + "apollo-logger": "^0.3.3", + "browser-bunyan": "^1.6.3", + "classnames": "^2.2.6", + "connected-react-router": "^6.9.1", + "cors": "^2.8.5", + "dotenv": "^8.2.0", + "envalid": "^7.2.2", + "error-stack-parser": "^2.0.4", + "esm": "^3.2.25", + "express": "^4.17.1", + "fela": "11.6.0", + "fela-beautifier": "^11.6.0", + "fela-dom": "11.6.0", + "fela-font-renderer": "^5.0.25", + "fela-perf": "^11.6.0", + "fela-plugin-fallback-value": "^11.6.0", + "fela-plugin-logger": "^11.6.0", + "fela-plugin-lvha": "^5.0.16", + "fela-plugin-prefixer": "^11.6.0", + "fela-plugin-unit": "^11.6.0", + "fela-plugin-validator": "^11.6.0", + "fela-preset-web": "^11.6.0", + "graphql": "^14.7.0", + "graphql-tag": "^2.11.0", + "history": "^4.10.1", + "immutability-helper": "^3.0.1", + "inversify": "^5.0.1", + "isomorphic-fetch": "^2.2.1", + "js-cookie": "^2.2.1", + "lodash": "^4.17.15", + "ramda": "^0.26.1", + "react": "17.0.1", + "react-dom": "17.0.1", + "react-fela": "11.6.0", + "react-helmet": "^6.1.0", + "react-loadable": "^5.5.0", + "react-redux": "^7.1.3", + "react-router": "^5.2.1", + "react-router-config": "^5.1.1", + "react-router-dom": "^5.3.0", + "react-transition-group": "^4.3.0", + "redux": "^4.0.5", + "redux-logger": "^3.0.6", + "redux-observable": "^1.2.0", + "redux-persist": "^6.0.0", + "redux-thunk": "^2.3.0", + "reflect-metadata": "^0.1.13", + "reselect": "^4.0.0", + "rxjs": "^6.5.3", + "rxjs-compat": "^6.5.3", + "rxjs-hooks": "^0.5.2", + "serialize-javascript": "^4.0.0", + "sourcemapped-stacktrace": "^1.1.11", + "subscriptions-transport-ws": "0.9.18", + "universal-cookie-express": "^4.0.1" + }, + "devDependencies": { + "@babel/polyfill": "7.7.0", + "cross-env": "^6.0.3", + "pm2": "^4.2.1", + "raf": "3.4.1", + "rimraf": "^3.0.0" + }, + "peerDependencies": { + "body-parser": "*" + } +} +\`\`\` + +## servers/moleculer-server/README.md + +\`\`\`md +[![Moleculer](https://badgen.net/badge/Powered%20by/Moleculer/0e83cd)](https://moleculer.services) + +# servers + +## NPM scripts +- `yarn dev` - Start development mode (load all services locally with hot-reload & REPL) +- `yarn build`- Uses typescript to transpile service to javascript +- `npm start` - Start production mode (set `SERVICES` env variable to load certain services) (previous build needed) +- `yarn cli`: Start a CLI and connect to production. Don't forget to set production namespace with `--ns` argument in script +- `yarn ci` - Run continuous test mode with watching +- `npm test` - Run tests & generate coverage report +\`\`\` + +## servers/moleculer-server/package.json + +\`\`\`json +{ + "name": "sample-stack-moleculer-server", + "version": "0.0.1", + "private": true, + "description": "My Moleculer microservices project", + "keywords": [ + "microservices", + "moleculer" + ], + "author": "", + "scripts": { + "prebuild": "yarn build:clean", + "build": "cross-env NODE_ENV=production yarn build:dist", + "build:clean": "rimraf dist", + "build:debug": "cross-env DEBUGGING=true NODE_ENV=production yarn build:dist", + "build:dist": "webpack", + "build:watch": "yarn build:dist -- --watch", + "cli": "moleculer connect --config ./dist/moleculer.config.js", + "cli:dev": " cross-env ENV_FILE=../../config/development/dev.env yarn cli", + "cli:docker": " cross-env NODE_ENV=staging ENV_FILE=../../config/staging/docker-staging.env yarn cli", + "cli:staging": " cross-env NODE_ENV=staging ENV_FILE=../../config/staging/staging.env yarn cli", + "cli:test": " cross-env NODE_ENV=test ENV_FILE=../../config/test/test.env yarn cli", + "docker:build": "yarn build && docker build . -t $npm_package_name:$npm_package_version", + "docker:run": "docker run -it --env-file ../../config/staging/docker-staging.env $npm_package_name:$npm_package_version", + "jest": "./node_modules/.bin/jest", + "start": "node dist/index.js", + "start:dev": "cross-env ENV_FILE=../../config/development/dev.env nodemon dist", + "start:staging": "cross-env NODE_ENV=staging ENV_FILE=../../config/staging/staging.env node --harmony dist", + "start:test": "cross-env NODE_ENV=test ENV_FILE=../../config/test/test.env nodemon dist", + "pretest": "yarn build", + "test": "jest", + "test:debug": "npm test -- --runInBand", + "test:watch": "npm test -- --watch", + "watch": "yarn start:dev" + }, + "dependencies": { + "@cdm-logger/client": "^7.0.8", + "@cdm-logger/server": "^7.0.7", + "@common-stack/client-core": "0.1.11", + "@common-stack/core": "0.1.11", + "@common-stack/server-core": "0.1.11", + "@sample-stack/core": "link:../../packages/sample-core", + "@sample-stack/counter-module-server": "link:../../packages-modules/counter/server", + "@sample-stack/platform-server": "link:../../packages/sample-platform/server", + "apollo-datasource": "^0.7.0", + "apollo-logger": "^0.3.3", + "apollo-server-cache-memcached": "^0.6.7", + "apollo-server-cache-redis": "^1.2.3", + "apollo-server-caching": "^0.5.3", + "apollo-server-errors": "^2.4.2", + "dotenv": "^8.2.0", + "envalid": "^7.2.2", + "esm": "^3.2.25", + "graphql": "^14.7.0", + "graphql-bigint": "^1.0.0", + "graphql-nats-subscriptions": "^1.5.0", + "graphql-subscriptions": "^1.2.0", + "graphql-tag": "^2.11.0", + "graphql-type-json": "^0.3.1", + "helmet": "^3.21.2", + "inversify": "^5.0.1", + "inversify-logger-middleware": "^3.1.0", + "ioredis": "^4.14.1", + "iterall": "1.3.0", + "lodash": "^4.17.15", + "moleculer": "^0.14.2", + "moleculer-zipkin": "0.2.2", + "mongoose": "^5.13.5", + "nats": "^1.3.2", + "reflect-metadata": "^0.1.13" + }, + "devDependencies": { + "cross-env": "^6.0.3", + "pm2": "^4.2.1", + "rimraf": "^3.0.0" + }, + "peerDependencies": { + "@cdm-logger/core": "*" + }, + "engines": { + "node": ">= 8.x.x" + } +} + +\`\`\` + +## tools/templates/module/browser/package.json + +\`\`\`json +{ + "name": "@adminide-stack/$module$-browser", + "version": "0.0.1", + "description": "Sample core for higher packages to depend on", + "main": "lib/index.js", + "scripts": { + "jest": "./node_modules/.bin/jest", + "test": "cross-env ENV_FILE=../../config/test/test.env jest", + "test:debug": "npm test -- --runInBand", + "test:watch": "npm test -- --watch", + "prepublish": "yarn build", + "watch": "yarn build:lib:watch", + "build": "yarn build:clean && yarn build:lib", + "build:clean": "rimraf lib", + "build:lib": "webpack", + "build:lib:watch": "yarn build:lib -- --watch", + "schema:download": "./node_modules/.bin/apollo-codegen download-schema http://localhost:8080/graphql --output schema.json ", + "schema:generate": "./node_modules/.bin/apollo-codegen generate src/browser/graphql/**/*.gql --schema schema.json --target typescript --output src/browser/graphql/schema.ts", + "githubschema:download": "./node_modules/.bin/apollo-codegen download-schema https://api.github.com/graphql --output githubapi-schema.json --header 'Authorization: Bearer 0ec1b5e245de6061066262daf4aac2135fddd683'", + "githubschema:generate": "./node_modules/.bin/apollo-codegen generate src/browser/graphql/**/*.gql --schema githubapi-schema.json --target typescript --output src/browser/graphql/github-schema.ts" + }, + "jest": { + "testEnvironment": "node", + "roots": [ + "src" + ], + "modulePaths": [ + "node_modules" + ], + "moduleFileExtensions": [ + "ts", + "tsx", + "js", + "json" + ], + "transform": { + "\\.(ts|tsx)$": "/../../node_modules/ts-jest/preprocessor.js" + }, + "testRegex": "/__tests__/.*test*\\.(ts|tsx|js)$" + }, + "author": "CDMBase LLC", + "license": "ISC", + "devDependencies": { + "cross-env": "^5.2.0", + "jest": "^22.4.2", + "rimraf": "^2.6.1", + "webpack": "4.46.0" + }, + "dependencies": { + }, + "typings": "lib/index.d.ts", + "typescript": { + "definition": "lib/index.d.ts" + } +} + +\`\`\` + +## tools/templates/module/server/package.json + +\`\`\`json +{ + "name": "@adminide-stack/$module$-server", + "version": "0.0.1", + "description": "Sample core for higher packages to depend on", + "main": "lib/index.js", + "scripts": { + "jest": "./node_modules/.bin/jest", + "test": "cross-env ENV_FILE=../../config/test/test.env jest", + "test:debug": "npm test -- --runInBand", + "test:watch": "npm test -- --watch", + "prepublish": "yarn build", + "watch": "yarn build:lib:watch", + "build": "yarn build:clean && yarn build:lib", + "build:clean": "rimraf lib", + "build:lib": "webpack", + "build:lib:watch": "yarn build:lib -- --watch" + }, + "jest": { + "testEnvironment": "node", + "roots": [ + "src" + ], + "modulePaths": [ + "node_modules" + ], + "moduleFileExtensions": [ + "ts", + "tsx", + "js", + "json" + ], + "transform": { + "\\.(ts|tsx)$": "/../../node_modules/ts-jest/preprocessor.js" + }, + "testRegex": "/__tests__/.*test*\\.(ts|tsx|js)$" + }, + "author": "CDMBase LLC", + "license": "ISC", + "devDependencies": { + "apollo": "^2.9.0", + "cross-env": "^5.2.0", + "jest": "^22.4.2", + "rimraf": "^2.6.1", + "webpack": "4.46.0" + }, + "typings": "lib/index.d.ts", + "typescript": { + "definition": "lib/index.d.ts" + }, + "dependencies": { + } +} + +\`\`\` + +## .eslintrc.js + +\`\`\`js +const common = { + env: { + node: true, + es6: true, + 'jest/globals': true, + }, + plugins: ['prettier', 'jest', 'markdown'], + extends: ['airbnb-base', 'prettier', 'plugin:jest/all'], + rules: { + 'prettier/prettier': 'error', + 'jest/no-disabled-tests': 'warn', + 'jest/no-focused-tests': 'error', + 'jest/no-identical-title': 'error', + 'jest/prefer-to-have-length': 'warn', + 'jest/valid-expect': 'error', + 'jest/expect-expect': 'off', + 'jest/prefer-expect-assertions': 'off', + 'jest/no-test-return-statement': 'off', + 'import/prefer-default-export': 'off', + 'import/extensions': 'off', + 'no-console': 'off', + 'no-iterator': 'off', + 'no-restricted-syntax': 'off', + 'no-await-in-loop': 'off', + 'consistent-return': 'off', + 'no-shadow': 'off', + 'no-unused-vars': 'off', + }, +}; + +module.exports = { + root: true, + overrides: [ + { + /* + eslint-plugin-markdown only finds javascript code block snippet. + For specific spec, refer to https://github.com/eslint/eslint-plugin-markdown + */ + files: ['**/*.js', '**/*.md'], + ...common, + }, + { + files: ['**/*.ts'], + parser: '@typescript-eslint/parser', + env: common.env, + plugins: [...common.plugins, '@typescript-eslint'], + extends: [ + ...common.extends, + 'plugin:@typescript-eslint/recommended', + 'plugin:import/errors', + 'plugin:import/warnings', + 'plugin:import/typescript', + ], + rules: { + ...common.rules, + '@typescript-eslint/explicit-function-return-type': 'off', + }, + settings: { + 'import/resolver': { + node: { + extensions: ['.js', '.jsx', '.ts', '.d.ts', '.tsx', '.graphql', '.gql'], + }, + typescript: { + // alwaysTryTypes: true, + // paths: './tsconfig.json', + }, + }, + }, + }, + ], +}; + +\`\`\` + +## .markdownlint.json + +\`\`\`json +{ + "default": true, + "MD013": false, + "MD042": false, + "MD033": false +} +\`\`\` + +## .vscode/extensions.json + +\`\`\`json +{ +"recommendations": [ + "dbaeumer.vscode-eslint" +] +} +\`\`\` + +## .vscode/launch.json + +\`\`\`json +{ + // Use IntelliSense to learn about possible Node.js debug attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + // Name of configuration; appears in the launch configuration drop down menu. + "name": "Run jest", + // Type of configuration. Possible values: "node", "mono". + "type": "node", + "request": "launch", + // Workspace relative or absolute path to the program. + "program": "${workspaceRoot}/node_modules/jest-cli/bin/jest.js", + // Automatically stop program after launch. + "stopOnEntry": false, + // Command line arguments passed to the program. + "args": [ + "--runInBand", + "--testPathPattern", + "index.test" + ], + // Workspace relative or absolute path to the working directory of the program being debugged. Default is the current workspace. + "cwd": "${workspaceRoot}/packages/sample-graphql-schema", + // Workspace relative or absolute path to the runtime executable to be used. Default is the runtime executable on the PATH. + "runtimeExecutable": null, + // Environment variables passed to the program. + "env": { + "NODE_ENV": "development" + }, + "console": "integratedTerminal", + "sourceMaps": false + } + ] +} +\`\`\` + +## .vscode/settings.json + +\`\`\`json +{ + "typescript.tsdk": "node_modules/typescript/lib" +} +\`\`\` + +## babel.config.js + +\`\`\`js +module.exports = { + compact: false, + presets: [ + '@babel/preset-typescript', + '@babel/preset-react', + ['@babel/preset-env', { modules: 'commonjs', loose: true }], + ], + plugins: [ + 'react-hot-loader/babel', + '@babel/plugin-transform-modules-commonjs', + '@babel/plugin-transform-destructuring', + '@babel/plugin-transform-for-of', + '@babel/plugin-transform-regenerator', + '@babel/plugin-transform-runtime', + '@babel/plugin-syntax-dynamic-import', + '@babel/plugin-proposal-class-properties', + ['@babel/plugin-proposal-decorators', { legacy: true }], + '@babel/plugin-proposal-object-rest-spread', + // ['styled-components', { ssr: true }], + ['import', { libraryName: '@ant-design/react-native' }], + ], + env: { + production: { + compact: true, + }, + }, +}; + +\`\`\` + +## commitlint.config.js + +\`\`\`js +module.exports = { + extends: ['@commitlint/config-conventional'], + rules: { + 'header-max-length': [0, 'always', 100], // corresponding to maxHeaderWidth of commitizen + }, +}; + +\`\`\` + +## config/development/settings.json + +\`\`\`json +{ + "database": { + "client": "sqlite3", + "connection": { + "filename": "dev-db.sqlite3" + }, + "useNullAsDefault": true + } +} +\`\`\` + +## husky.config.js + +\`\`\`js +module.exports = { + hooks: { + 'pre-commit': 'lint-staged', + //'pre-push': 'yarn test', // when production ready + }, +}; +\`\`\` + +## jest-mongodb-config.js + +\`\`\`js +module.exports = { + mongodbMemoryServerOptions: { + instance: { + dbName: 'jest' + }, + binary: { + version: '4.0.12', // Version of MongoDB + skipMD5: true + }, + autoStart: false + } + }; +\`\`\` + +## jest-transform-i18next.js + +\`\`\`js +const fs = require('fs'); +const path = require('path'); + +module.exports = { + process() { + const pathname = arguments[1]; + const dir = path.dirname(pathname); + const locales = fs.readdirSync(dir); + const result = {}; + for (const locale of locales) { + if (fs.statSync(path.join(dir, locale)).isDirectory()) { + const localeFiles = fs.readdirSync(path.join(dir, locale)); + for (const localeFile of localeFiles) { + if (localeFile.indexOf('.json') >= 0) { + result[locale] = JSON.parse(fs.readFileSync(path.join(dir, locale, localeFile), 'utf8')); + } + } + } + } + return { + code: `module.exports = ${JSON.stringify(result)};`, + }; + }, +}; + +\`\`\` + +## jest.config.base.js + +\`\`\`js +/* eslint-disable import/no-extraneous-dependencies */ +/* eslint-disable @typescript-eslint/no-var-requires */ +const { defaults } = require('jest-config'); + +module.exports = { + testEnvironment: 'node', + setupFiles: [ + // needed for UI to mock canvas load + // "jest-canvas-mock" + ], + preset: 'ts-jest', + testMatch: null, + testRegex: '.*test*\\.(ts|tsx|js)$', + testPathIgnorePatterns: ['/node_modules/', '/dist/'], + transform: { + '\\.(gql)$': 'jest-transform-graphql', + '\\.(graphql|graphqls)$': 'jest-raw-loader', + '\\.(ts|tsx)$': 'ts-jest', + // Use our custom transformer only for the *.js and *.jsx files + '\\.(js|jsx)?$': './transform.js', + // future need to test with + // "^.+\\.(js|jsx|ts|tsx)$": "./transform.js", + '.+\\.(css|styl|less|sass|scss)$': 'jest-css-modules-transform', + }, + roots: ['packages', 'packages-modules', 'servers'], + moduleFileExtensions: [ + 'tsx', // TODO can be removed as default extension includes + 'ts', // TODO can be removed as default extension includes + ...defaults.moduleFileExtensions, + 'js', // TODO can be removed as default extension includes + 'jsx', // TODO can be removed as default extension includes + 'json', + 'gql', + 'graphql', + ], + moduleNameMapper: { + '^__mocks__/(.*)$': '/../../__mocks__/$1', + // we'll use commonjs version of lodash for tests 👌 + // because we don't need to use any kind of tree shaking right?! + '^lodash-es$': '/node_modules/lodash/index.js', + }, + transformIgnorePatterns: ['/node_modules/(?!(babel-runtime|antd)).*/', '/node_modules/(?!lodash-es/.*)'], + clearMocks: true, + verbose: true, + // projects: [''], // TODO need to test with it https://github.com/bryan-hunter/yarn-workspace-lerna-monorepo/blob/master/jest.config.base.js + coverageDirectory: '/coverage/', + coveragePathIgnorePatterns: ['/build/', '/lib/', '/dist/', '/node_modules/'], + globals: { + __BACKEND_URL__: 'http://localhost:3010', + __GRAPHQL_URL__: 'http://localhost:8085/graphql', + 'ts-jest': { + // tsConfig: "/src/__tests__/tsconfig.json", + // https://github.com/kulshekhar/ts-jest/issues/766 + diagnostics: { + warnOnly: true, + }, + // "skipBabel": true + }, + }, +}; + +\`\`\` + +## jest.config.js + +\`\`\`js +const merge = require('merge') +const baseConfig = require('./jest.config.base'); +const mongodbConfig = require('./jest.config.mongodb') +module.exports = merge.recursive( + baseConfig, + mongodbConfig, + { + globals: { + + } + }, + // https://baltuta.eu/posts/typescript-lerna-monorepo-more-tools + // { + // roots: [''], + // projects: [ + // '/packages/ui', + // '/packages/api', + // '/packages/diceroll' + // ], + // } + +); +\`\`\` + +## jest.config.mongodb.js + +\`\`\`js +module.exports = { + preset: '@shelf/jest-mongodb', +}; + +\`\`\` + +## lerna.json + +\`\`\`json +{ + "changelog": { + "repo": "cdmbase/lerna-bootstrap", + "labels": { + "tag: breaking change": ":boom: Breaking Change", + "tag: new feature": ":rocket: New Feature", + "tag: bug fix": ":bug: Bug Fix", + "tag: polish": ":nail_care: Polish", + "tag: documentation": "Documentation", + "tag: internal": ":house: Internal" + } + }, + "command": { + "publish": { + "registry": "https://registry.npmjs.org", + "graphType": "all", + "allowBranch": [ + "publish", + "devpublish" + ], + "message": "chore(release): publish", + "ignoreChanges": [ + "**/__fixtures__/**", + "**/__tests__/**", + "**/*.md", + "**/example/**" + ] + }, + "version": { + "allowBranch": [ + "master", + "develop", + "publish", + "devpublish" + ], + "conventionalCommits": true, + "message": "chore: release package(s)" + } + }, + "npmClient": "yarn", + "useWorkspaces": true, + "packages": [ + "packages-modules/**", + "packages/**", + "servers/*", + "portable-devices/*" + ], + "version": "0.0.0" +} +\`\`\` + +## lint-staged.config.js + +\`\`\`js +module.exports = { + '*.{js,jsx,ts,tsx,json,md}': ['prettier --write', 'git add'], + // '*.{ts,tsx}': ['eslint --fix'], // this can be tested +}; +\`\`\` + +## packages/sample-core/jest.config.js + +\`\`\`js +const base = require('../../jest.config.base'); +const packageJson = require('./package'); + +module.exports = { + ...base, + name: packageJson.name, + displayName: packageJson.name, +}; + +\`\`\` + +## packages/sample-core/src/index.ts + +\`\`\`ts +export interface PersonType { + name: string; + id: string; + sex: string; + matches: [PersonType]; +} +export interface SomeType { + testInt: number; + testFloat: number; + fixedString: string; +} + +\`\`\` + +## packages/sample-core/tsconfig.json + +\`\`\`json +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "./src", + "outDir": "../lib", + "declarationDir": "lib" + }, + "exclude": [ + "node_modules", + "lib", + "dist", + "webpack.config.js" + ] +} +\`\`\` + +## packages/sample-core/webpack.config.js + +\`\`\`js +const nodeExternals = require('webpack-node-externals'); +const webpack = require('webpack'); +const path = require('path'); +const fs = require('fs'); + +const webpackOpts = { + mode: 'development', + entry: './src/index.ts', + target: 'node', + output: { + path: path.join(__dirname, 'lib'), + filename: 'index.js', + libraryTarget: 'commonjs2', + }, + resolve: { + extensions: ['.ts', '.js'], + }, + plugins: [ + new webpack.LoaderOptionsPlugin({ + options: { + test: /\.ts$/, + ts: { + compiler: 'typescript', + configFile: 'tsconfig.json', + }, + tslint: { + emitErrors: true, + failOnHint: true, + }, + }, + }), + ], + devtool: 'source-map', + module: { + rules: [ + { + test: /\.ts$/, + loaders: 'ts-loader', + }, + ], + }, + externals: [ + nodeExternals({ modulesDir: '../../node_modules' }), + nodeExternals(), + ], +}; + +module.exports = webpackOpts; + +\`\`\` + +## packages/sample-platform/browser/jest.config.js + +\`\`\`js +const base = require('../../../jest.config.base'); +const packageJson = require('./package'); + +module.exports = { + ...base, + name: packageJson.name, + displayName: packageJson.name, +}; + +\`\`\` + +## packages/sample-platform/browser/src/api.ts + +\`\`\`ts +// Simulate a flaky API around otherwise an otherwise synchronous `f()`. +const flakify = (f: () => T): Promise => + new Promise((resolve, reject) => + // We'll always take 200 * (1d10 + 1) ms to respond + window.setTimeout(() => { + try { + // And ~20% of the time we'll fail + if (Math.random() < 0.2) { + throw new Error('Failed arbitrarily'); + } + + resolve(f()); + } catch (e) { + return reject(e); + } + }, 200 + Math.random() * 2000), + ); + +export type Api = { + save(x: { value: number }): Promise; + load(): Promise<{ value: number }>; +}; + +export const api: Api = { + save: (counter) => + flakify(() => { + localStorage.setItem('__counterValue', counter.value.toString()); + return null; + }), + load: () => + flakify(() => { + const value = parseInt(localStorage.getItem('__counterValue'), 10); + return { value }; + }), +}; + +\`\`\` + +## packages/sample-platform/browser/src/components/Counter.tsx + +\`\`\`tsx +import * as React from 'react'; + +export interface ICounterProps { + label: string; + counter: { value: number }; + isSaving: boolean; + isLoading: boolean; + error: string; + increment: (n: number) => void; + save: (n: number) => void; + load: () => void; +} + +export interface ICounterState { +} + +export class CounterComponent extends React.Component { + + private _onClickIncrement = (e: React.SyntheticEvent) => { + e.preventDefault(); + this.props.increment(1); + } + + private _onClickSave = (e: React.SyntheticEvent) => { + e.preventDefault(); + if (!this.props.isSaving) { + this.props.save(this.props.counter.value); + } + } + + private _onClickLoad = (e: React.SyntheticEvent) => { + e.preventDefault(); + if (!this.props.isLoading) { + this.props.load(); + } + } + + public render(): JSX.Element { + const { counter, label, isSaving, isLoading, error } = this.props; + return ( +
+ {label} +
{JSON.stringify({ counter, isSaving, isLoading }, null, 2)}
+ + + + {error ?
{error}
: null} +
); + } +} + + + +\`\`\` + +## packages/sample-platform/browser/src/components/NavBar.tsx + +\`\`\`tsx +import * as React from 'react'; +import { Link } from 'react-router-dom'; + +const NavBar = () => ( +
+
Home Hello Counter
+
+); + +export { NavBar }; + +\`\`\` + +## packages/sample-platform/browser/src/components/index.ts + +\`\`\`ts +export * from './Counter'; + +\`\`\` + +## packages/sample-platform/browser/src/containers/Clock.tsx + +\`\`\`tsx + +\`\`\` + +## packages/sample-platform/browser/src/containers/Counter.tsx + +\`\`\`tsx +import { + incrementCounter, + loadCount, + saveCount, + Action, + Store, +} from '../redux'; +import { connect } from 'react-redux'; +import * as redux from 'redux'; +import { CounterComponent, ICounterProps } from '../components'; + +export type CounterOwnProps = { + label: string; + store?: Store.Sample; +}; + +const mapStateToProps = (state: Store.Sample) => ({ + counter: state['@sample-stack/counter'], + isSaving: state['@sample-stack/isSaving'], + isLoading: state['@sample-stack/isLoading'], + error: state['@sample-stack/error'], + +}); + +const mapDispatchToProps = (dispatch: Function) => ({ + increment: (n: number) => + dispatch(incrementCounter(n)), + load: () => + dispatch(loadCount(null)), + save: (value: number) => + dispatch(saveCount({ value })), +}); + +// const stateProps = mapStateToProps; +// const dispatchProps = returntypeof(mapDispatchToProps); +// type StateProps = typeof stateProps; +// type DispatchProps = typeof dispatchProps; + +// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/8787 +export const Counter = +connect(mapStateToProps, mapDispatchToProps)(CounterComponent); + + +\`\`\` + +## packages/sample-platform/browser/src/containers/Loading.tsx + +\`\`\`tsx +import * as React from 'react'; + +interface Props {} + +const Loading: React.SFC = () => ( +
Loading
+); + +// export const displayLoadingState = branch( +// props => props.data.loading, +// renderComponent(Loading) +// ); + +\`\`\` + +## packages/sample-platform/browser/src/containers/PersonList.tsx + +\`\`\`tsx +import * as React from 'react'; +import { graphql } from '@apollo/react-hoc'; +import compose from 'lodash/flowRight'; +import { PERSONS_QUERY } from '../graphql'; + +export interface IPersonListProps { + persons; +} +const PersonListComponent: React.SFC = ({ persons }) => ( +
+

Persons:

+ {persons && persons.map((person, i) =>
{person.name}
)} +
+); + +export const PersonList: React.ComponentClass<{}> = + compose( + graphql<{}, any, {}, {}>(PERSONS_QUERY), + // flattenProp('data'), + )(PersonListComponent); + +\`\`\` + +## packages/sample-platform/browser/src/containers/ServerCounter.tsx + +\`\`\`tsx +import * as React from 'react'; +import update from 'immutability-helper'; +import { graphql } from '@apollo/react-hoc'; +import compose from 'lodash/flowRight'; +import { CounterComponent, ICounterProps } from '../components'; +import { COUNT_SUBSCRIPTION, COUNT_QUERY, ADD_COUNT_MUTATION,} from '../graphql'; +import { logger } from '@cdm-logger/client'; +type SubscriptionProps = { + subscribeToMore: Function; +}; + +// class WithSubscriptionCounter extends React.Component { + +// public subscription; + +// constructor(props) { +// super(props); +// } + +// public componentWillReceiveProps(nextProps) { +// if (!nextProps.loading) { +// if (this.subscription) { +// this.subscription(); +// this.subscription = null; +// } + +// // Subscribe or re-subscribe +// if (!this.subscription) { +// this.subscribeToCount(); +// } +// } +// } + +// public componentWillUnmount() { +// if (this.subscription) { +// this.subscription(); +// } +// } + +// private subscribeToCount() { +// const { subscribeToMore } = this.props; +// this.subscription = subscribeToMore({ +// document: onCountUpdated, +// variables: {}, +// updateQuery: (prev, { subscriptionData: { data: { countUpdated: { amount } } } }) => { +// return update(prev, { +// count: { +// amount: { +// $set: amount, +// }, +// }, +// }); +// }, +// }); +// } + +// public render() { +// const { ICounerProps } = this.props; +// return ; +// } +// } + + +// save(amount) { +// return () => mutate({ +// variables: { amount }, +// updateQueries, +// }); +// }, + +type countOptions = any & { + countData: any; +}; + +const updateQueries = { + counter: (prev, { mutationResult }) => { + const newAmount = mutationResult.data.addCount.amount; + return update(prev, { + count: { + amount: { + $set: newAmount, + }, + }, + }); + }, +}; + +export const CounterWithApollo: React.ComponentClass = (compose( + graphql<{}, any, {}, {}>(ADD_COUNT_MUTATION, { + props: ({ ownProps, mutate }) => ({ + save: (amount) => { + return () => mutate({ + variables: { amount }, + // optimisticResponse: { + // __typename: 'Mutation', + + // }, + }); + }, + }), + }), + graphql<{}, any, {}, {}>(ADD_COUNT_MUTATION, { + props: ({ ownProps, mutate }) => ({ + increment: (amount) => { + return () => mutate({ + variables: { amount }, + // updateQueries, + }); + }, + }), + }), +)(graphql(COUNT_QUERY, { + name: 'countData', + props: ({ countData }: any) => { + const newlog = logger.child({ childName: 'UIController' }); + newlog.debug('count data : (%j)', countData); + return { + subscribeToCount: params => { + // logger.debug('count subscript data (%j)', params); + return countData.subscribeToMore({ + document: COUNT_SUBSCRIPTION, + variables: {}, + updateQuery: (prev: any, { subscriptionData }) => { + const payload = subscriptionData.data && subscriptionData.data.subscribeToWorkspace; + if (!payload) { + return prev; + } + return payload; + }, + }); + }, + counter: countData.count && countData.count.amount, + isLoading: countData.loading, + isSaving: false, + load: () => countData.count.amount, + error: countData.error, + }; + }, +})(CounterComponent as any)) +); + +\`\`\` + +## packages/sample-platform/browser/src/containers/__tests__/ApolloProvider.test.tsx + +\`\`\`tsx +// import * as React from 'react'; +// import { shallow } from 'enzyme'; +// import { createStore } from 'redux'; +// import * as PropTypes from 'prop-types'; + +// declare function require(name: string); +// import * as TestUtils from 'react-addons-test-utils'; + +// import ApolloClient from 'apollo-client'; +// import { ApolloProvider } from 'react-apollo'; +// import { InMemoryCache } from 'apollo-cache-inmemory'; +// import { ApolloLink, Observable } from 'apollo-link'; +// import { addTypenameToDocument } from 'apollo-utilities'; + +// interface ChildContext { +// store: Object; +// client: Object; +// } + +// describe(' Component', () => { + +// class Child extends React.Component { +// public static contextTypes: React.ValidationMap = { +// client: PropTypes.object.isRequired, +// store: PropTypes.object.isRequired, +// }; + +// public context: ChildContext; + +// public render() { +// return
; +// } +// } + +// const cache = new InMemoryCache(); +// const client = new ApolloClient(); +// const store = createStore(() => ({})); + +// it('should render children components', () => { +// const wrapper = shallow( +// +//
+// , +// ); + +// expect(wrapper.contains(
)).toBe(true); +// }); +// }); + +\`\`\` + +## packages/sample-platform/browser/src/containers/__tests__/Counter.test.tsx + +\`\`\`tsx +// import 'jest'; +// import * as React from 'react'; +// import { shallow, mount } from 'enzyme'; +// import * as Sinon from 'sinon'; +// import * as TestUtils from 'react-dom/test-utils'; +// import { createStore, combineReducers } from 'redux'; +// import configureStore from 'redux-mock-store'; +// import { Counter } from '../Counter'; +// import { reducers, Store } from '@sample-stack/platform-browser'; +// import { Provider } from 'react-redux'; +// import './setup'; + + +// describe('components/Counter', () => { +// it('renders', () => { +// const store: any = createStore(combineReducers(reducers)); + +// expect(shallow( +// , +// ).shallow()).toMatchSnapshot(); +// }); + +// describe('COUNTER --- react-redux clicking "increment"', () => { +// let counter; +// let store; + +// beforeEach(() => { +// store = createStore(combineReducers(reducers)); +// counter = mount( +// +// +// , +// ); +// }); + + +// it('+++ check Props after increments counter', () => { +// const increment = counter.find('button').first(); +// increment.simulate('click'); +// increment.simulate('click'); +// increment.simulate('click'); + + +// const getText = () => counter.find('pre').text(); +// expect(JSON.parse(getText()).counter.value).toBe(3); + +// }); +// }); +// }); + +\`\`\` + +## packages/sample-platform/browser/src/containers/__tests__/PersonList.test.tsx + +\`\`\`tsx +// import 'jest'; +// import * as React from 'react'; +// import { shallow, mount } from 'enzyme'; +// import ApolloClient from 'apollo-client'; +// import { MockedProvider } from 'react-apollo/test-utils'; +// import './setup'; + +// declare function require(name: string); +// import * as TestUtils from 'react-addons-test-utils'; +// import { createStore, combineReducers, applyMiddleware } from 'redux'; +// import gql from 'graphql-tag'; + +// import { PersonList } from '../PersonList'; +// import { reducers, Store } from '@sample-stack/platform-browser'; +// import { ApolloProvider, graphql, createNetworkInterface } from 'react-apollo'; +// import { database } from '@sample-stack/graphql-schema'; +// import { mockNetworkInterface } from 'apollo-test-utils'; +// import { PERSONS_QUERY } from '@sample-stack/platform-browser'; + +// describe('components/PersonList', () => { +// it('renders correctly', (done) => { +// const networkInterface = mockNetworkInterface({ +// request: { query: PERSONS_QUERY, variables: {} }, result: { data: database.persons }, +// }); + +// const client = new ApolloClient({ +// networkInterface: networkInterface, +// }); + + +// const store = createStore(combineReducers({ +// ...reducers, +// apollo: client.reducer(), +// }), applyMiddleware(client.middleware())); + + +// expect(mount( +// +// +// , +// )).toMatchSnapshot(); +// }); +// }); + +\`\`\` + +## packages/sample-platform/browser/src/containers/__tests__/redux.test.tsx + +\`\`\`tsx +// import * as React from 'react'; +// import * as renderer from 'react-test-renderer'; +// import { mount, shallow } from 'enzyme'; +// import { createStore, combineReducers, applyMiddleware } from 'redux'; +// import { connect } from 'react-redux'; +// import { print } from 'graphql'; +// import gql from 'graphql-tag'; + +// declare function require(name: string); + +// import ApolloClient from 'apollo-client'; +// import { ApolloProvider, graphql } from 'react-apollo'; +// import { mockNetworkInterface } from 'react-apollo/test-utils'; + +// describe('redux integration', () => { +// it('updates child props on state change', (done) => { +// const query = gql`query people($first: Int) { allPeople(first: $first) { people { name } } }`; +// const data = { allPeople: { people: [{ name: 'Luke Skywalker' }] } }; +// const variables = { first: 1 }; + +// const data2 = { allPeople: { people: [{ name: 'Leia Skywalker' }] } }; +// const variables2 = { first: 2 }; + +// const networkInterface = mockNetworkInterface( +// { request: { query, variables }, result: { data } }, +// { request: { query, variables: variables2 }, result: { data: data2 } }, +// ); + + +// const client = new ApolloClient({ networkInterface, addTypename: false }); +// let wrapper; + +// function counter(state = 1, action) { +// switch (action.type) { +// case 'INCREMENT': +// return state + 1; +// default: +// return state; +// } +// } + +// // Typscript workaround +// const apolloReducer = client.reducer() as () => any; + +// const store = createStore( +// combineReducers({ +// counter, +// apollo: apolloReducer, +// }), +// applyMiddleware(client.middleware()), +// ); + + +// class Component extends React.Component { +// public componentWillReceiveProps(nextProps) { +// // trigger redux action +// if (nextProps.first === 1) { +// this.props.dispatch({ type: 'INCREMENT' }); +// } + +// if (nextProps.first === 2) { +// if (nextProps.data.loading) { return; } +// expect(nextProps.data.allPeople.people).toContain(data2.allPeople.people); +// done(); +// } + +// } +// public render() { +// return null; +// } +// } +// const Container: React.ComponentClass<{}> = compose( +// connect((state) => ({ first: state.counter })), +// graphql(query), +// flattenProp('data'), +// pure, +// )(Component); + +// wrapper = renderer.create( +// +// +// , +// ); +// }); + +// }); + +\`\`\` + +## packages/sample-platform/browser/src/containers/__tests__/setup.ts + +\`\`\`ts +/* setup.js */ + +const { jsdom } = require('jsdom'); + +const exposedProperties = ['window', 'navigator', 'document']; + +(global).document = jsdom(''); +(global).window = document.defaultView; +Object.keys(document.defaultView).forEach((property) => { + if (typeof global[property] === 'undefined') { + exposedProperties.push(property); + global[property] = document.defaultView[property]; + } +}); + +(global).navigator = { + userAgent: 'node.js', +}; + +\`\`\` + +## packages/sample-platform/browser/src/containers/__tests__/utils.ts + +\`\`\`ts +/** + * Call this helper inside async test to let promises finish, because they are allways async. + */ +export const nextTick = () => + new Promise((resolve) => setTimeout(resolve, 200)); + +\`\`\` + +## packages/sample-platform/browser/src/containers/index.ts + +\`\`\`ts +export * from './Counter'; +export * from './PersonList'; +// export * from './Loading'; +export * from './ServerCounter'; + +\`\`\` + +## packages/sample-platform/browser/src/graphql/index.ts + +\`\`\`ts +export * from './mutations'; +export * from './queries'; +export * from './subscriptions'; + +\`\`\` + +## packages/sample-platform/browser/src/graphql/mutations/index.ts + +\`\`\`ts +export const ADD_PERSON_MUTATION = require('./addPerson'); +export const ADD_COUNT_MUTATION = require('./addCount'); + +\`\`\` + +## packages/sample-platform/browser/src/graphql/queries/index.ts + +\`\`\`ts +export const PERSON_QUERY = require('./person'); +export const PERSONS_QUERY = require('./persons'); +export const COUNT_QUERY = require('./count'); + +\`\`\` + +## packages/sample-platform/browser/src/graphql/subscriptions/index.ts + +\`\`\`ts +export const COUNT_SUBSCRIPTION = require('./count'); + +\`\`\` + +## packages/sample-platform/browser/src/index.ts + +\`\`\`ts +import PlatformModule from './module'; +export * from './graphql'; +export * from './components'; +export * from './containers'; + + +export default PlatformModule; + +\`\`\` + +## packages/sample-platform/browser/src/inversify-containers/index.ts + +\`\`\`ts +export * from './module'; + +\`\`\` + +## packages/sample-platform/browser/src/inversify-containers/module.ts + +\`\`\`ts +import { ContainerModule, interfaces, Container } from 'inversify'; + +export const platformModule: () => interfaces.ContainerModule = () => + new ContainerModule((bind: interfaces.Bind) => {}); + +\`\`\` + +## packages/sample-platform/browser/src/module.ts + +\`\`\`ts +import { Feature } from '@common-stack/client-react'; + +import { interfaces } from 'inversify'; +import { ApolloClient } from '@apollo/client'; +import { ClientTypes as BrowserTypes } from '@common-stack/client-core'; +import { platformModule } from './inversify-containers'; + +const platformServiceGen = (container: interfaces.Container) => ({ + apolloClient: container.get>(BrowserTypes.ApolloClient), + cache: container.get(BrowserTypes.InMemoryCache), + utility: container.get(BrowserTypes.UtilityClass), +}); +export default new Feature({ + createContainerFunc: platformModule, + createServiceFunc: platformServiceGen, +}); + +\`\`\` + +## packages/sample-platform/browser/src/redux/__mocks__/api.ts + +\`\`\`ts +export const api = { + save: jest.fn(), + load: jest.fn(), +}; + +\`\`\` + +## packages/sample-platform/browser/src/redux/actions/__tests__/sampleActions.test.ts + +\`\`\`ts +import 'jest'; + +import { createStore } from 'redux'; +import * as apiExports from '../../api'; +import * as actions from '../sampleActions'; + +jest.mock('../../api'); + +const api: jest.Mocked = apiExports.api as any; + +describe('actions', () => { + const store = () => { + const reducer = jest.fn(); + const { dispatch } = createStore(reducer); + reducer.mockReset(); // ignore @@redux/INIT + return { dispatch, reducer }; + }; + + const eventually = (assertFn) => + new Promise((resolve, reject) => { + setTimeout(() => { + try { + assertFn(); + } catch (e) { + return reject(e); + } + resolve(); + }, 1); + }); + + const expectTypes = (reducer, types) => () => + expect(reducer.mock.calls.map((x) => x[1].type)).toEqual(types); + + describe('.saveCount', () => { + beforeEach(() => { + api.save.mockReturnValue(Promise.resolve(null)); + }); + + it('sends an API request', () => { + actions.saveCount({ value: 14 })(jest.fn()); + expect(api.save.mock.calls).toHaveLength(1); + }); + + describe('when API request succeeds', () => { + it('dispatches @@sample-stack/SAVE_COUNT_SUCCESS', () => { + const { dispatch, reducer } = store(); + actions.saveCount({ value: 14 })(dispatch); + return eventually( + expectTypes(reducer, [ + '@@sample-stack/SAVE_COUNT_REQUEST', + '@@sample-stack/SAVE_COUNT_SUCCESS', + ]), + ); + }); + }); + + describe('when API request fails', () => { + beforeEach(() => { + api.save.mockReturnValue( + Promise.reject(new Error('something terrible happened')), + ); + }); + + it('dispatches @@sample-stack/SAVE_COUNT_ERROR', () => { + const { dispatch, reducer } = store(); + actions.saveCount({ value: 14 })(dispatch); + return eventually( + expectTypes(reducer, [ + '@@sample-stack/SAVE_COUNT_REQUEST', + '@@sample-stack/SAVE_COUNT_ERROR', + ]), + ); + }); + + it('includes error message with @@sample-stack/SAVE_COUNT_ERROR', () => { + const { dispatch, reducer } = store(); + actions.saveCount({ value: 14 })(dispatch); + return eventually(() => { + expect(reducer.mock.calls[1][1].error.message).toEqual( + 'something terrible happened', + ); + }); + }); + + it('includes request with @@sample-stack/SAVE_COUNT_ERROR for convenience', () => { + const { dispatch, reducer } = store(); + actions.saveCount({ value: 14 })(dispatch); + return eventually(() => { + expect(reducer.mock.calls[1][1].request).toEqual({ + value: 14, + }); + }); + }); + }); + }); + + describe('.loadCount', () => { + beforeEach(() => { + api.load.mockReturnValue(Promise.resolve({ value: 14 })); + }); + + it('sends an API request', () => { + actions.loadCount(null)(jest.fn()); + expect(api.load.mock.calls).toHaveLength(1); + }); + + describe('when API request succeeds', () => { + it('dispatches @sample-stack/LOAD_COUNT_SUCCESS', () => { + const { dispatch, reducer } = store(); + actions.loadCount(null)(dispatch); + return eventually( + expectTypes(reducer, [ + '@@sample-stack/LOAD_COUNT_REQUEST', + '@@sample-stack/LOAD_COUNT_SUCCESS', + ]), + ); + }); + + it('includes new value with LOAD_COUNT_SUCCESS', () => { + const { dispatch, reducer } = store(); + actions.loadCount(null)(dispatch); + return eventually(() => { + expect(reducer.mock.calls[1][1].response).toEqual({ + value: 14, + }); + }); + }); + }); + + describe('when API request fails', () => { + beforeEach(() => { + api.load.mockReturnValue( + Promise.reject(new Error('something terrible happened')), + ); + }); + + it('dispatches @@sample-stack/LOAD_COUNT_ERROR', () => { + const { dispatch, reducer } = store(); + actions.loadCount(null)(dispatch); + return eventually( + expectTypes(reducer, [ + '@@sample-stack/LOAD_COUNT_REQUEST', + '@@sample-stack/LOAD_COUNT_ERROR', + ]), + ); + }); + + it('includes error message with @@sample-stack/LOAD_COUNT_ERROR', () => { + const { dispatch, reducer } = store(); + actions.loadCount(null)(dispatch); + return eventually(() => { + expect(reducer.mock.calls[1][1].error.message).toEqual( + 'something terrible happened', + ); + }); + }); + }); + }); +}); + +\`\`\` + +## packages/sample-platform/browser/src/redux/actions/index.ts + +\`\`\`ts +export * from './sampleActions'; + +\`\`\` + +## packages/sample-platform/browser/src/redux/actions/sampleActions.ts + +\`\`\`ts +import * as redux from 'redux'; + +import { api } from '../../api'; +import { Store } from '../reducers/index'; + +export type Q = { request: T }; +export type S = { response: T }; +export type E = { error: Error }; + +export type QEmpty = Q; +export type QValue = Q<{ value: number }>; + +export type Action = + // UI actions + | { type: '@@sample-stack/INCREMENT_COUNTER'; delta: number } + | { type: '@@sample-stack/RESET_COUNTER' } + + // API Requests + | ({ type: '@@sample-stack/SAVE_COUNT_REQUEST' } & QValue) + | ({ type: '@@sample-stack/SAVE_COUNT_SUCCESS' } & QValue & S<{}>) + | ({ type: '@@sample-stack/SAVE_COUNT_ERROR' } & QValue & E) + | ({ type: '@@sample-stack/LOAD_COUNT_REQUEST' } & QEmpty) + | ({ type: '@@sample-stack/LOAD_COUNT_SUCCESS' } & QEmpty & + S<{ value: number }>) + | ({ type: '@@sample-stack/LOAD_COUNT_ERROR' } & QEmpty & E); + +export const incrementCounter = (delta: number): Action => ({ + type: '@@sample-stack/INCREMENT_COUNTER', + delta, +}); + +export const resetCounter = (): Action => ({ + type: '@@sample-stack/RESET_COUNTER', +}); + +export type ApiActionGroup<_Q, _S> = { + request: (q?: _Q) => Action & Q<_Q>; + success: (s: _S, q?: _Q) => Action & Q<_Q> & S<_S>; + error: (e: Error, q?: _Q) => Action & Q<_Q> & E; +}; + +const _saveCount: ApiActionGroup<{ value: number }, {}> = { + request: (request) => ({ + type: '@@sample-stack/SAVE_COUNT_REQUEST', + request, + }), + success: (response, request) => ({ + type: '@@sample-stack/SAVE_COUNT_SUCCESS', + request, + response, + }), + error: (error, request) => ({ + type: '@@sample-stack/SAVE_COUNT_ERROR', + request, + error, + }), +}; + +const _loadCount: ApiActionGroup = { + request: (request) => ({ + type: '@@sample-stack/LOAD_COUNT_REQUEST', + request: null, + }), + success: (response, request) => ({ + type: '@@sample-stack/LOAD_COUNT_SUCCESS', + request: null, + response, + }), + error: (error, request) => ({ + type: '@@sample-stack/LOAD_COUNT_ERROR', + request: null, + error, + }), +}; + +type apiFunc = (q: Q) => Promise; + +function apiActionGroupFactory( + x: ApiActionGroup, + go: apiFunc, +) { + return (request: Q) => (dispatch: redux.Dispatch) => { + dispatch(x.request(request)); + go(request) + .then((response) => dispatch(x.success(response, request))) + .catch((e: Error) => dispatch(x.error(e, request))); + }; +} + +export const saveCount = apiActionGroupFactory(_saveCount, api.save); +export const loadCount = apiActionGroupFactory(_loadCount, api.load); + +\`\`\` + +## packages/sample-platform/browser/src/redux/index.ts + +\`\`\`ts +export * from './actions'; +export * from './reducers'; + +\`\`\` + +## packages/sample-platform/browser/src/redux/reducers/Store.ts + +\`\`\`ts +export namespace Store { + export type Counter = { value: number }; + + export type Sample = { + '@sample-stack/counter': Counter; + '@sample-stack/isSaving': boolean; + '@sample-stack/isLoading': boolean; + '@sample-stack/error': string; + }; +} + +\`\`\` + +## packages/sample-platform/browser/src/redux/reducers/__tests__/sampleReducers.test.ts + +\`\`\`ts +import 'jest'; +import { createStore, combineReducers } from 'redux'; + +import { reducers } from '../sampleReducers'; +import { Store } from '../Store'; + +import { incrementCounter } from '../../actions'; + +describe('reducers/counter', () => { + it('starts at 0', () => { + const store = createStore(combineReducers(reducers)); + const counter = store.getState()['@sample-stack/counter']; + expect(counter.value).toEqual(0); + }); + + it('increments', (done) => { + const store = createStore(combineReducers(reducers)); + store.subscribe(() => { + const counter = store.getState()['@sample-stack/counter']; + expect(counter.value).toEqual(3); + done(); + }); + store.dispatch(incrementCounter(3)); + }); + + it('restores state', (done) => { + const store = createStore(combineReducers(reducers)); + store.subscribe(() => { + const counter = store.getState()['@sample-stack/counter']; + expect(counter.value).toEqual(14); + done(); + }); + store.dispatch({ + type: '@@sample-stack/LOAD_COUNT_SUCCESS', + request: {}, + response: { value: 14 }, + }); + }); +}); + +\`\`\` + +## packages/sample-platform/browser/src/redux/reducers/index.ts + +\`\`\`ts +import { reducers as sampleReducers } from './sampleReducers'; + +export { Store } from './Store'; + +export const reducers = { + ...sampleReducers, +}; + +\`\`\` + +## packages/sample-platform/browser/src/redux/reducers/sampleReducers.ts + +\`\`\`ts +import { combineReducers, Action as reduxAction } from 'redux'; +import { Action } from '../actions'; +import { Store } from './Store'; + +function isSaving(state = false, action: Action): boolean { + switch (action.type) { + case '@@sample-stack/SAVE_COUNT_REQUEST': + return true; + case '@@sample-stack/SAVE_COUNT_SUCCESS': + case '@@sample-stack/SAVE_COUNT_ERROR': + return false; + default: + return state; + } +} + +function isLoading(state = false, action: Action): boolean { + switch (action.type) { + case '@@sample-stack/LOAD_COUNT_REQUEST': + return true; + case '@@sample-stack/LOAD_COUNT_SUCCESS': + case '@@sample-stack/LOAD_COUNT_ERROR': + return false; + default: + return state; + } +} + +function error(state = '', action: Action): string { + switch (action.type) { + case '@@sample-stack/LOAD_COUNT_REQUEST': + case '@@sample-stack/SAVE_COUNT_REQUEST': + return ''; + case '@@sample-stack/LOAD_COUNT_ERROR': + case '@@sample-stack/SAVE_COUNT_ERROR': + return action.error.toString(); + default: + return state; + } +} + +const initialState: Store.Counter = { + value: 0, +}; + +function counter( + state: Store.Counter = initialState, + action: Action, +): Store.Counter { + switch (action.type) { + case '@@sample-stack/INCREMENT_COUNTER': + const { delta } = action; + return { value: state.value + delta }; + + case '@@sample-stack/RESET_COUNTER': + return { value: 0 }; + + case '@@sample-stack/LOAD_COUNT_SUCCESS': + return { value: action.response.value }; + + default: + return state; + } +} + +export const reducers = { + '@sample-stack/counter': counter, + '@sample-stack/isSaving': isSaving, + '@sample-stack/isLoading': isLoading, + '@sample-stack/error': error, +}; + +\`\`\` + +## packages/sample-platform/browser/src/services/index.ts + +\`\`\`ts + +\`\`\` + +## packages/sample-platform/browser/tsconfig.json + +\`\`\`json +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "allowSyntheticDefaultImports": true, + "experimentalDecorators": true, + "esModuleInterop": true, + "rootDir": "./src", + "outDir": "../lib", + "declarationDir": "lib" + }, + "exclude": [ + "node_modules", + "lib", + "dist", + "webpack.config.js" + ] +} +\`\`\` + +## packages/sample-platform/browser/webpack.config.js + +\`\`\`js +const nodeExternals = require('webpack-node-externals'); +const webpack = require('webpack'); +const path = require('path'); +const fs = require('fs'); + +const webpackOpts = { + mode: 'development', + entry: './src/index.ts', + target: 'node', + output: { + path: path.join(__dirname, 'lib'), + filename: 'index.js', + libraryTarget: 'commonjs2', + }, + node: { + __dirname: false, + }, + resolve: { + extensions: ['.ts', '.tsx', '.graphql', '.gql'], + }, + plugins: [ + new webpack.LoaderOptionsPlugin({ + options: { + test: /\.tsx?$/, + ts: { + compiler: 'typescript', + configFile: 'tsconfig.json', + }, + tslint: { + emitErrors: true, + failOnHint: true, + }, + }, + }), + ], + devtool: 'source-map', + module: { + rules: [ + { + test: /\.tsx?$/, + loaders: 'ts-loader', + }, + { + test: /\.graphql?/, + exclude: /node_modules/, + use: 'raw-loader', + }, + { + test: /\.(gql)$/, + exclude: /node_modules/, + use: ['graphql-tag/loader'], + }, + ], + }, + externals: [nodeExternals({ modulesDir: '../../../node_modules' }), nodeExternals()], +}; + +module.exports = webpackOpts; + +\`\`\` + +## packages/sample-platform/server/jest.config.js + +\`\`\`js +const base = require('../../../jest.config.base'); +const packageJson = require('./package'); + +module.exports = { + ...base, + name: packageJson.name, + displayName: packageJson.name, +}; + +\`\`\` + +## packages/sample-platform/server/src/index.ts + +\`\`\`ts + +\`\`\` + +## packages/sample-platform/server/tsconfig.json + +\`\`\`json +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "experimentalDecorators": true, + "preserveConstEnums": true, + "rootDir": "./src", + "outDir": "../lib", + "declarationDir": "lib" + }, + "exclude": [ + "node_modules", + "lib", + "dist", + "webpack.config.js", + ], +} +\`\`\` + +## packages/sample-platform/server/webpack.config.js + +\`\`\`js +const nodeExternals = require('webpack-node-externals'); +const webpack = require('webpack'); +const path = require('path'); +const glob = require('glob'); +const fs = require('fs'); + +const webpackOpts = { + mode: 'development', + entry: { + index: './src/index.ts', + }, + target: 'node', + output: { + path: path.join(__dirname, 'lib'), + filename: '[name].js', + libraryTarget: 'commonjs2', + }, + resolve: { + extensions: ['.ejs', '.ts', '.tsx', '.graphql', '.gql'], + }, + plugins: [ + new webpack.LoaderOptionsPlugin({ + options: { + test: /\.ts$/, + ts: { + compiler: 'typescript', + configFile: 'tsconfig.json', + }, + tslint: { + emitErrors: true, + failOnHint: true, + }, + }, + }), + ], + devtool: 'source-map', + module: { + rules: [ + { + test: /\.ts$/, + loaders: 'ts-loader', + }, + { + test: /\.graphql?/, + exclude: /node_modules/, + use: 'raw-loader', + }, + { + test: /\.(gql)$/, + exclude: /node_modules/, + use: ['graphql-tag/loader'], + }, + { + test: /\.ejs$/, + exclude: /node_modules/, + use: 'raw-loader', + }, + ], + }, + externals: [ + nodeExternals({ modulesDir: '../../../node_modules' }), + nodeExternals(), + ], +}; + +module.exports = webpackOpts; + +\`\`\` + +## packages/sample-store/src/__tests__/counter-hemera-repository.test.ts + +\`\`\`ts +// import 'reflect-metadata'; + +// import * as Hemera from 'nats-hemera'; +// import * as Nats from 'nats'; +// import * as HemeraTestSuite from 'hemera-testsuite'; +// import * as HemeraSqlStore from 'hemera-sql-store'; +// import { CounterRemoteRepository } from '../repository/counter-hemera-repository'; +// import { createCounter, dropCounter, Counter_Table } from '../database-store/migrations/counter'; +// import * as knex from 'knex'; +// import { logger } from '@cdm-logger/server'; + +// require('dotenv').config({ path: process.env.ENV_FILE }); + +// import 'jest'; + +// describe('Hemera-sql-store', function () { +// let PORT = 6242; +// let authUrl = 'nats://localhost:' + PORT; +// let server; +// let hemera; +// let testDatabase = process.env.DB_DATABASE; +// let testTable = Counter_Table; +// let repo: CounterRemoteRepository; + +// beforeAll(async (done) => { +// server = HemeraTestSuite.start_server(PORT, () => { +// const nats = Nats.connect(authUrl); +// hemera = new Hemera(nats, {}); +// hemera.use(require('hemera-safe-promises')); +// hemera.use(HemeraSqlStore, { +// knex: { +// dialect: 'mysql', +// connection: { +// host: process.env.DB_HOST, +// user: process.env.DB_USER, +// password: process.env.DB_PASSWORD, +// database: testDatabase, +// }, +// pool: { +// min: 0, +// max: 7, +// }, +// }, +// }); +// hemera.ready(async () => { +// await createCounter(hemera.sqlStore.useDb(testDatabase)); +// repo = new CounterRemoteRepository(hemera); +// done(); +// }); +// }); +// }); + +// afterAll(async () => { +// await dropCounter(hemera.sqlStore.useDb(testDatabase)); +// hemera.close(); +// server.kill(); +// }); + +// it('create', async (done) => { +// const count = { id: 1, amount: 1 }; +// try { +// const addCoutOutput = await repo.create(count); +// expect(addCoutOutput).toEqual([1]); +// done(); +// } catch (err) { +// logger.error(err); +// done.fail(); +// } +// }); + +// it('getById', async (done) => { +// try { +// const count = await repo.getById(1); +// expect(count[0].amount).toEqual(1); +// done(); +// } catch (err) { +// logger.error(err); +// done.fail(); +// } +// }); +// it('getCount', async (done) => { +// try { +// const count = await repo.getCount(); +// done(); +// } catch (err) { +// logger.error(err); +// done.fail(); +// } +// }); + +// it('addCount', async (done) => { +// try { +// await repo.addCount(1); +// done(); +// } catch (err) { +// logger.error(err); +// done.fail(); +// } +// }); +// }); + +\`\`\` + +## packages/sample-store/src/__tests__/counter-repository.test.ts + +\`\`\`ts +import 'reflect-metadata'; +import 'jest'; + +import { Container } from 'inversify'; +import * as Knex from 'knex'; + +import { TYPES } from '../constants'; +import { DbConfig } from '../db-helpers'; +import { CounterRepository, ICounterRepository } from '../repository'; + +const DEFAULT_DB_CONFIG = require('./db/config.json'); + +describe('dI Test', () => { + let container: Container; + let knex; + beforeAll(async () => { + knex = Knex(DEFAULT_DB_CONFIG); + await knex.migrate.latest(); + await knex.seed.run(); + + const dbConfig = new DbConfig(DEFAULT_DB_CONFIG); + container = new Container(); + + container.bind('DefaultDbConfig').toConstantValue(dbConfig); + + // container... + container + .bind(TYPES.ICounterRepository) + .to(CounterRepository); + }); + + afterAll(() => { + knex.destroy(); + }); + + it('counter', async () => { + const repository = container.get( + TYPES.ICounterRepository, + ); + expect(repository).toBeInstanceOf(CounterRepository); + try { + const count = await repository.getById(1); + expect(count.amount).toEqual(5); + } catch (e) { + expect(e).toBeUndefined(); + } + }); + + it('add counter', async () => { + const repository = container.get( + TYPES.ICounterRepository, + ); + try { + await repository.addCount(2); + const cnt = await repository.getCount(); + expect(cnt.amount).toEqual(7); + } catch (e) { + expect(e).toBeUndefined(); + } + }); +}); + +\`\`\` + +## packages/sample-store/src/__tests__/db/config.json + +\`\`\`json +{ + "client": "sqlite3", + "connection": { + "filename": "packages/sample-store/__tests__/db/test-db.sqlite3" + }, + "seeds": { + "directory": "packages/sample-store/__tests__/db/seeds" + }, + "migrations": { + "directory": "packages/sample-store/__tests__/db/migrations" + }, + "useNullAsDefault": true +} +\`\`\` + +## packages/sample-store/src/__tests__/db/migrations/counter.ts + +\`\`\`ts +import * as Knex from 'knex'; + +export async function up(knex: Knex) { + return knex.schema.createTable('count', (table) => { + table.increments(); + table.timestamps(); + table.integer('amount').notNullable(); + }); +} + +export async function down(knex: Knex) { + return knex.schema.dropTable('count'); +} + +\`\`\` + +## packages/sample-store/src/__tests__/db/seeds/counter.ts + +\`\`\`ts +import * as Knex from 'knex'; + +const initialAmount = 5; + +export async function seed(knex: Knex) { + await knex('count').truncate(); + + return knex('count').insert({ amount: initialAmount }); +} + +\`\`\` + +## packages/sample-store/src/constants/constants.ts + +\`\`\`ts +export const TYPES = { + ICounterRepository: Symbol('ICounterRepository'), +}; + +\`\`\` + +## packages/sample-store/src/constants/index.ts + +\`\`\`ts +export * from './constants'; + +\`\`\` + +## packages/sample-store/src/container/index.ts + +\`\`\`ts +export * from './module'; + +\`\`\` + +## packages/sample-store/src/container/module.ts + +\`\`\`ts +import { TaggedType } from '@common-stack/core'; +import { ContainerModule, interfaces } from 'inversify'; + +import { TYPES } from '../constants'; +import { DbConfig } from '../db-helpers'; +import { CounterRepository, ICounterRepository } from '../repository'; + +export const repositoryModule: ( + config: DbConfig, +) => interfaces.ContainerModule = (dbConfig) => + new ContainerModule((bind: interfaces.Bind) => { + bind('DefaultDbConfig').toConstantValue(dbConfig); + bind(TYPES.ICounterRepository) + .to(CounterRepository) + .whenTargetIsDefault(); + + // bind(TYPES.ICounterRepository) + // .to(CounterRemoteRepository) + // .whenTargetNamed(TaggedType.MICROSERVICE); + }); + +\`\`\` + +## packages/sample-store/src/database-store/migrations/counter.ts + +\`\`\`ts +import * as Knex from 'knex'; + +export const Counter_Table = 'count'; +export const createCounter = async (driver) => + driver.schema.createTable(Counter_Table, (table) => { + table.increments(); + table.timestamps(false, true); + table.integer('amount').notNull(); + }); +export const dropCounter = async (driver) => + driver.schema.dropTable(Counter_Table); + +export async function up(knex: Knex) { + return createCounter(knex); +} + +export async function down(knex: Knex) { + return dropCounter(knex); +} + +\`\`\` + +## packages/sample-store/src/database-store/migrations/index.ts + +\`\`\`ts +// empty file on purpose +exports.up = function () {}; +exports.down = function () {}; + +\`\`\` + +## packages/sample-store/src/database-store/seeds/counter.ts + +\`\`\`ts +import * as Knex from 'knex'; + +const initialAmount = 5; + +export async function seed(knex: Knex) { + await knex('count').truncate(); + + return knex('count').insert({ amount: initialAmount }); +} + +\`\`\` + +## packages/sample-store/src/database-store/seeds/index.ts + +\`\`\`ts +// empty file on purpose +// empty file on purpose +exports.seed = function () {}; + +\`\`\` + +## packages/sample-store/src/db-helpers/abstract-repository.ts + +\`\`\`ts +import { inject, injectable } from 'inversify'; +import * as Knex from 'knex'; + +import { DbConfig } from './db-config'; + +let _db: Knex; + +/** + * Returns an instance of database + */ +const getDb = (config: DbConfig): Knex => { + if (!_db) { + _db = Knex(config.getConfiguration()); + } + + return _db; +}; + +@injectable() +export abstract class AbstractRepository { + @inject('DefaultDbConfig') + public dbConfig: DbConfig; + + public abstract readonly tableName: string; + + /** + * Returns an instance of database + */ + public getDb(): Knex { + return getDb(this.dbConfig); + } + + /** + * Returns a IQueryBuilder instance of Table + */ + public getTable(): Knex.QueryBuilder { + return this.getDb().table(this.tableName); + } +} + +\`\`\` + +## packages/sample-store/src/db-helpers/db-config.ts + +\`\`\`ts +import { Config } from 'knex'; + +export class DbConfig { + constructor(private config: Config) {} + + public getConfiguration(): Config { + return this.config; + } +} + +\`\`\` + +## packages/sample-store/src/db-helpers/entity.ts + +\`\`\`ts +export interface IEntity { + id: number; +} + +\`\`\` + +## packages/sample-store/src/db-helpers/index.ts + +\`\`\`ts +export * from './entity'; +export * from './db-config'; +export * from './abstract-repository'; +export * from './repository'; + +\`\`\` + +## packages/sample-store/src/db-helpers/repository.ts + +\`\`\`ts +export interface IRepository { + getById(id: number): Promise; + + find(filter: string, pageNumber: number, count: number): Promise; + + create(dto: T): Promise; + + update(dto: T): Promise; +} + +\`\`\` + +## packages/sample-store/src/index.ts + +\`\`\`ts +export * from './constants'; +export * from './repository'; +export * from './models'; +export * from './container'; +export * from './db-helpers'; + +\`\`\` + +## packages/sample-store/src/models/counter.ts + +\`\`\`ts +import { DataTypes, Sequelize } from 'sequelize'; + +export default (sequelize: Sequelize, dataTypes: typeof DataTypes) => { + // const count = sequelize.define('count', { + // name: dataTypes.STRING, + // }); +}; + +\`\`\` + +## packages/sample-store/src/models/index.ts + +\`\`\`ts +export * from './interfaces'; +export * from './counter'; + +\`\`\` + +## packages/sample-store/src/models/interfaces/count-model.ts + +\`\`\`ts +import { IEntity } from '../../db-helpers'; + +export class ICount implements IEntity { + public id: number; + + public amount: number; +} + +\`\`\` + +## packages/sample-store/src/models/interfaces/index.ts + +\`\`\`ts +export * from './count-model'; + +\`\`\` + +## packages/sample-store/src/repository/counter-hemera-repository.ts + +\`\`\`ts +// import { AbstractRepository } from '../db-helpers'; +// import { ICounterRepository } from './interfaces'; +// import { ICount } from '../models'; +// import { injectable, inject, named } from 'inversify'; +// import * as Hemera from 'nats-hemera'; +// import HemeraJoi from 'hemera-joi'; +// import * as Nats from 'nats'; +// import { Counter_Table } from '../database-store/migrations/counter'; + +// const NATS_HEMERA_DATBASE_MANAGER = 'sql-store'; + +// @injectable() +// export class CounterRemoteRepository implements ICounterRepository { + +// constructor( +// @inject('Hemera') private hemera: Hemera, +// ) { +// } +// // Set the table name to count +// public readonly tableName: string = Counter_Table; +// private topic = NATS_HEMERA_DATBASE_MANAGER; + +// public async getById(id) { +// const result = await this.hemera.act( +// { +// topic: NATS_HEMERA_DATBASE_MANAGER, +// cmd: 'findById', +// collection: this.tableName, +// id, +// }, +// ); +// return result.data; +// } + +// public async find(filter: string, pageNumber: number = 1, count: number = 20): Promise { +// const result = await this.hemera.act( +// { +// topic: NATS_HEMERA_DATBASE_MANAGER, +// cmd: 'find', +// collection: this.tableName, +// query: {}, +// }, +// ); +// return result.data; +// } + +// public async create(data: ICount): Promise { +// const result = await this.hemera.act( +// { +// topic: NATS_HEMERA_DATBASE_MANAGER, +// cmd: 'create', +// collection: this.tableName, +// data, +// }, +// ); +// return result.data; +// } + +// public async update(data: ICount): Promise { +// const result = await this.hemera.act( +// { +// topic: NATS_HEMERA_DATBASE_MANAGER, +// cmd: 'update', +// collection: this.tableName, +// query: { +// id: data.id, +// }, +// data: { +// amount: data.amount, +// }, +// }, +// ); +// return result.data; +// } + +// public async getCount(): Promise { +// return this.getById(1); +// } + +// public async addCount(amount) { +// return this.update({ id: 1, amount: amount }); +// } + +// } + +\`\`\` + +## packages/sample-store/src/repository/counter-repository.ts + +\`\`\`ts +import { injectable } from 'inversify'; + +import { Counter_Table } from '../database-store/migrations/counter'; +import { AbstractRepository } from '../db-helpers'; +import { ICount } from '../models'; +import { ICounterRepository } from './interfaces'; + +@injectable() +export class CounterRepository + extends AbstractRepository + implements ICounterRepository { + // Set the table name to count + public readonly tableName: string = Counter_Table; + + public async getById(id: number): Promise { + return this.getTable().where({ id }).first(); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public async find( + filter: string, + pageNumber = 1, + count = 20, + ): Promise { + return this.getTable().where('amount', 'like', `%${filter}`).select(); + } + + public async create(dto: ICount): Promise { + return this.create(dto); + } + + public async update(dto: ICount): Promise { + return this.getTable().update(dto); + } + + public async getCount(): Promise { + return this.getTable().first(); + } + + public async addCount(amount) { + await this.getTable().increment('amount', amount); + } +} + +\`\`\` + +## packages/sample-store/src/repository/index.ts + +\`\`\`ts +export * from './counter-repository'; +export * from './interfaces'; +// export * from './counter-hemera-repository'; + +\`\`\` + +## packages/sample-store/src/repository/interfaces/counter-repository.ts + +\`\`\`ts +import { IRepository } from '../../db-helpers'; +import { ICount } from '../../models'; + +export interface ICounterRepository extends IRepository { + getCount: () => Promise; + + addCount: (int) => void; +} + +\`\`\` + +## packages/sample-store/src/repository/interfaces/index.ts + +\`\`\`ts +export * from './counter-repository'; + +\`\`\` + +## packages/sample-store/tsconfig.json + +\`\`\`json +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDirs": ["./src", "../../database"], + "outDir": "../lib", + "declarationDir": "lib" + }, + "exclude": [ + "node_modules", + "lib", + "dist", + "webpack.config.js" + ] +} +\`\`\` + +## packages/sample-store/webpack.config.js + +\`\`\`js +const nodeExternals = require('webpack-node-externals'); +const webpack = require('webpack'); +const path = require('path'); +const glob = require('glob'); + +function globEntries1(globPath) { + const files = glob.sync(globPath); + const entries = {}; + + for (let i = 0; i < files.length; i += 1) { + const entry = files[i]; + const pathObj = path.parse(entry); + entries[ + path.join( + pathObj.dir.replace( + new RegExp('^./src/database-store', ''), + 'store', + ), + pathObj.name, + ) + ] = entry; + } + return entries; +} + +const webpackOpts = { + mode: 'development', + entry: { + index: './src/index.ts', + ...globEntries1('./src/database-store/**/*.ts'), + }, + target: 'node', + output: { + path: path.join(__dirname, 'lib'), + filename: '[name].js', + libraryTarget: 'commonjs2', + library: '@sample-stack/store', + }, + resolve: { + extensions: ['.ts', '.js'], + }, + plugins: [ + new webpack.LoaderOptionsPlugin({ + options: { + test: /\.ts$/, + ts: { + compiler: 'typescript', + configFile: 'tsconfig.json', + }, + tslint: { + emitErrors: true, + failOnHint: true, + }, + }, + }), + ], + devtool: 'source-map', + module: { + rules: [ + { + test: /\.ts$/, + loaders: 'ts-loader', + }, + ], + }, + externals: [ + nodeExternals({ modulesDir: '../../node_modules' }), + nodeExternals(), + ], +}; + +module.exports = webpackOpts; + +\`\`\` + +## packages-modules/counter/browser/jest.config.js + +\`\`\`js +const base = require('../../../jest.config.base'); +const packageJson = require('./package'); + +module.exports = { + ...base, + testEnvironment: 'jsdom', // This is overriden, from the base testEnvironment + name: packageJson.name, + displayName: packageJson.name, +}; +\`\`\` + +## packages-modules/counter/browser/rollup.config.js + +\`\`\`js +import graphql from '@rollup/plugin-graphql'; +import image from '@rollup/plugin-image'; +import typescript from '@rollup/plugin-typescript'; +import { string } from 'rollup-plugin-string'; + +const bundle = (config) => ({ + ...config, + input: 'src/index.ts', + // marking all node modules as external + external: (id) => !/^[./]/.test(id), +}); +const globals = { react: 'React' }; + +export default [ + bundle({ + plugins: [ + image(), + graphql({ + include: '**/*.gql', + }), + string({ + include: '**/*.graphql', + }), + typescript({ noEmitOnError: true }), + ], + output: [ + { + dir: 'lib', + format: 'es', + name: 'Counter', + compact: true, + exports: 'named', + sourcemap: true, + preserveModules: true, + chunkFileNames: '[name]-[hash].[format].js', + globals, + }, + ], + }), +]; + +\`\`\` + +## packages-modules/counter/browser/src/apollo-server-n-client/components/CounterView.tsx + +\`\`\`tsx +import * as React from 'react'; +import { Helmet } from 'react-helmet'; + + +/** + * @description Plain`CounterView` component without any data dependency. + */ +const CounterView = ({ + loading, + counter, + addCounter, + counterState, + addCounterState, + onReduxIncrement, + reduxCount, + getCounterLoading, + addCounterWs, + getCounter, + syncCachedCounter, + cachedData, +}: any) => { + + const renderMetaData = () => ( + + Counter + + + ); + if (loading) { + return ( +
+ {renderMetaData()} +
Loading Page...
+
+ ); + } else { + return ( +
+ {renderMetaData()} +
+

+ Current counter, is {counter.amount} and cached data. This is being stored + server-side in the database and using Apollo subscription for + real-time updates. +

+ + +
+
+

+ Get Counter Cache + {getCounterLoading ? "Loading Counter Data..." : cachedData ? {cachedData.counterCache.amount} : null} +
+ + +

+
+
+

+ Current reduxCount, is {reduxCount}. This is being stored + client-side with Redux. +

+ +
+
+

+ Current apolloLinkStateCount, is {counterState}. This is being + stored client-side with Apollo Link State. +

+ +
+
+ ); + } +}; + +export default CounterView; + +\`\`\` + +## packages-modules/counter/browser/src/apollo-server-n-client/compute.tsx + +\`\`\`tsx +import * as React from 'react'; +import { IMenuPosition } from '@common-stack/client-react'; + +import Counter from './containers/Counter'; +import { Dashboard } from '../common/components/Dashboard'; +import { getFilteredMenus, getFilteredRoutes } from '../utils'; + +export const counterPageStore: any[] = [ + { + exact: false, + icon: 'export', + key: 'dashboard', + component: Dashboard, + tab: 'Apollo Server & Client', + position: IMenuPosition.MIDDLE, + name: 'Apollo Server & Client', + path: '/apollo-server-n-client', + }, + { + key: 'counter', + name: 'Counter', + icon: 'appstore-o', + component: Counter, + position: IMenuPosition.MIDDLE, + path: '/apollo-server-n-client/counter', + }, +]; + +const selectedRoutesAndMenus = ['dashboard', 'counter']; + +// get menus +const filteredMenus = getFilteredMenus(counterPageStore, selectedRoutesAndMenus); + +// get routes +const filteredRoutes = getFilteredRoutes(counterPageStore, selectedRoutesAndMenus); + +export { filteredMenus, filteredRoutes }; + +\`\`\` + +## packages-modules/counter/browser/src/apollo-server-n-client/containers/Counter.tsx + +\`\`\`tsx +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ +/* eslint-disable @typescript-eslint/restrict-plus-operands */ +import React, { useEffect } from 'react'; +import update from 'immutability-helper'; +import { useDispatch, useSelector } from 'react-redux'; +import CounterView from '../components/CounterView'; +import { + useCounterQueryQuery, + useAddCounterMutation, + useAddCounterStateMutation, + useCounterStateQuery, + useAddCounter_WsMutation, + useSyncCachedCounterMutation, + useCounterCacheQueryLazyQuery +} from '../generated-model'; +import { + OnCounterUpdatedDocument, +} from '../../common/generated-models'; + +/** + * + * @description Counter Component with Data dependency. + */ +const CounterWithApollo: React.SFC = (props) => { + const [addCounterMutation] = useAddCounterMutation(); + const { loading, error, data: counterData, subscribeToMore, } = useCounterQueryQuery(); + const [addCounterStateMutation] = useAddCounterStateMutation(); + const { data: couterStateData } = useCounterStateQuery(); + const dispatch = useDispatch(); + const reduxCount = useSelector(state => (state as any).counter.reduxCount); + const [getCounter, { loading: getCounterLoading, data: cachedData }] = useCounterCacheQueryLazyQuery({ fetchPolicy: 'network-only' }); + const [addCounterWs] = useAddCounter_WsMutation(); + const [syncCachedCounter] = useSyncCachedCounterMutation(); + + useEffect(() => { + const unsubscribe = subscribeToMore(getSubscriptionOptions({})); + return () => unsubscribe(); + }, [subscribeToMore]); + + const onReduxIncrement = (value) => () => { + dispatch({ + type: 'COUNTER_INCREMENT', + value: Number(value), + }); + } + const addCounter = (amount) => () => { + addCounterMutation({ + variables: { amount }, + // Update the Cache of the Query that need to be display when it's dependent mutation gets called. + // This is needed for two reasons + // a). When update the Optimistically update cache, this get executes + // b). When the mutation response from Server, this gets update. + // Note: Optimistically update wont' work when network is offline. + update: (cache, { data: { addCounter } }) => { + // update the query's cache manually + // recommend to be done using fields but it can be done directly updating the cache + cache.modify({ + fields: { + counter(prev) { + return { amount: addCounter.amount }; + } + } + }) + }, + // Optimistically update the amount to the locally cached + // before the server responds + // You can verify it by setting the "Network conditions" in devtools to `Slow 3G`. + // You will see the data gets updated before the server responds. + optimisticResponse: { + // __typename: 'Mutation', + addCounter: { + __typename: 'Counter', + amount: counterData?.counter.amount + amount, + } + } + }) + } + + const addCounterState = (amount) => () => { + addCounterStateMutation({ + variables: { amount } + }); + } + const getSubscriptionOptions = ({ }) => { + return { + document: OnCounterUpdatedDocument, + variables: {}, + updateQuery: (prev, + { + subscriptionData: { + data: { + counterUpdated: { amount }, + }, + } + }) => { + return update(prev, { + counter: { + amount: { + $set: amount, + }, + }, + }); + } + } + } + + return +} + +export default CounterWithApollo; +\`\`\` + +## packages-modules/counter/browser/src/apollo-server-n-client/generated-model.tsx + +\`\`\`tsx +/* tslint:disable */ +import * as SchemaTypes from '../generated-models'; + +import { gql } from '@apollo/client'; +import * as Apollo from '@apollo/client'; +import * as React from 'react'; +import * as ApolloReactComponents from '@apollo/client/react/components'; +export type Omit = Pick>; +const defaultOptions = {} + +export const AddCounterStateDocument = gql` + mutation addCounterState($amount: Int!) { + addCounterState(amount: $amount) @client { + counter + } +} + `; +export type AddCounterStateMutationFn = Apollo.MutationFunction; +export type AddCounterStateComponentProps = Omit, 'mutation'>; + + export const AddCounterStateComponent = (props: AddCounterStateComponentProps) => ( + mutation={AddCounterStateDocument} {...props} /> + ); + + +/** + * __useAddCounterStateMutation__ + * + * To run a mutation, you first call `useAddCounterStateMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useAddCounterStateMutation` returns a tuple that includes: + * - A mutate function that you can call at any time to execute the mutation + * - An object with fields that represent the current status of the mutation's execution + * + * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; + * + * @example + * const [addCounterStateMutation, { data, loading, error }] = useAddCounterStateMutation({ + * variables: { + * amount: // value for 'amount' + * }, + * }); + */ +export function useAddCounterStateMutation(baseOptions?: Apollo.MutationHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useMutation(AddCounterStateDocument, options); + } +export type AddCounterStateMutationHookResult = ReturnType; +export type AddCounterStateMutationResult = Apollo.MutationResult; +export type AddCounterStateMutationOptions = Apollo.BaseMutationOptions; +export const AddCounterDocument = gql` + mutation addCounter($amount: Int!) { + addCounter(amount: $amount) { + amount + } +} + `; +export type AddCounterMutationFn = Apollo.MutationFunction; +export type AddCounterComponentProps = Omit, 'mutation'>; + + export const AddCounterComponent = (props: AddCounterComponentProps) => ( + mutation={AddCounterDocument} {...props} /> + ); + + +/** + * __useAddCounterMutation__ + * + * To run a mutation, you first call `useAddCounterMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useAddCounterMutation` returns a tuple that includes: + * - A mutate function that you can call at any time to execute the mutation + * - An object with fields that represent the current status of the mutation's execution + * + * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; + * + * @example + * const [addCounterMutation, { data, loading, error }] = useAddCounterMutation({ + * variables: { + * amount: // value for 'amount' + * }, + * }); + */ +export function useAddCounterMutation(baseOptions?: Apollo.MutationHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useMutation(AddCounterDocument, options); + } +export type AddCounterMutationHookResult = ReturnType; +export type AddCounterMutationResult = Apollo.MutationResult; +export type AddCounterMutationOptions = Apollo.BaseMutationOptions; +export const AddCounter_WsDocument = gql` + mutation AddCounter_WS($amount: Int!) { + addCounter(amount: $amount) { + amount + } +} + `; +export type AddCounter_WsMutationFn = Apollo.MutationFunction; +export type AddCounter_WsComponentProps = Omit, 'mutation'>; + + export const AddCounter_WsComponent = (props: AddCounter_WsComponentProps) => ( + mutation={AddCounter_WsDocument} {...props} /> + ); + + +/** + * __useAddCounter_WsMutation__ + * + * To run a mutation, you first call `useAddCounter_WsMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useAddCounter_WsMutation` returns a tuple that includes: + * - A mutate function that you can call at any time to execute the mutation + * - An object with fields that represent the current status of the mutation's execution + * + * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; + * + * @example + * const [addCounterWsMutation, { data, loading, error }] = useAddCounter_WsMutation({ + * variables: { + * amount: // value for 'amount' + * }, + * }); + */ +export function useAddCounter_WsMutation(baseOptions?: Apollo.MutationHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useMutation(AddCounter_WsDocument, options); + } +export type AddCounter_WsMutationHookResult = ReturnType; +export type AddCounter_WsMutationResult = Apollo.MutationResult; +export type AddCounter_WsMutationOptions = Apollo.BaseMutationOptions; +export const SyncCachedCounterDocument = gql` + mutation SyncCachedCounter { + syncCachedCounter +} + `; +export type SyncCachedCounterMutationFn = Apollo.MutationFunction; +export type SyncCachedCounterComponentProps = Omit, 'mutation'>; + + export const SyncCachedCounterComponent = (props: SyncCachedCounterComponentProps) => ( + mutation={SyncCachedCounterDocument} {...props} /> + ); + + +/** + * __useSyncCachedCounterMutation__ + * + * To run a mutation, you first call `useSyncCachedCounterMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useSyncCachedCounterMutation` returns a tuple that includes: + * - A mutate function that you can call at any time to execute the mutation + * - An object with fields that represent the current status of the mutation's execution + * + * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; + * + * @example + * const [syncCachedCounterMutation, { data, loading, error }] = useSyncCachedCounterMutation({ + * variables: { + * }, + * }); + */ +export function useSyncCachedCounterMutation(baseOptions?: Apollo.MutationHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useMutation(SyncCachedCounterDocument, options); + } +export type SyncCachedCounterMutationHookResult = ReturnType; +export type SyncCachedCounterMutationResult = Apollo.MutationResult; +export type SyncCachedCounterMutationOptions = Apollo.BaseMutationOptions; +export const CounterCacheQueryDocument = gql` + query counterCacheQuery { + counterCache { + amount + } +} + `; +export type CounterCacheQueryComponentProps = Omit, 'query'>; + + export const CounterCacheQueryComponent = (props: CounterCacheQueryComponentProps) => ( + query={CounterCacheQueryDocument} {...props} /> + ); + + +/** + * __useCounterCacheQueryQuery__ + * + * To run a query within a React component, call `useCounterCacheQueryQuery` and pass it any options that fit your needs. + * When your component renders, `useCounterCacheQueryQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useCounterCacheQueryQuery({ + * variables: { + * }, + * }); + */ +export function useCounterCacheQueryQuery(baseOptions?: Apollo.QueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useQuery(CounterCacheQueryDocument, options); + } +export function useCounterCacheQueryLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useLazyQuery(CounterCacheQueryDocument, options); + } +export type CounterCacheQueryQueryHookResult = ReturnType; +export type CounterCacheQueryLazyQueryHookResult = ReturnType; +export type CounterCacheQueryQueryResult = Apollo.QueryResult; +export const CounterStateDocument = gql` + query CounterState { + counterState @client { + counter + } +} + `; +export type CounterStateComponentProps = Omit, 'query'>; + + export const CounterStateComponent = (props: CounterStateComponentProps) => ( + query={CounterStateDocument} {...props} /> + ); + + +/** + * __useCounterStateQuery__ + * + * To run a query within a React component, call `useCounterStateQuery` and pass it any options that fit your needs. + * When your component renders, `useCounterStateQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useCounterStateQuery({ + * variables: { + * }, + * }); + */ +export function useCounterStateQuery(baseOptions?: Apollo.QueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useQuery(CounterStateDocument, options); + } +export function useCounterStateLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useLazyQuery(CounterStateDocument, options); + } +export type CounterStateQueryHookResult = ReturnType; +export type CounterStateLazyQueryHookResult = ReturnType; +export type CounterStateQueryResult = Apollo.QueryResult; +export const CounterQueryDocument = gql` + query counterQuery { + counter { + amount + } +} + `; +export type CounterQueryComponentProps = Omit, 'query'>; + + export const CounterQueryComponent = (props: CounterQueryComponentProps) => ( + query={CounterQueryDocument} {...props} /> + ); + + +/** + * __useCounterQueryQuery__ + * + * To run a query within a React component, call `useCounterQueryQuery` and pass it any options that fit your needs. + * When your component renders, `useCounterQueryQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useCounterQueryQuery({ + * variables: { + * }, + * }); + */ +export function useCounterQueryQuery(baseOptions?: Apollo.QueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useQuery(CounterQueryDocument, options); + } +export function useCounterQueryLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useLazyQuery(CounterQueryDocument, options); + } +export type CounterQueryQueryHookResult = ReturnType; +export type CounterQueryLazyQueryHookResult = ReturnType; +export type CounterQueryQueryResult = Apollo.QueryResult; +export const OnCounterUpdatedDocument = gql` + subscription onCounterUpdated { + counterUpdated { + amount + } +} + `; +export type OnCounterUpdatedComponentProps = Omit, 'subscription'>; + + export const OnCounterUpdatedComponent = (props: OnCounterUpdatedComponentProps) => ( + subscription={OnCounterUpdatedDocument} {...props} /> + ); + + +/** + * __useOnCounterUpdatedSubscription__ + * + * To run a query within a React component, call `useOnCounterUpdatedSubscription` and pass it any options that fit your needs. + * When your component renders, `useOnCounterUpdatedSubscription` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the subscription, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useOnCounterUpdatedSubscription({ + * variables: { + * }, + * }); + */ +export function useOnCounterUpdatedSubscription(baseOptions?: Apollo.SubscriptionHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useSubscription(OnCounterUpdatedDocument, options); + } +export type OnCounterUpdatedSubscriptionHookResult = ReturnType; +export type OnCounterUpdatedSubscriptionResult = Apollo.SubscriptionResult; +\`\`\` + +## packages-modules/counter/browser/src/apollo-server-n-client/graphql/__tests__/apollo-client-test-helper.ts + +\`\`\`ts +/* eslint-disable no-use-before-define */ +/* eslint-disable import/no-extraneous-dependencies */ +import { ApolloClient, ApolloClientOptions, ApolloLink } from '@apollo/client'; +import { InMemoryCache } from '@apollo/client/cache'; +// import * as schema from '../schema/schema.graphql'; +import { resolvers } from '../resolvers'; +import { dataIdFromObject } from '../id-generation'; + +const defaultSchema = ` +type Query { + dummy: Int +} +type Mutation { + dummy: Int +} +`; + +const cache = new InMemoryCache({ + dataIdFromObject: (object) => getDataIdFromObject(object), +}); + +const params: ApolloClientOptions = { + cache, + resolvers, + // typeDefs: defaultSchema.concat(schema as any), // if client schema exist +}; +const links = []; + +const client = new ApolloClient({ + queryDeduplication: true, + link: ApolloLink.from(links), + cache, +}); + +function getDataIdFromObject(result: any) { + if (dataIdFromObject[result.__typename]) { + return dataIdFromObject[result.__typename](result); + } + return result.id || result._id; +} + +export { client, getDataIdFromObject }; + +\`\`\` + +## packages-modules/counter/browser/src/apollo-server-n-client/graphql/__tests__/apollo-client.test.ts + +\`\`\`ts +import { client } from './apollo-client-test-helper'; +import { AddCounterDocument } from '../../../generated-models'; +import 'jest'; + +describe('Apollo Client tests', () => { + it('client test', async () => { + const result = await client.mutate({ + mutation: AddCounterDocument, + variables: { amount: 1 }, + // data: {}, + }); + expect(result).toEqual({ data: { addCounterState: null }, errors: undefined }); + }); +}); + +\`\`\` + +## packages-modules/counter/browser/src/apollo-server-n-client/graphql/id-generation.ts + +\`\`\`ts +/** + * All the unique identifier to be used when normilizing the data in the store. + * Refer: https://www.apollographql.com/docs/angular/basics/caching#configuration + * We define it as Object and use a helper method to convert. + * ex: const dataIdFromObject = { + * 'ICounter': (result) => result.__typename + ':' + result._id, + * } + */ +export const dataIdFromObject = {}; + +\`\`\` + +## packages-modules/counter/browser/src/apollo-server-n-client/graphql/index.ts + +\`\`\`ts +export * from './resolvers'; + +\`\`\` + +## packages-modules/counter/browser/src/apollo-server-n-client/graphql/resolvers/index.ts + +\`\`\`ts +export * from './resolvers'; + +\`\`\` + +## packages-modules/counter/browser/src/apollo-server-n-client/graphql/resolvers/resolvers.ts + +\`\`\`ts +/* eslint-disable import/no-unresolved */ +/* eslint-disable import/no-extraneous-dependencies */ +import { IClientStateDefault } from '@common-stack/client-core'; +import { InMemoryCache } from '@apollo/client/cache'; +import { CounterStateDocument } from '../../../generated-models'; + +const TYPE_NAME = 'CounterState'; + +const stateDefault: IClientStateDefault = { + type: 'query', + query: CounterStateDocument, + data: { + counterState: { + counter: 1, + __typename: TYPE_NAME, + }, + }, +}; + +const resolvers = { + Query: { + counterState: (_, args, { cache }) => { + const { + counterState: { counter }, + } = cache.readQuery({ query: CounterStateDocument }); + return { + counter, + __typename: TYPE_NAME, + }; + }, + }, + Mutation: { + addCounterState: async (_, { amount }, { cache }: { cache: InMemoryCache }) => { + const { + counterState: { counter }, + } = cache.readQuery({ query: CounterStateDocument }); + const newAmount = amount + counter; + + await cache.writeQuery({ + query: CounterStateDocument, + data: { + counterState: { + counter: newAmount, + __typename: TYPE_NAME, + }, + }, + }); + + return null; + }, + }, +}; + +export { stateDefault, resolvers }; + +\`\`\` + +## packages-modules/counter/browser/src/apollo-server-n-client/index.ts + +\`\`\`ts +import { Feature } from '@common-stack/client-react'; +import repository from './module'; + +export default new Feature(repository); + +\`\`\` + +## packages-modules/counter/browser/src/apollo-server-n-client/module.tsx + +\`\`\`tsx +import { reducers } from './redux'; +import { resolvers, stateDefault } from './graphql'; + +import { Feature } from '@common-stack/client-react'; +import { filteredMenus, filteredRoutes } from './compute'; + +export default new Feature({ + menuConfig: filteredMenus, + routeConfig: filteredRoutes, + reducer: { counter: reducers }, + clientStateParams: { resolvers, defaults: [stateDefault] }, +}); + +\`\`\` + +## packages-modules/counter/browser/src/apollo-server-n-client/redux/index.ts + +\`\`\`ts +export * from './reducers'; + +\`\`\` + +## packages-modules/counter/browser/src/apollo-server-n-client/redux/reducers/index.ts + +\`\`\`ts +const defaultState = { + reduxCount: 1, +}; + +export const reducers = function (state = defaultState, action) { + switch (action.type) { + case 'COUNTER_INCREMENT': + return { + ...state, + reduxCount: state.reduxCount + action.value, + }; + + default: + return state; + } +}; + +\`\`\` + +## packages-modules/counter/browser/src/common/components/Dashboard.tsx + +\`\`\`tsx +import * as React from 'react'; +import { renderRoutes } from 'react-router-config'; + +export const Dashboard = (props) => <>{renderRoutes(props.route.routes, { matchPath: props.route.path })}; + +\`\`\` + +## packages-modules/counter/browser/src/common/components/Home.tsx + +\`\`\`tsx +import * as React from 'react'; + +export const Home = () => ( +
+

FullStack-Pro

+
+); + +\`\`\` + +## packages-modules/counter/browser/src/common/compute.tsx + +\`\`\`tsx +import * as React from 'react'; +import { IMenuPosition } from '@common-stack/client-react'; + +import { Home } from '../common/components/Home'; +import { getFilteredMenus, getFilteredRoutes } from '../utils'; + +export const commonPageStore: any[] = [ + { + path: '/', + key: 'home', + exact: true, + name: 'Home', + component: Home, + position: IMenuPosition.MIDDLE, + }, +]; + +const selectedRoutesAndMenus = ['home']; + +// get menus +const filteredMenus = getFilteredMenus(commonPageStore, selectedRoutesAndMenus); + +// get routes +const filteredRoutes = getFilteredRoutes(commonPageStore, selectedRoutesAndMenus); + +export { filteredMenus, filteredRoutes }; + +\`\`\` + +## packages-modules/counter/browser/src/common/generated-models.ts + +\`\`\`ts +/* tslint:disable */ + +import { GraphQLResolveInfo, GraphQLScalarType, GraphQLScalarTypeConfig } from 'graphql'; + +import gql from 'graphql-tag'; +import { MyContext } from './interfaces/context'; + +export type Maybe = T | null; +/** All built-in and custom scalars, mapped to their actual values */ +export type Scalars = { + ID: string; + String: string; + Boolean: boolean; + Int: number; + Float: number; + AnyObject: any; + JSON: any; + JSONObject: any; + Date: any; + Time: any; + DateTime: any; +}; + +export type ClientCounter = { + counter?: Maybe; +}; + +export type Counter = { + amount: Scalars['Int']; +}; + +export type FieldError = { + field: Scalars['String']; + message: Scalars['String']; +}; + +export type Mutation = { + dummy?: Maybe; + addCounterState?: Maybe; + addCounter?: Maybe; +}; + +export type MutationAddCounterStateArgs = { + amount: Scalars['Int']; +}; + +export type MutationAddCounterArgs = { + amount?: Maybe; +}; + +export type Node = { + id: Scalars['ID']; +}; + +export type Query = { + dummy?: Maybe; + counterState?: Maybe; + counter?: Maybe; +}; + +export type Subscription = { + dummy?: Maybe; + counterUpdated?: Maybe; +}; + +export type AddCounterStateMutationVariables = { + amount: Scalars['Int']; +}; + +export type AddCounterStateMutation = { __typename?: 'Mutation' } & { + addCounterState: Maybe<{ __typename?: 'ClientCounter' } & Pick>; +}; + +export type AddCounterMutationVariables = { + amount: Scalars['Int']; +}; + +export type AddCounterMutation = { __typename?: 'Mutation' } & { + addCounter: Maybe<{ __typename?: 'Counter' } & Pick>; +}; + +export type CounterStateQueryVariables = {}; + +export type CounterStateQuery = { __typename?: 'Query' } & { + counterState: Maybe<{ __typename?: 'ClientCounter' } & Pick>; +}; + +export type CounterQueryQueryVariables = {}; + +export type CounterQueryQuery = { __typename?: 'Query' } & { + counter: Maybe<{ __typename?: 'Counter' } & Pick>; +}; + +export type OnCounterUpdatedSubscriptionVariables = {}; + +export type OnCounterUpdatedSubscription = { __typename?: 'Subscription' } & { + counterUpdated: Maybe<{ __typename?: 'Counter' } & Pick>; +}; + +export type ResolverFn = ( + parent: TParent, + args: TArgs, + context: TContext, + info: GraphQLResolveInfo, +) => Promise | TResult; + +export type StitchingResolver = { + fragment: string; + resolve: ResolverFn; +}; + +export type Resolver = + | ResolverFn + | StitchingResolver; + +export type SubscriptionSubscribeFn = ( + parent: TParent, + args: TArgs, + context: TContext, + info: GraphQLResolveInfo, +) => AsyncIterator | Promise>; + +export type SubscriptionResolveFn = ( + parent: TParent, + args: TArgs, + context: TContext, + info: GraphQLResolveInfo, +) => TResult | Promise; + +export interface SubscriptionResolverObject { + subscribe: SubscriptionSubscribeFn; + resolve?: SubscriptionResolveFn; +} + +export type SubscriptionResolver = + | ((...args: any[]) => SubscriptionResolverObject) + | SubscriptionResolverObject; + +export type TypeResolveFn = ( + parent: TParent, + context: TContext, + info: GraphQLResolveInfo, +) => Maybe; + +export type NextResolverFn = () => Promise; + +export type DirectiveResolverFn = ( + next: NextResolverFn, + parent: TParent, + args: TArgs, + context: TContext, + info: GraphQLResolveInfo, +) => TResult | Promise; + +/** Mapping between all available schema types and the resolvers types */ +export type ResolversTypes = { + Query: {}; + Int: Scalars['Int']; + ClientCounter: ClientCounter; + Counter: Counter; + Mutation: {}; + Subscription: {}; + String: Scalars['String']; + Boolean: Scalars['Boolean']; + AnyObject: Scalars['AnyObject']; + JSON: Scalars['JSON']; + JSONObject: Scalars['JSONObject']; + FieldError: FieldError; + Node: Node; + ID: Scalars['ID']; + Date: Scalars['Date']; + Time: Scalars['Time']; + DateTime: Scalars['DateTime']; +}; + +export interface AnyObjectScalarConfig extends GraphQLScalarTypeConfig { + name: 'AnyObject'; +} + +export type ClientCounterResolvers = { + counter?: Resolver, ParentType, ContextType>; +}; + +export type CounterResolvers = { + amount?: Resolver; +}; + +export interface DateScalarConfig extends GraphQLScalarTypeConfig { + name: 'Date'; +} + +export interface DateTimeScalarConfig extends GraphQLScalarTypeConfig { + name: 'DateTime'; +} + +export type FieldErrorResolvers = { + field?: Resolver; + message?: Resolver; +}; + +export interface JsonScalarConfig extends GraphQLScalarTypeConfig { + name: 'JSON'; +} + +export interface JsonObjectScalarConfig extends GraphQLScalarTypeConfig { + name: 'JSONObject'; +} + +export type MutationResolvers = { + dummy?: Resolver, ParentType, ContextType>; + addCounterState?: Resolver< + Maybe, + ParentType, + ContextType, + MutationAddCounterStateArgs + >; + addCounter?: Resolver, ParentType, ContextType, MutationAddCounterArgs>; +}; + +export type NodeResolvers = { + __resolveType: TypeResolveFn; + id?: Resolver; +}; + +export type QueryResolvers = { + dummy?: Resolver, ParentType, ContextType>; + counterState?: Resolver, ParentType, ContextType>; + counter?: Resolver, ParentType, ContextType>; +}; + +export type SubscriptionResolvers = { + dummy?: SubscriptionResolver, ParentType, ContextType>; + counterUpdated?: SubscriptionResolver, ParentType, ContextType>; +}; + +export interface TimeScalarConfig extends GraphQLScalarTypeConfig { + name: 'Time'; +} + +export type Resolvers = { + AnyObject?: GraphQLScalarType; + ClientCounter?: ClientCounterResolvers; + Counter?: CounterResolvers; + Date?: GraphQLScalarType; + DateTime?: GraphQLScalarType; + FieldError?: FieldErrorResolvers; + JSON?: GraphQLScalarType; + JSONObject?: GraphQLScalarType; + Mutation?: MutationResolvers; + Node?: NodeResolvers; + Query?: QueryResolvers; + Subscription?: SubscriptionResolvers; + Time?: GraphQLScalarType; +}; + +/** + * @deprecated + * Use "Resolvers" root object instead. If you wish to get "IResolvers", add "typesPrefix: I" to your config. + */ +export type IResolvers = Resolvers; +export type Omit = Pick>; + +export const AddCounterStateDocument = gql` + mutation addCounterState($amount: Int!) { + addCounterState(amount: $amount) @client { + counter + } + } +`; +export const AddCounterDocument = gql` + mutation addCounter($amount: Int!) { + addCounter(amount: $amount) { + amount + } + } +`; +export const CounterStateDocument = gql` + query CounterState { + counterState @client { + counter + } + } +`; +export const CounterQueryDocument = gql` + query counterQuery { + counter { + amount + } + } +`; +export const OnCounterUpdatedDocument = gql` + subscription onCounterUpdated { + counterUpdated { + amount + } + } +`; + +\`\`\` + +## packages-modules/counter/browser/src/common/index.ts + +\`\`\`ts +import { Feature } from '@common-stack/client-react'; +import common from './module'; + +export default new Feature(common); + +\`\`\` + +## packages-modules/counter/browser/src/common/interfaces/context.ts + +\`\`\`ts +import { DataProxy } from '@apollo/client/cache'; +import { ApolloClient } from '@apollo/client'; + +export interface MyContext { + cache: DataProxy; + getCacheKey: (options: { __typename: string; resource?: string; id?: string }) => string; + apolloClient: ApolloClient; +} + +\`\`\` + +## packages-modules/counter/browser/src/common/module.tsx + +\`\`\`tsx +import * as React from 'react'; + +import { Feature } from '@common-stack/client-react'; +import { filteredMenus, filteredRoutes } from './compute'; + +export default new Feature({ + menuConfig: filteredMenus, + routeConfig: filteredRoutes, +}); + +\`\`\` + +## packages-modules/counter/browser/src/connected-react-router/__tests__/connected-react-router-module.test.ts + +\`\`\`ts +import Module from '../module'; + +import 'jest'; + +describe('connector modules', () => { + it('module configuredRoutes', () => { + const configuredRoutes = Module.getConfiguredRoutes(); + + expect(configuredRoutes).toMatchSnapshot(); + }); + + it('module routes', () => { + const routes = Module.getRoutes(); + + expect(routes).toMatchSnapshot(); + }); +}); + +\`\`\` + +## packages-modules/counter/browser/src/connected-react-router/components/Counter.tsx + +\`\`\`tsx +/* eslint-disable react/button-has-type */ +import * as React from 'react'; +import { Dispatch } from 'redux'; +import { connect } from 'react-redux'; +import { RouteComponentProps } from 'react-router'; +import { increment, decrement } from '../redux'; +import { State } from '../interfaces'; + +const CounterComponent: React.SFC = (props) => ( +
+ Counter: {props.count} + + +
+); + +interface StateProps { + count: number; +} + +interface DispatchProps { + increment: () => void; + decrement: () => void; +} + +const mapStateToProps = (state: State) => ({ + count: state.connectedReactRouterCounter, +}); + +const mapDispatchToProps = (dispatch: Dispatch) => ({ + increment: () => dispatch(increment()), + decrement: () => dispatch(decrement()), +}); + +export const Counter = connect( + mapStateToProps, + mapDispatchToProps, +)(CounterComponent); + +\`\`\` + +## packages-modules/counter/browser/src/connected-react-router/components/Hello.tsx + +\`\`\`tsx +import * as React from 'react'; +import { HelloChild } from './HelloChild'; + +const Hello = () => ( +
+
Hello
+ +
+); + +export { Hello }; + +\`\`\` + +## packages-modules/counter/browser/src/connected-react-router/components/HelloChild.tsx + +\`\`\`tsx + +import * as React from 'react'; +import { connect } from 'react-redux'; +import { Link } from 'react-router-dom'; +import { State } from '../interfaces'; +import { CONNECTED_REACT_ROUTER_ROUTES_TYPES } from '../constants'; + +interface HelloChildProps { + pathname: string; + search: string; + hash: string; +} + +const HelloChildComponent = ({ pathname, search, hash }: HelloChildProps) => ( +
+ Hello-Child +
    +
  • with query string
  • +
  • with hash
  • +
+
+ pathname: {pathname} +
+
+ search: {search} +
+
+ hash: {hash} +
+
+); + +const mapStateToProps = (state: State) => ({ + pathname: state.router.location.pathname, + search: state.router.location.search, + hash: state.router.location.hash, +}); + +export const HelloChild = connect(mapStateToProps)(HelloChildComponent); + +\`\`\` + +## packages-modules/counter/browser/src/connected-react-router/components/Home.tsx + +\`\`\`tsx +import * as React from 'react'; + +const Home = () => ( +
+ Home +
+); + +export { Home }; + +\`\`\` + +## packages-modules/counter/browser/src/connected-react-router/components/NavBar.tsx + +\`\`\`tsx +import * as React from 'react'; +import { Counter } from './Counter'; + +const NavBar = () => ( +
+ +
+); + +export default NavBar; + +\`\`\` + +## packages-modules/counter/browser/src/connected-react-router/components/NoMatch.tsx + +\`\`\`tsx +import * as React from 'react'; + +const NoMatch = () => ( +
+ No Match +
+); + +export default NoMatch; + +\`\`\` + +## packages-modules/counter/browser/src/connected-react-router/compute.tsx + +\`\`\`tsx +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +import * as React from 'react'; +import { IMenuPosition, IRoute } from '@common-stack/client-react'; + +import { Hello } from './components/Hello'; +import { Counter } from './components/Counter'; +import { Dashboard } from '../common/components/Dashboard'; +import { getFilteredMenus, getFilteredRoutes } from '../utils'; +import { CONNECTED_REACT_ROUTER_ROUTES_TYPES } from './constants'; + +export const counterPageStore: IRoute[] = [ + { + exact: false, + icon: 'export', + component: Dashboard, + position: IMenuPosition.MIDDLE, + name: 'Connected React Router', + key: CONNECTED_REACT_ROUTER_ROUTES_TYPES.HOME, + path: CONNECTED_REACT_ROUTER_ROUTES_TYPES.HOME, + }, + { + exact: true, + icon: 'export', + name: 'Hello', + component: Hello, + position: IMenuPosition.MIDDLE, + key: CONNECTED_REACT_ROUTER_ROUTES_TYPES.HELLO, + path: CONNECTED_REACT_ROUTER_ROUTES_TYPES.HELLO, + }, + { + exact: true, + icon: 'export', + name: 'Counter', + component: Counter, + position: IMenuPosition.MIDDLE, + key: CONNECTED_REACT_ROUTER_ROUTES_TYPES.COUNTER, + path: CONNECTED_REACT_ROUTER_ROUTES_TYPES.COUNTER, + }, +]; + +const selectedRoutesAndMenus = [ + CONNECTED_REACT_ROUTER_ROUTES_TYPES.HOME, + CONNECTED_REACT_ROUTER_ROUTES_TYPES.HELLO, + CONNECTED_REACT_ROUTER_ROUTES_TYPES.COUNTER, +]; + +// get routes +const filteredRoutes = getFilteredRoutes(counterPageStore, selectedRoutesAndMenus); + +// get menus +const filteredMenus = getFilteredMenus(counterPageStore, selectedRoutesAndMenus); + +export { filteredRoutes, filteredMenus }; + +\`\`\` + +## packages-modules/counter/browser/src/connected-react-router/constants/action-types.ts + +\`\`\`ts +export const enum CONNECTED_REACT_ROUTER_ACTION_TYPES { + INCREMENT = '@connected-react-router/INCREMENT', + DECREMENT = '@connected-react-router/DECREMENT', +} + +\`\`\` + +## packages-modules/counter/browser/src/connected-react-router/constants/index.ts + +\`\`\`ts +export * from './action-types'; +export * from './routes-types'; + +\`\`\` + +## packages-modules/counter/browser/src/connected-react-router/constants/routes-types.ts + +\`\`\`ts +export enum CONNECTED_REACT_ROUTER_ROUTES_TYPES { + ROOT = '/', + HOME = '/connected-react-router', + HELLO = '/connected-react-router/hello', + COUNTER = '/connected-react-router/counter', +} + +\`\`\` + +## packages-modules/counter/browser/src/connected-react-router/electron-module.tsx + +\`\`\`tsx +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +import { Feature } from '@common-stack/client-react'; +import { IMenuPosition, IRoute } from '@common-stack/client-react'; +import { Counter } from './components/Counter'; +import { connectedReactRouterCounter } from './redux'; +import { CONNECTED_REACT_ROUTER_ROUTES_TYPES } from './constants'; +import { getFilteredRoutes } from '../utils'; + + +export const counterPageStore: IRoute[] = [ + { + exact: true, + icon: 'export', + name: 'Counter', + component: Counter, + position: IMenuPosition.MIDDLE, + key: CONNECTED_REACT_ROUTER_ROUTES_TYPES.ROOT, + path: CONNECTED_REACT_ROUTER_ROUTES_TYPES.ROOT, + }, +]; + +const selectedRoutesAndMenus = [ + CONNECTED_REACT_ROUTER_ROUTES_TYPES.ROOT, +]; +// get routes +const filteredRoutes = getFilteredRoutes(counterPageStore, selectedRoutesAndMenus); + +export const ElectronTrayModule = new Feature({ + routeConfig: filteredRoutes, + reducer: { connectedReactRouterCounter }, +}); + + +\`\`\` + +## packages-modules/counter/browser/src/connected-react-router/index.electron.ts + +\`\`\`ts +export { ElectronTrayModule } from './electron-module'; + +\`\`\` + +## packages-modules/counter/browser/src/connected-react-router/index.ts + +\`\`\`ts +import { Feature } from '@common-stack/client-react'; +import repository from './module'; +import { Counter } from './components/Counter'; + +export { Counter }; +export default new Feature(repository); + +\`\`\` + +## packages-modules/counter/browser/src/connected-react-router/interfaces/index.ts + +\`\`\`ts +export * from './state'; + +\`\`\` + +## packages-modules/counter/browser/src/connected-react-router/interfaces/state.ts + +\`\`\`ts +import { RouterState } from 'connected-react-router'; + +export interface State { + connectedReactRouterCounter: number; + router: RouterState; +} + +\`\`\` + +## packages-modules/counter/browser/src/connected-react-router/module.tsx + +\`\`\`tsx +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +import * as React from 'react'; +import { Feature } from '@common-stack/client-react'; +import { Counter } from './components/Counter'; +import NavBar from './components/NavBar'; +import { connectedReactRouterCounter } from './redux'; +import { filteredRoutes, filteredMenus } from './compute'; + +export default new Feature({ + navItem: , // used in electron + menuConfig: filteredMenus, + routeConfig: filteredRoutes, + reducer: { connectedReactRouterCounter }, +}); + +\`\`\` + +## packages-modules/counter/browser/src/connected-react-router/redux/actions/counter.ts + +\`\`\`ts +import { CONNECTED_REACT_ROUTER_ACTION_TYPES } from '../../constants'; + +export const increment = () => ({ + type: CONNECTED_REACT_ROUTER_ACTION_TYPES.INCREMENT, +}); + +export const decrement = () => ({ + type: CONNECTED_REACT_ROUTER_ACTION_TYPES.DECREMENT, +}); + +\`\`\` + +## packages-modules/counter/browser/src/connected-react-router/redux/actions/index.ts + +\`\`\`ts +export * from './counter'; + +\`\`\` + +## packages-modules/counter/browser/src/connected-react-router/redux/index.ts + +\`\`\`ts +export * from './actions'; +export * from './reducers'; + +\`\`\` + +## packages-modules/counter/browser/src/connected-react-router/redux/reducers/counter.ts + +\`\`\`ts +import { Action } from 'redux'; +import { CONNECTED_REACT_ROUTER_ACTION_TYPES } from '../../constants'; + +const connectedReactRouterCounter = (state = 0, action: Action) => { + switch (action.type) { + case CONNECTED_REACT_ROUTER_ACTION_TYPES.INCREMENT: + return state + 1; + case CONNECTED_REACT_ROUTER_ACTION_TYPES.DECREMENT: + return state - 1; + default: + return state; + } +}; + +export { connectedReactRouterCounter }; + +\`\`\` + +## packages-modules/counter/browser/src/connected-react-router/redux/reducers/index.ts + +\`\`\`ts +export * from './counter'; + +\`\`\` + +## packages-modules/counter/browser/src/fela/components/CompledWithTheme.tsx + +\`\`\`tsx +import * as React from 'react'; + + +import { ThemeProvider } from 'react-fela'; +import { Complex } from './ComplexComponent'; +import { theme } from '../theme'; + + +const extendStyles = { + container: { + borderColor: 'black', + borderRadius: '10px', + borderStyle: 'solid', + }, +}; + + +export const ComplexWithTheme: React.FunctionComponent<{}> = props => { + + + return ( + + + + ); + +}; + +\`\`\` + +## packages-modules/counter/browser/src/fela/components/ComplexComponent.tsx + +\`\`\`tsx +import * as React from 'react'; +import { connect, FelaWithStylesProps, Rules } from 'react-fela'; +import { Theme } from '../interfaces'; + + +interface OwnProps { + fontScale: number; +} + +interface Styles { + container: any; + firstSection: any; + secondSection: any; + thirdSection: any; +} + +type Props = OwnProps & FelaWithStylesProps; + +const ComplexComponent: React.FunctionComponent = props => { + const { styles, rules, theme } = props; + + return ( +
+
+
First Section
+
Second Section
+
Third Section
+
+
+

Rules

+ {Object.entries(rules) + .map(([key, rule]) => ( +
+ {`${key}: ${JSON.stringify(rule(props))}`} +
+ ))} +
+
+

Theme

+ {JSON.stringify(theme)} +
+
+ + ); +}; + +const complexComponentStyle: Rules = ({fontScale, theme}) => ({ + container: { + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + }, + firstSection: ({ theme }) => ({ + backgroundColor: theme.color.primary, + fontSize: `{5 * fontScale}px`, + }), + secondSection: { + backgroundColor: theme.color.secondary, + fontSize: `${7 * fontScale}px`, + }, + thirdSection: { + backgroundColor: theme.color.additional, + fontSize: `${10 * fontScale}px`, + }, +}); + +export const Complex = connect(complexComponentStyle)(ComplexComponent); + + + + +\`\`\` + +## packages-modules/counter/browser/src/fela/components/index.ts + +\`\`\`ts +export * from './ComplexComponent'; + +\`\`\` + +## packages-modules/counter/browser/src/fela/compute.tsx + +\`\`\`tsx +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +import * as React from 'react'; +import { IMenuPosition } from '@common-stack/client-react'; +import { getFilteredMenus, getFilteredRoutes } from '../utils'; +import { ComplexWithTheme } from './components/CompledWithTheme'; + + + +export const felaPageStore: any[] = [ + { + component: ComplexWithTheme, + tab: 'Fela Styling', + key: 'fela', + position: IMenuPosition.MIDDLE, + name: 'Fela Styling', + path: '/fela', + }, +]; + +const selectedRoutesAndMenus = ['fela']; + +// get menus +const filteredMenus = getFilteredMenus(felaPageStore, selectedRoutesAndMenus); + +// get routes +const filteredRoutes = getFilteredRoutes(felaPageStore, selectedRoutesAndMenus); + +export { filteredMenus, filteredRoutes }; + +\`\`\` + +## packages-modules/counter/browser/src/fela/index.ts + +\`\`\`ts +import { Feature } from '@common-stack/client-react'; +import fela from './module'; + +export default new Feature(fela); + +\`\`\` + +## packages-modules/counter/browser/src/fela/interfaces/index.ts + +\`\`\`ts +export * from './theme'; + +\`\`\` + +## packages-modules/counter/browser/src/fela/interfaces/theme.ts + +\`\`\`ts +export interface Theme { + color: { + primary: string; + secondary: string; + additional: string; + }; +} + +\`\`\` + +## packages-modules/counter/browser/src/fela/module.tsx + +\`\`\`tsx +import * as React from 'react'; +import { Feature } from '@common-stack/client-react'; +import { filteredMenus, filteredRoutes } from './compute'; + + + +export default new Feature({ + menuConfig: filteredMenus, + routeConfig: filteredRoutes, +}); + +\`\`\` + +## packages-modules/counter/browser/src/fela/theme.ts + +\`\`\`ts +import { Theme } from './interfaces'; + +export const theme: Theme = { + color: { + primary: 'lightblue', + secondary: 'red', + additional: 'lightgreen', + }, +}; + +\`\`\` + +## packages-modules/counter/browser/src/generated-models.ts + +\`\`\`ts +/* tslint:disable */ +import { GraphQLResolveInfo } from 'graphql'; +import { gql } from '@apollo/client'; +import * as Apollo from '@apollo/client'; +export type Maybe = T | null; +export type Exact = { [K in keyof T]: T[K] }; +export type MakeOptional = Omit & { [SubKey in K]?: Maybe }; +export type MakeMaybe = Omit & { [SubKey in K]: Maybe }; +export type RequireFields = { [X in Exclude]?: T[X] } & { [P in K]-?: NonNullable }; +const defaultOptions = {} +/** All built-in and custom scalars, mapped to their actual values */ +export type Scalars = { + ID: string; + String: string; + Boolean: boolean; + Int: number; + Float: number; +}; + +export type ClientCounter = { + __typename?: 'ClientCounter'; + counter?: Maybe; +}; + +/** Database counter */ +export type Counter = { + __typename?: 'Counter'; + /** Current amount */ + amount: Scalars['Int']; +}; + +export type Mutation = { + __typename?: 'Mutation'; + /** Increase counter value returns current counter amount */ + addCounter?: Maybe; + addCounterState?: Maybe; + /** add Counter */ + addMoleculerCounter?: Maybe; + /** sync cached counter with current value */ + syncCachedCounter?: Maybe; +}; + + +export type MutationAddCounterArgs = { + amount?: Maybe; +}; + + +export type MutationAddCounterStateArgs = { + amount: Scalars['Int']; +}; + + +export type MutationAddMoleculerCounterArgs = { + amount?: Maybe; +}; + +export type Query = { + __typename?: 'Query'; + /** Counter */ + counter?: Maybe; + /** Counter from Datasource */ + counterCache?: Maybe; + counterState?: Maybe; + /** Moleculer Counter */ + moleculerCounter?: Maybe; +}; + +export type Subscription = { + __typename?: 'Subscription'; + /** Subscription fired when anyone increases counter */ + counterUpdated?: Maybe; + moleculerCounterUpdate?: Maybe; +}; + +export type AddCounterStateMutationVariables = Exact<{ + amount: Scalars['Int']; +}>; + + +export type AddCounterStateMutation = ( + { __typename?: 'Mutation' } + & { addCounterState?: Maybe<( + { __typename?: 'ClientCounter' } + & Pick + )> } +); + +export type AddCounterMutationVariables = Exact<{ + amount: Scalars['Int']; +}>; + + +export type AddCounterMutation = ( + { __typename?: 'Mutation' } + & { addCounter?: Maybe<( + { __typename?: 'Counter' } + & Pick + )> } +); + +export type AddCounter_WsMutationVariables = Exact<{ + amount: Scalars['Int']; +}>; + + +export type AddCounter_WsMutation = ( + { __typename?: 'Mutation' } + & { addCounter?: Maybe<( + { __typename?: 'Counter' } + & Pick + )> } +); + +export type SyncCachedCounterMutationVariables = Exact<{ [key: string]: never; }>; + + +export type SyncCachedCounterMutation = ( + { __typename?: 'Mutation' } + & Pick +); + +export type CounterCacheQueryQueryVariables = Exact<{ [key: string]: never; }>; + + +export type CounterCacheQueryQuery = ( + { __typename?: 'Query' } + & { counterCache?: Maybe<( + { __typename?: 'Counter' } + & Pick + )> } +); + +export type CounterStateQueryVariables = Exact<{ [key: string]: never; }>; + + +export type CounterStateQuery = ( + { __typename?: 'Query' } + & { counterState?: Maybe<( + { __typename?: 'ClientCounter' } + & Pick + )> } +); + +export type CounterQueryQueryVariables = Exact<{ [key: string]: never; }>; + + +export type CounterQueryQuery = ( + { __typename?: 'Query' } + & { counter?: Maybe<( + { __typename?: 'Counter' } + & Pick + )> } +); + +export type OnCounterUpdatedSubscriptionVariables = Exact<{ [key: string]: never; }>; + + +export type OnCounterUpdatedSubscription = ( + { __typename?: 'Subscription' } + & { counterUpdated?: Maybe<( + { __typename?: 'Counter' } + & Pick + )> } +); + + + +export type ResolverTypeWrapper = Promise | T; + + +export type ResolverWithResolve = { + resolve: ResolverFn; +}; + +export type LegacyStitchingResolver = { + fragment: string; + resolve: ResolverFn; +}; + +export type NewStitchingResolver = { + selectionSet: string; + resolve: ResolverFn; +}; +export type StitchingResolver = LegacyStitchingResolver | NewStitchingResolver; +export type Resolver = + | ResolverFn + | ResolverWithResolve + | StitchingResolver; + +export type ResolverFn = ( + parent: TParent, + args: TArgs, + context: TContext, + info: GraphQLResolveInfo +) => Promise | TResult; + +export type SubscriptionSubscribeFn = ( + parent: TParent, + args: TArgs, + context: TContext, + info: GraphQLResolveInfo +) => AsyncIterator | Promise>; + +export type SubscriptionResolveFn = ( + parent: TParent, + args: TArgs, + context: TContext, + info: GraphQLResolveInfo +) => TResult | Promise; + +export interface SubscriptionSubscriberObject { + subscribe: SubscriptionSubscribeFn<{ [key in TKey]: TResult }, TParent, TContext, TArgs>; + resolve?: SubscriptionResolveFn; +} + +export interface SubscriptionResolverObject { + subscribe: SubscriptionSubscribeFn; + resolve: SubscriptionResolveFn; +} + +export type SubscriptionObject = + | SubscriptionSubscriberObject + | SubscriptionResolverObject; + +export type SubscriptionResolver = + | ((...args: any[]) => SubscriptionObject) + | SubscriptionObject; + +export type TypeResolveFn = ( + parent: TParent, + context: TContext, + info: GraphQLResolveInfo +) => Maybe | Promise>; + +export type IsTypeOfResolverFn = (obj: T, context: TContext, info: GraphQLResolveInfo) => boolean | Promise; + +export type NextResolverFn = () => Promise; + +export type DirectiveResolverFn = ( + next: NextResolverFn, + parent: TParent, + args: TArgs, + context: TContext, + info: GraphQLResolveInfo +) => TResult | Promise; + +/** Mapping between all available schema types and the resolvers types */ +export type ResolversTypes = { + Query: ResolverTypeWrapper<{}>; + Counter: ResolverTypeWrapper; + Int: ResolverTypeWrapper; + ClientCounter: ResolverTypeWrapper; + Mutation: ResolverTypeWrapper<{}>; + Boolean: ResolverTypeWrapper; + Subscription: ResolverTypeWrapper<{}>; + String: ResolverTypeWrapper; +}; + +/** Mapping between all available schema types and the resolvers parents */ +export type ResolversParentTypes = { + Query: {}; + Counter: Counter; + Int: Scalars['Int']; + ClientCounter: ClientCounter; + Mutation: {}; + Boolean: Scalars['Boolean']; + Subscription: {}; + String: Scalars['String']; +}; + +export type ClientCounterResolvers = { + counter?: Resolver, ParentType, ContextType>; + __isTypeOf?: IsTypeOfResolverFn; +}; + +export type CounterResolvers = { + amount?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}; + +export type MutationResolvers = { + addCounter?: Resolver, ParentType, ContextType, RequireFields>; + addCounterState?: Resolver, ParentType, ContextType, RequireFields>; + addMoleculerCounter?: Resolver, ParentType, ContextType, RequireFields>; + syncCachedCounter?: Resolver, ParentType, ContextType>; +}; + +export type QueryResolvers = { + counter?: Resolver, ParentType, ContextType>; + counterCache?: Resolver, ParentType, ContextType>; + counterState?: Resolver, ParentType, ContextType>; + moleculerCounter?: Resolver, ParentType, ContextType>; +}; + +export type SubscriptionResolvers = { + counterUpdated?: SubscriptionResolver, "counterUpdated", ParentType, ContextType>; + moleculerCounterUpdate?: SubscriptionResolver, "moleculerCounterUpdate", ParentType, ContextType>; +}; + +export type Resolvers = { + ClientCounter?: ClientCounterResolvers; + Counter?: CounterResolvers; + Mutation?: MutationResolvers; + Query?: QueryResolvers; + Subscription?: SubscriptionResolvers; +}; + + +/** + * @deprecated + * Use "Resolvers" root object instead. If you wish to get "IResolvers", add "typesPrefix: I" to your config. + */ +export type IResolvers = Resolvers; + + +export const AddCounterStateDocument = gql` + mutation addCounterState($amount: Int!) { + addCounterState(amount: $amount) @client { + counter + } +} + `; + +/** + * __useAddCounterStateMutation__ + * + * To run a mutation, you first call `useAddCounterStateMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useAddCounterStateMutation` returns a tuple that includes: + * - A mutate function that you can call at any time to execute the mutation + * - An object with fields that represent the current status of the mutation's execution + * + * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; + * + * @example + * const [addCounterStateMutation, { data, loading, error }] = useAddCounterStateMutation({ + * variables: { + * amount: // value for 'amount' + * }, + * }); + */ +export function useAddCounterStateMutation(baseOptions?: Apollo.MutationHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useMutation(AddCounterStateDocument, options); + } +export type AddCounterStateMutationHookResult = ReturnType; +export type AddCounterStateMutationResult = Apollo.MutationResult; +export type AddCounterStateMutationOptions = Apollo.BaseMutationOptions; +export const AddCounterDocument = gql` + mutation addCounter($amount: Int!) { + addCounter(amount: $amount) { + amount + } +} + `; + +/** + * __useAddCounterMutation__ + * + * To run a mutation, you first call `useAddCounterMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useAddCounterMutation` returns a tuple that includes: + * - A mutate function that you can call at any time to execute the mutation + * - An object with fields that represent the current status of the mutation's execution + * + * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; + * + * @example + * const [addCounterMutation, { data, loading, error }] = useAddCounterMutation({ + * variables: { + * amount: // value for 'amount' + * }, + * }); + */ +export function useAddCounterMutation(baseOptions?: Apollo.MutationHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useMutation(AddCounterDocument, options); + } +export type AddCounterMutationHookResult = ReturnType; +export type AddCounterMutationResult = Apollo.MutationResult; +export type AddCounterMutationOptions = Apollo.BaseMutationOptions; +export const AddCounter_WsDocument = gql` + mutation AddCounter_WS($amount: Int!) { + addCounter(amount: $amount) { + amount + } +} + `; + +/** + * __useAddCounter_WsMutation__ + * + * To run a mutation, you first call `useAddCounter_WsMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useAddCounter_WsMutation` returns a tuple that includes: + * - A mutate function that you can call at any time to execute the mutation + * - An object with fields that represent the current status of the mutation's execution + * + * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; + * + * @example + * const [addCounterWsMutation, { data, loading, error }] = useAddCounter_WsMutation({ + * variables: { + * amount: // value for 'amount' + * }, + * }); + */ +export function useAddCounter_WsMutation(baseOptions?: Apollo.MutationHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useMutation(AddCounter_WsDocument, options); + } +export type AddCounter_WsMutationHookResult = ReturnType; +export type AddCounter_WsMutationResult = Apollo.MutationResult; +export type AddCounter_WsMutationOptions = Apollo.BaseMutationOptions; +export const SyncCachedCounterDocument = gql` + mutation SyncCachedCounter { + syncCachedCounter +} + `; + +/** + * __useSyncCachedCounterMutation__ + * + * To run a mutation, you first call `useSyncCachedCounterMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useSyncCachedCounterMutation` returns a tuple that includes: + * - A mutate function that you can call at any time to execute the mutation + * - An object with fields that represent the current status of the mutation's execution + * + * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; + * + * @example + * const [syncCachedCounterMutation, { data, loading, error }] = useSyncCachedCounterMutation({ + * variables: { + * }, + * }); + */ +export function useSyncCachedCounterMutation(baseOptions?: Apollo.MutationHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useMutation(SyncCachedCounterDocument, options); + } +export type SyncCachedCounterMutationHookResult = ReturnType; +export type SyncCachedCounterMutationResult = Apollo.MutationResult; +export type SyncCachedCounterMutationOptions = Apollo.BaseMutationOptions; +export const CounterCacheQueryDocument = gql` + query counterCacheQuery { + counterCache { + amount + } +} + `; + +/** + * __useCounterCacheQueryQuery__ + * + * To run a query within a React component, call `useCounterCacheQueryQuery` and pass it any options that fit your needs. + * When your component renders, `useCounterCacheQueryQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useCounterCacheQueryQuery({ + * variables: { + * }, + * }); + */ +export function useCounterCacheQueryQuery(baseOptions?: Apollo.QueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useQuery(CounterCacheQueryDocument, options); + } +export function useCounterCacheQueryLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useLazyQuery(CounterCacheQueryDocument, options); + } +export type CounterCacheQueryQueryHookResult = ReturnType; +export type CounterCacheQueryLazyQueryHookResult = ReturnType; +export type CounterCacheQueryQueryResult = Apollo.QueryResult; +export const CounterStateDocument = gql` + query CounterState { + counterState @client { + counter + } +} + `; + +/** + * __useCounterStateQuery__ + * + * To run a query within a React component, call `useCounterStateQuery` and pass it any options that fit your needs. + * When your component renders, `useCounterStateQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useCounterStateQuery({ + * variables: { + * }, + * }); + */ +export function useCounterStateQuery(baseOptions?: Apollo.QueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useQuery(CounterStateDocument, options); + } +export function useCounterStateLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useLazyQuery(CounterStateDocument, options); + } +export type CounterStateQueryHookResult = ReturnType; +export type CounterStateLazyQueryHookResult = ReturnType; +export type CounterStateQueryResult = Apollo.QueryResult; +export const CounterQueryDocument = gql` + query counterQuery { + counter { + amount + } +} + `; + +/** + * __useCounterQueryQuery__ + * + * To run a query within a React component, call `useCounterQueryQuery` and pass it any options that fit your needs. + * When your component renders, `useCounterQueryQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useCounterQueryQuery({ + * variables: { + * }, + * }); + */ +export function useCounterQueryQuery(baseOptions?: Apollo.QueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useQuery(CounterQueryDocument, options); + } +export function useCounterQueryLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useLazyQuery(CounterQueryDocument, options); + } +export type CounterQueryQueryHookResult = ReturnType; +export type CounterQueryLazyQueryHookResult = ReturnType; +export type CounterQueryQueryResult = Apollo.QueryResult; +export const OnCounterUpdatedDocument = gql` + subscription onCounterUpdated { + counterUpdated { + amount + } +} + `; + +/** + * __useOnCounterUpdatedSubscription__ + * + * To run a query within a React component, call `useOnCounterUpdatedSubscription` and pass it any options that fit your needs. + * When your component renders, `useOnCounterUpdatedSubscription` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the subscription, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useOnCounterUpdatedSubscription({ + * variables: { + * }, + * }); + */ +export function useOnCounterUpdatedSubscription(baseOptions?: Apollo.SubscriptionHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useSubscription(OnCounterUpdatedDocument, options); + } +export type OnCounterUpdatedSubscriptionHookResult = ReturnType; +export type OnCounterUpdatedSubscriptionResult = Apollo.SubscriptionResult; +\`\`\` + +## packages-modules/counter/browser/src/index.ts + +\`\`\`ts +import { Feature } from '@common-stack/client-react'; + +import Common from './common'; +import ApolloCounter from './apollo-server-n-client'; +import ConnectedReactRouter from './connected-react-router'; +import Fela from './fela'; +import { ElectronTrayModule } from './connected-react-router/index.electron'; + +export default new Feature(Common, ConnectedReactRouter, ApolloCounter, Fela); +export { ElectronTrayModule }; + +\`\`\` + +## packages-modules/counter/browser/src/utils/index.ts + +\`\`\`ts +export * from './menu'; + +\`\`\` + +## packages-modules/counter/browser/src/utils/menu.ts + +\`\`\`ts +export const getFilteredMenus = (accountPageStore, selectedMenu) => + accountPageStore + .map((item) => { + if (selectedMenu.indexOf(item.key) !== -1) { + const { path, component, ...rest } = item; + return { + [path]: { name: rest.tab, ...rest }, + }; + } + }) + .filter((valid) => valid); + +export const getFilteredRoutes = (accountPageStore, selectedRoutes) => + accountPageStore + .map((item) => { + if (selectedRoutes.indexOf(item.key) !== -1) { + const { path } = item; + return { + [path]: item, + }; + } + return null; + }) + .filter((valid) => valid); + +export const getFilteredTabs = (accountPageStore, selectedTabs) => + accountPageStore + .map((item) => { + if (selectedTabs.indexOf(item.key) !== -1) { + const { component, ...rest } = item; + return rest; + } + }) + .filter((valid) => valid); + +\`\`\` + +## packages-modules/counter/browser/tsconfig.json + +\`\`\`json +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "allowSyntheticDefaultImports": true, + "experimentalDecorators": true, + "esModuleInterop": true, + "skipLibCheck": true, + "rootDir": "./src", + "outDir": "lib", + "declarationDir": "lib", + "declaration": true, + "declarationMap": true, + "types": [ + "@types/node", + "@types/jest", + "../../../typings", + ] + }, + "exclude": [ + "node_modules", + "lib", + "dist", + "webpack.config.js", + "rollup.config.js" + ], + "include": [ + "src", + "./typings/*.d.ts" + ] +} +\`\`\` + +## packages-modules/counter/browser/typings/graphql.d.ts + +\`\`\`ts +declare module "*/AddCounter.client.gql" { + import { DocumentNode } from "graphql"; + const defaultDocument: DocumentNode; + export const addCounterState: DocumentNode; + + export default defaultDocument; +} + +declare module "*/AddCounter.gql" { + import { DocumentNode } from "graphql"; + const defaultDocument: DocumentNode; + export const addCounter: DocumentNode; + + export default defaultDocument; +} + +declare module "*/CounterQuery.client.gql" { + import { DocumentNode } from "graphql"; + const defaultDocument: DocumentNode; + export const CounterState: DocumentNode; + + export default defaultDocument; +} + +declare module "*/CounterQuery.gql" { + import { DocumentNode } from "graphql"; + const defaultDocument: DocumentNode; + export const counterQuery: DocumentNode; + + export default defaultDocument; +} + +declare module "*/CounterSubscription.gql" { + import { DocumentNode } from "graphql"; + const defaultDocument: DocumentNode; + export const onCounterUpdated: DocumentNode; + + export default defaultDocument; +} + +\`\`\` + +## packages-modules/counter/browser/webpack.config.js + +\`\`\`js +const nodeExternals = require('webpack-node-externals'); +const webpack = require('webpack'); +const path = require('path'); + +const webpackOpts = { + mode: 'development', + entry: { + index: './src/index.ts', + }, + target: 'node', + output: { + path: path.join(__dirname, 'lib'), + filename: '[name].js', + libraryTarget: 'commonjs2', + }, + resolve: { + extensions: ['.ts', '.tsx', '.graphql', '.graphqls', '.gql', '.native.tsx', '.native.ts'], + }, + plugins: [ + new webpack.LoaderOptionsPlugin({ + options: { + test: /\.tsx?$/, + ts: { + compiler: 'typescript', + configFile: 'tsconfig.json', + }, + tslint: { + emitErrors: true, + failOnHint: true, + }, + }, + }), + ], + devtool: 'source-map', + module: { + rules: [ + { + test: /\.tsx?$/, + loaders: 'ts-loader', + options: { + compilerOptions: { + outDir: path.join(), + }, + }, + }, + { + test: /\.mjs$/, + include: /node_modules/, + type: 'javascript/auto', + }, + { + test: /\.(gql)$/, + exclude: /node_modules/, + use: ['graphql-tag/loader'], + }, + { + test: /\.graphql?/, + exclude: /node_modules/, + use: 'raw-loader', + }, + ], + }, + externals: [nodeExternals({ modulesDir: '../../../node_modules' }), nodeExternals()], +}; + +module.exports = webpackOpts; + +\`\`\` + +## packages-modules/counter/electron/jest.config.js + +\`\`\`js +const base = require('../../../jest.config.base'); +const packageJson = require('./package'); + +module.exports = { + ...base, + testEnvironment: 'jsdom', // This is overriden, from the base testEnvironment + name: packageJson.name, + displayName: packageJson.name, +}; +\`\`\` + +## packages-modules/counter/electron/rollup.config.js + +\`\`\`js +import graphql from '@rollup/plugin-graphql'; +import image from '@rollup/plugin-image'; +import typescript from '@rollup/plugin-typescript'; + +const bundle = (config) => ({ + ...config, + input: 'src/index.ts', + // marking all node modules as external + external: (id) => !/^[./]/.test(id), +}); +const globals = { react: 'React' }; + +export default [ + bundle({ + plugins: [typescript({ noEmitOnError: true }), image(), graphql()], + output: [ + { + dir: 'lib', + format: 'es', + name: 'Counter', + compact: true, + exports: 'named', + sourcemap: true, + preserveModules: true, + chunkFileNames: '[name]-[hash].[format].js', + globals, + }, + ], + }), +]; + +\`\`\` + +## packages-modules/counter/electron/src/epics/count-tray-updater.ts + +\`\`\`ts +/* eslint-disable @typescript-eslint/no-unused-vars */ +import { ofType } from 'redux-observable'; +import { Observable, of } from 'rxjs'; +import { distinctUntilChanged, map, tap, exhaustMap, pluck, catchError, filter } from 'rxjs/operators'; +import { ElectronTypes } from '@common-stack/client-core'; +import { CONNECTED_REACT_ROUTER_ACTION_TYPES } from '@sample-stack/counter-module-browser/lib/connected-react-router/constants/action-types'; + +export const onCountChangedEpic = ( + action$: Observable, + state$: Observable, + { container, routes }: { container: any; routes }, +) => + action$.pipe( + ofType(CONNECTED_REACT_ROUTER_ACTION_TYPES.INCREMENT), + exhaustMap(() => + state$.pipe( + pluck('connectedReactRouterCounter'), + distinctUntilChanged(), + tap((count) => { + const st = container.get(ElectronTypes.TrayWindow); + st.updateTitle(count.toString()); + }), + ofType('TRAY_UPDATED'), + ), + ), + ); + +\`\`\` + +## packages-modules/counter/electron/src/epics/index.ts + +\`\`\`ts +export * from './count-tray-updater'; + +\`\`\` + +## packages-modules/counter/electron/src/index.ts + +\`\`\`ts +import { Feature } from '@common-stack/client-react'; +import { connectedReactRouterCounter } from '@sample-stack/counter-module-browser/lib/connected-react-router/redux/reducers/counter'; +import { onCountChangedEpic } from './epics'; + +const ElectronMainModule = new Feature({ + reducer: { connectedReactRouterCounter }, + epic: [onCountChangedEpic], +}); + +export default new Feature(ElectronMainModule); + +\`\`\` + +## packages-modules/counter/electron/tsconfig.json + +\`\`\`json +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "allowSyntheticDefaultImports": true, + "experimentalDecorators": true, + "esModuleInterop": true, + "skipLibCheck": true, + "rootDir": "./src", + "outDir": "lib", + "declarationDir": "lib", + "declaration": true, + "declarationMap": true + }, + "exclude": [ + "node_modules", + "lib", + "dist", + "webpack.config.js", + "rollup.config.js" + ], + "include": [ + "src", + "./typings/*.d.ts" + ] +} +\`\`\` + +## packages-modules/counter/electron/typings/graphql.d.ts + +\`\`\`ts +declare module "*/AddCounter.client.gql" { + import { DocumentNode } from "graphql"; + const defaultDocument: DocumentNode; + export const addCounterState: DocumentNode; + + export default defaultDocument; +} + +declare module "*/AddCounter.gql" { + import { DocumentNode } from "graphql"; + const defaultDocument: DocumentNode; + export const addCounter: DocumentNode; + + export default defaultDocument; +} + +declare module "*/CounterQuery.client.gql" { + import { DocumentNode } from "graphql"; + const defaultDocument: DocumentNode; + export const CounterState: DocumentNode; + + export default defaultDocument; +} + +declare module "*/CounterQuery.gql" { + import { DocumentNode } from "graphql"; + const defaultDocument: DocumentNode; + export const counterQuery: DocumentNode; + + export default defaultDocument; +} + +declare module "*/CounterSubscription.gql" { + import { DocumentNode } from "graphql"; + const defaultDocument: DocumentNode; + export const onCounterUpdated: DocumentNode; + + export default defaultDocument; +} + +\`\`\` + +## packages-modules/counter/electron/webpack.config.js + +\`\`\`js +const nodeExternals = require('webpack-node-externals'); +const webpack = require('webpack'); +const path = require('path'); + +const webpackOpts = { + mode: 'development', + entry: { + index: './src/index.ts', + }, + target: 'node', + output: { + path: path.join(__dirname, 'lib'), + filename: '[name].js', + libraryTarget: 'commonjs2', + }, + resolve: { + extensions: ['.ts', '.tsx', '.js', '.graphql', '.graphqls', '.gql', '.native.tsx', '.native.ts'], + }, + plugins: [ + new webpack.LoaderOptionsPlugin({ + options: { + test: /\.tsx?$/, + ts: { + compiler: 'typescript', + configFile: 'tsconfig.json', + }, + tslint: { + emitErrors: true, + failOnHint: true, + }, + }, + }), + ], + devtool: 'source-map', + module: { + rules: [ + { + test: /\.tsx?$/, + loaders: 'ts-loader', + options: { + compilerOptions: { + outDir: path.join(), + }, + }, + }, + { + test: /\.mjs$/, + include: /node_modules/, + type: 'javascript/auto', + }, + { + test: /\.(gql)$/, + exclude: /node_modules/, + use: ['graphql-tag/loader'], + }, + { + test: /\.graphql?/, + exclude: /node_modules/, + use: 'raw-loader', + }, + ], + }, + externals: [ + nodeExternals({ + allowlist: [/^@sample-stack/], + modulesDir: '../../../node_modules', + }), + nodeExternals({ + allowlist: [/^@sample-stack/], + }), + ], +}; + +module.exports = webpackOpts; + +\`\`\` + +## packages-modules/counter/mobile/jest.config.js + +\`\`\`js +const base = require('../../../jest.config.base'); +const packageJson = require('./package'); + +module.exports = { + ...base, + testEnvironment: 'jsdom', // This is overriden, from the base testEnvironment + name: packageJson.name, + displayName: packageJson.name, +}; + +\`\`\` + +## packages-modules/counter/mobile/src/common/components/Dashboard.tsx + +\`\`\`tsx +import * as React from 'react'; +import { renderRoutes } from 'react-router-config'; + +export const Dashboard = (props) => <>{renderRoutes(props.route.routes, { matchPath: props.route.path })}; + +\`\`\` + +## packages-modules/counter/mobile/src/common/components/Home.tsx + +\`\`\`tsx +import * as React from 'react'; + +export const Home = () => ( +
+

FullStack-Pro

+
+); + +\`\`\` + +## packages-modules/counter/mobile/src/common/compute.tsx + +\`\`\`tsx +import * as React from 'react'; +import { IMenuPosition } from '@common-stack/client-react'; + +import { Home } from '../common/components/Home'; +import { getFilteredRoutes } from '../utils'; + +export const commonPageStore: any[] = [ + { + path: '/', + key: 'home', + exact: true, + name: 'Home', + component: Home, + position: IMenuPosition.MIDDLE, + }, +]; + +const selectedRoutesAndMenus = ['home']; + + +// get routes +const filteredRoutes = getFilteredRoutes(commonPageStore, selectedRoutesAndMenus); + +export { filteredRoutes }; + +\`\`\` + +## packages-modules/counter/mobile/src/common/generated-models.ts + +\`\`\`ts +/* tslint:disable */ + +import { GraphQLResolveInfo, GraphQLScalarType, GraphQLScalarTypeConfig } from 'graphql'; + +import gql from 'graphql-tag'; +import { MyContext } from './interfaces/context'; + +export type Maybe = T | null; +/** All built-in and custom scalars, mapped to their actual values */ +export type Scalars = { + ID: string; + String: string; + Boolean: boolean; + Int: number; + Float: number; + AnyObject: any; + JSON: any; + JSONObject: any; + Date: any; + Time: any; + DateTime: any; +}; + +export type ClientCounter = { + counter?: Maybe; +}; + +export type Counter = { + amount: Scalars['Int']; +}; + +export type FieldError = { + field: Scalars['String']; + message: Scalars['String']; +}; + +export type Mutation = { + dummy?: Maybe; + addCounterState?: Maybe; + addCounter?: Maybe; +}; + +export type MutationAddCounterStateArgs = { + amount: Scalars['Int']; +}; + +export type MutationAddCounterArgs = { + amount?: Maybe; +}; + +export type Node = { + id: Scalars['ID']; +}; + +export type Query = { + dummy?: Maybe; + counterState?: Maybe; + counter?: Maybe; +}; + +export type Subscription = { + dummy?: Maybe; + counterUpdated?: Maybe; +}; + +export type AddCounterStateMutationVariables = { + amount: Scalars['Int']; +}; + +export type AddCounterStateMutation = { __typename?: 'Mutation' } & { + addCounterState: Maybe<{ __typename?: 'ClientCounter' } & Pick>; +}; + +export type AddCounterMutationVariables = { + amount: Scalars['Int']; +}; + +export type AddCounterMutation = { __typename?: 'Mutation' } & { + addCounter: Maybe<{ __typename?: 'Counter' } & Pick>; +}; + +export type CounterStateQueryVariables = {}; + +export type CounterStateQuery = { __typename?: 'Query' } & { + counterState: Maybe<{ __typename?: 'ClientCounter' } & Pick>; +}; + +export type CounterQueryQueryVariables = {}; + +export type CounterQueryQuery = { __typename?: 'Query' } & { + counter: Maybe<{ __typename?: 'Counter' } & Pick>; +}; + +export type OnCounterUpdatedSubscriptionVariables = {}; + +export type OnCounterUpdatedSubscription = { __typename?: 'Subscription' } & { + counterUpdated: Maybe<{ __typename?: 'Counter' } & Pick>; +}; + +export type ResolverFn = ( + parent: TParent, + args: TArgs, + context: TContext, + info: GraphQLResolveInfo, +) => Promise | TResult; + +export type StitchingResolver = { + fragment: string; + resolve: ResolverFn; +}; + +export type Resolver = + | ResolverFn + | StitchingResolver; + +export type SubscriptionSubscribeFn = ( + parent: TParent, + args: TArgs, + context: TContext, + info: GraphQLResolveInfo, +) => AsyncIterator | Promise>; + +export type SubscriptionResolveFn = ( + parent: TParent, + args: TArgs, + context: TContext, + info: GraphQLResolveInfo, +) => TResult | Promise; + +export interface SubscriptionResolverObject { + subscribe: SubscriptionSubscribeFn; + resolve?: SubscriptionResolveFn; +} + +export type SubscriptionResolver = + | ((...args: any[]) => SubscriptionResolverObject) + | SubscriptionResolverObject; + +export type TypeResolveFn = ( + parent: TParent, + context: TContext, + info: GraphQLResolveInfo, +) => Maybe; + +export type NextResolverFn = () => Promise; + +export type DirectiveResolverFn = ( + next: NextResolverFn, + parent: TParent, + args: TArgs, + context: TContext, + info: GraphQLResolveInfo, +) => TResult | Promise; + +/** Mapping between all available schema types and the resolvers types */ +export type ResolversTypes = { + Query: {}; + Int: Scalars['Int']; + ClientCounter: ClientCounter; + Counter: Counter; + Mutation: {}; + Subscription: {}; + String: Scalars['String']; + Boolean: Scalars['Boolean']; + AnyObject: Scalars['AnyObject']; + JSON: Scalars['JSON']; + JSONObject: Scalars['JSONObject']; + FieldError: FieldError; + Node: Node; + ID: Scalars['ID']; + Date: Scalars['Date']; + Time: Scalars['Time']; + DateTime: Scalars['DateTime']; +}; + +export interface AnyObjectScalarConfig extends GraphQLScalarTypeConfig { + name: 'AnyObject'; +} + +export type ClientCounterResolvers = { + counter?: Resolver, ParentType, ContextType>; +}; + +export type CounterResolvers = { + amount?: Resolver; +}; + +export interface DateScalarConfig extends GraphQLScalarTypeConfig { + name: 'Date'; +} + +export interface DateTimeScalarConfig extends GraphQLScalarTypeConfig { + name: 'DateTime'; +} + +export type FieldErrorResolvers = { + field?: Resolver; + message?: Resolver; +}; + +export interface JsonScalarConfig extends GraphQLScalarTypeConfig { + name: 'JSON'; +} + +export interface JsonObjectScalarConfig extends GraphQLScalarTypeConfig { + name: 'JSONObject'; +} + +export type MutationResolvers = { + dummy?: Resolver, ParentType, ContextType>; + addCounterState?: Resolver< + Maybe, + ParentType, + ContextType, + MutationAddCounterStateArgs + >; + addCounter?: Resolver, ParentType, ContextType, MutationAddCounterArgs>; +}; + +export type NodeResolvers = { + __resolveType: TypeResolveFn; + id?: Resolver; +}; + +export type QueryResolvers = { + dummy?: Resolver, ParentType, ContextType>; + counterState?: Resolver, ParentType, ContextType>; + counter?: Resolver, ParentType, ContextType>; +}; + +export type SubscriptionResolvers = { + dummy?: SubscriptionResolver, ParentType, ContextType>; + counterUpdated?: SubscriptionResolver, ParentType, ContextType>; +}; + +export interface TimeScalarConfig extends GraphQLScalarTypeConfig { + name: 'Time'; +} + +export type Resolvers = { + AnyObject?: GraphQLScalarType; + ClientCounter?: ClientCounterResolvers; + Counter?: CounterResolvers; + Date?: GraphQLScalarType; + DateTime?: GraphQLScalarType; + FieldError?: FieldErrorResolvers; + JSON?: GraphQLScalarType; + JSONObject?: GraphQLScalarType; + Mutation?: MutationResolvers; + Node?: NodeResolvers; + Query?: QueryResolvers; + Subscription?: SubscriptionResolvers; + Time?: GraphQLScalarType; +}; + +/** + * @deprecated + * Use "Resolvers" root object instead. If you wish to get "IResolvers", add "typesPrefix: I" to your config. + */ +export type IResolvers = Resolvers; +export type Omit = Pick>; + +export const AddCounterStateDocument = gql` + mutation addCounterState($amount: Int!) { + addCounterState(amount: $amount) @client { + counter + } + } +`; +export const AddCounterDocument = gql` + mutation addCounter($amount: Int!) { + addCounter(amount: $amount) { + amount + } + } +`; +export const CounterStateDocument = gql` + query CounterState { + counterState @client { + counter + } + } +`; +export const CounterQueryDocument = gql` + query counterQuery { + counter { + amount + } + } +`; +export const OnCounterUpdatedDocument = gql` + subscription onCounterUpdated { + counterUpdated { + amount + } + } +`; + +\`\`\` + +## packages-modules/counter/mobile/src/common/index.ts + +\`\`\`ts +import { Feature } from '@common-stack/client-react'; +import common from './module'; + +export default new Feature(common); + +\`\`\` + +## packages-modules/counter/mobile/src/common/interfaces/context.ts + +\`\`\`ts +import { DataProxy } from '@apollo/client/cache'; +import { ApolloClient } from '@apollo/client'; + +export interface MyContext { + cache: DataProxy; + getCacheKey: (options: { __typename: string; resource?: string; id?: string }) => string; + apolloClient: ApolloClient; +} + +\`\`\` + +## packages-modules/counter/mobile/src/common/module.tsx + +\`\`\`tsx +import * as React from 'react'; + +import { Feature } from '@common-stack/client-react'; +import { filteredRoutes } from './compute'; + +export default new Feature({ + routeConfig: filteredRoutes, +}); + +\`\`\` + +## packages-modules/counter/mobile/src/connected-react-router/components/Counter.tsx + +\`\`\`tsx +import * as React from 'react'; +import { Button, View, Text } from 'react-native'; +import { Dispatch } from 'redux'; +import { connect } from 'react-redux'; +import { increment, decrement } from '../redux'; +import { State } from '../interfaces'; + +interface CounterStateProps { + count: number; +} + +interface CounterDispatchProps { + increment: () => void; + decrement: () => void; +} + +const CounterScreen: React.SFC = (props) => { + return ( + + + + Counter value: {props.count} + + +