├── create-keystone-app ├── src │ ├── utils.ts │ ├── checkVersion.ts │ └── index.ts ├── bin.js ├── starter │ ├── _gitignore │ ├── tsconfig.json │ ├── package.json │ ├── CHANGELOG.md │ ├── keystone.ts │ ├── schema.prisma │ ├── auth.ts │ ├── README.md │ ├── schema.ts │ └── schema.graphql ├── package.json └── CHANGELOG.md ├── .prettierignore ├── babel.config.json ├── .changeset ├── config.json └── README.md ├── renovate.json ├── RELEASE.md ├── .github ├── actions │ └── ci-setup │ │ └── action.yml └── workflows │ ├── version_packages.yml │ ├── tests.yml │ └── publish.yml ├── README.md ├── LICENSE ├── package.json ├── .gitignore ├── tsconfig.json └── index.test.ts /create-keystone-app/src/utils.ts: -------------------------------------------------------------------------------- 1 | export class UserError extends Error {} 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | tsconfig.json 2 | dist 3 | CHANGELOG.md 4 | .changeset 5 | .keystone 6 | -------------------------------------------------------------------------------- /create-keystone-app/bin.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 'use strict'; 3 | require('.'); 4 | -------------------------------------------------------------------------------- /create-keystone-app/starter/_gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .keystone/ 3 | keystone.db 4 | *.log 5 | -------------------------------------------------------------------------------- /babel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["@babel/preset-env", { "targets": { "node": 12 } }], 4 | "@babel/preset-typescript", 5 | "@babel/preset-react" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /create-keystone-app/starter/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "commonjs", 5 | "strict": true, 6 | "noEmit": true, 7 | "esModuleInterop": true, 8 | "forceConsistentCasingInFileNames": true 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /.changeset/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/@changesets/config@1.5.0/schema.json", 3 | "changelog": [ 4 | "@changesets/changelog-github", 5 | { "repo": "keystonejs/create-keystone-app" } 6 | ], 7 | "commit": false, 8 | "linked": [], 9 | "access": "public", 10 | "baseBranch": "main", 11 | "updateInternalDependencies": "patch", 12 | "ignore": [] 13 | } 14 | -------------------------------------------------------------------------------- /create-keystone-app/starter/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "keystone-app", 3 | "version": "1.0.2", 4 | "private": true, 5 | "scripts": { 6 | "dev": "keystone dev", 7 | "start": "keystone start", 8 | "build": "keystone build", 9 | "postinstall": "keystone build --no-ui --frozen" 10 | }, 11 | "dependencies": { 12 | "@keystone-6/auth": "^7.0.0", 13 | "@keystone-6/core": "^5.0.0", 14 | "@keystone-6/fields-document": "^8.0.0", 15 | "typescript": "^5.0.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.changeset/README.md: -------------------------------------------------------------------------------- 1 | # Changesets 2 | 3 | Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works 4 | with multi-package repos, or single-package repos to help you version and publish your code. You can 5 | find the full documentation for it [in our repository](https://github.com/changesets/changesets) 6 | 7 | We have a quick list of common questions to get you started engaging with this project in 8 | [our documentation](https://github.com/changesets/changesets/blob/master/docs/common-questions.md) 9 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["config:base"], 3 | "lockFileMaintenance": { "enabled": true }, 4 | "separateMinorPatch": false, 5 | "separateMajorMinor": true, 6 | "prConcurrentLimit": 0, 7 | "prHourlyLimit": 3, 8 | "baseBranches": ["main"], 9 | "packageRules": [ 10 | { 11 | "packagePatterns": ["keystone"], 12 | "groupName": "Keystone" 13 | } 14 | ], 15 | "rangeStrategy": "replace", 16 | "schedule": ["before 7am on Tuesday", "before 7am on Wednesday"], 17 | "timezone": "Australia/Sydney", 18 | "updateNotScheduled": false 19 | } 20 | -------------------------------------------------------------------------------- /RELEASE.md: -------------------------------------------------------------------------------- 1 | # Release Guidelines 2 | 3 | ## How to do a release 4 | 5 | > **Note** 6 | > This can only be done by a short list of contributors 7 | 8 | The [`Version Packages`](https://github.com/keystonejs/create-keystone-app/actions/workflows/version_packages.yml) GitHub action should trigger automatically in the presence of any merged `.changesets`. 9 | 10 | Upon merging the `Version Packages` pull request to `main`, you can trigger the `Publish` GitHub action. 11 | This repository only has 1 public package, `create-keystone-app`; if no changesets exist for that package, nothing will be published. 12 | -------------------------------------------------------------------------------- /create-keystone-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "create-keystone-app", 3 | "version": "9.0.1", 4 | "main": "dist/create-keystone-app.cjs.js", 5 | "files": [ 6 | "dist", 7 | "starter", 8 | "!dist/**/*.d.ts", 9 | "!starter/node_modules" 10 | ], 11 | "bin": "./bin.js", 12 | "license": "MIT", 13 | "dependencies": { 14 | "@babel/runtime": "^7.18.9", 15 | "chalk": "^4.1.2", 16 | "enquirer": "^2.3.6", 17 | "execa": "^5.1.1", 18 | "fs-extra": "^11.0.0", 19 | "meow": "^9.0.0", 20 | "ora": "^5.4.1", 21 | "package-json": "^7.0.0", 22 | "semver": "^7.3.7", 23 | "terminal-link": "^2.1.1" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /create-keystone-app/src/checkVersion.ts: -------------------------------------------------------------------------------- 1 | import getPackageJson from 'package-json'; 2 | import currentPkgJson from '../package.json'; 3 | import * as semver from 'semver'; 4 | 5 | export async function checkVersion() { 6 | try { 7 | const { version } = await getPackageJson('create-keystone-app'); 8 | if (typeof version !== 'string') { 9 | throw new Error( 10 | 'version from package metadata was expected to be a string but was not' 11 | ); 12 | } 13 | if (semver.lt(currentPkgJson.version, version)) { 14 | console.error( 15 | `⚠️ You're running an old version of create-keystone-app, please update to ${version}` 16 | ); 17 | } 18 | } catch (err) { 19 | console.error( 20 | 'A problem occurred fetching the latest version of create-keystone-app' 21 | ); 22 | console.error(err); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.github/actions/ci-setup/action.yml: -------------------------------------------------------------------------------- 1 | name: 'CI setup' 2 | runs: 3 | using: 'composite' 4 | steps: 5 | - name: Setup Node.js LTS 6 | uses: actions/setup-node@v3 7 | with: 8 | # preferably lts/*, but we hit API limits when querying that 9 | node-version: 16 10 | registry-url: 'https://registry.npmjs.org' 11 | 12 | - name: Get yarn cache directory path 13 | id: yarn-cache-dir-path 14 | run: echo "::set-output name=dir::$(yarn cache dir)" 15 | shell: bash 16 | 17 | - uses: actions/cache@main 18 | id: yarn-cache 19 | with: 20 | path: | 21 | ${{ steps.yarn-cache-dir-path.outputs.dir }} 22 | node_modules 23 | key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }} 24 | restore-keys: | 25 | ${{ runner.os }}-yarn- 26 | 27 | - name: Install Dependencies 28 | run: yarn --frozen-lockfile 29 | shell: bash 30 | -------------------------------------------------------------------------------- /.github/workflows/version_packages.yml: -------------------------------------------------------------------------------- 1 | name: Version Packages 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | workflow_dispatch: 8 | 9 | jobs: 10 | release_pr: 11 | name: Pull Request 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@main 15 | with: 16 | fetch-depth: 0 17 | persist-credentials: false 18 | 19 | - uses: actions/setup-node@main 20 | with: 21 | node-version: lts/* 22 | 23 | - run: yarn 24 | 25 | - uses: changesets/action@v1 26 | env: 27 | # note that we're not using the GH token provided by Actions here because Actions has a rule that Actions 28 | # will not run as the result of another Action so CI wouldn't run on the release PRs then 29 | # we can get around it by using a personal access token from a GH account 30 | GITHUB_TOKEN: ${{ secrets.KEYSTONE_RELEASE_BOT_GITHUB_TOKEN }} 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # create-keystone-app 2 | 3 | This starter-kit will get you up and running with a new [Keystone](https://keystonejs.com) project in just a few minutes. Run one of the following commands to get started. 4 | 5 | ``` 6 | yarn create keystone-app 7 | ``` 8 | 9 | or 10 | 11 | ``` 12 | npm init keystone-app 13 | ``` 14 | 15 | or 16 | 17 | ``` 18 | npx create-keystone-app 19 | ``` 20 | 21 | See the [Keystone website](https://keystonejs.com/docs/walkthroughs/getting-started-with-create-keystone-app) for more details on how to get started with Keystone. 22 | 23 | ## FAQ 24 | 25 | ### Why is this in a different repository to the rest of Keystone 26 | 27 | We want to be able to have tests that create an app with `create-keystone-app` and then test to ensure it starts. This wouldn't be possible in the main Keystone repo since it relies on the rest of the packages being published (unless we published the packages to a local registry like Verdaccio but that would still add complexity and the chance for something to go wrong compared to the proper published versions to npm). 28 | -------------------------------------------------------------------------------- /create-keystone-app/starter/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # keystone-app 2 | 3 | ## 1.0.2 4 | 5 | ### Patch Changes 6 | 7 | - [`3b4360a`](https://github.com/keystonejs/create-keystone-app/commit/3b4360a114f00094e40fdc89dd4c82e1456b9ae5) Thanks [@dcousens](https://github.com/dcousens)! - Fix graphql@^15.8.0 and next@12.2.4 as pseudo-peer dependencies until next `@keystone-6/core` release 8 | 9 | ## 1.0.1 10 | 11 | ### Patch Changes 12 | 13 | - [#278](https://github.com/keystonejs/create-keystone-app/pull/278) [`26f9a79`](https://github.com/keystonejs/create-keystone-app/commit/26f9a79ef913915bac85657884f85ff7e4da46c2) Thanks [@Noviny](https://github.com/Noviny)! - Improve schema options for linking authors to posts: 14 | - Add `inlineConnect: true` to the post's relationship to users 15 | - Remove authors from being inline-creatable 16 | 17 | * [#319](https://github.com/keystonejs/create-keystone-app/pull/319) [`94a859e`](https://github.com/keystonejs/create-keystone-app/commit/94a859e43123d2f348d5e21551d59bd7e257aa81) Thanks [@Achisingh](https://github.com/Achisingh)! - Fix dependencies and update schemas for the latest `keystone-6` release 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Thinkmill Labs Pty Ltd 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 | -------------------------------------------------------------------------------- /create-keystone-app/starter/keystone.ts: -------------------------------------------------------------------------------- 1 | // Welcome to Keystone! 2 | // 3 | // This file is what Keystone uses as the entry-point to your headless backend 4 | // 5 | // Keystone imports the default export of this file, expecting a Keystone configuration object 6 | // you can find out more at https://keystonejs.com/docs/apis/config 7 | 8 | import { config } from '@keystone-6/core'; 9 | 10 | // to keep this file tidy, we define our schema in a different file 11 | import { lists } from './schema'; 12 | 13 | // authentication is configured separately here too, but you might move this elsewhere 14 | // when you write your list-level access control functions, as they typically rely on session data 15 | import { withAuth, session } from './auth'; 16 | 17 | export default withAuth( 18 | config({ 19 | db: { 20 | // we're using sqlite for the fastest startup experience 21 | // for more information on what database might be appropriate for you 22 | // see https://keystonejs.com/docs/guides/choosing-a-database#title 23 | provider: 'sqlite', 24 | url: 'file:./keystone.db', 25 | }, 26 | lists, 27 | session, 28 | }) 29 | ); 30 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | workflow_dispatch: 9 | 10 | concurrency: 11 | group: tests-${{ github.ref }} 12 | cancel-in-progress: true 13 | 14 | jobs: 15 | tests: 16 | name: Tests 17 | runs-on: ubuntu-latest 18 | env: 19 | DATABASE_URL: 'file:./keystone.db' 20 | strategy: 21 | fail-fast: false 22 | matrix: 23 | createProject: ['cka', 'cli'] 24 | steps: 25 | - uses: actions/checkout@main 26 | - uses: ./.github/actions/ci-setup 27 | 28 | - uses: microsoft/playwright-github-action@v1 29 | - run: node ./node_modules/playwright/install.js 30 | - name: Tests 31 | run: yarn test:unit 32 | timeout-minutes: 10 33 | env: 34 | TEST_MATRIX_NAME: ${{ matrix.createProject }} 35 | 36 | linting: 37 | name: Linting 38 | runs-on: ubuntu-latest 39 | steps: 40 | - uses: actions/checkout@main 41 | - uses: ./.github/actions/ci-setup 42 | 43 | - name: Prettier 44 | run: yarn lint:prettier 45 | 46 | - name: TypeScript 47 | run: yarn lint:types 48 | 49 | - name: Preconstruct 50 | run: yarn build 51 | -------------------------------------------------------------------------------- /create-keystone-app/starter/schema.prisma: -------------------------------------------------------------------------------- 1 | // This file is automatically generated by Keystone, do not modify it manually. 2 | // Modify your Keystone config when you want to change this. 3 | 4 | datasource sqlite { 5 | url = env("DATABASE_URL") 6 | shadowDatabaseUrl = env("SHADOW_DATABASE_URL") 7 | provider = "sqlite" 8 | } 9 | 10 | generator client { 11 | provider = "prisma-client-js" 12 | } 13 | 14 | model User { 15 | id String @id @default(cuid()) 16 | name String @default("") 17 | email String @unique @default("") 18 | password String 19 | posts Post[] @relation("Post_author") 20 | createdAt DateTime? @default(now()) 21 | } 22 | 23 | model Post { 24 | id String @id @default(cuid()) 25 | title String @default("") 26 | content String @default("[{\"type\":\"paragraph\",\"children\":[{\"text\":\"\"}]}]") 27 | author User? @relation("Post_author", fields: [authorId], references: [id]) 28 | authorId String? @map("author") 29 | tags Tag[] @relation("Post_tags") 30 | 31 | @@index([authorId]) 32 | } 33 | 34 | model Tag { 35 | id String @id @default(cuid()) 36 | name String @default("") 37 | posts Post[] @relation("Post_tags") 38 | } 39 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | publish: 8 | name: Publish 9 | runs-on: ubuntu-latest 10 | environment: release 11 | steps: 12 | - name: Checkout Repo 13 | uses: actions/checkout@master 14 | with: 15 | fetch-depth: 0 16 | persist-credentials: false 17 | 18 | - name: Setup Node.js LTS 19 | uses: actions/setup-node@master 20 | with: 21 | node-version: lts/* 22 | 23 | - name: Install Dependencies 24 | # we have a postinstall script that uses is-ci which doesn't yet detect GitHub Actions 25 | run: CI=true yarn 26 | 27 | - name: Publish to npm 28 | uses: changesets/action@v1 29 | with: 30 | version: yarn no-run-version-packages 31 | publish: yarn publish-changed 32 | createGithubReleases: false 33 | env: 34 | # note that we're not using the GH token provided by Actions here because Actions has a rule that Actions 35 | # will not run as the result of another Action so CI wouldn't run on the release PRs then 36 | # we can get around it by using a personal access token from a GH account 37 | GITHUB_TOKEN: ${{ secrets.KEYSTONE_RELEASE_BOT_GITHUB_TOKEN }} 38 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 39 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "create-keystone-app-repo", 3 | "private": true, 4 | "workspaces": [ 5 | "create-keystone-app", 6 | "create-keystone-app/starter" 7 | ], 8 | "license": "MIT", 9 | "dependencies": { 10 | "@babel/core": "^7.18.10", 11 | "@babel/plugin-transform-runtime": "^7.18.10", 12 | "@babel/preset-env": "^7.18.10", 13 | "@babel/preset-react": "^7.18.6", 14 | "@babel/preset-typescript": "^7.18.6", 15 | "@changesets/changelog-github": "^0.4.6", 16 | "@changesets/cli": "^2.24.2", 17 | "@manypkg/cli": "^0.20.0", 18 | "@preconstruct/cli": "^2.2.1", 19 | "@types/async-retry": "^1.4.4", 20 | "@types/fs-extra": "^11.0.0", 21 | "@types/jest": "^29.0.0", 22 | "async-retry": "^1.3.3", 23 | "jest": "^29.0.0", 24 | "playwright": "^1.24.2", 25 | "prettier": "^2.7.1", 26 | "tempy": "^1.0.1", 27 | "tree-kill": "^1.2.2", 28 | "typescript": "^5.0.0" 29 | }, 30 | "scripts": { 31 | "postinstall": "preconstruct dev && manypkg check && cd create-keystone-app/starter && yarn postinstall", 32 | "build": "preconstruct build", 33 | "format": "prettier --write \"**/*.{js,json,ts,tsx,md}\"", 34 | "lint:prettier": "prettier --list-different \"**/*.{js,json,ts,tsx,md}\"", 35 | "lint:types": "tsc", 36 | "test": "yarn lint:prettier && yarn lint:types", 37 | "test:unit": "DATABASE_URL=file:./keystone.db yarn jest --runInBand", 38 | "publish-changed": "yarn build && changeset publish", 39 | "no-run-version-packages": "echo \"This workflow should not be run when there are changesets on main\" && exit 1" 40 | }, 41 | "prettier": { 42 | "singleQuote": true 43 | }, 44 | "preconstruct": { 45 | "packages": [ 46 | "create-keystone-app" 47 | ] 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | 106 | .keystone 107 | keystone.db 108 | 109 | # OS files 110 | .DS_Store 111 | 112 | # IDE files 113 | *.sublime-* -------------------------------------------------------------------------------- /create-keystone-app/starter/auth.ts: -------------------------------------------------------------------------------- 1 | // Welcome to some authentication for Keystone 2 | // 3 | // This is using @keystone-6/auth to add the following 4 | // - A sign-in page for your Admin UI 5 | // - A cookie-based stateless session strategy 6 | // - Using a User email as the identifier 7 | // - 30 day cookie expiration 8 | // 9 | // This file does not configure what Users can do, and the default for this starter 10 | // project is to allow anyone - logged-in or not - to do anything. 11 | // 12 | // If you want to prevent random people on the internet from accessing your data, 13 | // you can find out how by reading https://keystonejs.com/docs/guides/auth-and-access-control 14 | // 15 | // If you want to learn more about how our out-of-the-box authentication works, please 16 | // read https://keystonejs.com/docs/apis/auth#authentication-api 17 | 18 | import { randomBytes } from 'crypto'; 19 | import { createAuth } from '@keystone-6/auth'; 20 | 21 | // see https://keystonejs.com/docs/apis/session for the session docs 22 | import { statelessSessions } from '@keystone-6/core/session'; 23 | 24 | // for a stateless session, a SESSION_SECRET should always be provided 25 | // especially in production (statelessSessions will throw if SESSION_SECRET is undefined) 26 | let sessionSecret = process.env.SESSION_SECRET; 27 | if (!sessionSecret && process.env.NODE_ENV !== 'production') { 28 | sessionSecret = randomBytes(32).toString('hex'); 29 | } 30 | 31 | // withAuth is a function we can use to wrap our base configuration 32 | const { withAuth } = createAuth({ 33 | listKey: 'User', 34 | identityField: 'email', 35 | 36 | // this is a GraphQL query fragment for fetching what data will be attached to a context.session 37 | // this can be helpful for when you are writing your access control functions 38 | // you can find out more at https://keystonejs.com/docs/guides/auth-and-access-control 39 | sessionData: 'name createdAt', 40 | secretField: 'password', 41 | 42 | // WARNING: remove initFirstItem functionality in production 43 | // see https://keystonejs.com/docs/config/auth#init-first-item for more 44 | initFirstItem: { 45 | // if there are no items in the database, by configuring this field 46 | // you are asking the Keystone AdminUI to create a new user 47 | // providing inputs for these fields 48 | fields: ['name', 'email', 'password'], 49 | 50 | // it uses context.sudo() to do this, which bypasses any access control you might have 51 | // you shouldn't use this in production 52 | }, 53 | }); 54 | 55 | // statelessSessions uses cookies for session tracking 56 | // these cookies have an expiry, in seconds 57 | // we use an expiry of 30 days for this starter 58 | const sessionMaxAge = 60 * 60 * 24 * 30; 59 | 60 | // you can find out more at https://keystonejs.com/docs/apis/session#session-api 61 | const session = statelessSessions({ 62 | maxAge: sessionMaxAge, 63 | secret: sessionSecret!, 64 | }); 65 | 66 | export { withAuth, session }; 67 | -------------------------------------------------------------------------------- /create-keystone-app/starter/README.md: -------------------------------------------------------------------------------- 1 | # Keystone Project Starter 2 | 3 | Welcome to Keystone! 4 | 5 | Run 6 | 7 | ``` 8 | yarn dev 9 | ``` 10 | 11 | To view the config for your new app, look at [./keystone.ts](./keystone.ts) 12 | 13 | This project starter is designed to give you a sense of the power Keystone can offer you, and show off some of its main features. It's also a pretty simple setup if you want to build out from it. 14 | 15 | We recommend you use this alongside our [getting started walkthrough](https://keystonejs.com/docs/walkthroughs/getting-started-with-create-keystone-app) which will walk you through what you get as part of this starter. 16 | 17 | If you want an overview of all the features Keystone offers, check out our [features](https://keystonejs.com/why-keystone#features) page. 18 | 19 | ## Some Quick Notes On Getting Started 20 | 21 | ### Changing the database 22 | 23 | We've set you up with an [SQLite database](https://keystonejs.com/docs/apis/config#sqlite) for ease-of-use. If you're wanting to use PostgreSQL, you can! 24 | 25 | Just change the `db` property on line 16 of the Keystone file [./keystone.ts](./keystone.ts) to 26 | 27 | ```typescript 28 | db: { 29 | provider: 'postgresql', 30 | url: process.env.DATABASE_URL || 'DATABASE_URL_TO_REPLACE', 31 | } 32 | ``` 33 | 34 | And provide your database url from PostgreSQL. 35 | 36 | For more on database configuration, check out or [DB API Docs](https://keystonejs.com/docs/apis/config#db) 37 | 38 | ### Auth 39 | 40 | We've put auth into its own file to make this humble starter easier to navigate. To explore it without auth turned on, comment out the `isAccessAllowed` on line 21 of the Keystone file [./keystone.ts](./keystone.ts). 41 | 42 | For more on auth, check out our [Authentication API Docs](https://keystonejs.com/docs/apis/auth#authentication-api) 43 | 44 | ### Adding a frontend 45 | 46 | As a Headless CMS, Keystone can be used with any frontend that uses GraphQL. It provides a GraphQL endpoint you can write queries against at `/api/graphql` (by default [http://localhost:3000/api/graphql](http://localhost:3000/api/graphql)). At Thinkmill, we tend to use [Next.js](https://nextjs.org/) and [Apollo GraphQL](https://www.apollographql.com/docs/react/get-started/) as our frontend and way to write queries, but if you have your own favourite, feel free to use it. 47 | 48 | A walkthrough on how to do this is forthcoming, but in the meantime our [todo example](https://github.com/keystonejs/keystone-react-todo-demo) shows a Keystone set up with a frontend. For a more full example, you can also look at an example app we built for [Prisma Day 2021](https://github.com/keystonejs/prisma-day-2021-workshop) 49 | 50 | ### Embedding Keystone in a Next.js frontend 51 | 52 | While Keystone works as a standalone app, you can embed your Keystone app into a [Next.js](https://nextjs.org/) app. This is quite a different setup to the starter, and we recommend checking out our walkthrough for that [here](https://keystonejs.com/docs/walkthroughs/embedded-mode-with-sqlite-nextjs#how-to-embed-keystone-sq-lite-in-a-next-js-app). 53 | -------------------------------------------------------------------------------- /create-keystone-app/src/index.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs-extra'; 2 | import path from 'path'; 3 | import meow from 'meow'; 4 | import enquirer from 'enquirer'; 5 | import execa, { ExecaError } from 'execa'; 6 | import ora from 'ora'; 7 | import { checkVersion } from './checkVersion'; 8 | import { UserError } from './utils'; 9 | import c from 'chalk'; 10 | import terminalLink from 'terminal-link'; 11 | 12 | const starterDir = path.normalize(`${__dirname}/../starter`); 13 | 14 | const cli = meow( 15 | ` 16 | Usage 17 | $ create-keystone-app [directory] 18 | ` 19 | ); 20 | 21 | type Args = { 22 | directory: string; 23 | }; 24 | 25 | const versionInfo = () => { 26 | process.stdout.write('\n'); 27 | console.log(`✨ You're about to generate a project using ${c.bold( 28 | 'Keystone 6' 29 | )} packages. 30 | `); 31 | }; 32 | 33 | async function normalizeArgs(): Promise { 34 | let directory = cli.input[0]; 35 | if (!directory) { 36 | ({ directory } = await enquirer.prompt({ 37 | type: 'input', 38 | name: 'directory', 39 | message: 40 | 'What directory should create-keystone-app generate your app into?', 41 | validate: (x) => !!x, 42 | })); 43 | process.stdout.write('\n'); 44 | } 45 | return { 46 | directory: path.resolve(directory), 47 | }; 48 | } 49 | 50 | const installDeps = async (cwd: string): Promise<'yarn' | 'npm'> => { 51 | const spinner = ora( 52 | 'Installing dependencies with yarn. This may take a few minutes.' 53 | ).start(); 54 | try { 55 | await execa('yarn', ['install'], { cwd }); 56 | spinner.succeed('Installed dependencies with yarn.'); 57 | return 'yarn'; 58 | } catch (_err: any) { 59 | let err: ExecaError = _err; 60 | if (err.failed) { 61 | process.stdout.write('\n'); 62 | spinner.warn('Failed to install with yarn.'); 63 | spinner.start( 64 | 'Installing dependencies with npm. This may take a few minutes.' 65 | ); 66 | try { 67 | await execa('npm', ['install'], { cwd }); 68 | spinner.succeed('Installed dependencies with npm.'); 69 | } catch (npmErr) { 70 | spinner.fail('Failed to install with npm.'); 71 | throw npmErr; 72 | } 73 | process.stdout.write('\n'); 74 | return 'npm'; 75 | } 76 | throw err; 77 | } 78 | }; 79 | 80 | (async () => { 81 | versionInfo(); 82 | await checkVersion(); 83 | const normalizedArgs = await normalizeArgs(); 84 | await fs.mkdir(normalizedArgs.directory); 85 | await Promise.all([ 86 | ...[ 87 | '_gitignore', 88 | 'schema.ts', 89 | 'package.json', 90 | 'tsconfig.json', 91 | 'schema.graphql', 92 | 'schema.prisma', 93 | 'keystone.ts', 94 | 'auth.ts', 95 | 'README.md', 96 | ].map((filename) => 97 | fs.copyFile( 98 | path.join(starterDir, filename), 99 | path.join(normalizedArgs.directory, filename.replace(/^_/, '.')) 100 | ) 101 | ), 102 | ]); 103 | const packageManager = await installDeps(normalizedArgs.directory); 104 | const relativeProjectDir = path.relative( 105 | process.cwd(), 106 | normalizedArgs.directory 107 | ); 108 | process.stdout.write('\n'); 109 | console.log(`🎉 Keystone created a starter project in: ${c.bold( 110 | relativeProjectDir 111 | )} 112 | 113 | ${c.bold('To launch your app, run:')} 114 | 115 | - cd ${relativeProjectDir} 116 | - ${packageManager === 'yarn' ? 'yarn' : 'npm run'} dev 117 | 118 | ${c.bold('Next steps:')} 119 | 120 | - Read ${c.bold( 121 | `${relativeProjectDir}${path.sep}README.md` 122 | )} for additional getting started details. 123 | - Edit ${c.bold( 124 | `${relativeProjectDir}${path.sep}keystone.ts` 125 | )} to customize your app. 126 | - ${terminalLink('Open the Admin UI', 'http://localhost:3000')} 127 | - ${terminalLink('Open the Graphql API', 'http://localhost:3000/api/graphql')} 128 | - ${terminalLink('Read the docs', 'https://keystonejs.com')} 129 | - ${terminalLink( 130 | 'Star Keystone on GitHub', 131 | 'https://github.com/keystonejs/keystone' 132 | )} 133 | `); 134 | })().catch((err) => { 135 | if (err instanceof UserError) { 136 | console.error(err.message); 137 | } else { 138 | console.error(err); 139 | } 140 | process.exit(1); 141 | }); 142 | -------------------------------------------------------------------------------- /create-keystone-app/starter/schema.ts: -------------------------------------------------------------------------------- 1 | // Welcome to your schema 2 | // Schema driven development is Keystone's modus operandi 3 | // 4 | // This file is where we define the lists, fields and hooks for our data. 5 | // If you want to learn more about how lists are configured, please read 6 | // - https://keystonejs.com/docs/config/lists 7 | 8 | import { list } from '@keystone-6/core'; 9 | import { allowAll } from '@keystone-6/core/access'; 10 | 11 | // see https://keystonejs.com/docs/fields/overview for the full list of fields 12 | // this is a few common fields for an example 13 | import { 14 | text, 15 | relationship, 16 | password, 17 | timestamp, 18 | select, 19 | } from '@keystone-6/core/fields'; 20 | 21 | // the document field is a more complicated field, so it has it's own package 22 | import { document } from '@keystone-6/fields-document'; 23 | // if you want to make your own fields, see https://keystonejs.com/docs/guides/custom-fields 24 | 25 | // when using Typescript, you can refine your types to a stricter subset by importing 26 | // the generated types from '.keystone/types' 27 | import type { Lists } from '.keystone/types'; 28 | 29 | export const lists: Lists = { 30 | User: list({ 31 | // WARNING 32 | // for this starter project, anyone can create, query, update and delete anything 33 | // if you want to prevent random people on the internet from accessing your data, 34 | // you can find out more at https://keystonejs.com/docs/guides/auth-and-access-control 35 | access: allowAll, 36 | 37 | // this is the fields for our User list 38 | fields: { 39 | // by adding isRequired, we enforce that every User should have a name 40 | // if no name is provided, an error will be displayed 41 | name: text({ validation: { isRequired: true } }), 42 | 43 | email: text({ 44 | validation: { isRequired: true }, 45 | // by adding isIndexed: 'unique', we're saying that no user can have the same 46 | // email as another user - this may or may not be a good idea for your project 47 | isIndexed: 'unique', 48 | }), 49 | 50 | password: password({ validation: { isRequired: true } }), 51 | 52 | // we can use this field to see what Posts this User has authored 53 | // more on that in the Post list below 54 | posts: relationship({ ref: 'Post.author', many: true }), 55 | 56 | createdAt: timestamp({ 57 | // this sets the timestamp to Date.now() when the user is first created 58 | defaultValue: { kind: 'now' }, 59 | }), 60 | }, 61 | }), 62 | 63 | Post: list({ 64 | // WARNING 65 | // for this starter project, anyone can create, query, update and delete anything 66 | // if you want to prevent random people on the internet from accessing your data, 67 | // you can find out more at https://keystonejs.com/docs/guides/auth-and-access-control 68 | access: allowAll, 69 | 70 | // this is the fields for our Post list 71 | fields: { 72 | title: text({ validation: { isRequired: true } }), 73 | 74 | // the document field can be used for making rich editable content 75 | // you can find out more at https://keystonejs.com/docs/guides/document-fields 76 | content: document({ 77 | formatting: true, 78 | layouts: [ 79 | [1, 1], 80 | [1, 1, 1], 81 | [2, 1], 82 | [1, 2], 83 | [1, 2, 1], 84 | ], 85 | links: true, 86 | dividers: true, 87 | }), 88 | 89 | // with this field, you can set a User as the author for a Post 90 | author: relationship({ 91 | // we could have used 'User', but then the relationship would only be 1-way 92 | ref: 'User.posts', 93 | 94 | // this is some customisations for changing how this will look in the AdminUI 95 | ui: { 96 | displayMode: 'cards', 97 | cardFields: ['name', 'email'], 98 | inlineEdit: { fields: ['name', 'email'] }, 99 | linkToItem: true, 100 | inlineConnect: true, 101 | }, 102 | 103 | // a Post can only have one author 104 | // this is the default, but we show it here for verbosity 105 | many: false, 106 | }), 107 | 108 | // with this field, you can add some Tags to Posts 109 | tags: relationship({ 110 | // we could have used 'Tag', but then the relationship would only be 1-way 111 | ref: 'Tag.posts', 112 | 113 | // a Post can have many Tags, not just one 114 | many: true, 115 | 116 | // this is some customisations for changing how this will look in the AdminUI 117 | ui: { 118 | displayMode: 'cards', 119 | cardFields: ['name'], 120 | inlineEdit: { fields: ['name'] }, 121 | linkToItem: true, 122 | inlineConnect: true, 123 | inlineCreate: { fields: ['name'] }, 124 | }, 125 | }), 126 | }, 127 | }), 128 | 129 | // this last list is our Tag list, it only has a name field for now 130 | Tag: list({ 131 | // WARNING 132 | // for this starter project, anyone can create, query, update and delete anything 133 | // if you want to prevent random people on the internet from accessing your data, 134 | // you can find out more at https://keystonejs.com/docs/guides/auth-and-access-control 135 | access: allowAll, 136 | 137 | // setting this to isHidden for the user interface prevents this list being visible in the Admin UI 138 | ui: { 139 | isHidden: true, 140 | }, 141 | 142 | // this is the fields for our Tag list 143 | fields: { 144 | name: text(), 145 | // this can be helpful to find out all the Posts associated with a Tag 146 | posts: relationship({ ref: 'Post.tags', many: true }), 147 | }, 148 | }), 149 | }; 150 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Visit https://aka.ms/tsconfig.json to read more about this file */ 4 | 5 | /* Basic Options */ 6 | // "incremental": true, /* Enable incremental compilation */ 7 | "target": "esnext", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ 8 | "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ 9 | // "lib": [], /* Specify library files to be included in the compilation. */ 10 | // "allowJs": true, /* Allow javascript files to be compiled. */ 11 | // "checkJs": true, /* Report errors in .js files. */ 12 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ 13 | // "declaration": true, /* Generates corresponding '.d.ts' file. */ 14 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ 15 | // "sourceMap": true, /* Generates corresponding '.map' file. */ 16 | // "outFile": "./", /* Concatenate and emit output to single file. */ 17 | // "outDir": "./", /* Redirect output structure to the directory. */ 18 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 19 | // "composite": true, /* Enable project compilation */ 20 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 21 | // "removeComments": true, /* Do not emit comments to output. */ 22 | "noEmit": true, /* Do not emit outputs. */ 23 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 24 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 25 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 26 | 27 | /* Strict Type-Checking Options */ 28 | "strict": true, /* Enable all strict type-checking options. */ 29 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 30 | // "strictNullChecks": true, /* Enable strict null checks. */ 31 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 32 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ 33 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 34 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 35 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 36 | 37 | /* Additional Checks */ 38 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 39 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 40 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 41 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 42 | // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ 43 | 44 | /* Module Resolution Options */ 45 | // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 46 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 47 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 48 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 49 | // "typeRoots": [], /* List of folders to include type definitions from. */ 50 | // "types": [], /* Type declaration files to be included in compilation. */ 51 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 52 | "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ 53 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 54 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 55 | 56 | /* Source Map Options */ 57 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 58 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 59 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 60 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 61 | 62 | /* Experimental Options */ 63 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 64 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 65 | "resolveJsonModule": true, 66 | /* Advanced Options */ 67 | "skipLibCheck": true, /* Skip type checking of declaration files. */ 68 | "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /index.test.ts: -------------------------------------------------------------------------------- 1 | import * as playwright from 'playwright'; 2 | import { execFile } from 'child_process'; 3 | import tempy from 'tempy'; 4 | import path from 'path'; 5 | import { promisify } from 'util'; 6 | import _treeKill from 'tree-kill'; 7 | import retry from 'async-retry'; 8 | import { randomBytes } from 'crypto'; 9 | 10 | const treeKill = promisify(_treeKill); 11 | const execFileAsync = promisify(execFile); 12 | 13 | // some tests are slow 14 | jest.setTimeout(100000); 15 | 16 | // WARNING: 17 | // the order of tests is important, and why we use --runInBand 18 | // `keystone dev` creates the database for production 19 | 20 | // playwright tips 21 | // for debugging, you'll want to set the env var PWDEBUG=1 22 | // this will open the browser in a headed mode and open the inspector 23 | // https://playwright.dev/docs/inspector 24 | 25 | let projectDir = path.join(__dirname, 'create-keystone-app', 'starter'); 26 | // cli and cka tests use different prisma client locations (only used for calling Prisma's deleteMany) 27 | let prismaClientLocation = '.prisma/client'; 28 | 29 | if (process.env.TEST_MATRIX_NAME === 'cka') { 30 | test('can create a basic project', async () => { 31 | const cwd = tempy.directory(); 32 | const createKeystoneAppProcess = execFileAsync( 33 | 'node', 34 | [require.resolve('./create-keystone-app/bin.js'), 'test-project'], 35 | { cwd } 36 | ); 37 | createKeystoneAppProcess.child.stdout!.pipe(process.stdout); 38 | await createKeystoneAppProcess; 39 | projectDir = path.join(cwd, 'test-project'); 40 | prismaClientLocation = path.join(projectDir, 'node_modules/.prisma/client'); 41 | }); 42 | } 43 | 44 | async function startKeystone( 45 | command: 'start' | 'dev', 46 | env: Record = process.env 47 | ) { 48 | const keystoneProcess = execFile('yarn', [command], { 49 | cwd: projectDir, 50 | env, 51 | }); 52 | keystoneProcess.stdout!.pipe(process.stdout); 53 | 54 | const adminUIReady = new Promise((resolve, reject) => { 55 | keystoneProcess.stdout!.on('data', (buffer: Buffer) => { 56 | if (buffer.toString('utf-8').includes('Admin UI ready')) { 57 | return resolve(true); 58 | } 59 | }); 60 | }); 61 | 62 | const cleanupKeystoneProcess = async () => { 63 | keystoneProcess.stdout!.unpipe(process.stdout); 64 | // childProcess.kill will only kill the direct child process 65 | // so we use tree-kill to kill the process and it's children 66 | if (keystoneProcess.pid) { 67 | await treeKill(keystoneProcess.pid); 68 | } 69 | }; 70 | 71 | await adminUIReady; 72 | return cleanupKeystoneProcess; 73 | } 74 | 75 | describe.each(['development', 'production'] as const)('%s', (mode) => { 76 | let cleanupKeystoneProcess = () => {}; 77 | afterAll(async () => { 78 | cleanupKeystoneProcess(); 79 | }); 80 | 81 | if (mode === 'development') { 82 | // process.env.SESSION_SECRET is randomly generated for this 83 | test('start keystone in dev', async () => { 84 | cleanupKeystoneProcess = await startKeystone('dev'); 85 | }); 86 | } else if (mode === 'production') { 87 | const env = { 88 | NODE_ENV: 'production', 89 | SESSION_SECRET: randomBytes(32).toString('hex'), 90 | }; 91 | 92 | test('build keystone', async () => { 93 | const keystoneBuildProcess = execFileAsync('yarn', ['build'], { 94 | cwd: projectDir, 95 | env: { 96 | ...process.env, 97 | ...env, 98 | }, 99 | }); 100 | keystoneBuildProcess.child.stdout!.pipe(process.stdout); 101 | keystoneBuildProcess.child.stderr!.pipe(process.stdout); 102 | await keystoneBuildProcess; 103 | }); 104 | 105 | test('start keystone in prod', async () => { 106 | cleanupKeystoneProcess = await startKeystone('start', { 107 | ...process.env, 108 | ...env, 109 | }); 110 | }); 111 | } 112 | 113 | describe.each(['chromium'] as const)('%s', (browserName) => { 114 | let page: playwright.Page = undefined as any; 115 | let browser: playwright.Browser = undefined as any; 116 | 117 | beforeAll(async () => { 118 | await deleteAllData(prismaClientLocation); 119 | browser = await playwright[browserName].launch(); 120 | page = await browser.newPage(); 121 | page.setDefaultNavigationTimeout(6000); 122 | }); 123 | 124 | test('create user', async () => { 125 | await page.goto('http://localhost:3000'); 126 | await page.fill('label:has-text("Name") >> .. >> input', 'Admin1'); 127 | await page.fill( 128 | 'label:has-text("Email") >> .. >> input', 129 | 'admin@keystonejs.com' 130 | ); 131 | await page.click('button:has-text("Set Password")'); 132 | await page.fill('[placeholder="New Password"]', 'password'); 133 | await page.fill('[placeholder="Confirm Password"]', 'password'); 134 | await page.click('button:has-text("Get started")'); 135 | await page.uncheck('input[type="checkbox"]', { force: true }); 136 | await page.click('text=Continue'); 137 | }); 138 | 139 | test('change admin name', async () => { 140 | await page.click('h3:has-text("Users")'); 141 | await page.click('a:has-text("Admin1")'); 142 | await page.fill('label:has-text("Name") >> .. >> input', 'Admin2'); 143 | await page.click('button:has-text("Save changes")'); 144 | await page.click('nav >> text=Users'); 145 | expect(await page.textContent('a:has-text("Admin2")')).toBe('Admin2'); 146 | }); 147 | 148 | test('create a post', async () => { 149 | await page.click('nav >> text=Posts'); 150 | await page.click('a:has-text("Create Post")'); 151 | await page.fill('input[type="text"]', 'title'); 152 | await page.click('button:has-text("Create Post")'); 153 | // await page.waitForTimeout(2000); // TODO: the 'Save changes' button is painful 154 | // await page.fill('input[type="text"]', 'title again'); 155 | // await page.click('button:has-text("Save changes")'); 156 | }); 157 | 158 | afterAll(async () => { 159 | await browser.close(); 160 | }); 161 | }); 162 | }); 163 | 164 | async function deleteAllData(prismaClientLocation: string) { 165 | const { PrismaClient } = require(prismaClientLocation); 166 | 167 | const prisma = new PrismaClient(); 168 | 169 | await Promise.all(Object.values(prisma).map((x: any) => x?.deleteMany?.({}))); 170 | 171 | await prisma.$disconnect(); 172 | } 173 | -------------------------------------------------------------------------------- /create-keystone-app/starter/schema.graphql: -------------------------------------------------------------------------------- 1 | # This file is automatically generated by Keystone, do not modify it manually. 2 | # Modify your Keystone config when you want to change this. 3 | 4 | type User { 5 | id: ID! 6 | name: String 7 | email: String 8 | password: PasswordState 9 | posts(where: PostWhereInput! = {}, orderBy: [PostOrderByInput!]! = [], take: Int, skip: Int! = 0, cursor: PostWhereUniqueInput): [Post!] 10 | postsCount(where: PostWhereInput! = {}): Int 11 | createdAt: DateTime 12 | } 13 | 14 | type PasswordState { 15 | isSet: Boolean! 16 | } 17 | 18 | scalar DateTime @specifiedBy(url: "https://datatracker.ietf.org/doc/html/rfc3339#section-5.6") 19 | 20 | input UserWhereUniqueInput { 21 | id: ID 22 | email: String 23 | } 24 | 25 | input UserWhereInput { 26 | AND: [UserWhereInput!] 27 | OR: [UserWhereInput!] 28 | NOT: [UserWhereInput!] 29 | id: IDFilter 30 | name: StringFilter 31 | email: StringFilter 32 | posts: PostManyRelationFilter 33 | createdAt: DateTimeNullableFilter 34 | } 35 | 36 | input IDFilter { 37 | equals: ID 38 | in: [ID!] 39 | notIn: [ID!] 40 | lt: ID 41 | lte: ID 42 | gt: ID 43 | gte: ID 44 | not: IDFilter 45 | } 46 | 47 | input StringFilter { 48 | equals: String 49 | in: [String!] 50 | notIn: [String!] 51 | lt: String 52 | lte: String 53 | gt: String 54 | gte: String 55 | contains: String 56 | startsWith: String 57 | endsWith: String 58 | not: NestedStringFilter 59 | } 60 | 61 | input NestedStringFilter { 62 | equals: String 63 | in: [String!] 64 | notIn: [String!] 65 | lt: String 66 | lte: String 67 | gt: String 68 | gte: String 69 | contains: String 70 | startsWith: String 71 | endsWith: String 72 | not: NestedStringFilter 73 | } 74 | 75 | input PostManyRelationFilter { 76 | every: PostWhereInput 77 | some: PostWhereInput 78 | none: PostWhereInput 79 | } 80 | 81 | input DateTimeNullableFilter { 82 | equals: DateTime 83 | in: [DateTime!] 84 | notIn: [DateTime!] 85 | lt: DateTime 86 | lte: DateTime 87 | gt: DateTime 88 | gte: DateTime 89 | not: DateTimeNullableFilter 90 | } 91 | 92 | input UserOrderByInput { 93 | id: OrderDirection 94 | name: OrderDirection 95 | email: OrderDirection 96 | createdAt: OrderDirection 97 | } 98 | 99 | enum OrderDirection { 100 | asc 101 | desc 102 | } 103 | 104 | input UserUpdateInput { 105 | name: String 106 | email: String 107 | password: String 108 | posts: PostRelateToManyForUpdateInput 109 | createdAt: DateTime 110 | } 111 | 112 | input PostRelateToManyForUpdateInput { 113 | disconnect: [PostWhereUniqueInput!] 114 | set: [PostWhereUniqueInput!] 115 | create: [PostCreateInput!] 116 | connect: [PostWhereUniqueInput!] 117 | } 118 | 119 | input UserUpdateArgs { 120 | where: UserWhereUniqueInput! 121 | data: UserUpdateInput! 122 | } 123 | 124 | input UserCreateInput { 125 | name: String 126 | email: String 127 | password: String 128 | posts: PostRelateToManyForCreateInput 129 | createdAt: DateTime 130 | } 131 | 132 | input PostRelateToManyForCreateInput { 133 | create: [PostCreateInput!] 134 | connect: [PostWhereUniqueInput!] 135 | } 136 | 137 | type Post { 138 | id: ID! 139 | title: String 140 | content: Post_content_Document 141 | author: User 142 | tags(where: TagWhereInput! = {}, orderBy: [TagOrderByInput!]! = [], take: Int, skip: Int! = 0, cursor: TagWhereUniqueInput): [Tag!] 143 | tagsCount(where: TagWhereInput! = {}): Int 144 | } 145 | 146 | type Post_content_Document { 147 | document(hydrateRelationships: Boolean! = false): JSON! 148 | } 149 | 150 | input PostWhereUniqueInput { 151 | id: ID 152 | } 153 | 154 | input PostWhereInput { 155 | AND: [PostWhereInput!] 156 | OR: [PostWhereInput!] 157 | NOT: [PostWhereInput!] 158 | id: IDFilter 159 | title: StringFilter 160 | author: UserWhereInput 161 | tags: TagManyRelationFilter 162 | } 163 | 164 | input TagManyRelationFilter { 165 | every: TagWhereInput 166 | some: TagWhereInput 167 | none: TagWhereInput 168 | } 169 | 170 | input PostOrderByInput { 171 | id: OrderDirection 172 | title: OrderDirection 173 | } 174 | 175 | input PostUpdateInput { 176 | title: String 177 | content: JSON 178 | author: UserRelateToOneForUpdateInput 179 | tags: TagRelateToManyForUpdateInput 180 | } 181 | 182 | input UserRelateToOneForUpdateInput { 183 | create: UserCreateInput 184 | connect: UserWhereUniqueInput 185 | disconnect: Boolean 186 | } 187 | 188 | input TagRelateToManyForUpdateInput { 189 | disconnect: [TagWhereUniqueInput!] 190 | set: [TagWhereUniqueInput!] 191 | create: [TagCreateInput!] 192 | connect: [TagWhereUniqueInput!] 193 | } 194 | 195 | input PostUpdateArgs { 196 | where: PostWhereUniqueInput! 197 | data: PostUpdateInput! 198 | } 199 | 200 | input PostCreateInput { 201 | title: String 202 | content: JSON 203 | author: UserRelateToOneForCreateInput 204 | tags: TagRelateToManyForCreateInput 205 | } 206 | 207 | input UserRelateToOneForCreateInput { 208 | create: UserCreateInput 209 | connect: UserWhereUniqueInput 210 | } 211 | 212 | input TagRelateToManyForCreateInput { 213 | create: [TagCreateInput!] 214 | connect: [TagWhereUniqueInput!] 215 | } 216 | 217 | type Tag { 218 | id: ID! 219 | name: String 220 | posts(where: PostWhereInput! = {}, orderBy: [PostOrderByInput!]! = [], take: Int, skip: Int! = 0, cursor: PostWhereUniqueInput): [Post!] 221 | postsCount(where: PostWhereInput! = {}): Int 222 | } 223 | 224 | input TagWhereUniqueInput { 225 | id: ID 226 | } 227 | 228 | input TagWhereInput { 229 | AND: [TagWhereInput!] 230 | OR: [TagWhereInput!] 231 | NOT: [TagWhereInput!] 232 | id: IDFilter 233 | name: StringFilter 234 | posts: PostManyRelationFilter 235 | } 236 | 237 | input TagOrderByInput { 238 | id: OrderDirection 239 | name: OrderDirection 240 | } 241 | 242 | input TagUpdateInput { 243 | name: String 244 | posts: PostRelateToManyForUpdateInput 245 | } 246 | 247 | input TagUpdateArgs { 248 | where: TagWhereUniqueInput! 249 | data: TagUpdateInput! 250 | } 251 | 252 | input TagCreateInput { 253 | name: String 254 | posts: PostRelateToManyForCreateInput 255 | } 256 | 257 | """ 258 | The `JSON` scalar type represents JSON values as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf). 259 | """ 260 | scalar JSON @specifiedBy(url: "http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf") 261 | 262 | type Mutation { 263 | createUser(data: UserCreateInput!): User 264 | createUsers(data: [UserCreateInput!]!): [User] 265 | updateUser(where: UserWhereUniqueInput!, data: UserUpdateInput!): User 266 | updateUsers(data: [UserUpdateArgs!]!): [User] 267 | deleteUser(where: UserWhereUniqueInput!): User 268 | deleteUsers(where: [UserWhereUniqueInput!]!): [User] 269 | createPost(data: PostCreateInput!): Post 270 | createPosts(data: [PostCreateInput!]!): [Post] 271 | updatePost(where: PostWhereUniqueInput!, data: PostUpdateInput!): Post 272 | updatePosts(data: [PostUpdateArgs!]!): [Post] 273 | deletePost(where: PostWhereUniqueInput!): Post 274 | deletePosts(where: [PostWhereUniqueInput!]!): [Post] 275 | createTag(data: TagCreateInput!): Tag 276 | createTags(data: [TagCreateInput!]!): [Tag] 277 | updateTag(where: TagWhereUniqueInput!, data: TagUpdateInput!): Tag 278 | updateTags(data: [TagUpdateArgs!]!): [Tag] 279 | deleteTag(where: TagWhereUniqueInput!): Tag 280 | deleteTags(where: [TagWhereUniqueInput!]!): [Tag] 281 | endSession: Boolean! 282 | authenticateUserWithPassword(email: String!, password: String!): UserAuthenticationWithPasswordResult 283 | createInitialUser(data: CreateInitialUserInput!): UserAuthenticationWithPasswordSuccess! 284 | } 285 | 286 | union UserAuthenticationWithPasswordResult = UserAuthenticationWithPasswordSuccess | UserAuthenticationWithPasswordFailure 287 | 288 | type UserAuthenticationWithPasswordSuccess { 289 | sessionToken: String! 290 | item: User! 291 | } 292 | 293 | type UserAuthenticationWithPasswordFailure { 294 | message: String! 295 | } 296 | 297 | input CreateInitialUserInput { 298 | name: String 299 | email: String 300 | password: String 301 | } 302 | 303 | type Query { 304 | users(where: UserWhereInput! = {}, orderBy: [UserOrderByInput!]! = [], take: Int, skip: Int! = 0, cursor: UserWhereUniqueInput): [User!] 305 | user(where: UserWhereUniqueInput!): User 306 | usersCount(where: UserWhereInput! = {}): Int 307 | posts(where: PostWhereInput! = {}, orderBy: [PostOrderByInput!]! = [], take: Int, skip: Int! = 0, cursor: PostWhereUniqueInput): [Post!] 308 | post(where: PostWhereUniqueInput!): Post 309 | postsCount(where: PostWhereInput! = {}): Int 310 | tags(where: TagWhereInput! = {}, orderBy: [TagOrderByInput!]! = [], take: Int, skip: Int! = 0, cursor: TagWhereUniqueInput): [Tag!] 311 | tag(where: TagWhereUniqueInput!): Tag 312 | tagsCount(where: TagWhereInput! = {}): Int 313 | keystone: KeystoneMeta! 314 | authenticatedItem: AuthenticatedItem 315 | } 316 | 317 | union AuthenticatedItem = User 318 | 319 | type KeystoneMeta { 320 | adminMeta: KeystoneAdminMeta! 321 | } 322 | 323 | type KeystoneAdminMeta { 324 | lists: [KeystoneAdminUIListMeta!]! 325 | list(key: String!): KeystoneAdminUIListMeta 326 | } 327 | 328 | type KeystoneAdminUIListMeta { 329 | key: String! 330 | itemQueryName: String! 331 | listQueryName: String! 332 | hideCreate: Boolean! 333 | hideDelete: Boolean! 334 | path: String! 335 | label: String! 336 | singular: String! 337 | plural: String! 338 | description: String 339 | initialColumns: [String!]! 340 | pageSize: Int! 341 | labelField: String! 342 | fields: [KeystoneAdminUIFieldMeta!]! 343 | groups: [KeystoneAdminUIFieldGroupMeta!]! 344 | initialSort: KeystoneAdminUISort 345 | isHidden: Boolean! 346 | isSingleton: Boolean! 347 | } 348 | 349 | type KeystoneAdminUIFieldMeta { 350 | path: String! 351 | label: String! 352 | description: String 353 | isOrderable: Boolean! 354 | isFilterable: Boolean! 355 | isNonNull: [KeystoneAdminUIFieldMetaIsNonNull!] 356 | fieldMeta: JSON 357 | viewsIndex: Int! 358 | customViewsIndex: Int 359 | createView: KeystoneAdminUIFieldMetaCreateView! 360 | listView: KeystoneAdminUIFieldMetaListView! 361 | itemView(id: ID): KeystoneAdminUIFieldMetaItemView 362 | search: QueryMode 363 | } 364 | 365 | enum KeystoneAdminUIFieldMetaIsNonNull { 366 | read 367 | create 368 | update 369 | } 370 | 371 | type KeystoneAdminUIFieldMetaCreateView { 372 | fieldMode: KeystoneAdminUIFieldMetaCreateViewFieldMode! 373 | } 374 | 375 | enum KeystoneAdminUIFieldMetaCreateViewFieldMode { 376 | edit 377 | hidden 378 | } 379 | 380 | type KeystoneAdminUIFieldMetaListView { 381 | fieldMode: KeystoneAdminUIFieldMetaListViewFieldMode! 382 | } 383 | 384 | enum KeystoneAdminUIFieldMetaListViewFieldMode { 385 | read 386 | hidden 387 | } 388 | 389 | type KeystoneAdminUIFieldMetaItemView { 390 | fieldMode: KeystoneAdminUIFieldMetaItemViewFieldMode 391 | fieldPosition: KeystoneAdminUIFieldMetaItemViewFieldPosition 392 | } 393 | 394 | enum KeystoneAdminUIFieldMetaItemViewFieldMode { 395 | edit 396 | read 397 | hidden 398 | } 399 | 400 | enum KeystoneAdminUIFieldMetaItemViewFieldPosition { 401 | form 402 | sidebar 403 | } 404 | 405 | enum QueryMode { 406 | default 407 | insensitive 408 | } 409 | 410 | type KeystoneAdminUIFieldGroupMeta { 411 | label: String! 412 | description: String 413 | fields: [KeystoneAdminUIFieldMeta!]! 414 | } 415 | 416 | type KeystoneAdminUISort { 417 | field: String! 418 | direction: KeystoneAdminUISortDirection! 419 | } 420 | 421 | enum KeystoneAdminUISortDirection { 422 | ASC 423 | DESC 424 | } 425 | -------------------------------------------------------------------------------- /create-keystone-app/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # create-keystone-app 2 | 3 | ## 9.0.1 4 | 5 | ### Patch Changes 6 | 7 | - [#398](https://github.com/keystonejs/create-keystone-app/pull/398) [`827990d`](https://github.com/keystonejs/create-keystone-app/commit/827990d49820b4377d9093a0b4a803d5a13add6a) Thanks [@borisno2](https://github.com/borisno2)! - Update Keystone-6 to `5.0.0` 8 | 9 | ## 9.0.0 10 | 11 | ### Major Changes 12 | 13 | - [#390](https://github.com/keystonejs/create-keystone-app/pull/390) [`b7705c2`](https://github.com/keystonejs/create-keystone-app/commit/b7705c23662c9f09920177e1ef619ed14a156dec) Thanks [@emmatown](https://github.com/emmatown)! - Upgrades @keystone-6/\* to the newest major release 14 | 15 | ## 8.1.0 16 | 17 | ### Minor Changes 18 | 19 | - [#383](https://github.com/keystonejs/create-keystone-app/pull/383) [`ec456c8`](https://github.com/keystonejs/create-keystone-app/commit/ec456c8df25127c26fb4af4e4a037928c9cda8d8) Thanks [@dcousens](https://github.com/dcousens)! - Upgrades `@keystone-6/*` to the newest minor release 20 | 21 | ## 8.0.2 22 | 23 | ### Patch Changes 24 | 25 | - [`27b3179`](https://github.com/keystonejs/create-keystone-app/commit/27b31793144f176aa64808fc452ae06adb40c2d9) Thanks [@dcousens](https://github.com/dcousens)! - Upgrades `@keystone-6/*` to the newest patch release 26 | 27 | ## 8.0.1 28 | 29 | ### Patch Changes 30 | 31 | - [#374](https://github.com/keystonejs/create-keystone-app/pull/374) [`7b5851a`](https://github.com/keystonejs/create-keystone-app/commit/7b5851ab1e5b6f937440f8d7aad60eb4b18e9dd5) Thanks [@dcousens](https://github.com/dcousens)! - Removes `@aws-sdk/util-endpoints` from dependencies as https://github.com/keystonejs/keystone/issues/8023 is resolved 32 | 33 | ## 8.0.0 34 | 35 | ### Major Changes 36 | 37 | - [#369](https://github.com/keystonejs/create-keystone-app/pull/369) [`0db3a2b`](https://github.com/keystonejs/create-keystone-app/commit/0db3a2b6f8108e0ef2e0c440cdbec969b475beb2) Thanks [@dcousens](https://github.com/dcousens)! - Upgrades `@keystone-6/*` to the newest set of major releases 38 | 39 | ## 7.0.2 40 | 41 | ### Patch Changes 42 | 43 | - [#357](https://github.com/keystonejs/create-keystone-app/pull/357) [`8a0a9c0`](https://github.com/keystonejs/create-keystone-app/commit/8a0a9c09e9a5a1436f63d60dd0ddf1b92b4ee4b1) Thanks [@dcousens](https://github.com/dcousens)! - Fix graphql@^15.8.0 and next@12.2.4 as pseudo-peer dependencies until next `@keystone-6/core` release 44 | 45 | ## 7.0.1 46 | 47 | ### Patch Changes 48 | 49 | - [#278](https://github.com/keystonejs/create-keystone-app/pull/278) [`26f9a79`](https://github.com/keystonejs/create-keystone-app/commit/26f9a79ef913915bac85657884f85ff7e4da46c2) Thanks [@Noviny](https://github.com/Noviny)! - Improve schema options for linking authors to posts: 50 | - Add `inlineConnect: true` to the post's relationship to users 51 | - Remove authors from being inline-creatable 52 | 53 | * [#319](https://github.com/keystonejs/create-keystone-app/pull/319) [`94a859e`](https://github.com/keystonejs/create-keystone-app/commit/94a859e43123d2f348d5e21551d59bd7e257aa81) Thanks [@Achisingh](https://github.com/Achisingh)! - Fix dependencies and update schemas for the latest `keystone-6` release 54 | 55 | ## 7.0.0 56 | 57 | ### Major Changes 58 | 59 | - [#311](https://github.com/keystonejs/create-keystone-app/pull/311) [`c6c8c7c`](https://github.com/keystonejs/create-keystone-app/commit/c6c8c7c1eb6aa488a4d91fd857d8517f09cef79b) Thanks [@borisno2](https://github.com/borisno2)! - Upgrade `@keystone-6/core` to `2.0.0` and `@keystone-6/auth` to `3.0.0` 60 | 61 | ## 6.0.1 62 | 63 | ### Patch Changes 64 | 65 | - [#273](https://github.com/keystonejs/create-keystone-app/pull/273) [`0d387f0`](https://github.com/keystonejs/create-keystone-app/commit/0d387f059911d4909f5e32d5e4d5e0de91ba05c8) Thanks [@renovate](https://github.com/apps/renovate)! - Updated Keystone dependencies to latest version. 66 | 67 | ## 6.0.0 68 | 69 | ### Major Changes 70 | 71 | - [#260](https://github.com/keystonejs/create-keystone-app/pull/260) [`6288ac4`](https://github.com/keystonejs/create-keystone-app/commit/6288ac493d82a4f4f669f70daa2f24cefd8e375e) Thanks [@bladey](https://github.com/bladey)! - Updated to use Keystone 6. 72 | 73 | ## 5.1.0 74 | 75 | ### Minor Changes 76 | 77 | - [#241](https://github.com/keystonejs/create-keystone-app/pull/241) [`d6ff06d`](https://github.com/keystonejs/create-keystone-app/commit/d6ff06d3a80830f37d5da07738a69e0af2edc039) Thanks [@bladey](https://github.com/bladey)! - Added engines to package.json to indicate required version of Node.js to run Keystone. 78 | 79 | ### Patch Changes 80 | 81 | - [#237](https://github.com/keystonejs/create-keystone-app/pull/237) [`a65ef75`](https://github.com/keystonejs/create-keystone-app/commit/a65ef75c8ca21007aade0c77a6482cbf145ff679) Thanks [@renovate](https://github.com/apps/renovate)! - Updated Keystone dependencies to latest version. 82 | 83 | ## 5.0.2 84 | 85 | ### Patch Changes 86 | 87 | - [#235](https://github.com/keystonejs/create-keystone-app/pull/235) [`9a3c00c`](https://github.com/keystonejs/create-keystone-app/commit/9a3c00cb6a719cef330955f8014f431c0ffa38d0) Thanks [@bladey](https://github.com/bladey)! - Updated Keystone dependencies to latest version. 88 | 89 | ## 5.0.1 90 | 91 | ### Patch Changes 92 | 93 | - [#218](https://github.com/keystonejs/create-keystone-app/pull/218) [`7547ee5`](https://github.com/keystonejs/create-keystone-app/commit/7547ee505c056cf18e7f9a6babdc0f51205b7d8c) Thanks [@renovate](https://github.com/apps/renovate)! - Updated Keystone dependencies to latest version. 94 | 95 | ## 5.0.0 96 | 97 | ### Major Changes 98 | 99 | - [#201](https://github.com/keystonejs/create-keystone-app/pull/201) [`ee98fdb`](https://github.com/keystonejs/create-keystone-app/commit/ee98fdba87ee303e47790aa146575316de299fb6) Thanks [@Noviny](https://github.com/Noviny)! - Switch to use sqlite as the default database 100 | 101 | ## 4.0.15 102 | 103 | ### Patch Changes 104 | 105 | - [#199](https://github.com/keystonejs/create-keystone-app/pull/199) [`fe7523d`](https://github.com/keystonejs/create-keystone-app/commit/fe7523dc1ee04d46a7d506554418fd3812729b46) Thanks [@renovate](https://github.com/apps/renovate)! - Updated Keystone dependencies to latest version. 106 | 107 | ## 4.0.14 108 | 109 | ### Patch Changes 110 | 111 | - [#194](https://github.com/keystonejs/create-keystone-app/pull/194) [`16614e1`](https://github.com/keystonejs/create-keystone-app/commit/16614e10160b2b0899ec0fe7cad5e3eeff129b3c) Thanks [@renovate](https://github.com/apps/renovate)! - Updated Keystone dependencies to latest version. 112 | 113 | ## 4.0.13 114 | 115 | ### Patch Changes 116 | 117 | - [#188](https://github.com/keystonejs/create-keystone-app/pull/188) [`621adbe`](https://github.com/keystonejs/create-keystone-app/commit/621adbe62de80a79b0759d3e806816b4097bb2a7) Thanks [@bladey](https://github.com/bladey)! - Updated Keystone dependencies to latest version. 118 | 119 | ## 4.0.12 120 | 121 | ### Patch Changes 122 | 123 | - [#173](https://github.com/keystonejs/create-keystone-app/pull/173) [`b8cf267`](https://github.com/keystonejs/create-keystone-app/commit/b8cf26719456a0c88788f6d6ea3fec05af2b57a6) Thanks [@renovate](https://github.com/apps/renovate)! - Updated Keystone dependencies to latest version. 124 | 125 | ## 4.0.11 126 | 127 | ### Patch Changes 128 | 129 | - [#166](https://github.com/keystonejs/create-keystone-app/pull/166) [`c853425`](https://github.com/keystonejs/create-keystone-app/commit/c8534250489c33e40323a69c41e644d7199f7329) Thanks [@renovate](https://github.com/apps/renovate)! - Updated Keystone dependencies to latest version. 130 | 131 | ## 4.0.10 132 | 133 | ### Patch Changes 134 | 135 | - [#157](https://github.com/keystonejs/create-keystone-app/pull/157) [`437237f`](https://github.com/keystonejs/create-keystone-app/commit/437237f671ae40fdbd1bed19ebc272cbb31cbfe6) Thanks [@renovate](https://github.com/apps/renovate)! - Updated Keystone dependencies to latest version. 136 | 137 | ## 4.0.9 138 | 139 | ### Patch Changes 140 | 141 | - [#150](https://github.com/keystonejs/create-keystone-app/pull/150) [`2af99bf`](https://github.com/keystonejs/create-keystone-app/commit/2af99bf669114eb3cd562abb707729a24aee533e) Thanks [@bladey](https://github.com/bladey)! - Updated Keystone dependencies to latest version. 142 | 143 | ## 4.0.8 144 | 145 | ### Patch Changes 146 | 147 | - [#143](https://github.com/keystonejs/create-keystone-app/pull/143) [`4aa566a`](https://github.com/keystonejs/create-keystone-app/commit/4aa566a8c2dccfab1264518f90d62899b467f15d) Thanks [@bladey](https://github.com/bladey)! - Updated Keystone dependencies to latest version. 148 | 149 | Updated README with getting starting instructions. 150 | 151 | ## 4.0.7 152 | 153 | ### Patch Changes 154 | 155 | - [#137](https://github.com/keystonejs/create-keystone-app/pull/137) [`a779d69`](https://github.com/keystonejs/create-keystone-app/commit/a779d691f385d152478d49a5f01ceb1bc0cc69ac) Thanks [@renovate](https://github.com/apps/renovate)! - Updated Keystone dependencies to latest version. 156 | 157 | ## 4.0.6 158 | 159 | ### Patch Changes 160 | 161 | - [#122](https://github.com/keystonejs/create-keystone-app/pull/122) [`60f1454`](https://github.com/keystonejs/create-keystone-app/commit/60f1454e354f37a587a75d7cb54c165862fc392d) Thanks [@renovate](https://github.com/apps/renovate)! - Updated Keystone dependencies to latest version. 162 | 163 | ## 4.0.5 164 | 165 | ### Patch Changes 166 | 167 | - [#116](https://github.com/keystonejs/create-keystone-app/pull/116) [`d532c8f`](https://github.com/keystonejs/create-keystone-app/commit/d532c8fcc243fd4d37d45a47eb4a1c7698fca494) Thanks [@bladey](https://github.com/bladey)! - Updated Keystone dependencies to latest version. 168 | 169 | ## 4.0.4 170 | 171 | ### Patch Changes 172 | 173 | - [#101](https://github.com/keystonejs/create-keystone-app/pull/101) [`446bf0e`](https://github.com/keystonejs/create-keystone-app/commit/446bf0e745e30d814a438c81eb8f7dd275174ff9) Thanks [@renovate](https://github.com/apps/renovate)! - Updated Keystone dependencies to latest version. 174 | 175 | ## 4.0.3 176 | 177 | ### Patch Changes 178 | 179 | - [#97](https://github.com/keystonejs/create-keystone-app/pull/97) [`4e47f27`](https://github.com/keystonejs/create-keystone-app/commit/4e47f27aa9f5925b6346960ee080758c8bfe34df) Thanks [@renovate](https://github.com/apps/renovate)! - Updated Keystone dependencies to latest version. 180 | 181 | ## 4.0.2 182 | 183 | ### Patch Changes 184 | 185 | - [#94](https://github.com/keystonejs/create-keystone-app/pull/94) [`be83fa6`](https://github.com/keystonejs/create-keystone-app/commit/be83fa60e004a2f11e65a0bc553312732912a828) Thanks [@bladey](https://github.com/bladey)! - Updated Keystone dependencies to latest version. 186 | 187 | ## 4.0.1 188 | 189 | ### Patch Changes 190 | 191 | - [#74](https://github.com/keystonejs/create-keystone-app/pull/74) [`79f19f5`](https://github.com/keystonejs/create-keystone-app/commit/79f19f51a6648941c15ac4f03bc0807a5670cb55) Thanks [@timleslie](https://github.com/timleslie)! - Updated `password` field in starter project to be `{ isRequired: true }`. 192 | 193 | ## 4.0.0 194 | 195 | ### Major Changes 196 | 197 | - [#65](https://github.com/keystonejs/create-keystone-next-app/pull/65) [`49c5ff2`](https://github.com/keystonejs/create-keystone-next-app/commit/49c5ff2d9892de0692a05a1f1dc01501f2979bc8) Thanks [@bladey](https://github.com/bladey)! - Updated to use Keystone Next packages instead of Keystone 5. 198 | 199 | ## 1.2.2 200 | 201 | ### Patch Changes 202 | 203 | - [#66](https://github.com/keystonejs/create-keystone-next-app/pull/66) [`f44867a`](https://github.com/keystonejs/create-keystone-next-app/commit/f44867a56626824e96a2135b2ec1eee07da9fde5) Thanks [@bladey](https://github.com/bladey)! - Added notice to advise users to use `create-keystone-app` instead of `create-keystone-next-app`. 204 | 205 | ## 1.2.1 206 | 207 | ### Patch Changes 208 | 209 | - [#52](https://github.com/keystonejs/create-keystone-next-app/pull/52) [`1b363c4`](https://github.com/keystonejs/create-keystone-next-app/commit/1b363c41cd96299e68cd3d9db3be94b13a7844e5) Thanks [@renovate](https://github.com/apps/renovate)! - Updated Keystone dependencies. 210 | 211 | ## 1.2.0 212 | 213 | ### Minor Changes 214 | 215 | - [#30](https://github.com/keystonejs/create-keystone-next-app/pull/30) [`fb45d53`](https://github.com/keystonejs/create-keystone-next-app/commit/fb45d53445ba2ec1fb30680e90c293d40c293d00) Thanks [@jesstelford](https://github.com/jesstelford)! - Updated script to show a loading spinner instead of the raw yarn or npm output. 216 | 217 | ### Patch Changes 218 | 219 | - [#36](https://github.com/keystonejs/create-keystone-next-app/pull/36) [`94f9f26`](https://github.com/keystonejs/create-keystone-next-app/commit/94f9f267eea6862605ca443a83997062eeeb6b92) Thanks [@renovate](https://github.com/apps/renovate)! - Updated Keystone dependencies. 220 | 221 | * [#46](https://github.com/keystonejs/create-keystone-next-app/pull/46) [`9cbc860`](https://github.com/keystonejs/create-keystone-next-app/commit/9cbc8608c42c4406e574dad12e2f871754ff5450) Thanks [@dominikwilkowski](https://github.com/dominikwilkowski)! - Fixed a windows issue where the package would not run via npx or npm init 222 | 223 | - [#50](https://github.com/keystonejs/create-keystone-next-app/pull/50) [`ceca306`](https://github.com/keystonejs/create-keystone-next-app/commit/ceca306853469eda199dc3d7b1c8ee411f414e9b) Thanks [@dominikwilkowski](https://github.com/dominikwilkowski)! - Updated keystone-next dependencies 224 | 225 | * [#28](https://github.com/keystonejs/create-keystone-next-app/pull/28) [`b52d27e`](https://github.com/keystonejs/create-keystone-next-app/commit/b52d27e4d2ad447cf4a5a268c6ccda65723d8d2f) Thanks [@dominikwilkowski](https://github.com/dominikwilkowski)! - Removed a single trailing space in tsconfig.json 226 | 227 | ## 1.1.0 228 | 229 | ### Minor Changes 230 | 231 | - [#24](https://github.com/keystonejs/create-keystone-next-app/pull/24) [`736c845`](https://github.com/keystonejs/create-keystone-next-app/commit/736c845d677e8520cb2263b5ae9122c1cc1590bf) Thanks [@mitchellhamilton](https://github.com/mitchellhamilton)! - Updated Keystone 232 | 233 | ## 1.0.0 234 | 235 | ### Major Changes 236 | 237 | - [`4f63ae3`](https://github.com/keystonejs/create-keystone-next-app/commit/4f63ae383f1995ba7494390717f628bed135d7b0) [#15](https://github.com/keystonejs/create-keystone-next-app/pull/15) Thanks [@mitchellhamilton](https://github.com/mitchellhamilton)! - Updated to the latest version of Keystone Next 238 | 239 | ## 0.0.1 240 | 241 | ### Patch Changes 242 | 243 | - [`201af8b`](https://github.com/keystonejs/create-keystone-next-app/commit/201af8b9b96edf5f63cf36c1540393f34fe06848) Thanks [@mitchellhamilton](https://github.com/mitchellhamilton)! - Improved output after creating project 244 | --------------------------------------------------------------------------------