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.
- package/.cabloy-version +1 -1
- package/.claude/hooks/contract-loop-gate.ts +151 -44
- package/CHANGELOG.md +48 -0
- package/CLAUDE.md +2 -0
- package/cabloy-docs/.vitepress/config.mjs +11 -0
- package/cabloy-docs/backend/controller-aop-guide.md +0 -1
- package/cabloy-docs/backend/image-guide.md +544 -0
- package/cabloy-docs/frontend/a-openapi-under-the-hood.md +1 -1
- package/cabloy-docs/frontend/generated-contract-consumption-entry-branch.md +1 -1
- package/cabloy-docs/frontend/generated-contract-consumption-list-branch.md +1 -1
- package/cabloy-docs/frontend/generated-contract-consumption-verify-playbook.md +1 -1
- package/cabloy-docs/frontend/image-guide.md +512 -0
- package/cabloy-docs/frontend/introduction.md +2 -0
- package/cabloy-docs/frontend/ioc-and-beans.md +34 -3
- package/cabloy-docs/frontend/model-architecture.md +2 -0
- package/cabloy-docs/frontend/model-resource-owner-pattern.md +3 -1
- package/cabloy-docs/frontend/model-state-guide.md +5 -1
- package/cabloy-docs/frontend/reading-zova-for-vue-developers.md +10 -8
- package/cabloy-docs/frontend/resource-entry-page-deep-dive.md +2 -2
- package/cabloy-docs/frontend/resource-list-page-deep-dive.md +1 -1
- package/cabloy-docs/frontend/rest-resource-source-reading-map.md +2 -2
- package/cabloy-docs/frontend/rest-resource-under-the-hood.md +4 -4
- package/cabloy-docs/frontend/server-data.md +2 -0
- package/cabloy-docs/frontend/state-architecture-for-vue-developers.md +218 -0
- package/cabloy-docs/frontend/system-startup-guide.md +2 -0
- package/cabloy-docs/frontend/use-state-data-best-practices.md +384 -0
- package/cabloy-docs/frontend/zova-vs-vue3-comparison.md +11 -10
- package/cabloy-docs/fullstack/image-workflow.md +403 -0
- package/cabloy-docs/fullstack/introduction.md +1 -0
- package/package.json +1 -1
- package/scripts/upgrade.ts +2 -0
- package/vona/package.original.json +0 -1
- package/vona/packages-cli/cli/package.json +1 -1
- package/vona/packages-cli/cli-set-api/package.json +1 -1
- package/vona/packages-vona/vona/package.json +1 -1
- package/vona/packages-vona/vona-core/package.json +1 -1
- package/vona/packages-vona/vona-core/src/lib/bean/beanBase.ts +9 -9
- package/vona/packages-vona/vona-mock/package.json +1 -1
- package/vona/pnpm-lock.yaml +424 -47
- package/vona/src/suite/a-training/modules/training-record/src/bean/fileScene.dossierFile.ts +22 -0
- package/vona/src/suite/a-training/modules/training-record/src/bean/meta.version.ts +1 -0
- package/vona/src/suite/a-training/modules/training-record/src/config/locale/en-us.ts +1 -0
- package/vona/src/suite/a-training/modules/training-record/src/config/locale/zh-cn.ts +1 -0
- package/vona/src/suite/a-training/modules/training-record/src/dto/recordDossierFileView.ts +47 -0
- package/vona/src/suite/a-training/modules/training-record/src/dto/recordSelectResItem.tsx +13 -0
- package/vona/src/suite/a-training/modules/training-record/src/dto/recordView.tsx +13 -0
- package/vona/src/suite/a-training/modules/training-record/src/entity/record.tsx +16 -5
- package/vona/src/suite/a-training/modules/training-record/src/index.ts +2 -0
- package/vona/src/suite/a-training/modules/training-record/src/lib/index.ts +1 -0
- package/vona/src/suite/a-training/modules/training-record/src/lib/resolveDossierFiles.ts +34 -0
- package/vona/src/suite/a-training/modules/training-record/test/record.test.ts +209 -123
- package/vona/src/suite/a-training/modules/training-student/src/config/locale/en-us.ts +1 -0
- package/vona/src/suite/a-training/modules/training-student/src/config/locale/zh-cn.ts +1 -0
- package/vona/src/suite/a-training/modules/training-student/src/dto/detailRecordBase.tsx +18 -1
- package/vona/src/suite/a-training/modules/training-student/src/entity/student.tsx +0 -2
- package/vona/src/suite/a-training/modules/training-student/src/model/student.ts +1 -0
- package/vona/src/suite/cabloy-basic/modules/basic-siteadmin/src/bean/ssrSite.admin.ts +4 -0
- package/vona/src/suite/cabloy-basic/modules/basic-siteweb/src/bean/ssrSite.web.ts +4 -0
- package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/cli/ssrSite/boilerplate/{{sceneName}}.{{beanName}}.ts_ +3 -0
- package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/package.json +1 -1
- package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/service/ssrHandler.ts +11 -2
- package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/types/ssrSite.ts +6 -0
- package/vona/src/suite-vendor/a-cabloy/package.json +1 -1
- package/vona/src/suite-vendor/a-file/modules/a-file/cli/fileProvider/boilerplate/{{sceneName}}.{{beanName}}.ts_ +23 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/cli/fileScene/boilerplate/{{sceneName}}.{{beanName}}.ts_ +5 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/package.json +68 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/.metadata/index.ts +425 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/.metadata/locales.ts +18 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/.metadata/this.ts +2 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/bean/bean.file.ts +322 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/bean/bean.fileProvider.ts +62 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/bean/bean.fileUploadPolicy.ts +224 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/bean/meta.redlock.ts +11 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/bean/meta.version.ts +38 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/config/config.ts +16 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/config/locale/en-us.ts +1 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/config/locale/zh-cn.ts +1 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/controller/file.ts +175 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/dto/fileDirectUploadRequest.ts +33 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/dto/fileDirectUploadResponse.ts +41 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/dto/fileDownloadRequest.ts +12 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/dto/fileUploadPolicyRequest.ts +15 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/dto/fileUploadPolicyResponse.ts +30 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/dto/fileUploadResponse.ts +59 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/dto/fileUploadTokenRequest.ts +24 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/dto/fileUploadTokenResponse.ts +16 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/dto/fileUploadUrlRequest.ts +33 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/entity/file.ts +55 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/entity/fileProvider.ts +24 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/index.ts +4 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/lib/fileProvider.ts +7 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/lib/fileScene.ts +7 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/lib/fileUploadValidation.ts +15 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/lib/index.ts +3 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/model/file.ts +10 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/model/fileProvider.ts +10 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/types/file.ts +144 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/types/fileProvider.ts +111 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/types/fileScene.ts +53 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/src/types/index.ts +3 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/tsconfig.build.json +11 -0
- package/vona/src/suite-vendor/a-file/modules/a-file/tsconfig.json +7 -0
- package/vona/src/suite-vendor/a-file/modules/file-cloudflare/package.json +53 -0
- package/vona/src/suite-vendor/a-file/modules/file-cloudflare/src/.metadata/index.ts +104 -0
- package/vona/src/suite-vendor/a-file/modules/file-cloudflare/src/.metadata/this.ts +2 -0
- package/vona/src/suite-vendor/a-file/modules/file-cloudflare/src/bean/fileProvider.cloudflare.ts +123 -0
- package/vona/src/suite-vendor/a-file/modules/file-cloudflare/src/index.ts +1 -0
- package/vona/src/suite-vendor/a-file/modules/file-cloudflare/src/service/fileCloudflare.ts +245 -0
- package/vona/src/suite-vendor/a-file/modules/file-cloudflare/tsconfig.build.json +11 -0
- package/vona/src/suite-vendor/a-file/modules/file-cloudflare/tsconfig.json +7 -0
- package/vona/src/suite-vendor/a-file/modules/file-native/package.json +50 -0
- package/vona/src/suite-vendor/a-file/modules/file-native/src/.metadata/index.ts +111 -0
- package/vona/src/suite-vendor/a-file/modules/file-native/src/.metadata/this.ts +2 -0
- package/vona/src/suite-vendor/a-file/modules/file-native/src/bean/fileProvider.native.ts +125 -0
- package/vona/src/suite-vendor/a-file/modules/file-native/src/config/config.ts +7 -0
- package/vona/src/suite-vendor/a-file/modules/file-native/src/index.ts +1 -0
- package/vona/src/suite-vendor/a-file/modules/file-native/src/service/fileNative.ts +125 -0
- package/vona/src/suite-vendor/a-file/modules/file-native/tsconfig.build.json +11 -0
- package/vona/src/suite-vendor/a-file/modules/file-native/tsconfig.json +7 -0
- package/vona/src/suite-vendor/a-file/package.json +14 -0
- package/vona/src/suite-vendor/a-file/tsconfig.base.json +4 -0
- package/vona/src/suite-vendor/a-file/tsconfig.json +16 -0
- package/vona/src/suite-vendor/a-image/modules/a-image/package.json +4 -1
- package/vona/src/suite-vendor/a-image/modules/a-image/src/.metadata/index.ts +61 -3
- package/vona/src/suite-vendor/a-image/modules/a-image/src/bean/bean.image.ts +336 -152
- package/vona/src/suite-vendor/a-image/modules/a-image/src/bean/bean.imageProvider.ts +21 -14
- package/vona/src/suite-vendor/a-image/modules/a-image/src/bean/bean.imageUploadPolicy.ts +86 -11
- package/vona/src/suite-vendor/a-image/modules/a-image/src/bean/meta.index.ts +12 -0
- package/vona/src/suite-vendor/a-image/modules/a-image/src/bean/meta.redlock.ts +1 -1
- package/vona/src/suite-vendor/a-image/modules/a-image/src/bean/meta.version.ts +3 -0
- package/vona/src/suite-vendor/a-image/modules/a-image/src/bean/schedule.imageDraftPrune.ts +29 -0
- package/vona/src/suite-vendor/a-image/modules/a-image/src/config/config.ts +3 -0
- package/vona/src/suite-vendor/a-image/modules/a-image/src/controller/image.ts +32 -54
- package/vona/src/suite-vendor/a-image/modules/a-image/src/dto/imageDirectUploadFinalizeRequest.ts +13 -0
- package/vona/src/suite-vendor/a-image/modules/a-image/src/dto/imageDirectUploadFinalizeResponse.ts +10 -0
- package/vona/src/suite-vendor/a-image/modules/a-image/src/dto/imageDirectUploadResponse.ts +10 -0
- package/vona/src/suite-vendor/a-image/modules/a-image/src/dto/imageUploadPolicyRequest.ts +15 -0
- package/vona/src/suite-vendor/a-image/modules/a-image/src/dto/imageUploadPolicyResponse.ts +27 -0
- package/vona/src/suite-vendor/a-image/modules/a-image/src/dto/imageUploadResponse.ts +10 -1
- package/vona/src/suite-vendor/a-image/modules/a-image/src/dto/imageView.ts +10 -1
- package/vona/src/suite-vendor/a-image/modules/a-image/src/entity/image.ts +10 -1
- package/vona/src/suite-vendor/a-image/modules/a-image/src/lib/imageUploadValidation.ts +15 -0
- package/vona/src/suite-vendor/a-image/modules/a-image/src/lib/index.ts +1 -0
- package/vona/src/suite-vendor/a-image/modules/a-image/src/types/image.ts +18 -0
- package/vona/src/suite-vendor/a-image/modules/a-image/src/types/imageProvider.ts +28 -2
- package/vona/src/suite-vendor/a-image/modules/image-cloudflare/package.json +1 -1
- package/vona/src/suite-vendor/a-image/modules/image-cloudflare/src/bean/imageProvider.cloudflare.ts +13 -1
- package/vona/src/suite-vendor/a-image/modules/image-cloudflare/src/service/imageCloudflare.ts +26 -0
- package/vona/src/suite-vendor/a-image/modules/image-native/package.json +6 -2
- package/vona/src/suite-vendor/a-image/modules/image-native/src/.metadata/index.ts +37 -0
- package/vona/src/suite-vendor/a-image/modules/image-native/src/bean/imageProvider.native.ts +21 -1
- package/vona/src/suite-vendor/a-image/modules/image-native/src/controller/image.ts +57 -0
- package/vona/src/suite-vendor/a-image/modules/image-native/src/service/imageNative.ts +142 -34
- package/vona/src/suite-vendor/a-image/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-core/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/types/entity.ts +2 -4
- package/vona/src/suite-vendor/a-vona/modules/a-ormutils/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-ormutils/src/lib/columns.ts +7 -3
- package/vona/src/suite-vendor/a-vona/modules/a-permission/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-permission/src/.metadata/index.ts +24 -9
- package/vona/src/suite-vendor/a-vona/modules/a-permission/src/bean/bean.permission.ts +87 -73
- package/vona/src/suite-vendor/a-vona/modules/a-permission/src/bean/summerCache.permissionActionByRoles.ts +27 -0
- package/vona/src/suite-vendor/a-vona/modules/a-permission/src/bean/summerCache.permissionUser.ts +20 -0
- package/vona/src/suite-vendor/a-vona/modules/a-startup/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-startup/src/service/startup.ts +4 -3
- package/vona/src/suite-vendor/a-vona/modules/a-upload/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-upload/src/bean/interceptor.upload.ts +5 -1
- package/vona/src/suite-vendor/a-vona/modules/a-user/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-user/src/.metadata/index.ts +0 -13
- package/vona/src/suite-vendor/a-vona/modules/a-user/src/lib/passport.ts +0 -7
- package/vona/src/suite-vendor/a-vona/modules/a-version/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-version/src/bean/meta.version.ts +2 -24
- package/vona/src/suite-vendor/a-vona/package.json +1 -1
- package/zova/packages-utils/zova-jsx/package.json +2 -2
- package/zova/packages-zova/zova/package.json +3 -3
- package/zova/packages-zova/zova-core/package.json +1 -1
- package/zova/packages-zova/zova-core/src/bean/beanBase.ts +9 -13
- package/zova/pnpm-lock.yaml +418 -411
- package/zova/src/suite/a-demo/modules/demo-basic/src/page/toolOne/controller.tsx +2 -2
- package/zova/src/suite/a-demo/modules/demo-todo/src/page/todo/controller.tsx +2 -2
- package/zova/src/suite/a-home/modules/home-layoutadmin/src/component/layoutAdmin/controller.tsx +2 -2
- package/zova/src/suite/a-home/modules/home-layoutweb/src/component/layoutWeb/controller.tsx +2 -2
- package/zova/src/suite/a-home/modules/home-login/src/page/login/controller.tsx +2 -2
- package/zova/src/suite/cabloy-basic/modules/basic-details/src/component/blockDetails/controller.tsx +5 -1
- package/zova/src/suite/cabloy-basic/modules/basic-details/src/component/blockTable/controller.tsx +20 -15
- package/zova/src/suite/cabloy-basic/modules/basic-details/src/service/detail.tsx +6 -1
- package/zova/src/suite/cabloy-basic/modules/basic-file/cli/openapi.config.ts +9 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/package.json +58 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/.metadata/component/formFieldFile.ts +31 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/.metadata/index.ts +222 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/.metadata/locales.ts +7 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/.metadata/this.ts +2 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/api/file.ts +121 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/api/openapi/baseURL.ts +5 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/api/openapi/index.ts +3 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/api/openapi/schemas.ts +306 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/api/openapi/types.ts +5718 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/apiSchema/file.ts +40 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/bean/tableCell.file.tsx +187 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/component/formFieldFile/controller.tsx +534 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/config/locale/en-us.ts +15 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/config/locale/zh-cn.ts +15 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/index.ts +4 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/lib/file.ts +74 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/lib/index.ts +1 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/model/file.ts +23 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/types/file.ts +12 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/src/types/index.ts +1 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/tsconfig.build.json +13 -0
- package/zova/src/suite/cabloy-basic/modules/basic-file/tsconfig.json +5 -0
- package/zova/src/suite/cabloy-basic/modules/basic-image/src/.metadata/index.ts +61 -0
- package/zova/src/suite/cabloy-basic/modules/basic-image/src/api/image.ts +38 -1
- package/zova/src/suite/cabloy-basic/modules/basic-image/src/api/imageNativeImage.ts +31 -0
- package/zova/src/suite/cabloy-basic/modules/basic-image/src/api/openapi/schemas.ts +52 -0
- package/zova/src/suite/cabloy-basic/modules/basic-image/src/api/openapi/types.ts +614 -1
- package/zova/src/suite/cabloy-basic/modules/basic-image/src/apiSchema/image.ts +10 -0
- package/zova/src/suite/cabloy-basic/modules/basic-image/src/apiSchema/imageNativeImage.ts +13 -0
- package/zova/src/suite/cabloy-basic/modules/basic-image/src/component/formFieldImage/controller.tsx +126 -62
- package/zova/src/suite/cabloy-basic/modules/basic-image/src/model/image.ts +23 -0
- package/zova/src/suite/cabloy-basic/modules/basic-page/src/component/blockPage/controller.tsx +44 -4
- package/zova/src/suite/cabloy-basic/modules/basic-page/src/component/blockTable/controller.tsx +17 -12
- package/zova/src/suite/cabloy-basic/modules/basic-pageentry/src/component/blockPageEntry/controller.tsx +2 -2
- package/zova/src/suite/cabloy-basic/modules/basic-resource/src/component/formFieldResourcePicker/controller.tsx +2 -2
- package/zova/src/suite/cabloy-basic/modules/basic-table/src/component/table/controller.tsx +7 -2
- package/zova/src/suite/cabloy-basic/modules/basic-table/src/component/table/render.tsx +34 -33
- package/zova/src/suite/cabloy-basic/package.json +1 -0
- package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/package.json +1 -1
- package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/src/model/resource.ts +5 -3
- package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/src/page/entry/controller.tsx +2 -2
- package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/src/page/resource/controller.tsx +2 -2
- package/zova/src/suite-vendor/a-cabloy/package.json +2 -2
- package/zova/src/suite-vendor/a-zova/modules/a-model/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-model/src/lib/utils.ts +17 -17
- package/zova/src/suite-vendor/a-zova/modules/a-openapi/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-openapi/src/model/sdk.ts +8 -3
- package/zova/src/suite-vendor/a-zova/modules/a-zova/package.json +2 -2
- package/zova/src/suite-vendor/a-zova/package.json +4 -4
- package/vona/src/suite-vendor/a-vona/modules/a-permission/src/bean/summerCache.permission.ts +0 -17
- package/vona/src/suite-vendor/a-vona/modules/a-user/src/bean/guard.userName.ts +0 -35
|
@@ -0,0 +1,544 @@
|
|
|
1
|
+
# Image Guide
|
|
2
|
+
|
|
3
|
+
This guide explains how to use **Vona image** in Cabloy Basic with a practical, contract-first path.
|
|
4
|
+
|
|
5
|
+
Vona image is not only a file-upload helper. It is a backend image layer built around:
|
|
6
|
+
|
|
7
|
+
- image scenes for business-specific upload policy
|
|
8
|
+
- image providers for storage and delivery behavior
|
|
9
|
+
- token-based upload and signed delivery flows
|
|
10
|
+
- named variants and ad hoc transforms
|
|
11
|
+
- serializer transforms that turn stored image IDs into `DtoImageView` output
|
|
12
|
+
|
|
13
|
+
Use this page together with:
|
|
14
|
+
|
|
15
|
+
- [Upload Guide](/backend/upload-guide)
|
|
16
|
+
- [Serialization Guide](/backend/serialization-guide)
|
|
17
|
+
- [Bean Scene Authoring](/backend/bean-scene-authoring)
|
|
18
|
+
- [OpenAPI Guide](/backend/openapi-guide)
|
|
19
|
+
- [Frontend Image Guide](/frontend/image-guide)
|
|
20
|
+
|
|
21
|
+
> [!TIP]
|
|
22
|
+
> **Image docs path**
|
|
23
|
+
>
|
|
24
|
+
> - **[Backend Image Guide](/backend/image-guide)** — learn image scenes, providers, serializer transforms, delivery policy, and backend lifecycle behavior
|
|
25
|
+
> - **[Frontend Image Guide](/frontend/image-guide)** — learn `basic-image:formFieldImage`, `basic-image:image`, relation previews, and client-side image-field behavior
|
|
26
|
+
>
|
|
27
|
+
> Use the backend guide when the question is about image scenes, providers, serializer transforms, signed delivery, or upload policy enforcement. Use the frontend guide when the question is about forms, tables, previews, or client-side upload UX.
|
|
28
|
+
|
|
29
|
+
Read this page first when your next question is:
|
|
30
|
+
|
|
31
|
+
- which image scene should own this upload flow
|
|
32
|
+
- which provider or delivery mode should be used
|
|
33
|
+
- how `resolveView` or `resolveViews` fit into the DTO contract
|
|
34
|
+
|
|
35
|
+
## Why the image suite exists
|
|
36
|
+
|
|
37
|
+
Real image handling usually needs more than accepting a multipart file.
|
|
38
|
+
|
|
39
|
+
A typical business flow also needs to answer questions such as:
|
|
40
|
+
|
|
41
|
+
- which upload use cases are allowed
|
|
42
|
+
- which provider should own the file
|
|
43
|
+
- what size and MIME types are allowed
|
|
44
|
+
- whether the client uploads through the app server, uploads directly, or uploads by URL
|
|
45
|
+
- how the image should be delivered later
|
|
46
|
+
- whether delivery should be signed
|
|
47
|
+
- how APIs should expose image data in DTO responses
|
|
48
|
+
|
|
49
|
+
Vona treats that as one backend capability.
|
|
50
|
+
|
|
51
|
+
## Smallest correct mental model
|
|
52
|
+
|
|
53
|
+
The smallest practical model is:
|
|
54
|
+
|
|
55
|
+
- `@ImageScene(...)` defines a business upload scene
|
|
56
|
+
- `BeanImageUploadPolicy` resolves the scene into provider, client, upload limits, and temp tokens
|
|
57
|
+
- `ControllerImage` exposes the HTTP endpoints
|
|
58
|
+
- `BeanImage` is the main programmatic API for upload, variant URL resolution, download, delete, and DTO-side image view resolution
|
|
59
|
+
- `@ImageProvider(...)` implementations map the generic image contract to concrete storage or CDN behavior
|
|
60
|
+
- serializer transforms `a-image:resolveView` and `a-image:resolveViews` convert stored image IDs into `DtoImageView` output
|
|
61
|
+
|
|
62
|
+
That division is the key to reading the source correctly.
|
|
63
|
+
|
|
64
|
+
## Suite boundary
|
|
65
|
+
|
|
66
|
+
The image suite lives under `vona/src/suite-vendor/a-image` and is split into:
|
|
67
|
+
|
|
68
|
+
- `a-image`: the core abstraction, controller, persistence, policy, variants, and serializer transforms
|
|
69
|
+
- `image-native`: local filesystem storage plus Sharp-based transforms
|
|
70
|
+
- `image-cloudflare`: Cloudflare Images integration
|
|
71
|
+
|
|
72
|
+
In other words:
|
|
73
|
+
|
|
74
|
+
- `a-image` defines the contract
|
|
75
|
+
- provider modules implement the contract
|
|
76
|
+
|
|
77
|
+
## Core concepts
|
|
78
|
+
|
|
79
|
+
### Image provider
|
|
80
|
+
|
|
81
|
+
An image provider is the storage and delivery backend.
|
|
82
|
+
|
|
83
|
+
Providers are registered with `@ImageProvider(...)` and implement the `IImageProviderExecute` contract.
|
|
84
|
+
|
|
85
|
+
Representative built-in providers are:
|
|
86
|
+
|
|
87
|
+
- `image-native:native`
|
|
88
|
+
- `image-cloudflare:cloudflare`
|
|
89
|
+
|
|
90
|
+
Required provider capabilities are:
|
|
91
|
+
|
|
92
|
+
- `upload`
|
|
93
|
+
- `get`
|
|
94
|
+
- `delete`
|
|
95
|
+
- `getVariantUrl`
|
|
96
|
+
|
|
97
|
+
Optional capabilities are:
|
|
98
|
+
|
|
99
|
+
- `uploadUrl`
|
|
100
|
+
- `createDirectUpload`
|
|
101
|
+
- `download`
|
|
102
|
+
|
|
103
|
+
That means a provider can support the basic lifecycle without supporting every upload style.
|
|
104
|
+
|
|
105
|
+
### Provider client
|
|
106
|
+
|
|
107
|
+
A provider can expose multiple named clients.
|
|
108
|
+
|
|
109
|
+
`BeanImageProvider.getClientOptions(...)` resolves client options by layering:
|
|
110
|
+
|
|
111
|
+
1. caller defaults
|
|
112
|
+
2. provider decorator `base`
|
|
113
|
+
3. provider decorator `clients[clientName]`
|
|
114
|
+
4. persisted `aImageProvider.clientOptions`
|
|
115
|
+
5. caller runtime overrides
|
|
116
|
+
|
|
117
|
+
This lets one provider support multiple delivery or credential configurations without changing business code.
|
|
118
|
+
|
|
119
|
+
### Image scene
|
|
120
|
+
|
|
121
|
+
An image scene is the business-facing upload policy.
|
|
122
|
+
|
|
123
|
+
A scene is registered with `@ImageScene(...)` and can define:
|
|
124
|
+
|
|
125
|
+
- provider selection
|
|
126
|
+
- client selection
|
|
127
|
+
- upload rules
|
|
128
|
+
- optional per-scene metadata
|
|
129
|
+
|
|
130
|
+
Representative scene shape:
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
@ImageScene({
|
|
134
|
+
upload: {
|
|
135
|
+
maxSize: 2 * 1024 * 1024,
|
|
136
|
+
mimeTypes: ['image/png', 'image/jpeg', 'image/webp'],
|
|
137
|
+
},
|
|
138
|
+
})
|
|
139
|
+
export class ImageSceneStudentImage extends BeanBase {}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
This keeps business upload rules in one named backend capability instead of scattering them across controllers and services.
|
|
143
|
+
|
|
144
|
+
### Image variant
|
|
145
|
+
|
|
146
|
+
A variant is how an image should be delivered.
|
|
147
|
+
|
|
148
|
+
Vona supports two modes:
|
|
149
|
+
|
|
150
|
+
- a named variant such as `original` or `thumbnail`
|
|
151
|
+
- an ad hoc transform expressed through `transformOptions`
|
|
152
|
+
|
|
153
|
+
The built-in named variants are:
|
|
154
|
+
|
|
155
|
+
- `original`
|
|
156
|
+
- `thumbnail`
|
|
157
|
+
- `small`
|
|
158
|
+
- `medium`
|
|
159
|
+
- `large`
|
|
160
|
+
- `cover`
|
|
161
|
+
- `avatar`
|
|
162
|
+
|
|
163
|
+
The source also makes one important rule explicit:
|
|
164
|
+
|
|
165
|
+
- `variantName` and `transformOptions` are mutually exclusive
|
|
166
|
+
|
|
167
|
+
If neither is provided, Vona falls back to the configured default variant, which is currently `original`.
|
|
168
|
+
|
|
169
|
+
### Signed delivery
|
|
170
|
+
|
|
171
|
+
Vona supports signed delivery in two forms:
|
|
172
|
+
|
|
173
|
+
- **proxy-signed delivery**: Vona creates a temporary delivery token and redirects through `/image/delivery/:imageId`
|
|
174
|
+
- **provider-signed delivery**: the provider generates the final signed URL directly
|
|
175
|
+
|
|
176
|
+
The built-in defaults differ:
|
|
177
|
+
|
|
178
|
+
- `image-native:native` defaults to `signedDeliveryKind: 'proxy'`
|
|
179
|
+
- `image-cloudflare:cloudflare` defaults to `signedDeliveryKind: 'provider'`
|
|
180
|
+
|
|
181
|
+
## Main backend API
|
|
182
|
+
|
|
183
|
+
The main backend surface is `app.bean.image`.
|
|
184
|
+
|
|
185
|
+
Representative methods are:
|
|
186
|
+
|
|
187
|
+
- `upload(...)`
|
|
188
|
+
- `uploadUrl(...)`
|
|
189
|
+
- `createDirectUpload(...)`
|
|
190
|
+
- `get(...)`
|
|
191
|
+
- `delete(...)`
|
|
192
|
+
- `getVariantUrl(...)`
|
|
193
|
+
- `download(...)`
|
|
194
|
+
- `resolveView(...)`
|
|
195
|
+
- `resolveViews(...)`
|
|
196
|
+
|
|
197
|
+
A useful practical boundary is:
|
|
198
|
+
|
|
199
|
+
- use `BeanImageUploadPolicy` when you need scene resolution and upload-token policy
|
|
200
|
+
- use `app.bean.image` when you need the actual image lifecycle and delivery behavior
|
|
201
|
+
|
|
202
|
+
## Persistence model
|
|
203
|
+
|
|
204
|
+
The core module persists two main tables.
|
|
205
|
+
|
|
206
|
+
### `aImage`
|
|
207
|
+
|
|
208
|
+
Each stored image records backend-facing data such as:
|
|
209
|
+
|
|
210
|
+
- provider and client
|
|
211
|
+
- provider resource ID
|
|
212
|
+
- filename and content type
|
|
213
|
+
- size, width, and height
|
|
214
|
+
- signed-delivery requirement
|
|
215
|
+
- variants
|
|
216
|
+
- custom metadata
|
|
217
|
+
- storage path
|
|
218
|
+
- delivery base URL
|
|
219
|
+
- image scene
|
|
220
|
+
|
|
221
|
+
### `aImageProvider`
|
|
222
|
+
|
|
223
|
+
Provider-client configuration is stored separately and includes fields such as:
|
|
224
|
+
|
|
225
|
+
- `disabled`
|
|
226
|
+
- `providerName`
|
|
227
|
+
- `clientName`
|
|
228
|
+
- `clientOptions`
|
|
229
|
+
|
|
230
|
+
If a provider-client record does not exist yet, `BeanImageProvider` can lazily register it.
|
|
231
|
+
|
|
232
|
+
## Operational flows
|
|
233
|
+
|
|
234
|
+
## Upload-token + multipart upload
|
|
235
|
+
|
|
236
|
+
The tokened upload flow is the most important request path.
|
|
237
|
+
|
|
238
|
+
The flow is:
|
|
239
|
+
|
|
240
|
+
1. call `POST /image/upload-token`
|
|
241
|
+
2. Vona resolves the image scene and validates the declared `size` and `mimeType`
|
|
242
|
+
3. Vona issues a temporary upload token that is bound to the upload path
|
|
243
|
+
4. submit `POST /image/upload` as multipart form data
|
|
244
|
+
5. send:
|
|
245
|
+
- `token`
|
|
246
|
+
- `image`
|
|
247
|
+
6. the controller verifies the token, re-checks file size, MIME type, and extension, then uploads through the resolved provider
|
|
248
|
+
|
|
249
|
+
Representative controller shape:
|
|
250
|
+
|
|
251
|
+
```typescript
|
|
252
|
+
@Web.post('upload-token')
|
|
253
|
+
async createUploadToken(@Arg.body() data: DtoImageUploadTokenRequest) {
|
|
254
|
+
return await this.bean.imageUploadPolicy.createUploadToken(data);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
@Web.post('upload')
|
|
258
|
+
@Core.fileUpload()
|
|
259
|
+
@Api.contentType('application/json')
|
|
260
|
+
async upload(@Arg.field('token') token: string, @Arg.file('image') file: IUploadFile) {
|
|
261
|
+
const payload = await this.bean.imageUploadPolicy.verifyUploadToken(
|
|
262
|
+
token,
|
|
263
|
+
this.ctx.route.routePathRaw,
|
|
264
|
+
);
|
|
265
|
+
await this._validateUploadFile(file, payload);
|
|
266
|
+
return await this.bean.image.upload(...);
|
|
267
|
+
}
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
This flow reuses the normal Vona upload model from [Upload Guide](/backend/upload-guide), but adds image-scene policy and a path-bound temporary token.
|
|
271
|
+
|
|
272
|
+
## Direct upload
|
|
273
|
+
|
|
274
|
+
`POST /image/direct-upload` is for providers that can create an upload target without proxying the entire lifecycle through the shared `POST /image/upload` endpoint.
|
|
275
|
+
|
|
276
|
+
The shared flow is:
|
|
277
|
+
|
|
278
|
+
1. resolve the image scene and upload policy
|
|
279
|
+
2. call `app.bean.image.createDirectUpload(...)`
|
|
280
|
+
3. let the provider create the direct-upload resource
|
|
281
|
+
4. persist the draft image record
|
|
282
|
+
5. return provider information plus `uploadUrl`
|
|
283
|
+
6. upload bytes to the returned `uploadUrl`
|
|
284
|
+
7. finalize through `POST /image/direct-upload/finalize`
|
|
285
|
+
|
|
286
|
+
The built-in providers currently support this flow in two different ways:
|
|
287
|
+
|
|
288
|
+
- `image-cloudflare:cloudflare` returns a provider-hosted upload target
|
|
289
|
+
- `image-native:native` returns an app-local upload target under `/image-native/direct-upload/:resourceId`, stores the uploaded bytes as a draft file, and promotes that draft into the final native storage path during finalize
|
|
290
|
+
|
|
291
|
+
That provider difference is exactly why the shared API keeps direct upload split into create-upload-target and finalize steps.
|
|
292
|
+
|
|
293
|
+
## Upload by URL
|
|
294
|
+
|
|
295
|
+
`POST /image/upload-url` is for providers that can ingest a remote URL directly.
|
|
296
|
+
|
|
297
|
+
The flow is:
|
|
298
|
+
|
|
299
|
+
1. resolve the image scene and upload policy
|
|
300
|
+
2. validate the declared `size` and `mimeType`
|
|
301
|
+
3. call `app.bean.image.uploadUrl(...)`
|
|
302
|
+
4. let the provider fetch or ingest the remote URL
|
|
303
|
+
5. persist the image record and return the normal image resource result
|
|
304
|
+
|
|
305
|
+
The built-in Cloudflare provider supports this flow.
|
|
306
|
+
|
|
307
|
+
The native provider does not implement `uploadUrl`.
|
|
308
|
+
|
|
309
|
+
## Signed delivery redirect
|
|
310
|
+
|
|
311
|
+
When delivery is signed and the provider uses proxy delivery, `BeanImage.getVariantUrl(...)` creates a temporary delivery token and returns a local delivery URL such as:
|
|
312
|
+
|
|
313
|
+
- `/image/delivery/:imageId?token=...`
|
|
314
|
+
|
|
315
|
+
The delivery controller then:
|
|
316
|
+
|
|
317
|
+
1. verifies the delivery token against the request path
|
|
318
|
+
2. confirms the token image ID matches the request image ID
|
|
319
|
+
3. redirects to the final target URL
|
|
320
|
+
|
|
321
|
+
This is why native signed delivery behaves differently from Cloudflare signed delivery.
|
|
322
|
+
|
|
323
|
+
## Configuration defaults
|
|
324
|
+
|
|
325
|
+
The core image config currently defines:
|
|
326
|
+
|
|
327
|
+
```typescript
|
|
328
|
+
image: {
|
|
329
|
+
defaultVariant: 'original',
|
|
330
|
+
defaultProvider: 'image-native:native',
|
|
331
|
+
defaultClientName: 'default',
|
|
332
|
+
upload: {
|
|
333
|
+
maxSize: 2 * 1024 * 1024,
|
|
334
|
+
mimeTypes: ['image/png', 'image/jpeg', 'image/webp'],
|
|
335
|
+
},
|
|
336
|
+
}
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
That gives the suite a working default even before a scene overrides anything.
|
|
340
|
+
|
|
341
|
+
## Provider behavior differences
|
|
342
|
+
|
|
343
|
+
### Native provider
|
|
344
|
+
|
|
345
|
+
`image-native:native` stores files locally and uses Sharp-backed processing.
|
|
346
|
+
|
|
347
|
+
Practical behavior to know:
|
|
348
|
+
|
|
349
|
+
- it defaults to proxy-signed delivery
|
|
350
|
+
- it can return local static URLs under `/api/static/...`
|
|
351
|
+
- it supports direct upload through an app-local upload target plus shared finalize
|
|
352
|
+
- named variants produce stable variant files such as `__thumbnail`
|
|
353
|
+
- ad hoc transforms produce stable hashed files such as `__t_<hash>`
|
|
354
|
+
- repeated requests for the same custom transform reuse the same cached transformed output
|
|
355
|
+
- when downloading the unsigned original variant, it can return a buffer instead of a URL
|
|
356
|
+
- it still does not implement `uploadUrl(...)` for provider-side remote URL ingestion
|
|
357
|
+
|
|
358
|
+
These behaviors are demonstrated directly in the native image tests.
|
|
359
|
+
|
|
360
|
+
### Cloudflare provider
|
|
361
|
+
|
|
362
|
+
`image-cloudflare:cloudflare` delegates storage and delivery to Cloudflare Images.
|
|
363
|
+
|
|
364
|
+
Practical behavior to know:
|
|
365
|
+
|
|
366
|
+
- it defaults to provider-signed delivery
|
|
367
|
+
- it supports direct upload and upload by URL
|
|
368
|
+
- `original` resolves to Cloudflare’s `public` delivery path
|
|
369
|
+
- ad hoc transforms become Cloudflare transform segments such as `w=320,h=180,fit=cover`
|
|
370
|
+
- signed delivery appends `exp` and `sig` query parameters
|
|
371
|
+
|
|
372
|
+
These behaviors are demonstrated in the Cloudflare mapping tests.
|
|
373
|
+
|
|
374
|
+
## Business integration patterns
|
|
375
|
+
|
|
376
|
+
The best way to understand Vona image is to follow a real business example.
|
|
377
|
+
|
|
378
|
+
### Single-image pattern: `training-student`
|
|
379
|
+
|
|
380
|
+
The training-student module stores one image field:
|
|
381
|
+
|
|
382
|
+
```typescript
|
|
383
|
+
@Api.field(
|
|
384
|
+
v.title($locale('StudentImage')),
|
|
385
|
+
v.optional(),
|
|
386
|
+
ZovaRender.field('basic-image:formFieldImage', {
|
|
387
|
+
imageScene: 'training-student:studentImage',
|
|
388
|
+
accept: ['image/png', 'image/jpeg', 'image/webp'],
|
|
389
|
+
maxSize: 2 * 1024 * 1024,
|
|
390
|
+
enableCrop: true,
|
|
391
|
+
cropAspectRatio: 1,
|
|
392
|
+
relationName: 'image',
|
|
393
|
+
resize: {
|
|
394
|
+
width: 512,
|
|
395
|
+
height: 512,
|
|
396
|
+
fit: 'cover',
|
|
397
|
+
format: 'jpeg',
|
|
398
|
+
quality: 90,
|
|
399
|
+
},
|
|
400
|
+
}),
|
|
401
|
+
ZovaRender.cell('basic-image:image', { relationName: 'image' }),
|
|
402
|
+
v.tableIdentity(),
|
|
403
|
+
)
|
|
404
|
+
imageId?: TableIdentity;
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
The important backend point is not the frontend widget itself.
|
|
408
|
+
|
|
409
|
+
The important point is that the backend contract names a scene:
|
|
410
|
+
|
|
411
|
+
- `training-student:studentImage`
|
|
412
|
+
|
|
413
|
+
That scene centralizes the upload policy while the entity field keeps the persisted value as `imageId`.
|
|
414
|
+
|
|
415
|
+
If you want the frontend-side meaning of that same field contract — how `imageId` pairs with `image`, how form previews refresh, and how the table thumbnail works — continue with [Frontend Image Guide](/frontend/image-guide).
|
|
416
|
+
|
|
417
|
+
### DTO-side image view resolution
|
|
418
|
+
|
|
419
|
+
The same module exposes the stored image ID through a resolved relation field in DTO output:
|
|
420
|
+
|
|
421
|
+
```typescript
|
|
422
|
+
@Api.field(
|
|
423
|
+
ZovaRender.visible(false),
|
|
424
|
+
v.optional(),
|
|
425
|
+
v.serializerTransform('a-image:resolveView', {
|
|
426
|
+
fieldName: 'imageId',
|
|
427
|
+
imageScene: 'training-student:studentImage',
|
|
428
|
+
}),
|
|
429
|
+
v.object(DtoImageView),
|
|
430
|
+
)
|
|
431
|
+
image?: DtoImageView;
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
This means:
|
|
435
|
+
|
|
436
|
+
- the stored field remains `imageId`
|
|
437
|
+
- the DTO adds the resolved relation field `image?: DtoImageView`
|
|
438
|
+
- serialization resolves the image lazily through `app.bean.image.resolveView(...)`
|
|
439
|
+
|
|
440
|
+
Remember the serializer rule from [Serialization Guide](/backend/serialization-guide): field-level serializer metadata only runs when the target API enables serialization.
|
|
441
|
+
|
|
442
|
+
### Multi-image pattern: `training-record`
|
|
443
|
+
|
|
444
|
+
The training-record module shows the array case.
|
|
445
|
+
|
|
446
|
+
The entity stores `sceneImageIds?: TableIdentity[]` and points to the scene:
|
|
447
|
+
|
|
448
|
+
- `training-record:sceneImage`
|
|
449
|
+
|
|
450
|
+
That scene allows multiple uploads.
|
|
451
|
+
|
|
452
|
+
The DTO then resolves the stored array with:
|
|
453
|
+
|
|
454
|
+
```typescript
|
|
455
|
+
@Api.field(
|
|
456
|
+
ZovaRender.visible(false),
|
|
457
|
+
v.optional(),
|
|
458
|
+
v.serializerTransform('a-image:resolveViews', {
|
|
459
|
+
fieldName: 'sceneImageIds',
|
|
460
|
+
imageScene: 'training-record:sceneImage',
|
|
461
|
+
}),
|
|
462
|
+
v.array(DtoImageView),
|
|
463
|
+
)
|
|
464
|
+
sceneImages?: DtoImageView[];
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
This is the cleanest pattern when business storage should keep image IDs, while API output should expose resolved relation fields with view-ready image objects.
|
|
468
|
+
|
|
469
|
+
## Extending the system
|
|
470
|
+
|
|
471
|
+
## Add a new image scene
|
|
472
|
+
|
|
473
|
+
When the business needs a new image use case, add a new `@ImageScene(...)` bean.
|
|
474
|
+
|
|
475
|
+
A scene is the right place for:
|
|
476
|
+
|
|
477
|
+
- upload limits
|
|
478
|
+
- provider routing
|
|
479
|
+
- provider client selection
|
|
480
|
+
- scene-specific metadata
|
|
481
|
+
- the decision about whether the use case is single-image or multi-image
|
|
482
|
+
|
|
483
|
+
The core module already provides a boilerplate path for image scenes:
|
|
484
|
+
|
|
485
|
+
- `vona/src/suite-vendor/a-image/modules/a-image/cli/imageScene/boilerplate/`
|
|
486
|
+
|
|
487
|
+
A practical sequence is:
|
|
488
|
+
|
|
489
|
+
1. create the scene bean
|
|
490
|
+
2. define `upload` rules and optional provider/meta resolvers
|
|
491
|
+
3. point entity or DTO metadata at the new scene name
|
|
492
|
+
4. verify upload-token and serializer behavior end to end
|
|
493
|
+
|
|
494
|
+
## Add a new image provider
|
|
495
|
+
|
|
496
|
+
When the storage backend changes, add a new `@ImageProvider(...)` bean that implements `IImageProviderExecute`.
|
|
497
|
+
|
|
498
|
+
The provider boilerplate lives under:
|
|
499
|
+
|
|
500
|
+
- `vona/src/suite-vendor/a-image/modules/a-image/cli/imageProvider/boilerplate/`
|
|
501
|
+
|
|
502
|
+
Model new providers after the built-in implementations:
|
|
503
|
+
|
|
504
|
+
- `image-native` for local filesystem plus transform behavior
|
|
505
|
+
- `image-cloudflare` for remote CDN-backed delivery behavior
|
|
506
|
+
|
|
507
|
+
A practical provider checklist is:
|
|
508
|
+
|
|
509
|
+
1. implement the required methods
|
|
510
|
+
2. decide whether to support `uploadUrl`, `createDirectUpload`, and `download`
|
|
511
|
+
3. define sensible base options in the decorator
|
|
512
|
+
4. verify variant URL behavior, signed delivery behavior, and delete behavior
|
|
513
|
+
|
|
514
|
+
## Test-backed reading map
|
|
515
|
+
|
|
516
|
+
The current repo already contains strong executable examples under:
|
|
517
|
+
|
|
518
|
+
- `vona/src/suite-vendor/a-test/modules/test-image/test/`
|
|
519
|
+
|
|
520
|
+
The most useful tests are:
|
|
521
|
+
|
|
522
|
+
- `imageUpload.test.ts` for upload-token, upload, direct-upload, and upload-url request flows
|
|
523
|
+
- `imageNative.test.ts` for local upload, native direct-upload/finalize, variant URLs, signed delivery, and delete behavior
|
|
524
|
+
- `imageCloudflareMapping.test.ts` for Cloudflare mapping, custom transforms, signed delivery, direct upload, and upload-by-URL behavior
|
|
525
|
+
|
|
526
|
+
When the source and a prose explanation seem to disagree, prefer the current source and tests.
|
|
527
|
+
|
|
528
|
+
## Verification checklist
|
|
529
|
+
|
|
530
|
+
When writing or changing image-related docs, verify in this order:
|
|
531
|
+
|
|
532
|
+
1. check the controller flow in `ControllerImage`
|
|
533
|
+
2. check scene and token behavior in `BeanImageUploadPolicy`
|
|
534
|
+
3. check delivery and DTO view behavior in `BeanImage`
|
|
535
|
+
4. check provider-specific differences in `image-native` and `image-cloudflare`
|
|
536
|
+
5. check business examples in `training-student` and `training-record`
|
|
537
|
+
6. check the test-image suite for executable confirmation
|
|
538
|
+
7. run the docs build and confirm the page appears in the sidebar
|
|
539
|
+
|
|
540
|
+
A good final question is:
|
|
541
|
+
|
|
542
|
+
- does this explanation stay backend-first and source-backed, or has it drifted into a generic image-upload article?
|
|
543
|
+
|
|
544
|
+
If it stays centered on scenes, providers, variants, signed delivery, and serializer transforms, it is aligned with how Vona image actually works.
|
|
@@ -131,7 +131,7 @@ The source confirms that:
|
|
|
131
131
|
|
|
132
132
|
- bootstrap data is loaded first
|
|
133
133
|
- permissions are then loaded through `getPermissions(resource)`
|
|
134
|
-
- on the server, permissions are
|
|
134
|
+
- on the server, permissions are ensured loaded as part of the bootstrap path
|
|
135
135
|
- on the client, permission queries are refetched when needed
|
|
136
136
|
|
|
137
137
|
That means bootstrap and permissions are part of the same lower-level OpenAPI/runtime ladder, not unrelated utilities.
|
|
@@ -121,7 +121,7 @@ Representative source facts:
|
|
|
121
121
|
|
|
122
122
|
- the page resolves `$$modelResource` from the route resource
|
|
123
123
|
- it derives `formMeta` from `formScene`
|
|
124
|
-
- it
|
|
124
|
+
- it ensures `this.$$modelResource.getFormApiSchemas(this.formMeta)?.sdk` is loaded
|
|
125
125
|
- it derives `formSchema` from `this.$$modelResource.getFormSchema(this.formMeta)`
|
|
126
126
|
|
|
127
127
|
This is the entry-shell boundary:
|
|
@@ -94,7 +94,7 @@ The clearest current list shell is:
|
|
|
94
94
|
Representative source facts:
|
|
95
95
|
|
|
96
96
|
- the page resolves `$$modelResource` from the route resource
|
|
97
|
-
- `__init__()`
|
|
97
|
+
- `__init__()` ensures `this.$$modelResource.apiSchemasSelect.sdk` is loaded
|
|
98
98
|
- render reads `schemaRow?.rest?.blocks`
|
|
99
99
|
|
|
100
100
|
This is the list-shell boundary:
|
|
@@ -117,7 +117,7 @@ Primary anchors:
|
|
|
117
117
|
What to prove:
|
|
118
118
|
|
|
119
119
|
- the list route shell resolves the expected `resource`
|
|
120
|
-
- `apiSchemasSelect.sdk` still
|
|
120
|
+
- `apiSchemasSelect.sdk` is still ensured loaded where expected
|
|
121
121
|
- `schemaRow`, `schemaFilter`, and list data flow are still exposed correctly
|
|
122
122
|
- `basic-page:blockPage` still becomes the deeper list runtime owner
|
|
123
123
|
|