Loading articles...
;
23 | }
24 |
25 | if (articles.length === 0) {
26 | return (
27 | (({ article, deleteArticle }) => {
20 | return (
21 |
22 | {article.title}
23 |
24 | View
25 | Edit
26 | deleteArticle(article)}
29 | style={{ color: 'darkred' }}
30 | >
31 | Delete
32 |
33 |
34 |
35 | );
36 | }, areEqual);
37 |
38 | const getStyle = (): React.CSSProperties => ({
39 | overflowX: 'hidden',
40 | textOverflow: 'ellipsis',
41 | width: '300px',
42 | });
43 |
44 | export default connect(
45 | null,
46 | dispatchProps
47 | )(ArticleListItem);
48 |
--------------------------------------------------------------------------------
/src/features/articles/reducer.ts:
--------------------------------------------------------------------------------
1 | import { Article } from 'MyModels';
2 | import { combineReducers } from 'redux';
3 | import { createReducer } from 'typesafe-actions';
4 |
5 | import {
6 | loadArticlesAsync,
7 | createArticleAsync,
8 | updateArticleAsync,
9 | deleteArticleAsync,
10 | } from './actions';
11 |
12 | const reducer = combineReducers({
13 | isLoadingArticles: createReducer(false as boolean)
14 | .handleAction([loadArticlesAsync.request], (state, action) => true)
15 | .handleAction(
16 | [loadArticlesAsync.success, loadArticlesAsync.failure],
17 | (state, action) => false
18 | ),
19 | articles: createReducer([] as Article[])
20 | .handleAction(
21 | [
22 | loadArticlesAsync.success,
23 | createArticleAsync.success,
24 | updateArticleAsync.success,
25 | deleteArticleAsync.success,
26 | ],
27 | (state, action) => action.payload
28 | )
29 | .handleAction(createArticleAsync.request, (state, action) => [
30 | ...state,
31 | action.payload,
32 | ])
33 | .handleAction(updateArticleAsync.request, (state, action) =>
34 | state.map(i => (i.id === action.payload.id ? action.payload : i))
35 | )
36 | .handleAction(deleteArticleAsync.request, (state, action) =>
37 | state.filter(i => i.id !== action.payload.id)
38 | )
39 | .handleAction(deleteArticleAsync.failure, (state, action) =>
40 | state.concat(action.payload)
41 | ),
42 | });
43 |
44 | export default reducer;
45 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
15 |
16 |
25 | React App
26 |
27 |
28 |
29 |
30 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/src/features/articles/epics.ts:
--------------------------------------------------------------------------------
1 | import { RootEpic } from 'MyTypes';
2 | import { from, of } from 'rxjs';
3 | import { filter, switchMap, map, catchError } from 'rxjs/operators';
4 | import { isActionOf } from 'typesafe-actions';
5 |
6 | import {
7 | loadArticlesAsync,
8 | createArticleAsync,
9 | updateArticleAsync,
10 | deleteArticleAsync,
11 | } from './actions';
12 |
13 | export const loadArticlesEpic: RootEpic = (action$, state$, { api }) =>
14 | action$.pipe(
15 | filter(isActionOf(loadArticlesAsync.request)),
16 | switchMap(() =>
17 | from(api.articles.loadArticles()).pipe(
18 | map(loadArticlesAsync.success),
19 | catchError(message => of(loadArticlesAsync.failure(message)))
20 | )
21 | )
22 | );
23 |
24 | export const createArticlesEpic: RootEpic = (action$, state$, { api }) =>
25 | action$.pipe(
26 | filter(isActionOf(createArticleAsync.request)),
27 | switchMap(action =>
28 | from(api.articles.createArticle(action.payload)).pipe(
29 | map(createArticleAsync.success),
30 | catchError(message => of(createArticleAsync.failure(message)))
31 | )
32 | )
33 | );
34 |
35 | export const updateArticlesEpic: RootEpic = (action$, state$, { api }) =>
36 | action$.pipe(
37 | filter(isActionOf(updateArticleAsync.request)),
38 | switchMap(action =>
39 | from(api.articles.updateArticle(action.payload)).pipe(
40 | map(updateArticleAsync.success),
41 | catchError(message => of(updateArticleAsync.failure(message)))
42 | )
43 | )
44 | );
45 |
46 | export const deleteArticlesEpic: RootEpic = (action$, state$, { api, toast }) =>
47 | action$.pipe(
48 | filter(isActionOf(deleteArticleAsync.request)),
49 | switchMap(action =>
50 | from(api.articles.deleteArticle(action.payload)).pipe(
51 | map(deleteArticleAsync.success),
52 | catchError(message => {
53 | toast.error(message);
54 | return of(deleteArticleAsync.failure(action.payload));
55 | })
56 | )
57 | )
58 | );
59 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # React, Redux, TypeScript - RealWorld App
4 |
5 | ## 🚧🚧🚧 UNDER CONSTRUCTION 🚧🚧🚧
6 |
7 | ### **LIVE DEMO: [LINK](https://react-redux-typescript-realworld-app.netlify.com/)**
8 |
9 | _Reference implementation of RealWorld [JAMStack](https://jamstack.org/) Application based on ["React, Redux, TypeScript Guide"](https://github.com/piotrwitek/react-redux-typescript-guide)
10 | and [Create React App v3.0](https://facebook.github.io/create-react-app/)._
11 |
12 |
13 |
14 | ---
15 |
16 | ## Features Roadmap:
17 | - [x] Routing with React-Router
18 | - [ ] User Identity
19 | - [ ] External providers (Google, Github, Bitbucket)
20 | - [ ] Registration / Authentication
21 | - [x] Cross-cutting Application Services
22 | - [x] Local Storage
23 | - [x] Client Logger
24 | - [x] Toasts
25 | - [ ] Analytics
26 | - [x] Feature Folders
27 | - [x] `/articles` - Articles listing with CRUD Operations
28 | - [ ] `/realtime-monitoring` - Realtime monitoring of connected users using Websockets
29 | - [x] REST API Integration (API Client)
30 | - [ ] WebSockets Integration
31 | - [ ] Serverless Lambda Functions (Netlify Functions)
32 | - [ ] Utilities (HOC, Hooks, Media Queries...)
33 | - [ ] Typesafe Styling/Theming with CSSinJS (`Emotion`)
34 | - [ ] ...
35 |
36 | ---
37 |
38 | ## Available Scripts
39 |
40 | ### `npm start`
41 |
42 | Runs the app in the development modeat [http://localhost:3000](http://localhost:3000)
43 |
44 | ### `npm test`
45 |
46 | Launches the test runner in the interactive watch mode.
47 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
48 |
49 | ### `npm run build`
50 |
51 | Builds the app for production to the `build` folder.
52 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
53 |
54 | ## Learn More
55 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
56 |
--------------------------------------------------------------------------------
/src/assets/logo.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/src/features/articles/components/ArticleForm.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import cuid from 'cuid';
3 | import { Form, FormikProps, Field, withFormik, ErrorMessage } from 'formik';
4 | import { Article } from 'MyModels';
5 | import { compose } from 'redux';
6 | import { connect } from 'react-redux';
7 | import { push } from 'connected-react-router';
8 |
9 | import { createArticleAsync, updateArticleAsync } from '../actions';
10 | // import { getPath } from '../../../router-paths';
11 |
12 | type FormValues = Pick & {};
13 |
14 | const dispatchProps = {
15 | createArticle: (values: FormValues) =>
16 | createArticleAsync.request({
17 | id: cuid(),
18 | ...values,
19 | }),
20 | updateArticle: (values: Article) =>
21 | updateArticleAsync.request({
22 | ...values,
23 | }),
24 | redirectToListing: () => push('/'),
25 | };
26 |
27 | type Props = typeof dispatchProps & {
28 | article?: Article;
29 | };
30 |
31 | const InnerForm: React.FC> = props => {
32 | const { isSubmitting, dirty } = props;
33 | return (
34 |
66 | );
67 | };
68 |
69 | export default compose(
70 | connect(
71 | null,
72 | dispatchProps
73 | ),
74 | withFormik({
75 | enableReinitialize: true,
76 | // initialize values
77 | mapPropsToValues: ({ article: data }) => ({
78 | title: (data && data.title) || '',
79 | content: (data && data.content) || '',
80 | }),
81 | handleSubmit: (values, form) => {
82 | if (form.props.article != null) {
83 | form.props.updateArticle({ ...form.props.article, ...values });
84 | } else {
85 | form.props.createArticle(values);
86 | }
87 |
88 | form.props.redirectToListing();
89 | form.setSubmitting(false);
90 | },
91 | })
92 | )(InnerForm);
93 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-redux-typescript-realworld-app",
3 | "description": "RealWorld App implementation based on \"react-redux-typescript-guide\"",
4 | "version": "0.1.0",
5 | "private": true,
6 | "author": "Piotr Witek (http://piotrwitek.github.io/)",
7 | "repository": "https://github.com/piotrwitek/react-redux-typescript-realworld-app.git",
8 | "homepage": "https://react-redux-typescript-realworld-app.netlify.com/",
9 | "license": "MIT",
10 | "main": "src/index.tsx",
11 | "scripts": {
12 | "start:client": "react-scripts start",
13 | "start:lambdas": "netlify-lambda serve lambdas/src",
14 | "start": "concurrently 'npm run start:client' 'npm run start:lambdas'",
15 | "build:client": "react-scripts build",
16 | "build:lambdas": "netlify-lambda build lambdas/src",
17 | "build": "concurrently 'npm run build:client' 'npm run build:lambdas'",
18 | "test": "react-scripts test",
19 | "eject": "react-scripts eject",
20 | "reinstall": "rm -rf ./node_modules && npm install",
21 | "ci-check": "npm run prettier && npm run tsc && npm run test",
22 | "prettier": "prettier --list-different 'src/**/*.ts' || (echo '\nPlease run the following command to fix:\nnpm run prettier:fix\n'; exit 1)",
23 | "prettier:fix": "prettier --write 'src/**/*.ts'",
24 | "tsc": "tsc -p ./ --noEmit",
25 | "tsc:watch": "tsc -p ./ --noEmit -w",
26 | "deploy": "openode deploy"
27 | },
28 | "dependencies": {
29 | "@babel/polyfill": "7.4.3",
30 | "@emotion/core": "10.0.10",
31 | "@emotion/styled": "10.0.10",
32 | "@types/aws-lambda": "8.10.24",
33 | "@types/jest": "24.0.11",
34 | "@types/node": "11.13.7",
35 | "@types/prop-types": "15.7.1",
36 | "@types/react": "16.8.14",
37 | "@types/react-dom": "16.8.4",
38 | "@types/react-redux": "7.0.8",
39 | "@types/react-router-dom": "4.3.2",
40 | "axios": "0.18.0",
41 | "connected-react-router": "6.4.0",
42 | "cuid": "2.1.6",
43 | "fast-deep-equal": "2.0.1",
44 | "formik": "1.5.2",
45 | "netlify-lambda": "1.4.5",
46 | "prettier": "1.17.0",
47 | "prop-types": "15.7.2",
48 | "react": "16.8.6",
49 | "react-dom": "16.8.6",
50 | "react-redux": "7.0.2",
51 | "react-redux-typescript-scripts": "1.5.0",
52 | "react-router-dom": "5.0.0",
53 | "react-scripts": "3.0.0",
54 | "react-testing-library": "6.1.2",
55 | "react-toastify": "5.1.0",
56 | "redux": "4.0.1",
57 | "redux-observable": "1.1.0",
58 | "reselect": "4.0.0",
59 | "rxjs": "6.5.1",
60 | "tslib": "1.9.3",
61 | "typesafe-actions": "4.1.2",
62 | "typescript": "3.4.5",
63 | "utility-types": "3.5.0",
64 | "yup": "0.27.0"
65 | },
66 | "browserslist": {
67 | "production": [
68 | ">0.2%",
69 | "not dead",
70 | "not op_mini all"
71 | ],
72 | "development": [
73 | "last 1 chrome version",
74 | "last 1 firefox version",
75 | "last 1 safari version"
76 | ]
77 | },
78 | "devDependencies": {
79 | "@types/yup": "0.26.12",
80 | "concurrently": "4.1.0"
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, sex characteristics, gender identity and expression,
9 | level of experience, education, socio-economic status, nationality, personal
10 | appearance, race, religion, or sexual identity and orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at piotrek.witek@gmail.com. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
72 |
73 | [homepage]: https://www.contributor-covenant.org
74 |
75 | For answers to common questions about this code of conduct, see
76 | https://www.contributor-covenant.org/faq
77 |
--------------------------------------------------------------------------------
/src/serviceWorker.ts:
--------------------------------------------------------------------------------
1 | // This optional code is used to register a service worker.
2 | // register() is not called by default.
3 |
4 | // This lets the app load faster on subsequent visits in production, and gives
5 | // it offline capabilities. However, it also means that developers (and users)
6 | // will only see deployed updates on subsequent visits to a page, after all the
7 | // existing tabs open on the page have been closed, since previously cached
8 | // resources are updated in the background.
9 |
10 | // To learn more about the benefits of this model and instructions on how to
11 | // opt-in, read https://bit.ly/CRA-PWA
12 |
13 | const isLocalhost = Boolean(
14 | window.location.hostname === 'localhost' ||
15 | // [::1] is the IPv6 localhost address.
16 | window.location.hostname === '[::1]' ||
17 | // 127.0.0.1/8 is considered localhost for IPv4.
18 | window.location.hostname.match(
19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
20 | )
21 | );
22 |
23 | type Config = {
24 | onSuccess?: (registration: ServiceWorkerRegistration) => void;
25 | onUpdate?: (registration: ServiceWorkerRegistration) => void;
26 | };
27 |
28 | export function register(config?: Config) {
29 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
30 | // The URL constructor is available in all browsers that support SW.
31 | const publicUrl = new URL(
32 | (process as { env: { [key: string]: string } }).env.PUBLIC_URL,
33 | window.location.href
34 | );
35 | if (publicUrl.origin !== window.location.origin) {
36 | // Our service worker won't work if PUBLIC_URL is on a different origin
37 | // from what our page is served on. This might happen if a CDN is used to
38 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374
39 | return;
40 | }
41 |
42 | window.addEventListener('load', () => {
43 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
44 |
45 | if (isLocalhost) {
46 | // This is running on localhost. Let's check if a service worker still exists or not.
47 | checkValidServiceWorker(swUrl, config);
48 |
49 | // Add some additional logging to localhost, pointing developers to the
50 | // service worker/PWA documentation.
51 | navigator.serviceWorker.ready.then(() => {
52 | console.log(
53 | 'This web app is being served cache-first by a service ' +
54 | 'worker. To learn more, visit https://bit.ly/CRA-PWA'
55 | );
56 | });
57 | } else {
58 | // Is not localhost. Just register service worker
59 | registerValidSW(swUrl, config);
60 | }
61 | });
62 | }
63 | }
64 |
65 | function registerValidSW(swUrl: string, config?: Config) {
66 | navigator.serviceWorker
67 | .register(swUrl)
68 | .then(registration => {
69 | registration.onupdatefound = () => {
70 | const installingWorker = registration.installing;
71 | if (installingWorker == null) {
72 | return;
73 | }
74 | installingWorker.onstatechange = () => {
75 | if (installingWorker.state === 'installed') {
76 | if (navigator.serviceWorker.controller) {
77 | // At this point, the updated precached content has been fetched,
78 | // but the previous service worker will still serve the older
79 | // content until all client tabs are closed.
80 | console.log(
81 | 'New content is available and will be used when all ' +
82 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
83 | );
84 |
85 | // Execute callback
86 | if (config && config.onUpdate) {
87 | config.onUpdate(registration);
88 | }
89 | } else {
90 | // At this point, everything has been precached.
91 | // It's the perfect time to display a
92 | // "Content is cached for offline use." message.
93 | console.log('Content is cached for offline use.');
94 |
95 | // Execute callback
96 | if (config && config.onSuccess) {
97 | config.onSuccess(registration);
98 | }
99 | }
100 | }
101 | };
102 | };
103 | })
104 | .catch(error => {
105 | console.error('Error during service worker registration:', error);
106 | });
107 | }
108 |
109 | function checkValidServiceWorker(swUrl: string, config?: Config) {
110 | // Check if the service worker can be found. If it can't reload the page.
111 | fetch(swUrl)
112 | .then(response => {
113 | // Ensure service worker exists, and that we really are getting a JS file.
114 | const contentType = response.headers.get('content-type');
115 | if (
116 | response.status === 404 ||
117 | (contentType != null && contentType.indexOf('javascript') === -1)
118 | ) {
119 | // No service worker found. Probably a different app. Reload the page.
120 | navigator.serviceWorker.ready.then(registration => {
121 | registration.unregister().then(() => {
122 | window.location.reload();
123 | });
124 | });
125 | } else {
126 | // Service worker found. Proceed as normal.
127 | registerValidSW(swUrl, config);
128 | }
129 | })
130 | .catch(() => {
131 | console.log(
132 | 'No internet connection found. App is running in offline mode.'
133 | );
134 | });
135 | }
136 |
137 | export function unregister() {
138 | if ('serviceWorker' in navigator) {
139 | navigator.serviceWorker.ready.then(registration => {
140 | registration.unregister();
141 | });
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/lambdas/build/hello.js:
--------------------------------------------------------------------------------
1 | !function(t,r){for(var e in r)t[e]=r[e]}(exports,function(t){var r={};function e(n){if(r[n])return r[n].exports;var o=r[n]={i:n,l:!1,exports:{}};return t[n].call(o.exports,o,o.exports,e),o.l=!0,o.exports}return e.m=t,e.c=r,e.d=function(t,r,n){e.o(t,r)||Object.defineProperty(t,r,{enumerable:!0,get:n})},e.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},e.t=function(t,r){if(1&r&&(t=e(t)),8&r)return t;if(4&r&&"object"==typeof t&&t&&t.__esModule)return t;var n=Object.create(null);if(e.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:t}),2&r&&"string"!=typeof t)for(var o in t)e.d(n,o,function(r){return t[r]}.bind(null,o));return n},e.n=function(t){var r=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(r,"a",r),r},e.o=function(t,r){return Object.prototype.hasOwnProperty.call(t,r)},e.p="",e(e.s=3)}([function(t,r,e){t.exports=e(4)},function(t,r){function e(t,r,e,n,o,i,a){try{var u=t[i](a),c=u.value}catch(t){return void e(t)}u.done?r(c):Promise.resolve(c).then(n,o)}t.exports=function(t){return function(){var r=this,n=arguments;return new Promise(function(o,i){var a=t.apply(r,n);function u(t){e(a,o,i,u,c,"next",t)}function c(t){e(a,o,i,u,c,"throw",t)}u(void 0)})}}},function(t,r){t.exports=require("querystring")},function(t,r,e){"use strict";e.r(r),e.d(r,"handler",function(){return f});var n=e(0),o=e.n(n),i=e(1),a=e.n(i),u=e(2),c=e.n(u),f=function(){var t=a()(o.a.mark(function t(r,e){var n,i;return o.a.wrap(function(t){for(;;)switch(t.prev=t.next){case 0:t.t0=r.httpMethod,t.next="GET"===t.t0?3:"POST"===t.t0?4:7;break;case 3:return t.abrupt("return",{statusCode:200,body:"Hello, World!"});case 4:return n=c.a.parse(r.body),i=n.name||"World!",t.abrupt("return",{statusCode:200,body:"Hello, ".concat(i)});case 7:return t.abrupt("return",{statusCode:405,body:"Method Not Allowed"});case 8:case"end":return t.stop()}},t)}));return function(r,e){return t.apply(this,arguments)}}()},function(t,r,e){var n=function(t){"use strict";var r,e=Object.prototype,n=e.hasOwnProperty,o="function"==typeof Symbol?Symbol:{},i=o.iterator||"@@iterator",a=o.asyncIterator||"@@asyncIterator",u=o.toStringTag||"@@toStringTag";function c(t,r,e,n){var o=r&&r.prototype instanceof d?r:d,i=Object.create(o.prototype),a=new P(n||[]);return i._invoke=function(t,r,e){var n=s;return function(o,i){if(n===h)throw new Error("Generator is already running");if(n===p){if("throw"===o)throw i;return k()}for(e.method=o,e.arg=i;;){var a=e.delegate;if(a){var u=_(a,e);if(u){if(u===y)continue;return u}}if("next"===e.method)e.sent=e._sent=e.arg;else if("throw"===e.method){if(n===s)throw n=p,e.arg;e.dispatchException(e.arg)}else"return"===e.method&&e.abrupt("return",e.arg);n=h;var c=f(t,r,e);if("normal"===c.type){if(n=e.done?p:l,c.arg===y)continue;return{value:c.arg,done:e.done}}"throw"===c.type&&(n=p,e.method="throw",e.arg=c.arg)}}}(t,e,a),i}function f(t,r,e){try{return{type:"normal",arg:t.call(r,e)}}catch(t){return{type:"throw",arg:t}}}t.wrap=c;var s="suspendedStart",l="suspendedYield",h="executing",p="completed",y={};function d(){}function v(){}function g(){}var m={};m[i]=function(){return this};var w=Object.getPrototypeOf,b=w&&w(w(S([])));b&&b!==e&&n.call(b,i)&&(m=b);var x=g.prototype=d.prototype=Object.create(m);function L(t){["next","throw","return"].forEach(function(r){t[r]=function(t){return this._invoke(r,t)}})}function E(t){var r;this._invoke=function(e,o){function i(){return new Promise(function(r,i){!function r(e,o,i,a){var u=f(t[e],t,o);if("throw"!==u.type){var c=u.arg,s=c.value;return s&&"object"==typeof s&&n.call(s,"__await")?Promise.resolve(s.__await).then(function(t){r("next",t,i,a)},function(t){r("throw",t,i,a)}):Promise.resolve(s).then(function(t){c.value=t,i(c)},function(t){return r("throw",t,i,a)})}a(u.arg)}(e,o,r,i)})}return r=r?r.then(i,i):i()}}function _(t,e){var n=t.iterator[e.method];if(n===r){if(e.delegate=null,"throw"===e.method){if(t.iterator.return&&(e.method="return",e.arg=r,_(t,e),"throw"===e.method))return y;e.method="throw",e.arg=new TypeError("The iterator does not provide a 'throw' method")}return y}var o=f(n,t.iterator,e.arg);if("throw"===o.type)return e.method="throw",e.arg=o.arg,e.delegate=null,y;var i=o.arg;return i?i.done?(e[t.resultName]=i.value,e.next=t.nextLoc,"return"!==e.method&&(e.method="next",e.arg=r),e.delegate=null,y):i:(e.method="throw",e.arg=new TypeError("iterator result is not an object"),e.delegate=null,y)}function O(t){var r={tryLoc:t[0]};1 in t&&(r.catchLoc=t[1]),2 in t&&(r.finallyLoc=t[2],r.afterLoc=t[3]),this.tryEntries.push(r)}function j(t){var r=t.completion||{};r.type="normal",delete r.arg,t.completion=r}function P(t){this.tryEntries=[{tryLoc:"root"}],t.forEach(O,this),this.reset(!0)}function S(t){if(t){var e=t[i];if(e)return e.call(t);if("function"==typeof t.next)return t;if(!isNaN(t.length)){var o=-1,a=function e(){for(;++o=0;--i){var a=this.tryEntries[i],u=a.completion;if("root"===a.tryLoc)return o("end");if(a.tryLoc<=this.prev){var c=n.call(a,"catchLoc"),f=n.call(a,"finallyLoc");if(c&&f){if(this.prev=0;--e){var o=this.tryEntries[e];if(o.tryLoc<=this.prev&&n.call(o,"finallyLoc")&&this.prev=0;--r){var e=this.tryEntries[r];if(e.finallyLoc===t)return this.complete(e.completion,e.afterLoc),j(e),y}},catch:function(t){for(var r=this.tryEntries.length-1;r>=0;--r){var e=this.tryEntries[r];if(e.tryLoc===t){var n=e.completion;if("throw"===n.type){var o=n.arg;j(e)}return o}}throw new Error("illegal catch attempt")},delegateYield:function(t,e,n){return this.delegate={iterator:S(t),resultName:e,nextLoc:n},"next"===this.method&&(this.arg=r),y}},t}(t.exports);try{regeneratorRuntime=n}catch(t){Function("r","regeneratorRuntime = r")(n)}}]));
--------------------------------------------------------------------------------