@classytic/mongokit 3.14.0 → 3.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -145,5 +145,44 @@ interface BetterAuthOverlayOptions<TDoc = Record<string, unknown>> {
145
145
  * would silently drop this call's `additionalFields` / `indexes`).
146
146
  */
147
147
  declare function createBetterAuthOverlay<TDoc = Record<string, unknown>>(options: BetterAuthOverlayOptions<TDoc>): Promise<DataAdapter<TDoc>>;
148
+ /**
149
+ * Minimal structural surface for the one write we need. Satisfied by BOTH a
150
+ * native MongoDB `Collection` (`db.collection('session')`) and a Mongoose
151
+ * `Model` — so the host passes whichever it has, and mongokit keeps mongoose +
152
+ * the mongodb driver as peer deps with zero runtime import.
153
+ */
154
+ interface SessionUpdaterLike {
155
+ updateMany(filter: Record<string, unknown>, update: Record<string, unknown>): Promise<{
156
+ modifiedCount?: number;
157
+ }>;
158
+ }
159
+ /**
160
+ * Clear a dangling `activeOrganizationId` from Better Auth `session` rows.
161
+ *
162
+ * WHY THIS EXISTS: Better Auth purges its `member` / `invitation` rows when an
163
+ * org is deleted (or a member removed), but it NEVER clears
164
+ * `session.activeOrganizationId`. A session left pointing at an org the user is
165
+ * no longer a member of makes `organization.getActiveMember()` return
166
+ * `MEMBER_NOT_FOUND`, and any frontend that trusts that stale pointer hangs —
167
+ * amplified by `session.cookieCache`, which serves the dead pointer for minutes.
168
+ *
169
+ * This is intentionally a host-invoked helper, NOT part of the arc org-delete
170
+ * cascade: arc is DB-agnostic (it purges through `@classytic/repo-core`
171
+ * adapters and must not reach a raw collection), whereas the `session`
172
+ * collection is a Better-Auth/mongo concern this kit already owns. Hosts wire
173
+ * it into their `organizationHooks`:
174
+ * - `afterDeleteOrganization` → org-wide (omit `userId`)
175
+ * - `afterRemoveMember` → one user (pass `userId`)
176
+ *
177
+ * The mongodb adapter stores `activeOrganizationId` as a STRING, so the org id
178
+ * is matched as-is (no ObjectId construction). `session.userId` is an ObjectId,
179
+ * so for the member-removal path pass `userId` already in the form your driver
180
+ * compares against (a constructed ObjectId) — it's matched verbatim.
181
+ *
182
+ * @returns the number of sessions whose pointer was cleared.
183
+ */
184
+ declare function clearActiveOrganizationFromSessions(sessions: SessionUpdaterLike, organizationId: string, options?: {
185
+ userId?: unknown;
186
+ }): Promise<number>;
148
187
  //#endregion
149
- export { BetterAuthInstance, BetterAuthOverlayOptions, type BetterAuthPluginKey, MongooseLike, RegisterBetterAuthStubsOptions, createBetterAuthOverlay, registerBetterAuthStubs };
188
+ export { BetterAuthInstance, BetterAuthOverlayOptions, type BetterAuthPluginKey, MongooseLike, RegisterBetterAuthStubsOptions, SessionUpdaterLike, clearActiveOrganizationFromSessions, createBetterAuthOverlay, registerBetterAuthStubs };
@@ -77,5 +77,35 @@ async function createBetterAuthOverlay(options) {
77
77
  schemaGenerator
78
78
  });
79
79
  }
80
+ /**
81
+ * Clear a dangling `activeOrganizationId` from Better Auth `session` rows.
82
+ *
83
+ * WHY THIS EXISTS: Better Auth purges its `member` / `invitation` rows when an
84
+ * org is deleted (or a member removed), but it NEVER clears
85
+ * `session.activeOrganizationId`. A session left pointing at an org the user is
86
+ * no longer a member of makes `organization.getActiveMember()` return
87
+ * `MEMBER_NOT_FOUND`, and any frontend that trusts that stale pointer hangs —
88
+ * amplified by `session.cookieCache`, which serves the dead pointer for minutes.
89
+ *
90
+ * This is intentionally a host-invoked helper, NOT part of the arc org-delete
91
+ * cascade: arc is DB-agnostic (it purges through `@classytic/repo-core`
92
+ * adapters and must not reach a raw collection), whereas the `session`
93
+ * collection is a Better-Auth/mongo concern this kit already owns. Hosts wire
94
+ * it into their `organizationHooks`:
95
+ * - `afterDeleteOrganization` → org-wide (omit `userId`)
96
+ * - `afterRemoveMember` → one user (pass `userId`)
97
+ *
98
+ * The mongodb adapter stores `activeOrganizationId` as a STRING, so the org id
99
+ * is matched as-is (no ObjectId construction). `session.userId` is an ObjectId,
100
+ * so for the member-removal path pass `userId` already in the form your driver
101
+ * compares against (a constructed ObjectId) — it's matched verbatim.
102
+ *
103
+ * @returns the number of sessions whose pointer was cleared.
104
+ */
105
+ async function clearActiveOrganizationFromSessions(sessions, organizationId, options) {
106
+ const filter = { activeOrganizationId: organizationId };
107
+ if (options?.userId != null) filter.userId = options.userId;
108
+ return (await sessions.updateMany(filter, { $set: { activeOrganizationId: null } })).modifiedCount ?? 0;
109
+ }
80
110
  //#endregion
81
- export { createBetterAuthOverlay, registerBetterAuthStubs };
111
+ export { clearActiveOrganizationFromSessions, createBetterAuthOverlay, registerBetterAuthStubs };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@classytic/mongokit",
3
- "version": "3.14.0",
3
+ "version": "3.15.0",
4
4
  "description": "Production-grade MongoDB repositories - smart pagination, events, and plugins. Built on @classytic/repo-core.",
5
5
  "type": "module",
6
6
  "sideEffects": false,