@antelopejs/dms 0.15.0 → 0.16.0

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 (354) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/README.md +36 -26
  3. package/bin/control/base.mjs +9 -4
  4. package/bin/control/browser-client.mjs +24 -5
  5. package/bin/control/browser-wire.mjs +41 -0
  6. package/bin/control/browser-wire.test.mjs +96 -0
  7. package/bin/control/browser.mjs +78 -27
  8. package/bin/control/commands-stack.mjs +58 -8
  9. package/bin/control/commands-verify.mjs +47 -1
  10. package/bin/control/doctor.mjs +49 -4
  11. package/bin/control/failure.mjs +9 -0
  12. package/bin/control/journey.mjs +163 -30
  13. package/bin/control/journey.test.mjs +61 -0
  14. package/bin/control/json.mjs +229 -0
  15. package/bin/control/json.test.mjs +130 -0
  16. package/bin/control/mail.mjs +57 -7
  17. package/bin/control/mail.test.mjs +55 -0
  18. package/bin/control/mongo.mjs +205 -27
  19. package/bin/control/mongo.test.mjs +298 -0
  20. package/bin/control/options.mjs +33 -0
  21. package/bin/control/processes.mjs +24 -2
  22. package/bin/control/renderer.mjs +39 -0
  23. package/bin/control/renderer.test.mjs +324 -0
  24. package/bin/control/run-state.mjs +119 -6
  25. package/bin/control/run-state.test.mjs +171 -0
  26. package/bin/control/session.mjs +31 -14
  27. package/bin/control/stack.mjs +96 -24
  28. package/bin/control/tab.mjs +117 -18
  29. package/bin/control/verify/behaviour.mjs +44 -6
  30. package/bin/control/verify/behaviour.test.mjs +130 -22
  31. package/bin/control/verify/decode.mjs +220 -0
  32. package/bin/control/verify/decode.test.mjs +245 -0
  33. package/bin/control/verify/doors.mjs +1 -1
  34. package/bin/control/verify/doors.test.mjs +40 -0
  35. package/bin/control/verify/emit.mjs +135 -9
  36. package/bin/control/verify/formal.mjs +61 -10
  37. package/bin/control/verify/formal.test.mjs +19 -3
  38. package/bin/control/verify/judgement.mjs +22 -3
  39. package/bin/control/verify/map.mjs +25 -5
  40. package/bin/control/verify/publish.mjs +20 -14
  41. package/bin/control/verify/seeded.mjs +65 -16
  42. package/bin/control/verify/skill.mjs +27 -6
  43. package/bin/control/verify/types.mjs +29 -0
  44. package/bin/control/verify/verdict.mjs +72 -7
  45. package/bin/dms-control.mjs +8 -28
  46. package/checks/api-error-presentation.d.mts +61 -0
  47. package/dist/attachments/access.js +1 -1
  48. package/dist/attachments/access.js.map +1 -1
  49. package/dist/attachments/fields.d.ts +2 -2
  50. package/dist/attachments/fields.js +26 -13
  51. package/dist/attachments/fields.js.map +1 -1
  52. package/dist/attachments/index.d.ts +1 -0
  53. package/dist/attachments/index.js +1 -0
  54. package/dist/attachments/index.js.map +1 -1
  55. package/dist/attachments/read.js.map +1 -1
  56. package/dist/attachments/save.d.ts +1 -1
  57. package/dist/attachments/save.js +49 -17
  58. package/dist/attachments/save.js.map +1 -1
  59. package/dist/attachments/table-view.d.ts +1 -2
  60. package/dist/attachments/table-view.js +23 -13
  61. package/dist/attachments/table-view.js.map +1 -1
  62. package/dist/automation/events.d.ts +10 -10
  63. package/dist/automation/index.js +3 -4
  64. package/dist/automation/index.js.map +1 -1
  65. package/dist/automation/send-notification.d.ts +2 -2
  66. package/dist/automation/send-notification.js +4 -6
  67. package/dist/automation/send-notification.js.map +1 -1
  68. package/dist/config/internal/state.d.ts +7 -0
  69. package/dist/config/internal/state.js +9 -0
  70. package/dist/config/internal/state.js.map +1 -1
  71. package/dist/config/startup-config.d.ts +1 -0
  72. package/dist/config/startup-config.js +11 -0
  73. package/dist/config/startup-config.js.map +1 -1
  74. package/dist/crons/cleanup-user-invites.js.map +1 -1
  75. package/dist/crons/sweep-expired-sessions.js.map +1 -1
  76. package/dist/crons/sweep-stale-exports.js.map +1 -1
  77. package/dist/db/guard-row.d.ts +2 -3
  78. package/dist/db/guard-row.js +1 -2
  79. package/dist/db/guard-row.js.map +1 -1
  80. package/dist/db/index.d.ts +1 -0
  81. package/dist/db/index.js +1 -0
  82. package/dist/db/index.js.map +1 -1
  83. package/dist/db/models/attachments.model.d.ts +7 -5
  84. package/dist/db/models/index.d.ts +1 -0
  85. package/dist/db/models/index.js +1 -0
  86. package/dist/db/models/index.js.map +1 -1
  87. package/dist/db/models/setupToken.model.d.ts +31 -0
  88. package/dist/db/models/setupToken.model.js +62 -0
  89. package/dist/db/models/setupToken.model.js.map +1 -0
  90. package/dist/db/models/signInAttempts.model.d.ts +7 -5
  91. package/dist/db/models/systemState.model.d.ts +7 -5
  92. package/dist/db/models/userEmailChanges.model.d.ts +7 -5
  93. package/dist/db/models/userKnownDevices.model.d.ts +7 -5
  94. package/dist/db/models/userNotificationPreferences.model.d.ts +7 -5
  95. package/dist/db/models/userNotifications.model.d.ts +9 -6
  96. package/dist/db/models/userNotifications.model.js.map +1 -1
  97. package/dist/db/setup-token.d.ts +2 -0
  98. package/dist/db/setup-token.js +28 -0
  99. package/dist/db/setup-token.js.map +1 -0
  100. package/dist/db/tables/index.d.ts +1 -0
  101. package/dist/db/tables/index.js +1 -0
  102. package/dist/db/tables/index.js.map +1 -1
  103. package/dist/db/tables/setupToken.table.d.ts +7 -0
  104. package/dist/db/tables/setupToken.table.js +34 -0
  105. package/dist/db/tables/setupToken.table.js.map +1 -0
  106. package/dist/dev/module-update-notifications.js.map +1 -1
  107. package/dist/hooks/internal/sign-in-monitor.js +6 -6
  108. package/dist/hooks/internal/sign-in-monitor.js.map +1 -1
  109. package/dist/implementations/dms/page/controller.d.ts +2 -2
  110. package/dist/implementations/dms/page/controller.js +3 -1
  111. package/dist/implementations/dms/page/controller.js.map +1 -1
  112. package/dist/implementations/dms/page/dynamic-menu.js +3 -2
  113. package/dist/implementations/dms/page/dynamic-menu.js.map +1 -1
  114. package/dist/implementations/dms/page/notifications.d.ts +6 -2
  115. package/dist/implementations/dms/page/notifications.js +1 -0
  116. package/dist/implementations/dms/page/notifications.js.map +1 -1
  117. package/dist/implementations/dms/page/payloads.js +6 -10
  118. package/dist/implementations/dms/page/payloads.js.map +1 -1
  119. package/dist/implementations/dms/page/preview-pages.js +6 -3
  120. package/dist/implementations/dms/page/preview-pages.js.map +1 -1
  121. package/dist/implementations/dms/page/public-layout.d.ts +2 -0
  122. package/dist/implementations/dms/page/public-layout.js +45 -0
  123. package/dist/implementations/dms/page/public-layout.js.map +1 -0
  124. package/dist/implementations/dms/page/registry.js +10 -4
  125. package/dist/implementations/dms/page/registry.js.map +1 -1
  126. package/dist/implementations/dms/permissions.js +12 -12
  127. package/dist/implementations/dms/permissions.js.map +1 -1
  128. package/dist/implementations/dms-base/bulk-selection.d.ts +3 -4
  129. package/dist/implementations/dms-base/bulk-selection.js +18 -6
  130. package/dist/implementations/dms-base/bulk-selection.js.map +1 -1
  131. package/dist/implementations/dms-base/filter-tokens.js.map +1 -1
  132. package/dist/implementations/dms-base/footer-summary.js +44 -27
  133. package/dist/implementations/dms-base/footer-summary.js.map +1 -1
  134. package/dist/implementations/dms-base/row-rule-operand.d.ts +6 -0
  135. package/dist/implementations/dms-base/row-rule-operand.js +41 -0
  136. package/dist/implementations/dms-base/row-rule-operand.js.map +1 -0
  137. package/dist/implementations/dms-base/search-route.d.ts +9 -13
  138. package/dist/implementations/dms-base/search-route.js +7 -6
  139. package/dist/implementations/dms-base/search-route.js.map +1 -1
  140. package/dist/implementations/dms-base/services.d.ts +1 -0
  141. package/dist/implementations/dms-base/services.js +1 -0
  142. package/dist/implementations/dms-base/services.js.map +1 -1
  143. package/dist/implementations/dms-base/table-view.d.ts +5 -6
  144. package/dist/implementations/dms-base/table-view.js +17 -6
  145. package/dist/implementations/dms-base/table-view.js.map +1 -1
  146. package/dist/implementations/dms-html-render/index.js +16 -6
  147. package/dist/implementations/dms-html-render/index.js.map +1 -1
  148. package/dist/implementations/dms-notifications/delivery-guard.d.ts +3 -2
  149. package/dist/implementations/dms-notifications/delivery-guard.js.map +1 -1
  150. package/dist/implementations/dms-notifications/index.js.map +1 -1
  151. package/dist/index.d.ts +0 -1
  152. package/dist/index.js +6 -2
  153. package/dist/index.js.map +1 -1
  154. package/dist/pages/public/preview/block-types.d.ts +1 -2
  155. package/dist/pages/public/preview/block-types.js.map +1 -1
  156. package/dist/pages/settings/overview/account-summary.js +4 -1
  157. package/dist/pages/settings/overview/account-summary.js.map +1 -1
  158. package/dist/pages/settings/users/account-activity-feed.js +3 -2
  159. package/dist/pages/settings/users/account-activity-feed.js.map +1 -1
  160. package/dist/pages/settings/users/account-credentials.d.ts +1 -1
  161. package/dist/pages/settings/users/account-credentials.js +1 -2
  162. package/dist/pages/settings/users/account-credentials.js.map +1 -1
  163. package/dist/pages/settings/users/account-data-store.d.ts +1 -1
  164. package/dist/pages/settings/users/account-data-store.js +1 -3
  165. package/dist/pages/settings/users/account-data-store.js.map +1 -1
  166. package/dist/pages/settings/users/current-password.js +1 -1
  167. package/dist/pages/settings/users/current-password.js.map +1 -1
  168. package/dist/pages/settings/users/email-change.d.ts +2 -2
  169. package/dist/pages/settings/users/email-change.js +2 -3
  170. package/dist/pages/settings/users/email-change.js.map +1 -1
  171. package/dist/pages/settings/users/invite-extension-routes.d.ts +2 -2
  172. package/dist/pages/settings/users/invite-extension-routes.js +11 -4
  173. package/dist/pages/settings/users/invite-extension-routes.js.map +1 -1
  174. package/dist/pages/settings/users/invites.d.ts +2 -2
  175. package/dist/pages/settings/users/member-invite-batch.d.ts +2 -2
  176. package/dist/pages/settings/users/member-invite-batch.js +7 -3
  177. package/dist/pages/settings/users/member-invite-batch.js.map +1 -1
  178. package/dist/pages/settings/users/member-roles-field.js +1 -4
  179. package/dist/pages/settings/users/member-roles-field.js.map +1 -1
  180. package/dist/pages/settings/users/members.d.ts +2 -3
  181. package/dist/pages/settings/users/members.js.map +1 -1
  182. package/dist/pages/settings/users/notifications.d.ts +2 -3
  183. package/dist/pages/settings/users/notifications.js +2 -1
  184. package/dist/pages/settings/users/notifications.js.map +1 -1
  185. package/dist/pages/settings/users/profile-helpers.d.ts +0 -1
  186. package/dist/pages/settings/users/profile-helpers.js +0 -17
  187. package/dist/pages/settings/users/profile-helpers.js.map +1 -1
  188. package/dist/pages/settings/users/profile.d.ts +3 -4
  189. package/dist/pages/settings/users/profile.js +12 -12
  190. package/dist/pages/settings/users/profile.js.map +1 -1
  191. package/dist/pages/settings/users/region.d.ts +3 -4
  192. package/dist/pages/settings/users/region.js.map +1 -1
  193. package/dist/pages/settings/users/regional-preferences.d.ts +8 -1
  194. package/dist/pages/settings/users/regional-preferences.js +10 -6
  195. package/dist/pages/settings/users/regional-preferences.js.map +1 -1
  196. package/dist/pages/settings/users/role-preview.js +3 -2
  197. package/dist/pages/settings/users/role-preview.js.map +1 -1
  198. package/dist/pages/settings/users/roles.d.ts +4 -5
  199. package/dist/pages/settings/users/roles.js.map +1 -1
  200. package/dist/pages/settings/users/security.d.ts +9 -12
  201. package/dist/pages/settings/users/security.js +11 -9
  202. package/dist/pages/settings/users/security.js.map +1 -1
  203. package/dist/pages/settings/users/two-factor-operations.d.ts +7 -7
  204. package/dist/pages/settings/users/two-factor-operations.js +5 -7
  205. package/dist/pages/settings/users/two-factor-operations.js.map +1 -1
  206. package/dist/realtime/broker.js.map +1 -1
  207. package/dist/realtime/heartbeat.js.map +1 -1
  208. package/dist/realtime/redis-broker.js.map +1 -1
  209. package/dist/realtime/sessions.js.map +1 -1
  210. package/dist/realtime/sse.js +1 -2
  211. package/dist/realtime/sse.js.map +1 -1
  212. package/dist/routes/auth/forgot-password.d.ts +1 -1
  213. package/dist/routes/auth/forgot-password.js +1 -3
  214. package/dist/routes/auth/forgot-password.js.map +1 -1
  215. package/dist/routes/auth/login.d.ts +2 -2
  216. package/dist/routes/auth/login.js +8 -4
  217. package/dist/routes/auth/login.js.map +1 -1
  218. package/dist/routes/auth/logout.d.ts +1 -1
  219. package/dist/routes/auth/logout.js +1 -2
  220. package/dist/routes/auth/logout.js.map +1 -1
  221. package/dist/routes/auth/oauth/providers.js +30 -6
  222. package/dist/routes/auth/oauth/providers.js.map +1 -1
  223. package/dist/routes/auth/oauth/state.js +8 -5
  224. package/dist/routes/auth/oauth/state.js.map +1 -1
  225. package/dist/routes/auth/oauth/token-exchange.js +8 -1
  226. package/dist/routes/auth/oauth/token-exchange.js.map +1 -1
  227. package/dist/routes/auth/password-hash-upgrade.d.ts +2 -0
  228. package/dist/routes/auth/password-hash-upgrade.js +22 -0
  229. package/dist/routes/auth/password-hash-upgrade.js.map +1 -0
  230. package/dist/routes/auth/refresh.d.ts +1 -1
  231. package/dist/routes/auth/refresh.js +1 -2
  232. package/dist/routes/auth/refresh.js.map +1 -1
  233. package/dist/routes/auth/request-2fa-email.d.ts +1 -1
  234. package/dist/routes/auth/request-2fa-email.js +1 -2
  235. package/dist/routes/auth/request-2fa-email.js.map +1 -1
  236. package/dist/routes/auth/reset-password.d.ts +1 -1
  237. package/dist/routes/auth/reset-password.js +1 -3
  238. package/dist/routes/auth/reset-password.js.map +1 -1
  239. package/dist/routes/auth/session-handoff.d.ts +1 -1
  240. package/dist/routes/auth/session-handoff.js +1 -2
  241. package/dist/routes/auth/session-handoff.js.map +1 -1
  242. package/dist/routes/auth/signup.d.ts +1 -1
  243. package/dist/routes/auth/signup.js +1 -2
  244. package/dist/routes/auth/signup.js.map +1 -1
  245. package/dist/routes/auth/switch-tenant.d.ts +2 -3
  246. package/dist/routes/auth/switch-tenant.js +3 -2
  247. package/dist/routes/auth/switch-tenant.js.map +1 -1
  248. package/dist/routes/auth/validate-forgot-password-token.d.ts +1 -1
  249. package/dist/routes/auth/validate-forgot-password-token.js +1 -3
  250. package/dist/routes/auth/validate-forgot-password-token.js.map +1 -1
  251. package/dist/routes/auth/validate-invite-token.d.ts +1 -1
  252. package/dist/routes/auth/validate-invite-token.js +1 -3
  253. package/dist/routes/auth/validate-invite-token.js.map +1 -1
  254. package/dist/routes/auth/verify-2fa.d.ts +1 -1
  255. package/dist/routes/auth/verify-2fa.js +1 -2
  256. package/dist/routes/auth/verify-2fa.js.map +1 -1
  257. package/dist/routes/auth/verify-email.d.ts +1 -1
  258. package/dist/routes/auth/verify-email.js +1 -2
  259. package/dist/routes/auth/verify-email.js.map +1 -1
  260. package/dist/routes/auth.d.ts +14 -15
  261. package/dist/routes/auth.js +15 -13
  262. package/dist/routes/auth.js.map +1 -1
  263. package/dist/routes/authentication.d.ts +2 -0
  264. package/dist/routes/authentication.js +2 -0
  265. package/dist/routes/authentication.js.map +1 -1
  266. package/dist/routes/files.d.ts +1 -2
  267. package/dist/routes/files.js.map +1 -1
  268. package/dist/routes/onboarding.d.ts +4 -3
  269. package/dist/routes/onboarding.js +20 -3
  270. package/dist/routes/onboarding.js.map +1 -1
  271. package/dist/routes/realtime.js +5 -6
  272. package/dist/routes/realtime.js.map +1 -1
  273. package/dist/routes/store.d.ts +6 -7
  274. package/dist/routes/store.js +19 -13
  275. package/dist/routes/store.js.map +1 -1
  276. package/dist/store/catalog.d.ts +13 -5
  277. package/dist/store/catalog.js +19 -9
  278. package/dist/store/catalog.js.map +1 -1
  279. package/dist/store/compatibility.d.ts +1 -0
  280. package/dist/store/compatibility.js +24 -8
  281. package/dist/store/compatibility.js.map +1 -1
  282. package/dist/store/frontend-compatibility.js +15 -3
  283. package/dist/store/frontend-compatibility.js.map +1 -1
  284. package/dist/store/interfaces.js +35 -13
  285. package/dist/store/interfaces.js.map +1 -1
  286. package/dist/store/internal/frontend/frontend-modules.js +7 -3
  287. package/dist/store/internal/frontend/frontend-modules.js.map +1 -1
  288. package/dist/store/internal/frontend/frontend-ready.d.ts +2 -1
  289. package/dist/store/internal/frontend/frontend-ready.js +6 -4
  290. package/dist/store/internal/frontend/frontend-ready.js.map +1 -1
  291. package/dist/store/internal/frontend/manifest.js +6 -7
  292. package/dist/store/internal/frontend/manifest.js.map +1 -1
  293. package/dist/store/operations.js.map +1 -1
  294. package/dist/store/registry.js.map +1 -1
  295. package/dist/store/runtime.d.ts +13 -0
  296. package/dist/store/runtime.js +9 -1
  297. package/dist/store/runtime.js.map +1 -1
  298. package/dist/utils/admin-invite-email.js +3 -1
  299. package/dist/utils/admin-invite-email.js.map +1 -1
  300. package/dist/utils/email-subjects.js +3 -1
  301. package/dist/utils/email-subjects.js.map +1 -1
  302. package/dist/utils/ensure-default-tenant.d.ts +2 -1
  303. package/dist/utils/ensure-default-tenant.js +126 -8
  304. package/dist/utils/ensure-default-tenant.js.map +1 -1
  305. package/dist/utils/export-jobs-run.js.map +1 -1
  306. package/dist/utils/export-jobs.js +3 -9
  307. package/dist/utils/export-jobs.js.map +1 -1
  308. package/dist/utils/index.d.ts +1 -0
  309. package/dist/utils/index.js +1 -0
  310. package/dist/utils/index.js.map +1 -1
  311. package/dist/utils/own-value.d.ts +1 -0
  312. package/dist/utils/own-value.js +7 -0
  313. package/dist/utils/own-value.js.map +1 -0
  314. package/dist/utils/row-action-rule-evaluator.js +31 -35
  315. package/dist/utils/row-action-rule-evaluator.js.map +1 -1
  316. package/dist/utils/sign-in-country.js +4 -2
  317. package/dist/utils/sign-in-country.js.map +1 -1
  318. package/dist/utils/tenant-export-archive.js +9 -14
  319. package/dist/utils/tenant-export-archive.js.map +1 -1
  320. package/dist/utils/user-agent.js +2 -1
  321. package/dist/utils/user-agent.js.map +1 -1
  322. package/dist/validation/auth.schema.d.ts +10 -10
  323. package/dist/validation/onboarding/register-admin.schema.d.ts +5 -0
  324. package/dist/validation/onboarding/register-admin.schema.js +3 -0
  325. package/dist/validation/onboarding/register-admin.schema.js.map +1 -1
  326. package/dist/validation/regional-preferences.schema.d.ts +2 -2
  327. package/dist/validation/role-editor.schema.d.ts +1 -1
  328. package/frontend-vue/build/backend-boundary.ts +49 -18
  329. package/frontend-vue/dms.frontend.ts +8 -12
  330. package/frontend-vue/layers/dms-auth/server/accounts.ts +44 -3
  331. package/frontend-vue/layers/dms-auth/server/actions.ts +17 -4
  332. package/frontend-vue/layers/dms-auth/server/oauth.ts +42 -4
  333. package/frontend-vue/layers/dms-core/app/types/watch.ts +1 -2
  334. package/frontend-vue/layers/dms-core/i18n/locales/core-en-GB.json +1 -0
  335. package/frontend-vue/layers/dms-core/i18n/locales/core-fr-FR.json +1 -0
  336. package/frontend-vue/layers/dms-core/shared/types/app-config.ts +5 -0
  337. package/frontend-vue/layers/dms-layout/app/app.config.ts +1 -0
  338. package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardSidebar.vue +9 -9
  339. package/frontend-vue/layers/dms-layout/app/components/layout/AppLogo.vue +1 -0
  340. package/frontend-vue/layers/dms-layout/app/composables/page/useInstalledModules.ts +78 -0
  341. package/frontend-vue/layers/dms-layout/app/custom-pages/modules.vue +5 -24
  342. package/frontend-vue/layers/dms-layout/i18n/locales/layout-en-GB.json +2 -1
  343. package/frontend-vue/layers/dms-layout/i18n/locales/layout-fr-FR.json +2 -1
  344. package/frontend-vue/layers/dms-onboarding/app/components/onboarding/steps/register.vue +21 -1
  345. package/frontend-vue/layers/dms-onboarding/app/custom-pages/onboarding.vue +14 -0
  346. package/frontend-vue/layers/dms-onboarding/i18n/locales/onboarding-en-GB.json +2 -0
  347. package/frontend-vue/layers/dms-onboarding/i18n/locales/onboarding-fr-FR.json +2 -0
  348. package/frontend-vue/layers/dms-ui/app/build/composables/form/fieldValueRule.ts +57 -6
  349. package/frontend-vue/layers/dms-ui/app/build/composables/form/registerFormFunctions.ts +7 -9
  350. package/frontend-vue/layers/dms-ui/app/entries/composables/form/registerFormFunctions.ts +1 -0
  351. package/frontend-vue/lazy-plugin.ts +18 -0
  352. package/frontend-vue/package.json +2 -2
  353. package/package.json +19 -13
  354. package/skills/dms-project/SKILL.md +4 -3
@@ -10,20 +10,32 @@ const QUERY_OR_HASH = /[?#].*$/;
10
10
 
11
11
  const backendRoots = new Map<string, string | null>();
12
12
 
13
- function packageName(manifest: string): unknown {
13
+ /** Whether a parsed manifest names its package. */
14
+ function isNamedManifest(value: unknown): value is { name: string } {
15
+ return (
16
+ typeof value === "object" &&
17
+ value !== null &&
18
+ "name" in value &&
19
+ typeof value.name === "string"
20
+ );
21
+ }
22
+
23
+ /** The package name a `package.json` declares, or `undefined`. */
24
+ function packageName(manifest: string): string | undefined {
14
25
  try {
15
- const parsed: { name?: unknown } = JSON.parse(
16
- readFileSync(manifest, "utf8"),
17
- );
18
- return parsed.name;
26
+ const parsed: unknown = JSON.parse(readFileSync(manifest, "utf8"));
27
+ return isNamedManifest(parsed) ? parsed.name : undefined;
19
28
  } catch {
20
29
  return undefined;
21
30
  }
22
31
  }
23
32
 
33
+ /** The directory of the backend package that holds `directory`, or null. */
24
34
  function backendRoot(directory: string): string | null {
25
35
  const known = backendRoots.get(directory);
26
- if (known !== undefined) return known;
36
+ if (known !== undefined) {
37
+ return known;
38
+ }
27
39
  const manifest = join(directory, "package.json");
28
40
  const parent = dirname(directory);
29
41
  let root: string | null = null;
@@ -44,7 +56,9 @@ function backendRoot(directory: string): string | null {
44
56
  */
45
57
  function backendSourceFile(id: string): string | undefined {
46
58
  const path = id.replace(QUERY_OR_HASH, "");
47
- if (!isAbsolute(path)) return undefined;
59
+ if (!isAbsolute(path)) {
60
+ return undefined;
61
+ }
48
62
  let real: string;
49
63
  try {
50
64
  real = realpathSync(path);
@@ -52,39 +66,56 @@ function backendSourceFile(id: string): string | undefined {
52
66
  return undefined;
53
67
  }
54
68
  const root = backendRoot(dirname(real));
55
- if (root === null) return undefined;
69
+ if (root === null) {
70
+ return undefined;
71
+ }
56
72
  return relative(root, real).split(sep)[0] === BACKEND_SOURCE
57
73
  ? real
58
74
  : undefined;
59
75
  }
60
76
 
61
77
  /**
62
- * Fails the build, the dev server and the tests when a module of the frontend
63
- * resolves to a file under `packages/dms/src`: the browser reaches the
78
+ * Fails the build and the dev server when a module of the frontend resolves to
79
+ * a file under the `src` of the backend package: the browser reaches the
64
80
  * backend only through its HTTP interface. It checks what the whole resolution
65
- * chain returns, so an alias is seen through and a symlink the resolver
66
- * returns is followed to its real path, and it runs before the other plugins
67
- * (`enforce: "pre"`). The build copies the frontend module with its symlinks
68
- * resolved, so only `pnpm run deps` sees a symlink inside the module.
81
+ * chain returns, an id marked external included, so an alias is seen through
82
+ * and a symlink the resolver returns is followed to its real path, and it runs
83
+ * before the other plugins (`enforce: "pre"`). Vite builds workers with their
84
+ * own plugin list, so the plugin adds itself there too.
85
+ *
86
+ * antelopejs-sync writes this file from `@antelopejs/tooling-configs` and
87
+ * restores it on every run: change the plugin in the pack, not here.
69
88
  */
70
89
  export function backendBoundary(): VitePlugin {
71
90
  return {
72
- name: "dms:backend-boundary",
91
+ ...boundaryCheck(),
92
+ config: () => ({ worker: { plugins: () => [boundaryCheck()] } }),
93
+ };
94
+ }
95
+
96
+ /** The check alone, as a worker build runs it. */
97
+ function boundaryCheck(): VitePlugin {
98
+ return {
99
+ name: `${BACKEND_PACKAGE}:backend-boundary`,
73
100
  enforce: "pre",
74
101
  async resolveId(source, importer, options) {
75
102
  const resolved = await this.resolve(source, importer, {
76
103
  ...options,
77
104
  skipSelf: true,
78
105
  });
79
- if (resolved === null || resolved.external) return resolved;
106
+ if (resolved === null) {
107
+ return resolved;
108
+ }
80
109
  const file = backendSourceFile(resolved.id);
81
- if (file === undefined) return resolved;
110
+ if (file === undefined) {
111
+ return resolved;
112
+ }
82
113
  const from =
83
114
  importer === undefined
84
115
  ? "an entry point"
85
116
  : importer.replace(QUERY_OR_HASH, "");
86
117
  return this.error(
87
- `Backend boundary: ${from} imports ${file}, which is under packages/dms/src. The frontend reaches the backend only through its HTTP interface.`,
118
+ `Backend boundary: ${from} imports ${file}, which is under the src of ${BACKEND_PACKAGE}. The frontend reaches the backend only through its HTTP interface.`,
88
119
  );
89
120
  },
90
121
  };
@@ -8,6 +8,7 @@ import {
8
8
  type DmsFrontendSdk,
9
9
  useDmsRuntimeConfig,
10
10
  } from "#dms/frontend-module";
11
+ import { lazyPlugin } from "./lazy-plugin";
11
12
  import authMiddleware from "./layers/dms-auth/app/middleware/auth";
12
13
  import permissionsPreloadPlugin from "./layers/dms-auth/app/plugins/permissions-preload";
13
14
  import asyncDataScopePlugin from "./layers/dms-core/app/plugins/async-data-scope";
@@ -164,20 +165,15 @@ function registerDeferredPlugins(
164
165
  function registerPlugins(sdk: Parameters<DmsFrontendModule["setup"]>[0]): void {
165
166
  const clientOnly = { clientOnly: true };
166
167
  if (import.meta.env.DEV) {
167
- sdk.registerPlugin(async (context) => {
168
- const plugin = await import(
169
- "./layers/dms-layout/app/plugins/dms-dev-reload.client"
170
- );
171
- await plugin.default(context);
172
- }, clientOnly);
168
+ sdk.registerPlugin(
169
+ lazyPlugin(
170
+ () => import("./layers/dms-layout/app/plugins/dms-dev-reload.client"),
171
+ ),
172
+ clientOnly,
173
+ );
173
174
 
174
175
  sdk.registerPlugin(
175
- async (context) => {
176
- const plugin = await import(
177
- "./layers/dms-core/app/plugins/dev-error-log"
178
- );
179
- await plugin.default(context);
180
- },
176
+ lazyPlugin(() => import("./layers/dms-core/app/plugins/dev-error-log")),
181
177
  { ...clientOnly, enforce: "pre" },
182
178
  );
183
179
  }
@@ -1,6 +1,10 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import type { DmsServerEvent, DmsSession } from "#dms/server";
3
- import { isString } from "../../dms-core/app/utils/type-check";
3
+ import {
4
+ isBoolean,
5
+ isObject,
6
+ isString,
7
+ } from "../../dms-core/app/utils/type-check";
4
8
 
5
9
  /**
6
10
  * The accounts this browser is signed in to: an index of their descriptors,
@@ -68,8 +72,42 @@ function descriptor(session: DmsSession, accountId: string): AccountDescriptor {
68
72
  };
69
73
  }
70
74
 
75
+ function isOptionalText(value: unknown): value is string | undefined {
76
+ return value === undefined || isString(value);
77
+ }
78
+
79
+ function isAccountDescriptor(value: unknown): value is AccountDescriptor {
80
+ return (
81
+ isObject(value) &&
82
+ value !== null &&
83
+ isString(value.accountId) &&
84
+ isOptionalText(value.userId) &&
85
+ isOptionalText(value.email) &&
86
+ isOptionalText(value.name) &&
87
+ isOptionalText(value.activeTenantId) &&
88
+ isBoolean(value.persistent)
89
+ );
90
+ }
91
+
92
+ function isStoredAccount(value: unknown): value is StoredAccount {
93
+ return (
94
+ isAccountDescriptor(value) &&
95
+ isObject(value) &&
96
+ isOptionalText(value.refreshToken)
97
+ );
98
+ }
99
+
100
+ function isAccountIndex(value: unknown): value is AccountIndex {
101
+ return (
102
+ isObject(value) &&
103
+ value !== null &&
104
+ !Array.isArray(value) &&
105
+ Object.values(value).every(isAccountDescriptor)
106
+ );
107
+ }
108
+
71
109
  export function readAccounts(event: DmsServerEvent): AccountIndex {
72
- return event.cookies.readSealed<AccountIndex>(ACCOUNT_INDEX_COOKIE) ?? {};
110
+ return event.cookies.readSealed(ACCOUNT_INDEX_COOKIE, isAccountIndex) ?? {};
73
111
  }
74
112
 
75
113
  function writeAccountIndex(event: DmsServerEvent, accounts: AccountIndex) {
@@ -84,7 +122,10 @@ export function readAccount(
84
122
  accountId: Parameters<typeof isString>[0],
85
123
  ): StoredAccount | undefined {
86
124
  if (!isString(accountId) || !UUID.test(accountId)) return undefined;
87
- return event.cookies.readSealed<StoredAccount>(accountCookieName(accountId));
125
+ return event.cookies.readSealed(
126
+ accountCookieName(accountId),
127
+ isStoredAccount,
128
+ );
88
129
  }
89
130
 
90
131
  export function storeAccount(
@@ -1,4 +1,8 @@
1
- import { isString } from "../../dms-core/app/utils/type-check";
1
+ import {
2
+ isBoolean,
3
+ isObject,
4
+ isString,
5
+ } from "../../dms-core/app/utils/type-check";
2
6
  import type { DmsBackendRequest, DmsServerEvent } from "#dms/server";
3
7
  import {
4
8
  isPersistent,
@@ -30,6 +34,15 @@ interface PendingLogin {
30
34
  persistent: boolean;
31
35
  }
32
36
 
37
+ function isPendingLogin(value: unknown): value is PendingLogin {
38
+ return (
39
+ isObject(value) &&
40
+ value !== null &&
41
+ isString(value.token) &&
42
+ isBoolean(value.persistent)
43
+ );
44
+ }
45
+
33
46
  /**
34
47
  * Calls a backend endpoint that opens a session and, unless it asks for a
35
48
  * second step, writes the session and stores the account.
@@ -51,7 +64,7 @@ async function establishWith(
51
64
  { maxAge: PENDING_LOGIN_LIFETIME_S, path: PENDING_LOGIN_PATH },
52
65
  );
53
66
  if (result.requires_2fa || result.requires_tenant_assignment) return result;
54
- if (event.cookies.readSealed(PENDING_LOGIN))
67
+ if (event.cookies.readSealed(PENDING_LOGIN, isPendingLogin))
55
68
  event.cookies.clear(PENDING_LOGIN, { path: PENDING_LOGIN_PATH });
56
69
  const account = persistAccountSession(event, sessionFrom(result, persistent));
57
70
  return { user: result.user, account };
@@ -83,9 +96,9 @@ export async function signup(event: DmsServerEvent) {
83
96
  */
84
97
  export async function verifyTwoFactor(event: DmsServerEvent) {
85
98
  const input = await event.readJson<Input>();
86
- const pending = event.cookies.readSealed<PendingLogin>(PENDING_LOGIN);
99
+ const pending = event.cookies.readSealed(PENDING_LOGIN, isPendingLogin);
87
100
  const persistent =
88
- isString(input.token) && pending?.token === input.token
101
+ pending !== undefined && pending.token === input.token
89
102
  ? isPersistent(pending)
90
103
  : true;
91
104
  return establishWith(event, "/api/auth/verify-2fa", input, persistent);
@@ -3,7 +3,16 @@ import type { DmsServerEvent } from "#dms/server";
3
3
  import { privateSecret } from "../../../server/private-options";
4
4
  import { persistAccountSession } from "./accounts";
5
5
  import { type SessionTokens, sessionFrom } from "./session";
6
- import { isNumber, isString } from "../../dms-core/app/utils/type-check";
6
+ import type {
7
+ NoHandoff,
8
+ NoWorkspaceHandoff,
9
+ TwoFactorHandoff,
10
+ } from "../shared/oauth";
11
+ import {
12
+ isNumber,
13
+ isObject,
14
+ isString,
15
+ } from "../../dms-core/app/utils/type-check";
7
16
 
8
17
  const FLOW = "dms_oauth_flow";
9
18
  const HANDOFF = "dms_oauth_handoff";
@@ -43,6 +52,35 @@ interface OAuthResult extends SessionTokens {
43
52
 
44
53
  const attempts = new Map<string, RateWindow>();
45
54
 
55
+ function isOAuthFlow(value: unknown): value is OAuthFlow {
56
+ return (
57
+ isObject(value) &&
58
+ value !== null &&
59
+ isString(value.state) &&
60
+ isNumber(value.expiresAt) &&
61
+ isString(value.redirect) &&
62
+ isString(value.language) &&
63
+ isString(value.invite)
64
+ );
65
+ }
66
+
67
+ function isTextList(value: unknown): value is string[] {
68
+ return Array.isArray(value) && value.every(isString);
69
+ }
70
+
71
+ function isOAuthHandoff(
72
+ value: unknown,
73
+ ): value is TwoFactorHandoff | NoWorkspaceHandoff {
74
+ if (!isObject(value) || value === null) return false;
75
+ if (value.kind === "2fa")
76
+ return isString(value.token) && isTextList(value.methods);
77
+ return (
78
+ value.kind === "no-workspace" &&
79
+ isString(value.token) &&
80
+ isString(value.provider)
81
+ );
82
+ }
83
+
46
84
  function sweepAttempts(now: number) {
47
85
  for (const [key, window] of attempts)
48
86
  if (now - window.startedAt >= RATE_WINDOW_MS) attempts.delete(key);
@@ -155,7 +193,6 @@ function sameState(
155
193
  left: OAuthFlow["state"],
156
194
  right: OAuthFlow["state"],
157
195
  ): boolean {
158
- if (!isString(left) || !isString(right)) return false;
159
196
  const a = Buffer.from(left);
160
197
  const b = Buffer.from(right);
161
198
  return a.length === b.length && timingSafeEqual(a, b);
@@ -239,7 +276,7 @@ export async function oauthCallback(event: DmsServerEvent): Promise<Response> {
239
276
  const { provider } = event.params;
240
277
  if (!isValidOAuthProvider(provider) || rateLimited(event, "callback"))
241
278
  return errorRedirect(FAILED);
242
- const flow = event.cookies.readSealed<OAuthFlow>(FLOW);
279
+ const flow = event.cookies.readSealed(FLOW, isOAuthFlow);
243
280
  event.cookies.clear(FLOW, { path: FLOW_PATH });
244
281
  const query = event.url.searchParams;
245
282
  if (query.has("error")) return errorRedirect("error.oauth.cancelled");
@@ -253,7 +290,8 @@ export async function oauthCallback(event: DmsServerEvent): Promise<Response> {
253
290
  * finish (a 2FA step, a workspace to pick), once.
254
291
  */
255
292
  export function oauthHandoff(event: DmsServerEvent) {
256
- const handoff = event.cookies.readSealed(HANDOFF) ?? { kind: "none" };
293
+ const handoff: TwoFactorHandoff | NoWorkspaceHandoff | NoHandoff =
294
+ event.cookies.readSealed(HANDOFF, isOAuthHandoff) ?? { kind: "none" };
257
295
  event.cookies.clear(HANDOFF, { path: FLOW_PATH });
258
296
  return handoff;
259
297
  }
@@ -21,7 +21,6 @@ export interface WatchAction {
21
21
  component: string;
22
22
  event: string;
23
23
  functionId: string;
24
- // oxlint-disable-next-line anti-slop/no-unsafe-dictionary-type -- Registered action functions own their heterogeneous parameters; no shared schema or ComponentOptionsData contract covers all public action callers.
25
- params?: Record<string, unknown>;
24
+ params?: object;
26
25
  onParam?: WatchActionCondition | WatchActionCondition[];
27
26
  }
@@ -64,6 +64,7 @@
64
64
  "invite_used": "This invitation was already used. Please log in.",
65
65
  "onboarding_already_completed": "Onboarding already completed.",
66
66
  "admin_already_set": "An administrator account already exists.",
67
+ "invalid_setup_token": "The setup token is missing or wrong. Use the address in the server log, or the value of DMS_SETUP_TOKEN.",
67
68
  "user_creation_failed": "The user account could not be created. Please try again.",
68
69
  "settings_not_found": "General settings not found.",
69
70
  "token_expired": "The validation code has expired.",
@@ -64,6 +64,7 @@
64
64
  "invite_used": "Cette invitation a déjà été utilisée. Veuillez vous connecter.",
65
65
  "onboarding_already_completed": "L'onboarding a déjà été completé.",
66
66
  "admin_already_set": "Un compte administrateur existe déjà.",
67
+ "invalid_setup_token": "Le jeton d'installation est absent ou incorrect. Utilisez l'adresse affichée dans les logs du serveur, ou la valeur de DMS_SETUP_TOKEN.",
67
68
  "user_creation_failed": "Le compte utilisateur n'a pas pu être créé. Veuillez réessayer.",
68
69
  "settings_not_found": "Les paramètres généraux n'ont pas été trouvés.",
69
70
  "token_expired": "Le code de validation a expiré.",
@@ -14,6 +14,11 @@ export interface DmsLogoSources {
14
14
 
15
15
  /** The logos of an instance, one per place it is drawn. */
16
16
  export interface DmsBrandingLogo {
17
+ /**
18
+ * The name a screen reader announces for the logo: the application's name.
19
+ * Set it with a logo of your own.
20
+ */
21
+ alt?: string;
17
22
  /** The expanded sidebar and the auth pages. */
18
23
  default: DmsLogoSources;
19
24
  /** The icon-only sidebar. */
@@ -30,6 +30,7 @@ function dmsColors(): DmsUiColors {
30
30
 
31
31
  function dmsLogo(): DmsBrandingLogo {
32
32
  return {
33
+ alt: "Antelope DMS",
33
34
  default: {
34
35
  light: "/images/antelope-logo/light.svg",
35
36
  dark: "/images/antelope-logo/dark.svg",
@@ -13,6 +13,7 @@ import { useSidebarState } from "./sidebarState";
13
13
  import { useKeyboardPlatform } from "#dms-ui/app/composables/global/keyboardPlatform";
14
14
  import { useMediaQuery } from "@vueuse/core";
15
15
  import DmsSidebarUserMenu from "./SidebarUserMenu.vue";
16
+ import { useInstalledModuleCount } from "../../../composables/page/useInstalledModules";
16
17
 
17
18
  const MODULES_PATH = "/modules";
18
19
 
@@ -205,14 +206,7 @@ watch(
205
206
  { immediate: true },
206
207
  );
207
208
 
208
- const moduleCount = computed(() => {
209
- const modulesNode =
210
- siteLayout.siteLayoutTree.value?.children[MODULES_TREE_KEY];
211
- if (!modulesNode) return 0;
212
- return modulesNode.childrenOrders.filter(
213
- (childId) => modulesNode.children[childId]?.isModuleRoot,
214
- ).length;
215
- });
209
+ const moduleCount = useInstalledModuleCount();
216
210
 
217
211
  const footerItems = computed((): NavigationMenuItem[] => {
218
212
  const result: NavigationMenuItem[] = [];
@@ -297,7 +291,13 @@ const footerItems = computed((): NavigationMenuItem[] => {
297
291
  "
298
292
  :class="isRail(sidebarRail) ? 'size-7' : 'h-12 w-auto'"
299
293
  class="object-contain"
300
- :alt="t('navigation.goToHomepage')"
294
+ :alt="
295
+ appConfig.branding?.logo?.alt
296
+ ? t('navigation.goToBrandHomepage', {
297
+ name: appConfig.branding.logo.alt,
298
+ })
299
+ : t('navigation.goToHomepage')
300
+ "
301
301
  />
302
302
  </DmsLink>
303
303
  </template>
@@ -7,5 +7,6 @@ const appConfig = useDmsAppConfig();
7
7
  v-if="appConfig.branding.logo"
8
8
  :light="appConfig.branding?.logo?.default.light"
9
9
  :dark="appConfig.branding?.logo?.default.dark"
10
+ :alt="appConfig.branding.logo.alt"
10
11
  />
11
12
  </template>
@@ -0,0 +1,78 @@
1
+ import type { StoreListing } from "#dms-core/app/types/module-store";
2
+ import type { ModuleCatalogEntry } from "#dms-core/app/types/page";
3
+ import type { Ref } from "vue";
4
+ import {
5
+ sortStoreModules,
6
+ storeOnlyModules,
7
+ toModuleStoreEntry,
8
+ withStoreDetails,
9
+ } from "../../build/utils/module-store-catalog";
10
+ import { ENDPOINT_STORE_LISTING } from "./useModuleStore";
11
+ import { useModulesListing } from "./useModulesListing";
12
+
13
+ /**
14
+ * The installed modules, as the Modules page lists them and the sidebar
15
+ * counts them: the modules that register pages, with the store's details,
16
+ * and the installed store modules that register none, such as the Builder.
17
+ * Both read it here, so the badge and the page never count differently.
18
+ *
19
+ * @param modulesListing The registered modules
20
+ * @param moduleStore The module store
21
+ */
22
+ export const useInstalledModules = (
23
+ modulesListing: ReturnType<typeof useModulesListing>,
24
+ moduleStore: { listing: Readonly<Ref<StoreListing | undefined>> },
25
+ ) => {
26
+ const { locale } = useI18n();
27
+ const { processI18n } = useTranslation();
28
+
29
+ const storeModules = computed(() =>
30
+ sortStoreModules(
31
+ (moduleStore.listing.value?.modules ?? []).map((entry) =>
32
+ toModuleStoreEntry(entry, modulesListing.modules.value ?? []),
33
+ ),
34
+ processI18n,
35
+ locale.value,
36
+ ),
37
+ );
38
+
39
+ const modules = computed<ModuleCatalogEntry[]>(() => {
40
+ const registered = withStoreDetails(
41
+ (modulesListing.modules.value ?? []).filter((entry) => entry.hasAccess),
42
+ storeModules.value,
43
+ );
44
+ return [...registered, ...storeOnlyModules(storeModules.value, registered)];
45
+ });
46
+
47
+ return { storeModules, modules };
48
+ };
49
+
50
+ /**
51
+ * How many modules are installed, for the sidebar's badge. The store listing
52
+ * waits on the npm registry, so a server render must not wait for it: the
53
+ * browser reads it, and the count shows once mounted and the read is over,
54
+ * whether it answered or failed. Until then the badge is absent, on the server
55
+ * and on the browser's first render, which a page that handed the listing
56
+ * over (the Modules page) would otherwise have filled in and mismatched, and
57
+ * while the read runs, so the count never shows without the store's modules.
58
+ */
59
+ export const useInstalledModuleCount = () => {
60
+ const isOwner = useIsOwner();
61
+ const storeListing = useApiData<StoreListing>(
62
+ () => (isOwner.value ? ENDPOINT_STORE_LISTING : null),
63
+ { server: false },
64
+ );
65
+ const { modules } = useInstalledModules(useModulesListing(), {
66
+ listing: computed(() => storeListing.data.value ?? undefined),
67
+ });
68
+ const isMounted = ref(false);
69
+ onMounted(() => (isMounted.value = true));
70
+ const isSettled = computed(
71
+ () =>
72
+ storeListing.status.value === "success" ||
73
+ storeListing.status.value === "error",
74
+ );
75
+ return computed(() =>
76
+ isMounted.value && isSettled.value ? modules.value.length : 0,
77
+ );
78
+ };
@@ -20,7 +20,6 @@ import {
20
20
  useModulesListing,
21
21
  } from "../composables/page/useModulesListing";
22
22
  import { useModuleHistory } from "../composables/page/useModuleHistory";
23
- import type { ModuleCatalogEntry } from "#dms-core/app/types/page";
24
23
  import {
25
24
  MODULE_CATEGORY_ALL,
26
25
  MODULE_STORE_PATH,
@@ -33,13 +32,8 @@ import {
33
32
  summarizeModules,
34
33
  type ModuleSortMode,
35
34
  } from "../build/utils/modules-catalog";
36
- import {
37
- sortStoreModules,
38
- storeOnlyModules,
39
- storeOpenTarget,
40
- toModuleStoreEntry,
41
- withStoreDetails,
42
- } from "../build/utils/module-store-catalog";
35
+ import { storeOpenTarget } from "../build/utils/module-store-catalog";
36
+ import { useInstalledModules } from "../composables/page/useInstalledModules";
43
37
  import { useModuleStore } from "../composables/page/useModuleStore";
44
38
  import { useModuleStoreActions } from "../composables/page/useModuleStoreActions";
45
39
  import DmsSearchInput from "#dms-ui/app/entries/components/form/SearchInput";
@@ -123,26 +117,13 @@ const isInitiallyLoading = computed(
123
117
  );
124
118
  const isRetrying = ref(false);
125
119
 
126
- const storeModules = computed(() =>
127
- sortStoreModules(
128
- (moduleStore.listing.value?.modules ?? []).map((entry) =>
129
- toModuleStoreEntry(entry, modulesListing.modules.value ?? []),
130
- ),
131
- processI18n,
132
- locale.value,
133
- ),
120
+ const { storeModules, modules } = useInstalledModules(
121
+ modulesListing,
122
+ moduleStore,
134
123
  );
135
124
  const storeEntries = computed(
136
125
  () => new Map(storeModules.value.map((entry) => [entry.id, entry])),
137
126
  );
138
-
139
- const modules = computed<ModuleCatalogEntry[]>(() => {
140
- const registered = withStoreDetails(
141
- (modulesListing.modules.value ?? []).filter((entry) => entry.hasAccess),
142
- storeModules.value,
143
- );
144
- return [...registered, ...storeOnlyModules(storeModules.value, registered)];
145
- });
146
127
  const summary = computed(() => summarizeModules(modules.value));
147
128
 
148
129
  const visitedAt = computed(
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "navigation": {
3
- "goToHomepage": "Go to homepage"
3
+ "goToHomepage": "Go to homepage",
4
+ "goToBrandHomepage": "{name}, go to homepage"
4
5
  },
5
6
  "header": {
6
7
  "home": "Home",
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "navigation": {
3
- "goToHomepage": "Aller à la page d'accueil"
3
+ "goToHomepage": "Aller à la page d'accueil",
4
+ "goToBrandHomepage": "{name}, aller à la page d'accueil"
4
5
  },
5
6
  "header": {
6
7
  "home": "Accueil",
@@ -23,6 +23,8 @@ import {
23
23
 
24
24
  interface RegisterStepProps {
25
25
  platform: OnboardingPlatform;
26
+ /** The token the address carried; empty when the wizard has to ask for it. */
27
+ setupToken: string;
26
28
  }
27
29
 
28
30
  interface RegisterStepEmits {
@@ -43,6 +45,7 @@ const MAX_NAME_PART_LENGTH = 100;
43
45
 
44
46
  const isLoading = ref(false);
45
47
  const isRegistered = ref(false);
48
+ const asksForSetupToken = !props.setupToken;
46
49
 
47
50
  const namePart = z.string().trim().min(1).max(MAX_NAME_PART_LENGTH);
48
51
 
@@ -52,12 +55,14 @@ const fields = z
52
55
  lastName: namePart,
53
56
  email: z.string().trim().email(),
54
57
  password: passwordSchema,
58
+ setupToken: z.string().trim().min(1),
55
59
  })
56
60
  .transform((data) => ({
57
61
  firstName: striptags(data.firstName),
58
62
  lastName: striptags(data.lastName),
59
63
  email: striptags(data.email),
60
64
  password: data.password,
65
+ setupToken: data.setupToken,
61
66
  }));
62
67
 
63
68
  type Schema = z.output<typeof fields>;
@@ -68,6 +73,7 @@ const state = reactive<Partial<Schema>>({
68
73
  lastName: undefined,
69
74
  email: undefined,
70
75
  password: undefined,
76
+ setupToken: props.setupToken,
71
77
  });
72
78
  useLiveFormErrors(form, state);
73
79
 
@@ -106,7 +112,7 @@ async function onSubmit(event: FormSubmitEvent<Schema>) {
106
112
  }
107
113
 
108
114
  await showError(error, "page.onboarding.administrator.error_title", {
109
- fields: ["firstName", "lastName", "email", "password"],
115
+ fields: ["firstName", "lastName", "email", "password", "setupToken"],
110
116
  form,
111
117
  });
112
118
  } finally {
@@ -139,6 +145,20 @@ async function onSubmit(event: FormSubmitEvent<Schema>) {
139
145
  >
140
146
  <AuthFormAlert :error="formError" />
141
147
 
148
+ <UFormField
149
+ v-if="asksForSetupToken"
150
+ :label="$t('page.onboarding.administrator.setup_token')"
151
+ :help="$t('page.onboarding.administrator.setup_token_help')"
152
+ name="setupToken"
153
+ >
154
+ <UInput
155
+ v-model="state.setupToken"
156
+ autocomplete="off"
157
+ size="lg"
158
+ class="w-full"
159
+ />
160
+ </UFormField>
161
+
142
162
  <div class="grid gap-3 sm:grid-cols-2">
143
163
  <UFormField
144
164
  :label="$t('page.onboarding.administrator.first_name')"