├── .gitignore
├── .vscode
├── launch.json
├── settings.json
└── tasks.json
├── LICENSE
├── README.md
├── client.code-workspace
├── package-lock.json
├── package.json
├── packages
├── appkit-client
│ ├── .vscode
│ │ └── settings.json
│ ├── README.md
│ ├── src
│ │ ├── components
│ │ │ ├── AppBar.tsx
│ │ │ ├── AppContainer.tsx
│ │ │ ├── ButtonSimple.tsx
│ │ │ ├── CheckboxSimple.tsx
│ │ │ ├── ErrorBoundary.tsx
│ │ │ ├── InformationModal.tsx
│ │ │ ├── ListMenu.tsx
│ │ │ ├── LoginModal.tsx
│ │ │ ├── ModalSimple.tsx
│ │ │ ├── SingleTextInputModal.tsx
│ │ │ ├── SwitchSimple.tsx
│ │ │ ├── TextFieldSimple.tsx
│ │ │ ├── appContainer
│ │ │ │ └── theme.ts
│ │ │ └── index.ts
│ │ ├── index.ts
│ │ ├── model
│ │ │ ├── config.ts
│ │ │ ├── index.ts
│ │ │ └── menus.ts
│ │ ├── stores
│ │ │ ├── InputStore.ts
│ │ │ └── NotificationStore.ts
│ │ └── util
│ │ │ └── fetch.ts
│ └── tsconfig.json
├── appkit-common
│ ├── .vscode
│ │ ├── launch.json
│ │ └── settings.json
│ ├── src
│ │ └── model
│ │ │ ├── auth.ts
│ │ │ ├── index.ts
│ │ │ └── misc.ts
│ └── tsconfig.json
├── appkit-server
│ ├── .vscode
│ │ └── settings.json
│ ├── src
│ │ ├── model
│ │ │ └── config.ts
│ │ └── util
│ │ │ ├── auth.ts
│ │ │ ├── env.ts
│ │ │ ├── file.ts
│ │ │ ├── index.ts
│ │ │ └── time.ts
│ └── tsconfig.json
├── boilerplate-client
│ ├── .gitignore
│ ├── .vscode
│ │ ├── settings.json
│ │ └── tasks.json
│ ├── README.md
│ ├── jest.debug.config.json
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ │ ├── favicon.ico
│ │ └── index.html
│ ├── src
│ │ ├── components
│ │ │ ├── App.tsx
│ │ │ └── app
│ │ │ │ └── ConditionalModals.tsx
│ │ ├── config
│ │ │ └── config.ts
│ │ ├── index.tsx
│ │ ├── model
│ │ │ ├── index.ts
│ │ │ └── serverApi.ts
│ │ ├── pages
│ │ │ ├── ComponentsDemo.tsx
│ │ │ ├── Home.tsx
│ │ │ ├── HooksDemo.tsx
│ │ │ ├── ServerApiDemo.tsx
│ │ │ ├── home
│ │ │ │ └── home_content.md
│ │ │ ├── hooksDemo
│ │ │ │ ├── HooksTest.tsx
│ │ │ │ └── useComputedValue.ts
│ │ │ └── index.ts
│ │ ├── stores
│ │ │ ├── RootStore.ts
│ │ │ ├── UserStore.ts
│ │ │ └── ViewStore.ts
│ │ └── util
│ │ │ └── fetch.ts
│ ├── tsconfig.json
│ └── webpack.config.js
└── boilerplate-server
│ ├── .vscode
│ ├── launch.json
│ ├── settings.json
│ └── tasks.json
│ ├── Dockerfile
│ ├── package-lock.json
│ ├── package.json
│ ├── src
│ ├── config
│ │ ├── config.ts
│ │ ├── middleware.ts
│ │ └── version.ts
│ ├── logic
│ │ └── auth.ts
│ ├── routes
│ │ ├── auth.ts
│ │ ├── index.ts
│ │ └── test.ts
│ ├── server.ts
│ └── util
│ │ └── env.ts
│ ├── swaggerConfig.json
│ ├── tsconfig.json
│ └── webpack.config.js
├── server.code-workspace
├── tsconfig.base.json
└── tsconfig.json
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | lib
3 | dist
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "type": "node",
9 | "request": "launch",
10 | "name": "Launch Program",
11 | "program": "${workspaceFolder}\\index.js",
12 | "outFiles": [
13 | "${workspaceFolder}/**/*.js"
14 | ]
15 | }
16 | ]
17 | }
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "search.exclude": {
3 | "**/*.js": true
4 | },
5 | "prettier.semi": false,
6 | "prettier.singleQuote": true,
7 | "prettier.printWidth": 120
8 | }
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | // See https://go.microsoft.com/fwlink/?LinkId=733558
3 | // for the documentation about the tasks.json format
4 | "version": "2.0.0",
5 | "tasks": [
6 | {
7 | "label": "boilerplate-server-watch",
8 | "type": "npm",
9 | "script": "watch",
10 | "path": "packages/boilerplate-server/",
11 | "problemMatcher": []
12 | },
13 | {
14 | "label": "boilerplate-server-start",
15 | "type": "npm",
16 | "script": "start",
17 | "path": "packages/boilerplate-server/",
18 | "problemMatcher": []
19 | },
20 | {
21 | "label": "boilerplate-client-dev",
22 | "type": "npm",
23 | "script": "start",
24 | "path": "packages/boilerplate-client/",
25 | "problemMatcher": []
26 | },
27 | {
28 | "label": "boilerplate-server-all",
29 | "dependsOn": ["boilerplate-server-start", "boilerplate-server-watch"],
30 | "problemMatcher": []
31 | },
32 | {
33 | "label": "boilerplate-all",
34 | "dependsOn": ["boilerplate-server-all", "boilerplate-client-dev"],
35 | "problemMatcher": []
36 | },
37 | {
38 | "type": "npm",
39 | "script": "clean",
40 | "path": "packages/boilerplate-server/",
41 | "problemMatcher": []
42 | },
43 | {
44 | "type": "npm",
45 | "script": "watch",
46 | "path": "packages/boilerplate-server/",
47 | "problemMatcher": []
48 | }
49 | ]
50 | }
51 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Jeff Hull
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Typescript Fullstack Monorepo
2 |
3 | A fullstack boilerplate application using typescript, node, react, and mobx.
4 |
5 | # Quick Start
6 |
7 | ## Install dependencies
8 |
9 | ```
10 | git clone https://github.com/jeffkhull/typescript-fullstack-monorepo.git
11 | cd typescript-fullstack-monorepo
12 | npm i
13 | cd packages/boilerplate-server && npm i
14 | ```
15 |
16 | ## Start Processes
17 |
18 | ### If you are using vscode
19 |
20 | 1. From the project root, open client.code-workspaces and server.code-workspace in separate instances of vscode
21 | 2. In the client code workspace, run the vscode task "server all"
22 | 3. In the client code workspace, run the vscode task "client start"
23 |
24 | ### If you're not using vscode
25 |
26 | #### From a terminal in the project root
27 |
28 | 1. cd packages/boilerplate-server
29 | 2. npm run watch & npm run start
30 |
31 | #### From a second terminal in the project root
32 |
33 | 1. cd packages/boilerplate-client
34 | 2. npm run start
35 |
36 | # Dependencies
37 |
38 | 1. Node.js 8+
39 |
40 | # Motivation
41 |
42 | The large number of language, framework, library, and toolchain choices in today's javascript ecosystem can make it difficult to get started in any javascript development, let alone develop a full-stack application. This repository is one such full-stack application meant as an example and easy starting point for those interested in building an application using typescript, node, react, and mobx.
43 |
44 | # Technology Choices
45 |
46 | This boilerplate uses my current preferred development stack. I've moved on from other technologies and will continue to do so as I learn. Here's the reasoning behind the technologies used thus far, and some explanation for why I've chosen some tech over others.
47 |
48 | ## Typescript
49 |
50 | I've been impressed at the active role Microsoft has taken in the open source community, in large part caused (as far as I can tell) by the leadership of [Satya Nadella](https://twitter.com/satyanadella). Typescript is one Microsoft project whose momentum, quality, and versatility are impressive. I started with vanilla ES6 and after some time in development, came to appreciate the power of strong typing and static analysis. Many popular open source projects have been adopting typescript. Angular was an early adopter, and recently, Vue.js [announced version 3.0 was being written in typescript](https://medium.com/the-vue-point/plans-for-the-next-iteration-of-vue-js-777ffea6fabf). My first exposure to strong typing in javascript was [this video](https://www.youtube.com/watch?v=Qiqsg02nXFE), and several talks by [Andrews Hejlsberg](https://twitter.com/ahejlsberg) shed light on the design and philosophy behind Typescript. The javascript community as a whole has seen a flurry of innovation in the past few years, being rekindled perhaps with ES6 (AKA ES2015) and the leadership of the folks on the [TC39](https://github.com/tc39/proposals). Typescript's commitment to the community and commitment to support all official javascript features, so that it is a true superset and maintains compatibility with javascript as a whole, coupled with the power of its associated tooling has made Typescript the current clear choice for my projects.
51 |
52 | ### Cons of Typescript
53 |
54 | I recently came across [this article](https://medium.com/javascript-scene/the-typescript-tax-132ff4cb175b) which discusses the cons of Typescript with respect to Javascript. One of my favorite quotes from this article is
55 |
56 | `"Interfaces are one of the best features of TypeScript, and I wish this feature was built into JavaScript."`
57 |
58 | This comment resonates with me, as interfaces are the one TypeScript feature I have a hard time doing without. They provide a way to explicitly define and enforce the shape of objects, which makes it easier to understand how those values are intended to be used within an application. I think if interfaces were part of standard Javascript, I would seriously consider going back to plain Javascript as my language of choice.
59 |
60 | ## Mobx
61 |
62 | When I started developing with React, I adopted Redux as my state management library of choice. After working with it for a year or so, I became tired of the amount of code I needed to maintain X piece of shared state in my React application. That's when I ran into the ideas of [Michel Westrate](https://twitter.com/mweststrate) and his React state management library, [Mobx](https://medium.com/@mweststrate/interactive-introduction-to-mobx-and-reactjs-1760e448103c). After putting it off for a while, I tried mobx out in one of my apps, and I have since used it as my global state management solution of choice.
63 |
64 | ### Cons of Mobx as of May 1, 2019
65 |
66 | My biggest issue with Mobx is that the observable pattern runs counter to the direction of React and the majority of the React ecosystem. Thus by choosing to use mobx, I am making my code less understandable to React developers whose mental models are adapted to using the flux pattern. In addition, I'm introducing a net new, and non-trivial, abstraction of observables into my code, thus increasing the conceptual overhead of my solution.
67 |
68 | ### Reasons I'm sticking with Mobx as of May 1, 2019
69 |
70 | I was about to switch back to redux, leveraging [redux-react-hook](https://github.com/facebookincubator/redux-react-hook) and [immer](https://github.com/immerjs/immer) as means of reducing boilerplate and using hooks as a natural way to use state and dispatch within function components. However, midway though this refactoring, I realized I felt I was moving in the wrong direction. My main gripe with the redux approach is that now, to maintain a piece of state, instead of needing a single ES6 observable class with state and methods colocated, I need a reducer, action model, and action creators. Immer and redux-react-hook help to make redux more ergonomic, but even with those improvements, the overhead and fragmented concepts needed to maintain redux state vs an observable class are still a high price to pay. By choosing Mobx, I incur the costs associated with the cons listed above... but as of right now, I find choosing Mobx to be a net positive for productivity and simplicity of my codebase.
71 |
72 | ## No React Router
73 |
74 | Like many starting out with react, I started with React Router as my routing solution. However, when I started migrating to react router v4 from v3, I developed the opinion that having my components control my routes is backward in an application where the state of my components should be driven by the contents of my stores, not the other way around. So once again Michel Westrate writes [an article](https://hackernoon.com/how-to-decouple-state-and-ui-a-k-a-you-dont-need-componentwillmount-cc90b787aa37) laying out the reasons to ditch route aware components for a store based approach using the simple [director](https://github.com/flatiron/director) routing library.
75 |
76 | ## Function Components
77 |
78 | The introduction of hooks has made it possible to create a react application without any class components. I'm a big fan of this approach, one reason being the conciseness and simplicity of just using functions everywhere, and another being not needing to deal with "this." in my components.
79 |
80 | ## Disclaimer
81 |
82 | This repository contains one set of technology choices and doesn't intend to claim the choices made are the best possible. I'm always looking to optimize my tooling, so if you have any interesting alternatives, please let me know!
83 |
--------------------------------------------------------------------------------
/client.code-workspace:
--------------------------------------------------------------------------------
1 | {
2 | "folders": [
3 | {
4 | "path": "packages/appkit-client"
5 | },
6 | {
7 | "path": "packages/appkit-common"
8 | },
9 | {
10 | "path": "packages/boilerplate-client"
11 | }
12 | ],
13 | "settings": {
14 | "prettier.singleQuote": true,
15 | "prettier.semi": false
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "typescript-fullstack-monorepo",
3 | "version": "1.0.0",
4 | "description": "A fullstack example monorepo using typescript, node, react, and mobx.",
5 | "repository": {
6 | "type": "git",
7 | "url": "https://gitlab.com/jh23us-boilerplate/typescript-fullstack-boilerplate.git"
8 | },
9 | "author": "",
10 | "license": "ISC",
11 | "dependencies": {},
12 | "devDependencies": {
13 | "@material-ui/core": "^3.9.3",
14 | "@material-ui/icons": "^3.0.2",
15 | "@types/node": "^10.14.5",
16 | "@types/react": "^16.8.14",
17 | "cors": "^2.8.5",
18 | "director": "^1.2.8",
19 | "html-loader": "^0.5.5",
20 | "html-webpack-plugin": "^3.2.0",
21 | "jsonwebtoken": "^8.5.1",
22 | "jss-preset-default": "^4.5.0",
23 | "markdown-loader": "^4.0.0",
24 | "mobx": "^5.9.4",
25 | "mobx-react-lite": "^1.2.0",
26 | "nodemon": "^1.18.11",
27 | "prettier": "^1.17.0",
28 | "react": "^16.8.6",
29 | "react-dom": "^16.8.6",
30 | "rimraf": "^2.6.3",
31 | "ts-loader": "^5.4.3",
32 | "tsconfig-paths-webpack-plugin": "^3.2.0",
33 | "typescript": "^3.4.5",
34 | "webpack": "^4.30.0",
35 | "webpack-cli": "^3.3.1",
36 | "webpack-dev-server": "^3.3.1",
37 | "webpack-node-externals": "^1.7.2"
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/packages/appkit-client/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "prettier.singleQuote": true,
3 | "prettier.semi": false
4 | }
--------------------------------------------------------------------------------
/packages/appkit-client/README.md:
--------------------------------------------------------------------------------
1 | # Typescript Appkit Client
2 |
3 | A set of React components to enable rapid app development using React, Mobx, and Typescript
4 |
--------------------------------------------------------------------------------
/packages/appkit-client/src/components/AppBar.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import AccountCircle from '@material-ui/icons/AccountCircle'
3 | import IconButton from '@material-ui/core/IconButton'
4 | import Menu from '@material-ui/core/Menu'
5 | import MenuIcon from '@material-ui/icons/Menu'
6 | import MenuItem from '@material-ui/core/MenuItem'
7 | import Toolbar from '@material-ui/core/Toolbar'
8 | import Typography from '@material-ui/core/Typography'
9 | import { default as MuiAppbar } from '@material-ui/core/AppBar'
10 |
11 | export interface AppBarProps {
12 | leftMenuOnClick: () => void
13 | title: string
14 | loggedIn: boolean
15 | loginAction: () => void
16 | logoutAction: () => void
17 | }
18 |
19 | export const AppBar = (props: AppBarProps) => {
20 | const [anchorEl, setAnchorEl] = React.useState(null)
21 | // @observable anchorEl: any = null
22 |
23 | const handleMenu = event => {
24 | setAnchorEl(event.currentTarget)
25 | }
26 |
27 | const handleClose = () => {
28 | setAnchorEl(null)
29 | }
30 |
31 | return (
32 |
36 |
37 |
38 |
46 | {
50 | props.leftMenuOnClick()
51 | }}
52 | >
53 |
54 |
55 |
56 | {props.title}
57 |
58 |
59 |
60 |
66 | {' '}
67 |
68 |
69 |
106 |
107 |
108 |
109 |
110 | )
111 | }
112 |
--------------------------------------------------------------------------------
/packages/appkit-client/src/components/AppContainer.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import Drawer from '@material-ui/core/Drawer'
3 | import { AppBar } from './AppBar'
4 | import { AppkitMenuItem } from '../model'
5 | import { ClientBaseConfig } from '@appkit-client/model/config'
6 | import { ListMenu } from './ListMenu'
7 | import { MuiThemeProvider } from '@material-ui/core/styles'
8 |
9 | export interface AppkitContainerProps {
10 | menuItems: AppkitMenuItem[]
11 | config: ClientBaseConfig
12 | loggedIn: boolean
13 | loginAction: () => void
14 | logoutAction: () => void
15 | children?: JSX.Element | JSX.Element[]
16 | }
17 |
18 | export const AppContainer = (props: AppkitContainerProps) => {
19 | const [drawerOpen, setDrawerOpen] = React.useState(false)
20 |
21 | return (
22 | {
25 | setDrawerOpen(false)
26 | }}
27 | >
28 |
29 | {
33 | setDrawerOpen(!drawerOpen)
34 | }}
35 | title={props.config.appName + ' ' + (props.config.instanceName || '')}
36 | loggedIn={props.loggedIn}
37 | />
38 |
39 |
40 |
41 |
53 | {props.children}
54 |
55 |
56 |
57 | )
58 | }
59 |
--------------------------------------------------------------------------------
/packages/appkit-client/src/components/ButtonSimple.tsx:
--------------------------------------------------------------------------------
1 | import { Button as MuiButton } from '@material-ui/core'
2 | import * as React from 'react'
3 |
4 | export interface ButtonSimpleProps {
5 | label: string
6 | onClick: () => void
7 | noPadding?: boolean
8 | }
9 |
10 | export const ButtonSimple = (props: ButtonSimpleProps) => {
11 | const paddingOverride = props.noPadding === true ? '0px' : undefined
12 | return (
13 |
17 | {props.label}
18 |
19 | )
20 | }
21 |
--------------------------------------------------------------------------------
/packages/appkit-client/src/components/CheckboxSimple.tsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cyphercider/typescript-fullstack-monorepo/8875cf48b95e392bfa85f6af9125cfe4dfe8b273/packages/appkit-client/src/components/CheckboxSimple.tsx
--------------------------------------------------------------------------------
/packages/appkit-client/src/components/ErrorBoundary.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import { observable, runInAction } from 'mobx'
3 |
4 | export class ErrorBoundary extends React.Component {
5 | @observable hasError: boolean = false
6 | @observable errorMessage: string = ''
7 |
8 | constructor(props) {
9 | super(props)
10 | this.state = { hasError: false }
11 | }
12 |
13 | static getDerivedStateFromError(error) {
14 | return { hasError: true }
15 | }
16 |
17 | componentDidCatch(error, info) {
18 | runInAction(() => {
19 | this.hasError = true
20 | this.errorMessage = `React encountered an error
21 | ${error.toString()}
22 | ${info.componentStack}
23 | `
24 | })
25 | }
26 |
27 | render() {
28 | if (this.hasError) {
29 | return {this.errorMessage}
30 | }
31 |
32 | return this.props.children
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/packages/appkit-client/src/components/InformationModal.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import { ModalSimple } from '@appkit-client/index'
3 | import { Typography } from '@material-ui/core'
4 |
5 | interface Props {
6 | title: string
7 | message: string
8 | onClose: () => void
9 | }
10 |
11 | export const InformationModal = (props: Props) => {
12 | return (
13 |
14 | {props.message}
15 |
16 | )
17 | }
18 |
--------------------------------------------------------------------------------
/packages/appkit-client/src/components/ListMenu.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import { AppkitMenuItem } from '../model/menus'
3 | import { List, ListItem, ListItemText } from '@material-ui/core'
4 |
5 | interface ListMenuProps {
6 | menuItems: AppkitMenuItem[]
7 | }
8 |
9 | export const ListMenu = (props: ListMenuProps) => {
10 | return (
11 |
12 | {props.menuItems.map(item => {
13 | return (
14 |
15 | {item.name}
16 |
17 | )
18 | })}
19 |
20 | )
21 | }
22 |
--------------------------------------------------------------------------------
/packages/appkit-client/src/components/LoginModal.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import { ModalSimple } from './ModalSimple'
3 | import { TextFieldSimple } from './TextFieldSimple'
4 | import { Credentials } from '@appkit-common/model/auth'
5 |
6 | interface LoginModalProps {
7 | callbackWithCredentials: (cred: Credentials) => void
8 | closeModal: () => void
9 | }
10 |
11 | export const LoginModal = (props: LoginModalProps) => {
12 | const [userName, setUserName] = React.useState('')
13 | const [password, setPassword] = React.useState('')
14 |
15 | const doCommit = () => {
16 | props.callbackWithCredentials({
17 | userName: userName,
18 | password: password
19 | })
20 | props.closeModal()
21 | }
22 |
23 | return (
24 | {
27 | props.closeModal()
28 | }}
29 | onCommit={() => {
30 | doCommit()
31 | }}
32 | >
33 |
34 |
41 |
48 |
49 |
50 | )
51 | }
52 |
--------------------------------------------------------------------------------
/packages/appkit-client/src/components/ModalSimple.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import {
3 | Dialog,
4 | DialogActions,
5 | DialogTitle,
6 | DialogContent
7 | } from '@material-ui/core'
8 | import { ButtonSimple } from './ButtonSimple'
9 |
10 | interface Props {
11 | title: string
12 | onClose: () => void
13 | /**
14 | * To display OK button separate from close button, supply a commit function
15 | */
16 | onCommit?: () => void
17 | children?: JSX.Element
18 | }
19 |
20 | export const ModalSimple = (props: Props) => {
21 | return (
22 |
31 | {props.title}
32 | {props.children}
33 |
34 | {props.onCommit && }
35 |
39 |
40 |
41 | )
42 | }
43 |
--------------------------------------------------------------------------------
/packages/appkit-client/src/components/SingleTextInputModal.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import { ModalSimple } from '@appkit-client/index'
3 | import { TextFieldSimple } from '@appkit-client/components/index'
4 |
5 | interface Props {
6 | title: string
7 | message: string
8 | returnWithTextValue: (val: string) => void
9 | inputLabel: string
10 | style?: React.CSSProperties
11 | }
12 |
13 | export const SingleTextInputModal = (props: Props) => {
14 | const [inputText, setText] = React.useState('')
15 | return (
16 | {
19 | props.returnWithTextValue(inputText)
20 | }}
21 | >
22 | {
25 | props.returnWithTextValue(inputText)
26 | }}
27 | id="single-text-modal-input"
28 | label={props.inputLabel}
29 | updateValue={(val: string) => {
30 | setText(val)
31 | }}
32 | currentValue={inputText}
33 | style={props.style}
34 | />
35 |
36 | )
37 | }
38 |
--------------------------------------------------------------------------------
/packages/appkit-client/src/components/SwitchSimple.tsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cyphercider/typescript-fullstack-monorepo/8875cf48b95e392bfa85f6af9125cfe4dfe8b273/packages/appkit-client/src/components/SwitchSimple.tsx
--------------------------------------------------------------------------------
/packages/appkit-client/src/components/TextFieldSimple.tsx:
--------------------------------------------------------------------------------
1 | import { TextField } from '@material-ui/core'
2 | import * as React from 'react'
3 |
4 | export interface TextFieldSimpleProps {
5 | label: string
6 | updateValue: (val: string) => void
7 | currentValue: string
8 | passwordField?: boolean
9 | onCtrlEnter?: () => void
10 | style?: React.CSSProperties
11 | id?: string
12 | autofocus?: boolean
13 | }
14 |
15 | export const TextFieldSimple = (props: TextFieldSimpleProps) => {
16 | return (
17 | {
19 | if (event.ctrlKey && event.keyCode === 13) {
20 | if (props.onCtrlEnter != null) {
21 | props.onCtrlEnter()
22 | }
23 | }
24 | }}
25 | autoFocus={props.autofocus}
26 | type={props.passwordField ? 'password' : undefined}
27 | id={props.id}
28 | label={props.label}
29 | value={props.currentValue}
30 | onChange={(event: any) => {
31 | props.updateValue(event.target.value)
32 | }}
33 | style={{ width: '100%', ...props.style }}
34 | >
35 | {props.label}
36 |
37 | )
38 | }
39 |
--------------------------------------------------------------------------------
/packages/appkit-client/src/components/appContainer/theme.ts:
--------------------------------------------------------------------------------
1 | import { createMuiTheme } from '@material-ui/core/styles'
2 | import { Theme as MaterialUITheme } from '@material-ui/core'
3 | import blue from '@material-ui/core/colors/blue'
4 | import green from '@material-ui/core/colors/green'
5 | import { MarkdownTheme } from '../../model/config'
6 |
7 | export const blueMuiTheme: MaterialUITheme = createMuiTheme({
8 | typography: {
9 | useNextVariants: true
10 | },
11 | palette: {
12 | primary: blue,
13 | secondary: green
14 | },
15 | overrides: {
16 | MuiPaper: {
17 | root: {
18 | backgroundColor: 'rgb(240,240,240)'
19 | }
20 | },
21 | MuiToolbar: {
22 | dense: {
23 | minHeight: '50px'
24 | },
25 | root: {
26 | minHeight: '50px'
27 | },
28 | regular: {
29 | height: '50px',
30 | minHeight: '50px'
31 | }
32 | },
33 | MuiTypography: {
34 | root: { margin: undefined },
35 | body1: { fontSize: '14px' },
36 | h1: {
37 | marginBlockEnd: '10px',
38 | marginBlockStart: '10px',
39 | fontSize: '64px'
40 | },
41 | h2: { marginBlockEnd: '10px', marginBlockStart: '10px', fontSize: '46px' }
42 | }
43 | }
44 | })
45 |
46 | export const blueMarkdownTheme: MarkdownTheme = {
47 | h1: {
48 | ...blueMuiTheme.typography.h2,
49 | marginTop: '25px',
50 | marginBottom: '25px'
51 | },
52 | h2: {
53 | ...blueMuiTheme.typography.h3,
54 | marginTop: '13px',
55 | marginBottom: '13px',
56 | fontWeight: 300
57 | },
58 | h3: { ...blueMuiTheme.typography.h5, marginTop: '7px', marginBottom: '7px' },
59 | h4: undefined,
60 | h5: undefined,
61 | h6: undefined,
62 | p: { ...blueMuiTheme.typography.body1, fontSize: '1rem' },
63 | li: { ...blueMuiTheme.typography.body1, fontSize: '1rem' }
64 | }
65 |
--------------------------------------------------------------------------------
/packages/appkit-client/src/components/index.ts:
--------------------------------------------------------------------------------
1 | export * from './ButtonSimple'
2 | export * from './AppContainer'
3 | export * from './AppBar'
4 | export * from './ModalSimple'
5 | export * from './ErrorBoundary'
6 | export * from './appContainer/theme'
7 | export * from './TextFieldSimple'
8 | export * from './LoginModal'
9 |
--------------------------------------------------------------------------------
/packages/appkit-client/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './components/index'
2 | export * from './model/index'
3 |
--------------------------------------------------------------------------------
/packages/appkit-client/src/model/config.ts:
--------------------------------------------------------------------------------
1 | import { User } from '@appkit-common/model/auth'
2 | import { Theme } from '@material-ui/core'
3 | import * as React from 'react'
4 |
5 | export interface MarkdownTheme {
6 | h1?: React.CSSProperties
7 | h2?: React.CSSProperties
8 | h3?: React.CSSProperties
9 | h4?: React.CSSProperties
10 | h5?: React.CSSProperties
11 | h6?: React.CSSProperties
12 | p?: React.CSSProperties
13 | li?: React.CSSProperties
14 | }
15 |
16 | export interface ClientBaseConfig {
17 | /**
18 | * This is the name of the app - e.g. to be displayed in the app title bar
19 | */
20 | appName: string
21 | /**
22 | * This is the fully qualified server host for server API calls- i.e. protocol://host:port
23 | */
24 | host: string
25 | /**
26 | * If you have multiple application instances - this can keep track of the name of the instance you are in
27 | */
28 | instanceName: string
29 | /**
30 | * The client build version
31 | */
32 | clientVersion: string
33 | /**
34 | * The server build version. Will be received from server upon login
35 | */
36 | serverVersion: string | null
37 | /**
38 | * The current logged in user. Useful for displaying profile information to the user.
39 | */
40 | user: User | null
41 | /**
42 | * Whether or not our app is running in development mode
43 | */
44 | inDevelopment: boolean
45 | muiTheme: Theme
46 | markdownTheme: MarkdownTheme
47 | }
48 |
--------------------------------------------------------------------------------
/packages/appkit-client/src/model/index.ts:
--------------------------------------------------------------------------------
1 | // export client specific models
2 | export * from './config'
3 | export * from './menus'
4 |
5 | // export common models
6 | export * from '@appkit-common/model/index'
7 |
--------------------------------------------------------------------------------
/packages/appkit-client/src/model/menus.ts:
--------------------------------------------------------------------------------
1 | // list item / handler ifc here
2 |
3 | export interface AppkitMenuItem {
4 | name: string,
5 | onClick: () => void
6 | }
--------------------------------------------------------------------------------
/packages/appkit-client/src/stores/InputStore.ts:
--------------------------------------------------------------------------------
1 | import { runInAction, observable } from 'mobx'
2 |
3 | export default class InputStore {
4 | rootStore: any
5 | @observable inputTitle: string = ''
6 | singleTextValueReturnCallback: (strVal: string) => void
7 | @observable singleTextInputOpen: boolean = false
8 | @observable singleTextInputValue: string = ''
9 |
10 | constructor(rootStore: any) {
11 | this.rootStore = rootStore
12 | }
13 |
14 | openSingleTextInputModal = (
15 | title: string,
16 | callbackWithFinalValue: (val: string) => void
17 | ) => {
18 | runInAction(() => {
19 | this.inputTitle = title
20 | this.singleTextValueReturnCallback = callbackWithFinalValue
21 | this.singleTextInputOpen = true
22 | })
23 | }
24 |
25 | closeSingleTextInputModal = () => {
26 | runInAction(() => {
27 | this.singleTextInputOpen = false
28 | })
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/packages/appkit-client/src/stores/NotificationStore.ts:
--------------------------------------------------------------------------------
1 | import { action, observable } from 'mobx'
2 |
3 | export default class NotificationStore {
4 | rootStore: any
5 | @observable infoModalOpen: boolean = false
6 | @observable infoModalMessage: string = ''
7 | @observable infoModalTitle: string = ''
8 |
9 | constructor(rootStore: any) {
10 | this.rootStore = rootStore
11 | }
12 |
13 | @action
14 | displayInfoModal = (title: string, message: string) => {
15 | console.log(`DISPLAYING INFO MODAL`)
16 | this.infoModalMessage = message
17 | this.infoModalTitle = title
18 | this.infoModalOpen = true
19 | }
20 |
21 | @action
22 | closeInfoModal = () => {
23 | this.infoModalOpen = false
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/packages/appkit-client/src/util/fetch.ts:
--------------------------------------------------------------------------------
1 | export async function fetchBodyPost(
2 | url: string,
3 | reqBodyObject: Object,
4 | authorizationHeaderValue: string | null = null
5 | ) {
6 | try {
7 | let params: RequestInit = {
8 | method: 'POST',
9 | headers: {
10 | 'Content-Type': 'application/json; charset=utf-8'
11 | },
12 | body: JSON.stringify(reqBodyObject)
13 | }
14 |
15 | if (authorizationHeaderValue != null && params.headers != null) {
16 | params.headers['Authorization'] = authorizationHeaderValue
17 | }
18 |
19 | const res = await fetch(url, params)
20 | return await res.json()
21 | } catch (err) {
22 | console.error(`Problem calling fetch post from gui utils`)
23 | throw err
24 | }
25 | }
26 |
27 | export async function fetchBodyGet(
28 | url: string,
29 | authorizationHeaderValue: string | null = null
30 | ) {
31 | try {
32 | let params: RequestInit = {
33 | method: 'GET',
34 | mode: 'cors',
35 | headers: {}
36 | }
37 |
38 | if (authorizationHeaderValue != null && params.headers != null) {
39 | params.headers['Authorization'] = authorizationHeaderValue
40 | }
41 |
42 | const resRaw = await fetch(url, params)
43 | const res = await resRaw.json()
44 | // console.log(`returning`)
45 | // console.log(JSON.stringify(res))
46 | return res
47 | } catch (err) {
48 | console.error(`Problem calling fetch post from gui utils`)
49 | throw err
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/packages/appkit-client/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base",
3 | "compilerOptions": {
4 | "outDir": "lib",
5 | "jsx": "react",
6 | "baseUrl": "..",
7 | "lib": ["dom", "es2018"]
8 | },
9 | "include": ["./src/**/*", "../appkit-server/src/model/auth.ts"]
10 | }
11 |
--------------------------------------------------------------------------------
/packages/appkit-common/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "type": "node",
9 | "request": "launch",
10 | "name": "Launch Program",
11 | "program": "${workspaceFolder}\\index.js"
12 | }
13 | ]
14 | }
--------------------------------------------------------------------------------
/packages/appkit-common/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "prettier.singleQuote": true,
3 | "prettier.semi": false
4 | }
--------------------------------------------------------------------------------
/packages/appkit-common/src/model/auth.ts:
--------------------------------------------------------------------------------
1 | export interface Credentials {
2 | userName: string
3 | password: string
4 | }
5 |
6 | export interface User {
7 | userName: string
8 | email: string
9 | firstName: string
10 | lastName: string
11 | }
12 |
13 | export interface LoginRequest {
14 | credentials: Credentials
15 | }
16 |
17 | export interface LoginResponse {
18 | succeeded: boolean
19 | token: string
20 | user: User
21 | serverVersion: string
22 | }
23 |
24 | export interface CheckTokenRequest {
25 | token: string
26 | }
27 |
28 | export interface CheckTokenResponse {
29 | succeeded: boolean
30 | user: User
31 | serverVersion: string
32 | }
33 |
--------------------------------------------------------------------------------
/packages/appkit-common/src/model/index.ts:
--------------------------------------------------------------------------------
1 | export * from './auth'
2 | export * from './misc'
3 |
--------------------------------------------------------------------------------
/packages/appkit-common/src/model/misc.ts:
--------------------------------------------------------------------------------
1 | export interface StringContainer {
2 | string: string
3 | }
4 |
--------------------------------------------------------------------------------
/packages/appkit-common/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base",
3 | "compilerOptions": {
4 | "outDir": "lib",
5 | "jsx": "react",
6 | "baseUrl": ".."
7 | },
8 | "include": ["./src/**/*"]
9 | }
10 |
--------------------------------------------------------------------------------
/packages/appkit-server/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "prettier.semi": false,
3 | "prettier.singleQuote": true
4 | }
--------------------------------------------------------------------------------
/packages/appkit-server/src/model/config.ts:
--------------------------------------------------------------------------------
1 | export interface BaseServerConfig {
2 | port: number
3 | version: string
4 | authSecretKey: string
5 | configEnvironmentVariable: string
6 | rootDir: string
7 | nodeEnvironment: 'production' | 'development'
8 | }
9 |
--------------------------------------------------------------------------------
/packages/appkit-server/src/util/auth.ts:
--------------------------------------------------------------------------------
1 | import { sign, verify } from 'jsonwebtoken'
2 |
3 | /**
4 | * @returns {string} token
5 | */
6 | export function signJwt(payload: any, secretKey: string): string {
7 | return sign(payload, secretKey)
8 | }
9 |
10 | /**
11 | * @returns {string} decoded token
12 | */
13 | export function verifyJwt(jwt: string, secretKey: string): string {
14 | return verify(jwt, secretKey)
15 | }
16 |
--------------------------------------------------------------------------------
/packages/appkit-server/src/util/env.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * retrieve a JSON config environment variable, parse it, and return it
3 | */
4 | export function getServerConfigEnvVar(envVarName: string): Object {
5 | try {
6 | const varJson = process.env[envVarName]
7 | if (varJson == null) {
8 | throw new Error(`environment variable name ${envVarName} doesn't exist`)
9 | }
10 |
11 | try {
12 | const obj = JSON.parse(varJson) as Object
13 | return obj
14 | } catch (err) {
15 | throw new Error(`Error parsing environment variable JSON ${varJson} `)
16 | }
17 | } catch (err) {
18 | throw new Error(`Error getting environment variable ${envVarName} `)
19 | }
20 | }
21 |
22 | export function inProduction(): boolean {
23 | const env = process.env.NODE_ENV
24 | if (env == null) {
25 | return false
26 | }
27 |
28 | return env.toLowerCase().substr(0, 3) === 'dev'
29 | }
30 |
31 | /**
32 | * returns the location on disk of the location of node.exe which is running
33 | */
34 | export function getWorkingDirectory(): string {
35 | return process.cwd()
36 | }
37 |
--------------------------------------------------------------------------------
/packages/appkit-server/src/util/file.ts:
--------------------------------------------------------------------------------
1 | import * as fs from 'fs'
2 | import * as path from 'path'
3 |
4 | /**
5 | * Get an array of filenames at a provided directory
6 | *
7 | * IF the name returned
8 | */
9 | export async function getFileList(directoryPath: string): Promise {
10 | return new Promise((resolve: (res: string[]) => void, reject) => {
11 | const fileNames: string[] = []
12 | try {
13 | const files = fs.readdirSync(directoryPath)
14 |
15 | files.forEach(file => {
16 | const stat = fs.statSync(directoryPath)
17 | if (stat.isDirectory()) {
18 | const joinedName = path.join(directoryPath, file)
19 | const moreFiles = fs.readdirSync(joinedName)
20 | moreFiles.forEach(otherfile => {
21 | const nestedFile = path.join(joinedName, otherfile)
22 | if (fs.statSync(nestedFile).isDirectory() === false) {
23 | fileNames.push(nestedFile)
24 | }
25 | })
26 | // const recursed = await getFileList()
27 | fileNames.push()
28 | } else {
29 | fileNames.push(file)
30 | }
31 | })
32 | resolve(fileNames)
33 | } catch (err) {
34 | reject(err)
35 | }
36 | })
37 | }
38 |
39 | export async function getFileContentsString(filePath: string): Promise {
40 | return new Promise((resolve, reject) => {
41 | try {
42 | fs.readFile(filePath, { encoding: 'utf8' }, (err, data) => {
43 | if (err) {
44 | reject(err)
45 | }
46 | resolve(data)
47 | })
48 | } catch (err) {
49 | reject(err)
50 | }
51 | })
52 | }
53 |
--------------------------------------------------------------------------------
/packages/appkit-server/src/util/index.ts:
--------------------------------------------------------------------------------
1 | // export server specific utils
2 | export * from './auth'
3 | export * from './env'
4 | export * from './file'
5 | export * from './time'
6 |
7 | // export common utils once available
8 |
--------------------------------------------------------------------------------
/packages/appkit-server/src/util/time.ts:
--------------------------------------------------------------------------------
1 | export async function delay(msDelay: number): Promise {
2 | return new Promise(resolve => {
3 | setTimeout(() => {
4 | resolve()
5 | }, msDelay)
6 | })
7 | }
8 |
--------------------------------------------------------------------------------
/packages/appkit-server/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base",
3 | "compilerOptions": {
4 | "outDir": "lib",
5 | "baseUrl": "..",
6 | "lib": ["es2018"]
7 | },
8 | "include": ["./src/**/*", "../appkit-server/src/**/*"]
9 | }
10 |
--------------------------------------------------------------------------------
/packages/boilerplate-client/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
--------------------------------------------------------------------------------
/packages/boilerplate-client/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "prettier.singleQuote": true,
3 | "prettier.semi": false
4 | }
--------------------------------------------------------------------------------
/packages/boilerplate-client/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | // See https://go.microsoft.com/fwlink/?LinkId=733558
3 | // for the documentation about the tasks.json format
4 | "version": "2.0.0",
5 | "tasks": [
6 | {
7 | "label": "client start",
8 | "type": "npm",
9 | "script": "start",
10 | "problemMatcher": []
11 | },
12 | {
13 | "type": "npm",
14 | "script": "pack",
15 | "problemMatcher": []
16 | }
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/packages/boilerplate-client/README.md:
--------------------------------------------------------------------------------
1 | # File structure
2 |
3 | [Fractal](https://hackernoon.com/fractal-a-react-app-structure-for-infinite-scale-4dab943092af)
4 |
--------------------------------------------------------------------------------
/packages/boilerplate-client/jest.debug.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "transform": {
3 | "^.+\\.tsx?$": "ts-jest"
4 | },
5 | "testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$",
6 | "moduleFileExtensions": ["ts", "tsx", "js", "jsx", "json", "node"],
7 | "watchPathIgnorePatterns": ["/node_modules/"],
8 | "testPathIgnorePatterns": ["/node_modules/", "/src/"],
9 | "moduleNameMapper": {
10 | "^@root(.*)$": "/dist$1",
11 | "^@utils(.*)$": "/dist/utils$1",
12 | "^@components(.*)$": "/dist/components/generic$1",
13 | "^@stores(.*)$": "/dist/stores$1",
14 | "^@styles(.*)$": "/dist/styles$1",
15 | "^@model(.*)$": "/dist/model$1",
16 | "\\.(css|less)$": "/__mocks__/styleMock.js",
17 | "\\.(gif|ttf|eot|svg)$": "/__mocks__/fileMock.js"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/packages/boilerplate-client/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "client",
3 | "version": "1.0.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "@babel/runtime": {
8 | "version": "7.1.2",
9 | "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.1.2.tgz",
10 | "integrity": "sha512-Y3SCjmhSupzFB6wcv1KmmFucH6gDVnI30WjOcicV10ju0cZjak3Jcs67YLIXBrmZYw1xCrVeJPbycFwrqNyxpg==",
11 | "requires": {
12 | "regenerator-runtime": "^0.12.0"
13 | }
14 | },
15 | "@material-ui/core": {
16 | "version": "3.6.0",
17 | "resolved": "https://registry.npmjs.org/@material-ui/core/-/core-3.6.0.tgz",
18 | "integrity": "sha512-Erg9PI1xYa8pQFZf6tvWMGyoKCup+P3HJ6phagKGGPzNQ8dswpgW/qNkTOJNG2DaTtXDmkOswpjWcceHqURwYw==",
19 | "requires": {
20 | "@babel/runtime": "7.1.2",
21 | "@material-ui/utils": "^3.0.0-alpha.0",
22 | "@types/jss": "^9.5.6",
23 | "@types/react-transition-group": "^2.0.8",
24 | "brcast": "^3.0.1",
25 | "classnames": "^2.2.5",
26 | "csstype": "^2.5.2",
27 | "debounce": "^1.1.0",
28 | "deepmerge": "^2.0.1",
29 | "dom-helpers": "^3.2.1",
30 | "hoist-non-react-statics": "^3.0.0",
31 | "is-plain-object": "^2.0.4",
32 | "jss": "^9.3.3",
33 | "jss-camel-case": "^6.0.0",
34 | "jss-default-unit": "^8.0.2",
35 | "jss-global": "^3.0.0",
36 | "jss-nested": "^6.0.1",
37 | "jss-props-sort": "^6.0.0",
38 | "jss-vendor-prefixer": "^7.0.0",
39 | "keycode": "^2.1.9",
40 | "normalize-scroll-left": "^0.1.2",
41 | "popper.js": "^1.14.1",
42 | "prop-types": "^15.6.0",
43 | "react-event-listener": "^0.6.2",
44 | "react-transition-group": "^2.2.1",
45 | "recompose": "0.28.0 - 0.30.0",
46 | "warning": "^4.0.1"
47 | },
48 | "dependencies": {
49 | "hoist-non-react-statics": {
50 | "version": "3.2.0",
51 | "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.2.0.tgz",
52 | "integrity": "sha512-3IascCRfaEkbmHjJnUxWSspIUE1okLPjGTMVXW8zraUo1t3yg1BadKAxAGILHwgoBzmMnzrgeeaDGBvpuPz6dA==",
53 | "requires": {
54 | "react-is": "^16.3.2"
55 | }
56 | }
57 | }
58 | },
59 | "@material-ui/icons": {
60 | "version": "3.0.1",
61 | "resolved": "https://registry.npmjs.org/@material-ui/icons/-/icons-3.0.1.tgz",
62 | "integrity": "sha512-1kNcxYiIT1x8iDPEAlgmKrfRTIV8UyK6fLVcZ9kMHIKGWft9I451V5mvSrbCjbf7MX1TbLWzZjph0aVCRf9MqQ==",
63 | "requires": {
64 | "@babel/runtime": "7.0.0",
65 | "recompose": "^0.29.0"
66 | },
67 | "dependencies": {
68 | "@babel/runtime": {
69 | "version": "7.0.0",
70 | "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.0.0.tgz",
71 | "integrity": "sha512-7hGhzlcmg01CvH1EHdSPVXYX1aJ8KCEyz6I9xYIi/asDtzBPMyMhVibhM/K6g/5qnKBwjZtp10bNZIEFTRW1MA==",
72 | "requires": {
73 | "regenerator-runtime": "^0.12.0"
74 | }
75 | },
76 | "recompose": {
77 | "version": "0.29.0",
78 | "resolved": "https://registry.npmjs.org/recompose/-/recompose-0.29.0.tgz",
79 | "integrity": "sha512-J/qLXNU4W+AeHCDR70ajW8eMd1uroqZaECTj6qqDLPMILz3y0EzpYlvrnxKB9DnqcngWrtGwjXY9JeXaW9kS1A==",
80 | "requires": {
81 | "@babel/runtime": "^7.0.0",
82 | "change-emitter": "^0.1.2",
83 | "fbjs": "^0.8.1",
84 | "hoist-non-react-statics": "^2.3.1",
85 | "react-lifecycles-compat": "^3.0.2",
86 | "symbol-observable": "^1.0.4"
87 | }
88 | },
89 | "regenerator-runtime": {
90 | "version": "0.12.1",
91 | "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz",
92 | "integrity": "sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg=="
93 | }
94 | }
95 | },
96 | "@material-ui/utils": {
97 | "version": "3.0.0-alpha.0",
98 | "resolved": "https://registry.npmjs.org/@material-ui/utils/-/utils-3.0.0-alpha.0.tgz",
99 | "integrity": "sha512-HwC/pBcPG81UP4Jh0BaH2i4wAY9aDOrvL5+B/7J6cyFlG/Wip5Li+qI+GDlk9MR3kpG8v0sxfpWA0IlQYcHK6g==",
100 | "requires": {
101 | "@babel/runtime": "7.1.2"
102 | }
103 | },
104 | "@types/jss": {
105 | "version": "9.5.7",
106 | "resolved": "https://registry.npmjs.org/@types/jss/-/jss-9.5.7.tgz",
107 | "integrity": "sha512-OZimStu2QdDMtZ0h72JXqvLVbWUjXd5ZLk8vxLmfuC/nM1AabRyyGoxSufnzixrbpEcVcyy/JV5qeQu2JnjVZw==",
108 | "requires": {
109 | "csstype": "^2.0.0",
110 | "indefinite-observable": "^1.0.1"
111 | }
112 | },
113 | "@types/prop-types": {
114 | "version": "15.5.6",
115 | "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.5.6.tgz",
116 | "integrity": "sha512-ZBFR7TROLVzCkswA3Fmqq+IIJt62/T7aY/Dmz+QkU7CaW2QFqAitCE8Ups7IzmGhcN1YWMBT4Qcoc07jU9hOJQ=="
117 | },
118 | "@types/react": {
119 | "version": "16.7.8",
120 | "resolved": "https://registry.npmjs.org/@types/react/-/react-16.7.8.tgz",
121 | "integrity": "sha512-7Vua2IYokMiPBk0WWXaDt4Cg3XVcwU6HpkuPhvwhBrVEy4SafpcvEfGYByoY9jxyFMiegYQPaSOT+H+AY00CPw==",
122 | "requires": {
123 | "@types/prop-types": "*",
124 | "csstype": "^2.2.0"
125 | }
126 | },
127 | "@types/react-transition-group": {
128 | "version": "2.0.14",
129 | "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-2.0.14.tgz",
130 | "integrity": "sha512-pa7qB0/mkhwWMBFoXhX8BcntK8G4eQl4sIfSrJCxnivTYRQWjOWf2ClR9bWdm0EUFBDHzMbKYS+QYfDtBzkY4w==",
131 | "requires": {
132 | "@types/react": "*"
133 | }
134 | },
135 | "ansi-regex": {
136 | "version": "2.1.1",
137 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
138 | "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
139 | "dev": true
140 | },
141 | "asap": {
142 | "version": "2.0.6",
143 | "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
144 | "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY="
145 | },
146 | "big.js": {
147 | "version": "3.2.0",
148 | "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz",
149 | "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==",
150 | "dev": true
151 | },
152 | "boolbase": {
153 | "version": "1.0.0",
154 | "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
155 | "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=",
156 | "dev": true
157 | },
158 | "brcast": {
159 | "version": "3.0.1",
160 | "resolved": "https://registry.npmjs.org/brcast/-/brcast-3.0.1.tgz",
161 | "integrity": "sha512-eI3yqf9YEqyGl9PCNTR46MGvDylGtaHjalcz6Q3fAPnP/PhpKkkve52vFdfGpwp4VUvK6LUr4TQN+2stCrEwTg=="
162 | },
163 | "camel-case": {
164 | "version": "3.0.0",
165 | "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz",
166 | "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=",
167 | "dev": true,
168 | "requires": {
169 | "no-case": "^2.2.0",
170 | "upper-case": "^1.1.1"
171 | }
172 | },
173 | "change-emitter": {
174 | "version": "0.1.6",
175 | "resolved": "https://registry.npmjs.org/change-emitter/-/change-emitter-0.1.6.tgz",
176 | "integrity": "sha1-6LL+PX8at9aaMhma/5HqaTFAlRU="
177 | },
178 | "classnames": {
179 | "version": "2.2.6",
180 | "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz",
181 | "integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q=="
182 | },
183 | "clean-css": {
184 | "version": "4.2.1",
185 | "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz",
186 | "integrity": "sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==",
187 | "dev": true,
188 | "requires": {
189 | "source-map": "~0.6.0"
190 | },
191 | "dependencies": {
192 | "source-map": {
193 | "version": "0.6.1",
194 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
195 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
196 | "dev": true
197 | }
198 | }
199 | },
200 | "core-js": {
201 | "version": "1.2.7",
202 | "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz",
203 | "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY="
204 | },
205 | "core-util-is": {
206 | "version": "1.0.2",
207 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
208 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
209 | "dev": true
210 | },
211 | "css-select": {
212 | "version": "1.2.0",
213 | "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz",
214 | "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=",
215 | "dev": true,
216 | "requires": {
217 | "boolbase": "~1.0.0",
218 | "css-what": "2.1",
219 | "domutils": "1.5.1",
220 | "nth-check": "~1.0.1"
221 | }
222 | },
223 | "css-vendor": {
224 | "version": "0.3.8",
225 | "resolved": "https://registry.npmjs.org/css-vendor/-/css-vendor-0.3.8.tgz",
226 | "integrity": "sha1-ZCHP0wNM5mT+dnOXL9ARn8KJQfo=",
227 | "requires": {
228 | "is-in-browser": "^1.0.2"
229 | }
230 | },
231 | "css-what": {
232 | "version": "2.1.0",
233 | "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.0.tgz",
234 | "integrity": "sha1-lGfQMsOM+u+58teVASUwYvh/ob0=",
235 | "dev": true
236 | },
237 | "csstype": {
238 | "version": "2.5.7",
239 | "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.5.7.tgz",
240 | "integrity": "sha512-Nt5VDyOTIIV4/nRFswoCKps1R5CD1hkiyjBE9/thNaNZILLEviVw9yWQw15+O+CpNjQKB/uvdcxFFOrSflY3Yw=="
241 | },
242 | "debounce": {
243 | "version": "1.2.0",
244 | "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.0.tgz",
245 | "integrity": "sha512-mYtLl1xfZLi1m4RtQYlZgJUNQjl4ZxVnHzIR8nLLgi4q1YT8o/WM+MK/f8yfcc9s5Ir5zRaPZyZU6xs1Syoocg=="
246 | },
247 | "deepmerge": {
248 | "version": "2.1.1",
249 | "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.1.1.tgz",
250 | "integrity": "sha512-urQxA1smbLZ2cBbXbaYObM1dJ82aJ2H57A1C/Kklfh/ZN1bgH4G/n5KWhdNfOK11W98gqZfyYj7W4frJJRwA2w=="
251 | },
252 | "define-properties": {
253 | "version": "1.1.3",
254 | "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
255 | "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
256 | "dev": true,
257 | "requires": {
258 | "object-keys": "^1.0.12"
259 | }
260 | },
261 | "director": {
262 | "version": "1.2.8",
263 | "resolved": "https://registry.npmjs.org/director/-/director-1.2.8.tgz",
264 | "integrity": "sha1-xtm03YkOmv9TZRg/6cyOc5lM8tU="
265 | },
266 | "dom-converter": {
267 | "version": "0.1.4",
268 | "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.1.4.tgz",
269 | "integrity": "sha1-pF71cnuJDJv/5tfIduexnLDhfzs=",
270 | "dev": true,
271 | "requires": {
272 | "utila": "~0.3"
273 | },
274 | "dependencies": {
275 | "utila": {
276 | "version": "0.3.3",
277 | "resolved": "https://registry.npmjs.org/utila/-/utila-0.3.3.tgz",
278 | "integrity": "sha1-1+jn1+MJEHCSsF+NloiCTWM6QiY=",
279 | "dev": true
280 | }
281 | }
282 | },
283 | "dom-helpers": {
284 | "version": "3.4.0",
285 | "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz",
286 | "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==",
287 | "requires": {
288 | "@babel/runtime": "^7.1.2"
289 | }
290 | },
291 | "dom-serializer": {
292 | "version": "0.1.0",
293 | "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz",
294 | "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=",
295 | "dev": true,
296 | "requires": {
297 | "domelementtype": "~1.1.1",
298 | "entities": "~1.1.1"
299 | },
300 | "dependencies": {
301 | "domelementtype": {
302 | "version": "1.1.3",
303 | "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz",
304 | "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=",
305 | "dev": true
306 | }
307 | }
308 | },
309 | "domelementtype": {
310 | "version": "1.3.0",
311 | "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz",
312 | "integrity": "sha1-sXrtguirWeUt2cGbF1bg/BhyBMI=",
313 | "dev": true
314 | },
315 | "domhandler": {
316 | "version": "2.1.0",
317 | "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.1.0.tgz",
318 | "integrity": "sha1-0mRvXlf2w7qxHPbLBdPArPdBJZQ=",
319 | "dev": true,
320 | "requires": {
321 | "domelementtype": "1"
322 | }
323 | },
324 | "domutils": {
325 | "version": "1.5.1",
326 | "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz",
327 | "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=",
328 | "dev": true,
329 | "requires": {
330 | "dom-serializer": "0",
331 | "domelementtype": "1"
332 | }
333 | },
334 | "emojis-list": {
335 | "version": "2.1.0",
336 | "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz",
337 | "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=",
338 | "dev": true
339 | },
340 | "encoding": {
341 | "version": "0.1.12",
342 | "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz",
343 | "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=",
344 | "requires": {
345 | "iconv-lite": "~0.4.13"
346 | }
347 | },
348 | "entities": {
349 | "version": "1.1.1",
350 | "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz",
351 | "integrity": "sha1-blwtClYhtdra7O+AuQ7ftc13cvA=",
352 | "dev": true
353 | },
354 | "es-abstract": {
355 | "version": "1.12.0",
356 | "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.12.0.tgz",
357 | "integrity": "sha512-C8Fx/0jFmV5IPoMOFPA9P9G5NtqW+4cOPit3MIuvR2t7Ag2K15EJTpxnHAYTzL+aYQJIESYeXZmDBfOBE1HcpA==",
358 | "dev": true,
359 | "requires": {
360 | "es-to-primitive": "^1.1.1",
361 | "function-bind": "^1.1.1",
362 | "has": "^1.0.1",
363 | "is-callable": "^1.1.3",
364 | "is-regex": "^1.0.4"
365 | }
366 | },
367 | "es-to-primitive": {
368 | "version": "1.1.1",
369 | "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz",
370 | "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=",
371 | "dev": true,
372 | "requires": {
373 | "is-callable": "^1.1.1",
374 | "is-date-object": "^1.0.1",
375 | "is-symbol": "^1.0.1"
376 | }
377 | },
378 | "fbjs": {
379 | "version": "0.8.17",
380 | "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz",
381 | "integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=",
382 | "requires": {
383 | "core-js": "^1.0.0",
384 | "isomorphic-fetch": "^2.1.1",
385 | "loose-envify": "^1.0.0",
386 | "object-assign": "^4.1.0",
387 | "promise": "^7.1.1",
388 | "setimmediate": "^1.0.5",
389 | "ua-parser-js": "^0.7.18"
390 | }
391 | },
392 | "function-bind": {
393 | "version": "1.1.1",
394 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
395 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
396 | "dev": true
397 | },
398 | "has": {
399 | "version": "1.0.3",
400 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
401 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
402 | "dev": true,
403 | "requires": {
404 | "function-bind": "^1.1.1"
405 | }
406 | },
407 | "he": {
408 | "version": "1.1.1",
409 | "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz",
410 | "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=",
411 | "dev": true
412 | },
413 | "hoist-non-react-statics": {
414 | "version": "2.5.5",
415 | "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz",
416 | "integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw=="
417 | },
418 | "html-minifier": {
419 | "version": "3.5.20",
420 | "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.20.tgz",
421 | "integrity": "sha512-ZmgNLaTp54+HFKkONyLFEfs5dd/ZOtlquKaTnqIWFmx3Av5zG6ZPcV2d0o9XM2fXOTxxIf6eDcwzFFotke/5zA==",
422 | "dev": true,
423 | "requires": {
424 | "camel-case": "3.0.x",
425 | "clean-css": "4.2.x",
426 | "commander": "2.17.x",
427 | "he": "1.1.x",
428 | "param-case": "2.1.x",
429 | "relateurl": "0.2.x",
430 | "uglify-js": "3.4.x"
431 | },
432 | "dependencies": {
433 | "commander": {
434 | "version": "2.17.1",
435 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz",
436 | "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==",
437 | "dev": true
438 | },
439 | "source-map": {
440 | "version": "0.6.1",
441 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
442 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
443 | "dev": true
444 | },
445 | "uglify-js": {
446 | "version": "3.4.9",
447 | "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.9.tgz",
448 | "integrity": "sha512-8CJsbKOtEbnJsTyv6LE6m6ZKniqMiFWmm9sRbopbkGs3gMPPfd3Fh8iIA4Ykv5MgaTbqHr4BaoGLJLZNhsrW1Q==",
449 | "dev": true,
450 | "requires": {
451 | "commander": "~2.17.1",
452 | "source-map": "~0.6.1"
453 | }
454 | }
455 | }
456 | },
457 | "html-webpack-plugin": {
458 | "version": "3.2.0",
459 | "resolved": "http://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz",
460 | "integrity": "sha1-sBq71yOsqqeze2r0SS69oD2d03s=",
461 | "dev": true,
462 | "requires": {
463 | "html-minifier": "^3.2.3",
464 | "loader-utils": "^0.2.16",
465 | "lodash": "^4.17.3",
466 | "pretty-error": "^2.0.2",
467 | "tapable": "^1.0.0",
468 | "toposort": "^1.0.0",
469 | "util.promisify": "1.0.0"
470 | },
471 | "dependencies": {
472 | "loader-utils": {
473 | "version": "0.2.17",
474 | "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz",
475 | "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=",
476 | "dev": true,
477 | "requires": {
478 | "big.js": "^3.1.3",
479 | "emojis-list": "^2.0.0",
480 | "json5": "^0.5.0",
481 | "object-assign": "^4.0.1"
482 | }
483 | }
484 | }
485 | },
486 | "htmlparser2": {
487 | "version": "3.3.0",
488 | "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.3.0.tgz",
489 | "integrity": "sha1-zHDQWln2VC5D8OaFyYLhTJJKnv4=",
490 | "dev": true,
491 | "requires": {
492 | "domelementtype": "1",
493 | "domhandler": "2.1",
494 | "domutils": "1.1",
495 | "readable-stream": "1.0"
496 | },
497 | "dependencies": {
498 | "domutils": {
499 | "version": "1.1.6",
500 | "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.1.6.tgz",
501 | "integrity": "sha1-vdw94Jm5ou+sxRxiPyj0FuzFdIU=",
502 | "dev": true,
503 | "requires": {
504 | "domelementtype": "1"
505 | }
506 | },
507 | "readable-stream": {
508 | "version": "1.0.34",
509 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
510 | "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=",
511 | "dev": true,
512 | "requires": {
513 | "core-util-is": "~1.0.0",
514 | "inherits": "~2.0.1",
515 | "isarray": "0.0.1",
516 | "string_decoder": "~0.10.x"
517 | }
518 | },
519 | "string_decoder": {
520 | "version": "0.10.31",
521 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
522 | "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
523 | "dev": true
524 | }
525 | }
526 | },
527 | "hyphenate-style-name": {
528 | "version": "1.0.2",
529 | "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.2.tgz",
530 | "integrity": "sha1-MRYKNpMK2vH8BMYHT360FGXU7Es="
531 | },
532 | "iconv-lite": {
533 | "version": "0.4.24",
534 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
535 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
536 | "requires": {
537 | "safer-buffer": ">= 2.1.2 < 3"
538 | }
539 | },
540 | "indefinite-observable": {
541 | "version": "1.0.2",
542 | "resolved": "https://registry.npmjs.org/indefinite-observable/-/indefinite-observable-1.0.2.tgz",
543 | "integrity": "sha512-Mps0898zEduHyPhb7UCgNmfzlqNZknVmaFz5qzr0mm04YQ5FGLhAyK/dJ+NaRxGyR6juQXIxh5Ev0xx+qq0nYA==",
544 | "requires": {
545 | "symbol-observable": "1.2.0"
546 | }
547 | },
548 | "inherits": {
549 | "version": "2.0.3",
550 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
551 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
552 | "dev": true
553 | },
554 | "is-callable": {
555 | "version": "1.1.4",
556 | "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz",
557 | "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==",
558 | "dev": true
559 | },
560 | "is-date-object": {
561 | "version": "1.0.1",
562 | "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz",
563 | "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=",
564 | "dev": true
565 | },
566 | "is-in-browser": {
567 | "version": "1.1.3",
568 | "resolved": "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz",
569 | "integrity": "sha1-Vv9NtoOgeMYILrldrX3GLh0E+DU="
570 | },
571 | "is-plain-object": {
572 | "version": "2.0.4",
573 | "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
574 | "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
575 | "requires": {
576 | "isobject": "^3.0.1"
577 | }
578 | },
579 | "is-regex": {
580 | "version": "1.0.4",
581 | "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz",
582 | "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=",
583 | "dev": true,
584 | "requires": {
585 | "has": "^1.0.1"
586 | }
587 | },
588 | "is-stream": {
589 | "version": "1.1.0",
590 | "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
591 | "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ="
592 | },
593 | "is-symbol": {
594 | "version": "1.0.1",
595 | "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz",
596 | "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=",
597 | "dev": true
598 | },
599 | "isarray": {
600 | "version": "0.0.1",
601 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
602 | "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
603 | "dev": true
604 | },
605 | "isobject": {
606 | "version": "3.0.1",
607 | "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
608 | "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8="
609 | },
610 | "isomorphic-fetch": {
611 | "version": "2.2.1",
612 | "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz",
613 | "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=",
614 | "requires": {
615 | "node-fetch": "^1.0.1",
616 | "whatwg-fetch": ">=0.10.0"
617 | }
618 | },
619 | "js-tokens": {
620 | "version": "4.0.0",
621 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
622 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
623 | },
624 | "json5": {
625 | "version": "0.5.1",
626 | "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz",
627 | "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=",
628 | "dev": true
629 | },
630 | "jss": {
631 | "version": "9.8.7",
632 | "resolved": "https://registry.npmjs.org/jss/-/jss-9.8.7.tgz",
633 | "integrity": "sha512-awj3XRZYxbrmmrx9LUSj5pXSUfm12m8xzi/VKeqI1ZwWBtQ0kVPTs3vYs32t4rFw83CgFDukA8wKzOE9sMQnoQ==",
634 | "requires": {
635 | "is-in-browser": "^1.1.3",
636 | "symbol-observable": "^1.1.0",
637 | "warning": "^3.0.0"
638 | },
639 | "dependencies": {
640 | "warning": {
641 | "version": "3.0.0",
642 | "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz",
643 | "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=",
644 | "requires": {
645 | "loose-envify": "^1.0.0"
646 | }
647 | }
648 | }
649 | },
650 | "jss-camel-case": {
651 | "version": "6.1.0",
652 | "resolved": "https://registry.npmjs.org/jss-camel-case/-/jss-camel-case-6.1.0.tgz",
653 | "integrity": "sha512-HPF2Q7wmNW1t79mCqSeU2vdd/vFFGpkazwvfHMOhPlMgXrJDzdj9viA2SaHk9ZbD5pfL63a8ylp4++irYbbzMQ==",
654 | "requires": {
655 | "hyphenate-style-name": "^1.0.2"
656 | }
657 | },
658 | "jss-default-unit": {
659 | "version": "8.0.2",
660 | "resolved": "https://registry.npmjs.org/jss-default-unit/-/jss-default-unit-8.0.2.tgz",
661 | "integrity": "sha512-WxNHrF/18CdoAGw2H0FqOEvJdREXVXLazn7PQYU7V6/BWkCV0GkmWsppNiExdw8dP4TU1ma1dT9zBNJ95feLmg=="
662 | },
663 | "jss-global": {
664 | "version": "3.0.0",
665 | "resolved": "https://registry.npmjs.org/jss-global/-/jss-global-3.0.0.tgz",
666 | "integrity": "sha512-wxYn7vL+TImyQYGAfdplg7yaxnPQ9RaXY/cIA8hawaVnmmWxDHzBK32u1y+RAvWboa3lW83ya3nVZ/C+jyjZ5Q=="
667 | },
668 | "jss-nested": {
669 | "version": "6.0.1",
670 | "resolved": "https://registry.npmjs.org/jss-nested/-/jss-nested-6.0.1.tgz",
671 | "integrity": "sha512-rn964TralHOZxoyEgeq3hXY8hyuCElnvQoVrQwKHVmu55VRDd6IqExAx9be5HgK0yN/+hQdgAXQl/GUrBbbSTA==",
672 | "requires": {
673 | "warning": "^3.0.0"
674 | },
675 | "dependencies": {
676 | "warning": {
677 | "version": "3.0.0",
678 | "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz",
679 | "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=",
680 | "requires": {
681 | "loose-envify": "^1.0.0"
682 | }
683 | }
684 | }
685 | },
686 | "jss-props-sort": {
687 | "version": "6.0.0",
688 | "resolved": "https://registry.npmjs.org/jss-props-sort/-/jss-props-sort-6.0.0.tgz",
689 | "integrity": "sha512-E89UDcrphmI0LzmvYk25Hp4aE5ZBsXqMWlkFXS0EtPkunJkRr+WXdCNYbXbksIPnKlBenGB9OxzQY+mVc70S+g=="
690 | },
691 | "jss-vendor-prefixer": {
692 | "version": "7.0.0",
693 | "resolved": "https://registry.npmjs.org/jss-vendor-prefixer/-/jss-vendor-prefixer-7.0.0.tgz",
694 | "integrity": "sha512-Agd+FKmvsI0HLcYXkvy8GYOw3AAASBUpsmIRvVQheps+JWaN892uFOInTr0DRydwaD91vSSUCU4NssschvF7MA==",
695 | "requires": {
696 | "css-vendor": "^0.3.8"
697 | }
698 | },
699 | "keycode": {
700 | "version": "2.2.0",
701 | "resolved": "https://registry.npmjs.org/keycode/-/keycode-2.2.0.tgz",
702 | "integrity": "sha1-PQr1bce4uOXLqNCpfxByBO7CKwQ="
703 | },
704 | "lodash": {
705 | "version": "4.17.10",
706 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz",
707 | "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==",
708 | "dev": true
709 | },
710 | "loose-envify": {
711 | "version": "1.4.0",
712 | "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
713 | "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
714 | "requires": {
715 | "js-tokens": "^3.0.0 || ^4.0.0"
716 | }
717 | },
718 | "lower-case": {
719 | "version": "1.1.4",
720 | "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz",
721 | "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=",
722 | "dev": true
723 | },
724 | "mobx": {
725 | "version": "5.6.0",
726 | "resolved": "https://registry.npmjs.org/mobx/-/mobx-5.6.0.tgz",
727 | "integrity": "sha512-xrA0tBnSMANXCDjS2W/6YTasesA8mkg9o+v/Bw/OcbXaRVE6/soVwDMWIh7A6TwMDY4tYQprQJZ5WQN1TRSb3A=="
728 | },
729 | "mobx-react": {
730 | "version": "5.4.2",
731 | "resolved": "https://registry.npmjs.org/mobx-react/-/mobx-react-5.4.2.tgz",
732 | "integrity": "sha512-alSN0KDAAOb1OkYujfoJjjk0JWxWRKO4sLGB4hN2CuvaJMrlj7bhGQe7CBMJvEFNjtJRbhJcquYVjQ3rrH2zQQ==",
733 | "requires": {
734 | "hoist-non-react-statics": "^3.0.0",
735 | "react-lifecycles-compat": "^3.0.2"
736 | },
737 | "dependencies": {
738 | "hoist-non-react-statics": {
739 | "version": "3.2.0",
740 | "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.2.0.tgz",
741 | "integrity": "sha512-3IascCRfaEkbmHjJnUxWSspIUE1okLPjGTMVXW8zraUo1t3yg1BadKAxAGILHwgoBzmMnzrgeeaDGBvpuPz6dA==",
742 | "requires": {
743 | "react-is": "^16.3.2"
744 | }
745 | }
746 | }
747 | },
748 | "no-case": {
749 | "version": "2.3.2",
750 | "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz",
751 | "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==",
752 | "dev": true,
753 | "requires": {
754 | "lower-case": "^1.1.1"
755 | }
756 | },
757 | "node-fetch": {
758 | "version": "1.7.3",
759 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz",
760 | "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==",
761 | "requires": {
762 | "encoding": "^0.1.11",
763 | "is-stream": "^1.0.1"
764 | }
765 | },
766 | "normalize-scroll-left": {
767 | "version": "0.1.2",
768 | "resolved": "https://registry.npmjs.org/normalize-scroll-left/-/normalize-scroll-left-0.1.2.tgz",
769 | "integrity": "sha512-F9YMRls0zCF6BFIE2YnXDRpHPpfd91nOIaNdDgrx5YMoPLo8Wqj+6jNXHQsYBavJeXP4ww8HCt0xQAKc5qk2Fg=="
770 | },
771 | "nth-check": {
772 | "version": "1.0.1",
773 | "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.1.tgz",
774 | "integrity": "sha1-mSms32KPwsQQmN6rgqxYDPFJquQ=",
775 | "dev": true,
776 | "requires": {
777 | "boolbase": "~1.0.0"
778 | }
779 | },
780 | "object-assign": {
781 | "version": "4.1.1",
782 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
783 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
784 | },
785 | "object-keys": {
786 | "version": "1.0.12",
787 | "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz",
788 | "integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag==",
789 | "dev": true
790 | },
791 | "object.getownpropertydescriptors": {
792 | "version": "2.0.3",
793 | "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz",
794 | "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=",
795 | "dev": true,
796 | "requires": {
797 | "define-properties": "^1.1.2",
798 | "es-abstract": "^1.5.1"
799 | }
800 | },
801 | "param-case": {
802 | "version": "2.1.1",
803 | "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz",
804 | "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=",
805 | "dev": true,
806 | "requires": {
807 | "no-case": "^2.2.0"
808 | }
809 | },
810 | "popper.js": {
811 | "version": "1.14.6",
812 | "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.14.6.tgz",
813 | "integrity": "sha512-AGwHGQBKumlk/MDfrSOf0JHhJCImdDMcGNoqKmKkU+68GFazv3CQ6q9r7Ja1sKDZmYWTckY/uLyEznheTDycnA=="
814 | },
815 | "prettier": {
816 | "version": "1.15.2",
817 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.15.2.tgz",
818 | "integrity": "sha512-YgPLFFA0CdKL4Eg2IHtUSjzj/BWgszDHiNQAe0VAIBse34148whfdzLagRL+QiKS+YfK5ftB6X4v/MBw8yCoug==",
819 | "dev": true
820 | },
821 | "pretty-error": {
822 | "version": "2.1.1",
823 | "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.1.tgz",
824 | "integrity": "sha1-X0+HyPkeWuPzuoerTPXgOxoX8aM=",
825 | "dev": true,
826 | "requires": {
827 | "renderkid": "^2.0.1",
828 | "utila": "~0.4"
829 | }
830 | },
831 | "promise": {
832 | "version": "7.3.1",
833 | "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
834 | "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
835 | "requires": {
836 | "asap": "~2.0.3"
837 | }
838 | },
839 | "prop-types": {
840 | "version": "15.6.2",
841 | "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz",
842 | "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==",
843 | "requires": {
844 | "loose-envify": "^1.3.1",
845 | "object-assign": "^4.1.1"
846 | }
847 | },
848 | "react": {
849 | "version": "16.7.0-alpha.2",
850 | "resolved": "https://registry.npmjs.org/react/-/react-16.7.0-alpha.2.tgz",
851 | "integrity": "sha512-Xh1CC8KkqIojhC+LFXd21jxlVtzoVYdGnQAi/I2+dxbmos9ghbx5TQf9/nDxc4WxaFfUQJkya0w1k6rMeyIaxQ==",
852 | "requires": {
853 | "loose-envify": "^1.1.0",
854 | "object-assign": "^4.1.1",
855 | "prop-types": "^15.6.2",
856 | "scheduler": "^0.12.0-alpha.2"
857 | }
858 | },
859 | "react-dom": {
860 | "version": "16.7.0-alpha.2",
861 | "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.7.0-alpha.2.tgz",
862 | "integrity": "sha512-o0mMw8jBlwHjGZEy/vvKd/6giAX0+skREMOTs3/QHmgi+yAhUClp4My4Z9lsKy3SXV+03uPdm1l/QM7NTcGuMw==",
863 | "requires": {
864 | "loose-envify": "^1.1.0",
865 | "object-assign": "^4.1.1",
866 | "prop-types": "^15.6.2",
867 | "scheduler": "^0.12.0-alpha.2"
868 | }
869 | },
870 | "react-event-listener": {
871 | "version": "0.6.4",
872 | "resolved": "https://registry.npmjs.org/react-event-listener/-/react-event-listener-0.6.4.tgz",
873 | "integrity": "sha512-t7VSjIuUFmN+GeyKb+wm025YLeojVB85kJL6sSs0wEBJddfmKBEQz+CNBZ2zBLKVWkPy/fZXM6U5yvojjYBVYQ==",
874 | "requires": {
875 | "@babel/runtime": "7.0.0",
876 | "prop-types": "^15.6.0",
877 | "warning": "^4.0.1"
878 | },
879 | "dependencies": {
880 | "@babel/runtime": {
881 | "version": "7.0.0",
882 | "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.0.0.tgz",
883 | "integrity": "sha512-7hGhzlcmg01CvH1EHdSPVXYX1aJ8KCEyz6I9xYIi/asDtzBPMyMhVibhM/K6g/5qnKBwjZtp10bNZIEFTRW1MA==",
884 | "requires": {
885 | "regenerator-runtime": "^0.12.0"
886 | }
887 | }
888 | }
889 | },
890 | "react-is": {
891 | "version": "16.6.3",
892 | "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.6.3.tgz",
893 | "integrity": "sha512-u7FDWtthB4rWibG/+mFbVd5FvdI20yde86qKGx4lVUTWmPlSWQ4QxbBIrrs+HnXGbxOUlUzTAP/VDmvCwaP2yA=="
894 | },
895 | "react-lifecycles-compat": {
896 | "version": "3.0.4",
897 | "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
898 | "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
899 | },
900 | "react-transition-group": {
901 | "version": "2.5.0",
902 | "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.5.0.tgz",
903 | "integrity": "sha512-qYB3JBF+9Y4sE4/Mg/9O6WFpdoYjeeYqx0AFb64PTazVy8RPMiE3A47CG9QmM4WJ/mzDiZYslV+Uly6O1Erlgw==",
904 | "requires": {
905 | "dom-helpers": "^3.3.1",
906 | "loose-envify": "^1.4.0",
907 | "prop-types": "^15.6.2",
908 | "react-lifecycles-compat": "^3.0.4"
909 | }
910 | },
911 | "recompose": {
912 | "version": "0.30.0",
913 | "resolved": "https://registry.npmjs.org/recompose/-/recompose-0.30.0.tgz",
914 | "integrity": "sha512-ZTrzzUDa9AqUIhRk4KmVFihH0rapdCSMFXjhHbNrjAWxBuUD/guYlyysMnuHjlZC/KRiOKRtB4jf96yYSkKE8w==",
915 | "requires": {
916 | "@babel/runtime": "^7.0.0",
917 | "change-emitter": "^0.1.2",
918 | "fbjs": "^0.8.1",
919 | "hoist-non-react-statics": "^2.3.1",
920 | "react-lifecycles-compat": "^3.0.2",
921 | "symbol-observable": "^1.0.4"
922 | }
923 | },
924 | "regenerator-runtime": {
925 | "version": "0.12.1",
926 | "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz",
927 | "integrity": "sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg=="
928 | },
929 | "relateurl": {
930 | "version": "0.2.7",
931 | "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
932 | "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=",
933 | "dev": true
934 | },
935 | "renderkid": {
936 | "version": "2.0.1",
937 | "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.1.tgz",
938 | "integrity": "sha1-iYyr/Ivt5Le5ETWj/9Mj5YwNsxk=",
939 | "dev": true,
940 | "requires": {
941 | "css-select": "^1.1.0",
942 | "dom-converter": "~0.1",
943 | "htmlparser2": "~3.3.0",
944 | "strip-ansi": "^3.0.0",
945 | "utila": "~0.3"
946 | },
947 | "dependencies": {
948 | "utila": {
949 | "version": "0.3.3",
950 | "resolved": "https://registry.npmjs.org/utila/-/utila-0.3.3.tgz",
951 | "integrity": "sha1-1+jn1+MJEHCSsF+NloiCTWM6QiY=",
952 | "dev": true
953 | }
954 | }
955 | },
956 | "safer-buffer": {
957 | "version": "2.1.2",
958 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
959 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
960 | },
961 | "scheduler": {
962 | "version": "0.12.0-alpha.2",
963 | "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.12.0-alpha.2.tgz",
964 | "integrity": "sha512-bfqFzGH18MjjhePIzYQNR0uGQ1wMCX6Q83c2s+3fzyuqKT6zBI2wNQTpq01q72C7QItAp8if5w2LfMiXnI2SYw==",
965 | "requires": {
966 | "loose-envify": "^1.1.0",
967 | "object-assign": "^4.1.1"
968 | }
969 | },
970 | "setimmediate": {
971 | "version": "1.0.5",
972 | "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
973 | "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU="
974 | },
975 | "strip-ansi": {
976 | "version": "3.0.1",
977 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
978 | "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
979 | "dev": true,
980 | "requires": {
981 | "ansi-regex": "^2.0.0"
982 | }
983 | },
984 | "symbol-observable": {
985 | "version": "1.2.0",
986 | "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz",
987 | "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ=="
988 | },
989 | "tapable": {
990 | "version": "1.0.0",
991 | "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.0.0.tgz",
992 | "integrity": "sha512-dQRhbNQkRnaqauC7WqSJ21EEksgT0fYZX2lqXzGkpo8JNig9zGZTYoMGvyI2nWmXlE2VSVXVDu7wLVGu/mQEsg==",
993 | "dev": true
994 | },
995 | "toposort": {
996 | "version": "1.0.7",
997 | "resolved": "https://registry.npmjs.org/toposort/-/toposort-1.0.7.tgz",
998 | "integrity": "sha1-LmhELZ9k7HILjMieZEOsbKqVACk=",
999 | "dev": true
1000 | },
1001 | "ua-parser-js": {
1002 | "version": "0.7.18",
1003 | "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.18.tgz",
1004 | "integrity": "sha512-LtzwHlVHwFGTptfNSgezHp7WUlwiqb0gA9AALRbKaERfxwJoiX0A73QbTToxteIAuIaFshhgIZfqK8s7clqgnA=="
1005 | },
1006 | "upper-case": {
1007 | "version": "1.1.3",
1008 | "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz",
1009 | "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=",
1010 | "dev": true
1011 | },
1012 | "util.promisify": {
1013 | "version": "1.0.0",
1014 | "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz",
1015 | "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==",
1016 | "dev": true,
1017 | "requires": {
1018 | "define-properties": "^1.1.2",
1019 | "object.getownpropertydescriptors": "^2.0.3"
1020 | }
1021 | },
1022 | "utila": {
1023 | "version": "0.4.0",
1024 | "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz",
1025 | "integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=",
1026 | "dev": true
1027 | },
1028 | "warning": {
1029 | "version": "4.0.2",
1030 | "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.2.tgz",
1031 | "integrity": "sha512-wbTp09q/9C+jJn4KKJfJfoS6VleK/Dti0yqWSm6KMvJ4MRCXFQNapHuJXutJIrWV0Cf4AhTdeIe4qdKHR1+Hug==",
1032 | "requires": {
1033 | "loose-envify": "^1.0.0"
1034 | }
1035 | },
1036 | "whatwg-fetch": {
1037 | "version": "3.0.0",
1038 | "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz",
1039 | "integrity": "sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q=="
1040 | }
1041 | }
1042 | }
1043 |
--------------------------------------------------------------------------------
/packages/boilerplate-client/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "client",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "webpack-dev-server --progress --inline",
8 | "pack": "NODE_ENV=production webpack",
9 | "packWindows": "set NODE_ENV=production && webpack"
10 | },
11 | "author": "Jeff Hull",
12 | "license": "MIT",
13 | "dependencies": {},
14 | "devDependencies": {}
15 | }
16 |
--------------------------------------------------------------------------------
/packages/boilerplate-client/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cyphercider/typescript-fullstack-monorepo/8875cf48b95e392bfa85f6af9125cfe4dfe8b273/packages/boilerplate-client/public/favicon.ico
--------------------------------------------------------------------------------
/packages/boilerplate-client/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Boilerplate Client
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/packages/boilerplate-client/src/components/App.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import { runInAction } from 'mobx'
3 | import {
4 | Home,
5 | HooksDemo,
6 | ComponentsDemo,
7 | ServerApiDemo
8 | } from '@client/pages/index'
9 | import { AppContainer } from '@appkit-client/index'
10 | import { AppkitMenuItem } from '@appkit-client/index'
11 | import { AppPage } from '@client/stores/ViewStore'
12 | import { Paper } from '@material-ui/core'
13 | import { config } from '../config/config'
14 | import { ConditionalModals } from './app/ConditionalModals'
15 | import jss from 'jss'
16 | import jssPreset from 'jss-preset-default'
17 | import { useComputed, observer } from 'mobx-react-lite'
18 | import { StoreContext } from '@client/index'
19 |
20 | const styles = {
21 | '@global': {
22 | h1: config.markdownTheme.h1,
23 | h2: config.markdownTheme.h2,
24 | h3: config.markdownTheme.h3,
25 | p: config.markdownTheme.p,
26 | li: config.markdownTheme.li
27 | }
28 | }
29 |
30 | jss.setup(jssPreset())
31 | jss.createStyleSheet(styles as any).attach()
32 |
33 | export const App = observer(() => {
34 | const store = React.useContext(StoreContext)
35 | const currentPage: JSX.Element = useComputed(() => {
36 | switch (store.viewStore.currentPage) {
37 | case AppPage.Home:
38 | return
39 | case AppPage.ComponentsDemos:
40 | return
41 | case AppPage.ServerApiDemo:
42 | return
43 | case AppPage.HooksDemo:
44 | return
45 | default:
46 | throw new Error(`Unknown page ${store.viewStore.currentPage}`)
47 | }
48 | })
49 |
50 | const menuItems: AppkitMenuItem[] = useComputed(() => {
51 | return [
52 | {
53 | name: 'Home',
54 | onClick: () => {
55 | store.viewStore.setPage(AppPage.Home)
56 | }
57 | },
58 | {
59 | name: 'Components Demo',
60 | onClick: () => {
61 | store.viewStore.setPage(AppPage.ComponentsDemos)
62 | }
63 | },
64 | {
65 | name: 'Server API Demo',
66 | onClick: () => {
67 | store.viewStore.setPage(AppPage.ServerApiDemo)
68 | }
69 | },
70 | {
71 | name: 'Hooks Demo',
72 | onClick: () => {
73 | store.viewStore.setPage(AppPage.HooksDemo)
74 | }
75 | }
76 | ]
77 | })
78 |
79 | return (
80 | {
85 | runInAction(() => {
86 | store.userStore.loginModalOpen = true
87 | })
88 | }}
89 | logoutAction={() => {
90 | store.userStore.logout()
91 | }}
92 | >
93 |
94 |
106 | {currentPage}
107 |
108 |
109 | )
110 | })
111 |
--------------------------------------------------------------------------------
/packages/boilerplate-client/src/components/app/ConditionalModals.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import { runInAction } from 'mobx'
3 | import { InformationModal } from '@appkit-client/components/InformationModal'
4 | import { SingleTextInputModal } from '@appkit-client/components/SingleTextInputModal'
5 | import { LoginModal } from '@appkit-client/index'
6 | import * as authModel from '@appkit-common/model/auth'
7 | import { StoreContext } from '@client/index'
8 | import { observer } from 'mobx-react-lite'
9 |
10 | export const ConditionalModals = observer(() => {
11 | const store = React.useContext(StoreContext)
12 | return (
13 |
14 | {[
15 | store.userStore.loginModalOpen && (
16 | {
19 | runInAction(() => {
20 | store.userStore.loginModalOpen = false
21 | })
22 | }}
23 | callbackWithCredentials={(cred: authModel.Credentials) => {
24 | runInAction(() => {
25 | store.userStore.login({ credentials: cred })
26 | })
27 | }}
28 | />
29 | ),
30 | store.notificationStore.infoModalOpen && (
31 |
37 | ),
38 | store.inputStore.singleTextInputOpen && (
39 | {
44 | store.inputStore.singleTextValueReturnCallback(val)
45 | store.inputStore.closeSingleTextInputModal()
46 | }}
47 | inputLabel="input label"
48 | />
49 | )
50 | ]}
51 |
52 | )
53 | })
54 |
--------------------------------------------------------------------------------
/packages/boilerplate-client/src/config/config.ts:
--------------------------------------------------------------------------------
1 | import { ClientBaseConfig } from '@appkit-client/model/config'
2 | import { User } from '@appkit-common/model/auth'
3 | import { blueMarkdownTheme, blueMuiTheme } from '@appkit-client/index'
4 |
5 | declare var process
6 |
7 | export const config: ClientBaseConfig = {
8 | appName: 'Boilerplate Client',
9 | host: window.location.origin,
10 | instanceName: 'Development',
11 | clientVersion: '1.0.0',
12 | serverVersion: null,
13 | user: null,
14 | inDevelopment: process.env.NODE_ENV === 'development',
15 | muiTheme: blueMuiTheme,
16 | markdownTheme: blueMarkdownTheme
17 | }
18 |
19 | if (config.inDevelopment === true) {
20 | config.host = 'http://localhost:3000'
21 | }
22 |
23 | console.log(`config object: ${JSON.stringify(config)}`)
24 | console.log(`process object: ${JSON.stringify(process)}`)
25 |
26 | export function setServerVersion(version: string) {
27 | config.serverVersion = version
28 | }
29 |
30 | export function setUser(user: User) {
31 | config.user = user
32 | }
33 |
--------------------------------------------------------------------------------
/packages/boilerplate-client/src/index.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import * as ReactDOM from 'react-dom'
3 | import { ErrorBoundary } from '@appkit-client/index'
4 | import RootStore from '@client/stores/RootStore'
5 | import { configure } from 'mobx'
6 | import { App } from './components/App'
7 |
8 | configure({ enforceActions: 'observed' })
9 |
10 | const store = new RootStore()
11 |
12 | console.log(`created root store ${typeof store}`)
13 | export const StoreContext: React.Context = React.createContext(store)
14 | console.log(`created store context ${typeof StoreContext}`)
15 |
16 | async function renderView() {
17 | await store.initAllStores()
18 | ReactDOM.render(
19 |
20 |
21 | ,
22 | document.getElementById('root')
23 | )
24 | }
25 |
26 | renderView()
27 |
--------------------------------------------------------------------------------
/packages/boilerplate-client/src/model/index.ts:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cyphercider/typescript-fullstack-monorepo/8875cf48b95e392bfa85f6af9125cfe4dfe8b273/packages/boilerplate-client/src/model/index.ts
--------------------------------------------------------------------------------
/packages/boilerplate-client/src/model/serverApi.ts:
--------------------------------------------------------------------------------
1 | import * as fetchUtil from '@client/util/fetch'
2 | import * as authModel from '@appkit-common/model/auth'
3 |
4 | const endpoints = {
5 | getHello: '/test/hello',
6 | login: '/auth/login'
7 | }
8 |
9 | export async function login(
10 | req: authModel.LoginRequest
11 | ): Promise {
12 | return await fetchUtil.postToServerNow(endpoints.login, req)
13 | }
14 |
15 | export async function getHello(): Promise {
16 | return await fetchUtil.getFromServerNow(endpoints.getHello)
17 | }
18 |
--------------------------------------------------------------------------------
/packages/boilerplate-client/src/pages/ComponentsDemo.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import { Typography } from '@material-ui/core'
3 | import { ButtonSimple } from '@appkit-client/index'
4 | import { StoreContext } from '@client/index'
5 |
6 | export const ComponentsDemo = () => {
7 | const store = React.useContext(StoreContext)
8 | return (
9 |
10 | Simple Components
11 |
12 | A showcase of simple wrapper components, which exposure minimal API
13 | surface area for just the component features you need.
14 |
15 | {
19 | store.inputStore.openSingleTextInputModal(
20 | `test input`,
21 | (val: string) => {
22 | console.log(`got value ${val} from input`)
23 | }
24 | )
25 | }}
26 | />
27 |
28 | )
29 | }
30 |
--------------------------------------------------------------------------------
/packages/boilerplate-client/src/pages/Home.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import { Divider } from '@material-ui/core'
3 | const markdownContent = require('./home/home_content.md')
4 |
5 | export const Home = () => {
6 | return (
7 |
11 | )
12 | }
13 |
--------------------------------------------------------------------------------
/packages/boilerplate-client/src/pages/HooksDemo.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import { Typography } from '@material-ui/core'
3 | import { HooksTest } from './hooksDemo/HooksTest'
4 |
5 | export const HooksDemo = () => {
6 | return (
7 |
8 | Hooks Demo
9 |
10 | A simple example of usage of React 16.7.alpha hooks
11 |
12 | Hooks test
13 |
14 |
15 | )
16 | }
17 |
--------------------------------------------------------------------------------
/packages/boilerplate-client/src/pages/ServerApiDemo.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import { Typography } from '@material-ui/core'
3 | import { ButtonSimple } from '@appkit-client/index'
4 | import * as serverApi from '@client/model/serverApi'
5 | import { StoreContext } from '@client/index'
6 | import { observer } from 'mobx-react-lite'
7 |
8 | export const ServerApiDemo = observer(() => {
9 | const store = React.useContext(StoreContext)
10 | return (
11 |
12 | Server Api Demo
13 |
14 | A demo of some basic server API calls
15 |
16 | {
20 | const res = await serverApi.getHello()
21 | store.notificationStore.displayInfoModal(
22 | 'Server Response',
23 | JSON.stringify(res)
24 | )
25 | console.log(`Got response from server ${JSON.stringify(res)}`)
26 | }}
27 | />
28 |
29 | )
30 | })
31 |
--------------------------------------------------------------------------------
/packages/boilerplate-client/src/pages/home/home_content.md:
--------------------------------------------------------------------------------
1 | # Typescript Fullstack Boilerplate
2 |
3 | A starter fullstack typescript application with the following features
4 |
5 | ## Frontend
6 |
7 | 1. UI components with React
8 | 2. State management with Mobx
9 | 3. Bundling with webpack
10 | 1. Markdown content display with [markdown-loader](https://www.npmjs.com/package/markdown-loader)
11 | 2. Absolute path mapping, using mappings from tsconfig.json, with [tsconfig-paths-webpack-plugin](https://www.npmjs.com/package/tsconfig-paths-webpack-plugin)
12 |
13 | ## Backend
14 |
15 | 1. Node.js
16 | 2. Bundling with webpack
17 | 1. Absolute path mapping, using mappings from tsconfig.json, with [tsconfig-paths-webpack-plugin](https://www.npmjs.com/package/tsconfig-paths-webpack-plugin)
18 |
19 | ## Repository-wide
20 |
21 | 1. [Monorepository](https://en.wikipedia.org/wiki/Monorepo)
22 | 1. Multiple applications (in this case, node server and browser client) sharing some common dependencies
23 | 1. Note: applications depend directly on .ts source - not on a package as when using lerna
24 | 2. [tsconfig.json](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html) inheritance using "extends"
25 | 2. Vscode [multi-root workspaces](https://code.visualstudio.com/docs/editor/multi-root-workspaces) for client and server
26 | 3. A dockerfile for containerizing the fullstack app for deployment
27 |
--------------------------------------------------------------------------------
/packages/boilerplate-client/src/pages/hooksDemo/HooksTest.tsx:
--------------------------------------------------------------------------------
1 | import { useState, useEffect, useRef, useMemo } from 'react'
2 | import * as React from 'react'
3 | // import { useComputedValue } from './useComputedValue'
4 |
5 | // set timeout not working - see https://github.com/facebook/react/issues/14010
6 |
7 | export function HooksTest() {
8 | // Declare a new state variable, which we'll call "count"
9 | const [clickCount, setClickCount] = useState(0)
10 | const [timer, setTimer] = useState(0)
11 |
12 | const countRef = useRef(timer)
13 | countRef.current = timer
14 |
15 | const startTimer = () => {
16 | return setInterval(() => {
17 | setTimer(countRef.current + 1)
18 | }, 3000)
19 | }
20 |
21 | useEffect(() => {
22 | const intervalId = startTimer()
23 | return () => {
24 | clearInterval(intervalId)
25 | }
26 | }, [])
27 |
28 | const computedValue = useMemo(
29 | () => {
30 | return timer * 2
31 | },
32 | [timer]
33 | )
34 |
35 | const computedValueFromClick = useMemo(
36 | () => {
37 | return clickCount * 2
38 | },
39 | [clickCount]
40 | )
41 |
42 | return (
43 |
44 |
You clicked {clickCount} times
45 |
setClickCount(clickCount + 1)}>Click me
46 |
Your timer is at {timer}
47 |
Your timer computed value is {computedValue}
48 |
Your click computed value is {computedValueFromClick}
49 |
50 | )
51 | }
52 |
--------------------------------------------------------------------------------
/packages/boilerplate-client/src/pages/hooksDemo/useComputedValue.ts:
--------------------------------------------------------------------------------
1 | // https://reactjs.org/docs/hooks-faq.html
2 | import { useMemo } from 'react'
3 |
4 | export function useComputedValue(fn: () => any, watchedValue: any[]) {
5 | return useMemo(fn, watchedValue)
6 | }
7 |
8 | // export const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b])
9 |
--------------------------------------------------------------------------------
/packages/boilerplate-client/src/pages/index.ts:
--------------------------------------------------------------------------------
1 | export * from './Home'
2 | export * from './HooksDemo'
3 | export * from './ComponentsDemo'
4 | export * from './ServerApiDemo'
5 |
--------------------------------------------------------------------------------
/packages/boilerplate-client/src/stores/RootStore.ts:
--------------------------------------------------------------------------------
1 | import { observable } from 'mobx'
2 | import ViewStore from '@client/stores/ViewStore'
3 | import NotificationStore from '@appkit-client/stores/NotificationStore'
4 | import UserStore from './UserStore'
5 | import InputStore from '@appkit-client/stores/InputStore'
6 |
7 | export default class RootStore {
8 | static rootStore: RootStore
9 | @observable serverVersion: string = 'not updated'
10 | @observable clientVersion: string = 'not updated'
11 | viewStore: ViewStore
12 | notificationStore: NotificationStore
13 | inputStore: InputStore
14 | userStore: UserStore
15 |
16 | constructor() {
17 | RootStore.rootStore = this
18 | this.viewStore = new ViewStore(this)
19 | this.notificationStore = new NotificationStore(this)
20 | this.inputStore = new InputStore(this)
21 | this.userStore = new UserStore(this)
22 | // init all stores
23 | // this.initAllStores(callbackOnInit)
24 | }
25 |
26 | initAllStores = async () => {
27 | console.log(`init all stores`)
28 | // yield this.setVersionInfo(() => {})
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/packages/boilerplate-client/src/stores/UserStore.ts:
--------------------------------------------------------------------------------
1 | import RootStore from './RootStore'
2 | import { observable, runInAction } from 'mobx'
3 | import { LoginRequest, User } from '@appkit-common/model/auth'
4 | import { login } from '@client/model/serverApi'
5 |
6 | export default class UserStore {
7 | rootStore: RootStore
8 | @observable loggedIn: boolean = false
9 | @observable user: User
10 | @observable loginModalOpen: boolean = false
11 |
12 | logout = async () => {
13 | console.log(`do logout`)
14 | runInAction(() => {
15 | this.loggedIn = false
16 | })
17 | }
18 |
19 | login = async (req: LoginRequest) => {
20 | const res = await login(req)
21 | console.log(`got res ${JSON.stringify(res)}`)
22 | runInAction(() => {
23 | this.user = res.user
24 | this.loggedIn = true
25 | })
26 | }
27 |
28 | constructor(rootStore: RootStore) {
29 | this.rootStore = rootStore
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/packages/boilerplate-client/src/stores/ViewStore.ts:
--------------------------------------------------------------------------------
1 | import { observable, action, computed, autorun } from 'mobx'
2 | import RootStore from '@client/stores/RootStore'
3 | import { Router } from 'director/build/director'
4 |
5 | export enum AppPage {
6 | Home = 'Home',
7 | HooksDemo = 'HooksDemo',
8 | ComponentsDemos = 'ComponentsDemos',
9 | ServerApiDemo = 'ServerApiDemo'
10 | }
11 |
12 | export default class ViewStore {
13 | rootStore: RootStore
14 | @observable serverVersion: string = 'not updated'
15 | @observable clientVersion: string = 'not updated'
16 | @observable currentPage: AppPage = AppPage.Home
17 |
18 | constructor(rootStore: RootStore) {
19 | this.rootStore = rootStore
20 | this.startRouter()
21 | }
22 |
23 | @action.bound
24 | setPage(page: AppPage) {
25 | if (page !== this.currentPage) {
26 | this.currentPage = page
27 | }
28 | }
29 |
30 | @computed
31 | get currentRoute() {
32 | switch (this.currentPage) {
33 | case AppPage.Home:
34 | return '/#/home'
35 | case AppPage.HooksDemo:
36 | return '/#/hooks'
37 | case AppPage.ServerApiDemo:
38 | return '/#/serverApi'
39 | case AppPage.ComponentsDemos:
40 | return '/#/components'
41 | default:
42 | throw new Error(`page not recognized ${this.currentPage}`)
43 | }
44 | }
45 |
46 | startRouter = () => {
47 | const router = new Router({
48 | '/': () => this.setPage(AppPage.Home),
49 | '/home': () => this.setPage(AppPage.Home),
50 | '/hooks': () => this.setPage(AppPage.HooksDemo),
51 | '/serverApi': () => this.setPage(AppPage.ServerApiDemo),
52 | '/components': () => this.setPage(AppPage.ComponentsDemos)
53 | })
54 | router.configure({
55 | notfound: () => this.setPage(AppPage.Home),
56 | html5history: true
57 | })
58 | router.init()
59 | autorun(() => {
60 | const path = this.currentRoute
61 | if (path !== window.location.pathname)
62 | window.history.pushState(null, '', path)
63 | })
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/packages/boilerplate-client/src/util/fetch.ts:
--------------------------------------------------------------------------------
1 | import * as fetchUtil from '@appkit-client/util/fetch'
2 | import { config } from '@client/config/config'
3 |
4 | export async function postToServerNow(
5 | endpoint: string,
6 | body: Object
7 | ): Promise {
8 | return await fetchUtil.fetchBodyPost(config.host + endpoint, body)
9 | }
10 |
11 | export async function getFromServerNow(endpoint: string): Promise {
12 | return await fetchUtil.fetchBodyGet(config.host + endpoint)
13 | }
14 |
--------------------------------------------------------------------------------
/packages/boilerplate-client/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base",
3 | "compilerOptions": {
4 | "outDir": "dist",
5 | "jsx": "react",
6 | "baseUrl": "..",
7 | "lib": ["es2018", "dom"]
8 | },
9 | "include": ["./src/**/*"]
10 | }
11 |
--------------------------------------------------------------------------------
/packages/boilerplate-client/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require("path")
2 | const webpack = require("webpack")
3 | const HtmlWebpackPlugin = require("html-webpack-plugin")
4 | const TsconfigPathsPlugin = require("tsconfig-paths-webpack-plugin")
5 | // const marked = require('marked')
6 | // const markdownRenderer = new marked.Renderer()
7 |
8 | let devMode = false
9 | if (
10 | process.env.NODE_ENV == null ||
11 | process.env.NODE_ENV.substr(0, 3).toLowerCase() === "dev"
12 | ) {
13 | devMode = true
14 | console.log(`Webpack is running in development mode`)
15 | }
16 |
17 | module.exports = {
18 | target: "web",
19 | mode: "development",
20 | entry: ["./src/index.tsx"],
21 | devtool: "source-map",
22 | resolve: {
23 | extensions: [".ts", ".tsx", ".js", ".json"],
24 | plugins: [
25 | new TsconfigPathsPlugin({
26 | configFile: "./tsconfig.json",
27 | logLevel: "info",
28 | logInfoToStdOut: true,
29 | extensions: [".ts", ".tsx"]
30 | })
31 | ]
32 | },
33 | module: {
34 | rules: [
35 | { test: /\.tsx?$/, use: "ts-loader", exclude: /node_modules/ },
36 | {
37 | test: /\.md$/,
38 | use: [{ loader: "html-loader" }, { loader: "markdown-loader" }]
39 | },
40 | {
41 | test: /\.css$/,
42 | exclude: /\.useable\.css$/,
43 | loader: "style-loader!raw-loader"
44 | },
45 | { test: /\.useable\.css$/, loader: "style-loader/useable!raw-loader" },
46 | {
47 | test: /\.(png|ico|gif)?$/,
48 | loaders: ["file"],
49 | include: __dirname
50 | }
51 | ]
52 | },
53 | plugins: [
54 | new webpack.DefinePlugin({
55 | "process.env.NODE_ENV": devMode ? '"development"' : '"production"'
56 | }),
57 | new HtmlWebpackPlugin({
58 | template: "./public/index.html",
59 | favicon: "./public/favicon.ico",
60 | filename: "index.html"
61 | }),
62 | ...(devMode ? [new webpack.HotModuleReplacementPlugin()] : [])
63 | ],
64 | devServer: {
65 | host: "0.0.0.0",
66 | disableHostCheck: true,
67 | contentBase: path.join(__dirname, "build"),
68 | port: 8000,
69 | compress: true
70 | },
71 | output: {
72 | path: path.join(__dirname, "dist"),
73 | filename: "bundle.js",
74 | publicPath: "/"
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/packages/boilerplate-server/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "type": "node",
9 | "request": "launch",
10 | "name": "Debug Boilerplate Server",
11 | "program": "${workspaceFolder}\\src\\server.ts",
12 | "env": {
13 | "NODE_ENV": "DEV"
14 | },
15 | "smartStep": true,
16 | "restart": true,
17 | "console": "integratedTerminal",
18 | "internalConsoleOptions": "neverOpen",
19 | "outFiles": ["${workspaceFolder}/dist/**/*.js"],
20 | "sourceMaps": true
21 | }
22 | ]
23 | }
24 |
--------------------------------------------------------------------------------
/packages/boilerplate-server/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "prettier.semi": false,
3 | "prettier.singleQuote": true
4 | }
--------------------------------------------------------------------------------
/packages/boilerplate-server/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | // See https://go.microsoft.com/fwlink/?LinkId=733558
3 | // for the documentation about the tasks.json format
4 | "version": "2.0.0",
5 | "tasks": [
6 | {
7 | "label": "server watch",
8 | "type": "npm",
9 | "script": "watch",
10 | "problemMatcher": []
11 | },
12 | {
13 | "label": "server clean",
14 | "type": "npm",
15 | "script": "clean",
16 | "problemMatcher": []
17 | },
18 | {
19 | "label": "server start",
20 | "type": "npm",
21 | "script": "start",
22 | "problemMatcher": []
23 | },
24 | {
25 | "label": "server all",
26 | "dependsOn": ["server watch", "server start"],
27 | "problemMatcher": []
28 | }
29 | ]
30 | }
31 |
--------------------------------------------------------------------------------
/packages/boilerplate-server/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:lts-alpine
2 | WORKDIR /usr/src/app
3 | COPY dist ./dist
4 | COPY public ./public
5 | COPY package.json .
6 | RUN npm install --only=production
7 |
8 | EXPOSE 3000
9 |
10 | CMD [ "npm", "run", "startServer" ]
--------------------------------------------------------------------------------
/packages/boilerplate-server/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "server",
3 | "version": "1.0.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "@types/body-parser": {
8 | "version": "1.17.0",
9 | "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.17.0.tgz",
10 | "integrity": "sha512-a2+YeUjPkztKJu5aIF2yArYFQQp8d51wZ7DavSHjFuY1mqVgidGyzEQ41JIVNy82fXj8yPgy2vJmfIywgESW6w==",
11 | "requires": {
12 | "@types/connect": "*",
13 | "@types/node": "*"
14 | }
15 | },
16 | "@types/connect": {
17 | "version": "3.4.32",
18 | "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.32.tgz",
19 | "integrity": "sha512-4r8qa0quOvh7lGD0pre62CAb1oni1OO6ecJLGCezTmhQ8Fz50Arx9RUszryR8KlgK6avuSXvviL6yWyViQABOg==",
20 | "requires": {
21 | "@types/node": "*"
22 | }
23 | },
24 | "@types/express": {
25 | "version": "4.16.1",
26 | "resolved": "https://registry.npmjs.org/@types/express/-/express-4.16.1.tgz",
27 | "integrity": "sha512-V0clmJow23WeyblmACoxbHBu2JKlE5TiIme6Lem14FnPW9gsttyHtk6wq7njcdIWH1njAaFgR8gW09lgY98gQg==",
28 | "requires": {
29 | "@types/body-parser": "*",
30 | "@types/express-serve-static-core": "*",
31 | "@types/serve-static": "*"
32 | }
33 | },
34 | "@types/express-serve-static-core": {
35 | "version": "4.16.1",
36 | "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.16.1.tgz",
37 | "integrity": "sha512-QgbIMRU1EVRry5cIu1ORCQP4flSYqLM1lS5LYyGWfKnFT3E58f0gKto7BR13clBFVrVZ0G0rbLZ1hUpSkgQQOA==",
38 | "requires": {
39 | "@types/node": "*",
40 | "@types/range-parser": "*"
41 | }
42 | },
43 | "@types/mime": {
44 | "version": "2.0.1",
45 | "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.1.tgz",
46 | "integrity": "sha512-FwI9gX75FgVBJ7ywgnq/P7tw+/o1GUbtP0KzbtusLigAOgIgNISRK0ZPl4qertvXSIE8YbsVJueQ90cDt9YYyw=="
47 | },
48 | "@types/node": {
49 | "version": "10.14.5",
50 | "resolved": "https://registry.npmjs.org/@types/node/-/node-10.14.5.tgz",
51 | "integrity": "sha512-Ja7d4s0qyGFxjGeDq5S7Si25OFibSAHUi6i17UWnwNnpitADN7hah9q0Tl25gxuV5R1u2Bx+np6w4LHXfHyj/g=="
52 | },
53 | "@types/range-parser": {
54 | "version": "1.2.3",
55 | "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz",
56 | "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA=="
57 | },
58 | "@types/serve-static": {
59 | "version": "1.13.2",
60 | "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.2.tgz",
61 | "integrity": "sha512-/BZ4QRLpH/bNYgZgwhKEh+5AsboDBcUdlBYgzoLX0fpj3Y2gp6EApyOlM3bK53wQS/OE1SrdSYBAbux2D1528Q==",
62 | "requires": {
63 | "@types/express-serve-static-core": "*",
64 | "@types/mime": "*"
65 | }
66 | },
67 | "accepts": {
68 | "version": "1.3.5",
69 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz",
70 | "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=",
71 | "requires": {
72 | "mime-types": "~2.1.18",
73 | "negotiator": "0.6.1"
74 | }
75 | },
76 | "array-flatten": {
77 | "version": "1.1.1",
78 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
79 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
80 | },
81 | "async": {
82 | "version": "1.5.2",
83 | "resolved": "http://registry.npmjs.org/async/-/async-1.5.2.tgz",
84 | "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo="
85 | },
86 | "body-parser": {
87 | "version": "1.18.3",
88 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz",
89 | "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=",
90 | "requires": {
91 | "bytes": "3.0.0",
92 | "content-type": "~1.0.4",
93 | "debug": "2.6.9",
94 | "depd": "~1.1.2",
95 | "http-errors": "~1.6.3",
96 | "iconv-lite": "0.4.23",
97 | "on-finished": "~2.3.0",
98 | "qs": "6.5.2",
99 | "raw-body": "2.3.3",
100 | "type-is": "~1.6.16"
101 | }
102 | },
103 | "buffer-equal-constant-time": {
104 | "version": "1.0.1",
105 | "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
106 | "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk="
107 | },
108 | "buffer-from": {
109 | "version": "1.1.1",
110 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
111 | "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
112 | "dev": true
113 | },
114 | "bytes": {
115 | "version": "3.0.0",
116 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
117 | "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg="
118 | },
119 | "content-disposition": {
120 | "version": "0.5.2",
121 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
122 | "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ="
123 | },
124 | "content-type": {
125 | "version": "1.0.4",
126 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
127 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
128 | },
129 | "cookie": {
130 | "version": "0.3.1",
131 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
132 | "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
133 | },
134 | "cookie-signature": {
135 | "version": "1.0.6",
136 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
137 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
138 | },
139 | "cors": {
140 | "version": "2.8.5",
141 | "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
142 | "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
143 | "requires": {
144 | "object-assign": "^4",
145 | "vary": "^1"
146 | }
147 | },
148 | "debug": {
149 | "version": "2.6.9",
150 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
151 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
152 | "requires": {
153 | "ms": "2.0.0"
154 | }
155 | },
156 | "depd": {
157 | "version": "1.1.2",
158 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
159 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
160 | },
161 | "destroy": {
162 | "version": "1.0.4",
163 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
164 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
165 | },
166 | "ecdsa-sig-formatter": {
167 | "version": "1.0.11",
168 | "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
169 | "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
170 | "requires": {
171 | "safe-buffer": "^5.0.1"
172 | }
173 | },
174 | "ee-first": {
175 | "version": "1.1.1",
176 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
177 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
178 | },
179 | "encodeurl": {
180 | "version": "1.0.2",
181 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
182 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
183 | },
184 | "escape-html": {
185 | "version": "1.0.3",
186 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
187 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
188 | },
189 | "etag": {
190 | "version": "1.8.1",
191 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
192 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
193 | },
194 | "express": {
195 | "version": "4.16.4",
196 | "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz",
197 | "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==",
198 | "requires": {
199 | "accepts": "~1.3.5",
200 | "array-flatten": "1.1.1",
201 | "body-parser": "1.18.3",
202 | "content-disposition": "0.5.2",
203 | "content-type": "~1.0.4",
204 | "cookie": "0.3.1",
205 | "cookie-signature": "1.0.6",
206 | "debug": "2.6.9",
207 | "depd": "~1.1.2",
208 | "encodeurl": "~1.0.2",
209 | "escape-html": "~1.0.3",
210 | "etag": "~1.8.1",
211 | "finalhandler": "1.1.1",
212 | "fresh": "0.5.2",
213 | "merge-descriptors": "1.0.1",
214 | "methods": "~1.1.2",
215 | "on-finished": "~2.3.0",
216 | "parseurl": "~1.3.2",
217 | "path-to-regexp": "0.1.7",
218 | "proxy-addr": "~2.0.4",
219 | "qs": "6.5.2",
220 | "range-parser": "~1.2.0",
221 | "safe-buffer": "5.1.2",
222 | "send": "0.16.2",
223 | "serve-static": "1.13.2",
224 | "setprototypeof": "1.1.0",
225 | "statuses": "~1.4.0",
226 | "type-is": "~1.6.16",
227 | "utils-merge": "1.0.1",
228 | "vary": "~1.1.2"
229 | }
230 | },
231 | "express-jwt": {
232 | "version": "5.3.1",
233 | "resolved": "https://registry.npmjs.org/express-jwt/-/express-jwt-5.3.1.tgz",
234 | "integrity": "sha512-1C9RNq0wMp/JvsH/qZMlg3SIPvKu14YkZ4YYv7gJQ1Vq+Dv8LH9tLKenS5vMNth45gTlEUGx+ycp9IHIlaHP/g==",
235 | "requires": {
236 | "async": "^1.5.0",
237 | "express-unless": "^0.3.0",
238 | "jsonwebtoken": "^8.1.0",
239 | "lodash.set": "^4.0.0"
240 | }
241 | },
242 | "express-unless": {
243 | "version": "0.3.1",
244 | "resolved": "https://registry.npmjs.org/express-unless/-/express-unless-0.3.1.tgz",
245 | "integrity": "sha1-JVfBRudb65A+LSR/m1ugFFJpbiA="
246 | },
247 | "finalhandler": {
248 | "version": "1.1.1",
249 | "resolved": "http://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz",
250 | "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==",
251 | "requires": {
252 | "debug": "2.6.9",
253 | "encodeurl": "~1.0.2",
254 | "escape-html": "~1.0.3",
255 | "on-finished": "~2.3.0",
256 | "parseurl": "~1.3.2",
257 | "statuses": "~1.4.0",
258 | "unpipe": "~1.0.0"
259 | }
260 | },
261 | "forwarded": {
262 | "version": "0.1.2",
263 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
264 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
265 | },
266 | "fresh": {
267 | "version": "0.5.2",
268 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
269 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
270 | },
271 | "http-errors": {
272 | "version": "1.6.3",
273 | "resolved": "http://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
274 | "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
275 | "requires": {
276 | "depd": "~1.1.2",
277 | "inherits": "2.0.3",
278 | "setprototypeof": "1.1.0",
279 | "statuses": ">= 1.4.0 < 2"
280 | }
281 | },
282 | "iconv-lite": {
283 | "version": "0.4.23",
284 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
285 | "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==",
286 | "requires": {
287 | "safer-buffer": ">= 2.1.2 < 3"
288 | }
289 | },
290 | "inherits": {
291 | "version": "2.0.3",
292 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
293 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
294 | },
295 | "ipaddr.js": {
296 | "version": "1.8.0",
297 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz",
298 | "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4="
299 | },
300 | "jsonwebtoken": {
301 | "version": "8.5.1",
302 | "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz",
303 | "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==",
304 | "requires": {
305 | "jws": "^3.2.2",
306 | "lodash.includes": "^4.3.0",
307 | "lodash.isboolean": "^3.0.3",
308 | "lodash.isinteger": "^4.0.4",
309 | "lodash.isnumber": "^3.0.3",
310 | "lodash.isplainobject": "^4.0.6",
311 | "lodash.isstring": "^4.0.1",
312 | "lodash.once": "^4.0.0",
313 | "ms": "^2.1.1",
314 | "semver": "^5.6.0"
315 | },
316 | "dependencies": {
317 | "ms": {
318 | "version": "2.1.1",
319 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
320 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
321 | }
322 | }
323 | },
324 | "jwa": {
325 | "version": "1.4.1",
326 | "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
327 | "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
328 | "requires": {
329 | "buffer-equal-constant-time": "1.0.1",
330 | "ecdsa-sig-formatter": "1.0.11",
331 | "safe-buffer": "^5.0.1"
332 | }
333 | },
334 | "jws": {
335 | "version": "3.2.2",
336 | "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
337 | "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
338 | "requires": {
339 | "jwa": "^1.4.1",
340 | "safe-buffer": "^5.0.1"
341 | }
342 | },
343 | "lodash.includes": {
344 | "version": "4.3.0",
345 | "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
346 | "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8="
347 | },
348 | "lodash.isboolean": {
349 | "version": "3.0.3",
350 | "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
351 | "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY="
352 | },
353 | "lodash.isinteger": {
354 | "version": "4.0.4",
355 | "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
356 | "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M="
357 | },
358 | "lodash.isnumber": {
359 | "version": "3.0.3",
360 | "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
361 | "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w="
362 | },
363 | "lodash.isplainobject": {
364 | "version": "4.0.6",
365 | "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
366 | "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs="
367 | },
368 | "lodash.isstring": {
369 | "version": "4.0.1",
370 | "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
371 | "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE="
372 | },
373 | "lodash.once": {
374 | "version": "4.1.1",
375 | "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
376 | "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w="
377 | },
378 | "lodash.set": {
379 | "version": "4.3.2",
380 | "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz",
381 | "integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM="
382 | },
383 | "media-typer": {
384 | "version": "0.3.0",
385 | "resolved": "http://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
386 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
387 | },
388 | "merge-descriptors": {
389 | "version": "1.0.1",
390 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
391 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
392 | },
393 | "methods": {
394 | "version": "1.1.2",
395 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
396 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
397 | },
398 | "mime": {
399 | "version": "1.4.1",
400 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz",
401 | "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ=="
402 | },
403 | "mime-db": {
404 | "version": "1.37.0",
405 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz",
406 | "integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg=="
407 | },
408 | "mime-types": {
409 | "version": "2.1.21",
410 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.21.tgz",
411 | "integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==",
412 | "requires": {
413 | "mime-db": "~1.37.0"
414 | }
415 | },
416 | "ms": {
417 | "version": "2.0.0",
418 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
419 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
420 | },
421 | "negotiator": {
422 | "version": "0.6.1",
423 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz",
424 | "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk="
425 | },
426 | "object-assign": {
427 | "version": "4.1.1",
428 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
429 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
430 | },
431 | "on-finished": {
432 | "version": "2.3.0",
433 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
434 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
435 | "requires": {
436 | "ee-first": "1.1.1"
437 | }
438 | },
439 | "parseurl": {
440 | "version": "1.3.2",
441 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz",
442 | "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M="
443 | },
444 | "path-to-regexp": {
445 | "version": "0.1.7",
446 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
447 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
448 | },
449 | "prettier": {
450 | "version": "1.17.0",
451 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.17.0.tgz",
452 | "integrity": "sha512-sXe5lSt2WQlCbydGETgfm1YBShgOX4HxQkFPvbxkcwgDvGDeqVau8h+12+lmSVlP3rHPz0oavfddSZg/q+Szjw==",
453 | "dev": true
454 | },
455 | "proxy-addr": {
456 | "version": "2.0.4",
457 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz",
458 | "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==",
459 | "requires": {
460 | "forwarded": "~0.1.2",
461 | "ipaddr.js": "1.8.0"
462 | }
463 | },
464 | "qs": {
465 | "version": "6.5.2",
466 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
467 | "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
468 | },
469 | "range-parser": {
470 | "version": "1.2.0",
471 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz",
472 | "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4="
473 | },
474 | "raw-body": {
475 | "version": "2.3.3",
476 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz",
477 | "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==",
478 | "requires": {
479 | "bytes": "3.0.0",
480 | "http-errors": "1.6.3",
481 | "iconv-lite": "0.4.23",
482 | "unpipe": "1.0.0"
483 | }
484 | },
485 | "safe-buffer": {
486 | "version": "5.1.2",
487 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
488 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
489 | },
490 | "safer-buffer": {
491 | "version": "2.1.2",
492 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
493 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
494 | },
495 | "semver": {
496 | "version": "5.7.0",
497 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz",
498 | "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA=="
499 | },
500 | "send": {
501 | "version": "0.16.2",
502 | "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz",
503 | "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==",
504 | "requires": {
505 | "debug": "2.6.9",
506 | "depd": "~1.1.2",
507 | "destroy": "~1.0.4",
508 | "encodeurl": "~1.0.2",
509 | "escape-html": "~1.0.3",
510 | "etag": "~1.8.1",
511 | "fresh": "0.5.2",
512 | "http-errors": "~1.6.2",
513 | "mime": "1.4.1",
514 | "ms": "2.0.0",
515 | "on-finished": "~2.3.0",
516 | "range-parser": "~1.2.0",
517 | "statuses": "~1.4.0"
518 | }
519 | },
520 | "serve-static": {
521 | "version": "1.13.2",
522 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz",
523 | "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==",
524 | "requires": {
525 | "encodeurl": "~1.0.2",
526 | "escape-html": "~1.0.3",
527 | "parseurl": "~1.3.2",
528 | "send": "0.16.2"
529 | }
530 | },
531 | "setprototypeof": {
532 | "version": "1.1.0",
533 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
534 | "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ=="
535 | },
536 | "source-map": {
537 | "version": "0.6.1",
538 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
539 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
540 | "dev": true
541 | },
542 | "source-map-support": {
543 | "version": "0.5.12",
544 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.12.tgz",
545 | "integrity": "sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ==",
546 | "dev": true,
547 | "requires": {
548 | "buffer-from": "^1.0.0",
549 | "source-map": "^0.6.0"
550 | }
551 | },
552 | "statuses": {
553 | "version": "1.4.0",
554 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
555 | "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew=="
556 | },
557 | "type-is": {
558 | "version": "1.6.16",
559 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz",
560 | "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==",
561 | "requires": {
562 | "media-typer": "0.3.0",
563 | "mime-types": "~2.1.18"
564 | }
565 | },
566 | "unpipe": {
567 | "version": "1.0.0",
568 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
569 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
570 | },
571 | "utils-merge": {
572 | "version": "1.0.1",
573 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
574 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
575 | },
576 | "vary": {
577 | "version": "1.1.2",
578 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
579 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
580 | }
581 | }
582 | }
583 |
--------------------------------------------------------------------------------
/packages/boilerplate-server/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "server",
3 | "version": "1.0.0",
4 | "description": "A boilerplate node.js server using typescript",
5 | "scripts": {
6 | "watch": "webpack",
7 | "pack": "NODE_ENV=production webpack",
8 | "packWindows": "set NODE_ENV=production && webpack",
9 | "start": "nodemon --delay 3000ms ./dist/server.js",
10 | "clean": "rimraf dist",
11 | "startServer": "node ./dist/server.js"
12 | },
13 | "author": "Jeff Hull",
14 | "license": "ISC",
15 | "dependencies": {
16 | "@types/express": "^4.16.1",
17 | "cors": "^2.8.5",
18 | "express": "^4.16.4",
19 | "express-jwt": "^5.3.1"
20 | },
21 | "devDependencies": {
22 | "@types/node": "^10.14.5",
23 | "jsonwebtoken": "^8.5.1",
24 | "prettier": "^1.17.0",
25 | "source-map-support": "^0.5.12"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/packages/boilerplate-server/src/config/config.ts:
--------------------------------------------------------------------------------
1 | import { version } from './version'
2 | import { BaseServerConfig } from '@appkit-server/model/config'
3 | import { getServerConfigEnvVar } from '@appkit-server/util/env'
4 | import { User } from '@appkit-common/model/auth'
5 |
6 | interface Config extends BaseServerConfig {
7 | publicPath: string
8 | userDocsPath: string
9 | defaultUser: User
10 | }
11 |
12 | let initConfig: Config = {
13 | publicPath: '/public',
14 | userDocsPath: '/docs',
15 | authSecretKey: 'secretkey',
16 | port: 3000,
17 | version: version,
18 | rootDir: process.cwd(),
19 | configEnvironmentVariable: 'BOILERPLATE_SERVER_CONFIG',
20 | nodeEnvironment: 'development',
21 | defaultUser: {
22 | userName: 'admin',
23 | firstName: 'admin',
24 | lastName: 'admin',
25 | email: ''
26 | }
27 | }
28 |
29 | const nodeEnv = process.env.NODE_ENV
30 |
31 | if (
32 | nodeEnv != null &&
33 | nodeEnv
34 | .trim()
35 | .toLowerCase()
36 | .substr(0, 3) === 'dev'
37 | ) {
38 | initConfig.nodeEnvironment = 'development'
39 | } else {
40 | initConfig.nodeEnvironment = 'production'
41 | }
42 |
43 | if (initConfig.nodeEnvironment === 'production') {
44 | initConfig = {
45 | ...initConfig,
46 | ...(getServerConfigEnvVar(initConfig.configEnvironmentVariable) as any)
47 | }
48 | }
49 |
50 | export const config = initConfig
51 |
--------------------------------------------------------------------------------
/packages/boilerplate-server/src/config/middleware.ts:
--------------------------------------------------------------------------------
1 | import * as bodyParser from 'body-parser'
2 | import * as cors from 'cors'
3 | import * as express from 'express'
4 | import { config } from './config'
5 | // import * as jwtExpress from 'express-jwt'
6 |
7 | export function mountMiddleware(app: express.Express) {
8 | app.use('/', express.static(config.publicPath))
9 | app.use('/docs', express.static(config.userDocsPath))
10 | app.use(bodyParser.json({ limit: '10mb' }))
11 | if (config.nodeEnvironment === 'development') {
12 | // If we're in development, we need to enable cors so our webpack dev server can talk to our node server
13 | app.use(cors())
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/packages/boilerplate-server/src/config/version.ts:
--------------------------------------------------------------------------------
1 | export const version = '2018.09.08.01'
2 |
--------------------------------------------------------------------------------
/packages/boilerplate-server/src/logic/auth.ts:
--------------------------------------------------------------------------------
1 | import * as authUtil from '@appkit-server/util/auth'
2 | import {
3 | LoginRequest,
4 | LoginResponse,
5 | CheckTokenRequest,
6 | CheckTokenResponse
7 | } from '@appkit-common/model/auth'
8 | import { config } from '@server/config/config'
9 |
10 | export async function login(req: LoginRequest): Promise {
11 | if (
12 | req.credentials.userName === 'admin' &&
13 | req.credentials.password === 'admin'
14 | ) {
15 | const jwt = authUtil.signJwt(
16 | { string: req.credentials.userName },
17 | config.authSecretKey
18 | )
19 | return {
20 | succeeded: true,
21 | token: jwt,
22 | user: config.defaultUser,
23 | serverVersion: config.version
24 | }
25 | } else {
26 | throw new Error(`Login failed`)
27 | }
28 | }
29 |
30 | export async function checkToken(
31 | req: CheckTokenRequest
32 | ): Promise {
33 | try {
34 | authUtil.verifyJwt(req.token, config.authSecretKey)
35 | return {
36 | succeeded: true,
37 | serverVersion: config.version,
38 | user: config.defaultUser
39 | }
40 | } catch (err) {
41 | throw new Error(`Error validating token! ${err} `)
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/packages/boilerplate-server/src/routes/auth.ts:
--------------------------------------------------------------------------------
1 | import {
2 | LoginRequest,
3 | LoginResponse
4 | // LoginResponse,
5 | // CheckTokenRequest,
6 | // CheckTokenResponse
7 | } from '@appkit-common/model/auth'
8 | import * as authUtil from '@appkit-server/util/auth'
9 | import { config } from '@server/config/config'
10 |
11 | import { Express } from 'express'
12 |
13 | export function makeRoutes(app: Express) {
14 | // app.get('/test/hello', async (req, res) => {
15 | // res.send('Hello')
16 | // })
17 |
18 | app.post('/auth/login', async (req, res) => {
19 | const reqBody = req.body as LoginRequest
20 | // first check password. if OK, then provide a token based on the user name
21 | const token = authUtil.signJwt(
22 | { userName: reqBody.credentials.userName },
23 | config.authSecretKey
24 | )
25 | const loginResponse: LoginResponse = {
26 | succeeded: true,
27 | token: token,
28 | user: {
29 | userName: reqBody.credentials.userName,
30 | email: '',
31 | firstName: '',
32 | lastName: ''
33 | },
34 | serverVersion: config.version
35 | }
36 | res.json(loginResponse)
37 | })
38 | }
39 |
40 | // @Path('/auth')
41 | // export class AuthService {
42 | // @Path('/login')
43 | // @POST
44 | // async login(
45 | // req: LoginRequest,
46 | // @HeaderParam('authorization') authToken: string
47 | // ): Promise {
48 | // return {
49 | // succeeded: false,
50 | // token: '',
51 | // user: null,
52 | // serverVersion: null
53 | // }
54 | // }
55 |
56 | // @Path('/checkToken')
57 | // @POST
58 | // async checkToken(
59 | // req: CheckTokenRequest,
60 | // @HeaderParam('authorization') authToken: string
61 | // ): Promise {
62 | // return {
63 | // succeeded: false,
64 | // user: null,
65 | // serverVersion: null
66 | // }
67 | // }
68 | // }
69 |
--------------------------------------------------------------------------------
/packages/boilerplate-server/src/routes/index.ts:
--------------------------------------------------------------------------------
1 | import { Express } from 'express'
2 | import { makeRoutes as makeTestRoutes } from './test'
3 | import { makeRoutes as makeAuthRoutes } from './auth'
4 |
5 | /**
6 | * How can we build a route creator helper function?
7 | * One for get and one for post (prefer over just 'use')
8 | * Would be nice if we could pass an async function into it... so like
9 | *
10 | * makeroute(async (R) => T)
11 | * where R is the type of the body expected by the route, and T is the return type of the route.
12 | */
13 |
14 | export { makeRoutes } from './test'
15 |
16 | export function createRoutes(app: Express) {
17 | // Server.buildServices(app)
18 | makeTestRoutes(app)
19 | makeAuthRoutes(app)
20 | }
21 |
--------------------------------------------------------------------------------
/packages/boilerplate-server/src/routes/test.ts:
--------------------------------------------------------------------------------
1 | import { Express } from 'express'
2 |
3 | export function makeRoutes(app: Express) {
4 | app.get('/test/hello', async (req, res) => {
5 | console.log(`got request...sending response`)
6 | res.json({ msg: 'hello' })
7 | })
8 | }
9 |
--------------------------------------------------------------------------------
/packages/boilerplate-server/src/server.ts:
--------------------------------------------------------------------------------
1 | import * as express from 'express'
2 | import * as http from 'http'
3 | import * as timeUtils from '@appkit-server/util/time'
4 | import { mountMiddleware } from '@server/config/middleware'
5 | import { createRoutes } from '@server/routes/index'
6 | import { config } from '@server/config/config'
7 |
8 | if (config.nodeEnvironment === 'development') {
9 | require('source-map-support').install()
10 | }
11 |
12 | let expressApp: express.Express = express()
13 |
14 | mountMiddleware(expressApp)
15 | createRoutes(expressApp)
16 |
17 | let server = http.createServer(expressApp)
18 |
19 | async function doInitTasks() {
20 | console.log(`init tasks here`)
21 | }
22 |
23 | server.listen(config.port, '0.0.0.0', async function() {
24 | console.log(`Server started on port ${config.port}`)
25 | const MAX_INIT_RETRIES = 50
26 | const INIT_FAIL_DELAY_MS = 5000
27 | for (let i = 0; i < MAX_INIT_RETRIES; i++) {
28 | try {
29 | await doInitTasks()
30 | break
31 | } catch (e) {
32 | console.log(`Error starting server`)
33 | await timeUtils.delay(INIT_FAIL_DELAY_MS)
34 | }
35 | }
36 | })
37 |
--------------------------------------------------------------------------------
/packages/boilerplate-server/src/util/env.ts:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cyphercider/typescript-fullstack-monorepo/8875cf48b95e392bfa85f6af9125cfe4dfe8b273/packages/boilerplate-server/src/util/env.ts
--------------------------------------------------------------------------------
/packages/boilerplate-server/swaggerConfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "swagger": {
3 | "outputDirectory": "./dist",
4 | "entryFile": "./src/routes/index.ts",
5 | "host": "",
6 | "version": "",
7 | "name": "Kolbot Dashboard",
8 | "description": "A web client for managing Kolbot",
9 | "license": "ISC",
10 | "basePath": ""
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/packages/boilerplate-server/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base",
3 | "compileOnSave": true,
4 | "compilerOptions": {
5 | "outDir": "dist",
6 | "baseUrl": ".."
7 | },
8 | "include": [
9 | "../appkit-server/src/**/*.ts",
10 | "./src/**/*.ts",
11 | "../kolbot-pickit/src/**/*.ts"
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/packages/boilerplate-server/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require("path")
2 | const webpack = require("webpack")
3 | const TsconfigPathsPlugin = require("tsconfig-paths-webpack-plugin")
4 | const nodeExternals = require("webpack-node-externals")
5 |
6 | let devMode = false
7 | if (
8 | process.env.NODE_ENV == null ||
9 | process.env.NODE_ENV.substr(0, 3).toLowerCase() === "dev"
10 | ) {
11 | devMode = true
12 | console.log(`Webpack is running in development mode`)
13 | }
14 |
15 | module.exports = {
16 | target: "node",
17 | mode: devMode ? "development" : "production",
18 | entry: ["./src/server.ts"],
19 | watch: devMode,
20 | devtool: devMode ? "source-map" : undefined,
21 | externals: [nodeExternals()],
22 | resolve: {
23 | extensions: [".ts", ".js", ".json"],
24 | plugins: [
25 | new TsconfigPathsPlugin({
26 | configFile: "./tsconfig.json",
27 | logLevel: "info",
28 | logInfoToStdOut: true,
29 | extensions: [".ts"]
30 | })
31 | ]
32 | },
33 | module: {
34 | rules: [
35 | {
36 | test: /.tsx?$/,
37 | use: "ts-loader"
38 | }
39 | ]
40 | },
41 | plugins: [],
42 | output: {
43 | path: path.join(__dirname, "dist"),
44 | filename: "server.js",
45 | publicPath: "/"
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/server.code-workspace:
--------------------------------------------------------------------------------
1 | {
2 | "folders": [
3 | {
4 | "path": "packages/appkit-common"
5 | },
6 | {
7 | "path": "packages/boilerplate-server"
8 | },
9 | {
10 | "path": "packages/appkit-server"
11 | }
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/tsconfig.base.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": "packages",
4 | "emitDecoratorMetadata": true,
5 | "experimentalDecorators": true,
6 | "lib": ["es2018"],
7 | "module": "commonjs",
8 | "noUnusedLocals": true,
9 | "skipLibCheck": true,
10 | "sourceMap": true,
11 | "target": "ES2016",
12 | "strictNullChecks": true,
13 | "paths": {
14 | "@appkit-client/*": ["appkit-client/src/*"],
15 | "@appkit-server/*": ["appkit-server/src/*"],
16 | "@appkit-common/*": ["appkit-common/src/*"],
17 | "@server/*": ["boilerplate-server/src/*"],
18 | "@client/*": ["boilerplate-client/src/*"]
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.base"
3 | }
4 |
--------------------------------------------------------------------------------