@fonderie/auth 7.8.0 → 7.9.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.
package/dist/index.d.cts CHANGED
@@ -236,12 +236,24 @@ declare function startUserRetention(store: IStoreAdapter, options: IUserRetentio
236
236
 
237
237
  declare function validateAuthConfig(config: IAuthConfig): void;
238
238
 
239
+ interface IUserPage {
240
+ users: Array<IUser & {
241
+ createdAtRaw?: string;
242
+ }>;
243
+ hasMore: boolean;
244
+ }
245
+
239
246
  interface IAdminUserDTO extends IUserDTO {
240
247
  suspended: boolean;
241
248
  deletedAt: string | null;
242
249
  createdAt: string;
243
250
  }
244
251
  declare function toAdminUserDTO(user: IUser): IAdminUserDTO;
252
+ interface IAdminUserPageDTO {
253
+ users: IAdminUserDTO[];
254
+ nextCursor: string | null;
255
+ }
256
+ declare function toAdminUserPageDTO(page: IUserPage): IAdminUserPageDTO;
245
257
  declare function describeAuthAdminRoutes(store: IStoreAdapter): IAdminRoute[];
246
258
 
247
- export { AuthModule, type ChangePasswordInput, DEFAULT_TEMPLATES, type IAdminUserDTO, IAuthConfig, type IImportUser, type ILoginEventDTO, type ILoginHistoryPageDTO, type IPurgeOptions, type ISessionDTO, IUser, IUserDTO, type IUserRetentionScheduleOptions, type LoginInput, type RegisterInput, type ResetPasswordInput, describeAuthAdminRoutes, importUser, normalizeEmail, normalizeEmailSafe, purgeSoftDeletedUsers, schemas, startUserRetention, toAdminUserDTO, validateAuthConfig };
259
+ export { AuthModule, type ChangePasswordInput, DEFAULT_TEMPLATES, type IAdminUserDTO, IAuthConfig, type IImportUser, type ILoginEventDTO, type ILoginHistoryPageDTO, type IPurgeOptions, type ISessionDTO, IUser, IUserDTO, type IUserRetentionScheduleOptions, type LoginInput, type RegisterInput, type ResetPasswordInput, describeAuthAdminRoutes, importUser, normalizeEmail, normalizeEmailSafe, purgeSoftDeletedUsers, schemas, startUserRetention, toAdminUserDTO, toAdminUserPageDTO, validateAuthConfig };
package/dist/index.d.ts CHANGED
@@ -236,12 +236,24 @@ declare function startUserRetention(store: IStoreAdapter, options: IUserRetentio
236
236
 
237
237
  declare function validateAuthConfig(config: IAuthConfig): void;
238
238
 
239
+ interface IUserPage {
240
+ users: Array<IUser & {
241
+ createdAtRaw?: string;
242
+ }>;
243
+ hasMore: boolean;
244
+ }
245
+
239
246
  interface IAdminUserDTO extends IUserDTO {
240
247
  suspended: boolean;
241
248
  deletedAt: string | null;
242
249
  createdAt: string;
243
250
  }
244
251
  declare function toAdminUserDTO(user: IUser): IAdminUserDTO;
252
+ interface IAdminUserPageDTO {
253
+ users: IAdminUserDTO[];
254
+ nextCursor: string | null;
255
+ }
256
+ declare function toAdminUserPageDTO(page: IUserPage): IAdminUserPageDTO;
245
257
  declare function describeAuthAdminRoutes(store: IStoreAdapter): IAdminRoute[];
246
258
 
247
- export { AuthModule, type ChangePasswordInput, DEFAULT_TEMPLATES, type IAdminUserDTO, IAuthConfig, type IImportUser, type ILoginEventDTO, type ILoginHistoryPageDTO, type IPurgeOptions, type ISessionDTO, IUser, IUserDTO, type IUserRetentionScheduleOptions, type LoginInput, type RegisterInput, type ResetPasswordInput, describeAuthAdminRoutes, importUser, normalizeEmail, normalizeEmailSafe, purgeSoftDeletedUsers, schemas, startUserRetention, toAdminUserDTO, validateAuthConfig };
259
+ export { AuthModule, type ChangePasswordInput, DEFAULT_TEMPLATES, type IAdminUserDTO, IAuthConfig, type IImportUser, type ILoginEventDTO, type ILoginHistoryPageDTO, type IPurgeOptions, type ISessionDTO, IUser, IUserDTO, type IUserRetentionScheduleOptions, type LoginInput, type RegisterInput, type ResetPasswordInput, describeAuthAdminRoutes, importUser, normalizeEmail, normalizeEmailSafe, purgeSoftDeletedUsers, schemas, startUserRetention, toAdminUserDTO, toAdminUserPageDTO, validateAuthConfig };
package/dist/index.js CHANGED
@@ -541,6 +541,29 @@ var UserModel = class {
541
541
  this.store = store;
542
542
  }
543
543
  store;
544
+ // One page of users, newest first. Keyset-paginated on (created_at, id) —
545
+ // the same cursor contract as login history and the audit log. The +1
546
+ // over-fetch that detects a next page lives here so no outer clamp can
547
+ // shave it off. Soft-deleted rows are excluded, as in every other finder.
548
+ async list(query = {}) {
549
+ const limit = Math.min(query.limit ?? 50, MAX_LIST_LIMIT);
550
+ const params = [];
551
+ const where = ["deleted_at IS NULL"];
552
+ if (query.cursor) {
553
+ params.push(query.cursor.createdAt, query.cursor.id);
554
+ where.push(`(created_at, id) < ($${params.length - 1}::timestamptz, $${params.length}::uuid)`);
555
+ }
556
+ params.push(limit + 1);
557
+ const rows = await this.store.query(
558
+ `SELECT ${USER_COLUMNS}, created_at::text AS "createdAtRaw"
559
+ FROM fonderie_users
560
+ WHERE ${where.join(" AND ")}
561
+ ORDER BY created_at DESC, id DESC
562
+ LIMIT $${params.length}`,
563
+ params
564
+ );
565
+ return { users: rows.slice(0, limit), hasMore: rows.length > limit };
566
+ }
544
567
  async findById(id) {
545
568
  const [row] = await this.store.query(
546
569
  `SELECT ${USER_COLUMNS} FROM fonderie_users WHERE id = $1 AND deleted_at IS NULL`,
@@ -813,6 +836,7 @@ var UserModel = class {
813
836
  return row ?? null;
814
837
  }
815
838
  };
839
+ var MAX_LIST_LIMIT = 200;
816
840
 
817
841
  // src/models/session.model.ts
818
842
  var SessionModel = class {
@@ -3192,7 +3216,7 @@ function extractToken(request) {
3192
3216
  }
3193
3217
 
3194
3218
  // src/admin.ts
3195
- import { HTTP as HTTP7, setApiResponse as setApiResponse7 } from "@fonderie/core";
3219
+ import { HTTP as HTTP7, encodeKeysetCursor as encodeKeysetCursor2, setApiResponse as setApiResponse7 } from "@fonderie/core";
3196
3220
  function toAdminUserDTO(user) {
3197
3221
  return {
3198
3222
  ...toUserDTO(user),
@@ -3201,6 +3225,11 @@ function toAdminUserDTO(user) {
3201
3225
  createdAt: user.createdAt.toISOString()
3202
3226
  };
3203
3227
  }
3228
+ function toAdminUserPageDTO(page) {
3229
+ const last = page.users[page.users.length - 1];
3230
+ const nextCursor = page.hasMore && last ? encodeKeysetCursor2(last.createdAtRaw ?? last.createdAt.toISOString(), last.id) : null;
3231
+ return { users: page.users.map(toAdminUserDTO), nextCursor };
3232
+ }
3204
3233
  var ADMIN_PREFIX = "/_admin";
3205
3234
  var idOf = (ctx) => ctx.meta.params?.["id"] ?? "";
3206
3235
  var NOT_FOUND = () => setApiResponse7(HTTP7.NOT_FOUND, "NOT_FOUND", "No such user");
@@ -3223,12 +3252,24 @@ function adminRouteTable(store) {
3223
3252
  [
3224
3253
  "GET",
3225
3254
  "/_admin/users",
3255
+ // With an email, the exact lookup support reaches for. Without one, a
3256
+ // page of users — an operator who has to know a name before they can
3257
+ // see anything cannot discover who signed up this morning.
3226
3258
  async (ctx) => {
3227
- const email2 = new URL(ctx.request.url).searchParams.get("email")?.trim().toLowerCase();
3228
- if (!email2)
3229
- return setApiResponse7(HTTP7.UNPROCESSABLE, "INVALID_PARAMETER", "email is required");
3230
- const user = await users.findByEmail(email2);
3231
- return user ? setApiResponse7(HTTP7.OK, "USER", "User", toAdminUserDTO(user)) : NOT_FOUND();
3259
+ const params = new URL(ctx.request.url).searchParams;
3260
+ const email2 = params.get("email")?.trim().toLowerCase();
3261
+ if (email2) {
3262
+ const user = await users.findByEmail(email2);
3263
+ return user ? setApiResponse7(HTTP7.OK, "USER", "User", toAdminUserDTO(user)) : NOT_FOUND();
3264
+ }
3265
+ const rawLimit = Number(params.get("limit") ?? 50);
3266
+ const limit = Number.isFinite(rawLimit) ? Math.min(Math.max(Math.trunc(rawLimit), 1), 200) : 50;
3267
+ const cursorParam = params.get("cursor");
3268
+ const cursor = cursorParam ? decodeLoginCursor(cursorParam) : null;
3269
+ if (cursorParam && !cursor)
3270
+ return setApiResponse7(HTTP7.UNPROCESSABLE, "INVALID_PARAMETER", "Invalid cursor");
3271
+ const page = await users.list({ limit, ...cursor ? { cursor } : {} });
3272
+ return setApiResponse7(HTTP7.OK, "USERS", "Users", toAdminUserPageDTO(page));
3232
3273
  }
3233
3274
  ],
3234
3275
  [
@@ -3591,6 +3632,7 @@ export {
3591
3632
  schemas_exports as schemas,
3592
3633
  startUserRetention,
3593
3634
  toAdminUserDTO,
3635
+ toAdminUserPageDTO,
3594
3636
  toUserDTO,
3595
3637
  validate,
3596
3638
  validateAuthConfig,