@smartcorehub/localization-sdk 1.0.3507 → 1.0.3529
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/README.md +96 -46
- package/dist/index.d.mts +25 -3
- package/dist/index.d.ts +25 -3
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,8 +7,9 @@ SDK TypeScript para consumir a API de localizacao da SmartCoreHub em projetos An
|
|
|
7
7
|
- Cliente tipado para localizacao
|
|
8
8
|
- Autenticacao por header X-Auth-Token
|
|
9
9
|
- Tratamento de erros padronizado
|
|
10
|
-
- Cache
|
|
10
|
+
- Cache opcional e configuravel
|
|
11
11
|
- Integracao Angular via provider e service
|
|
12
|
+
- Suporte de locale via header HTTP padrao `Accept-Language`
|
|
12
13
|
|
|
13
14
|
## Requisitos
|
|
14
15
|
|
|
@@ -31,6 +32,7 @@ import { createLocalizationClient } from '@smartcorehub/localization-sdk';
|
|
|
31
32
|
const localizationClient = createLocalizationClient({
|
|
32
33
|
baseUrl: 'https://api.smartcorehub.io/',
|
|
33
34
|
token: 'SEU_TOKEN',
|
|
35
|
+
locale: 'pt-BR',
|
|
34
36
|
timeoutMs: 10000,
|
|
35
37
|
cache: {
|
|
36
38
|
enabled: true,
|
|
@@ -47,47 +49,78 @@ const list = await localizationClient.listResources();
|
|
|
47
49
|
// list retorna objetos { key, value, language }
|
|
48
50
|
~~~
|
|
49
51
|
|
|
50
|
-
##
|
|
52
|
+
## Locale via Accept-Language
|
|
53
|
+
|
|
54
|
+
O SDK envia locale usando o header HTTP padrao `Accept-Language`.
|
|
55
|
+
|
|
56
|
+
Ordem de prioridade para definir o locale enviado:
|
|
51
57
|
|
|
52
|
-
|
|
58
|
+
- `options.locale` por chamada
|
|
59
|
+
- `locale` configurado no `createLocalizationClient`
|
|
60
|
+
- `navigator.language` no browser (fallback automatico)
|
|
61
|
+
|
|
62
|
+
Exemplo com locale global:
|
|
53
63
|
|
|
54
64
|
~~~ts
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
provideLocalization({
|
|
61
|
-
baseUrl: 'https://api.smartcorehub.io/',
|
|
62
|
-
token: 'SEU_TOKEN',
|
|
63
|
-
cache: { enabled: true, ttlMs: 60000 }
|
|
64
|
-
})
|
|
65
|
-
]
|
|
66
|
-
};
|
|
65
|
+
const client = createLocalizationClient({
|
|
66
|
+
baseUrl: 'https://api.smartcorehub.io/',
|
|
67
|
+
token: 'SEU_TOKEN',
|
|
68
|
+
locale: 'pt-BR'
|
|
69
|
+
});
|
|
67
70
|
~~~
|
|
68
71
|
|
|
69
|
-
|
|
72
|
+
Exemplo com override por chamada:
|
|
70
73
|
|
|
71
74
|
~~~ts
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
75
|
+
await client.getCurrentLanguage('home.title', { locale: 'en-US' });
|
|
76
|
+
~~~
|
|
77
|
+
|
|
78
|
+
Quando cache.enabled = true:
|
|
79
|
+
|
|
80
|
+
- a primeira chamada de getCurrentLanguage/getByLanguage usa listResources internamente e guarda em cache
|
|
81
|
+
- chamadas seguintes retornam do cache
|
|
82
|
+
- invalidateCache(key?) e clearCache() podem ser usados para invalidar/limpar
|
|
83
|
+
|
|
84
|
+
Quando cache.enabled = false:
|
|
85
|
+
|
|
86
|
+
- o fluxo segue com chamadas diretas aos endpoints
|
|
87
|
+
|
|
88
|
+
## Integracao com Angular
|
|
89
|
+
|
|
90
|
+
Forma validada e recomendada (mesma usada no exampleangular): facade com `createLocalizationClient`.
|
|
91
|
+
|
|
92
|
+
~~~ts
|
|
93
|
+
import { Injectable } from '@angular/core';
|
|
94
|
+
import { from, Observable } from 'rxjs';
|
|
95
|
+
import { createLocalizationClient, LocalizationListResource } from '@smartcorehub/localization-sdk';
|
|
96
|
+
|
|
97
|
+
@Injectable({ providedIn: 'root' })
|
|
98
|
+
export class LocalizationFacadeService {
|
|
99
|
+
private readonly client = createLocalizationClient({
|
|
100
|
+
baseUrl: 'https://api.smartcorehub.io/',
|
|
101
|
+
token: 'SEU_TOKEN',
|
|
102
|
+
cache: { enabled: false },
|
|
103
|
+
enableLogs: true
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
getCurrentLanguage(resourceKey: string): Observable<string> {
|
|
107
|
+
return from(this.client.getCurrentLanguage(resourceKey));
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
getByLanguage(resourceKey: string, languageKey: string): Observable<string> {
|
|
111
|
+
return from(this.client.getByLanguage(resourceKey, languageKey));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
listResources(): Observable<readonly LocalizationListResource[]> {
|
|
115
|
+
return from(this.client.listResources());
|
|
87
116
|
}
|
|
88
117
|
}
|
|
89
118
|
~~~
|
|
90
119
|
|
|
120
|
+
### Forma com provider Angular (opcional)
|
|
121
|
+
|
|
122
|
+
Modo alternativo: integracao via provider Angular (`provideLocalization` + `LocalizationService`).
|
|
123
|
+
|
|
91
124
|
## Integracao com React
|
|
92
125
|
|
|
93
126
|
~~~tsx
|
|
@@ -117,6 +150,8 @@ export function HomeTitle() {
|
|
|
117
150
|
- `getCurrentLanguage(resourceKey)`: retorna `string`
|
|
118
151
|
- `getByLanguage(resourceKey, languageKey)`: retorna `string`
|
|
119
152
|
- `listResources()`: retorna `LocalizationListResource[]`
|
|
153
|
+
- `invalidateCache(key?)`: invalida uma chave especifica ou todas as entradas
|
|
154
|
+
- `clearCache()`: limpa todo o cache configurado
|
|
120
155
|
|
|
121
156
|
~~~ts
|
|
122
157
|
export interface LocalizationListResource {
|
|
@@ -174,10 +209,15 @@ try {
|
|
|
174
209
|
|
|
175
210
|
## Cache configuravel
|
|
176
211
|
|
|
177
|
-
|
|
212
|
+
Quando cache.enabled = true, o provider padrao e MemoryCacheProvider.
|
|
178
213
|
|
|
179
214
|
~~~ts
|
|
180
|
-
import {
|
|
215
|
+
import {
|
|
216
|
+
createLocalizationClient,
|
|
217
|
+
MemoryCacheProvider,
|
|
218
|
+
LocalStorageCacheProvider,
|
|
219
|
+
SessionStorageCacheProvider
|
|
220
|
+
} from '@smartcorehub/localization-sdk';
|
|
181
221
|
|
|
182
222
|
const client = createLocalizationClient({
|
|
183
223
|
baseUrl: 'https://api.smartcorehub.io/',
|
|
@@ -188,6 +228,24 @@ const client = createLocalizationClient({
|
|
|
188
228
|
ttlMs: 120000
|
|
189
229
|
}
|
|
190
230
|
});
|
|
231
|
+
|
|
232
|
+
const localStorageClient = createLocalizationClient({
|
|
233
|
+
baseUrl: 'https://api.smartcorehub.io/',
|
|
234
|
+
token: 'SEU_TOKEN',
|
|
235
|
+
cache: {
|
|
236
|
+
enabled: true,
|
|
237
|
+
provider: new LocalStorageCacheProvider()
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
const sessionStorageClient = createLocalizationClient({
|
|
242
|
+
baseUrl: 'https://api.smartcorehub.io/',
|
|
243
|
+
token: 'SEU_TOKEN',
|
|
244
|
+
cache: {
|
|
245
|
+
enabled: true,
|
|
246
|
+
provider: new SessionStorageCacheProvider()
|
|
247
|
+
}
|
|
248
|
+
});
|
|
191
249
|
~~~
|
|
192
250
|
|
|
193
251
|
## Logging opcional
|
|
@@ -206,21 +264,13 @@ const client = createLocalizationClient({
|
|
|
206
264
|
|
|
207
265
|
Quando ativo, o SDK escreve trilhas em `console.debug`, `console.info` e `console.error` para facilitar o rastreio de requests, cache, sucesso e excecoes mapeadas.
|
|
208
266
|
|
|
209
|
-
##
|
|
210
|
-
|
|
211
|
-
No diretorio do pacote:
|
|
212
|
-
|
|
213
|
-
~~~bash
|
|
214
|
-
npm install
|
|
215
|
-
npm run build
|
|
216
|
-
npm test
|
|
217
|
-
~~~
|
|
218
|
-
|
|
219
|
-
Para detalhes de publicacao em pipeline classico, consulte NPM-PUBLISH-GUIDE.md.
|
|
267
|
+
## Documentacao tecnica para desenvolvimento
|
|
220
268
|
|
|
221
|
-
Para
|
|
269
|
+
Para build, testes automatizados, smoke test e validacao de pacote publicado, consulte:
|
|
222
270
|
|
|
223
|
-
|
|
271
|
+
- SDK-DEVELOPMENT-TESTING.md
|
|
272
|
+
- MANUAL-TESTING.md
|
|
273
|
+
- DEVELOPMENT-GUIDE.md
|
|
224
274
|
|
|
225
275
|
## Licensing / Licenciamento
|
|
226
276
|
|
package/dist/index.d.mts
CHANGED
|
@@ -38,6 +38,7 @@ interface HttpTransport {
|
|
|
38
38
|
interface LocalizationClientOptions {
|
|
39
39
|
readonly baseUrl: string;
|
|
40
40
|
readonly token?: string;
|
|
41
|
+
readonly locale?: string;
|
|
41
42
|
readonly authProvider?: AuthProvider;
|
|
42
43
|
readonly timeoutMs?: number;
|
|
43
44
|
readonly headers?: Readonly<Record<string, string>>;
|
|
@@ -58,11 +59,15 @@ interface LocalizationListResource {
|
|
|
58
59
|
}
|
|
59
60
|
type ResourceResponse = LocalizationListResource;
|
|
60
61
|
|
|
62
|
+
interface LocaleOverrideOptions {
|
|
63
|
+
readonly locale?: string;
|
|
64
|
+
}
|
|
61
65
|
interface LocalizationClient {
|
|
62
|
-
getCurrentLanguage(resourceKey: string): Promise<string>;
|
|
63
|
-
getByLanguage(resourceKey: string, languageKey: string): Promise<string>;
|
|
66
|
+
getCurrentLanguage(resourceKey: string, options?: LocaleOverrideOptions): Promise<string>;
|
|
67
|
+
getByLanguage(resourceKey: string, languageKey: string, options?: LocaleOverrideOptions): Promise<string>;
|
|
64
68
|
listResources(): Promise<readonly LocalizationListResource[]>;
|
|
65
69
|
invalidateCache(key?: string): Promise<void>;
|
|
70
|
+
clearCache(): Promise<void>;
|
|
66
71
|
}
|
|
67
72
|
|
|
68
73
|
declare function createLocalizationClient(options: LocalizationClientOptions): LocalizationClient;
|
|
@@ -81,6 +86,22 @@ declare class MemoryCacheProvider implements CacheProvider {
|
|
|
81
86
|
clear(): Promise<void>;
|
|
82
87
|
}
|
|
83
88
|
|
|
89
|
+
declare class StorageCacheProvider implements CacheProvider {
|
|
90
|
+
private readonly storage;
|
|
91
|
+
constructor(storage: Storage);
|
|
92
|
+
get<T>(key: string): Promise<T | null>;
|
|
93
|
+
set<T>(key: string, value: T, ttlMs?: number): Promise<void>;
|
|
94
|
+
delete(key: string): Promise<void>;
|
|
95
|
+
clear(): Promise<void>;
|
|
96
|
+
private safeParse;
|
|
97
|
+
}
|
|
98
|
+
declare class LocalStorageCacheProvider extends StorageCacheProvider {
|
|
99
|
+
constructor();
|
|
100
|
+
}
|
|
101
|
+
declare class SessionStorageCacheProvider extends StorageCacheProvider {
|
|
102
|
+
constructor();
|
|
103
|
+
}
|
|
104
|
+
|
|
84
105
|
interface FetchHttpTransportOptions {
|
|
85
106
|
readonly baseUrl: string;
|
|
86
107
|
readonly fetchFn?: typeof fetch;
|
|
@@ -137,9 +158,10 @@ declare class LocalizationService {
|
|
|
137
158
|
getByLanguage(resourceKey: string, languageKey: string): Observable<string>;
|
|
138
159
|
listResources(): Observable<readonly LocalizationListResource[]>;
|
|
139
160
|
invalidateCache(key?: string): Observable<void>;
|
|
161
|
+
clearCache(): Observable<void>;
|
|
140
162
|
}
|
|
141
163
|
|
|
142
164
|
declare const LOCALIZATION_SDK_OPTIONS: InjectionToken<LocalizationClientOptions>;
|
|
143
165
|
declare const LOCALIZATION_CLIENT: InjectionToken<LocalizationClient>;
|
|
144
166
|
|
|
145
|
-
export { type AuthProvider, type CacheProvider, FetchHttpTransport, type HttpRequest, type HttpResponse, type HttpTransport, LOCALIZATION_CLIENT, LOCALIZATION_SDK_OPTIONS, LocalizationAuthError, type LocalizationClient, type LocalizationClientOptions, LocalizationError, type LocalizationListResource, LocalizationNetworkError, LocalizationServerError, LocalizationService, LocalizationTimeoutError, MemoryCacheProvider, ResourceNotFoundError, type ResourceResponse, type SDKMiddleware, XAuthTokenProvider, createLocalizationClient, provideLocalization };
|
|
167
|
+
export { type AuthProvider, type CacheProvider, FetchHttpTransport, type HttpRequest, type HttpResponse, type HttpTransport, LOCALIZATION_CLIENT, LOCALIZATION_SDK_OPTIONS, LocalStorageCacheProvider, LocalizationAuthError, type LocalizationClient, type LocalizationClientOptions, LocalizationError, type LocalizationListResource, LocalizationNetworkError, LocalizationServerError, LocalizationService, LocalizationTimeoutError, MemoryCacheProvider, ResourceNotFoundError, type ResourceResponse, type SDKMiddleware, SessionStorageCacheProvider, StorageCacheProvider, XAuthTokenProvider, createLocalizationClient, provideLocalization };
|
package/dist/index.d.ts
CHANGED
|
@@ -38,6 +38,7 @@ interface HttpTransport {
|
|
|
38
38
|
interface LocalizationClientOptions {
|
|
39
39
|
readonly baseUrl: string;
|
|
40
40
|
readonly token?: string;
|
|
41
|
+
readonly locale?: string;
|
|
41
42
|
readonly authProvider?: AuthProvider;
|
|
42
43
|
readonly timeoutMs?: number;
|
|
43
44
|
readonly headers?: Readonly<Record<string, string>>;
|
|
@@ -58,11 +59,15 @@ interface LocalizationListResource {
|
|
|
58
59
|
}
|
|
59
60
|
type ResourceResponse = LocalizationListResource;
|
|
60
61
|
|
|
62
|
+
interface LocaleOverrideOptions {
|
|
63
|
+
readonly locale?: string;
|
|
64
|
+
}
|
|
61
65
|
interface LocalizationClient {
|
|
62
|
-
getCurrentLanguage(resourceKey: string): Promise<string>;
|
|
63
|
-
getByLanguage(resourceKey: string, languageKey: string): Promise<string>;
|
|
66
|
+
getCurrentLanguage(resourceKey: string, options?: LocaleOverrideOptions): Promise<string>;
|
|
67
|
+
getByLanguage(resourceKey: string, languageKey: string, options?: LocaleOverrideOptions): Promise<string>;
|
|
64
68
|
listResources(): Promise<readonly LocalizationListResource[]>;
|
|
65
69
|
invalidateCache(key?: string): Promise<void>;
|
|
70
|
+
clearCache(): Promise<void>;
|
|
66
71
|
}
|
|
67
72
|
|
|
68
73
|
declare function createLocalizationClient(options: LocalizationClientOptions): LocalizationClient;
|
|
@@ -81,6 +86,22 @@ declare class MemoryCacheProvider implements CacheProvider {
|
|
|
81
86
|
clear(): Promise<void>;
|
|
82
87
|
}
|
|
83
88
|
|
|
89
|
+
declare class StorageCacheProvider implements CacheProvider {
|
|
90
|
+
private readonly storage;
|
|
91
|
+
constructor(storage: Storage);
|
|
92
|
+
get<T>(key: string): Promise<T | null>;
|
|
93
|
+
set<T>(key: string, value: T, ttlMs?: number): Promise<void>;
|
|
94
|
+
delete(key: string): Promise<void>;
|
|
95
|
+
clear(): Promise<void>;
|
|
96
|
+
private safeParse;
|
|
97
|
+
}
|
|
98
|
+
declare class LocalStorageCacheProvider extends StorageCacheProvider {
|
|
99
|
+
constructor();
|
|
100
|
+
}
|
|
101
|
+
declare class SessionStorageCacheProvider extends StorageCacheProvider {
|
|
102
|
+
constructor();
|
|
103
|
+
}
|
|
104
|
+
|
|
84
105
|
interface FetchHttpTransportOptions {
|
|
85
106
|
readonly baseUrl: string;
|
|
86
107
|
readonly fetchFn?: typeof fetch;
|
|
@@ -137,9 +158,10 @@ declare class LocalizationService {
|
|
|
137
158
|
getByLanguage(resourceKey: string, languageKey: string): Observable<string>;
|
|
138
159
|
listResources(): Observable<readonly LocalizationListResource[]>;
|
|
139
160
|
invalidateCache(key?: string): Observable<void>;
|
|
161
|
+
clearCache(): Observable<void>;
|
|
140
162
|
}
|
|
141
163
|
|
|
142
164
|
declare const LOCALIZATION_SDK_OPTIONS: InjectionToken<LocalizationClientOptions>;
|
|
143
165
|
declare const LOCALIZATION_CLIENT: InjectionToken<LocalizationClient>;
|
|
144
166
|
|
|
145
|
-
export { type AuthProvider, type CacheProvider, FetchHttpTransport, type HttpRequest, type HttpResponse, type HttpTransport, LOCALIZATION_CLIENT, LOCALIZATION_SDK_OPTIONS, LocalizationAuthError, type LocalizationClient, type LocalizationClientOptions, LocalizationError, type LocalizationListResource, LocalizationNetworkError, LocalizationServerError, LocalizationService, LocalizationTimeoutError, MemoryCacheProvider, ResourceNotFoundError, type ResourceResponse, type SDKMiddleware, XAuthTokenProvider, createLocalizationClient, provideLocalization };
|
|
167
|
+
export { type AuthProvider, type CacheProvider, FetchHttpTransport, type HttpRequest, type HttpResponse, type HttpTransport, LOCALIZATION_CLIENT, LOCALIZATION_SDK_OPTIONS, LocalStorageCacheProvider, LocalizationAuthError, type LocalizationClient, type LocalizationClientOptions, LocalizationError, type LocalizationListResource, LocalizationNetworkError, LocalizationServerError, LocalizationService, LocalizationTimeoutError, MemoryCacheProvider, ResourceNotFoundError, type ResourceResponse, type SDKMiddleware, SessionStorageCacheProvider, StorageCacheProvider, XAuthTokenProvider, createLocalizationClient, provideLocalization };
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
'use strict';var core=require('@angular/core'),rxjs=require('rxjs');var H=(o,e,t,r)=>{for(var n=e,i=o.length-1,s;i>=0;i--)(s=o[i])&&(n=(s(n))||n);return n},I=(o,e)=>(t,r)=>e(t,r,o);var u=class{constructor(e){this.token=e;}async getToken(){return this.token?.trim()?this.token:null}};var d=class{constructor(){this.store=new Map;}async get(e){let t=this.store.get(e);return t?t.expiresAt!==void 0&&Date.now()>t.expiresAt?(this.store.delete(e),null):t.value:null}async set(e,t,r){let n=r&&r>0?Date.now()+r:void 0;this.store.set(e,{value:t,expiresAt:n});}async delete(e){this.store.delete(e);}async clear(){this.store.clear();}};var a=class extends Error{constructor(t,r){super(t);this.metadata=r;this.name="LocalizationError";}},p=class extends a{constructor(e="Authentication failed",t){super(e,t),this.name="LocalizationAuthError";}},h=class extends a{constructor(e="Resource not found",t){super(e,t),this.name="ResourceNotFoundError";}},g=class extends a{constructor(e="Localization server error",t){super(e,t),this.name="LocalizationServerError";}},m=class extends a{constructor(e="Network error while contacting localization API",t){super(e,t),this.name="LocalizationNetworkError";}},f=class extends a{constructor(e="Localization request timeout",t){super(e,t),this.name="LocalizationTimeoutError";}};var v=class extends Error{constructor(t,r,n){super(`Request failed with status ${t}`);this.statusCode=t;this.responseBody=r;this.responseHeaders=n;this.name="HttpStatusError";}},L=class extends Error{constructor(t){super(`Request timed out after ${t}ms`);this.timeoutMs=t;this.name="HttpTimeoutError";}},l=class extends Error{constructor(t){super("Network error while sending request");this.causeError=t;this.name="HttpNetworkError";}},y=class{constructor(e){this.options=e;this.fetchFn=e.fetchFn??globalThis.fetch.bind(globalThis);}async request(e){let t=e.timeoutMs??this.options.timeoutMs,r=new AbortController,n=setTimeout(()=>r.abort(),t),i=new Request(this.resolveUrl(e),{method:e.method,headers:{"Content-Type":"application/json",...this.options.defaultHeaders,...e.headers},body:e.body===void 0?void 0:JSON.stringify(e.body),signal:r.signal});try{let s=await this.runRequestMiddleware(i),O;try{O=await this.fetchFn(s);}catch(E){throw this.isAbortError(E)?new L(t):new l(E)}let c=await this.runResponseMiddleware(O);if(!c.ok){let E=await c.text();throw new v(c.status,E,c.headers)}let $=await this.parseResponse(c);return {status:c.status,headers:c.headers,data:$}}catch(s){throw await this.runErrorMiddleware(this.toError(s)),s}finally{clearTimeout(n);}}resolveUrl(e){let t=new URL(e.path,this.options.baseUrl);return e.query&&Object.entries(e.query).forEach(([r,n])=>{n!==void 0&&t.searchParams.set(r,String(n));}),t.toString()}async runRequestMiddleware(e){let t=e;for(let r of this.options.middleware??[])r.onRequest&&(t=await r.onRequest(t));return t}async runResponseMiddleware(e){let t=e;for(let r of this.options.middleware??[])r.onResponse&&(t=await r.onResponse(t));return t}async runErrorMiddleware(e){for(let t of this.options.middleware??[])t.onError&&await t.onError(e);}async parseResponse(e){return e.status===204?null:(e.headers.get("content-type")??"").includes("application/json")?await e.json():await e.text()}toError(e){return e instanceof Error?e:new Error("Unknown request error")}isAbortError(e){return e instanceof DOMException&&e.name==="AbortError"}};var k="localization:listResources",T=class{constructor(e,t,r){this.cacheProvider=e;this.cacheTtlMs=t;this.logger=r;}async getListResources(){this.logger.debug(`listResources:start cacheEnabled=${!!this.cacheProvider}`);let e=await this.cacheProvider?.get(k);return e?this.logger.info(`listResources:cache-hit count=${e.length}`):this.logger.debug("listResources:cache-miss"),e}async storeListResources(e){await this.cacheProvider?.set(k,e,this.cacheTtlMs),this.logger.debug(`listResources:cache-store ttlMs=${this.cacheTtlMs??0} count=${e.length}`);}async invalidate(e){if(this.logger.debug(`invalidateCache:start scope=${e??"all"}`),!this.cacheProvider){this.logger.debug("invalidateCache:skipped no-cache-provider");return}if(e){await this.cacheProvider.delete(e),this.logger.info(`invalidateCache:deleted key=${e}`);return}await this.cacheProvider.clear(),this.logger.info("invalidateCache:cleared-all");}};var b=class{constructor(e){this.logger=e;}async run(e,t){try{return await t()}catch(r){let n=this.map(r);throw this.logger.errorForOperation(e,n,r),n}}validateRequired(e,t){if(!e?.trim())throw new a(`${t} is required`)}map(e){return e instanceof a?e:e instanceof L?new f(e.message):e instanceof l?new m(e.message):e instanceof v?this.mapStatusError(e):new a("Unexpected localization SDK error")}preserveCause(e){let t=this.map(e);return t.cause??(t.cause=e),t}mapStatusError(e){return e.statusCode===401||e.statusCode===403?new p("Invalid token for localization endpoint",{statusCode:e.statusCode}):e.statusCode===404?new h("Localization resource was not found",{statusCode:e.statusCode}):e.statusCode>=500?new g("Localization API is unavailable",{statusCode:e.statusCode}):new a(e.message,{statusCode:e.statusCode})}};var q="[LocalizationSdk]",R=class{constructor(e=false){this.enabled=e;}debug(e){this.write("debug",e);}info(e){this.write("info",e);}errorForOperation(e,t,r){this.write("error",`${e}:error ${this.formatErrorDetails(t,r)}`);}formatErrorDetails(e,t){let r=this.getOriginalCause(e,t),n=this.getErrorMessage(r),i=[`type=${e.name}`,`message=${e.message}`];return n&&n!==e.message&&i.push(`original=${n}`),e instanceof a&&e.metadata?.statusCode&&i.push(`statusCode=${e.metadata.statusCode}`),i.join(" ")}getOriginalCause(e,t){let n=e.cause??t;return n instanceof l?n.causeError:n}getErrorMessage(e){return e instanceof Error?e.message:typeof e=="string"?e:"unknown-error"}write(e,t){if(!this.enabled)return;let r=`${q} ${t}`;switch(e){case "debug":console.debug(r);break;case "info":console.info(r);break;case "error":console.error(r);break}}};var z=class{constructor(e){this.dependencies=e;this.logger=new R(e.enableLogs),this.cacheHelper=new T(e.cacheProvider,e.cacheTtlMs,this.logger),this.errorHandler=new b(this.logger);}async getCurrentLanguage(e){return this.errorHandler.run("getCurrentLanguage",async()=>{this.errorHandler.validateRequired(e,"resourceKey"),this.logger.debug(`getCurrentLanguage:start resourceKey=${e}`);let t=await this.send({method:"GET",path:"api/localization/client/currentlanguage",query:{resourceKey:e}});return this.logger.info(`getCurrentLanguage:success resourceKey=${e} hasValue=${!!t}`),t??""})}async getByLanguage(e,t){return this.errorHandler.run("getByLanguage",async()=>{this.errorHandler.validateRequired(e,"resourceKey"),this.errorHandler.validateRequired(t,"languageKey"),this.logger.debug(`getByLanguage:start resourceKey=${e} languageKey=${t}`);let r=await this.send({method:"GET",path:"api/localization/client/bylanguage",query:{resourceKey:e,languageKey:t}});return this.logger.info(`getByLanguage:success resourceKey=${e} languageKey=${t} hasValue=${!!r}`),r??""})}async listResources(){return this.errorHandler.run("listResources",async()=>{let e=await this.cacheHelper.getListResources();if(e)return e;let r=(await this.send({method:"GET",path:"api/localization/client/list"})??[]).map(n=>({key:n.resourceKey,value:n.value,language:n.languageKey}));return await this.cacheHelper.storeListResources(r),this.logger.info(`listResources:success count=${r.length}`),r})}async invalidateCache(e){return this.errorHandler.run("invalidateCache",async()=>this.cacheHelper.invalidate(e))}async send(e){try{let t=await this.dependencies.authProvider?.getToken(),r=t?{...e.headers,"X-Auth-Token":t}:e.headers;this.logger.debug(`request:start method=${e.method} path=${e.path} hasAuth=${!!t}`);let n=await this.dependencies.httpTransport.request({...e,headers:r});return this.logger.debug(`request:success method=${e.method} path=${e.path} status=${n.status}`),n.data}catch(t){throw this.errorHandler.preserveCause(t)}}};function x(o){D(o);let t=o.cache?.enabled??false?o.cache?.provider??new d:void 0,r=new y({baseUrl:o.baseUrl,fetchFn:o.fetchFn,timeoutMs:o.timeoutMs??1e4,defaultHeaders:o.headers,middleware:o.middleware});return new z({httpTransport:r,authProvider:o.authProvider??(o.token?new u(o.token):void 0),cacheProvider:t,cacheTtlMs:o.cache?.ttlMs,enableLogs:o.enableLogs??false})}function D(o){if(!o?.baseUrl?.trim())throw new a("baseUrl is required to create localization client")}var A=new core.InjectionToken("LOCALIZATION_SDK_OPTIONS"),w=new core.InjectionToken("LOCALIZATION_CLIENT");function F(o){return core.makeEnvironmentProviders([{provide:A,useValue:o},{provide:w,useFactory:()=>x(o)}])}exports.LocalizationService=class C{constructor(e){this.client=e;}getCurrentLanguage(e){return rxjs.from(this.client.getCurrentLanguage(e))}getByLanguage(e,t){return rxjs.from(this.client.getByLanguage(e,t))}listResources(){return rxjs.from(this.client.listResources())}invalidateCache(e){return rxjs.from(this.client.invalidateCache(e))}};exports.LocalizationService=H([core.Injectable({providedIn:"root"}),I(0,core.Inject(w))],exports.LocalizationService);exports.FetchHttpTransport=y;exports.LOCALIZATION_CLIENT=w;exports.LOCALIZATION_SDK_OPTIONS=A;exports.LocalizationAuthError=p;exports.LocalizationError=a;exports.LocalizationNetworkError=m;exports.LocalizationServerError=g;exports.LocalizationTimeoutError=f;exports.MemoryCacheProvider=d;exports.ResourceNotFoundError=h;exports.XAuthTokenProvider=u;exports.createLocalizationClient=x;exports.provideLocalization=F;
|
|
1
|
+
'use strict';var core=require('@angular/core'),rxjs=require('rxjs');var k=(o,e,r,t)=>{for(var n=e,i=o.length-1,s;i>=0;i--)(s=o[i])&&(n=(s(n))||n);return n},$=(o,e)=>(r,t)=>e(r,t,o);var u=class{constructor(e){this.token=e;}async getToken(){return this.token?.trim()?this.token:null}};var d=class{constructor(){this.store=new Map;}async get(e){let r=this.store.get(e);return r?r.expiresAt!==void 0&&Date.now()>r.expiresAt?(this.store.delete(e),null):r.value:null}async set(e,r,t){let n=t&&t>0?Date.now()+t:void 0;this.store.set(e,{value:r,expiresAt:n});}async delete(e){this.store.delete(e);}async clear(){this.store.clear();}};var a=class extends Error{constructor(r,t){super(r);this.metadata=t;this.name="LocalizationError";}},h=class extends a{constructor(e="Authentication failed",r){super(e,r),this.name="LocalizationAuthError";}},p=class extends a{constructor(e="Resource not found",r){super(e,r),this.name="ResourceNotFoundError";}},g=class extends a{constructor(e="Localization server error",r){super(e,r),this.name="LocalizationServerError";}},m=class extends a{constructor(e="Network error while contacting localization API",r){super(e,r),this.name="LocalizationNetworkError";}},f=class extends a{constructor(e="Localization request timeout",r){super(e,r),this.name="LocalizationTimeoutError";}};var v=class extends Error{constructor(r,t,n){super(`Request failed with status ${r}`);this.statusCode=r;this.responseBody=t;this.responseHeaders=n;this.name="HttpStatusError";}},y=class extends Error{constructor(r){super(`Request timed out after ${r}ms`);this.timeoutMs=r;this.name="HttpTimeoutError";}},l=class extends Error{constructor(r){super("Network error while sending request");this.causeError=r;this.name="HttpNetworkError";}},L=class{constructor(e){this.options=e;this.fetchFn=e.fetchFn??globalThis.fetch.bind(globalThis);}async request(e){let r=e.timeoutMs??this.options.timeoutMs,t=new AbortController,n=setTimeout(()=>t.abort(),r),i=new Request(this.resolveUrl(e),{method:e.method,headers:{"Content-Type":"application/json",...this.options.defaultHeaders,...e.headers},body:e.body===void 0?void 0:JSON.stringify(e.body),signal:t.signal});try{let s=await this.runRequestMiddleware(i),S;try{S=await this.fetchFn(s);}catch(b){throw this.isAbortError(b)?new y(r):new l(b)}let c=await this.runResponseMiddleware(S);if(!c.ok){let b=await c.text();throw new v(c.status,b,c.headers)}let D=await this.parseResponse(c);return {status:c.status,headers:c.headers,data:D}}catch(s){throw await this.runErrorMiddleware(this.toError(s)),s}finally{clearTimeout(n);}}resolveUrl(e){let r=new URL(e.path,this.options.baseUrl);return e.query&&Object.entries(e.query).forEach(([t,n])=>{n!==void 0&&r.searchParams.set(t,String(n));}),r.toString()}async runRequestMiddleware(e){let r=e;for(let t of this.options.middleware??[])t.onRequest&&(r=await t.onRequest(r));return r}async runResponseMiddleware(e){let r=e;for(let t of this.options.middleware??[])t.onResponse&&(r=await t.onResponse(r));return r}async runErrorMiddleware(e){for(let r of this.options.middleware??[])r.onError&&await r.onError(e);}async parseResponse(e){return e.status===204?null:(e.headers.get("content-type")??"").includes("application/json")?await e.json():await e.text()}toError(e){return e instanceof Error?e:new Error("Unknown request error")}isAbortError(e){return e instanceof DOMException&&e.name==="AbortError"}};var M="localization:listResources",R=class{constructor(e,r,t){this.cacheProvider=e;this.cacheTtlMs=r;this.logger=t;}async getListResources(){this.logger.debug(`listResources:start cacheEnabled=${!!this.cacheProvider}`);let e=await this.cacheProvider?.get(M);return e?this.logger.info(`listResources:cache-hit count=${e.length}`):this.logger.debug("listResources:cache-miss"),e}async storeListResources(e){await this.cacheProvider?.set(M,e,this.cacheTtlMs),this.logger.debug(`listResources:cache-store ttlMs=${this.cacheTtlMs??0} count=${e.length}`);}async invalidate(e){if(this.logger.debug(`invalidateCache:start scope=${e??"all"}`),!this.cacheProvider){this.logger.debug("invalidateCache:skipped no-cache-provider");return}if(e){await this.cacheProvider.delete(e),this.logger.info(`invalidateCache:deleted key=${e}`);return}await this.cacheProvider.clear(),this.logger.info("invalidateCache:cleared-all");}};var z=class{constructor(e){this.logger=e;}async run(e,r){try{return await r()}catch(t){let n=this.map(t);throw this.logger.errorForOperation(e,n,t),n}}validateRequired(e,r){if(!e?.trim())throw new a(`${r} is required`)}map(e){return e instanceof a?e:e instanceof y?new f(e.message):e instanceof l?new m(e.message):e instanceof v?this.mapStatusError(e):new a("Unexpected localization SDK error")}preserveCause(e){let r=this.map(e);return r.cause??(r.cause=e),r}mapStatusError(e){return e.statusCode===401||e.statusCode===403?new h("Invalid token for localization endpoint",{statusCode:e.statusCode}):e.statusCode===404?new p("Localization resource was not found",{statusCode:e.statusCode}):e.statusCode>=500?new g("Localization API is unavailable",{statusCode:e.statusCode}):new a(e.message,{statusCode:e.statusCode})}};var K="[LocalizationSdk]",P=class{constructor(e=false){this.enabled=e;}debug(e){this.write("debug",e);}info(e){this.write("info",e);}errorForOperation(e,r,t){this.write("error",`${e}:error ${this.formatErrorDetails(r,t)}`);}formatErrorDetails(e,r){let t=this.getOriginalCause(e,r),n=this.getErrorMessage(t),i=[`type=${e.name}`,`message=${e.message}`];return n&&n!==e.message&&i.push(`original=${n}`),e instanceof a&&e.metadata?.statusCode&&i.push(`statusCode=${e.metadata.statusCode}`),i.join(" ")}getOriginalCause(e,r){let n=e.cause??r;return n instanceof l?n.causeError:n}getErrorMessage(e){return e instanceof Error?e.message:typeof e=="string"?e:"unknown-error"}write(e,r){if(!this.enabled)return;let t=this.resolveIcon(e,r),n=`${K} ${t} ${r}`;switch(e){case "debug":console.debug(n);break;case "info":console.info(n);break;case "error":console.error(n);break}}resolveIcon(e,r){return e==="error"||r.includes(":error")?"\u274C":r.includes("request:start")?"\u{1F310}":r.includes("request:success")?"\u{1F4E1}":r.includes("cache-hit")?"\u26A1":r.includes("cache-miss")?"\u{1F4ED}":r.includes("cache-store")?"\u{1F4BE}":r.includes("invalidateCache")?"\u{1F9F9}":r.includes("listResources")?"\u{1F4DA}":r.includes("getByLanguage")?"\u{1F30D}":r.includes("getCurrentLanguage")?"\u{1F22F}":r.includes(":start")?"\u{1F680}":r.includes(":success")?"\u2705":e==="info"?"\u2139\uFE0F":"\u{1F50E}"}};var x=class{constructor(e){this.dependencies=e;this.logger=new P(e.enableLogs),this.cacheHelper=new R(e.cacheProvider,e.cacheTtlMs,this.logger),this.errorHandler=new z(this.logger);}async getCurrentLanguage(e,r){return this.errorHandler.run("getCurrentLanguage",async()=>{if(this.errorHandler.validateRequired(e,"resourceKey"),this.logger.debug(`getCurrentLanguage:start resourceKey=${e}`),this.dependencies.cacheProvider){let n=await this.resolveFromCachedResources(e);return this.logger.info(`getCurrentLanguage:cache-result resourceKey=${e} hasValue=${!!n}`),n}let t=await this.send({method:"GET",path:"api/localization/client/currentlanguage",query:{resourceKey:e},headers:this.resolveLocaleHeader(r)});return this.logger.info(`getCurrentLanguage:success resourceKey=${e} hasValue=${!!t}`),t??""})}async getByLanguage(e,r,t){return this.errorHandler.run("getByLanguage",async()=>{if(this.errorHandler.validateRequired(e,"resourceKey"),this.errorHandler.validateRequired(r,"languageKey"),this.logger.debug(`getByLanguage:start resourceKey=${e} languageKey=${r}`),this.dependencies.cacheProvider){let i=await this.resolveFromCachedResources(e,r);return this.logger.info(`getByLanguage:cache-result resourceKey=${e} languageKey=${r} hasValue=${!!i}`),i}let n=await this.send({method:"GET",path:"api/localization/client/bylanguage",query:{resourceKey:e,languageKey:r},headers:this.resolveLocaleHeader(t)});return this.logger.info(`getByLanguage:success resourceKey=${e} languageKey=${r} hasValue=${!!n}`),n??""})}async listResources(){return this.errorHandler.run("listResources",async()=>{let e=await this.cacheHelper.getListResources();if(e)return e;let t=(await this.send({method:"GET",path:"api/localization/client/list"})??[]).map(n=>({key:n.resourceKey,value:n.value,language:n.languageKey}));return await this.cacheHelper.storeListResources(t),this.logger.info(`listResources:success count=${t.length}`),t})}async invalidateCache(e){return this.errorHandler.run("invalidateCache",async()=>this.cacheHelper.invalidate(e))}async clearCache(){return this.errorHandler.run("clearCache",async()=>this.cacheHelper.invalidate())}async resolveFromCachedResources(e,r){return (await this.listResources()).find(i=>i.key!==e?false:r===void 0?true:i.language===r)?.value??""}async send(e){try{let r=await this.dependencies.authProvider?.getToken(),t=r?{...e.headers,"X-Auth-Token":r}:e.headers;this.logger.debug(`request:start method=${e.method} path=${e.path} hasAuth=${!!r}`);let n=await this.dependencies.httpTransport.request({...e,headers:t});return this.logger.debug(`request:success method=${e.method} path=${e.path} status=${n.status}`),n.data}catch(r){throw this.errorHandler.preserveCause(r)}}resolveLocaleHeader(e){let r=e?.locale?.trim()||this.dependencies.locale?.trim()||this.resolveBrowserLocale();if(r)return {"Accept-Language":r}}resolveBrowserLocale(){return globalThis.navigator?.language?.trim()||void 0}};function O(o){_(o);let r=o.cache?.enabled??false?o.cache?.provider??new d:void 0,t=new L({baseUrl:o.baseUrl,fetchFn:o.fetchFn,timeoutMs:o.timeoutMs??1e4,defaultHeaders:o.headers,middleware:o.middleware});return new x({httpTransport:t,authProvider:o.authProvider??(o.token?new u(o.token):void 0),locale:o.locale,cacheProvider:r,cacheTtlMs:o.cache?.ttlMs,enableLogs:o.enableLogs??false})}function _(o){if(!o?.baseUrl?.trim())throw new a("baseUrl is required to create localization client")}var w=class{constructor(e){this.storage=e;}async get(e){let r=this.storage.getItem(e);if(!r)return null;let t=this.safeParse(r);return t?t.expiresAt!==void 0&&Date.now()>t.expiresAt?(this.storage.removeItem(e),null):t.value:(this.storage.removeItem(e),null)}async set(e,r,t){let n=t&&t>0?Date.now()+t:void 0,i={value:r,expiresAt:n};this.storage.setItem(e,JSON.stringify(i));}async delete(e){this.storage.removeItem(e);}async clear(){this.storage.clear();}safeParse(e){try{return JSON.parse(e)}catch{return null}}},A=class extends w{constructor(){super(N("localStorage"));}},H=class extends w{constructor(){super(N("sessionStorage"));}};function N(o){let e=typeof window>"u"?void 0:window[o];return e||U()}function U(){let o=new Map;return {get length(){return o.size},clear:()=>{o.clear();},getItem:e=>o.get(e)??null,key:e=>Array.from(o.keys())[e]??null,removeItem:e=>{o.delete(e);},setItem:(e,r)=>{o.set(e,r);}}}var I=new core.InjectionToken("LOCALIZATION_SDK_OPTIONS"),C=new core.InjectionToken("LOCALIZATION_CLIENT");function Z(o){return core.makeEnvironmentProviders([{provide:I,useValue:o},{provide:C,useFactory:()=>O(o)}])}exports.LocalizationService=class E{constructor(e){this.client=e;}getCurrentLanguage(e){return rxjs.from(this.client.getCurrentLanguage(e))}getByLanguage(e,r){return rxjs.from(this.client.getByLanguage(e,r))}listResources(){return rxjs.from(this.client.listResources())}invalidateCache(e){return rxjs.from(this.client.invalidateCache(e))}clearCache(){return rxjs.from(this.client.clearCache())}};exports.LocalizationService=k([core.Injectable({providedIn:"root"}),$(0,core.Inject(C))],exports.LocalizationService);exports.FetchHttpTransport=L;exports.LOCALIZATION_CLIENT=C;exports.LOCALIZATION_SDK_OPTIONS=I;exports.LocalStorageCacheProvider=A;exports.LocalizationAuthError=h;exports.LocalizationError=a;exports.LocalizationNetworkError=m;exports.LocalizationServerError=g;exports.LocalizationTimeoutError=f;exports.MemoryCacheProvider=d;exports.ResourceNotFoundError=p;exports.SessionStorageCacheProvider=H;exports.StorageCacheProvider=w;exports.XAuthTokenProvider=u;exports.createLocalizationClient=O;exports.provideLocalization=Z;
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import {InjectionToken,Injectable,Inject,makeEnvironmentProviders}from'@angular/core';import {from}from'rxjs';var H=(o,e,t,r)=>{for(var n=e,i=o.length-1,s;i>=0;i--)(s=o[i])&&(n=(s(n))||n);return n},I=(o,e)=>(t,r)=>e(t,r,o);var u=class{constructor(e){this.token=e;}async getToken(){return this.token?.trim()?this.token:null}};var d=class{constructor(){this.store=new Map;}async get(e){let t=this.store.get(e);return t?t.expiresAt!==void 0&&Date.now()>t.expiresAt?(this.store.delete(e),null):t.value:null}async set(e,t,r){let n=r&&r>0?Date.now()+r:void 0;this.store.set(e,{value:t,expiresAt:n});}async delete(e){this.store.delete(e);}async clear(){this.store.clear();}};var a=class extends Error{constructor(t,r){super(t);this.metadata=r;this.name="LocalizationError";}},p=class extends a{constructor(e="Authentication failed",t){super(e,t),this.name="LocalizationAuthError";}},h=class extends a{constructor(e="Resource not found",t){super(e,t),this.name="ResourceNotFoundError";}},g=class extends a{constructor(e="Localization server error",t){super(e,t),this.name="LocalizationServerError";}},m=class extends a{constructor(e="Network error while contacting localization API",t){super(e,t),this.name="LocalizationNetworkError";}},f=class extends a{constructor(e="Localization request timeout",t){super(e,t),this.name="LocalizationTimeoutError";}};var v=class extends Error{constructor(t,r,n){super(`Request failed with status ${t}`);this.statusCode=t;this.responseBody=r;this.responseHeaders=n;this.name="HttpStatusError";}},L=class extends Error{constructor(t){super(`Request timed out after ${t}ms`);this.timeoutMs=t;this.name="HttpTimeoutError";}},l=class extends Error{constructor(t){super("Network error while sending request");this.causeError=t;this.name="HttpNetworkError";}},y=class{constructor(e){this.options=e;this.fetchFn=e.fetchFn??globalThis.fetch.bind(globalThis);}async request(e){let t=e.timeoutMs??this.options.timeoutMs,r=new AbortController,n=setTimeout(()=>r.abort(),t),i=new Request(this.resolveUrl(e),{method:e.method,headers:{"Content-Type":"application/json",...this.options.defaultHeaders,...e.headers},body:e.body===void 0?void 0:JSON.stringify(e.body),signal:r.signal});try{let s=await this.runRequestMiddleware(i),O;try{O=await this.fetchFn(s);}catch(E){throw this.isAbortError(E)?new L(t):new l(E)}let c=await this.runResponseMiddleware(O);if(!c.ok){let E=await c.text();throw new v(c.status,E,c.headers)}let $=await this.parseResponse(c);return {status:c.status,headers:c.headers,data:$}}catch(s){throw await this.runErrorMiddleware(this.toError(s)),s}finally{clearTimeout(n);}}resolveUrl(e){let t=new URL(e.path,this.options.baseUrl);return e.query&&Object.entries(e.query).forEach(([r,n])=>{n!==void 0&&t.searchParams.set(r,String(n));}),t.toString()}async runRequestMiddleware(e){let t=e;for(let r of this.options.middleware??[])r.onRequest&&(t=await r.onRequest(t));return t}async runResponseMiddleware(e){let t=e;for(let r of this.options.middleware??[])r.onResponse&&(t=await r.onResponse(t));return t}async runErrorMiddleware(e){for(let t of this.options.middleware??[])t.onError&&await t.onError(e);}async parseResponse(e){return e.status===204?null:(e.headers.get("content-type")??"").includes("application/json")?await e.json():await e.text()}toError(e){return e instanceof Error?e:new Error("Unknown request error")}isAbortError(e){return e instanceof DOMException&&e.name==="AbortError"}};var k="localization:listResources",T=class{constructor(e,t,r){this.cacheProvider=e;this.cacheTtlMs=t;this.logger=r;}async getListResources(){this.logger.debug(`listResources:start cacheEnabled=${!!this.cacheProvider}`);let e=await this.cacheProvider?.get(k);return e?this.logger.info(`listResources:cache-hit count=${e.length}`):this.logger.debug("listResources:cache-miss"),e}async storeListResources(e){await this.cacheProvider?.set(k,e,this.cacheTtlMs),this.logger.debug(`listResources:cache-store ttlMs=${this.cacheTtlMs??0} count=${e.length}`);}async invalidate(e){if(this.logger.debug(`invalidateCache:start scope=${e??"all"}`),!this.cacheProvider){this.logger.debug("invalidateCache:skipped no-cache-provider");return}if(e){await this.cacheProvider.delete(e),this.logger.info(`invalidateCache:deleted key=${e}`);return}await this.cacheProvider.clear(),this.logger.info("invalidateCache:cleared-all");}};var b=class{constructor(e){this.logger=e;}async run(e,t){try{return await t()}catch(r){let n=this.map(r);throw this.logger.errorForOperation(e,n,r),n}}validateRequired(e,t){if(!e?.trim())throw new a(`${t} is required`)}map(e){return e instanceof a?e:e instanceof L?new f(e.message):e instanceof l?new m(e.message):e instanceof v?this.mapStatusError(e):new a("Unexpected localization SDK error")}preserveCause(e){let t=this.map(e);return t.cause??(t.cause=e),t}mapStatusError(e){return e.statusCode===401||e.statusCode===403?new p("Invalid token for localization endpoint",{statusCode:e.statusCode}):e.statusCode===404?new h("Localization resource was not found",{statusCode:e.statusCode}):e.statusCode>=500?new g("Localization API is unavailable",{statusCode:e.statusCode}):new a(e.message,{statusCode:e.statusCode})}};var q="[LocalizationSdk]",R=class{constructor(e=false){this.enabled=e;}debug(e){this.write("debug",e);}info(e){this.write("info",e);}errorForOperation(e,t,r){this.write("error",`${e}:error ${this.formatErrorDetails(t,r)}`);}formatErrorDetails(e,t){let r=this.getOriginalCause(e,t),n=this.getErrorMessage(r),i=[`type=${e.name}`,`message=${e.message}`];return n&&n!==e.message&&i.push(`original=${n}`),e instanceof a&&e.metadata?.statusCode&&i.push(`statusCode=${e.metadata.statusCode}`),i.join(" ")}getOriginalCause(e,t){let n=e.cause??t;return n instanceof l?n.causeError:n}getErrorMessage(e){return e instanceof Error?e.message:typeof e=="string"?e:"unknown-error"}write(e,t){if(!this.enabled)return;let r=`${q} ${t}`;switch(e){case "debug":console.debug(r);break;case "info":console.info(r);break;case "error":console.error(r);break}}};var z=class{constructor(e){this.dependencies=e;this.logger=new R(e.enableLogs),this.cacheHelper=new T(e.cacheProvider,e.cacheTtlMs,this.logger),this.errorHandler=new b(this.logger);}async getCurrentLanguage(e){return this.errorHandler.run("getCurrentLanguage",async()=>{this.errorHandler.validateRequired(e,"resourceKey"),this.logger.debug(`getCurrentLanguage:start resourceKey=${e}`);let t=await this.send({method:"GET",path:"api/localization/client/currentlanguage",query:{resourceKey:e}});return this.logger.info(`getCurrentLanguage:success resourceKey=${e} hasValue=${!!t}`),t??""})}async getByLanguage(e,t){return this.errorHandler.run("getByLanguage",async()=>{this.errorHandler.validateRequired(e,"resourceKey"),this.errorHandler.validateRequired(t,"languageKey"),this.logger.debug(`getByLanguage:start resourceKey=${e} languageKey=${t}`);let r=await this.send({method:"GET",path:"api/localization/client/bylanguage",query:{resourceKey:e,languageKey:t}});return this.logger.info(`getByLanguage:success resourceKey=${e} languageKey=${t} hasValue=${!!r}`),r??""})}async listResources(){return this.errorHandler.run("listResources",async()=>{let e=await this.cacheHelper.getListResources();if(e)return e;let r=(await this.send({method:"GET",path:"api/localization/client/list"})??[]).map(n=>({key:n.resourceKey,value:n.value,language:n.languageKey}));return await this.cacheHelper.storeListResources(r),this.logger.info(`listResources:success count=${r.length}`),r})}async invalidateCache(e){return this.errorHandler.run("invalidateCache",async()=>this.cacheHelper.invalidate(e))}async send(e){try{let t=await this.dependencies.authProvider?.getToken(),r=t?{...e.headers,"X-Auth-Token":t}:e.headers;this.logger.debug(`request:start method=${e.method} path=${e.path} hasAuth=${!!t}`);let n=await this.dependencies.httpTransport.request({...e,headers:r});return this.logger.debug(`request:success method=${e.method} path=${e.path} status=${n.status}`),n.data}catch(t){throw this.errorHandler.preserveCause(t)}}};function x(o){D(o);let t=o.cache?.enabled??false?o.cache?.provider??new d:void 0,r=new y({baseUrl:o.baseUrl,fetchFn:o.fetchFn,timeoutMs:o.timeoutMs??1e4,defaultHeaders:o.headers,middleware:o.middleware});return new z({httpTransport:r,authProvider:o.authProvider??(o.token?new u(o.token):void 0),cacheProvider:t,cacheTtlMs:o.cache?.ttlMs,enableLogs:o.enableLogs??false})}function D(o){if(!o?.baseUrl?.trim())throw new a("baseUrl is required to create localization client")}var A=new InjectionToken("LOCALIZATION_SDK_OPTIONS"),w=new InjectionToken("LOCALIZATION_CLIENT");function F(o){return makeEnvironmentProviders([{provide:A,useValue:o},{provide:w,useFactory:()=>x(o)}])}var C=class{constructor(e){this.client=e;}getCurrentLanguage(e){return from(this.client.getCurrentLanguage(e))}getByLanguage(e,t){return from(this.client.getByLanguage(e,t))}listResources(){return from(this.client.listResources())}invalidateCache(e){return from(this.client.invalidateCache(e))}};C=H([Injectable({providedIn:"root"}),I(0,Inject(w))],C);export{y as FetchHttpTransport,w as LOCALIZATION_CLIENT,A as LOCALIZATION_SDK_OPTIONS,p as LocalizationAuthError,a as LocalizationError,m as LocalizationNetworkError,g as LocalizationServerError,C as LocalizationService,f as LocalizationTimeoutError,d as MemoryCacheProvider,h as ResourceNotFoundError,u as XAuthTokenProvider,x as createLocalizationClient,F as provideLocalization};
|
|
1
|
+
import {InjectionToken,Injectable,Inject,makeEnvironmentProviders}from'@angular/core';import {from}from'rxjs';var k=(o,e,r,t)=>{for(var n=e,i=o.length-1,s;i>=0;i--)(s=o[i])&&(n=(s(n))||n);return n},$=(o,e)=>(r,t)=>e(r,t,o);var u=class{constructor(e){this.token=e;}async getToken(){return this.token?.trim()?this.token:null}};var d=class{constructor(){this.store=new Map;}async get(e){let r=this.store.get(e);return r?r.expiresAt!==void 0&&Date.now()>r.expiresAt?(this.store.delete(e),null):r.value:null}async set(e,r,t){let n=t&&t>0?Date.now()+t:void 0;this.store.set(e,{value:r,expiresAt:n});}async delete(e){this.store.delete(e);}async clear(){this.store.clear();}};var a=class extends Error{constructor(r,t){super(r);this.metadata=t;this.name="LocalizationError";}},h=class extends a{constructor(e="Authentication failed",r){super(e,r),this.name="LocalizationAuthError";}},p=class extends a{constructor(e="Resource not found",r){super(e,r),this.name="ResourceNotFoundError";}},g=class extends a{constructor(e="Localization server error",r){super(e,r),this.name="LocalizationServerError";}},m=class extends a{constructor(e="Network error while contacting localization API",r){super(e,r),this.name="LocalizationNetworkError";}},f=class extends a{constructor(e="Localization request timeout",r){super(e,r),this.name="LocalizationTimeoutError";}};var v=class extends Error{constructor(r,t,n){super(`Request failed with status ${r}`);this.statusCode=r;this.responseBody=t;this.responseHeaders=n;this.name="HttpStatusError";}},y=class extends Error{constructor(r){super(`Request timed out after ${r}ms`);this.timeoutMs=r;this.name="HttpTimeoutError";}},l=class extends Error{constructor(r){super("Network error while sending request");this.causeError=r;this.name="HttpNetworkError";}},L=class{constructor(e){this.options=e;this.fetchFn=e.fetchFn??globalThis.fetch.bind(globalThis);}async request(e){let r=e.timeoutMs??this.options.timeoutMs,t=new AbortController,n=setTimeout(()=>t.abort(),r),i=new Request(this.resolveUrl(e),{method:e.method,headers:{"Content-Type":"application/json",...this.options.defaultHeaders,...e.headers},body:e.body===void 0?void 0:JSON.stringify(e.body),signal:t.signal});try{let s=await this.runRequestMiddleware(i),S;try{S=await this.fetchFn(s);}catch(b){throw this.isAbortError(b)?new y(r):new l(b)}let c=await this.runResponseMiddleware(S);if(!c.ok){let b=await c.text();throw new v(c.status,b,c.headers)}let D=await this.parseResponse(c);return {status:c.status,headers:c.headers,data:D}}catch(s){throw await this.runErrorMiddleware(this.toError(s)),s}finally{clearTimeout(n);}}resolveUrl(e){let r=new URL(e.path,this.options.baseUrl);return e.query&&Object.entries(e.query).forEach(([t,n])=>{n!==void 0&&r.searchParams.set(t,String(n));}),r.toString()}async runRequestMiddleware(e){let r=e;for(let t of this.options.middleware??[])t.onRequest&&(r=await t.onRequest(r));return r}async runResponseMiddleware(e){let r=e;for(let t of this.options.middleware??[])t.onResponse&&(r=await t.onResponse(r));return r}async runErrorMiddleware(e){for(let r of this.options.middleware??[])r.onError&&await r.onError(e);}async parseResponse(e){return e.status===204?null:(e.headers.get("content-type")??"").includes("application/json")?await e.json():await e.text()}toError(e){return e instanceof Error?e:new Error("Unknown request error")}isAbortError(e){return e instanceof DOMException&&e.name==="AbortError"}};var M="localization:listResources",R=class{constructor(e,r,t){this.cacheProvider=e;this.cacheTtlMs=r;this.logger=t;}async getListResources(){this.logger.debug(`listResources:start cacheEnabled=${!!this.cacheProvider}`);let e=await this.cacheProvider?.get(M);return e?this.logger.info(`listResources:cache-hit count=${e.length}`):this.logger.debug("listResources:cache-miss"),e}async storeListResources(e){await this.cacheProvider?.set(M,e,this.cacheTtlMs),this.logger.debug(`listResources:cache-store ttlMs=${this.cacheTtlMs??0} count=${e.length}`);}async invalidate(e){if(this.logger.debug(`invalidateCache:start scope=${e??"all"}`),!this.cacheProvider){this.logger.debug("invalidateCache:skipped no-cache-provider");return}if(e){await this.cacheProvider.delete(e),this.logger.info(`invalidateCache:deleted key=${e}`);return}await this.cacheProvider.clear(),this.logger.info("invalidateCache:cleared-all");}};var z=class{constructor(e){this.logger=e;}async run(e,r){try{return await r()}catch(t){let n=this.map(t);throw this.logger.errorForOperation(e,n,t),n}}validateRequired(e,r){if(!e?.trim())throw new a(`${r} is required`)}map(e){return e instanceof a?e:e instanceof y?new f(e.message):e instanceof l?new m(e.message):e instanceof v?this.mapStatusError(e):new a("Unexpected localization SDK error")}preserveCause(e){let r=this.map(e);return r.cause??(r.cause=e),r}mapStatusError(e){return e.statusCode===401||e.statusCode===403?new h("Invalid token for localization endpoint",{statusCode:e.statusCode}):e.statusCode===404?new p("Localization resource was not found",{statusCode:e.statusCode}):e.statusCode>=500?new g("Localization API is unavailable",{statusCode:e.statusCode}):new a(e.message,{statusCode:e.statusCode})}};var K="[LocalizationSdk]",P=class{constructor(e=false){this.enabled=e;}debug(e){this.write("debug",e);}info(e){this.write("info",e);}errorForOperation(e,r,t){this.write("error",`${e}:error ${this.formatErrorDetails(r,t)}`);}formatErrorDetails(e,r){let t=this.getOriginalCause(e,r),n=this.getErrorMessage(t),i=[`type=${e.name}`,`message=${e.message}`];return n&&n!==e.message&&i.push(`original=${n}`),e instanceof a&&e.metadata?.statusCode&&i.push(`statusCode=${e.metadata.statusCode}`),i.join(" ")}getOriginalCause(e,r){let n=e.cause??r;return n instanceof l?n.causeError:n}getErrorMessage(e){return e instanceof Error?e.message:typeof e=="string"?e:"unknown-error"}write(e,r){if(!this.enabled)return;let t=this.resolveIcon(e,r),n=`${K} ${t} ${r}`;switch(e){case "debug":console.debug(n);break;case "info":console.info(n);break;case "error":console.error(n);break}}resolveIcon(e,r){return e==="error"||r.includes(":error")?"\u274C":r.includes("request:start")?"\u{1F310}":r.includes("request:success")?"\u{1F4E1}":r.includes("cache-hit")?"\u26A1":r.includes("cache-miss")?"\u{1F4ED}":r.includes("cache-store")?"\u{1F4BE}":r.includes("invalidateCache")?"\u{1F9F9}":r.includes("listResources")?"\u{1F4DA}":r.includes("getByLanguage")?"\u{1F30D}":r.includes("getCurrentLanguage")?"\u{1F22F}":r.includes(":start")?"\u{1F680}":r.includes(":success")?"\u2705":e==="info"?"\u2139\uFE0F":"\u{1F50E}"}};var x=class{constructor(e){this.dependencies=e;this.logger=new P(e.enableLogs),this.cacheHelper=new R(e.cacheProvider,e.cacheTtlMs,this.logger),this.errorHandler=new z(this.logger);}async getCurrentLanguage(e,r){return this.errorHandler.run("getCurrentLanguage",async()=>{if(this.errorHandler.validateRequired(e,"resourceKey"),this.logger.debug(`getCurrentLanguage:start resourceKey=${e}`),this.dependencies.cacheProvider){let n=await this.resolveFromCachedResources(e);return this.logger.info(`getCurrentLanguage:cache-result resourceKey=${e} hasValue=${!!n}`),n}let t=await this.send({method:"GET",path:"api/localization/client/currentlanguage",query:{resourceKey:e},headers:this.resolveLocaleHeader(r)});return this.logger.info(`getCurrentLanguage:success resourceKey=${e} hasValue=${!!t}`),t??""})}async getByLanguage(e,r,t){return this.errorHandler.run("getByLanguage",async()=>{if(this.errorHandler.validateRequired(e,"resourceKey"),this.errorHandler.validateRequired(r,"languageKey"),this.logger.debug(`getByLanguage:start resourceKey=${e} languageKey=${r}`),this.dependencies.cacheProvider){let i=await this.resolveFromCachedResources(e,r);return this.logger.info(`getByLanguage:cache-result resourceKey=${e} languageKey=${r} hasValue=${!!i}`),i}let n=await this.send({method:"GET",path:"api/localization/client/bylanguage",query:{resourceKey:e,languageKey:r},headers:this.resolveLocaleHeader(t)});return this.logger.info(`getByLanguage:success resourceKey=${e} languageKey=${r} hasValue=${!!n}`),n??""})}async listResources(){return this.errorHandler.run("listResources",async()=>{let e=await this.cacheHelper.getListResources();if(e)return e;let t=(await this.send({method:"GET",path:"api/localization/client/list"})??[]).map(n=>({key:n.resourceKey,value:n.value,language:n.languageKey}));return await this.cacheHelper.storeListResources(t),this.logger.info(`listResources:success count=${t.length}`),t})}async invalidateCache(e){return this.errorHandler.run("invalidateCache",async()=>this.cacheHelper.invalidate(e))}async clearCache(){return this.errorHandler.run("clearCache",async()=>this.cacheHelper.invalidate())}async resolveFromCachedResources(e,r){return (await this.listResources()).find(i=>i.key!==e?false:r===void 0?true:i.language===r)?.value??""}async send(e){try{let r=await this.dependencies.authProvider?.getToken(),t=r?{...e.headers,"X-Auth-Token":r}:e.headers;this.logger.debug(`request:start method=${e.method} path=${e.path} hasAuth=${!!r}`);let n=await this.dependencies.httpTransport.request({...e,headers:t});return this.logger.debug(`request:success method=${e.method} path=${e.path} status=${n.status}`),n.data}catch(r){throw this.errorHandler.preserveCause(r)}}resolveLocaleHeader(e){let r=e?.locale?.trim()||this.dependencies.locale?.trim()||this.resolveBrowserLocale();if(r)return {"Accept-Language":r}}resolveBrowserLocale(){return globalThis.navigator?.language?.trim()||void 0}};function O(o){_(o);let r=o.cache?.enabled??false?o.cache?.provider??new d:void 0,t=new L({baseUrl:o.baseUrl,fetchFn:o.fetchFn,timeoutMs:o.timeoutMs??1e4,defaultHeaders:o.headers,middleware:o.middleware});return new x({httpTransport:t,authProvider:o.authProvider??(o.token?new u(o.token):void 0),locale:o.locale,cacheProvider:r,cacheTtlMs:o.cache?.ttlMs,enableLogs:o.enableLogs??false})}function _(o){if(!o?.baseUrl?.trim())throw new a("baseUrl is required to create localization client")}var w=class{constructor(e){this.storage=e;}async get(e){let r=this.storage.getItem(e);if(!r)return null;let t=this.safeParse(r);return t?t.expiresAt!==void 0&&Date.now()>t.expiresAt?(this.storage.removeItem(e),null):t.value:(this.storage.removeItem(e),null)}async set(e,r,t){let n=t&&t>0?Date.now()+t:void 0,i={value:r,expiresAt:n};this.storage.setItem(e,JSON.stringify(i));}async delete(e){this.storage.removeItem(e);}async clear(){this.storage.clear();}safeParse(e){try{return JSON.parse(e)}catch{return null}}},A=class extends w{constructor(){super(N("localStorage"));}},H=class extends w{constructor(){super(N("sessionStorage"));}};function N(o){let e=typeof window>"u"?void 0:window[o];return e||U()}function U(){let o=new Map;return {get length(){return o.size},clear:()=>{o.clear();},getItem:e=>o.get(e)??null,key:e=>Array.from(o.keys())[e]??null,removeItem:e=>{o.delete(e);},setItem:(e,r)=>{o.set(e,r);}}}var I=new InjectionToken("LOCALIZATION_SDK_OPTIONS"),C=new InjectionToken("LOCALIZATION_CLIENT");function Z(o){return makeEnvironmentProviders([{provide:I,useValue:o},{provide:C,useFactory:()=>O(o)}])}var E=class{constructor(e){this.client=e;}getCurrentLanguage(e){return from(this.client.getCurrentLanguage(e))}getByLanguage(e,r){return from(this.client.getByLanguage(e,r))}listResources(){return from(this.client.listResources())}invalidateCache(e){return from(this.client.invalidateCache(e))}clearCache(){return from(this.client.clearCache())}};E=k([Injectable({providedIn:"root"}),$(0,Inject(C))],E);export{L as FetchHttpTransport,C as LOCALIZATION_CLIENT,I as LOCALIZATION_SDK_OPTIONS,A as LocalStorageCacheProvider,h as LocalizationAuthError,a as LocalizationError,m as LocalizationNetworkError,g as LocalizationServerError,E as LocalizationService,f as LocalizationTimeoutError,d as MemoryCacheProvider,p as ResourceNotFoundError,H as SessionStorageCacheProvider,w as StorageCacheProvider,u as XAuthTokenProvider,O as createLocalizationClient,Z as provideLocalization};
|