@wener/common 2.0.3 → 2.0.6
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 +1 -1
- package/lib/ai/qwen3vl/utils.js +15 -15
- package/lib/ai/qwen3vl/utils.js.map +1 -1
- package/lib/ai/vision/DocLayoutElementTypeSchema.js +22 -22
- package/lib/ai/vision/ImageAnnotationSchema.js +63 -47
- package/lib/ai/vision/index.js +2 -2
- package/lib/ai/vision/resolveImageAnnotation.js +81 -95
- package/lib/cn/ChineseResidentIdNo.js +55 -41
- package/lib/cn/ChineseResidentIdNo.js.map +1 -1
- package/lib/cn/ChineseResidentIdNo.mod.js +6 -1
- package/lib/cn/ChineseResidentIdNo.test.js +22 -21
- package/lib/cn/DivisionCode.js +220 -235
- package/lib/cn/DivisionCode.mod.js +6 -1
- package/lib/cn/DivisionCode.test.js +92 -121
- package/lib/cn/Mod11.js +18 -37
- package/lib/cn/Mod11.js.map +1 -1
- package/lib/cn/Mod31.js +23 -41
- package/lib/cn/UnifiedSocialCreditCode.js +143 -137
- package/lib/cn/UnifiedSocialCreditCode.mod.js +6 -1
- package/lib/cn/UnifiedSocialCreditCode.test.js +21 -15
- package/lib/cn/formatChineseAmount.js +46 -71
- package/lib/cn/index.js +6 -6
- package/lib/cn/mod.js +5 -3
- package/lib/cn/parseChineseNumber.js +81 -85
- package/lib/cn/parseChineseNumber.test.js +183 -261
- package/lib/cn/pinyin/cartesianProduct.js +19 -19
- package/lib/cn/pinyin/cartesianProduct.test.js +78 -178
- package/lib/cn/pinyin/loader.js +13 -11
- package/lib/cn/pinyin/preload.js +2 -1
- package/lib/cn/pinyin/toPinyin.test.js +149 -161
- package/lib/cn/pinyin/toPinyinPure.js +28 -23
- package/lib/cn/pinyin/transform.js +11 -11
- package/lib/cn/types.d.js +2 -2
- package/lib/consola/createStandardConsolaReporter.js +14 -15
- package/lib/consola/formatLogObject.js +149 -133
- package/lib/consola/formatLogObject.js.map +1 -1
- package/lib/consola/formatLogObject.test.js +167 -178
- package/lib/consola/index.js +2 -2
- package/lib/data/formatSort.js +14 -12
- package/lib/data/formatSort.test.js +33 -33
- package/lib/data/index.js +3 -3
- package/lib/data/maybeNumber.js +23 -23
- package/lib/data/maybeNumber.js.map +1 -1
- package/lib/data/parseSort.js +75 -68
- package/lib/data/parseSort.test.js +196 -187
- package/lib/data/resolvePagination.js +38 -39
- package/lib/data/resolvePagination.test.js +228 -218
- package/lib/data/types.d.js +2 -2
- package/lib/data/types.d.js.map +1 -1
- package/lib/dayjs/dayjs.js +20 -20
- package/lib/dayjs/formatDuration.js +56 -56
- package/lib/dayjs/formatDuration.js.map +1 -1
- package/lib/dayjs/formatDuration.test.js +63 -77
- package/lib/dayjs/index.js +4 -4
- package/lib/dayjs/parseDuration.js +21 -26
- package/lib/dayjs/parseRelativeTime.js +65 -66
- package/lib/dayjs/parseRelativeTime.test.js +227 -243
- package/lib/dayjs/resolveRelativeTime.js +74 -144
- package/lib/dayjs/resolveRelativeTime.js.map +1 -1
- package/lib/dayjs/resolveRelativeTime.test.js +296 -307
- package/lib/decimal/index.js +1 -1
- package/lib/decimal/parseDecimal.js +12 -12
- package/lib/drain3/Drain.js +321 -0
- package/lib/drain3/Drain.js.map +1 -0
- package/lib/drain3/LogCluster.js +38 -0
- package/lib/drain3/LogCluster.js.map +1 -0
- package/lib/drain3/Node.js +39 -0
- package/lib/drain3/Node.js.map +1 -0
- package/lib/drain3/TemplateMiner.js +205 -0
- package/lib/drain3/TemplateMiner.js.map +1 -0
- package/lib/drain3/index.js +31 -0
- package/lib/drain3/index.js.map +1 -0
- package/lib/drain3/persistence/FilePersistence.js +24 -0
- package/lib/drain3/persistence/FilePersistence.js.map +1 -0
- package/lib/drain3/persistence/MemoryPersistence.js +18 -0
- package/lib/drain3/persistence/MemoryPersistence.js.map +1 -0
- package/lib/drain3/persistence/PersistenceHandler.js +5 -0
- package/lib/drain3/persistence/PersistenceHandler.js.map +1 -0
- package/lib/drain3/types.js +7 -0
- package/lib/drain3/types.js.map +1 -0
- package/lib/emittery/emitter.js +7 -7
- package/lib/emittery/index.js +1 -1
- package/lib/foundation/schema/SexType.js +15 -12
- package/lib/foundation/schema/index.js +1 -1
- package/lib/foundation/schema/parseSexType.js +15 -16
- package/lib/foundation/schema/types.js +8 -6
- package/lib/fs/FileSystemError.js +18 -18
- package/lib/fs/IFileSystem.d.js +2 -2
- package/lib/fs/IFileSystem.d.js.map +1 -1
- package/lib/fs/MemoryFileSystem.test.js +172 -181
- package/lib/fs/createBrowserFileSystem.js +222 -233
- package/lib/fs/createBrowserFileSystem.js.map +1 -1
- package/lib/fs/createMemoryFileSystem.js +473 -510
- package/lib/fs/createMemoryFileSystem.js.map +1 -1
- package/lib/fs/createSandboxFileSystem.js +102 -101
- package/lib/fs/createSandboxFileSystem.js.map +1 -1
- package/lib/fs/createWebDavFileSystem.js +162 -132
- package/lib/fs/createWebDavFileSystem.js.map +1 -1
- package/lib/fs/createWebFileSystem.js +202 -0
- package/lib/fs/createWebFileSystem.js.map +1 -0
- package/lib/fs/findMimeType.js +14 -14
- package/lib/fs/findMimeType.js.map +1 -1
- package/lib/fs/index.js +7 -7
- package/lib/fs/index.js.map +1 -1
- package/lib/fs/minio/createMinioFileSystem.js +977 -0
- package/lib/fs/minio/createMinioFileSystem.js.map +1 -0
- package/lib/fs/minio/index.js +2 -0
- package/lib/fs/minio/index.js.map +1 -0
- package/lib/fs/orpc/FileSystemContract.js +57 -57
- package/lib/fs/orpc/createContractClientFileSystem.js +88 -88
- package/lib/fs/orpc/createContractClientFileSystem.js.map +1 -1
- package/lib/fs/orpc/index.js +2 -2
- package/lib/fs/orpc/server/createFileSystemContractImpl.js +62 -60
- package/lib/fs/orpc/server/createFileSystemContractImpl.js.map +1 -1
- package/lib/fs/orpc/server/index.js +1 -1
- package/lib/fs/s3/createS3MiniFileSystem.js +756 -689
- package/lib/fs/s3/createS3MiniFileSystem.js.map +1 -1
- package/lib/fs/s3/index.js +1 -1
- package/lib/fs/s3/s3mini.test.js +524 -553
- package/lib/fs/scandir.js +56 -56
- package/lib/fs/server/createDatabaseFileSystem.js +834 -741
- package/lib/fs/server/createDatabaseFileSystem.js.map +1 -1
- package/lib/fs/server/createNodeFileSystem.js +407 -380
- package/lib/fs/server/createNodeFileSystem.js.map +1 -1
- package/lib/fs/server/dbfs.test.js +201 -214
- package/lib/fs/server/index.js +1 -1
- package/lib/fs/server/loadTestDatabase.js +40 -43
- package/lib/fs/tests/runFileSystemTest.js +352 -315
- package/lib/fs/tests/runFileSystemTest.js.map +1 -1
- package/lib/fs/types.js +17 -20
- package/lib/fs/utils/getFileUrl.js +24 -30
- package/lib/fs/utils.js +17 -17
- package/lib/fs/utils.js.map +1 -1
- package/lib/fs/webdav/index.js +2 -0
- package/lib/fs/webdav/index.js.map +1 -0
- package/lib/index.js +2 -2
- package/lib/jsonschema/JsonSchema.js +216 -155
- package/lib/jsonschema/JsonSchema.js.map +1 -1
- package/lib/jsonschema/JsonSchema.test.js +123 -124
- package/lib/jsonschema/forEachJsonSchema.js +41 -41
- package/lib/jsonschema/forEachJsonSchema.js.map +1 -1
- package/lib/jsonschema/index.js +2 -2
- package/lib/jsonschema/types.d.js +2 -2
- package/lib/jsonschema/types.d.js.map +1 -1
- package/lib/meta/defineFileType.js +32 -38
- package/lib/meta/defineInit.js +39 -35
- package/lib/meta/defineMetadata.js +37 -34
- package/lib/meta/defineMetadata.js.map +1 -1
- package/lib/meta/defineMetadata.test.js +13 -12
- package/lib/meta/index.js +3 -3
- package/lib/orpc/createOpenApiContractClient.js +26 -24
- package/lib/orpc/createOpenApiContractClient.js.map +1 -1
- package/lib/orpc/createRpcContractClient.js +37 -31
- package/lib/orpc/index.js +2 -2
- package/lib/orpc/resolveLinkPlugins.js +25 -25
- package/lib/password/PHC.js +187 -189
- package/lib/password/PHC.js.map +1 -1
- package/lib/password/PHC.test.js +517 -535
- package/lib/password/Password.js +85 -80
- package/lib/password/Password.test.js +330 -364
- package/lib/password/createArgon2PasswordAlgorithm.js +50 -51
- package/lib/password/createArgon2PasswordAlgorithm.js.map +1 -1
- package/lib/password/createBase64PasswordAlgorithm.js +11 -11
- package/lib/password/createBase64PasswordAlgorithm.js.map +1 -1
- package/lib/password/createBcryptPasswordAlgorithm.js +20 -18
- package/lib/password/createBcryptPasswordAlgorithm.js.map +1 -1
- package/lib/password/createPBKDF2PasswordAlgorithm.js +65 -52
- package/lib/password/createPBKDF2PasswordAlgorithm.js.map +1 -1
- package/lib/password/createScryptPasswordAlgorithm.js +74 -63
- package/lib/password/createScryptPasswordAlgorithm.js.map +1 -1
- package/lib/password/index.js +5 -5
- package/lib/password/server/index.js +1 -1
- package/lib/resource/Identifiable.js +2 -2
- package/lib/resource/ListQuery.js +42 -42
- package/lib/resource/ListQuery.js.map +1 -1
- package/lib/resource/getTitleOfResource.js +5 -5
- package/lib/resource/index.js +2 -2
- package/lib/resource/index.js.map +1 -1
- package/lib/resource/schema/AnyResourceSchema.js +91 -89
- package/lib/resource/schema/BaseResourceSchema.js +26 -26
- package/lib/resource/schema/ResourceActionType.js +117 -115
- package/lib/resource/schema/ResourceStatus.js +94 -92
- package/lib/resource/schema/ResourceType.js +25 -23
- package/lib/resource/schema/index.js +5 -5
- package/lib/resource/schema/types.js +86 -55
- package/lib/resource/schema/types.test.js +16 -13
- package/lib/s3/formatS3Url.js +60 -60
- package/lib/s3/formatS3Url.js.map +1 -1
- package/lib/s3/formatS3Url.test.js +238 -261
- package/lib/s3/index.js +2 -2
- package/lib/s3/parseS3Url.js +61 -60
- package/lib/s3/parseS3Url.js.map +1 -1
- package/lib/s3/parseS3Url.test.js +270 -269
- package/lib/schema/SchemaRegistry.js +41 -42
- package/lib/schema/SchemaRegistry.js.map +1 -1
- package/lib/schema/SchemaRegistry.mod.js +1 -1
- package/lib/schema/TypeSchema.d.js +2 -2
- package/lib/schema/TypeSchema.d.js.map +1 -1
- package/lib/schema/createSchemaData.js +113 -67
- package/lib/schema/createSchemaData.js.map +1 -1
- package/lib/schema/findJsonSchemaByPath.js +28 -23
- package/lib/schema/findJsonSchemaByPath.js.map +1 -1
- package/lib/schema/formatZodError.js +113 -134
- package/lib/schema/formatZodError.js.map +1 -1
- package/lib/schema/formatZodError.test.js +192 -195
- package/lib/schema/getSchemaCache.js +7 -7
- package/lib/schema/getSchemaOptions.js +17 -16
- package/lib/schema/index.js +6 -6
- package/lib/schema/toJsonSchema.js +196 -190
- package/lib/schema/toJsonSchema.js.map +1 -1
- package/lib/schema/toJsonSchema.test.js +34 -26
- package/lib/schema/validate.js +106 -97
- package/lib/schema/validate.js.map +1 -1
- package/lib/tools/generateSchema.js +40 -40
- package/lib/tools/renderJsonSchemaToMarkdownDoc.js +74 -74
- package/lib/utils/buildBaseUrl.js +8 -8
- package/lib/utils/buildRedactorFormSchema.js +55 -54
- package/lib/utils/buildRedactorFormSchema.js.map +1 -1
- package/lib/utils/getEstimateProcessTime.js +24 -19
- package/lib/utils/index.js +3 -3
- package/lib/utils/resolveFeatureOptions.js +9 -9
- package/package.json +37 -18
- package/src/ai/qwen3vl/utils.ts +1 -1
- package/src/ai/vision/index.ts +2 -2
- package/src/cn/ChineseResidentIdNo.ts +1 -1
- package/src/cn/Mod11.ts +1 -1
- package/src/cn/__snapshots__/ChineseResidentIdNo.test.ts.snap +1 -1
- package/src/cn/__snapshots__/UnifiedSocialCreditCode.test.ts.snap +0 -23
- package/src/cn/index.ts +1 -2
- package/src/cn/parseChineseNumber.test.ts +4 -4
- package/src/consola/formatLogObject.ts +6 -6
- package/src/consola/index.ts +1 -1
- package/src/data/index.ts +3 -4
- package/src/data/maybeNumber.ts +1 -1
- package/src/data/parseSort.test.ts +0 -1
- package/src/data/resolvePagination.ts +2 -2
- package/src/data/types.d.ts +2 -2
- package/src/dayjs/formatDuration.ts +10 -11
- package/src/dayjs/index.ts +1 -1
- package/src/dayjs/parseRelativeTime.ts +1 -1
- package/src/dayjs/resolveRelativeTime.ts +11 -14
- package/src/drain3/Drain.test.ts +378 -0
- package/src/drain3/Drain.ts +394 -0
- package/src/drain3/LogCluster.ts +46 -0
- package/src/drain3/Node.ts +53 -0
- package/src/drain3/TemplateMiner.ts +246 -0
- package/src/drain3/index.ts +34 -0
- package/src/drain3/persistence/FilePersistence.ts +24 -0
- package/src/drain3/persistence/MemoryPersistence.ts +23 -0
- package/src/drain3/persistence/PersistenceHandler.ts +19 -0
- package/src/drain3/types.ts +75 -0
- package/src/fs/IFileSystem.d.ts +1 -2
- package/src/fs/createBrowserFileSystem.ts +7 -5
- package/src/fs/createMemoryFileSystem.ts +9 -13
- package/src/fs/createSandboxFileSystem.ts +1 -1
- package/src/fs/createWebDavFileSystem.ts +30 -17
- package/src/fs/createWebFileSystem.ts +242 -0
- package/src/fs/findMimeType.ts +1 -4
- package/src/fs/index.ts +5 -5
- package/src/fs/minio/createMinioFileSystem.ts +1148 -0
- package/src/fs/minio/index.ts +1 -0
- package/src/fs/orpc/createContractClientFileSystem.ts +5 -5
- package/src/fs/orpc/server/createFileSystemContractImpl.ts +1 -1
- package/src/fs/s3/createS3MiniFileSystem.ts +120 -79
- package/src/fs/s3/s3fs.test.ts +441 -0
- package/src/fs/s3/s3mini.test.ts +2 -2
- package/src/fs/server/createDatabaseFileSystem.ts +78 -114
- package/src/fs/server/createNodeFileSystem.ts +32 -13
- package/src/fs/server/dbfs.test.ts +13 -8
- package/src/fs/server/index.ts +1 -0
- package/src/fs/server/loadTestDatabase.ts +8 -119
- package/src/fs/tests/runFileSystemTest.ts +29 -28
- package/src/fs/utils.ts +1 -1
- package/src/fs/webdav/index.ts +1 -0
- package/src/jsonschema/JsonSchema.ts +5 -5
- package/src/jsonschema/forEachJsonSchema.ts +1 -1
- package/src/jsonschema/index.ts +1 -1
- package/src/jsonschema/types.d.ts +1 -1
- package/src/meta/defineMetadata.ts +1 -1
- package/src/meta/index.ts +2 -3
- package/src/orm/createSqliteDialect.ts +17 -0
- package/src/orm/index.ts +1 -0
- package/src/orpc/createOpenApiContractClient.ts +3 -3
- package/src/orpc/index.ts +1 -1
- package/src/password/PHC.ts +3 -3
- package/src/password/createArgon2PasswordAlgorithm.ts +2 -2
- package/src/password/createBase64PasswordAlgorithm.ts +2 -2
- package/src/password/createBcryptPasswordAlgorithm.ts +4 -2
- package/src/password/createPBKDF2PasswordAlgorithm.ts +2 -2
- package/src/password/createScryptPasswordAlgorithm.ts +4 -4
- package/src/password/index.ts +2 -2
- package/src/resource/ListQuery.ts +4 -1
- package/src/resource/index.ts +3 -3
- package/src/resource/schema/index.ts +4 -4
- package/src/s3/formatS3Url.test.ts +1 -1
- package/src/s3/formatS3Url.ts +2 -2
- package/src/s3/index.ts +1 -1
- package/src/s3/parseS3Url.ts +1 -1
- package/src/schema/SchemaRegistry.ts +2 -2
- package/src/schema/TypeSchema.d.ts +6 -6
- package/src/schema/createSchemaData.ts +5 -5
- package/src/schema/findJsonSchemaByPath.ts +5 -5
- package/src/schema/formatZodError.test.ts +2 -1
- package/src/schema/formatZodError.ts +50 -62
- package/src/schema/index.ts +5 -5
- package/src/schema/toJsonSchema.ts +6 -6
- package/src/schema/validate.ts +2 -2
- package/src/utils/buildRedactorFormSchema.ts +4 -4
- package/src/utils/formatNumber.ts +18 -0
- package/src/utils/formatPercent.ts +17 -0
- package/src/utils/index.ts +3 -3
- package/src/utils/resolveFeatureOptions.ts +1 -1
|
@@ -1,21 +1,8 @@
|
|
|
1
1
|
import * as crypto from 'node:crypto';
|
|
2
2
|
import { basename, dirname, join, normalize } from 'node:path';
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
Entity,
|
|
7
|
-
ManyToOne,
|
|
8
|
-
OneToMany,
|
|
9
|
-
OneToOne,
|
|
10
|
-
Property,
|
|
11
|
-
types,
|
|
12
|
-
Unique,
|
|
13
|
-
type Opt,
|
|
14
|
-
type Rel,
|
|
15
|
-
} from '@mikro-orm/core';
|
|
16
|
-
import type { EntityManager } from '@mikro-orm/knex';
|
|
17
|
-
import { TenantBaseEntity } from '@wener/server/entity';
|
|
18
|
-
import { getEntityManager } from '@wener/server/mikro-orm';
|
|
3
|
+
import { BaseEntity, Cascade, Collection, type Opt, type Rel } from '@mikro-orm/core';
|
|
4
|
+
import { Entity, ManyToOne, OneToMany, OneToOne, PrimaryKey, Property, Unique } from '@mikro-orm/decorators/legacy';
|
|
5
|
+
import type { EntityManager } from '@mikro-orm/sql';
|
|
19
6
|
import type { CopyOptions } from 'fs-extra';
|
|
20
7
|
import { FileSystemError, FileSystemErrorCode } from '../FileSystemError';
|
|
21
8
|
import type {
|
|
@@ -49,11 +36,13 @@ class DBFS implements IFileSystem {
|
|
|
49
36
|
options: IDatabaseFileSystemOptions;
|
|
50
37
|
|
|
51
38
|
constructor(options: Partial<IDatabaseFileSystemOptions> = {}) {
|
|
39
|
+
if (!options.getEntityManager) {
|
|
40
|
+
throw new Error('getEntityManager is required for DBFS');
|
|
41
|
+
}
|
|
52
42
|
this.options = {
|
|
53
|
-
getEntityManager: () => getEntityManager<EntityManager>().fork(),
|
|
54
43
|
smallFileThreshold: 512,
|
|
55
44
|
...options,
|
|
56
|
-
};
|
|
45
|
+
} as IDatabaseFileSystemOptions;
|
|
57
46
|
}
|
|
58
47
|
|
|
59
48
|
get em() {
|
|
@@ -87,7 +76,7 @@ class DBFS implements IFileSystem {
|
|
|
87
76
|
mtime: now,
|
|
88
77
|
});
|
|
89
78
|
try {
|
|
90
|
-
await em.
|
|
79
|
+
await em.persist(rootDir).flush();
|
|
91
80
|
return rootDir;
|
|
92
81
|
} catch (error: any) {
|
|
93
82
|
// If root already exists (race condition), fetch and return it
|
|
@@ -169,7 +158,7 @@ class DBFS implements IFileSystem {
|
|
|
169
158
|
mtime: now,
|
|
170
159
|
});
|
|
171
160
|
try {
|
|
172
|
-
await em.
|
|
161
|
+
await em.persist(rootDir).flush();
|
|
173
162
|
} catch (error: any) {
|
|
174
163
|
// If root already exists (race condition), ignore the error
|
|
175
164
|
if (!error.message?.includes('UNIQUE constraint') && !error.message?.includes('duplicate')) {
|
|
@@ -188,7 +177,6 @@ class DBFS implements IFileSystem {
|
|
|
188
177
|
|
|
189
178
|
if (!parentNode) {
|
|
190
179
|
if (options.recursive) {
|
|
191
|
-
// 递归创建父目录
|
|
192
180
|
await this._mkdirInTransaction(parentPath, options, em);
|
|
193
181
|
parentNode = await this._getNodeByPath(parentPath, em);
|
|
194
182
|
} else {
|
|
@@ -206,13 +194,11 @@ class DBFS implements IFileSystem {
|
|
|
206
194
|
|
|
207
195
|
if (existing) {
|
|
208
196
|
if (existing.kind === FileKind.directory) {
|
|
209
|
-
// Directory already exists, return silently
|
|
210
197
|
return;
|
|
211
198
|
}
|
|
212
199
|
throw new FileSystemError(`A file with the same name already exists: ${path}`, FileSystemErrorCode.EEXIST);
|
|
213
200
|
}
|
|
214
201
|
|
|
215
|
-
// Create directory using EntityManager
|
|
216
202
|
const now = new Date();
|
|
217
203
|
const newDir = em.create(FileNodeMetaEntity, {
|
|
218
204
|
tid: parentNode.tid,
|
|
@@ -225,7 +211,7 @@ class DBFS implements IFileSystem {
|
|
|
225
211
|
ctime: now,
|
|
226
212
|
mtime: now,
|
|
227
213
|
});
|
|
228
|
-
await em.
|
|
214
|
+
await em.persist(newDir).flush();
|
|
229
215
|
}
|
|
230
216
|
|
|
231
217
|
readFile(path: string, options?: ReadFileOptions & { encoding: 'text' }): Promise<string>;
|
|
@@ -245,24 +231,22 @@ class DBFS implements IFileSystem {
|
|
|
245
231
|
if (node.kind !== FileKind.file)
|
|
246
232
|
throw new FileSystemError(`Path is not a file: ${path}`, FileSystemErrorCode.EISDIR);
|
|
247
233
|
|
|
248
|
-
let
|
|
234
|
+
let data: Buffer | Uint8Array;
|
|
249
235
|
if (node.content) {
|
|
250
|
-
|
|
251
|
-
buffer = node.content;
|
|
236
|
+
data = node.content;
|
|
252
237
|
} else {
|
|
253
|
-
// Large file: load from file_node_content table using QueryBuilder
|
|
254
238
|
const fileContentQb = em.createQueryBuilder(FileNodeContentEntity, 'fc');
|
|
255
239
|
fileContentQb.where({ node: node });
|
|
256
240
|
const fileContent = await fileContentQb.getSingleResult();
|
|
257
241
|
if (!fileContent) throw new FileSystemError('File content is missing', FileSystemErrorCode.ENOENT);
|
|
258
|
-
|
|
242
|
+
data = fileContent.content;
|
|
259
243
|
}
|
|
260
244
|
|
|
261
|
-
|
|
245
|
+
const buf = Buffer.isBuffer(data) ? data : Buffer.from(data);
|
|
246
|
+
return options?.encoding === 'text' ? buf.toString('utf-8') : buf;
|
|
262
247
|
}
|
|
263
248
|
|
|
264
249
|
async writeFile(path: string, data: string | Buffer, options: WriteFileOptions = {}): Promise<void> {
|
|
265
|
-
// Validate input
|
|
266
250
|
if (!path || typeof path !== 'string') {
|
|
267
251
|
throw new FileSystemError('Invalid path', FileSystemErrorCode.EINVAL);
|
|
268
252
|
}
|
|
@@ -278,33 +262,26 @@ class DBFS implements IFileSystem {
|
|
|
278
262
|
const parentPath = dirname(path);
|
|
279
263
|
const filename = basename(path);
|
|
280
264
|
|
|
281
|
-
// Validate filename
|
|
282
265
|
if (!filename) {
|
|
283
266
|
throw new FileSystemError('filename cannot be empty', FileSystemErrorCode.EINVAL);
|
|
284
267
|
}
|
|
285
268
|
|
|
286
|
-
// 确保父目录存在 - create it within the transaction
|
|
287
269
|
await this._mkdirInTransaction(parentPath, { recursive: true }, em);
|
|
288
270
|
const parentNode = await this._getNodeByPath(parentPath, em);
|
|
289
271
|
if (!parentNode) throw new FileSystemError('Failed to establish parent directory', FileSystemErrorCode.EINVAL);
|
|
290
272
|
|
|
291
|
-
// Find existing node using QueryBuilder
|
|
292
273
|
const nodeQb = em.createQueryBuilder(FileNodeMetaEntity, 'f');
|
|
293
274
|
nodeQb.where({ parent: parentNode, filename });
|
|
294
275
|
let node = await nodeQb.getSingleResult();
|
|
295
276
|
|
|
296
277
|
if (node) {
|
|
297
|
-
// 文件已存在
|
|
298
278
|
if (!overwrite) throw new FileSystemError(`File already exists: ${path}`, FileSystemErrorCode.EEXIST);
|
|
299
279
|
if (node.kind === FileKind.directory)
|
|
300
280
|
throw new FileSystemError(`Cannot overwrite a directory with a file: ${path}`, FileSystemErrorCode.EISDIR);
|
|
301
281
|
|
|
302
|
-
// 更新节点
|
|
303
282
|
node.size = size;
|
|
304
283
|
node.mtime = new Date();
|
|
305
|
-
// ... 其他时间戳
|
|
306
284
|
} else {
|
|
307
|
-
// 新建文件
|
|
308
285
|
const now = new Date();
|
|
309
286
|
node = em.create(FileNodeMetaEntity, {
|
|
310
287
|
tid: parentNode.tid,
|
|
@@ -319,49 +296,41 @@ class DBFS implements IFileSystem {
|
|
|
319
296
|
});
|
|
320
297
|
}
|
|
321
298
|
|
|
322
|
-
// 处理文件内容
|
|
323
299
|
if (size <= this.options.smallFileThreshold!) {
|
|
324
|
-
// Small file: store in file_node_meta.content
|
|
325
300
|
node.content = bufferData;
|
|
326
|
-
// If there was large file content, delete it
|
|
327
|
-
// Use QueryBuilder to avoid relationship issues
|
|
328
301
|
const existingContentQb = em.createQueryBuilder(FileNodeContentEntity, 'fc');
|
|
329
302
|
existingContentQb.where({ node: node });
|
|
330
303
|
const existingContent = await existingContentQb.getSingleResult();
|
|
331
304
|
if (existingContent) {
|
|
332
|
-
await em.
|
|
305
|
+
await em.remove(existingContent).flush();
|
|
333
306
|
}
|
|
334
307
|
} else {
|
|
335
|
-
|
|
336
|
-
node.content = undefined; // Clear small file content
|
|
308
|
+
node.content = undefined;
|
|
337
309
|
const md5 = crypto.createHash('md5').update(bufferData).digest('hex');
|
|
338
310
|
const sha256 = crypto.createHash('sha256').update(bufferData).digest('hex');
|
|
339
311
|
|
|
340
|
-
// Check if fileContent already exists using QueryBuilder
|
|
341
312
|
const fileContentQb = em.createQueryBuilder(FileNodeContentEntity, 'fc');
|
|
342
313
|
fileContentQb.where({ node: node });
|
|
343
314
|
let fileContent = await fileContentQb.getSingleResult();
|
|
344
315
|
|
|
345
316
|
if (fileContent) {
|
|
346
|
-
// Update existing content
|
|
347
317
|
fileContent.content = bufferData;
|
|
348
318
|
fileContent.size = size;
|
|
349
319
|
fileContent.md5 = md5;
|
|
350
320
|
fileContent.sha256 = sha256;
|
|
351
321
|
} else {
|
|
352
|
-
// Create new content entity
|
|
353
322
|
fileContent = em.create(FileNodeContentEntity, {
|
|
354
|
-
node: node,
|
|
323
|
+
node: node,
|
|
355
324
|
tid: node.tid,
|
|
356
325
|
content: bufferData,
|
|
357
326
|
size: size,
|
|
358
327
|
md5: md5,
|
|
359
328
|
sha256: sha256,
|
|
360
|
-
});
|
|
329
|
+
} as any);
|
|
361
330
|
}
|
|
362
331
|
}
|
|
363
332
|
|
|
364
|
-
await em.
|
|
333
|
+
await em.persist(node).flush();
|
|
365
334
|
});
|
|
366
335
|
}
|
|
367
336
|
|
|
@@ -369,12 +338,11 @@ class DBFS implements IFileSystem {
|
|
|
369
338
|
await this.em.transactional(async (em) => {
|
|
370
339
|
const node = await this._getNodeByPath(path, em);
|
|
371
340
|
if (!node) {
|
|
372
|
-
if (options.force) return;
|
|
341
|
+
if (options.force) return;
|
|
373
342
|
throw new FileSystemError(`Path not found: ${path}`, FileSystemErrorCode.ENOENT);
|
|
374
343
|
}
|
|
375
344
|
|
|
376
345
|
if (node.kind === FileKind.directory && !options.recursive) {
|
|
377
|
-
// Check if directory has children using QueryBuilder
|
|
378
346
|
const childrenQb = em.createQueryBuilder(FileNodeMetaEntity, 'f');
|
|
379
347
|
childrenQb.where({ parent: node });
|
|
380
348
|
childrenQb.select('id');
|
|
@@ -384,8 +352,7 @@ class DBFS implements IFileSystem {
|
|
|
384
352
|
}
|
|
385
353
|
}
|
|
386
354
|
|
|
387
|
-
|
|
388
|
-
await em.removeAndFlush(node);
|
|
355
|
+
await em.remove(node).flush();
|
|
389
356
|
});
|
|
390
357
|
}
|
|
391
358
|
|
|
@@ -407,8 +374,8 @@ class DBFS implements IFileSystem {
|
|
|
407
374
|
if (existingDest) {
|
|
408
375
|
if (!options.overwrite)
|
|
409
376
|
throw new FileSystemError(`Destination path already exists: ${newPath}`, FileSystemErrorCode.EEXIST);
|
|
410
|
-
if (node.id === existingDest.id) return;
|
|
411
|
-
await em.
|
|
377
|
+
if (node.id === existingDest.id) return;
|
|
378
|
+
await em.remove(existingDest).flush();
|
|
412
379
|
}
|
|
413
380
|
|
|
414
381
|
node.parent = newParentNode;
|
|
@@ -437,8 +404,7 @@ class DBFS implements IFileSystem {
|
|
|
437
404
|
if (existingDest) {
|
|
438
405
|
if (!options.overwrite)
|
|
439
406
|
throw new FileSystemError(`Destination path already exists: ${destPath}`, FileSystemErrorCode.EEXIST);
|
|
440
|
-
|
|
441
|
-
await em.removeAndFlush(existingDest);
|
|
407
|
+
await em.remove(existingDest).flush();
|
|
442
408
|
}
|
|
443
409
|
|
|
444
410
|
await this._copyNode(srcNode, destParentNode, destFilename, em);
|
|
@@ -461,17 +427,10 @@ class DBFS implements IFileSystem {
|
|
|
461
427
|
throw new Error('WritableStream is not supported by DBFS yet.');
|
|
462
428
|
}
|
|
463
429
|
|
|
464
|
-
/**
|
|
465
|
-
* 将路径字符串解析为数据库中的节点。这是大部分操作的基础。
|
|
466
|
-
* @param pathStr 绝对路径, e.g., /home/user/file.txt
|
|
467
|
-
* @param em EntityManager 实例
|
|
468
|
-
* @returns 找到的节点或 null
|
|
469
|
-
*/
|
|
470
430
|
private async _getNodeByPath(pathStr: string, em: EntityManager): Promise<FileNodeMetaEntity | null> {
|
|
471
431
|
const normalized = normalize(pathStr);
|
|
472
432
|
|
|
473
433
|
if (normalized === '/') {
|
|
474
|
-
// Use QueryBuilder to avoid automatic relationship loading
|
|
475
434
|
const qb = em.createQueryBuilder(FileNodeMetaEntity, 'f');
|
|
476
435
|
qb.where({ parent: null });
|
|
477
436
|
const rootNode = await qb.getSingleResult();
|
|
@@ -480,13 +439,11 @@ class DBFS implements IFileSystem {
|
|
|
480
439
|
|
|
481
440
|
const parts = normalized.split('/').filter((p) => p);
|
|
482
441
|
|
|
483
|
-
// Get root node
|
|
484
442
|
const rootQb = em.createQueryBuilder(FileNodeMetaEntity, 'f');
|
|
485
443
|
rootQb.where({ parent: null });
|
|
486
444
|
let currentNode = await rootQb.getSingleResult();
|
|
487
445
|
if (!currentNode) return null;
|
|
488
446
|
|
|
489
|
-
// Traverse path parts
|
|
490
447
|
for (const part of parts) {
|
|
491
448
|
const childQb = em.createQueryBuilder(FileNodeMetaEntity, 'f');
|
|
492
449
|
childQb.where({ parent: currentNode, filename: part });
|
|
@@ -498,11 +455,7 @@ class DBFS implements IFileSystem {
|
|
|
498
455
|
return currentNode;
|
|
499
456
|
}
|
|
500
457
|
|
|
501
|
-
/**
|
|
502
|
-
* 将数据库实体转换为 IFileStat 接口
|
|
503
|
-
*/
|
|
504
458
|
private _toFileStat(node: FileNodeMetaEntity, path: string): IFileStat {
|
|
505
|
-
// Handle mtime - it might be a Date object or a string from raw query
|
|
506
459
|
let mtime: number;
|
|
507
460
|
if (node.mtime instanceof Date) {
|
|
508
461
|
mtime = node.mtime.getTime();
|
|
@@ -512,7 +465,6 @@ class DBFS implements IFileSystem {
|
|
|
512
465
|
mtime = Date.now();
|
|
513
466
|
}
|
|
514
467
|
|
|
515
|
-
// Handle size - convert bigint to number if needed
|
|
516
468
|
let size: number;
|
|
517
469
|
if (typeof node.size === 'bigint') {
|
|
518
470
|
size = Number(node.size);
|
|
@@ -527,7 +479,6 @@ class DBFS implements IFileSystem {
|
|
|
527
479
|
size: size,
|
|
528
480
|
mtime: mtime,
|
|
529
481
|
meta: node.metadata || {},
|
|
530
|
-
// `directory` 字段可以根据 path 动态计算
|
|
531
482
|
directory: dirname(path),
|
|
532
483
|
};
|
|
533
484
|
}
|
|
@@ -538,44 +489,42 @@ class DBFS implements IFileSystem {
|
|
|
538
489
|
newName: string,
|
|
539
490
|
em: EntityManager,
|
|
540
491
|
): Promise<void> {
|
|
541
|
-
// 1. 复制节点本身
|
|
542
492
|
const now = new Date();
|
|
493
|
+
const copiedContent = srcNode.content ? Buffer.from(srcNode.content) : undefined;
|
|
543
494
|
const newNode = em.create(FileNodeMetaEntity, {
|
|
544
495
|
tid: srcNode.tid,
|
|
545
496
|
filename: newName,
|
|
546
497
|
parent: destParent,
|
|
547
498
|
kind: srcNode.kind,
|
|
548
499
|
size: srcNode.size,
|
|
549
|
-
metadata: srcNode.metadata
|
|
550
|
-
content:
|
|
500
|
+
metadata: srcNode.metadata ? { ...srcNode.metadata } : undefined,
|
|
501
|
+
content: copiedContent,
|
|
551
502
|
atime: now,
|
|
552
503
|
btime: now,
|
|
553
504
|
ctime: now,
|
|
554
505
|
mtime: now,
|
|
555
506
|
});
|
|
556
507
|
|
|
557
|
-
// 2. 复制大文件内容 (如果存在) - use QueryBuilder to avoid relationship loading
|
|
558
508
|
if (!srcNode.content) {
|
|
559
509
|
const srcFileContentQb = em.createQueryBuilder(FileNodeContentEntity, 'fc');
|
|
560
510
|
srcFileContentQb.where({ node: srcNode });
|
|
561
511
|
const srcFileContent = await srcFileContentQb.getSingleResult();
|
|
562
512
|
if (srcFileContent) {
|
|
563
|
-
|
|
564
|
-
node: newNode,
|
|
513
|
+
em.create(FileNodeContentEntity, {
|
|
514
|
+
node: newNode,
|
|
565
515
|
tid: srcNode.tid,
|
|
566
|
-
content: srcFileContent.content,
|
|
516
|
+
content: srcFileContent.content ? Buffer.from(srcFileContent.content) : srcFileContent.content,
|
|
567
517
|
size: srcFileContent.size,
|
|
568
518
|
md5: srcFileContent.md5,
|
|
569
519
|
sha256: srcFileContent.sha256,
|
|
570
520
|
mimeType: srcFileContent.mimeType,
|
|
571
|
-
metadata: srcFileContent.metadata,
|
|
572
|
-
});
|
|
521
|
+
metadata: srcFileContent.metadata ? { ...srcFileContent.metadata } : undefined,
|
|
522
|
+
} as any);
|
|
573
523
|
}
|
|
574
524
|
}
|
|
575
525
|
|
|
576
|
-
await em.
|
|
526
|
+
await em.persist(newNode).flush();
|
|
577
527
|
|
|
578
|
-
// 3. 如果是目录,递归复制子节点
|
|
579
528
|
if (srcNode.kind === FileKind.directory) {
|
|
580
529
|
const childrenQb = em.createQueryBuilder(FileNodeMetaEntity, 'f');
|
|
581
530
|
childrenQb.where({ parent: srcNode });
|
|
@@ -589,25 +538,36 @@ class DBFS implements IFileSystem {
|
|
|
589
538
|
|
|
590
539
|
@Entity({ tableName: 'file_node_meta' })
|
|
591
540
|
@Unique({ properties: ['tid', 'parent', 'filename'] })
|
|
592
|
-
export class FileNodeMetaEntity extends
|
|
593
|
-
@
|
|
541
|
+
export class FileNodeMetaEntity extends BaseEntity {
|
|
542
|
+
@PrimaryKey({ type: 'text', onCreate: () => crypto.randomUUID() })
|
|
543
|
+
id!: string & Opt;
|
|
544
|
+
|
|
545
|
+
@Property({ type: 'text', nullable: true })
|
|
546
|
+
tid?: string;
|
|
547
|
+
|
|
548
|
+
@Property({ type: 'text', nullable: false })
|
|
594
549
|
filename!: string;
|
|
595
|
-
|
|
550
|
+
|
|
551
|
+
@Property({ type: 'integer', nullable: false, default: 0 })
|
|
596
552
|
size!: number & Opt;
|
|
597
|
-
|
|
553
|
+
|
|
554
|
+
@Property({ type: 'text', nullable: false })
|
|
598
555
|
kind!: FileKind;
|
|
599
556
|
|
|
600
|
-
@Property({ type:
|
|
557
|
+
@Property({ type: 'text', nullable: false })
|
|
601
558
|
atime!: Date & Opt;
|
|
602
|
-
|
|
559
|
+
|
|
560
|
+
@Property({ type: 'text', nullable: false })
|
|
603
561
|
btime!: Date & Opt;
|
|
604
|
-
|
|
562
|
+
|
|
563
|
+
@Property({ type: 'text', nullable: false })
|
|
605
564
|
ctime!: Date & Opt;
|
|
606
|
-
|
|
565
|
+
|
|
566
|
+
@Property({ type: 'text', nullable: false })
|
|
607
567
|
mtime!: Date & Opt;
|
|
608
568
|
|
|
609
|
-
@Property({ type:
|
|
610
|
-
metadata!: Record<string, any
|
|
569
|
+
@Property({ type: 'json', nullable: false, default: '{}' })
|
|
570
|
+
metadata!: Record<string, any> & Opt;
|
|
611
571
|
|
|
612
572
|
@ManyToOne(() => FileNodeMetaEntity, { nullable: true, cascade: [] })
|
|
613
573
|
parent?: Rel<FileNodeMetaEntity>;
|
|
@@ -623,46 +583,50 @@ export class FileNodeMetaEntity extends TenantBaseEntity {
|
|
|
623
583
|
cascade: [Cascade.ALL],
|
|
624
584
|
})
|
|
625
585
|
fileContent?: Rel<FileNodeContentEntity>;
|
|
626
|
-
@Property({ type: types.blob, nullable: true, comment: '文件内容' })
|
|
627
|
-
content?: Buffer; // for small file, e.g. < 64k
|
|
628
586
|
|
|
629
|
-
|
|
587
|
+
@Property({ type: 'blob', nullable: true })
|
|
588
|
+
content?: Buffer;
|
|
630
589
|
|
|
631
590
|
get parentId() {
|
|
632
591
|
return this.parent?.id as Opt<string | undefined>;
|
|
633
592
|
}
|
|
634
|
-
|
|
635
|
-
//endregion
|
|
636
593
|
}
|
|
637
594
|
|
|
638
595
|
@Entity({ tableName: 'file_node_content' })
|
|
639
|
-
export class FileNodeContentEntity extends
|
|
596
|
+
export class FileNodeContentEntity extends BaseEntity {
|
|
597
|
+
@PrimaryKey({ type: 'text', onCreate: () => crypto.randomUUID() })
|
|
598
|
+
id!: string & Opt;
|
|
599
|
+
|
|
600
|
+
@Property({ type: 'text', nullable: true })
|
|
601
|
+
tid?: string;
|
|
602
|
+
|
|
640
603
|
@OneToOne({ entity: () => FileNodeMetaEntity, owner: true, joinColumn: 'node_id' })
|
|
641
604
|
node!: Rel<FileNodeMetaEntity>;
|
|
642
605
|
|
|
643
|
-
@Property({ type:
|
|
644
|
-
size!: number;
|
|
606
|
+
@Property({ type: 'integer', nullable: false })
|
|
607
|
+
size!: number;
|
|
645
608
|
|
|
646
|
-
@Property({ type:
|
|
609
|
+
@Property({ type: 'blob', lazy: true })
|
|
647
610
|
content!: Buffer;
|
|
648
611
|
|
|
649
|
-
@Property({ type:
|
|
612
|
+
@Property({ type: 'text', nullable: true })
|
|
650
613
|
mimeType?: string;
|
|
651
614
|
|
|
652
|
-
@Property({ type:
|
|
615
|
+
@Property({ type: 'text', nullable: true })
|
|
653
616
|
md5?: string;
|
|
654
|
-
|
|
617
|
+
|
|
618
|
+
@Property({ type: 'text', nullable: true })
|
|
655
619
|
sha256?: string;
|
|
656
620
|
|
|
657
|
-
@Property({ type:
|
|
621
|
+
@Property({ type: 'text', nullable: true })
|
|
658
622
|
text?: string;
|
|
659
|
-
|
|
623
|
+
|
|
624
|
+
@Property({ type: 'integer', nullable: true })
|
|
660
625
|
width?: number;
|
|
661
|
-
|
|
626
|
+
|
|
627
|
+
@Property({ type: 'integer', nullable: true })
|
|
662
628
|
height?: number;
|
|
663
|
-
// @Property({ type: types.integer, nullable: true })
|
|
664
|
-
// length?: number;
|
|
665
629
|
|
|
666
|
-
@Property({ type:
|
|
667
|
-
metadata!: Record<string, any
|
|
630
|
+
@Property({ type: 'json', nullable: false, default: '{}' })
|
|
631
|
+
metadata!: Record<string, any> & Opt;
|
|
668
632
|
}
|
|
@@ -12,17 +12,18 @@ import type {
|
|
|
12
12
|
CreateReadStreamOptions,
|
|
13
13
|
CreateWriteStreamOptions,
|
|
14
14
|
IFileStat,
|
|
15
|
-
|
|
15
|
+
IServerFileSystem,
|
|
16
16
|
MkdirOptions,
|
|
17
17
|
ReaddirOptions,
|
|
18
18
|
ReadFileOptions,
|
|
19
19
|
RenameOptions,
|
|
20
20
|
RmOptions,
|
|
21
21
|
StatOptions,
|
|
22
|
+
WritableData,
|
|
22
23
|
WriteFileOptions,
|
|
23
24
|
} from '../IFileSystem';
|
|
24
25
|
|
|
25
|
-
export type INodeFileSystem =
|
|
26
|
+
export type INodeFileSystem = IServerFileSystem & {
|
|
26
27
|
readonly root: string;
|
|
27
28
|
resolvePath(filePath: string): string;
|
|
28
29
|
};
|
|
@@ -38,7 +39,7 @@ export function createNodeFileSystem(options: { root?: string } = {}): INodeFile
|
|
|
38
39
|
|
|
39
40
|
type IFS = typeof import('fs/promises');
|
|
40
41
|
|
|
41
|
-
class NodeFs implements
|
|
42
|
+
class NodeFs implements IServerFileSystem, INodeFileSystem {
|
|
42
43
|
readonly root: string;
|
|
43
44
|
private readonly fs: IFS;
|
|
44
45
|
|
|
@@ -113,7 +114,7 @@ class NodeFs implements IFileSystem, INodeFileSystem {
|
|
|
113
114
|
}
|
|
114
115
|
|
|
115
116
|
if (!relativePath.startsWith('/') && !relativePath.startsWith('\\')) {
|
|
116
|
-
relativePath =
|
|
117
|
+
relativePath = `/${relativePath}`;
|
|
117
118
|
}
|
|
118
119
|
|
|
119
120
|
// Normalize to ensure consistent path separators
|
|
@@ -183,7 +184,7 @@ class NodeFs implements IFileSystem, INodeFileSystem {
|
|
|
183
184
|
const maxDepth = recursive ? Infinity : depth - 1;
|
|
184
185
|
if (maxDepth > 0) {
|
|
185
186
|
// Need to convert the path back to a full path for the recursive call
|
|
186
|
-
const
|
|
187
|
+
const _subdirFullPath = this.resolvePath(subdir.path);
|
|
187
188
|
|
|
188
189
|
const subEntries = await this.readdir(subdir.path, {
|
|
189
190
|
...options,
|
|
@@ -295,11 +296,7 @@ class NodeFs implements IFileSystem, INodeFileSystem {
|
|
|
295
296
|
}
|
|
296
297
|
}
|
|
297
298
|
|
|
298
|
-
async writeFile(
|
|
299
|
-
path: string,
|
|
300
|
-
data: string | Buffer | ArrayBuffer | Readable,
|
|
301
|
-
options: WriteFileOptions = {},
|
|
302
|
-
): Promise<void> {
|
|
299
|
+
async writeFile(path: string, data: WritableData, options: WriteFileOptions = {}): Promise<void> {
|
|
303
300
|
const { fs } = this;
|
|
304
301
|
|
|
305
302
|
const { signal, overwrite = true, onUploadProgress } = options;
|
|
@@ -354,12 +351,34 @@ class NodeFs implements IFileSystem, INodeFileSystem {
|
|
|
354
351
|
});
|
|
355
352
|
}
|
|
356
353
|
});
|
|
354
|
+
} else if (data instanceof ReadableStream) {
|
|
355
|
+
// Handle web ReadableStream
|
|
356
|
+
const reader = data.getReader();
|
|
357
|
+
const chunks: Uint8Array[] = [];
|
|
358
|
+
let loaded = 0;
|
|
359
|
+
while (true) {
|
|
360
|
+
const { done, value } = await reader.read();
|
|
361
|
+
if (done) break;
|
|
362
|
+
if (value) {
|
|
363
|
+
chunks.push(value);
|
|
364
|
+
loaded += value.length;
|
|
365
|
+
if (onUploadProgress) {
|
|
366
|
+
onUploadProgress({ loaded, total: -1 });
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
await fs.writeFile(resolvedPath, Buffer.concat(chunks));
|
|
357
371
|
} else {
|
|
358
|
-
// Convert ArrayBuffer to Buffer if necessary
|
|
372
|
+
// Convert ArrayBuffer/ArrayBufferView to Buffer if necessary
|
|
373
|
+
let writeData: string | Buffer;
|
|
359
374
|
if (data instanceof ArrayBuffer) {
|
|
360
|
-
|
|
375
|
+
writeData = Buffer.from(data);
|
|
376
|
+
} else if (ArrayBuffer.isView(data)) {
|
|
377
|
+
writeData = Buffer.from(data.buffer, data.byteOffset, data.byteLength);
|
|
378
|
+
} else {
|
|
379
|
+
writeData = data;
|
|
361
380
|
}
|
|
362
|
-
await fs.writeFile(resolvedPath,
|
|
381
|
+
await fs.writeFile(resolvedPath, writeData);
|
|
363
382
|
}
|
|
364
383
|
}
|
|
365
384
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { EntityManager } from '@mikro-orm/
|
|
2
|
-
import { beforeEach, describe, test } from 'vitest';
|
|
1
|
+
import type { EntityManager } from '@mikro-orm/sql';
|
|
2
|
+
import { beforeEach, describe, expect, test } from 'vitest';
|
|
3
3
|
import { runFileSystemTest } from '../tests/runFileSystemTest';
|
|
4
4
|
import { createDatabaseFileSystem, FileNodeMetaEntity } from './createDatabaseFileSystem';
|
|
5
5
|
import { loadTestDatabase } from './loadTestDatabase';
|
|
@@ -10,10 +10,10 @@ describe('DatabaseFileSystem', () => {
|
|
|
10
10
|
|
|
11
11
|
beforeEach(async () => {
|
|
12
12
|
const { em: entityManager } = await loadTestDatabase();
|
|
13
|
-
em = entityManager as any;
|
|
13
|
+
em = entityManager as any;
|
|
14
14
|
|
|
15
15
|
fs = createDatabaseFileSystem({
|
|
16
|
-
getEntityManager: () => em as any,
|
|
16
|
+
getEntityManager: () => (em as any).fork(),
|
|
17
17
|
});
|
|
18
18
|
|
|
19
19
|
// Setup initial state: ensure root directory exists and create /README.txt
|
|
@@ -31,9 +31,9 @@ describe('DatabaseFileSystem', () => {
|
|
|
31
31
|
btime: new Date(),
|
|
32
32
|
ctime: new Date(),
|
|
33
33
|
mtime: new Date(),
|
|
34
|
-
});
|
|
35
|
-
await em.
|
|
36
|
-
}, 30000);
|
|
34
|
+
} as any);
|
|
35
|
+
await em.persist(readmeFile).flush();
|
|
36
|
+
}, 30000);
|
|
37
37
|
|
|
38
38
|
test('common tests', async () => {
|
|
39
39
|
await runFileSystemTest(fs, {
|
|
@@ -43,5 +43,10 @@ describe('DatabaseFileSystem', () => {
|
|
|
43
43
|
writeStream: false,
|
|
44
44
|
abort: false,
|
|
45
45
|
});
|
|
46
|
-
}, 60000);
|
|
46
|
+
}, 60000);
|
|
47
|
+
|
|
48
|
+
test('root node exists', async () => {
|
|
49
|
+
const stat = await fs.stat('/');
|
|
50
|
+
expect(stat.kind).toBe('directory');
|
|
51
|
+
});
|
|
47
52
|
});
|
package/src/fs/server/index.ts
CHANGED