feat(storage): generalize S3 client and watcher interfaces to storage-oriented naming with backward compatibility

This commit is contained in:
2026-03-14 19:24:36 +00:00
parent 7959fa6296
commit 18bdb5c7c2
15 changed files with 3598 additions and 3202 deletions

View File

@@ -3,7 +3,7 @@ import { expect, tap } from '@git.zone/tstest/tapbundle';
import * as plugins from '../ts/plugins.js';
import * as smartbucket from '../ts/index.js';
class FakeS3Client {
class FakeStorageClient {
private callIndex = 0;
constructor(private readonly pages: Array<Partial<plugins.s3.ListObjectsV2Output>>) {}
@@ -30,7 +30,7 @@ tap.test('MetaData.hasMetaData should return false when metadata file does not e
});
tap.test('getSubDirectoryByName should create correct parent chain for new nested directories', async () => {
const fakeSmartbucket = { s3Client: new FakeS3Client([{ Contents: [], CommonPrefixes: [] }]) } as unknown as smartbucket.SmartBucket;
const fakeSmartbucket = { storageClient: new FakeStorageClient([{ Contents: [], CommonPrefixes: [] }]) } as unknown as smartbucket.SmartBucket;
const bucket = new smartbucket.Bucket(fakeSmartbucket, 'test-bucket');
const baseDirectory = new smartbucket.Directory(bucket, null as any, '');
@@ -51,7 +51,7 @@ tap.test('listFiles should aggregate results across paginated ListObjectsV2 resp
Contents: Array.from({ length: 200 }, (_, index) => ({ Key: `file-${1000 + index}` })),
IsTruncated: false,
};
const fakeSmartbucket = { s3Client: new FakeS3Client([firstPage, secondPage]) } as unknown as smartbucket.SmartBucket;
const fakeSmartbucket = { storageClient: new FakeStorageClient([firstPage, secondPage]) } as unknown as smartbucket.SmartBucket;
const bucket = new smartbucket.Bucket(fakeSmartbucket, 'test-bucket');
const baseDirectory = new smartbucket.Directory(bucket, null as any, '');
@@ -61,7 +61,7 @@ tap.test('listFiles should aggregate results across paginated ListObjectsV2 resp
tap.test('listDirectories should aggregate CommonPrefixes across pagination', async () => {
const fakeSmartbucket = {
s3Client: new FakeS3Client([
storageClient: new FakeStorageClient([
{ CommonPrefixes: [{ Prefix: 'dirA/' }], IsTruncated: true, NextContinuationToken: 'token-1' },
{ CommonPrefixes: [{ Prefix: 'dirB/' }], IsTruncated: false },
]),

View File

@@ -2,7 +2,7 @@
import { tap, expect } from '@git.zone/tstest/tapbundle';
import * as smartbucket from '../ts/index.js';
import type { IS3ChangeEvent } from '../ts/interfaces.js';
import type { IStorageChangeEvent } from '../ts/interfaces.js';
// Get test configuration
import * as qenv from '@push.rocks/qenv';
@@ -57,7 +57,7 @@ tap.test('should create watcher with custom options', async () => {
// ==========================
tap.test('should detect add events for new files', async () => {
const events: IS3ChangeEvent[] = [];
const events: IStorageChangeEvent[] = [];
const watcher = testBucket.createWatcher({
prefix: 'watcher-test/',
pollIntervalMs: 500,
@@ -94,7 +94,7 @@ tap.test('should detect add events for new files', async () => {
// ==========================
tap.test('should detect modify events for changed files', async () => {
const events: IS3ChangeEvent[] = [];
const events: IStorageChangeEvent[] = [];
const watcher = testBucket.createWatcher({
prefix: 'watcher-test/',
pollIntervalMs: 500,
@@ -131,7 +131,7 @@ tap.test('should detect modify events for changed files', async () => {
// ==========================
tap.test('should detect delete events for removed files', async () => {
const events: IS3ChangeEvent[] = [];
const events: IStorageChangeEvent[] = [];
const watcher = testBucket.createWatcher({
prefix: 'watcher-test/',
pollIntervalMs: 500,
@@ -174,7 +174,7 @@ tap.test('should emit initial state as add events when includeInitial is true',
contents: 'content 2',
});
const events: IS3ChangeEvent[] = [];
const events: IStorageChangeEvent[] = [];
const watcher = testBucket.createWatcher({
prefix: 'watcher-initial/',
pollIntervalMs: 10000, // Long interval - we only care about initial events
@@ -206,13 +206,13 @@ tap.test('should emit initial state as add events when includeInitial is true',
// ==========================
tap.test('should emit events via EventEmitter pattern', async () => {
const events: IS3ChangeEvent[] = [];
const events: IStorageChangeEvent[] = [];
const watcher = testBucket.createWatcher({
prefix: 'watcher-emitter/',
pollIntervalMs: 500,
});
watcher.on('change', (event: IS3ChangeEvent) => {
watcher.on('change', (event: IStorageChangeEvent) => {
events.push(event);
});
@@ -239,7 +239,7 @@ tap.test('should emit events via EventEmitter pattern', async () => {
// ==========================
tap.test('should buffer events when bufferTimeMs is set', async () => {
const bufferedEvents: (IS3ChangeEvent | IS3ChangeEvent[])[] = [];
const bufferedEvents: (IStorageChangeEvent | IStorageChangeEvent[])[] = [];
const watcher = testBucket.createWatcher({
prefix: 'watcher-buffer/',
pollIntervalMs: 200,
@@ -327,8 +327,8 @@ tap.test('should stop gracefully with stop()', async () => {
await watcher.stop();
// Watcher should not poll after stop
const eventsCaptured: IS3ChangeEvent[] = [];
watcher.on('change', (event: IS3ChangeEvent) => {
const eventsCaptured: IStorageChangeEvent[] = [];
watcher.on('change', (event: IStorageChangeEvent) => {
eventsCaptured.push(event);
});
@@ -362,7 +362,7 @@ tap.test('should stop gracefully with close() alias', async () => {
// ==========================
tap.test('should only detect changes within specified prefix', async () => {
const events: IS3ChangeEvent[] = [];
const events: IStorageChangeEvent[] = [];
const watcher = testBucket.createWatcher({
prefix: 'watcher-prefix-a/',
pollIntervalMs: 500,