// classes.listcursor.ts import * as plugins from './plugins.js'; import type { Bucket } from './classes.bucket.js'; export interface IListCursorOptions { pageSize?: number; } export interface IListCursorResult { keys: string[]; done: boolean; } /** * ListCursor provides explicit pagination control for listing objects in a bucket. * Useful for UI pagination, resumable operations, and manual batch processing. */ export class ListCursor { private continuationToken?: string; private exhausted = false; private pageSize: number; constructor( private bucket: Bucket, private prefix: string, options: IListCursorOptions = {} ) { this.pageSize = options.pageSize || 1000; } /** * Fetch the next page of object keys * @returns Object with keys array and done flag */ public async next(): Promise { if (this.exhausted) { return { keys: [], done: true }; } const command = new plugins.s3.ListObjectsV2Command({ Bucket: this.bucket.name, Prefix: this.prefix, MaxKeys: this.pageSize, ContinuationToken: this.continuationToken, }); const response = await this.bucket.smartbucketRef.s3Client.send(command); const keys = (response.Contents || []) .map((obj) => obj.Key) .filter((key): key is string => !!key); this.continuationToken = response.NextContinuationToken; this.exhausted = !this.continuationToken; return { keys, done: this.exhausted }; } /** * Check if there are more pages to fetch */ public hasMore(): boolean { return !this.exhausted; } /** * Reset the cursor to start from the beginning */ public reset(): void { this.continuationToken = undefined; this.exhausted = false; } /** * Get the current continuation token (for saving/restoring state) */ public getToken(): string | undefined { return this.continuationToken; } /** * Set the continuation token (for resuming from a saved state) */ public setToken(token: string | undefined): void { this.continuationToken = token; this.exhausted = !token; } }