function clean(value, schema, path) {
  const {need = [], allow = [], remove = []} = schema
  const keys = Object.keys(value)
    .filter(function (d) {
      return !remove.includes(d)
    })
    .sort()
  const allAllowed = new Set([...allow, ...need])

  for (const d of need) {
    assert.ok(
      keys.includes(d),
      'expected field `' + d + '` in `' + value + '` at `' + path + '`'
    )
  }

  for (const d of keys) {
    assert.ok(
      allAllowed.has(d),
      'unexpected field `' + d + '` in `' + value + '` at `' + path + '`'
    )
  }

  /** @type {Thing} */
  // @ts-expect-error: clone.
  const result = {}

  for (const d of keys) {
    // @ts-expect-error: clone.
    result[d] = value[d]
  }

  return result
}
@template {Object} Thing
  Kind.
@param {Thing} value
  Value to clean.
@param {Object} schema
  Schema to clean against.
@param {ReadonlyArray<string>} [schema.need]
  Fields to require.
@param {ReadonlyArray<string>} schema.allow
  Fields to allow.
@param {ReadonlyArray<string>} schema.remove
  Fields to remove.
@param {string} path
  Path.
@returns {Thing}
  Clone.