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
@@ -39,6 +39,8 @@ export type IImageNamedVariants = Partial<Record<TypeImageVariantName, IImageTra
39
39
 
40
40
  export type TypeImageDeliveryExpiry = Date | string | number;
41
41
 
42
+ export type TypeImageStatus = 'draft' | 'ready' | 'expired';
43
+
42
44
  export interface IImageDeliveryOptions {
43
45
  signed?: boolean;
44
46
  expiresIn?: number;
@@ -142,6 +144,9 @@ export interface IImageResource<
142
144
  provider: keyof IImageProviderRecord;
143
145
  clientName: string;
144
146
  imageScene?: keyof IImageSceneRecord | string;
147
+ status?: TypeImageStatus;
148
+ draftExpiresAt?: Date;
149
+ finalizedAt?: Date;
145
150
  uploadedAt?: Date;
146
151
  }
147
152
 
@@ -153,6 +158,11 @@ export interface IImageDirectUploadResult<
153
158
  draft?: boolean;
154
159
  }
155
160
 
161
+ export interface IImageFinalizeDirectUploadResult<
162
+ TMeta extends TypeImageMeta = TypeImageMeta,
163
+ TRaw = unknown,
164
+ > extends IImageResource<TMeta, TRaw> {}
165
+
156
166
  export interface IImageView {
157
167
  id: TableIdentity;
158
168
  url: string;
@@ -162,6 +172,9 @@ export interface IImageView {
162
172
  provider: keyof IImageProviderRecord;
163
173
  clientName: string;
164
174
  imageScene?: keyof IImageSceneRecord | string;
175
+ status?: TypeImageStatus;
176
+ draftExpiresAt?: Date;
177
+ finalizedAt?: Date;
165
178
  uploadedAt?: Date;
166
179
  variants?: IImageNamedVariants;
167
180
  signed?: boolean;
@@ -194,6 +207,11 @@ export interface IImageUploadTokenPayload<
194
207
  kind: 'imageUpload';
195
208
  }
196
209
 
210
+ export interface IImageDirectUploadTokenPayload {
211
+ kind: 'imageDirectUpload';
212
+ resourceId: string;
213
+ }
214
+
197
215
  export interface IImageDeliveryTokenPayload {
198
216
  kind: 'imageDelivery';
199
217
  imageId: TableIdentity;
@@ -37,16 +37,37 @@ export type TypeImageProviderClientOptions<T> =
37
37
  T extends IDecoratorImageProviderOptions<any, infer O> ? O : never;
38
38
 
39
39
  export type TypeImageProviderClientName<T> =
40
- T extends IDecoratorImageProviderOptions<infer R, any> ? keyof R : never;
40
+ T extends IDecoratorImageProviderOptions<infer R, any> ? keyof R & string : never;
41
41
 
42
42
  export interface IDecoratorImageProviderOptions<
43
43
  R extends IImageProviderClientRecord = IImageProviderClientRecord,
44
44
  T extends IImageProviderClientOptions = IImageProviderClientOptions,
45
45
  > extends TypeOnionOptionsEnableSimple {
46
46
  base?: T;
47
- clients?: { [K in keyof R]?: T };
47
+ clients?: { [K in keyof R]?: R[K] extends undefined ? T : R[K] };
48
48
  }
49
49
 
50
+ export type TypeImageProviderOptionsByName<N extends keyof IImageProviderRecord> =
51
+ IImageProviderRecord[N];
52
+
53
+ export type TypeImageProviderClientOptionsByName<N extends keyof IImageProviderRecord> =
54
+ TypeImageProviderOptionsByName<N> extends IDecoratorImageProviderOptions<any, any>
55
+ ? TypeImageProviderClientOptions<TypeImageProviderOptionsByName<N>>
56
+ : never;
57
+
58
+ export type TypeImageProviderClientNameByName<N extends keyof IImageProviderRecord> =
59
+ TypeImageProviderOptionsByName<N> extends IDecoratorImageProviderOptions<any, any>
60
+ ? TypeImageProviderClientName<TypeImageProviderOptionsByName<N>>
61
+ : never;
62
+
63
+ export type TypeImageProviderExecuteByName<N extends keyof IImageProviderRecord> =
64
+ TypeImageProviderOptionsByName<N> extends IDecoratorImageProviderOptions<any, any>
65
+ ? IImageProviderExecute<
66
+ TypeImageProviderClientOptionsByName<N>,
67
+ TypeImageProviderOptionsByName<N>
68
+ >
69
+ : never;
70
+
50
71
  // Provider-internal resolved variant names include both declaration-merged named variants
51
72
  // and the internal `custom` sentinel used for ad hoc transform requests.
52
73
  export type TypeImageProviderResolvedVariantName = TypeImageVariantName | 'custom';
@@ -71,6 +92,11 @@ export interface IImageProviderExecute<
71
92
  clientOptions: T,
72
93
  options: O,
73
94
  ): Promise<IImageProviderDirectUploadResource>;
95
+ finalizeDirectUpload?(
96
+ image: EntityImage,
97
+ clientOptions: T,
98
+ options: O,
99
+ ): Promise<IImageProviderResource | undefined>;
74
100
  get(
75
101
  image: EntityImage,
76
102
  clientOptions: T,
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-module-image-cloudflare",
3
- "version": "5.0.1",
3
+ "version": "5.0.4",
4
4
  "description": "",
5
5
  "keywords": [
6
6
  "Vona Module"
@@ -43,7 +43,11 @@ export interface IImageProviderOptionsCloudflare extends IDecoratorImageProvider
43
43
  },
44
44
  },
45
45
  })
46
- export class ImageProviderCloudflare extends BeanBase implements IImageProviderExecute {
46
+ export class ImageProviderCloudflare
47
+ extends BeanBase
48
+ implements
49
+ IImageProviderExecute<IImageProviderCloudflareClientOptions, IImageProviderOptionsCloudflare>
50
+ {
47
51
  async upload(
48
52
  input: IImageUploadInput,
49
53
  clientOptions: IImageProviderCloudflareClientOptions,
@@ -68,6 +72,14 @@ export class ImageProviderCloudflare extends BeanBase implements IImageProviderE
68
72
  return await this.scope.service.imageCloudflare.createDirectUpload(input, clientOptions);
69
73
  }
70
74
 
75
+ async finalizeDirectUpload(
76
+ image: EntityImage,
77
+ clientOptions: IImageProviderCloudflareClientOptions,
78
+ _options: IImageProviderOptionsCloudflare,
79
+ ) {
80
+ return await this.scope.service.imageCloudflare.finalizeDirectUpload(image, clientOptions);
81
+ }
82
+
71
83
  async get(
72
84
  image: EntityImage,
73
85
  clientOptions: IImageProviderCloudflareClientOptions,
@@ -38,6 +38,12 @@ interface ICloudflareImageResult {
38
38
  draft?: boolean;
39
39
  }
40
40
 
41
+ interface ICloudflareImageDetailsResult extends ICloudflareImageResult {
42
+ meta?: {
43
+ filename?: string;
44
+ };
45
+ }
46
+
41
47
  interface ICloudflareClientOptionsNormalized extends IImageProviderCloudflareClientOptions {
42
48
  accountId: string;
43
49
  apiToken: string;
@@ -130,6 +136,26 @@ export class ServiceImageCloudflare extends BeanBase {
130
136
  };
131
137
  }
132
138
 
139
+ async finalizeDirectUpload(
140
+ image: Pick<EntityImage, 'resourceId' | 'filename' | 'meta' | 'requireSignedURLs'>,
141
+ options: IImageProviderCloudflareClientOptions,
142
+ ): Promise<IImageProviderResource | undefined> {
143
+ const normalized = this._normalizeClientOptions(options);
144
+ const result = await this._request<ICloudflareImageDetailsResult>(
145
+ normalized,
146
+ `/images/v1/${encodeURIComponent(image.resourceId)}`,
147
+ {
148
+ method: 'GET',
149
+ },
150
+ );
151
+ if (result.draft) return undefined;
152
+ return this._mapImageResource(result, normalized, {
153
+ filename: result.filename ?? result.meta?.filename ?? image.filename,
154
+ meta: image.meta,
155
+ requireSignedURLs: image.requireSignedURLs,
156
+ });
157
+ }
158
+
133
159
  async remove(
134
160
  image: Pick<EntityImage, 'resourceId'>,
135
161
  options: IImageProviderCloudflareClientOptions,
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-module-image-native",
3
- "version": "5.0.1",
3
+ "version": "5.0.5",
4
4
  "description": "",
5
5
  "keywords": [
6
6
  "Vona Module"
@@ -46,7 +46,11 @@
46
46
  "title": "image-native",
47
47
  "vonaModule": {
48
48
  "dependencies": {
49
- "a-image": "5.0.0"
49
+ "a-core": "5.0.0",
50
+ "a-image": "5.0.0",
51
+ "a-upload": "5.0.0",
52
+ "a-user": "5.0.0",
53
+ "a-web": "5.0.0"
50
54
  },
51
55
  "globalDependencies": {
52
56
  "sharp": true
@@ -1,4 +1,5 @@
1
1
  // eslint-disable
2
+ import type { TypeControllerOptionsActions } from 'vona-module-a-openapi';
2
3
  /** service: begin */
3
4
  export * from '../service/imageNative.ts';
4
5
 
@@ -72,6 +73,42 @@ export interface IModuleImageProvider {
72
73
  'native': ImageProviderNative;
73
74
  }
74
75
  /** imageProvider: end */
76
+ /** controller: begin */
77
+ export * from '../controller/image.ts';
78
+ import type { IControllerOptionsImage } from '../controller/image.ts';
79
+ import 'vona-module-a-web';
80
+ declare module 'vona-module-a-web' {
81
+ export interface IControllerRecord {
82
+ 'image-native:image': IControllerOptionsImage;
83
+ }
84
+ }
85
+ declare module 'vona-module-image-native' {
86
+ export interface ControllerImage {
87
+ /** @internal */
88
+ get scope(): ScopeModuleImageNative;
89
+ }
90
+
91
+ export interface ControllerImage {
92
+ get $beanFullName(): 'image-native.controller.image';
93
+ get $onionName(): 'image-native:image';
94
+ get $onionOptions(): IControllerOptionsImage;
95
+ }
96
+ }
97
+ /** controller: end */
98
+ /** controller: begin */
99
+ // @ts-ignore ignore
100
+ import type { ControllerImage } from '../controller/image.ts';
101
+ declare module 'vona-module-image-native' {
102
+ export interface IControllerOptionsImage {
103
+ actions?: TypeControllerOptionsActions<ControllerImage>;
104
+ }
105
+ }
106
+ declare module 'vona-module-a-web' {
107
+ export interface IApiPathPostRecord {
108
+ '/image-native/direct-upload/:resourceId': undefined;
109
+ }
110
+ }
111
+ /** controller: end */
75
112
  /** config: begin */
76
113
  export * from '../config/config.ts';
77
114
  import type { config } from '../config/config.ts';
@@ -1,6 +1,7 @@
1
1
  import type {
2
2
  IDecoratorImageProviderOptions,
3
3
  IImageDeliveryOptions,
4
+ IImageDirectUploadInput,
4
5
  IImageDownloadResult,
5
6
  IImageProviderClientOptions,
6
7
  IImageProviderClientRecord,
@@ -34,7 +35,10 @@ export interface IImageProviderOptionsNative extends IDecoratorImageProviderOpti
34
35
  signedDeliveryKind: 'proxy',
35
36
  },
36
37
  })
37
- export class ImageProviderNative extends BeanBase implements IImageProviderExecute {
38
+ export class ImageProviderNative
39
+ extends BeanBase
40
+ implements IImageProviderExecute<IImageProviderNativeClientOptions, IImageProviderOptionsNative>
41
+ {
38
42
  async upload(
39
43
  input: IImageUploadInput,
40
44
  clientOptions: IImageProviderNativeClientOptions,
@@ -49,6 +53,22 @@ export class ImageProviderNative extends BeanBase implements IImageProviderExecu
49
53
  };
50
54
  }
51
55
 
56
+ async createDirectUpload(
57
+ input: IImageDirectUploadInput,
58
+ clientOptions: IImageProviderNativeClientOptions,
59
+ _options: IImageProviderOptionsNative,
60
+ ) {
61
+ return await this.scope.service.imageNative.createDirectUpload(input, clientOptions);
62
+ }
63
+
64
+ async finalizeDirectUpload(
65
+ image: EntityImage,
66
+ clientOptions: IImageProviderNativeClientOptions,
67
+ _options: IImageProviderOptionsNative,
68
+ ) {
69
+ return await this.scope.service.imageNative.finalizeDirectUpload(image, clientOptions);
70
+ }
71
+
52
72
  async get(
53
73
  image: EntityImage,
54
74
  clientOptions: IImageProviderNativeClientOptions,
@@ -0,0 +1,57 @@
1
+ import type { IUploadFile } from 'vona-module-a-upload';
2
+ import type { IDecoratorControllerOptions } from 'vona-module-a-web';
3
+
4
+ import fse from 'fs-extra';
5
+ import { BeanBase } from 'vona';
6
+ import { Core } from 'vona-module-a-core';
7
+ import { Passport } from 'vona-module-a-user';
8
+ import { Arg, Controller, Web } from 'vona-module-a-web';
9
+
10
+ export interface IControllerOptionsImage extends IDecoratorControllerOptions {}
11
+
12
+ @Controller<IControllerOptionsImage>({ path: 'image-native' })
13
+ export class ControllerImage extends BeanBase {
14
+ @Web.post('direct-upload/:resourceId')
15
+ @Passport.public()
16
+ @Core.fileUpload()
17
+ async directUpload(
18
+ @Arg.param('resourceId') resourceId: string,
19
+ @Arg.query('token') token: string,
20
+ @Arg.file('image') file: IUploadFile,
21
+ ) {
22
+ const payload = await this.bean.imageUploadPolicy.verifyDirectUploadToken(
23
+ token,
24
+ this.ctx.route.routePathRaw,
25
+ );
26
+ if (payload.resourceId !== resourceId) {
27
+ return this.app.throw(401);
28
+ }
29
+ const modelImage = this.app.bean._getBean<any>('a-image.model.image' as never);
30
+ const image = await modelImage.get({
31
+ providerName: 'image-native:native',
32
+ resourceId,
33
+ });
34
+ if (!image || image.status !== 'draft') {
35
+ return this.app.throw(403, `image is not draft: ${resourceId}`);
36
+ }
37
+ if (!image.imageScene) {
38
+ throw new Error(`image scene missing: ${resourceId}`);
39
+ }
40
+ const stat = await fse.stat(file.file);
41
+ const policy = await this.bean.imageUploadPolicy.resolveUploadPolicy({
42
+ imageScene: image.imageScene as never,
43
+ size: Number(stat.size),
44
+ mimeType: file.info.mimeType,
45
+ });
46
+ await this.bean.imageUploadPolicy.validateUploadFile(
47
+ {
48
+ file: file.file,
49
+ filename: file.info.filename,
50
+ mimeType: file.info.mimeType,
51
+ },
52
+ policy,
53
+ );
54
+ await this.scope.service.imageNative.uploadDirectFile(image, file);
55
+ return { ok: true };
56
+ }
57
+ }
@@ -2,13 +2,15 @@ import type { SharpConstructor } from 'sharp';
2
2
  import type {
3
3
  EntityImage,
4
4
  IImageDeliveryOptions,
5
- IImageNamedVariants,
5
+ IImageDirectUploadInput,
6
+ IImageProviderDirectUploadResource,
6
7
  IImageProviderResolvedVariant,
7
8
  IImageProviderResource,
8
9
  IImageTransformOptions,
9
10
  IImageUploadInput,
10
11
  IImageVariantRequest,
11
12
  } from 'vona-module-a-image';
13
+ import type { IUploadFile } from 'vona-module-a-upload';
12
14
 
13
15
  import fse from 'fs-extra';
14
16
  import { createHash } from 'node:crypto';
@@ -24,6 +26,20 @@ type IImageNativeStoredImage = Pick<
24
26
  'resourceId' | 'filename' | 'storagePath' | 'variants'
25
27
  >;
26
28
 
29
+ type IImageNativeDraftImage = Pick<
30
+ EntityImage,
31
+ | 'resourceId'
32
+ | 'filename'
33
+ | 'contentType'
34
+ | 'meta'
35
+ | 'imageScene'
36
+ | 'requireSignedURLs'
37
+ | 'storagePath'
38
+ | 'deliveryBaseUrl'
39
+ | 'variants'
40
+ | 'clientName'
41
+ >;
42
+
27
43
  @Service()
28
44
  export class ServiceImageNative extends BeanBase {
29
45
  private _sharpInstance: SharpConstructor;
@@ -39,39 +55,90 @@ export class ServiceImageNative extends BeanBase {
39
55
  input: IImageUploadInput,
40
56
  options: IImageProviderNativeClientOptions,
41
57
  ): Promise<IImageProviderResource> {
42
- const publicPath = await this.app.util.getPublicPathPhysical(
43
- path.join('image-native', options.subdir ?? 'default'),
44
- true,
45
- );
46
58
  const resourceId = uuidv4();
47
- const ext = this._getOriginalExt(input.filename, input.file);
48
- const targetName = `${resourceId}${ext}`;
49
- const targetPath = path.join(publicPath, targetName);
59
+ const targetPath = await this._getFinalPath(resourceId, input.filename, options, input.file);
50
60
  await fse.copy(input.file, targetPath);
51
- const sharp = await this.getSharp();
52
- const metadata = await sharp(targetPath).metadata();
53
- const stat = await fse.stat(targetPath);
54
- const variants = options.variants ?? this.scope.config.imageNative.variants;
55
- await this._generateNamedVariants(
56
- { resourceId, filename: input.filename, storagePath: targetPath, variants },
57
- options,
61
+ return await this._buildStoredResource(targetPath, {
62
+ resourceId,
63
+ filename: input.filename,
64
+ contentType: input.contentType,
65
+ meta: input.meta,
66
+ requireSignedURLs: input.requireSignedURLs ?? options.requireSignedURLs,
67
+ variants: options.variants ?? this.scope.config.imageNative.variants,
68
+ deliveryBaseUrl: options.deliveryBaseUrl,
69
+ });
70
+ }
71
+
72
+ async createDirectUpload(
73
+ input: IImageDirectUploadInput,
74
+ options: IImageProviderNativeClientOptions,
75
+ ): Promise<IImageProviderDirectUploadResource> {
76
+ const resourceId = this._sanitizeResourceId(input.customId) ?? uuidv4();
77
+ const storagePath = await this._getFinalPath(resourceId, input.filename, options);
78
+ const routePath = this.scope.util.combineApiPath(
79
+ `image-native/direct-upload/${encodeURIComponent(resourceId)}`,
80
+ false,
81
+ true,
58
82
  );
83
+ const tokenPath = this.scope.util.combineApiPath(
84
+ 'image-native/direct-upload/:resourceId',
85
+ false,
86
+ true,
87
+ );
88
+ const tokenPayload = await this.bean.imageUploadPolicy.createDirectUploadToken({
89
+ resourceId,
90
+ path: tokenPath,
91
+ });
92
+ const uploadUrl = new URL(this.app.util.getAbsoluteUrlByApiPath(routePath));
93
+ uploadUrl.searchParams.set('token', tokenPayload.token);
59
94
  return {
60
95
  resourceId,
61
- size: Number(stat.size),
62
- width: metadata.width,
63
- height: metadata.height,
64
- storagePath: targetPath,
65
- deliveryBaseUrl: options.deliveryBaseUrl,
96
+ filename: input.filename,
97
+ contentType: input.contentType,
98
+ meta: input.meta,
66
99
  requireSignedURLs: input.requireSignedURLs ?? options.requireSignedURLs,
67
- variants,
100
+ variants: options.variants ?? this.scope.config.imageNative.variants,
101
+ deliveryBaseUrl: options.deliveryBaseUrl,
102
+ storagePath,
103
+ uploadUrl: uploadUrl.toString(),
104
+ draft: true,
68
105
  };
69
106
  }
70
107
 
108
+ async uploadDirectFile(image: IImageNativeDraftImage, file: IUploadFile) {
109
+ const draftPath = this._getDraftPath(image.storagePath, image.resourceId);
110
+ await fse.ensureDir(path.dirname(draftPath));
111
+ await fse.move(file.file, draftPath, { overwrite: true });
112
+ return draftPath;
113
+ }
114
+
115
+ async finalizeDirectUpload(
116
+ image: IImageNativeDraftImage,
117
+ options: IImageProviderNativeClientOptions,
118
+ ): Promise<IImageProviderResource | undefined> {
119
+ const storagePath =
120
+ image.storagePath ?? (await this._getFinalPath(image.resourceId, image.filename, options));
121
+ const draftPath = this._getDraftPath(storagePath, image.resourceId);
122
+ if (!(await fse.pathExists(draftPath))) return undefined;
123
+ await fse.ensureDir(path.dirname(storagePath));
124
+ await fse.move(draftPath, storagePath, { overwrite: true });
125
+ return await this._buildStoredResource(storagePath, {
126
+ resourceId: image.resourceId,
127
+ filename: image.filename,
128
+ contentType: image.contentType,
129
+ meta: image.meta,
130
+ requireSignedURLs: image.requireSignedURLs ?? options.requireSignedURLs,
131
+ variants: image.variants ?? options.variants ?? this.scope.config.imageNative.variants,
132
+ deliveryBaseUrl: image.deliveryBaseUrl ?? options.deliveryBaseUrl,
133
+ });
134
+ }
135
+
71
136
  async remove(image: IImageNativeStoredImage) {
72
137
  if (!image.storagePath) return;
73
138
  const dir = path.dirname(image.storagePath);
74
139
  if (!(await fse.pathExists(dir))) return;
140
+ const draftPath = this._getDraftPath(image.storagePath, image.resourceId);
141
+ await fse.remove(draftPath);
75
142
  const entries = await fse.readdir(dir);
76
143
  await Promise.all(
77
144
  entries
@@ -100,22 +167,57 @@ export class ServiceImageNative extends BeanBase {
100
167
  return await this._buildUrl(targetPath, options.deliveryBaseUrl);
101
168
  }
102
169
 
103
- private async _generateNamedVariants(
104
- image: IImageNativeStoredImage,
170
+ private async _buildStoredResource(
171
+ targetPath: string,
172
+ data: {
173
+ resourceId: string;
174
+ filename?: string;
175
+ contentType?: string;
176
+ meta?: Record<string, unknown>;
177
+ requireSignedURLs?: boolean;
178
+ variants?: EntityImage['variants'];
179
+ deliveryBaseUrl?: string;
180
+ },
181
+ ): Promise<IImageProviderResource> {
182
+ const sharp = await this.getSharp();
183
+ const metadata = await sharp(targetPath).metadata();
184
+ const stat = await fse.stat(targetPath);
185
+ return {
186
+ resourceId: data.resourceId,
187
+ filename: data.filename,
188
+ contentType: data.contentType,
189
+ size: Number(stat.size),
190
+ width: metadata.width,
191
+ height: metadata.height,
192
+ requireSignedURLs: data.requireSignedURLs,
193
+ variants: data.variants,
194
+ meta: data.meta,
195
+ storagePath: targetPath,
196
+ deliveryBaseUrl: data.deliveryBaseUrl,
197
+ };
198
+ }
199
+
200
+ private async _getFinalPath(
201
+ resourceId: string,
202
+ filename: string | undefined,
105
203
  options: IImageProviderNativeClientOptions,
204
+ filePath?: string,
106
205
  ) {
107
- const variantNames = Object.keys(image.variants ?? {}) as Array<
108
- keyof NonNullable<IImageNamedVariants>
109
- >;
110
- for (const variantName of variantNames) {
111
- if (variantName === 'original') continue;
112
- await this._ensureVariantFile(
113
- image,
114
- resolveImageVariantRequestToTransform({ variantName }, 'original', image.variants),
115
- );
206
+ const publicPath = await this.app.util.getPublicPathPhysical(
207
+ path.join('image-native', options.subdir ?? 'default'),
208
+ true,
209
+ );
210
+ const ext = this._getOriginalExt(filename, filePath ?? resourceId);
211
+ return path.join(publicPath, `${resourceId}${ext}`);
212
+ }
213
+
214
+ private _getDraftPath(storagePath: string | undefined, resourceId: string) {
215
+ if (!storagePath) {
216
+ throw new Error(`Image storage path missing: ${resourceId}`);
116
217
  }
117
- if (!image.variants?.original) return;
118
- await this.getVariantUrl(image, { variantName: 'original' }, options);
218
+ const dir = path.dirname(storagePath);
219
+ const ext = path.extname(storagePath) || '.bin';
220
+ return path.join(dir, `${resourceId}__draft${ext}`);
119
221
  }
120
222
 
121
223
  private async _ensureVariantFile(
@@ -266,6 +368,12 @@ export class ServiceImageNative extends BeanBase {
266
368
  return JSON.stringify(value);
267
369
  }
268
370
 
371
+ private _sanitizeResourceId(resourceId: string | undefined) {
372
+ if (!resourceId) return undefined;
373
+ const sanitized = resourceId.replace(/[^\w.-]/g, '-');
374
+ return sanitized || undefined;
375
+ }
376
+
269
377
  private _sanitizeVariantName(variantName: string) {
270
378
  return variantName.replace(/[^\w.-]/g, '-');
271
379
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-suite-a-image",
3
- "version": "5.0.1",
3
+ "version": "5.0.5",
4
4
  "description": "",
5
5
  "author": "",
6
6
  "files": [],
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-module-a-core",
3
- "version": "5.1.42",
3
+ "version": "5.1.43",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "",
6
6
  "keywords": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-module-a-orm",
3
- "version": "5.1.20",
3
+ "version": "5.1.23",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "db",
6
6
  "keywords": [
@@ -11,7 +11,5 @@ export type TypeEntityMeta<T, N = string> = Required<{
11
11
  export const SymbolKeyFieldsMore = Symbol('$fieldsMore');
12
12
  export type TypeSymbolKeyFieldsMore = typeof SymbolKeyFieldsMore;
13
13
 
14
- export type TypeEntityStudentMetaSimple<T> = Omit<T, '$table' | '$comment' | '$default'>;
15
- export type TypeEntityStudentMetaSimpleColumns<T> = PickString<
16
- keyof TypeEntityStudentMetaSimple<T>
17
- >;
14
+ export type TypeEntityMetaFields<T> = Omit<T, '$table' | '$comment' | '$default'>;
15
+ export type TypeEntityMetaFieldKeys<T> = PickString<keyof TypeEntityMetaFields<T>>;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-module-a-ormutils",
3
- "version": "5.1.11",
3
+ "version": "5.1.14",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "",
6
6
  "keywords": [
@@ -4,7 +4,7 @@ import type {
4
4
  IDecoratorEntityOptions,
5
5
  ITableRecord,
6
6
  TypeEntityMeta,
7
- TypeEntityStudentMetaSimpleColumns,
7
+ TypeEntityMetaFieldKeys,
8
8
  } from 'vona-module-a-orm';
9
9
 
10
10
  import { isClass } from '@cabloy/utils';
@@ -15,6 +15,9 @@ import {
15
15
  getTargetDecoratorRules,
16
16
  } from 'vona-module-a-openapiutils';
17
17
 
18
+ export type TypeEntityMetaCompositeFieldKeys<T> =
19
+ `${TypeEntityMetaFieldKeys<T>}+${TypeEntityMetaFieldKeys<T>}`;
20
+
18
21
  export function $column<T>(key: keyof T): keyof T {
19
22
  return key;
20
23
  }
@@ -53,8 +56,9 @@ export function $columnsAll<T, TableName extends boolean, Meta extends boolean>(
53
56
  export function $tableColumns<K extends keyof ITableRecord>(
54
57
  tableName: K,
55
58
  key?:
56
- | TypeEntityStudentMetaSimpleColumns<ITableRecord[K]>
57
- | TypeEntityStudentMetaSimpleColumns<ITableRecord[K]>[]
59
+ | TypeEntityMetaFieldKeys<ITableRecord[K]>
60
+ | TypeEntityMetaCompositeFieldKeys<ITableRecord[K]>
61
+ | TypeEntityMetaFieldKeys<ITableRecord[K]>[]
58
62
  | undefined,
59
63
  ) {
60
64
  return { [tableName]: key };
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-module-a-permission",
3
- "version": "5.1.13",
3
+ "version": "5.1.14",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "",
6
6
  "keywords": [