@smartcorehub/localization-sdk 1.0.3514 → 1.0.3542
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 +72 -31
- 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,6 +49,42 @@ const list = await localizationClient.listResources();
|
|
|
47
49
|
// list retorna objetos { key, value, language }
|
|
48
50
|
~~~
|
|
49
51
|
|
|
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:
|
|
57
|
+
|
|
58
|
+
- `options.locale` por chamada
|
|
59
|
+
- `locale` configurado no `createLocalizationClient`
|
|
60
|
+
- `navigator.language` no browser (fallback automatico)
|
|
61
|
+
|
|
62
|
+
Exemplo com locale global:
|
|
63
|
+
|
|
64
|
+
~~~ts
|
|
65
|
+
const client = createLocalizationClient({
|
|
66
|
+
baseUrl: 'https://api.smartcorehub.io/',
|
|
67
|
+
token: 'SEU_TOKEN',
|
|
68
|
+
locale: 'pt-BR'
|
|
69
|
+
});
|
|
70
|
+
~~~
|
|
71
|
+
|
|
72
|
+
Exemplo com override por chamada:
|
|
73
|
+
|
|
74
|
+
~~~ts
|
|
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
|
+
|
|
50
88
|
## Integracao com Angular
|
|
51
89
|
|
|
52
90
|
Forma validada e recomendada (mesma usada no exampleangular): facade com `createLocalizationClient`.
|
|
@@ -81,9 +119,7 @@ export class LocalizationFacadeService {
|
|
|
81
119
|
|
|
82
120
|
### Forma com provider Angular (opcional)
|
|
83
121
|
|
|
84
|
-
Modo alternativo
|
|
85
|
-
|
|
86
|
-
Status atual: nao validado como fluxo principal no exemplo oficial. Use apenas para experimentacao, sem garantia de estabilidade nesta versao.
|
|
122
|
+
Modo alternativo: integracao via provider Angular (`provideLocalization` + `LocalizationService`).
|
|
87
123
|
|
|
88
124
|
## Integracao com React
|
|
89
125
|
|
|
@@ -114,6 +150,8 @@ export function HomeTitle() {
|
|
|
114
150
|
- `getCurrentLanguage(resourceKey)`: retorna `string`
|
|
115
151
|
- `getByLanguage(resourceKey, languageKey)`: retorna `string`
|
|
116
152
|
- `listResources()`: retorna `LocalizationListResource[]`
|
|
153
|
+
- `invalidateCache(key?)`: invalida uma chave especifica ou todas as entradas
|
|
154
|
+
- `clearCache()`: limpa todo o cache configurado
|
|
117
155
|
|
|
118
156
|
~~~ts
|
|
119
157
|
export interface LocalizationListResource {
|
|
@@ -171,10 +209,15 @@ try {
|
|
|
171
209
|
|
|
172
210
|
## Cache configuravel
|
|
173
211
|
|
|
174
|
-
|
|
212
|
+
Quando cache.enabled = true, o provider padrao e MemoryCacheProvider.
|
|
175
213
|
|
|
176
214
|
~~~ts
|
|
177
|
-
import {
|
|
215
|
+
import {
|
|
216
|
+
createLocalizationClient,
|
|
217
|
+
MemoryCacheProvider,
|
|
218
|
+
LocalStorageCacheProvider,
|
|
219
|
+
SessionStorageCacheProvider
|
|
220
|
+
} from '@smartcorehub/localization-sdk';
|
|
178
221
|
|
|
179
222
|
const client = createLocalizationClient({
|
|
180
223
|
baseUrl: 'https://api.smartcorehub.io/',
|
|
@@ -185,6 +228,24 @@ const client = createLocalizationClient({
|
|
|
185
228
|
ttlMs: 120000
|
|
186
229
|
}
|
|
187
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
|
+
});
|
|
188
249
|
~~~
|
|
189
250
|
|
|
190
251
|
## Logging opcional
|
|
@@ -203,33 +264,13 @@ const client = createLocalizationClient({
|
|
|
203
264
|
|
|
204
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.
|
|
205
266
|
|
|
206
|
-
##
|
|
207
|
-
|
|
208
|
-
No diretorio do pacote:
|
|
209
|
-
|
|
210
|
-
~~~bash
|
|
211
|
-
npm install
|
|
212
|
-
npm run build
|
|
213
|
-
npm test
|
|
214
|
-
~~~
|
|
215
|
-
|
|
216
|
-
Para detalhes de publicacao em pipeline classico, consulte NPM-PUBLISH-GUIDE.md.
|
|
267
|
+
## Documentacao tecnica para desenvolvimento
|
|
217
268
|
|
|
218
|
-
Para
|
|
219
|
-
|
|
220
|
-
Para fluxo interno de contribuicao e troubleshooting de desenvolvimento, consulte o arquivo DEVELOPMENT-GUIDE.md.
|
|
221
|
-
|
|
222
|
-
## Validar pacote publicado (npm)
|
|
223
|
-
|
|
224
|
-
No projeto `frontendsdk/exampleangular`, rode:
|
|
225
|
-
|
|
226
|
-
~~~bash
|
|
227
|
-
npm run sdk:use:npm
|
|
228
|
-
npm run sdk:status
|
|
229
|
-
npm run build
|
|
230
|
-
~~~
|
|
269
|
+
Para build, testes automatizados, smoke test e validacao de pacote publicado, consulte:
|
|
231
270
|
|
|
232
|
-
|
|
271
|
+
- SDK-DEVELOPMENT-TESTING.md
|
|
272
|
+
- MANUAL-TESTING.md
|
|
273
|
+
- DEVELOPMENT-GUIDE.md
|
|
233
274
|
|
|
234
275
|
## Licensing / Licenciamento
|
|
235
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,r,t)=>{for(var n=e,a=o.length-1,s;a>=0;a--)(s=o[a])&&(n=(s(n))||n);return n},I=(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 i=class extends Error{constructor(r,t){super(r);this.metadata=t;this.name="LocalizationError";}},p=class extends i{constructor(e="Authentication failed",r){super(e,r),this.name="LocalizationAuthError";}},h=class extends i{constructor(e="Resource not found",r){super(e,r),this.name="ResourceNotFoundError";}},g=class extends i{constructor(e="Localization server error",r){super(e,r),this.name="LocalizationServerError";}},m=class extends i{constructor(e="Network error while contacting localization API",r){super(e,r),this.name="LocalizationNetworkError";}},f=class extends i{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";}},L=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";}},y=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),a=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(a),O;try{O=await this.fetchFn(s);}catch(E){throw this.isAbortError(E)?new L(r):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 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 k="localization:listResources",T=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(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,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 i(`${r} is required`)}map(e){return e instanceof i?e:e instanceof L?new f(e.message):e instanceof l?new m(e.message):e instanceof v?this.mapStatusError(e):new i("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 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 i(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,r,t){this.write("error",`${e}:error ${this.formatErrorDetails(r,t)}`);}formatErrorDetails(e,r){let t=this.getOriginalCause(e,r),n=this.getErrorMessage(t),a=[`type=${e.name}`,`message=${e.message}`];return n&&n!==e.message&&a.push(`original=${n}`),e instanceof i&&e.metadata?.statusCode&&a.push(`statusCode=${e.metadata.statusCode}`),a.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=`${q} ${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 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 r=await this.send({method:"GET",path:"api/localization/client/currentlanguage",query:{resourceKey:e}});return this.logger.info(`getCurrentLanguage:success resourceKey=${e} hasValue=${!!r}`),r??""})}async getByLanguage(e,r){return this.errorHandler.run("getByLanguage",async()=>{this.errorHandler.validateRequired(e,"resourceKey"),this.errorHandler.validateRequired(r,"languageKey"),this.logger.debug(`getByLanguage:start resourceKey=${e} languageKey=${r}`);let t=await this.send({method:"GET",path:"api/localization/client/bylanguage",query:{resourceKey:e,languageKey:r}});return this.logger.info(`getByLanguage:success resourceKey=${e} languageKey=${r} hasValue=${!!t}`),t??""})}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 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)}}};function x(o){D(o);let r=o.cache?.enabled??false?o.cache?.provider??new d:void 0,t=new y({baseUrl:o.baseUrl,fetchFn:o.fetchFn,timeoutMs:o.timeoutMs??1e4,defaultHeaders:o.headers,middleware:o.middleware});return new z({httpTransport:t,authProvider:o.authProvider??(o.token?new u(o.token):void 0),cacheProvider:r,cacheTtlMs:o.cache?.ttlMs,enableLogs:o.enableLogs??false})}function D(o){if(!o?.baseUrl?.trim())throw new i("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,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))}};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=i;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 S=(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},k=(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),$;try{$=await this.fetchFn(s);}catch(T){throw this.isAbortError(T)?new y(r):new l(T)}let c=await this.runResponseMiddleware($);if(!c.ok){let T=await c.text();throw new v(c.status,T,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")||r.includes(":api-call")?"\u{1F310}":r.includes("request:success")?"\u{1F4E1}":r.includes(":cache-hit")||r.includes("cache-hit")?"\u{1F5C4}\uFE0F":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.isCacheEnabled()){let n=await this.resolveFromCachedResources(e);return this.logger.info(`getCurrentLanguage:cache-${n?"hit":"miss"} resourceKey=${e} hasValue=${!!n}`),n}this.logger.info(`getCurrentLanguage:api-call path=api/localization/client/currentlanguage resourceKey=${e}`);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.isCacheEnabled()){let i=await this.resolveFromCachedResources(e,r);return this.logger.info(`getByLanguage:cache-${i?"hit":"miss"} resourceKey=${e} languageKey=${r} hasValue=${!!i}`),i}this.logger.info(`getByLanguage:api-call path=api/localization/client/bylanguage resourceKey=${e} languageKey=${r}`);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()=>{if(this.isCacheEnabled()){let t=await this.cacheHelper.getListResources();if(t)return this.logger.info(`listResources:cache-hit-return count=${t.length}`),t}else this.logger.debug("listResources:cache-disabled");this.logger.info("listResources:api-call path=api/localization/client/list");let r=(await this.send({method:"GET",path:"api/localization/client/list"})??[]).map(t=>({key:t.resourceKey,value:t.value,language:t.languageKey}));return this.isCacheEnabled()&&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 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}isCacheEnabled(){if(this.dependencies.cacheEnabled!==void 0)return this.logger.debug(`isCacheEnabled: enabled=${this.dependencies.cacheEnabled} source=cacheEnabled-flag`),this.dependencies.cacheEnabled;let e=!!this.dependencies.cacheProvider;return this.logger.debug(`isCacheEnabled: enabled=${e} source=cacheProvider`),e}};function O(o){_(o);let e=o.cache?.enabled??false,r=e?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,cacheEnabled:e,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=S([core.Injectable({providedIn:"root"}),k(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,r,t)=>{for(var n=e,a=o.length-1,s;a>=0;a--)(s=o[a])&&(n=(s(n))||n);return n},I=(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 i=class extends Error{constructor(r,t){super(r);this.metadata=t;this.name="LocalizationError";}},p=class extends i{constructor(e="Authentication failed",r){super(e,r),this.name="LocalizationAuthError";}},h=class extends i{constructor(e="Resource not found",r){super(e,r),this.name="ResourceNotFoundError";}},g=class extends i{constructor(e="Localization server error",r){super(e,r),this.name="LocalizationServerError";}},m=class extends i{constructor(e="Network error while contacting localization API",r){super(e,r),this.name="LocalizationNetworkError";}},f=class extends i{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";}},L=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";}},y=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),a=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(a),O;try{O=await this.fetchFn(s);}catch(E){throw this.isAbortError(E)?new L(r):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 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 k="localization:listResources",T=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(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,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 i(`${r} is required`)}map(e){return e instanceof i?e:e instanceof L?new f(e.message):e instanceof l?new m(e.message):e instanceof v?this.mapStatusError(e):new i("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 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 i(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,r,t){this.write("error",`${e}:error ${this.formatErrorDetails(r,t)}`);}formatErrorDetails(e,r){let t=this.getOriginalCause(e,r),n=this.getErrorMessage(t),a=[`type=${e.name}`,`message=${e.message}`];return n&&n!==e.message&&a.push(`original=${n}`),e instanceof i&&e.metadata?.statusCode&&a.push(`statusCode=${e.metadata.statusCode}`),a.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=`${q} ${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 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 r=await this.send({method:"GET",path:"api/localization/client/currentlanguage",query:{resourceKey:e}});return this.logger.info(`getCurrentLanguage:success resourceKey=${e} hasValue=${!!r}`),r??""})}async getByLanguage(e,r){return this.errorHandler.run("getByLanguage",async()=>{this.errorHandler.validateRequired(e,"resourceKey"),this.errorHandler.validateRequired(r,"languageKey"),this.logger.debug(`getByLanguage:start resourceKey=${e} languageKey=${r}`);let t=await this.send({method:"GET",path:"api/localization/client/bylanguage",query:{resourceKey:e,languageKey:r}});return this.logger.info(`getByLanguage:success resourceKey=${e} languageKey=${r} hasValue=${!!t}`),t??""})}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 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)}}};function x(o){D(o);let r=o.cache?.enabled??false?o.cache?.provider??new d:void 0,t=new y({baseUrl:o.baseUrl,fetchFn:o.fetchFn,timeoutMs:o.timeoutMs??1e4,defaultHeaders:o.headers,middleware:o.middleware});return new z({httpTransport:t,authProvider:o.authProvider??(o.token?new u(o.token):void 0),cacheProvider:r,cacheTtlMs:o.cache?.ttlMs,enableLogs:o.enableLogs??false})}function D(o){if(!o?.baseUrl?.trim())throw new i("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,r){return from(this.client.getByLanguage(e,r))}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,i 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 S=(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},k=(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),$;try{$=await this.fetchFn(s);}catch(T){throw this.isAbortError(T)?new y(r):new l(T)}let c=await this.runResponseMiddleware($);if(!c.ok){let T=await c.text();throw new v(c.status,T,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")||r.includes(":api-call")?"\u{1F310}":r.includes("request:success")?"\u{1F4E1}":r.includes(":cache-hit")||r.includes("cache-hit")?"\u{1F5C4}\uFE0F":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.isCacheEnabled()){let n=await this.resolveFromCachedResources(e);return this.logger.info(`getCurrentLanguage:cache-${n?"hit":"miss"} resourceKey=${e} hasValue=${!!n}`),n}this.logger.info(`getCurrentLanguage:api-call path=api/localization/client/currentlanguage resourceKey=${e}`);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.isCacheEnabled()){let i=await this.resolveFromCachedResources(e,r);return this.logger.info(`getByLanguage:cache-${i?"hit":"miss"} resourceKey=${e} languageKey=${r} hasValue=${!!i}`),i}this.logger.info(`getByLanguage:api-call path=api/localization/client/bylanguage resourceKey=${e} languageKey=${r}`);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()=>{if(this.isCacheEnabled()){let t=await this.cacheHelper.getListResources();if(t)return this.logger.info(`listResources:cache-hit-return count=${t.length}`),t}else this.logger.debug("listResources:cache-disabled");this.logger.info("listResources:api-call path=api/localization/client/list");let r=(await this.send({method:"GET",path:"api/localization/client/list"})??[]).map(t=>({key:t.resourceKey,value:t.value,language:t.languageKey}));return this.isCacheEnabled()&&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 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}isCacheEnabled(){if(this.dependencies.cacheEnabled!==void 0)return this.logger.debug(`isCacheEnabled: enabled=${this.dependencies.cacheEnabled} source=cacheEnabled-flag`),this.dependencies.cacheEnabled;let e=!!this.dependencies.cacheProvider;return this.logger.debug(`isCacheEnabled: enabled=${e} source=cacheProvider`),e}};function O(o){_(o);let e=o.cache?.enabled??false,r=e?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,cacheEnabled:e,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=S([Injectable({providedIn:"root"}),k(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};
|