├── .babelrc ├── react-ui ├── .env.development ├── src │ ├── index.css │ ├── redux │ │ ├── index.js │ │ └── products.js │ ├── App.js │ ├── containers │ │ ├── About.spec.js │ │ ├── About.js │ │ ├── ProductsPage.spec.js │ │ ├── __snapshots__ │ │ │ ├── About.spec.js.snap │ │ │ └── ProductsPage.spec.js.snap │ │ └── ProductsPage.js │ ├── store.js │ ├── types │ │ └── index.js │ ├── index.js │ ├── logo.svg │ └── registerServiceWorker.js ├── public │ ├── favicon.ico │ ├── manifest.json │ └── index.html ├── flow-typed │ └── npm │ │ ├── flow-bin_v0.x.x.js │ │ ├── @shopify │ │ └── polaris_vx.x.x.js │ │ ├── redux-thunk_vx.x.x.js │ │ ├── react-redux_v5.x.x.js │ │ ├── react-router-redux_vx.x.x.js │ │ ├── enzyme_v2.3.x.js │ │ ├── redux_v3.x.x.js │ │ ├── axios_v0.16.x.js │ │ └── react-router-dom_v4.x.x.js ├── .gitignore └── package.json ├── .flowconfig ├── .eslintignore ├── nodemon.json ├── server ├── db │ ├── index.js │ ├── connect.js │ ├── models │ │ ├── shop.js │ │ └── index.js │ ├── session.js │ ├── sequelize_config.json │ └── migrations │ │ └── 20170805140708-create-shop.js ├── views │ ├── shopify_redirect.pug │ ├── 500.pug │ └── charge_declined.pug ├── config │ └── index.js ├── routes │ ├── session-helper.js │ ├── setupMocks.js │ ├── shopify.js │ └── shopify.spec.js ├── server.js └── app.js ├── .gitignore ├── flow-typed └── npm │ ├── flow-bin_v0.x.x.js │ ├── debug_v2.x.x.js │ ├── body-parser_v1.x.x.js │ ├── morgan_v1.x.x.js │ ├── express_v4.x.x.js │ ├── bluebird_v3.x.x.js │ ├── pg_v6.x.x.js │ ├── jest_v20.x.x.js │ └── lodash_v4.x.x.js ├── .sequelizerc ├── .eslintrc ├── LICENSE ├── webpack.config.js ├── package.json └── README.md /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "stage-0"] 3 | } 4 | -------------------------------------------------------------------------------- /react-ui/.env.development: -------------------------------------------------------------------------------- 1 | DANGEROUSLY_DISABLE_HOST_CHECK=true -------------------------------------------------------------------------------- /react-ui/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /react-ui/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/airmiha/create-shopify-app/HEAD/react-ui/public/favicon.ico -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | .*node_modules.* 3 | [include] 4 | 5 | [libs] 6 | react-ui/flow-typed 7 | 8 | [options] 9 | 10 | [lints] 11 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | /compiled/** 2 | /flow-typed/** 3 | /public/** 4 | /node_modules/** 5 | /react-ui/flow-typed/** 6 | /react-ui/node_modules/** 7 | /react-ui/build/** -------------------------------------------------------------------------------- /nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "verbose": false, 3 | "watch": ["server", "webpack.config.js", ".env"], 4 | "exec": "npm run build:dev && node compiled/server.dev.js" 5 | } 6 | -------------------------------------------------------------------------------- /server/db/index.js: -------------------------------------------------------------------------------- 1 | import connect from './connect'; 2 | import session from './session'; 3 | import { Models, sequelize } from './models'; 4 | 5 | export { connect, Models, sequelize, session }; 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Node build artifacts 2 | node_modules 3 | npm-debug.log 4 | 5 | # Local development 6 | *.env 7 | *.dev 8 | .vscode 9 | .DS_Store 10 | compiled 11 | coverage 12 | 13 | # Docker 14 | Dockerfile 15 | docker-compose.yml -------------------------------------------------------------------------------- /flow-typed/npm/flow-bin_v0.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: 67b0c3a16b2d6f8ef0a31a5745a0b3e1 2 | // flow-typed version: 3817bc6980/flow-bin_v0.x.x/flow_>=v0.25.x 3 | 4 | declare module "flow-bin" { 5 | declare module.exports: string; 6 | } 7 | -------------------------------------------------------------------------------- /react-ui/flow-typed/npm/flow-bin_v0.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: 67b0c3a16b2d6f8ef0a31a5745a0b3e1 2 | // flow-typed version: 3817bc6980/flow-bin_v0.x.x/flow_>=v0.25.x 3 | 4 | declare module "flow-bin" { 5 | declare module.exports: string; 6 | } 7 | -------------------------------------------------------------------------------- /.sequelizerc: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | 3 | module.exports = { 4 | 'config': path.resolve('./server/db', 'sequelize_config.json'), 5 | 'migrations-path': path.resolve('./server/db', 'migrations'), 6 | 'models-path': path.resolve('./server/db', 'models') 7 | } -------------------------------------------------------------------------------- /react-ui/src/redux/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import { combineReducers } from 'redux'; 3 | import { routerReducer } from 'react-router-redux'; 4 | import products from './products'; 5 | 6 | export default combineReducers({ 7 | router: routerReducer, 8 | products 9 | }); 10 | -------------------------------------------------------------------------------- /react-ui/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /server/db/connect.js: -------------------------------------------------------------------------------- 1 | import logger from 'winston'; 2 | import { sequelize } from './models'; 3 | 4 | export default () => { 5 | sequelize 6 | .authenticate() 7 | .then(() => { 8 | logger.info('Connection has been established successfully.'); 9 | }) 10 | .catch((err) => { 11 | logger.error('Unable to connect to the database:', err); 12 | }); 13 | }; 14 | -------------------------------------------------------------------------------- /react-ui/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | -------------------------------------------------------------------------------- /react-ui/src/App.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import React from 'react'; 3 | import { Route } from 'react-router-dom'; 4 | 5 | import About from './containers/About'; 6 | import ProductsPage from './containers/ProductsPage'; 7 | 8 | const App = () => 9 |
79 | Your React Shopify app is ready. You can start building solutions for merchants! 80 |
81 |,
40 | props: OP,
41 | state: void
42 | }
43 |
44 | declare type ConnectedComponentClass
50 | ) => ConnectedComponentClass (
146 | Component: React$ComponentType ;
148 |
149 | declare type MatchPathOptions = {
150 | path?: string,
151 | exact?: boolean,
152 | sensitive?: boolean,
153 | strict?: boolean
154 | };
155 |
156 | declare export function matchPath(
157 | pathname: string,
158 | options?: MatchPathOptions | string
159 | ): null | Match;
160 | }
161 |
--------------------------------------------------------------------------------
/flow-typed/npm/morgan_v1.x.x.js:
--------------------------------------------------------------------------------
1 | // flow-typed signature: f3639eb89945dc27e7a7ab35964fa591
2 | // flow-typed version: b43dff3e0e/morgan_v1.x.x/flow_>=v0.20.x
3 |
4 | /* @flow */
5 | import type { Middleware, $Request, $Response } from 'express';
6 |
7 | declare module "morgan" {
8 |
9 | declare type FormatFn = (tokens: TokenIndexer, req: $Request, res: $Response) => string;
10 |
11 | declare type TokenCallbackFn = (req: $Request, res: $Response, arg?: string | number | boolean) => string;
12 |
13 | declare interface TokenIndexer {
14 | [tokenName: string]: TokenCallbackFn;
15 | }
16 |
17 | /**
18 | * Public interface of morgan logger
19 | */
20 | declare interface Morgan {
21 | /***
22 | * Create a new morgan logger middleware function using the given format and options. The format argument may be a string of a predefined name (see below for the names),
23 | * or a string of a format string containing defined tokens.
24 | * @param format
25 | * @param options
26 | */
27 | (format: string, options?: Options): Middleware;
28 | /***
29 | * Standard Apache combined log output.
30 | * :remote-addr - :remote-user [:date] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent"
31 | * @param format
32 | * @param options
33 | */
34 | (format: 'combined', options?: Options): Middleware;
35 | /***
36 | * Standard Apache common log output.
37 | * :remote-addr - :remote-user [:date] ":method :url HTTP/:http-version" :status :res[content-length]
38 | * @param format
39 | * @param options
40 | */
41 | (format: 'common', options?: Options): Middleware;
42 | /**
43 | * Concise output colored by response status for development use. The :status token will be colored red for server error codes, yellow for client error codes, cyan for redirection codes, and uncolored for all other codes.
44 | * :method :url :status :response-time ms - :res[content-length]
45 | * @param format
46 | * @param options
47 | */
48 | (format: 'dev', options?: Options): Middleware;
49 |
50 | /***
51 | * Shorter than default, also including response time.
52 | * :remote-addr :remote-user :method :url HTTP/:http-version :status :res[content-length] - :response-time ms
53 | * @param format
54 | * @param options
55 | */
56 | (format: 'short', options?: Options): Middleware;
57 |
58 | /***
59 | * The minimal output.
60 | * :method :url :status :res[content-length] - :response-time ms
61 | * @param format
62 | * @param options
63 | */
64 | (format: 'tiny', options?: Options): Middleware;
65 |
66 | /***
67 | * Create a new morgan logger middleware function using the given format and options. The format argument may be a
68 | * custom format function which adheres to the signature.
69 | * @param format
70 | * @param options
71 | */
72 | (format: FormatFn, options?: Options): Middleware;
73 |
74 | /**
75 | * Define a custom token which can be used in custom morgan logging formats.
76 | */
77 | token(name: string, callback: TokenCallbackFn): Morgan;
78 | /**
79 | * Define a named custom format by specifying a format string in token notation
80 | */
81 | format(name: string, fmt: string): Morgan;
82 |
83 | /**
84 | * Define a named custom format by specifying a format function
85 | */
86 | format(name: string, fmt: FormatFn): Morgan;
87 |
88 | /**
89 | * Compile a format string in token notation into a format function
90 | */
91 | compile(format: string): FormatFn;
92 | }
93 |
94 | /**
95 | * Define a custom token which can be used in custom morgan logging formats.
96 | */
97 | declare function token(name: string, callback: TokenCallbackFn): Morgan;
98 |
99 | /**
100 | * Define a named custom format by specifying a format string in token notation
101 | */
102 | declare function format(name: string, fmt: string): Morgan;
103 |
104 | /**
105 | * Define a named custom format by specifying a format function
106 | */
107 | declare function format(name: string, fmt: FormatFn): Morgan;
108 |
109 | /**
110 | * Compile a format string in token notation into a format function
111 | */
112 | declare function compile(format: string): FormatFn;
113 |
114 | declare interface StreamOptions {
115 | /**
116 | * Output stream for writing log lines
117 | */
118 | write: (str: string) => void;
119 | }
120 |
121 | /***
122 | * Morgan accepts these properties in the options object.
123 | */
124 | declare interface Options {
125 |
126 | /***
127 | * Buffer duration before writing logs to the stream, defaults to false. When set to true, defaults to 1000 ms.
128 | */
129 | buffer?: boolean;
130 |
131 | /***
132 | * Write log line on request instead of response. This means that a requests will be logged even if the server crashes, but data from the response cannot be logged (like the response code).
133 | */
134 | immediate?: boolean;
135 |
136 | /***
137 | * Function to determine if logging is skipped, defaults to false. This function will be called as skip(req, res).
138 | */
139 | skip?: (req: $Request, res: $Response) => boolean;
140 |
141 | /***
142 | * Output stream for writing log lines, defaults to process.stdout.
143 | * @param str
144 | */
145 | stream?: StreamOptions;
146 | }
147 |
148 | declare var exports: Morgan
149 | }
150 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | # Create Shopify App with Node and React
3 |
4 | Shopify Application starter with React, Polaris, Express, and Postgres.
5 |
6 | ## Description
7 |
8 | Use this project as a starter for applications using the Shopify API. It gives you all the required code for authenticating with a shop via Oauth. It even includes billing. It also demonstrates the usage of the Embedded App SDK.
9 |
10 | The project has a create-react-app client application backed by an Express server. It fetches a list of products from the shop. You can then add more with the ResourcePicker from the Embedded App SDK.
11 |
12 | ### Why use this?
13 |
14 | If you're a Javascript developer, use create-shopify-app to kickstart your development. It saves you a lot of time you'd use on setting up the project. You get everything you need to build a modern Shopify app with your favorite tools:
15 |
16 | * React, JSX, ES6, and Flow syntax support.
17 | * A Webpack Dev server with live reloading
18 | * State management with Redux
19 | * React Router v4
20 | * Embedded App SDK and Polaris
21 | * Unit testing with Jest
22 | * All the code for authenticating with a shop via oAuth using Express middleware
23 | * Middleware for setting up billing and recurring charges
24 | * Best practices from the community
25 |
26 | ## Prerequisites
27 |
28 | This project uses Postgres for data persistence and Redis for session management. You'll need to install and run both. You'll also need Node and npm.
29 |
30 | Download the project from this Github repository and install dependencies:
31 |
32 | 1. Run **npm install** from the root to install main dependencies
33 | 2. Run **cd react-ui && npm install** for client-side dependencies
34 | 3. Expose your application to the Internet using ngrok. See [Shopify's documentation](https://help.shopify.com/api/tutorials/building-public-app) . (replace port 4567 with 3000)
35 |
36 | ## Getting started
37 |
38 | The following list of steps will get you ready for development.
39 |
40 | ### Step 1: Becoming a Shopify App Developer
41 |
42 | If you don't have a Shopify Partner account yet head over to http://shopify.com/partners to create one. You'll need it before you can start developing apps.
43 |
44 | Once you have a Partner account create a new application to get an API key and other API credentials.
45 |
46 | ### Step 2: Configuring your application
47 |
48 | When you start ngrok, it'll give you a random subdomain (*.ngrok.io).
49 |
50 | In the project root directory, open `server/config/index.js`. Set `APP_URL` to the subdomain ngrok assigned to you. In production, this value should match your deployment URL (for example, **.herokuapp.com). Also, set your `APP_NAME`.
51 |
52 | In the project root directory, create a new file named `.env` and open it in a text editor. Login to your Shopify partner account and find your App credentials. Set your API key and App secret in the `.env` file.
53 |
54 | ```sh
55 | SHOPIFY_API_KEY=your API key
56 | SHOPIFY_API_SECRET=app secret
57 | ```
58 |
59 | In the `react-ui` directory, create a new file named `.env` and open it in a text editor. Set your API key and development store URL.
60 |
61 | ```sh
62 | REACT_APP_SHOPIFY_API_KEY=your API key
63 | REACT_APP_SHOP_ORIGIN=your-development-store.myshopify.com
64 | ```
65 |
66 | You'll only use these values in development. The Embedded app SDK uses them to initialize itself. In production, they are injected by the Express server in the built client app.
67 |
68 | **Your api credentials should not be in source control**. In production, keep your keys in environment variables.
69 |
70 | In your partner dashboard, go to App info. For the App URL, set
71 |
72 | ```
73 | https://#{app_url}/home
74 | ```
75 |
76 | Here `app_url` is the root path of your application (the same value as APP_URL in your config file).
77 |
78 | For Whistlisted redirection URL, set
79 |
80 | ```
81 | https://#{app_url}/auth/callback
82 | ```
83 |
84 | Also, remember to check `enabled` for the embedded settings.
85 |
86 | You can set these URLs in the config file. But, the values in config should match the ones in the partner dashboard.
87 |
88 | ### Step 3: Set-up your database
89 |
90 | This project uses Postgres for its persistence layer, with Sequelize ORM. Create local databases for development and testing. Then run the Sequelize migration script to create a shop table:
91 |
92 | ```sh
93 | createdb shopify-app-development # or test/production
94 | npm run sequelize db:migrate
95 | ```
96 |
97 | In production, you connect to the database through an environment variable DATABASE_URL.
98 |
99 | ### Step 4: Run the app on your local machine
100 |
101 | ```sh
102 | npm run start:dev
103 | ```
104 | This will start `nodemon` on the server side and `create-react-app` on the client. The Node server will restart when you make changes in that part of the code.
105 |
106 | The page will reload if you make edits in the `react-ui` folder. You'll see the build errors and lint warnings in the console.
107 |
108 | ### Step 5: Install your app on a test store
109 |
110 | If you don't have one already, create a development store. Open [Shopify's documentation](https://help.shopify.com/api/tutorials/building-public-app). Scroll down to the **Install your app on a test store** section. Follow those steps. Once you start the installation process, the following will happen:
111 |
112 | 1. You'll see a screen to confirm the installation, with the scopes you requested.
113 | 2. Once you confirm, you'll have to accept a recurring application charge. It's only a test charge so don't worry.
114 | 3. You'll see the app inside the Shopify admin. You can play with it or start building.
115 |
116 | ## Deploying to Heroku
117 |
118 | ```sh
119 | heroku create your-app-name
120 |
121 | # Add Redis
122 | heroku addons:create heroku-redis:hobby-dev -a your-app
123 |
124 | # Add Postgres
125 | heroku addons:create heroku-postgresql:hobby-dev -a your-app
126 |
127 | # Deploy to Heroku server
128 | git push heroku master
129 |
130 | # Set environment variables
131 | heroku config:set APP_URL=your-app.herokuapp.com
132 | heroku config:set SHOPIFY_API_KEY=your API key
133 | heroku config:set SHOPIFY_API_SECRET=app secret
134 | heroku config:set SESSION_SECRET=session secret
135 |
136 | # Run the migration
137 | heroku run sequelize db:migrate
138 |
139 | # Open Link in browser
140 | heroku open
141 | ```
142 |
143 | ## Testing
144 |
145 | We use Jest for both client and server tests. The `create-react-app` project comes with Jest by default. For the server side, we use a custom configuration. Jest has spy and mock capabilities so there's no need for additional libraries.
146 |
147 | ```sh
148 |
149 | # Run client tests in watch mode
150 | npm run test:client
151 |
152 | # Run server tests in watch mode
153 | npm run test:server
154 |
155 | # Run all tests for Continuous integration
156 | npm run test
157 | ```
158 |
159 | ## License
160 |
161 | [MIT](LICENSE)
162 |
--------------------------------------------------------------------------------
/flow-typed/npm/express_v4.x.x.js:
--------------------------------------------------------------------------------
1 | // flow-typed signature: d375d667aaa59b8207e6a167cc9a06fe
2 | // flow-typed version: ef2fdb0770/express_v4.x.x/flow_>=v0.32.x
3 |
4 | import type { Server } from 'http';
5 | import type { Socket } from 'net';
6 |
7 | declare type express$RouterOptions = {
8 | caseSensitive?: boolean,
9 | mergeParams?: boolean,
10 | strict?: boolean
11 | };
12 |
13 | declare class express$RequestResponseBase {
14 | app: express$Application;
15 | get(field: string): string | void;
16 | }
17 |
18 | declare type express$RequestParams = {
19 | [param: string]: string
20 | }
21 |
22 | declare class express$Request extends http$IncomingMessage mixins express$RequestResponseBase {
23 | baseUrl: string;
24 | body: any;
25 | cookies: {[cookie: string]: string};
26 | connection: Socket;
27 | fresh: boolean;
28 | hostname: string;
29 | ip: string;
30 | ips: Array extends React$Component<{
53 | store: Store,
54 | children?: any
55 | }> {}
56 |
57 | declare type ConnectOptions = {
58 | pure?: boolean,
59 | withRef?: boolean
60 | };
61 |
62 | declare type Null = null | void;
63 |
64 | declare function connect(
65 | ...rest: Array(
76 | mapStateToProps: MapStateToProps,
77 | mapDispatchToProps: Null,
78 | mergeProps: Null,
79 | options?: ConnectOptions
80 | ): Connector(
90 | mapStateToProps: MapStateToProps,
91 | mapDispatchToProps: MapDispatchToProps,
92 | mergeProps: Null,
93 | options?: ConnectOptions
94 | ): Connector(
97 | mapStateToProps: MapStateToProps,
98 | mapDispatchToProps: Null,
99 | mergeProps: MergeProps(
104 | mapStateToProps: MapStateToProps,
105 | mapDispatchToProps: MapDispatchToProps,
106 | mergeProps: MergeProps> = {
17 | dispatch: D,
18 | getState(): S
19 | };
20 |
21 | declare type Store> = {
22 | // rewrite MiddlewareAPI members in order to get nicer error messages (intersections produce long messages)
23 | dispatch: D,
24 | getState(): S,
25 | subscribe(listener: () => void): () => void,
26 | replaceReducer(nextReducer: Reducer): void
27 | };
28 |
29 | declare type Reducer = (state: S, action: A) => S;
30 |
31 | declare type CombinedReducer = (
32 | state: ($Shape & {}) | void,
33 | action: A
34 | ) => S;
35 |
36 | declare type Middleware> = (
37 | api: MiddlewareAPI
38 | ) => (next: D) => D;
39 |
40 | declare type StoreCreator> = {
41 | (reducer: Reducer, enhancer?: StoreEnhancer): Store,
42 | (
43 | reducer: Reducer,
44 | preloadedState: S,
45 | enhancer?: StoreEnhancer
46 | ): Store
47 | };
48 |
49 | declare type StoreEnhancer> = (
50 | next: StoreCreator
51 | ) => StoreCreator;
52 |
53 | declare function createStore(
54 | reducer: Reducer,
55 | enhancer?: StoreEnhancer
56 | ): Store;
57 | declare function createStore(
58 | reducer: Reducer,
59 | preloadedState: S,
60 | enhancer?: StoreEnhancer
61 | ): Store;
62 |
63 | declare function applyMiddleware(
64 | ...middlewares: Array;
66 |
67 | declare type ActionCreator = (...args: Array) => A;
68 | declare type ActionCreators) => S>, A>;
91 |
92 | declare function compose(ab: (a: A) => B): (a: A) => B;
93 | declare function compose(
94 | bc: (b: B) => C,
95 | ab: (a: A) => B
96 | ): (a: A) => C;
97 | declare function compose(
98 | cd: (c: C) => D,
99 | bc: (b: B) => C,
100 | ab: (a: A) => B
101 | ): (a: A) => D;
102 | declare function compose(
103 | de: (d: D) => E,
104 | cd: (c: C) => D,
105 | bc: (b: B) => C,
106 | ab: (a: A) => B
107 | ): (a: A) => E;
108 | declare function compose(
109 | ef: (e: E) => F,
110 | de: (d: D) => E,
111 | cd: (c: C) => D,
112 | bc: (b: B) => C,
113 | ab: (a: A) => B
114 | ): (a: A) => F;
115 | declare function compose(
116 | fg: (f: F) => G,
117 | ef: (e: E) => F,
118 | de: (d: D) => E,
119 | cd: (c: C) => D,
120 | bc: (b: B) => C,
121 | ab: (a: A) => B
122 | ): (a: A) => G;
123 | declare function compose(
124 | gh: (g: G) => H,
125 | fg: (f: F) => G,
126 | ef: (e: E) => F,
127 | de: (d: D) => E,
128 | cd: (c: C) => D,
129 | bc: (b: B) => C,
130 | ab: (a: A) => B
131 | ): (a: A) => H;
132 | declare function compose(
133 | hi: (h: H) => I,
134 | gh: (g: G) => H,
135 | fg: (f: F) => G,
136 | ef: (e: E) => F,
137 | de: (d: D) => E,
138 | cd: (c: C) => D,
139 | bc: (b: B) => C,
140 | ab: (a: A) => B
141 | ): (a: A) => I;
142 | }
143 |
--------------------------------------------------------------------------------
/react-ui/src/registerServiceWorker.js:
--------------------------------------------------------------------------------
1 | // In production, we register a service worker to serve assets from local cache.
2 |
3 | // This lets the app load faster on subsequent visits in production, and gives
4 | // it offline capabilities. However, it also means that developers (and users)
5 | // will only see deployed updates on the "N+1" visit to a page, since previously
6 | // cached resources are updated in the background.
7 |
8 | // To learn more about the benefits of this model, read https://goo.gl/KwvDNy.
9 | // This link also includes instructions on opting out of this behavior.
10 |
11 | const isLocalhost = Boolean(
12 | window.location.hostname === 'localhost' ||
13 | // [::1] is the IPv6 localhost address.
14 | window.location.hostname === '[::1]' ||
15 | // 127.0.0.1/8 is considered localhost for IPv4.
16 | window.location.hostname.match(
17 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/,
18 | ),
19 | );
20 |
21 | function registerValidSW(swUrl) {
22 | navigator.serviceWorker
23 | .register(swUrl)
24 | .then((registration) => {
25 | // eslint-disable-next-line no-param-reassign
26 | registration.onupdatefound = () => {
27 | const installingWorker = registration.installing;
28 | installingWorker.onstatechange = () => {
29 | if (installingWorker.state === 'installed') {
30 | if (navigator.serviceWorker.controller) {
31 | // At this point, the old content will have been purged and
32 | // the fresh content will have been added to the cache.
33 | // It's the perfect time to display a "New content is
34 | // available; please refresh." message in your web app.
35 | console.log('New content is available; please refresh.');
36 | } else {
37 | // At this point, everything has been precached.
38 | // It's the perfect time to display a
39 | // "Content is cached for offline use." message.
40 | console.log('Content is cached for offline use.');
41 | }
42 | }
43 | };
44 | };
45 | })
46 | .catch((error) => {
47 | console.error('Error during service worker registration:', error);
48 | });
49 | }
50 |
51 | function checkValidServiceWorker(swUrl) {
52 | // Check if the service worker can be found. If it can't reload the page.
53 | fetch(swUrl)
54 | .then((response) => {
55 | // Ensure service worker exists, and that we really are getting a JS file.
56 | if (
57 | response.status === 404 ||
58 | response.headers.get('content-type').indexOf('javascript') === -1
59 | ) {
60 | // No service worker found. Probably a different app. Reload the page.
61 | navigator.serviceWorker.ready.then((registration) => {
62 | registration.unregister().then(() => {
63 | window.location.reload();
64 | });
65 | });
66 | } else {
67 | // Service worker found. Proceed as normal.
68 | registerValidSW(swUrl);
69 | }
70 | })
71 | .catch(() => {
72 | console.log(
73 | 'No internet connection found. App is running in offline mode.',
74 | );
75 | });
76 | }
77 |
78 | export default function register() {
79 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
80 | // The URL constructor is available in all browsers that support SW.
81 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location);
82 | if (publicUrl.origin !== window.location.origin) {
83 | // Our service worker won't work if PUBLIC_URL is on a different origin
84 | // from what our page is served on. This might happen if a CDN is used to
85 | // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374
86 | return;
87 | }
88 |
89 | window.addEventListener('load', () => {
90 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
91 |
92 | if (!isLocalhost) {
93 | // Is not local host. Just register service worker
94 | registerValidSW(swUrl);
95 | } else {
96 | // This is running on localhost. Lets check if a service worker still exists or not.
97 | checkValidServiceWorker(swUrl);
98 | }
99 | });
100 | }
101 | }
102 |
103 | export function unregister() {
104 | if ('serviceWorker' in navigator) {
105 | navigator.serviceWorker.ready.then((registration) => {
106 | registration.unregister();
107 | });
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/react-ui/flow-typed/npm/axios_v0.16.x.js:
--------------------------------------------------------------------------------
1 | // flow-typed signature: fe9c24a017795779e7874809ca910ad6
2 | // flow-typed version: b43dff3e0e/axios_v0.16.x/flow_>=v0.25.x
3 |
4 | declare module 'axios' {
5 | declare interface ProxyConfig {
6 | host: string;
7 | port: number;
8 | }
9 | declare interface Cancel {
10 | constructor(message?: string): Cancel;
11 | message: string;
12 | }
13 | declare interface Canceler {
14 | (message?: string): void;
15 | }
16 | declare interface CancelTokenSource {
17 | token: CancelToken;
18 | cancel: Canceler;
19 | }
20 | declare interface CancelToken {
21 | constructor(executor: (cancel: Canceler) => void): CancelToken;
22 | static source(): CancelTokenSource;
23 | promise: Promise