@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
|
+
{"version":3,"sources":["../../../src/fs/server/createDatabaseFileSystem.ts"],"sourcesContent":["import * as crypto from 'node:crypto';\nimport { basename, dirname, join, normalize } from 'node:path';\nimport {\n\tCascade,\n\tCollection,\n\tEntity,\n\tManyToOne,\n\tOneToMany,\n\tOneToOne,\n\tProperty,\n\ttypes,\n\tUnique,\n\ttype Opt,\n\ttype Rel,\n} from '@mikro-orm/core';\nimport type { EntityManager } from '@mikro-orm/knex';\nimport { TenantBaseEntity } from '@wener/server/entity';\nimport { getEntityManager } from '@wener/server/mikro-orm';\nimport type { CopyOptions } from 'fs-extra';\nimport { FileSystemError, FileSystemErrorCode } from '../FileSystemError';\nimport type {\n\tCreateReadStreamOptions,\n\tCreateWriteStreamOptions,\n\tIFileStat,\n\tIFileSystem,\n\tMkdirOptions,\n\tReaddirOptions,\n\tReadFileOptions,\n\tRenameOptions,\n\tRmOptions,\n\tStatOptions,\n\tWriteFileOptions,\n} from '../IFileSystem';\nimport { FileKind } from '../types';\n\nexport function createDatabaseFileSystem(options: Partial<IDatabaseFileSystemOptions> = {}): IDatabaseFileSystem {\n\treturn new DBFS(options);\n}\n\ntype IDatabaseFileSystem = IFileSystem & {\n\tensureRootNode(): Promise<FileNodeMetaEntity>;\n};\ntype IDatabaseFileSystemOptions = {\n\tgetEntityManager: () => EntityManager;\n\tsmallFileThreshold?: number;\n};\n\nclass DBFS implements IFileSystem {\n\toptions: IDatabaseFileSystemOptions;\n\n\tconstructor(options: Partial<IDatabaseFileSystemOptions> = {}) {\n\t\tthis.options = {\n\t\t\tgetEntityManager: () => getEntityManager<EntityManager>().fork(),\n\t\t\tsmallFileThreshold: 512,\n\t\t\t...options,\n\t\t};\n\t}\n\n\tget em() {\n\t\treturn this.options.getEntityManager();\n\t}\n\n\t/**\n\t * Ensure root node exists, create it if it doesn't exist\n\t * @returns The root FileNodeMetaEntity\n\t */\n\tasync ensureRootNode(): Promise<FileNodeMetaEntity> {\n\t\tconst em = this.em;\n\t\tconst rootQb = em.createQueryBuilder(FileNodeMetaEntity, 'f');\n\t\trootQb.where({ parent: null });\n\t\tconst rootNode = await rootQb.getSingleResult();\n\n\t\tif (rootNode) {\n\t\t\treturn rootNode;\n\t\t}\n\n\t\t// Create root directory (parent is null, filename is empty string)\n\t\tconst now = new Date();\n\t\tconst rootDir = em.create(FileNodeMetaEntity, {\n\t\t\tfilename: '',\n\t\t\tparent: null,\n\t\t\tkind: FileKind.directory,\n\t\t\tsize: 0,\n\t\t\tatime: now,\n\t\t\tbtime: now,\n\t\t\tctime: now,\n\t\t\tmtime: now,\n\t\t});\n\t\ttry {\n\t\t\tawait em.persistAndFlush(rootDir);\n\t\t\treturn rootDir;\n\t\t} catch (error: any) {\n\t\t\t// If root already exists (race condition), fetch and return it\n\t\t\tif (error.message?.includes('UNIQUE constraint') || error.message?.includes('duplicate')) {\n\t\t\t\tconst existingRootQb = em.createQueryBuilder(FileNodeMetaEntity, 'f');\n\t\t\t\texistingRootQb.where({ parent: null });\n\t\t\t\tconst existingRoot = await existingRootQb.getSingleResult();\n\t\t\t\tif (existingRoot) {\n\t\t\t\t\treturn existingRoot;\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tasync stat(path: string, options?: StatOptions): Promise<IFileStat> {\n\t\t// Validate input\n\t\tif (!path || typeof path !== 'string') {\n\t\t\tthrow new FileSystemError('Invalid path', FileSystemErrorCode.EINVAL);\n\t\t}\n\n\t\tconst em = this.em;\n\t\tconst node = await this._getNodeByPath(path, em);\n\t\tif (!node) {\n\t\t\tthrow new FileSystemError(`Path not found: ${path}`, FileSystemErrorCode.ENOENT);\n\t\t}\n\t\treturn this._toFileStat(node, path);\n\t}\n\n\tasync exists(path: string): Promise<boolean> {\n\t\treturn !!(await this._getNodeByPath(path, this.em));\n\t}\n\n\tasync readdir(dir: string, options?: ReaddirOptions): Promise<IFileStat[]> {\n\t\tconst em = this.em;\n\t\tconst parentNode = await this._getNodeByPath(dir, em);\n\n\t\tif (!parentNode) {\n\t\t\tthrow new FileSystemError(`Directory not found: ${dir}`, FileSystemErrorCode.ENOENT);\n\t\t}\n\t\tif (parentNode.kind !== FileKind.directory) {\n\t\t\tthrow new FileSystemError(`Path is not a directory: ${dir}`, FileSystemErrorCode.ENOTDIR);\n\t\t}\n\n\t\t// Use QueryBuilder to avoid automatic relationship loading\n\t\tconst qb = em.createQueryBuilder(FileNodeMetaEntity, 'f');\n\t\tqb.where({ parent: parentNode });\n\t\tconst children = await qb.getResult();\n\n\t\treturn children.map((child) => this._toFileStat(child, join(dir, child.filename)));\n\t}\n\n\tasync mkdir(path: string, options: MkdirOptions = {}): Promise<void> {\n\t\tawait this._mkdirInTransaction(path, options, this.em);\n\t}\n\n\tprivate async _mkdirInTransaction(path: string, options: MkdirOptions, em: EntityManager): Promise<void> {\n\t\tconst normalized = normalize(path);\n\n\t\t// Special handling for root directory\n\t\tif (normalized === '/') {\n\t\t\tconst rootQb = em.createQueryBuilder(FileNodeMetaEntity, 'f');\n\t\t\trootQb.where({ parent: null });\n\t\t\tconst rootNode = await rootQb.getSingleResult();\n\t\t\tif (rootNode) {\n\t\t\t\t// Root directory already exists\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// Create root directory (parent is null, filename is empty string)\n\t\t\tconst now = new Date();\n\t\t\tconst rootDir = em.create(FileNodeMetaEntity, {\n\t\t\t\tfilename: '',\n\t\t\t\tparent: null,\n\t\t\t\tkind: FileKind.directory,\n\t\t\t\tsize: 0,\n\t\t\t\tatime: now,\n\t\t\t\tbtime: now,\n\t\t\t\tctime: now,\n\t\t\t\tmtime: now,\n\t\t\t});\n\t\t\ttry {\n\t\t\t\tawait em.persistAndFlush(rootDir);\n\t\t\t} catch (error: any) {\n\t\t\t\t// If root already exists (race condition), ignore the error\n\t\t\t\tif (!error.message?.includes('UNIQUE constraint') && !error.message?.includes('duplicate')) {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tconst parentPath = dirname(normalized);\n\t\tconst newDirName = basename(normalized);\n\n\t\tif (!newDirName) throw new FileSystemError('Cannot create directory with empty name', FileSystemErrorCode.EINVAL);\n\n\t\tlet parentNode = await this._getNodeByPath(parentPath, em);\n\n\t\tif (!parentNode) {\n\t\t\tif (options.recursive) {\n\t\t\t\t// 递归创建父目录\n\t\t\t\tawait this._mkdirInTransaction(parentPath, options, em);\n\t\t\t\tparentNode = await this._getNodeByPath(parentPath, em);\n\t\t\t} else {\n\t\t\t\tthrow new FileSystemError(`Parent directory not found: ${parentPath}`, FileSystemErrorCode.ENOENT);\n\t\t\t}\n\t\t}\n\n\t\tif (!parentNode)\n\t\t\tthrow new FileSystemError('Failed to create parent directory structure', FileSystemErrorCode.EINVAL);\n\n\t\t// Check if directory already exists using QueryBuilder\n\t\tconst existingQb = em.createQueryBuilder(FileNodeMetaEntity, 'f');\n\t\texistingQb.where({ parent: parentNode, filename: newDirName });\n\t\tconst existing = await existingQb.getSingleResult();\n\n\t\tif (existing) {\n\t\t\tif (existing.kind === FileKind.directory) {\n\t\t\t\t// Directory already exists, return silently\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthrow new FileSystemError(`A file with the same name already exists: ${path}`, FileSystemErrorCode.EEXIST);\n\t\t}\n\n\t\t// Create directory using EntityManager\n\t\tconst now = new Date();\n\t\tconst newDir = em.create(FileNodeMetaEntity, {\n\t\t\ttid: parentNode.tid,\n\t\t\tparent: parentNode,\n\t\t\tfilename: newDirName,\n\t\t\tkind: FileKind.directory,\n\t\t\tsize: 0,\n\t\t\tatime: now,\n\t\t\tbtime: now,\n\t\t\tctime: now,\n\t\t\tmtime: now,\n\t\t});\n\t\tawait em.persistAndFlush(newDir);\n\t}\n\n\treadFile(path: string, options?: ReadFileOptions & { encoding: 'text' }): Promise<string>;\n\n\treadFile(path: string, options?: ReadFileOptions): Promise<Uint8Array>;\n\n\tasync readFile(\n\t\tpath: string,\n\t\toptions?: ReadFileOptions & {\n\t\t\tencoding?: 'text' | 'binary' | string;\n\t\t},\n\t): Promise<string | Uint8Array> {\n\t\tconst em = this.em;\n\t\tconst node = await this._getNodeByPath(path, em);\n\n\t\tif (!node) throw new FileSystemError(`File not found: ${path}`, FileSystemErrorCode.ENOENT);\n\t\tif (node.kind !== FileKind.file)\n\t\t\tthrow new FileSystemError(`Path is not a file: ${path}`, FileSystemErrorCode.EISDIR);\n\n\t\tlet buffer: Buffer;\n\t\tif (node.content) {\n\t\t\t// 小文件优化 - content is already loaded\n\t\t\tbuffer = node.content;\n\t\t} else {\n\t\t\t// Large file: load from file_node_content table using QueryBuilder\n\t\t\tconst fileContentQb = em.createQueryBuilder(FileNodeContentEntity, 'fc');\n\t\t\tfileContentQb.where({ node: node });\n\t\t\tconst fileContent = await fileContentQb.getSingleResult();\n\t\t\tif (!fileContent) throw new FileSystemError('File content is missing', FileSystemErrorCode.ENOENT);\n\t\t\tbuffer = fileContent.content;\n\t\t}\n\n\t\treturn options?.encoding === 'text' ? buffer.toString('utf-8') : buffer;\n\t}\n\n\tasync writeFile(path: string, data: string | Buffer, options: WriteFileOptions = {}): Promise<void> {\n\t\t// Validate input\n\t\tif (!path || typeof path !== 'string') {\n\t\t\tthrow new FileSystemError('Invalid path', FileSystemErrorCode.EINVAL);\n\t\t}\n\t\tif (data === null || data === undefined) {\n\t\t\tthrow new FileSystemError('Invalid data', FileSystemErrorCode.EINVAL);\n\t\t}\n\n\t\tawait this.em.transactional(async (em) => {\n\t\t\tconst { overwrite = true } = options;\n\t\t\tconst bufferData = Buffer.isBuffer(data) ? data : Buffer.from(data, 'utf-8');\n\t\t\tconst size = bufferData.length;\n\n\t\t\tconst parentPath = dirname(path);\n\t\t\tconst filename = basename(path);\n\n\t\t\t// Validate filename\n\t\t\tif (!filename) {\n\t\t\t\tthrow new FileSystemError('filename cannot be empty', FileSystemErrorCode.EINVAL);\n\t\t\t}\n\n\t\t\t// 确保父目录存在 - create it within the transaction\n\t\t\tawait this._mkdirInTransaction(parentPath, { recursive: true }, em);\n\t\t\tconst parentNode = await this._getNodeByPath(parentPath, em);\n\t\t\tif (!parentNode) throw new FileSystemError('Failed to establish parent directory', FileSystemErrorCode.EINVAL);\n\n\t\t\t// Find existing node using QueryBuilder\n\t\t\tconst nodeQb = em.createQueryBuilder(FileNodeMetaEntity, 'f');\n\t\t\tnodeQb.where({ parent: parentNode, filename });\n\t\t\tlet node = await nodeQb.getSingleResult();\n\n\t\t\tif (node) {\n\t\t\t\t// 文件已存在\n\t\t\t\tif (!overwrite) throw new FileSystemError(`File already exists: ${path}`, FileSystemErrorCode.EEXIST);\n\t\t\t\tif (node.kind === FileKind.directory)\n\t\t\t\t\tthrow new FileSystemError(`Cannot overwrite a directory with a file: ${path}`, FileSystemErrorCode.EISDIR);\n\n\t\t\t\t// 更新节点\n\t\t\t\tnode.size = size;\n\t\t\t\tnode.mtime = new Date();\n\t\t\t\t// ... 其他时间戳\n\t\t\t} else {\n\t\t\t\t// 新建文件\n\t\t\t\tconst now = new Date();\n\t\t\t\tnode = em.create(FileNodeMetaEntity, {\n\t\t\t\t\ttid: parentNode.tid,\n\t\t\t\t\tfilename,\n\t\t\t\t\tparent: parentNode,\n\t\t\t\t\tkind: FileKind.file,\n\t\t\t\t\tsize,\n\t\t\t\t\tatime: now,\n\t\t\t\t\tbtime: now,\n\t\t\t\t\tctime: now,\n\t\t\t\t\tmtime: now,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// 处理文件内容\n\t\t\tif (size <= this.options.smallFileThreshold!) {\n\t\t\t\t// Small file: store in file_node_meta.content\n\t\t\t\tnode.content = bufferData;\n\t\t\t\t// If there was large file content, delete it\n\t\t\t\t// Use QueryBuilder to avoid relationship issues\n\t\t\t\tconst existingContentQb = em.createQueryBuilder(FileNodeContentEntity, 'fc');\n\t\t\t\texistingContentQb.where({ node: node });\n\t\t\t\tconst existingContent = await existingContentQb.getSingleResult();\n\t\t\t\tif (existingContent) {\n\t\t\t\t\tawait em.removeAndFlush(existingContent);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Large file: store in file_node_content table\n\t\t\t\tnode.content = undefined; // Clear small file content\n\t\t\t\tconst md5 = crypto.createHash('md5').update(bufferData).digest('hex');\n\t\t\t\tconst sha256 = crypto.createHash('sha256').update(bufferData).digest('hex');\n\n\t\t\t\t// Check if fileContent already exists using QueryBuilder\n\t\t\t\tconst fileContentQb = em.createQueryBuilder(FileNodeContentEntity, 'fc');\n\t\t\t\tfileContentQb.where({ node: node });\n\t\t\t\tlet fileContent = await fileContentQb.getSingleResult();\n\n\t\t\t\tif (fileContent) {\n\t\t\t\t\t// Update existing content\n\t\t\t\t\tfileContent.content = bufferData;\n\t\t\t\t\tfileContent.size = size;\n\t\t\t\t\tfileContent.md5 = md5;\n\t\t\t\t\tfileContent.sha256 = sha256;\n\t\t\t\t} else {\n\t\t\t\t\t// Create new content entity\n\t\t\t\t\tfileContent = em.create(FileNodeContentEntity, {\n\t\t\t\t\t\tnode: node, // Use node relationship as primary key\n\t\t\t\t\t\ttid: node.tid,\n\t\t\t\t\t\tcontent: bufferData,\n\t\t\t\t\t\tsize: size,\n\t\t\t\t\t\tmd5: md5,\n\t\t\t\t\t\tsha256: sha256,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tawait em.persistAndFlush(node);\n\t\t});\n\t}\n\n\tasync rm(path: string, options: RmOptions = {}): Promise<void> {\n\t\tawait this.em.transactional(async (em) => {\n\t\t\tconst node = await this._getNodeByPath(path, em);\n\t\t\tif (!node) {\n\t\t\t\tif (options.force) return; // force=true, 不存在也算成功\n\t\t\t\tthrow new FileSystemError(`Path not found: ${path}`, FileSystemErrorCode.ENOENT);\n\t\t\t}\n\n\t\t\tif (node.kind === FileKind.directory && !options.recursive) {\n\t\t\t\t// Check if directory has children using QueryBuilder\n\t\t\t\tconst childrenQb = em.createQueryBuilder(FileNodeMetaEntity, 'f');\n\t\t\t\tchildrenQb.where({ parent: node });\n\t\t\t\tchildrenQb.select('id');\n\t\t\t\tconst children = await childrenQb.getResult();\n\t\t\t\tif (children.length > 0) {\n\t\t\t\t\tthrow new FileSystemError(`Directory not empty: ${path}`, FileSystemErrorCode.ENOTEMPTY);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// 使用 orphanRemoval: true, ORM会自动处理子节点和内容的删除\n\t\t\tawait em.removeAndFlush(node);\n\t\t});\n\t}\n\n\tasync rename(oldPath: string, newPath: string, options: RenameOptions = {}): Promise<void> {\n\t\tawait this.em.transactional(async (em) => {\n\t\t\tconst node = await this._getNodeByPath(oldPath, em);\n\t\t\tif (!node) throw new FileSystemError(`Source path not found: ${oldPath}`, FileSystemErrorCode.ENOENT);\n\n\t\t\tconst newParentPath = dirname(newPath);\n\t\t\tconst newFilename = basename(newPath);\n\n\t\t\tconst newParentNode = await this._getNodeByPath(newParentPath, em);\n\t\t\tif (!newParentNode)\n\t\t\t\tthrow new FileSystemError(`Destination directory not found: ${newParentPath}`, FileSystemErrorCode.ENOENT);\n\n\t\t\tconst existingDestQb = em.createQueryBuilder(FileNodeMetaEntity, 'f');\n\t\t\texistingDestQb.where({ parent: newParentNode, filename: newFilename });\n\t\t\tconst existingDest = await existingDestQb.getSingleResult();\n\t\t\tif (existingDest) {\n\t\t\t\tif (!options.overwrite)\n\t\t\t\t\tthrow new FileSystemError(`Destination path already exists: ${newPath}`, FileSystemErrorCode.EEXIST);\n\t\t\t\tif (node.id === existingDest.id) return; // 移动到原位置,什么都不做\n\t\t\t\tawait em.removeAndFlush(existingDest);\n\t\t\t}\n\n\t\t\tnode.parent = newParentNode;\n\t\t\tnode.filename = newFilename;\n\t\t\tnode.mtime = new Date();\n\n\t\t\tawait em.flush();\n\t\t});\n\t}\n\n\tasync copy(srcPath: string, destPath: string, options: CopyOptions = {}): Promise<void> {\n\t\tawait this.em.transactional(async (em) => {\n\t\t\tconst srcNode = await this._getNodeByPath(srcPath, em);\n\t\t\tif (!srcNode) throw new FileSystemError(`Source path not found: ${srcPath}`, FileSystemErrorCode.ENOENT);\n\n\t\t\tconst destParentPath = dirname(destPath);\n\t\t\tconst destFilename = basename(destPath);\n\n\t\t\tconst destParentNode = await this._getNodeByPath(destParentPath, em);\n\t\t\tif (!destParentNode)\n\t\t\t\tthrow new FileSystemError(`Destination directory not found: ${destParentPath}`, FileSystemErrorCode.ENOENT);\n\n\t\t\tconst existingDestQb = em.createQueryBuilder(FileNodeMetaEntity, 'f');\n\t\t\texistingDestQb.where({ parent: destParentNode, filename: destFilename });\n\t\t\tconst existingDest = await existingDestQb.getSingleResult();\n\t\t\tif (existingDest) {\n\t\t\t\tif (!options.overwrite)\n\t\t\t\t\tthrow new FileSystemError(`Destination path already exists: ${destPath}`, FileSystemErrorCode.EEXIST);\n\t\t\t\t// Delete existing destination within the same transaction\n\t\t\t\tawait em.removeAndFlush(existingDest);\n\t\t\t}\n\n\t\t\tawait this._copyNode(srcNode, destParentNode, destFilename, em);\n\t\t});\n\t}\n\n\tcreateReadStream(path: string, options?: CreateReadStreamOptions): never {\n\t\tthrow new Error('Streaming read is not supported by DBFS yet.');\n\t}\n\n\tcreateWriteStream(path: string, options?: CreateWriteStreamOptions): never {\n\t\tthrow new Error('Streaming write is not supported by DBFS yet.');\n\t}\n\n\tcreateReadableStream(path: string, options?: CreateReadStreamOptions): ReadableStream {\n\t\tthrow new Error('ReadableStream is not supported by DBFS yet.');\n\t}\n\n\tcreateWritableStream(path: string, options?: CreateWriteStreamOptions): WritableStream {\n\t\tthrow new Error('WritableStream is not supported by DBFS yet.');\n\t}\n\n\t/**\n\t * 将路径字符串解析为数据库中的节点。这是大部分操作的基础。\n\t * @param pathStr 绝对路径, e.g., /home/user/file.txt\n\t * @param em EntityManager 实例\n\t * @returns 找到的节点或 null\n\t */\n\tprivate async _getNodeByPath(pathStr: string, em: EntityManager): Promise<FileNodeMetaEntity | null> {\n\t\tconst normalized = normalize(pathStr);\n\n\t\tif (normalized === '/') {\n\t\t\t// Use QueryBuilder to avoid automatic relationship loading\n\t\t\tconst qb = em.createQueryBuilder(FileNodeMetaEntity, 'f');\n\t\t\tqb.where({ parent: null });\n\t\t\tconst rootNode = await qb.getSingleResult();\n\t\t\treturn rootNode || null;\n\t\t}\n\n\t\tconst parts = normalized.split('/').filter((p) => p);\n\n\t\t// Get root node\n\t\tconst rootQb = em.createQueryBuilder(FileNodeMetaEntity, 'f');\n\t\trootQb.where({ parent: null });\n\t\tlet currentNode = await rootQb.getSingleResult();\n\t\tif (!currentNode) return null;\n\n\t\t// Traverse path parts\n\t\tfor (const part of parts) {\n\t\t\tconst childQb = em.createQueryBuilder(FileNodeMetaEntity, 'f');\n\t\t\tchildQb.where({ parent: currentNode, filename: part });\n\t\t\tconst child = await childQb.getSingleResult();\n\t\t\tif (!child) return null;\n\t\t\tcurrentNode = child;\n\t\t}\n\n\t\treturn currentNode;\n\t}\n\n\t/**\n\t * 将数据库实体转换为 IFileStat 接口\n\t */\n\tprivate _toFileStat(node: FileNodeMetaEntity, path: string): IFileStat {\n\t\t// Handle mtime - it might be a Date object or a string from raw query\n\t\tlet mtime: number;\n\t\tif (node.mtime instanceof Date) {\n\t\t\tmtime = node.mtime.getTime();\n\t\t} else if (typeof node.mtime === 'string') {\n\t\t\tmtime = new Date(node.mtime).getTime();\n\t\t} else {\n\t\t\tmtime = Date.now();\n\t\t}\n\n\t\t// Handle size - convert bigint to number if needed\n\t\tlet size: number;\n\t\tif (typeof node.size === 'bigint') {\n\t\t\tsize = Number(node.size);\n\t\t} else {\n\t\t\tsize = node.size;\n\t\t}\n\n\t\treturn {\n\t\t\tpath: path,\n\t\t\tname: node.filename,\n\t\t\tkind: node.kind,\n\t\t\tsize: size,\n\t\t\tmtime: mtime,\n\t\t\tmeta: node.metadata || {},\n\t\t\t// `directory` 字段可以根据 path 动态计算\n\t\t\tdirectory: dirname(path),\n\t\t};\n\t}\n\n\tprivate async _copyNode(\n\t\tsrcNode: FileNodeMetaEntity,\n\t\tdestParent: FileNodeMetaEntity,\n\t\tnewName: string,\n\t\tem: EntityManager,\n\t): Promise<void> {\n\t\t// 1. 复制节点本身\n\t\tconst now = new Date();\n\t\tconst newNode = em.create(FileNodeMetaEntity, {\n\t\t\ttid: srcNode.tid,\n\t\t\tfilename: newName,\n\t\t\tparent: destParent,\n\t\t\tkind: srcNode.kind,\n\t\t\tsize: srcNode.size,\n\t\t\tmetadata: srcNode.metadata, // deep copy metadata\n\t\t\tcontent: srcNode.content, // copy small file content\n\t\t\tatime: now,\n\t\t\tbtime: now,\n\t\t\tctime: now,\n\t\t\tmtime: now,\n\t\t});\n\n\t\t// 2. 复制大文件内容 (如果存在) - use QueryBuilder to avoid relationship loading\n\t\tif (!srcNode.content) {\n\t\t\tconst srcFileContentQb = em.createQueryBuilder(FileNodeContentEntity, 'fc');\n\t\t\tsrcFileContentQb.where({ node: srcNode });\n\t\t\tconst srcFileContent = await srcFileContentQb.getSingleResult();\n\t\t\tif (srcFileContent) {\n\t\t\t\tconst newContent = em.create(FileNodeContentEntity, {\n\t\t\t\t\tnode: newNode, // Use node relationship as primary key\n\t\t\t\t\ttid: srcNode.tid,\n\t\t\t\t\tcontent: srcFileContent.content,\n\t\t\t\t\tsize: srcFileContent.size,\n\t\t\t\t\tmd5: srcFileContent.md5,\n\t\t\t\t\tsha256: srcFileContent.sha256,\n\t\t\t\t\tmimeType: srcFileContent.mimeType,\n\t\t\t\t\tmetadata: srcFileContent.metadata,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tawait em.persistAndFlush(newNode);\n\n\t\t// 3. 如果是目录,递归复制子节点\n\t\tif (srcNode.kind === FileKind.directory) {\n\t\t\tconst childrenQb = em.createQueryBuilder(FileNodeMetaEntity, 'f');\n\t\t\tchildrenQb.where({ parent: srcNode });\n\t\t\tconst children = await childrenQb.getResult();\n\t\t\tfor (const child of children) {\n\t\t\t\tawait this._copyNode(child, newNode, child.filename, em);\n\t\t\t}\n\t\t}\n\t}\n}\n\n@Entity({ tableName: 'file_node_meta' })\n@Unique({ properties: ['tid', 'parent', 'filename'] })\nexport class FileNodeMetaEntity extends TenantBaseEntity {\n\t@Property({ type: types.string, nullable: false, comment: '文件名' })\n\tfilename!: string;\n\t@Property({ type: types.bigint, nullable: false, default: 0, comment: '文件大小' })\n\tsize!: number & Opt;\n\t@Property({ type: types.string, nullable: false, comment: '文件类型' })\n\tkind!: FileKind;\n\n\t@Property({ type: types.datetime, nullable: false, defaultRaw: 'CURRENT_TIMESTAMP' })\n\tatime!: Date & Opt;\n\t@Property({ type: types.datetime, nullable: false, defaultRaw: 'CURRENT_TIMESTAMP' })\n\tbtime!: Date & Opt;\n\t@Property({ type: types.datetime, nullable: false, defaultRaw: 'CURRENT_TIMESTAMP' })\n\tctime!: Date & Opt;\n\t@Property({ type: types.datetime, nullable: false, defaultRaw: 'CURRENT_TIMESTAMP' })\n\tmtime!: Date & Opt;\n\n\t@Property({ type: types.json, nullable: false, defaultRaw: '{}' })\n\tmetadata!: Record<string, any>;\n\n\t@ManyToOne(() => FileNodeMetaEntity, { nullable: true, cascade: [] })\n\tparent?: Rel<FileNodeMetaEntity>;\n\n\t@OneToMany({ entity: () => FileNodeMetaEntity, mappedBy: 'parent', orphanRemoval: true })\n\tchildren = new Collection<FileNodeMetaEntity>(this);\n\n\t@OneToOne({\n\t\tentity: () => FileNodeContentEntity,\n\t\tmappedBy: 'node',\n\t\torphanRemoval: true,\n\t\tnullable: true,\n\t\tcascade: [Cascade.ALL],\n\t})\n\tfileContent?: Rel<FileNodeContentEntity>;\n\t@Property({ type: types.blob, nullable: true, comment: '文件内容' })\n\tcontent?: Buffer; // for small file, e.g. < 64k\n\n\t//region content\n\n\tget parentId() {\n\t\treturn this.parent?.id as Opt<string | undefined>;\n\t}\n\n\t//endregion\n}\n\n@Entity({ tableName: 'file_node_content' })\nexport class FileNodeContentEntity extends TenantBaseEntity {\n\t@OneToOne({ entity: () => FileNodeMetaEntity, owner: true, joinColumn: 'node_id' })\n\tnode!: Rel<FileNodeMetaEntity>;\n\n\t@Property({ type: types.integer, nullable: false })\n\tsize!: number; // 保留 size 方便查询分析\n\n\t@Property({ type: types.blob, lazy: true, comment: '文件内容' })\n\tcontent!: Buffer;\n\n\t@Property({ type: types.string, nullable: true })\n\tmimeType?: string;\n\n\t@Property({ type: types.string, nullable: false })\n\tmd5?: string;\n\t@Property({ type: types.string, nullable: false })\n\tsha256?: string;\n\n\t@Property({ type: types.string, nullable: true })\n\ttext?: string;\n\t@Property({ type: types.integer, nullable: true })\n\twidth?: number;\n\t@Property({ type: types.integer, nullable: true })\n\theight?: number;\n\t// @Property({ type: types.integer, nullable: true })\n\t// length?: number;\n\n\t@Property({ type: types.json, nullable: false, defaultRaw: '{}' })\n\tmetadata!: Record<string, any>;\n}\n"],"names":["crypto","basename","dirname","join","normalize","Cascade","Collection","Entity","ManyToOne","OneToMany","OneToOne","Property","types","Unique","TenantBaseEntity","getEntityManager","FileSystemError","FileSystemErrorCode","FileKind","createDatabaseFileSystem","options","DBFS","fork","smallFileThreshold","em","ensureRootNode","rootQb","createQueryBuilder","FileNodeMetaEntity","where","parent","rootNode","getSingleResult","now","Date","rootDir","create","filename","kind","directory","size","atime","btime","ctime","mtime","persistAndFlush","error","message","includes","existingRootQb","existingRoot","stat","path","EINVAL","node","_getNodeByPath","ENOENT","_toFileStat","exists","readdir","dir","parentNode","ENOTDIR","qb","children","getResult","map","child","mkdir","_mkdirInTransaction","normalized","parentPath","newDirName","recursive","existingQb","existing","EEXIST","newDir","tid","readFile","file","EISDIR","buffer","content","fileContentQb","FileNodeContentEntity","fileContent","encoding","toString","writeFile","data","undefined","transactional","overwrite","bufferData","Buffer","isBuffer","from","length","nodeQb","existingContentQb","existingContent","removeAndFlush","md5","createHash","update","digest","sha256","rm","force","childrenQb","select","ENOTEMPTY","rename","oldPath","newPath","newParentPath","newFilename","newParentNode","existingDestQb","existingDest","id","flush","copy","srcPath","destPath","srcNode","destParentPath","destFilename","destParentNode","_copyNode","createReadStream","Error","createWriteStream","createReadableStream","createWritableStream","pathStr","parts","split","filter","p","currentNode","part","childQb","getTime","Number","name","meta","metadata","destParent","newName","newNode","srcFileContentQb","srcFileContent","newContent","mimeType","parentId","type","string","nullable","comment","bigint","default","datetime","defaultRaw","json","cascade","entity","mappedBy","orphanRemoval","ALL","blob","tableName","properties","text","width","height","owner","joinColumn","integer","lazy"],"mappings":"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"}
|
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
import { createReadStream as nodeCreateReadStream, createWriteStream as nodeCreateWriteStream } from "node:fs";
|
|
2
|
+
import fsp from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { Readable } from "node:stream";
|
|
5
|
+
import { pathToFileURL } from "node:url";
|
|
6
|
+
/**
|
|
7
|
+
* Creates a Node.js filesystem adapter that implements the IFileSystem interface
|
|
8
|
+
* @param options Configuration options for the filesystem
|
|
9
|
+
* @param options.root Optional root directory to restrict all operations within
|
|
10
|
+
*/ export function createNodeFileSystem(options = {}) {
|
|
11
|
+
return new NodeFs(options);
|
|
12
|
+
}
|
|
13
|
+
let NodeFs = class NodeFs {
|
|
14
|
+
root;
|
|
15
|
+
fs;
|
|
16
|
+
constructor({ root, fs = fsp } = {}){
|
|
17
|
+
// Normalize the root path if provided
|
|
18
|
+
if (root) {
|
|
19
|
+
this.root = path.resolve(root);
|
|
20
|
+
} else {
|
|
21
|
+
this.root = '';
|
|
22
|
+
}
|
|
23
|
+
this.fs = fs;
|
|
24
|
+
}
|
|
25
|
+
// Helper method to handle aborted signals consistently
|
|
26
|
+
checkAborted(signal) {
|
|
27
|
+
if (signal?.aborted) {
|
|
28
|
+
throw new Error('The operation was aborted');
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Resolves and secures a path by:
|
|
33
|
+
* 1. Normalizing the path
|
|
34
|
+
* 2. Prepending the root directory if one is set
|
|
35
|
+
* 3. Verifying the resulting path is within the root directory
|
|
36
|
+
*
|
|
37
|
+
* This prevents path traversal attacks.
|
|
38
|
+
*/ resolvePath(filePath) {
|
|
39
|
+
// Handle empty paths
|
|
40
|
+
if (!filePath) {
|
|
41
|
+
return this.root || '';
|
|
42
|
+
}
|
|
43
|
+
// Normalize to remove any '..' segments and handle slashes
|
|
44
|
+
const normalizedPath = path.normalize(filePath);
|
|
45
|
+
// If no root is set, just return the normalized path
|
|
46
|
+
if (!this.root) {
|
|
47
|
+
return normalizedPath;
|
|
48
|
+
}
|
|
49
|
+
// Join with root and resolve to absolute path
|
|
50
|
+
const resolvedPath = path.resolve(path.join(this.root, normalizedPath));
|
|
51
|
+
// Security check: ensure the path is within the root directory
|
|
52
|
+
if (!resolvedPath.startsWith(this.root)) {
|
|
53
|
+
throw new Error(`Security violation: Path ${filePath} attempts to access outside of the root directory`);
|
|
54
|
+
}
|
|
55
|
+
return resolvedPath;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Removes the root prefix from a path for external representation
|
|
59
|
+
*/ stripRoot(fullPath) {
|
|
60
|
+
if (!this.root || !fullPath.startsWith(this.root)) {
|
|
61
|
+
return fullPath;
|
|
62
|
+
}
|
|
63
|
+
// Remove the root prefix and ensure there's a leading slash
|
|
64
|
+
let relativePath = fullPath.substring(this.root.length);
|
|
65
|
+
if (!relativePath) {
|
|
66
|
+
return '/';
|
|
67
|
+
}
|
|
68
|
+
if (!relativePath.startsWith('/') && !relativePath.startsWith('\\')) {
|
|
69
|
+
relativePath = '/' + relativePath;
|
|
70
|
+
}
|
|
71
|
+
// Normalize to ensure consistent path separators
|
|
72
|
+
return path.normalize(relativePath).replace(/\\/g, '/');
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Converts a system file stat to our IFileStat interface,
|
|
76
|
+
* stripping the root prefix from paths
|
|
77
|
+
*/ toFileStat(fullPath, fsStats) {
|
|
78
|
+
const normalizedPath = this.stripRoot(fullPath);
|
|
79
|
+
const directoryPath = path.dirname(normalizedPath);
|
|
80
|
+
const directory = directoryPath === '.' ? '/' : directoryPath.replace(/\\/g, '/');
|
|
81
|
+
return {
|
|
82
|
+
directory,
|
|
83
|
+
path: normalizedPath,
|
|
84
|
+
name: path.basename(fullPath),
|
|
85
|
+
kind: fsStats.isDirectory() ? 'directory' : 'file',
|
|
86
|
+
mtime: fsStats.mtimeMs,
|
|
87
|
+
size: fsStats.size,
|
|
88
|
+
meta: {}
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
async readdir(dir, options = {}) {
|
|
92
|
+
const { fs } = this;
|
|
93
|
+
const { glob, recursive, depth = 1, kind, hidden = true, signal } = options;
|
|
94
|
+
this.checkAborted(signal);
|
|
95
|
+
// Resolve the directory path with security checks
|
|
96
|
+
const resolvedDir = this.resolvePath(dir);
|
|
97
|
+
// Basic file listing
|
|
98
|
+
const entries = await fs.readdir(resolvedDir, {
|
|
99
|
+
withFileTypes: true
|
|
100
|
+
});
|
|
101
|
+
let results = await Promise.all(entries.filter((entry)=>hidden || !entry.name.startsWith('.')).filter((entry)=>!kind || (kind === 'directory' ? entry.isDirectory() : entry.isFile())).map(async (entry)=>{
|
|
102
|
+
this.checkAborted(signal);
|
|
103
|
+
const entryFullPath = path.join(resolvedDir, entry.name);
|
|
104
|
+
const stat = await fs.stat(entryFullPath);
|
|
105
|
+
// Convert to external representation
|
|
106
|
+
return {
|
|
107
|
+
directory: this.stripRoot(resolvedDir),
|
|
108
|
+
path: this.stripRoot(entryFullPath),
|
|
109
|
+
name: entry.name,
|
|
110
|
+
kind: entry.isDirectory() ? 'directory' : 'file',
|
|
111
|
+
mtime: stat.mtimeMs,
|
|
112
|
+
size: stat.size,
|
|
113
|
+
meta: {}
|
|
114
|
+
};
|
|
115
|
+
}));
|
|
116
|
+
// Handle recursive option
|
|
117
|
+
if (recursive || depth > 1) {
|
|
118
|
+
const subdirs = results.filter((entry)=>entry.kind === 'directory');
|
|
119
|
+
for (const subdir of subdirs){
|
|
120
|
+
this.checkAborted(signal);
|
|
121
|
+
const maxDepth = recursive ? Infinity : depth - 1;
|
|
122
|
+
if (maxDepth > 0) {
|
|
123
|
+
// Need to convert the path back to a full path for the recursive call
|
|
124
|
+
const subdirFullPath = this.resolvePath(subdir.path);
|
|
125
|
+
const subEntries = await this.readdir(subdir.path, {
|
|
126
|
+
...options,
|
|
127
|
+
depth: maxDepth
|
|
128
|
+
});
|
|
129
|
+
results = [
|
|
130
|
+
...results,
|
|
131
|
+
...subEntries
|
|
132
|
+
];
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
// Handle glob filtering
|
|
137
|
+
if (glob) {
|
|
138
|
+
const { matcher } = await import("micromatch");
|
|
139
|
+
const match = matcher(glob);
|
|
140
|
+
results = results.filter((entry)=>match(entry.path));
|
|
141
|
+
}
|
|
142
|
+
return results;
|
|
143
|
+
}
|
|
144
|
+
async stat(filePath, options = {}) {
|
|
145
|
+
const { fs } = this;
|
|
146
|
+
const { signal } = options;
|
|
147
|
+
this.checkAborted(signal);
|
|
148
|
+
const resolvedPath = this.resolvePath(filePath);
|
|
149
|
+
try {
|
|
150
|
+
const stat = await fs.stat(resolvedPath);
|
|
151
|
+
return this.toFileStat(resolvedPath, stat);
|
|
152
|
+
} catch (err) {
|
|
153
|
+
if (err.code === 'ENOENT') {
|
|
154
|
+
throw new Error(`File not found: ${filePath}`);
|
|
155
|
+
}
|
|
156
|
+
throw err;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
async mkdir(dirPath, options = {}) {
|
|
160
|
+
const { fs } = this;
|
|
161
|
+
const { recursive = false, signal } = options;
|
|
162
|
+
this.checkAborted(signal);
|
|
163
|
+
const resolvedPath = this.resolvePath(dirPath);
|
|
164
|
+
try {
|
|
165
|
+
await fs.mkdir(resolvedPath, {
|
|
166
|
+
recursive
|
|
167
|
+
});
|
|
168
|
+
} catch (err) {
|
|
169
|
+
if (err.code === 'EEXIST' && recursive) {
|
|
170
|
+
// Ignore if directory exists and recursive is true
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
throw err;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
async readFile(path, options = {}) {
|
|
177
|
+
const { fs } = this;
|
|
178
|
+
const { encoding = 'binary', signal, onDownloadProgress } = options;
|
|
179
|
+
this.checkAborted(signal);
|
|
180
|
+
const resolvedPath = this.resolvePath(path);
|
|
181
|
+
try {
|
|
182
|
+
// Handle progress reporting if needed
|
|
183
|
+
if (onDownloadProgress) {
|
|
184
|
+
const stat = await fs.stat(resolvedPath);
|
|
185
|
+
const stream = this.createReadStream(path, {
|
|
186
|
+
signal
|
|
187
|
+
});
|
|
188
|
+
return new Promise((resolve, reject)=>{
|
|
189
|
+
const chunks = [];
|
|
190
|
+
let loaded = 0;
|
|
191
|
+
stream.on('data', (chunk)=>{
|
|
192
|
+
chunks.push(Buffer.from(chunk));
|
|
193
|
+
loaded += chunk.length;
|
|
194
|
+
onDownloadProgress({
|
|
195
|
+
loaded,
|
|
196
|
+
total: stat.size
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
stream.on('end', ()=>{
|
|
200
|
+
const buffer = Buffer.concat(chunks);
|
|
201
|
+
if (encoding === 'text') {
|
|
202
|
+
resolve(buffer.toString('utf-8'));
|
|
203
|
+
} else {
|
|
204
|
+
resolve(buffer);
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
stream.on('error', reject);
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
// Standard file reading
|
|
211
|
+
if (encoding === 'text') {
|
|
212
|
+
return await fs.readFile(resolvedPath, {
|
|
213
|
+
encoding: 'utf-8'
|
|
214
|
+
});
|
|
215
|
+
} else {
|
|
216
|
+
return await fs.readFile(resolvedPath);
|
|
217
|
+
}
|
|
218
|
+
} catch (err) {
|
|
219
|
+
if (err.code === 'ENOENT') {
|
|
220
|
+
throw new Error(`File not found: ${path}`);
|
|
221
|
+
}
|
|
222
|
+
throw err;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
async writeFile(path, data, options = {}) {
|
|
226
|
+
const { fs } = this;
|
|
227
|
+
const { signal, overwrite = true, onUploadProgress } = options;
|
|
228
|
+
this.checkAborted(signal);
|
|
229
|
+
const resolvedPath = this.resolvePath(path);
|
|
230
|
+
// Check if file exists and overwrite is false
|
|
231
|
+
if (!overwrite) {
|
|
232
|
+
const exists = await this.exists(path);
|
|
233
|
+
if (exists) {
|
|
234
|
+
throw new Error(`File already exists: ${path}`);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
// Create parent directories if they don't exist
|
|
238
|
+
const directory = this.getDirectoryName(resolvedPath);
|
|
239
|
+
if (directory !== resolvedPath) {
|
|
240
|
+
try {
|
|
241
|
+
await this.mkdir(this.stripRoot(directory), {
|
|
242
|
+
recursive: true
|
|
243
|
+
});
|
|
244
|
+
} catch (err) {
|
|
245
|
+
// Ignore directory exists error
|
|
246
|
+
if (err?.code !== 'EEXIST') {
|
|
247
|
+
throw err;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
if (data instanceof Readable) {
|
|
252
|
+
let _data = data;
|
|
253
|
+
return new Promise((resolve, reject)=>{
|
|
254
|
+
const writeStream = this.createWriteStream(path, options);
|
|
255
|
+
let totalBytes = 0;
|
|
256
|
+
if (onUploadProgress) {
|
|
257
|
+
_data.on('data', (chunk)=>{
|
|
258
|
+
totalBytes += chunk.length;
|
|
259
|
+
onUploadProgress({
|
|
260
|
+
loaded: totalBytes,
|
|
261
|
+
total: -1
|
|
262
|
+
});
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
_data.pipe(writeStream);
|
|
266
|
+
writeStream.on('finish', ()=>resolve());
|
|
267
|
+
writeStream.on('error', reject);
|
|
268
|
+
if (signal) {
|
|
269
|
+
signal.addEventListener('abort', ()=>{
|
|
270
|
+
_data.destroy();
|
|
271
|
+
writeStream.destroy();
|
|
272
|
+
reject(new Error('The operation was aborted'));
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
} else {
|
|
277
|
+
// Convert ArrayBuffer to Buffer if necessary
|
|
278
|
+
if (data instanceof ArrayBuffer) {
|
|
279
|
+
data = Buffer.from(data);
|
|
280
|
+
}
|
|
281
|
+
await fs.writeFile(resolvedPath, data);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
async rm(path, options = {}) {
|
|
285
|
+
const { fs } = this;
|
|
286
|
+
const { recursive = false, force = false, signal } = options;
|
|
287
|
+
this.checkAborted(signal);
|
|
288
|
+
const resolvedPath = this.resolvePath(path);
|
|
289
|
+
try {
|
|
290
|
+
await fs.rm(resolvedPath, {
|
|
291
|
+
recursive,
|
|
292
|
+
force
|
|
293
|
+
});
|
|
294
|
+
} catch (err) {
|
|
295
|
+
if (force && err.code === 'ENOENT') {
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
throw err;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
async rename(oldPath, newPath, options = {}) {
|
|
302
|
+
const { fs } = this;
|
|
303
|
+
const { signal, overwrite = false } = options;
|
|
304
|
+
this.checkAborted(signal);
|
|
305
|
+
const resolvedOldPath = this.resolvePath(oldPath);
|
|
306
|
+
const resolvedNewPath = this.resolvePath(newPath);
|
|
307
|
+
// Check if target exists and overwrite is false
|
|
308
|
+
if (!overwrite) {
|
|
309
|
+
const exists = await this.exists(newPath);
|
|
310
|
+
if (exists) {
|
|
311
|
+
throw new Error(`Destination already exists: ${newPath}`);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
try {
|
|
315
|
+
await fs.rename(resolvedOldPath, resolvedNewPath);
|
|
316
|
+
} catch (err) {
|
|
317
|
+
if (err.code === 'ENOENT') {
|
|
318
|
+
throw new Error(`Source file not found: ${oldPath}`);
|
|
319
|
+
}
|
|
320
|
+
throw err;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
async exists(path) {
|
|
324
|
+
const { fs } = this;
|
|
325
|
+
try {
|
|
326
|
+
await fs.access(this.resolvePath(path));
|
|
327
|
+
return true;
|
|
328
|
+
} catch {
|
|
329
|
+
return false;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
async copy(src, dest, options = {}) {
|
|
333
|
+
const { fs } = this;
|
|
334
|
+
const { signal, overwrite = true, shallow = false } = options;
|
|
335
|
+
this.checkAborted(signal);
|
|
336
|
+
const resolvedSrc = this.resolvePath(src);
|
|
337
|
+
const resolvedDest = this.resolvePath(dest);
|
|
338
|
+
// Check if source exists
|
|
339
|
+
try {
|
|
340
|
+
const stat = await fs.stat(resolvedSrc);
|
|
341
|
+
// Check if destination exists and overwrite is false
|
|
342
|
+
if (!overwrite) {
|
|
343
|
+
const exists = await this.exists(dest);
|
|
344
|
+
if (exists) {
|
|
345
|
+
throw new Error(`Destination already exists: ${dest}`);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
// Create parent directory if it doesn't exist
|
|
349
|
+
const parentDir = this.getDirectoryName(resolvedDest);
|
|
350
|
+
await this.mkdir(this.stripRoot(parentDir), {
|
|
351
|
+
recursive: true
|
|
352
|
+
});
|
|
353
|
+
// Copy recursively or not based on shallow and if it's a directory
|
|
354
|
+
await fs.cp(resolvedSrc, resolvedDest, {
|
|
355
|
+
recursive: !shallow && stat.isDirectory(),
|
|
356
|
+
force: overwrite
|
|
357
|
+
});
|
|
358
|
+
} catch (err) {
|
|
359
|
+
if (err.code === 'ENOENT') {
|
|
360
|
+
throw new Error(`Source file not found: ${src}`);
|
|
361
|
+
}
|
|
362
|
+
throw err;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
createReadStream(filePath, options = {}) {
|
|
366
|
+
const resolvedPath = this.resolvePath(filePath);
|
|
367
|
+
const { signal, range } = options;
|
|
368
|
+
const stream = nodeCreateReadStream(resolvedPath, {
|
|
369
|
+
start: range?.start,
|
|
370
|
+
end: range?.end
|
|
371
|
+
});
|
|
372
|
+
signal?.addEventListener('abort', ()=>stream.destroy(new Error('The operation was aborted')));
|
|
373
|
+
return stream;
|
|
374
|
+
}
|
|
375
|
+
createWriteStream(filePath, options = {}) {
|
|
376
|
+
const resolvedPath = this.resolvePath(filePath);
|
|
377
|
+
const { signal } = options;
|
|
378
|
+
const stream = nodeCreateWriteStream(resolvedPath, {
|
|
379
|
+
flags: options.overwrite === false ? 'wx' : 'w'
|
|
380
|
+
});
|
|
381
|
+
signal?.addEventListener('abort', ()=>stream.destroy(new Error('The operation was aborted')));
|
|
382
|
+
return stream;
|
|
383
|
+
}
|
|
384
|
+
// Helper to extract directory name correctly
|
|
385
|
+
getDirectoryName(filePath) {
|
|
386
|
+
return path.dirname(filePath);
|
|
387
|
+
}
|
|
388
|
+
getUrl(needle) {
|
|
389
|
+
if (typeof needle === 'object' && needle?.kind !== 'file') {
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
let path = typeof needle === 'string' ? needle : needle.path;
|
|
393
|
+
if (!path) {
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
// file://
|
|
397
|
+
return pathToFileURL(this.resolvePath(path)).toString();
|
|
398
|
+
}
|
|
399
|
+
};
|
|
400
|
+
|
|
401
|
+
//# sourceMappingURL=createNodeFileSystem.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/fs/server/createNodeFileSystem.ts"],"sourcesContent":["import {\n\tcreateReadStream as nodeCreateReadStream,\n\tcreateWriteStream as nodeCreateWriteStream,\n\ttype Stats,\n} from 'node:fs';\nimport fsp from 'node:fs/promises';\nimport path from 'node:path';\nimport { Readable, type Writable } from 'node:stream';\nimport { pathToFileURL } from 'node:url';\nimport type {\n\tCopyOptions,\n\tCreateReadStreamOptions,\n\tCreateWriteStreamOptions,\n\tIFileStat,\n\tIFileSystem,\n\tMkdirOptions,\n\tReaddirOptions,\n\tReadFileOptions,\n\tRenameOptions,\n\tRmOptions,\n\tStatOptions,\n\tWriteFileOptions,\n} from '../IFileSystem';\n\nexport type INodeFileSystem = IFileSystem & {\n\treadonly root: string;\n\tresolvePath(filePath: string): string;\n};\n\n/**\n * Creates a Node.js filesystem adapter that implements the IFileSystem interface\n * @param options Configuration options for the filesystem\n * @param options.root Optional root directory to restrict all operations within\n */\nexport function createNodeFileSystem(options: { root?: string } = {}): INodeFileSystem {\n\treturn new NodeFs(options);\n}\n\ntype IFS = typeof import('fs/promises');\n\nclass NodeFs implements IFileSystem, INodeFileSystem {\n\treadonly root: string;\n\tprivate readonly fs: IFS;\n\n\tconstructor({\n\t\troot,\n\t\tfs = fsp,\n\t}: {\n\t\troot?: string;\n\t\tfs?: IFS;\n\t} = {}) {\n\t\t// Normalize the root path if provided\n\t\tif (root) {\n\t\t\tthis.root = path.resolve(root);\n\t\t} else {\n\t\t\tthis.root = '';\n\t\t}\n\t\tthis.fs = fs;\n\t}\n\n\t// Helper method to handle aborted signals consistently\n\tprivate checkAborted(signal?: AbortSignal): void {\n\t\tif (signal?.aborted) {\n\t\t\tthrow new Error('The operation was aborted');\n\t\t}\n\t}\n\n\t/**\n\t * Resolves and secures a path by:\n\t * 1. Normalizing the path\n\t * 2. Prepending the root directory if one is set\n\t * 3. Verifying the resulting path is within the root directory\n\t *\n\t * This prevents path traversal attacks.\n\t */\n\tresolvePath(filePath: string): string {\n\t\t// Handle empty paths\n\t\tif (!filePath) {\n\t\t\treturn this.root || '';\n\t\t}\n\n\t\t// Normalize to remove any '..' segments and handle slashes\n\t\tconst normalizedPath = path.normalize(filePath);\n\n\t\t// If no root is set, just return the normalized path\n\t\tif (!this.root) {\n\t\t\treturn normalizedPath;\n\t\t}\n\n\t\t// Join with root and resolve to absolute path\n\t\tconst resolvedPath = path.resolve(path.join(this.root, normalizedPath));\n\n\t\t// Security check: ensure the path is within the root directory\n\t\tif (!resolvedPath.startsWith(this.root)) {\n\t\t\tthrow new Error(`Security violation: Path ${filePath} attempts to access outside of the root directory`);\n\t\t}\n\n\t\treturn resolvedPath;\n\t}\n\n\t/**\n\t * Removes the root prefix from a path for external representation\n\t */\n\tprivate stripRoot(fullPath: string): string {\n\t\tif (!this.root || !fullPath.startsWith(this.root)) {\n\t\t\treturn fullPath;\n\t\t}\n\n\t\t// Remove the root prefix and ensure there's a leading slash\n\t\tlet relativePath = fullPath.substring(this.root.length);\n\t\tif (!relativePath) {\n\t\t\treturn '/';\n\t\t}\n\n\t\tif (!relativePath.startsWith('/') && !relativePath.startsWith('\\\\')) {\n\t\t\trelativePath = '/' + relativePath;\n\t\t}\n\n\t\t// Normalize to ensure consistent path separators\n\t\treturn path.normalize(relativePath).replace(/\\\\/g, '/');\n\t}\n\n\t/**\n\t * Converts a system file stat to our IFileStat interface,\n\t * stripping the root prefix from paths\n\t */\n\tprivate toFileStat(fullPath: string, fsStats: Stats): IFileStat {\n\t\tconst normalizedPath = this.stripRoot(fullPath);\n\t\tconst directoryPath = path.dirname(normalizedPath);\n\t\tconst directory = directoryPath === '.' ? '/' : directoryPath.replace(/\\\\/g, '/');\n\n\t\treturn {\n\t\t\tdirectory,\n\t\t\tpath: normalizedPath,\n\t\t\tname: path.basename(fullPath),\n\t\t\tkind: fsStats.isDirectory() ? 'directory' : 'file',\n\t\t\tmtime: fsStats.mtimeMs,\n\t\t\tsize: fsStats.size,\n\t\t\tmeta: {},\n\t\t};\n\t}\n\n\tasync readdir(dir: string, options: ReaddirOptions = {}): Promise<IFileStat[]> {\n\t\tconst { fs } = this;\n\t\tconst { glob, recursive, depth = 1, kind, hidden = true, signal } = options;\n\t\tthis.checkAborted(signal);\n\n\t\t// Resolve the directory path with security checks\n\t\tconst resolvedDir = this.resolvePath(dir);\n\n\t\t// Basic file listing\n\t\tconst entries = await fs.readdir(resolvedDir, { withFileTypes: true });\n\t\tlet results = await Promise.all(\n\t\t\tentries\n\t\t\t\t.filter((entry) => hidden || !entry.name.startsWith('.'))\n\t\t\t\t.filter((entry) => !kind || (kind === 'directory' ? entry.isDirectory() : entry.isFile()))\n\t\t\t\t.map(async (entry) => {\n\t\t\t\t\tthis.checkAborted(signal);\n\n\t\t\t\t\tconst entryFullPath = path.join(resolvedDir, entry.name);\n\t\t\t\t\tconst stat = await fs.stat(entryFullPath);\n\n\t\t\t\t\t// Convert to external representation\n\t\t\t\t\treturn {\n\t\t\t\t\t\tdirectory: this.stripRoot(resolvedDir),\n\t\t\t\t\t\tpath: this.stripRoot(entryFullPath),\n\t\t\t\t\t\tname: entry.name,\n\t\t\t\t\t\tkind: entry.isDirectory() ? 'directory' : 'file',\n\t\t\t\t\t\tmtime: stat.mtimeMs,\n\t\t\t\t\t\tsize: stat.size,\n\t\t\t\t\t\tmeta: {},\n\t\t\t\t\t} as IFileStat;\n\t\t\t\t}),\n\t\t);\n\n\t\t// Handle recursive option\n\t\tif (recursive || depth > 1) {\n\t\t\tconst subdirs = results.filter((entry) => entry.kind === 'directory');\n\n\t\t\tfor (const subdir of subdirs) {\n\t\t\t\tthis.checkAborted(signal);\n\n\t\t\t\tconst maxDepth = recursive ? Infinity : depth - 1;\n\t\t\t\tif (maxDepth > 0) {\n\t\t\t\t\t// Need to convert the path back to a full path for the recursive call\n\t\t\t\t\tconst subdirFullPath = this.resolvePath(subdir.path);\n\n\t\t\t\t\tconst subEntries = await this.readdir(subdir.path, {\n\t\t\t\t\t\t...options,\n\t\t\t\t\t\tdepth: maxDepth,\n\t\t\t\t\t});\n\t\t\t\t\tresults = [...results, ...subEntries];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Handle glob filtering\n\t\tif (glob) {\n\t\t\tconst { matcher } = await import('micromatch');\n\t\t\tconst match = matcher(glob);\n\t\t\tresults = results.filter((entry) => match(entry.path));\n\t\t}\n\n\t\treturn results;\n\t}\n\n\tasync stat(filePath: string, options: StatOptions = {}): Promise<IFileStat> {\n\t\tconst { fs } = this;\n\n\t\tconst { signal } = options;\n\t\tthis.checkAborted(signal);\n\n\t\tconst resolvedPath = this.resolvePath(filePath);\n\n\t\ttry {\n\t\t\tconst stat = await fs.stat(resolvedPath);\n\t\t\treturn this.toFileStat(resolvedPath, stat);\n\t\t} catch (err: any) {\n\t\t\tif (err.code === 'ENOENT') {\n\t\t\t\tthrow new Error(`File not found: ${filePath}`);\n\t\t\t}\n\t\t\tthrow err;\n\t\t}\n\t}\n\n\tasync mkdir(dirPath: string, options: MkdirOptions = {}): Promise<void> {\n\t\tconst { fs } = this;\n\n\t\tconst { recursive = false, signal } = options;\n\t\tthis.checkAborted(signal);\n\n\t\tconst resolvedPath = this.resolvePath(dirPath);\n\n\t\ttry {\n\t\t\tawait fs.mkdir(resolvedPath, { recursive });\n\t\t} catch (err: any) {\n\t\t\tif (err.code === 'EEXIST' && recursive) {\n\t\t\t\t// Ignore if directory exists and recursive is true\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthrow err;\n\t\t}\n\t}\n\n\tasync readFile(path: string, options?: ReadFileOptions & { encoding: 'text' }): Promise<string>;\n\tasync readFile(path: string, options?: ReadFileOptions): Promise<Uint8Array>;\n\tasync readFile(path: string, options: ReadFileOptions = {}): Promise<string | Uint8Array> {\n\t\tconst { fs } = this;\n\n\t\tconst { encoding = 'binary', signal, onDownloadProgress } = options;\n\t\tthis.checkAborted(signal);\n\n\t\tconst resolvedPath = this.resolvePath(path);\n\n\t\ttry {\n\t\t\t// Handle progress reporting if needed\n\t\t\tif (onDownloadProgress) {\n\t\t\t\tconst stat = await fs.stat(resolvedPath);\n\t\t\t\tconst stream = this.createReadStream(path, { signal });\n\n\t\t\t\treturn new Promise((resolve, reject) => {\n\t\t\t\t\tconst chunks: Buffer[] = [];\n\t\t\t\t\tlet loaded = 0;\n\n\t\t\t\t\tstream.on('data', (chunk) => {\n\t\t\t\t\t\tchunks.push(Buffer.from(chunk));\n\t\t\t\t\t\tloaded += chunk.length;\n\t\t\t\t\t\tonDownloadProgress({ loaded, total: stat.size });\n\t\t\t\t\t});\n\n\t\t\t\t\tstream.on('end', () => {\n\t\t\t\t\t\tconst buffer = Buffer.concat(chunks);\n\t\t\t\t\t\tif (encoding === 'text') {\n\t\t\t\t\t\t\tresolve(buffer.toString('utf-8'));\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tresolve(buffer);\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\n\t\t\t\t\tstream.on('error', reject);\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Standard file reading\n\t\t\tif (encoding === 'text') {\n\t\t\t\treturn await fs.readFile(resolvedPath, { encoding: 'utf-8' });\n\t\t\t} else {\n\t\t\t\treturn await fs.readFile(resolvedPath);\n\t\t\t}\n\t\t} catch (err: any) {\n\t\t\tif (err.code === 'ENOENT') {\n\t\t\t\tthrow new Error(`File not found: ${path}`);\n\t\t\t}\n\t\t\tthrow err;\n\t\t}\n\t}\n\n\tasync writeFile(\n\t\tpath: string,\n\t\tdata: string | Buffer | ArrayBuffer | Readable,\n\t\toptions: WriteFileOptions = {},\n\t): Promise<void> {\n\t\tconst { fs } = this;\n\n\t\tconst { signal, overwrite = true, onUploadProgress } = options;\n\t\tthis.checkAborted(signal);\n\n\t\tconst resolvedPath = this.resolvePath(path);\n\n\t\t// Check if file exists and overwrite is false\n\t\tif (!overwrite) {\n\t\t\tconst exists = await this.exists(path);\n\t\t\tif (exists) {\n\t\t\t\tthrow new Error(`File already exists: ${path}`);\n\t\t\t}\n\t\t}\n\n\t\t// Create parent directories if they don't exist\n\t\tconst directory = this.getDirectoryName(resolvedPath);\n\t\tif (directory !== resolvedPath) {\n\t\t\ttry {\n\t\t\t\tawait this.mkdir(this.stripRoot(directory), { recursive: true });\n\t\t\t} catch (err: any) {\n\t\t\t\t// Ignore directory exists error\n\t\t\t\tif (err?.code !== 'EEXIST') {\n\t\t\t\t\tthrow err;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (data instanceof Readable) {\n\t\t\tlet _data = data;\n\t\t\treturn new Promise((resolve, reject) => {\n\t\t\t\tconst writeStream = this.createWriteStream(path, options);\n\t\t\t\tlet totalBytes = 0;\n\n\t\t\t\tif (onUploadProgress) {\n\t\t\t\t\t_data.on('data', (chunk) => {\n\t\t\t\t\t\ttotalBytes += chunk.length;\n\t\t\t\t\t\tonUploadProgress({ loaded: totalBytes, total: -1 });\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\t_data.pipe(writeStream);\n\n\t\t\t\twriteStream.on('finish', () => resolve());\n\t\t\t\twriteStream.on('error', reject);\n\n\t\t\t\tif (signal) {\n\t\t\t\t\tsignal.addEventListener('abort', () => {\n\t\t\t\t\t\t_data.destroy();\n\t\t\t\t\t\twriteStream.destroy();\n\t\t\t\t\t\treject(new Error('The operation was aborted'));\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t});\n\t\t} else {\n\t\t\t// Convert ArrayBuffer to Buffer if necessary\n\t\t\tif (data instanceof ArrayBuffer) {\n\t\t\t\tdata = Buffer.from(data);\n\t\t\t}\n\t\t\tawait fs.writeFile(resolvedPath, data);\n\t\t}\n\t}\n\n\tasync rm(path: string, options: RmOptions = {}): Promise<void> {\n\t\tconst { fs } = this;\n\n\t\tconst { recursive = false, force = false, signal } = options;\n\t\tthis.checkAborted(signal);\n\n\t\tconst resolvedPath = this.resolvePath(path);\n\n\t\ttry {\n\t\t\tawait fs.rm(resolvedPath, { recursive, force });\n\t\t} catch (err: any) {\n\t\t\tif (force && err.code === 'ENOENT') {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthrow err;\n\t\t}\n\t}\n\n\tasync rename(oldPath: string, newPath: string, options: RenameOptions = {}): Promise<void> {\n\t\tconst { fs } = this;\n\n\t\tconst { signal, overwrite = false } = options;\n\t\tthis.checkAborted(signal);\n\n\t\tconst resolvedOldPath = this.resolvePath(oldPath);\n\t\tconst resolvedNewPath = this.resolvePath(newPath);\n\n\t\t// Check if target exists and overwrite is false\n\t\tif (!overwrite) {\n\t\t\tconst exists = await this.exists(newPath);\n\t\t\tif (exists) {\n\t\t\t\tthrow new Error(`Destination already exists: ${newPath}`);\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\tawait fs.rename(resolvedOldPath, resolvedNewPath);\n\t\t} catch (err: any) {\n\t\t\tif (err.code === 'ENOENT') {\n\t\t\t\tthrow new Error(`Source file not found: ${oldPath}`);\n\t\t\t}\n\t\t\tthrow err;\n\t\t}\n\t}\n\n\tasync exists(path: string): Promise<boolean> {\n\t\tconst { fs } = this;\n\n\t\ttry {\n\t\t\tawait fs.access(this.resolvePath(path));\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tasync copy(src: string, dest: string, options: CopyOptions = {}): Promise<void> {\n\t\tconst { fs } = this;\n\n\t\tconst { signal, overwrite = true, shallow = false } = options;\n\t\tthis.checkAborted(signal);\n\n\t\tconst resolvedSrc = this.resolvePath(src);\n\t\tconst resolvedDest = this.resolvePath(dest);\n\n\t\t// Check if source exists\n\t\ttry {\n\t\t\tconst stat = await fs.stat(resolvedSrc);\n\n\t\t\t// Check if destination exists and overwrite is false\n\t\t\tif (!overwrite) {\n\t\t\t\tconst exists = await this.exists(dest);\n\t\t\t\tif (exists) {\n\t\t\t\t\tthrow new Error(`Destination already exists: ${dest}`);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Create parent directory if it doesn't exist\n\t\t\tconst parentDir = this.getDirectoryName(resolvedDest);\n\t\t\tawait this.mkdir(this.stripRoot(parentDir), { recursive: true });\n\n\t\t\t// Copy recursively or not based on shallow and if it's a directory\n\t\t\tawait fs.cp(resolvedSrc, resolvedDest, {\n\t\t\t\trecursive: !shallow && stat.isDirectory(),\n\t\t\t\tforce: overwrite,\n\t\t\t});\n\t\t} catch (err: any) {\n\t\t\tif (err.code === 'ENOENT') {\n\t\t\t\tthrow new Error(`Source file not found: ${src}`);\n\t\t\t}\n\t\t\tthrow err;\n\t\t}\n\t}\n\n\tcreateReadStream(filePath: string, options: CreateReadStreamOptions = {}): Readable {\n\t\tconst resolvedPath = this.resolvePath(filePath);\n\t\tconst { signal, range } = options;\n\t\tconst stream = nodeCreateReadStream(resolvedPath, {\n\t\t\tstart: range?.start,\n\t\t\tend: range?.end,\n\t\t});\n\n\t\tsignal?.addEventListener('abort', () => stream.destroy(new Error('The operation was aborted')));\n\n\t\treturn stream;\n\t}\n\n\tcreateWriteStream(filePath: string, options: CreateWriteStreamOptions = {}): Writable {\n\t\tconst resolvedPath = this.resolvePath(filePath);\n\t\tconst { signal } = options;\n\t\tconst stream = nodeCreateWriteStream(resolvedPath, { flags: options.overwrite === false ? 'wx' : 'w' });\n\n\t\tsignal?.addEventListener('abort', () => stream.destroy(new Error('The operation was aborted')));\n\n\t\treturn stream;\n\t}\n\n\t// Helper to extract directory name correctly\n\tprivate getDirectoryName(filePath: string): string {\n\t\treturn path.dirname(filePath);\n\t}\n\n\tgetUrl(needle: IFileStat | string) {\n\t\tif (typeof needle === 'object' && needle?.kind !== 'file') {\n\t\t\treturn;\n\t\t}\n\t\tlet path = typeof needle === 'string' ? needle : needle.path;\n\t\tif (!path) {\n\t\t\treturn;\n\t\t}\n\t\t// file://\n\t\treturn pathToFileURL(this.resolvePath(path)).toString();\n\t}\n}\n"],"names":["createReadStream","nodeCreateReadStream","createWriteStream","nodeCreateWriteStream","fsp","path","Readable","pathToFileURL","createNodeFileSystem","options","NodeFs","root","fs","resolve","checkAborted","signal","aborted","Error","resolvePath","filePath","normalizedPath","normalize","resolvedPath","join","startsWith","stripRoot","fullPath","relativePath","substring","length","replace","toFileStat","fsStats","directoryPath","dirname","directory","name","basename","kind","isDirectory","mtime","mtimeMs","size","meta","readdir","dir","glob","recursive","depth","hidden","resolvedDir","entries","withFileTypes","results","Promise","all","filter","entry","isFile","map","entryFullPath","stat","subdirs","subdir","maxDepth","Infinity","subdirFullPath","subEntries","matcher","match","err","code","mkdir","dirPath","readFile","encoding","onDownloadProgress","stream","reject","chunks","loaded","on","chunk","push","Buffer","from","total","buffer","concat","toString","writeFile","data","overwrite","onUploadProgress","exists","getDirectoryName","_data","writeStream","totalBytes","pipe","addEventListener","destroy","ArrayBuffer","rm","force","rename","oldPath","newPath","resolvedOldPath","resolvedNewPath","access","copy","src","dest","shallow","resolvedSrc","resolvedDest","parentDir","cp","range","start","end","flags","getUrl","needle"],"mappings":"AAAA,SACCA,oBAAoBC,oBAAoB,EACxCC,qBAAqBC,qBAAqB,QAEpC,UAAU;AACjB,OAAOC,SAAS,mBAAmB;AACnC,OAAOC,UAAU,YAAY;AAC7B,SAASC,QAAQ,QAAuB,cAAc;AACtD,SAASC,aAAa,QAAQ,WAAW;AAqBzC;;;;CAIC,GACD,OAAO,SAASC,qBAAqBC,UAA6B,CAAC,CAAC;IACnE,OAAO,IAAIC,OAAOD;AACnB;AAIA,IAAA,AAAMC,SAAN,MAAMA;IACIC,KAAa;IACLC,GAAQ;IAEzB,YAAY,EACXD,IAAI,EACJC,KAAKR,GAAG,EAIR,GAAG,CAAC,CAAC,CAAE;QACP,sCAAsC;QACtC,IAAIO,MAAM;YACT,IAAI,CAACA,IAAI,GAAGN,KAAKQ,OAAO,CAACF;QAC1B,OAAO;YACN,IAAI,CAACA,IAAI,GAAG;QACb;QACA,IAAI,CAACC,EAAE,GAAGA;IACX;IAEA,uDAAuD;IAC/CE,aAAaC,MAAoB,EAAQ;QAChD,IAAIA,QAAQC,SAAS;YACpB,MAAM,IAAIC,MAAM;QACjB;IACD;IAEA;;;;;;;EAOC,GACDC,YAAYC,QAAgB,EAAU;QACrC,qBAAqB;QACrB,IAAI,CAACA,UAAU;YACd,OAAO,IAAI,CAACR,IAAI,IAAI;QACrB;QAEA,2DAA2D;QAC3D,MAAMS,iBAAiBf,KAAKgB,SAAS,CAACF;QAEtC,qDAAqD;QACrD,IAAI,CAAC,IAAI,CAACR,IAAI,EAAE;YACf,OAAOS;QACR;QAEA,8CAA8C;QAC9C,MAAME,eAAejB,KAAKQ,OAAO,CAACR,KAAKkB,IAAI,CAAC,IAAI,CAACZ,IAAI,EAAES;QAEvD,+DAA+D;QAC/D,IAAI,CAACE,aAAaE,UAAU,CAAC,IAAI,CAACb,IAAI,GAAG;YACxC,MAAM,IAAIM,MAAM,CAAC,yBAAyB,EAAEE,SAAS,iDAAiD,CAAC;QACxG;QAEA,OAAOG;IACR;IAEA;;EAEC,GACD,AAAQG,UAAUC,QAAgB,EAAU;QAC3C,IAAI,CAAC,IAAI,CAACf,IAAI,IAAI,CAACe,SAASF,UAAU,CAAC,IAAI,CAACb,IAAI,GAAG;YAClD,OAAOe;QACR;QAEA,4DAA4D;QAC5D,IAAIC,eAAeD,SAASE,SAAS,CAAC,IAAI,CAACjB,IAAI,CAACkB,MAAM;QACtD,IAAI,CAACF,cAAc;YAClB,OAAO;QACR;QAEA,IAAI,CAACA,aAAaH,UAAU,CAAC,QAAQ,CAACG,aAAaH,UAAU,CAAC,OAAO;YACpEG,eAAe,MAAMA;QACtB;QAEA,iDAAiD;QACjD,OAAOtB,KAAKgB,SAAS,CAACM,cAAcG,OAAO,CAAC,OAAO;IACpD;IAEA;;;EAGC,GACD,AAAQC,WAAWL,QAAgB,EAAEM,OAAc,EAAa;QAC/D,MAAMZ,iBAAiB,IAAI,CAACK,SAAS,CAACC;QACtC,MAAMO,gBAAgB5B,KAAK6B,OAAO,CAACd;QACnC,MAAMe,YAAYF,kBAAkB,MAAM,MAAMA,cAAcH,OAAO,CAAC,OAAO;QAE7E,OAAO;YACNK;YACA9B,MAAMe;YACNgB,MAAM/B,KAAKgC,QAAQ,CAACX;YACpBY,MAAMN,QAAQO,WAAW,KAAK,cAAc;YAC5CC,OAAOR,QAAQS,OAAO;YACtBC,MAAMV,QAAQU,IAAI;YAClBC,MAAM,CAAC;QACR;IACD;IAEA,MAAMC,QAAQC,GAAW,EAAEpC,UAA0B,CAAC,CAAC,EAAwB;QAC9E,MAAM,EAAEG,EAAE,EAAE,GAAG,IAAI;QACnB,MAAM,EAAEkC,IAAI,EAAEC,SAAS,EAAEC,QAAQ,CAAC,EAAEV,IAAI,EAAEW,SAAS,IAAI,EAAElC,MAAM,EAAE,GAAGN;QACpE,IAAI,CAACK,YAAY,CAACC;QAElB,kDAAkD;QAClD,MAAMmC,cAAc,IAAI,CAAChC,WAAW,CAAC2B;QAErC,qBAAqB;QACrB,MAAMM,UAAU,MAAMvC,GAAGgC,OAAO,CAACM,aAAa;YAAEE,eAAe;QAAK;QACpE,IAAIC,UAAU,MAAMC,QAAQC,GAAG,CAC9BJ,QACEK,MAAM,CAAC,CAACC,QAAUR,UAAU,CAACQ,MAAMrB,IAAI,CAACZ,UAAU,CAAC,MACnDgC,MAAM,CAAC,CAACC,QAAU,CAACnB,QAASA,CAAAA,SAAS,cAAcmB,MAAMlB,WAAW,KAAKkB,MAAMC,MAAM,EAAC,GACtFC,GAAG,CAAC,OAAOF;YACX,IAAI,CAAC3C,YAAY,CAACC;YAElB,MAAM6C,gBAAgBvD,KAAKkB,IAAI,CAAC2B,aAAaO,MAAMrB,IAAI;YACvD,MAAMyB,OAAO,MAAMjD,GAAGiD,IAAI,CAACD;YAE3B,qCAAqC;YACrC,OAAO;gBACNzB,WAAW,IAAI,CAACV,SAAS,CAACyB;gBAC1B7C,MAAM,IAAI,CAACoB,SAAS,CAACmC;gBACrBxB,MAAMqB,MAAMrB,IAAI;gBAChBE,MAAMmB,MAAMlB,WAAW,KAAK,cAAc;gBAC1CC,OAAOqB,KAAKpB,OAAO;gBACnBC,MAAMmB,KAAKnB,IAAI;gBACfC,MAAM,CAAC;YACR;QACD;QAGF,0BAA0B;QAC1B,IAAII,aAAaC,QAAQ,GAAG;YAC3B,MAAMc,UAAUT,QAAQG,MAAM,CAAC,CAACC,QAAUA,MAAMnB,IAAI,KAAK;YAEzD,KAAK,MAAMyB,UAAUD,QAAS;gBAC7B,IAAI,CAAChD,YAAY,CAACC;gBAElB,MAAMiD,WAAWjB,YAAYkB,WAAWjB,QAAQ;gBAChD,IAAIgB,WAAW,GAAG;oBACjB,sEAAsE;oBACtE,MAAME,iBAAiB,IAAI,CAAChD,WAAW,CAAC6C,OAAO1D,IAAI;oBAEnD,MAAM8D,aAAa,MAAM,IAAI,CAACvB,OAAO,CAACmB,OAAO1D,IAAI,EAAE;wBAClD,GAAGI,OAAO;wBACVuC,OAAOgB;oBACR;oBACAX,UAAU;2BAAIA;2BAAYc;qBAAW;gBACtC;YACD;QACD;QAEA,wBAAwB;QACxB,IAAIrB,MAAM;YACT,MAAM,EAAEsB,OAAO,EAAE,GAAG,MAAM,MAAM,CAAC;YACjC,MAAMC,QAAQD,QAAQtB;YACtBO,UAAUA,QAAQG,MAAM,CAAC,CAACC,QAAUY,MAAMZ,MAAMpD,IAAI;QACrD;QAEA,OAAOgD;IACR;IAEA,MAAMQ,KAAK1C,QAAgB,EAAEV,UAAuB,CAAC,CAAC,EAAsB;QAC3E,MAAM,EAAEG,EAAE,EAAE,GAAG,IAAI;QAEnB,MAAM,EAAEG,MAAM,EAAE,GAAGN;QACnB,IAAI,CAACK,YAAY,CAACC;QAElB,MAAMO,eAAe,IAAI,CAACJ,WAAW,CAACC;QAEtC,IAAI;YACH,MAAM0C,OAAO,MAAMjD,GAAGiD,IAAI,CAACvC;YAC3B,OAAO,IAAI,CAACS,UAAU,CAACT,cAAcuC;QACtC,EAAE,OAAOS,KAAU;YAClB,IAAIA,IAAIC,IAAI,KAAK,UAAU;gBAC1B,MAAM,IAAItD,MAAM,CAAC,gBAAgB,EAAEE,UAAU;YAC9C;YACA,MAAMmD;QACP;IACD;IAEA,MAAME,MAAMC,OAAe,EAAEhE,UAAwB,CAAC,CAAC,EAAiB;QACvE,MAAM,EAAEG,EAAE,EAAE,GAAG,IAAI;QAEnB,MAAM,EAAEmC,YAAY,KAAK,EAAEhC,MAAM,EAAE,GAAGN;QACtC,IAAI,CAACK,YAAY,CAACC;QAElB,MAAMO,eAAe,IAAI,CAACJ,WAAW,CAACuD;QAEtC,IAAI;YACH,MAAM7D,GAAG4D,KAAK,CAAClD,cAAc;gBAAEyB;YAAU;QAC1C,EAAE,OAAOuB,KAAU;YAClB,IAAIA,IAAIC,IAAI,KAAK,YAAYxB,WAAW;gBACvC,mDAAmD;gBACnD;YACD;YACA,MAAMuB;QACP;IACD;IAIA,MAAMI,SAASrE,IAAY,EAAEI,UAA2B,CAAC,CAAC,EAAgC;QACzF,MAAM,EAAEG,EAAE,EAAE,GAAG,IAAI;QAEnB,MAAM,EAAE+D,WAAW,QAAQ,EAAE5D,MAAM,EAAE6D,kBAAkB,EAAE,GAAGnE;QAC5D,IAAI,CAACK,YAAY,CAACC;QAElB,MAAMO,eAAe,IAAI,CAACJ,WAAW,CAACb;QAEtC,IAAI;YACH,sCAAsC;YACtC,IAAIuE,oBAAoB;gBACvB,MAAMf,OAAO,MAAMjD,GAAGiD,IAAI,CAACvC;gBAC3B,MAAMuD,SAAS,IAAI,CAAC7E,gBAAgB,CAACK,MAAM;oBAAEU;gBAAO;gBAEpD,OAAO,IAAIuC,QAAQ,CAACzC,SAASiE;oBAC5B,MAAMC,SAAmB,EAAE;oBAC3B,IAAIC,SAAS;oBAEbH,OAAOI,EAAE,CAAC,QAAQ,CAACC;wBAClBH,OAAOI,IAAI,CAACC,OAAOC,IAAI,CAACH;wBACxBF,UAAUE,MAAMrD,MAAM;wBACtB+C,mBAAmB;4BAAEI;4BAAQM,OAAOzB,KAAKnB,IAAI;wBAAC;oBAC/C;oBAEAmC,OAAOI,EAAE,CAAC,OAAO;wBAChB,MAAMM,SAASH,OAAOI,MAAM,CAACT;wBAC7B,IAAIJ,aAAa,QAAQ;4BACxB9D,QAAQ0E,OAAOE,QAAQ,CAAC;wBACzB,OAAO;4BACN5E,QAAQ0E;wBACT;oBACD;oBAEAV,OAAOI,EAAE,CAAC,SAASH;gBACpB;YACD;YAEA,wBAAwB;YACxB,IAAIH,aAAa,QAAQ;gBACxB,OAAO,MAAM/D,GAAG8D,QAAQ,CAACpD,cAAc;oBAAEqD,UAAU;gBAAQ;YAC5D,OAAO;gBACN,OAAO,MAAM/D,GAAG8D,QAAQ,CAACpD;YAC1B;QACD,EAAE,OAAOgD,KAAU;YAClB,IAAIA,IAAIC,IAAI,KAAK,UAAU;gBAC1B,MAAM,IAAItD,MAAM,CAAC,gBAAgB,EAAEZ,MAAM;YAC1C;YACA,MAAMiE;QACP;IACD;IAEA,MAAMoB,UACLrF,IAAY,EACZsF,IAA8C,EAC9ClF,UAA4B,CAAC,CAAC,EACd;QAChB,MAAM,EAAEG,EAAE,EAAE,GAAG,IAAI;QAEnB,MAAM,EAAEG,MAAM,EAAE6E,YAAY,IAAI,EAAEC,gBAAgB,EAAE,GAAGpF;QACvD,IAAI,CAACK,YAAY,CAACC;QAElB,MAAMO,eAAe,IAAI,CAACJ,WAAW,CAACb;QAEtC,8CAA8C;QAC9C,IAAI,CAACuF,WAAW;YACf,MAAME,SAAS,MAAM,IAAI,CAACA,MAAM,CAACzF;YACjC,IAAIyF,QAAQ;gBACX,MAAM,IAAI7E,MAAM,CAAC,qBAAqB,EAAEZ,MAAM;YAC/C;QACD;QAEA,gDAAgD;QAChD,MAAM8B,YAAY,IAAI,CAAC4D,gBAAgB,CAACzE;QACxC,IAAIa,cAAcb,cAAc;YAC/B,IAAI;gBACH,MAAM,IAAI,CAACkD,KAAK,CAAC,IAAI,CAAC/C,SAAS,CAACU,YAAY;oBAAEY,WAAW;gBAAK;YAC/D,EAAE,OAAOuB,KAAU;gBAClB,gCAAgC;gBAChC,IAAIA,KAAKC,SAAS,UAAU;oBAC3B,MAAMD;gBACP;YACD;QACD;QAEA,IAAIqB,gBAAgBrF,UAAU;YAC7B,IAAI0F,QAAQL;YACZ,OAAO,IAAIrC,QAAQ,CAACzC,SAASiE;gBAC5B,MAAMmB,cAAc,IAAI,CAAC/F,iBAAiB,CAACG,MAAMI;gBACjD,IAAIyF,aAAa;gBAEjB,IAAIL,kBAAkB;oBACrBG,MAAMf,EAAE,CAAC,QAAQ,CAACC;wBACjBgB,cAAchB,MAAMrD,MAAM;wBAC1BgE,iBAAiB;4BAAEb,QAAQkB;4BAAYZ,OAAO,CAAC;wBAAE;oBAClD;gBACD;gBAEAU,MAAMG,IAAI,CAACF;gBAEXA,YAAYhB,EAAE,CAAC,UAAU,IAAMpE;gBAC/BoF,YAAYhB,EAAE,CAAC,SAASH;gBAExB,IAAI/D,QAAQ;oBACXA,OAAOqF,gBAAgB,CAAC,SAAS;wBAChCJ,MAAMK,OAAO;wBACbJ,YAAYI,OAAO;wBACnBvB,OAAO,IAAI7D,MAAM;oBAClB;gBACD;YACD;QACD,OAAO;YACN,6CAA6C;YAC7C,IAAI0E,gBAAgBW,aAAa;gBAChCX,OAAOP,OAAOC,IAAI,CAACM;YACpB;YACA,MAAM/E,GAAG8E,SAAS,CAACpE,cAAcqE;QAClC;IACD;IAEA,MAAMY,GAAGlG,IAAY,EAAEI,UAAqB,CAAC,CAAC,EAAiB;QAC9D,MAAM,EAAEG,EAAE,EAAE,GAAG,IAAI;QAEnB,MAAM,EAAEmC,YAAY,KAAK,EAAEyD,QAAQ,KAAK,EAAEzF,MAAM,EAAE,GAAGN;QACrD,IAAI,CAACK,YAAY,CAACC;QAElB,MAAMO,eAAe,IAAI,CAACJ,WAAW,CAACb;QAEtC,IAAI;YACH,MAAMO,GAAG2F,EAAE,CAACjF,cAAc;gBAAEyB;gBAAWyD;YAAM;QAC9C,EAAE,OAAOlC,KAAU;YAClB,IAAIkC,SAASlC,IAAIC,IAAI,KAAK,UAAU;gBACnC;YACD;YACA,MAAMD;QACP;IACD;IAEA,MAAMmC,OAAOC,OAAe,EAAEC,OAAe,EAAElG,UAAyB,CAAC,CAAC,EAAiB;QAC1F,MAAM,EAAEG,EAAE,EAAE,GAAG,IAAI;QAEnB,MAAM,EAAEG,MAAM,EAAE6E,YAAY,KAAK,EAAE,GAAGnF;QACtC,IAAI,CAACK,YAAY,CAACC;QAElB,MAAM6F,kBAAkB,IAAI,CAAC1F,WAAW,CAACwF;QACzC,MAAMG,kBAAkB,IAAI,CAAC3F,WAAW,CAACyF;QAEzC,gDAAgD;QAChD,IAAI,CAACf,WAAW;YACf,MAAME,SAAS,MAAM,IAAI,CAACA,MAAM,CAACa;YACjC,IAAIb,QAAQ;gBACX,MAAM,IAAI7E,MAAM,CAAC,4BAA4B,EAAE0F,SAAS;YACzD;QACD;QAEA,IAAI;YACH,MAAM/F,GAAG6F,MAAM,CAACG,iBAAiBC;QAClC,EAAE,OAAOvC,KAAU;YAClB,IAAIA,IAAIC,IAAI,KAAK,UAAU;gBAC1B,MAAM,IAAItD,MAAM,CAAC,uBAAuB,EAAEyF,SAAS;YACpD;YACA,MAAMpC;QACP;IACD;IAEA,MAAMwB,OAAOzF,IAAY,EAAoB;QAC5C,MAAM,EAAEO,EAAE,EAAE,GAAG,IAAI;QAEnB,IAAI;YACH,MAAMA,GAAGkG,MAAM,CAAC,IAAI,CAAC5F,WAAW,CAACb;YACjC,OAAO;QACR,EAAE,OAAM;YACP,OAAO;QACR;IACD;IAEA,MAAM0G,KAAKC,GAAW,EAAEC,IAAY,EAAExG,UAAuB,CAAC,CAAC,EAAiB;QAC/E,MAAM,EAAEG,EAAE,EAAE,GAAG,IAAI;QAEnB,MAAM,EAAEG,MAAM,EAAE6E,YAAY,IAAI,EAAEsB,UAAU,KAAK,EAAE,GAAGzG;QACtD,IAAI,CAACK,YAAY,CAACC;QAElB,MAAMoG,cAAc,IAAI,CAACjG,WAAW,CAAC8F;QACrC,MAAMI,eAAe,IAAI,CAAClG,WAAW,CAAC+F;QAEtC,yBAAyB;QACzB,IAAI;YACH,MAAMpD,OAAO,MAAMjD,GAAGiD,IAAI,CAACsD;YAE3B,qDAAqD;YACrD,IAAI,CAACvB,WAAW;gBACf,MAAME,SAAS,MAAM,IAAI,CAACA,MAAM,CAACmB;gBACjC,IAAInB,QAAQ;oBACX,MAAM,IAAI7E,MAAM,CAAC,4BAA4B,EAAEgG,MAAM;gBACtD;YACD;YAEA,8CAA8C;YAC9C,MAAMI,YAAY,IAAI,CAACtB,gBAAgB,CAACqB;YACxC,MAAM,IAAI,CAAC5C,KAAK,CAAC,IAAI,CAAC/C,SAAS,CAAC4F,YAAY;gBAAEtE,WAAW;YAAK;YAE9D,mEAAmE;YACnE,MAAMnC,GAAG0G,EAAE,CAACH,aAAaC,cAAc;gBACtCrE,WAAW,CAACmE,WAAWrD,KAAKtB,WAAW;gBACvCiE,OAAOZ;YACR;QACD,EAAE,OAAOtB,KAAU;YAClB,IAAIA,IAAIC,IAAI,KAAK,UAAU;gBAC1B,MAAM,IAAItD,MAAM,CAAC,uBAAuB,EAAE+F,KAAK;YAChD;YACA,MAAM1C;QACP;IACD;IAEAtE,iBAAiBmB,QAAgB,EAAEV,UAAmC,CAAC,CAAC,EAAY;QACnF,MAAMa,eAAe,IAAI,CAACJ,WAAW,CAACC;QACtC,MAAM,EAAEJ,MAAM,EAAEwG,KAAK,EAAE,GAAG9G;QAC1B,MAAMoE,SAAS5E,qBAAqBqB,cAAc;YACjDkG,OAAOD,OAAOC;YACdC,KAAKF,OAAOE;QACb;QAEA1G,QAAQqF,iBAAiB,SAAS,IAAMvB,OAAOwB,OAAO,CAAC,IAAIpF,MAAM;QAEjE,OAAO4D;IACR;IAEA3E,kBAAkBiB,QAAgB,EAAEV,UAAoC,CAAC,CAAC,EAAY;QACrF,MAAMa,eAAe,IAAI,CAACJ,WAAW,CAACC;QACtC,MAAM,EAAEJ,MAAM,EAAE,GAAGN;QACnB,MAAMoE,SAAS1E,sBAAsBmB,cAAc;YAAEoG,OAAOjH,QAAQmF,SAAS,KAAK,QAAQ,OAAO;QAAI;QAErG7E,QAAQqF,iBAAiB,SAAS,IAAMvB,OAAOwB,OAAO,CAAC,IAAIpF,MAAM;QAEjE,OAAO4D;IACR;IAEA,6CAA6C;IACrCkB,iBAAiB5E,QAAgB,EAAU;QAClD,OAAOd,KAAK6B,OAAO,CAACf;IACrB;IAEAwG,OAAOC,MAA0B,EAAE;QAClC,IAAI,OAAOA,WAAW,YAAYA,QAAQtF,SAAS,QAAQ;YAC1D;QACD;QACA,IAAIjC,OAAO,OAAOuH,WAAW,WAAWA,SAASA,OAAOvH,IAAI;QAC5D,IAAI,CAACA,MAAM;YACV;QACD;QACA,UAAU;QACV,OAAOE,cAAc,IAAI,CAACW,WAAW,CAACb,OAAOoF,QAAQ;IACtD;AACD"}
|