@h-ai/cli 1.0.0-beta.2 → 1.0.0-beta.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +74 -154
- package/README.ja.md +74 -154
- package/README.md +74 -154
- package/dist/{chunk-AKV2CQZV.js → chunk-2ZL5ORQ2.js} +316 -97
- package/dist/chunk-2ZL5ORQ2.js.map +1 -0
- package/dist/{chunk-EF7WOFWZ.js → chunk-L53P43B2.js} +1312 -163
- package/dist/chunk-L53P43B2.js.map +1 -0
- package/dist/cli-config-templates-LWMD2I6V.js +5 -0
- package/dist/{cli-config-templates-HHFHNXBN.js.map → cli-config-templates-LWMD2I6V.js.map} +1 -1
- package/dist/{cli-skill-templates-PN4FLGBC.js → cli-skill-templates-D6S2OQ6X.js} +3 -3
- package/dist/{cli-skill-templates-PN4FLGBC.js.map → cli-skill-templates-D6S2OQ6X.js.map} +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +30118 -3253
- package/dist/index.js.map +1 -1
- package/dist/metafile-esm.json +1 -1
- package/package.json +22 -18
- package/templates/README.md +45 -30
- package/templates/apps/admin/src/routes/+layout.svelte.hbs +16 -10
- package/templates/apps/admin/src/routes/+page.svelte.hbs +4 -4
- package/templates/apps/admin/src/routes/admin/+layout.svelte.hbs +17 -7
- package/templates/apps/admin/src/routes/admin/+page.svelte.hbs +26 -6
- package/templates/apps/admin/src/routes/admin/settings/+page.svelte.hbs +2 -2
- package/templates/apps/api/.dockerignore.hbs +10 -0
- package/templates/apps/api/apps/{{projectName}}-contract/README.md.hbs +8 -0
- package/templates/apps/api/apps/{{projectName}}-contract/src/app-schemas.ts.hbs +6 -1
- package/templates/apps/api/apps/{{projectName}}-contract/src/{{projectName}}-contract.ts.hbs +13 -5
- package/templates/apps/api/apps/{{projectName}}-contract/tests/contract.test.ts.hbs +13 -5
- package/templates/apps/api/apps/{{projectName}}-contract/tsup.config.ts.hbs +3 -1
- package/templates/apps/api/apps/{{projectName}}-serv/README.md.hbs +6 -2
- package/templates/apps/api/apps/{{projectName}}-serv/config/_serv.yml.hbs +24 -2
- package/templates/apps/api/apps/{{projectName}}-serv/package.json.hbs +4 -4
- package/templates/apps/api/apps/{{projectName}}-serv/src/app.ts.hbs +74 -24
- package/templates/apps/api/apps/{{projectName}}-serv/src/build-metadata.ts.hbs +0 -1
- package/templates/apps/api/apps/{{projectName}}-serv/src/lib/server/init.ts.hbs +153 -37
- package/templates/apps/api/apps/{{projectName}}-serv/src/server/procedures/index.ts.hbs +39 -13
- package/templates/apps/api/apps/{{projectName}}-serv/tests/service.test.ts.hbs +106 -33
- package/templates/apps/api/apps/{{projectName}}-serv/tsup.config.ts.hbs +3 -1
- package/templates/apps/api/apps/{{projectName}}-serv/vitest.config.ts.hbs +3 -1
- package/templates/apps/api/e2e/api-serv.spec.ts.hbs +20 -3
- package/templates/apps/api/e2e/capture-evidence.ts.hbs +4 -1
- package/templates/apps/api/eslint.config.js.hbs +3 -1
- package/templates/apps/api/images/serv.Dockerfile.hbs +15 -10
- package/templates/apps/api/playwright.config.ts.hbs +23 -19
- package/templates/apps/api/pnpm-workspace.yaml.hbs +27 -16
- package/templates/apps/fullstack/.dockerignore.hbs +10 -0
- package/templates/apps/fullstack/.gitignore.hbs +7 -2
- package/templates/apps/fullstack/apps/{{projectName}}-app/README.md.hbs +14 -12
- package/templates/apps/fullstack/apps/{{projectName}}-app/capacitor.config.ts.hbs +5 -3
- package/templates/apps/fullstack/apps/{{projectName}}-app/eslint.config.js.hbs +10 -2
- package/templates/apps/fullstack/apps/{{projectName}}-app/package.json.hbs +1 -1
- package/templates/apps/fullstack/apps/{{projectName}}-app/src/App.svelte.hbs +10 -2
- package/templates/apps/fullstack/apps/{{projectName}}-app/src/build-metadata.ts.hbs +0 -1
- package/templates/apps/fullstack/apps/{{projectName}}-app/src/lib/capacitor.ts.hbs +0 -1
- package/templates/apps/fullstack/apps/{{projectName}}-app/svelte.config.js.hbs +4 -1
- package/templates/apps/fullstack/apps/{{projectName}}-app/vite.config.ts.hbs +16 -4
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/README.md.hbs +10 -8
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/package.json.hbs +3 -3
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/App.svelte.hbs +8 -2
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/build-metadata.ts.hbs +0 -1
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/tauri.conf.json.hbs +0 -2
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/svelte.config.js.hbs +4 -1
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/vite.config.ts.hbs +16 -4
- package/templates/apps/fullstack/apps/{{projectName}}-miniapp/README.md.hbs +2 -0
- package/templates/apps/fullstack/apps/{{projectName}}-web/README.md.hbs +9 -9
- package/templates/apps/fullstack/apps/{{projectName}}-web/eslint.config.js.hbs +10 -2
- package/templates/apps/fullstack/apps/{{projectName}}-web/index.html.hbs +1 -0
- package/templates/apps/fullstack/apps/{{projectName}}-web/src/build-metadata.ts.hbs +0 -1
- package/templates/apps/fullstack/apps/{{projectName}}-web/src/main.ts.hbs +0 -2
- package/templates/apps/fullstack/apps/{{projectName}}-web/svelte.config.js.hbs +4 -1
- package/templates/apps/fullstack/apps/{{projectName}}-web/vite.config.ts.hbs +19 -5
- package/templates/apps/fullstack/docs/security-configuration.md.hbs +4 -1
- package/templates/apps/fullstack/e2e/fullstack.spec.ts.hbs +140 -28
- package/templates/apps/fullstack/e2e/session-close.spec.ts.hbs +19 -5
- package/templates/apps/fullstack/eslint.config.js.hbs +3 -1
- package/templates/apps/fullstack/images/serv.Dockerfile.hbs +19 -11
- package/templates/apps/fullstack/images/web.Dockerfile.hbs +17 -8
- package/templates/apps/fullstack/package.json.hbs +2 -1
- package/templates/apps/fullstack/packages/{{projectName}}-contract/README.md.hbs +7 -5
- package/templates/apps/fullstack/packages/{{projectName}}-contract/src/admin-contract.ts.hbs +114 -23
- package/templates/apps/fullstack/packages/{{projectName}}-contract/src/admin-schemas.ts.hbs +57 -11
- package/templates/apps/fullstack/packages/{{projectName}}-contract/tsup.config.ts.hbs +3 -1
- package/templates/apps/fullstack/packages/{{projectName}}-serv/README.md.hbs +17 -7
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_reach.yml.hbs +13 -7
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_serv.yml.hbs +17 -7
- package/templates/apps/fullstack/packages/{{projectName}}-serv/package.json.hbs +10 -4
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/app-server.ts.hbs +65 -30
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/build-metadata.ts.hbs +0 -1
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/index.ts.hbs +1 -2
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/admin-service.ts.hbs +69 -21
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/business-settings.ts.hbs +21 -5
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/admin-procedures.ts.hbs +63 -11
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/app-procedures.ts.hbs +6 -1
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/index.ts.hbs +3 -1
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/runtime.ts.hbs +147 -33
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/security-roles.ts.hbs +43 -12
- package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/app-server.test.ts.hbs +146 -30
- package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/runtime.test.ts.hbs +33 -10
- package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/security.test.ts.hbs +142 -26
- package/templates/apps/fullstack/packages/{{projectName}}-serv/tsup.config.ts.hbs +6 -2
- package/templates/apps/fullstack/packages/{{projectName}}-serv/vitest.config.ts.hbs +3 -1
- package/templates/apps/fullstack/packages/{{projectName}}-shared/README.md.hbs +48 -66
- package/templates/apps/fullstack/packages/{{projectName}}-shared/messages/en-US.json.hbs +1 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/messages/ja-JP.json.hbs +1 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/messages/zh-CN.json.hbs +1 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/api/api-client.ts.hbs +9 -2
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/api/feedback-client.ts.hbs +0 -3
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/api/sensitive-actions.svelte.ts.hbs +15 -3
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/AppShell.svelte.hbs +33 -7
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/FeedbackEntry.svelte.hbs +16 -2
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/audit.svelte.hbs +104 -13
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/feedback.svelte.hbs +37 -7
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/roles.svelte.hbs +9 -3
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/settings.svelte.hbs +25 -3
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/users.svelte.hbs +133 -19
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/security/role-label.ts.hbs +14 -2
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/stores/guide-store.ts.hbs +66 -34
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/stores/management-store.svelte.ts.hbs +132 -30
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/stores/theme-store.svelte.ts.hbs +8 -2
- package/templates/apps/fullstack/packages/{{projectName}}-shared/tests/api-client.test.ts.hbs +23 -5
- package/templates/apps/fullstack/packages/{{projectName}}-shared/tests/management-store.test.ts.hbs +143 -21
- package/templates/apps/fullstack/packages/{{projectName}}-shared/vitest.config.ts.hbs +7 -2
- package/templates/apps/fullstack/playwright.config.ts.hbs +25 -3
- package/templates/apps/fullstack/pnpm-workspace.yaml.hbs +30 -14
- package/templates/apps/h5/src/routes/+layout.svelte.hbs +74 -30
- package/templates/apps/h5/src/routes/+page.svelte.hbs +2 -2
- package/templates/apps/h5/src/routes/discover/+page.svelte.hbs +2 -2
- package/templates/apps/h5/src/routes/profile/+page.svelte.hbs +2 -2
- package/templates/apps/mobile-app/capacitor.config.ts.hbs +5 -2
- package/templates/apps/mobile-app/messages/en-US.json +2 -2
- package/templates/apps/mobile-app/messages/ja-JP.json +2 -2
- package/templates/apps/mobile-app/messages/zh-CN.json +2 -2
- package/templates/apps/mobile-app/src/App.svelte.hbs +17 -5
- package/templates/apps/mobile-app/src/lib/api.ts.hbs +20 -6
- package/templates/apps/mobile-app/src/lib/app-config.ts.hbs +8 -1
- package/templates/apps/mobile-app/src/lib/pages/home.svelte.hbs +6 -2
- package/templates/apps/mobile-app/src/lib/pages/profile.svelte.hbs +13 -1
- package/templates/apps/mobile-app/src/lib/pages/tasks.svelte.hbs +5 -1
- package/templates/apps/website/scripts/static-website.mjs.hbs +19 -6
- package/templates/apps/website/src/routes/+layout.svelte.hbs +56 -29
- package/templates/apps/website/src/routes/+page.svelte.hbs +4 -4
- package/templates/apps/website/src/routes/about/+page.svelte.hbs +2 -2
- package/templates/apps/website/src/routes/contact/+page.svelte.hbs +2 -2
- package/templates/apps/website/src/routes/services/+page.svelte.hbs +20 -5
- package/templates/base/.dockerignore.hbs +10 -0
- package/templates/base/.gitignore.hbs +7 -2
- package/templates/base/e2e/app.spec.ts.hbs +130 -36
- package/templates/base/eslint-rules.js +44 -25
- package/templates/base/eslint.config.js.hbs +3 -1
- package/templates/base/images/web.Dockerfile.hbs +25 -14
- package/templates/base/package.json.hbs +13 -7
- package/templates/base/patches/daisyui-5.7.50.patch +8 -0
- package/templates/base/patches/deepmerge-4.3.1.patch +51 -0
- package/templates/base/playwright.config.ts +3 -1
- package/templates/base/pnpm-workspace.yaml.hbs +33 -16
- package/templates/base/src/app.d.ts.hbs +1 -1
- package/templates/base/src/app.html.hbs +1 -0
- package/templates/base/src/hooks.server.ts.hbs +44 -33
- package/templates/base/src/lib/build-metadata.ts.hbs +0 -1
- package/templates/base/src/lib/server/init.ts.hbs +146 -35
- package/templates/base/src/lib/server/kit-config.ts.hbs +7 -4
- package/templates/base/src/routes/+layout.server.ts.hbs +2 -2
- package/templates/base/svelte.config.js.hbs +10 -100
- package/templates/base/tests/scaffold.test.ts.hbs +12 -3
- package/templates/base/tsconfig.json.hbs +7 -4
- package/templates/base/vite.config.ts.hbs +108 -14
- package/templates/base/vitest.config.ts.hbs +6 -2
- package/templates/features/ai/routes/api/chat/+server.ts +3 -1
- package/templates/features/iam/routes-admin/(auth)/auth/forgot-password/+page.svelte.hbs +3 -3
- package/templates/features/iam/routes-admin/(auth)/auth/login/+page.svelte.hbs +3 -3
- package/templates/features/iam/routes-admin/(auth)/auth/register/+page.svelte.hbs +3 -3
- package/templates/features/iam/routes-admin/(auth)/auth/reset-password/+page.svelte.hbs +3 -3
- package/templates/features/iam/routes-h5/auth/forgot-password/+page.svelte.hbs +3 -3
- package/templates/features/iam/routes-h5/auth/login/+page.svelte.hbs +3 -3
- package/templates/features/iam/routes-h5/auth/register/+page.svelte.hbs +3 -3
- package/templates/features/iam/routes-h5/auth/reset-password/+page.svelte.hbs +3 -3
- package/templates/features/iam/routes-website/auth/forgot-password/+page.svelte.hbs +3 -3
- package/templates/features/iam/routes-website/auth/login/+page.svelte.hbs +3 -3
- package/templates/features/iam/routes-website/auth/register/+page.svelte.hbs +3 -3
- package/templates/features/iam/routes-website/auth/reset-password/+page.svelte.hbs +3 -3
- package/templates/features/payment/routes/api/v1/payment/create/+server.ts +14 -3
- package/templates/shared/browser-check/startup.js +115 -0
- package/templates/shared/browser-check/update.css +37 -0
- package/templates/shared/browser-check/update.html +19 -0
- package/templates/shared/browser-startup.spec.ts +23 -0
- package/templates/shared/build-metadata.ts +18 -4
- package/templates/shared/e2e-env.ts +22 -6
- package/templates/shared/native-name.d.mts +5 -0
- package/templates/shared/native-name.mjs +94 -0
- package/templates/shared/public-base-path.mjs +17 -4
- package/templates/shared/run-e2e.mjs +46 -12
- package/templates/shared/run-node.mjs +56 -0
- package/templates/shared/run-tauri.mjs +74 -0
- package/templates/shared/vite-runtime-config.ts +9 -3
- package/templates/skills/AGENTS.md +10 -73
- package/templates/skills/bridges/admin/AGENTS.md +9 -65
- package/templates/skills/bridges/api/AGENTS.md +8 -59
- package/templates/skills/bridges/fullstack/AGENTS.md.hbs +9 -54
- package/templates/skills/bridges/h5/AGENTS.md +8 -63
- package/templates/skills/bridges/mobile-app/AGENTS.md +8 -62
- package/templates/skills/bridges/website/AGENTS.md +8 -63
- package/templates/skills/hai-ai/SKILL.md +6 -2
- package/templates/skills/hai-ai/acp.md +153 -0
- package/templates/skills/hai-ai/guide.md +27 -12
- package/templates/skills/hai-ai/reference.md +24 -15
- package/templates/skills/hai-ai/resources.md +69 -0
- package/templates/skills/hai-api-client/SKILL.md +1 -1
- package/templates/skills/hai-api-client/reference.md +3 -1
- package/templates/skills/hai-app-create/collaboration.md +7 -0
- package/templates/skills/hai-app-create/development.md +19 -10
- package/templates/skills/hai-app-create/project.md +33 -0
- package/templates/skills/hai-app-create/reference.md +10 -10
- package/templates/skills/hai-app-tests/reference.md +1 -1
- package/templates/skills/hai-app-tests/validation.md +1 -0
- package/templates/skills/hai-build/SKILL.md +19 -9
- package/templates/skills/hai-build/paths.md +4 -2
- package/templates/skills/hai-build/reference.md +26 -8
- package/templates/skills/hai-cache/reference.md +6 -2
- package/templates/skills/hai-capacitor/SKILL.md +1 -0
- package/templates/skills/hai-capacitor/reference.md +24 -0
- package/templates/skills/hai-ci/SKILL.md +3 -0
- package/templates/skills/hai-core/SKILL.md +9 -4
- package/templates/skills/hai-core/reference.md +40 -10
- package/templates/skills/hai-core/request-access.md +2 -2
- package/templates/skills/hai-crypto/SKILL.md +2 -2
- package/templates/skills/hai-crypto/reference.md +7 -3
- package/templates/skills/hai-deploy/SKILL.md +41 -5
- package/templates/skills/hai-deploy/middleware.md +26 -14
- package/templates/skills/hai-framework-sync/SKILL.md +13 -9
- package/templates/skills/hai-framework-sync/sync.md +2 -0
- package/templates/skills/hai-iam/reference.md +2 -2
- package/templates/skills/hai-kit/SKILL.md +1 -1
- package/templates/skills/hai-kit/reference.md +19 -5
- package/templates/skills/hai-payment/reference.md +41 -17
- package/templates/skills/hai-reach/SKILL.md +3 -1
- package/templates/skills/hai-reach/reference.md +29 -9
- package/templates/skills/hai-reldb/SKILL.md +1 -1
- package/templates/skills/hai-reldb/reference.md +9 -2
- package/templates/skills/hai-scheduler/reference.md +1 -1
- package/templates/skills/hai-serv/SKILL.md +2 -1
- package/templates/skills/hai-serv/reference.md +26 -15
- package/templates/skills/hai-storage/reference.md +23 -6
- package/templates/skills/hai-ui/SKILL.md +11 -6
- package/templates/skills/hai-ui/charts.md +51 -0
- package/templates/skills/hai-ui/guide.md +1 -1
- package/templates/skills/hai-ui/reference.md +58 -38
- package/templates/skills/hai-vecdb/reference.md +8 -3
- package/dist/chunk-AKV2CQZV.js.map +0 -1
- package/dist/chunk-EF7WOFWZ.js.map +0 -1
- package/dist/chunk-MUHRBJSW.js +0 -29272
- package/dist/chunk-MUHRBJSW.js.map +0 -1
- package/dist/cli-config-templates-HHFHNXBN.js +0 -5
- package/dist/src-TEBHR5I5.js +0 -5
- package/dist/src-TEBHR5I5.js.map +0 -1
- package/templates/apps/fullstack/playwright.session-close.config.ts.hbs +0 -18
|
@@ -4,19 +4,9 @@
|
|
|
4
4
|
* 官网根布局
|
|
5
5
|
*/
|
|
6
6
|
import type { Snippet } from 'svelte'
|
|
7
|
-
{
|
|
8
|
-
import {
|
|
9
|
-
{{else}}
|
|
10
|
-
{{#if features.crypto}}
|
|
11
|
-
import { browser } from '$app/environment'
|
|
12
|
-
{{/if}}
|
|
13
|
-
{{/if}}
|
|
14
|
-
import { {{#if features.iam}}base, {{else}}{{#if features.crypto}}base, {{/if}}{{/if}}resolve } from '$app/paths'
|
|
7
|
+
import { browser } from '$app/env'
|
|
8
|
+
import { resolve } from '$app/paths'
|
|
15
9
|
import { page } from '$app/state'
|
|
16
|
-
{{#if hasI18n}}
|
|
17
|
-
import * as m from '$lib/paraglide/messages'
|
|
18
|
-
import { getLocale, setLocale } from '$lib/paraglide/runtime.js'
|
|
19
|
-
{{/if}}
|
|
20
10
|
{{#if features.crypto}}
|
|
21
11
|
import { crypto } from '@h-ai/crypto'
|
|
22
12
|
import { kit } from '@h-ai/kit'
|
|
@@ -26,12 +16,17 @@
|
|
|
26
16
|
{{/if}}
|
|
27
17
|
{{/if}}
|
|
28
18
|
{{#if hasI18n}}
|
|
29
|
-
import { LanguageSwitch, setGlobalLocale, ToastContainer } from '@h-ai/ui'
|
|
19
|
+
import { LanguageSwitch, setGlobalLocale, ThemeSelector, ToastContainer } from '@h-ai/ui'
|
|
30
20
|
{{else}}
|
|
31
|
-
import { ToastContainer } from '@h-ai/ui'
|
|
21
|
+
import { ThemeSelector, ToastContainer } from '@h-ai/ui'
|
|
32
22
|
{{/if}}
|
|
23
|
+
import { applyTheme, getSavedTheme } from '@h-ai/ui/theme'
|
|
33
24
|
{{#if features.crypto}}
|
|
34
25
|
import { untrack } from 'svelte'
|
|
26
|
+
{{/if}}
|
|
27
|
+
{{#if hasI18n}}
|
|
28
|
+
import * as m from '#lib/paraglide/messages.js'
|
|
29
|
+
import { getLocale, setLocale } from '#lib/paraglide/runtime.js'
|
|
35
30
|
{{/if}}
|
|
36
31
|
import '../app.css'
|
|
37
32
|
|
|
@@ -40,26 +35,53 @@
|
|
|
40
35
|
}
|
|
41
36
|
|
|
42
37
|
const { children }: Props = $props()
|
|
43
|
-
|
|
38
|
+
const initialTheme = browser
|
|
39
|
+
? getSavedTheme()
|
|
40
|
+
: 'light'
|
|
41
|
+
let currentTheme = $state(initialTheme)
|
|
44
42
|
if (browser)
|
|
45
|
-
|
|
43
|
+
applyTheme(initialTheme, false)
|
|
44
|
+
|
|
45
|
+
function handleThemeChange(theme: string) {
|
|
46
|
+
currentTheme = theme
|
|
47
|
+
applyTheme(theme)
|
|
48
|
+
}
|
|
49
|
+
{{#if features.crypto}}
|
|
50
|
+
if (browser) {
|
|
51
|
+
kit.client.installBrowserTransport(untrack(() => page.data.kitConfig), {
|
|
52
|
+
crypto,
|
|
53
|
+
basePath: resolve('/').replace(/\/$/, ''),
|
|
54
|
+
})
|
|
55
|
+
}
|
|
46
56
|
{{/if}}
|
|
47
57
|
{{#if features.iam}}
|
|
48
|
-
const authActions = kit.client.createAuthActions({ basePath: `${
|
|
58
|
+
const authActions = kit.client.createAuthActions({ basePath: `${resolve('/').replace(/\/$/, '')}/api/auth` })
|
|
49
59
|
$effect(() => authActions.bindPageExit({ enabled: page.data.iamPublicConfig?.session.endOnPageHide }))
|
|
50
60
|
{{/if}}
|
|
51
61
|
|
|
52
62
|
const navItems = [
|
|
53
63
|
{{#if hasI18n}}
|
|
54
|
-
{
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
64
|
+
{
|
|
65
|
+
path: resolve('/'),
|
|
66
|
+
label: m.nav_home(),
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
path: resolve('about'),
|
|
70
|
+
label: m.nav_about(),
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
path: resolve('services'),
|
|
74
|
+
label: m.nav_services(),
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
path: resolve('contact'),
|
|
78
|
+
label: m.nav_contact(),
|
|
79
|
+
},
|
|
58
80
|
{{else}}
|
|
59
|
-
{ path: '/', label: '首页' },
|
|
60
|
-
{ path: '
|
|
61
|
-
{ path: '
|
|
62
|
-
{ path: '
|
|
81
|
+
{ path: resolve('/'), label: '首页' },
|
|
82
|
+
{ path: resolve('about'), label: '关于' },
|
|
83
|
+
{ path: resolve('services'), label: '服务' },
|
|
84
|
+
{ path: resolve('contact'), label: '联系我们' },
|
|
63
85
|
{{/if}}
|
|
64
86
|
] as const
|
|
65
87
|
{{#if hasI18n}}
|
|
@@ -84,21 +106,24 @@
|
|
|
84
106
|
<header class='navbar flex-wrap bg-base-100 shadow-sm'>
|
|
85
107
|
<div class='flex-1'>
|
|
86
108
|
{{#if hasI18n}}
|
|
87
|
-
<a href={resolve('/'
|
|
109
|
+
<a href={resolve('/')} class='btn btn-ghost text-xl font-black tracking-tight'>{page.data.appConfig.name}</a>
|
|
88
110
|
{{else}}
|
|
89
|
-
<a href={resolve('/'
|
|
111
|
+
<a href={resolve('/')} class='btn btn-ghost text-xl font-black tracking-tight'>{page.data.appConfig.name}</a>
|
|
90
112
|
{{/if}}
|
|
91
113
|
</div>
|
|
92
114
|
<nav class='w-full flex-none sm:w-auto'>
|
|
93
115
|
<ul class='menu menu-horizontal flex-wrap px-1'>
|
|
94
116
|
{#each navItems as item (item.path)}
|
|
95
|
-
<li
|
|
117
|
+
<li><!-- eslint-disable svelte/no-navigation-without-resolve -- 菜单字段已由 resolve 生成完整挂载地址;规则无法追踪派生/筛选后的集合,不能二次添加前缀。 -->
|
|
118
|
+
<a href={item.path}>{item.label}</a>
|
|
119
|
+
<!-- eslint-enable svelte/no-navigation-without-resolve --></li>
|
|
96
120
|
{/each}
|
|
97
121
|
</ul>
|
|
98
122
|
</nav>
|
|
99
123
|
{{#if hasI18n}}
|
|
100
124
|
<LanguageSwitch currentLanguage={getLocale()} onchange={handleLanguageChange} />
|
|
101
125
|
{{/if}}
|
|
126
|
+
<ThemeSelector {currentTheme} onchange={handleThemeChange} compact />
|
|
102
127
|
</header>
|
|
103
128
|
|
|
104
129
|
<main class='flex-1'>
|
|
@@ -108,7 +133,9 @@
|
|
|
108
133
|
<footer class='footer footer-center p-4 bg-base-300 text-base-content'>
|
|
109
134
|
<span>{page.data.appConfig.name} · {page.data.appConfig.version} · {page.data.appConfig.env}</span>
|
|
110
135
|
{{#if hasI18n}}
|
|
111
|
-
<p>{m.footer_copyright({
|
|
136
|
+
<p>{m.footer_copyright({
|
|
137
|
+
year: new Date().getFullYear(),
|
|
138
|
+
})}</p>
|
|
112
139
|
{{else}}
|
|
113
140
|
<p>Copyright © 2025 - All rights reserved</p>
|
|
114
141
|
{{/if}}
|
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { resolve } from '$app/paths'
|
|
7
7
|
import { page } from '$app/state'
|
|
8
|
+
import { Badge } from '@h-ai/ui'
|
|
8
9
|
{{#if hasI18n}}
|
|
9
|
-
import * as m from '
|
|
10
|
+
import * as m from '#lib/paraglide/messages.js'
|
|
10
11
|
{{/if}}
|
|
11
|
-
import { Badge } from '@h-ai/ui'
|
|
12
12
|
</script>
|
|
13
13
|
|
|
14
14
|
<svelte:head>
|
|
@@ -35,14 +35,14 @@
|
|
|
35
35
|
{{#if hasI18n}}
|
|
36
36
|
<h1 class='mt-5 text-5xl font-black tracking-tight'>{m.page_home_title()}</h1>
|
|
37
37
|
<p class='py-6 text-base-content/60'>{m.page_home_subtitle()}</p>
|
|
38
|
-
<a href={resolve('
|
|
38
|
+
<a href={resolve('about')} class='btn btn-primary'>
|
|
39
39
|
<span class='icon-[tabler--arrow-right] size-5'></span>
|
|
40
40
|
{m.page_home_cta()}
|
|
41
41
|
</a>
|
|
42
42
|
{{else}}
|
|
43
43
|
<h1 class='mt-5 text-5xl font-black tracking-tight'>欢迎</h1>
|
|
44
44
|
<p class='py-6 text-base-content/60'>基于 hai Agent Framework 构建</p>
|
|
45
|
-
<a href={resolve('
|
|
45
|
+
<a href={resolve('about')} class='btn btn-primary'>
|
|
46
46
|
<span class='icon-[tabler--arrow-right] size-5'></span>
|
|
47
47
|
了解更多
|
|
48
48
|
</a>
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* 关于页面
|
|
5
5
|
*/
|
|
6
|
+
import { Card, PageHeader } from '@h-ai/ui'
|
|
6
7
|
{{#if hasI18n}}
|
|
7
|
-
import * as m from '
|
|
8
|
+
import * as m from '#lib/paraglide/messages.js'
|
|
8
9
|
{{/if}}
|
|
9
|
-
import { Card, PageHeader } from '@h-ai/ui'
|
|
10
10
|
</script>
|
|
11
11
|
|
|
12
12
|
<svelte:head>
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* 联系我们页面
|
|
5
5
|
*/
|
|
6
|
+
import { Button, Card, FormField, FormLayout, Input, notifyWarning, PageHeader, Textarea } from '@h-ai/ui'
|
|
6
7
|
{{#if hasI18n}}
|
|
7
|
-
import * as m from '
|
|
8
|
+
import * as m from '#lib/paraglide/messages.js'
|
|
8
9
|
{{/if}}
|
|
9
|
-
import { Button, Card, FormField, FormLayout, Input, notifyWarning, PageHeader, Textarea } from '@h-ai/ui'
|
|
10
10
|
|
|
11
11
|
let name = $state('')
|
|
12
12
|
let email = $state('')
|
|
@@ -3,16 +3,31 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* 服务页面
|
|
5
5
|
*/
|
|
6
|
+
import { Card, PageHeader } from '@h-ai/ui'
|
|
6
7
|
{{#if hasI18n}}
|
|
7
|
-
import * as m from '
|
|
8
|
+
import * as m from '#lib/paraglide/messages.js'
|
|
8
9
|
{{/if}}
|
|
9
|
-
import { Card, PageHeader } from '@h-ai/ui'
|
|
10
10
|
|
|
11
11
|
{{#if hasI18n}}
|
|
12
12
|
const services = $derived([
|
|
13
|
-
{
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
{
|
|
14
|
+
id: 'consulting',
|
|
15
|
+
title: m.page_services_item1(),
|
|
16
|
+
desc: m.page_services_desc(),
|
|
17
|
+
icon: 'icon-[tabler--message-circle]',
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
id: 'development',
|
|
21
|
+
title: m.page_services_item2(),
|
|
22
|
+
desc: m.page_services_desc(),
|
|
23
|
+
icon: 'icon-[tabler--code]',
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
id: 'operations',
|
|
27
|
+
title: m.page_services_item3(),
|
|
28
|
+
desc: m.page_services_desc(),
|
|
29
|
+
icon: 'icon-[tabler--server-cog]',
|
|
30
|
+
},
|
|
16
31
|
])
|
|
17
32
|
{{else}}
|
|
18
33
|
const services = [
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
{{#if_eq appType 'h5'}}
|
|
2
|
+
import type { Page } from '@playwright/test'
|
|
3
|
+
{{/if_eq}}
|
|
1
4
|
{{#if features.iam}}
|
|
2
5
|
import { randomUUID } from 'node:crypto'
|
|
3
6
|
{{/if}}
|
|
@@ -6,17 +9,40 @@ import path from 'node:path'
|
|
|
6
9
|
import process from 'node:process'
|
|
7
10
|
import { expect, test } from '@playwright/test'
|
|
8
11
|
|
|
12
|
+
{{#if_eq appType 'h5'}}
|
|
13
|
+
/** 延迟客户端脚本,覆盖 SSR 已显示而事件尚未绑定的阶段。 */
|
|
14
|
+
async function whileClientBootPaused(page: Page, inspect: () => Promise<void>) {
|
|
15
|
+
let resumeClient = () => { }
|
|
16
|
+
const clientBoot = new Promise<void>((resolve) => {
|
|
17
|
+
resumeClient = resolve
|
|
18
|
+
})
|
|
19
|
+
await page.route('**/*.js', async (route) => {
|
|
20
|
+
if (!new URL(route.request().url()).pathname.endsWith('/_hai/browser-check/startup.js'))
|
|
21
|
+
await clientBoot
|
|
22
|
+
await route.continue()
|
|
23
|
+
})
|
|
24
|
+
try {
|
|
25
|
+
await inspect()
|
|
26
|
+
}
|
|
27
|
+
finally {
|
|
28
|
+
resumeClient()
|
|
29
|
+
await page.unrouteAll({ behavior: 'wait' })
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
{{/if_eq}}
|
|
9
33
|
{{#if_eq appType 'admin'}}
|
|
10
34
|
test('high contrast appearance is selectable and survives reload', async ({ page }) => {
|
|
11
35
|
await page.goto('./')
|
|
12
36
|
const header = page.locator('header').first()
|
|
13
|
-
|
|
37
|
+
const themeToggle = header.getByRole('button', { name: /选择主题|Select theme|テーマを選択/, exact: true })
|
|
38
|
+
await themeToggle.click()
|
|
14
39
|
await header.getByRole('button', { name: /高对比|High contrast|ハイコントラスト/, exact: true }).click()
|
|
15
40
|
await expect(page.locator('html')).toHaveAttribute('data-theme', 'high-contrast')
|
|
16
41
|
await page.reload()
|
|
17
42
|
await expect(page.locator('html')).toHaveAttribute('data-theme', 'high-contrast')
|
|
18
43
|
await expect.poll(() => page.evaluate(() => getComputedStyle(document.documentElement).colorScheme)).toBe('dark')
|
|
19
|
-
await
|
|
44
|
+
await expect(themeToggle).toBeEnabled()
|
|
45
|
+
await themeToggle.press('Enter')
|
|
20
46
|
await header.getByRole('button', { name: /简洁商务|Formal|ビジネス(角丸なし)/, exact: true }).press('Enter')
|
|
21
47
|
await expect(page.locator('html')).toHaveAttribute('data-theme', 'formal')
|
|
22
48
|
await page.reload()
|
|
@@ -24,12 +50,49 @@ test('high contrast appearance is selectable and survives reload', async ({ page
|
|
|
24
50
|
expect(await header.locator('button').first().evaluate(element => getComputedStyle(element).borderTopLeftRadius)).toBe('0px')
|
|
25
51
|
})
|
|
26
52
|
{{/if_eq}}
|
|
27
|
-
|
|
53
|
+
{{#if hasUi}}
|
|
54
|
+
{{#unless isCapacitorApp}}
|
|
55
|
+
test('eye care appearance is selectable and survives reload', async ({ page }, testInfo) => {
|
|
56
|
+
const header = page.locator('header').first()
|
|
57
|
+
const themeToggle = header.getByRole('button', { name: /选择主题|Select theme|テーマを選択/, exact: true })
|
|
58
|
+
{{#if_eq appType 'h5'}}
|
|
59
|
+
await whileClientBootPaused(page, async () => {
|
|
60
|
+
await page.goto('./', { waitUntil: 'commit' })
|
|
61
|
+
await expect(themeToggle).toBeDisabled()
|
|
62
|
+
})
|
|
63
|
+
{{else}}
|
|
64
|
+
await page.goto('./')
|
|
65
|
+
{{/if_eq}}
|
|
66
|
+
await page.setViewportSize({ width: 1440, height: 960 })
|
|
67
|
+
await themeToggle.click()
|
|
68
|
+
await header.getByRole('button', { name: /护眼|Eye care|目にやさしい/, exact: true }).click()
|
|
69
|
+
await expect(page.locator('html')).toHaveAttribute('data-theme', 'eye-care')
|
|
70
|
+
await page.reload()
|
|
71
|
+
await expect(page.locator('html')).toHaveAttribute('data-theme', 'eye-care')
|
|
72
|
+
await expect.poll(() => page.evaluate(() => getComputedStyle(document.documentElement).colorScheme)).toBe('light')
|
|
73
|
+
await expect.poll(() => page.evaluate(() => getComputedStyle(document.documentElement).getPropertyValue('--color-base-100').trim())).toBe('#e8f0df')
|
|
74
|
+
await page.screenshot({
|
|
75
|
+
path: testInfo.outputPath('eye-care-desktop.png'),
|
|
76
|
+
animations: 'disabled',
|
|
77
|
+
})
|
|
78
|
+
await page.setViewportSize({ width: 390, height: 844 })
|
|
79
|
+
expect(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth)).toBe(true)
|
|
80
|
+
await page.screenshot({
|
|
81
|
+
path: testInfo.outputPath('eye-care-mobile.png'),
|
|
82
|
+
animations: 'disabled',
|
|
83
|
+
})
|
|
84
|
+
})
|
|
85
|
+
{{/unless}}
|
|
86
|
+
{{/if}}
|
|
28
87
|
{{#if_eq appType 'h5'}}
|
|
29
|
-
test.use({
|
|
88
|
+
test.use({
|
|
89
|
+
viewport: { width: 390, height: 844 },
|
|
90
|
+
})
|
|
30
91
|
{{/if_eq}}
|
|
31
92
|
{{#if isCapacitorApp}}
|
|
32
|
-
test.use({
|
|
93
|
+
test.use({
|
|
94
|
+
viewport: { width: 390, height: 844 },
|
|
95
|
+
})
|
|
33
96
|
{{/if}}
|
|
34
97
|
|
|
35
98
|
/** 首页从真实浏览器页面发起,覆盖基础渲染链路。 */
|
|
@@ -60,10 +123,14 @@ test('home page renders from browser', async ({ page }) => {
|
|
|
60
123
|
const nav = page.locator('nav')
|
|
61
124
|
await nav.getByRole('button', { name: /发现|Discover/i, exact: true }).click()
|
|
62
125
|
await expect(page).toHaveURL(/\/discover$/)
|
|
63
|
-
|
|
64
|
-
await
|
|
65
|
-
|
|
66
|
-
|
|
126
|
+
const home = nav.getByRole('button', { name: /首页|Home/i, exact: true })
|
|
127
|
+
await whileClientBootPaused(page, async () => {
|
|
128
|
+
await page.reload({ waitUntil: 'commit' })
|
|
129
|
+
await expect(page).toHaveURL(/\/discover$/)
|
|
130
|
+
await expect(page.getByRole('heading', { name: /发现|Discover/i })).toBeVisible()
|
|
131
|
+
await expect(home).toBeDisabled()
|
|
132
|
+
})
|
|
133
|
+
await home.click()
|
|
67
134
|
await expect(page).toHaveURL(/\/$/)
|
|
68
135
|
expect(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth)).toBe(true)
|
|
69
136
|
{{/if_eq}}
|
|
@@ -77,7 +144,11 @@ test('home page renders from browser', async ({ page }) => {
|
|
|
77
144
|
const evidence = process.env.HAI_AUTH_EVIDENCE_DIR || path.resolve('test-results/auth-screenshots')
|
|
78
145
|
await mkdir(evidence, { recursive: true })
|
|
79
146
|
await page.evaluate(() => document.fonts.ready)
|
|
80
|
-
await page.screenshot({
|
|
147
|
+
await page.screenshot({
|
|
148
|
+
path: path.join(evidence, '{{projectName}}-home.png'),
|
|
149
|
+
fullPage: true,
|
|
150
|
+
animations: 'disabled',
|
|
151
|
+
})
|
|
81
152
|
})
|
|
82
153
|
{{#if features.iam}}
|
|
83
154
|
|
|
@@ -96,21 +167,30 @@ test('configured IAM registration and password login', async ({ page, context })
|
|
|
96
167
|
await expect(page.locator('#login-username')).toBeVisible()
|
|
97
168
|
await expect(page.locator('header').first()).toBeVisible()
|
|
98
169
|
await expect(page.locator('footer')).toBeVisible()
|
|
99
|
-
await page.screenshot({
|
|
170
|
+
await page.screenshot({
|
|
171
|
+
path: path.join(evidence, '{{projectName}}-login.png'),
|
|
172
|
+
fullPage: true,
|
|
173
|
+
})
|
|
100
174
|
await page.getByRole('{{#if isCapacitorApp}}button{{else}}link{{/if}}', { name: /立即注册|Register now/i }).click()
|
|
101
175
|
{{#unless isCapacitorApp}}
|
|
102
176
|
await expect(page).toHaveURL(/\/auth\/register(?:\?|$)/)
|
|
103
177
|
{{/unless}}
|
|
104
178
|
const registerForm = page.locator('.register-form')
|
|
105
179
|
await expect(registerForm.locator('#register-username')).toBeVisible()
|
|
106
|
-
await page.screenshot({
|
|
180
|
+
await page.screenshot({
|
|
181
|
+
path: path.join(evidence, '{{projectName}}-register.png'),
|
|
182
|
+
fullPage: true,
|
|
183
|
+
})
|
|
107
184
|
if (process.env.IAM_NOTIFICATION_URL) {
|
|
108
185
|
// 协议弹窗留在认证页,关闭后继续填写注册表单。
|
|
109
186
|
await registerForm.getByRole('button', { name: /用户协议|Terms of Service/i, exact: true }).click()
|
|
110
187
|
await expect(page.getByRole('dialog')).toBeVisible()
|
|
111
188
|
await expect(page.getByRole('dialog').getByRole('heading', { name: 'Configured registration agreement' })).toBeVisible()
|
|
112
189
|
await expect(page.locator('iframe')).toHaveCount(0)
|
|
113
|
-
await page.screenshot({
|
|
190
|
+
await page.screenshot({
|
|
191
|
+
path: path.join(evidence, '{{projectName}}-agreement.png'),
|
|
192
|
+
fullPage: true,
|
|
193
|
+
})
|
|
114
194
|
await page.keyboard.press('Escape')
|
|
115
195
|
await expect(page.getByRole('dialog')).not.toBeVisible()
|
|
116
196
|
}
|
|
@@ -120,12 +200,30 @@ test('configured IAM registration and password login', async ({ page, context })
|
|
|
120
200
|
await registerForm.locator('#register-confirm-password').fill(password)
|
|
121
201
|
await registerForm.locator('button[type="submit"]').click()
|
|
122
202
|
await expect(page.locator('.register-form')).not.toBeVisible()
|
|
123
|
-
{{#
|
|
203
|
+
{{#if isCapacitorApp}}
|
|
204
|
+
// 浏览器预览通过刷新 Cookie 恢复会话,不能把页面重载当作退出。
|
|
205
|
+
await page.reload()
|
|
206
|
+
await expect(page.getByRole('navigation')).toBeVisible()
|
|
207
|
+
await page.getByRole('navigation').getByRole('button', { name: /我的|Profile|プロフィール/, exact: true }).click()
|
|
208
|
+
const appearance = page.getByTestId('profile-theme')
|
|
209
|
+
await appearance.getByRole('button', { name: /选择主题|Select theme|テーマを選択/, exact: true }).click()
|
|
210
|
+
await appearance.getByRole('button', { name: /护眼|Eye care|目にやさしい/, exact: true }).click()
|
|
211
|
+
await expect(page.locator('html')).toHaveAttribute('data-theme', 'eye-care')
|
|
212
|
+
await page.reload()
|
|
213
|
+
await expect(page.getByTestId('profile-theme')).toBeVisible()
|
|
214
|
+
await expect(page.locator('html')).toHaveAttribute('data-theme', 'eye-care')
|
|
215
|
+
await expect.poll(() => page.evaluate(() => getComputedStyle(document.documentElement).getPropertyValue('--color-base-100').trim())).toBe('#e8f0df')
|
|
216
|
+
await page.screenshot({
|
|
217
|
+
path: path.join(evidence, '{{projectName}}-eye-care.png'),
|
|
218
|
+
fullPage: true,
|
|
219
|
+
animations: 'disabled',
|
|
220
|
+
})
|
|
221
|
+
{{else}}
|
|
124
222
|
// 通过页面 fetch 复用 Cookie 与已安装的传输加密,避免 HTTP 辅助客户端绕过浏览器协议。
|
|
125
223
|
const me = await page.evaluate(async url => (await fetch(url)).status, new URL('./api/auth/me', test.info().project.use.baseURL).href)
|
|
126
224
|
expect(me).toBe(200)
|
|
225
|
+
{{/if}}
|
|
127
226
|
await context.clearCookies()
|
|
128
|
-
{{/unless}}
|
|
129
227
|
await page.reload()
|
|
130
228
|
await page.goto(authUrl)
|
|
131
229
|
await expect(page.locator('#login-username')).toBeVisible()
|
|
@@ -141,7 +239,10 @@ test('configured IAM registration and password login', async ({ page, context })
|
|
|
141
239
|
await page.reload()
|
|
142
240
|
await page.goto(authUrl)
|
|
143
241
|
await page.getByRole('button', { name: /邮件验证码|Email code/i, exact: true }).click()
|
|
144
|
-
await page.screenshot({
|
|
242
|
+
await page.screenshot({
|
|
243
|
+
path: path.join(evidence, '{{projectName}}-email.png'),
|
|
244
|
+
fullPage: true,
|
|
245
|
+
})
|
|
145
246
|
const email = `${username}@example.com`
|
|
146
247
|
await page.locator('#login-username').fill(email)
|
|
147
248
|
await page.getByRole('button', { name: /发送验证码|Send code/i, exact: true }).click()
|
|
@@ -160,34 +261,26 @@ test('configured IAM registration and password login', async ({ page, context })
|
|
|
160
261
|
await page.reload()
|
|
161
262
|
await page.goto(authUrl)
|
|
162
263
|
await page.getByRole('button', { name: /忘记密码|Forgot password/i }).click()
|
|
163
|
-
await page.screenshot({
|
|
264
|
+
await page.screenshot({
|
|
265
|
+
path: path.join(evidence, '{{projectName}}-forgot.png'),
|
|
266
|
+
fullPage: true,
|
|
267
|
+
})
|
|
164
268
|
await page.locator('input[type="email"]').fill(email)
|
|
165
269
|
await page.locator('button[type="submit"]').click()
|
|
166
270
|
await expect(page.locator('.hai-toast-item[data-variant="success"]')).toBeVisible()
|
|
167
271
|
const reset = await received('password_reset')
|
|
168
272
|
expect(Boolean(reset?.token)).toBe(true)
|
|
169
273
|
await page.getByRole('button', { name: /已有重置令牌|I have a reset token/i }).click()
|
|
170
|
-
await page.screenshot({
|
|
274
|
+
await page.screenshot({
|
|
275
|
+
path: path.join(evidence, '{{projectName}}-reset.png'),
|
|
276
|
+
fullPage: true,
|
|
277
|
+
})
|
|
171
278
|
const resetUrl = `{{#if isCapacitorApp}}/{{else}}/auth/reset-password{{/if}}?token=${encodeURIComponent(reset?.token ?? '')}`
|
|
172
279
|
{{#if_eq appType 'h5'}}
|
|
173
|
-
|
|
174
|
-
let resumeClient = () => {}
|
|
175
|
-
const clientBoot = new Promise<void>((resolve) => {
|
|
176
|
-
resumeClient = resolve
|
|
177
|
-
})
|
|
178
|
-
await page.route('**/*.js', async (route) => {
|
|
179
|
-
await clientBoot
|
|
180
|
-
await route.continue()
|
|
181
|
-
})
|
|
182
|
-
try {
|
|
280
|
+
await whileClientBootPaused(page, async () => {
|
|
183
281
|
await page.goto(resetUrl, { waitUntil: 'commit' })
|
|
184
282
|
await expect(page.locator('input[type="password"]').nth(0)).toBeDisabled()
|
|
185
|
-
}
|
|
186
|
-
finally {
|
|
187
|
-
resumeClient()
|
|
188
|
-
// 等待正在恢复的路由完成,避免提前移除拦截导致 continue 被重复处理。
|
|
189
|
-
await page.unrouteAll({ behavior: 'wait' })
|
|
190
|
-
}
|
|
283
|
+
})
|
|
191
284
|
{{else}}
|
|
192
285
|
await page.goto(resetUrl)
|
|
193
286
|
{{/if_eq}}
|
|
@@ -204,8 +297,9 @@ test('configured IAM registration and password login', async ({ page, context })
|
|
|
204
297
|
expect(new URL(page.url()).searchParams.has('token')).toBe(false)
|
|
205
298
|
await page.reload()
|
|
206
299
|
{{#if isCapacitorApp}}
|
|
207
|
-
//
|
|
208
|
-
await expect(page.
|
|
300
|
+
// 已消费的重置令牌不再参与启动;刷新 Cookie 恢复同一已登录会话。
|
|
301
|
+
await expect(page.getByRole('navigation')).toBeVisible()
|
|
302
|
+
await expect(page.locator('#login-username')).not.toBeVisible()
|
|
209
303
|
{{else}}
|
|
210
304
|
expect(await page.evaluate(async url => (await fetch(url)).status, new URL('./api/auth/me', test.info().project.use.baseURL).href)).toBe(200)
|
|
211
305
|
{{/if}}
|
|
@@ -11,35 +11,54 @@ export const haiQualityConfig = {
|
|
|
11
11
|
rules: {
|
|
12
12
|
'no-console': ['error', {}],
|
|
13
13
|
'ts/no-explicit-any': 'error',
|
|
14
|
-
'ts/ban-ts-comment': [
|
|
15
|
-
'
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
14
|
+
'ts/ban-ts-comment': [
|
|
15
|
+
'error',
|
|
16
|
+
{
|
|
17
|
+
'ts-ignore': true,
|
|
18
|
+
'ts-nocheck': true,
|
|
19
|
+
'ts-check': false,
|
|
20
|
+
'ts-expect-error': 'allow-with-description',
|
|
21
|
+
'minimumDescriptionLength': 10,
|
|
22
|
+
},
|
|
23
|
+
],
|
|
21
24
|
'eslint-comments/no-unlimited-disable': 'error',
|
|
22
25
|
'eslint-comments/require-description': 'error',
|
|
23
26
|
// 只禁内部源码/构建目录,公开的 /client、/adapter 等 exports 子路径可用。
|
|
24
|
-
'no-restricted-imports': [
|
|
25
|
-
|
|
26
|
-
|
|
27
|
+
'no-restricted-imports': [
|
|
28
|
+
'error',
|
|
29
|
+
{
|
|
30
|
+
patterns: [
|
|
31
|
+
{
|
|
32
|
+
group: [
|
|
33
|
+
'@h-ai/*/src',
|
|
34
|
+
'@h-ai/*/src/**',
|
|
35
|
+
'@h-ai/*/dist',
|
|
36
|
+
'@h-ai/*/dist/**',
|
|
37
|
+
],
|
|
38
|
+
message: 'Use the public exports of hai packages instead of their src/dist internals.',
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
'no-restricted-syntax': [
|
|
44
|
+
'error',
|
|
45
|
+
{
|
|
46
|
+
selector: ':matches(TSAsExpression, TSTypeAssertion)[expression.typeAnnotation.type="TSUnknownKeyword"]',
|
|
47
|
+
message: 'Use the real upstream type or narrow unknown. Explain necessary third-party workarounds with a scoped lint exception.',
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
selector: 'ImportExpression[source.value=/^@h-ai\\/[^/]+\\/(src|dist)(\\/|$)/]',
|
|
27
51
|
message: 'Use the public exports of hai packages instead of their src/dist internals.',
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
'no-restricted-syntax': ['error', {
|
|
31
|
-
selector: ':matches(TSAsExpression, TSTypeAssertion)[expression.typeAnnotation.type="TSUnknownKeyword"]',
|
|
32
|
-
message: 'Use the real upstream type or narrow unknown. Explain necessary third-party workarounds with a scoped lint exception.',
|
|
33
|
-
}, {
|
|
34
|
-
selector: 'ImportExpression[source.value=/^@h-ai\\/[^/]+\\/(src|dist)(\\/|$)/]',
|
|
35
|
-
message: 'Use the public exports of hai packages instead of their src/dist internals.',
|
|
36
|
-
}, {
|
|
52
|
+
},
|
|
53
|
+
{
|
|
37
54
|
// 仅识别明确的静态敏感键;变量键、别名及业务敏感程度仍需审查。
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
55
|
+
selector: 'CallExpression[callee.property.name="setItem"][callee.object.name="localStorage"][arguments.0.value=/^(token|password|secret|api[_-]?key|(access|refresh|auth|session)[_-]?token)$/i]',
|
|
56
|
+
message: 'Keep sensitive tokens in memory, httpOnly cookies or a secure native TokenStore instead of localStorage.',
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
selector: 'CallExpression[callee.property.name="setItem"][callee.object.property.name="localStorage"][arguments.0.value=/^(token|password|secret|api[_-]?key|(access|refresh|auth|session)[_-]?token)$/i]',
|
|
60
|
+
message: 'Keep sensitive tokens in memory, httpOnly cookies or a secure native TokenStore instead of localStorage.',
|
|
61
|
+
},
|
|
62
|
+
],
|
|
44
63
|
},
|
|
45
64
|
}
|
|
@@ -9,7 +9,9 @@ import { haiQualityConfig } from './eslint-rules.js'
|
|
|
9
9
|
|
|
10
10
|
export default antfu({
|
|
11
11
|
// unused-imports 使用 TS 规则,JS 同样使用 TS parser 以保持 scope 契约一致。
|
|
12
|
-
typescript: {
|
|
12
|
+
typescript: {
|
|
13
|
+
files: [GLOB_SRC],
|
|
14
|
+
},
|
|
13
15
|
formatters: true,
|
|
14
16
|
ignores: [
|
|
15
17
|
{{#unless isSvelteOnlyApp}}
|