+
+The TypeScript library for working with microsharded PostgreSQL databases.
+
+* [Getting Started and Tutorials](https://ent-framework.net)
+* [API documentation](https://github.com/dimikot/ent-framework/blob/main/docs/globals.md)
+* [Source code](https://github.com/dimikot/ent-framework/tree/main/src)
+* [Ent Framework's Discord](https://discord.gg/QXvN6VTCKS)
+
+#### Core Features
1. **Graph-like representation of entities.** With Ent Framework, you represent each Ent (a domain object of your business logic) as a TypeScript class with immutable properties. An Ent class instance maps to one row of some table in a relational database (like PostgreSQL). It may look similar to ORM, but has many aspects that traditional ORMs don't have.
2. **Row-level security in a graph (privacy layer).** You manage data as a graph where each node is an Ent instance, and each edge is a field link (think of foreign keys) to other Ents. To be allowed to read (or update/delete) some Ent, you define a set of explicit rules like "user can read EntA if they can read EntB or EntC". And, consequently, in EntB you define its own set of rules, like "user can read EntB if they can read EntD".
diff --git a/barrelsby.json b/barrelsby.json
new file mode 100644
index 0000000..48dc270
--- /dev/null
+++ b/barrelsby.json
@@ -0,0 +1,15 @@
+{
+ "name": "index",
+ "directory": [
+ "./src/abstract",
+ "./src/ent",
+ "./src/helpers",
+ "./src/pg",
+ "./src/tools"
+ ],
+ "exclude": [
+ "ent-framework\/.*\/__",
+ "ent-framework\/.*\/internal"
+ ],
+ "delete": true
+}
diff --git a/docker-compose.postgres.yml b/docker-compose.postgres.yml
new file mode 100644
index 0000000..6b232a8
--- /dev/null
+++ b/docker-compose.postgres.yml
@@ -0,0 +1,14 @@
+services:
+ postgres:
+ image: postgres:16
+ ports:
+ - "${PGPORT:?err}:${PGPORT}"
+ environment:
+ POSTGRES_PASSWORD: postgres
+ PGDATA: /tmp/postgresql
+ POSTGRES_INITDB_ARGS: "-c port=$PGPORT -c max_connections=2000 -c synchronous_commit=off -c wal_level=logical"
+ healthcheck:
+ test: "PGPORT=$PGPORT pg_isready -U postgres"
+ interval: 0.3s
+ timeout: 20s
+ retries: 10
diff --git a/docs/README.md b/docs/README.md
new file mode 100644
index 0000000..93ab141
--- /dev/null
+++ b/docs/README.md
@@ -0,0 +1,33 @@
+**ent-framework**
+
+***
+
+# Ent Framework
+
+
+
+The TypeScript library for working with microsharded PostgreSQL databases.
+
+* [Getting Started and Tutorials](https://ent-framework.net)
+* [API documentation](https://github.com/dimikot/ent-framework/blob/main/docs/globals.md)
+* [Source code](https://github.com/dimikot/ent-framework/tree/main/src)
+* [Ent Framework's Discord](https://discord.gg/QXvN6VTCKS)
+
+#### Core Features
+
+1. **Graph-like representation of entities.** With Ent Framework, you represent each Ent (a domain object of your business logic) as a TypeScript class with immutable properties. An Ent class instance maps to one row of some table in a relational database (like PostgreSQL). It may look similar to ORM, but has many aspects that traditional ORMs don't have.
+2. **Row-level security in a graph (privacy layer).** You manage data as a graph where each node is an Ent instance, and each edge is a field link (think of foreign keys) to other Ents. To be allowed to read (or update/delete) some Ent, you define a set of explicit rules like "user can read EntA if they can read EntB or EntC". And, consequently, in EntB you define its own set of rules, like "user can read EntB if they can read EntD".
+3. **Query batching and coalescing.** Ent Framework holistically solves the "N+1 selects" problem commonly known in ORM world. You still write you code as if you work with individual Ents and individual IDs, and the framework magically takes care of sending batched requests (both read and write) to the underlying relational database. You do not work with lists and JOINs anymore.
+4. **Microsharding and replication lag tracking support out of the box.** Splitting your database horizontally is like a breeze now: Ent Framework takes care of routing the requests to the proper microshards. When scaling reads, Ent Framework knows whether a replica node is "good enough" for that particular query. It automatically uses the proper replica when possible, falling back to master when not.
+5. **Pluggable to your existing relational database.** If your project already uses some ORM or runs raw SQL queries, Ent Framework can be plugged in.
+6. **Tens of other features.** Some examples: cross-microshards foreign keys, composite fields, triggers, build-in caching etc.
+
+#### Installation
+
+```
+npm add ent-framework
+pnpm add ent-framework
+yarn add ent-framework
+```
+
+
diff --git a/docs/_media/logo-berkshire-swash.svg b/docs/_media/logo-berkshire-swash.svg
new file mode 100644
index 0000000..6ccce58
--- /dev/null
+++ b/docs/_media/logo-berkshire-swash.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/docs/classes/AllowIf.md b/docs/classes/AllowIf.md
new file mode 100644
index 0000000..8b2562b
--- /dev/null
+++ b/docs/classes/AllowIf.md
@@ -0,0 +1,77 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / AllowIf
+
+# Class: AllowIf\
+
+Defined in: [src/ent/rules/AllowIf.ts:12](https://github.com/clickup/ent-framework/blob/master/src/ent/rules/AllowIf.ts#L12)
+
+Returns ALLOW if the predicate succeeds, otherwise SKIP.
+- Used mostly for read permission checks.
+- This rule may still throw an exception if the exception is a wild one (not
+ derived from EntAccessError).
+
+## Extends
+
+- [`Rule`](Rule.md)\<`TInput`\>
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TInput` *extends* `object` |
+
+## Constructors
+
+### new AllowIf()
+
+> **new AllowIf**\<`TInput`\>(`predicate`): [`AllowIf`](AllowIf.md)\<`TInput`\>
+
+Defined in: [src/ent/rules/Rule.ts:43](https://github.com/clickup/ent-framework/blob/master/src/ent/rules/Rule.ts#L43)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `predicate` | [`Predicate`](../interfaces/Predicate.md)\<`TInput`\> \| (`vc`, `input`) => `boolean` \| `Promise`\<`boolean`\> |
+
+#### Returns
+
+[`AllowIf`](AllowIf.md)\<`TInput`\>
+
+#### Inherited from
+
+[`Rule`](Rule.md).[`constructor`](Rule.md#constructors)
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `_TAG` | `"AllowIf"` |
+| `predicate` | [`Predicate`](../interfaces/Predicate.md)\<`TInput`\> |
+| `name` | `string` |
+
+## Methods
+
+### evaluate()
+
+> **evaluate**(`vc`, `input`): `Promise`\<[`RuleResult`](../interfaces/RuleResult.md)\>
+
+Defined in: [src/ent/rules/AllowIf.ts:15](https://github.com/clickup/ent-framework/blob/master/src/ent/rules/AllowIf.ts#L15)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `vc` | [`VC`](VC.md) |
+| `input` | `TInput` |
+
+#### Returns
+
+`Promise`\<[`RuleResult`](../interfaces/RuleResult.md)\>
+
+#### Overrides
+
+[`Rule`](Rule.md).[`evaluate`](Rule.md#evaluate)
diff --git a/docs/classes/Batcher.md b/docs/classes/Batcher.md
new file mode 100644
index 0000000..4010d00
--- /dev/null
+++ b/docs/classes/Batcher.md
@@ -0,0 +1,75 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / Batcher
+
+# Class: Batcher\
+
+Defined in: [src/abstract/Batcher.ts:19](https://github.com/clickup/ent-framework/blob/master/src/abstract/Batcher.ts#L19)
+
+Batcher is similar to DataLoader, but with a few important differences:
+1. It's strongly typed not only for the output, but for input too. And input
+ can be arbitrary, not only strings (e.g. rows).
+2. It does requests dedupping for all queries (including selects).
+3. It's not limited by read-only requests like DataLoader, and thus it
+ doesn't to any caching. Caching is delegated to some other layer (either
+ above Batcher or in Runner).
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TInput` |
+| `TOutput` |
+
+## Constructors
+
+### new Batcher()
+
+> **new Batcher**\<`TInput`, `TOutput`\>(`runner`, `batchDelayMs`, `disableBatching`): [`Batcher`](Batcher.md)\<`TInput`, `TOutput`\>
+
+Defined in: [src/abstract/Batcher.ts:90](https://github.com/clickup/ent-framework/blob/master/src/abstract/Batcher.ts#L90)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `runner` | [`Runner`](Runner.md)\<`TInput`, `TOutput`\> |
+| `batchDelayMs` | `MaybeCallable`\<`number`\> |
+| `disableBatching` | `boolean` |
+
+#### Returns
+
+[`Batcher`](Batcher.md)\<`TInput`, `TOutput`\>
+
+## Methods
+
+### flushQueue()
+
+> `protected` **flushQueue**(): `Promise`\<`void`\>
+
+Defined in: [src/abstract/Batcher.ts:32](https://github.com/clickup/ent-framework/blob/master/src/abstract/Batcher.ts#L32)
+
+#### Returns
+
+`Promise`\<`void`\>
+
+***
+
+### run()
+
+> **run**(`input`, `annotation`): `Promise`\<`TOutput`\>
+
+Defined in: [src/abstract/Batcher.ts:96](https://github.com/clickup/ent-framework/blob/master/src/abstract/Batcher.ts#L96)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `input` | `TInput` |
+| `annotation` | [`QueryAnnotation`](../interfaces/QueryAnnotation.md) |
+
+#### Returns
+
+`Promise`\<`TOutput`\>
diff --git a/docs/classes/CanDeleteOutgoingEdge.md b/docs/classes/CanDeleteOutgoingEdge.md
new file mode 100644
index 0000000..e91bc1a
--- /dev/null
+++ b/docs/classes/CanDeleteOutgoingEdge.md
@@ -0,0 +1,72 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / CanDeleteOutgoingEdge
+
+# Class: CanDeleteOutgoingEdge\
+
+Defined in: [src/ent/predicates/CanDeleteOutgoingEdge.ts:10](https://github.com/clickup/ent-framework/blob/master/src/ent/predicates/CanDeleteOutgoingEdge.ts#L10)
+
+Checks that an Ent available via a field can be deleted, or Ent doesn't exist
+(e.g. Ent is orphaned). See CanReadOutgoingEdge comments for more details.
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TField` *extends* `string` |
+
+## Implements
+
+- [`Predicate`](../interfaces/Predicate.md)\<`Record`\<`TField`, `string` \| `null`\>\>
+
+## Constructors
+
+### new CanDeleteOutgoingEdge()
+
+> **new CanDeleteOutgoingEdge**\<`TField`\>(`field`, `toEntClass`): [`CanDeleteOutgoingEdge`](CanDeleteOutgoingEdge.md)\<`TField`\>
+
+Defined in: [src/ent/predicates/CanDeleteOutgoingEdge.ts:15](https://github.com/clickup/ent-framework/blob/master/src/ent/predicates/CanDeleteOutgoingEdge.ts#L15)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `field` | `TField` |
+| `toEntClass` | [`EntClass`](../interfaces/EntClass.md) |
+
+#### Returns
+
+[`CanDeleteOutgoingEdge`](CanDeleteOutgoingEdge.md)\<`TField`\>
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `name` | `string` |
+| `field` | `TField` |
+| `toEntClass` | [`EntClass`](../interfaces/EntClass.md) |
+
+## Methods
+
+### check()
+
+> **check**(`vc`, `row`): `Promise`\<`boolean`\>
+
+Defined in: [src/ent/predicates/CanDeleteOutgoingEdge.ts:22](https://github.com/clickup/ent-framework/blob/master/src/ent/predicates/CanDeleteOutgoingEdge.ts#L22)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `vc` | [`VC`](VC.md) |
+| `row` | `Record`\<`TField`, `null` \| `string`\> |
+
+#### Returns
+
+`Promise`\<`boolean`\>
+
+#### Implementation of
+
+[`Predicate`](../interfaces/Predicate.md).[`check`](../interfaces/Predicate.md#check)
diff --git a/docs/classes/CanReadOutgoingEdge.md b/docs/classes/CanReadOutgoingEdge.md
new file mode 100644
index 0000000..f302fa5
--- /dev/null
+++ b/docs/classes/CanReadOutgoingEdge.md
@@ -0,0 +1,79 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / CanReadOutgoingEdge
+
+# Class: CanReadOutgoingEdge\
+
+Defined in: [src/ent/predicates/CanReadOutgoingEdge.ts:18](https://github.com/clickup/ent-framework/blob/master/src/ent/predicates/CanReadOutgoingEdge.ts#L18)
+
+Checks that an ent which a field is pointing to is readable:
+
+EntOur[company_id] ---> EntCompany[id]
+
+This predicate delegates the readability permission check for the current ent
+to another ent with ID equals to the value of our ent's field.
+
+- field = user_id in the above example
+- toEntClass = EntCompany in the above example
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TField` *extends* `string` |
+
+## Implements
+
+- [`Predicate`](../interfaces/Predicate.md)\<`Record`\<`TField`, `string` \| `null`\>\>
+
+## Constructors
+
+### new CanReadOutgoingEdge()
+
+> **new CanReadOutgoingEdge**\<`TField`\>(`field`, `toEntClass`): [`CanReadOutgoingEdge`](CanReadOutgoingEdge.md)\<`TField`\>
+
+Defined in: [src/ent/predicates/CanReadOutgoingEdge.ts:23](https://github.com/clickup/ent-framework/blob/master/src/ent/predicates/CanReadOutgoingEdge.ts#L23)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `field` | `TField` |
+| `toEntClass` | [`EntClass`](../interfaces/EntClass.md) |
+
+#### Returns
+
+[`CanReadOutgoingEdge`](CanReadOutgoingEdge.md)\<`TField`\>
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `name` | `string` |
+| `field` | `TField` |
+| `toEntClass` | [`EntClass`](../interfaces/EntClass.md) |
+
+## Methods
+
+### check()
+
+> **check**(`vc`, `row`): `Promise`\<`boolean`\>
+
+Defined in: [src/ent/predicates/CanReadOutgoingEdge.ts:30](https://github.com/clickup/ent-framework/blob/master/src/ent/predicates/CanReadOutgoingEdge.ts#L30)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `vc` | [`VC`](VC.md) |
+| `row` | `Record`\<`TField`, `null` \| `string`\> |
+
+#### Returns
+
+`Promise`\<`boolean`\>
+
+#### Implementation of
+
+[`Predicate`](../interfaces/Predicate.md).[`check`](../interfaces/Predicate.md#check)
diff --git a/docs/classes/CanUpdateOutgoingEdge.md b/docs/classes/CanUpdateOutgoingEdge.md
new file mode 100644
index 0000000..5cf14e6
--- /dev/null
+++ b/docs/classes/CanUpdateOutgoingEdge.md
@@ -0,0 +1,72 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / CanUpdateOutgoingEdge
+
+# Class: CanUpdateOutgoingEdge\
+
+Defined in: [src/ent/predicates/CanUpdateOutgoingEdge.ts:10](https://github.com/clickup/ent-framework/blob/master/src/ent/predicates/CanUpdateOutgoingEdge.ts#L10)
+
+Checks that an Ent available via a field is updatable. See
+CanReadOutgoingEdge comments for more details.
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TField` *extends* `string` |
+
+## Implements
+
+- [`Predicate`](../interfaces/Predicate.md)\<`Record`\<`TField`, `string` \| `null`\>\>
+
+## Constructors
+
+### new CanUpdateOutgoingEdge()
+
+> **new CanUpdateOutgoingEdge**\<`TField`\>(`field`, `toEntClass`): [`CanUpdateOutgoingEdge`](CanUpdateOutgoingEdge.md)\<`TField`\>
+
+Defined in: [src/ent/predicates/CanUpdateOutgoingEdge.ts:15](https://github.com/clickup/ent-framework/blob/master/src/ent/predicates/CanUpdateOutgoingEdge.ts#L15)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `field` | `TField` |
+| `toEntClass` | [`EntClass`](../interfaces/EntClass.md) |
+
+#### Returns
+
+[`CanUpdateOutgoingEdge`](CanUpdateOutgoingEdge.md)\<`TField`\>
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `name` | `string` |
+| `field` | `TField` |
+| `toEntClass` | [`EntClass`](../interfaces/EntClass.md) |
+
+## Methods
+
+### check()
+
+> **check**(`vc`, `row`): `Promise`\<`boolean`\>
+
+Defined in: [src/ent/predicates/CanUpdateOutgoingEdge.ts:22](https://github.com/clickup/ent-framework/blob/master/src/ent/predicates/CanUpdateOutgoingEdge.ts#L22)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `vc` | [`VC`](VC.md) |
+| `row` | `Record`\<`TField`, `null` \| `string`\> |
+
+#### Returns
+
+`Promise`\<`boolean`\>
+
+#### Implementation of
+
+[`Predicate`](../interfaces/Predicate.md).[`check`](../interfaces/Predicate.md#check)
diff --git a/docs/classes/Client.md b/docs/classes/Client.md
new file mode 100644
index 0000000..3ee8048
--- /dev/null
+++ b/docs/classes/Client.md
@@ -0,0 +1,273 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / Client
+
+# Class: `abstract` Client
+
+Defined in: [src/abstract/Client.ts:67](https://github.com/clickup/ent-framework/blob/master/src/abstract/Client.ts#L67)
+
+Client is a Shard name aware abstraction which sends an actual query and
+tracks the master/replica timeline. The concrete query sending implementation
+(including required arguments) is up to the derived classes.
+
+## Extended by
+
+- [`PgClient`](PgClient.md)
+
+## Constructors
+
+### new Client()
+
+> **new Client**(`options`): [`Client`](Client.md)
+
+Defined in: [src/abstract/Client.ts:158](https://github.com/clickup/ent-framework/blob/master/src/abstract/Client.ts#L158)
+
+Initializes an instance of Client.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `options` | [`ClientOptions`](../interfaces/ClientOptions.md) |
+
+#### Returns
+
+[`Client`](Client.md)
+
+## Properties
+
+| Property | Type | Description |
+| ------ | ------ | ------ |
+| `DEFAULT_OPTIONS` | `Required`\<`PickPartial`\<[`ClientOptions`](../interfaces/ClientOptions.md)\>\> | Default values for the constructor options. |
+| `options` | `Required`\<[`ClientOptions`](../interfaces/ClientOptions.md)\> | Client configuration options. |
+| `createdAt` | `Date` | Date when this Client instance was constructed. |
+| `shardName` | `string` | Each Client may be bound to some Shard, so the queries executed via it will be namespaced to this Shard. E.g. in relational databases, Shard name may be a namespace (or schema) name (or "public" if the Client wasn't created by withShard() method). |
+| `timelineManager` | [`TimelineManager`](TimelineManager.md) | Tracks the master/replica replication timeline position. Shared across all the Clients within the same Island. |
+
+## Methods
+
+### address()
+
+> `abstract` **address**(): `string`
+
+Defined in: [src/abstract/Client.ts:98](https://github.com/clickup/ent-framework/blob/master/src/abstract/Client.ts#L98)
+
+Represents the full destination address this Client is working with.
+Depending on the implementation, it may include hostname, port number,
+database name, shard name etc. It is required that the address is stable
+enough to be able to cache some destination database related metadata (e.g.
+shardNos) based on that address.
+
+#### Returns
+
+`string`
+
+***
+
+### end()
+
+> `abstract` **end**(): `Promise`\<`void`\>
+
+Defined in: [src/abstract/Client.ts:105](https://github.com/clickup/ent-framework/blob/master/src/abstract/Client.ts#L105)
+
+Gracefully closes the connections to let the caller destroy the Client. The
+pending queries are awaited to finish before returning. The Client becomes
+unusable after calling this method: you should not send queries to it.
+
+#### Returns
+
+`Promise`\<`void`\>
+
+***
+
+### isEnded()
+
+> `abstract` **isEnded**(): `boolean`
+
+Defined in: [src/abstract/Client.ts:110](https://github.com/clickup/ent-framework/blob/master/src/abstract/Client.ts#L110)
+
+Returns true if the Client is ended and can't be used anymore.
+
+#### Returns
+
+`boolean`
+
+***
+
+### shardNos()
+
+> `abstract` **shardNos**(): `Promise`\
+
+Defined in: [src/abstract/Client.ts:116](https://github.com/clickup/ent-framework/blob/master/src/abstract/Client.ts#L116)
+
+Returns all Shard numbers discoverable via the connection to the Client's
+database.
+
+#### Returns
+
+`Promise`\
+
+***
+
+### ping()
+
+> `abstract` **ping**(`input`): `Promise`\<`void`\>
+
+Defined in: [src/abstract/Client.ts:122](https://github.com/clickup/ent-framework/blob/master/src/abstract/Client.ts#L122)
+
+Sends a read or write test query to the server. Tells the server to sit and
+wait for at least the provided number of milliseconds.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `input` | [`ClientPingInput`](../interfaces/ClientPingInput.md) |
+
+#### Returns
+
+`Promise`\<`void`\>
+
+***
+
+### withShard()
+
+> `abstract` **withShard**(`no`): `this`
+
+Defined in: [src/abstract/Client.ts:128](https://github.com/clickup/ent-framework/blob/master/src/abstract/Client.ts#L128)
+
+Creates a new Client which is namespaced to the provided Shard number. The
+new Client will share the same connection pool with the parent's Client.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `no` | `number` |
+
+#### Returns
+
+`this`
+
+***
+
+### role()
+
+> `abstract` **role**(): [`ClientRole`](../type-aliases/ClientRole.md)
+
+Defined in: [src/abstract/Client.ts:135](https://github.com/clickup/ent-framework/blob/master/src/abstract/Client.ts#L135)
+
+Returns the Client's role reported after the last successful query. Master
+and replica roles may switch online unpredictably, without reconnecting, so
+we only know the role after a query.
+
+#### Returns
+
+[`ClientRole`](../type-aliases/ClientRole.md)
+
+***
+
+### connectionIssue()
+
+> `abstract` **connectionIssue**(): `null` \| [`ClientConnectionIssue`](../interfaces/ClientConnectionIssue.md)
+
+Defined in: [src/abstract/Client.ts:143](https://github.com/clickup/ent-framework/blob/master/src/abstract/Client.ts#L143)
+
+Returns a non-nullable value if the Client couldn't connect to the server
+(or it could, but the load balancer reported the remote server as not
+working), so it should ideally be removed from the list of active replicas
+until e.g. the next discovery query to it (or any query) succeeds.
+
+#### Returns
+
+`null` \| [`ClientConnectionIssue`](../interfaces/ClientConnectionIssue.md)
+
+***
+
+### logSwallowedError()
+
+> `protected` **logSwallowedError**(`props`): `void`
+
+Defined in: [src/abstract/Client.ts:148](https://github.com/clickup/ent-framework/blob/master/src/abstract/Client.ts#L148)
+
+Calls swallowedErrorLogger() doing some preliminary amendment.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `props` | [`SwallowedErrorLoggerProps`](../interfaces/SwallowedErrorLoggerProps.md) |
+
+#### Returns
+
+`void`
+
+***
+
+### batcher()
+
+> **batcher**\<`TInput`, `TOutput`, `TTable`\>(`_QueryClass`, `_schema`, `_additionalShape`, `disableBatching`, `runnerCreator`): [`Batcher`](Batcher.md)\<`TInput`, `TOutput`\>
+
+Defined in: [src/abstract/Client.ts:189](https://github.com/clickup/ent-framework/blob/master/src/abstract/Client.ts#L189)
+
+Batcher is per-Client per-query-type
+per-table-name-and-shape-and-disableBatching:
+
+- Per-Client means that batchers are removed as soon as the Client is
+ removed, i.e. the Client owns all the batchers for all tables.
+- Per-query-type means that the batcher for a SELECT query is different
+ from the batcher for an INSERT query (obviously).
+- Per-table-name-and-shape-and-disableBatching means that each table has
+ its own set of batchers (obviously). Also, some queries may be complex
+ (like UPDATE), so the batcher also depends on the "shape" - the list of
+ fields we're updating. Plus, for some inputs, we want to disable batching
+ at all - that produces a separate Batcher instance.
+
+Also, for every Batcher, there is exactly one Runner (which knows how to
+build the actual query in the context of the current Client). Batchers are
+generic (like DataLoader, but more general), and Runners are very custom to
+the query (and are private to these queries).
+
+All that means that in a 1000-Shard 20-table Cluster we'll eventually have
+1000x20x8 Batchers/Runners (assuming we have 8 different operations).
+
+#### Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TInput` |
+| `TOutput` |
+| `TTable` *extends* [`Table`](../type-aliases/Table.md) |
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `_QueryClass` | `Function` |
+| `_schema` | [`Schema`](Schema.md)\<`TTable`, [`UniqueKey`](../type-aliases/UniqueKey.md)\<`TTable`\>\> |
+| `_additionalShape` | `string` |
+| `disableBatching` | `boolean` |
+| `runnerCreator` | () => [`Runner`](Runner.md)\<`TInput`, `TOutput`\> |
+
+#### Returns
+
+[`Batcher`](Batcher.md)\<`TInput`, `TOutput`\>
+
+***
+
+### prewarm()
+
+> **prewarm**(): `void`
+
+Defined in: [src/abstract/Client.ts:214](https://github.com/clickup/ent-framework/blob/master/src/abstract/Client.ts#L214)
+
+A convenience method to put connections prewarming logic to. The idea is to
+keep the needed number of open connections and also, in each connection,
+minimize the time which the very 1st query will take (e.g. pre-cache
+full-text dictionaries).
+
+#### Returns
+
+`void`
diff --git a/docs/classes/ClientError.md b/docs/classes/ClientError.md
new file mode 100644
index 0000000..a4f48f4
--- /dev/null
+++ b/docs/classes/ClientError.md
@@ -0,0 +1,57 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / ClientError
+
+# Class: ClientError
+
+Defined in: [src/abstract/ClientError.ts:43](https://github.com/clickup/ent-framework/blob/master/src/abstract/ClientError.ts#L43)
+
+Encapsulates the error thrown when running a Client query. The object also
+carries suggestions, what to do next.
+
+## Extends
+
+- `Error`
+
+## Extended by
+
+- [`PgError`](PgError.md)
+
+## Constructors
+
+### new ClientError()
+
+> **new ClientError**(`cause`, `where`, `postAction`, `kind`, `abbreviation`, `comment`?): [`ClientError`](ClientError.md)
+
+Defined in: [src/abstract/ClientError.ts:44](https://github.com/clickup/ent-framework/blob/master/src/abstract/ClientError.ts#L44)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `cause` | `MaybeError` |
+| `where` | `string` |
+| `postAction` | [`ClientErrorPostAction`](../type-aliases/ClientErrorPostAction.md) |
+| `kind` | [`ClientErrorKind`](../type-aliases/ClientErrorKind.md) |
+| `abbreviation` | `string` |
+| `comment`? | `string` |
+
+#### Returns
+
+[`ClientError`](ClientError.md)
+
+#### Overrides
+
+`Error.constructor`
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `cause` | `MaybeError` |
+| `postAction` | [`ClientErrorPostAction`](../type-aliases/ClientErrorPostAction.md) |
+| `kind` | [`ClientErrorKind`](../type-aliases/ClientErrorKind.md) |
+| `abbreviation` | `string` |
+| `comment?` | `string` |
diff --git a/docs/classes/Cluster.md b/docs/classes/Cluster.md
new file mode 100644
index 0000000..7437319
--- /dev/null
+++ b/docs/classes/Cluster.md
@@ -0,0 +1,246 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / Cluster
+
+# Class: Cluster\
+
+Defined in: [src/abstract/Cluster.ts:129](https://github.com/clickup/ent-framework/blob/master/src/abstract/Cluster.ts#L129)
+
+Cluster is a collection of Islands and an orchestration of shardNo -> Island
+resolution.
+
+It's unknown beforehand, which Island some particular Shard belongs to; the
+resolution is done asynchronously and lazily.
+
+Shard 0 is a special "global" Shard.
+
+## Type Parameters
+
+| Type Parameter | Default type |
+| ------ | ------ |
+| `TClient` *extends* [`Client`](Client.md) | - |
+| `TNode` | `DesperateAny` |
+
+## Constructors
+
+### new Cluster()
+
+> **new Cluster**\<`TClient`, `TNode`\>(`options`): [`Cluster`](Cluster.md)\<`TClient`, `TNode`\>
+
+Defined in: [src/abstract/Cluster.ts:172](https://github.com/clickup/ent-framework/blob/master/src/abstract/Cluster.ts#L172)
+
+Initializes the Cluster, but doesn't send any queries yet, even discovery
+queries (also, no implicit prewarming).
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `options` | [`ClusterOptions`](../interfaces/ClusterOptions.md)\<`TClient`, `TNode`\> |
+
+#### Returns
+
+[`Cluster`](Cluster.md)\<`TClient`, `TNode`\>
+
+## Properties
+
+| Property | Type | Description |
+| ------ | ------ | ------ |
+| `DEFAULT_OPTIONS` | `Required`\<`PickPartial`\<[`ClusterOptions`](../interfaces/ClusterOptions.md)\<[`Client`](Client.md), `never`\>\>\> | Default values for the constructor options. |
+| `options` | `Required`\<[`ClusterOptions`](../interfaces/ClusterOptions.md)\<`TClient`, `TNode`\>\> | Cluster configuration options. |
+
+## Methods
+
+### prewarm()
+
+> **prewarm**(`randomizedDelayMs`, `onInitialPrewarm`?): `void`
+
+Defined in: [src/abstract/Cluster.ts:303](https://github.com/clickup/ent-framework/blob/master/src/abstract/Cluster.ts#L303)
+
+Signals the Cluster to keep the Clients pre-warmed, e.g. open. (It's up to
+the particular Client's implementation, what does a "pre-warmed Client"
+mean; typically, it's keeping some minimal number of pooled connections.)
+
+Except when `randomizedDelayMs` is passed as 0, the actual prewarm (and
+Islands discovery) queries will run with a randomized delay between N/2 and
+N ms. It is better to operate in such mode: if multiple Node processes
+start simultaneously in the cluster, then the randomization helps to avoid
+new connections burst (new connections establishment is expensive for e.g.
+pgbouncer or when DB is accessed over SSL).
+
+#### Parameters
+
+| Parameter | Type | Default value |
+| ------ | ------ | ------ |
+| `randomizedDelayMs` | `number` | `5000` |
+| `onInitialPrewarm`? | (`delayMs`) => `void` | `undefined` |
+
+#### Returns
+
+`void`
+
+***
+
+### globalShard()
+
+> **globalShard**(): [`Shard`](Shard.md)\<`TClient`\>
+
+Defined in: [src/abstract/Cluster.ts:334](https://github.com/clickup/ent-framework/blob/master/src/abstract/Cluster.ts#L334)
+
+Returns a global Shard of the Cluster. This method is made synchronous
+intentionally, to defer the I/O and possible errors to the moment of the
+actual query.
+
+#### Returns
+
+[`Shard`](Shard.md)\<`TClient`\>
+
+***
+
+### nonGlobalShards()
+
+> **nonGlobalShards**(): `Promise`\[]\>
+
+Defined in: [src/abstract/Cluster.ts:341](https://github.com/clickup/ent-framework/blob/master/src/abstract/Cluster.ts#L341)
+
+Returns all currently known (discovered) non-global Shards in the Cluster.
+
+#### Returns
+
+`Promise`\[]\>
+
+***
+
+### shard()
+
+> **shard**(`id`): [`Shard`](Shard.md)\<`TClient`\>
+
+Defined in: [src/abstract/Cluster.ts:361](https://github.com/clickup/ent-framework/blob/master/src/abstract/Cluster.ts#L361)
+
+Returns Shard of a particular id. This method is made synchronous
+intentionally, to defer the I/O and possible errors to the moment of the
+actual query.
+
+Why is it important? Because Shards may go up and down temporarily at
+random moments of time. Imagine we made this method async and asserted that
+the Shard is actually available at the moment when the method is called.
+What would happen if the Shard object was stored somewhere as "successful"
+by the caller, then the Island went down, and then a query is sent to the
+Shard in, say, 20 seconds? We'd get an absolutely different exception, at
+the moment of the query. We don't want this to happen: we want all of the
+exceptions to be thrown with a consistent call stack (e.g. at the moment of
+the query), no matter whether it was an immediate call or a deferred one.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `id` | `string` |
+
+#### Returns
+
+[`Shard`](Shard.md)\<`TClient`\>
+
+***
+
+### shardByNo()
+
+> **shardByNo**(`shardNo`): [`Shard`](Shard.md)\<`TClient`\>
+
+Defined in: [src/abstract/Cluster.ts:376](https://github.com/clickup/ent-framework/blob/master/src/abstract/Cluster.ts#L376)
+
+Returns a Shard if we know its number. The idea: for each Shard number
+(even for non-discovered yet Shards), we keep the corresponding Shard
+object in a Memoize cache, so Shards with the same number always resolve
+into the same Shard object. Then, an actual Island locating process happens
+when the caller wants to get a Client of that Shard (and it throws if such
+Shard hasn't been discovered actually).
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `shardNo` | `number` |
+
+#### Returns
+
+[`Shard`](Shard.md)\<`TClient`\>
+
+***
+
+### randomShard()
+
+> **randomShard**(`seed`?): `Promise`\<[`Shard`](Shard.md)\<`TClient`\>\>
+
+Defined in: [src/abstract/Cluster.ts:384](https://github.com/clickup/ent-framework/blob/master/src/abstract/Cluster.ts#L384)
+
+Returns a random Shard among the ones which are currently known
+(discovered) in the Cluster.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `seed`? | `object` |
+
+#### Returns
+
+`Promise`\<[`Shard`](Shard.md)\<`TClient`\>\>
+
+***
+
+### island()
+
+> **island**(`islandNo`): `Promise`\<[`Island`](Island.md)\<`TClient`\>\>
+
+Defined in: [src/abstract/Cluster.ts:408](https://github.com/clickup/ent-framework/blob/master/src/abstract/Cluster.ts#L408)
+
+Returns an Island by its number.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `islandNo` | `number` |
+
+#### Returns
+
+`Promise`\<[`Island`](Island.md)\<`TClient`\>\>
+
+***
+
+### islands()
+
+> **islands**(): `Promise`\<[`Island`](Island.md)\<`TClient`\>[]\>
+
+Defined in: [src/abstract/Cluster.ts:419](https://github.com/clickup/ent-framework/blob/master/src/abstract/Cluster.ts#L419)
+
+Returns all Islands in the Cluster.
+
+#### Returns
+
+`Promise`\<[`Island`](Island.md)\<`TClient`\>[]\>
+
+***
+
+### rediscover()
+
+> **rediscover**(`what`?): `Promise`\<`void`\>
+
+Defined in: [src/abstract/Cluster.ts:429](https://github.com/clickup/ent-framework/blob/master/src/abstract/Cluster.ts#L429)
+
+Triggers shards rediscovery and finishes as soon as it's done. To be used
+in unit tests mostly, because in real life, it's enough to just modify the
+cluster configuration.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `what`? | `"islands"` \| `"shards"` |
+
+#### Returns
+
+`Promise`\<`void`\>
diff --git a/docs/classes/Configuration.md b/docs/classes/Configuration.md
new file mode 100644
index 0000000..f49d6ad
--- /dev/null
+++ b/docs/classes/Configuration.md
@@ -0,0 +1,70 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / Configuration
+
+# Class: Configuration\
+
+Defined in: [src/ent/Configuration.ts:37](https://github.com/clickup/ent-framework/blob/master/src/ent/Configuration.ts#L37)
+
+Strongly typed configuration framework to force TS auto-infer privacy
+callbacks arguments types (which are not Ents, but row-like inputs).
+
+Motivation:
+1. We MUST resolve privacyXyz rules below lazily, at actual operation;
+ otherwise in case of cyclic Ent dependencies between EntA and EntB, one of
+ them will be magically undefined.
+2. We can’t define these parameter as BaseEnt arguments: privacy rules may
+ refer the derived Ent itself and other Ents and thus produce cyclic
+ dependencies. TS doesn't allow to work with such cyclic dependencies
+ during the class is defining.
+3. Configuration can’t be just returned from a virtual method, because in TS,
+ type inference in return values is poor:
+ https://github.com/Microsoft/TypeScript/issues/31273
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TTable` *extends* [`Table`](../type-aliases/Table.md) |
+
+## Constructors
+
+### new Configuration()
+
+> **new Configuration**\<`TTable`\>(`cfg`): [`Configuration`](Configuration.md)\<`TTable`\>
+
+Defined in: [src/ent/Configuration.ts:141](https://github.com/clickup/ent-framework/blob/master/src/ent/Configuration.ts#L141)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `cfg` | [`Configuration`](Configuration.md)\<`TTable`\> |
+
+#### Returns
+
+[`Configuration`](Configuration.md)\<`TTable`\>
+
+## Properties
+
+| Property | Type | Description |
+| ------ | ------ | ------ |
+| `shardAffinity` | [`ShardAffinity`](../type-aliases/ShardAffinity.md)\<[`FieldOfIDType`](../type-aliases/FieldOfIDType.md)\<`TTable`\>\> | Defines how to locate a Shard at Ent insert time. See ShardAffinity for more details. 1. GLOBAL_SHARD: places the Ent in the global Shard (0). 2. `[]`: places the Ent in a random Shard. The "randomness" of the "random Shard" is deterministic by the Ent's unique key at the moment of insertion (if it's defined; otherwise completely random). This helps two racy insert operations running concurrently to choose the same Shard for the Ent to be created in, so only one of them will win, instead of both winning and mistakenly creating the Ent duplicates. I.e. having the same value in unique key forces the engine to target the same "random" Shard. 3. `["field1", "field2", ...]`: places the Ent in the Shard that is pointed to by the value in field1 (if it's null, then field2 etc.). A special treatment is applied if a fieldN value in (3) points to the global Shard. In such a case, the Shard for the current Ent is chosen deterministic-randomly at insert time, as if [] is passed. This allows the Ent to refer other "owning" Ents of different types, some of which may be located in the global Shard. Keep in mind that, to locate such an Ent pointing to another Ent in the global Shard, an inverse for fieldN must be defined in most of the cases. |
+| `inverses?` | `{ [k in string]?: { name: string; type: string } }` | Inverses allow cross-Shard foreign keys & cross-Shard selection. If a field points to an Ent in another Shard, and we're e.g. selecting by a value in this field, inverses allow to locate Shard(s) of the Ent. |
+| `privacyTenantPrincipalField?` | [`InsertFieldsRequired`](../type-aliases/InsertFieldsRequired.md)\<`TTable`\> & `string` | If defined, forces all Ents of this class to have the value of that field equal to VC's principal at load time. This is a very 1st unavoidable check in the privacy rules chain, thus it's bullet-proof. |
+| `privacyInferPrincipal` | `null` \| `string` \| (`vc`, `row`) => `null` \| `string` \| `Promise`\<`null` \| `string` \| [`Ent`](../interfaces/Ent.md)\<\{\}\>\> | An attempt to load this Ent using an omni VC will "lower" that VC to the principal returned. Omni VC is always lowered. 1. If an Ent is returned, the lowered principal will be Ent#vc.principal. It is a way to delegate principal inference to another Ent. 2. If a string is returned, then it's treated as a principal ID. 3. If a null is returned, then a guest principal will be used. 4. Returning an omni principal or VC will result in a run-time error. |
+| `privacyLoad` | [`LoadRule`](../type-aliases/LoadRule.md)\<[`Row`](../type-aliases/Row.md)\<`TTable`\>\>[] | Privacy rules checked on every row loaded from the DB. |
+| `privacyInsert` | [`WriteRules`](../type-aliases/WriteRules.md)\<[`InsertInput`](../type-aliases/InsertInput.md)\<`TTable`\>\> | Privacy rules checked before a row is inserted to the DB. - It the list is empty, then only omni VC can insert; it's typically a good option for Ents representing e.g. a user. - If no update/delete rules are defined, then privacyInsert rules are also run on update/delete by default. - Unless empty, the rules must include at least one Require() predicate, they can't entirely consist of AllowIf(). This is because for write rules (privacyInsert, privacyUpdate, privacyDelete) it's important to make sure that ALL rules permit the operation, not only one of them allows it; this is what Require() is exactly for. |
+| `privacyUpdate?` | [`WriteRules`](../type-aliases/WriteRules.md)\<[`Row`](../type-aliases/Row.md)\<`TTable`\>\> | Privacy rules checked before a row is updated in the DB. - If not defined, privacyInsert rules are used. - The rules must include at least one Require() predicate. |
+| `privacyDelete?` | [`WriteRules`](../type-aliases/WriteRules.md)\<[`Row`](../type-aliases/Row.md)\<`TTable`\>\> | Privacy rules checked before a row is deleted in the DB. - If not defined, privacyInsert rules are used. - The rules must include at least one Require() predicate. |
+| `validators?` | [`AbstractIs`](../interfaces/AbstractIs.md)\<[`InsertInput`](../type-aliases/InsertInput.md)\<`TTable`\>\>[] | Custom field values validators run before any insert/update. |
+| `beforeInsert?` | [`InsertTrigger`](../type-aliases/InsertTrigger.md)\<`TTable`\>[] | Triggers run before every insert. |
+| `beforeUpdate?` | ([`BeforeUpdateTrigger`](../type-aliases/BeforeUpdateTrigger.md)\<`TTable`\> \| \[[`DepsBuilder`](../type-aliases/DepsBuilder.md)\<`TTable`\>, [`BeforeUpdateTrigger`](../type-aliases/BeforeUpdateTrigger.md)\<`TTable`\>\])[] | Triggers run before every update. |
+| `beforeDelete?` | [`DeleteTrigger`](../type-aliases/DeleteTrigger.md)\<`TTable`\>[] | Triggers run before every delete. |
+| `beforeMutation?` | ([`BeforeMutationTrigger`](../type-aliases/BeforeMutationTrigger.md)\<`TTable`\> \| \[[`DepsBuilder`](../type-aliases/DepsBuilder.md)\<`TTable`\>, [`BeforeMutationTrigger`](../type-aliases/BeforeMutationTrigger.md)\<`TTable`\>\])[] | Triggers run before every insert/update/delete. Each trigger may also be passed as "React useEffect-like" tuple where the callback is executed only if the deps are modified. |
+| `afterInsert?` | [`InsertTrigger`](../type-aliases/InsertTrigger.md)\<`TTable`\>[] | Triggers run after every delete. |
+| `afterUpdate?` | ([`AfterUpdateTrigger`](../type-aliases/AfterUpdateTrigger.md)\<`TTable`\> \| \[[`DepsBuilder`](../type-aliases/DepsBuilder.md)\<`TTable`\>, [`AfterUpdateTrigger`](../type-aliases/AfterUpdateTrigger.md)\<`TTable`\>\])[] | Triggers run after every update. |
+| `afterDelete?` | [`DeleteTrigger`](../type-aliases/DeleteTrigger.md)\<`TTable`\>[] | Triggers run after every delete. |
+| `afterMutation?` | ([`AfterMutationTrigger`](../type-aliases/AfterMutationTrigger.md)\<`TTable`\> \| \[[`DepsBuilder`](../type-aliases/DepsBuilder.md)\<`TTable`\>, [`AfterMutationTrigger`](../type-aliases/AfterMutationTrigger.md)\<`TTable`\>\])[] | Triggers run after every insert/update/delete. Each trigger may also be passed as "React useEffect-like" tuple where the callback is executed only if the deps are modified. |
diff --git a/docs/classes/DenyIf.md b/docs/classes/DenyIf.md
new file mode 100644
index 0000000..fb34919
--- /dev/null
+++ b/docs/classes/DenyIf.md
@@ -0,0 +1,79 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / DenyIf
+
+# Class: DenyIf\
+
+Defined in: [src/ent/rules/DenyIf.ts:14](https://github.com/clickup/ent-framework/blob/master/src/ent/rules/DenyIf.ts#L14)
+
+Returns DENY if the predicate succeeds, otherwise SKIP.
+- Used mostly to early block some read/write access.
+- EntAccessError exception will be treated as a DENY signal (so it will abort
+ processing immediately).
+- This rule may still throw an exception if the exception is a wild one (not
+ derived from EntAccessError).
+
+## Extends
+
+- [`Rule`](Rule.md)\<`TInput`\>
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TInput` *extends* `object` |
+
+## Constructors
+
+### new DenyIf()
+
+> **new DenyIf**\<`TInput`\>(`predicate`): [`DenyIf`](DenyIf.md)\<`TInput`\>
+
+Defined in: [src/ent/rules/Rule.ts:43](https://github.com/clickup/ent-framework/blob/master/src/ent/rules/Rule.ts#L43)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `predicate` | [`Predicate`](../interfaces/Predicate.md)\<`TInput`\> \| (`vc`, `input`) => `boolean` \| `Promise`\<`boolean`\> |
+
+#### Returns
+
+[`DenyIf`](DenyIf.md)\<`TInput`\>
+
+#### Inherited from
+
+[`Rule`](Rule.md).[`constructor`](Rule.md#constructors)
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `_TAG` | `"DenyIf"` |
+| `predicate` | [`Predicate`](../interfaces/Predicate.md)\<`TInput`\> |
+| `name` | `string` |
+
+## Methods
+
+### evaluate()
+
+> **evaluate**(`vc`, `input`): `Promise`\<[`RuleResult`](../interfaces/RuleResult.md)\>
+
+Defined in: [src/ent/rules/DenyIf.ts:17](https://github.com/clickup/ent-framework/blob/master/src/ent/rules/DenyIf.ts#L17)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `vc` | [`VC`](VC.md) |
+| `input` | `TInput` |
+
+#### Returns
+
+`Promise`\<[`RuleResult`](../interfaces/RuleResult.md)\>
+
+#### Overrides
+
+[`Rule`](Rule.md).[`evaluate`](Rule.md#evaluate)
diff --git a/docs/classes/EntAccessError.md b/docs/classes/EntAccessError.md
new file mode 100644
index 0000000..6883ab3
--- /dev/null
+++ b/docs/classes/EntAccessError.md
@@ -0,0 +1,71 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / EntAccessError
+
+# Class: EntAccessError
+
+Defined in: [src/ent/errors/EntAccessError.ts:22](https://github.com/clickup/ent-framework/blob/master/src/ent/errors/EntAccessError.ts#L22)
+
+A base class for errors that trigger the validation framework to process them
+as a DENY/SKIP. Invariants in derived classes: the error message should be
+safe to pass to the client (it must not have any private information; a good
+example is EntValidationError), plus the message alone should be descriptive
+enough to extract information from it. If `cause` is passed, it becomes a
+part of the message, with the above assumptions.
+
+## Extends
+
+- `Error`
+
+## Extended by
+
+- [`EntNotFoundError`](EntNotFoundError.md)
+- [`EntNotInsertableError`](EntNotInsertableError.md)
+- [`EntNotReadableError`](EntNotReadableError.md)
+- [`EntNotUpdatableError`](EntNotUpdatableError.md)
+- [`EntValidationError`](EntValidationError.md)
+
+## Constructors
+
+### new EntAccessError()
+
+> **new EntAccessError**(`entName`, `message`, `cause`): [`EntAccessError`](EntAccessError.md)
+
+Defined in: [src/ent/errors/EntAccessError.ts:25](https://github.com/clickup/ent-framework/blob/master/src/ent/errors/EntAccessError.ts#L25)
+
+#### Parameters
+
+| Parameter | Type | Default value |
+| ------ | ------ | ------ |
+| `entName` | `string` | `undefined` |
+| `message` | `string` | `undefined` |
+| `cause` | `unknown` | `null` |
+
+#### Returns
+
+[`EntAccessError`](EntAccessError.md)
+
+#### Overrides
+
+`Error.constructor`
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `cause` | `null` \| `string` \| `Error` |
+| `entName` | `string` |
+
+## Methods
+
+### toStandardSchemaV1()
+
+> **toStandardSchemaV1**(): [`StandardSchemaV1FailureResult`](../interfaces/StandardSchemaV1FailureResult.md)
+
+Defined in: [src/ent/errors/EntAccessError.ts:52](https://github.com/clickup/ent-framework/blob/master/src/ent/errors/EntAccessError.ts#L52)
+
+#### Returns
+
+[`StandardSchemaV1FailureResult`](../interfaces/StandardSchemaV1FailureResult.md)
diff --git a/docs/classes/EntNotFoundError.md b/docs/classes/EntNotFoundError.md
new file mode 100644
index 0000000..0ad01b8
--- /dev/null
+++ b/docs/classes/EntNotFoundError.md
@@ -0,0 +1,65 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / EntNotFoundError
+
+# Class: EntNotFoundError
+
+Defined in: [src/ent/errors/EntNotFoundError.ts:9](https://github.com/clickup/ent-framework/blob/master/src/ent/errors/EntNotFoundError.ts#L9)
+
+Error: non-existing ID in the database (failed loadX() call), or non-existing
+Ent (failed loadByX() call). Notice that `where` data is intentionally NOT
+considered as private and may be delivered to the client.
+
+## Extends
+
+- [`EntAccessError`](EntAccessError.md)
+
+## Constructors
+
+### new EntNotFoundError()
+
+> **new EntNotFoundError**(`entName`, `where`, `cause`): [`EntNotFoundError`](EntNotFoundError.md)
+
+Defined in: [src/ent/errors/EntNotFoundError.ts:10](https://github.com/clickup/ent-framework/blob/master/src/ent/errors/EntNotFoundError.ts#L10)
+
+#### Parameters
+
+| Parameter | Type | Default value |
+| ------ | ------ | ------ |
+| `entName` | `string` | `undefined` |
+| `where` | `Record`\<`string`, `unknown`\> | `undefined` |
+| `cause` | `unknown` | `null` |
+
+#### Returns
+
+[`EntNotFoundError`](EntNotFoundError.md)
+
+#### Overrides
+
+[`EntAccessError`](EntAccessError.md).[`constructor`](EntAccessError.md#constructors)
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `cause` | `null` \| `string` \| `Error` |
+| `entName` | `string` |
+| `where` | `Record`\<`string`, `unknown`\> |
+
+## Methods
+
+### toStandardSchemaV1()
+
+> **toStandardSchemaV1**(): [`StandardSchemaV1FailureResult`](../interfaces/StandardSchemaV1FailureResult.md)
+
+Defined in: [src/ent/errors/EntAccessError.ts:52](https://github.com/clickup/ent-framework/blob/master/src/ent/errors/EntAccessError.ts#L52)
+
+#### Returns
+
+[`StandardSchemaV1FailureResult`](../interfaces/StandardSchemaV1FailureResult.md)
+
+#### Inherited from
+
+[`EntAccessError`](EntAccessError.md).[`toStandardSchemaV1`](EntAccessError.md#tostandardschemav1)
diff --git a/docs/classes/EntNotInsertableError.md b/docs/classes/EntNotInsertableError.md
new file mode 100644
index 0000000..d2ef432
--- /dev/null
+++ b/docs/classes/EntNotInsertableError.md
@@ -0,0 +1,65 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / EntNotInsertableError
+
+# Class: EntNotInsertableError
+
+Defined in: [src/ent/errors/EntNotInsertableError.ts:6](https://github.com/clickup/ent-framework/blob/master/src/ent/errors/EntNotInsertableError.ts#L6)
+
+Error: thrown when an Ent cannot be inserted due to privacy reasons.
+
+## Extends
+
+- [`EntAccessError`](EntAccessError.md)
+
+## Constructors
+
+### new EntNotInsertableError()
+
+> **new EntNotInsertableError**(`entName`, `vc`, `row`, `cause`): [`EntNotInsertableError`](EntNotInsertableError.md)
+
+Defined in: [src/ent/errors/EntNotInsertableError.ts:7](https://github.com/clickup/ent-framework/blob/master/src/ent/errors/EntNotInsertableError.ts#L7)
+
+#### Parameters
+
+| Parameter | Type | Default value |
+| ------ | ------ | ------ |
+| `entName` | `string` | `undefined` |
+| `vc` | `string` | `undefined` |
+| `row` | `object` | `undefined` |
+| `cause` | `unknown` | `null` |
+
+#### Returns
+
+[`EntNotInsertableError`](EntNotInsertableError.md)
+
+#### Overrides
+
+[`EntAccessError`](EntAccessError.md).[`constructor`](EntAccessError.md#constructors)
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `cause` | `null` \| `string` \| `Error` |
+| `entName` | `string` |
+| `vc` | `string` |
+| `row` | `object` |
+
+## Methods
+
+### toStandardSchemaV1()
+
+> **toStandardSchemaV1**(): [`StandardSchemaV1FailureResult`](../interfaces/StandardSchemaV1FailureResult.md)
+
+Defined in: [src/ent/errors/EntAccessError.ts:52](https://github.com/clickup/ent-framework/blob/master/src/ent/errors/EntAccessError.ts#L52)
+
+#### Returns
+
+[`StandardSchemaV1FailureResult`](../interfaces/StandardSchemaV1FailureResult.md)
+
+#### Inherited from
+
+[`EntAccessError`](EntAccessError.md).[`toStandardSchemaV1`](EntAccessError.md#tostandardschemav1)
diff --git a/docs/classes/EntNotReadableError.md b/docs/classes/EntNotReadableError.md
new file mode 100644
index 0000000..43c6e21
--- /dev/null
+++ b/docs/classes/EntNotReadableError.md
@@ -0,0 +1,65 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / EntNotReadableError
+
+# Class: EntNotReadableError
+
+Defined in: [src/ent/errors/EntNotReadableError.ts:8](https://github.com/clickup/ent-framework/blob/master/src/ent/errors/EntNotReadableError.ts#L8)
+
+Error: thrown when an Ent cannot be read due to privacy reasons.
+
+## Extends
+
+- [`EntAccessError`](EntAccessError.md)
+
+## Constructors
+
+### new EntNotReadableError()
+
+> **new EntNotReadableError**(`entName`, `vc`, `row`, `cause`): [`EntNotReadableError`](EntNotReadableError.md)
+
+Defined in: [src/ent/errors/EntNotReadableError.ts:9](https://github.com/clickup/ent-framework/blob/master/src/ent/errors/EntNotReadableError.ts#L9)
+
+#### Parameters
+
+| Parameter | Type | Default value |
+| ------ | ------ | ------ |
+| `entName` | `string` | `undefined` |
+| `vc` | `string` | `undefined` |
+| `row` | [`RowWithID`](../type-aliases/RowWithID.md) | `undefined` |
+| `cause` | `unknown` | `null` |
+
+#### Returns
+
+[`EntNotReadableError`](EntNotReadableError.md)
+
+#### Overrides
+
+[`EntAccessError`](EntAccessError.md).[`constructor`](EntAccessError.md#constructors)
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `cause` | `null` \| `string` \| `Error` |
+| `entName` | `string` |
+| `vc` | `string` |
+| `row` | [`RowWithID`](../type-aliases/RowWithID.md) |
+
+## Methods
+
+### toStandardSchemaV1()
+
+> **toStandardSchemaV1**(): [`StandardSchemaV1FailureResult`](../interfaces/StandardSchemaV1FailureResult.md)
+
+Defined in: [src/ent/errors/EntAccessError.ts:52](https://github.com/clickup/ent-framework/blob/master/src/ent/errors/EntAccessError.ts#L52)
+
+#### Returns
+
+[`StandardSchemaV1FailureResult`](../interfaces/StandardSchemaV1FailureResult.md)
+
+#### Inherited from
+
+[`EntAccessError`](EntAccessError.md).[`toStandardSchemaV1`](EntAccessError.md#tostandardschemav1)
diff --git a/docs/classes/EntNotUpdatableError.md b/docs/classes/EntNotUpdatableError.md
new file mode 100644
index 0000000..4a69659
--- /dev/null
+++ b/docs/classes/EntNotUpdatableError.md
@@ -0,0 +1,65 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / EntNotUpdatableError
+
+# Class: EntNotUpdatableError
+
+Defined in: [src/ent/errors/EntNotUpdatableError.ts:8](https://github.com/clickup/ent-framework/blob/master/src/ent/errors/EntNotUpdatableError.ts#L8)
+
+Error: thrown when an Ent cannot be updated or deleted due to privacy reasons.
+
+## Extends
+
+- [`EntAccessError`](EntAccessError.md)
+
+## Constructors
+
+### new EntNotUpdatableError()
+
+> **new EntNotUpdatableError**(`entName`, `vc`, `row`, `cause`): [`EntNotUpdatableError`](EntNotUpdatableError.md)
+
+Defined in: [src/ent/errors/EntNotUpdatableError.ts:9](https://github.com/clickup/ent-framework/blob/master/src/ent/errors/EntNotUpdatableError.ts#L9)
+
+#### Parameters
+
+| Parameter | Type | Default value |
+| ------ | ------ | ------ |
+| `entName` | `string` | `undefined` |
+| `vc` | `string` | `undefined` |
+| `row` | [`RowWithID`](../type-aliases/RowWithID.md) | `undefined` |
+| `cause` | `unknown` | `null` |
+
+#### Returns
+
+[`EntNotUpdatableError`](EntNotUpdatableError.md)
+
+#### Overrides
+
+[`EntAccessError`](EntAccessError.md).[`constructor`](EntAccessError.md#constructors)
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `cause` | `null` \| `string` \| `Error` |
+| `entName` | `string` |
+| `vc` | `string` |
+| `row` | [`RowWithID`](../type-aliases/RowWithID.md) |
+
+## Methods
+
+### toStandardSchemaV1()
+
+> **toStandardSchemaV1**(): [`StandardSchemaV1FailureResult`](../interfaces/StandardSchemaV1FailureResult.md)
+
+Defined in: [src/ent/errors/EntAccessError.ts:52](https://github.com/clickup/ent-framework/blob/master/src/ent/errors/EntAccessError.ts#L52)
+
+#### Returns
+
+[`StandardSchemaV1FailureResult`](../interfaces/StandardSchemaV1FailureResult.md)
+
+#### Inherited from
+
+[`EntAccessError`](EntAccessError.md).[`toStandardSchemaV1`](EntAccessError.md#tostandardschemav1)
diff --git a/docs/classes/EntUniqueKeyError.md b/docs/classes/EntUniqueKeyError.md
new file mode 100644
index 0000000..dc4e346
--- /dev/null
+++ b/docs/classes/EntUniqueKeyError.md
@@ -0,0 +1,73 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / EntUniqueKeyError
+
+# Class: EntUniqueKeyError
+
+Defined in: [src/ent/errors/EntUniqueKeyError.ts:7](https://github.com/clickup/ent-framework/blob/master/src/ent/errors/EntUniqueKeyError.ts#L7)
+
+Error: while inserting or updating, DB unique key was violated,
+so the Ent was not mutated.
+
+## Extends
+
+- `Error`
+
+## Constructors
+
+### new EntUniqueKeyError()
+
+> **new EntUniqueKeyError**(`entName`, `input`): [`EntUniqueKeyError`](EntUniqueKeyError.md)
+
+Defined in: [src/ent/errors/EntUniqueKeyError.ts:8](https://github.com/clickup/ent-framework/blob/master/src/ent/errors/EntUniqueKeyError.ts#L8)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `entName` | `string` |
+| `input` | `unknown` |
+
+#### Returns
+
+[`EntUniqueKeyError`](EntUniqueKeyError.md)
+
+#### Overrides
+
+`Error.constructor`
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `entName` | `string` |
+| `input` | `unknown` |
+
+## Methods
+
+### ignore()
+
+> `static` **ignore**\<`T`\>(`promise`): `Promise`\<`undefined` \| `T`\>
+
+Defined in: [src/ent/errors/EntUniqueKeyError.ts:28](https://github.com/clickup/ent-framework/blob/master/src/ent/errors/EntUniqueKeyError.ts#L28)
+
+Returns a promise of T on success, and undefined in case unique key
+violation happened during the promise resolution.
+
+#### Type Parameters
+
+| Type Parameter |
+| ------ |
+| `T` |
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `promise` | `Promise`\<`T`\> |
+
+#### Returns
+
+`Promise`\<`undefined` \| `T`\>
diff --git a/docs/classes/EntValidationError.md b/docs/classes/EntValidationError.md
new file mode 100644
index 0000000..5d10c8c
--- /dev/null
+++ b/docs/classes/EntValidationError.md
@@ -0,0 +1,66 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / EntValidationError
+
+# Class: EntValidationError
+
+Defined in: [src/ent/errors/EntValidationError.ts:8](https://github.com/clickup/ent-framework/blob/master/src/ent/errors/EntValidationError.ts#L8)
+
+Error: thrown after all validators are executed, and some of them think that
+the row is invalid.
+
+## Extends
+
+- [`EntAccessError`](EntAccessError.md)
+
+## Constructors
+
+### new EntValidationError()
+
+> **new EntValidationError**(`entName`, `errors`): [`EntValidationError`](EntValidationError.md)
+
+Defined in: [src/ent/errors/EntValidationError.ts:9](https://github.com/clickup/ent-framework/blob/master/src/ent/errors/EntValidationError.ts#L9)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `entName` | `string` |
+| `errors` | readonly [`EntValidationErrorInfo`](../interfaces/EntValidationErrorInfo.md)[] |
+
+#### Returns
+
+[`EntValidationError`](EntValidationError.md)
+
+#### Overrides
+
+[`EntAccessError`](EntAccessError.md).[`constructor`](EntAccessError.md#constructors)
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `cause` | `null` \| `string` \| `Error` |
+| `entName` | `string` |
+| `errors` | readonly [`EntValidationErrorInfo`](../interfaces/EntValidationErrorInfo.md)[] |
+
+## Methods
+
+### toStandardSchemaV1()
+
+> **toStandardSchemaV1**(): [`StandardSchemaV1FailureResult`](../interfaces/StandardSchemaV1FailureResult.md)
+
+Defined in: [src/ent/errors/EntValidationError.ts:27](https://github.com/clickup/ent-framework/blob/master/src/ent/errors/EntValidationError.ts#L27)
+
+Converts the payload to a Standard Schema V1 compatible error result. See
+https://standardschema.dev.
+
+#### Returns
+
+[`StandardSchemaV1FailureResult`](../interfaces/StandardSchemaV1FailureResult.md)
+
+#### Overrides
+
+[`EntAccessError`](EntAccessError.md).[`toStandardSchemaV1`](EntAccessError.md#tostandardschemav1)
diff --git a/docs/classes/FieldIs.md b/docs/classes/FieldIs.md
new file mode 100644
index 0000000..3c80e4d
--- /dev/null
+++ b/docs/classes/FieldIs.md
@@ -0,0 +1,101 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / FieldIs
+
+# Class: FieldIs\
+
+Defined in: [src/ent/predicates/FieldIs.ts:43](https://github.com/clickup/ent-framework/blob/master/src/ent/predicates/FieldIs.ts#L43)
+
+Checks that the validator function returns true for the value in some field.
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TField` *extends* `string` |
+| `TRow` *extends* `Partial`\<`Record`\<`TField`, `unknown`\>\> |
+
+## Implements
+
+- [`AbstractIs`](../interfaces/AbstractIs.md)\<`TRow`\>
+
+## Constructors
+
+### new FieldIs()
+
+> **new FieldIs**\<`TField`, `TRow`\>(`field`, `validator`, `message`): [`FieldIs`](FieldIs.md)\<`TField`, `TRow`\>
+
+Defined in: [src/ent/predicates/FieldIs.ts:55](https://github.com/clickup/ent-framework/blob/master/src/ent/predicates/FieldIs.ts#L55)
+
+Manual validator. Implies that we can trust the fieldValue TS type.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `field` | `TField` |
+| `validator` | [`FieldIsValidatorPlain`](../type-aliases/FieldIsValidatorPlain.md)\<`TField`, `TRow`\> |
+| `message` | `string` |
+
+#### Returns
+
+[`FieldIs`](FieldIs.md)\<`TField`, `TRow`\>
+
+### new FieldIs()
+
+> **new FieldIs**\<`TField`, `TRow`\>(`field`, `validator`): [`FieldIs`](FieldIs.md)\<`TField`, `TRow`\>
+
+Defined in: [src/ent/predicates/FieldIs.ts:65](https://github.com/clickup/ent-framework/blob/master/src/ent/predicates/FieldIs.ts#L65)
+
+Rich validator, like Standard Schema (https://standardschema.dev) or Zod.
+No implications are made on the fieldValue type.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `field` | `TField` |
+| `validator` | [`FieldIsValidatorZodSafeParse`](../type-aliases/FieldIsValidatorZodSafeParse.md)\<`TRow`\> \| [`FieldIsValidatorStandardSchemaV1`](../type-aliases/FieldIsValidatorStandardSchemaV1.md)\<`TRow`\> |
+
+#### Returns
+
+[`FieldIs`](FieldIs.md)\<`TField`, `TRow`\>
+
+## Properties
+
+| Property | Type | Description |
+| ------ | ------ | ------ |
+| `name` | `string` | - |
+| `field` | `TField` | The field this validation predicate is related to (null means that it applies to the entire Ent). |
+| `message` | `null` \| `string` | In case the predicate returns false or doesn't provide error messages by throwing EntValidationError, this message will be used. When message is null, it means that we expect the validator to return detailed information about each field errored (e.g. ValidatorStandardSchemaResult). |
+| `validator` | [`FieldIsValidatorPlain`](../type-aliases/FieldIsValidatorPlain.md)\<`TField`, `TRow`\> \| [`FieldIsValidatorZodSafeParse`](../type-aliases/FieldIsValidatorZodSafeParse.md)\<`TRow`\> \| [`FieldIsValidatorStandardSchemaV1`](../type-aliases/FieldIsValidatorStandardSchemaV1.md)\<`TRow`\> | - |
+
+## Methods
+
+### check()
+
+> **check**(`vc`, `row`): `Promise`\<`boolean`\>
+
+Defined in: [src/ent/predicates/FieldIs.ts:92](https://github.com/clickup/ent-framework/blob/master/src/ent/predicates/FieldIs.ts#L92)
+
+Returns true if validation succeeds. Returns false if it wants the client
+to use this.message as a validation failure response. Throws an instance of
+EntValidationError when it needs to deliver the detailed error messages
+about multiple fields.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `vc` | [`VC`](VC.md) |
+| `row` | `TRow` |
+
+#### Returns
+
+`Promise`\<`boolean`\>
+
+#### Implementation of
+
+[`AbstractIs`](../interfaces/AbstractIs.md).[`check`](../interfaces/AbstractIs.md#check)
diff --git a/docs/classes/FuncToPredicate.md b/docs/classes/FuncToPredicate.md
new file mode 100644
index 0000000..43d7685
--- /dev/null
+++ b/docs/classes/FuncToPredicate.md
@@ -0,0 +1,72 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / FuncToPredicate
+
+# Class: FuncToPredicate\
+
+Defined in: [src/ent/predicates/Predicate.ts:29](https://github.com/clickup/ent-framework/blob/master/src/ent/predicates/Predicate.ts#L29)
+
+Sometimes, instead of passing a well-known predicate like OutgoingEdgePointsToVC
+or CanUpdateOutgoingEdge, we want to pass just a function which accepts a row
+and returns true or false. This class represents a Predicate which delegates
+its work to such a function. The name of the function becomes the name of the
+predicate.
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TInput` |
+
+## Implements
+
+- [`Predicate`](../interfaces/Predicate.md)\<`TInput`\>
+
+## Constructors
+
+### new FuncToPredicate()
+
+> **new FuncToPredicate**\<`TInput`\>(`func`): [`FuncToPredicate`](FuncToPredicate.md)\<`TInput`\>
+
+Defined in: [src/ent/predicates/Predicate.ts:32](https://github.com/clickup/ent-framework/blob/master/src/ent/predicates/Predicate.ts#L32)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `func` | (`vc`, `input`) => `boolean` \| `Promise`\<`boolean`\> |
+
+#### Returns
+
+[`FuncToPredicate`](FuncToPredicate.md)\<`TInput`\>
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `name` | `string` |
+
+## Methods
+
+### check()
+
+> **check**(`vc`, `input`): `Promise`\<`boolean`\>
+
+Defined in: [src/ent/predicates/Predicate.ts:38](https://github.com/clickup/ent-framework/blob/master/src/ent/predicates/Predicate.ts#L38)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `vc` | [`VC`](VC.md) |
+| `input` | `TInput` |
+
+#### Returns
+
+`Promise`\<`boolean`\>
+
+#### Implementation of
+
+[`Predicate`](../interfaces/Predicate.md).[`check`](../interfaces/Predicate.md#check)
diff --git a/docs/classes/IDsCache.md b/docs/classes/IDsCache.md
new file mode 100644
index 0000000..ef2ff8e
--- /dev/null
+++ b/docs/classes/IDsCache.md
@@ -0,0 +1,84 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / IDsCache
+
+# Class: `abstract` IDsCache
+
+Defined in: [src/ent/IDsCache.ts:10](https://github.com/clickup/ent-framework/blob/master/src/ent/IDsCache.ts#L10)
+
+## Extended by
+
+- [`IDsCacheReadable`](IDsCacheReadable.md)
+- [`IDsCacheUpdatable`](IDsCacheUpdatable.md)
+- [`IDsCacheDeletable`](IDsCacheDeletable.md)
+- [`IDsCacheCanReadIncomingEdge`](IDsCacheCanReadIncomingEdge.md)
+
+## Constructors
+
+### new IDsCache()
+
+> **new IDsCache**(): [`IDsCache`](IDsCache.md)
+
+#### Returns
+
+[`IDsCache`](IDsCache.md)
+
+## Methods
+
+### has()
+
+> **has**(`Ent`, `id`): `boolean`
+
+Defined in: [src/ent/IDsCache.ts:13](https://github.com/clickup/ent-framework/blob/master/src/ent/IDsCache.ts#L13)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `Ent` | `EntClassAlike` |
+| `id` | `string` |
+
+#### Returns
+
+`boolean`
+
+***
+
+### add()
+
+> **add**(`Ent`, `id`, `value`): `void`
+
+Defined in: [src/ent/IDsCache.ts:17](https://github.com/clickup/ent-framework/blob/master/src/ent/IDsCache.ts#L17)
+
+#### Parameters
+
+| Parameter | Type | Default value |
+| ------ | ------ | ------ |
+| `Ent` | `EntClassAlike` | `undefined` |
+| `id` | `string` | `undefined` |
+| `value` | `boolean` | `true` |
+
+#### Returns
+
+`void`
+
+***
+
+### get()
+
+> **get**(`Ent`, `id`): `undefined` \| `boolean`
+
+Defined in: [src/ent/IDsCache.ts:21](https://github.com/clickup/ent-framework/blob/master/src/ent/IDsCache.ts#L21)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `Ent` | `EntClassAlike` |
+| `id` | `string` |
+
+#### Returns
+
+`undefined` \| `boolean`
diff --git a/docs/classes/IDsCacheCanReadIncomingEdge.md b/docs/classes/IDsCacheCanReadIncomingEdge.md
new file mode 100644
index 0000000..28da809
--- /dev/null
+++ b/docs/classes/IDsCacheCanReadIncomingEdge.md
@@ -0,0 +1,97 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / IDsCacheCanReadIncomingEdge
+
+# Class: IDsCacheCanReadIncomingEdge
+
+Defined in: [src/ent/predicates/Predicate.ts:49](https://github.com/clickup/ent-framework/blob/master/src/ent/predicates/Predicate.ts#L49)
+
+## Extends
+
+- [`IDsCache`](IDsCache.md)
+
+## Constructors
+
+### new IDsCacheCanReadIncomingEdge()
+
+> **new IDsCacheCanReadIncomingEdge**(): [`IDsCacheCanReadIncomingEdge`](IDsCacheCanReadIncomingEdge.md)
+
+#### Returns
+
+[`IDsCacheCanReadIncomingEdge`](IDsCacheCanReadIncomingEdge.md)
+
+#### Inherited from
+
+[`IDsCache`](IDsCache.md).[`constructor`](IDsCache.md#constructors)
+
+## Methods
+
+### has()
+
+> **has**(`Ent`, `id`): `boolean`
+
+Defined in: [src/ent/IDsCache.ts:13](https://github.com/clickup/ent-framework/blob/master/src/ent/IDsCache.ts#L13)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `Ent` | `EntClassAlike` |
+| `id` | `string` |
+
+#### Returns
+
+`boolean`
+
+#### Inherited from
+
+[`IDsCache`](IDsCache.md).[`has`](IDsCache.md#has)
+
+***
+
+### add()
+
+> **add**(`Ent`, `id`, `value`): `void`
+
+Defined in: [src/ent/IDsCache.ts:17](https://github.com/clickup/ent-framework/blob/master/src/ent/IDsCache.ts#L17)
+
+#### Parameters
+
+| Parameter | Type | Default value |
+| ------ | ------ | ------ |
+| `Ent` | `EntClassAlike` | `undefined` |
+| `id` | `string` | `undefined` |
+| `value` | `boolean` | `true` |
+
+#### Returns
+
+`void`
+
+#### Inherited from
+
+[`IDsCache`](IDsCache.md).[`add`](IDsCache.md#add)
+
+***
+
+### get()
+
+> **get**(`Ent`, `id`): `undefined` \| `boolean`
+
+Defined in: [src/ent/IDsCache.ts:21](https://github.com/clickup/ent-framework/blob/master/src/ent/IDsCache.ts#L21)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `Ent` | `EntClassAlike` |
+| `id` | `string` |
+
+#### Returns
+
+`undefined` \| `boolean`
+
+#### Inherited from
+
+[`IDsCache`](IDsCache.md).[`get`](IDsCache.md#get)
diff --git a/docs/classes/IDsCacheDeletable.md b/docs/classes/IDsCacheDeletable.md
new file mode 100644
index 0000000..f533ef3
--- /dev/null
+++ b/docs/classes/IDsCacheDeletable.md
@@ -0,0 +1,97 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / IDsCacheDeletable
+
+# Class: IDsCacheDeletable
+
+Defined in: [src/ent/predicates/Predicate.ts:48](https://github.com/clickup/ent-framework/blob/master/src/ent/predicates/Predicate.ts#L48)
+
+## Extends
+
+- [`IDsCache`](IDsCache.md)
+
+## Constructors
+
+### new IDsCacheDeletable()
+
+> **new IDsCacheDeletable**(): [`IDsCacheDeletable`](IDsCacheDeletable.md)
+
+#### Returns
+
+[`IDsCacheDeletable`](IDsCacheDeletable.md)
+
+#### Inherited from
+
+[`IDsCache`](IDsCache.md).[`constructor`](IDsCache.md#constructors)
+
+## Methods
+
+### has()
+
+> **has**(`Ent`, `id`): `boolean`
+
+Defined in: [src/ent/IDsCache.ts:13](https://github.com/clickup/ent-framework/blob/master/src/ent/IDsCache.ts#L13)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `Ent` | `EntClassAlike` |
+| `id` | `string` |
+
+#### Returns
+
+`boolean`
+
+#### Inherited from
+
+[`IDsCache`](IDsCache.md).[`has`](IDsCache.md#has)
+
+***
+
+### add()
+
+> **add**(`Ent`, `id`, `value`): `void`
+
+Defined in: [src/ent/IDsCache.ts:17](https://github.com/clickup/ent-framework/blob/master/src/ent/IDsCache.ts#L17)
+
+#### Parameters
+
+| Parameter | Type | Default value |
+| ------ | ------ | ------ |
+| `Ent` | `EntClassAlike` | `undefined` |
+| `id` | `string` | `undefined` |
+| `value` | `boolean` | `true` |
+
+#### Returns
+
+`void`
+
+#### Inherited from
+
+[`IDsCache`](IDsCache.md).[`add`](IDsCache.md#add)
+
+***
+
+### get()
+
+> **get**(`Ent`, `id`): `undefined` \| `boolean`
+
+Defined in: [src/ent/IDsCache.ts:21](https://github.com/clickup/ent-framework/blob/master/src/ent/IDsCache.ts#L21)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `Ent` | `EntClassAlike` |
+| `id` | `string` |
+
+#### Returns
+
+`undefined` \| `boolean`
+
+#### Inherited from
+
+[`IDsCache`](IDsCache.md).[`get`](IDsCache.md#get)
diff --git a/docs/classes/IDsCacheReadable.md b/docs/classes/IDsCacheReadable.md
new file mode 100644
index 0000000..f0c4bde
--- /dev/null
+++ b/docs/classes/IDsCacheReadable.md
@@ -0,0 +1,97 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / IDsCacheReadable
+
+# Class: IDsCacheReadable
+
+Defined in: [src/ent/predicates/Predicate.ts:46](https://github.com/clickup/ent-framework/blob/master/src/ent/predicates/Predicate.ts#L46)
+
+## Extends
+
+- [`IDsCache`](IDsCache.md)
+
+## Constructors
+
+### new IDsCacheReadable()
+
+> **new IDsCacheReadable**(): [`IDsCacheReadable`](IDsCacheReadable.md)
+
+#### Returns
+
+[`IDsCacheReadable`](IDsCacheReadable.md)
+
+#### Inherited from
+
+[`IDsCache`](IDsCache.md).[`constructor`](IDsCache.md#constructors)
+
+## Methods
+
+### has()
+
+> **has**(`Ent`, `id`): `boolean`
+
+Defined in: [src/ent/IDsCache.ts:13](https://github.com/clickup/ent-framework/blob/master/src/ent/IDsCache.ts#L13)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `Ent` | `EntClassAlike` |
+| `id` | `string` |
+
+#### Returns
+
+`boolean`
+
+#### Inherited from
+
+[`IDsCache`](IDsCache.md).[`has`](IDsCache.md#has)
+
+***
+
+### add()
+
+> **add**(`Ent`, `id`, `value`): `void`
+
+Defined in: [src/ent/IDsCache.ts:17](https://github.com/clickup/ent-framework/blob/master/src/ent/IDsCache.ts#L17)
+
+#### Parameters
+
+| Parameter | Type | Default value |
+| ------ | ------ | ------ |
+| `Ent` | `EntClassAlike` | `undefined` |
+| `id` | `string` | `undefined` |
+| `value` | `boolean` | `true` |
+
+#### Returns
+
+`void`
+
+#### Inherited from
+
+[`IDsCache`](IDsCache.md).[`add`](IDsCache.md#add)
+
+***
+
+### get()
+
+> **get**(`Ent`, `id`): `undefined` \| `boolean`
+
+Defined in: [src/ent/IDsCache.ts:21](https://github.com/clickup/ent-framework/blob/master/src/ent/IDsCache.ts#L21)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `Ent` | `EntClassAlike` |
+| `id` | `string` |
+
+#### Returns
+
+`undefined` \| `boolean`
+
+#### Inherited from
+
+[`IDsCache`](IDsCache.md).[`get`](IDsCache.md#get)
diff --git a/docs/classes/IDsCacheUpdatable.md b/docs/classes/IDsCacheUpdatable.md
new file mode 100644
index 0000000..8e71c69
--- /dev/null
+++ b/docs/classes/IDsCacheUpdatable.md
@@ -0,0 +1,97 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / IDsCacheUpdatable
+
+# Class: IDsCacheUpdatable
+
+Defined in: [src/ent/predicates/Predicate.ts:47](https://github.com/clickup/ent-framework/blob/master/src/ent/predicates/Predicate.ts#L47)
+
+## Extends
+
+- [`IDsCache`](IDsCache.md)
+
+## Constructors
+
+### new IDsCacheUpdatable()
+
+> **new IDsCacheUpdatable**(): [`IDsCacheUpdatable`](IDsCacheUpdatable.md)
+
+#### Returns
+
+[`IDsCacheUpdatable`](IDsCacheUpdatable.md)
+
+#### Inherited from
+
+[`IDsCache`](IDsCache.md).[`constructor`](IDsCache.md#constructors)
+
+## Methods
+
+### has()
+
+> **has**(`Ent`, `id`): `boolean`
+
+Defined in: [src/ent/IDsCache.ts:13](https://github.com/clickup/ent-framework/blob/master/src/ent/IDsCache.ts#L13)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `Ent` | `EntClassAlike` |
+| `id` | `string` |
+
+#### Returns
+
+`boolean`
+
+#### Inherited from
+
+[`IDsCache`](IDsCache.md).[`has`](IDsCache.md#has)
+
+***
+
+### add()
+
+> **add**(`Ent`, `id`, `value`): `void`
+
+Defined in: [src/ent/IDsCache.ts:17](https://github.com/clickup/ent-framework/blob/master/src/ent/IDsCache.ts#L17)
+
+#### Parameters
+
+| Parameter | Type | Default value |
+| ------ | ------ | ------ |
+| `Ent` | `EntClassAlike` | `undefined` |
+| `id` | `string` | `undefined` |
+| `value` | `boolean` | `true` |
+
+#### Returns
+
+`void`
+
+#### Inherited from
+
+[`IDsCache`](IDsCache.md).[`add`](IDsCache.md#add)
+
+***
+
+### get()
+
+> **get**(`Ent`, `id`): `undefined` \| `boolean`
+
+Defined in: [src/ent/IDsCache.ts:21](https://github.com/clickup/ent-framework/blob/master/src/ent/IDsCache.ts#L21)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `Ent` | `EntClassAlike` |
+| `id` | `string` |
+
+#### Returns
+
+`undefined` \| `boolean`
+
+#### Inherited from
+
+[`IDsCache`](IDsCache.md).[`get`](IDsCache.md#get)
diff --git a/docs/classes/IncomingEdgeFromVCExists.md b/docs/classes/IncomingEdgeFromVCExists.md
new file mode 100644
index 0000000..64b1fec
--- /dev/null
+++ b/docs/classes/IncomingEdgeFromVCExists.md
@@ -0,0 +1,86 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / IncomingEdgeFromVCExists
+
+# Class: IncomingEdgeFromVCExists\
+
+Defined in: [src/ent/predicates/IncomingEdgeFromVCExists.ts:23](https://github.com/clickup/ent-framework/blob/master/src/ent/predicates/IncomingEdgeFromVCExists.ts#L23)
+
+An ent may represent not necessarily a node in the graph, but also an edge
+between two nodes. Consider EntMember in the below example:
+
+vc.principal <--- EntMember[user_id, company_id] ---> EntCompany
+
+This predicate verifies that for a e.g. given EntCompany row and a given VC,
+an EntMember row exists (and optionally matches some criterion) in the
+database.
+
+- entEdgeVCField = user_id in the above example
+- entEdgeFKField = company_id in the above example
+- if an EntMember object exists, it must also match entEdgeFilter()
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TEdgeTable` *extends* [`Table`](../type-aliases/Table.md) |
+
+## Implements
+
+- [`Predicate`](../interfaces/Predicate.md)\<[`RowWithID`](../type-aliases/RowWithID.md)\>
+
+## Constructors
+
+### new IncomingEdgeFromVCExists()
+
+> **new IncomingEdgeFromVCExists**\<`TEdgeTable`\>(`EntEdge`, `entEdgeVCField`, `entEdgeFKField`, `entEdgeFilter`?): [`IncomingEdgeFromVCExists`](IncomingEdgeFromVCExists.md)\<`TEdgeTable`\>
+
+Defined in: [src/ent/predicates/IncomingEdgeFromVCExists.ts:29](https://github.com/clickup/ent-framework/blob/master/src/ent/predicates/IncomingEdgeFromVCExists.ts#L29)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `EntEdge` | [`EntClass`](../interfaces/EntClass.md)\<`TEdgeTable`\> |
+| `entEdgeVCField` | `"id"` \| [`Field`](../type-aliases/Field.md)\<`TEdgeTable`\> |
+| `entEdgeFKField` | `"id"` \| [`Field`](../type-aliases/Field.md)\<`TEdgeTable`\> |
+| `entEdgeFilter`? | (`ent`) => `boolean` |
+
+#### Returns
+
+[`IncomingEdgeFromVCExists`](IncomingEdgeFromVCExists.md)\<`TEdgeTable`\>
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `name` | `string` |
+| `EntEdge` | [`EntClass`](../interfaces/EntClass.md)\<`TEdgeTable`\> |
+| `entEdgeVCField` | `"id"` \| [`Field`](../type-aliases/Field.md)\<`TEdgeTable`\> |
+| `entEdgeFKField` | `"id"` \| [`Field`](../type-aliases/Field.md)\<`TEdgeTable`\> |
+| `entEdgeFilter?` | (`ent`: [`Row`](../type-aliases/Row.md)\<`TEdgeTable`\>) => `boolean` |
+
+## Methods
+
+### check()
+
+> **check**(`vc`, `row`): `Promise`\<`boolean`\>
+
+Defined in: [src/ent/predicates/IncomingEdgeFromVCExists.ts:46](https://github.com/clickup/ent-framework/blob/master/src/ent/predicates/IncomingEdgeFromVCExists.ts#L46)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `vc` | [`VC`](VC.md) |
+| `row` | [`RowWithID`](../type-aliases/RowWithID.md) |
+
+#### Returns
+
+`Promise`\<`boolean`\>
+
+#### Implementation of
+
+[`Predicate`](../interfaces/Predicate.md).[`check`](../interfaces/Predicate.md#check)
diff --git a/docs/classes/Inverse.md b/docs/classes/Inverse.md
new file mode 100644
index 0000000..45236f6
--- /dev/null
+++ b/docs/classes/Inverse.md
@@ -0,0 +1,143 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / Inverse
+
+# Class: Inverse\
+
+Defined in: [src/ent/Inverse.ts:29](https://github.com/clickup/ent-framework/blob/master/src/ent/Inverse.ts#L29)
+
+Represents an Inverse assoc manager which knows how to modify/query Inverses.
+Parameter `name` is the Inverse's schema name (in relational databases, most
+likely a table name), and `type` holds both the name of the "parent" entity
+and the field name of the child (e.g. "org2users" when a field "org_id" in
+EntUser refers an EntOrg row).
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TClient` *extends* [`Client`](Client.md) |
+| `TTable` *extends* [`Table`](../type-aliases/Table.md) |
+
+## Constructors
+
+### new Inverse()
+
+> **new Inverse**\<`TClient`, `TTable`\>(`__namedParameters`): [`Inverse`](Inverse.md)\<`TClient`, `TTable`\>
+
+Defined in: [src/ent/Inverse.ts:37](https://github.com/clickup/ent-framework/blob/master/src/ent/Inverse.ts#L37)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `__namedParameters` | \{ `cluster`: [`Cluster`](Cluster.md)\<`TClient`, `any`\>; `shardAffinity`: [`ShardAffinity`](../type-aliases/ShardAffinity.md)\<`string`\>; `id2Schema`: [`Schema`](Schema.md)\<`TTable`, [`UniqueKey`](../type-aliases/UniqueKey.md)\<`TTable`\>\>; `id2Field`: [`FieldOfIDTypeRequired`](../type-aliases/FieldOfIDTypeRequired.md)\<`TTable`\>; `name`: `string`; `type`: `string`; \} |
+| `__namedParameters.cluster` | [`Cluster`](Cluster.md)\<`TClient`, `any`\> |
+| `__namedParameters.shardAffinity` | [`ShardAffinity`](../type-aliases/ShardAffinity.md)\<`string`\> |
+| `__namedParameters.id2Schema` | [`Schema`](Schema.md)\<`TTable`, [`UniqueKey`](../type-aliases/UniqueKey.md)\<`TTable`\>\> |
+| `__namedParameters.id2Field` | [`FieldOfIDTypeRequired`](../type-aliases/FieldOfIDTypeRequired.md)\<`TTable`\> |
+| `__namedParameters.name` | `string` |
+| `__namedParameters.type` | `string` |
+
+#### Returns
+
+[`Inverse`](Inverse.md)\<`TClient`, `TTable`\>
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `id2Field` | [`FieldOfIDTypeRequired`](../type-aliases/FieldOfIDTypeRequired.md)\<`TTable`\> |
+| `type` | `string` |
+
+## Methods
+
+### beforeInsert()
+
+> **beforeInsert**(`vc`, `id1`, `id2`): `Promise`\<`boolean`\>
+
+Defined in: [src/ent/Inverse.ts:64](https://github.com/clickup/ent-framework/blob/master/src/ent/Inverse.ts#L64)
+
+Runs before a row with a pre-generated id2 was inserted to the main schema.
+Returns true if the Inverse row was actually inserted in the DB.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `vc` | [`VC`](VC.md) |
+| `id1` | `null` \| `string` |
+| `id2` | `string` |
+
+#### Returns
+
+`Promise`\<`boolean`\>
+
+***
+
+### afterUpdate()
+
+> **afterUpdate**(`vc`, `id1`, `id2`, `oldID1`): `Promise`\<`void`\>
+
+Defined in: [src/ent/Inverse.ts:88](https://github.com/clickup/ent-framework/blob/master/src/ent/Inverse.ts#L88)
+
+Runs after a row was updated in the main schema.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `vc` | [`VC`](VC.md) |
+| `id1` | `null` \| `string` |
+| `id2` | `string` |
+| `oldID1` | `null` \| `string` |
+
+#### Returns
+
+`Promise`\<`void`\>
+
+***
+
+### afterDelete()
+
+> **afterDelete**(`vc`, `id1`, `id2`): `Promise`\<`void`\>
+
+Defined in: [src/ent/Inverse.ts:107](https://github.com/clickup/ent-framework/blob/master/src/ent/Inverse.ts#L107)
+
+Runs after a row was deleted in the main schema.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `vc` | [`VC`](VC.md) |
+| `id1` | `null` \| `string` |
+| `id2` | `string` |
+
+#### Returns
+
+`Promise`\<`void`\>
+
+***
+
+### id2s()
+
+> **id2s**(`vc`, `id1`): `Promise`\<`string`[]\>
+
+Defined in: [src/ent/Inverse.ts:130](https://github.com/clickup/ent-framework/blob/master/src/ent/Inverse.ts#L130)
+
+Returns all id2s by a particular (id1, type) pair. The number of resulting
+rows is limited to not overload the database.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `vc` | [`VC`](VC.md) |
+| `id1` | `null` \| `string` |
+
+#### Returns
+
+`Promise`\<`string`[]\>
diff --git a/docs/classes/Island.md b/docs/classes/Island.md
new file mode 100644
index 0000000..845c035
--- /dev/null
+++ b/docs/classes/Island.md
@@ -0,0 +1,177 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / Island
+
+# Class: Island\
+
+Defined in: [src/abstract/Island.ts:94](https://github.com/clickup/ent-framework/blob/master/src/abstract/Island.ts#L94)
+
+Island is a moderately short-lived collection of DB connections (represented
+as Clients) that contains a single master Client and any number of replicas.
+
+- In normal situations, you don't likely need to work with Islands directly,
+ you can rely on higher level abstractions which support automatic
+ rediscovery and retries: Ent (or lower level Shard and Schema).
+- Islands are helpful mostly when working with cross-Shards logic.
+- Island is somewhat temporary: if the Cluster is reconfigured in real-time,
+ then its Island objects may be recycled and re-created, and the
+ corresponding Clients may be ended. This also applies to any given Client
+ instance. Don't retain and reuse those objects for too long. The reliable
+ abstractions (resilient to disconnects, shards migration, failover etc.)
+ start from Shard level.
+- There is no guarantee that the data returned by shards(), master() or
+ replica() will be up to date. Shards may be just migrated to another
+ Island. Master may become a replica, or vice versa.
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TClient` *extends* [`Client`](Client.md) |
+
+## Constructors
+
+### new Island()
+
+> **new Island**\<`TClient`\>(`options`): [`Island`](Island.md)\<`TClient`\>
+
+Defined in: [src/abstract/Island.ts:121](https://github.com/clickup/ent-framework/blob/master/src/abstract/Island.ts#L121)
+
+Initializes the Island by copying the Client references into it.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `options` | [`IslandOptions`](../interfaces/IslandOptions.md)\<`TClient`\> |
+
+#### Returns
+
+[`Island`](Island.md)\<`TClient`\>
+
+## Properties
+
+| Property | Type | Description |
+| ------ | ------ | ------ |
+| `DEFAULT_OPTIONS` | `Required`\<`PickPartial`\<[`IslandOptions`](../interfaces/IslandOptions.md)\<[`Client`](Client.md)\>\>\> | Default values for the constructor options. |
+| `options` | `Required`\<[`IslandOptions`](../interfaces/IslandOptions.md)\<`TClient`\>\> | Island configuration options. |
+
+## Accessors
+
+### no
+
+#### Get Signature
+
+> **get** **no**(): `number`
+
+Defined in: [src/abstract/Island.ts:133](https://github.com/clickup/ent-framework/blob/master/src/abstract/Island.ts#L133)
+
+Island number.
+
+##### Returns
+
+`number`
+
+***
+
+### clients
+
+#### Get Signature
+
+> **get** **clients**(): readonly `TClient`[]
+
+Defined in: [src/abstract/Island.ts:140](https://github.com/clickup/ent-framework/blob/master/src/abstract/Island.ts#L140)
+
+The list of Clients in this Island. No assumptions about the order.
+
+##### Returns
+
+readonly `TClient`[]
+
+## Methods
+
+### rediscover()
+
+> **rediscover**(): `Promise`\<[`SwallowedErrorLoggerProps`](../interfaces/SwallowedErrorLoggerProps.md)[]\>
+
+Defined in: [src/abstract/Island.ts:157](https://github.com/clickup/ent-framework/blob/master/src/abstract/Island.ts#L157)
+
+Queries for Shards on the best available Client (preferably master, then
+replicas) and stores the result internally, available for the further
+shards() call.
+- If some Clients are unavailable, tries its best to infer the data from
+ other Clients.
+- The method queries ALL clients in parallel, because the caller logic
+ anyways needs to know, who's master and who's replica, as a side effect
+ of the very 1st query after the Client creation. We infer that as a piggy
+ back after calling Client#shardNos().
+- In case we could not discover shards, returns the list of errors happened
+ during the discovery.
+
+#### Returns
+
+`Promise`\<[`SwallowedErrorLoggerProps`](../interfaces/SwallowedErrorLoggerProps.md)[]\>
+
+***
+
+### shards()
+
+> **shards**(): [`Shard`](Shard.md)\<`TClient`\>[]
+
+Defined in: [src/abstract/Island.ts:224](https://github.com/clickup/ent-framework/blob/master/src/abstract/Island.ts#L224)
+
+Returns the currently best-known Shards on this Island. This method is
+needed only when working with cross-Shards logic; in normal situations, it
+is not called much.
+
+#### Returns
+
+[`Shard`](Shard.md)\<`TClient`\>[]
+
+***
+
+### master()
+
+> **master**(): `TClient`
+
+Defined in: [src/abstract/Island.ts:252](https://github.com/clickup/ent-framework/blob/master/src/abstract/Island.ts#L252)
+
+Returns the currently best-known master Client among the Clients of this
+Island.
+
+- If all masters are unhealthy, we still return one of them and prefer not
+ to fall back on a replica, because otherwise, we'll see non-obvious
+ errors in logs ("can't write in a read-only Client" or so) and suspect
+ that there is a bug in logic, although there is really no bug, it's just
+ the master node went down. It's way better to throw a straightforward
+ error like "Client is down".
+- If we can't find a master, but there is a list of Clients with unknown
+ roles, prefer returning one of them vs. any known replica, since there is
+ a chance that among those unknown Clients, there will be a master.
+- In case all Clients are read-only (replicas), still returns the 1st of
+ them, assuming that it's better to throw at the caller side on a failed
+ write (at worst) rather than here. It is not common to have an Island
+ without a master Client, that happens only temporarily during
+ failover/switchover, so the caller will likely rediscover and find a new
+ master on a next retry.
+
+#### Returns
+
+`TClient`
+
+***
+
+### replica()
+
+> **replica**(): `TClient`
+
+Defined in: [src/abstract/Island.ts:279](https://github.com/clickup/ent-framework/blob/master/src/abstract/Island.ts#L279)
+
+Returns a currently best-known random replica Client. In case there are no
+replicas, returns the master Client.
+
+#### Returns
+
+`TClient`
diff --git a/docs/classes/Loader.md b/docs/classes/Loader.md
new file mode 100644
index 0000000..632e0e6
--- /dev/null
+++ b/docs/classes/Loader.md
@@ -0,0 +1,75 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / Loader
+
+# Class: Loader\
+
+Defined in: [src/abstract/Loader.ts:42](https://github.com/clickup/ent-framework/blob/master/src/abstract/Loader.ts#L42)
+
+Loader allows to batch single-item requests into batches. It uses a different
+architecture than Facebook's DataLoader:
+
+- it's more developers-friendly: multi-parameter loadings, you may implement
+ automatic deduplication of requests at onCollect stage, no requirement to
+ serialize/deserialize requests into string keys;
+- strong-typed load() and handler arguments.
+
+To create your own specific loader:
+1. Define a handler class with onCollect/onReturn/onFlush methods.
+2. In onCollect, accumulate the incoming requests in the handler object's
+ private property.
+3. In onFlush, process what you accumulated so far and save to another
+ handler object's private property (and by adding, say, delay(50) in the
+ beginning of onFlush, you may group the requests coming within the 1st 50
+ ms).
+3. In onReturn, extract the result corresponding to the request and return
+ it, so the caller will receive it seamlessly as a load() return value.
+
+In the future, Batcher may be refactored to use Loader as the underlying
+engine, but for now they're separate (Batcher is much more domain logic
+specific and Loader is completely abstract).
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TLoadArgs` *extends* `unknown`[] |
+| `TReturn` |
+
+## Constructors
+
+### new Loader()
+
+> **new Loader**\<`TLoadArgs`, `TReturn`\>(`handlerCreator`): [`Loader`](Loader.md)\<`TLoadArgs`, `TReturn`\>
+
+Defined in: [src/abstract/Loader.ts:45](https://github.com/clickup/ent-framework/blob/master/src/abstract/Loader.ts#L45)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `handlerCreator` | () => [`Handler`](../interfaces/Handler.md)\<`TLoadArgs`, `TReturn`\> |
+
+#### Returns
+
+[`Loader`](Loader.md)\<`TLoadArgs`, `TReturn`\>
+
+## Methods
+
+### load()
+
+> **load**(...`args`): `Promise`\<`TReturn`\>
+
+Defined in: [src/abstract/Loader.ts:47](https://github.com/clickup/ent-framework/blob/master/src/abstract/Loader.ts#L47)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| ...`args` | `TLoadArgs` |
+
+#### Returns
+
+`Promise`\<`TReturn`\>
diff --git a/docs/classes/LocalCache.md b/docs/classes/LocalCache.md
new file mode 100644
index 0000000..4faa66d
--- /dev/null
+++ b/docs/classes/LocalCache.md
@@ -0,0 +1,109 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / LocalCache
+
+# Class: LocalCache\
+
+Defined in: [src/abstract/LocalCache.ts:45](https://github.com/clickup/ent-framework/blob/master/src/abstract/LocalCache.ts#L45)
+
+A simple key-value cache stored on the local machine.
+
+- The expectation is that there will be not too many keys stored, since the
+ background cleanup process running time to time is O(numKeysStored).
+- Guarantees corruption-free writes to the keys from multiple processes
+ running concurrently.
+- The values which are not requested longer than approximately `expirationMs`
+ are auto-removed.
+- Each key is stored in an individual file under `dir`. Some temporary files
+ may also appear in that directory, but eventually, they will be cleaned up,
+ even if they get stuck for some time.
+
+## Type Parameters
+
+| Type Parameter | Default type |
+| ------ | ------ |
+| `TValue` *extends* `object` | `never` |
+
+## Constructors
+
+### new LocalCache()
+
+> **new LocalCache**\<`TValue`\>(`options`): [`LocalCache`](LocalCache.md)\<`TValue`\>
+
+Defined in: [src/abstract/LocalCache.ts:64](https://github.com/clickup/ent-framework/blob/master/src/abstract/LocalCache.ts#L64)
+
+Initializes the instance.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `options` | [`LocalCacheOptions`](../interfaces/LocalCacheOptions.md) |
+
+#### Returns
+
+[`LocalCache`](LocalCache.md)\<`TValue`\>
+
+## Properties
+
+| Property | Type | Description |
+| ------ | ------ | ------ |
+| `DEFAULT_OPTIONS` | `Required`\<`PickPartial`\<[`LocalCacheOptions`](../interfaces/LocalCacheOptions.md)\>\> | Default values for the constructor options. |
+| `options` | `Required`\<[`LocalCacheOptions`](../interfaces/LocalCacheOptions.md)\> | LocalCache configuration options. |
+
+## Methods
+
+### end()
+
+> **end**(): `void`
+
+Defined in: [src/abstract/LocalCache.ts:78](https://github.com/clickup/ent-framework/blob/master/src/abstract/LocalCache.ts#L78)
+
+Ends the instance lifecycle (e.g. garbage recheck interval).
+
+#### Returns
+
+`void`
+
+***
+
+### get()
+
+> **get**(`key`): `Promise`\<`null` \| `TValue`\>
+
+Defined in: [src/abstract/LocalCache.ts:86](https://github.com/clickup/ent-framework/blob/master/src/abstract/LocalCache.ts#L86)
+
+Returns the value for the given key, or null if the key does not exist.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `key` | `string` |
+
+#### Returns
+
+`Promise`\<`null` \| `TValue`\>
+
+***
+
+### set()
+
+> **set**(`key`, `value`): `Promise`\<`void`\>
+
+Defined in: [src/abstract/LocalCache.ts:105](https://github.com/clickup/ent-framework/blob/master/src/abstract/LocalCache.ts#L105)
+
+Sets the value for the given key.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `key` | `string` |
+| `value` | `TValue` |
+
+#### Returns
+
+`Promise`\<`void`\>
diff --git a/docs/classes/Or.md b/docs/classes/Or.md
new file mode 100644
index 0000000..2840800
--- /dev/null
+++ b/docs/classes/Or.md
@@ -0,0 +1,69 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / Or
+
+# Class: Or\
+
+Defined in: [src/ent/predicates/Or.ts:10](https://github.com/clickup/ent-framework/blob/master/src/ent/predicates/Or.ts#L10)
+
+Checks that at least one of the children predicates succeed.
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TInput` |
+
+## Implements
+
+- [`Predicate`](../interfaces/Predicate.md)\<`TInput`\>
+
+## Constructors
+
+### new Or()
+
+> **new Or**\<`TInput`\>(...`predicates`): [`Or`](Or.md)\<`TInput`\>
+
+Defined in: [src/ent/predicates/Or.ts:14](https://github.com/clickup/ent-framework/blob/master/src/ent/predicates/Or.ts#L14)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| ...`predicates` | readonly ([`Predicate`](../interfaces/Predicate.md)\<`TInput`\> \| (`vc`, `input`) => `boolean` \| `Promise`\<`boolean`\>)[] |
+
+#### Returns
+
+[`Or`](Or.md)\<`TInput`\>
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `name` | `string` |
+| `predicates` | readonly [`Predicate`](../interfaces/Predicate.md)\<`TInput`\>[] |
+
+## Methods
+
+### check()
+
+> **check**(`vc`, `input`): `Promise`\<`boolean`\>
+
+Defined in: [src/ent/predicates/Or.ts:27](https://github.com/clickup/ent-framework/blob/master/src/ent/predicates/Or.ts#L27)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `vc` | [`VC`](VC.md) |
+| `input` | `TInput` |
+
+#### Returns
+
+`Promise`\<`boolean`\>
+
+#### Implementation of
+
+[`Predicate`](../interfaces/Predicate.md).[`check`](../interfaces/Predicate.md#check)
diff --git a/docs/classes/OutgoingEdgePointsToVC.md b/docs/classes/OutgoingEdgePointsToVC.md
new file mode 100644
index 0000000..b858369
--- /dev/null
+++ b/docs/classes/OutgoingEdgePointsToVC.md
@@ -0,0 +1,71 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / OutgoingEdgePointsToVC
+
+# Class: OutgoingEdgePointsToVC\
+
+Defined in: [src/ent/predicates/OutgoingEdgePointsToVC.ts:9](https://github.com/clickup/ent-framework/blob/master/src/ent/predicates/OutgoingEdgePointsToVC.ts#L9)
+
+Checks that the field's value is the same as VC's principal:
+
+EntOur[user_id] ---> vc.principal
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TField` *extends* `string` |
+
+## Implements
+
+- [`Predicate`](../interfaces/Predicate.md)\<`Record`\<`TField`, `string` \| `null`\>\>
+
+## Constructors
+
+### new OutgoingEdgePointsToVC()
+
+> **new OutgoingEdgePointsToVC**\<`TField`\>(`field`): [`OutgoingEdgePointsToVC`](OutgoingEdgePointsToVC.md)\<`TField`\>
+
+Defined in: [src/ent/predicates/OutgoingEdgePointsToVC.ts:14](https://github.com/clickup/ent-framework/blob/master/src/ent/predicates/OutgoingEdgePointsToVC.ts#L14)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `field` | `TField` |
+
+#### Returns
+
+[`OutgoingEdgePointsToVC`](OutgoingEdgePointsToVC.md)\<`TField`\>
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `name` | `string` |
+| `field` | `TField` |
+
+## Methods
+
+### check()
+
+> **check**(`vc`, `row`): `Promise`\<`boolean`\>
+
+Defined in: [src/ent/predicates/OutgoingEdgePointsToVC.ts:18](https://github.com/clickup/ent-framework/blob/master/src/ent/predicates/OutgoingEdgePointsToVC.ts#L18)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `vc` | [`VC`](VC.md) |
+| `row` | `Record`\<`TField`, `null` \| `string`\> |
+
+#### Returns
+
+`Promise`\<`boolean`\>
+
+#### Implementation of
+
+[`Predicate`](../interfaces/Predicate.md).[`check`](../interfaces/Predicate.md#check)
diff --git a/docs/classes/PgClient.md b/docs/classes/PgClient.md
new file mode 100644
index 0000000..bde5e54
--- /dev/null
+++ b/docs/classes/PgClient.md
@@ -0,0 +1,425 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / PgClient
+
+# Class: PgClient\
+
+Defined in: [src/pg/PgClient.ts:128](https://github.com/clickup/ent-framework/blob/master/src/pg/PgClient.ts#L128)
+
+An abstract PostgreSQL Client. Includes connection pooling logic.
+
+Since the class is cloneable internally (using the prototype substitution
+technique, see withShard()), the contract of this class is that ALL its
+derived classes may only have readonly immediate properties. Use Ref helper
+if you need some mutable properties.
+
+## Extends
+
+- [`Client`](Client.md)
+
+## Type Parameters
+
+| Type Parameter | Default type |
+| ------ | ------ |
+| `TPool` *extends* `pg.Pool` | `pg.Pool` |
+
+## Constructors
+
+### new PgClient()
+
+> **new PgClient**\<`TPool`\>(`options`): [`PgClient`](PgClient.md)\<`TPool`\>
+
+Defined in: [src/pg/PgClient.ts:190](https://github.com/clickup/ent-framework/blob/master/src/pg/PgClient.ts#L190)
+
+Initializes an instance of PgClient.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `options` | [`PgClientOptions`](../interfaces/PgClientOptions.md)\<`TPool`\> |
+
+#### Returns
+
+[`PgClient`](PgClient.md)\<`TPool`\>
+
+#### Overrides
+
+[`Client`](Client.md).[`constructor`](Client.md#constructors)
+
+## Properties
+
+| Property | Type | Default value | Description |
+| ------ | ------ | ------ | ------ |
+| `createdAt` | `Date` | `undefined` | Date when this Client instance was constructed. |
+| `DEFAULT_OPTIONS` | `Required`\<`PickPartial`\<[`PgClientOptions`](../interfaces/PgClientOptions.md)\<`Pool`\>\>\> | `undefined` | Default values for the constructor options. |
+| `options` | `Required`\<[`PgClientOptions`](../interfaces/PgClientOptions.md)\<`TPool`\>\> | `undefined` | PgClient configuration options. |
+| `shardName` | `string` | `"public"` | Name of the shard associated to this Client. |
+| `timelineManager` | [`TimelineManager`](TimelineManager.md) | `undefined` | An active TimelineManager for this particular Client. |
+
+## Methods
+
+### batcher()
+
+> **batcher**\<`TInput`, `TOutput`, `TTable`\>(`_QueryClass`, `_schema`, `_additionalShape`, `disableBatching`, `runnerCreator`): [`Batcher`](Batcher.md)\<`TInput`, `TOutput`\>
+
+Defined in: [src/abstract/Client.ts:189](https://github.com/clickup/ent-framework/blob/master/src/abstract/Client.ts#L189)
+
+Batcher is per-Client per-query-type
+per-table-name-and-shape-and-disableBatching:
+
+- Per-Client means that batchers are removed as soon as the Client is
+ removed, i.e. the Client owns all the batchers for all tables.
+- Per-query-type means that the batcher for a SELECT query is different
+ from the batcher for an INSERT query (obviously).
+- Per-table-name-and-shape-and-disableBatching means that each table has
+ its own set of batchers (obviously). Also, some queries may be complex
+ (like UPDATE), so the batcher also depends on the "shape" - the list of
+ fields we're updating. Plus, for some inputs, we want to disable batching
+ at all - that produces a separate Batcher instance.
+
+Also, for every Batcher, there is exactly one Runner (which knows how to
+build the actual query in the context of the current Client). Batchers are
+generic (like DataLoader, but more general), and Runners are very custom to
+the query (and are private to these queries).
+
+All that means that in a 1000-Shard 20-table Cluster we'll eventually have
+1000x20x8 Batchers/Runners (assuming we have 8 different operations).
+
+#### Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TInput` |
+| `TOutput` |
+| `TTable` *extends* [`Table`](../type-aliases/Table.md) |
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `_QueryClass` | `Function` |
+| `_schema` | [`Schema`](Schema.md)\<`TTable`, [`UniqueKey`](../type-aliases/UniqueKey.md)\<`TTable`\>\> |
+| `_additionalShape` | `string` |
+| `disableBatching` | `boolean` |
+| `runnerCreator` | () => [`Runner`](Runner.md)\<`TInput`, `TOutput`\> |
+
+#### Returns
+
+[`Batcher`](Batcher.md)\<`TInput`, `TOutput`\>
+
+#### Inherited from
+
+[`Client`](Client.md).[`batcher`](Client.md#batcher)
+
+***
+
+### logSwallowedError()
+
+> `protected` **logSwallowedError**(`props`): `void`
+
+Defined in: [src/pg/PgClient.ts:181](https://github.com/clickup/ent-framework/blob/master/src/pg/PgClient.ts#L181)
+
+Calls swallowedErrorLogger() doing some preliminary amendment.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `props` | [`SwallowedErrorLoggerProps`](../interfaces/SwallowedErrorLoggerProps.md) |
+
+#### Returns
+
+`void`
+
+#### Overrides
+
+[`Client`](Client.md).[`logSwallowedError`](Client.md#logswallowederror)
+
+***
+
+### address()
+
+> **address**(): `string`
+
+Defined in: [src/pg/PgClient.ts:239](https://github.com/clickup/ent-framework/blob/master/src/pg/PgClient.ts#L239)
+
+Represents the full destination address this Client is working with.
+Depending on the implementation, it may include hostname, port number,
+database name, shard name etc. It is required that the address is stable
+enough to be able to cache some destination database related metadata (e.g.
+shardNos) based on that address.
+
+#### Returns
+
+`string`
+
+#### Overrides
+
+[`Client`](Client.md).[`address`](Client.md#address)
+
+***
+
+### end()
+
+> **end**(): `Promise`\<`void`\>
+
+Defined in: [src/pg/PgClient.ts:256](https://github.com/clickup/ent-framework/blob/master/src/pg/PgClient.ts#L256)
+
+Gracefully closes all the connections of this Client to let the caller
+destroy it. The pending queries are awaited to finish before returning. The
+Client becomes unusable right after calling this method (even before the
+connections are drained): you should not send queries to it.
+
+#### Returns
+
+`Promise`\<`void`\>
+
+#### Overrides
+
+[`Client`](Client.md).[`end`](Client.md#end)
+
+***
+
+### isEnded()
+
+> **isEnded**(): `boolean`
+
+Defined in: [src/pg/PgClient.ts:270](https://github.com/clickup/ent-framework/blob/master/src/pg/PgClient.ts#L270)
+
+Returns true if the Client is ended and can't be used anymore.
+
+#### Returns
+
+`boolean`
+
+#### Overrides
+
+[`Client`](Client.md).[`isEnded`](Client.md#isended)
+
+***
+
+### shardNos()
+
+> **shardNos**(): `Promise`\
+
+Defined in: [src/pg/PgClient.ts:278](https://github.com/clickup/ent-framework/blob/master/src/pg/PgClient.ts#L278)
+
+Returns all Shard numbers discoverable via the connection to the Client's
+database.
+
+#### Returns
+
+`Promise`\
+
+#### Overrides
+
+[`Client`](Client.md).[`shardNos`](Client.md#shardnos)
+
+***
+
+### ping()
+
+> **ping**(`__namedParameters`): `Promise`\<`void`\>
+
+Defined in: [src/pg/PgClient.ts:305](https://github.com/clickup/ent-framework/blob/master/src/pg/PgClient.ts#L305)
+
+Sends a read or write test query to the server. Tells the server to sit and
+wait for at least the provided number of milliseconds.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `__namedParameters` | [`ClientPingInput`](../interfaces/ClientPingInput.md) |
+
+#### Returns
+
+`Promise`\<`void`\>
+
+#### Overrides
+
+[`Client`](Client.md).[`ping`](Client.md#ping)
+
+***
+
+### withShard()
+
+> **withShard**(`no`): `this`
+
+Defined in: [src/pg/PgClient.ts:327](https://github.com/clickup/ent-framework/blob/master/src/pg/PgClient.ts#L327)
+
+Creates a new Client which is namespaced to the provided Shard number. The
+new Client will share the same connection pool with the parent's Client.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `no` | `number` |
+
+#### Returns
+
+`this`
+
+#### Overrides
+
+[`Client`](Client.md).[`withShard`](Client.md#withshard)
+
+***
+
+### role()
+
+> **role**(): [`ClientRole`](../type-aliases/ClientRole.md)
+
+Defined in: [src/pg/PgClient.ts:345](https://github.com/clickup/ent-framework/blob/master/src/pg/PgClient.ts#L345)
+
+Returns the Client's role reported after the last successful query. Master
+and replica roles may switch online unpredictably, without reconnecting, so
+we only know the role after a query.
+
+#### Returns
+
+[`ClientRole`](../type-aliases/ClientRole.md)
+
+#### Overrides
+
+[`Client`](Client.md).[`role`](Client.md#role)
+
+***
+
+### connectionIssue()
+
+> **connectionIssue**(): `null` \| [`ClientConnectionIssue`](../interfaces/ClientConnectionIssue.md)
+
+Defined in: [src/pg/PgClient.ts:355](https://github.com/clickup/ent-framework/blob/master/src/pg/PgClient.ts#L355)
+
+Returns a non-nullable value if the Client couldn't connect to the server
+(or it could, but the load balancer reported the remote server as not
+working), so it should ideally be removed from the list of active replicas
+until e.g. the next discovery query to it (or any query) succeeds.
+
+#### Returns
+
+`null` \| [`ClientConnectionIssue`](../interfaces/ClientConnectionIssue.md)
+
+#### Overrides
+
+[`Client`](Client.md).[`connectionIssue`](Client.md#connectionissue)
+
+***
+
+### prewarm()
+
+> **prewarm**(): `void`
+
+Defined in: [src/pg/PgClient.ts:365](https://github.com/clickup/ent-framework/blob/master/src/pg/PgClient.ts#L365)
+
+A convenience method to put connections prewarming logic to. The idea is to
+keep the needed number of open connections and also, in each connection,
+minimize the time which the very 1st query will take (e.g. pre-cache
+full-text dictionaries).
+
+#### Returns
+
+`void`
+
+#### Overrides
+
+[`Client`](Client.md).[`prewarm`](Client.md#prewarm)
+
+***
+
+### pool()
+
+> **pool**(`subPoolConfig`?): `TPool`
+
+Defined in: [src/pg/PgClient.ts:431](https://github.com/clickup/ent-framework/blob/master/src/pg/PgClient.ts#L431)
+
+Returns a default pool (when subPoolConfig is not passed), or a "sub-pool"
+(a named low-level Pool implementation compatible to node-postgres). The
+method is specific to the current class and is not a part of
+database-agnostic Client API.
+- Sub-pools are lazily created and memoized by the provided name. They may
+ differ by config options (like statement_timeout or max connections).
+- Sub-pools inherit the properties from default PgClientOptions.config.
+- It is implied (but not enforced) that all sub-pools use the same physical
+ database, because otherwise it makes not a lot of sense.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `subPoolConfig`? | [`PgClientSubPoolConfig`](../interfaces/PgClientSubPoolConfig.md) |
+
+#### Returns
+
+`TPool`
+
+***
+
+### acquireConn()
+
+> **acquireConn**(`subPoolConfig`?): `Promise`\<[`PgClientConn`](../interfaces/PgClientConn.md)\<`TPool`\>\>
+
+Defined in: [src/pg/PgClient.ts:493](https://github.com/clickup/ent-framework/blob/master/src/pg/PgClient.ts#L493)
+
+Called when the Client needs a connection in the default pool (when
+subPoolConfig is not passed), or in a sub-pool (see pool() method) to run a
+query against. Implies than the caller MUST call release() method on the
+returned object. The difference from pool().connect() is that when calling
+release() on a result of acquireConn(), it additionally closes the
+connection automatically if was OPENED (not queried!) more than
+maxConnLifetimeMs ago (node-postgres Pool doesn't have this feature) The
+method is specific to the current class and is not a part of
+database-agnostic Client API.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `subPoolConfig`? | [`PgClientSubPoolConfig`](../interfaces/PgClientSubPoolConfig.md) |
+
+#### Returns
+
+`Promise`\<[`PgClientConn`](../interfaces/PgClientConn.md)\<`TPool`\>\>
+
+***
+
+### query()
+
+> **query**\<`TRow`\>(`__namedParameters`): `Promise`\<`TRow`[]\>
+
+Defined in: [src/pg/PgClient.ts:517](https://github.com/clickup/ent-framework/blob/master/src/pg/PgClient.ts#L517)
+
+Sends a query (internally, a multi-query) through the default Pool (if
+subPoolConfig is not passed), or through a named sub-pool (see pool()
+method). After the query finishes, we should expect that role() returns the
+actual master/replica role. The method is specific to the current class and
+is not a part of database-agnostic Client API.
+
+#### Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TRow` |
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `__namedParameters` | \{ `query`: [`Literal`](../type-aliases/Literal.md); `hints`: [`Hints`](../type-aliases/Hints.md); `isWrite`: `boolean`; `annotations`: [`QueryAnnotation`](../interfaces/QueryAnnotation.md)[]; `op`: `string`; `table`: `string`; `batchFactor`: `number`; `subPoolConfig`: [`PgClientSubPoolConfig`](../interfaces/PgClientSubPoolConfig.md); \} |
+| `__namedParameters.query` | [`Literal`](../type-aliases/Literal.md) |
+| `__namedParameters.hints`? | [`Hints`](../type-aliases/Hints.md) |
+| `__namedParameters.isWrite` | `boolean` |
+| `__namedParameters.annotations` | [`QueryAnnotation`](../interfaces/QueryAnnotation.md)[] |
+| `__namedParameters.op` | `string` |
+| `__namedParameters.table` | `string` |
+| `__namedParameters.batchFactor`? | `number` |
+| `__namedParameters.subPoolConfig`? | [`PgClientSubPoolConfig`](../interfaces/PgClientSubPoolConfig.md) |
+
+#### Returns
+
+`Promise`\<`TRow`[]\>
diff --git a/docs/classes/PgError.md b/docs/classes/PgError.md
new file mode 100644
index 0000000..203e07c
--- /dev/null
+++ b/docs/classes/PgError.md
@@ -0,0 +1,71 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / PgError
+
+# Class: PgError
+
+Defined in: [src/pg/PgError.ts:3](https://github.com/clickup/ent-framework/blob/master/src/pg/PgError.ts#L3)
+
+Encapsulates the error thrown when running a Client query. The object also
+carries suggestions, what to do next.
+
+## Extends
+
+- [`ClientError`](ClientError.md)
+
+## Constructors
+
+### new PgError()
+
+> **new PgError**(`cause`, `where`, `sql`, `table`): [`PgError`](PgError.md)
+
+Defined in: [src/pg/PgError.ts:4](https://github.com/clickup/ent-framework/blob/master/src/pg/PgError.ts#L4)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `cause` | `undefined` \| `null` \| \{\} |
+| `where` | `string` |
+| `sql` | `string` |
+| `table` | `string` |
+
+#### Returns
+
+[`PgError`](PgError.md)
+
+#### Overrides
+
+[`ClientError`](ClientError.md).[`constructor`](ClientError.md#constructors)
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `cause` | `MaybeError` |
+| `postAction` | [`ClientErrorPostAction`](../type-aliases/ClientErrorPostAction.md) |
+| `kind` | [`ClientErrorKind`](../type-aliases/ClientErrorKind.md) |
+| `abbreviation` | `string` |
+| `comment?` | `string` |
+| `sql` | `string` |
+| `table` | `string` |
+
+## Methods
+
+### isFKError()
+
+> **isFKError**(`fkName`?): `boolean`
+
+Defined in: [src/pg/PgError.ts:19](https://github.com/clickup/ent-framework/blob/master/src/pg/PgError.ts#L19)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `fkName`? | `string` |
+
+#### Returns
+
+`boolean`
diff --git a/docs/classes/PgQueryCount.md b/docs/classes/PgQueryCount.md
new file mode 100644
index 0000000..eed8b41
--- /dev/null
+++ b/docs/classes/PgQueryCount.md
@@ -0,0 +1,96 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / PgQueryCount
+
+# Class: PgQueryCount\
+
+Defined in: [src/pg/PgQueryCount.ts:8](https://github.com/clickup/ent-framework/blob/master/src/pg/PgQueryCount.ts#L8)
+
+A convenient base class for most (but not all) of the queries, where the
+Runner instance is the same for different query input shapes. If the query
+doesn't fit the QueryBase framework (like PgQueryUpdate for instance where we
+have separate Runner instances for separate set of updated fields), a Query
+is used directly instead.
+
+## Extends
+
+- [`QueryBase`](QueryBase.md)\<`TTable`, [`CountInput`](../type-aliases/CountInput.md)\<`TTable`\>, `number`, [`PgClient`](PgClient.md)\>
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TTable` *extends* [`Table`](../type-aliases/Table.md) |
+
+## Constructors
+
+### new PgQueryCount()
+
+> **new PgQueryCount**\<`TTable`\>(`schema`, `input`): [`PgQueryCount`](PgQueryCount.md)\<`TTable`\>
+
+Defined in: [src/abstract/QueryBase.ts:27](https://github.com/clickup/ent-framework/blob/master/src/abstract/QueryBase.ts#L27)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `schema` | [`Schema`](Schema.md)\<`TTable`, [`UniqueKey`](../type-aliases/UniqueKey.md)\<`TTable`\>\> |
+| `input` | [`CountInput`](../type-aliases/CountInput.md)\<`TTable`\> |
+
+#### Returns
+
+[`PgQueryCount`](PgQueryCount.md)\<`TTable`\>
+
+#### Inherited from
+
+[`QueryBase`](QueryBase.md).[`constructor`](QueryBase.md#constructors)
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `schema` | [`Schema`](Schema.md)\<`TTable`, [`UniqueKey`](../type-aliases/UniqueKey.md)\<`TTable`\>\> |
+| `input` | [`CountInput`](../type-aliases/CountInput.md)\<`TTable`\> |
+
+## Accessors
+
+### IS\_WRITE
+
+#### Get Signature
+
+> **get** **IS\_WRITE**(): `boolean`
+
+Defined in: [src/abstract/QueryBase.ts:32](https://github.com/clickup/ent-framework/blob/master/src/abstract/QueryBase.ts#L32)
+
+##### Returns
+
+`boolean`
+
+#### Inherited from
+
+[`QueryBase`](QueryBase.md).[`IS_WRITE`](QueryBase.md#is_write)
+
+## Methods
+
+### run()
+
+> **run**(`client`, `annotation`): `Promise`\<`number`\>
+
+Defined in: [src/abstract/QueryBase.ts:36](https://github.com/clickup/ent-framework/blob/master/src/abstract/QueryBase.ts#L36)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `client` | [`PgClient`](PgClient.md)\<`Pool`\> |
+| `annotation` | [`QueryAnnotation`](../interfaces/QueryAnnotation.md) |
+
+#### Returns
+
+`Promise`\<`number`\>
+
+#### Inherited from
+
+[`QueryBase`](QueryBase.md).[`run`](QueryBase.md#run)
diff --git a/docs/classes/PgQueryDelete.md b/docs/classes/PgQueryDelete.md
new file mode 100644
index 0000000..c5ef8ad
--- /dev/null
+++ b/docs/classes/PgQueryDelete.md
@@ -0,0 +1,96 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / PgQueryDelete
+
+# Class: PgQueryDelete\
+
+Defined in: [src/pg/PgQueryDelete.ts:9](https://github.com/clickup/ent-framework/blob/master/src/pg/PgQueryDelete.ts#L9)
+
+A convenient base class for most (but not all) of the queries, where the
+Runner instance is the same for different query input shapes. If the query
+doesn't fit the QueryBase framework (like PgQueryUpdate for instance where we
+have separate Runner instances for separate set of updated fields), a Query
+is used directly instead.
+
+## Extends
+
+- [`QueryBase`](QueryBase.md)\<`TTable`, `string`, `boolean`, [`PgClient`](PgClient.md)\>
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TTable` *extends* [`Table`](../type-aliases/Table.md) |
+
+## Constructors
+
+### new PgQueryDelete()
+
+> **new PgQueryDelete**\<`TTable`\>(`schema`, `input`): [`PgQueryDelete`](PgQueryDelete.md)\<`TTable`\>
+
+Defined in: [src/abstract/QueryBase.ts:27](https://github.com/clickup/ent-framework/blob/master/src/abstract/QueryBase.ts#L27)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `schema` | [`Schema`](Schema.md)\<`TTable`, [`UniqueKey`](../type-aliases/UniqueKey.md)\<`TTable`\>\> |
+| `input` | `string` |
+
+#### Returns
+
+[`PgQueryDelete`](PgQueryDelete.md)\<`TTable`\>
+
+#### Inherited from
+
+[`QueryBase`](QueryBase.md).[`constructor`](QueryBase.md#constructors)
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `schema` | [`Schema`](Schema.md)\<`TTable`, [`UniqueKey`](../type-aliases/UniqueKey.md)\<`TTable`\>\> |
+| `input` | `string` |
+
+## Accessors
+
+### IS\_WRITE
+
+#### Get Signature
+
+> **get** **IS\_WRITE**(): `boolean`
+
+Defined in: [src/abstract/QueryBase.ts:32](https://github.com/clickup/ent-framework/blob/master/src/abstract/QueryBase.ts#L32)
+
+##### Returns
+
+`boolean`
+
+#### Inherited from
+
+[`QueryBase`](QueryBase.md).[`IS_WRITE`](QueryBase.md#is_write)
+
+## Methods
+
+### run()
+
+> **run**(`client`, `annotation`): `Promise`\<`boolean`\>
+
+Defined in: [src/abstract/QueryBase.ts:36](https://github.com/clickup/ent-framework/blob/master/src/abstract/QueryBase.ts#L36)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `client` | [`PgClient`](PgClient.md)\<`Pool`\> |
+| `annotation` | [`QueryAnnotation`](../interfaces/QueryAnnotation.md) |
+
+#### Returns
+
+`Promise`\<`boolean`\>
+
+#### Inherited from
+
+[`QueryBase`](QueryBase.md).[`run`](QueryBase.md#run)
diff --git a/docs/classes/PgQueryDeleteWhere.md b/docs/classes/PgQueryDeleteWhere.md
new file mode 100644
index 0000000..46f939d
--- /dev/null
+++ b/docs/classes/PgQueryDeleteWhere.md
@@ -0,0 +1,96 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / PgQueryDeleteWhere
+
+# Class: PgQueryDeleteWhere\
+
+Defined in: [src/pg/PgQueryDeleteWhere.ts:10](https://github.com/clickup/ent-framework/blob/master/src/pg/PgQueryDeleteWhere.ts#L10)
+
+A convenient base class for most (but not all) of the queries, where the
+Runner instance is the same for different query input shapes. If the query
+doesn't fit the QueryBase framework (like PgQueryUpdate for instance where we
+have separate Runner instances for separate set of updated fields), a Query
+is used directly instead.
+
+## Extends
+
+- [`QueryBase`](QueryBase.md)\<`TTable`, [`DeleteWhereInput`](../type-aliases/DeleteWhereInput.md)\<`TTable`\>, `string`[], [`PgClient`](PgClient.md)\>
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TTable` *extends* [`Table`](../type-aliases/Table.md) |
+
+## Constructors
+
+### new PgQueryDeleteWhere()
+
+> **new PgQueryDeleteWhere**\<`TTable`\>(`schema`, `input`): [`PgQueryDeleteWhere`](PgQueryDeleteWhere.md)\<`TTable`\>
+
+Defined in: [src/abstract/QueryBase.ts:27](https://github.com/clickup/ent-framework/blob/master/src/abstract/QueryBase.ts#L27)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `schema` | [`Schema`](Schema.md)\<`TTable`, [`UniqueKey`](../type-aliases/UniqueKey.md)\<`TTable`\>\> |
+| `input` | [`DeleteWhereInput`](../type-aliases/DeleteWhereInput.md)\<`TTable`\> |
+
+#### Returns
+
+[`PgQueryDeleteWhere`](PgQueryDeleteWhere.md)\<`TTable`\>
+
+#### Inherited from
+
+[`QueryBase`](QueryBase.md).[`constructor`](QueryBase.md#constructors)
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `schema` | [`Schema`](Schema.md)\<`TTable`, [`UniqueKey`](../type-aliases/UniqueKey.md)\<`TTable`\>\> |
+| `input` | [`DeleteWhereInput`](../type-aliases/DeleteWhereInput.md)\<`TTable`\> |
+
+## Accessors
+
+### IS\_WRITE
+
+#### Get Signature
+
+> **get** **IS\_WRITE**(): `boolean`
+
+Defined in: [src/abstract/QueryBase.ts:32](https://github.com/clickup/ent-framework/blob/master/src/abstract/QueryBase.ts#L32)
+
+##### Returns
+
+`boolean`
+
+#### Inherited from
+
+[`QueryBase`](QueryBase.md).[`IS_WRITE`](QueryBase.md#is_write)
+
+## Methods
+
+### run()
+
+> **run**(`client`, `annotation`): `Promise`\<`string`[]\>
+
+Defined in: [src/abstract/QueryBase.ts:36](https://github.com/clickup/ent-framework/blob/master/src/abstract/QueryBase.ts#L36)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `client` | [`PgClient`](PgClient.md)\<`Pool`\> |
+| `annotation` | [`QueryAnnotation`](../interfaces/QueryAnnotation.md) |
+
+#### Returns
+
+`Promise`\<`string`[]\>
+
+#### Inherited from
+
+[`QueryBase`](QueryBase.md).[`run`](QueryBase.md#run)
diff --git a/docs/classes/PgQueryExists.md b/docs/classes/PgQueryExists.md
new file mode 100644
index 0000000..59c0331
--- /dev/null
+++ b/docs/classes/PgQueryExists.md
@@ -0,0 +1,96 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / PgQueryExists
+
+# Class: PgQueryExists\
+
+Defined in: [src/pg/PgQueryExists.ts:8](https://github.com/clickup/ent-framework/blob/master/src/pg/PgQueryExists.ts#L8)
+
+A convenient base class for most (but not all) of the queries, where the
+Runner instance is the same for different query input shapes. If the query
+doesn't fit the QueryBase framework (like PgQueryUpdate for instance where we
+have separate Runner instances for separate set of updated fields), a Query
+is used directly instead.
+
+## Extends
+
+- [`QueryBase`](QueryBase.md)\<`TTable`, [`ExistsInput`](../type-aliases/ExistsInput.md)\<`TTable`\>, `boolean`, [`PgClient`](PgClient.md)\>
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TTable` *extends* [`Table`](../type-aliases/Table.md) |
+
+## Constructors
+
+### new PgQueryExists()
+
+> **new PgQueryExists**\<`TTable`\>(`schema`, `input`): [`PgQueryExists`](PgQueryExists.md)\<`TTable`\>
+
+Defined in: [src/abstract/QueryBase.ts:27](https://github.com/clickup/ent-framework/blob/master/src/abstract/QueryBase.ts#L27)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `schema` | [`Schema`](Schema.md)\<`TTable`, [`UniqueKey`](../type-aliases/UniqueKey.md)\<`TTable`\>\> |
+| `input` | [`ExistsInput`](../type-aliases/ExistsInput.md)\<`TTable`\> |
+
+#### Returns
+
+[`PgQueryExists`](PgQueryExists.md)\<`TTable`\>
+
+#### Inherited from
+
+[`QueryBase`](QueryBase.md).[`constructor`](QueryBase.md#constructors)
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `schema` | [`Schema`](Schema.md)\<`TTable`, [`UniqueKey`](../type-aliases/UniqueKey.md)\<`TTable`\>\> |
+| `input` | [`ExistsInput`](../type-aliases/ExistsInput.md)\<`TTable`\> |
+
+## Accessors
+
+### IS\_WRITE
+
+#### Get Signature
+
+> **get** **IS\_WRITE**(): `boolean`
+
+Defined in: [src/abstract/QueryBase.ts:32](https://github.com/clickup/ent-framework/blob/master/src/abstract/QueryBase.ts#L32)
+
+##### Returns
+
+`boolean`
+
+#### Inherited from
+
+[`QueryBase`](QueryBase.md).[`IS_WRITE`](QueryBase.md#is_write)
+
+## Methods
+
+### run()
+
+> **run**(`client`, `annotation`): `Promise`\<`boolean`\>
+
+Defined in: [src/abstract/QueryBase.ts:36](https://github.com/clickup/ent-framework/blob/master/src/abstract/QueryBase.ts#L36)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `client` | [`PgClient`](PgClient.md)\<`Pool`\> |
+| `annotation` | [`QueryAnnotation`](../interfaces/QueryAnnotation.md) |
+
+#### Returns
+
+`Promise`\<`boolean`\>
+
+#### Inherited from
+
+[`QueryBase`](QueryBase.md).[`run`](QueryBase.md#run)
diff --git a/docs/classes/PgQueryIDGen.md b/docs/classes/PgQueryIDGen.md
new file mode 100644
index 0000000..3df5903
--- /dev/null
+++ b/docs/classes/PgQueryIDGen.md
@@ -0,0 +1,96 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / PgQueryIDGen
+
+# Class: PgQueryIDGen\
+
+Defined in: [src/pg/PgQueryIDGen.ts:10](https://github.com/clickup/ent-framework/blob/master/src/pg/PgQueryIDGen.ts#L10)
+
+A convenient base class for most (but not all) of the queries, where the
+Runner instance is the same for different query input shapes. If the query
+doesn't fit the QueryBase framework (like PgQueryUpdate for instance where we
+have separate Runner instances for separate set of updated fields), a Query
+is used directly instead.
+
+## Extends
+
+- [`QueryBase`](QueryBase.md)\<`TTable`, `void`, `string`, [`PgClient`](PgClient.md)\>
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TTable` *extends* [`Table`](../type-aliases/Table.md) |
+
+## Constructors
+
+### new PgQueryIDGen()
+
+> **new PgQueryIDGen**\<`TTable`\>(`schema`, `input`): [`PgQueryIDGen`](PgQueryIDGen.md)\<`TTable`\>
+
+Defined in: [src/abstract/QueryBase.ts:27](https://github.com/clickup/ent-framework/blob/master/src/abstract/QueryBase.ts#L27)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `schema` | [`Schema`](Schema.md)\<`TTable`, [`UniqueKey`](../type-aliases/UniqueKey.md)\<`TTable`\>\> |
+| `input` | `void` |
+
+#### Returns
+
+[`PgQueryIDGen`](PgQueryIDGen.md)\<`TTable`\>
+
+#### Inherited from
+
+[`QueryBase`](QueryBase.md).[`constructor`](QueryBase.md#constructors)
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `schema` | [`Schema`](Schema.md)\<`TTable`, [`UniqueKey`](../type-aliases/UniqueKey.md)\<`TTable`\>\> |
+| `input` | `void` |
+
+## Accessors
+
+### IS\_WRITE
+
+#### Get Signature
+
+> **get** **IS\_WRITE**(): `boolean`
+
+Defined in: [src/abstract/QueryBase.ts:32](https://github.com/clickup/ent-framework/blob/master/src/abstract/QueryBase.ts#L32)
+
+##### Returns
+
+`boolean`
+
+#### Inherited from
+
+[`QueryBase`](QueryBase.md).[`IS_WRITE`](QueryBase.md#is_write)
+
+## Methods
+
+### run()
+
+> **run**(`client`, `annotation`): `Promise`\<`string`\>
+
+Defined in: [src/abstract/QueryBase.ts:36](https://github.com/clickup/ent-framework/blob/master/src/abstract/QueryBase.ts#L36)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `client` | [`PgClient`](PgClient.md)\<`Pool`\> |
+| `annotation` | [`QueryAnnotation`](../interfaces/QueryAnnotation.md) |
+
+#### Returns
+
+`Promise`\<`string`\>
+
+#### Inherited from
+
+[`QueryBase`](QueryBase.md).[`run`](QueryBase.md#run)
diff --git a/docs/classes/PgQueryInsert.md b/docs/classes/PgQueryInsert.md
new file mode 100644
index 0000000..431f23f
--- /dev/null
+++ b/docs/classes/PgQueryInsert.md
@@ -0,0 +1,96 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / PgQueryInsert
+
+# Class: PgQueryInsert\
+
+Defined in: [src/pg/PgQueryInsert.ts:9](https://github.com/clickup/ent-framework/blob/master/src/pg/PgQueryInsert.ts#L9)
+
+A convenient base class for most (but not all) of the queries, where the
+Runner instance is the same for different query input shapes. If the query
+doesn't fit the QueryBase framework (like PgQueryUpdate for instance where we
+have separate Runner instances for separate set of updated fields), a Query
+is used directly instead.
+
+## Extends
+
+- [`QueryBase`](QueryBase.md)\<`TTable`, [`InsertInput`](../type-aliases/InsertInput.md)\<`TTable`\>, `string` \| `null`, [`PgClient`](PgClient.md)\>
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TTable` *extends* [`Table`](../type-aliases/Table.md) |
+
+## Constructors
+
+### new PgQueryInsert()
+
+> **new PgQueryInsert**\<`TTable`\>(`schema`, `input`): [`PgQueryInsert`](PgQueryInsert.md)\<`TTable`\>
+
+Defined in: [src/abstract/QueryBase.ts:27](https://github.com/clickup/ent-framework/blob/master/src/abstract/QueryBase.ts#L27)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `schema` | [`Schema`](Schema.md)\<`TTable`, [`UniqueKey`](../type-aliases/UniqueKey.md)\<`TTable`\>\> |
+| `input` | [`InsertInput`](../type-aliases/InsertInput.md)\<`TTable`\> |
+
+#### Returns
+
+[`PgQueryInsert`](PgQueryInsert.md)\<`TTable`\>
+
+#### Inherited from
+
+[`QueryBase`](QueryBase.md).[`constructor`](QueryBase.md#constructors)
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `schema` | [`Schema`](Schema.md)\<`TTable`, [`UniqueKey`](../type-aliases/UniqueKey.md)\<`TTable`\>\> |
+| `input` | [`InsertInput`](../type-aliases/InsertInput.md)\<`TTable`\> |
+
+## Accessors
+
+### IS\_WRITE
+
+#### Get Signature
+
+> **get** **IS\_WRITE**(): `boolean`
+
+Defined in: [src/abstract/QueryBase.ts:32](https://github.com/clickup/ent-framework/blob/master/src/abstract/QueryBase.ts#L32)
+
+##### Returns
+
+`boolean`
+
+#### Inherited from
+
+[`QueryBase`](QueryBase.md).[`IS_WRITE`](QueryBase.md#is_write)
+
+## Methods
+
+### run()
+
+> **run**(`client`, `annotation`): `Promise`\<`null` \| `string`\>
+
+Defined in: [src/abstract/QueryBase.ts:36](https://github.com/clickup/ent-framework/blob/master/src/abstract/QueryBase.ts#L36)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `client` | [`PgClient`](PgClient.md)\<`Pool`\> |
+| `annotation` | [`QueryAnnotation`](../interfaces/QueryAnnotation.md) |
+
+#### Returns
+
+`Promise`\<`null` \| `string`\>
+
+#### Inherited from
+
+[`QueryBase`](QueryBase.md).[`run`](QueryBase.md#run)
diff --git a/docs/classes/PgQueryLoad.md b/docs/classes/PgQueryLoad.md
new file mode 100644
index 0000000..9796e02
--- /dev/null
+++ b/docs/classes/PgQueryLoad.md
@@ -0,0 +1,96 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / PgQueryLoad
+
+# Class: PgQueryLoad\
+
+Defined in: [src/pg/PgQueryLoad.ts:9](https://github.com/clickup/ent-framework/blob/master/src/pg/PgQueryLoad.ts#L9)
+
+A convenient base class for most (but not all) of the queries, where the
+Runner instance is the same for different query input shapes. If the query
+doesn't fit the QueryBase framework (like PgQueryUpdate for instance where we
+have separate Runner instances for separate set of updated fields), a Query
+is used directly instead.
+
+## Extends
+
+- [`QueryBase`](QueryBase.md)\<`TTable`, `string`, [`Row`](../type-aliases/Row.md)\<`TTable`\> \| `null`, [`PgClient`](PgClient.md)\>
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TTable` *extends* [`Table`](../type-aliases/Table.md) |
+
+## Constructors
+
+### new PgQueryLoad()
+
+> **new PgQueryLoad**\<`TTable`\>(`schema`, `input`): [`PgQueryLoad`](PgQueryLoad.md)\<`TTable`\>
+
+Defined in: [src/abstract/QueryBase.ts:27](https://github.com/clickup/ent-framework/blob/master/src/abstract/QueryBase.ts#L27)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `schema` | [`Schema`](Schema.md)\<`TTable`, [`UniqueKey`](../type-aliases/UniqueKey.md)\<`TTable`\>\> |
+| `input` | `string` |
+
+#### Returns
+
+[`PgQueryLoad`](PgQueryLoad.md)\<`TTable`\>
+
+#### Inherited from
+
+[`QueryBase`](QueryBase.md).[`constructor`](QueryBase.md#constructors)
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `schema` | [`Schema`](Schema.md)\<`TTable`, [`UniqueKey`](../type-aliases/UniqueKey.md)\<`TTable`\>\> |
+| `input` | `string` |
+
+## Accessors
+
+### IS\_WRITE
+
+#### Get Signature
+
+> **get** **IS\_WRITE**(): `boolean`
+
+Defined in: [src/abstract/QueryBase.ts:32](https://github.com/clickup/ent-framework/blob/master/src/abstract/QueryBase.ts#L32)
+
+##### Returns
+
+`boolean`
+
+#### Inherited from
+
+[`QueryBase`](QueryBase.md).[`IS_WRITE`](QueryBase.md#is_write)
+
+## Methods
+
+### run()
+
+> **run**(`client`, `annotation`): `Promise`\<`null` \| [`Row`](../type-aliases/Row.md)\<`TTable`\>\>
+
+Defined in: [src/abstract/QueryBase.ts:36](https://github.com/clickup/ent-framework/blob/master/src/abstract/QueryBase.ts#L36)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `client` | [`PgClient`](PgClient.md)\<`Pool`\> |
+| `annotation` | [`QueryAnnotation`](../interfaces/QueryAnnotation.md) |
+
+#### Returns
+
+`Promise`\<`null` \| [`Row`](../type-aliases/Row.md)\<`TTable`\>\>
+
+#### Inherited from
+
+[`QueryBase`](QueryBase.md).[`run`](QueryBase.md#run)
diff --git a/docs/classes/PgQueryLoadBy.md b/docs/classes/PgQueryLoadBy.md
new file mode 100644
index 0000000..f7811f3
--- /dev/null
+++ b/docs/classes/PgQueryLoadBy.md
@@ -0,0 +1,97 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / PgQueryLoadBy
+
+# Class: PgQueryLoadBy\
+
+Defined in: [src/pg/PgQueryLoadBy.ts:8](https://github.com/clickup/ent-framework/blob/master/src/pg/PgQueryLoadBy.ts#L8)
+
+A convenient base class for most (but not all) of the queries, where the
+Runner instance is the same for different query input shapes. If the query
+doesn't fit the QueryBase framework (like PgQueryUpdate for instance where we
+have separate Runner instances for separate set of updated fields), a Query
+is used directly instead.
+
+## Extends
+
+- [`QueryBase`](QueryBase.md)\<`TTable`, [`LoadByInput`](../type-aliases/LoadByInput.md)\<`TTable`, `TUniqueKey`\>, [`Row`](../type-aliases/Row.md)\<`TTable`\> \| `null`, [`PgClient`](PgClient.md)\>
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TTable` *extends* [`Table`](../type-aliases/Table.md) |
+| `TUniqueKey` *extends* [`UniqueKey`](../type-aliases/UniqueKey.md)\<`TTable`\> |
+
+## Constructors
+
+### new PgQueryLoadBy()
+
+> **new PgQueryLoadBy**\<`TTable`, `TUniqueKey`\>(`schema`, `input`): [`PgQueryLoadBy`](PgQueryLoadBy.md)\<`TTable`, `TUniqueKey`\>
+
+Defined in: [src/abstract/QueryBase.ts:27](https://github.com/clickup/ent-framework/blob/master/src/abstract/QueryBase.ts#L27)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `schema` | [`Schema`](Schema.md)\<`TTable`, [`UniqueKey`](../type-aliases/UniqueKey.md)\<`TTable`\>\> |
+| `input` | [`LoadByInput`](../type-aliases/LoadByInput.md)\<`TTable`, `TUniqueKey`\> |
+
+#### Returns
+
+[`PgQueryLoadBy`](PgQueryLoadBy.md)\<`TTable`, `TUniqueKey`\>
+
+#### Inherited from
+
+[`QueryBase`](QueryBase.md).[`constructor`](QueryBase.md#constructors)
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `schema` | [`Schema`](Schema.md)\<`TTable`, [`UniqueKey`](../type-aliases/UniqueKey.md)\<`TTable`\>\> |
+| `input` | [`LoadByInput`](../type-aliases/LoadByInput.md)\<`TTable`, `TUniqueKey`\> |
+
+## Accessors
+
+### IS\_WRITE
+
+#### Get Signature
+
+> **get** **IS\_WRITE**(): `boolean`
+
+Defined in: [src/abstract/QueryBase.ts:32](https://github.com/clickup/ent-framework/blob/master/src/abstract/QueryBase.ts#L32)
+
+##### Returns
+
+`boolean`
+
+#### Inherited from
+
+[`QueryBase`](QueryBase.md).[`IS_WRITE`](QueryBase.md#is_write)
+
+## Methods
+
+### run()
+
+> **run**(`client`, `annotation`): `Promise`\<`null` \| [`Row`](../type-aliases/Row.md)\<`TTable`\>\>
+
+Defined in: [src/abstract/QueryBase.ts:36](https://github.com/clickup/ent-framework/blob/master/src/abstract/QueryBase.ts#L36)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `client` | [`PgClient`](PgClient.md)\<`Pool`\> |
+| `annotation` | [`QueryAnnotation`](../interfaces/QueryAnnotation.md) |
+
+#### Returns
+
+`Promise`\<`null` \| [`Row`](../type-aliases/Row.md)\<`TTable`\>\>
+
+#### Inherited from
+
+[`QueryBase`](QueryBase.md).[`run`](QueryBase.md#run)
diff --git a/docs/classes/PgQuerySelect.md b/docs/classes/PgQuerySelect.md
new file mode 100644
index 0000000..5875075
--- /dev/null
+++ b/docs/classes/PgQuerySelect.md
@@ -0,0 +1,96 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / PgQuerySelect
+
+# Class: PgQuerySelect\
+
+Defined in: [src/pg/PgQuerySelect.ts:35](https://github.com/clickup/ent-framework/blob/master/src/pg/PgQuerySelect.ts#L35)
+
+A convenient base class for most (but not all) of the queries, where the
+Runner instance is the same for different query input shapes. If the query
+doesn't fit the QueryBase framework (like PgQueryUpdate for instance where we
+have separate Runner instances for separate set of updated fields), a Query
+is used directly instead.
+
+## Extends
+
+- [`QueryBase`](QueryBase.md)\<`TTable`, [`SelectInput`](../type-aliases/SelectInput.md)\<`TTable`\>, [`Row`](../type-aliases/Row.md)\<`TTable`\>[], [`PgClient`](PgClient.md)\>
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TTable` *extends* [`Table`](../type-aliases/Table.md) |
+
+## Constructors
+
+### new PgQuerySelect()
+
+> **new PgQuerySelect**\<`TTable`\>(`schema`, `input`): [`PgQuerySelect`](PgQuerySelect.md)\<`TTable`\>
+
+Defined in: [src/abstract/QueryBase.ts:27](https://github.com/clickup/ent-framework/blob/master/src/abstract/QueryBase.ts#L27)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `schema` | [`Schema`](Schema.md)\<`TTable`, [`UniqueKey`](../type-aliases/UniqueKey.md)\<`TTable`\>\> |
+| `input` | [`SelectInput`](../type-aliases/SelectInput.md)\<`TTable`\> |
+
+#### Returns
+
+[`PgQuerySelect`](PgQuerySelect.md)\<`TTable`\>
+
+#### Inherited from
+
+[`QueryBase`](QueryBase.md).[`constructor`](QueryBase.md#constructors)
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `schema` | [`Schema`](Schema.md)\<`TTable`, [`UniqueKey`](../type-aliases/UniqueKey.md)\<`TTable`\>\> |
+| `input` | [`SelectInput`](../type-aliases/SelectInput.md)\<`TTable`\> |
+
+## Accessors
+
+### IS\_WRITE
+
+#### Get Signature
+
+> **get** **IS\_WRITE**(): `boolean`
+
+Defined in: [src/abstract/QueryBase.ts:32](https://github.com/clickup/ent-framework/blob/master/src/abstract/QueryBase.ts#L32)
+
+##### Returns
+
+`boolean`
+
+#### Inherited from
+
+[`QueryBase`](QueryBase.md).[`IS_WRITE`](QueryBase.md#is_write)
+
+## Methods
+
+### run()
+
+> **run**(`client`, `annotation`): `Promise`\<[`Row`](../type-aliases/Row.md)\<`TTable`\>[]\>
+
+Defined in: [src/pg/PgQuerySelect.ts:44](https://github.com/clickup/ent-framework/blob/master/src/pg/PgQuerySelect.ts#L44)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `client` | [`PgClient`](PgClient.md)\<`Pool`\> |
+| `annotation` | [`QueryAnnotation`](../interfaces/QueryAnnotation.md) |
+
+#### Returns
+
+`Promise`\<[`Row`](../type-aliases/Row.md)\<`TTable`\>[]\>
+
+#### Overrides
+
+[`QueryBase`](QueryBase.md).[`run`](QueryBase.md#run)
diff --git a/docs/classes/PgQuerySelectBy.md b/docs/classes/PgQuerySelectBy.md
new file mode 100644
index 0000000..84eb34f
--- /dev/null
+++ b/docs/classes/PgQuerySelectBy.md
@@ -0,0 +1,73 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / PgQuerySelectBy
+
+# Class: PgQuerySelectBy\
+
+Defined in: [src/pg/PgQuerySelectBy.ts:14](https://github.com/clickup/ent-framework/blob/master/src/pg/PgQuerySelectBy.ts#L14)
+
+A very lean interface for a Query. In practice each query is so different
+that this interface is the only common part of them all.
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TTable` *extends* [`Table`](../type-aliases/Table.md) |
+| `TUniqueKey` *extends* [`UniqueKey`](../type-aliases/UniqueKey.md)\<`TTable`\> |
+
+## Implements
+
+- [`Query`](../interfaces/Query.md)\<[`Row`](../type-aliases/Row.md)\<`TTable`\>[]\>
+
+## Constructors
+
+### new PgQuerySelectBy()
+
+> **new PgQuerySelectBy**\<`TTable`, `TUniqueKey`\>(`schema`, `input`): [`PgQuerySelectBy`](PgQuerySelectBy.md)\<`TTable`, `TUniqueKey`\>
+
+Defined in: [src/pg/PgQuerySelectBy.ts:20](https://github.com/clickup/ent-framework/blob/master/src/pg/PgQuerySelectBy.ts#L20)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `schema` | [`Schema`](Schema.md)\<`TTable`, [`UniqueKey`](../type-aliases/UniqueKey.md)\<`TTable`\>\> |
+| `input` | [`SelectByInput`](../type-aliases/SelectByInput.md)\<`TTable`, `TUniqueKey`\> |
+
+#### Returns
+
+[`PgQuerySelectBy`](PgQuerySelectBy.md)\<`TTable`, `TUniqueKey`\>
+
+## Properties
+
+| Property | Type | Default value |
+| ------ | ------ | ------ |
+| `IS_WRITE` | `false` | `false` |
+| `schema` | [`Schema`](Schema.md)\<`TTable`, [`UniqueKey`](../type-aliases/UniqueKey.md)\<`TTable`\>\> | `undefined` |
+| `input` | [`LoadByInput`](../type-aliases/LoadByInput.md)\<`TTable`, `TuplePrefixes`\<`TUniqueKey`\>\> | `undefined` |
+
+## Methods
+
+### run()
+
+> **run**(`client`, `annotation`): `Promise`\<[`Row`](../type-aliases/Row.md)\<`TTable`\>[]\>
+
+Defined in: [src/pg/PgQuerySelectBy.ts:25](https://github.com/clickup/ent-framework/blob/master/src/pg/PgQuerySelectBy.ts#L25)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `client` | [`PgClient`](PgClient.md)\<`Pool`\> |
+| `annotation` | [`QueryAnnotation`](../interfaces/QueryAnnotation.md) |
+
+#### Returns
+
+`Promise`\<[`Row`](../type-aliases/Row.md)\<`TTable`\>[]\>
+
+#### Implementation of
+
+[`Query`](../interfaces/Query.md).[`run`](../interfaces/Query.md#run)
diff --git a/docs/classes/PgQueryUpdate.md b/docs/classes/PgQueryUpdate.md
new file mode 100644
index 0000000..d01696f
--- /dev/null
+++ b/docs/classes/PgQueryUpdate.md
@@ -0,0 +1,73 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / PgQueryUpdate
+
+# Class: PgQueryUpdate\
+
+Defined in: [src/pg/PgQueryUpdate.ts:10](https://github.com/clickup/ent-framework/blob/master/src/pg/PgQueryUpdate.ts#L10)
+
+A very lean interface for a Query. In practice each query is so different
+that this interface is the only common part of them all.
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TTable` *extends* [`Table`](../type-aliases/Table.md) |
+
+## Implements
+
+- [`Query`](../interfaces/Query.md)\<`boolean`\>
+
+## Constructors
+
+### new PgQueryUpdate()
+
+> **new PgQueryUpdate**\<`TTable`\>(`schema`, `id`, `input`): [`PgQueryUpdate`](PgQueryUpdate.md)\<`TTable`\>
+
+Defined in: [src/pg/PgQueryUpdate.ts:15](https://github.com/clickup/ent-framework/blob/master/src/pg/PgQueryUpdate.ts#L15)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `schema` | [`Schema`](Schema.md)\<`TTable`, [`UniqueKey`](../type-aliases/UniqueKey.md)\<`TTable`\>\> |
+| `id` | `string` |
+| `input` | [`UpdateInput`](../type-aliases/UpdateInput.md)\<`TTable`\> |
+
+#### Returns
+
+[`PgQueryUpdate`](PgQueryUpdate.md)\<`TTable`\>
+
+## Properties
+
+| Property | Type | Default value |
+| ------ | ------ | ------ |
+| `input` | \{ \[K in string \| number \| symbol\]?: Value\ \} & `object` & `object` | `undefined` |
+| `IS_WRITE` | `true` | `true` |
+| `schema` | [`Schema`](Schema.md)\<`TTable`, [`UniqueKey`](../type-aliases/UniqueKey.md)\<`TTable`\>\> | `undefined` |
+
+## Methods
+
+### run()
+
+> **run**(`client`, `annotation`): `Promise`\<`boolean`\>
+
+Defined in: [src/pg/PgQueryUpdate.ts:25](https://github.com/clickup/ent-framework/blob/master/src/pg/PgQueryUpdate.ts#L25)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `client` | [`PgClient`](PgClient.md)\<`Pool`\> |
+| `annotation` | [`QueryAnnotation`](../interfaces/QueryAnnotation.md) |
+
+#### Returns
+
+`Promise`\<`boolean`\>
+
+#### Implementation of
+
+[`Query`](../interfaces/Query.md).[`run`](../interfaces/Query.md#run)
diff --git a/docs/classes/PgQueryUpsert.md b/docs/classes/PgQueryUpsert.md
new file mode 100644
index 0000000..00b58f8
--- /dev/null
+++ b/docs/classes/PgQueryUpsert.md
@@ -0,0 +1,72 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / PgQueryUpsert
+
+# Class: PgQueryUpsert\
+
+Defined in: [src/pg/PgQueryUpsert.ts:15](https://github.com/clickup/ent-framework/blob/master/src/pg/PgQueryUpsert.ts#L15)
+
+A very lean interface for a Query. In practice each query is so different
+that this interface is the only common part of them all.
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TTable` *extends* [`Table`](../type-aliases/Table.md) |
+
+## Implements
+
+- [`Query`](../interfaces/Query.md)\<`string`\>
+
+## Constructors
+
+### new PgQueryUpsert()
+
+> **new PgQueryUpsert**\<`TTable`\>(`schema`, `input`): [`PgQueryUpsert`](PgQueryUpsert.md)\<`TTable`\>
+
+Defined in: [src/pg/PgQueryUpsert.ts:18](https://github.com/clickup/ent-framework/blob/master/src/pg/PgQueryUpsert.ts#L18)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `schema` | [`Schema`](Schema.md)\<`TTable`, [`UniqueKey`](../type-aliases/UniqueKey.md)\<`TTable`\>\> |
+| `input` | [`InsertInput`](../type-aliases/InsertInput.md)\<`TTable`\> |
+
+#### Returns
+
+[`PgQueryUpsert`](PgQueryUpsert.md)\<`TTable`\>
+
+## Properties
+
+| Property | Type | Default value |
+| ------ | ------ | ------ |
+| `IS_WRITE` | `true` | `true` |
+| `schema` | [`Schema`](Schema.md)\<`TTable`, [`UniqueKey`](../type-aliases/UniqueKey.md)\<`TTable`\>\> | `undefined` |
+| `input` | [`InsertInput`](../type-aliases/InsertInput.md)\<`TTable`\> | `undefined` |
+
+## Methods
+
+### run()
+
+> **run**(`client`, `annotation`): `Promise`\<`string`\>
+
+Defined in: [src/pg/PgQueryUpsert.ts:23](https://github.com/clickup/ent-framework/blob/master/src/pg/PgQueryUpsert.ts#L23)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `client` | [`PgClient`](PgClient.md)\<`Pool`\> |
+| `annotation` | [`QueryAnnotation`](../interfaces/QueryAnnotation.md) |
+
+#### Returns
+
+`Promise`\<`string`\>
+
+#### Implementation of
+
+[`Query`](../interfaces/Query.md).[`run`](../interfaces/Query.md#run)
diff --git a/docs/classes/PgRunner.md b/docs/classes/PgRunner.md
new file mode 100644
index 0000000..dc4829d
--- /dev/null
+++ b/docs/classes/PgRunner.md
@@ -0,0 +1,706 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / PgRunner
+
+# Class: `abstract` PgRunner\
+
+Defined in: [src/pg/PgRunner.ts:52](https://github.com/clickup/ent-framework/blob/master/src/pg/PgRunner.ts#L52)
+
+A convenient pile of helper methods usable by most of PgQuery* classes. In
+some sense it's an anti-pattern, but still reduces the boilerplate.
+
+PgRunner is also responsible for stringifying the values passed to the
+queries and parsing values returned from the DB according to the field types
+specs.
+
+## Extends
+
+- [`Runner`](Runner.md)\<`TInput`, `TOutput`\>
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TTable` *extends* [`Table`](../type-aliases/Table.md) |
+| `TInput` |
+| `TOutput` |
+
+## Constructors
+
+### new PgRunner()
+
+> **new PgRunner**\<`TTable`, `TInput`, `TOutput`\>(`schema`, `client`): [`PgRunner`](PgRunner.md)\<`TTable`, `TInput`, `TOutput`\>
+
+Defined in: [src/pg/PgRunner.ts:547](https://github.com/clickup/ent-framework/blob/master/src/pg/PgRunner.ts#L547)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `schema` | [`Schema`](Schema.md)\<`TTable`, [`UniqueKey`](../type-aliases/UniqueKey.md)\<`TTable`\>\> |
+| `client` | [`PgClient`](PgClient.md)\<`Pool`\> |
+
+#### Returns
+
+[`PgRunner`](PgRunner.md)\<`TTable`, `TInput`, `TOutput`\>
+
+#### Overrides
+
+[`Runner`](Runner.md).[`constructor`](Runner.md#constructors)
+
+## Properties
+
+| Property | Type | Description |
+| ------ | ------ | ------ |
+| `IS_WRITE` | `boolean` | If true, it's a write operation. |
+| `op` | `string` | Operation name for logging purposes. |
+| `maxBatchSize` | `number` | Maximum batch size for this type of operations. |
+| `default` | `TOutput` | In case undefined is returned from batching, this value will be returned instead. |
+| `name` | `string` | - |
+| `constructor` | *typeof* [`PgRunner`](PgRunner.md) | The initial value of Object.prototype.constructor is the standard built-in Object constructor. |
+| `schema` | [`Schema`](Schema.md)\<`TTable`, [`UniqueKey`](../type-aliases/UniqueKey.md)\<`TTable`\>\> | - |
+
+## Methods
+
+### runSingle()
+
+> `abstract` **runSingle**(`input`, `annotations`): `Promise`\<`undefined` \| `TOutput`\>
+
+Defined in: [src/abstract/Runner.ts:30](https://github.com/clickup/ent-framework/blob/master/src/abstract/Runner.ts#L30)
+
+Method runSingle is to e.g. produce simple DB requests when we have only
+one input to process, not many.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `input` | `TInput` |
+| `annotations` | [`QueryAnnotation`](../interfaces/QueryAnnotation.md)[] |
+
+#### Returns
+
+`Promise`\<`undefined` \| `TOutput`\>
+
+#### Inherited from
+
+[`Runner`](Runner.md).[`runSingle`](Runner.md#runsingle)
+
+***
+
+### runBatch()?
+
+> `abstract` `optional` **runBatch**(`inputs`, `annotations`): `Promise`\<`Map`\<`string`, `TOutput`\>\>
+
+Defined in: [src/abstract/Runner.ts:39](https://github.com/clickup/ent-framework/blob/master/src/abstract/Runner.ts#L39)
+
+Typically issues complex queries with magic. If the method is not defined,
+then the runner doesn't support batching, so only runSingle() will be used.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `inputs` | `Map`\<`string`, `TInput`\> |
+| `annotations` | [`QueryAnnotation`](../interfaces/QueryAnnotation.md)[] |
+
+#### Returns
+
+`Promise`\<`Map`\<`string`, `TOutput`\>\>
+
+#### Inherited from
+
+[`Runner`](Runner.md).[`runBatch`](Runner.md#runbatch)
+
+***
+
+### key()
+
+> **key**(`_input`): `string`
+
+Defined in: [src/abstract/Runner.ts:77](https://github.com/clickup/ent-framework/blob/master/src/abstract/Runner.ts#L77)
+
+Returns a batch-dedupping key for the input. By default, no dedupping is
+performed (i.e. all inputs are processed individually and not collapsed
+into one input; e.g. this is needed for inserts).
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `_input` | `TInput` |
+
+#### Returns
+
+`string`
+
+#### Inherited from
+
+[`Runner`](Runner.md).[`key`](Runner.md#key)
+
+***
+
+### clientQuery()
+
+> `protected` **clientQuery**\<`TOutput`\>(`sql`, `annotations`, `batchFactor`, `hints`?): `Promise`\<`TOutput`[]\>
+
+Defined in: [src/pg/PgRunner.ts:66](https://github.com/clickup/ent-framework/blob/master/src/pg/PgRunner.ts#L66)
+
+#### Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TOutput` *extends* `object` |
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `sql` | `string` |
+| `annotations` | [`QueryAnnotation`](../interfaces/QueryAnnotation.md)[] |
+| `batchFactor` | `number` |
+| `hints`? | [`Hints`](../type-aliases/Hints.md) |
+
+#### Returns
+
+`Promise`\<`TOutput`[]\>
+
+***
+
+### fmt()
+
+> `protected` **fmt**(`template`, `args`): `string`
+
+Defined in: [src/pg/PgRunner.ts:105](https://github.com/clickup/ent-framework/blob/master/src/pg/PgRunner.ts#L105)
+
+Formats prefixes/suffixes of various compound SQL clauses. Don't use on
+performance-critical path!
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `template` | `string` |
+| `args` | \{ `fields`: [`FieldAliased`](../type-aliases/FieldAliased.md)\<`TTable`\>[]; `autos`: `Partial`\<`Record`\<`string`, `string`\>\>; `normalize`: `boolean`; \} |
+| `args.fields`? | [`FieldAliased`](../type-aliases/FieldAliased.md)\<`TTable`\>[] |
+| `args.autos`? | `Partial`\<`Record`\<`string`, `string`\>\> |
+| `args.normalize`? | `boolean` |
+
+#### Returns
+
+`string`
+
+***
+
+### escapeValue()
+
+> `protected` **escapeValue**(`field`, `value`): `string`
+
+Defined in: [src/pg/PgRunner.ts:197](https://github.com/clickup/ent-framework/blob/master/src/pg/PgRunner.ts#L197)
+
+Escapes a value at runtime using the codegen functions created above. We
+use escapers table and the codegen for the following reasons:
+1. We want to be sure that we know in advance, how to escape all table
+ fields (and not fail at runtime).
+2. We want to make createEscapeCode() the single source of truth about
+ fields escaping, even at runtime.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `field` | [`Field`](../type-aliases/Field.md)\<`TTable`\> |
+| `value` | `unknown` |
+
+#### Returns
+
+`string`
+
+***
+
+### escapeField()
+
+> `protected` **escapeField**(`info`, `__namedParameters`): `string`
+
+Defined in: [src/pg/PgRunner.ts:213](https://github.com/clickup/ent-framework/blob/master/src/pg/PgRunner.ts#L213)
+
+Escapes field name identifier.
+- In case it's a composite primary key, returns its `ROW(f1,f2,...)`
+ representation.
+- A field may be aliased, e.g. if `{ field: "abc", alias: "$cas.abc" }` is
+ passed, then the returned value will be `"$cas.abc"`. Basically, `field`
+ name is used only to verify that such field is presented in the schema.
+- If `autos` is passed, and the field is in it, returns the value of the
+ `autos` record for the field. This is used to inject an
+ autoInsert/autoUpdate SQL expression at the position of some field.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `info` | [`FieldAliased`](../type-aliases/FieldAliased.md)\<`TTable`\> |
+| `__namedParameters` | \{ `withTable`: `string`; `normalize`: `boolean`; \} |
+| `__namedParameters.withTable`? | `string` |
+| `__namedParameters.normalize`? | `boolean` |
+
+#### Returns
+
+`string`
+
+***
+
+### createWithBuilder()
+
+> `protected` **createWithBuilder**(`__namedParameters`): `object`
+
+Defined in: [src/pg/PgRunner.ts:251](https://github.com/clickup/ent-framework/blob/master/src/pg/PgRunner.ts#L251)
+
+Returns a newly created JS function which, when called with a row set,
+returns the following SQL clause:
+
+```
+WITH rows(id, a, b, _key) AS (VALUES
+ ((NULL::tbl).id, (NULL::tbl).a, (NULL::tbl).b, 'k0'),
+ ('123', 'xyz', 'nn', 'kSome'),
+ ('456', 'abc', 'nn', 'kOther'),
+ ...
+)
+{suffix}
+```
+
+For composite primary key, its parts (fields) are always prepended. The set
+of columns is passed in specs.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `__namedParameters` | \{ `fields`: readonly [`FieldAliased`](../type-aliases/FieldAliased.md)\<`TTable`\>[]; `skipSorting`: `boolean`; `suffix`: `string`; \} |
+| `__namedParameters.fields` | readonly [`FieldAliased`](../type-aliases/FieldAliased.md)\<`TTable`\>[] |
+| `__namedParameters.skipSorting`? | `boolean` |
+| `__namedParameters.suffix` | `string` |
+
+#### Returns
+
+`object`
+
+##### prefix
+
+> **prefix**: `string`
+
+##### func()
+
+> **func**: (`entries`) => `string`
+
+###### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `entries` | `Iterable`\<\[`string`, `object`\], `any`, `any`\> |
+
+###### Returns
+
+`string`
+
+##### suffix
+
+> **suffix**: `string`
+
+***
+
+### createValuesBuilder()
+
+> `protected` **createValuesBuilder**\<`TInput`\>(`__namedParameters`): `object`
+
+Defined in: [src/pg/PgRunner.ts:331](https://github.com/clickup/ent-framework/blob/master/src/pg/PgRunner.ts#L331)
+
+Returns a newly created JS function which, when called with a row set,
+returns the following SQL clause:
+
+When called with withKey=true:
+
+```
+('123', 'xyz', 'nn', 'kSome'),
+('456', 'abc', 'nn', 'kOther'),
+```
+
+When called with withKey=true, but with empty `fields`:
+
+```
+('kSome'),
+('kOther'),
+```
+
+When called with withKey=false:
+
+```
+('123', 'xyz', 'nn'),
+('456', 'abc', 'nn'),
+```
+
+The list of column names is passed in `fields`.
+
+When the builder func is called, the actual values for some field in a row
+is extracted from the same-named prop of the row, but if `{ field, alias }`
+object is passed in `fields` array, then the value is extracted from the
+`alias` sub-prop of the row. This is used to e.g. access `row.$cas.blah`
+value for a field named blah (in this case, `alias="$cas"`).
+
+Notice that either a simple primary key or a composite primary key columns
+are always prepended to the list of values since it makes no sense to
+generate VALUES clause without exact identification of the destination.
+
+#### Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TInput` *extends* `object` |
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `__namedParameters` | \{ `prefix`: `string`; `indent`: `string`; `fields`: readonly [`FieldAliased`](../type-aliases/FieldAliased.md)\<`TTable`\>[]; `withKey`: `boolean`; `skipSorting`: `boolean`; `suffix`: `string`; \} |
+| `__namedParameters.prefix` | `string` |
+| `__namedParameters.indent` | `string` |
+| `__namedParameters.fields` | readonly [`FieldAliased`](../type-aliases/FieldAliased.md)\<`TTable`\>[] |
+| `__namedParameters.withKey`? | `boolean` |
+| `__namedParameters.skipSorting`? | `boolean` |
+| `__namedParameters.suffix` | `string` |
+
+#### Returns
+
+`object`
+
+##### prefix
+
+> **prefix**: `string`
+
+##### func()
+
+> **func**: (`entries`) => `string`
+
+###### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `entries` | `Iterable`\<\[`string`, `TInput`\], `any`, `any`\> |
+
+###### Returns
+
+`string`
+
+##### suffix
+
+> **suffix**: `string`
+
+***
+
+### createUpdateKVsBuilder()
+
+> `protected` **createUpdateKVsBuilder**(`fields`): (`input`, `literal`?) => `string`
+
+Defined in: [src/pg/PgRunner.ts:404](https://github.com/clickup/ent-framework/blob/master/src/pg/PgRunner.ts#L404)
+
+Returns a newly created JS function which, when called with an object,
+returns the following SQL clause:
+
+id='123', a='xyz', b='nnn' [, {literal}]
+
+The set of columns is passed in specs, all other columns are ignored.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `fields` | [`Field`](../type-aliases/Field.md)\<`TTable`\>[] |
+
+#### Returns
+
+`Function`
+
+##### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `input` | `object` |
+| `literal`? | [`Literal`](../type-aliases/Literal.md) |
+
+##### Returns
+
+`string`
+
+***
+
+### createOneOfBuilder()
+
+> `protected` **createOneOfBuilder**(`field`, `fieldValCode`): (`values`) => `string`
+
+Defined in: [src/pg/PgRunner.ts:432](https://github.com/clickup/ent-framework/blob/master/src/pg/PgRunner.ts#L432)
+
+Prefers to do utilize createAnyBuilder() if it can (i.e. build
+a=ANY('{...}') clause). Otherwise, builds an IN(...) clause.
+
+#### Parameters
+
+| Parameter | Type | Default value |
+| ------ | ------ | ------ |
+| `field` | [`Field`](../type-aliases/Field.md)\<`TTable`\> | `undefined` |
+| `fieldValCode` | `string` | `"$value"` |
+
+#### Returns
+
+`Function`
+
+##### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `values` | `Iterable`\<`unknown`, `any`, `any`\> |
+
+##### Returns
+
+`string`
+
+***
+
+### createWhereBuildersFieldsEq()
+
+> `protected` **createWhereBuildersFieldsEq**\<`TInput`\>(`args`): `object`
+
+Defined in: [src/pg/PgRunner.ts:466](https://github.com/clickup/ent-framework/blob/master/src/pg/PgRunner.ts#L466)
+
+Given a list of fields, returns two builders:
+
+1. "Optimized": a newly created JS function which, when called with a row
+ set, returns one the following SQL clauses:
+
+```
+WHERE (field1, field2) IN(VALUES
+ ((NULL::tbl).field1, (NULL::tbl).field2),
+ ('aa', 'bb'),
+ ('cc', 'dd'))
+
+or
+
+WHERE (field1='a' AND field2='b' AND field3 IN('a', 'b', 'c', ...)) OR (...)
+ ^^^^^^^^^^prefix^^^^^^^^^ ^^^^^^^^ins^^^^^^^
+```
+
+2. "Plain": the last one builder mentioned above (good to always use for
+ non-batched queries for instance).
+
+#### Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TInput` *extends* `object` |
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `args` | \{ `prefix`: `string`; `fields`: readonly [`Field`](../type-aliases/Field.md)\<`TTable`\>[]; `suffix`: `string`; \} |
+| `args.prefix` | `string` |
+| `args.fields` | readonly [`Field`](../type-aliases/Field.md)\<`TTable`\>[] |
+| `args.suffix` | `string` |
+
+#### Returns
+
+`object`
+
+##### plain
+
+> **plain**: `object`
+
+###### plain.prefix
+
+> **prefix**: `string`
+
+###### plain.func()
+
+> **func**: (`inputs`) => `string`
+
+###### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `inputs` | `Iterable`\<\[`string`, `TInput`\], `any`, `any`\> |
+
+###### Returns
+
+`string`
+
+###### plain.suffix
+
+> **suffix**: `string`
+
+##### optimized
+
+> **optimized**: `object`
+
+###### optimized.prefix
+
+> **prefix**: `string`
+
+###### optimized.func()
+
+> **func**: (`inputs`) => `string`
+
+###### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `inputs` | `Iterable`\<\[`string`, `TInput`\], `any`, `any`\> |
+
+###### Returns
+
+`string`
+
+###### optimized.suffix
+
+> **suffix**: `string`
+
+***
+
+### createWhereBuilder()
+
+> `protected` **createWhereBuilder**(`__namedParameters`): `object`
+
+Defined in: [src/pg/PgRunner.ts:501](https://github.com/clickup/ent-framework/blob/master/src/pg/PgRunner.ts#L501)
+
+Returns a newly created JS function which, when called with a Where object,
+returns the generated SQL WHERE clause.
+
+- The building is relatively expensive, since it traverses the Where object
+ at run-time and doesn't know the shape beforehand.
+- If the Where object is undefined, skips the entire WHERE clause.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `__namedParameters` | \{ `prefix`: `string`; `suffix`: `string`; \} |
+| `__namedParameters.prefix` | `string` |
+| `__namedParameters.suffix` | `string` |
+
+#### Returns
+
+`object`
+
+##### prefix
+
+> **prefix**: `string`
+
+##### func()
+
+> **func**: (`where`) => `string`
+
+###### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `where` | [`Where`](../type-aliases/Where.md)\<`TTable`\> |
+
+###### Returns
+
+`string`
+
+##### suffix
+
+> **suffix**: `string`
+
+***
+
+### addPK()
+
+> `protected` **addPK**(`fields`, `mode`): `string`[]
+
+Defined in: [src/pg/PgRunner.ts:536](https://github.com/clickup/ent-framework/blob/master/src/pg/PgRunner.ts#L536)
+
+Prepends or appends a primary key to the list of fields. In case the
+primary key is plain (i.e. "id" field), it's just added as a field;
+otherwise, the unique key fields are added.
+
+For INSERT/UPSERT operations, we want to append the primary key, since it's
+often types pre-generated as a random-looking value. In many places, we
+sort batched lists of rows before e.g. inserting them, so we order them by
+their natural data order which prevents deadlocks on unique key conflict
+when multiple concurrent transactions try to insert the same set of rows in
+different order ("while inserting index tuple").
+
+For UPDATE operations though, we want to prepend the primary key, to make
+sure we run batched updates in the same order in multiple concurrent
+transactions. This lowers the chances of deadlocks too.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `fields` | readonly [`Field`](../type-aliases/Field.md)\<`TTable`\>[] |
+| `mode` | `"prepend"` \| `"append"` |
+
+#### Returns
+
+`string`[]
+
+***
+
+### delayForSingleQueryRetryOnError()
+
+> **delayForSingleQueryRetryOnError**(`e`): `number` \| `"immediate_retry"` \| `"no_retry"`
+
+Defined in: [src/pg/PgRunner.ts:569](https://github.com/clickup/ent-framework/blob/master/src/pg/PgRunner.ts#L569)
+
+If the single query's error needs to be retried (e.g. it's a deadlock
+error), returns the number of milliseconds to wait before retrying.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `e` | `unknown` |
+
+#### Returns
+
+`number` \| `"immediate_retry"` \| `"no_retry"`
+
+#### Overrides
+
+[`Runner`](Runner.md).[`delayForSingleQueryRetryOnError`](Runner.md#delayforsinglequeryretryonerror)
+
+***
+
+### shouldDebatchOnError()
+
+> **shouldDebatchOnError**(`e`): `boolean`
+
+Defined in: [src/pg/PgRunner.ts:582](https://github.com/clickup/ent-framework/blob/master/src/pg/PgRunner.ts#L582)
+
+If this method returns true for an error object, the batch is split back
+into sub-queries, they are executed individually, and then the response of
+each query is delivered to each caller individually. Used mostly for e.g.
+batch-deadlock errors or for FK constraint errors when it makes sense to
+retry other members of the batch and not fail it entirely hurting other
+innocent queries.
+
+We can do this, because we know that if some transaction is aborted, it's
+always safe to retry it. (If we're not sure about the transaction, e.g. the
+Client doesn't support transactions at all, then the method should return
+false.)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `e` | `unknown` |
+
+#### Returns
+
+`boolean`
+
+#### Overrides
+
+[`Runner`](Runner.md).[`shouldDebatchOnError`](Runner.md#shoulddebatchonerror)
diff --git a/docs/classes/PgSchema.md b/docs/classes/PgSchema.md
new file mode 100644
index 0000000..d860deb
--- /dev/null
+++ b/docs/classes/PgSchema.md
@@ -0,0 +1,333 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / PgSchema
+
+# Class: PgSchema\
+
+Defined in: [src/pg/PgSchema.ts:27](https://github.com/clickup/ent-framework/blob/master/src/pg/PgSchema.ts#L27)
+
+Schema is like a "table" in some database (sharded, but it's beyond the scope
+of Schema). It is also a factory of Query: it knows how to build runnable
+Query objects. This 2nd role is database engine specific (e.g. there might be
+PgSchema, RedisSchema etc.): such composition simplifies the code and lowers
+the number of abstractions.
+
+The set of supported Queries is opinionated and is crafted carefully to
+support the minimal possible list of primitives, but at the same time, be not
+too limited in the queries the DB engine can execute.
+
+## Extends
+
+- [`Schema`](Schema.md)\<`TTable`, `TUniqueKey`\>
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TTable` *extends* [`Table`](../type-aliases/Table.md) |
+| `TUniqueKey` *extends* [`UniqueKey`](../type-aliases/UniqueKey.md)\<`TTable`\> |
+
+## Constructors
+
+### new PgSchema()
+
+> **new PgSchema**\<`TTable`, `TUniqueKey`\>(`name`, `table`, `uniqueKey`): [`PgSchema`](PgSchema.md)\<`TTable`, `TUniqueKey`\>
+
+Defined in: [src/abstract/Schema.ts:116](https://github.com/clickup/ent-framework/blob/master/src/abstract/Schema.ts#L116)
+
+Used in e.g. inverses. This casts this.constructor to SchemaClass with all
+static methods and `new` semantic (TS doesn't do it by default; for TS,
+x.constructor is Function).
+
+#### Parameters
+
+| Parameter | Type | Description |
+| ------ | ------ | ------ |
+| `name` | `string` | For relational databases, it's likely a table name. |
+| `table` | `TTable` | Structure of the table. |
+| `uniqueKey` | `TUniqueKey` | Fields which the native unique key consists of (if any). |
+
+#### Returns
+
+[`PgSchema`](PgSchema.md)\<`TTable`, `TUniqueKey`\>
+
+#### Inherited from
+
+[`Schema`](Schema.md).[`constructor`](Schema.md#constructors)
+
+## Properties
+
+| Property | Type | Description |
+| ------ | ------ | ------ |
+| `constructor` | [`SchemaClass`](../interfaces/SchemaClass.md) | Used in e.g. inverses. This casts this.constructor to SchemaClass with all static methods and `new` semantic (TS doesn't do it by default; for TS, x.constructor is Function). |
+| `name` | `string` | For relational databases, it's likely a table name. |
+| `table` | `TTable` | Structure of the table. |
+| `uniqueKey` | `TUniqueKey` | Fields which the native unique key consists of (if any). |
+
+## Methods
+
+### idGen()
+
+> **idGen**(): [`Query`](../interfaces/Query.md)\<`string`\>
+
+Defined in: [src/pg/PgSchema.ts:31](https://github.com/clickup/ent-framework/blob/master/src/pg/PgSchema.ts#L31)
+
+Generates a new ID for the row. Used when e.g. there is a beforeInsert
+trigger on the Ent which needs to know the ID beforehand.
+
+#### Returns
+
+[`Query`](../interfaces/Query.md)\<`string`\>
+
+#### Overrides
+
+[`Schema`](Schema.md).[`idGen`](Schema.md#idgen)
+
+***
+
+### insert()
+
+> **insert**(`input`): [`Query`](../interfaces/Query.md)\<`null` \| `string`\>
+
+Defined in: [src/pg/PgSchema.ts:35](https://github.com/clickup/ent-framework/blob/master/src/pg/PgSchema.ts#L35)
+
+Creates a new row. Returns null if the row violates some unique key
+constraint, otherwise returns the row ID.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `input` | [`InsertInput`](../type-aliases/InsertInput.md)\<`TTable`\> |
+
+#### Returns
+
+[`Query`](../interfaces/Query.md)\<`null` \| `string`\>
+
+#### Overrides
+
+[`Schema`](Schema.md).[`insert`](Schema.md#insert)
+
+***
+
+### upsert()
+
+> **upsert**(`input`): [`Query`](../interfaces/Query.md)\<`string`\>
+
+Defined in: [src/pg/PgSchema.ts:39](https://github.com/clickup/ent-framework/blob/master/src/pg/PgSchema.ts#L39)
+
+Upserts a row. Always returns the row ID.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `input` | [`InsertInput`](../type-aliases/InsertInput.md)\<`TTable`\> |
+
+#### Returns
+
+[`Query`](../interfaces/Query.md)\<`string`\>
+
+#### Overrides
+
+[`Schema`](Schema.md).[`upsert`](Schema.md#upsert)
+
+***
+
+### update()
+
+> **update**(`id`, `input`): [`Query`](../interfaces/Query.md)\<`boolean`\>
+
+Defined in: [src/pg/PgSchema.ts:43](https://github.com/clickup/ent-framework/blob/master/src/pg/PgSchema.ts#L43)
+
+Updates one single row by its ID. Returns true if it actually existed.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `id` | `string` |
+| `input` | [`UpdateInput`](../type-aliases/UpdateInput.md)\<`TTable`\> |
+
+#### Returns
+
+[`Query`](../interfaces/Query.md)\<`boolean`\>
+
+#### Overrides
+
+[`Schema`](Schema.md).[`update`](Schema.md#update)
+
+***
+
+### delete()
+
+> **delete**(`id`): [`Query`](../interfaces/Query.md)\<`boolean`\>
+
+Defined in: [src/pg/PgSchema.ts:47](https://github.com/clickup/ent-framework/blob/master/src/pg/PgSchema.ts#L47)
+
+Deletes a row by id. Returns true if it actually existed.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `id` | `string` |
+
+#### Returns
+
+[`Query`](../interfaces/Query.md)\<`boolean`\>
+
+#### Overrides
+
+[`Schema`](Schema.md).[`delete`](Schema.md#delete)
+
+***
+
+### load()
+
+> **load**(`id`): [`Query`](../interfaces/Query.md)\<`null` \| [`Row`](../type-aliases/Row.md)\<`TTable`\>\>
+
+Defined in: [src/pg/PgSchema.ts:51](https://github.com/clickup/ent-framework/blob/master/src/pg/PgSchema.ts#L51)
+
+"Load" family of methods means that we load exactly one row. This one
+returns a row by its ID or null if it's not found.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `id` | `string` |
+
+#### Returns
+
+[`Query`](../interfaces/Query.md)\<`null` \| [`Row`](../type-aliases/Row.md)\<`TTable`\>\>
+
+#### Overrides
+
+[`Schema`](Schema.md).[`load`](Schema.md#load)
+
+***
+
+### loadBy()
+
+> **loadBy**(`input`): [`Query`](../interfaces/Query.md)\<`null` \| [`Row`](../type-aliases/Row.md)\<`TTable`\>\>
+
+Defined in: [src/pg/PgSchema.ts:55](https://github.com/clickup/ent-framework/blob/master/src/pg/PgSchema.ts#L55)
+
+Loads one single row by its unique key ("by" denotes that it's based on an
+unique key, not on an ID). Returns null if it's not found.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `input` | [`LoadByInput`](../type-aliases/LoadByInput.md)\<`TTable`, `TUniqueKey`\> |
+
+#### Returns
+
+[`Query`](../interfaces/Query.md)\<`null` \| [`Row`](../type-aliases/Row.md)\<`TTable`\>\>
+
+#### Overrides
+
+[`Schema`](Schema.md).[`loadBy`](Schema.md#loadby)
+
+***
+
+### selectBy()
+
+> **selectBy**(`input`): [`Query`](../interfaces/Query.md)\<[`Row`](../type-aliases/Row.md)\<`TTable`\>[]\>
+
+Defined in: [src/pg/PgSchema.ts:59](https://github.com/clickup/ent-framework/blob/master/src/pg/PgSchema.ts#L59)
+
+"Select" family of methods means that we load multiple rows ("by" denotes
+that it's based on an unique key, not on an arbitrary query). This one
+returns all rows whose unique key prefix matches the input.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `input` | [`SelectByInput`](../type-aliases/SelectByInput.md)\<`TTable`, `TUniqueKey`\> |
+
+#### Returns
+
+[`Query`](../interfaces/Query.md)\<[`Row`](../type-aliases/Row.md)\<`TTable`\>[]\>
+
+#### Overrides
+
+[`Schema`](Schema.md).[`selectBy`](Schema.md#selectby)
+
+***
+
+### select()
+
+> **select**(`input`): [`Query`](../interfaces/Query.md)\<[`Row`](../type-aliases/Row.md)\<`TTable`\>[]\>
+
+Defined in: [src/pg/PgSchema.ts:65](https://github.com/clickup/ent-framework/blob/master/src/pg/PgSchema.ts#L65)
+
+Returns all rows matching an arbitrary query.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `input` | [`SelectInput`](../type-aliases/SelectInput.md)\<`TTable`\> |
+
+#### Returns
+
+[`Query`](../interfaces/Query.md)\<[`Row`](../type-aliases/Row.md)\<`TTable`\>[]\>
+
+#### Overrides
+
+[`Schema`](Schema.md).[`select`](Schema.md#select)
+
+***
+
+### count()
+
+> **count**(`input`): [`Query`](../interfaces/Query.md)\<`number`\>
+
+Defined in: [src/pg/PgSchema.ts:69](https://github.com/clickup/ent-framework/blob/master/src/pg/PgSchema.ts#L69)
+
+Returns the number of rows matching an arbitrary query.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `input` | [`CountInput`](../type-aliases/CountInput.md)\<`TTable`\> |
+
+#### Returns
+
+[`Query`](../interfaces/Query.md)\<`number`\>
+
+#### Overrides
+
+[`Schema`](Schema.md).[`count`](Schema.md#count)
+
+***
+
+### exists()
+
+> **exists**(`input`): [`Query`](../interfaces/Query.md)\<`boolean`\>
+
+Defined in: [src/pg/PgSchema.ts:73](https://github.com/clickup/ent-framework/blob/master/src/pg/PgSchema.ts#L73)
+
+An optimized version of count() for the cases where we only need to know
+whether at least one row exists, and don't need a precise count.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `input` | [`ExistsInput`](../type-aliases/ExistsInput.md)\<`TTable`\> |
+
+#### Returns
+
+[`Query`](../interfaces/Query.md)\<`boolean`\>
+
+#### Overrides
+
+[`Schema`](Schema.md).[`exists`](Schema.md#exists)
diff --git a/docs/classes/PgShardNamer.md b/docs/classes/PgShardNamer.md
new file mode 100644
index 0000000..c4960a6
--- /dev/null
+++ b/docs/classes/PgShardNamer.md
@@ -0,0 +1,123 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / PgShardNamer
+
+# Class: PgShardNamer
+
+Defined in: [src/pg/PgShardNamer.ts:9](https://github.com/clickup/ent-framework/blob/master/src/pg/PgShardNamer.ts#L9)
+
+ShardNamer implementation for PG.
+
+## Extends
+
+- [`ShardNamer`](ShardNamer.md)
+
+## Constructors
+
+### new PgShardNamer()
+
+> **new PgShardNamer**(`options`): [`PgShardNamer`](PgShardNamer.md)
+
+Defined in: [src/abstract/ShardNamer.ts:34](https://github.com/clickup/ent-framework/blob/master/src/abstract/ShardNamer.ts#L34)
+
+Initializes an instance of ShardNamer.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `options` | [`ShardNamerOptions`](../interfaces/ShardNamerOptions.md) |
+
+#### Returns
+
+[`PgShardNamer`](PgShardNamer.md)
+
+#### Inherited from
+
+[`ShardNamer`](ShardNamer.md).[`constructor`](ShardNamer.md#constructors)
+
+## Properties
+
+| Property | Type | Description |
+| ------ | ------ | ------ |
+| `shardNoPadLen` | `number` | Number of decimal digits in an ID allocated for Shard number. Calculated dynamically based on `ShardNamerOptions#nameFormat` (e.g. for "sh%04d", it will be 4 since it expands to "sh0012"). |
+| `options` | [`ShardNamerOptions`](../interfaces/ShardNamerOptions.md) | - |
+
+## Methods
+
+### shardNoByName()
+
+> **shardNoByName**(`name`): `null` \| `number`
+
+Defined in: [src/abstract/ShardNamer.ts:46](https://github.com/clickup/ent-framework/blob/master/src/abstract/ShardNamer.ts#L46)
+
+Converts a Shard name to Shard number. Returns null if it's not a correct
+Shard name.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `name` | `string` |
+
+#### Returns
+
+`null` \| `number`
+
+#### Inherited from
+
+[`ShardNamer`](ShardNamer.md).[`shardNoByName`](ShardNamer.md#shardnobyname)
+
+***
+
+### shardNameByNo()
+
+> **shardNameByNo**(`no`): `string`
+
+Defined in: [src/abstract/ShardNamer.ts:58](https://github.com/clickup/ent-framework/blob/master/src/abstract/ShardNamer.ts#L58)
+
+Builds the Shard name (e.g. for PG, "schema name") by Shard number using
+`ShardNamerOptions#nameFormat`.
+
+E.g. nameFormat="sh%04d" generates names like "sh0042".
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `no` | `number` |
+
+#### Returns
+
+`string`
+
+#### Inherited from
+
+[`ShardNamer`](ShardNamer.md).[`shardNameByNo`](ShardNamer.md#shardnamebyno)
+
+***
+
+### shardNoByID()
+
+> **shardNoByID**(`id`): `number`
+
+Defined in: [src/pg/PgShardNamer.ts:14](https://github.com/clickup/ent-framework/blob/master/src/pg/PgShardNamer.ts#L14)
+
+Synchronously extracts Shard number from an ID. Can also extract from PG
+composite rows (to support composite IDs).
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `id` | `string` |
+
+#### Returns
+
+`number`
+
+#### Overrides
+
+[`ShardNamer`](ShardNamer.md).[`shardNoByID`](ShardNamer.md#shardnobyid)
diff --git a/docs/classes/PgTimelineStorage.md b/docs/classes/PgTimelineStorage.md
new file mode 100644
index 0000000..e7eceab
--- /dev/null
+++ b/docs/classes/PgTimelineStorage.md
@@ -0,0 +1,119 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / PgTimelineStorage
+
+# Class: PgTimelineStorage
+
+Defined in: [src/pg/PgTimelineStorage.ts:46](https://github.com/clickup/ent-framework/blob/master/src/pg/PgTimelineStorage.ts#L46)
+
+An append-only (with compaction) timeline storage for PG. The timelines are
+always appended to the table, but from time to time, when the number of
+chunks per principal exceeds the limit, the timelines are read back,
+compacted and written back as a single row. This is race condition safe,
+since timelines merging is an associative and idempotent operation, i.e.
+(T1+T2)+T3 == T1+(T2+T3); in the worst case, we'll just have slightly
+suboptimal timeline rows.
+
+The expected table schema is:
+```
+CREATE UNLOGGED TABLE timelines(
+ id bigserial PRIMARY KEY,
+ principal text NOT NULL,
+ data text NOT NULL,
+ created_at timestamptz NOT NULL
+);
+CREATE INDEX timelines_principal ON timelines (principal);
+```
+
+Notes:
+1. Index on `principal` must be non-unique, since there may be multiple
+ records with the same value.
+2. The `id` field should have sequential auto-increment, since it's used for
+ garbage collection.
+3. The table must exist in all microshards (including global shard).
+
+## Extends
+
+- [`TimelineStorage`](TimelineStorage.md)
+
+## Constructors
+
+### new PgTimelineStorage()
+
+> **new PgTimelineStorage**(`options`): [`PgTimelineStorage`](PgTimelineStorage.md)
+
+Defined in: [src/pg/PgTimelineStorage.ts:62](https://github.com/clickup/ent-framework/blob/master/src/pg/PgTimelineStorage.ts#L62)
+
+Initializes an instance of PgTimelineStorage.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `options` | [`PgTimelineStorageOptions`](../interfaces/PgTimelineStorageOptions.md) |
+
+#### Returns
+
+[`PgTimelineStorage`](PgTimelineStorage.md)
+
+#### Overrides
+
+[`TimelineStorage`](TimelineStorage.md).[`constructor`](TimelineStorage.md#constructors)
+
+## Properties
+
+| Property | Type | Description |
+| ------ | ------ | ------ |
+| `DEFAULT_OPTIONS` | `Required`\<`PickPartial`\<[`PgTimelineStorageOptions`](../interfaces/PgTimelineStorageOptions.md)\>\> | Default values for the constructor options. |
+| `options` | `Required`\<[`PgTimelineStorageOptions`](../interfaces/PgTimelineStorageOptions.md)\> | PgTimelineStorage configuration options. |
+
+## Methods
+
+### load()
+
+> **load**(`principal`): `Promise`\<`string`[]\>
+
+Defined in: [src/pg/PgTimelineStorage.ts:72](https://github.com/clickup/ent-framework/blob/master/src/pg/PgTimelineStorage.ts#L72)
+
+Loads the timelines from the storage for a given principal.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `principal` | `string` |
+
+#### Returns
+
+`Promise`\<`string`[]\>
+
+#### Overrides
+
+[`TimelineStorage`](TimelineStorage.md).[`load`](TimelineStorage.md#load)
+
+***
+
+### save()
+
+> **save**(`principal`, `dataStr`): `Promise`\<`void`\>
+
+Defined in: [src/pg/PgTimelineStorage.ts:81](https://github.com/clickup/ent-framework/blob/master/src/pg/PgTimelineStorage.ts#L81)
+
+Saves the timelines in the storage for a given principal.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `principal` | `string` |
+| `dataStr` | `string` |
+
+#### Returns
+
+`Promise`\<`void`\>
+
+#### Overrides
+
+[`TimelineStorage`](TimelineStorage.md).[`save`](TimelineStorage.md#save)
diff --git a/docs/classes/QueryBase.md b/docs/classes/QueryBase.md
new file mode 100644
index 0000000..e220a68
--- /dev/null
+++ b/docs/classes/QueryBase.md
@@ -0,0 +1,107 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / QueryBase
+
+# Class: `abstract` QueryBase\
+
+Defined in: [src/abstract/QueryBase.ts:15](https://github.com/clickup/ent-framework/blob/master/src/abstract/QueryBase.ts#L15)
+
+A convenient base class for most (but not all) of the queries, where the
+Runner instance is the same for different query input shapes. If the query
+doesn't fit the QueryBase framework (like PgQueryUpdate for instance where we
+have separate Runner instances for separate set of updated fields), a Query
+is used directly instead.
+
+## Extended by
+
+- [`PgQueryCount`](PgQueryCount.md)
+- [`PgQueryDelete`](PgQueryDelete.md)
+- [`PgQueryDeleteWhere`](PgQueryDeleteWhere.md)
+- [`PgQueryExists`](PgQueryExists.md)
+- [`PgQueryIDGen`](PgQueryIDGen.md)
+- [`PgQueryInsert`](PgQueryInsert.md)
+- [`PgQueryLoad`](PgQueryLoad.md)
+- [`PgQueryLoadBy`](PgQueryLoadBy.md)
+- [`PgQuerySelect`](PgQuerySelect.md)
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TTable` *extends* [`Table`](../type-aliases/Table.md) |
+| `TInput` |
+| `TOutput` |
+| `TClient` *extends* [`Client`](Client.md) |
+
+## Implements
+
+- [`Query`](../interfaces/Query.md)\<`TOutput`\>
+
+## Constructors
+
+### new QueryBase()
+
+> **new QueryBase**\<`TTable`, `TInput`, `TOutput`, `TClient`\>(`schema`, `input`): [`QueryBase`](QueryBase.md)\<`TTable`, `TInput`, `TOutput`, `TClient`\>
+
+Defined in: [src/abstract/QueryBase.ts:27](https://github.com/clickup/ent-framework/blob/master/src/abstract/QueryBase.ts#L27)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `schema` | [`Schema`](Schema.md)\<`TTable`, [`UniqueKey`](../type-aliases/UniqueKey.md)\<`TTable`\>\> |
+| `input` | `TInput` |
+
+#### Returns
+
+[`QueryBase`](QueryBase.md)\<`TTable`, `TInput`, `TOutput`, `TClient`\>
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `schema` | [`Schema`](Schema.md)\<`TTable`, [`UniqueKey`](../type-aliases/UniqueKey.md)\<`TTable`\>\> |
+| `input` | `TInput` |
+
+## Accessors
+
+### IS\_WRITE
+
+#### Get Signature
+
+> **get** **IS\_WRITE**(): `boolean`
+
+Defined in: [src/abstract/QueryBase.ts:32](https://github.com/clickup/ent-framework/blob/master/src/abstract/QueryBase.ts#L32)
+
+##### Returns
+
+`boolean`
+
+#### Implementation of
+
+[`Query`](../interfaces/Query.md).[`IS_WRITE`](../interfaces/Query.md#is_write)
+
+## Methods
+
+### run()
+
+> **run**(`client`, `annotation`): `Promise`\<`TOutput`\>
+
+Defined in: [src/abstract/QueryBase.ts:36](https://github.com/clickup/ent-framework/blob/master/src/abstract/QueryBase.ts#L36)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `client` | `TClient` |
+| `annotation` | [`QueryAnnotation`](../interfaces/QueryAnnotation.md) |
+
+#### Returns
+
+`Promise`\<`TOutput`\>
+
+#### Implementation of
+
+[`Query`](../interfaces/Query.md).[`run`](../interfaces/Query.md#run)
diff --git a/docs/classes/QueryCache.md b/docs/classes/QueryCache.md
new file mode 100644
index 0000000..2e651c4
--- /dev/null
+++ b/docs/classes/QueryCache.md
@@ -0,0 +1,147 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / QueryCache
+
+# Class: QueryCache
+
+Defined in: [src/ent/QueryCache.ts:26](https://github.com/clickup/ent-framework/blob/master/src/ent/QueryCache.ts#L26)
+
+Caches Ents loaded by a particular VC. I.e. the same query running for the
+same VC twice will quickly return the same Ents. This is typically enabled on
+web servers only, to deliver the fastest UI response.
+
+## Constructors
+
+### new QueryCache()
+
+> **new QueryCache**(`vc`): [`QueryCache`](QueryCache.md)
+
+Defined in: [src/ent/QueryCache.ts:38](https://github.com/clickup/ent-framework/blob/master/src/ent/QueryCache.ts#L38)
+
+Creates the QueryCache object. It enable caching only if VCWithQueryCache
+was manually added to the VC by the user, otherwise caching is a no-op.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `vc` | [`VC`](VC.md) |
+
+#### Returns
+
+[`QueryCache`](QueryCache.md)
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `whyOff?` | `string` |
+
+## Methods
+
+### set()
+
+> **set**(`EntClass`, `op`, `key`, `value`): `this`
+
+Defined in: [src/ent/QueryCache.ts:59](https://github.com/clickup/ent-framework/blob/master/src/ent/QueryCache.ts#L59)
+
+Saves a Promise to the cache slot for `op`. If this Promise rejects, the
+slot will automatically be cleared (we don't cache rejected Promises to not
+have a risk of caching a transient DB error).
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `EntClass` | [`AnyClass`](../type-aliases/AnyClass.md) |
+| `op` | `"loadNullable"` \| `"loadByNullable"` \| `"selectBy"` \| `"select"` \| `"count"` \| `"exists"` |
+| `key` | `string` |
+| `value` | `undefined` \| `Promise`\<`unknown`\> |
+
+#### Returns
+
+`this`
+
+***
+
+### delete()
+
+> **delete**(`EntClass`, `ops`, `key`?): `this`
+
+Defined in: [src/ent/QueryCache.ts:95](https://github.com/clickup/ent-framework/blob/master/src/ent/QueryCache.ts#L95)
+
+Deletes cache slots or keys for an Ent. If key is null, skips the deletion.
+If key is undefined (i.e. not passed), then deletes all slots.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `EntClass` | [`AnyClass`](../type-aliases/AnyClass.md) |
+| `ops` | readonly (`"loadNullable"` \| `"loadByNullable"` \| `"selectBy"` \| `"select"` \| `"count"` \| `"exists"`)[] |
+| `key`? | `null` \| `string` |
+
+#### Returns
+
+`this`
+
+***
+
+### get()
+
+> **get**\<`TValue`\>(`EntClass`, `op`, `key`): `undefined` \| `Promise`\<`TValue`\>
+
+Defined in: [src/ent/QueryCache.ts:121](https://github.com/clickup/ent-framework/blob/master/src/ent/QueryCache.ts#L121)
+
+This method is non-async on intent. We store Promises in the cache, not end
+values, because we want the code to join awaiting an ongoing operation in
+case it's inflight already.
+
+#### Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TValue` |
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `EntClass` | [`AnyClass`](../type-aliases/AnyClass.md) |
+| `op` | `"loadNullable"` \| `"loadByNullable"` \| `"selectBy"` \| `"select"` \| `"count"` \| `"exists"` |
+| `key` | `string` |
+
+#### Returns
+
+`undefined` \| `Promise`\<`TValue`\>
+
+***
+
+### through()
+
+> **through**\<`TValue`\>(`EntClass`, `op`, `key`, `creator`): `Promise`\<`TValue`\>
+
+Defined in: [src/ent/QueryCache.ts:137](https://github.com/clickup/ent-framework/blob/master/src/ent/QueryCache.ts#L137)
+
+Read-through caching pattern.
+
+#### Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TValue` |
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `EntClass` | [`AnyClass`](../type-aliases/AnyClass.md) |
+| `op` | `"loadNullable"` \| `"loadByNullable"` \| `"selectBy"` \| `"select"` \| `"count"` \| `"exists"` |
+| `key` | `string` |
+| `creator` | () => `Promise`\<`TValue`\> |
+
+#### Returns
+
+`Promise`\<`TValue`\>
diff --git a/docs/classes/QueryPing.md b/docs/classes/QueryPing.md
new file mode 100644
index 0000000..e4ad7ab
--- /dev/null
+++ b/docs/classes/QueryPing.md
@@ -0,0 +1,80 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / QueryPing
+
+# Class: QueryPing
+
+Defined in: [src/abstract/QueryPing.ts:8](https://github.com/clickup/ent-framework/blob/master/src/abstract/QueryPing.ts#L8)
+
+A helper Query which delegates to Client#ping(execTimeMs).
+
+## Implements
+
+- [`Query`](../interfaces/Query.md)\<`void`\>
+
+## Constructors
+
+### new QueryPing()
+
+> **new QueryPing**(`input`): [`QueryPing`](QueryPing.md)
+
+Defined in: [src/abstract/QueryPing.ts:9](https://github.com/clickup/ent-framework/blob/master/src/abstract/QueryPing.ts#L9)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `input` | `Omit`\<[`ClientPingInput`](../interfaces/ClientPingInput.md), `"annotation"`\> |
+
+#### Returns
+
+[`QueryPing`](QueryPing.md)
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `input` | `Omit`\<[`ClientPingInput`](../interfaces/ClientPingInput.md), `"annotation"`\> |
+
+## Accessors
+
+### IS\_WRITE
+
+#### Get Signature
+
+> **get** **IS\_WRITE**(): `boolean`
+
+Defined in: [src/abstract/QueryPing.ts:11](https://github.com/clickup/ent-framework/blob/master/src/abstract/QueryPing.ts#L11)
+
+##### Returns
+
+`boolean`
+
+#### Implementation of
+
+[`Query`](../interfaces/Query.md).[`IS_WRITE`](../interfaces/Query.md#is_write)
+
+## Methods
+
+### run()
+
+> **run**(`client`, `annotation`): `Promise`\<`void`\>
+
+Defined in: [src/abstract/QueryPing.ts:15](https://github.com/clickup/ent-framework/blob/master/src/abstract/QueryPing.ts#L15)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `client` | [`Client`](Client.md) |
+| `annotation` | [`QueryAnnotation`](../interfaces/QueryAnnotation.md) |
+
+#### Returns
+
+`Promise`\<`void`\>
+
+#### Implementation of
+
+[`Query`](../interfaces/Query.md).[`run`](../interfaces/Query.md#run)
diff --git a/docs/classes/Require.md b/docs/classes/Require.md
new file mode 100644
index 0000000..a0faae1
--- /dev/null
+++ b/docs/classes/Require.md
@@ -0,0 +1,77 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / Require
+
+# Class: Require\
+
+Defined in: [src/ent/rules/Require.ts:11](https://github.com/clickup/ent-framework/blob/master/src/ent/rules/Require.ts#L11)
+
+Returns TOLERATE if the predicate succeeds, otherwise DENY.
+- Used mostly for write permission checks.
+- This rule may still throw an exception if it's a wild one (i.e. not derived
+ from EntAccessError).
+
+## Extends
+
+- [`Rule`](Rule.md)\<`TInput`\>
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TInput` *extends* `object` |
+
+## Constructors
+
+### new Require()
+
+> **new Require**\<`TInput`\>(`predicate`): [`Require`](Require.md)\<`TInput`\>
+
+Defined in: [src/ent/rules/Rule.ts:43](https://github.com/clickup/ent-framework/blob/master/src/ent/rules/Rule.ts#L43)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `predicate` | [`Predicate`](../interfaces/Predicate.md)\<`TInput`\> \| (`vc`, `input`) => `boolean` \| `Promise`\<`boolean`\> |
+
+#### Returns
+
+[`Require`](Require.md)\<`TInput`\>
+
+#### Inherited from
+
+[`Rule`](Rule.md).[`constructor`](Rule.md#constructors)
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `_TAG` | `"Require"` |
+| `predicate` | [`Predicate`](../interfaces/Predicate.md)\<`TInput`\> |
+| `name` | `string` |
+
+## Methods
+
+### evaluate()
+
+> **evaluate**(`vc`, `input`): `Promise`\<[`RuleResult`](../interfaces/RuleResult.md)\>
+
+Defined in: [src/ent/rules/Require.ts:14](https://github.com/clickup/ent-framework/blob/master/src/ent/rules/Require.ts#L14)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `vc` | [`VC`](VC.md) |
+| `input` | `TInput` |
+
+#### Returns
+
+`Promise`\<[`RuleResult`](../interfaces/RuleResult.md)\>
+
+#### Overrides
+
+[`Rule`](Rule.md).[`evaluate`](Rule.md#evaluate)
diff --git a/docs/classes/RowIs.md b/docs/classes/RowIs.md
new file mode 100644
index 0000000..2405797
--- /dev/null
+++ b/docs/classes/RowIs.md
@@ -0,0 +1,97 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / RowIs
+
+# Class: RowIs\
+
+Defined in: [src/ent/predicates/RowIs.ts:37](https://github.com/clickup/ent-framework/blob/master/src/ent/predicates/RowIs.ts#L37)
+
+Checks that the validator function returns true for the entire row.
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TRow` |
+
+## Implements
+
+- [`AbstractIs`](../interfaces/AbstractIs.md)\<`TRow`\>
+
+## Constructors
+
+### new RowIs()
+
+> **new RowIs**\<`TRow`\>(`validator`, `message`): [`RowIs`](RowIs.md)\<`TRow`\>
+
+Defined in: [src/ent/predicates/RowIs.ts:46](https://github.com/clickup/ent-framework/blob/master/src/ent/predicates/RowIs.ts#L46)
+
+Manual validator.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `validator` | [`RowIsValidatorPlain`](../type-aliases/RowIsValidatorPlain.md)\<`TRow`\> |
+| `message` | `string` |
+
+#### Returns
+
+[`RowIs`](RowIs.md)\<`TRow`\>
+
+### new RowIs()
+
+> **new RowIs**\<`TRow`\>(`validator`): [`RowIs`](RowIs.md)\<`TRow`\>
+
+Defined in: [src/ent/predicates/RowIs.ts:51](https://github.com/clickup/ent-framework/blob/master/src/ent/predicates/RowIs.ts#L51)
+
+Rich validator, like Standard Schema (https://standardschema.dev) or Zod.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `validator` | [`RowIsValidatorZodSafeParse`](../type-aliases/RowIsValidatorZodSafeParse.md)\<`TRow`\> \| [`RowIsValidatorStandardSchemaV1`](../type-aliases/RowIsValidatorStandardSchemaV1.md)\<`TRow`\> |
+
+#### Returns
+
+[`RowIs`](RowIs.md)\<`TRow`\>
+
+## Properties
+
+| Property | Type | Default value | Description |
+| ------ | ------ | ------ | ------ |
+| `name` | `string` | `undefined` | - |
+| `field` | `null` | `null` | The field this validation predicate is related to (null means that it applies to the entire Ent). |
+| `message` | `null` \| `string` | `undefined` | In case the predicate returns false or doesn't provide error messages by throwing EntValidationError, this message will be used. When message is null, it means that we expect the validator to return detailed information about each field errored (e.g. ValidatorStandardSchemaResult). |
+| `validator` | [`RowIsValidatorPlain`](../type-aliases/RowIsValidatorPlain.md)\<`TRow`\> \| [`RowIsValidatorZodSafeParse`](../type-aliases/RowIsValidatorZodSafeParse.md)\<`TRow`\> \| [`RowIsValidatorStandardSchemaV1`](../type-aliases/RowIsValidatorStandardSchemaV1.md)\<`TRow`\> | `undefined` | - |
+
+## Methods
+
+### check()
+
+> **check**(`vc`, `row`): `Promise`\<`boolean`\>
+
+Defined in: [src/ent/predicates/RowIs.ts:75](https://github.com/clickup/ent-framework/blob/master/src/ent/predicates/RowIs.ts#L75)
+
+Returns true if validation succeeds. Returns false if it wants the client
+to use this.message as a validation failure response. Throws an instance of
+EntValidationError when it needs to deliver the detailed error messages
+about multiple fields.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `vc` | [`VC`](VC.md) |
+| `row` | `TRow` |
+
+#### Returns
+
+`Promise`\<`boolean`\>
+
+#### Implementation of
+
+[`AbstractIs`](../interfaces/AbstractIs.md).[`check`](../interfaces/AbstractIs.md#check)
diff --git a/docs/classes/Rule.md b/docs/classes/Rule.md
new file mode 100644
index 0000000..0845168
--- /dev/null
+++ b/docs/classes/Rule.md
@@ -0,0 +1,80 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / Rule
+
+# Class: `abstract` Rule\
+
+Defined in: [src/ent/rules/Rule.ts:37](https://github.com/clickup/ent-framework/blob/master/src/ent/rules/Rule.ts#L37)
+
+A base class which can e.g. accept not only a predicate, but also a plain JS
+lambda function as a predicate. Also has a logic of "glueing" the rule name
+with the predicate name.
+
+Each Rule must either:
+- throw (or return DENY) if it disallows access immediately,
+- return ALLOW if the access is granted (so no other rules will run),
+- return TOLERATE if it's okay with the row, but wants others' votes too,
+- or return SKIP to fully delegate the decision to the next rule.
+
+See more comments in rules.ts.
+
+Each rule carries a predicate which it calls and then decides, how to
+interpret the result.
+
+## Extended by
+
+- [`AllowIf`](AllowIf.md)
+- [`DenyIf`](DenyIf.md)
+- [`Require`](Require.md)
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TInput` |
+
+## Constructors
+
+### new Rule()
+
+> **new Rule**\<`TInput`\>(`predicate`): [`Rule`](Rule.md)\<`TInput`\>
+
+Defined in: [src/ent/rules/Rule.ts:43](https://github.com/clickup/ent-framework/blob/master/src/ent/rules/Rule.ts#L43)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `predicate` | [`Predicate`](../interfaces/Predicate.md)\<`TInput`\> \| (`vc`, `input`) => `boolean` \| `Promise`\<`boolean`\> |
+
+#### Returns
+
+[`Rule`](Rule.md)\<`TInput`\>
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `predicate` | [`Predicate`](../interfaces/Predicate.md)\<`TInput`\> |
+| `name` | `string` |
+
+## Methods
+
+### evaluate()
+
+> `abstract` **evaluate**(`vc`, `input`): `Promise`\<[`RuleResult`](../interfaces/RuleResult.md)\>
+
+Defined in: [src/ent/rules/Rule.ts:41](https://github.com/clickup/ent-framework/blob/master/src/ent/rules/Rule.ts#L41)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `vc` | [`VC`](VC.md) |
+| `input` | `TInput` |
+
+#### Returns
+
+`Promise`\<[`RuleResult`](../interfaces/RuleResult.md)\>
diff --git a/docs/classes/Runner.md b/docs/classes/Runner.md
new file mode 100644
index 0000000..e5bc224
--- /dev/null
+++ b/docs/classes/Runner.md
@@ -0,0 +1,170 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / Runner
+
+# Class: `abstract` Runner\
+
+Defined in: [src/abstract/Runner.ts:9](https://github.com/clickup/ent-framework/blob/master/src/abstract/Runner.ts#L9)
+
+Knows how to translate individual strongly typed requests into DB language
+and how to parse the result back.
+
+## Extended by
+
+- [`PgRunner`](PgRunner.md)
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TInput` |
+| `TOutput` |
+
+## Constructors
+
+### new Runner()
+
+> **new Runner**\<`TInput`, `TOutput`\>(`name`): [`Runner`](Runner.md)\<`TInput`, `TOutput`\>
+
+Defined in: [src/abstract/Runner.ts:70](https://github.com/clickup/ent-framework/blob/master/src/abstract/Runner.ts#L70)
+
+Parameter `name` is typically a table name.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `name` | `string` |
+
+#### Returns
+
+[`Runner`](Runner.md)\<`TInput`, `TOutput`\>
+
+## Properties
+
+| Property | Type | Description |
+| ------ | ------ | ------ |
+| `IS_WRITE` | `boolean` | If true, it's a write operation. |
+| `op` | `string` | Operation name for logging purposes. |
+| `maxBatchSize` | `number` | Maximum batch size for this type of operations. |
+| `default` | `TOutput` | In case undefined is returned from batching, this value will be returned instead. |
+| `name` | `string` | - |
+
+## Methods
+
+### runSingle()
+
+> `abstract` **runSingle**(`input`, `annotations`): `Promise`\<`undefined` \| `TOutput`\>
+
+Defined in: [src/abstract/Runner.ts:30](https://github.com/clickup/ent-framework/blob/master/src/abstract/Runner.ts#L30)
+
+Method runSingle is to e.g. produce simple DB requests when we have only
+one input to process, not many.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `input` | `TInput` |
+| `annotations` | [`QueryAnnotation`](../interfaces/QueryAnnotation.md)[] |
+
+#### Returns
+
+`Promise`\<`undefined` \| `TOutput`\>
+
+***
+
+### runBatch()?
+
+> `abstract` `optional` **runBatch**(`inputs`, `annotations`): `Promise`\<`Map`\<`string`, `TOutput`\>\>
+
+Defined in: [src/abstract/Runner.ts:39](https://github.com/clickup/ent-framework/blob/master/src/abstract/Runner.ts#L39)
+
+Typically issues complex queries with magic. If the method is not defined,
+then the runner doesn't support batching, so only runSingle() will be used.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `inputs` | `Map`\<`string`, `TInput`\> |
+| `annotations` | [`QueryAnnotation`](../interfaces/QueryAnnotation.md)[] |
+
+#### Returns
+
+`Promise`\<`Map`\<`string`, `TOutput`\>\>
+
+***
+
+### delayForSingleQueryRetryOnError()
+
+> `abstract` **delayForSingleQueryRetryOnError**(`error`): `number` \| `"immediate_retry"` \| `"no_retry"`
+
+Defined in: [src/abstract/Runner.ts:48](https://github.com/clickup/ent-framework/blob/master/src/abstract/Runner.ts#L48)
+
+If the single query's error needs to be retried (e.g. it's a deadlock
+error), returns the number of milliseconds to wait before retrying.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `error` | `unknown` |
+
+#### Returns
+
+`number` \| `"immediate_retry"` \| `"no_retry"`
+
+***
+
+### shouldDebatchOnError()
+
+> `abstract` **shouldDebatchOnError**(`error`): `boolean`
+
+Defined in: [src/abstract/Runner.ts:65](https://github.com/clickup/ent-framework/blob/master/src/abstract/Runner.ts#L65)
+
+If this method returns true for an error object, the batch is split back
+into sub-queries, they are executed individually, and then the response of
+each query is delivered to each caller individually. Used mostly for e.g.
+batch-deadlock errors or for FK constraint errors when it makes sense to
+retry other members of the batch and not fail it entirely hurting other
+innocent queries.
+
+We can do this, because we know that if some transaction is aborted, it's
+always safe to retry it. (If we're not sure about the transaction, e.g. the
+Client doesn't support transactions at all, then the method should return
+false.)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `error` | `unknown` |
+
+#### Returns
+
+`boolean`
+
+***
+
+### key()
+
+> **key**(`_input`): `string`
+
+Defined in: [src/abstract/Runner.ts:77](https://github.com/clickup/ent-framework/blob/master/src/abstract/Runner.ts#L77)
+
+Returns a batch-dedupping key for the input. By default, no dedupping is
+performed (i.e. all inputs are processed individually and not collapsed
+into one input; e.g. this is needed for inserts).
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `_input` | `TInput` |
+
+#### Returns
+
+`string`
diff --git a/docs/classes/Schema.md b/docs/classes/Schema.md
new file mode 100644
index 0000000..8c6c538
--- /dev/null
+++ b/docs/classes/Schema.md
@@ -0,0 +1,281 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / Schema
+
+# Class: `abstract` Schema\
+
+Defined in: [src/abstract/Schema.ts:37](https://github.com/clickup/ent-framework/blob/master/src/abstract/Schema.ts#L37)
+
+Schema is like a "table" in some database (sharded, but it's beyond the scope
+of Schema). It is also a factory of Query: it knows how to build runnable
+Query objects. This 2nd role is database engine specific (e.g. there might be
+PgSchema, RedisSchema etc.): such composition simplifies the code and lowers
+the number of abstractions.
+
+The set of supported Queries is opinionated and is crafted carefully to
+support the minimal possible list of primitives, but at the same time, be not
+too limited in the queries the DB engine can execute.
+
+## Extended by
+
+- [`PgSchema`](PgSchema.md)
+
+## Type Parameters
+
+| Type Parameter | Default type |
+| ------ | ------ |
+| `TTable` *extends* [`Table`](../type-aliases/Table.md) | - |
+| `TUniqueKey` *extends* [`UniqueKey`](../type-aliases/UniqueKey.md)\<`TTable`\> | [`UniqueKey`](../type-aliases/UniqueKey.md)\<`TTable`\> |
+
+## Constructors
+
+### new Schema()
+
+> **new Schema**\<`TTable`, `TUniqueKey`\>(`name`, `table`, `uniqueKey`): [`Schema`](Schema.md)\<`TTable`, `TUniqueKey`\>
+
+Defined in: [src/abstract/Schema.ts:116](https://github.com/clickup/ent-framework/blob/master/src/abstract/Schema.ts#L116)
+
+#### Parameters
+
+| Parameter | Type | Description |
+| ------ | ------ | ------ |
+| `name` | `string` | For relational databases, it's likely a table name. |
+| `table` | `TTable` | Structure of the table. |
+| `uniqueKey` | `TUniqueKey` | Fields which the native unique key consists of (if any). |
+
+#### Returns
+
+[`Schema`](Schema.md)\<`TTable`, `TUniqueKey`\>
+
+## Properties
+
+| Property | Type | Description |
+| ------ | ------ | ------ |
+| `constructor` | [`SchemaClass`](../interfaces/SchemaClass.md) | Used in e.g. inverses. This casts this.constructor to SchemaClass with all static methods and `new` semantic (TS doesn't do it by default; for TS, x.constructor is Function). |
+| `name` | `string` | For relational databases, it's likely a table name. |
+| `table` | `TTable` | Structure of the table. |
+| `uniqueKey` | `TUniqueKey` | Fields which the native unique key consists of (if any). |
+
+## Methods
+
+### idGen()
+
+> `abstract` **idGen**(): [`Query`](../interfaces/Query.md)\<`string`\>
+
+Defined in: [src/abstract/Schema.ts:54](https://github.com/clickup/ent-framework/blob/master/src/abstract/Schema.ts#L54)
+
+Generates a new ID for the row. Used when e.g. there is a beforeInsert
+trigger on the Ent which needs to know the ID beforehand.
+
+#### Returns
+
+[`Query`](../interfaces/Query.md)\<`string`\>
+
+***
+
+### insert()
+
+> `abstract` **insert**(`input`): [`Query`](../interfaces/Query.md)\<`null` \| `string`\>
+
+Defined in: [src/abstract/Schema.ts:60](https://github.com/clickup/ent-framework/blob/master/src/abstract/Schema.ts#L60)
+
+Creates a new row. Returns null if the row violates some unique key
+constraint, otherwise returns the row ID.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `input` | [`InsertInput`](../type-aliases/InsertInput.md)\<`TTable`\> |
+
+#### Returns
+
+[`Query`](../interfaces/Query.md)\<`null` \| `string`\>
+
+***
+
+### upsert()
+
+> `abstract` **upsert**(`input`): [`Query`](../interfaces/Query.md)\<`string`\>
+
+Defined in: [src/abstract/Schema.ts:65](https://github.com/clickup/ent-framework/blob/master/src/abstract/Schema.ts#L65)
+
+Upserts a row. Always returns the row ID.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `input` | [`InsertInput`](../type-aliases/InsertInput.md)\<`TTable`\> |
+
+#### Returns
+
+[`Query`](../interfaces/Query.md)\<`string`\>
+
+***
+
+### update()
+
+> `abstract` **update**(`id`, `input`): [`Query`](../interfaces/Query.md)\<`boolean`\>
+
+Defined in: [src/abstract/Schema.ts:70](https://github.com/clickup/ent-framework/blob/master/src/abstract/Schema.ts#L70)
+
+Updates one single row by its ID. Returns true if it actually existed.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `id` | `string` |
+| `input` | [`UpdateInput`](../type-aliases/UpdateInput.md)\<`TTable`\> |
+
+#### Returns
+
+[`Query`](../interfaces/Query.md)\<`boolean`\>
+
+***
+
+### delete()
+
+> `abstract` **delete**(`id`): [`Query`](../interfaces/Query.md)\<`boolean`\>
+
+Defined in: [src/abstract/Schema.ts:75](https://github.com/clickup/ent-framework/blob/master/src/abstract/Schema.ts#L75)
+
+Deletes a row by id. Returns true if it actually existed.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `id` | `string` |
+
+#### Returns
+
+[`Query`](../interfaces/Query.md)\<`boolean`\>
+
+***
+
+### load()
+
+> `abstract` **load**(`id`): [`Query`](../interfaces/Query.md)\<`null` \| [`Row`](../type-aliases/Row.md)\<`TTable`\>\>
+
+Defined in: [src/abstract/Schema.ts:81](https://github.com/clickup/ent-framework/blob/master/src/abstract/Schema.ts#L81)
+
+"Load" family of methods means that we load exactly one row. This one
+returns a row by its ID or null if it's not found.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `id` | `string` |
+
+#### Returns
+
+[`Query`](../interfaces/Query.md)\<`null` \| [`Row`](../type-aliases/Row.md)\<`TTable`\>\>
+
+***
+
+### loadBy()
+
+> `abstract` **loadBy**(`input`): [`Query`](../interfaces/Query.md)\<`null` \| [`Row`](../type-aliases/Row.md)\<`TTable`\>\>
+
+Defined in: [src/abstract/Schema.ts:87](https://github.com/clickup/ent-framework/blob/master/src/abstract/Schema.ts#L87)
+
+Loads one single row by its unique key ("by" denotes that it's based on an
+unique key, not on an ID). Returns null if it's not found.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `input` | [`LoadByInput`](../type-aliases/LoadByInput.md)\<`TTable`, `TUniqueKey`\> |
+
+#### Returns
+
+[`Query`](../interfaces/Query.md)\<`null` \| [`Row`](../type-aliases/Row.md)\<`TTable`\>\>
+
+***
+
+### selectBy()
+
+> `abstract` **selectBy**(`input`): [`Query`](../interfaces/Query.md)\<[`Row`](../type-aliases/Row.md)\<`TTable`\>[]\>
+
+Defined in: [src/abstract/Schema.ts:96](https://github.com/clickup/ent-framework/blob/master/src/abstract/Schema.ts#L96)
+
+"Select" family of methods means that we load multiple rows ("by" denotes
+that it's based on an unique key, not on an arbitrary query). This one
+returns all rows whose unique key prefix matches the input.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `input` | [`SelectByInput`](../type-aliases/SelectByInput.md)\<`TTable`, `TUniqueKey`\> |
+
+#### Returns
+
+[`Query`](../interfaces/Query.md)\<[`Row`](../type-aliases/Row.md)\<`TTable`\>[]\>
+
+***
+
+### select()
+
+> `abstract` **select**(`input`): [`Query`](../interfaces/Query.md)\<[`Row`](../type-aliases/Row.md)\<`TTable`\>[]\>
+
+Defined in: [src/abstract/Schema.ts:103](https://github.com/clickup/ent-framework/blob/master/src/abstract/Schema.ts#L103)
+
+Returns all rows matching an arbitrary query.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `input` | [`SelectInput`](../type-aliases/SelectInput.md)\<`TTable`\> |
+
+#### Returns
+
+[`Query`](../interfaces/Query.md)\<[`Row`](../type-aliases/Row.md)\<`TTable`\>[]\>
+
+***
+
+### count()
+
+> `abstract` **count**(`input`): [`Query`](../interfaces/Query.md)\<`number`\>
+
+Defined in: [src/abstract/Schema.ts:108](https://github.com/clickup/ent-framework/blob/master/src/abstract/Schema.ts#L108)
+
+Returns the number of rows matching an arbitrary query.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `input` | [`CountInput`](../type-aliases/CountInput.md)\<`TTable`\> |
+
+#### Returns
+
+[`Query`](../interfaces/Query.md)\<`number`\>
+
+***
+
+### exists()
+
+> `abstract` **exists**(`input`): [`Query`](../interfaces/Query.md)\<`boolean`\>
+
+Defined in: [src/abstract/Schema.ts:114](https://github.com/clickup/ent-framework/blob/master/src/abstract/Schema.ts#L114)
+
+An optimized version of count() for the cases where we only need to know
+whether at least one row exists, and don't need a precise count.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `input` | [`ExistsInput`](../type-aliases/ExistsInput.md)\<`TTable`\> |
+
+#### Returns
+
+[`Query`](../interfaces/Query.md)\<`boolean`\>
diff --git a/docs/classes/Shard.md b/docs/classes/Shard.md
new file mode 100644
index 0000000..9725642
--- /dev/null
+++ b/docs/classes/Shard.md
@@ -0,0 +1,111 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / Shard
+
+# Class: Shard\
+
+Defined in: [src/abstract/Shard.ts:21](https://github.com/clickup/ent-framework/blob/master/src/abstract/Shard.ts#L21)
+
+Shard lives within an Island with one master and N replicas.
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TClient` *extends* [`Client`](Client.md) |
+
+## Constructors
+
+### new Shard()
+
+> **new Shard**\<`TClient`\>(`no`, `runOnShard`): [`Shard`](Shard.md)\<`TClient`\>
+
+Defined in: [src/abstract/Shard.ts:29](https://github.com/clickup/ent-framework/blob/master/src/abstract/Shard.ts#L29)
+
+#### Parameters
+
+| Parameter | Type | Description |
+| ------ | ------ | ------ |
+| `no` | `number` | Shard number. |
+| `runOnShard` | \<`TRes`\>(`shardNo`, `body`, `onAttemptError`?) => `Promise`\<`TRes`\> | A middleware to wrap queries with. It's responsible for locating the right Island and retrying the call to body() (i.e. failed queries) in case e.g. a shard is moved to another Island. |
+
+#### Returns
+
+[`Shard`](Shard.md)\<`TClient`\>
+
+## Properties
+
+| Property | Type | Default value | Description |
+| ------ | ------ | ------ | ------ |
+| `lastKnownIslandNo` | `null` \| `number` | `null` | The last known Island number where this Shard was discovered. It may be out of date after the Shard is moved, and also it may be null in case there was no discovery happened yet. |
+| `no` | `number` | `undefined` | Shard number. |
+| `runOnShard` | \<`TRes`\>(`shardNo`: `number`, `body`: (`island`, `attempt`) => `Promise`\<`TRes`\>, `onAttemptError`?: (`error`, `attempt`) => `void`) => `Promise`\<`TRes`\> | `undefined` | A middleware to wrap queries with. It's responsible for locating the right Island and retrying the call to body() (i.e. failed queries) in case e.g. a shard is moved to another Island. |
+
+## Methods
+
+### client()
+
+> **client**(`timeline`): `Promise`\<`TClient`\>
+
+Defined in: [src/abstract/Shard.ts:46](https://github.com/clickup/ent-framework/blob/master/src/abstract/Shard.ts#L46)
+
+Chooses the right Client to be used for this Shard. We don't memoize,
+because the Shard may relocate to another Island during re-discovery.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `timeline` | [`Timeline`](Timeline.md) \| *typeof* [`MASTER`](../variables/MASTER.md) \| *typeof* [`STALE_REPLICA`](../variables/STALE_REPLICA.md) |
+
+#### Returns
+
+`Promise`\<`TClient`\>
+
+***
+
+### run()
+
+> **run**\<`TOutput`\>(`query`, `annotation`, `timeline`, `freshness`, `onAttemptError`?): `Promise`\<`TOutput`\>
+
+Defined in: [src/abstract/Shard.ts:59](https://github.com/clickup/ent-framework/blob/master/src/abstract/Shard.ts#L59)
+
+Runs a query after choosing the right Client (destination connection,
+Shard, annotation etc.)
+
+#### Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TOutput` |
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `query` | [`Query`](../interfaces/Query.md)\<`TOutput`\> |
+| `annotation` | [`QueryAnnotation`](../interfaces/QueryAnnotation.md) |
+| `timeline` | [`Timeline`](Timeline.md) |
+| `freshness` | `null` \| *typeof* [`MASTER`](../variables/MASTER.md) \| *typeof* [`STALE_REPLICA`](../variables/STALE_REPLICA.md) |
+| `onAttemptError`? | (`error`, `attempt`) => `void` |
+
+#### Returns
+
+`Promise`\<`TOutput`\>
+
+***
+
+### assertDiscoverable()
+
+> **assertDiscoverable**(): `Promise`\<`void`\>
+
+Defined in: [src/abstract/Shard.ts:101](https://github.com/clickup/ent-framework/blob/master/src/abstract/Shard.ts#L101)
+
+Throws if this Shard does not exist, or its Island is down, or something
+else is wrong with it.
+
+#### Returns
+
+`Promise`\<`void`\>
diff --git a/docs/classes/ShardError.md b/docs/classes/ShardError.md
new file mode 100644
index 0000000..8954250
--- /dev/null
+++ b/docs/classes/ShardError.md
@@ -0,0 +1,43 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / ShardError
+
+# Class: ShardError
+
+Defined in: [src/abstract/ShardError.ts:5](https://github.com/clickup/ent-framework/blob/master/src/abstract/ShardError.ts#L5)
+
+This non-retriable error is thrown when the system cannot detect the target
+shard to work with (e.g. a null ID or a missing field or something else).
+
+## Extends
+
+- `Error`
+
+## Extended by
+
+- [`ShardIsNotDiscoverableError`](ShardIsNotDiscoverableError.md)
+
+## Constructors
+
+### new ShardError()
+
+> **new ShardError**(`message`, `where`?): [`ShardError`](ShardError.md)
+
+Defined in: [src/abstract/ShardError.ts:6](https://github.com/clickup/ent-framework/blob/master/src/abstract/ShardError.ts#L6)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `message` | `string` |
+| `where`? | `string` |
+
+#### Returns
+
+[`ShardError`](ShardError.md)
+
+#### Overrides
+
+`Error.constructor`
diff --git a/docs/classes/ShardIsNotDiscoverableError.md b/docs/classes/ShardIsNotDiscoverableError.md
new file mode 100644
index 0000000..4bae72e
--- /dev/null
+++ b/docs/classes/ShardIsNotDiscoverableError.md
@@ -0,0 +1,41 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / ShardIsNotDiscoverableError
+
+# Class: ShardIsNotDiscoverableError
+
+Defined in: [src/abstract/ShardIsNotDiscoverableError.ts:10](https://github.com/clickup/ent-framework/blob/master/src/abstract/ShardIsNotDiscoverableError.ts#L10)
+
+This non-retriable error is thrown when shardsDiscoverCache.cached() returns
+no shard with the requested number.
+
+## Extends
+
+- [`ShardError`](ShardError.md)
+
+## Constructors
+
+### new ShardIsNotDiscoverableError()
+
+> **new ShardIsNotDiscoverableError**(`shardNo`, `errors`, `islands`, `elapsed`): [`ShardIsNotDiscoverableError`](ShardIsNotDiscoverableError.md)
+
+Defined in: [src/abstract/ShardIsNotDiscoverableError.ts:11](https://github.com/clickup/ent-framework/blob/master/src/abstract/ShardIsNotDiscoverableError.ts#L11)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `shardNo` | `number` |
+| `errors` | [`SwallowedErrorLoggerProps`](../interfaces/SwallowedErrorLoggerProps.md)[] |
+| `islands` | [`Island`](Island.md)\<[`Client`](Client.md)\>[] |
+| `elapsed` | `number` |
+
+#### Returns
+
+[`ShardIsNotDiscoverableError`](ShardIsNotDiscoverableError.md)
+
+#### Overrides
+
+[`ShardError`](ShardError.md).[`constructor`](ShardError.md#constructors)
diff --git a/docs/classes/ShardLocator.md b/docs/classes/ShardLocator.md
new file mode 100644
index 0000000..d4e4f5d
--- /dev/null
+++ b/docs/classes/ShardLocator.md
@@ -0,0 +1,147 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / ShardLocator
+
+# Class: ShardLocator\
+
+Defined in: [src/ent/ShardLocator.ts:21](https://github.com/clickup/ent-framework/blob/master/src/ent/ShardLocator.ts#L21)
+
+Knows how to locate Shard(s) based on various inputs. In some contexts, we
+expect exactly one Shard returned, and in other contexts, multiple Shards are
+okay.
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TClient` *extends* [`Client`](Client.md) |
+| `TTable` *extends* [`Table`](../type-aliases/Table.md) |
+| `TField` *extends* `string` |
+
+## Constructors
+
+### new ShardLocator()
+
+> **new ShardLocator**\<`TClient`, `TTable`, `TField`\>(`__namedParameters`): [`ShardLocator`](ShardLocator.md)\<`TClient`, `TTable`, `TField`\>
+
+Defined in: [src/ent/ShardLocator.ts:34](https://github.com/clickup/ent-framework/blob/master/src/ent/ShardLocator.ts#L34)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `__namedParameters` | \{ `cluster`: [`Cluster`](Cluster.md)\<`TClient`, `any`\>; `entName`: `string`; `shardAffinity`: [`ShardAffinity`](../type-aliases/ShardAffinity.md)\<`TField`\>; `uniqueKey`: `undefined` \| readonly `string`[]; `inverses`: readonly [`Inverse`](Inverse.md)\<`TClient`, `TTable`\>[]; \} |
+| `__namedParameters.cluster` | [`Cluster`](Cluster.md)\<`TClient`, `any`\> |
+| `__namedParameters.entName` | `string` |
+| `__namedParameters.shardAffinity` | [`ShardAffinity`](../type-aliases/ShardAffinity.md)\<`TField`\> |
+| `__namedParameters.uniqueKey` | `undefined` \| readonly `string`[] |
+| `__namedParameters.inverses` | readonly [`Inverse`](Inverse.md)\<`TClient`, `TTable`\>[] |
+
+#### Returns
+
+[`ShardLocator`](ShardLocator.md)\<`TClient`, `TTable`, `TField`\>
+
+## Methods
+
+### singleShardForInsert()
+
+> **singleShardForInsert**(`input`, `op`): `Promise`\<[`Shard`](Shard.md)\<`TClient`\>\>
+
+Defined in: [src/ent/ShardLocator.ts:76](https://github.com/clickup/ent-framework/blob/master/src/ent/ShardLocator.ts#L76)
+
+Called in a context when we must know exactly 1 Shard to work with (e.g.
+INSERT, UPSERT etc.). If op === "insert" (fallback to random Shard), then
+returns a random Shard in case when it can't infer the Shard number from
+the input (used in e.g. INSERT operations); otherwise throws ShardError
+(happens in e.g. UPSERT).
+
+The "randomness" of the "random Shard" is deterministic by the Ent's unique
+key (if it's defined), so Ents with the same unique key will map to the
+same "random" Shard (considering the total number of discovered Shards is
+unchanged). Notice that this logic applies at INSERT time: since we often
+times add Shards to the Cluster, we can't rely on it consistently at SELECT
+time (but relying at INSERT time is more or less fine: it protects against
+most of "unique key violation" problems, although still doesn't prevent all
+of them for a fraction of the second when the number of Shards has just
+been changed).
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `input` | `Record`\<`string`, `unknown`\> |
+| `op` | `"insert"` \| `"upsert"` |
+
+#### Returns
+
+`Promise`\<[`Shard`](Shard.md)\<`TClient`\>\>
+
+***
+
+### multiShardsFromInput()
+
+> **multiShardsFromInput**(`vc`, `input`, `op`): `Promise`\<[`Shard`](Shard.md)\<`TClient`\>[]\>
+
+Defined in: [src/ent/ShardLocator.ts:110](https://github.com/clickup/ent-framework/blob/master/src/ent/ShardLocator.ts#L110)
+
+Called in a context when multiple Shards may be involved, e.g. when
+selecting Ents referred by some Inverses. May also return the empty list of
+Shards when, although there are fields with Inverses in input (i.e. the
+filtering is correct), there are no Inverse rows existing in the database.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `vc` | [`VC`](VC.md) |
+| `input` | `Record`\<`string`, `unknown`\> |
+| `op` | `string` |
+
+#### Returns
+
+`Promise`\<[`Shard`](Shard.md)\<`TClient`\>[]\>
+
+***
+
+### singleShardFromID()
+
+> **singleShardFromID**(`field`, `id`, `op`): `Promise`\<`null` \| [`Shard`](Shard.md)\<`TClient`\>\>
+
+Defined in: [src/ent/ShardLocator.ts:182](https://github.com/clickup/ent-framework/blob/master/src/ent/ShardLocator.ts#L182)
+
+A wrapper for Cluster#shard() which injects Ent name to the exception (in
+case of e.g. "Cannot locate Shard" exception). This is just a convenience
+for debugging.
+
+If this method returns null, that means the caller should give up trying to
+load the Ent with this ID, because it won't find it anyways (e.g. when we
+try to load a sharded Ent using an ID from the global Shard). This is
+identical to the case of an Ent not existing in the database.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `field` | `string` |
+| `id` | `undefined` \| `null` \| `string` |
+| `op` | `string` |
+
+#### Returns
+
+`Promise`\<`null` \| [`Shard`](Shard.md)\<`TClient`\>\>
+
+***
+
+### allShards()
+
+> **allShards**(): `Promise`\[]\>
+
+Defined in: [src/ent/ShardLocator.ts:239](https://github.com/clickup/ent-framework/blob/master/src/ent/ShardLocator.ts#L239)
+
+All shards for this particular Ent depending on its affinity.
+
+#### Returns
+
+`Promise`\[]\>
diff --git a/docs/classes/ShardNamer.md b/docs/classes/ShardNamer.md
new file mode 100644
index 0000000..35377c9
--- /dev/null
+++ b/docs/classes/ShardNamer.md
@@ -0,0 +1,109 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / ShardNamer
+
+# Class: `abstract` ShardNamer
+
+Defined in: [src/abstract/ShardNamer.ts:19](https://github.com/clickup/ent-framework/blob/master/src/abstract/ShardNamer.ts#L19)
+
+Client-specific logic on how to synchronously convert an ID into Shard number
+(only for the use cases when ID is prefixed with a Shard number), how to
+build Shard names, and how to extract Shard number from a Shard name.
+
+## Extended by
+
+- [`PgShardNamer`](PgShardNamer.md)
+
+## Constructors
+
+### new ShardNamer()
+
+> **new ShardNamer**(`options`): [`ShardNamer`](ShardNamer.md)
+
+Defined in: [src/abstract/ShardNamer.ts:34](https://github.com/clickup/ent-framework/blob/master/src/abstract/ShardNamer.ts#L34)
+
+Initializes an instance of ShardNamer.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `options` | [`ShardNamerOptions`](../interfaces/ShardNamerOptions.md) |
+
+#### Returns
+
+[`ShardNamer`](ShardNamer.md)
+
+## Properties
+
+| Property | Type | Description |
+| ------ | ------ | ------ |
+| `shardNoPadLen` | `number` | Number of decimal digits in an ID allocated for Shard number. Calculated dynamically based on `ShardNamerOptions#nameFormat` (e.g. for "sh%04d", it will be 4 since it expands to "sh0012"). |
+| `options` | [`ShardNamerOptions`](../interfaces/ShardNamerOptions.md) | - |
+
+## Methods
+
+### shardNoByID()
+
+> `abstract` **shardNoByID**(`id`): `number`
+
+Defined in: [src/abstract/ShardNamer.ts:24](https://github.com/clickup/ent-framework/blob/master/src/abstract/ShardNamer.ts#L24)
+
+Synchronously extracts Shard number from an ID prefix, for the use cases
+where IDs have this information.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `id` | `string` |
+
+#### Returns
+
+`number`
+
+***
+
+### shardNoByName()
+
+> **shardNoByName**(`name`): `null` \| `number`
+
+Defined in: [src/abstract/ShardNamer.ts:46](https://github.com/clickup/ent-framework/blob/master/src/abstract/ShardNamer.ts#L46)
+
+Converts a Shard name to Shard number. Returns null if it's not a correct
+Shard name.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `name` | `string` |
+
+#### Returns
+
+`null` \| `number`
+
+***
+
+### shardNameByNo()
+
+> **shardNameByNo**(`no`): `string`
+
+Defined in: [src/abstract/ShardNamer.ts:58](https://github.com/clickup/ent-framework/blob/master/src/abstract/ShardNamer.ts#L58)
+
+Builds the Shard name (e.g. for PG, "schema name") by Shard number using
+`ShardNamerOptions#nameFormat`.
+
+E.g. nameFormat="sh%04d" generates names like "sh0042".
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `no` | `number` |
+
+#### Returns
+
+`string`
diff --git a/docs/classes/Timeline.md b/docs/classes/Timeline.md
new file mode 100644
index 0000000..820ff02
--- /dev/null
+++ b/docs/classes/Timeline.md
@@ -0,0 +1,164 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / Timeline
+
+# Class: Timeline
+
+Defined in: [src/abstract/Timeline.ts:36](https://github.com/clickup/ent-framework/blob/master/src/abstract/Timeline.ts#L36)
+
+Tracks replication lag timeline position at master per "user" and Ent.
+- serialization format: "pos:expiresAt"
+- wipes expired records (expiration is calculated at assignment moment)
+
+How replication lag (timeline) tracking works: for each
+microshard+Ent+"user", we know the “last write-ahead log write position”
+which that user (typically, VC#principal) made recently. This info can be
+propagated through e.g. user's session and push notifications/subscriptions
+channels automatically (“serialized timeline” and “timelines merging”). So
+the next time the same user tries to read the data from the same Ent on the
+same microshard, Ent Framework makes a choice, whether the replica is “good
+enough” for this already; if not, it falls back to master read. I.e. the data
+is not granular to individual Ent ID, it’s granular to the
+user+Ent+microshard, and thus it is decoupled from IDs.
+
+## Constructors
+
+### new Timeline()
+
+> **new Timeline**(`state`): [`Timeline`](Timeline.md)
+
+Defined in: [src/abstract/Timeline.ts:39](https://github.com/clickup/ent-framework/blob/master/src/abstract/Timeline.ts#L39)
+
+#### Parameters
+
+| Parameter | Type | Default value |
+| ------ | ------ | ------ |
+| `state` | \{ `pos`: `bigint`; `expiresAt`: `number`; \} \| `"unknown"` | `"unknown"` |
+
+#### Returns
+
+[`Timeline`](Timeline.md)
+
+## Methods
+
+### deserialize()
+
+> `static` **deserialize**(`data`, `prevTimeline`): [`Timeline`](Timeline.md)
+
+Defined in: [src/abstract/Timeline.ts:45](https://github.com/clickup/ent-framework/blob/master/src/abstract/Timeline.ts#L45)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `data` | `undefined` \| `string` |
+| `prevTimeline` | `null` \| [`Timeline`](Timeline.md) |
+
+#### Returns
+
+[`Timeline`](Timeline.md)
+
+***
+
+### cloneMap()
+
+> `static` **cloneMap**(`timelines`): `Map`\<`string`, [`Timeline`](Timeline.md)\>
+
+Defined in: [src/abstract/Timeline.ts:67](https://github.com/clickup/ent-framework/blob/master/src/abstract/Timeline.ts#L67)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `timelines` | `ReadonlyMap`\<`string`, [`Timeline`](Timeline.md)\> |
+
+#### Returns
+
+`Map`\<`string`, [`Timeline`](Timeline.md)\>
+
+***
+
+### serialize()
+
+> **serialize**(): `undefined` \| `string`
+
+Defined in: [src/abstract/Timeline.ts:80](https://github.com/clickup/ent-framework/blob/master/src/abstract/Timeline.ts#L80)
+
+#### Returns
+
+`undefined` \| `string`
+
+***
+
+### setPos()
+
+> **setPos**(`pos`, `maxLagMs`): `void`
+
+Defined in: [src/abstract/Timeline.ts:87](https://github.com/clickup/ent-framework/blob/master/src/abstract/Timeline.ts#L87)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `pos` | `bigint` |
+| `maxLagMs` | `number` |
+
+#### Returns
+
+`void`
+
+***
+
+### isCaughtUp()
+
+> **isCaughtUp**(`replicaPos`): [`TimelineCaughtUpReason`](../type-aliases/TimelineCaughtUpReason.md)
+
+Defined in: [src/abstract/Timeline.ts:100](https://github.com/clickup/ent-framework/blob/master/src/abstract/Timeline.ts#L100)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `replicaPos` | `bigint` |
+
+#### Returns
+
+[`TimelineCaughtUpReason`](../type-aliases/TimelineCaughtUpReason.md)
+
+***
+
+### isSaved()
+
+> **isSaved**(): `boolean`
+
+Defined in: [src/abstract/Timeline.ts:110](https://github.com/clickup/ent-framework/blob/master/src/abstract/Timeline.ts#L110)
+
+#### Returns
+
+`boolean`
+
+***
+
+### setIsSaved()
+
+> **setIsSaved**(): `void`
+
+Defined in: [src/abstract/Timeline.ts:118](https://github.com/clickup/ent-framework/blob/master/src/abstract/Timeline.ts#L118)
+
+#### Returns
+
+`void`
+
+***
+
+### reset()
+
+> **reset**(): `void`
+
+Defined in: [src/abstract/Timeline.ts:122](https://github.com/clickup/ent-framework/blob/master/src/abstract/Timeline.ts#L122)
+
+#### Returns
+
+`void`
diff --git a/docs/classes/TimelineManager.md b/docs/classes/TimelineManager.md
new file mode 100644
index 0000000..be71d5f
--- /dev/null
+++ b/docs/classes/TimelineManager.md
@@ -0,0 +1,78 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / TimelineManager
+
+# Class: TimelineManager
+
+Defined in: [src/abstract/TimelineManager.ts:10](https://github.com/clickup/ent-framework/blob/master/src/abstract/TimelineManager.ts#L10)
+
+A side effect based container which holds the current master or replica
+timeline position. For master, the expectation is that the pos will be
+updated after each query only, so no need to use refreshMs. For replica, it's
+also updated after each query PLUS the class will call triggerRefresh() hook
+not more often than every refreshMs interval.
+
+## Constructors
+
+### new TimelineManager()
+
+> **new TimelineManager**(`maxLagMs`, `refreshMs`, `triggerRefresh`): [`TimelineManager`](TimelineManager.md)
+
+Defined in: [src/abstract/TimelineManager.ts:15](https://github.com/clickup/ent-framework/blob/master/src/abstract/TimelineManager.ts#L15)
+
+#### Parameters
+
+| Parameter | Type | Description |
+| ------ | ------ | ------ |
+| `maxLagMs` | `MaybeCallable`\<`number`\> | Time interval after which a replica is declared as "caught up" even if it's not caught up. This is to not read from master forever when something has happened with the replica. |
+| `refreshMs` | `MaybeCallable`\<`number`\> | Up to how often we call triggerRefresh(). |
+| `triggerRefresh` | () => `Promise`\<`unknown`\> | This method is called from time to time to refresh the data which is later returned by currentPos(). Makes sense for replica connections which execute queries rarely: for them, the framework triggers the update when the fresh data is needed. |
+
+#### Returns
+
+[`TimelineManager`](TimelineManager.md)
+
+## Properties
+
+| Property | Type | Description |
+| ------ | ------ | ------ |
+| `maxLagMs` | `MaybeCallable`\<`number`\> | Time interval after which a replica is declared as "caught up" even if it's not caught up. This is to not read from master forever when something has happened with the replica. |
+
+## Methods
+
+### currentPos()
+
+> **currentPos**(): `Promise`\<`bigint`\>
+
+Defined in: [src/abstract/TimelineManager.ts:33](https://github.com/clickup/ent-framework/blob/master/src/abstract/TimelineManager.ts#L33)
+
+Returns the current Client's replication timeline position (e.g. WAL
+position).
+
+#### Returns
+
+`Promise`\<`bigint`\>
+
+***
+
+### setCurrentPos()
+
+> **setCurrentPos**(`pos`, `force`?): `void`
+
+Defined in: [src/abstract/TimelineManager.ts:56](https://github.com/clickup/ent-framework/blob/master/src/abstract/TimelineManager.ts#L56)
+
+Sets the actual timeline pos. Must be called by the Client after each
+interaction with the database.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `pos` | `bigint` |
+| `force`? | `boolean` |
+
+#### Returns
+
+`void`
diff --git a/docs/classes/TimelineStorage.md b/docs/classes/TimelineStorage.md
new file mode 100644
index 0000000..07d9fc1
--- /dev/null
+++ b/docs/classes/TimelineStorage.md
@@ -0,0 +1,84 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / TimelineStorage
+
+# Class: `abstract` TimelineStorage
+
+Defined in: [src/ent/TimelineStorage.ts:14](https://github.com/clickup/ent-framework/blob/master/src/ent/TimelineStorage.ts#L14)
+
+An abstract class that defines the interface for loading and storing
+timelines per VC principals.
+
+## Extended by
+
+- [`PgTimelineStorage`](PgTimelineStorage.md)
+
+## Constructors
+
+### new TimelineStorage()
+
+> **new TimelineStorage**(`options`): [`TimelineStorage`](TimelineStorage.md)
+
+Defined in: [src/ent/TimelineStorage.ts:43](https://github.com/clickup/ent-framework/blob/master/src/ent/TimelineStorage.ts#L43)
+
+Initializes an instance of TimelineStorage.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `options` | [`TimelineStorageOptions`](../interfaces/TimelineStorageOptions.md) |
+
+#### Returns
+
+[`TimelineStorage`](TimelineStorage.md)
+
+## Properties
+
+| Property | Type | Description |
+| ------ | ------ | ------ |
+| `DEFAULT_OPTIONS` | `Required`\<`PickPartial`\<[`TimelineStorageOptions`](../interfaces/TimelineStorageOptions.md)\>\> | Default values for the constructor options. |
+| `options` | `Required`\<[`TimelineStorageOptions`](../interfaces/TimelineStorageOptions.md)\> | Client configuration options. |
+
+## Methods
+
+### load()
+
+> `abstract` **load**(`principal`): `Promise`\<`string`[]\>
+
+Defined in: [src/ent/TimelineStorage.ts:33](https://github.com/clickup/ent-framework/blob/master/src/ent/TimelineStorage.ts#L33)
+
+Loads the timelines from the storage for a given principal.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `principal` | `string` |
+
+#### Returns
+
+`Promise`\<`string`[]\>
+
+***
+
+### save()
+
+> `abstract` **save**(`principal`, `dataStr`): `Promise`\<`void`\>
+
+Defined in: [src/ent/TimelineStorage.ts:38](https://github.com/clickup/ent-framework/blob/master/src/ent/TimelineStorage.ts#L38)
+
+Saves the timelines in the storage for a given principal.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `principal` | `string` |
+| `dataStr` | `string` |
+
+#### Returns
+
+`Promise`\<`void`\>
diff --git a/docs/classes/ToolPing.md b/docs/classes/ToolPing.md
new file mode 100644
index 0000000..5cfb7ad
--- /dev/null
+++ b/docs/classes/ToolPing.md
@@ -0,0 +1,55 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / ToolPing
+
+# Class: ToolPing
+
+Defined in: [src/tools/ToolPing.ts:34](https://github.com/clickup/ent-framework/blob/master/src/tools/ToolPing.ts#L34)
+
+A tool which plays the role of Linux `ping` command, but for master() or
+replica() Client of a Shard. Allows to verify that there is no downtime
+happening when a PG node goes down or experiences a failover/switchover.
+
+## Constructors
+
+### new ToolPing()
+
+> **new ToolPing**(`options`): [`ToolPing`](ToolPing.md)
+
+Defined in: [src/tools/ToolPing.ts:49](https://github.com/clickup/ent-framework/blob/master/src/tools/ToolPing.ts#L49)
+
+Initializes the instance.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `options` | [`ToolPingOptions`](../interfaces/ToolPingOptions.md) |
+
+#### Returns
+
+[`ToolPing`](ToolPing.md)
+
+## Properties
+
+| Property | Type | Description |
+| ------ | ------ | ------ |
+| `DEFAULT_OPTIONS` | `Required`\<`PickPartial`\<[`ToolPingOptions`](../interfaces/ToolPingOptions.md)\>\> | Default values for the constructor options. |
+| `options` | `Required`\<[`ToolPingOptions`](../interfaces/ToolPingOptions.md)\> | Options of this tool. |
+
+## Methods
+
+### \[asyncIterator\]()
+
+> **\[asyncIterator\]**(): `AsyncGenerator`\<`string`, `any`, `any`\>
+
+Defined in: [src/tools/ToolPing.ts:57](https://github.com/clickup/ent-framework/blob/master/src/tools/ToolPing.ts#L57)
+
+Runs an endless loop that pings a master() or replica() Client of the
+passed Island. Yields the colored output line by line.
+
+#### Returns
+
+`AsyncGenerator`\<`string`, `any`, `any`\>
diff --git a/docs/classes/ToolScoreboard.md b/docs/classes/ToolScoreboard.md
new file mode 100644
index 0000000..80d3361
--- /dev/null
+++ b/docs/classes/ToolScoreboard.md
@@ -0,0 +1,73 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / ToolScoreboard
+
+# Class: ToolScoreboard
+
+Defined in: [src/tools/ToolScoreboard.ts:75](https://github.com/clickup/ent-framework/blob/master/src/tools/ToolScoreboard.ts#L75)
+
+A tool which plays the role of Linux `top` command, but for the Cluster.
+Tracks the state of the Cluster and Clients health.
+
+## Constructors
+
+### new ToolScoreboard()
+
+> **new ToolScoreboard**(`options`): [`ToolScoreboard`](ToolScoreboard.md)
+
+Defined in: [src/tools/ToolScoreboard.ts:127](https://github.com/clickup/ent-framework/blob/master/src/tools/ToolScoreboard.ts#L127)
+
+Initializes the instance.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `options` | [`ToolScoreboardOptions`](../interfaces/ToolScoreboardOptions.md) |
+
+#### Returns
+
+[`ToolScoreboard`](ToolScoreboard.md)
+
+## Properties
+
+| Property | Type | Default value | Description |
+| ------ | ------ | ------ | ------ |
+| `DEFAULT_OPTIONS` | `Required`\<`PickPartial`\<[`ToolScoreboardOptions`](../interfaces/ToolScoreboardOptions.md)\>\> | `undefined` | Default values for the constructor options. |
+| `options` | `Required`\<[`ToolScoreboardOptions`](../interfaces/ToolScoreboardOptions.md)\> | `undefined` | Options of this tool. |
+| `islands` | `Map`\<`number`, \{ `shards`: `number`; `clients`: `Map`\<`ClientIdent`, [`Client`](Client.md)\>; \}\> | `undefined` | Registry of all Islands with Clients. |
+| `queries` | `DefaultMap`\<`number`, `DefaultMap`\<`ClientIdent`, `ToolScoreboardQuery`[]\>\> | `undefined` | Log of queries sent (ping, discovery, tick). |
+| `poolStats` | `DefaultMap`\<`number`, `Map`\<`ClientIdent`, \{ `totalConns`: `number`; `idleConns`: `number`; `queuedReqs`: `number`; \}\>\> | `undefined` | Pool stats of Clients. |
+| `swallowedErrors` | `ToolScoreboardSwallowedError`[] | `[]` | Registry of the recent swallowed errors (pings-independent). |
+| `queryErrors` | `ToolScoreboardQueryError`[] | `[]` | Errors extracted from the queries log. |
+
+## Methods
+
+### \[asyncIterator\]()
+
+> **\[asyncIterator\]**(): `AsyncGenerator`\<[`ToolScoreboard`](ToolScoreboard.md), `any`, `any`\>
+
+Defined in: [src/tools/ToolScoreboard.ts:135](https://github.com/clickup/ent-framework/blob/master/src/tools/ToolScoreboard.ts#L135)
+
+Runs an endless loop that updates the Scoreboard with the current state of
+the Cluster and yields back on every refreshMs tick.
+
+#### Returns
+
+`AsyncGenerator`\<[`ToolScoreboard`](ToolScoreboard.md), `any`, `any`\>
+
+***
+
+### render()
+
+> **render**(): `string`
+
+Defined in: [src/tools/ToolScoreboard.ts:304](https://github.com/clickup/ent-framework/blob/master/src/tools/ToolScoreboard.ts#L304)
+
+Renders the current state of the Scoreboard as a string.
+
+#### Returns
+
+`string`
diff --git a/docs/classes/Triggers.md b/docs/classes/Triggers.md
new file mode 100644
index 0000000..a073621
--- /dev/null
+++ b/docs/classes/Triggers.md
@@ -0,0 +1,125 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / Triggers
+
+# Class: Triggers\
+
+Defined in: [src/ent/Triggers.ts:169](https://github.com/clickup/ent-framework/blob/master/src/ent/Triggers.ts#L169)
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TTable` *extends* [`Table`](../type-aliases/Table.md) |
+
+## Constructors
+
+### new Triggers()
+
+> **new Triggers**\<`TTable`\>(`beforeInsert`, `beforeUpdate`, `beforeDelete`, `beforeMutation`, `afterInsert`, `afterUpdate`, `afterDelete`, `afterMutation`): [`Triggers`](Triggers.md)\<`TTable`\>
+
+Defined in: [src/ent/Triggers.ts:170](https://github.com/clickup/ent-framework/blob/master/src/ent/Triggers.ts#L170)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `beforeInsert` | [`InsertTrigger`](../type-aliases/InsertTrigger.md)\<`TTable`\>[] |
+| `beforeUpdate` | \[`null` \| [`DepsBuilder`](../type-aliases/DepsBuilder.md)\<`TTable`\>, [`BeforeUpdateTrigger`](../type-aliases/BeforeUpdateTrigger.md)\<`TTable`\>\][] |
+| `beforeDelete` | [`DeleteTrigger`](../type-aliases/DeleteTrigger.md)\<`TTable`\>[] |
+| `beforeMutation` | \[`null` \| [`DepsBuilder`](../type-aliases/DepsBuilder.md)\<`TTable`\>, [`BeforeMutationTrigger`](../type-aliases/BeforeMutationTrigger.md)\<`TTable`\>\][] |
+| `afterInsert` | [`InsertTrigger`](../type-aliases/InsertTrigger.md)\<`TTable`\>[] |
+| `afterUpdate` | \[`null` \| [`DepsBuilder`](../type-aliases/DepsBuilder.md)\<`TTable`\>, [`AfterUpdateTrigger`](../type-aliases/AfterUpdateTrigger.md)\<`TTable`\>\][] |
+| `afterDelete` | [`DeleteTrigger`](../type-aliases/DeleteTrigger.md)\<`TTable`\>[] |
+| `afterMutation` | \[`null` \| [`DepsBuilder`](../type-aliases/DepsBuilder.md)\<`TTable`\>, [`AfterMutationTrigger`](../type-aliases/AfterMutationTrigger.md)\<`TTable`\>\][] |
+
+#### Returns
+
+[`Triggers`](Triggers.md)\<`TTable`\>
+
+## Methods
+
+### hasInsertTriggers()
+
+> **hasInsertTriggers**(): `boolean`
+
+Defined in: [src/ent/Triggers.ts:189](https://github.com/clickup/ent-framework/blob/master/src/ent/Triggers.ts#L189)
+
+#### Returns
+
+`boolean`
+
+***
+
+### hasUpdateTriggers()
+
+> **hasUpdateTriggers**(): `boolean`
+
+Defined in: [src/ent/Triggers.ts:198](https://github.com/clickup/ent-framework/blob/master/src/ent/Triggers.ts#L198)
+
+#### Returns
+
+`boolean`
+
+***
+
+### wrapInsert()
+
+> **wrapInsert**(`func`, `vc`, `input`): `Promise`\<`null` \| `string`\>
+
+Defined in: [src/ent/Triggers.ts:207](https://github.com/clickup/ent-framework/blob/master/src/ent/Triggers.ts#L207)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `func` | (`input`) => `Promise`\<`null` \| `string`\> |
+| `vc` | [`VC`](VC.md) |
+| `input` | \{ \[K in string \| number \| symbol\]: Value\ \} & \{ \[K in string \| number \| symbol\]?: Value\ \} & [`RowWithID`](../type-aliases/RowWithID.md) |
+
+#### Returns
+
+`Promise`\<`null` \| `string`\>
+
+***
+
+### wrapUpdate()
+
+> **wrapUpdate**(`func`, `vc`, `oldRow`, `input`): `Promise`\<`boolean`\>
+
+Defined in: [src/ent/Triggers.ts:251](https://github.com/clickup/ent-framework/blob/master/src/ent/Triggers.ts#L251)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `func` | (`input`) => `Promise`\<`boolean`\> |
+| `vc` | [`VC`](VC.md) |
+| `oldRow` | \{ \[P in string \| symbol\]: Readonly\ \} & Record\\>\[P\] \} |
+| `input` | [`UpdateInput`](../type-aliases/UpdateInput.md)\<`TTable`\> |
+
+#### Returns
+
+`Promise`\<`boolean`\>
+
+***
+
+### wrapDelete()
+
+> **wrapDelete**(`func`, `vc`, `oldRow`): `Promise`\<`boolean`\>
+
+Defined in: [src/ent/Triggers.ts:312](https://github.com/clickup/ent-framework/blob/master/src/ent/Triggers.ts#L312)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `func` | () => `Promise`\<`boolean`\> |
+| `vc` | [`VC`](VC.md) |
+| `oldRow` | \{ \[P in string \| symbol\]: Readonly\ \} & Record\\>\[P\] \} |
+
+#### Returns
+
+`Promise`\<`boolean`\>
diff --git a/docs/classes/True.md b/docs/classes/True.md
new file mode 100644
index 0000000..0b81934
--- /dev/null
+++ b/docs/classes/True.md
@@ -0,0 +1,53 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / True
+
+# Class: True
+
+Defined in: [src/ent/predicates/True.ts:7](https://github.com/clickup/ent-framework/blob/master/src/ent/predicates/True.ts#L7)
+
+Always passes; used for e.g. globally accessed objects.
+
+## Implements
+
+- [`Predicate`](../interfaces/Predicate.md)\<`never`\>
+
+## Constructors
+
+### new True()
+
+> **new True**(): [`True`](True.md)
+
+#### Returns
+
+[`True`](True.md)
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `name` | `string` |
+
+## Methods
+
+### check()
+
+> **check**(`_vc`): `Promise`\<`boolean`\>
+
+Defined in: [src/ent/predicates/True.ts:10](https://github.com/clickup/ent-framework/blob/master/src/ent/predicates/True.ts#L10)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `_vc` | [`VC`](VC.md) |
+
+#### Returns
+
+`Promise`\<`boolean`\>
+
+#### Implementation of
+
+[`Predicate`](../interfaces/Predicate.md).[`check`](../interfaces/Predicate.md#check)
diff --git a/docs/classes/VC.md b/docs/classes/VC.md
new file mode 100644
index 0000000..98df359
--- /dev/null
+++ b/docs/classes/VC.md
@@ -0,0 +1,598 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / VC
+
+# Class: VC
+
+Defined in: [src/ent/VC.ts:48](https://github.com/clickup/ent-framework/blob/master/src/ent/VC.ts#L48)
+
+VC - Viewer Context.
+
+VC is set per HTTP request (or per worker job) in each Ent and represents the
+person who is about to run some database operation. It can represent a user,
+or a guest, or a bot observing that Ent.
+
+Depending on the Ent's Configuration object and privacy rules, it may allow
+the user to load/insert/update/etc. or to traverse to related objects.
+
+## Properties
+
+| Property | Type | Description |
+| ------ | ------ | ------ |
+| `principal` | `string` | A principal (typically user ID) represented by this VC. |
+| `freshness` | `null` \| *typeof* [`MASTER`](../variables/MASTER.md) \| *typeof* [`STALE_REPLICA`](../variables/STALE_REPLICA.md) | Allows to set VC to always use either a master or a replica DB. E.g. if freshness=MASTER, then all the timeline data is ignored, and all the requests are sent to master. |
+| `heartbeater` | `object` | The heartbeat callback is called before each primitive operation. It plays the similar role as AbortController: when called, it may throw sometimes (signalled externally). Delay callback can also be passed since it's pretty common use case to wait for some time and be aborted on a heartbeat exception. |
+| `heartbeater.heartbeat` | () => `Promise`\<`void`\> | - |
+| `heartbeater.delay` | (`ms`: `number`) => `Promise`\<`void`\> | - |
+
+## Methods
+
+### createGuestPleaseDoNotUseCreationPointsMustBeLimited()
+
+> `static` **createGuestPleaseDoNotUseCreationPointsMustBeLimited**(`__namedParameters`): [`VC`](VC.md)
+
+Defined in: [src/ent/VC.ts:71](https://github.com/clickup/ent-framework/blob/master/src/ent/VC.ts#L71)
+
+Please please don't call this method except one or two core places. The
+idea is that we create an "origin" VC once and then derive all other VCs
+from it (possibly upgrading or downgrading permissions, controlling
+master/replica read policy etc.). It's also good to trace the entire chain
+of calls and reasons, why some object was accessed.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `__namedParameters` | \{ `trace`: `string`; `cachesExpirationMs`: `number`; \} |
+| `__namedParameters.trace`? | `string` |
+| `__namedParameters.cachesExpirationMs`? | `number` |
+
+#### Returns
+
+[`VC`](VC.md)
+
+***
+
+### \[custom\]()
+
+> **\[custom\]**(): `string`
+
+Defined in: [src/ent/VC.ts:93](https://github.com/clickup/ent-framework/blob/master/src/ent/VC.ts#L93)
+
+This is to show VCs in console.log() and inspect() nicely.
+
+#### Returns
+
+`string`
+
+***
+
+### cache()
+
+#### Call Signature
+
+> **cache**\<`TInstance`\>(`Class`): `TInstance`
+
+Defined in: [src/ent/VC.ts:101](https://github.com/clickup/ent-framework/blob/master/src/ent/VC.ts#L101)
+
+Some IDs are cached in VC (e.g. is this ID readable? is it writable? is
+this VC an admin VC?). Also, people may define their own VC-local caches.
+
+##### Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TInstance` |
+
+##### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `Class` | (`vc`) => `TInstance` |
+
+##### Returns
+
+`TInstance`
+
+#### Call Signature
+
+> **cache**\<`TInstance`\>(`tag`, `creator`): `TInstance`
+
+Defined in: [src/ent/VC.ts:107](https://github.com/clickup/ent-framework/blob/master/src/ent/VC.ts#L107)
+
+Same as the above overload, but allows to use a custom creating function.
+This is useful when e.g. cached values are async-created.
+
+##### Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TInstance` |
+
+##### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `tag` | `symbol` |
+| `creator` | (`vc`) => `TInstance` |
+
+##### Returns
+
+`TInstance`
+
+***
+
+### loader()
+
+> **loader**\<`TLoadArgs`, `TReturn`\>(`HandlerClass`): [`Loader`](Loader.md)\<`TLoadArgs`, `TReturn`\>
+
+Defined in: [src/ent/VC.ts:132](https://github.com/clickup/ent-framework/blob/master/src/ent/VC.ts#L132)
+
+Returns a cached instance of Loader whose actual code is defined in
+HandlerClass. In case there is no such Loader yet, creates it.
+
+#### Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TLoadArgs` *extends* `unknown`[] |
+| `TReturn` |
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `HandlerClass` | (`vc`) => [`Handler`](../interfaces/Handler.md)\<`TLoadArgs`, `TReturn`\> |
+| `HandlerClass.$loader`? | `symbol` |
+
+#### Returns
+
+[`Loader`](Loader.md)\<`TLoadArgs`, `TReturn`\>
+
+***
+
+### timeline()
+
+> **timeline**(`shard`, `schemaName`): [`Timeline`](Timeline.md)
+
+Defined in: [src/ent/VC.ts:148](https://github.com/clickup/ent-framework/blob/master/src/ent/VC.ts#L148)
+
+Returns Shard+schemaName timeline which tracks replica staleness for the
+particular schema name (most likely, table).
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `shard` | [`Shard`](Shard.md)\<[`Client`](Client.md)\> |
+| `schemaName` | `string` |
+
+#### Returns
+
+[`Timeline`](Timeline.md)
+
+***
+
+### serializeTimelines()
+
+> **serializeTimelines**(): `undefined` \| `string`
+
+Defined in: [src/ent/VC.ts:164](https://github.com/clickup/ent-framework/blob/master/src/ent/VC.ts#L164)
+
+Serializes Shard timelines (master WAL positions) to a string format. The
+method always returns a value which is compatible to
+deserializeTimelines() input.
+
+#### Returns
+
+`undefined` \| `string`
+
+***
+
+### deserializeTimelines()
+
+> **deserializeTimelines**(...`dataStrs`): [`VC`](VC.md)
+
+Defined in: [src/ent/VC.ts:196](https://github.com/clickup/ent-framework/blob/master/src/ent/VC.ts#L196)
+
+Restores all replication timelines in the VC based on the serialized info
+provided. Returns the new VC derived from the current one, but with empty
+caches.
+
+This method has a side effect of changing the timelines of the current VC
+(and actually all parent VCs), because it reflects the changes in the
+global DB state as seen by the current VC's principal. It restores
+previously serialized timelines to the existing VC and all its parent VCs
+which share the same principal. (The latter happens, because
+`this.timelines` map is passed by reference to all derived VCs starting
+from the one which sets principal; see `new VC(...)` clauses all around and
+toLowerInternal() logic.) The timelines are merged according to WAL
+positions (larger WAL positions win).
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| ...`dataStrs` | readonly (`undefined` \| `string`)[] |
+
+#### Returns
+
+[`VC`](VC.md)
+
+***
+
+### saveTimelines()
+
+> **saveTimelines**(`storage`): `Promise`\<`undefined` \| `string`\>
+
+Defined in: [src/ent/VC.ts:224](https://github.com/clickup/ent-framework/blob/master/src/ent/VC.ts#L224)
+
+Saves the timelines to the storage. The function minimizes the number of
+writes to the storage:
+
+- Calling saveTimelines() the 2nd time without making changes to the
+ timelines is a no-op.
+- Calling saveTimelines() after loadTimelines() without making changes to
+ the timelines is a no-op.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `storage` | [`TimelineStorage`](TimelineStorage.md) |
+
+#### Returns
+
+`Promise`\<`undefined` \| `string`\>
+
+***
+
+### loadTimelines()
+
+> **loadTimelines**(`storage`): `Promise`\<[`VC`](VC.md)\>
+
+Defined in: [src/ent/VC.ts:256](https://github.com/clickup/ent-framework/blob/master/src/ent/VC.ts#L256)
+
+Loads the timelines from the storage and deserializes them into the current
+VC. Returns the new VC derived from the current one, but with empty caches.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `storage` | [`TimelineStorage`](TimelineStorage.md) |
+
+#### Returns
+
+`Promise`\<[`VC`](VC.md)\>
+
+***
+
+### withEmptyCache()
+
+> **withEmptyCache**(): [`VC`](VC.md)
+
+Defined in: [src/ent/VC.ts:275](https://github.com/clickup/ent-framework/blob/master/src/ent/VC.ts#L275)
+
+Returns a new VC derived from the current one, but with empty cache.
+
+#### Returns
+
+[`VC`](VC.md)
+
+***
+
+### withTransitiveMasterFreshness()
+
+> **withTransitiveMasterFreshness**(): [`VC`](VC.md)
+
+Defined in: [src/ent/VC.ts:293](https://github.com/clickup/ent-framework/blob/master/src/ent/VC.ts#L293)
+
+Returns a new VC derived from the current one, but with master freshness.
+Master freshness is inherited by ent.vc after an Ent is loaded.
+
+#### Returns
+
+[`VC`](VC.md)
+
+***
+
+### withOneTimeStaleReplica()
+
+> **withOneTimeStaleReplica**(): [`VC`](VC.md)
+
+Defined in: [src/ent/VC.ts:320](https://github.com/clickup/ent-framework/blob/master/src/ent/VC.ts#L320)
+
+Returns a new VC derived from the current one, but which forces an Ent to
+be loaded always from replica. Freshness is NOT inherited by Ents (not
+transitive): e.g. if an Ent is loaded with STALE_REPLICA freshness, its
+ent.vc will have the DEFAULT freshness.
+
+Also, if an Ent is inserted with a VC of STALE_REPLICA freshness, its VC
+won't remember it, so next immediate reads will go to a replica and not to
+the master.
+
+#### Returns
+
+[`VC`](VC.md)
+
+***
+
+### withDefaultFreshness()
+
+> **withDefaultFreshness**(): [`VC`](VC.md)
+
+Defined in: [src/ent/VC.ts:343](https://github.com/clickup/ent-framework/blob/master/src/ent/VC.ts#L343)
+
+Creates a new VC with default freshness (i.e. not sticky to master or
+replica, auto-detected on request). Generally, it's not a good idea to use
+this derivation since we lose some bit of internal knowledge from the past
+history of the VC, but for e.g. tests or benchmarks, it's fine.
+
+#### Returns
+
+[`VC`](VC.md)
+
+***
+
+### withFlavor()
+
+#### Call Signature
+
+> **withFlavor**(`prepend`, ...`flavors`): `this`
+
+Defined in: [src/ent/VC.ts:364](https://github.com/clickup/ent-framework/blob/master/src/ent/VC.ts#L364)
+
+Returns a new VC derived from the current one adding some more flavors to
+it. If no flavors were added, returns the same VC (`this`).
+
+##### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `prepend` | `"prepend"` |
+| ...`flavors` | (`undefined` \| [`VCFlavor`](VCFlavor.md))[] |
+
+##### Returns
+
+`this`
+
+#### Call Signature
+
+> **withFlavor**(...`flavors`): `this`
+
+Defined in: [src/ent/VC.ts:365](https://github.com/clickup/ent-framework/blob/master/src/ent/VC.ts#L365)
+
+Returns a new VC derived from the current one adding some more flavors to
+it. If no flavors were added, returns the same VC (`this`).
+
+##### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| ...`flavors` | (`undefined` \| [`VCFlavor`](VCFlavor.md))[] |
+
+##### Returns
+
+`this`
+
+***
+
+### withoutFlavor()
+
+> **withoutFlavor**(...`flavorClasses`): `this`
+
+Defined in: [src/ent/VC.ts:403](https://github.com/clickup/ent-framework/blob/master/src/ent/VC.ts#L403)
+
+Returns a new VC derived from the current one removing the specified flavors.
+If no flavors were removed, returns the same VC (`this`).
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| ...`flavorClasses` | (...`args`) => [`VCFlavor`](VCFlavor.md)[] |
+
+#### Returns
+
+`this`
+
+***
+
+### withNewTrace()
+
+> **withNewTrace**(`trace`): [`VC`](VC.md)
+
+Defined in: [src/ent/VC.ts:436](https://github.com/clickup/ent-framework/blob/master/src/ent/VC.ts#L436)
+
+Derives the VC with new trace ID.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `trace` | `undefined` \| `string` |
+
+#### Returns
+
+[`VC`](VC.md)
+
+***
+
+### withHeartbeater()
+
+> **withHeartbeater**(`heartbeater`): [`VC`](VC.md)
+
+Defined in: [src/ent/VC.ts:452](https://github.com/clickup/ent-framework/blob/master/src/ent/VC.ts#L452)
+
+Derives the VC with the provided heartbeater injected.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `heartbeater` | \{ `heartbeat`: () => `Promise`\<`void`\>; `delay`: (`ms`) => `Promise`\<`void`\>; \} |
+| `heartbeater.heartbeat` | () => `Promise`\<`void`\> |
+| `heartbeater.delay` | (`ms`) => `Promise`\<`void`\> |
+
+#### Returns
+
+[`VC`](VC.md)
+
+***
+
+### toOmniDangerous()
+
+> **toOmniDangerous**(): [`VC`](VC.md)
+
+Defined in: [src/ent/VC.ts:472](https://github.com/clickup/ent-framework/blob/master/src/ent/VC.ts#L472)
+
+Creates a new VC upgraded to omni permissions. This VC will not
+be placed to some Ent's ent.vc property; instead, it will be
+automatically downgraded to either the owning VC of this Ent or
+to a guest VC (see Ent.ts).
+
+#### Returns
+
+[`VC`](VC.md)
+
+***
+
+### toGuest()
+
+> **toGuest**(): [`VC`](VC.md)
+
+Defined in: [src/ent/VC.ts:489](https://github.com/clickup/ent-framework/blob/master/src/ent/VC.ts#L489)
+
+Creates a new VC downgraded to guest permissions.
+
+#### Returns
+
+[`VC`](VC.md)
+
+***
+
+### isOmni()
+
+> **isOmni**(): `boolean`
+
+Defined in: [src/ent/VC.ts:504](https://github.com/clickup/ent-framework/blob/master/src/ent/VC.ts#L504)
+
+Checks if it's an omni VC.
+
+#### Returns
+
+`boolean`
+
+***
+
+### isGuest()
+
+> **isGuest**(): `boolean`
+
+Defined in: [src/ent/VC.ts:511](https://github.com/clickup/ent-framework/blob/master/src/ent/VC.ts#L511)
+
+Checks if it's a guest VC.
+
+#### Returns
+
+`boolean`
+
+***
+
+### isLoggedIn()
+
+> **isLoggedIn**(): `boolean`
+
+Defined in: [src/ent/VC.ts:518](https://github.com/clickup/ent-framework/blob/master/src/ent/VC.ts#L518)
+
+Checks if it's a regular user (i.e. owning) VC.
+
+#### Returns
+
+`boolean`
+
+***
+
+### flavor()
+
+> **flavor**\<`TFlavor`\>(`flavor`): `null` \| `TFlavor`
+
+Defined in: [src/ent/VC.ts:525](https://github.com/clickup/ent-framework/blob/master/src/ent/VC.ts#L525)
+
+Returns VC's flavor of the particular type.
+
+#### Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TFlavor` *extends* [`VCFlavor`](VCFlavor.md) |
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `flavor` | (...`args`) => `TFlavor` |
+
+#### Returns
+
+`null` \| `TFlavor`
+
+***
+
+### toString()
+
+> **toString**(`withInstanceNumber`): `string`
+
+Defined in: [src/ent/VC.ts:534](https://github.com/clickup/ent-framework/blob/master/src/ent/VC.ts#L534)
+
+Used for debugging purposes.
+
+#### Parameters
+
+| Parameter | Type | Default value |
+| ------ | ------ | ------ |
+| `withInstanceNumber` | `boolean` | `false` |
+
+#### Returns
+
+`string`
+
+***
+
+### toAnnotation()
+
+> **toAnnotation**(): [`QueryAnnotation`](../interfaces/QueryAnnotation.md)
+
+Defined in: [src/ent/VC.ts:553](https://github.com/clickup/ent-framework/blob/master/src/ent/VC.ts#L553)
+
+Returns a debug annotation of this VC.
+
+#### Returns
+
+[`QueryAnnotation`](../interfaces/QueryAnnotation.md)
+
+***
+
+### toLowerInternal()
+
+> **toLowerInternal**(`principal`): [`VC`](VC.md)
+
+Defined in: [src/ent/VC.ts:584](https://github.com/clickup/ent-framework/blob/master/src/ent/VC.ts#L584)
+
+Used internally by Ent framework to lower permissions of an injected VC.
+For guest, principal === null.
+- freshness is always reset to default one it VC is demoted
+- isRoot is changed to false once a root VC is switched to a per-user VC
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `principal` | `null` \| `string` |
+
+#### Returns
+
+[`VC`](VC.md)
diff --git a/docs/classes/VCCaches.md b/docs/classes/VCCaches.md
new file mode 100644
index 0000000..6c79bfd
--- /dev/null
+++ b/docs/classes/VCCaches.md
@@ -0,0 +1,89 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / VCCaches
+
+# Class: VCCaches\
+
+Defined in: [src/ent/VCCaches.ts:6](https://github.com/clickup/ent-framework/blob/master/src/ent/VCCaches.ts#L6)
+
+Holds an auto-expiring map of VC caches.
+
+## Extends
+
+- `Map`\<`TKey`, `TValue`\>
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TKey` |
+| `TValue` |
+
+## Constructors
+
+### new VCCaches()
+
+> **new VCCaches**\<`TKey`, `TValue`\>(`expirationMs`): [`VCCaches`](VCCaches.md)\<`TKey`, `TValue`\>
+
+Defined in: [src/ent/VCCaches.ts:7](https://github.com/clickup/ent-framework/blob/master/src/ent/VCCaches.ts#L7)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `expirationMs` | `number` |
+
+#### Returns
+
+[`VCCaches`](VCCaches.md)\<`TKey`, `TValue`\>
+
+#### Overrides
+
+`Map.constructor`
+
+## Methods
+
+### get()
+
+> **get**(`key`): `undefined` \| `TValue`
+
+Defined in: [src/ent/VCCaches.ts:15](https://github.com/clickup/ent-framework/blob/master/src/ent/VCCaches.ts#L15)
+
+Calls the Map's get() and defers cache clearing to the next WeakTicker
+tick (i.e. schedules clearing on inactivity).
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `key` | `TKey` |
+
+#### Returns
+
+`undefined` \| `TValue`
+
+#### Overrides
+
+`Map.get`
+
+***
+
+### onTick()
+
+> **onTick**(`tickNoSinceScheduling`): `"keep"` \| `"unschedule"`
+
+Defined in: [src/ent/VCCaches.ts:26](https://github.com/clickup/ent-framework/blob/master/src/ent/VCCaches.ts#L26)
+
+Called periodically after VC#cache() was called at least once.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `tickNoSinceScheduling` | `number` |
+
+#### Returns
+
+`"keep"` \| `"unschedule"`
diff --git a/docs/classes/VCFlavor.md b/docs/classes/VCFlavor.md
new file mode 100644
index 0000000..5f9c2ee
--- /dev/null
+++ b/docs/classes/VCFlavor.md
@@ -0,0 +1,52 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / VCFlavor
+
+# Class: `abstract` VCFlavor
+
+Defined in: [src/ent/VCFlavor.ts:9](https://github.com/clickup/ent-framework/blob/master/src/ent/VCFlavor.ts#L9)
+
+VCFlavor is some piece of info which is transitively attached to a VC and is
+preserved when VC derivation (upgrade/downgrade) is happening. This piece of
+info may be just an object of a separate class with no data (acts like a
+boolean flag), or it can be an object with payload.
+
+For each flavor type, only a single VCFlavor object may exist.
+
+## Extended by
+
+- [`VCWithStacks`](VCWithStacks.md)
+- [`VCWithQueryCache`](VCWithQueryCache.md)
+
+## Constructors
+
+### new VCFlavor()
+
+> **new VCFlavor**(): [`VCFlavor`](VCFlavor.md)
+
+#### Returns
+
+[`VCFlavor`](VCFlavor.md)
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `_tag` | `"VCFlavorClass"` |
+| `_tag` | `"VCFlavorInstance"` |
+
+## Methods
+
+### toDebugString()
+
+> **toDebugString**(): `string`
+
+Defined in: [src/ent/VCFlavor.ts:16](https://github.com/clickup/ent-framework/blob/master/src/ent/VCFlavor.ts#L16)
+
+Appended to the end of VC.toString() result.
+
+#### Returns
+
+`string`
diff --git a/docs/classes/VCHasFlavor.md b/docs/classes/VCHasFlavor.md
new file mode 100644
index 0000000..1515048
--- /dev/null
+++ b/docs/classes/VCHasFlavor.md
@@ -0,0 +1,61 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / VCHasFlavor
+
+# Class: VCHasFlavor
+
+Defined in: [src/ent/predicates/VCHasFlavor.ts:8](https://github.com/clickup/ent-framework/blob/master/src/ent/predicates/VCHasFlavor.ts#L8)
+
+Checks that the VC has some flavor.
+
+## Implements
+
+- [`Predicate`](../interfaces/Predicate.md)\<`never`\>
+
+## Constructors
+
+### new VCHasFlavor()
+
+> **new VCHasFlavor**(`Flavor`): [`VCHasFlavor`](VCHasFlavor.md)
+
+Defined in: [src/ent/predicates/VCHasFlavor.ts:11](https://github.com/clickup/ent-framework/blob/master/src/ent/predicates/VCHasFlavor.ts#L11)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `Flavor` | (...`args`) => [`VCFlavor`](VCFlavor.md) |
+
+#### Returns
+
+[`VCHasFlavor`](VCHasFlavor.md)
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `name` | `string` |
+
+## Methods
+
+### check()
+
+> **check**(`vc`): `Promise`\<`boolean`\>
+
+Defined in: [src/ent/predicates/VCHasFlavor.ts:15](https://github.com/clickup/ent-framework/blob/master/src/ent/predicates/VCHasFlavor.ts#L15)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `vc` | [`VC`](VC.md) |
+
+#### Returns
+
+`Promise`\<`boolean`\>
+
+#### Implementation of
+
+[`Predicate`](../interfaces/Predicate.md).[`check`](../interfaces/Predicate.md#check)
diff --git a/docs/classes/VCTrace.md b/docs/classes/VCTrace.md
new file mode 100644
index 0000000..28eb91b
--- /dev/null
+++ b/docs/classes/VCTrace.md
@@ -0,0 +1,53 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / VCTrace
+
+# Class: VCTrace
+
+Defined in: [src/ent/VCTrace.ts:14](https://github.com/clickup/ent-framework/blob/master/src/ent/VCTrace.ts#L14)
+
+A "trace" objects which allows to group database related stuff while logging
+it. Traces are inherited during VC derivation, similar to flavors, but
+they're a part of VC core interface to allow faster access.
+
+## Constructors
+
+### new VCTrace()
+
+> **new VCTrace**(`trace`?): [`VCTrace`](VCTrace.md)
+
+Defined in: [src/ent/VCTrace.ts:17](https://github.com/clickup/ent-framework/blob/master/src/ent/VCTrace.ts#L17)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `trace`? | `string` |
+
+#### Returns
+
+[`VCTrace`](VCTrace.md)
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `trace` | `string` |
+
+## Methods
+
+### tryExtractCreationDate()
+
+> **tryExtractCreationDate**(): `null` \| `Date`
+
+Defined in: [src/ent/VCTrace.ts:26](https://github.com/clickup/ent-framework/blob/master/src/ent/VCTrace.ts#L26)
+
+In case the trace was created by this tool, tries to extract the date of
+its creation. As a sanity check, verifies that this date is not too far
+away from the present time.
+
+#### Returns
+
+`null` \| `Date`
diff --git a/docs/classes/VCWithQueryCache.md b/docs/classes/VCWithQueryCache.md
new file mode 100644
index 0000000..e487560
--- /dev/null
+++ b/docs/classes/VCWithQueryCache.md
@@ -0,0 +1,65 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / VCWithQueryCache
+
+# Class: VCWithQueryCache
+
+Defined in: [src/ent/VCFlavor.ts:30](https://github.com/clickup/ent-framework/blob/master/src/ent/VCFlavor.ts#L30)
+
+If set, Ent cache is enabled for operations in this VC.
+
+## Extends
+
+- [`VCFlavor`](VCFlavor.md)
+
+## Constructors
+
+### new VCWithQueryCache()
+
+> **new VCWithQueryCache**(`options`): [`VCWithQueryCache`](VCWithQueryCache.md)
+
+Defined in: [src/ent/VCFlavor.ts:31](https://github.com/clickup/ent-framework/blob/master/src/ent/VCFlavor.ts#L31)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `options` | \{ `maxQueries`: `number`; \} |
+| `options.maxQueries` | `number` |
+
+#### Returns
+
+[`VCWithQueryCache`](VCWithQueryCache.md)
+
+#### Overrides
+
+[`VCFlavor`](VCFlavor.md).[`constructor`](VCFlavor.md#constructors)
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `_tag` | `"VCFlavorClass"` |
+| `_tag` | `"VCFlavorInstance"` |
+| `options` | `object` |
+| `options.maxQueries` | `number` |
+
+## Methods
+
+### toDebugString()
+
+> **toDebugString**(): `string`
+
+Defined in: [src/ent/VCFlavor.ts:16](https://github.com/clickup/ent-framework/blob/master/src/ent/VCFlavor.ts#L16)
+
+Appended to the end of VC.toString() result.
+
+#### Returns
+
+`string`
+
+#### Inherited from
+
+[`VCFlavor`](VCFlavor.md).[`toDebugString`](VCFlavor.md#todebugstring)
diff --git a/docs/classes/VCWithStacks.md b/docs/classes/VCWithStacks.md
new file mode 100644
index 0000000..0a583b9
--- /dev/null
+++ b/docs/classes/VCWithStacks.md
@@ -0,0 +1,55 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / VCWithStacks
+
+# Class: VCWithStacks
+
+Defined in: [src/ent/VCFlavor.ts:25](https://github.com/clickup/ent-framework/blob/master/src/ent/VCFlavor.ts#L25)
+
+If turned on, the debug logs will contain caller stack traces for each Ent
+query. This is expensive, use in dev mode only!
+
+## Extends
+
+- [`VCFlavor`](VCFlavor.md)
+
+## Constructors
+
+### new VCWithStacks()
+
+> **new VCWithStacks**(): [`VCWithStacks`](VCWithStacks.md)
+
+#### Returns
+
+[`VCWithStacks`](VCWithStacks.md)
+
+#### Inherited from
+
+[`VCFlavor`](VCFlavor.md).[`constructor`](VCFlavor.md#constructors)
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `_tag` | `"VCFlavorClass"` |
+| `_tag` | `"VCFlavorInstance"` |
+
+## Methods
+
+### toDebugString()
+
+> **toDebugString**(): `string`
+
+Defined in: [src/ent/VCFlavor.ts:16](https://github.com/clickup/ent-framework/blob/master/src/ent/VCFlavor.ts#L16)
+
+Appended to the end of VC.toString() result.
+
+#### Returns
+
+`string`
+
+#### Inherited from
+
+[`VCFlavor`](VCFlavor.md).[`toDebugString`](VCFlavor.md#todebugstring)
diff --git a/docs/classes/Validation.md b/docs/classes/Validation.md
new file mode 100644
index 0000000..0acae73
--- /dev/null
+++ b/docs/classes/Validation.md
@@ -0,0 +1,124 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / Validation
+
+# Class: Validation\
+
+Defined in: [src/ent/Validation.ts:69](https://github.com/clickup/ent-framework/blob/master/src/ent/Validation.ts#L69)
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TTable` *extends* [`Table`](../type-aliases/Table.md) |
+
+## Constructors
+
+### new Validation()
+
+> **new Validation**\<`TTable`\>(`entName`, `rules`): [`Validation`](Validation.md)\<`TTable`\>
+
+Defined in: [src/ent/Validation.ts:78](https://github.com/clickup/ent-framework/blob/master/src/ent/Validation.ts#L78)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `entName` | `string` |
+| `rules` | [`ValidationRules`](../type-aliases/ValidationRules.md)\<`TTable`\> |
+
+#### Returns
+
+[`Validation`](Validation.md)\<`TTable`\>
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `tenantPrincipalField?` | [`InsertFieldsRequired`](../type-aliases/InsertFieldsRequired.md)\<`TTable`\> & `string` |
+| `inferPrincipal` | (`vc`: [`VC`](VC.md), `row`: [`Row`](../type-aliases/Row.md)\<`TTable`\>) => `Promise`\<[`VC`](VC.md)\> |
+| `load` | [`LoadRule`](../type-aliases/LoadRule.md)\<[`Row`](../type-aliases/Row.md)\<`TTable`\>\>[] |
+| `insert` | [`WriteRules`](../type-aliases/WriteRules.md)\<[`InsertInput`](../type-aliases/InsertInput.md)\<`TTable`\>\> |
+| `update` | [`WriteRules`](../type-aliases/WriteRules.md)\<[`Row`](../type-aliases/Row.md)\<`TTable`\>\> |
+| `delete` | [`WriteRules`](../type-aliases/WriteRules.md)\<[`Row`](../type-aliases/Row.md)\<`TTable`\>\> |
+| `validate` | [`Require`](Require.md)\<[`InsertInput`](../type-aliases/InsertInput.md)\<`TTable`\>\>[] |
+
+## Methods
+
+### validateLoad()
+
+> **validateLoad**(`vc`, `row`): `Promise`\<`void`\>
+
+Defined in: [src/ent/Validation.ts:91](https://github.com/clickup/ent-framework/blob/master/src/ent/Validation.ts#L91)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `vc` | [`VC`](VC.md) |
+| `row` | [`Row`](../type-aliases/Row.md)\<`TTable`\> |
+
+#### Returns
+
+`Promise`\<`void`\>
+
+***
+
+### validateInsert()
+
+> **validateInsert**(`vc`, `input`): `Promise`\<`void`\>
+
+Defined in: [src/ent/Validation.ts:102](https://github.com/clickup/ent-framework/blob/master/src/ent/Validation.ts#L102)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `vc` | [`VC`](VC.md) |
+| `input` | [`InsertInput`](../type-aliases/InsertInput.md)\<`TTable`\> |
+
+#### Returns
+
+`Promise`\<`void`\>
+
+***
+
+### validateUpdate()
+
+> **validateUpdate**(`vc`, `old`, `input`, `privacyOnly`): `Promise`\<`void`\>
+
+Defined in: [src/ent/Validation.ts:114](https://github.com/clickup/ent-framework/blob/master/src/ent/Validation.ts#L114)
+
+#### Parameters
+
+| Parameter | Type | Default value |
+| ------ | ------ | ------ |
+| `vc` | [`VC`](VC.md) | `undefined` |
+| `old` | [`Row`](../type-aliases/Row.md)\<`TTable`\> | `undefined` |
+| `input` | [`UpdateInput`](../type-aliases/UpdateInput.md)\<`TTable`\> | `undefined` |
+| `privacyOnly` | `boolean` | `false` |
+
+#### Returns
+
+`Promise`\<`void`\>
+
+***
+
+### validateDelete()
+
+> **validateDelete**(`vc`, `row`): `Promise`\<`void`\>
+
+Defined in: [src/ent/Validation.ts:141](https://github.com/clickup/ent-framework/blob/master/src/ent/Validation.ts#L141)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `vc` | [`VC`](VC.md) |
+| `row` | [`Row`](../type-aliases/Row.md)\<`TTable`\> |
+
+#### Returns
+
+`Promise`\<`void`\>
diff --git a/docs/functions/Base64BufferType.md b/docs/functions/Base64BufferType.md
new file mode 100644
index 0000000..073e72e
--- /dev/null
+++ b/docs/functions/Base64BufferType.md
@@ -0,0 +1,63 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / Base64BufferType
+
+# Function: Base64BufferType()
+
+> **Base64BufferType**(): `object`
+
+Defined in: [src/types.ts:392](https://github.com/clickup/ent-framework/blob/master/src/types.ts#L392)
+
+A value stored in the DB as a base64 encoded binary buffer. Actually, DB
+engines (like PostgreSQL) support native binary data fields (and store binary
+data efficiently), but sometimes (especially for small things, like
+public/private keys), it's easier to store the binary data as base64 encoded
+strings rather than dealing with the native binary data type.
+
+## Returns
+
+`object`
+
+### dbValueToJs()
+
+> **dbValueToJs**: (`dbValue`) => `Buffer`
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `dbValue` | `string` |
+
+#### Returns
+
+`Buffer`
+
+### stringify()
+
+> **stringify**: (`jsValue`) => `string`
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `jsValue` | `Buffer` |
+
+#### Returns
+
+`string`
+
+### parse()
+
+> **parse**: (`str`) => `Buffer`
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `str` | `string` |
+
+#### Returns
+
+`Buffer`
diff --git a/docs/functions/BaseEnt.md b/docs/functions/BaseEnt.md
new file mode 100644
index 0000000..2fac758
--- /dev/null
+++ b/docs/functions/BaseEnt.md
@@ -0,0 +1,47 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / BaseEnt
+
+# Function: BaseEnt()
+
+> **BaseEnt**\<`TTable`, `TUniqueKey`, `TClient`\>(`cluster`, `schema`): [`HelpersClass`](../interfaces/HelpersClass.md)\<`TTable`, `TUniqueKey`, `TClient`\>
+
+Defined in: [src/ent/BaseEnt.ts:29](https://github.com/clickup/ent-framework/blob/master/src/ent/BaseEnt.ts#L29)
+
+This is a helper function to create new Ent classes. Run once per each
+Ent+Cluster on app boot. See examples in __tests__/TestObjects.ts and
+EntTest.ts.
+
+Since all Ent objects are immutable (following the modern practices),
+1. Ent is not a DataMapper pattern;
+2. Ent is not an ActiveRecord;
+3. At last, Ent is not quite a DAO pattern too.
+
+We assume that Ents are very simple (we don't need triggers or multi-ent
+touching mutations), because we anyway have a GraphQL layer on top of it.
+
+Finally, a naming decision has been made: we translate database field names
+directly to Ent field names, no camelCase. This has proven its simplicity
+benefits in the past: the less translation layers we have, the easier it is
+to debug and develop.
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TTable` *extends* [`Table`](../type-aliases/Table.md) |
+| `TUniqueKey` *extends* [`UniqueKey`](../type-aliases/UniqueKey.md)\<`TTable`\> |
+| `TClient` *extends* [`Client`](../classes/Client.md) |
+
+## Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `cluster` | [`Cluster`](../classes/Cluster.md)\<`TClient`, `any`\> |
+| `schema` | [`Schema`](../classes/Schema.md)\<`TTable`, `TUniqueKey`\> |
+
+## Returns
+
+[`HelpersClass`](../interfaces/HelpersClass.md)\<`TTable`, `TUniqueKey`, `TClient`\>
diff --git a/docs/functions/BigIntArrayType.md b/docs/functions/BigIntArrayType.md
new file mode 100644
index 0000000..2ec9845
--- /dev/null
+++ b/docs/functions/BigIntArrayType.md
@@ -0,0 +1,71 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / BigIntArrayType
+
+# Function: BigIntArrayType()
+
+> **BigIntArrayType**\<`T`\>(): `object`
+
+Defined in: [src/pg/types/BigIntArrayType.ts:19](https://github.com/clickup/ent-framework/blob/master/src/pg/types/BigIntArrayType.ts#L19)
+
+An array of IDs. Notice that:
+1. Node-postgres natively supports this type on read path, but on write path,
+ we have to stringify by ourselves.
+2. GIN index doesn't support NULL, because PG's "&&" operator (intersection
+ check) doesn't work with NULLs. But we still allow NULLs in
+ BigIntArrayType, since to query such values, the user could use a separate
+ partial index.
+
+## Type Parameters
+
+| Type Parameter | Default type |
+| ------ | ------ |
+| `T` *extends* `null` \| `string` | `null` \| `string` |
+
+## Returns
+
+`object`
+
+### dbValueToJs()
+
+> **dbValueToJs**: (`dbValue`) => `T`[]
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `dbValue` | `T`[] |
+
+#### Returns
+
+`T`[]
+
+### stringify()
+
+> **stringify**: (`jsValue`) => `string`
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `jsValue` | `T`[] |
+
+#### Returns
+
+`string`
+
+### parse()
+
+> **parse**: (`str`) => `T`[]
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `str` | `string` |
+
+#### Returns
+
+`T`[]
diff --git a/docs/functions/ByteaBufferType.md b/docs/functions/ByteaBufferType.md
new file mode 100644
index 0000000..c82cbd2
--- /dev/null
+++ b/docs/functions/ByteaBufferType.md
@@ -0,0 +1,59 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / ByteaBufferType
+
+# Function: ByteaBufferType()
+
+> **ByteaBufferType**(): `object`
+
+Defined in: [src/pg/types/ByteaBufferType.ts:6](https://github.com/clickup/ent-framework/blob/master/src/pg/types/ByteaBufferType.ts#L6)
+
+A value stored in the DB as a bytea buffer.
+
+## Returns
+
+`object`
+
+### dbValueToJs()
+
+> **dbValueToJs**: (`dbValue`) => `Buffer`
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `dbValue` | `Buffer` |
+
+#### Returns
+
+`Buffer`
+
+### stringify()
+
+> **stringify**: (`jsValue`) => `string`
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `jsValue` | `Buffer` |
+
+#### Returns
+
+`string`
+
+### parse()
+
+> **parse**: (`str`) => `Buffer`
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `str` | `string` |
+
+#### Returns
+
+`Buffer`
diff --git a/docs/functions/CacheMixin.md b/docs/functions/CacheMixin.md
new file mode 100644
index 0000000..771509c
--- /dev/null
+++ b/docs/functions/CacheMixin.md
@@ -0,0 +1,31 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / CacheMixin
+
+# Function: CacheMixin()
+
+> **CacheMixin**\<`TTable`, `TUniqueKey`, `TClient`\>(`Base`): [`PrimitiveClass`](../type-aliases/PrimitiveClass.md)\<`TTable`, `TUniqueKey`, `TClient`\>
+
+Defined in: [src/ent/mixins/CacheMixin.ts:31](https://github.com/clickup/ent-framework/blob/master/src/ent/mixins/CacheMixin.ts#L31)
+
+Modifies the passed class adding VC-stored cache layer to it.
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TTable` *extends* [`Table`](../type-aliases/Table.md) |
+| `TUniqueKey` *extends* [`UniqueKey`](../type-aliases/UniqueKey.md)\<`TTable`\> |
+| `TClient` *extends* [`Client`](../classes/Client.md) |
+
+## Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `Base` | [`PrimitiveClass`](../type-aliases/PrimitiveClass.md)\<`TTable`, `TUniqueKey`, `TClient`\> |
+
+## Returns
+
+[`PrimitiveClass`](../type-aliases/PrimitiveClass.md)\<`TTable`, `TUniqueKey`, `TClient`\>
diff --git a/docs/functions/ConfigMixin.md b/docs/functions/ConfigMixin.md
new file mode 100644
index 0000000..8dfe22a
--- /dev/null
+++ b/docs/functions/ConfigMixin.md
@@ -0,0 +1,34 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / ConfigMixin
+
+# Function: ConfigMixin()
+
+> **ConfigMixin**\<`TTable`, `TUniqueKey`, `TClient`\>(`Base`, `cluster`, `schema`): [`ConfigClass`](../interfaces/ConfigClass.md)\<`TTable`, `TUniqueKey`, `TClient`\>
+
+Defined in: [src/ent/mixins/ConfigMixin.ts:86](https://github.com/clickup/ent-framework/blob/master/src/ent/mixins/ConfigMixin.ts#L86)
+
+Modifies the passed class adding support for Ent configuration (such as:
+Cluster, table schema, privacy rules, triggers etc.).
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TTable` *extends* [`Table`](../type-aliases/Table.md) |
+| `TUniqueKey` *extends* [`UniqueKey`](../type-aliases/UniqueKey.md)\<`TTable`\> |
+| `TClient` *extends* [`Client`](../classes/Client.md) |
+
+## Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `Base` | () => `object` |
+| `cluster` | [`Cluster`](../classes/Cluster.md)\<`TClient`, `any`\> |
+| `schema` | [`Schema`](../classes/Schema.md)\<`TTable`, `TUniqueKey`\> |
+
+## Returns
+
+[`ConfigClass`](../interfaces/ConfigClass.md)\<`TTable`, `TUniqueKey`, `TClient`\>
diff --git a/docs/functions/HelpersMixin.md b/docs/functions/HelpersMixin.md
new file mode 100644
index 0000000..9f1a1ac
--- /dev/null
+++ b/docs/functions/HelpersMixin.md
@@ -0,0 +1,33 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / HelpersMixin
+
+# Function: HelpersMixin()
+
+> **HelpersMixin**\<`TTable`, `TUniqueKey`, `TClient`\>(`Base`): [`HelpersClass`](../interfaces/HelpersClass.md)\<`TTable`, `TUniqueKey`, `TClient`\>
+
+Defined in: [src/ent/mixins/HelpersMixin.ts:139](https://github.com/clickup/ent-framework/blob/master/src/ent/mixins/HelpersMixin.ts#L139)
+
+Modifies the passed class adding convenience methods (like loadX() which
+throws when an Ent can't be loaded instead of returning null as it's done in
+the primitive operations).
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TTable` *extends* [`Table`](../type-aliases/Table.md) |
+| `TUniqueKey` *extends* [`UniqueKey`](../type-aliases/UniqueKey.md)\<`TTable`\> |
+| `TClient` *extends* [`Client`](../classes/Client.md) |
+
+## Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `Base` | [`PrimitiveClass`](../type-aliases/PrimitiveClass.md)\<`TTable`, `TUniqueKey`, `TClient`\> |
+
+## Returns
+
+[`HelpersClass`](../interfaces/HelpersClass.md)\<`TTable`, `TUniqueKey`, `TClient`\>
diff --git a/docs/functions/JSONType.md b/docs/functions/JSONType.md
new file mode 100644
index 0000000..d71d3a6
--- /dev/null
+++ b/docs/functions/JSONType.md
@@ -0,0 +1,65 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / JSONType
+
+# Function: JSONType()
+
+> **JSONType**\<`TCurrent`\>(): `object`
+
+Defined in: [src/types.ts:418](https://github.com/clickup/ent-framework/blob/master/src/types.ts#L418)
+
+An arbitrary JSON field type.
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TCurrent` *extends* [`JSONValue`](../type-aliases/JSONValue.md) |
+
+## Returns
+
+`object`
+
+### dbValueToJs()
+
+> **dbValueToJs**: (`dbValue`) => `TCurrent`
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `dbValue` | `TCurrent` |
+
+#### Returns
+
+`TCurrent`
+
+### stringify()
+
+> **stringify**: (`jsValue`) => `string`
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `jsValue` | `TCurrent` |
+
+#### Returns
+
+`string`
+
+### parse()
+
+> **parse**: (`str`) => `TCurrent`
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `str` | `string` |
+
+#### Returns
+
+`TCurrent`
diff --git a/docs/functions/PrimitiveMixin.md b/docs/functions/PrimitiveMixin.md
new file mode 100644
index 0000000..4ceeac2
--- /dev/null
+++ b/docs/functions/PrimitiveMixin.md
@@ -0,0 +1,32 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / PrimitiveMixin
+
+# Function: PrimitiveMixin()
+
+> **PrimitiveMixin**\<`TTable`, `TUniqueKey`, `TClient`\>(`Base`): [`PrimitiveClass`](../type-aliases/PrimitiveClass.md)\<`TTable`, `TUniqueKey`, `TClient`\>
+
+Defined in: [src/ent/mixins/PrimitiveMixin.ts:197](https://github.com/clickup/ent-framework/blob/master/src/ent/mixins/PrimitiveMixin.ts#L197)
+
+Modifies the passed class adding support for the minimal number of basic Ent
+operations. Internally, uses Schema abstractions to run them.
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TTable` *extends* [`Table`](../type-aliases/Table.md) |
+| `TUniqueKey` *extends* [`UniqueKey`](../type-aliases/UniqueKey.md)\<`TTable`\> |
+| `TClient` *extends* [`Client`](../classes/Client.md) |
+
+## Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `Base` | [`ConfigClass`](../interfaces/ConfigClass.md)\<`TTable`, `TUniqueKey`, `TClient`\> |
+
+## Returns
+
+[`PrimitiveClass`](../type-aliases/PrimitiveClass.md)\<`TTable`, `TUniqueKey`, `TClient`\>
diff --git a/docs/functions/StringArrayType.md b/docs/functions/StringArrayType.md
new file mode 100644
index 0000000..ac6f1c7
--- /dev/null
+++ b/docs/functions/StringArrayType.md
@@ -0,0 +1,66 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / StringArrayType
+
+# Function: StringArrayType()
+
+> **StringArrayType**\<`T`\>(): `object`
+
+Defined in: [src/pg/types/StringArrayType.ts:14](https://github.com/clickup/ent-framework/blob/master/src/pg/types/StringArrayType.ts#L14)
+
+An array of Strings. Note: node-postgres natively supports this type on read
+path, but on write path, we have to stringify by ourselves.
+
+## Type Parameters
+
+| Type Parameter | Default type |
+| ------ | ------ |
+| `T` *extends* `null` \| `string` | `null` \| `string` |
+
+## Returns
+
+`object`
+
+### dbValueToJs()
+
+> **dbValueToJs**: (`dbValue`) => `T`[]
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `dbValue` | `T`[] |
+
+#### Returns
+
+`T`[]
+
+### stringify()
+
+> **stringify**: (`jsValue`) => `string`
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `jsValue` | `T`[] |
+
+#### Returns
+
+`string`
+
+### parse()
+
+> **parse**: (`str`) => `T`[]
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `str` | `string` |
+
+#### Returns
+
+`T`[]
diff --git a/docs/functions/buildShape.md b/docs/functions/buildShape.md
new file mode 100644
index 0000000..aa4347f
--- /dev/null
+++ b/docs/functions/buildShape.md
@@ -0,0 +1,24 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / buildShape
+
+# Function: buildShape()
+
+> **buildShape**(`sql`): `string`
+
+Defined in: [src/pg/helpers/buildShape.ts:52](https://github.com/clickup/ent-framework/blob/master/src/pg/helpers/buildShape.ts#L52)
+
+Extracts a "shape" from some commonly built SQL queries. This function may be
+used from the outside for logging/debugging, so it's here, not in __tests__.
+
+## Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `sql` | `string` |
+
+## Returns
+
+`string`
diff --git a/docs/functions/escapeIdent.md b/docs/functions/escapeIdent.md
new file mode 100644
index 0000000..6c7cd77
--- /dev/null
+++ b/docs/functions/escapeIdent.md
@@ -0,0 +1,23 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / escapeIdent
+
+# Function: escapeIdent()
+
+> **escapeIdent**(`ident`): `string`
+
+Defined in: [src/pg/helpers/escapeIdent.ts:4](https://github.com/clickup/ent-framework/blob/master/src/pg/helpers/escapeIdent.ts#L4)
+
+Optionally encloses a PG identifier (like table name) in "".
+
+## Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `ident` | `string` |
+
+## Returns
+
+`string`
diff --git a/docs/functions/escapeLiteral.md b/docs/functions/escapeLiteral.md
new file mode 100644
index 0000000..bd1f1b5
--- /dev/null
+++ b/docs/functions/escapeLiteral.md
@@ -0,0 +1,29 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / escapeLiteral
+
+# Function: escapeLiteral()
+
+> **escapeLiteral**(`literal`): `string`
+
+Defined in: [src/pg/helpers/escapeLiteral.ts:15](https://github.com/clickup/ent-framework/blob/master/src/pg/helpers/escapeLiteral.ts#L15)
+
+Builds a part of SQL query using ?-placeholders to prevent SQL Injection.
+Everywhere where we want to accept a piece of SQL, we should instead accept a
+Literal tuple.
+
+The function converts a Literal tuple [fmt, ...args] into a string, escaping
+the args and interpolating them into the format SQL where "?" is a
+placeholder for the replacing value.
+
+## Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `literal` | [`Literal`](../type-aliases/Literal.md) |
+
+## Returns
+
+`string`
diff --git a/docs/functions/evaluate.md b/docs/functions/evaluate.md
new file mode 100644
index 0000000..3143f2f
--- /dev/null
+++ b/docs/functions/evaluate.md
@@ -0,0 +1,72 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / evaluate
+
+# Function: evaluate()
+
+> **evaluate**\<`TInput`\>(`vc`, `input`, `rules`, `fashion`): `Promise`\<\{ `allow`: `boolean`; `results`: [`RuleResult`](../interfaces/RuleResult.md)[]; `cause`: `string`; \}\>
+
+Defined in: [src/ent/rules/evaluate.ts:49](https://github.com/clickup/ent-framework/blob/master/src/ent/rules/evaluate.ts#L49)
+
+This is a hearth of permissions checking, a machine which evaluates the rules
+chain from top to bottom (one after another) and makes the decision based on
+the following logic:
+- ALLOW immediately allows the chain, the rest of the rules are not checked.
+ It's an eager allowance.
+- DENY immediately denies the chain, the rest of the rules are not checked.
+ It's an eager denial.
+- TOLERATE delegates the decision to the next rules; if it's the last
+ decision in the chain, then allows the chain. I.e. it's like an allowance,
+ but only if everyone else is tolerant.
+- SKIP also delegates the decision to the next rules, but if it's the last
+ rule in the chain (i.e. nothing to skip to anymore), denies the chain. I.e.
+ it's "I don't vote here, please ask others".
+- An empty chain is always denied.
+
+Having TOLERATE decision may sound superfluous, but unfortunately it's not.
+The TOLERATE enables usage of the same machinery for both read-like checks
+(where we typically want ANY of the rules to be okay with the row) and for
+write-like checks (where we typically want ALL rules to be okay with the
+row). Having the same logic for everything simplifies the code.
+
+If parallel argument is true, all the rules are run at once in concurrent
+promises before the machine starts. This doesn't affect the final result,
+just speeds up processing if we know that there is a high chance that most of
+the rules will likely return TOLERATE and we'll anyway need to evaluate all
+of them (e.g. most of the rules are Require, like in write operations). As
+opposed, for read operation, there is a high chance for the first rule (which
+is often AllowIf) to succeed, so we evaluate the rules sequentially, not in
+parallel (to minimize the number of DB queries).
+
+Example of a chain (the order of rules always matters!):
+- new Require(new OutgoingEdgePointsToVC("user_id"))
+- new Require(new CanReadOutgoingEdge("post_id", EntPost))
+
+Example of a chain:
+- new AllowIf(new OutgoingEdgePointsToVC("user_id"))
+- new AllowIf(new CanReadOutgoingEdge("post_id", EntPost))
+
+Example of a chain:
+- new DenyIf(new UserIsPendingApproval())
+- new AllowIf(new OutgoingEdgePointsToVC("user_id"))
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TInput` *extends* `object` |
+
+## Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `vc` | [`VC`](../classes/VC.md) |
+| `input` | `TInput` |
+| `rules` | [`Rule`](../classes/Rule.md)\<`TInput`\>[] |
+| `fashion` | `"parallel"` \| `"sequential"` |
+
+## Returns
+
+`Promise`\<\{ `allow`: `boolean`; `results`: [`RuleResult`](../interfaces/RuleResult.md)[]; `cause`: `string`; \}\>
diff --git a/docs/functions/isBigintStr.md b/docs/functions/isBigintStr.md
new file mode 100644
index 0000000..a00bb32
--- /dev/null
+++ b/docs/functions/isBigintStr.md
@@ -0,0 +1,24 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / isBigintStr
+
+# Function: isBigintStr()
+
+> **isBigintStr**(`str`): `boolean`
+
+Defined in: [src/helpers/isBigintStr.ts:8](https://github.com/clickup/ent-framework/blob/master/src/helpers/isBigintStr.ts#L8)
+
+It's hard to support PG bigint type in JS, so people use strings instead.
+This function checks that a string can be passed to PG as a bigint.
+
+## Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `str` | `string` |
+
+## Returns
+
+`boolean`
diff --git a/docs/functions/testSpecTypeIntegrity.md b/docs/functions/testSpecTypeIntegrity.md
new file mode 100644
index 0000000..f6d1c99
--- /dev/null
+++ b/docs/functions/testSpecTypeIntegrity.md
@@ -0,0 +1,45 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / testSpecTypeIntegrity
+
+# Function: testSpecTypeIntegrity()
+
+> **testSpecTypeIntegrity**\<`TDBValue`, `TJsValue`\>(`SpecType`, `dbValue`): `object`
+
+Defined in: [src/helpers/testSpecTypeIntegrity.ts:9](https://github.com/clickup/ent-framework/blob/master/src/helpers/testSpecTypeIntegrity.ts#L9)
+
+A tool to verify integrity of custom field types. It is meant to be called
+from Jest expect(). The helper runs dbValueToJs, stringify and parse methods
+on the type and makes sure that parse() is the opposite of stringify(). The
+returned object can then be compared against a Jest snapshot.
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TDBValue` |
+| `TJsValue` |
+
+## Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `SpecType` | \{ `dbValueToJs`: (`dbValue`) => `TJsValue`; `stringify`: (`jsValue`) => `string`; `parse`: (`str`) => `TJsValue`; \} |
+| `SpecType.dbValueToJs` | (`dbValue`) => `TJsValue` |
+| `SpecType.stringify` | (`jsValue`) => `string` |
+| `SpecType.parse` | (`str`) => `TJsValue` |
+| `dbValue` | `TDBValue` |
+
+## Returns
+
+`object`
+
+### jsValueDecoded
+
+> **jsValueDecoded**: `TJsValue`
+
+### stringifiedBack
+
+> **stringifiedBack**: `string`
diff --git a/docs/globals.md b/docs/globals.md
new file mode 100644
index 0000000..cf1c357
--- /dev/null
+++ b/docs/globals.md
@@ -0,0 +1,219 @@
+[**ent-framework**](README.md)
+
+***
+
+# ent-framework
+
+## Classes
+
+- [Batcher](classes/Batcher.md)
+- [Client](classes/Client.md)
+- [ClientError](classes/ClientError.md)
+- [Cluster](classes/Cluster.md)
+- [Island](classes/Island.md)
+- [Loader](classes/Loader.md)
+- [LocalCache](classes/LocalCache.md)
+- [QueryBase](classes/QueryBase.md)
+- [QueryPing](classes/QueryPing.md)
+- [Runner](classes/Runner.md)
+- [Schema](classes/Schema.md)
+- [Shard](classes/Shard.md)
+- [ShardError](classes/ShardError.md)
+- [ShardIsNotDiscoverableError](classes/ShardIsNotDiscoverableError.md)
+- [ShardNamer](classes/ShardNamer.md)
+- [Timeline](classes/Timeline.md)
+- [TimelineManager](classes/TimelineManager.md)
+- [Configuration](classes/Configuration.md)
+- [IDsCache](classes/IDsCache.md)
+- [Inverse](classes/Inverse.md)
+- [QueryCache](classes/QueryCache.md)
+- [ShardLocator](classes/ShardLocator.md)
+- [TimelineStorage](classes/TimelineStorage.md)
+- [Triggers](classes/Triggers.md)
+- [VC](classes/VC.md)
+- [VCCaches](classes/VCCaches.md)
+- [VCFlavor](classes/VCFlavor.md)
+- [VCWithStacks](classes/VCWithStacks.md)
+- [VCWithQueryCache](classes/VCWithQueryCache.md)
+- [VCTrace](classes/VCTrace.md)
+- [Validation](classes/Validation.md)
+- [EntAccessError](classes/EntAccessError.md)
+- [EntNotFoundError](classes/EntNotFoundError.md)
+- [EntNotInsertableError](classes/EntNotInsertableError.md)
+- [EntNotReadableError](classes/EntNotReadableError.md)
+- [EntNotUpdatableError](classes/EntNotUpdatableError.md)
+- [EntUniqueKeyError](classes/EntUniqueKeyError.md)
+- [EntValidationError](classes/EntValidationError.md)
+- [CanDeleteOutgoingEdge](classes/CanDeleteOutgoingEdge.md)
+- [CanReadOutgoingEdge](classes/CanReadOutgoingEdge.md)
+- [CanUpdateOutgoingEdge](classes/CanUpdateOutgoingEdge.md)
+- [FieldIs](classes/FieldIs.md)
+- [IncomingEdgeFromVCExists](classes/IncomingEdgeFromVCExists.md)
+- [Or](classes/Or.md)
+- [OutgoingEdgePointsToVC](classes/OutgoingEdgePointsToVC.md)
+- [FuncToPredicate](classes/FuncToPredicate.md)
+- [IDsCacheReadable](classes/IDsCacheReadable.md)
+- [IDsCacheUpdatable](classes/IDsCacheUpdatable.md)
+- [IDsCacheDeletable](classes/IDsCacheDeletable.md)
+- [IDsCacheCanReadIncomingEdge](classes/IDsCacheCanReadIncomingEdge.md)
+- [RowIs](classes/RowIs.md)
+- [True](classes/True.md)
+- [VCHasFlavor](classes/VCHasFlavor.md)
+- [AllowIf](classes/AllowIf.md)
+- [DenyIf](classes/DenyIf.md)
+- [Require](classes/Require.md)
+- [Rule](classes/Rule.md)
+- [PgClient](classes/PgClient.md)
+- [PgError](classes/PgError.md)
+- [PgQueryCount](classes/PgQueryCount.md)
+- [PgQueryDelete](classes/PgQueryDelete.md)
+- [PgQueryDeleteWhere](classes/PgQueryDeleteWhere.md)
+- [PgQueryExists](classes/PgQueryExists.md)
+- [PgQueryIDGen](classes/PgQueryIDGen.md)
+- [PgQueryInsert](classes/PgQueryInsert.md)
+- [PgQueryLoad](classes/PgQueryLoad.md)
+- [PgQueryLoadBy](classes/PgQueryLoadBy.md)
+- [PgQuerySelect](classes/PgQuerySelect.md)
+- [PgQuerySelectBy](classes/PgQuerySelectBy.md)
+- [PgQueryUpdate](classes/PgQueryUpdate.md)
+- [PgQueryUpsert](classes/PgQueryUpsert.md)
+- [PgRunner](classes/PgRunner.md)
+- [PgSchema](classes/PgSchema.md)
+- [PgShardNamer](classes/PgShardNamer.md)
+- [PgTimelineStorage](classes/PgTimelineStorage.md)
+- [ToolPing](classes/ToolPing.md)
+- [ToolScoreboard](classes/ToolScoreboard.md)
+
+## Interfaces
+
+- [ClientOptions](interfaces/ClientOptions.md)
+- [ClientConnectionIssue](interfaces/ClientConnectionIssue.md)
+- [ClientPingInput](interfaces/ClientPingInput.md)
+- [ClusterOptions](interfaces/ClusterOptions.md)
+- [IslandOptions](interfaces/IslandOptions.md)
+- [Handler](interfaces/Handler.md)
+- [LocalCacheOptions](interfaces/LocalCacheOptions.md)
+- [Loggers](interfaces/Loggers.md)
+- [ClientQueryLoggerProps](interfaces/ClientQueryLoggerProps.md)
+- [SwallowedErrorLoggerProps](interfaces/SwallowedErrorLoggerProps.md)
+- [RunOnShardErrorLoggerProps](interfaces/RunOnShardErrorLoggerProps.md)
+- [ClientEndLoggerProps](interfaces/ClientEndLoggerProps.md)
+- [ClientConnectedLoggerProps](interfaces/ClientConnectedLoggerProps.md)
+- [Query](interfaces/Query.md)
+- [QueryAnnotation](interfaces/QueryAnnotation.md)
+- [SchemaClass](interfaces/SchemaClass.md)
+- [ShardNamerOptions](interfaces/ShardNamerOptions.md)
+- [TimelineStorageOptions](interfaces/TimelineStorageOptions.md)
+- [StandardSchemaV1FailureResult](interfaces/StandardSchemaV1FailureResult.md)
+- [EntValidationErrorInfo](interfaces/EntValidationErrorInfo.md)
+- [ConfigInstance](interfaces/ConfigInstance.md)
+- [ConfigClass](interfaces/ConfigClass.md)
+- [HelpersInstance](interfaces/HelpersInstance.md)
+- [HelpersClass](interfaces/HelpersClass.md)
+- [PrimitiveInstance](interfaces/PrimitiveInstance.md)
+- [AbstractIs](interfaces/AbstractIs.md)
+- [Predicate](interfaces/Predicate.md)
+- [RuleResult](interfaces/RuleResult.md)
+- [EntClass](interfaces/EntClass.md)
+- [Ent](interfaces/Ent.md)
+- [PgClientOptions](interfaces/PgClientOptions.md)
+- [PgClientConn](interfaces/PgClientConn.md)
+- [PgClientSubPoolConfig](interfaces/PgClientSubPoolConfig.md)
+- [PgTimelineStorageOptions](interfaces/PgTimelineStorageOptions.md)
+- [ToolPingOptions](interfaces/ToolPingOptions.md)
+- [ToolScoreboardOptions](interfaces/ToolScoreboardOptions.md)
+
+## Type Aliases
+
+- [ClientRole](type-aliases/ClientRole.md)
+- [ClientErrorPostAction](type-aliases/ClientErrorPostAction.md)
+- [ClientErrorKind](type-aliases/ClientErrorKind.md)
+- [ClusterIslands](type-aliases/ClusterIslands.md)
+- [WhyClient](type-aliases/WhyClient.md)
+- [TimelineCaughtUpReason](type-aliases/TimelineCaughtUpReason.md)
+- [AnyClass](type-aliases/AnyClass.md)
+- [ShardAffinity](type-aliases/ShardAffinity.md)
+- [TriggerInsertInput](type-aliases/TriggerInsertInput.md)
+- [TriggerUpdateInput](type-aliases/TriggerUpdateInput.md)
+- [TriggerUpdateNewRow](type-aliases/TriggerUpdateNewRow.md)
+- [TriggerUpdateOrDeleteOldRow](type-aliases/TriggerUpdateOrDeleteOldRow.md)
+- [InsertTrigger](type-aliases/InsertTrigger.md)
+- [BeforeUpdateTrigger](type-aliases/BeforeUpdateTrigger.md)
+- [AfterUpdateTrigger](type-aliases/AfterUpdateTrigger.md)
+- [DeleteTrigger](type-aliases/DeleteTrigger.md)
+- [BeforeMutationTrigger](type-aliases/BeforeMutationTrigger.md)
+- [AfterMutationTrigger](type-aliases/AfterMutationTrigger.md)
+- [DepsBuilder](type-aliases/DepsBuilder.md)
+- [LoadRule](type-aliases/LoadRule.md)
+- [WriteRules](type-aliases/WriteRules.md)
+- [ValidationRules](type-aliases/ValidationRules.md)
+- [PrimitiveClass](type-aliases/PrimitiveClass.md)
+- [ValidatorPlainResult](type-aliases/ValidatorPlainResult.md)
+- [ValidatorZodSafeParseResult](type-aliases/ValidatorZodSafeParseResult.md)
+- [ValidatorStandardSchemaResult](type-aliases/ValidatorStandardSchemaResult.md)
+- [FieldIsValidatorPlain](type-aliases/FieldIsValidatorPlain.md)
+- [FieldIsValidatorZodSafeParse](type-aliases/FieldIsValidatorZodSafeParse.md)
+- [FieldIsValidatorStandardSchemaV1](type-aliases/FieldIsValidatorStandardSchemaV1.md)
+- [RowIsValidatorPlain](type-aliases/RowIsValidatorPlain.md)
+- [RowIsValidatorZodSafeParse](type-aliases/RowIsValidatorZodSafeParse.md)
+- [RowIsValidatorStandardSchemaV1](type-aliases/RowIsValidatorStandardSchemaV1.md)
+- [RuleDecision](type-aliases/RuleDecision.md)
+- [UpdateOriginalInput](type-aliases/UpdateOriginalInput.md)
+- [SelectInputCustom](type-aliases/SelectInputCustom.md)
+- [Literal](type-aliases/Literal.md)
+- [RowWithID](type-aliases/RowWithID.md)
+- [SpecType](type-aliases/SpecType.md)
+- [Spec](type-aliases/Spec.md)
+- [Table](type-aliases/Table.md)
+- [Field](type-aliases/Field.md)
+- [FieldAliased](type-aliases/FieldAliased.md)
+- [FieldOfPotentialUniqueKey](type-aliases/FieldOfPotentialUniqueKey.md)
+- [FieldOfIDType](type-aliases/FieldOfIDType.md)
+- [FieldOfIDTypeRequired](type-aliases/FieldOfIDTypeRequired.md)
+- [ValueRequired](type-aliases/ValueRequired.md)
+- [Value](type-aliases/Value.md)
+- [Row](type-aliases/Row.md)
+- [InsertFieldsRequired](type-aliases/InsertFieldsRequired.md)
+- [InsertFieldsOptional](type-aliases/InsertFieldsOptional.md)
+- [InsertInput](type-aliases/InsertInput.md)
+- [UpdateField](type-aliases/UpdateField.md)
+- [UpdateInput](type-aliases/UpdateInput.md)
+- [UniqueKey](type-aliases/UniqueKey.md)
+- [LoadByInput](type-aliases/LoadByInput.md)
+- [SelectByInput](type-aliases/SelectByInput.md)
+- [Where](type-aliases/Where.md)
+- [Order](type-aliases/Order.md)
+- [SelectInput](type-aliases/SelectInput.md)
+- [CountInput](type-aliases/CountInput.md)
+- [ExistsInput](type-aliases/ExistsInput.md)
+- [DeleteWhereInput](type-aliases/DeleteWhereInput.md)
+- [Hints](type-aliases/Hints.md)
+- [JSONValue](type-aliases/JSONValue.md)
+
+## Variables
+
+- [MASTER](variables/MASTER.md)
+- [STALE\_REPLICA](variables/STALE_REPLICA.md)
+- [GLOBAL\_SHARD](variables/GLOBAL_SHARD.md)
+- [GUEST\_ID](variables/GUEST_ID.md)
+- [OMNI\_ID](variables/OMNI_ID.md)
+- [ID](variables/ID.md)
+
+## Functions
+
+- [BaseEnt](functions/BaseEnt.md)
+- [CacheMixin](functions/CacheMixin.md)
+- [ConfigMixin](functions/ConfigMixin.md)
+- [HelpersMixin](functions/HelpersMixin.md)
+- [PrimitiveMixin](functions/PrimitiveMixin.md)
+- [evaluate](functions/evaluate.md)
+- [isBigintStr](functions/isBigintStr.md)
+- [testSpecTypeIntegrity](functions/testSpecTypeIntegrity.md)
+- [buildShape](functions/buildShape.md)
+- [escapeIdent](functions/escapeIdent.md)
+- [escapeLiteral](functions/escapeLiteral.md)
+- [BigIntArrayType](functions/BigIntArrayType.md)
+- [ByteaBufferType](functions/ByteaBufferType.md)
+- [StringArrayType](functions/StringArrayType.md)
+- [Base64BufferType](functions/Base64BufferType.md)
+- [JSONType](functions/JSONType.md)
diff --git a/docs/interfaces/AbstractIs.md b/docs/interfaces/AbstractIs.md
new file mode 100644
index 0000000..256b4c8
--- /dev/null
+++ b/docs/interfaces/AbstractIs.md
@@ -0,0 +1,52 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / AbstractIs
+
+# Interface: AbstractIs\
+
+Defined in: [src/ent/predicates/AbstractIs.ts:6](https://github.com/clickup/ent-framework/blob/master/src/ent/predicates/AbstractIs.ts#L6)
+
+A base interface for all user validation predicates.
+
+## Extends
+
+- [`Predicate`](Predicate.md)\<`TRow`\>
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TRow` |
+
+## Properties
+
+| Property | Type | Description |
+| ------ | ------ | ------ |
+| `field` | `null` \| `string` | The field this validation predicate is related to (null means that it applies to the entire Ent). |
+| `message` | `null` \| `string` | In case the predicate returns false or doesn't provide error messages by throwing EntValidationError, this message will be used. When message is null, it means that we expect the validator to return detailed information about each field errored (e.g. ValidatorStandardSchemaResult). |
+| `name` | `string` | - |
+
+## Methods
+
+### check()
+
+> **check**(`vc`, `input`): `Promise`\<`boolean`\>
+
+Defined in: [src/ent/predicates/Predicate.ts:19](https://github.com/clickup/ent-framework/blob/master/src/ent/predicates/Predicate.ts#L19)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `vc` | [`VC`](../classes/VC.md) |
+| `input` | `TRow` |
+
+#### Returns
+
+`Promise`\<`boolean`\>
+
+#### Inherited from
+
+[`Predicate`](Predicate.md).[`check`](Predicate.md#check)
diff --git a/docs/interfaces/ClientConnectedLoggerProps.md b/docs/interfaces/ClientConnectedLoggerProps.md
new file mode 100644
index 0000000..f9806df
--- /dev/null
+++ b/docs/interfaces/ClientConnectedLoggerProps.md
@@ -0,0 +1,21 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / ClientConnectedLoggerProps
+
+# Interface: ClientConnectedLoggerProps\
+
+Defined in: [src/abstract/Loggers.ts:78](https://github.com/clickup/ent-framework/blob/master/src/abstract/Loggers.ts#L78)
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TNode` |
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `node` | `TNode` |
diff --git a/docs/interfaces/ClientConnectionIssue.md b/docs/interfaces/ClientConnectionIssue.md
new file mode 100644
index 0000000..b08f44a
--- /dev/null
+++ b/docs/interfaces/ClientConnectionIssue.md
@@ -0,0 +1,21 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / ClientConnectionIssue
+
+# Interface: ClientConnectionIssue
+
+Defined in: [src/abstract/Client.ts:45](https://github.com/clickup/ent-framework/blob/master/src/abstract/Client.ts#L45)
+
+An information about Client's connection related issue.
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `timestamp` | `Date` |
+| `cause` | `unknown` |
+| `postAction` | [`ClientErrorPostAction`](../type-aliases/ClientErrorPostAction.md) |
+| `kind` | [`ClientErrorKind`](../type-aliases/ClientErrorKind.md) |
+| `comment` | `string` |
diff --git a/docs/interfaces/ClientEndLoggerProps.md b/docs/interfaces/ClientEndLoggerProps.md
new file mode 100644
index 0000000..d5713bf
--- /dev/null
+++ b/docs/interfaces/ClientEndLoggerProps.md
@@ -0,0 +1,23 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / ClientEndLoggerProps
+
+# Interface: ClientEndLoggerProps\
+
+Defined in: [src/abstract/Loggers.ts:72](https://github.com/clickup/ent-framework/blob/master/src/abstract/Loggers.ts#L72)
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TNode` |
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `client` | [`Client`](../classes/Client.md) |
+| `key` | `string` |
+| `node` | `TNode` |
diff --git a/docs/interfaces/ClientOptions.md b/docs/interfaces/ClientOptions.md
new file mode 100644
index 0000000..ca65dcb
--- /dev/null
+++ b/docs/interfaces/ClientOptions.md
@@ -0,0 +1,24 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / ClientOptions
+
+# Interface: ClientOptions
+
+Defined in: [src/abstract/Client.ts:18](https://github.com/clickup/ent-framework/blob/master/src/abstract/Client.ts#L18)
+
+Options for Client constructor.
+
+## Extended by
+
+- [`PgClientOptions`](PgClientOptions.md)
+
+## Properties
+
+| Property | Type | Description |
+| ------ | ------ | ------ |
+| `name` | `string` | Name of the Client; used for logging. |
+| `shardNamer?` | `null` \| [`ShardNamer`](../classes/ShardNamer.md) | Info on how to build/parse Shard names. If not set, then Cluster injects its own ShardNamer here right after creating a Client instance. |
+| `loggers?` | `null` \| [`Loggers`](Loggers.md)\<`any`\> | Loggers to be called at different stages. Client code calls into them. Also, Cluster injects its own loggers here, in addition to the provided ones (if any). |
+| `batchDelayMs?` | `MaybeCallable`\<`number`\> | If passed, there will be an artificial queries accumulation delay while batching the requests. Default is 0 (turned off). Passed to Batcher#batchDelayMs. |
diff --git a/docs/interfaces/ClientPingInput.md b/docs/interfaces/ClientPingInput.md
new file mode 100644
index 0000000..67b3579
--- /dev/null
+++ b/docs/interfaces/ClientPingInput.md
@@ -0,0 +1,19 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / ClientPingInput
+
+# Interface: ClientPingInput
+
+Defined in: [src/abstract/Client.ts:56](https://github.com/clickup/ent-framework/blob/master/src/abstract/Client.ts#L56)
+
+Input for Client#ping().
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `execTimeMs` | `number` |
+| `isWrite` | `boolean` |
+| `annotation` | [`QueryAnnotation`](QueryAnnotation.md) |
diff --git a/docs/interfaces/ClientQueryLoggerProps.md b/docs/interfaces/ClientQueryLoggerProps.md
new file mode 100644
index 0000000..166840b
--- /dev/null
+++ b/docs/interfaces/ClientQueryLoggerProps.md
@@ -0,0 +1,35 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / ClientQueryLoggerProps
+
+# Interface: ClientQueryLoggerProps
+
+Defined in: [src/abstract/Loggers.ts:27](https://github.com/clickup/ent-framework/blob/master/src/abstract/Loggers.ts#L27)
+
+## Properties
+
+| Property | Type | Description |
+| ------ | ------ | ------ |
+| `annotations` | [`QueryAnnotation`](QueryAnnotation.md)[] | - |
+| `op` | `string` | - |
+| `shard` | `string` | - |
+| `table` | `string` | - |
+| `batchFactor` | `number` | - |
+| `msg` | `string` | - |
+| `output` | `unknown` | - |
+| `elapsed` | `object` | - |
+| `elapsed.total` | `number` | - |
+| `elapsed.acquire` | `number` | - |
+| `connStats` | `object` | - |
+| `connStats.id` | `string` | The stats related to the used connection. |
+| `connStats.queriesSent` | `number` | The number of queries sent to the connection. |
+| `poolStats` | `object` | - |
+| `poolStats.totalConns` | `number` | Total number of connections in the pool. |
+| `poolStats.idleConns` | `number` | Connections not busy running a query. |
+| `poolStats.queuedReqs` | `number` | Once all idle connections are over, requests are queued waiting for a new available connection. This is the number of such queued requests. |
+| `error` | `undefined` \| `string` | - |
+| `role` | [`ClientRole`](../type-aliases/ClientRole.md) | - |
+| `backend` | `string` | - |
+| `address` | `string` | - |
diff --git a/docs/interfaces/ClusterOptions.md b/docs/interfaces/ClusterOptions.md
new file mode 100644
index 0000000..e4d7662
--- /dev/null
+++ b/docs/interfaces/ClusterOptions.md
@@ -0,0 +1,35 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / ClusterOptions
+
+# Interface: ClusterOptions\
+
+Defined in: [src/abstract/Cluster.ts:45](https://github.com/clickup/ent-framework/blob/master/src/abstract/Cluster.ts#L45)
+
+Options for Cluster constructor.
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TClient` *extends* [`Client`](../classes/Client.md) |
+| `TNode` |
+
+## Properties
+
+| Property | Type | Description |
+| ------ | ------ | ------ |
+| `islands` | `MaybeAsyncCallable`\<[`ClusterIslands`](../type-aliases/ClusterIslands.md)\<`TNode`\>\> | Islands configuration of the Cluster. |
+| `createClient` | (`node`: `TNode`) => `TClient` | Given a node of some Island, instantiates a Client for this node. Called when a new node appears in the Cluster statically or dynamically. |
+| `loggers` | [`Loggers`](Loggers.md)\<`any`\> | Loggers to be injected into all Clients returned by createClient(). |
+| `localCache?` | `null` \| [`LocalCache`](../classes/LocalCache.md)\<`never`\> | An instance of LocalCache which may be used for auxiliary purposes when discovering Shards/Clients. |
+| `reloadIslandsIntervalMs?` | `MaybeCallable`\<`number`\> | How often to recheck for changes in `options.islands`. If it is SYNC, then by default - often, like every 500 ms (since it's assumed that `options.islands` calculation is cheap). If it is ASYNC, then by default - not so often, every `shardsDiscoverIntervalMs` (we assume that getting the list of Island nodes may be expensive, e.g. fetching from AWS API or so). If the Islands list here changes, then we trigger Shards rediscovery and Clients recreation ASAP. |
+| `shardNamer?` | `null` \| [`ShardNamer`](../classes/ShardNamer.md) | Info on how to build/parse Shard names. |
+| `shardsDiscoverIntervalMs?` | `MaybeCallable`\<`number`\> | How often to run Shards rediscovery in normal circumstances. |
+| `shardsDiscoverIntervalJitter?` | `MaybeCallable`\<`number`\> | Jitter for shardsDiscoverIntervalMs and reloadIslandsIntervalMs. |
+| `runOnShardErrorRetryCount?` | `MaybeCallable`\<`number`\> | Used in the following situations: 1. If we think that we know the Island of a particular Shard, but an attempt to access it fails. This means that maybe the Shard is migrating to another Island. So, we wait a bit and retry that many times. We should not do it too many times though, because all DB requests will be blocked waiting for the resolution. 2. If we sent a WRITE request to a Client, but it appeared that this Client is a replica, and the master moved to some other Client. In this case, we wait a bit and ping all Clients of the Island to refresh, who is master and who is replica. |
+| `runOnShardErrorRediscoverClusterDelayMs?` | `MaybeCallable`\<`number`\> | How much time to wait before we retry rediscovering the entire Cluster after a Shard-to-Island resolution error. The time here should be just enough to wait for switching the Shard from one Island to another (typically quick). |
+| `runOnShardErrorRediscoverIslandDelayMs?` | `MaybeCallable`\<`number`\> | How much time to wait before sending discover requests to all Clients of the Island trying to find the new master (or to reconnect). The time here may reach several seconds, since some DBs shut down the old master and promote some replica to it not simultaneously. |
+| `clientEndDelayMs?` | `MaybeCallable`\<`number`\> | Delay the Client ending when this Client got removed from the Cluster. This allows to lower the number of client_is_ended errors in case a Client instance gets captured somewhere in the application code. |
diff --git a/docs/interfaces/ConfigClass.md b/docs/interfaces/ConfigClass.md
new file mode 100644
index 0000000..0e3aa52
--- /dev/null
+++ b/docs/interfaces/ConfigClass.md
@@ -0,0 +1,63 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / ConfigClass
+
+# Interface: ConfigClass\
+
+Defined in: [src/ent/mixins/ConfigMixin.ts:17](https://github.com/clickup/ent-framework/blob/master/src/ent/mixins/ConfigMixin.ts#L17)
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TTable` *extends* [`Table`](../type-aliases/Table.md) |
+| `TUniqueKey` *extends* [`UniqueKey`](../type-aliases/UniqueKey.md)\<`TTable`\> |
+| `TClient` *extends* [`Client`](../classes/Client.md) |
+
+## Constructors
+
+### new ConfigClass()
+
+> **new ConfigClass**(): [`ConfigInstance`](ConfigInstance.md)
+
+Defined in: [src/ent/mixins/ConfigMixin.ts:79](https://github.com/clickup/ent-framework/blob/master/src/ent/mixins/ConfigMixin.ts#L79)
+
+TS requires us to have a public constructor to infer instance types in
+various places. We make this constructor throw if it's called.
+
+#### Returns
+
+[`ConfigInstance`](ConfigInstance.md)
+
+## Properties
+
+| Property | Type | Description |
+| ------ | ------ | ------ |
+| `Configuration` | (`cfg`: [`Configuration`](../classes/Configuration.md)\<`TTable`\>) => [`Configuration`](../classes/Configuration.md)\<`TTable`\> | A helper class to work-around TS weakness in return value type inference: https://github.com/Microsoft/TypeScript/issues/31273. It could've been just a function, but having a class is a little more natural. |
+| `CLUSTER` | [`Cluster`](../classes/Cluster.md)\<`TClient`, `any`\> | A Cluster where this Ent lives. |
+| `SCHEMA` | [`Schema`](../classes/Schema.md)\<`TTable`, `TUniqueKey`\> | A schema which represents this Ent. |
+| `SHARD_AFFINITY` | [`ShardAffinity`](../type-aliases/ShardAffinity.md)\<[`FieldOfIDType`](../type-aliases/FieldOfIDType.md)\<`TTable`\>\> | Defines how to find the right Shard during Ent insertion. |
+| `SHARD_LOCATOR` | [`ShardLocator`](../classes/ShardLocator.md)\<`TClient`, `TTable`, [`FieldOfIDType`](../type-aliases/FieldOfIDType.md)\<`TTable`\>\> | Shard locator for this Ent, responsible for resolving IDs into Shard objects. |
+| `VALIDATION` | [`Validation`](../classes/Validation.md)\<`TTable`\> | Privacy rules for this Ent class. |
+| `TRIGGERS` | [`Triggers`](../classes/Triggers.md)\<`TTable`\> | Triggers for this Ent class. |
+| `INVERSES` | [`Inverse`](../classes/Inverse.md)\<`TClient`, `TTable`\>[] | Inverse assoc managers for fields. |
+
+## Methods
+
+### configure()
+
+> **configure**(): [`Configuration`](../classes/Configuration.md)\<`TTable`\>
+
+Defined in: [src/ent/mixins/ConfigMixin.ts:29](https://github.com/clickup/ent-framework/blob/master/src/ent/mixins/ConfigMixin.ts#L29)
+
+Some Ent parameters need to be configured lazily, on the 1st access,
+because there could be cyclic references between Ent classes (e.g. in their
+privacy rules). So configure() is called on some later stage, at the moment
+of actual Ent operations (like loading, creation etc.). There is no static
+abstract methods in TS yet, so making it non-abstract.
+
+#### Returns
+
+[`Configuration`](../classes/Configuration.md)\<`TTable`\>
diff --git a/docs/interfaces/ConfigInstance.md b/docs/interfaces/ConfigInstance.md
new file mode 100644
index 0000000..c5d7239
--- /dev/null
+++ b/docs/interfaces/ConfigInstance.md
@@ -0,0 +1,13 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / ConfigInstance
+
+# Interface: ConfigInstance
+
+Defined in: [src/ent/mixins/ConfigMixin.ts:15](https://github.com/clickup/ent-framework/blob/master/src/ent/mixins/ConfigMixin.ts#L15)
+
+## Extended by
+
+- [`PrimitiveInstance`](PrimitiveInstance.md)
diff --git a/docs/interfaces/Ent.md b/docs/interfaces/Ent.md
new file mode 100644
index 0000000..6e9e9c7
--- /dev/null
+++ b/docs/interfaces/Ent.md
@@ -0,0 +1,54 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / Ent
+
+# Interface: Ent\
+
+Defined in: [src/ent/types.ts:69](https://github.com/clickup/ent-framework/blob/master/src/ent/types.ts#L69)
+
+A very shallow interface of one Ent.
+
+## Type Parameters
+
+| Type Parameter | Default type |
+| ------ | ------ |
+| `TTable` *extends* [`Table`](../type-aliases/Table.md) | `object` |
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `id` | `string` |
+| `vc` | [`VC`](../classes/VC.md) |
+
+## Methods
+
+### deleteOriginal()
+
+> **deleteOriginal**(): `Promise`\<`boolean`\>
+
+Defined in: [src/ent/types.ts:72](https://github.com/clickup/ent-framework/blob/master/src/ent/types.ts#L72)
+
+#### Returns
+
+`Promise`\<`boolean`\>
+
+***
+
+### updateOriginal()
+
+> **updateOriginal**(`input`): `Promise`\<`boolean`\>
+
+Defined in: [src/ent/types.ts:73](https://github.com/clickup/ent-framework/blob/master/src/ent/types.ts#L73)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `input` | [`UpdateOriginalInput`](../type-aliases/UpdateOriginalInput.md)\<`TTable`\> |
+
+#### Returns
+
+`Promise`\<`boolean`\>
diff --git a/docs/interfaces/EntClass.md b/docs/interfaces/EntClass.md
new file mode 100644
index 0000000..9c50f07
--- /dev/null
+++ b/docs/interfaces/EntClass.md
@@ -0,0 +1,253 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / EntClass
+
+# Interface: EntClass\
+
+Defined in: [src/ent/types.ts:28](https://github.com/clickup/ent-framework/blob/master/src/ent/types.ts#L28)
+
+A very shallow interface of Ent class (as a collection of static methods).
+Used in some places where we need the very minimum from the Ent.
+
+## Type Parameters
+
+| Type Parameter | Default type |
+| ------ | ------ |
+| `TTable` *extends* [`Table`](../type-aliases/Table.md) | `DesperateAny` |
+
+## Constructors
+
+### new EntClass()
+
+> **new EntClass**(): [`Ent`](Ent.md)\<`TTable`\>
+
+Defined in: [src/ent/types.ts:34](https://github.com/clickup/ent-framework/blob/master/src/ent/types.ts#L34)
+
+#### Returns
+
+[`Ent`](Ent.md)\<`TTable`\>
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `SCHEMA` | [`Schema`](../classes/Schema.md)\<`TTable`, [`UniqueKey`](../type-aliases/UniqueKey.md)\<`TTable`\>\> |
+| `VALIDATION` | [`Validation`](../classes/Validation.md)\<`TTable`\> |
+| `SHARD_LOCATOR` | [`ShardLocator`](../classes/ShardLocator.md)\<[`Client`](../classes/Client.md), `TTable`, `string`\> |
+| `name` | `string` |
+
+## Methods
+
+### loadX()
+
+> **loadX**(`vc`, `id`): `Promise`\<[`Ent`](Ent.md)\<`TTable`\>\>
+
+Defined in: [src/ent/types.ts:35](https://github.com/clickup/ent-framework/blob/master/src/ent/types.ts#L35)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `vc` | [`VC`](../classes/VC.md) |
+| `id` | `string` |
+
+#### Returns
+
+`Promise`\<[`Ent`](Ent.md)\<`TTable`\>\>
+
+***
+
+### loadNullable()
+
+> **loadNullable**(`vc`, `id`): `Promise`\<`null` \| [`Ent`](Ent.md)\<`TTable`\>\>
+
+Defined in: [src/ent/types.ts:36](https://github.com/clickup/ent-framework/blob/master/src/ent/types.ts#L36)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `vc` | [`VC`](../classes/VC.md) |
+| `id` | `string` |
+
+#### Returns
+
+`Promise`\<`null` \| [`Ent`](Ent.md)\<`TTable`\>\>
+
+***
+
+### loadIfReadableNullable()
+
+> **loadIfReadableNullable**(`vc`, `id`): `Promise`\<`null` \| [`Ent`](Ent.md)\<`TTable`\>\>
+
+Defined in: [src/ent/types.ts:37](https://github.com/clickup/ent-framework/blob/master/src/ent/types.ts#L37)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `vc` | [`VC`](../classes/VC.md) |
+| `id` | `string` |
+
+#### Returns
+
+`Promise`\<`null` \| [`Ent`](Ent.md)\<`TTable`\>\>
+
+***
+
+### count()
+
+> **count**(`vc`, `where`): `Promise`\<`number`\>
+
+Defined in: [src/ent/types.ts:38](https://github.com/clickup/ent-framework/blob/master/src/ent/types.ts#L38)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `vc` | [`VC`](../classes/VC.md) |
+| `where` | [`CountInput`](../type-aliases/CountInput.md)\<`TTable`\> |
+
+#### Returns
+
+`Promise`\<`number`\>
+
+***
+
+### exists()
+
+> **exists**(`vc`, `where`): `Promise`\<`boolean`\>
+
+Defined in: [src/ent/types.ts:39](https://github.com/clickup/ent-framework/blob/master/src/ent/types.ts#L39)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `vc` | [`VC`](../classes/VC.md) |
+| `where` | [`ExistsInput`](../type-aliases/ExistsInput.md)\<`TTable`\> |
+
+#### Returns
+
+`Promise`\<`boolean`\>
+
+***
+
+### select()
+
+> **select**(`vc`, `where`, `limit`, `order`?): `Promise`\<[`Ent`](Ent.md)\<`TTable`\>[]\>
+
+Defined in: [src/ent/types.ts:40](https://github.com/clickup/ent-framework/blob/master/src/ent/types.ts#L40)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `vc` | [`VC`](../classes/VC.md) |
+| `where` | [`Where`](../type-aliases/Where.md)\<`TTable`\> |
+| `limit` | `number` |
+| `order`? | [`Order`](../type-aliases/Order.md)\<`TTable`\> |
+
+#### Returns
+
+`Promise`\<[`Ent`](Ent.md)\<`TTable`\>[]\>
+
+***
+
+### selectChunked()
+
+> **selectChunked**(`vc`, `where`, `chunkSize`, `limit`, `custom`?): `AsyncIterableIterator`\<[`Ent`](Ent.md)\<`TTable`\>[], `any`, `any`\>
+
+Defined in: [src/ent/types.ts:46](https://github.com/clickup/ent-framework/blob/master/src/ent/types.ts#L46)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `vc` | [`VC`](../classes/VC.md) |
+| `where` | [`Where`](../type-aliases/Where.md)\<`TTable`\> |
+| `chunkSize` | `number` |
+| `limit` | `number` |
+| `custom`? | \{\} |
+
+#### Returns
+
+`AsyncIterableIterator`\<[`Ent`](Ent.md)\<`TTable`\>[], `any`, `any`\>
+
+***
+
+### loadByX()
+
+> **loadByX**(`vc`, `keys`): `Promise`\<[`Ent`](Ent.md)\<`TTable`\>\>
+
+Defined in: [src/ent/types.ts:53](https://github.com/clickup/ent-framework/blob/master/src/ent/types.ts#L53)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `vc` | [`VC`](../classes/VC.md) |
+| `keys` | `{ [K in string]: Value }` |
+
+#### Returns
+
+`Promise`\<[`Ent`](Ent.md)\<`TTable`\>\>
+
+***
+
+### loadByNullable()
+
+> **loadByNullable**(`vc`, `input`): `Promise`\<`null` \| [`Ent`](Ent.md)\<`TTable`\>\>
+
+Defined in: [src/ent/types.ts:57](https://github.com/clickup/ent-framework/blob/master/src/ent/types.ts#L57)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `vc` | [`VC`](../classes/VC.md) |
+| `input` | `{ [K in string]: Value }` |
+
+#### Returns
+
+`Promise`\<`null` \| [`Ent`](Ent.md)\<`TTable`\>\>
+
+***
+
+### insert()
+
+> **insert**(`vc`, `input`): `Promise`\<`string`\>
+
+Defined in: [src/ent/types.ts:61](https://github.com/clickup/ent-framework/blob/master/src/ent/types.ts#L61)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `vc` | [`VC`](../classes/VC.md) |
+| `input` | [`InsertInput`](../type-aliases/InsertInput.md)\<`TTable`\> |
+
+#### Returns
+
+`Promise`\<`string`\>
+
+***
+
+### upsert()
+
+> **upsert**(`vc`, `input`): `Promise`\<`string`\>
+
+Defined in: [src/ent/types.ts:62](https://github.com/clickup/ent-framework/blob/master/src/ent/types.ts#L62)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `vc` | [`VC`](../classes/VC.md) |
+| `input` | [`InsertInput`](../type-aliases/InsertInput.md)\<`TTable`\> |
+
+#### Returns
+
+`Promise`\<`string`\>
diff --git a/docs/interfaces/EntValidationErrorInfo.md b/docs/interfaces/EntValidationErrorInfo.md
new file mode 100644
index 0000000..838fdf0
--- /dev/null
+++ b/docs/interfaces/EntValidationErrorInfo.md
@@ -0,0 +1,18 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / EntValidationErrorInfo
+
+# Interface: EntValidationErrorInfo
+
+Defined in: [src/ent/errors/EntValidationError.ts:46](https://github.com/clickup/ent-framework/blob/master/src/ent/errors/EntValidationError.ts#L46)
+
+Auxiliary information which every validation predicate should emit.
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `field` | `null` \| `string` |
+| `message` | `string` |
diff --git a/docs/interfaces/Handler.md b/docs/interfaces/Handler.md
new file mode 100644
index 0000000..355da84
--- /dev/null
+++ b/docs/interfaces/Handler.md
@@ -0,0 +1,25 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / Handler
+
+# Interface: Handler\
+
+Defined in: [src/abstract/Loader.ts:4](https://github.com/clickup/ent-framework/blob/master/src/abstract/Loader.ts#L4)
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TLoadArgs` *extends* `unknown`[] |
+| `TReturn` |
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `onCollect` | (...`args`: `TLoadArgs`) => `void` \| `"flush"` \| `"wait"` |
+| `onWait?` | () => `Promise`\<`void`\> |
+| `onFlush` | (`collected`: `number`) => `Promise`\<`void`\> |
+| `onReturn` | (...`args`: `TLoadArgs`) => `TReturn` |
diff --git a/docs/interfaces/HelpersClass.md b/docs/interfaces/HelpersClass.md
new file mode 100644
index 0000000..ec7c9d7
--- /dev/null
+++ b/docs/interfaces/HelpersClass.md
@@ -0,0 +1,90 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / HelpersClass
+
+# Interface: HelpersClass\
+
+Defined in: [src/ent/mixins/HelpersMixin.ts:68](https://github.com/clickup/ent-framework/blob/master/src/ent/mixins/HelpersMixin.ts#L68)
+
+## Extends
+
+- `OmitNew`\<[`PrimitiveClass`](../type-aliases/PrimitiveClass.md)\<`TTable`, `TUniqueKey`, `TClient`\>\>
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TTable` *extends* [`Table`](../type-aliases/Table.md) |
+| `TUniqueKey` *extends* [`UniqueKey`](../type-aliases/UniqueKey.md)\<`TTable`\> |
+| `TClient` *extends* [`Client`](../classes/Client.md) |
+
+## Constructors
+
+### new HelpersClass()
+
+> **new HelpersClass**(): [`HelpersInstance`](HelpersInstance.md)\<`TTable`\> & [`RowWithID`](../type-aliases/RowWithID.md) & `{ [K in string]: Value }`
+
+Defined in: [src/ent/mixins/HelpersMixin.ts:131](https://github.com/clickup/ent-framework/blob/master/src/ent/mixins/HelpersMixin.ts#L131)
+
+TS requires us to have a public constructor to infer instance types in
+various places. We make this constructor throw if it's called.
+
+#### Returns
+
+[`HelpersInstance`](HelpersInstance.md)\<`TTable`\> & [`RowWithID`](../type-aliases/RowWithID.md) & `{ [K in string]: Value }`
+
+#### Inherited from
+
+`OmitNew>.constructor`
+
+## Properties
+
+| Property | Type | Description |
+| ------ | ------ | ------ |
+| `Configuration` | (`cfg`: [`Configuration`](../classes/Configuration.md)\<`TTable`\>) => [`Configuration`](../classes/Configuration.md)\<`TTable`\> | A helper class to work-around TS weakness in return value type inference: https://github.com/Microsoft/TypeScript/issues/31273. It could've been just a function, but having a class is a little more natural. |
+| `CLUSTER` | [`Cluster`](../classes/Cluster.md)\<`TClient`, `any`\> | A Cluster where this Ent lives. |
+| `SCHEMA` | [`Schema`](../classes/Schema.md)\<`TTable`, `TUniqueKey`\> | A schema which represents this Ent. |
+| `SHARD_AFFINITY` | [`ShardAffinity`](../type-aliases/ShardAffinity.md)\<[`FieldOfIDType`](../type-aliases/FieldOfIDType.md)\<`TTable`\>\> | Defines how to find the right Shard during Ent insertion. |
+| `SHARD_LOCATOR` | [`ShardLocator`](../classes/ShardLocator.md)\<`TClient`, `TTable`, [`FieldOfIDType`](../type-aliases/FieldOfIDType.md)\<`TTable`\>\> | Shard locator for this Ent, responsible for resolving IDs into Shard objects. |
+| `VALIDATION` | [`Validation`](../classes/Validation.md)\<`TTable`\> | Privacy rules for this Ent class. |
+| `TRIGGERS` | [`Triggers`](../classes/Triggers.md)\<`TTable`\> | Triggers for this Ent class. |
+| `INVERSES` | [`Inverse`](../classes/Inverse.md)\<`TClient`, `TTable`\>[] | Inverse assoc managers for fields. |
+| `insert` | (`vc`: [`VC`](../classes/VC.md), `input`: [`InsertInput`](../type-aliases/InsertInput.md)\<`TTable`\>) => `Promise`\<`string`\> | Same as insertIfNotExists(), but throws if the Ent violates unique key constraints. |
+| `insertReturning` | \<`TEnt`\>(`this`: () => `TEnt`, `vc`: [`VC`](../classes/VC.md), `input`: [`InsertInput`](../type-aliases/InsertInput.md)\<`TTable`\>) => `Promise`\<`TEnt`\> | Same as insert(), but returns the created Ent. |
+| `upsertReturning` | \<`TEnt`\>(`this`: () => `TEnt`, `vc`: [`VC`](../classes/VC.md), `input`: [`InsertInput`](../type-aliases/InsertInput.md)\<`TTable`\>) => `Promise`\<`TEnt`\> | Same, but returns the created/updated Ent. |
+| `loadIfReadableNullable` | \<`TEnt`\>(`this`: () => `TEnt`, `vc`: [`VC`](../classes/VC.md), `id`: `string`) => `Promise`\<`null` \| `TEnt`\> | Same as loadNullable(), but if no permissions to read, returns null and doesn't throw. It's more a convenience function rather than a concept. |
+| `loadX` | \<`TEnt`\>(`this`: () => `TEnt`, `vc`: [`VC`](../classes/VC.md), `id`: `string`) => `Promise`\<`TEnt`\> | Loads an Ent by its ID. Throws if no such Ent is found. This method is used VERY often. |
+| `loadByX` | \<`TEnt`\>(`this`: () => `TEnt`, `vc`: [`VC`](../classes/VC.md), `input`: [`LoadByInput`](../type-aliases/LoadByInput.md)\<`TTable`, `TUniqueKey`\>) => `Promise`\<`TEnt`\> | Loads an Ent by its ID. Throws if no such Ent is found. This method is used VERY often. |
+| `insertIfNotExists` | (`vc`: [`VC`](../classes/VC.md), `input`: [`InsertInput`](../type-aliases/InsertInput.md)\<`TTable`\>) => `Promise`\<`null` \| `string`\> | Runs INSERT mutation for the Ent. - The Shard is inferred from the input fields using SHARD_AFFINITY. - Returns ID of the newly inserted row. - Returns null if the Ent violates unique key constraints. - If the Ent has some triggers set up, this will be translated into two schema operations: idGen() and insert(), and before-triggers will run in between having the ID known in advance. |
+| `upsert` | (`vc`: [`VC`](../classes/VC.md), `input`: [`InsertInput`](../type-aliases/InsertInput.md)\<`TTable`\>) => `Promise`\<`string`\> | Inserts an Ent or updates an existing one if unique key matches. - Don't use upsert() too often, because upsert may still delete IDs even if the object was updated, not inserted (there is no good ways to solve this in some DB engines like relational DBs so far). - Upsert can't work if some triggers are defined for the Ent, because we don't know Ent ID in advance (whether the upsert succeeds or skips on duplication). |
+| `loadNullable` | \<`TEnt`\>(`this`: () => `TEnt`, `vc`: [`VC`](../classes/VC.md), `id`: `string`) => `Promise`\<`null` \| `TEnt`\> | Loads an Ent by its ID. Returns null if no such Ent exists. Try to use loadX() instead as much as you can. |
+| `loadByNullable` | \<`TEnt`\>(`this`: () => `TEnt`, `vc`: [`VC`](../classes/VC.md), `input`: [`LoadByInput`](../type-aliases/LoadByInput.md)\<`TTable`, `TUniqueKey`\>) => `Promise`\<`null` \| `TEnt`\> | Loads an Ent by its unique key. Returns null if no such Ent exists. Notice that the key must be REALLY unique, otherwise the database may return multiple items, and the API will break. Don't try to use this method with non-unique keys! |
+| `selectBy` | \<`TEnt`\>(`this`: () => `TEnt`, `vc`: [`VC`](../classes/VC.md), `input`: [`LoadByInput`](../type-aliases/LoadByInput.md)\<`TTable`, `TuplePrefixes`\<`TUniqueKey`\>\>) => `Promise`\<`TEnt`[]\> | Selects the list of Ents by their unique key prefix. The query can span multiple Shards if their locations can be inferred from inverses related to the fields mentioned in the query. Ordering of the results is not guaranteed. |
+| `select` | \<`TEnt`\>(`this`: () => `TEnt`, `vc`: [`VC`](../classes/VC.md), `where`: [`Where`](../type-aliases/Where.md)\<`TTable`\>, `limit`: `number`, `order`?: [`Order`](../type-aliases/Order.md)\<`TTable`\>, `custom`?: `object`) => `Promise`\<`TEnt`[]\> | Selects the list of Ents by some predicate. - The query can span multiple Shards if their locations can be inferred from inverses related to the fields mentioned in the query. - In multi-Shard case, ordering of results is not guaranteed. - In multi-Shard case, it may return more results than requested by limit (basically, limit is applied to each Shard individually). The caller has then freedom to reorder & slice the results as they wish. |
+| `selectChunked` | \<`TEnt`\>(`this`: () => `TEnt`, `vc`: [`VC`](../classes/VC.md), `where`: [`Where`](../type-aliases/Where.md)\<`TTable`\>, `chunkSize`: `number`, `limit`: `number`, `custom`?: `object`) => `AsyncIterableIterator`\<`TEnt`[], `any`, `any`\> | Same as select(), but returns data in chunks. - Uses multiple select() queries under the hood. - The query can span multiple Shards if their locations can be inferred from inverses related to the fields mentioned in the query. - Ents in each chunk always belong to the same Shard and are ordered by ID (there is no support for custom ordering). Make sure you have the right index in the database. |
+| `count` | (`vc`: [`VC`](../classes/VC.md), `where`: [`CountInput`](../type-aliases/CountInput.md)\<`TTable`\>) => `Promise`\<`number`\> | Returns count of Ents matching a predicate. The query can span multiple Shards if their locations can be inferred from inverses related to the fields mentioned in the query. |
+| `exists` | (`vc`: [`VC`](../classes/VC.md), `where`: [`ExistsInput`](../type-aliases/ExistsInput.md)\<`TTable`\>) => `Promise`\<`boolean`\> | A more optimal approach than count() when we basically just need to know whether we have "0 or not 0" rows. |
+
+## Methods
+
+### configure()
+
+> **configure**(): [`Configuration`](../classes/Configuration.md)\<`TTable`\>
+
+Defined in: [src/ent/mixins/ConfigMixin.ts:29](https://github.com/clickup/ent-framework/blob/master/src/ent/mixins/ConfigMixin.ts#L29)
+
+Some Ent parameters need to be configured lazily, on the 1st access,
+because there could be cyclic references between Ent classes (e.g. in their
+privacy rules). So configure() is called on some later stage, at the moment
+of actual Ent operations (like loading, creation etc.). There is no static
+abstract methods in TS yet, so making it non-abstract.
+
+#### Returns
+
+[`Configuration`](../classes/Configuration.md)\<`TTable`\>
+
+#### Inherited from
+
+`OmitNew.configure`
diff --git a/docs/interfaces/HelpersInstance.md b/docs/interfaces/HelpersInstance.md
new file mode 100644
index 0000000..6413213
--- /dev/null
+++ b/docs/interfaces/HelpersInstance.md
@@ -0,0 +1,195 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / HelpersInstance
+
+# Interface: HelpersInstance\
+
+Defined in: [src/ent/mixins/HelpersMixin.ts:20](https://github.com/clickup/ent-framework/blob/master/src/ent/mixins/HelpersMixin.ts#L20)
+
+## Extends
+
+- [`PrimitiveInstance`](PrimitiveInstance.md)\<`TTable`\>
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TTable` *extends* [`Table`](../type-aliases/Table.md) |
+
+## Properties
+
+| Property | Type | Description |
+| ------ | ------ | ------ |
+| `vc` | [`VC`](../classes/VC.md) | VC of this Ent. |
+| `id` | `string` | For simplicity, every Ent has an ID field name hardcoded to "id". |
+
+## Methods
+
+### updateChanged()
+
+> **updateChanged**(`input`): `Promise`\<`null` \| `false` \| [`UpdateField`](../type-aliases/UpdateField.md)\<`TTable`\>[]\>
+
+Defined in: [src/ent/mixins/HelpersMixin.ts:36](https://github.com/clickup/ent-framework/blob/master/src/ent/mixins/HelpersMixin.ts#L36)
+
+Same as updateOriginal(), but updates only the fields which are different
+in input and in the current object.
+- This method can works with CAS; see $cas property of the passed object.
+ If CAS fails, returns false, the same way as updateOriginal() does.
+- If there was no such Ent in the DB, returns false, the same way as
+ updateOriginal() does.
+- If no changed fields are detected, returns null as an indication (it's
+ still falsy, but is different from the parent updateOriginal's `false`).
+- Otherwise, when an update happened, returns the list of fields which were
+ different and triggered that change (a truthy value). The order of fields
+ in the list matches the order of fields in the Ent schema definition.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `input` | [`UpdateOriginalInput`](../type-aliases/UpdateOriginalInput.md)\<`TTable`\> |
+
+#### Returns
+
+`Promise`\<`null` \| `false` \| [`UpdateField`](../type-aliases/UpdateField.md)\<`TTable`\>[]\>
+
+***
+
+### updateChangedReturningX()
+
+> **updateChangedReturningX**\<`TEnt`\>(`this`, `input`): `Promise`\<`TEnt`\>
+
+Defined in: [src/ent/mixins/HelpersMixin.ts:44](https://github.com/clickup/ent-framework/blob/master/src/ent/mixins/HelpersMixin.ts#L44)
+
+Same as updateChanged(), but returns the updated Ent (or the original one
+if no fields were updated).
+
+#### Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TEnt` *extends* [`HelpersInstance`](HelpersInstance.md)\<`TTable`\> |
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `this` | `TEnt` |
+| `input` | [`UpdateInput`](../type-aliases/UpdateInput.md)\<`TTable`\> |
+
+#### Returns
+
+`Promise`\<`TEnt`\>
+
+***
+
+### updateReturningNullable()
+
+> **updateReturningNullable**\<`TEnt`\>(`this`, `input`): `Promise`\<`null` \| `TEnt`\>
+
+Defined in: [src/ent/mixins/HelpersMixin.ts:53](https://github.com/clickup/ent-framework/blob/master/src/ent/mixins/HelpersMixin.ts#L53)
+
+Same as updateOriginal(), but returns the updated Ent (or null of there
+was no such Ent in the database).
+
+#### Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TEnt` *extends* [`HelpersInstance`](HelpersInstance.md)\<`TTable`\> |
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `this` | `TEnt` |
+| `input` | [`UpdateInput`](../type-aliases/UpdateInput.md)\<`TTable`\> |
+
+#### Returns
+
+`Promise`\<`null` \| `TEnt`\>
+
+***
+
+### updateReturningX()
+
+> **updateReturningX**\<`TEnt`\>(`this`, `input`): `Promise`\<`TEnt`\>
+
+Defined in: [src/ent/mixins/HelpersMixin.ts:62](https://github.com/clickup/ent-framework/blob/master/src/ent/mixins/HelpersMixin.ts#L62)
+
+Same as updateOriginal(), but throws if the object wasn't updated or
+doesn't exist after the update.
+
+#### Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TEnt` *extends* [`HelpersInstance`](HelpersInstance.md)\<`TTable`\> |
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `this` | `TEnt` |
+| `input` | [`UpdateInput`](../type-aliases/UpdateInput.md)\<`TTable`\> |
+
+#### Returns
+
+`Promise`\<`TEnt`\>
+
+***
+
+### updateOriginal()
+
+> **updateOriginal**(`input`): `Promise`\<`boolean`\>
+
+Defined in: [src/ent/mixins/PrimitiveMixin.ts:59](https://github.com/clickup/ent-framework/blob/master/src/ent/mixins/PrimitiveMixin.ts#L59)
+
+Updates the object in the DB, but doesn't update the Ent itself (since it's
+immutable).
+- This method can work with CAS; see $cas property of the passed object.
+- If a special value "skip-if-someone-else-changed-updating-ent-props" is
+ passed to $cas, then the list of props for CAS is brought from the input,
+ and the values of these props are brought from the Ent itself (i.e. from
+ `this`).
+- If a special value, a list of field names, is passed to $cas, then it
+ works like described above, but the list of prop names is brought from
+ that list of field names.
+- Returns false if there is no such object in the DB, or if CAS check
+ didn't succeed.
+- Returns true if the object was found and updated.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `input` | [`UpdateOriginalInput`](../type-aliases/UpdateOriginalInput.md)\<`TTable`\> |
+
+#### Returns
+
+`Promise`\<`boolean`\>
+
+#### Inherited from
+
+[`PrimitiveInstance`](PrimitiveInstance.md).[`updateOriginal`](PrimitiveInstance.md#updateoriginal)
+
+***
+
+### deleteOriginal()
+
+> **deleteOriginal**(): `Promise`\<`boolean`\>
+
+Defined in: [src/ent/mixins/PrimitiveMixin.ts:65](https://github.com/clickup/ent-framework/blob/master/src/ent/mixins/PrimitiveMixin.ts#L65)
+
+Deletes the object in the DB. Returns true if the object was found. Keeps
+the current object untouched (since it's immutable).
+
+#### Returns
+
+`Promise`\<`boolean`\>
+
+#### Inherited from
+
+[`PrimitiveInstance`](PrimitiveInstance.md).[`deleteOriginal`](PrimitiveInstance.md#deleteoriginal)
diff --git a/docs/interfaces/IslandOptions.md b/docs/interfaces/IslandOptions.md
new file mode 100644
index 0000000..85ea5f8
--- /dev/null
+++ b/docs/interfaces/IslandOptions.md
@@ -0,0 +1,27 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / IslandOptions
+
+# Interface: IslandOptions\
+
+Defined in: [src/abstract/Island.ts:26](https://github.com/clickup/ent-framework/blob/master/src/abstract/Island.ts#L26)
+
+Options for Island constructor.
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TClient` *extends* [`Client`](../classes/Client.md) |
+
+## Properties
+
+| Property | Type | Description |
+| ------ | ------ | ------ |
+| `no` | `number` | Island number. |
+| `clients` | readonly `TClient`[] | Clients of that Island (the order is arbitrary). |
+| `createShard` | (`no`: `number`) => [`Shard`](../classes/Shard.md)\<`TClient`\> | Should return a Memoize'd Shards object by its number. |
+| `localCache?` | `null` \| [`LocalCache`](../classes/LocalCache.md)\<\{ `address`: `string`; `role`: [`ClientRole`](../type-aliases/ClientRole.md); \}\> | An auxillary LocalCache used to fallback-infer master/replica role in case some Client is unavailable right now. |
+| `shardNosConcurrentRetryDelayMs?` | `number` | If nonzero, runs the second shardNos() call attempt on a Client if the 1st call on that Client gets stuck for longer than the provided number of ms. This option is used to detect the unhealthy DB connection quicker, and thus, exit from rediscover() faster (the Shards map can likely be loaded from a replica still, so the down DB is not the end of the world). The idea is that the 1st shardNos() could get stuck due to the load balancer trying to wait until the DB goes back up again (e.g. for PgBouncer, that is query_wait_timeout situation; "pause_client" is printed to PgBouncer debug logs, and then the Client gets frozen for up to query_wait_timeout; other engines may have similar behavior). But for the NEW connections/queries, after a small delay, the load balancer may realize that the DB is really down (the load balancer typically can get "connection refused" while connecting to the DB server really quickly), and the 2nd shardNos() call will reject almost immediately ("fast fail" workflow), way before the 1st call rejects (e.g. for PgBouncer and query_wait_timeout=15s, the 1st call may get stuck for up to 15 seconds!). So, we will not wait that long to figure out that the DB is down, and will detect that situation quicker. Typically, the connection attempt from load balancer to an unhealthy DB server ends up quickly with "connection refused" TCP error (e.g. when the load balancer and the DB server run on the same host), so the value in this option can be small. But not always. Sometimes, the new connection from load balancer to the DB server gets stuck in "connecting..." state (e.g. this happens when the load balancer runs in a Docker container, and the DB container gets killed; the connection attempt will eventually fail, but in 1+ minutes and with "no route to host" error). In this case, the value in the option must be greater than e.g. server_connect_timeout (example for PgBouncer; basically, server_connect_timeout is PgBouncer's tool to detect "stuck" connection attempts (the connections which don't get "connection refused" quickly). |
diff --git a/docs/interfaces/LocalCacheOptions.md b/docs/interfaces/LocalCacheOptions.md
new file mode 100644
index 0000000..c363d26
--- /dev/null
+++ b/docs/interfaces/LocalCacheOptions.md
@@ -0,0 +1,22 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / LocalCacheOptions
+
+# Interface: LocalCacheOptions
+
+Defined in: [src/abstract/LocalCache.ts:8](https://github.com/clickup/ent-framework/blob/master/src/abstract/LocalCache.ts#L8)
+
+## Properties
+
+| Property | Type | Description |
+| ------ | ------ | ------ |
+| `dir` | `string` | Directory to store the cache in (auto-created). |
+| `loggers` | `Pick`\<[`Loggers`](Loggers.md)\<`any`\>, `"swallowedErrorLogger"`\> | Loggers for e.g. swallowed errors. |
+| `expirationMs?` | `number` | Max time (approximately) for an unread key to exist. |
+| `ext?` | `string` | Extension of cache files (without dot). |
+| `cleanupJitter?` | `number` | Jitter for cleanup runs. |
+| `cleanupFirstRunDelayMs?` | `number` | How much time to wait till the very 1st cleanup run. The idea is that Node process may be short-lived, so the next cleanup run configured via cleanupRoundsPerExpiration may never happen, and we also need to cleanup in the very beginning of the object lifetime. |
+| `cleanupRoundsPerExpiration?` | `number` | How many times per expirationMs interval should we run the cleanup. |
+| `mtimeUpdatesOnReadPerExpiration?` | `number` | How often to update mtime on read operations. E.g. if this value is 10, then mtime will be updated not more than ~10 times within the expiration period (optimizing filesystem writes). |
diff --git a/docs/interfaces/Loggers.md b/docs/interfaces/Loggers.md
new file mode 100644
index 0000000..fa04c8b
--- /dev/null
+++ b/docs/interfaces/Loggers.md
@@ -0,0 +1,30 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / Loggers
+
+# Interface: Loggers\
+
+Defined in: [src/abstract/Loggers.ts:11](https://github.com/clickup/ent-framework/blob/master/src/abstract/Loggers.ts#L11)
+
+Loggers are called at different stages of the query lifecycle. We do not use
+EventEmitter for several reasons:
+1. It is not friendly to mocking in Jest.
+2. The built-in EventEmitter is not strongly typed.
+
+## Type Parameters
+
+| Type Parameter | Default type |
+| ------ | ------ |
+| `TNode` | `DesperateAny` |
+
+## Properties
+
+| Property | Type | Description |
+| ------ | ------ | ------ |
+| `clientQueryLogger?` | (`props`: [`ClientQueryLoggerProps`](ClientQueryLoggerProps.md)) => `void` | Logs actual queries to the database (after batching). |
+| `swallowedErrorLogger` | (`props`: [`SwallowedErrorLoggerProps`](SwallowedErrorLoggerProps.md)) => `void` | Logs errors which did not throw through (typically recoverable). |
+| `runOnShardErrorLogger?` | (`props`: [`RunOnShardErrorLoggerProps`](RunOnShardErrorLoggerProps.md)) => `void` | Called when Island-from-Shard location fails (e.g. no such Shard), or when a query on a particular Shard fails due to any reason (like transport error). Mostly used in unit tests, since it's called for every retry. |
+| `clientEndLogger?` | (`props`: [`ClientEndLoggerProps`](ClientEndLoggerProps.md)\<`TNode`\>) => `void` | Called when a Client gets ended due to dynamic Islands reconfiguration. Allows to debug flaky Island reconfiguration. |
+| `clientConnectedLogger?` | (`props`: [`ClientConnectedLoggerProps`](ClientConnectedLoggerProps.md)\<`TNode`\>) => `void` | Called when a Client connects to the database. |
diff --git a/docs/interfaces/PgClientConn.md b/docs/interfaces/PgClientConn.md
new file mode 100644
index 0000000..4f24c23
--- /dev/null
+++ b/docs/interfaces/PgClientConn.md
@@ -0,0 +1,30 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / PgClientConn
+
+# Interface: PgClientConn\
+
+Defined in: [src/pg/PgClient.ts:96](https://github.com/clickup/ent-framework/blob/master/src/pg/PgClient.ts#L96)
+
+An opened low-level PostgreSQL connection.
+
+## Extends
+
+- `PoolClient`
+
+## Type Parameters
+
+| Type Parameter | Default type |
+| ------ | ------ |
+| `TPool` *extends* `pg.Pool` | `pg.Pool` |
+
+## Properties
+
+| Property | Type | Description |
+| ------ | ------ | ------ |
+| `pool` | `TPool` | Pool instance that created this connection. |
+| `id` | `number` | An additional property to the vanilla client: auto-incrementing ID of the connection for logging purposes. |
+| `queriesSent` | `number` | An additional property to the vanilla client: number of queries sent within this connection. |
+| `closeAt` | `null` \| `number` | An additional property to the vanilla client: when do we want to hard-close that connection. |
diff --git a/docs/interfaces/PgClientOptions.md b/docs/interfaces/PgClientOptions.md
new file mode 100644
index 0000000..e75b0a2
--- /dev/null
+++ b/docs/interfaces/PgClientOptions.md
@@ -0,0 +1,43 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / PgClientOptions
+
+# Interface: PgClientOptions\
+
+Defined in: [src/pg/PgClient.ts:40](https://github.com/clickup/ent-framework/blob/master/src/pg/PgClient.ts#L40)
+
+Options for PgClient constructor.
+
+## Extends
+
+- [`ClientOptions`](ClientOptions.md)
+
+## Type Parameters
+
+| Type Parameter | Default type |
+| ------ | ------ |
+| `TPool` *extends* `pg.Pool` | `pg.Pool` |
+
+## Properties
+
+| Property | Type | Description |
+| ------ | ------ | ------ |
+| `name` | `string` | Name of the Client; used for logging. |
+| `shardNamer?` | `null` \| [`ShardNamer`](../classes/ShardNamer.md) | Info on how to build/parse Shard names. If not set, then Cluster injects its own ShardNamer here right after creating a Client instance. |
+| `loggers?` | `null` \| [`Loggers`](Loggers.md)\<`any`\> | Loggers to be called at different stages. Client code calls into them. Also, Cluster injects its own loggers here, in addition to the provided ones (if any). |
+| `batchDelayMs?` | `MaybeCallable`\<`number`\> | If passed, there will be an artificial queries accumulation delay while batching the requests. Default is 0 (turned off). Passed to Batcher#batchDelayMs. |
+| `config` | `PoolConfig` & `object` | Node-Postgres config. We can't make it MaybeCallable unfortunately, because it's used to initialize Node-Postgres Pool. |
+| `createPool?` | (`config`: `PoolConfig`) => `TPool` | Should create an instance of Pool class compatible with node-postgres Pool. By default, node-postgres Pool is used. |
+| `maxConnLifetimeMs?` | `MaybeCallable`\<`number`\> | Close the connection after the query if it was opened long time ago. |
+| `maxConnLifetimeJitter?` | `MaybeCallable`\<`number`\> | Jitter for maxConnLifetimeMs. |
+| `prewarmIntervalStep?` | `MaybeCallable`\<`number`\> | Add not more than this number of connections in each prewarm interval. New connections are expensive to establish (especially when SSL is enabled). |
+| `prewarmIntervalMs?` | `MaybeCallable`\<`number`\> | How often to send bursts of prewarm queries to all Clients to keep the minimal number of open connections. The default value is half of the default node-postgres'es idleTimeoutMillis=10s. Together with 1..1.5x jitter (default prewarmIntervalJitter=0.5), it is still slightly below idleTimeoutMillis, and thus, doesn't let Ent Framework close the connections prematurely. |
+| `prewarmIntervalJitter?` | `MaybeCallable`\<`number`\> | Jitter for prewarmIntervalMs. |
+| `prewarmQuery?` | `MaybeCallable`\<`string`\> | What prewarm query to send. |
+| `prewarmSubPools?` | `boolean` | If true, also sends prewarm queries and keeps the min number of connections in all sub-pools. See pool() method for details. |
+| `hints?` | `null` \| `MaybeCallable`\<[`Hints`](../type-aliases/Hints.md)\> | PG "SET key=value" hints to run before each query. Often times we use it to pass statement_timeout option since e.g. PGBouncer doesn't support per-connection statement timeout in transaction pooling mode: it throws "unsupported startup parameter" error. I.e. we may want to emit "SET statement_timeout TO ..." before each query in multi-query mode. |
+| `maxReplicationLagMs?` | `MaybeCallable`\<`number`\> | After how many milliseconds we give up waiting for the replica to catch up with the master. When role="replica", then this option is the only way to "unlatch" the reads from the master node after a write. |
+| `role?` | [`ClientRole`](../type-aliases/ClientRole.md) | Sometimes, the role of this Client is known statically, e.g. when pointing to AWS Aurora writer and reader endpoints. If "master" or "replica" are provided, then no attempt is made to use functions like pg_current_wal_insert_lsn() etc. (they are barely supported in e.g. AWS Aurora). Instead, for "replica" role, it is treated as "always lagging up until maxReplicationLagMs after the last write". If role="unknown", then auto-detection and automatic lag tracking is performed using pg_current_wal_insert_lsn() and other built-in PostgreSQL functions. |
+| `replicaTimelinePosRefreshMs?` | `MaybeCallable`\<`number`\> | Up to how often we call TimelineManager#triggerRefresh(). |
diff --git a/docs/interfaces/PgClientSubPoolConfig.md b/docs/interfaces/PgClientSubPoolConfig.md
new file mode 100644
index 0000000..9b2a1be
--- /dev/null
+++ b/docs/interfaces/PgClientSubPoolConfig.md
@@ -0,0 +1,23 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / PgClientSubPoolConfig
+
+# Interface: PgClientSubPoolConfig
+
+Defined in: [src/pg/PgClient.ts:116](https://github.com/clickup/ent-framework/blob/master/src/pg/PgClient.ts#L116)
+
+A named low-level Pool config used to create sub-pools. Sub-pool derives
+configuration from the default PgClientOptions#config, but allow overrides.
+See PgClient#pool() method for details.
+
+## Extends
+
+- `Partial`\<`pg.PoolConfig`\>
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `name` | `string` |
diff --git a/docs/interfaces/PgTimelineStorageOptions.md b/docs/interfaces/PgTimelineStorageOptions.md
new file mode 100644
index 0000000..608e648
--- /dev/null
+++ b/docs/interfaces/PgTimelineStorageOptions.md
@@ -0,0 +1,22 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / PgTimelineStorageOptions
+
+# Interface: PgTimelineStorageOptions
+
+Defined in: [src/pg/PgTimelineStorage.ts:14](https://github.com/clickup/ent-framework/blob/master/src/pg/PgTimelineStorage.ts#L14)
+
+## Extends
+
+- [`TimelineStorageOptions`](TimelineStorageOptions.md)
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `merge?` | (`dataStrs`: `string`[]) => `string` |
+| `maxChunksPerPrincipal?` | `MaybeCallable`\<`number`\> |
+| `cluster` | [`Cluster`](../classes/Cluster.md)\<[`PgClient`](../classes/PgClient.md)\<`Pool`\>, `any`\> |
+| `table?` | `string` |
diff --git a/docs/interfaces/Predicate.md b/docs/interfaces/Predicate.md
new file mode 100644
index 0000000..d056843
--- /dev/null
+++ b/docs/interfaces/Predicate.md
@@ -0,0 +1,56 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / Predicate
+
+# Interface: Predicate\
+
+Defined in: [src/ent/predicates/Predicate.ts:17](https://github.com/clickup/ent-framework/blob/master/src/ent/predicates/Predicate.ts#L17)
+
+A predicate evaluates against some input (typically a row) and returns true
+or false (or throws which is considering the similar way as returning false).
+
+I.e. Predicate is a "yes/no" logic. If it resolves to "no", then the
+framework may disallow some Ent operation and include predicate object's
+properties (like name or any other info) to the exception.
+
+Also, some predicates try to use caches in vc to make the decision faster
+based on the previously computed results. E.g. CanReadOutgoingEdge predicate
+knows that it already returned true for some ID once, it returns true again
+immediately. This saves us lots of database operations.
+
+## Extended by
+
+- [`AbstractIs`](AbstractIs.md)
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TInput` |
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `name` | `string` |
+
+## Methods
+
+### check()
+
+> **check**(`vc`, `input`): `Promise`\<`boolean`\>
+
+Defined in: [src/ent/predicates/Predicate.ts:19](https://github.com/clickup/ent-framework/blob/master/src/ent/predicates/Predicate.ts#L19)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `vc` | [`VC`](../classes/VC.md) |
+| `input` | `TInput` |
+
+#### Returns
+
+`Promise`\<`boolean`\>
diff --git a/docs/interfaces/PrimitiveInstance.md b/docs/interfaces/PrimitiveInstance.md
new file mode 100644
index 0000000..3422559
--- /dev/null
+++ b/docs/interfaces/PrimitiveInstance.md
@@ -0,0 +1,77 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / PrimitiveInstance
+
+# Interface: PrimitiveInstance\
+
+Defined in: [src/ent/mixins/PrimitiveMixin.ts:31](https://github.com/clickup/ent-framework/blob/master/src/ent/mixins/PrimitiveMixin.ts#L31)
+
+## Extends
+
+- [`ConfigInstance`](ConfigInstance.md)
+
+## Extended by
+
+- [`HelpersInstance`](HelpersInstance.md)
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TTable` *extends* [`Table`](../type-aliases/Table.md) |
+
+## Properties
+
+| Property | Type | Description |
+| ------ | ------ | ------ |
+| `vc` | [`VC`](../classes/VC.md) | VC of this Ent. |
+| `id` | `string` | For simplicity, every Ent has an ID field name hardcoded to "id". |
+
+## Methods
+
+### updateOriginal()
+
+> **updateOriginal**(`input`): `Promise`\<`boolean`\>
+
+Defined in: [src/ent/mixins/PrimitiveMixin.ts:59](https://github.com/clickup/ent-framework/blob/master/src/ent/mixins/PrimitiveMixin.ts#L59)
+
+Updates the object in the DB, but doesn't update the Ent itself (since it's
+immutable).
+- This method can work with CAS; see $cas property of the passed object.
+- If a special value "skip-if-someone-else-changed-updating-ent-props" is
+ passed to $cas, then the list of props for CAS is brought from the input,
+ and the values of these props are brought from the Ent itself (i.e. from
+ `this`).
+- If a special value, a list of field names, is passed to $cas, then it
+ works like described above, but the list of prop names is brought from
+ that list of field names.
+- Returns false if there is no such object in the DB, or if CAS check
+ didn't succeed.
+- Returns true if the object was found and updated.
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `input` | [`UpdateOriginalInput`](../type-aliases/UpdateOriginalInput.md)\<`TTable`\> |
+
+#### Returns
+
+`Promise`\<`boolean`\>
+
+***
+
+### deleteOriginal()
+
+> **deleteOriginal**(): `Promise`\<`boolean`\>
+
+Defined in: [src/ent/mixins/PrimitiveMixin.ts:65](https://github.com/clickup/ent-framework/blob/master/src/ent/mixins/PrimitiveMixin.ts#L65)
+
+Deletes the object in the DB. Returns true if the object was found. Keeps
+the current object untouched (since it's immutable).
+
+#### Returns
+
+`Promise`\<`boolean`\>
diff --git a/docs/interfaces/Query.md b/docs/interfaces/Query.md
new file mode 100644
index 0000000..5577845
--- /dev/null
+++ b/docs/interfaces/Query.md
@@ -0,0 +1,43 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / Query
+
+# Interface: Query\
+
+Defined in: [src/abstract/Query.ts:8](https://github.com/clickup/ent-framework/blob/master/src/abstract/Query.ts#L8)
+
+A very lean interface for a Query. In practice each query is so different
+that this interface is the only common part of them all.
+
+## Type Parameters
+
+| Type Parameter |
+| ------ |
+| `TOutput` |
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `IS_WRITE` | `boolean` |
+
+## Methods
+
+### run()
+
+> **run**(`client`, `annotation`): `Promise`\<`TOutput`\>
+
+Defined in: [src/abstract/Query.ts:10](https://github.com/clickup/ent-framework/blob/master/src/abstract/Query.ts#L10)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `client` | [`Client`](../classes/Client.md) |
+| `annotation` | [`QueryAnnotation`](QueryAnnotation.md) |
+
+#### Returns
+
+`Promise`\<`TOutput`\>
diff --git a/docs/interfaces/QueryAnnotation.md b/docs/interfaces/QueryAnnotation.md
new file mode 100644
index 0000000..b479d27
--- /dev/null
+++ b/docs/interfaces/QueryAnnotation.md
@@ -0,0 +1,24 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / QueryAnnotation
+
+# Interface: QueryAnnotation
+
+Defined in: [src/abstract/QueryAnnotation.ts:27](https://github.com/clickup/ent-framework/blob/master/src/abstract/QueryAnnotation.ts#L27)
+
+A debug annotation from each individual place which initiated the query. When
+multiple queries are grouped into one large query by Ent Framework (even
+cross-async-trace and cross-VC), the resulting large query is accompanied
+with all those annotations.
+
+## Properties
+
+| Property | Type | Description |
+| ------ | ------ | ------ |
+| `trace` | `string` | Trace ID of the series of the queries. |
+| `vc` | `string` | Something which identifies the acting user; it's named `vc` after Ent's VC for simplicity, but at this layer of abstractions, there are no Ents. |
+| `debugStack` | `string` | Sometimes a query may be annotated by the source stack trace. It's typically expensive, so it's likely "" in production. |
+| `whyClient` | `undefined` \| [`WhyClient`](../type-aliases/WhyClient.md) | Answers, why exactly this Client was selected to send the query to. |
+| `attempt` | `number` | In case it's a retry, the attempt number will be greater than 0. |
diff --git a/docs/interfaces/RuleResult.md b/docs/interfaces/RuleResult.md
new file mode 100644
index 0000000..8cdf3ad
--- /dev/null
+++ b/docs/interfaces/RuleResult.md
@@ -0,0 +1,20 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / RuleResult
+
+# Interface: RuleResult
+
+Defined in: [src/ent/rules/Rule.ts:15](https://github.com/clickup/ent-framework/blob/master/src/ent/rules/Rule.ts#L15)
+
+A full debug info about some Rule decision (which Rule produced this
+decision, what was thrown etc.).
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `decision` | [`RuleDecision`](../type-aliases/RuleDecision.md) |
+| `rule` | [`Rule`](../classes/Rule.md)\<`object`\> |
+| `cause` | `null` \| [`EntAccessError`](../classes/EntAccessError.md) |
diff --git a/docs/interfaces/RunOnShardErrorLoggerProps.md b/docs/interfaces/RunOnShardErrorLoggerProps.md
new file mode 100644
index 0000000..fc0c4ac
--- /dev/null
+++ b/docs/interfaces/RunOnShardErrorLoggerProps.md
@@ -0,0 +1,16 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / RunOnShardErrorLoggerProps
+
+# Interface: RunOnShardErrorLoggerProps
+
+Defined in: [src/abstract/Loggers.ts:67](https://github.com/clickup/ent-framework/blob/master/src/abstract/Loggers.ts#L67)
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `error` | `unknown` |
+| `attempt` | `number` |
diff --git a/docs/interfaces/SchemaClass.md b/docs/interfaces/SchemaClass.md
new file mode 100644
index 0000000..18b402d
--- /dev/null
+++ b/docs/interfaces/SchemaClass.md
@@ -0,0 +1,29 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / SchemaClass
+
+# Interface: SchemaClass
+
+Defined in: [src/abstract/Schema.ts:15](https://github.com/clickup/ent-framework/blob/master/src/abstract/Schema.ts#L15)
+
+## Constructors
+
+### new SchemaClass()
+
+> **new SchemaClass**\<`TTable`, `TUniqueKey`\>(`name`, `table`, `uniqueKey`?): [`Schema`](../classes/Schema.md)\<`TTable`, `TUniqueKey`\>
+
+Defined in: [src/abstract/Schema.ts:16](https://github.com/clickup/ent-framework/blob/master/src/abstract/Schema.ts#L16)
+
+#### Parameters
+
+| Parameter | Type |
+| ------ | ------ |
+| `name` | `string` |
+| `table` | `TTable` |
+| `uniqueKey`? | `TUniqueKey` |
+
+#### Returns
+
+[`Schema`](../classes/Schema.md)\<`TTable`, `TUniqueKey`\>
diff --git a/docs/interfaces/ShardNamerOptions.md b/docs/interfaces/ShardNamerOptions.md
new file mode 100644
index 0000000..bec7a3f
--- /dev/null
+++ b/docs/interfaces/ShardNamerOptions.md
@@ -0,0 +1,18 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / ShardNamerOptions
+
+# Interface: ShardNamerOptions
+
+Defined in: [src/abstract/ShardNamer.ts:6](https://github.com/clickup/ent-framework/blob/master/src/abstract/ShardNamer.ts#L6)
+
+Options for ShardNamer constructor.
+
+## Properties
+
+| Property | Type | Description |
+| ------ | ------ | ------ |
+| `nameFormat` | `string` | A format string to turn a Shard number to Shard name (e.g. "sh%04d"). |
+| `discoverQuery` | `MaybeCallable`\<`string`\> | A DB engine query that should return the names of Shards served by this Client. |
diff --git a/docs/interfaces/StandardSchemaV1FailureResult.md b/docs/interfaces/StandardSchemaV1FailureResult.md
new file mode 100644
index 0000000..d7739f4
--- /dev/null
+++ b/docs/interfaces/StandardSchemaV1FailureResult.md
@@ -0,0 +1,18 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / StandardSchemaV1FailureResult
+
+# Interface: StandardSchemaV1FailureResult
+
+Defined in: [src/ent/errors/EntAccessError.ts:7](https://github.com/clickup/ent-framework/blob/master/src/ent/errors/EntAccessError.ts#L7)
+
+Standard Schema V1 compatible error result. Every EntAccessError can be
+converted to it. See https://standardschema.dev.
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `issues` | readonly `object`[] |
diff --git a/docs/interfaces/SwallowedErrorLoggerProps.md b/docs/interfaces/SwallowedErrorLoggerProps.md
new file mode 100644
index 0000000..841dbe1
--- /dev/null
+++ b/docs/interfaces/SwallowedErrorLoggerProps.md
@@ -0,0 +1,18 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / SwallowedErrorLoggerProps
+
+# Interface: SwallowedErrorLoggerProps
+
+Defined in: [src/abstract/Loggers.ts:60](https://github.com/clickup/ent-framework/blob/master/src/abstract/Loggers.ts#L60)
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `where` | `string` |
+| `error` | `unknown` |
+| `elapsed` | `null` \| `number` |
+| `importance` | `"low"` \| `"normal"` |
diff --git a/docs/interfaces/TimelineStorageOptions.md b/docs/interfaces/TimelineStorageOptions.md
new file mode 100644
index 0000000..72b678a
--- /dev/null
+++ b/docs/interfaces/TimelineStorageOptions.md
@@ -0,0 +1,20 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / TimelineStorageOptions
+
+# Interface: TimelineStorageOptions
+
+Defined in: [src/ent/TimelineStorage.ts:5](https://github.com/clickup/ent-framework/blob/master/src/ent/TimelineStorage.ts#L5)
+
+## Extended by
+
+- [`PgTimelineStorageOptions`](PgTimelineStorageOptions.md)
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `merge?` | (`dataStrs`: `string`[]) => `string` |
+| `maxChunksPerPrincipal?` | `MaybeCallable`\<`number`\> |
diff --git a/docs/interfaces/ToolPingOptions.md b/docs/interfaces/ToolPingOptions.md
new file mode 100644
index 0000000..24838b1
--- /dev/null
+++ b/docs/interfaces/ToolPingOptions.md
@@ -0,0 +1,21 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / ToolPingOptions
+
+# Interface: ToolPingOptions
+
+Defined in: [src/tools/ToolPing.ts:21](https://github.com/clickup/ent-framework/blob/master/src/tools/ToolPing.ts#L21)
+
+Ping tool constructor options.
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `cluster` | [`Cluster`](../classes/Cluster.md)\<[`Client`](../classes/Client.md), `any`\> |
+| `shard?` | `number` |
+| `pingExecTimeMs?` | `number` |
+| `pingPollMs?` | `number` |
+| `pingIsWrite?` | `boolean` |
diff --git a/docs/interfaces/ToolScoreboardOptions.md b/docs/interfaces/ToolScoreboardOptions.md
new file mode 100644
index 0000000..60fcbf7
--- /dev/null
+++ b/docs/interfaces/ToolScoreboardOptions.md
@@ -0,0 +1,24 @@
+[**ent-framework**](../README.md)
+
+***
+
+[ent-framework](../globals.md) / ToolScoreboardOptions
+
+# Interface: ToolScoreboardOptions
+
+Defined in: [src/tools/ToolScoreboard.ts:35](https://github.com/clickup/ent-framework/blob/master/src/tools/ToolScoreboard.ts#L35)
+
+Scoreboard tool constructor options.
+
+## Properties
+
+| Property | Type |
+| ------ | ------ |
+| `cluster` | [`Cluster`](../classes/Cluster.md)\<[`Client`](../classes/Client.md), `any`\> |
+| `refreshMs?` | `number` |
+| `pingExecTimeMs?` | `number` |
+| `pingParallelism?` | `number` |
+| `pingPollMs?` | `number` |
+| `tickMs?` | `number` |
+| `maxQueries?` | `number` |
+| `maxErrors?` | `number` |
diff --git a/docs/llms-full.md b/docs/llms-full.md
new file mode 100644
index 0000000..d40f313
--- /dev/null
+++ b/docs/llms-full.md
@@ -0,0 +1,6572 @@
+# Ent Framework
+
+
+
+The TypeScript library for working with microsharded PostgreSQL databases.
+
+* [Getting Started and Tutorials](https://ent-framework.net)
+* [API documentation](https://github.com/clickup/ent-framework/blob/main/docs/globals.md)
+* [Source code](https://github.com/clickup/ent-framework)
+* [Ent Framework's Discord](https://discord.gg/QXvN6VTCKS)
+
+#### Core Features
+
+1. **Graph-like representation of entities.** With Ent Framework, you represent each Ent (a domain object of your business logic) as a TypeScript class with immutable properties. An Ent class instance maps to one row of some table in a relational database (like PostgreSQL). It may look similar to ORM, but has many aspects that traditional ORMs don't have.
+2. **Row-level security in a graph (privacy layer).** You manage data as a graph where each node is an Ent instance, and each edge is a field link (think of foreign keys) to other Ents. To be allowed to read (or update/delete) some Ent, you define a set of explicit rules like "user can read EntA if they can read EntB or EntC". And, consequently, in EntB you define its own set of rules, like "user can read EntB if they can read EntD".
+3. **Query batching and coalescing.** Ent Framework holistically solves the "N+1 selects" problem commonly known in ORM world. You still write you code as if you work with individual Ents and individual IDs, and the framework magically takes care of sending batched requests (both read and write) to the underlying relational database. You do not work with lists and JOINs anymore.
+4. **Microsharding and replication lag tracking support out of the box.** Splitting your database horizontally is like a breeze now: Ent Framework takes care of routing the requests to the proper microshards. When scaling reads, Ent Framework knows whether a replica node is "good enough" for that particular query. It automatically uses the proper replica when possible, falling back to master when not.
+5. **Pluggable to your existing relational database.** If your project already uses some ORM or runs raw SQL queries, Ent Framework can be plugged in.
+6. **Tens of other features.** Some examples: cross-microshards foreign keys, composite fields, triggers, build-in caching etc.
+
+#### Installation
+
+```
+npm add ent-framework
+pnpm add ent-framework
+yarn add ent-framework
+```
+
+
+
+
+# Code Structure
+
+Below, we'll show some Ent Framework usage examples. We will progress from the simplest code snippets to more and more advanced topics, like:
+
+* custom ID schemas
+* privacy rules
+* triggers
+* composite field types
+* Viewer Context flavors
+* master-replica and automatic replication lag tracking
+* microsharding and migrations
+* cross-shards foreign keys and inverse indexes
+* etc.
+
+### Code Structure
+
+The examples in this tutorial will approximately follow [examples/next-example](https://github.com/dimikot/ent-framework/tree/main/examples/next-example) `src` folder structure:
+
+* ents/
+ * cluster.sql
+ * cluster.ts
+ * EntComment.ts
+ * EntTopic.ts
+ * EntUser.ts
+ * getServerVC.ts
+* app/
+ * api/
+ * auth/\[...nextauth]
+ * route.ts
+ * topics/
+ * route.ts
+
+
+# Connect to a Database
+
+To start simple, create a PostgreSQL database and several tables there. You can also use you existing database:
+
+```bash
+$ psql postgresql://postgres:postgres@127.0.0.1/postgres -f ents/cluster.sql
+```
+
+{% code title="ents/cluster.sql" %}
+
+```sql
+CREATE TABLE users(
+ id bigserial PRIMARY KEY,
+ email varchar(256) NOT NULL UNIQUE,
+ is_admin boolean NOT NULL DEFAULT FALSE
+);
+
+CREATE TABLE topics(
+ id bigserial PRIMARY KEY,
+ created_at timestamptz NOT NULL,
+ updated_at timestamptz NOT NULL,
+ slug varchar(64) NOT NULL UNIQUE,
+ creator_id bigint NOT NULL,
+ subject text DEFAULT NULL
+);
+
+CREATE TABLE comments(
+ id bigserial PRIMARY KEY,
+ created_at timestamptz NOT NULL,
+ topic_id bigint REFERENCES topics,
+ creator_id bigint NOT NULL,
+ message text NOT NULL
+);
+
+CREATE TABLE organizations(
+ id bigserial PRIMARY KEY,
+ name text NOT NULL UNIQUE
+);
+
+CREATE TABLE organization_users(
+ id bigserial PRIMARY KEY,
+ organization_id bigint REFERENCES organizations,
+ user_id bigint REFERENCES users,
+ UNIQUE (organization_id, user_id)
+);
+```
+
+{% endcode %}
+
+To access that database, create an instance of Cluster:
+
+{% code title="ents/cluster.ts" fullWidth="false" %}
+
+```typescript
+import { Cluster } from "ent-framework";
+import type { PgClientOptions } from "ent-framework/pg";
+import { PgClient } from "ent-framework/pg";
+import type { PoolConfig } from "pg";
+
+export const cluster = new Cluster({
+ islands: async () => [ // sync or async
+ {
+ no: 0,
+ nodes: [
+ {
+ name: "island0-master",
+ config: {
+ connectionString: process.env.DATABASE_URL, // e.g. from .env
+ // This object is of the standard node-postgres type PoolConfig.
+ // Thus, you can use host, port, user, password, database and other
+ // properties instead of connectionString if you want.
+ min: 5,
+ max: 20,
+ } satisfies PoolConfig,
+ },
+ ],
+ },
+ ],
+ createClient: (node) => new PgClient(node),
+ loggers: {
+ clientQueryLogger: (props) => console.debug(props.msg),
+ swallowedErrorLogger: (props) => console.log(props),
+ },
+});
+
+// Pre-open min number of DB connections.
+cluster.prewarm();
+```
+
+{% endcode %}
+
+Terminology:
+
+1. **Cluster** consists of **Islands**. Each Island is identified by an integer number (there can be many islands for horizontal scaling of the cluster).
+2. Island consists of master + replica **nodes** (in the above example, we only define one master node and no replicas).
+3. Island also hosts **Microshards** (in the example above, we will have no microshards, aka just one global shard). Microshards may travel from island to island during shards rebalancing process; the engine tracks this automatically ("shards discovery").
+
+Notice that we define the layout of the cluster using a callback. Ent Framework will call it from time to time to refresh the view of the cluster, so in this callback, you can read the data from some centralized configuration database (new nodes may be added, or empty nodes may be removed with no downtime). This is called "dynamic real-time reconfiguration".
+
+[PgClient](https://github.com/clickup/ent-framework/blob/main/docs/classes/PgClient.md) class accepts several options, one of them is the standard [node-postgres PoolConfig](https://node-postgres.com/apis/pool) interface. For simplicity, when we define a cluster shape in `islands`, we just return a list of such configs, to be passed into `createClient()` lambda.
+
+As of `prewarm()` call, it's explained in Advanced section.
+
+
+# Create Ent Classes
+
+Once you have a Cluster instance, you can create Ent classes to access the data.
+
+{% code title="ents/EntUser.ts" %}
+
+```typescript
+import { PgSchema } from "ent-framework/pg";
+import { ID, BaseEnt, GLOBAL_SHARD, AllowIf, OutgoingEdgePointsToVC } from "ent-framework";
+import { cluster } from "./cluster";
+
+const schema = new PgSchema(
+ "users",
+ {
+ id: { type: ID, autoInsert: "nextval('users_id_seq')" },
+ email: { type: String },
+ is_admin: { type: Boolean, autoInsert: "false" },
+ },
+ ["email"]
+);
+
+export class EntUser extends BaseEnt(cluster, schema) {
+ static override configure() {
+ return new this.Configuration({
+ shardAffinity: GLOBAL_SHARD,
+ privacyInferPrincipal: async (_vc, row) => row.id,
+ privacyLoad: [new AllowIf(new OutgoingEdgePointsToVC("id"))],
+ privacyInsert: [],
+ });
+ }
+}
+```
+
+{% endcode %}
+
+If your app uses UUID type for IDs, replace `{ type: ID, autoInsert: "nextval('users_id_seq')" }` with something like:
+
+```typescript
+id: { type: String, autoInsert: "gen_random_uuid()" }
+```
+
+(Notice that you need to use type `String` and not `ID` for UUID fields. Read more about ID formats and microsharding aspects in [Locating a Shard and ID Format](/scalability/locating-a-shard-id-format) article.)
+
+Each Ent may also have one optional "unique key" (possible composite) which is treated by the engine in a specific optimized way. In the above example, it's `email`.
+
+{% code title="ents/EntTopic.ts" %}
+
+```typescript
+import { PgSchema } from "ent-framework/pg";
+import {
+ ID,
+ BaseEnt,
+ GLOBAL_SHARD,
+ AllowIf,
+ OutgoingEdgePointsToVC,
+ Require,
+} from "ent-framework";
+import { cluster } from "./cluster";
+
+const schema = new PgSchema(
+ "topics",
+ {
+ id: { type: ID, autoInsert: "nextval('topics_id_seq')" },
+ created_at: { type: Date, autoInsert: "now()" },
+ updated_at: { type: Date, autoUpdate: "now()" },
+ slug: { type: String },
+ creator_id: { type: ID },
+ subject: { type: String, allowNull: true },
+ },
+ ["slug"]
+);
+
+export class EntTopic extends BaseEnt(cluster, schema) {
+ static override configure() {
+ return new this.Configuration({
+ shardAffinity: GLOBAL_SHARD,
+ privacyInferPrincipal: async (_vc, row) => row.creator_id,
+ privacyLoad: [new AllowIf(new OutgoingEdgePointsToVC("creator_id"))],
+ privacyInsert: [new Require(new OutgoingEdgePointsToVC("creator_id"))],
+ });
+ }
+}
+```
+
+{% endcode %}
+
+By default, all fields are non-nullable (unless you provide `allowNull` option).
+
+Disregard privacy rules for now, it's a more complicated topic which will be covered later. For now, the code should be obvious enough.
+
+{% code title="ents/EntComment.ts" %}
+
+```typescript
+import { PgSchema } from "ent-framework/pg";
+import {
+ ID,
+ BaseEnt,
+ AllowIf,
+ CanReadOutgoingEdge,
+ OutgoingEdgePointsToVC,
+ Require,
+} from "ent-framework";
+import { cluster } from "./cluster";
+import { EntTopic } from "./EntTopic";
+
+const schema = new PgSchema(
+ "comments",
+ {
+ id: { type: ID, autoInsert: "nextval('comments_id_seq')" },
+ created_at: { type: Date, autoInsert: "now()" },
+ topic_id: { type: ID },
+ creator_id: { type: ID },
+ message: { type: String },
+ },
+ []
+);
+
+export class EntComment extends BaseEnt(cluster, schema) {
+ static override configure() {
+ return new this.Configuration({
+ shardAffinity: GLOBAL_SHARD,
+ privacyInferPrincipal: async (_vc, row) => row.creator_id,
+ privacyLoad: [
+ new AllowIf(new CanReadOutgoingEdge("topic_id", EntTopic)),
+ new AllowIf(new OutgoingEdgePointsToVC("creator_id")),
+ ],
+ privacyInsert: [new Require(new OutgoingEdgePointsToVC("creator_id"))],
+ });
+ }
+}
+```
+
+{% endcode %}
+
+Since we have no microshards yet, `shardAffinity` basically does nothing. We'll talk about microsharding in [Locating a Shard and ID Format](/scalability/locating-a-shard-id-format).
+
+
+# VC: Viewer Context and Principal
+
+One of the most important Ent Framework traits is that it always knows, "who" is sending some read/write query to the database, and is able to check permissions. Typically, that "who" is a user who opens a web page, or on behalf of whom a background worker job is running, but it can be any other **Principal**. This mechanism is quite different from traditional database abstraction layers or ORMs, which typically lack awareness of the specific user on whose behalf the queries are executed.
+
+To send a query, you must always have an instance of [VC](https://github.com/clickup/ent-framework/blob/main/docs/classes/VC.md) class in hand (stands for **Viewer Context**). The most important property in a VC is `principal`, it's a string which identifies the party who's acting. Typically, we store some user ID in `vc.principal`.
+
+It is intentionally not easy to create a brand new VC instance. In fact, you should only do it once in your app (this VC is called "root VC"), and all other VCs created should **derive** from that VC using its methods.
+
+Below is a basic example for [Next.js](https://nextjs.org/) framework. (Of course you can use any other framework like Express or whatever. Next.js is here only for illustrative purposes, it has nothing to do with Ent Framework.)
+
+## Integrate with e.g. Google Auth
+
+For simplicity of the example, we'll plug in "Login with Google" feature to our Next app, and then will use the user's email as a primary method of addressing an EntUser.
+
+{% code title="app/api/auth/\[...nextauth]/route.ts" %}
+
+```typescript
+import NextAuth from "next-auth";
+import GoogleProvider from "next-auth/providers/google";
+
+const handler = NextAuth({
+ providers: [
+ GoogleProvider({
+ clientId: process.env.GOOGLE_ID,
+ clientSecret: process.env.GOOGLE_SECRET,
+ }),
+ ],
+});
+
+export { handler as GET, handler as POST };
+```
+
+{% endcode %}
+
+Now on any page, you may place a [Sign in button component](https://github.com/dimikot/ent-framework/blob/main/examples/next-example/src/components/SignInButton.tsx):
+
+{% code title="components/SignInButton.tsx" %}
+
+```typescript
+import { signIn } from "next-auth/react";
+...
+ signIn("google")}>Sign in
+```
+
+{% endcode %}
+
+Next.js exposes `getServerSession()` function for server components, to allow you access the session data of the user, including their email:
+
+{% code title="app/page.tsx" %}
+
+```typescript
+import { getServerSession } from "next-auth";
+
+export default async function Home() {
+ const session = await getServerSession();
+ return session ? (
+
Welcome, {session.user?.name}!
+ ) : (
+
Please sign in to continue.
+ );
+}
+```
+
+{% endcode %}
+
+You can also use `getServerSession()` from inside of your API route handlers.
+
+## Build a Request VC Accessor Function
+
+The same way as `getServerSession()` gives us access to the user's session, let's build a function that returns a VC instance for that user. Technically, this function should work exactly the same way as `getServerSession()`: it will even use `session.user.email` field from there.
+
+And in case the user is not authenticated yet, we still need a "guest VC" to be returned by this function. Such VC can still access some "public" Ents (depending on their privacy rules).
+
+The VC instance should be "memoized" per the HTTP request, so if the VC accessor function is called multiple time, it should return the same object. This is critical: otherwise, many Ent Framework features (like queries batching and caching) will just not work as they should.
+
+Different frameworks have different ways of attaching a property to the request object. In Next, the easiest way so far is to use `WeakMap` and `headers()` API function. (In Express, you would likely just assign a value to `req.vc` in some middleware.)
+
+{% code title="ents/getServerVC.ts" %}
+
+```typescript
+import { VC } from "ent-framework";
+import { getServerSession } from "next-auth";
+import { headers } from "next/headers";
+import { EntUser } from "./EntUser";
+
+const vcStore = new WeakMap