This PR contains the following updates:
| Package | Update | Change |
|---|---|---|
| redis | digest | `718f745` → `6f81e89` |
---
### Configuration
📅 **Schedule**: (UTC)
- Branch creation
- "before 9am on monday"
- Automerge
- At any time (no schedule defined)
🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.
♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
🔕 **Ignore**: Close this PR and you won't be reminded about this update again.
---
- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box
---
This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/expo/entity).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4xMjUuMSIsInVwZGF0ZWRJblZlciI6IjQ0LjEyNS4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->
This PR contains the following updates:
| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [ip-address](https://redirect.github.com/beaugunderson/ip-address) | [`10.4.0` → `10.5.1`](https://renovatebot.com/diffs/npm/ip-address/10.4.0/10.5.1) |  |  |
---
### ip-address: no classifier recognizes the NAT64 local-use range 64:ff9b:1::/48, allowing SSRF and trust-boundary bypass
[CVE-2026-101910](https://nvd.nist.gov/vuln/detail/CVE-2026-101910) / [GHSA-2vr4-cq9g-pvrc](https://redirect.github.com/advisories/GHSA-2vr4-cq9g-pvrc)
<details>
<summary>More information</summary>
#### Details
##### Summary
No classifier on `Address6` recognizes the NAT64 local-use range `64:ff9b:1::/48` (RFC 8215). `isPrivate()`, `isLoopback()`, `isLinkLocal()` and their siblings all return `false` for every address in it, so an internal IPv4 destination written through a local-use NAT64 prefix (`64:ff9b:1:7f00:0:100::` for `127.0.0.1`, `64:ff9b:1:a9fe:a9:fe00::` for `169.254.169.254`) reads as an ordinary global address. `getType()` names the range `'NAT64 (local-use)'`, so the library knows what the address is and classifies it as nothing.
An application that builds a network trust-boundary decision on these checks (for example a filter intended to block Server-Side Request Forgery, or SSRF) will classify an internal target as external and allow the request. SSRF is an attack in which a user-supplied address coaxes the server into making a request to an internal destination the user could not otherwise reach, such as a loopback service or a cloud metadata endpoint.
##### Details
The fix for GHSA-22jq-vg5j-6vgg (10.2.1) classifies IPv4-mapped (`::ffff:0:0/96`) and NAT64 well-known (`64:ff9b::/96`) addresses by their embedded IPv4 address: `embeddedIPv4()` in `src/ipv6.ts` decodes the trailing 32 bits and every special-use classifier delegates to the resulting `Address4`. Those are the only two ranges `embeddedIPv4()` handles.
The local-use range differs in kind from the well-known prefix, which is why extending the decoder does not fix it. RFC 8215 reserves `64:ff9b:1::/48` so an operator can carve their own NAT64 prefix out of it, of any length RFC 6052 allows (`/48`, `/56`, `/64`, or `/96`). Where the IPv4 address sits depends on that choice: `127.0.0.1` is `64:ff9b:1:7f00:0:100::` under a `/48` prefix and `64:ff9b:1::7f00:1` under a `/96` prefix, and each of those decodes to a different address under the other prefix length. There is no single embedded IPv4 address for the library to classify by.
What is well-defined is the range as a whole. The IANA IPv6 Special-Purpose Address Registry lists `64:ff9b:1::/48` as not globally reachable, and Python's `ipaddress` module reports `is_private` as `True` and `is_global` as `False` for every address in it. `isPrivate()` covered ULA (`fc00::/7`) plus the decoded mapped and well-known cases, and nothing in the local-use range.
##### Affected versions
`>= 10.2.0, <= 10.5.0`. The `is*` classification API was extended to `Address6` in 10.2.0; releases before it do not expose the method and are not affected through this vector.
##### Impact
Every address in `64:ff9b:1::/48` parses successfully, `isValid()` is `true`, and no classifier catches it. Which internal target is reached depends on the NAT64 prefix deployed on the server's network; the well-known prefix column is the patched control from GHSA-22jq-vg5j-6vgg.
| Internal target | Well-known form | Classified | Local-use form (`/48` prefix) | Classified |
|---|---|---|---|---|
| `127.0.0.1` | `64:ff9b::7f00:1` | loopback | `64:ff9b:1:7f00:0:100::` | nothing |
| `10.0.0.1` | `64:ff9b::a00:1` | private | `64:ff9b:1:a00:0:100::` | nothing |
| `169.254.169.254` | `64:ff9b::a9fe:a9fe` | link-local | `64:ff9b:1:a9fe:a9:fe00::` | nothing |
| `192.168.1.1` | `64:ff9b::c0a8:101` | private | `64:ff9b:1:c0a8:1:100::` | nothing |
##### Reachability
Reaching an internal host through one of these addresses requires that the server's network run a NAT64 translator on a prefix inside `64:ff9b:1::/48` and that the attacker guess or know the prefix length. That is the same precondition the well-known prefix carries (a translator on `64:ff9b::/96`), with the additional constraint that the prefix is operator-chosen rather than fixed. The severity reflects that precondition.
##### Proof of concept
`npm i [email protected]`, then:
```js
const { Address6 } = require('ip-address');
// A guard of the shape the library documents.
function isBlocked(host) {
const a = new Address6(host);
return a.isLoopback() || a.isLinkLocal() || a.isPrivate() || a.isMulticast() || a.isUnspecified();
}
for (const h of ['64:ff9b::7f00:1', '64:ff9b:1:7f00:0:100::', '64:ff9b:1::7f00:1']) {
console.log(isBlocked(h) ? 'BLOCK' : 'ALLOW', h, '-> getType()', new Address6(h).getType());
}
```
On affected versions:
```
BLOCK 64:ff9b::7f00:1 -> getType() NAT64 (well-known)
ALLOW 64:ff9b:1:7f00:0:100:: -> getType() NAT64 (local-use)
ALLOW 64:ff9b:1::7f00:1 -> getType() NAT64 (local-use)
```
##### Remediation
Upgrade to the patched release. In the fix, `isPrivate()` returns `true` for every address in `64:ff9b:1::/48`, alongside ULA and the decoded mapped and well-known cases. The range is reported private as a whole rather than by a decoded IPv4 address, for the reason given above; `toAddress4Nat64(prefix)` remains the way to decode an address under a known deployment prefix. `isLoopback()` and `isLinkLocal()` are unchanged for this range, since without the prefix length the library cannot know which IPv4 address is embedded.
If you cannot upgrade immediately, test the range directly:
```js
const NAT64_LOCAL_USE = new Address6('64:ff9b:1::/48');
const localUse = new Address6(host).isHostInSubnet(NAT64_LOCAL_USE);
```
##### A note on SSRF defense
These methods are address classifiers, not a complete SSRF defense. Regardless of this fix, a robust SSRF guard must resolve the hostname and validate the *resolved* IP against the socket it connects to, and account for DNS rebinding and redirects. Treat these checks as one layer, not the only one.
#### Severity
- CVSS Score: 6.9 / 10 (Medium)
- Vector String: `CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:L/VI:N/VA:N/SC:H/SI:N/SA:N`
#### References
- [https://github.com/beaugunderson/ip-address/security/advisories/GHSA-2vr4-cq9g-pvrc](https://redirect.github.com/beaugunderson/ip-address/security/advisories/GHSA-2vr4-cq9g-pvrc)
- [https://nvd.nist.gov/vuln/detail/CVE-2026-101910](https://nvd.nist.gov/vuln/detail/CVE-2026-101910)
- [https://github.com/beaugunderson/ip-address/commit/ab3dc88bcf5374344168a2ba075ca7ac4ff257f8](https://redirect.github.com/beaugunderson/ip-address/commit/ab3dc88bcf5374344168a2ba075ca7ac4ff257f8)
- [https://github.com/beaugunderson/ip-address/releases/tag/v10.5.1](https://redirect.github.com/beaugunderson/ip-address/releases/tag/v10.5.1)
- [https://github.com/advisories/GHSA-2vr4-cq9g-pvrc](https://redirect.github.com/advisories/GHSA-2vr4-cq9g-pvrc)
This data is provided by the [GitHub Advisory Database](https://redirect.github.com/advisories/GHSA-2vr4-cq9g-pvrc) ([CC-BY 4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).
</details>
---
### Release Notes
<details>
<summary>beaugunderson/ip-address (ip-address)</summary>
### [`v10.5.1`](https://redirect.github.com/beaugunderson/ip-address/compare/v10.5.0...38b02d73cfc0724f9ad61c65b6328e116eac03ec)
[Compare Source](https://redirect.github.com/beaugunderson/ip-address/compare/v10.5.0...v10.5.1)
### [`v10.5.0`](https://redirect.github.com/beaugunderson/ip-address/releases/tag/v10.5.0)
[Compare Source](https://redirect.github.com/beaugunderson/ip-address/compare/v10.4.0...v10.5.0)
##### What's Changed
- Honor the fromURL graceful-failure contract for non-IPv6 hosts by [@​beaugunderson](https://redirect.github.com/beaugunderson) in [#​218](https://redirect.github.com/beaugunderson/ip-address/pull/218)
- Correct the documentation where it disagreed with the library by [@​beaugunderson](https://redirect.github.com/beaugunderson) in [#​219](https://redirect.github.com/beaugunderson/ip-address/pull/219)
**Full Changelog**: <https://github.com/beaugunderson/ip-address/compare/v10.4.0...v10.5.0>
</details>
---
### Configuration
📅 **Schedule**: (UTC)
- Branch creation
- At any time (no schedule defined)
- Automerge
- At any time (no schedule defined)
🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.
♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
🔕 **Ignore**: Close this PR and you won't be reminded about this update again.
---
- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box
---
This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/expo/entity).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4xMTIuMCIsInVwZGF0ZWRJblZlciI6IjQ0LjExMi4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->
This PR contains the following updates:
| Package | Update | Change |
|---|---|---|
| [postgres](https://hub.docker.com/_/postgres) ([source](https://redirect.github.com/docker-library/postgres)) | digest | `86c951e` → `5a5a84b` |
---
### Configuration
📅 **Schedule**: (UTC)
- Branch creation
- "before 9am on monday"
- Automerge
- At any time (no schedule defined)
🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.
♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
🔕 **Ignore**: Close this PR and you won't be reminded about this update again.
---
- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box
---
This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/expo/entity).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4xMTIuMCIsInVwZGF0ZWRJblZlciI6IjQ0LjExMi4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->
This PR contains the following updates:
| Package | Update | Change |
|---|---|---|
| redis | digest | `8a1efc5` → `718f745` |
---
### Configuration
📅 **Schedule**: (UTC)
- Branch creation
- "before 9am on monday"
- Automerge
- At any time (no schedule defined)
🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.
♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
🔕 **Ignore**: Close this PR and you won't be reminded about this update again.
---
- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box
---
This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/expo/entity).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4xMTIuMCIsInVwZGF0ZWRJblZlciI6IjQ0LjExMi4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->
This PR contains the following updates:
| Package | Update | Change |
|---|---|---|
| redis | digest | `298e5b3` → `8a1efc5` |
---
### Configuration
📅 **Schedule**: (UTC)
- Branch creation
- "before 9am on monday"
- Automerge
- At any time (no schedule defined)
🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.
♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
🔕 **Ignore**: Close this PR and you won't be reminded about this update again.
---
- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box
---
This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/expo/entity).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4xMDMuMCIsInVwZGF0ZWRJblZlciI6IjQ0LjEwMy4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->
This PR contains the following updates:
| Package | Update | Change |
|---|---|---|
| [postgres](https://hub.docker.com/_/postgres) ([source](https://redirect.github.com/docker-library/postgres)) | digest | `4ef4dbc` → `86c951e` |
---
### Configuration
📅 **Schedule**: (UTC)
- Branch creation
- "before 9am on monday"
- Automerge
- At any time (no schedule defined)
🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.
♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
🔕 **Ignore**: Close this PR and you won't be reminded about this update again.
---
- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box
---
This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/expo/entity).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4xMDMuMCIsInVwZGF0ZWRJblZlciI6IjQ0LjEwMy4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->
This PR contains the following updates:
| Package | Update | Change |
|---|---|---|
| [jdx/mise](https://redirect.github.com/jdx/mise) | patch | `v2026.9.2` → `v2026.9.7` |
---
### Release Notes
<details>
<summary>jdx/mise (jdx/mise)</summary>
### [`v2026.9.7`](https://redirect.github.com/jdx/mise/releases/tag/v2026.9.7): : Lockfile revision 2 with npm and Python dependency graphs, dotfile conflict inspection
[Compare Source](https://redirect.github.com/jdx/mise/compare/v2026.9.6...v2026.9.7)
This release introduces `mise.lock` revision 2, which records complete transitive dependency graphs for npm tools (via embedded aube) and Python tools (via uv) in native sidecar files, and adds `mise bootstrap dotfiles conflicts` for inspecting dotfile sync conflicts before resolving them. It also lets dotfile templates consume bootstrap secrets, stops `minimum_release_age` from rejecting versions already committed to a lockfile, and closes a security gap in `history.describe_command`.
##### Added
- **lock:** Lockfile revision 2 records the full dependency graph of npm tools installed by embedded aube and of `pypi:` tools installed by uv, then replays it with a strict frozen install so two projects on the same top-level version can still receive their own reviewed transitive graph. Graphs live in native sidecar files (`uv.lock` / `aube-lock.yaml` plus a manifest) under `.mise/locks/<backend-tool>/<version>/`, referenced from `mise.lock` by relative path and SHA-256 digest, so the lockfile itself stays small. Commit the sidecar directory with `mise.lock`. New lockfiles use revision 2; existing revision 0 and 1 files keep their format until you run `mise lock --upgrade`. `mise lock --bump <tool>` refreshes a tool's transitive graph even when its top-level version is unchanged, ordinary `mise install` validates and accepts hand-edited sidecars, and `mise install --locked` rejects digest mismatches until you run `mise lock`. Python graph locking requires uv 0.12.10 or newer and published wheels for the target platform; Git sources, standalone pipx installs, and free-form `uvx_args`/`pipx_args` stay version-only. ([#​13131](https://redirect.github.com/jdx/mise/pull/13131), [#​13146](https://redirect.github.com/jdx/mise/pull/13146) by [@​jdx](https://redirect.github.com/jdx))
```sh
mise lock --upgrade # move an existing lockfile to revision 2 and resolve graphs
mise install --locked # replay the recorded graphs
mise lock --bump pypi:black # refresh Black's dependencies without changing its version
```
- **pypi:** `pypi:` is now the preferred name for the Python CLI backend; `pipx:` remains fully supported as an alias with no warnings, and settings accept both `pypi.*` and `pipx.*` names. The two spellings are distinct tool identities (`pypi-black` vs `pipx-black` install directories and lock entries), so switching spelling creates a new installation. ([#​13146](https://redirect.github.com/jdx/mise/pull/13146) by [@​jdx](https://redirect.github.com/jdx))
- **bootstrap:** `mise bootstrap dotfiles conflicts [PATH...]` shows a read-only comparison of the saved local and fetched remote versions of a conflicted dotfile so you can decide between `--take-remote` and `--keep-local` with full context. The default output is a unified diff including file-mode changes; `--difftool` opens the configured Git `diff.tool` (falling back to `merge.tool`) and `--tool <name>` picks one explicitly. Encrypted contents are decrypted only into private temporary files, and inspection never modifies either side or marks the conflict resolved. Bootstrap secrets are also now resolved from the same composed config maps as dotfile discovery, so root-scoped dotfile templates can use secrets declared by their bootstrap root. ([#​13144](https://redirect.github.com/jdx/mise/pull/13144) by [@​jdx](https://redirect.github.com/jdx))
```sh
mise bootstrap dotfiles conflicts ~/.config/mise/config.toml
mise bootstrap dotfiles conflicts --difftool ~/.config/mise/config.toml
```
- **dotfiles:** Dotfile templates (`mode = "template"`) can reference `[bootstrap.secrets]` values with `{{ secret(name="...") }}`, matching managed bootstrap file templates. Dotfiles commands that render templates (`add`, `apply`, `diff`, `edit`, `status`, `unapply`) accept `--prompt-secrets`; without an available value, rendering fails closed. A full `mise bootstrap` run preflights dotfile templates before making changes, `mise bootstrap status` reports secrets used only by dotfiles, and textual diffs redact resolved secret values. ([#​13140](https://redirect.github.com/jdx/mise/pull/13140) by [@​jdx](https://redirect.github.com/jdx))
```toml
[bootstrap.secrets]
api_token = "EXAMPLE_API_TOKEN"
[dotfiles."~/.config/example/credentials"]
source = "dotfiles/credentials.tmpl"
mode = "template"
```
##### Fixed
- **lock:** Installing from a committed `mise.lock` no longer fails when the locked release is younger than `minimum_release_age`. The cutoff still applies when resolving unlocked fuzzy requests and when generating or bumping a lockfile, and `npm:`/`pypi:` still forward it to unpinned transitive dependencies, but a reviewed lock entry now reproduces immediately in CI instead of waiting for the release to cool. ([#​13128](https://redirect.github.com/jdx/mise/pull/13128) by [@​jdx](https://redirect.github.com/jdx))
- **config:** A `.python-version` (or other idiomatic version file) containing `system` selects the system interpreter without printing the mise-specific `@system` deprecation warning, matching the existing `.tool-versions` exception. Explicit `python@system` requests from mise configuration or command arguments still warn. ([#​13132](https://redirect.github.com/jdx/mise/pull/13132) by [@​jdx](https://redirect.github.com/jdx))
- **npm:** Embedded aube is updated to 2.2.16, fixing the Bun checksum install regression and ensuring local npm tarballs keep their manifest package name. ([#​13145](https://redirect.github.com/jdx/mise/pull/13145) by [@​jdx](https://redirect.github.com/jdx))
- **registry:** The `mc` shorthand uses `aqua:minio/mc` again now that the upstream Aqua registry entry is restored, with `asdf:mise-plugins/mise-mc` kept as the fallback. ([#​13124](https://redirect.github.com/jdx/mise/pull/13124) by [@​jdx](https://redirect.github.com/jdx))
##### Security
- **history:** `history.describe_command` is now global-only. Previously an implicitly trusted project could set it and have a later dotfiles history checkpoint execute the project-controlled command with unencrypted tracked-file diffs. The setting is honored only from system/global configuration or `MISE_HISTORY_DESCRIBE_COMMAND`; project values are ignored with a warning. ([#​13134](https://redirect.github.com/jdx/mise/pull/13134) by [@​jdx](https://redirect.github.com/jdx))
- **oci:** `mise oci build` now renders dotfile templates with a restricted engine: `secret()` is rejected and the `env` context, `get_env()`, `exec()`, and `read_file()` are unavailable, so ambient credentials cannot be baked into a publishable image layer. ([#​13140](https://redirect.github.com/jdx/mise/pull/13140) by [@​jdx](https://redirect.github.com/jdx))
##### Breaking Changes
- **Lockfile revision 2 is not readable by older mise versions.** Newly created lockfiles use revision 2, and existing files switch only when you run `mise lock --upgrade`. Upgrade collaborators and CI to this release before committing a revision 2 `mise.lock`, and commit the `.mise/locks/` (or `.config/mise/locks/`) sidecar directory alongside it. Revision 2 `--locked` installs fail if a recorded graph is missing or its digest does not match. If you gitignore `mise.local.lock`, also ignore its matching sidecar subdirectory (for example `.mise/locks/mise.local/`).
- **`history.describe_command` in project configuration is ignored.** Move it to `~/.config/mise/config.toml` or set `MISE_HISTORY_DESCRIBE_COMMAND`.
- **`mise oci build` dotfile templates** can no longer call `secret()`, `get_env()`, `exec()`, or `read_file()` or read the `env` context.
**Full Changelog**: <https://github.com/jdx/mise/compare/v2026.9.6...v2026.9.7>
##### 💚 Sponsor mise
mise is built and maintained by [@​jdx](https://redirect.github.com/jdx), an open source developer at [**entire.io**](https://entire.io/), the title sponsor of his open source work.
If mise saves you or your team time, please consider becoming an [individual or company sponsor](https://jdx.dev/sponsors.html). Your support funds ongoing development and helps keep mise fast, free, and independent.
### [`v2026.9.6`](https://redirect.github.com/jdx/mise/releases/tag/v2026.9.6): : Project daemons, mise doctor project, and vfox backend tool discovery
[Compare Source](https://redirect.github.com/jdx/mise/compare/vfox-v2026.9.5...v2026.9.6)
This release adds experimental project daemons backed by pitchfork, a `mise doctor project` command for project-declared diagnostic checks, and tool discovery from vfox backend plugins in `mise search`. It also changes the HTTP backend's default install layout so uninstall and prune reclaim disk space, speeds up warm `lockfile_mode = "generate"` installs and repeated OCI builds, and fixes a batch of nushell, monorepo, lockfile, brew, and Windows bootstrap issues.
#### Highlights
- **Services next to tools:** `[daemons]` declares background processes and PostgreSQL/Redis presets in `mise.toml`, managed through `mise daemons` and optionally started when you enter the project. `[doctor.checks.<name>]` lets projects declare their own environment probes for `mise doctor project`.
- **Discovery and output:** `mise search`, shell completion, and interactive `mise use` now include tools published by installed vfox backend plugins, and `settings.truncate` / `--no-truncate` disable terminal-width truncation (automatically when a coding agent is detected).
- **Storage and speed:** HTTP tools now extract into their own install directory by default (opt back into deduplication with `shared_extraction = true`), warm generate-mode installs skip needless lockfile rewrites, and OCI builds share a local tool-layer cache.
#### Added
- **daemons:** New experimental `[daemons]` section and `mise daemons` command family (`start`, `stop`, `restart`, `ls`, `status`, `logs`, `tui`) manage project background processes with pitchfork. PostgreSQL and Redis presets install the database as a tool (participating in lockfiles), supply connection environment variables and readiness checks, and keep project data across stop/start. Daemons with `auto = ["start", "stop"]` start when entering the project from an activated Bash, Zsh, or Fish shell and are released when the last shell session leaves. Requires `experimental = true` and pitchfork 2.25.0 or later; database presets are Unix-only and PostgreSQL uses loopback trust authentication intended for local development. ([#​13085](https://redirect.github.com/jdx/mise/pull/13085) by [@​jdx](https://redirect.github.com/jdx))
```toml
[settings]
experimental = true
[daemons]
postgres = "18"
redis = "8"
[daemons.web]
run = "npm run dev"
port = 3000
auto = ["start", "stop"]
```
- **doctor:** `mise doctor project` runs checks declared in `[doctor.checks.<name>]` with the project's environment and installed tools, reporting PASS/FAIL/error/skipped per check in text or `--json`. Checks support `description`, `hint`, `timeout` (default `10s`), `dir`, `shell`, and `os` selectors, run concurrently under the `jobs` limit, and exit nonzero when any check fails. Ordinary `mise doctor` does not run them, and hints are never executed. A follow-up aligned `dir` resolution with task conventions (config root for project configs including `~/mise.toml`, `~/` expansion), fixed head-of-line blocking when one probe hangs, and kept `nohup mise doctor project` alive on SIGHUP. ([#​13062](https://redirect.github.com/jdx/mise/pull/13062), [#​13089](https://redirect.github.com/jdx/mise/pull/13089) by [@​jdx](https://redirect.github.com/jdx))
```toml
[doctor.checks.openssl]
description = "OpenSSL development files are discoverable"
run = "pkg-config --exists openssl"
hint = "Run `mise bootstrap packages apply` to install the declared build dependencies."
timeout = "5s"
os = ["linux", "macos"]
```
- **vfox:** Tools provided by installed vfox backend plugins now appear in `mise search`, shell completion, and interactive `mise use`, namespaced as `<plugin>:<tool>`. Plugins can implement `BackendListTools` for a finite catalog and/or `BackendSearchTools` for query-driven discovery in large ecosystems; a prefixed query like `npm:eslint` is routed only to that plugin. Results are cached, slow plugins fall back to stale cache, and existing plugins need no changes. `mise registry` remains registry-only. ([#​13111](https://redirect.github.com/jdx/mise/pull/13111) by [@​jdx](https://redirect.github.com/jdx))
- **cli:** New `settings.truncate` (and `MISE_TRUNCATE`, default `true`) controls terminal-width shortening of table cells and task metadata. `mise ls`, `mise config ls`, and `mise bootstrap dotfiles status` gain `--truncate` / `--no-truncate`, and output is kept complete automatically when a known coding agent is detected. ([#​13112](https://redirect.github.com/jdx/mise/pull/13112) by [@​jdx](https://redirect.github.com/jdx))
```sh
mise bootstrap dotfiles status --no-truncate
```
- **bootstrap:** `[bootstrap.macos.dock]` gains `apps`, an ordered list of pinned application paths. Status compares identity and order (ignoring Dock-added metadata), apply adds, removes, and reorders application tiles while preserving other tiles and `persistent-others`, and an empty list removes all application tiles. Paths must be absolute or home-relative `.app` bundles. ([#​13075](https://redirect.github.com/jdx/mise/pull/13075) by [@​azohra](https://redirect.github.com/azohra))
```toml
[bootstrap.macos.dock]
apps = [
"/System/Applications/Utilities/Terminal.app",
"/Applications/Firefox.app",
]
```
- **bootstrap:** `mise bootstrap packages where brew:<formula>` prints an installed formula's stable `opt` root (for example `/opt/homebrew/opt/unzip`), so scripts can put keg-only executables on PATH without hardcoding the Homebrew prefix or Cellar version. Missing installs exit nonzero with empty stdout. ([#​13083](https://redirect.github.com/jdx/mise/pull/13083) by [@​himkt](https://redirect.github.com/himkt))
- **dotfiles:** Destination `variants` can omit `source` when every variant sets a `target`; the entry key is then resolved as a relative path under `settings.dotfiles.root` instead of next to `mise.toml`. Parent traversal is rejected. ([#​13087](https://redirect.github.com/jdx/mise/pull/13087) by [@​jdx](https://redirect.github.com/jdx))
```toml
[dotfiles."vscode/settings.json"]
mode = "copy"
variants = [
{ os = "macos", target = "~/Library/Application Support/Code/User/settings.json" },
{ os = "linux", target = "~/.config/Code/User/settings.json" },
]
```
- **fmt:** `mise fmt` now sorts lists whose order has no meaning: `redactions` lexically, and task `sources`/`outputs`, `task_templates` sources/outputs, `task_config.global_inputs`, and `input_groups` by reach (`@group:` references, then globs, then literal paths). Lists containing `!` exclusions, entries starting with template syntax, or comments are left untouched, and precedence-sensitive lists such as `env_file`, `tools.*`, `includes`, and `depends` are never sorted. ([#​13058](https://redirect.github.com/jdx/mise/pull/13058) by [@​jrandolf](https://redirect.github.com/jrandolf))
- **oci:** `mise oci build` gains `--no-cache` to bypass the new local tool-layer cache; entries live under each tool's cache directory and are removed by `mise cache clear TOOL`. ([#​13056](https://redirect.github.com/jdx/mise/pull/13056) by [@​jdx](https://redirect.github.com/jdx))
#### Changed
- **http:** New `http:` installations extract directly into their own install directory, so `mise uninstall` and `mise prune` now remove their files instead of leaving payloads in `$MISE_DATA_DIR/http-tarballs/`. Set `shared_extraction = true` on a tool to keep the previous deduplicated symlink layout. Existing symlinked installs keep working; `mise install --force <tool>` migrates one to independent files without disturbing other installs that share the content. Legacy `http-tarballs` entries are not reclaimed automatically. Shared raw and compressed binary caches now also include the executable filename in their key, so differently named tools no longer reuse the wrong filename. ([#​13059](https://redirect.github.com/jdx/mise/pull/13059) by [@​jdx](https://redirect.github.com/jdx))
- **oci:** `mise oci push --no-cache` now bypasses both the remote registry cache and the local tool-layer cache. ([#​13056](https://redirect.github.com/jdx/mise/pull/13056) by [@​jdx](https://redirect.github.com/jdx))
- **registry:** `postgres`, `redis`, and `mongodb` now prefer `conda:` backends, installing prebuilt conda-forge binaries in seconds instead of compiling through vfox; vfox and asdf remain as fallbacks. `conda:redis-server` covers Linux and macOS only. ([#​13061](https://redirect.github.com/jdx/mise/pull/13061) by [@​jdx](https://redirect.github.com/jdx))
#### Performance
- **lockfile:** Warm `mise install` runs in `lockfile_mode = "generate"` skip scheduling work for tools whose artifact metadata is already reusable, and skip rebuilding, serializing, and staging the lockfile entirely when nothing was installed and the on-disk lock already matches (preserving comments in the file). Explicit `mise lock`, forced provenance verification, upgrades, and new platforms still regenerate. ([#​13101](https://redirect.github.com/jdx/mise/pull/13101), [#​13103](https://redirect.github.com/jdx/mise/pull/13103) by [@​jdx](https://redirect.github.com/jdx))
- **oci:** `mise oci push --from BASE` no longer downloads base layers when the base and target live in the same repository, and `oci build`, `oci run`, and `oci push` share a local cache of packaged tool layers keyed on file contents, so repeated builds with overlapping tools skip tar and gzip work. ([#​13055](https://redirect.github.com/jdx/mise/pull/13055), [#​13056](https://redirect.github.com/jdx/mise/pull/13056) by [@​jdx](https://redirect.github.com/jdx))
#### Fixed
- **nushell:** `mise activate nu` no longer throws `env_variable_not_found` on every prompt or `cd` when a variable to hide is absent from the current scope; `hide-env` is now wrapped in `try`, matching the no-op behavior of other shells. ([#​13071](https://redirect.github.com/jdx/mise/pull/13071) by [@​i-api](https://redirect.github.com/i-api))
- **task:** Task-level tools are now auto-installed for tasks referenced from `run` entries, including names rendered at runtime, right before they execute; `--skip-tools` is honored and install failures are reported as task failures without blocking siblings. ([#​13086](https://redirect.github.com/jdx/mise/pull/13086) by [@​jdx](https://redirect.github.com/jdx))
- **config:** `[monorepo]` settings are now merged across same-directory config layers (base plus `mise.<env>.toml` overlays): omitted fields are inherited, an overlay's `config_roots` replaces the base list, and `monorepo_root = false` in an overlay disables the root and its descendant trust. ([#​13084](https://redirect.github.com/jdx/mise/pull/13084) by [@​jdx](https://redirect.github.com/jdx))
- **lockfile:** Runtime tool requests such as `mise which hk --tool hk@latest` now use the lockfile belonging to the config that effectively defines the tool, instead of merging project and global pins and reporting a false "multiple resolutions" ambiguity or selecting an overridden pin. `mise which --tool` warns when a lower-precedence config has a matching pin the effective config lacks. ([#​13042](https://redirect.github.com/jdx/mise/pull/13042) by [@​nettlesh](https://redirect.github.com/nettlesh))
- **lockfile:** Complete lockfile generation for Packslip tools skips platforms with no published artifact while still writing supported targets, and a verified Packslip signer may now replace legacy `github-attestations` metadata on upgrade instead of being rejected as a provenance downgrade. ([#​13102](https://redirect.github.com/jdx/mise/pull/13102), [#​13105](https://redirect.github.com/jdx/mise/pull/13105) by [@​jdx](https://redirect.github.com/jdx))
- **upgrade:** `mise upgrade` now detects updates between letter-suffixed versions such as tmux `3.7b` to `3.7c`; `sub-N` aliases keep resolving numeric components as before. ([#​13119](https://redirect.github.com/jdx/mise/pull/13119) by [@​jdx](https://redirect.github.com/jdx))
- **brew:** Formulae that are keg-only solely because macOS ships them (such as `brew:zip` and `brew:unzip`) are now linked into `<prefix>/bin` on Linux, matching Homebrew. Kegs installed by earlier mise versions stay unlinked until the next `mise bootstrap packages upgrade` or a reinstall. ([#​13108](https://redirect.github.com/jdx/mise/pull/13108) by [@​lil-lon](https://redirect.github.com/lil-lon))
- **brew-cask:** Tap casks with `preflight_steps` or `postflight_steps` no longer fail during metadata extraction; declarative `run` steps are captured as structured steps and executed by mise, with support for `must_succeed = false`. ([#​13060](https://redirect.github.com/jdx/mise/pull/13060) by [@​jdx](https://redirect.github.com/jdx))
- **bootstrap:** `mise bootstrap remote` on Windows now finds `ssh.exe` and `tar.exe` on PATH instead of failing with `required command 'ssh' not found`. ([#​13117](https://redirect.github.com/jdx/mise/pull/13117) by [@​JamBalaya56562](https://redirect.github.com/JamBalaya56562))
- **bootstrap:** macOS defaults status explains type mismatches, showing for example `2 (real; expected integer)` instead of two identical-looking values marked `differs`. ([#​13096](https://redirect.github.com/jdx/mise/pull/13096) by [@​jdx](https://redirect.github.com/jdx))
- **dotfiles:** `mise bootstrap dotfiles track` honors the global `yes` setting for confirmations, and warns when tracking a symlink whose resolved source is not itself tracked, suggesting the command to enroll it. ([#​13072](https://redirect.github.com/jdx/mise/pull/13072) by [@​nettlesh](https://redirect.github.com/nettlesh), [#​13095](https://redirect.github.com/jdx/mise/pull/13095) by [@​jdx](https://redirect.github.com/jdx))
- **schema:** The JSON schema now models Git directory `manifest` dotfile entries, restricting explicit modes to `copy` or `symlink-each` and rejecting combinations with inline content or file-edit fields. ([#​12741](https://redirect.github.com/jdx/mise/pull/12741) by [@​risu729](https://redirect.github.com/risu729))
- **asdf:** `mise asdf install` and `mise asdf reshim` no longer re-enter the full CLI dispatch, avoiding stack overflows on small-stack Linux environments; `asdf install` now follows the same implicit config trust as `mise install`. Bash completions are regenerated for the updated usage-rs word-break handling. ([#​13114](https://redirect.github.com/jdx/mise/pull/13114) by [@​jdx](https://redirect.github.com/jdx))
#### Registry
- Added `mpv` (`conda:mpv`, Linux and macOS) ([#​13049](https://redirect.github.com/jdx/mise/pull/13049) by [@​i-api](https://redirect.github.com/i-api)), `agent-browser` (`aqua:vercel-labs/agent-browser`) ([#​13088](https://redirect.github.com/jdx/mise/pull/13088) by [@​3w36zj6](https://redirect.github.com/3w36zj6)), and `himalaya` (`github:pimalaya/himalaya`) ([#​13091](https://redirect.github.com/jdx/mise/pull/13091) by [@​i-api](https://redirect.github.com/i-api)).
- `editorconfig-checker` installs again after 4.0.1 renamed its assets and executable; the shorthand now uses the GitHub backend. ([#​13098](https://redirect.github.com/jdx/mise/pull/13098) by [@​jdx](https://redirect.github.com/jdx))
- `mc` installs from MinIO's GitHub releases instead of the retired Aqua download URL that returned HTTP 410. ([#​13113](https://redirect.github.com/jdx/mise/pull/13113) by [@​jdx](https://redirect.github.com/jdx))
#### Documentation
- The docs landing page gains interactive diagrams for project environment switching, bootstrap machine resources, tool configuration precedence, artifact cache execution, and tracked dotfile synchronization. ([#​13065](https://redirect.github.com/jdx/mise/pull/13065), [#​13066](https://redirect.github.com/jdx/mise/pull/13066), [#​13067](https://redirect.github.com/jdx/mise/pull/13067), [#​13068](https://redirect.github.com/jdx/mise/pull/13068), [#​13069](https://redirect.github.com/jdx/mise/pull/13069) by [@​jdx](https://redirect.github.com/jdx))
- The task-running guide is reorganized around finding and running tasks, passing arguments, then controlling execution. ([#​13077](https://redirect.github.com/jdx/mise/pull/13077) by [@​azohra](https://redirect.github.com/azohra))
#### New Contributors
- [@​lil-lon](https://redirect.github.com/lil-lon) made their first contribution in [#​13108](https://redirect.github.com/jdx/mise/pull/13108)
**Full Changelog**: <https://github.com/jdx/mise/compare/v2026.9.5...v2026.9.6>
#### 💚 Sponsor mise
mise is built and maintained by [@​jdx](https://redirect.github.com/jdx), an open source developer at [**entire.io**](https://entire.io/), the title sponsor of his open source work.
If mise saves you or your team time, please consider becoming an [individual or company sponsor](https://jdx.dev/sponsors.html). Your support funds ongoing development and helps keep mise fast, free, and independent.
### [`v2026.9.5`](https://redirect.github.com/jdx/mise/releases/tag/v2026.9.5): : macOS defaults, per-platform dotfiles, and complete lockfiles
[Compare Source](https://redirect.github.com/jdx/mise/compare/vfox-v2026.9.4...vfox-v2026.9.5)
This release deepens macOS bootstrap support with current-host and nested defaults, lets dotfiles and tasks adapt to the platform and to parsed arguments, and adds an opt-in trial of complete lockfile generation. It also carries a batch of install progress, self-update, brew-cask, and sandbox fixes.
#### Added
- **bootstrap:** New `[[bootstrap.macos.defaults_entries]]` blocks let you set macOS preferences explicitly with `domain`, `key`, `value`, and an optional `host` (`any` by default, or `current`), covering preferences normally written via `defaults -currentHost` while keeping the existing `[bootstrap.macos.defaults]` shorthand. Entries also accept an optional `path` to patch a nested dictionary value without replacing its siblings, preserving property-list types and creating missing parents. ([#​12983](https://redirect.github.com/jdx/mise/pull/12983) by [@​azohra](https://redirect.github.com/azohra), [#​12984](https://redirect.github.com/jdx/mise/pull/12984) by [@​azohra](https://redirect.github.com/azohra))
```toml
[[bootstrap.macos.defaults_entries]]
domain = "com.apple.dock"
key = "autohide"
value = true
host = "current"
```
- **bootstrap:** More friendly macOS preferences: Finder folder sorting and default cloud save location, Dock autohide delay and timing (integers or floats), and keyboard automatic capitalization and spelling correction, all using snake\_case names consistent with the existing sections. ([#​13032](https://redirect.github.com/jdx/mise/pull/13032) by [@​jdx](https://redirect.github.com/jdx))
- **bootstrap:** `[bootstrap.files]` and `[bootstrap.directories]` entries gain `phase = "pre-packages"` so repository definitions, apt sources, and signing keys can be applied before package installation instead of only afterward. Existing declarations default to `"post-packages"`. ([#​13052](https://redirect.github.com/jdx/mise/pull/13052) by [@​jdx](https://redirect.github.com/jdx))
```toml
[bootstrap.files."/etc/apt/sources.list.d/vendor.sources"]
source = "./files/vendor.sources"
phase = "pre-packages"
```
- **bootstrap:** Ordinary `[bootstrap.files]` templates can now reference resolved `[vars]` values, alongside the existing `config_root`, `target`, and `secret()` helpers. ([#​13033](https://redirect.github.com/jdx/mise/pull/13033) by [@​nettlesh](https://redirect.github.com/nettlesh))
- **dotfiles:** A single dotfiles source can deploy to different destinations per operating system, architecture, or mise profile using `variants` with an optional `target`. This works for `copy`, `symlink`, `symlink-each`, and `template` modes. ([#​13050](https://redirect.github.com/jdx/mise/pull/13050) by [@​jdx](https://redirect.github.com/jdx))
```toml
[dotfiles.settings]
source = "dotfiles/vscode/settings.json"
mode = "copy"
variants = [
{ os = "macos", target = "~/Library/Application Support/Code/User/settings.json" },
{ os = "linux", target = "~/.config/Code/User/settings.json" },
]
```
- **task:** Task `sources` and `outputs` can now use `{{usage.*}}` templates, resolved per invocation from parsed arguments and flags before freshness and artifact-cache checks run, so different argument values track freshness independently. ([#​13051](https://redirect.github.com/jdx/mise/pull/13051) by [@​jdx](https://redirect.github.com/jdx))
- **brew-cask:** Casks with structured `set_permissions` preflight/postflight steps now install correctly (for example `brew-cask:blender`), running an unprivileged `chmod` over resolved staged or appdir paths instead of failing with an unsupported step-type error. ([#​13043](https://redirect.github.com/jdx/mise/pull/13043) by [@​azohra](https://redirect.github.com/azohra))
- **lock:** Opt-in trial of complete lockfile generation via `lockfile_mode = "generate"` (or `MISE_LOCKFILE_MODE=generate`). The default remains incremental `merge`. Generate mode rebuilds lockfiles from current requests while treating the previous file as an immutable baseline, reusing unchanged artifacts and publishing through staged atomic writes so failures or concurrent edits do not clobber a good lockfile. This mode records only cryptographically verified provenance per target platform; `provenance_verified` is no longer treated as a trust signal. ([#​13031](https://redirect.github.com/jdx/mise/pull/13031) by [@​jdx](https://redirect.github.com/jdx))
- **self-update:** New `disable_update_warning` setting (`MISE_DISABLE_UPDATE_WARNING`) suppresses "newer mise available" notices in `mise version`, `mise --version`, and `mise doctor`. Explicit self-update and automatic updates are unaffected. ([#​13028](https://redirect.github.com/jdx/mise/pull/13028) by [@​jdx](https://redirect.github.com/jdx))
#### Fixed
- **install:** Interactive installs no longer leave a permanent line for every resolved, skipped, or already-installed tool; live progress shows what is happening while the final summary lists what changed (for example `installed 1 tool in 1.1s: [email protected]`). `mise upgrade` no longer duplicates its old to new version list. ([#​13030](https://redirect.github.com/jdx/mise/pull/13030) by [@​jdx](https://redirect.github.com/jdx))
- **install:** Long non-TTY installs no longer flood CI logs with a snapshot every three seconds. The heartbeat now scales to roughly 10% of elapsed time, clamped between 3 seconds and 1 minute. ([#​13036](https://redirect.github.com/jdx/mise/pull/13036) by [@​jdx](https://redirect.github.com/jdx))
- **self-update:** On 32-bit ARM, `mise self-update` now selects the correct `linux-armv7` archive instead of requesting a missing `linux-arm` one and falling back to an ARM64 binary that failed signature verification. A missing archive now fails asset selection rather than picking the wrong architecture. ([#​13023](https://redirect.github.com/jdx/mise/pull/13023) by [@​jdx](https://redirect.github.com/jdx))
- **self-update:** npm installs now ship the instructions file that redirects update guidance to the package manager, so they no longer advertise `mise self-update`. ([#​13028](https://redirect.github.com/jdx/mise/pull/13028) by [@​jdx](https://redirect.github.com/jdx))
- **brew-cask:** `mise bootstrap packages upgrade` no longer replaces the bundle of a running self-updating app (for example Chrome), which could strand helper processes and blank out tabs. Such apps are skipped while running and left to update themselves. ([#​13041](https://redirect.github.com/jdx/mise/pull/13041) by [@​azohra](https://redirect.github.com/azohra))
- **brew-cask:** The cask metadata fetch error no longer includes stale advice about installing with `brew` and a broken documentation anchor; it now gives a short, accurate message. ([#​12800](https://redirect.github.com/jdx/mise/pull/12800) by [@​Marukome0743](https://redirect.github.com/Marukome0743))
- **bootstrap:** `mise bootstrap dotfiles origin set <url>` now uses the repository's own default branch when `--branch` is omitted, so repositories on `master` connect correctly instead of publishing a second root branch. A missing requested branch is now reported clearly rather than mistaken for an empty repository. ([#​13037](https://redirect.github.com/jdx/mise/pull/13037) by [@​Dhaulagiri](https://redirect.github.com/Dhaulagiri))
- **dotfiles:** History watcher locks now live alongside the history store in the state directory instead of being hashed into the cache directory, so a launchd watcher and an interactive shell using different cache directories coordinate correctly and `dotfiles status` no longer misreports `declared-not-running`. Existing watchers must be stopped and restarted with the updated binary. ([#​13038](https://redirect.github.com/jdx/mise/pull/13038) by [@​ascarter](https://redirect.github.com/ascarter))
- **sandbox:** The macOS Seatbelt profile now allows `file-read-metadata` on the ancestors of readable paths, fixing `Operation not permitted` failures when a portable Ruby resolves its own executable during third-party tap evaluation. Symlinked data directories and allow-listed paths are also handled. ([#​13039](https://redirect.github.com/jdx/mise/pull/13039) by [@​Marukome0743](https://redirect.github.com/Marukome0743))
#### Registry
- Added `clipboard` ([github:Slackadays/Clipboard](https://redirect.github.com/Slackadays/Clipboard)) by [@​i-api](https://redirect.github.com/i-api) in [#​13044](https://redirect.github.com/jdx/mise/pull/13044)
#### New Contributors
- [@​Dhaulagiri](https://redirect.github.com/Dhaulagiri) made their first contribution in [#​13037](https://redirect.github.com/jdx/mise/pull/13037)
**Full Changelog**: <https://github.com/jdx/mise/compare/v2026.9.4...v2026.9.5>
#### 💚 Sponsor mise
mise is built and maintained by [@​jdx](https://redirect.github.com/jdx), an open source developer at [**entire.io**](https://entire.io/), the title sponsor of his open source work.
If mise saves you or your team time, please consider becoming an [individual or company sponsor](https://jdx.dev/sponsors.html). Your support funds ongoing development and helps keep mise fast, free, and independent.
### [`v2026.9.4`](https://redirect.github.com/jdx/mise/releases/tag/v2026.9.4): : Nix Bootstrap, Environment Selectors, and Man Pages
[Compare Source](https://redirect.github.com/jdx/mise/compare/vfox-v2026.9.3...vfox-v2026.9.4)
This release expands the bootstrap package model with Nix support and environment selectors, teaches packslip tools to ship man pages, and adds a task-scoped quiet setting. It also carries a wide batch of packslip, bootstrap, and cross-platform fixes plus a major speedup to dotfiles history rebuilds.
#### Added
- **bootstrap:** Nix is now a built-in `[bootstrap.packages]` manager on Linux and macOS. Declare packages with the `nix:` prefix and apply them through your normal Nix profile; the manager supports use, apply, status JSON, and targeted upgrades while leaving Nix sources, caches, trust, and profile rollback under Nix control. This also adds `mise bootstrap packages export --format nix` to emit a deterministic NixOS module from your `nix:` declarations and `packages use --no-install` to record declarations without touching package managers. ([#​13013](https://redirect.github.com/jdx/mise/pull/13013) by [@​jdx](https://redirect.github.com/jdx))
```toml
[bootstrap.packages]
"nix:ripgrep" = "latest"
```
- **bootstrap:** `[bootstrap.packages]` entries gain an `env` selector (a single environment or a list), so a package is only enabled when one of its listed mise environments is active via `-E` or `MISE_ENV`. When both `os` and `env` are set, both must match. Packages for inactive environments stay declared and are protected from pruning. ([#​12956](https://redirect.github.com/jdx/mise/pull/12956) by [@​jdx](https://redirect.github.com/jdx))
```toml
[bootstrap.packages]
"brew:postgresql" = { version = "latest", env = ["dev", "test"] }
```
- **packslip:** Packslip-installed tools can now ship man pages declared as static `man` resources, alongside completions and agent skills. While a tool version is active, mise prepends its man root to `MANPATH` and keeps system and caller-defined paths visible, so `man <tool>` works. Existing installs need to be reinstalled to pick up man pages. ([#​13012](https://redirect.github.com/jdx/mise/pull/13012) by [@​jdx](https://redirect.github.com/jdx))
- **task:** New `task.quiet` setting (and `MISE_TASK_QUIET`) suppresses mise's own task messages, prefixes, and command-echo headers without hiding task output or affecting other commands. The bundled `output = "quiet"` mode is deprecated in favor of explicit output style plus this setting; removal is scheduled for 2027.9.3. ([#​12980](https://redirect.github.com/jdx/mise/pull/12980) by [@​jdx](https://redirect.github.com/jdx))
#### Fixed
- **install:** A lazy tool whose `depends` target is also lazy now installs correctly on first use through a shim, `mise x`, or a task; mise installs the provider together with its still-missing configured dependencies instead of failing the preflight. ([#​12997](https://redirect.github.com/jdx/mise/pull/12997) by [@​balintant](https://redirect.github.com/balintant))
- **backend:** 32-bit ARM resolution now uses Go's canonical `arm` architecture name for Aqua (so registry replacements apply), and automatic GitHub release asset selection no longer picks a generic `source.tar.gz` when no published binary targets the host. ([#​13004](https://redirect.github.com/jdx/mise/pull/13004) by [@​jdx](https://redirect.github.com/jdx))
- **bootstrap:** Homebrew bottles with hard-linked Mach-O executables are now signed correctly on macOS. Every hard-link alias is tracked and included in the signing list after relocation, fixing cases like fish where an aliased binary was killed with SIGKILL after a successful install. ([#​12988](https://redirect.github.com/jdx/mise/pull/12988) by [@​nettlesh](https://redirect.github.com/nettlesh))
- **packslip:** `mise lock --platform` now verifies the signed release manifest and records the correct URL, checksum, size, and signer for each requested target platform (including Windows x64), so locked installs work across platforms. ([#​13002](https://redirect.github.com/jdx/mise/pull/13002) by [@​jdx](https://redirect.github.com/jdx))
- **packslip:** On glibc Linux, an artifact's `glibc_min` is now honored during selection: when the GNU build requires a newer glibc than the host, mise falls back to a matching static musl build if one exists. ([#​13009](https://redirect.github.com/jdx/mise/pull/13009) by [@​jdx](https://redirect.github.com/jdx))
- **packslip:** Windows installs now link shims with the correct `.exe` filename, while Unix keeps extensionless names. ([#​13006](https://redirect.github.com/jdx/mise/pull/13006) by [@​jdx](https://redirect.github.com/jdx))
- **packslip:** Activated man roots are now scoped to Packslip-backed tool versions, and the caller's original `MANPATH` is included in the environment-cache identity so one cached process cannot serve another caller's `MANPATH`. ([#​13016](https://redirect.github.com/jdx/mise/pull/13016) by [@​jdx](https://redirect.github.com/jdx))
- **prune:** `mise prune` no longer exits successfully without pruning when it cannot show a confirmation prompt; it now fails with guidance to pass `--yes` or set `MISE_YES=1`. Follow-up prunes from commands like `mise unuse` still leave installs in place when nobody can answer. ([#​13003](https://redirect.github.com/jdx/mise/pull/13003) by [@​jdx](https://redirect.github.com/jdx))
- **generate:** `mise generate tool-stub` now respects a stub's top-level `os` selector when deciding whether to write a Windows `.cmd` launcher, so a linux/macos-only stub no longer produces a launcher even with `--lock`. ([#​13008](https://redirect.github.com/jdx/mise/pull/13008) by [@​jdx](https://redirect.github.com/jdx))
- **dotfiles:** History path filters resolve correctly when `HOME` is a symlink, so aliased, canonical, and tilde paths address the same history entry and `show latest`/`diff` no longer report a missing checkpoint. ([#​12982](https://redirect.github.com/jdx/mise/pull/12982) by [@​azohra](https://redirect.github.com/azohra))
- **brew:** The published crate now includes the tap formula and cask metadata shims, fixing `cargo install --locked mise` builds from crates.io. ([#​13001](https://redirect.github.com/jdx/mise/pull/13001) by [@​jdx](https://redirect.github.com/jdx))
#### Changed
- **bootstrap:** `[bootstrap].config_roots` and the `mise bootstrap config-roots` command are now deprecated (hidden from help and removed from public docs), with removal scheduled for mise 2027.3.3. Existing configurations keep working during the compatibility window but emit a warning directing users to global or system bootstrap configuration. ([#​13010](https://redirect.github.com/jdx/mise/pull/13010) by [@​jdx](https://redirect.github.com/jdx))
- **registry:** Added basecamp to the registry. ([#​13024](https://redirect.github.com/jdx/mise/pull/13024))
#### Performance
- **history:** Dotfiles history index rebuilds are dramatically faster, reconstructing checkpoint metadata in-process with gix instead of spawning Git per checkpoint. On an 80-checkpoint, 44-file reproducer this cut a rebuild from roughly 26-28 seconds and thousands of Git processes down to under a second. Removed annotations are now correctly dropped after a rebuild, and SHA-256 history repositories are supported. ([#​13011](https://redirect.github.com/jdx/mise/pull/13011) by [@​jdx](https://redirect.github.com/jdx))
#### Deprecated
- The task `output = "quiet"` mode (use `task.quiet` instead; removal in 2027.9.3) ([#​12980](https://redirect.github.com/jdx/mise/pull/12980)).
- `[bootstrap].config_roots` and `mise bootstrap config-roots` (removal in mise 2027.3.3) ([#​13010](https://redirect.github.com/jdx/mise/pull/13010)).
#### New Contributors
- [@​balintant](https://redirect.github.com/balintant) made their first contribution in [#​12997](https://redirect.github.com/jdx/mise/pull/12997)
**Full Changelog**: <https://github.com/jdx/mise/compare/v2026.9.3...v2026.9.4>
#### 💚 Sponsor mise
mise is built and maintained by [@​jdx](https://redirect.github.com/jdx), an open source developer at [**entire.io**](https://entire.io/), the title sponsor of his open source work.
If mise saves you or your team time, please consider becoming an [individual or company sponsor](https://jdx.dev/sponsors.html). Your support funds ongoing development and helps keep mise fast, free, and independent.
### [`v2026.9.3`](https://redirect.github.com/jdx/mise/releases/tag/v2026.9.3): : Winget bootstrap, signed vfox plugins, and faster inline commands
[Compare Source](https://redirect.github.com/jdx/mise/compare/vfox-v2026.9.2...vfox-v2026.9.3)
This release brings Windows bootstrap into the package-manager era with WinGet support, extends the packslip signing model to vfox plugins, and speeds up simple tasks by skipping the shell on Unix. It also carries a batch of bootstrap, sandbox, and Windows ARM64 fixes.
##### Added
- **bootstrap:** WinGet is now a built-in `[bootstrap.packages]` manager on Windows, so `mise bootstrap packages` can check status, install, and upgrade apps by exact package ID. It supports version pins, source refresh, and automatic source-agreement acceptance. Scoop, Chocolatey, and package removal remain future work. ([#​12928](https://redirect.github.com/jdx/mise/pull/12928) by [@​jdx](https://redirect.github.com/jdx))
```toml
[bootstrap.packages]
"winget:Microsoft.PowerShell" = "latest"
```
- **vfox:** External vfox plugins can now be installed from signed packslip archives instead of a Git clone, reusing the packslip backend's release selection, signature verification, digest checks, and signer pins. This is aimed at non-registry plugins; registry defaults and embedded plugins are unchanged. ([#​12948](https://redirect.github.com/jdx/mise/pull/12948) by [@​jdx](https://redirect.github.com/jdx))
```sh
mise plugins install vfox:bfs 'packslip:mise-plugins/vfox-bfs#0.1.0'
```
- **bootstrap:** Dotfile line edits gain `position = "prepend"` to insert a managed line at the top of a file instead of appending (the default). An existing exact match is left in place, and unrelated bytes, CRLF endings, and a UTF-8 BOM are preserved. ([#​12941](https://redirect.github.com/jdx/mise/pull/12941) by [@​jdx](https://redirect.github.com/jdx))
```toml
[dotfiles]
"/etc/zshrc/zdotdir" = { line = 'ZDOTDIR=$HOME/.config/zsh/', position = "prepend" }
```
- **bootstrap:** macOS bootstrap `defaults` now support nested TOML arrays and tables (for example Dock `persistent-apps`), reading and writing preferences through Core Foundation so booleans, numbers, strings, arrays, and dictionaries keep their plist types. Collections apply as whole-value replacements; datetimes and binary plist data are still skipped with a warning. ([#​12947](https://redirect.github.com/jdx/mise/pull/12947) by [@​jdx](https://redirect.github.com/jdx))
##### Changed
- **bootstrap:** Adopting global configuration or a shared dotfile-history setup repository is now spelled `mise bootstrap --adopt` (and `mise bootstrap remote --adopt`), separate from `mise bootstrap --from` for running a bootstrap project. The dotfiles, history, setup, and services guides were reorganized to lead with usable examples. The previous `--from-git` spelling still works as a hidden alias but warns and is scheduled for removal in mise 2026.10.0. ([#​12953](https://redirect.github.com/jdx/mise/pull/12953) by [@​jdx](https://redirect.github.com/jdx))
##### Fixed
- **github:** Git operations (clones, fetches, submodules) that GitHub now challenges with HTTP 401 on public repositories can authenticate using mise's existing GitHub token sources via a command-scoped `github.com` HTTPS credential helper. Tokens are kept out of command arguments and URLs, and Enterprise hosts are out of scope. ([#​12945](https://redirect.github.com/jdx/mise/pull/12945) by [@​jdx](https://redirect.github.com/jdx))
- **config:** The default inline task shell is now `sh -o errexit -c` so shells that consume the argument right after `-c` (such as FreeBSD sh) run your command instead of trying to execute `-o`. ([#​12949](https://redirect.github.com/jdx/mise/pull/12949) by [@​jdx](https://redirect.github.com/jdx))
- **windows:** Fixed two Windows ARM64 issues: stale command-wrapper shims (like a leftover `cargo.exe`) that caused recursive `.exe`/`.cmd` dispatch after shim-mode changes are now pruned, and Aqua ARM64 emulation candidates apply the registry's `amd64` replacement so tools like LuaLS resolve their published `win32-x64` assets. ([#​12931](https://redirect.github.com/jdx/mise/pull/12931) by [@​jdx](https://redirect.github.com/jdx))
- **bootstrap:** Bootstrap no longer aborts on Arch when `pacman -Q` prints a file advisory for a missing package that shares its name with a directory in the current working directory (for example a `fish/` folder in a dotfiles checkout). ([#​12932](https://redirect.github.com/jdx/mise/pull/12932) by [@​nettlesh](https://redirect.github.com/nettlesh))
- **sandbox:** Read-restricted macOS Seatbelt profiles now allow metadata-only access to `/private`, fixing startup failures for load-relative binaries like Ruby 4.0.6 when run under `/private/tmp`. Directory listings and descendant reads stay denied. ([#​12940](https://redirect.github.com/jdx/mise/pull/12940) by [@​jdx](https://redirect.github.com/jdx))
- **bootstrap:** The macOS notification helper is now built and Developer ID-signed at release time and spawned with a start handshake, so short `dotfiles sync` runs no longer race the helper and macOS accepts it. Unsigned source builds (such as Homebrew) disable notifications and warn during origin setup. ([#​12946](https://redirect.github.com/jdx/mise/pull/12946) by [@​jdx](https://redirect.github.com/jdx))
- **vfox:** vfox install phases now flow through the active install progress reporter, mapping noisy download, checksum, attestation, and extraction lines into concise phases instead of scrolling log output. ([#​12944](https://redirect.github.com/jdx/mise/pull/12944) by [@​jdx](https://redirect.github.com/jdx))
- **java:** The `java -version` banner shown during install verification now stays inside the interactive install progress row instead of printing to the terminal. ([#​12943](https://redirect.github.com/jdx/mise/pull/12943) by [@​jdx](https://redirect.github.com/jdx))
- **version:** The auto-update hint from `mise version` now uses the `mise settings auto_update=true` syntax to match the rest of `mise settings`. ([#​12957](https://redirect.github.com/jdx/mise/pull/12957) by [@​jdx](https://redirect.github.com/jdx))
##### Performance
- On Unix, plain inline commands (like `node build.js`) now run directly when a conservative planner deems them safe, skipping the shell wrapper. Anything involving shell syntax, quoting, expansion, builtins, ambiguous PATH lookup, explicit shell settings, or sandboxed/audited tasks still uses the shell, and Windows is unchanged. In a microbenchmark of short commands this cut about 11% off elapsed time. ([#​12950](https://redirect.github.com/jdx/mise/pull/12950) by [@​jdx](https://redirect.github.com/jdx))
##### Registry
- Added cargo-deny, kingfisher, nushell, shellharden, sherif, and tauri-cli. ([#​12938](https://redirect.github.com/jdx/mise/pull/12938) by [@​jrandolf](https://redirect.github.com/jrandolf))
##### Documentation
- Restored the prominent project name and pronunciation on the homepage. ([#​12936](https://redirect.github.com/jdx/mise/pull/12936) by [@​jdx](https://redirect.github.com/jdx))
- Dropped the "you need Git installed" prerequisite from the install docs. ([#​12958](https://redirect.github.com/jdx/mise/pull/12958) by [@​jdx](https://redirect.github.com/jdx))
**Full Changelog**: <https://github.com/jdx/mise/compare/v2026.9.2...v2026.9.3>
##### 💚 Sponsor mise
mise is built and maintained by [@​jdx](https://redirect.github.com/jdx), an open source developer at [**entire.io**](https://entire.io/), the title sponsor of his open source work.
If mise saves you or your team time, please consider becoming an [individual or company sponsor](https://jdx.dev/sponsors.html). Your support funds ongoing development and helps keep mise fast, free, and independent.
</details>
---
### Configuration
📅 **Schedule**: (UTC)
- Branch creation
- "before 9am on monday"
- Automerge
- At any time (no schedule defined)
🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.
♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
🔕 **Ignore**: Close this PR and you won't be reminded about this update again.
---
- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box
---
This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/expo/entity).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC43OS4xIiwidXBkYXRlZEluVmVyIjoiNDQuNzkuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->
This PR contains the following updates:
| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [@jest/globals](https://redirect.github.com/jestjs/jest) ([source](https://redirect.github.com/jestjs/jest/tree/HEAD/packages/jest-globals)) | [`30.4.1` → `30.5.1`](https://renovatebot.com/diffs/npm/@jest%2fglobals/30.4.1/30.5.1) |  |  |
| [jest](https://jestjs.io/) ([source](https://redirect.github.com/jestjs/jest/tree/HEAD/packages/jest)) | [`30.4.2` → `30.5.1`](https://renovatebot.com/diffs/npm/jest/30.4.2/30.5.1) |  |  |
---
### Release Notes
<details>
<summary>jestjs/jest (@​jest/globals)</summary>
### [`v30.5.1`](https://redirect.github.com/jestjs/jest/blob/HEAD/CHANGELOG.md#3051)
[Compare Source](https://redirect.github.com/jestjs/jest/compare/v30.5.0...v30.5.1)
##### Fixes
- `[jest-config]` Don't warn about global-only options in the config that supplies the global config - the root config a project resolves to, or the first entry of `--projects` when no root config is passed ([#​16411](https://redirect.github.com/jestjs/jest/pull/16411))
- `[jest-config, jest-types]` Stop accepting `reporters`, `coverageReporters`, `workerIdleMemoryLimit`, `cwd` and `runnerOptions` in a project config - they were silently ignored, and now warn like the other global-only options ([#​16411](https://redirect.github.com/jestjs/jest/pull/16411))
- `[jest-config, jest-validate]` Warn about `maxWorkers` and `coverageThreshold` in a project config instead of dropping them without a word ([#​16411](https://redirect.github.com/jestjs/jest/pull/16411))
- `[jest-resolve]` Match `moduleNameMapper` patterns against the specifier as written again (reverting [#​16390](https://redirect.github.com/jestjs/jest/pull/16390)) ([#​16417](https://redirect.github.com/jestjs/jest/pull/16417))
- `[jest-runtime]` Resolve package `imports` specifiers like `#dep` under ESM again ([#​16413](https://redirect.github.com/jestjs/jest/pull/16413))
##### Chore & Maintenance
- `[jest-util]` Name the `testEnvironmentOptions.globalsCleanup` option and link the docs from the `JEST-01` deprecation warning, and document the option's modes ([#​16404](https://redirect.github.com/jestjs/jest/pull/16404))
### [`v30.5.0`](https://redirect.github.com/jestjs/jest/blob/HEAD/CHANGELOG.md#3050)
[Compare Source](https://redirect.github.com/jestjs/jest/compare/v30.4.1...v30.5.0)
##### Features
- `[@jest/expect-utils, jest-mock]` Add `mockFn.whenCalledWith(...args)` for configuring return values per argument list, with first-class asymmetric-matcher support ([#​16053](https://redirect.github.com/jestjs/jest/pull/16053))
- `[@jest/expect-utils]` Export `AsymmetricMatcher` and `FunctionParameters` types (previously private to `expect`) ([#​16053](https://redirect.github.com/jestjs/jest/pull/16053))
- `[jest-circus, jest-core, jest-jasmine2, jest-test-result, jest-types]` `--collectTests` now expands `test.each`/`describe.each` cases and reports per-status counts (skipped/todo via the new `wouldRun` flag for selected tests) plus a summary line that match a real run, including under `--testNamePattern` and `.only`/`fdescribe` focus on both the circus and jasmine2 runners ([#​16259](https://redirect.github.com/jestjs/jest/pull/16259))
- `[jest-circus, jest-environment, jest-runtime, jest-types]` Add describe-level retries via `jest.retryTimes(..., {entireDescribe: true})` ([#​16322](https://redirect.github.com/jestjs/jest/pull/16322))
- `[jest-circus, jest-message-util, jest-reporters, jest-types]` Add `retryMessages` to `AssertionResult` and export `formatErrorStack`, so the retry log renders nested `cause` and `AggregateError` sections with code frames instead of serialized `[cause]:`/`[errors]:` markers ([#​16316](https://redirect.github.com/jestjs/jest/pull/16316))
- `[jest-circus, jest-types]` Add `unhandledErrorsDetailed` to `Circus.RunResult`, so an unhandled rejection reports its `cause` chain and `AggregateError` entries with code frames instead of a pre-serialized stack ([#​16316](https://redirect.github.com/jestjs/jest/pull/16316))
- `[jest-haste-map]` Replace `NodeWatcher` and `FSEventsWatcher` with `@parcel/watcher` for the non-watchman watch path ([#​16188](https://redirect.github.com/jestjs/jest/pull/16188))
- `[jest-resolve]` Bump `unrs-resolver` to 1.12.1, remove `jest-pnp-resolver` and unnecessary checks ([#​15721](https://redirect.github.com/jestjs/jest/pull/15721))
- `[jest-resolve]` Honor Node's `--preserve-symlinks` / `NODE_PRESERVE_SYMLINKS` in the default resolver by passing `symlinks: false` to `unrs-resolver` ([#​16260](https://redirect.github.com/jestjs/jest/pull/16260))
- `[jest-runtime]` Apply automocking and manual `__mocks__` files to synchronously evaluable ESM graphs on Node 24.9+ - static imports, dynamic `import()` and `require()` of an ESM file now generate an automock from the real module's namespace instead of failing with "Attempting to import a mock without a factory". Graphs that need async evaluation (top-level await) or an async-only resolver or transformer still throw ([#​16391](https://redirect.github.com/jestjs/jest/pull/16391))
- `[jest-runtime]` Route `process.getBuiltinModule` through the sandbox, so it returns the sandbox `process` and the hooked `node:module` instead of the host's ([#​16391](https://redirect.github.com/jestjs/jest/pull/16391))
- `[jest-runtime]` Throw an actionable error from `module.register()` and `module.registerHooks()` inside a test - the hooks attached to the loader running Jest itself, never saw the sandboxed requires they were meant for, and stayed registered for every later test file in the worker ([#​16391](https://redirect.github.com/jestjs/jest/pull/16391))
- `[jest-runtime]` Surface resolution and import-attribute errors in an ESM graph before executing any of its CJS dependencies on Node 24.9+, matching Node's run-nothing-on-a-broken-graph behavior; the legacy loader on older versions keeps its linking-time execution order ([#​16391](https://redirect.github.com/jestjs/jest/pull/16391))
- `[jest-runtime]` Throw `ERR_SOURCE_PHASE_NOT_DEFINED` with an actionable message for `import source` and `import.source()`, instead of failing at instantiation with V8's bare "Source phase import object is not defined" ([#​16391](https://redirect.github.com/jestjs/jest/pull/16391))
- `[jest-runtime]` Emit the JSON-without-import-attribute deprecation warning once per test file instead of once per worker, so it is no longer silently swallowed for every file after the first ([#​16391](https://redirect.github.com/jestjs/jest/pull/16391))
- `[jest-runtime]` Set `import.meta.main` to `true` in the test file and `false` in every module it loads, matching Node 24+ ([#​16367](https://redirect.github.com/jestjs/jest/pull/16367))
- `[jest-runtime]` Resolve the `module-sync` export condition, so a package that exposes its ESM entry point for `require()` loads the same file Node would ([#​16336](https://redirect.github.com/jestjs/jest/pull/16336))
- `[jest-snapshot]` Add external snapshot paths to custom reporter failure details ([#​16374](https://redirect.github.com/jestjs/jest/pull/16374))
##### Fixes
- `[jest-console, jest-reporters]` `CustomConsole` now buffers console output so `TestResult.console` is populated for reporters when `verbose` is enabled, while `GitHubActionsReporter` avoids replaying buffered output in verbose mode ([#​16155](https://redirect.github.com/jestjs/jest/pull/16155))
- `[expect, jest-message-util, jest-pattern, jest-regex-util, jest-util]` Revert `node:` protocol imports to restore webpack/browser-bundle compatibility ([#​16167](https://redirect.github.com/jestjs/jest/pull/16167))
- `[expect]` Widen `toMatchObject` and `objectContaining` parameter type from `Record<string, unknown>` to `object` so class instances are accepted ([#​16196](https://redirect.github.com/jestjs/jest/pull/16196))
- `[jest-circus]` Call a generator test body with the shared test context, so `this` matches what a regular test function receives ([#​16347](https://redirect.github.com/jestjs/jest/pull/16347))
- `[jest-circus]` Capture the error listeners of the parent process instead of the in-sandbox `process`, so listeners registered before the test file survive teardown and sandbox listeners no longer leak onto the parent ([#​16347](https://redirect.github.com/jestjs/jest/pull/16347))
- `[jest-circus]` Clear `currentlyRunningTest` after skipped and todo tests ([#​16342](https://redirect.github.com/jestjs/jest/pull/16342))
- `[jest-circus]` Prevent late `done()` callbacks from affecting later test or hook invocations ([#​16343](https://redirect.github.com/jestjs/jest/pull/16343))
- `[jest-circus, jest-jasmine2]` Honor `--expand` when formatting `node:assert` failures, instead of always collapsing the diff ([#​16347](https://redirect.github.com/jestjs/jest/pull/16347))
- `[jest-circus, jest-jasmine2, jest-message-util]` Serialize the inner errors of an `AggregateError` into `failureMessages`, `retryReasons` and `unhandledErrors`, so `--json` output and reporter annotations include them ([#​16316](https://redirect.github.com/jestjs/jest/pull/16316))
- `[jest-circus, jest-snapshot]` Keep snapshot state and counts correct when a test retries ([#​16344](https://redirect.github.com/jestjs/jest/pull/16344))
- `[@jest/create-cache-key-function]` Include the caller support flags in the generated key, so a transformer that emits ESM or CJS based on them no longer shares one cache entry between the two ([#​16331](https://redirect.github.com/jestjs/jest/pull/16331))
- `[@jest/create-cache-key-function]` Include the stringified project config in the generated key, so editing a transformer's own settings invalidates what it cached ([#​16331](https://redirect.github.com/jestjs/jest/pull/16331))
- `[@jest/transform]` Include the caller support flags in a transform's cache key, so a file transformed both as ESM and as CJS no longer serves one shape's output for the other ([#​16331](https://redirect.github.com/jestjs/jest/pull/16331))
- `[jest-config]` Add missing `findRelatedTests`, `outputFile`, and `replname` entries to `ValidConfig` so they no longer trigger spurious "Unknown option" warnings ([#​16224](https://redirect.github.com/jestjs/jest/pull/16224))
- `[jest-config]` Use `--config` for the global config when multiple `--projects` are specified ([#​16273](https://redirect.github.com/jestjs/jest/pull/16273))
- `[jest-core]` Serialize `bigint` values in `--json` and `--outputFile` output as their literal form (`4n`), instead of failing the run with `TypeError: Do not know how to serialize a BigInt` ([#​16338](https://redirect.github.com/jestjs/jest/pull/16338))
- `[jest-core]` Do not report a `CustomGC` async resource (used by N-API addons such as napi-rs for per-isolate GC bookkeeping) as an open handle, since it is `napi_unref`'d by the addon and can never keep the event loop alive ([#​16379](https://redirect.github.com/jestjs/jest/pull/16379))
- `[jest-each]` Keep a `$&`, `` $` ``, `$'` or `$$` inside a `%p` param value out of the replacement, so the title shows the value instead of the text around it ([#​16338](https://redirect.github.com/jestjs/jest/pull/16338))
- `[jest-each]` Interpolate a `bigint` into a `%j` title as its literal form (`"4n"`) at any depth, instead of throwing `TypeError: Do not know how to serialize a BigInt` while collecting the tests ([#​16338](https://redirect.github.com/jestjs/jest/pull/16338))
- `[jest-environment, jest-runtime]` Bind `sandboxInjectedGlobals` to the right values when `injectGlobals` is `false`, instead of shifting every one of them by a position ([#​16377](https://redirect.github.com/jestjs/jest/pull/16377))
- `[jest-environment-node, jest-util]` Only warn about a conflicting `globalsCleanup` mode when one was explicitly configured, and follow the mode that is actually in effect ([#​16323](https://redirect.github.com/jestjs/jest/pull/16323))
- `[jest-environment-node, jest-util]` Stop resolving lazy globals when setting up an environment, so Node 26's builtin module globals are no longer loaded (and no longer emit their deprecation warnings) for every test file ([#​16324](https://redirect.github.com/jestjs/jest/pull/16324))
- `[jest-haste-map]` Keep watch mode alive when an outside process briefly makes a file unreadable on Windows, instead of tearing the watcher down on `EPERM` ([#​16295](https://redirect.github.com/jestjs/jest/pull/16295))
- `[jest-haste-map]` Keep indexing when an outside process holds a file open on Windows, instead of failing the whole crawl on `EPERM` ([#​16358](https://redirect.github.com/jestjs/jest/pull/16358))
- `[jest-haste-map]` Keep a duplicated manual mock resolving when the file it pointed at is deleted in watch mode ([#​16360](https://redirect.github.com/jestjs/jest/pull/16360))
- `[jest-haste-map]` Shut the worker farm down when a duplicate manual mock aborts the build under `throwOnModuleCollision` ([#​16354](https://redirect.github.com/jestjs/jest/pull/16354))
- `[jest-haste-map]` Attach the watchman client's `error` listener before the first command, so a watchman failure falls back to the node crawler instead of crashing on an unhandled `error` event, and always end the client ([#​16355](https://redirect.github.com/jestjs/jest/pull/16355))
- `[jest-haste-map]` Stop delivering watch events after `WatchmanWatcher` is closed, and route its warnings through the configured console ([#​16355](https://redirect.github.com/jestjs/jest/pull/16355))
- `[jest-haste-map]` Restore the nested `duplicates` index correctly in `ModuleMap.fromJSON`, so a haste collision reported inside a test worker raises `DuplicateHasteCandidatesError` instead of a `TypeError` ([#​16353](https://redirect.github.com/jestjs/jest/pull/16353))
- `[jest-haste-map]` Match watched files on a full extension, so `moduleFileExtensions: ['js']` no longer accepts `foo.mjs` ([#​16352](https://redirect.github.com/jestjs/jest/pull/16352))
- `[jest-haste-map]` Delimit the fields that make up the haste map cache key, so two different option sets cannot hash to the same cache file ([#​16352](https://redirect.github.com/jestjs/jest/pull/16352))
- `[jest-message-util]` Print the inner errors of an `AggregateError` thrown inside a test ([#​16316](https://redirect.github.com/jestjs/jest/pull/16316))
- `[jest-message-util]` Indent nested `cause` and `AggregateError` sections of a test failure by one level per depth, so the nesting is legible instead of rendering flat ([#​16316](https://redirect.github.com/jestjs/jest/pull/16316))
- `[jest-message-util]` Color stack traces line by line so blank lines stay blank ([#​16316](https://redirect.github.com/jestjs/jest/pull/16316))
- `[jest-message-util]` Detect Jest's own frames without assuming the checkout directory's name, and cover `@jest/*` packages, so stack traces and code frames point at user code ([#​16326](https://redirect.github.com/jestjs/jest/pull/16326))
- `[jest-mock]` `mockResolvedValue` / `mockRejectedValue` now see all overload return types, so a Promise-returning overload survives even when a later overload returns a non-Promise (e.g. `pg.Client['end']`) ([#​16237](https://redirect.github.com/jestjs/jest/pull/16237))
- `[@jest-environment/jsdom-abstract]` Make `@types/jsdom` a peer dependency ([#​16166](https://redirect.github.com/jestjs/jest/pull/16166))
- `[jest-mock]` Remove the leftover own accessor descriptor when restoring a `spyOn` of an inherited getter or setter, so the instance keeps reflecting the prototype ([#​16226](https://redirect.github.com/jestjs/jest/pull/16226))
- `[jest-resolve]` Include `extensionsToTreatAsEsm` in the `shouldLoadAsEsm` cache key, so projects with different extension lists don't read each other's answers ([#​16369](https://redirect.github.com/jestjs/jest/pull/16369))
- `[jest-resolve]` Make `getModuleIDAsync` build and cache `data:` URI module IDs the same way as `getModuleID` ([#​16370](https://redirect.github.com/jestjs/jest/pull/16370))
- `[jest-resolve]` Keep the `node:` prefix when resolving a core module asynchronously, so a builtin that only exists prefixed (`node:sea`, `node:sqlite`, `node:test`, `node:test/reporters`) resolves instead of failing as a missing bare package ([#​16388](https://redirect.github.com/jestjs/jest/pull/16388))
- `[jest-resolve]` Look up manual mocks for `node:` protocol specifiers under the unprefixed name they are stored as ([#​16388](https://redirect.github.com/jestjs/jest/pull/16388))
- `[jest-resolve]` Apply `moduleNameMapper` consistently to both spellings of core module specifiers (`fs` vs `node:fs`) ([#​16390](https://redirect.github.com/jestjs/jest/pull/16390))
- `[jest-resolve]` Keep virtual and ordinary mock module IDs isolated across test files ([#​16296](https://redirect.github.com/jestjs/jest/pull/16296))
- `[jest-resolve]` Guard missing `require.resolve.paths` ([#​16052](https://redirect.github.com/jestjs/jest/pull/16052))
- `[jest-resolve, jest-config, jest-runner]` Support a user resolver written as an ES module ([#​16332](https://redirect.github.com/jestjs/jest/pull/16332))
- `[jest-resolve, jest-runtime]` Throw the CJS parse error for ESM syntax in a `"type": "commonjs"` package or a `.cjs` file instead of loading it as ESM, matching Node ([#​16368](https://redirect.github.com/jestjs/jest/pull/16368))
- `[@jest/source-map]` Keep source map sources that name a scheme, such as `webpack:///`, instead of resolving them into a path that does not exist ([#​16327](https://redirect.github.com/jestjs/jest/pull/16327))
- `[@jest/source-map]` Look up `--testLocationInResults` positions at the right column, and keep a mapping to the first column instead of discarding it ([#​16327](https://redirect.github.com/jestjs/jest/pull/16327))
- `[@jest/source-map]` Warn when a source map cannot be parsed, instead of silently leaving its frames untranslated ([#​16327](https://redirect.github.com/jestjs/jest/pull/16327))
- `[jest-runner, @jest/source-map]` Keep a source-mapped stack for an error thrown after the test environment was torn down ([#​16327](https://redirect.github.com/jestjs/jest/pull/16327))
- `[jest-runtime, @jest/source-map]` Keep source maps past teardown and past the next test file's `install`, so a stack from a file no earlier stack mentioned still points at the original source ([#​16330](https://redirect.github.com/jestjs/jest/pull/16330))
- `[jest-runtime]` Report that no coverage was collected when `getAllV8CoverageInfoCopy` is called after `teardown`, instead of returning an empty result ([#​16385](https://redirect.github.com/jestjs/jest/pull/16385))
- `[jest-runtime]` Cache a CJS module's parsed exports before walking its re-exports, so two modules that re-export each other no longer overflow the stack when imported from ESM ([#​16363](https://redirect.github.com/jestjs/jest/pull/16363))
- `[jest-runtime]` Keep a re-exported ES module's parse failure from marking the re-exporting CommonJS file as ESM, so `module.exports = require('./dep.mjs')` loads instead of failing with `module is not defined` ([#​16363](https://redirect.github.com/jestjs/jest/pull/16363))
- `[jest-runtime]` Scope module mocks instantiated inside `jest.isolateModules`/`isolateModulesAsync` to that block, so a mock first imported there no longer outlives it - matching how CommonJS mocks already behave ([#​16365](https://redirect.github.com/jestjs/jest/pull/16365))
- `[jest-runtime]` Suspend module isolation while generating an automock, so loading the real module to read its shape no longer populates the isolated registry ([#​16365](https://redirect.github.com/jestjs/jest/pull/16365))
- `[jest-runtime]` Check a cached ES module's status before `require()` returns it, so a module whose evaluation threw rethrows that error and one left linked by a failed sibling is evaluated instead of returning uninitialized bindings ([#​16364](https://redirect.github.com/jestjs/jest/pull/16364))
- `[jest-runtime]` Report the original `ERR_REQUIRE_ASYNC_MODULE` when a `require()` of a top-level-await graph is retried, instead of a spurious "concurrent `import()`" error ([#​16364](https://redirect.github.com/jestjs/jest/pull/16364))
- `[jest-runtime]` Throw the evaluation error when another caller's `import()` of the same module failed while we awaited it, instead of resolving with the errored module ([#​16364](https://redirect.github.com/jestjs/jest/pull/16364))
- `[jest-runtime]` Mark the result of `require()`ing an ES module that has a default export with `__esModule: true` through a live-binding facade, and serve the same object from `require.cache`, matching Node ([#​16367](https://redirect.github.com/jestjs/jest/pull/16367))
- `[jest-runtime]` Provide a CommonJS module's exports under the `'module.exports'` named export when imported from ESM, matching Node 23+ ([#​16367](https://redirect.github.com/jestjs/jest/pull/16367))
- `[jest-runtime]` Give the test file itself a non-null `require.main` ([#​16367](https://redirect.github.com/jestjs/jest/pull/16367))
- `[jest-runtime]` Populate `module.children` with the modules a file loads, matching Node ([#​16368](https://redirect.github.com/jestjs/jest/pull/16368))
- `[jest-runtime]` Provide `import.meta.resolve` and `import.meta.jest` in `data:` URI modules, accept any-case mediatype parameters, and use Node's error codes for invalid `data:` URIs ([#​16368](https://redirect.github.com/jestjs/jest/pull/16368))
- `[jest-runtime]` Key ES modules by full URL, so query and fragment suffixes create the same module instances as Node and show up in `import.meta.url` ([#​16375](https://redirect.github.com/jestjs/jest/pull/16375))
- `[jest-runtime]` Share modules between overlapping graphs when a CommonJS module `require()`s an ES module mid-load, instead of evaluating shared dependencies twice ([#​16375](https://redirect.github.com/jestjs/jest/pull/16375))
- `[jest-runtime]` Throw `ERR_REQUIRE_CYCLE_MODULE` like Node when a CommonJS module `require()`s an ES module that is still being loaded, instead of evaluating the module a second time ([#​16366](https://redirect.github.com/jestjs/jest/pull/16366))
- `[jest-runtime]` Key builtin modules in the ESM registry by one canonical specifier ([#​16341](https://redirect.github.com/jestjs/jest/pull/16341))
- `[jest-runtime]` `import.meta.resolve()` for a builtin uses its `node:` specifier ([#​16341](https://redirect.github.com/jestjs/jest/pull/16341))
- `[jest-runtime]` Fall back to native ESM when a `.js` file contains ESM syntax but has no `"type":"module"` marker ([#​16152](https://redirect.github.com/jestjs/jest/pull/16152))
- `[jest-runtime]` Allow `require()` of ESM-marked files on Node < 24.9 via transform fallback ([#​16244](https://redirect.github.com/jestjs/jest/pull/16244))
- `[jest-runtime, @jest/transform]` Surface actionable `ERR_REQUIRE_ESM` error for files with untransformed ESM syntax instead of the generic "unexpected token" message ([#​16244](https://redirect.github.com/jestjs/jest/pull/16244))
- `[jest-runtime]` Support older test environments whose `moduleMocker` does not implement `clearMocksOnScope` ([#​16169](https://redirect.github.com/jestjs/jest/pull/16169))
- `[jest-runtime]` Apply `jest.unstable_mockModule` when the mocked file itself is `require()`d, not only when it is imported as a dependency ([#​16389](https://redirect.github.com/jestjs/jest/pull/16389))
- `[jest-runtime]` Apply `jest.unstable_mockModule` to statically imported `data:` URIs on Node 24.9+, matching dynamic `import()` ([#​16389](https://redirect.github.com/jestjs/jest/pull/16389))
- `[jest-runtime]` Run an async `jest.unstable_mockModule` factory once per module instead of twice, and fail the import instead of crashing the worker when the factory rejects ([#​16389](https://redirect.github.com/jestjs/jest/pull/16389))
- `[jest-runtime]` Hide a `require(esm)` module that failed to evaluate from `require.cache`, as Node does, instead of exposing a namespace with uninitialized bindings ([#​16389](https://redirect.github.com/jestjs/jest/pull/16389))
- `[jest-runtime]` Strip the byte-order mark when importing a JSON module, matching `require()` and Node ([#​16389](https://redirect.github.com/jestjs/jest/pull/16389))
- `[jest-runtime]` Throw `ERR_REQUIRE_ASYNC_MODULE` when `require(esm)` runs under an async-only custom resolver, instead of silently resolving with the default resolver ([#​16389](https://redirect.github.com/jestjs/jest/pull/16389))
- `[jest-runtime]` Parse imported JSON modules with the test realm's `JSON`, so their objects pass `instanceof Object` inside the test like `require()`d JSON does ([#​16389](https://redirect.github.com/jestjs/jest/pull/16389))
- `[jest-runtime]` Accept every `file:` URL string in the sandboxed `module.createRequire`, including one with a `localhost` authority, as Node does ([#​16389](https://redirect.github.com/jestjs/jest/pull/16389))
- `[jest-runtime]` Point at `{virtual: true}` when `jest.mock` or `jest.unstable_mockModule` is given a module that cannot be resolved ([#​16389](https://redirect.github.com/jestjs/jest/pull/16389))
- `[jest-reporters]` Fix coverage report table formatting in CI/GitHub Actions environments where `process.stdout.columns` is undefined by falling back to the `COLUMNS` env var or `80` columns in CI, preserving existing behaviour in other non-TTY environments ([#​16227](https://redirect.github.com/jestjs/jest/pull/16227))
- `[jest-runtime]` Support CJS-in-ESM exports via `"module.exports"` named exports ([#​16277](https://redirect.github.com/jestjs/jest/pull/16277))
- `[jest-snapshot]` Keep a skipped or failed test's hinted snapshots, instead of reporting them obsolete ([#​16348](https://redirect.github.com/jestjs/jest/pull/16348))
- `[jest-util]` Stop `globsToMatcher` reusing a cached matcher compiled with different picomatch options, and keep its `dot: true` default when `dot` is passed as `undefined` ([#​16381](https://redirect.github.com/jestjs/jest/pull/16381))
- `[pretty-format]` Move the `react-is` aliases into the `@jest` scope, so they cannot be shadowed by unrelated packages published under the alias names ([#​16333](https://redirect.github.com/jestjs/jest/pull/16333))
##### Chore & Maintenance
- `[docs]` Document the intentional divergences from Node's module system in the ECMAScript Modules page ([#​16368](https://redirect.github.com/jestjs/jest/pull/16368))
- `[docs]` Note deprecation of `react-test-renderer` in React Native tutorial and `pretty-format` README ([#​16294](https://redirect.github.com/jestjs/jest/pull/16294))
- `[docs]` Use `@testing-library/react-native` in the React Native tutorial instead of the deprecated `react-test-renderer` ([#​16318](https://redirect.github.com/jestjs/jest/pull/16318))
- `[babel-jest, @jest/transform]` Update `babel-plugin-istanbul` to v8 ([#​16049](https://redirect.github.com/jestjs/jest/pull/16049))
- `[jest-config, @jest/reporters, jest-runtime]` Update `glob` to v13 ([#​16397](https://redirect.github.com/jestjs/jest/pull/16397))
- `[jest-haste-map]` Refactor massive class into multiple files ([#​16180](https://redirect.github.com/jestjs/jest/pull/16180))
- `[jest-haste-map]` Drop `walker` dependency; replace hand-rolled directory recursion in the JS crawler and watcher startup with `fdir` ([#​16187](https://redirect.github.com/jestjs/jest/pull/16187))
- `[jest-haste-map]` Reuse cached metadata for files whose haste name is a known duplicate, instead of re-reading and re-parsing them on every startup ([#​16351](https://redirect.github.com/jestjs/jest/pull/16351))
- `[jest-haste-map]` Cache the watchman socket path and replace the `watchman --version` probe with `get-sockname`, so warm runs spawn no watchman processes ([#​16386](https://redirect.github.com/jestjs/jest/pull/16386))
- `[jest-resolve]` Store the per-directory package-type lookup in the cache it reads, so it actually memoizes ([#​16369](https://redirect.github.com/jestjs/jest/pull/16369))
- `[jest-resolve, jest-runtime]` Cut repeated work on the resolution hot path: hoist the platform-extension list to construction, memoize `isCoreModule` and the options cache-key serialization, skip mapper preparation when no `moduleNameMapper` is configured, run each mapper regex once, and stop re-parsing `NODE_OPTIONS` on every default-resolver call ([#​16371](https://redirect.github.com/jestjs/jest/pull/16371))
- `[jest-resolve]` Cut warm resolution cost to about a third: reuse one `unrs-resolver` factory per options shape instead of cloning per resolution, compose the factory cache key from per-array cached strings instead of serializing options, and stop constructing an `Error` for misses that `findNodeModule` swallows; add a `__benchmarks__` suite for the default resolver ([#​16373](https://redirect.github.com/jestjs/jest/pull/16373))
- `[jest-runner, @jest/source-map]` Replace `source-map-support` with an implementation in `@jest/source-map` ([#​16327](https://redirect.github.com/jestjs/jest/pull/16327))
- `[jest-snapshot]` Load babel, semver and synckit lazily, so requiring the package (which every test process does through `@jest/expect`) no longer loads \~200 modules that only writing inline snapshots needs ([#​16387](https://redirect.github.com/jestjs/jest/pull/16387))
- `[jest-runtime]` Reduce per-require overhead: skip module ID resolution when no mock can apply, answer core modules before probing for a manual mock, share one `require.cache` proxy across modules, and cache empty files ([#​16376](https://redirect.github.com/jestjs/jest/pull/16376))
- `[@jest/source-map]` Deprecate `getCallsite` in favour of `SourceMapSupport#getCallsite` ([#​16327](https://redirect.github.com/jestjs/jest/pull/16327))
- `[jest-runtime]` Avoid magical `null` value in ESM loader ([#​16160](https://redirect.github.com/jestjs/jest/pull/16160))
</details>
---
### Configuration
📅 **Schedule**: (UTC)
- Branch creation
- "before 9am on monday"
- Automerge
- At any time (no schedule defined)
🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.
♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
🔕 **Ignore**: Close this PR and you won't be reminded about these updates again.
---
- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box
---
This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/expo/entity).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC42MS4zIiwidXBkYXRlZEluVmVyIjoiNDQuNjEuMyIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->
# Why
Count queries reported their result through the load event, so a `COUNT(*)` returning 8k looked like we loaded 8k entities.
# How
Give counts their own event (`logDataManagerCountEvent`) so the count number stops being read as entities loaded. Breaking: `IEntityMetricsAdapter` implementers need to add the method; I'm happy to go for another path, non-breaking change if you prefer, but this felt more fundamentally correct
<img width="480" height="240" alt="giphy" src="https://github.com/user-attachments/assets/4630fefc-cca4-42ca-afc4-22b9919df4a8" />
# Test Plan
CI. Updated the metrics unit test to check the counts route to the new event.
This PR contains the following updates:
| Package | Update | Change |
|---|---|---|
| redis | digest | `234c902` → `c88d347` |
---
### Configuration
📅 **Schedule**: (UTC)
- Branch creation
- "before 9am on monday"
- Automerge
- At any time (no schedule defined)
🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.
♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
🔕 **Ignore**: Close this PR and you won't be reminded about this update again.
---
- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box
---
This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/expo/entity).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yODAuMCIsInVwZGF0ZWRJblZlciI6IjQzLjI4MC4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->
# Why
A bug exists where the ts-mockito extension entity aware matcher would report map equality even when the maps were of different lengths (assuming shorter map had all the same elements as first part of longer map).
# How
Fix by preemptively checking length. Also fix recursive call to use `isEqualWithEntityAware` in case nested objects need it.
# Test Plan
Run updated test.
# Why
The stub adapters were doing a simple `:` join, so it was possible for two rows to collide in the stub adapter during tests, if value has the delimiter.
# How
Fix is to use JSON.stringify so that only truly same rows map to the same thing.
# Test Plan
Run new tests.
# Why
Just a comment update to make it clearer that IEntityMetricsAdapter implementations must not throw since it'll cause adverse effects for the library.
In a follow-up, it may be good to defend against this, but it's not a straightforward fix.
# How
Update comment.
# Test Plan
Proofread.
# Why
`pick` was calling `object.hasOwnProperty(prop)`, but if the object had a property `hasOwnProperty`, it'd be used.
# How
Fix is to use the Object prototype's method.
# Test Plan
Run added test.
# Why
The bug is that if the column in the current entity that contains the referenced entity's `id` is an empty string, the load method would erroneously return null. This correct the behavior to correctly try to load the entity by ID, which correctly fails since the field is invalid.
# How
Fix check.
# Test Plan
Add new test.
# Why
Typescript enums come in two flavors: https://www.typescriptlang.org/docs/handbook/enums.html
Previously, the validation here only worked for string enums.
# How
Update logic to make it work for both string and numeric enums.
# Test Plan
Run new test.
# Why
#601 exposed another validation gap in assumptions made about cache keys. We need certain assumptions to be true, namely:
- delimiter is nonzero-length (otherwise issue #601 fixes still would exist)
- delimiter doesn't include escape character (otherwise escape breaks)
- ttlSecondsPositive and negative are positive integers
- cacheKeyVersionsToInvalidateFn returns > 0 versions (otherwise silently doesn't invalidate)
# How
Add validation.
# Test Plan
Add test.
# Why
In places where a rule list is dynamically composed, it's likely possible for this list to be empty. Due to the way it's written, it would erroneously allow in this case.
# How
Add check for case and skip.
# Test Plan
Add test.
# Why
There is a bug in self-referential (or transitively-self-referential) cascade cycle detection.
From claude:
```
In AuthorizationResultBasedDeleteMutator:
- deleteInternalAsync (line 802) runs the authorize check, calls
processEntityDeletionForInboundEdgesAsync, runs validators/triggers, deletes the row, and schedules
invalidation callbacks and afterCommit triggers.
- The cycle guard is at the top of processEntityDeletionForInboundEdgesAsync (line 932) — if
(processedEntityIdentifiers.has(entity.getUniqueIdentifier())) return;.
Trace a cycle (A↔B cascade-delete):
1. deleteInternalAsync(A, visited=∅) → runs authorize+triggers+DB delete for A →
processEntityDeletionForInboundEdgesAsync(A, visited=∅), marks A visited, finds B, calls
deleteInternalAsync(B, visited={A}).
2. deleteInternalAsync(B, visited={A}) → runs authorize+triggers+DB delete for B →
processEntityDeletionForInboundEdgesAsync(B, visited={A}), marks B visited, finds A, calls deleteInternalAsync(A,
visited={A,B}).
3. deleteInternalAsync(A, visited={A,B}) → runs authorize+triggers+DB delete for A again → only when it gets into
processEntityDeletionForInboundEdgesAsync does the visited check finally short-circuit.
So beforeDelete/afterDelete/beforeAll/afterAll, the database delete, the post-commit invalidation, and
afterCommit triggers all fire twice for A. The DB delete is idempotent (row's already gone) but triggers
with side effects (emit events, write audit rows, etc.) double-fire.
```
# How
Fix is to move cycle detection to to of `deleteInternalAsync`, thus guarding all checks for already-run entities.
# Test Plan
- Run new test
- Existing test was passing due a stale entity being passed into deleter. The why is a bit nuanced. Here's claude:
```
The original test setup
const categoryA = await CategoryEntity.creator(viewerContext).createAsync();
const categoryB = await CategoryEntity.creator(viewerContext)
.setField('parent_category_id', categoryA.getID())
.createAsync();
await CategoryEntity.updater(categoryA)
.setField('parent_category_id', categoryB.getID())
.updateAsync();
After these three statements, the database state is:
- A: {id: uuidA, parent_category_id: uuidB} (after update)
- B: {id: uuidB, parent_category_id: uuidA}
But the local variable categoryA still holds the original entity instance from line 1, where
parent_category_id was null. updateAsync() returns a new entity, but that return value is discarded — the
local categoryA was never rebound. So categoryA.getAllDatabaseFields() in the test returns {id: uuidA,
parent_category_id: null}.
Why the test passed before the fix
When the test calls CategoryEntity.deleter(categoryA).deleteAsync(), the outermost mutator's this.entity
is the stale categoryA (with parent_category_id = null).
Trace through the OLD cycle behavior for CASCADE_DELETE_INVALIDATE_CACHE_ONLY (so skipDatabaseDeletion =
true on cascade calls):
1. Outer A mutator runs. this.entity = stale A. Traverses inbound edges of A, finds B.
2. Middle B mutator runs (cascade). this.entity = freshly-loaded B from DB (correct fields). Traverses
inbound edges of B, finds A → cascades to A again.
3. Inner A mutator runs (cycle re-entry). this.entity = freshly-loaded A from DB, with parent_category_id
= uuidB (the post-update value).
3. Under the OLD code, the cycle guard at the top of processEntityDeletionForInboundEdgesAsync stops the
traversal from recursing further, but the inner deleteInternalAsync still runs validators, triggers, and
registers a post-commit invalidation callback using this freshly-loaded A's fields.
After commit, the post-commit invalidation callbacks fire:
- Outer A registers invalidation for {id: uuidA, parent_category_id: null} → invalidates (id, uuidA) only
(null isn't a cache key).
- Middle B registers invalidation for {id: uuidB, parent_category_id: uuidA} → invalidates (id, uuidB) and
(parent_category_id, uuidA).
- Inner A registers invalidation for {id: uuidA, parent_category_id: uuidB} → invalidates (id, uuidA) and
(parent_category_id, uuidB).
The test asserts that (parent_category_id, uuidB) is invalidated, and it was — because of the inner A's
fresh-loaded fields. The OLD bug (running deletion side effects twice) was accidentally compensating for
the stale entity reference held by the test.
Why the test failed after the fix
With the cycle guard moved to the top of deleteInternalAsync, the inner A call returns immediately without
registering any invalidation. Only the outer A's invalidation runs, with the stale parent_category_id =
null, so (parent_category_id, uuidB) is never invalidated → still HIT → assertion fails.
This isn't a regression in real-world usage. In real code, callers don't keep stale references after
updating, because:
- The idiomatic pattern is const x = await Entity.updater(x)...updateAsync() (rebinding).
- Non-cycle deletions of stale references already produced incomplete invalidation in the OLD code — the
cascade re-load only happened to "rescue" the cycle path.
After the fix, cycle and non-cycle paths behave consistently: the entity instance the caller hands to
deleter() is the source of truth for what gets invalidated.
The test edit
const initialCategoryA = await CategoryEntity.creator(viewerContext).createAsync();
const categoryB = await CategoryEntity.creator(viewerContext)
.setField('parent_category_id', initialCategoryA.getID())
.createAsync();
const categoryA = await CategoryEntity.updater(initialCategoryA)
.setField('parent_category_id', categoryB.getID())
.updateAsync();
categoryA now binds to the return value of updateAsync(), so it carries the post-update fields. The cycle
behavior remains under test — the assertion that both (parent_category_id, uuidA) and (parent_category_id,
uuidB) are MISS still verifies that the cascade correctly invalidated B's and A's cache keys. The fix to
the test reflects correct API usage, and the cycle handling is independently confirmed by the new fires
delete-side-effects exactly once per entity in a cycle test.
```
# Why
In the same way that `processEntityDeletionForInboundEdgesAsync` has detection for reference cycles, this preemptive check utility method needs the same detection mechanism.
# How
Add the same detection mechanism as `processedEntityIdentifiers`
# Test Plan
Add test case for this that fails before change, succeeds after.
# Why
One way memory can grow unbounded within a single request is via transactional dataloaders. If a single request executes N transactions, each one accumulates it's own dataloader state.
Though this has the same semantics as regular dataloaders (which belong to companion provider which is scoped to request by application and discarded after response is sent), they can accumulate more.
# How
The fix is to clear the transactional dataloader at the end of the transaction (or nested transaction), no matter whether the transaction succeeded or rolled-back.
# Test Plan
Add test for condition.
# Why
Please describe the motivation for this PR, and link to relevant GitHub issues, forums posts, or feature requests.
# How
How did you build this feature or fix this bug?
# Test Plan
Please describe how you tested this change and how a reviewer could reproduce your test, especially if this PR does not include automated tests!
# Why
Similar to #601, it was possible for a misconfigured application to have collisions in cache keys for secondary caches. Because they likely share the same redis instance at the application level, these keys need similar treatment for separating them logically from normal loader keys plus consistent delimiter escaping.
# How
Hoist final key creation from application level to library level and apply consistent delimiter escaping and namespacing.
# Test Plan
Run new test.
# Why
Since cache key construction function is application-supplyable, there's potential that it doesn't escape properly, causing potential cache collisions.
# How
Change the API to only allow specifying the delimiter, and hoist code into the package to handle delimiter escape.
# Test Plan
Run new test to ensure no collisions on something that would've previously collided.
# Why
There's nothing preventing two entity classes from sharing the same class name. While not common in practice, this preemptively defends against the issue of collisions in companion maps.
Secondarily, table data coordinators could collide, even if they have different flavors of database and cache adapters. Since those are used to construct the coordinator, include their flavors in the map key.
# How
Update key generation code.
# Test Plan
Run new tests.
# Why
ViewerScopedEntityMutatorFactory existed to make both the loader and mutator patterns more aligned. But it doesn't make as much sense for mutators since for update and delete, the viewerContext used is the one from the entity being updated/deleted instead of the one for the viewer scoped factory. So, in practice the one from the factory was only being used for create mutations.
The simplest change is to just remove the ViewerScopedEntityMutatorFactory to reduce confusion.
# How
Remove class.
# Test Plan
CI
# Why
oxfmt/oxlint has near parity with prettier/eslint but is about 10x faster (10s -> 1s).
This uses `oxlint-config-universe` equivalent of `eslint-config-universe` as the base. There are known differences between eslint and oxlint, documented in the readme of `oxlint-config-universe`.
The missing rules which we can live without temporarily in exchange for the speedups:
- `naming-convention` (async suffix, PascalCase, UPPER_CASE)
- `no-restricted-properties` (.only/.skip)
- `no-restricted-exports` (no default exports)
- `tsdoc/syntax`
- object-shorthand
- no-dupe-args, no-octal, no-octal-escape
- n/no-deprecated-api
I've created a task to track: #567.
# How
Have claude convert. Use `oxlint-config-universe` and manually audit resulting config against old eslint config. Note down known differences in #567.
In stacked PR, use oxfmt for import ordering as suggested by readme in `oxlint-config-universe`
# Test Plan
`yarn lint`
# Why
Noticed that this could throw a TypeError if the decoded JSON wasn't an object:
> TypeError: Cannot use 'in' operator to search for 'id' in blah
# How
Add check that this is an object type ahead of using `in` operator.
# Test Plan
Add new tests for this, run them.
# Why
This argument was unused, remove it.
# How
Was checking whether EntityInvalidationUtils was the same instance (or same behaviorally) across multiple entities backed by the same table (share a config, different TSelectedFields). Being able to remove this arg proves it since the other args aren't entity-type-specific and are only configuration-specitic.
# Test Plan
`yarn tsc`
# Why
Noticed while using these methods in the Expo server application that they were missing docblocks due to vscode preferring the docblock for the overload over the object property.
# How
This adds overload-specific docblocks to ensure they are present in vscode, and since they're overload-specific they are more accurate as well.
# Test Plan
`yarn tsc`
Mise can manage yarn versions, but the free renovate doesn't know to use
that configuration. After this commit, the yarn version is set by the
packageManager field in the root package.json file, and downloaded by
corepack in all environments.
# Why
Asked claude to do an API usability review and it noted that forcing the user to context-switch between entity fields and database fields for SQLFragments was an unfortunate pattern.
# How
This PR provides a SQLFragment helper, `entityField<TFields>('fieldName')` which does the conversion for the user so they don't need to look up the underlying database field.
Secondarily, this provides type safety to ensure SQLFragments for one entity aren't used for SQLFragments of another entity (unless types overlap via shared config).
# Test Plan
Add feature and tests and examples.
# Why
Asked claude to do a security review of entity-database-adapter-knex and renaming `raw` to `unsafeRaw` was one suggestion.
# How
Rename.
# Test Plan
`yarn tsc`
# Why
This bug was subtle but important, and can be demonstrated best by looking at the new test case added `performs forward pagination with descending order`.
The bug was that cursor comparison must be derived from both direction but also ASC/DESC of the order bys.
Example:
```
obj1(t=1)
obj2(t=2)
obj3(t=3)
obj4(t=4)
> loadPage(first: 2, orderBy: t DESC)
obj4
obj3
> loadPage(first: 2, orderBy: t DESC, after: obj3)
```
the issue is that the cursor comparison for this second load would be:
```
(t) > (obj3.t)
```
which yields only obj4, which is incorrect.
# How
The fix is to determine cursor comparator by both the order by direction and the pagination direction, thus making the cursor from the example above:
```
(t) < (obj3.t)
```
The second part of this is to require all orderBys to have the same ordering for pagination, otherwise this new assumption breaks since cursor comparison then would be unable to be done with a single comparator. While there are ways we can handle different order by directions, we'd have to not use a cursor which adds a massive amount of complexity.
A small other fix was to set the `id` tiebreaker column order to the same as the other orders.
# Test Plan
Add new test case.
# Why
There's a bug in where clause construction, which used SQLFragmentHelpers.and: https://github.com/expo/entity/blob/main/packages/entity-database-adapter-knex/src/internal/EntityKnexDataManager.ts#L303
The bug is that given something like:
```
where: sql`account_id = ${accountId}`,
searchWhere: sql`something ILIKE term OR similarity(term, ...) OR ...`
```
it would produce
```
WHERE account_id = ${accountId} AND something ILIKE term OR similarity(term, ...) OR ...
```
which has the incorrect parentheses and therefore incorrect precedence.
This exposed more bugs in the precedence in `or`, `and`, `join`, etc methods.
# How
The fix is multifold:
- Make the public `join` method only be used for comma-separation joining. Rename it to `joinWithCommaSeparator`.
- Wrap each group in `or` and `and` in parentheses to ensure correct precedence.
```
> SQLFragment.or(sql`account_id = ${accountId}`, sql`app_id = ${appId}`
sql`(account_id = ${accountId}) OR (app_id = ${appId})`
```
# Test Plan
Run existing tests to ensure nothing breaks, update unit tests and inspect output SQL statements to ensure they are correct parentheses. Especially the `builds complex queries with multiple helpers` test case.
# Why
In a decent number of tests in the Expo server application, it's useful to test pagination from a specific point. It may also be useful for production code in an thus far unbeknownst way.
To do this, one must be able to generate the cursor given an entity. This PR adds such a method.
# How
Add the method that creates a cursor.
# Test Plan
Run the new test.
# Why
When testing this downstream, I noticed that it was allowed in types to specify first & before / last & after which aren't supported in the pagination.
# How
Fix the types by being explicit about the other fields.
# Test Plan
`yarn tsc`
# Why
#468 added nulls support for regular order bys, but explicitly disabled it for search pagination due to it adding quite a lot of complexity. It also fixed a bug where using a nullable field in pagination in general would break the cursor condition and omit rows (any tuple containing NULL would be dropped due to postgres behavior).
But applications still need to be able to search fields that are nullable, they just need to provide a mechanism for dictating how to cast them as non-nullable. This PR provides such a mechanism.
# How
Allow the user to specify a function to craft the search field SQLFragment from nullable fields. This is required to be a somewhat awkward shape since the library needs to retain control over the field name due to table aliasing in the cursor.
# Test Plan
Run new tests.
# Why
When these were installed extension functions, we could get away without the `TMViewerContext2` technique. But now that these are loader methods on the `PostgresEntity` subclass, we need this again (it's the same as Entity and ReadonlyEntity).
This type allows the typechecker to behave correctly when a subclass of ViewerContext is used.
# How
Add generic the same as ReadonlyEntity/Entity.
# Test Plan
`yarn tsc` in application that uses ViewerContext subclass and knexLoader.
# Why
Now that `loadManyBySQL` supports all the features of this method (including raw order by), deprecate it.
# How
Mark as deprecated in docblock.
# Test Plan
`yarn tsc`
# Why
#457 added a new mechanism for specifying ORDER BY clauses using SQLFragment that aligns a lot better with the loader APIs than having separate `orderByRaw`/`orderByFragment` fields in the query modifiers.
# How
Replace all with the new consistent order by in order field, update code to handle that.
One neat side-effect is that we no longer need the hard-to-follow direction-specific ordering in deep methods like `buildTrigramExactMatchPriority` since now we can have those just produce a SQLFragment and reverse the ordering the same as all the other pagination methods.
# Test Plan
Run all tests.
# Why
Small change to the debug method for SQLFragment. Noticed claude made it a `get toDebugString`, which doesn't quite fit within the patterns in this repo.
# How
Rename to `getDebugString()` method for consistency.
# Test Plan
`yarn tsc`
# Why
One missing piece from pagination is the ability to order by a SQL fragment. This is useful for cases like needing to apply postgres transforms or coalescing to the columns in the order by clause.
Note that this, in combination with `loadBySQL` will completely replace the need for `loadManyByRawWhereClause` (including it's orderByRaw variant), as well as `orderByFragment` in the new pagination code, which is done in a PR upstack.
# How
Make order by specifications either be able to use SQLFragment to specify the field selection but not the ASC/DESC since the framework still needs to retain control of those for pagination purposes.
# Test Plan
Run new added tests.
# Why
The one downside of using id-based cursor pagination and subqueries (added in #422, #431) is that it risks the row referenced by the cursor being deleted between pagination requests. It's an edge case though to be clear. Encoding the full cursor alleviates this, but has it's own downsides since trigram similarity isn't encodable and neither are things like Date objects. It is still preferable to use ID-based cursors for all pagination.
But it becomes the library's responsibility to explicitly document what the behavior is when a row referenced by the cursor is no longer present. This PR does this.
# How
When the cursor row is no longer present, the tuple evaluates to `NULL`, which produces a empty page.
The alternative to this behavior is to run an id check query ahead of the pagination query, and throw an error if it doesn't exist, or even return the first page of data if it doesn't exist. Both of these are less optimal since they cause unexpected behavior during results consumption.
So we keep the behavior as is and explicitly document it.
# Test Plan
Run new tests.
# Why
Did a brief audit of `any` casts in the library and removed ones that are no longer needed or could be replaced with a better concrete type.
# How
Grep for `as any`, fix.
# Test Plan
`yarn tsc`
# Why
As part of #451, claude noticed that we used both american and british english variants of "adapter"/"adaptor". This makes them consistent by making them all "adapter".
# How
Update name. Technically this is a breaking change but the nature of the library allows `tsc` to catch it in all application code that uses the library.
# Test Plan
`yarn tsc`
# Why
Asked claude to audit the docblocks for incorrect or missing info. This is what it mentioned as being good improvements.
# How
Update docblocks.
# Test Plan
Proofread.
# Why
In #410 we split out `EntityLoaderUtils` into two classes:
- EntityInvalidationUtils - functions for invalidating entity caches from application code when underlying data is mutated outside of the entity framework
- EntityConstructorUtils - functions for constructing and authorizing entities.
Originally, these were both public due to them being the same class. Now, with them separated, we can better restrict access to entity construction which should only be done within the framework itself.
# How
Make constructor utils private everywhere except EntityLoaderFactory, which is deep enough in the abstraction that it's not immediately usable by top-level entity APIs (static methods on Entity/ReadonlyEntity, loader classes).
# Test Plan
`yarn tsc`, CI
# Why
Part 4 of #441. This reverts the test changes to use the better pattern, though both work equivalently and are both tested.
# How
Update tests.
# Test Plan
Run tests
# Why
Part 3 of the refactor from #441.
This reverts the codemod back to use the `BlahEntity.knexLoader(vc)` syntax.
# How
`git revert`
# Test Plan
Run the tests in codemod.
# Why
This is part 2 of the refactor described in #441.
It adds subclasses of Entity and ReadonlyEntity that supply the static `knexLoader` methods to keep the ergonomics of the extension but without the monkey-patching.
# How
Add subclasses, add test.
# Test Plan
Run test.
# Why
Asked claude extended thinking to analyze the `installExtensions` monkey-patching strategy we went with in #410 and compare it to other typescript ORM libraries.
It said essentially that it was ok, but most modern libraries choose not to do it that way, and instead recommended "free functions" (TIL about that term) to instantiate the loaders. (`knexLoader(BlahEntity, vc)`)
Working with it even more, I came up with an even better method on top of free functions to keep the `BlahEntity.knexLoader(vc)` syntax. The strategy is to:
1. Convert to free functions (this PR).
2. Add `PostgresEntity`/`ReadonlyPostgresEntity` subclasses that application entities can extend that implement the static extension methods.
3. Revert the codemod change from this PR to keep the codemod to prefer the `BlahEntity.knexLoader(vc)` syntax since most applications should use the subclasses (though it's not strictly necessary if they prefer the free function at callsites).
4. Revert the codemod in the integration tests to express better syntax pattern.
# How
Claude did most of this, but broadly speaking the strategy is to have the free functions, and then instead of patching the prototypes/classes for singleton caching, use a WeakMap where the core package singleton is the key and the value is the new knex-specific object.
# Test Plan
Run all tests.
# Why
When testing an upgrade to this package in the Expo server application, noticed that this was a minor annoyance since TSelectedFields has a default in all other public API methods.
# How
Add default to be consistent.
# Test Plan
`yarn tsc`
# Why
Currently, the Entity pagination system allows clients to request arbitrarily large page sizes using the `first` or `last` parameters in pagination queries. This can lead to:
- Performance degradation when clients request excessively large pages
- Potential memory issues on the server
- Unpredictable query response times
- Difficulty in capacity planning and resource allocation
By adding configurable max page size limits, we can:
- Protect backend services from resource exhaustion
- Ensure predictable performance characteristics
- Provide better control over API usage patterns
- Maintain backward compatibility (no limit by default)
# How
The implementation adds a `paginationMaxPageSize` configuration option to the PostgresEntityDatabaseAdapterProvider that flows through the adapter hierarchy:
1. **Configuration Interface**: Added `PostgresEntityDatabaseAdapterConfiguration` with an optional `paginationMaxPageSize` field
2. **Provider Level**: `PostgresEntityDatabaseAdapterProvider` constructor now accepts the configuration object
3. **Adapter Level**:
- `BasePostgresEntityDatabaseAdapter` defines a `paginationMaxPageSize` getter (defaults to `undefined`)
- `PostgresEntityDatabaseAdapter` overrides it to return the configured value
4. **Validation**: `EntityKnexDataManager.loadPageAsync` validates `first`/`last` parameters against the max page size before executing queries
The validation throws descriptive errors when limits are exceeded:
- `"first must not exceed maximum page size of {limit}"`
- `"last must not exceed maximum page size of {limit}"`
Usage example:
```typescript
const provider = new PostgresEntityDatabaseAdapterProvider({
paginationMaxPageSize: 100
});
```
# Test Plan
Added comprehensive unit tests in `EntityKnexDataManager-test.ts`.
# Why
This PR adds search capabilities to the existing cursor-based pagination system added in #422 which only supported orderBy ordering.
This adds:
1. Case-insensitive pattern matching (ILIKE) - Useful for basic text search across entity fields
2. Trigram similarity search - Provides fuzzy matching capabilities for more advanced search use cases like handling typos or finding similar text
This enables building user-facing search features while maintaining the benefits of cursor-based pagination (stable results, efficient queries).
# How
The implementation introduces a unified PaginationSpecification that supports three strategies:
1. Standard pagination - The existing orderBy-based pagination
2. ILIKE search - Pattern matching with automatic wildcard escaping
3. Trigram search - PostgreSQL trigram similarity with configurable threshold
Key implementation details:
- Search terms are properly parameterized to prevent SQL injection
- ILIKE special characters (%, _, \) are escaped to prevent pattern injection
- Results maintain stable cursor-based pagination with proper ordering:
- ILIKE: Ordered by search fields, then ID
- Trigram: Ordered by exact match priority, similarity score, optional extra fields, then ID
# Test Plan
The PR includes comprehensive test coverage:
- Unit tests for both AuthorizationResultBasedKnexEntityLoader and
EnforcingKnexEntityLoader
- Integration tests covering:
- Forward/backward pagination with search
- Multi-field search
- Case-insensitive matching
- Trigram similarity with various thresholds
- Cursor stability across pages
- Edge cases (empty results, special characters)
- Combined with WHERE clauses
- Security validation ensuring proper escaping and parameterization
All existing tests pass, confirming backward compatibility for standard pagination when using the new API.
# Why
Noticed this was mis-typed in a later PR, hoisted into this one.
# How
Update type of EntityConfiguration.idField to be `TIDField`, which is the most correct and precise type for the field. This will help later on when adding pagination ID field stuff.
The other changes to tests are fixes since these previously were underspecified and changing the field type exposed these missing types. (claude informed me of this)
# Test Plan
`yarn tsc`
# Why
Noticed while creating an upcoming pagination PR that loaders should use `TSelectedFields` for `orderBy` clauses. This was an accidental omission when the selected fields concept was added.
Succinctly, one should only be able to order entities by their fields rather than by all their underlying table's fields.
# How
Create new orderBy clause type that lives at the loader level. It is fully compatible (subset or equal to) the database orderBy clause, so calling the data manager methods directly with the arguments is by design.
# Test Plan
`yarn tsc`
# Why
As of moving raw and conjunction loading into the `entity-database-adapter-knex` package, the stub database adapater is no longer as useful in applications that use the package (it used to support conjunction loading synthetically for unit tests).
# How
This PR adds a new package, `entity-database-adapter-knex-testing-utils` that provides what StubDatabaseAdapter used to be (it has conjunction loading).
The idea is that applications that use `entity-database-adapter-knex` will use this package for their stub database needs and unit test companion provider.
# Test Plan
Full test coverage.
# Why
This commits the claude prompts I have been using to pair program with claude while building this stack.
I also noticed that claude loved to add `test.skip` to make things pass, which now is disallowed as of this PR.
# How
Commit the file.
# Test Plan
yarn lint
# Why
Part 3 of #407.
# How
This is the main PR of the stack. It creates a mechanism for "installing" the extension methods on required objects, and then installs them and tests them.
The goal is to make it better self-contained about what is core in the library and guaranteed to be efficient (dataloader'd and cached) and what is not (raw SQL and SQLFragment loads in a future PR).
This is becoming more critical in a time of agentic coding where having a discrete set of loaders that can be linted separately and instruct agents to run `EXPLAIN ANALYZE` in raw cases.
# Test Plan
Full test coverage.
# Why
This is a long stack, but the goal is to make a place to put postgres-specific loading logic to avoid polluting the core dataloader-based library with logic specific to knex/postgres. And potentially even postgres-specific mutation logic but that's way down the road.
The best place for this stuff is in the `entity-database-adapter-knex` library. Therefore, the strategy is (in PR order):
1. Move existing knex/postgres-specific loader methods to a new loader, `knexLoader`. I'm also open to calling this a "Postgres Loader" since the `entity-database-adapter-knex` package already uses postgres-specific queries (and more are added here since this stack eventually produces raw queries) within knex even though technically knex is database agnostic.
1. Move knex-specific logic out of EntityDataManager, move it to its own EntityKnexDataManager.
1. Move these two now-separated pieces into the `entity-database-adapter-knex` package. This requires creating a "plugin" system for database adapters, where when they are included their methods are added to the prototype so that seamless loading continues to work.
1. Add a codemod for these three pieces.
1. Add the first new feature to entity loading, load by tagged template string, which creates a better/safer way to express raw queries for loading entities.
Future other things that could be added here (after thorough thought on authorization) are:
- Pagination
- Create, update, delete by tagged template string
- Batch creation
- Upsert (maybe)
# For Reviewers
The most critical things to assess are:
- "install" method in #410
- sql tagged template API usability in #414
# How
This is part 1. It refactors the loaders by moving `loadManyByFieldEqualityConjunctionAsync`, `loadFirstByFieldEqualityConjunctionAsync`, and `loadManyByRawWhereClauseAsync` to a separate loader.
The new pattern for these is:
```
await PostgresTestEntity.knexLoader(vc1).loadManyByRawWhereClauseAsync(...)
```
# Test Plan
This has full coverage in new tests.
# Why
One other set of rules we've found useful in the Expo application for expressing complex privacy logic is a boolean any/all rule that composes an array of rules.
This PR upstreams these rules.
# How
Add rules and tests.
# Test Plan
Run tests.
# Why
One other rule we've found useful in the Expo application is a simple predicate rule based on an entity field.
This PR upstreams this rule.
# How
Add rule and test.
# Test Plan
Run test.
# Why
Found this to be useful in the Expo backend application. The use case is that one may wish to permit access for the mostly-programmatic step of nullification of a field in an entity, whether or not any of the other privacy policy rules in the policy allow.
For example, let's say a user creates a build on organization X. Their user ID may be recorded in a foreign key field on the build so that the application can query all builds for that user. But then the user loses access to organization X. Then, they want to delete their user. This will do a cascade set null on the builds initiated by the user, but the user no longer has permission to update those builds via the normal organization membership permission rules, so this explicit allow-if-cascade-rule is used.
# How
Add a generic rule for use in applications.
# Test Plan
Add test.
# Why
Since this library uses conventional commits to generate changelogs, we want to validate that the PR title is in a valid conventional commit format.
Closes ENG-15958.
# How
Add lint action.
# Test Plan
This only runs once committed to main so we'll need to commit this and then try it out.
I audited the action code at a glance.
Why
===
es6-error uses Object.defineProperty calls to set name, message, and stack on every error instantiation. Modern Node.js (14+) supports native Error subclassing, making this overhead unnecessary.
How
===
- Remove es6-error dependency
- Use native Error class with static blocks to set the prototype name once per class rather than per instance
- Use abstract getters for state and code in the base class, with concrete implementations in subclasses returning narrowed literal types (e.g., `get state(): EntityErrorState.PERMANENT`)
- Pass cause to Error constructor options instead of manually setting it
Abstract getters were chosen over prototype assignment because:
- They use standard TypeScript/JavaScript patterns that are self-documenting
- They don't require `declare` statements to satisfy TypeScript
- They allow narrowed return types (e.g., `EntityErrorState.PERMANENT` instead of `EntityErrorState`)
The name property is set on the prototype via static initializer because Error.prototype.name is conventionally a prototype property, and this matches how native error subclasses like TypeError work.
Benchmark shows ~1.9x faster error instantiation:
- es6-error: ~213k ops/sec
- native: ~400k ops/sec
Test Plan
===
yarn tsc passes. All tests pass
Why
===
The map().filter() pattern creates an intermediate array with null values
that are immediately discarded. For an entity with 8 fields, this allocates
an 8-element array where ~25% of values are null, only to filter them out.
Benchmarks show inlining as a for loop is 1.8x faster:
- map().filter(): 7.0M ops/sec
- for loop: 12.7M ops/sec
How
===
Replace two instances of `.map(...).filter(x => x !== null)` with for...of
loops that conditionally push to the result array. This is the same pattern
used elsewhere in the codebase (e.g., successfulResults, failedResults in
entityUtils.ts).
Test Plan
=========
All existing tests pass (362/362). Behavior is identical.
Co-authored-by: Will Schurman 🦦 <[email protected]>
Why
===
The previous implementation used Set + Object.entries + Object.fromEntries
which created intermediate data structures. Benchmarks showed this was 5.6x
slower than direct property access for typical usage (2 props from medium
entity: 1.8M vs 10.1M ops/sec).
How
===
Replace the Set-based filtering approach with a simple for...of loop that
directly accesses and copies properties. This avoids:
- Creating an intermediate Set
- Object.entries() array allocation
- Object.fromEntries() reconstruction
Use hasOwnProperty() instead of `in` operator to match the original semantics
(Object.entries only returns own properties) and avoid edge cases with
inherited properties like "toString".
Test Plan
=========
All existing tests pass. The function is semantically identical - it still
returns a new object with only the specified own properties.
# Why
For a while in Expo application code, we've used beforeDelete triggers to approximate beforeDelete validators, but we finally found a need to separate them out. Due to the nature of validators (no side-effects) vs triggers (side-effectful), there can be a need to manually run validators but not triggers. One such need is ahead of batch deletion, to ensure that an object and it's related objects can be deleted and retain any constraints implemented in validator logic.
Closes ENG-18066.
# How
Split out definition to be more similar to triggers, update tests.
# Test Plan
Add new test for these, ensure that they are called appropriately.
# Why
#311 updated the errors for postgres, but the conditions in the stub adapters preempted those errors in test code, so their behavior diverged. This PR makes the stub adapters behave more similarly to Postgres adapter.
# How
Return more similar results in not-found cases. See inline comments.
# Test Plan
`yarn tsc`. Also the tests in entity-example test this.
# Why
Technically upper-case UUIDs are valid according to https://datatracker.ietf.org/doc/html/rfc4122.
But the way entity is written expects case sensitivity due to them being treated as simple strings. For example, this would be the expected behavior:
```
const entities = await TestEntity.loader(vc).loadManyByFieldEqualingAsync('id', ['018EBFDA-DC80-782D-A891-22A0AA057D52']);
=> Map(['018EBFDA-DC80-782D-A891-22A0AA057D52', TestEntity[...]])
```
The issue is that Postgres stores them in a case-insensitive manner (which is correct according to the UUID spec). So in actuality, the above code would theoretically do something like:
```
const entities = await TestEntity.loader(vc).loadManyByFieldEqualingAsync('id', ['018EBFDA-DC80-782D-A891-22A0AA057D52']);
=> Map(['018ebfda-dc80-782d-a891-22a0aa057d52', TestEntity[...]])
```
(it actually fails due to this change of case further up in the stack at https://github.com/expo/entity/blob/077d53cd86d4c54132ea2ebcc59d1d75e2b507d0/packages/entity/src/EntityDatabaseAdapter.ts#L182, but the reason is the same: postgres transforms to lower case under-the-hood)
This results in breaking invariants the framework provides (all keys in === all keys out).
This PR solves this problem by just enforcing UUID casing in fields to match what Postgres already does to avoid this issue.
Ref: WWW-37
# How
I first tried a field normalization approach in #309, but this also had the same problem: the fields in !== fields out. Unless we were to invert the normalization on the way out, but that comes with a lot of added complexity.
So this is the solution I propose: just don't allow any upper-case characters in the UUIDs. This is technically not UUID spec compliant since we no longer accept all technically valid UUIDs.
# Test Plan
Run the test.
# Why
In other repositories at Expo we have this lint disallow rule to avoid accidental skips like this. This PR adds the same rule.
# How
1. Add rule.
2. Fix violation.
# Test Plan
`yarn lint`
Before this commit, for projects with `"type": "module"`, anything
exported from a file `export default` was not resolvable from the barrel
file as a named export.
After this change, the interface of the index is unchanged, but ESM
projects can use it.
To test this, the entity-example package is given `"type": "module"`,
and changed to run with `node --experimental-transform-types`. Its
imports from other packages do not need to change. The other
integration tests passing are evidence that commonjs modules also do not
need to change their imports.
This commit also adds an eslint rule to enforce this standard.
# Why
Noticed in this stack that a few more places could use the `@internal` typedoc annotation to prevent it from polluting the public docs.
# How
Add annotation to internal constructs.
# Test Plan
Inspect docs manually (`yarn typedoc`, open html file)
# Why
This is a retry of #98. It has become more important in Expo's server application due to more things running within transactions. When it was written previously, relatively few things were transactional.
The idea is that within a transaction, subsequent reads should be able to be cached locally (dataloader) as long as the local cache is kept consistent (not in the cache adapter though).
# How
The way this is done is to keep a dataloader map (dataloader per-entity like we do for the top-level loads) per-transaction as well. Then, when in a transaction, instead of going directly to the database, go through the transaction's dataloader before hitting the database. This way loads within the transaction are coalesced and cached locally.
Invalidation becomes more complex though.
- We still only want to invalidate the non-transactional dataloader/cache at the end of the transaction (as late as possible for least chance of race condition) for all changed entities. This didn't change.
- As for when to invalidate the transactional dataloaders, we need to think about this like database sub-transaction visibility, but only in the sense that we don't want inconsistent reads. Put another way, any load in or out of any transaction or nested transaction needs to be consistent with what is in the database, so any time the loaded value would change if it went directly do the database we must also invalidate the cache.
- This is further complicated by the fact that postgres doesn't have real subtransactions (see #194 for more discussion) and subtransactions weren't an entity feature for the first attempt at this. This means that it's hard to test the case where a nested transaction is isolated from the parent transaction for uncommitted reads or the inverse.
- The way I came up with achieving this is to just to over-invalidate: clear all the dataloaders in the transaction recursively starting from the outermost transaction. And do this clearing right after the mutation and at the end of the sub-transaction and at the end of the transaction (recursively).
# Test Plan
Run new tests.
# Why
Noticed that this (major) dependency of entity has been updated. The [changelog](https://github.com/graphql/dataloader/blob/main/CHANGELOG.md) doesn't indicate anything breaking.
# How
Upgrade.
# Test Plan
Rely upon full test coverage for compatibility.
# Why
Step 2 of #201.
This PR abstracts out (genericizes) the load key and load value interfaces for batched/cached loading in preparation for composite field loading.
# How
Accomplishing this requires a number of things:
1. A set of interfaces for the keys and load values to be used to load entity field objects.
2. A concrete implementation of that interface for the current use case (single fieldName, fieldValue(s)).
3. Data structure changes in the adapters and data manager to handle the new key type not being comparable via `===`. (see base PR for new map data structure for using these objects as keys).
4. Updates to the test matchers to know how to compare the serializable holder objects.
# Test Plan
Update a number of tests and add a few new tests. This has full coverage.
# Why
This adds the utility classes needed for composite field loading as described in #201.
# How
This adds a few concepts:
- `areSetsEqual` - simple utility for checking set equality
- `SerializableKeyMap` - a map where keys must be of a custom interface for checking equality.
# Test Plan
Add new tests, run them. Full coverage.
* Add global mutation trigger field to EntityCompanionProvider
* Add globalMutationTrigger to additional test cases
* merge mutation triggers outside of EntityMutator
* Exclude triggers with empty arrays
Why
---
Currently the UUIDField type supports only UUIDv1-5 and to use a newer UUID version we need to use StringField.
How
---
Manually implemented UUID validation, adding support for versions 6, 7, and 8. Also added support for the "max" UUID, which is now part of the spec.
Test Plan
---
Added a UUIDv7 value (from https://uuid7.com/) to test cases.
* feat: add orderByRaw to querySelectionModifiers
* implement PR feedback
* fix tsc
* restrict orderByRaw to loadManyByRawWhereClauseAsync
* create new type for orderbyraw param
Node 16 is LTS now and 12 is unsupported. 18 is also now released and we will one day run on it so this makes sure one of our major dependencies works on Node 18.
All tests pass, no changes needed.
Why
---
There were some automated security notices about outdated npm package versions, many of which often can be addressed with minor dependency upgrades.
How
---
Deleted and regenerated yarn.lock, which affects this repo but not consumers of these packages. However, TypeScript 4.7 has a change where it considers stringifying a Symbol to be a potential error. This commit explicitly stringifies keys (entity field names).
I opted for `String(fieldName)` over constraining the field name with `N extends string & keyof TFields` because adding `string &` is "viral" in the sense that lots of files including users of Entity would need to add `string &`. Also, using Symbol field names probably would work fine, they just need to be stringified before printing them.
There were also some Koa router type changes that treat params as `string | undefined` so we need to use `!` in the Entity example router.
Test Plan
---
tsc + unit tests
Why: ioredis v5 ships with its own TypeScript declarations. The breaking changes from v4 to v5 affect the code that instantiates ioredis, not the code that uses it. As such, I believe that entity is still technically compatible with ioredis v4, but for the sake of keeping our test matrix smaller, this commit uses just ioredis v5 in tests and makes v5 a peer dependency of `entity-cache-adapter-redis`.
Additionally ioredis was a dependency of `entity-cache-adapter-redis`. I changed it to a devDependency and changed the import to `import type` since the package is used only for type checking.
ioredis v5.0.0 changelog: https://github.com/luin/ioredis/releases/tag/v5.0.0. Notable it drops support for Node 10 and for custom Promise libraries, neither of which we use.
Test Plan: building all packages and testing them with Lerna succeeds
Why: Expo's API server will use TS 4.6 and knex 1.0.5. This change makes it so that we run entity's CI tests against these new dependencies.
How: Upgraded the version of typescript in the root package.json. Modified yarn.lock to use knex 1.0.5 instead of updating the db adapter's package.json since knex 1.0.2 still works fine.
Test Plan: Lerna runs tsc in every package (no errors). Unit tests in CI.
Upgrades knex to 1.x internally. This changes the behavior of `returning` but not in a way that affects how the knex adapter uses it: `returning('*')` behaves the same as before.
This does, however, change the TypeScript declarations for knex, which changed from:
```
Knex<TRecord extends {} = any, TResult = unknown[]>
```
to:
```
Knex<TRecord extends {} = any, TResult = Record<string, any>[]>
```
* Add @expo/entity-ip-address-field
- adds an entity field for IP addresses, validates both v4 and v6 type IPs
- ip address field functionality moved to its own package
- specified `@expo/entity` as a dev & peer dependency
Why
To better represent Postgres' type system, deprecated the type NumberField and replaced with IntField and FloatField.
How
Added the new types to EntityFields.ts. To release, will need to bump minor version so breaking changes aren't propagated.
Test Plan
Update tests using the NumberField to use IntField.
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
This is Entity, a privacy-aware data layer for defining, caching, and authorizing access to application data models.
## Essential Development Commands
### Dependencies
-`yarn add` - Add a dependency. Run this only if necessary and ask before running.
### Building and Linting
-`yarn tsc` - Typecheck the code
-`yarn lint` - Run oxlint on source code
-`yarn format` - Format code with oxfmt
-`yarn format-check` - Check formatting without writing
-`yarn ctix` - Build barrel index.ts files
### Testing
-`yarn test` - Run unit tests
-`yarn integration` - Run integration tests against dockerized environment
### Commit messages
- All PRs use conventional commits for their titles: https://www.conventionalcommits.org/
## Code conventions
- Backwards compatibility is a non-goal of refactorings of this library. Consumers rely upon typescript to know what needs to be migrated, so all breaking changes should be detectable via running typescript post-entity-version-upgrade in a consumer application that makes use of this package.
- Do not use jest's `test.skip`
- Prefer not to use dynamic imports for now
- Whenever technically correct, use typescript types for something instead of `any`
- Prefer American English spellings of words over British English. For example, prefer "Adapter" over "Adaptor".
- add ID-based and field-based knex database loaders for use with locking modifiers ([#706](https://github.com/expo/entity/issues/706)) ([e87e2a3](https://github.com/expo/entity/commit/e87e2a3a28f4279f5911bfd0189964b94a2ced7f))
- only throw EntityDatabaseAdapterPaginationCursorInvalidError from cursor decoding logic ([#537](https://github.com/expo/entity/issues/537)) ([e3cee07](https://github.com/expo/entity/commit/e3cee07091af8d1e6c5bb2d82f13e837c7658ea9))
- remove unused arg from EntityInvalidationUtils ([#536](https://github.com/expo/entity/issues/536)) ([0a1d1b5](https://github.com/expo/entity/commit/0a1d1b5972c15c932f5c31015f2519163f882f83))
- **deps:** update dependency graphql to v16.13.1 ([#510](https://github.com/expo/entity/issues/510)) ([1a8535f](https://github.com/expo/entity/commit/1a8535f983f38aaa462a8d587e2f62f5fab1043c))
### Features
- update the SQLExpression and SQLFragmentHelpers APIs ([#516](https://github.com/expo/entity/issues/516)) ([112d4dc](https://github.com/expo/entity/commit/112d4dc9b379da5317ff6acaf1d2e9bd597128a6))
- Add SQLExpression class that supports fluent comparison method API ([#506](https://github.com/expo/entity/issues/506)) ([535b681](https://github.com/expo/entity/commit/535b6811e08578aba1aadb095f3e401cfe7eaa26))
- **deps:** update dependency @koa/router to v15.3.1 ([#497](https://github.com/expo/entity/issues/497)) ([72b3c80](https://github.com/expo/entity/commit/72b3c80d12a1afa3c5593bcf6d7ad233c3524341))
### Features
- add support to SQLFragment for `= ANY(?)` ([#498](https://github.com/expo/entity/issues/498)) ([b931daa](https://github.com/expo/entity/commit/b931daa9a082cdb33f4559d6d32e1655f0459b79))
- escape all matching portions of parts of cacher cache keys ([#486](https://github.com/expo/entity/issues/486)) ([21f2f6f](https://github.com/expo/entity/commit/21f2f6f461ee771e8b1c1b22dbaf9b275094c0db))
- fix some security review suggestions ([#485](https://github.com/expo/entity/issues/485)) ([e263bd2](https://github.com/expo/entity/commit/e263bd218cda579365766d0918c4961304bd1d9f))
- Add default for TSelectedFields for application-instantiated types ([#438](https://github.com/expo/entity/issues/438)) ([3d4aa0e](https://github.com/expo/entity/commit/3d4aa0e9efa519a36f3db69bd8f117a993c100fa))
- Apply same viewer context subclass type mechanism to knexLoader and related methods ([#472](https://github.com/expo/entity/issues/472)) ([018a40b](https://github.com/expo/entity/commit/018a40bc01d579ffbf88f308e641921e6fc39097))
- correct discriminated union type for forward/backward pagination ([#474](https://github.com/expo/entity/issues/474)) ([90ae6e2](https://github.com/expo/entity/commit/90ae6e299bda26ac9a80d83e83334d4152c022dd))
- correct pagination behavior for standard pagination with descending ordering clauses ([#477](https://github.com/expo/entity/issues/477)) ([e32c4c2](https://github.com/expo/entity/commit/e32c4c2bcc05f225a556f95c90f1ee0f4e24de74))
- **deps:** update dependency @apollo/server to v5.4.0 [security] ([#420](https://github.com/expo/entity/issues/420)) ([cd81c5a](https://github.com/expo/entity/commit/cd81c5a5b6be43ef60248eeea4031a883dd1eae8))
- **deps:** update dependency @koa/bodyparser to v6.1.0 ([#405](https://github.com/expo/entity/issues/405)) ([2dda22d](https://github.com/expo/entity/commit/2dda22d761f87851e3fcccc8443597daf8ac42e8))
- **deps:** update dependency @koa/router to v15.3.0 ([#406](https://github.com/expo/entity/issues/406)) ([9e88cf6](https://github.com/expo/entity/commit/9e88cf6aab76902c65994d28de8dab73f1267021))
- **deps:** update dependency graphql to v16.13.0 ([#479](https://github.com/expo/entity/issues/479)) ([976e2c3](https://github.com/expo/entity/commit/976e2c33867da71a2920d050c4086e2af8f6d54d))
- **deps:** update dependency koa to v3.1.2 [security] ([#484](https://github.com/expo/entity/issues/484)) ([b258dad](https://github.com/expo/entity/commit/b258dad2ed69c473f04d57d26910eab2032d142c))
- eliminate some unnecessary any casts ([#448](https://github.com/expo/entity/issues/448)) ([cc78029](https://github.com/expo/entity/commit/cc78029117c14510b0934b5e0d19876a45570f8d))
- explicitly document behavior when cursor entity no longer exists ([#453](https://github.com/expo/entity/issues/453)) ([4dc156c](https://github.com/expo/entity/commit/4dc156c7a8dc50bc9e2802be133b9e258b289f54)), closes [#422](https://github.com/expo/entity/issues/422) [#431](https://github.com/expo/entity/issues/431)
- move EntityPrivacyUtils back into core package ([#429](https://github.com/expo/entity/issues/429)) ([56ec27d](https://github.com/expo/entity/commit/56ec27d5f201d83930a2cc05dfc380a054bff1e1))
- pass in augmentable classes to installation ([#437](https://github.com/expo/entity/issues/437)) ([f5c728b](https://github.com/expo/entity/commit/f5c728be565e01c0a8ace9ef51f6de8f49184dab)), closes [/github.com/expo/entity/pull/410#pullrequestreview-3765893898](https://github.com//github.com/expo/entity/pull/410/issues/pullrequestreview-3765893898)
- refactor install method to instead be free functions ([#441](https://github.com/expo/entity/issues/441)) ([51d632d](https://github.com/expo/entity/commit/51d632da1619dc039d5c41ce6533f63254ec4337)), closes [#410](https://github.com/expo/entity/issues/410)
- Reorganize constructionUtils to make non-public ([#454](https://github.com/expo/entity/issues/454)) ([d9377a2](https://github.com/expo/entity/commit/d9377a290b7e0c6803f9e83537eddb7204bc9320)), closes [#410](https://github.com/expo/entity/issues/410)
- revert change to codemod now that Entity.knexLoader is supported again via subclass ([#443](https://github.com/expo/entity/issues/443)) ([ed6b2e0](https://github.com/expo/entity/commit/ed6b2e05539c77ee2abd2c6da74e2a451967d06e)), closes [#441](https://github.com/expo/entity/issues/441)
- Type idField in EntityConfiguration as TIDField ([#425](https://github.com/expo/entity/issues/425)) ([be40bcf](https://github.com/expo/entity/commit/be40bcffa6e7db9b7ad75e1a80bbb48ff417c6c5))
- update integration test to use BlahEntity.knexLoader syntax ([#444](https://github.com/expo/entity/issues/444)) ([457f58a](https://github.com/expo/entity/commit/457f58ad2c4e1cf09f3132f3b9af5008ba0290c7)), closes [#441](https://github.com/expo/entity/issues/441)
- Use TSelectedFields for knex loader order by method ([#424](https://github.com/expo/entity/issues/424)) ([a9d09f4](https://github.com/expo/entity/commit/a9d09f42b67378d06133bcfb5b99c9afb17aac0d))
### Features
- Add authorization action to EntityPrivacyPolicyRuleEvaluationContext ([#464](https://github.com/expo/entity/issues/464)) ([e71ae5f](https://github.com/expo/entity/commit/e71ae5fcbae95096e0d6a42a8c960896ab83ccfb))
- Add ilike and trigram similarity search to pagination ([#431](https://github.com/expo/entity/issues/431)) ([f72cbca](https://github.com/expo/entity/commit/f72cbcaf5a722e6bb8299644ec32b07f1d3362a9)), closes [#422](https://github.com/expo/entity/issues/422)
- add method to get pagination cursor for single entity ([#475](https://github.com/expo/entity/issues/475)) ([898f71c](https://github.com/expo/entity/commit/898f71ce082c2593585575b60bd6ef2546717124))
- Add paginated loader to entity-database-adapter-knex ([#422](https://github.com/expo/entity/issues/422)) ([58deffd](https://github.com/expo/entity/commit/58deffd426b0214073ede608cd47c04db2596d60))
- Add pagination max page size configuration ([#436](https://github.com/expo/entity/issues/436)) ([3ec4b5f](https://github.com/expo/entity/commit/3ec4b5f0cd53bf18d6bcfa27dd29b70d3f4d5f19))
- Add raw sqlfragment orderBy capability for pagination ([#457](https://github.com/expo/entity/issues/457)) ([0615ffa](https://github.com/expo/entity/commit/0615ffacffbe9e903a9bf03b171c102f0554b992))
- add support for nulls ordering in order by clauses ([#468](https://github.com/expo/entity/issues/468)) ([6c0838d](https://github.com/expo/entity/commit/6c0838dc577a3b6190e3551ee834925d7bd53bc6))
- support nullable search fields with postgres transforms ([#470](https://github.com/expo/entity/issues/470)) ([53b0604](https://github.com/expo/entity/commit/53b0604152b8ca5ad9fc616d18478f93473c7eb0))
- **deps:** pin dependency @apollo/server to 4.12.2 ([#330](https://github.com/expo/entity/issues/330)) ([7038912](https://github.com/expo/entity/commit/7038912ca5e5b203ebbca4b46bda5612e7660eac))
- **deps:** pin dependency @koa/bodyparser to 6.0.0 ([#339](https://github.com/expo/entity/issues/339)) ([9f93926](https://github.com/expo/entity/commit/9f939269cf843059ec0ac729d470db074730348e))
- **deps:** pin dependency koa to 2.16.3 ([#331](https://github.com/expo/entity/issues/331)) ([ce55922](https://github.com/expo/entity/commit/ce55922e3ec6692a9ea4847427dc475902174830))
- Inline map().filter() loops in EntityLoaderUtils ([#369](https://github.com/expo/entity/issues/369)) ([ea6c128](https://github.com/expo/entity/commit/ea6c128d9c8fc21484645796f79e1aa8839dd395))
- run tsc during tests CI job ([#373](https://github.com/expo/entity/issues/373)) ([1cc57ed](https://github.com/expo/entity/commit/1cc57eddce72663cff17a5102afa6c3e8248aee4))
### Features
- synthetically set cascading set-null fields to null during canViewerDeleteAsync evaluation ([#376](https://github.com/expo/entity/issues/376)) ([544bb7c](https://github.com/expo/entity/commit/544bb7c3f2f41419cb3b779f7e3d968e09455fa0))
- switch local cache to an interface ([#322](https://github.com/expo/entity/issues/322)) ([c453726](https://github.com/expo/entity/commit/c4537262d810ea01e519f9aec6ae5b17a54ce936))
- add default for TSelectedFields in PrivacyPolicyTestUtils Case ([#316](https://github.com/expo/entity/issues/316)) ([2f9d487](https://github.com/expo/entity/commit/2f9d4873aaefe891920c25a41088f7c467da6113))
- make in memory and stub database adapters behave more similarly to sql ([#315](https://github.com/expo/entity/issues/315)) ([0f53c24](https://github.com/expo/entity/commit/0f53c24faff8a994906e630d7f17d67d89ff5b0f)), closes [#311](https://github.com/expo/entity/issues/311)
- add readonly modifier to partitionArray utility method ([#307](https://github.com/expo/entity/issues/307)) ([077d53c](https://github.com/expo/entity/commit/077d53cd86d4c54132ea2ebcc59d1d75e2b507d0))
- validate that UUID field is lower-case ([#310](https://github.com/expo/entity/issues/310)) ([949f00d](https://github.com/expo/entity/commit/949f00d93b52e405188b9b23e23ae59ce1c546de)), closes [/github.com/expo/entity/blob/077d53cd86d4c54132ea2ebcc59d1d75e2b507d0/packages/entity/src/EntityDatabaseAdapter.ts#L182](https://github.com//github.com/expo/entity/blob/077d53cd86d4c54132ea2ebcc59d1d75e2b507d0/packages/entity/src/EntityDatabaseAdapter.ts/issues/L182) [#309](https://github.com/expo/entity/issues/309)
### Features
- add more documentation to entity errors ([#312](https://github.com/expo/entity/issues/312)) ([4c3fd17](https://github.com/expo/entity/commit/4c3fd175a91cf3107b1ac413e39e52e051a9dc4b))
- throw specific errors for database adapter issues ([#311](https://github.com/expo/entity/issues/311)) ([f6639f9](https://github.com/expo/entity/commit/f6639f9def4ac20b628dd48fdca8d7cf61b6347b))
- add support for Buffer fields ([#305](https://github.com/expo/entity/issues/305)) ([31b52a5](https://github.com/expo/entity/commit/31b52a5f75842f4810f66c12ad03a10d9192f889))
- allow manual specification of cascadingDeleteCause in delete mutator factory method ([#306](https://github.com/expo/entity/issues/306)) ([4a3eda2](https://github.com/expo/entity/commit/4a3eda209fa747ee093ca3053b3196bf9a52193d))
- add ability to define a custom cache key invalidation version supplier ([#290](https://github.com/expo/entity/issues/290)) ([a3ab61b](https://github.com/expo/entity/commit/a3ab61bfa8d2eab3bbd9c703c5e483184bb67f13))
- add createOrGetExistingAsync and createWithUniqueConstraintRecoveryAsync utility methods ([#291](https://github.com/expo/entity/issues/291)) ([78bc264](https://github.com/expo/entity/commit/78bc264b3328692b4b6722388a57d298f02e65c5))
- introduce transactional dataloader mode and fix global transaction ID generation ([#289](https://github.com/expo/entity/issues/289)) ([d3e89bd](https://github.com/expo/entity/commit/d3e89bdd82e6df5c5b35522bc3574fd9e1cfddff))
- fix metrics for dataloader waterfall logging ([#267](https://github.com/expo/entity/issues/267)) ([1d95eba](https://github.com/expo/entity/commit/1d95eba1f9efb67164c3affc917c9b2514e1529f))
- move source of truth of loader docblocks to enforcing loader since it is not the default loader ([#264](https://github.com/expo/entity/issues/264)) ([aa56b70](https://github.com/expo/entity/commit/aa56b705db115cf760125208eb2d3585a3794e58))
- add composite field loading and caching ([#272](https://github.com/expo/entity/issues/272)) ([f0aa0da](https://github.com/expo/entity/commit/f0aa0dafebdb56418cbd22dda437233d850db3e7))
- add some utility classes and methods in preparation for composite keys ([#269](https://github.com/expo/entity/issues/269)) ([ad41af1](https://github.com/expo/entity/commit/ad41af1c826fb267f5e463771107c85802578a19)), closes [#201](https://github.com/expo/entity/issues/201)
- change entity id field generic to field name and derive type where necessary ([#278](https://github.com/expo/entity/issues/278)) ([b7e524c](https://github.com/expo/entity/commit/b7e524c41892797608d0b884410b8d520a80a9ef))
- enforce explicit id field cache property ([#276](https://github.com/expo/entity/issues/276)) ([1da5cc0](https://github.com/expo/entity/commit/1da5cc01e1affc3b3338a0ab2050504b5853ac6f))
- invalidate n+/-1 cacheKeyVersion for entities for push safety ([#275](https://github.com/expo/entity/issues/275)) ([d9c1852](https://github.com/expo/entity/commit/d9c1852706f98f0761ff24ddd244e4ed2beca580))
- feat!: Default all loaders and mutators to enforcing (#256) ([a976e76](https://github.com/expo/entity/commit/a976e765098bca27049585e43a3bed5c71b9ac6b)), closes [#256](https://github.com/expo/entity/issues/256)
### Features
- Create codemod for v0.40.0 -> v0.41.0 ([#260](https://github.com/expo/entity/issues/260)) ([4a2b61f](https://github.com/expo/entity/commit/4a2b61f2bd8edfc19109ef4c8ac780eaf0f7795c))
- add function to get authorization results for canViewerUpdateAsync/canViewerDeleteAsync ([#249](https://github.com/expo/entity/issues/249)) ([27a358d](https://github.com/expo/entity/commit/27a358d259503aa8d7d7413bfc1e52e0974dc626))
- make using non-enforcing entity loader explicit ([#238](https://github.com/expo/entity/issues/238)) ([2edc7af](https://github.com/expo/entity/commit/2edc7af7e20c362688ff2ee674fa2245205d6de4))
### Bug Fixes
- always reload entity after update since cascading changes may have changed it since commit ([#233](https://github.com/expo/entity/issues/233)) ([7c3c985](https://github.com/expo/entity/commit/7c3c9854a4dd91d4b73ebdb18bdeadea8b63f4c7))
- constrain entity fields type to string-keyed object ([#235](https://github.com/expo/entity/issues/235)) ([7e2cea1](https://github.com/expo/entity/commit/7e2cea16973a0ae1917f867cd25d6ef7c8eaecef))
- disallow keys of JS Object prototype for safety ([#236](https://github.com/expo/entity/issues/236)) ([05726d4](https://github.com/expo/entity/commit/05726d45fa9b91a6b25ddbc9c7e3e097b02097ca))
### Features
- Add global mutation trigger field to EntityCompanionProvider ([#215](https://github.com/expo/entity/issues/215)) ([6569486](https://github.com/expo/entity/commit/6569486ecddf2fd33c626f634c761e06636b1730))
- add previousValue to privacy policy context for updates ([#232](https://github.com/expo/entity/issues/232)) ([af495a9](https://github.com/expo/entity/commit/af495a9a856a5042f00cfd150348e415139cb85e))
- Add StrictEnumField with better validation ([#222](https://github.com/expo/entity/issues/222)) ([8753252](https://github.com/expo/entity/commit/87532525d090b3a67f1cedd95ac990f7dcb3ab5d))
- make canViewerDeleteAsync recursive ([#224](https://github.com/expo/entity/issues/224)) ([60fc9a4](https://github.com/expo/entity/commit/60fc9a4c4b587b866f2f5a1b3bd22145bc77e6e9))
- Use uuid v7 in stub database adapter ([#234](https://github.com/expo/entity/issues/234)) ([1e8ea64](https://github.com/expo/entity/commit/1e8ea64017430a2ce5ea197678bd78713ad77ecd))
- move entity loader utils into their own object ([#239](https://github.com/expo/entity/issues/239))([93905be](https://github.com/expo/entity/commit/93905be81cf74c5626ca19358794261c17223f95))
- remove unused field from redis configuration ([#213](https://github.com/expo/entity/issues/213)) ([503a2fb](https://github.com/expo/entity/commit/503a2fb6a8d57f6a09f6da99daf5ab92fc3e7945))
### Features
- add ability to specify knex transaction config ([#207](https://github.com/expo/entity/issues/207)) ([2069a0d](https://github.com/expo/entity/commit/2069a0d9fd1d8f7d090a71a90a758b2429ea5dd3))
- add example of redis mget batching using @expo/batcher ([#199](https://github.com/expo/entity/issues/199)) ([684f91b](https://github.com/expo/entity/commit/684f91b1f270851dbe331df2e2620664eea17266))
- change redis cache adapter to use redis-like interface ([#200](https://github.com/expo/entity/issues/200)) ([e381f5b](https://github.com/expo/entity/commit/e381f5b2acfc17dcade2cf6e8506fa1c1c255e73))
- update ioredis to v5, make it a devDependency of the cache packages ([#172](https://github.com/expo/entity/issues/172)) ([9fcfbf7](https://github.com/expo/entity/commit/9fcfbf746a61554cefedf7fb0f1a3a9056b58ffe))
- add composed cache to index ([#166](https://github.com/expo/entity/issues/166)) ([5c82edc](https://github.com/expo/entity/commit/5c82edcd9f26854878d29950a05852e117bbd723))
- Move data transforming responsibilities to the CacheAdapter instead of the ReadThroughEntityCache. Affects any classes extending the EntityCacheAdapter and relying on the transformation behavior of the ReadThroughEntityCache. ([#153](https://github.com/expo/entity/pull/153))
- Upgrade knex to 1.x in `@expo/entity-database-adapter-knex` ([#150](https://github.com/expo/entity/issues/150)) ([e1b0ee5](https://github.com/expo/entity/commit/e1b0ee528175646239d88aa71ffce73983eeb52f))
- separate post-commit invalidation callbacks from trigger and user callbacks ([#144](https://github.com/expo/entity/issues/144)) ([efa131e](https://github.com/expo/entity/commit/efa131ef29532accf0e51b6d867ffe15a27b4c90))
### Features
- add cascading deletion info ([#145](https://github.com/expo/entity/issues/145)) ([3727191](https://github.com/expo/entity/commit/372719176742d3ead6b7cbca29cb66b09b3fe09c))
- add IP support ([#138](https://github.com/expo/entity/issues/138)) ([1405a91](https://github.com/expo/entity/commit/1405a9126ee3a56c3e2a38bf3b697283744f5b5d))
- expose describeFieldTestCase for use in other packages ([#139](https://github.com/expo/entity/issues/139)) ([1220477](https://github.com/expo/entity/commit/1220477aa331349b482ad015b44f76fa2bda7b90))
- upgrade TypeScript to 4.4 ([#134](https://github.com/expo/entity/issues/134)) ([7612392](https://github.com/expo/entity/commit/7612392dbfd9778d25c465c1626c372d75a6d05a))
- use better QueryContext types for transactional triggers and validators ([#136](https://github.com/expo/entity/issues/136)) ([5c7efe1](https://github.com/expo/entity/commit/5c7efe1678424ff907687e9327175bdc2538c3e0))
- allow null field values in loadManyByFieldEqualityConjunctionAsync ([#130](https://github.com/expo/entity/issues/130)) ([2f37dc6](https://github.com/expo/entity/commit/2f37dc6a37d368b12b3c8375e04f0777fd448797))
- upgrade ioredis, knex, and pg ([#125](https://github.com/expo/entity/issues/125)) ([7c43edf](https://github.com/expo/entity/commit/7c43edf3de37e8af13e2a99bd44f03f54803d9b9))
- upgrade some other packages to fix dependabot alerts ([#126](https://github.com/expo/entity/issues/126)) ([226bffe](https://github.com/expo/entity/commit/226bffe0a95d7b8a3e9e9d46aebdb0edf1eeec1a))
- use column name instead of field name for redis cache key ([#124](https://github.com/expo/entity/issues/124)) ([d78f452](https://github.com/expo/entity/commit/d78f452bb3ac5527069813a03f5b2264375bd8ab))
- mutation validator and trigger cyclic import structure ([#118](https://github.com/expo/entity/issues/118)) ([b11dbc0](https://github.com/expo/entity/commit/b11dbc087ba5ad13c748032698dd73279b7960b0))
### Features
- add mutation type to validator and trigger execute functions ([#119](https://github.com/expo/entity/issues/119)) ([ef40fa5](https://github.com/expo/entity/commit/ef40fa5e9cdf53e25e78a9423c698e6d1c9737af))
- allow undefined value for all fields ([#114](https://github.com/expo/entity/issues/114)) ([022e0e9](https://github.com/expo/entity/commit/022e0e9b88a5e84e0c1bc6c8810c850beb05f0cf))
### Features
- add basic field type runtime validators ([#113](https://github.com/expo/entity/issues/113)) ([6c4d4b0](https://github.com/expo/entity/commit/6c4d4b03b5404dd776a12c5eb6f0af77a8c964f8))
- move runInTransaction to ViewerContext ([#108](https://github.com/expo/entity/issues/108)) ([b7309e1](https://github.com/expo/entity/commit/b7309e18a2dd780b64cd306d566de1f12bf8df3a))
- remove cache and database adaptor flavor enums ([#109](https://github.com/expo/entity/issues/109)) ([72a77f8](https://github.com/expo/entity/commit/72a77f8b6893a4b76d3b1cce16659bdf3ce473ee))
### Features
- better typing on filterMap for use with predicates ([#106](https://github.com/expo/entity/issues/106)) ([01b3bfe](https://github.com/expo/entity/commit/01b3bfef7c6da55e7d89d8cccb52a0050187aee8))
- add type generic default in test utils ([#104](https://github.com/expo/entity/issues/104)) ([fe2917e](https://github.com/expo/entity/commit/fe2917e6b5af808f166aa354d476e168c41e6d21))
- remove any cast from EntityLoader ([#100](https://github.com/expo/entity/issues/100)) ([b8e07f9](https://github.com/expo/entity/commit/b8e07f9ddc4077768980c000d61f5ddcc824e2e3))
- add ability to invalidate an entity manually ([#91](https://github.com/expo/entity/issues/91)) ([d1a6f13](https://github.com/expo/entity/commit/d1a6f1339648942f12b3fb605eb329aefe5cdd63))
- correct type of runInTransactionAsync ([#81](https://github.com/expo/entity/issues/81)) ([f98f7d1](https://github.com/expo/entity/commit/f98f7d14b6149a496a7a3e97dd63d89b2842cc26))
- fix incorrect loader types for field selecitons ([#72](https://github.com/expo/entity/issues/72)) ([cd2df8d](https://github.com/expo/entity/commit/cd2df8da4b81bcd4769e950d3124c18886df2816))
- fix index export for EntityMutationTrigger and EntityMutationValidator ([#73](https://github.com/expo/entity/issues/73)) ([7e829f3](https://github.com/expo/entity/commit/7e829f30fde0bd343cb8a584ccda5530cf61ec33))
- move cache invalidation to after transaction commit ([#77](https://github.com/expo/entity/issues/77)) ([dbc3c81](https://github.com/expo/entity/commit/dbc3c81b359a668b5134189ce6de49b5893bb5e7))
- ensure transitive deletion triggers are run for CASCADE_DELETE_INVALIDATE_CACHE ([#66](https://github.com/expo/entity/issues/66)) ([5744c51](https://github.com/expo/entity/commit/5744c51e6b3178bd9f229f3f465b683bb4474c8a))
- separate out EntityMutationValidator ([#68](https://github.com/expo/entity/issues/68)) ([547a1ef](https://github.com/expo/entity/commit/547a1efcecd17cc085702d1a3e9888ce5b644b13))
- update setField to correct typing ([#62](https://github.com/expo/entity/issues/62)) ([7a77afc](https://github.com/expo/entity/commit/7a77afc83ea732c9b062a5d6865eff9d3131d015))
- correct some typing issues and exports ([#57](https://github.com/expo/entity/issues/57)) ([e91cfba](https://github.com/expo/entity/commit/e91cfba99321c2c4da2078aafd8627cbf0ceca50))
# 0.5.0 (2020-07-01)
### Bug Fixes
- support number ID field generation in StubDatabaseAdapter ([#43](https://github.com/expo/entity/issues/43)) ([60cc0fb](https://github.com/expo/entity/commit/60cc0fbda460c3ada1f5244a2780f90382fe3ebd))
- upgrade Apollo server for security advisory ([#38](https://github.com/expo/entity/issues/38)) ([d25c1e1](https://github.com/expo/entity/commit/d25c1e142d52385725593f4f155d1795697ab490))
### Features
- add two fan out methods to association loader ([#52](https://github.com/expo/entity/issues/52)) ([89cfb3d](https://github.com/expo/entity/commit/89cfb3d5a01d4da90f64acc52ae1b839ba348a35))
- support entity fields subset of db fields ([#49](https://github.com/expo/entity/issues/49)) ([4e40b2e](https://github.com/expo/entity/commit/4e40b2e521407e521d236978ec3b3b56db3990be))
@@ -71,6 +67,7 @@ async function get_photo_page(viewer: ViewerContext): string {
## Use Case
Entity is not limited in where it can or should be used, but was designed for use in a [Koa](https://koajs.com/)-like environment with a request and response. At Expo, we use Entity in the following manner:
1. A request comes into Koa router
1. Middleware initializes the Entity framework for the request
1. A `ViewerContext` is created identifying the individual making the request.
@@ -78,6 +75,25 @@ Entity is not limited in where it can or should be used, but was designed for us
Note: The entity framework instance should not be shared across multiple requests since it contains a unique memoized [Dataloader](https://github.com/graphql/dataloader#class-dataloader). A long-lived instance is prone to data synchronization issues, especially when the application is scaled horizontally and multiple shared caches would exist for the same data.
## Contributing
We control development tooling versions using [mise-en-place](https://mise.jdx.dev). If you have it installed, then:
- Running `mise run ci` will run all tests that our CI environment will run.
- Running `mise t` will show a list of mise tasks you can run with `mise run <name>`.
- Running `mise en .` will enter a shell environment which includes node and yarn.
If you don't have `mise` installed, [you can install it](https://mise.jdx.dev/installing-mise.html#https-mise-run) as easily as running `curl https://mise.run/zsh | sh`.
- escape all matching portions of parts of cacher cache keys ([#486](https://github.com/expo/entity/issues/486)) ([21f2f6f](https://github.com/expo/entity/commit/21f2f6f461ee771e8b1c1b22dbaf9b275094c0db))
- switch local cache to an interface ([#322](https://github.com/expo/entity/issues/322)) ([c453726](https://github.com/expo/entity/commit/c4537262d810ea01e519f9aec6ae5b17a54ce936))
- add composite field loading and caching ([#272](https://github.com/expo/entity/issues/272)) ([f0aa0da](https://github.com/expo/entity/commit/f0aa0dafebdb56418cbd22dda437233d850db3e7))
- change entity id field generic to field name and derive type where necessary ([#278](https://github.com/expo/entity/issues/278)) ([b7e524c](https://github.com/expo/entity/commit/b7e524c41892797608d0b884410b8d520a80a9ef))
- feat!: Default all loaders and mutators to enforcing (#256) ([a976e76](https://github.com/expo/entity/commit/a976e765098bca27049585e43a3bed5c71b9ac6b)), closes [#256](https://github.com/expo/entity/issues/256)
- constrain entity fields type to string-keyed object ([#235](https://github.com/expo/entity/issues/235)) ([7e2cea1](https://github.com/expo/entity/commit/7e2cea16973a0ae1917f867cd25d6ef7c8eaecef))
Cross-request [LRU](https://github.com/isaacs/node-lru-cache) cache adapter for `@expo/entity`. Use
this cache with caution - it is nonstandard. The cache is shared between requests in the node process.
Note: This uses version 6.0.0 of `node-lru-cache` since it the most tuned version for our use case (low TTL + LRU). Upgrading
to 7.x will cause high memory usage for the entity cache adapter use case since it allocates fixed-size data structures up front to tune for the non-TTL use case: https://github.com/isaacs/node-lru-cache/issues/208.
- escape all matching portions of parts of cacher cache keys ([#486](https://github.com/expo/entity/issues/486)) ([21f2f6f](https://github.com/expo/entity/commit/21f2f6f461ee771e8b1c1b22dbaf9b275094c0db))
- eliminate some unnecessary any casts ([#448](https://github.com/expo/entity/issues/448)) ([cc78029](https://github.com/expo/entity/commit/cc78029117c14510b0934b5e0d19876a45570f8d))
- add support for Buffer fields ([#305](https://github.com/expo/entity/issues/305)) ([31b52a5](https://github.com/expo/entity/commit/31b52a5f75842f4810f66c12ad03a10d9192f889))
- add ability to define a custom cache key invalidation version supplier ([#290](https://github.com/expo/entity/issues/290)) ([a3ab61b](https://github.com/expo/entity/commit/a3ab61bfa8d2eab3bbd9c703c5e483184bb67f13))
- add composite field loading and caching ([#272](https://github.com/expo/entity/issues/272)) ([f0aa0da](https://github.com/expo/entity/commit/f0aa0dafebdb56418cbd22dda437233d850db3e7))
- change entity id field generic to field name and derive type where necessary ([#278](https://github.com/expo/entity/issues/278)) ([b7e524c](https://github.com/expo/entity/commit/b7e524c41892797608d0b884410b8d520a80a9ef))
- feat!: Default all loaders and mutators to enforcing (#256) ([a976e76](https://github.com/expo/entity/commit/a976e765098bca27049585e43a3bed5c71b9ac6b)), closes [#256](https://github.com/expo/entity/issues/256)
- constrain entity fields type to string-keyed object ([#235](https://github.com/expo/entity/issues/235)) ([7e2cea1](https://github.com/expo/entity/commit/7e2cea16973a0ae1917f867cd25d6ef7c8eaecef))
- remove unused field from redis configuration ([#213](https://github.com/expo/entity/issues/213)) ([503a2fb](https://github.com/expo/entity/commit/503a2fb6a8d57f6a09f6da99daf5ab92fc3e7945))
- add example of redis mget batching using @expo/batcher ([#199](https://github.com/expo/entity/issues/199)) ([684f91b](https://github.com/expo/entity/commit/684f91b1f270851dbe331df2e2620664eea17266))
- change redis cache adapter to use redis-like interface ([#200](https://github.com/expo/entity/issues/200)) ([e381f5b](https://github.com/expo/entity/commit/e381f5b2acfc17dcade2cf6e8506fa1c1c255e73))
- update ioredis to v5, make it a devDependency of the cache packages ([#172](https://github.com/expo/entity/issues/172)) ([9fcfbf7](https://github.com/expo/entity/commit/9fcfbf746a61554cefedf7fb0f1a3a9056b58ffe))
- upgrade ioredis, knex, and pg ([#125](https://github.com/expo/entity/issues/125)) ([7c43edf](https://github.com/expo/entity/commit/7c43edf3de37e8af13e2a99bd44f03f54803d9b9))
- use column name instead of field name for redis cache key ([#124](https://github.com/expo/entity/issues/124)) ([d78f452](https://github.com/expo/entity/commit/d78f452bb3ac5527069813a03f5b2264375bd8ab))
**Note:** Version bump only for package @expo/entity-cache-adapter-redis
# 0.5.0 (2020-07-01)
### Features
- support entity fields subset of db fields ([#49](https://github.com/expo/entity/issues/49)) ([4e40b2e](https://github.com/expo/entity/commit/4e40b2e521407e521d236978ec3b3b56db3990be))
`GenericRedisCacheContext.invalidationConfig.cacheKeyVersionsToInvalidateFn returned an empty list for cacheKeyVersion ${this.entityConfiguration.cacheKeyVersion}; this would silently disable invalidation`,
- refactor install method to instead be free functions ([#441](https://github.com/expo/entity/issues/441)) ([51d632d](https://github.com/expo/entity/commit/51d632da1619dc039d5c41ce6533f63254ec4337)), closes [#410](https://github.com/expo/entity/issues/410)
- revert change to codemod now that Entity.knexLoader is supported again via subclass ([#443](https://github.com/expo/entity/issues/443)) ([ed6b2e0](https://github.com/expo/entity/commit/ed6b2e05539c77ee2abd2c6da74e2a451967d06e)), closes [#441](https://github.com/expo/entity/issues/441)
A package containing jscodeshift codemods for @expo/entity upgrades.
Codemods are transformations that run on your codebase programmatically. This allows for a large amount of changes to be applied without having to manually go through every file.
## Usage
These should be used via the [jscodeshift CLI](https://github.com/facebook/jscodeshift?tab=readme-ov-file#usage-cli).
- move EntityPrivacyUtils back into core package ([#429](https://github.com/expo/entity/issues/429)) ([56ec27d](https://github.com/expo/entity/commit/56ec27d5f201d83930a2cc05dfc380a054bff1e1))
- pass in augmentable classes to installation ([#437](https://github.com/expo/entity/issues/437)) ([f5c728b](https://github.com/expo/entity/commit/f5c728be565e01c0a8ace9ef51f6de8f49184dab)), closes [/github.com/expo/entity/pull/410#pullrequestreview-3765893898](https://github.com//github.com/expo/entity/pull/410/issues/pullrequestreview-3765893898)
- refactor install method to instead be free functions ([#441](https://github.com/expo/entity/issues/441)) ([51d632d](https://github.com/expo/entity/commit/51d632da1619dc039d5c41ce6533f63254ec4337)), closes [#410](https://github.com/expo/entity/issues/410)
- Add raw sqlfragment orderBy capability for pagination ([#457](https://github.com/expo/entity/issues/457)) ([0615ffa](https://github.com/expo/entity/commit/0615ffacffbe9e903a9bf03b171c102f0554b992))
- add support for nulls ordering in order by clauses ([#468](https://github.com/expo/entity/issues/468)) ([6c0838d](https://github.com/expo/entity/commit/6c0838dc577a3b6190e3551ee834925d7bd53bc6))
- add ID-based and field-based knex database loaders for use with locking modifiers ([#706](https://github.com/expo/entity/issues/706)) ([e87e2a3](https://github.com/expo/entity/commit/e87e2a3a28f4279f5911bfd0189964b94a2ced7f))
- only throw EntityDatabaseAdapterPaginationCursorInvalidError from cursor decoding logic ([#537](https://github.com/expo/entity/issues/537)) ([e3cee07](https://github.com/expo/entity/commit/e3cee07091af8d1e6c5bb2d82f13e837c7658ea9))
- update the SQLExpression and SQLFragmentHelpers APIs ([#516](https://github.com/expo/entity/issues/516)) ([112d4dc](https://github.com/expo/entity/commit/112d4dc9b379da5317ff6acaf1d2e9bd597128a6))
- Add SQLExpression class that supports fluent comparison method API ([#506](https://github.com/expo/entity/issues/506)) ([535b681](https://github.com/expo/entity/commit/535b6811e08578aba1aadb095f3e401cfe7eaa26))
- add support to SQLFragment for `= ANY(?)` ([#498](https://github.com/expo/entity/issues/498)) ([b931daa](https://github.com/expo/entity/commit/b931daa9a082cdb33f4559d6d32e1655f0459b79))
- Add default for TSelectedFields for application-instantiated types ([#438](https://github.com/expo/entity/issues/438)) ([3d4aa0e](https://github.com/expo/entity/commit/3d4aa0e9efa519a36f3db69bd8f117a993c100fa))
- Apply same viewer context subclass type mechanism to knexLoader and related methods ([#472](https://github.com/expo/entity/issues/472)) ([018a40b](https://github.com/expo/entity/commit/018a40bc01d579ffbf88f308e641921e6fc39097))
- correct discriminated union type for forward/backward pagination ([#474](https://github.com/expo/entity/issues/474)) ([90ae6e2](https://github.com/expo/entity/commit/90ae6e299bda26ac9a80d83e83334d4152c022dd))
- correct pagination behavior for standard pagination with descending ordering clauses ([#477](https://github.com/expo/entity/issues/477)) ([e32c4c2](https://github.com/expo/entity/commit/e32c4c2bcc05f225a556f95c90f1ee0f4e24de74))
- eliminate some unnecessary any casts ([#448](https://github.com/expo/entity/issues/448)) ([cc78029](https://github.com/expo/entity/commit/cc78029117c14510b0934b5e0d19876a45570f8d))
- explicitly document behavior when cursor entity no longer exists ([#453](https://github.com/expo/entity/issues/453)) ([4dc156c](https://github.com/expo/entity/commit/4dc156c7a8dc50bc9e2802be133b9e258b289f54)), closes [#422](https://github.com/expo/entity/issues/422) [#431](https://github.com/expo/entity/issues/431)
- move EntityPrivacyUtils back into core package ([#429](https://github.com/expo/entity/issues/429)) ([56ec27d](https://github.com/expo/entity/commit/56ec27d5f201d83930a2cc05dfc380a054bff1e1))
- pass in augmentable classes to installation ([#437](https://github.com/expo/entity/issues/437)) ([f5c728b](https://github.com/expo/entity/commit/f5c728be565e01c0a8ace9ef51f6de8f49184dab)), closes [/github.com/expo/entity/pull/410#pullrequestreview-3765893898](https://github.com//github.com/expo/entity/pull/410/issues/pullrequestreview-3765893898)
- refactor install method to instead be free functions ([#441](https://github.com/expo/entity/issues/441)) ([51d632d](https://github.com/expo/entity/commit/51d632da1619dc039d5c41ce6533f63254ec4337)), closes [#410](https://github.com/expo/entity/issues/410)
- update integration test to use BlahEntity.knexLoader syntax ([#444](https://github.com/expo/entity/issues/444)) ([457f58a](https://github.com/expo/entity/commit/457f58ad2c4e1cf09f3132f3b9af5008ba0290c7)), closes [#441](https://github.com/expo/entity/issues/441)
- Use TSelectedFields for knex loader order by method ([#424](https://github.com/expo/entity/issues/424)) ([a9d09f4](https://github.com/expo/entity/commit/a9d09f42b67378d06133bcfb5b99c9afb17aac0d))
- Add ilike and trigram similarity search to pagination ([#431](https://github.com/expo/entity/issues/431)) ([f72cbca](https://github.com/expo/entity/commit/f72cbcaf5a722e6bb8299644ec32b07f1d3362a9)), closes [#422](https://github.com/expo/entity/issues/422)
- add method to get pagination cursor for single entity ([#475](https://github.com/expo/entity/issues/475)) ([898f71c](https://github.com/expo/entity/commit/898f71ce082c2593585575b60bd6ef2546717124))
- Add paginated loader to entity-database-adapter-knex ([#422](https://github.com/expo/entity/issues/422)) ([58deffd](https://github.com/expo/entity/commit/58deffd426b0214073ede608cd47c04db2596d60))
- Add pagination max page size configuration ([#436](https://github.com/expo/entity/issues/436)) ([3ec4b5f](https://github.com/expo/entity/commit/3ec4b5f0cd53bf18d6bcfa27dd29b70d3f4d5f19))
- Add raw sqlfragment orderBy capability for pagination ([#457](https://github.com/expo/entity/issues/457)) ([0615ffa](https://github.com/expo/entity/commit/0615ffacffbe9e903a9bf03b171c102f0554b992))
- add support for nulls ordering in order by clauses ([#468](https://github.com/expo/entity/issues/468)) ([6c0838d](https://github.com/expo/entity/commit/6c0838dc577a3b6190e3551ee834925d7bd53bc6))
- support nullable search fields with postgres transforms ([#470](https://github.com/expo/entity/issues/470)) ([53b0604](https://github.com/expo/entity/commit/53b0604152b8ca5ad9fc616d18478f93473c7eb0))
- throw specific errors for database adapter issues ([#311](https://github.com/expo/entity/issues/311)) ([f6639f9](https://github.com/expo/entity/commit/f6639f9def4ac20b628dd48fdca8d7cf61b6347b))
- add support for Buffer fields ([#305](https://github.com/expo/entity/issues/305)) ([31b52a5](https://github.com/expo/entity/commit/31b52a5f75842f4810f66c12ad03a10d9192f889))
- introduce transactional dataloader mode and fix global transaction ID generation ([#289](https://github.com/expo/entity/issues/289)) ([d3e89bd](https://github.com/expo/entity/commit/d3e89bdd82e6df5c5b35522bc3574fd9e1cfddff))
- add composite field loading and caching ([#272](https://github.com/expo/entity/issues/272)) ([f0aa0da](https://github.com/expo/entity/commit/f0aa0dafebdb56418cbd22dda437233d850db3e7))
- change entity id field generic to field name and derive type where necessary ([#278](https://github.com/expo/entity/issues/278)) ([b7e524c](https://github.com/expo/entity/commit/b7e524c41892797608d0b884410b8d520a80a9ef))
- enforce explicit id field cache property ([#276](https://github.com/expo/entity/issues/276)) ([1da5cc0](https://github.com/expo/entity/commit/1da5cc01e1affc3b3338a0ab2050504b5853ac6f))
- feat!: Default all loaders and mutators to enforcing (#256) ([a976e76](https://github.com/expo/entity/commit/a976e765098bca27049585e43a3bed5c71b9ac6b)), closes [#256](https://github.com/expo/entity/issues/256)
- constrain entity fields type to string-keyed object ([#235](https://github.com/expo/entity/issues/235)) ([7e2cea1](https://github.com/expo/entity/commit/7e2cea16973a0ae1917f867cd25d6ef7c8eaecef))
- Upgrade knex to 1.x in `@expo/entity-database-adapter-knex` ([#150](https://github.com/expo/entity/issues/150)) ([e1b0ee5](https://github.com/expo/entity/commit/e1b0ee528175646239d88aa71ffce73983eeb52f))
- allow null field values in loadManyByFieldEqualityConjunctionAsync ([#130](https://github.com/expo/entity/issues/130)) ([2f37dc6](https://github.com/expo/entity/commit/2f37dc6a37d368b12b3c8375e04f0777fd448797))
- add basic field type runtime validators ([#113](https://github.com/expo/entity/issues/113)) ([6c4d4b0](https://github.com/expo/entity/commit/6c4d4b03b5404dd776a12c5eb6f0af77a8c964f8))
- fix index export for EntityMutationTrigger and EntityMutationValidator ([#73](https://github.com/expo/entity/issues/73)) ([7e829f3](https://github.com/expo/entity/commit/7e829f30fde0bd343cb8a584ccda5530cf61ec33))
- move cache invalidation to after transaction commit ([#77](https://github.com/expo/entity/issues/77)) ([dbc3c81](https://github.com/expo/entity/commit/dbc3c81b359a668b5134189ce6de49b5893bb5e7))
- separate out EntityMutationValidator ([#68](https://github.com/expo/entity/issues/68)) ([547a1ef](https://github.com/expo/entity/commit/547a1efcecd17cc085702d1a3e9888ce5b644b13))
- correct some typing issues and exports ([#57](https://github.com/expo/entity/issues/57)) ([e91cfba](https://github.com/expo/entity/commit/e91cfba99321c2c4da2078aafd8627cbf0ceca50))
# 0.5.0 (2020-07-01)
### Features
- support entity fields subset of db fields ([#49](https://github.com/expo/entity/issues/49)) ([4e40b2e](https://github.com/expo/entity/commit/4e40b2e521407e521d236978ec3b3b56db3990be))
* Similarity search using PostgreSQL trigram similarity. Results are ordered by exact match priority, then by similarity score, then by specified extra order by fields if provided, then by ID for tie-breaking and stable pagination.
* Note that trigram similarity search can be significantly slower than ILIKE search, especially on large datasets without appropriate indexes, and results may not be as relevant as more advanced full-text search solutions.
* It is recommended to use this strategy only when ILIKE search does not meet the application's needs and to ensure appropriate database indexing for performance.
* Authorization-result-based version of the EnforcingKnexEntityLoader method by the same name.
* @returns entity result where uniqueFieldName equals fieldValue, or null if no entity matches the condition, where result error can be UnauthorizedError.
* @throws when multiple entities match the condition
* Authorization-result-based version of the EnforcingKnexEntityLoader method by the same name.
* @returns SQL query builder for building and executing SQL queries that when executed returns entity results where result error can be UnauthorizedError.
Loaded 100 of 402 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.