cabloy 5.1.50 → 5.1.51
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/.claude/skills/cabloy-backend-scaffold/SKILL.md +207 -0
- package/.claude/skills/cabloy-backend-scaffold/evals/evals.json +29 -0
- package/.claude/skills/cabloy-backend-scaffold/references/backend-thread-map.md +52 -0
- package/.claude/skills/cabloy-backend-scaffold/references/follow-up-checklist.md +39 -0
- package/.claude/skills/cabloy-contract-loop/SKILL.md +201 -0
- package/.claude/skills/cabloy-contract-loop/evals/evals.json +29 -0
- package/.claude/skills/cabloy-contract-loop/references/contract-loop-map.md +47 -0
- package/.claude/skills/cabloy-contract-loop/references/verification-checklist.md +32 -0
- package/.claude/skills/cabloy-frontend-scaffold/SKILL.md +230 -0
- package/.claude/skills/cabloy-frontend-scaffold/evals/evals.json +35 -0
- package/.claude/skills/cabloy-frontend-scaffold/references/follow-up-checklist.md +41 -0
- package/.claude/skills/cabloy-frontend-scaffold/references/frontend-thread-map.md +54 -0
- package/.claude/skills/cabloy-workflow/SKILL.md +288 -0
- package/.claude/skills/cabloy-workflow/evals/evals.json +35 -0
- package/.claude/skills/cabloy-workflow/references/cli-strategy.md +39 -0
- package/.claude/skills/cabloy-workflow/references/edition-detection.md +29 -0
- package/.github/workflows/docs-pages.yml +54 -0
- package/.gitignore +1 -0
- package/CHANGELOG.md +29 -0
- package/CLAUDE.md +59 -0
- package/README.md +137 -0
- package/cabloy-docs/.vitepress/config.mjs +222 -0
- package/cabloy-docs/.vitepress/public/CNAME +1 -0
- package/cabloy-docs/.vitepress/theme/custom.css +64 -0
- package/cabloy-docs/.vitepress/theme/edition-badges.md +5 -0
- package/cabloy-docs/.vitepress/theme/index.js +5 -0
- package/cabloy-docs/ai/class-placement-rule.md +138 -0
- package/cabloy-docs/ai/cli-for-agents.md +56 -0
- package/cabloy-docs/ai/cli-to-skill-map.md +165 -0
- package/cabloy-docs/ai/docs-skills-rules-mapping.md +167 -0
- package/cabloy-docs/ai/edition-detection.md +30 -0
- package/cabloy-docs/ai/future-skill-roadmap.md +135 -0
- package/cabloy-docs/ai/global-bean-lookup.md +157 -0
- package/cabloy-docs/ai/introduction.md +62 -0
- package/cabloy-docs/ai/playbook-backend-module.md +111 -0
- package/cabloy-docs/ai/playbook-contract-regeneration.md +100 -0
- package/cabloy-docs/ai/playbook-frontend-page.md +99 -0
- package/cabloy-docs/ai/playbook-metadata-refresh.md +67 -0
- package/cabloy-docs/ai/repo-guidance.md +58 -0
- package/cabloy-docs/ai/rules-and-config.md +29 -0
- package/cabloy-docs/ai/skills.md +35 -0
- package/cabloy-docs/ai/verification.md +30 -0
- package/cabloy-docs/backend/aop-overview.md +128 -0
- package/cabloy-docs/backend/auth-guide.md +151 -0
- package/cabloy-docs/backend/backend-essentials.md +128 -0
- package/cabloy-docs/backend/broadcast-guide.md +138 -0
- package/cabloy-docs/backend/cache-guide.md +70 -0
- package/cabloy-docs/backend/captcha-guide.md +162 -0
- package/cabloy-docs/backend/cli.md +173 -0
- package/cabloy-docs/backend/config-guide.md +249 -0
- package/cabloy-docs/backend/controller-aop-guide.md +270 -0
- package/cabloy-docs/backend/controller-guide.md +347 -0
- package/cabloy-docs/backend/crud-workflow.md +118 -0
- package/cabloy-docs/backend/dto-guide.md +161 -0
- package/cabloy-docs/backend/dto-infer-generation.md +153 -0
- package/cabloy-docs/backend/dynamic-datasource-guide.md +70 -0
- package/cabloy-docs/backend/election-guide.md +141 -0
- package/cabloy-docs/backend/entity-guide.md +150 -0
- package/cabloy-docs/backend/error-guide.md +108 -0
- package/cabloy-docs/backend/event-guide.md +183 -0
- package/cabloy-docs/backend/external-aop-guide.md +149 -0
- package/cabloy-docs/backend/field-indexes.md +79 -0
- package/cabloy-docs/backend/foundation.md +281 -0
- package/cabloy-docs/backend/i18n-guide.md +211 -0
- package/cabloy-docs/backend/internal-aop-guide.md +181 -0
- package/cabloy-docs/backend/introduction.md +95 -0
- package/cabloy-docs/backend/jwt-guide.md +276 -0
- package/cabloy-docs/backend/logger-guide.md +223 -0
- package/cabloy-docs/backend/mail-guide.md +189 -0
- package/cabloy-docs/backend/menu-guide.md +80 -0
- package/cabloy-docs/backend/migration-and-changes.md +192 -0
- package/cabloy-docs/backend/model-guide.md +274 -0
- package/cabloy-docs/backend/multi-database-datasource.md +171 -0
- package/cabloy-docs/backend/multi-instance-and-instance-resolution.md +196 -0
- package/cabloy-docs/backend/openapi-guide.md +118 -0
- package/cabloy-docs/backend/orm-aggregate-group-guide.md +210 -0
- package/cabloy-docs/backend/orm-configuration-guide.md +165 -0
- package/cabloy-docs/backend/orm-guide.md +109 -0
- package/cabloy-docs/backend/orm-mutation-guide.md +195 -0
- package/cabloy-docs/backend/orm-select-guide.md +243 -0
- package/cabloy-docs/backend/queue-guide.md +271 -0
- package/cabloy-docs/backend/quickstart.md +141 -0
- package/cabloy-docs/backend/redis-guide.md +70 -0
- package/cabloy-docs/backend/redlock-guide.md +60 -0
- package/cabloy-docs/backend/relations-guide.md +250 -0
- package/cabloy-docs/backend/runtime-and-flavors.md +304 -0
- package/cabloy-docs/backend/schedule-guide.md +81 -0
- package/cabloy-docs/backend/scripts.md +116 -0
- package/cabloy-docs/backend/serialization-guide.md +192 -0
- package/cabloy-docs/backend/service-guide.md +166 -0
- package/cabloy-docs/backend/sharding-guide.md +49 -0
- package/cabloy-docs/backend/startup-guide.md +326 -0
- package/cabloy-docs/backend/transaction-guide.md +82 -0
- package/cabloy-docs/backend/unit-testing.md +209 -0
- package/cabloy-docs/backend/upload-guide.md +160 -0
- package/cabloy-docs/backend/user-access-guide.md +157 -0
- package/cabloy-docs/backend/validation-guide.md +80 -0
- package/cabloy-docs/backend/worker-guide.md +59 -0
- package/cabloy-docs/editions/cabloy-basic.md +25 -0
- package/cabloy-docs/editions/cabloy-start.md +24 -0
- package/cabloy-docs/editions/detection.md +31 -0
- package/cabloy-docs/editions/overview.md +44 -0
- package/cabloy-docs/frontend/api-guide.md +93 -0
- package/cabloy-docs/frontend/api-schema-guide.md +43 -0
- package/cabloy-docs/frontend/app-startup-guide.md +185 -0
- package/cabloy-docs/frontend/cli.md +78 -0
- package/cabloy-docs/frontend/component-guide.md +105 -0
- package/cabloy-docs/frontend/component-props-guide.md +97 -0
- package/cabloy-docs/frontend/component-v-model-guide.md +110 -0
- package/cabloy-docs/frontend/css-in-js-guide.md +151 -0
- package/cabloy-docs/frontend/design-principles.md +55 -0
- package/cabloy-docs/frontend/environment-config-guide.md +250 -0
- package/cabloy-docs/frontend/foundation.md +57 -0
- package/cabloy-docs/frontend/generic-component-guide.md +35 -0
- package/cabloy-docs/frontend/icon-engine-guide.md +88 -0
- package/cabloy-docs/frontend/introduction.md +32 -0
- package/cabloy-docs/frontend/ioc-and-beans.md +211 -0
- package/cabloy-docs/frontend/mock-guide.md +109 -0
- package/cabloy-docs/frontend/model-architecture.md +87 -0
- package/cabloy-docs/frontend/model-state-guide.md +70 -0
- package/cabloy-docs/frontend/module-scope.md +168 -0
- package/cabloy-docs/frontend/modules-and-suites.md +179 -0
- package/cabloy-docs/frontend/navigation-guards-guide.md +68 -0
- package/cabloy-docs/frontend/openapi-sdk-guide.md +102 -0
- package/cabloy-docs/frontend/page-guide.md +223 -0
- package/cabloy-docs/frontend/page-params-guide.md +87 -0
- package/cabloy-docs/frontend/page-query-guide.md +96 -0
- package/cabloy-docs/frontend/page-route-guide.md +147 -0
- package/cabloy-docs/frontend/quickstart.md +201 -0
- package/cabloy-docs/frontend/route-alias-guide.md +61 -0
- package/cabloy-docs/frontend/scripts.md +86 -0
- package/cabloy-docs/frontend/sdk-guide.md +45 -0
- package/cabloy-docs/frontend/server-data.md +74 -0
- package/cabloy-docs/frontend/ssr-client-only.md +40 -0
- package/cabloy-docs/frontend/ssr-env.md +51 -0
- package/cabloy-docs/frontend/ssr-init-data.md +46 -0
- package/cabloy-docs/frontend/ssr-overview.md +48 -0
- package/cabloy-docs/frontend/ssr-seo-meta.md +52 -0
- package/cabloy-docs/frontend/system-startup-guide.md +186 -0
- package/cabloy-docs/frontend/theme-guide.md +154 -0
- package/cabloy-docs/frontend/zod-guide.md +161 -0
- package/cabloy-docs/fullstack/edition-collaboration-differences.md +61 -0
- package/cabloy-docs/fullstack/frontend-metadata-to-backend.md +64 -0
- package/cabloy-docs/fullstack/introduction.md +69 -0
- package/cabloy-docs/fullstack/openapi-to-sdk.md +116 -0
- package/cabloy-docs/fullstack/quickstart.md +86 -0
- package/cabloy-docs/fullstack/vona-zova-integration.md +86 -0
- package/cabloy-docs/index.md +73 -0
- package/cabloy-docs/package.json +16 -0
- package/cabloy-docs/pnpm-lock.yaml +1607 -0
- package/cabloy-docs/reference/backend-directory-structure.md +88 -0
- package/cabloy-docs/reference/cli-reference.md +49 -0
- package/cabloy-docs/reference/glossary.md +38 -0
- package/cabloy-docs/reference/package-map.md +105 -0
- package/cabloy-docs/reference/repo-scripts.md +36 -0
- package/package.json +4 -1
- package/scripts/init.ts +12 -0
- package/scripts/upgrade.ts +31 -3
- package/vona/README.md +3 -3
- package/vona/README.zh-CN.md +4 -4
- package/vona/packages-vona/vona/package.json +1 -1
- package/vona/src/suite-vendor/a-cabloy/modules/a-datasharding/package.json +1 -1
- package/vona/src/suite-vendor/a-cabloy/modules/a-datasharding/src/bean/summerCache.datasourceWrite.ts +2 -2
- package/vona/src/suite-vendor/a-cabloy/package.json +1 -1
- package/vona/src/suite-vendor/a-captcha/modules/a-captcha/package.json +1 -1
- package/vona/src/suite-vendor/a-captcha/modules/a-captcha/src/bean/cacheRedis.captcha.ts +2 -2
- package/vona/src/suite-vendor/a-captcha/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-bean/cli/bean/metadata/generate.ts +5 -6
- package/vona/src/suite-vendor/a-vona/modules/a-bean/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-cache/cli/cacheMem/boilerplate/{{sceneName}}.{{beanName}}.ts_ +2 -2
- package/vona/src/suite-vendor/a-vona/modules/a-cache/cli/cacheRedis/boilerplate/{{sceneName}}.{{beanName}}.ts_ +2 -2
- package/vona/src/suite-vendor/a-vona/modules/a-cache/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-cache/src/.metadata/index.ts +13 -2
- package/vona/src/suite-vendor/a-vona/modules/a-cache/src/bean/bean.cache.ts +6 -7
- package/vona/src/suite-vendor/a-vona/modules/a-cache/src/{bean/bean.cacheMemBase.ts → service/cacheMemBase_.ts} +3 -3
- package/vona/src/suite-vendor/a-vona/modules/a-cache/src/{bean/bean.cacheRedisBase.ts → service/cacheRedisBase_.ts} +3 -3
- package/vona/src/suite-vendor/a-vona/modules/a-mailconfirm/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-mailconfirm/src/bean/cacheRedis.emailConfirm.ts +2 -2
- package/vona/src/suite-vendor/a-vona/modules/a-mailconfirm/src/bean/cacheRedis.passwordReset.ts +2 -2
- package/vona/src/suite-vendor/a-vona/modules/a-openapi/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-openapi/src/bean/summerCache.json.ts +2 -2
- package/vona/src/suite-vendor/a-vona/modules/a-orm/cli/databaseDialect/boilerplate/{{sceneName}}.{{beanName}}.ts_ +2 -2
- 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/.metadata/index.ts +4 -3
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/bean/bean.database.ts +3 -3
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/bean/bean.model.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/bean/schedule.softDeletionPrune.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/common/utils.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/index.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/{bean → lib}/bean.model/bean.model_cache.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/{bean → lib}/bean.model/bean.model_meta.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/{bean → lib}/bean.modelBase.ts +0 -5
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/dto/dtoAggregate.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/dto/dtoCreate.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/dto/dtoGet.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/dto/dtoGroup.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/dto/dtoMutate.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/dto/dtoSelectAndCount.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/dto/dtoUpdate.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/index.ts +1 -0
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/modelCacheBase.ts +3 -3
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/relations.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/relationsDynamic.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/relationsMutate.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/relationsStatic.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/service/cacheEntity_.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/service/cacheQuery_.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/service/database.ts +2 -2
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/{bean/bean.databaseDialectBase.ts → service/databaseDialectBase_.ts} +38 -41
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/service/db_.ts +2 -2
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/service/relations_.ts +3 -3
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/types/database.ts +0 -5
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/types/dto/dtoGet.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/types/dto/dtoMutate.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/types/dto/dtoSelectAndCount.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/types/model.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/types/modelAggr.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/types/modelCount.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/types/modelGeneral.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/types/modelGroup.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/types/modelIncrement.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/types/relations.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/types/relationsColumns.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/types/relationsDef.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/types/relationsDefDynamic.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/types/relationsDefMutate.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/types/relationsMutate.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/types/relationsTables.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-ormdialect/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-ormdialect/src/bean/databaseDialect.betterSqlite3.ts +2 -2
- package/vona/src/suite-vendor/a-vona/modules/a-ormdialect/src/bean/databaseDialect.mysql.ts +2 -2
- package/vona/src/suite-vendor/a-vona/modules/a-ormdialect/src/bean/databaseDialect.pg.ts +2 -2
- 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/bean/bean.permission.ts +109 -40
- package/vona/src/suite-vendor/a-vona/modules/a-permission/src/bean/summerCache.permission.ts +2 -2
- 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/bean/cacheRedis.startupDebounce.ts +2 -2
- package/vona/src/suite-vendor/a-vona/modules/a-summer/cli/boilerplate/{{sceneName}}.{{beanName}}.ts_ +2 -2
- package/vona/src/suite-vendor/a-vona/modules/a-summer/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-summer/src/.metadata/index.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-summer/src/bean/bean.summer.ts +3 -3
- package/vona/src/suite-vendor/a-vona/modules/a-summer/src/service/localMem_.ts +3 -3
- package/vona/src/suite-vendor/a-vona/modules/a-summer/src/service/localRedis_.ts +3 -3
- package/vona/src/suite-vendor/a-vona/modules/a-summer/src/{bean/bean.summerCacheBase.ts → service/summerCacheBase_.ts} +3 -3
- package/vona/src/suite-vendor/a-vona/modules/a-swagger/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-swagger/src/bean/summerCache.rapidoc.ts +2 -2
- package/vona/src/suite-vendor/a-vona/modules/a-swagger/src/bean/summerCache.swagger.ts +2 -2
- 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/bean/cacheRedis.authToken.ts +2 -2
- package/vona/src/suite-vendor/a-vona/modules/a-worker/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-worker/src/bean/cacheRedis.workerAlive.ts +2 -2
- package/vona/src/suite-vendor/a-vona/package.json +1 -1
- package/zova/README.md +4 -4
- package/zova/README.zh-CN.md +4 -4
- /package/vona/src/suite-vendor/a-vona/modules/a-orm/src/{bean → lib}/bean.model/bean.model_crud.ts +0 -0
- /package/vona/src/suite-vendor/a-vona/modules/a-orm/src/{bean → lib}/bean.model/bean.model_crud_inner.ts +0 -0
- /package/vona/src/suite-vendor/a-vona/modules/a-orm/src/{bean → lib}/bean.model/bean.model_crud_table.ts +0 -0
- /package/vona/src/suite-vendor/a-vona/modules/a-orm/src/{bean → lib}/bean.model/bean.model_knex.ts +0 -0
- /package/vona/src/suite-vendor/a-vona/modules/a-orm/src/{bean → lib}/bean.model/bean.model_utils.ts +0 -0
- /package/vona/src/suite-vendor/a-vona/modules/a-orm/src/{bean → lib}/bean.model/bean.model_view.ts +0 -0
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# ORM Mutation Guide
|
|
2
|
+
|
|
3
|
+
This guide explains how ORM mutation operations work in Vona within the Cabloy monorepo.
|
|
4
|
+
|
|
5
|
+
## Why mutation operations matter
|
|
6
|
+
|
|
7
|
+
Mutation is where data shape, persistence behavior, soft deletion, relation-aware writes, and business rules intersect.
|
|
8
|
+
|
|
9
|
+
Vona ORM provides a structured mutation surface rather than forcing every change through raw SQL or hand-written branching.
|
|
10
|
+
|
|
11
|
+
## Basic mutation operations
|
|
12
|
+
|
|
13
|
+
Representative operations include:
|
|
14
|
+
|
|
15
|
+
- `insert`
|
|
16
|
+
- `insertBulk`
|
|
17
|
+
- `update`
|
|
18
|
+
- `updateBulk`
|
|
19
|
+
- `delete`
|
|
20
|
+
- `deleteBulk`
|
|
21
|
+
|
|
22
|
+
Representative examples:
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
await this.scope.model.post.insert({ title: 'Post001' });
|
|
26
|
+
await this.scope.model.post.update({ id: 1, title: 'Post001-Update' });
|
|
27
|
+
await this.scope.model.post.delete({ id: 1 });
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
These operations are the clearest fit when the caller already knows the exact write intent.
|
|
31
|
+
|
|
32
|
+
## Conditional update and delete paths
|
|
33
|
+
|
|
34
|
+
Write operations do not have to target one row only by primary key.
|
|
35
|
+
|
|
36
|
+
Representative patterns:
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
await this.scope.model.post.update(
|
|
40
|
+
{
|
|
41
|
+
title: 'Post001-Update',
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
where: {
|
|
45
|
+
title: { _startsWith_: 'Post001' },
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
);
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
await this.scope.model.post.delete({
|
|
53
|
+
title: {
|
|
54
|
+
_startsWith_: 'Post',
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
That matters because the mutation layer still participates in the same structured query language used by select operations.
|
|
60
|
+
|
|
61
|
+
## Bulk mutation operations
|
|
62
|
+
|
|
63
|
+
Representative bulk patterns:
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
await this.scope.model.post.insertBulk([{ title: 'Post001' }, { title: 'Post002' }]);
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
await this.scope.model.post.updateBulk([
|
|
71
|
+
{ id: 1, title: 'Post001-Update' },
|
|
72
|
+
{ id: 2, title: 'Post002-Update' },
|
|
73
|
+
]);
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
await this.scope.model.post.deleteBulk([1, 2]);
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Bulk methods are useful when the business flow already has a set of independent records to process in one operation family.
|
|
81
|
+
|
|
82
|
+
## `mutate` and `mutateBulk`
|
|
83
|
+
|
|
84
|
+
One of the most interesting Vona ideas is the `mutate` model.
|
|
85
|
+
|
|
86
|
+
Instead of forcing callers to choose insert/update/delete up front, Vona can infer the mutation kind from data characteristics.
|
|
87
|
+
|
|
88
|
+
Representative logic:
|
|
89
|
+
|
|
90
|
+
- no `id` → insert
|
|
91
|
+
- `id` present → update
|
|
92
|
+
- `id` present and `deleted: true` → delete
|
|
93
|
+
|
|
94
|
+
Representative pattern:
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
const post = await this.scope.model.post.mutate({
|
|
98
|
+
title: 'Post001',
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
await this.scope.model.post.mutate({
|
|
102
|
+
id: post.id,
|
|
103
|
+
title: 'Post001-Update',
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
await this.scope.model.post.mutate({
|
|
107
|
+
id: post.id,
|
|
108
|
+
deleted: true,
|
|
109
|
+
});
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
`mutateBulk` applies the same inference to a list of rows:
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
await this.scope.model.post.mutateBulk([
|
|
116
|
+
{ title: 'Post003' },
|
|
117
|
+
{ id: 1, title: 'Post001-Update' },
|
|
118
|
+
{ id: 2, deleted: true },
|
|
119
|
+
]);
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
This is important because many CRUD-oriented business flows naturally arrive as mixed create/update/delete batches.
|
|
123
|
+
|
|
124
|
+
## Explicit operations vs `mutate`
|
|
125
|
+
|
|
126
|
+
A practical rule is:
|
|
127
|
+
|
|
128
|
+
- use explicit `insert` / `update` / `delete` when write intent should stay obvious at the call site
|
|
129
|
+
- use `mutate` when the business payload itself should drive the write behavior
|
|
130
|
+
- use `mutateBulk` when one payload contains mixed create/update/delete rows
|
|
131
|
+
|
|
132
|
+
That keeps write APIs expressive without overcomplicating caller logic.
|
|
133
|
+
|
|
134
|
+
## Relation-aware writes and nested CRUD
|
|
135
|
+
|
|
136
|
+
Mutation is also where relations become operational, not only descriptive.
|
|
137
|
+
|
|
138
|
+
For `hasOne`, `hasMany`, and `belongsToMany` scenarios, nested writes can be expressed by combining the write payload with `include` or `with` relation definitions.
|
|
139
|
+
|
|
140
|
+
A representative `hasMany` pattern is:
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
await this.scope.model.order.update(
|
|
144
|
+
{
|
|
145
|
+
id: orderCreate.id,
|
|
146
|
+
orderNo: 'Order001-Update',
|
|
147
|
+
products: [
|
|
148
|
+
{ name: 'Peach' },
|
|
149
|
+
{ id: orderCreate.products?.[0].id, name: 'Apple-Update' },
|
|
150
|
+
{ id: orderCreate.products?.[1].id, deleted: true },
|
|
151
|
+
],
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
include: {
|
|
155
|
+
products: true,
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
);
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
This is one of the strongest reasons mutation guidance must be read together with relation guidance.
|
|
162
|
+
|
|
163
|
+
## Relationship to soft deletion
|
|
164
|
+
|
|
165
|
+
Deletion behavior is not always hard deletion. In Vona, mutation semantics may also interact with soft-delete behavior, model defaults, and cleanup policies.
|
|
166
|
+
|
|
167
|
+
Read this guide together with:
|
|
168
|
+
|
|
169
|
+
- [ORM Configuration Guide](/backend/orm-configuration-guide)
|
|
170
|
+
- [Relations Guide](/backend/relations-guide)
|
|
171
|
+
- [Transaction Guide](/backend/transaction-guide)
|
|
172
|
+
|
|
173
|
+
## Relationship to magic methods
|
|
174
|
+
|
|
175
|
+
Legacy ORM docs also highlighted model-level convenience methods such as `getByName`, `updateById`, or custom helper methods that wrap ordinary mutation/select behavior.
|
|
176
|
+
|
|
177
|
+
The important rule is:
|
|
178
|
+
|
|
179
|
+
- magic-style methods are convenience entry points
|
|
180
|
+
- the underlying write semantics still come from the standard ORM mutation surface
|
|
181
|
+
- custom model methods take precedence when business-specific behavior is needed
|
|
182
|
+
|
|
183
|
+
That means mutation should stay conceptually grounded in the standard model methods even when convenience wrappers are present.
|
|
184
|
+
|
|
185
|
+
## Implementation checks for ORM mutation changes
|
|
186
|
+
|
|
187
|
+
When writing mutation logic, ask:
|
|
188
|
+
|
|
189
|
+
1. is this best expressed as explicit insert/update/delete?
|
|
190
|
+
2. or is `mutate` a cleaner fit for the business flow?
|
|
191
|
+
3. does the deletion behavior depend on Vona soft-delete semantics?
|
|
192
|
+
4. does the write path also update related records through `include` or `with`?
|
|
193
|
+
5. should the mutation contract be reflected in DTO or controller definitions too?
|
|
194
|
+
|
|
195
|
+
That helps keep write-path logic aligned with the ORM’s intended abstractions.
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
# ORM Select Guide
|
|
2
|
+
|
|
3
|
+
This guide explains how ORM select operations work in Vona within the Cabloy monorepo.
|
|
4
|
+
|
|
5
|
+
## Why select operations matter
|
|
6
|
+
|
|
7
|
+
Select operations are where model definitions, relationships, filters, ordering, pagination, and caching behavior begin to interact.
|
|
8
|
+
|
|
9
|
+
Vona does not treat queries as untyped string fragments. It provides a richer model-aware query surface.
|
|
10
|
+
|
|
11
|
+
## Basic select operations
|
|
12
|
+
|
|
13
|
+
Representative patterns:
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
await this.scope.model.post.select();
|
|
17
|
+
await this.scope.model.post.count();
|
|
18
|
+
await this.scope.model.post.selectAndCount();
|
|
19
|
+
await this.scope.model.post.get({ id });
|
|
20
|
+
await this.scope.model.post.mget(ids);
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
These operations show the basic query vocabulary that services can build on.
|
|
24
|
+
|
|
25
|
+
## Rich select parameters
|
|
26
|
+
|
|
27
|
+
`select` can combine:
|
|
28
|
+
|
|
29
|
+
- `distinct`
|
|
30
|
+
- `columns`
|
|
31
|
+
- `where`
|
|
32
|
+
- `joins`
|
|
33
|
+
- `orders`
|
|
34
|
+
- `offset`
|
|
35
|
+
- `limit`
|
|
36
|
+
- `include`
|
|
37
|
+
- `with`
|
|
38
|
+
|
|
39
|
+
Representative pattern:
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
await this.scope.model.post.select(
|
|
43
|
+
{
|
|
44
|
+
columns: ['id', 'title', 'userId'],
|
|
45
|
+
where: {
|
|
46
|
+
'id': { _gt_: 1 },
|
|
47
|
+
'testVonaUser.id': 1,
|
|
48
|
+
},
|
|
49
|
+
joins: [['innerJoin', 'testVonaUser', ['userId', 'testVonaUser.id']]],
|
|
50
|
+
offset: 0,
|
|
51
|
+
limit: 20,
|
|
52
|
+
orders: [['createdAt', 'desc']],
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
disableDeleted: false,
|
|
56
|
+
},
|
|
57
|
+
'test-vona:user',
|
|
58
|
+
);
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
This matters because Vona ORM encourages structured query building rather than ad hoc query scattering.
|
|
62
|
+
|
|
63
|
+
## Query options
|
|
64
|
+
|
|
65
|
+
Representative option areas include:
|
|
66
|
+
|
|
67
|
+
- `disableDeleted`
|
|
68
|
+
- `disableCreateTime`
|
|
69
|
+
- `disableUpdateTime`
|
|
70
|
+
- `disableCacheQuery`
|
|
71
|
+
- `disableCacheEntity`
|
|
72
|
+
- `deleted`
|
|
73
|
+
|
|
74
|
+
This is important because select behavior may depend on caching and soft-deletion policy, not only on columns and filters.
|
|
75
|
+
|
|
76
|
+
## `joins`, `include`, and `with`
|
|
77
|
+
|
|
78
|
+
A useful distinction is:
|
|
79
|
+
|
|
80
|
+
- `joins` shapes table-level join behavior
|
|
81
|
+
- `include` loads declared static relations
|
|
82
|
+
- `with` loads dynamic relations declared at the usage site
|
|
83
|
+
|
|
84
|
+
That means joins are not floating SQL trivia. They are part of a model-aware query system that can move between table-level control and relation-level convenience.
|
|
85
|
+
|
|
86
|
+
## Type-guided joins
|
|
87
|
+
|
|
88
|
+
A key insight is that joinable tables often come from relationships already declared on the model.
|
|
89
|
+
|
|
90
|
+
In broader systems, not every useful join is declared on the current model. In those cases, the `_modelJoins` parameter can provide additional model hints so the join surface remains typed and discoverable.
|
|
91
|
+
|
|
92
|
+
## `where` operators
|
|
93
|
+
|
|
94
|
+
The operator model is broad and includes examples like:
|
|
95
|
+
|
|
96
|
+
- `_eq_`
|
|
97
|
+
- `_notEq_`
|
|
98
|
+
- `_gt_`
|
|
99
|
+
- `_gte_`
|
|
100
|
+
- `_lt_`
|
|
101
|
+
- `_lte_`
|
|
102
|
+
- `_in_`
|
|
103
|
+
- `_notIn_`
|
|
104
|
+
- `_is_`
|
|
105
|
+
- `_isNot_`
|
|
106
|
+
- `_between_`
|
|
107
|
+
- `_notBetween_`
|
|
108
|
+
- `_startsWith_`
|
|
109
|
+
- `_endsWith_`
|
|
110
|
+
- `_includes_`
|
|
111
|
+
- case-insensitive variants
|
|
112
|
+
- `_ref_`
|
|
113
|
+
- `_skip_`
|
|
114
|
+
|
|
115
|
+
That operator vocabulary is part of the Cabloy data language and should be reused consistently.
|
|
116
|
+
|
|
117
|
+
A practical operator-family reading is:
|
|
118
|
+
|
|
119
|
+
- comparison: `_eq_`, `_notEq_`, `_gt_`, `_gte_`, `_lt_`, `_lte_`
|
|
120
|
+
- membership/range: `_in_`, `_notIn_`, `_between_`, `_notBetween_`
|
|
121
|
+
- null checks: `_is_`, `_isNot_`
|
|
122
|
+
- string matching: `_startsWith_`, `_endsWith_`, `_includes_`, and case-insensitive variants
|
|
123
|
+
- composition/subquery: `_and_`, `_or_`, `_not_`, `_exists_`, `_notExists_`
|
|
124
|
+
- identifier or composition helpers: `_ref_`, `_skip_`
|
|
125
|
+
|
|
126
|
+
### `_skip_`
|
|
127
|
+
|
|
128
|
+
`_skip_` is especially useful when building query objects compositionally.
|
|
129
|
+
|
|
130
|
+
Representative pattern:
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
const where = {
|
|
134
|
+
title: { _includes_: 'ai' },
|
|
135
|
+
stars: { _gt_: 20 },
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
await this.scope.model.post.select({
|
|
139
|
+
where: {
|
|
140
|
+
...where,
|
|
141
|
+
stars: '_skip_' as const,
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
This lets a query builder remove one condition cleanly without rebuilding the whole `where` object by hand.
|
|
147
|
+
|
|
148
|
+
## Joint operators and subqueries
|
|
149
|
+
|
|
150
|
+
The query language also supports joint operators such as:
|
|
151
|
+
|
|
152
|
+
- `_and_`
|
|
153
|
+
- `_or_`
|
|
154
|
+
- `_not_`
|
|
155
|
+
- `_exists_`
|
|
156
|
+
- `_notExists_`
|
|
157
|
+
|
|
158
|
+
These operators matter because many real queries are not flat field comparisons.
|
|
159
|
+
|
|
160
|
+
Representative `_exists_` pattern:
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
await this.scope.model.post.select({
|
|
164
|
+
where: {
|
|
165
|
+
_exists_: function (builder: Knex.QueryBuilder) {
|
|
166
|
+
builder
|
|
167
|
+
.select('*')
|
|
168
|
+
.from('testVonaPostContent')
|
|
169
|
+
.where('postId', this.scope.model.post.ref('testVonaPost.id'));
|
|
170
|
+
} as any,
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## `raw` and `ref`
|
|
176
|
+
|
|
177
|
+
The structured query language is the default, but Vona still exposes escape hatches when they are truly needed.
|
|
178
|
+
|
|
179
|
+
Representative `raw` pattern:
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
await this.scope.model.post.select({
|
|
183
|
+
where: this.scope.model.post.raw('?? > ?', ['stars', 20]) as any,
|
|
184
|
+
});
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Representative `ref` pattern:
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
await this.scope.model.post.select({
|
|
191
|
+
where: {
|
|
192
|
+
title: {
|
|
193
|
+
_eq_: this.scope.model.post.ref('testVonaPost.title') as any,
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
});
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
A practical rule is:
|
|
200
|
+
|
|
201
|
+
- start with structured operators first
|
|
202
|
+
- use `ref` when comparisons need identifier semantics
|
|
203
|
+
- use `raw` only when the structured surface is not sufficient
|
|
204
|
+
|
|
205
|
+
## `selectAndCount` and pagination-shaped results
|
|
206
|
+
|
|
207
|
+
`selectAndCount` is especially useful when one backend query should return both rows and pagination metadata together.
|
|
208
|
+
|
|
209
|
+
A practical result shape includes:
|
|
210
|
+
|
|
211
|
+
- `list`
|
|
212
|
+
- `total`
|
|
213
|
+
- `pageCount`
|
|
214
|
+
- `pageSize`
|
|
215
|
+
- `pageNo`
|
|
216
|
+
|
|
217
|
+
This matters because page-query contracts often need more than a plain row array. They need a stable list-plus-metadata response shape that can map directly into DTOs and controller response contracts.
|
|
218
|
+
|
|
219
|
+
## Relationship to aggregate/group and DTOs
|
|
220
|
+
|
|
221
|
+
Read this guide together with:
|
|
222
|
+
|
|
223
|
+
- [Relations Guide](/backend/relations-guide)
|
|
224
|
+
- [ORM Aggregate and Group Guide](/backend/orm-aggregate-group-guide)
|
|
225
|
+
- [DTO Infer and Generation](/backend/dto-infer-generation)
|
|
226
|
+
|
|
227
|
+
A practical split is:
|
|
228
|
+
|
|
229
|
+
- use this guide for row-oriented query structure
|
|
230
|
+
- use the aggregate/group guide when the result shape is summary-oriented
|
|
231
|
+
- use DTO guidance when query shape must become an explicit API contract
|
|
232
|
+
|
|
233
|
+
## Implementation checks for ORM select changes
|
|
234
|
+
|
|
235
|
+
When writing or editing select logic:
|
|
236
|
+
|
|
237
|
+
1. start from model relationships and typed query structure
|
|
238
|
+
2. choose deliberately among `joins`, `include`, and `with`
|
|
239
|
+
3. prefer the ORM query surface before dropping to raw SQL
|
|
240
|
+
4. remember that soft-delete, cache, and datasource behavior may affect query semantics
|
|
241
|
+
5. think about whether the query shape should also drive DTO or OpenAPI-facing output
|
|
242
|
+
|
|
243
|
+
That keeps query logic aligned with Vona’s intended abstractions.
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
# Queue Guide
|
|
2
|
+
|
|
3
|
+
This guide explains how queues work in Vona within the Cabloy monorepo.
|
|
4
|
+
|
|
5
|
+
## Why queues matter
|
|
6
|
+
|
|
7
|
+
Vona provides a queue component based on BullMQ so business logic can be executed asynchronously and reliably through framework-native job handling.
|
|
8
|
+
|
|
9
|
+
This is one of the main bridges between synchronous application code and distributed background execution.
|
|
10
|
+
|
|
11
|
+
## Create a queue
|
|
12
|
+
|
|
13
|
+
Example: create a queue named `add` in module `demo-student`.
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm run vona :create:bean queue add -- --module=demo-student
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Queue definition
|
|
20
|
+
|
|
21
|
+
Representative shape:
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
@Queue()
|
|
25
|
+
export class QueueAdd
|
|
26
|
+
extends BeanQueueBase<TypeQueueAddJobData, TypeQueueAddJobResult>
|
|
27
|
+
implements IQueueExecute<TypeQueueAddJobData, TypeQueueAddJobResult>
|
|
28
|
+
{
|
|
29
|
+
async execute(
|
|
30
|
+
data: TypeQueueAddJobData,
|
|
31
|
+
_options?: IQueuePushOptions,
|
|
32
|
+
): Promise<TypeQueueAddJobResult> {
|
|
33
|
+
return data.a + data.b;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The important point is that queue jobs are strongly typed at both input and output boundaries.
|
|
39
|
+
|
|
40
|
+
## Push jobs
|
|
41
|
+
|
|
42
|
+
Two main modes are supported:
|
|
43
|
+
|
|
44
|
+
- `push` for fire-and-forget jobs
|
|
45
|
+
- `pushAsync` for jobs where the caller awaits a result
|
|
46
|
+
|
|
47
|
+
Representative patterns:
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
this.scope.queue.add.push({ a: 1, b: 2 });
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
const result = await this.scope.queue.add.pushAsync({ a: 1, b: 2 });
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
This is important because it gives queue usage a clear business-level interface.
|
|
58
|
+
|
|
59
|
+
## Push options and propagated context
|
|
60
|
+
|
|
61
|
+
`push` and `pushAsync` both support queue-push options.
|
|
62
|
+
|
|
63
|
+
Representative option areas include:
|
|
64
|
+
|
|
65
|
+
- `queueNameSub`
|
|
66
|
+
- `jobName`
|
|
67
|
+
- `jobOptions`
|
|
68
|
+
- `dbInfo`
|
|
69
|
+
- `locale`
|
|
70
|
+
- `instanceName`
|
|
71
|
+
- `extraData`
|
|
72
|
+
|
|
73
|
+
This matters because a queue push can propagate more than business payload alone. It can also carry runtime context that affects how the worker executes the job.
|
|
74
|
+
|
|
75
|
+
A practical push-context reading is:
|
|
76
|
+
|
|
77
|
+
| Field | Typical purpose |
|
|
78
|
+
| --------------- | --------------------------------------------------- |
|
|
79
|
+
| `data` | business payload |
|
|
80
|
+
| `queueNameSub` | refine queue routing |
|
|
81
|
+
| `jobName` | identify the job kind |
|
|
82
|
+
| `jobOptions` | pass BullMQ job options |
|
|
83
|
+
| `dbInfo` | control datasource/runtime database context |
|
|
84
|
+
| `locale` / `tz` | preserve request-oriented locale/timezone context |
|
|
85
|
+
| `instanceName` | preserve instance-aware execution context |
|
|
86
|
+
| `extraData` | pass selected request metadata or auxiliary context |
|
|
87
|
+
|
|
88
|
+
## Datasource-level isolation and deadlock avoidance
|
|
89
|
+
|
|
90
|
+
One of the most important distributed queue details is datasource-level isolation.
|
|
91
|
+
|
|
92
|
+
A common risk looks like this:
|
|
93
|
+
|
|
94
|
+
1. a request opens a database transaction
|
|
95
|
+
2. the request pushes a queue job and awaits its result
|
|
96
|
+
3. the worker handling the job also needs a database connection
|
|
97
|
+
4. both sides could wait on the same constrained datasource pool
|
|
98
|
+
|
|
99
|
+
Vona avoids this by using datasource levels so queue work can run with an isolated datasource context.
|
|
100
|
+
|
|
101
|
+
A useful practical rule is:
|
|
102
|
+
|
|
103
|
+
- ordinary request code uses the current datasource level
|
|
104
|
+
- queued job execution is pushed to `current.level + 1`
|
|
105
|
+
|
|
106
|
+
Representative pattern:
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
await this.scope.queue.add.pushAsync(data);
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
This behaves like passing:
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
await this.scope.queue.add.pushAsync(data, {
|
|
116
|
+
dbInfo: {
|
|
117
|
+
level: this.bean.database.current.level + 1,
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
So queue behavior is not only “run later.” It also participates in connection-pool isolation and distributed consistency design.
|
|
123
|
+
|
|
124
|
+
A practical refinement is:
|
|
125
|
+
|
|
126
|
+
- queue push helpers prepare and carry runtime context explicitly before execution
|
|
127
|
+
- that is why queue jobs can preserve datasource, locale, and instance-aware behavior without forcing the business payload itself to hold every context field
|
|
128
|
+
|
|
129
|
+
For the surrounding datasource architecture, also see [Multi-Database and Datasource Guide](/backend/multi-database-datasource) and [Dynamic Datasource Guide](/backend/dynamic-datasource-guide).
|
|
130
|
+
|
|
131
|
+
## Extra data and header passthrough
|
|
132
|
+
|
|
133
|
+
Queue pushes can also include `extraData`.
|
|
134
|
+
|
|
135
|
+
Representative pattern:
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
this.scope.queue.add.push(data, {
|
|
139
|
+
extraData: {
|
|
140
|
+
request: {
|
|
141
|
+
headers: {
|
|
142
|
+
'x-custom': 'xxxx',
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
The job can then read the propagated header value from request context:
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
@Queue()
|
|
153
|
+
class QueueAdd {
|
|
154
|
+
async execute(data, _options) {
|
|
155
|
+
console.log(this.ctx.headers['x-custom']);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Vona also provides a header passthrough convention: headers prefixed with `x-vona-data-` are appended automatically to extra data and passed through to the job context.
|
|
161
|
+
|
|
162
|
+
This is useful when background work needs selected request metadata without manually copying every field into the main business payload.
|
|
163
|
+
|
|
164
|
+
## Queue options
|
|
165
|
+
|
|
166
|
+
The queue system supports options around:
|
|
167
|
+
|
|
168
|
+
- concurrency
|
|
169
|
+
- transaction behavior
|
|
170
|
+
- Bull queue/worker/job options
|
|
171
|
+
- redlock-backed serial execution behavior
|
|
172
|
+
|
|
173
|
+
Representative decorator pattern:
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
@Queue({
|
|
177
|
+
concurrency: false,
|
|
178
|
+
transaction: false,
|
|
179
|
+
options: {
|
|
180
|
+
queue: {},
|
|
181
|
+
worker: {},
|
|
182
|
+
job: {},
|
|
183
|
+
redlock: {},
|
|
184
|
+
},
|
|
185
|
+
})
|
|
186
|
+
class QueueAdd {}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
That means queue behavior is not only asynchronous. It also participates in concurrency, isolation, and consistency policy.
|
|
190
|
+
|
|
191
|
+
## Configure queues in app config
|
|
192
|
+
|
|
193
|
+
Queue options can also be overridden in app config.
|
|
194
|
+
|
|
195
|
+
Representative pattern:
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
config.onions = {
|
|
199
|
+
queue: {
|
|
200
|
+
'demo-student:add': {
|
|
201
|
+
concurrency: false,
|
|
202
|
+
transaction: false,
|
|
203
|
+
options: {
|
|
204
|
+
queue: {},
|
|
205
|
+
worker: {},
|
|
206
|
+
job: {},
|
|
207
|
+
redlock: {},
|
|
208
|
+
},
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
};
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## Enable/disable and environment scoping
|
|
215
|
+
|
|
216
|
+
Queues can also be enabled or limited by environment metadata such as flavor or mode.
|
|
217
|
+
|
|
218
|
+
Representative pattern:
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
@Queue({
|
|
222
|
+
meta: {
|
|
223
|
+
flavor: 'normal',
|
|
224
|
+
mode: 'dev',
|
|
225
|
+
},
|
|
226
|
+
})
|
|
227
|
+
class QueueAdd {}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
This is especially important in Cabloy because runtime environment and flavor are first-class concepts.
|
|
231
|
+
|
|
232
|
+
## Relationship to startup, election, and broadcast
|
|
233
|
+
|
|
234
|
+
Read this guide together with:
|
|
235
|
+
|
|
236
|
+
- [Backend Startup Guide](/backend/startup-guide)
|
|
237
|
+
- [Election Guide](/backend/election-guide)
|
|
238
|
+
- [Broadcast Guide](/backend/broadcast-guide)
|
|
239
|
+
- [Worker Guide](/backend/worker-guide)
|
|
240
|
+
- [Schedule Guide](/backend/schedule-guide)
|
|
241
|
+
- [Redlock Guide](/backend/redlock-guide)
|
|
242
|
+
|
|
243
|
+
A practical split is:
|
|
244
|
+
|
|
245
|
+
- startup initializes queue workers and other runtime capabilities
|
|
246
|
+
- election decides which worker should own singleton-like responsibilities
|
|
247
|
+
- queue handles asynchronous point-to-point work
|
|
248
|
+
- broadcast sends the same message to multiple workers
|
|
249
|
+
- redlock helps serialize queue behavior when concurrent execution must be restricted
|
|
250
|
+
|
|
251
|
+
## Inspection
|
|
252
|
+
|
|
253
|
+
The queue system can expose the effective queue list for inspection, which is useful for debugging and operational visibility.
|
|
254
|
+
|
|
255
|
+
Representative pattern:
|
|
256
|
+
|
|
257
|
+
```typescript
|
|
258
|
+
this.bean.onion.queue.inspect();
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
## Implementation checks for background-job changes
|
|
262
|
+
|
|
263
|
+
When asked to move work into the background, ask:
|
|
264
|
+
|
|
265
|
+
1. is this a queue job instead of an inline request-path operation?
|
|
266
|
+
2. should the caller use `push` or `pushAsync`?
|
|
267
|
+
3. does the queue need transactional, serialized, or datasource-isolated behavior?
|
|
268
|
+
4. does the job need request metadata through `extraData` or header passthrough?
|
|
269
|
+
5. should enable/disable rules depend on flavor or mode?
|
|
270
|
+
|
|
271
|
+
That keeps background work aligned with Vona’s distributed execution model.
|