Consider more semantic join syntax

```javascript
{
  type: 'select'
, table: 'users'

  // ` left join "consumer" "c" on "c"."id" = "users"."id" `
  // or the object version
  // ` left join (select "consumers".* from "consumers") "c" on "c"."id" = "users"."id" `
, joins: {
    c: {
      type: 'left'

      // If target is not specified, assume object key is target and alias
      // If it's a string, we'll assume that it's a table
    , target: 'consumers'

      // If it's an object, run it through the query builder
    , target: {
        type: 'select'
      , table: 'consumers'
      }

      // This gets run through the conditional evaluator
      // Left hand by default gets the joined table name
      // in this case, we know it's consumers aliased as c
      //   `"c"."id" = "users"."id"
    , on: {
        'id': '$users.id$'
      }
    }
  }
}
```

This is so much more semantic and gives the ability to more easily reason about joins. Also, it can be introduced as a new query helper without breaking the old functionality. We can just deprecate all of the old join types. Also, this will combine all joins into one namespace allowing you to just specify a type. As with other parts of the mosql, you could also use an array of objects and instead of using the object key as the alias, you could specify an alias field.

This would also take care of #12