6. `prisma2 lift save --name init && prisma2 lift up`

If you have your local blog example running, you can now just do changes to `TSClient.ts` and re-execute `npx ts-node fixtures/generate.ts ./fixtures/blog/`.

keep keep keep keep keep replace keep keep keep

If you have your local blog fixture running, you can now do changes to `TSClient.ts` and re-execute `npx ts-node fixtures/generate.ts ./fixtures/blog/`.

When doing changes and working on a fixture use `yarn build && rm -rf fixtures/blog/node_modules/ && ts-node fixtures/generate.ts fixtures/blog`

import indent from 'indent-string'
import path from 'path'
import { DMMFClass } from '../runtime/dmmf'
import { DMMF } from '../runtime/dmmf-types'

export enum Projection {
  select = 'select',
  include = 'include',
}

export function getScalarsName(modelName: string) {
  return `${modelName}Scalars`
}

export function getPayloadName(modelName: string, projection: Projection) {
  return `${modelName}Get${capitalize(projection)}Payload`
}

// export function getExtractName(modelName: string, projection: Projection) {
//   return `Extract${modelName}${capitalize(projection)}`
// }

export function getSelectName(modelName: string) {
  return `${modelName}Select`
}

export function getIncludeName(modelName: string) {
  return `${modelName}Include`
}

export function getDefaultName(modelName: string) {
  return `${modelName}Default`
}

export function getFieldArgName(
  field: DMMF.SchemaField,
  projection?: Projection,
): string {
  return getArgName(
    (field.outputType.type as DMMF.OutputType).name,
    field.outputType.isList,
    projection,
  )
}

export function getArgName(
  name: string,
  isList: boolean,
  projection?: Projection,
): string {
  const projectionString = projection ? capitalize(projection) : ''
  if (!isList) {
    return `${name}${projectionString}Args`
  }

  return `FindMany${name}${projectionString}Args`
}

// we need names for all top level args,
// as GraphQL doesn't have the concept of unnamed args
export function getModelArgName(
  modelName: string,
  projection?: Projection,
  action?: DMMF.ModelAction,
): string {
  const projectionName = projection ? capitalize(projection) : ''
  if (!action) {
    return `${modelName}${projectionName}Args`
  }
  switch (action) {
    case DMMF.ModelAction.findMany:
      return `FindMany${modelName}${projectionName}Args`
    case DMMF.ModelAction.findOne:
      return `FindOne${modelName}${projectionName}Args`
    case DMMF.ModelAction.upsert:
      return `${modelName}${projectionName}UpsertArgs`
    case DMMF.ModelAction.update:
      return `${modelName}${projectionName}UpdateArgs`
    case DMMF.ModelAction.updateMany:
      return `${modelName}${projectionName}UpdateManyArgs`
    case DMMF.ModelAction.delete:
      return `${modelName}${projectionName}DeleteArgs`
    case DMMF.ModelAction.create:
      return `${modelName}${projectionName}CreateArgs`
    case DMMF.ModelAction.deleteMany:
      return `${modelName}${projectionName}DeleteManyArgs`
  }
}

export function getDefaultArgName(
  dmmf: DMMFClass,
  modelName: string,
  action: DMMF.ModelAction,
) {
  const mapping = dmmf.mappings.find(m => m.model === modelName)!

  const fieldName = mapping[action]
  const operation = getOperation(action)
  const queryType = operation === 'query' ? dmmf.queryType : dmmf.mutationType
  const field = queryType.fields.find(f => f.name === fieldName)!
  return (field.args[0].inputType[0].type as DMMF.InputType).name
}

export function getOperation(action: DMMF.ModelAction): 'query' | 'mutation' {
  if (
    action === DMMF.ModelAction.findMany ||
    action === DMMF.ModelAction.findOne
  ) {
    return 'query'
  }
  return 'mutation'
}

/**
 * Used to render the initial client args
 * @param modelName
 * @param fieldName
 * @param mapping
 */
export function renderInitialClientArgs(
  actionName: DMMF.ModelAction,
  fieldName: string,
  mapping: DMMF.Mapping,
): string {
  return `
  dmmf,
  fetcher,
  '${getOperation(actionName as DMMF.ModelAction)}',
  '${fieldName}',
  '${mapping.plural}.${actionName}',
  args,
  [],
  errorFormat,
  collectTimestamps\n`
}

export function getFieldTypeName(field: DMMF.SchemaField) {
  if (typeof field.outputType.type === 'string') {
    return field.outputType.type
  }

  return field.outputType.type.name
}

export function getType(name: string, isList: boolean, isOptional?: boolean) {
  return name + (isList ? '[]' : '') + (isOptional ? ' | null' : '')
}

export function getFieldType(field: DMMF.SchemaField) {
  return getType(getFieldTypeName(field), field.outputType.isList)
}

interface SelectReturnTypeOptions {
  name: string
  actionName: DMMF.ModelAction
  renderPromise?: boolean
  hideCondition?: boolean
  isField?: boolean
  fieldName?: string
  projection: Projection
}

/**
 * Get the complicated extract output
 * @param name Model name
 * @param actionName action name
 */
export function getSelectReturnType({
  name,
  actionName,
  renderPromise = true,
  hideCondition = false,
  isField = false,
  fieldName,
}: SelectReturnTypeOptions) {
  const isList = actionName === DMMF.ModelAction.findMany

  const selectArgName = isField
    ? getArgName(name, isList, Projection.select)
    : getModelArgName(name, Projection.select, actionName as DMMF.ModelAction)

  const includeArgName = isField
    ? getArgName(name, isList, Projection.include)
    : getModelArgName(name, Projection.include, actionName as DMMF.ModelAction)

  const requiredArgName =
    getModelArgName(name, undefined, actionName as DMMF.ModelAction) +
    'Required'
  const requiredCheck = `T extends ${requiredArgName} ? 'Please either choose \`select\` or \`include\`' : `
  if (actionName === 'deleteMany' || actionName === 'updateMany') {
    return `Promise<BatchPayload>`
  }

  /**
   * Important: We handle findMany or isList special, as we don't want chaining from there
   */
  if (isList || hideCondition) {
    const listOpen = isList ? 'Array<' : ''
    const listClose = isList ? '>' : ''
    const promiseOpen = renderPromise ? 'Promise<' : ''
    const promiseClose = renderPromise ? '>' : ''
    const renderType = (projection: Projection) =>
      `${promiseOpen}${listOpen}${getPayloadName(
        name,
        projection,
      )}<Extract${getModelArgName(name, projection, actionName)}<T>${
        actionName === 'findOne' ? ' | null' : ''
      }>${listClose}${promiseClose}`

    return `${requiredCheck}T extends ${selectArgName}
? ${renderType(Projection.select)} : T extends ${includeArgName}
? ${renderType(
      Projection.include,
    )} : ${promiseOpen}${listOpen}${name}${promiseClose}${listClose}`
  }

  const selectType = `${renderPromise ? 'Promise<' : ''}${getPayloadName(
    name,
    Projection.select,
  )}<Extract${selectArgName}<T>${renderPromise ? '>' : ''}${
    actionName === 'findOne' ? ' | null' : ''
  }>`

  const includeType = `${renderPromise ? 'Promise<' : ''}${getPayloadName(
    name,
    Projection.include,
  )}<Extract${includeArgName}<T>${renderPromise ? '>' : ''}${
    actionName === 'findOne' ? ' | null' : ''
  }>`

  return `${requiredCheck}T extends ${selectArgName} ? ${selectType}
: T extends ${includeArgName} ? ${includeType} : ${name}Client<${getType(
    name,
    isList,
  )}${actionName === 'findOne' ? ' | null' : ''}>`
}

export function isQueryAction(
  action: DMMF.ModelAction,
  operation: 'query' | 'mutation',
): boolean {
  if (!(action in DMMF.ModelAction)) {
    return false
  }
  const result =
    action === DMMF.ModelAction.findOne || action === DMMF.ModelAction.findMany
  return operation === 'query' ? result : !result
}

export function capitalize(str: string) {
  return str[0].toUpperCase() + str.slice(1)
}

export function indentAllButFirstLine(str: string, indentation: number) {
  const lines = str.split('\n')

  return lines[0] + '\n' + indent(lines.slice(1).join('\n'), indentation)
}

export function getRelativePathResolveStatement(
  outputDir: string,
  cwd?: string,
) {
  if (!cwd) {
    return 'undefined'
  }
  return `path.resolve(__dirname, ${JSON.stringify(
    path.relative(outputDir, cwd),
  )})`
}

function flatten(array) {
  return Array.prototype.concat.apply([], array)
}

export function flatMap<T, U>(
  array: T[],
  callbackFn: (value: T, index: number, array: T[]) => U[],
  thisArg?: any,
): U[] {
  return flatten(array.map(callbackFn, thisArg))
}


import chalk from 'chalk'
import { chinook } from '../fixtures/chinook'
import { DMMFClass, makeDocument, transformDocument } from '../runtime'
import { getDMMF } from '../runtime/getDMMF'
chalk.level = 0

describe('relation where transformation', () => {
  let dmmf
  beforeAll(async () => {
    dmmf = new DMMFClass(await getDMMF({ datamodel: chinook }))
  })

  test('transform correctly', () => {
    const select = {
      where: {
        Albums: {
          some: {
            Tracks: {
              some: {
                AND: {
                  UnitPrice: 5,
                  Playlists: {
                    some: {
                      Tracks: {
                        some: {
                          Name: '',
                          Genre: {
                            id: 5,
                          },
                        },
                      },
                    },
                  },
                },
              },
            },
          },
        },
      },
    }
    const document = makeDocument({
      dmmf,
      select,
      rootTypeName: 'query',
      rootField: 'findManyArtist',
    })

    expect(() => document.validate(select, false, 'users'))
      .toThrowErrorMatchingInlineSnapshot(`
"
Invalid \`photon.users()\` invocation:

{
  where: {
    Albums: {
      some: {
        Tracks: {
          some: {
            AND: {
              UnitPrice: 5,
              Playlists: {
                some: {
                  Tracks: {
                  ~~~~~~
                    some: {
                      Name: '',
                      Genre: {
                        id: 5
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

Unknown arg \`Tracks\` in where.Albums.some.Tracks.some.AND.0.Playlists.some.Tracks for type PlaylistTrackWhereInput. Did you mean \`Track\`? Available args:
type PlaylistTrackWhereInput {
  id?: Int | IntFilter
  AND?: PlaylistTrackWhereInput
  OR?: PlaylistTrackWhereInput
  NOT?: PlaylistTrackWhereInput
  Playlist?: PlaylistWhereInput
  Track?: TrackWhereInput
}

"
`)
  })

  test('throw correctly for incorrect deep scalar', () => {
    const select = {
      where: {
        Albums: {
          some: {
            Tracks: {
              some: {
                AND: {
                  UnitPrice: 5,
                  Playlists: {
                    some: {
                      Tracks: {
                        some: {
                          Name: '',
                          Genre: {
                            id: '5',
                          },
                        },
                      },
                    },
                  },
                },
              },
            },
          },
        },
      },
    }
    const document = makeDocument({
      dmmf,
      select,
      rootTypeName: 'query',
      rootField: 'findManyArtist',
    })
    expect(() => document.validate(select, false, 'users'))
      .toThrowErrorMatchingInlineSnapshot(`
"
Invalid \`photon.users()\` invocation:

{
  where: {
    Albums: {
      some: {
        Tracks: {
          some: {
            AND: {
              UnitPrice: 5,
              Playlists: {
                some: {
                  Tracks: {
                  ~~~~~~
                    some: {
                      Name: '',
                      Genre: {
                        id: '5'
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

Unknown arg \`Tracks\` in where.Albums.some.Tracks.some.AND.0.Playlists.some.Tracks for type PlaylistTrackWhereInput. Did you mean \`Track\`? Available args:
type PlaylistTrackWhereInput {
  id?: Int | IntFilter
  AND?: PlaylistTrackWhereInput
  OR?: PlaylistTrackWhereInput
  NOT?: PlaylistTrackWhereInput
  Playlist?: PlaylistWhereInput
  Track?: TrackWhereInput
}

"
`)
  })
  test('throw correctly for deep at least one error', () => {
    const select = {
      where: {
        Albums: {
          some: {
            Tracks: {
              some: {
                AND: {
                  UnitPrice: 5,
                  Playlists: {
                    some: {
                      Tracks: {
                        some: {
                          Name: '',
                          Genre: {},
                        },
                      },
                    },
                  },
                },
              },
            },
          },
        },
      },
    }
    const document = makeDocument({
      dmmf,
      select,
      rootTypeName: 'query',
      rootField: 'findManyArtist',
    })
    expect(() => document.validate(select, false, 'artists'))
      .toThrowErrorMatchingInlineSnapshot(`
"
Invalid \`photon.artists()\` invocation:

{
  where: {
    Albums: {
      some: {
        Tracks: {
          some: {
            AND: {
              UnitPrice: 5,
              Playlists: {
                some: {
                  Tracks: {
                  ~~~~~~
                    some: {
                      Name: '',
                      Genre: {}
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

Unknown arg \`Tracks\` in where.Albums.some.Tracks.some.AND.0.Playlists.some.Tracks for type PlaylistTrackWhereInput. Did you mean \`Track\`? Available args:
type PlaylistTrackWhereInput {
  id?: Int | IntFilter
  AND?: PlaylistTrackWhereInput
  OR?: PlaylistTrackWhereInput
  NOT?: PlaylistTrackWhereInput
  Playlist?: PlaylistWhereInput
  Track?: TrackWhereInput
}

"
`)
  })
})


If you want to use the local runtime in the blog example, just run

If you want to use the local runtime in the blog fixture, run

import { GeneratorConfig } from '@prisma/generator-helper'
import 'flat-map-polyfill' // unfortunately needed as it's not properly polyfilled in TypeScript
import indent from 'indent-string'
import { DMMFClass } from '../runtime/dmmf'
import { BaseField, DMMF } from '../runtime/dmmf-types'
import { capitalize, GraphQLScalarToJSTypeTable } from '../runtime/utils/common'
import { InternalDatasource } from '../runtime/utils/printDatasources'
import { DatasourceOverwrite } from './extractSqliteSources'
import { serializeDatasources } from './serializeDatasources'
import {
  flatMap,
  getDefaultName,
  getFieldArgName,
  getFieldTypeName,
  getIncludeName,
  getModelArgName,
  getPayloadName,
  getRelativePathResolveStatement,
  getScalarsName,
  getSelectName,
  getSelectReturnType,
  getType,
  // getExtractName,
  indentAllButFirstLine,
  isQueryAction,
  Projection,
  renderInitialClientArgs,
} from './utils'

const tab = 2

const commonCode = (runtimePath: string, version?: string) => `import {
  DMMF,
  DMMFClass,
  deepGet,
  deepSet,
  makeDocument,
  Engine,
  debugLib,
  transformDocument,
  chalk,
  printStack,
  mergeBy,
  unpack,
  stripAnsi
} from '${runtimePath}'

/**
 * Query Engine version: ${version || 'latest'}
 */

import path = require('path')
import fs = require('fs')

const debug = debugLib('photon')

/**
 * Utility Types
 */

export type Enumerable<T> = T | Array<T>

export type MergeTruthyValues<R extends object, S extends object> = {
  [key in keyof S | keyof R]: key extends false
    ? never
    : key extends keyof S
    ? S[key] extends false
      ? never
      : S[key]
    : key extends keyof R
    ? R[key]
    : never
}

export type CleanupNever<T> = { [key in keyof T]: T[key] extends never ? never : key }[keyof T]

/**
 * Subset
 * @desc From \`T\` pick properties that exist in \`U\`. Simple version of Intersection
 */
export type Subset<T, U> = { [key in keyof T]: key extends keyof U ? T[key] : never }

/**
 * A PhotonRequestError is an error that is thrown in conjunction to a concrete query that has been performed with Photon.js.
 */
export class PhotonRequestError extends Error {
  constructor(public message: string, public code?: string, public meta?: any) {
    super(message)
    this.code = code
    this.meta = meta
  }
}

class PhotonFetcher {
  constructor(
    private readonly photon: Photon,
    private readonly engine: Engine,
    private readonly debug = false,
    private readonly hooks?: Hooks,
    private readonly errorFormat?: ErrorFormat
  ) {}
  async request<T>(document: any, dataPath: string[] = [], rootField?: string, typeName?: string, isList?: boolean, callsite?: string, collectTimestamps?: any): Promise<T> {
    const query = String(document)
    debug('Request:')
    debug(query)
    if (this.hooks && this.hooks.beforeRequest) {
      this.hooks.beforeRequest({ query, path: dataPath, rootField, typeName, document })
    }
    try {
      collectTimestamps && collectTimestamps.record("Pre-connect")
      await this.photon.connect()
      collectTimestamps && collectTimestamps.record("Post-connect")
      collectTimestamps && collectTimestamps.record("Pre-engine")
      const result = await this.engine.request(query, typeName)
      collectTimestamps && collectTimestamps.record("Post-engine")
      debug('Response:')
      debug(result)
      collectTimestamps && collectTimestamps.record("Pre-unpack")
      const unpackResult = this.unpack(document, result, dataPath, rootField, isList)
      collectTimestamps && collectTimestamps.record("Post-unpack")
      return unpackResult
    } catch (e) {
      if (callsite) {
        const { stack } = printStack({
          callsite,
          originalMethod: dataPath.join('.'),
          onUs: e.isPanic
        })
        const message = stack + '\\n\\n' + e.message
        if (e.code) {
          throw new PhotonRequestError(this.sanitizeMessage(message), e.code, e.meta)
        }
        throw new Error(this.sanitizeMessage(message))
      } else {
        if (e.code) {
          throw new PhotonRequestError(this.sanitizeMessage(e.message), e.code, e.meta)
        }
        if (e.isPanic) {
          throw e
        } else {
          throw new Error(this.sanitizeMessage(e.message))
        }
      }
    }
  }
  sanitizeMessage(message: string): string {
    if (this.errorFormat && this.errorFormat !== 'pretty') {
      return stripAnsi(message)
    }

    return message
  }
  protected unpack(document: any, data: any, path: string[], rootField?: string, isList?: boolean) {
    const getPath: string[] = []
    if (rootField) {
      getPath.push(rootField)
    }
    getPath.push(...path.filter(p => p !== 'select' && p !== 'include'))
    return unpack({ document, data, path: getPath })
  }
}
class CollectTimestamps {
  public readonly records: Array<{ name: string, value: [number, number]}> = []
  public start: { name: string, value: [number, number]} | undefined = undefined
  constructor(startName: string) {
    this.start = { name: startName, value: process.hrtime() }
  }
  public record(name: string) {
    this.records.push({ name, value: process.hrtime() })
  }
  public elapsed(start: [number, number], end: [number, number]) {
    const diff = [ end[0] - start[0], end[1] - start[1] ];
    const nanoseconds = (diff[0] * 1e9) + diff[1];
    const milliseconds = nanoseconds / 1e6;
    return milliseconds;
  }
  public getResults() {
    const results = this.records.reduce((acc, record) => {
      const name = record.name.split('-')[1]
      if (acc[name]) {
        acc[name] = this.elapsed(acc[name], record.value)
      } else {
        acc[name] = record.value
      }
      return acc
    }, {})
    results.total = this.elapsed(this.start.value, this.records[this.records.length - 1].value)
    return results
  }
}
`

interface TSClientOptions {
  version?: string
  document: DMMF.Document
  runtimePath: string
  browser?: boolean
  datasources: InternalDatasource[]
  generator?: GeneratorConfig
  platforms?: string[]
  sqliteDatasourceOverrides?: DatasourceOverwrite[]
  schemaDir?: string
  outputDir: string
}

export class TSClient {
  protected readonly dmmf: DMMFClass
  protected readonly document: DMMF.Document
  protected readonly runtimePath: string
  protected readonly browser: boolean
  protected readonly outputDir: string
  protected readonly internalDatasources: InternalDatasource[]
  protected readonly generator?: GeneratorConfig
  protected readonly platforms?: string[]
  protected readonly sqliteDatasourceOverrides?: DatasourceOverwrite[]
  protected readonly version?: string
  protected readonly schemaDir?: string
  constructor({
    document,
    runtimePath,
    browser = false,
    datasources,
    generator,
    platforms,
    sqliteDatasourceOverrides,
    schemaDir,
    outputDir,
  }: TSClientOptions) {
    this.document = document
    this.runtimePath = runtimePath
    this.browser = browser
    this.internalDatasources = datasources
    this.generator = generator
    this.platforms = platforms
    this.sqliteDatasourceOverrides = sqliteDatasourceOverrides
    // We make a deep clone here as otherwise we would serialize circular references
    // which we're building up in the DMMFClass
    this.dmmf = new DMMFClass(JSON.parse(JSON.stringify(document)))
    this.schemaDir = schemaDir
    this.outputDir = outputDir
  }
  public toString() {
    return `${commonCode(this.runtimePath, this.version)}

/**
 * Build tool annotations
 * In order to make \`ncc\` and \`node-file-trace\` happy.
**/

${
  this.platforms
    ? this.platforms
        .map(p => `path.join(__dirname, 'runtime/query-engine-${p}');`)
        .join('\n')
    : ''
}

/**
 * Client
**/

${new PhotonClientClass(
  this.dmmf,
  this.internalDatasources,
  this.outputDir,
  this.browser,
  this.generator,
  this.sqliteDatasourceOverrides,
  this.schemaDir,
)}

${/*new Query(this.dmmf, 'query')*/ ''}

/**
 * Enums
 */

// Based on
// https://github.com/microsoft/TypeScript/issues/3192#issuecomment-261720275

function makeEnum<T extends {[index: string]: U}, U extends string>(x: T) { return x }

${this.dmmf.schema.enums.map(type => new Enum(type)).join('\n\n')}

${Object.values(this.dmmf.modelMap)
  .map(model => new Model(model, this.dmmf))
  .join('\n')}

/**
 * Deep Input Types
 */

${this.dmmf.inputTypes.map(inputType => new InputType(inputType)).join('\n')}

/**
 * Batch Payload for updateMany & deleteMany
 */

export type BatchPayload = {
  count: number
}

/**
 * DMMF
 */

export const dmmf: DMMF.Document = ${JSON.stringify(this.document)}
    `
  }
}

class Datasources {
  constructor(protected readonly internalDatasources: InternalDatasource[]) {}
  public toString() {
    const sources = this.internalDatasources
    return `export type Datasources = {
${indent(sources.map(s => `${s.name}?: string`).join('\n'), 2)}
}`
  }
}

class PhotonClientClass {
  constructor(
    protected readonly dmmf: DMMFClass,
    protected readonly internalDatasources: InternalDatasource[],
    protected readonly outputDir: string,
    protected readonly browser?: boolean,
    protected readonly generator?: GeneratorConfig,
    protected readonly sqliteDatasourceOverrides?: DatasourceOverwrite[],
    protected readonly cwd?: string,
  ) {}
  public toString() {
    const { dmmf } = this

    return `
${new Datasources(this.internalDatasources)}

export type LogLevel = 'INFO' | 'WARN' | 'QUERY'

export type LogOption = LogLevel | {
  level: LogLevel
  /**
   * @default 'stdout'
   */
  emit?: 'event' | 'stdout'
}

export type ErrorFormat = 'pretty' | 'colorless' | 'minimal'

export interface PhotonOptions {
  datasources?: Datasources

  /**
   * @default "pretty"
   */
  errorFormat?: ErrorFormat

  /**
   * @default false
   */
  log?: boolean | LogOption[]

  debug?: any

  /**
   * You probably don't want to use this. \`__internal\` is used by internal tooling.
   */
  __internal?: {
    debug?: boolean
    hooks?: Hooks
    engine?: {
      cwd?: string
      binaryPath?: string
    }
  }
}

export type Hooks = {
  beforeRequest?: (options: {query: string, path: string[], rootField?: string, typeName?: string, document: any}) => any
}

export class Photon {
  private fetcher: PhotonFetcher
  private readonly dmmf: DMMFClass
  private readonly engine: Engine
  private connectionPromise?: Promise<any>
  private errorFormat: ErrorFormat
  constructor(options: PhotonOptions = {}) {
    const useDebug = options.debug === true ? true : typeof options.debug === 'object' ? Boolean(options.debug.library) : false
    if (useDebug) {
      debugLib.enable('photon')
    }
    const debugEngine = options.debug === true ? true : typeof options.debug === 'object' ? Boolean(options.debug.engine) : false

    // datamodel = datamodel without datasources + printed datasources

    const predefinedDatasources = ${
      this.sqliteDatasourceOverrides
        ? indentAllButFirstLine(
            serializeDatasources(this.sqliteDatasourceOverrides),
            4,
          )
        : '[]'
    }
    const inputDatasources = Object.entries(options.datasources || {}).map(([name, url]) => ({ name, url: url! }))
    const datasources = mergeBy(predefinedDatasources, inputDatasources, (source: any) => source.name)

    const internal = options.__internal || {}
    const engineConfig = internal.engine || {}

    if (options.errorFormat) {
      this.errorFormat = options.errorFormat
    } else if (process.env.NODE_ENV === 'production') {
      this.errorFormat = 'minimal'
    } else if (process.env.NO_COLOR) {
      this.errorFormat = 'colorless'
    } else {
      this.errorFormat = 'pretty'
    }

    this.engine = new Engine({
      cwd: engineConfig.cwd || ${getRelativePathResolveStatement(
        this.outputDir,
        this.cwd,
      )},
      debug: debugEngine,
      datamodelPath: path.resolve(__dirname, 'schema.prisma'),
      prismaPath: engineConfig.binaryPath || undefined,
      datasources,
      generator: ${
        this.generator ? JSON.stringify(this.generator) : 'undefined'
      },
      showColors: this.errorFormat === 'pretty'
    })

    this.dmmf = new DMMFClass(dmmf)

    this.fetcher = new PhotonFetcher(this, this.engine, false, internal.hooks, this.errorFormat)
  }
  private async connectEngine(publicCall?: boolean) {
    return this.engine.start()
  }
  connect(): Promise<void> {
    if (this.connectionPromise) {
      return this.connectionPromise
    }
    this.connectionPromise = this.connectEngine(true)
    return this.connectionPromise!
  }
  async disconnect() {
    await this.engine.stop()
  }
  // won't be generated for now
  // private _query?: QueryDelegate
  // get query(): QueryDelegate {
  //   return this._query ? this._query: (this._query = QueryDelegate(this.dmmf, this.fetcher))
  // }
${indent(
  dmmf.mappings
    .filter(m => m.findMany)
    .map(
      m => `
get ${m.plural}(): ${m.model}Delegate {
  return ${m.model}Delegate(this.dmmf, this.fetcher, this.errorFormat, this.collectTimestamps)
}`,
    )
    .join('\n'),
  2,
)}
}
`
  }
}

class QueryPayloadType {
  constructor(protected readonly type: OutputType) {}
  public toString() {
    const { type } = this
    const { name } = type

    const relationFields = type.fields.filter(
      f => f.outputType.kind === 'object' && f.name !== 'node',
    )
    const relationFieldConditions =
      relationFields.length === 0
        ? ''
        : `\n${relationFields
            .map(f =>
              indent(
                `: P extends '${f.name}'\n? ${this.wrapArray(
                  f,
                  `${getPayloadName(
                    (f.outputType.type as DMMF.OutputType).name,
                    Projection.select,
                  )}<Extract${getModelArgName(
                    (f.outputType.type as DMMF.OutputType).name,
                    Projection.select,
                    f.outputType.isList
                      ? DMMF.ModelAction.findMany
                      : DMMF.ModelAction.findOne,
                  )}<S[P]>>`,
                )}`,
                8,
              ),
            )
            .join('\n')}`

    return `\
type ${getPayloadName(
      name,
      Projection.select,
    )}<S extends ${name}Args> = S extends ${name}Args
  ? {
      [P in keyof S] ${relationFieldConditions}
        : never
    } : never
  `
  }
  protected wrapArray(field: DMMF.SchemaField, str: string) {
    if (field.outputType.isList) {
      return `Array<${str}>`
    }
    return str
  }
}

/**
 * Generates the generic type to calculate a payload based on a include statement
 */
class PayloadType {
  constructor(
    protected readonly type: OutputType,
    protected readonly projection: Projection,
  ) {}
  public toString() {
    const { type, projection } = this
    const { name } = type

    const relationFields = type.fields.filter(
      f => f.outputType.kind === 'object',
    )
    const relationFieldConditions =
      relationFields.length === 0
        ? ''
        : `\n${relationFields
            .map(f =>
              indent(
                `: P extends '${f.name}'\n? ${this.wrapArray(
                  f,
                  `${getPayloadName(
                    (f.outputType.type as DMMF.OutputType).name,
                    projection,
                  )}<Extract${getFieldArgName(f, projection)}<S[P]>>${
                    !f.outputType.isRequired && !f.outputType.isList
                      ? ' | null'
                      : ''
                  }`,
                )}`,
                8,
              ),
            )
            .join('\n')}`

    const hasScalarFields =
      type.fields.filter(f => f.outputType.kind !== 'object').length > 0
    const projectionName =
      projection === Projection.select
        ? getSelectName(name)
        : getIncludeName(name)
    return `\
export type ${getPayloadName(
      name,
      projection,
    )}<S extends boolean | ${projectionName}> = S extends true
  ? ${name}
  : S extends ${projectionName}
  ? {
      [P in CleanupNever<MergeTruthyValues<${
        projection === Projection.select ? '{}' : getDefaultName(name)
      }, S>>]${
      hasScalarFields
        ? `: P extends ${getScalarsName(name)}
        ? ${name}[P]`
        : ''
    }${relationFieldConditions}
        : never
    }
   : never`
  }
  protected wrapArray(field: DMMF.SchemaField, str: string) {
    if (field.outputType.isList) {
      return `Array<${str}>`
    }
    return str
  }
}

/**
 * Generates the default selection of a model
 */
class ModelDefault {
  constructor(
    protected readonly model: DMMF.Model,
    protected readonly dmmf: DMMFClass,
  ) {}
  public toString() {
    const { model } = this
    return `\
type ${getDefaultName(model.name)} = {
${indent(
  model.fields
    .filter(f => this.isDefault(f))
    .map(f => `${f.name}: true`)
    .join('\n'),
  tab,
)}
}
`
  }
  protected isDefault(field: DMMF.Field) {
    if (field.kind !== 'object') {
      return true
    }

    const model = this.dmmf.datamodel.models.find(m => field.type === m.name)
    return model!.isEmbedded
  }
}

export class Model {
  protected outputType?: OutputType
  protected mapping: DMMF.Mapping
  constructor(
    protected readonly model: DMMF.Model,
    protected readonly dmmf: DMMFClass,
  ) {
    const outputType = dmmf.outputTypeMap[model.name]
    this.outputType = new OutputType(outputType)
    this.mapping = dmmf.mappings.find(m => m.model === model.name)!
  }
  protected get argsTypes() {
    const { mapping, model } = this
    const argsTypes: Stringifiable[] = []
    for (const action in DMMF.ModelAction) {
      const fieldName = mapping[action]
      if (!fieldName) {
        continue
      }
      const field = this.dmmf.getField(fieldName)
      if (!field) {
        throw new Error(
          `Oops this must not happen. Could not find field ${fieldName} on either Query or Mutation`,
        )
      }
      if (action === 'updateMany' || action === 'deleteMany') {
        argsTypes.push(
          new MinimalArgsType(field.args, model, action as DMMF.ModelAction),
        )
      } else {
        argsTypes.push(
          new ArgsType(field.args, model, action as DMMF.ModelAction),
        )
      }
    }

    argsTypes.push(new ArgsType([], model))

    return argsTypes
  }
  public toString() {
    const { model, outputType } = this

    if (!outputType) {
      return ''
    }

    const scalarFields = model.fields.filter(f => f.kind !== 'object')

    return `
/**
 * Model ${model.name}
 */

export type ${model.name} = {
${indent(
  model.fields
    .filter(f => f.kind !== 'object')
    .map(field => new OutputField(field).toString())
    .join('\n'),
  tab,
)}
}

${
  scalarFields.length > 0
    ? `export type ${getScalarsName(model.name)} = ${
        scalarFields.length > 0
          ? scalarFields.map(f => `'${f.name}'`).join(' | ')
          : ``
      }
  `
    : ''
}

export type ${getSelectName(model.name)} = {
${indent(
  outputType.fields
    .map(
      f =>
        `${f.name}?: boolean` +
        (f.outputType.kind === 'object'
          ? ` | ${getFieldArgName(f, Projection.select)}Optional`
          : ''),
    )
    .join('\n'),
  tab,
)}
}

export type ${getIncludeName(model.name)} = {
${indent(
  outputType.fields
    .filter(f => f.outputType.kind === 'object')
    .map(
      f =>
        `${f.name}?: boolean` +
        (f.outputType.kind === 'object'
          ? ` | ${getFieldArgName(f, Projection.include)}Optional`
          : ''),
    )
    .join('\n'),
  tab,
)}
}

${new ModelDefault(model, this.dmmf)}

${new PayloadType(this.outputType!, Projection.select)}

${new PayloadType(this.outputType!, Projection.include)}

${new ModelDelegate(this.outputType!, this.dmmf)}

// Custom InputTypes
${this.argsTypes.map(String).join('\n')}
`
  }
}

export class Query {
  constructor(
    protected readonly dmmf: DMMFClass,
    protected readonly operation: 'query' | 'mutation',
  ) {}
  public toString() {
    const { dmmf, operation } = this
    const queryName = capitalize(operation)
    const mappings = dmmf.mappings.map(mapping => ({
      name: mapping.model,
      mapping: Object.entries(mapping).filter(([key]) =>
        isQueryAction(key as DMMF.ModelAction, operation),
      ),
    }))
    const queryType = operation === 'query' ? dmmf.queryType : dmmf.mutationType
    const outputType = new OutputType(queryType)
    return `\
/**
 * ${queryName}
 */

export type ${queryName}Args = {
${indent(
  flatMap(mappings, ({ name, mapping }) =>
    mapping
      .filter(([action, field]) => field)
      .map(
        ([action, field]) =>
          `${field}?: ${getModelArgName(
            name,
            Projection.select,
            action as DMMF.ModelAction,
          )}`,
      ),
  ).join('\n'),
  tab,
)}
}

${new QueryPayloadType(outputType)}

${new QueryDelegate(outputType)}
`
  }
}
export class ModelDelegate {
  constructor(
    protected readonly outputType: OutputType,
    protected readonly dmmf: DMMFClass,
  ) {}
  public toString() {
    const { fields, name } = this.outputType
    const mapping = this.dmmf.mappings.find(m => m.model === name)!
    const actions = Object.entries(mapping).filter(
      ([key, value]) =>
        key !== 'model' && key !== 'plural' && key !== 'aggregate' && value,
    )

    const listConstraint = getModelArgName(
      name,
      /*projection*/ undefined,
      DMMF.ModelAction.findMany,
    )
    // TODO: The following code needs to be split up and is a mess
    return `\
export interface ${name}Delegate {
  <T extends ${listConstraint}>(args?: Subset<T, ${getModelArgName(
      name,
      undefined,
      DMMF.ModelAction.findMany,
    )}>): ${getSelectReturnType({
      name,
      actionName: DMMF.ModelAction.findMany,
      hideCondition: true,
      isField: false,
      renderPromise: true,
      projection: Projection.select,
    })}
${indent(
  actions
    .map(
      ([actionName]: [any, any]) =>
        `${actionName}<T extends ${getModelArgName(
          name,
          /*projection*/ undefined,
          actionName,
        )}>(
  args${
    actionName === DMMF.ModelAction.findMany ? '?' : ''
  }: Subset<T, ${getModelArgName(name, undefined, actionName)}>
): ${getSelectReturnType({ name, actionName, projection: Projection.select })}`,
    )
    .join('\n'),
  tab,
)}
  count(): Promise<number>
}
function ${name}Delegate(dmmf: DMMFClass, fetcher: PhotonFetcher, errorFormat: ErrorFormat, collectTimestamps?: CollectTimestamps): ${name}Delegate {
  const ${name} = <T extends ${listConstraint}>(args: Subset<T, ${getModelArgName(
      name,
      undefined,
      DMMF.ModelAction.findMany,
    )}>) => new ${name}Client<${getSelectReturnType({
      name,
      actionName: DMMF.ModelAction.findMany,
      projection: Projection.select,
    })}>(dmmf, fetcher, 'query', '${mapping.findMany}', '${
      mapping.plural
    }', args, [], errorFormat)
${indent(
  actions
    .map(([actionName, fieldName]: [any, any]) =>
      actionName === 'deleteMany' || actionName === 'updateMany'
        ? `${name}.${actionName} = (args: ${getModelArgName(
            name,
            undefined,
            actionName,
          )}) => new ${name}Client<Promise<BatchPayload>>(${renderInitialClientArgs(
            actionName,
            fieldName,
            mapping,
          )})`
        : `${name}.${actionName} = <T extends ${getModelArgName(
            name,
            /*projection*/ undefined,
            actionName as DMMF.ModelAction,
          )}>(args: Subset<T, ${getModelArgName(
            name,
            Projection.select,
            actionName as DMMF.ModelAction,
          )}>) => ${
            actionName !== 'findMany' ? `args.select ? ` : ''
          }new ${name}Client<${getSelectReturnType({
            name,
            actionName,
            hideCondition: false,
            isField: true,
            projection: Projection.select,
          })}>(${renderInitialClientArgs(actionName, fieldName, mapping)})${
            actionName !== 'findMany'
              ? ` : new ${name}Client<${(getType(
                  name,
                  actionName === 'findMany',
                ),
                actionName === 'findOne')}>(${renderInitialClientArgs(
                  actionName,
                  fieldName,
                  mapping,
                )})`
              : ''
          }`,
    )
    .join('\n'),
  tab,
)}
  ${name}.count = () => new ${name}Client<number>(dmmf, fetcher, 'query', '${mapping.aggregate!}', '${
      mapping.plural
    }.count', {}, ['count'], errorFormat)
  return ${name} as any // any needed because of https://github.com/microsoft/TypeScript/issues/31335
}

export class ${name}Client<T> implements Promise<T> {
  private _callsite: any
  private _requestPromise?: Promise<any>
  constructor(
    private readonly _dmmf: DMMFClass,
    private readonly _fetcher: PhotonFetcher,
    private readonly _queryType: 'query' | 'mutation',
    private readonly _rootField: string,
    private readonly _clientMethod: string,
    private readonly _args: any,
    private readonly _dataPath: string[],
    private readonly _errorFormat: ErrorFormat,
    private readonly _collectTimestamps: CollectTimestamps,
    private _isList = false
  ) {
    // Timestamps for performance checks
    this._collectTimestamps = new CollectTimestamps("PhotonClient")
    // @ts-ignore
    if (process.env.NODE_ENV !== 'production' && this._errorFormat !== 'minimal') {
      const error = new Error()
      if (error && error.stack) {
        const stack = error.stack
        this._callsite = stack
      }
    }
  }
  readonly [Symbol.toStringTag]: 'PhotonPromise'

${indent(
  fields
    .filter(f => f.outputType.kind === 'object')
    .map(f => {
      const fieldTypeName = (f.outputType.type as DMMF.OutputType).name
      return `
${f.name}<T extends ${getFieldArgName(
        f,
      )} = {}>(args?: Subset<T, ${getFieldArgName(f)}>): ${getSelectReturnType({
        name: fieldTypeName,
        actionName: f.outputType.isList
          ? DMMF.ModelAction.findMany
          : DMMF.ModelAction.findOne,
        hideCondition: false,
        isField: true,
        renderPromise: true,
        fieldName: f.name,
        projection: Projection.select,
      })} {
  const prefix = this._dataPath.includes('select') ? 'select' : this._dataPath.includes('include') ? 'include' : 'select'
  const dataPath = [...this._dataPath, prefix, '${f.name}']
  const newArgs = deepSet(this._args, dataPath, args || true)
  this._isList = ${f.outputType.isList}
  return new ${getFieldTypeName(f)}Client<${getSelectReturnType({
        name: fieldTypeName,
        actionName: f.outputType.isList
          ? DMMF.ModelAction.findMany
          : DMMF.ModelAction.findOne,
        hideCondition: false,
        isField: true,
        renderPromise: true,
        projection: Projection.select,
      })}>(this._dmmf, this._fetcher, this._queryType, this._rootField, this._clientMethod, newArgs, dataPath, this._errorFormat, this._collectTimestamps, this._isList) as any
}`
    })
    .join('\n'),
  2,
)}

  private get _document() {
    const { _rootField: rootField } = this
    this._collectTimestamps && this._collectTimestamps.record("Pre-makeDocument")
    const document = makeDocument({
      dmmf: this._dmmf,
      rootField,
      rootTypeName: this._queryType,
      select: this._args
    })
    this._collectTimestamps && this._collectTimestamps.record("Post-makeDocument")
    try {
      this._collectTimestamps && this._collectTimestamps.record("Pre-document.validate")
      document.validate(this._args, false, this._clientMethod, this._errorFormat)
      this._collectTimestamps && this._collectTimestamps.record("Post-document.validate")
    } catch (e) {
      const x: any = e
      if (this._errorFormat !== 'minimal' && x.render) {
        if (this._callsite) {
          e.message = x.render(this._callsite)
        }
      }
      throw e
    }
    this._collectTimestamps && this._collectTimestamps.record("Pre-transformDocument")
    const transformedDocument = transformDocument(document)
    this._collectTimestamps && this._collectTimestamps.record("Post-transformDocument")
    return transformedDocument
  }

  /**
   * Attaches callbacks for the resolution and/or rejection of the Promise.
   * @param onfulfilled The callback to execute when the Promise is resolved.
   * @param onrejected The callback to execute when the Promise is rejected.
   * @returns A Promise for the completion of which ever callback is executed.
   */
  then<TResult1 = T, TResult2 = never>(
    onfulfilled?: ((value: T) => TResult1 | Promise<TResult1>) | undefined | null,
    onrejected?: ((reason: any) => TResult2 | Promise<TResult2>) | undefined | null,
  ): Promise<TResult1 | TResult2> {
    if (!this._requestPromise){
      this._requestPromise = this._fetcher.request<T>(this._document, this._dataPath, this._rootField, '${name}', this._isList, this._callsite, this._collectTimestamps)
    }
    return this._requestPromise!.then(onfulfilled, onrejected)
  }

  /**
   * Attaches a callback for only the rejection of the Promise.
   * @param onrejected The callback to execute when the Promise is rejected.
   * @returns A Promise for the completion of the callback.
   */
  catch<TResult = never>(
    onrejected?: ((reason: any) => TResult | Promise<TResult>) | undefined | null,
  ): Promise<T | TResult> {
    if (!this._requestPromise) {
      this._requestPromise = this._fetcher.request<T>(this._document, this._dataPath, this._rootField, '${name}', this._isList, this._callsite, this._collectTimestamps)
    }
    return this._requestPromise!.catch(onrejected)
  }

  /**
   * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The
   * resolved value cannot be modified from the callback.
   * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).
   * @returns A Promise for the completion of the callback.
   */
  finally(onfinally?: (() => void) | undefined | null): Promise<T> {
    if (!this._requestPromise) {
      this._requestPromise = this._fetcher.request<T>(this._document, this._dataPath, this._rootField, '${name}', this._isList, this._callsite, this._collectTimestamps)
    }
    return this._requestPromise!.finally(onfinally)
  }
}
    `
  }
}

export class QueryDelegate {
  constructor(protected readonly outputType: OutputType) {}
  public toString() {
    const name = this.outputType.name
    return `\
interface ${name}Delegate {
  <T extends ${name}Args>(args: Subset<T,${name}Args>): Promise<${getPayloadName(
      name,
      Projection.select,
    )}<T>>
}
function ${name}Delegate(dmmf: DMMFClass, fetcher: PhotonFetcher): ${name}Delegate {
  const ${name} = <T extends ${name}Args>(args: ${name}Args) => new ${name}Client<T>(dmmf, fetcher, args, [])
  return ${name}
}

class ${name}Client<T extends ${name}Args, U = ${getPayloadName(
      name,
      Projection.select,
    )}<T>> implements Promise<U> {
  constructor(private readonly dmmf: DMMFClass, private readonly fetcher: PhotonFetcher, private readonly args: ${name}Args, private readonly _dataPath: []) {}

  readonly [Symbol.toStringTag]: 'Promise'

  protected get document() {
    const rootField = Object.keys(this.args)[0]
    const document = makeDocument({
      dmmf: this.dmmf,
      rootField,
      rootTypeName: 'query',
      // @ts-ignore
      select: this.args[rootField]
    })
    // @ts-ignore
    document.validate(this.args[rootField], true)
    return document
  }

  /**
   * Attaches callbacks for the resolution and/or rejection of the Promise.
   * @param onfulfilled The callback to execute when the Promise is resolved.
   * @param onrejected The callback to execute when the Promise is rejected.
   * @returns A Promise for the completion of which ever callback is executed.
   */
  then<TResult1 = U, TResult2 = never>(
    onfulfilled?: ((value: U) => TResult1 | Promise<TResult1>) | undefined | null,
    onrejected?: ((reason: any) => TResult2 | Promise<TResult2>) | undefined | null,
  ): Promise<TResult1 | TResult2> {
    return this.fetcher.request<U>(this.document, this._dataPath, undefined, '${name}').then(onfulfilled, onrejected)
  }

  /**
   * Attaches a callback for only the rejection of the Promise.
   * @param onrejected The callback to execute when the Promise is rejected.
   * @returns A Promise for the completion of the callback.
   */
  catch<TResult = never>(
    onrejected?: ((reason: any) => TResult | Promise<TResult>) | undefined | null,
  ): Promise<U | TResult> {
    return this.fetcher.request<U>(this.document, this._dataPath, undefined, '${name}').catch(onrejected)
  }
}
    `
  }
}

export class InputField {
  constructor(
    protected readonly field: DMMF.SchemaArg,
    protected readonly prefixFilter = false,
  ) {}
  public toString() {
    const { field } = this
    let fieldType
    if (Array.isArray(field.inputType)) {
      fieldType = flatMap(field.inputType, t =>
        typeof t.type === 'string'
          ? GraphQLScalarToJSTypeTable[t.type] || t.type
          : this.prefixFilter
          ? `Base${t.type.name}`
          : t.type.name,
      ).join(' | ')
    }
    const fieldInputType = field.inputType[0]
    const optionalStr = fieldInputType.isRequired ? '' : '?'
    if (fieldInputType.isList) {
      fieldType = `Enumerable<${fieldType}>`
    }
    const nullableStr = !fieldInputType.isRequired ? ' | null' : ''
    return `${field.name}${optionalStr}: ${fieldType}${nullableStr}`
  }
}

export class OutputField {
  constructor(protected readonly field: BaseField) {}
  public toString() {
    const { field } = this
    // ENUMTODO
    let fieldType =
      typeof field.type === 'string'
        ? GraphQLScalarToJSTypeTable[field.type] || field.type
        : field.type[0].name
    if (Array.isArray(fieldType)) {
      fieldType = fieldType[0]
    }
    const arrayStr = field.isList ? `[]` : ''
    const nullableStr = !field.isRequired && !field.isList ? ' | null' : ''
    return `${field.name}: ${fieldType}${arrayStr}${nullableStr}`
  }
}

export class OutputType {
  public name: string
  public fields: DMMF.SchemaField[]
  constructor(protected readonly type: DMMF.OutputType) {
    this.name = type.name
    this.fields = type.fields
  }
  public toString() {
    const { type } = this
    return `
export type ${type.name} = {
${indent(
  type.fields
    .map(field => new OutputField({ ...field, ...field.outputType }).toString())
    .join('\n'),
  tab,
)}
}`
  }
}

interface Stringifiable {
  toString(): string
}

export class MinimalArgsType {
  constructor(
    protected readonly args: DMMF.SchemaArg[],
    protected readonly model: DMMF.Model,
    protected readonly action?: DMMF.ModelAction,
  ) {}
  public toString() {
    const { action, args } = this
    const { name } = this.model

    return `
/**
 * ${name} ${action ? action : 'without action'}
 */
export type ${getModelArgName(name, undefined, action)} = {
${indent(args.map(arg => new InputField(arg).toString()).join('\n'), tab)}
}
`
  }
}

export class ArgsType {
  constructor(
    protected readonly args: DMMF.SchemaArg[],
    protected readonly model: DMMF.Model,
    protected readonly action?: DMMF.ModelAction,
  ) {}
  public toString() {
    const { action, args } = this
    const { name } = this.model

    const bothArgsOptional: DMMF.SchemaArg[] = [
      {
        name: 'select',
        inputType: [
          {
            type: getSelectName(name),
            kind: 'object',
            isList: false,
            isRequired: false,
          },
        ],
      },
      {
        name: 'include',
        inputType: [
          {
            type: getIncludeName(name),
            kind: 'object',
            isList: false,
            isRequired: false,
          },
        ],
      },
      ...args,
    ]

    const bothArgsRequired: DMMF.SchemaArg[] = [
      {
        name: 'select',
        inputType: [
          {
            type: getSelectName(name),
            kind: 'object',
            isList: false,
            isRequired: true,
          },
        ],
      },
      {
        name: 'include',
        inputType: [
          {
            type: getIncludeName(name),
            kind: 'object',
            isList: false,
            isRequired: true,
          },
        ],
      },
      ...args,
    ]

    const selectArgsRequired: DMMF.SchemaArg[] = [
      {
        name: 'select',
        inputType: [
          {
            type: getSelectName(name),
            kind: 'object',
            isList: false,
            isRequired: true,
          },
        ],
      },
      ...args,
    ]

    const selectArgsOptional: DMMF.SchemaArg[] = [
      {
        name: 'select',
        inputType: [
          {
            type: getSelectName(name),
            kind: 'object',
            isList: false,
            isRequired: false,
          },
        ],
      },
      ...args,
    ]

    const includeArgsRequired: DMMF.SchemaArg[] = [
      {
        name: 'include',
        inputType: [
          {
            type: getIncludeName(name),
            kind: 'object',
            isList: false,
            isRequired: true,
          },
        ],
      },
      ...args,
    ]

    const includeArgsOptional: DMMF.SchemaArg[] = [
      {
        name: 'include',
        inputType: [
          {
            type: getIncludeName(name),
            kind: 'object',
            isList: false,
            isRequired: false,
          },
        ],
      },
      ...args,
    ]

    return `
/**
 * ${name} ${action ? action : 'without action'}
 */
export type ${getModelArgName(name, undefined, action)} = {
${indent(
  bothArgsOptional.map(arg => new InputField(arg).toString()).join('\n'),
  tab,
)}
}

export type ${getModelArgName(name, undefined, action)}Required = {
${indent(
  bothArgsRequired.map(arg => new InputField(arg).toString()).join('\n'),
  tab,
)}
}

export type ${getModelArgName(name, Projection.select, action)} = {
${indent(
  selectArgsRequired.map(arg => new InputField(arg).toString()).join('\n'),
  tab,
)}
}

export type ${getModelArgName(name, Projection.select, action)}Optional = {
${indent(
  selectArgsOptional.map(arg => new InputField(arg).toString()).join('\n'),
  tab,
)}
}

export type ${getModelArgName(name, Projection.include, action)} = {
${indent(
  includeArgsRequired.map(arg => new InputField(arg).toString()).join('\n'),
  tab,
)}
}

export type ${getModelArgName(name, Projection.include, action)}Optional = {
${indent(
  includeArgsOptional.map(arg => new InputField(arg).toString()).join('\n'),
  tab,
)}
}

export type Extract${getModelArgName(
      name,
      Projection.select,
      action,
    )}<S extends undefined | boolean | ${getModelArgName(
      name,
      Projection.select,
      action,
    )}Optional> = S extends undefined
  ? false
  : S extends boolean
  ? S
  : S extends ${getModelArgName(name, Projection.select, action)}
  ? S['select']
  : true

export type Extract${getModelArgName(
      name,
      Projection.include,
      action,
    )}<S extends undefined | boolean | ${getModelArgName(
      name,
      Projection.include,
      action,
    )}Optional> = S extends undefined
  ? false
  : S extends boolean
  ? S
  : S extends ${getModelArgName(name, Projection.include, action)}
  ? S['include']
  : true

`
  }
}

export class InputType {
  constructor(protected readonly type: DMMF.InputType) {}
  public toString() {
    const { type } = this
    // TO DISCUSS: Should we rely on TypeScript's error messages?
    const body = `{
${indent(
  type.fields
    .map(arg => new InputField(arg /*, type.atLeastOne && !type.atMostOne*/))
    .join('\n'),
  tab,
)}
}`
    //     if (type.atLeastOne && !type.atMostOne) {
    //       return `export type Base${type.name} = ${body}
    // export type ${type.name} = AtLeastOne<Base${type.name}>
    //       `
    //     } else if (type.atLeastOne && type.atMostOne) {
    //       return `export type Base${type.name} = ${body}
    // export type ${type.name} = OnlyOne<Base${type.name}>
    //       `
    //     }
    return `
export type ${type.name} = ${body}`
  }
}

export class Enum {
  constructor(protected readonly type: DMMF.Enum) {}
  public toString() {
    const { type } = this

    return `export const ${type.name} = makeEnum({
${indent(type.values.map(v => `${v}: '${v}'`).join(',\n'), tab)}
})

export type ${type.name} = (typeof ${type.name})[keyof typeof ${type.name}]\n`
  }
}


export { NodeEngine as Engine } from './NodeEngine'
export { getInternalDatamodelJson } from './getInternalDatamodelJson'
export { printGeneratorConfig } from './printGeneratorConfig'
export { fixPlatforms } from './util'


# @prisma/photon

This package is being used by `prisma2`.

## Contributing

### Getting a local test version running

1. Clone this repo
2. `cd photonjs/packages/photon`
3. `yarn`
4. `ts-node fixtures/generate.ts ./fixtures/blog/`
5. `cd fixtures/blog`
6. `prisma2 lift save --name init && prisma2 lift up`
7. `ts-node main.ts`

### Working on code generation

If you have your local blog example running, you can now just do changes to `TSClient.ts` and re-execute `npx ts-node fixtures/generate.ts ./fixtures/blog/`.

### Working with the runtime

If you want to use the local runtime in the blog example, just run

```
ts-node fixtures/generate.ts ./fixtures/blog/ --local-runtime
```

Changes to `query.ts` will then be reflected when running `fixtures/blog/main.ts`


const identity = str => str || ''

const browserChalk: any = new Proxy(identity, {
  get: (obj, prop) => {
    return browserChalk
  },
  apply: (target, that, args) => {
    return target(args[0])
  },
})

export default browserChalk


                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright [yyyy] [name of copyright owner]

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.


import * as process from 'process'
import { spawn, ChildProcess } from 'child_process'

/**
 * Prisma Client
 */

type Settings = {
  executablePath: string
}

// could be a class if we want to require new Prisma(...)
export default function Prisma(settings: Partial<Settings> = {}) {
  return new PrismaClient({
    executablePath: settings.executablePath || './rust.ts',
  })
}

class PrismaClient {
  constructor(settings: Settings) {}

  async send<T>(query: string): Promise<T> {
    // wait for rust to open (if it's resolved already, should just return immediately)
    // const rust = await this.rust
    console.log(query)
    return {} as T
  }

  async close() {}

  // lazily initialize once and cache
  private _users?: UsersDelegate
  get users() {
    return this._users ? this._users : (this._users = UsersDelegate(this))
  }
}

// User query parameters
type UserQuery = Partial<User>

// User output
type User = {
  id: number
  email: string
  firstName: string
}

// UserDelegate is a helper function to pass our initialized Prisma client to UsersClient
interface UsersDelegate {
  (query: UserQuery): UsersClient
  byEmail(email: string): UsersClient
}
function UsersDelegate(prisma: PrismaClient, from: PostQuery = {}): UsersDelegate {
  //  new UsersClient(...) will be called each time, but I think this is optimized
  // in V8 since we're not dynamically recreating the class each time.
  // TODO: double-check
  const Users = (userQuery: UserQuery) => new UsersClient(prisma, Object.assign(from, userQuery))
  Users.byEmail = (email: string) => new UsersClient(prisma, Object.assign(from, { email }))
  return Users
}

// private user client class
class UsersClient<T = User[]> implements PromiseLike<T> {
  constructor(private readonly prisma: PrismaClient, private readonly userQuery: UserQuery) {}

  // lazily initialize once and cache
  private _posts?: PostsDelegate
  get posts() {
    return this._posts ? this._posts : (this._posts = PostsDelegate(this.prisma, { user: this.userQuery }))
  }

  /**
   * NOTE: Pulled from typescript's: interface Promise<T> { ... }
   * via "Go to definition" on Promise
   */
  /**
   * Attaches callbacks for the resolution and/or rejection of the Promise.
   * @param onfulfilled The callback to execute when the Promise is resolved.
   * @param onrejected The callback to execute when the Promise is rejected.
   * @returns A Promise for the completion of which ever callback is executed.
   */
  then<TResult1 = T, TResult2 = never>(
    onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null,
    onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null,
  ): Promise<TResult1 | TResult2> {
    return this.prisma.send<T>(JSON.stringify(this.userQuery)).then(onfulfilled, onrejected)
  }

  /**
   * Attaches a callback for only the rejection of the Promise.
   * @param onrejected The callback to execute when the Promise is rejected.
   * @returns A Promise for the completion of the callback.
   */
  catch<TResult = never>(
    onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null,
  ): Promise<T | TResult> {
    return this.prisma.send<T>(JSON.stringify(this.userQuery)).catch(onrejected)
  }
}

/**
 * Posts
 */

// User query parameters
type PostQuery = Partial<
  | Post
  | {
      user: UserQuery
    }
>

// Post output
type Post = {
  id: number
  slug: string
  title: string
  user?: User
  user_id: number
}

// PostsDelegate is a helper function to pass our initialized Prisma client to PostsClient
interface PostsDelegate {
  (query: PostQuery): PostsClient
  byTitle(email: string): PostsClient
}
function PostsDelegate(prisma: PrismaClient, from: PostQuery = {}): PostsDelegate {
  //  new PostsClient(...) will be called each time, but I think this is optimized
  // in V8 since we're not dynamically recreating the class each time.
  // TODO: double-check
  const Posts = (postQuery: PostQuery) => new PostsClient(prisma, Object.assign(from, postQuery))
  Posts.byTitle = (title: string) => new PostsClient(prisma, Object.assign(from, { title }))
  return Posts
}

// private posts client class
class PostsClient<T = Post[]> implements PromiseLike<T> {
  constructor(private readonly prisma: PrismaClient, private readonly query: PostQuery) {}

  /**
   * NOTE: Pulled from typescript's: interface Promise<T> { ... }
   * via "Go to definition" on Promise
   */
  /**
   * Attaches callbacks for the resolution and/or rejection of the Promise.
   * @param onfulfilled The callback to execute when the Promise is resolved.
   * @param onrejected The callback to execute when the Promise is rejected.
   * @returns A Promise for the completion of which ever callback is executed.
   */
  then<TResult1 = T, TResult2 = never>(
    onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null,
    onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null,
  ): Promise<TResult1 | TResult2> {
    return this.prisma.send<T>(JSON.stringify(this.query)).then(onfulfilled, onrejected)
  }

  /**
   * Attaches a callback for only the rejection of the Promise.
   * @param onrejected The callback to execute when the Promise is rejected.
   * @returns A Promise for the completion of the callback.
   */
  catch<TResult = never>(
    onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null,
  ): Promise<T | TResult> {
    return this.prisma.send<T>(JSON.stringify(this.query)).catch(onrejected)
  }
}

// USAGE
async function main() {
  const prisma = Prisma({})
  const users1 = await prisma.users({ id: 10 })
  console.log(users1)
  const users2 = await prisma.users({ id: 20 })
  const users3 = await prisma.users.byEmail('blah@blah.com')
  const usersPosts = await prisma.users({ id: 10 }).posts({ title: 'hi world' })
  await prisma.close()
}

main()


datasource db {
provider = "sqlite"
url      = env("SQLITE_URL")
}
generator photon {


# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1


module.exports = {
  preset: 'ts-jest',
  testEnvironment: 'node',
  modulePathIgnorePatterns: [
    'build/',
    'dist/',
    'generator/',
    'runtime/',
    '@generated',
    'index.ts',
    'index.js',
  ],
}


import { identity, theme } from './theme'

/* tslint:disable */

/* **********************************************
     Begin prism-core.js
********************************************** */

var _self: any = {}

/**
 * Prism: Lightweight, robust, elegant syntax highlighting
 * MIT license http://www.opensource.org/licenses/mit-license.php/
 * @author Lea Verou http://lea.verou.me
 */

// Private helper vars
var uniqueId = 0

export var Prism: any = {
  manual: _self.Prism && _self.Prism.manual,
  disableWorkerMessageHandler: _self.Prism && _self.Prism.disableWorkerMessageHandler,
  util: {
    encode: function(tokens: any) {
      if (tokens instanceof Token) {
        const anyTokens: any = tokens
        return new Token(anyTokens.type, Prism.util.encode(anyTokens.content), anyTokens.alias)
      } else if (Array.isArray(tokens)) {
        return tokens.map(Prism.util.encode)
      } else {
        return tokens
          .replace(/&/g, '&')
          .replace(/</g, '<')
          .replace(/\u00a0/g, ' ')
      }
    },

    type: function(o) {
      return Object.prototype.toString.call(o).slice(8, -1)
    },

    objId: function(obj) {
      if (!obj['__id']) {
        Object.defineProperty(obj, '__id', { value: ++uniqueId })
      }
      return obj['__id']
    },

    // Deep clone a language definition (e.g. to extend it)
    clone: function deepClone(o, visited?: any) {
      var clone,
        id,
        type = Prism.util.type(o)
      visited = visited || {}

      switch (type) {
        case 'Object':
          id = Prism.util.objId(o)
          if (visited[id]) {
            return visited[id]
          }
          clone = {}
          visited[id] = clone

          for (var key in o) {
            if (o.hasOwnProperty(key)) {
              clone[key] = deepClone(o[key], visited)
            }
          }

          return clone

        case 'Array':
          id = Prism.util.objId(o)
          if (visited[id]) {
            return visited[id]
          }
          clone = []
          visited[id] = clone

          o.forEach(function(v, i) {
            clone[i] = deepClone(v, visited)
          })

          return clone

        default:
          return o
      }
    },
  },

  languages: {
    extend: function(id, redef) {
      var lang = Prism.util.clone(Prism.languages[id])

      for (var key in redef) {
        lang[key] = redef[key]
      }

      return lang
    },

    /**
     * Insert a token before another token in a language literal
     * As this needs to recreate the object (we cannot actually insert before keys in object literals),
     * we cannot just provide an object, we need an object and a key.
     * @param inside The key (or language id) of the parent
     * @param before The key to insert before.
     * @param insert Object with the key/value pairs to insert
     * @param root The object that contains `inside`. If equal to Prism.languages, it can be omitted.
     */
    insertBefore: function(inside, before, insert, root) {
      root = root || Prism.languages
      var grammar = root[inside]
      var ret = {}

      for (var token in grammar) {
        if (grammar.hasOwnProperty(token)) {
          if (token == before) {
            for (var newToken in insert) {
              if (insert.hasOwnProperty(newToken)) {
                ret[newToken] = insert[newToken]
              }
            }
          }

          // Do not insert token which also occur in insert. See #1525
          if (!insert.hasOwnProperty(token)) {
            ret[token] = grammar[token]
          }
        }
      }

      var old = root[inside]
      root[inside] = ret

      // Update references in other language definitions
      Prism.languages.DFS(Prism.languages, function(this: any, key, value) {
        if (value === old && key != inside) {
          this[key] = ret
        }
      })

      return ret
    },

    // Traverse a language definition with Depth First Search
    DFS: function DFS(o, callback, type?: any, visited?: any) {
      visited = visited || {}

      var objId = Prism.util.objId

      for (var i in o) {
        if (o.hasOwnProperty(i)) {
          callback.call(o, i, o[i], type || i)

          var property = o[i],
            propertyType = Prism.util.type(property)

          if (propertyType === 'Object' && !visited[objId(property)]) {
            visited[objId(property)] = true
            DFS(property, callback, null, visited)
          } else if (propertyType === 'Array' && !visited[objId(property)]) {
            visited[objId(property)] = true
            DFS(property, callback, i, visited)
          }
        }
      }
    },
  },
  plugins: {},

  highlight: function(text, grammar, language) {
    var env: any = {
      code: text,
      grammar: grammar,
      language: language,
    }
    Prism.hooks.run('before-tokenize', env)
    env.tokens = Prism.tokenize(env.code, env.grammar)
    Prism.hooks.run('after-tokenize', env)
    return Token.stringify(Prism.util.encode(env.tokens), env.language)
  },

  matchGrammar: function(text, strarr, grammar, index, startPos, oneshot, target?: any) {
    for (var token in grammar) {
      if (!grammar.hasOwnProperty(token) || !grammar[token]) {
        continue
      }

      if (token == target) {
        return
      }

      var patterns = grammar[token]
      patterns = Prism.util.type(patterns) === 'Array' ? patterns : [patterns]

      for (var j = 0; j < patterns.length; ++j) {
        var pattern = patterns[j],
          inside = pattern.inside,
          lookbehind = !!pattern.lookbehind,
          greedy = !!pattern.greedy,
          lookbehindLength = 0,
          alias = pattern.alias

        if (greedy && !pattern.pattern.global) {
          // Without the global flag, lastIndex won't work
          var flags = pattern.pattern.toString().match(/[imuy]*$/)[0]
          pattern.pattern = RegExp(pattern.pattern.source, flags + 'g')
        }

        pattern = pattern.pattern || pattern

        // Don’t cache length as it changes during the loop
        for (var i = index, pos = startPos; i < strarr.length; pos += strarr[i].length, ++i) {
          var str = strarr[i]

          if (strarr.length > text.length) {
            // Something went terribly wrong, ABORT, ABORT!
            return
          }

          if (str instanceof Token) {
            continue
          }

          if (greedy && i != strarr.length - 1) {
            pattern.lastIndex = pos
            var match = pattern.exec(text)
            if (!match) {
              break
            }

            var from = match.index + (lookbehind ? match[1].length : 0),
              to = match.index + match[0].length,
              k = i,
              p = pos

            for (var len = strarr.length; k < len && (p < to || (!strarr[k].type && !strarr[k - 1].greedy)); ++k) {
              p += strarr[k].length
              // Move the index i to the element in strarr that is closest to from
              if (from >= p) {
                ++i
                pos = p
              }
            }

            // If strarr[i] is a Token, then the match starts inside another Token, which is invalid
            if (strarr[i] instanceof Token) {
              continue
            }

            // Number of tokens to delete and replace with the new match
            delNum = k - i
            str = text.slice(pos, p)
            match.index -= pos
          } else {
            pattern.lastIndex = 0

            var match = pattern.exec(str),
              delNum = 1
          }

          if (!match) {
            if (oneshot) {
              break
            }

            continue
          }

          if (lookbehind) {
            lookbehindLength = match[1] ? match[1].length : 0
          }

          var from = match.index + lookbehindLength,
            match = match[0].slice(lookbehindLength),
            to = from + match.length,
            before = str.slice(0, from),
            after = str.slice(to)

          var args: any = [i, delNum]

          if (before) {
            ++i
            pos += before.length
            args.push(before)
          }

          var wrapped = new Token(token, inside ? Prism.tokenize(match, inside) : match, alias, match, greedy)

          args.push(wrapped)

          if (after) {
            args.push(after)
          }

          Array.prototype.splice.apply(strarr, args)

          if (delNum != 1) Prism.matchGrammar(text, strarr, grammar, i, pos, true, token)

          if (oneshot) break
        }
      }
    }
  },

  tokenize: function(text, grammar) {
    var strarr = [text]

    var rest = grammar.rest

    if (rest) {
      for (var token in rest) {
        grammar[token] = rest[token]
      }

      delete grammar.rest
    }

    Prism.matchGrammar(text, strarr, grammar, 0, 0, false)

    return strarr
  },

  hooks: {
    all: {},

    add: function(name, callback) {
      var hooks = Prism.hooks.all

      hooks[name] = hooks[name] || []

      hooks[name].push(callback)
    },

    run: function(name, env) {
      var callbacks = Prism.hooks.all[name]

      if (!callbacks || !callbacks.length) {
        return
      }

      for (var i = 0, callback; (callback = callbacks[i++]); ) {
        callback(env)
      }
    },
  },

  Token: Token,
}

Prism.languages.clike = {
  comment: [
    {
      pattern: /(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,
      lookbehind: true,
    },
    {
      pattern: /(^|[^\\:])\/\/.*/,
      lookbehind: true,
      greedy: true,
    },
  ],
  string: {
    pattern: /(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,
    greedy: true,
  },
  'class-name': {
    pattern: /((?:\b(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[\w.\\]+/i,
    lookbehind: true,
    inside: {
      punctuation: /[.\\]/,
    },
  },
  keyword: /\b(?:if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/,
  boolean: /\b(?:true|false)\b/,
  function: /\w+(?=\()/,
  number: /\b0x[\da-f]+\b|(?:\b\d+\.?\d*|\B\.\d+)(?:e[+-]?\d+)?/i,
  operator: /--?|\+\+?|!=?=?|<=?|>=?|==?=?|&&?|\|\|?|\?|\*|\/|~|\^|%/,
  punctuation: /[{}[\];(),.:]/,
}

Prism.languages.javascript = Prism.languages.extend('clike', {
  'class-name': [
    Prism.languages.clike['class-name'],
    {
      pattern: /(^|[^$\w\xA0-\uFFFF])[_$A-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\.(?:prototype|constructor))/,
      lookbehind: true,
    },
  ],
  keyword: [
    {
      pattern: /((?:^|})\s*)(?:catch|finally)\b/,
      lookbehind: true,
    },
    {
      pattern: /(^|[^.])\b(?:as|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,
      lookbehind: true,
    },
  ],
  number: /\b(?:(?:0[xX](?:[\dA-Fa-f](?:_[\dA-Fa-f])?)+|0[bB](?:[01](?:_[01])?)+|0[oO](?:[0-7](?:_[0-7])?)+)n?|(?:\d(?:_\d)?)+n|NaN|Infinity)\b|(?:\b(?:\d(?:_\d)?)+\.?(?:\d(?:_\d)?)*|\B\.(?:\d(?:_\d)?)+)(?:[Ee][+-]?(?:\d(?:_\d)?)+)?/,
  // Allow for all non-ASCII characters (See http://stackoverflow.com/a/2008444)
  function: /[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,
  operator: /-[-=]?|\+[+=]?|!=?=?|<<?=?|>>?>?=?|=(?:==?|>)?|&[&=]?|\|[|=]?|\*\*?=?|\/=?|~|\^=?|%=?|\?|\.{3}/,
})

Prism.languages.javascript[
  'class-name'
][0].pattern = /(\b(?:class|interface|extends|implements|instanceof|new)\s+)[\w.\\]+/

Prism.languages.insertBefore('javascript', 'keyword', {
  regex: {
    pattern: /((?:^|[^$\w\xA0-\uFFFF."'\])\s])\s*)\/(\[(?:[^\]\\\r\n]|\\.)*]|\\.|[^/\\\[\r\n])+\/[gimyus]{0,6}(?=\s*($|[\r\n,.;})\]]))/,
    lookbehind: true,
    greedy: true,
  },
  // This must be declared before keyword because we use "function" inside the look-forward
  'function-variable': {
    pattern: /[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*)\s*=>))/,
    alias: 'function',
  },
  parameter: [
    {
      pattern: /(function(?:\s+[_$A-Za-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*)?\s*\(\s*)(?!\s)(?:[^()]|\([^()]*\))+?(?=\s*\))/,
      lookbehind: true,
      inside: Prism.languages.javascript,
    },
    {
      pattern: /[_$a-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*=>)/i,
      inside: Prism.languages.javascript,
    },
    {
      pattern: /(\(\s*)(?!\s)(?:[^()]|\([^()]*\))+?(?=\s*\)\s*=>)/,
      lookbehind: true,
      inside: Prism.languages.javascript,
    },
    {
      pattern: /((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:[_$A-Za-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*\s*)\(\s*)(?!\s)(?:[^()]|\([^()]*\))+?(?=\s*\)\s*\{)/,
      lookbehind: true,
      inside: Prism.languages.javascript,
    },
  ],
  constant: /\b[A-Z](?:[A-Z_]|\dx?)*\b/,
})

Prism.languages.insertBefore('javascript', 'string', {
  'template-string': {
    pattern: /`(?:\\[\s\S]|\${(?:[^{}]|{(?:[^{}]|{[^}]*})*})+}|[^\\`])*`/,
    greedy: true,
    inside: {
      interpolation: {
        pattern: /\${(?:[^{}]|{(?:[^{}]|{[^}]*})*})+}/,
        inside: {
          'interpolation-punctuation': {
            pattern: /^\${|}$/,
            alias: 'punctuation',
          },
          rest: Prism.languages.javascript,
        },
      },
      string: /[\s\S]+/,
    },
  },
})

if (Prism.languages.markup) {
  Prism.languages.markup.tag.addInlined('script', 'javascript')
}

Prism.languages.js = Prism.languages.javascript

Prism.languages.typescript = Prism.languages.extend('javascript', {
  // From JavaScript Prism keyword list and TypeScript language spec: https://github.com/Microsoft/TypeScript/blob/master/doc/spec.md#221-reserved-words
  keyword: /\b(?:abstract|as|async|await|break|case|catch|class|const|constructor|continue|debugger|declare|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|is|keyof|let|module|namespace|new|null|of|package|private|protected|public|readonly|return|require|set|static|super|switch|this|throw|try|type|typeof|var|void|while|with|yield)\b/,
  builtin: /\b(?:string|Function|any|number|boolean|Array|symbol|console|Promise|unknown|never)\b/,
})

Prism.languages.ts = Prism.languages.typescript

export function Token(this: any, type, content, alias, matchedStr?: any, greedy?: any) {
  this.type = type
  this.content = content
  this.alias = alias
  // Copy of the full string this token was created from
  this.length = (matchedStr || '').length | 0
  this.greedy = !!greedy
}

Token.stringify = function(o, language?: any) {
  if (typeof o == 'string') {
    return o
  }

  if (Array.isArray(o)) {
    return o
      .map(function(element) {
        return Token.stringify(element, language)
      })
      .join('')
  }

  return getColorForSyntaxKind(o.type)(o.content)
}

function getColorForSyntaxKind(syntaxKind: string) {
  return theme[syntaxKind] || identity
}


beforeRequest?: (options: {query: string, path: string[], rootField?: string, typeName?: string, document: any}) => any

keep keep keep keep add keep keep keep keep keep


datasource db {
  provider = "sqlite"
  url      = "file:dev.db"
  default  = true
}

generator photon {
  provider = "photonjs"
}

/// User model comment
model User {
  id    String  @default(uuid()) @id
  email String  @unique
  /// name comment
  name  String?
  posts Post[]
}

model Post {
  id        String   @default(cuid()) @id
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
  published Boolean
  title     String
  content   String?
  author    User?
}

enum Role {
  USER
  ADMIN
}


import chalk from 'chalk'
import * as stackTraceParser from 'stacktrace-parser'
import { highlightTS } from '../highlight/highlight'
import { dedent } from '../utils/dedent'

function renderN(n: number, max: number): string {
  const wantedLetters = String(max).length
  const hasLetters = String(n).length
  if (hasLetters >= wantedLetters) {
    return String(n)
  }

  return String(' '.repeat(wantedLetters - hasLetters) + n)
}

export interface ErrorArgs {
  callsite?: string
  originalMethod: string
  onUs?: boolean // is this on us?
}

export interface PrintStackResult {
  stack: string
  indent: number
  lastErrorHeight: number
  afterLines: string
}

export const printStack = ({
  callsite,
  originalMethod,
  onUs,
}: ErrorArgs): PrintStackResult => {
  const lastErrorHeight = 20
  let callsiteStr = ':'
  let prevLines = '\n'
  let afterLines = ''
  let indentValue = 0
  let functionName = `photon.${originalMethod}()`

  // @ts-ignore
  if (callsite && typeof window === 'undefined') {
    const stack = stackTraceParser.parse(callsite)
    // TODO: more resilient logic to check that it's not relative to cwd
    const trace = stack.find(
      t =>
        t.file &&
        !t.file.includes('@prisma') &&
        !t.methodName.includes('new ') &&
        t.methodName.split('.').length < 4,
    )
    if (
      process.env.NODE_ENV !== 'production' &&
      trace &&
      trace.file &&
      trace.lineNumber &&
      trace.column
    ) {
      const fileName = trace.file
      const lineNumber = trace.lineNumber
      callsiteStr = callsite
        ? ` in\n${chalk.underline(
            `${trace.file}:${trace.lineNumber}:${trace.column}`,
          )}`
        : ''
      const height = process.stdout.rows || 20
      const start = Math.max(0, lineNumber - 5)
      const neededHeight = lastErrorHeight + lineNumber - start
      if (height > neededHeight) {
        const fs = require('fs')
        if (fs.existsSync(fileName)) {
          const file = fs.readFileSync(fileName, 'utf-8')
          const slicedFile = file
            .split('\n')
            .slice(start, lineNumber)
            .join('\n')
          const lines = dedent(slicedFile).split('\n')

          const theLine = lines[lines.length - 1]
          const photonRegex = /(=|return)*\s+(await)?\s*(.*\()/
          const match = theLine.match(photonRegex)
          if (match) {
            functionName = `${match[3]})`
          }
          const slicePoint = theLine.indexOf('{')
          const highlightedLines = highlightTS(
            lines
              .map((l, i, all) =>
                !onUs && i === all.length - 1
                  ? l.slice(0, slicePoint > -1 ? slicePoint : l.length - 1)
                  : l,
              )
              .join('\n'),
          ).split('\n')
          prevLines =
            '\n' +
            highlightedLines
              .map(
                (l, i) =>
                  chalk.grey(
                    renderN(i + start + 1, lineNumber + start + 1) + ' ',
                  ) +
                  chalk.reset() +
                  l,
              )
              .map((l, i, arr) =>
                i === arr.length - 1
                  ? `${chalk.red.bold('→')} ${l}`
                  : chalk.dim('  ' + l),
              )
              .join('\n')
          afterLines = ')'
          indentValue =
            String(lineNumber + start + 1).length + getIndent(theLine) + 1
        }
      }
    }
  }

  function getIndent(line: string) {
    let spaceCount = 0
    for (let i = 0; i < line.length; i++) {
      if (line.charAt(i) !== ' ') {
        return spaceCount
      }
      spaceCount++
    }

    return spaceCount
  }

  const introText = onUs
    ? chalk.red(`Oops, an unknown error occured! This is ${chalk.bold(
        'on us',
      )}, you did nothing wrong.
It occured in the ${chalk.bold(
        `\`${functionName}\``,
      )} invocation${callsiteStr}`)
    : chalk.red(
        `Invalid ${chalk.bold(`\`${functionName}\``)} invocation${callsiteStr}`,
      )

  const stackStr = `\n${introText}
${prevLines}${chalk.reset()}`
  return { indent: indentValue, stack: stackStr, afterLines, lastErrorHeight }
}


async function get(): Promise<{ id: string } | null> {
  return { id: '' }
}

async function run() {
  const result = await get()
}


cwd: engineConfig.cwd || ${getRelativePathResolveStatement(

if (internal.measurePerformance) {
this.measurePerformance = true
}


import indent from 'indent-string'
import { Dictionary } from './common'

export type ConnectorType = 'mysql' | 'mongo' | 'sqlite' | 'postgresql'

export interface GeneratorConfig {
  name: string
  output: string | null
  provider: string
  config: Dictionary<string>
}

export type Datasource =
  | string
  | {
      url: string
      [key: string]: any | undefined
    }

export interface InternalDatasource {
  name: string
  connectorType: ConnectorType
  url: EnvValue
  config: any
}

// We could do import { EnvValue } from '../../isdlToDatamodel2'
// but we don't want to pull that into the runtime build
export interface EnvValue {
  fromEnvVar: null | string
  value: string
}

export function printDatasources(
  dataSources: Dictionary<Datasource | undefined>,
  internalDatasources: InternalDatasource[],
): string {
  const mergedInternalDataSources: InternalDatasource[] = internalDatasources.map(internalDataSource => {
    const override = dataSources[internalDataSource.name]
    if (!override) {
      return internalDataSource
    }

    if (typeof override === 'string') {
      return {
        ...internalDataSource,
        url: {
          value: override,
          fromEnvVar: null,
        },
      }
    }

    const { url, ...rest } = override

    return {
      ...internalDataSource,
      ...rest,
      url: {
        value: override.url,
        fromEnvVar: null,
      },
    }
  })

  return mergedInternalDataSources.map(d => String(new InternalDataSourceClass(d))).join('\n\n')
}

const tab = 2

class InternalDataSourceClass {
  constructor(private readonly dataSource: InternalDatasource) {}

  public toString() {
    const { dataSource } = this
    const obj = {
      provider: dataSource.connectorType,
      url: dataSource.url,
    }
    if (dataSource.config && typeof dataSource.config === 'object') {
      Object.assign(obj, dataSource.config)
    }
    return `datasource ${dataSource.name} {
${indent(printDatamodelObject(obj), tab)}
}`
  }
}

export function printDatamodelObject(obj) {
  const maxLength = Object.keys(obj).reduce((max, curr) => Math.max(max, curr.length), 0)
  return Object.entries(obj)
    .map(([key, value]) => `${key.padEnd(maxLength)} = ${JSON.stringify(value)}`)
    .join('\n')
}


import indent from 'indent-string'
import { GeneratorConfig } from '@prisma/generator-helper'

export function printGeneratorConfig(config: GeneratorConfig): string {
  return String(new GeneratorConfigClass(config))
}

export class GeneratorConfigClass {
  constructor(private readonly config: GeneratorConfig) {}
  toString() {
    const { config } = this
    // parse & stringify trims out all the undefined values
    const obj = JSON.parse(
      JSON.stringify({
        provider: config.provider,
        binaryTargets: config.binaryTargets || undefined,
      }),
    )

    return `generator ${config.name} {
${indent(printDatamodelObject(obj), 2)}
}`
  }
}

export function printDatamodelObject(obj) {
  const maxLength = Object.keys(obj).reduce((max, curr) => Math.max(max, curr.length), 0)
  return Object.entries(obj)
    .map(([key, value]) => `${key.padEnd(maxLength)} = ${niceStringify(value)}`)
    .join('\n')
}

function niceStringify(value) {
  return JSON.parse(
    JSON.stringify(value, (_, value) => {
      if (Array.isArray(value)) {
        return `[${value.map(element => JSON.stringify(element)).join(', ')}]`
      }
      return JSON.stringify(value)
    }),
  )
}


return ${m.model}Delegate(this.dmmf, this.fetcher, this.errorFormat, this.collectTimestamps)

keep keep keep keep replace keep keep keep keep keep

return ${m.model}Delegate(this.dmmf, this.fetcher, this.errorFormat, this.measurePerformance)


import { Suite } from 'benchmark'
import dl from 'damerau-levenshtein-js'
import leven from 'js-levenshtein'
import jaroWinkler from 'jaro-winkler'
import jaro from 'wink-jaro-distance'

const suite = new Suite()

const words = ['title', 'titel', 'string', 'strings', 'nme', 'ahtoe', 'author']

suite
  .add('levenshtein', () => {
    for (const word of words) {
      for (const word2 of words) {
        leven(word, word2)
      }
    }
  })
  .add('damerau levenshtein', () => {
    for (const word of words) {
      for (const word2 of words) {
        dl.distance(word, word2)
      }
    }
  })
  .add('jaro', () => {
    for (const word of words) {
      for (const word2 of words) {
        jaro(word, word2)
      }
    }
  })
  .add('jaro-winkler', () => {
    for (const word of words) {
      for (const word2 of words) {
        jaroWinkler(word, word2)
      }
    }
  })
  .on('cycle', function(event) {
    console.log(String(event.target))
  })
  .on('complete', function() {
    console.log('Fastest is ' + this.filter('fastest').map('name'))
  })
  // run async
  .run()

/**
   * Results:
   * levenshtein x 223,459 ops/sec ±0.43% (96 runs sampled)
damerau levenshtein x 17,503 ops/sec ±0.48% (93 runs sampled)
jaro x 185,553 ops/sec ±0.92% (92 runs sampled)
jaro-winkler x 160,204 ops/sec ±0.55% (95 runs sampled)
jaro_winkler x 15,306 ops/sec ±0.45% (94 runs sampled)
Fastest is levenshtein

   */