@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
@@ -88,7 +88,13 @@ export class ManagementStore {
88
88
  }
89
89
 
90
90
  canOpenPage(page: string): boolean {
91
- const permissions: Record<string, string> = { users: 'app.users.manage', roles: 'app.roles.manage', audit: 'audit:read', configuration: 'app.config.manage', feedback: 'feedback:read' }
91
+ const permissions: Record<string, string> = {
92
+ users: 'app.users.manage',
93
+ roles: 'app.roles.manage',
94
+ audit: 'audit:read',
95
+ configuration: 'app.config.manage',
96
+ feedback: 'feedback:read',
97
+ }
92
98
  return !permissions[page] || this.hasPermission(permissions[page])
93
99
  }
94
100
 
@@ -97,14 +103,21 @@ export class ManagementStore {
97
103
  this.initializing = true
98
104
  this.error = ''
99
105
  try {
100
- const init = await initializeManagementApi({ ...options, onSessionExpired: () => this.expireSession() })
106
+ const init = await initializeManagementApi({
107
+ ...options,
108
+ onSessionExpired: () => this.expireSession(),
109
+ })
101
110
  if (!init.success) {
102
- this.error = init.error.code === HaiApiClientError.CONFIG_ERROR.code ? m.api_not_configured() : m.home_error()
111
+ this.error = init.error.code === HaiApiClientError.CONFIG_ERROR.code
112
+ ? m.api_not_configured()
113
+ : m.home_error()
103
114
  return
104
115
  }
105
116
  const info = await managementApi.app.info()
106
117
  if (!info.success) {
107
- this.error = info.error.httpStatus === 400 ? info.error.message : m.home_error()
118
+ this.error = info.error.httpStatus === 400
119
+ ? info.error.message
120
+ : m.home_error()
108
121
  return
109
122
  }
110
123
  this.info = info.data
@@ -122,7 +135,9 @@ export class ManagementStore {
122
135
  await this.navigate(this.router.path.slice(1))
123
136
  }
124
137
  else if (me.error.httpStatus !== 401) {
125
- this.error = me.error.httpStatus === 400 ? me.error.message : m.operation_failed()
138
+ this.error = me.error.httpStatus === 400
139
+ ? me.error.message
140
+ : m.operation_failed()
126
141
  }
127
142
  }
128
143
  catch {
@@ -163,7 +178,9 @@ export class ManagementStore {
163
178
  return
164
179
  this.requestVersion++
165
180
  this.loading = false
166
- this.page = this.canOpenPage(page) ? page : 'settings'
181
+ this.page = this.canOpenPage(page)
182
+ ? page
183
+ : 'settings'
167
184
  if (this.router.path !== `/${this.page}`)
168
185
  this.router.navigate(`/${this.page}`)
169
186
  this.page = this.router.path.slice(1)
@@ -191,18 +208,32 @@ export class ManagementStore {
191
208
  const result = await this.request(() => requestSensitive('business.settings.update', options => managementApi.admin.configuration.update({ notice: this.businessNotice }, options)))
192
209
  if (result?.success && this.info) {
193
210
  this.info = { ...this.info, notice: result.data.notice }
194
- toast.add({ 'message': m.saved(), 'variant': 'success', 'data-testid': 'notice' })
211
+ toast.add({
212
+ 'message': m.saved(),
213
+ 'variant': 'success',
214
+ 'data-testid': 'notice',
215
+ })
195
216
  }
196
217
  })
197
218
  }
198
219
 
199
220
  async queryUsers(): Promise<void> {
200
- this.appliedUsersQuery = { search: this.usersSearch.trim() || undefined, accountStatus: this.usersStatus === 'all' ? undefined : this.usersStatus }
221
+ this.appliedUsersQuery = {
222
+ search: this.usersSearch.trim() || undefined,
223
+ accountStatus: this.usersStatus === 'all'
224
+ ? undefined
225
+ : this.usersStatus,
226
+ }
201
227
  await this.loadUsers()
202
228
  }
203
229
 
204
230
  async sortUsers(key: string, direction: 'asc' | 'desc'): Promise<void> {
205
- const field = (['username', 'email', 'accountStatus', 'createdAt'] as const).find(field => field === key)
231
+ const field = ([
232
+ 'username',
233
+ 'email',
234
+ 'accountStatus',
235
+ 'createdAt',
236
+ ] as const).find(field => field === key)
206
237
  if (!field)
207
238
  return
208
239
  this.usersSortBy = field
@@ -220,7 +251,10 @@ export class ManagementStore {
220
251
  sortDirection: this.usersSortDirection,
221
252
  }), version)
222
253
  if (result?.success && version === this.requestVersion) {
223
- this.users = result.data.items.map(user => ({ ...user, avatarUrl: resolveAvatarUrl(user.avatarUrl) }))
254
+ this.users = result.data.items.map(user => ({
255
+ ...user,
256
+ avatarUrl: resolveAvatarUrl(user.avatarUrl),
257
+ }))
224
258
  this.usersTotal = result.data.total
225
259
  this.usersPage = result.data.page
226
260
  }
@@ -281,14 +315,25 @@ export class ManagementStore {
281
315
  severity: this.auditSeverity || undefined,
282
316
  action: this.auditAction.trim() || undefined,
283
317
  resource: this.auditResource.trim() || undefined,
284
- startDate: this.auditStartDate ? new SvelteDate(`${this.auditStartDate}T00:00:00`).toISOString() : undefined,
285
- endDate: this.auditEndDate ? new SvelteDate(`${this.auditEndDate}T23:59:59.999`).toISOString() : undefined,
318
+ startDate: this.auditStartDate
319
+ ? new SvelteDate(`${this.auditStartDate}T00:00:00`).toISOString()
320
+ : undefined,
321
+ endDate: this.auditEndDate
322
+ ? new SvelteDate(`${this.auditEndDate}T23:59:59.999`).toISOString()
323
+ : undefined,
286
324
  }
287
325
  await this.loadAudit()
288
326
  }
289
327
 
290
328
  async sortAudit(key: string, direction: 'asc' | 'desc'): Promise<void> {
291
- const field = (['createdAt', 'action', 'resource', 'category', 'outcome', 'severity'] as const).find(field => field === key)
329
+ const field = ([
330
+ 'createdAt',
331
+ 'action',
332
+ 'resource',
333
+ 'category',
334
+ 'outcome',
335
+ 'severity',
336
+ ] as const).find(field => field === key)
292
337
  if (!field)
293
338
  return
294
339
  this.auditSortBy = field
@@ -326,7 +371,11 @@ export class ManagementStore {
326
371
  }
327
372
  this.auditBackupReceipt = result.data
328
373
  this.auditBackupId = ''
329
- toast.add({ 'message': m.audit_backup_private_hint(), 'variant': 'success', 'data-testid': 'notice' })
374
+ toast.add({
375
+ 'message': m.audit_backup_private_hint(),
376
+ 'variant': 'success',
377
+ 'data-testid': 'notice',
378
+ })
330
379
  })
331
380
  }
332
381
 
@@ -340,7 +389,11 @@ export class ManagementStore {
340
389
  return
341
390
  saved = true
342
391
  await this.loadUsers(this.usersPage)
343
- toast.add({ 'message': m.saved(), 'variant': 'success', 'data-testid': 'notice' })
392
+ toast.add({
393
+ 'message': m.saved(),
394
+ 'variant': 'success',
395
+ 'data-testid': 'notice',
396
+ })
344
397
  })
345
398
  return saved
346
399
  }
@@ -356,7 +409,11 @@ export class ManagementStore {
356
409
  return
357
410
  }
358
411
  invited = true
359
- toast.add({ 'message': m.saved(), 'variant': 'success', 'data-testid': 'notice' })
412
+ toast.add({
413
+ 'message': m.saved(),
414
+ 'variant': 'success',
415
+ 'data-testid': 'notice',
416
+ })
360
417
  })
361
418
  return invited
362
419
  }
@@ -368,9 +425,15 @@ export class ManagementStore {
368
425
  if (!result?.success)
369
426
  return
370
427
  deleted = true
371
- const page = this.users.length === 1 ? Math.max(1, this.usersPage - 1) : this.usersPage
428
+ const page = this.users.length === 1
429
+ ? Math.max(1, this.usersPage - 1)
430
+ : this.usersPage
372
431
  await this.loadUsers(page)
373
- toast.add({ 'message': m.user_deleted(), 'variant': 'success', 'data-testid': 'notice' })
432
+ toast.add({
433
+ 'message': m.user_deleted(),
434
+ 'variant': 'success',
435
+ 'data-testid': 'notice',
436
+ })
374
437
  })
375
438
  return deleted
376
439
  }
@@ -386,7 +449,11 @@ export class ManagementStore {
386
449
  await managementApi.auth.clear()
387
450
  this.clearSession()
388
451
  }
389
- toast.add({ 'message': m.user_password_reset_done(), 'variant': 'success', 'data-testid': 'notice' })
452
+ toast.add({
453
+ 'message': m.user_password_reset_done(),
454
+ 'variant': 'success',
455
+ 'data-testid': 'notice',
456
+ })
390
457
  })
391
458
  return reset
392
459
  }
@@ -399,25 +466,38 @@ export class ManagementStore {
399
466
  return
400
467
  saved = true
401
468
  await this.loadRoles()
402
- toast.add({ 'message': m.saved(), 'variant': 'success', 'data-testid': 'notice' })
469
+ toast.add({
470
+ 'message': m.saved(),
471
+ 'variant': 'success',
472
+ 'data-testid': 'notice',
473
+ })
403
474
  })
404
475
  return saved
405
476
  }
406
477
 
407
- async saveProfile(data: UserProfileSubmitData): Promise<void> {
408
- await this.savePreferences({ displayName: data.displayName?.trim() ?? '', email: data.email?.trim() ?? '' })
478
+ /** 提交资料表单;返回已确认的持久化结果供编辑器决定是否关闭。 */
479
+ async saveProfile(data: UserProfileSubmitData): Promise<boolean> {
480
+ return this.savePreferences({ displayName: data.displayName?.trim() ?? '', email: data.email?.trim() ?? '' })
409
481
  }
410
482
 
411
- async savePreferences(data: ProfileUpdate): Promise<void> {
483
+ /** 保存偏好并刷新当前用户;写入失败返回 false,不把失败当正常关闭。 */
484
+ async savePreferences(data: ProfileUpdate): Promise<boolean> {
485
+ let saved = false
412
486
  await this.mutate(async () => {
413
487
  const result = await this.request(() => requestSensitive('iam.profile.update', options => managementApi.profile.update(data, options)))
414
488
  if (!result?.success)
415
489
  return
490
+ saved = true
416
491
  const me = await this.request(() => managementApi.iam.auth.currentUser())
417
492
  if (me?.success)
418
493
  this.setUser(me.data)
419
- toast.add({ 'message': m.saved(), 'variant': 'success', 'data-testid': 'notice' })
494
+ toast.add({
495
+ 'message': m.saved(),
496
+ 'variant': 'success',
497
+ 'data-testid': 'notice',
498
+ })
420
499
  })
500
+ return saved
421
501
  }
422
502
 
423
503
  async changePassword(data: ChangePasswordFormData): Promise<void> {
@@ -439,7 +519,11 @@ export class ManagementStore {
439
519
  }
440
520
  await managementApi.auth.clear()
441
521
  this.clearSession()
442
- toast.add({ 'message': m.password_changed(), 'variant': 'success', 'data-testid': 'notice' })
522
+ toast.add({
523
+ 'message': m.password_changed(),
524
+ 'variant': 'success',
525
+ 'data-testid': 'notice',
526
+ })
443
527
  })
444
528
  }
445
529
 
@@ -460,7 +544,11 @@ export class ManagementStore {
460
544
  }
461
545
  if (userId)
462
546
  await this.loadUsers(this.usersPage)
463
- toast.add({ 'message': m.avatar_saved(), 'variant': 'success', 'data-testid': 'notice' })
547
+ toast.add({
548
+ 'message': m.avatar_saved(),
549
+ 'variant': 'success',
550
+ 'data-testid': 'notice',
551
+ })
464
552
  })
465
553
  }
466
554
 
@@ -477,13 +565,19 @@ export class ManagementStore {
477
565
  let binary = ''
478
566
  for (const byte of bytes)
479
567
  binary += String.fromCharCode(byte)
480
- return { contentType: file.type as AvatarUpload['contentType'], base64: btoa(binary) }
568
+ return {
569
+ contentType: file.type as AvatarUpload['contentType'],
570
+ base64: btoa(binary),
571
+ }
481
572
  }
482
573
 
483
574
  private setUser(user: CurrentUser): void {
484
575
  if (this.user?.id !== user.id)
485
576
  this.sessionEpoch++
486
- this.user = { ...user, avatarUrl: resolveAvatarUrl(user.avatarUrl) }
577
+ this.user = {
578
+ ...user,
579
+ avatarUrl: resolveAvatarUrl(user.avatarUrl),
580
+ }
487
581
  if (!this.hasPermission('app.users.manage')) {
488
582
  this.users = []
489
583
  this.usersTotal = 0
@@ -504,7 +598,9 @@ export class ManagementStore {
504
598
  this.auditBackupReceipt = null
505
599
  this.auditBackupId = ''
506
600
  }
507
- this.applyLocale(typeof user.metadata?.locale === 'string' ? user.metadata.locale : this.info?.defaultLocale ?? getLocale())
601
+ this.applyLocale(typeof user.metadata?.locale === 'string'
602
+ ? user.metadata.locale
603
+ : this.info?.defaultLocale ?? getLocale())
508
604
  if (typeof user.metadata?.theme === 'string' && AVAILABLE_THEMES.includes(user.metadata.theme))
509
605
  themeStore.set(user.metadata.theme)
510
606
  else
@@ -574,7 +670,13 @@ export class ManagementStore {
574
670
  this.error = m.forbidden()
575
671
  }
576
672
  else {
577
- this.error = result.error.httpStatus === 403 ? m.forbidden() : result.error.httpStatus === 409 ? m.conflict() : result.error.httpStatus === 400 || result.error.httpStatus === 422 ? result.error.message || m.validation_failed() : m.operation_failed()
673
+ this.error = result.error.httpStatus === 403
674
+ ? m.forbidden()
675
+ : result.error.httpStatus === 409
676
+ ? m.conflict()
677
+ : result.error.httpStatus === 400 || result.error.httpStatus === 422
678
+ ? result.error.message || m.validation_failed()
679
+ : m.operation_failed()
578
680
  }
579
681
  this.error = core.error.formatMessage({ message: this.error, traceId: result.error.traceId })
580
682
  }
@@ -1,7 +1,11 @@
1
1
  import { applyTheme, getSavedTheme } from '@h-ai/ui/theme'
2
2
 
3
3
  export const DEFAULT_THEME = '{{fullstack.theme.defaultTheme}}'
4
- export const AVAILABLE_THEMES: readonly string[] = [{{#each fullstack.theme.themes}}'{{this}}'{{#unless @last}}, {{/unless}}{{/each}}]
4
+ export const AVAILABLE_THEMES: readonly string[] = [
5
+ {{#each fullstack.theme.themes}}
6
+ '{{this}}',
7
+ {{/each}}
8
+ ]
5
9
 
6
10
  /** 只维护响应式状态和已编译主题白名单,DOM 与安全持久化复用 UI 公共工具。 */
7
11
  class ThemeState {
@@ -9,7 +13,9 @@ class ThemeState {
9
13
 
10
14
  constructor() {
11
15
  const saved = getSavedTheme(DEFAULT_THEME)
12
- this.set(AVAILABLE_THEMES.includes(saved) ? saved : DEFAULT_THEME)
16
+ this.set(AVAILABLE_THEMES.includes(saved)
17
+ ? saved
18
+ : DEFAULT_THEME)
13
19
  }
14
20
 
15
21
  set(value: string): void {
@@ -18,7 +18,13 @@ function createServerFetch() {
18
18
  const key = crypto.asymmetric.generateKeyPair()
19
19
  if (!key.success)
20
20
  throw new Error(key.error.message)
21
- const created = crypto.transport.createServer({ serverKeyRing: { activeKeyId: 'test-v1', keys: { 'test-v1': key.data } }, keyStore: createInMemoryKeyStore() })
21
+ const created = crypto.transport.createServer({
22
+ serverKeyRing: {
23
+ activeKeyId: 'test-v1',
24
+ keys: { 'test-v1': key.data },
25
+ },
26
+ keyStore: createInMemoryKeyStore(),
27
+ })
22
28
  if (!created.success)
23
29
  throw new Error(created.error.message)
24
30
  const server = created.data
@@ -31,7 +37,11 @@ function createServerFetch() {
31
37
  if (url.pathname.endsWith('/_hai/key-exchange')) {
32
38
  const body = await request.json() as { clientPublicKey: string }
33
39
  const clientId = await server.registerClientKey(body.clientPublicKey)
34
- return Response.json({ serverKeyId: server.getServerKeyId(), serverPublicKey: server.getServerPublicKey(), clientId })
40
+ return Response.json({
41
+ serverKeyId: server.getServerKeyId(),
42
+ serverPublicKey: server.getServerPublicKey(),
43
+ clientId,
44
+ })
35
45
  }
36
46
  const clientId = request.headers.get(TRANSPORT_PROTOCOL.CLIENT_ID_HEADER)
37
47
  if (!clientId)
@@ -49,7 +59,9 @@ function createServerFetch() {
49
59
  const response = await server.encryptResponse(verified.data.context, JSON.stringify({ success: true, data }), 200)
50
60
  if (!response.success)
51
61
  throw new Error(response.error.message)
52
- return Response.json(response.data, { headers: { [TRANSPORT_PROTOCOL.ENCRYPTED_HEADER]: TRANSPORT_PROTOCOL.ENCRYPTED_HEADER_VALUE } })
62
+ return Response.json(response.data, {
63
+ headers: { [TRANSPORT_PROTOCOL.ENCRYPTED_HEADER]: TRANSPORT_PROTOCOL.ENCRYPTED_HEADER_VALUE },
64
+ })
53
65
  }
54
66
  return { fetchFn, requests, messages }
55
67
  }
@@ -75,7 +87,9 @@ describe('shared API transport', () => {
75
87
  })
76
88
 
77
89
  it.each([false, true])('uses the injected API origin when native configuration is required: %s', async (requireApiBase) => {
78
- Object.assign(globalThis, { __HAI_PUBLIC_CONFIG__: { apiBase: 'https://runtime.example.com' } })
90
+ Object.assign(globalThis, {
91
+ __HAI_PUBLIC_CONFIG__: { apiBase: 'https://runtime.example.com' },
92
+ })
79
93
  const server = createServerFetch()
80
94
  try {
81
95
  expect((await initializeManagementApi({ fetchFn: server.fetchFn, requireApiBase })).success).toBe(true)
@@ -89,7 +103,11 @@ describe('shared API transport', () => {
89
103
 
90
104
  it('encrypts business input and preserves the native token transport header', async () => {
91
105
  const server = createServerFetch()
92
- const init = await initializeManagementApi({ fetchFn: server.fetchFn, baseUrl: 'https://api.example.com/api/v1', headers: { 'X-HAI-Token-Transport': 'body' } })
106
+ const init = await initializeManagementApi({
107
+ fetchFn: server.fetchFn,
108
+ baseUrl: 'https://api.example.com/api/v1',
109
+ headers: { 'X-HAI-Token-Transport': 'body' },
110
+ })
93
111
  expect(init.success).toBe(true)
94
112
  try {
95
113
  const result = await managementApi.app.echo({ message: 'private message' })
@@ -6,10 +6,29 @@ import { ManagementStore } from '../src/lib/stores/management-store.svelte.js'
6
6
 
7
7
  const api = vi.hoisted(() => ({
8
8
  initialize: vi.fn(),
9
- app: { info: vi.fn() },
10
- auth: { clear: vi.fn() },
11
- iam: { auth: { logout: vi.fn(), currentUser: vi.fn(), reauthenticate: vi.fn(), changePassword: vi.fn() } },
12
- admin: { users: { list: vi.fn(), resetPassword: vi.fn() }, audit: { list: vi.fn() } },
9
+ app: {
10
+ info: vi.fn(),
11
+ },
12
+ auth: {
13
+ clear: vi.fn(),
14
+ },
15
+ iam: {
16
+ auth: {
17
+ logout: vi.fn(),
18
+ currentUser: vi.fn(),
19
+ reauthenticate: vi.fn(),
20
+ changePassword: vi.fn(),
21
+ },
22
+ },
23
+ admin: {
24
+ users: {
25
+ list: vi.fn(),
26
+ resetPassword: vi.fn(),
27
+ },
28
+ audit: {
29
+ list: vi.fn(),
30
+ },
31
+ },
13
32
  }))
14
33
  vi.mock('../src/lib/api/api-client.js', () => ({
15
34
  managementApi: api,
@@ -27,8 +46,17 @@ const member = {
27
46
  createdAt: '2026-01-01T00:00:00.000Z',
28
47
  updatedAt: '2026-01-01T00:00:00.000Z',
29
48
  }
30
- const administrator = { ...member, id: 'admin-id', username: 'admin', roles: ['admin'], permissions: ['app.users.manage', 'app.roles.manage'] }
31
- const forbidden = { success: false, error: { code: 'test:forbidden', message: 'Forbidden', httpStatus: 403 } } as const
49
+ const administrator = {
50
+ ...member,
51
+ id: 'admin-id',
52
+ username: 'admin',
53
+ roles: ['admin'],
54
+ permissions: ['app.users.manage', 'app.roles.manage'],
55
+ }
56
+ const forbidden = {
57
+ success: false,
58
+ error: { code: 'test:forbidden', message: 'Forbidden', httpStatus: 403 },
59
+ } as const
32
60
 
33
61
  function deferred<T>() {
34
62
  let resolve!: (result: T) => void
@@ -41,7 +69,10 @@ function deferred<T>() {
41
69
  beforeEach(() => {
42
70
  vi.resetAllMocks()
43
71
  vi.stubGlobal('location', { hash: '#/users' })
44
- vi.stubGlobal('history', { replaceState: vi.fn(), pushState: vi.fn() })
72
+ vi.stubGlobal('history', {
73
+ replaceState: vi.fn(),
74
+ pushState: vi.fn(),
75
+ })
45
76
  api.iam.auth.logout.mockResolvedValue({ success: true })
46
77
  api.iam.auth.currentUser.mockResolvedValue({ success: true, data: member })
47
78
  })
@@ -66,7 +97,19 @@ describe('management session and request ownership', () => {
66
97
  })
67
98
 
68
99
  it.each(['hai:iam:002', 'hai:iam:034', 'hai:iam:999'])('password failures use safe messages and keep the active session: %s', async (code) => {
69
- api.iam.auth.changePassword.mockResolvedValueOnce({ success: false, error: { code, message: 'Private password or database detail', httpStatus: code === 'hai:iam:002' ? 401 : code === 'hai:iam:999' ? 500 : 400, traceId: 'password-failure' } })
100
+ api.iam.auth.changePassword.mockResolvedValueOnce({
101
+ success: false,
102
+ error: {
103
+ code,
104
+ message: 'Private password or database detail',
105
+ httpStatus: code === 'hai:iam:002'
106
+ ? 401
107
+ : code === 'hai:iam:999'
108
+ ? 500
109
+ : 400,
110
+ traceId: 'password-failure',
111
+ },
112
+ })
70
113
  const store = createStore()
71
114
  store.user = member
72
115
  await store.changePassword({ oldPassword: 'CurrentPassword123!', newPassword: 'NewPassword123!', confirmPassword: 'NewPassword123!' })
@@ -88,13 +131,22 @@ describe('management session and request ownership', () => {
88
131
  })
89
132
 
90
133
  it.each(['dormant', 'closed'] as const)('resetting another duty password preserves its %s state and the administrator session', async (accountStatus) => {
91
- const target: AdminUser = { ...member, roles: ['config_admin'], accountStatus, accountType: 'permanent', accountExpiresAt: null, lastLoginAt: null }
134
+ const target: AdminUser = {
135
+ ...member,
136
+ roles: ['config_admin'],
137
+ accountStatus,
138
+ accountType: 'permanent',
139
+ accountExpiresAt: null,
140
+ lastLoginAt: null,
141
+ }
92
142
  api.admin.users.resetPassword.mockResolvedValueOnce({ success: true })
93
143
  const store = createStore()
94
144
  store.user = administrator
95
145
  store.users = [target]
96
146
  expect(await store.resetUserPassword(target, 'NewPassword123!')).toBe(true)
97
- expect(api.admin.users.resetPassword).toHaveBeenCalledWith({ id: target.id, newPassword: 'NewPassword123!' }, expect.objectContaining({ context: expect.any(Object) }))
147
+ expect(api.admin.users.resetPassword).toHaveBeenCalledWith({ id: target.id, newPassword: 'NewPassword123!' }, expect.objectContaining({
148
+ context: expect.any(Object),
149
+ }))
98
150
  expect(store.users[0]?.accountStatus).toBe(accountStatus)
99
151
  expect(store.user?.id).toBe(administrator.id)
100
152
  expect(api.auth.clear).not.toHaveBeenCalled()
@@ -102,8 +154,22 @@ describe('management session and request ownership', () => {
102
154
  })
103
155
 
104
156
  it('an unsuccessful self reset retains authentication and hides private server details', async () => {
105
- const target: AdminUser = { ...administrator, roles: ['admin'], accountType: 'permanent', accountExpiresAt: null, lastLoginAt: null }
106
- api.admin.users.resetPassword.mockResolvedValueOnce({ success: false, error: { code: 'hai:iam:999', httpStatus: 500, message: 'Private password reset detail', traceId: 'reset-failure' } })
157
+ const target: AdminUser = {
158
+ ...administrator,
159
+ roles: ['admin'],
160
+ accountType: 'permanent',
161
+ accountExpiresAt: null,
162
+ lastLoginAt: null,
163
+ }
164
+ api.admin.users.resetPassword.mockResolvedValueOnce({
165
+ success: false,
166
+ error: {
167
+ code: 'hai:iam:999',
168
+ httpStatus: 500,
169
+ message: 'Private password reset detail',
170
+ traceId: 'reset-failure',
171
+ },
172
+ })
107
173
  const store = createStore()
108
174
  store.user = administrator
109
175
  expect(await store.resetUserPassword(target, 'NewPassword123!')).toBe(false)
@@ -114,7 +180,13 @@ describe('management session and request ownership', () => {
114
180
  })
115
181
 
116
182
  it('a successful self reset clears local authentication after the server confirms revocation', async () => {
117
- const target: AdminUser = { ...administrator, roles: ['admin'], accountType: 'permanent', accountExpiresAt: null, lastLoginAt: null }
183
+ const target: AdminUser = {
184
+ ...administrator,
185
+ roles: ['admin'],
186
+ accountType: 'permanent',
187
+ accountExpiresAt: null,
188
+ lastLoginAt: null,
189
+ }
118
190
  const response = deferred<{ success: true }>()
119
191
  api.admin.users.resetPassword.mockReturnValueOnce(response.promise)
120
192
  const store = createStore()
@@ -131,7 +203,15 @@ describe('management session and request ownership', () => {
131
203
  it('preserves the clock synchronization advice when application bootstrap is rejected', async () => {
132
204
  const message = 'Synchronize device and server clocks, then refresh to retry.'
133
205
  api.initialize.mockResolvedValueOnce({ success: true })
134
- api.app.info.mockResolvedValueOnce({ success: false, error: { code: 'hai:api-client:007', httpStatus: 400, message, cause: { transportCode: 'hai:crypto:071' } } })
206
+ api.app.info.mockResolvedValueOnce({
207
+ success: false,
208
+ error: {
209
+ code: 'hai:api-client:007',
210
+ httpStatus: 400,
211
+ message,
212
+ cause: { transportCode: 'hai:crypto:071' },
213
+ },
214
+ })
135
215
  const store = createStore()
136
216
  await store.initialize({ baseUrl: 'https://api.example.test' })
137
217
  expect(store.error).toBe(message)
@@ -141,7 +221,10 @@ describe('management session and request ownership', () => {
141
221
 
142
222
  it('preserves useful validation advice instead of replacing it with a generic operation error', async () => {
143
223
  const message = 'Synchronize device and server clocks, then refresh to retry.'
144
- api.admin.users.list.mockResolvedValueOnce({ success: false, error: { code: 'hai:api-client:007', httpStatus: 400, message } })
224
+ api.admin.users.list.mockResolvedValueOnce({
225
+ success: false,
226
+ error: { code: 'hai:api-client:007', httpStatus: 400, message },
227
+ })
145
228
  const store = createStore()
146
229
  store.user = administrator
147
230
  await store.loadUsers()
@@ -185,7 +268,14 @@ describe('management session and request ownership', () => {
185
268
  api.admin.users.list.mockResolvedValueOnce(forbidden)
186
269
  const store = createStore()
187
270
  store.user = administrator
188
- store.roles = [{ id: 'role-id', code: 'admin', name: 'Administrator', userCount: 1 }]
271
+ store.roles = [
272
+ {
273
+ id: 'role-id',
274
+ code: 'admin',
275
+ name: 'Administrator',
276
+ userCount: 1,
277
+ },
278
+ ]
189
279
  await store.loadUsers()
190
280
  expect(store.user?.roles).toEqual(['user'])
191
281
  expect(store.page).toBe('settings')
@@ -197,7 +287,10 @@ describe('management session and request ownership', () => {
197
287
  })
198
288
 
199
289
  it.each(['hai:iam:105', 'hai:common:100'])('keeps the page, original user and unsaved fields when an expired session request is cancelled: %s', async (code) => {
200
- api.admin.users.list.mockResolvedValueOnce({ success: false, error: { code, message: 'Expired', httpStatus: 401 } })
290
+ api.admin.users.list.mockResolvedValueOnce({
291
+ success: false,
292
+ error: { code, message: 'Expired', httpStatus: 401 },
293
+ })
201
294
  const store = createStore()
202
295
  store.user = administrator
203
296
  store.businessNotice = 'Unsaved business input'
@@ -221,7 +314,15 @@ describe('management session and request ownership', () => {
221
314
  })
222
315
 
223
316
  it('user paging and header sorting keep applied filters until Query submits drafts', async () => {
224
- api.admin.users.list.mockResolvedValue({ success: true, data: { items: [], total: 60, page: 1, pageSize: 20 } })
317
+ api.admin.users.list.mockResolvedValue({
318
+ success: true,
319
+ data: {
320
+ items: [],
321
+ total: 60,
322
+ page: 1,
323
+ pageSize: 20,
324
+ },
325
+ })
225
326
  const store = createStore()
226
327
  store.user = administrator
227
328
  store.usersSearch = 'applied'
@@ -232,13 +333,29 @@ it('user paging and header sorting keep applied filters until Query submits draf
232
333
  await store.loadUsers(2)
233
334
  expect(api.admin.users.list).toHaveBeenLastCalledWith(expect.objectContaining({ page: 2, search: 'applied', accountStatus: 'active' }))
234
335
  await store.sortUsers('username', 'asc')
235
- expect(api.admin.users.list).toHaveBeenLastCalledWith(expect.objectContaining({ page: 1, search: 'applied', accountStatus: 'active', sortBy: 'username', sortDirection: 'asc' }))
336
+ expect(api.admin.users.list).toHaveBeenLastCalledWith(expect.objectContaining({
337
+ page: 1,
338
+ search: 'applied',
339
+ accountStatus: 'active',
340
+ sortBy: 'username',
341
+ sortDirection: 'asc',
342
+ }))
236
343
  await store.queryUsers()
237
344
  expect(api.admin.users.list).toHaveBeenLastCalledWith(expect.objectContaining({ search: 'draft', accountStatus: 'dormant' }))
238
345
  })
239
346
 
240
347
  it('audit paging and header sorting do not submit unfinished filter drafts', async () => {
241
- api.admin.audit.list.mockResolvedValue({ success: true, data: { items: [], stats: [], backupEnabled: false, total: 60, page: 1, pageSize: 20 } })
348
+ api.admin.audit.list.mockResolvedValue({
349
+ success: true,
350
+ data: {
351
+ items: [],
352
+ stats: [],
353
+ backupEnabled: false,
354
+ total: 60,
355
+ page: 1,
356
+ pageSize: 20,
357
+ },
358
+ })
242
359
  const store = createStore()
243
360
  store.user = administrator
244
361
  store.auditResource = 'applied'
@@ -247,7 +364,12 @@ it('audit paging and header sorting do not submit unfinished filter drafts', asy
247
364
  await store.loadAudit(2)
248
365
  expect(api.admin.audit.list).toHaveBeenLastCalledWith(expect.objectContaining({ page: 2, resource: 'applied' }))
249
366
  await store.sortAudit('action', 'asc')
250
- expect(api.admin.audit.list).toHaveBeenLastCalledWith(expect.objectContaining({ page: 1, resource: 'applied', sortBy: 'action', sortDirection: 'asc' }))
367
+ expect(api.admin.audit.list).toHaveBeenLastCalledWith(expect.objectContaining({
368
+ page: 1,
369
+ resource: 'applied',
370
+ sortBy: 'action',
371
+ sortDirection: 'asc',
372
+ }))
251
373
  await store.queryAudit()
252
374
  expect(api.admin.audit.list).toHaveBeenLastCalledWith(expect.objectContaining({ page: 1, resource: 'draft' }))
253
375
  })