nextemos 6.3.0 → 6.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,27 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
2
25
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
26
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
27
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -25,7 +48,68 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
25
48
  Object.defineProperty(exports, "__esModule", { value: true });
26
49
  const __1 = require("../");
27
50
  const getConfigs_1 = __importDefault(require("./getConfigs"));
28
- const { FETCH_OPTIONS } = getConfigs_1.default;
51
+ const { FETCH_OPTIONS, ENVIRONMENT } = getConfigs_1.default;
52
+ // --- www-authenticate interceptor yardımcıları -------------------------------
53
+ // Backend, environment_token süresi dolduğunda yanıtta standart `www-authenticate`
54
+ // başlığını döndürür. Aşağıdaki yardımcılar böyle bir yanıtta token'ı yenileyip isteği bir
55
+ // kez tekrarlar. Eskiden tüketici projelerde Edge middleware'in (readEnvironment) yaptığı işi
56
+ // merkezîleştirir → tüm projeler yalnız nextemos versiyonunu yükselterek faydalanır.
57
+ // Interceptor logları nextemos LOG_LEVEL konvansiyonuna uyar (debug/info/trace).
58
+ const logEnvRefresh = (...args) => {
59
+ if (["debug", "info", "trace"].includes(FETCH_OPTIONS.LOG_LEVEL))
60
+ console.log("🔄 [nextemos:env-refresh]", ...args);
61
+ };
62
+ // Aynı süresi dolmuş token için eşzamanlı yenilemeleri tek isteğe indirir (race önleme).
63
+ const environmentRefreshInFlight = new Map();
64
+ const refreshExpiredEnvironment = (environment, ctx) => {
65
+ const inFlight = environmentRefreshInFlight.get(environment);
66
+ if (inFlight) {
67
+ logEnvRefresh("zaten devam eden yenilemeye katıldı (single-flight)");
68
+ return inFlight;
69
+ }
70
+ const task = (() => __awaiter(void 0, void 0, void 0, function* () {
71
+ try {
72
+ logEnvRefresh("environment_token yenileniyor…");
73
+ // Dinamik import: services → fetchRequest dairesel bağımlılığını ve TDZ'yi önler.
74
+ const { EnvironmentService } = yield Promise.resolve().then(() => __importStar(require("../services/environment")));
75
+ const { data } = yield EnvironmentService.Refresh({
76
+ environment,
77
+ countryCode: ENVIRONMENT.COUNTRY_CODE,
78
+ platform: ENVIRONMENT.PLATFORM,
79
+ }, {
80
+ version: ctx.version,
81
+ __envRetried: true,
82
+ headers: ctx.authorization
83
+ ? { Authorization: ctx.authorization }
84
+ : undefined,
85
+ });
86
+ const refreshed = (data === null || data === void 0 ? void 0 : data.environment) || null;
87
+ logEnvRefresh(refreshed
88
+ ? "environment_token yenilendi ✅"
89
+ : "yenileme başarısız: yanıtta environment yok ⚠️");
90
+ return refreshed;
91
+ }
92
+ catch (error) {
93
+ logEnvRefresh("yenileme hatası ❌", error);
94
+ return null;
95
+ }
96
+ finally {
97
+ environmentRefreshInFlight.delete(environment);
98
+ }
99
+ }))();
100
+ environmentRefreshInFlight.set(environment, task);
101
+ return task;
102
+ };
103
+ // Yenilenen token'ı, tüketicideki mevcut cookieHandler'ın (data.refreshEnvironment) sıfır
104
+ // değişiklikle yazabilmesi için yanıt gövdesine de enjekte eder.
105
+ const injectRefreshEnvironment = (data, environment) => {
106
+ if (data && typeof data === "object" && !Array.isArray(data)) {
107
+ const obj = data;
108
+ if (!obj.refreshEnvironment)
109
+ obj.refreshEnvironment = environment;
110
+ }
111
+ return data;
112
+ };
29
113
  /**
30
114
  * İstek yapmak için bir HTTP istemcisi oluşturur.
31
115
  * @returns {object} İstek yapmak için yöntemlere sahip HTTP istemcisi.
@@ -39,7 +123,7 @@ const fetchRequest = () => {
39
123
  */
40
124
  const request = (_a) => __awaiter(void 0, void 0, void 0, function* () {
41
125
  var _b;
42
- var { url, method, params } = _a, options = __rest(_a, ["url", "method", "params"]);
126
+ var { url, method, params, __envRetried } = _a, options = __rest(_a, ["url", "method", "params", "__envRetried"]);
43
127
  const apiURL = new URL(url);
44
128
  if (params) {
45
129
  Object.keys(params).forEach((key) => {
@@ -104,6 +188,27 @@ const fetchRequest = () => {
104
188
  };
105
189
  }
106
190
  if (!response.ok) {
191
+ // www-authenticate interceptor: environment_token süresi dolmuş → yenile + bir kez retry
192
+ if (response.status === __1.HttpStatusCode.UNAUTHORIZED &&
193
+ headers["www-authenticate"] === "Environment" &&
194
+ !__envRetried) {
195
+ logEnvRefresh("www-authenticate: Environment yakalandı →", apiURL.toString());
196
+ const requestHeaders = options.headers;
197
+ const currentEnvironment = requestHeaders === null || requestHeaders === void 0 ? void 0 : requestHeaders["Environment"];
198
+ if (currentEnvironment) {
199
+ const refreshedEnvironment = yield refreshExpiredEnvironment(currentEnvironment, {
200
+ authorization: requestHeaders === null || requestHeaders === void 0 ? void 0 : requestHeaders["Authorization"],
201
+ version: options.version,
202
+ });
203
+ if (refreshedEnvironment) {
204
+ logEnvRefresh("istek yeni token ile tekrarlanıyor →", apiURL.toString());
205
+ const retried = yield request(Object.assign(Object.assign({ url,
206
+ method,
207
+ params }, options), { __envRetried: true, headers: Object.assign(Object.assign({}, requestHeaders), { Environment: refreshedEnvironment }) }));
208
+ return Object.assign(Object.assign({}, retried), { refreshedEnvironment, data: injectRefreshEnvironment(retried.data, refreshedEnvironment) });
209
+ }
210
+ }
211
+ }
107
212
  const errorDetail = yield response.text();
108
213
  if (["debug", "info", "trace"].includes(FETCH_OPTIONS.LOG_LEVEL) &&
109
214
  response.status !== __1.HttpStatusCode.NOT_FOUND)
@@ -6,12 +6,9 @@ export declare const defaultConfig: {
6
6
  LOG_LEVEL: string;
7
7
  CACHE: string;
8
8
  };
9
- KEYCLOAK: {
10
- URL: string;
11
- REALM: string;
12
- CLIENT_ID: string;
13
- CLIENT_SECRET: string;
14
- GRANT_TYPE: string;
9
+ ENVIRONMENT: {
10
+ COUNTRY_CODE: string;
11
+ PLATFORM: string;
15
12
  };
16
13
  K8S_NAMESPACE: string;
17
14
  K8S_SERVICE_NAMES: {
@@ -29,7 +29,7 @@ function deepMerge(target, ...sources) {
29
29
  * Check if value is an object (but not null, array, or primitive)
30
30
  */
31
31
  function isObject(item) {
32
- return item && typeof item === 'object' && !Array.isArray(item);
32
+ return item && typeof item === "object" && !Array.isArray(item);
33
33
  }
34
34
  exports.defaultConfig = {
35
35
  API_URL: "",
@@ -39,12 +39,11 @@ exports.defaultConfig = {
39
39
  LOG_LEVEL: "info",
40
40
  CACHE: "",
41
41
  },
42
- KEYCLOAK: {
43
- URL: "https://identity.proj-e.com",
44
- REALM: "test",
45
- CLIENT_ID: "test_client",
46
- CLIENT_SECRET: "",
47
- GRANT_TYPE: "client_credentials",
42
+ // www-authenticate interceptor environment_token'ı yenilerken kullanılan varsayılanlar.
43
+ // Proje bazında nextemos-config.json ile override edilebilir.
44
+ ENVIRONMENT: {
45
+ COUNTRY_CODE: "TR",
46
+ PLATFORM: "desktop",
48
47
  },
49
48
  K8S_NAMESPACE: "emosv2service",
50
49
  K8S_SERVICE_NAMES: {
@@ -80,7 +79,4 @@ catch (error) {
80
79
  }
81
80
  // Merge with default config to ensure all required fields exist
82
81
  const NEXTEMOS_CONFIG = deepMerge({}, exports.defaultConfig, loadedConfig);
83
- if (process.env.KEYCLOAK_CLIENT_SECRET) {
84
- NEXTEMOS_CONFIG.KEYCLOAK.CLIENT_SECRET = process.env.KEYCLOAK_CLIENT_SECRET;
85
- }
86
82
  exports.default = NEXTEMOS_CONFIG;
@@ -4,10 +4,14 @@ export interface IRequestOptions extends RequestInit {
4
4
  url: string;
5
5
  method: HTTPMethod;
6
6
  params?: Record<string, any>;
7
+ /** internal: www-authenticate retry loop guard */
8
+ __envRetried?: boolean;
7
9
  }
8
10
  export interface IRequestInit extends RequestInit {
9
11
  useClient?: boolean;
10
12
  version?: string;
13
+ /** internal: www-authenticate retry loop guard */
14
+ __envRetried?: boolean;
11
15
  }
12
16
  export interface IApiResponse<T = any> {
13
17
  data?: T;
@@ -17,6 +21,8 @@ export interface IApiResponse<T = any> {
17
21
  headers?: {
18
22
  [key: string]: string;
19
23
  };
24
+ /** www-authenticate sonrası interceptor tarafından yenilenen environment_token */
25
+ refreshedEnvironment?: string;
20
26
  }
21
27
  export interface IRequestBase {
22
28
  language?: string;
@@ -8,14 +8,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  step((generator = generator.apply(thisArg, _arguments || [])).next());
9
9
  });
10
10
  };
11
- var __importDefault = (this && this.__importDefault) || function (mod) {
12
- return (mod && mod.__esModule) ? mod : { "default": mod };
13
- };
14
11
  Object.defineProperty(exports, "__esModule", { value: true });
15
12
  exports.KeycloakService = void 0;
16
13
  const __1 = require("../..");
17
- const getConfigs_1 = __importDefault(require("../../helpers/getConfigs"));
18
- const { KEYCLOAK } = getConfigs_1.default;
19
14
  const createUrlEncodedBody = (plainBodyObject) => {
20
15
  const encodedBody = Object.entries(plainBodyObject)
21
16
  .map(([key, value]) => {
@@ -26,24 +21,24 @@ const createUrlEncodedBody = (plainBodyObject) => {
26
21
  };
27
22
  // Environment servisi
28
23
  exports.KeycloakService = {
29
- ServiceUrl: KEYCLOAK.URL,
30
- Token: function (options) {
31
- return __awaiter(this, void 0, void 0, function* () {
24
+ ServiceUrl: "",
25
+ Token: function (_a, options_1) {
26
+ return __awaiter(this, arguments, void 0, function* ({ realm, client_id, client_secret, grant_type }, options) {
32
27
  // request
33
28
  const encodedBody = createUrlEncodedBody({
34
- realm: KEYCLOAK.REALM,
35
- client_id: KEYCLOAK.CLIENT_ID,
36
- client_secret: KEYCLOAK.CLIENT_SECRET,
37
- grant_type: KEYCLOAK.GRANT_TYPE,
29
+ realm,
30
+ client_id,
31
+ client_secret,
32
+ grant_type,
38
33
  });
39
- return yield (0, __1.fetchRequest)().post(`${this.ServiceUrl}/realms/${KEYCLOAK.REALM}/protocol/openid-connect/token`, Object.assign(Object.assign({}, options), { headers: {
34
+ return yield (0, __1.fetchRequest)().post(`${this.ServiceUrl}/realms/${realm}/protocol/openid-connect/token`, Object.assign(Object.assign({}, options), { headers: {
40
35
  "Content-Type": "application/x-www-form-urlencoded",
41
36
  }, body: encodedBody }));
42
37
  });
43
38
  },
44
- GetConfigurations: function () {
45
- return __awaiter(this, void 0, void 0, function* () {
46
- return yield (0, __1.fetchRequest)().get(`${this.ServiceUrl}/realms/${KEYCLOAK.REALM}`, {
39
+ GetConfigurations: function (_a) {
40
+ return __awaiter(this, arguments, void 0, function* ({ realm }) {
41
+ return yield (0, __1.fetchRequest)().get(`${this.ServiceUrl}/realms/${realm}`, {
47
42
  headers: {
48
43
  "Content-Type": "application/json",
49
44
  },
@@ -8,6 +8,8 @@ export interface IKeycloakTokenRequest {
8
8
  }
9
9
  export interface IKeycloakService {
10
10
  ServiceUrl: string;
11
- Token: (options?: IRequestInit) => Promise<IApiResponse<IKeycloakTokenResponse>>;
12
- GetConfigurations: () => Promise<IApiResponse<IKeycloakConfigurationsResponse>>;
11
+ Token: ({ realm, client_id, client_secret, grant_type }: IKeycloakTokenRequest, options?: IRequestInit) => Promise<IApiResponse<IKeycloakTokenResponse>>;
12
+ GetConfigurations: ({ realm, }: {
13
+ realm: string;
14
+ }) => Promise<IApiResponse<IKeycloakConfigurationsResponse>>;
13
15
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nextemos",
3
- "version": "6.3.0",
3
+ "version": "6.4.0",
4
4
  "description": "For helpers and hooks used in NextJS projects",
5
5
  "main": "dist/index.js",
6
6
  "types": "./dist/index.d.ts",