@c-rex/services 0.1.1 → 0.1.3

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/index.mjs CHANGED
@@ -9,14 +9,21 @@ var API = {
9
9
  "content-Type": "application/json"
10
10
  }
11
11
  };
12
- var SDK_CONFIG_KEY = "crex-sdk-config";
13
12
  var FLAGS_BY_LANG = {
14
13
  "en": "US",
15
14
  "de": "DE"
16
15
  };
17
-
18
- // ../core/src/requests.ts
19
- import { Issuer } from "openid-client";
16
+ var EN_LANG = "en";
17
+ var TOPIC = "TOPIC";
18
+ var DOCUMENT = "DOCUMENT";
19
+ var PACKAGE = "PACKAGE";
20
+ var RESULT_TYPES = {
21
+ TOPIC,
22
+ DOCUMENT,
23
+ PACKAGE
24
+ };
25
+ var DEFAULT_COOKIE_LIMIT = 7 * 24 * 60 * 60 * 1e3;
26
+ var CREX_TOKEN_HEADER_KEY = "crex-token";
20
27
 
21
28
  // ../utils/src/utils.ts
22
29
  var call = async (method, params) => {
@@ -24,7 +31,8 @@ var call = async (method, params) => {
24
31
  const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/rpc`, {
25
32
  method: "POST",
26
33
  headers: { "Content-Type": "application/json" },
27
- body: JSON.stringify({ method, params })
34
+ body: JSON.stringify({ method, params }),
35
+ credentials: "include"
28
36
  });
29
37
  const json = await res.json();
30
38
  if (!res.ok) throw new Error(json.error || "Unknown error");
@@ -44,23 +52,35 @@ var getCountryCodeByLang = (lang) => {
44
52
  };
45
53
 
46
54
  // ../utils/src/memory.ts
47
- function isBrowser() {
48
- return typeof window !== "undefined" && typeof document !== "undefined";
49
- }
50
- function saveInMemory(value, key) {
51
- if (isBrowser()) throw new Error("saveInMemory is not supported in browser");
52
- if (typeof global !== "undefined" && !(key in global)) {
53
- global[key] = null;
55
+ var getCookie = async (key) => {
56
+ const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/cookies?key=${key}`);
57
+ if (!res.ok) {
58
+ return { key, value: null };
54
59
  }
55
- const globalConfig = global[key];
56
- if (globalConfig === null) {
57
- global[key] = value;
60
+ const json = await res.json();
61
+ return json;
62
+ };
63
+ var setCookie = async (key, value, maxAge) => {
64
+ try {
65
+ if (maxAge === void 0) {
66
+ maxAge = DEFAULT_COOKIE_LIMIT;
67
+ }
68
+ await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/cookies`, {
69
+ method: "POST",
70
+ credentials: "include",
71
+ body: JSON.stringify({
72
+ key,
73
+ value,
74
+ maxAge
75
+ })
76
+ });
77
+ } catch (error) {
78
+ call("CrexLogger.log", {
79
+ level: "error",
80
+ message: `utils.setCookie error: ${error}`
81
+ });
58
82
  }
59
- }
60
- function getFromMemory(key) {
61
- if (isBrowser()) throw new Error("getFromMemory is not supported in browser");
62
- return global[key];
63
- }
83
+ };
64
84
 
65
85
  // ../utils/src/classMerge.ts
66
86
  import { clsx } from "clsx";
@@ -78,68 +98,153 @@ var generateQueryParams = (params) => {
78
98
  return queryParams;
79
99
  };
80
100
 
81
- // ../core/src/requests.ts
82
- import { getCookie } from "@c-rex/utils/next-cookies";
83
- var CREX_TOKEN_HEADER_KEY = "crex-token";
84
- var CREX_TOKEN_EXPIRY_HEADER_KEY = "crex-token-expiry";
85
- var CrexApi = class {
86
- customerConfig;
87
- apiClient;
88
- async manageToken() {
89
- const headersAux = {};
90
- if (this.customerConfig.OIDC.client.enabled) {
91
- let token = getFromMemory(CREX_TOKEN_HEADER_KEY);
92
- let tokenExpiry = getFromMemory(CREX_TOKEN_EXPIRY_HEADER_KEY);
93
- const now = Math.floor(Date.now() / 1e3);
94
- if (!(token && tokenExpiry > now + 60)) {
95
- const { token: tokenAux, tokenExpiry: tokenExpiryAux } = await this.getToken();
96
- token = tokenAux;
97
- saveInMemory(token, CREX_TOKEN_HEADER_KEY);
98
- saveInMemory(tokenExpiryAux, CREX_TOKEN_EXPIRY_HEADER_KEY);
99
- }
100
- headersAux["Authorization"] = `Bearer ${token}`;
101
+ // ../utils/src/token.ts
102
+ var updateToken = async () => {
103
+ try {
104
+ const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/token`, {
105
+ method: "POST",
106
+ credentials: "include"
107
+ });
108
+ const cookies = response.headers.get("set-cookie");
109
+ if (cookies === null) return null;
110
+ const aux = cookies.split(`${CREX_TOKEN_HEADER_KEY}=`);
111
+ if (aux.length == 0) return null;
112
+ const token = aux[1].split(";")[0];
113
+ if (token === void 0) throw new Error("Token is undefined");
114
+ return token;
115
+ } catch (error) {
116
+ call("CrexLogger.log", {
117
+ level: "error",
118
+ message: `utils.updateToken error: ${error}`
119
+ });
120
+ return null;
121
+ }
122
+ };
123
+ var manageToken = async () => {
124
+ try {
125
+ const hasToken = await getCookie(CREX_TOKEN_HEADER_KEY);
126
+ let token = hasToken.value;
127
+ if (hasToken.value === null) {
128
+ const tokenResult = await updateToken();
129
+ if (tokenResult === null) throw new Error("Token is undefined");
130
+ token = tokenResult;
101
131
  }
102
- return headersAux;
132
+ return token;
133
+ } catch (error) {
134
+ call("CrexLogger.log", {
135
+ level: "error",
136
+ message: `utils.manageToken error: ${error}`
137
+ });
138
+ return null;
139
+ }
140
+ };
141
+
142
+ // ../core/src/requests.ts
143
+ import { getConfigs } from "@c-rex/utils/next-cookies";
144
+
145
+ // ../core/src/cache.ts
146
+ var CrexCache = class {
147
+ /**
148
+ * Retrieves a value from the cache by key.
149
+ *
150
+ * @param key - The cache key to retrieve
151
+ * @returns The cached value as a string, or null if not found
152
+ */
153
+ async get(key) {
154
+ const cookie = await getCookie(key);
155
+ return cookie.value;
103
156
  }
104
- async getToken() {
105
- let token = "";
106
- let tokenExpiry = 0;
157
+ /**
158
+ * Stores a value in the cache with the specified key.
159
+ * Checks if the value size is within cookie size limits (4KB).
160
+ *
161
+ * @param key - The cache key
162
+ * @param value - The value to store
163
+ */
164
+ async set(key, value) {
107
165
  try {
108
- const now = Math.floor(Date.now() / 1e3);
109
- const issuer = await Issuer.discover(this.customerConfig.OIDC.client.issuer);
110
- const client = new issuer.Client({
111
- client_id: this.customerConfig.OIDC.client.id,
112
- client_secret: this.customerConfig.OIDC.client.secret
113
- });
114
- const tokenSet = await client.grant({ grant_type: "client_credentials" });
115
- token = tokenSet.access_token;
116
- tokenExpiry = now + tokenSet.expires_at;
117
- console.log("token", token);
166
+ const byteLength = new TextEncoder().encode(value).length;
167
+ if (byteLength <= 4096) {
168
+ await setCookie(key, value);
169
+ } else {
170
+ console.warn(`Cookie ${key} value is too large to be stored in a single cookie`);
171
+ }
118
172
  } catch (error) {
119
173
  call("CrexLogger.log", {
120
174
  level: "error",
121
- message: `API.getToken error when request ${this.customerConfig.OIDC.client.issuer}. Error: ${error}`
175
+ message: `CrexCache.set error: ${error}`
122
176
  });
123
177
  }
124
- return {
125
- token,
126
- tokenExpiry
127
- };
128
178
  }
179
+ /**
180
+ * Generates a cache key based on request parameters.
181
+ * Combines URL, method, and optionally params, body, and headers into a single string key.
182
+ *
183
+ * @param options - Request options to generate key from
184
+ * @param options.url - The request URL
185
+ * @param options.method - The HTTP method
186
+ * @param options.body - Optional request body
187
+ * @param options.params - Optional query parameters
188
+ * @param options.headers - Optional request headers
189
+ * @returns A string cache key
190
+ */
191
+ generateCacheKey({
192
+ url,
193
+ method,
194
+ body,
195
+ params,
196
+ headers
197
+ }) {
198
+ let cacheKey = `${url}-${method}`;
199
+ if (params !== void 0 && Object.keys(params).length > 0) {
200
+ cacheKey += `-${JSON.stringify(params)}`;
201
+ }
202
+ if (body !== void 0 && Object.keys(body).length > 0) {
203
+ cacheKey += `-${JSON.stringify(body)}`;
204
+ }
205
+ if (headers !== void 0 && Object.keys(headers).length > 0) {
206
+ cacheKey += `-${JSON.stringify(headers)}`;
207
+ }
208
+ return cacheKey;
209
+ }
210
+ };
211
+
212
+ // ../core/src/requests.ts
213
+ var CrexApi = class {
214
+ customerConfig;
215
+ apiClient;
216
+ cache;
217
+ /**
218
+ * Initializes the API client if it hasn't been initialized yet.
219
+ * Loads customer configuration, creates the axios instance, and initializes the cache.
220
+ *
221
+ * @private
222
+ */
129
223
  async initAPI() {
130
224
  if (!this.customerConfig) {
131
- const jsonConfigs = await getCookie(SDK_CONFIG_KEY);
132
- if (!jsonConfigs) {
133
- throw new Error("SDK not initialized");
134
- }
135
- this.customerConfig = JSON.parse(jsonConfigs);
225
+ this.customerConfig = await getConfigs();
136
226
  }
137
227
  if (!this.apiClient) {
138
228
  this.apiClient = axios.create({
139
229
  baseURL: this.customerConfig.baseUrl
140
230
  });
141
231
  }
232
+ if (!this.cache) {
233
+ this.cache = new CrexCache();
234
+ }
142
235
  }
236
+ /**
237
+ * Executes an API request with caching, authentication, and retry logic.
238
+ *
239
+ * @param options - Request options
240
+ * @param options.url - The URL to request
241
+ * @param options.method - The HTTP method to use
242
+ * @param options.params - Optional query parameters
243
+ * @param options.body - Optional request body
244
+ * @param options.headers - Optional request headers
245
+ * @returns The response data
246
+ * @throws Error if the request fails after maximum retries
247
+ */
143
248
  async execute({
144
249
  url,
145
250
  method,
@@ -149,10 +254,14 @@ var CrexApi = class {
149
254
  }) {
150
255
  await this.initAPI();
151
256
  let response = void 0;
152
- headers = {
153
- ...headers,
154
- ...await this.manageToken()
155
- };
257
+ if (this.customerConfig.OIDC.client.enabled) {
258
+ const token = await manageToken();
259
+ headers = {
260
+ ...headers,
261
+ Authorization: `Bearer ${token}`
262
+ };
263
+ this.apiClient.defaults.headers.common["Authorization"] = `Bearer ${token}`;
264
+ }
156
265
  for (let retry = 0; retry < API.MAX_RETRY; retry++) {
157
266
  try {
158
267
  response = await this.apiClient.request({
@@ -164,10 +273,10 @@ var CrexApi = class {
164
273
  });
165
274
  break;
166
275
  } catch (error) {
167
- console.log(
168
- "error",
169
- `API.execute ${retry + 1}\xBA error when request ${url}. Error: ${error}`
170
- );
276
+ call("CrexLogger.log", {
277
+ level: "error",
278
+ message: `API.execute ${retry + 1}\xBA error when request ${url}. Error: ${error}`
279
+ });
171
280
  if (retry === API.MAX_RETRY - 1) {
172
281
  throw error;
173
282
  }
@@ -184,10 +293,26 @@ var CrexApi = class {
184
293
  var BaseService = class {
185
294
  api;
186
295
  endpoint;
296
+ /**
297
+ * Creates a new instance of BaseService.
298
+ *
299
+ * @param endpoint - The API endpoint URL for this service
300
+ */
187
301
  constructor(endpoint) {
188
302
  this.api = new CrexApi();
189
303
  this.endpoint = endpoint;
190
304
  }
305
+ /**
306
+ * Makes an API request to the specified endpoint.
307
+ *
308
+ * @param options - Request configuration options
309
+ * @param options.path - Optional path to append to the endpoint
310
+ * @param options.params - Optional query parameters to include in the request
311
+ * @param options.method - HTTP method to use (defaults to 'get')
312
+ * @param options.transformer - Optional function to transform the response data
313
+ * @returns The response data, optionally transformed
314
+ * @throws Error if the API request fails
315
+ */
191
316
  async request({
192
317
  path = "",
193
318
  method = "get",
@@ -222,7 +347,15 @@ var RenditionsService = class extends BaseService {
222
347
  constructor() {
223
348
  super("Renditions/");
224
349
  }
225
- async getHTMLRendition(renditions) {
350
+ /**
351
+ * Retrieves the HTML rendition from a list of renditions.
352
+ * Filters for renditions with format 'application/xhtml+xml' and rel 'view'.
353
+ *
354
+ * @param renditions - Array of rendition objects to process
355
+ * @returns A promise that resolves to the HTML content as a string, or empty string if no suitable rendition is found
356
+ * @throws Error if the API request fails
357
+ */
358
+ async getHTMLRendition({ renditions }) {
226
359
  const filteredRenditions = renditions.filter(
227
360
  (item2) => item2.format == "application/xhtml+xml"
228
361
  );
@@ -240,17 +373,48 @@ var RenditionsService = class extends BaseService {
240
373
  });
241
374
  return response;
242
375
  }
243
- async getDocumentRendition(renditions, type) {
376
+ /**
377
+ * Processes a list of renditions and categorizes them into files to download and files to open.
378
+ * Excludes renditions with formats 'application/xhtml+xml', 'application/json', and 'application/llm+xml'.
379
+ *
380
+ * @param renditions - Array of rendition objects to process
381
+ * @returns An object containing arrays of file renditions categorized as 'filesToDownload' and 'filesToOpen'
382
+ */
383
+ getFileRenditions = ({ renditions }) => {
384
+ if (renditions == void 0 || renditions.length == 0) {
385
+ return {
386
+ filesToDownload: [],
387
+ filesToOpen: []
388
+ };
389
+ }
244
390
  const filteredRenditions = renditions.filter(
245
- (item2) => item2.format == `application/${type}`
391
+ (item) => item.format != "application/xhtml+xml" && item.format != "application/json" && item.format != "application/llm+xml"
246
392
  );
247
- if (filteredRenditions.length == 0 || filteredRenditions[0] == void 0) return "";
248
- const item = filteredRenditions[0];
249
- const filteredLinks = item.links.filter((item2) => item2.rel == "view");
250
- if (filteredLinks.length == 0 || filteredLinks[0] == void 0) return "";
251
- const url = filteredLinks[0].href;
252
- return url;
253
- }
393
+ if (filteredRenditions.length == 0 || filteredRenditions[0] == void 0) {
394
+ return {
395
+ filesToDownload: [],
396
+ filesToOpen: []
397
+ };
398
+ }
399
+ const filesToDownload = filteredRenditions.map((item) => {
400
+ const filteredLinks = item.links.filter((item2) => item2.rel == "download");
401
+ return {
402
+ format: item.format,
403
+ link: filteredLinks[0].href
404
+ };
405
+ });
406
+ const filesToOpen = filteredRenditions.map((item) => {
407
+ const filteredLinks = item.links.filter((item2) => item2.rel == "view");
408
+ return {
409
+ format: item.format,
410
+ link: filteredLinks[0].href
411
+ };
412
+ });
413
+ return {
414
+ filesToDownload,
415
+ filesToOpen
416
+ };
417
+ };
254
418
  };
255
419
 
256
420
  // src/directoryNodes.ts
@@ -258,11 +422,36 @@ var DirectoryNodesService = class extends BaseService {
258
422
  constructor() {
259
423
  super("DirectoryNodes/");
260
424
  }
425
+ /**
426
+ * Retrieves a specific directory node by its ID.
427
+ *
428
+ * @param id - The unique identifier of the directory node
429
+ * @returns A promise that resolves to the directory node data
430
+ * @throws Error if the API request fails
431
+ */
261
432
  async getItem(id) {
262
433
  return await this.request({
263
434
  path: id
264
435
  });
265
436
  }
437
+ /**
438
+ * Retrieves a list of directory nodes based on specified filters.
439
+ *
440
+ * @param options - Options for filtering the directory nodes list
441
+ * @param options.filters - Optional array of filter strings to apply
442
+ * @returns A promise that resolves to the directory nodes response
443
+ * @throws Error if the API request fails
444
+ */
445
+ async getList({
446
+ filters = []
447
+ }) {
448
+ const remainFilters = createParams(filters, "Filter");
449
+ return await this.request({
450
+ params: {
451
+ ...remainFilters
452
+ }
453
+ });
454
+ }
266
455
  };
267
456
 
268
457
  // src/transforms/documentTypes.ts
@@ -282,6 +471,14 @@ var DocumentTypesService = class extends BaseService {
282
471
  constructor() {
283
472
  super("DocumentTypes/");
284
473
  }
474
+ /**
475
+ * Retrieves document type labels for the specified fields.
476
+ * The labels are restricted to English language (EN-us).
477
+ *
478
+ * @param fields - Array of field names to retrieve labels for
479
+ * @returns A promise that resolves to an array of label strings
480
+ * @throws Error if the API request fails
481
+ */
285
482
  async getLabels(fields) {
286
483
  const params = [
287
484
  {
@@ -298,9 +495,33 @@ var DocumentTypesService = class extends BaseService {
298
495
  };
299
496
 
300
497
  // src/transforms/information.ts
301
- var transformInformationUnits = (data) => {
498
+ import { getConfigs as getConfigs2 } from "@c-rex/utils/next-cookies";
499
+ var transformInformationUnits = async (data) => {
500
+ const config = getConfigs2();
501
+ const items = await Promise.all(data.items.map(async (item) => {
502
+ const type = item.class.labels.filter((item2) => item2.language === EN_LANG)[0].value.toUpperCase();
503
+ const service = new RenditionsService();
504
+ const { filesToOpen, filesToDownload } = service.getFileRenditions({ renditions: item?.renditions });
505
+ let link = `/topics/${item.shortId}`;
506
+ if (config.results.articlePageLayout == "BLOG") {
507
+ link = `/blog/${item.shortId}`;
508
+ } else if (type == RESULT_TYPES.DOCUMENT) {
509
+ link = `/documents/${item.shortId}`;
510
+ }
511
+ return {
512
+ language: item.labels[0].language,
513
+ title: item.labels[0].value,
514
+ type,
515
+ localeType: "",
516
+ shortId: item.shortId,
517
+ disabled: config.results.disabledResults.includes(type),
518
+ link,
519
+ filesToOpen,
520
+ filesToDownload
521
+ };
522
+ }));
302
523
  return {
303
- items: data.items.map((item) => item),
524
+ items,
304
525
  pageInfo: data.pageInfo
305
526
  };
306
527
  };
@@ -310,6 +531,18 @@ var InformationUnitsService = class extends BaseService {
310
531
  constructor() {
311
532
  super("InformationUnits/");
312
533
  }
534
+ /**
535
+ * Retrieves a list of information units based on specified criteria.
536
+ *
537
+ * @param options - Options for filtering and paginating the information units list
538
+ * @param options.queries - Optional search query string
539
+ * @param options.page - Optional page number for pagination (defaults to 1)
540
+ * @param options.fields - Optional array of fields to include in the response
541
+ * @param options.filters - Optional array of filter strings to apply
542
+ * @param options.languages - Optional array of language codes to filter by
543
+ * @returns A promise that resolves to the information units response
544
+ * @throws Error if the API request fails
545
+ */
313
546
  async getList({
314
547
  queries = "",
315
548
  page = 1,
@@ -324,8 +557,8 @@ var InformationUnitsService = class extends BaseService {
324
557
  "Filter"
325
558
  );
326
559
  const params = [
327
- { key: "pageSize", value: "9" },
328
- { key: "PageNumber", value: (page - 1).toString() },
560
+ { key: "pageSize", value: "12" },
561
+ { key: "PageNumber", value: page.toString() },
329
562
  ...remainFields,
330
563
  ...languageParams,
331
564
  ...remainFilters
@@ -340,42 +573,71 @@ var InformationUnitsService = class extends BaseService {
340
573
  transformer: transformInformationUnits
341
574
  });
342
575
  }
576
+ /**
577
+ * Retrieves a specific information unit by its ID.
578
+ * Includes renditions, directory nodes, version information, titles, languages, and labels.
579
+ *
580
+ * @param options - Options for retrieving the information unit
581
+ * @param options.id - The unique identifier of the information unit
582
+ * @returns A promise that resolves to the information unit data
583
+ * @throws Error if the API request fails
584
+ */
343
585
  async getItem({ id }) {
344
586
  const params = [
345
587
  { key: "Fields", value: "renditions" },
346
588
  { key: "Fields", value: "directoryNodes" },
347
589
  { key: "Fields", value: "versionOf" },
348
- { key: "Fields", value: "titles" }
590
+ { key: "Fields", value: "titles" },
591
+ { key: "Fields", value: "languages" },
592
+ { key: "Fields", value: "labels" }
349
593
  ];
350
594
  return await this.request({
351
595
  path: id,
352
596
  params
353
597
  });
354
598
  }
355
- async getSuggestions({ query }) {
599
+ /**
600
+ * Retrieves autocomplete suggestions based on a query prefix.
601
+ *
602
+ * @param options - Options for retrieving suggestions
603
+ * @param options.query - The query prefix to get suggestions for
604
+ * @param options.language - The language of the suggestions
605
+ * @returns A promise that resolves to an array of suggestion strings
606
+ * @throws Error if the API request fails
607
+ */
608
+ async getSuggestions({ query, language }) {
356
609
  return await this.request({
357
610
  path: `Suggestions`,
358
- params: [{ key: "prefix", value: query }],
611
+ params: [
612
+ { key: "prefix", value: query },
613
+ { key: "lang", value: language }
614
+ ],
359
615
  transformer: (data) => {
360
- return data.suggestions.map((item) => item.value);
616
+ const suggestions = [];
617
+ const comparableList = [];
618
+ data.suggestions.forEach((item) => {
619
+ suggestions.push(item.value);
620
+ comparableList.push(item.value.toLowerCase());
621
+ });
622
+ if (!comparableList.includes(query.toLowerCase())) {
623
+ return [query, ...suggestions];
624
+ }
625
+ return suggestions;
361
626
  }
362
627
  });
363
628
  }
364
629
  };
365
630
 
366
631
  // src/language.ts
632
+ import { getConfigs as getConfigs3 } from "@c-rex/utils/next-cookies";
367
633
  var LanguageService = class extends BaseService {
368
- constructor(endpoint) {
369
- super(endpoint);
634
+ constructor() {
635
+ const configs = getConfigs3();
636
+ super(configs.languageSwitcher.endpoint);
370
637
  }
371
638
  /*
372
639
  public static async getInstance(): Promise<LanguageService> {
373
- const jsonConfigs = await getCookie(SDK_CONFIG_KEY);
374
- if (!jsonConfigs) {
375
- throw new Error("SDK not initialized");
376
- }
377
-
378
- const customerConfig = JSON.parse(jsonConfigs) as ConfigInterface;
640
+ const customerConfig = await getConfigs()
379
641
 
380
642
  if (!LanguageService.instance) {
381
643
  LanguageService.instance = new LanguageService(customerConfig.languageSwitcher.endpoint);
@@ -383,6 +645,13 @@ var LanguageService = class extends BaseService {
383
645
  return LanguageService.instance;
384
646
  }
385
647
  */
648
+ /**
649
+ * Retrieves a list of available languages and their associated countries.
650
+ * Transforms the API response to include language code, country code, and original value.
651
+ *
652
+ * @returns A promise that resolves to an array of language and country objects
653
+ * @throws Error if the API request fails
654
+ */
386
655
  async getLanguagesAndCountries() {
387
656
  return await this.request({
388
657
  transformer: (data) => {