wacom 21.1.24 → 21.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -54,6 +54,7 @@ export const appConfig = {
54
54
  | Name | Description |
55
55
  | -------------------------------------------------------------------- | :-----------------------------------------------------------------: |
56
56
  | [**`Core`**](https://www.npmjs.com/package/wacom#core-service) | Common supportive function which can be used in any service |
57
+ | [**`Emitter`**](#emitter-service) | Lightweight app-wide event and task signaling |
57
58
  | [**`Http`**](https://www.npmjs.com/package/wacom#http-service) | Http layer for HttpClient |
58
59
  | [**`Store`**](https://www.npmjs.com/package/wacom#store-service) | Service responsible for keeping information on the device |
59
60
  | [**`Meta`**](https://www.npmjs.com/package/wacom#meta-service) | Website meta tags management within router |
@@ -65,65 +66,7 @@ export const appConfig = {
65
66
  | [**`RTC`**](https://www.npmjs.com/package/wacom#rtc-service) | Wraps WebRTC peer connections and local media streams |
66
67
  | [**`Util`**](https://www.npmjs.com/package/wacom#util-service) | Utility methods for forms, validation, and CSS variables |
67
68
  | [**`Theme`**](#theme-service) | Manages UI theme mode, density, and radius preferences |
68
- | [**`Emitter`**](#emitter-service) | Lightweight app-wide event and task signaling |
69
-
70
- ## [Emitter Service](#emitter-service)
71
-
72
- The `EmitterService` provides a lightweight event bus and completion signaling built on Angular Signals and RxJS.
73
-
74
- ### Events
75
-
76
- - `emit(id: string, data?: any): void`: Publish an event on a channel.
77
- - `on<T = any>(id: string): Observable<T>`: Subscribe to a channel (hot, no replay).
78
- - `off(id: string): void`: Close and remove a channel.
79
- - `offAll(): void`: Close and remove all channels.
80
- - `has(id: string): boolean`: Check if a channel exists.
81
-
82
- Example:
83
-
84
- ```ts
85
- import { EmitterService } from 'wacom';
86
-
87
- constructor(private emitter: EmitterService) {}
88
-
89
- ngOnInit() {
90
- this.emitter.on<string>('user:login').subscribe((uid) => {
91
- console.log('Logged in:', uid);
92
- });
93
- }
94
-
95
- login(uid: string) {
96
- this.emitter.emit('user:login', uid);
97
- }
98
- ```
99
-
100
- ### Completion tasks
101
-
102
- Track once-off tasks and await their completion.
103
-
104
- - `complete<T = any>(task: string, value: T = true): void`: Mark task done with payload.
105
- - `clearCompleted(task: string): void`: Reset completion state.
106
- - `completed(task: string): any | undefined`: Read current payload or `undefined`.
107
- - `isCompleted(task: string): boolean`: Convenience check.
108
- - `onComplete(tasks: string | string[], opts?: { mode?: 'all' | 'any'; timeoutMs?: number; abort?: AbortSignal; }): Observable<any | any[]>`: Await task(s) completion.
109
-
110
- Example:
111
-
112
- ```ts
113
- // Somewhere that waits for a single task
114
- this.emitter.onComplete("profile:loaded").subscribe(() => {
115
- // safe to render UI
116
- });
117
-
118
- // Somewhere that fulfills it
119
- await api.loadProfile();
120
- this.emitter.complete("profile:loaded");
121
-
122
- // Wait for any of several tasks
123
- this.emitter
124
- .onComplete(["a", "b"], { mode: "any", timeoutMs: 5000 })
125
- .subscribe((which) => console.log("First done:", which));
126
- ```
69
+ | [**`Translation`**](#translation-service) | Lightweight, signal-based runtime translation engine |
127
70
 
128
71
  ## [Core Service](#core-service)
129
72
 
@@ -503,6 +446,64 @@ In this example:
503
446
 
504
447
  This ensures controlled access to the resource, preventing race conditions and ensuring data integrity.
505
448
 
449
+ ## [Emitter Service](#emitter-service)
450
+
451
+ The `EmitterService` provides a lightweight event bus and completion signaling built on Angular Signals and RxJS.
452
+
453
+ ### Events
454
+
455
+ - `emit(id: string, data?: any): void`: Publish an event on a channel.
456
+ - `on<T = any>(id: string): Observable<T>`: Subscribe to a channel (hot, no replay).
457
+ - `off(id: string): void`: Close and remove a channel.
458
+ - `offAll(): void`: Close and remove all channels.
459
+ - `has(id: string): boolean`: Check if a channel exists.
460
+
461
+ Example:
462
+
463
+ ```ts
464
+ import { EmitterService } from 'wacom';
465
+
466
+ constructor(private emitter: EmitterService) {}
467
+
468
+ ngOnInit() {
469
+ this.emitter.on<string>('user:login').subscribe((uid) => {
470
+ console.log('Logged in:', uid);
471
+ });
472
+ }
473
+
474
+ login(uid: string) {
475
+ this.emitter.emit('user:login', uid);
476
+ }
477
+ ```
478
+
479
+ ### Completion tasks
480
+
481
+ Track once-off tasks and await their completion.
482
+
483
+ - `complete<T = any>(task: string, value: T = true): void`: Mark task done with payload.
484
+ - `clearCompleted(task: string): void`: Reset completion state.
485
+ - `completed(task: string): any | undefined`: Read current payload or `undefined`.
486
+ - `isCompleted(task: string): boolean`: Convenience check.
487
+ - `onComplete(tasks: string | string[], opts?: { mode?: 'all' | 'any'; timeoutMs?: number; abort?: AbortSignal; }): Observable<any | any[]>`: Await task(s) completion.
488
+
489
+ Example:
490
+
491
+ ```ts
492
+ // Somewhere that waits for a single task
493
+ this.emitter.onComplete("profile:loaded").subscribe(() => {
494
+ // safe to render UI
495
+ });
496
+
497
+ // Somewhere that fulfills it
498
+ await api.loadProfile();
499
+ this.emitter.complete("profile:loaded");
500
+
501
+ // Wait for any of several tasks
502
+ this.emitter
503
+ .onComplete(["a", "b"], { mode: "any", timeoutMs: 5000 })
504
+ .subscribe((which) => console.log("First done:", which));
505
+ ```
506
+
506
507
  ## [Http Service](#http-service)
507
508
 
508
509
  The `HttpService` provides an HTTP layer for `HttpClient` in Angular, supporting both callbacks and observables for various HTTP operations.
@@ -712,224 +713,179 @@ httpService.unlock();
712
713
 
713
714
  ## [Store Service](#store-service)
714
715
 
715
- The `StoreService` manages local storage in a configurable manner. It can set, get, and remove items from storage, with support for asynchronous operations and JSON serialization.
716
-
717
- ### Properties
718
-
719
- #### `_prefix: string`
720
-
721
- The prefix for storage keys.
716
+ `StoreService` provides a unified, async-first API for working with storage (localStorage by default or a custom provider via config).
717
+ It supports raw values, safe JSON handling, key prefixing, and optional lifecycle hooks.
722
718
 
723
- ### Methods
719
+ ---
724
720
 
725
- #### `setPrefix(prefix: string): void`
721
+ ### Key features
726
722
 
727
- Sets the prefix for storage keys.
728
- **Parameters**:
723
+ - async/await everywhere (no separate `*Async` methods)
724
+ - optional side-effects via `options`
725
+ - automatic JSON corruption handling
726
+ - configurable storage backend
727
+ - prefix support for namespacing
729
728
 
730
- - `prefix` (string): The prefix to set.
729
+ ---
731
730
 
732
- **Example**:
731
+ ### Prefixing keys
733
732
 
734
- ```Typescript
735
- storeService.setPrefix('app_');
733
+ ```ts
734
+ storeService.setPrefix("app_");
736
735
  ```
737
736
 
738
- #### `set(key: string, value: string, callback: () => void = () => {}, errCallback: () => void = () => {}): void`
737
+ All keys will be stored as `app_<key>` (plus global config prefix if defined).
739
738
 
740
- Sets a value in storage.
741
-
742
- **Parameters**:
739
+ ---
743
740
 
744
- - `key` (string): The storage key.
745
- - `value` (string): The value to store.
746
- - `callback` (function): The callback to execute on success.
747
- - `errCallback` (function): The callback to execute on error.
741
+ ### set
748
742
 
749
- **Example**:
743
+ Stores a raw string value.
750
744
 
751
- ```Typescript
752
- storeService.set('key', 'value', () => console.log('Success'), () => console.log('Error'));
745
+ ```ts
746
+ await storeService.set("token", "abc123");
753
747
  ```
754
748
 
755
- #### `setAsync(key: string, value: string): Promise<boolean>`
756
-
757
- Sets a value in storage asynchronously.
758
-
759
- **Parameters**:
760
-
761
- - `key` (string): The storage key.
762
- - `value` (string): The value to store.
763
-
764
- **Returns**:
765
-
766
- - `Promise<boolean>`: A promise that resolves to a boolean indicating success.
749
+ With hooks:
767
750
 
768
- **Example**:
769
-
770
- ```Typescript
771
- await storeService.setAsync('key', 'value');
751
+ ```ts
752
+ await storeService.set("token", "abc123", {
753
+ onSuccess: () => console.log("saved"),
754
+ onError: console.error,
755
+ });
772
756
  ```
773
757
 
774
- #### `get(key: string, callback: (value: string) => void = () => {}, errCallback: () => void = () => {}): void`
758
+ **Returns:** `Promise<boolean>`
775
759
 
776
- Gets a value from storage.
777
-
778
- **Parameters**:
760
+ ---
779
761
 
780
- - `key` (string): The storage key.
781
- - `callback` (function): The callback to execute with the retrieved value.
782
- - `errCallback` (function): The callback to execute on error.
762
+ ### get
783
763
 
784
- **Example**:
764
+ Retrieves a raw string value.
785
765
 
786
- ```Typescript
787
- storeService.get('key', value => console.log(value), () => console.log('Error'));
766
+ ```ts
767
+ const token = await storeService.get("token");
788
768
  ```
789
769
 
790
- #### `getAsync(key: string): Promise<string | null>`
791
-
792
- Gets a value from storage asynchronously.
793
-
794
- **Parameters**:
795
-
796
- - `key` (string): The storage key.
797
-
798
- **Returns**:
770
+ With hooks:
799
771
 
800
- - `Promise<string | null>`: A promise that resolves to the retrieved value or `null` if the key is missing.
801
-
802
- **Example**:
803
-
804
- ```Typescript
805
- const value = await storeService.getAsync('key');
772
+ ```ts
773
+ const token = await storeService.get("token", {
774
+ onSuccess: (v) => console.log(v),
775
+ onError: console.error,
776
+ });
806
777
  ```
807
778
 
808
- #### `setJson(key: string, value: any, callback: () => void = () => {}, errCallback: () => void = () => {}): void`
809
-
810
- Sets a JSON value in storage.
779
+ **Returns:** `Promise<string | null>`
811
780
 
812
- **Parameters**:
781
+ ---
813
782
 
814
- - `key` (string): The storage key.
815
- - `value` (any): The value to store.
816
- - `callback` (function): The callback to execute on success.
817
- - `errCallback` (function): The callback to execute on error.
783
+ ### setJson
818
784
 
819
- **Example**:
785
+ Stores a JSON-serializable value.
820
786
 
821
- ```Typescript
822
- storeService.setJson('key', { data: 'value' }, () => console.log('Success'), () => console.log('Error'));
787
+ ```ts
788
+ await storeService.setJson("profile", { name: "Den", role: "dev" });
823
789
  ```
824
790
 
825
- #### `setJsonAsync(key: string, value: any): Promise<boolean>`
826
-
827
- Sets a JSON value in storage asynchronously.
828
-
829
- **Parameters**:
830
-
831
- - `key` (string): The storage key.
832
- - `value` (any): The value to store.
833
-
834
- **Returns**:
835
-
836
- - `Promise<boolean>`: A promise that resolves to a boolean indicating success.
791
+ With hooks:
837
792
 
838
- **Example**:
839
-
840
- ```Typescript
841
- await storeService.setJsonAsync('key', { data: 'value' });
793
+ ```ts
794
+ await storeService.setJson("profile", user, {
795
+ onSuccess: () => console.log("saved"),
796
+ onError: console.error,
797
+ });
842
798
  ```
843
799
 
844
- #### `getJson(key: string, callback: (value: any) => void = () => {}, errCallback: () => void = () => {}): void`
800
+ **Returns:** `Promise<boolean>`
845
801
 
846
- Gets a JSON value from storage.
802
+ ---
847
803
 
848
- **Parameters**:
804
+ ### getJson
849
805
 
850
- - `key` (string): The storage key.
851
- - `callback` (function): The callback to execute with the retrieved value.
852
- - `errCallback` (function): The callback to execute on error.
806
+ Retrieves a JSON value safely.
853
807
 
854
- **Example**:
808
+ - empty or missing → `null` (or `defaultValue`)
809
+ - corrupted JSON → auto-cleared (by default)
855
810
 
856
- ```Typescript
857
- storeService.getJson('key', value => console.log(value), () => console.log('Error'));
811
+ ```ts
812
+ const profile = await storeService.getJson<User>("profile");
858
813
  ```
859
814
 
860
- #### `getJsonAsync<T = any>(key: string): Promise<T | null>`
861
-
862
- Gets a JSON value from storage asynchronously.
863
-
864
- **Parameters**:
865
-
866
- - `key` (string): The storage key.
867
-
868
- **Returns**:
869
-
870
- - `Promise<T | null>`: A promise that resolves to the retrieved value.
871
-
872
- **Example**:
815
+ With defaults and error handling:
873
816
 
874
- ```Typescript
875
- const value = await storeService.getJsonAsync('key');
817
+ ```ts
818
+ const profile = await storeService.getJson<User>("profile", {
819
+ defaultValue: {},
820
+ onError: console.warn,
821
+ });
876
822
  ```
877
823
 
878
- #### `remove(key: string, callback?: () => void, errCallback?: () => void): Promise<boolean>`
824
+ Disable auto-clean:
879
825
 
880
- Removes a value from storage.
881
-
882
- **Parameters**:
826
+ ```ts
827
+ await storeService.getJson("profile", {
828
+ clearOnError: false,
829
+ });
830
+ ```
883
831
 
884
- - `key` (string): The storage key.
885
- - `callback` (function): The callback to execute on success.
886
- - `errCallback` (function): The callback to execute on error.
832
+ **Returns:** `Promise<T | null>`
887
833
 
888
- **Returns**:
834
+ ---
889
835
 
890
- - `Promise<boolean>`: A promise that resolves to a boolean indicating success.
836
+ ### remove
891
837
 
892
- **Example**:
838
+ Removes a single key.
893
839
 
894
- ```Typescript
895
- await storeService.remove('key', () => console.log('Success'), () => console.log('Error'));
840
+ ```ts
841
+ await storeService.remove("token");
896
842
  ```
897
843
 
898
- #### `clear(callback?: () => void, errCallback?: () => void): Promise<boolean>`
899
-
900
- Clears all values from storage.
844
+ With hooks:
901
845
 
902
- **Parameters**:
846
+ ```ts
847
+ await storeService.remove("token", {
848
+ onSuccess: () => console.log("removed"),
849
+ onError: console.error,
850
+ });
851
+ ```
903
852
 
904
- - `callback` (function): The callback to execute on success.
905
- - `errCallback` (function): The callback to execute on error.
853
+ **Returns:** `Promise<boolean>`
906
854
 
907
- **Returns**:
855
+ ---
908
856
 
909
- - `Promise<boolean>`: A promise that resolves to a boolean indicating success.
857
+ ### clear
910
858
 
911
- **Example**:
859
+ Clears all stored values.
912
860
 
913
- ```Typescript
914
- await storeService.clear(() => console.log('Success'), () => console.log('Error'));
861
+ ```ts
862
+ await storeService.clear();
915
863
  ```
916
864
 
917
- #### `applyPrefix(key: string): string`
865
+ With hooks:
918
866
 
919
- Applies the configured prefix to a storage key.
920
-
921
- **Parameters**:
867
+ ```ts
868
+ await storeService.clear({
869
+ onSuccess: () => console.log("cleared"),
870
+ onError: console.error,
871
+ });
872
+ ```
922
873
 
923
- - `key` (string): The storage key.
874
+ **Returns:** `Promise<boolean>`
924
875
 
925
- **Returns**:
876
+ ---
926
877
 
927
- - `string`: The prefixed storage key.
878
+ ### Options object
928
879
 
929
- **Example**:
880
+ All methods accept an optional `options` parameter:
930
881
 
931
- ```Typescript
932
- const prefixedKey = storeService.applyPrefix('key');
882
+ ```ts
883
+ interface StoreOptions<T = unknown> {
884
+ onSuccess?: (value?: T | null) => void;
885
+ onError?: (err: unknown) => void;
886
+ defaultValue?: T; // getJson only
887
+ clearOnError?: boolean; // getJson only (default: true)
888
+ }
933
889
  ```
934
890
 
935
891
  ## [Meta Service](#meta-service)
@@ -2193,3 +2149,129 @@ ngOnInit() {
2193
2149
  this.theme.setRadius("square");
2194
2150
  }
2195
2151
  ```
2152
+
2153
+ ## [Translation Service](#translation-service)
2154
+
2155
+ Wacom includes a lightweight, signal-based runtime translation engine built for Angular Signals.
2156
+
2157
+ It provides:
2158
+
2159
+ - reactive translations via `WritableSignal<string>`
2160
+ - zero-config fallback (source text renders until translated)
2161
+ - persistent storage (auto-hydrates from `StoreService`)
2162
+ - directive + pipe for ergonomic template usage
2163
+ - instant language switching without reload
2164
+
2165
+ Unlike compile-time Angular i18n, this works fully at runtime.
2166
+
2167
+ ---
2168
+
2169
+ ### Translation model
2170
+
2171
+ ```ts
2172
+ export interface Translation {
2173
+ sourceText: string;
2174
+ text: string;
2175
+ }
2176
+ ```
2177
+
2178
+ Each `sourceText` acts as both:
2179
+
2180
+ - translation key
2181
+ - default visible UI text (fallback)
2182
+
2183
+ ---
2184
+
2185
+ ### Basic usage (signal API)
2186
+
2187
+ ```ts
2188
+ import { TranslationService } from "wacom";
2189
+
2190
+ private _translationService = inject(TranslationService);
2191
+
2192
+ title = this._translationService.translate("Create project");
2193
+ ```
2194
+
2195
+ The returned value is a `WritableSignal<string>`.
2196
+
2197
+ If no translation exists yet, it renders:
2198
+
2199
+ ```
2200
+ Create project
2201
+ ```
2202
+
2203
+ automatically.
2204
+
2205
+ ---
2206
+
2207
+ ### Updating translations in bulk (language switch)
2208
+
2209
+ ```ts
2210
+ this._translationService.setMany([
2211
+ { sourceText: "Create project", text: "Створити проєкт" },
2212
+ { sourceText: "Save", text: "Зберегти" },
2213
+ ]);
2214
+ ```
2215
+
2216
+ Behavior:
2217
+
2218
+ - provided keys update reactively
2219
+ - missing keys reset back to original text
2220
+ - state persists in storage
2221
+
2222
+ ---
2223
+
2224
+ ### Updating a single translation
2225
+
2226
+ ```ts
2227
+ this._translationService.setOne({
2228
+ sourceText: "Save",
2229
+ text: "Зберегти",
2230
+ });
2231
+ ```
2232
+
2233
+ Useful for:
2234
+
2235
+ - live editors
2236
+ - dynamic language loading
2237
+ - admin translation panels
2238
+
2239
+ ---
2240
+
2241
+ ### Translate pipe (template friendly)
2242
+
2243
+ ```html
2244
+ <h1>{{ 'Create project' | translate }}</h1>
2245
+ ```
2246
+
2247
+ Auto-reacts when translations change.
2248
+
2249
+ ---
2250
+
2251
+ ### Translate directive (zero-key mode)
2252
+
2253
+ Automatically uses element’s original rendered text as translation key.
2254
+
2255
+ ```html
2256
+ <h1 translate>Create project</h1>
2257
+ <button translate>Save</button>
2258
+ ```
2259
+
2260
+ Optional explicit key:
2261
+
2262
+ ```html
2263
+ <h1 translate="Create project"></h1>
2264
+ ```
2265
+
2266
+ > The directive replaces `textContent` and is SSR-safe.
2267
+
2268
+ ---
2269
+
2270
+ ### Persistence
2271
+
2272
+ Translations are automatically:
2273
+
2274
+ - hydrated from storage on startup
2275
+ - synced after every update
2276
+
2277
+ No extra config required.