@wener/common 2.0.2 → 2.0.3
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/lib/ai/qwen3vl/index.js +2 -0
- package/lib/ai/qwen3vl/index.js.map +1 -0
- package/lib/ai/qwen3vl/utils.js +31 -0
- package/lib/ai/qwen3vl/utils.js.map +1 -0
- package/lib/ai/vision/DocLayoutElementTypeSchema.js +28 -0
- package/lib/ai/vision/DocLayoutElementTypeSchema.js.map +1 -0
- package/lib/ai/vision/ImageAnnotationSchema.js +50 -0
- package/lib/ai/vision/ImageAnnotationSchema.js.map +1 -0
- package/lib/ai/vision/index.js +3 -0
- package/lib/ai/vision/index.js.map +1 -0
- package/lib/ai/vision/resolveImageAnnotation.js +105 -0
- package/lib/ai/vision/resolveImageAnnotation.js.map +1 -0
- package/lib/cn/ChineseResidentIdNo.js +21 -14
- package/lib/cn/ChineseResidentIdNo.js.map +1 -0
- package/lib/cn/ChineseResidentIdNo.test.js +1 -1
- package/lib/cn/DivisionCode.js +30 -25
- package/lib/cn/DivisionCode.js.map +1 -0
- package/lib/cn/DivisionCode.test.js +1 -1
- package/lib/cn/Mod11.js +38 -81
- package/lib/cn/Mod11.js.map +1 -0
- package/lib/cn/Mod31.js +41 -90
- package/lib/cn/Mod31.js.map +1 -0
- package/lib/cn/UnifiedSocialCreditCode.js +43 -34
- package/lib/cn/UnifiedSocialCreditCode.js.map +1 -0
- package/lib/cn/UnifiedSocialCreditCode.test.js +1 -1
- package/lib/cn/formatChineseAmount.js +77 -0
- package/lib/cn/formatChineseAmount.js.map +1 -0
- package/lib/cn/index.js +7 -1
- package/lib/cn/index.js.map +1 -0
- package/lib/cn/parseChineseNumber.js +94 -0
- package/lib/cn/parseChineseNumber.js.map +1 -0
- package/lib/cn/parseChineseNumber.test.js +278 -0
- package/lib/cn/pinyin/cartesianProduct.js +22 -0
- package/lib/cn/pinyin/cartesianProduct.js.map +1 -0
- package/lib/cn/pinyin/cartesianProduct.test.js +179 -0
- package/lib/cn/pinyin/data.json +23573 -0
- package/lib/cn/pinyin/loader.js +14 -0
- package/lib/cn/pinyin/loader.js.map +1 -0
- package/lib/cn/pinyin/preload.js +3 -0
- package/lib/cn/pinyin/preload.js.map +1 -0
- package/lib/cn/pinyin/toPinyin.test.js +167 -0
- package/lib/cn/pinyin/toPinyinPure.js +33 -0
- package/lib/cn/pinyin/toPinyinPure.js.map +1 -0
- package/lib/cn/pinyin/transform.js +14 -0
- package/lib/cn/pinyin/transform.js.map +1 -0
- package/lib/cn/types.d.js +2 -0
- package/lib/cn/types.d.js.map +1 -0
- package/lib/consola/createStandardConsolaReporter.js +6 -6
- package/lib/consola/createStandardConsolaReporter.js.map +1 -0
- package/lib/consola/formatLogObject.js +65 -145
- package/lib/consola/formatLogObject.js.map +1 -0
- package/lib/consola/formatLogObject.test.js +184 -0
- package/lib/consola/index.js +1 -0
- package/lib/consola/index.js.map +1 -0
- package/lib/data/formatSort.js +6 -5
- package/lib/data/formatSort.js.map +1 -0
- package/lib/data/index.js +1 -0
- package/lib/data/index.js.map +1 -0
- package/lib/data/maybeNumber.js +5 -7
- package/lib/data/maybeNumber.js.map +1 -0
- package/lib/data/parseSort.js +22 -28
- package/lib/data/parseSort.js.map +1 -0
- package/lib/data/resolvePagination.js +13 -17
- package/lib/data/resolvePagination.js.map +1 -0
- package/lib/data/types.d.js +2 -0
- package/lib/data/types.d.js.map +1 -0
- package/lib/dayjs/dayjs.js +21 -19
- package/lib/dayjs/dayjs.js.map +1 -0
- package/lib/dayjs/formatDuration.js +15 -14
- package/lib/dayjs/formatDuration.js.map +1 -0
- package/lib/dayjs/index.js +2 -0
- package/lib/dayjs/index.js.map +1 -0
- package/lib/dayjs/parseDuration.js +5 -8
- package/lib/dayjs/parseDuration.js.map +1 -0
- package/lib/dayjs/parseRelativeTime.js +90 -0
- package/lib/dayjs/parseRelativeTime.js.map +1 -0
- package/lib/dayjs/parseRelativeTime.test.js +247 -0
- package/lib/dayjs/resolveRelativeTime.js +158 -0
- package/lib/dayjs/resolveRelativeTime.js.map +1 -0
- package/lib/dayjs/resolveRelativeTime.test.js +310 -0
- package/lib/decimal/index.js +1 -0
- package/lib/decimal/index.js.map +1 -0
- package/lib/decimal/parseDecimal.js +3 -1
- package/lib/decimal/parseDecimal.js.map +1 -0
- package/lib/emittery/emitter.js +10 -0
- package/lib/emittery/emitter.js.map +1 -0
- package/lib/emittery/index.js +2 -0
- package/lib/emittery/index.js.map +1 -0
- package/lib/foundation/schema/SexType.js +5 -3
- package/lib/foundation/schema/SexType.js.map +1 -0
- package/lib/foundation/schema/index.js +1 -0
- package/lib/foundation/schema/index.js.map +1 -0
- package/lib/foundation/schema/parseSexType.js +1 -0
- package/lib/foundation/schema/parseSexType.js.map +1 -0
- package/lib/foundation/schema/types.js +4 -2
- package/lib/foundation/schema/types.js.map +1 -0
- package/lib/fs/FileSystemError.js +23 -0
- package/lib/fs/FileSystemError.js.map +1 -0
- package/lib/fs/IFileSystem.d.js +3 -0
- package/lib/fs/IFileSystem.d.js.map +1 -0
- package/lib/fs/MemoryFileSystem.test.js +188 -0
- package/lib/fs/createBrowserFileSystem.js +248 -0
- package/lib/fs/createBrowserFileSystem.js.map +1 -0
- package/lib/fs/createMemoryFileSystem.js +516 -0
- package/lib/fs/createMemoryFileSystem.js.map +1 -0
- package/lib/fs/createSandboxFileSystem.js +108 -0
- package/lib/fs/createSandboxFileSystem.js.map +1 -0
- package/lib/fs/createWebDavFileSystem.js +137 -0
- package/lib/fs/createWebDavFileSystem.js.map +1 -0
- package/lib/fs/findMimeType.js +17 -0
- package/lib/fs/findMimeType.js.map +1 -0
- package/lib/fs/index.js +8 -0
- package/lib/fs/index.js.map +1 -0
- package/lib/fs/orpc/FileSystemContract.js +93 -0
- package/lib/fs/orpc/FileSystemContract.js.map +1 -0
- package/lib/fs/orpc/createContractClientFileSystem.js +93 -0
- package/lib/fs/orpc/createContractClientFileSystem.js.map +1 -0
- package/lib/fs/orpc/index.js +3 -0
- package/lib/fs/orpc/index.js.map +1 -0
- package/lib/fs/orpc/server/createFileSystemContractImpl.js +63 -0
- package/lib/fs/orpc/server/createFileSystemContractImpl.js.map +1 -0
- package/lib/fs/orpc/server/index.js +2 -0
- package/lib/fs/orpc/server/index.js.map +1 -0
- package/lib/fs/s3/createS3MiniFileSystem.js +705 -0
- package/lib/fs/s3/createS3MiniFileSystem.js.map +1 -0
- package/lib/fs/s3/index.js +2 -0
- package/lib/fs/s3/index.js.map +1 -0
- package/lib/fs/s3/s3mini.test.js +584 -0
- package/lib/fs/scandir.js +59 -0
- package/lib/fs/scandir.js.map +1 -0
- package/lib/fs/server/createDatabaseFileSystem.js +750 -0
- package/lib/fs/server/createDatabaseFileSystem.js.map +1 -0
- package/lib/fs/server/createNodeFileSystem.js +401 -0
- package/lib/fs/server/createNodeFileSystem.js.map +1 -0
- package/lib/fs/server/dbfs.test.js +221 -0
- package/lib/fs/server/index.js +2 -0
- package/lib/fs/server/index.js.map +1 -0
- package/lib/fs/server/loadTestDatabase.js +127 -0
- package/lib/fs/server/loadTestDatabase.js.map +1 -0
- package/lib/fs/tests/runFileSystemTest.js +318 -0
- package/lib/fs/tests/runFileSystemTest.js.map +1 -0
- package/lib/fs/types.js +27 -0
- package/lib/fs/types.js.map +1 -0
- package/lib/fs/utils/getFileUrl.js +35 -0
- package/lib/fs/utils/getFileUrl.js.map +1 -0
- package/lib/fs/utils.js +22 -0
- package/lib/fs/utils.js.map +1 -0
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -0
- package/lib/jsonschema/JsonSchema.js +146 -172
- package/lib/jsonschema/JsonSchema.js.map +1 -0
- package/lib/jsonschema/forEachJsonSchema.js +44 -0
- package/lib/jsonschema/forEachJsonSchema.js.map +1 -0
- package/lib/jsonschema/index.js +2 -0
- package/lib/jsonschema/index.js.map +1 -0
- package/lib/jsonschema/types.d.js +2 -0
- package/lib/jsonschema/types.d.js.map +1 -0
- package/lib/meta/defineFileType.js +20 -103
- package/lib/meta/defineFileType.js.map +1 -0
- package/lib/meta/defineInit.js +31 -250
- package/lib/meta/defineInit.js.map +1 -0
- package/lib/meta/defineMetadata.js +24 -140
- package/lib/meta/defineMetadata.js.map +1 -0
- package/lib/meta/index.js +1 -0
- package/lib/meta/index.js.map +1 -0
- package/lib/orpc/createOpenApiContractClient.js +27 -0
- package/lib/orpc/createOpenApiContractClient.js.map +1 -0
- package/lib/orpc/createRpcContractClient.js +34 -0
- package/lib/orpc/createRpcContractClient.js.map +1 -0
- package/lib/orpc/index.js +3 -0
- package/lib/orpc/index.js.map +1 -0
- package/lib/orpc/resolveLinkPlugins.js +28 -0
- package/lib/orpc/resolveLinkPlugins.js.map +1 -0
- package/lib/password/PHC.js +63 -87
- package/lib/password/PHC.js.map +1 -0
- package/lib/password/PHC.test.js +11 -3
- package/lib/password/Password.js +29 -294
- package/lib/password/Password.js.map +1 -0
- package/lib/password/Password.test.js +35 -22
- package/lib/password/createArgon2PasswordAlgorithm.js +35 -191
- package/lib/password/createArgon2PasswordAlgorithm.js.map +1 -0
- package/lib/password/createBase64PasswordAlgorithm.js +8 -141
- package/lib/password/createBase64PasswordAlgorithm.js.map +1 -0
- package/lib/password/createBcryptPasswordAlgorithm.js +13 -168
- package/lib/password/createBcryptPasswordAlgorithm.js.map +1 -0
- package/lib/password/createPBKDF2PasswordAlgorithm.js +46 -228
- package/lib/password/createPBKDF2PasswordAlgorithm.js.map +1 -0
- package/lib/password/createScryptPasswordAlgorithm.js +55 -211
- package/lib/password/createScryptPasswordAlgorithm.js.map +1 -0
- package/lib/password/index.js +1 -0
- package/lib/password/index.js.map +1 -0
- package/lib/password/server/index.js +1 -0
- package/lib/password/server/index.js.map +1 -0
- package/lib/resource/Identifiable.js +2 -0
- package/lib/resource/Identifiable.js.map +1 -0
- package/lib/resource/ListQuery.js +21 -93
- package/lib/resource/ListQuery.js.map +1 -0
- package/lib/resource/getTitleOfResource.js +3 -5
- package/lib/resource/getTitleOfResource.js.map +1 -0
- package/lib/resource/index.js +1 -0
- package/lib/resource/index.js.map +1 -0
- package/lib/resource/schema/AnyResourceSchema.js +2 -1
- package/lib/resource/schema/AnyResourceSchema.js.map +1 -0
- package/lib/resource/schema/BaseResourceSchema.js +2 -1
- package/lib/resource/schema/BaseResourceSchema.js.map +1 -0
- package/lib/resource/schema/ResourceActionType.js +6 -4
- package/lib/resource/schema/ResourceActionType.js.map +1 -0
- package/lib/resource/schema/ResourceStatus.js +5 -3
- package/lib/resource/schema/ResourceStatus.js.map +1 -0
- package/lib/resource/schema/ResourceType.js +5 -3
- package/lib/resource/schema/ResourceType.js.map +1 -0
- package/lib/resource/schema/index.js +1 -0
- package/lib/resource/schema/index.js.map +1 -0
- package/lib/resource/schema/types.js +16 -20
- package/lib/resource/schema/types.js.map +1 -0
- package/lib/s3/formatS3Url.js +65 -0
- package/lib/s3/formatS3Url.js.map +1 -0
- package/lib/s3/formatS3Url.test.js +262 -0
- package/lib/s3/index.js +3 -0
- package/lib/s3/index.js.map +1 -0
- package/lib/s3/parseS3Url.js +65 -0
- package/lib/s3/parseS3Url.js.map +1 -0
- package/lib/s3/parseS3Url.test.js +270 -0
- package/lib/schema/SchemaRegistry.js +38 -38
- package/lib/schema/SchemaRegistry.js.map +1 -0
- package/lib/schema/TypeSchema.d.js +2 -0
- package/lib/schema/TypeSchema.d.js.map +1 -0
- package/lib/schema/createSchemaData.js +26 -125
- package/lib/schema/createSchemaData.js.map +1 -0
- package/lib/schema/findJsonSchemaByPath.js +13 -36
- package/lib/schema/findJsonSchemaByPath.js.map +1 -0
- package/lib/schema/formatZodError.js +140 -0
- package/lib/schema/formatZodError.js.map +1 -0
- package/lib/schema/formatZodError.test.js +196 -0
- package/lib/schema/getSchemaCache.js +5 -5
- package/lib/schema/getSchemaCache.js.map +1 -0
- package/lib/schema/getSchemaOptions.js +8 -11
- package/lib/schema/getSchemaOptions.js.map +1 -0
- package/lib/schema/index.js +2 -1
- package/lib/schema/index.js.map +1 -0
- package/lib/schema/toJsonSchema.js +47 -290
- package/lib/schema/toJsonSchema.js.map +1 -0
- package/lib/schema/validate.js +33 -45
- package/lib/schema/validate.js.map +1 -0
- package/lib/tools/generateSchema.js +39 -197
- package/lib/tools/generateSchema.js.map +1 -0
- package/lib/tools/renderJsonSchemaToMarkdownDoc.js +55 -143
- package/lib/tools/renderJsonSchemaToMarkdownDoc.js.map +1 -0
- package/lib/utils/buildBaseUrl.js +13 -0
- package/lib/utils/buildBaseUrl.js.map +1 -0
- package/lib/utils/buildRedactorFormSchema.js +59 -0
- package/lib/utils/buildRedactorFormSchema.js.map +1 -0
- package/lib/utils/getEstimateProcessTime.js +12 -11
- package/lib/utils/getEstimateProcessTime.js.map +1 -0
- package/lib/utils/index.js +3 -0
- package/lib/utils/index.js.map +1 -0
- package/lib/utils/resolveFeatureOptions.js +12 -0
- package/lib/utils/resolveFeatureOptions.js.map +1 -0
- package/package.json +61 -13
- package/src/ai/qwen3vl/index.ts +1 -0
- package/src/ai/qwen3vl/utils.ts +36 -0
- package/src/ai/vision/DocLayoutElementTypeSchema.ts +30 -0
- package/src/ai/vision/ImageAnnotationSchema.ts +60 -0
- package/src/ai/vision/index.ts +2 -0
- package/src/ai/vision/resolveImageAnnotation.ts +135 -0
- package/src/cn/ChineseResidentIdNo.test.ts +1 -1
- package/src/cn/ChineseResidentIdNo.ts +8 -0
- package/src/cn/DivisionCode.test.ts +1 -1
- package/src/cn/DivisionCode.ts +8 -0
- package/src/cn/UnifiedSocialCreditCode.test.ts +1 -1
- package/src/cn/UnifiedSocialCreditCode.ts +15 -0
- package/src/cn/__snapshots__/UnifiedSocialCreditCode.test.ts.snap +23 -0
- package/src/cn/formatChineseAmount.ts +61 -0
- package/src/cn/index.ts +7 -1
- package/src/cn/parseChineseNumber.test.ts +159 -0
- package/src/cn/parseChineseNumber.ts +97 -0
- package/src/cn/pinyin/cartesianProduct.test.ts +64 -0
- package/src/cn/pinyin/cartesianProduct.ts +24 -0
- package/src/cn/pinyin/data.json +23573 -0
- package/src/cn/pinyin/loader.ts +12 -0
- package/src/cn/pinyin/preload.ts +3 -0
- package/src/cn/pinyin/toPinyin.test.ts +12 -0
- package/src/cn/pinyin/toPinyinPure.ts +43 -0
- package/src/cn/pinyin/transform.ts +12 -0
- package/src/consola/formatLogObject.test.ts +27 -0
- package/src/consola/formatLogObject.ts +34 -6
- package/src/dayjs/dayjs.ts +18 -18
- package/src/dayjs/index.ts +3 -1
- package/src/dayjs/parseRelativeTime.test.ts +185 -0
- package/src/dayjs/parseRelativeTime.ts +115 -0
- package/src/dayjs/resolveRelativeTime.test.ts +357 -0
- package/src/dayjs/resolveRelativeTime.ts +167 -0
- package/src/emittery/emitter.ts +9 -0
- package/src/emittery/index.ts +1 -0
- package/src/fs/FileSystemError.ts +26 -0
- package/src/fs/IFileSystem.d.ts +102 -0
- package/src/fs/MemoryFileSystem.test.ts +37 -0
- package/src/fs/createBrowserFileSystem.ts +291 -0
- package/src/fs/createMemoryFileSystem.ts +604 -0
- package/src/fs/createSandboxFileSystem.ts +136 -0
- package/src/fs/createWebDavFileSystem.ts +172 -0
- package/src/fs/findMimeType.ts +23 -0
- package/src/fs/index.ts +8 -0
- package/src/fs/orpc/FileSystemContract.ts +92 -0
- package/src/fs/orpc/createContractClientFileSystem.ts +115 -0
- package/src/fs/orpc/index.ts +2 -0
- package/src/fs/orpc/server/createFileSystemContractImpl.ts +64 -0
- package/src/fs/orpc/server/index.ts +1 -0
- package/src/fs/s3/createS3MiniFileSystem.ts +830 -0
- package/src/fs/s3/index.ts +1 -0
- package/src/fs/s3/s3mini.test.ts +264 -0
- package/src/fs/scandir.ts +75 -0
- package/src/fs/server/createDatabaseFileSystem.ts +668 -0
- package/src/fs/server/createNodeFileSystem.ts +499 -0
- package/src/fs/server/dbfs.test.ts +47 -0
- package/src/fs/server/index.ts +1 -0
- package/src/fs/server/loadTestDatabase.ts +131 -0
- package/src/fs/tests/runFileSystemTest.ts +288 -0
- package/src/fs/types.ts +29 -0
- package/src/fs/utils/getFileUrl.ts +44 -0
- package/src/fs/utils.ts +23 -0
- package/src/jsonschema/JsonSchema.ts +118 -110
- package/src/jsonschema/forEachJsonSchema.ts +50 -0
- package/src/jsonschema/index.ts +1 -0
- package/src/orpc/createOpenApiContractClient.ts +52 -0
- package/src/orpc/createRpcContractClient.ts +50 -0
- package/src/orpc/index.ts +2 -0
- package/src/orpc/resolveLinkPlugins.ts +29 -0
- package/src/password/PHC.ts +3 -3
- package/src/password/Password.test.ts +1 -1
- package/src/password/createPBKDF2PasswordAlgorithm.ts +2 -2
- package/src/resource/schema/AnyResourceSchema.ts +16 -2
- package/src/s3/formatS3Url.test.ts +254 -0
- package/src/s3/formatS3Url.ts +84 -0
- package/src/s3/index.ts +2 -0
- package/src/s3/parseS3Url.test.ts +258 -0
- package/src/s3/parseS3Url.ts +88 -0
- package/src/schema/SchemaRegistry.ts +35 -33
- package/src/schema/formatZodError.test.ts +196 -0
- package/src/schema/formatZodError.ts +151 -0
- package/src/schema/getSchemaOptions.ts +2 -2
- package/src/schema/index.ts +1 -1
- package/src/utils/buildBaseUrl.ts +12 -0
- package/src/utils/buildRedactorFormSchema.ts +85 -0
- package/src/utils/index.ts +4 -0
- package/src/utils/resolveFeatureOptions.ts +14 -0
- package/src/cn/ChineseResidentIdNo.mod.ts +0 -7
- package/src/cn/DivisionCode.mod.ts +0 -7
- package/src/cn/UnifiedSocialCreditCode.mod.ts +0 -7
- package/src/cn/mod.ts +0 -3
- package/src/schema/SchemaRegistry.mod.ts +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { createS3MiniFileSystem } from './createS3MiniFileSystem';
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import { S3mini } from 's3mini';
|
|
2
|
+
import { afterAll, beforeAll, describe, expect, test } from 'vitest';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Tests for s3mini and S3 filesystem behavior
|
|
6
|
+
*
|
|
7
|
+
* Uses MinIO Play server for integration testing:
|
|
8
|
+
* - Endpoint: play.min.io
|
|
9
|
+
* - Access Key: Q3AM3UQ867SPQQA43P2F
|
|
10
|
+
* - Secret Key: zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG
|
|
11
|
+
*
|
|
12
|
+
* Note: s3mini expects bucket name to be part of the endpoint URL.
|
|
13
|
+
* e.g., https://play.min.io/bucket-name
|
|
14
|
+
*
|
|
15
|
+
* s3mini does not support anonymous access to public S3 buckets.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
// Set to true to run network tests
|
|
19
|
+
const RUN_NETWORK_TESTS = process.env.RUN_S3_NETWORK_TESTS === 'true';
|
|
20
|
+
|
|
21
|
+
// Generate unique bucket name for this test run
|
|
22
|
+
const TEST_BUCKET = `s3mini-test-${Date.now()}`;
|
|
23
|
+
|
|
24
|
+
describe('s3mini with MinIO Play', () => {
|
|
25
|
+
let client: S3mini;
|
|
26
|
+
|
|
27
|
+
beforeAll(async () => {
|
|
28
|
+
if (!RUN_NETWORK_TESTS) return;
|
|
29
|
+
|
|
30
|
+
// s3mini expects bucket name in the endpoint URL
|
|
31
|
+
client = new S3mini({
|
|
32
|
+
endpoint: `https://play.min.io/${TEST_BUCKET}`,
|
|
33
|
+
accessKeyId: 'Q3AM3UQ867SPQQA43P2F',
|
|
34
|
+
secretAccessKey: 'zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG',
|
|
35
|
+
region: 'us-east-1',
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Create test bucket
|
|
39
|
+
try {
|
|
40
|
+
await client.createBucket();
|
|
41
|
+
console.log(`Created bucket: ${TEST_BUCKET}`);
|
|
42
|
+
} catch (e) {
|
|
43
|
+
console.log('Bucket creation error:', e);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
afterAll(async () => {
|
|
48
|
+
if (!RUN_NETWORK_TESTS || !client) return;
|
|
49
|
+
|
|
50
|
+
// Cleanup: delete all objects and bucket
|
|
51
|
+
try {
|
|
52
|
+
const objects = await client.listObjects('/', '', 1000);
|
|
53
|
+
if (objects && objects.length > 0) {
|
|
54
|
+
const keys = objects.map((o) => o.Key).filter(Boolean) as string[];
|
|
55
|
+
if (keys.length > 0) {
|
|
56
|
+
await client.deleteObjects(keys);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// Note: s3mini doesn't have deleteBucket - we'll leave the bucket
|
|
60
|
+
// MinIO Play auto-cleans old buckets
|
|
61
|
+
} catch (e) {
|
|
62
|
+
console.log('Cleanup error:', e);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
describe.skipIf(!RUN_NETWORK_TESTS)('listObjects with delimiter', () => {
|
|
67
|
+
test('should list objects and detect directories', async () => {
|
|
68
|
+
// Create test structure
|
|
69
|
+
await client.putObject('file1.txt', 'content1');
|
|
70
|
+
await client.putObject('file2.txt', 'content2');
|
|
71
|
+
await client.putObject('dir1/', ''); // Directory marker
|
|
72
|
+
await client.putObject('dir1/nested.txt', 'nested content');
|
|
73
|
+
await client.putObject('dir2/', ''); // Another directory marker
|
|
74
|
+
|
|
75
|
+
// List with delimiter
|
|
76
|
+
const objects = await client.listObjects('/', '', 100, {
|
|
77
|
+
delimiter: '/',
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
console.log('MinIO Play listing with delimiter:', JSON.stringify(objects, null, 2));
|
|
81
|
+
|
|
82
|
+
expect(objects).toBeDefined();
|
|
83
|
+
expect(Array.isArray(objects)).toBe(true);
|
|
84
|
+
|
|
85
|
+
if (objects && objects.length > 0) {
|
|
86
|
+
// Check for files and directories
|
|
87
|
+
const dirs = objects.filter((o) => o.Key?.endsWith('/'));
|
|
88
|
+
const files = objects.filter((o) => !o.Key?.endsWith('/'));
|
|
89
|
+
|
|
90
|
+
console.log(`Found ${dirs.length} directories and ${files.length} files`);
|
|
91
|
+
console.log(
|
|
92
|
+
'Directories:',
|
|
93
|
+
dirs.map((d) => d.Key),
|
|
94
|
+
);
|
|
95
|
+
console.log(
|
|
96
|
+
'Files:',
|
|
97
|
+
files.map((f) => f.Key),
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
// Directories should have Size: 0
|
|
101
|
+
for (const dir of dirs) {
|
|
102
|
+
expect(Number(dir.Size)).toBe(0);
|
|
103
|
+
expect(dir.Key).toMatch(/\/$/);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Files should have Size > 0
|
|
107
|
+
for (const file of files) {
|
|
108
|
+
expect(Number(file.Size)).toBeGreaterThan(0);
|
|
109
|
+
expect(file.Key).not.toMatch(/\/$/);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test('should list nested directory contents', async () => {
|
|
115
|
+
// List dir1/ contents
|
|
116
|
+
const objects = await client.listObjects('/', 'dir1/', 100, {
|
|
117
|
+
delimiter: '/',
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
console.log('MinIO Play dir1/ listing:', JSON.stringify(objects, null, 2));
|
|
121
|
+
|
|
122
|
+
expect(objects).toBeDefined();
|
|
123
|
+
expect(Array.isArray(objects)).toBe(true);
|
|
124
|
+
|
|
125
|
+
// Should find nested.txt
|
|
126
|
+
const files = objects?.filter((o) => !o.Key?.endsWith('/')) || [];
|
|
127
|
+
expect(files.some((f) => f.Key?.includes('nested.txt'))).toBe(true);
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Unit tests for S3 directory detection logic (no network required)
|
|
134
|
+
*
|
|
135
|
+
* When using delimiter: '/' with listObjects, S3 returns:
|
|
136
|
+
* - Files: Key without trailing /, Size > 0
|
|
137
|
+
* - Directories: Key with trailing /, Size: 0
|
|
138
|
+
*/
|
|
139
|
+
describe('s3mini listObjects behavior', () => {
|
|
140
|
+
test('directory detection logic - directories end with / and have Size: 0', () => {
|
|
141
|
+
// Mock response similar to user's example
|
|
142
|
+
const mockResponse = [
|
|
143
|
+
{
|
|
144
|
+
Key: 'fusion/README.md',
|
|
145
|
+
LastModified: '2025-12-18T01:51:09.000Z',
|
|
146
|
+
ETag: '"adc69293e8fd256b2609664f1e11cb53"',
|
|
147
|
+
Size: 6,
|
|
148
|
+
StorageClass: 'STANDARD',
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
Key: 'fusion/home/',
|
|
152
|
+
Size: 0,
|
|
153
|
+
LastModified: new Date('1970-01-01T00:00:00.000Z'),
|
|
154
|
+
ETag: '',
|
|
155
|
+
StorageClass: '',
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
Key: 'fusion/mcp/',
|
|
159
|
+
Size: 0,
|
|
160
|
+
LastModified: new Date('1970-01-01T00:00:00.000Z'),
|
|
161
|
+
ETag: '',
|
|
162
|
+
StorageClass: '',
|
|
163
|
+
},
|
|
164
|
+
];
|
|
165
|
+
|
|
166
|
+
// Test directory detection logic
|
|
167
|
+
for (const obj of mockResponse) {
|
|
168
|
+
const isDir = obj.Key.endsWith('/');
|
|
169
|
+
|
|
170
|
+
if (isDir) {
|
|
171
|
+
expect(obj.Size).toBe(0);
|
|
172
|
+
expect(obj.Key).toMatch(/\/$/);
|
|
173
|
+
} else {
|
|
174
|
+
expect(obj.Size).toBeGreaterThan(0);
|
|
175
|
+
expect(obj.Key).not.toMatch(/\/$/);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Count directories and files
|
|
180
|
+
const dirs = mockResponse.filter((o) => o.Key.endsWith('/'));
|
|
181
|
+
const files = mockResponse.filter((o) => !o.Key.endsWith('/'));
|
|
182
|
+
|
|
183
|
+
expect(dirs).toHaveLength(2);
|
|
184
|
+
expect(files).toHaveLength(1);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
test('readdir should properly identify directories from S3 response', () => {
|
|
188
|
+
const s3Response = [
|
|
189
|
+
{ Key: 'fusion/README.md', Size: 6, LastModified: new Date(), ETag: '"abc"' },
|
|
190
|
+
{ Key: 'fusion/home/', Size: 0, LastModified: new Date(0), ETag: '' },
|
|
191
|
+
{ Key: 'fusion/mcp/', Size: 0, LastModified: new Date(0), ETag: '' },
|
|
192
|
+
];
|
|
193
|
+
|
|
194
|
+
const results = s3Response.map((obj) => {
|
|
195
|
+
const key = obj.Key;
|
|
196
|
+
const isDir = key.endsWith('/');
|
|
197
|
+
const name = isDir ? key.slice(0, -1).split('/').pop() : key.split('/').pop();
|
|
198
|
+
|
|
199
|
+
return {
|
|
200
|
+
name,
|
|
201
|
+
kind: isDir ? 'directory' : 'file',
|
|
202
|
+
size: obj.Size,
|
|
203
|
+
};
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
expect(results).toEqual([
|
|
207
|
+
{ name: 'README.md', kind: 'file', size: 6 },
|
|
208
|
+
{ name: 'home', kind: 'directory', size: 0 },
|
|
209
|
+
{ name: 'mcp', kind: 'directory', size: 0 },
|
|
210
|
+
]);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
test('should handle nested paths correctly', () => {
|
|
214
|
+
const s3Response = [
|
|
215
|
+
{ Key: 'data/2024/01/file.csv', Size: 1024 },
|
|
216
|
+
{ Key: 'data/2024/02/', Size: 0 },
|
|
217
|
+
{ Key: 'data/config.json', Size: 256 },
|
|
218
|
+
];
|
|
219
|
+
|
|
220
|
+
const prefix = 'data/';
|
|
221
|
+
const results = s3Response.map((obj) => {
|
|
222
|
+
const key = obj.Key;
|
|
223
|
+
const relativeKey = key.startsWith(prefix) ? key.slice(prefix.length) : key;
|
|
224
|
+
const isDir = key.endsWith('/');
|
|
225
|
+
|
|
226
|
+
const parts = relativeKey.split('/').filter(Boolean);
|
|
227
|
+
const name = parts[0];
|
|
228
|
+
|
|
229
|
+
return {
|
|
230
|
+
key,
|
|
231
|
+
relativeKey,
|
|
232
|
+
name,
|
|
233
|
+
isDir,
|
|
234
|
+
};
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
// For non-recursive listing, only want immediate children
|
|
238
|
+
const immediateChildren = results.filter((r) => {
|
|
239
|
+
const keyWithoutTrailingSlash = r.relativeKey.replace(/\/$/, '');
|
|
240
|
+
return !keyWithoutTrailingSlash.includes('/');
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
expect(immediateChildren).toHaveLength(1);
|
|
244
|
+
expect(immediateChildren[0].name).toBe('config.json');
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
test('should correctly strip prefix and build file stat', () => {
|
|
248
|
+
const prefix = 'myapp/data';
|
|
249
|
+
const normalizedPrefix = prefix.replace(/^\/+/, '').replace(/\/+$/, '');
|
|
250
|
+
|
|
251
|
+
const stripPrefix = (key: string): string => {
|
|
252
|
+
if (!normalizedPrefix || !key.startsWith(normalizedPrefix + '/')) {
|
|
253
|
+
return key.startsWith('/') ? key : '/' + key;
|
|
254
|
+
}
|
|
255
|
+
const withoutPrefix = key.slice(normalizedPrefix.length);
|
|
256
|
+
return withoutPrefix || '/';
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
expect(stripPrefix('myapp/data/file.txt')).toBe('/file.txt');
|
|
260
|
+
expect(stripPrefix('myapp/data/subdir/')).toBe('/subdir/');
|
|
261
|
+
expect(stripPrefix('myapp/data/')).toBe('/');
|
|
262
|
+
expect(stripPrefix('other/path')).toBe('/other/path');
|
|
263
|
+
});
|
|
264
|
+
});
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import pathe from 'pathe';
|
|
2
|
+
import type { IFileStat } from './IFileSystem';
|
|
3
|
+
|
|
4
|
+
export type ScandirOptions = {
|
|
5
|
+
readdir: (path: string) => Promise<IFileStat[]>;
|
|
6
|
+
path: string;
|
|
7
|
+
signal?: AbortSignal;
|
|
8
|
+
cursor?: string;
|
|
9
|
+
depth?: number;
|
|
10
|
+
limit?: number;
|
|
11
|
+
strategy?: 'depth' | 'breadth';
|
|
12
|
+
filter?: (file: IFileStat) => boolean;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export async function* scandir(options: ScandirOptions): AsyncGenerator<IFileStat> {
|
|
16
|
+
const { readdir, path: startPath, signal, depth = 1, limit, strategy = 'depth', filter = () => true } = options;
|
|
17
|
+
let cursor = options.cursor;
|
|
18
|
+
if (cursor && !cursor.startsWith('/')) {
|
|
19
|
+
cursor = pathe.join(startPath, cursor);
|
|
20
|
+
}
|
|
21
|
+
if (cursor && !cursor.startsWith(startPath)) {
|
|
22
|
+
throw new Error(`Cursor path "${cursor}" is not within the start path "${startPath}"`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (signal?.aborted) return;
|
|
26
|
+
|
|
27
|
+
// Collection to manage which directories to visit next.
|
|
28
|
+
// For 'depth'-first, it acts as a Stack (LIFO).
|
|
29
|
+
// For 'breadth'-first, it acts as a Queue (FIFO).
|
|
30
|
+
const collection: { path: string; level: number }[] = [{ path: startPath, level: 1 }];
|
|
31
|
+
|
|
32
|
+
let yielded = 0;
|
|
33
|
+
let cursorFound = !cursor; // If no cursor is provided, we can start yielding immediately.
|
|
34
|
+
|
|
35
|
+
while (collection.length > 0) {
|
|
36
|
+
if (signal?.aborted) return;
|
|
37
|
+
if (limit && yielded >= limit) return;
|
|
38
|
+
|
|
39
|
+
// Get the next directory to process based on the strategy.
|
|
40
|
+
const { path: currentPath, level: currentLevel } =
|
|
41
|
+
strategy === 'depth'
|
|
42
|
+
? collection.pop()! // LIFO for depth-first
|
|
43
|
+
: collection.shift()!; // FIFO for breadth-first
|
|
44
|
+
|
|
45
|
+
let entries: IFileStat[];
|
|
46
|
+
try {
|
|
47
|
+
entries = await readdir(currentPath);
|
|
48
|
+
} catch (error) {
|
|
49
|
+
// Could not read directory, skip it. You might want to log this error.
|
|
50
|
+
console.warn(`scandir: Could not read directory ${currentPath}`, error);
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
for (const file of entries) {
|
|
55
|
+
if (signal?.aborted) return;
|
|
56
|
+
|
|
57
|
+
if (!cursorFound) {
|
|
58
|
+
if (file.path === cursor) {
|
|
59
|
+
cursorFound = true;
|
|
60
|
+
}
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (filter(file)) {
|
|
65
|
+
yield file;
|
|
66
|
+
yielded++;
|
|
67
|
+
if (limit && yielded >= limit) return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (file.kind === 'directory' && currentLevel < depth) {
|
|
71
|
+
collection.push({ path: file.path, level: currentLevel + 1 });
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|