create-keystone-app 7.0.2 → 8.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -44,7 +44,7 @@ var terminalLink__default = /*#__PURE__*/_interopDefault(terminalLink);
44
44
 
45
45
  var currentPkgJson = {
46
46
  name: "create-keystone-app",
47
- version: "7.0.2",
47
+ version: "8.0.0",
48
48
  main: "dist/create-keystone-app.cjs.js",
49
49
  files: [
50
50
  "dist",
@@ -73,11 +73,9 @@ async function checkVersion() {
73
73
  const {
74
74
  version
75
75
  } = await getPackageJson__default["default"]('create-keystone-app');
76
-
77
76
  if (typeof version !== 'string') {
78
77
  throw new Error('version from package metadata was expected to be a string but was not');
79
78
  }
80
-
81
79
  if (semver__namespace.lt(currentPkgJson.version, version)) {
82
80
  console.error(`⚠️ You're running an old version of create-keystone-app, please update to ${version}`);
83
81
  }
@@ -94,16 +92,13 @@ const cli = meow__default["default"](`
94
92
  Usage
95
93
  $ create-keystone-app [directory]
96
94
  `);
97
-
98
95
  const versionInfo = () => {
99
96
  process.stdout.write('\n');
100
97
  console.log(`✨ You're about to generate a project using ${c__default["default"].bold('Keystone 6')} packages.
101
98
  `);
102
99
  };
103
-
104
100
  async function normalizeArgs() {
105
101
  let directory = cli.input[0];
106
-
107
102
  if (!directory) {
108
103
  ({
109
104
  directory
@@ -115,15 +110,12 @@ async function normalizeArgs() {
115
110
  }));
116
111
  process.stdout.write('\n');
117
112
  }
118
-
119
113
  return {
120
114
  directory: path__default["default"].resolve(directory)
121
115
  };
122
116
  }
123
-
124
117
  const installDeps = async cwd => {
125
118
  const spinner = ora__default["default"]('Installing dependencies with yarn. This may take a few minutes.').start();
126
-
127
119
  try {
128
120
  await execa__default["default"]('yarn', ['install'], {
129
121
  cwd
@@ -132,12 +124,10 @@ const installDeps = async cwd => {
132
124
  return 'yarn';
133
125
  } catch (_err) {
134
126
  let err = _err;
135
-
136
127
  if (err.failed) {
137
128
  process.stdout.write('\n');
138
129
  spinner.warn('Failed to install with yarn.');
139
130
  spinner.start('Installing dependencies with npm. This may take a few minutes.');
140
-
141
131
  try {
142
132
  await execa__default["default"]('npm', ['install'], {
143
133
  cwd
@@ -147,15 +137,12 @@ const installDeps = async cwd => {
147
137
  spinner.fail('Failed to install with npm.');
148
138
  throw npmErr;
149
139
  }
150
-
151
140
  process.stdout.write('\n');
152
141
  return 'npm';
153
142
  }
154
-
155
143
  throw err;
156
144
  }
157
145
  };
158
-
159
146
  (async () => {
160
147
  versionInfo();
161
148
  await checkVersion();
@@ -187,6 +174,5 @@ const installDeps = async cwd => {
187
174
  } else {
188
175
  console.error(err);
189
176
  }
190
-
191
177
  process.exit(1);
192
178
  });
@@ -44,7 +44,7 @@ var terminalLink__default = /*#__PURE__*/_interopDefault(terminalLink);
44
44
 
45
45
  var currentPkgJson = {
46
46
  name: "create-keystone-app",
47
- version: "7.0.2",
47
+ version: "8.0.0",
48
48
  main: "dist/create-keystone-app.cjs.js",
49
49
  files: [
50
50
  "dist",
@@ -73,11 +73,9 @@ async function checkVersion() {
73
73
  const {
74
74
  version
75
75
  } = await getPackageJson__default["default"]('create-keystone-app');
76
-
77
76
  if (typeof version !== 'string') {
78
77
  throw new Error('version from package metadata was expected to be a string but was not');
79
78
  }
80
-
81
79
  if (semver__namespace.lt(currentPkgJson.version, version)) {
82
80
  console.error(`⚠️ You're running an old version of create-keystone-app, please update to ${version}`);
83
81
  }
@@ -94,16 +92,13 @@ const cli = meow__default["default"](`
94
92
  Usage
95
93
  $ create-keystone-app [directory]
96
94
  `);
97
-
98
95
  const versionInfo = () => {
99
96
  process.stdout.write('\n');
100
97
  console.log(`✨ You're about to generate a project using ${c__default["default"].bold('Keystone 6')} packages.
101
98
  `);
102
99
  };
103
-
104
100
  async function normalizeArgs() {
105
101
  let directory = cli.input[0];
106
-
107
102
  if (!directory) {
108
103
  ({
109
104
  directory
@@ -115,15 +110,12 @@ async function normalizeArgs() {
115
110
  }));
116
111
  process.stdout.write('\n');
117
112
  }
118
-
119
113
  return {
120
114
  directory: path__default["default"].resolve(directory)
121
115
  };
122
116
  }
123
-
124
117
  const installDeps = async cwd => {
125
118
  const spinner = ora__default["default"]('Installing dependencies with yarn. This may take a few minutes.').start();
126
-
127
119
  try {
128
120
  await execa__default["default"]('yarn', ['install'], {
129
121
  cwd
@@ -132,12 +124,10 @@ const installDeps = async cwd => {
132
124
  return 'yarn';
133
125
  } catch (_err) {
134
126
  let err = _err;
135
-
136
127
  if (err.failed) {
137
128
  process.stdout.write('\n');
138
129
  spinner.warn('Failed to install with yarn.');
139
130
  spinner.start('Installing dependencies with npm. This may take a few minutes.');
140
-
141
131
  try {
142
132
  await execa__default["default"]('npm', ['install'], {
143
133
  cwd
@@ -147,15 +137,12 @@ const installDeps = async cwd => {
147
137
  spinner.fail('Failed to install with npm.');
148
138
  throw npmErr;
149
139
  }
150
-
151
140
  process.stdout.write('\n');
152
141
  return 'npm';
153
142
  }
154
-
155
143
  throw err;
156
144
  }
157
145
  };
158
-
159
146
  (async () => {
160
147
  versionInfo();
161
148
  await checkVersion();
@@ -187,6 +174,5 @@ const installDeps = async cwd => {
187
174
  } else {
188
175
  console.error(err);
189
176
  }
190
-
191
177
  process.exit(1);
192
178
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-keystone-app",
3
- "version": "7.0.2",
3
+ "version": "8.0.0",
4
4
  "main": "dist/create-keystone-app.cjs.js",
5
5
  "files": [
6
6
  "dist",
@@ -0,0 +1,135 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // keystone.ts
21
+ var keystone_exports = {};
22
+ __export(keystone_exports, {
23
+ default: () => keystone_default
24
+ });
25
+ module.exports = __toCommonJS(keystone_exports);
26
+ var import_core2 = require("@keystone-6/core");
27
+
28
+ // schema.ts
29
+ var import_core = require("@keystone-6/core");
30
+ var import_access = require("@keystone-6/core/access");
31
+ var import_fields = require("@keystone-6/core/fields");
32
+ var import_fields_document = require("@keystone-6/fields-document");
33
+ var lists = {
34
+ User: (0, import_core.list)({
35
+ access: import_access.allowAll,
36
+ fields: {
37
+ name: (0, import_fields.text)({ validation: { isRequired: true } }),
38
+ email: (0, import_fields.text)({
39
+ validation: { isRequired: true },
40
+ isIndexed: "unique"
41
+ }),
42
+ password: (0, import_fields.password)({ validation: { isRequired: true } }),
43
+ posts: (0, import_fields.relationship)({ ref: "Post.author", many: true }),
44
+ createdAt: (0, import_fields.timestamp)({
45
+ defaultValue: { kind: "now" }
46
+ })
47
+ }
48
+ }),
49
+ Post: (0, import_core.list)({
50
+ access: import_access.allowAll,
51
+ fields: {
52
+ title: (0, import_fields.text)({ validation: { isRequired: true } }),
53
+ content: (0, import_fields_document.document)({
54
+ formatting: true,
55
+ layouts: [
56
+ [1, 1],
57
+ [1, 1, 1],
58
+ [2, 1],
59
+ [1, 2],
60
+ [1, 2, 1]
61
+ ],
62
+ links: true,
63
+ dividers: true
64
+ }),
65
+ author: (0, import_fields.relationship)({
66
+ ref: "User.posts",
67
+ ui: {
68
+ displayMode: "cards",
69
+ cardFields: ["name", "email"],
70
+ inlineEdit: { fields: ["name", "email"] },
71
+ linkToItem: true,
72
+ inlineConnect: true
73
+ },
74
+ many: false
75
+ }),
76
+ tags: (0, import_fields.relationship)({
77
+ ref: "Tag.posts",
78
+ many: true,
79
+ ui: {
80
+ displayMode: "cards",
81
+ cardFields: ["name"],
82
+ inlineEdit: { fields: ["name"] },
83
+ linkToItem: true,
84
+ inlineConnect: true,
85
+ inlineCreate: { fields: ["name"] }
86
+ }
87
+ })
88
+ }
89
+ }),
90
+ Tag: (0, import_core.list)({
91
+ access: import_access.allowAll,
92
+ ui: {
93
+ isHidden: true
94
+ },
95
+ fields: {
96
+ name: (0, import_fields.text)(),
97
+ posts: (0, import_fields.relationship)({ ref: "Post.tags", many: true })
98
+ }
99
+ })
100
+ };
101
+
102
+ // auth.ts
103
+ var import_crypto = require("crypto");
104
+ var import_auth = require("@keystone-6/auth");
105
+ var import_session = require("@keystone-6/core/session");
106
+ var sessionSecret = process.env.SESSION_SECRET;
107
+ if (!sessionSecret && true) {
108
+ sessionSecret = (0, import_crypto.randomBytes)(32).toString("hex");
109
+ }
110
+ var { withAuth } = (0, import_auth.createAuth)({
111
+ listKey: "User",
112
+ identityField: "email",
113
+ sessionData: "name createdAt",
114
+ secretField: "password",
115
+ initFirstItem: false ? void 0 : {
116
+ fields: ["name", "email", "password"]
117
+ }
118
+ });
119
+ var sessionMaxAge = 60 * 60 * 24 * 30;
120
+ var session = (0, import_session.statelessSessions)({
121
+ maxAge: sessionMaxAge,
122
+ secret: sessionSecret
123
+ });
124
+
125
+ // keystone.ts
126
+ var keystone_default = withAuth(
127
+ (0, import_core2.config)({
128
+ db: {
129
+ provider: "sqlite",
130
+ url: "file:./keystone.db"
131
+ },
132
+ lists,
133
+ session
134
+ })
135
+ );
package/starter/auth.ts CHANGED
@@ -1,51 +1,66 @@
1
- /*
2
- Welcome to the auth file! Here we have put a config to do basic auth in Keystone.
3
-
4
- `createAuth` is an implementation for an email-password login out of the box.
5
- `statelessSessions` is a base implementation of session logic.
6
-
7
- For more on auth, check out: https://keystonejs.com/docs/apis/auth#authentication-api
8
- */
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
9
17
 
18
+ import { randomBytes } from 'crypto';
10
19
  import { createAuth } from '@keystone-6/auth';
11
20
 
12
- // See https://keystonejs.com/docs/apis/session#session-api for the session docs
21
+ // see https://keystonejs.com/docs/apis/session for the session docs
13
22
  import { statelessSessions } from '@keystone-6/core/session';
14
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)
15
26
  let sessionSecret = process.env.SESSION_SECRET;
16
-
17
- // Here is a best practice! It's fine to not have provided a session secret in dev,
18
- // however it should always be there in production.
19
- if (!sessionSecret) {
20
- if (process.env.NODE_ENV === 'production') {
21
- throw new Error(
22
- 'The SESSION_SECRET environment variable must be set in production'
23
- );
24
- } else {
25
- sessionSecret = '-- DEV COOKIE SECRET; CHANGE ME --';
26
- }
27
+ if (!sessionSecret && process.env.NODE_ENV !== 'production') {
28
+ sessionSecret = randomBytes(32).toString('hex');
27
29
  }
28
30
 
29
- // Here we define how auth relates to our schemas.
30
- // What we are saying here is that we want to use the list `User`, and to log in
31
- // we will need their email and password.
31
+ // withAuth is a function we can use to wrap our base configuration
32
32
  const { withAuth } = createAuth({
33
33
  listKey: 'User',
34
34
  identityField: 'email',
35
- sessionData: 'name',
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',
36
40
  secretField: 'password',
37
- initFirstItem: {
38
- // If there are no items in the database, keystone will ask you to create
39
- // a new user, filling in these fields.
40
- fields: ['name', 'email', 'password'],
41
- },
41
+
42
+ // dont support initFirstItem functionality in production
43
+ // see https://keystonejs.com/docs/config/auth#init-first-item for more
44
+ initFirstItem:
45
+ process.env.NODE_ENV === 'production'
46
+ ? undefined
47
+ : {
48
+ // if there are no items in the database, by configuring this field
49
+ // you are asking the Keystone AdminUI to create a new user
50
+ // providing inputs for these fields
51
+ fields: ['name', 'email', 'password'],
52
+
53
+ // it uses context.sudo() to do this, which bypasses any access control you might have
54
+ // you shouldn't use this in production
55
+ },
42
56
  });
43
57
 
44
- // This defines how long people will remain logged in for.
45
- // This will get refreshed when they log back in.
46
- let sessionMaxAge = 60 * 60 * 24 * 30; // 30 days
58
+ // statelessSessions uses cookies for session tracking
59
+ // these cookies have an expiry, in seconds
60
+ // we use an expiry of 30 days for this starter
61
+ const sessionMaxAge = 60 * 60 * 24 * 30;
47
62
 
48
- // This defines how sessions should work. For more details, check out: https://keystonejs.com/docs/apis/session#session-api
63
+ // you can find out more at https://keystonejs.com/docs/apis/session#session-api
49
64
  const session = statelessSessions({
50
65
  maxAge: sessionMaxAge,
51
66
  secret: sessionSecret!,
@@ -1,32 +1,28 @@
1
- /*
2
- Welcome to Keystone! This file is what keystone uses to start the app.
3
-
4
- It looks at the default export, and expects a Keystone config object.
5
-
6
- You can find all the config options in our docs here: https://keystonejs.com/docs/apis/config
7
- */
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
8
7
 
9
8
  import { config } from '@keystone-6/core';
10
9
 
11
- // Look in the schema file for how we define our lists, and how users interact with them through graphql or the Admin UI
10
+ // to keep this file tidy, we define our schema in a different file
12
11
  import { lists } from './schema';
13
12
 
14
- // Keystone auth is configured separately - check out the basic auth setup we are importing from our auth file.
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
15
  import { withAuth, session } from './auth';
16
16
 
17
17
  export default withAuth(
18
- // Using the config function helps typescript guide you to the available options.
19
18
  config({
20
- // the db sets the database provider - we're using sqlite for the fastest startup experience
21
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
22
23
  provider: 'sqlite',
23
24
  url: 'file:./keystone.db',
24
25
  },
25
- // This config allows us to set up features of the Admin UI https://keystonejs.com/docs/apis/config#ui
26
- ui: {
27
- // For our starter, we check that someone has session data before letting them see the Admin UI.
28
- isAccessAllowed: (context) => !!context.session?.data,
29
- },
30
26
  lists,
31
27
  session,
32
28
  })
@@ -8,22 +8,12 @@
8
8
  "build": "keystone build",
9
9
  "postinstall": "keystone postinstall"
10
10
  },
11
+ "// @aws-sdk": "temporary dependency until https://github.com/keystonejs/keystone/issues/8023 is resolved",
11
12
  "dependencies": {
12
- "@keystone-6/auth": "^4.0.0",
13
- "@keystone-6/core": "^2.1.0",
14
- "@keystone-6/fields-document": "^4.0.1",
15
- "graphql": "^15.8.0",
16
- "next": "12.2.4",
17
- "typescript": "^4.7.4"
18
- },
19
- "// npm": "this is a temporary workaround for npm users, see https://github.com/keystonejs/create-keystone-app/pull/350",
20
- "overrides": {
21
- "graphql": "^15.8.0",
22
- "next": "12.2.4"
23
- },
24
- "// yarn": "this is a temporary workaround for yarn users, see https://github.com/keystonejs/create-keystone-app/pull/350",
25
- "resolutions": {
26
- "graphql": "^15.8.0",
27
- "next": "12.2.4"
13
+ "@aws-sdk/util-endpoints": "^3.192.0",
14
+ "@keystone-6/auth": "^5.0.0",
15
+ "@keystone-6/core": "^3.0.1",
16
+ "@keystone-6/fields-document": "^5.0.0",
17
+ "typescript": "^4.8.0"
28
18
  }
29
19
  }
@@ -6,19 +6,17 @@ type User {
6
6
  name: String
7
7
  email: String
8
8
  password: PasswordState
9
- posts(
10
- where: PostWhereInput! = {}
11
- orderBy: [PostOrderByInput!]! = []
12
- take: Int
13
- skip: Int! = 0
14
- ): [Post!]
9
+ posts(where: PostWhereInput! = {}, orderBy: [PostOrderByInput!]! = [], take: Int, skip: Int! = 0): [Post!]
15
10
  postsCount(where: PostWhereInput! = {}): Int
11
+ createdAt: DateTime
16
12
  }
17
13
 
18
14
  type PasswordState {
19
15
  isSet: Boolean!
20
16
  }
21
17
 
18
+ scalar DateTime @specifiedBy(url: "https://datatracker.ietf.org/doc/html/rfc3339#section-5.6")
19
+
22
20
  input UserWhereUniqueInput {
23
21
  id: ID
24
22
  email: String
@@ -32,6 +30,7 @@ input UserWhereInput {
32
30
  name: StringFilter
33
31
  email: StringFilter
34
32
  posts: PostManyRelationFilter
33
+ createdAt: DateTimeNullableFilter
35
34
  }
36
35
 
37
36
  input IDFilter {
@@ -79,10 +78,22 @@ input PostManyRelationFilter {
79
78
  none: PostWhereInput
80
79
  }
81
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
+
82
92
  input UserOrderByInput {
83
93
  id: OrderDirection
84
94
  name: OrderDirection
85
95
  email: OrderDirection
96
+ createdAt: OrderDirection
86
97
  }
87
98
 
88
99
  enum OrderDirection {
@@ -95,6 +106,7 @@ input UserUpdateInput {
95
106
  email: String
96
107
  password: String
97
108
  posts: PostRelateToManyForUpdateInput
109
+ createdAt: DateTime
98
110
  }
99
111
 
100
112
  input PostRelateToManyForUpdateInput {
@@ -114,6 +126,7 @@ input UserCreateInput {
114
126
  email: String
115
127
  password: String
116
128
  posts: PostRelateToManyForCreateInput
129
+ createdAt: DateTime
117
130
  }
118
131
 
119
132
  input PostRelateToManyForCreateInput {
@@ -124,16 +137,9 @@ input PostRelateToManyForCreateInput {
124
137
  type Post {
125
138
  id: ID!
126
139
  title: String
127
- status: String
128
140
  content: Post_content_Document
129
- publishDate: DateTime
130
141
  author: User
131
- tags(
132
- where: TagWhereInput! = {}
133
- orderBy: [TagOrderByInput!]! = []
134
- take: Int
135
- skip: Int! = 0
136
- ): [Tag!]
142
+ tags(where: TagWhereInput! = {}, orderBy: [TagOrderByInput!]! = [], take: Int, skip: Int! = 0): [Tag!]
137
143
  tagsCount(where: TagWhereInput! = {}): Int
138
144
  }
139
145
 
@@ -141,9 +147,6 @@ type Post_content_Document {
141
147
  document(hydrateRelationships: Boolean! = false): JSON!
142
148
  }
143
149
 
144
- scalar DateTime
145
- @specifiedBy(url: "https://datatracker.ietf.org/doc/html/rfc3339#section-5.6")
146
-
147
150
  input PostWhereUniqueInput {
148
151
  id: ID
149
152
  }
@@ -154,51 +157,10 @@ input PostWhereInput {
154
157
  NOT: [PostWhereInput!]
155
158
  id: IDFilter
156
159
  title: StringFilter
157
- status: StringNullableFilter
158
- publishDate: DateTimeNullableFilter
159
160
  author: UserWhereInput
160
161
  tags: TagManyRelationFilter
161
162
  }
162
163
 
163
- input StringNullableFilter {
164
- equals: String
165
- in: [String!]
166
- notIn: [String!]
167
- lt: String
168
- lte: String
169
- gt: String
170
- gte: String
171
- contains: String
172
- startsWith: String
173
- endsWith: String
174
- not: NestedStringNullableFilter
175
- }
176
-
177
- input NestedStringNullableFilter {
178
- equals: String
179
- in: [String!]
180
- notIn: [String!]
181
- lt: String
182
- lte: String
183
- gt: String
184
- gte: String
185
- contains: String
186
- startsWith: String
187
- endsWith: String
188
- not: NestedStringNullableFilter
189
- }
190
-
191
- input DateTimeNullableFilter {
192
- equals: DateTime
193
- in: [DateTime!]
194
- notIn: [DateTime!]
195
- lt: DateTime
196
- lte: DateTime
197
- gt: DateTime
198
- gte: DateTime
199
- not: DateTimeNullableFilter
200
- }
201
-
202
164
  input TagManyRelationFilter {
203
165
  every: TagWhereInput
204
166
  some: TagWhereInput
@@ -208,15 +170,11 @@ input TagManyRelationFilter {
208
170
  input PostOrderByInput {
209
171
  id: OrderDirection
210
172
  title: OrderDirection
211
- status: OrderDirection
212
- publishDate: OrderDirection
213
173
  }
214
174
 
215
175
  input PostUpdateInput {
216
176
  title: String
217
- status: String
218
177
  content: JSON
219
- publishDate: DateTime
220
178
  author: UserRelateToOneForUpdateInput
221
179
  tags: TagRelateToManyForUpdateInput
222
180
  }
@@ -241,9 +199,7 @@ input PostUpdateArgs {
241
199
 
242
200
  input PostCreateInput {
243
201
  title: String
244
- status: String
245
202
  content: JSON
246
- publishDate: DateTime
247
203
  author: UserRelateToOneForCreateInput
248
204
  tags: TagRelateToManyForCreateInput
249
205
  }
@@ -261,12 +217,7 @@ input TagRelateToManyForCreateInput {
261
217
  type Tag {
262
218
  id: ID!
263
219
  name: String
264
- posts(
265
- where: PostWhereInput! = {}
266
- orderBy: [PostOrderByInput!]! = []
267
- take: Int
268
- skip: Int! = 0
269
- ): [Post!]
220
+ posts(where: PostWhereInput! = {}, orderBy: [PostOrderByInput!]! = [], take: Int, skip: Int! = 0): [Post!]
270
221
  postsCount(where: PostWhereInput! = {}): Int
271
222
  }
272
223
 
@@ -306,10 +257,7 @@ input TagCreateInput {
306
257
  """
307
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).
308
259
  """
309
- scalar JSON
310
- @specifiedBy(
311
- url: "http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf"
312
- )
260
+ scalar JSON @specifiedBy(url: "http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf")
313
261
 
314
262
  type Mutation {
315
263
  createUser(data: UserCreateInput!): User
@@ -331,18 +279,11 @@ type Mutation {
331
279
  deleteTag(where: TagWhereUniqueInput!): Tag
332
280
  deleteTags(where: [TagWhereUniqueInput!]!): [Tag]
333
281
  endSession: Boolean!
334
- authenticateUserWithPassword(
335
- email: String!
336
- password: String!
337
- ): UserAuthenticationWithPasswordResult
338
- createInitialUser(
339
- data: CreateInitialUserInput!
340
- ): UserAuthenticationWithPasswordSuccess!
282
+ authenticateUserWithPassword(email: String!, password: String!): UserAuthenticationWithPasswordResult
283
+ createInitialUser(data: CreateInitialUserInput!): UserAuthenticationWithPasswordSuccess!
341
284
  }
342
285
 
343
- union UserAuthenticationWithPasswordResult =
344
- UserAuthenticationWithPasswordSuccess
345
- | UserAuthenticationWithPasswordFailure
286
+ union UserAuthenticationWithPasswordResult = UserAuthenticationWithPasswordSuccess | UserAuthenticationWithPasswordFailure
346
287
 
347
288
  type UserAuthenticationWithPasswordSuccess {
348
289
  sessionToken: String!
@@ -360,28 +301,13 @@ input CreateInitialUserInput {
360
301
  }
361
302
 
362
303
  type Query {
363
- users(
364
- where: UserWhereInput! = {}
365
- orderBy: [UserOrderByInput!]! = []
366
- take: Int
367
- skip: Int! = 0
368
- ): [User!]
304
+ users(where: UserWhereInput! = {}, orderBy: [UserOrderByInput!]! = [], take: Int, skip: Int! = 0): [User!]
369
305
  user(where: UserWhereUniqueInput!): User
370
306
  usersCount(where: UserWhereInput! = {}): Int
371
- posts(
372
- where: PostWhereInput! = {}
373
- orderBy: [PostOrderByInput!]! = []
374
- take: Int
375
- skip: Int! = 0
376
- ): [Post!]
307
+ posts(where: PostWhereInput! = {}, orderBy: [PostOrderByInput!]! = [], take: Int, skip: Int! = 0): [Post!]
377
308
  post(where: PostWhereUniqueInput!): Post
378
309
  postsCount(where: PostWhereInput! = {}): Int
379
- tags(
380
- where: TagWhereInput! = {}
381
- orderBy: [TagOrderByInput!]! = []
382
- take: Int
383
- skip: Int! = 0
384
- ): [Tag!]
310
+ tags(where: TagWhereInput! = {}, orderBy: [TagOrderByInput!]! = [], take: Int, skip: Int! = 0): [Tag!]
385
311
  tag(where: TagWhereUniqueInput!): Tag
386
312
  tagsCount(where: TagWhereInput! = {}): Int
387
313
  keystone: KeystoneMeta!
@@ -395,8 +321,6 @@ type KeystoneMeta {
395
321
  }
396
322
 
397
323
  type KeystoneAdminMeta {
398
- enableSignout: Boolean!
399
- enableSessionItem: Boolean!
400
324
  lists: [KeystoneAdminUIListMeta!]!
401
325
  list(key: String!): KeystoneAdminUIListMeta
402
326
  }
@@ -418,6 +342,7 @@ type KeystoneAdminUIListMeta {
418
342
  fields: [KeystoneAdminUIFieldMeta!]!
419
343
  initialSort: KeystoneAdminUISort
420
344
  isHidden: Boolean!
345
+ isSingleton: Boolean
421
346
  }
422
347
 
423
348
  type KeystoneAdminUIFieldMeta {
@@ -13,22 +13,21 @@ generator client {
13
13
  }
14
14
 
15
15
  model User {
16
- id String @id @default(cuid())
17
- name String @default("")
18
- email String @unique @default("")
19
- password String
20
- posts Post[] @relation("Post_author")
16
+ id String @id @default(cuid())
17
+ name String @default("")
18
+ email String @unique @default("")
19
+ password String
20
+ posts Post[] @relation("Post_author")
21
+ createdAt DateTime? @default(now())
21
22
  }
22
23
 
23
24
  model Post {
24
- id String @id @default(cuid())
25
- title String @default("")
26
- status String? @default("draft")
27
- content String @default("[{\"type\":\"paragraph\",\"children\":[{\"text\":\"\"}]}]")
28
- publishDate DateTime?
29
- author User? @relation("Post_author", fields: [authorId], references: [id])
30
- authorId String? @map("author")
31
- tags Tag[] @relation("Post_tags")
25
+ id String @id @default(cuid())
26
+ title String @default("")
27
+ content String @default("[{\"type\":\"paragraph\",\"children\":[{\"text\":\"\"}]}]")
28
+ author User? @relation("Post_author", fields: [authorId], references: [id])
29
+ authorId String? @map("author")
30
+ tags Tag[] @relation("Post_tags")
32
31
 
33
32
  @@index([authorId])
34
33
  }
@@ -37,4 +36,4 @@ model Tag {
37
36
  id String @id @default(cuid())
38
37
  name String @default("")
39
38
  posts Post[] @relation("Post_tags")
40
- }
39
+ }
package/starter/schema.ts CHANGED
@@ -1,24 +1,15 @@
1
- /*
2
- Welcome to the schema! The schema is the heart of Keystone.
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
3
7
 
4
- Here we define our 'lists', which will then be used both for the GraphQL
5
- API definition, our database tables, and our Admin UI layout.
6
-
7
- Some quick definitions to help out:
8
- A list: A definition of a collection of fields with a name. For the starter
9
- we have `User`, `Post`, and `Tag` lists.
10
- A field: The individual bits of data on your list, each with its own type.
11
- you can see some of the lists in what we use below.
12
-
13
- */
14
-
15
- // Like the `config` function we use in keystone.ts, we use functions
16
- // for putting in our config so we get useful errors. With typescript,
17
- // we get these even before code runs.
18
8
  import { list } from '@keystone-6/core';
9
+ import { allowAll } from '@keystone-6/core/access';
19
10
 
20
- // We're using some common fields in the starter. Check out https://keystonejs.com/docs/apis/fields#fields-api
21
- // for the full list of fields.
11
+ // see https://keystonejs.com/docs/fields/overview for the full list of fields
12
+ // this is a few common fields for an example
22
13
  import {
23
14
  text,
24
15
  relationship,
@@ -26,69 +17,62 @@ import {
26
17
  timestamp,
27
18
  select,
28
19
  } from '@keystone-6/core/fields';
29
- // The document field is a more complicated field, so it's in its own package
30
- // Keystone aims to have all the base field types, but you can make your own
31
- // custom ones.
20
+
21
+ // the document field is a more complicated field, so it has it's own package
32
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
33
24
 
34
- // We are using Typescript, and we want our types experience to be as strict as it can be.
35
- // By providing the Keystone generated `Lists` type to our lists object, we refine
36
- // our types to a stricter subset that is type-aware of other lists in our schema
37
- // that Typescript cannot easily infer.
38
- import { Lists } from '.keystone/types';
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';
39
28
 
40
- // We have a users list, a blogs list, and tags for blog posts, so they can be filtered.
41
- // Each property on the exported object will become the name of a list (a.k.a. the `listKey`),
42
- // with the value being the definition of the list, including the fields.
43
29
  export const lists: Lists = {
44
- // Here we define the user list.
45
30
  User: list({
46
- // Here are the fields that `User` will have. We want an email and password so they can log in
47
- // a name so we can refer to them, and a way to connect users to posts.
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
48
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
49
41
  name: text({ validation: { isRequired: true } }),
42
+
50
43
  email: text({
51
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
52
47
  isIndexed: 'unique',
53
- isFilterable: true,
54
48
  }),
55
- // The password field takes care of hiding details and hashing values
49
+
56
50
  password: password({ validation: { isRequired: true } }),
57
- // Relationships allow us to reference other lists. In this case,
58
- // we want a user to have many posts, and we are saying that the user
59
- // should be referencable by the 'author' field of posts.
60
- // Make sure you read the docs to understand how they work: https://keystonejs.com/docs/guides/relationships#understanding-relationships
51
+
52
+ // we can use this field to see what Posts this User has authored
53
+ // more on that in the Post list below
61
54
  posts: relationship({ ref: 'Post.author', many: true }),
62
- },
63
- // Here we can configure the Admin UI. We want to show a user's name and posts in the Admin UI
64
- ui: {
65
- listView: {
66
- initialColumns: ['name', 'posts'],
67
- },
55
+
56
+ createdAt: timestamp({
57
+ // this sets the timestamp to Date.now() when the user is first created
58
+ defaultValue: { kind: 'now' },
59
+ }),
68
60
  },
69
61
  }),
70
- // Our second list is the Posts list. We've got a few more fields here
71
- // so we have all the info we need for displaying posts.
62
+
72
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
73
71
  fields: {
74
- title: text(),
75
- // Having the status here will make it easy for us to choose whether to display
76
- // posts on a live site.
77
- status: select({
78
- options: [
79
- { label: 'Published', value: 'published' },
80
- { label: 'Draft', value: 'draft' },
81
- ],
82
- // We want to make sure new posts start off as a draft when they are created
83
- defaultValue: 'draft',
84
- // fields also have the ability to configure their appearance in the Admin UI
85
- ui: {
86
- displayMode: 'segmented-control',
87
- },
88
- }),
89
- // The document field can be used for making highly editable content. Check out our
90
- // guide on the document field https://keystonejs.com/docs/guides/document-fields#how-to-use-document-fields
91
- // for more information
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
92
76
  content: document({
93
77
  formatting: true,
94
78
  layouts: [
@@ -101,11 +85,13 @@ export const lists: Lists = {
101
85
  links: true,
102
86
  dividers: true,
103
87
  }),
104
- publishDate: timestamp(),
105
- // Here is the link from post => author.
106
- // We've configured its UI display quite a lot to make the experience of editing posts better.
88
+
89
+ // with this field, you can set a User as the author for a Post
107
90
  author: relationship({
91
+ // we could have used 'User', but then the relationship would only be 1-way
108
92
  ref: 'User.posts',
93
+
94
+ // this is some customisations for changing how this will look in the AdminUI
109
95
  ui: {
110
96
  displayMode: 'cards',
111
97
  cardFields: ['name', 'email'],
@@ -113,10 +99,21 @@ export const lists: Lists = {
113
99
  linkToItem: true,
114
100
  inlineConnect: true,
115
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,
116
106
  }),
117
- // We also link posts to tags. This is a many <=> many linking.
107
+
108
+ // with this field, you can add some Tags to Posts
118
109
  tags: relationship({
110
+ // we could have used 'Tag', but then the relationship would only be 1-way
119
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
120
117
  ui: {
121
118
  displayMode: 'cards',
122
119
  cardFields: ['name'],
@@ -125,17 +122,27 @@ export const lists: Lists = {
125
122
  inlineConnect: true,
126
123
  inlineCreate: { fields: ['name'] },
127
124
  },
128
- many: true,
129
125
  }),
130
126
  },
131
127
  }),
132
- // Our final list is the tag list. This field is just a name and a relationship to posts
128
+
129
+ // this last list is our Tag list, it only has a name field for now
133
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
134
138
  ui: {
135
139
  isHidden: true,
136
140
  },
141
+
142
+ // this is the fields for our Tag list
137
143
  fields: {
138
144
  name: text(),
145
+ // this can be helpful to find out all the Posts associated with a Tag
139
146
  posts: relationship({ ref: 'Post.tags', many: true }),
140
147
  },
141
148
  }),