@strapi/admin 5.53.0 → 5.54.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 (105) hide show
  1. package/dist/admin/admin/src/App.js +1 -1
  2. package/dist/admin/admin/src/App.js.map +1 -1
  3. package/dist/admin/admin/src/App.mjs +1 -1
  4. package/dist/admin/admin/src/App.mjs.map +1 -1
  5. package/dist/admin/admin/src/components/DismissibleBanner.js +60 -0
  6. package/dist/admin/admin/src/components/DismissibleBanner.js.map +1 -0
  7. package/dist/admin/admin/src/components/DismissibleBanner.mjs +58 -0
  8. package/dist/admin/admin/src/components/DismissibleBanner.mjs.map +1 -0
  9. package/dist/admin/admin/src/components/Form.js +11 -2
  10. package/dist/admin/admin/src/components/Form.js.map +1 -1
  11. package/dist/admin/admin/src/components/Form.mjs +11 -2
  12. package/dist/admin/admin/src/components/Form.mjs.map +1 -1
  13. package/dist/admin/admin/src/components/FormInputs/DateTime.js +1 -1
  14. package/dist/admin/admin/src/components/FormInputs/DateTime.js.map +1 -1
  15. package/dist/admin/admin/src/components/FormInputs/DateTime.mjs +1 -1
  16. package/dist/admin/admin/src/components/FormInputs/DateTime.mjs.map +1 -1
  17. package/dist/admin/admin/src/components/FormInputs/Time.js +1 -1
  18. package/dist/admin/admin/src/components/FormInputs/Time.js.map +1 -1
  19. package/dist/admin/admin/src/components/FormInputs/Time.mjs +1 -1
  20. package/dist/admin/admin/src/components/FormInputs/Time.mjs.map +1 -1
  21. package/dist/admin/admin/src/components/MainNav/NavUser.js +1 -2
  22. package/dist/admin/admin/src/components/MainNav/NavUser.js.map +1 -1
  23. package/dist/admin/admin/src/components/MainNav/NavUser.mjs +1 -2
  24. package/dist/admin/admin/src/components/MainNav/NavUser.mjs.map +1 -1
  25. package/dist/admin/admin/src/components/MediaLibraryBanner.js +85 -0
  26. package/dist/admin/admin/src/components/MediaLibraryBanner.js.map +1 -0
  27. package/dist/admin/admin/src/components/MediaLibraryBanner.mjs +83 -0
  28. package/dist/admin/admin/src/components/MediaLibraryBanner.mjs.map +1 -0
  29. package/dist/admin/admin/src/components/Providers.js +6 -6
  30. package/dist/admin/admin/src/components/Providers.js.map +1 -1
  31. package/dist/admin/admin/src/components/Providers.mjs +6 -6
  32. package/dist/admin/admin/src/components/Providers.mjs.map +1 -1
  33. package/dist/admin/admin/src/components/UpsellBanner.js +56 -86
  34. package/dist/admin/admin/src/components/UpsellBanner.js.map +1 -1
  35. package/dist/admin/admin/src/components/UpsellBanner.mjs +58 -88
  36. package/dist/admin/admin/src/components/UpsellBanner.mjs.map +1 -1
  37. package/dist/admin/admin/src/core/store/configure.js +14 -2
  38. package/dist/admin/admin/src/core/store/configure.js.map +1 -1
  39. package/dist/admin/admin/src/core/store/configure.mjs +14 -2
  40. package/dist/admin/admin/src/core/store/configure.mjs.map +1 -1
  41. package/dist/admin/admin/src/core/store/hooks.mjs +1 -1
  42. package/dist/admin/admin/src/features/Auth.js +162 -46
  43. package/dist/admin/admin/src/features/Auth.js.map +1 -1
  44. package/dist/admin/admin/src/features/Auth.mjs +164 -48
  45. package/dist/admin/admin/src/features/Auth.mjs.map +1 -1
  46. package/dist/admin/admin/src/hooks/useRegisterUnsavedChanges.js +19 -0
  47. package/dist/admin/admin/src/hooks/useRegisterUnsavedChanges.js.map +1 -0
  48. package/dist/admin/admin/src/hooks/useRegisterUnsavedChanges.mjs +17 -0
  49. package/dist/admin/admin/src/hooks/useRegisterUnsavedChanges.mjs.map +1 -0
  50. package/dist/admin/admin/src/hooks/{useIdleSessionLogout.js → useSessionKeepalive.js} +34 -27
  51. package/dist/admin/admin/src/hooks/useSessionKeepalive.js.map +1 -0
  52. package/dist/admin/admin/src/hooks/{useIdleSessionLogout.mjs → useSessionKeepalive.mjs} +34 -27
  53. package/dist/admin/admin/src/hooks/useSessionKeepalive.mjs.map +1 -0
  54. package/dist/admin/admin/src/layouts/AuthenticatedLayout.js +2 -0
  55. package/dist/admin/admin/src/layouts/AuthenticatedLayout.js.map +1 -1
  56. package/dist/admin/admin/src/layouts/AuthenticatedLayout.mjs +2 -0
  57. package/dist/admin/admin/src/layouts/AuthenticatedLayout.mjs.map +1 -1
  58. package/dist/admin/admin/src/pages/SessionsPage.js +2 -5
  59. package/dist/admin/admin/src/pages/SessionsPage.js.map +1 -1
  60. package/dist/admin/admin/src/pages/SessionsPage.mjs +2 -5
  61. package/dist/admin/admin/src/pages/SessionsPage.mjs.map +1 -1
  62. package/dist/admin/admin/src/render.js +5 -0
  63. package/dist/admin/admin/src/render.js.map +1 -1
  64. package/dist/admin/admin/src/render.mjs +5 -0
  65. package/dist/admin/admin/src/render.mjs.map +1 -1
  66. package/dist/admin/admin/src/translations/en.json.js +12 -0
  67. package/dist/admin/admin/src/translations/en.json.js.map +1 -1
  68. package/dist/admin/admin/src/translations/en.json.mjs +12 -0
  69. package/dist/admin/admin/src/translations/en.json.mjs.map +1 -1
  70. package/dist/admin/admin/src/utils/baseQuery.js +3 -13
  71. package/dist/admin/admin/src/utils/baseQuery.js.map +1 -1
  72. package/dist/admin/admin/src/utils/baseQuery.mjs +3 -13
  73. package/dist/admin/admin/src/utils/baseQuery.mjs.map +1 -1
  74. package/dist/admin/admin/src/utils/getFetchClient.js +34 -5
  75. package/dist/admin/admin/src/utils/getFetchClient.js.map +1 -1
  76. package/dist/admin/admin/src/utils/getFetchClient.mjs +34 -6
  77. package/dist/admin/admin/src/utils/getFetchClient.mjs.map +1 -1
  78. package/dist/admin/admin/src/utils/unsavedChangesRegistry.js +25 -0
  79. package/dist/admin/admin/src/utils/unsavedChangesRegistry.js.map +1 -0
  80. package/dist/admin/admin/src/utils/unsavedChangesRegistry.mjs +22 -0
  81. package/dist/admin/admin/src/utils/unsavedChangesRegistry.mjs.map +1 -0
  82. package/dist/admin/admin/tests/utils.js +14 -14
  83. package/dist/admin/admin/tests/utils.js.map +1 -1
  84. package/dist/admin/admin/tests/utils.mjs +14 -14
  85. package/dist/admin/admin/tests/utils.mjs.map +1 -1
  86. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/utils/getActionTypesDefaultMessages.js +5 -1
  87. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/utils/getActionTypesDefaultMessages.js.map +1 -1
  88. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/utils/getActionTypesDefaultMessages.mjs +5 -1
  89. package/dist/admin/ee/admin/src/pages/SettingsPage/pages/AuditLogs/utils/getActionTypesDefaultMessages.mjs.map +1 -1
  90. package/dist/admin/index.js +1 -0
  91. package/dist/admin/index.js.map +1 -1
  92. package/dist/admin/index.mjs +1 -1
  93. package/dist/admin/src/components/DismissibleBanner.d.ts +8 -0
  94. package/dist/admin/src/components/MediaLibraryBanner.d.ts +2 -0
  95. package/dist/admin/src/hooks/useRegisterUnsavedChanges.d.ts +6 -0
  96. package/dist/admin/src/hooks/useSessionKeepalive.d.ts +88 -0
  97. package/dist/admin/src/utils/getFetchClient.d.ts +14 -6
  98. package/dist/admin/src/utils/unsavedChangesRegistry.d.ts +11 -0
  99. package/dist/ee/admin/src/pages/SettingsPage/pages/AuditLogs/utils/getActionTypesDefaultMessages.d.ts +4 -0
  100. package/dist/shared/contracts/shared.d.ts +1 -0
  101. package/dist/shared/contracts/shared.d.ts.map +1 -1
  102. package/package.json +7 -7
  103. package/dist/admin/admin/src/hooks/useIdleSessionLogout.js.map +0 -1
  104. package/dist/admin/admin/src/hooks/useIdleSessionLogout.mjs.map +0 -1
  105. package/dist/admin/src/hooks/useIdleSessionLogout.d.ts +0 -87
@@ -37,7 +37,11 @@ const actionTypes = {
37
37
  'release.entry.update': 'Change entry on release',
38
38
  'release.entry.remove': 'Remove entry from release',
39
39
  'release.settings.update': 'Update release settings',
40
- 'audit-log.export': 'Export audit logs'
40
+ 'audit-log.export': 'Export audit logs',
41
+ 'locale.create': 'Create locale',
42
+ 'locale.update': 'Update locale',
43
+ 'locale.delete': 'Delete locale',
44
+ 'locale.default.update': 'Change default locale'
41
45
  };
42
46
  const getDefaultMessage = (value)=>{
43
47
  return actionTypes[value] || value;
@@ -1 +1 @@
1
- {"version":3,"file":"getActionTypesDefaultMessages.js","sources":["../../../../../../../../../../ee/admin/src/pages/SettingsPage/pages/AuditLogs/utils/getActionTypesDefaultMessages.ts"],"sourcesContent":["export const actionTypes = {\n 'entry.create': 'Create entry{model, select, undefined {} other { ({model})}}',\n 'entry.update': 'Update entry{model, select, undefined {} other { ({model})}}',\n 'entry.delete': 'Delete entry{model, select, undefined {} other { ({model})}}',\n 'entry.publish': 'Publish entry{model, select, undefined {} other { ({model})}}',\n 'entry.unpublish': 'Unpublish entry{model, select, undefined {} other { ({model})}}',\n 'media.create': 'Create media',\n 'media.update': 'Update media',\n 'media.delete': 'Delete media',\n 'media-folder.create': 'Create media folder',\n 'media-folder.update': 'Update media folder',\n 'media-folder.delete': 'Delete media folder',\n 'user.create': 'Create user',\n 'user.update': 'Update user',\n 'user.delete': 'Delete user',\n 'admin.auth.success': 'Admin login',\n 'admin.logout': 'Admin logout',\n 'content-type.create': 'Create content type',\n 'content-type.update': 'Update content type',\n 'content-type.delete': 'Delete content type',\n 'component.create': 'Create component',\n 'component.update': 'Update component',\n 'component.delete': 'Delete component',\n 'role.create': 'Create role',\n 'role.update': 'Update role',\n 'role.delete': 'Delete role',\n 'permission.create': 'Create permission',\n 'permission.update': 'Update permission',\n 'permission.delete': 'Delete permission',\n 'release.create': 'Create release',\n 'release.update': 'Update release',\n 'release.delete': 'Delete release',\n 'release.trigger': 'Trigger release',\n 'release.entry.add': 'Add entry to release',\n 'release.entry.update': 'Change entry on release',\n 'release.entry.remove': 'Remove entry from release',\n 'release.settings.update': 'Update release settings',\n 'audit-log.export': 'Export audit logs',\n};\n\nexport const getDefaultMessage = (value: keyof typeof actionTypes) => {\n return actionTypes[value] || value;\n};\n"],"names":["actionTypes","getDefaultMessage","value"],"mappings":";;MAAaA,WAAAA,GAAc;IACzB,cAAA,EAAgB,8DAAA;IAChB,cAAA,EAAgB,8DAAA;IAChB,cAAA,EAAgB,8DAAA;IAChB,eAAA,EAAiB,+DAAA;IACjB,iBAAA,EAAmB,iEAAA;IACnB,cAAA,EAAgB,cAAA;IAChB,cAAA,EAAgB,cAAA;IAChB,cAAA,EAAgB,cAAA;IAChB,qBAAA,EAAuB,qBAAA;IACvB,qBAAA,EAAuB,qBAAA;IACvB,qBAAA,EAAuB,qBAAA;IACvB,aAAA,EAAe,aAAA;IACf,aAAA,EAAe,aAAA;IACf,aAAA,EAAe,aAAA;IACf,oBAAA,EAAsB,aAAA;IACtB,cAAA,EAAgB,cAAA;IAChB,qBAAA,EAAuB,qBAAA;IACvB,qBAAA,EAAuB,qBAAA;IACvB,qBAAA,EAAuB,qBAAA;IACvB,kBAAA,EAAoB,kBAAA;IACpB,kBAAA,EAAoB,kBAAA;IACpB,kBAAA,EAAoB,kBAAA;IACpB,aAAA,EAAe,aAAA;IACf,aAAA,EAAe,aAAA;IACf,aAAA,EAAe,aAAA;IACf,mBAAA,EAAqB,mBAAA;IACrB,mBAAA,EAAqB,mBAAA;IACrB,mBAAA,EAAqB,mBAAA;IACrB,gBAAA,EAAkB,gBAAA;IAClB,gBAAA,EAAkB,gBAAA;IAClB,gBAAA,EAAkB,gBAAA;IAClB,iBAAA,EAAmB,iBAAA;IACnB,mBAAA,EAAqB,sBAAA;IACrB,sBAAA,EAAwB,yBAAA;IACxB,sBAAA,EAAwB,2BAAA;IACxB,yBAAA,EAA2B,yBAAA;IAC3B,kBAAA,EAAoB;AACtB;AAEO,MAAMC,oBAAoB,CAACC,KAAAA,GAAAA;IAChC,OAAOF,WAAW,CAACE,KAAAA,CAAM,IAAIA,KAAAA;AAC/B;;;;;"}
1
+ {"version":3,"file":"getActionTypesDefaultMessages.js","sources":["../../../../../../../../../../ee/admin/src/pages/SettingsPage/pages/AuditLogs/utils/getActionTypesDefaultMessages.ts"],"sourcesContent":["export const actionTypes = {\n 'entry.create': 'Create entry{model, select, undefined {} other { ({model})}}',\n 'entry.update': 'Update entry{model, select, undefined {} other { ({model})}}',\n 'entry.delete': 'Delete entry{model, select, undefined {} other { ({model})}}',\n 'entry.publish': 'Publish entry{model, select, undefined {} other { ({model})}}',\n 'entry.unpublish': 'Unpublish entry{model, select, undefined {} other { ({model})}}',\n 'media.create': 'Create media',\n 'media.update': 'Update media',\n 'media.delete': 'Delete media',\n 'media-folder.create': 'Create media folder',\n 'media-folder.update': 'Update media folder',\n 'media-folder.delete': 'Delete media folder',\n 'user.create': 'Create user',\n 'user.update': 'Update user',\n 'user.delete': 'Delete user',\n 'admin.auth.success': 'Admin login',\n 'admin.logout': 'Admin logout',\n 'content-type.create': 'Create content type',\n 'content-type.update': 'Update content type',\n 'content-type.delete': 'Delete content type',\n 'component.create': 'Create component',\n 'component.update': 'Update component',\n 'component.delete': 'Delete component',\n 'role.create': 'Create role',\n 'role.update': 'Update role',\n 'role.delete': 'Delete role',\n 'permission.create': 'Create permission',\n 'permission.update': 'Update permission',\n 'permission.delete': 'Delete permission',\n 'release.create': 'Create release',\n 'release.update': 'Update release',\n 'release.delete': 'Delete release',\n 'release.trigger': 'Trigger release',\n 'release.entry.add': 'Add entry to release',\n 'release.entry.update': 'Change entry on release',\n 'release.entry.remove': 'Remove entry from release',\n 'release.settings.update': 'Update release settings',\n 'audit-log.export': 'Export audit logs',\n 'locale.create': 'Create locale',\n 'locale.update': 'Update locale',\n 'locale.delete': 'Delete locale',\n 'locale.default.update': 'Change default locale',\n};\n\nexport const getDefaultMessage = (value: keyof typeof actionTypes) => {\n return actionTypes[value] || value;\n};\n"],"names":["actionTypes","getDefaultMessage","value"],"mappings":";;MAAaA,WAAAA,GAAc;IACzB,cAAA,EAAgB,8DAAA;IAChB,cAAA,EAAgB,8DAAA;IAChB,cAAA,EAAgB,8DAAA;IAChB,eAAA,EAAiB,+DAAA;IACjB,iBAAA,EAAmB,iEAAA;IACnB,cAAA,EAAgB,cAAA;IAChB,cAAA,EAAgB,cAAA;IAChB,cAAA,EAAgB,cAAA;IAChB,qBAAA,EAAuB,qBAAA;IACvB,qBAAA,EAAuB,qBAAA;IACvB,qBAAA,EAAuB,qBAAA;IACvB,aAAA,EAAe,aAAA;IACf,aAAA,EAAe,aAAA;IACf,aAAA,EAAe,aAAA;IACf,oBAAA,EAAsB,aAAA;IACtB,cAAA,EAAgB,cAAA;IAChB,qBAAA,EAAuB,qBAAA;IACvB,qBAAA,EAAuB,qBAAA;IACvB,qBAAA,EAAuB,qBAAA;IACvB,kBAAA,EAAoB,kBAAA;IACpB,kBAAA,EAAoB,kBAAA;IACpB,kBAAA,EAAoB,kBAAA;IACpB,aAAA,EAAe,aAAA;IACf,aAAA,EAAe,aAAA;IACf,aAAA,EAAe,aAAA;IACf,mBAAA,EAAqB,mBAAA;IACrB,mBAAA,EAAqB,mBAAA;IACrB,mBAAA,EAAqB,mBAAA;IACrB,gBAAA,EAAkB,gBAAA;IAClB,gBAAA,EAAkB,gBAAA;IAClB,gBAAA,EAAkB,gBAAA;IAClB,iBAAA,EAAmB,iBAAA;IACnB,mBAAA,EAAqB,sBAAA;IACrB,sBAAA,EAAwB,yBAAA;IACxB,sBAAA,EAAwB,2BAAA;IACxB,yBAAA,EAA2B,yBAAA;IAC3B,kBAAA,EAAoB,mBAAA;IACpB,eAAA,EAAiB,eAAA;IACjB,eAAA,EAAiB,eAAA;IACjB,eAAA,EAAiB,eAAA;IACjB,uBAAA,EAAyB;AAC3B;AAEO,MAAMC,oBAAoB,CAACC,KAAAA,GAAAA;IAChC,OAAOF,WAAW,CAACE,KAAAA,CAAM,IAAIA,KAAAA;AAC/B;;;;;"}
@@ -35,7 +35,11 @@ const actionTypes = {
35
35
  'release.entry.update': 'Change entry on release',
36
36
  'release.entry.remove': 'Remove entry from release',
37
37
  'release.settings.update': 'Update release settings',
38
- 'audit-log.export': 'Export audit logs'
38
+ 'audit-log.export': 'Export audit logs',
39
+ 'locale.create': 'Create locale',
40
+ 'locale.update': 'Update locale',
41
+ 'locale.delete': 'Delete locale',
42
+ 'locale.default.update': 'Change default locale'
39
43
  };
40
44
  const getDefaultMessage = (value)=>{
41
45
  return actionTypes[value] || value;
@@ -1 +1 @@
1
- {"version":3,"file":"getActionTypesDefaultMessages.mjs","sources":["../../../../../../../../../../ee/admin/src/pages/SettingsPage/pages/AuditLogs/utils/getActionTypesDefaultMessages.ts"],"sourcesContent":["export const actionTypes = {\n 'entry.create': 'Create entry{model, select, undefined {} other { ({model})}}',\n 'entry.update': 'Update entry{model, select, undefined {} other { ({model})}}',\n 'entry.delete': 'Delete entry{model, select, undefined {} other { ({model})}}',\n 'entry.publish': 'Publish entry{model, select, undefined {} other { ({model})}}',\n 'entry.unpublish': 'Unpublish entry{model, select, undefined {} other { ({model})}}',\n 'media.create': 'Create media',\n 'media.update': 'Update media',\n 'media.delete': 'Delete media',\n 'media-folder.create': 'Create media folder',\n 'media-folder.update': 'Update media folder',\n 'media-folder.delete': 'Delete media folder',\n 'user.create': 'Create user',\n 'user.update': 'Update user',\n 'user.delete': 'Delete user',\n 'admin.auth.success': 'Admin login',\n 'admin.logout': 'Admin logout',\n 'content-type.create': 'Create content type',\n 'content-type.update': 'Update content type',\n 'content-type.delete': 'Delete content type',\n 'component.create': 'Create component',\n 'component.update': 'Update component',\n 'component.delete': 'Delete component',\n 'role.create': 'Create role',\n 'role.update': 'Update role',\n 'role.delete': 'Delete role',\n 'permission.create': 'Create permission',\n 'permission.update': 'Update permission',\n 'permission.delete': 'Delete permission',\n 'release.create': 'Create release',\n 'release.update': 'Update release',\n 'release.delete': 'Delete release',\n 'release.trigger': 'Trigger release',\n 'release.entry.add': 'Add entry to release',\n 'release.entry.update': 'Change entry on release',\n 'release.entry.remove': 'Remove entry from release',\n 'release.settings.update': 'Update release settings',\n 'audit-log.export': 'Export audit logs',\n};\n\nexport const getDefaultMessage = (value: keyof typeof actionTypes) => {\n return actionTypes[value] || value;\n};\n"],"names":["actionTypes","getDefaultMessage","value"],"mappings":"MAAaA,WAAAA,GAAc;IACzB,cAAA,EAAgB,8DAAA;IAChB,cAAA,EAAgB,8DAAA;IAChB,cAAA,EAAgB,8DAAA;IAChB,eAAA,EAAiB,+DAAA;IACjB,iBAAA,EAAmB,iEAAA;IACnB,cAAA,EAAgB,cAAA;IAChB,cAAA,EAAgB,cAAA;IAChB,cAAA,EAAgB,cAAA;IAChB,qBAAA,EAAuB,qBAAA;IACvB,qBAAA,EAAuB,qBAAA;IACvB,qBAAA,EAAuB,qBAAA;IACvB,aAAA,EAAe,aAAA;IACf,aAAA,EAAe,aAAA;IACf,aAAA,EAAe,aAAA;IACf,oBAAA,EAAsB,aAAA;IACtB,cAAA,EAAgB,cAAA;IAChB,qBAAA,EAAuB,qBAAA;IACvB,qBAAA,EAAuB,qBAAA;IACvB,qBAAA,EAAuB,qBAAA;IACvB,kBAAA,EAAoB,kBAAA;IACpB,kBAAA,EAAoB,kBAAA;IACpB,kBAAA,EAAoB,kBAAA;IACpB,aAAA,EAAe,aAAA;IACf,aAAA,EAAe,aAAA;IACf,aAAA,EAAe,aAAA;IACf,mBAAA,EAAqB,mBAAA;IACrB,mBAAA,EAAqB,mBAAA;IACrB,mBAAA,EAAqB,mBAAA;IACrB,gBAAA,EAAkB,gBAAA;IAClB,gBAAA,EAAkB,gBAAA;IAClB,gBAAA,EAAkB,gBAAA;IAClB,iBAAA,EAAmB,iBAAA;IACnB,mBAAA,EAAqB,sBAAA;IACrB,sBAAA,EAAwB,yBAAA;IACxB,sBAAA,EAAwB,2BAAA;IACxB,yBAAA,EAA2B,yBAAA;IAC3B,kBAAA,EAAoB;AACtB;AAEO,MAAMC,oBAAoB,CAACC,KAAAA,GAAAA;IAChC,OAAOF,WAAW,CAACE,KAAAA,CAAM,IAAIA,KAAAA;AAC/B;;;;"}
1
+ {"version":3,"file":"getActionTypesDefaultMessages.mjs","sources":["../../../../../../../../../../ee/admin/src/pages/SettingsPage/pages/AuditLogs/utils/getActionTypesDefaultMessages.ts"],"sourcesContent":["export const actionTypes = {\n 'entry.create': 'Create entry{model, select, undefined {} other { ({model})}}',\n 'entry.update': 'Update entry{model, select, undefined {} other { ({model})}}',\n 'entry.delete': 'Delete entry{model, select, undefined {} other { ({model})}}',\n 'entry.publish': 'Publish entry{model, select, undefined {} other { ({model})}}',\n 'entry.unpublish': 'Unpublish entry{model, select, undefined {} other { ({model})}}',\n 'media.create': 'Create media',\n 'media.update': 'Update media',\n 'media.delete': 'Delete media',\n 'media-folder.create': 'Create media folder',\n 'media-folder.update': 'Update media folder',\n 'media-folder.delete': 'Delete media folder',\n 'user.create': 'Create user',\n 'user.update': 'Update user',\n 'user.delete': 'Delete user',\n 'admin.auth.success': 'Admin login',\n 'admin.logout': 'Admin logout',\n 'content-type.create': 'Create content type',\n 'content-type.update': 'Update content type',\n 'content-type.delete': 'Delete content type',\n 'component.create': 'Create component',\n 'component.update': 'Update component',\n 'component.delete': 'Delete component',\n 'role.create': 'Create role',\n 'role.update': 'Update role',\n 'role.delete': 'Delete role',\n 'permission.create': 'Create permission',\n 'permission.update': 'Update permission',\n 'permission.delete': 'Delete permission',\n 'release.create': 'Create release',\n 'release.update': 'Update release',\n 'release.delete': 'Delete release',\n 'release.trigger': 'Trigger release',\n 'release.entry.add': 'Add entry to release',\n 'release.entry.update': 'Change entry on release',\n 'release.entry.remove': 'Remove entry from release',\n 'release.settings.update': 'Update release settings',\n 'audit-log.export': 'Export audit logs',\n 'locale.create': 'Create locale',\n 'locale.update': 'Update locale',\n 'locale.delete': 'Delete locale',\n 'locale.default.update': 'Change default locale',\n};\n\nexport const getDefaultMessage = (value: keyof typeof actionTypes) => {\n return actionTypes[value] || value;\n};\n"],"names":["actionTypes","getDefaultMessage","value"],"mappings":"MAAaA,WAAAA,GAAc;IACzB,cAAA,EAAgB,8DAAA;IAChB,cAAA,EAAgB,8DAAA;IAChB,cAAA,EAAgB,8DAAA;IAChB,eAAA,EAAiB,+DAAA;IACjB,iBAAA,EAAmB,iEAAA;IACnB,cAAA,EAAgB,cAAA;IAChB,cAAA,EAAgB,cAAA;IAChB,cAAA,EAAgB,cAAA;IAChB,qBAAA,EAAuB,qBAAA;IACvB,qBAAA,EAAuB,qBAAA;IACvB,qBAAA,EAAuB,qBAAA;IACvB,aAAA,EAAe,aAAA;IACf,aAAA,EAAe,aAAA;IACf,aAAA,EAAe,aAAA;IACf,oBAAA,EAAsB,aAAA;IACtB,cAAA,EAAgB,cAAA;IAChB,qBAAA,EAAuB,qBAAA;IACvB,qBAAA,EAAuB,qBAAA;IACvB,qBAAA,EAAuB,qBAAA;IACvB,kBAAA,EAAoB,kBAAA;IACpB,kBAAA,EAAoB,kBAAA;IACpB,kBAAA,EAAoB,kBAAA;IACpB,aAAA,EAAe,aAAA;IACf,aAAA,EAAe,aAAA;IACf,aAAA,EAAe,aAAA;IACf,mBAAA,EAAqB,mBAAA;IACrB,mBAAA,EAAqB,mBAAA;IACrB,mBAAA,EAAqB,mBAAA;IACrB,gBAAA,EAAkB,gBAAA;IAClB,gBAAA,EAAkB,gBAAA;IAClB,gBAAA,EAAkB,gBAAA;IAClB,iBAAA,EAAmB,iBAAA;IACnB,mBAAA,EAAqB,sBAAA;IACrB,sBAAA,EAAwB,yBAAA;IACxB,sBAAA,EAAwB,2BAAA;IACxB,yBAAA,EAA2B,yBAAA;IAC3B,kBAAA,EAAoB,mBAAA;IACpB,eAAA,EAAiB,eAAA;IACjB,eAAA,EAAiB,eAAA;IACjB,eAAA,EAAiB,eAAA;IACjB,uBAAA,EAAyB;AAC3B;AAEO,MAAMC,oBAAoB,CAACC,KAAAA,GAAAA;IAChC,OAAOF,WAAW,CAACE,KAAAA,CAAM,IAAIA,KAAAA;AAC/B;;;;"}
@@ -114,6 +114,7 @@ exports.attemptTokenRefresh = getFetchClient.attemptTokenRefresh;
114
114
  exports.getFetchClient = getFetchClient.getFetchClient;
115
115
  exports.isFetchError = getFetchClient.isFetchError;
116
116
  exports.refreshAccessToken = getFetchClient.refreshAccessToken;
117
+ exports.resetSessionExpiredNotification = getFetchClient.resetSessionExpiredNotification;
117
118
  exports.setOnSessionExpired = getFetchClient.setOnSessionExpired;
118
119
  exports.setOnTokenUpdate = getFetchClient.setOnTokenUpdate;
119
120
  exports.storeToken = getFetchClient.storeToken;
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -39,7 +39,7 @@ export { usePersistentState, usePersistentStateScope, useScopedPersistentState }
39
39
  export { useAdminUsers } from './admin/src/services/users.mjs';
40
40
  export { useGetCountDocumentsQuery } from './admin/src/services/homepage.mjs';
41
41
  export { translatedErrors } from './admin/src/utils/translatedErrors.mjs';
42
- export { ADMIN_REFRESH_LOCK, FetchError, attemptTokenRefresh, getFetchClient, isFetchError, refreshAccessToken, setOnSessionExpired, setOnTokenUpdate, storeToken, triggerSessionExpired } from './admin/src/utils/getFetchClient.mjs';
42
+ export { ADMIN_REFRESH_LOCK, FetchError, attemptTokenRefresh, getFetchClient, isFetchError, refreshAccessToken, resetSessionExpiredNotification, setOnSessionExpired, setOnTokenUpdate, storeToken, triggerSessionExpired } from './admin/src/utils/getFetchClient.mjs';
43
43
  export { fetchBaseQuery, isBaseQueryError } from './admin/src/utils/baseQuery.mjs';
44
44
  export { createRulesEngine } from './admin/src/utils/rulesEngine.mjs';
45
45
  export { getDisplayName, getInitials, hashAdminUserEmail } from './admin/src/utils/users.mjs';
@@ -0,0 +1,8 @@
1
+ interface DismissibleBannerProps {
2
+ message: React.ReactNode;
3
+ action?: React.ReactNode;
4
+ closeLabel: string;
5
+ onDismiss: () => void;
6
+ }
7
+ declare const DismissibleBanner: ({ message, action, closeLabel, onDismiss }: DismissibleBannerProps) => import("react/jsx-runtime").JSX.Element;
8
+ export { DismissibleBanner };
@@ -0,0 +1,2 @@
1
+ declare const MediaLibraryBanner: () => import("react/jsx-runtime").JSX.Element | null;
2
+ export { MediaLibraryBanner };
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Registers whether the current surface has unsaved edits so session-expiry
3
+ * logout can warn before clearing auth state (which unmounts forms).
4
+ */
5
+ declare const useRegisterUnsavedChanges: (id: string, enabled: boolean) => void;
6
+ export { useRegisterUnsavedChanges };
@@ -0,0 +1,88 @@
1
+ declare const SESSION_EXPIRY_BUFFER_MS = 1000;
2
+ /**
3
+ * How long before the access token's `exp` an active tab renews. Renewing
4
+ * ahead of expiry (rather than at it) means the refreshed token is already in
5
+ * shared storage by the time any *other* tab's post-expiry timer fires — so an
6
+ * idle sibling re-syncs instead of racing the refresh.
7
+ */
8
+ declare const SESSION_RENEW_MARGIN_MS = 10000;
9
+ interface UseSessionKeepaliveOptions {
10
+ /**
11
+ * The current admin access JWT (from Redux), or `null` when logged out.
12
+ */
13
+ token: string | null;
14
+ /**
15
+ * Called when a renewal attempt is rejected by the server (the session is
16
+ * truly over: refresh token idle/max window elapsed). This is a
17
+ * server-confirmed end of session, so it SHOULD broadcast a global logout to
18
+ * every tab.
19
+ */
20
+ onSessionDead?: () => void;
21
+ /**
22
+ * Read the access token currently in shared storage (localStorage/cookie).
23
+ * Because every open tab shares the same refresh cookie, an active tab that
24
+ * refreshes writes a newer access token here. When omitted, the hook never
25
+ * re-syncs across tabs.
26
+ */
27
+ getStoredToken?: () => string | null;
28
+ /**
29
+ * Adopt a newer shared token instead of treating this tab's token as stale.
30
+ * Typically dispatches `setToken`, which updates Redux and causes this hook
31
+ * to re-arm against the later `exp`. Only called when `getStoredToken`
32
+ * returns a token whose `exp` is later than the in-memory one.
33
+ */
34
+ onResync?: (token: string) => void;
35
+ /**
36
+ * Epoch ms of the user's last interaction (pointer, key, scroll, …). Used to
37
+ * decide whether to silently renew when the access token is about to expire.
38
+ * Idle tabs must NOT renew — that would defeat the server's
39
+ * `idleSessionLifespan` / `idleRefreshTokenLifespan`. When omitted, the hook
40
+ * never treats the tab as active.
41
+ */
42
+ getLastActivityAt?: () => number;
43
+ /**
44
+ * Silently renew the session (rotate the refresh token + mint a new access
45
+ * token). Resolves `true` on success — the resulting `setToken` re-arms this
46
+ * hook against the new `exp` — or `false` when the server rejects it. Only
47
+ * called when the user was active during the current token's lifetime.
48
+ */
49
+ renewSession?: () => Promise<boolean>;
50
+ /**
51
+ * Escape hatch for tests / dev environments where we don't want the
52
+ * automatic renew / re-sync behavior. Mirrors the existing `_disableRenewToken`
53
+ * prop on `<AuthProvider>`.
54
+ */
55
+ disabled?: boolean;
56
+ }
57
+ /**
58
+ * Keep the admin session alive while the user is active — without one tab
59
+ * tearing down another, and without logging anyone out because a short-lived
60
+ * access token aged out.
61
+ *
62
+ * The access token (`accessTokenLifespan`, e.g. 30 min) is transport plumbing,
63
+ * not the session length; the refresh token's idle/max window is the session.
64
+ * So expiry of the access JWT must never clear auth state or unmount the page:
65
+ * the next user-initiated request 401s, `withTokenRefresh` refreshes and
66
+ * retries, and only a *rejected* refresh ends the session (`onSessionDead`).
67
+ * Two timers are armed per token:
68
+ *
69
+ * - Renew (at `exp` minus a margin): if the user interacted during this token's
70
+ * lifetime, silently renew *ahead* of expiry. The new `setToken` re-arms the
71
+ * hook; a server rejection (`onSessionDead`) ends the session globally.
72
+ * Renewing early guarantees the refreshed token reaches shared storage before
73
+ * any sibling tab's post-expiry timer fires.
74
+ * - Post-expiry (just after `exp`): the fallback when this tab did not renew.
75
+ * It first re-syncs from a token another tab refreshed (so an idle tab rides
76
+ * on an active sibling), then renews if activity arrived late. If the tab is
77
+ * still idle, it does nothing destructive — the stale access token stays in
78
+ * place so form state survives until the next request.
79
+ *
80
+ * Idle tabs must not renew: a tab left open forever must not keep the session
81
+ * alive past the server's idle/max window.
82
+ *
83
+ * If the JWT can't be decoded (malformed, missing `exp`), the timers are skipped
84
+ * silently — the 401 refresh path still covers the next user-initiated request.
85
+ */
86
+ declare const useSessionKeepalive: ({ token, onSessionDead, getStoredToken, onResync, getLastActivityAt, renewSession, disabled, }: UseSessionKeepaliveOptions) => void;
87
+ export { useSessionKeepalive, SESSION_EXPIRY_BUFFER_MS, SESSION_RENEW_MARGIN_MS };
88
+ export type { UseSessionKeepaliveOptions };
@@ -15,18 +15,26 @@ export type ApiError = errors.ApplicationError | errors.ForbiddenError | errors.
15
15
  declare const setOnTokenUpdate: (callback: ((token: string) => void) | null) => void;
16
16
  /**
17
17
  * Set the callback that will be called when the active session is no longer
18
- * valid (refresh token rejected by the server, or detected idle on the
19
- * client). This lets the active tab redirect to /auth/login without waiting
20
- * for the next user-initiated request to fail.
18
+ * valid — i.e. the server rejected the refresh token, not merely that the
19
+ * short-lived access token expired. This lets the tab redirect to /auth/login
20
+ * (after any unsaved-changes guard) rather than sitting on a dead session.
21
21
  *
22
22
  * @param callback - Function to call when the session ends, or null to clear
23
23
  */
24
24
  declare const setOnSessionExpired: (callback: (() => void) | null) => void;
25
+ /**
26
+ * Re-arm session-expired notifications after a new access token is stored
27
+ * (silent refresh) or after a successful login.
28
+ */
29
+ declare const resetSessionExpiredNotification: () => void;
25
30
  /**
26
31
  * Trigger the registered session-expired callback, if any. Safe to call from
27
- * non-React code (e.g., the RTK Query baseQuery 401 handler).
32
+ * non-React code (e.g., the RTK Query baseQuery 401 handler). Returns `false`
33
+ * when nothing is listening, so callers can fall back to a hard redirect
34
+ * instead of leaving the user on a dead page. Subsequent calls during the same
35
+ * dead session return `true` without invoking the handler again.
28
36
  */
29
- declare const triggerSessionExpired: () => void;
37
+ declare const triggerSessionExpired: () => boolean;
30
38
  /**
31
39
  * Store the new token in the appropriate storage (localStorage or cookie)
32
40
  * and notify the app to update its state.
@@ -124,5 +132,5 @@ type FetchClient = {
124
132
  * ```
125
133
  */
126
134
  declare const getFetchClient: (defaultOptions?: FetchConfig) => FetchClient;
127
- export { getFetchClient, isFetchError, FetchError, attemptTokenRefresh, refreshAccessToken, storeToken, setOnTokenUpdate, setOnSessionExpired, triggerSessionExpired, ADMIN_REFRESH_LOCK, };
135
+ export { getFetchClient, isFetchError, FetchError, attemptTokenRefresh, refreshAccessToken, storeToken, setOnTokenUpdate, setOnSessionExpired, triggerSessionExpired, resetSessionExpiredNotification, ADMIN_REFRESH_LOCK, };
128
136
  export type { FetchOptions, FetchResponse, FetchConfig, FetchClient, ErrorResponse };
@@ -0,0 +1,11 @@
1
+ type UnsavedChangesCheck = () => boolean;
2
+ /**
3
+ * Register a callback that reports whether a form (or other surface) has unsaved
4
+ * edits. Used by the auth layer to warn before session-expiry logout discards
5
+ * in-memory state that route blockers never see (token clear unmounts the form).
6
+ */
7
+ declare const registerUnsavedChangesCheck: (id: string, check: UnsavedChangesCheck) => (() => void);
8
+ declare const hasUnsavedChanges: () => boolean;
9
+ declare const clearUnsavedChangesChecks: () => void;
10
+ export { registerUnsavedChangesCheck, hasUnsavedChanges, clearUnsavedChangesChecks };
11
+ export type { UnsavedChangesCheck };
@@ -36,5 +36,9 @@ export declare const actionTypes: {
36
36
  'release.entry.remove': string;
37
37
  'release.settings.update': string;
38
38
  'audit-log.export': string;
39
+ 'locale.create': string;
40
+ 'locale.update': string;
41
+ 'locale.delete': string;
42
+ 'locale.default.update': string;
39
43
  };
40
44
  export declare const getDefaultMessage: (value: keyof typeof actionTypes) => string;
@@ -60,4 +60,5 @@ export interface FieldContentSourceMap {
60
60
  locale: string | null;
61
61
  model?: UID.Schema;
62
62
  kind?: Struct.ContentTypeKind;
63
+ fieldPath?: string;
63
64
  }
@@ -1 +1 @@
1
- {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../shared/contracts/shared.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../shared/contracts/shared.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@strapi/admin",
3
- "version": "5.53.0",
3
+ "version": "5.54.0",
4
4
  "description": "Strapi Admin",
5
5
  "homepage": "https://strapi.io",
6
6
  "bugs": {
@@ -91,10 +91,10 @@
91
91
  "@reduxjs/toolkit": "1.9.7",
92
92
  "@strapi/design-system": "2.2.4",
93
93
  "@strapi/icons": "2.2.4",
94
- "@strapi/permissions": "5.53.0",
95
- "@strapi/types": "5.53.0",
96
- "@strapi/typescript-utils": "5.53.0",
97
- "@strapi/utils": "5.53.0",
94
+ "@strapi/permissions": "5.54.0",
95
+ "@strapi/types": "5.54.0",
96
+ "@strapi/typescript-utils": "5.54.0",
97
+ "@strapi/utils": "5.54.0",
98
98
  "@testing-library/dom": "10.4.1",
99
99
  "@testing-library/react": "16.3.2",
100
100
  "@testing-library/user-event": "14.6.1",
@@ -148,8 +148,8 @@
148
148
  "zod": "4.4.3"
149
149
  },
150
150
  "devDependencies": {
151
- "@strapi/admin-test-utils": "5.53.0",
152
- "@strapi/data-transfer": "5.53.0",
151
+ "@strapi/admin-test-utils": "5.54.0",
152
+ "@strapi/data-transfer": "5.54.0",
153
153
  "@testing-library/jest-dom": "6.9.1",
154
154
  "@types/codemirror5": "npm:@types/codemirror@^5.60.15",
155
155
  "@types/fs-extra": "11.0.4",
@@ -1 +0,0 @@
1
- {"version":3,"file":"useIdleSessionLogout.js","sources":["../../../../../admin/src/hooks/useIdleSessionLogout.ts"],"sourcesContent":["import * as React from 'react';\n\nimport { decodeAccessTokenExpiry } from '../utils/jwt';\n\nconst SESSION_EXPIRY_BUFFER_MS = 1000;\n/**\n * How long before the access token's `exp` an active tab renews. Renewing\n * ahead of expiry (rather than at it) means the refreshed token is already in\n * shared storage by the time any *other* tab's logout timer fires — so an idle\n * sibling re-syncs instead of racing the refresh and logging itself out.\n */\nconst SESSION_RENEW_MARGIN_MS = 10_000;\n\ninterface UseIdleSessionLogoutOptions {\n /**\n * The current admin access JWT (from Redux), or `null` when logged out.\n */\n token: string | null;\n /**\n * Called when the timer fires and the user is genuinely idle. Should clear\n * *this tab's* local auth state and navigate to /auth/login. It must NOT\n * broadcast a global logout (e.g. by removing the shared `isLoggedIn` key),\n * otherwise an idle tab firing on a stale token would log out other tabs that\n * are still active — see the cross-tab handling below.\n */\n onExpired: () => void;\n /**\n * Called when a renewal attempt is rejected by the server (the session is\n * truly over: refresh token idle/max window elapsed). This is a\n * server-confirmed end of session, so unlike `onExpired` it SHOULD broadcast\n * a global logout to every tab. Falls back to `onExpired` when omitted.\n */\n onSessionDead?: () => void;\n /**\n * Read the access token currently in shared storage (localStorage/cookie).\n * Because every open tab shares the same refresh cookie, an active tab that\n * refreshes writes a newer access token here. When omitted, the hook never\n * re-syncs across tabs.\n */\n getStoredToken?: () => string | null;\n /**\n * Adopt a newer shared token instead of expiring. Typically dispatches\n * `setToken`, which updates Redux and causes this hook to re-arm against the\n * later `exp`. Only called when `getStoredToken` returns a token whose `exp`\n * is later than the in-memory one.\n */\n onResync?: (token: string) => void;\n /**\n * Epoch ms of the user's last interaction (pointer, key, scroll, …). Used to\n * decide whether to silently renew (active) or log out (idle) when the access\n * token expires. When omitted, the hook always treats expiry as idle.\n */\n getLastActivityAt?: () => number;\n /**\n * Silently renew the session (rotate the refresh token + mint a new access\n * token). Resolves `true` on success — the resulting `setToken` re-arms this\n * hook against the new `exp` — or `false` when the server rejects it. Only\n * called when the user was active during the current token's lifetime.\n */\n renewSession?: () => Promise<boolean>;\n /**\n * Escape hatch for tests / dev environments where we don't want the\n * automatic logout behavior. Mirrors the existing `_disableRenewToken`\n * prop on `<AuthProvider>`.\n */\n disabled?: boolean;\n}\n\n/**\n * Keep the admin session alive while the user is active, and end it when they\n * are genuinely idle — without one tab tearing down another.\n *\n * The access token is short-lived (`accessTokenLifespan`, e.g. 30 min) and is\n * meant to be refreshed, not to be the session length. Two timers are armed per\n * token:\n *\n * - Renew (at `exp` minus a margin): if the user interacted during this token's\n * lifetime, silently renew *ahead* of expiry. The new `setToken` re-arms the\n * hook; a server rejection (`onSessionDead`) ends the session globally.\n * Renewing early guarantees the refreshed token reaches shared storage before\n * any sibling tab's logout timer fires.\n * - Logout (just after `exp`): the fallback when this tab did not renew. It\n * first re-syncs from a token another tab refreshed (so an idle tab rides on\n * an active sibling), then renews if activity arrived late, and only otherwise\n * logs out — locally, so idle tabs never force-logout active ones.\n *\n * If the JWT can't be decoded (malformed, missing `exp`), the timers are skipped\n * silently — the active-tab redirect on 401 still covers the symptom on the next\n * user-initiated request.\n */\nconst useIdleSessionLogout = ({\n token,\n onExpired,\n onSessionDead,\n getStoredToken,\n onResync,\n getLastActivityAt,\n renewSession,\n disabled = false,\n}: UseIdleSessionLogoutOptions): void => {\n React.useEffect(() => {\n if (!token || disabled) {\n return undefined;\n }\n\n const expiry = decodeAccessTokenExpiry(token);\n if (expiry === null) {\n return undefined;\n }\n\n // When this token became active. Activity recorded after this point means\n // the user was present during the token's lifetime.\n const armedAt = Date.now();\n const wasActive = () =>\n typeof getLastActivityAt === 'function' && getLastActivityAt() >= armedAt;\n\n // Adopt a fresher token another tab wrote to shared storage. Returns true\n // when it re-synced (caller should stop).\n const tryResync = (): boolean => {\n const storedToken = getStoredToken?.() ?? null;\n if (storedToken && storedToken !== token && onResync) {\n const storedExpiry = decodeAccessTokenExpiry(storedToken);\n if (storedExpiry !== null && storedExpiry > expiry) {\n onResync(storedToken);\n return true;\n }\n }\n return false;\n };\n\n const renew = () => {\n renewSession!().then((renewed) => {\n if (!renewed) {\n // Server rejected the refresh: the session is truly over.\n (onSessionDead ?? onExpired)();\n }\n // On success, storeToken -> setToken updates the token prop, which\n // re-runs this effect and re-arms against the new exp.\n });\n };\n\n // Renew ahead of expiry while the user is active.\n const renewDelay = Math.max(expiry - armedAt - SESSION_RENEW_MARGIN_MS, 0);\n const renewTimeoutId = window.setTimeout(() => {\n if (tryResync()) {\n return;\n }\n if (wasActive() && renewSession) {\n renew();\n }\n }, renewDelay);\n\n // Fallback decision just after expiry, for tabs that did not renew.\n const logoutDelay = Math.max(expiry - armedAt + SESSION_EXPIRY_BUFFER_MS, 0);\n const logoutTimeoutId = window.setTimeout(() => {\n if (tryResync()) {\n return;\n }\n if (wasActive() && renewSession) {\n renew();\n return;\n }\n // Genuinely idle: log out only this tab.\n onExpired();\n }, logoutDelay);\n\n return () => {\n window.clearTimeout(renewTimeoutId);\n window.clearTimeout(logoutTimeoutId);\n };\n }, [\n token,\n onExpired,\n onSessionDead,\n getStoredToken,\n onResync,\n getLastActivityAt,\n renewSession,\n disabled,\n ]);\n};\n\nexport { useIdleSessionLogout, SESSION_EXPIRY_BUFFER_MS, SESSION_RENEW_MARGIN_MS };\nexport type { UseIdleSessionLogoutOptions };\n"],"names":["SESSION_EXPIRY_BUFFER_MS","SESSION_RENEW_MARGIN_MS","useIdleSessionLogout","token","onExpired","onSessionDead","getStoredToken","onResync","getLastActivityAt","renewSession","disabled","React","useEffect","undefined","expiry","decodeAccessTokenExpiry","armedAt","Date","now","wasActive","tryResync","storedToken","storedExpiry","renew","then","renewed","renewDelay","Math","max","renewTimeoutId","window","setTimeout","logoutDelay","logoutTimeoutId","clearTimeout"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"useIdleSessionLogout.mjs","sources":["../../../../../admin/src/hooks/useIdleSessionLogout.ts"],"sourcesContent":["import * as React from 'react';\n\nimport { decodeAccessTokenExpiry } from '../utils/jwt';\n\nconst SESSION_EXPIRY_BUFFER_MS = 1000;\n/**\n * How long before the access token's `exp` an active tab renews. Renewing\n * ahead of expiry (rather than at it) means the refreshed token is already in\n * shared storage by the time any *other* tab's logout timer fires — so an idle\n * sibling re-syncs instead of racing the refresh and logging itself out.\n */\nconst SESSION_RENEW_MARGIN_MS = 10_000;\n\ninterface UseIdleSessionLogoutOptions {\n /**\n * The current admin access JWT (from Redux), or `null` when logged out.\n */\n token: string | null;\n /**\n * Called when the timer fires and the user is genuinely idle. Should clear\n * *this tab's* local auth state and navigate to /auth/login. It must NOT\n * broadcast a global logout (e.g. by removing the shared `isLoggedIn` key),\n * otherwise an idle tab firing on a stale token would log out other tabs that\n * are still active — see the cross-tab handling below.\n */\n onExpired: () => void;\n /**\n * Called when a renewal attempt is rejected by the server (the session is\n * truly over: refresh token idle/max window elapsed). This is a\n * server-confirmed end of session, so unlike `onExpired` it SHOULD broadcast\n * a global logout to every tab. Falls back to `onExpired` when omitted.\n */\n onSessionDead?: () => void;\n /**\n * Read the access token currently in shared storage (localStorage/cookie).\n * Because every open tab shares the same refresh cookie, an active tab that\n * refreshes writes a newer access token here. When omitted, the hook never\n * re-syncs across tabs.\n */\n getStoredToken?: () => string | null;\n /**\n * Adopt a newer shared token instead of expiring. Typically dispatches\n * `setToken`, which updates Redux and causes this hook to re-arm against the\n * later `exp`. Only called when `getStoredToken` returns a token whose `exp`\n * is later than the in-memory one.\n */\n onResync?: (token: string) => void;\n /**\n * Epoch ms of the user's last interaction (pointer, key, scroll, …). Used to\n * decide whether to silently renew (active) or log out (idle) when the access\n * token expires. When omitted, the hook always treats expiry as idle.\n */\n getLastActivityAt?: () => number;\n /**\n * Silently renew the session (rotate the refresh token + mint a new access\n * token). Resolves `true` on success — the resulting `setToken` re-arms this\n * hook against the new `exp` — or `false` when the server rejects it. Only\n * called when the user was active during the current token's lifetime.\n */\n renewSession?: () => Promise<boolean>;\n /**\n * Escape hatch for tests / dev environments where we don't want the\n * automatic logout behavior. Mirrors the existing `_disableRenewToken`\n * prop on `<AuthProvider>`.\n */\n disabled?: boolean;\n}\n\n/**\n * Keep the admin session alive while the user is active, and end it when they\n * are genuinely idle — without one tab tearing down another.\n *\n * The access token is short-lived (`accessTokenLifespan`, e.g. 30 min) and is\n * meant to be refreshed, not to be the session length. Two timers are armed per\n * token:\n *\n * - Renew (at `exp` minus a margin): if the user interacted during this token's\n * lifetime, silently renew *ahead* of expiry. The new `setToken` re-arms the\n * hook; a server rejection (`onSessionDead`) ends the session globally.\n * Renewing early guarantees the refreshed token reaches shared storage before\n * any sibling tab's logout timer fires.\n * - Logout (just after `exp`): the fallback when this tab did not renew. It\n * first re-syncs from a token another tab refreshed (so an idle tab rides on\n * an active sibling), then renews if activity arrived late, and only otherwise\n * logs out — locally, so idle tabs never force-logout active ones.\n *\n * If the JWT can't be decoded (malformed, missing `exp`), the timers are skipped\n * silently — the active-tab redirect on 401 still covers the symptom on the next\n * user-initiated request.\n */\nconst useIdleSessionLogout = ({\n token,\n onExpired,\n onSessionDead,\n getStoredToken,\n onResync,\n getLastActivityAt,\n renewSession,\n disabled = false,\n}: UseIdleSessionLogoutOptions): void => {\n React.useEffect(() => {\n if (!token || disabled) {\n return undefined;\n }\n\n const expiry = decodeAccessTokenExpiry(token);\n if (expiry === null) {\n return undefined;\n }\n\n // When this token became active. Activity recorded after this point means\n // the user was present during the token's lifetime.\n const armedAt = Date.now();\n const wasActive = () =>\n typeof getLastActivityAt === 'function' && getLastActivityAt() >= armedAt;\n\n // Adopt a fresher token another tab wrote to shared storage. Returns true\n // when it re-synced (caller should stop).\n const tryResync = (): boolean => {\n const storedToken = getStoredToken?.() ?? null;\n if (storedToken && storedToken !== token && onResync) {\n const storedExpiry = decodeAccessTokenExpiry(storedToken);\n if (storedExpiry !== null && storedExpiry > expiry) {\n onResync(storedToken);\n return true;\n }\n }\n return false;\n };\n\n const renew = () => {\n renewSession!().then((renewed) => {\n if (!renewed) {\n // Server rejected the refresh: the session is truly over.\n (onSessionDead ?? onExpired)();\n }\n // On success, storeToken -> setToken updates the token prop, which\n // re-runs this effect and re-arms against the new exp.\n });\n };\n\n // Renew ahead of expiry while the user is active.\n const renewDelay = Math.max(expiry - armedAt - SESSION_RENEW_MARGIN_MS, 0);\n const renewTimeoutId = window.setTimeout(() => {\n if (tryResync()) {\n return;\n }\n if (wasActive() && renewSession) {\n renew();\n }\n }, renewDelay);\n\n // Fallback decision just after expiry, for tabs that did not renew.\n const logoutDelay = Math.max(expiry - armedAt + SESSION_EXPIRY_BUFFER_MS, 0);\n const logoutTimeoutId = window.setTimeout(() => {\n if (tryResync()) {\n return;\n }\n if (wasActive() && renewSession) {\n renew();\n return;\n }\n // Genuinely idle: log out only this tab.\n onExpired();\n }, logoutDelay);\n\n return () => {\n window.clearTimeout(renewTimeoutId);\n window.clearTimeout(logoutTimeoutId);\n };\n }, [\n token,\n onExpired,\n onSessionDead,\n getStoredToken,\n onResync,\n getLastActivityAt,\n renewSession,\n disabled,\n ]);\n};\n\nexport { useIdleSessionLogout, SESSION_EXPIRY_BUFFER_MS, SESSION_RENEW_MARGIN_MS };\nexport type { UseIdleSessionLogoutOptions };\n"],"names":["SESSION_EXPIRY_BUFFER_MS","SESSION_RENEW_MARGIN_MS","useIdleSessionLogout","token","onExpired","onSessionDead","getStoredToken","onResync","getLastActivityAt","renewSession","disabled","React","useEffect","undefined","expiry","decodeAccessTokenExpiry","armedAt","Date","now","wasActive","tryResync","storedToken","storedExpiry","renew","then","renewed","renewDelay","Math","max","renewTimeoutId","window","setTimeout","logoutDelay","logoutTimeoutId","clearTimeout"],"mappings":"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"}
@@ -1,87 +0,0 @@
1
- declare const SESSION_EXPIRY_BUFFER_MS = 1000;
2
- /**
3
- * How long before the access token's `exp` an active tab renews. Renewing
4
- * ahead of expiry (rather than at it) means the refreshed token is already in
5
- * shared storage by the time any *other* tab's logout timer fires — so an idle
6
- * sibling re-syncs instead of racing the refresh and logging itself out.
7
- */
8
- declare const SESSION_RENEW_MARGIN_MS = 10000;
9
- interface UseIdleSessionLogoutOptions {
10
- /**
11
- * The current admin access JWT (from Redux), or `null` when logged out.
12
- */
13
- token: string | null;
14
- /**
15
- * Called when the timer fires and the user is genuinely idle. Should clear
16
- * *this tab's* local auth state and navigate to /auth/login. It must NOT
17
- * broadcast a global logout (e.g. by removing the shared `isLoggedIn` key),
18
- * otherwise an idle tab firing on a stale token would log out other tabs that
19
- * are still active — see the cross-tab handling below.
20
- */
21
- onExpired: () => void;
22
- /**
23
- * Called when a renewal attempt is rejected by the server (the session is
24
- * truly over: refresh token idle/max window elapsed). This is a
25
- * server-confirmed end of session, so unlike `onExpired` it SHOULD broadcast
26
- * a global logout to every tab. Falls back to `onExpired` when omitted.
27
- */
28
- onSessionDead?: () => void;
29
- /**
30
- * Read the access token currently in shared storage (localStorage/cookie).
31
- * Because every open tab shares the same refresh cookie, an active tab that
32
- * refreshes writes a newer access token here. When omitted, the hook never
33
- * re-syncs across tabs.
34
- */
35
- getStoredToken?: () => string | null;
36
- /**
37
- * Adopt a newer shared token instead of expiring. Typically dispatches
38
- * `setToken`, which updates Redux and causes this hook to re-arm against the
39
- * later `exp`. Only called when `getStoredToken` returns a token whose `exp`
40
- * is later than the in-memory one.
41
- */
42
- onResync?: (token: string) => void;
43
- /**
44
- * Epoch ms of the user's last interaction (pointer, key, scroll, …). Used to
45
- * decide whether to silently renew (active) or log out (idle) when the access
46
- * token expires. When omitted, the hook always treats expiry as idle.
47
- */
48
- getLastActivityAt?: () => number;
49
- /**
50
- * Silently renew the session (rotate the refresh token + mint a new access
51
- * token). Resolves `true` on success — the resulting `setToken` re-arms this
52
- * hook against the new `exp` — or `false` when the server rejects it. Only
53
- * called when the user was active during the current token's lifetime.
54
- */
55
- renewSession?: () => Promise<boolean>;
56
- /**
57
- * Escape hatch for tests / dev environments where we don't want the
58
- * automatic logout behavior. Mirrors the existing `_disableRenewToken`
59
- * prop on `<AuthProvider>`.
60
- */
61
- disabled?: boolean;
62
- }
63
- /**
64
- * Keep the admin session alive while the user is active, and end it when they
65
- * are genuinely idle — without one tab tearing down another.
66
- *
67
- * The access token is short-lived (`accessTokenLifespan`, e.g. 30 min) and is
68
- * meant to be refreshed, not to be the session length. Two timers are armed per
69
- * token:
70
- *
71
- * - Renew (at `exp` minus a margin): if the user interacted during this token's
72
- * lifetime, silently renew *ahead* of expiry. The new `setToken` re-arms the
73
- * hook; a server rejection (`onSessionDead`) ends the session globally.
74
- * Renewing early guarantees the refreshed token reaches shared storage before
75
- * any sibling tab's logout timer fires.
76
- * - Logout (just after `exp`): the fallback when this tab did not renew. It
77
- * first re-syncs from a token another tab refreshed (so an idle tab rides on
78
- * an active sibling), then renews if activity arrived late, and only otherwise
79
- * logs out — locally, so idle tabs never force-logout active ones.
80
- *
81
- * If the JWT can't be decoded (malformed, missing `exp`), the timers are skipped
82
- * silently — the active-tab redirect on 401 still covers the symptom on the next
83
- * user-initiated request.
84
- */
85
- declare const useIdleSessionLogout: ({ token, onExpired, onSessionDead, getStoredToken, onResync, getLastActivityAt, renewSession, disabled, }: UseIdleSessionLogoutOptions) => void;
86
- export { useIdleSessionLogout, SESSION_EXPIRY_BUFFER_MS, SESSION_RENEW_MARGIN_MS };
87
- export type { UseIdleSessionLogoutOptions };