@h-ai/cli 0.1.0-alpha.19 → 0.1.0-alpha.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.
Files changed (219) hide show
  1. package/README.md +10 -7
  2. package/dist/{chunk-GSMXGUYF.js → chunk-5IGMJVV5.js} +56 -38
  3. package/dist/chunk-5IGMJVV5.js.map +1 -0
  4. package/dist/{chunk-BPO34NRL.js → chunk-CFCXOKGV.js} +27 -5
  5. package/dist/chunk-CFCXOKGV.js.map +1 -0
  6. package/dist/cli-config-templates-5J5VKWWD.js +5 -0
  7. package/dist/{cli-config-templates-JCS7T3NU.js.map → cli-config-templates-5J5VKWWD.js.map} +1 -1
  8. package/dist/cli-skill-templates-JMSV2SVW.js +5 -0
  9. package/dist/{cli-skill-templates-IMYZPQRG.js.map → cli-skill-templates-JMSV2SVW.js.map} +1 -1
  10. package/dist/index.js +627 -107
  11. package/dist/index.js.map +1 -1
  12. package/dist/{src-N4NPS7AG.js → src-QNEJJ3BV.js} +261 -103
  13. package/dist/src-QNEJJ3BV.js.map +1 -0
  14. package/package.json +3 -2
  15. package/templates/README.md +40 -29
  16. package/templates/apps/admin/messages/en-US.json +5 -0
  17. package/templates/apps/admin/messages/zh-CN.json +5 -0
  18. package/templates/apps/admin/src/routes/+layout.svelte.hbs +5 -5
  19. package/templates/apps/admin/src/routes/+page.svelte.hbs +32 -12
  20. package/templates/apps/admin/src/routes/admin/+layout.svelte.hbs +37 -20
  21. package/templates/apps/admin/src/routes/admin/+page.svelte.hbs +33 -24
  22. package/templates/apps/admin/src/routes/admin/settings/+page.svelte.hbs +16 -13
  23. package/templates/apps/api/apps/{{projectName}}-contract/README.md.hbs +16 -0
  24. package/templates/apps/api/apps/{{projectName}}-contract/package.json.hbs +38 -0
  25. package/templates/apps/api/apps/{{projectName}}-contract/src/app-contract.ts.hbs +27 -0
  26. package/templates/apps/api/apps/{{projectName}}-contract/src/app-schemas.ts.hbs +33 -0
  27. package/templates/apps/api/apps/{{projectName}}-contract/src/index.ts.hbs +3 -0
  28. package/templates/apps/api/apps/{{projectName}}-contract/src/{{projectName}}-contract.ts.hbs +12 -0
  29. package/templates/apps/api/apps/{{projectName}}-contract/tests/contract.test.ts.hbs +18 -0
  30. package/templates/apps/api/apps/{{projectName}}-contract/tsconfig.json.hbs +12 -0
  31. package/templates/apps/api/apps/{{projectName}}-contract/tsup.config.ts.hbs +11 -0
  32. package/templates/apps/api/apps/{{projectName}}-contract/vitest.config.ts.hbs +9 -0
  33. package/templates/apps/api/apps/{{projectName}}-service/README.md.hbs +17 -0
  34. package/templates/apps/api/apps/{{projectName}}-service/config/_core.yml.hbs +19 -0
  35. package/templates/apps/api/apps/{{projectName}}-service/config/_serv.yml.hbs +26 -0
  36. package/templates/apps/api/apps/{{projectName}}-service/package.json.hbs +55 -0
  37. package/templates/apps/api/apps/{{projectName}}-service/src/app.ts.hbs +98 -0
  38. package/templates/apps/api/apps/{{projectName}}-service/src/index.ts.hbs +21 -0
  39. package/templates/apps/api/apps/{{projectName}}-service/src/lib/server/init.ts.hbs +184 -0
  40. package/templates/apps/api/apps/{{projectName}}-service/src/server/procedures/app-procedures.ts.hbs +49 -0
  41. package/templates/apps/api/apps/{{projectName}}-service/src/server/procedures/index.ts.hbs +18 -0
  42. package/templates/apps/api/apps/{{projectName}}-service/tests/service.test.ts.hbs +168 -0
  43. package/templates/apps/api/apps/{{projectName}}-service/tsconfig.json.hbs +16 -0
  44. package/templates/apps/api/apps/{{projectName}}-service/tsup.config.ts.hbs +11 -0
  45. package/templates/apps/api/apps/{{projectName}}-service/vitest.config.ts.hbs +9 -0
  46. package/templates/apps/api/e2e/api-service.spec.ts.hbs +18 -0
  47. package/templates/apps/api/eslint.config.js +32 -0
  48. package/templates/apps/api/package.json.hbs +29 -0
  49. package/templates/apps/api/playwright.config.ts.hbs +58 -0
  50. package/templates/apps/api/pnpm-workspace.yaml.hbs +14 -0
  51. package/templates/apps/api/tsconfig.base.json +15 -0
  52. package/templates/apps/fullstack/apps/{{projectName}}-app/.env.example.hbs +10 -0
  53. package/templates/apps/fullstack/apps/{{projectName}}-app/README.md.hbs +24 -7
  54. package/templates/apps/fullstack/apps/{{projectName}}-app/capacitor.config.ts.hbs +18 -0
  55. package/templates/apps/fullstack/apps/{{projectName}}-app/eslint.config.js.hbs +1 -1
  56. package/templates/apps/fullstack/apps/{{projectName}}-app/index.html.hbs +16 -0
  57. package/templates/apps/fullstack/apps/{{projectName}}-app/messages/en-US.json.hbs +6 -0
  58. package/templates/apps/fullstack/apps/{{projectName}}-app/messages/zh-CN.json.hbs +6 -0
  59. package/templates/apps/fullstack/apps/{{projectName}}-app/package.json.hbs +51 -29
  60. package/templates/apps/fullstack/apps/{{projectName}}-app/src/App.svelte.hbs +186 -0
  61. package/templates/apps/fullstack/apps/{{projectName}}-app/src/app.css.hbs +2 -0
  62. package/templates/apps/fullstack/apps/{{projectName}}-app/src/app.d.ts.hbs +0 -1
  63. package/templates/apps/fullstack/apps/{{projectName}}-app/src/lib/capacitor.ts.hbs +18 -0
  64. package/templates/apps/fullstack/apps/{{projectName}}-app/src/main.ts.hbs +14 -0
  65. package/templates/apps/fullstack/apps/{{projectName}}-app/svelte.config.js.hbs +1 -5
  66. package/templates/apps/fullstack/apps/{{projectName}}-app/tsconfig.json.hbs +10 -4
  67. package/templates/apps/fullstack/apps/{{projectName}}-app/vite.config.ts.hbs +4 -3
  68. package/templates/apps/fullstack/apps/{{projectName}}-desktop/.env.example.hbs +6 -0
  69. package/templates/apps/fullstack/apps/{{projectName}}-desktop/README.md.hbs +20 -6
  70. package/templates/apps/fullstack/apps/{{projectName}}-desktop/eslint.config.js.hbs +1 -1
  71. package/templates/apps/fullstack/apps/{{projectName}}-desktop/index.html.hbs +15 -0
  72. package/templates/apps/fullstack/apps/{{projectName}}-desktop/package.json.hbs +33 -30
  73. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/App.svelte.hbs +152 -0
  74. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/app.d.ts.hbs +1 -2
  75. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/main.ts.hbs +13 -0
  76. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/Cargo.toml.hbs +22 -0
  77. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/build.rs.hbs +6 -0
  78. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/capabilities/default.json.hbs +12 -0
  79. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/icons/128x128.png +0 -0
  80. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/icons/128x128@2x.png +0 -0
  81. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/icons/32x32.png +0 -0
  82. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/icons/icon.icns +0 -0
  83. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/icons/icon.ico +0 -0
  84. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/src/lib.rs.hbs +16 -0
  85. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/src/main.rs.hbs +8 -0
  86. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/tauri.conf.json.hbs +41 -0
  87. package/templates/apps/fullstack/apps/{{projectName}}-desktop/svelte.config.js.hbs +2 -8
  88. package/templates/apps/fullstack/apps/{{projectName}}-desktop/tsconfig.json.hbs +9 -4
  89. package/templates/apps/fullstack/apps/{{projectName}}-desktop/vite.config.ts.hbs +44 -21
  90. package/templates/apps/fullstack/apps/{{projectName}}-web/README.md.hbs +15 -6
  91. package/templates/apps/fullstack/apps/{{projectName}}-web/eslint.config.js.hbs +1 -1
  92. package/templates/apps/fullstack/apps/{{projectName}}-web/index.html.hbs +15 -0
  93. package/templates/apps/fullstack/apps/{{projectName}}-web/package.json.hbs +25 -27
  94. package/templates/apps/fullstack/apps/{{projectName}}-web/src/App.svelte.hbs +153 -0
  95. package/templates/apps/fullstack/apps/{{projectName}}-web/src/app.d.ts.hbs +0 -1
  96. package/templates/apps/fullstack/apps/{{projectName}}-web/src/main.ts.hbs +14 -0
  97. package/templates/apps/fullstack/apps/{{projectName}}-web/svelte.config.js.hbs +1 -5
  98. package/templates/apps/fullstack/apps/{{projectName}}-web/tsconfig.json.hbs +10 -4
  99. package/templates/apps/fullstack/apps/{{projectName}}-web/vite.config.ts.hbs +3 -2
  100. package/templates/apps/fullstack/e2e/fullstack.spec.ts.hbs +2 -1
  101. package/templates/apps/fullstack/eslint.config.js +0 -1
  102. package/templates/apps/fullstack/package.json.hbs +18 -17
  103. package/templates/apps/fullstack/packages/{{projectName}}-contract/package.json.hbs +3 -3
  104. package/templates/apps/fullstack/packages/{{projectName}}-serv/README.md.hbs +5 -2
  105. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_core.yml.hbs +14 -0
  106. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_serv.yml.hbs +13 -0
  107. package/templates/apps/fullstack/packages/{{projectName}}-serv/package.json.hbs +2 -2
  108. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/{server-app.ts.hbs → app-server.ts.hbs} +23 -3
  109. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/index.ts.hbs +4 -1
  110. package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/{server-app.test.ts.hbs → app-server.test.ts.hbs} +1 -1
  111. package/templates/apps/fullstack/packages/{{projectName}}-shared/package.json.hbs +9 -8
  112. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/index.ts.hbs +1 -1
  113. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/api/api-client.ts.hbs +5 -1
  114. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/ThemeSwitcher.svelte.hbs +1 -1
  115. package/templates/apps/fullstack/packages/{{projectName}}-shared/tsconfig.json.hbs +1 -1
  116. package/templates/apps/fullstack/playwright.config.ts.hbs +41 -4
  117. package/templates/apps/fullstack/pnpm-workspace.yaml.hbs +15 -0
  118. package/templates/apps/h5/messages/en-US.json +3 -0
  119. package/templates/apps/h5/messages/zh-CN.json +3 -0
  120. package/templates/apps/h5/src/routes/+layout.svelte.hbs +30 -29
  121. package/templates/apps/h5/src/routes/+page.svelte.hbs +17 -6
  122. package/templates/apps/h5/src/routes/discover/+page.svelte.hbs +17 -6
  123. package/templates/apps/h5/src/routes/profile/+page.svelte.hbs +17 -6
  124. package/templates/apps/mobile-app/.env.hbs +3 -0
  125. package/templates/apps/{android-app → mobile-app}/capacitor.config.ts.hbs +2 -2
  126. package/templates/apps/mobile-app/index.html.hbs +13 -0
  127. package/templates/apps/mobile-app/messages/en-US.json +36 -0
  128. package/templates/apps/mobile-app/messages/zh-CN.json +36 -0
  129. package/templates/apps/mobile-app/src/App.svelte.hbs +162 -0
  130. package/templates/apps/mobile-app/src/lib/api.ts.hbs +54 -0
  131. package/templates/apps/{android-app → mobile-app}/src/lib/capacitor.ts +6 -7
  132. package/templates/apps/mobile-app/src/main.ts.hbs +11 -0
  133. package/templates/apps/website/src/routes/+layout.svelte.hbs +35 -26
  134. package/templates/apps/website/src/routes/+page.svelte.hbs +28 -10
  135. package/templates/apps/website/src/routes/about/+page.svelte.hbs +11 -6
  136. package/templates/apps/website/src/routes/contact/+page.svelte.hbs +37 -34
  137. package/templates/apps/website/src/routes/services/+page.svelte.hbs +27 -40
  138. package/templates/base/.gitignore.hbs +16 -0
  139. package/templates/base/{eslint.config.js → eslint.config.js.hbs} +4 -1
  140. package/templates/base/package.json.hbs +55 -31
  141. package/templates/base/playwright.config.ts +30 -3
  142. package/templates/base/pnpm-workspace.yaml.hbs +14 -0
  143. package/templates/base/src/app.d.ts.hbs +3 -0
  144. package/templates/base/src/app.html.hbs +7 -0
  145. package/templates/base/src/hooks.server.ts.hbs +17 -7
  146. package/templates/base/src/lib/server/init.ts.hbs +20 -12
  147. package/templates/base/static/favicon.png +0 -0
  148. package/templates/base/svelte.config.js.hbs +43 -3
  149. package/templates/base/tests/scaffold.test.ts.hbs +16 -1
  150. package/templates/base/tsconfig.json.hbs +50 -0
  151. package/templates/base/vite.config.ts.hbs +13 -3
  152. package/templates/features/iam/routes-admin/(auth)/+layout.svelte.hbs +5 -5
  153. package/templates/features/iam/routes-admin/(auth)/auth/login/+page.svelte.hbs +34 -32
  154. package/templates/features/iam/routes-admin/(auth)/auth/register/+page.svelte.hbs +46 -48
  155. package/templates/features/iam/routes-h5/auth/login/+page.svelte.hbs +35 -31
  156. package/templates/features/iam/routes-h5/auth/register/+page.svelte.hbs +41 -38
  157. package/templates/skills/AGENTS.md +60 -17
  158. package/templates/skills/CLAUDE.md +18 -6
  159. package/templates/skills/bridges/admin/AGENTS.md +66 -0
  160. package/templates/skills/bridges/admin/CLAUDE.md +19 -0
  161. package/templates/skills/bridges/admin/copilot-instructions.md +46 -0
  162. package/templates/skills/bridges/api/AGENTS.md +65 -0
  163. package/templates/skills/bridges/api/CLAUDE.md +19 -0
  164. package/templates/skills/bridges/api/copilot-instructions.md +46 -0
  165. package/templates/skills/bridges/fullstack/AGENTS.md +84 -0
  166. package/templates/skills/bridges/fullstack/CLAUDE.md +19 -0
  167. package/templates/skills/bridges/fullstack/copilot-instructions.md +54 -0
  168. package/templates/skills/bridges/h5/AGENTS.md +65 -0
  169. package/templates/skills/bridges/h5/CLAUDE.md +19 -0
  170. package/templates/skills/bridges/h5/copilot-instructions.md +44 -0
  171. package/templates/skills/bridges/mobile-app/AGENTS.md +77 -0
  172. package/templates/skills/bridges/mobile-app/CLAUDE.md +19 -0
  173. package/templates/skills/bridges/mobile-app/copilot-instructions.md +45 -0
  174. package/templates/skills/bridges/website/AGENTS.md +65 -0
  175. package/templates/skills/bridges/website/CLAUDE.md +19 -0
  176. package/templates/skills/bridges/website/copilot-instructions.md +44 -0
  177. package/templates/skills/copilot-instructions.md +29 -50
  178. package/templates/skills/hai-ai/SKILL.md +115 -648
  179. package/templates/skills/hai-ai/reference.md +197 -0
  180. package/templates/skills/hai-api-client/SKILL.md +3 -1
  181. package/templates/skills/hai-app-create/SKILL.md +2 -1
  182. package/templates/skills/hai-app-review/SKILL.md +2 -2
  183. package/templates/skills/hai-app-tests/SKILL.md +28 -3
  184. package/templates/skills/hai-capacitor/SKILL.md +14 -22
  185. package/templates/skills/hai-deploy/SKILL.md +12 -9
  186. package/templates/skills/hai-kit/SKILL.md +5 -3
  187. package/templates/skills/hai-serv/SKILL.md +2 -0
  188. package/templates/skills/hai-ui/SKILL.md +25 -11
  189. package/templates/skills/opencode.json +1 -6
  190. package/dist/chunk-BPO34NRL.js.map +0 -1
  191. package/dist/chunk-GSMXGUYF.js.map +0 -1
  192. package/dist/cli-config-templates-JCS7T3NU.js +0 -5
  193. package/dist/cli-skill-templates-IMYZPQRG.js +0 -5
  194. package/dist/src-N4NPS7AG.js.map +0 -1
  195. package/templates/apps/android-app/.env.hbs +0 -3
  196. package/templates/apps/android-app/messages/en-US.json +0 -24
  197. package/templates/apps/android-app/messages/zh-CN.json +0 -24
  198. package/templates/apps/android-app/src/lib/api.ts.hbs +0 -31
  199. package/templates/apps/android-app/src/routes/+layout.svelte.hbs +0 -42
  200. package/templates/apps/android-app/src/routes/+layout.ts +0 -7
  201. package/templates/apps/android-app/src/routes/+page.svelte.hbs +0 -27
  202. package/templates/apps/api/src/routes/+layout.svelte +0 -14
  203. package/templates/apps/api/src/routes/+page.svelte +0 -11
  204. package/templates/apps/api/src/routes/api/v1/health/+server.ts +0 -14
  205. package/templates/apps/api/src/routes/api/v1/items/+server.ts +0 -37
  206. package/templates/apps/api/src/routes/api/v1/items/[id]/+server.ts +0 -30
  207. package/templates/apps/api/tests/health.test.ts.hbs +0 -26
  208. package/templates/apps/fullstack/apps/{{projectName}}-app/src/app.html.hbs +0 -14
  209. package/templates/apps/fullstack/apps/{{projectName}}-app/src/routes/+layout.svelte.hbs +0 -18
  210. package/templates/apps/fullstack/apps/{{projectName}}-app/src/routes/+page.svelte.hbs +0 -162
  211. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/app.html.hbs +0 -14
  212. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/routes/+layout.svelte.hbs +0 -18
  213. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/routes/+page.svelte.hbs +0 -162
  214. package/templates/apps/fullstack/apps/{{projectName}}-web/src/app.html.hbs +0 -14
  215. package/templates/apps/fullstack/apps/{{projectName}}-web/src/routes/+layout.svelte.hbs +0 -18
  216. package/templates/apps/fullstack/apps/{{projectName}}-web/src/routes/+page.svelte.hbs +0 -162
  217. package/templates/apps/fullstack/pnpm-workspace.yaml +0 -7
  218. package/templates/base/tsconfig.json +0 -20
  219. /package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/stores/{theme.svelte.ts.hbs → theme-store.svelte.ts.hbs} +0 -0
@@ -0,0 +1,168 @@
1
+ import type { AddressInfo } from 'node:net'
2
+ import { mkdtempSync, rmSync } from 'node:fs'
3
+ import { tmpdir } from 'node:os'
4
+ import { join } from 'node:path'
5
+ {{#if features.ai}}import { ai } from '@h-ai/ai'
6
+ {{/if}}import { apiClient } from '@h-ai/api-client'
7
+ {{#if features.cache}}import { cache } from '@h-ai/cache'
8
+ {{/if}}import { core } from '@h-ai/core'
9
+ {{#if features.iam}}import { iam } from '@h-ai/iam'
10
+ {{/if}}{{#if features.db}}import { reldb } from '@h-ai/reldb'
11
+ {{/if}}import { serv } from '@h-ai/serv'
12
+ {{#if features.storage}}import { storage } from '@h-ai/storage'
13
+ {{/if}}{{#if features.vecdb}}import { vecdb } from '@h-ai/vecdb'
14
+ {{/if}}import { {{api.contractExportName}} } from '{{api.contractPackageName}}'
15
+ import { afterAll, beforeAll, describe, expect, it } from 'vitest'
16
+
17
+ import { {{api.serviceAppFactoryName}} } from '../src/app.js'
18
+
19
+ let tempRoot = ''
20
+ let baseUrl = ''
21
+ let client: ReturnType<typeof apiClient.create<typeof {{api.contractExportName}}>>
22
+ let server: ReturnType<typeof serv.listen>
23
+ {{#if features.storage}}let storageRoot = ''
24
+ {{/if}}{{#if features.vecdb}}let vecdbRoot = ''
25
+ {{/if}}
26
+
27
+ {{#if features.iam}}
28
+ function uniqueUser(prefix = 'api') {
29
+ const suffix = `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 6)}`.slice(-8)
30
+ return {
31
+ username: `${prefix}_${suffix}`,
32
+ password: 'TestPass123',
33
+ email: `${prefix}_${suffix}@test.local`,
34
+ }
35
+ }
36
+ {{/if}}
37
+
38
+ beforeAll(async () => {
39
+ tempRoot = mkdtempSync(join(tmpdir(), '{{projectName}}-service-test-'))
40
+ {{#if features.storage}} storageRoot = join(tempRoot, 'storage')
41
+ {{/if}}{{#if features.vecdb}} vecdbRoot = join(tempRoot, 'vecdb')
42
+ {{/if}}
43
+
44
+ core.init({
45
+ configDir: './config',
46
+ logging: { level: 'warn' },
47
+ })
48
+ {{#if features.db}}
49
+
50
+ const dbResult = await reldb.init({ type: 'sqlite', database: ':memory:' })
51
+ if (!dbResult.success)
52
+ throw new Error(`reldb: ${dbResult.error.message}`)
53
+ {{/if}}{{#if features.cache}}
54
+
55
+ const cacheResult = await cache.init({ type: 'memory' })
56
+ if (!cacheResult.success)
57
+ throw new Error(`cache: ${cacheResult.error.message}`)
58
+ {{/if}}{{#if features.vecdb}}
59
+
60
+ const vecdbResult = await vecdb.init({ type: 'lancedb', path: vecdbRoot })
61
+ if (!vecdbResult.success)
62
+ throw new Error(`vecdb: ${vecdbResult.error.message}`)
63
+ {{/if}}{{#if features.iam}}
64
+
65
+ const iamResult = await iam.init({})
66
+ if (!iamResult.success)
67
+ throw new Error(`iam: ${iamResult.error.message}`)
68
+ {{/if}}{{#if features.storage}}
69
+
70
+ const storageResult = await storage.init({ type: 'local', root: storageRoot })
71
+ if (!storageResult.success)
72
+ throw new Error(`storage: ${storageResult.error.message}`)
73
+ {{/if}}{{#if features.ai}}
74
+
75
+ const aiResult = await ai.init({})
76
+ if (!aiResult.success)
77
+ throw new Error(`ai: ${aiResult.error.message}`)
78
+ {{/if}}
79
+
80
+ const app = {{api.serviceAppFactoryName}}({
81
+ transport: 'disabled',{{#if features.iam}}
82
+ refreshCookie: 'disabled',{{/if}}
83
+ })
84
+ const listening = new Promise<AddressInfo>((resolve) => {
85
+ server = serv.listen(app, {
86
+ host: '127.0.0.1',
87
+ port: 0,
88
+ onListening: info => resolve(info as AddressInfo),
89
+ })
90
+ })
91
+ const address = await listening
92
+ baseUrl = `http://127.0.0.1:${address.port}`
93
+
94
+ client = apiClient.create({{api.contractExportName}})
95
+ const initResult = await client.init({
96
+ baseUrl: `${baseUrl}/api/v1`,
97
+ auth: { storage: apiClient.tokenStorage.memory() },
98
+ })
99
+ if (!initResult.success)
100
+ throw new Error(`client.init: ${initResult.error.message}`)
101
+ }, 30_000)
102
+
103
+ afterAll(async () => {
104
+ await client?.close()
105
+ await server?.close()
106
+ {{#if features.ai}} await ai.close()
107
+ {{/if}}{{#if features.storage}} await storage.close()
108
+ {{/if}}{{#if features.iam}} await iam.close()
109
+ {{/if}}{{#if features.vecdb}} await vecdb.close()
110
+ {{/if}}{{#if features.cache}} await cache.close()
111
+ {{/if}}{{#if features.db}} await reldb.close()
112
+ {{/if}} rmSync(tempRoot, { recursive: true, force: true })
113
+ })
114
+
115
+ describe('{{api.servicePackageName}}', () => {
116
+ it('returns health status', async () => {
117
+ const response = await fetch(`${baseUrl}/health`)
118
+
119
+ expect(response.status).toBe(200)
120
+ await expect(response.json()).resolves.toEqual({ status: 'ok' })
121
+ })
122
+
123
+ it('returns app info as HaiResult', async () => {
124
+ const result = await client.app.info()
125
+
126
+ expect(result.success).toBe(true)
127
+ if (result.success) {
128
+ expect(result.data.name).toContain('{{projectName}}')
129
+ expect(typeof result.data.transportEnabled).toBe('boolean')
130
+ }
131
+ })
132
+
133
+ it('validates echo input', async () => {
134
+ const result = await client.app.echo({ message: '' })
135
+
136
+ expect(result.success).toBe(false)
137
+ })
138
+
139
+ {{#if features.iam}}
140
+ it('registers a user and returns authenticated echo context', async () => {
141
+ const user = uniqueUser('svc')
142
+ const register = await client.iam.auth.register({
143
+ username: user.username,
144
+ password: user.password,
145
+ email: user.email,
146
+ })
147
+
148
+ expect(register.success).toBe(true)
149
+
150
+ const result = await client.app.echo({ message: 'hello unit' })
151
+ expect(result.success).toBe(true)
152
+ if (result.success) {
153
+ expect(result.data.message).toBe('hello unit')
154
+ expect(result.data.userId).toBeTruthy()
155
+ }
156
+ })
157
+ {{else}}
158
+ it('returns echo result as HaiResult', async () => {
159
+ const result = await client.app.echo({ message: 'hello unit' })
160
+
161
+ expect(result.success).toBe(true)
162
+ if (result.success) {
163
+ expect(result.data.message).toBe('hello unit')
164
+ expect(result.data.userId).toBeUndefined()
165
+ }
166
+ })
167
+ {{/if}}
168
+ })
@@ -0,0 +1,16 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "moduleResolution": "bundler",
5
+ "types": ["node"],
6
+ "verbatimModuleSyntax": true,
7
+ "skipLibCheck": true
8
+ },
9
+ "include": [
10
+ "src/**/*.ts"
11
+ ],
12
+ "exclude": [
13
+ "node_modules",
14
+ "dist"
15
+ ]
16
+ }
@@ -0,0 +1,11 @@
1
+ import { defineConfig } from 'tsup'
2
+
3
+ export default defineConfig({
4
+ entry: ['src/index.ts'],
5
+ format: ['esm'],
6
+ target: 'node22',
7
+ sourcemap: true,
8
+ clean: true,
9
+ dts: false,
10
+ external: ['@lancedb/lancedb'],
11
+ })
@@ -0,0 +1,9 @@
1
+ import { defineConfig } from 'vitest/config'
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ globals: true,
6
+ environment: 'node',
7
+ include: ['tests/**/*.test.ts'],
8
+ },
9
+ })
@@ -0,0 +1,18 @@
1
+ import { expect, test } from '@playwright/test'
2
+
3
+ test('service health endpoint returns ok', async ({ request }) => {
4
+ const response = await request.get('/health')
5
+
6
+ expect(response.status()).toBe(200)
7
+ await expect(response.json()).resolves.toEqual({ status: 'ok' })
8
+ })
9
+
10
+ test('service app.info endpoint returns HaiResult', async ({ request }) => {
11
+ const response = await request.post('/api/v1/app/info')
12
+ const body = await response.json()
13
+
14
+ expect(response.status()).toBe(200)
15
+ expect(body.success).toBe(true)
16
+ expect(body.data.name).toContain('{{projectName}}')
17
+ expect(typeof body.data.transportEnabled).toBe('boolean')
18
+ })
@@ -0,0 +1,32 @@
1
+ import antfu from '@antfu/eslint-config'
2
+
3
+ export default antfu({
4
+ formatters: true,
5
+ ignores: [
6
+ '**/dist/**',
7
+ '**/node_modules/**',
8
+ '**/.agents/**',
9
+ '**/.github/**',
10
+ '**/AGENTS.md',
11
+ '**/CLAUDE.md',
12
+ '**/README.md',
13
+ '**/opencode.json',
14
+ '**/package.json',
15
+ ],
16
+ }, {
17
+ files: ['**/*.js', '**/*.mjs', '**/*.ts'],
18
+ rules: {
19
+ 'no-console': 'error',
20
+ 'ts/no-explicit-any': 'error',
21
+ },
22
+ }, {
23
+ files: ['**/tsconfig.json', '**/tsconfig.*.json'],
24
+ rules: {
25
+ 'jsonc/sort-keys': 'off',
26
+ },
27
+ }, {
28
+ files: ['**/index.ts'],
29
+ rules: {
30
+ 'perfectionist/sort-exports': 'off',
31
+ },
32
+ })
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "{{projectName}}",
3
+ "type": "module",
4
+ "version": "0.1.0",
5
+ "private": true,
6
+ "packageManager": "pnpm@9.15.0",
7
+ "description": "Typed hai API workspace with separated contract and service packages.",
8
+ "scripts": {
9
+ "dev": "pnpm --filter {{api.servicePackageName}} dev",
10
+ "compile": "pnpm typecheck && pnpm build",
11
+ "build": "pnpm --filter {{api.contractPackageName}} build && pnpm -r --if-present build",
12
+ "typecheck": "pnpm --filter {{api.contractPackageName}} build && pnpm -r --if-present typecheck",
13
+ "lint": "pnpm -r --if-present lint",
14
+ "test": "pnpm --filter {{api.contractPackageName}} build && pnpm -r --if-present test",
15
+ "test:e2e": "pnpm build && playwright test",
16
+ "clean": "pnpm -r --if-present clean && rimraf node_modules .turbo"
17
+ },
18
+ "devDependencies": {
19
+ "@antfu/eslint-config": "{{api.packageSpecifiers.antfuEslintConfig}}",
20
+ "@playwright/test": "{{api.packageSpecifiers.playwright}}",
21
+ "@types/node": "{{api.packageSpecifiers.nodeTypes}}",
22
+ "eslint": "{{api.packageSpecifiers.eslint}}",
23
+ "eslint-plugin-format": "{{api.packageSpecifiers.eslintPluginFormat}}",
24
+ "rimraf": "{{api.packageSpecifiers.rimraf}}",
25
+ "tsup": "{{api.packageSpecifiers.tsup}}",
26
+ "typescript": "{{api.packageSpecifiers.typescript}}",
27
+ "vitest": "{{api.packageSpecifiers.vitest}}"
28
+ }
29
+ }
@@ -0,0 +1,58 @@
1
+ import process from 'node:process'
2
+ import { defineConfig } from '@playwright/test'
3
+
4
+ const defaultBaseURL = 'http://127.0.0.1:3000'
5
+ const baseURL = process.env.BASE_URL || defaultBaseURL
6
+ const serviceHost = resolveHostFromBaseUrl(baseURL, '127.0.0.1')
7
+ const servicePort = resolvePortFromBaseUrl(baseURL, '3000')
8
+
9
+ function resolveHostFromBaseUrl(urlString: string, fallbackHost: string): string {
10
+ try {
11
+ const url = new URL(urlString)
12
+ return url.hostname || fallbackHost
13
+ }
14
+ catch {
15
+ return fallbackHost
16
+ }
17
+ }
18
+
19
+ function resolvePortFromBaseUrl(urlString: string, fallbackPort: string): string {
20
+ try {
21
+ const url = new URL(urlString)
22
+ if (url.port) {
23
+ return url.port
24
+ }
25
+
26
+ return url.protocol === 'https:' ? '443' : '80'
27
+ }
28
+ catch {
29
+ return fallbackPort
30
+ }
31
+ }
32
+
33
+ export default defineConfig({
34
+ testDir: './e2e',
35
+ fullyParallel: false,
36
+ forbidOnly: !!process.env.CI,
37
+ retries: process.env.CI ? 2 : 0,
38
+ workers: 1,
39
+ reporter: 'list',
40
+ timeout: 30_000,
41
+ use: {
42
+ baseURL,
43
+ channel: 'chrome',
44
+ trace: 'on-first-retry',
45
+ screenshot: 'only-on-failure',
46
+ },
47
+ webServer: [{
48
+ name: '{{api.servicePackageName}}',
49
+ command: 'pnpm --filter {{api.servicePackageName}} start',
50
+ env: {
51
+ HOST: serviceHost,
52
+ PORT: servicePort,
53
+ },
54
+ url: `${baseURL}/health`,
55
+ reuseExistingServer: false,
56
+ timeout: 120_000,
57
+ }],
58
+ })
@@ -0,0 +1,14 @@
1
+ {{! @skipIfEmpty }}
2
+ shellEmulator: true
3
+
4
+ trustPolicy: no-downgrade
5
+
6
+ packages:
7
+ - apps/*
8
+
9
+ {{#if useCatalogProtocol}}
10
+ catalog:
11
+ {{#each haiCatalogPackages}}
12
+ '{{@key}}': {{this}}
13
+ {{/each}}
14
+ {{/if}}
@@ -0,0 +1,15 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "ignoreDeprecations": "6.0",
7
+ "resolveJsonModule": true,
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "forceConsistentCasingInFileNames": true,
11
+ "isolatedModules": true,
12
+ "verbatimModuleSyntax": true,
13
+ "skipLibCheck": true
14
+ }
15
+ }
@@ -0,0 +1,10 @@
1
+ {{! @skipIfEmpty }}
2
+ {{#if fullstack.frontends.app}}
3
+ # {{projectName}}-app — 环境变量
4
+ # Android 模拟器访问宿主机后端时通常使用 http://10.0.2.2:3000。
5
+
6
+ PUBLIC_API_BASE=http://localhost:3000
7
+
8
+ # 仅用于 Capacitor live reload 前端页面。
9
+ # CAPACITOR_SERVER_URL=http://localhost:{{fullstack.ports.app}}
10
+ {{/if}}
@@ -2,20 +2,37 @@
2
2
  {{#if fullstack.frontends.app}}
3
3
  # {{projectName}}-app
4
4
 
5
- App/H5 前端工程,使用 `@h-ai/api-client` 绑定共享 contract 调用后端 API,并通过 `@h-ai/ui` 组件搭建页面。
5
+ App/H5 前端工程,参考 `apps/mobile-app` 的移动端 UI 风格,使用 Svelte 5 + Vite + Capacitor,可输出 Web 预览并同步 Android / iOS 原生工程。
6
6
 
7
7
  ## i18n 与 shared 协同
8
8
 
9
9
  - 本端页面文案:`messages/{locale}.json` + `project.inlang/settings.json`
10
10
  - 共享 Shell/主题/语言/API:`packages/{{fullstack.sharedPackageName}}`
11
- - 语言切换由 shared 的 `LanguageSwitcher` 触发,并同步本端 `$lib/paraglide/runtime.js`
11
+ - 语言切换通过 shared runtime 与本端 `src/lib/paraglide/runtime.js` 同步。
12
+ - 设置 tab 默认集成 shared 的 `ThemeSwitcher` / `LanguageSwitcher`,可直接验证主题与语言同步。
12
13
 
13
14
  自定义建议:移动端独有引导、运营位、按钮文案放在本目录 `messages/`;跨端通用组件放到 shared。
14
15
 
15
- ## 开发
16
+ ## 编译、打包与部署
16
17
 
17
- - `pnpm i18n:compile`:在工作区根目录统一编译 shared + 各前端 Paraglide 输出
18
- - `pnpm --filter {{projectName}}-app dev`,默认端口 4174
19
- - `pnpm --filter {{projectName}}-app paraglide:compile`:只编译本端 messages
20
- - `pnpm --filter {{projectName}}-app test`
18
+ ```bash
19
+ # 开发预览,默认端口 {{fullstack.ports.app}}
20
+ pnpm --filter {{projectName}}-app dev
21
+
22
+ # 编译静态 SPA 产物到 dist/
23
+ pnpm --filter {{projectName}}-app build
24
+
25
+ # 同步原生工程;首次使用前按需执行 cap:add:android / cap:add:ios
26
+ pnpm --filter {{projectName}}-app package
27
+
28
+ # Android / iOS 原生打包
29
+ pnpm --filter {{projectName}}-app cap:build:android:debug
30
+ pnpm --filter {{projectName}}-app cap:build:android:release
31
+ pnpm --filter {{projectName}}-app cap:build:ios
32
+
33
+ # 当前模板的 deploy 入口默认执行 package,可按目标商店或 CI 平台替换
34
+ pnpm --filter {{projectName}}-app deploy
35
+ ```
36
+
37
+ 部署前确认 `PUBLIC_API_BASE` 在 `.env` 中指向可访问的后端服务。Android 模拟器访问宿主机通常使用 `http://10.0.2.2:3000`。
21
38
  {{/if}}
@@ -0,0 +1,18 @@
1
+ {{! @skipIfEmpty }}
2
+ {{#if fullstack.frontends.app}}
3
+ import type { CapacitorConfig } from '@capacitor/cli'
4
+ import process from 'node:process'
5
+
6
+ const config: CapacitorConfig = {
7
+ appId: 'com.hai.{{fullstack.nativeAppIdSegment}}.app',
8
+ appName: '{{projectName}} App',
9
+ webDir: 'dist',
10
+ server: {
11
+ // 仅用于 Capacitor live reload 前端页面;API 地址由前端配置或 shared client 参数控制。
12
+ url: process.env.CAPACITOR_SERVER_URL,
13
+ cleartext: process.env.CAPACITOR_SERVER_URL?.startsWith('http://') ?? false,
14
+ },
15
+ }
16
+
17
+ export default config
18
+ {{/if}}
@@ -4,7 +4,7 @@ import antfu from '@antfu/eslint-config'
4
4
 
5
5
  export default antfu({
6
6
  formatters: true,
7
- ignores: ['**/.svelte-kit/**', '**/build/**', '**/dist/**', '**/*.d.ts', '**/project.inlang/**', '**/src/lib/paraglide/**'],
7
+ ignores: ['**/build/**', '**/dist/**', '**/*.d.ts', '**/project.inlang/**', '**/src/lib/paraglide/**'],
8
8
  svelte: true,
9
9
  }, {
10
10
  files: ['**/*.ts', '**/*.svelte'],
@@ -0,0 +1,16 @@
1
+ {{! @skipIfEmpty }}
2
+ {{#if fullstack.frontends.app}}
3
+ <!doctype html>
4
+ <html lang="{{fullstack.i18n.baseLocale}}">
5
+ <head>
6
+ <meta charset="UTF-8" />
7
+ <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
8
+ <meta name="theme-color" content="#2563eb" />
9
+ <title>{{projectName}} App</title>
10
+ </head>
11
+ <body>
12
+ <div id="app"></div>
13
+ <script type="module" src="/src/main.ts"></script>
14
+ </body>
15
+ </html>
16
+ {{/if}}
@@ -4,6 +4,12 @@
4
4
  "$schema": "https://inlang.com/schema/inlang-message-format",
5
5
  "page_title": "{name} · App",
6
6
  "platform_name": "App/H5",
7
+ "nav_workspace": "Workspace",
8
+ "nav_status": "Status",
9
+ "nav_settings": "Settings",
10
+ "settings_description": "Switch theme and language here to verify shared theme and i18n state take effect instantly on mobile.",
11
+ "settings_theme_description": "Theme switching reuses the shared theme store and persists the preference on the device.",
12
+ "settings_language_description": "Language switching keeps the shared runtime and the app runtime in sync.",
7
13
  "hero_badge": "Mobile workspace",
8
14
  "hero_title": "{name} App Experience",
9
15
  "hero_subtitle": "A mobile and H5 oriented entry point. This app keeps its own messages so onboarding and campaign copy can evolve independently.",
@@ -4,6 +4,12 @@
4
4
  "$schema": "https://inlang.com/schema/inlang-message-format",
5
5
  "page_title": "{name} · App",
6
6
  "platform_name": "App/H5",
7
+ "nav_workspace": "工作台",
8
+ "nav_status": "状态",
9
+ "nav_settings": "设置",
10
+ "settings_description": "在这里切换主题与语言,验证 shared 的主题与 i18n 在移动端即时生效。",
11
+ "settings_theme_description": "主题切换复用 shared theme store,并将偏好持久化到本地设备。",
12
+ "settings_language_description": "语言切换会同时同步 shared runtime 与当前 App runtime。",
7
13
  "hero_badge": "移动端工作台",
8
14
  "hero_title": "{name} App 体验",
9
15
  "hero_subtitle": "面向移动端与 H5 场景的轻量入口,保留本端 messages 以便独立调整文案、引导语和运营内容。",
@@ -9,42 +9,64 @@
9
9
  "paraglide:compile": "paraglide-js compile --project ./project.inlang --outdir ./src/lib/paraglide",
10
10
  "paraglide:watch": "paraglide-js compile --project ./project.inlang --outdir ./src/lib/paraglide --watch",
11
11
  "dev": "vite dev --host 127.0.0.1 --port {{fullstack.ports.app}}",
12
- "build": "vite build",
12
+ "build": "pnpm paraglide:compile && vite build",
13
+ "package": "pnpm cap:sync",
14
+ "deploy": "pnpm package",
13
15
  "preview": "vite preview --host 127.0.0.1 --port {{fullstack.ports.app}} --strictPort",
14
- "typecheck": "pnpm paraglide:compile && svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
16
+ "typecheck": "pnpm paraglide:compile && svelte-check --tsconfig ./tsconfig.json",
15
17
  "lint": "eslint .",
16
- "test": "vitest run",
17
- "test:watch": "vitest",
18
- "clean": "rimraf .svelte-kit build .turbo src/lib/paraglide"
18
+ "test": "pnpm paraglide:compile && vitest run",
19
+ "test:watch": "pnpm paraglide:compile && vitest",
20
+ "clean": "rimraf dist build android ios .turbo src/lib/paraglide",
21
+ "cap:add:android": "cap add android",
22
+ "cap:add:ios": "cap add ios",
23
+ "cap:sync": "pnpm build && cap sync",
24
+ "cap:sync:android": "pnpm build && cap sync android",
25
+ "cap:sync:ios": "pnpm build && cap sync ios",
26
+ "cap:android": "cap open android",
27
+ "cap:ios": "cap open ios",
28
+ "cap:run:android": "cap run android",
29
+ "cap:run:ios": "cap run ios",
30
+ "cap:build:android:debug": "pnpm build && cap sync android && cd android && gradlew.bat assembleDebug",
31
+ "cap:build:android:release": "pnpm build && cap sync android && cap build android --androidreleasetype APK",
32
+ "cap:build:ios": "pnpm build && cap sync ios && cap build ios"
19
33
  },
20
34
  "dependencies": {
21
- "@h-ai/api-client": "{{fullstack.versions.apiClient}}",
22
- "@h-ai/ui": "{{fullstack.versions.ui}}",
23
- "{{fullstack.contractPackageName}}": "workspace:*",
35
+ "@h-ai/capacitor": "{{haiPackageSpecifier}}",
36
+ "@h-ai/ui": "{{haiPackageSpecifier}}",
24
37
  "{{fullstack.sharedPackageName}}": "workspace:*"
25
38
  },
26
39
  "devDependencies": {
27
- "@antfu/eslint-config": "{{fullstack.versions.antfuEslintConfig}}",
28
- "@iconify-json/tabler": "{{fullstack.versions.iconifyJsonTabler}}",
29
- "@iconify/tailwind4": "{{fullstack.versions.iconifyTailwind}}",
30
- "@inlang/paraglide-js": "{{fullstack.versions.inlangParaglide}}",
31
- "@inlang/plugin-message-format": "{{fullstack.versions.inlangMessageFormat}}",
32
- "@sveltejs/adapter-auto": "{{fullstack.versions.adapterAuto}}",
33
- "@sveltejs/kit": "{{fullstack.versions.svelteKit}}",
34
- "@sveltejs/vite-plugin-svelte": "{{fullstack.versions.sveltePlugin}}",
35
- "@tailwindcss/vite": "{{fullstack.versions.tailwindcssVite}}",
36
- "bits-ui": "{{fullstack.versions.bitsUi}}",
37
- "daisyui": "{{fullstack.versions.daisyui}}",
38
- "eslint": "{{fullstack.versions.eslint}}",
39
- "eslint-plugin-format": "{{fullstack.versions.eslintPluginFormat}}",
40
- "eslint-plugin-svelte": "{{fullstack.versions.eslintPluginSvelte}}",
41
- "svelte": "{{fullstack.versions.svelte}}",
42
- "svelte-check": "{{fullstack.versions.svelteCheck}}",
43
- "svelte-eslint-parser": "{{fullstack.versions.svelteEslintParser}}",
44
- "tailwindcss": "{{fullstack.versions.tailwindcss}}",
45
- "typescript": "{{fullstack.versions.typescript}}",
46
- "vite": "{{fullstack.versions.vite}}",
47
- "vitest": "{{fullstack.versions.vitest}}"
40
+ "@antfu/eslint-config": "{{fullstack.packageSpecifiers.antfuEslintConfig}}",
41
+ "@aparajita/capacitor-secure-storage": "{{fullstack.packageSpecifiers.capacitorSecureStorage}}",
42
+ "@capacitor/android": "{{fullstack.packageSpecifiers.capacitorAndroid}}",
43
+ "@capacitor/app": "{{fullstack.packageSpecifiers.capacitorApp}}",
44
+ "@capacitor/camera": "{{fullstack.packageSpecifiers.capacitorCamera}}",
45
+ "@capacitor/cli": "{{fullstack.packageSpecifiers.capacitorCli}}",
46
+ "@capacitor/core": "{{fullstack.packageSpecifiers.capacitorCore}}",
47
+ "@capacitor/device": "{{fullstack.packageSpecifiers.capacitorDevice}}",
48
+ "@capacitor/ios": "{{fullstack.packageSpecifiers.capacitorIos}}",
49
+ "@capacitor/preferences": "{{fullstack.packageSpecifiers.capacitorPreferences}}",
50
+ "@capacitor/push-notifications": "{{fullstack.packageSpecifiers.capacitorPushNotifications}}",
51
+ "@capacitor/status-bar": "{{fullstack.packageSpecifiers.capacitorStatusBar}}",
52
+ "@iconify-json/tabler": "{{fullstack.packageSpecifiers.iconifyJsonTabler}}",
53
+ "@iconify/tailwind4": "{{fullstack.packageSpecifiers.iconifyTailwind}}",
54
+ "@inlang/paraglide-js": "{{fullstack.packageSpecifiers.inlangParaglide}}",
55
+ "@inlang/plugin-message-format": "{{fullstack.packageSpecifiers.inlangMessageFormat}}",
56
+ "@sveltejs/vite-plugin-svelte": "{{fullstack.packageSpecifiers.sveltePlugin}}",
57
+ "@tailwindcss/vite": "{{fullstack.packageSpecifiers.tailwindcssVite}}",
58
+ "bits-ui": "{{fullstack.packageSpecifiers.bitsUi}}",
59
+ "daisyui": "{{fullstack.packageSpecifiers.daisyui}}",
60
+ "eslint": "{{fullstack.packageSpecifiers.eslint}}",
61
+ "eslint-plugin-format": "{{fullstack.packageSpecifiers.eslintPluginFormat}}",
62
+ "eslint-plugin-svelte": "{{fullstack.packageSpecifiers.eslintPluginSvelte}}",
63
+ "svelte": "{{fullstack.packageSpecifiers.svelte}}",
64
+ "svelte-check": "{{fullstack.packageSpecifiers.svelteCheck}}",
65
+ "svelte-eslint-parser": "{{fullstack.packageSpecifiers.svelteEslintParser}}",
66
+ "tailwindcss": "{{fullstack.packageSpecifiers.tailwindcss}}",
67
+ "typescript": "{{fullstack.packageSpecifiers.typescript}}",
68
+ "vite": "{{fullstack.packageSpecifiers.vite}}",
69
+ "vitest": "{{fullstack.packageSpecifiers.vitest}}"
48
70
  }
49
71
  }
50
72
  {{/if}}