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
|
@@ -102,7 +102,7 @@ zova/src/suite-vendor/a-cabloy/modules/rest-resource/src/page/resource/controlle
|
|
|
102
102
|
Its main jobs are:
|
|
103
103
|
|
|
104
104
|
- resolve the current selector-backed `ModelResource`
|
|
105
|
-
-
|
|
105
|
+
- ensure select API schemas are loaded
|
|
106
106
|
- read `schemaRow?.rest?.blocks`
|
|
107
107
|
- render those blocks through `ZovaJsx`
|
|
108
108
|
|
|
@@ -207,7 +207,7 @@ Read:
|
|
|
207
207
|
This controller does three main things:
|
|
208
208
|
|
|
209
209
|
1. resolves the selector-backed `ModelResource` instance from `this.$params.resource`
|
|
210
|
-
2.
|
|
210
|
+
2. ensures `apiSchemasSelect.sdk` is loaded
|
|
211
211
|
3. reads `schemaRow?.rest?.blocks` and renders those blocks through `ZovaJsx`
|
|
212
212
|
|
|
213
213
|
The key point is what it does **not** do.
|
|
@@ -239,7 +239,7 @@ It mainly owns:
|
|
|
239
239
|
- `formMeta` derivation
|
|
240
240
|
- `formProvider` lookup from `ModelResource`
|
|
241
241
|
- `formSchema` lookup from `ModelResource`
|
|
242
|
-
-
|
|
242
|
+
- ensuring the form API schemas are loaded
|
|
243
243
|
- block rendering from `formSchema?.rest?.blocks`
|
|
244
244
|
|
|
245
245
|
Again, the most important insight is what it does **not** fully own.
|
|
@@ -98,7 +98,7 @@ Route record
|
|
|
98
98
|
└─ page-controller shell
|
|
99
99
|
├─ resolves current resource / id / formScene
|
|
100
100
|
├─ resolves selector-backed ModelResource
|
|
101
|
-
├─
|
|
101
|
+
├─ ensures top-level API schema surface is loaded
|
|
102
102
|
└─ reads schemaRow.rest.blocks or formSchema.rest.blocks
|
|
103
103
|
│
|
|
104
104
|
▼
|
|
@@ -255,7 +255,7 @@ zova/src/suite-vendor/a-cabloy/modules/rest-resource/src/page/resource/controlle
|
|
|
255
255
|
The main jobs of `ControllerPageResource` are:
|
|
256
256
|
|
|
257
257
|
1. resolve the selector-backed model from `this.$params.resource`
|
|
258
|
-
2.
|
|
258
|
+
2. ensure the select API schema is loaded through `this.$$modelResource.apiSchemasSelect.sdk`
|
|
259
259
|
3. read `this.schemaRow?.rest?.blocks`
|
|
260
260
|
4. render those blocks through `ZovaJsx`
|
|
261
261
|
|
|
@@ -291,7 +291,7 @@ The main jobs of `ControllerPageEntry` are:
|
|
|
291
291
|
- derive `formMeta`
|
|
292
292
|
- expose `formProvider` from the model
|
|
293
293
|
- expose `formSchema` from the model
|
|
294
|
-
-
|
|
294
|
+
- ensure the form API schemas are loaded
|
|
295
295
|
- read `formSchema?.rest?.blocks`
|
|
296
296
|
- render those blocks through `ZovaJsx`
|
|
297
297
|
|
|
@@ -366,7 +366,7 @@ That means one generic model class can serve many resources safely, because the
|
|
|
366
366
|
Inside initialization, `_bootstrap()` calls:
|
|
367
367
|
|
|
368
368
|
```typescript
|
|
369
|
-
$
|
|
369
|
+
$QueryEnsureLoaded(() => this.$sdk.getBootstrap(this.resource));
|
|
370
370
|
```
|
|
371
371
|
|
|
372
372
|
and then resolves:
|
|
@@ -45,6 +45,8 @@ Use OpenAPI SDK generation when you want backend-generated API contracts to driv
|
|
|
45
45
|
|
|
46
46
|
Use schema-driven layers when metadata itself needs to participate in higher-level frontend behavior such as validation or automatic rendering.
|
|
47
47
|
|
|
48
|
+
If your main question is not only which data-access layer to choose, but how data ownership, sharing boundaries, persistence, and SSR semantics fit together in large Vue systems, read [State Architecture for Vue Developers](/frontend/state-architecture-for-vue-developers) together with this page.
|
|
49
|
+
|
|
48
50
|
## Why this matters for Cabloy
|
|
49
51
|
|
|
50
52
|
This abstraction ladder is one of the key links between Zova and Vona.
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# State Architecture for Vue Developers
|
|
2
|
+
|
|
3
|
+
This guide is for Vue developers who already know common Vue state patterns and want the shortest docs-style explanation of how Zova/Cabloy organizes large-project state differently.
|
|
4
|
+
|
|
5
|
+
It does **not** argue that Vue's usual tools are wrong.
|
|
6
|
+
It explains why large projects often need a stronger architecture for state ownership, sharing boundaries, persistence, cache semantics, and SSR behavior.
|
|
7
|
+
|
|
8
|
+
Use this page together with:
|
|
9
|
+
|
|
10
|
+
- [Reading Zova for Vue Developers](/frontend/reading-zova-for-vue-developers)
|
|
11
|
+
- [Zova vs Vue 3 Comparison](/frontend/zova-vs-vue3-comparison)
|
|
12
|
+
- [IoC and Beans](/frontend/ioc-and-beans)
|
|
13
|
+
- [Model Architecture](/frontend/model-architecture)
|
|
14
|
+
- [Model Resource Owner Pattern](/frontend/model-resource-owner-pattern)
|
|
15
|
+
|
|
16
|
+
## Why this page exists
|
|
17
|
+
|
|
18
|
+
Many Vue teams do not struggle because they picked one obviously bad tool.
|
|
19
|
+
|
|
20
|
+
They struggle because a large codebase gradually accumulates many valid state mechanisms at the same time:
|
|
21
|
+
|
|
22
|
+
- component-local reactive state
|
|
23
|
+
- composables
|
|
24
|
+
- `provide/inject`
|
|
25
|
+
- store layers
|
|
26
|
+
- query cache
|
|
27
|
+
- local persistence
|
|
28
|
+
- SSR-specific state rules
|
|
29
|
+
|
|
30
|
+
Each mechanism can solve a real local problem.
|
|
31
|
+
|
|
32
|
+
The large-project difficulty usually appears later, when the team can no longer answer these questions cleanly:
|
|
33
|
+
|
|
34
|
+
- who owns this state?
|
|
35
|
+
- how far should it be shared?
|
|
36
|
+
- which lifecycle rules apply to it?
|
|
37
|
+
- who owns cache identity and invalidation?
|
|
38
|
+
- is this only a value, or part of a larger resource boundary?
|
|
39
|
+
|
|
40
|
+
That is the architectural problem this page addresses.
|
|
41
|
+
|
|
42
|
+
## The shortest accurate summary
|
|
43
|
+
|
|
44
|
+
If you only remember one paragraph, remember this one:
|
|
45
|
+
|
|
46
|
+
> Large Vue projects rarely become hard because they need one more state tool. They usually become hard because ownership, sharing boundaries, persistence, cache semantics, and SSR behavior stop composing into one coherent system.
|
|
47
|
+
|
|
48
|
+
Zova's answer is to push those concerns back under a more explicit architecture:
|
|
49
|
+
|
|
50
|
+
- **Controller / Bean** makes ownership more explicit
|
|
51
|
+
- **IoC scopes** make sharing boundaries more explicit
|
|
52
|
+
- **Model** unifies several state families under one model-owned boundary
|
|
53
|
+
- **Resource Owner** keeps query, schema, permissions, forms, and invalidation closer to one resource-level owner
|
|
54
|
+
|
|
55
|
+
## Start with boundary questions, not only tool questions
|
|
56
|
+
|
|
57
|
+
In many Vue projects, state discussions begin with questions like these:
|
|
58
|
+
|
|
59
|
+
- should this go into a composable?
|
|
60
|
+
- should this move to a store?
|
|
61
|
+
- should we add a query layer?
|
|
62
|
+
- should we persist this in localStorage?
|
|
63
|
+
|
|
64
|
+
Those are reasonable questions.
|
|
65
|
+
|
|
66
|
+
But in a large system, the more important questions usually come first:
|
|
67
|
+
|
|
68
|
+
1. **Ownership** — which framework-managed object should own this state?
|
|
69
|
+
2. **Sharing boundary** — is it local, parent/child shared, app-level, or system-level?
|
|
70
|
+
3. **State family** — is it query-style remote state, in-memory state, local persistence, cookie-backed state, or async persisted state?
|
|
71
|
+
4. **Lifecycle** — what should survive page disposal, refresh, route transitions, or SSR request boundaries?
|
|
72
|
+
5. **Resource boundary** — does this state belong to a larger resource owner that also owns schema, permissions, forms, and invalidation?
|
|
73
|
+
|
|
74
|
+
Zova tries to answer those questions structurally instead of leaving them spread across unrelated mechanisms.
|
|
75
|
+
|
|
76
|
+
## 1. Ownership: make the state host explicit
|
|
77
|
+
|
|
78
|
+
A common Vue mental model starts with local reactive primitives and composition logic inside `setup()`.
|
|
79
|
+
|
|
80
|
+
Zova keeps Vue's reactive foundation, but changes the visible business-facing host. The most important state often lives on framework-managed Controller or Bean instances instead of starting life as scattered local variables.
|
|
81
|
+
|
|
82
|
+
That changes the reading question from:
|
|
83
|
+
|
|
84
|
+
- which composable or local variable holds this?
|
|
85
|
+
|
|
86
|
+
into:
|
|
87
|
+
|
|
88
|
+
- which Controller, Bean, or Model owns this?
|
|
89
|
+
|
|
90
|
+
That is a better scaling question because ownership drift is often the first step toward cache drift, persistence drift, and SSR drift.
|
|
91
|
+
|
|
92
|
+
For the broader authoring-model shift, read [Reading Zova for Vue Developers](/frontend/reading-zova-for-vue-developers) and [Zova vs Vue 3 Comparison](/frontend/zova-vs-vue3-comparison).
|
|
93
|
+
|
|
94
|
+
## 2. Sharing: treat scope as a first-class architectural decision
|
|
95
|
+
|
|
96
|
+
Large Vue codebases often express different sharing ranges through different mechanisms:
|
|
97
|
+
|
|
98
|
+
- component-local state
|
|
99
|
+
- parent-child props or `provide/inject`
|
|
100
|
+
- app-level stores
|
|
101
|
+
- long-lived globals or imported singletons
|
|
102
|
+
|
|
103
|
+
Zova tries to keep more of those cases inside one bean-and-container architecture.
|
|
104
|
+
|
|
105
|
+
A compact comparison is:
|
|
106
|
+
|
|
107
|
+
| Sharing range | Common Vue pattern | Zova framing |
|
|
108
|
+
| ------------------ | ------------------------------------------------------- | --------------------------------------------------- |
|
|
109
|
+
| component-internal | local state or component-local composables | `ctx`-scoped bean ownership |
|
|
110
|
+
| between-components | props/emits, parent-owned composables, `provide/inject` | hierarchical injection such as `host` or `skipSelf` |
|
|
111
|
+
| app-global | store or app-level provided state | `app`-scoped bean ownership |
|
|
112
|
+
| system-level | module singleton or long-lived imported state | `sys`-scoped bean ownership |
|
|
113
|
+
|
|
114
|
+
The key idea is simple:
|
|
115
|
+
|
|
116
|
+
> Do not start by switching mechanisms. Start by choosing the sharing boundary.
|
|
117
|
+
|
|
118
|
+
This distinction becomes especially important in SSR, where app/request state and system-level state should not be mixed casually.
|
|
119
|
+
|
|
120
|
+
Read together with [IoC and Beans](/frontend/ioc-and-beans) and [System Startup Guide](/frontend/system-startup-guide).
|
|
121
|
+
|
|
122
|
+
## 3. Model: unify multiple state families under one model boundary
|
|
123
|
+
|
|
124
|
+
In many stacks, remote cache, in-memory shared state, local persistence, cookie state, and async persistent state are handled by separate tools or utilities.
|
|
125
|
+
|
|
126
|
+
Zova Model tries to organize those families under one broader model-owned runtime.
|
|
127
|
+
|
|
128
|
+
The current model layer exposes five main state families:
|
|
129
|
+
|
|
130
|
+
- `data`
|
|
131
|
+
- `mem`
|
|
132
|
+
- `local`
|
|
133
|
+
- `cookie`
|
|
134
|
+
- `db`
|
|
135
|
+
|
|
136
|
+
This means Model is not only a request wrapper and not only another store.
|
|
137
|
+
|
|
138
|
+
Its architectural role is to give several important state categories a shared home for concerns such as:
|
|
139
|
+
|
|
140
|
+
- cache identity
|
|
141
|
+
- invalidation
|
|
142
|
+
- restore behavior
|
|
143
|
+
- persistence strategy
|
|
144
|
+
- SSR-aware hydration behavior
|
|
145
|
+
|
|
146
|
+
Read together with [Model Architecture](/frontend/model-architecture), [Model State Guide](/frontend/model-state-guide), and [Server Data](/frontend/server-data).
|
|
147
|
+
|
|
148
|
+
## 4. Resource Owner: keep resource semantics together
|
|
149
|
+
|
|
150
|
+
A large frontend resource usually needs more than a query call.
|
|
151
|
+
|
|
152
|
+
The frontend often also needs:
|
|
153
|
+
|
|
154
|
+
- schema
|
|
155
|
+
- permissions
|
|
156
|
+
- forms
|
|
157
|
+
- list and item queries
|
|
158
|
+
- create/update/delete mutations
|
|
159
|
+
- invalidation rules
|
|
160
|
+
|
|
161
|
+
If those concerns are split across pages, form helpers, store logic, and query callbacks, the resource boundary becomes hard to explain.
|
|
162
|
+
|
|
163
|
+
Zova's resource-owner direction tries to keep more of that semantic bundle under one stable owner.
|
|
164
|
+
|
|
165
|
+
A useful shorthand is:
|
|
166
|
+
|
|
167
|
+
> the page consumes resource semantics, while the model owns query and resource semantics.
|
|
168
|
+
|
|
169
|
+
That is why the Resource Owner pattern matters in large applications: it improves not only reuse, but also boundary stability.
|
|
170
|
+
|
|
171
|
+
Read together with [Model Resource Owner Pattern](/frontend/model-resource-owner-pattern) and [Using ModelResource in Your Module](/frontend/model-resource-usage-guide).
|
|
172
|
+
|
|
173
|
+
## One comparison table
|
|
174
|
+
|
|
175
|
+
| Dimension | Common large-Vue drift | Zova architectural answer |
|
|
176
|
+
| ------------------ | ----------------------------------------------------------------- | --------------------------------------------------------------- |
|
|
177
|
+
| State host | state starts locally, then moves outward case by case | assign ownership first to Controller / Bean / Model |
|
|
178
|
+
| Sharing | sharing range often changes the mechanism | sharing is framed through IoC scopes |
|
|
179
|
+
| Persistence | local, cookie, and async persistence drift into utilities | persistence lives inside model-state families |
|
|
180
|
+
| Cache identity | keys and refresh rules drift across pages and callbacks | model identity and model policy own more of the cache semantics |
|
|
181
|
+
| SSR | request-level and long-lived state can blur together | `app` and `sys` make the distinction structural |
|
|
182
|
+
| Resource semantics | schema, forms, permissions, and invalidation spread across layers | Resource Owner pulls them back under one owner |
|
|
183
|
+
|
|
184
|
+
## When this architecture matters most
|
|
185
|
+
|
|
186
|
+
This architecture is most valuable when the frontend is:
|
|
187
|
+
|
|
188
|
+
- long-lived
|
|
189
|
+
- maintained by multiple developers
|
|
190
|
+
- SSR-aware
|
|
191
|
+
- cache-heavy
|
|
192
|
+
- persistence-heavy
|
|
193
|
+
- resource-heavy
|
|
194
|
+
|
|
195
|
+
For small or short-lived projects, a lighter scattered approach may still be good enough.
|
|
196
|
+
|
|
197
|
+
The point is not that every Vue project must adopt the same architecture.
|
|
198
|
+
The point is that as complexity rises, boundary control becomes more important than adding one more tool.
|
|
199
|
+
|
|
200
|
+
## Recommended reading path
|
|
201
|
+
|
|
202
|
+
If this page matches your question, continue in this order:
|
|
203
|
+
|
|
204
|
+
1. [Reading Zova for Vue Developers](/frontend/reading-zova-for-vue-developers)
|
|
205
|
+
2. [Zova vs Vue 3 Comparison](/frontend/zova-vs-vue3-comparison)
|
|
206
|
+
3. [IoC and Beans](/frontend/ioc-and-beans)
|
|
207
|
+
4. [Model Architecture](/frontend/model-architecture)
|
|
208
|
+
5. [Model State Guide](/frontend/model-state-guide)
|
|
209
|
+
6. [Model Resource Owner Pattern](/frontend/model-resource-owner-pattern)
|
|
210
|
+
7. [System Startup Guide](/frontend/system-startup-guide)
|
|
211
|
+
|
|
212
|
+
## Final takeaway
|
|
213
|
+
|
|
214
|
+
The most important practical idea is simple:
|
|
215
|
+
|
|
216
|
+
> Large Vue projects usually do not need more state tools first. They need a state architecture that can keep ownership, scope, persistence, cache semantics, SSR behavior, and resource boundaries coherent as the system grows.
|
|
217
|
+
|
|
218
|
+
That is the main reason to read Zova's frontend architecture through Controller / Bean ownership, IoC scopes, Model, and Resource Owner, instead of mapping every state problem back to one more isolated Vue mechanism.
|
|
@@ -167,6 +167,8 @@ The legacy system-start docs used route registration as the clearest example bou
|
|
|
167
167
|
|
|
168
168
|
That distinction is especially important in SSR-capable systems, where app lifecycles can repeat while system-level route and config wiring should not.
|
|
169
169
|
|
|
170
|
+
If your main question is how this `app` versus `sys` lifecycle split participates in the broader state architecture of a large Vue application, read [State Architecture for Vue Developers](/frontend/state-architecture-for-vue-developers) together with [IoC and Beans](/frontend/ioc-and-beans).
|
|
171
|
+
|
|
170
172
|
A practical reading sequence is:
|
|
171
173
|
|
|
172
174
|
1. [Environment and Config Guide](/frontend/environment-config-guide)
|
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
# `$useStateData` Best Practices
|
|
2
|
+
|
|
3
|
+
This guide explains how to use `$useStateData(...)` well in Zova, especially when you are deciding whether a piece of async frontend data should behave like render-time state or interaction-time ad hoc fetch logic.
|
|
4
|
+
|
|
5
|
+
Read [Model Architecture](/frontend/model-architecture) first if you want the broader role of Model.
|
|
6
|
+
|
|
7
|
+
Read [Model State Guide](/frontend/model-state-guide) first if you want the larger helper-family overview.
|
|
8
|
+
|
|
9
|
+
Use this page when your real design question is one of these:
|
|
10
|
+
|
|
11
|
+
- should this async value live in a model query or stay inside one controller?
|
|
12
|
+
- should the query be established during render or only during interaction?
|
|
13
|
+
- should I use `enabled: false` or let the query participate in the normal lifecycle?
|
|
14
|
+
- how should I think about `disableSuspenseOnInit: true`?
|
|
15
|
+
- if I remove a manual `ensure...()` helper, can interaction still wait for query readiness when needed?
|
|
16
|
+
|
|
17
|
+
## Why this page exists
|
|
18
|
+
|
|
19
|
+
`$useStateData(...)` is easy to misuse if it is treated only as a request helper.
|
|
20
|
+
|
|
21
|
+
The larger value is architectural:
|
|
22
|
+
|
|
23
|
+
- model-owned async state gets one stable owner
|
|
24
|
+
- query identity, persistence, restore, and refetch behavior stay in one place
|
|
25
|
+
- render logic can consume a formal query state instead of rebuilding mini loading/cache systems in controllers
|
|
26
|
+
|
|
27
|
+
The common failure mode is not that the code stops working immediately.
|
|
28
|
+
|
|
29
|
+
The common failure mode is that the code keeps working while the state model becomes increasingly command-oriented, duplicated, and difficult to reason about.
|
|
30
|
+
|
|
31
|
+
## The core mental model
|
|
32
|
+
|
|
33
|
+
The best starting point is simple:
|
|
34
|
+
|
|
35
|
+
> treat `$useStateData(...)` as a model-owned async state source, not only as a function that happens to fetch data.
|
|
36
|
+
|
|
37
|
+
That changes several downstream decisions.
|
|
38
|
+
|
|
39
|
+
If a value affects:
|
|
40
|
+
|
|
41
|
+
- whether a button can be clicked
|
|
42
|
+
- accepted file types
|
|
43
|
+
- maximum upload size
|
|
44
|
+
- whether a UI branch is shown
|
|
45
|
+
- whether a flow can continue safely
|
|
46
|
+
|
|
47
|
+
then that value is usually not just a click-time fetch result.
|
|
48
|
+
It is part of the page's real interaction state.
|
|
49
|
+
|
|
50
|
+
## Practical rule 1: prefer model ownership for reusable async state
|
|
51
|
+
|
|
52
|
+
When a value has broader reuse, persistence, or lifecycle requirements, prefer model ownership.
|
|
53
|
+
|
|
54
|
+
Typical good fits are:
|
|
55
|
+
|
|
56
|
+
- upload policy by scene
|
|
57
|
+
- resource bootstrap
|
|
58
|
+
- schema metadata
|
|
59
|
+
- permission/capability state
|
|
60
|
+
- query-style domain state reused across pages or components
|
|
61
|
+
|
|
62
|
+
Typical weak fits are:
|
|
63
|
+
|
|
64
|
+
- a one-off page-local toggle
|
|
65
|
+
- a tiny local interaction detail with no reuse value
|
|
66
|
+
- temporary page-only state that does not benefit from caching or restore semantics
|
|
67
|
+
|
|
68
|
+
### Review question
|
|
69
|
+
|
|
70
|
+
Ask:
|
|
71
|
+
|
|
72
|
+
> if another controller needed this same async value tomorrow, would I want the ownership and lifecycle policy to already exist in one model?
|
|
73
|
+
|
|
74
|
+
If the answer is yes, that state usually belongs in a model.
|
|
75
|
+
|
|
76
|
+
## Practical rule 2: if the value affects render or interaction rules, establish the query during render
|
|
77
|
+
|
|
78
|
+
If a value determines runtime interaction conditions, prefer this shape:
|
|
79
|
+
|
|
80
|
+
1. model exposes `getXxx(...)`
|
|
81
|
+
2. controller/page establishes the query during render
|
|
82
|
+
3. render consumes `data`, `pending`, and derived values
|
|
83
|
+
4. interaction consumes that existing query state instead of inventing a second lifecycle
|
|
84
|
+
|
|
85
|
+
### Why this is good
|
|
86
|
+
|
|
87
|
+
This keeps the UI honest.
|
|
88
|
+
|
|
89
|
+
The rendered UI can already know:
|
|
90
|
+
|
|
91
|
+
- whether interaction should wait
|
|
92
|
+
- which accept/multiple flags apply
|
|
93
|
+
- which validation path should be used
|
|
94
|
+
|
|
95
|
+
### Smell
|
|
96
|
+
|
|
97
|
+
A controller renders as though the interaction is ready, but clicking the button is what really begins the important state lookup.
|
|
98
|
+
|
|
99
|
+
That is a sign that a query-state concern may have been pushed too far into imperative interaction code.
|
|
100
|
+
|
|
101
|
+
## Practical rule 3: `disableSuspenseOnInit: true` disables the init-time `query.suspense()` kick, not the query itself
|
|
102
|
+
|
|
103
|
+
This point needs precise wording.
|
|
104
|
+
|
|
105
|
+
Current model runtime shows that `$useStateData(...)` creates the query wrapper first, then, only on first creation, optionally calls `query.suspense()`:
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
if (!this[SymbolUseQueries][queryHash]) {
|
|
109
|
+
const useQuery = this.$useQuery(options, queryClient);
|
|
110
|
+
this[SymbolUseQueries][queryHash] = useQuery;
|
|
111
|
+
if (!options.meta?.disableSuspenseOnInit) {
|
|
112
|
+
useQuery.suspense();
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Two details matter:
|
|
118
|
+
|
|
119
|
+
- the query is already created either way
|
|
120
|
+
- the runtime does **not** use `await` there
|
|
121
|
+
|
|
122
|
+
So `disableSuspenseOnInit: true` does **not** mean “block vs do not block render” in a strict synchronous sense.
|
|
123
|
+
|
|
124
|
+
It more precisely means:
|
|
125
|
+
|
|
126
|
+
- with default behavior, the first consumer of that query immediately kicks one init-time `query.suspense()` call
|
|
127
|
+
- with `disableSuspenseOnInit: true`, that automatic init-time kick is skipped
|
|
128
|
+
|
|
129
|
+
This option is often a good fit when you want:
|
|
130
|
+
|
|
131
|
+
- query state to exist immediately
|
|
132
|
+
- persistence/restore/refetch behavior to remain normal
|
|
133
|
+
- no automatic init-time `query.suspense()` kick on first creation
|
|
134
|
+
|
|
135
|
+
Representative pattern:
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
getUploadPolicy(imageScene?: string) {
|
|
139
|
+
if (!imageScene) return undefined;
|
|
140
|
+
return this.$useStateData({
|
|
141
|
+
queryKey: ['uploadPolicy', 'image', imageScene],
|
|
142
|
+
queryFn: async () => {
|
|
143
|
+
return this.scope.api.image.getUploadPolicy({ imageScene });
|
|
144
|
+
},
|
|
145
|
+
meta: {
|
|
146
|
+
disableSuspenseOnInit: true,
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### What it does not mean
|
|
153
|
+
|
|
154
|
+
It does not mean:
|
|
155
|
+
|
|
156
|
+
- no query should be created
|
|
157
|
+
- no request should run
|
|
158
|
+
- no persisted value should be restored
|
|
159
|
+
- no refetch should happen later
|
|
160
|
+
- interaction can never explicitly wait for query readiness
|
|
161
|
+
|
|
162
|
+
That distinction matters a lot.
|
|
163
|
+
|
|
164
|
+
## Practical rule 4: distinguish three different semantics
|
|
165
|
+
|
|
166
|
+
A useful review habit is to keep these three behaviors separate.
|
|
167
|
+
|
|
168
|
+
| Case | What happens | What it does **not** mean | Best fit |
|
|
169
|
+
| --------------------------------------------------------------- | ---------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
|
|
170
|
+
| Default `$useStateData(...)` | On first query creation, runtime immediately calls `query.suspense()` once | It does **not** synchronously `await` readiness in that call site | Data where “first consumer should eagerly kick one refresh/update semantic” is a good default |
|
|
171
|
+
| `$useStateData(..., { meta: { disableSuspenseOnInit: true } })` | Query still exists, but the automatic init-time `query.suspense()` kick is skipped | It does **not** disable the query, persistence, restore, or later refetch behavior | Data that is relatively stable inside the current frontend process and does not need an automatic first-consumer kick every time |
|
|
172
|
+
| `await query.suspense()` | Current logic sequence explicitly waits until the query is ready | It is **not** merely a hint or passive kickoff | Interaction or orchestration boundaries where later logic must not continue until the value is ready |
|
|
173
|
+
|
|
174
|
+
Two practical consequences follow from this table:
|
|
175
|
+
|
|
176
|
+
1. multiple synchronous `query.suspense()` calls can still deduplicate at the query runtime level
|
|
177
|
+
2. only explicit `await query.suspense()` introduces a true sequencing guarantee for the current logic path
|
|
178
|
+
|
|
179
|
+
## Practical rule 5: be cautious with `enabled: false` and `staleTime: Infinity`
|
|
180
|
+
|
|
181
|
+
These options are useful, but they are easy to over-apply.
|
|
182
|
+
|
|
183
|
+
### `enabled: false`
|
|
184
|
+
|
|
185
|
+
Use it when the query should not exist yet because a real prerequisite is missing.
|
|
186
|
+
|
|
187
|
+
Good examples:
|
|
188
|
+
|
|
189
|
+
- required params are absent
|
|
190
|
+
- the page is in a branch where the query truly does not apply
|
|
191
|
+
- the query would be meaningless or invalid before some explicit condition
|
|
192
|
+
|
|
193
|
+
Be cautious when the real motivation is only:
|
|
194
|
+
|
|
195
|
+
- “I want to fetch later when the user clicks”
|
|
196
|
+
|
|
197
|
+
That often means you are taking a state concern and turning it back into a command concern.
|
|
198
|
+
|
|
199
|
+
### `staleTime: Infinity`
|
|
200
|
+
|
|
201
|
+
Use it when the value is effectively static for the relevant session.
|
|
202
|
+
|
|
203
|
+
Be cautious when the underlying backend configuration may change.
|
|
204
|
+
|
|
205
|
+
If backend scene config can change, letting the query participate in normal freshness/refetch rules is usually healthier than freezing it forever.
|
|
206
|
+
|
|
207
|
+
## Practical rule 6: removing a manual `ensure...()` helper does not mean interaction can never wait
|
|
208
|
+
|
|
209
|
+
A common over-correction is:
|
|
210
|
+
|
|
211
|
+
- first version: controller manually owns fetching through `ensure...()`
|
|
212
|
+
- second version: remove `ensure...()` and assume interaction must never wait for anything
|
|
213
|
+
|
|
214
|
+
The better middle ground is:
|
|
215
|
+
|
|
216
|
+
- query lifecycle remains model-owned
|
|
217
|
+
- query is established during render
|
|
218
|
+
- interaction consumes the existing query
|
|
219
|
+
- if an edge timing window appears, interaction may wait for the existing query to finish
|
|
220
|
+
|
|
221
|
+
That is very different from rebuilding a second fetch lifecycle.
|
|
222
|
+
|
|
223
|
+
## Practical rule 7: derive render-time state once per render when possible
|
|
224
|
+
|
|
225
|
+
Even when the query object is reused, a controller can still become noisy if it repeatedly derives the same values in several helper calls.
|
|
226
|
+
|
|
227
|
+
A good cleanup pattern is to derive one local state object such as:
|
|
228
|
+
|
|
229
|
+
- `acceptAttr`
|
|
230
|
+
- `multiple`
|
|
231
|
+
- `pending`
|
|
232
|
+
|
|
233
|
+
and let input/button/render branches consume that shared result.
|
|
234
|
+
|
|
235
|
+
### Why this helps
|
|
236
|
+
|
|
237
|
+
It makes one render pass easier to understand:
|
|
238
|
+
|
|
239
|
+
- what query-backed state is being consumed
|
|
240
|
+
- which UI props come from that state
|
|
241
|
+
- where the interaction gate really is
|
|
242
|
+
|
|
243
|
+
### Smell
|
|
244
|
+
|
|
245
|
+
The controller repeatedly:
|
|
246
|
+
|
|
247
|
+
- grabs the same query
|
|
248
|
+
- re-reads the same policy data
|
|
249
|
+
- re-derives the same `multiple` or `accept` value in several places
|
|
250
|
+
|
|
251
|
+
That is often a sign that local render-time derivation should be consolidated.
|
|
252
|
+
|
|
253
|
+
## Practical rule 8: design query keys so the ownership boundary is obvious
|
|
254
|
+
|
|
255
|
+
Prefer keys that make the domain explicit.
|
|
256
|
+
|
|
257
|
+
For example:
|
|
258
|
+
|
|
259
|
+
- `['uploadPolicy', 'image', imageScene]`
|
|
260
|
+
- `['uploadPolicy', 'file', fileScene]`
|
|
261
|
+
|
|
262
|
+
This is usually better than ambiguous cross-domain shapes such as:
|
|
263
|
+
|
|
264
|
+
- `['uploadPolicy', scene]`
|
|
265
|
+
|
|
266
|
+
### Why this matters
|
|
267
|
+
|
|
268
|
+
Readable keys improve:
|
|
269
|
+
|
|
270
|
+
- debugging
|
|
271
|
+
- tracing cache behavior
|
|
272
|
+
- future refactors when similar query families grow
|
|
273
|
+
|
|
274
|
+
## Anti-pattern: command-first controller state
|
|
275
|
+
|
|
276
|
+
A common anti-pattern looks like this:
|
|
277
|
+
|
|
278
|
+
- model query is disabled by default
|
|
279
|
+
- controller clicks trigger `ensure...()`
|
|
280
|
+
- controller maintains the important timing logic itself
|
|
281
|
+
- render does not fully know the interaction conditions yet
|
|
282
|
+
|
|
283
|
+
### Why this is harmful
|
|
284
|
+
|
|
285
|
+
- the model no longer owns the full lifecycle cleanly
|
|
286
|
+
- restore/refetch semantics are weakened or bypassed
|
|
287
|
+
- controller responsibilities grow too large
|
|
288
|
+
- interaction rules become harder to inspect from the render path
|
|
289
|
+
|
|
290
|
+
## Better pattern: model-owned query, render-owned derivation, interaction-owned orchestration
|
|
291
|
+
|
|
292
|
+
A healthier split is:
|
|
293
|
+
|
|
294
|
+
### Model owns
|
|
295
|
+
|
|
296
|
+
- `queryKey`
|
|
297
|
+
- `queryFn`
|
|
298
|
+
- persistence/restore/refetch lifecycle
|
|
299
|
+
- the stable meaning of the async state
|
|
300
|
+
|
|
301
|
+
### Controller/page owns
|
|
302
|
+
|
|
303
|
+
- how the query-backed state is rendered
|
|
304
|
+
- derived props needed by the current UI
|
|
305
|
+
- user interaction flow
|
|
306
|
+
- edge-timing waits on the already-existing query when necessary
|
|
307
|
+
|
|
308
|
+
This creates a much cleaner division of responsibilities.
|
|
309
|
+
|
|
310
|
+
## Representative examples
|
|
311
|
+
|
|
312
|
+
### Example A: resource entry pages usually prefer the default init-time kick
|
|
313
|
+
|
|
314
|
+
A good counterexample to overusing `disableSuspenseOnInit: true` is the resource entry-page branch.
|
|
315
|
+
|
|
316
|
+
Useful reading pages are:
|
|
317
|
+
|
|
318
|
+
- [Resource Entry Page Deep Dive](/frontend/resource-entry-page-deep-dive)
|
|
319
|
+
- [ModelResource Internals Deep Dive](/frontend/model-resource-internals-deep-dive)
|
|
320
|
+
|
|
321
|
+
In that branch, the practical expectation is often:
|
|
322
|
+
|
|
323
|
+
- opening a row page may show an already-known value first
|
|
324
|
+
- but entering that page should still eagerly kick one update semantic for the item-level state
|
|
325
|
+
- the page benefits from the default “first consumer kicks `query.suspense()` once” behavior
|
|
326
|
+
|
|
327
|
+
This makes entry pages a good mental model for queries where the default init-time kick is healthy:
|
|
328
|
+
|
|
329
|
+
- the query is a real page-state dependency
|
|
330
|
+
- the first consumer should actively nudge freshness
|
|
331
|
+
- you usually do not want every caller to remember to add that kickoff manually
|
|
332
|
+
|
|
333
|
+
This is not about synchronous blocking.
|
|
334
|
+
It is about preserving the default eager-init semantic when the first consumer of that page state appears.
|
|
335
|
+
|
|
336
|
+
### Example B: upload policy prefers `disableSuspenseOnInit: true`
|
|
337
|
+
|
|
338
|
+
Representative files in the Cabloy Basic frontend:
|
|
339
|
+
|
|
340
|
+
- `zova/src/suite/cabloy-basic/modules/basic-image/src/model/image.ts`
|
|
341
|
+
- `zova/src/suite/cabloy-basic/modules/basic-file/src/model/file.ts`
|
|
342
|
+
- `zova/src/suite/cabloy-basic/modules/basic-image/src/component/formFieldImage/controller.tsx`
|
|
343
|
+
- `zova/src/suite/cabloy-basic/modules/basic-file/src/component/formFieldFile/controller.tsx`
|
|
344
|
+
|
|
345
|
+
### What this example demonstrates
|
|
346
|
+
|
|
347
|
+
- `getUploadPolicy(...)` stays in the model
|
|
348
|
+
- the query is created as formal state instead of a one-off click helper
|
|
349
|
+
- `disableSuspenseOnInit: true` skips the automatic init-time `query.suspense()` kick while preserving the query as formal state
|
|
350
|
+
- render derives `acceptAttr`, `multiple`, and `pending`
|
|
351
|
+
- interaction reuses that state
|
|
352
|
+
- interaction may still `await query.suspense()` on the already-created query if an edge timing window requires it
|
|
353
|
+
|
|
354
|
+
Why this is a good fit:
|
|
355
|
+
|
|
356
|
+
- upload policy is a real query-backed state
|
|
357
|
+
- it is relatively stable within the current frontend process
|
|
358
|
+
- every first consumer does not need to auto-kick one eager init-time refresh semantic
|
|
359
|
+
- the strict-ready moment is the interaction boundary, not every initial render consumer
|
|
360
|
+
|
|
361
|
+
Taken together, Example A and Example B show the most important design contrast on this page:
|
|
362
|
+
|
|
363
|
+
- entry-page item state is a good example of why the default init-time kick can be valuable
|
|
364
|
+
- upload policy is a good example of why a stable state may still be a formal query while skipping that init-time kick
|
|
365
|
+
|
|
366
|
+
## Checklist before you use `$useStateData(...)`
|
|
367
|
+
|
|
368
|
+
Ask these questions:
|
|
369
|
+
|
|
370
|
+
1. Is this value really a reusable async state source rather than a one-off action result?
|
|
371
|
+
2. Will this value affect render or interaction conditions?
|
|
372
|
+
3. Should another page or controller be able to consume the same lifecycle and cache policy later?
|
|
373
|
+
4. Is `disableSuspenseOnInit: true` enough, instead of disabling the query entirely?
|
|
374
|
+
5. Would `enabled: false` or `staleTime: Infinity` accidentally cut off useful restore/refetch behavior?
|
|
375
|
+
6. If interaction needs a safety wait, can it wait on the existing query rather than reintroducing a manual `ensure...()` lifecycle?
|
|
376
|
+
7. Can I derive the render-time state once and reuse it across the UI branch?
|
|
377
|
+
|
|
378
|
+
## Final takeaway
|
|
379
|
+
|
|
380
|
+
The most important usage insight is simple:
|
|
381
|
+
|
|
382
|
+
> `$useStateData(...)` is strongest when it models async state that the UI can render from, persist, restore, and refresh naturally.
|
|
383
|
+
|
|
384
|
+
When that mindset is clear, controllers become thinner, models become more authoritative, and query lifecycle decisions become easier to reason about.
|