cabloy 5.1.178 → 5.1.180

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/.cabloy-version +1 -1
  2. package/.claude/scheduled_tasks.lock +1 -1
  3. package/CHANGELOG.md +25 -0
  4. package/package.json +1 -1
  5. package/repo-docs/.vitepress/config.mjs +1 -0
  6. package/repo-docs/backend/controller-aop-guide.md +1 -1
  7. package/repo-docs/backend/demonstration-mode-guide.md +130 -0
  8. package/repo-docs/frontend/page-meta-guide.md +91 -13
  9. package/repo-docs/frontend/router-view-hosts-guide.md +37 -0
  10. package/repo-docs/fullstack/framework-performance.md +3 -3
  11. package/vona/packages-cli/cli/package.json +1 -1
  12. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudBasic/boilerplate/src/controller/{{resourceName}}.ts_ +7 -7
  13. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudBasic/boilerplate/test/{{resourceName}}.test.ts_ +93 -39
  14. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudBasic/snippets/3-en-us.ts +1 -1
  15. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudBasic/snippets/4-zh-cn.ts +1 -1
  16. package/vona/packages-cli/cli-set-api/package.json +1 -1
  17. package/vona/packages-vona/vona/package.json +1 -1
  18. package/vona/pnpm-lock.yaml +1931 -2430
  19. package/vona/pnpm-workspace.yaml +1 -1
  20. package/vona/src/suite/a-demo/modules/demo-demonstration/package.json +55 -0
  21. package/vona/src/suite/a-demo/modules/demo-demonstration/src/.metadata/index.ts +68 -0
  22. package/vona/src/suite/a-demo/modules/demo-demonstration/src/.metadata/locales.ts +18 -0
  23. package/vona/src/suite/a-demo/modules/demo-demonstration/src/.metadata/this.ts +2 -0
  24. package/vona/src/suite/a-demo/modules/demo-demonstration/src/bean/guard.demonstration.ts +68 -0
  25. package/vona/src/suite/a-demo/modules/demo-demonstration/src/config/errors.ts +6 -0
  26. package/vona/src/suite/a-demo/modules/demo-demonstration/src/config/locale/en-us.ts +3 -0
  27. package/vona/src/suite/a-demo/modules/demo-demonstration/src/config/locale/zh-cn.ts +3 -0
  28. package/vona/src/suite/a-demo/modules/demo-demonstration/src/index.ts +3 -0
  29. package/vona/src/suite/a-demo/modules/demo-demonstration/src/types/index.ts +8 -0
  30. package/vona/src/suite/a-demo/modules/demo-demonstration/test/guardDemonstration.test.ts +121 -0
  31. package/vona/src/suite/a-demo/modules/demo-demonstration/tsconfig.build.json +11 -0
  32. package/vona/src/suite/a-demo/modules/demo-demonstration/tsconfig.json +7 -0
  33. package/vona/src/suite/a-demo/package.json +2 -1
  34. package/vona/src/suite/a-demo/tsconfig.json +3 -0
  35. package/vona/src/suite/a-training/modules/training-student/src/config/locale/en-us.ts +9 -0
  36. package/vona/src/suite/a-training/modules/training-student/src/config/locale/zh-cn.ts +9 -0
  37. package/vona/src/suite/a-training/modules/training-student/src/controller/student.ts +19 -17
  38. package/vona/src/suite/a-training/modules/training-student/test/student.test.ts +47 -27
  39. package/vona/src/suite-vendor/a-cabloy/modules/a-rbac/package.json +1 -1
  40. package/vona/src/suite-vendor/a-cabloy/modules/a-rbac/src/bean/bean.rbacScope.ts +13 -7
  41. package/vona/src/suite-vendor/a-cabloy/modules/a-rbac/test/rbacScopeCurrent.test.ts +28 -0
  42. package/vona/src/suite-vendor/a-cabloy/package.json +2 -2
  43. package/vona/src/suite-vendor/a-vona/modules/a-permission/package.json +1 -1
  44. package/vona/src/suite-vendor/a-vona/package.json +1 -1
  45. package/zova/packages-utils/zova-jsx/package.json +2 -2
  46. package/zova/packages-zova/zova/package.json +3 -3
  47. package/zova/packages-zova/zova-core/package.json +1 -1
  48. package/zova/packages-zova/zova-core/src/bean/type.ts +1 -0
  49. package/zova/packages-zova/zova-core/src/composables/useController.ts +1 -1
  50. package/zova/pnpm-lock.yaml +21 -21
  51. package/zova/src/suite/a-home/modules/home-indexadmin/src/page/dashboard/controller.tsx +26 -7
  52. package/zova/src/suite-vendor/a-zova/modules/a-router/package.json +1 -1
  53. package/zova/src/suite-vendor/a-zova/modules/a-router/src/lib/const.ts +1 -0
  54. package/zova/src/suite-vendor/a-zova/modules/a-router/src/monkey.ts +39 -2
  55. package/zova/src/suite-vendor/a-zova/modules/a-router/src/types/index.ts +1 -0
  56. package/zova/src/suite-vendor/a-zova/modules/a-router/src/types/pageHost.ts +9 -0
  57. package/zova/src/suite-vendor/a-zova/modules/a-router/src/types/router.ts +2 -0
  58. package/zova/src/suite-vendor/a-zova/modules/a-router/test/lib/pageHost.test.ts +53 -0
  59. package/zova/src/suite-vendor/a-zova/modules/a-zova/package.json +2 -2
  60. package/zova/src/suite-vendor/a-zova/package.json +3 -3
  61. /package/vona/patches/{zova-core@5.1.90.patch → zova-core@5.1.91.patch} +0 -0
@@ -11,10 +11,12 @@ import { getRbacDecision, hasRbacAllScope, rbacActionKey } from '../lib/rbac.ts'
11
11
 
12
12
  @Bean()
13
13
  export class BeanRbacScope extends BeanBase {
14
- private _scopeAdapter: IRbacScopeAdapter | undefined;
14
+ private _scopeAdapter: IRbacScopeAdapter | null | undefined;
15
15
 
16
16
  async isUnrestricted(): Promise<boolean> {
17
- return await this.scopeAdapter.isUnrestricted();
17
+ const scopeAdapter = this.scopeAdapter;
18
+ if (scopeAdapter) return await scopeAdapter.isUnrestricted();
19
+ return await this.bean.passport.isSystemAdmin();
18
20
  }
19
21
 
20
22
  async current(actionKey?: string): Promise<IRbacScopeAccess> {
@@ -26,19 +28,21 @@ export class BeanRbacScope extends BeanBase {
26
28
 
27
29
  const unrestricted = hasRbacAllScope(decision);
28
30
  const ownerValues =
29
- action.action === 'create'
31
+ action.action === 'create' && action.options.dataScope
30
32
  ? await this.prepareOwnerValues(action, decision, unrestricted)
31
33
  : undefined;
32
34
  return this.createAccess(action, decision, unrestricted, ownerValues);
33
35
  }
34
36
 
35
- private get scopeAdapter(): IRbacScopeAdapter {
36
- if (!this._scopeAdapter) {
37
+ private get scopeAdapter(): IRbacScopeAdapter | null {
38
+ if (this._scopeAdapter === undefined) {
37
39
  const beanFullName = beanFullNameFromOnionName(
38
40
  this.scope.config.adapter.rbacScope,
39
41
  'service',
40
42
  );
41
- this._scopeAdapter = this.bean._getBean(beanFullName) as unknown as IRbacScopeAdapter;
43
+ this._scopeAdapter =
44
+ (this.bean._getBean(beanFullName) as unknown as IRbacScopeAdapter | null | undefined) ??
45
+ null;
42
46
  }
43
47
  return this._scopeAdapter;
44
48
  }
@@ -48,7 +52,9 @@ export class BeanRbacScope extends BeanBase {
48
52
  decision: IRbacPolicyDecision,
49
53
  unrestricted: boolean,
50
54
  ): Promise<Record<string, TableIdentity | null | undefined>> {
51
- const values = await this.scopeAdapter.ownerValues(action, decision);
55
+ const scopeAdapter = this.scopeAdapter;
56
+ if (!scopeAdapter) this.app.throw(403);
57
+ const values = await scopeAdapter.ownerValues(action, decision);
52
58
  const departmentField = action.options.dataScopeField ?? 'departmentId';
53
59
  const ownerField = action.options.dataScopeMineField ?? 'userIdOwner';
54
60
  const ownerValues = {
@@ -90,6 +90,21 @@ describe('rbacScopeCurrent.test.ts', { concurrency: false }, () => {
90
90
  access.checkEntries([{ departmentId: 'one' }, { departmentId: 'two' }]);
91
91
  });
92
92
 
93
+ it('falls back to the system-admin passport check without a configured adapter', async () => {
94
+ let systemAdmin = false;
95
+ const scope = Object.create(BeanRbacScope.prototype) as BeanRbacScope;
96
+ Object.defineProperties(scope, {
97
+ bean: {
98
+ value: { passport: { isSystemAdmin: async () => systemAdmin } },
99
+ },
100
+ _scopeAdapter: { value: null, writable: true },
101
+ });
102
+
103
+ assert.equal(await scope.isUnrestricted(), false);
104
+ systemAdmin = true;
105
+ assert.equal(await scope.isUnrestricted(), true);
106
+ });
107
+
93
108
  it('does not expose the decision to a child context sharing caller state', () => {
94
109
  const action = createAction();
95
110
  const ctx = { route: action.route, state: {} } as VonaContext;
@@ -146,6 +161,19 @@ describe('rbacScopeCurrent.test.ts', { concurrency: false }, () => {
146
161
  await assertForbidden(() => scope.current());
147
162
  });
148
163
 
164
+ it('does not require adapter owner values for an unscoped create action', async () => {
165
+ const action = { ...createAction('create'), options: {} };
166
+ const { scope, ctx } = createScope(action, null as never);
167
+ setRbacDecision(ctx, createDecision(action, [{ dataScope: 'all' }]));
168
+
169
+ const access = await scope.current();
170
+ assert.equal(access.unrestricted, true);
171
+ assert.throws(
172
+ () => access.ownerValues(),
173
+ error => (error as { code?: number }).code === 500,
174
+ );
175
+ });
176
+
149
177
  it('derives create owner values from the configured adapter', async () => {
150
178
  const action = createAction('create');
151
179
  const adapter: IRbacScopeAdapter = {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-suite-a-cabloy",
3
- "version": "5.1.54",
3
+ "version": "5.1.55",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "",
6
6
  "author": "",
@@ -10,7 +10,7 @@
10
10
  "vona-module-a-cabloy": "workspace:^",
11
11
  "vona-module-a-layoutprofile": "^5.0.2",
12
12
  "vona-module-a-markdown": "^5.0.3",
13
- "vona-module-a-rbac": "^5.0.6",
13
+ "vona-module-a-rbac": "^5.0.7",
14
14
  "vona-module-a-socket": "workspace:^",
15
15
  "vona-module-a-ssr": "workspace:^",
16
16
  "vona-module-a-ssrhmr": "workspace:^"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-module-a-permission",
3
- "version": "5.1.22",
3
+ "version": "5.1.23",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "",
6
6
  "keywords": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-suite-a-vona",
3
- "version": "5.1.87",
3
+ "version": "5.1.88",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "",
6
6
  "author": "",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zova-jsx",
3
- "version": "1.1.91",
3
+ "version": "1.1.92",
4
4
  "gitHead": "2c5c19284bab738e492856189acb6fad74b8a7b7",
5
5
  "description": "Zova JSX",
6
6
  "keywords": [
@@ -50,7 +50,7 @@
50
50
  "@cabloy/word-utils": "^2.1.14",
51
51
  "typestyle": "^2.4.0",
52
52
  "vue": "^3.5.38",
53
- "zova-core": "^5.1.91"
53
+ "zova-core": "^5.1.92"
54
54
  },
55
55
  "devDependencies": {
56
56
  "clean-package": "^2.2.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zova",
3
- "version": "5.1.165",
3
+ "version": "5.1.166",
4
4
  "gitHead": "2c5c19284bab738e492856189acb6fad74b8a7b7",
5
5
  "description": "A vue3 framework with ioc",
6
6
  "keywords": [
@@ -45,8 +45,8 @@
45
45
  "postpack": "clean-package restore"
46
46
  },
47
47
  "dependencies": {
48
- "zova-core": "^5.1.91",
49
- "zova-suite-a-zova": "^5.1.164"
48
+ "zova-core": "^5.1.92",
49
+ "zova-suite-a-zova": "^5.1.165"
50
50
  },
51
51
  "devDependencies": {
52
52
  "clean-package": "^2.2.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zova-core",
3
- "version": "5.1.91",
3
+ "version": "5.1.92",
4
4
  "gitHead": "2c5c19284bab738e492856189acb6fad74b8a7b7",
5
5
  "description": "A vue3 framework with ioc",
6
6
  "keywords": [
@@ -62,6 +62,7 @@ export type TypeAllErrorCodes = TypeScopesErrorCodes | keyof TypeErrorsInternal;
62
62
 
63
63
  export interface IControllerDataContext {
64
64
  slots?: object;
65
+ page?: boolean;
65
66
  }
66
67
 
67
68
  export interface IControllerData {
@@ -39,7 +39,7 @@ export function useControllerPage(
39
39
  styleBeanFullName?: Constructable | string,
40
40
  ) {
41
41
  // controllerData
42
- const controllerData = { context: {} };
42
+ const controllerData = { context: { page: true } };
43
43
  // use controller
44
44
  _useController(controllerData, controllerBeanFullName, renderBeanFullName, styleBeanFullName);
45
45
  }
@@ -77,7 +77,7 @@ importers:
77
77
  version: 10.6.0(postcss@8.5.28)
78
78
  axios:
79
79
  specifier: ^1.18.1
80
- version: 1.20.0(supports-color@10.2.2)
80
+ version: 1.20.0
81
81
  compression:
82
82
  specifier: ^1.8.1
83
83
  version: 1.8.2
@@ -136,7 +136,7 @@ importers:
136
136
  specifier: workspace:^
137
137
  version: link:packages-zova/zova
138
138
  zova-jsx:
139
- specifier: ^1.1.90
139
+ specifier: ^1.1.91
140
140
  version: link:packages-utils/zova-jsx
141
141
  zova-module-a-api:
142
142
  specifier: workspace:^
@@ -369,7 +369,7 @@ importers:
369
369
  devDependencies:
370
370
  '@cabloy/cli':
371
371
  specifier: ^3.1.29
372
- version: 3.1.29(supports-color@10.2.2)(vue@3.5.42(typescript@5.9.3))
372
+ version: 3.1.29(vue@3.5.42(typescript@5.9.3))
373
373
  '@cabloy/lint':
374
374
  specifier: ^5.1.35
375
375
  version: 5.1.35(@typescript-eslint/eslint-plugin@8.70.0(@typescript-eslint/parser@8.70.0(eslint@10.10.0(jiti@2.7.0))(supports-color@10.2.2)(typescript@5.9.3))(eslint@10.10.0(jiti@2.7.0))(typescript@5.9.3))(@typescript-eslint/typescript-estree@8.70.0(typescript@5.9.3))(@typescript-eslint/utils@8.70.0(eslint@10.10.0(jiti@2.7.0))(typescript@5.9.3))(eslint@10.10.0(jiti@2.7.0))(oxfmt@0.62.0)(oxlint@1.82.0)(supports-color@10.2.2)(typescript@5.9.3)(vue-eslint-parser@10.4.1(eslint@10.10.0(jiti@2.7.0)))
@@ -444,7 +444,7 @@ importers:
444
444
  dependencies:
445
445
  '@cabloy/cli':
446
446
  specifier: ^3.1.29
447
- version: 3.1.29(supports-color@10.2.2)(vue@3.5.42(typescript@5.9.3))
447
+ version: 3.1.29(vue@3.5.42(typescript@5.9.3))
448
448
  '@cabloy/process-helper':
449
449
  specifier: ^3.1.8
450
450
  version: 3.1.8
@@ -478,7 +478,7 @@ importers:
478
478
  version: 7.29.7(@babel/core@7.29.7)
479
479
  '@cabloy/cli':
480
480
  specifier: ^3.1.29
481
- version: 3.1.29(supports-color@10.2.2)(vue@3.5.42(typescript@5.9.3))
481
+ version: 3.1.29(vue@3.5.42(typescript@5.9.3))
482
482
  '@cabloy/extend':
483
483
  specifier: ^3.2.8
484
484
  version: 3.2.8
@@ -732,7 +732,7 @@ importers:
732
732
  specifier: ^3.5.38
733
733
  version: 3.5.42(typescript@5.9.3)
734
734
  zova-core:
735
- specifier: ^5.1.90
735
+ specifier: ^5.1.91
736
736
  version: link:../../packages-zova/zova-core
737
737
  devDependencies:
738
738
  clean-package:
@@ -873,10 +873,10 @@ importers:
873
873
  packages-zova/zova:
874
874
  dependencies:
875
875
  zova-core:
876
- specifier: ^5.1.90
876
+ specifier: ^5.1.91
877
877
  version: link:../zova-core
878
878
  zova-suite-a-zova:
879
- specifier: ^5.1.163
879
+ specifier: ^5.1.164
880
880
  version: link:../../src/suite-vendor/a-zova
881
881
  devDependencies:
882
882
  clean-package:
@@ -1112,7 +1112,7 @@ importers:
1112
1112
  specifier: ^5.1.54
1113
1113
  version: link:modules/a-openapi
1114
1114
  zova-module-a-router:
1115
- specifier: ^5.1.39
1115
+ specifier: ^5.1.40
1116
1116
  version: link:modules/a-router
1117
1117
  zova-module-a-routerstack:
1118
1118
  specifier: ^5.1.29
@@ -1139,7 +1139,7 @@ importers:
1139
1139
  specifier: ^5.1.37
1140
1140
  version: link:modules/a-zod
1141
1141
  zova-module-a-zova:
1142
- specifier: ^5.1.106
1142
+ specifier: ^5.1.107
1143
1143
  version: link:modules/a-zova
1144
1144
 
1145
1145
  src/suite-vendor/a-zova/modules/a-api:
@@ -1204,7 +1204,7 @@ importers:
1204
1204
  dependencies:
1205
1205
  axios:
1206
1206
  specifier: ^1.18.1
1207
- version: 1.20.0(supports-color@10.2.2)
1207
+ version: 1.20.0
1208
1208
  devDependencies:
1209
1209
  clean-package:
1210
1210
  specifier: ^2.2.0
@@ -1472,12 +1472,12 @@ importers:
1472
1472
  specifier: ^3.7.2
1473
1473
  version: 3.7.2
1474
1474
  zova-jsx:
1475
- specifier: ^1.1.90
1475
+ specifier: ^1.1.91
1476
1476
  version: link:../../../../../packages-utils/zova-jsx
1477
1477
  devDependencies:
1478
1478
  '@cabloy/cli':
1479
1479
  specifier: ^3.1.29
1480
- version: 3.1.29(supports-color@10.2.2)(vue@3.5.42(typescript@5.9.3))
1480
+ version: 3.1.29(vue@3.5.42(typescript@5.9.3))
1481
1481
  '@types/luxon':
1482
1482
  specifier: ^3.7.1
1483
1483
  version: 3.7.5
@@ -8851,7 +8851,7 @@ snapshots:
8851
8851
 
8852
8852
  '@bufbuild/protobuf@2.15.0': {}
8853
8853
 
8854
- '@cabloy/cli@3.1.29(supports-color@10.2.2)(vue@3.5.42(typescript@5.9.3))':
8854
+ '@cabloy/cli@3.1.29(vue@3.5.42(typescript@5.9.3))':
8855
8855
  dependencies:
8856
8856
  '@babel/parser': 7.29.8
8857
8857
  '@cabloy/module-glob': 5.3.18
@@ -8860,7 +8860,7 @@ snapshots:
8860
8860
  '@cabloy/utils': 2.1.27
8861
8861
  '@cabloy/word-utils': link:packages-utils/word-utils
8862
8862
  '@npmcli/config': 10.12.0
8863
- '@zhennann/common-bin': 4.0.1(supports-color@10.2.2)
8863
+ '@zhennann/common-bin': 4.0.1
8864
8864
  '@zhennann/ejs': 3.0.1
8865
8865
  boxen: 4.2.0
8866
8866
  chalk: 3.0.0
@@ -11048,7 +11048,7 @@ snapshots:
11048
11048
 
11049
11049
  '@vue/shared@3.5.13': {}
11050
11050
 
11051
- '@zhennann/common-bin@4.0.1(supports-color@10.2.2)':
11051
+ '@zhennann/common-bin@4.0.1':
11052
11052
  dependencies:
11053
11053
  chalk: 4.1.2
11054
11054
  change-case: 4.1.2
@@ -11082,7 +11082,7 @@ snapshots:
11082
11082
 
11083
11083
  acorn@8.18.0: {}
11084
11084
 
11085
- agent-base@6.0.2(supports-color@10.2.2):
11085
+ agent-base@6.0.2:
11086
11086
  dependencies:
11087
11087
  debug: 4.4.3(supports-color@10.2.2)
11088
11088
  transitivePeerDependencies:
@@ -11190,11 +11190,11 @@ snapshots:
11190
11190
  dependencies:
11191
11191
  possible-typed-array-names: 1.1.0
11192
11192
 
11193
- axios@1.20.0(supports-color@10.2.2):
11193
+ axios@1.20.0:
11194
11194
  dependencies:
11195
11195
  follow-redirects: 1.16.0
11196
11196
  form-data: 4.0.6
11197
- https-proxy-agent: 5.0.1(supports-color@10.2.2)
11197
+ https-proxy-agent: 5.0.1
11198
11198
  proxy-from-env: 2.1.0
11199
11199
  transitivePeerDependencies:
11200
11200
  - debug
@@ -12589,9 +12589,9 @@ snapshots:
12589
12589
  statuses: 2.0.2
12590
12590
  toidentifier: 1.0.1
12591
12591
 
12592
- https-proxy-agent@5.0.1(supports-color@10.2.2):
12592
+ https-proxy-agent@5.0.1:
12593
12593
  dependencies:
12594
- agent-base: 6.0.2(supports-color@10.2.2)
12594
+ agent-base: 6.0.2
12595
12595
  debug: 4.4.3(supports-color@10.2.2)
12596
12596
  transitivePeerDependencies:
12597
12597
  - supports-color
@@ -10,10 +10,17 @@ export class ControllerPageDashboard extends BeanControllerPageBase {
10
10
  protected __init__() {
11
11
  if (!this.$pageRoute) return;
12
12
  this.$ssr.handleDirectOrOnHydrated(() => {
13
- this.currentTime = new Date().toLocaleTimeString();
14
- this._timer = window.setInterval(() => {
15
- this.currentTime = new Date().toLocaleTimeString();
16
- }, 1000);
13
+ this.$watch(
14
+ () => this.$pageHost?.active,
15
+ active => {
16
+ if (active) {
17
+ this._startTimer();
18
+ } else {
19
+ this._stopTimer();
20
+ }
21
+ },
22
+ { immediate: true },
23
+ );
17
24
  });
18
25
  this.$router.setPageMeta(this.$pageRoute, {
19
26
  onCustomRenderIsolate: () => (
@@ -29,10 +36,22 @@ export class ControllerPageDashboard extends BeanControllerPageBase {
29
36
  });
30
37
  }
31
38
 
39
+ private _startTimer() {
40
+ if (this._timer !== undefined) return;
41
+ this.currentTime = new Date().toLocaleTimeString();
42
+ this._timer = window.setInterval(() => {
43
+ this.currentTime = new Date().toLocaleTimeString();
44
+ }, 1000);
45
+ }
46
+
47
+ private _stopTimer() {
48
+ if (this._timer === undefined) return;
49
+ window.clearInterval(this._timer);
50
+ this._timer = undefined;
51
+ }
52
+
32
53
  protected __dispose__() {
33
- if (this._timer !== undefined) {
34
- window.clearInterval(this._timer);
35
- }
54
+ this._stopTimer();
36
55
  }
37
56
 
38
57
  protected render() {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zova-module-a-router",
3
- "version": "5.1.40",
3
+ "version": "5.1.41",
4
4
  "gitHead": "09d901d17140a80ee0764211b441cda72fd94663",
5
5
  "description": "router",
6
6
  "keywords": [
@@ -1,2 +1,3 @@
1
1
  export const pageRouteKey = '$$pageRoute';
2
+ export const pageHostKey = '$$pageHost';
2
3
  export const routerViewKey = '$$routerView';
@@ -18,14 +18,15 @@ import type {
18
18
  import type { ErrorSSR } from 'zova-module-a-ssr';
19
19
 
20
20
  import * as ModuleInfo from '@cabloy/module-info';
21
- import { shallowReactive } from 'vue';
21
+ import { onActivated, onBeforeUnmount, onDeactivated, shallowReactive } from 'vue';
22
22
  import { BeanControllerPageBase, BeanSimple, cast, useComputed } from 'zova';
23
23
 
24
24
  import type { BeanRouter } from './bean/bean.router.js';
25
25
  import type { SysRouter } from './bean/sys.router.js';
26
+ import type { IPageHost } from './types/pageHost.js';
26
27
  import type { TypePageSchema } from './types/router.js';
27
28
 
28
- import { routerViewKey } from './lib/const.js';
29
+ import { pageHostKey, routerViewKey } from './lib/const.js';
29
30
  import { getCurrentRoute, getPageRoute, getRealRouteName, getRouteMatched } from './lib/utils.js';
30
31
  import { ServiceRouterGuards } from './service/routerGuards.js';
31
32
  import { SymbolRouterHistory } from './types/utils.js';
@@ -42,6 +43,7 @@ export class Monkey
42
43
  {
43
44
  private _beanRouter: BeanRouter;
44
45
  private _sysRouter: SysRouter;
46
+ private _pageHosts = new WeakMap<ZovaContext, IPageHost>();
45
47
  serviceRouterGuards: ServiceRouterGuards;
46
48
 
47
49
  private async _getSysRouter() {
@@ -149,6 +151,13 @@ export class Monkey
149
151
  });
150
152
  },
151
153
  });
154
+ bean.defineProperty(beanInstance, '$pageHost', {
155
+ enumerable: false,
156
+ configurable: true,
157
+ get() {
158
+ return bean._getBeanFromHost({ name: pageHostKey }) ?? undefined;
159
+ },
160
+ });
152
161
  bean.defineProperty(beanInstance, '$currentRoute', {
153
162
  enumerable: false,
154
163
  configurable: true,
@@ -161,9 +170,37 @@ export class Monkey
161
170
  }
162
171
 
163
172
  controllerDataPrepare(controllerData: IControllerData, ctx: ZovaContext) {
173
+ if (controllerData.context.page) {
174
+ this._initPageHost(ctx);
175
+ }
164
176
  controllerData.context.route = getPageRoute(ctx);
165
177
  }
166
178
 
179
+ private _initPageHost(ctx: ZovaContext) {
180
+ const existing = this._pageHosts.get(ctx);
181
+ if (existing) {
182
+ ctx.bean._setBean(pageHostKey, existing);
183
+ return;
184
+ }
185
+
186
+ const pageHost = shallowReactive({ active: true });
187
+ this._pageHosts.set(ctx, pageHost);
188
+ ctx.bean._setBean(pageHostKey, pageHost);
189
+ if (!process.env.CLIENT) return;
190
+
191
+ ctx.util.instanceScope(() => {
192
+ onActivated(() => {
193
+ pageHost.active = true;
194
+ });
195
+ onDeactivated(() => {
196
+ pageHost.active = false;
197
+ });
198
+ onBeforeUnmount(() => {
199
+ pageHost.active = false;
200
+ });
201
+ });
202
+ }
203
+
167
204
  controllerDataInit(controllerData: IControllerData, controller: BeanBase) {
168
205
  // only for controller page
169
206
  if (!(controller instanceof BeanControllerPageBase)) return;
@@ -1,3 +1,4 @@
1
+ export * from './pageHost.js';
1
2
  export * from './pageMeta.js';
2
3
  export * from './router.js';
3
4
  export * from './routerView.js';
@@ -0,0 +1,9 @@
1
+ export interface IPageHost {
2
+ /**
3
+ * Whether the concrete routed page instance is currently activated.
4
+ *
5
+ * This does not describe route selection, cache membership, visibility, or
6
+ * data readiness.
7
+ */
8
+ readonly active: boolean;
9
+ }
@@ -17,6 +17,7 @@ import type {
17
17
 
18
18
  import type { BeanRouter } from '../bean/bean.router.js';
19
19
  import type { BeanRouterViewBase } from '../lib/routerViewBase.js';
20
+ import type { IPageHost } from './pageHost.js';
20
21
  import type { IGetPagePathOptions, IGotoPageOptions } from './utils.js';
21
22
  import 'vue-router';
22
23
 
@@ -72,6 +73,7 @@ declare module 'zova' {
72
73
  $router: BeanRouter;
73
74
  $routerView: BeanRouterViewBase;
74
75
  $pageRoute: RouteLocationNormalizedLoadedGeneric | undefined;
76
+ $pageHost: IPageHost | undefined;
75
77
  $currentRoute: RouteLocationNormalizedLoadedGeneric | undefined;
76
78
  }
77
79
 
@@ -0,0 +1,53 @@
1
+ import type { BeanBase, BeanContainer } from 'zova';
2
+
3
+ import assert from 'node:assert/strict';
4
+ import test from 'node:test';
5
+
6
+ import { pageHostKey } from '../../src/lib/const.js';
7
+ import { Monkey } from '../../src/monkey.js';
8
+
9
+ function createBean() {
10
+ const lookups: string[] = [];
11
+ let pageHost: object | undefined;
12
+ const beanContainer = {
13
+ defineProperty<T>(beanInstance: T, prop: string, attributes: PropertyDescriptor) {
14
+ return Object.defineProperty(beanInstance, prop, attributes);
15
+ },
16
+ _getBeanFromHost<T>({ name }: { name: string }) {
17
+ lookups.push(name);
18
+ return pageHost as T | undefined;
19
+ },
20
+ } as unknown as BeanContainer;
21
+ const beanInstance = {} as BeanBase;
22
+ const monkey = new Monkey();
23
+
24
+ return {
25
+ beanInstance,
26
+ beanContainer,
27
+ lookups,
28
+ monkey,
29
+ setPageHost(value: object | undefined) {
30
+ pageHost = value;
31
+ },
32
+ };
33
+ }
34
+
35
+ test('$pageHost reads the current page host from the bean host', async () => {
36
+ const { beanInstance, beanContainer, lookups, monkey, setPageHost } = createBean();
37
+ await monkey.beanInit(beanContainer, beanInstance);
38
+
39
+ assert.equal(beanInstance.$pageHost, undefined);
40
+
41
+ const firstPageHost = { active: true };
42
+ setPageHost(firstPageHost);
43
+ assert.equal(beanInstance.$pageHost, firstPageHost);
44
+
45
+ const secondPageHost = { active: false };
46
+ setPageHost(secondPageHost);
47
+ assert.equal(beanInstance.$pageHost, secondPageHost);
48
+
49
+ assert.deepEqual(lookups, [pageHostKey, pageHostKey, pageHostKey]);
50
+ const descriptor = Object.getOwnPropertyDescriptor(beanInstance, '$pageHost');
51
+ assert.equal(descriptor?.enumerable, false);
52
+ assert.equal(descriptor?.configurable, true);
53
+ });
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zova-module-a-zova",
3
- "version": "5.1.107",
3
+ "version": "5.1.108",
4
4
  "gitHead": "2c5c19284bab738e492856189acb6fad74b8a7b7",
5
5
  "description": "zova",
6
6
  "keywords": [
@@ -43,7 +43,7 @@
43
43
  "@cabloy/word-utils": "^2.1.14",
44
44
  "defu": "^6.1.7",
45
45
  "luxon": "^3.7.2",
46
- "zova-jsx": "^1.1.91"
46
+ "zova-jsx": "^1.1.92"
47
47
  },
48
48
  "devDependencies": {
49
49
  "@cabloy/cli": "^3.1.29",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zova-suite-a-zova",
3
- "version": "5.1.164",
3
+ "version": "5.1.165",
4
4
  "gitHead": "2c5c19284bab738e492856189acb6fad74b8a7b7",
5
5
  "description": "zova",
6
6
  "license": "MIT",
@@ -22,7 +22,7 @@
22
22
  "zova-module-a-meta": "^5.1.22",
23
23
  "zova-module-a-model": "^5.1.37",
24
24
  "zova-module-a-openapi": "^5.1.54",
25
- "zova-module-a-router": "^5.1.40",
25
+ "zova-module-a-router": "^5.1.41",
26
26
  "zova-module-a-routerstack": "^5.1.29",
27
27
  "zova-module-a-routertabs": "^5.1.34",
28
28
  "zova-module-a-ssr": "^5.1.40",
@@ -31,7 +31,7 @@
31
31
  "zova-module-a-style": "^5.1.35",
32
32
  "zova-module-a-table": "^5.1.43",
33
33
  "zova-module-a-zod": "^5.1.37",
34
- "zova-module-a-zova": "^5.1.107"
34
+ "zova-module-a-zova": "^5.1.108"
35
35
  },
36
36
  "title": "a-zova"
37
37
  }