@h-ai/cli 1.0.0-beta.18 → 1.0.0-beta.19

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 (194) hide show
  1. package/README.en.md +4 -0
  2. package/README.ja.md +4 -0
  3. package/README.md +4 -0
  4. package/dist/{chunk-6YZBC5MO.js → chunk-2ZL5ORQ2.js} +85 -25
  5. package/dist/chunk-2ZL5ORQ2.js.map +1 -0
  6. package/dist/{chunk-7NVRYYHN.js → chunk-L53P43B2.js} +129 -31
  7. package/dist/chunk-L53P43B2.js.map +1 -0
  8. package/dist/cli-config-templates-LWMD2I6V.js +5 -0
  9. package/dist/{cli-config-templates-QG5HOYR5.js.map → cli-config-templates-LWMD2I6V.js.map} +1 -1
  10. package/dist/{cli-skill-templates-W4I6ZKYW.js → cli-skill-templates-D6S2OQ6X.js} +3 -3
  11. package/dist/{cli-skill-templates-W4I6ZKYW.js.map → cli-skill-templates-D6S2OQ6X.js.map} +1 -1
  12. package/dist/index.d.ts +1 -1
  13. package/dist/index.js +2747 -671
  14. package/dist/index.js.map +1 -1
  15. package/dist/metafile-esm.json +1 -1
  16. package/package.json +22 -22
  17. package/templates/apps/admin/src/routes/+layout.svelte.hbs +16 -10
  18. package/templates/apps/admin/src/routes/+page.svelte.hbs +4 -4
  19. package/templates/apps/admin/src/routes/admin/+layout.svelte.hbs +17 -7
  20. package/templates/apps/admin/src/routes/admin/+page.svelte.hbs +26 -6
  21. package/templates/apps/admin/src/routes/admin/settings/+page.svelte.hbs +2 -2
  22. package/templates/apps/api/apps/{{projectName}}-contract/src/app-schemas.ts.hbs +6 -1
  23. package/templates/apps/api/apps/{{projectName}}-contract/src/{{projectName}}-contract.ts.hbs +13 -5
  24. package/templates/apps/api/apps/{{projectName}}-contract/tests/contract.test.ts.hbs +13 -5
  25. package/templates/apps/api/apps/{{projectName}}-contract/tsup.config.ts.hbs +3 -1
  26. package/templates/apps/api/apps/{{projectName}}-serv/src/app.ts.hbs +71 -32
  27. package/templates/apps/api/apps/{{projectName}}-serv/src/build-metadata.ts.hbs +0 -1
  28. package/templates/apps/api/apps/{{projectName}}-serv/src/lib/server/init.ts.hbs +131 -54
  29. package/templates/apps/api/apps/{{projectName}}-serv/src/server/procedures/index.ts.hbs +39 -13
  30. package/templates/apps/api/apps/{{projectName}}-serv/tests/service.test.ts.hbs +78 -31
  31. package/templates/apps/api/apps/{{projectName}}-serv/tsup.config.ts.hbs +3 -1
  32. package/templates/apps/api/apps/{{projectName}}-serv/vitest.config.ts.hbs +3 -1
  33. package/templates/apps/api/e2e/api-serv.spec.ts.hbs +20 -3
  34. package/templates/apps/api/e2e/capture-evidence.ts.hbs +4 -1
  35. package/templates/apps/api/eslint.config.js.hbs +3 -1
  36. package/templates/apps/api/images/serv.Dockerfile.hbs +3 -4
  37. package/templates/apps/api/playwright.config.ts.hbs +23 -19
  38. package/templates/apps/api/pnpm-workspace.yaml.hbs +27 -16
  39. package/templates/apps/fullstack/apps/{{projectName}}-app/capacitor.config.ts.hbs +1 -3
  40. package/templates/apps/fullstack/apps/{{projectName}}-app/eslint.config.js.hbs +10 -2
  41. package/templates/apps/fullstack/apps/{{projectName}}-app/src/App.svelte.hbs +10 -2
  42. package/templates/apps/fullstack/apps/{{projectName}}-app/src/build-metadata.ts.hbs +0 -1
  43. package/templates/apps/fullstack/apps/{{projectName}}-app/src/lib/capacitor.ts.hbs +0 -1
  44. package/templates/apps/fullstack/apps/{{projectName}}-app/svelte.config.js.hbs +4 -1
  45. package/templates/apps/fullstack/apps/{{projectName}}-app/vite.config.ts.hbs +16 -4
  46. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/App.svelte.hbs +8 -2
  47. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/build-metadata.ts.hbs +0 -1
  48. package/templates/apps/fullstack/apps/{{projectName}}-desktop/svelte.config.js.hbs +4 -1
  49. package/templates/apps/fullstack/apps/{{projectName}}-desktop/vite.config.ts.hbs +16 -4
  50. package/templates/apps/fullstack/apps/{{projectName}}-web/eslint.config.js.hbs +10 -2
  51. package/templates/apps/fullstack/apps/{{projectName}}-web/src/build-metadata.ts.hbs +0 -1
  52. package/templates/apps/fullstack/apps/{{projectName}}-web/src/main.ts.hbs +0 -2
  53. package/templates/apps/fullstack/apps/{{projectName}}-web/svelte.config.js.hbs +4 -1
  54. package/templates/apps/fullstack/apps/{{projectName}}-web/vite.config.ts.hbs +19 -5
  55. package/templates/apps/fullstack/e2e/fullstack.spec.ts.hbs +111 -26
  56. package/templates/apps/fullstack/e2e/session-close.spec.ts.hbs +19 -5
  57. package/templates/apps/fullstack/eslint.config.js.hbs +3 -1
  58. package/templates/apps/fullstack/images/serv.Dockerfile.hbs +3 -4
  59. package/templates/apps/fullstack/images/web.Dockerfile.hbs +0 -2
  60. package/templates/apps/fullstack/packages/{{projectName}}-contract/src/admin-contract.ts.hbs +114 -23
  61. package/templates/apps/fullstack/packages/{{projectName}}-contract/src/admin-schemas.ts.hbs +57 -11
  62. package/templates/apps/fullstack/packages/{{projectName}}-contract/tsup.config.ts.hbs +3 -1
  63. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/app-server.ts.hbs +65 -30
  64. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/build-metadata.ts.hbs +0 -1
  65. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/admin-service.ts.hbs +69 -21
  66. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/business-settings.ts.hbs +21 -5
  67. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/admin-procedures.ts.hbs +63 -11
  68. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/app-procedures.ts.hbs +6 -1
  69. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/index.ts.hbs +3 -1
  70. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/runtime.ts.hbs +139 -49
  71. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/security-roles.ts.hbs +43 -12
  72. package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/app-server.test.ts.hbs +139 -28
  73. package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/runtime.test.ts.hbs +33 -10
  74. package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/security.test.ts.hbs +141 -25
  75. package/templates/apps/fullstack/packages/{{projectName}}-serv/tsup.config.ts.hbs +6 -2
  76. package/templates/apps/fullstack/packages/{{projectName}}-serv/vitest.config.ts.hbs +3 -1
  77. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/api/api-client.ts.hbs +9 -2
  78. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/api/feedback-client.ts.hbs +0 -3
  79. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/api/sensitive-actions.svelte.ts.hbs +15 -3
  80. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/AppShell.svelte.hbs +33 -7
  81. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/FeedbackEntry.svelte.hbs +16 -2
  82. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/audit.svelte.hbs +104 -13
  83. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/feedback.svelte.hbs +37 -7
  84. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/roles.svelte.hbs +9 -3
  85. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/settings.svelte.hbs +25 -3
  86. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/users.svelte.hbs +133 -19
  87. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/security/role-label.ts.hbs +14 -2
  88. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/stores/guide-store.ts.hbs +66 -34
  89. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/stores/management-store.svelte.ts.hbs +132 -30
  90. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/stores/theme-store.svelte.ts.hbs +8 -2
  91. package/templates/apps/fullstack/packages/{{projectName}}-shared/tests/api-client.test.ts.hbs +23 -5
  92. package/templates/apps/fullstack/packages/{{projectName}}-shared/tests/management-store.test.ts.hbs +143 -21
  93. package/templates/apps/fullstack/packages/{{projectName}}-shared/vitest.config.ts.hbs +7 -2
  94. package/templates/apps/fullstack/playwright.config.ts.hbs +9 -3
  95. package/templates/apps/fullstack/pnpm-workspace.yaml.hbs +30 -15
  96. package/templates/apps/h5/src/routes/+layout.svelte.hbs +57 -21
  97. package/templates/apps/h5/src/routes/+page.svelte.hbs +2 -2
  98. package/templates/apps/h5/src/routes/discover/+page.svelte.hbs +2 -2
  99. package/templates/apps/h5/src/routes/profile/+page.svelte.hbs +2 -2
  100. package/templates/apps/mobile-app/src/App.svelte.hbs +17 -5
  101. package/templates/apps/mobile-app/src/lib/api.ts.hbs +14 -3
  102. package/templates/apps/mobile-app/src/lib/app-config.ts.hbs +8 -1
  103. package/templates/apps/mobile-app/src/lib/pages/home.svelte.hbs +6 -2
  104. package/templates/apps/mobile-app/src/lib/pages/tasks.svelte.hbs +5 -1
  105. package/templates/apps/website/scripts/static-website.mjs.hbs +16 -3
  106. package/templates/apps/website/src/routes/+layout.svelte.hbs +44 -23
  107. package/templates/apps/website/src/routes/+page.svelte.hbs +4 -4
  108. package/templates/apps/website/src/routes/about/+page.svelte.hbs +2 -2
  109. package/templates/apps/website/src/routes/contact/+page.svelte.hbs +2 -2
  110. package/templates/apps/website/src/routes/services/+page.svelte.hbs +20 -5
  111. package/templates/base/e2e/app.spec.ts.hbs +49 -16
  112. package/templates/base/eslint-rules.js +44 -25
  113. package/templates/base/eslint.config.js.hbs +3 -1
  114. package/templates/base/images/web.Dockerfile.hbs +3 -4
  115. package/templates/base/package.json.hbs +5 -4
  116. package/templates/base/patches/daisyui-5.7.50.patch +8 -0
  117. package/templates/base/patches/deepmerge-4.3.1.patch +51 -0
  118. package/templates/base/playwright.config.ts +3 -1
  119. package/templates/base/pnpm-workspace.yaml.hbs +33 -16
  120. package/templates/base/src/app.d.ts.hbs +1 -1
  121. package/templates/base/src/hooks.server.ts.hbs +36 -27
  122. package/templates/base/src/lib/build-metadata.ts.hbs +0 -1
  123. package/templates/base/src/lib/server/init.ts.hbs +124 -51
  124. package/templates/base/src/lib/server/kit-config.ts.hbs +7 -4
  125. package/templates/base/src/routes/+layout.server.ts.hbs +2 -2
  126. package/templates/base/svelte.config.js.hbs +10 -100
  127. package/templates/base/tests/scaffold.test.ts.hbs +12 -3
  128. package/templates/base/tsconfig.json.hbs +7 -4
  129. package/templates/base/vite.config.ts.hbs +108 -14
  130. package/templates/base/vitest.config.ts.hbs +6 -2
  131. package/templates/features/ai/routes/api/chat/+server.ts +3 -1
  132. package/templates/features/iam/routes-admin/(auth)/auth/forgot-password/+page.svelte.hbs +3 -3
  133. package/templates/features/iam/routes-admin/(auth)/auth/login/+page.svelte.hbs +3 -3
  134. package/templates/features/iam/routes-admin/(auth)/auth/register/+page.svelte.hbs +3 -3
  135. package/templates/features/iam/routes-admin/(auth)/auth/reset-password/+page.svelte.hbs +3 -3
  136. package/templates/features/iam/routes-h5/auth/forgot-password/+page.svelte.hbs +3 -3
  137. package/templates/features/iam/routes-h5/auth/login/+page.svelte.hbs +3 -3
  138. package/templates/features/iam/routes-h5/auth/register/+page.svelte.hbs +3 -3
  139. package/templates/features/iam/routes-h5/auth/reset-password/+page.svelte.hbs +3 -3
  140. package/templates/features/iam/routes-website/auth/forgot-password/+page.svelte.hbs +3 -3
  141. package/templates/features/iam/routes-website/auth/login/+page.svelte.hbs +3 -3
  142. package/templates/features/iam/routes-website/auth/register/+page.svelte.hbs +3 -3
  143. package/templates/features/iam/routes-website/auth/reset-password/+page.svelte.hbs +3 -3
  144. package/templates/features/payment/routes/api/v1/payment/create/+server.ts +11 -2
  145. package/templates/shared/browser-check/startup.js +32 -6
  146. package/templates/shared/browser-startup.spec.ts +3 -1
  147. package/templates/shared/build-metadata.ts +18 -4
  148. package/templates/shared/e2e-env.ts +22 -6
  149. package/templates/shared/native-name.mjs +9 -2
  150. package/templates/shared/public-base-path.mjs +17 -4
  151. package/templates/shared/run-e2e.mjs +40 -9
  152. package/templates/shared/run-node.mjs +15 -4
  153. package/templates/shared/run-tauri.mjs +37 -8
  154. package/templates/shared/vite-runtime-config.ts +9 -3
  155. package/templates/skills/AGENTS.md +10 -73
  156. package/templates/skills/bridges/admin/AGENTS.md +9 -65
  157. package/templates/skills/bridges/api/AGENTS.md +8 -59
  158. package/templates/skills/bridges/fullstack/AGENTS.md.hbs +9 -54
  159. package/templates/skills/bridges/h5/AGENTS.md +8 -63
  160. package/templates/skills/bridges/mobile-app/AGENTS.md +8 -62
  161. package/templates/skills/bridges/website/AGENTS.md +8 -63
  162. package/templates/skills/hai-ai/acp.md +2 -0
  163. package/templates/skills/hai-ai/guide.md +17 -6
  164. package/templates/skills/hai-ai/reference.md +21 -12
  165. package/templates/skills/hai-api-client/reference.md +1 -1
  166. package/templates/skills/hai-app-create/development.md +3 -1
  167. package/templates/skills/hai-app-create/project.md +33 -0
  168. package/templates/skills/hai-app-create/reference.md +8 -8
  169. package/templates/skills/hai-app-tests/reference.md +1 -1
  170. package/templates/skills/hai-build/SKILL.md +2 -2
  171. package/templates/skills/hai-build/paths.md +1 -1
  172. package/templates/skills/hai-build/reference.md +11 -7
  173. package/templates/skills/hai-capacitor/reference.md +2 -0
  174. package/templates/skills/hai-ci/SKILL.md +1 -0
  175. package/templates/skills/hai-core/reference.md +3 -0
  176. package/templates/skills/hai-crypto/reference.md +2 -0
  177. package/templates/skills/hai-deploy/SKILL.md +7 -1
  178. package/templates/skills/hai-deploy/middleware.md +3 -1
  179. package/templates/skills/hai-framework-sync/sync.md +2 -0
  180. package/templates/skills/hai-kit/reference.md +16 -4
  181. package/templates/skills/hai-payment/reference.md +2 -0
  182. package/templates/skills/hai-reach/SKILL.md +2 -1
  183. package/templates/skills/hai-reach/reference.md +1 -1
  184. package/templates/skills/hai-reldb/SKILL.md +1 -1
  185. package/templates/skills/hai-reldb/reference.md +4 -0
  186. package/templates/skills/hai-serv/reference.md +12 -2
  187. package/templates/skills/hai-storage/reference.md +12 -3
  188. package/templates/skills/hai-ui/SKILL.md +3 -2
  189. package/templates/skills/hai-ui/guide.md +1 -1
  190. package/templates/skills/hai-ui/reference.md +23 -35
  191. package/templates/skills/hai-vecdb/reference.md +1 -1
  192. package/dist/chunk-6YZBC5MO.js.map +0 -1
  193. package/dist/chunk-7NVRYYHN.js.map +0 -1
  194. package/dist/cli-config-templates-QG5HOYR5.js +0 -5
@@ -12,7 +12,7 @@ import { expect, test } from '@playwright/test'
12
12
  {{#if_eq appType 'h5'}}
13
13
  /** 延迟客户端脚本,覆盖 SSR 已显示而事件尚未绑定的阶段。 */
14
14
  async function whileClientBootPaused(page: Page, inspect: () => Promise<void>) {
15
- let resumeClient = () => {}
15
+ let resumeClient = () => { }
16
16
  const clientBoot = new Promise<void>((resolve) => {
17
17
  resumeClient = resolve
18
18
  })
@@ -29,7 +29,6 @@ async function whileClientBootPaused(page: Page, inspect: () => Promise<void>) {
29
29
  await page.unrouteAll({ behavior: 'wait' })
30
30
  }
31
31
  }
32
-
33
32
  {{/if_eq}}
34
33
  {{#if_eq appType 'admin'}}
35
34
  test('high contrast appearance is selectable and survives reload', async ({ page }) => {
@@ -51,7 +50,6 @@ test('high contrast appearance is selectable and survives reload', async ({ page
51
50
  expect(await header.locator('button').first().evaluate(element => getComputedStyle(element).borderTopLeftRadius)).toBe('0px')
52
51
  })
53
52
  {{/if_eq}}
54
-
55
53
  {{#if hasUi}}
56
54
  {{#unless isCapacitorApp}}
57
55
  test('eye care appearance is selectable and survives reload', async ({ page }, testInfo) => {
@@ -73,19 +71,28 @@ test('eye care appearance is selectable and survives reload', async ({ page }, t
73
71
  await expect(page.locator('html')).toHaveAttribute('data-theme', 'eye-care')
74
72
  await expect.poll(() => page.evaluate(() => getComputedStyle(document.documentElement).colorScheme)).toBe('light')
75
73
  await expect.poll(() => page.evaluate(() => getComputedStyle(document.documentElement).getPropertyValue('--color-base-100').trim())).toBe('#e8f0df')
76
- await page.screenshot({ path: testInfo.outputPath('eye-care-desktop.png'), animations: 'disabled' })
74
+ await page.screenshot({
75
+ path: testInfo.outputPath('eye-care-desktop.png'),
76
+ animations: 'disabled',
77
+ })
77
78
  await page.setViewportSize({ width: 390, height: 844 })
78
79
  expect(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth)).toBe(true)
79
- await page.screenshot({ path: testInfo.outputPath('eye-care-mobile.png'), animations: 'disabled' })
80
+ await page.screenshot({
81
+ path: testInfo.outputPath('eye-care-mobile.png'),
82
+ animations: 'disabled',
83
+ })
80
84
  })
81
85
  {{/unless}}
82
86
  {{/if}}
83
-
84
87
  {{#if_eq appType 'h5'}}
85
- test.use({ viewport: { width: 390, height: 844 } })
88
+ test.use({
89
+ viewport: { width: 390, height: 844 },
90
+ })
86
91
  {{/if_eq}}
87
92
  {{#if isCapacitorApp}}
88
- test.use({ viewport: { width: 390, height: 844 } })
93
+ test.use({
94
+ viewport: { width: 390, height: 844 },
95
+ })
89
96
  {{/if}}
90
97
 
91
98
  /** 首页从真实浏览器页面发起,覆盖基础渲染链路。 */
@@ -137,7 +144,11 @@ test('home page renders from browser', async ({ page }) => {
137
144
  const evidence = process.env.HAI_AUTH_EVIDENCE_DIR || path.resolve('test-results/auth-screenshots')
138
145
  await mkdir(evidence, { recursive: true })
139
146
  await page.evaluate(() => document.fonts.ready)
140
- await page.screenshot({ path: path.join(evidence, '{{projectName}}-home.png'), fullPage: true, animations: 'disabled' })
147
+ await page.screenshot({
148
+ path: path.join(evidence, '{{projectName}}-home.png'),
149
+ fullPage: true,
150
+ animations: 'disabled',
151
+ })
141
152
  })
142
153
  {{#if features.iam}}
143
154
 
@@ -156,21 +167,30 @@ test('configured IAM registration and password login', async ({ page, context })
156
167
  await expect(page.locator('#login-username')).toBeVisible()
157
168
  await expect(page.locator('header').first()).toBeVisible()
158
169
  await expect(page.locator('footer')).toBeVisible()
159
- await page.screenshot({ path: path.join(evidence, '{{projectName}}-login.png'), fullPage: true })
170
+ await page.screenshot({
171
+ path: path.join(evidence, '{{projectName}}-login.png'),
172
+ fullPage: true,
173
+ })
160
174
  await page.getByRole('{{#if isCapacitorApp}}button{{else}}link{{/if}}', { name: /立即注册|Register now/i }).click()
161
175
  {{#unless isCapacitorApp}}
162
176
  await expect(page).toHaveURL(/\/auth\/register(?:\?|$)/)
163
177
  {{/unless}}
164
178
  const registerForm = page.locator('.register-form')
165
179
  await expect(registerForm.locator('#register-username')).toBeVisible()
166
- await page.screenshot({ path: path.join(evidence, '{{projectName}}-register.png'), fullPage: true })
180
+ await page.screenshot({
181
+ path: path.join(evidence, '{{projectName}}-register.png'),
182
+ fullPage: true,
183
+ })
167
184
  if (process.env.IAM_NOTIFICATION_URL) {
168
185
  // 协议弹窗留在认证页,关闭后继续填写注册表单。
169
186
  await registerForm.getByRole('button', { name: /用户协议|Terms of Service/i, exact: true }).click()
170
187
  await expect(page.getByRole('dialog')).toBeVisible()
171
188
  await expect(page.getByRole('dialog').getByRole('heading', { name: 'Configured registration agreement' })).toBeVisible()
172
189
  await expect(page.locator('iframe')).toHaveCount(0)
173
- await page.screenshot({ path: path.join(evidence, '{{projectName}}-agreement.png'), fullPage: true })
190
+ await page.screenshot({
191
+ path: path.join(evidence, '{{projectName}}-agreement.png'),
192
+ fullPage: true,
193
+ })
174
194
  await page.keyboard.press('Escape')
175
195
  await expect(page.getByRole('dialog')).not.toBeVisible()
176
196
  }
@@ -193,7 +213,11 @@ test('configured IAM registration and password login', async ({ page, context })
193
213
  await expect(page.getByTestId('profile-theme')).toBeVisible()
194
214
  await expect(page.locator('html')).toHaveAttribute('data-theme', 'eye-care')
195
215
  await expect.poll(() => page.evaluate(() => getComputedStyle(document.documentElement).getPropertyValue('--color-base-100').trim())).toBe('#e8f0df')
196
- await page.screenshot({ path: path.join(evidence, '{{projectName}}-eye-care.png'), fullPage: true, animations: 'disabled' })
216
+ await page.screenshot({
217
+ path: path.join(evidence, '{{projectName}}-eye-care.png'),
218
+ fullPage: true,
219
+ animations: 'disabled',
220
+ })
197
221
  {{else}}
198
222
  // 通过页面 fetch 复用 Cookie 与已安装的传输加密,避免 HTTP 辅助客户端绕过浏览器协议。
199
223
  const me = await page.evaluate(async url => (await fetch(url)).status, new URL('./api/auth/me', test.info().project.use.baseURL).href)
@@ -215,7 +239,10 @@ test('configured IAM registration and password login', async ({ page, context })
215
239
  await page.reload()
216
240
  await page.goto(authUrl)
217
241
  await page.getByRole('button', { name: /邮件验证码|Email code/i, exact: true }).click()
218
- await page.screenshot({ path: path.join(evidence, '{{projectName}}-email.png'), fullPage: true })
242
+ await page.screenshot({
243
+ path: path.join(evidence, '{{projectName}}-email.png'),
244
+ fullPage: true,
245
+ })
219
246
  const email = `${username}@example.com`
220
247
  await page.locator('#login-username').fill(email)
221
248
  await page.getByRole('button', { name: /发送验证码|Send code/i, exact: true }).click()
@@ -234,14 +261,20 @@ test('configured IAM registration and password login', async ({ page, context })
234
261
  await page.reload()
235
262
  await page.goto(authUrl)
236
263
  await page.getByRole('button', { name: /忘记密码|Forgot password/i }).click()
237
- await page.screenshot({ path: path.join(evidence, '{{projectName}}-forgot.png'), fullPage: true })
264
+ await page.screenshot({
265
+ path: path.join(evidence, '{{projectName}}-forgot.png'),
266
+ fullPage: true,
267
+ })
238
268
  await page.locator('input[type="email"]').fill(email)
239
269
  await page.locator('button[type="submit"]').click()
240
270
  await expect(page.locator('.hai-toast-item[data-variant="success"]')).toBeVisible()
241
271
  const reset = await received('password_reset')
242
272
  expect(Boolean(reset?.token)).toBe(true)
243
273
  await page.getByRole('button', { name: /已有重置令牌|I have a reset token/i }).click()
244
- await page.screenshot({ path: path.join(evidence, '{{projectName}}-reset.png'), fullPage: true })
274
+ await page.screenshot({
275
+ path: path.join(evidence, '{{projectName}}-reset.png'),
276
+ fullPage: true,
277
+ })
245
278
  const resetUrl = `{{#if isCapacitorApp}}/{{else}}/auth/reset-password{{/if}}?token=${encodeURIComponent(reset?.token ?? '')}`
246
279
  {{#if_eq appType 'h5'}}
247
280
  await whileClientBootPaused(page, async () => {
@@ -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': ['error', {
15
- 'ts-ignore': true,
16
- 'ts-nocheck': true,
17
- 'ts-check': false,
18
- 'ts-expect-error': 'allow-with-description',
19
- 'minimumDescriptionLength': 10,
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': ['error', {
25
- patterns: [{
26
- group: ['@h-ai/*/src', '@h-ai/*/src/**', '@h-ai/*/dist', '@h-ai/*/dist/**'],
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
- selector: 'CallExpression[callee.property.name="setItem"][callee.object.name="localStorage"][arguments.0.value=/^(token|password|secret|api[_-]?key|(access|refresh|auth|session)[_-]?token)$/i]',
39
- message: 'Keep sensitive tokens in memory, httpOnly cookies or a secure native TokenStore instead of localStorage.',
40
- }, {
41
- 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]',
42
- message: 'Keep sensitive tokens in memory, httpOnly cookies or a secure native TokenStore instead of localStorage.',
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: { files: [GLOB_SRC] },
12
+ typescript: {
13
+ files: [GLOB_SRC],
14
+ },
13
15
  formatters: true,
14
16
  ignores: [
15
17
  {{#unless isSvelteOnlyApp}}
@@ -36,8 +36,6 @@ HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
36
36
  CMD wget -q -O /dev/null http://127.0.0.1:8080/health || exit 1
37
37
  {{else}}
38
38
  FROM ${HAI_NODE_IMAGE} AS build
39
- ARG SQLITE_PREBUILD_MIRROR=
40
- ENV npm_config_better_sqlite3_binary_host_mirror=$SQLITE_PREBUILD_MIRROR
41
39
 
42
40
  ENV PNPM_HOME=/pnpm
43
41
  ENV PATH=$PNPM_HOME:$PATH
@@ -59,8 +57,9 @@ ARG HAI_BUILD_ID=
59
57
  ARG HAI_BUILD_COMMIT=unknown
60
58
  ARG PUBLIC_BASE_PATH=
61
59
  RUN HAI_BUILD_ID="$HAI_BUILD_ID" HAI_BUILD_COMMIT="$HAI_BUILD_COMMIT" PUBLIC_BASE_PATH="$PUBLIC_BASE_PATH" pnpm compile
62
- RUN --mount=type=cache,id={{projectName}}-sqlite,target=/root/.npm/_prebuilds if [ -d node_modules/better-sqlite3 ]; then cd node_modules/better-sqlite3; for attempt in 1 2 3; do node -e "require('node:child_process').execFileSync(process.execPath,[require.resolve('prebuild-install/bin.js'),'--verbose'],{stdio:'inherit'})" && break; [ "$attempt" = 3 ] && exit 1; sleep 3; done; fi
63
- RUN --mount=type=cache,id={{projectName}}-pnpm,target=/pnpm/store --mount=type=cache,id={{projectName}}-sqlite,target=/root/.npm/_prebuilds pnpm pm deploy --filter {{projectName}} --prod /prod/app
60
+ # SQLite 13 的原生二进制随 npm 包交付,在构建时验证实际加载。
61
+ RUN if [ -d node_modules/better-sqlite3 ]; then node -e "const Database=require('better-sqlite3');const db=new Database(':memory:');db.prepare('SELECT 1').get();db.close()"; fi
62
+ RUN --mount=type=cache,id={{projectName}}-pnpm,target=/pnpm/store pnpm pm deploy --filter {{projectName}} --prod /prod/app
64
63
  RUN rm -f /prod/app/README* && find /prod/app/build -type f \( -name '*.map' -o -name '*.d.ts' -o -name '*.d.mts' -o -name '*.d.cts' \) -delete
65
64
 
66
65
  FROM ${HAI_NODE_IMAGE} AS runtime-base
@@ -1,13 +1,14 @@
1
1
  {
2
2
  "engines": { "node": "^22.23.4 || ^24.22.1 || ^26.12.1" },
3
3
  "name": "{{projectName}}",
4
- "version": "1.0.0-beta.18",
4
+ "version": "1.0.0-beta.19",
5
5
  "private": true,
6
6
  "packageManager": "pnpm@12.11.2",
7
7
  "description": "{{#if_eq appType 'admin'}}Enterprise management system built with hai Agent Framework{{/if_eq}}{{#if_eq appType 'website'}}Enterprise website built with hai Agent Framework{{/if_eq}}{{#if_eq appType 'h5'}}Mobile H5 app built with hai Agent Framework{{/if_eq}}{{#if_eq appType 'api'}}REST API service built with hai Agent Framework{{/if_eq}}{{#if_eq appType 'mobile-app'}}Mobile app built with hai Agent Framework{{/if_eq}}",
8
8
  "keywords": ["hai", {{#unless isSvelteOnlyApp}}"sveltekit", {{/unless}}"svelte5", "{{appType}}"],
9
9
  "license": "Apache-2.0",
10
10
  "type": "module",
11
+ "imports": { "#lib/*": "./src/lib/*" },
11
12
  {{#unless isSvelteOnlyApp}}
12
13
  "files": [
13
14
  "build",
@@ -38,8 +39,8 @@
38
39
  "typecheck": "{{#if hasI18n}}pnpm paraglide:compile && {{/if}}{{#if isSvelteOnlyApp}}svelte-check --tsconfig ./tsconfig.json{{else}}svelte-kit sync && svelte-check --tsconfig ./tsconfig.json{{/if}}",
39
40
  "lint": "eslint .",
40
41
  "lint:fix": "eslint . --fix",
41
- "test": "{{#if hasI18n}}pnpm paraglide:compile && {{/if}}vitest run",
42
- "test:watch": "vitest",
42
+ "test": "{{#if hasI18n}}pnpm paraglide:compile && {{/if}}{{#unless isSvelteOnlyApp}}svelte-kit sync && {{/unless}}vitest run",
43
+ "test:watch": "{{#if hasI18n}}pnpm paraglide:compile && {{/if}}{{#unless isSvelteOnlyApp}}svelte-kit sync && {{/unless}}vitest",
43
44
  "test:e2e": "node scripts/run-e2e.mjs",
44
45
  "clean": "{{#if isSvelteOnlyApp}}rimraf dist build .turbo src/lib/paraglide{{else}}rimraf .svelte-kit build .turbo{{/if}}"{{#if isCapacitorApp}},
45
46
  "cap:add:android": "cap add android",
@@ -87,7 +88,7 @@
87
88
  "@h-ai/capacitor": "{{haiPackageSpecifier}}"{{/if}}{{#if isCapacitorApp}}{{#unless features.api-client}},
88
89
  "@h-ai/api-client": "{{haiPackageSpecifier}}"{{/unless}}{{#unless features.capacitor}},
89
90
  "@h-ai/capacitor": "{{haiPackageSpecifier}}"{{/unless}}{{/if}},
90
- "sm-crypto": "0.5.7",
91
+ "sm-crypto": "0.5.8",
91
92
  "zod": "{{versions.zod}}"
92
93
  {{/if}}
93
94
  },
@@ -0,0 +1,8 @@
1
+ diff --git a/utilities/join/object.js b/utilities/join/object.js
2
+ --- a/utilities/join/object.js
3
+ +++ b/utilities/join/object.js
4
+ @@ -1 +1 @@
5
+ -export default {".join":{"display":"inline-flex","align-items":"stretch","--join-ss":"0","--join-se":"0","--join-es":"0","--join-ee":"0","--join-ml":"0","--join-mt":"0","--join-v":"0","--join-h":"1","@scope (&)":{"> :where(:focus, :has(:focus))":{"z-index":2},"@media (hover: hover)":{"> :where(.btn:hover, :has(.btn:hover))":{"z-index":1}},":where(:scope > :first-child)":{"--join-ss":"var(--radius-field)","--join-se":"calc(var(--radius-field) * var(--join-v))","--join-es":"calc(var(--radius-field) * var(--join-h))","--join-ee":"0"},":where(:scope > :last-child)":{"--join-ss":"0","--join-se":"calc(var(--radius-field) * var(--join-h))","--join-es":"calc(var(--radius-field) * var(--join-v))","--join-ee":"var(--radius-field)"},":where(:scope > :only-child)":{"--join-ss":"var(--radius-field)","--join-se":"var(--radius-field)","--join-es":"var(--radius-field)","--join-ee":"var(--radius-field)"},":where(:scope > :not(:first-child))":{"--join-ml":"calc(var(--border, 1px) * -1 * var(--join-h))","--join-mt":"calc(var(--border, 1px) * -1 * var(--join-v))"}}},".join-item":{"@layer daisyui.l1.l2.l3.l4":{"> *":{"--join-ss":"initial","--join-se":"initial","--join-es":"initial","--join-ee":"initial"}},"border-style":"solid","border-width":"var(--border, 1px)","border-start-start-radius":"var(--join-ss)","border-start-end-radius":"var(--join-se)","border-end-start-radius":"var(--join-es)","border-end-end-radius":"var(--join-ee)","&:not(:disabled, [disabled], .btn-disabled)":{"margin-inline-start":"var(--join-ml, 0)","margin-block-start":"var(--join-mt, 0)"},"&:is(:disabled, [disabled], .btn-disabled)":{"border-width":"var(--border, 1px)","border-inline-end-width":"calc(var(--border, 1px) * var(--join-v))","border-block-end-width":"calc(var(--border, 1px) * var(--join-h))"}},".join-vertical":{"flex-direction":"column","--join-v":"1","--join-h":"0"},".join-horizontal":{"flex-direction":"row","--join-v":"0","--join-h":"1"}};
6
+
7
+ +export default {".join":{"display":"inline-flex","align-items":"stretch","--join-ss":"0","--join-se":"0","--join-es":"0","--join-ee":"0","--join-ml":"0","--join-mt":"0","--join-v":"0","--join-h":"1","@scope (&)":{":where(:scope) > :where(:focus, :has(:focus))":{"z-index":2},"@media (hover: hover)":{":where(:scope) > :where(.btn:hover, :has(.btn:hover))":{"z-index":1}},":where(:scope > :first-child)":{"--join-ss":"var(--radius-field)","--join-se":"calc(var(--radius-field) * var(--join-v))","--join-es":"calc(var(--radius-field) * var(--join-h))","--join-ee":"0"},":where(:scope > :last-child)":{"--join-ss":"0","--join-se":"calc(var(--radius-field) * var(--join-h))","--join-es":"calc(var(--radius-field) * var(--join-v))","--join-ee":"var(--radius-field)"},":where(:scope > :only-child)":{"--join-ss":"var(--radius-field)","--join-se":"var(--radius-field)","--join-es":"var(--radius-field)","--join-ee":"var(--radius-field)"},":where(:scope > :not(:first-child))":{"--join-ml":"calc(var(--border, 1px) * -1 * var(--join-h))","--join-mt":"calc(var(--border, 1px) * -1 * var(--join-v))"}}},".join-item":{"@layer daisyui.l1.l2.l3.l4":{"> *":{"--join-ss":"initial","--join-se":"initial","--join-es":"initial","--join-ee":"initial"}},"border-style":"solid","border-width":"var(--border, 1px)","border-start-start-radius":"var(--join-ss)","border-start-end-radius":"var(--join-se)","border-end-start-radius":"var(--join-es)","border-end-end-radius":"var(--join-ee)","&:not(:disabled, [disabled], .btn-disabled)":{"margin-inline-start":"var(--join-ml, 0)","margin-block-start":"var(--join-mt, 0)"},"&:is(:disabled, [disabled], .btn-disabled)":{"border-width":"var(--border, 1px)","border-inline-end-width":"calc(var(--border, 1px) * var(--join-v))","border-block-end-width":"calc(var(--border, 1px) * var(--join-h))"}},".join-vertical":{"flex-direction":"column","--join-v":"1","--join-h":"0"},".join-horizontal":{"flex-direction":"row","--join-v":"0","--join-h":"1"}};
8
+
@@ -0,0 +1,51 @@
1
+ diff --git a/index.js b/index.js
2
+ --- a/index.js
3
+ +++ b/index.js
4
+ @@ -55,11 +55,12 @@
5
+ var destination = {}
6
+ if (options.isMergeableObject(target)) {
7
+ getKeys(target).forEach(function(key) {
8
+ + if (key === '__proto__') return
9
+ destination[key] = cloneUnlessOtherwiseSpecified(target[key], options)
10
+ })
11
+ }
12
+ getKeys(source).forEach(function(key) {
13
+ - if (propertyIsUnsafe(target, key)) {
14
+ + if (key === '__proto__' || propertyIsUnsafe(target, key)) {
15
+ return
16
+ }
17
+
18
+ diff --git a/dist/cjs.js b/dist/cjs.js
19
+ --- a/dist/cjs.js
20
+ +++ b/dist/cjs.js
21
+ @@ -80,11 +80,12 @@
22
+ var destination = {};
23
+ if (options.isMergeableObject(target)) {
24
+ getKeys(target).forEach(function(key) {
25
+ + if (key === '__proto__') return
26
+ destination[key] = cloneUnlessOtherwiseSpecified(target[key], options);
27
+ });
28
+ }
29
+ getKeys(source).forEach(function(key) {
30
+ - if (propertyIsUnsafe(target, key)) {
31
+ + if (key === '__proto__' || propertyIsUnsafe(target, key)) {
32
+ return
33
+ }
34
+
35
+ diff --git a/dist/umd.js b/dist/umd.js
36
+ --- a/dist/umd.js
37
+ +++ b/dist/umd.js
38
+ @@ -84,11 +84,12 @@
39
+ var destination = {};
40
+ if (options.isMergeableObject(target)) {
41
+ getKeys(target).forEach(function(key) {
42
+ + if (key === '__proto__') return
43
+ destination[key] = cloneUnlessOtherwiseSpecified(target[key], options);
44
+ });
45
+ }
46
+ getKeys(source).forEach(function(key) {
47
+ - if (propertyIsUnsafe(target, key)) {
48
+ + if (key === '__proto__' || propertyIsUnsafe(target, key)) {
49
+ return
50
+ }
51
+
@@ -24,7 +24,9 @@ function resolvePortFromBaseUrl(urlString: string, fallbackPort: string): string
24
24
  return url.port
25
25
  }
26
26
 
27
- return url.protocol === 'https:' ? '443' : '80'
27
+ return url.protocol === 'https:'
28
+ ? '443'
29
+ : '80'
28
30
  }
29
31
  catch {
30
32
  return fallbackPort
@@ -1,6 +1,6 @@
1
1
  {{! @skipIfEmpty }}
2
2
  {{#if useCatalogProtocol}}
3
- shellEmulator: true
3
+ minimumReleaseAgeExcludePrune: true
4
4
 
5
5
  trustPolicy: no-downgrade
6
6
  # 上游构建工具仍要求 4.x;4.0.3 tarball 已核对原 lockfile 的 SHA-512。
@@ -9,32 +9,49 @@ trustPolicyExclude:
9
9
 
10
10
  # pnpm 12 配置;原生依赖仅运行明确列出的安装脚本。
11
11
  nodeLinker: hoisted
12
- allowBuilds:
13
- cpu-features: true
14
- onnxruntime-node: true
15
- protobufjs: true
16
- ssh2: true
17
- vue-demi: true
18
- better-sqlite3: true
19
- esbuild: true
20
- core-js: false
12
+ shellEmulator: true
13
+
14
+ packages:
15
+ - .
21
16
 
17
+ # 构建与浏览器依赖的安全修复;版本与框架工作区同步维护。
22
18
  overrides:
23
- "@ai-sdk/provider-utils@>=4.0.0 <4.0.33": 4.0.57
19
+ '@ai-sdk/provider-utils@>=4.0.0 <4.0.33': 4.0.57
20
+ '@types/node': {{versions.nodeTypes}}
21
+ body-parser@>=2 <2.4.0: 2.4.0
24
22
  cookie@<0.7.0: 0.7.2
23
+ dompurify@>=3 <3.4.17: 3.4.17
25
24
  esbuild@<0.28.1: 0.28.2
25
+ fast-uri@>=3 <3.1.9: 3.1.9
26
+ follow-redirects@>=1.16.0 <1.16.2: 1.16.2
27
+ glob@>=9 <=14.0.1: 14.0.4
28
+ ip-address@<=10.7.3: 10.7.6
26
29
  katex@<0.18.2: 0.18.10
30
+ minimatch@>=7 <11.0.1: 11.0.5
31
+ parse5@>=8 <8.1.0: 8.1.0
32
+ qs@>=6.7 <6.16.1: 6.17.1
33
+ runed@<0.38.0: 0.38.0
27
34
  sharp@<0.35.5: 0.35.5
28
35
  tinypool@<2.1.2: 2.2.0
29
- '@qdrant/js-client-rest>undici': 7.30.0
30
36
  xcode>uuid: 11.1.1
31
- "@types/node": "{{versions.nodeTypes}}"
32
37
 
33
- packages:
34
- - .
38
+ {{#if hasUi}}
39
+ patchedDependencies:
40
+ daisyui@5.7.50: patches/daisyui-5.7.50.patch
41
+ deepmerge@4.3.1: patches/deepmerge-4.3.1.patch
42
+ {{/if}}
43
+
44
+ allowBuilds:
45
+ core-js: false
46
+ cpu-features: true
47
+ esbuild: true
48
+ onnxruntime-node: true
49
+ protobufjs: true
50
+ ssh2: true
51
+ vue-demi: true
35
52
 
36
53
  catalog:
37
54
  {{#each haiCatalogPackages}}
38
- '{{@key}}': {{this}}
55
+ {{{yaml @key this}}}
39
56
  {{/each}}
40
57
  {{/if}}
@@ -27,5 +27,5 @@ declare global {
27
27
  }
28
28
  }
29
29
 
30
- export {}
30
+ export { }
31
31
  {{/unless}}
@@ -3,28 +3,22 @@
3
3
  * {{projectName}} - Server Hooks
4
4
  * =============================================================================
5
5
  */
6
-
7
6
  {{#if features.crypto}}
8
7
  import type { TransportEncryptionOptions } from '@h-ai/kit'
9
8
  {{/if}}
10
- import type { Handle } from '@sveltejs/kit'
9
+ import type { Handle } from '@sveltejs/kit/hooks'
11
10
  import process from 'node:process'
12
11
  {{#if hasI18n}}
13
- import { base } from '$app/paths'
12
+ import { resolve as resolvePath } from '$app/paths'
14
13
  {{else}}
15
14
  {{#if features.iam}}
16
- import { base } from '$app/paths'
15
+ import { resolve as resolvePath } from '$app/paths'
17
16
  {{else}}
18
17
  {{#if features.crypto}}
19
- import { base } from '$app/paths'
20
- {{/if}}
18
+ import { resolve as resolvePath } from '$app/paths'
21
19
  {{/if}}
22
20
  {{/if}}
23
- {{#if hasI18n}}
24
- import { paraglideMiddleware } from '$lib/paraglide/server.js'
25
21
  {{/if}}
26
- import { closeApp, initApp } from '$lib/server/init.js'
27
- import { getKitConfig } from '$lib/server/kit-config.js'
28
22
  {{#if features.cache}}
29
23
  import { cache } from '@h-ai/cache'
30
24
  {{/if}}
@@ -41,6 +35,11 @@ import { kit } from '@h-ai/kit'
41
35
  import { reldb } from '@h-ai/reldb'
42
36
  {{/if}}
43
37
  {{/if}}
38
+ {{#if hasI18n}}
39
+ import { paraglideMiddleware } from '#lib/paraglide/server.js'
40
+ {{/if}}
41
+ import { closeApp, initApp } from '#lib/server/init.js'
42
+ import { getKitConfig } from '#lib/server/kit-config.js'
44
43
 
45
44
  // =============================================================================
46
45
  // 应用初始化
@@ -74,7 +73,6 @@ const initHandle: Handle = async ({ event, resolve }) => {
74
73
  await ensureAppInitialized()
75
74
  return resolve(event)
76
75
  }
77
-
78
76
  {{#if hasI18n}}
79
77
  // =============================================================================
80
78
  // Paraglide i18n Middleware
@@ -86,7 +84,7 @@ const initHandle: Handle = async ({ event, resolve }) => {
86
84
  * API 请求跳过 paraglideMiddleware(不需要页面级 locale + 避免消耗 body)
87
85
  */
88
86
  const i18nHandle: Handle = async ({ event, resolve }) => {
89
- if (event.url.pathname.startsWith(`${base}/api/`)) {
87
+ if (event.url.pathname.startsWith(`${resolvePath('/').replace(/\/$/, '')}/api/`)) {
90
88
  const locale = event.cookies.get('PARAGLIDE_LOCALE') ?? '{{defaultLocale}}'
91
89
  event.locals.locale = locale
92
90
  return kit.i18n.runWithLocale(locale, () => resolve(event))
@@ -99,7 +97,6 @@ const i18nHandle: Handle = async ({ event, resolve }) => {
99
97
  }))
100
98
  })
101
99
  }
102
-
103
100
  {{/if}}
104
101
  // =============================================================================
105
102
  // hai Handle(会话验证 + 限流 + 日志)
@@ -113,20 +110,20 @@ const haiHandle: Handle = async (input) => {
113
110
  let transport: TransportEncryptionOptions | undefined
114
111
  if (settings.transport !== false) {
115
112
  {{#unless features.db}}
116
- if (core.module.requiresSharedState(process.env))
117
- throw new Error('Production transport requires the db feature and a shared database registry')
113
+ if (core.module.requiresSharedState(process.env))
114
+ throw new Error('Production transport requires the db feature and a shared database registry')
118
115
  {{/unless}}
119
116
  const ring = parseTransportServerKeyRing(process.env.HAI_CRYPTO_TRANSPORT_SERVERKEYRING)
120
117
  if (!ring.success)
121
118
  throw new Error(`HAI_CRYPTO_TRANSPORT_SERVERKEYRING: ${ring.error.message} (${ring.error.code})`)
122
119
  transport = {
123
120
  ...settings.transport,
124
- basePath: base,
121
+ basePath: resolvePath('/').replace(/\/$/, ''),
125
122
  serverKeyRing: ring.data,
126
123
  {{#if features.db}}
127
124
  keyStore: createReldbTransportKeyStore({ reldb, ttlSeconds: 3600 }),
128
125
  {{else}}
129
- keyStore: createInMemoryKeyStore(),
126
+ keyStore: createInMemoryKeyStore(),
130
127
  {{/if}}
131
128
  }
132
129
  }
@@ -140,7 +137,7 @@ const haiHandle: Handle = async (input) => {
140
137
  {{#if features.crypto}}
141
138
  crypto: { crypto, transport },
142
139
  {{/if}}
143
- {{#if features.iam}}
140
+ {{#if features.iam}}
144
141
  auth: {
145
142
  verifyToken: async (token) => {
146
143
  const result = await iam.auth.verifyToken(token)
@@ -156,18 +153,27 @@ const haiHandle: Handle = async (input) => {
156
153
  data: session.data,
157
154
  }
158
155
  },
159
- loginUrl: `${base}/auth/login`,
160
- protectedPaths: ['/admin/*', '/api/*'].map(path => `${base}${path}`),
161
- publicPaths: [...kit.auth.publicPaths, '/api/auth/logout', '/api/public/*', '/api/health'].map(path => `${base}${path}`),
156
+ loginUrl: `${resolvePath('/').replace(/\/$/, '')}/auth/login`,
157
+ protectedPaths: ['/admin/*', '/api/*'].map(path => `${resolvePath('/').replace(/\/$/, '')}${path}`),
158
+ publicPaths: [
159
+ ...kit.auth.publicPaths,
160
+ '/api/auth/logout',
161
+ '/api/public/*',
162
+ '/api/health',
163
+ ].map(path => `${resolvePath('/').replace(/\/$/, '')}${path}`),
162
164
  operations: () => iam.auth,
163
165
  },
164
- {{/if}}
165
- {{#if features.cache}}
166
- rateLimit: { windowMs: 60000, maxRequests: 200, store: kit.middleware.createCacheRateLimitStore(cache, { namespace: '{{projectName}}', allowMemory: process.env.NODE_ENV !== 'production' || process.env.HAI_E2E === '1' }) },
167
- {{else}}
166
+ {{/if}}
167
+ {{#if features.cache}}
168
+ rateLimit: {
169
+ windowMs: 60000,
170
+ maxRequests: 200,
171
+ store: kit.middleware.createCacheRateLimitStore(cache, { namespace: '{{projectName}}', allowMemory: process.env.NODE_ENV !== 'production' || process.env.HAI_E2E === '1' }),
172
+ },
173
+ {{else}}
168
174
  // 未选择cache能力时不启用限流;启用前须接入共享原子store。
169
175
  rateLimit: false,
170
- {{/if}}
176
+ {{/if}}
171
177
  logging: { logBody: false },
172
178
  onError: (error: unknown, _event) => {
173
179
  core.logger.error('Request error:', { error })
@@ -176,7 +182,10 @@ const haiHandle: Handle = async (input) => {
176
182
  success: false,
177
183
  error: core.error.toPublic({ code: 'INTERNAL_ERROR', message: '', httpStatus: 500 }),
178
184
  }),
179
- { status: 500, headers: { 'Content-Type': 'application/json' } },
185
+ {
186
+ status: 500,
187
+ headers: { 'Content-Type': 'application/json' },
188
+ },
180
189
  )
181
190
  },
182
191
  })
@@ -2,6 +2,5 @@
2
2
  {{#if features.feedback}}
3
3
  /** 当前应用编译时烘焙的产物身份;运行时配置不能覆盖。 */
4
4
  declare const __HAI_BUILD_METADATA__: { appVersion: string, buildId: string, commit: string }
5
-
6
5
  export const buildMetadata = __HAI_BUILD_METADATA__
7
6
  {{/if}}