|
1 | | -import type { ReadStream } from 'node:fs' |
2 | | -import * as graphqlTsSchema from '@graphql-ts/schema' |
3 | | -// @ts-expect-error |
4 | | -import GraphQLUpload from 'graphql-upload/GraphQLUpload.js' |
| 1 | +import type * as graphqlTsSchema from '@graphql-ts/schema' |
5 | 2 | import type { GraphQLFieldExtensions, GraphQLResolveInfo } from 'graphql' |
6 | | -import { GraphQLError, GraphQLScalarType } from 'graphql' |
7 | | -import { Decimal as DecimalValue } from 'decimal.js' |
8 | 3 | import type { KeystoneContext } from '../context' |
9 | | -import type { JSONValue } from '../utils' |
10 | 4 | import { field as fieldd } from './schema-api-with-context' |
11 | 5 |
|
12 | 6 | export { |
@@ -42,7 +36,7 @@ export { bindGraphQLSchemaAPIToContext } from '@graphql-ts/schema' |
42 | 36 | export type { BaseSchemaMeta, Extension } from '@graphql-ts/extend' |
43 | 37 | export { extend, wrap } from '@graphql-ts/extend' |
44 | 38 | export { fields, interface, interfaceField, object, union } from './schema-api-with-context' |
45 | | - |
| 39 | +export { BigInt, CalendarDay, DateTime, Decimal, Empty, Hex, JSON, Upload } from './scalars' |
46 | 40 | // TODO: remove when we use { graphql } from '.keystone' |
47 | 41 | type SomeTypeThatIsntARecordOfArgs = string |
48 | 42 | export type FieldFuncResolve< |
@@ -122,235 +116,6 @@ type FieldFunc = < |
122 | 116 | export const field = fieldd as FieldFunc |
123 | 117 | // TODO: remove when we use { graphql } from '.keystone' |
124 | 118 |
|
125 | | -export const JSON = graphqlTsSchema.g.scalar<JSONValue>( |
126 | | - new GraphQLScalarType({ |
127 | | - name: 'JSON', |
128 | | - description: |
129 | | - 'The `JSON` scalar type represents JSON values as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf).', |
130 | | - specifiedByURL: 'http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf', |
131 | | - // the defaults for serialize, parseValue and parseLiteral do what makes sense for JSON |
132 | | - }) |
133 | | -) |
134 | | - |
135 | | -// avoiding using Buffer.from/etc. because we want a plain Uint8Array and that would be an extra conversion |
136 | | -// when https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array/toHex and etc. |
137 | | -// is available we should use that instead |
138 | | - |
139 | | -function hexToBytes(value: string): Uint8Array { |
140 | | - if (!/^[0-9a-fA-F]*$/.test(value)) { |
141 | | - throw new GraphQLError('Hex values must be a string of hexadecimal characters') |
142 | | - } |
143 | | - if (value.length % 2 !== 0) { |
144 | | - throw new GraphQLError('Hex values must have an even number of characters') |
145 | | - } |
146 | | - const bytes = new Uint8Array(value.length / 2) |
147 | | - for (let i = 0; i < bytes.byteLength; i += 1) { |
148 | | - const start = i * 2 |
149 | | - bytes[i] = parseInt(value.slice(start, start + 2), 16) |
150 | | - } |
151 | | - return bytes |
152 | | -} |
153 | | - |
154 | | -function bytesToHex(bytes: Uint8Array): string { |
155 | | - let str = '' |
156 | | - for (const byte of bytes) { |
157 | | - str += byte.toString(16).padStart(2, '0') |
158 | | - } |
159 | | - return str |
160 | | -} |
161 | | - |
162 | | -export const Hex = graphqlTsSchema.g.scalar<Uint8Array>( |
163 | | - new GraphQLScalarType({ |
164 | | - name: 'Hex', |
165 | | - description: 'The `Hex` scalar type represents bytes as a string of hexadecimal characters.', |
166 | | - parseLiteral(value) { |
167 | | - if (value.kind !== 'StringValue') { |
168 | | - throw new GraphQLError('Hex only accepts values as strings') |
169 | | - } |
170 | | - return hexToBytes(value.value) |
171 | | - }, |
172 | | - parseValue(value) { |
173 | | - // so that when you're doing a mutation in a resolver, you can just pass in a Uint8Array directly |
174 | | - if (value instanceof Uint8Array) { |
175 | | - // duplicate it though to avoid any weirdness with the array being mutated |
176 | | - // + ensuring that if you pass in a Buffer, resolvers recieve a normal Uint8Array |
177 | | - return Uint8Array.from(value) |
178 | | - } |
179 | | - if (typeof value !== 'string') { |
180 | | - throw new GraphQLError('Hex only accepts values as strings') |
181 | | - } |
182 | | - return hexToBytes(value) |
183 | | - }, |
184 | | - serialize(value) { |
185 | | - if (!(value instanceof Uint8Array)) { |
186 | | - throw new GraphQLError(`unexpected value provided to Hex scalar: ${value}`) |
187 | | - } |
188 | | - return bytesToHex(value) |
189 | | - }, |
190 | | - }) |
191 | | -) |
192 | | - |
193 | | -type FileUpload = { |
194 | | - filename: string |
195 | | - mimetype: string |
196 | | - encoding: string |
197 | | - createReadStream(): ReadStream |
198 | | -} |
199 | | - |
200 | | -export const Upload = graphqlTsSchema.g.scalar<Promise<FileUpload>>(GraphQLUpload) |
201 | | - |
202 | | -// - Decimal.js throws on invalid inputs |
203 | | -// - Decimal.js can represent +Infinity and -Infinity, these aren't values in Postgres' decimal, |
204 | | -// NaN is but Prisma doesn't support it |
205 | | -// .isFinite refers to +Infinity, -Infinity and NaN |
206 | | -export const Decimal = graphqlTsSchema.graphql.scalar<DecimalValue & { scaleToPrint?: number }>( |
207 | | - new GraphQLScalarType({ |
208 | | - name: 'Decimal', |
209 | | - serialize(value) { |
210 | | - if (!DecimalValue.isDecimal(value)) |
211 | | - throw new GraphQLError(`unexpected value provided to Decimal scalar: ${value}`) |
212 | | - const cast = value as DecimalValue & { scaleToPrint?: number } |
213 | | - if (cast.scaleToPrint !== undefined) return value.toFixed(cast.scaleToPrint) |
214 | | - return value.toString() |
215 | | - }, |
216 | | - parseLiteral(value) { |
217 | | - if (value.kind !== 'StringValue') |
218 | | - throw new GraphQLError('Decimal only accepts values as strings') |
219 | | - const decimal = new DecimalValue(value.value) |
220 | | - if (!decimal.isFinite()) throw new GraphQLError('Decimal values must be finite') |
221 | | - return decimal |
222 | | - }, |
223 | | - parseValue(value) { |
224 | | - if (DecimalValue.isDecimal(value)) { |
225 | | - if (!value.isFinite()) throw new GraphQLError('Decimal values must be finite') |
226 | | - return value |
227 | | - } |
228 | | - if (typeof value !== 'string') |
229 | | - throw new GraphQLError('Decimal only accepts values as strings') |
230 | | - const decimal = new DecimalValue(value) |
231 | | - if (!decimal.isFinite()) throw new GraphQLError('Decimal values must be finite') |
232 | | - return decimal |
233 | | - }, |
234 | | - }) |
235 | | -) |
236 | | - |
237 | | -export const BigInt = graphqlTsSchema.graphql.scalar<bigint>( |
238 | | - new GraphQLScalarType({ |
239 | | - name: 'BigInt', |
240 | | - serialize(value) { |
241 | | - if (typeof value !== 'bigint') |
242 | | - throw new GraphQLError(`unexpected value provided to BigInt scalar: ${value}`) |
243 | | - return value.toString() |
244 | | - }, |
245 | | - parseLiteral(value) { |
246 | | - if (value.kind !== 'StringValue') |
247 | | - throw new GraphQLError('BigInt only accepts values as strings') |
248 | | - return globalThis.BigInt(value.value) |
249 | | - }, |
250 | | - parseValue(value) { |
251 | | - if (typeof value === 'bigint') return value |
252 | | - if (typeof value !== 'string') throw new GraphQLError('BigInt only accepts values as strings') |
253 | | - return globalThis.BigInt(value) |
254 | | - }, |
255 | | - }) |
256 | | -) |
257 | | - |
258 | | -// from https://github.com/excitement-engineer/graphql-iso-date/blob/master/src/utils/validator.js#L121 |
259 | | -// this is also what prisma uses https://github.com/prisma/prisma/blob/20b58fe65d581bcb43c0d5c28d4b89cabc2d99b2/packages/client/src/runtime/utils/common.ts#L126-L128 |
260 | | -const RFC_3339_REGEX = |
261 | | - /^(\d{4}-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])T([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60))(\.\d{1,})?(([Z])|([+|-]([01][0-9]|2[0-3]):[0-5][0-9]))$/ |
262 | | - |
263 | | -function parseDate(input: string): Date { |
264 | | - if (!RFC_3339_REGEX.test(input)) { |
265 | | - throw new GraphQLError( |
266 | | - 'DateTime scalars must be in the form of a full ISO 8601 date-time string' |
267 | | - ) |
268 | | - } |
269 | | - const parsed = new Date(input) |
270 | | - if (isNaN(parsed.valueOf())) { |
271 | | - throw new GraphQLError( |
272 | | - 'DateTime scalars must be in the form of a full ISO 8601 date-time string' |
273 | | - ) |
274 | | - } |
275 | | - return parsed |
276 | | -} |
277 | | - |
278 | | -export const DateTime = graphqlTsSchema.g.scalar<Date>( |
279 | | - new GraphQLScalarType({ |
280 | | - name: 'DateTime', |
281 | | - specifiedByURL: 'https://datatracker.ietf.org/doc/html/rfc3339#section-5.6', |
282 | | - serialize(value: unknown) { |
283 | | - if (!(value instanceof Date) || isNaN(value.valueOf())) { |
284 | | - throw new GraphQLError(`unexpected value provided to DateTime scalar: ${value}`) |
285 | | - } |
286 | | - return value.toISOString() |
287 | | - }, |
288 | | - parseLiteral(value) { |
289 | | - if (value.kind !== 'StringValue') { |
290 | | - throw new GraphQLError('DateTime only accepts values as strings') |
291 | | - } |
292 | | - return parseDate(value.value) |
293 | | - }, |
294 | | - parseValue(value: unknown) { |
295 | | - if (value instanceof Date) return value |
296 | | - if (typeof value !== 'string') { |
297 | | - throw new GraphQLError('DateTime only accepts values as strings') |
298 | | - } |
299 | | - return parseDate(value) |
300 | | - }, |
301 | | - }) |
302 | | -) |
303 | | - |
304 | | -const RFC_3339_FULL_DATE_REGEX = /^\d{4}-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])$/ |
305 | | - |
306 | | -function validateCalendarDay(input: string) { |
307 | | - if (!RFC_3339_FULL_DATE_REGEX.test(input)) { |
308 | | - throw new GraphQLError('CalendarDay scalars must be in the form of a full-date ISO 8601 string') |
309 | | - } |
310 | | -} |
311 | | - |
312 | | -export const CalendarDay = graphqlTsSchema.g.scalar<string>( |
313 | | - new GraphQLScalarType({ |
314 | | - name: 'CalendarDay', |
315 | | - specifiedByURL: 'https://datatracker.ietf.org/doc/html/rfc3339#section-5.6', |
316 | | - serialize(value: unknown) { |
317 | | - if (typeof value !== 'string') { |
318 | | - throw new GraphQLError(`unexpected value provided to CalendarDay scalar: ${value}`) |
319 | | - } |
320 | | - return value |
321 | | - }, |
322 | | - parseLiteral(value) { |
323 | | - if (value.kind !== 'StringValue') { |
324 | | - throw new GraphQLError('CalendarDay only accepts values as strings') |
325 | | - } |
326 | | - validateCalendarDay(value.value) |
327 | | - return value.value |
328 | | - }, |
329 | | - parseValue(value: unknown) { |
330 | | - if (typeof value !== 'string') { |
331 | | - throw new GraphQLError('CalendarDay only accepts values as strings') |
332 | | - } |
333 | | - validateCalendarDay(value) |
334 | | - return value |
335 | | - }, |
336 | | - }) |
337 | | -) |
338 | | - |
339 | | -export const Empty = graphqlTsSchema.g.scalar<{}>( |
340 | | - new GraphQLScalarType({ |
341 | | - name: 'Empty', |
342 | | - serialize(value) { |
343 | | - return null |
344 | | - }, |
345 | | - parseLiteral(value) { |
346 | | - return {} |
347 | | - }, |
348 | | - parseValue(value) { |
349 | | - return {} |
350 | | - }, |
351 | | - }) |
352 | | -) |
353 | | - |
354 | 119 | export type NullableType<Context extends KeystoneContext = KeystoneContext> = |
355 | 120 | graphqlTsSchema.NullableType<Context> |
356 | 121 | export type Type<Context extends KeystoneContext = KeystoneContext> = graphqlTsSchema.Type<Context> |
|
0 commit comments