cabloy 5.1.153 → 5.1.155

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 (64) hide show
  1. package/.cabloy-version +1 -1
  2. package/.claude/scheduled_tasks.lock +1 -0
  3. package/.claude/skills/cabloy-spec-generation/scripts/generate-implementation-charts.mjs +46 -11
  4. package/.claude/skills/cabloy-spec-generation/scripts/generate-implementation-charts.test.mjs +13 -0
  5. package/CHANGELOG.md +19 -0
  6. package/package.json +1 -1
  7. package/repo-docs/backend/menu-guide.md +3 -2
  8. package/test-results/.last-run.json +2 -4
  9. package/vona/env/.env +8 -0
  10. package/vona/packages-vona/vona/package.json +1 -1
  11. package/vona/src/suite/a-commerce/modules/commerce-siteadmin/package.json +1 -2
  12. package/vona/src/suite/a-commerce/modules/commerce-siteadmin/src/.metadata/index.ts +40 -65
  13. package/vona/src/suite/a-home/modules/home-base/src/.metadata/index.ts +78 -72
  14. package/vona/src/suite/a-home/modules/home-base/src/.metadata/locales.ts +18 -0
  15. package/vona/src/suite/a-home/modules/home-base/src/config/locale/en-us.ts +3 -0
  16. package/vona/src/suite/a-home/modules/home-base/src/config/locale/zh-cn.ts +3 -0
  17. package/vona/src/suite/a-home/modules/home-base/src/index.ts +1 -0
  18. package/vona/src/suite/a-home/modules/home-base/src/service/siteCatalog.ts +17 -10
  19. package/vona/src/suite/a-home/modules/home-user/src/bean/meta.version.ts +5 -0
  20. package/vona/src/suite/a-home/modules/home-user/src/config/config.ts +36 -5
  21. package/vona/src/suite/a-home/modules/home-user/src/config/roles.ts +7 -2
  22. package/vona/src/suite/a-home/modules/home-user/src/entity/role.ts +3 -0
  23. package/vona/src/suite/a-home/modules/home-user/src/types/env.ts +11 -0
  24. package/vona/src/suite/a-home/modules/home-user/src/types/index.ts +1 -0
  25. package/vona/src/suite/a-home/modules/home-user/test/config.test.ts +65 -0
  26. package/vona/src/suite/a-home/modules/home-user/test/role.test.ts +14 -6
  27. package/vona/src/suite-vendor/a-cabloy/modules/a-rbac/package.json +1 -1
  28. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/package.json +1 -1
  29. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/.metadata/index.ts +17 -2
  30. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/bean/event.resolveMenuVisibility.ts +19 -0
  31. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/lib/beanSsrSiteBase.ts +32 -16
  32. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/lib/index.ts +2 -0
  33. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/lib/ssrMenuEligibility.ts +107 -0
  34. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/lib/ssrMenuVisibility.ts +91 -0
  35. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/service/ssr.ts +30 -0
  36. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/types/ssrMenu.ts +67 -0
  37. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/test/ssrMenuEligibility.test.ts +135 -0
  38. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/test/ssrMenuVisibility.test.ts +64 -0
  39. package/vona/src/suite-vendor/a-cabloy/package.json +2 -2
  40. package/vona/src/suite-vendor/a-vona/modules/a-openapiutils/package.json +1 -1
  41. package/vona/src/suite-vendor/a-vona/modules/a-openapiutils/src/lib/const/index.ts +1 -0
  42. package/vona/src/suite-vendor/a-vona/modules/a-openapiutils/src/lib/const/site.ts +1 -0
  43. package/vona/src/suite-vendor/a-vona/modules/a-permission/package.json +1 -1
  44. package/vona/src/suite-vendor/a-vona/modules/a-user/package.json +1 -1
  45. package/vona/src/suite-vendor/a-vona/modules/a-user/src/types/role.ts +1 -0
  46. package/vona/src/suite-vendor/a-vona/package.json +1 -1
  47. package/zova/pnpm-lock.yaml +1 -1
  48. package/zova/src/suite/a-home/modules/home-api/mock/passport.fake.ts +6 -6
  49. package/zova/src/suite/a-home/modules/home-base/src/lib/index.ts +1 -0
  50. package/zova/src/suite/a-home/modules/home-base/src/lib/roleSiteAdmission.ts +15 -0
  51. package/zova/src/suite/a-home/modules/home-base/src/service/routerGuards.ts +3 -3
  52. package/zova/src/suite/a-home/modules/home-layoutadmin/src/model/menu.ts +4 -3
  53. package/zova/src/suite/a-home/modules/home-layoutweb/src/model/menu.ts +4 -3
  54. package/zova/src/suite/a-home/modules/home-passport/package.json +2 -1
  55. package/zova/src/suite/a-home/modules/home-passport/src/model/passport.ts +2 -2
  56. package/zova/src/suite/a-home/modules/home-passport/test/lib/roleSiteAdmission.test.ts +23 -0
  57. package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/package.json +1 -1
  58. package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/src/lib/resourceRouteMeta.ts +9 -0
  59. package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/src/routes.ts +4 -10
  60. package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/test/lib/routes.test.ts +37 -0
  61. package/zova/src/suite-vendor/a-cabloy/package.json +2 -2
  62. package/test-results/cabloy-basic-ATP-BASIC-SUM-58d8a-ry-states-and-Markdown-HTML/error-context.md +0 -195
  63. package/vona/src/suite/a-commerce/modules/commerce-siteadmin/src/bean/meta.version.ts +0 -32
  64. package/vona/src/suite/a-commerce/modules/commerce-siteadmin/test/meta.version.test.ts +0 -44
package/.cabloy-version CHANGED
@@ -1 +1 @@
1
- 5.1.153
1
+ 5.1.155
@@ -0,0 +1 @@
1
+ {"sessionId":"834e10b3-c7de-4e46-8255-347f58ac99c9","pid":21283,"procStart":"Tue Sep 1 21:20:05 2026","acquiredAt":1788301241462}
@@ -66,24 +66,46 @@ function escapeXml(value) {
66
66
  return String(value).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&apos;');
67
67
  }
68
68
 
69
- function wrapChartText(value, maxCharacters) {
69
+ function chartTextWidth(value, fontSize) {
70
+ return [...String(value)].reduce((width, character) => {
71
+ const characterWidth = /[⺀-鿿가-힯豈-﫿\ud800-\udfff]/.test(character)
72
+ ? 1
73
+ : /[MW@#%&]/.test(character)
74
+ ? 0.9
75
+ : /[A-Z0-9]/.test(character)
76
+ ? 0.62
77
+ : /[ilI.,:;!'` ]/.test(character)
78
+ ? 0.28
79
+ : 0.5;
80
+ return width + characterWidth * fontSize;
81
+ }, 0);
82
+ }
83
+
84
+ function wrapChartText(value, maxWidth, measure = value => [...String(value)].length) {
70
85
  const words = String(value).trim().replace(/\s+/g, ' ').split(' ');
71
86
  const lines = [];
72
87
  let line = '';
73
88
  for (const word of words) {
74
89
  const characters = [...word];
75
- if (characters.length > maxCharacters) {
90
+ if (measure(word) > maxWidth) {
76
91
  if (line) {
77
92
  lines.push(line);
78
93
  line = '';
79
94
  }
80
- for (let index = 0; index < characters.length; index += maxCharacters) {
81
- lines.push(characters.slice(index, index + maxCharacters).join(''));
95
+ let chunk = ''; let chunkWidth = 0;
96
+ for (const character of characters) {
97
+ const characterWidth = measure(character);
98
+ if (chunk && chunkWidth + characterWidth > maxWidth) {
99
+ lines.push(chunk);
100
+ chunk = ''; chunkWidth = 0;
101
+ }
102
+ chunk += character; chunkWidth += characterWidth;
82
103
  }
104
+ if (chunk) lines.push(chunk);
83
105
  continue;
84
106
  }
85
107
  const candidate = line ? `${line} ${word}` : word;
86
- if ([...candidate].length > maxCharacters) {
108
+ if (measure(candidate) > maxWidth) {
87
109
  lines.push(line);
88
110
  line = word;
89
111
  } else {
@@ -229,6 +251,11 @@ function statusIcon(x, y, status) {
229
251
  return `<circle cx="${x}" cy="${y}" r="5" fill="${STATUS_COLORS[status]}"/>`;
230
252
  }
231
253
 
254
+ function renderChartTextLines(className, x, y, lines, lineHeight) {
255
+ if (lines.length === 1) return `<text class="${className}" x="${x}" y="${y}">${escapeXml(lines[0])}</text>`;
256
+ return `<text class="${className}" x="${x}" y="${y}">${lines.map((line, lineIndex) => `<tspan x="${x}" dy="${lineIndex ? lineHeight : 0}">${escapeXml(line)}</tspan>`).join('')}</text>`;
257
+ }
258
+
232
259
  function svgDocument(title, desc, body, height, metadata) {
233
260
  return `<?xml version="1.0" encoding="UTF-8"?>
234
261
  <svg xmlns="http://www.w3.org/2000/svg" width="1600" height="${height}" viewBox="0 0 1600 ${height}" role="img" aria-labelledby="chart-title chart-desc">
@@ -243,25 +270,33 @@ function svgDocument(title, desc, body, height, metadata) {
243
270
 
244
271
  export function renderGantt(model, suiteName) {
245
272
  const { copy: t, tasks, reviewed, language } = model;
246
- const phaseX = 72; const wbsX = 265; const workX = 405; const dependencyX = 735; const statusX = 900;
273
+ const phaseX = 72; const wbsX = 265; const workX = 405; const dependencyX = 680; const statusX = 900;
247
274
  const plotX = 980; const plotW = 520; const rowY = 270; const rowH = 34; const phaseLineH = 12;
275
+ const workMaxWidth = dependencyX - workX - 10;
276
+ const dependencyMaxWidth = statusX - dependencyX - 12;
248
277
  let nextY = rowY;
249
278
  const layout = tasks.map((task, index) => {
250
279
  const phaseStart = index === 0 || tasks[index - 1].phase.number !== task.phase.number;
251
280
  const descriptionLines = phaseStart ? wrapChartText(task.phase.title, 30) : [];
281
+ const workLines = wrapChartText(task.title, workMaxWidth, value => chartTextWidth(value, 10));
282
+ const dependencyLines = wrapChartText(task.dependency, dependencyMaxWidth, value => chartTextWidth(value, 9));
283
+ const contentLines = Math.max(workLines.length, dependencyLines.length);
284
+ const taskRowH = Math.max(rowH, contentLines * phaseLineH + 22);
252
285
  const phaseLabelY = phaseStart ? nextY : undefined;
253
286
  const y = phaseStart ? nextY + 23 + descriptionLines.length * phaseLineH : nextY;
254
- nextY = y + rowH;
255
- return { task, index, y, phaseLabelY, descriptionLines };
287
+ const separatorY = y + Math.max(14, contentLines * phaseLineH + 2);
288
+ nextY = y + taskRowH;
289
+ return { task, index, y, separatorY, phaseLabelY, descriptionLines, workLines, dependencyLines };
256
290
  });
257
- const contentBottom = layout.at(-1)?.y + 14 ?? rowY;
291
+ const contentBottom = layout.at(-1)?.separatorY ?? rowY;
258
292
  const footerY = Math.max(772, contentBottom + 80);
259
293
  const height = Math.max(850, footerY + 78);
260
- const rows = layout.map(({ task, index, y, phaseLabelY, descriptionLines }) => {
294
+ const rows = layout.map(({ task, index, y, separatorY, phaseLabelY, descriptionLines, workLines, dependencyLines }) => {
261
295
  const barX = plotX + (index / Math.max(tasks.length, 1)) * (plotW - 90);
262
296
  const barW = task.status === 'verified' ? 58 : 42;
263
297
  const phaseHeader = phaseLabelY === undefined ? '' : `<text class="phase" x="${phaseX}" y="${phaseLabelY}">${escapeXml(`${t.phase} ${task.phase.number}`)}</text>${descriptionLines.map((line, lineIndex) => `<text class="small" x="${phaseX}" y="${phaseLabelY + 13 + lineIndex * phaseLineH}">${escapeXml(line)}</text>`).join('')}`;
264
- return `<g class="row" tabindex="0" aria-label="${escapeXml(`${task.id}: ${task.title}; ${statusLabel(task.status, language)}`)}"><title>${escapeXml(`${task.id}: ${task.title}; dependencies: ${task.dependency}; ${statusLabel(task.status, language)}`)}</title><line class="grid" x1="72" y1="${y + 14}" x2="1500" y2="${y + 14}"/>${phaseHeader}<text class="num" x="${wbsX}" y="${y}">${escapeXml(task.id)}</text><text class="task" x="${workX}" y="${y}">${escapeXml(task.title)}</text><text class="small" x="${dependencyX}" y="${y}">${escapeXml(task.dependency)}</text>${statusIcon(statusX + 5, y - 4, task.status)}<text class="small" x="${statusX + 17}" y="${y}">${escapeXml(statusLabel(task.status, language))}</text><rect x="${barX.toFixed(1)}" y="${y - 15}" width="${barW}" height="18" rx="4" fill="${STATUS_COLORS[task.status]}"/></g>`;
298
+ const dependencyText = renderChartTextLines('small', dependencyX, y, dependencyLines, phaseLineH);
299
+ return `<g class="row" tabindex="0" aria-label="${escapeXml(`${task.id}: ${task.title}; ${statusLabel(task.status, language)}`)}"><title>${escapeXml(`${task.id}: ${task.title}; dependencies: ${task.dependency}; ${statusLabel(task.status, language)}`)}</title><line class="grid" x1="72" y1="${separatorY}" x2="1500" y2="${separatorY}"/>${phaseHeader}<text class="num" x="${wbsX}" y="${y}">${escapeXml(task.id)}</text>${renderChartTextLines('task', workX, y, workLines, phaseLineH)}${dependencyText}${statusIcon(statusX + 5, y - 4, task.status)}<text class="small" x="${statusX + 17}" y="${y}">${escapeXml(statusLabel(task.status, language))}</text><rect x="${barX.toFixed(1)}" y="${y - 15}" width="${barW}" height="18" rx="4" fill="${STATUS_COLORS[task.status]}"/></g>`;
265
300
  }).join('\n');
266
301
  const completed = new Set(tasks.filter(task => task.status === 'verified').map(task => task.id));
267
302
  const dependencyIds = task => [...task.dependency.matchAll(/\bWBS-(?:[A-Za-z0-9]+-)*[A-Za-z0-9]+(?:-\*)?/g)].flatMap(match => {
@@ -131,6 +131,19 @@ test('renders a phase description directly beneath its phase label', async t =>
131
131
  assert.doesNotMatch(gantt, /y="432">Delivery<\/text>/);
132
132
  });
133
133
 
134
+ test('widens and wraps the Dependencies column without covering status', async t => {
135
+ const directory = await fixture({
136
+ 'pdp-wbs.md': files['pdp-wbs.md'].replace(
137
+ 'Dependencies: `WBS-10-01`.',
138
+ 'Dependencies: `WBS-10-01`, `WBS-10-01`, `WBS-10-01`, `WBS-10-01`.',
139
+ ),
140
+ });
141
+ t.after(() => removeFixture(directory));
142
+ const gantt = (await generateCharts(directory)).artifacts.get('implementation-gantt.svg');
143
+ assert.match(gantt, /<text class="small" x="680" y="246">Dependencies<\/text>/);
144
+ assert.match(gantt, /<text class="small" x="680" y="\d+"><tspan x="680" dy="0">[^<]+<\/tspan><tspan x="680" dy="12">[^<]+<\/tspan><\/text><circle cx="905" cy="\d+"/);
145
+ });
146
+
134
147
  test('selects Chinese chart copy from a Chinese README', () => {
135
148
  const model = createChartModel({
136
149
  readme: '# 示例内部规划\n\n这是中文规划记录。\n',
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # Changelog
2
2
 
3
+ ## 5.1.155
4
+
5
+ ### Features
6
+
7
+ - Add support for REST resource keys.
8
+
9
+ ## 5.1.154
10
+
11
+ ### Features
12
+
13
+ - Add home user functionality.
14
+ - Add commerce role site support.
15
+ - Add role site support.
16
+
17
+ ### Bug Fixes
18
+
19
+ - Fix the menu guide.
20
+ - Correct Gantt chart render width.
21
+
3
22
  ## 5.1.153
4
23
 
5
24
  ### Features
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cabloy",
3
- "version": "5.1.153",
3
+ "version": "5.1.155",
4
4
  "gitHead": "2c5c19284bab738e492856189acb6fad74b8a7b7",
5
5
  "description": "A Node.js fullstack framework",
6
6
  "keywords": [
@@ -70,8 +70,9 @@ In the current repo implementation, the out-of-the-box menu controller is public
70
70
  })
71
71
  ```
72
72
 
73
- - Omit `roles`, or use `roles: []`, to make an item visible to anonymous and authenticated callers.
74
- - A nonempty `roles` array is visible when the current Passport has at least one matching role name.
73
+ - Omit `roles` to make an item visible to anonymous and authenticated callers.
74
+ - Use `roles: []` when an item has no static role visibility and is intended to be disclosed only through dynamic Role-menu configuration.
75
+ - A nonempty `roles` array is visible when the current Passport has at least one matching role name; it can also be disclosed through dynamic Role-menu configuration.
75
76
  - `roles` is server-only declaration metadata. It is filtered out before the API response and is not part of `IMenuItem`, OpenAPI, or generated frontend clients.
76
77
  - This controls navigation disclosure only. It never grants access to a page, controller action, API, or resource; those boundaries retain their own route and Passport/permission guards. For the fullstack pattern that pairs an Admin `presetResource` entry with independently authorized Admin APIs and separately scoped Web self-service APIs, see [Admin Resource and Web Self-Service](/fullstack/admin-resource-and-web-self-service).
77
78
 
@@ -1,6 +1,4 @@
1
1
  {
2
- "status": "failed",
3
- "failedTests": [
4
- "0d2518050eca8cd1231a-03158dd238416d7c8c3e"
5
- ]
2
+ "status": "passed",
3
+ "failedTests": []
6
4
  }
package/vona/env/.env CHANGED
@@ -84,6 +84,14 @@ REDIS_DEFAULT_PORT = 6379
84
84
  REDIS_DEFAULT_PASSWORD =
85
85
  REDIS_DEFAULT_DB = 0
86
86
 
87
+ # home user
88
+
89
+ HOME_USER_PASSWORD_DEFAULT_ADMIN = 123456
90
+ HOME_USER_DISABLE_BOOTSTRAP_SYSTEM_ADMIN = false
91
+ HOME_USER_DISABLE_USER_ADMIN = false
92
+ HOME_USER_BUILTIN_ROLE_REGISTERED_USER_SITE_IDS = *
93
+ HOME_USER_BUILTIN_ROLE_SYSTEM_ADMIN_SITE_IDS = *
94
+
87
95
  # payment
88
96
 
89
97
  PAY_MOCK_DEFAULT_CREDENTIAL = pay-mock-dev-credential
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona",
3
- "version": "5.1.88",
3
+ "version": "5.1.89",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "Vona is an intuitive, elegant and powerful Node.js framework for rapidly developing enterprise applications of any size",
6
6
  "keywords": [
@@ -49,7 +49,6 @@
49
49
  "a-vona": "5.0.0",
50
50
  "a-ssr": "5.0.0",
51
51
  "home-user": "5.0.0"
52
- },
53
- "fileVersion": 1
52
+ }
54
53
  }
55
54
  }
@@ -1,71 +1,6 @@
1
1
  // eslint-disable
2
2
  import type { TypeSymbolKeyFieldsMore } from 'vona-module-a-orm';
3
3
  import type { TypeEntityOptionsFields,TypeControllerOptionsActions } from 'vona-module-a-openapi';
4
- /** service: begin */
5
- export * from '../service/operator.ts';
6
-
7
- import 'vona-module-a-bean';
8
- declare module 'vona-module-a-bean' {
9
-
10
- export interface IServiceRecord {
11
- 'commerce-siteadmin:operator': never;
12
- }
13
-
14
-
15
- }
16
- declare module 'vona-module-commerce-siteadmin' {
17
-
18
- export interface ServiceOperator {
19
- /** @internal */
20
- get scope(): ScopeModuleCommerceSiteadmin;
21
- }
22
-
23
- export interface ServiceOperator {
24
- get $beanFullName(): 'commerce-siteadmin.service.operator';
25
- get $onionName(): 'commerce-siteadmin:operator';
26
- }
27
- }
28
- /** service: end */
29
- /** service: begin */
30
- import type { ServiceOperator } from '../service/operator.ts';
31
- export interface IModuleService {
32
- 'operator': ServiceOperator;
33
- }
34
- /** service: end */
35
- /** service: begin */
36
-
37
- import 'vona';
38
- declare module 'vona' {
39
- export interface IBeanRecordGeneral {
40
- 'commerce-siteadmin.service.operator': ServiceOperator;
41
- }
42
- }
43
- /** service: end */
44
- /** meta: begin */
45
- export * from '../bean/meta.version.ts';
46
-
47
- import 'vona-module-a-meta';
48
- declare module 'vona-module-a-meta' {
49
-
50
- export interface IMetaRecord {
51
- 'commerce-siteadmin:version': never;
52
- }
53
-
54
-
55
- }
56
- declare module 'vona-module-commerce-siteadmin' {
57
-
58
- export interface MetaVersion {
59
- /** @internal */
60
- get scope(): ScopeModuleCommerceSiteadmin;
61
- }
62
-
63
- export interface MetaVersion {
64
- get $beanFullName(): 'commerce-siteadmin.meta.version';
65
- get $onionName(): 'commerce-siteadmin:version';
66
- }
67
- }
68
- /** meta: end */
69
4
  /** dto: begin */
70
5
  export * from '../dto/operatorContext.ts';
71
6
  import type { IDtoOptionsOperatorContext } from '../dto/operatorContext.ts';
@@ -140,6 +75,46 @@ import 'vona-module-a-openapi';
140
75
  }
141
76
 
142
77
  /** controller: end */
78
+ /** service: begin */
79
+ export * from '../service/operator.ts';
80
+
81
+ import 'vona-module-a-bean';
82
+ declare module 'vona-module-a-bean' {
83
+
84
+ export interface IServiceRecord {
85
+ 'commerce-siteadmin:operator': never;
86
+ }
87
+
88
+
89
+ }
90
+ declare module 'vona-module-commerce-siteadmin' {
91
+
92
+ export interface ServiceOperator {
93
+ /** @internal */
94
+ get scope(): ScopeModuleCommerceSiteadmin;
95
+ }
96
+
97
+ export interface ServiceOperator {
98
+ get $beanFullName(): 'commerce-siteadmin.service.operator';
99
+ get $onionName(): 'commerce-siteadmin:operator';
100
+ }
101
+ }
102
+ /** service: end */
103
+ /** service: begin */
104
+ import type { ServiceOperator } from '../service/operator.ts';
105
+ export interface IModuleService {
106
+ 'operator': ServiceOperator;
107
+ }
108
+ /** service: end */
109
+ /** service: begin */
110
+
111
+ import 'vona';
112
+ declare module 'vona' {
113
+ export interface IBeanRecordGeneral {
114
+ 'commerce-siteadmin.service.operator': ServiceOperator;
115
+ }
116
+ }
117
+ /** service: end */
143
118
  /** ssrSite: begin */
144
119
  export * from '../bean/ssrSite.commerceAdmin.ts';
145
120
  import type { ISsrSiteOptionsCommerceAdmin } from '../bean/ssrSite.commerceAdmin.ts';
@@ -1,76 +1,6 @@
1
1
  // eslint-disable
2
2
  import type { TypeSymbolKeyFieldsMore } from 'vona-module-a-orm';
3
3
  import type { TypeEntityOptionsFields,TypeControllerOptionsActions } from 'vona-module-a-openapi';
4
- /** service: begin */
5
- export * from '../service/menu.ts';
6
- export * from '../service/permission.ts';
7
- export * from '../service/siteCatalog.ts';
8
-
9
- import 'vona-module-a-bean';
10
- declare module 'vona-module-a-bean' {
11
-
12
- export interface IServiceRecord {
13
- 'home-base:menu': never;
14
- 'home-base:permission': never;
15
- 'home-base:siteCatalog': never;
16
- }
17
-
18
-
19
- }
20
- declare module 'vona-module-home-base' {
21
-
22
- export interface ServiceMenu {
23
- /** @internal */
24
- get scope(): ScopeModuleHomeBase;
25
- }
26
-
27
- export interface ServiceMenu {
28
- get $beanFullName(): 'home-base.service.menu';
29
- get $onionName(): 'home-base:menu';
30
- }
31
-
32
- export interface ServicePermission {
33
- /** @internal */
34
- get scope(): ScopeModuleHomeBase;
35
- }
36
-
37
- export interface ServicePermission {
38
- get $beanFullName(): 'home-base.service.permission';
39
- get $onionName(): 'home-base:permission';
40
- }
41
-
42
- export interface ServiceSiteCatalog {
43
- /** @internal */
44
- get scope(): ScopeModuleHomeBase;
45
- }
46
-
47
- export interface ServiceSiteCatalog {
48
- get $beanFullName(): 'home-base.service.siteCatalog';
49
- get $onionName(): 'home-base:siteCatalog';
50
- }
51
- }
52
- /** service: end */
53
- /** service: begin */
54
- import type { ServiceMenu } from '../service/menu.ts';
55
- import type { ServicePermission } from '../service/permission.ts';
56
- import type { ServiceSiteCatalog } from '../service/siteCatalog.ts';
57
- export interface IModuleService {
58
- 'menu': ServiceMenu;
59
- 'permission': ServicePermission;
60
- 'siteCatalog': ServiceSiteCatalog;
61
- }
62
- /** service: end */
63
- /** service: begin */
64
-
65
- import 'vona';
66
- declare module 'vona' {
67
- export interface IBeanRecordGeneral {
68
- 'home-base.service.menu': ServiceMenu;
69
- 'home-base.service.permission': ServicePermission;
70
- 'home-base.service.siteCatalog': ServiceSiteCatalog;
71
- }
72
- }
73
- /** service: end */
74
4
  /** dto: begin */
75
5
  export * from '../dto/siteCatalogSelectRes.ts';
76
6
  export * from '../dto/siteCatalogSelectResItem.ts';
@@ -195,11 +125,84 @@ import 'vona-module-a-openapi';
195
125
  }
196
126
 
197
127
  /** controller: end */
128
+ /** service: begin */
129
+ export * from '../service/menu.ts';
130
+ export * from '../service/permission.ts';
131
+ export * from '../service/siteCatalog.ts';
132
+
133
+ import 'vona-module-a-bean';
134
+ declare module 'vona-module-a-bean' {
135
+
136
+ export interface IServiceRecord {
137
+ 'home-base:menu': never;
138
+ 'home-base:permission': never;
139
+ 'home-base:siteCatalog': never;
140
+ }
141
+
142
+
143
+ }
144
+ declare module 'vona-module-home-base' {
145
+
146
+ export interface ServiceMenu {
147
+ /** @internal */
148
+ get scope(): ScopeModuleHomeBase;
149
+ }
150
+
151
+ export interface ServiceMenu {
152
+ get $beanFullName(): 'home-base.service.menu';
153
+ get $onionName(): 'home-base:menu';
154
+ }
155
+
156
+ export interface ServicePermission {
157
+ /** @internal */
158
+ get scope(): ScopeModuleHomeBase;
159
+ }
160
+
161
+ export interface ServicePermission {
162
+ get $beanFullName(): 'home-base.service.permission';
163
+ get $onionName(): 'home-base:permission';
164
+ }
165
+
166
+ export interface ServiceSiteCatalog {
167
+ /** @internal */
168
+ get scope(): ScopeModuleHomeBase;
169
+ }
170
+
171
+ export interface ServiceSiteCatalog {
172
+ get $beanFullName(): 'home-base.service.siteCatalog';
173
+ get $onionName(): 'home-base:siteCatalog';
174
+ }
175
+ }
176
+ /** service: end */
177
+ /** service: begin */
178
+ import type { ServiceMenu } from '../service/menu.ts';
179
+ import type { ServicePermission } from '../service/permission.ts';
180
+ import type { ServiceSiteCatalog } from '../service/siteCatalog.ts';
181
+ export interface IModuleService {
182
+ 'menu': ServiceMenu;
183
+ 'permission': ServicePermission;
184
+ 'siteCatalog': ServiceSiteCatalog;
185
+ }
186
+ /** service: end */
187
+ /** service: begin */
188
+
189
+ import 'vona';
190
+ declare module 'vona' {
191
+ export interface IBeanRecordGeneral {
192
+ 'home-base.service.menu': ServiceMenu;
193
+ 'home-base.service.permission': ServicePermission;
194
+ 'home-base.service.siteCatalog': ServiceSiteCatalog;
195
+ }
196
+ }
197
+ /** service: end */
198
+ /** locale: begin */
199
+ import { locales } from './locales.ts';
200
+ /** locale: end */
198
201
  /** main: begin */
199
202
  export * from '../main.ts';
200
203
  /** main: end */
201
204
  /** scope: begin */
202
- import { BeanScopeBase, type BeanScopeUtil } from 'vona';
205
+ import { BeanScopeBase, type BeanScopeUtil, type TypeModuleLocales, type TypeLocaleBase } from 'vona';
203
206
  import { Scope } from 'vona-module-a-bean';
204
207
 
205
208
  @Scope()
@@ -207,6 +210,7 @@ export class ScopeModuleHomeBase extends BeanScopeBase {}
207
210
 
208
211
  export interface ScopeModuleHomeBase {
209
212
  util: BeanScopeUtil;
213
+ locale: TypeModuleLocales<(typeof locales)[TypeLocaleBase]>;
210
214
  service: IModuleService;
211
215
  }
212
216
 
@@ -222,7 +226,9 @@ declare module 'vona' {
222
226
 
223
227
 
224
228
 
225
-
229
+ export interface IBeanScopeLocale {
230
+ 'home-base': (typeof locales)[TypeLocaleBase];
231
+ }
226
232
 
227
233
 
228
234
  }
@@ -0,0 +1,18 @@
1
+ import type { TypeLocaleBase } from 'vona';
2
+
3
+ import { $makeLocaleMagic } from 'vona';
4
+
5
+ import locale_en_us from '../config/locale/en-us.ts';
6
+ import locale_zh_cn from '../config/locale/zh-cn.ts';
7
+
8
+ export const locales = {
9
+ 'en-us': locale_en_us,
10
+ 'zh-cn': locale_zh_cn,
11
+ };
12
+
13
+ export function $locale<K extends keyof (typeof locales)[TypeLocaleBase]>(
14
+ key: K,
15
+ ...args: any[]
16
+ ): any {
17
+ return $makeLocaleMagic(`home-base::${key}`, ...args);
18
+ }
@@ -0,0 +1,3 @@
1
+ export default {
2
+ SiteAll: 'All Sites',
3
+ };
@@ -0,0 +1,3 @@
1
+ export default {
2
+ SiteAll: '全部站点',
3
+ };
@@ -1 +1,2 @@
1
+ export * from './.metadata/locales.ts';
1
2
  export * from './.metadata/index.ts';
@@ -2,22 +2,29 @@ import type { IDecoratorSsrSiteOptions } from 'vona-module-a-ssr';
2
2
 
3
3
  import { BeanBase } from 'vona';
4
4
  import { Service } from 'vona-module-a-bean';
5
+ import { roleSiteIdAll } from 'vona-module-a-openapiutils';
5
6
 
6
7
  import type { DtoSiteCatalogSelectRes } from '../dto/siteCatalogSelectRes.ts';
7
8
 
8
9
  @Service()
9
10
  export class ServiceSiteCatalog extends BeanBase {
10
11
  async select(): Promise<DtoSiteCatalogSelectRes> {
11
- const list = this.app
12
- .scope('a-ssr')
13
- .service.ssr.getSitesEnabled()
14
- .map(site => {
15
- const options = site.beanOptions.options as IDecoratorSsrSiteOptions;
16
- return {
17
- siteId: String(options.siteId),
18
- title: this.app.meta.text.locale(this.ctx.locale, options.title),
19
- };
20
- });
12
+ const list = [
13
+ {
14
+ siteId: roleSiteIdAll,
15
+ title: this.scope.locale.SiteAll(),
16
+ },
17
+ ...this.app
18
+ .scope('a-ssr')
19
+ .service.ssr.getSitesEnabled()
20
+ .map(site => {
21
+ const options = site.beanOptions.options as IDecoratorSsrSiteOptions;
22
+ return {
23
+ siteId: String(options.siteId),
24
+ title: this.app.meta.text.locale(this.ctx.locale, options.title),
25
+ };
26
+ }),
27
+ ];
21
28
  return {
22
29
  list,
23
30
  total: String(list.length),
@@ -21,6 +21,10 @@ export class MetaVersion extends BeanBase implements IMetaVersionUpdate, IMetaVe
21
21
  table.string(entityRole.title, 255).comment(entityRole.$comment.title);
22
22
  table.json(entityRole.titleLocales).comment(entityRole.$comment.titleLocales);
23
23
  table.json(entityRole.siteIds).comment(entityRole.$comment.siteIds);
24
+ table
25
+ .boolean(entityRole.builtin)
26
+ .defaultTo(entityRole.$default.builtin)
27
+ .comment(entityRole.$comment.builtin);
24
28
  });
25
29
  // homeUser
26
30
  const entityUser = this.scope.entity.user;
@@ -62,6 +66,7 @@ export class MetaVersion extends BeanBase implements IMetaVersionUpdate, IMetaVe
62
66
  title: role.title,
63
67
  titleLocales: role.titleLocales ?? {},
64
68
  siteIds: role.siteIds,
69
+ builtin: role.builtin,
65
70
  });
66
71
  }
67
72
  // user: admin