@byline/host-tanstack-start 5.0.0 → 5.1.1

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 (258) hide show
  1. package/dist/admin-shell/chrome/admin-app-bar.d.ts +7 -0
  2. package/dist/admin-shell/chrome/admin-app-bar.js +10 -2
  3. package/dist/admin-shell/chrome/optional-i18n-provider.d.ts +29 -0
  4. package/dist/admin-shell/chrome/optional-i18n-provider.js +24 -0
  5. package/dist/admin-shell/chrome/session-change-boundary.d.ts +14 -0
  6. package/dist/admin-shell/chrome/session-change-boundary.js +119 -0
  7. package/dist/admin-shell/chrome/session-change-boundary.module.js +11 -0
  8. package/dist/admin-shell/chrome/session-change-boundary.test.d.ts +8 -0
  9. package/dist/admin-shell/chrome/session-change-boundary_module.css +81 -0
  10. package/dist/admin-shell/chrome/sign-in-page.d.ts +2 -1
  11. package/dist/admin-shell/chrome/sign-in-page.js +60 -3
  12. package/dist/integrations/byline-admin-services.js +7 -1
  13. package/dist/integrations/client-ip.d.ts +19 -0
  14. package/dist/integrations/client-ip.js +34 -0
  15. package/dist/integrations/session-coordination.d.ts +24 -0
  16. package/dist/integrations/session-coordination.js +85 -0
  17. package/dist/integrations/session-middleware.d.ts +11 -0
  18. package/dist/integrations/session-middleware.js +94 -0
  19. package/dist/integrations/session-request-kind.d.ts +9 -0
  20. package/dist/integrations/session-request-kind.js +10 -0
  21. package/dist/integrations/session-request-middleware.d.ts +9 -0
  22. package/dist/integrations/session-request-middleware.js +7 -0
  23. package/dist/integrations/sign-in-body.d.ts +12 -0
  24. package/dist/integrations/sign-in-body.js +52 -0
  25. package/dist/integrations/sign-in-middleware.d.ts +9 -0
  26. package/dist/integrations/sign-in-middleware.js +10 -0
  27. package/dist/routes/create-admin-layout-route.js +46 -31
  28. package/dist/routes/create-sign-in-route.js +4 -2
  29. package/dist/server-fns/admin-account/change-password.d.ts +3 -1
  30. package/dist/server-fns/admin-account/change-password.js +9 -3
  31. package/dist/server-fns/admin-account/get.d.ts +3 -1
  32. package/dist/server-fns/admin-account/get.js +4 -1
  33. package/dist/server-fns/admin-account/index.d.ts +5 -5
  34. package/dist/server-fns/admin-account/update.d.ts +3 -1
  35. package/dist/server-fns/admin-account/update.js +4 -1
  36. package/dist/server-fns/admin-activity/get.d.ts +3 -1
  37. package/dist/server-fns/admin-activity/get.js +4 -1
  38. package/dist/server-fns/admin-permissions/get-role-abilities.d.ts +3 -1
  39. package/dist/server-fns/admin-permissions/get-role-abilities.js +4 -1
  40. package/dist/server-fns/admin-permissions/index.d.ts +4 -4
  41. package/dist/server-fns/admin-permissions/list-registered.d.ts +3 -1
  42. package/dist/server-fns/admin-permissions/list-registered.js +4 -1
  43. package/dist/server-fns/admin-permissions/set-role-abilities.d.ts +3 -1
  44. package/dist/server-fns/admin-permissions/set-role-abilities.js +4 -1
  45. package/dist/server-fns/admin-permissions/who-has.d.ts +3 -1
  46. package/dist/server-fns/admin-permissions/who-has.js +4 -1
  47. package/dist/server-fns/admin-roles/create.d.ts +3 -1
  48. package/dist/server-fns/admin-roles/create.js +4 -1
  49. package/dist/server-fns/admin-roles/delete.d.ts +3 -1
  50. package/dist/server-fns/admin-roles/delete.js +4 -1
  51. package/dist/server-fns/admin-roles/get.d.ts +3 -1
  52. package/dist/server-fns/admin-roles/get.js +4 -1
  53. package/dist/server-fns/admin-roles/index.d.ts +6 -6
  54. package/dist/server-fns/admin-roles/list.d.ts +3 -1
  55. package/dist/server-fns/admin-roles/list.js +4 -1
  56. package/dist/server-fns/admin-roles/reorder.d.ts +3 -1
  57. package/dist/server-fns/admin-roles/reorder.js +4 -1
  58. package/dist/server-fns/admin-roles/update.d.ts +3 -1
  59. package/dist/server-fns/admin-roles/update.js +4 -1
  60. package/dist/server-fns/admin-users/create.d.ts +3 -1
  61. package/dist/server-fns/admin-users/create.js +4 -1
  62. package/dist/server-fns/admin-users/delete.d.ts +3 -1
  63. package/dist/server-fns/admin-users/delete.js +4 -1
  64. package/dist/server-fns/admin-users/disable.d.ts +3 -1
  65. package/dist/server-fns/admin-users/disable.js +4 -1
  66. package/dist/server-fns/admin-users/enable.d.ts +3 -1
  67. package/dist/server-fns/admin-users/enable.js +4 -1
  68. package/dist/server-fns/admin-users/get-user-roles.d.ts +3 -1
  69. package/dist/server-fns/admin-users/get-user-roles.js +4 -1
  70. package/dist/server-fns/admin-users/get.d.ts +3 -1
  71. package/dist/server-fns/admin-users/get.js +4 -1
  72. package/dist/server-fns/admin-users/index.d.ts +10 -10
  73. package/dist/server-fns/admin-users/list.d.ts +3 -1
  74. package/dist/server-fns/admin-users/list.js +4 -1
  75. package/dist/server-fns/admin-users/set-password.d.ts +3 -1
  76. package/dist/server-fns/admin-users/set-password.js +4 -1
  77. package/dist/server-fns/admin-users/set-user-roles.d.ts +3 -1
  78. package/dist/server-fns/admin-users/set-user-roles.js +4 -1
  79. package/dist/server-fns/admin-users/update.d.ts +3 -1
  80. package/dist/server-fns/admin-users/update.js +4 -1
  81. package/dist/server-fns/ai/execute.d.ts +3 -1
  82. package/dist/server-fns/ai/execute.js +4 -1
  83. package/dist/server-fns/ai/index.d.ts +1 -1
  84. package/dist/server-fns/analytics/maintenance.d.ts +6 -2
  85. package/dist/server-fns/analytics/maintenance.js +7 -2
  86. package/dist/server-fns/analytics/queries.d.ts +18 -6
  87. package/dist/server-fns/analytics/queries.js +19 -6
  88. package/dist/server-fns/auth/current-user.d.ts +1 -0
  89. package/dist/server-fns/auth/current-user.js +5 -2
  90. package/dist/server-fns/auth/renew.d.ts +13 -0
  91. package/dist/server-fns/auth/renew.js +45 -0
  92. package/dist/server-fns/auth/sign-in.d.ts +2 -1
  93. package/dist/server-fns/auth/sign-in.js +88 -17
  94. package/dist/server-fns/auth/sign-out.d.ts +3 -1
  95. package/dist/server-fns/auth/sign-out.js +38 -6
  96. package/dist/server-fns/collections/audit.d.ts +3 -1
  97. package/dist/server-fns/collections/audit.js +4 -1
  98. package/dist/server-fns/collections/copy-to-locale.d.ts +10 -1
  99. package/dist/server-fns/collections/copy-to-locale.js +5 -2
  100. package/dist/server-fns/collections/create.d.ts +10 -1
  101. package/dist/server-fns/collections/create.js +5 -2
  102. package/dist/server-fns/collections/delete-locale.d.ts +10 -1
  103. package/dist/server-fns/collections/delete-locale.js +5 -2
  104. package/dist/server-fns/collections/delete.d.ts +10 -1
  105. package/dist/server-fns/collections/delete.js +5 -2
  106. package/dist/server-fns/collections/duplicate.d.ts +10 -1
  107. package/dist/server-fns/collections/duplicate.js +5 -2
  108. package/dist/server-fns/collections/get.d.ts +7 -0
  109. package/dist/server-fns/collections/get.js +8 -3
  110. package/dist/server-fns/collections/history.d.ts +3 -1
  111. package/dist/server-fns/collections/history.js +4 -1
  112. package/dist/server-fns/collections/index.d.ts +21 -21
  113. package/dist/server-fns/collections/list.d.ts +10 -1
  114. package/dist/server-fns/collections/list.js +5 -2
  115. package/dist/server-fns/collections/reindex.d.ts +3 -1
  116. package/dist/server-fns/collections/reindex.js +4 -1
  117. package/dist/server-fns/collections/reorder.d.ts +10 -1
  118. package/dist/server-fns/collections/reorder.js +5 -2
  119. package/dist/server-fns/collections/restore-version.d.ts +10 -1
  120. package/dist/server-fns/collections/restore-version.js +5 -2
  121. package/dist/server-fns/collections/scheduled-publication.d.ts +22 -5
  122. package/dist/server-fns/collections/scheduled-publication.js +17 -6
  123. package/dist/server-fns/collections/set-list-view-preference.d.ts +3 -1
  124. package/dist/server-fns/collections/set-list-view-preference.js +4 -1
  125. package/dist/server-fns/collections/stats.js +4 -1
  126. package/dist/server-fns/collections/status.d.ts +13 -2
  127. package/dist/server-fns/collections/status.js +8 -3
  128. package/dist/server-fns/collections/tree.d.ts +22 -5
  129. package/dist/server-fns/collections/tree.js +17 -6
  130. package/dist/server-fns/collections/update.d.ts +13 -2
  131. package/dist/server-fns/collections/update.js +8 -3
  132. package/dist/server-fns/collections/upload.d.ts +10 -1
  133. package/dist/server-fns/collections/upload.js +4 -1
  134. package/dist/server-fns/i18n/set-locale.d.ts +3 -1
  135. package/dist/server-fns/i18n/set-locale.js +18 -3
  136. package/dist/server-fns/preview/disable.d.ts +3 -1
  137. package/dist/server-fns/preview/disable.js +4 -1
  138. package/dist/server-fns/preview/enable.d.ts +3 -1
  139. package/dist/server-fns/preview/enable.js +4 -1
  140. package/dist/server-fns/singletons/cancel-scheduled-publish.d.ts +10 -1
  141. package/dist/server-fns/singletons/cancel-scheduled-publish.js +5 -2
  142. package/dist/server-fns/singletons/change-status.d.ts +10 -1
  143. package/dist/server-fns/singletons/change-status.js +5 -2
  144. package/dist/server-fns/singletons/confirm-scheduled-publish.d.ts +10 -1
  145. package/dist/server-fns/singletons/confirm-scheduled-publish.js +5 -2
  146. package/dist/server-fns/singletons/copy-to-locale.d.ts +10 -1
  147. package/dist/server-fns/singletons/copy-to-locale.js +5 -2
  148. package/dist/server-fns/singletons/find-by-version.d.ts +3 -1
  149. package/dist/server-fns/singletons/find-by-version.js +4 -1
  150. package/dist/server-fns/singletons/get-scheduled-publish.d.ts +3 -1
  151. package/dist/server-fns/singletons/get-scheduled-publish.js +4 -1
  152. package/dist/server-fns/singletons/get.d.ts +3 -1
  153. package/dist/server-fns/singletons/get.js +4 -1
  154. package/dist/server-fns/singletons/history.d.ts +3 -1
  155. package/dist/server-fns/singletons/history.js +4 -1
  156. package/dist/server-fns/singletons/restore-version.d.ts +10 -1
  157. package/dist/server-fns/singletons/restore-version.js +5 -2
  158. package/dist/server-fns/singletons/schedule-publish.d.ts +10 -1
  159. package/dist/server-fns/singletons/schedule-publish.js +5 -2
  160. package/dist/server-fns/singletons/unpublish.d.ts +10 -1
  161. package/dist/server-fns/singletons/unpublish.js +5 -2
  162. package/dist/server-fns/singletons/update.d.ts +10 -1
  163. package/dist/server-fns/singletons/update.js +5 -2
  164. package/package.json +19 -19
  165. package/src/admin-shell/chrome/admin-app-bar.tsx +11 -6
  166. package/src/admin-shell/chrome/optional-i18n-provider.tsx +61 -0
  167. package/src/admin-shell/chrome/session-change-boundary.module.css +112 -0
  168. package/src/admin-shell/chrome/session-change-boundary.test.tsx +91 -0
  169. package/src/admin-shell/chrome/session-change-boundary.tsx +155 -0
  170. package/src/admin-shell/chrome/sign-in-page.tsx +68 -3
  171. package/src/integrations/byline-admin-services.ts +7 -1
  172. package/src/integrations/client-ip.test.node.ts +45 -0
  173. package/src/integrations/client-ip.ts +54 -0
  174. package/src/integrations/framework-fetch-shape.test.node.ts +56 -0
  175. package/src/integrations/session-client.test.node.ts +121 -0
  176. package/src/integrations/session-coordination.test.node.ts +77 -0
  177. package/src/integrations/session-coordination.ts +96 -0
  178. package/src/integrations/session-middleware.test.node.ts +87 -0
  179. package/src/integrations/session-middleware.ts +107 -0
  180. package/src/integrations/session-request-kind.ts +18 -0
  181. package/src/integrations/session-request-middleware.ts +17 -0
  182. package/src/integrations/sign-in-body.test.node.ts +59 -0
  183. package/src/integrations/sign-in-body.ts +50 -0
  184. package/src/integrations/sign-in-middleware.ts +24 -0
  185. package/src/routes/create-admin-layout-route.tsx +48 -30
  186. package/src/routes/create-sign-in-route.tsx +10 -2
  187. package/src/server-fns/admin-account/change-password.test.node.ts +69 -0
  188. package/src/server-fns/admin-account/change-password.ts +13 -2
  189. package/src/server-fns/admin-account/get.ts +2 -0
  190. package/src/server-fns/admin-account/update.ts +2 -0
  191. package/src/server-fns/admin-activity/get.ts +2 -0
  192. package/src/server-fns/admin-permissions/get-role-abilities.ts +2 -0
  193. package/src/server-fns/admin-permissions/list-registered.ts +5 -4
  194. package/src/server-fns/admin-permissions/set-role-abilities.ts +2 -0
  195. package/src/server-fns/admin-permissions/who-has.ts +2 -0
  196. package/src/server-fns/admin-roles/create.ts +2 -0
  197. package/src/server-fns/admin-roles/delete.ts +2 -0
  198. package/src/server-fns/admin-roles/get.ts +2 -0
  199. package/src/server-fns/admin-roles/list.ts +5 -4
  200. package/src/server-fns/admin-roles/reorder.ts +2 -0
  201. package/src/server-fns/admin-roles/update.ts +2 -0
  202. package/src/server-fns/admin-users/create.ts +2 -0
  203. package/src/server-fns/admin-users/delete.ts +2 -0
  204. package/src/server-fns/admin-users/disable.ts +2 -0
  205. package/src/server-fns/admin-users/enable.ts +2 -0
  206. package/src/server-fns/admin-users/get-user-roles.ts +2 -0
  207. package/src/server-fns/admin-users/get.ts +2 -0
  208. package/src/server-fns/admin-users/list.ts +2 -0
  209. package/src/server-fns/admin-users/set-password.ts +2 -0
  210. package/src/server-fns/admin-users/set-user-roles.ts +2 -0
  211. package/src/server-fns/admin-users/update.ts +2 -0
  212. package/src/server-fns/ai/execute.ts +3 -0
  213. package/src/server-fns/analytics/maintenance.ts +4 -0
  214. package/src/server-fns/analytics/queries.ts +14 -8
  215. package/src/server-fns/auth/current-user.ts +7 -3
  216. package/src/server-fns/auth/renew.test.node.ts +85 -0
  217. package/src/server-fns/auth/renew.ts +54 -0
  218. package/src/server-fns/auth/sign-in.test.node.ts +114 -0
  219. package/src/server-fns/auth/sign-in.ts +80 -27
  220. package/src/server-fns/auth/sign-out.test.node.ts +85 -0
  221. package/src/server-fns/auth/sign-out.ts +44 -19
  222. package/src/server-fns/collections/audit.ts +2 -0
  223. package/src/server-fns/collections/copy-to-locale.ts +3 -1
  224. package/src/server-fns/collections/create.ts +3 -1
  225. package/src/server-fns/collections/delete-locale.ts +3 -1
  226. package/src/server-fns/collections/delete.ts +3 -1
  227. package/src/server-fns/collections/duplicate.ts +3 -1
  228. package/src/server-fns/collections/get.ts +4 -1
  229. package/src/server-fns/collections/history.ts +2 -0
  230. package/src/server-fns/collections/list.ts +3 -1
  231. package/src/server-fns/collections/reindex.ts +2 -0
  232. package/src/server-fns/collections/reorder.ts +3 -1
  233. package/src/server-fns/collections/restore-version.ts +3 -1
  234. package/src/server-fns/collections/revision-input.test.node.ts +14 -0
  235. package/src/server-fns/collections/scheduled-publication.ts +14 -7
  236. package/src/server-fns/collections/set-list-view-preference.ts +2 -0
  237. package/src/server-fns/collections/stats.ts +2 -0
  238. package/src/server-fns/collections/status.ts +4 -1
  239. package/src/server-fns/collections/tree.ts +7 -1
  240. package/src/server-fns/collections/update.ts +4 -1
  241. package/src/server-fns/collections/upload-singleton.test.node.ts +8 -1
  242. package/src/server-fns/collections/upload.ts +10 -0
  243. package/src/server-fns/i18n/set-locale.ts +19 -3
  244. package/src/server-fns/preview/disable.ts +8 -4
  245. package/src/server-fns/preview/enable.ts +9 -5
  246. package/src/server-fns/singletons/cancel-scheduled-publish.ts +3 -1
  247. package/src/server-fns/singletons/change-status.ts +3 -1
  248. package/src/server-fns/singletons/confirm-scheduled-publish.ts +3 -1
  249. package/src/server-fns/singletons/copy-to-locale.ts +3 -1
  250. package/src/server-fns/singletons/find-by-version.ts +2 -0
  251. package/src/server-fns/singletons/get-scheduled-publish.ts +2 -0
  252. package/src/server-fns/singletons/get.ts +2 -0
  253. package/src/server-fns/singletons/history.ts +2 -0
  254. package/src/server-fns/singletons/restore-version.ts +3 -1
  255. package/src/server-fns/singletons/schedule-publish.ts +3 -1
  256. package/src/server-fns/singletons/singletons.test.node.ts +6 -0
  257. package/src/server-fns/singletons/unpublish.ts +3 -1
  258. package/src/server-fns/singletons/update.ts +3 -1
@@ -1,3 +1,10 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
1
8
  import { type CurrentAdminUser } from '../../server-fns/auth/index.js';
2
9
  interface AdminAppBarProps {
3
10
  user: CurrentAdminUser;
@@ -22,13 +22,17 @@ function AdminAppBar({ user }) {
22
22
  const { breadCrumbSettings } = useBreadcrumbs();
23
23
  const { t } = useTranslation('byline-admin');
24
24
  const [signingOut, setSigningOut] = useState(false);
25
+ const [signOutError, setSignOutError] = useState(false);
25
26
  async function handleSignOut() {
26
27
  if (signingOut) return;
27
28
  setSigningOut(true);
29
+ setSignOutError(false);
28
30
  try {
29
31
  await adminSignOut();
30
- } catch (err) {
31
- console.warn('sign-out request failed', err);
32
+ } catch {
33
+ setSignOutError(true);
34
+ setSigningOut(false);
35
+ return;
32
36
  }
33
37
  window.location.assign(getSignInRoutePath());
34
38
  }
@@ -62,6 +66,10 @@ function AdminAppBar({ user }) {
62
66
  })
63
67
  ]
64
68
  }),
69
+ signOutError && /*#__PURE__*/ jsx("span", {
70
+ role: "alert",
71
+ children: "Sign-out could not be confirmed. Please try again."
72
+ }),
65
73
  /*#__PURE__*/ jsx(Button, {
66
74
  size: "xs",
67
75
  intent: "secondary",
@@ -0,0 +1,29 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+ import { type ReactNode } from 'react';
9
+ /**
10
+ * Guarantees a translation context for chrome that can render *outside*
11
+ * the admin layout's `<I18nProvider>`.
12
+ *
13
+ * When a provider is already above us it is reused as-is, preserving the
14
+ * user's active locale. Only when none is present does this self-mount.
15
+ *
16
+ * Resilient by design: components using this are the ones shown when
17
+ * something has already gone wrong, so throwing here would mask the real
18
+ * condition. If the admin config can't be read it falls back to an
19
+ * English-default provider with an empty bundle — with a provider always
20
+ * present, `useTranslation` never throws, and a missing key renders as the
21
+ * raw key rather than crashing (see `I18nProvider`'s `onMissing`).
22
+ *
23
+ * `route-error.tsx` carries its own equivalent copy of this logic; it was
24
+ * left in place deliberately rather than refactored alongside an unrelated
25
+ * change, and can adopt this component whenever that file is next touched.
26
+ */
27
+ export declare function OptionalI18nProvider({ children }: {
28
+ children: ReactNode;
29
+ }): import("react").JSX.Element;
@@ -0,0 +1,24 @@
1
+ import { Fragment, jsx } from "react/jsx-runtime";
2
+ import { useContext } from "react";
3
+ import { getAdminConfig } from "@byline/core";
4
+ import { I18nContext, I18nProvider } from "@byline/i18n/react";
5
+ function OptionalI18nProvider({ children }) {
6
+ if (null != useContext(I18nContext)) return /*#__PURE__*/ jsx(Fragment, {
7
+ children: children
8
+ });
9
+ let bundle = {};
10
+ let locale = 'en';
11
+ try {
12
+ const { i18n } = getAdminConfig();
13
+ bundle = i18n.translations ?? {};
14
+ locale = i18n.admin.defaultLocale;
15
+ } catch {}
16
+ return /*#__PURE__*/ jsx(I18nProvider, {
17
+ bundle: bundle,
18
+ activeLocale: locale,
19
+ defaultLocale: locale,
20
+ localeDefinitions: [],
21
+ children: children
22
+ });
23
+ }
24
+ export { OptionalI18nProvider };
@@ -0,0 +1,14 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+ import { type ReactNode } from 'react';
9
+ import { type CurrentAdminUser } from '../../server-fns/auth/current-user.js';
10
+ /** Blocks the page until the user explicitly acknowledges freshly verified login identity. */
11
+ export declare function SessionChangeBoundary({ user, children, }: {
12
+ user?: CurrentAdminUser;
13
+ children: ReactNode;
14
+ }): string | number | bigint | boolean | import("react").JSX.Element | Iterable<ReactNode> | Promise<string | number | bigint | boolean | Iterable<ReactNode> | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | import("react").ReactPortal | null | undefined> | null | undefined;
@@ -0,0 +1,119 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useState, useSyncExternalStore } from "react";
3
+ import { useTranslation } from "@byline/i18n/react";
4
+ import { Button } from "@byline/ui/react";
5
+ import clsx from "clsx";
6
+ import { acceptSession, expectedSession, flagSessionChanged, observeSession, sessionEpoch, sessionIsChanged, startSessionNotifications, subscribeSessionChange } from "../../integrations/session-coordination.js";
7
+ import { getSignInRoutePath } from "../../routes/sign-in-path.js";
8
+ import { getCurrentAdminUser } from "../../server-fns/auth/current-user.js";
9
+ import { OptionalI18nProvider } from "./optional-i18n-provider.js";
10
+ import session_change_boundary_module from "./session-change-boundary.module.js";
11
+ function SessionChangeBoundary({ user, children }) {
12
+ const epoch = useSyncExternalStore(subscribeSessionChange, sessionEpoch, ()=>0);
13
+ const changed = sessionIsChanged();
14
+ const [current, setCurrent] = useState(user);
15
+ const [error, setError] = useState();
16
+ const [busy, setBusy] = useState(false);
17
+ const mismatch = "u" > typeof window && !!user && !!expectedSession() && expectedSession() !== user.sessionId;
18
+ useEffect(()=>{
19
+ startSessionNotifications();
20
+ if (user) try {
21
+ observeSession(user.sessionId);
22
+ } catch {}
23
+ }, [
24
+ user
25
+ ]);
26
+ useEffect(()=>{
27
+ if (!changed && !mismatch) return;
28
+ getCurrentAdminUser().then((verified)=>{
29
+ if (sessionEpoch() !== epoch) return;
30
+ setCurrent(verified);
31
+ setError(void 0);
32
+ }).catch(()=>setError('sessionChange.errors.unavailable'));
33
+ }, [
34
+ changed,
35
+ mismatch,
36
+ epoch
37
+ ]);
38
+ if (!changed && !mismatch) return children;
39
+ async function acknowledge() {
40
+ setBusy(true);
41
+ try {
42
+ const verified = await getCurrentAdminUser();
43
+ if (!current || current.sessionId !== verified.sessionId) {
44
+ setCurrent(verified);
45
+ setError('sessionChange.errors.changedAgain');
46
+ return;
47
+ }
48
+ acceptSession(verified.sessionId);
49
+ window.location.reload();
50
+ } catch {
51
+ flagSessionChanged();
52
+ setError('sessionChange.errors.unverifiable');
53
+ } finally{
54
+ setBusy(false);
55
+ }
56
+ }
57
+ return /*#__PURE__*/ jsx(OptionalI18nProvider, {
58
+ children: /*#__PURE__*/ jsx(SessionChangeInterstitial, {
59
+ current: current,
60
+ errorKey: error,
61
+ busy: busy,
62
+ onAcknowledge: acknowledge
63
+ })
64
+ });
65
+ }
66
+ function SessionChangeInterstitial({ current, errorKey, busy, onAcknowledge }) {
67
+ const { t } = useTranslation('byline-admin');
68
+ return /*#__PURE__*/ jsx("main", {
69
+ className: clsx('byline-session-change', session_change_boundary_module.main),
70
+ role: "alertdialog",
71
+ "aria-modal": "true",
72
+ "aria-labelledby": "session-change-title",
73
+ children: /*#__PURE__*/ jsxs("div", {
74
+ className: clsx('byline-session-change-card', session_change_boundary_module.card),
75
+ children: [
76
+ /*#__PURE__*/ jsx("h1", {
77
+ id: "session-change-title",
78
+ className: clsx('byline-session-change-title', session_change_boundary_module.title),
79
+ children: t('sessionChange.title')
80
+ }),
81
+ /*#__PURE__*/ jsx("p", {
82
+ className: clsx('byline-session-change-body', session_change_boundary_module.body),
83
+ children: t('sessionChange.body')
84
+ }),
85
+ current && /*#__PURE__*/ jsx("p", {
86
+ className: clsx('byline-session-change-account', session_change_boundary_module.account),
87
+ children: t('sessionChange.activeAccount', {
88
+ email: current.email
89
+ })
90
+ }),
91
+ errorKey && /*#__PURE__*/ jsx("p", {
92
+ className: clsx('byline-session-change-error', session_change_boundary_module.error),
93
+ role: "alert",
94
+ children: t(errorKey)
95
+ }),
96
+ /*#__PURE__*/ jsxs("div", {
97
+ className: clsx('byline-session-change-actions', session_change_boundary_module.actions),
98
+ children: [
99
+ /*#__PURE__*/ jsx(Button, {
100
+ type: "button",
101
+ disabled: busy || !current,
102
+ onClick: onAcknowledge,
103
+ children: t('sessionChange.continue')
104
+ }),
105
+ /*#__PURE__*/ jsx(Button, {
106
+ variant: "outlined",
107
+ intent: "noeffect",
108
+ render: /*#__PURE__*/ jsx("a", {
109
+ href: `${getSignInRoutePath()}?reauthenticate=1`
110
+ }),
111
+ children: t('sessionChange.signInAgain')
112
+ })
113
+ ]
114
+ })
115
+ ]
116
+ })
117
+ });
118
+ }
119
+ export { SessionChangeBoundary };
@@ -0,0 +1,11 @@
1
+ import "./session-change-boundary_module.css";
2
+ const session_change_boundary_module = {
3
+ main: "main-MNvpLI",
4
+ card: "card-BMYD6l",
5
+ title: "title-N8HIe4",
6
+ body: "body-WE9gi2",
7
+ account: "account-ZFcz_R",
8
+ error: "error-GXrBRW",
9
+ actions: "actions-O152EZ"
10
+ };
11
+ export default session_change_boundary_module;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+ export {};
@@ -0,0 +1,81 @@
1
+ :is(.main-MNvpLI, .byline-session-change) {
2
+ justify-content: center;
3
+ align-items: center;
4
+ min-height: 100dvh;
5
+ padding: 1.5rem 1.5rem calc(1.5rem + 12vh);
6
+ display: flex;
7
+ }
8
+
9
+ :is(.card-BMYD6l, .byline-session-change-card) {
10
+ border: var(--border-width-thin) var(--border-style-solid) var(--gray-100);
11
+ border-radius: var(--border-radius-md);
12
+ background-color: var(--canvas-25);
13
+ flex-direction: column;
14
+ gap: .75rem;
15
+ width: 100%;
16
+ max-width: 30rem;
17
+ padding: 1.5rem;
18
+ display: flex;
19
+ }
20
+
21
+ :is(:is([data-theme="dark"], .dark) .card-BMYD6l, :is([data-theme="dark"], .dark) .byline-session-change-card) {
22
+ border-color: var(--gray-700);
23
+ background-color: var(--canvas-800);
24
+ }
25
+
26
+ :is(.title-N8HIe4, .byline-session-change-title) {
27
+ font-size: var(--font-size-xl);
28
+ font-weight: var(--font-weight-semibold);
29
+ line-height: var(--line-height-snug);
30
+ letter-spacing: var(--letter-spacing-tight);
31
+ color: var(--gray-900);
32
+ margin: 0;
33
+ }
34
+
35
+ :is(:is([data-theme="dark"], .dark) .title-N8HIe4, :is([data-theme="dark"], .dark) .byline-session-change-title) {
36
+ color: var(--gray-100);
37
+ }
38
+
39
+ :is(.body-WE9gi2, .byline-session-change-body) {
40
+ font-size: var(--font-size-sm);
41
+ line-height: var(--line-height-relaxed);
42
+ color: var(--gray-700);
43
+ margin: 0;
44
+ }
45
+
46
+ :is(:is([data-theme="dark"], .dark) .body-WE9gi2, :is([data-theme="dark"], .dark) .byline-session-change-body) {
47
+ color: var(--gray-400);
48
+ }
49
+
50
+ :is(.account-ZFcz_R, .byline-session-change-account) {
51
+ font-size: var(--font-size-sm);
52
+ font-weight: var(--font-weight-medium);
53
+ line-height: var(--line-height-normal);
54
+ color: var(--gray-900);
55
+ overflow-wrap: anywhere;
56
+ margin: 0;
57
+ }
58
+
59
+ :is(:is([data-theme="dark"], .dark) .account-ZFcz_R, :is([data-theme="dark"], .dark) .byline-session-change-account) {
60
+ color: var(--gray-100);
61
+ }
62
+
63
+ :is(.error-GXrBRW, .byline-session-change-error) {
64
+ font-size: var(--font-size-sm);
65
+ line-height: var(--line-height-relaxed);
66
+ color: var(--red-600);
67
+ margin: 0;
68
+ }
69
+
70
+ :is(:is([data-theme="dark"], .dark) .error-GXrBRW, :is([data-theme="dark"], .dark) .byline-session-change-error) {
71
+ color: var(--red-400);
72
+ }
73
+
74
+ :is(.actions-O152EZ, .byline-session-change-actions) {
75
+ flex-wrap: wrap;
76
+ align-items: center;
77
+ gap: .75rem;
78
+ margin-top: .25rem;
79
+ display: flex;
80
+ }
81
+
@@ -7,6 +7,7 @@
7
7
  */
8
8
  import type { LocaleCode } from '@byline/i18n';
9
9
  interface SignInPageProps {
10
+ reauthenticate?: boolean;
10
11
  redirectTo: string;
11
12
  activeLocale: LocaleCode;
12
13
  homeUrl?: string;
@@ -31,5 +32,5 @@ interface SignInPageProps {
31
32
  * Threads an optional host-owned `homeUrl` into `SignInForm` so the form's
32
33
  * action row can render a plain "Home" link beside the submit button.
33
34
  */
34
- export declare function SignInPage({ redirectTo, activeLocale, homeUrl }: SignInPageProps): import("react").JSX.Element;
35
+ export declare function SignInPage({ redirectTo, activeLocale, homeUrl, reauthenticate, }: SignInPageProps): import("react").JSX.Element;
35
36
  export {};
@@ -1,4 +1,5 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useState } from "react";
2
3
  import { SignInForm } from "@byline/admin/auth/components/sign-in-form";
3
4
  import { BylineAdminServicesProvider } from "@byline/admin/services";
4
5
  import { getAdminConfig } from "@byline/core";
@@ -6,9 +7,44 @@ import { I18nProvider, LanguageMenu } from "@byline/i18n/react";
6
7
  import clsx from "clsx";
7
8
  import { buildLocaleDefinitions } from "../../i18n/locale-definitions.js";
8
9
  import { bylineAdminServices } from "../../integrations/byline-admin-services.js";
10
+ import { coordinateAuthAction, expectedSession, flagSessionChanged, observeSession, renewSingleFlight } from "../../integrations/session-coordination.js";
11
+ import { renewAdminSession } from "../../server-fns/auth/renew.js";
9
12
  import { setAdminLocaleFn } from "../../server-fns/i18n/index.js";
13
+ import { SessionChangeBoundary } from "./session-change-boundary.js";
10
14
  import sign_in_page_module from "./sign-in-page.module.js";
11
- function SignInPage({ redirectTo, activeLocale, homeUrl }) {
15
+ function SignInPage({ redirectTo, activeLocale, homeUrl, reauthenticate = false }) {
16
+ const [checking, setChecking] = useState(!reauthenticate);
17
+ const [bootstrapError, setBootstrapError] = useState(false);
18
+ useEffect(()=>{
19
+ if (reauthenticate) return;
20
+ let active = true;
21
+ renewSingleFlight(()=>coordinateAuthAction(async ()=>{
22
+ const result = await renewAdminSession({
23
+ data: {
24
+ expectedSessionId: expectedSession() ?? void 0
25
+ }
26
+ });
27
+ observeSession(result.sessionId);
28
+ })).then(()=>{
29
+ if (active) window.location.replace(redirectTo);
30
+ }).catch((error)=>{
31
+ if (error?.code === 'ERR_SESSION_CHANGED') flagSessionChanged();
32
+ else if (![
33
+ 'ERR_UNAUTHENTICATED',
34
+ 'ERR_INVALID_TOKEN',
35
+ 'ERR_REVOKED_TOKEN',
36
+ 'ERR_ACCOUNT_DISABLED'
37
+ ].includes(error?.code)) setBootstrapError(true);
38
+ }).finally(()=>{
39
+ if (active) setChecking(false);
40
+ });
41
+ return ()=>{
42
+ active = false;
43
+ };
44
+ }, [
45
+ redirectTo,
46
+ reauthenticate
47
+ ]);
12
48
  const { i18n } = getAdminConfig();
13
49
  const localeDefinitions = buildLocaleDefinitions(i18n.admin.locales, i18n.admin.localeDefinitions);
14
50
  const handleSetLocale = async (next)=>{
@@ -19,7 +55,7 @@ function SignInPage({ redirectTo, activeLocale, homeUrl }) {
19
55
  });
20
56
  window.location.reload();
21
57
  };
22
- return /*#__PURE__*/ jsx(I18nProvider, {
58
+ const content = /*#__PURE__*/ jsx(I18nProvider, {
23
59
  bundle: i18n.translations ?? {},
24
60
  activeLocale: activeLocale,
25
61
  defaultLocale: i18n.admin.defaultLocale,
@@ -30,13 +66,31 @@ function SignInPage({ redirectTo, activeLocale, homeUrl }) {
30
66
  children: /*#__PURE__*/ jsxs("main", {
31
67
  className: clsx('byline-sign-in-page', sign_in_page_module.main),
32
68
  children: [
69
+ /*#__PURE__*/ jsx("noscript", {
70
+ children: "JavaScript is required to sign in or renew your admin session."
71
+ }),
33
72
  /*#__PURE__*/ jsx("div", {
34
73
  className: clsx('byline-sign-in-page-bar', sign_in_page_module.bar),
35
74
  children: /*#__PURE__*/ jsx(LanguageMenu, {})
36
75
  }),
37
76
  /*#__PURE__*/ jsx("div", {
38
77
  className: clsx('byline-sign-in-page-inner', sign_in_page_module.inner),
39
- children: /*#__PURE__*/ jsx(SignInForm, {
78
+ children: bootstrapError ? /*#__PURE__*/ jsxs("div", {
79
+ role: "alert",
80
+ children: [
81
+ /*#__PURE__*/ jsx("p", {
82
+ children: "Unable to check your session. Please try again."
83
+ }),
84
+ /*#__PURE__*/ jsx("button", {
85
+ type: "button",
86
+ onClick: ()=>window.location.reload(),
87
+ children: "Try again"
88
+ })
89
+ ]
90
+ }) : checking ? /*#__PURE__*/ jsx("p", {
91
+ role: "status",
92
+ children: "Checking your session…"
93
+ }) : /*#__PURE__*/ jsx(SignInForm, {
40
94
  redirectTo: redirectTo,
41
95
  homeUrl: homeUrl
42
96
  })
@@ -45,5 +99,8 @@ function SignInPage({ redirectTo, activeLocale, homeUrl }) {
45
99
  })
46
100
  })
47
101
  });
102
+ return reauthenticate ? content : /*#__PURE__*/ jsx(SessionChangeBoundary, {
103
+ children: content
104
+ });
48
105
  }
49
106
  export { SignInPage };
@@ -5,13 +5,19 @@ import { createAdminUser, setAdminUserPassword, setUserRoles, updateAdminUser }
5
5
  import { adminSignIn } from "../server-fns/auth/index.js";
6
6
  import { getCollectionDocumentVersion } from "../server-fns/collections/get.js";
7
7
  import { setAdminLocaleFn } from "../server-fns/i18n/index.js";
8
+ import { acceptSession, coordinateAuthAction, notifySessionAction } from "./session-coordination.js";
8
9
  const byline_admin_services_getCollectionDocumentVersion = async (collection, documentId, versionId, locale)=>{
9
10
  const result = await getCollectionDocumentVersion(collection, documentId, versionId, locale);
10
11
  if (null == result) throw new Error(`Document version not found: collection=${collection} document=${documentId} version=${versionId}`);
11
12
  return result;
12
13
  };
13
14
  const bylineAdminServices = {
14
- adminSignIn: adminSignIn,
15
+ adminSignIn: async (args)=>{
16
+ const result = await coordinateAuthAction(()=>adminSignIn(args));
17
+ acceptSession(result.sessionId);
18
+ notifySessionAction();
19
+ return result;
20
+ },
15
21
  updateAccount: updateAccount,
16
22
  changeAccountPassword: changeAccountPassword,
17
23
  setAdminLocale: setAdminLocaleFn,
@@ -0,0 +1,19 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+ import type { ClientIpResolver } from '@byline/auth';
9
+ /** Normalize equivalent IPv6 spellings and IPv4-mapped IPv6 before choosing a rate-limit key. */
10
+ export declare function normalizeClientIp(value: string | null | undefined): string | null;
11
+ /**
12
+ * Configure either a direct peer resolver or an explicitly trusted proxy header.
13
+ * Header mode requires the deployment to overwrite that header and block proxy bypass.
14
+ * Never falls back from a missing/invalid trusted header to another client-supplied header.
15
+ */
16
+ export declare function createClientIpResolver(options: {
17
+ trustedProxyHeader?: string;
18
+ resolvePeerIp: ClientIpResolver;
19
+ }): ClientIpResolver;
@@ -0,0 +1,34 @@
1
+ import { isIP } from "node:net";
2
+ function normalizeClientIp(value) {
3
+ if (!value || value.length > 64) return null;
4
+ const ip = value.trim();
5
+ if (4 === isIP(ip)) return ip;
6
+ if (6 !== isIP(ip) || ip.includes('%')) return null;
7
+ const normalized = new URL(`http://[${ip}]/`).hostname.slice(1, -1);
8
+ const mapped = /^::ffff:([0-9a-f]+):([0-9a-f]+)$/.exec(normalized);
9
+ if (mapped) {
10
+ const high = Number.parseInt(mapped[1], 16);
11
+ const low = Number.parseInt(mapped[2], 16);
12
+ return [
13
+ high >>> 8,
14
+ 255 & high,
15
+ low >>> 8,
16
+ 255 & low
17
+ ].join('.');
18
+ }
19
+ return normalized;
20
+ }
21
+ function createClientIpResolver(options) {
22
+ const header = options.trustedProxyHeader;
23
+ if (void 0 !== header && !/^[a-z0-9-]+$/i.test(header)) throw new Error('Invalid trusted IP header');
24
+ let lastDiagnostic = -1 / 0;
25
+ return async (request)=>{
26
+ const ip = normalizeClientIp(void 0 === header ? await options.resolvePeerIp(request) : request.headers.get(header));
27
+ if (!ip && performance.now() - lastDiagnostic >= 60000) {
28
+ lastDiagnostic = performance.now();
29
+ console.warn('[byline:client-ip] No valid single client IP; check the configured peer resolver or trusted proxy header.');
30
+ }
31
+ return ip;
32
+ };
33
+ }
34
+ export { createClientIpResolver, normalizeClientIp };
@@ -0,0 +1,24 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+ export declare const sessionChangedError: () => Error;
9
+ export declare function expectedSession(): string | null;
10
+ export declare function sessionSnapshot(): {
11
+ sessionId: string | null;
12
+ epoch: number;
13
+ };
14
+ export declare function requireUnchanged(snapshot: ReturnType<typeof sessionSnapshot>): void;
15
+ export declare function flagSessionChanged(): void;
16
+ export declare function acceptSession(sessionId: string): void;
17
+ export declare function observeSession(sessionId: string): void;
18
+ export declare function sessionEpoch(): number;
19
+ export declare function sessionIsChanged(): boolean;
20
+ export declare function subscribeSessionChange(listener: () => void): () => void;
21
+ export declare function renewSingleFlight(work: () => Promise<void>): Promise<void>;
22
+ export declare function coordinateAuthAction<T>(work: () => Promise<T>): Promise<T>;
23
+ export declare function startSessionNotifications(): void;
24
+ export declare function notifySessionAction(): void;
@@ -0,0 +1,85 @@
1
+ const storageKey = 'byline.expected-login';
2
+ let expected = null;
3
+ let epoch = 0;
4
+ let changed = false;
5
+ let renewal;
6
+ const listeners = new Set();
7
+ const sessionChangedError = ()=>new Error('Session changed. Acknowledge the active account before continuing.');
8
+ function expectedSession() {
9
+ if ("u" < typeof window) return null;
10
+ if (!expected) expected = window.sessionStorage.getItem(storageKey);
11
+ return expected;
12
+ }
13
+ function sessionSnapshot() {
14
+ return {
15
+ sessionId: expectedSession(),
16
+ epoch: epoch
17
+ };
18
+ }
19
+ function requireUnchanged(snapshot) {
20
+ if (changed || snapshot.epoch !== epoch || snapshot.sessionId !== expectedSession()) throw sessionChangedError();
21
+ }
22
+ function flagSessionChanged() {
23
+ changed = true;
24
+ epoch++;
25
+ for (const listener of listeners)listener();
26
+ }
27
+ function acceptSession(sessionId) {
28
+ if ("u" < typeof window) return;
29
+ if ('string' != typeof sessionId || !sessionId) throw new Error('Provider omitted login identity');
30
+ window.sessionStorage.setItem(storageKey, sessionId);
31
+ expected = sessionId;
32
+ changed = false;
33
+ epoch++;
34
+ for (const listener of listeners)listener();
35
+ }
36
+ function observeSession(sessionId) {
37
+ const old = expectedSession();
38
+ if (old && old !== sessionId) {
39
+ flagSessionChanged();
40
+ throw sessionChangedError();
41
+ }
42
+ if (!old) acceptSession(sessionId);
43
+ }
44
+ function sessionEpoch() {
45
+ return epoch;
46
+ }
47
+ function sessionIsChanged() {
48
+ return changed;
49
+ }
50
+ function subscribeSessionChange(listener) {
51
+ listeners.add(listener);
52
+ return ()=>{
53
+ listeners.delete(listener);
54
+ };
55
+ }
56
+ function renewSingleFlight(work) {
57
+ if (!renewal) renewal = work().finally(()=>{
58
+ renewal = void 0;
59
+ });
60
+ return renewal;
61
+ }
62
+ let authQueue = Promise.resolve();
63
+ function coordinateAuthAction(work) {
64
+ const run = authQueue.catch(()=>{}).then(async ()=>{
65
+ if ("u" > typeof navigator && navigator.locks) return navigator.locks.request('byline.auth', work);
66
+ return work();
67
+ });
68
+ authQueue = run;
69
+ return run;
70
+ }
71
+ let channel;
72
+ function startSessionNotifications() {
73
+ if ("u" < typeof BroadcastChannel || channel) return;
74
+ channel = new BroadcastChannel('byline.session');
75
+ channel.onmessage = ()=>{
76
+ flagSessionChanged();
77
+ };
78
+ }
79
+ function notifySessionAction() {
80
+ startSessionNotifications();
81
+ channel?.postMessage({
82
+ type: 'session-changed'
83
+ });
84
+ }
85
+ export { acceptSession, coordinateAuthAction, expectedSession, flagSessionChanged, notifySessionAction, observeSession, renewSingleFlight, requireUnchanged, sessionChangedError, sessionEpoch, sessionIsChanged, sessionSnapshot, startSessionNotifications, subscribeSessionChange };
@@ -0,0 +1,11 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+ /** The boundary owns the retry signal; handler errors cannot turn into retryable outcomes. */
9
+ export declare const adminSessionMiddleware: import("@tanstack/react-start").FunctionMiddlewareAfterServer<{}, unknown, undefined, undefined, {
10
+ bylineExpectedSessionId: string | null;
11
+ }, undefined, undefined>;