cabloy 5.1.97 → 5.1.99

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 (240) hide show
  1. package/.cabloy-version +1 -1
  2. package/.claude/hooks/contract-loop-gate.ts +151 -44
  3. package/CHANGELOG.md +48 -0
  4. package/CLAUDE.md +2 -0
  5. package/cabloy-docs/.vitepress/config.mjs +11 -0
  6. package/cabloy-docs/backend/controller-aop-guide.md +0 -1
  7. package/cabloy-docs/backend/image-guide.md +544 -0
  8. package/cabloy-docs/frontend/a-openapi-under-the-hood.md +1 -1
  9. package/cabloy-docs/frontend/generated-contract-consumption-entry-branch.md +1 -1
  10. package/cabloy-docs/frontend/generated-contract-consumption-list-branch.md +1 -1
  11. package/cabloy-docs/frontend/generated-contract-consumption-verify-playbook.md +1 -1
  12. package/cabloy-docs/frontend/image-guide.md +512 -0
  13. package/cabloy-docs/frontend/introduction.md +2 -0
  14. package/cabloy-docs/frontend/ioc-and-beans.md +34 -3
  15. package/cabloy-docs/frontend/model-architecture.md +2 -0
  16. package/cabloy-docs/frontend/model-resource-owner-pattern.md +3 -1
  17. package/cabloy-docs/frontend/model-state-guide.md +5 -1
  18. package/cabloy-docs/frontend/reading-zova-for-vue-developers.md +10 -8
  19. package/cabloy-docs/frontend/resource-entry-page-deep-dive.md +2 -2
  20. package/cabloy-docs/frontend/resource-list-page-deep-dive.md +1 -1
  21. package/cabloy-docs/frontend/rest-resource-source-reading-map.md +2 -2
  22. package/cabloy-docs/frontend/rest-resource-under-the-hood.md +4 -4
  23. package/cabloy-docs/frontend/server-data.md +2 -0
  24. package/cabloy-docs/frontend/state-architecture-for-vue-developers.md +218 -0
  25. package/cabloy-docs/frontend/system-startup-guide.md +2 -0
  26. package/cabloy-docs/frontend/use-state-data-best-practices.md +384 -0
  27. package/cabloy-docs/frontend/zova-vs-vue3-comparison.md +11 -10
  28. package/cabloy-docs/fullstack/image-workflow.md +403 -0
  29. package/cabloy-docs/fullstack/introduction.md +1 -0
  30. package/package.json +1 -1
  31. package/scripts/upgrade.ts +2 -0
  32. package/vona/package.original.json +0 -1
  33. package/vona/packages-cli/cli/package.json +1 -1
  34. package/vona/packages-cli/cli-set-api/package.json +1 -1
  35. package/vona/packages-vona/vona/package.json +1 -1
  36. package/vona/packages-vona/vona-core/package.json +1 -1
  37. package/vona/packages-vona/vona-core/src/lib/bean/beanBase.ts +9 -9
  38. package/vona/packages-vona/vona-mock/package.json +1 -1
  39. package/vona/pnpm-lock.yaml +424 -47
  40. package/vona/src/suite/a-training/modules/training-record/src/bean/fileScene.dossierFile.ts +22 -0
  41. package/vona/src/suite/a-training/modules/training-record/src/bean/meta.version.ts +1 -0
  42. package/vona/src/suite/a-training/modules/training-record/src/config/locale/en-us.ts +1 -0
  43. package/vona/src/suite/a-training/modules/training-record/src/config/locale/zh-cn.ts +1 -0
  44. package/vona/src/suite/a-training/modules/training-record/src/dto/recordDossierFileView.ts +47 -0
  45. package/vona/src/suite/a-training/modules/training-record/src/dto/recordSelectResItem.tsx +13 -0
  46. package/vona/src/suite/a-training/modules/training-record/src/dto/recordView.tsx +13 -0
  47. package/vona/src/suite/a-training/modules/training-record/src/entity/record.tsx +16 -5
  48. package/vona/src/suite/a-training/modules/training-record/src/index.ts +2 -0
  49. package/vona/src/suite/a-training/modules/training-record/src/lib/index.ts +1 -0
  50. package/vona/src/suite/a-training/modules/training-record/src/lib/resolveDossierFiles.ts +34 -0
  51. package/vona/src/suite/a-training/modules/training-record/test/record.test.ts +209 -123
  52. package/vona/src/suite/a-training/modules/training-student/src/config/locale/en-us.ts +1 -0
  53. package/vona/src/suite/a-training/modules/training-student/src/config/locale/zh-cn.ts +1 -0
  54. package/vona/src/suite/a-training/modules/training-student/src/dto/detailRecordBase.tsx +18 -1
  55. package/vona/src/suite/a-training/modules/training-student/src/entity/student.tsx +0 -2
  56. package/vona/src/suite/a-training/modules/training-student/src/model/student.ts +1 -0
  57. package/vona/src/suite/cabloy-basic/modules/basic-siteadmin/src/bean/ssrSite.admin.ts +4 -0
  58. package/vona/src/suite/cabloy-basic/modules/basic-siteweb/src/bean/ssrSite.web.ts +4 -0
  59. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/cli/ssrSite/boilerplate/{{sceneName}}.{{beanName}}.ts_ +3 -0
  60. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/package.json +1 -1
  61. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/service/ssrHandler.ts +11 -2
  62. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/types/ssrSite.ts +6 -0
  63. package/vona/src/suite-vendor/a-cabloy/package.json +1 -1
  64. package/vona/src/suite-vendor/a-file/modules/a-file/cli/fileProvider/boilerplate/{{sceneName}}.{{beanName}}.ts_ +23 -0
  65. package/vona/src/suite-vendor/a-file/modules/a-file/cli/fileScene/boilerplate/{{sceneName}}.{{beanName}}.ts_ +5 -0
  66. package/vona/src/suite-vendor/a-file/modules/a-file/package.json +68 -0
  67. package/vona/src/suite-vendor/a-file/modules/a-file/src/.metadata/index.ts +425 -0
  68. package/vona/src/suite-vendor/a-file/modules/a-file/src/.metadata/locales.ts +18 -0
  69. package/vona/src/suite-vendor/a-file/modules/a-file/src/.metadata/this.ts +2 -0
  70. package/vona/src/suite-vendor/a-file/modules/a-file/src/bean/bean.file.ts +322 -0
  71. package/vona/src/suite-vendor/a-file/modules/a-file/src/bean/bean.fileProvider.ts +62 -0
  72. package/vona/src/suite-vendor/a-file/modules/a-file/src/bean/bean.fileUploadPolicy.ts +224 -0
  73. package/vona/src/suite-vendor/a-file/modules/a-file/src/bean/meta.redlock.ts +11 -0
  74. package/vona/src/suite-vendor/a-file/modules/a-file/src/bean/meta.version.ts +38 -0
  75. package/vona/src/suite-vendor/a-file/modules/a-file/src/config/config.ts +16 -0
  76. package/vona/src/suite-vendor/a-file/modules/a-file/src/config/locale/en-us.ts +1 -0
  77. package/vona/src/suite-vendor/a-file/modules/a-file/src/config/locale/zh-cn.ts +1 -0
  78. package/vona/src/suite-vendor/a-file/modules/a-file/src/controller/file.ts +175 -0
  79. package/vona/src/suite-vendor/a-file/modules/a-file/src/dto/fileDirectUploadRequest.ts +33 -0
  80. package/vona/src/suite-vendor/a-file/modules/a-file/src/dto/fileDirectUploadResponse.ts +41 -0
  81. package/vona/src/suite-vendor/a-file/modules/a-file/src/dto/fileDownloadRequest.ts +12 -0
  82. package/vona/src/suite-vendor/a-file/modules/a-file/src/dto/fileUploadPolicyRequest.ts +15 -0
  83. package/vona/src/suite-vendor/a-file/modules/a-file/src/dto/fileUploadPolicyResponse.ts +30 -0
  84. package/vona/src/suite-vendor/a-file/modules/a-file/src/dto/fileUploadResponse.ts +59 -0
  85. package/vona/src/suite-vendor/a-file/modules/a-file/src/dto/fileUploadTokenRequest.ts +24 -0
  86. package/vona/src/suite-vendor/a-file/modules/a-file/src/dto/fileUploadTokenResponse.ts +16 -0
  87. package/vona/src/suite-vendor/a-file/modules/a-file/src/dto/fileUploadUrlRequest.ts +33 -0
  88. package/vona/src/suite-vendor/a-file/modules/a-file/src/entity/file.ts +55 -0
  89. package/vona/src/suite-vendor/a-file/modules/a-file/src/entity/fileProvider.ts +24 -0
  90. package/vona/src/suite-vendor/a-file/modules/a-file/src/index.ts +4 -0
  91. package/vona/src/suite-vendor/a-file/modules/a-file/src/lib/fileProvider.ts +7 -0
  92. package/vona/src/suite-vendor/a-file/modules/a-file/src/lib/fileScene.ts +7 -0
  93. package/vona/src/suite-vendor/a-file/modules/a-file/src/lib/fileUploadValidation.ts +15 -0
  94. package/vona/src/suite-vendor/a-file/modules/a-file/src/lib/index.ts +3 -0
  95. package/vona/src/suite-vendor/a-file/modules/a-file/src/model/file.ts +10 -0
  96. package/vona/src/suite-vendor/a-file/modules/a-file/src/model/fileProvider.ts +10 -0
  97. package/vona/src/suite-vendor/a-file/modules/a-file/src/types/file.ts +144 -0
  98. package/vona/src/suite-vendor/a-file/modules/a-file/src/types/fileProvider.ts +111 -0
  99. package/vona/src/suite-vendor/a-file/modules/a-file/src/types/fileScene.ts +53 -0
  100. package/vona/src/suite-vendor/a-file/modules/a-file/src/types/index.ts +3 -0
  101. package/vona/src/suite-vendor/a-file/modules/a-file/tsconfig.build.json +11 -0
  102. package/vona/src/suite-vendor/a-file/modules/a-file/tsconfig.json +7 -0
  103. package/vona/src/suite-vendor/a-file/modules/file-cloudflare/package.json +53 -0
  104. package/vona/src/suite-vendor/a-file/modules/file-cloudflare/src/.metadata/index.ts +104 -0
  105. package/vona/src/suite-vendor/a-file/modules/file-cloudflare/src/.metadata/this.ts +2 -0
  106. package/vona/src/suite-vendor/a-file/modules/file-cloudflare/src/bean/fileProvider.cloudflare.ts +123 -0
  107. package/vona/src/suite-vendor/a-file/modules/file-cloudflare/src/index.ts +1 -0
  108. package/vona/src/suite-vendor/a-file/modules/file-cloudflare/src/service/fileCloudflare.ts +245 -0
  109. package/vona/src/suite-vendor/a-file/modules/file-cloudflare/tsconfig.build.json +11 -0
  110. package/vona/src/suite-vendor/a-file/modules/file-cloudflare/tsconfig.json +7 -0
  111. package/vona/src/suite-vendor/a-file/modules/file-native/package.json +50 -0
  112. package/vona/src/suite-vendor/a-file/modules/file-native/src/.metadata/index.ts +111 -0
  113. package/vona/src/suite-vendor/a-file/modules/file-native/src/.metadata/this.ts +2 -0
  114. package/vona/src/suite-vendor/a-file/modules/file-native/src/bean/fileProvider.native.ts +125 -0
  115. package/vona/src/suite-vendor/a-file/modules/file-native/src/config/config.ts +7 -0
  116. package/vona/src/suite-vendor/a-file/modules/file-native/src/index.ts +1 -0
  117. package/vona/src/suite-vendor/a-file/modules/file-native/src/service/fileNative.ts +125 -0
  118. package/vona/src/suite-vendor/a-file/modules/file-native/tsconfig.build.json +11 -0
  119. package/vona/src/suite-vendor/a-file/modules/file-native/tsconfig.json +7 -0
  120. package/vona/src/suite-vendor/a-file/package.json +14 -0
  121. package/vona/src/suite-vendor/a-file/tsconfig.base.json +4 -0
  122. package/vona/src/suite-vendor/a-file/tsconfig.json +16 -0
  123. package/vona/src/suite-vendor/a-image/modules/a-image/package.json +4 -1
  124. package/vona/src/suite-vendor/a-image/modules/a-image/src/.metadata/index.ts +61 -3
  125. package/vona/src/suite-vendor/a-image/modules/a-image/src/bean/bean.image.ts +336 -152
  126. package/vona/src/suite-vendor/a-image/modules/a-image/src/bean/bean.imageProvider.ts +21 -14
  127. package/vona/src/suite-vendor/a-image/modules/a-image/src/bean/bean.imageUploadPolicy.ts +86 -11
  128. package/vona/src/suite-vendor/a-image/modules/a-image/src/bean/meta.index.ts +12 -0
  129. package/vona/src/suite-vendor/a-image/modules/a-image/src/bean/meta.redlock.ts +1 -1
  130. package/vona/src/suite-vendor/a-image/modules/a-image/src/bean/meta.version.ts +3 -0
  131. package/vona/src/suite-vendor/a-image/modules/a-image/src/bean/schedule.imageDraftPrune.ts +29 -0
  132. package/vona/src/suite-vendor/a-image/modules/a-image/src/config/config.ts +3 -0
  133. package/vona/src/suite-vendor/a-image/modules/a-image/src/controller/image.ts +32 -54
  134. package/vona/src/suite-vendor/a-image/modules/a-image/src/dto/imageDirectUploadFinalizeRequest.ts +13 -0
  135. package/vona/src/suite-vendor/a-image/modules/a-image/src/dto/imageDirectUploadFinalizeResponse.ts +10 -0
  136. package/vona/src/suite-vendor/a-image/modules/a-image/src/dto/imageDirectUploadResponse.ts +10 -0
  137. package/vona/src/suite-vendor/a-image/modules/a-image/src/dto/imageUploadPolicyRequest.ts +15 -0
  138. package/vona/src/suite-vendor/a-image/modules/a-image/src/dto/imageUploadPolicyResponse.ts +27 -0
  139. package/vona/src/suite-vendor/a-image/modules/a-image/src/dto/imageUploadResponse.ts +10 -1
  140. package/vona/src/suite-vendor/a-image/modules/a-image/src/dto/imageView.ts +10 -1
  141. package/vona/src/suite-vendor/a-image/modules/a-image/src/entity/image.ts +10 -1
  142. package/vona/src/suite-vendor/a-image/modules/a-image/src/lib/imageUploadValidation.ts +15 -0
  143. package/vona/src/suite-vendor/a-image/modules/a-image/src/lib/index.ts +1 -0
  144. package/vona/src/suite-vendor/a-image/modules/a-image/src/types/image.ts +18 -0
  145. package/vona/src/suite-vendor/a-image/modules/a-image/src/types/imageProvider.ts +28 -2
  146. package/vona/src/suite-vendor/a-image/modules/image-cloudflare/package.json +1 -1
  147. package/vona/src/suite-vendor/a-image/modules/image-cloudflare/src/bean/imageProvider.cloudflare.ts +13 -1
  148. package/vona/src/suite-vendor/a-image/modules/image-cloudflare/src/service/imageCloudflare.ts +26 -0
  149. package/vona/src/suite-vendor/a-image/modules/image-native/package.json +6 -2
  150. package/vona/src/suite-vendor/a-image/modules/image-native/src/.metadata/index.ts +37 -0
  151. package/vona/src/suite-vendor/a-image/modules/image-native/src/bean/imageProvider.native.ts +21 -1
  152. package/vona/src/suite-vendor/a-image/modules/image-native/src/controller/image.ts +57 -0
  153. package/vona/src/suite-vendor/a-image/modules/image-native/src/service/imageNative.ts +142 -34
  154. package/vona/src/suite-vendor/a-image/package.json +1 -1
  155. package/vona/src/suite-vendor/a-vona/modules/a-core/package.json +1 -1
  156. package/vona/src/suite-vendor/a-vona/modules/a-orm/package.json +1 -1
  157. package/vona/src/suite-vendor/a-vona/modules/a-orm/src/types/entity.ts +2 -4
  158. package/vona/src/suite-vendor/a-vona/modules/a-ormutils/package.json +1 -1
  159. package/vona/src/suite-vendor/a-vona/modules/a-ormutils/src/lib/columns.ts +7 -3
  160. package/vona/src/suite-vendor/a-vona/modules/a-permission/package.json +1 -1
  161. package/vona/src/suite-vendor/a-vona/modules/a-permission/src/.metadata/index.ts +24 -9
  162. package/vona/src/suite-vendor/a-vona/modules/a-permission/src/bean/bean.permission.ts +87 -73
  163. package/vona/src/suite-vendor/a-vona/modules/a-permission/src/bean/summerCache.permissionActionByRoles.ts +27 -0
  164. package/vona/src/suite-vendor/a-vona/modules/a-permission/src/bean/summerCache.permissionUser.ts +20 -0
  165. package/vona/src/suite-vendor/a-vona/modules/a-startup/package.json +1 -1
  166. package/vona/src/suite-vendor/a-vona/modules/a-startup/src/service/startup.ts +4 -3
  167. package/vona/src/suite-vendor/a-vona/modules/a-upload/package.json +1 -1
  168. package/vona/src/suite-vendor/a-vona/modules/a-upload/src/bean/interceptor.upload.ts +5 -1
  169. package/vona/src/suite-vendor/a-vona/modules/a-user/package.json +1 -1
  170. package/vona/src/suite-vendor/a-vona/modules/a-user/src/.metadata/index.ts +0 -13
  171. package/vona/src/suite-vendor/a-vona/modules/a-user/src/lib/passport.ts +0 -7
  172. package/vona/src/suite-vendor/a-vona/modules/a-version/package.json +1 -1
  173. package/vona/src/suite-vendor/a-vona/modules/a-version/src/bean/meta.version.ts +2 -24
  174. package/vona/src/suite-vendor/a-vona/package.json +1 -1
  175. package/zova/packages-utils/zova-jsx/package.json +2 -2
  176. package/zova/packages-zova/zova/package.json +3 -3
  177. package/zova/packages-zova/zova-core/package.json +1 -1
  178. package/zova/packages-zova/zova-core/src/bean/beanBase.ts +9 -13
  179. package/zova/pnpm-lock.yaml +418 -411
  180. package/zova/src/suite/a-demo/modules/demo-basic/src/page/toolOne/controller.tsx +2 -2
  181. package/zova/src/suite/a-demo/modules/demo-todo/src/page/todo/controller.tsx +2 -2
  182. package/zova/src/suite/a-home/modules/home-layoutadmin/src/component/layoutAdmin/controller.tsx +2 -2
  183. package/zova/src/suite/a-home/modules/home-layoutweb/src/component/layoutWeb/controller.tsx +2 -2
  184. package/zova/src/suite/a-home/modules/home-login/src/page/login/controller.tsx +2 -2
  185. package/zova/src/suite/cabloy-basic/modules/basic-details/src/component/blockDetails/controller.tsx +5 -1
  186. package/zova/src/suite/cabloy-basic/modules/basic-details/src/component/blockTable/controller.tsx +20 -15
  187. package/zova/src/suite/cabloy-basic/modules/basic-details/src/service/detail.tsx +6 -1
  188. package/zova/src/suite/cabloy-basic/modules/basic-file/cli/openapi.config.ts +9 -0
  189. package/zova/src/suite/cabloy-basic/modules/basic-file/package.json +58 -0
  190. package/zova/src/suite/cabloy-basic/modules/basic-file/src/.metadata/component/formFieldFile.ts +31 -0
  191. package/zova/src/suite/cabloy-basic/modules/basic-file/src/.metadata/index.ts +222 -0
  192. package/zova/src/suite/cabloy-basic/modules/basic-file/src/.metadata/locales.ts +7 -0
  193. package/zova/src/suite/cabloy-basic/modules/basic-file/src/.metadata/this.ts +2 -0
  194. package/zova/src/suite/cabloy-basic/modules/basic-file/src/api/file.ts +121 -0
  195. package/zova/src/suite/cabloy-basic/modules/basic-file/src/api/openapi/baseURL.ts +5 -0
  196. package/zova/src/suite/cabloy-basic/modules/basic-file/src/api/openapi/index.ts +3 -0
  197. package/zova/src/suite/cabloy-basic/modules/basic-file/src/api/openapi/schemas.ts +306 -0
  198. package/zova/src/suite/cabloy-basic/modules/basic-file/src/api/openapi/types.ts +5718 -0
  199. package/zova/src/suite/cabloy-basic/modules/basic-file/src/apiSchema/file.ts +40 -0
  200. package/zova/src/suite/cabloy-basic/modules/basic-file/src/bean/tableCell.file.tsx +187 -0
  201. package/zova/src/suite/cabloy-basic/modules/basic-file/src/component/formFieldFile/controller.tsx +534 -0
  202. package/zova/src/suite/cabloy-basic/modules/basic-file/src/config/locale/en-us.ts +15 -0
  203. package/zova/src/suite/cabloy-basic/modules/basic-file/src/config/locale/zh-cn.ts +15 -0
  204. package/zova/src/suite/cabloy-basic/modules/basic-file/src/index.ts +4 -0
  205. package/zova/src/suite/cabloy-basic/modules/basic-file/src/lib/file.ts +74 -0
  206. package/zova/src/suite/cabloy-basic/modules/basic-file/src/lib/index.ts +1 -0
  207. package/zova/src/suite/cabloy-basic/modules/basic-file/src/model/file.ts +23 -0
  208. package/zova/src/suite/cabloy-basic/modules/basic-file/src/types/file.ts +12 -0
  209. package/zova/src/suite/cabloy-basic/modules/basic-file/src/types/index.ts +1 -0
  210. package/zova/src/suite/cabloy-basic/modules/basic-file/tsconfig.build.json +13 -0
  211. package/zova/src/suite/cabloy-basic/modules/basic-file/tsconfig.json +5 -0
  212. package/zova/src/suite/cabloy-basic/modules/basic-image/src/.metadata/index.ts +61 -0
  213. package/zova/src/suite/cabloy-basic/modules/basic-image/src/api/image.ts +38 -1
  214. package/zova/src/suite/cabloy-basic/modules/basic-image/src/api/imageNativeImage.ts +31 -0
  215. package/zova/src/suite/cabloy-basic/modules/basic-image/src/api/openapi/schemas.ts +52 -0
  216. package/zova/src/suite/cabloy-basic/modules/basic-image/src/api/openapi/types.ts +614 -1
  217. package/zova/src/suite/cabloy-basic/modules/basic-image/src/apiSchema/image.ts +10 -0
  218. package/zova/src/suite/cabloy-basic/modules/basic-image/src/apiSchema/imageNativeImage.ts +13 -0
  219. package/zova/src/suite/cabloy-basic/modules/basic-image/src/component/formFieldImage/controller.tsx +126 -62
  220. package/zova/src/suite/cabloy-basic/modules/basic-image/src/model/image.ts +23 -0
  221. package/zova/src/suite/cabloy-basic/modules/basic-page/src/component/blockPage/controller.tsx +44 -4
  222. package/zova/src/suite/cabloy-basic/modules/basic-page/src/component/blockTable/controller.tsx +17 -12
  223. package/zova/src/suite/cabloy-basic/modules/basic-pageentry/src/component/blockPageEntry/controller.tsx +2 -2
  224. package/zova/src/suite/cabloy-basic/modules/basic-resource/src/component/formFieldResourcePicker/controller.tsx +2 -2
  225. package/zova/src/suite/cabloy-basic/modules/basic-table/src/component/table/controller.tsx +7 -2
  226. package/zova/src/suite/cabloy-basic/modules/basic-table/src/component/table/render.tsx +34 -33
  227. package/zova/src/suite/cabloy-basic/package.json +1 -0
  228. package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/package.json +1 -1
  229. package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/src/model/resource.ts +5 -3
  230. package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/src/page/entry/controller.tsx +2 -2
  231. package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/src/page/resource/controller.tsx +2 -2
  232. package/zova/src/suite-vendor/a-cabloy/package.json +2 -2
  233. package/zova/src/suite-vendor/a-zova/modules/a-model/package.json +1 -1
  234. package/zova/src/suite-vendor/a-zova/modules/a-model/src/lib/utils.ts +17 -17
  235. package/zova/src/suite-vendor/a-zova/modules/a-openapi/package.json +1 -1
  236. package/zova/src/suite-vendor/a-zova/modules/a-openapi/src/model/sdk.ts +8 -3
  237. package/zova/src/suite-vendor/a-zova/modules/a-zova/package.json +2 -2
  238. package/zova/src/suite-vendor/a-zova/package.json +4 -4
  239. package/vona/src/suite-vendor/a-vona/modules/a-permission/src/bean/summerCache.permission.ts +0 -17
  240. package/vona/src/suite-vendor/a-vona/modules/a-user/src/bean/guard.userName.ts +0 -35
@@ -0,0 +1,245 @@
1
+ import type {
2
+ EntityFile,
3
+ IFileDeliveryOptions,
4
+ IFileDirectUploadInput,
5
+ IFileProviderDirectUploadResource,
6
+ IFileProviderResource,
7
+ IFileUploadInput,
8
+ IFileUploadUrlInput,
9
+ } from 'vona-module-a-file';
10
+
11
+ import {
12
+ DeleteObjectCommand,
13
+ GetObjectCommand,
14
+ HeadObjectCommand,
15
+ PutObjectCommand,
16
+ S3Client,
17
+ } from '@aws-sdk/client-s3';
18
+ import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
19
+ import fse from 'fs-extra';
20
+ import path from 'node:path';
21
+ import { BeanBase, uuidv4 } from 'vona';
22
+ import { Service } from 'vona-module-a-bean';
23
+
24
+ import type { IFileProviderCloudflareClientOptions } from '../bean/fileProvider.cloudflare.ts';
25
+
26
+ interface IFileCloudflareClientOptionsNormalized extends IFileProviderCloudflareClientOptions {
27
+ endpoint: string;
28
+ region: string;
29
+ accessKeyId: string;
30
+ secretAccessKey: string;
31
+ bucket: string;
32
+ }
33
+
34
+ @Service()
35
+ export class ServiceFileCloudflare extends BeanBase {
36
+ async upload(
37
+ input: IFileUploadInput,
38
+ options: IFileProviderCloudflareClientOptions,
39
+ ): Promise<IFileProviderResource> {
40
+ const normalized = this._normalizeClientOptions(options);
41
+ const objectKey = input.objectKey ?? this._createObjectKey(input.filename);
42
+ const body = await fse.readFile(input.file);
43
+ const command = new PutObjectCommand({
44
+ Bucket: normalized.bucket,
45
+ Key: objectKey,
46
+ Body: body,
47
+ ContentType: input.contentType,
48
+ });
49
+ const result = await this._getClient(normalized).send(command);
50
+ const stat = await fse.stat(input.file);
51
+ return {
52
+ resourceId: uuidv4(),
53
+ bucket: normalized.bucket,
54
+ objectKey,
55
+ filename: input.filename ?? path.basename(objectKey),
56
+ contentType: input.contentType,
57
+ size: input.size ?? Number(stat.size),
58
+ etag: this._normalizeEtag(result.ETag),
59
+ deliveryBaseUrl: normalized.deliveryBaseUrl,
60
+ public: input.public ?? normalized.public,
61
+ meta: input.meta,
62
+ raw: result,
63
+ };
64
+ }
65
+
66
+ async uploadUrl(
67
+ input: IFileUploadUrlInput,
68
+ options: IFileProviderCloudflareClientOptions,
69
+ ): Promise<IFileProviderResource> {
70
+ const response = await fetch(input.url);
71
+ if (!response.ok) {
72
+ throw new Error(`Remote file fetch failed: ${response.status}`);
73
+ }
74
+ const tempDir = await this.app.util.getPublicPathPhysical('.temp-file-cloudflare', true);
75
+ const tempFile = path.join(
76
+ tempDir,
77
+ `${uuidv4()}${path.extname(input.filename ?? new URL(input.url).pathname)}`,
78
+ );
79
+ const arrayBuffer = await response.arrayBuffer();
80
+ await fse.writeFile(tempFile, Buffer.from(arrayBuffer));
81
+ try {
82
+ return await this.upload(
83
+ {
84
+ file: tempFile,
85
+ filename: input.filename ?? path.basename(new URL(input.url).pathname),
86
+ contentType: input.contentType ?? response.headers.get('content-type') ?? undefined,
87
+ size: input.size,
88
+ objectKey: input.objectKey,
89
+ public: input.public,
90
+ meta: input.meta,
91
+ },
92
+ options,
93
+ );
94
+ } finally {
95
+ await fse.remove(tempFile);
96
+ }
97
+ }
98
+
99
+ async createDirectUpload(
100
+ input: IFileDirectUploadInput,
101
+ options: IFileProviderCloudflareClientOptions,
102
+ ): Promise<IFileProviderDirectUploadResource> {
103
+ const normalized = this._normalizeClientOptions(options);
104
+ const objectKey = input.objectKey ?? this._createObjectKey(input.filename);
105
+ const command = new PutObjectCommand({
106
+ Bucket: normalized.bucket,
107
+ Key: objectKey,
108
+ ContentType: input.contentType,
109
+ });
110
+ const uploadUrl = await getSignedUrl(this._getClient(normalized), command, {
111
+ expiresIn: this._resolveExpiresIn(input.expiry, normalized.presignExpiresIn ?? 600),
112
+ });
113
+ return {
114
+ resourceId: uuidv4(),
115
+ bucket: normalized.bucket,
116
+ objectKey,
117
+ filename: input.filename ?? path.basename(objectKey),
118
+ contentType: input.contentType,
119
+ size: input.size,
120
+ deliveryBaseUrl: normalized.deliveryBaseUrl,
121
+ public: input.public ?? normalized.public,
122
+ meta: input.meta,
123
+ uploadUrl,
124
+ method: 'PUT',
125
+ headers: input.contentType ? { 'content-type': input.contentType } : undefined,
126
+ };
127
+ }
128
+
129
+ async remove(
130
+ file: Pick<EntityFile, 'bucket' | 'objectKey'>,
131
+ options: IFileProviderCloudflareClientOptions,
132
+ ) {
133
+ const normalized = this._normalizeClientOptions(options);
134
+ if (!file.objectKey) return;
135
+ await this._getClient(normalized).send(
136
+ new DeleteObjectCommand({
137
+ Bucket: file.bucket ?? normalized.bucket,
138
+ Key: file.objectKey,
139
+ }),
140
+ );
141
+ }
142
+
143
+ async getDownloadUrl(
144
+ file: Pick<EntityFile, 'bucket' | 'objectKey' | 'deliveryBaseUrl' | 'public'>,
145
+ options: IFileProviderCloudflareClientOptions,
146
+ deliveryOptions?: IFileDeliveryOptions,
147
+ ) {
148
+ const normalized = this._normalizeClientOptions(options);
149
+ const bucket = file.bucket ?? normalized.bucket;
150
+ const objectKey = file.objectKey;
151
+ if (!objectKey) {
152
+ throw new Error('File objectKey is required');
153
+ }
154
+ const isPublic = file.public ?? normalized.public;
155
+ const deliveryBaseUrl = file.deliveryBaseUrl ?? normalized.deliveryBaseUrl;
156
+ if (isPublic && deliveryBaseUrl) {
157
+ return this._buildPublicUrl(deliveryBaseUrl, objectKey);
158
+ }
159
+ const command = new GetObjectCommand({
160
+ Bucket: bucket,
161
+ Key: objectKey,
162
+ });
163
+ return await getSignedUrl(this._getClient(normalized), command, {
164
+ expiresIn: deliveryOptions?.expiresIn ?? normalized.presignExpiresIn ?? 600,
165
+ });
166
+ }
167
+
168
+ async getMetadata(
169
+ file: Pick<EntityFile, 'bucket' | 'objectKey'>,
170
+ options: IFileProviderCloudflareClientOptions,
171
+ ) {
172
+ const normalized = this._normalizeClientOptions(options);
173
+ if (!file.objectKey) return undefined;
174
+ return await this._getClient(normalized).send(
175
+ new HeadObjectCommand({
176
+ Bucket: file.bucket ?? normalized.bucket,
177
+ Key: file.objectKey,
178
+ }),
179
+ );
180
+ }
181
+
182
+ private _normalizeClientOptions(options: IFileProviderCloudflareClientOptions) {
183
+ const endpoint = options.endpoint;
184
+ const accessKeyId = options.accessKeyId;
185
+ const secretAccessKey = options.secretAccessKey;
186
+ const bucket = options.bucket;
187
+ if (!endpoint) throw new Error('Cloudflare R2 endpoint is required');
188
+ if (!accessKeyId) throw new Error('Cloudflare R2 accessKeyId is required');
189
+ if (!secretAccessKey) throw new Error('Cloudflare R2 secretAccessKey is required');
190
+ if (!bucket) throw new Error('Cloudflare R2 bucket is required');
191
+ return {
192
+ ...options,
193
+ endpoint: endpoint.replace(/\/$/, ''),
194
+ region: options.region ?? 'auto',
195
+ accessKeyId,
196
+ secretAccessKey,
197
+ bucket,
198
+ } as IFileCloudflareClientOptionsNormalized;
199
+ }
200
+
201
+ private _getClient(options: IFileCloudflareClientOptionsNormalized) {
202
+ return new S3Client({
203
+ region: options.region,
204
+ endpoint: options.endpoint,
205
+ forcePathStyle: options.forcePathStyle ?? true,
206
+ credentials: {
207
+ accessKeyId: options.accessKeyId,
208
+ secretAccessKey: options.secretAccessKey,
209
+ },
210
+ });
211
+ }
212
+
213
+ private _createObjectKey(filename?: string) {
214
+ const ext = path.extname(filename ?? '') || '.bin';
215
+ return `${uuidv4()}${ext}`;
216
+ }
217
+
218
+ private _normalizeEtag(etag?: string) {
219
+ return etag?.replace(/^"|"$/g, '');
220
+ }
221
+
222
+ private _resolveExpiresIn(expiry: IFileDirectUploadInput['expiry'], fallback: number) {
223
+ if (expiry === undefined) return fallback;
224
+ const expiresAt =
225
+ expiry instanceof Date
226
+ ? expiry.getTime()
227
+ : typeof expiry === 'string'
228
+ ? new Date(expiry).getTime()
229
+ : Number(expiry);
230
+ if (!Number.isFinite(expiresAt)) {
231
+ throw new TypeError(`Invalid direct upload expiry: ${String(expiry)}`);
232
+ }
233
+ const expiresIn = Math.max(1, Math.floor((expiresAt - Date.now()) / 1000));
234
+ return expiresIn;
235
+ }
236
+
237
+ private _buildPublicUrl(deliveryBaseUrl: string, objectKey: string) {
238
+ const base = deliveryBaseUrl.replace(/\/$/, '');
239
+ const key = objectKey
240
+ .split('/')
241
+ .map(segment => encodeURIComponent(segment))
242
+ .join('/');
243
+ return `${base}/${key}`;
244
+ }
245
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "extends": "../../tsconfig.base",
3
+ "compilerOptions": {
4
+ "rootDir": "src",
5
+ "declaration": true,
6
+ "declarationMap": true,
7
+ "emitDeclarationOnly": true,
8
+ "outDir": "dist"
9
+ },
10
+ "include": ["src", "src/.metadata/*.ts", "src/**/*.json"]
11
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "extends": "../../tsconfig.base",
3
+ "compilerOptions": {
4
+ "noEmit": true
5
+ },
6
+ "include": ["src", "src/.metadata/*.ts", "src/**/*.json", "test"]
7
+ }
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "vona-module-file-native",
3
+ "version": "5.0.3",
4
+ "description": "",
5
+ "keywords": [
6
+ "Vona Module"
7
+ ],
8
+ "author": "",
9
+ "files": [
10
+ "dist",
11
+ "src"
12
+ ],
13
+ "type": "module",
14
+ "exports": {
15
+ ".": {
16
+ "types": [
17
+ "./src/index.ts",
18
+ "./dist/index.d.ts"
19
+ ],
20
+ "import": "./src/index.ts",
21
+ "default": "./dist/index.js"
22
+ },
23
+ "./package.json": "./package.json"
24
+ },
25
+ "scripts": {
26
+ "clean": "rimraf dist tsconfig.build.tsbuildinfo",
27
+ "tsc:publish": "npm run clean && node ../../../../../packages-cli/cli/src/bin/vona.ts :bin:buildModule --sourcemap && tsc -p tsconfig.build.json",
28
+ "prepublishOnly": "npm run tsc:publish",
29
+ "prepack": "clean-package",
30
+ "postpack": "clean-package restore"
31
+ },
32
+ "dependencies": {},
33
+ "devDependencies": {
34
+ "clean-package": "^2.2.0",
35
+ "rimraf": "^6.1.3"
36
+ },
37
+ "clean-package": {
38
+ "indent": 2,
39
+ "remove": [
40
+ "clean-package",
41
+ "exports.\\..import"
42
+ ]
43
+ },
44
+ "title": "file-native",
45
+ "vonaModule": {
46
+ "dependencies": {
47
+ "a-file": "5.0.0"
48
+ }
49
+ }
50
+ }
@@ -0,0 +1,111 @@
1
+ // eslint-disable
2
+ /** service: begin */
3
+ export * from '../service/fileNative.ts';
4
+
5
+ import 'vona-module-a-bean';
6
+ declare module 'vona-module-a-bean' {
7
+
8
+ export interface IServiceRecord {
9
+ 'file-native:fileNative': never;
10
+ }
11
+
12
+
13
+ }
14
+ declare module 'vona-module-file-native' {
15
+
16
+ export interface ServiceFileNative {
17
+ /** @internal */
18
+ get scope(): ScopeModuleFileNative;
19
+ }
20
+
21
+ export interface ServiceFileNative {
22
+ get $beanFullName(): 'file-native.service.fileNative';
23
+ get $onionName(): 'file-native:fileNative';
24
+
25
+ }
26
+ }
27
+ /** service: end */
28
+ /** service: begin */
29
+ import type { ServiceFileNative } from '../service/fileNative.ts';
30
+ export interface IModuleService {
31
+ 'fileNative': ServiceFileNative;
32
+ }
33
+ /** service: end */
34
+ /** service: begin */
35
+
36
+ import 'vona';
37
+ declare module 'vona' {
38
+ export interface IBeanRecordGeneral {
39
+ 'file-native.service.fileNative': ServiceFileNative;
40
+ }
41
+ }
42
+ /** service: end */
43
+ /** fileProvider: begin */
44
+ export * from '../bean/fileProvider.native.ts';
45
+ import type { IFileProviderOptionsNative } from '../bean/fileProvider.native.ts';
46
+ import 'vona-module-a-file';
47
+ declare module 'vona-module-a-file' {
48
+
49
+ export interface IFileProviderRecord {
50
+ 'file-native:native': IFileProviderOptionsNative;
51
+ }
52
+
53
+
54
+ }
55
+ declare module 'vona-module-file-native' {
56
+
57
+ export interface FileProviderNative {
58
+ /** @internal */
59
+ get scope(): ScopeModuleFileNative;
60
+ }
61
+
62
+ export interface FileProviderNative {
63
+ get $beanFullName(): 'file-native.fileProvider.native';
64
+ get $onionName(): 'file-native:native';
65
+ get $onionOptions(): IFileProviderOptionsNative;
66
+ }
67
+ }
68
+ /** fileProvider: end */
69
+ /** fileProvider: begin */
70
+ import type { FileProviderNative } from '../bean/fileProvider.native.ts';
71
+ export interface IModuleFileProvider {
72
+ 'native': FileProviderNative;
73
+ }
74
+ /** fileProvider: end */
75
+ /** config: begin */
76
+ export * from '../config/config.ts';
77
+ import type { config } from '../config/config.ts';
78
+ /** config: end */
79
+ /** scope: begin */
80
+ import { BeanScopeBase, type BeanScopeUtil, type TypeModuleConfig } from 'vona';
81
+ import { Scope } from 'vona-module-a-bean';
82
+
83
+ @Scope()
84
+ export class ScopeModuleFileNative extends BeanScopeBase {}
85
+
86
+ export interface ScopeModuleFileNative {
87
+ util: BeanScopeUtil;
88
+ config: TypeModuleConfig<typeof config>;
89
+ service: IModuleService;
90
+ fileProvider: IModuleFileProvider;
91
+ }
92
+
93
+ import 'vona';
94
+ declare module 'vona' {
95
+ export interface IBeanScopeRecord {
96
+ 'file-native': ScopeModuleFileNative;
97
+ }
98
+
99
+ export interface IBeanScopeContainer {
100
+ fileNative: ScopeModuleFileNative;
101
+ }
102
+
103
+ export interface IBeanScopeConfig {
104
+ 'file-native': ReturnType<typeof config>;
105
+ }
106
+
107
+
108
+
109
+
110
+ }
111
+ /** scope: end */
@@ -0,0 +1,2 @@
1
+ export const __ThisModule__ = 'file-native';
2
+ export { ScopeModuleFileNative as ScopeModule } from './index.ts';
@@ -0,0 +1,125 @@
1
+ import type {
2
+ EntityFile,
3
+ IDecoratorFileProviderOptions,
4
+ IFileDeliveryOptions,
5
+ IFileDownloadResult,
6
+ IFileProviderClientOptions,
7
+ IFileProviderClientRecord,
8
+ IFileProviderExecute,
9
+ IFileProviderResource,
10
+ IFileUploadInput,
11
+ } from 'vona-module-a-file';
12
+
13
+ import fse from 'fs-extra';
14
+ import { BeanBase } from 'vona';
15
+ import { FileProvider } from 'vona-module-a-file';
16
+
17
+ export interface IFileProviderNativeClientRecord extends IFileProviderClientRecord {}
18
+
19
+ export interface IFileProviderNativeClientOptions extends IFileProviderClientOptions {
20
+ subdir?: string;
21
+ }
22
+
23
+ export interface IFileProviderOptionsNative extends IDecoratorFileProviderOptions<
24
+ IFileProviderNativeClientRecord,
25
+ IFileProviderNativeClientOptions
26
+ > {}
27
+
28
+ @FileProvider<IFileProviderOptionsNative>({
29
+ base: {
30
+ subdir: 'default',
31
+ signedDeliveryKind: 'proxy',
32
+ public: false,
33
+ },
34
+ })
35
+ export class FileProviderNative
36
+ extends BeanBase
37
+ implements IFileProviderExecute<IFileProviderNativeClientOptions, IFileProviderOptionsNative>
38
+ {
39
+ async upload(
40
+ input: IFileUploadInput,
41
+ clientOptions: IFileProviderNativeClientOptions,
42
+ _options: IFileProviderOptionsNative,
43
+ ): Promise<IFileProviderResource> {
44
+ const resource = await this.scope.service.fileNative.upload(input, clientOptions);
45
+ return {
46
+ ...resource,
47
+ filename: input.filename,
48
+ contentType: input.contentType,
49
+ meta: input.meta,
50
+ };
51
+ }
52
+
53
+ async get(
54
+ file: EntityFile,
55
+ clientOptions: IFileProviderNativeClientOptions,
56
+ _options: IFileProviderOptionsNative,
57
+ ) {
58
+ return {
59
+ resourceId: file.resourceId,
60
+ bucket: file.bucket,
61
+ objectKey: file.objectKey,
62
+ filename: file.filename,
63
+ contentType: file.contentType,
64
+ size: file.size,
65
+ etag: file.etag,
66
+ public: file.public ?? clientOptions.public,
67
+ meta: file.meta,
68
+ storagePath: file.storagePath,
69
+ deliveryBaseUrl: file.deliveryBaseUrl ?? clientOptions.deliveryBaseUrl,
70
+ };
71
+ }
72
+
73
+ async delete(
74
+ file: EntityFile,
75
+ _clientOptions: IFileProviderNativeClientOptions,
76
+ _options: IFileProviderOptionsNative,
77
+ ) {
78
+ await this.scope.service.fileNative.remove(file);
79
+ }
80
+
81
+ async getDownloadUrl(
82
+ file: EntityFile,
83
+ clientOptions: IFileProviderNativeClientOptions,
84
+ _options: IFileProviderOptionsNative,
85
+ deliveryOptions?: IFileDeliveryOptions,
86
+ ) {
87
+ return await this.scope.service.fileNative.getDownloadUrl(
88
+ file,
89
+ {
90
+ ...clientOptions,
91
+ subdir: clientOptions.subdir ?? 'default',
92
+ deliveryBaseUrl: file.deliveryBaseUrl ?? clientOptions.deliveryBaseUrl,
93
+ public: file.public ?? clientOptions.public,
94
+ },
95
+ deliveryOptions,
96
+ );
97
+ }
98
+
99
+ async download(
100
+ file: EntityFile,
101
+ clientOptions: IFileProviderNativeClientOptions,
102
+ options: IFileProviderOptionsNative,
103
+ deliveryOptions?: IFileDeliveryOptions,
104
+ ): Promise<IFileDownloadResult> {
105
+ if ((deliveryOptions?.responseMode ?? 'auto') !== 'url' && !deliveryOptions?.signed) {
106
+ const buffer = file.storagePath ? await fse.readFile(file.storagePath) : undefined;
107
+ if (buffer) {
108
+ return {
109
+ kind: 'buffer',
110
+ buffer,
111
+ filename: file.filename,
112
+ contentType: file.contentType,
113
+ signed: false,
114
+ };
115
+ }
116
+ }
117
+ return {
118
+ kind: 'url',
119
+ url: await this.getDownloadUrl(file, clientOptions, options, deliveryOptions),
120
+ filename: file.filename,
121
+ contentType: file.contentType,
122
+ signed: !!deliveryOptions?.signed,
123
+ };
124
+ }
125
+ }
@@ -0,0 +1,7 @@
1
+ import type { VonaApplication } from 'vona';
2
+
3
+ export function config(_app: VonaApplication) {
4
+ return {
5
+ fileNative: {},
6
+ };
7
+ }
@@ -0,0 +1 @@
1
+ export * from './.metadata/index.ts';
@@ -0,0 +1,125 @@
1
+ import type {
2
+ EntityFile,
3
+ IFileDeliveryOptions,
4
+ IFileProviderResource,
5
+ IFileUploadInput,
6
+ } from 'vona-module-a-file';
7
+
8
+ import fse from 'fs-extra';
9
+ import { createHash } from 'node:crypto';
10
+ import path from 'node:path';
11
+ import { BeanBase, getRuntimePathPhysicalRoot, uuidv4 } from 'vona';
12
+ import { Service } from 'vona-module-a-bean';
13
+
14
+ import type { IFileProviderNativeClientOptions } from '../bean/fileProvider.native.ts';
15
+
16
+ type IFileNativeStoredFile = Pick<
17
+ EntityFile,
18
+ 'id' | 'resourceId' | 'filename' | 'storagePath' | 'public' | 'objectKey' | 'deliveryBaseUrl'
19
+ >;
20
+
21
+ @Service()
22
+ export class ServiceFileNative extends BeanBase {
23
+ async upload(
24
+ input: IFileUploadInput,
25
+ options: IFileProviderNativeClientOptions,
26
+ ): Promise<IFileProviderResource> {
27
+ const resourceId = uuidv4();
28
+ const objectKey = this._getObjectKey(resourceId, input, options);
29
+ const targetPath = await this._getTargetPath(
30
+ objectKey,
31
+ input.public ?? options.public,
32
+ options,
33
+ );
34
+ await fse.ensureDir(path.dirname(targetPath));
35
+ await fse.copy(input.file, targetPath);
36
+ const stat = await fse.stat(targetPath);
37
+ return {
38
+ resourceId,
39
+ objectKey,
40
+ size: Number(stat.size),
41
+ etag: await this._calculateEtag(targetPath),
42
+ storagePath: targetPath,
43
+ deliveryBaseUrl: options.deliveryBaseUrl,
44
+ public: input.public ?? options.public,
45
+ };
46
+ }
47
+
48
+ async remove(file: IFileNativeStoredFile) {
49
+ if (!file.storagePath) return;
50
+ if (!(await fse.pathExists(file.storagePath))) return;
51
+ await fse.remove(file.storagePath);
52
+ }
53
+
54
+ async getDownloadUrl(
55
+ file: IFileNativeStoredFile,
56
+ options: IFileProviderNativeClientOptions,
57
+ _deliveryOptions?: IFileDeliveryOptions,
58
+ ) {
59
+ if (!file.storagePath) {
60
+ throw new Error(`File storage path missing: ${file.resourceId}`);
61
+ }
62
+ if (!(file.public ?? options.public)) {
63
+ const routePath = this.scope.util.combineApiPath(
64
+ `file/download/${file.id ?? file.resourceId}`,
65
+ false,
66
+ true,
67
+ );
68
+ return this.app.util.getAbsoluteUrlByApiPath(routePath);
69
+ }
70
+ return await this._buildUrl(file.storagePath, options.deliveryBaseUrl);
71
+ }
72
+
73
+ private async _getTargetPath(
74
+ objectKey: string,
75
+ isPublic: boolean | undefined,
76
+ options: IFileProviderNativeClientOptions,
77
+ ) {
78
+ if (isPublic) {
79
+ const publicPath = await this.app.util.getPublicPathPhysical(
80
+ path.join('file-native', options.subdir ?? 'default'),
81
+ true,
82
+ );
83
+ return path.join(publicPath, objectKey);
84
+ }
85
+ const runtimeRoot = getRuntimePathPhysicalRoot(this.app);
86
+ const basePath = path.join(
87
+ runtimeRoot,
88
+ this.ctx.instance.id.toString(),
89
+ 'file-native',
90
+ options.subdir ?? 'default',
91
+ );
92
+ await fse.ensureDir(basePath);
93
+ return path.join(basePath, objectKey);
94
+ }
95
+
96
+ private _getObjectKey(
97
+ resourceId: string,
98
+ input: IFileUploadInput,
99
+ options: IFileProviderNativeClientOptions,
100
+ ) {
101
+ if (input.objectKey) return input.objectKey.replace(/^\/+/, '');
102
+ const ext = path.extname(input.filename ?? '') || path.extname(input.file) || '.bin';
103
+ const prefix = options.subdir ? `${options.subdir.replace(/\/+$/, '')}/` : '';
104
+ return `${prefix}${resourceId}${ext}`;
105
+ }
106
+
107
+ private async _buildUrl(targetPath: string, deliveryBaseUrl?: string) {
108
+ const staticPath = await this._getStaticPath(targetPath);
109
+ if (deliveryBaseUrl) {
110
+ return `${deliveryBaseUrl.replace(/\/$/, '')}/${staticPath}`;
111
+ }
112
+ return this.app.util.combineStaticPath(`/${staticPath}`);
113
+ }
114
+
115
+ private async _getStaticPath(targetPath: string) {
116
+ const publicRoot = await this.app.util.getPublicPathPhysical(undefined, true);
117
+ const relativePath = path.relative(publicRoot, targetPath).split(path.sep).join(path.posix.sep);
118
+ return path.posix.join('public', this.ctx.instance.id.toString(), relativePath);
119
+ }
120
+
121
+ private async _calculateEtag(targetPath: string) {
122
+ const buffer = await fse.readFile(targetPath);
123
+ return createHash('md5').update(buffer).digest('hex');
124
+ }
125
+ }