# Schema generated by Grats (https://grats.capt.dev)
# Do not manually edit. Regenerate by running `npx grats`.

"""
Indicates that a position is semantically non null: it is only null if there is a matching error in the `errors` array.
In all other cases, the position is non-null.

Tools doing code generation may use this information to generate the position as non-null if field errors are handled out of band:

```graphql
type User {
    # email is semantically non-null and can be generated as non-null by error-handling clients.
    email: String @semanticNonNull
}
```

The `levels` argument indicates what levels are semantically non null in case of lists:

```graphql
type User {
    # friends is semantically non null
    friends: [User] @semanticNonNull # same as @semanticNonNull(levels: [0])

    # every friends[k] is semantically non null
    friends: [User] @semanticNonNull(levels: [1])

    # friends as well as every friends[k] is semantically non null
    friends: [User] @semanticNonNull(levels: [0, 1])
}
```

`levels` are zero indexed.
Passing a negative level or a level greater than the list dimension is an error.
"""
directive @semanticNonNull(levels: [Int] = [0]) on FIELD_DEFINITION

scalar DateTime

enum AlbumCoverArtSortBy {
  DATE_CREATED
  LAST_EDITED
}

enum AppleMusicAlbumSortBy {
  DATE_CREATED
  LAST_EDITED
}

enum CharacterSortBy {
  DATE_CREATED
  LAST_EDITED
}

enum ComposerSortBy {
  DATE_CREATED
  LAST_EDITED
}

enum ConductorSortBy {
  DATE_CREATED
  LAST_EDITED
}

"""What a piece of feedback is about."""
enum FeedbackKind {
  album
  bug
  error
  general
  recording
  suggestion
}

enum LanguageSortBy {
  DATE_CREATED
  LAST_EDITED
}

enum MusicBrainzReleaseGroupSortBy {
  DATE_CREATED
  LAST_EDITED
}

enum MusicBrainzReleaseSortBy {
  DATE_CREATED
  LAST_EDITED
}

enum NaxosAlbumSortBy {
  DATE_CREATED
  LAST_EDITED
}

enum OperaSortBy {
  DATE_CREATED
  LAST_EDITED
}

enum PortrayalSortBy {
  DATE_CREATED
  LAST_EDITED
}

enum PrestoMusicAlbumSortBy {
  DATE_CREATED
  LAST_EDITED
}

"""
A table `recordChanges` can be narrowed to.

Includes the join tables, which have no model of their own.
"""
enum RecordTable {
  ALBUM_COVER_ART
  APPLE_MUSIC_ALBUMS
  CHARACTERS
  COMPOSERS
  CONDUCTORS
  LANGUAGES
  MUSIC_BRAINZ_RELEASES
  MUSIC_BRAINZ_RELEASE_GROUPS
  NAXOS_ALBUMS
  OPERAS
  PORTRAYALS
  PRESTO_MUSIC_ALBUMS
  RECORDINGS
  RECORDING_UPCS
  RECORDING_URLS
  SINGERS
  SPOTIFY_ALBUMS
  TIDAL_ALBUMS
  UPCS
  WIKIPEDIA_IMAGES
}

enum RecordingSortBy {
  DATE_CREATED
  LAST_EDITED
}

enum RecordingURLSortBy {
  DATE_CREATED
  LAST_EDITED
}

enum SingerSortBy {
  DATE_CREATED
  LAST_EDITED
}

enum SortOrder {
  ASC
  DESC
}

enum SpotifyAlbumSortBy {
  DATE_CREATED
  LAST_EDITED
}

enum TidalAlbumSortBy {
  DATE_CREATED
  LAST_EDITED
}

enum UpcSortBy {
  DATE_CREATED
  LAST_EDITED
}

enum WikipediaImageSortBy {
  DATE_CREATED
  LAST_EDITED
}

"""
Anything an edit can have touched.

Records here are immutable and append-only, so a record reached this way is
the record as it stands *now*, not as that edit left it. Following an edit
to a singer tells you which singer was changed, not what the change said —
for that, read the versions.
"""
union EditableRecord = AlbumCoverArt | AppleMusicAlbum | Character | Composer | Conductor | Language | MusicBrainzRelease | MusicBrainzReleaseGroup | NaxosAlbum | Opera | Portrayal | PrestoMusicAlbum | Recording | RecordingURL | Singer | SpotifyAlbum | TidalAlbum | Upc | WikipediaImage

input CreateAlbumCoverArtInput {
  imageUrl: String
  source: String
  """
  The barcode, always at its full fourteen digits.
  
  UPC-A, EAN-13 and GTIN-14 are three lengths of one number rather than three
  different barcodes, and the check digit is the same at every length. This
  returns the longest, zero-padded, because it is the one form that can hold
  any of them.
  
  Any of the three is accepted wherever a barcode is taken, and answered for
  with this one. Somewhere less formal than an API -- a page, a link, anything
  a person reads -- is welcome to drop the leading zeros again.
  """
  upc: String!
}

input CreateAppleMusicAlbumInput {
  id: String!
  upcId: String
}

input CreateCharacterInput {
  englishName: String
  name: String!
  operaId: String!
}

input CreateComposerInput {
  born: Int
  died: Int
  firstName: String!
  lastName: String!
  operaclassId: Int
  spotifyArtistId: String
  wikipediaImageId: String
  wikipediaUrl: String
}

input CreateConductorInput {
  born: Int
  died: Int
  firstName: String!
  lastName: String!
  operaclassId: Int
  spotifyArtistId: String
  wikipediaImageId: String
  wikipediaUrl: String
}

input CreateLanguageInput {
  abbreviation: String!
  name: String!
  wikipediaCategory: String!
}

input CreateMusicBrainzReleaseGroupInput {
  id: String!
  recordingId: String
}

input CreateMusicBrainzReleaseInput {
  id: String!
  upcId: String
}

input CreateNaxosAlbumInput {
  catalogueId: String!
  upcId: String
}

input CreateOperaInput {
  composerId: String!
  englishTitle: String
  languageId: String
  librettist: String
  operaclassId: Int
  title: String!
  wikipediaImageId: String
  wikipediaUrl: String
}

input CreatePortrayalInput {
  characterId: String!
  """
  Whether this portrayal is part of the recording's top billing.
  
  Three distinct singers per recording, or all of them where a recording
  has fewer than three. The flag sits on portrayals but is read as a set
  of singers, so a singer who doubles roles may be flagged on one of
  those roles or on all of them; both say the same thing, and
  addRecordingWithPortrayals counts distinct singers and accepts either.
  Recording 77 carries six flagged portrayals for three singers, and
  recording 6417 flags Deborah Polaski for both Cassandre and Didon.
  
  Which three is an editorial judgment, and the editorial policy's rule
  on billing flags is the authoritative text for making it. It cannot be
  worked out from the cast: recording 88 notes Carreras, van Dam and
  Zednik and leaves the title role unflagged, so reading the principals
  off the roles and inferring the answer gives the wrong three.
  """
  noted: Boolean
  recordingId: String!
  singerId: String!
}

input CreatePrestoMusicAlbumInput {
  isStreaming: Boolean
  slug: String!
  upcId: String
}

input CreateRecordingInput {
  chorus: String
  conductorId: String!
  day: Int
  month: Int
  operaId: String!
  operaclassId: Int
  orchestra: String
  year: Int
}

input CreateRecordingURLInput {
  recordingId: String!
  url: String!
}

input CreateSingerInput {
  born: Int
  died: Int
  firstName: String!
  lastName: String!
  operaclassId: Int
  spotifyArtistId: String
  wikipediaImageId: String
  wikipediaUrl: String
}

input CreateSpotifyAlbumInput {
  albumName: String
  id: String!
  upcId: String
}

input CreateTidalAlbumInput {
  id: String!
  upcId: String
}

input CreateUpcInput {
  """
  The barcode, always at its full fourteen digits.
  
  UPC-A, EAN-13 and GTIN-14 are three lengths of one number rather than three
  different barcodes, and the check digit is the same at every length. This
  returns the longest, zero-padded, because it is the one form that can hold
  any of them.
  
  Any of the three is accepted wherever a barcode is taken, and answered for
  with this one. Somewhere less formal than an API -- a page, a link, anything
  a person reads -- is welcome to drop the leading zeros again.
  """
  upc: String!
}

input CreateWikipediaImageInput {
  imageUrl: String
  wikipediaUrl: String!
}

input LinkRecordingUpcInput {
  recordingId: String!
  upc: String!
}

"""
Filter criteria for searching recordings.
All fields are optional - only provided fields will be used for filtering.
"""
input RecordingFilterInput {
  """Filter by approximate year (within 2 years of the given year)"""
  approximateYear: Int
  """
  A role that somebody sings on the recording, by name. "Which recordings
  have a Scarpia" is a question about the work rather than about any
  particular singer. A short fragment; matching is fuzzy and ignores
  accents.
  """
  characterNameSearch: String
  """
  The composer of the opera, by name. The catalog holds two operas called La
  Boheme, by Puccini and by Leoncavallo, so a title alone does not always
  say which work is meant. A short fragment; matching is fuzzy and ignores
  accents.
  """
  composerNameSearch: String
  """Filter by conductor ID"""
  conductorId: String
  """
  Part of the conductor's name. A short fragment, because matching is fuzzy
  and ignores case and accents.
  """
  conductorNameSearch: String
  """
  Only recordings in this collection of the viewer's.
  
  Refuses an id that is not a collection the viewer keeps. Not found and not
  yours are the same refusal, so that walking the ids teaches nothing.
  """
  inCollection: Int
  """Filter by opera ID"""
  operaId: String
  """
  Part of the opera's title, as in "Boheme". A short fragment, because
  matching is fuzzy and ignores case and accents: the stored spelling is
  often not the one anybody would type.
  """
  operaTitleSearch: String
  """
  Filter by singer IDs - returns recordings that include ALL specified singers
  """
  singerIds: [String!]
  """
  Singers by name. ALL of them must appear on a recording for it to match,
  so add names to narrow and remove them to widen. Short fragments;
  matching is fuzzy and ignores accents.
  """
  singerNameSearches: [String!]
  """
  Filter by barcode. Matches however the code is written: spaces and dashes
  are ignored, and a UPC-A, EAN-13 or GTIN-14 form of the same product all
  find each other.
  """
  upc: String
}

input RecordingPortrayalsInput {
  characterId: String!
  noted: Boolean!
  singerId: String!
}

input UpdateAlbumCoverArtInput {
  imageUrl: String
  source: String
  """
  The barcode, always at its full fourteen digits.
  
  UPC-A, EAN-13 and GTIN-14 are three lengths of one number rather than three
  different barcodes, and the check digit is the same at every length. This
  returns the longest, zero-padded, because it is the one form that can hold
  any of them.
  
  Any of the three is accepted wherever a barcode is taken, and answered for
  with this one. Somewhere less formal than an API -- a page, a link, anything
  a person reads -- is welcome to drop the leading zeros again.
  """
  upc: String
}

input UpdateAppleMusicAlbumInput {
  id: String
  upcId: String
}

input UpdateCharacterInput {
  englishName: String
  name: String
  operaId: String
}

input UpdateComposerInput {
  born: Int
  died: Int
  firstName: String
  lastName: String
  operaclassId: Int
  spotifyArtistId: String
  wikipediaImageId: String
  wikipediaUrl: String
}

input UpdateConductorInput {
  born: Int
  died: Int
  firstName: String
  lastName: String
  operaclassId: Int
  spotifyArtistId: String
  wikipediaImageId: String
  wikipediaUrl: String
}

input UpdateLanguageInput {
  abbreviation: String
  name: String
  wikipediaCategory: String
}

input UpdateMusicBrainzReleaseGroupInput {
  id: String
  recordingId: String
}

input UpdateMusicBrainzReleaseInput {
  id: String
  upcId: String
}

input UpdateNaxosAlbumInput {
  catalogueId: String
  upcId: String
}

input UpdateOperaInput {
  composerId: String
  englishTitle: String
  languageId: String
  librettist: String
  operaclassId: Int
  title: String
  wikipediaImageId: String
  wikipediaUrl: String
}

input UpdatePortrayalInput {
  characterId: String
  """
  Whether this portrayal is part of the recording's top billing.
  
  Three distinct singers per recording, or all of them where a recording
  has fewer than three. The flag sits on portrayals but is read as a set
  of singers, so a singer who doubles roles may be flagged on one of
  those roles or on all of them; both say the same thing, and
  addRecordingWithPortrayals counts distinct singers and accepts either.
  Recording 77 carries six flagged portrayals for three singers, and
  recording 6417 flags Deborah Polaski for both Cassandre and Didon.
  
  Which three is an editorial judgment, and the editorial policy's rule
  on billing flags is the authoritative text for making it. It cannot be
  worked out from the cast: recording 88 notes Carreras, van Dam and
  Zednik and leaves the title role unflagged, so reading the principals
  off the roles and inferring the answer gives the wrong three.
  """
  noted: Boolean
  recordingId: String
  singerId: String
}

input UpdatePrestoMusicAlbumInput {
  isStreaming: Boolean
  slug: String
  upcId: String
}

input UpdateRecordingInput {
  chorus: String
  conductorId: String
  day: Int
  month: Int
  operaId: String
  operaclassId: Int
  orchestra: String
  year: Int
}

input UpdateRecordingURLInput {
  recordingId: String
  url: String
}

input UpdateSingerInput {
  born: Int
  died: Int
  firstName: String
  lastName: String
  operaclassId: Int
  spotifyArtistId: String
  wikipediaImageId: String
  wikipediaUrl: String
}

input UpdateSpotifyAlbumInput {
  albumName: String
  id: String
  upcId: String
}

input UpdateTidalAlbumInput {
  id: String
  upcId: String
}

input UpdateUpcInput {
  """
  The barcode, always at its full fourteen digits.
  
  UPC-A, EAN-13 and GTIN-14 are three lengths of one number rather than three
  different barcodes, and the check digit is the same at every length. This
  returns the longest, zero-padded, because it is the one form that can hold
  any of them.
  
  Any of the three is accepted wherever a barcode is taken, and answered for
  with this one. Somewhere less formal than an API -- a page, a link, anything
  a person reads -- is welcome to drop the leading zeros again.
  """
  upc: String
}

input UpdateWikipediaImageInput {
  imageUrl: String
  wikipediaUrl: String
}

"""A newly issued token, and the account it belongs to."""
type AccessToken {
  """
  The token. Send it as `Authorization: Bearer <token>`.
  
  Only its hash is stored, so this is the one and only time it can be read.
  A lost token is replaced, not recovered.
  """
  token: String @semanticNonNull
  """
  The name edits made with this token will be attributed to.
  
  Public, and shown on every edit you make.
  """
  username: String @semanticNonNull
}

type AlbumCoverArt {
  """
  This Album cover art written out.
  
  For reading, not parsing: one field instead of a query, and the layout may
  change without notice.
  """
  asPlainText: String @semanticNonNull
  history: [Edit!] @semanticNonNull
  """
  The value that identifies this record.
  
  A cover image is stored against the release it belongs to, so the UPC is
  both its key and its identity.
  
  Every other type here answers to `id`, and code that walks all of them —
  the CLI's get/create/edit/delete commands, for one — should not have to
  carry a table of which five are spelled differently. The underlying column
  is unchanged; this is another name for it.
  """
  id: String @semanticNonNull
  imageUrl: String
  source: String
  """
  The barcode, always at its full fourteen digits.
  
  UPC-A, EAN-13 and GTIN-14 are three lengths of one number rather than three
  different barcodes, and the check digit is the same at every length. This
  returns the longest, zero-padded, because it is the one form that can hold
  any of them.
  
  Any of the three is accepted wherever a barcode is taken, and answered for
  with this one. Somewhere less formal than an API -- a page, a link, anything
  a person reads -- is welcome to drop the leading zeros again.
  """
  upc: String @semanticNonNull
}

type AppleMusicAlbum {
  """
  This Apple Music album written out.
  
  For reading, not parsing: one field instead of a query, and the layout may
  change without notice.
  """
  asPlainText: String @semanticNonNull
  history: [Edit!] @semanticNonNull
  id: String @semanticNonNull
  upc: Upc
  """URL to the album on Apple Music."""
  url: String @semanticNonNull
}

type Character {
  """
  This character written out: opera.
  
  For reading, not parsing: one field instead of a query, and the layout may
  change without notice.
  """
  asPlainText: String @semanticNonNull
  englishName: String
  history: [Edit!] @semanticNonNull
  historyUrl: String @semanticNonNull
  id: Int @semanticNonNull
  name: String @semanticNonNull
  opera: Opera @semanticNonNull
  portrayals: [Portrayal!] @semanticNonNull
  url: String @semanticNonNull
}

"""
A list of recordings that one person keeps.

Private, wholly: a collection is reachable only through the account that owns
it, and that account is only ever the one asking. Nothing here is published,
nothing appears in the change log, and no other reader can discover that a
given list exists.

That is narrower than lists usually end up being, and it is the narrow end on
purpose. Sharing one is a decision with a schema change behind it and it can
be made later; a list that was public before anybody meant it to be cannot be
made private again for the people who already read it.
"""
type Collection {
  createdAt: DateTime @semanticNonNull
  """How many recordings are in it."""
  entryCount: Int @semanticNonNull
  id: Int @semanticNonNull
  """
  Whether a save with no list named goes here. At most one of an account's
  collections is.
  """
  isDefault: Boolean @semanticNonNull
  """What the owner calls it."""
  name: String @semanticNonNull
  """
  What is in it, most recently saved first.
  
  The same search as `filterRecordings`, narrowed to this collection: every
  filter that works against the catalog works inside a list, so "the
  Karajans I own" is one query rather than a fetch and a scan.
  
  A recording merged into another shows as the survivor. A recording deleted
  outright is absent. The membership row outlives both, so nothing has to be
  swept up when the catalog changes underneath somebody's list.
  """
  recordings(
    """Narrow it further. Everything `filterRecordings` accepts."""
    filter: RecordingFilterInput
    """At most this many. Capped at 200."""
    first: Int! = 50
    """Skip this many."""
    offset: Int! = 0
  ): [Recording!] @semanticNonNull
  url: String @semanticNonNull
}

type Composer {
  """
  This composer written out: operas.
  
  For reading, not parsing: one field instead of a query, and the layout may
  change without notice.
  """
  asPlainText: String @semanticNonNull
  born: Int
  died: Int
  firstName: String @semanticNonNull
  """Returns the full name of the composer (first name + last name)."""
  fullName: String @semanticNonNull
  history: [Edit!] @semanticNonNull
  historyUrl: String @semanticNonNull
  id: Int @semanticNonNull
  lastName: String @semanticNonNull
  operaclassId: Int
  operas: [Opera!] @semanticNonNull
  spotifyArtistId: String
  url: String @semanticNonNull
  wikipediaImage: WikipediaImage
  wikipediaUrl: String
}

type Conductor {
  """
  This conductor written out: recordings.
  
  For reading, not parsing: one field instead of a query, and the layout may
  change without notice.
  """
  asPlainText: String @semanticNonNull
  born: Int
  died: Int
  firstName: String @semanticNonNull
  """Returns the full name of the conductor (first name + last name)."""
  fullName: String @semanticNonNull
  history: [Edit!] @semanticNonNull
  historyUrl: String @semanticNonNull
  id: Int @semanticNonNull
  lastName: String @semanticNonNull
  operaclassId: Int
  recordings: [Recording!] @semanticNonNull
  spotifyArtistId: String
  url: String @semanticNonNull
  wikipediaImage: WikipediaImage
  wikipediaUrl: String
}

"""
Represents an edit made to a record. In FindOpera, records are immutable, so
an edit corresponds to the creation of a new record with updated data. The
Edit model captures metadata about the edit, such as when it was made, who
made it, and the justification for the edit.
"""
type Edit {
  """
  The account that made this edit.
  
  Null when no account made it: an import from before accounts existed, or a
  migration. The justification usually names the source in that case.
  """
  editor: User
  id: Int @semanticNonNull
  """
  The rational for the edit which was provided by the editor at the time of
  the edit.
  
  Generally this should include a link to the source of the information that
  was added/changed, or an explanation of why information was removed.
  """
  justification: String
  """
  How many records this edit touched, including any `records` did not
  return.
  
  Most edits touch one. The imports that seeded this database are edits too,
  and the largest wrote over sixty thousand rows — so this is what tells a
  caller that an edit is a bulk import rather than a change to be read.
  """
  recordCount: Int @semanticNonNull
  """
  What this edit touched: every record it wrote a new version of.
  
  Read from `edit_records`, which the versioned tables fill in by trigger.
  Before that table this meant asking all twenty-one versioned tables in
  turn, because a versioned row names its edit but nothing named the
  reverse.
  
  Ordered so that an edit reads the way it happened: by table, then by
  record.
  
  Bounded, and worth bounding even though the reads are cheap — five hundred
  of these resolve in two queries and four milliseconds, because `getById`
  batches through the request's dataloaders. What costs is what happens
  afterwards: five hundred rendered rows is half a megabyte of HTML, and
  that is the difference between a page that answers in a tenth of a second
  and one that takes three. `recordCount` is how a caller knows there is
  more.
  """
  records(
    """At most this many records."""
    first: Int! = 100
  ): [EditedRecord!] @semanticNonNull
  """Date/time at which the edit was made"""
  timestamp: DateTime @semanticNonNull
  """A stable address for this edit."""
  url: String @semanticNonNull
}

"""
One record an edit touched.

Records here are immutable: an edit does not change a row, it writes a new
version of one. So this names the record and the version this edit produced,
`changes` says what that version did, and `record` reaches the record itself
as it stands today.
"""
type EditedRecord {
  """
  What this edit did to this record, field by field.
  
  The one thing a reviewer cannot get anywhere else. `record` below returns
  the record as it stands today, which says which opera was changed but not
  what the change said, and the old values live only in the versioned
  tables. So this reads the version this edit wrote and the version before
  it, and returns the fields that differ — nothing about the fields the edit
  left alone.
  
  On a record this edit created, every field it set, with `before` null.
  
  A deletion writes a new version with the columns unchanged and a flag set,
  so it is reported as a change to `isDeleted` — otherwise removing a record
  would read as having changed nothing. `isDeleted` is not a field anything
  can write; deleting is its own mutation, and there is no undoing one.
  
  Empty for a table added since this was generated, and for the odd row
  whose previous version is missing — the historical imports wrote versioned
  rows directly, and a few did not start at 1.
  """
  changes: [FieldChange!] @semanticNonNull
  """
  How this record was removed, or null while it is still live.
  
  Describes the state this version left rather than what this edit did, so
  that an entry can be applied on its own: it is set on every version of an
  already-removed record, not only on the one that removed it. `changes`
  carries the transition.
  """
  deletion: RecordDeletion
  """The edit that wrote this version: who made it, when, and why."""
  edit: Edit @semanticNonNull
  """True if this edit created the record rather than changing one."""
  isNew: Boolean @semanticNonNull
  """
  The model this record belongs to, e.g. `Recording`.
  
  Null for a raw join table, which has no model of its own — linking a
  recording to a barcode is an edit like any other, but there is no type to
  name.
  """
  modelName: String
  """
  The record itself, **as it stands now** — not as this edit left it.
  
  Following an edit here tells you which singer was changed, not what the
  change said. The models read the latest version of a row, and their
  relationships resolve against today's data, so there is no honest way to
  hand back the record as of an old version without version-aware traversal
  through every relationship. `changes` is what the change said.
  
  Deleted and merged-away records are returned too, through a lookup that
  reads past the view. Reviewing a merge means seeing the record it took
  away, and the edit that removed a record is exactly the edit somebody needs
  to look at; returning null there left the one case that most needed an
  answer without one.
  
  Null when the record has no model of its own — a join table.
  """
  record: EditableRecord
  """
  Identifies the record within its table. A join table has no single key, so
  its parts are joined with a colon.
  """
  recordId: String @semanticNonNull
  """
  The table the record lives in, e.g. `recordings`. Always present, even
  when there is no model.
  """
  tableName: String @semanticNonNull
  """
  Which version of the record this edit produced. Version 1 is the record's
  creation.
  """
  version: Int @semanticNonNull
}

type EditedRecordConnection {
  """The entries on this page, each with its position."""
  edges: [EditedRecordEdge!] @semanticNonNull
  """Whether there is more, and the positions at either end."""
  pageInfo: PageInfo @semanticNonNull
}

type EditedRecordEdge {
  """
  Where this entry sits in the list that returned it. Opaque. Pass it back
  as `after` to resume from just past it.
  """
  cursor: String @semanticNonNull
  node: EditedRecord @semanticNonNull
}

"""
One field an edit changed, and what it changed it from.

Values are rendered as text. The columns behind them are a mix of numbers,
strings and flags, and a reviewer is reading them rather than computing with
them — so `year` arrives as `"1953"` and `noted` as `"true"`, which is what
they would have to type to write the value back. Null is kept as null, so
that a column that held nothing is distinguishable from one that held the
word "null" or an empty string.

A foreign key is the exception that needs more than rendering: `"13282"` is
true and useless. `beforeRecord` and `afterRecord` hand back the record it
points at, for the caller to name however it names things.

Those two replaced `beforeLabel` and `afterLabel`, which named it here.
Naming is a presentation choice, and making it once on the server meant
making it twice in fact: there was a copy for this and a copy for the
change log, they knew different numbers of types, and a recording answered
to two different names depending which you reached it through.
"""
type FieldChange {
  """What this edit made it. Null means the edit cleared the field."""
  after: String
  """The record `after` points at. Null on the same terms."""
  afterRecord: EditableRecord
  """
  What the field held before this edit. Null means the column held NULL, and
  on a record this edit created, means there was nothing there at all.
  """
  before: String
  """
  The record `before` points at, when this field is a foreign key.
  
  What `beforeLabel` above is, before somebody decided what to call it. A
  name is a presentation choice, and the two places this codebase made that
  choice had drifted into disagreeing: handing back the record instead lets
  whoever is drawing it name it the way the rest of their page names things,
  once.
  
  `beforeLabel` stays because it is the right answer for a caller that is
  not drawing a page. An agent reading the edit queue wants
  `"Elena Suliotis"` in the JSON it already has, not a second query and a
  switch on `__typename`.
  
  Null on the same terms as `beforeLabel`: not a foreign key, a null value,
  or a record that no longer exists.
  """
  beforeRecord: EditableRecord
  """
  The field's name in the API — `languageId`, not `language_id` — because
  somebody reading a diff is usually on their way to writing the correction.
  
  A join table has no model and so no public names; its columns are named as
  SQLite has them.
  """
  field: String @semanticNonNull
}

type Language {
  abbreviation: String @semanticNonNull
  """
  This language written out.
  
  For reading, not parsing: one field instead of a query, and the layout may
  change without notice.
  """
  asPlainText: String @semanticNonNull
  history: [Edit!] @semanticNonNull
  id: Int @semanticNonNull
  name: String @semanticNonNull
  operas: [Opera!] @semanticNonNull
  wikipediaCategory: String @semanticNonNull
}

type MusicBrainzRelease {
  """
  This MusicBrainz release written out.
  
  For reading, not parsing: one field instead of a query, and the layout may
  change without notice.
  """
  asPlainText: String @semanticNonNull
  history: [Edit!] @semanticNonNull
  id: String @semanticNonNull
  upc: Upc
}

type MusicBrainzReleaseGroup {
  """
  This MusicBrainz release group written out.
  
  For reading, not parsing: one field instead of a query, and the layout may
  change without notice.
  """
  asPlainText: String @semanticNonNull
  history: [Edit!] @semanticNonNull
  id: String @semanticNonNull
  recording: Recording
}

type Mutation {
  """Create a new AlbumCoverArt record."""
  addAlbumCoverArt(
    input: CreateAlbumCoverArtInput!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): AlbumCoverArt @semanticNonNull
  """Create a new AppleMusicAlbum record."""
  addAppleMusicAlbum(
    input: CreateAppleMusicAlbumInput!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): AppleMusicAlbum @semanticNonNull
  """Create a new Character record."""
  addCharacter(
    input: CreateCharacterInput!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): Character @semanticNonNull
  """Create a new Composer record."""
  addComposer(
    input: CreateComposerInput!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): Composer @semanticNonNull
  """Create a new Conductor record."""
  addConductor(
    input: CreateConductorInput!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): Conductor @semanticNonNull
  """Create a new Language record."""
  addLanguage(
    input: CreateLanguageInput!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): Language @semanticNonNull
  """Create a new MusicBrainzRelease record."""
  addMusicBrainzRelease(
    input: CreateMusicBrainzReleaseInput!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): MusicBrainzRelease @semanticNonNull
  """Create a new MusicBrainzReleaseGroup record."""
  addMusicBrainzReleaseGroup(
    input: CreateMusicBrainzReleaseGroupInput!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): MusicBrainzReleaseGroup @semanticNonNull
  """Create a new NaxosAlbum record."""
  addNaxosAlbum(
    input: CreateNaxosAlbumInput!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): NaxosAlbum @semanticNonNull
  """Create a new Opera record."""
  addOpera(
    input: CreateOperaInput!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): Opera @semanticNonNull
  """Create a new Portrayal record."""
  addPortrayal(
    input: CreatePortrayalInput!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): Portrayal @semanticNonNull
  """Create a new PrestoMusicAlbum record."""
  addPrestoMusicAlbum(
    input: CreatePrestoMusicAlbumInput!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): PrestoMusicAlbum @semanticNonNull
  """Create a new Recording record."""
  addRecording(
    input: CreateRecordingInput!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): Recording @semanticNonNull
  """
  Put a recording in a list.
  
  Saving the same recording twice is the same save: the primary key says so
  and `ON CONFLICT DO NOTHING` makes a repeat succeed quietly rather than
  fail. A caller retrying after a dropped connection wanted the recording in
  the list, and it is.
  """
  addRecordingToCollection(collectionId: Int!, recordingId: Int!): Collection @semanticNonNull
  """Create a new RecordingURL record."""
  addRecordingURL(
    input: CreateRecordingURLInput!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): RecordingURL @semanticNonNull
  """
  Like Mutation.addRecording, but also creates associated Portrayal records in
  a single transaction. Optionally creates a source URL, UPC, and/or Spotify
  album alongside the recording.
  """
  addRecordingWithPortrayals(
    input: CreateRecordingInput!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
    portrayalInputs: [RecordingPortrayalsInput!]!
    """
    If provided, a RecordingURL will be created linking this URL to the new recording.
    """
    sourceUrl: String
    """
    If provided, a SpotifyAlbum record will be created. If `upc` is also provided it will be linked to that UPC.
    """
    spotifyAlbumId: String
    """
    If provided, a UPC record will be created and linked to the new recording.
    """
    upc: String
  ): Recording @semanticNonNull
  """Create a new Singer record."""
  addSinger(
    input: CreateSingerInput!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): Singer @semanticNonNull
  """Create a new SpotifyAlbum record."""
  addSpotifyAlbum(
    input: CreateSpotifyAlbumInput!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): SpotifyAlbum @semanticNonNull
  """Create a new TidalAlbum record."""
  addTidalAlbum(
    input: CreateTidalAlbumInput!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): TidalAlbum @semanticNonNull
  """Create a new Upc record."""
  addUpc(
    input: CreateUpcInput!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): Upc @semanticNonNull
  """Create a new WikipediaImage record."""
  addWikipediaImage(
    input: CreateWikipediaImageInput!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): WikipediaImage @semanticNonNull
  """
  Get a token, so that requests can be told apart from one another.
  
  Nobody is asked to prove who they are: this hands out a fresh account to
  whoever asks. The point is not to know which person is editing, it is that a
  given editor is the same editor from one request to the next — which is what
  lets an edit be attributed, traffic be shaped per caller, and a run of bad
  changes be found together and undone.
  
  You do have to pick a name, which is not the same as proving anything.
  """
  createAccessToken(
    """
    Optional, and never verified. Somewhere to reach you if something you are
    doing is being blocked — a limit, or a bug at this end. It is not a login,
    and it is never published. Leave it out and everything still works.
    """
    email: String
    """
    A private note stored with this token. Never published.
    
    Not yet shown back to you either: nothing lists your tokens, so this is
    write-only for the moment. Give it the name of the machine or the agent
    that will hold the token, and it will mean something to you when there is
    somewhere to read it.
    """
    label: String
    """
    What to call yourself. Public, and shown on every edit you make.
    
    Letters, digits and hyphens, up to 39 of them, with no hyphen at either end
    and none doubled. Compared without regard to case, so `Tosca` and `tosca`
    are the same name and only one of them is free.
    
    Worth a moment's thought: it is not meant to be changed.
    """
    username: String!
  ): AccessToken @semanticNonNull
  """Start a list. The first one an account has becomes its default."""
  createCollection(name: String!): Collection @semanticNonNull
  """
  Delete a AlbumCoverArt record by ID.
  Returns true if the record was deleted, false if it didn't exist.
  """
  deleteAlbumCoverArt(
    id: String!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): Boolean @semanticNonNull
  """
  Delete a AppleMusicAlbum record by ID.
  Returns true if the record was deleted, false if it didn't exist.
  """
  deleteAppleMusicAlbum(
    id: String!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): Boolean @semanticNonNull
  """
  Delete a Character record by ID.
  Returns true if the record was deleted, false if it didn't exist.
  """
  deleteCharacter(
    id: String!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): Boolean @semanticNonNull
  """
  Put one away. Nothing can reach it afterwards, and nothing about the
  recordings themselves is touched; they were never owned by the list.
  """
  deleteCollection(id: Int!): Boolean @semanticNonNull
  """
  Delete a Composer record by ID.
  Returns true if the record was deleted, false if it didn't exist.
  """
  deleteComposer(
    id: String!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): Boolean @semanticNonNull
  """
  Delete a Conductor record by ID.
  Returns true if the record was deleted, false if it didn't exist.
  """
  deleteConductor(
    id: String!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): Boolean @semanticNonNull
  """
  Delete a Language record by ID.
  Returns true if the record was deleted, false if it didn't exist.
  """
  deleteLanguage(
    id: String!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): Boolean @semanticNonNull
  """
  Delete a MusicBrainzRelease record by ID.
  Returns true if the record was deleted, false if it didn't exist.
  """
  deleteMusicBrainzRelease(
    id: String!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): Boolean @semanticNonNull
  """
  Delete a MusicBrainzReleaseGroup record by ID.
  Returns true if the record was deleted, false if it didn't exist.
  """
  deleteMusicBrainzReleaseGroup(
    id: String!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): Boolean @semanticNonNull
  """
  Delete a NaxosAlbum record by ID.
  Returns true if the record was deleted, false if it didn't exist.
  """
  deleteNaxosAlbum(
    id: String!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): Boolean @semanticNonNull
  """
  Delete a Opera record by ID.
  Returns true if the record was deleted, false if it didn't exist.
  """
  deleteOpera(
    id: String!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): Boolean @semanticNonNull
  """
  Delete a Portrayal record by ID.
  Returns true if the record was deleted, false if it didn't exist.
  """
  deletePortrayal(
    id: String!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): Boolean @semanticNonNull
  """
  Delete a PrestoMusicAlbum record by ID.
  Returns true if the record was deleted, false if it didn't exist.
  """
  deletePrestoMusicAlbum(
    id: String!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): Boolean @semanticNonNull
  """
  Delete a Recording record by ID.
  Returns true if the record was deleted, false if it didn't exist.
  """
  deleteRecording(
    id: String!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): Boolean @semanticNonNull
  """
  Delete a RecordingURL record by ID.
  Returns true if the record was deleted, false if it didn't exist.
  """
  deleteRecordingURL(
    id: String!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): Boolean @semanticNonNull
  """
  Delete a Singer record by ID.
  Returns true if the record was deleted, false if it didn't exist.
  """
  deleteSinger(
    id: String!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): Boolean @semanticNonNull
  """
  Delete a SpotifyAlbum record by ID.
  Returns true if the record was deleted, false if it didn't exist.
  """
  deleteSpotifyAlbum(
    id: String!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): Boolean @semanticNonNull
  """
  Delete a TidalAlbum record by ID.
  Returns true if the record was deleted, false if it didn't exist.
  """
  deleteTidalAlbum(
    id: String!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): Boolean @semanticNonNull
  """
  Delete a Upc record by ID.
  Returns true if the record was deleted, false if it didn't exist.
  """
  deleteUpc(
    id: String!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): Boolean @semanticNonNull
  """
  Delete a WikipediaImage record by ID.
  Returns true if the record was deleted, false if it didn't exist.
  """
  deleteWikipediaImage(
    id: String!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): Boolean @semanticNonNull
  linkRecordingToUpc(
    input: LinkRecordingUpcInput!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): Boolean @semanticNonNull
  """
  Merge one Character into another, in favour of the second.
  
  Use when two records turn out to describe the same thing. The losing record
  is deleted and keeps its history; anyone arriving with its id is sent to
  the surviving record instead.
  
  Refused while anything still references the losing record — move or remove
  those first, so that what happens to them is a decision somebody made.
  """
  mergeCharacter(
    id: String!
    intoId: String!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): Character @semanticNonNull
  """
  Merge one Composer into another, in favour of the second.
  
  Use when two records turn out to describe the same thing. The losing record
  is deleted and keeps its history; anyone arriving with its id is sent to
  the surviving record instead.
  
  Refused while anything still references the losing record — move or remove
  those first, so that what happens to them is a decision somebody made.
  """
  mergeComposer(
    id: String!
    intoId: String!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): Composer @semanticNonNull
  """
  Merge one Conductor into another, in favour of the second.
  
  Use when two records turn out to describe the same thing. The losing record
  is deleted and keeps its history; anyone arriving with its id is sent to
  the surviving record instead.
  
  Refused while anything still references the losing record — move or remove
  those first, so that what happens to them is a decision somebody made.
  """
  mergeConductor(
    id: String!
    intoId: String!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): Conductor @semanticNonNull
  """
  Merge one Language into another, in favour of the second.
  
  Use when two records turn out to describe the same thing. The losing record
  is deleted and keeps its history; anyone arriving with its id is sent to
  the surviving record instead.
  
  Refused while anything still references the losing record — move or remove
  those first, so that what happens to them is a decision somebody made.
  """
  mergeLanguage(
    id: String!
    intoId: String!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): Language @semanticNonNull
  """
  Merge one Opera into another, in favour of the second.
  
  Use when two records turn out to describe the same thing. The losing record
  is deleted and keeps its history; anyone arriving with its id is sent to
  the surviving record instead.
  
  Refused while anything still references the losing record — move or remove
  those first, so that what happens to them is a decision somebody made.
  """
  mergeOpera(
    id: String!
    intoId: String!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): Opera @semanticNonNull
  """
  Merge one Recording into another, in favour of the second.
  
  Use when two records turn out to describe the same thing. The losing record
  is deleted and keeps its history; anyone arriving with its id is sent to
  the surviving record instead.
  
  Refused while anything still references the losing record — move or remove
  those first, so that what happens to them is a decision somebody made.
  """
  mergeRecording(
    id: String!
    intoId: String!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): Recording @semanticNonNull
  """
  Merge one Singer into another, in favour of the second.
  
  Use when two records turn out to describe the same thing. The losing record
  is deleted and keeps its history; anyone arriving with its id is sent to
  the surviving record instead.
  
  Refused while anything still references the losing record — move or remove
  those first, so that what happens to them is a decision somebody made.
  """
  mergeSinger(
    id: String!
    intoId: String!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): Singer @semanticNonNull
  """
  Take one out. Taking out what was never in is not an error: the caller
  wanted it gone and it is gone.
  """
  removeRecordingFromCollection(collectionId: Int!, recordingId: Int!): Collection @semanticNonNull
  """Give one a different name. Its id and its contents are unchanged."""
  renameCollection(id: Int!, name: String!): Collection @semanticNonNull
  """Make a list and put this recording in it, in one step."""
  saveRecordingToNewCollection(name: String!, recordingId: Int!): Collection @semanticNonNull
  """
  Make the list hold exactly these recordings.
  
  For a caller that has a whole list in hand and wants it to match --
  reconciling against something else, or applying a batch of edits at once --
  rather than one `add` and one `remove` per difference.
  
  Recordings that have been merged are accepted and understood, so the list
  you read is a list you can send back.
  """
  setCollectionRecordings(
    collectionId: Int!
    """Exactly what the list should hold afterwards. Empty clears it."""
    recordingIds: [Int!]!
  ): Collection @semanticNonNull
  """
  Make this the list a save goes to when nobody says where, taking it from
  whichever list held that job. Nominating the one already nominated is not
  an error.
  """
  setDefaultCollection(id: Int!): Collection @semanticNonNull
  """
  Say something to whoever maintains this.
  
  The website has had a way to do this since long before there was an API. A
  tool talking to that API is where a problem is most likely to be met and
  least likely to be reported, since there is no page to notice a link on, so
  the same box is reachable from here.
  
  Anonymous on purpose: someone blocked by a token, or by a limit, still needs
  a way to say so.
  """
  submitFeedback(
    """Optional, and only used to reply to you"""
    email: String
    """What this is about"""
    kind: FeedbackKind!
    """What you want to say"""
    message: String!
    """Optional. What you were looking at, or the command you ran"""
    url: String
  ): Boolean @semanticNonNull
  """
  Put a deleted or merged AlbumCoverArt back, by id.
  
  Returns the restored record, so a caller can see what came back rather
  than only that something did. That matters most for a merged record:
  restoring one stops it redirecting, and whether the thing now answering
  to this id is what the caller expected is the question they asked.
  
  Null when there is no such record at all. A record that is already live
  is an error rather than a null, because asking to restore something that
  was never gone means the caller is working from a wrong idea of what
  happened.
  """
  undeleteAlbumCoverArt(
    id: String!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): AlbumCoverArt
  """
  Put a deleted or merged AppleMusicAlbum back, by id.
  
  Returns the restored record, so a caller can see what came back rather
  than only that something did. That matters most for a merged record:
  restoring one stops it redirecting, and whether the thing now answering
  to this id is what the caller expected is the question they asked.
  
  Null when there is no such record at all. A record that is already live
  is an error rather than a null, because asking to restore something that
  was never gone means the caller is working from a wrong idea of what
  happened.
  """
  undeleteAppleMusicAlbum(
    id: String!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): AppleMusicAlbum
  """
  Put a deleted or merged Character back, by id.
  
  Returns the restored record, so a caller can see what came back rather
  than only that something did. That matters most for a merged record:
  restoring one stops it redirecting, and whether the thing now answering
  to this id is what the caller expected is the question they asked.
  
  Null when there is no such record at all. A record that is already live
  is an error rather than a null, because asking to restore something that
  was never gone means the caller is working from a wrong idea of what
  happened.
  """
  undeleteCharacter(
    id: String!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): Character
  """
  Put a deleted or merged Composer back, by id.
  
  Returns the restored record, so a caller can see what came back rather
  than only that something did. That matters most for a merged record:
  restoring one stops it redirecting, and whether the thing now answering
  to this id is what the caller expected is the question they asked.
  
  Null when there is no such record at all. A record that is already live
  is an error rather than a null, because asking to restore something that
  was never gone means the caller is working from a wrong idea of what
  happened.
  """
  undeleteComposer(
    id: String!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): Composer
  """
  Put a deleted or merged Conductor back, by id.
  
  Returns the restored record, so a caller can see what came back rather
  than only that something did. That matters most for a merged record:
  restoring one stops it redirecting, and whether the thing now answering
  to this id is what the caller expected is the question they asked.
  
  Null when there is no such record at all. A record that is already live
  is an error rather than a null, because asking to restore something that
  was never gone means the caller is working from a wrong idea of what
  happened.
  """
  undeleteConductor(
    id: String!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): Conductor
  """
  Put a deleted or merged Language back, by id.
  
  Returns the restored record, so a caller can see what came back rather
  than only that something did. That matters most for a merged record:
  restoring one stops it redirecting, and whether the thing now answering
  to this id is what the caller expected is the question they asked.
  
  Null when there is no such record at all. A record that is already live
  is an error rather than a null, because asking to restore something that
  was never gone means the caller is working from a wrong idea of what
  happened.
  """
  undeleteLanguage(
    id: String!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): Language
  """
  Put a deleted or merged MusicBrainzRelease back, by id.
  
  Returns the restored record, so a caller can see what came back rather
  than only that something did. That matters most for a merged record:
  restoring one stops it redirecting, and whether the thing now answering
  to this id is what the caller expected is the question they asked.
  
  Null when there is no such record at all. A record that is already live
  is an error rather than a null, because asking to restore something that
  was never gone means the caller is working from a wrong idea of what
  happened.
  """
  undeleteMusicBrainzRelease(
    id: String!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): MusicBrainzRelease
  """
  Put a deleted or merged MusicBrainzReleaseGroup back, by id.
  
  Returns the restored record, so a caller can see what came back rather
  than only that something did. That matters most for a merged record:
  restoring one stops it redirecting, and whether the thing now answering
  to this id is what the caller expected is the question they asked.
  
  Null when there is no such record at all. A record that is already live
  is an error rather than a null, because asking to restore something that
  was never gone means the caller is working from a wrong idea of what
  happened.
  """
  undeleteMusicBrainzReleaseGroup(
    id: String!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): MusicBrainzReleaseGroup
  """
  Put a deleted or merged NaxosAlbum back, by id.
  
  Returns the restored record, so a caller can see what came back rather
  than only that something did. That matters most for a merged record:
  restoring one stops it redirecting, and whether the thing now answering
  to this id is what the caller expected is the question they asked.
  
  Null when there is no such record at all. A record that is already live
  is an error rather than a null, because asking to restore something that
  was never gone means the caller is working from a wrong idea of what
  happened.
  """
  undeleteNaxosAlbum(
    id: String!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): NaxosAlbum
  """
  Put a deleted or merged Opera back, by id.
  
  Returns the restored record, so a caller can see what came back rather
  than only that something did. That matters most for a merged record:
  restoring one stops it redirecting, and whether the thing now answering
  to this id is what the caller expected is the question they asked.
  
  Null when there is no such record at all. A record that is already live
  is an error rather than a null, because asking to restore something that
  was never gone means the caller is working from a wrong idea of what
  happened.
  """
  undeleteOpera(
    id: String!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): Opera
  """
  Put a deleted or merged Portrayal back, by id.
  
  Returns the restored record, so a caller can see what came back rather
  than only that something did. That matters most for a merged record:
  restoring one stops it redirecting, and whether the thing now answering
  to this id is what the caller expected is the question they asked.
  
  Null when there is no such record at all. A record that is already live
  is an error rather than a null, because asking to restore something that
  was never gone means the caller is working from a wrong idea of what
  happened.
  """
  undeletePortrayal(
    id: String!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): Portrayal
  """
  Put a deleted or merged PrestoMusicAlbum back, by id.
  
  Returns the restored record, so a caller can see what came back rather
  than only that something did. That matters most for a merged record:
  restoring one stops it redirecting, and whether the thing now answering
  to this id is what the caller expected is the question they asked.
  
  Null when there is no such record at all. A record that is already live
  is an error rather than a null, because asking to restore something that
  was never gone means the caller is working from a wrong idea of what
  happened.
  """
  undeletePrestoMusicAlbum(
    id: String!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): PrestoMusicAlbum
  """
  Put a deleted or merged Recording back, by id.
  
  Returns the restored record, so a caller can see what came back rather
  than only that something did. That matters most for a merged record:
  restoring one stops it redirecting, and whether the thing now answering
  to this id is what the caller expected is the question they asked.
  
  Null when there is no such record at all. A record that is already live
  is an error rather than a null, because asking to restore something that
  was never gone means the caller is working from a wrong idea of what
  happened.
  """
  undeleteRecording(
    id: String!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): Recording
  """
  Put a deleted or merged RecordingURL back, by id.
  
  Returns the restored record, so a caller can see what came back rather
  than only that something did. That matters most for a merged record:
  restoring one stops it redirecting, and whether the thing now answering
  to this id is what the caller expected is the question they asked.
  
  Null when there is no such record at all. A record that is already live
  is an error rather than a null, because asking to restore something that
  was never gone means the caller is working from a wrong idea of what
  happened.
  """
  undeleteRecordingURL(
    id: String!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): RecordingURL
  """
  Put a deleted or merged Singer back, by id.
  
  Returns the restored record, so a caller can see what came back rather
  than only that something did. That matters most for a merged record:
  restoring one stops it redirecting, and whether the thing now answering
  to this id is what the caller expected is the question they asked.
  
  Null when there is no such record at all. A record that is already live
  is an error rather than a null, because asking to restore something that
  was never gone means the caller is working from a wrong idea of what
  happened.
  """
  undeleteSinger(
    id: String!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): Singer
  """
  Put a deleted or merged SpotifyAlbum back, by id.
  
  Returns the restored record, so a caller can see what came back rather
  than only that something did. That matters most for a merged record:
  restoring one stops it redirecting, and whether the thing now answering
  to this id is what the caller expected is the question they asked.
  
  Null when there is no such record at all. A record that is already live
  is an error rather than a null, because asking to restore something that
  was never gone means the caller is working from a wrong idea of what
  happened.
  """
  undeleteSpotifyAlbum(
    id: String!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): SpotifyAlbum
  """
  Put a deleted or merged TidalAlbum back, by id.
  
  Returns the restored record, so a caller can see what came back rather
  than only that something did. That matters most for a merged record:
  restoring one stops it redirecting, and whether the thing now answering
  to this id is what the caller expected is the question they asked.
  
  Null when there is no such record at all. A record that is already live
  is an error rather than a null, because asking to restore something that
  was never gone means the caller is working from a wrong idea of what
  happened.
  """
  undeleteTidalAlbum(
    id: String!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): TidalAlbum
  """
  Put a deleted or merged Upc back, by id.
  
  Returns the restored record, so a caller can see what came back rather
  than only that something did. That matters most for a merged record:
  restoring one stops it redirecting, and whether the thing now answering
  to this id is what the caller expected is the question they asked.
  
  Null when there is no such record at all. A record that is already live
  is an error rather than a null, because asking to restore something that
  was never gone means the caller is working from a wrong idea of what
  happened.
  """
  undeleteUpc(
    id: String!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): Upc
  """
  Put a deleted or merged WikipediaImage back, by id.
  
  Returns the restored record, so a caller can see what came back rather
  than only that something did. That matters most for a merged record:
  restoring one stops it redirecting, and whether the thing now answering
  to this id is what the caller expected is the question they asked.
  
  Null when there is no such record at all. A record that is already live
  is an error rather than a null, because asking to restore something that
  was never gone means the caller is working from a wrong idea of what
  happened.
  """
  undeleteWikipediaImage(
    id: String!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): WikipediaImage
  """
  Assuming a UPC and Recording exist, unlink them. Returns true if a link was
  deleted, false if no link existed.
  """
  unlinkRecordingUpc(
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
    recordingId: String!
    upc: String!
  ): Boolean @semanticNonNull
  """Update a AlbumCoverArt record by ID."""
  updateAlbumCoverArt(
    id: String!
    input: UpdateAlbumCoverArtInput!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): AlbumCoverArt @semanticNonNull
  """Update a AppleMusicAlbum record by ID."""
  updateAppleMusicAlbum(
    id: String!
    input: UpdateAppleMusicAlbumInput!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): AppleMusicAlbum @semanticNonNull
  """Update a Character record by ID."""
  updateCharacter(
    id: String!
    input: UpdateCharacterInput!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): Character @semanticNonNull
  """Update a Composer record by ID."""
  updateComposer(
    id: String!
    input: UpdateComposerInput!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): Composer @semanticNonNull
  """Update a Conductor record by ID."""
  updateConductor(
    id: String!
    input: UpdateConductorInput!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): Conductor @semanticNonNull
  """Update a Language record by ID."""
  updateLanguage(
    id: String!
    input: UpdateLanguageInput!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): Language @semanticNonNull
  """Update a MusicBrainzRelease record by ID."""
  updateMusicBrainzRelease(
    id: String!
    input: UpdateMusicBrainzReleaseInput!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): MusicBrainzRelease @semanticNonNull
  """Update a MusicBrainzReleaseGroup record by ID."""
  updateMusicBrainzReleaseGroup(
    id: String!
    input: UpdateMusicBrainzReleaseGroupInput!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): MusicBrainzReleaseGroup @semanticNonNull
  """Update a NaxosAlbum record by ID."""
  updateNaxosAlbum(
    id: String!
    input: UpdateNaxosAlbumInput!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): NaxosAlbum @semanticNonNull
  """Update a Opera record by ID."""
  updateOpera(
    id: String!
    input: UpdateOperaInput!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): Opera @semanticNonNull
  """Update a Portrayal record by ID."""
  updatePortrayal(
    id: String!
    input: UpdatePortrayalInput!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): Portrayal @semanticNonNull
  """Update a PrestoMusicAlbum record by ID."""
  updatePrestoMusicAlbum(
    id: String!
    input: UpdatePrestoMusicAlbumInput!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): PrestoMusicAlbum @semanticNonNull
  """Update a Recording record by ID."""
  updateRecording(
    id: String!
    input: UpdateRecordingInput!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): Recording @semanticNonNull
  """Update a RecordingURL record by ID."""
  updateRecordingURL(
    id: String!
    input: UpdateRecordingURLInput!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): RecordingURL @semanticNonNull
  """Update a Singer record by ID."""
  updateSinger(
    id: String!
    input: UpdateSingerInput!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): Singer @semanticNonNull
  """Update a SpotifyAlbum record by ID."""
  updateSpotifyAlbum(
    id: String!
    input: UpdateSpotifyAlbumInput!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): SpotifyAlbum @semanticNonNull
  """Update a TidalAlbum record by ID."""
  updateTidalAlbum(
    id: String!
    input: UpdateTidalAlbumInput!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): TidalAlbum @semanticNonNull
  """Update a Upc record by ID."""
  updateUpc(
    id: String!
    input: UpdateUpcInput!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): Upc @semanticNonNull
  """Update a WikipediaImage record by ID."""
  updateWikipediaImage(
    id: String!
    input: UpdateWikipediaImageInput!
    """Verifiable source (ideally URL) and context for this change"""
    justification: String!
  ): WikipediaImage @semanticNonNull
}

type NaxosAlbum {
  """
  This Naxos album written out.
  
  For reading, not parsing: one field instead of a query, and the layout may
  change without notice.
  """
  asPlainText: String @semanticNonNull
  catalogueId: String @semanticNonNull
  history: [Edit!] @semanticNonNull
  """
  The value that identifies this record.
  
  Naxos catalog numbers are unique, and are what their own site keys on.
  
  Every other type here answers to `id`, and code that walks all of them —
  the CLI's get/create/edit/delete commands, for one — should not have to
  carry a table of which five are spelled differently. The underlying column
  is unchanged; this is another name for it.
  """
  id: String @semanticNonNull
  upc: Upc
  """URL to the catalog page on naxos.com."""
  url: String @semanticNonNull
}

type Opera {
  """
  This opera written out: composer, roles, recordings.
  
  For reading, not parsing: one field instead of a query, and the layout may
  change without notice.
  """
  asPlainText: String @semanticNonNull
  characters: [Character!] @semanticNonNull
  composer: Composer @semanticNonNull
  englishTitle: String
  history: [Edit!] @semanticNonNull
  historyUrl: String @semanticNonNull
  id: Int @semanticNonNull
  language: Language
  librettist: String
  operaclassId: Int
  recordings: [Recording!] @semanticNonNull
  title: String @semanticNonNull
  url: String @semanticNonNull
  wikipediaImage: WikipediaImage
  wikipediaUrl: String
}

type PageInfo {
  """The position of the last entry, or null on an empty page."""
  endCursor: String
  """Whether asking again with `after: endCursor` would return anything."""
  hasNextPage: Boolean @semanticNonNull
  """Whether anything precedes this page."""
  hasPreviousPage: Boolean @semanticNonNull
  """The position of the first entry, or null on an empty page."""
  startCursor: String
}

type Portrayal {
  """
  This portrayal written out.
  
  For reading, not parsing: one field instead of a query, and the layout may
  change without notice.
  """
  asPlainText: String @semanticNonNull
  character: Character @semanticNonNull
  history: [Edit!] @semanticNonNull
  id: Int @semanticNonNull
  """
  Whether this portrayal is part of the recording's top billing.
  
  Three distinct singers per recording, or all of them where a recording
  has fewer than three. The flag sits on portrayals but is read as a set
  of singers, so a singer who doubles roles may be flagged on one of
  those roles or on all of them; both say the same thing, and
  addRecordingWithPortrayals counts distinct singers and accepts either.
  Recording 77 carries six flagged portrayals for three singers, and
  recording 6417 flags Deborah Polaski for both Cassandre and Didon.
  
  Which three is an editorial judgment, and the editorial policy's rule
  on billing flags is the authoritative text for making it. It cannot be
  worked out from the cast: recording 88 notes Carreras, van Dam and
  Zednik and leaves the title role unflagged, so reading the principals
  off the roles and inferring the answer gives the wrong three.
  """
  noted: Boolean
  recording: Recording @semanticNonNull
  singer: Singer @semanticNonNull
}

type PrestoMusicAlbum {
  """
  This Presto Music album written out.
  
  For reading, not parsing: one field instead of a query, and the layout may
  change without notice.
  """
  asPlainText: String @semanticNonNull
  history: [Edit!] @semanticNonNull
  """
  The value that identifies this record.
  
  The slug, which is what `presto_music_album_id` holds and what getById
  looks up. Not `prestoCode`, which is derived from it by taking the part
  before the dashes and cannot be looked up with.
  
  Every other type here answers to `id`, and code that walks all of them —
  the CLI's get/create/edit/delete commands, for one — should not have to
  carry a table of which five are spelled differently. The underlying column
  is unchanged; this is another name for it.
  """
  id: String @semanticNonNull
  isStreaming: Boolean
  """
  The numeric Presto Music product code, extracted from the slug.
  e.g. "8475796--johann-sebastian-bach" → "8475796"
  """
  prestoCode: String @semanticNonNull
  slug: String @semanticNonNull
  upc: Upc
  """URL to the product page on prestomusic.com."""
  url: String @semanticNonNull
}

type Query {
  """
  Search for recordings matching the given filter criteria.
  All filter fields are optional and can be combined.
  """
  filterRecordings(filter: RecordingFilterInput!, first: Int! = 50, offset: Int! = 0): [Recording!] @semanticNonNull
  getAlbumCoverArtById(id: String!): AlbumCoverArt
  getAlbumCoverArtByIds(ids: [String!]!): [AlbumCoverArt] @semanticNonNull
  getAppleMusicAlbumById(id: String!): AppleMusicAlbum
  getAppleMusicAlbumByIds(ids: [String!]!): [AppleMusicAlbum] @semanticNonNull
  """
  Look up a Character by id, following any merge.
  
  If this id belongs to a record that has since been merged into another,
  the surviving record is returned, so that a link made before the merge
  keeps working. Reading a merged-away record itself — its `history`, most
  of all — is done by asking for its history, which never redirects.
  """
  getCharacterById(id: String!): Character
  """
  Look up Characters by id, following any merge.
  
  An id belonging to a record that has since been merged into another yields
  the surviving record, so a stored list of ids keeps working. Positions are
  preserved: the nth answer is for the nth id, whether or not it moved.
  """
  getCharacterByIds(ids: [String!]!): [Character] @semanticNonNull
  """Gets all UPCs that are associated with more than one opera recording."""
  getCollectionUpcs: [Upc!] @semanticNonNull
  """
  Look up a Composer by id, following any merge.
  
  If this id belongs to a record that has since been merged into another,
  the surviving record is returned, so that a link made before the merge
  keeps working. Reading a merged-away record itself — its `history`, most
  of all — is done by asking for its history, which never redirects.
  """
  getComposerById(id: String!): Composer
  """
  Look up Composers by id, following any merge.
  
  An id belonging to a record that has since been merged into another yields
  the surviving record, so a stored list of ids keeps working. Positions are
  preserved: the nth answer is for the nth id, whether or not it moved.
  """
  getComposerByIds(ids: [String!]!): [Composer] @semanticNonNull
  """
  Look up a Composer by its operaclassId.
  
  At most one live Composer holds any given operaclassId;
  where a record predating that rule still shares one, the lowest id wins,
  so the answer is at least stable.
  """
  getComposerByOperaclassId(operaclassId: Int!): Composer
  """
  Look up a Conductor by id, following any merge.
  
  If this id belongs to a record that has since been merged into another,
  the surviving record is returned, so that a link made before the merge
  keeps working. Reading a merged-away record itself — its `history`, most
  of all — is done by asking for its history, which never redirects.
  """
  getConductorById(id: String!): Conductor
  """
  Look up Conductors by id, following any merge.
  
  An id belonging to a record that has since been merged into another yields
  the surviving record, so a stored list of ids keeps working. Positions are
  preserved: the nth answer is for the nth id, whether or not it moved.
  """
  getConductorByIds(ids: [String!]!): [Conductor] @semanticNonNull
  """
  Look up a Conductor by its operaclassId.
  
  At most one live Conductor holds any given operaclassId;
  where a record predating that rule still shares one, the lowest id wins,
  so the answer is at least stable.
  """
  getConductorByOperaclassId(operaclassId: Int!): Conductor
  """A single edit, by id."""
  getEditById(id: Int!): Edit
  """
  Look up a Language by id, following any merge.
  
  If this id belongs to a record that has since been merged into another,
  the surviving record is returned, so that a link made before the merge
  keeps working. Reading a merged-away record itself — its `history`, most
  of all — is done by asking for its history, which never redirects.
  """
  getLanguageById(id: String!): Language
  """
  Look up Languages by id, following any merge.
  
  An id belonging to a record that has since been merged into another yields
  the surviving record, so a stored list of ids keeps working. Positions are
  preserved: the nth answer is for the nth id, whether or not it moved.
  """
  getLanguageByIds(ids: [String!]!): [Language] @semanticNonNull
  getMusicBrainzReleaseById(id: String!): MusicBrainzRelease
  getMusicBrainzReleaseByIds(ids: [String!]!): [MusicBrainzRelease] @semanticNonNull
  getMusicBrainzReleaseGroupById(id: String!): MusicBrainzReleaseGroup
  getMusicBrainzReleaseGroupByIds(ids: [String!]!): [MusicBrainzReleaseGroup] @semanticNonNull
  getNaxosAlbumById(id: String!): NaxosAlbum
  getNaxosAlbumByIds(ids: [String!]!): [NaxosAlbum] @semanticNonNull
  """
  Look up a Opera by id, following any merge.
  
  If this id belongs to a record that has since been merged into another,
  the surviving record is returned, so that a link made before the merge
  keeps working. Reading a merged-away record itself — its `history`, most
  of all — is done by asking for its history, which never redirects.
  """
  getOperaById(id: String!): Opera
  """
  Look up Operas by id, following any merge.
  
  An id belonging to a record that has since been merged into another yields
  the surviving record, so a stored list of ids keeps working. Positions are
  preserved: the nth answer is for the nth id, whether or not it moved.
  """
  getOperaByIds(ids: [String!]!): [Opera] @semanticNonNull
  """
  Look up a Opera by its operaclassId.
  
  At most one live Opera holds any given operaclassId;
  where a record predating that rule still shares one, the lowest id wins,
  so the answer is at least stable.
  """
  getOperaByOperaclassId(operaclassId: Int!): Opera
  getPortrayalById(id: String!): Portrayal
  getPortrayalByIds(ids: [String!]!): [Portrayal] @semanticNonNull
  getPrestoMusicAlbumById(id: String!): PrestoMusicAlbum
  getPrestoMusicAlbumByIds(ids: [String!]!): [PrestoMusicAlbum] @semanticNonNull
  """
  Look up a Recording by id, following any merge.
  
  If this id belongs to a record that has since been merged into another,
  the surviving record is returned, so that a link made before the merge
  keeps working. Reading a merged-away record itself — its `history`, most
  of all — is done by asking for its history, which never redirects.
  """
  getRecordingById(id: String!): Recording
  """
  Look up Recordings by id, following any merge.
  
  An id belonging to a record that has since been merged into another yields
  the surviving record, so a stored list of ids keeps working. Positions are
  preserved: the nth answer is for the nth id, whether or not it moved.
  """
  getRecordingByIds(ids: [String!]!): [Recording] @semanticNonNull
  """
  Look up a Recording by its operaclassId.
  
  At most one live Recording holds any given operaclassId;
  where a record predating that rule still shares one, the lowest id wins,
  so the answer is at least stable.
  """
  getRecordingByOperaclassId(operaclassId: Int!): Recording
  getRecordingURLById(id: String!): RecordingURL
  getRecordingURLByIds(ids: [String!]!): [RecordingURL] @semanticNonNull
  """
  Look up a Singer by id, following any merge.
  
  If this id belongs to a record that has since been merged into another,
  the surviving record is returned, so that a link made before the merge
  keeps working. Reading a merged-away record itself — its `history`, most
  of all — is done by asking for its history, which never redirects.
  """
  getSingerById(id: String!): Singer
  """
  Look up Singers by id, following any merge.
  
  An id belonging to a record that has since been merged into another yields
  the surviving record, so a stored list of ids keeps working. Positions are
  preserved: the nth answer is for the nth id, whether or not it moved.
  """
  getSingerByIds(ids: [String!]!): [Singer] @semanticNonNull
  """
  Look up a Singer by its operaclassId.
  
  At most one live Singer holds any given operaclassId;
  where a record predating that rule still shares one, the lowest id wins,
  so the answer is at least stable.
  """
  getSingerByOperaclassId(operaclassId: Int!): Singer
  getSpotifyAlbumById(id: String!): SpotifyAlbum
  getSpotifyAlbumByIds(ids: [String!]!): [SpotifyAlbum] @semanticNonNull
  getTidalAlbumById(id: String!): TidalAlbum
  getTidalAlbumByIds(ids: [String!]!): [TidalAlbum] @semanticNonNull
  getUpcById(id: String!): Upc
  getUpcByIds(ids: [String!]!): [Upc] @semanticNonNull
  getWikipediaImageById(id: String!): WikipediaImage
  getWikipediaImageByIds(ids: [String!]!): [WikipediaImage] @semanticNonNull
  listAlbumCoverArts(first: Int! = 100, offset: Int! = 0, order: SortOrder, sortBy: AlbumCoverArtSortBy): [AlbumCoverArt!] @semanticNonNull
  listAppleMusicAlbums(first: Int! = 100, offset: Int! = 0, order: SortOrder, sortBy: AppleMusicAlbumSortBy): [AppleMusicAlbum!] @semanticNonNull
  listCharacters(first: Int! = 100, offset: Int! = 0, order: SortOrder, sortBy: CharacterSortBy): [Character!] @semanticNonNull
  listComposers(first: Int! = 100, offset: Int! = 0, order: SortOrder, sortBy: ComposerSortBy): [Composer!] @semanticNonNull
  listConductors(first: Int! = 100, offset: Int! = 0, order: SortOrder, sortBy: ConductorSortBy): [Conductor!] @semanticNonNull
  listLanguages(first: Int! = 100, offset: Int! = 0, order: SortOrder, sortBy: LanguageSortBy): [Language!] @semanticNonNull
  listMusicBrainzReleaseGroups(first: Int! = 100, offset: Int! = 0, order: SortOrder, sortBy: MusicBrainzReleaseGroupSortBy): [MusicBrainzReleaseGroup!] @semanticNonNull
  listMusicBrainzReleases(first: Int! = 100, offset: Int! = 0, order: SortOrder, sortBy: MusicBrainzReleaseSortBy): [MusicBrainzRelease!] @semanticNonNull
  listNaxosAlbums(first: Int! = 100, offset: Int! = 0, order: SortOrder, sortBy: NaxosAlbumSortBy): [NaxosAlbum!] @semanticNonNull
  listOperas(first: Int! = 100, offset: Int! = 0, order: SortOrder, sortBy: OperaSortBy): [Opera!] @semanticNonNull
  listPortrayals(first: Int! = 100, offset: Int! = 0, order: SortOrder, sortBy: PortrayalSortBy): [Portrayal!] @semanticNonNull
  listPrestoMusicAlbums(first: Int! = 100, offset: Int! = 0, order: SortOrder, sortBy: PrestoMusicAlbumSortBy): [PrestoMusicAlbum!] @semanticNonNull
  listRecordingURLs(first: Int! = 100, offset: Int! = 0, order: SortOrder, sortBy: RecordingURLSortBy): [RecordingURL!] @semanticNonNull
  listRecordings(first: Int! = 100, offset: Int! = 0, order: SortOrder, sortBy: RecordingSortBy): [Recording!] @semanticNonNull
  listSingers(first: Int! = 100, offset: Int! = 0, order: SortOrder, sortBy: SingerSortBy): [Singer!] @semanticNonNull
  listSpotifyAlbums(first: Int! = 100, offset: Int! = 0, order: SortOrder, sortBy: SpotifyAlbumSortBy): [SpotifyAlbum!] @semanticNonNull
  listTidalAlbums(first: Int! = 100, offset: Int! = 0, order: SortOrder, sortBy: TidalAlbumSortBy): [TidalAlbum!] @semanticNonNull
  listUpcs(first: Int! = 100, offset: Int! = 0, order: SortOrder, sortBy: UpcSortBy): [Upc!] @semanticNonNull
  listWikipediaImages(first: Int! = 100, offset: Int! = 0, order: SortOrder, sortBy: WikipediaImageSortBy): [WikipediaImage!] @semanticNonNull
  """
  Every record version, oldest first by default: the change log, one entry
  per record rather than one per edit.
  
  Each entry says what is true of the record after that version, `isNew` and
  `deletion`, so it can be applied on its own. A consumer
  that stores the `cursor` of the last entry it handled and resumes from it
  later arrives where one that read the whole log at once would.
  
  One direction at a time, whichever `order` names, and nothing is ever
  inserted behind a cursor, so a stored cursor cannot miss a change.
  """
  recordChanges(
    """
    Resume from the `cursor` of the last entry read. Read in whichever
    direction `order` names: ascending it means entries after this one,
    descending, before it. Null starts at whichever end `order` begins from.
    """
    after: String
    """At most this many. Capped at 500."""
    first: Int! = 100
    """
    `ASC`, the default, reads the log forwards from the oldest entry, which
    is what a consumer catching up wants. `DESC` starts at the newest and
    reads backwards.
    """
    order: SortOrder
    """
    Only rows of this table. A cursor belongs to the feed that issued it, so
    read each table with its own cursor; passing one from a differently
    narrowed feed is an error rather than a silent skip.
    """
    table: RecordTable
  ): EditedRecordConnection @semanticNonNull
  searchCharacters(first: Int! = 5, query: String!): [Character!] @semanticNonNull
  searchComposers(first: Int! = 5, query: String!): [Composer!] @semanticNonNull
  searchConductors(first: Int! = 5, query: String!): [Conductor!] @semanticNonNull
  searchOperas(first: Int! = 5, query: String!): [Opera!] @semanticNonNull
  searchSingers(first: Int! = 5, query: String!): [Singer!] @semanticNonNull
  searchUpcs(first: Int! = 5, query: String!): [Upc!] @semanticNonNull
  """
  The account this request is signed in as, or null if it is not signed in.
  
  Null is an answer rather than a refusal: almost everything here reads
  without signing in.
  """
  viewer: User
}

"""
A record that is no longer live, and where it went.

A class rather than the plain object this was, so that naming the survivor
has a `vc` to look it up with. The shape a caller sees is unchanged.
"""
type RecordDeletion {
  """
  The surviving record, by name and address.
  
  `mergedIntoId` on its own says a merge happened and nothing about what it
  decided, which is the one thing anybody reading a merge wants: an id is
  not a singer. Null for a plain deletion.
  
  Read past the view, because a survivor can itself have been merged away
  later and the chain is still worth following.
  """
  mergedInto: EditableRecord
  """
  Where this record's id now leads. Null for a plain deletion, which leads
  nowhere.
  
  Never more than one step: merging repoints any id that already led here.
  """
  mergedIntoId: String
}

type Recording {
  """
  This recording written out: opera, conductor, barcodes, where to listen, cast.
  
  For reading, not parsing: one field instead of a query, and the layout may
  change without notice.
  """
  asPlainText: String @semanticNonNull
  chorus: String
  conductor: Conductor @semanticNonNull
  """
  A representative cover artwork for the recording, if any.
  
  Note that a recording may have many releases (UPCs), each with its own cover art.
  """
  coverArt: AlbumCoverArt
  day: Int
  """
  Get all Apple Music albums associated with a recording through UPC relationships.
  Traverses: Recording -> RecordingUpc -> Upc -> AppleMusicAlbum
  """
  getAppleMusicAlbums: [AppleMusicAlbum!] @semanticNonNull
  """
  Get all Spotify albums associated with a recording through UPC relationships.
  Traverses: Recording -> RecordingUpc -> Upc -> SpotifyAlbum
  """
  getSpotifyAlbums: [SpotifyAlbum!] @semanticNonNull
  history: [Edit!] @semanticNonNull
  historyUrl: String @semanticNonNull
  id: Int @semanticNonNull
  markerUrl: String @semanticNonNull
  month: Int
  musicBrainzReleaseGroups: [MusicBrainzReleaseGroup!] @semanticNonNull
  """
  The singers on this recording who received top billing.
  
  Three of them, taken in the order the release itself bills them. A singer
  who doubles a role is flagged on each of their portrayals, so the count of
  flagged portrayals can exceed three while this list stays at three: it is
  singers, deduplicated, not portrayals.
  
  Nothing enforces the three, and it cannot be recovered from the cast --
  recording 88 bills Carreras, van Dam and Zednik and leaves the title role
  unflagged. See `Portrayal.noted`.
  """
  notedSingers: [Singer!] @semanticNonNull
  opera: Opera @semanticNonNull
  operaclassId: Int
  orchestra: String
  portrayals: [Portrayal!] @semanticNonNull
  relatedUrls: [RecordingURL!] @semanticNonNull
  upcs: [Upc!] @semanticNonNull
  url: String @semanticNonNull
  year: Int
}

type RecordingURL {
  """
  This Recording URL written out.
  
  For reading, not parsing: one field instead of a query, and the layout may
  change without notice.
  """
  asPlainText: String @semanticNonNull
  history: [Edit!] @semanticNonNull
  id: Int @semanticNonNull
  recording: Recording @semanticNonNull
  url: String @semanticNonNull
}

type Singer {
  """
  This singer written out: roles recorded.
  
  For reading, not parsing: one field instead of a query, and the layout may
  change without notice.
  """
  asPlainText: String @semanticNonNull
  born: Int
  died: Int
  firstName: String @semanticNonNull
  """Returns the full name of the singer (first name + last name)."""
  fullName: String @semanticNonNull
  history: [Edit!] @semanticNonNull
  historyUrl: String @semanticNonNull
  id: Int @semanticNonNull
  lastName: String @semanticNonNull
  operaclassId: Int
  portrayals: [Portrayal!] @semanticNonNull
  spotifyArtistId: String
  url: String @semanticNonNull
  wikipediaImage: WikipediaImage
  wikipediaUrl: String
}

type SpotifyAlbum {
  albumName: String
  """
  This Spotify album written out.
  
  For reading, not parsing: one field instead of a query, and the layout may
  change without notice.
  """
  asPlainText: String @semanticNonNull
  history: [Edit!] @semanticNonNull
  id: String @semanticNonNull
  upc: Upc
  """URL to the album on Spotify."""
  url: String @semanticNonNull
}

type TidalAlbum {
  """
  This Tidal album written out.
  
  For reading, not parsing: one field instead of a query, and the layout may
  change without notice.
  """
  asPlainText: String @semanticNonNull
  history: [Edit!] @semanticNonNull
  id: String @semanticNonNull
  upc: Upc
  """URL to the album on Tidal."""
  url: String @semanticNonNull
}

type Upc {
  appleMusicAlbums: [AppleMusicAlbum!] @semanticNonNull
  """
  This UPC written out.
  
  For reading, not parsing: one field instead of a query, and the layout may
  change without notice.
  """
  asPlainText: String @semanticNonNull
  """
  Indicates a UPC represent a release that includes multiple opera recordings.
  """
  collection: Boolean @semanticNonNull
  history: [Edit!] @semanticNonNull
  """
  The value that identifies this record.
  
  A UPC is its own identity.
  
  Every other type here answers to `id`, and code that walks all of them —
  the CLI's get/create/edit/delete commands, for one — should not have to
  carry a table of which five are spelled differently. The underlying column
  is unchanged; this is another name for it.
  """
  id: String @semanticNonNull
  musicBrainzReleases: [MusicBrainzRelease!] @semanticNonNull
  naxosAlbums: [NaxosAlbum!] @semanticNonNull
  prestoMusicAlbums: [PrestoMusicAlbum!] @semanticNonNull
  recordings: [Recording!] @semanticNonNull
  spotifyAlbums: [SpotifyAlbum!] @semanticNonNull
  tidalAlbums: [TidalAlbum!] @semanticNonNull
  """
  The barcode, always at its full fourteen digits.
  
  UPC-A, EAN-13 and GTIN-14 are three lengths of one number rather than three
  different barcodes, and the check digit is the same at every length. This
  returns the longest, zero-padded, because it is the one form that can hold
  any of them.
  
  Any of the three is accepted wherever a barcode is taken, and answered for
  with this one. Somewhere less formal than an API -- a page, a link, anything
  a person reads -- is welcome to drop the leading zeros again.
  """
  upc: String @semanticNonNull
}

"""
An account: a name, and nothing else.

Not a person, and not verified -- anyone can have one, and nobody is asked to
prove anything to get it. What an account is for is narrower than it looks:
it makes the edits of one caller recognisable as one caller's work, so that a
run of changes can be read, trusted or doubted together.

Usually somebody else's. This is what an edit hands back when asked who made
it, so the account being described is far more often the one you are reading
about than the one you are signed in as. `viewer` is the exception.
"""
type User {
  """
  The lists this account keeps, oldest first.
  
  Empty unless the account asking is the account asked about, which today is
  the only way to get here at all -- `viewer` is the sole route to a `User`
  whose collections anybody can see. The check is written anyway, because the
  day a second route appears is not the day anybody will remember this.
  """
  collections: [Collection!] @semanticNonNull
  """
  The change log, narrowed to what this account wrote.
  
  The same entries as `Query.recordChanges`, in the same order, without any
  other account's. A cursor from here resumes here: passing one to
  `Query.recordChanges` silently steps over everything written in between.
  """
  recordChanges(
    """Resume from the `cursor` of the last entry this field returned."""
    after: String
    """At most this many. Capped at 500."""
    first: Int! = 100
    """
    `ASC`, the default, reads from this account's oldest change. `DESC`
    starts at its newest, which is usually the question being asked here.
    """
    order: SortOrder
    """Only rows of this table, read with its own cursor."""
    table: RecordTable
  ): EditedRecordConnection @semanticNonNull
  """
  What to call this account.
  
  Public, and shown on every edit the account has made. Letters, digits and
  hyphens, at most 39, with no hyphen at either end and none doubled. Two
  accounts cannot differ by case alone.
  """
  username: String @semanticNonNull
}

type WikipediaImage {
  """
  This Wikipedia image written out.
  
  For reading, not parsing: one field instead of a query, and the layout may
  change without notice.
  """
  asPlainText: String @semanticNonNull
  history: [Edit!] @semanticNonNull
  """
  The value that identifies this record.
  
  Keyed by the Wikipedia page it came from.
  
  Every other type here answers to `id`, and code that walks all of them —
  the CLI's get/create/edit/delete commands, for one — should not have to
  carry a table of which five are spelled differently. The underlying column
  is unchanged; this is another name for it.
  """
  id: String @semanticNonNull
  imageUrl: String
  wikipediaUrl: String @semanticNonNull
}
