@strapi/admin 5.52.0 → 5.52.2
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.
- package/dist/admin/admin/src/components/PageHelpers.js +7 -5
- package/dist/admin/admin/src/components/PageHelpers.js.map +1 -1
- package/dist/admin/admin/src/components/PageHelpers.mjs +7 -5
- package/dist/admin/admin/src/components/PageHelpers.mjs.map +1 -1
- package/dist/admin/admin/src/pages/Auth/AuthPage.js +14 -9
- package/dist/admin/admin/src/pages/Auth/AuthPage.js.map +1 -1
- package/dist/admin/admin/src/pages/Auth/AuthPage.mjs +15 -10
- package/dist/admin/admin/src/pages/Auth/AuthPage.mjs.map +1 -1
- package/dist/admin/admin/src/pages/Auth/components/Login.js +2 -6
- package/dist/admin/admin/src/pages/Auth/components/Login.js.map +1 -1
- package/dist/admin/admin/src/pages/Auth/components/Login.mjs +2 -6
- package/dist/admin/admin/src/pages/Auth/components/Login.mjs.map +1 -1
- package/dist/admin/admin/src/pages/Auth/constants.js +24 -8
- package/dist/admin/admin/src/pages/Auth/constants.js.map +1 -1
- package/dist/admin/admin/src/pages/Auth/constants.mjs +23 -9
- package/dist/admin/admin/src/pages/Auth/constants.mjs.map +1 -1
- package/dist/admin/admin/src/pages/Auth/utils.js +34 -0
- package/dist/admin/admin/src/pages/Auth/utils.js.map +1 -0
- package/dist/admin/admin/src/pages/Auth/utils.mjs +32 -0
- package/dist/admin/admin/src/pages/Auth/utils.mjs.map +1 -0
- package/dist/admin/admin/src/translations/pt-BR.json.js +1 -1
- package/dist/admin/admin/src/translations/pt-BR.json.mjs +1 -1
- package/dist/admin/admin/src/utils/getFetchClient.js +4 -0
- package/dist/admin/admin/src/utils/getFetchClient.js.map +1 -1
- package/dist/admin/admin/src/utils/getFetchClient.mjs +4 -0
- package/dist/admin/admin/src/utils/getFetchClient.mjs.map +1 -1
- package/dist/admin/admin/src/utils/rulesEngine.js +27 -9
- package/dist/admin/admin/src/utils/rulesEngine.js.map +1 -1
- package/dist/admin/admin/src/utils/rulesEngine.mjs +2 -2
- package/dist/admin/admin/src/utils/rulesEngine.mjs.map +1 -1
- package/dist/admin/index.js +0 -1
- package/dist/admin/index.js.map +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/admin/src/pages/Auth/constants.d.ts +19 -5
- package/dist/admin/src/pages/Auth/utils.d.ts +7 -0
- package/dist/admin/src/utils/rulesEngine.d.ts +9 -13
- package/dist/ee/server/src/audit-logs/services/audit-logs.d.ts.map +1 -1
- package/dist/server/ee/server/src/audit-logs/services/audit-logs.js +22 -4
- package/dist/server/ee/server/src/audit-logs/services/audit-logs.js.map +1 -1
- package/dist/server/ee/server/src/audit-logs/services/audit-logs.mjs +22 -4
- package/dist/server/ee/server/src/audit-logs/services/audit-logs.mjs.map +1 -1
- package/dist/server/server/src/controllers/validation/schema.js +36 -16
- package/dist/server/server/src/controllers/validation/schema.js.map +1 -1
- package/dist/server/server/src/controllers/validation/schema.mjs +1 -1
- package/dist/server/server/src/controllers/validation/schema.mjs.map +1 -1
- package/dist/server/server/src/services/content-type.js +8 -3
- package/dist/server/server/src/services/content-type.js.map +1 -1
- package/dist/server/server/src/services/content-type.mjs +8 -3
- package/dist/server/server/src/services/content-type.mjs.map +1 -1
- package/dist/server/server/src/services/permission/queries.js +3 -1
- package/dist/server/server/src/services/permission/queries.js.map +1 -1
- package/dist/server/server/src/services/permission/queries.mjs +3 -1
- package/dist/server/server/src/services/permission/queries.mjs.map +1 -1
- package/dist/server/src/controllers/validation/schema.d.ts +8 -48
- package/dist/server/src/controllers/validation/schema.d.ts.map +1 -1
- package/dist/server/src/services/content-type.d.ts.map +1 -1
- package/dist/server/src/services/permission/queries.d.ts.map +1 -1
- package/package.json +8 -8
|
@@ -6,18 +6,34 @@ var Oops = require('./components/Oops.js');
|
|
|
6
6
|
var Register = require('./components/Register.js');
|
|
7
7
|
var ResetPassword = require('./components/ResetPassword.js');
|
|
8
8
|
|
|
9
|
+
const AUTH_TYPES = {
|
|
10
|
+
LOGIN: 'login',
|
|
11
|
+
REGISTER: 'register',
|
|
12
|
+
REGISTER_ADMIN: 'register-admin',
|
|
13
|
+
FORGOT_PASSWORD: 'forgot-password',
|
|
14
|
+
RESET_PASSWORD: 'reset-password',
|
|
15
|
+
FORGOT_PASSWORD_SUCCESS: 'forgot-password-success',
|
|
16
|
+
OOPS: 'oops',
|
|
17
|
+
PROVIDERS: 'providers'
|
|
18
|
+
};
|
|
19
|
+
const AUTH_TYPE_VALUES = Object.values(AUTH_TYPES);
|
|
20
|
+
/**
|
|
21
|
+
* Narrows the `:authType` route param, which is an arbitrary string, to a known auth type.
|
|
22
|
+
*/ const isAuthType = (value)=>value !== undefined && AUTH_TYPE_VALUES.includes(value);
|
|
9
23
|
const FORMS = {
|
|
10
|
-
|
|
11
|
-
|
|
24
|
+
[AUTH_TYPES.FORGOT_PASSWORD]: ForgotPassword.ForgotPassword,
|
|
25
|
+
[AUTH_TYPES.FORGOT_PASSWORD_SUCCESS]: ForgotPasswordSuccess.ForgotPasswordSuccess,
|
|
12
26
|
// the `Component` attribute is set after all forms and CE/EE components are loaded, but since we
|
|
13
27
|
// are here outside of a React component we can not use the hook directly
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
28
|
+
[AUTH_TYPES.LOGIN]: ()=>null,
|
|
29
|
+
[AUTH_TYPES.OOPS]: Oops.Oops,
|
|
30
|
+
[AUTH_TYPES.REGISTER]: Register.Register,
|
|
31
|
+
[AUTH_TYPES.REGISTER_ADMIN]: Register.Register,
|
|
32
|
+
[AUTH_TYPES.RESET_PASSWORD]: ResetPassword.ResetPassword,
|
|
33
|
+
[AUTH_TYPES.PROVIDERS]: ()=>null
|
|
20
34
|
};
|
|
21
35
|
|
|
36
|
+
exports.AUTH_TYPES = AUTH_TYPES;
|
|
22
37
|
exports.FORMS = FORMS;
|
|
38
|
+
exports.isAuthType = isAuthType;
|
|
23
39
|
//# sourceMappingURL=constants.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.js","sources":["../../../../../../admin/src/pages/Auth/constants.ts"],"sourcesContent":["import type { ComponentType } from 'react';\n\nimport { ForgotPassword } from './components/ForgotPassword';\nimport { ForgotPasswordSuccess } from './components/ForgotPasswordSuccess';\nimport { Oops } from './components/Oops';\nimport { Register, RegisterProps } from './components/Register';\nimport { ResetPassword } from './components/ResetPassword';\n\nexport
|
|
1
|
+
{"version":3,"file":"constants.js","sources":["../../../../../../admin/src/pages/Auth/constants.ts"],"sourcesContent":["import type { ComponentType } from 'react';\n\nimport { ForgotPassword } from './components/ForgotPassword';\nimport { ForgotPasswordSuccess } from './components/ForgotPasswordSuccess';\nimport { Oops } from './components/Oops';\nimport { Register, RegisterProps } from './components/Register';\nimport { ResetPassword } from './components/ResetPassword';\n\nexport const AUTH_TYPES = {\n LOGIN: 'login',\n REGISTER: 'register',\n REGISTER_ADMIN: 'register-admin',\n FORGOT_PASSWORD: 'forgot-password',\n RESET_PASSWORD: 'reset-password',\n FORGOT_PASSWORD_SUCCESS: 'forgot-password-success',\n OOPS: 'oops',\n PROVIDERS: 'providers',\n} as const;\n\nexport type AuthType = (typeof AUTH_TYPES)[keyof typeof AUTH_TYPES];\n\nconst AUTH_TYPE_VALUES: readonly string[] = Object.values(AUTH_TYPES);\n\n/**\n * Narrows the `:authType` route param, which is an arbitrary string, to a known auth type.\n */\nexport const isAuthType = (value: string | undefined): value is AuthType =>\n value !== undefined && AUTH_TYPE_VALUES.includes(value);\n\nexport type FormDictionary = Record<AuthType, ComponentType | ComponentType<RegisterProps>>;\n\nexport const FORMS = {\n [AUTH_TYPES.FORGOT_PASSWORD]: ForgotPassword,\n [AUTH_TYPES.FORGOT_PASSWORD_SUCCESS]: ForgotPasswordSuccess,\n // the `Component` attribute is set after all forms and CE/EE components are loaded, but since we\n // are here outside of a React component we can not use the hook directly\n [AUTH_TYPES.LOGIN]: () => null,\n [AUTH_TYPES.OOPS]: Oops,\n [AUTH_TYPES.REGISTER]: Register,\n [AUTH_TYPES.REGISTER_ADMIN]: Register,\n [AUTH_TYPES.RESET_PASSWORD]: ResetPassword,\n [AUTH_TYPES.PROVIDERS]: () => null,\n} satisfies FormDictionary;\n"],"names":["AUTH_TYPES","LOGIN","REGISTER","REGISTER_ADMIN","FORGOT_PASSWORD","RESET_PASSWORD","FORGOT_PASSWORD_SUCCESS","OOPS","PROVIDERS","AUTH_TYPE_VALUES","Object","values","isAuthType","value","undefined","includes","FORMS","ForgotPassword","ForgotPasswordSuccess","Oops","Register","ResetPassword"],"mappings":";;;;;;;;MAQaA,UAAAA,GAAa;IACxBC,KAAAA,EAAO,OAAA;IACPC,QAAAA,EAAU,UAAA;IACVC,cAAAA,EAAgB,gBAAA;IAChBC,eAAAA,EAAiB,iBAAA;IACjBC,cAAAA,EAAgB,gBAAA;IAChBC,uBAAAA,EAAyB,yBAAA;IACzBC,IAAAA,EAAM,MAAA;IACNC,SAAAA,EAAW;AACb;AAIA,MAAMC,gBAAAA,GAAsCC,MAAAA,CAAOC,MAAM,CAACX,UAAAA,CAAAA;AAE1D;;IAGO,MAAMY,UAAAA,GAAa,CAACC,KAAAA,GACzBA,UAAUC,SAAAA,IAAaL,gBAAAA,CAAiBM,QAAQ,CAACF,KAAAA;MAItCG,KAAAA,GAAQ;IACnB,CAAChB,UAAAA,CAAWI,eAAe,GAAGa,6BAAAA;IAC9B,CAACjB,UAAAA,CAAWM,uBAAuB,GAAGY,2CAAAA;;;AAGtC,IAAA,CAAClB,UAAAA,CAAWC,KAAK,GAAG,IAAM,IAAA;IAC1B,CAACD,UAAAA,CAAWO,IAAI,GAAGY,SAAAA;IACnB,CAACnB,UAAAA,CAAWE,QAAQ,GAAGkB,iBAAAA;IACvB,CAACpB,UAAAA,CAAWG,cAAc,GAAGiB,iBAAAA;IAC7B,CAACpB,UAAAA,CAAWK,cAAc,GAAGgB,2BAAAA;AAC7B,IAAA,CAACrB,UAAAA,CAAWQ,SAAS,GAAG,IAAM;AAChC;;;;;;"}
|
|
@@ -4,18 +4,32 @@ import { Oops } from './components/Oops.mjs';
|
|
|
4
4
|
import { Register } from './components/Register.mjs';
|
|
5
5
|
import { ResetPassword } from './components/ResetPassword.mjs';
|
|
6
6
|
|
|
7
|
+
const AUTH_TYPES = {
|
|
8
|
+
LOGIN: 'login',
|
|
9
|
+
REGISTER: 'register',
|
|
10
|
+
REGISTER_ADMIN: 'register-admin',
|
|
11
|
+
FORGOT_PASSWORD: 'forgot-password',
|
|
12
|
+
RESET_PASSWORD: 'reset-password',
|
|
13
|
+
FORGOT_PASSWORD_SUCCESS: 'forgot-password-success',
|
|
14
|
+
OOPS: 'oops',
|
|
15
|
+
PROVIDERS: 'providers'
|
|
16
|
+
};
|
|
17
|
+
const AUTH_TYPE_VALUES = Object.values(AUTH_TYPES);
|
|
18
|
+
/**
|
|
19
|
+
* Narrows the `:authType` route param, which is an arbitrary string, to a known auth type.
|
|
20
|
+
*/ const isAuthType = (value)=>value !== undefined && AUTH_TYPE_VALUES.includes(value);
|
|
7
21
|
const FORMS = {
|
|
8
|
-
|
|
9
|
-
|
|
22
|
+
[AUTH_TYPES.FORGOT_PASSWORD]: ForgotPassword,
|
|
23
|
+
[AUTH_TYPES.FORGOT_PASSWORD_SUCCESS]: ForgotPasswordSuccess,
|
|
10
24
|
// the `Component` attribute is set after all forms and CE/EE components are loaded, but since we
|
|
11
25
|
// are here outside of a React component we can not use the hook directly
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
26
|
+
[AUTH_TYPES.LOGIN]: ()=>null,
|
|
27
|
+
[AUTH_TYPES.OOPS]: Oops,
|
|
28
|
+
[AUTH_TYPES.REGISTER]: Register,
|
|
29
|
+
[AUTH_TYPES.REGISTER_ADMIN]: Register,
|
|
30
|
+
[AUTH_TYPES.RESET_PASSWORD]: ResetPassword,
|
|
31
|
+
[AUTH_TYPES.PROVIDERS]: ()=>null
|
|
18
32
|
};
|
|
19
33
|
|
|
20
|
-
export { FORMS };
|
|
34
|
+
export { AUTH_TYPES, FORMS, isAuthType };
|
|
21
35
|
//# sourceMappingURL=constants.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.mjs","sources":["../../../../../../admin/src/pages/Auth/constants.ts"],"sourcesContent":["import type { ComponentType } from 'react';\n\nimport { ForgotPassword } from './components/ForgotPassword';\nimport { ForgotPasswordSuccess } from './components/ForgotPasswordSuccess';\nimport { Oops } from './components/Oops';\nimport { Register, RegisterProps } from './components/Register';\nimport { ResetPassword } from './components/ResetPassword';\n\nexport
|
|
1
|
+
{"version":3,"file":"constants.mjs","sources":["../../../../../../admin/src/pages/Auth/constants.ts"],"sourcesContent":["import type { ComponentType } from 'react';\n\nimport { ForgotPassword } from './components/ForgotPassword';\nimport { ForgotPasswordSuccess } from './components/ForgotPasswordSuccess';\nimport { Oops } from './components/Oops';\nimport { Register, RegisterProps } from './components/Register';\nimport { ResetPassword } from './components/ResetPassword';\n\nexport const AUTH_TYPES = {\n LOGIN: 'login',\n REGISTER: 'register',\n REGISTER_ADMIN: 'register-admin',\n FORGOT_PASSWORD: 'forgot-password',\n RESET_PASSWORD: 'reset-password',\n FORGOT_PASSWORD_SUCCESS: 'forgot-password-success',\n OOPS: 'oops',\n PROVIDERS: 'providers',\n} as const;\n\nexport type AuthType = (typeof AUTH_TYPES)[keyof typeof AUTH_TYPES];\n\nconst AUTH_TYPE_VALUES: readonly string[] = Object.values(AUTH_TYPES);\n\n/**\n * Narrows the `:authType` route param, which is an arbitrary string, to a known auth type.\n */\nexport const isAuthType = (value: string | undefined): value is AuthType =>\n value !== undefined && AUTH_TYPE_VALUES.includes(value);\n\nexport type FormDictionary = Record<AuthType, ComponentType | ComponentType<RegisterProps>>;\n\nexport const FORMS = {\n [AUTH_TYPES.FORGOT_PASSWORD]: ForgotPassword,\n [AUTH_TYPES.FORGOT_PASSWORD_SUCCESS]: ForgotPasswordSuccess,\n // the `Component` attribute is set after all forms and CE/EE components are loaded, but since we\n // are here outside of a React component we can not use the hook directly\n [AUTH_TYPES.LOGIN]: () => null,\n [AUTH_TYPES.OOPS]: Oops,\n [AUTH_TYPES.REGISTER]: Register,\n [AUTH_TYPES.REGISTER_ADMIN]: Register,\n [AUTH_TYPES.RESET_PASSWORD]: ResetPassword,\n [AUTH_TYPES.PROVIDERS]: () => null,\n} satisfies FormDictionary;\n"],"names":["AUTH_TYPES","LOGIN","REGISTER","REGISTER_ADMIN","FORGOT_PASSWORD","RESET_PASSWORD","FORGOT_PASSWORD_SUCCESS","OOPS","PROVIDERS","AUTH_TYPE_VALUES","Object","values","isAuthType","value","undefined","includes","FORMS","ForgotPassword","ForgotPasswordSuccess","Oops","Register","ResetPassword"],"mappings":";;;;;;MAQaA,UAAAA,GAAa;IACxBC,KAAAA,EAAO,OAAA;IACPC,QAAAA,EAAU,UAAA;IACVC,cAAAA,EAAgB,gBAAA;IAChBC,eAAAA,EAAiB,iBAAA;IACjBC,cAAAA,EAAgB,gBAAA;IAChBC,uBAAAA,EAAyB,yBAAA;IACzBC,IAAAA,EAAM,MAAA;IACNC,SAAAA,EAAW;AACb;AAIA,MAAMC,gBAAAA,GAAsCC,MAAAA,CAAOC,MAAM,CAACX,UAAAA,CAAAA;AAE1D;;IAGO,MAAMY,UAAAA,GAAa,CAACC,KAAAA,GACzBA,UAAUC,SAAAA,IAAaL,gBAAAA,CAAiBM,QAAQ,CAACF,KAAAA;MAItCG,KAAAA,GAAQ;IACnB,CAAChB,UAAAA,CAAWI,eAAe,GAAGa,cAAAA;IAC9B,CAACjB,UAAAA,CAAWM,uBAAuB,GAAGY,qBAAAA;;;AAGtC,IAAA,CAAClB,UAAAA,CAAWC,KAAK,GAAG,IAAM,IAAA;IAC1B,CAACD,UAAAA,CAAWO,IAAI,GAAGY,IAAAA;IACnB,CAACnB,UAAAA,CAAWE,QAAQ,GAAGkB,QAAAA;IACvB,CAACpB,UAAAA,CAAWG,cAAc,GAAGiB,QAAAA;IAC7B,CAACpB,UAAAA,CAAWK,cAAc,GAAGgB,aAAAA;AAC7B,IAAA,CAACrB,UAAAA,CAAWQ,SAAS,GAAG,IAAM;AAChC;;;;"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const DEFAULT_REDIRECT_TO = '/';
|
|
4
|
+
/**
|
|
5
|
+
* A redirect target is only safe if it resolves inside the admin app.
|
|
6
|
+
*
|
|
7
|
+
* Anything else must be rejected: react-router's browser history calls
|
|
8
|
+
* `window.location.assign(href)` when `pushState` throws, and `pushState` throws a
|
|
9
|
+
* `SecurityError` on a cross-origin href, so an off-app value becomes a real navigation
|
|
10
|
+
* away from the admin origin.
|
|
11
|
+
*
|
|
12
|
+
* Requiring a single leading `/` is what does the work. It rejects protocol-relative
|
|
13
|
+
* references (`//evil.com`), their backslash variant (`/\evil.com`, which browsers
|
|
14
|
+
* normalise to `//`), and every scheme-bearing value (`https://evil.com`,
|
|
15
|
+
* `javascript:alert(1)`), none of which start with a slash.
|
|
16
|
+
*/ const isSameAppPath = (value)=>value.startsWith('/') && !value.startsWith('//') && !value.startsWith('/\\');
|
|
17
|
+
/**
|
|
18
|
+
* Reads the `?redirectTo` the user was sent to the auth pages with, so that logging in from a
|
|
19
|
+
* deep link returns them to where they started instead of the home page.
|
|
20
|
+
*
|
|
21
|
+
* Falls back to the home page when the param is absent, empty, or not a same-app path.
|
|
22
|
+
*/ const getRedirectTo = (search)=>{
|
|
23
|
+
// `URLSearchParams` already percent-decodes the value. Decoding it a second time would
|
|
24
|
+
// corrupt targets holding encoded delimiters: `/foo?value=a%26b` would become
|
|
25
|
+
// `/foo?value=a&b`, splitting one query value into two.
|
|
26
|
+
const redirectTo = new URLSearchParams(search).get('redirectTo');
|
|
27
|
+
if (!redirectTo || !isSameAppPath(redirectTo)) {
|
|
28
|
+
return DEFAULT_REDIRECT_TO;
|
|
29
|
+
}
|
|
30
|
+
return redirectTo;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
exports.getRedirectTo = getRedirectTo;
|
|
34
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sources":["../../../../../../admin/src/pages/Auth/utils.ts"],"sourcesContent":["const DEFAULT_REDIRECT_TO = '/';\n\n/**\n * A redirect target is only safe if it resolves inside the admin app.\n *\n * Anything else must be rejected: react-router's browser history calls\n * `window.location.assign(href)` when `pushState` throws, and `pushState` throws a\n * `SecurityError` on a cross-origin href, so an off-app value becomes a real navigation\n * away from the admin origin.\n *\n * Requiring a single leading `/` is what does the work. It rejects protocol-relative\n * references (`//evil.com`), their backslash variant (`/\\evil.com`, which browsers\n * normalise to `//`), and every scheme-bearing value (`https://evil.com`,\n * `javascript:alert(1)`), none of which start with a slash.\n */\nconst isSameAppPath = (value: string): boolean =>\n value.startsWith('/') && !value.startsWith('//') && !value.startsWith('/\\\\');\n\n/**\n * Reads the `?redirectTo` the user was sent to the auth pages with, so that logging in from a\n * deep link returns them to where they started instead of the home page.\n *\n * Falls back to the home page when the param is absent, empty, or not a same-app path.\n */\nexport const getRedirectTo = (search: string): string => {\n // `URLSearchParams` already percent-decodes the value. Decoding it a second time would\n // corrupt targets holding encoded delimiters: `/foo?value=a%26b` would become\n // `/foo?value=a&b`, splitting one query value into two.\n const redirectTo = new URLSearchParams(search).get('redirectTo');\n\n if (!redirectTo || !isSameAppPath(redirectTo)) {\n return DEFAULT_REDIRECT_TO;\n }\n\n return redirectTo;\n};\n"],"names":["DEFAULT_REDIRECT_TO","isSameAppPath","value","startsWith","getRedirectTo","search","redirectTo","URLSearchParams","get"],"mappings":";;AAAA,MAAMA,mBAAAA,GAAsB,GAAA;AAE5B;;;;;;;;;;;;AAYC,IACD,MAAMC,aAAAA,GAAgB,CAACC,KAAAA,GACrBA,KAAAA,CAAMC,UAAU,CAAC,GAAA,CAAA,IAAQ,CAACD,KAAAA,CAAMC,UAAU,CAAC,IAAA,CAAA,IAAS,CAACD,KAAAA,CAAMC,UAAU,CAAC,KAAA,CAAA;AAExE;;;;;IAMO,MAAMC,aAAAA,GAAgB,CAACC,MAAAA,GAAAA;;;;AAI5B,IAAA,MAAMC,UAAAA,GAAa,IAAIC,eAAAA,CAAgBF,MAAAA,CAAAA,CAAQG,GAAG,CAAC,YAAA,CAAA;AAEnD,IAAA,IAAI,CAACF,UAAAA,IAAc,CAACL,aAAAA,CAAcK,UAAAA,CAAAA,EAAa;QAC7C,OAAON,mBAAAA;AACT,IAAA;IAEA,OAAOM,UAAAA;AACT;;;;"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const DEFAULT_REDIRECT_TO = '/';
|
|
2
|
+
/**
|
|
3
|
+
* A redirect target is only safe if it resolves inside the admin app.
|
|
4
|
+
*
|
|
5
|
+
* Anything else must be rejected: react-router's browser history calls
|
|
6
|
+
* `window.location.assign(href)` when `pushState` throws, and `pushState` throws a
|
|
7
|
+
* `SecurityError` on a cross-origin href, so an off-app value becomes a real navigation
|
|
8
|
+
* away from the admin origin.
|
|
9
|
+
*
|
|
10
|
+
* Requiring a single leading `/` is what does the work. It rejects protocol-relative
|
|
11
|
+
* references (`//evil.com`), their backslash variant (`/\evil.com`, which browsers
|
|
12
|
+
* normalise to `//`), and every scheme-bearing value (`https://evil.com`,
|
|
13
|
+
* `javascript:alert(1)`), none of which start with a slash.
|
|
14
|
+
*/ const isSameAppPath = (value)=>value.startsWith('/') && !value.startsWith('//') && !value.startsWith('/\\');
|
|
15
|
+
/**
|
|
16
|
+
* Reads the `?redirectTo` the user was sent to the auth pages with, so that logging in from a
|
|
17
|
+
* deep link returns them to where they started instead of the home page.
|
|
18
|
+
*
|
|
19
|
+
* Falls back to the home page when the param is absent, empty, or not a same-app path.
|
|
20
|
+
*/ const getRedirectTo = (search)=>{
|
|
21
|
+
// `URLSearchParams` already percent-decodes the value. Decoding it a second time would
|
|
22
|
+
// corrupt targets holding encoded delimiters: `/foo?value=a%26b` would become
|
|
23
|
+
// `/foo?value=a&b`, splitting one query value into two.
|
|
24
|
+
const redirectTo = new URLSearchParams(search).get('redirectTo');
|
|
25
|
+
if (!redirectTo || !isSameAppPath(redirectTo)) {
|
|
26
|
+
return DEFAULT_REDIRECT_TO;
|
|
27
|
+
}
|
|
28
|
+
return redirectTo;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export { getRedirectTo };
|
|
32
|
+
//# sourceMappingURL=utils.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.mjs","sources":["../../../../../../admin/src/pages/Auth/utils.ts"],"sourcesContent":["const DEFAULT_REDIRECT_TO = '/';\n\n/**\n * A redirect target is only safe if it resolves inside the admin app.\n *\n * Anything else must be rejected: react-router's browser history calls\n * `window.location.assign(href)` when `pushState` throws, and `pushState` throws a\n * `SecurityError` on a cross-origin href, so an off-app value becomes a real navigation\n * away from the admin origin.\n *\n * Requiring a single leading `/` is what does the work. It rejects protocol-relative\n * references (`//evil.com`), their backslash variant (`/\\evil.com`, which browsers\n * normalise to `//`), and every scheme-bearing value (`https://evil.com`,\n * `javascript:alert(1)`), none of which start with a slash.\n */\nconst isSameAppPath = (value: string): boolean =>\n value.startsWith('/') && !value.startsWith('//') && !value.startsWith('/\\\\');\n\n/**\n * Reads the `?redirectTo` the user was sent to the auth pages with, so that logging in from a\n * deep link returns them to where they started instead of the home page.\n *\n * Falls back to the home page when the param is absent, empty, or not a same-app path.\n */\nexport const getRedirectTo = (search: string): string => {\n // `URLSearchParams` already percent-decodes the value. Decoding it a second time would\n // corrupt targets holding encoded delimiters: `/foo?value=a%26b` would become\n // `/foo?value=a&b`, splitting one query value into two.\n const redirectTo = new URLSearchParams(search).get('redirectTo');\n\n if (!redirectTo || !isSameAppPath(redirectTo)) {\n return DEFAULT_REDIRECT_TO;\n }\n\n return redirectTo;\n};\n"],"names":["DEFAULT_REDIRECT_TO","isSameAppPath","value","startsWith","getRedirectTo","search","redirectTo","URLSearchParams","get"],"mappings":"AAAA,MAAMA,mBAAAA,GAAsB,GAAA;AAE5B;;;;;;;;;;;;AAYC,IACD,MAAMC,aAAAA,GAAgB,CAACC,KAAAA,GACrBA,KAAAA,CAAMC,UAAU,CAAC,GAAA,CAAA,IAAQ,CAACD,KAAAA,CAAMC,UAAU,CAAC,IAAA,CAAA,IAAS,CAACD,KAAAA,CAAMC,UAAU,CAAC,KAAA,CAAA;AAExE;;;;;IAMO,MAAMC,aAAAA,GAAgB,CAACC,MAAAA,GAAAA;;;;AAI5B,IAAA,MAAMC,UAAAA,GAAa,IAAIC,eAAAA,CAAgBF,MAAAA,CAAAA,CAAQG,GAAG,CAAC,YAAA,CAAA;AAEnD,IAAA,IAAI,CAACF,UAAAA,IAAc,CAACL,aAAAA,CAAcK,UAAAA,CAAAA,EAAa;QAC7C,OAAON,mBAAAA;AACT,IAAA;IAEA,OAAOM,UAAAA;AACT;;;;"}
|
|
@@ -174,7 +174,7 @@ var ptBR = {
|
|
|
174
174
|
"Settings.profile.form.section.experience.here": "aqui",
|
|
175
175
|
"Settings.profile.form.section.experience.interfaceLanguage": "Linguagem da interface",
|
|
176
176
|
"Settings.profile.form.section.experience.interfaceLanguage.hint": "Somente sua interface será exibida com a linguagem selecionada.",
|
|
177
|
-
"Settings.profile.form.section.experience.interfaceLanguageHelp": "
|
|
177
|
+
"Settings.profile.form.section.experience.interfaceLanguageHelp": "As alterações de preferência se aplicam apenas a você. Mais informações disponíveis {here}.",
|
|
178
178
|
"Settings.profile.form.section.experience.mode.label": "Modo de interface",
|
|
179
179
|
"Settings.profile.form.section.experience.mode.hint": "Selecione o modo de interface que você deseja usar",
|
|
180
180
|
"Settings.profile.form.section.experience.mode.option-label": "modo {name}",
|
|
@@ -170,7 +170,7 @@ var ptBR = {
|
|
|
170
170
|
"Settings.profile.form.section.experience.here": "aqui",
|
|
171
171
|
"Settings.profile.form.section.experience.interfaceLanguage": "Linguagem da interface",
|
|
172
172
|
"Settings.profile.form.section.experience.interfaceLanguage.hint": "Somente sua interface será exibida com a linguagem selecionada.",
|
|
173
|
-
"Settings.profile.form.section.experience.interfaceLanguageHelp": "
|
|
173
|
+
"Settings.profile.form.section.experience.interfaceLanguageHelp": "As alterações de preferência se aplicam apenas a você. Mais informações disponíveis {here}.",
|
|
174
174
|
"Settings.profile.form.section.experience.mode.label": "Modo de interface",
|
|
175
175
|
"Settings.profile.form.section.experience.mode.hint": "Selecione o modo de interface que você deseja usar",
|
|
176
176
|
"Settings.profile.form.section.experience.mode.option-label": "modo {name}",
|
|
@@ -321,6 +321,7 @@ const getToken = ()=>{
|
|
|
321
321
|
const response = await fetch(createRequestUrl(url), {
|
|
322
322
|
signal: options?.signal ?? defaultOptions.signal,
|
|
323
323
|
method: 'GET',
|
|
324
|
+
credentials: 'include',
|
|
324
325
|
headers
|
|
325
326
|
});
|
|
326
327
|
return responseInterceptor(response, options?.validateStatus, responseType);
|
|
@@ -343,6 +344,7 @@ const getToken = ()=>{
|
|
|
343
344
|
const response = await fetch(createRequestUrl(url), {
|
|
344
345
|
signal: options?.signal ?? defaultOptions.signal,
|
|
345
346
|
method: 'POST',
|
|
347
|
+
credentials: 'include',
|
|
346
348
|
headers,
|
|
347
349
|
body: isFormDataRequest(data) ? data : JSON.stringify(data)
|
|
348
350
|
});
|
|
@@ -366,6 +368,7 @@ const getToken = ()=>{
|
|
|
366
368
|
const response = await fetch(createRequestUrl(url), {
|
|
367
369
|
signal: options?.signal ?? defaultOptions.signal,
|
|
368
370
|
method: 'PUT',
|
|
371
|
+
credentials: 'include',
|
|
369
372
|
headers,
|
|
370
373
|
body: isFormDataRequest(data) ? data : JSON.stringify(data)
|
|
371
374
|
});
|
|
@@ -383,6 +386,7 @@ const getToken = ()=>{
|
|
|
383
386
|
const response = await fetch(createRequestUrl(url), {
|
|
384
387
|
signal: options?.signal ?? defaultOptions.signal,
|
|
385
388
|
method: 'DELETE',
|
|
389
|
+
credentials: 'include',
|
|
386
390
|
headers
|
|
387
391
|
});
|
|
388
392
|
return responseInterceptor(response, options?.validateStatus);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getFetchClient.js","sources":["../../../../../admin/src/utils/getFetchClient.ts"],"sourcesContent":["import pipe from 'lodash/fp/pipe';\n// eslint-disable-next-line import/default\nimport qs from 'qs';\n\nimport { AUTH_COOKIE_NAME, getCookieValue, setCookie } from './cookies';\n\nimport type { errors } from '@strapi/utils';\n\nexport type ApiError =\n | errors.ApplicationError\n | errors.ForbiddenError\n | errors.NotFoundError\n | errors.NotImplementedError\n | errors.PaginationError\n | errors.PayloadTooLargeError\n | errors.PolicyError\n | errors.RateLimitError\n | errors.UnauthorizedError\n | errors.ValidationError\n | errors.YupValidationError;\n\nconst STORAGE_KEYS = {\n TOKEN: 'jwtToken',\n USER: 'userInfo',\n};\n\n/**\n * Module-level promise to ensure only one token refresh happens at a time\n */\nlet refreshPromise: Promise<string | null> | null = null;\n\n/**\n * Callback to notify the app when the token is updated (e.g., to update Redux state)\n */\nlet onTokenUpdate: ((token: string) => void) | null = null;\n\n/**\n * Callback to notify the app when the session has been terminated and the user\n * should be redirected to the login page (e.g., refresh token rejected, idle\n * session expired). The React layer registers a handler that clears local\n * auth state and navigates to /auth/login.\n */\nlet onSessionExpired: (() => void) | null = null;\n\n/**\n * Set the callback that will be called when the token is refreshed.\n * This allows the React layer to update Redux state when a token refresh occurs.\n *\n * @param callback - Function to call with the new token, or null to clear\n * @example\n * // In a React component\n * useEffect(() => {\n * setOnTokenUpdate((token) => dispatch(setToken(token)));\n * return () => setOnTokenUpdate(null);\n * }, [dispatch]);\n */\nconst setOnTokenUpdate = (callback: ((token: string) => void) | null): void => {\n onTokenUpdate = callback;\n};\n\n/**\n * Set the callback that will be called when the active session is no longer\n * valid (refresh token rejected by the server, or detected idle on the\n * client). This lets the active tab redirect to /auth/login without waiting\n * for the next user-initiated request to fail.\n *\n * @param callback - Function to call when the session ends, or null to clear\n */\nconst setOnSessionExpired = (callback: (() => void) | null): void => {\n onSessionExpired = callback;\n};\n\n/**\n * Trigger the registered session-expired callback, if any. Safe to call from\n * non-React code (e.g., the RTK Query baseQuery 401 handler).\n */\nconst triggerSessionExpired = (): void => {\n onSessionExpired?.();\n};\n\n/**\n * Check if the URL is an auth path that should not trigger token refresh.\n * Note: No ^ anchor since the URL may include the baseURL prefix (e.g., \"http://localhost:1337/admin/login\").\n * This differs from baseQuery.ts which uses ^/admin since it receives normalized paths.\n */\nconst isAuthPath = (url: string) => /\\/admin\\/(login|logout|access-token)\\b/.test(url);\n\n/**\n * Store the new token in the appropriate storage (localStorage or cookie)\n * and notify the app to update its state.\n *\n * Uses localStorage if the user selected \"remember me\" during login,\n * otherwise uses cookies for session-based storage.\n *\n * @param token - The JWT token to store\n * @internal Exported for testing purposes\n */\nconst storeToken = (token: string): void => {\n // Check if the original token was stored in localStorage (persist mode)\n const wasPersistedToLocalStorage = Boolean(localStorage.getItem(STORAGE_KEYS.TOKEN));\n\n if (wasPersistedToLocalStorage) {\n localStorage.setItem(STORAGE_KEYS.TOKEN, JSON.stringify(token));\n } else {\n setCookie(AUTH_COOKIE_NAME, token);\n }\n\n // Notify the app to update its state (e.g., Redux)\n if (onTokenUpdate) {\n onTokenUpdate(token);\n }\n};\n\n/**\n * Refresh the access token by calling the /admin/access-token endpoint.\n * This uses a low-level fetch to avoid recursion through the interceptor.\n * Returns the new token on success, or null on failure.\n */\nconst refreshAccessToken = async (): Promise<string | null> => {\n const backendURL = window.strapi.backendURL;\n\n try {\n const response = await fetch(`${backendURL}/admin/access-token`, {\n method: 'POST',\n credentials: 'include', // Include cookies for the refresh token\n headers: {\n Accept: 'application/json',\n 'Content-Type': 'application/json',\n },\n });\n\n if (!response.ok) {\n console.warn('[Auth] Token refresh failed with status:', response.status);\n return null;\n }\n\n const result = await response.json();\n const token = result?.data?.token as string | undefined;\n\n if (!token) {\n console.warn('[Auth] Token refresh response missing token');\n return null;\n }\n\n storeToken(token);\n return token;\n } catch (error) {\n console.error('[Auth] Token refresh error:', error);\n return null;\n }\n};\n\n/**\n * Attempt to refresh the token if not already refreshing.\n * Uses a module-level promise to prevent concurrent refresh requests.\n *\n * @returns The new authentication token\n * @throws {Error} If the token refresh fails (e.g., refresh token expired)\n * @internal Exported for testing purposes\n */\nconst attemptTokenRefresh = async (): Promise<string> => {\n if (!refreshPromise) {\n refreshPromise = refreshAccessToken().finally(() => {\n refreshPromise = null;\n });\n }\n\n const newToken = await refreshPromise;\n if (!newToken) {\n const error = new Error('Session expired. Please log in again.');\n error.name = 'TokenRefreshError';\n throw error;\n }\n\n return newToken;\n};\n\ntype FetchResponse<TData = unknown> = {\n data: TData;\n status?: number;\n headers?: Headers;\n};\n\ntype FetchOptions = {\n params?: unknown;\n signal?: AbortSignal;\n headers?: Record<string, string>;\n validateStatus?: ((status: number) => boolean) | null;\n responseType?: 'json' | 'blob' | 'text' | 'arrayBuffer';\n};\n\ntype FetchConfig = {\n signal?: AbortSignal;\n};\n\ninterface ErrorResponse {\n data: {\n data?: unknown;\n error: ApiError & { status?: number };\n };\n}\n\nclass FetchError extends Error {\n public name: string;\n public message: string;\n public response?: ErrorResponse;\n public code?: number;\n public status?: number;\n\n constructor(message: string, response?: ErrorResponse) {\n super(message);\n this.name = 'FetchError';\n this.message = message;\n this.response = response;\n this.code = response?.data?.error?.status;\n this.status = response?.data?.error?.status;\n\n // Ensure correct stack trace in error object\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, FetchError);\n }\n }\n}\n\nconst isFetchError = (error: unknown): error is FetchError => {\n return error instanceof FetchError;\n};\n\nconst getToken = (): string | null => {\n const fromLocalStorage = localStorage.getItem(STORAGE_KEYS.TOKEN);\n if (fromLocalStorage) {\n return JSON.parse(fromLocalStorage);\n }\n\n const fromCookie = getCookieValue(AUTH_COOKIE_NAME);\n return fromCookie ?? null;\n};\n\ntype FetchClient = {\n get: {\n (url: string, config: FetchOptions & { responseType: 'blob' }): Promise<FetchResponse<Blob>>;\n (url: string, config: FetchOptions & { responseType: 'text' }): Promise<FetchResponse<string>>;\n (\n url: string,\n config: FetchOptions & { responseType: 'arrayBuffer' }\n ): Promise<FetchResponse<ArrayBuffer>>;\n <TData = unknown>(url: string, config?: FetchOptions): Promise<FetchResponse<TData>>;\n };\n put: <TData = unknown, TSend = unknown>(\n url: string,\n data?: TSend,\n config?: FetchOptions\n ) => Promise<FetchResponse<TData>>;\n post: <TData = unknown, TSend = unknown>(\n url: string,\n data?: TSend,\n config?: FetchOptions\n ) => Promise<FetchResponse<TData>>;\n del: <TData = unknown>(url: string, config?: FetchOptions) => Promise<FetchResponse<TData>>;\n};\n\n/**\n * @public\n * @param {FetchConfig} [defaultOptions={}] - Fetch Configs.\n * @returns {FetchClient} A fetch client object with methods for making HTTP requests.\n * @description This is an abstraction around the native fetch exposed by a function. It provides a simple interface to handle API calls\n * to the Strapi backend.\n * @example\n * ```tsx\n * import { getFetchClient } from '@strapi/admin/admin';\n *\n * const myFunct = () => {\n * const { get } = getFetchClient();\n * const requestURL = \"/some-endpoint\";\n *\n * const { data } = await get(requestURL);\n *\n * return data;\n * };\n * ```\n */\nconst getFetchClient = (defaultOptions: FetchConfig = {}): FetchClient => {\n const backendURL = window.strapi.backendURL;\n\n /**\n * Create default headers with the current token.\n * This is a function so we can get a fresh token after refresh.\n */\n const getDefaultHeaders = () => ({\n Accept: 'application/json',\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${getToken()}`,\n });\n\n const isFormDataRequest = (body: unknown) => body instanceof FormData;\n const addPrependingSlash = (url: string) => (url.charAt(0) !== '/' ? `/${url}` : url);\n\n // This regular expression matches a string that starts with either \"http://\" or \"https://\" or any other protocol name in lower case letters, followed by \"://\" and ends with anything else\n const hasProtocol = (url: string) => new RegExp('^(?:[a-z+]+:)?//', 'i').test(url);\n\n // Check if the url has a prepending slash, if not add a slash\n const normalizeUrl = (url: string) => (hasProtocol(url) ? url : addPrependingSlash(url));\n\n // Add a response interceptor to return the response\n const responseInterceptor = async <TData = unknown>(\n response: Response,\n validateStatus?: FetchOptions['validateStatus'],\n responseType: NonNullable<FetchOptions['responseType']> = 'json'\n ): Promise<FetchResponse<TData>> => {\n if (responseType !== 'json') {\n if (!response.ok && !validateStatus?.(response.status)) {\n const fetchError = new FetchError('Server Error');\n fetchError.status = response.status;\n throw fetchError;\n }\n\n let result: Blob | string | ArrayBuffer;\n if (responseType === 'blob') {\n result = await response.blob();\n } else if (responseType === 'text') {\n result = await response.text();\n } else {\n result = await response.arrayBuffer();\n }\n\n return { data: result as TData, status: response.status, headers: response.headers };\n }\n\n if (response.status === 204) {\n return { data: {} as TData, status: response.status };\n }\n\n try {\n const result = await response.json();\n\n if (!response.ok && result.error && !validateStatus?.(response.status)) {\n const fetchError = new FetchError(result.error.message, { data: result });\n fetchError.status = response.status;\n throw fetchError;\n }\n\n if (!response.ok && !validateStatus?.(response.status)) {\n const fetchError = new FetchError('Unknown Server Error');\n fetchError.status = response.status;\n throw fetchError;\n }\n\n return { data: result };\n } catch (error) {\n // An empty 200 body causes `response.json()` to throw a `SyntaxError`. We treat\n // it as success and return an empty payload. We match on `error.name` rather\n // than `instanceof SyntaxError` because constructor identity differs across JS\n // realms — a Response from a different realm (e.g. undici under jsdom in tests,\n // a service worker or iframe in browsers) throws a `SyntaxError` whose\n // constructor is not the same identity as the one this module closes over. Name\n // comparison is realm-agnostic.\n if ((error as Error | null)?.name === 'SyntaxError' && response.ok) {\n return { data: {}, status: response.status } as FetchResponse<TData>;\n } else {\n throw error;\n }\n }\n };\n\n /**\n * Execute a fetch request with automatic token refresh on 401 errors.\n * @param url - The request URL (used to check if it's an auth path)\n * @param executeRequest - Function that performs the fetch (called again on retry with fresh headers)\n */\n const withTokenRefresh = async <TData>(\n url: string,\n executeRequest: () => Promise<FetchResponse<TData>>\n ): Promise<FetchResponse<TData>> => {\n try {\n return await executeRequest();\n } catch (error) {\n // Only attempt refresh for 401 errors on non-auth paths\n if (isFetchError(error) && error.status === 401 && !isAuthPath(url)) {\n try {\n await attemptTokenRefresh();\n // Retry - executeRequest will call getDefaultHeaders() again, picking up the new token\n return await executeRequest();\n } catch {\n // If refresh fails, throw the original error\n throw error;\n }\n }\n throw error;\n }\n };\n\n const paramsSerializer =\n <Param = unknown>(params?: Param) =>\n (url: string) => {\n if (params) {\n if (typeof params === 'string') {\n return `${url}?${params}`;\n }\n\n /**\n * TODO V6: Encoding should be enabled in this step\n * So the rest of the app doesn't have to worry about it,\n * It's considered a breaking change because it impacts any API request, including the user's custom code\n */\n const serializedParams = qs.stringify(params, { encode: false });\n if (serializedParams) {\n return `${url}?${serializedParams}`;\n }\n return url;\n }\n return url;\n };\n\n const addBaseUrl = (url: Parameters<typeof fetch>[0]) => {\n return `${backendURL}${url}`;\n };\n\n /**\n * We use the factory method because the options\n * are unique to the individual request\n */\n const makeCreateRequestUrl = (options?: FetchOptions) =>\n pipe(normalizeUrl, addBaseUrl, paramsSerializer(options?.params));\n\n const fetchClient: FetchClient = {\n get: async <TData>(url: string, options?: FetchOptions): Promise<FetchResponse<TData>> => {\n const createRequestUrl = makeCreateRequestUrl(options);\n const responseType = options?.responseType ?? 'json';\n\n const executeRequest = async () => {\n const { Authorization } = getDefaultHeaders();\n\n // For non-JSON response types, omit content negotiation headers that imply JSON\n const defaultHeaders = responseType === 'json' ? getDefaultHeaders() : { Authorization };\n\n const headers = new Headers({\n ...defaultHeaders,\n ...options?.headers,\n });\n\n const response = await fetch(createRequestUrl(url), {\n signal: options?.signal ?? defaultOptions.signal,\n method: 'GET',\n headers,\n });\n\n return responseInterceptor<TData>(response, options?.validateStatus, responseType);\n };\n\n return withTokenRefresh(url, executeRequest);\n },\n post: async <TData, TSend = unknown>(\n url: string,\n data?: TSend,\n options?: FetchOptions\n ): Promise<FetchResponse<TData>> => {\n const createRequestUrl = makeCreateRequestUrl(options);\n\n const executeRequest = async () => {\n const headers = new Headers({\n ...getDefaultHeaders(),\n ...options?.headers,\n });\n\n /**\n * we have to remove the Content-Type value if it was a formData request\n * the browser will automatically set the header value\n */\n if (isFormDataRequest(data)) {\n headers.delete('Content-Type');\n }\n\n const response = await fetch(createRequestUrl(url), {\n signal: options?.signal ?? defaultOptions.signal,\n method: 'POST',\n headers,\n body: isFormDataRequest(data) ? (data as FormData) : JSON.stringify(data),\n });\n return responseInterceptor<TData>(response, options?.validateStatus);\n };\n\n return withTokenRefresh(url, executeRequest);\n },\n put: async <TData, TSend = unknown>(\n url: string,\n data?: TSend,\n options?: FetchOptions\n ): Promise<FetchResponse<TData>> => {\n const createRequestUrl = makeCreateRequestUrl(options);\n\n const executeRequest = async () => {\n const headers = new Headers({\n ...getDefaultHeaders(),\n ...options?.headers,\n });\n\n /**\n * we have to remove the Content-Type value if it was a formData request\n * the browser will automatically set the header value\n */\n if (isFormDataRequest(data)) {\n headers.delete('Content-Type');\n }\n\n const response = await fetch(createRequestUrl(url), {\n signal: options?.signal ?? defaultOptions.signal,\n method: 'PUT',\n headers,\n body: isFormDataRequest(data) ? (data as FormData) : JSON.stringify(data),\n });\n\n return responseInterceptor<TData>(response, options?.validateStatus);\n };\n\n return withTokenRefresh(url, executeRequest);\n },\n del: async <TData>(url: string, options?: FetchOptions): Promise<FetchResponse<TData>> => {\n const createRequestUrl = makeCreateRequestUrl(options);\n\n const executeRequest = async () => {\n const headers = new Headers({\n ...getDefaultHeaders(),\n ...options?.headers,\n });\n\n const response = await fetch(createRequestUrl(url), {\n signal: options?.signal ?? defaultOptions.signal,\n method: 'DELETE',\n headers,\n });\n return responseInterceptor<TData>(response, options?.validateStatus);\n };\n\n return withTokenRefresh(url, executeRequest);\n },\n };\n\n return fetchClient;\n};\n\nexport {\n getFetchClient,\n isFetchError,\n FetchError,\n attemptTokenRefresh,\n storeToken,\n setOnTokenUpdate,\n setOnSessionExpired,\n triggerSessionExpired,\n};\nexport type { FetchOptions, FetchResponse, FetchConfig, FetchClient, ErrorResponse };\n"],"names":["STORAGE_KEYS","TOKEN","refreshPromise","onTokenUpdate","onSessionExpired","setOnTokenUpdate","callback","setOnSessionExpired","triggerSessionExpired","isAuthPath","url","test","storeToken","token","wasPersistedToLocalStorage","Boolean","localStorage","getItem","setItem","JSON","stringify","setCookie","AUTH_COOKIE_NAME","refreshAccessToken","backendURL","window","strapi","response","fetch","method","credentials","headers","Accept","ok","console","warn","status","result","json","data","error","attemptTokenRefresh","finally","newToken","Error","name","FetchError","message","code","captureStackTrace","isFetchError","getToken","fromLocalStorage","parse","fromCookie","getCookieValue","getFetchClient","defaultOptions","getDefaultHeaders","Authorization","isFormDataRequest","body","FormData","addPrependingSlash","charAt","hasProtocol","RegExp","normalizeUrl","responseInterceptor","validateStatus","responseType","fetchError","blob","text","arrayBuffer","withTokenRefresh","executeRequest","paramsSerializer","params","serializedParams","qs","encode","addBaseUrl","makeCreateRequestUrl","options","pipe","fetchClient","get","createRequestUrl","defaultHeaders","Headers","signal","post","delete","put","del"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"getFetchClient.js","sources":["../../../../../admin/src/utils/getFetchClient.ts"],"sourcesContent":["import pipe from 'lodash/fp/pipe';\n// eslint-disable-next-line import/default\nimport qs from 'qs';\n\nimport { AUTH_COOKIE_NAME, getCookieValue, setCookie } from './cookies';\n\nimport type { errors } from '@strapi/utils';\n\nexport type ApiError =\n | errors.ApplicationError\n | errors.ForbiddenError\n | errors.NotFoundError\n | errors.NotImplementedError\n | errors.PaginationError\n | errors.PayloadTooLargeError\n | errors.PolicyError\n | errors.RateLimitError\n | errors.UnauthorizedError\n | errors.ValidationError\n | errors.YupValidationError;\n\nconst STORAGE_KEYS = {\n TOKEN: 'jwtToken',\n USER: 'userInfo',\n};\n\n/**\n * Module-level promise to ensure only one token refresh happens at a time\n */\nlet refreshPromise: Promise<string | null> | null = null;\n\n/**\n * Callback to notify the app when the token is updated (e.g., to update Redux state)\n */\nlet onTokenUpdate: ((token: string) => void) | null = null;\n\n/**\n * Callback to notify the app when the session has been terminated and the user\n * should be redirected to the login page (e.g., refresh token rejected, idle\n * session expired). The React layer registers a handler that clears local\n * auth state and navigates to /auth/login.\n */\nlet onSessionExpired: (() => void) | null = null;\n\n/**\n * Set the callback that will be called when the token is refreshed.\n * This allows the React layer to update Redux state when a token refresh occurs.\n *\n * @param callback - Function to call with the new token, or null to clear\n * @example\n * // In a React component\n * useEffect(() => {\n * setOnTokenUpdate((token) => dispatch(setToken(token)));\n * return () => setOnTokenUpdate(null);\n * }, [dispatch]);\n */\nconst setOnTokenUpdate = (callback: ((token: string) => void) | null): void => {\n onTokenUpdate = callback;\n};\n\n/**\n * Set the callback that will be called when the active session is no longer\n * valid (refresh token rejected by the server, or detected idle on the\n * client). This lets the active tab redirect to /auth/login without waiting\n * for the next user-initiated request to fail.\n *\n * @param callback - Function to call when the session ends, or null to clear\n */\nconst setOnSessionExpired = (callback: (() => void) | null): void => {\n onSessionExpired = callback;\n};\n\n/**\n * Trigger the registered session-expired callback, if any. Safe to call from\n * non-React code (e.g., the RTK Query baseQuery 401 handler).\n */\nconst triggerSessionExpired = (): void => {\n onSessionExpired?.();\n};\n\n/**\n * Check if the URL is an auth path that should not trigger token refresh.\n * Note: No ^ anchor since the URL may include the baseURL prefix (e.g., \"http://localhost:1337/admin/login\").\n * This differs from baseQuery.ts which uses ^/admin since it receives normalized paths.\n */\nconst isAuthPath = (url: string) => /\\/admin\\/(login|logout|access-token)\\b/.test(url);\n\n/**\n * Store the new token in the appropriate storage (localStorage or cookie)\n * and notify the app to update its state.\n *\n * Uses localStorage if the user selected \"remember me\" during login,\n * otherwise uses cookies for session-based storage.\n *\n * @param token - The JWT token to store\n * @internal Exported for testing purposes\n */\nconst storeToken = (token: string): void => {\n // Check if the original token was stored in localStorage (persist mode)\n const wasPersistedToLocalStorage = Boolean(localStorage.getItem(STORAGE_KEYS.TOKEN));\n\n if (wasPersistedToLocalStorage) {\n localStorage.setItem(STORAGE_KEYS.TOKEN, JSON.stringify(token));\n } else {\n setCookie(AUTH_COOKIE_NAME, token);\n }\n\n // Notify the app to update its state (e.g., Redux)\n if (onTokenUpdate) {\n onTokenUpdate(token);\n }\n};\n\n/**\n * Refresh the access token by calling the /admin/access-token endpoint.\n * This uses a low-level fetch to avoid recursion through the interceptor.\n * Returns the new token on success, or null on failure.\n */\nconst refreshAccessToken = async (): Promise<string | null> => {\n const backendURL = window.strapi.backendURL;\n\n try {\n const response = await fetch(`${backendURL}/admin/access-token`, {\n method: 'POST',\n credentials: 'include', // Include cookies for the refresh token\n headers: {\n Accept: 'application/json',\n 'Content-Type': 'application/json',\n },\n });\n\n if (!response.ok) {\n console.warn('[Auth] Token refresh failed with status:', response.status);\n return null;\n }\n\n const result = await response.json();\n const token = result?.data?.token as string | undefined;\n\n if (!token) {\n console.warn('[Auth] Token refresh response missing token');\n return null;\n }\n\n storeToken(token);\n return token;\n } catch (error) {\n console.error('[Auth] Token refresh error:', error);\n return null;\n }\n};\n\n/**\n * Attempt to refresh the token if not already refreshing.\n * Uses a module-level promise to prevent concurrent refresh requests.\n *\n * @returns The new authentication token\n * @throws {Error} If the token refresh fails (e.g., refresh token expired)\n * @internal Exported for testing purposes\n */\nconst attemptTokenRefresh = async (): Promise<string> => {\n if (!refreshPromise) {\n refreshPromise = refreshAccessToken().finally(() => {\n refreshPromise = null;\n });\n }\n\n const newToken = await refreshPromise;\n if (!newToken) {\n const error = new Error('Session expired. Please log in again.');\n error.name = 'TokenRefreshError';\n throw error;\n }\n\n return newToken;\n};\n\ntype FetchResponse<TData = unknown> = {\n data: TData;\n status?: number;\n headers?: Headers;\n};\n\ntype FetchOptions = {\n params?: unknown;\n signal?: AbortSignal;\n headers?: Record<string, string>;\n validateStatus?: ((status: number) => boolean) | null;\n responseType?: 'json' | 'blob' | 'text' | 'arrayBuffer';\n};\n\ntype FetchConfig = {\n signal?: AbortSignal;\n};\n\ninterface ErrorResponse {\n data: {\n data?: unknown;\n error: ApiError & { status?: number };\n };\n}\n\nclass FetchError extends Error {\n public name: string;\n public message: string;\n public response?: ErrorResponse;\n public code?: number;\n public status?: number;\n\n constructor(message: string, response?: ErrorResponse) {\n super(message);\n this.name = 'FetchError';\n this.message = message;\n this.response = response;\n this.code = response?.data?.error?.status;\n this.status = response?.data?.error?.status;\n\n // Ensure correct stack trace in error object\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, FetchError);\n }\n }\n}\n\nconst isFetchError = (error: unknown): error is FetchError => {\n return error instanceof FetchError;\n};\n\nconst getToken = (): string | null => {\n const fromLocalStorage = localStorage.getItem(STORAGE_KEYS.TOKEN);\n if (fromLocalStorage) {\n return JSON.parse(fromLocalStorage);\n }\n\n const fromCookie = getCookieValue(AUTH_COOKIE_NAME);\n return fromCookie ?? null;\n};\n\ntype FetchClient = {\n get: {\n (url: string, config: FetchOptions & { responseType: 'blob' }): Promise<FetchResponse<Blob>>;\n (url: string, config: FetchOptions & { responseType: 'text' }): Promise<FetchResponse<string>>;\n (\n url: string,\n config: FetchOptions & { responseType: 'arrayBuffer' }\n ): Promise<FetchResponse<ArrayBuffer>>;\n <TData = unknown>(url: string, config?: FetchOptions): Promise<FetchResponse<TData>>;\n };\n put: <TData = unknown, TSend = unknown>(\n url: string,\n data?: TSend,\n config?: FetchOptions\n ) => Promise<FetchResponse<TData>>;\n post: <TData = unknown, TSend = unknown>(\n url: string,\n data?: TSend,\n config?: FetchOptions\n ) => Promise<FetchResponse<TData>>;\n del: <TData = unknown>(url: string, config?: FetchOptions) => Promise<FetchResponse<TData>>;\n};\n\n/**\n * @public\n * @param {FetchConfig} [defaultOptions={}] - Fetch Configs.\n * @returns {FetchClient} A fetch client object with methods for making HTTP requests.\n * @description This is an abstraction around the native fetch exposed by a function. It provides a simple interface to handle API calls\n * to the Strapi backend.\n * @example\n * ```tsx\n * import { getFetchClient } from '@strapi/admin/admin';\n *\n * const myFunct = () => {\n * const { get } = getFetchClient();\n * const requestURL = \"/some-endpoint\";\n *\n * const { data } = await get(requestURL);\n *\n * return data;\n * };\n * ```\n */\nconst getFetchClient = (defaultOptions: FetchConfig = {}): FetchClient => {\n const backendURL = window.strapi.backendURL;\n\n /**\n * Create default headers with the current token.\n * This is a function so we can get a fresh token after refresh.\n */\n const getDefaultHeaders = () => ({\n Accept: 'application/json',\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${getToken()}`,\n });\n\n const isFormDataRequest = (body: unknown) => body instanceof FormData;\n const addPrependingSlash = (url: string) => (url.charAt(0) !== '/' ? `/${url}` : url);\n\n // This regular expression matches a string that starts with either \"http://\" or \"https://\" or any other protocol name in lower case letters, followed by \"://\" and ends with anything else\n const hasProtocol = (url: string) => new RegExp('^(?:[a-z+]+:)?//', 'i').test(url);\n\n // Check if the url has a prepending slash, if not add a slash\n const normalizeUrl = (url: string) => (hasProtocol(url) ? url : addPrependingSlash(url));\n\n // Add a response interceptor to return the response\n const responseInterceptor = async <TData = unknown>(\n response: Response,\n validateStatus?: FetchOptions['validateStatus'],\n responseType: NonNullable<FetchOptions['responseType']> = 'json'\n ): Promise<FetchResponse<TData>> => {\n if (responseType !== 'json') {\n if (!response.ok && !validateStatus?.(response.status)) {\n const fetchError = new FetchError('Server Error');\n fetchError.status = response.status;\n throw fetchError;\n }\n\n let result: Blob | string | ArrayBuffer;\n if (responseType === 'blob') {\n result = await response.blob();\n } else if (responseType === 'text') {\n result = await response.text();\n } else {\n result = await response.arrayBuffer();\n }\n\n return { data: result as TData, status: response.status, headers: response.headers };\n }\n\n if (response.status === 204) {\n return { data: {} as TData, status: response.status };\n }\n\n try {\n const result = await response.json();\n\n if (!response.ok && result.error && !validateStatus?.(response.status)) {\n const fetchError = new FetchError(result.error.message, { data: result });\n fetchError.status = response.status;\n throw fetchError;\n }\n\n if (!response.ok && !validateStatus?.(response.status)) {\n const fetchError = new FetchError('Unknown Server Error');\n fetchError.status = response.status;\n throw fetchError;\n }\n\n return { data: result };\n } catch (error) {\n // An empty 200 body causes `response.json()` to throw a `SyntaxError`. We treat\n // it as success and return an empty payload. We match on `error.name` rather\n // than `instanceof SyntaxError` because constructor identity differs across JS\n // realms — a Response from a different realm (e.g. undici under jsdom in tests,\n // a service worker or iframe in browsers) throws a `SyntaxError` whose\n // constructor is not the same identity as the one this module closes over. Name\n // comparison is realm-agnostic.\n if ((error as Error | null)?.name === 'SyntaxError' && response.ok) {\n return { data: {}, status: response.status } as FetchResponse<TData>;\n } else {\n throw error;\n }\n }\n };\n\n /**\n * Execute a fetch request with automatic token refresh on 401 errors.\n * @param url - The request URL (used to check if it's an auth path)\n * @param executeRequest - Function that performs the fetch (called again on retry with fresh headers)\n */\n const withTokenRefresh = async <TData>(\n url: string,\n executeRequest: () => Promise<FetchResponse<TData>>\n ): Promise<FetchResponse<TData>> => {\n try {\n return await executeRequest();\n } catch (error) {\n // Only attempt refresh for 401 errors on non-auth paths\n if (isFetchError(error) && error.status === 401 && !isAuthPath(url)) {\n try {\n await attemptTokenRefresh();\n // Retry - executeRequest will call getDefaultHeaders() again, picking up the new token\n return await executeRequest();\n } catch {\n // If refresh fails, throw the original error\n throw error;\n }\n }\n throw error;\n }\n };\n\n const paramsSerializer =\n <Param = unknown>(params?: Param) =>\n (url: string) => {\n if (params) {\n if (typeof params === 'string') {\n return `${url}?${params}`;\n }\n\n /**\n * TODO V6: Encoding should be enabled in this step\n * So the rest of the app doesn't have to worry about it,\n * It's considered a breaking change because it impacts any API request, including the user's custom code\n */\n const serializedParams = qs.stringify(params, { encode: false });\n if (serializedParams) {\n return `${url}?${serializedParams}`;\n }\n return url;\n }\n return url;\n };\n\n const addBaseUrl = (url: Parameters<typeof fetch>[0]) => {\n return `${backendURL}${url}`;\n };\n\n /**\n * We use the factory method because the options\n * are unique to the individual request\n */\n const makeCreateRequestUrl = (options?: FetchOptions) =>\n pipe(normalizeUrl, addBaseUrl, paramsSerializer(options?.params));\n\n const fetchClient: FetchClient = {\n get: async <TData>(url: string, options?: FetchOptions): Promise<FetchResponse<TData>> => {\n const createRequestUrl = makeCreateRequestUrl(options);\n const responseType = options?.responseType ?? 'json';\n\n const executeRequest = async () => {\n const { Authorization } = getDefaultHeaders();\n\n // For non-JSON response types, omit content negotiation headers that imply JSON\n const defaultHeaders = responseType === 'json' ? getDefaultHeaders() : { Authorization };\n\n const headers = new Headers({\n ...defaultHeaders,\n ...options?.headers,\n });\n\n const response = await fetch(createRequestUrl(url), {\n signal: options?.signal ?? defaultOptions.signal,\n method: 'GET',\n credentials: 'include',\n headers,\n });\n\n return responseInterceptor<TData>(response, options?.validateStatus, responseType);\n };\n\n return withTokenRefresh(url, executeRequest);\n },\n post: async <TData, TSend = unknown>(\n url: string,\n data?: TSend,\n options?: FetchOptions\n ): Promise<FetchResponse<TData>> => {\n const createRequestUrl = makeCreateRequestUrl(options);\n\n const executeRequest = async () => {\n const headers = new Headers({\n ...getDefaultHeaders(),\n ...options?.headers,\n });\n\n /**\n * we have to remove the Content-Type value if it was a formData request\n * the browser will automatically set the header value\n */\n if (isFormDataRequest(data)) {\n headers.delete('Content-Type');\n }\n\n const response = await fetch(createRequestUrl(url), {\n signal: options?.signal ?? defaultOptions.signal,\n method: 'POST',\n credentials: 'include',\n headers,\n body: isFormDataRequest(data) ? (data as FormData) : JSON.stringify(data),\n });\n return responseInterceptor<TData>(response, options?.validateStatus);\n };\n\n return withTokenRefresh(url, executeRequest);\n },\n put: async <TData, TSend = unknown>(\n url: string,\n data?: TSend,\n options?: FetchOptions\n ): Promise<FetchResponse<TData>> => {\n const createRequestUrl = makeCreateRequestUrl(options);\n\n const executeRequest = async () => {\n const headers = new Headers({\n ...getDefaultHeaders(),\n ...options?.headers,\n });\n\n /**\n * we have to remove the Content-Type value if it was a formData request\n * the browser will automatically set the header value\n */\n if (isFormDataRequest(data)) {\n headers.delete('Content-Type');\n }\n\n const response = await fetch(createRequestUrl(url), {\n signal: options?.signal ?? defaultOptions.signal,\n method: 'PUT',\n credentials: 'include',\n headers,\n body: isFormDataRequest(data) ? (data as FormData) : JSON.stringify(data),\n });\n\n return responseInterceptor<TData>(response, options?.validateStatus);\n };\n\n return withTokenRefresh(url, executeRequest);\n },\n del: async <TData>(url: string, options?: FetchOptions): Promise<FetchResponse<TData>> => {\n const createRequestUrl = makeCreateRequestUrl(options);\n\n const executeRequest = async () => {\n const headers = new Headers({\n ...getDefaultHeaders(),\n ...options?.headers,\n });\n\n const response = await fetch(createRequestUrl(url), {\n signal: options?.signal ?? defaultOptions.signal,\n method: 'DELETE',\n credentials: 'include',\n headers,\n });\n return responseInterceptor<TData>(response, options?.validateStatus);\n };\n\n return withTokenRefresh(url, executeRequest);\n },\n };\n\n return fetchClient;\n};\n\nexport {\n getFetchClient,\n isFetchError,\n FetchError,\n attemptTokenRefresh,\n storeToken,\n setOnTokenUpdate,\n setOnSessionExpired,\n triggerSessionExpired,\n};\nexport type { FetchOptions, FetchResponse, FetchConfig, FetchClient, ErrorResponse };\n"],"names":["STORAGE_KEYS","TOKEN","refreshPromise","onTokenUpdate","onSessionExpired","setOnTokenUpdate","callback","setOnSessionExpired","triggerSessionExpired","isAuthPath","url","test","storeToken","token","wasPersistedToLocalStorage","Boolean","localStorage","getItem","setItem","JSON","stringify","setCookie","AUTH_COOKIE_NAME","refreshAccessToken","backendURL","window","strapi","response","fetch","method","credentials","headers","Accept","ok","console","warn","status","result","json","data","error","attemptTokenRefresh","finally","newToken","Error","name","FetchError","message","code","captureStackTrace","isFetchError","getToken","fromLocalStorage","parse","fromCookie","getCookieValue","getFetchClient","defaultOptions","getDefaultHeaders","Authorization","isFormDataRequest","body","FormData","addPrependingSlash","charAt","hasProtocol","RegExp","normalizeUrl","responseInterceptor","validateStatus","responseType","fetchError","blob","text","arrayBuffer","withTokenRefresh","executeRequest","paramsSerializer","params","serializedParams","qs","encode","addBaseUrl","makeCreateRequestUrl","options","pipe","fetchClient","get","createRequestUrl","defaultHeaders","Headers","signal","post","delete","put","del"],"mappings":"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"}
|
|
@@ -314,6 +314,7 @@ const getToken = ()=>{
|
|
|
314
314
|
const response = await fetch(createRequestUrl(url), {
|
|
315
315
|
signal: options?.signal ?? defaultOptions.signal,
|
|
316
316
|
method: 'GET',
|
|
317
|
+
credentials: 'include',
|
|
317
318
|
headers
|
|
318
319
|
});
|
|
319
320
|
return responseInterceptor(response, options?.validateStatus, responseType);
|
|
@@ -336,6 +337,7 @@ const getToken = ()=>{
|
|
|
336
337
|
const response = await fetch(createRequestUrl(url), {
|
|
337
338
|
signal: options?.signal ?? defaultOptions.signal,
|
|
338
339
|
method: 'POST',
|
|
340
|
+
credentials: 'include',
|
|
339
341
|
headers,
|
|
340
342
|
body: isFormDataRequest(data) ? data : JSON.stringify(data)
|
|
341
343
|
});
|
|
@@ -359,6 +361,7 @@ const getToken = ()=>{
|
|
|
359
361
|
const response = await fetch(createRequestUrl(url), {
|
|
360
362
|
signal: options?.signal ?? defaultOptions.signal,
|
|
361
363
|
method: 'PUT',
|
|
364
|
+
credentials: 'include',
|
|
362
365
|
headers,
|
|
363
366
|
body: isFormDataRequest(data) ? data : JSON.stringify(data)
|
|
364
367
|
});
|
|
@@ -376,6 +379,7 @@ const getToken = ()=>{
|
|
|
376
379
|
const response = await fetch(createRequestUrl(url), {
|
|
377
380
|
signal: options?.signal ?? defaultOptions.signal,
|
|
378
381
|
method: 'DELETE',
|
|
382
|
+
credentials: 'include',
|
|
379
383
|
headers
|
|
380
384
|
});
|
|
381
385
|
return responseInterceptor(response, options?.validateStatus);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getFetchClient.mjs","sources":["../../../../../admin/src/utils/getFetchClient.ts"],"sourcesContent":["import pipe from 'lodash/fp/pipe';\n// eslint-disable-next-line import/default\nimport qs from 'qs';\n\nimport { AUTH_COOKIE_NAME, getCookieValue, setCookie } from './cookies';\n\nimport type { errors } from '@strapi/utils';\n\nexport type ApiError =\n | errors.ApplicationError\n | errors.ForbiddenError\n | errors.NotFoundError\n | errors.NotImplementedError\n | errors.PaginationError\n | errors.PayloadTooLargeError\n | errors.PolicyError\n | errors.RateLimitError\n | errors.UnauthorizedError\n | errors.ValidationError\n | errors.YupValidationError;\n\nconst STORAGE_KEYS = {\n TOKEN: 'jwtToken',\n USER: 'userInfo',\n};\n\n/**\n * Module-level promise to ensure only one token refresh happens at a time\n */\nlet refreshPromise: Promise<string | null> | null = null;\n\n/**\n * Callback to notify the app when the token is updated (e.g., to update Redux state)\n */\nlet onTokenUpdate: ((token: string) => void) | null = null;\n\n/**\n * Callback to notify the app when the session has been terminated and the user\n * should be redirected to the login page (e.g., refresh token rejected, idle\n * session expired). The React layer registers a handler that clears local\n * auth state and navigates to /auth/login.\n */\nlet onSessionExpired: (() => void) | null = null;\n\n/**\n * Set the callback that will be called when the token is refreshed.\n * This allows the React layer to update Redux state when a token refresh occurs.\n *\n * @param callback - Function to call with the new token, or null to clear\n * @example\n * // In a React component\n * useEffect(() => {\n * setOnTokenUpdate((token) => dispatch(setToken(token)));\n * return () => setOnTokenUpdate(null);\n * }, [dispatch]);\n */\nconst setOnTokenUpdate = (callback: ((token: string) => void) | null): void => {\n onTokenUpdate = callback;\n};\n\n/**\n * Set the callback that will be called when the active session is no longer\n * valid (refresh token rejected by the server, or detected idle on the\n * client). This lets the active tab redirect to /auth/login without waiting\n * for the next user-initiated request to fail.\n *\n * @param callback - Function to call when the session ends, or null to clear\n */\nconst setOnSessionExpired = (callback: (() => void) | null): void => {\n onSessionExpired = callback;\n};\n\n/**\n * Trigger the registered session-expired callback, if any. Safe to call from\n * non-React code (e.g., the RTK Query baseQuery 401 handler).\n */\nconst triggerSessionExpired = (): void => {\n onSessionExpired?.();\n};\n\n/**\n * Check if the URL is an auth path that should not trigger token refresh.\n * Note: No ^ anchor since the URL may include the baseURL prefix (e.g., \"http://localhost:1337/admin/login\").\n * This differs from baseQuery.ts which uses ^/admin since it receives normalized paths.\n */\nconst isAuthPath = (url: string) => /\\/admin\\/(login|logout|access-token)\\b/.test(url);\n\n/**\n * Store the new token in the appropriate storage (localStorage or cookie)\n * and notify the app to update its state.\n *\n * Uses localStorage if the user selected \"remember me\" during login,\n * otherwise uses cookies for session-based storage.\n *\n * @param token - The JWT token to store\n * @internal Exported for testing purposes\n */\nconst storeToken = (token: string): void => {\n // Check if the original token was stored in localStorage (persist mode)\n const wasPersistedToLocalStorage = Boolean(localStorage.getItem(STORAGE_KEYS.TOKEN));\n\n if (wasPersistedToLocalStorage) {\n localStorage.setItem(STORAGE_KEYS.TOKEN, JSON.stringify(token));\n } else {\n setCookie(AUTH_COOKIE_NAME, token);\n }\n\n // Notify the app to update its state (e.g., Redux)\n if (onTokenUpdate) {\n onTokenUpdate(token);\n }\n};\n\n/**\n * Refresh the access token by calling the /admin/access-token endpoint.\n * This uses a low-level fetch to avoid recursion through the interceptor.\n * Returns the new token on success, or null on failure.\n */\nconst refreshAccessToken = async (): Promise<string | null> => {\n const backendURL = window.strapi.backendURL;\n\n try {\n const response = await fetch(`${backendURL}/admin/access-token`, {\n method: 'POST',\n credentials: 'include', // Include cookies for the refresh token\n headers: {\n Accept: 'application/json',\n 'Content-Type': 'application/json',\n },\n });\n\n if (!response.ok) {\n console.warn('[Auth] Token refresh failed with status:', response.status);\n return null;\n }\n\n const result = await response.json();\n const token = result?.data?.token as string | undefined;\n\n if (!token) {\n console.warn('[Auth] Token refresh response missing token');\n return null;\n }\n\n storeToken(token);\n return token;\n } catch (error) {\n console.error('[Auth] Token refresh error:', error);\n return null;\n }\n};\n\n/**\n * Attempt to refresh the token if not already refreshing.\n * Uses a module-level promise to prevent concurrent refresh requests.\n *\n * @returns The new authentication token\n * @throws {Error} If the token refresh fails (e.g., refresh token expired)\n * @internal Exported for testing purposes\n */\nconst attemptTokenRefresh = async (): Promise<string> => {\n if (!refreshPromise) {\n refreshPromise = refreshAccessToken().finally(() => {\n refreshPromise = null;\n });\n }\n\n const newToken = await refreshPromise;\n if (!newToken) {\n const error = new Error('Session expired. Please log in again.');\n error.name = 'TokenRefreshError';\n throw error;\n }\n\n return newToken;\n};\n\ntype FetchResponse<TData = unknown> = {\n data: TData;\n status?: number;\n headers?: Headers;\n};\n\ntype FetchOptions = {\n params?: unknown;\n signal?: AbortSignal;\n headers?: Record<string, string>;\n validateStatus?: ((status: number) => boolean) | null;\n responseType?: 'json' | 'blob' | 'text' | 'arrayBuffer';\n};\n\ntype FetchConfig = {\n signal?: AbortSignal;\n};\n\ninterface ErrorResponse {\n data: {\n data?: unknown;\n error: ApiError & { status?: number };\n };\n}\n\nclass FetchError extends Error {\n public name: string;\n public message: string;\n public response?: ErrorResponse;\n public code?: number;\n public status?: number;\n\n constructor(message: string, response?: ErrorResponse) {\n super(message);\n this.name = 'FetchError';\n this.message = message;\n this.response = response;\n this.code = response?.data?.error?.status;\n this.status = response?.data?.error?.status;\n\n // Ensure correct stack trace in error object\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, FetchError);\n }\n }\n}\n\nconst isFetchError = (error: unknown): error is FetchError => {\n return error instanceof FetchError;\n};\n\nconst getToken = (): string | null => {\n const fromLocalStorage = localStorage.getItem(STORAGE_KEYS.TOKEN);\n if (fromLocalStorage) {\n return JSON.parse(fromLocalStorage);\n }\n\n const fromCookie = getCookieValue(AUTH_COOKIE_NAME);\n return fromCookie ?? null;\n};\n\ntype FetchClient = {\n get: {\n (url: string, config: FetchOptions & { responseType: 'blob' }): Promise<FetchResponse<Blob>>;\n (url: string, config: FetchOptions & { responseType: 'text' }): Promise<FetchResponse<string>>;\n (\n url: string,\n config: FetchOptions & { responseType: 'arrayBuffer' }\n ): Promise<FetchResponse<ArrayBuffer>>;\n <TData = unknown>(url: string, config?: FetchOptions): Promise<FetchResponse<TData>>;\n };\n put: <TData = unknown, TSend = unknown>(\n url: string,\n data?: TSend,\n config?: FetchOptions\n ) => Promise<FetchResponse<TData>>;\n post: <TData = unknown, TSend = unknown>(\n url: string,\n data?: TSend,\n config?: FetchOptions\n ) => Promise<FetchResponse<TData>>;\n del: <TData = unknown>(url: string, config?: FetchOptions) => Promise<FetchResponse<TData>>;\n};\n\n/**\n * @public\n * @param {FetchConfig} [defaultOptions={}] - Fetch Configs.\n * @returns {FetchClient} A fetch client object with methods for making HTTP requests.\n * @description This is an abstraction around the native fetch exposed by a function. It provides a simple interface to handle API calls\n * to the Strapi backend.\n * @example\n * ```tsx\n * import { getFetchClient } from '@strapi/admin/admin';\n *\n * const myFunct = () => {\n * const { get } = getFetchClient();\n * const requestURL = \"/some-endpoint\";\n *\n * const { data } = await get(requestURL);\n *\n * return data;\n * };\n * ```\n */\nconst getFetchClient = (defaultOptions: FetchConfig = {}): FetchClient => {\n const backendURL = window.strapi.backendURL;\n\n /**\n * Create default headers with the current token.\n * This is a function so we can get a fresh token after refresh.\n */\n const getDefaultHeaders = () => ({\n Accept: 'application/json',\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${getToken()}`,\n });\n\n const isFormDataRequest = (body: unknown) => body instanceof FormData;\n const addPrependingSlash = (url: string) => (url.charAt(0) !== '/' ? `/${url}` : url);\n\n // This regular expression matches a string that starts with either \"http://\" or \"https://\" or any other protocol name in lower case letters, followed by \"://\" and ends with anything else\n const hasProtocol = (url: string) => new RegExp('^(?:[a-z+]+:)?//', 'i').test(url);\n\n // Check if the url has a prepending slash, if not add a slash\n const normalizeUrl = (url: string) => (hasProtocol(url) ? url : addPrependingSlash(url));\n\n // Add a response interceptor to return the response\n const responseInterceptor = async <TData = unknown>(\n response: Response,\n validateStatus?: FetchOptions['validateStatus'],\n responseType: NonNullable<FetchOptions['responseType']> = 'json'\n ): Promise<FetchResponse<TData>> => {\n if (responseType !== 'json') {\n if (!response.ok && !validateStatus?.(response.status)) {\n const fetchError = new FetchError('Server Error');\n fetchError.status = response.status;\n throw fetchError;\n }\n\n let result: Blob | string | ArrayBuffer;\n if (responseType === 'blob') {\n result = await response.blob();\n } else if (responseType === 'text') {\n result = await response.text();\n } else {\n result = await response.arrayBuffer();\n }\n\n return { data: result as TData, status: response.status, headers: response.headers };\n }\n\n if (response.status === 204) {\n return { data: {} as TData, status: response.status };\n }\n\n try {\n const result = await response.json();\n\n if (!response.ok && result.error && !validateStatus?.(response.status)) {\n const fetchError = new FetchError(result.error.message, { data: result });\n fetchError.status = response.status;\n throw fetchError;\n }\n\n if (!response.ok && !validateStatus?.(response.status)) {\n const fetchError = new FetchError('Unknown Server Error');\n fetchError.status = response.status;\n throw fetchError;\n }\n\n return { data: result };\n } catch (error) {\n // An empty 200 body causes `response.json()` to throw a `SyntaxError`. We treat\n // it as success and return an empty payload. We match on `error.name` rather\n // than `instanceof SyntaxError` because constructor identity differs across JS\n // realms — a Response from a different realm (e.g. undici under jsdom in tests,\n // a service worker or iframe in browsers) throws a `SyntaxError` whose\n // constructor is not the same identity as the one this module closes over. Name\n // comparison is realm-agnostic.\n if ((error as Error | null)?.name === 'SyntaxError' && response.ok) {\n return { data: {}, status: response.status } as FetchResponse<TData>;\n } else {\n throw error;\n }\n }\n };\n\n /**\n * Execute a fetch request with automatic token refresh on 401 errors.\n * @param url - The request URL (used to check if it's an auth path)\n * @param executeRequest - Function that performs the fetch (called again on retry with fresh headers)\n */\n const withTokenRefresh = async <TData>(\n url: string,\n executeRequest: () => Promise<FetchResponse<TData>>\n ): Promise<FetchResponse<TData>> => {\n try {\n return await executeRequest();\n } catch (error) {\n // Only attempt refresh for 401 errors on non-auth paths\n if (isFetchError(error) && error.status === 401 && !isAuthPath(url)) {\n try {\n await attemptTokenRefresh();\n // Retry - executeRequest will call getDefaultHeaders() again, picking up the new token\n return await executeRequest();\n } catch {\n // If refresh fails, throw the original error\n throw error;\n }\n }\n throw error;\n }\n };\n\n const paramsSerializer =\n <Param = unknown>(params?: Param) =>\n (url: string) => {\n if (params) {\n if (typeof params === 'string') {\n return `${url}?${params}`;\n }\n\n /**\n * TODO V6: Encoding should be enabled in this step\n * So the rest of the app doesn't have to worry about it,\n * It's considered a breaking change because it impacts any API request, including the user's custom code\n */\n const serializedParams = qs.stringify(params, { encode: false });\n if (serializedParams) {\n return `${url}?${serializedParams}`;\n }\n return url;\n }\n return url;\n };\n\n const addBaseUrl = (url: Parameters<typeof fetch>[0]) => {\n return `${backendURL}${url}`;\n };\n\n /**\n * We use the factory method because the options\n * are unique to the individual request\n */\n const makeCreateRequestUrl = (options?: FetchOptions) =>\n pipe(normalizeUrl, addBaseUrl, paramsSerializer(options?.params));\n\n const fetchClient: FetchClient = {\n get: async <TData>(url: string, options?: FetchOptions): Promise<FetchResponse<TData>> => {\n const createRequestUrl = makeCreateRequestUrl(options);\n const responseType = options?.responseType ?? 'json';\n\n const executeRequest = async () => {\n const { Authorization } = getDefaultHeaders();\n\n // For non-JSON response types, omit content negotiation headers that imply JSON\n const defaultHeaders = responseType === 'json' ? getDefaultHeaders() : { Authorization };\n\n const headers = new Headers({\n ...defaultHeaders,\n ...options?.headers,\n });\n\n const response = await fetch(createRequestUrl(url), {\n signal: options?.signal ?? defaultOptions.signal,\n method: 'GET',\n headers,\n });\n\n return responseInterceptor<TData>(response, options?.validateStatus, responseType);\n };\n\n return withTokenRefresh(url, executeRequest);\n },\n post: async <TData, TSend = unknown>(\n url: string,\n data?: TSend,\n options?: FetchOptions\n ): Promise<FetchResponse<TData>> => {\n const createRequestUrl = makeCreateRequestUrl(options);\n\n const executeRequest = async () => {\n const headers = new Headers({\n ...getDefaultHeaders(),\n ...options?.headers,\n });\n\n /**\n * we have to remove the Content-Type value if it was a formData request\n * the browser will automatically set the header value\n */\n if (isFormDataRequest(data)) {\n headers.delete('Content-Type');\n }\n\n const response = await fetch(createRequestUrl(url), {\n signal: options?.signal ?? defaultOptions.signal,\n method: 'POST',\n headers,\n body: isFormDataRequest(data) ? (data as FormData) : JSON.stringify(data),\n });\n return responseInterceptor<TData>(response, options?.validateStatus);\n };\n\n return withTokenRefresh(url, executeRequest);\n },\n put: async <TData, TSend = unknown>(\n url: string,\n data?: TSend,\n options?: FetchOptions\n ): Promise<FetchResponse<TData>> => {\n const createRequestUrl = makeCreateRequestUrl(options);\n\n const executeRequest = async () => {\n const headers = new Headers({\n ...getDefaultHeaders(),\n ...options?.headers,\n });\n\n /**\n * we have to remove the Content-Type value if it was a formData request\n * the browser will automatically set the header value\n */\n if (isFormDataRequest(data)) {\n headers.delete('Content-Type');\n }\n\n const response = await fetch(createRequestUrl(url), {\n signal: options?.signal ?? defaultOptions.signal,\n method: 'PUT',\n headers,\n body: isFormDataRequest(data) ? (data as FormData) : JSON.stringify(data),\n });\n\n return responseInterceptor<TData>(response, options?.validateStatus);\n };\n\n return withTokenRefresh(url, executeRequest);\n },\n del: async <TData>(url: string, options?: FetchOptions): Promise<FetchResponse<TData>> => {\n const createRequestUrl = makeCreateRequestUrl(options);\n\n const executeRequest = async () => {\n const headers = new Headers({\n ...getDefaultHeaders(),\n ...options?.headers,\n });\n\n const response = await fetch(createRequestUrl(url), {\n signal: options?.signal ?? defaultOptions.signal,\n method: 'DELETE',\n headers,\n });\n return responseInterceptor<TData>(response, options?.validateStatus);\n };\n\n return withTokenRefresh(url, executeRequest);\n },\n };\n\n return fetchClient;\n};\n\nexport {\n getFetchClient,\n isFetchError,\n FetchError,\n attemptTokenRefresh,\n storeToken,\n setOnTokenUpdate,\n setOnSessionExpired,\n triggerSessionExpired,\n};\nexport type { FetchOptions, FetchResponse, FetchConfig, FetchClient, ErrorResponse };\n"],"names":["STORAGE_KEYS","TOKEN","refreshPromise","onTokenUpdate","onSessionExpired","setOnTokenUpdate","callback","setOnSessionExpired","triggerSessionExpired","isAuthPath","url","test","storeToken","token","wasPersistedToLocalStorage","Boolean","localStorage","getItem","setItem","JSON","stringify","setCookie","AUTH_COOKIE_NAME","refreshAccessToken","backendURL","window","strapi","response","fetch","method","credentials","headers","Accept","ok","console","warn","status","result","json","data","error","attemptTokenRefresh","finally","newToken","Error","name","FetchError","message","code","captureStackTrace","isFetchError","getToken","fromLocalStorage","parse","fromCookie","getCookieValue","getFetchClient","defaultOptions","getDefaultHeaders","Authorization","isFormDataRequest","body","FormData","addPrependingSlash","charAt","hasProtocol","RegExp","normalizeUrl","responseInterceptor","validateStatus","responseType","fetchError","blob","text","arrayBuffer","withTokenRefresh","executeRequest","paramsSerializer","params","serializedParams","qs","encode","addBaseUrl","makeCreateRequestUrl","options","pipe","fetchClient","get","createRequestUrl","defaultHeaders","Headers","signal","post","delete","put","del"],"mappings":";;;;AAqBA,MAAMA,YAAAA,GAAe;IACnBC,KAAAA,EAAO,UAET,CAAA;AAEA;;AAEC,IACD,IAAIC,cAAAA,GAAgD,IAAA;AAEpD;;AAEC,IACD,IAAIC,aAAAA,GAAkD,IAAA;AAEtD;;;;;AAKC,IACD,IAAIC,gBAAAA,GAAwC,IAAA;AAE5C;;;;;;;;;;;IAYA,MAAMC,mBAAmB,CAACC,QAAAA,GAAAA;IACxBH,aAAAA,GAAgBG,QAAAA;AAClB;AAEA;;;;;;;IAQA,MAAMC,sBAAsB,CAACD,QAAAA,GAAAA;IAC3BF,gBAAAA,GAAmBE,QAAAA;AACrB;AAEA;;;AAGC,UACKE,qBAAAA,GAAwB,IAAA;AAC5BJ,IAAAA,gBAAAA,IAAAA;AACF;AAEA;;;;AAIC,IACD,MAAMK,UAAAA,GAAa,CAACC,GAAAA,GAAgB,wCAAA,CAAyCC,IAAI,CAACD,GAAAA,CAAAA;AAElF;;;;;;;;;IAUA,MAAME,aAAa,CAACC,KAAAA,GAAAA;;AAElB,IAAA,MAAMC,6BAA6BC,OAAAA,CAAQC,YAAAA,CAAaC,OAAO,CAACjB,aAAaC,KAAK,CAAA,CAAA;AAElF,IAAA,IAAIa,0BAAAA,EAA4B;AAC9BE,QAAAA,YAAAA,CAAaE,OAAO,CAAClB,YAAAA,CAAaC,KAAK,EAAEkB,IAAAA,CAAKC,SAAS,CAACP,KAAAA,CAAAA,CAAAA;IAC1D,CAAA,MAAO;AACLQ,QAAAA,SAAAA,CAAUC,gBAAAA,EAAkBT,KAAAA,CAAAA;AAC9B,IAAA;;AAGA,IAAA,IAAIV,aAAAA,EAAe;QACjBA,aAAAA,CAAcU,KAAAA,CAAAA;AAChB,IAAA;AACF;AAEA;;;;AAIC,IACD,MAAMU,kBAAAA,GAAqB,UAAA;AACzB,IAAA,MAAMC,UAAAA,GAAaC,MAAAA,CAAOC,MAAM,CAACF,UAAU;IAE3C,IAAI;AACF,QAAA,MAAMG,WAAW,MAAMC,KAAAA,CAAM,GAAGJ,UAAAA,CAAW,mBAAmB,CAAC,EAAE;YAC/DK,MAAAA,EAAQ,MAAA;YACRC,WAAAA,EAAa,SAAA;YACbC,OAAAA,EAAS;gBACPC,MAAAA,EAAQ,kBAAA;gBACR,cAAA,EAAgB;AAClB;AACF,SAAA,CAAA;QAEA,IAAI,CAACL,QAAAA,CAASM,EAAE,EAAE;AAChBC,YAAAA,OAAAA,CAAQC,IAAI,CAAC,0CAAA,EAA4CR,QAAAA,CAASS,MAAM,CAAA;YACxE,OAAO,IAAA;AACT,QAAA;QAEA,MAAMC,MAAAA,GAAS,MAAMV,QAAAA,CAASW,IAAI,EAAA;QAClC,MAAMzB,KAAAA,GAAQwB,QAAQE,IAAAA,EAAM1B,KAAAA;AAE5B,QAAA,IAAI,CAACA,KAAAA,EAAO;AACVqB,YAAAA,OAAAA,CAAQC,IAAI,CAAC,6CAAA,CAAA;YACb,OAAO,IAAA;AACT,QAAA;QAEAvB,UAAAA,CAAWC,KAAAA,CAAAA;QACX,OAAOA,KAAAA;AACT,IAAA,CAAA,CAAE,OAAO2B,KAAAA,EAAO;QACdN,OAAAA,CAAQM,KAAK,CAAC,6BAAA,EAA+BA,KAAAA,CAAAA;QAC7C,OAAO,IAAA;AACT,IAAA;AACF,CAAA;AAEA;;;;;;;AAOC,UACKC,mBAAAA,GAAsB,UAAA;AAC1B,IAAA,IAAI,CAACvC,cAAAA,EAAgB;QACnBA,cAAAA,GAAiBqB,kBAAAA,EAAAA,CAAqBmB,OAAO,CAAC,IAAA;YAC5CxC,cAAAA,GAAiB,IAAA;AACnB,QAAA,CAAA,CAAA;AACF,IAAA;AAEA,IAAA,MAAMyC,WAAW,MAAMzC,cAAAA;AACvB,IAAA,IAAI,CAACyC,QAAAA,EAAU;QACb,MAAMH,KAAAA,GAAQ,IAAII,KAAAA,CAAM,uCAAA,CAAA;AACxBJ,QAAAA,KAAAA,CAAMK,IAAI,GAAG,mBAAA;QACb,MAAML,KAAAA;AACR,IAAA;IAEA,OAAOG,QAAAA;AACT;AA2BA,MAAMG,UAAAA,SAAmBF,KAAAA,CAAAA;IAOvB,WAAA,CAAYG,OAAe,EAAEpB,QAAwB,CAAE;AACrD,QAAA,KAAK,CAACoB,OAAAA,CAAAA;QACN,IAAI,CAACF,IAAI,GAAG,YAAA;QACZ,IAAI,CAACE,OAAO,GAAGA,OAAAA;QACf,IAAI,CAACpB,QAAQ,GAAGA,QAAAA;AAChB,QAAA,IAAI,CAACqB,IAAI,GAAGrB,QAAAA,EAAUY,MAAMC,KAAAA,EAAOJ,MAAAA;AACnC,QAAA,IAAI,CAACA,MAAM,GAAGT,QAAAA,EAAUY,MAAMC,KAAAA,EAAOJ,MAAAA;;QAGrC,IAAIQ,KAAAA,CAAMK,iBAAiB,EAAE;YAC3BL,KAAAA,CAAMK,iBAAiB,CAAC,IAAI,EAAEH,UAAAA,CAAAA;AAChC,QAAA;AACF,IAAA;AACF;AAEA,MAAMI,eAAe,CAACV,KAAAA,GAAAA;AACpB,IAAA,OAAOA,KAAAA,YAAiBM,UAAAA;AAC1B;AAEA,MAAMK,QAAAA,GAAW,IAAA;AACf,IAAA,MAAMC,gBAAAA,GAAmBpC,YAAAA,CAAaC,OAAO,CAACjB,aAAaC,KAAK,CAAA;AAChE,IAAA,IAAImD,gBAAAA,EAAkB;QACpB,OAAOjC,IAAAA,CAAKkC,KAAK,CAACD,gBAAAA,CAAAA;AACpB,IAAA;AAEA,IAAA,MAAME,aAAaC,cAAAA,CAAejC,gBAAAA,CAAAA;AAClC,IAAA,OAAOgC,UAAAA,IAAc,IAAA;AACvB,CAAA;AAyBA;;;;;;;;;;;;;;;;;;;AAmBC,IACD,MAAME,cAAAA,GAAiB,CAACC,cAAAA,GAA8B,EAAE,GAAA;AACtD,IAAA,MAAMjC,UAAAA,GAAaC,MAAAA,CAAOC,MAAM,CAACF,UAAU;AAE3C;;;MAIA,MAAMkC,iBAAAA,GAAoB,KAAO;YAC/B1B,MAAAA,EAAQ,kBAAA;YACR,cAAA,EAAgB,kBAAA;YAChB2B,aAAAA,EAAe,CAAC,OAAO,EAAER,QAAAA,EAAAA,CAAAA;SAC3B,CAAA;IAEA,MAAMS,iBAAAA,GAAoB,CAACC,IAAAA,GAAkBA,IAAAA,YAAgBC,QAAAA;AAC7D,IAAA,MAAMC,kBAAAA,GAAqB,CAACrD,GAAAA,GAAiBA,GAAAA,CAAIsD,MAAM,CAAC,CAAA,CAAA,KAAO,GAAA,GAAM,CAAC,CAAC,EAAEtD,GAAAA,CAAAA,CAAK,GAAGA,GAAAA;;IAGjF,MAAMuD,WAAAA,GAAc,CAACvD,GAAAA,GAAgB,IAAIwD,OAAO,kBAAA,EAAoB,GAAA,CAAA,CAAKvD,IAAI,CAACD,GAAAA,CAAAA;;AAG9E,IAAA,MAAMyD,eAAe,CAACzD,GAAAA,GAAiBuD,WAAAA,CAAYvD,GAAAA,CAAAA,GAAOA,MAAMqD,kBAAAA,CAAmBrD,GAAAA,CAAAA;;AAGnF,IAAA,MAAM0D,mBAAAA,GAAsB,OAC1BzC,QAAAA,EACA0C,cAAAA,EACAC,eAA0D,MAAM,GAAA;AAEhE,QAAA,IAAIA,iBAAiB,MAAA,EAAQ;YAC3B,IAAI,CAAC3C,SAASM,EAAE,IAAI,CAACoC,cAAAA,GAAiB1C,QAAAA,CAASS,MAAM,CAAA,EAAG;gBACtD,MAAMmC,UAAAA,GAAa,IAAIzB,UAAAA,CAAW,cAAA,CAAA;gBAClCyB,UAAAA,CAAWnC,MAAM,GAAGT,QAAAA,CAASS,MAAM;gBACnC,MAAMmC,UAAAA;AACR,YAAA;YAEA,IAAIlC,MAAAA;AACJ,YAAA,IAAIiC,iBAAiB,MAAA,EAAQ;gBAC3BjC,MAAAA,GAAS,MAAMV,SAAS6C,IAAI,EAAA;YAC9B,CAAA,MAAO,IAAIF,iBAAiB,MAAA,EAAQ;gBAClCjC,MAAAA,GAAS,MAAMV,SAAS8C,IAAI,EAAA;YAC9B,CAAA,MAAO;gBACLpC,MAAAA,GAAS,MAAMV,SAAS+C,WAAW,EAAA;AACrC,YAAA;YAEA,OAAO;gBAAEnC,IAAAA,EAAMF,MAAAA;AAAiBD,gBAAAA,MAAAA,EAAQT,SAASS,MAAM;AAAEL,gBAAAA,OAAAA,EAASJ,SAASI;AAAQ,aAAA;AACrF,QAAA;QAEA,IAAIJ,QAAAA,CAASS,MAAM,KAAK,GAAA,EAAK;YAC3B,OAAO;AAAEG,gBAAAA,IAAAA,EAAM,EAAC;AAAYH,gBAAAA,MAAAA,EAAQT,SAASS;AAAO,aAAA;AACtD,QAAA;QAEA,IAAI;YACF,MAAMC,MAAAA,GAAS,MAAMV,QAAAA,CAASW,IAAI,EAAA;YAElC,IAAI,CAACX,QAAAA,CAASM,EAAE,IAAII,MAAAA,CAAOG,KAAK,IAAI,CAAC6B,cAAAA,GAAiB1C,QAAAA,CAASS,MAAM,CAAA,EAAG;AACtE,gBAAA,MAAMmC,aAAa,IAAIzB,UAAAA,CAAWT,OAAOG,KAAK,CAACO,OAAO,EAAE;oBAAER,IAAAA,EAAMF;AAAO,iBAAA,CAAA;gBACvEkC,UAAAA,CAAWnC,MAAM,GAAGT,QAAAA,CAASS,MAAM;gBACnC,MAAMmC,UAAAA;AACR,YAAA;YAEA,IAAI,CAAC5C,SAASM,EAAE,IAAI,CAACoC,cAAAA,GAAiB1C,QAAAA,CAASS,MAAM,CAAA,EAAG;gBACtD,MAAMmC,UAAAA,GAAa,IAAIzB,UAAAA,CAAW,sBAAA,CAAA;gBAClCyB,UAAAA,CAAWnC,MAAM,GAAGT,QAAAA,CAASS,MAAM;gBACnC,MAAMmC,UAAAA;AACR,YAAA;YAEA,OAAO;gBAAEhC,IAAAA,EAAMF;AAAO,aAAA;AACxB,QAAA,CAAA,CAAE,OAAOG,KAAAA,EAAO;;;;;;;;AAQd,YAAA,IAAI,KAACA,EAAwBK,IAAAA,KAAS,aAAA,IAAiBlB,QAAAA,CAASM,EAAE,EAAE;gBAClE,OAAO;AAAEM,oBAAAA,IAAAA,EAAM,EAAC;AAAGH,oBAAAA,MAAAA,EAAQT,SAASS;AAAO,iBAAA;YAC7C,CAAA,MAAO;gBACL,MAAMI,KAAAA;AACR,YAAA;AACF,QAAA;AACF,IAAA,CAAA;AAEA;;;;MAKA,MAAMmC,gBAAAA,GAAmB,OACvBjE,GAAAA,EACAkE,cAAAA,GAAAA;QAEA,IAAI;AACF,YAAA,OAAO,MAAMA,cAAAA,EAAAA;AACf,QAAA,CAAA,CAAE,OAAOpC,KAAAA,EAAO;;YAEd,IAAIU,YAAAA,CAAaV,UAAUA,KAAAA,CAAMJ,MAAM,KAAK,GAAA,IAAO,CAAC3B,WAAWC,GAAAA,CAAAA,EAAM;gBACnE,IAAI;oBACF,MAAM+B,mBAAAA,EAAAA;;AAEN,oBAAA,OAAO,MAAMmC,cAAAA,EAAAA;AACf,gBAAA,CAAA,CAAE,OAAM;;oBAEN,MAAMpC,KAAAA;AACR,gBAAA;AACF,YAAA;YACA,MAAMA,KAAAA;AACR,QAAA;AACF,IAAA,CAAA;IAEA,MAAMqC,gBAAAA,GACJ,CAAkBC,MAAAA,GAClB,CAACpE,GAAAA,GAAAA;AACC,YAAA,IAAIoE,MAAAA,EAAQ;gBACV,IAAI,OAAOA,WAAW,QAAA,EAAU;AAC9B,oBAAA,OAAO,CAAA,EAAGpE,GAAAA,CAAI,CAAC,EAAEoE,MAAAA,CAAAA,CAAQ;AAC3B,gBAAA;AAEA;;;;AAIC,YACD,MAAMC,gBAAAA,GAAmBC,WAAAA,CAAG5D,SAAS,CAAC0D,MAAAA,EAAQ;oBAAEG,MAAAA,EAAQ;AAAM,iBAAA,CAAA;AAC9D,gBAAA,IAAIF,gBAAAA,EAAkB;AACpB,oBAAA,OAAO,CAAA,EAAGrE,GAAAA,CAAI,CAAC,EAAEqE,gBAAAA,CAAAA,CAAkB;AACrC,gBAAA;gBACA,OAAOrE,GAAAA;AACT,YAAA;YACA,OAAOA,GAAAA;AACT,QAAA,CAAA;AAEF,IAAA,MAAMwE,aAAa,CAACxE,GAAAA,GAAAA;QAClB,OAAO,CAAA,EAAGc,aAAad,GAAAA,CAAAA,CAAK;AAC9B,IAAA,CAAA;AAEA;;;MAIA,MAAMyE,uBAAuB,CAACC,OAAAA,GAC5BC,KAAKlB,YAAAA,EAAce,UAAAA,EAAYL,iBAAiBO,OAAAA,EAASN,MAAAA,CAAAA,CAAAA;AAE3D,IAAA,MAAMQ,WAAAA,GAA2B;AAC/BC,QAAAA,GAAAA,EAAK,OAAc7E,GAAAA,EAAa0E,OAAAA,GAAAA;AAC9B,YAAA,MAAMI,mBAAmBL,oBAAAA,CAAqBC,OAAAA,CAAAA;YAC9C,MAAMd,YAAAA,GAAec,SAASd,YAAAA,IAAgB,MAAA;AAE9C,YAAA,MAAMM,cAAAA,GAAiB,UAAA;gBACrB,MAAM,EAAEjB,aAAa,EAAE,GAAGD,iBAAAA,EAAAA;;gBAG1B,MAAM+B,cAAAA,GAAiBnB,YAAAA,KAAiB,MAAA,GAASZ,iBAAAA,EAAAA,GAAsB;AAAEC,oBAAAA;AAAc,iBAAA;gBAEvF,MAAM5B,OAAAA,GAAU,IAAI2D,OAAAA,CAAQ;AAC1B,oBAAA,GAAGD,cAAc;AACjB,oBAAA,GAAGL,SAASrD;AACd,iBAAA,CAAA;AAEA,gBAAA,MAAMJ,QAAAA,GAAW,MAAMC,KAAAA,CAAM4D,gBAAAA,CAAiB9E,GAAAA,CAAAA,EAAM;oBAClDiF,MAAAA,EAAQP,OAAAA,EAASO,MAAAA,IAAUlC,cAAAA,CAAekC,MAAM;oBAChD9D,MAAAA,EAAQ,KAAA;AACRE,oBAAAA;AACF,iBAAA,CAAA;gBAEA,OAAOqC,mBAAAA,CAA2BzC,QAAAA,EAAUyD,OAAAA,EAASf,cAAAA,EAAgBC,YAAAA,CAAAA;AACvE,YAAA,CAAA;AAEA,YAAA,OAAOK,iBAAiBjE,GAAAA,EAAKkE,cAAAA,CAAAA;AAC/B,QAAA,CAAA;QACAgB,IAAAA,EAAM,OACJlF,KACA6B,IAAAA,EACA6C,OAAAA,GAAAA;AAEA,YAAA,MAAMI,mBAAmBL,oBAAAA,CAAqBC,OAAAA,CAAAA;AAE9C,YAAA,MAAMR,cAAAA,GAAiB,UAAA;gBACrB,MAAM7C,OAAAA,GAAU,IAAI2D,OAAAA,CAAQ;AAC1B,oBAAA,GAAGhC,iBAAAA,EAAmB;AACtB,oBAAA,GAAG0B,SAASrD;AACd,iBAAA,CAAA;AAEA;;;YAIA,IAAI6B,kBAAkBrB,IAAAA,CAAAA,EAAO;AAC3BR,oBAAAA,OAAAA,CAAQ8D,MAAM,CAAC,cAAA,CAAA;AACjB,gBAAA;AAEA,gBAAA,MAAMlE,QAAAA,GAAW,MAAMC,KAAAA,CAAM4D,gBAAAA,CAAiB9E,GAAAA,CAAAA,EAAM;oBAClDiF,MAAAA,EAAQP,OAAAA,EAASO,MAAAA,IAAUlC,cAAAA,CAAekC,MAAM;oBAChD9D,MAAAA,EAAQ,MAAA;AACRE,oBAAAA,OAAAA;AACA8B,oBAAAA,IAAAA,EAAMD,iBAAAA,CAAkBrB,IAAAA,CAAAA,GAASA,IAAAA,GAAoBpB,IAAAA,CAAKC,SAAS,CAACmB,IAAAA;AACtE,iBAAA,CAAA;gBACA,OAAO6B,mBAAAA,CAA2BzC,UAAUyD,OAAAA,EAASf,cAAAA,CAAAA;AACvD,YAAA,CAAA;AAEA,YAAA,OAAOM,iBAAiBjE,GAAAA,EAAKkE,cAAAA,CAAAA;AAC/B,QAAA,CAAA;QACAkB,GAAAA,EAAK,OACHpF,KACA6B,IAAAA,EACA6C,OAAAA,GAAAA;AAEA,YAAA,MAAMI,mBAAmBL,oBAAAA,CAAqBC,OAAAA,CAAAA;AAE9C,YAAA,MAAMR,cAAAA,GAAiB,UAAA;gBACrB,MAAM7C,OAAAA,GAAU,IAAI2D,OAAAA,CAAQ;AAC1B,oBAAA,GAAGhC,iBAAAA,EAAmB;AACtB,oBAAA,GAAG0B,SAASrD;AACd,iBAAA,CAAA;AAEA;;;YAIA,IAAI6B,kBAAkBrB,IAAAA,CAAAA,EAAO;AAC3BR,oBAAAA,OAAAA,CAAQ8D,MAAM,CAAC,cAAA,CAAA;AACjB,gBAAA;AAEA,gBAAA,MAAMlE,QAAAA,GAAW,MAAMC,KAAAA,CAAM4D,gBAAAA,CAAiB9E,GAAAA,CAAAA,EAAM;oBAClDiF,MAAAA,EAAQP,OAAAA,EAASO,MAAAA,IAAUlC,cAAAA,CAAekC,MAAM;oBAChD9D,MAAAA,EAAQ,KAAA;AACRE,oBAAAA,OAAAA;AACA8B,oBAAAA,IAAAA,EAAMD,iBAAAA,CAAkBrB,IAAAA,CAAAA,GAASA,IAAAA,GAAoBpB,IAAAA,CAAKC,SAAS,CAACmB,IAAAA;AACtE,iBAAA,CAAA;gBAEA,OAAO6B,mBAAAA,CAA2BzC,UAAUyD,OAAAA,EAASf,cAAAA,CAAAA;AACvD,YAAA,CAAA;AAEA,YAAA,OAAOM,iBAAiBjE,GAAAA,EAAKkE,cAAAA,CAAAA;AAC/B,QAAA,CAAA;AACAmB,QAAAA,GAAAA,EAAK,OAAcrF,GAAAA,EAAa0E,OAAAA,GAAAA;AAC9B,YAAA,MAAMI,mBAAmBL,oBAAAA,CAAqBC,OAAAA,CAAAA;AAE9C,YAAA,MAAMR,cAAAA,GAAiB,UAAA;gBACrB,MAAM7C,OAAAA,GAAU,IAAI2D,OAAAA,CAAQ;AAC1B,oBAAA,GAAGhC,iBAAAA,EAAmB;AACtB,oBAAA,GAAG0B,SAASrD;AACd,iBAAA,CAAA;AAEA,gBAAA,MAAMJ,QAAAA,GAAW,MAAMC,KAAAA,CAAM4D,gBAAAA,CAAiB9E,GAAAA,CAAAA,EAAM;oBAClDiF,MAAAA,EAAQP,OAAAA,EAASO,MAAAA,IAAUlC,cAAAA,CAAekC,MAAM;oBAChD9D,MAAAA,EAAQ,QAAA;AACRE,oBAAAA;AACF,iBAAA,CAAA;gBACA,OAAOqC,mBAAAA,CAA2BzC,UAAUyD,OAAAA,EAASf,cAAAA,CAAAA;AACvD,YAAA,CAAA;AAEA,YAAA,OAAOM,iBAAiBjE,GAAAA,EAAKkE,cAAAA,CAAAA;AAC/B,QAAA;AACF,KAAA;IAEA,OAAOU,WAAAA;AACT;;;;"}
|
|
1
|
+
{"version":3,"file":"getFetchClient.mjs","sources":["../../../../../admin/src/utils/getFetchClient.ts"],"sourcesContent":["import pipe from 'lodash/fp/pipe';\n// eslint-disable-next-line import/default\nimport qs from 'qs';\n\nimport { AUTH_COOKIE_NAME, getCookieValue, setCookie } from './cookies';\n\nimport type { errors } from '@strapi/utils';\n\nexport type ApiError =\n | errors.ApplicationError\n | errors.ForbiddenError\n | errors.NotFoundError\n | errors.NotImplementedError\n | errors.PaginationError\n | errors.PayloadTooLargeError\n | errors.PolicyError\n | errors.RateLimitError\n | errors.UnauthorizedError\n | errors.ValidationError\n | errors.YupValidationError;\n\nconst STORAGE_KEYS = {\n TOKEN: 'jwtToken',\n USER: 'userInfo',\n};\n\n/**\n * Module-level promise to ensure only one token refresh happens at a time\n */\nlet refreshPromise: Promise<string | null> | null = null;\n\n/**\n * Callback to notify the app when the token is updated (e.g., to update Redux state)\n */\nlet onTokenUpdate: ((token: string) => void) | null = null;\n\n/**\n * Callback to notify the app when the session has been terminated and the user\n * should be redirected to the login page (e.g., refresh token rejected, idle\n * session expired). The React layer registers a handler that clears local\n * auth state and navigates to /auth/login.\n */\nlet onSessionExpired: (() => void) | null = null;\n\n/**\n * Set the callback that will be called when the token is refreshed.\n * This allows the React layer to update Redux state when a token refresh occurs.\n *\n * @param callback - Function to call with the new token, or null to clear\n * @example\n * // In a React component\n * useEffect(() => {\n * setOnTokenUpdate((token) => dispatch(setToken(token)));\n * return () => setOnTokenUpdate(null);\n * }, [dispatch]);\n */\nconst setOnTokenUpdate = (callback: ((token: string) => void) | null): void => {\n onTokenUpdate = callback;\n};\n\n/**\n * Set the callback that will be called when the active session is no longer\n * valid (refresh token rejected by the server, or detected idle on the\n * client). This lets the active tab redirect to /auth/login without waiting\n * for the next user-initiated request to fail.\n *\n * @param callback - Function to call when the session ends, or null to clear\n */\nconst setOnSessionExpired = (callback: (() => void) | null): void => {\n onSessionExpired = callback;\n};\n\n/**\n * Trigger the registered session-expired callback, if any. Safe to call from\n * non-React code (e.g., the RTK Query baseQuery 401 handler).\n */\nconst triggerSessionExpired = (): void => {\n onSessionExpired?.();\n};\n\n/**\n * Check if the URL is an auth path that should not trigger token refresh.\n * Note: No ^ anchor since the URL may include the baseURL prefix (e.g., \"http://localhost:1337/admin/login\").\n * This differs from baseQuery.ts which uses ^/admin since it receives normalized paths.\n */\nconst isAuthPath = (url: string) => /\\/admin\\/(login|logout|access-token)\\b/.test(url);\n\n/**\n * Store the new token in the appropriate storage (localStorage or cookie)\n * and notify the app to update its state.\n *\n * Uses localStorage if the user selected \"remember me\" during login,\n * otherwise uses cookies for session-based storage.\n *\n * @param token - The JWT token to store\n * @internal Exported for testing purposes\n */\nconst storeToken = (token: string): void => {\n // Check if the original token was stored in localStorage (persist mode)\n const wasPersistedToLocalStorage = Boolean(localStorage.getItem(STORAGE_KEYS.TOKEN));\n\n if (wasPersistedToLocalStorage) {\n localStorage.setItem(STORAGE_KEYS.TOKEN, JSON.stringify(token));\n } else {\n setCookie(AUTH_COOKIE_NAME, token);\n }\n\n // Notify the app to update its state (e.g., Redux)\n if (onTokenUpdate) {\n onTokenUpdate(token);\n }\n};\n\n/**\n * Refresh the access token by calling the /admin/access-token endpoint.\n * This uses a low-level fetch to avoid recursion through the interceptor.\n * Returns the new token on success, or null on failure.\n */\nconst refreshAccessToken = async (): Promise<string | null> => {\n const backendURL = window.strapi.backendURL;\n\n try {\n const response = await fetch(`${backendURL}/admin/access-token`, {\n method: 'POST',\n credentials: 'include', // Include cookies for the refresh token\n headers: {\n Accept: 'application/json',\n 'Content-Type': 'application/json',\n },\n });\n\n if (!response.ok) {\n console.warn('[Auth] Token refresh failed with status:', response.status);\n return null;\n }\n\n const result = await response.json();\n const token = result?.data?.token as string | undefined;\n\n if (!token) {\n console.warn('[Auth] Token refresh response missing token');\n return null;\n }\n\n storeToken(token);\n return token;\n } catch (error) {\n console.error('[Auth] Token refresh error:', error);\n return null;\n }\n};\n\n/**\n * Attempt to refresh the token if not already refreshing.\n * Uses a module-level promise to prevent concurrent refresh requests.\n *\n * @returns The new authentication token\n * @throws {Error} If the token refresh fails (e.g., refresh token expired)\n * @internal Exported for testing purposes\n */\nconst attemptTokenRefresh = async (): Promise<string> => {\n if (!refreshPromise) {\n refreshPromise = refreshAccessToken().finally(() => {\n refreshPromise = null;\n });\n }\n\n const newToken = await refreshPromise;\n if (!newToken) {\n const error = new Error('Session expired. Please log in again.');\n error.name = 'TokenRefreshError';\n throw error;\n }\n\n return newToken;\n};\n\ntype FetchResponse<TData = unknown> = {\n data: TData;\n status?: number;\n headers?: Headers;\n};\n\ntype FetchOptions = {\n params?: unknown;\n signal?: AbortSignal;\n headers?: Record<string, string>;\n validateStatus?: ((status: number) => boolean) | null;\n responseType?: 'json' | 'blob' | 'text' | 'arrayBuffer';\n};\n\ntype FetchConfig = {\n signal?: AbortSignal;\n};\n\ninterface ErrorResponse {\n data: {\n data?: unknown;\n error: ApiError & { status?: number };\n };\n}\n\nclass FetchError extends Error {\n public name: string;\n public message: string;\n public response?: ErrorResponse;\n public code?: number;\n public status?: number;\n\n constructor(message: string, response?: ErrorResponse) {\n super(message);\n this.name = 'FetchError';\n this.message = message;\n this.response = response;\n this.code = response?.data?.error?.status;\n this.status = response?.data?.error?.status;\n\n // Ensure correct stack trace in error object\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, FetchError);\n }\n }\n}\n\nconst isFetchError = (error: unknown): error is FetchError => {\n return error instanceof FetchError;\n};\n\nconst getToken = (): string | null => {\n const fromLocalStorage = localStorage.getItem(STORAGE_KEYS.TOKEN);\n if (fromLocalStorage) {\n return JSON.parse(fromLocalStorage);\n }\n\n const fromCookie = getCookieValue(AUTH_COOKIE_NAME);\n return fromCookie ?? null;\n};\n\ntype FetchClient = {\n get: {\n (url: string, config: FetchOptions & { responseType: 'blob' }): Promise<FetchResponse<Blob>>;\n (url: string, config: FetchOptions & { responseType: 'text' }): Promise<FetchResponse<string>>;\n (\n url: string,\n config: FetchOptions & { responseType: 'arrayBuffer' }\n ): Promise<FetchResponse<ArrayBuffer>>;\n <TData = unknown>(url: string, config?: FetchOptions): Promise<FetchResponse<TData>>;\n };\n put: <TData = unknown, TSend = unknown>(\n url: string,\n data?: TSend,\n config?: FetchOptions\n ) => Promise<FetchResponse<TData>>;\n post: <TData = unknown, TSend = unknown>(\n url: string,\n data?: TSend,\n config?: FetchOptions\n ) => Promise<FetchResponse<TData>>;\n del: <TData = unknown>(url: string, config?: FetchOptions) => Promise<FetchResponse<TData>>;\n};\n\n/**\n * @public\n * @param {FetchConfig} [defaultOptions={}] - Fetch Configs.\n * @returns {FetchClient} A fetch client object with methods for making HTTP requests.\n * @description This is an abstraction around the native fetch exposed by a function. It provides a simple interface to handle API calls\n * to the Strapi backend.\n * @example\n * ```tsx\n * import { getFetchClient } from '@strapi/admin/admin';\n *\n * const myFunct = () => {\n * const { get } = getFetchClient();\n * const requestURL = \"/some-endpoint\";\n *\n * const { data } = await get(requestURL);\n *\n * return data;\n * };\n * ```\n */\nconst getFetchClient = (defaultOptions: FetchConfig = {}): FetchClient => {\n const backendURL = window.strapi.backendURL;\n\n /**\n * Create default headers with the current token.\n * This is a function so we can get a fresh token after refresh.\n */\n const getDefaultHeaders = () => ({\n Accept: 'application/json',\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${getToken()}`,\n });\n\n const isFormDataRequest = (body: unknown) => body instanceof FormData;\n const addPrependingSlash = (url: string) => (url.charAt(0) !== '/' ? `/${url}` : url);\n\n // This regular expression matches a string that starts with either \"http://\" or \"https://\" or any other protocol name in lower case letters, followed by \"://\" and ends with anything else\n const hasProtocol = (url: string) => new RegExp('^(?:[a-z+]+:)?//', 'i').test(url);\n\n // Check if the url has a prepending slash, if not add a slash\n const normalizeUrl = (url: string) => (hasProtocol(url) ? url : addPrependingSlash(url));\n\n // Add a response interceptor to return the response\n const responseInterceptor = async <TData = unknown>(\n response: Response,\n validateStatus?: FetchOptions['validateStatus'],\n responseType: NonNullable<FetchOptions['responseType']> = 'json'\n ): Promise<FetchResponse<TData>> => {\n if (responseType !== 'json') {\n if (!response.ok && !validateStatus?.(response.status)) {\n const fetchError = new FetchError('Server Error');\n fetchError.status = response.status;\n throw fetchError;\n }\n\n let result: Blob | string | ArrayBuffer;\n if (responseType === 'blob') {\n result = await response.blob();\n } else if (responseType === 'text') {\n result = await response.text();\n } else {\n result = await response.arrayBuffer();\n }\n\n return { data: result as TData, status: response.status, headers: response.headers };\n }\n\n if (response.status === 204) {\n return { data: {} as TData, status: response.status };\n }\n\n try {\n const result = await response.json();\n\n if (!response.ok && result.error && !validateStatus?.(response.status)) {\n const fetchError = new FetchError(result.error.message, { data: result });\n fetchError.status = response.status;\n throw fetchError;\n }\n\n if (!response.ok && !validateStatus?.(response.status)) {\n const fetchError = new FetchError('Unknown Server Error');\n fetchError.status = response.status;\n throw fetchError;\n }\n\n return { data: result };\n } catch (error) {\n // An empty 200 body causes `response.json()` to throw a `SyntaxError`. We treat\n // it as success and return an empty payload. We match on `error.name` rather\n // than `instanceof SyntaxError` because constructor identity differs across JS\n // realms — a Response from a different realm (e.g. undici under jsdom in tests,\n // a service worker or iframe in browsers) throws a `SyntaxError` whose\n // constructor is not the same identity as the one this module closes over. Name\n // comparison is realm-agnostic.\n if ((error as Error | null)?.name === 'SyntaxError' && response.ok) {\n return { data: {}, status: response.status } as FetchResponse<TData>;\n } else {\n throw error;\n }\n }\n };\n\n /**\n * Execute a fetch request with automatic token refresh on 401 errors.\n * @param url - The request URL (used to check if it's an auth path)\n * @param executeRequest - Function that performs the fetch (called again on retry with fresh headers)\n */\n const withTokenRefresh = async <TData>(\n url: string,\n executeRequest: () => Promise<FetchResponse<TData>>\n ): Promise<FetchResponse<TData>> => {\n try {\n return await executeRequest();\n } catch (error) {\n // Only attempt refresh for 401 errors on non-auth paths\n if (isFetchError(error) && error.status === 401 && !isAuthPath(url)) {\n try {\n await attemptTokenRefresh();\n // Retry - executeRequest will call getDefaultHeaders() again, picking up the new token\n return await executeRequest();\n } catch {\n // If refresh fails, throw the original error\n throw error;\n }\n }\n throw error;\n }\n };\n\n const paramsSerializer =\n <Param = unknown>(params?: Param) =>\n (url: string) => {\n if (params) {\n if (typeof params === 'string') {\n return `${url}?${params}`;\n }\n\n /**\n * TODO V6: Encoding should be enabled in this step\n * So the rest of the app doesn't have to worry about it,\n * It's considered a breaking change because it impacts any API request, including the user's custom code\n */\n const serializedParams = qs.stringify(params, { encode: false });\n if (serializedParams) {\n return `${url}?${serializedParams}`;\n }\n return url;\n }\n return url;\n };\n\n const addBaseUrl = (url: Parameters<typeof fetch>[0]) => {\n return `${backendURL}${url}`;\n };\n\n /**\n * We use the factory method because the options\n * are unique to the individual request\n */\n const makeCreateRequestUrl = (options?: FetchOptions) =>\n pipe(normalizeUrl, addBaseUrl, paramsSerializer(options?.params));\n\n const fetchClient: FetchClient = {\n get: async <TData>(url: string, options?: FetchOptions): Promise<FetchResponse<TData>> => {\n const createRequestUrl = makeCreateRequestUrl(options);\n const responseType = options?.responseType ?? 'json';\n\n const executeRequest = async () => {\n const { Authorization } = getDefaultHeaders();\n\n // For non-JSON response types, omit content negotiation headers that imply JSON\n const defaultHeaders = responseType === 'json' ? getDefaultHeaders() : { Authorization };\n\n const headers = new Headers({\n ...defaultHeaders,\n ...options?.headers,\n });\n\n const response = await fetch(createRequestUrl(url), {\n signal: options?.signal ?? defaultOptions.signal,\n method: 'GET',\n credentials: 'include',\n headers,\n });\n\n return responseInterceptor<TData>(response, options?.validateStatus, responseType);\n };\n\n return withTokenRefresh(url, executeRequest);\n },\n post: async <TData, TSend = unknown>(\n url: string,\n data?: TSend,\n options?: FetchOptions\n ): Promise<FetchResponse<TData>> => {\n const createRequestUrl = makeCreateRequestUrl(options);\n\n const executeRequest = async () => {\n const headers = new Headers({\n ...getDefaultHeaders(),\n ...options?.headers,\n });\n\n /**\n * we have to remove the Content-Type value if it was a formData request\n * the browser will automatically set the header value\n */\n if (isFormDataRequest(data)) {\n headers.delete('Content-Type');\n }\n\n const response = await fetch(createRequestUrl(url), {\n signal: options?.signal ?? defaultOptions.signal,\n method: 'POST',\n credentials: 'include',\n headers,\n body: isFormDataRequest(data) ? (data as FormData) : JSON.stringify(data),\n });\n return responseInterceptor<TData>(response, options?.validateStatus);\n };\n\n return withTokenRefresh(url, executeRequest);\n },\n put: async <TData, TSend = unknown>(\n url: string,\n data?: TSend,\n options?: FetchOptions\n ): Promise<FetchResponse<TData>> => {\n const createRequestUrl = makeCreateRequestUrl(options);\n\n const executeRequest = async () => {\n const headers = new Headers({\n ...getDefaultHeaders(),\n ...options?.headers,\n });\n\n /**\n * we have to remove the Content-Type value if it was a formData request\n * the browser will automatically set the header value\n */\n if (isFormDataRequest(data)) {\n headers.delete('Content-Type');\n }\n\n const response = await fetch(createRequestUrl(url), {\n signal: options?.signal ?? defaultOptions.signal,\n method: 'PUT',\n credentials: 'include',\n headers,\n body: isFormDataRequest(data) ? (data as FormData) : JSON.stringify(data),\n });\n\n return responseInterceptor<TData>(response, options?.validateStatus);\n };\n\n return withTokenRefresh(url, executeRequest);\n },\n del: async <TData>(url: string, options?: FetchOptions): Promise<FetchResponse<TData>> => {\n const createRequestUrl = makeCreateRequestUrl(options);\n\n const executeRequest = async () => {\n const headers = new Headers({\n ...getDefaultHeaders(),\n ...options?.headers,\n });\n\n const response = await fetch(createRequestUrl(url), {\n signal: options?.signal ?? defaultOptions.signal,\n method: 'DELETE',\n credentials: 'include',\n headers,\n });\n return responseInterceptor<TData>(response, options?.validateStatus);\n };\n\n return withTokenRefresh(url, executeRequest);\n },\n };\n\n return fetchClient;\n};\n\nexport {\n getFetchClient,\n isFetchError,\n FetchError,\n attemptTokenRefresh,\n storeToken,\n setOnTokenUpdate,\n setOnSessionExpired,\n triggerSessionExpired,\n};\nexport type { FetchOptions, FetchResponse, FetchConfig, FetchClient, ErrorResponse };\n"],"names":["STORAGE_KEYS","TOKEN","refreshPromise","onTokenUpdate","onSessionExpired","setOnTokenUpdate","callback","setOnSessionExpired","triggerSessionExpired","isAuthPath","url","test","storeToken","token","wasPersistedToLocalStorage","Boolean","localStorage","getItem","setItem","JSON","stringify","setCookie","AUTH_COOKIE_NAME","refreshAccessToken","backendURL","window","strapi","response","fetch","method","credentials","headers","Accept","ok","console","warn","status","result","json","data","error","attemptTokenRefresh","finally","newToken","Error","name","FetchError","message","code","captureStackTrace","isFetchError","getToken","fromLocalStorage","parse","fromCookie","getCookieValue","getFetchClient","defaultOptions","getDefaultHeaders","Authorization","isFormDataRequest","body","FormData","addPrependingSlash","charAt","hasProtocol","RegExp","normalizeUrl","responseInterceptor","validateStatus","responseType","fetchError","blob","text","arrayBuffer","withTokenRefresh","executeRequest","paramsSerializer","params","serializedParams","qs","encode","addBaseUrl","makeCreateRequestUrl","options","pipe","fetchClient","get","createRequestUrl","defaultHeaders","Headers","signal","post","delete","put","del"],"mappings":"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"}
|