@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.
Files changed (312) hide show
  1. package/lib/ai/qwen3vl/index.js +1 -1
  2. package/lib/ai/qwen3vl/utils.js +15 -15
  3. package/lib/ai/qwen3vl/utils.js.map +1 -1
  4. package/lib/ai/vision/DocLayoutElementTypeSchema.js +22 -22
  5. package/lib/ai/vision/ImageAnnotationSchema.js +63 -47
  6. package/lib/ai/vision/index.js +2 -2
  7. package/lib/ai/vision/resolveImageAnnotation.js +81 -95
  8. package/lib/cn/ChineseResidentIdNo.js +55 -41
  9. package/lib/cn/ChineseResidentIdNo.js.map +1 -1
  10. package/lib/cn/ChineseResidentIdNo.mod.js +6 -1
  11. package/lib/cn/ChineseResidentIdNo.test.js +22 -21
  12. package/lib/cn/DivisionCode.js +220 -235
  13. package/lib/cn/DivisionCode.mod.js +6 -1
  14. package/lib/cn/DivisionCode.test.js +92 -121
  15. package/lib/cn/Mod11.js +18 -37
  16. package/lib/cn/Mod11.js.map +1 -1
  17. package/lib/cn/Mod31.js +23 -41
  18. package/lib/cn/UnifiedSocialCreditCode.js +143 -137
  19. package/lib/cn/UnifiedSocialCreditCode.mod.js +6 -1
  20. package/lib/cn/UnifiedSocialCreditCode.test.js +21 -15
  21. package/lib/cn/formatChineseAmount.js +46 -71
  22. package/lib/cn/index.js +6 -6
  23. package/lib/cn/mod.js +5 -3
  24. package/lib/cn/parseChineseNumber.js +81 -85
  25. package/lib/cn/parseChineseNumber.test.js +183 -261
  26. package/lib/cn/pinyin/cartesianProduct.js +19 -19
  27. package/lib/cn/pinyin/cartesianProduct.test.js +78 -178
  28. package/lib/cn/pinyin/loader.js +13 -11
  29. package/lib/cn/pinyin/preload.js +2 -1
  30. package/lib/cn/pinyin/toPinyin.test.js +149 -161
  31. package/lib/cn/pinyin/toPinyinPure.js +28 -23
  32. package/lib/cn/pinyin/transform.js +11 -11
  33. package/lib/cn/types.d.js +2 -2
  34. package/lib/consola/createStandardConsolaReporter.js +14 -15
  35. package/lib/consola/formatLogObject.js +149 -133
  36. package/lib/consola/formatLogObject.js.map +1 -1
  37. package/lib/consola/formatLogObject.test.js +167 -178
  38. package/lib/consola/index.js +2 -2
  39. package/lib/data/formatSort.js +14 -12
  40. package/lib/data/formatSort.test.js +33 -33
  41. package/lib/data/index.js +3 -3
  42. package/lib/data/maybeNumber.js +23 -23
  43. package/lib/data/maybeNumber.js.map +1 -1
  44. package/lib/data/parseSort.js +75 -68
  45. package/lib/data/parseSort.test.js +196 -187
  46. package/lib/data/resolvePagination.js +38 -39
  47. package/lib/data/resolvePagination.test.js +228 -218
  48. package/lib/data/types.d.js +2 -2
  49. package/lib/data/types.d.js.map +1 -1
  50. package/lib/dayjs/dayjs.js +20 -20
  51. package/lib/dayjs/formatDuration.js +56 -56
  52. package/lib/dayjs/formatDuration.js.map +1 -1
  53. package/lib/dayjs/formatDuration.test.js +63 -77
  54. package/lib/dayjs/index.js +4 -4
  55. package/lib/dayjs/parseDuration.js +21 -26
  56. package/lib/dayjs/parseRelativeTime.js +65 -66
  57. package/lib/dayjs/parseRelativeTime.test.js +227 -243
  58. package/lib/dayjs/resolveRelativeTime.js +74 -144
  59. package/lib/dayjs/resolveRelativeTime.js.map +1 -1
  60. package/lib/dayjs/resolveRelativeTime.test.js +296 -307
  61. package/lib/decimal/index.js +1 -1
  62. package/lib/decimal/parseDecimal.js +12 -12
  63. package/lib/drain3/Drain.js +321 -0
  64. package/lib/drain3/Drain.js.map +1 -0
  65. package/lib/drain3/LogCluster.js +38 -0
  66. package/lib/drain3/LogCluster.js.map +1 -0
  67. package/lib/drain3/Node.js +39 -0
  68. package/lib/drain3/Node.js.map +1 -0
  69. package/lib/drain3/TemplateMiner.js +205 -0
  70. package/lib/drain3/TemplateMiner.js.map +1 -0
  71. package/lib/drain3/index.js +31 -0
  72. package/lib/drain3/index.js.map +1 -0
  73. package/lib/drain3/persistence/FilePersistence.js +24 -0
  74. package/lib/drain3/persistence/FilePersistence.js.map +1 -0
  75. package/lib/drain3/persistence/MemoryPersistence.js +18 -0
  76. package/lib/drain3/persistence/MemoryPersistence.js.map +1 -0
  77. package/lib/drain3/persistence/PersistenceHandler.js +5 -0
  78. package/lib/drain3/persistence/PersistenceHandler.js.map +1 -0
  79. package/lib/drain3/types.js +7 -0
  80. package/lib/drain3/types.js.map +1 -0
  81. package/lib/emittery/emitter.js +7 -7
  82. package/lib/emittery/index.js +1 -1
  83. package/lib/foundation/schema/SexType.js +15 -12
  84. package/lib/foundation/schema/index.js +1 -1
  85. package/lib/foundation/schema/parseSexType.js +15 -16
  86. package/lib/foundation/schema/types.js +8 -6
  87. package/lib/fs/FileSystemError.js +18 -18
  88. package/lib/fs/IFileSystem.d.js +2 -2
  89. package/lib/fs/IFileSystem.d.js.map +1 -1
  90. package/lib/fs/MemoryFileSystem.test.js +172 -181
  91. package/lib/fs/createBrowserFileSystem.js +222 -233
  92. package/lib/fs/createBrowserFileSystem.js.map +1 -1
  93. package/lib/fs/createMemoryFileSystem.js +473 -510
  94. package/lib/fs/createMemoryFileSystem.js.map +1 -1
  95. package/lib/fs/createSandboxFileSystem.js +102 -101
  96. package/lib/fs/createSandboxFileSystem.js.map +1 -1
  97. package/lib/fs/createWebDavFileSystem.js +162 -132
  98. package/lib/fs/createWebDavFileSystem.js.map +1 -1
  99. package/lib/fs/createWebFileSystem.js +202 -0
  100. package/lib/fs/createWebFileSystem.js.map +1 -0
  101. package/lib/fs/findMimeType.js +14 -14
  102. package/lib/fs/findMimeType.js.map +1 -1
  103. package/lib/fs/index.js +7 -7
  104. package/lib/fs/index.js.map +1 -1
  105. package/lib/fs/minio/createMinioFileSystem.js +977 -0
  106. package/lib/fs/minio/createMinioFileSystem.js.map +1 -0
  107. package/lib/fs/minio/index.js +2 -0
  108. package/lib/fs/minio/index.js.map +1 -0
  109. package/lib/fs/orpc/FileSystemContract.js +57 -57
  110. package/lib/fs/orpc/createContractClientFileSystem.js +88 -88
  111. package/lib/fs/orpc/createContractClientFileSystem.js.map +1 -1
  112. package/lib/fs/orpc/index.js +2 -2
  113. package/lib/fs/orpc/server/createFileSystemContractImpl.js +62 -60
  114. package/lib/fs/orpc/server/createFileSystemContractImpl.js.map +1 -1
  115. package/lib/fs/orpc/server/index.js +1 -1
  116. package/lib/fs/s3/createS3MiniFileSystem.js +756 -689
  117. package/lib/fs/s3/createS3MiniFileSystem.js.map +1 -1
  118. package/lib/fs/s3/index.js +1 -1
  119. package/lib/fs/s3/s3mini.test.js +524 -553
  120. package/lib/fs/scandir.js +56 -56
  121. package/lib/fs/server/createDatabaseFileSystem.js +834 -741
  122. package/lib/fs/server/createDatabaseFileSystem.js.map +1 -1
  123. package/lib/fs/server/createNodeFileSystem.js +407 -380
  124. package/lib/fs/server/createNodeFileSystem.js.map +1 -1
  125. package/lib/fs/server/dbfs.test.js +201 -214
  126. package/lib/fs/server/index.js +1 -1
  127. package/lib/fs/server/loadTestDatabase.js +40 -43
  128. package/lib/fs/tests/runFileSystemTest.js +352 -315
  129. package/lib/fs/tests/runFileSystemTest.js.map +1 -1
  130. package/lib/fs/types.js +17 -20
  131. package/lib/fs/utils/getFileUrl.js +24 -30
  132. package/lib/fs/utils.js +17 -17
  133. package/lib/fs/utils.js.map +1 -1
  134. package/lib/fs/webdav/index.js +2 -0
  135. package/lib/fs/webdav/index.js.map +1 -0
  136. package/lib/index.js +2 -2
  137. package/lib/jsonschema/JsonSchema.js +216 -155
  138. package/lib/jsonschema/JsonSchema.js.map +1 -1
  139. package/lib/jsonschema/JsonSchema.test.js +123 -124
  140. package/lib/jsonschema/forEachJsonSchema.js +41 -41
  141. package/lib/jsonschema/forEachJsonSchema.js.map +1 -1
  142. package/lib/jsonschema/index.js +2 -2
  143. package/lib/jsonschema/types.d.js +2 -2
  144. package/lib/jsonschema/types.d.js.map +1 -1
  145. package/lib/meta/defineFileType.js +32 -38
  146. package/lib/meta/defineInit.js +39 -35
  147. package/lib/meta/defineMetadata.js +37 -34
  148. package/lib/meta/defineMetadata.js.map +1 -1
  149. package/lib/meta/defineMetadata.test.js +13 -12
  150. package/lib/meta/index.js +3 -3
  151. package/lib/orpc/createOpenApiContractClient.js +26 -24
  152. package/lib/orpc/createOpenApiContractClient.js.map +1 -1
  153. package/lib/orpc/createRpcContractClient.js +37 -31
  154. package/lib/orpc/index.js +2 -2
  155. package/lib/orpc/resolveLinkPlugins.js +25 -25
  156. package/lib/password/PHC.js +187 -189
  157. package/lib/password/PHC.js.map +1 -1
  158. package/lib/password/PHC.test.js +517 -535
  159. package/lib/password/Password.js +85 -80
  160. package/lib/password/Password.test.js +330 -364
  161. package/lib/password/createArgon2PasswordAlgorithm.js +50 -51
  162. package/lib/password/createArgon2PasswordAlgorithm.js.map +1 -1
  163. package/lib/password/createBase64PasswordAlgorithm.js +11 -11
  164. package/lib/password/createBase64PasswordAlgorithm.js.map +1 -1
  165. package/lib/password/createBcryptPasswordAlgorithm.js +20 -18
  166. package/lib/password/createBcryptPasswordAlgorithm.js.map +1 -1
  167. package/lib/password/createPBKDF2PasswordAlgorithm.js +65 -52
  168. package/lib/password/createPBKDF2PasswordAlgorithm.js.map +1 -1
  169. package/lib/password/createScryptPasswordAlgorithm.js +74 -63
  170. package/lib/password/createScryptPasswordAlgorithm.js.map +1 -1
  171. package/lib/password/index.js +5 -5
  172. package/lib/password/server/index.js +1 -1
  173. package/lib/resource/Identifiable.js +2 -2
  174. package/lib/resource/ListQuery.js +42 -42
  175. package/lib/resource/ListQuery.js.map +1 -1
  176. package/lib/resource/getTitleOfResource.js +5 -5
  177. package/lib/resource/index.js +2 -2
  178. package/lib/resource/index.js.map +1 -1
  179. package/lib/resource/schema/AnyResourceSchema.js +91 -89
  180. package/lib/resource/schema/BaseResourceSchema.js +26 -26
  181. package/lib/resource/schema/ResourceActionType.js +117 -115
  182. package/lib/resource/schema/ResourceStatus.js +94 -92
  183. package/lib/resource/schema/ResourceType.js +25 -23
  184. package/lib/resource/schema/index.js +5 -5
  185. package/lib/resource/schema/types.js +86 -55
  186. package/lib/resource/schema/types.test.js +16 -13
  187. package/lib/s3/formatS3Url.js +60 -60
  188. package/lib/s3/formatS3Url.js.map +1 -1
  189. package/lib/s3/formatS3Url.test.js +238 -261
  190. package/lib/s3/index.js +2 -2
  191. package/lib/s3/parseS3Url.js +61 -60
  192. package/lib/s3/parseS3Url.js.map +1 -1
  193. package/lib/s3/parseS3Url.test.js +270 -269
  194. package/lib/schema/SchemaRegistry.js +41 -42
  195. package/lib/schema/SchemaRegistry.js.map +1 -1
  196. package/lib/schema/SchemaRegistry.mod.js +1 -1
  197. package/lib/schema/TypeSchema.d.js +2 -2
  198. package/lib/schema/TypeSchema.d.js.map +1 -1
  199. package/lib/schema/createSchemaData.js +113 -67
  200. package/lib/schema/createSchemaData.js.map +1 -1
  201. package/lib/schema/findJsonSchemaByPath.js +28 -23
  202. package/lib/schema/findJsonSchemaByPath.js.map +1 -1
  203. package/lib/schema/formatZodError.js +113 -134
  204. package/lib/schema/formatZodError.js.map +1 -1
  205. package/lib/schema/formatZodError.test.js +192 -195
  206. package/lib/schema/getSchemaCache.js +7 -7
  207. package/lib/schema/getSchemaOptions.js +17 -16
  208. package/lib/schema/index.js +6 -6
  209. package/lib/schema/toJsonSchema.js +196 -190
  210. package/lib/schema/toJsonSchema.js.map +1 -1
  211. package/lib/schema/toJsonSchema.test.js +34 -26
  212. package/lib/schema/validate.js +106 -97
  213. package/lib/schema/validate.js.map +1 -1
  214. package/lib/tools/generateSchema.js +40 -40
  215. package/lib/tools/renderJsonSchemaToMarkdownDoc.js +74 -74
  216. package/lib/utils/buildBaseUrl.js +8 -8
  217. package/lib/utils/buildRedactorFormSchema.js +55 -54
  218. package/lib/utils/buildRedactorFormSchema.js.map +1 -1
  219. package/lib/utils/getEstimateProcessTime.js +24 -19
  220. package/lib/utils/index.js +3 -3
  221. package/lib/utils/resolveFeatureOptions.js +9 -9
  222. package/package.json +37 -18
  223. package/src/ai/qwen3vl/utils.ts +1 -1
  224. package/src/ai/vision/index.ts +2 -2
  225. package/src/cn/ChineseResidentIdNo.ts +1 -1
  226. package/src/cn/Mod11.ts +1 -1
  227. package/src/cn/__snapshots__/ChineseResidentIdNo.test.ts.snap +1 -1
  228. package/src/cn/__snapshots__/UnifiedSocialCreditCode.test.ts.snap +0 -23
  229. package/src/cn/index.ts +1 -2
  230. package/src/cn/parseChineseNumber.test.ts +4 -4
  231. package/src/consola/formatLogObject.ts +6 -6
  232. package/src/consola/index.ts +1 -1
  233. package/src/data/index.ts +3 -4
  234. package/src/data/maybeNumber.ts +1 -1
  235. package/src/data/parseSort.test.ts +0 -1
  236. package/src/data/resolvePagination.ts +2 -2
  237. package/src/data/types.d.ts +2 -2
  238. package/src/dayjs/formatDuration.ts +10 -11
  239. package/src/dayjs/index.ts +1 -1
  240. package/src/dayjs/parseRelativeTime.ts +1 -1
  241. package/src/dayjs/resolveRelativeTime.ts +11 -14
  242. package/src/drain3/Drain.test.ts +378 -0
  243. package/src/drain3/Drain.ts +394 -0
  244. package/src/drain3/LogCluster.ts +46 -0
  245. package/src/drain3/Node.ts +53 -0
  246. package/src/drain3/TemplateMiner.ts +246 -0
  247. package/src/drain3/index.ts +34 -0
  248. package/src/drain3/persistence/FilePersistence.ts +24 -0
  249. package/src/drain3/persistence/MemoryPersistence.ts +23 -0
  250. package/src/drain3/persistence/PersistenceHandler.ts +19 -0
  251. package/src/drain3/types.ts +75 -0
  252. package/src/fs/IFileSystem.d.ts +1 -2
  253. package/src/fs/createBrowserFileSystem.ts +7 -5
  254. package/src/fs/createMemoryFileSystem.ts +9 -13
  255. package/src/fs/createSandboxFileSystem.ts +1 -1
  256. package/src/fs/createWebDavFileSystem.ts +30 -17
  257. package/src/fs/createWebFileSystem.ts +242 -0
  258. package/src/fs/findMimeType.ts +1 -4
  259. package/src/fs/index.ts +5 -5
  260. package/src/fs/minio/createMinioFileSystem.ts +1148 -0
  261. package/src/fs/minio/index.ts +1 -0
  262. package/src/fs/orpc/createContractClientFileSystem.ts +5 -5
  263. package/src/fs/orpc/server/createFileSystemContractImpl.ts +1 -1
  264. package/src/fs/s3/createS3MiniFileSystem.ts +120 -79
  265. package/src/fs/s3/s3fs.test.ts +441 -0
  266. package/src/fs/s3/s3mini.test.ts +2 -2
  267. package/src/fs/server/createDatabaseFileSystem.ts +78 -114
  268. package/src/fs/server/createNodeFileSystem.ts +32 -13
  269. package/src/fs/server/dbfs.test.ts +13 -8
  270. package/src/fs/server/index.ts +1 -0
  271. package/src/fs/server/loadTestDatabase.ts +8 -119
  272. package/src/fs/tests/runFileSystemTest.ts +29 -28
  273. package/src/fs/utils.ts +1 -1
  274. package/src/fs/webdav/index.ts +1 -0
  275. package/src/jsonschema/JsonSchema.ts +5 -5
  276. package/src/jsonschema/forEachJsonSchema.ts +1 -1
  277. package/src/jsonschema/index.ts +1 -1
  278. package/src/jsonschema/types.d.ts +1 -1
  279. package/src/meta/defineMetadata.ts +1 -1
  280. package/src/meta/index.ts +2 -3
  281. package/src/orm/createSqliteDialect.ts +17 -0
  282. package/src/orm/index.ts +1 -0
  283. package/src/orpc/createOpenApiContractClient.ts +3 -3
  284. package/src/orpc/index.ts +1 -1
  285. package/src/password/PHC.ts +3 -3
  286. package/src/password/createArgon2PasswordAlgorithm.ts +2 -2
  287. package/src/password/createBase64PasswordAlgorithm.ts +2 -2
  288. package/src/password/createBcryptPasswordAlgorithm.ts +4 -2
  289. package/src/password/createPBKDF2PasswordAlgorithm.ts +2 -2
  290. package/src/password/createScryptPasswordAlgorithm.ts +4 -4
  291. package/src/password/index.ts +2 -2
  292. package/src/resource/ListQuery.ts +4 -1
  293. package/src/resource/index.ts +3 -3
  294. package/src/resource/schema/index.ts +4 -4
  295. package/src/s3/formatS3Url.test.ts +1 -1
  296. package/src/s3/formatS3Url.ts +2 -2
  297. package/src/s3/index.ts +1 -1
  298. package/src/s3/parseS3Url.ts +1 -1
  299. package/src/schema/SchemaRegistry.ts +2 -2
  300. package/src/schema/TypeSchema.d.ts +6 -6
  301. package/src/schema/createSchemaData.ts +5 -5
  302. package/src/schema/findJsonSchemaByPath.ts +5 -5
  303. package/src/schema/formatZodError.test.ts +2 -1
  304. package/src/schema/formatZodError.ts +50 -62
  305. package/src/schema/index.ts +5 -5
  306. package/src/schema/toJsonSchema.ts +6 -6
  307. package/src/schema/validate.ts +2 -2
  308. package/src/utils/buildRedactorFormSchema.ts +4 -4
  309. package/src/utils/formatNumber.ts +18 -0
  310. package/src/utils/formatPercent.ts +17 -0
  311. package/src/utils/index.ts +3 -3
  312. package/src/utils/resolveFeatureOptions.ts +1 -1
@@ -1,750 +1,843 @@
1
- import { _ as _ts_decorate } from "@swc/helpers/_/_ts_decorate";
2
- import { _ as _ts_metadata } from "@swc/helpers/_/_ts_metadata";
3
- import * as crypto from "node:crypto";
4
- import { basename, dirname, join, normalize } from "node:path";
5
- import { Cascade, Collection, Entity, ManyToOne, OneToMany, OneToOne, Property, types, Unique } from "@mikro-orm/core";
6
- import { TenantBaseEntity } from "@wener/server/entity";
7
- import { getEntityManager } from "@wener/server/mikro-orm";
8
- import { FileSystemError, FileSystemErrorCode } from "../FileSystemError.js";
9
- import { FileKind } from "../types.js";
1
+ import * as crypto from 'node:crypto';
2
+ import { basename, dirname, join, normalize } from 'node:path';
3
+ import { Cascade, Collection, Entity, ManyToOne, OneToMany, OneToOne, Property, types, Unique } from '@mikro-orm/core';
4
+ import { _ as _ts_decorate } from '@swc/helpers/_/_ts_decorate';
5
+ import { _ as _ts_metadata } from '@swc/helpers/_/_ts_metadata';
6
+ import { TenantBaseEntity } from '@wener/server/entity';
7
+ import { getEntityManager } from '@wener/server/mikro-orm';
8
+ import { FileSystemError, FileSystemErrorCode } from '../FileSystemError.js';
9
+ import { FileKind } from '../types.js';
10
10
  export function createDatabaseFileSystem(options = {}) {
11
- return new DBFS(options);
11
+ return new DBFS(options);
12
12
  }
13
13
  let DBFS = class DBFS {
14
- options;
15
- constructor(options = {}) {
16
- this.options = {
17
- getEntityManager: () => getEntityManager().fork(),
18
- smallFileThreshold: 512,
19
- ...options
20
- };
21
- }
22
- get em() {
23
- return this.options.getEntityManager();
24
- }
25
- /**
26
- * Ensure root node exists, create it if it doesn't exist
27
- * @returns The root FileNodeMetaEntity
28
- */ async ensureRootNode() {
29
- const em = this.em;
30
- const rootQb = em.createQueryBuilder(FileNodeMetaEntity, "f");
31
- rootQb.where({
32
- parent: null
33
- });
34
- const rootNode = await rootQb.getSingleResult();
35
- if (rootNode) {
36
- return rootNode;
37
- }
38
- // Create root directory (parent is null, filename is empty string)
39
- const now = new Date();
40
- const rootDir = em.create(FileNodeMetaEntity, {
41
- filename: "",
42
- parent: null,
43
- kind: FileKind.directory,
44
- size: 0,
45
- atime: now,
46
- btime: now,
47
- ctime: now,
48
- mtime: now
49
- });
50
- try {
51
- await em.persistAndFlush(rootDir);
52
- return rootDir;
53
- }
54
- catch (error) {
55
- // If root already exists (race condition), fetch and return it
56
- if (error.message?.includes("UNIQUE constraint") || error.message?.includes("duplicate")) {
57
- const existingRootQb = em.createQueryBuilder(FileNodeMetaEntity, "f");
58
- existingRootQb.where({
59
- parent: null
60
- });
61
- const existingRoot = await existingRootQb.getSingleResult();
62
- if (existingRoot) {
63
- return existingRoot;
64
- }
65
- }
66
- throw error;
67
- }
68
- }
69
- async stat(path, options) {
70
- // Validate input
71
- if (!path || typeof path !== "string") {
72
- throw new FileSystemError("Invalid path", FileSystemErrorCode.EINVAL);
73
- }
74
- const em = this.em;
75
- const node = await this._getNodeByPath(path, em);
76
- if (!node) {
77
- throw new FileSystemError(`Path not found: ${path}`, FileSystemErrorCode.ENOENT);
78
- }
79
- return this._toFileStat(node, path);
80
- }
81
- async exists(path) {
82
- return !!await this._getNodeByPath(path, this.em);
83
- }
84
- async readdir(dir, options) {
85
- const em = this.em;
86
- const parentNode = await this._getNodeByPath(dir, em);
87
- if (!parentNode) {
88
- throw new FileSystemError(`Directory not found: ${dir}`, FileSystemErrorCode.ENOENT);
89
- }
90
- if (parentNode.kind !== FileKind.directory) {
91
- throw new FileSystemError(`Path is not a directory: ${dir}`, FileSystemErrorCode.ENOTDIR);
92
- }
93
- // Use QueryBuilder to avoid automatic relationship loading
94
- const qb = em.createQueryBuilder(FileNodeMetaEntity, "f");
95
- qb.where({
96
- parent: parentNode
97
- });
98
- const children = await qb.getResult();
99
- return children.map((child) => this._toFileStat(child, join(dir, child.filename)));
100
- }
101
- async mkdir(path, options = {}) {
102
- await this._mkdirInTransaction(path, options, this.em);
103
- }
104
- async _mkdirInTransaction(path, options, em) {
105
- const normalized = normalize(path);
106
- // Special handling for root directory
107
- if (normalized === "/") {
108
- const rootQb = em.createQueryBuilder(FileNodeMetaEntity, "f");
109
- rootQb.where({
110
- parent: null
111
- });
112
- const rootNode = await rootQb.getSingleResult();
113
- if (rootNode) {
114
- // Root directory already exists
115
- return;
116
- }
117
- // Create root directory (parent is null, filename is empty string)
118
- const now = new Date();
119
- const rootDir = em.create(FileNodeMetaEntity, {
120
- filename: "",
121
- parent: null,
122
- kind: FileKind.directory,
123
- size: 0,
124
- atime: now,
125
- btime: now,
126
- ctime: now,
127
- mtime: now
128
- });
129
- try {
130
- await em.persistAndFlush(rootDir);
131
- }
132
- catch (error) {
133
- // If root already exists (race condition), ignore the error
134
- if (!error.message?.includes("UNIQUE constraint") && !error.message?.includes("duplicate")) {
135
- throw error;
136
- }
137
- }
138
- return;
139
- }
140
- const parentPath = dirname(normalized);
141
- const newDirName = basename(normalized);
142
- if (!newDirName)
143
- throw new FileSystemError("Cannot create directory with empty name", FileSystemErrorCode.EINVAL);
144
- let parentNode = await this._getNodeByPath(parentPath, em);
145
- if (!parentNode) {
146
- if (options.recursive) {
147
- // 递归创建父目录
148
- await this._mkdirInTransaction(parentPath, options, em);
149
- parentNode = await this._getNodeByPath(parentPath, em);
150
- }
151
- else {
152
- throw new FileSystemError(`Parent directory not found: ${parentPath}`, FileSystemErrorCode.ENOENT);
153
- }
154
- }
155
- if (!parentNode)
156
- throw new FileSystemError("Failed to create parent directory structure", FileSystemErrorCode.EINVAL);
157
- // Check if directory already exists using QueryBuilder
158
- const existingQb = em.createQueryBuilder(FileNodeMetaEntity, "f");
159
- existingQb.where({
160
- parent: parentNode,
161
- filename: newDirName
162
- });
163
- const existing = await existingQb.getSingleResult();
164
- if (existing) {
165
- if (existing.kind === FileKind.directory) {
166
- // Directory already exists, return silently
167
- return;
168
- }
169
- throw new FileSystemError(`A file with the same name already exists: ${path}`, FileSystemErrorCode.EEXIST);
170
- }
171
- // Create directory using EntityManager
172
- const now = new Date();
173
- const newDir = em.create(FileNodeMetaEntity, {
174
- tid: parentNode.tid,
175
- parent: parentNode,
176
- filename: newDirName,
177
- kind: FileKind.directory,
178
- size: 0,
179
- atime: now,
180
- btime: now,
181
- ctime: now,
182
- mtime: now
183
- });
184
- await em.persistAndFlush(newDir);
185
- }
186
- async readFile(path, options) {
187
- const em = this.em;
188
- const node = await this._getNodeByPath(path, em);
189
- if (!node)
190
- throw new FileSystemError(`File not found: ${path}`, FileSystemErrorCode.ENOENT);
191
- if (node.kind !== FileKind.file)
192
- throw new FileSystemError(`Path is not a file: ${path}`, FileSystemErrorCode.EISDIR);
193
- let buffer;
194
- if (node.content) {
195
- // 小文件优化 - content is already loaded
196
- buffer = node.content;
197
- }
198
- else {
199
- // Large file: load from file_node_content table using QueryBuilder
200
- const fileContentQb = em.createQueryBuilder(FileNodeContentEntity, "fc");
201
- fileContentQb.where({
202
- node: node
203
- });
204
- const fileContent = await fileContentQb.getSingleResult();
205
- if (!fileContent)
206
- throw new FileSystemError("File content is missing", FileSystemErrorCode.ENOENT);
207
- buffer = fileContent.content;
208
- }
209
- return options?.encoding === "text" ? buffer.toString("utf-8") : buffer;
210
- }
211
- async writeFile(path, data, options = {}) {
212
- // Validate input
213
- if (!path || typeof path !== "string") {
214
- throw new FileSystemError("Invalid path", FileSystemErrorCode.EINVAL);
215
- }
216
- if (data === null || data === undefined) {
217
- throw new FileSystemError("Invalid data", FileSystemErrorCode.EINVAL);
218
- }
219
- await this.em.transactional(async (em) => {
220
- const { overwrite = true } = options;
221
- const bufferData = Buffer.isBuffer(data) ? data : Buffer.from(data, "utf-8");
222
- const size = bufferData.length;
223
- const parentPath = dirname(path);
224
- const filename = basename(path);
225
- // Validate filename
226
- if (!filename) {
227
- throw new FileSystemError("filename cannot be empty", FileSystemErrorCode.EINVAL);
228
- }
229
- // 确保父目录存在 - create it within the transaction
230
- await this._mkdirInTransaction(parentPath, {
231
- recursive: true
232
- }, em);
233
- const parentNode = await this._getNodeByPath(parentPath, em);
234
- if (!parentNode)
235
- throw new FileSystemError("Failed to establish parent directory", FileSystemErrorCode.EINVAL);
236
- // Find existing node using QueryBuilder
237
- const nodeQb = em.createQueryBuilder(FileNodeMetaEntity, "f");
238
- nodeQb.where({
239
- parent: parentNode,
240
- filename
241
- });
242
- let node = await nodeQb.getSingleResult();
243
- if (node) {
244
- // 文件已存在
245
- if (!overwrite)
246
- throw new FileSystemError(`File already exists: ${path}`, FileSystemErrorCode.EEXIST);
247
- if (node.kind === FileKind.directory)
248
- throw new FileSystemError(`Cannot overwrite a directory with a file: ${path}`, FileSystemErrorCode.EISDIR);
249
- // 更新节点
250
- node.size = size;
251
- node.mtime = new Date();
252
- // ... 其他时间戳
253
- }
254
- else {
255
- // 新建文件
256
- const now = new Date();
257
- node = em.create(FileNodeMetaEntity, {
258
- tid: parentNode.tid,
259
- filename,
260
- parent: parentNode,
261
- kind: FileKind.file,
262
- size,
263
- atime: now,
264
- btime: now,
265
- ctime: now,
266
- mtime: now
267
- });
268
- }
269
- // 处理文件内容
270
- if (size <= this.options.smallFileThreshold) {
271
- // Small file: store in file_node_meta.content
272
- node.content = bufferData;
273
- // If there was large file content, delete it
274
- // Use QueryBuilder to avoid relationship issues
275
- const existingContentQb = em.createQueryBuilder(FileNodeContentEntity, "fc");
276
- existingContentQb.where({
277
- node: node
278
- });
279
- const existingContent = await existingContentQb.getSingleResult();
280
- if (existingContent) {
281
- await em.removeAndFlush(existingContent);
282
- }
283
- }
284
- else {
285
- // Large file: store in file_node_content table
286
- node.content = undefined; // Clear small file content
287
- const md5 = crypto.createHash("md5").update(bufferData).digest("hex");
288
- const sha256 = crypto.createHash("sha256").update(bufferData).digest("hex");
289
- // Check if fileContent already exists using QueryBuilder
290
- const fileContentQb = em.createQueryBuilder(FileNodeContentEntity, "fc");
291
- fileContentQb.where({
292
- node: node
293
- });
294
- let fileContent = await fileContentQb.getSingleResult();
295
- if (fileContent) {
296
- // Update existing content
297
- fileContent.content = bufferData;
298
- fileContent.size = size;
299
- fileContent.md5 = md5;
300
- fileContent.sha256 = sha256;
301
- }
302
- else {
303
- // Create new content entity
304
- fileContent = em.create(FileNodeContentEntity, {
305
- node: node,
306
- tid: node.tid,
307
- content: bufferData,
308
- size: size,
309
- md5: md5,
310
- sha256: sha256
311
- });
312
- }
313
- }
314
- await em.persistAndFlush(node);
315
- });
316
- }
317
- async rm(path, options = {}) {
318
- await this.em.transactional(async (em) => {
319
- const node = await this._getNodeByPath(path, em);
320
- if (!node) {
321
- if (options.force)
322
- return; // force=true, 不存在也算成功
323
- throw new FileSystemError(`Path not found: ${path}`, FileSystemErrorCode.ENOENT);
324
- }
325
- if (node.kind === FileKind.directory && !options.recursive) {
326
- // Check if directory has children using QueryBuilder
327
- const childrenQb = em.createQueryBuilder(FileNodeMetaEntity, "f");
328
- childrenQb.where({
329
- parent: node
330
- });
331
- childrenQb.select("id");
332
- const children = await childrenQb.getResult();
333
- if (children.length > 0) {
334
- throw new FileSystemError(`Directory not empty: ${path}`, FileSystemErrorCode.ENOTEMPTY);
335
- }
336
- }
337
- // 使用 orphanRemoval: true, ORM会自动处理子节点和内容的删除
338
- await em.removeAndFlush(node);
339
- });
340
- }
341
- async rename(oldPath, newPath, options = {}) {
342
- await this.em.transactional(async (em) => {
343
- const node = await this._getNodeByPath(oldPath, em);
344
- if (!node)
345
- throw new FileSystemError(`Source path not found: ${oldPath}`, FileSystemErrorCode.ENOENT);
346
- const newParentPath = dirname(newPath);
347
- const newFilename = basename(newPath);
348
- const newParentNode = await this._getNodeByPath(newParentPath, em);
349
- if (!newParentNode)
350
- throw new FileSystemError(`Destination directory not found: ${newParentPath}`, FileSystemErrorCode.ENOENT);
351
- const existingDestQb = em.createQueryBuilder(FileNodeMetaEntity, "f");
352
- existingDestQb.where({
353
- parent: newParentNode,
354
- filename: newFilename
355
- });
356
- const existingDest = await existingDestQb.getSingleResult();
357
- if (existingDest) {
358
- if (!options.overwrite)
359
- throw new FileSystemError(`Destination path already exists: ${newPath}`, FileSystemErrorCode.EEXIST);
360
- if (node.id === existingDest.id)
361
- return; // 移动到原位置,什么都不做
362
- await em.removeAndFlush(existingDest);
363
- }
364
- node.parent = newParentNode;
365
- node.filename = newFilename;
366
- node.mtime = new Date();
367
- await em.flush();
368
- });
369
- }
370
- async copy(srcPath, destPath, options = {}) {
371
- await this.em.transactional(async (em) => {
372
- const srcNode = await this._getNodeByPath(srcPath, em);
373
- if (!srcNode)
374
- throw new FileSystemError(`Source path not found: ${srcPath}`, FileSystemErrorCode.ENOENT);
375
- const destParentPath = dirname(destPath);
376
- const destFilename = basename(destPath);
377
- const destParentNode = await this._getNodeByPath(destParentPath, em);
378
- if (!destParentNode)
379
- throw new FileSystemError(`Destination directory not found: ${destParentPath}`, FileSystemErrorCode.ENOENT);
380
- const existingDestQb = em.createQueryBuilder(FileNodeMetaEntity, "f");
381
- existingDestQb.where({
382
- parent: destParentNode,
383
- filename: destFilename
384
- });
385
- const existingDest = await existingDestQb.getSingleResult();
386
- if (existingDest) {
387
- if (!options.overwrite)
388
- throw new FileSystemError(`Destination path already exists: ${destPath}`, FileSystemErrorCode.EEXIST);
389
- // Delete existing destination within the same transaction
390
- await em.removeAndFlush(existingDest);
391
- }
392
- await this._copyNode(srcNode, destParentNode, destFilename, em);
393
- });
394
- }
395
- createReadStream(path, options) {
396
- throw new Error("Streaming read is not supported by DBFS yet.");
397
- }
398
- createWriteStream(path, options) {
399
- throw new Error("Streaming write is not supported by DBFS yet.");
400
- }
401
- createReadableStream(path, options) {
402
- throw new Error("ReadableStream is not supported by DBFS yet.");
403
- }
404
- createWritableStream(path, options) {
405
- throw new Error("WritableStream is not supported by DBFS yet.");
406
- }
407
- /**
408
- * 将路径字符串解析为数据库中的节点。这是大部分操作的基础。
409
- * @param pathStr 绝对路径, e.g., /home/user/file.txt
410
- * @param em EntityManager 实例
411
- * @returns 找到的节点或 null
412
- */ async _getNodeByPath(pathStr, em) {
413
- const normalized = normalize(pathStr);
414
- if (normalized === "/") {
415
- // Use QueryBuilder to avoid automatic relationship loading
416
- const qb = em.createQueryBuilder(FileNodeMetaEntity, "f");
417
- qb.where({
418
- parent: null
419
- });
420
- const rootNode = await qb.getSingleResult();
421
- return rootNode || null;
422
- }
423
- const parts = normalized.split("/").filter((p) => p);
424
- // Get root node
425
- const rootQb = em.createQueryBuilder(FileNodeMetaEntity, "f");
426
- rootQb.where({
427
- parent: null
428
- });
429
- let currentNode = await rootQb.getSingleResult();
430
- if (!currentNode)
431
- return null;
432
- // Traverse path parts
433
- for (const part of parts) {
434
- const childQb = em.createQueryBuilder(FileNodeMetaEntity, "f");
435
- childQb.where({
436
- parent: currentNode,
437
- filename: part
438
- });
439
- const child = await childQb.getSingleResult();
440
- if (!child)
441
- return null;
442
- currentNode = child;
443
- }
444
- return currentNode;
445
- }
446
- /**
447
- * 将数据库实体转换为 IFileStat 接口
448
- */ _toFileStat(node, path) {
449
- // Handle mtime - it might be a Date object or a string from raw query
450
- let mtime;
451
- if (node.mtime instanceof Date) {
452
- mtime = node.mtime.getTime();
453
- }
454
- else if (typeof node.mtime === "string") {
455
- mtime = new Date(node.mtime).getTime();
456
- }
457
- else {
458
- mtime = Date.now();
459
- }
460
- // Handle size - convert bigint to number if needed
461
- let size;
462
- if (typeof node.size === "bigint") {
463
- size = Number(node.size);
464
- }
465
- else {
466
- size = node.size;
467
- }
468
- return {
469
- path: path,
470
- name: node.filename,
471
- kind: node.kind,
472
- size: size,
473
- mtime: mtime,
474
- meta: node.metadata || {},
475
- // `directory` 字段可以根据 path 动态计算
476
- directory: dirname(path)
477
- };
478
- }
479
- async _copyNode(srcNode, destParent, newName, em) {
480
- // 1. 复制节点本身
481
- const now = new Date();
482
- const newNode = em.create(FileNodeMetaEntity, {
483
- tid: srcNode.tid,
484
- filename: newName,
485
- parent: destParent,
486
- kind: srcNode.kind,
487
- size: srcNode.size,
488
- metadata: srcNode.metadata,
489
- content: srcNode.content,
490
- atime: now,
491
- btime: now,
492
- ctime: now,
493
- mtime: now
494
- });
495
- // 2. 复制大文件内容 (如果存在) - use QueryBuilder to avoid relationship loading
496
- if (!srcNode.content) {
497
- const srcFileContentQb = em.createQueryBuilder(FileNodeContentEntity, "fc");
498
- srcFileContentQb.where({
499
- node: srcNode
500
- });
501
- const srcFileContent = await srcFileContentQb.getSingleResult();
502
- if (srcFileContent) {
503
- const newContent = em.create(FileNodeContentEntity, {
504
- node: newNode,
505
- tid: srcNode.tid,
506
- content: srcFileContent.content,
507
- size: srcFileContent.size,
508
- md5: srcFileContent.md5,
509
- sha256: srcFileContent.sha256,
510
- mimeType: srcFileContent.mimeType,
511
- metadata: srcFileContent.metadata
512
- });
513
- }
514
- }
515
- await em.persistAndFlush(newNode);
516
- // 3. 如果是目录,递归复制子节点
517
- if (srcNode.kind === FileKind.directory) {
518
- const childrenQb = em.createQueryBuilder(FileNodeMetaEntity, "f");
519
- childrenQb.where({
520
- parent: srcNode
521
- });
522
- const children = await childrenQb.getResult();
523
- for (const child of children) {
524
- await this._copyNode(child, newNode, child.filename, em);
525
- }
526
- }
527
- }
14
+ options;
15
+ constructor(options = {}) {
16
+ this.options = {
17
+ getEntityManager: () => getEntityManager().fork(),
18
+ smallFileThreshold: 512,
19
+ ...options,
20
+ };
21
+ }
22
+ get em() {
23
+ return this.options.getEntityManager();
24
+ }
25
+ /**
26
+ * Ensure root node exists, create it if it doesn't exist
27
+ * @returns The root FileNodeMetaEntity
28
+ */ async ensureRootNode() {
29
+ const em = this.em;
30
+ const rootQb = em.createQueryBuilder(FileNodeMetaEntity, 'f');
31
+ rootQb.where({
32
+ parent: null,
33
+ });
34
+ const rootNode = await rootQb.getSingleResult();
35
+ if (rootNode) {
36
+ return rootNode;
37
+ }
38
+ // Create root directory (parent is null, filename is empty string)
39
+ const now = new Date();
40
+ const rootDir = em.create(FileNodeMetaEntity, {
41
+ filename: '',
42
+ parent: null,
43
+ kind: FileKind.directory,
44
+ size: 0,
45
+ atime: now,
46
+ btime: now,
47
+ ctime: now,
48
+ mtime: now,
49
+ });
50
+ try {
51
+ await em.persistAndFlush(rootDir);
52
+ return rootDir;
53
+ } catch (error) {
54
+ // If root already exists (race condition), fetch and return it
55
+ if (error.message?.includes('UNIQUE constraint') || error.message?.includes('duplicate')) {
56
+ const existingRootQb = em.createQueryBuilder(FileNodeMetaEntity, 'f');
57
+ existingRootQb.where({
58
+ parent: null,
59
+ });
60
+ const existingRoot = await existingRootQb.getSingleResult();
61
+ if (existingRoot) {
62
+ return existingRoot;
63
+ }
64
+ }
65
+ throw error;
66
+ }
67
+ }
68
+ async stat(path, _options) {
69
+ // Validate input
70
+ if (!path || typeof path !== 'string') {
71
+ throw new FileSystemError('Invalid path', FileSystemErrorCode.EINVAL);
72
+ }
73
+ const em = this.em;
74
+ const node = await this._getNodeByPath(path, em);
75
+ if (!node) {
76
+ throw new FileSystemError(`Path not found: ${path}`, FileSystemErrorCode.ENOENT);
77
+ }
78
+ return this._toFileStat(node, path);
79
+ }
80
+ async exists(path) {
81
+ return !!(await this._getNodeByPath(path, this.em));
82
+ }
83
+ async readdir(dir, _options) {
84
+ const em = this.em;
85
+ const parentNode = await this._getNodeByPath(dir, em);
86
+ if (!parentNode) {
87
+ throw new FileSystemError(`Directory not found: ${dir}`, FileSystemErrorCode.ENOENT);
88
+ }
89
+ if (parentNode.kind !== FileKind.directory) {
90
+ throw new FileSystemError(`Path is not a directory: ${dir}`, FileSystemErrorCode.ENOTDIR);
91
+ }
92
+ // Use QueryBuilder to avoid automatic relationship loading
93
+ const qb = em.createQueryBuilder(FileNodeMetaEntity, 'f');
94
+ qb.where({
95
+ parent: parentNode,
96
+ });
97
+ const children = await qb.getResult();
98
+ return children.map((child) => this._toFileStat(child, join(dir, child.filename)));
99
+ }
100
+ async mkdir(path, options = {}) {
101
+ await this._mkdirInTransaction(path, options, this.em);
102
+ }
103
+ async _mkdirInTransaction(path, options, em) {
104
+ const normalized = normalize(path);
105
+ // Special handling for root directory
106
+ if (normalized === '/') {
107
+ const rootQb = em.createQueryBuilder(FileNodeMetaEntity, 'f');
108
+ rootQb.where({
109
+ parent: null,
110
+ });
111
+ const rootNode = await rootQb.getSingleResult();
112
+ if (rootNode) {
113
+ // Root directory already exists
114
+ return;
115
+ }
116
+ // Create root directory (parent is null, filename is empty string)
117
+ const now = new Date();
118
+ const rootDir = em.create(FileNodeMetaEntity, {
119
+ filename: '',
120
+ parent: null,
121
+ kind: FileKind.directory,
122
+ size: 0,
123
+ atime: now,
124
+ btime: now,
125
+ ctime: now,
126
+ mtime: now,
127
+ });
128
+ try {
129
+ await em.persistAndFlush(rootDir);
130
+ } catch (error) {
131
+ // If root already exists (race condition), ignore the error
132
+ if (!error.message?.includes('UNIQUE constraint') && !error.message?.includes('duplicate')) {
133
+ throw error;
134
+ }
135
+ }
136
+ return;
137
+ }
138
+ const parentPath = dirname(normalized);
139
+ const newDirName = basename(normalized);
140
+ if (!newDirName) throw new FileSystemError('Cannot create directory with empty name', FileSystemErrorCode.EINVAL);
141
+ let parentNode = await this._getNodeByPath(parentPath, em);
142
+ if (!parentNode) {
143
+ if (options.recursive) {
144
+ // 递归创建父目录
145
+ await this._mkdirInTransaction(parentPath, options, em);
146
+ parentNode = await this._getNodeByPath(parentPath, em);
147
+ } else {
148
+ throw new FileSystemError(`Parent directory not found: ${parentPath}`, FileSystemErrorCode.ENOENT);
149
+ }
150
+ }
151
+ if (!parentNode)
152
+ throw new FileSystemError('Failed to create parent directory structure', FileSystemErrorCode.EINVAL);
153
+ // Check if directory already exists using QueryBuilder
154
+ const existingQb = em.createQueryBuilder(FileNodeMetaEntity, 'f');
155
+ existingQb.where({
156
+ parent: parentNode,
157
+ filename: newDirName,
158
+ });
159
+ const existing = await existingQb.getSingleResult();
160
+ if (existing) {
161
+ if (existing.kind === FileKind.directory) {
162
+ // Directory already exists, return silently
163
+ return;
164
+ }
165
+ throw new FileSystemError(`A file with the same name already exists: ${path}`, FileSystemErrorCode.EEXIST);
166
+ }
167
+ // Create directory using EntityManager
168
+ const now = new Date();
169
+ const newDir = em.create(FileNodeMetaEntity, {
170
+ tid: parentNode.tid,
171
+ parent: parentNode,
172
+ filename: newDirName,
173
+ kind: FileKind.directory,
174
+ size: 0,
175
+ atime: now,
176
+ btime: now,
177
+ ctime: now,
178
+ mtime: now,
179
+ });
180
+ await em.persistAndFlush(newDir);
181
+ }
182
+ async readFile(path, options) {
183
+ const em = this.em;
184
+ const node = await this._getNodeByPath(path, em);
185
+ if (!node) throw new FileSystemError(`File not found: ${path}`, FileSystemErrorCode.ENOENT);
186
+ if (node.kind !== FileKind.file)
187
+ throw new FileSystemError(`Path is not a file: ${path}`, FileSystemErrorCode.EISDIR);
188
+ let buffer;
189
+ if (node.content) {
190
+ // 小文件优化 - content is already loaded
191
+ buffer = node.content;
192
+ } else {
193
+ // Large file: load from file_node_content table using QueryBuilder
194
+ const fileContentQb = em.createQueryBuilder(FileNodeContentEntity, 'fc');
195
+ fileContentQb.where({
196
+ node: node,
197
+ });
198
+ const fileContent = await fileContentQb.getSingleResult();
199
+ if (!fileContent) throw new FileSystemError('File content is missing', FileSystemErrorCode.ENOENT);
200
+ buffer = fileContent.content;
201
+ }
202
+ return options?.encoding === 'text' ? buffer.toString('utf-8') : buffer;
203
+ }
204
+ async writeFile(path, data, options = {}) {
205
+ // Validate input
206
+ if (!path || typeof path !== 'string') {
207
+ throw new FileSystemError('Invalid path', FileSystemErrorCode.EINVAL);
208
+ }
209
+ if (data === null || data === undefined) {
210
+ throw new FileSystemError('Invalid data', FileSystemErrorCode.EINVAL);
211
+ }
212
+ await this.em.transactional(async (em) => {
213
+ const { overwrite = true } = options;
214
+ const bufferData = Buffer.isBuffer(data) ? data : Buffer.from(data, 'utf-8');
215
+ const size = bufferData.length;
216
+ const parentPath = dirname(path);
217
+ const filename = basename(path);
218
+ // Validate filename
219
+ if (!filename) {
220
+ throw new FileSystemError('filename cannot be empty', FileSystemErrorCode.EINVAL);
221
+ }
222
+ // 确保父目录存在 - create it within the transaction
223
+ await this._mkdirInTransaction(
224
+ parentPath,
225
+ {
226
+ recursive: true,
227
+ },
228
+ em,
229
+ );
230
+ const parentNode = await this._getNodeByPath(parentPath, em);
231
+ if (!parentNode) throw new FileSystemError('Failed to establish parent directory', FileSystemErrorCode.EINVAL);
232
+ // Find existing node using QueryBuilder
233
+ const nodeQb = em.createQueryBuilder(FileNodeMetaEntity, 'f');
234
+ nodeQb.where({
235
+ parent: parentNode,
236
+ filename,
237
+ });
238
+ let node = await nodeQb.getSingleResult();
239
+ if (node) {
240
+ // 文件已存在
241
+ if (!overwrite) throw new FileSystemError(`File already exists: ${path}`, FileSystemErrorCode.EEXIST);
242
+ if (node.kind === FileKind.directory)
243
+ throw new FileSystemError(`Cannot overwrite a directory with a file: ${path}`, FileSystemErrorCode.EISDIR);
244
+ // 更新节点
245
+ node.size = size;
246
+ node.mtime = new Date();
247
+ // ... 其他时间戳
248
+ } else {
249
+ // 新建文件
250
+ const now = new Date();
251
+ node = em.create(FileNodeMetaEntity, {
252
+ tid: parentNode.tid,
253
+ filename,
254
+ parent: parentNode,
255
+ kind: FileKind.file,
256
+ size,
257
+ atime: now,
258
+ btime: now,
259
+ ctime: now,
260
+ mtime: now,
261
+ });
262
+ }
263
+ // 处理文件内容
264
+ if (size <= this.options.smallFileThreshold) {
265
+ // Small file: store in file_node_meta.content
266
+ node.content = bufferData;
267
+ // If there was large file content, delete it
268
+ // Use QueryBuilder to avoid relationship issues
269
+ const existingContentQb = em.createQueryBuilder(FileNodeContentEntity, 'fc');
270
+ existingContentQb.where({
271
+ node: node,
272
+ });
273
+ const existingContent = await existingContentQb.getSingleResult();
274
+ if (existingContent) {
275
+ await em.removeAndFlush(existingContent);
276
+ }
277
+ } else {
278
+ // Large file: store in file_node_content table
279
+ node.content = undefined; // Clear small file content
280
+ const md5 = crypto.createHash('md5').update(bufferData).digest('hex');
281
+ const sha256 = crypto.createHash('sha256').update(bufferData).digest('hex');
282
+ // Check if fileContent already exists using QueryBuilder
283
+ const fileContentQb = em.createQueryBuilder(FileNodeContentEntity, 'fc');
284
+ fileContentQb.where({
285
+ node: node,
286
+ });
287
+ let fileContent = await fileContentQb.getSingleResult();
288
+ if (fileContent) {
289
+ // Update existing content
290
+ fileContent.content = bufferData;
291
+ fileContent.size = size;
292
+ fileContent.md5 = md5;
293
+ fileContent.sha256 = sha256;
294
+ } else {
295
+ // Create new content entity
296
+ fileContent = em.create(FileNodeContentEntity, {
297
+ node: node,
298
+ tid: node.tid,
299
+ content: bufferData,
300
+ size: size,
301
+ md5: md5,
302
+ sha256: sha256,
303
+ });
304
+ }
305
+ }
306
+ await em.persistAndFlush(node);
307
+ });
308
+ }
309
+ async rm(path, options = {}) {
310
+ await this.em.transactional(async (em) => {
311
+ const node = await this._getNodeByPath(path, em);
312
+ if (!node) {
313
+ if (options.force) return; // force=true, 不存在也算成功
314
+ throw new FileSystemError(`Path not found: ${path}`, FileSystemErrorCode.ENOENT);
315
+ }
316
+ if (node.kind === FileKind.directory && !options.recursive) {
317
+ // Check if directory has children using QueryBuilder
318
+ const childrenQb = em.createQueryBuilder(FileNodeMetaEntity, 'f');
319
+ childrenQb.where({
320
+ parent: node,
321
+ });
322
+ childrenQb.select('id');
323
+ const children = await childrenQb.getResult();
324
+ if (children.length > 0) {
325
+ throw new FileSystemError(`Directory not empty: ${path}`, FileSystemErrorCode.ENOTEMPTY);
326
+ }
327
+ }
328
+ // 使用 orphanRemoval: true, ORM会自动处理子节点和内容的删除
329
+ await em.removeAndFlush(node);
330
+ });
331
+ }
332
+ async rename(oldPath, newPath, options = {}) {
333
+ await this.em.transactional(async (em) => {
334
+ const node = await this._getNodeByPath(oldPath, em);
335
+ if (!node) throw new FileSystemError(`Source path not found: ${oldPath}`, FileSystemErrorCode.ENOENT);
336
+ const newParentPath = dirname(newPath);
337
+ const newFilename = basename(newPath);
338
+ const newParentNode = await this._getNodeByPath(newParentPath, em);
339
+ if (!newParentNode)
340
+ throw new FileSystemError(`Destination directory not found: ${newParentPath}`, FileSystemErrorCode.ENOENT);
341
+ const existingDestQb = em.createQueryBuilder(FileNodeMetaEntity, 'f');
342
+ existingDestQb.where({
343
+ parent: newParentNode,
344
+ filename: newFilename,
345
+ });
346
+ const existingDest = await existingDestQb.getSingleResult();
347
+ if (existingDest) {
348
+ if (!options.overwrite)
349
+ throw new FileSystemError(`Destination path already exists: ${newPath}`, FileSystemErrorCode.EEXIST);
350
+ if (node.id === existingDest.id) return; // 移动到原位置,什么都不做
351
+ await em.removeAndFlush(existingDest);
352
+ }
353
+ node.parent = newParentNode;
354
+ node.filename = newFilename;
355
+ node.mtime = new Date();
356
+ await em.flush();
357
+ });
358
+ }
359
+ async copy(srcPath, destPath, options = {}) {
360
+ await this.em.transactional(async (em) => {
361
+ const srcNode = await this._getNodeByPath(srcPath, em);
362
+ if (!srcNode) throw new FileSystemError(`Source path not found: ${srcPath}`, FileSystemErrorCode.ENOENT);
363
+ const destParentPath = dirname(destPath);
364
+ const destFilename = basename(destPath);
365
+ const destParentNode = await this._getNodeByPath(destParentPath, em);
366
+ if (!destParentNode)
367
+ throw new FileSystemError(`Destination directory not found: ${destParentPath}`, FileSystemErrorCode.ENOENT);
368
+ const existingDestQb = em.createQueryBuilder(FileNodeMetaEntity, 'f');
369
+ existingDestQb.where({
370
+ parent: destParentNode,
371
+ filename: destFilename,
372
+ });
373
+ const existingDest = await existingDestQb.getSingleResult();
374
+ if (existingDest) {
375
+ if (!options.overwrite)
376
+ throw new FileSystemError(`Destination path already exists: ${destPath}`, FileSystemErrorCode.EEXIST);
377
+ // Delete existing destination within the same transaction
378
+ await em.removeAndFlush(existingDest);
379
+ }
380
+ await this._copyNode(srcNode, destParentNode, destFilename, em);
381
+ });
382
+ }
383
+ createReadStream(_path, _options) {
384
+ throw new Error('Streaming read is not supported by DBFS yet.');
385
+ }
386
+ createWriteStream(_path, _options) {
387
+ throw new Error('Streaming write is not supported by DBFS yet.');
388
+ }
389
+ createReadableStream(_path, _options) {
390
+ throw new Error('ReadableStream is not supported by DBFS yet.');
391
+ }
392
+ createWritableStream(_path, _options) {
393
+ throw new Error('WritableStream is not supported by DBFS yet.');
394
+ }
395
+ /**
396
+ * 将路径字符串解析为数据库中的节点。这是大部分操作的基础。
397
+ * @param pathStr 绝对路径, e.g., /home/user/file.txt
398
+ * @param em EntityManager 实例
399
+ * @returns 找到的节点或 null
400
+ */ async _getNodeByPath(pathStr, em) {
401
+ const normalized = normalize(pathStr);
402
+ if (normalized === '/') {
403
+ // Use QueryBuilder to avoid automatic relationship loading
404
+ const qb = em.createQueryBuilder(FileNodeMetaEntity, 'f');
405
+ qb.where({
406
+ parent: null,
407
+ });
408
+ const rootNode = await qb.getSingleResult();
409
+ return rootNode || null;
410
+ }
411
+ const parts = normalized.split('/').filter((p) => p);
412
+ // Get root node
413
+ const rootQb = em.createQueryBuilder(FileNodeMetaEntity, 'f');
414
+ rootQb.where({
415
+ parent: null,
416
+ });
417
+ let currentNode = await rootQb.getSingleResult();
418
+ if (!currentNode) return null;
419
+ // Traverse path parts
420
+ for (const part of parts) {
421
+ const childQb = em.createQueryBuilder(FileNodeMetaEntity, 'f');
422
+ childQb.where({
423
+ parent: currentNode,
424
+ filename: part,
425
+ });
426
+ const child = await childQb.getSingleResult();
427
+ if (!child) return null;
428
+ currentNode = child;
429
+ }
430
+ return currentNode;
431
+ }
432
+ /**
433
+ * 将数据库实体转换为 IFileStat 接口
434
+ */ _toFileStat(node, path) {
435
+ // Handle mtime - it might be a Date object or a string from raw query
436
+ let mtime;
437
+ if (node.mtime instanceof Date) {
438
+ mtime = node.mtime.getTime();
439
+ } else if (typeof node.mtime === 'string') {
440
+ mtime = new Date(node.mtime).getTime();
441
+ } else {
442
+ mtime = Date.now();
443
+ }
444
+ // Handle size - convert bigint to number if needed
445
+ let size;
446
+ if (typeof node.size === 'bigint') {
447
+ size = Number(node.size);
448
+ } else {
449
+ size = node.size;
450
+ }
451
+ return {
452
+ path: path,
453
+ name: node.filename,
454
+ kind: node.kind,
455
+ size: size,
456
+ mtime: mtime,
457
+ meta: node.metadata || {},
458
+ // `directory` 字段可以根据 path 动态计算
459
+ directory: dirname(path),
460
+ };
461
+ }
462
+ async _copyNode(srcNode, destParent, newName, em) {
463
+ // 1. 复制节点本身
464
+ const now = new Date();
465
+ const newNode = em.create(FileNodeMetaEntity, {
466
+ tid: srcNode.tid,
467
+ filename: newName,
468
+ parent: destParent,
469
+ kind: srcNode.kind,
470
+ size: srcNode.size,
471
+ metadata: srcNode.metadata,
472
+ content: srcNode.content,
473
+ atime: now,
474
+ btime: now,
475
+ ctime: now,
476
+ mtime: now,
477
+ });
478
+ // 2. 复制大文件内容 (如果存在) - use QueryBuilder to avoid relationship loading
479
+ if (!srcNode.content) {
480
+ const srcFileContentQb = em.createQueryBuilder(FileNodeContentEntity, 'fc');
481
+ srcFileContentQb.where({
482
+ node: srcNode,
483
+ });
484
+ const srcFileContent = await srcFileContentQb.getSingleResult();
485
+ if (srcFileContent) {
486
+ const _newContent = em.create(FileNodeContentEntity, {
487
+ node: newNode,
488
+ tid: srcNode.tid,
489
+ content: srcFileContent.content,
490
+ size: srcFileContent.size,
491
+ md5: srcFileContent.md5,
492
+ sha256: srcFileContent.sha256,
493
+ mimeType: srcFileContent.mimeType,
494
+ metadata: srcFileContent.metadata,
495
+ });
496
+ }
497
+ }
498
+ await em.persistAndFlush(newNode);
499
+ // 3. 如果是目录,递归复制子节点
500
+ if (srcNode.kind === FileKind.directory) {
501
+ const childrenQb = em.createQueryBuilder(FileNodeMetaEntity, 'f');
502
+ childrenQb.where({
503
+ parent: srcNode,
504
+ });
505
+ const children = await childrenQb.getResult();
506
+ for (const child of children) {
507
+ await this._copyNode(child, newNode, child.filename, em);
508
+ }
509
+ }
510
+ }
528
511
  };
529
512
  export class FileNodeMetaEntity extends TenantBaseEntity {
530
- filename;
531
- size;
532
- kind;
533
- atime;
534
- btime;
535
- ctime;
536
- mtime;
537
- metadata;
538
- parent;
539
- children = new Collection(this);
540
- fileContent;
541
- content;
542
- //region content
543
- get parentId() {
544
- return this.parent?.id;
545
- }
513
+ filename;
514
+ size;
515
+ kind;
516
+ atime;
517
+ btime;
518
+ ctime;
519
+ mtime;
520
+ metadata;
521
+ parent;
522
+ children = new Collection(this);
523
+ fileContent;
524
+ content;
525
+ //region content
526
+ get parentId() {
527
+ return this.parent?.id;
528
+ }
546
529
  }
547
- _ts_decorate([
548
- Property({
549
- type: types.string,
550
- nullable: false,
551
- comment: "\u6587\u4EF6\u540D"
552
- }),
553
- _ts_metadata("design:type", String)
554
- ], FileNodeMetaEntity.prototype, "filename", void 0);
555
- _ts_decorate([
556
- Property({
557
- type: types.bigint,
558
- nullable: false,
559
- default: 0,
560
- comment: "\u6587\u4EF6\u5927\u5C0F"
561
- }),
562
- _ts_metadata("design:type", Object)
563
- ], FileNodeMetaEntity.prototype, "size", void 0);
564
- _ts_decorate([
565
- Property({
566
- type: types.string,
567
- nullable: false,
568
- comment: "\u6587\u4EF6\u7C7B\u578B"
569
- }),
570
- _ts_metadata("design:type", typeof FileKind === "undefined" ? Object : FileKind)
571
- ], FileNodeMetaEntity.prototype, "kind", void 0);
572
- _ts_decorate([
573
- Property({
574
- type: types.datetime,
575
- nullable: false,
576
- defaultRaw: "CURRENT_TIMESTAMP"
577
- }),
578
- _ts_metadata("design:type", Object)
579
- ], FileNodeMetaEntity.prototype, "atime", void 0);
580
- _ts_decorate([
581
- Property({
582
- type: types.datetime,
583
- nullable: false,
584
- defaultRaw: "CURRENT_TIMESTAMP"
585
- }),
586
- _ts_metadata("design:type", Object)
587
- ], FileNodeMetaEntity.prototype, "btime", void 0);
588
- _ts_decorate([
589
- Property({
590
- type: types.datetime,
591
- nullable: false,
592
- defaultRaw: "CURRENT_TIMESTAMP"
593
- }),
594
- _ts_metadata("design:type", Object)
595
- ], FileNodeMetaEntity.prototype, "ctime", void 0);
596
- _ts_decorate([
597
- Property({
598
- type: types.datetime,
599
- nullable: false,
600
- defaultRaw: "CURRENT_TIMESTAMP"
601
- }),
602
- _ts_metadata("design:type", Object)
603
- ], FileNodeMetaEntity.prototype, "mtime", void 0);
604
- _ts_decorate([
605
- Property({
606
- type: types.json,
607
- nullable: false,
608
- defaultRaw: "{}"
609
- }),
610
- _ts_metadata("design:type", typeof Record === "undefined" ? Object : Record)
611
- ], FileNodeMetaEntity.prototype, "metadata", void 0);
612
- _ts_decorate([
613
- ManyToOne(() => FileNodeMetaEntity, {
614
- nullable: true,
615
- cascade: []
616
- }),
617
- _ts_metadata("design:type", typeof Rel === "undefined" ? Object : Rel)
618
- ], FileNodeMetaEntity.prototype, "parent", void 0);
619
- _ts_decorate([
620
- OneToMany({
621
- entity: () => FileNodeMetaEntity,
622
- mappedBy: "parent",
623
- orphanRemoval: true
624
- })
625
- ], FileNodeMetaEntity.prototype, "children", void 0);
626
- _ts_decorate([
627
- OneToOne({
628
- entity: () => FileNodeContentEntity,
629
- mappedBy: "node",
630
- orphanRemoval: true,
631
- nullable: true,
632
- cascade: [
633
- Cascade.ALL
634
- ]
635
- }),
636
- _ts_metadata("design:type", typeof Rel === "undefined" ? Object : Rel)
637
- ], FileNodeMetaEntity.prototype, "fileContent", void 0);
638
- _ts_decorate([
639
- Property({
640
- type: types.blob,
641
- nullable: true,
642
- comment: "\u6587\u4EF6\u5185\u5BB9"
643
- }),
644
- _ts_metadata("design:type", typeof Buffer === "undefined" ? Object : Buffer)
645
- ], FileNodeMetaEntity.prototype, "content", void 0);
646
- FileNodeMetaEntity = _ts_decorate([
647
- Entity({
648
- tableName: "file_node_meta"
649
- }),
650
- Unique({
651
- properties: [
652
- "tid",
653
- "parent",
654
- "filename"
655
- ]
656
- })
657
- ], FileNodeMetaEntity);
530
+ _ts_decorate(
531
+ [
532
+ Property({
533
+ type: types.string,
534
+ nullable: false,
535
+ comment: '\u6587\u4EF6\u540D',
536
+ }),
537
+ _ts_metadata('design:type', String),
538
+ ],
539
+ FileNodeMetaEntity.prototype,
540
+ 'filename',
541
+ void 0,
542
+ );
543
+ _ts_decorate(
544
+ [
545
+ Property({
546
+ type: types.bigint,
547
+ nullable: false,
548
+ default: 0,
549
+ comment: '\u6587\u4EF6\u5927\u5C0F',
550
+ }),
551
+ _ts_metadata('design:type', Object),
552
+ ],
553
+ FileNodeMetaEntity.prototype,
554
+ 'size',
555
+ void 0,
556
+ );
557
+ _ts_decorate(
558
+ [
559
+ Property({
560
+ type: types.string,
561
+ nullable: false,
562
+ comment: '\u6587\u4EF6\u7C7B\u578B',
563
+ }),
564
+ _ts_metadata('design:type', typeof FileKind === 'undefined' ? Object : FileKind),
565
+ ],
566
+ FileNodeMetaEntity.prototype,
567
+ 'kind',
568
+ void 0,
569
+ );
570
+ _ts_decorate(
571
+ [
572
+ Property({
573
+ type: types.datetime,
574
+ nullable: false,
575
+ defaultRaw: 'CURRENT_TIMESTAMP',
576
+ }),
577
+ _ts_metadata('design:type', Object),
578
+ ],
579
+ FileNodeMetaEntity.prototype,
580
+ 'atime',
581
+ void 0,
582
+ );
583
+ _ts_decorate(
584
+ [
585
+ Property({
586
+ type: types.datetime,
587
+ nullable: false,
588
+ defaultRaw: 'CURRENT_TIMESTAMP',
589
+ }),
590
+ _ts_metadata('design:type', Object),
591
+ ],
592
+ FileNodeMetaEntity.prototype,
593
+ 'btime',
594
+ void 0,
595
+ );
596
+ _ts_decorate(
597
+ [
598
+ Property({
599
+ type: types.datetime,
600
+ nullable: false,
601
+ defaultRaw: 'CURRENT_TIMESTAMP',
602
+ }),
603
+ _ts_metadata('design:type', Object),
604
+ ],
605
+ FileNodeMetaEntity.prototype,
606
+ 'ctime',
607
+ void 0,
608
+ );
609
+ _ts_decorate(
610
+ [
611
+ Property({
612
+ type: types.datetime,
613
+ nullable: false,
614
+ defaultRaw: 'CURRENT_TIMESTAMP',
615
+ }),
616
+ _ts_metadata('design:type', Object),
617
+ ],
618
+ FileNodeMetaEntity.prototype,
619
+ 'mtime',
620
+ void 0,
621
+ );
622
+ _ts_decorate(
623
+ [
624
+ Property({
625
+ type: types.json,
626
+ nullable: false,
627
+ defaultRaw: '{}',
628
+ }),
629
+ _ts_metadata('design:type', typeof Record === 'undefined' ? Object : Record),
630
+ ],
631
+ FileNodeMetaEntity.prototype,
632
+ 'metadata',
633
+ void 0,
634
+ );
635
+ _ts_decorate(
636
+ [
637
+ ManyToOne(() => FileNodeMetaEntity, {
638
+ nullable: true,
639
+ cascade: [],
640
+ }),
641
+ _ts_metadata('design:type', typeof Rel === 'undefined' ? Object : Rel),
642
+ ],
643
+ FileNodeMetaEntity.prototype,
644
+ 'parent',
645
+ void 0,
646
+ );
647
+ _ts_decorate(
648
+ [
649
+ OneToMany({
650
+ entity: () => FileNodeMetaEntity,
651
+ mappedBy: 'parent',
652
+ orphanRemoval: true,
653
+ }),
654
+ ],
655
+ FileNodeMetaEntity.prototype,
656
+ 'children',
657
+ void 0,
658
+ );
659
+ _ts_decorate(
660
+ [
661
+ OneToOne({
662
+ entity: () => FileNodeContentEntity,
663
+ mappedBy: 'node',
664
+ orphanRemoval: true,
665
+ nullable: true,
666
+ cascade: [Cascade.ALL],
667
+ }),
668
+ _ts_metadata('design:type', typeof Rel === 'undefined' ? Object : Rel),
669
+ ],
670
+ FileNodeMetaEntity.prototype,
671
+ 'fileContent',
672
+ void 0,
673
+ );
674
+ _ts_decorate(
675
+ [
676
+ Property({
677
+ type: types.blob,
678
+ nullable: true,
679
+ comment: '\u6587\u4EF6\u5185\u5BB9',
680
+ }),
681
+ _ts_metadata('design:type', typeof Buffer === 'undefined' ? Object : Buffer),
682
+ ],
683
+ FileNodeMetaEntity.prototype,
684
+ 'content',
685
+ void 0,
686
+ );
687
+ FileNodeMetaEntity = _ts_decorate(
688
+ [
689
+ Entity({
690
+ tableName: 'file_node_meta',
691
+ }),
692
+ Unique({
693
+ properties: ['tid', 'parent', 'filename'],
694
+ }),
695
+ ],
696
+ FileNodeMetaEntity,
697
+ );
658
698
  export class FileNodeContentEntity extends TenantBaseEntity {
659
- node;
660
- size;
661
- content;
662
- mimeType;
663
- md5;
664
- sha256;
665
- text;
666
- width;
667
- height;
668
- // @Property({ type: types.integer, nullable: true })
669
- // length?: number;
670
- metadata;
699
+ node;
700
+ size;
701
+ content;
702
+ mimeType;
703
+ md5;
704
+ sha256;
705
+ text;
706
+ width;
707
+ height;
708
+ // @Property({ type: types.integer, nullable: true })
709
+ // length?: number;
710
+ metadata;
671
711
  }
672
- _ts_decorate([
673
- OneToOne({
674
- entity: () => FileNodeMetaEntity,
675
- owner: true,
676
- joinColumn: "node_id"
677
- }),
678
- _ts_metadata("design:type", typeof Rel === "undefined" ? Object : Rel)
679
- ], FileNodeContentEntity.prototype, "node", void 0);
680
- _ts_decorate([
681
- Property({
682
- type: types.integer,
683
- nullable: false
684
- }),
685
- _ts_metadata("design:type", Number)
686
- ], FileNodeContentEntity.prototype, "size", void 0);
687
- _ts_decorate([
688
- Property({
689
- type: types.blob,
690
- lazy: true,
691
- comment: "\u6587\u4EF6\u5185\u5BB9"
692
- }),
693
- _ts_metadata("design:type", typeof Buffer === "undefined" ? Object : Buffer)
694
- ], FileNodeContentEntity.prototype, "content", void 0);
695
- _ts_decorate([
696
- Property({
697
- type: types.string,
698
- nullable: true
699
- }),
700
- _ts_metadata("design:type", String)
701
- ], FileNodeContentEntity.prototype, "mimeType", void 0);
702
- _ts_decorate([
703
- Property({
704
- type: types.string,
705
- nullable: false
706
- }),
707
- _ts_metadata("design:type", String)
708
- ], FileNodeContentEntity.prototype, "md5", void 0);
709
- _ts_decorate([
710
- Property({
711
- type: types.string,
712
- nullable: false
713
- }),
714
- _ts_metadata("design:type", String)
715
- ], FileNodeContentEntity.prototype, "sha256", void 0);
716
- _ts_decorate([
717
- Property({
718
- type: types.string,
719
- nullable: true
720
- }),
721
- _ts_metadata("design:type", String)
722
- ], FileNodeContentEntity.prototype, "text", void 0);
723
- _ts_decorate([
724
- Property({
725
- type: types.integer,
726
- nullable: true
727
- }),
728
- _ts_metadata("design:type", Number)
729
- ], FileNodeContentEntity.prototype, "width", void 0);
730
- _ts_decorate([
731
- Property({
732
- type: types.integer,
733
- nullable: true
734
- }),
735
- _ts_metadata("design:type", Number)
736
- ], FileNodeContentEntity.prototype, "height", void 0);
737
- _ts_decorate([
738
- Property({
739
- type: types.json,
740
- nullable: false,
741
- defaultRaw: "{}"
742
- }),
743
- _ts_metadata("design:type", typeof Record === "undefined" ? Object : Record)
744
- ], FileNodeContentEntity.prototype, "metadata", void 0);
745
- FileNodeContentEntity = _ts_decorate([
746
- Entity({
747
- tableName: "file_node_content"
748
- })
749
- ], FileNodeContentEntity);
712
+ _ts_decorate(
713
+ [
714
+ OneToOne({
715
+ entity: () => FileNodeMetaEntity,
716
+ owner: true,
717
+ joinColumn: 'node_id',
718
+ }),
719
+ _ts_metadata('design:type', typeof Rel === 'undefined' ? Object : Rel),
720
+ ],
721
+ FileNodeContentEntity.prototype,
722
+ 'node',
723
+ void 0,
724
+ );
725
+ _ts_decorate(
726
+ [
727
+ Property({
728
+ type: types.integer,
729
+ nullable: false,
730
+ }),
731
+ _ts_metadata('design:type', Number),
732
+ ],
733
+ FileNodeContentEntity.prototype,
734
+ 'size',
735
+ void 0,
736
+ );
737
+ _ts_decorate(
738
+ [
739
+ Property({
740
+ type: types.blob,
741
+ lazy: true,
742
+ comment: '\u6587\u4EF6\u5185\u5BB9',
743
+ }),
744
+ _ts_metadata('design:type', typeof Buffer === 'undefined' ? Object : Buffer),
745
+ ],
746
+ FileNodeContentEntity.prototype,
747
+ 'content',
748
+ void 0,
749
+ );
750
+ _ts_decorate(
751
+ [
752
+ Property({
753
+ type: types.string,
754
+ nullable: true,
755
+ }),
756
+ _ts_metadata('design:type', String),
757
+ ],
758
+ FileNodeContentEntity.prototype,
759
+ 'mimeType',
760
+ void 0,
761
+ );
762
+ _ts_decorate(
763
+ [
764
+ Property({
765
+ type: types.string,
766
+ nullable: false,
767
+ }),
768
+ _ts_metadata('design:type', String),
769
+ ],
770
+ FileNodeContentEntity.prototype,
771
+ 'md5',
772
+ void 0,
773
+ );
774
+ _ts_decorate(
775
+ [
776
+ Property({
777
+ type: types.string,
778
+ nullable: false,
779
+ }),
780
+ _ts_metadata('design:type', String),
781
+ ],
782
+ FileNodeContentEntity.prototype,
783
+ 'sha256',
784
+ void 0,
785
+ );
786
+ _ts_decorate(
787
+ [
788
+ Property({
789
+ type: types.string,
790
+ nullable: true,
791
+ }),
792
+ _ts_metadata('design:type', String),
793
+ ],
794
+ FileNodeContentEntity.prototype,
795
+ 'text',
796
+ void 0,
797
+ );
798
+ _ts_decorate(
799
+ [
800
+ Property({
801
+ type: types.integer,
802
+ nullable: true,
803
+ }),
804
+ _ts_metadata('design:type', Number),
805
+ ],
806
+ FileNodeContentEntity.prototype,
807
+ 'width',
808
+ void 0,
809
+ );
810
+ _ts_decorate(
811
+ [
812
+ Property({
813
+ type: types.integer,
814
+ nullable: true,
815
+ }),
816
+ _ts_metadata('design:type', Number),
817
+ ],
818
+ FileNodeContentEntity.prototype,
819
+ 'height',
820
+ void 0,
821
+ );
822
+ _ts_decorate(
823
+ [
824
+ Property({
825
+ type: types.json,
826
+ nullable: false,
827
+ defaultRaw: '{}',
828
+ }),
829
+ _ts_metadata('design:type', typeof Record === 'undefined' ? Object : Record),
830
+ ],
831
+ FileNodeContentEntity.prototype,
832
+ 'metadata',
833
+ void 0,
834
+ );
835
+ FileNodeContentEntity = _ts_decorate(
836
+ [
837
+ Entity({
838
+ tableName: 'file_node_content',
839
+ }),
840
+ ],
841
+ FileNodeContentEntity,
842
+ );
750
843
  //# sourceMappingURL=createDatabaseFileSystem.js.map