cabloy 5.1.187 → 5.1.189

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 (47) hide show
  1. package/.cabloy-version +1 -1
  2. package/CHANGELOG.md +22 -0
  3. package/package.json +1 -1
  4. package/repo-docs/backend/runtime-and-flavors.md +1 -1
  5. package/repo-e2e/specs/markdown.spec.ts +46 -1
  6. package/vona/docker-compose-original/config/nginx/conf.d/nginx.conf +2 -2
  7. package/vona/packages-cli/cli/package.json +1 -1
  8. package/vona/packages-cli/cli-set-api/package.json +1 -1
  9. package/vona/packages-vona/vona/package.json +1 -1
  10. package/vona/packages-vona/vona-core/package.json +1 -1
  11. package/vona/packages-vona/vona-core/src/lib/core/application.ts +12 -5
  12. package/vona/packages-vona/vona-core/src/lib/core/context.ts +1 -1
  13. package/vona/packages-vona/vona-mock/package.json +1 -1
  14. package/vona/pnpm-lock.yaml +14 -136
  15. package/vona/src/suite/a-home/modules/home-user/src/service/account.ts +1 -1
  16. package/vona/src/suite/a-home/modules/home-user/test/accountPasswordReset.test.ts +24 -5
  17. package/vona/src/suite/a-home/modules/home-user/test/accountPasswordSet.test.ts +22 -5
  18. package/vona/src/suite-vendor/a-auth/modules/a-auth/package.json +1 -1
  19. package/vona/src/suite-vendor/a-auth/modules/a-auth/src/lib/utils.ts +42 -0
  20. package/vona/src/suite-vendor/a-auth/modules/a-auth/src/service/auth.ts +60 -2
  21. package/vona/src/suite-vendor/a-auth/modules/a-auth/test/authCallbackRedirect.test.ts +44 -0
  22. package/vona/src/suite-vendor/a-auth/modules/a-auth/test/oauthRequestContext.test.ts +171 -0
  23. package/vona/src/suite-vendor/a-auth/package.json +1 -1
  24. package/vona/src/suite-vendor/a-cabloy/modules/a-socket/package.json +1 -1
  25. package/vona/src/suite-vendor/a-cabloy/modules/a-socket/src/bean/socketConnection.cors.ts +1 -1
  26. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/package.json +1 -1
  27. package/vona/src/suite-vendor/a-cabloy/package.json +1 -1
  28. package/vona/src/suite-vendor/a-vona/modules/a-core/package.json +1 -1
  29. package/vona/src/suite-vendor/a-vona/modules/a-security/package.json +1 -1
  30. package/vona/src/suite-vendor/a-vona/modules/a-security/src/bean/middlewareSystem.cors.ts +1 -1
  31. package/vona/src/suite-vendor/a-vona/modules/a-version/package.json +1 -1
  32. package/vona/src/suite-vendor/a-vona/modules/a-version/src/lib/databaseTestName.ts +47 -0
  33. package/vona/src/suite-vendor/a-vona/modules/a-version/src/service/database.ts +8 -3
  34. package/vona/src/suite-vendor/a-vona/modules/a-version/test/databaseName.test.ts +72 -0
  35. package/vona/src/suite-vendor/a-vona/package.json +1 -1
  36. package/zova/packages-cli/cli/package.json +2 -2
  37. package/zova/packages-cli/cli-set-front/package.json +2 -2
  38. package/zova/packages-utils/zova-vite/package.json +1 -1
  39. package/zova/packages-utils/zova-vite/src/collect-css-ssr.ts +7 -5
  40. package/zova/pnpm-lock.yaml +16 -16
  41. package/zova/src/suite/a-home/modules/home-base/src/config/config.ts +0 -8
  42. package/zova/src/suite/a-home/modules/home-base/src/service/ssrLayout.ts +2 -3
  43. package/zova/src/suite/a-home/modules/home-layoutadmin/src/component/layoutAdmin/controller.tsx +1 -0
  44. package/zova/src/suite/a-home/modules/home-layoutadmin/src/component/layoutAdmin/render.sidebar.tsx +1 -4
  45. package/zova/src/suite/a-home/modules/home-layoutadmin/src/config/config.ts +4 -0
  46. package/zova/src/suite/a-home/modules/home-layoutweb/src/component/layoutWeb/controller.tsx +1 -0
  47. package/zova/src/suite/cabloy-basic/modules/basic-markdown/src/lib/richTextContentStyle.ts +16 -2
package/.cabloy-version CHANGED
@@ -1 +1 @@
1
- 5.1.187
1
+ 5.1.189
package/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # Changelog
2
2
 
3
+ ## 5.1.189
4
+
5
+ ### Features
6
+
7
+ - Add anonymous context creation support.
8
+ - Add home user functionality.
9
+ - Add application host utility access through the context.
10
+ - Add authentication enhancements.
11
+ - Update core feature behavior.
12
+
13
+ ## 5.1.188
14
+
15
+ ### Bug Fixes
16
+
17
+ - Enforce database name length limits.
18
+ - Correct markdown task list item rendering.
19
+
20
+ ### Improvements
21
+
22
+ - Refactor the layout implementation.
23
+ - Update SSR CSS collection handling.
24
+
3
25
  ## 5.1.187
4
26
 
5
27
  ### Improvements
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cabloy",
3
- "version": "5.1.187",
3
+ "version": "5.1.189",
4
4
  "gitHead": "2c5c19284bab738e492856189acb6fad74b8a7b7",
5
5
  "description": "A Node.js fullstack framework",
6
6
  "keywords": [
@@ -133,7 +133,7 @@ Use this page as the runtime-facing overview, then inspect the current app confi
133
133
 
134
134
  When configured, `SERVER_SERVE_PROTOCOL` and `SERVER_SERVE_HOST` define Vona's canonical public origin for backend APIs and backend-hosted SSR sites. If either serve value is not configured, Vona resolves the effective protocol and host from the current proxy-aware request context; background work that needs a stable backend public origin should configure both serve values explicitly.
135
135
 
136
- A browser consumer can be deployed on a different origin and pathname. Account password-set and password-reset delivery therefore does not derive its destination from `SERVER_SERVE_*`, SSR Site metadata, `publicPath`, `siteId`, `Referer`, or browser-selected mount data: Zova supplies the complete token-free absolute public page URL, and Vona validates only HTTP(S), absence of userinfo/query/fragment, and an exact trusted origin. `checkOriginExact` permits the exact current origin when protocol, host, and effective port all match; request or proxy Host values are not a general cross-origin allowlist and cannot authorize lookalike, suffix, alternate-scheme, or alternate-port destinations. Other production consumer origins must exactly match an explicit `a-security:cors` `whiteList` entry. In `dev/test`, exact credential-link authorization additionally accepts different ports only when both API and consumer hostnames are loopback (`localhost`, `127.0.0.1`, or `::1`). Vona preserves the frontend pathname and alone adds the raw token as the `token` URL query parameter. Query transport reaches the initial request; Referrer-Policy and Vona, edge, or APM query-log redaction are intentionally outside this flow's current scope. Normal CORS defaults to an empty `whiteList`; configure `CORS_WHITE_LIST` with the permitted browser origins, or explicitly configure `a-security:cors`. In `dev/test`, loopback requests may cross ports; elsewhere, cross-origin CORS requires an allowed entry. Normal CORS wildcard and suffix matching remain available when configured but are not credential-link authorization.
136
+ A browser consumer can be deployed on a different origin and pathname. Account password-set and password-reset delivery does not derive its destination from SSR Site metadata, `publicPath`, `siteId`, `Referer`, or browser-selected mount data: Zova supplies the complete token-free absolute public page URL, and Vona validates only HTTP(S), absence of userinfo/query/fragment, and an exact trusted origin. Production origin checks supply `app.util.host` as their same-host authority: when configured, `SERVER_SERVE_HOST` is the canonical host; otherwise Vona falls back to the current proxy-aware request host. `checkOriginExact` permits an origin whose protocol, canonical/fallback host, and effective port match; request or proxy Host values are not a general cross-origin allowlist and cannot authorize lookalike, suffix, alternate-scheme, or alternate-port destinations when a canonical host is configured. Other production consumer origins must exactly match an explicit `a-security:cors` `whiteList` entry. This change supplies the canonical host only; `checkOriginExact` continues to obtain the protocol from the active request context. In `dev/test`, exact credential-link authorization additionally accepts different ports only when both API and consumer hostnames are loopback (`localhost`, `127.0.0.1`, or `::1`). Vona preserves the frontend pathname and alone adds the raw token as the `token` URL query parameter. Query transport reaches the initial request; Referrer-Policy and Vona, edge, or APM query-log redaction are intentionally outside this flow's current scope. Normal CORS defaults to an empty `whiteList`; configure `CORS_WHITE_LIST` with the permitted browser origins, or explicitly configure `a-security:cors`. In `dev/test`, loopback requests may cross ports; elsewhere, cross-origin CORS requires an allowed entry. Normal CORS wildcard and suffix matching remain available when configured but are not credential-link authorization.
137
137
 
138
138
  ## Configure browser CORS origins
139
139
 
@@ -1,4 +1,4 @@
1
- import type { Page } from '@playwright/test';
1
+ import type { Locator, Page } from '@playwright/test';
2
2
 
3
3
  import { expect, test } from '@playwright/test';
4
4
 
@@ -35,6 +35,49 @@ function expectTableIdentity(value: unknown) {
35
35
  expect(['string', 'number']).toContain(typeof value);
36
36
  }
37
37
 
38
+ async function expectTaskCheckboxLayout(root: Locator) {
39
+ const layout = await root.locator('ul[data-type="taskList"] li').evaluateAll(taskItems =>
40
+ taskItems.map(taskItem => {
41
+ const checkbox = taskItem.querySelector<HTMLInputElement>('label > input[type="checkbox"]');
42
+ const label = taskItem.querySelector<HTMLElement>('label');
43
+ const text = taskItem.querySelector<HTMLElement>('div > p');
44
+ if (!checkbox || !label || !text) return null;
45
+
46
+ const checkboxRect = checkbox.getBoundingClientRect();
47
+ const textRect = text.getBoundingClientRect();
48
+ const checkboxStyle = getComputedStyle(checkbox);
49
+ const labelStyle = getComputedStyle(label);
50
+ const itemStyle = getComputedStyle(taskItem);
51
+ return {
52
+ checkboxHeight: checkboxRect.height,
53
+ checkboxWidth: checkboxRect.width,
54
+ checkboxCenter: checkboxRect.top + checkboxRect.height / 2,
55
+ checkboxMarginBlockEnd: checkboxStyle.marginBlockEnd,
56
+ checkboxMarginBlockStart: checkboxStyle.marginBlockStart,
57
+ checkboxMarginInlineEnd: checkboxStyle.marginInlineEnd,
58
+ checkboxMarginInlineStart: checkboxStyle.marginInlineStart,
59
+ itemDisplay: itemStyle.display,
60
+ labelDisplay: labelStyle.display,
61
+ textCenter: textRect.top + Number.parseFloat(getComputedStyle(text).lineHeight) / 2,
62
+ };
63
+ }),
64
+ );
65
+
66
+ expect(layout).toHaveLength(2);
67
+ for (const task of layout) {
68
+ expect(task).not.toBeNull();
69
+ expect(task?.itemDisplay).toBe('flex');
70
+ expect(task?.labelDisplay).toBe('flex');
71
+ expect(task?.checkboxWidth).toBeGreaterThan(0);
72
+ expect(task?.checkboxHeight).toBe(task?.checkboxWidth);
73
+ expect(task?.checkboxMarginBlockStart).toBe('0px');
74
+ expect(task?.checkboxMarginBlockEnd).toBe('0px');
75
+ expect(task?.checkboxMarginInlineStart).toBe('0px');
76
+ expect(task?.checkboxMarginInlineEnd).toBe('0px');
77
+ expect(Math.abs((task?.checkboxCenter ?? 0) - (task?.textCenter ?? 0))).toBeLessThanOrEqual(2);
78
+ }
79
+ }
80
+
38
81
  async function login(
39
82
  page: Page,
40
83
  path: string,
@@ -181,6 +224,7 @@ test(
181
224
  await expect(description.locator('input[type="checkbox"]').nth(0)).toBeDisabled();
182
225
  await expect(description.locator('input[type="checkbox"]').nth(1)).toBeChecked();
183
226
  await expect(description.locator('input[type="checkbox"]').nth(1)).toBeDisabled();
227
+ await expectTaskCheckboxLayout(description);
184
228
  expect(webPageErrors).toEqual([]);
185
229
  expect(webConsoleErrors).toEqual([]);
186
230
  } finally {
@@ -353,6 +397,7 @@ const product = '${suffix}';
353
397
  await expect(editor.locator('input[type="checkbox"]')).toHaveCount(2);
354
398
  await expect(editor.locator('input[type="checkbox"]').nth(0)).not.toBeChecked();
355
399
  await expect(editor.locator('input[type="checkbox"]').nth(1)).toBeChecked();
400
+ await expectTaskCheckboxLayout(editor);
356
401
  await expect(editor.locator('blockquote')).toHaveCSS('border-left-width', '4px');
357
402
  await expect(editor.locator('pre')).toHaveCSS('overflow-x', 'auto');
358
403
  await expect(editor.locator('table th').first()).toHaveCSS('font-weight', '600');
@@ -12,9 +12,9 @@ server {
12
12
  proxy_http_version 1.1;
13
13
  proxy_set_header X-Real-IP $remote_addr;
14
14
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
15
- proxy_set_header X-Forwarded-Host $http_host;
15
+ proxy_set_header X-Forwarded-Host $host;
16
16
  proxy_set_header X-Forwarded-Proto $scheme;
17
- proxy_set_header Host $http_host;
17
+ proxy_set_header Host $host;
18
18
  proxy_set_header X-NginX-Proxy true;
19
19
  proxy_set_header Upgrade $http_upgrade;
20
20
  proxy_set_header Connection "upgrade";
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-cli",
3
- "version": "1.1.154",
3
+ "version": "1.1.155",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "vona cli",
6
6
  "keywords": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-cli-set-api",
3
- "version": "1.1.152",
3
+ "version": "1.1.153",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "vona cli-set-api",
6
6
  "keywords": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona",
3
- "version": "5.1.98",
3
+ "version": "5.1.100",
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": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-core",
3
- "version": "5.1.40",
3
+ "version": "5.1.41",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "vona",
6
6
  "keywords": [
@@ -72,22 +72,29 @@ export class VonaApplication extends KoaApplication {
72
72
  if (req) {
73
73
  request = req;
74
74
  } else {
75
- const host = `localhost:${this.meta.env.SERVER_LISTEN_PORT}`;
75
+ const port = this.config?.server.listen.port ?? this.meta.env.SERVER_LISTEN_PORT;
76
+ const hostFallback = port ? `localhost:${port}` : 'localhost';
77
+ const protocol = (this.config ? this.util.protocol : undefined) || 'http';
78
+ const host = (this.config ? this.util.host : undefined) || hostFallback;
79
+ const hostname = new URL(`${protocol}://${host}`).hostname;
76
80
  request = {
77
81
  headers: {
78
82
  'host': host,
79
- 'x-forwarded-for': host,
83
+ 'x-forwarded-for': '127.0.0.1',
84
+ 'x-forwarded-host': host,
85
+ 'x-forwarded-proto': protocol,
80
86
  },
81
87
  query: {},
82
88
  querystring: '',
83
89
  host,
84
- hostname: 'localhost',
85
- protocol: 'http',
86
- secure: 'false',
90
+ hostname,
91
+ protocol,
92
+ secure: protocol === 'https',
87
93
  method: 'POST',
88
94
  url: '',
89
95
  path: '',
90
96
  socket: {
97
+ encrypted: protocol === 'https',
91
98
  remoteAddress: '127.0.0.1',
92
99
  remotePort: 7001,
93
100
  },
@@ -127,7 +127,7 @@ export const contextBase: ContextBase = {
127
127
  typeof statusOrOptions === 'number' ? { status: statusOrOptions } : (statusOrOptions ?? {});
128
128
  // checkOrigin
129
129
  if (!url.startsWith('/') && !options.trustedExternal) {
130
- const origin = cast(self.app.bean).security.checkOrigin(url, self.host);
130
+ const origin = cast(self.app.bean).security.checkOrigin(url, self.app.util.host);
131
131
  if (!origin) self.app.throw(403);
132
132
  }
133
133
  // throw
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-mock",
3
- "version": "6.1.40",
3
+ "version": "6.1.41",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "vona mock",
6
6
  "keywords": [
@@ -741,11 +741,11 @@ importers:
741
741
  specifier: npm:@cabloy/zod@4.3.8
742
742
  version: '@cabloy/zod@4.3.8'
743
743
  zova:
744
- specifier: ^5.1.164
745
- version: 5.1.165(typescript@5.9.3)(vue@3.5.43(typescript@5.9.3))
744
+ specifier: ^5.1.166
745
+ version: 5.1.166(typescript@5.9.3)(vue@3.5.43(typescript@5.9.3))
746
746
  zova-jsx:
747
- specifier: ^1.1.90
748
- version: 1.1.91(typescript@5.9.3)
747
+ specifier: ^1.1.92
748
+ version: 1.1.92(typescript@5.9.3)
749
749
  zova-module-a-api:
750
750
  specifier: ^5.1.22
751
751
  version: 5.1.22
@@ -777,11 +777,11 @@ importers:
777
777
  specifier: ^5.1.54
778
778
  version: 5.1.54
779
779
  zova-module-a-pay:
780
- specifier: ^5.0.8
780
+ specifier: ^5.0.9
781
781
  version: 5.0.9
782
782
  zova-module-a-router:
783
- specifier: ^5.1.39
784
- version: 5.1.40
783
+ specifier: ^5.1.41
784
+ version: 5.1.41
785
785
  zova-module-a-routertabs:
786
786
  specifier: ^5.1.34
787
787
  version: 5.1.34
@@ -825,11 +825,11 @@ importers:
825
825
  specifier: npm:@cabloy/zod@4.3.8
826
826
  version: '@cabloy/zod@4.3.8'
827
827
  zova:
828
- specifier: ^5.1.164
829
- version: 5.1.165(typescript@5.9.3)(vue@3.5.43(typescript@5.9.3))
828
+ specifier: ^5.1.166
829
+ version: 5.1.166(typescript@5.9.3)(vue@3.5.43(typescript@5.9.3))
830
830
  zova-jsx:
831
- specifier: ^1.1.90
832
- version: 1.1.91(typescript@5.9.3)
831
+ specifier: ^1.1.92
832
+ version: 1.1.92(typescript@5.9.3)
833
833
  zova-module-a-api:
834
834
  specifier: ^5.1.22
835
835
  version: 5.1.22
@@ -861,11 +861,11 @@ importers:
861
861
  specifier: ^5.1.54
862
862
  version: 5.1.54
863
863
  zova-module-a-pay:
864
- specifier: ^5.0.8
864
+ specifier: ^5.0.9
865
865
  version: 5.0.9
866
866
  zova-module-a-router:
867
- specifier: ^5.1.39
868
- version: 5.1.40
867
+ specifier: ^5.1.41
868
+ version: 5.1.41
869
869
  zova-module-a-routertabs:
870
870
  specifier: ^5.1.34
871
871
  version: 5.1.34
@@ -8890,15 +8890,9 @@ packages:
8890
8890
  resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
8891
8891
  engines: {node: '>=10'}
8892
8892
 
8893
- zova-core@5.1.91:
8894
- resolution: {integrity: sha512-5e4EgInhcU7LUMhl6x7oOQnsF2X3lk1HMRvbo882FZrwTB3lvIz/ejOOdpRxoId29hqxvX5HFiKcb9aZi/cHog==}
8895
-
8896
8893
  zova-core@5.1.92:
8897
8894
  resolution: {integrity: sha512-Y6j3YCMlVCZ5f9JGRX79mFRYW96stGgnhTyMmo0LZCD/P3CqjIlNwyL8edK8e/DBzQ+JYFj65CuBf/n/lIcl7Q==}
8898
8895
 
8899
- zova-jsx@1.1.91:
8900
- resolution: {integrity: sha512-VIScqEFG6jwzNqjW7MvBvZdLJAqpkzchop6Q/g2ttGBKXMGjyTZnaISrB3gWv/feReGnUb0BFPzDgmrOdFnQgA==}
8901
-
8902
8896
  zova-jsx@1.1.92:
8903
8897
  resolution: {integrity: sha512-cbjpwK8qI3ufIKNXlYuWsbljfIbxZ3ISQXKKXn199V7A4IDRz0CNUmffPHU0EX3uZ1mMvqkt9HUJxaVseFb0qQ==}
8904
8898
 
@@ -8950,9 +8944,6 @@ packages:
8950
8944
  zova-module-a-pay@5.0.9:
8951
8945
  resolution: {integrity: sha512-Chl6EKp30HBOGFCJXbla2/lS19WhzQyTZwgYmsL3nnG9Vt2TeUlb82vsYCVuDcy0cgmxVaWjPF7dzKL/r3Pfng==}
8952
8946
 
8953
- zova-module-a-router@5.1.40:
8954
- resolution: {integrity: sha512-X8sOcMqDrNju+J+kjpyRwUkecMdnXIq2MFbl3KLY1lb641sXLEmdJMSJBmVWCrGVBA+kA6nGnI//wtMGXTVIyg==}
8955
-
8956
8947
  zova-module-a-router@5.1.41:
8957
8948
  resolution: {integrity: sha512-oSv4S+9bAx79X8oUBARFKs4n56FNG/bbqdyxP10CXxlVue8WU7CmNKN4ckzLJ2mleVIezijZk/1aHS1Fl5rk+g==}
8958
8949
 
@@ -8980,9 +8971,6 @@ packages:
8980
8971
  zova-module-a-zod@5.1.37:
8981
8972
  resolution: {integrity: sha512-rXHN9rKbum9w3gDSnC/pIusG2d2FGJOcIauf8jrbT96f+Olk4Q3iV3t2V92mxiLCh9kMvBJPfcOgJFW7pRB5Fw==}
8982
8973
 
8983
- zova-module-a-zova@5.1.107:
8984
- resolution: {integrity: sha512-apqfQ2DSDd/2witE+qeVXOC5pxG4cqt0hfiXfJVHcfLeius4h9lpnLFQqFswkyAgCaXrcPk329Nq6FGSij8RKw==}
8985
-
8986
8974
  zova-module-a-zova@5.1.108:
8987
8975
  resolution: {integrity: sha512-K6ZmbuKPScDXOmmNuDxrMtoo3n+YTgbf9BSVJcen++CVuHPXNSdr+kQTpOa+B12O+PxUKCfmwwCSh7T9RrjiEA==}
8988
8976
 
@@ -8992,15 +8980,9 @@ packages:
8992
8980
  zova-module-rest-resource@5.1.55:
8993
8981
  resolution: {integrity: sha512-Kk3oJIcxM5EbeTLl+ucJSeI2kT0We8YilXBaX8Z7QWCbZ0fHoQDy/Ac4YsVSWIXUrC2SQ267+nMl3sHXnE9G0Q==}
8994
8982
 
8995
- zova-suite-a-zova@5.1.164:
8996
- resolution: {integrity: sha512-0o/mBzx6fiPsPIKEltIRW2Hfntza2Hc8q/n7y+BQYXdoNN3WdvDpE6XrbG5Lu/+AXlHn42n49bgEgawNSYb/Xw==}
8997
-
8998
8983
  zova-suite-a-zova@5.1.165:
8999
8984
  resolution: {integrity: sha512-hXjFEx9MXrtvPgAv28VF2UbgM45hzz7B7ZAsElJloBNWB3zyz2HUHYxNWDbCrrKfrw5zM3Mbr9i+dzyy3/6LQw==}
9000
8985
 
9001
- zova@5.1.165:
9002
- resolution: {integrity: sha512-oz67Qkzx+u/xE3hVOkIDEFFr7IwImfWIurmxdSpWA3UuzC+9bVDaDu3CDj/3l0n/0LIYnjmbff66k5gKikYWzg==}
9003
-
9004
8986
  zova@5.1.166:
9005
8987
  resolution: {integrity: sha512-2qh4Na6G8JYHWIyX7+NNfMLzzgHsheQFUgeH6KVsMp66kxpdR9YSr5Ivz+dJepthtmnlCB7R+WWczhURx4wYYA==}
9006
8988
 
@@ -15081,37 +15063,6 @@ snapshots:
15081
15063
 
15082
15064
  yocto-queue@0.1.0: {}
15083
15065
 
15084
- zova-core@5.1.91(typescript@5.9.3):
15085
- dependencies:
15086
- '@cabloy/compose': link:packages-utils/compose
15087
- '@cabloy/extend': link:packages-utils/extend
15088
- '@cabloy/json5': link:packages-utils/json5
15089
- '@cabloy/localeutil': link:packages-utils/localeutil
15090
- '@cabloy/logger': 1.1.25
15091
- '@cabloy/module-info': 2.0.3
15092
- '@cabloy/utils': link:packages-utils/utils
15093
- '@cabloy/vue-compiler-sfc': 3.5.14
15094
- '@cabloy/vue-reactivity': 3.5.16
15095
- '@cabloy/vue-router': 4.4.18(vue@3.5.43(typescript@5.9.3))
15096
- '@cabloy/vue-runtime-core': 3.5.57
15097
- '@cabloy/vue-runtime-dom': 3.5.13
15098
- '@cabloy/vue-server-renderer': 3.5.18(vue@3.5.43(typescript@5.9.3))
15099
- '@cabloy/word-utils': 2.1.14
15100
- '@cabloy/zod-errors-custom': link:packages-utils/zod-errors-custom
15101
- '@cabloy/zod-openapi': link:packages-utils/zod-openapi
15102
- '@cabloy/zod-query': link:packages-utils/zod-query
15103
- '@types/js-cookie': 3.0.6
15104
- '@types/ms': 2.1.0
15105
- '@vue/shared': 3.5.36
15106
- deep-equal: 2.2.3
15107
- ms: 2.1.3
15108
- reflect-metadata: 0.2.2
15109
- vue: 3.5.43(typescript@5.9.3)
15110
- vue-plugin-render-freeze: 2.0.4(vue@3.5.43(typescript@5.9.3))
15111
- zod: '@cabloy/zod@4.3.8'
15112
- transitivePeerDependencies:
15113
- - typescript
15114
-
15115
15066
  zova-core@5.1.92(patch_hash=144450bd7a567a6067e5857c0cf4c780981b7e90bc48745c09e1696a9b6f1b97)(typescript@5.9.3):
15116
15067
  dependencies:
15117
15068
  '@cabloy/compose': link:packages-utils/compose
@@ -15143,17 +15094,6 @@ snapshots:
15143
15094
  transitivePeerDependencies:
15144
15095
  - typescript
15145
15096
 
15146
- zova-jsx@1.1.91(typescript@5.9.3):
15147
- dependencies:
15148
- '@cabloy/compose': link:packages-utils/compose
15149
- '@cabloy/utils': link:packages-utils/utils
15150
- '@cabloy/word-utils': 2.1.14
15151
- typestyle: 2.4.0
15152
- vue: 3.5.43(typescript@5.9.3)
15153
- zova-core: 5.1.91(typescript@5.9.3)
15154
- transitivePeerDependencies:
15155
- - typescript
15156
-
15157
15097
  zova-jsx@1.1.92(typescript@5.9.3):
15158
15098
  dependencies:
15159
15099
  '@cabloy/compose': link:packages-utils/compose
@@ -15226,8 +15166,6 @@ snapshots:
15226
15166
 
15227
15167
  zova-module-a-pay@5.0.9: {}
15228
15168
 
15229
- zova-module-a-router@5.1.40: {}
15230
-
15231
15169
  zova-module-a-router@5.1.41: {}
15232
15170
 
15233
15171
  zova-module-a-routerstack@5.1.29: {}
@@ -15267,22 +15205,6 @@ snapshots:
15267
15205
  '@cabloy/zod-query': link:packages-utils/zod-query
15268
15206
  zod: '@cabloy/zod@4.3.8'
15269
15207
 
15270
- zova-module-a-zova@5.1.107(typescript@5.9.3)(vue@3.5.43(typescript@5.9.3)):
15271
- dependencies:
15272
- '@cabloy/compose': link:packages-utils/compose
15273
- '@cabloy/deps': link:packages-utils/deps
15274
- '@cabloy/json5': link:packages-utils/json5
15275
- '@cabloy/module-info': 2.0.3
15276
- '@cabloy/utils': link:packages-utils/utils
15277
- '@cabloy/vue-router': 4.4.18(vue@3.5.43(typescript@5.9.3))
15278
- '@cabloy/word-utils': 2.1.14
15279
- defu: 6.1.7
15280
- luxon: 3.7.2
15281
- zova-jsx: 1.1.91(typescript@5.9.3)
15282
- transitivePeerDependencies:
15283
- - typescript
15284
- - vue
15285
-
15286
15208
  zova-module-a-zova@5.1.108(typescript@5.9.3)(vue@3.5.43(typescript@5.9.3)):
15287
15209
  dependencies:
15288
15210
  '@cabloy/compose': link:packages-utils/compose
@@ -15303,39 +15225,6 @@ snapshots:
15303
15225
 
15304
15226
  zova-module-rest-resource@5.1.55: {}
15305
15227
 
15306
- zova-suite-a-zova@5.1.164(typescript@5.9.3)(vue@3.5.43(typescript@5.9.3)):
15307
- dependencies:
15308
- zova-module-a-api: 5.1.22
15309
- zova-module-a-app: 5.1.26
15310
- zova-module-a-bean: 5.1.36
15311
- zova-module-a-behavior: 5.1.26
15312
- zova-module-a-behaviors: 5.1.22
15313
- zova-module-a-command: 5.1.35
15314
- zova-module-a-fetch: 5.1.25
15315
- zova-module-a-form: 5.1.51(vue@3.5.43(typescript@5.9.3))
15316
- zova-module-a-icon: 5.1.27
15317
- zova-module-a-interceptor: 5.1.31
15318
- zova-module-a-logger: 5.1.28
15319
- zova-module-a-meta: 5.1.22
15320
- zova-module-a-model: 5.1.37(vue@3.5.43(typescript@5.9.3))
15321
- zova-module-a-openapi: 5.1.54
15322
- zova-module-a-router: 5.1.40
15323
- zova-module-a-routerstack: 5.1.29
15324
- zova-module-a-routertabs: 5.1.34
15325
- zova-module-a-ssr: 5.1.40
15326
- zova-module-a-ssrhmr: 5.1.23
15327
- zova-module-a-ssrserver: 5.1.26
15328
- zova-module-a-style: 5.1.35
15329
- zova-module-a-table: 5.1.43(vue@3.5.43(typescript@5.9.3))
15330
- zova-module-a-zod: 5.1.37
15331
- zova-module-a-zova: 5.1.107(typescript@5.9.3)(vue@3.5.43(typescript@5.9.3))
15332
- transitivePeerDependencies:
15333
- - '@vue/composition-api'
15334
- - debug
15335
- - supports-color
15336
- - typescript
15337
- - vue
15338
-
15339
15228
  zova-suite-a-zova@5.1.165(typescript@5.9.3)(vue@3.5.43(typescript@5.9.3)):
15340
15229
  dependencies:
15341
15230
  zova-module-a-api: 5.1.22
@@ -15369,17 +15258,6 @@ snapshots:
15369
15258
  - typescript
15370
15259
  - vue
15371
15260
 
15372
- zova@5.1.165(typescript@5.9.3)(vue@3.5.43(typescript@5.9.3)):
15373
- dependencies:
15374
- zova-core: 5.1.91(typescript@5.9.3)
15375
- zova-suite-a-zova: 5.1.164(typescript@5.9.3)(vue@3.5.43(typescript@5.9.3))
15376
- transitivePeerDependencies:
15377
- - '@vue/composition-api'
15378
- - debug
15379
- - supports-color
15380
- - typescript
15381
- - vue
15382
-
15383
15261
  zova@5.1.166(typescript@5.9.3)(vue@3.5.43(typescript@5.9.3)):
15384
15262
  dependencies:
15385
15263
  zova-core: 5.1.92(patch_hash=144450bd7a567a6067e5857c0cf4c780981b7e90bc48745c09e1696a9b6f1b97)(typescript@5.9.3)
@@ -601,7 +601,7 @@ export class ServiceAccount extends BeanBase {
601
601
  url.password ||
602
602
  url.search ||
603
603
  url.hash ||
604
- this.bean.security.checkOriginExact(url.origin, this.ctx.host) !== url.origin
604
+ this.bean.security.checkOriginExact(url.origin, this.app.util.host) !== url.origin
605
605
  ) {
606
606
  this.app.throw(503, `account ${purpose} consumer URL is invalid`);
607
607
  }
@@ -248,21 +248,38 @@ describe('accountPasswordReset.test.ts', { concurrency: false }, () => {
248
248
  }
249
249
  });
250
250
 
251
- it('allows an exact same-origin consumer without a whitelist entry', async () => {
251
+ it('uses the canonical host for an exact same-origin consumer without a whitelist entry', async () => {
252
252
  let fixture: IAccountPasswordResetFixture | undefined;
253
253
  let restoreConfig: (() => void) | undefined;
254
+ let serve: typeof app.config.server.serve | undefined;
255
+ let servePrevious: { protocol?: string; host?: string } | undefined;
254
256
  try {
255
257
  fixture = await createFixture({ simpleAuth: true });
256
258
  restoreConfig = await configurePasswordReset([]);
259
+ await app.bean.executor.mockCtx(async () => {
260
+ serve = app.ctx.config.server.serve;
261
+ servePrevious = { ...serve };
262
+ (serve as any).host = 'canonical.example.test';
263
+ });
257
264
 
258
265
  const sameOriginConsumerUrl = await app.bean.executor.mockCtx(async () => {
259
- return `${app.ctx.protocol}://${app.ctx.host}/home/user/password-reset`;
266
+ return `${app.ctx.protocol}://${app.util.host}/home/user/password-reset`;
260
267
  });
261
268
  const issued = await issuePasswordResetLink(fixture, sameOriginConsumerUrl);
262
269
  assert.equal(issued.origin, new URL(sameOriginConsumerUrl).origin);
263
270
  assert.equal(issued.path, '/home/user/password-reset');
264
271
  assert.equal(issued.search, `?token=${issued.token}`);
272
+
273
+ await clearPasswordResetState(fixture.userId);
274
+ await clearPasswordResetRecipientCooldown(fixture.email);
275
+ const requestOriginConsumerUrl = await app.bean.executor.mockCtx(async () => {
276
+ return `${app.ctx.protocol}://${app.ctx.host}/home/user/password-reset`;
277
+ });
278
+ await requestPasswordReset(fixture.email, requestOriginConsumerUrl);
279
+ await assertNoPasswordResetState(fixture.userId);
280
+ await assertNoPasswordResetRecipientCooldown(fixture.email);
265
281
  } finally {
282
+ if (serve && servePrevious) Object.assign(serve, servePrevious);
266
283
  restoreConfig?.();
267
284
  if (fixture) await removeFixture(fixture);
268
285
  }
@@ -706,9 +723,11 @@ async function assertNoPasswordResetRecipientCooldown(email: string) {
706
723
  }
707
724
 
708
725
  async function clearPasswordResetState(userId: string): Promise<void> {
709
- const digest = await app.scope('home-user').cacheRedis.passwordResetCurrent.get(userId as any);
710
- if (digest) await app.scope('home-user').cacheRedis.passwordReset.del(digest);
711
- await app.scope('home-user').cacheRedis.passwordResetCurrent.del(userId as any);
726
+ await app.bean.executor.mockCtx(async () => {
727
+ const digest = await app.scope('home-user').cacheRedis.passwordResetCurrent.get(userId as any);
728
+ if (digest) await app.scope('home-user').cacheRedis.passwordReset.del(digest);
729
+ await app.scope('home-user').cacheRedis.passwordResetCurrent.del(userId as any);
730
+ });
712
731
  }
713
732
 
714
733
  async function assertNoPasswordResetState(userId: string, digest?: string): Promise<void> {
@@ -453,21 +453,36 @@ describe('accountPasswordSet.test.ts', { concurrency: false, sequential: true },
453
453
  }
454
454
  });
455
455
 
456
- it('allows an exact same-origin consumer without a whitelist entry', async () => {
456
+ it('uses the canonical host for an exact same-origin consumer without a whitelist entry', async () => {
457
457
  let fixture: IAccountPasswordSetFixture | undefined;
458
458
  let restoreConfig: (() => void) | undefined;
459
+ let serve: typeof app.config.server.serve | undefined;
460
+ let servePrevious: { protocol?: string; host?: string } | undefined;
459
461
  try {
460
462
  fixture = await createFixture();
461
463
  restoreConfig = await configurePasswordSet([]);
464
+ await app.bean.executor.mockCtx(async () => {
465
+ serve = app.ctx.config.server.serve;
466
+ servePrevious = { ...serve };
467
+ (serve as any).host = 'canonical.example.test';
468
+ });
462
469
 
463
470
  const sameOriginConsumerUrl = await app.bean.executor.mockCtx(async () => {
464
- return `${app.ctx.protocol}://${app.ctx.host}/home/user/password-set`;
471
+ return `${app.ctx.protocol}://${app.util.host}/home/user/password-set`;
465
472
  });
466
473
  const issued = await issuePasswordSetLink(fixture, sameOriginConsumerUrl);
467
474
  assert.equal(issued.origin, new URL(sameOriginConsumerUrl).origin);
468
475
  assert.equal(issued.path, '/home/user/password-set');
469
476
  assert.equal(issued.search, `?token=${issued.token}`);
477
+
478
+ await clearPasswordSetState(fixture.userId);
479
+ const requestOriginConsumerUrl = await app.bean.executor.mockCtx(async () => {
480
+ return `${app.ctx.protocol}://${app.ctx.host}/home/user/password-set`;
481
+ });
482
+ await issuePasswordSetLinkRejected(fixture, requestOriginConsumerUrl);
483
+ await assertNoPasswordSetState(fixture.userId);
470
484
  } finally {
485
+ if (serve && servePrevious) Object.assign(serve, servePrevious);
471
486
  restoreConfig?.();
472
487
  if (fixture) await removeFixture(fixture);
473
488
  }
@@ -784,9 +799,11 @@ async function getCacheTtl(cacheName: 'passwordSet' | 'passwordSetCurrent', key:
784
799
  }
785
800
 
786
801
  async function clearPasswordSetState(userId: string): Promise<void> {
787
- const digest = await app.scope('home-user').cacheRedis.passwordSetCurrent.get(userId);
788
- if (digest) await app.scope('home-user').cacheRedis.passwordSet.del(digest);
789
- await app.scope('home-user').cacheRedis.passwordSetCurrent.del(userId);
802
+ await app.bean.executor.mockCtx(async () => {
803
+ const digest = await app.scope('home-user').cacheRedis.passwordSetCurrent.get(userId);
804
+ if (digest) await app.scope('home-user').cacheRedis.passwordSet.del(digest);
805
+ await app.scope('home-user').cacheRedis.passwordSetCurrent.del(userId);
806
+ });
790
807
  }
791
808
 
792
809
  async function assertNoPasswordSetState(userId: string, digest?: string): Promise<void> {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-module-a-auth",
3
- "version": "5.1.23",
3
+ "version": "5.1.24",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "",
6
6
  "keywords": [