├── .babelrc ├── .editorconfig ├── .github ├── FUNDING.yml └── workflows │ └── build-and-test.yml ├── .gitignore ├── .husky └── pre-commit ├── .prettierignore ├── .prettierrc ├── .vscode └── settings.json ├── .yarn ├── patches │ └── react-scripts-npm-5.0.1-d06bd2d5ad.patch └── releases │ └── yarn-3.2.1.cjs ├── .yarnrc.yml ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── examples ├── 1-getting-started │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── public │ │ └── index.html │ ├── src │ │ ├── app │ │ │ ├── Home.tsx │ │ │ ├── components │ │ │ │ ├── Error.tsx │ │ │ │ ├── Loading.tsx │ │ │ │ ├── ServerString.tsx │ │ │ │ ├── Todo.tsx │ │ │ │ └── index.ts │ │ │ ├── index.css │ │ │ ├── index.tsx │ │ │ └── models │ │ │ │ ├── ModelBase.ts │ │ │ │ ├── RootStore.base.ts │ │ │ │ ├── RootStore.ts │ │ │ │ ├── TodoModel.base.ts │ │ │ │ ├── TodoModel.ts │ │ │ │ ├── index.ts │ │ │ │ └── reactUtils.ts │ │ └── server │ │ │ ├── index.js │ │ │ └── schema.js │ └── tsconfig.json ├── 2-scaffolding │ ├── README.md │ ├── graphql-schema.json │ ├── mst-gql.config.js │ ├── package.json │ ├── src │ │ └── models │ │ │ ├── AttackModel.base.ts │ │ │ ├── AttackModel.ts │ │ │ ├── ModelBase.ts │ │ │ ├── PokemonAttackModel.base.ts │ │ │ ├── PokemonAttackModel.ts │ │ │ ├── PokemonDimensionModel.base.ts │ │ │ ├── PokemonDimensionModel.ts │ │ │ ├── PokemonEvolutionRequirementModel.base.ts │ │ │ ├── PokemonEvolutionRequirementModel.ts │ │ │ ├── PokemonModel.base.ts │ │ │ ├── PokemonModel.ts │ │ │ ├── RootStore.base.ts │ │ │ ├── RootStore.ts │ │ │ ├── index.ts │ │ │ └── reactUtils.ts │ └── tsconfig.json ├── 3-twitter-clone │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── public │ │ └── index.html │ └── src │ │ ├── app │ │ ├── avatars │ │ │ ├── chuck.jpg │ │ │ └── michel.jpg │ │ ├── components │ │ │ ├── Composer.tsx │ │ │ ├── Home.tsx │ │ │ ├── Message.tsx │ │ │ ├── MessageWall.tsx │ │ │ ├── Profile.tsx │ │ │ ├── Replies.tsx │ │ │ └── utils │ │ │ │ ├── Error.tsx │ │ │ │ ├── Loading.tsx │ │ │ │ ├── index.ts │ │ │ │ └── spinner.gif │ │ ├── index.css │ │ ├── index.tsx │ │ ├── models │ │ │ ├── BaseIDModelSelector.ts │ │ │ ├── MessageModel.base.ts │ │ │ ├── MessageModel.ts │ │ │ ├── ModelBase.ts │ │ │ ├── RootStore.base.ts │ │ │ ├── RootStore.ts │ │ │ ├── SearchResultModelSelector.ts │ │ │ ├── UserModel.base.ts │ │ │ ├── UserModel.ts │ │ │ ├── index.ts │ │ │ └── reactUtils.ts │ │ └── tsconfig.json │ │ └── server │ │ ├── db │ │ └── initial.json │ │ ├── index.ts │ │ ├── models │ │ ├── MessageModel.base.ts │ │ ├── MessageModel.ts │ │ ├── ModelBase.ts │ │ ├── ReplyModel.base.ts │ │ ├── ReplyModel.ts │ │ ├── RootStore.base.ts │ │ ├── RootStore.ts │ │ ├── UserModel.base.ts │ │ ├── UserModel.ts │ │ ├── index.ts │ │ └── reactUtils.ts │ │ ├── schema.graphql │ │ ├── schema.ts │ │ └── tsconfig.json ├── 4-apollo-tutorial │ ├── README.md │ ├── client │ │ ├── .env │ │ ├── .env.example │ │ ├── .gitignore │ │ ├── README.md │ │ ├── apollo.config.js │ │ ├── demo_schema.json │ │ ├── package.json │ │ ├── public │ │ │ ├── favicon.ico │ │ │ ├── index.html │ │ │ └── manifest.json │ │ └── src │ │ │ ├── assets │ │ │ ├── curve.svg │ │ │ ├── icons │ │ │ │ ├── cart.svg │ │ │ │ ├── exit.svg │ │ │ │ ├── home.svg │ │ │ │ └── profile.svg │ │ │ ├── images │ │ │ │ ├── badge-1.png │ │ │ │ ├── badge-2.png │ │ │ │ ├── badge-3.png │ │ │ │ ├── dog-1.png │ │ │ │ ├── dog-2.png │ │ │ │ ├── dog-3.png │ │ │ │ ├── galaxy.jpg │ │ │ │ ├── iss.jpg │ │ │ │ ├── moon.jpg │ │ │ │ └── space.jpg │ │ │ ├── logo.svg │ │ │ └── rocket.svg │ │ │ ├── components │ │ │ ├── __tests__ │ │ │ │ ├── button.js │ │ │ │ ├── footer.js │ │ │ │ ├── header.js │ │ │ │ ├── launch-detail.js │ │ │ │ ├── launch-tile.js │ │ │ │ ├── loading.js │ │ │ │ ├── login-form.js │ │ │ │ ├── menu-item.js │ │ │ │ └── page-container.js │ │ │ ├── button.js │ │ │ ├── footer.js │ │ │ ├── header.js │ │ │ ├── index.js │ │ │ ├── launch-detail.js │ │ │ ├── launch-tile.js │ │ │ ├── loading.js │ │ │ ├── login-form.js │ │ │ ├── menu-item.js │ │ │ └── page-container.js │ │ │ ├── containers │ │ │ ├── __tests__ │ │ │ │ ├── action-button.js │ │ │ │ ├── book-trips.js │ │ │ │ ├── cart-item.js │ │ │ │ └── logout-button.js │ │ │ ├── action-button.js │ │ │ ├── book-trips.js │ │ │ ├── cart-item.js │ │ │ ├── index.js │ │ │ └── logout-button.js │ │ │ ├── index.js │ │ │ ├── models │ │ │ ├── LaunchConnectionModel.base.js │ │ │ ├── LaunchConnectionModel.js │ │ │ ├── LaunchModel.base.js │ │ │ ├── LaunchModel.js │ │ │ ├── MissionModel.base.js │ │ │ ├── MissionModel.js │ │ │ ├── ModelBase.js │ │ │ ├── PatchSizeEnum.js │ │ │ ├── RocketModel.base.js │ │ │ ├── RocketModel.js │ │ │ ├── RootStore.base.js │ │ │ ├── RootStore.js │ │ │ ├── UserModel.base.js │ │ │ ├── UserModel.js │ │ │ ├── index.js │ │ │ └── reactUtils.js │ │ │ ├── pages │ │ │ ├── __tests__ │ │ │ │ ├── cart.js │ │ │ │ ├── launch.js │ │ │ │ ├── launches.js │ │ │ │ ├── login.js │ │ │ │ └── profile.js │ │ │ ├── cart.js │ │ │ ├── index.js │ │ │ ├── launch.js │ │ │ ├── launches.js │ │ │ ├── login.js │ │ │ └── profile.js │ │ │ ├── styles.js │ │ │ └── test-utils.js │ └── server │ │ ├── .env.example │ │ ├── apollo.config.js │ │ ├── now.json │ │ ├── package.json │ │ ├── src │ │ ├── __tests__ │ │ │ ├── __snapshots__ │ │ │ │ ├── e2e.js.snap │ │ │ │ └── integration.js.snap │ │ │ ├── __utils.js │ │ │ ├── e2e.js │ │ │ ├── integration.js │ │ │ ├── resolvers.mission.js │ │ │ ├── resolvers.mutation.js │ │ │ ├── resolvers.query.js │ │ │ └── resolvers.user.js │ │ ├── datasources │ │ │ ├── __tests__ │ │ │ │ ├── launch.js │ │ │ │ └── user.js │ │ │ ├── launch.js │ │ │ └── user.js │ │ ├── engine-demo.js │ │ ├── index.js │ │ ├── resolvers.js │ │ ├── schema.js │ │ └── utils.js │ │ └── store.sqlite ├── 5-nextjs │ ├── README.md │ ├── components │ │ ├── todos.tsx │ │ └── users.tsx │ ├── mst-gql.config.js │ ├── next-env.d.ts │ ├── package.json │ ├── pages │ │ ├── _app.tsx │ │ ├── index.tsx │ │ └── other.tsx │ ├── server │ │ ├── index.js │ │ ├── schema.js │ │ └── store.js │ ├── src │ │ └── models │ │ │ ├── ModelBase.ts │ │ │ ├── RootStore.base.ts │ │ │ ├── RootStore.ts │ │ │ ├── TodoModel.base.ts │ │ │ ├── TodoModel.ts │ │ │ ├── UserModel.base.ts │ │ │ ├── UserModel.ts │ │ │ ├── index.ts │ │ │ └── reactUtils.ts │ └── tsconfig.json └── 6-scaffolding-ts-hasura │ ├── README.md │ ├── mst-gql.config.js │ ├── package.json │ ├── schema.graphql │ ├── src │ └── models │ │ ├── ChoicesAggregateFieldsModel.base.ts │ │ ├── ChoicesAggregateFieldsModel.ts │ │ ├── ChoicesAggregateModel.base.ts │ │ ├── ChoicesAggregateModel.ts │ │ ├── ChoicesAvgFieldsModel.base.ts │ │ ├── ChoicesAvgFieldsModel.ts │ │ ├── ChoicesConstraintEnum.ts │ │ ├── ChoicesMaxFieldsModel.base.ts │ │ ├── ChoicesMaxFieldsModel.ts │ │ ├── ChoicesMinFieldsModel.base.ts │ │ ├── ChoicesMinFieldsModel.ts │ │ ├── ChoicesModel.base.ts │ │ ├── ChoicesModel.ts │ │ ├── ChoicesMutationResponseModel.base.ts │ │ ├── ChoicesMutationResponseModel.ts │ │ ├── ChoicesSelectColumnEnum.ts │ │ ├── ChoicesStddevFieldsModel.base.ts │ │ ├── ChoicesStddevFieldsModel.ts │ │ ├── ChoicesStddevPopFieldsModel.base.ts │ │ ├── ChoicesStddevPopFieldsModel.ts │ │ ├── ChoicesStddevSampFieldsModel.base.ts │ │ ├── ChoicesStddevSampFieldsModel.ts │ │ ├── ChoicesSumFieldsModel.base.ts │ │ ├── ChoicesSumFieldsModel.ts │ │ ├── ChoicesUpdateColumnEnum.ts │ │ ├── ChoicesVarPopFieldsModel.base.ts │ │ ├── ChoicesVarPopFieldsModel.ts │ │ ├── ChoicesVarSampFieldsModel.base.ts │ │ ├── ChoicesVarSampFieldsModel.ts │ │ ├── ChoicesVarianceFieldsModel.base.ts │ │ ├── ChoicesVarianceFieldsModel.ts │ │ ├── ConflictActionEnum.ts │ │ ├── ModelBase.ts │ │ ├── MutationRootModel.base.ts │ │ ├── MutationRootModel.ts │ │ ├── OrderByEnum.ts │ │ ├── PollsAggregateFieldsModel.base.ts │ │ ├── PollsAggregateFieldsModel.ts │ │ ├── PollsAggregateModel.base.ts │ │ ├── PollsAggregateModel.ts │ │ ├── PollsAvgFieldsModel.base.ts │ │ ├── PollsAvgFieldsModel.ts │ │ ├── PollsConstraintEnum.ts │ │ ├── PollsMaxFieldsModel.base.ts │ │ ├── PollsMaxFieldsModel.ts │ │ ├── PollsMinFieldsModel.base.ts │ │ ├── PollsMinFieldsModel.ts │ │ ├── PollsModel.base.ts │ │ ├── PollsModel.ts │ │ ├── PollsMutationResponseModel.base.ts │ │ ├── PollsMutationResponseModel.ts │ │ ├── PollsSelectColumnEnum.ts │ │ ├── PollsStddevFieldsModel.base.ts │ │ ├── PollsStddevFieldsModel.ts │ │ ├── PollsStddevPopFieldsModel.base.ts │ │ ├── PollsStddevPopFieldsModel.ts │ │ ├── PollsStddevSampFieldsModel.base.ts │ │ ├── PollsStddevSampFieldsModel.ts │ │ ├── PollsSumFieldsModel.base.ts │ │ ├── PollsSumFieldsModel.ts │ │ ├── PollsUpdateColumnEnum.ts │ │ ├── PollsVarPopFieldsModel.base.ts │ │ ├── PollsVarPopFieldsModel.ts │ │ ├── PollsVarSampFieldsModel.base.ts │ │ ├── PollsVarSampFieldsModel.ts │ │ ├── PollsVarianceFieldsModel.base.ts │ │ ├── PollsVarianceFieldsModel.ts │ │ ├── QueryRootModel.base.ts │ │ ├── QueryRootModel.ts │ │ ├── RootStore.base.ts │ │ ├── RootStore.ts │ │ ├── SubscriptionRootModel.base.ts │ │ ├── SubscriptionRootModel.ts │ │ ├── index.ts │ │ └── reactUtils.ts │ └── tsconfig.json ├── generator ├── config.js ├── generate.js ├── logger.js ├── mst-gql-scaffold.js └── utilities.js ├── jest.config.js ├── package.json ├── src ├── MSTGQLObject.ts ├── MSTGQLStore.ts ├── Query.ts ├── createHttpClient.ts ├── deflateHelper.ts ├── localStorageMixin.ts ├── mergeHelper.ts ├── mst-gql.ts ├── queryBuilder.ts ├── react.tsx ├── utils.ts └── withTypedRefs.ts ├── tests ├── config.test.js ├── cra-test │ ├── .yarnrc.yml │ ├── package.json │ └── yarn.lock ├── generator │ ├── __snapshots__ │ │ └── generate.test.js.snap │ └── generate.test.js ├── lib │ ├── abstractTypes │ │ ├── abstractTypes.test.js │ │ ├── models │ │ │ ├── BookModel.base.js │ │ │ ├── BookModel.js │ │ │ ├── ModelBase.js │ │ │ ├── MovieModel.base.js │ │ │ ├── MovieModel.js │ │ │ ├── OrganizationModel.base.js │ │ │ ├── OrganizationModel.js │ │ │ ├── OwnerModelSelector.js │ │ │ ├── RepoModel.base.js │ │ │ ├── RepoModel.js │ │ │ ├── RootStore.base.js │ │ │ ├── RootStore.js │ │ │ ├── SearchItemModelSelector.js │ │ │ ├── SearchResultModel.base.js │ │ │ ├── SearchResultModel.js │ │ │ ├── UserModel.base.js │ │ │ ├── UserModel.js │ │ │ ├── index.js │ │ │ └── reactUtils.js │ │ └── schema.graphql │ ├── todos │ │ ├── __snapshots__ │ │ │ └── todostore.test.js.snap │ │ ├── models │ │ │ ├── ModelBase.js │ │ │ ├── RootStore.base.js │ │ │ ├── RootStore.js │ │ │ ├── TodoModel.base.js │ │ │ ├── TodoModel.js │ │ │ ├── index.js │ │ │ └── reactUtils.js │ │ ├── todos.graphql │ │ └── todostore.test.js │ └── unionTypes │ │ ├── models │ │ ├── BasicTodoModel.base.js │ │ ├── BasicTodoModel.js │ │ ├── FancyTodoModel.base.js │ │ ├── FancyTodoModel.js │ │ ├── ModelBase.js │ │ ├── RootStore.base.js │ │ ├── RootStore.js │ │ ├── TodoListModel.base.js │ │ ├── TodoListModel.js │ │ ├── TodoModelSelector.js │ │ ├── index.js │ │ └── reactUtils.js │ │ ├── todos.graphql │ │ └── todostore.test.js └── vite-app-test │ └── test-vite-app.sh ├── tsconfig.json └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["@babel/preset-env", { "targets": { "node": "current" } }], 4 | "@babel/preset-typescript" 5 | ], 6 | "plugins": [ 7 | ["@babel/plugin-proposal-decorators", { "legacy": true }], 8 | ["@babel/plugin-proposal-class-properties", { "loose": true }], 9 | ["@babel/plugin-syntax-jsx", {}] 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | 7 | [*.{js,json,yml}] 8 | charset = utf-8 9 | indent_style = space 10 | indent_size = 2 11 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | #github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | #patreon: # Replace with a single Patreon username 5 | #open_collective: # Replace with a single Open Collective username 6 | #ko_fi: # Replace with a single Ko-fi username 7 | #tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | #community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | #liberapay: # Replace with a single Liberapay username 10 | #issuehunt: # Replace with a single IssueHunt username 11 | #otechie: # Replace with a single Otechie username 12 | #custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.github/workflows/build-and-test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | on: 3 | pull_request: {} 4 | workflow_dispatch: 5 | 6 | jobs: 7 | test: 8 | name: Tests 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout Source Code 12 | uses: actions/checkout@v4 13 | with: 14 | fetch-depth: 0 15 | lfs: true 16 | 17 | - name: Install Node.js 18 | uses: actions/setup-node@v4 19 | with: 20 | node-version: '18' 21 | cache: 'yarn' 22 | 23 | - name: Install Dependencies 24 | run: yarn install --immutable 25 | 26 | - name: Check Formatting 27 | run: yarn format --check 28 | 29 | - name: Build mst-gql 30 | run: yarn build 31 | 32 | - name: Test mst-gql 33 | run: yarn test:mst-gql 34 | 35 | - name: Test Example 2 (Scaffolding) 36 | run: yarn test:example-2 37 | 38 | - name: Test Example 6 (Hasura) 39 | run: yarn test:example-6 40 | 41 | # - name: Test e2e vite app 42 | # run: yarn test:vite 43 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | yarn pretty-quick --staged 2 | yarn syncpack format 3 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # ignore a wide range of support files 2 | .* 3 | 4 | # build files 5 | dist/ 6 | 7 | # test files 8 | __snapshots__ 9 | tests/test-cra.sh 10 | *.sqlite 11 | 12 | # mst-gql generated files 13 | **/models/index.* 14 | **/models/reactUtils.* 15 | **/models/*.base.* 16 | **/models/*Enum.* 17 | **/models/*ModelSelector.* 18 | 19 | # yarn 20 | yarn.lock 21 | yarn-error.log 22 | 23 | # images 24 | *.png 25 | *.jpg 26 | *.svg 27 | *.gif 28 | *.ico 29 | 30 | # etc 31 | LICENSE 32 | *.txt 33 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "bracketSpacing": true, 3 | "printWidth": 80, 4 | "proseWrap": "never", 5 | "requirePragma": false, 6 | "semi": false, 7 | "singleQuote": false, 8 | "tabWidth": 2, 9 | "trailingComma": "none", 10 | "useTabs": false, 11 | "overrides": [ 12 | { 13 | "files": [".prettierrc", "*.json"], 14 | "options": { 15 | "printWidth": 200, 16 | "tabWidth": 2 17 | } 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.tabSize": 2, 3 | "eslint.enable": false, 4 | "cSpell.enabled": false, 5 | "typescript.tsdk": "node_modules/typescript/lib" 6 | } 7 | -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | defaultSemverRangePrefix: "" 2 | 3 | nodeLinker: node-modules 4 | 5 | npmPublishRegistry: "https://registry.yarnpkg.com" 6 | 7 | yarnPath: .yarn/releases/yarn-3.2.1.cjs 8 | 9 | enableGlobalCache: true 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 MobX 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 | -------------------------------------------------------------------------------- /examples/1-getting-started/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .build 3 | lib 4 | .cache -------------------------------------------------------------------------------- /examples/1-getting-started/README.md: -------------------------------------------------------------------------------- 1 | ## About 2 | 3 | This example is "borrowed" from the awesome urql repository [here](https://github.com/FormidableLabs/urql/tree/master/examples/1-getting-started). 4 | 5 | This example demonstrates a simple mst-gql project (without scaffolding set up) 6 | 7 | ## Usage 8 | 9 | #### 0. install `mst-gql` dependencies (within the parent `mst-gql` directory) 10 | 11 | ```bash 12 | yarn 13 | ``` 14 | 15 | #### 1. Install dependencies (within this example's folder) 16 | 17 | ```bash 18 | yarn 19 | ``` 20 | 21 | #### 2. Start server 22 | 23 | ```bash 24 | yarn start 25 | ``` 26 | 27 | #### 3. Open browser 28 | 29 | Navigate to example at [http://localhost:3000/](http://localhost:3000/). 30 | -------------------------------------------------------------------------------- /examples/1-getting-started/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@mst-gql/getting-started", 3 | "license": "MIT", 4 | "version": "1.0.0", 5 | "scripts": { 6 | "scaffold": "node ../../generator/mst-gql-scaffold.js --roots Todo --outDir src/app/models/ --format ts http://localhost:3001/graphql", 7 | "start": "run-p start:server start:client", 8 | "start:client": "wait-port 3001 && yarn scaffold && parcel serve -p 3000 public/index.html", 9 | "start:server": "node ./src/server/index.js", 10 | "type-check": "tsc --noEmit", 11 | "type-check:watch": "yarn type-check -- --watch" 12 | }, 13 | "dependencies": { 14 | "apollo-server-express": "^2.18.0", 15 | "arg": "^5.0.2", 16 | "cors": "^2.8.5", 17 | "express": "^4.17.1", 18 | "graphql": "15.8.0", 19 | "graphql-playground-middleware-express": "^1.7.21", 20 | "graphql-tag": "^2.12.6", 21 | "isomorphic-fetch": "^2.2.1", 22 | "jest": "^27.0.6", 23 | "mobx": "^6.10.2", 24 | "mobx-react": "^7.5.0", 25 | "mobx-state-tree": "^5.3.0", 26 | "mst-gql": "portal:../../", 27 | "react": "^18.2.0", 28 | "react-dom": "^18.2.0" 29 | }, 30 | "devDependencies": { 31 | "@babel/core": "^7.14.3", 32 | "@types/jest": "^27.5.2", 33 | "@types/react": "^18.0.15", 34 | "@types/react-dom": "^18.0.6", 35 | "npm-run-all": "^4.1.5", 36 | "parcel": "^2.6.0", 37 | "typescript": "^4.7.3", 38 | "wait-port": "^0.2.9" 39 | }, 40 | "private": true 41 | } 42 | -------------------------------------------------------------------------------- /examples/1-getting-started/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | MST-GQL Getting Started Example 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /examples/1-getting-started/src/app/Home.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { observer } from "mobx-react-lite" 3 | import { useQuery } from "./models/reactUtils" 4 | 5 | import { Error, Loading, Todo } from "./components" 6 | 7 | export const Home = observer(() => { 8 | const { loading, error, data, query } = useQuery((store) => 9 | store.queryTodos() 10 | ) 11 | if (error) return {error.message} 12 | if (data) 13 | return ( 14 | <> 15 | 20 | {loading ? ( 21 | 22 | ) : ( 23 | 24 | )} 25 | 26 | ) 27 | return 28 | }) 29 | -------------------------------------------------------------------------------- /examples/1-getting-started/src/app/components/Error.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from "react" 2 | 3 | export const Error: FC = (props) => ( 4 | <> 5 |

Error

6 |

Something went wrong

7 |

Message: {props.children}

8 | 9 | ) 10 | -------------------------------------------------------------------------------- /examples/1-getting-started/src/app/components/Loading.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | 3 | export const Loading = () =>

Loading...

4 | -------------------------------------------------------------------------------- /examples/1-getting-started/src/app/components/ServerString.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { observer } from "mobx-react-lite" 3 | import { useQuery } from "../models/reactUtils" 4 | 5 | import { Error, Loading } from "./" 6 | 7 | export const ServerString = observer(() => { 8 | const { loading, error, data, query } = useQuery((store) => 9 | store.queryStringFromServer({ string: "Todos" }) 10 | ) 11 | if (error) return {error.message} 12 | if (data) return <>{data.stringFromServer} 13 | return 14 | }) 15 | -------------------------------------------------------------------------------- /examples/1-getting-started/src/app/components/Todo.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import { observer } from "mobx-react-lite" 3 | 4 | import { TodoModelType } from "../models" 5 | import { useQuery } from "../models/reactUtils" 6 | 7 | export const Todo = observer(({ todo }: { todo: TodoModelType }) => { 8 | const { setQuery, loading, error } = useQuery(undefined) 9 | 10 | return ( 11 |
  • setQuery(todo.toggle())}> 12 |

    {todo.text}

    13 | {error && Failed to update} 14 | {loading && (updating)} 15 |
  • 16 | ) 17 | }) 18 | -------------------------------------------------------------------------------- /examples/1-getting-started/src/app/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./Error" 2 | export * from "./Loading" 3 | export * from "./Todo" 4 | export * from "./ServerString" 5 | -------------------------------------------------------------------------------- /examples/1-getting-started/src/app/index.css: -------------------------------------------------------------------------------- 1 | html { 2 | background: #488; 3 | font-family: sans-serif; 4 | } 5 | 6 | #root { 7 | width: 100%; 8 | display: flex; 9 | justify-content: center; 10 | } 11 | 12 | main { 13 | width: 400px; 14 | } 15 | 16 | h1 { 17 | color: #2d4a4a; 18 | } 19 | 20 | h4 { 21 | margin: 0; 22 | margin-bottom: 5px; 23 | } 24 | 25 | ul { 26 | list-style-type: none; 27 | padding: 0; 28 | } 29 | 30 | li { 31 | background: #fff; 32 | display: flex; 33 | justify-content: space-between; 34 | box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.2); 35 | padding: 20px; 36 | margin: 20px 0; 37 | border-radius: 4px; 38 | } 39 | 40 | li span { 41 | color: #999; 42 | } 43 | 44 | .strikethrough { 45 | text-decoration: line-through; 46 | } 47 | -------------------------------------------------------------------------------- /examples/1-getting-started/src/app/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import * as ReactDOM from "react-dom" 3 | import { createHttpClient } from "mst-gql" 4 | 5 | import "./index.css" 6 | 7 | import { RootStore } from "./models/RootStore" 8 | import { StoreContext } from "./models/reactUtils" 9 | import { Home } from "./Home" 10 | import { ServerString } from "./components" 11 | 12 | const rootStore = RootStore.create(undefined, { 13 | gqlHttpClient: createHttpClient("http://localhost:3001/graphql") 14 | }) 15 | 16 | export const App: React.FC = () => ( 17 | 18 |
    19 |

    20 | 21 |

    22 | 23 |
    24 |
    25 | ) 26 | 27 | ReactDOM.render(, document.getElementById("root")) 28 | -------------------------------------------------------------------------------- /examples/1-getting-started/src/app/models/ModelBase.ts: -------------------------------------------------------------------------------- 1 | import { MSTGQLObject } from "mst-gql" 2 | 3 | export const ModelBase = MSTGQLObject 4 | -------------------------------------------------------------------------------- /examples/1-getting-started/src/app/models/RootStore.ts: -------------------------------------------------------------------------------- 1 | import { Instance } from "mobx-state-tree" 2 | import { RootStoreBase } from "./RootStore.base" 3 | 4 | export interface RootStoreType extends Instance {} 5 | 6 | export const RootStore = RootStoreBase.actions((self) => ({ 7 | // This is an auto-generated example action. 8 | log() { 9 | console.log(JSON.stringify(self)) 10 | } 11 | })) 12 | -------------------------------------------------------------------------------- /examples/1-getting-started/src/app/models/TodoModel.base.ts: -------------------------------------------------------------------------------- 1 | /* This is a mst-gql generated file, don't modify it manually */ 2 | /* eslint-disable */ 3 | /* tslint:disable */ 4 | 5 | import { types } from "mobx-state-tree" 6 | import { QueryBuilder } from "mst-gql" 7 | import { ModelBase } from "./ModelBase" 8 | import { RootStoreType } from "./index" 9 | 10 | 11 | /** 12 | * TodoBase 13 | * auto generated base class for the model TodoModel. 14 | */ 15 | export const TodoModelBase = ModelBase 16 | .named('Todo') 17 | .props({ 18 | __typename: types.optional(types.literal("Todo"), "Todo"), 19 | id: types.identifier, 20 | text: types.union(types.undefined, types.null, types.string), 21 | complete: types.union(types.undefined, types.null, types.boolean), 22 | }) 23 | .views(self => ({ 24 | get store() { 25 | return self.__getStore() 26 | } 27 | })) 28 | 29 | export class TodoModelSelector extends QueryBuilder { 30 | get id() { return this.__attr(`id`) } 31 | get text() { return this.__attr(`text`) } 32 | get complete() { return this.__attr(`complete`) } 33 | } 34 | export function selectFromTodo() { 35 | return new TodoModelSelector() 36 | } 37 | 38 | export const todoModelPrimitives = selectFromTodo().text.complete 39 | -------------------------------------------------------------------------------- /examples/1-getting-started/src/app/models/TodoModel.ts: -------------------------------------------------------------------------------- 1 | import { Instance } from "mobx-state-tree" 2 | import { TodoModelBase } from "./TodoModel.base" 3 | import { Query } from "mst-gql" 4 | 5 | /* The TypeScript type of an instance of TodoModel */ 6 | export interface TodoModelType extends Instance {} 7 | 8 | /* A graphql query fragment builders for TodoModel */ 9 | export { 10 | selectFromTodo, 11 | todoModelPrimitives, 12 | TodoModelSelector 13 | } from "./TodoModel.base" 14 | 15 | /** 16 | * TodoModel 17 | */ 18 | export const TodoModel = TodoModelBase.actions((self) => ({ 19 | toggle(): Query<{ 20 | toggleTodo: TodoModelType 21 | }> { 22 | return self.store.mutateToggleTodo({ id: self.id }, undefined, () => { 23 | self.complete = !self.complete 24 | }) 25 | } 26 | })) 27 | -------------------------------------------------------------------------------- /examples/1-getting-started/src/app/models/index.ts: -------------------------------------------------------------------------------- 1 | /* This is a mst-gql generated file, don't modify it manually */ 2 | /* eslint-disable */ 3 | /* tslint:disable */ 4 | 5 | export * from "./TodoModel" 6 | export * from "./RootStore" 7 | export * from "./reactUtils" 8 | -------------------------------------------------------------------------------- /examples/1-getting-started/src/app/models/reactUtils.ts: -------------------------------------------------------------------------------- 1 | /* This is a mst-gql generated file, don't modify it manually */ 2 | /* eslint-disable */ 3 | /* tslint:disable */ 4 | 5 | import { createStoreContext, createUseQueryHook } from "mst-gql" 6 | import * as React from "react" 7 | import { RootStoreType } from "./RootStore" 8 | 9 | export const StoreContext = createStoreContext(React) 10 | 11 | export const useQuery = createUseQueryHook(StoreContext, React) 12 | -------------------------------------------------------------------------------- /examples/1-getting-started/src/server/index.js: -------------------------------------------------------------------------------- 1 | const { ApolloServer, graphiqlExpress } = require("apollo-server-express") 2 | const { createServer } = require("http") 3 | const { SubscriptionServer } = require("subscriptions-transport-ws") 4 | const expressPlayground = 5 | require("graphql-playground-middleware-express").default 6 | const express = require("express") 7 | const app = express() 8 | const cors = require("cors") 9 | 10 | const { typeDefs, resolvers } = require("./schema") 11 | 12 | const PORT = 3001 13 | 14 | const server = new ApolloServer({ 15 | typeDefs, 16 | resolvers 17 | }) 18 | 19 | server.applyMiddleware({ app }) 20 | 21 | const webServer = createServer(app) 22 | server.installSubscriptionHandlers(webServer) 23 | 24 | const graphqlEndpoint = `http://localhost:${PORT}${server.graphqlPath}` 25 | const subscriptionEndpoint = `ws://localhost:${PORT}${server.subscriptionsPath}` 26 | 27 | app.use(cors()) 28 | app.get( 29 | "/", 30 | expressPlayground({ 31 | endpoint: graphqlEndpoint, 32 | subscriptionEndpoint: subscriptionEndpoint 33 | }) 34 | ) 35 | 36 | webServer.listen(PORT, () => { 37 | console.log(`🚀 Server ready at ${graphqlEndpoint}`) 38 | console.log(`🚀 Subscriptions ready at ${subscriptionEndpoint}`) 39 | }) 40 | -------------------------------------------------------------------------------- /examples/1-getting-started/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "commonjs", 5 | "allowSyntheticDefaultImports": true, 6 | "esModuleInterop": true, 7 | "strict": true, 8 | "lib": ["dom", "dom.iterable", "esnext"], 9 | "jsx": "react", 10 | "noEmit": true, 11 | "moduleResolution": "node", 12 | "skipLibCheck": true 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /examples/2-scaffolding/README.md: -------------------------------------------------------------------------------- 1 | ## About 2 | 3 | ❗️Note that this example does not run a client or server, it just scaffolds the models. 4 | 5 | This example shows a minimal example of how to configure the code scaffolding, if you have some schema available somewhere. 6 | 7 | ## Usage 8 | 9 | #### 0. install `mst-gql` dependencies (within the parent `mst-gql` directory) 10 | 11 | ```bash 12 | yarn 13 | ``` 14 | 15 | #### 1. Install dependencies (within this example's folder) 16 | 17 | ```bash 18 | yarn 19 | ``` 20 | 21 | #### 2. Scaffold 22 | 23 | ```bash 24 | yarn start 25 | ``` 26 | 27 | Runs the scaffolder and uses TypeScript to compile the generated sources. 28 | 29 | ```bash 30 | yarn download 31 | ``` 32 | 33 | Re-fetches the graphql-schema from the demo endpoint (this might take a while). The schema definition is downloaded from: https://graphql-pokemon.now.sh/ 34 | -------------------------------------------------------------------------------- /examples/2-scaffolding/mst-gql.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | force: true, 3 | format: "ts", 4 | input: "graphql-schema.json", 5 | outDir: "src/models", 6 | roots: ["Pokemon", "Attack"] 7 | } 8 | -------------------------------------------------------------------------------- /examples/2-scaffolding/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@mst-gql/scaffold-test-2", 3 | "scripts": { 4 | "build": "tsc", 5 | "download": "yarn dlx gql-sdl https://graphql-pokemon.now.sh/ --output graphql-schema.gql", 6 | "scaffold": "../../generator/mst-gql-scaffold.js", 7 | "start": "yarn scaffold && yarn build" 8 | }, 9 | "dependencies": { 10 | "graphql": "^16.5.0", 11 | "graphql-request": "^4.3.0", 12 | "mobx": "^6.10.2", 13 | "mobx-state-tree": "^5.3.0", 14 | "mst-gql": "portal:../../" 15 | }, 16 | "devDependencies": { 17 | "@types/react": "^18.0.15", 18 | "@types/react-dom": "^18.0.6", 19 | "react": "^18.2.0", 20 | "react-dom": "^18.2.0", 21 | "typescript": "^4.7.3" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /examples/2-scaffolding/src/models/AttackModel.ts: -------------------------------------------------------------------------------- 1 | import { Instance } from "mobx-state-tree" 2 | import { AttackModelBase } from "./AttackModel.base" 3 | 4 | /* The TypeScript type of an instance of AttackModel */ 5 | export interface AttackModelType extends Instance {} 6 | 7 | /* A graphql query fragment builders for AttackModel */ 8 | export { 9 | selectFromAttack, 10 | attackModelPrimitives, 11 | AttackModelSelector 12 | } from "./AttackModel.base" 13 | 14 | /** 15 | * AttackModel 16 | * 17 | * Represents a Pokémon's attack types 18 | */ 19 | export const AttackModel = AttackModelBase.actions((self) => ({ 20 | // This is an auto-generated example action. 21 | log() { 22 | console.log(JSON.stringify(self)) 23 | } 24 | })) 25 | -------------------------------------------------------------------------------- /examples/2-scaffolding/src/models/ModelBase.ts: -------------------------------------------------------------------------------- 1 | import { MSTGQLObject } from "mst-gql" 2 | 3 | export const ModelBase = MSTGQLObject 4 | -------------------------------------------------------------------------------- /examples/2-scaffolding/src/models/PokemonAttackModel.ts: -------------------------------------------------------------------------------- 1 | import { Instance } from "mobx-state-tree" 2 | import { PokemonAttackModelBase } from "./PokemonAttackModel.base" 3 | 4 | /* The TypeScript type of an instance of PokemonAttackModel */ 5 | export interface PokemonAttackModelType 6 | extends Instance {} 7 | 8 | /* A graphql query fragment builders for PokemonAttackModel */ 9 | export { 10 | selectFromPokemonAttack, 11 | pokemonAttackModelPrimitives, 12 | PokemonAttackModelSelector 13 | } from "./PokemonAttackModel.base" 14 | 15 | /** 16 | * PokemonAttackModel 17 | * 18 | * Represents a Pokémon's attack types 19 | */ 20 | export const PokemonAttackModel = PokemonAttackModelBase.actions((self) => ({ 21 | // This is an auto-generated example action. 22 | log() { 23 | console.log(JSON.stringify(self)) 24 | } 25 | })) 26 | -------------------------------------------------------------------------------- /examples/2-scaffolding/src/models/PokemonDimensionModel.base.ts: -------------------------------------------------------------------------------- 1 | /* This is a mst-gql generated file, don't modify it manually */ 2 | /* eslint-disable */ 3 | /* tslint:disable */ 4 | 5 | import { types } from "mobx-state-tree" 6 | import { QueryBuilder } from "mst-gql" 7 | import { ModelBase } from "./ModelBase" 8 | import { RootStoreType } from "./index" 9 | 10 | 11 | /** 12 | * PokemonDimensionBase 13 | * auto generated base class for the model PokemonDimensionModel. 14 | * 15 | * Represents a Pokémon's dimensions 16 | */ 17 | export const PokemonDimensionModelBase = ModelBase 18 | .named('PokemonDimension') 19 | .props({ 20 | __typename: types.optional(types.literal("PokemonDimension"), "PokemonDimension"), 21 | /** The minimum value of this dimension */ 22 | minimum: types.union(types.undefined, types.null, types.string), 23 | /** The maximum value of this dimension */ 24 | maximum: types.union(types.undefined, types.null, types.string), 25 | }) 26 | .views(self => ({ 27 | get store() { 28 | return self.__getStore() 29 | } 30 | })) 31 | 32 | export class PokemonDimensionModelSelector extends QueryBuilder { 33 | get minimum() { return this.__attr(`minimum`) } 34 | get maximum() { return this.__attr(`maximum`) } 35 | } 36 | export function selectFromPokemonDimension() { 37 | return new PokemonDimensionModelSelector() 38 | } 39 | 40 | export const pokemonDimensionModelPrimitives = selectFromPokemonDimension().minimum.maximum 41 | -------------------------------------------------------------------------------- /examples/2-scaffolding/src/models/PokemonDimensionModel.ts: -------------------------------------------------------------------------------- 1 | import { Instance } from "mobx-state-tree" 2 | import { PokemonDimensionModelBase } from "./PokemonDimensionModel.base" 3 | 4 | /* The TypeScript type of an instance of PokemonDimensionModel */ 5 | export interface PokemonDimensionModelType 6 | extends Instance {} 7 | 8 | /* A graphql query fragment builders for PokemonDimensionModel */ 9 | export { 10 | selectFromPokemonDimension, 11 | pokemonDimensionModelPrimitives, 12 | PokemonDimensionModelSelector 13 | } from "./PokemonDimensionModel.base" 14 | 15 | /** 16 | * PokemonDimensionModel 17 | * 18 | * Represents a Pokémon's dimensions 19 | */ 20 | export const PokemonDimensionModel = PokemonDimensionModelBase.actions( 21 | (self) => ({ 22 | // This is an auto-generated example action. 23 | log() { 24 | console.log(JSON.stringify(self)) 25 | } 26 | }) 27 | ) 28 | -------------------------------------------------------------------------------- /examples/2-scaffolding/src/models/PokemonEvolutionRequirementModel.ts: -------------------------------------------------------------------------------- 1 | import { Instance } from "mobx-state-tree" 2 | import { PokemonEvolutionRequirementModelBase } from "./PokemonEvolutionRequirementModel.base" 3 | 4 | /* The TypeScript type of an instance of PokemonEvolutionRequirementModel */ 5 | export interface PokemonEvolutionRequirementModelType 6 | extends Instance {} 7 | 8 | /* A graphql query fragment builders for PokemonEvolutionRequirementModel */ 9 | export { 10 | selectFromPokemonEvolutionRequirement, 11 | pokemonEvolutionRequirementModelPrimitives, 12 | PokemonEvolutionRequirementModelSelector 13 | } from "./PokemonEvolutionRequirementModel.base" 14 | 15 | /** 16 | * PokemonEvolutionRequirementModel 17 | * 18 | * Represents a Pokémon's requirement to evolve 19 | */ 20 | export const PokemonEvolutionRequirementModel = 21 | PokemonEvolutionRequirementModelBase.actions((self) => ({ 22 | // This is an auto-generated example action. 23 | log() { 24 | console.log(JSON.stringify(self)) 25 | } 26 | })) 27 | -------------------------------------------------------------------------------- /examples/2-scaffolding/src/models/PokemonModel.ts: -------------------------------------------------------------------------------- 1 | import { Instance } from "mobx-state-tree" 2 | import { PokemonModelBase } from "./PokemonModel.base" 3 | 4 | /* The TypeScript type of an instance of PokemonModel */ 5 | export interface PokemonModelType extends Instance {} 6 | 7 | /* A graphql query fragment builders for PokemonModel */ 8 | export { 9 | selectFromPokemon, 10 | pokemonModelPrimitives, 11 | PokemonModelSelector 12 | } from "./PokemonModel.base" 13 | 14 | /** 15 | * PokemonModel 16 | * 17 | * Represents a Pokémon 18 | */ 19 | export const PokemonModel = PokemonModelBase.actions((self) => ({ 20 | // This is an auto-generated example action. 21 | log() { 22 | console.log(JSON.stringify(self)) 23 | } 24 | })) 25 | -------------------------------------------------------------------------------- /examples/2-scaffolding/src/models/RootStore.ts: -------------------------------------------------------------------------------- 1 | import { Instance } from "mobx-state-tree" 2 | import { RootStoreBase } from "./RootStore.base" 3 | 4 | export interface RootStoreType extends Instance {} 5 | 6 | export const RootStore = RootStoreBase.actions((self) => ({ 7 | // This is an auto-generated example action. 8 | log() { 9 | console.log(JSON.stringify(self)) 10 | } 11 | })) 12 | -------------------------------------------------------------------------------- /examples/2-scaffolding/src/models/index.ts: -------------------------------------------------------------------------------- 1 | /* This is a mst-gql generated file, don't modify it manually */ 2 | /* eslint-disable */ 3 | /* tslint:disable */ 4 | 5 | export * from "./PokemonModel" 6 | export * from "./PokemonDimensionModel" 7 | export * from "./PokemonAttackModel" 8 | export * from "./AttackModel" 9 | export * from "./PokemonEvolutionRequirementModel" 10 | export * from "./RootStore" 11 | export * from "./reactUtils" 12 | -------------------------------------------------------------------------------- /examples/2-scaffolding/src/models/reactUtils.ts: -------------------------------------------------------------------------------- 1 | /* This is a mst-gql generated file, don't modify it manually */ 2 | /* eslint-disable */ 3 | /* tslint:disable */ 4 | 5 | import { createStoreContext, createUseQueryHook } from "mst-gql" 6 | import * as React from "react" 7 | import { RootStoreType } from "./RootStore" 8 | 9 | export const StoreContext = createStoreContext(React) 10 | 11 | export const useQuery = createUseQueryHook(StoreContext, React) 12 | -------------------------------------------------------------------------------- /examples/3-twitter-clone/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .build 3 | lib 4 | src/server/db/data.json -------------------------------------------------------------------------------- /examples/3-twitter-clone/README.md: -------------------------------------------------------------------------------- 1 | ## About 2 | 3 | This example is a twttier clone that uses mst-gql. 4 | 5 | ## Usage 6 | 7 | #### 0. install `mst-gql` dependencies (within the parent `mst-gql` directory) 8 | 9 | ```bash 10 | yarn 11 | ``` 12 | 13 | #### 1. Install dependencies (within this example's folder) 14 | 15 | ```bash 16 | yarn 17 | ``` 18 | 19 | #### 2. Start server 20 | 21 | ```bash 22 | yarn start 23 | ``` 24 | 25 | #### 3. Open browser 26 | 27 | Navigate to example at [http://localhost:3000/](http://localhost:3000/). 28 | -------------------------------------------------------------------------------- /examples/3-twitter-clone/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | MST-GQL Twitter Clone Example 8 | 9 | 10 | 11 |
    12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /examples/3-twitter-clone/src/app/avatars/chuck.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobxjs/mst-gql/ab8bd37c3cacebd6905b4842d2472cbcec1fb09e/examples/3-twitter-clone/src/app/avatars/chuck.jpg -------------------------------------------------------------------------------- /examples/3-twitter-clone/src/app/avatars/michel.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobxjs/mst-gql/ab8bd37c3cacebd6905b4842d2472cbcec1fb09e/examples/3-twitter-clone/src/app/avatars/michel.jpg -------------------------------------------------------------------------------- /examples/3-twitter-clone/src/app/components/Composer.tsx: -------------------------------------------------------------------------------- 1 | import React, { useRef } from "react" 2 | import { observer } from "mobx-react-lite" 3 | 4 | import { MessageModelType } from "../models" 5 | 6 | import { Loading, Error } from "./utils" 7 | import { useQuery } from "../models/reactUtils" 8 | 9 | export const Composer = observer( 10 | ({ replyTo }: { replyTo?: MessageModelType }) => { 11 | const inputRef = useRef() 12 | const { store, loading, error, setQuery } = useQuery() 13 | return error ? ( 14 | Failed to post message: ${error} 15 | ) : loading ? ( 16 | 17 | ) : ( 18 |
    19 | 20 | 34 |
    35 | ) 36 | } 37 | ) 38 | -------------------------------------------------------------------------------- /examples/3-twitter-clone/src/app/components/Home.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | 3 | import { Composer } from "./Composer" 4 | import { MessageWall } from "./MessageWall" 5 | 6 | export const Home = () => ( 7 | <> 8 |
    9 | Share 🤯 experience... 10 |
    11 |
    12 | 13 |
    14 | 15 | 16 | ) 17 | -------------------------------------------------------------------------------- /examples/3-twitter-clone/src/app/components/Message.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import { useState } from "react" 3 | import { observer } from "mobx-react-lite" 4 | 5 | import { MessageModelType } from "../models/" 6 | import { Replies } from "./Replies" 7 | 8 | // @ts-ignore 9 | import chuck from "../avatars/chuck.jpg" 10 | // @ts-ignore 11 | import michel from "../avatars/michel.jpg" 12 | 13 | const images = { 14 | michel, 15 | chuck 16 | } 17 | 18 | export const Message = observer( 19 | ({ message, asChild }: { message: MessageModelType; asChild?: boolean }) => { 20 | const [collapsed, setCollapsed] = useState(true) 21 | return ( 22 |
  • 23 | 24 |
    25 |

    {message.user.name}

    26 |

    {message.text}

    27 |
    28 |
    32 | 💙 33 |
    34 | {asChild ? null : ( 35 |
    setCollapsed((c) => !c)}> 36 | 💬 37 |
    38 | )} 39 |
    40 |
    41 | {collapsed ? null : } 42 |
  • 43 | ) 44 | } 45 | ) 46 | -------------------------------------------------------------------------------- /examples/3-twitter-clone/src/app/components/MessageWall.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { observer } from "mobx-react-lite" 3 | 4 | import { Loading, Error } from "./utils" 5 | 6 | import { useQuery } from "../models/reactUtils" 7 | import { Message } from "./Message" 8 | 9 | export const MessageWall = observer(() => { 10 | const { store, error, loading, setQuery } = useQuery((store) => 11 | store.loadInitialMessages() 12 | ) 13 | if (error) return {error.message} 14 | if (!store.messages.size) return 15 | return ( 16 | <> 17 |
      18 | {store.sortedMessages.map((message) => ( 19 | 20 | ))} 21 |
    22 | {loading ? ( 23 | 24 | ) : ( 25 | 33 | )} 34 | 35 | ) 36 | }) 37 | -------------------------------------------------------------------------------- /examples/3-twitter-clone/src/app/components/Profile.tsx: -------------------------------------------------------------------------------- 1 | import React, { useRef } from "react" 2 | import { observer } from "mobx-react-lite" 3 | 4 | import { Error } from "./utils" 5 | 6 | import { useQuery } from "../models/reactUtils" 7 | 8 | export const Profile = observer(() => { 9 | const inputRef = useRef() 10 | const { loading, error, data, store, setQuery } = useQuery((store) => 11 | store.queryMe() 12 | ) 13 | 14 | if (error) return {error.message} 15 | return ( 16 | <> 17 |

    Edit profile

    18 | 23 | 31 | 32 | ) 33 | }) 34 | -------------------------------------------------------------------------------- /examples/3-twitter-clone/src/app/components/Replies.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { observer } from "mobx-react-lite" 3 | 4 | import { Loading, Error } from "./utils" 5 | import { Message } from "./Message" 6 | import { Composer } from "./Composer" 7 | 8 | import { useQuery } from "../models/reactUtils" 9 | import { MessageModelType } from "../models" 10 | 11 | export const Replies = observer( 12 | ({ message }: { message: MessageModelType }) => { 13 | const { data, error, loading } = useQuery(() => message.loadReplies()) 14 | return ( 15 |
    16 | {error ? ( 17 | {error} 18 | ) : loading ? ( 19 | 20 | ) : ( 21 | <> 22 |
      23 | {data.messages.map((message) => ( 24 | 25 | ))} 26 |
    27 | 28 | 29 | )} 30 |
    31 | ) 32 | } 33 | ) 34 | -------------------------------------------------------------------------------- /examples/3-twitter-clone/src/app/components/utils/Error.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from "react" 2 | 3 | export const Error: FC = (props) => ( 4 |
    {props.children || "Something went wrong"}
    5 | ) 6 | -------------------------------------------------------------------------------- /examples/3-twitter-clone/src/app/components/utils/Loading.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | 3 | export const Loading = () => 4 | -------------------------------------------------------------------------------- /examples/3-twitter-clone/src/app/components/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./Error" 2 | export * from "./Loading" 3 | -------------------------------------------------------------------------------- /examples/3-twitter-clone/src/app/components/utils/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobxjs/mst-gql/ab8bd37c3cacebd6905b4842d2472cbcec1fb09e/examples/3-twitter-clone/src/app/components/utils/spinner.gif -------------------------------------------------------------------------------- /examples/3-twitter-clone/src/app/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import * as ReactDOM from "react-dom" 3 | import { createHttpClient } from "mst-gql" 4 | import { SubscriptionClient } from "subscriptions-transport-ws" 5 | 6 | import "./index.css" 7 | 8 | import { RootStore } from "./models/RootStore" 9 | import { StoreContext } from "./models/reactUtils" 10 | 11 | import { Home } from "./components/Home" 12 | import { Profile } from "./components/Profile" 13 | 14 | const gqlHttpClient = createHttpClient("http://localhost:4000/graphql") 15 | 16 | const gqlWsClient = new SubscriptionClient("ws://localhost:4000/graphql", { 17 | reconnect: true 18 | }) 19 | 20 | const rootStore = RootStore.create(undefined, { 21 | gqlHttpClient, 22 | gqlWsClient 23 | }) 24 | 25 | export const App = () => ( 26 | 27 |
    28 | 29 | 30 |
    31 |
    32 | ) 33 | 34 | ReactDOM.render(, document.getElementById("root")) 35 | 36 | // For debugging 37 | // @ts-ignore 38 | window.store = rootStore 39 | -------------------------------------------------------------------------------- /examples/3-twitter-clone/src/app/models/BaseIDModelSelector.ts: -------------------------------------------------------------------------------- 1 | /* This is a mst-gql generated file, don't modify it manually */ 2 | /* eslint-disable */ 3 | /* tslint:disable */ 4 | 5 | import { QueryBuilder } from "mst-gql" 6 | import { MessageModelType } from "./MessageModel" 7 | import { MessageModelSelector } from "./MessageModel.base" 8 | import { UserModelType } from "./UserModel" 9 | import { UserModelSelector } from "./UserModel.base" 10 | 11 | export type BaseIDUnion = UserModelType | MessageModelType 12 | 13 | export class BaseIDModelSelector extends QueryBuilder { 14 | get id() { return this.__attr(`id`) } 15 | user(builder?: string | UserModelSelector | ((selector: UserModelSelector) => UserModelSelector)) { return this.__inlineFragment(`User`, UserModelSelector, builder) } 16 | message(builder?: string | MessageModelSelector | ((selector: MessageModelSelector) => MessageModelSelector)) { return this.__inlineFragment(`Message`, MessageModelSelector, builder) } 17 | } 18 | export function selectFromBaseID() { 19 | return new BaseIDModelSelector() 20 | } 21 | 22 | export const baseIDModelPrimitives = selectFromBaseID() -------------------------------------------------------------------------------- /examples/3-twitter-clone/src/app/models/MessageModel.ts: -------------------------------------------------------------------------------- 1 | import { Instance } from "mobx-state-tree" 2 | import { MessageModelBase } from "./MessageModel.base" 3 | 4 | /* The TypeScript type of an instance of MessageModel */ 5 | export interface MessageModelType extends Instance {} 6 | 7 | /* A graphql query fragment builders for MessageModel */ 8 | export { 9 | selectFromMessage, 10 | messageModelPrimitives, 11 | MessageModelSelector 12 | } from "./MessageModel.base" 13 | 14 | /** 15 | * MessageModel 16 | */ 17 | export const MessageModel = MessageModelBase.views((self) => ({ 18 | get isLikedByMe(): boolean { 19 | return self.likes.includes(self.store.me) 20 | } 21 | })).actions((self) => { 22 | let loadReplyQuery: ReturnType 23 | 24 | return { 25 | like() { 26 | return self.store.mutateLike( 27 | { 28 | msg: self.id, 29 | user: self.store.me.id 30 | }, 31 | `__typename id likes { __typename id }`, 32 | () => { 33 | if (self.likes.includes(self.store.me)) 34 | self.likes.remove(self.store.me) 35 | else self.likes.push(self.store.me) 36 | } 37 | ) 38 | }, 39 | loadReplies() { 40 | if (!loadReplyQuery) { 41 | loadReplyQuery = self.store.loadMessages("", 100, self.id) 42 | } else { 43 | loadReplyQuery.refetch() 44 | } 45 | return loadReplyQuery 46 | } 47 | } 48 | }) 49 | -------------------------------------------------------------------------------- /examples/3-twitter-clone/src/app/models/ModelBase.ts: -------------------------------------------------------------------------------- 1 | import { MSTGQLObject } from "mst-gql" 2 | 3 | export const ModelBase = MSTGQLObject 4 | -------------------------------------------------------------------------------- /examples/3-twitter-clone/src/app/models/SearchResultModelSelector.ts: -------------------------------------------------------------------------------- 1 | /* This is a mst-gql generated file, don't modify it manually */ 2 | /* eslint-disable */ 3 | /* tslint:disable */ 4 | 5 | import { QueryBuilder } from "mst-gql" 6 | import { MessageModelType } from "./MessageModel" 7 | import { MessageModelSelector, messageModelPrimitives } from "./MessageModel.base" 8 | import { UserModelType } from "./UserModel" 9 | import { UserModelSelector, userModelPrimitives } from "./UserModel.base" 10 | 11 | export type SearchResultUnion = UserModelType | MessageModelType 12 | 13 | export class SearchResultModelSelector extends QueryBuilder { 14 | user(builder?: string | UserModelSelector | ((selector: UserModelSelector) => UserModelSelector)) { return this.__inlineFragment(`User`, UserModelSelector, builder) } 15 | message(builder?: string | MessageModelSelector | ((selector: MessageModelSelector) => MessageModelSelector)) { return this.__inlineFragment(`Message`, MessageModelSelector, builder) } 16 | } 17 | export function selectFromSearchResult() { 18 | return new SearchResultModelSelector() 19 | } 20 | 21 | // provides all primitive fields of union member types combined together 22 | export const searchResultModelPrimitives = selectFromSearchResult().user(userModelPrimitives).message(messageModelPrimitives) -------------------------------------------------------------------------------- /examples/3-twitter-clone/src/app/models/UserModel.base.ts: -------------------------------------------------------------------------------- 1 | /* This is a mst-gql generated file, don't modify it manually */ 2 | /* eslint-disable */ 3 | /* tslint:disable */ 4 | 5 | import { types } from "mobx-state-tree" 6 | import { QueryBuilder } from "mst-gql" 7 | import { ModelBase } from "./ModelBase" 8 | import { RootStoreType } from "./index" 9 | 10 | 11 | /** 12 | * UserBase 13 | * auto generated base class for the model UserModel. 14 | */ 15 | export const UserModelBase = ModelBase 16 | .named('User') 17 | .props({ 18 | __typename: types.optional(types.literal("User"), "User"), 19 | id: types.identifier, 20 | name: types.union(types.undefined, types.string), 21 | avatar: types.union(types.undefined, types.string), 22 | }) 23 | .views(self => ({ 24 | get store() { 25 | return self.__getStore() 26 | } 27 | })) 28 | 29 | export class UserModelSelector extends QueryBuilder { 30 | get id() { return this.__attr(`id`) } 31 | get name() { return this.__attr(`name`) } 32 | get avatar() { return this.__attr(`avatar`) } 33 | } 34 | export function selectFromUser() { 35 | return new UserModelSelector() 36 | } 37 | 38 | export const userModelPrimitives = selectFromUser().name.avatar 39 | -------------------------------------------------------------------------------- /examples/3-twitter-clone/src/app/models/UserModel.ts: -------------------------------------------------------------------------------- 1 | import { Instance } from "mobx-state-tree" 2 | import { UserModelBase } from "./UserModel.base" 3 | 4 | /* The TypeScript type of an instance of UserModel */ 5 | export interface UserModelType extends Instance {} 6 | 7 | /* A graphql query fragment builders for UserModel */ 8 | export { 9 | selectFromUser, 10 | userModelPrimitives, 11 | UserModelSelector 12 | } from "./UserModel.base" 13 | 14 | /** 15 | * UserModel 16 | */ 17 | export const UserModel = UserModelBase.actions((self) => ({ 18 | changeName(name: string) { 19 | return self.store.mutateChangeName( 20 | { 21 | id: self.id, 22 | name 23 | }, 24 | (user) => user.name, 25 | () => { 26 | self.name = name 27 | } 28 | ) 29 | } 30 | })) 31 | -------------------------------------------------------------------------------- /examples/3-twitter-clone/src/app/models/index.ts: -------------------------------------------------------------------------------- 1 | /* This is a mst-gql generated file, don't modify it manually */ 2 | /* eslint-disable */ 3 | /* tslint:disable */ 4 | 5 | export * from "./UserModel" 6 | export * from "./MessageModel" 7 | export * from "./SearchResultModelSelector" 8 | export * from "./RootStore" 9 | export * from "./reactUtils" 10 | -------------------------------------------------------------------------------- /examples/3-twitter-clone/src/app/models/reactUtils.ts: -------------------------------------------------------------------------------- 1 | /* This is a mst-gql generated file, don't modify it manually */ 2 | /* eslint-disable */ 3 | /* tslint:disable */ 4 | 5 | import { createStoreContext, createUseQueryHook } from "mst-gql" 6 | import * as React from "react" 7 | import { RootStoreType } from "./RootStore" 8 | 9 | export const StoreContext = createStoreContext(React) 10 | 11 | export const useQuery = createUseQueryHook(StoreContext, React) 12 | -------------------------------------------------------------------------------- /examples/3-twitter-clone/src/app/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "rootDir": "./", 5 | // "baseUrl": "./src/app", 6 | "strict": false, 7 | "lib": ["dom", "es2018", "esnext.asynciterable", "esnext.array"], 8 | "jsx": "react", 9 | "esModuleInterop": true, 10 | "moduleResolution": "node", 11 | "isolatedModules": true 12 | }, 13 | "files": ["index.tsx"] 14 | } 15 | -------------------------------------------------------------------------------- /examples/3-twitter-clone/src/server/index.ts: -------------------------------------------------------------------------------- 1 | import { ApolloServer } from "apollo-server-express" 2 | import cors from "cors" 3 | import express from "express" 4 | import http from "http" 5 | 6 | import { typeDefs, resolvers } from "./schema" 7 | 8 | const PORT = 4000 9 | 10 | const app = express() 11 | app.use(cors()) 12 | 13 | const server = new ApolloServer({ 14 | typeDefs, 15 | resolvers 16 | }) 17 | 18 | server.applyMiddleware({ app }) 19 | 20 | const httpServer = http.createServer(app) 21 | server.installSubscriptionHandlers(httpServer) 22 | 23 | httpServer.listen(PORT, () => { 24 | console.log( 25 | `🚀 Server ready at http://localhost:${PORT}${server.graphqlPath}` 26 | ) 27 | console.log( 28 | `🚀 Subscriptions ready at ws://localhost:${PORT}${server.subscriptionsPath}` 29 | ) 30 | }) 31 | -------------------------------------------------------------------------------- /examples/3-twitter-clone/src/server/models/MessageModel.ts: -------------------------------------------------------------------------------- 1 | import { MessageModelBase } from "./MessageModel.base" 2 | import { getSnapshot } from "mobx-state-tree" 3 | 4 | /* The TypeScript type of an instance of MessageModel */ 5 | export type MessageModelType = typeof MessageModel.Type 6 | 7 | /** 8 | * MessageModel 9 | */ 10 | export const MessageModel = MessageModelBase.views((self) => ({ 11 | serialize() { 12 | return { 13 | ...getSnapshot(self), 14 | likes: self.likes.map((like) => like.serialize()), 15 | user: self.user.serialize() 16 | } 17 | } 18 | })) 19 | -------------------------------------------------------------------------------- /examples/3-twitter-clone/src/server/models/ModelBase.ts: -------------------------------------------------------------------------------- 1 | import { MSTGQLObject } from "mst-gql" 2 | 3 | export const ModelBase = MSTGQLObject 4 | -------------------------------------------------------------------------------- /examples/3-twitter-clone/src/server/models/ReplyModel.base.ts: -------------------------------------------------------------------------------- 1 | /* This is a mst-gql generated file, don't modify it manually */ 2 | /* eslint-disable */ 3 | /* tslint:disable */ 4 | 5 | import { types } from "mobx-state-tree" 6 | import { MSTGQLObject, MSTGQLRef } from "mst-gql" 7 | 8 | import { UserModel } from "./UserModel" 9 | import { RootStore } from "./index" 10 | 11 | /** 12 | * ReplyBase 13 | * auto generated base class for the model ReplyModel. 14 | */ 15 | export const ReplyModelBase = MSTGQLObject 16 | .named('Reply') 17 | .props({ 18 | __typename: types.optional(types.literal("Reply"), "Reply"), 19 | id: types.identifier, 20 | timestamp: types.number, 21 | user: MSTGQLRef(types.late(() => UserModel)), 22 | text: types.string, 23 | likes: types.array(MSTGQLRef(types.late(() => UserModel))), 24 | }) 25 | .views(self => ({ 26 | get store() { 27 | return self.__getStore() 28 | } 29 | })) 30 | 31 | 32 | -------------------------------------------------------------------------------- /examples/3-twitter-clone/src/server/models/ReplyModel.ts: -------------------------------------------------------------------------------- 1 | import { ReplyModelBase } from "./ReplyModel.base" 2 | import { getSnapshot } from "mobx-state-tree" 3 | 4 | /* The TypeScript type of an instance of ReplyModel */ 5 | export type ReplyModelType = typeof ReplyModel.Type 6 | 7 | /** 8 | * ReplyModel 9 | */ 10 | export const ReplyModel = ReplyModelBase.views((self) => ({ 11 | serialize() { 12 | return { 13 | ...getSnapshot(self), 14 | likes: self.likes.map((like) => like.serialize()), 15 | user: self.user.serialize() 16 | } 17 | } 18 | })) 19 | -------------------------------------------------------------------------------- /examples/3-twitter-clone/src/server/models/UserModel.base.ts: -------------------------------------------------------------------------------- 1 | /* This is a mst-gql generated file, don't modify it manually */ 2 | /* eslint-disable */ 3 | /* tslint:disable */ 4 | 5 | import { types } from "mobx-state-tree" 6 | import { QueryBuilder } from "mst-gql" 7 | import { ModelBase } from "./ModelBase" 8 | import { RootStoreType } from "./index" 9 | 10 | 11 | /** 12 | * UserBase 13 | * auto generated base class for the model UserModel. 14 | */ 15 | export const UserModelBase = ModelBase 16 | .named('User') 17 | .props({ 18 | __typename: types.optional(types.literal("User"), "User"), 19 | id: types.identifier, 20 | name: types.union(types.undefined, types.string), 21 | avatar: types.union(types.undefined, types.string), 22 | }) 23 | .views(self => ({ 24 | get store() { 25 | return self.__getStore() 26 | } 27 | })) 28 | 29 | 30 | -------------------------------------------------------------------------------- /examples/3-twitter-clone/src/server/models/UserModel.ts: -------------------------------------------------------------------------------- 1 | import { UserModelBase } from "./UserModel.base" 2 | import { getSnapshot } from "mobx-state-tree" 3 | 4 | /* The TypeScript type of an instance of UserModel */ 5 | export type UserModelType = typeof UserModel.Type 6 | 7 | /** 8 | * UserModel 9 | */ 10 | export const UserModel = UserModelBase.actions((self) => ({ 11 | setName(name: string) { 12 | self.name = name 13 | self.store.save() 14 | } 15 | })).views((self) => ({ 16 | serialize() { 17 | return getSnapshot(self) 18 | } 19 | })) 20 | -------------------------------------------------------------------------------- /examples/3-twitter-clone/src/server/models/index.ts: -------------------------------------------------------------------------------- 1 | /* This is a mst-gql generated file, don't modify it manually */ 2 | /* eslint-disable */ 3 | /* tslint:disable */ 4 | 5 | export * from "./UserModel" 6 | export * from "./MessageModel" 7 | export * from "./RootStore" 8 | -------------------------------------------------------------------------------- /examples/3-twitter-clone/src/server/models/reactUtils.ts: -------------------------------------------------------------------------------- 1 | /* This is a mst-gql generated file, don't modify it manually */ 2 | /* eslint-disable */ 3 | /* tslint:disable */ 4 | 5 | import { createStoreContext, createUseQueryHook } from "mst-gql" 6 | import * as React from "react" 7 | import { RootStore } from "./RootStore" 8 | 9 | export const StoreContext = createStoreContext(React) 10 | 11 | export const useQuery = createUseQueryHook(StoreContext, React) 12 | -------------------------------------------------------------------------------- /examples/3-twitter-clone/src/server/schema.graphql: -------------------------------------------------------------------------------- 1 | type User { 2 | id: ID 3 | name: String! 4 | avatar: String! 5 | } 6 | type Message { 7 | id: ID 8 | timestamp: Float! 9 | user: User! 10 | text: String! 11 | likes: [User!] 12 | replyTo: Message 13 | } 14 | type Query { 15 | search(searchText: String!): [SearchResult!] 16 | messages(offset: ID, count: Int, replyTo: ID): [Message] 17 | message(id: ID!): Message 18 | me: User 19 | } 20 | type Subscription { 21 | newMessages: Message 22 | } 23 | type Mutation { 24 | changeName(id: ID!, name: String!): User 25 | like(msg: ID!, user: ID!): Message 26 | postTweet(text: String!, user: ID!, replyTo: ID): Message 27 | } 28 | 29 | union SearchResult = User | Message 30 | -------------------------------------------------------------------------------- /examples/3-twitter-clone/src/server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "rootDir": "./src/server", 5 | "baseUrl": "./src/server", 6 | "strict": false, 7 | "lib": ["es2018", "esnext.asynciterable", "esnext.array"], 8 | "jsx": "react", 9 | "esModuleInterop": true, 10 | "moduleResolution": "node", 11 | "isolatedModules": true 12 | }, 13 | "files": ["index.ts"] 14 | } 15 | -------------------------------------------------------------------------------- /examples/4-apollo-tutorial/README.md: -------------------------------------------------------------------------------- 1 | ## About 2 | 3 | Based on the [apollo full-stack tutorial](https://github.com/apollographql/fullstack-tutorial/tree/d780ec0ceed274fdc296eebfaf20d54499e8ea31/final) 4 | 5 | ## Usage 6 | 7 | #### 0. install `mst-gql` dependencies (within the parent `mst-gql` directory) 8 | 9 | ```bash 10 | yarn 11 | ``` 12 | 13 | #### 1. Add `ENGINE_API_KEY` 14 | 15 | 1. Visit (https://engine.apollographql.com/)[Apollo] and create an account. 16 | 2. Create a new graph 17 | 3. grab the `ENGINE_API_KEY` they give you and add it to a `.env` file within the `server` in this example. 18 | 19 | #### 2. Run the server 20 | 21 | ```bash 22 | cd server 23 | yarn 24 | yarn start 25 | ``` 26 | 27 | #### 3. Run the client (in another terminal) 28 | 29 | ```bash 30 | cd client 31 | yarn 32 | yarn start 33 | ``` 34 | -------------------------------------------------------------------------------- /examples/4-apollo-tutorial/client/.env: -------------------------------------------------------------------------------- 1 | SKIP_PREFLIGHT_CHECK=true 2 | -------------------------------------------------------------------------------- /examples/4-apollo-tutorial/client/.env.example: -------------------------------------------------------------------------------- 1 | ENGINE_API_KEY= -------------------------------------------------------------------------------- /examples/4-apollo-tutorial/client/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-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 | -------------------------------------------------------------------------------- /examples/4-apollo-tutorial/client/README.md: -------------------------------------------------------------------------------- 1 | # Apollo Fullstack Tutorial 2 | 3 | ## Client 4 | -------------------------------------------------------------------------------- /examples/4-apollo-tutorial/client/apollo.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | client: { 3 | name: "Space Explorer [web]", 4 | service: "space-explorer" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /examples/4-apollo-tutorial/client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "client", 3 | "version": "0.1.0", 4 | "scripts": { 5 | "build": "react-scripts build", 6 | "eject": "react-scripts eject", 7 | "scaffold": "node ../../../generator/mst-gql-scaffold.js --excludes TripUpdateResponse --format js demo_schema.json", 8 | "start": "yarn scaffold && wait-port 4000 && react-scripts start", 9 | "test": "react-scripts test" 10 | }, 11 | "dependencies": { 12 | "@reach/router": "^1.2.1", 13 | "emotion": "^10.0.6", 14 | "mst-gql": "portal:../../../", 15 | "polished": "^2.3.3", 16 | "react": "^18.2.0", 17 | "react-dom": "^18.2.0", 18 | "react-emotion": "^9.2.12", 19 | "react-scripts": "^5.0.1" 20 | }, 21 | "devDependencies": { 22 | "graphql": "^16.8.1", 23 | "graphql-request": "^6.1.0", 24 | "jest-dom": "^3.0.0", 25 | "mobx": "^6.10.2", 26 | "mobx-react": "^7.5.0", 27 | "mobx-state-tree": "^5.3.0", 28 | "react-testing-library": "^5.4.4", 29 | "wait-port": "^0.2.9" 30 | }, 31 | "browserslist": [ 32 | ">0.2%", 33 | "not dead", 34 | "not ie <= 11", 35 | "not op_mini all" 36 | ], 37 | "eslintConfig": { 38 | "extends": "react-app" 39 | }, 40 | "private": true 41 | } 42 | -------------------------------------------------------------------------------- /examples/4-apollo-tutorial/client/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobxjs/mst-gql/ab8bd37c3cacebd6905b4842d2472cbcec1fb09e/examples/4-apollo-tutorial/client/public/favicon.ico -------------------------------------------------------------------------------- /examples/4-apollo-tutorial/client/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Launches", 3 | "name": "Launches", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /examples/4-apollo-tutorial/client/src/assets/curve.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /examples/4-apollo-tutorial/client/src/assets/images/badge-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobxjs/mst-gql/ab8bd37c3cacebd6905b4842d2472cbcec1fb09e/examples/4-apollo-tutorial/client/src/assets/images/badge-1.png -------------------------------------------------------------------------------- /examples/4-apollo-tutorial/client/src/assets/images/badge-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobxjs/mst-gql/ab8bd37c3cacebd6905b4842d2472cbcec1fb09e/examples/4-apollo-tutorial/client/src/assets/images/badge-2.png -------------------------------------------------------------------------------- /examples/4-apollo-tutorial/client/src/assets/images/badge-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobxjs/mst-gql/ab8bd37c3cacebd6905b4842d2472cbcec1fb09e/examples/4-apollo-tutorial/client/src/assets/images/badge-3.png -------------------------------------------------------------------------------- /examples/4-apollo-tutorial/client/src/assets/images/dog-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobxjs/mst-gql/ab8bd37c3cacebd6905b4842d2472cbcec1fb09e/examples/4-apollo-tutorial/client/src/assets/images/dog-1.png -------------------------------------------------------------------------------- /examples/4-apollo-tutorial/client/src/assets/images/dog-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobxjs/mst-gql/ab8bd37c3cacebd6905b4842d2472cbcec1fb09e/examples/4-apollo-tutorial/client/src/assets/images/dog-2.png -------------------------------------------------------------------------------- /examples/4-apollo-tutorial/client/src/assets/images/dog-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobxjs/mst-gql/ab8bd37c3cacebd6905b4842d2472cbcec1fb09e/examples/4-apollo-tutorial/client/src/assets/images/dog-3.png -------------------------------------------------------------------------------- /examples/4-apollo-tutorial/client/src/assets/images/galaxy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobxjs/mst-gql/ab8bd37c3cacebd6905b4842d2472cbcec1fb09e/examples/4-apollo-tutorial/client/src/assets/images/galaxy.jpg -------------------------------------------------------------------------------- /examples/4-apollo-tutorial/client/src/assets/images/iss.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobxjs/mst-gql/ab8bd37c3cacebd6905b4842d2472cbcec1fb09e/examples/4-apollo-tutorial/client/src/assets/images/iss.jpg -------------------------------------------------------------------------------- /examples/4-apollo-tutorial/client/src/assets/images/moon.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobxjs/mst-gql/ab8bd37c3cacebd6905b4842d2472cbcec1fb09e/examples/4-apollo-tutorial/client/src/assets/images/moon.jpg -------------------------------------------------------------------------------- /examples/4-apollo-tutorial/client/src/assets/images/space.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobxjs/mst-gql/ab8bd37c3cacebd6905b4842d2472cbcec1fb09e/examples/4-apollo-tutorial/client/src/assets/images/space.jpg -------------------------------------------------------------------------------- /examples/4-apollo-tutorial/client/src/components/__tests__/button.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | 3 | import { render, cleanup } from "../../test-utils" 4 | import Button from "../button" 5 | 6 | describe("Button", () => { 7 | // automatically unmount and cleanup DOM after the test is finished. 8 | afterEach(cleanup) 9 | 10 | it("renders without error", () => { 11 | render() 12 | }) 13 | }) 14 | -------------------------------------------------------------------------------- /examples/4-apollo-tutorial/client/src/components/__tests__/footer.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | 3 | import { renderApollo, cleanup } from "../../test-utils" 4 | import Footer from "../footer" 5 | 6 | describe("Footer", () => { 7 | // automatically unmount and cleanup DOM after the test is finished. 8 | afterEach(cleanup) 9 | 10 | it("renders without error", () => { 11 | renderApollo(