@nixxie-cms/auth 1.0.1

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.
Files changed (42) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +6 -0
  3. package/components/Navigation/dist/nixxie-cms-auth-components-Navigation.cjs.d.ts +3 -0
  4. package/components/Navigation/dist/nixxie-cms-auth-components-Navigation.cjs.js +147 -0
  5. package/components/Navigation/dist/nixxie-cms-auth-components-Navigation.esm.js +143 -0
  6. package/components/Navigation/package.json +4 -0
  7. package/dist/declarations/src/components/Navigation.d.ts +6 -0
  8. package/dist/declarations/src/components/Navigation.d.ts.map +1 -0
  9. package/dist/declarations/src/index.d.ts +15 -0
  10. package/dist/declarations/src/index.d.ts.map +1 -0
  11. package/dist/declarations/src/pages/InitPage.d.ts +9 -0
  12. package/dist/declarations/src/pages/InitPage.d.ts.map +1 -0
  13. package/dist/declarations/src/pages/SigninPage.d.ts +9 -0
  14. package/dist/declarations/src/pages/SigninPage.d.ts.map +1 -0
  15. package/dist/declarations/src/types.d.ts +49 -0
  16. package/dist/declarations/src/types.d.ts.map +1 -0
  17. package/dist/nixxie-cms-auth.cjs.d.ts +2 -0
  18. package/dist/nixxie-cms-auth.cjs.js +552 -0
  19. package/dist/nixxie-cms-auth.esm.js +548 -0
  20. package/dist/useFromRedirect-2de239a9.cjs.js +26 -0
  21. package/dist/useFromRedirect-b3deee00.esm.js +24 -0
  22. package/package.json +56 -0
  23. package/pages/InitPage/dist/nixxie-cms-auth-pages-InitPage.cjs.d.ts +3 -0
  24. package/pages/InitPage/dist/nixxie-cms-auth-pages-InitPage.cjs.js +274 -0
  25. package/pages/InitPage/dist/nixxie-cms-auth-pages-InitPage.esm.js +266 -0
  26. package/pages/InitPage/package.json +4 -0
  27. package/pages/SigninPage/dist/nixxie-cms-auth-pages-SigninPage.cjs.d.ts +3 -0
  28. package/pages/SigninPage/dist/nixxie-cms-auth-pages-SigninPage.cjs.js +319 -0
  29. package/pages/SigninPage/dist/nixxie-cms-auth-pages-SigninPage.esm.js +311 -0
  30. package/pages/SigninPage/package.json +4 -0
  31. package/src/components/Navigation.tsx +182 -0
  32. package/src/gql/getBaseAuthSchema.ts +129 -0
  33. package/src/gql/getInitFirstItemSchema.ts +87 -0
  34. package/src/index.ts +291 -0
  35. package/src/lib/useFromRedirect.ts +23 -0
  36. package/src/pages/InitPage.tsx +292 -0
  37. package/src/pages/SigninPage.tsx +331 -0
  38. package/src/schema.ts +84 -0
  39. package/src/templates/config.ts +9 -0
  40. package/src/templates/init.ts +22 -0
  41. package/src/templates/signin.ts +20 -0
  42. package/src/types.ts +57 -0
@@ -0,0 +1,292 @@
1
+ import NextHead from 'next/head'
2
+
3
+ import { css } from '@keystar/ui/style'
4
+
5
+ import { gql, type TypedDocumentNode, useMutation } from '@nixxie-cms/core/admin-ui/apollo'
6
+ import { GraphQLErrorNotice } from '@nixxie-cms/core/admin-ui/components'
7
+ import { useList } from '@nixxie-cms/core/admin-ui/context'
8
+ import { useRouter } from '@nixxie-cms/core/admin-ui/router'
9
+ import { Fields, useBuildItem } from '@nixxie-cms/core/admin-ui/utils'
10
+
11
+ import { useRedirect } from '../lib/useFromRedirect'
12
+ import type { AuthGqlNames } from '../types'
13
+
14
+ export default (props: Parameters<typeof InitPage>[0]) => () => <InitPage {...props} />
15
+
16
+ function InitPage({
17
+ authGqlNames,
18
+ listKey,
19
+ fieldPaths,
20
+ }: {
21
+ authGqlNames: AuthGqlNames
22
+ listKey: string
23
+ fieldPaths: string[]
24
+ }) {
25
+ const router = useRouter()
26
+ const redirect = useRedirect()
27
+ const list = useList(listKey)
28
+
29
+ const builder = useBuildItem(list, fieldPaths)
30
+ const {
31
+ createInitialItem,
32
+ CreateInitialInput,
33
+ ItemAuthenticationWithPasswordSuccess: successTypename,
34
+ } = authGqlNames
35
+ const [tryCreateItem, { loading, error, data }] = useMutation(
36
+ gql`
37
+ mutation NxAuthCreateFirstItem ($data: ${CreateInitialInput}!) {
38
+ authenticate: ${createInitialItem}(data: $data) {
39
+ ... on ${successTypename} {
40
+ item {
41
+ id
42
+ }
43
+ }
44
+ }
45
+ }` as TypedDocumentNode<
46
+ { authenticate: { __typename: string; item: { id: string } } },
47
+ { data: Record<string, unknown> }
48
+ >
49
+ )
50
+
51
+ const onSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
52
+ if (e.target !== e.currentTarget) return
53
+ e.preventDefault()
54
+ const builtItem = await builder.build()
55
+ if (!builtItem) return
56
+
57
+ try {
58
+ await tryCreateItem({
59
+ variables: {
60
+ data: builtItem,
61
+ },
62
+ })
63
+ } catch (e) {
64
+ console.error(e)
65
+ return
66
+ }
67
+
68
+ router.push(redirect)
69
+ }
70
+
71
+ const pending = loading || data?.authenticate?.__typename === successTypename
72
+
73
+ return (
74
+ <>
75
+ <NextHead>
76
+ <title key="title">Welcome to Nixxie CMS</title>
77
+ </NextHead>
78
+
79
+ <div
80
+ className={css({
81
+ display: 'flex',
82
+ minHeight: '100vh',
83
+ fontFamily: "'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif",
84
+ })}
85
+ >
86
+ {/* ── Left brand panel ── */}
87
+ <div
88
+ className={css({
89
+ width: 420,
90
+ backgroundColor: '#000000',
91
+ display: 'flex',
92
+ flexDirection: 'column',
93
+ justifyContent: 'space-between',
94
+ padding: '48px',
95
+ flexShrink: 0,
96
+ '@media (max-width: 900px)': { display: 'none' },
97
+ })}
98
+ >
99
+ {/* Logo */}
100
+ <div className={css({ display: 'flex', alignItems: 'center', gap: 10 })}>
101
+ <span
102
+ className={css({
103
+ display: 'inline-flex',
104
+ alignItems: 'center',
105
+ justifyContent: 'center',
106
+ width: 30,
107
+ height: 30,
108
+ borderRadius: 7,
109
+ border: '1.5px solid rgba(255,255,255,0.18)',
110
+ flexShrink: 0,
111
+ })}
112
+ >
113
+ <svg width="14" height="14" viewBox="0 0 14 14" fill="none">
114
+ <path
115
+ d="M2.5 11V3L11.5 11V3"
116
+ stroke="white"
117
+ strokeWidth="1.8"
118
+ strokeLinecap="round"
119
+ strokeLinejoin="round"
120
+ />
121
+ </svg>
122
+ </span>
123
+ <span
124
+ className={css({
125
+ fontSize: 15,
126
+ fontWeight: 600,
127
+ color: '#ffffff',
128
+ letterSpacing: '-0.03em',
129
+ lineHeight: 1,
130
+ })}
131
+ >
132
+ Nixxie
133
+ </span>
134
+ </div>
135
+
136
+ {/* Tagline */}
137
+ <div>
138
+ <p
139
+ className={css({
140
+ margin: '0 0 16px',
141
+ fontSize: 34,
142
+ fontWeight: 700,
143
+ color: '#ffffff',
144
+ letterSpacing: '-0.04em',
145
+ lineHeight: 1.15,
146
+ })}
147
+ >
148
+ Welcome
149
+ <br />
150
+ aboard.
151
+ </p>
152
+ <p
153
+ className={css({
154
+ margin: 0,
155
+ fontSize: 13.5,
156
+ color: 'rgba(255,255,255,0.38)',
157
+ lineHeight: 1.65,
158
+ })}
159
+ >
160
+ Create your first admin account to
161
+ <br />
162
+ get started with Nixxie CMS.
163
+ </p>
164
+ </div>
165
+
166
+ {/* Footer */}
167
+ <p
168
+ className={css({
169
+ margin: 0,
170
+ fontSize: 10.5,
171
+ color: 'rgba(255,255,255,0.18)',
172
+ letterSpacing: '0.06em',
173
+ textTransform: 'uppercase',
174
+ })}
175
+ >
176
+ Nixxie CMS
177
+ </p>
178
+ </div>
179
+
180
+ {/* ── Right form panel ── */}
181
+ <div
182
+ className={css({
183
+ flex: 1,
184
+ backgroundColor: '#ffffff',
185
+ display: 'flex',
186
+ alignItems: 'center',
187
+ justifyContent: 'center',
188
+ padding: '48px 64px',
189
+ '@media (max-width: 600px)': { padding: '32px 24px' },
190
+ })}
191
+ >
192
+ <div className={css({ width: '100%', maxWidth: 400 })}>
193
+ {/* N mark */}
194
+ <div
195
+ className={css({
196
+ display: 'inline-flex',
197
+ alignItems: 'center',
198
+ justifyContent: 'center',
199
+ width: 36,
200
+ height: 36,
201
+ borderRadius: 8,
202
+ backgroundColor: '#000000',
203
+ marginBottom: 32,
204
+ })}
205
+ >
206
+ <svg width="16" height="16" viewBox="0 0 14 14" fill="none">
207
+ <path
208
+ d="M2.5 11V3L11.5 11V3"
209
+ stroke="white"
210
+ strokeWidth="1.8"
211
+ strokeLinecap="round"
212
+ strokeLinejoin="round"
213
+ />
214
+ </svg>
215
+ </div>
216
+
217
+ <h1
218
+ className={css({
219
+ margin: '0 0 6px',
220
+ fontSize: 23,
221
+ fontWeight: 700,
222
+ color: '#0a0a0a',
223
+ letterSpacing: '-0.03em',
224
+ lineHeight: 1.2,
225
+ })}
226
+ >
227
+ Create your first user
228
+ </h1>
229
+ <p
230
+ className={css({
231
+ margin: '0 0 28px',
232
+ fontSize: 13.5,
233
+ color: '#8a8a8a',
234
+ lineHeight: 1.5,
235
+ })}
236
+ >
237
+ Set up your admin account to start using Nixxie CMS.
238
+ </p>
239
+
240
+ {/* Errors */}
241
+ <GraphQLErrorNotice errors={[error]} />
242
+
243
+ {/* Form */}
244
+ <form
245
+ onSubmit={onSubmit}
246
+ className={css({
247
+ display: 'flex',
248
+ flexDirection: 'column',
249
+ gap: 16,
250
+ })}
251
+ >
252
+ <Fields {...builder.props} />
253
+
254
+ <button
255
+ type="submit"
256
+ disabled={pending}
257
+ className={css({
258
+ marginTop: 4,
259
+ display: 'flex',
260
+ alignItems: 'center',
261
+ justifyContent: 'center',
262
+ paddingInline: 20,
263
+ paddingBlock: 11,
264
+ backgroundColor: '#000000',
265
+ color: '#ffffff',
266
+ borderRadius: 7,
267
+ fontSize: 14,
268
+ fontWeight: 600,
269
+ letterSpacing: '-0.01em',
270
+ cursor: pending ? 'default' : 'pointer',
271
+ opacity: pending ? 0.6 : 1,
272
+ transition: 'opacity 150ms, background-color 150ms',
273
+ width: '100%',
274
+ border: 'none',
275
+ fontFamily: "'Inter', -apple-system, BlinkMacSystemFont, sans-serif",
276
+ '&:hover:not(:disabled)': { backgroundColor: '#1a1a1a' },
277
+ '&:focus-visible': {
278
+ outline: '2px solid #000',
279
+ outlineOffset: 3,
280
+ borderRadius: 7,
281
+ },
282
+ })}
283
+ >
284
+ {pending ? 'Setting up…' : 'Get started'}
285
+ </button>
286
+ </form>
287
+ </div>
288
+ </div>
289
+ </div>
290
+ </>
291
+ )
292
+ }
@@ -0,0 +1,331 @@
1
+ import NextHead from 'next/head'
2
+ import { useState } from 'react'
3
+
4
+ import { Notice } from '@keystar/ui/notice'
5
+ import { PasswordField } from '@keystar/ui/password-field'
6
+ import { Content } from '@keystar/ui/slots'
7
+ import { css } from '@keystar/ui/style'
8
+ import { TextField } from '@keystar/ui/text-field'
9
+ import { Text } from '@keystar/ui/typography'
10
+
11
+ import { gql, type TypedDocumentNode, useMutation } from '@nixxie-cms/core/admin-ui/apollo'
12
+ import { GraphQLErrorNotice } from '@nixxie-cms/core/admin-ui/components'
13
+ import { useRouter } from '@nixxie-cms/core/admin-ui/router'
14
+ import { useRedirect } from '../lib/useFromRedirect'
15
+ import type { AuthGqlNames } from '../types'
16
+
17
+ export default (props: Parameters<typeof SigninPage>[0]) => () => <SigninPage {...props} />
18
+
19
+ function SigninPage({
20
+ identityField,
21
+ secretField,
22
+ authGqlNames,
23
+ }: {
24
+ identityField: string
25
+ secretField: string
26
+ authGqlNames: AuthGqlNames
27
+ }) {
28
+ const router = useRouter()
29
+ const redirect = useRedirect()
30
+ const [state, setState] = useState({ identity: '', secret: '' })
31
+ const {
32
+ authenticateItemWithPassword,
33
+ ItemAuthenticationWithPasswordSuccess: successTypename,
34
+ ItemAuthenticationWithPasswordFailure: failureTypename,
35
+ } = authGqlNames
36
+ const [tryAuthenticate, { error, loading, data }] = useMutation(
37
+ gql`
38
+ mutation NxAuthSignin ($identity: String!, $secret: String!) {
39
+ authenticate: ${authenticateItemWithPassword}(${identityField}: $identity, ${secretField}: $secret) {
40
+ ... on ${successTypename} {
41
+ item {
42
+ id
43
+ }
44
+ }
45
+ ... on ${failureTypename} {
46
+ message
47
+ }
48
+ }
49
+ }` as TypedDocumentNode<
50
+ { authenticate: { __typename: string; item?: { id: string }; message?: string } },
51
+ { identity: string; secret: string }
52
+ >,
53
+ {
54
+ refetchQueries: ['NxFetchAdminMeta'],
55
+ }
56
+ )
57
+
58
+ const onSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
59
+ if (event.target !== event.currentTarget) return
60
+ event.preventDefault()
61
+
62
+ try {
63
+ const { data } = await tryAuthenticate({
64
+ variables: {
65
+ identity: state.identity,
66
+ secret: state.secret,
67
+ },
68
+ })
69
+
70
+ if (data?.authenticate.item) {
71
+ router.push(redirect)
72
+ }
73
+ } catch (e) {
74
+ console.error(e)
75
+ return
76
+ }
77
+ }
78
+
79
+ const pending = loading || data?.authenticate?.__typename === successTypename
80
+
81
+ return (
82
+ <>
83
+ <NextHead>
84
+ <title key="title">Nixxie — Sign in</title>
85
+ </NextHead>
86
+
87
+ <div
88
+ className={css({
89
+ display: 'flex',
90
+ minHeight: '100vh',
91
+ fontFamily: "'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif",
92
+ })}
93
+ >
94
+ {/* ── Left brand panel ── */}
95
+ <div
96
+ className={css({
97
+ width: 420,
98
+ backgroundColor: '#000000',
99
+ display: 'flex',
100
+ flexDirection: 'column',
101
+ justifyContent: 'space-between',
102
+ padding: '48px',
103
+ flexShrink: 0,
104
+ '@media (max-width: 900px)': { display: 'none' },
105
+ })}
106
+ >
107
+ {/* Logo */}
108
+ <div className={css({ display: 'flex', alignItems: 'center', gap: 10 })}>
109
+ <span
110
+ className={css({
111
+ display: 'inline-flex',
112
+ alignItems: 'center',
113
+ justifyContent: 'center',
114
+ width: 30,
115
+ height: 30,
116
+ borderRadius: 7,
117
+ border: '1.5px solid rgba(255,255,255,0.18)',
118
+ flexShrink: 0,
119
+ })}
120
+ >
121
+ <svg width="14" height="14" viewBox="0 0 14 14" fill="none">
122
+ <path
123
+ d="M2.5 11V3L11.5 11V3"
124
+ stroke="white"
125
+ strokeWidth="1.8"
126
+ strokeLinecap="round"
127
+ strokeLinejoin="round"
128
+ />
129
+ </svg>
130
+ </span>
131
+ <span
132
+ className={css({
133
+ fontSize: 15,
134
+ fontWeight: 600,
135
+ color: '#ffffff',
136
+ letterSpacing: '-0.03em',
137
+ lineHeight: 1,
138
+ })}
139
+ >
140
+ Nixxie
141
+ </span>
142
+ </div>
143
+
144
+ {/* Tagline */}
145
+ <div>
146
+ <p
147
+ className={css({
148
+ margin: '0 0 16px',
149
+ fontSize: 34,
150
+ fontWeight: 700,
151
+ color: '#ffffff',
152
+ letterSpacing: '-0.04em',
153
+ lineHeight: 1.15,
154
+ })}
155
+ >
156
+ Content.
157
+ <br />
158
+ Managed
159
+ <br />
160
+ Beautifully.
161
+ </p>
162
+ <p
163
+ className={css({
164
+ margin: 0,
165
+ fontSize: 13.5,
166
+ color: 'rgba(255,255,255,0.38)',
167
+ lineHeight: 1.65,
168
+ })}
169
+ >
170
+ A professional CMS built for modern
171
+ <br />
172
+ teams who care about craft.
173
+ </p>
174
+ </div>
175
+
176
+ {/* Footer */}
177
+ <p
178
+ className={css({
179
+ margin: 0,
180
+ fontSize: 10.5,
181
+ color: 'rgba(255,255,255,0.18)',
182
+ letterSpacing: '0.06em',
183
+ textTransform: 'uppercase',
184
+ })}
185
+ >
186
+ Nixxie CMS
187
+ </p>
188
+ </div>
189
+
190
+ {/* ── Right form panel ── */}
191
+ <div
192
+ className={css({
193
+ flex: 1,
194
+ backgroundColor: '#ffffff',
195
+ display: 'flex',
196
+ alignItems: 'center',
197
+ justifyContent: 'center',
198
+ padding: '48px 64px',
199
+ '@media (max-width: 600px)': { padding: '32px 24px' },
200
+ })}
201
+ >
202
+ <div className={css({ width: '100%', maxWidth: 360 })}>
203
+ {/* N mark */}
204
+ <div
205
+ className={css({
206
+ display: 'inline-flex',
207
+ alignItems: 'center',
208
+ justifyContent: 'center',
209
+ width: 36,
210
+ height: 36,
211
+ borderRadius: 8,
212
+ backgroundColor: '#000000',
213
+ marginBottom: 32,
214
+ })}
215
+ >
216
+ <svg width="16" height="16" viewBox="0 0 14 14" fill="none">
217
+ <path
218
+ d="M2.5 11V3L11.5 11V3"
219
+ stroke="white"
220
+ strokeWidth="1.8"
221
+ strokeLinecap="round"
222
+ strokeLinejoin="round"
223
+ />
224
+ </svg>
225
+ </div>
226
+
227
+ <h1
228
+ className={css({
229
+ margin: '0 0 6px',
230
+ fontSize: 23,
231
+ fontWeight: 700,
232
+ color: '#0a0a0a',
233
+ letterSpacing: '-0.03em',
234
+ lineHeight: 1.2,
235
+ })}
236
+ >
237
+ Sign in
238
+ </h1>
239
+ <p
240
+ className={css({
241
+ margin: '0 0 28px',
242
+ fontSize: 13.5,
243
+ color: '#8a8a8a',
244
+ lineHeight: 1.5,
245
+ })}
246
+ >
247
+ Welcome back. Enter your credentials to continue.
248
+ </p>
249
+
250
+ {/* Errors */}
251
+ <GraphQLErrorNotice errors={[error]} />
252
+ {data?.authenticate?.__typename === failureTypename && (
253
+ <Notice tone="critical">
254
+ <Content>
255
+ <Text>{data?.authenticate.message}</Text>
256
+ </Content>
257
+ </Notice>
258
+ )}
259
+
260
+ {/* Form */}
261
+ <form
262
+ onSubmit={onSubmit}
263
+ className={css({
264
+ display: 'flex',
265
+ flexDirection: 'column',
266
+ gap: 16,
267
+ })}
268
+ >
269
+ <TextField
270
+ autoFocus
271
+ id="identity"
272
+ isRequired
273
+ label={capitalizeFirstLetter(identityField)}
274
+ name="identity"
275
+ onChange={v => setState({ ...state, identity: v })}
276
+ value={state.identity}
277
+ />
278
+ <PasswordField
279
+ id="password"
280
+ isRequired
281
+ label={capitalizeFirstLetter(secretField)}
282
+ // @ts-expect-error — valid prop, types need to be fixed in "@keystar/ui"
283
+ name="password"
284
+ onChange={v => setState({ ...state, secret: v })}
285
+ type="password"
286
+ value={state.secret}
287
+ />
288
+
289
+ <button
290
+ type="submit"
291
+ disabled={pending}
292
+ className={css({
293
+ marginTop: 4,
294
+ display: 'flex',
295
+ alignItems: 'center',
296
+ justifyContent: 'center',
297
+ paddingInline: 20,
298
+ paddingBlock: 11,
299
+ backgroundColor: '#000000',
300
+ color: '#ffffff',
301
+ borderRadius: 7,
302
+ fontSize: 14,
303
+ fontWeight: 600,
304
+ letterSpacing: '-0.01em',
305
+ cursor: pending ? 'default' : 'pointer',
306
+ opacity: pending ? 0.6 : 1,
307
+ transition: 'opacity 150ms, background-color 150ms',
308
+ width: '100%',
309
+ border: 'none',
310
+ fontFamily: "'Inter', -apple-system, BlinkMacSystemFont, sans-serif",
311
+ '&:hover:not(:disabled)': { backgroundColor: '#1a1a1a' },
312
+ '&:focus-visible': {
313
+ outline: '2px solid #000',
314
+ outlineOffset: 3,
315
+ borderRadius: 7,
316
+ },
317
+ })}
318
+ >
319
+ {pending ? 'Signing in…' : 'Sign in'}
320
+ </button>
321
+ </form>
322
+ </div>
323
+ </div>
324
+ </div>
325
+ </>
326
+ )
327
+ }
328
+
329
+ function capitalizeFirstLetter(value: string) {
330
+ return value.charAt(0).toUpperCase() + value.slice(1)
331
+ }
package/src/schema.ts ADDED
@@ -0,0 +1,84 @@
1
+ import { assertInputObjectType, GraphQLString, GraphQLID, parse, validate } from 'graphql'
2
+
3
+ import { g } from '@nixxie-cms/core'
4
+ import type { AuthGqlNames, AuthTokenTypeConfig, InitFirstItemConfig } from './types'
5
+ import { getBaseAuthSchema } from './gql/getBaseAuthSchema'
6
+ import { getInitFirstItemSchema } from './gql/getInitFirstItemSchema'
7
+
8
+ export const getSchemaExtension = ({
9
+ authGqlNames,
10
+ listKey,
11
+ identityField,
12
+ secretField,
13
+ initFirstItem,
14
+ sessionData,
15
+ }: {
16
+ authGqlNames: AuthGqlNames
17
+ listKey: string
18
+ identityField: string
19
+ secretField: string
20
+ initFirstItem?: InitFirstItemConfig<any>
21
+ passwordResetLink?: AuthTokenTypeConfig
22
+ magicAuthLink?: AuthTokenTypeConfig
23
+ sessionData: string
24
+ }) =>
25
+ g.extend(base => {
26
+ const uniqueWhereInputType = assertInputObjectType(
27
+ base.schema.getType(authGqlNames.whereUniqueInputName)
28
+ )
29
+ const identityFieldOnUniqueWhere = uniqueWhereInputType.getFields()[identityField]
30
+ if (
31
+ base.schema.extensions.sudo &&
32
+ identityFieldOnUniqueWhere?.type !== GraphQLString &&
33
+ identityFieldOnUniqueWhere?.type !== GraphQLID
34
+ ) {
35
+ throw new Error(
36
+ `createAuth was called with an identityField of ${identityField} on the list ${listKey} ` +
37
+ `but that field doesn't allow being searched uniquely with a String or ID. ` +
38
+ `You should likely add \`isIndexed: 'unique'\` ` +
39
+ `to the field at ${listKey}.${identityField}`
40
+ )
41
+ }
42
+
43
+ const baseSchema = getBaseAuthSchema({
44
+ authGqlNames: authGqlNames,
45
+ identityField,
46
+ listKey,
47
+ secretField,
48
+ base,
49
+ })
50
+
51
+ // technically this will incorrectly error if someone has a schema extension that adds a field to the list output type
52
+ // and then wants to fetch that field with `sessionData` but it's extremely unlikely someone will do that since if
53
+ // they want to add a GraphQL field, they'll probably use a virtual field
54
+ const query = `query($id: ID!) { ${authGqlNames.itemQueryName}(where: { id: $id }) { ${sessionData} } }`
55
+
56
+ let ast
57
+ try {
58
+ ast = parse(query)
59
+ } catch (err) {
60
+ throw new Error(
61
+ `The query to get session data has a syntax error, the sessionData option in your createAuth usage is likely incorrect\n${err}`
62
+ )
63
+ }
64
+
65
+ const errors = validate(base.schema, ast)
66
+ if (errors.length) {
67
+ throw new Error(
68
+ `The query to get session data has validation errors, the sessionData option in your createAuth usage is likely incorrect\n${errors.join('\n')}`
69
+ )
70
+ }
71
+
72
+ return [
73
+ baseSchema.extension,
74
+ initFirstItem &&
75
+ getInitFirstItemSchema({
76
+ authGqlNames,
77
+ listKey,
78
+ fields: initFirstItem.fields,
79
+ defaultItemData: initFirstItem.itemData,
80
+ graphQLSchema: base.schema,
81
+ ItemAuthenticationWithPasswordSuccess: baseSchema.ItemAuthenticationWithPasswordSuccess,
82
+ }),
83
+ ].filter((x): x is Exclude<typeof x, undefined> => x !== undefined)
84
+ })