@opengeni/db 5.0.1-canary.0 → 5.0.1-canary.1

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.ts CHANGED
@@ -5323,22 +5323,22 @@ export type SessionListSnapshotCursor = {
5323
5323
  offset: number;
5324
5324
  parentSessionFilter: string;
5325
5325
  search: string | null;
5326
- archiveMode: "active" | "archived";
5326
+ archiveMode: "active" | "archived" | "all";
5327
5327
  /** Additive filter identity; absent on legacy snapshot cursors. */
5328
5328
  filter?: string;
5329
5329
  };
5330
5330
  export type SessionListKeysetCursor = {
5331
5331
  kind: "keyset";
5332
5332
  /** Absent on v2 cursors, which always used session updatedAt. */
5333
- sortBy?: "updatedAt" | "archivedAt";
5333
+ sortBy?: "updatedAt" | "archivedAt" | "createdAt" | "name";
5334
5334
  /** Decimal committed workspace activity revision frozen on page one. */
5335
5335
  snapshotRevision: string;
5336
- /** Exact PostgreSQL timestamp text, including microseconds. */
5336
+ /** Exact PostgreSQL timestamp (including microseconds), or normalized name for name order. */
5337
5337
  sortAt: string;
5338
5338
  id: string;
5339
5339
  parentSessionFilter: string;
5340
5340
  search: string | null;
5341
- archiveMode: "active" | "archived";
5341
+ archiveMode: "active" | "archived" | "all";
5342
5342
  /** Canonical identity for channel, creator, and time-bound filters. */
5343
5343
  filter?: string;
5344
5344
  };
@@ -5372,6 +5372,8 @@ export type ListSessionsForSubjectOptions = ListSessionsOptions & SessionListFil
5372
5372
  pinsOnly?: boolean | undefined;
5373
5373
  /** List personally archived chats instead of active chats. */
5374
5374
  archivedOnly?: boolean | undefined;
5375
+ sortBy?: "updatedAt" | "createdAt" | "name" | undefined;
5376
+ archiveStatus?: "active" | "archived" | "all" | undefined;
5375
5377
  /** Return a continuation cursor. Disable for legacy one-page array reads. */
5376
5378
  materializeSnapshot?: boolean | undefined;
5377
5379
  authorizationScope?: SessionAuthorizationListScope | undefined;
package/dist/index.js CHANGED
@@ -65069,7 +65069,10 @@ function sessionFilters(options) {
65069
65069
  and archive_state.session_id = ${sessions.rootSessionId}
65070
65070
  and archive_state.archived = true
65071
65071
  )`;
65072
- filters.push(options.archivedOnly ? archivedRoot : sql86`not (${archivedRoot})`);
65072
+ const archiveStatus = options.archiveStatus ?? (options.archivedOnly ? "archived" : "active");
65073
+ if (archiveStatus !== "all") {
65074
+ filters.push(archiveStatus === "archived" ? archivedRoot : sql86`not (${archivedRoot})`);
65075
+ }
65073
65076
  if (options.authorizationScope) {
65074
65077
  filters.push(sessionAuthorizationScopeFilter(options.authorizationScope));
65075
65078
  }
@@ -65214,7 +65217,8 @@ function encodeSessionListCursor(cursor) {
65214
65217
  return Buffer.from(
65215
65218
  JSON.stringify(
65216
65219
  cursor.kind === "keyset" ? {
65217
- version: cursor.sortBy === "archivedAt" ? 3 : 2,
65220
+ version: 4,
65221
+ sortBy: cursor.sortBy ?? "updatedAt",
65218
65222
  // Preserve the old cursor envelope until every pre-v2 replica has
65219
65223
  // rolled away. It resolves only to the typed expiry/rebase path.
65220
65224
  snapshotId: SESSION_LIST_KEYSET_LEGACY_SNAPSHOT_ID,
@@ -65244,15 +65248,16 @@ function decodeSessionListCursor(value) {
65244
65248
  const search = parsed.search;
65245
65249
  const archiveMode = parsed.archiveMode ?? "active";
65246
65250
  const filter = parsed.filter ?? "all";
65247
- const filtersAreValid = (parentSessionFilter === "all" || parentSessionFilter === "null" || typeof parentSessionFilter === "string" && UUID_PATTERN2.test(parentSessionFilter)) && (search === null || typeof search === "string" && search.length <= 200) && (archiveMode === "active" || archiveMode === "archived") && typeof filter === "string" && filter.length <= 2048;
65251
+ const filtersAreValid = (parentSessionFilter === "all" || parentSessionFilter === "null" || typeof parentSessionFilter === "string" && UUID_PATTERN2.test(parentSessionFilter)) && (search === null || typeof search === "string" && search.length <= 200) && (archiveMode === "active" || archiveMode === "archived" || parsed.version === 4 && archiveMode === "all") && typeof filter === "string" && filter.length <= 2048;
65248
65252
  if (!filtersAreValid) return null;
65249
- if (parsed.version === 2 || parsed.version === 3) {
65250
- if (parsed.version === 3 && archiveMode !== "archived" || typeof parsed.snapshotRevision !== "string" || typeof parsed.sortAt !== "string" || !isSessionListCursorTimestamp(parsed.sortAt) || typeof parsed.id !== "string" || !UUID_PATTERN2.test(parsed.id)) {
65253
+ if (parsed.version === 2 || parsed.version === 3 || parsed.version === 4) {
65254
+ const sortBy = parsed.version === 4 ? parsed.sortBy : parsed.version === 3 ? "archivedAt" : "updatedAt";
65255
+ if (!["updatedAt", "createdAt", "name", "archivedAt"].includes(sortBy) || sortBy === "archivedAt" && archiveMode !== "archived" || typeof parsed.snapshotRevision !== "string" || typeof parsed.sortAt !== "string" || sortBy !== "name" && !isSessionListCursorTimestamp(parsed.sortAt) || sortBy === "name" && (parsed.sortAt.length > 1e4 || parsed.sortAt.includes("\0")) || typeof parsed.id !== "string" || !UUID_PATTERN2.test(parsed.id)) {
65251
65256
  return null;
65252
65257
  }
65253
65258
  return {
65254
65259
  kind: "keyset",
65255
- ...parsed.version === 3 ? { sortBy: "archivedAt" } : {},
65260
+ ...parsed.version !== 2 ? { sortBy } : {},
65256
65261
  snapshotRevision: normalizeSessionActivityRevision(
65257
65262
  parsed.snapshotRevision,
65258
65263
  "cursor snapshot revision"
@@ -65265,6 +65270,7 @@ function decodeSessionListCursor(value) {
65265
65270
  filter
65266
65271
  };
65267
65272
  }
65273
+ if (parsed.version !== void 0) return null;
65268
65274
  const offset = parsed.offset;
65269
65275
  if (typeof parsed.snapshotId !== "string" || !UUID_PATTERN2.test(parsed.snapshotId) || typeof offset !== "number" || !Number.isSafeInteger(offset) || offset < 0) {
65270
65276
  return null;
@@ -65316,21 +65322,28 @@ async function listSessionsForSubject(db, workspaceId, options) {
65316
65322
  tx,
65317
65323
  workspaceId
65318
65324
  );
65325
+ const archiveMode = options.archiveStatus ?? (options.archivedOnly ? "archived" : "active");
65326
+ const sortBy = options.sortBy ?? (archiveMode === "archived" ? "archivedAt" : "updatedAt");
65327
+ if (options.archivedOnly && archiveMode !== "archived") {
65328
+ throw new SessionListCursorError("archivedOnly conflicts with archiveStatus");
65329
+ }
65319
65330
  const filters = [eq56(sessions.workspaceId, workspaceId), ...sessionFilters(options)];
65320
- const ordinaryPinFilter = options.archivedOnly ? sql86`true` : or12(isNull16(sessionPins.id), eq56(sessionPins.pinned, false));
65331
+ const ordinaryPinFilter = archiveMode === "archived" ? sql86`true` : or12(isNull16(sessionPins.id), eq56(sessionPins.pinned, false));
65321
65332
  const parentFilter = sessionParentFilter(options.parentSessionId);
65322
65333
  const searchFilter = sessionSearchFilter(options.search);
65323
- const archiveMode = options.archivedOnly ? "archived" : "active";
65324
- const ordinarySortAt = options.archivedOnly ? sql86`(select archive_order.archived_at
65334
+ const archiveSortAt = sql86`(select archive_order.archived_at
65325
65335
  from ${sessionPins} archive_order
65326
65336
  where archive_order.workspace_id = ${sessions.workspaceId}
65327
65337
  and archive_order.subject_id = ${options.subjectId}
65328
65338
  and archive_order.session_id = ${sessions.rootSessionId}
65329
- and archive_order.archived = true)` : sessions.updatedAt;
65330
- const exactOrdinarySortAt = sql86`to_char(${ordinarySortAt} at time zone 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.US"Z"')`;
65339
+ and archive_order.archived = true)`;
65340
+ const ordinarySortAt = sortBy === "name" ? sql86`translate(btrim(coalesce(${sessions.title}, '')), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz') collate "C"` : sortBy === "createdAt" ? sessions.createdAt : sortBy === "archivedAt" ? archiveSortAt : sessions.updatedAt;
65341
+ const exactOrdinarySortAt = sortBy === "name" ? sql86`${ordinarySortAt}` : sql86`to_char(${ordinarySortAt} at time zone 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.US"Z"')`;
65342
+ const exactArchiveSortAt = archiveMode === "active" ? sql86`null` : sql86`to_char(${archiveSortAt} at time zone 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.US"Z"')`;
65343
+ const ordinaryOrder = sortBy === "name" ? [asc22(ordinarySortAt), asc22(sessions.id)] : [desc22(ordinarySortAt), desc22(sessions.id)];
65331
65344
  const listFilter = sessionListFilterIdentity(options);
65332
65345
  const now = /* @__PURE__ */ new Date();
65333
- if (options.pinsOnly && options.archivedOnly) {
65346
+ if (options.pinsOnly && archiveMode === "archived") {
65334
65347
  throw new SessionListCursorError(
65335
65348
  "pins-only and archived session lists cannot be combined"
65336
65349
  );
@@ -65338,13 +65351,14 @@ async function listSessionsForSubject(db, workspaceId, options) {
65338
65351
  if (options.pinsOnly && options.cursor) {
65339
65352
  throw new SessionListCursorError("pins-only session lists do not accept a cursor");
65340
65353
  }
65341
- if (options.cursor && options.cursor.archiveMode === "archived" && (options.cursor.kind === "snapshot" || options.cursor.sortBy !== "archivedAt")) {
65354
+ if (options.cursor && (options.cursor.kind === "snapshot" && (sortBy !== "updatedAt" || archiveMode !== "active") || options.cursor.kind === "keyset" && (options.cursor.sortBy ?? "updatedAt") !== sortBy)) {
65342
65355
  throw new SessionListCursorExpiredError();
65343
65356
  }
65344
65357
  let pageIds;
65345
65358
  let selectedOrdinaryRows;
65346
65359
  let nextCursor = null;
65347
65360
  const exactArchiveTimestamps = /* @__PURE__ */ new Map();
65361
+ const exactOrdinaryTimestamps = /* @__PURE__ */ new Map();
65348
65362
  if (options.pinsOnly) {
65349
65363
  pageIds = [];
65350
65364
  } else if (options.cursor?.kind === "snapshot") {
@@ -65420,7 +65434,8 @@ async function listSessionsForSubject(db, workspaceId, options) {
65420
65434
  id: sessions.id,
65421
65435
  session: sessions,
65422
65436
  pin: sessionPins,
65423
- sortAt: exactOrdinarySortAt
65437
+ sortAt: exactOrdinarySortAt,
65438
+ archiveAt: exactArchiveSortAt
65424
65439
  }).from(sessions).leftJoin(
65425
65440
  sessionPins,
65426
65441
  and55(
@@ -65428,11 +65443,14 @@ async function listSessionsForSubject(db, workspaceId, options) {
65428
65443
  eq56(sessionPins.subjectId, options.subjectId),
65429
65444
  eq56(sessionPins.sessionId, sessions.id)
65430
65445
  )
65431
- ).where(and55(...filters, ordinaryPinFilter)).orderBy(desc22(ordinarySortAt), desc22(sessions.id)).limit(limit);
65446
+ ).where(and55(...filters, ordinaryPinFilter)).orderBy(...ordinaryOrder).limit(limit);
65432
65447
  pageIds = ordinaryIdRows.map((row) => row.id);
65433
65448
  selectedOrdinaryRows = ordinaryIdRows;
65434
- if (options.archivedOnly) {
65435
- for (const row of ordinaryIdRows) exactArchiveTimestamps.set(row.id, row.sortAt);
65449
+ if (sortBy === "updatedAt" || sortBy === "createdAt") {
65450
+ for (const row of ordinaryIdRows) exactOrdinaryTimestamps.set(row.id, row.sortAt);
65451
+ }
65452
+ if (archiveMode !== "active") {
65453
+ for (const row of ordinaryIdRows) exactArchiveTimestamps.set(row.id, row.archiveAt);
65436
65454
  }
65437
65455
  } else {
65438
65456
  const cursor = options.cursor?.kind === "keyset" ? options.cursor : void 0;
@@ -65440,7 +65458,13 @@ async function listSessionsForSubject(db, workspaceId, options) {
65440
65458
  throw new SessionListCursorError("session list cursor does not match its filters");
65441
65459
  }
65442
65460
  const snapshotRevision = cursor ? normalizeSessionActivityRevision(cursor.snapshotRevision, "cursor snapshot revision") : await readWorkspaceSessionActivityRevision(tx, workspaceId);
65443
- const cursorPredicate = cursor ? or12(
65461
+ const cursorPredicate = cursor ? sortBy === "name" ? or12(
65462
+ sql86`${ordinarySortAt} > ${cursor.sortAt}::text collate "C"`,
65463
+ and55(
65464
+ sql86`${ordinarySortAt} = ${cursor.sortAt}::text collate "C"`,
65465
+ gt9(sessions.id, cursor.id)
65466
+ )
65467
+ ) : or12(
65444
65468
  sql86`${ordinarySortAt} < ${cursor.sortAt}::text::timestamptz`,
65445
65469
  and55(
65446
65470
  sql86`${ordinarySortAt} = ${cursor.sortAt}::text::timestamptz`,
@@ -65451,7 +65475,8 @@ async function listSessionsForSubject(db, workspaceId, options) {
65451
65475
  id: sessions.id,
65452
65476
  session: sessions,
65453
65477
  pin: sessionPins,
65454
- sortAt: exactOrdinarySortAt
65478
+ sortAt: exactOrdinarySortAt,
65479
+ archiveAt: exactArchiveSortAt
65455
65480
  }).from(sessions).leftJoin(
65456
65481
  sessionPins,
65457
65482
  and55(
@@ -65466,19 +65491,22 @@ async function listSessionsForSubject(db, workspaceId, options) {
65466
65491
  sql86`${sessions.activityRevision} <= ${snapshotRevision}::text::bigint`,
65467
65492
  cursorPredicate
65468
65493
  )
65469
- ).orderBy(desc22(ordinarySortAt), desc22(sessions.id)).limit(limit + 1);
65494
+ ).orderBy(...ordinaryOrder).limit(limit + 1);
65470
65495
  const hasMore = ordinaryIdRows.length > limit;
65471
65496
  const page = ordinaryIdRows.slice(0, limit);
65472
65497
  pageIds = page.map((row) => row.id);
65473
65498
  selectedOrdinaryRows = page;
65474
- if (options.archivedOnly) {
65475
- for (const row of page) exactArchiveTimestamps.set(row.id, row.sortAt);
65499
+ if (sortBy === "updatedAt" || sortBy === "createdAt") {
65500
+ for (const row of page) exactOrdinaryTimestamps.set(row.id, row.sortAt);
65501
+ }
65502
+ if (archiveMode !== "active") {
65503
+ for (const row of page) exactArchiveTimestamps.set(row.id, row.archiveAt);
65476
65504
  }
65477
65505
  const last = page.at(-1);
65478
65506
  if (hasMore && last) {
65479
65507
  nextCursor = encodeSessionListCursor({
65480
65508
  kind: "keyset",
65481
- ...options.archivedOnly ? { sortBy: "archivedAt" } : {},
65509
+ sortBy,
65482
65510
  snapshotRevision,
65483
65511
  sortAt: last.sortAt,
65484
65512
  id: last.id,
@@ -65489,7 +65517,7 @@ async function listSessionsForSubject(db, workspaceId, options) {
65489
65517
  });
65490
65518
  }
65491
65519
  }
65492
- const pinnedLookaheadRows = options.archivedOnly ? [] : await tx.select({ session: sessions, pin: sessionPins }).from(sessionPins).innerJoin(sessions, eq56(sessions.id, sessionPins.sessionId)).where(
65520
+ const pinnedLookaheadRows = archiveMode === "archived" ? [] : await tx.select({ session: sessions, pin: sessionPins }).from(sessionPins).innerJoin(sessions, eq56(sessions.id, sessionPins.sessionId)).where(
65493
65521
  and55(
65494
65522
  eq56(sessionPins.workspaceId, workspaceId),
65495
65523
  eq56(sessionPins.subjectId, options.subjectId),
@@ -65571,6 +65599,8 @@ async function listSessionsForSubject(db, workspaceId, options) {
65571
65599
  },
65572
65600
  { subjectId: options.subjectId, activated: tenancyActivated }
65573
65601
  ),
65602
+ ...sortBy === "updatedAt" && exactOrdinaryTimestamps.has(session.id) ? { updatedAt: exactOrdinaryTimestamps.get(session.id) } : {},
65603
+ ...sortBy === "createdAt" && exactOrdinaryTimestamps.has(session.id) ? { createdAt: exactOrdinaryTimestamps.get(session.id) } : {},
65574
65604
  treeStats: treeStats.get(session.id) ?? EMPTY_SESSION_TREE_STATS
65575
65605
  },
65576
65606
  requiresActionSince
@@ -65582,7 +65612,9 @@ async function listSessionsForSubject(db, workspaceId, options) {
65582
65612
  pinned: pinnedRows.map(mapListSession),
65583
65613
  pinnedTruncated,
65584
65614
  sessions: pageRows.map(mapListSession),
65585
- nextCursor
65615
+ nextCursor,
65616
+ sortBy,
65617
+ archiveStatus: archiveMode
65586
65618
  };
65587
65619
  },
65588
65620
  { isolationLevel: "read committed" }