@sdcorejs/angular 20.0.9 → 20.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. package/README.md +270 -140
  2. package/assets/scss/core/utilities/_grid.scss +25 -19
  3. package/components/document-builder/index.d.ts +2 -2
  4. package/components/form-generic/index.d.ts +45 -23
  5. package/components/import-excel/index.d.ts +2 -2
  6. package/components/mini-editor/index.d.ts +4 -4
  7. package/components/modal/index.d.ts +1 -1
  8. package/components/query-builder/index.d.ts +4 -4
  9. package/components/tab-router/index.d.ts +3 -3
  10. package/components/table/index.d.ts +3 -2
  11. package/components/view/index.d.ts +1 -1
  12. package/fesm2022/sdcorejs-angular-components-anchor.mjs +15 -18
  13. package/fesm2022/sdcorejs-angular-components-anchor.mjs.map +1 -1
  14. package/fesm2022/sdcorejs-angular-components-autoid-inspector.mjs.map +1 -1
  15. package/fesm2022/sdcorejs-angular-components-avatar.mjs.map +1 -1
  16. package/fesm2022/sdcorejs-angular-components-badge.mjs +0 -1
  17. package/fesm2022/sdcorejs-angular-components-badge.mjs.map +1 -1
  18. package/fesm2022/sdcorejs-angular-components-button.mjs +11 -12
  19. package/fesm2022/sdcorejs-angular-components-button.mjs.map +1 -1
  20. package/fesm2022/sdcorejs-angular-components-chart.mjs.map +1 -1
  21. package/fesm2022/sdcorejs-angular-components-ckeditor-styles.mjs.map +1 -1
  22. package/fesm2022/sdcorejs-angular-components-code-editor.mjs +1 -1
  23. package/fesm2022/sdcorejs-angular-components-code-editor.mjs.map +1 -1
  24. package/fesm2022/sdcorejs-angular-components-document-builder.mjs +17 -26
  25. package/fesm2022/sdcorejs-angular-components-document-builder.mjs.map +1 -1
  26. package/fesm2022/sdcorejs-angular-components-editor.mjs +7 -2
  27. package/fesm2022/sdcorejs-angular-components-editor.mjs.map +1 -1
  28. package/fesm2022/sdcorejs-angular-components-form-generic.mjs +712 -427
  29. package/fesm2022/sdcorejs-angular-components-form-generic.mjs.map +1 -1
  30. package/fesm2022/sdcorejs-angular-components-import-excel.mjs +35 -18
  31. package/fesm2022/sdcorejs-angular-components-import-excel.mjs.map +1 -1
  32. package/fesm2022/sdcorejs-angular-components-inform.mjs +0 -1
  33. package/fesm2022/sdcorejs-angular-components-inform.mjs.map +1 -1
  34. package/fesm2022/sdcorejs-angular-components-mini-editor.mjs.map +1 -1
  35. package/fesm2022/sdcorejs-angular-components-modal-resizable.mjs.map +1 -1
  36. package/fesm2022/sdcorejs-angular-components-modal.mjs +25 -14
  37. package/fesm2022/sdcorejs-angular-components-modal.mjs.map +1 -1
  38. package/fesm2022/sdcorejs-angular-components-operator.mjs +1 -1
  39. package/fesm2022/sdcorejs-angular-components-operator.mjs.map +1 -1
  40. package/fesm2022/sdcorejs-angular-components-org-chart.mjs.map +1 -1
  41. package/fesm2022/sdcorejs-angular-components-preview.mjs +33 -29
  42. package/fesm2022/sdcorejs-angular-components-preview.mjs.map +1 -1
  43. package/fesm2022/sdcorejs-angular-components-query-bar.mjs +15 -28
  44. package/fesm2022/sdcorejs-angular-components-query-bar.mjs.map +1 -1
  45. package/fesm2022/sdcorejs-angular-components-query-builder.mjs +2 -3
  46. package/fesm2022/sdcorejs-angular-components-query-builder.mjs.map +1 -1
  47. package/fesm2022/sdcorejs-angular-components-quick-action.mjs.map +1 -1
  48. package/fesm2022/sdcorejs-angular-components-section.mjs +0 -1
  49. package/fesm2022/sdcorejs-angular-components-section.mjs.map +1 -1
  50. package/fesm2022/sdcorejs-angular-components-side-drawer.mjs +3 -3
  51. package/fesm2022/sdcorejs-angular-components-side-drawer.mjs.map +1 -1
  52. package/fesm2022/sdcorejs-angular-components-splitter.mjs +5 -5
  53. package/fesm2022/sdcorejs-angular-components-splitter.mjs.map +1 -1
  54. package/fesm2022/sdcorejs-angular-components-stepper.mjs.map +1 -1
  55. package/fesm2022/sdcorejs-angular-components-tab-router.mjs +31 -31
  56. package/fesm2022/sdcorejs-angular-components-tab-router.mjs.map +1 -1
  57. package/fesm2022/sdcorejs-angular-components-tab.mjs.map +1 -1
  58. package/fesm2022/sdcorejs-angular-components-table.mjs +66 -84
  59. package/fesm2022/sdcorejs-angular-components-table.mjs.map +1 -1
  60. package/fesm2022/sdcorejs-angular-components-tree.mjs.map +1 -1
  61. package/fesm2022/sdcorejs-angular-components-upload-file.mjs +6 -6
  62. package/fesm2022/sdcorejs-angular-components-upload-file.mjs.map +1 -1
  63. package/fesm2022/sdcorejs-angular-components-view.mjs +3 -9
  64. package/fesm2022/sdcorejs-angular-components-view.mjs.map +1 -1
  65. package/fesm2022/sdcorejs-angular-configurations.mjs.map +1 -1
  66. package/fesm2022/sdcorejs-angular-directives.mjs +2 -7
  67. package/fesm2022/sdcorejs-angular-directives.mjs.map +1 -1
  68. package/fesm2022/sdcorejs-angular-forms-autocomplete.mjs +0 -3
  69. package/fesm2022/sdcorejs-angular-forms-autocomplete.mjs.map +1 -1
  70. package/fesm2022/sdcorejs-angular-forms-checkbox.mjs.map +1 -1
  71. package/fesm2022/sdcorejs-angular-forms-chip-calendar.mjs +2 -2
  72. package/fesm2022/sdcorejs-angular-forms-chip-calendar.mjs.map +1 -1
  73. package/fesm2022/sdcorejs-angular-forms-chip.mjs +2 -2
  74. package/fesm2022/sdcorejs-angular-forms-chip.mjs.map +1 -1
  75. package/fesm2022/sdcorejs-angular-forms-date-range.mjs +1 -3
  76. package/fesm2022/sdcorejs-angular-forms-date-range.mjs.map +1 -1
  77. package/fesm2022/sdcorejs-angular-forms-date.mjs +2 -4
  78. package/fesm2022/sdcorejs-angular-forms-date.mjs.map +1 -1
  79. package/fesm2022/sdcorejs-angular-forms-datetime.mjs +2 -6
  80. package/fesm2022/sdcorejs-angular-forms-datetime.mjs.map +1 -1
  81. package/fesm2022/sdcorejs-angular-forms-input-color.mjs +4 -4
  82. package/fesm2022/sdcorejs-angular-forms-input-color.mjs.map +1 -1
  83. package/fesm2022/sdcorejs-angular-forms-input-number.mjs +1 -1
  84. package/fesm2022/sdcorejs-angular-forms-input-number.mjs.map +1 -1
  85. package/fesm2022/sdcorejs-angular-forms-input.mjs.map +1 -1
  86. package/fesm2022/sdcorejs-angular-forms-label.mjs +0 -1
  87. package/fesm2022/sdcorejs-angular-forms-label.mjs.map +1 -1
  88. package/fesm2022/sdcorejs-angular-forms-models.mjs.map +1 -1
  89. package/fesm2022/sdcorejs-angular-forms-radio.mjs +12 -2
  90. package/fesm2022/sdcorejs-angular-forms-radio.mjs.map +1 -1
  91. package/fesm2022/sdcorejs-angular-forms-select.mjs +45 -14
  92. package/fesm2022/sdcorejs-angular-forms-select.mjs.map +1 -1
  93. package/fesm2022/sdcorejs-angular-forms-switch.mjs.map +1 -1
  94. package/fesm2022/sdcorejs-angular-forms-textarea.mjs +3 -3
  95. package/fesm2022/sdcorejs-angular-forms-textarea.mjs.map +1 -1
  96. package/fesm2022/sdcorejs-angular-handlers.mjs +3 -6
  97. package/fesm2022/sdcorejs-angular-handlers.mjs.map +1 -1
  98. package/fesm2022/sdcorejs-angular-i18n.mjs +11 -7
  99. package/fesm2022/sdcorejs-angular-i18n.mjs.map +1 -1
  100. package/fesm2022/sdcorejs-angular-interceptors.mjs +0 -2
  101. package/fesm2022/sdcorejs-angular-interceptors.mjs.map +1 -1
  102. package/fesm2022/sdcorejs-angular-modules-auth.mjs +14 -38
  103. package/fesm2022/sdcorejs-angular-modules-auth.mjs.map +1 -1
  104. package/fesm2022/sdcorejs-angular-modules-authom.mjs +14 -14
  105. package/fesm2022/sdcorejs-angular-modules-authom.mjs.map +1 -1
  106. package/fesm2022/sdcorejs-angular-modules-keycloak.mjs +22 -11
  107. package/fesm2022/sdcorejs-angular-modules-keycloak.mjs.map +1 -1
  108. package/fesm2022/sdcorejs-angular-modules-layout.mjs +30 -27
  109. package/fesm2022/sdcorejs-angular-modules-layout.mjs.map +1 -1
  110. package/fesm2022/sdcorejs-angular-modules-permission.mjs +5 -12
  111. package/fesm2022/sdcorejs-angular-modules-permission.mjs.map +1 -1
  112. package/fesm2022/sdcorejs-angular-pipes.mjs +71 -17
  113. package/fesm2022/sdcorejs-angular-pipes.mjs.map +1 -1
  114. package/fesm2022/sdcorejs-angular-services-api.mjs +0 -2
  115. package/fesm2022/sdcorejs-angular-services-api.mjs.map +1 -1
  116. package/fesm2022/sdcorejs-angular-services-cache.mjs +5 -11
  117. package/fesm2022/sdcorejs-angular-services-cache.mjs.map +1 -1
  118. package/fesm2022/sdcorejs-angular-services-confirm.mjs +16 -21
  119. package/fesm2022/sdcorejs-angular-services-confirm.mjs.map +1 -1
  120. package/fesm2022/sdcorejs-angular-services-docx.mjs +2 -8
  121. package/fesm2022/sdcorejs-angular-services-docx.mjs.map +1 -1
  122. package/fesm2022/sdcorejs-angular-services-excel.mjs +2 -2
  123. package/fesm2022/sdcorejs-angular-services-excel.mjs.map +1 -1
  124. package/fesm2022/sdcorejs-angular-services-license.mjs +5 -12
  125. package/fesm2022/sdcorejs-angular-services-license.mjs.map +1 -1
  126. package/fesm2022/sdcorejs-angular-services-notify.mjs +70 -87
  127. package/fesm2022/sdcorejs-angular-services-notify.mjs.map +1 -1
  128. package/fesm2022/sdcorejs-angular-services-storage.mjs +6 -15
  129. package/fesm2022/sdcorejs-angular-services-storage.mjs.map +1 -1
  130. package/fesm2022/sdcorejs-angular-utilities-data-state.mjs.map +1 -1
  131. package/fesm2022/sdcorejs-angular-utilities-extensions.mjs +47 -5
  132. package/fesm2022/sdcorejs-angular-utilities-extensions.mjs.map +1 -1
  133. package/fesm2022/sdcorejs-angular-utilities-models.mjs +18 -3
  134. package/fesm2022/sdcorejs-angular-utilities-models.mjs.map +1 -1
  135. package/fesm2022/sdcorejs-angular.mjs.map +1 -1
  136. package/forms/chip/index.d.ts +2 -2
  137. package/forms/chip-calendar/index.d.ts +2 -2
  138. package/forms/input-color/index.d.ts +2 -1
  139. package/forms/select/index.d.ts +10 -2
  140. package/modules/auth/index.d.ts +7 -9
  141. package/modules/permission/index.d.ts +5 -6
  142. package/package.json +2 -2
  143. package/pipes/index.d.ts +22 -4
  144. package/services/confirm/index.d.ts +3 -3
  145. package/services/license/index.d.ts +2 -3
  146. package/services/notify/index.d.ts +4 -5
  147. package/services/storage/index.d.ts +2 -3
  148. package/utilities/extensions/index.d.ts +9 -1
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, inject, Injectable, TemplateRef, ViewContainerRef, input, effect, Directive, Inject } from '@angular/core';
2
+ import { InjectionToken, inject, Injectable, TemplateRef, ViewContainerRef, input, effect, Directive } from '@angular/core';
3
3
  import { SdCacheService } from '@sdcorejs/angular/services/cache';
4
4
  import { ArrayUtilities } from '@sdcorejs/angular/utilities/extensions';
5
5
  import { resolveMaybeAsync } from '@sdcorejs/utils/models';
@@ -198,12 +198,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
198
198
  }], ctorParameters: () => [], propDecorators: { sdPermission: [{ type: i0.Input, args: [{ isSignal: true, alias: "sdPermission", required: false }] }], sdPermissionKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "sdPermissionKey", required: false }] }] } });
199
199
 
200
200
  class SdPermissionGuard {
201
- configuration;
202
- permissionService;
203
- constructor(configuration, permissionService) {
204
- this.configuration = configuration;
205
- this.permissionService = permissionService;
206
- }
201
+ configuration = inject(SD_PERMISSION_CONFIGURATION);
202
+ permissionService = inject(SdPermissionService);
207
203
  #getConfigurations = () => {
208
204
  const config = this.configuration;
209
205
  if (!config) {
@@ -230,16 +226,13 @@ class SdPermissionGuard {
230
226
  onForbiden?.();
231
227
  return false;
232
228
  };
233
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdPermissionGuard, deps: [{ token: SD_PERMISSION_CONFIGURATION }, { token: SdPermissionService }], target: i0.ɵɵFactoryTarget.Injectable });
229
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdPermissionGuard, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
234
230
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdPermissionGuard, providedIn: 'root' });
235
231
  }
236
232
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdPermissionGuard, decorators: [{
237
233
  type: Injectable,
238
234
  args: [{ providedIn: 'root' }]
239
- }], ctorParameters: () => [{ type: undefined, decorators: [{
240
- type: Inject,
241
- args: [SD_PERMISSION_CONFIGURATION]
242
- }] }, { type: SdPermissionService }] });
235
+ }] });
243
236
 
244
237
  /**
245
238
  * Generated bundle index. Do not edit.
@@ -1 +1 @@
1
- {"version":3,"file":"sdcorejs-angular-modules-permission.mjs","sources":["../../../projects/sdcorejs-angular/modules/permission/src/configurations/permission.configuration.ts","../../../projects/sdcorejs-angular/modules/permission/src/services/permission.service.ts","../../../projects/sdcorejs-angular/modules/permission/src/directives/permission.directive.ts","../../../projects/sdcorejs-angular/modules/permission/src/guards/permission.guard.ts","../../../projects/sdcorejs-angular/modules/permission/sdcorejs-angular-modules-permission.ts"],"sourcesContent":["import { InjectionToken } from '@angular/core';\nimport { MaybeAsync } from '@sdcorejs/utils/models';\n\n/**\n * Cấu hình trung tâm cho module permission.\n *\n * Luồng hoạt động chính:\n * 1. `loadPermissions(key?)` ở service được gọi để lấy danh sách mã quyền theo từng `key` cấu hình.\n * 2. Guard/service đối chiếu mã quyền theo route metadata.\n * 3. Khi không đủ quyền, callback `onForbiden()` sẽ được gọi (nếu có).\n *\n * Lưu ý:\n * - `SD_PERMISSION_CONFIGURATION` hỗ trợ cả cấu hình đơn và mảng cấu hình (`multi: true`).\n * - Nếu cần tạm bỏ qua kiểm tra quyền (POC/UAT cục bộ), đặt `disabled = true`.\n */\nexport interface ISdPermissionConfiguration {\n /**\n * Khóa định danh cấu hình.\n * Dùng để phân biệt khi hệ thống mở rộng theo nhiều profile permission.\n *\n * Lưu ý: `undefined` cũng được xem là một key hợp lệ (cấu hình mặc định).\n */\n key?: string;\n\n /**\n * Bật/tắt kiểm tra permission toàn cục.\n * - `true`: bỏ qua kiểm tra quyền.\n * - `false | undefined`: kiểm tra quyền theo cấu hình route.\n */\n disabled?: boolean;\n\n /**\n * Trả về danh sách mã quyền của user hiện tại.\n * Có thể đồng bộ hoặc bất đồng bộ.\n *\n * Ví dụ giá trị trả về:\n * - `['PRODUCT_C_EMPLOYEE_VIEW', 'PRODUCT_C_EMPLOYEE_UPDATE']`\n */\n loadPermissions: () => MaybeAsync<string[]>;\n\n /**\n * Callback xử lý khi user không có quyền truy cập URL hiện tại.\n * Thường dùng để điều hướng sang trang forbidden hoặc hiển thị thông báo.\n *\n * Giữ nguyên tên `onForbiden` để tương thích API hiện tại.\n */\n onForbiden?: () => void;\n\n /**\n * Cung cấp access token hiện tại cho các tác vụ liên quan permission.\n * Hỗ trợ trả về đồng bộ, Promise hoặc Observable.\n */\n getToken?: () => MaybeAsync<string | undefined | null>;\n}\n\n/**\n * InjectionToken cho cấu hình permission.\n *\n * Ví dụ provider:\n * {\n * provide: SD_PERMISSION_CONFIGURATION,\n * useValue: {\n * disabled: false,\n * loadPermissions: () => ['SAMPLE_C_EMPLOYEE_VIEW'],\n * onForbiden: () => router.navigateByUrl('/layout/forbidden')\n * }\n * }\n */\nexport const SD_PERMISSION_CONFIGURATION =\n new InjectionToken<ISdPermissionConfiguration | ISdPermissionConfiguration[]>('sd-permission.configuration');\n","import { inject, Injectable } from '@angular/core';\nimport { SdCache, SdCacheService } from '@sdcorejs/angular/services/cache';\nimport { ArrayUtilities } from '@sdcorejs/angular/utilities/extensions';\nimport { ISdPermissionConfiguration, SD_PERMISSION_CONFIGURATION } from '../configurations';\nimport { MaybeAsync, resolveMaybeAsync } from '@sdcorejs/utils/models';\n\n@Injectable({ providedIn: 'root' })\nexport class SdPermissionService {\n #permissionMapByKey: Record<string, Record<string, boolean>> = {};\n readonly #configuration = inject<ISdPermissionConfiguration | ISdPermissionConfiguration[]>(SD_PERMISSION_CONFIGURATION);\n readonly #cacheService = inject(SdCacheService);\n #permissionsByKey: SdCache<Record<string, string[]>> = this.#cacheService.create<Record<string, string[]>>('212a51fa-38d5-43b2-bd46-922d85950ba3', {\n type: 'session',\n default: {},\n });\n readonly #loadedKeys = new Set<string>();\n\n constructor() {\n this.#validateDuplicateConfigKeys();\n }\n\n #getConfigurations = (): ISdPermissionConfiguration[] => {\n const config = this.#configuration;\n if (!config) {\n return [];\n }\n return Array.isArray(config) ? config : [config];\n };\n\n #normalizeKey = (key?: string): string => {\n return key === undefined ? '__undefined__' : key;\n };\n\n #validateDuplicateConfigKeys = (): void => {\n const seen = new Set<string>();\n for (const config of this.#getConfigurations()) {\n const normalizedKey = this.#normalizeKey(config.key);\n if (seen.has(normalizedKey)) {\n const keyLabel = config.key === undefined ? 'undefined' : config.key;\n throw new Error(`[Permission] Duplicate permission configuration key: ${keyLabel}`);\n }\n seen.add(normalizedKey);\n }\n };\n\n #getConfigurationByKey = (key?: string): ISdPermissionConfiguration | undefined => {\n return this.#getConfigurations().find(config => config.key === key);\n };\n\n #getEffectivePermissionKey = (key?: string): string | undefined => {\n if (this.#getConfigurationByKey(key)) {\n return key;\n }\n\n // Portal-level config uses key = undefined and acts as default fallback\n if (key !== undefined && this.#getConfigurationByKey(undefined)) {\n return undefined;\n }\n\n return key;\n };\n\n #setPermissionsForKey = (normalizedKey: string, permissions: string[]): void => {\n const distinctPermissions = ArrayUtilities.distinct(permissions || []);\n const current = this.#permissionsByKey.get() || {};\n this.#permissionsByKey.set({\n ...current,\n [normalizedKey]: distinctPermissions,\n });\n\n const permissionMap: Record<string, boolean> = {};\n distinctPermissions.forEach(permission => {\n permissionMap[permission] = true;\n });\n this.#permissionMapByKey[normalizedKey] = permissionMap;\n };\n\n loadPermissions = async (key?: string): Promise<string[]> => {\n const effectiveKey = this.#getEffectivePermissionKey(key);\n const normalizedKey = this.#normalizeKey(effectiveKey);\n if (this.#loadedKeys.has(normalizedKey)) {\n return this.#permissionsByKey.get()?.[normalizedKey] || [];\n }\n\n const configuration = this.#getConfigurationByKey(effectiveKey);\n if (!configuration) {\n this.#setPermissionsForKey(normalizedKey, []);\n this.#loadedKeys.add(normalizedKey);\n return [];\n }\n\n try {\n const permissions: string[] = await resolveMaybeAsync(configuration.loadPermissions());\n this.#setPermissionsForKey(normalizedKey, permissions || []);\n } catch (err) {\n console.error(err);\n this.#setPermissionsForKey(normalizedKey, []);\n } finally {\n this.#loadedKeys.add(normalizedKey);\n }\n\n return this.#permissionsByKey.get()?.[normalizedKey] || [];\n };\n\n loadAllPermissions = async (): Promise<void> => {\n const configurations = this.#getConfigurations();\n if (!configurations.length) {\n await this.loadPermissions(undefined);\n return;\n }\n\n await Promise.all(configurations.map(config => this.loadPermissions(config.key)));\n };\n\n hasPermission = (permission: string | string[], key?: string) => {\n if (!permission?.toString()) {\n return true;\n }\n\n const effectiveKey = this.#getEffectivePermissionKey(key);\n const configuration = this.#getConfigurationByKey(effectiveKey);\n if (configuration?.disabled) {\n return true;\n }\n\n const normalizedKey = this.#normalizeKey(effectiveKey);\n const permissionMap = this.#permissionMapByKey[normalizedKey] || {};\n const permissions = Array.isArray(permission) ? permission : [permission];\n return permissions.some(val => permissionMap[val]);\n };\n\n getToken = async (key?: string) => {\n const effectiveKey = this.#getEffectivePermissionKey(key);\n const getToken = this.#getConfigurationByKey(effectiveKey)?.getToken as (() => MaybeAsync<string | undefined | null>) | undefined;\n if (!getToken) {\n throw new Error('[Permission] Method getToken');\n }\n\n const token = await resolveMaybeAsync(getToken());\n if (token === '') {\n return undefined;\n }\n return token;\n };\n\n decodeToken = async <T>(key?: string): Promise<T | null> => {\n const token = await this.getToken(key);\n if (!token) {\n return null;\n }\n try {\n const payload = token.split('.')[1];\n const base64 = payload.replace(/-/g, '+').replace(/_/g, '/');\n const jsonPayload = decodeURIComponent(\n atob(base64)\n .split('')\n .map(c => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))\n .join('')\n );\n return JSON.parse(jsonPayload);\n } catch (error) {\n console.error('Invalid token', error);\n return null;\n }\n };\n}\n","import { Directive, effect, inject, input, TemplateRef, ViewContainerRef } from '@angular/core';\nimport { SdPermissionService } from '../services';\n\n@Directive({\n selector: '[sdPermission]',\n})\nexport class SdPermissionDirective {\n readonly #templateRef: TemplateRef<Record<string, never>> = inject(TemplateRef);\n readonly #viewContainerRef = inject(ViewContainerRef);\n readonly #permissionService = inject(SdPermissionService);\n\n // Nếu là mảng thì chỉ cần có 1 permission trong mảng đó xem như có quyền\n readonly sdPermission = input<string | string[] | undefined | null>(undefined);\n readonly sdPermissionKey = input<string | undefined>(undefined);\n\n constructor() {\n effect(() => {\n const permission = this.sdPermission();\n const permissionKey = this.sdPermissionKey();\n\n this.#viewContainerRef.clear();\n\n // Nếu không gắn permission thì render\n if (!permission?.toString()) {\n this.#viewContainerRef.createEmbeddedView(this.#templateRef);\n return;\n }\n\n // Kiểm tra permission theo key (nếu có)\n if (this.#permissionService.hasPermission(permission, permissionKey)) {\n this.#viewContainerRef.createEmbeddedView(this.#templateRef);\n }\n });\n }\n}\n","import { Inject, Injectable } from '@angular/core';\nimport { ActivatedRouteSnapshot, CanActivate, CanActivateChild, RouterStateSnapshot } from '@angular/router';\nimport { ISdPermissionConfiguration, SD_PERMISSION_CONFIGURATION } from '../configurations';\nimport { SdPermissionService } from '../services';\n\n@Injectable({ providedIn: 'root' })\nexport class SdPermissionGuard implements CanActivate, CanActivateChild {\n constructor(\n @Inject(SD_PERMISSION_CONFIGURATION)\n private configuration: ISdPermissionConfiguration | ISdPermissionConfiguration[],\n private permissionService: SdPermissionService\n ) {}\n\n #getConfigurations = (): ISdPermissionConfiguration[] => {\n const config = this.configuration;\n if (!config) {\n return [];\n }\n return Array.isArray(config) ? config : [config];\n };\n\n canActivate = async (next: ActivatedRouteSnapshot, state: RouterStateSnapshot) => {\n // Guard ở layer portal: preload toàn bộ permission theo tất cả key đã cấu hình\n await this.permissionService.loadAllPermissions().catch(console.error);\n return true;\n };\n\n canActivateChild = async (activatedRoute: ActivatedRouteSnapshot, state: RouterStateSnapshot) => {\n const permission = activatedRoute.data['permission'];\n const permissionKey = activatedRoute.data?.['permissionKey'] as string | undefined;\n if (this.permissionService.hasPermission(permission, permissionKey)) {\n return true;\n }\n\n const configurations = this.#getConfigurations();\n const onForbiden = configurations\n .filter(config => config.key === permissionKey || (permissionKey !== undefined && config.key === undefined))\n .map(config => config.onForbiden)\n .find(val => !!val);\n onForbiden?.();\n return false;\n };\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":["i1.SdPermissionService"],"mappings":"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"}
1
+ {"version":3,"file":"sdcorejs-angular-modules-permission.mjs","sources":["../../../projects/sdcorejs-angular/modules/permission/src/configurations/permission.configuration.ts","../../../projects/sdcorejs-angular/modules/permission/src/services/permission.service.ts","../../../projects/sdcorejs-angular/modules/permission/src/directives/permission.directive.ts","../../../projects/sdcorejs-angular/modules/permission/src/guards/permission.guard.ts","../../../projects/sdcorejs-angular/modules/permission/sdcorejs-angular-modules-permission.ts"],"sourcesContent":["import { InjectionToken } from '@angular/core';\nimport { MaybeAsync } from '@sdcorejs/utils/models';\n\n/**\n * Cấu hình trung tâm cho module permission.\n *\n * Luồng hoạt động chính:\n * 1. `loadPermissions(key?)` ở service được gọi để lấy danh sách mã quyền theo từng `key` cấu hình.\n * 2. Guard/service đối chiếu mã quyền theo route metadata.\n * 3. Khi không đủ quyền, callback `onForbiden()` sẽ được gọi (nếu có).\n *\n * Lưu ý:\n * - `SD_PERMISSION_CONFIGURATION` hỗ trợ cả cấu hình đơn và mảng cấu hình (`multi: true`).\n * - Nếu cần tạm bỏ qua kiểm tra quyền (POC/UAT cục bộ), đặt `disabled = true`.\n */\nexport interface ISdPermissionConfiguration {\n /**\n * Khóa định danh cấu hình.\n * Dùng để phân biệt khi hệ thống mở rộng theo nhiều profile permission.\n *\n * Lưu ý: `undefined` cũng được xem là một key hợp lệ (cấu hình mặc định).\n */\n key?: string;\n\n /**\n * Bật/tắt kiểm tra permission toàn cục.\n * - `true`: bỏ qua kiểm tra quyền.\n * - `false | undefined`: kiểm tra quyền theo cấu hình route.\n */\n disabled?: boolean;\n\n /**\n * Trả về danh sách mã quyền của user hiện tại.\n * Có thể đồng bộ hoặc bất đồng bộ.\n *\n * Ví dụ giá trị trả về:\n * - `['PRODUCT_C_EMPLOYEE_VIEW', 'PRODUCT_C_EMPLOYEE_UPDATE']`\n */\n loadPermissions: () => MaybeAsync<string[]>;\n\n /**\n * Callback xử lý khi user không có quyền truy cập URL hiện tại.\n * Thường dùng để điều hướng sang trang forbidden hoặc hiển thị thông báo.\n *\n * Giữ nguyên tên `onForbiden` để tương thích API hiện tại.\n */\n onForbiden?: () => void;\n\n /**\n * Cung cấp access token hiện tại cho các tác vụ liên quan permission.\n * Hỗ trợ trả về đồng bộ, Promise hoặc Observable.\n */\n getToken?: () => MaybeAsync<string | undefined | null>;\n}\n\n/**\n * InjectionToken cho cấu hình permission.\n *\n * Ví dụ provider:\n * {\n * provide: SD_PERMISSION_CONFIGURATION,\n * useValue: {\n * disabled: false,\n * loadPermissions: () => ['SAMPLE_C_EMPLOYEE_VIEW'],\n * onForbiden: () => router.navigateByUrl('/layout/forbidden')\n * }\n * }\n */\nexport const SD_PERMISSION_CONFIGURATION = new InjectionToken<ISdPermissionConfiguration | ISdPermissionConfiguration[]>(\n 'sd-permission.configuration'\n);\n","import { inject, Injectable } from '@angular/core';\nimport { SdCache, SdCacheService } from '@sdcorejs/angular/services/cache';\nimport { ArrayUtilities } from '@sdcorejs/angular/utilities/extensions';\nimport { ISdPermissionConfiguration, SD_PERMISSION_CONFIGURATION } from '../configurations';\nimport { MaybeAsync, resolveMaybeAsync } from '@sdcorejs/utils/models';\n\n@Injectable({ providedIn: 'root' })\nexport class SdPermissionService {\n #permissionMapByKey: Record<string, Record<string, boolean>> = {};\n readonly #configuration = inject<ISdPermissionConfiguration | ISdPermissionConfiguration[]>(SD_PERMISSION_CONFIGURATION);\n readonly #cacheService = inject(SdCacheService);\n #permissionsByKey: SdCache<Record<string, string[]>> = this.#cacheService.create<Record<string, string[]>>(\n '212a51fa-38d5-43b2-bd46-922d85950ba3',\n {\n type: 'session',\n default: {},\n }\n );\n readonly #loadedKeys = new Set<string>();\n\n constructor() {\n this.#validateDuplicateConfigKeys();\n }\n\n #getConfigurations = (): ISdPermissionConfiguration[] => {\n const config = this.#configuration;\n if (!config) {\n return [];\n }\n return Array.isArray(config) ? config : [config];\n };\n\n #normalizeKey = (key?: string): string => {\n return key === undefined ? '__undefined__' : key;\n };\n\n #validateDuplicateConfigKeys = (): void => {\n const seen = new Set<string>();\n for (const config of this.#getConfigurations()) {\n const normalizedKey = this.#normalizeKey(config.key);\n if (seen.has(normalizedKey)) {\n const keyLabel = config.key === undefined ? 'undefined' : config.key;\n throw new Error(`[Permission] Duplicate permission configuration key: ${keyLabel}`);\n }\n seen.add(normalizedKey);\n }\n };\n\n #getConfigurationByKey = (key?: string): ISdPermissionConfiguration | undefined => {\n return this.#getConfigurations().find(config => config.key === key);\n };\n\n #getEffectivePermissionKey = (key?: string): string | undefined => {\n if (this.#getConfigurationByKey(key)) {\n return key;\n }\n\n // Portal-level config uses key = undefined and acts as default fallback\n if (key !== undefined && this.#getConfigurationByKey(undefined)) {\n return undefined;\n }\n\n return key;\n };\n\n #setPermissionsForKey = (normalizedKey: string, permissions: string[]): void => {\n const distinctPermissions = ArrayUtilities.distinct(permissions || []);\n const current = this.#permissionsByKey.get() || {};\n this.#permissionsByKey.set({\n ...current,\n [normalizedKey]: distinctPermissions,\n });\n\n const permissionMap: Record<string, boolean> = {};\n distinctPermissions.forEach(permission => {\n permissionMap[permission] = true;\n });\n this.#permissionMapByKey[normalizedKey] = permissionMap;\n };\n\n loadPermissions = async (key?: string): Promise<string[]> => {\n const effectiveKey = this.#getEffectivePermissionKey(key);\n const normalizedKey = this.#normalizeKey(effectiveKey);\n if (this.#loadedKeys.has(normalizedKey)) {\n return this.#permissionsByKey.get()?.[normalizedKey] || [];\n }\n\n const configuration = this.#getConfigurationByKey(effectiveKey);\n if (!configuration) {\n this.#setPermissionsForKey(normalizedKey, []);\n this.#loadedKeys.add(normalizedKey);\n return [];\n }\n\n try {\n const permissions: string[] = await resolveMaybeAsync(configuration.loadPermissions());\n this.#setPermissionsForKey(normalizedKey, permissions || []);\n } catch (err) {\n console.error(err);\n this.#setPermissionsForKey(normalizedKey, []);\n } finally {\n this.#loadedKeys.add(normalizedKey);\n }\n\n return this.#permissionsByKey.get()?.[normalizedKey] || [];\n };\n\n loadAllPermissions = async (): Promise<void> => {\n const configurations = this.#getConfigurations();\n if (!configurations.length) {\n await this.loadPermissions(undefined);\n return;\n }\n\n await Promise.all(configurations.map(config => this.loadPermissions(config.key)));\n };\n\n hasPermission = (permission: string | string[], key?: string) => {\n if (!permission?.toString()) {\n return true;\n }\n\n const effectiveKey = this.#getEffectivePermissionKey(key);\n const configuration = this.#getConfigurationByKey(effectiveKey);\n if (configuration?.disabled) {\n return true;\n }\n\n const normalizedKey = this.#normalizeKey(effectiveKey);\n const permissionMap = this.#permissionMapByKey[normalizedKey] || {};\n const permissions = Array.isArray(permission) ? permission : [permission];\n return permissions.some(val => permissionMap[val]);\n };\n\n getToken = async (key?: string) => {\n const effectiveKey = this.#getEffectivePermissionKey(key);\n const getToken = this.#getConfigurationByKey(effectiveKey)?.getToken as (() => MaybeAsync<string | undefined | null>) | undefined;\n if (!getToken) {\n throw new Error('[Permission] Method getToken');\n }\n\n const token = await resolveMaybeAsync(getToken());\n if (token === '') {\n return undefined;\n }\n return token;\n };\n\n decodeToken = async <T>(key?: string): Promise<T | null> => {\n const token = await this.getToken(key);\n if (!token) {\n return null;\n }\n try {\n const payload = token.split('.')[1];\n const base64 = payload.replace(/-/g, '+').replace(/_/g, '/');\n const jsonPayload = decodeURIComponent(\n atob(base64)\n .split('')\n .map(c => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))\n .join('')\n );\n return JSON.parse(jsonPayload);\n } catch (error) {\n console.error('Invalid token', error);\n return null;\n }\n };\n}\n","import { Directive, effect, inject, input, TemplateRef, ViewContainerRef } from '@angular/core';\nimport { SdPermissionService } from '../services';\n\n@Directive({\n selector: '[sdPermission]',\n})\nexport class SdPermissionDirective {\n readonly #templateRef: TemplateRef<Record<string, never>> = inject(TemplateRef);\n readonly #viewContainerRef = inject(ViewContainerRef);\n readonly #permissionService = inject(SdPermissionService);\n\n // Nếu là mảng thì chỉ cần có 1 permission trong mảng đó xem như có quyền\n readonly sdPermission = input<string | string[] | undefined | null>(undefined);\n readonly sdPermissionKey = input<string | undefined>(undefined);\n\n constructor() {\n effect(() => {\n const permission = this.sdPermission();\n const permissionKey = this.sdPermissionKey();\n\n this.#viewContainerRef.clear();\n\n // Nếu không gắn permission thì render\n if (!permission?.toString()) {\n this.#viewContainerRef.createEmbeddedView(this.#templateRef);\n return;\n }\n\n // Kiểm tra permission theo key (nếu có)\n if (this.#permissionService.hasPermission(permission, permissionKey)) {\n this.#viewContainerRef.createEmbeddedView(this.#templateRef);\n }\n });\n }\n}\n","import { inject, Injectable } from '@angular/core';\nimport { ActivatedRouteSnapshot, CanActivate, CanActivateChild, RouterStateSnapshot } from '@angular/router';\nimport { ISdPermissionConfiguration, SD_PERMISSION_CONFIGURATION } from '../configurations';\nimport { SdPermissionService } from '../services';\n\n@Injectable({ providedIn: 'root' })\nexport class SdPermissionGuard implements CanActivate, CanActivateChild {\n private readonly configuration: ISdPermissionConfiguration | ISdPermissionConfiguration[] = inject(SD_PERMISSION_CONFIGURATION);\n private readonly permissionService = inject(SdPermissionService);\n\n #getConfigurations = (): ISdPermissionConfiguration[] => {\n const config = this.configuration;\n if (!config) {\n return [];\n }\n return Array.isArray(config) ? config : [config];\n };\n\n canActivate = async (next: ActivatedRouteSnapshot, state: RouterStateSnapshot) => {\n // Guard ở layer portal: preload toàn bộ permission theo tất cả key đã cấu hình\n await this.permissionService.loadAllPermissions().catch(console.error);\n return true;\n };\n\n canActivateChild = async (activatedRoute: ActivatedRouteSnapshot, state: RouterStateSnapshot) => {\n const permission = activatedRoute.data['permission'];\n const permissionKey = activatedRoute.data?.['permissionKey'] as string | undefined;\n if (this.permissionService.hasPermission(permission, permissionKey)) {\n return true;\n }\n\n const configurations = this.#getConfigurations();\n const onForbiden = configurations\n .filter(config => config.key === permissionKey || (permissionKey !== undefined && config.key === undefined))\n .map(config => config.onForbiden)\n .find(val => !!val);\n onForbiden?.();\n return false;\n };\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
@@ -1,18 +1,56 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Inject, Optional, Pipe, Injectable } from '@angular/core';
2
+ import { Pipe, Injectable, inject } from '@angular/core';
3
+ import { DateUtilities, NumberUtilities } from '@sdcorejs/angular/utilities/extensions';
3
4
  import { SD_CORE_CONFIGURATION } from '@sdcorejs/angular/configurations';
4
- import { NumberUtilities, DateUtilities } from '@sdcorejs/angular/utilities/extensions';
5
5
  import * as i1 from '@angular/platform-browser';
6
6
  import { of, interval } from 'rxjs';
7
7
  import { map } from 'rxjs/operators';
8
8
  import { EMPTY_STR } from '@sdcorejs/utils/constants';
9
9
 
10
- class SdFormatNumberPipe {
11
- coreConfiguration;
12
- constructor(coreConfiguration) {
13
- this.coreConfiguration = coreConfiguration;
10
+ class SdFormatDatePipe {
11
+ transform(value, format = 'dd/MM/yyyy') {
12
+ return DateUtilities.toFormat(value, format) || null;
13
+ }
14
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdFormatDatePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
15
+ static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.3.25", ngImport: i0, type: SdFormatDatePipe, isStandalone: true, name: "sdFormatDate" });
16
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdFormatDatePipe, providedIn: 'root' });
17
+ }
18
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdFormatDatePipe, decorators: [{
19
+ type: Pipe,
20
+ args: [{
21
+ name: 'sdFormatDate',
22
+ standalone: true,
23
+ }]
24
+ }, {
25
+ type: Injectable,
26
+ args: [{
27
+ providedIn: 'root',
28
+ }]
29
+ }] });
30
+
31
+ class SdFormatDatetimePipe {
32
+ transform(value, format = 'dd/MM/yyyy HH:mm:ss') {
33
+ return DateUtilities.toFormat(value, format) || null;
14
34
  }
15
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
35
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdFormatDatetimePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
36
+ static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.3.25", ngImport: i0, type: SdFormatDatetimePipe, isStandalone: true, name: "sdFormatDatetime" });
37
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdFormatDatetimePipe, providedIn: 'root' });
38
+ }
39
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdFormatDatetimePipe, decorators: [{
40
+ type: Pipe,
41
+ args: [{
42
+ name: 'sdFormatDatetime',
43
+ standalone: true,
44
+ }]
45
+ }, {
46
+ type: Injectable,
47
+ args: [{
48
+ providedIn: 'root',
49
+ }]
50
+ }] });
51
+
52
+ class SdFormatNumberPipe {
53
+ coreConfiguration = inject(SD_CORE_CONFIGURATION, { optional: true });
16
54
  transform(value, digits, format) {
17
55
  const resolvedFormat = format ?? this.coreConfiguration?.format?.number;
18
56
  const fixedValue = NumberUtilities.isNumber(value) ? (+value).toFixed(digits ?? 2) : null;
@@ -21,7 +59,7 @@ class SdFormatNumberPipe {
21
59
  }
22
60
  return NumberUtilities.toISO(fixedValue);
23
61
  }
24
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdFormatNumberPipe, deps: [{ token: SD_CORE_CONFIGURATION, optional: true }], target: i0.ɵɵFactoryTarget.Pipe });
62
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdFormatNumberPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
25
63
  static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.3.25", ngImport: i0, type: SdFormatNumberPipe, isStandalone: true, name: "sdFormatNumber" });
26
64
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdFormatNumberPipe, providedIn: 'root' });
27
65
  }
@@ -36,12 +74,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
36
74
  args: [{
37
75
  providedIn: 'root',
38
76
  }]
39
- }], ctorParameters: () => [{ type: undefined, decorators: [{
40
- type: Inject,
41
- args: [SD_CORE_CONFIGURATION]
42
- }, {
43
- type: Optional
44
- }] }] });
77
+ }] });
45
78
 
46
79
  class SdSafeHtmlPipe {
47
80
  sanitizer;
@@ -74,7 +107,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
74
107
  }]
75
108
  }], ctorParameters: () => [{ type: i1.DomSanitizer }] });
76
109
 
77
- /* eslint-disable @typescript-eslint/no-explicit-any */
78
110
  class SdTimeDifferentPipe {
79
111
  maxSecond = 60;
80
112
  maxMinute = this.maxSecond * 60;
@@ -122,7 +154,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
122
154
  }]
123
155
  }] });
124
156
 
125
- /* eslint-disable @typescript-eslint/no-explicit-any */
126
157
  class SdEmptyPipe {
127
158
  transform(value) {
128
159
  if (value === undefined || value === null || value === '') {
@@ -141,9 +172,32 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
141
172
  }]
142
173
  }] });
143
174
 
175
+ class SdViewPipe {
176
+ transform(value) {
177
+ if (value === null || value === undefined || value === '' || (typeof value === 'number' && Number.isNaN(value))) {
178
+ return EMPTY_STR;
179
+ }
180
+ if (Array.isArray(value)) {
181
+ if (!value.length)
182
+ return EMPTY_STR;
183
+ return value.map(item => this.transform(item)).join(', ');
184
+ }
185
+ return `${value}`;
186
+ }
187
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdViewPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
188
+ static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.3.25", ngImport: i0, type: SdViewPipe, isStandalone: true, name: "sdView" });
189
+ }
190
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdViewPipe, decorators: [{
191
+ type: Pipe,
192
+ args: [{
193
+ name: 'sdView',
194
+ standalone: true,
195
+ }]
196
+ }] });
197
+
144
198
  /**
145
199
  * Generated bundle index. Do not edit.
146
200
  */
147
201
 
148
- export { SdEmptyPipe, SdFormatNumberPipe, SdSafeHtmlPipe, SdTimeDifferentPipe };
202
+ export { SdEmptyPipe, SdFormatDatePipe, SdFormatDatetimePipe, SdFormatNumberPipe, SdSafeHtmlPipe, SdTimeDifferentPipe, SdViewPipe };
149
203
  //# sourceMappingURL=sdcorejs-angular-pipes.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"sdcorejs-angular-pipes.mjs","sources":["../../../projects/sdcorejs-angular/pipes/src/format-number.pipe.ts","../../../projects/sdcorejs-angular/pipes/src/safe-html.pipe.ts","../../../projects/sdcorejs-angular/pipes/src/time-different.pipe.ts","../../../projects/sdcorejs-angular/pipes/src/empty.pipe.ts","../../../projects/sdcorejs-angular/pipes/sdcorejs-angular-pipes.ts"],"sourcesContent":["import { Inject, Injectable, Optional, Pipe, PipeTransform } from '@angular/core';\nimport { ISdCoreConfiguration, SD_CORE_CONFIGURATION } from '@sdcorejs/angular/configurations';\nimport { NumberUtilities } from '@sdcorejs/angular/utilities/extensions';\n@Pipe({\n name: 'sdFormatNumber',\n standalone: true,\n})\n@Injectable({\n providedIn: 'root',\n})\nexport class SdFormatNumberPipe implements PipeTransform {\n constructor(@Inject(SD_CORE_CONFIGURATION) @Optional() private readonly coreConfiguration: ISdCoreConfiguration | undefined) {}\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n transform(value: any, digits?: number, format?: '1,234,567.89' | '1.234.567,89') {\n const resolvedFormat = format ?? this.coreConfiguration?.format?.number;\n const fixedValue = NumberUtilities.isNumber(value) ? (+value).toFixed(digits ?? 2) : null;\n if (resolvedFormat === '1.234.567,89') {\n return NumberUtilities.toVN(fixedValue);\n }\n return NumberUtilities.toISO(fixedValue);\n }\n}\n","import { Injectable, Pipe, PipeTransform } from '@angular/core';\nimport { DomSanitizer } from '@angular/platform-browser';\n\n@Pipe({\n name: 'sdSafeHtml',\n standalone: true,\n})\n@Injectable({\n providedIn: 'root',\n})\nexport class SdSafeHtmlPipe implements PipeTransform {\n constructor(private sanitizer: DomSanitizer) {}\n transform(html: string | number | undefined | null) {\n if (typeof html === 'number') {\n return html;\n }\n if (!html) {\n return undefined;\n }\n return this.sanitizer.bypassSecurityTrustHtml(html);\n }\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { Pipe, PipeTransform } from '@angular/core';\nimport { DateUtilities } from '@sdcorejs/angular/utilities/extensions';\nimport { interval, Observable, of } from 'rxjs';\nimport { map } from 'rxjs/operators';\n@Pipe({\n name: 'sdTimeDifferent',\n standalone: true,\n})\nexport class SdTimeDifferentPipe implements PipeTransform {\n private maxSecond = 60;\n private maxMinute = this.maxSecond * 60;\n private maxHour = this.maxMinute * 24;\n private maxDay = this.maxHour * 30;\n private maxMonth = this.maxHour * 365;\n transform(value: any, format: string, different: 'second' | 'minute' | 'hour' | 'day' | 'month'): Observable<string> {\n if (!DateUtilities.isDate(value)) {\n return of('');\n }\n if (!different) {\n return of(DateUtilities.toFormat(value, format));\n }\n if (Math.round((new Date().getTime() - new Date(value).getTime()) / 1000) < 0) {\n return of(DateUtilities.toFormat(value, format));\n }\n return interval(1000).pipe(\n map(() => {\n const timeDifferent = Math.round((new Date().getTime() - new Date(value).getTime()) / 1000);\n if (different === 'month' && timeDifferent < this.maxMonth) {\n return DateUtilities.timeDifference(value);\n }\n if (different === 'day' && timeDifferent < this.maxDay) {\n return DateUtilities.timeDifference(value);\n }\n if (different === 'hour' && timeDifferent < this.maxHour) {\n return DateUtilities.timeDifference(value);\n }\n if (different === 'minute' && timeDifferent < this.maxMinute) {\n return DateUtilities.timeDifference(value);\n }\n if (different === 'second' && timeDifferent < this.maxSecond) {\n return DateUtilities.timeDifference(value);\n }\n return DateUtilities.toFormat(value, format);\n })\n );\n }\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { Pipe, PipeTransform } from '@angular/core';\nimport { EMPTY_STR } from '@sdcorejs/utils/constants';\n@Pipe({\n name: 'sdEmpty',\n standalone: true,\n})\nexport class SdEmptyPipe implements PipeTransform {\n transform(value: any): string {\n if (value === undefined || value === null || value === '') {\n return EMPTY_STR;\n }\n return value;\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sdcorejs-angular-pipes.mjs","sources":["../../../projects/sdcorejs-angular/pipes/src/format-date.pipe.ts","../../../projects/sdcorejs-angular/pipes/src/format-datetime.pipe.ts","../../../projects/sdcorejs-angular/pipes/src/format-number.pipe.ts","../../../projects/sdcorejs-angular/pipes/src/safe-html.pipe.ts","../../../projects/sdcorejs-angular/pipes/src/time-different.pipe.ts","../../../projects/sdcorejs-angular/pipes/src/empty.pipe.ts","../../../projects/sdcorejs-angular/pipes/src/view.pipe.ts","../../../projects/sdcorejs-angular/pipes/sdcorejs-angular-pipes.ts"],"sourcesContent":["import { Injectable, Pipe, PipeTransform } from '@angular/core';\nimport { DateUtilities } from '@sdcorejs/angular/utilities/extensions';\n\n@Pipe({\n name: 'sdFormatDate',\n standalone: true,\n})\n@Injectable({\n providedIn: 'root',\n})\nexport class SdFormatDatePipe implements PipeTransform {\n transform(value: unknown, format = 'dd/MM/yyyy'): string | null {\n return DateUtilities.toFormat(value, format) || null;\n }\n}\n","import { Injectable, Pipe, PipeTransform } from '@angular/core';\nimport { DateUtilities } from '@sdcorejs/angular/utilities/extensions';\n\n@Pipe({\n name: 'sdFormatDatetime',\n standalone: true,\n})\n@Injectable({\n providedIn: 'root',\n})\nexport class SdFormatDatetimePipe implements PipeTransform {\n transform(value: unknown, format = 'dd/MM/yyyy HH:mm:ss'): string | null {\n return DateUtilities.toFormat(value, format) || null;\n }\n}\n","import { inject, Injectable, Pipe, PipeTransform } from '@angular/core';\nimport { ISdCoreConfiguration, SD_CORE_CONFIGURATION } from '@sdcorejs/angular/configurations';\nimport { NumberUtilities } from '@sdcorejs/angular/utilities/extensions';\n@Pipe({\n name: 'sdFormatNumber',\n standalone: true,\n})\n@Injectable({\n providedIn: 'root',\n})\nexport class SdFormatNumberPipe implements PipeTransform {\n private readonly coreConfiguration: ISdCoreConfiguration | null = inject(SD_CORE_CONFIGURATION, { optional: true });\n\n\n transform(value: any, digits?: number, format?: '1,234,567.89' | '1.234.567,89') {\n const resolvedFormat = format ?? this.coreConfiguration?.format?.number;\n const fixedValue = NumberUtilities.isNumber(value) ? (+value).toFixed(digits ?? 2) : null;\n if (resolvedFormat === '1.234.567,89') {\n return NumberUtilities.toVN(fixedValue);\n }\n return NumberUtilities.toISO(fixedValue);\n }\n}\n","import { Injectable, Pipe, PipeTransform } from '@angular/core';\nimport { DomSanitizer } from '@angular/platform-browser';\n\n@Pipe({\n name: 'sdSafeHtml',\n standalone: true,\n})\n@Injectable({\n providedIn: 'root',\n})\nexport class SdSafeHtmlPipe implements PipeTransform {\n constructor(private sanitizer: DomSanitizer) {}\n transform(html: string | number | undefined | null) {\n if (typeof html === 'number') {\n return html;\n }\n if (!html) {\n return undefined;\n }\n return this.sanitizer.bypassSecurityTrustHtml(html);\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\nimport { DateUtilities } from '@sdcorejs/angular/utilities/extensions';\nimport { interval, Observable, of } from 'rxjs';\nimport { map } from 'rxjs/operators';\n@Pipe({\n name: 'sdTimeDifferent',\n standalone: true,\n})\nexport class SdTimeDifferentPipe implements PipeTransform {\n private maxSecond = 60;\n private maxMinute = this.maxSecond * 60;\n private maxHour = this.maxMinute * 24;\n private maxDay = this.maxHour * 30;\n private maxMonth = this.maxHour * 365;\n transform(value: any, format: string, different: 'second' | 'minute' | 'hour' | 'day' | 'month'): Observable<string> {\n if (!DateUtilities.isDate(value)) {\n return of('');\n }\n if (!different) {\n return of(DateUtilities.toFormat(value, format));\n }\n if (Math.round((new Date().getTime() - new Date(value).getTime()) / 1000) < 0) {\n return of(DateUtilities.toFormat(value, format));\n }\n return interval(1000).pipe(\n map(() => {\n const timeDifferent = Math.round((new Date().getTime() - new Date(value).getTime()) / 1000);\n if (different === 'month' && timeDifferent < this.maxMonth) {\n return DateUtilities.timeDifference(value);\n }\n if (different === 'day' && timeDifferent < this.maxDay) {\n return DateUtilities.timeDifference(value);\n }\n if (different === 'hour' && timeDifferent < this.maxHour) {\n return DateUtilities.timeDifference(value);\n }\n if (different === 'minute' && timeDifferent < this.maxMinute) {\n return DateUtilities.timeDifference(value);\n }\n if (different === 'second' && timeDifferent < this.maxSecond) {\n return DateUtilities.timeDifference(value);\n }\n return DateUtilities.toFormat(value, format);\n })\n );\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\nimport { EMPTY_STR } from '@sdcorejs/utils/constants';\n@Pipe({\n name: 'sdEmpty',\n standalone: true,\n})\nexport class SdEmptyPipe implements PipeTransform {\n transform(value: any): string {\n if (value === undefined || value === null || value === '') {\n return EMPTY_STR;\n }\n return value;\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\nimport { EMPTY_STR } from '@sdcorejs/utils/constants';\n\n@Pipe({\n name: 'sdView',\n standalone: true,\n})\nexport class SdViewPipe implements PipeTransform {\n transform(value: unknown): string {\n if (value === null || value === undefined || value === '' || (typeof value === 'number' && Number.isNaN(value))) {\n return EMPTY_STR;\n }\n\n if (Array.isArray(value)) {\n if (!value.length) return EMPTY_STR;\n return value.map(item => this.transform(item)).join(', ');\n }\n\n return `${value}`;\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
@@ -9,7 +9,6 @@ import { timeout, map, catchError, shareReplay, switchMap } from 'rxjs/operators
9
9
  const SD_API_CONFIG = new InjectionToken('sd-api.configuration');
10
10
  const SD_API_CONFIGURATION = new InjectionToken('sd-api.configuration');
11
11
 
12
- /* eslint-disable @typescript-eslint/no-explicit-any */
13
12
  class SdApiService {
14
13
  #defaultTimeout = 60000; // 60s
15
14
  #dedupCacheDuration = 1000; // 1s (Deduplication cache duration)
@@ -162,7 +161,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
162
161
  }]
163
162
  }], ctorParameters: () => [] });
164
163
 
165
- /* eslint-disable @typescript-eslint/no-explicit-any */
166
164
  class SdHttpInterceptor {
167
165
  #configurations = inject(SD_API_CONFIG, { optional: true }) || [];
168
166
  intercept(request, next) {
@@ -1 +1 @@
1
- {"version":3,"file":"sdcorejs-angular-services-api.mjs","sources":["../../../projects/sdcorejs-angular/services/api/src/api.model.ts","../../../projects/sdcorejs-angular/services/api/src/api.service.ts","../../../projects/sdcorejs-angular/services/api/src/interceptors/api.interceptor.ts","../../../projects/sdcorejs-angular/services/api/src/api.module.ts","../../../projects/sdcorejs-angular/services/api/sdcorejs-angular-services-api.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { HttpClient, HttpErrorResponse, HttpRequest, HttpResponse } from '@angular/common/http';\nimport { InjectionToken } from '@angular/core';\nimport { SdCacheOption } from '@sdcorejs/angular/services/cache';\n// import hash from 'object-hash';\n\nexport interface SdApiOption {\n cacheOption?: SdCacheOption;\n timeout?: number; // Default: 30000 (30s)\n autoCache?: boolean; // Default: true\n}\ntype HttpGetOption = Parameters<HttpClient['get']>[1];\nexport type SdGetOption = HttpGetOption & SdApiOption;\n\ntype HttpPostOption = Parameters<HttpClient['post']>[2];\nexport type SdPostOption = HttpPostOption & SdApiOption;\n\ntype HttpPutOption = Parameters<HttpClient['put']>[2];\nexport type SdPutOption = HttpPutOption & SdApiOption;\n\ntype HttpDeleteOption = Parameters<HttpClient['delete']>[1];\nexport type SdDeleteOption = HttpDeleteOption & SdApiOption;\n\nexport interface SdApiHandler {\n /** Danh sách host URL mà handler này sẽ xử lý */\n hosts: string[];\n /** Can thiệp request: gắn header, token, transform body... */\n intercept?: (request: HttpRequest<any>) => HttpRequest<any>;\n /** Hook chạy TRƯỚC khi gửi request (dùng để log, tracking...) */\n beforeRemote?: (request: HttpRequest<any>) => void | Promise<void>;\n /** Hook chạy SAU khi nhận response (xử lý lỗi, notify...) */\n afterRemote?: (\n response: HttpResponse<any> | HttpErrorResponse | Error\n ) => void | Promise<void>;\n /** Transform response body thành kiểu dữ liệu mong muốn */\n mapResponse?: <Tres = any, Tdata = any>(response: Tres) => Tdata;\n /** Timeout tính bằng milliseconds. Mặc định: 30000 (30 giây) */\n timeout?: number;\n}\n\nexport interface ISdApiConfiguration {\n handlers: SdApiHandler[];\n}\n\nexport const SD_API_CONFIG = new InjectionToken<ISdApiConfiguration>('sd-api.configuration');\n\nexport const SD_API_CONFIGURATION = new InjectionToken<ISdApiConfiguration>('sd-api.configuration');","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { HttpClient } from '@angular/common/http';\nimport { inject, Injectable } from '@angular/core';\nimport { SdCacheService } from '@sdcorejs/angular/services/cache';\nimport { BrowserUtilities, Utilities } from '@sdcorejs/utils/fns';\nimport { lastValueFrom, Observable } from 'rxjs';\nimport { catchError, map, shareReplay, timeout } from 'rxjs/operators';\nimport { ISdApiConfiguration, SD_API_CONFIG, SdApiHandler, SdDeleteOption, SdGetOption, SdPostOption, SdPutOption } from './api.model';\n\n// Gom nhóm các Option lại cho gọn\ntype SdHttpOptions = SdGetOption & SdPostOption & SdPutOption & SdDeleteOption;\ntype HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';\n\n@Injectable({\n providedIn: 'root',\n})\nexport class SdApiService {\n readonly #defaultTimeout = 60000; // 60s\n readonly #dedupCacheDuration = 1000; // 1s (Deduplication cache duration)\n #httpClient = inject(HttpClient);\n #configurations = inject<ISdApiConfiguration[]>(SD_API_CONFIG, { optional: true }) || [];\n #cacheService = inject(SdCacheService);\n\n // Thay đổi cấu trúc Cache: Lưu Observable thay vì Subject phức tạp\n // Key: hash string -> Value: { stream$: Observable, expiry: number }\n #inFlightRequests: Map<string, { stream$: Observable<any>; expiry: number }> = new Map();\n\n constructor() {\n // Optional: Cơ chế dọn dẹp cache định kỳ (mỗi 1 phút dọn dẹp các key hết hạn)\n setInterval(() => this.#cleanupCache(), 60000);\n }\n\n get http() {\n return this.#httpClient;\n }\n\n // --- PUBLIC METHODS ---\n\n get = <T = any>(url: string, option?: SdGetOption): Promise<T> => {\n return this.#executeWithLayeredCache<T>(url, 'GET', undefined, option);\n };\n\n post = <T = any>(url: string, body?: any, option?: SdPostOption): Promise<T> => {\n return this.#executeWithLayeredCache<T>(url, 'POST', body, option);\n };\n\n put = <T = any>(url: string, body?: any, option?: SdPutOption): Promise<T> => {\n return this.#executeWithLayeredCache<T>(url, 'PUT', body, option);\n };\n\n delete = <T = any>(url: string, option?: SdDeleteOption): Promise<T> => {\n return this.#executeWithLayeredCache<T>(url, 'DELETE', undefined, option);\n };\n\n // Upload file logic giữ nguyên nhưng refactor nhẹ\n upload = async (url: string, option?: { extensions?: string[]; maxSizeInMb?: number }): Promise<any> => {\n const file = await BrowserUtilities.upload(option);\n if (!Array.isArray(file) && file) {\n return this.uploadFile(url, file);\n }\n };\n\n uploadFile = async (url: string, file: File): Promise<any> => {\n if (!file) return null;\n if (file.name.lastIndexOf('.') === -1) throw new Error('Invalid file extension');\n\n const formData = new FormData();\n formData.append('file', file, file.name);\n // Upload thường không cần deduplication cache, set autoCache: false\n return await this.post(url, formData, { autoCache: false });\n };\n\n // --- PRIVATE CORE LOGIC ---\n\n /**\n * Hàm trung gian xử lý Layer Cache (SdCacheService) trước khi gọi API thực tế\n */\n #executeWithLayeredCache = async <T>(url: string, method: HttpMethod, body?: any, option?: SdHttpOptions): Promise<T> => {\n // Layer 1: Persistent Cache (SdCacheService)\n if (option?.cacheOption) {\n const key = this.#generateKey(url, method, body, option);\n const { get, set, has } = this.#cacheService.create(key, option.cacheOption);\n\n if (has()) {\n return get();\n }\n\n const result = await this.#request<T>(url, method, body, option);\n set(result);\n return result;\n }\n\n // Không có cache dài hạn thì gọi trực tiếp logic deduplication\n return this.#request<T>(url, method, body, option);\n };\n\n /**\n * Core Request: Xử lý Deduplication, Timeout, Mapping Response\n */\n #request = <T>(url: string, method: HttpMethod, body: any, option?: SdHttpOptions): Promise<T> => {\n const key = this.#generateKey(url, method, body, option);\n\n // Layer 2: Deduplication Cache (In-Flight Request / Short-term cache)\n const now = Date.now();\n const cachedItem = this.#inFlightRequests.get(key);\n\n // Nếu đã có request đang chạy hoặc mới chạy xong trong vòng 1s -> Trả về stream đó luôn\n if (cachedItem && cachedItem.expiry > now) {\n return lastValueFrom(cachedItem.stream$);\n }\n\n // Setup request mới\n const handler = this.#getHandler(url);\n const apiTimeout = option?.timeout ?? handler?.timeout ?? this.#defaultTimeout;\n\n // Tạo Observable call API\n const request$ = this.#httpClient\n .request(method, url, {\n body,\n headers: option?.headers,\n params: option?.params,\n observe: 'response', // Lấy full response để check status\n responseType: option?.responseType,\n })\n .pipe(\n timeout(apiTimeout),\n map(res => {\n // Normalize Response Logic\n const bodyRes = res.body as any;\n // Logic check response cũ của bạn\n if (bodyRes && typeof bodyRes === 'object' && 'ok' in bodyRes && !bodyRes.ok) {\n throw bodyRes; // Giả sử structure trả về { ok: false, ... } là lỗi\n }\n\n if (handler?.mapResponse) {\n return handler.mapResponse(bodyRes);\n }\n\n return bodyRes;\n }),\n catchError(err => {\n // Xóa cache ngay lập tức nếu lỗi để user có thể retry\n this.#inFlightRequests.delete(key);\n throw err;\n }),\n // QUAN TRỌNG: shareReplay(1) giúp share kết quả cho các subscriber đến sau (trong 1s)\n shareReplay(1)\n );\n\n // Lưu vào Map\n this.#inFlightRequests.set(key, {\n stream$: request$,\n expiry: now + this.#dedupCacheDuration,\n });\n\n // Chuyển đổi sang Promise cho đúng return type của bạn\n return lastValueFrom(request$);\n };\n\n // --- HELPERS ---\n\n #getHandler = (url: string): SdApiHandler | undefined => {\n const handlers = this.#configurations.flatMap(b => b?.handlers || []);\n return handlers.find(e => e.hosts.some(host => url.startsWith(host)));\n };\n\n #generateKey = (url: string, method: HttpMethod, body: any, option?: SdHttpOptions): string => {\n // FormData không hash được nội dung file, luôn generate key mới\n if (body instanceof FormData || option?.autoCache === false) {\n return Utilities.generateUuid();\n }\n return Utilities.hash({\n url,\n method,\n params: option?.params,\n headers: option?.headers,\n body,\n });\n };\n\n // Dọn dẹp memory leak\n #cleanupCache() {\n const now = Date.now();\n this.#inFlightRequests.forEach((value, key) => {\n if (value.expiry < now) {\n this.#inFlightRequests.delete(key);\n }\n });\n }\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse } from '@angular/common/http';\nimport { inject, Injectable } from '@angular/core';\nimport { from, Observable, throwError } from 'rxjs';\nimport { catchError, map, switchMap } from 'rxjs/operators';\nimport { ISdApiConfiguration, SD_API_CONFIG } from '../api.model';\n\n@Injectable()\nexport class SdHttpInterceptor implements HttpInterceptor {\n #configurations = inject<ISdApiConfiguration[]>(SD_API_CONFIG, { optional: true }) || [];\n\n intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {\n const url = request.url;\n if (!url) {\n throw new Error(`Invalid URL`);\n }\n const handlers = this.#configurations.flatMap(configuration => configuration?.handlers || []);\n const handler = handlers?.find(e => e.hosts.some(host => url.startsWith(host)));\n const intercept = handler?.intercept;\n if (intercept) {\n request = request.clone(intercept(request));\n }\n if (request.body instanceof FormData) {\n request = request.clone({\n headers: request.headers.delete('Content-Type'),\n });\n }\n const beforeRemoteHandler = handler?.beforeRemote;\n const afterRemoteHandler = handler?.afterRemote;\n const beforeRemote = beforeRemoteHandler?.(request);\n if (beforeRemote instanceof Promise) {\n return from(beforeRemote).pipe(\n switchMap(() => next.handle(request)),\n map((event: HttpEvent<any>) => {\n if (event instanceof HttpResponse) {\n afterRemoteHandler?.(event);\n }\n return event;\n }),\n catchError((error: HttpErrorResponse) => {\n afterRemoteHandler?.(error);\n return throwError(() => error);\n })\n );\n }\n return next.handle(request).pipe(\n map((event: HttpEvent<any>) => {\n if (event instanceof HttpResponse) {\n afterRemoteHandler?.(event);\n }\n return event;\n }),\n catchError((error: HttpErrorResponse) => {\n afterRemoteHandler?.(error);\n return throwError(() => error);\n })\n );\n }\n}\n","import { HTTP_INTERCEPTORS, provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';\nimport { NgModule } from '@angular/core';\nimport { SdHttpInterceptor } from './interceptors/api.interceptor';\n\n@NgModule({\n imports: [],\n exports: [],\n providers: [provideHttpClient(withInterceptorsFromDi()), { provide: HTTP_INTERCEPTORS, useClass: SdHttpInterceptor, multi: true }],\n})\nexport class SdApiModule {}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sdcorejs-angular-services-api.mjs","sources":["../../../projects/sdcorejs-angular/services/api/src/api.model.ts","../../../projects/sdcorejs-angular/services/api/src/api.service.ts","../../../projects/sdcorejs-angular/services/api/src/interceptors/api.interceptor.ts","../../../projects/sdcorejs-angular/services/api/src/api.module.ts","../../../projects/sdcorejs-angular/services/api/sdcorejs-angular-services-api.ts"],"sourcesContent":["import { HttpClient, HttpErrorResponse, HttpRequest, HttpResponse } from '@angular/common/http';\nimport { InjectionToken } from '@angular/core';\nimport { SdCacheOption } from '@sdcorejs/angular/services/cache';\n// import hash from 'object-hash';\n\nexport interface SdApiOption {\n cacheOption?: SdCacheOption;\n timeout?: number; // Default: 30000 (30s)\n autoCache?: boolean; // Default: true\n}\ntype HttpGetOption = Parameters<HttpClient['get']>[1];\nexport type SdGetOption = HttpGetOption & SdApiOption;\n\ntype HttpPostOption = Parameters<HttpClient['post']>[2];\nexport type SdPostOption = HttpPostOption & SdApiOption;\n\ntype HttpPutOption = Parameters<HttpClient['put']>[2];\nexport type SdPutOption = HttpPutOption & SdApiOption;\n\ntype HttpDeleteOption = Parameters<HttpClient['delete']>[1];\nexport type SdDeleteOption = HttpDeleteOption & SdApiOption;\n\nexport interface SdApiHandler {\n /** Danh sách host URL mà handler này sẽ xử lý */\n hosts: string[];\n /** Can thiệp request: gắn header, token, transform body... */\n intercept?: (request: HttpRequest<any>) => HttpRequest<any>;\n /** Hook chạy TRƯỚC khi gửi request (dùng để log, tracking...) */\n beforeRemote?: (request: HttpRequest<any>) => void | Promise<void>;\n /** Hook chạy SAU khi nhận response (xử lý lỗi, notify...) */\n afterRemote?: (response: HttpResponse<any> | HttpErrorResponse | Error) => void | Promise<void>;\n /** Transform response body thành kiểu dữ liệu mong muốn */\n mapResponse?: <Tres = any, Tdata = any>(response: Tres) => Tdata;\n /** Timeout tính bằng milliseconds. Mặc định: 30000 (30 giây) */\n timeout?: number;\n}\n\nexport interface ISdApiConfiguration {\n handlers: SdApiHandler[];\n}\n\nexport const SD_API_CONFIG = new InjectionToken<ISdApiConfiguration>('sd-api.configuration');\n\nexport const SD_API_CONFIGURATION = new InjectionToken<ISdApiConfiguration>('sd-api.configuration');\n","import { HttpClient } from '@angular/common/http';\nimport { inject, Injectable } from '@angular/core';\nimport { SdCacheService } from '@sdcorejs/angular/services/cache';\nimport { BrowserUtilities, Utilities } from '@sdcorejs/utils/fns';\nimport { lastValueFrom, Observable } from 'rxjs';\nimport { catchError, map, shareReplay, timeout } from 'rxjs/operators';\nimport { ISdApiConfiguration, SD_API_CONFIG, SdApiHandler, SdDeleteOption, SdGetOption, SdPostOption, SdPutOption } from './api.model';\n\n// Gom nhóm các Option lại cho gọn\ntype SdHttpOptions = SdGetOption & SdPostOption & SdPutOption & SdDeleteOption;\ntype HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';\n\n@Injectable({\n providedIn: 'root',\n})\nexport class SdApiService {\n readonly #defaultTimeout = 60000; // 60s\n readonly #dedupCacheDuration = 1000; // 1s (Deduplication cache duration)\n #httpClient = inject(HttpClient);\n #configurations = inject<ISdApiConfiguration[]>(SD_API_CONFIG, { optional: true }) || [];\n #cacheService = inject(SdCacheService);\n\n // Thay đổi cấu trúc Cache: Lưu Observable thay vì Subject phức tạp\n // Key: hash string -> Value: { stream$: Observable, expiry: number }\n #inFlightRequests = new Map<string, { stream$: Observable<any>; expiry: number }>();\n\n constructor() {\n // Optional: Cơ chế dọn dẹp cache định kỳ (mỗi 1 phút dọn dẹp các key hết hạn)\n setInterval(() => this.#cleanupCache(), 60000);\n }\n\n get http() {\n return this.#httpClient;\n }\n\n // --- PUBLIC METHODS ---\n\n get = <T = any>(url: string, option?: SdGetOption): Promise<T> => {\n return this.#executeWithLayeredCache<T>(url, 'GET', undefined, option);\n };\n\n post = <T = any>(url: string, body?: any, option?: SdPostOption): Promise<T> => {\n return this.#executeWithLayeredCache<T>(url, 'POST', body, option);\n };\n\n put = <T = any>(url: string, body?: any, option?: SdPutOption): Promise<T> => {\n return this.#executeWithLayeredCache<T>(url, 'PUT', body, option);\n };\n\n delete = <T = any>(url: string, option?: SdDeleteOption): Promise<T> => {\n return this.#executeWithLayeredCache<T>(url, 'DELETE', undefined, option);\n };\n\n // Upload file logic giữ nguyên nhưng refactor nhẹ\n upload = async (url: string, option?: { extensions?: string[]; maxSizeInMb?: number }): Promise<any> => {\n const file = await BrowserUtilities.upload(option);\n if (!Array.isArray(file) && file) {\n return this.uploadFile(url, file);\n }\n };\n\n uploadFile = async (url: string, file: File): Promise<any> => {\n if (!file) return null;\n if (file.name.lastIndexOf('.') === -1) throw new Error('Invalid file extension');\n\n const formData = new FormData();\n formData.append('file', file, file.name);\n // Upload thường không cần deduplication cache, set autoCache: false\n return await this.post(url, formData, { autoCache: false });\n };\n\n // --- PRIVATE CORE LOGIC ---\n\n /**\n * Hàm trung gian xử lý Layer Cache (SdCacheService) trước khi gọi API thực tế\n */\n #executeWithLayeredCache = async <T>(url: string, method: HttpMethod, body?: any, option?: SdHttpOptions): Promise<T> => {\n // Layer 1: Persistent Cache (SdCacheService)\n if (option?.cacheOption) {\n const key = this.#generateKey(url, method, body, option);\n const { get, set, has } = this.#cacheService.create(key, option.cacheOption);\n\n if (has()) {\n return get();\n }\n\n const result = await this.#request<T>(url, method, body, option);\n set(result);\n return result;\n }\n\n // Không có cache dài hạn thì gọi trực tiếp logic deduplication\n return this.#request<T>(url, method, body, option);\n };\n\n /**\n * Core Request: Xử lý Deduplication, Timeout, Mapping Response\n */\n #request = <T>(url: string, method: HttpMethod, body: any, option?: SdHttpOptions): Promise<T> => {\n const key = this.#generateKey(url, method, body, option);\n\n // Layer 2: Deduplication Cache (In-Flight Request / Short-term cache)\n const now = Date.now();\n const cachedItem = this.#inFlightRequests.get(key);\n\n // Nếu đã có request đang chạy hoặc mới chạy xong trong vòng 1s -> Trả về stream đó luôn\n if (cachedItem && cachedItem.expiry > now) {\n return lastValueFrom(cachedItem.stream$);\n }\n\n // Setup request mới\n const handler = this.#getHandler(url);\n const apiTimeout = option?.timeout ?? handler?.timeout ?? this.#defaultTimeout;\n\n // Tạo Observable call API\n const request$ = this.#httpClient\n .request(method, url, {\n body,\n headers: option?.headers,\n params: option?.params,\n observe: 'response', // Lấy full response để check status\n responseType: option?.responseType,\n })\n .pipe(\n timeout(apiTimeout),\n map(res => {\n // Normalize Response Logic\n const bodyRes = res.body as any;\n // Logic check response cũ của bạn\n if (bodyRes && typeof bodyRes === 'object' && 'ok' in bodyRes && !bodyRes.ok) {\n throw bodyRes; // Giả sử structure trả về { ok: false, ... } là lỗi\n }\n\n if (handler?.mapResponse) {\n return handler.mapResponse(bodyRes);\n }\n\n return bodyRes;\n }),\n catchError(err => {\n // Xóa cache ngay lập tức nếu lỗi để user có thể retry\n this.#inFlightRequests.delete(key);\n throw err;\n }),\n // QUAN TRỌNG: shareReplay(1) giúp share kết quả cho các subscriber đến sau (trong 1s)\n shareReplay(1)\n );\n\n // Lưu vào Map\n this.#inFlightRequests.set(key, {\n stream$: request$,\n expiry: now + this.#dedupCacheDuration,\n });\n\n // Chuyển đổi sang Promise cho đúng return type của bạn\n return lastValueFrom(request$);\n };\n\n // --- HELPERS ---\n\n #getHandler = (url: string): SdApiHandler | undefined => {\n const handlers = this.#configurations.flatMap(b => b?.handlers || []);\n return handlers.find(e => e.hosts.some(host => url.startsWith(host)));\n };\n\n #generateKey = (url: string, method: HttpMethod, body: any, option?: SdHttpOptions): string => {\n // FormData không hash được nội dung file, luôn generate key mới\n if (body instanceof FormData || option?.autoCache === false) {\n return Utilities.generateUuid();\n }\n return Utilities.hash({\n url,\n method,\n params: option?.params,\n headers: option?.headers,\n body,\n });\n };\n\n // Dọn dẹp memory leak\n #cleanupCache() {\n const now = Date.now();\n this.#inFlightRequests.forEach((value, key) => {\n if (value.expiry < now) {\n this.#inFlightRequests.delete(key);\n }\n });\n }\n}\n","import { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse } from '@angular/common/http';\nimport { inject, Injectable } from '@angular/core';\nimport { from, Observable, throwError } from 'rxjs';\nimport { catchError, map, switchMap } from 'rxjs/operators';\nimport { ISdApiConfiguration, SD_API_CONFIG } from '../api.model';\n\n@Injectable()\nexport class SdHttpInterceptor implements HttpInterceptor {\n #configurations = inject<ISdApiConfiguration[]>(SD_API_CONFIG, { optional: true }) || [];\n\n intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {\n const url = request.url;\n if (!url) {\n throw new Error(`Invalid URL`);\n }\n const handlers = this.#configurations.flatMap(configuration => configuration?.handlers || []);\n const handler = handlers?.find(e => e.hosts.some(host => url.startsWith(host)));\n const intercept = handler?.intercept;\n if (intercept) {\n request = request.clone(intercept(request));\n }\n if (request.body instanceof FormData) {\n request = request.clone({\n headers: request.headers.delete('Content-Type'),\n });\n }\n const beforeRemoteHandler = handler?.beforeRemote;\n const afterRemoteHandler = handler?.afterRemote;\n const beforeRemote = beforeRemoteHandler?.(request);\n if (beforeRemote instanceof Promise) {\n return from(beforeRemote).pipe(\n switchMap(() => next.handle(request)),\n map((event: HttpEvent<any>) => {\n if (event instanceof HttpResponse) {\n afterRemoteHandler?.(event);\n }\n return event;\n }),\n catchError((error: HttpErrorResponse) => {\n afterRemoteHandler?.(error);\n return throwError(() => error);\n })\n );\n }\n return next.handle(request).pipe(\n map((event: HttpEvent<any>) => {\n if (event instanceof HttpResponse) {\n afterRemoteHandler?.(event);\n }\n return event;\n }),\n catchError((error: HttpErrorResponse) => {\n afterRemoteHandler?.(error);\n return throwError(() => error);\n })\n );\n }\n}\n","import { HTTP_INTERCEPTORS, provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';\nimport { NgModule } from '@angular/core';\nimport { SdHttpInterceptor } from './interceptors/api.interceptor';\n\n@NgModule({\n imports: [],\n exports: [],\n providers: [provideHttpClient(withInterceptorsFromDi()), { provide: HTTP_INTERCEPTORS, useClass: SdHttpInterceptor, multi: true }],\n})\nexport class SdApiModule {}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
@@ -5,7 +5,6 @@ import { Utilities } from '@sdcorejs/utils/fns';
5
5
  import { BehaviorSubject } from 'rxjs';
6
6
  import { map } from 'rxjs/operators';
7
7
 
8
- /* eslint-disable @typescript-eslint/no-explicit-any */
9
8
  class SdCacheService {
10
9
  #memoryCache = new Map();
11
10
  #subjects = new Map();
@@ -49,17 +48,12 @@ class SdCacheService {
49
48
  return get();
50
49
  }
51
50
  // Cache miss -> Gọi API
52
- try {
53
- const result = await callback();
54
- // Chỉ lưu nếu kết quả hợp lệ (khác undefined/null)
55
- if (result !== undefined && result !== null) {
56
- set(result);
57
- }
58
- return result;
59
- }
60
- catch (error) {
61
- throw error;
51
+ const result = await callback();
52
+ // Chỉ lưu nếu kết quả hợp lệ (khác undefined/null)
53
+ if (result !== undefined && result !== null) {
54
+ set(result);
62
55
  }
56
+ return result;
63
57
  };
64
58
  return {
65
59
  get,
@@ -1 +1 @@
1
- {"version":3,"file":"sdcorejs-angular-services-cache.mjs","sources":["../../../projects/sdcorejs-angular/services/cache/src/cache.service.ts","../../../projects/sdcorejs-angular/services/cache/src/cache.model.ts","../../../projects/sdcorejs-angular/services/cache/sdcorejs-angular-services-cache.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { Injectable } from '@angular/core';\nimport { DateUtilities } from '@sdcorejs/angular/utilities/extensions';\nimport { Utilities } from '@sdcorejs/utils/fns';\nimport { BehaviorSubject } from 'rxjs';\nimport { map } from 'rxjs/operators';\nimport { SdCache, SdCacheOption } from './cache.model';\n\ninterface CacheEntry<T> {\n data: T;\n createdOn: Date;\n}\n\n@Injectable({\n providedIn: 'root',\n})\nexport class SdCacheService {\n #memoryCache = new Map<string, CacheEntry<any>>();\n #subjects = new Map<string, BehaviorSubject<any>>();\n\n constructor() {}\n\n create<T = any>(key: string | object, option?: SdCacheOption<T>): SdCache<T> {\n if (!key) throw new Error('Key is required');\n\n const hashKey = Utilities.hash({ key });\n\n // 1. Init Subject: Load data có sẵn nếu có\n if (!this.#subjects.has(hashKey)) {\n const existingData = this.#internalGet<T>(hashKey, option);\n this.#subjects.set(hashKey, new BehaviorSubject<T | undefined>(existingData ?? option?.default));\n }\n\n const subject = this.#subjects.get(hashKey)!;\n\n // 2. Define helpers\n const get = () => {\n const val = this.#internalGet<T>(hashKey, option);\n // Fix strict type: Ép kiểu về T (chấp nhận rủi ro undefined nếu user không check)\n return (val ?? option?.default) as T;\n };\n\n const set = (data: T) => {\n this.#internalSet(hashKey, data, option);\n subject.next(data);\n };\n\n const has = () => {\n return this.#internalGet<T>(hashKey, option) !== undefined;\n };\n\n const remove = () => {\n this.#internalRemove(hashKey, option);\n subject.next(undefined);\n };\n\n const destroy = () => {\n subject.complete();\n this.#subjects.delete(hashKey);\n this.#memoryCache.delete(hashKey);\n };\n\n // 3. IMPLEMENT LOAD (Get-Or-Set Pattern)\n const load = async (callback: () => Promise<T>): Promise<T> => {\n // Check cache trước\n if (has()) {\n return get();\n }\n\n // Cache miss -> Gọi API\n try {\n const result = await callback();\n // Chỉ lưu nếu kết quả hợp lệ (khác undefined/null)\n if (result !== undefined && result !== null) {\n set(result);\n }\n return result;\n } catch (error) {\n throw error;\n }\n };\n\n return {\n get,\n set,\n has,\n remove,\n destroy,\n load,\n observer: subject.asObservable().pipe(map(() => get())),\n };\n }\n\n // --- PRIVATE HELPERS ---\n\n #getStorage(option?: SdCacheOption): Storage | null {\n if (option?.type === 'local') return localStorage;\n if (option?.type === 'session') return sessionStorage;\n return null;\n }\n\n #internalGet<T>(key: string, option?: SdCacheOption): T | undefined {\n let entry = this.#memoryCache.get(key);\n\n if (!entry) {\n const storage = this.#getStorage(option);\n if (storage) {\n const raw = storage.getItem(key);\n if (raw) {\n try {\n const parsed = JSON.parse(raw);\n entry = {\n data: parsed.data,\n createdOn: new Date(parsed.createdOn),\n };\n this.#memoryCache.set(key, entry);\n } catch (e) {\n this.#internalRemove(key, option);\n }\n }\n }\n }\n\n if (!entry) return undefined;\n\n if (option?.hours && entry.createdOn) {\n const expiredOn = DateUtilities.addHours(entry.createdOn, option.hours);\n if (expiredOn && expiredOn < new Date()) {\n this.#internalRemove(key, option);\n return undefined;\n }\n }\n\n return this.#deepClone(entry.data);\n }\n\n #internalSet<T>(key: string, data: T, option?: SdCacheOption): void {\n // FIX LỖI TẠI ĐÂY: Bỏ \"?? null\" để giữ nguyên kiểu T\n const clonedData = this.#deepClone(data);\n\n const entry: CacheEntry<T> = {\n data: clonedData,\n createdOn: new Date(),\n };\n\n this.#memoryCache.set(key, entry);\n\n const storage = this.#getStorage(option);\n if (storage) {\n try {\n storage.setItem(key, JSON.stringify(entry));\n } catch (e) {\n console.error('Storage quota exceeded', e);\n }\n }\n }\n\n #internalRemove(key: string, option?: SdCacheOption): void {\n this.#memoryCache.delete(key);\n const storage = this.#getStorage(option);\n if (storage) {\n storage.removeItem(key);\n }\n }\n\n #deepClone<T>(val: T): T {\n if (val === undefined || val === null) return val;\n return JSON.parse(JSON.stringify(val));\n }\n}\n","import { InjectionToken } from '@angular/core';\nimport { Observable } from 'rxjs';\n\nexport interface SdCacheOption<T = any> {\n type?: 'memory' | 'session' | 'local';\n hours?: number;\n default?: T;\n args?: Record<string, any>;\n}\n\nexport interface SdCache<T = any> {\n get: () => T;\n set: (data: T) => void;\n has: () => boolean;\n remove: () => void;\n destroy: () => void;\n observer: Observable<T | undefined>;\n // Bổ sung hàm load\n load: (callback: () => Promise<T>) => Promise<T>;\n}\n\nexport interface ISdCacheConfiguration {\n convertKey?: (key: string) => string;\n set?: (key: string, value: { data: any; createdOn: Date }, option?: SdCacheOption) => Promise<void>;\n get?: (key: string, option?: SdCacheOption) => Promise<{ data: any; createdOn: Date }>;\n remove?: (key: string, option?: SdCacheOption) => Promise<void>;\n}\n\nexport const SD_CACHE_CONFIG = new InjectionToken<ISdCacheConfiguration>('sd-cache.configuration');\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sdcorejs-angular-services-cache.mjs","sources":["../../../projects/sdcorejs-angular/services/cache/src/cache.service.ts","../../../projects/sdcorejs-angular/services/cache/src/cache.model.ts","../../../projects/sdcorejs-angular/services/cache/sdcorejs-angular-services-cache.ts"],"sourcesContent":["import { Injectable } from '@angular/core';\nimport { DateUtilities } from '@sdcorejs/angular/utilities/extensions';\nimport { Utilities } from '@sdcorejs/utils/fns';\nimport { BehaviorSubject } from 'rxjs';\nimport { map } from 'rxjs/operators';\nimport { SdCache, SdCacheOption } from './cache.model';\n\ninterface CacheEntry<T> {\n data: T;\n createdOn: Date;\n}\n\n@Injectable({\n providedIn: 'root',\n})\nexport class SdCacheService {\n #memoryCache = new Map<string, CacheEntry<any>>();\n #subjects = new Map<string, BehaviorSubject<any>>();\n\n constructor() {}\n\n create<T = any>(key: string | object, option?: SdCacheOption<T>): SdCache<T> {\n if (!key) throw new Error('Key is required');\n\n const hashKey = Utilities.hash({ key });\n\n // 1. Init Subject: Load data có sẵn nếu có\n if (!this.#subjects.has(hashKey)) {\n const existingData = this.#internalGet<T>(hashKey, option);\n this.#subjects.set(hashKey, new BehaviorSubject<T | undefined>(existingData ?? option?.default));\n }\n\n const subject = this.#subjects.get(hashKey)!;\n\n // 2. Define helpers\n const get = () => {\n const val = this.#internalGet<T>(hashKey, option);\n // Fix strict type: Ép kiểu về T (chấp nhận rủi ro undefined nếu user không check)\n return (val ?? option?.default) as T;\n };\n\n const set = (data: T) => {\n this.#internalSet(hashKey, data, option);\n subject.next(data);\n };\n\n const has = () => {\n return this.#internalGet<T>(hashKey, option) !== undefined;\n };\n\n const remove = () => {\n this.#internalRemove(hashKey, option);\n subject.next(undefined);\n };\n\n const destroy = () => {\n subject.complete();\n this.#subjects.delete(hashKey);\n this.#memoryCache.delete(hashKey);\n };\n\n // 3. IMPLEMENT LOAD (Get-Or-Set Pattern)\n const load = async (callback: () => Promise<T>): Promise<T> => {\n // Check cache trước\n if (has()) {\n return get();\n }\n\n // Cache miss -> Gọi API\n const result = await callback();\n // Chỉ lưu nếu kết quả hợp lệ (khác undefined/null)\n if (result !== undefined && result !== null) {\n set(result);\n }\n return result;\n };\n\n return {\n get,\n set,\n has,\n remove,\n destroy,\n load,\n observer: subject.asObservable().pipe(map(() => get())),\n };\n }\n\n // --- PRIVATE HELPERS ---\n\n #getStorage(option?: SdCacheOption): Storage | null {\n if (option?.type === 'local') return localStorage;\n if (option?.type === 'session') return sessionStorage;\n return null;\n }\n\n #internalGet<T>(key: string, option?: SdCacheOption): T | undefined {\n let entry = this.#memoryCache.get(key);\n\n if (!entry) {\n const storage = this.#getStorage(option);\n if (storage) {\n const raw = storage.getItem(key);\n if (raw) {\n try {\n const parsed = JSON.parse(raw);\n entry = {\n data: parsed.data,\n createdOn: new Date(parsed.createdOn),\n };\n this.#memoryCache.set(key, entry);\n } catch (e) {\n this.#internalRemove(key, option);\n }\n }\n }\n }\n\n if (!entry) return undefined;\n\n if (option?.hours && entry.createdOn) {\n const expiredOn = DateUtilities.addHours(entry.createdOn, option.hours);\n if (expiredOn && expiredOn < new Date()) {\n this.#internalRemove(key, option);\n return undefined;\n }\n }\n\n return this.#deepClone(entry.data);\n }\n\n #internalSet<T>(key: string, data: T, option?: SdCacheOption): void {\n // FIX LỖI TẠI ĐÂY: Bỏ \"?? null\" để giữ nguyên kiểu T\n const clonedData = this.#deepClone(data);\n\n const entry: CacheEntry<T> = {\n data: clonedData,\n createdOn: new Date(),\n };\n\n this.#memoryCache.set(key, entry);\n\n const storage = this.#getStorage(option);\n if (storage) {\n try {\n storage.setItem(key, JSON.stringify(entry));\n } catch (e) {\n console.error('Storage quota exceeded', e);\n }\n }\n }\n\n #internalRemove(key: string, option?: SdCacheOption): void {\n this.#memoryCache.delete(key);\n const storage = this.#getStorage(option);\n if (storage) {\n storage.removeItem(key);\n }\n }\n\n #deepClone<T>(val: T): T {\n if (val === undefined || val === null) return val;\n return JSON.parse(JSON.stringify(val));\n }\n}\n","import { InjectionToken } from '@angular/core';\nimport { Observable } from 'rxjs';\n\nexport interface SdCacheOption<T = any> {\n type?: 'memory' | 'session' | 'local';\n hours?: number;\n default?: T;\n args?: Record<string, any>;\n}\n\nexport interface SdCache<T = any> {\n get: () => T;\n set: (data: T) => void;\n has: () => boolean;\n remove: () => void;\n destroy: () => void;\n observer: Observable<T | undefined>;\n // Bổ sung hàm load\n load: (callback: () => Promise<T>) => Promise<T>;\n}\n\nexport interface ISdCacheConfiguration {\n convertKey?: (key: string) => string;\n set?: (key: string, value: { data: any; createdOn: Date }, option?: SdCacheOption) => Promise<void>;\n get?: (key: string, option?: SdCacheOption) => Promise<{ data: any; createdOn: Date }>;\n remove?: (key: string, option?: SdCacheOption) => Promise<void>;\n}\n\nexport const SD_CACHE_CONFIG = new InjectionToken<ISdCacheConfiguration>('sd-cache.configuration');\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}