cabloy 5.1.152 → 5.1.154

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 (95) hide show
  1. package/.cabloy-version +1 -1
  2. package/.claude/skills/cabloy-module-removal/SKILL.md +1 -1
  3. package/.claude/skills/cabloy-resource-field-update/SKILL.md +1 -1
  4. package/.claude/skills/cabloy-spec-generation/SKILL.md +1 -1
  5. package/.claude/skills/cabloy-spec-generation/references/repo-aware-discovery.md +1 -1
  6. package/.claude/skills/cabloy-spec-generation/scripts/generate-implementation-charts.mjs +46 -11
  7. package/.claude/skills/cabloy-spec-generation/scripts/generate-implementation-charts.test.mjs +13 -0
  8. package/.claude/skills/cabloy-workflow/SKILL.md +13 -14
  9. package/.claude/skills/cabloy-workflow/evals/evals.json +4 -4
  10. package/CHANGELOG.md +27 -0
  11. package/CLAUDE.md +2 -3
  12. package/package.json +1 -1
  13. package/repo-docs/.vitepress/config.mjs +5 -0
  14. package/repo-docs/ai/cli-to-skill-map.md +19 -0
  15. package/repo-docs/ai/docs-skills-rules-mapping.md +21 -6
  16. package/repo-docs/ai/future-skill-roadmap.md +17 -72
  17. package/repo-docs/ai/introduction.md +15 -5
  18. package/repo-docs/ai/playbook-spec-execution.md +144 -0
  19. package/repo-docs/ai/playbook-spec-generation.md +155 -0
  20. package/repo-docs/ai/repo-guidance.md +9 -5
  21. package/repo-docs/ai/rules-and-config.md +2 -2
  22. package/repo-docs/ai/skills.md +4 -0
  23. package/repo-docs/ai/virtual-decorator-guidance.md +1 -1
  24. package/repo-docs/backend/introduction.md +1 -1
  25. package/repo-docs/backend/menu-guide.md +3 -2
  26. package/repo-docs/frontend/model-resource-cookbook.md +38 -7
  27. package/repo-docs/frontend/model-resource-usage-guide.md +2 -1
  28. package/repo-docs/frontend/router-tabs-introduction.md +1 -1
  29. package/repo-docs/frontend/use-state-data-best-practices.md +39 -6
  30. package/repo-docs/reference/package-map.md +4 -1
  31. package/repo-docs/reference/repo-scripts.md +25 -0
  32. package/repo-e2e/specs/cabloy-basic.spec.ts +69 -20
  33. package/test-results/.last-run.json +4 -2
  34. package/test-results/cabloy-basic-ATP-BASIC-SUM-58d8a-ry-states-and-Markdown-HTML/error-context.md +195 -0
  35. package/vona/env/.env +8 -0
  36. package/vona/packages-vona/vona/package.json +1 -1
  37. package/vona/pnpm-lock.yaml +81 -145
  38. package/vona/src/suite/a-commerce/modules/commerce-siteadmin/package.json +1 -2
  39. package/vona/src/suite/a-commerce/modules/commerce-siteadmin/src/.metadata/index.ts +40 -65
  40. package/vona/src/suite/a-home/modules/home-base/src/.metadata/index.ts +78 -72
  41. package/vona/src/suite/a-home/modules/home-base/src/.metadata/locales.ts +18 -0
  42. package/vona/src/suite/a-home/modules/home-base/src/config/locale/en-us.ts +3 -0
  43. package/vona/src/suite/a-home/modules/home-base/src/config/locale/zh-cn.ts +3 -0
  44. package/vona/src/suite/a-home/modules/home-base/src/index.ts +1 -0
  45. package/vona/src/suite/a-home/modules/home-base/src/service/siteCatalog.ts +17 -10
  46. package/vona/src/suite/a-home/modules/home-user/src/bean/meta.version.ts +5 -0
  47. package/vona/src/suite/a-home/modules/home-user/src/config/config.ts +36 -5
  48. package/vona/src/suite/a-home/modules/home-user/src/config/roles.ts +7 -2
  49. package/vona/src/suite/a-home/modules/home-user/src/entity/role.ts +3 -0
  50. package/vona/src/suite/a-home/modules/home-user/src/types/env.ts +11 -0
  51. package/vona/src/suite/a-home/modules/home-user/src/types/index.ts +1 -0
  52. package/vona/src/suite/a-home/modules/home-user/test/config.test.ts +65 -0
  53. package/vona/src/suite/a-home/modules/home-user/test/role.test.ts +14 -6
  54. package/vona/src/suite-vendor/a-cabloy/modules/a-rbac/package.json +1 -1
  55. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/package.json +1 -1
  56. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/.metadata/index.ts +17 -2
  57. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/bean/event.resolveMenuVisibility.ts +19 -0
  58. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/lib/beanSsrSiteBase.ts +32 -16
  59. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/lib/index.ts +2 -0
  60. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/lib/ssrMenuEligibility.ts +107 -0
  61. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/lib/ssrMenuVisibility.ts +91 -0
  62. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/service/ssr.ts +30 -0
  63. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/types/ssrMenu.ts +67 -0
  64. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/test/ssrMenuEligibility.test.ts +135 -0
  65. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/test/ssrMenuVisibility.test.ts +64 -0
  66. package/vona/src/suite-vendor/a-cabloy/package.json +2 -2
  67. package/vona/src/suite-vendor/a-vona/modules/a-openapiutils/package.json +1 -1
  68. package/vona/src/suite-vendor/a-vona/modules/a-openapiutils/src/lib/const/index.ts +1 -0
  69. package/vona/src/suite-vendor/a-vona/modules/a-openapiutils/src/lib/const/site.ts +1 -0
  70. package/vona/src/suite-vendor/a-vona/modules/a-permission/package.json +1 -1
  71. package/vona/src/suite-vendor/a-vona/modules/a-user/package.json +1 -1
  72. package/vona/src/suite-vendor/a-vona/modules/a-user/src/types/role.ts +1 -0
  73. package/vona/src/suite-vendor/a-vona/package.json +1 -1
  74. package/zova/pnpm-lock.yaml +1 -1
  75. package/zova/src/suite/a-commerce/modules/commerce-trade/src/page/payment/controller.tsx +3 -2
  76. package/zova/src/suite/a-home/modules/home-api/mock/passport.fake.ts +6 -6
  77. package/zova/src/suite/a-home/modules/home-base/src/lib/index.ts +1 -0
  78. package/zova/src/suite/a-home/modules/home-base/src/lib/roleSiteAdmission.ts +15 -0
  79. package/zova/src/suite/a-home/modules/home-base/src/service/routerGuards.ts +3 -3
  80. package/zova/src/suite/a-home/modules/home-layoutadmin/src/model/menu.ts +4 -3
  81. package/zova/src/suite/a-home/modules/home-layoutweb/src/model/menu.ts +4 -3
  82. package/zova/src/suite/a-home/modules/home-passport/package.json +2 -1
  83. package/zova/src/suite/a-home/modules/home-passport/src/model/passport.ts +2 -2
  84. package/zova/src/suite/a-home/modules/home-passport/test/lib/roleSiteAdmission.test.ts +23 -0
  85. package/zova/src/suite/a-training/modules/training-student/src/bean/tableCell.actionSummary.tsx +29 -6
  86. package/zova/src/suite/a-training/modules/training-student/src/config/locale/en-us.ts +1 -0
  87. package/zova/src/suite/a-training/modules/training-student/src/config/locale/zh-cn.ts +1 -0
  88. package/zova/src/suite/a-training/modules/training-student/src/model/student.ts +1 -1
  89. package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/package.json +1 -1
  90. package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/src/lib/mutationSuccess.ts +11 -0
  91. package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/src/model/resource.ts +12 -7
  92. package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/test/lib/mutationSuccess.test.ts +95 -0
  93. package/zova/src/suite-vendor/a-cabloy/package.json +2 -2
  94. package/vona/src/suite/a-commerce/modules/commerce-siteadmin/src/bean/meta.version.ts +0 -32
  95. package/vona/src/suite/a-commerce/modules/commerce-siteadmin/test/meta.version.test.ts +0 -44
@@ -42,6 +42,31 @@ Cabloy Basic exposes these shared root scripts:
42
42
 
43
43
  `npm run init` prepares all Cabloy Basic SSR and REST artifacts with `npm run build:zova:all`, which sequentially builds the Basic and Commerce flavor batches before Vona initialization. Use `build:zova` or `build:zova:commerce` for focused artifact refreshes; use `build:zova:all` only when every shipped Basic flavor must be prepared.
44
44
 
45
+ ## Specification planning and derived charts
46
+
47
+ The current Cabloy Basic root scripts also expose:
48
+
49
+ ```bash
50
+ npm run test:spec-charts
51
+ npm run spec:charts -- <suite>
52
+ npm run spec:charts:check -- <suite>
53
+ ```
54
+
55
+ `spec:charts` refreshes the generated Gantt and burndown SVG views for a chart-compatible `repo-specs/<suite>/` record. `spec:charts:check` validates the supported input contract and detects stale generated views; `test:spec-charts` runs the chart-tool test suite.
56
+
57
+ ### Chart input contract
58
+
59
+ The generator consumes `README.md`, `pdp-wbs.md`, `test-plan.md`, and `progress.md`. The supported Markdown format includes:
60
+
61
+ - formal `### Phase <number>:` and `#### WBS-...:` headings in the WBS, with supported dependency labels
62
+ - formally defined `ATP-*` scenarios in the test plan for every ATP reference used by a WBS task
63
+ - one progress row for each WBS item, with the WBS ID in the first cell and its supported status in the second cell
64
+ - a README whose current title and language should be reflected by regenerated chart output
65
+
66
+ A legacy suite with a different WBS or progress-table layout is not chart-compatible until a deliberate record-format normalization aligns its authoritative Markdown with this input contract. Format normalization must preserve the existing planning authority; it does not require an unrelated product or delivery change.
67
+
68
+ These commands do not create planning authority, implement a WBS task, execute an ATP, produce acceptance evidence, or replace traceability/status review. Confirm the active root `package.json` and script input expectations before assuming equivalent behavior in Cabloy Start or another repository.
69
+
45
70
  ## Cabloy Start entrypoints
46
71
 
47
72
  Cabloy Start exposes the equivalent Start repository surface:
@@ -414,7 +414,7 @@ test(
414
414
  );
415
415
 
416
416
  test(
417
- 'ATP-BASIC-SUMMARY-01: Training Student summary renders Markdown HTML in a dialog',
417
+ 'ATP-BASIC-SUMMARY-01: Training Student summary dialog renders query states and Markdown HTML',
418
418
  { tag: ['@admin', '@flow'] },
419
419
  async ({ page }) => {
420
420
  const pageErrors = collectPageErrors(page);
@@ -466,25 +466,74 @@ test(
466
466
  await page.getByRole('button', { name: 'Search', exact: true }).click();
467
467
  await searchResponse;
468
468
 
469
- const row = page.locator('tr').filter({ hasText: studentName });
470
- await expect(row).toHaveCount(1);
471
- const summaryResponse = page.waitForResponse(response => {
472
- const url = new URL(response.url());
473
- return (
474
- response.request().method() === 'GET' &&
475
- response.ok() &&
476
- url.pathname === `/api/training/student/summary/${studentId}` &&
477
- !response.request().headers()['x-vona-openapi-schema']
478
- );
479
- });
480
- await row.getByRole('button', { name: 'Summary', exact: true }).click();
481
- await summaryResponse;
482
-
483
- const description = page.locator('.student-summary-description');
484
- await expect(description).toBeVisible();
485
- await expect(description.locator('h2')).toHaveText('Summary heading');
486
- await expect(description).toContainText(`Summary paragraph ${studentName}`);
487
- expect(pageErrors).toEqual([]);
469
+ const summaryPath = `/api/training/student/summary/${studentId}`;
470
+ let summaryMode: 'failure' | 'success' = 'failure';
471
+ let releaseSummaryRequest: (() => void) | undefined;
472
+ let summaryRequest: Promise<void> | undefined;
473
+ let releaseSummaryResponse: (() => void) | undefined;
474
+ let summaryResponseRelease: Promise<void> | undefined;
475
+ const summaryHandler = async (route: Route) => {
476
+ releaseSummaryRequest?.();
477
+ if (summaryMode === 'failure') {
478
+ await route.fulfill({
479
+ status: 500,
480
+ contentType: 'application/json',
481
+ body: JSON.stringify({ code: 500, message: 'Summary temporarily unavailable' }),
482
+ });
483
+ return;
484
+ }
485
+ const response = await route.fetch();
486
+ await summaryResponseRelease;
487
+ await route.fulfill({ response });
488
+ };
489
+ const summaryRouteUrl = (url: URL) => url.pathname === summaryPath;
490
+ await page.route(summaryRouteUrl, summaryHandler);
491
+ try {
492
+ let row = page.locator('tr').filter({ hasText: studentName });
493
+ await expect(row).toHaveCount(1);
494
+ await row.getByRole('button', { name: 'Summary', exact: true }).click();
495
+
496
+ const dialog = page.getByRole('dialog');
497
+ await expect(dialog).toBeVisible();
498
+ await expect(dialog.locator('.alert.alert-error')).toHaveCount(1);
499
+ await expect(dialog.locator('.student-summary-description')).toHaveCount(0);
500
+ await page.keyboard.press('Escape');
501
+ await expect(dialog).toHaveCount(0);
502
+
503
+ summaryMode = 'success';
504
+ summaryRequest = new Promise(resolve => {
505
+ releaseSummaryRequest = resolve;
506
+ });
507
+ summaryResponseRelease = new Promise(resolve => {
508
+ releaseSummaryResponse = resolve;
509
+ });
510
+ const listResponse = await page.goto('/admin/rest/resource/training-student%3Astudent', {
511
+ waitUntil: 'load',
512
+ });
513
+ expect(listResponse?.ok()).toBeTruthy();
514
+ await expect(page.locator('html')).toHaveAttribute('data-zova-hydrated', 'admin');
515
+ await page.getByLabel('Student Name').fill(studentName);
516
+ const nextSearchResponse = waitForStudentSelect(page);
517
+ await page.getByRole('button', { name: 'Search', exact: true }).click();
518
+ await nextSearchResponse;
519
+
520
+ row = page.locator('tr').filter({ hasText: studentName });
521
+ await expect(row).toHaveCount(1);
522
+ await row.getByRole('button', { name: 'Summary', exact: true }).click();
523
+ await summaryRequest;
524
+ await expect(dialog).toHaveCount(0, { timeout: 250 });
525
+ releaseSummaryResponse?.();
526
+
527
+ const description = dialog.locator('.student-summary-description');
528
+ await expect(dialog).toBeVisible();
529
+ await expect(description).toBeVisible();
530
+ await expect(description.locator('h2')).toHaveText('Summary heading');
531
+ await expect(description).toContainText(`Summary paragraph ${studentName}`);
532
+ expect(pageErrors).toEqual([]);
533
+ } finally {
534
+ releaseSummaryResponse?.();
535
+ await page.unroute(summaryRouteUrl, summaryHandler);
536
+ }
488
537
  } finally {
489
538
  try {
490
539
  if (studentId !== undefined) {
@@ -1,4 +1,6 @@
1
1
  {
2
- "status": "passed",
3
- "failedTests": []
2
+ "status": "failed",
3
+ "failedTests": [
4
+ "0d2518050eca8cd1231a-03158dd238416d7c8c3e"
5
+ ]
4
6
  }
@@ -0,0 +1,195 @@
1
+ # Instructions
2
+
3
+ - Following Playwright test failed.
4
+ - Explain why, be concise, respect Playwright best practices.
5
+ - Provide a snippet of code with the fix, if possible.
6
+
7
+ # Test info
8
+
9
+ - Name: cabloy-basic.spec.ts >> ATP-BASIC-SUMMARY-01: Training Student summary dialog renders query states and Markdown HTML
10
+ - Location: repo-e2e/specs/cabloy-basic.spec.ts:416:1
11
+
12
+ # Error details
13
+
14
+ ```
15
+ Error: expect(page).not.toHaveURL(expected) failed
16
+
17
+ Expected pattern: not /\/admin\/login(?:\?|$)/
18
+ Received string: "http://127.0.0.1:7102/admin/login"
19
+ Timeout: 5000ms
20
+
21
+ Call log:
22
+ - Expect "not toHaveURL" with timeout 5000ms
23
+ 14 × locator resolved to <html lang="en-us" data-zova-hydrated="admin">…</html>
24
+ - unexpected value "http://127.0.0.1:7102/admin/login"
25
+
26
+ ```
27
+
28
+ ```yaml
29
+ - heading "Zova" [level=1]
30
+ - heading "Less is more, while more is less" [level=5]
31
+ - heading "Login" [level=2]
32
+ - textbox:
33
+ - /placeholder: Your Username
34
+ - text: admin
35
+ - textbox:
36
+ - /placeholder: Your Password
37
+ - text: "123456"
38
+ - textbox:
39
+ - /placeholder: Please input captcha
40
+ - text: TrAs
41
+ - img
42
+ - button "Login"
43
+ - button "Create account"
44
+ - button "Forgot password?"
45
+ - text: OR
46
+ - button "Sign in with GitHub"
47
+ - dialog:
48
+ - heading "Cabloy Basic" [level=3]
49
+ - paragraph: "select `aAuthProvider`.`id` from `aAuthProvider` where `providerName` = 'auth-simple:simple' and `clientName` = 'default' and `aAuthProvider`.`iid` = 1 - no such table: aAuthProvider"
50
+ - button "Close"
51
+ ```
52
+
53
+ # Test source
54
+
55
+ ```ts
56
+ 1 | import type { Locator, Page, Route } from '@playwright/test';
57
+ 2 |
58
+ 3 | import { expect, test } from '@playwright/test';
59
+ 4 |
60
+ 5 | const studentResourceUrl =
61
+ 6 | /\/admin\/rest\/resource\/training-student(?:%3A|:|%253A)student(?:[/?#]|$)/;
62
+ 7 |
63
+ 8 | interface IFieldGeometry {
64
+ 9 | container: {
65
+ 10 | display: string;
66
+ 11 | flexWrap: string;
67
+ 12 | left: number;
68
+ 13 | right: number;
69
+ 14 | };
70
+ 15 | field: {
71
+ 16 | top: number;
72
+ 17 | left: number;
73
+ 18 | right: number;
74
+ 19 | bottom: number;
75
+ 20 | };
76
+ 21 | }
77
+ 22 |
78
+ 23 | function collectPageErrors(page: Page) {
79
+ 24 | const errors: Error[] = [];
80
+ 25 | page.on('pageerror', error => {
81
+ 26 | errors.push(error);
82
+ 27 | });
83
+ 28 | return errors;
84
+ 29 | }
85
+ 30 |
86
+ 31 | async function loginAsAdmin(page: Page) {
87
+ 32 | await page.goto('/admin/', { waitUntil: 'load' });
88
+ 33 | if (page.url().includes('/admin/login')) {
89
+ 34 | await page.getByPlaceholder('Your Username').fill('admin');
90
+ 35 | await page.getByPlaceholder('Your Password').fill('123456');
91
+ 36 | await expect(page.getByPlaceholder('Please input captcha')).not.toHaveValue('');
92
+ 37 | await page.getByRole('button', { name: 'Login', exact: true }).click();
93
+ > 38 | await expect(page).not.toHaveURL(/\/admin\/login(?:\?|$)/);
94
+ | ^ Error: expect(page).not.toHaveURL(expected) failed
95
+ 39 | }
96
+ 40 |
97
+ 41 | await expect(page.locator('html')).toHaveAttribute('data-zova-hydrated', 'admin');
98
+ 42 | const dashboardResponse = await page.goto('/admin/', { waitUntil: 'load' });
99
+ 43 | await expect(page.locator('html')).toHaveAttribute('data-zova-hydrated', 'admin');
100
+ 44 | return dashboardResponse;
101
+ 45 | }
102
+ 46 |
103
+ 47 | function waitForStudentSelect(page: Page) {
104
+ 48 | return page.waitForResponse(response => {
105
+ 49 | const url = new URL(response.url());
106
+ 50 | return (
107
+ 51 | response.request().method() === 'GET' &&
108
+ 52 | response.ok() &&
109
+ 53 | url.pathname === '/api/training/student' &&
110
+ 54 | !response.request().headers()['x-vona-openapi-schema']
111
+ 55 | );
112
+ 56 | });
113
+ 57 | }
114
+ 58 |
115
+ 59 | function installStudentCreateResponseCapture(page: Page) {
116
+ 60 | let studentId: string | number | undefined;
117
+ 61 | const handler = async (route: Route) => {
118
+ 62 | const request = route.request();
119
+ 63 | const url = new URL(request.url());
120
+ 64 | if (
121
+ 65 | request.method() !== 'POST' ||
122
+ 66 | url.pathname !== '/api/training/student' ||
123
+ 67 | request.headers()['x-vona-openapi-schema']
124
+ 68 | ) {
125
+ 69 | await route.continue();
126
+ 70 | return;
127
+ 71 | }
128
+ 72 | const response = await route.fetch();
129
+ 73 | const payload = await response.json();
130
+ 74 | if (response.ok() && ['string', 'number'].includes(typeof payload.data)) {
131
+ 75 | studentId = payload.data;
132
+ 76 | }
133
+ 77 | await route.fulfill({ response });
134
+ 78 | };
135
+ 79 | return {
136
+ 80 | async install() {
137
+ 81 | await page.route('**/api/training/student', handler);
138
+ 82 | },
139
+ 83 | async uninstall() {
140
+ 84 | await page.unroute('**/api/training/student', handler);
141
+ 85 | },
142
+ 86 | get studentId() {
143
+ 87 | return studentId;
144
+ 88 | },
145
+ 89 | };
146
+ 90 | }
147
+ 91 |
148
+ 92 | async function getFieldGeometry(locator: Locator): Promise<IFieldGeometry> {
149
+ 93 | return locator.evaluate(element => {
150
+ 94 | const field = element.closest('label')?.parentElement;
151
+ 95 | const container = field?.parentElement;
152
+ 96 | if (!field || !container) throw new Error('Could not resolve the filter field layout');
153
+ 97 |
154
+ 98 | const fieldRect = field.getBoundingClientRect();
155
+ 99 | const containerRect = container.getBoundingClientRect();
156
+ 100 | const containerStyle = getComputedStyle(container);
157
+ 101 | return {
158
+ 102 | container: {
159
+ 103 | display: containerStyle.display,
160
+ 104 | flexWrap: containerStyle.flexWrap,
161
+ 105 | left: containerRect.left,
162
+ 106 | right: containerRect.right,
163
+ 107 | },
164
+ 108 | field: {
165
+ 109 | top: fieldRect.top,
166
+ 110 | left: fieldRect.left,
167
+ 111 | right: fieldRect.right,
168
+ 112 | bottom: fieldRect.bottom,
169
+ 113 | },
170
+ 114 | };
171
+ 115 | });
172
+ 116 | }
173
+ 117 |
174
+ 118 | async function getFlowGeometry(name: Locator, level: Locator, createdAtStart: Locator) {
175
+ 119 | return Promise.all([
176
+ 120 | getFieldGeometry(name),
177
+ 121 | getFieldGeometry(level),
178
+ 122 | getFieldGeometry(createdAtStart),
179
+ 123 | ]);
180
+ 124 | }
181
+ 125 |
182
+ 126 | function assertVisualOrder(geometry: IFieldGeometry[]) {
183
+ 127 | for (let index = 1; index < geometry.length; index++) {
184
+ 128 | const previous = geometry[index - 1].field;
185
+ 129 | const current = geometry[index].field;
186
+ 130 | expect(
187
+ 131 | current.top > previous.top + 2 ||
188
+ 132 | (Math.abs(current.top - previous.top) <= 2 && current.left > previous.left),
189
+ 133 | ).toBeTruthy();
190
+ 134 | }
191
+ 135 | }
192
+ 136 |
193
+ 137 | async function getDocumentHorizontalOverflow(page: Page) {
194
+ 138 | return page.evaluate(() => {
195
+ ```
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": [