Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions MIGRATIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,37 @@ Breaking changes and upgrade notes for downstream projects.

---

## @casl/ability v6 → v7 (2026-05-22)

`@casl/ability` upgraded from `^6.8.1` to `^7.0.0`.

### What changed (this repo)

- **`lib/middlewares/policy.js`** — v7 renames `PureAbility` to `Ability` and **drops the default conditions matcher** from it; the historical MongoDB-matching `Ability` class no longer exists. `defineAbilityFor()` now builds via `createMongoAbility`:
```js
// before (v6)
const { AbilityBuilder, Ability } = await import('@casl/ability');
const { can, cannot, build } = new AbilityBuilder(Ability);
// after (v7)
const { AbilityBuilder, createMongoAbility } = await import('@casl/ability');
const { can, cannot, build } = new AbilityBuilder(createMongoAbility);
```
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Without this, conditions like `can('manage', 'Organization', { _id })` stop matching → authorization silently denies → endpoints return 403/422.
- **JSDoc type refs** `import('@casl/ability').Ability` → `MongoAbility` (`lib/middlewares/policy.js`, `lib/helpers/abilities.js`).
- **`package.json`** — `@casl/ability` `^6.8.1` → `^7.0.0`.

### Downstream action required

The `policy.js` fix is a devkit-owned file → it arrives via `/update-stack` (`--theirs`). The **dependency bump does not auto-propagate** (`package.json` is `--ours`):

1. Bump `@casl/ability` to `^7.0.0` in `package.json` and reinstall.
2. After `/update-stack`, verify `lib/middlewares/policy.js` (~line 95) reads `new AbilityBuilder(createMongoAbility)`.
3. **Module policy files need no change** — they use `can`/`cannot` closures, never the `Ability` class.
4. The serialized rules format is **unchanged** (`createMongoAbility` keeps the MongoQuery rule shape), so Node→client rule packing stays compatible.
5. Run unit + integration + e2e to confirm authorization paths still pass.

---

## Sentry removed — PostHog Error Tracking is now sole source (2026-05-10)

The `@sentry/node` integration shipped in 2026-03-26 (still documented below as **PostHog Analytics (2026-03-26)** + the now-removed Sentry monitoring section) is dropped. Error capture moves entirely to PostHog Error Tracking via `posthog.capture('$exception', ...)`.
Expand Down
2 changes: 1 addition & 1 deletion lib/helpers/abilities.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/**
* Serialize CASL abilities to a JSON-safe array compatible with createMongoAbility().
* @param {import('@casl/ability').Ability} ability - The CASL ability instance
* @param {import('@casl/ability').MongoAbility} ability - The CASL ability instance
* @returns {Array<{action: string, subject: string, conditions?: Object, inverted?: boolean}>} Array of serialized rules
*/
const serializeAbilities = (ability) =>
Expand Down
8 changes: 5 additions & 3 deletions lib/middlewares/policy.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,13 @@ const registerAbilities = (entry) => {
* Iterates over all registered ability builder functions from module policy files.
* @param {Object|null} user - The authenticated user, or null/undefined for guests
* @param {Object|null} [membership] - Optional organization membership (reserved for future use)
* @returns {Promise<import('@casl/ability').Ability>} CASL ability instance
* @returns {Promise<import('@casl/ability').MongoAbility>} CASL ability instance
*/
const defineAbilityFor = async (user, membership) => {
const { AbilityBuilder, Ability } = await loadCasl();
const { can, cannot, build } = new AbilityBuilder(Ability);
// v7: the `Ability` export is now PureAbility (no default conditions matcher).
// createMongoAbility restores MongoDB-style condition matching ({ _id }, { organizationId }).
const { AbilityBuilder, createMongoAbility } = await loadCasl();
const { can, cannot, build } = new AbilityBuilder(createMongoAbility);
Comment thread
PierreBrisorgueil marked this conversation as resolved.

// Normalize Mongoose membership to a plain object so all fields (role, status, etc.)
// are accessible, and flatten populated organizationId to a plain string ID.
Expand Down
44 changes: 22 additions & 22 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
"release:auto": "npx semantic-release"
},
"dependencies": {
"@casl/ability": "^6.8.1",
"@casl/ability": "^7.0.0",
"@jest/globals": "^30.4.1",
"axios": "^1.16.1",
"bcrypt": "^6.0.0",
Expand Down
Loading