@molecule/app-angular 1.0.2 → 1.0.4

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
@@ -3,21 +3,21 @@ AUTO-GENERATED — DO NOT EDIT THIS FILE.
3
3
  Generated by `mlcl sync-docs` from the package's src/index.ts JSDoc + mlcl/registry.json.
4
4
  Edits here are overwritten on the next commit (molecule's pre-commit hook regenerates).
5
5
  To change this document, edit the module-level JSDoc in src/index.ts.
6
- Generated: 2026-09-15T19:11:41.538Z
6
+ Generated: 2026-10-23T15:23:09.411Z
7
7
  -->
8
8
 
9
9
  # @molecule/app-angular
10
10
 
11
- > **Auto-generated, AI-first package reference** for the [molecule.dev](https://molecule.dev) ecosystem.
12
- > It is written to be read by coding agents as much as by people, and is generated from this
13
- > package's source — edit `src/index.ts` JSDoc, not this file.
14
-
15
11
  Angular framework bindings for molecule.dev.
16
12
 
17
13
  Provides Angular-specific services and providers for all molecule
18
14
  core interfaces. This package enables the use of molecule's framework-agnostic
19
15
  interfaces with Angular's idioms (services, DI, RxJS observables, etc.).
20
16
 
17
+ > **Auto-generated, AI-first package reference** for the [molecule.dev](https://molecule.dev) ecosystem.
18
+ > It is written to be read by coding agents as much as by people, and is generated from this
19
+ > package's source — edit `src/index.ts` JSDoc, not this file.
20
+
21
21
  ## Quick Start
22
22
 
23
23
  ```typescript
@@ -86,131 +86,17 @@ npm install @molecule/app-angular @angular/core @molecule/app-auth @molecule/app
86
86
 
87
87
  ### Interfaces
88
88
 
89
- #### `AsyncStateManager`
90
-
91
- Async state manager.
92
-
93
- ```typescript
94
- interface AsyncStateManager<T> {
95
- state$: Observable<T>
96
- getState: () => T
97
- setState: (value: T | ((prev: T) => T) | Promise<T | ((prev: T) => T)>) => void
98
- extendState: (
99
- partial:
100
- Partial<T> | ((prev: T) => Partial<T>) | Promise<Partial<T> | ((prev: T) => Partial<T>)>,
101
- ) => void
102
- destroy: () => void
103
- }
104
- ```
105
-
106
- #### `AuthClient`
107
-
108
- Auth client interface that all auth bond packages must implement.
109
- Provides login/logout/register flows, token management, profile
110
- updates, and auth state subscription.
111
-
112
- ```typescript
113
- interface AuthClient<T = UserProfile> {
114
- /**
115
- * Returns the current authentication state snapshot.
116
- */
117
- getState(): AuthState<T>
118
- /**
119
- * Returns whether the user is currently authenticated.
120
- */
121
- isAuthenticated(): boolean
122
- /**
123
- * Gets the current user.
124
- */
125
- getUser(): T | null
126
- /**
127
- * Updates the cached user object (state + persistent storage) without
128
- * hitting the network. Intended for local refreshes after a per-app
129
- * mutation (e.g., the user just PATCHed their own profile and the
130
- * server returned the canonical row). Does NOT change tokens.
131
- */
132
- setUser(user: T | null): void
133
- /**
134
- * Gets the current access token.
135
- */
136
- getAccessToken(): string | null
137
- /**
138
- * Stores the access token in the configured token storage adapter (in-memory
139
- * by default). Use this to seed the token after an out-of-band exchange (e.g.
140
- * the OAuth code→token redirect) instead of writing to `localStorage` directly,
141
- * which would violate the in-memory-default storage contract and make the bearer
142
- * token JS-readable (XSS-exfiltratable). Pass `null` to clear it.
143
- */
144
- setAccessToken(token: string | null): void
145
- /**
146
- * Gets the refresh token.
147
- */
148
- getRefreshToken(): string | null
149
- /**
150
- * Logs in with credentials.
151
- */
152
- login(credentials: LoginCredentials): Promise<AuthResult<T>>
153
- /**
154
- * Logs out the current user.
155
- */
156
- logout(): Promise<void>
157
- /**
158
- * Registers a new user.
159
- */
160
- register(data: RegisterData): Promise<AuthResult<T>>
161
- /**
162
- * Refreshes the access token.
163
- */
164
- refresh(): Promise<AuthResult<T>>
165
- /**
166
- * Requests a password reset.
167
- */
168
- requestPasswordReset(data: PasswordResetRequest): Promise<void>
169
- /**
170
- * Confirms a password reset.
171
- */
172
- confirmPasswordReset(data: PasswordResetConfirm): Promise<void>
173
- /**
174
- * Updates the current user's profile.
175
- */
176
- updateProfile(data: Partial<T>): Promise<T>
177
- /**
178
- * Changes the current user's password.
179
- */
180
- changePassword(oldPassword: string, newPassword: string): Promise<void>
181
- /**
182
- * Initializes auth state (e.g., from stored tokens).
183
- */
184
- initialize(): Promise<void>
185
- /**
186
- * Subscribes to auth state changes.
187
- */
188
- subscribe(callback: (state: AuthState<T>) => void): () => void
189
- /**
190
- * Subscribes to auth state changes (alias for subscribe).
191
- */
192
- onAuthChange(callback: (state: AuthState<T>) => void): () => void
193
- /**
194
- * Gets the current access token (alias for getAccessToken).
195
- */
196
- getToken?(): string | null
197
- /**
198
- * Adds an auth event listener.
199
- */
200
- addEventListener(listener: AuthEventListener): () => void
201
- /**
202
- * Destroys the auth client.
203
- */
204
- destroy(): void
205
- }
206
- ```
207
-
208
89
  #### `AuthState`
209
90
 
210
91
  Reactive authentication state snapshot (initialized, authenticated, user, loading, and error).
211
92
 
212
93
  ```typescript
213
94
  interface AuthState<T = UserProfile> {
95
+ /**
96
+ * The session's state as one value — render from this instead of combining
97
+ * `initialized`/`authenticated`/`loading`. See {@link AuthStatus}.
98
+ */
99
+ status: AuthStatus
214
100
  /**
215
101
  * Whether auth state has been initialized.
216
102
  */
@@ -234,49 +120,178 @@ interface AuthState<T = UserProfile> {
234
120
  }
235
121
  ```
236
122
 
237
- #### `CapacitorAppManager`
238
-
239
- Capacitor app manager interface.
240
-
241
- ```typescript
242
- interface CapacitorAppManager {
243
- state$: Observable<CapacitorAppState>
244
- ready$: Observable<boolean>
245
- initialize: () => Promise<void>
246
- destroy: () => void
247
- }
248
- ```
249
-
250
- #### `ChangePasswordStateManager`
251
-
252
- Change password state manager.
253
-
254
- ```typescript
255
- interface ChangePasswordStateManager {
256
- state$: Observable<PromiseState<void>>
257
- getState: () => PromiseState<void>
258
- changePassword: (oldPassword: string, newPassword: string) => Promise<void>
259
- reset: () => void
260
- destroy: () => void
261
- }
262
- ```
263
-
264
- #### `DeviceService`
123
+ #### `AuthClient`
265
124
 
266
- Device service interface.
125
+ Auth client interface that all auth bond packages must implement.
126
+ Provides login/logout/register flows, token management, profile
127
+ updates, and auth state subscription.
267
128
 
268
129
  ```typescript
269
- interface DeviceService {
270
- deviceInfo: DeviceInfo
271
- screenInfo: ScreenInfo
272
- hardwareInfo: HardwareInfo
273
- featureSupport: FeatureSupport
274
- supports: (feature: keyof FeatureSupport) => boolean
275
- isOnline: () => boolean
276
- isStandalone: () => boolean
277
- language: string
278
- languages: string[]
279
- }
130
+ interface AuthClient<T = UserProfile, TRegister extends RegisterExtraShape<TRegister> = Record<never, never>> {
131
+ /**
132
+ * Returns the current authentication state snapshot.
133
+ *
134
+ * READING the state starts the client's one session restore when nobody has called
135
+ * {@link initialize} yet — lazily and exactly once, sharing the promise an explicit
136
+ * `initialize()` returns. A hook (`useAuth()`), a subscriber, `isAuthenticated()` and
137
+ * `getUser()` are reads too, so a client whose entry file dropped the template's
138
+ * `authClient.initialize()` line still restores instead of reporting `'loading'`
139
+ * forever.
140
+ */
141
+ getState(): AuthState<T>;
142
+ /**
143
+ * Returns whether the user is currently authenticated.
144
+ */
145
+ isAuthenticated(): boolean;
146
+ /**
147
+ * Gets the current user.
148
+ */
149
+ getUser(): T | null;
150
+ /**
151
+ * Optional ownership marker for the current session. Compare values only within
152
+ * this client: replacing or ending a session changes it, including re-adopting
153
+ * the same token and user. Profile updates and same-session refreshes keep it.
154
+ * Consumers can use it to discard stale async work without treating editable
155
+ * user fields as an immutable account identifier. Providers without this
156
+ * capability require the consumer's own stable identity check.
157
+ */
158
+ getSessionRevision?(): number;
159
+ /**
160
+ * Updates the cached user object (state + persistent storage) without
161
+ * hitting the network. Intended for local refreshes after a per-app
162
+ * mutation (e.g., the user just PATCHed their own profile and the
163
+ * server returned the canonical row). Does NOT change tokens.
164
+ *
165
+ * This does not sign the client in: `authenticated` and `status` are
166
+ * unchanged, so an anonymous client stays anonymous. Use
167
+ * {@link login} / {@link register} for a credential exchange, or
168
+ * {@link setSession} when the token was issued outside the client.
169
+ */
170
+ setUser(user: T | null): void;
171
+ /**
172
+ * Gets the current access token.
173
+ */
174
+ getAccessToken(): string | null;
175
+ /**
176
+ * Stores the access token in the configured token storage adapter (in a
177
+ * browser, Web Storage; `'memory'` where no browser store exists). Use this
178
+ * to seed the token after an out-of-band exchange (e.g. the OAuth
179
+ * code→token redirect) instead of writing to `localStorage` directly,
180
+ * which would violate the storage contract and make a stored bearer token
181
+ * JS-readable (XSS-exfiltratable). Pass `null` to clear it.
182
+ *
183
+ * This changes token storage only; it does not sign the client in
184
+ * (`authenticated` and `status` are unchanged). Use {@link login} /
185
+ * {@link register} for a credential exchange, or call {@link setSession}
186
+ * with the token and the user to establish an already-issued session.
187
+ */
188
+ setAccessToken(token: string | null): void;
189
+ /**
190
+ * Adopts a session an out-of-band exchange already established: stores the
191
+ * access token (and the refresh token when given), caches the user, and
192
+ * sets the COMPLETE state — `initialized`, `authenticated`, `status:
193
+ * 'authenticated'`, no error — in one update, with one subscriber
194
+ * notification. Every hook reading the shared client sees the user as
195
+ * signed in immediately.
196
+ *
197
+ * This is the way to seed an OAuth/custom sign-in. Calling
198
+ * {@link setAccessToken} and {@link setUser} instead does NOT sign the
199
+ * client in.
200
+ */
201
+ setSession(session: AuthSessionSeed<T>): void;
202
+ /**
203
+ * Gets the refresh token.
204
+ */
205
+ getRefreshToken(): string | null;
206
+ /**
207
+ * Logs in with credentials.
208
+ */
209
+ login(credentials: LoginCredentials): Promise<AuthResult<T>>;
210
+ /**
211
+ * Logs out the current user.
212
+ */
213
+ logout(): Promise<void>;
214
+ /**
215
+ * Registers a new user. Accepts the base fields plus the app's own fields
216
+ * (`TRegister`) at the top level — see {@link RegisterData}.
217
+ */
218
+ register(data: RegisterData<TRegister>): Promise<AuthResult<T>>;
219
+ /**
220
+ * Refreshes the access token.
221
+ */
222
+ refresh(): Promise<AuthResult<T>>;
223
+ /**
224
+ * Requests a password reset.
225
+ */
226
+ requestPasswordReset(data: PasswordResetRequest): Promise<void>;
227
+ /**
228
+ * Confirms a password reset.
229
+ */
230
+ confirmPasswordReset(data: PasswordResetConfirm): Promise<void>;
231
+ /**
232
+ * Updates the current user's profile.
233
+ * Returns the saved response. If the session is explicitly replaced or ended
234
+ * while the request is pending, that response does not replace the current
235
+ * session's user or persisted cache.
236
+ */
237
+ updateProfile(data: Partial<T>): Promise<T>;
238
+ /**
239
+ * Changes the current user's password.
240
+ */
241
+ changePassword(oldPassword: string, newPassword: string): Promise<void>;
242
+ /**
243
+ * Initializes auth state (e.g., from stored tokens).
244
+ *
245
+ * LAZY and IDEMPOTENT: the first read of the state ({@link getState},
246
+ * {@link subscribe}, `useAuth()`) starts this same restore when nobody has called
247
+ * `initialize()`, and every later call shares the one promise instead of sending a
248
+ * second request. An app that keeps the template's explicit call behaves exactly as
249
+ * before; an app that loses that line still restores its session.
250
+ *
251
+ * With a stored token AND a stored user it marks the session `'authenticated'`
252
+ * SYNCHRONOUSLY — before its first `await` — so the first render is the
253
+ * signed-in UI and no page waits on a verification round trip. Any check
254
+ * (a refresh, the cookie-restore probe) settles behind that render, and only an
255
+ * explicit 401/403 ends the session; a network/5xx/404 failure keeps it and
256
+ * reports `'unavailable'`. With no stored user the client stays `'loading'`
257
+ * until the cookie restore decides.
258
+ *
259
+ * Do not put this call (or a `/me` request of your own) in front of the first
260
+ * render, and do not render a loading screen over a session it just restored:
261
+ * `whenRestored()` already resolves as soon as the restore has settled, and the
262
+ * HTTP gate built on it holds requests for exactly that window.
263
+ */
264
+ initialize(): Promise<void>;
265
+ /**
266
+ * Re-checks a restored session against the server, in the background — the
267
+ * boot-time validation an app with a stored token needs when a revoked or
268
+ * expired token must sign the user out. Call it AFTER the first render
269
+ * (`initialize()` restores synchronously from storage; this costs one request).
270
+ *
271
+ * - The server confirms the user: the session is refreshed and {@link AuthState.status}
272
+ * stays `'authenticated'`.
273
+ * - The server answers 401 or 403: the token is dead — the stored session is
274
+ * cleared and the status becomes `'unauthenticated'`.
275
+ * - The server cannot confirm the session — a network failure, a 5xx, or a 404
276
+ * from a misconfigured {@link AuthClientConfig.currentUserEndpoint}: the
277
+ * session (token AND user) is KEPT and the status becomes `'unavailable'`, so
278
+ * the app can retry instead of signing the user out over a bad connection or a
279
+ * route that does not exist. The 404 error names `currentUserEndpoint`.
280
+ *
281
+ * Do not hand-write this beside the client: a parallel token/user store for a
282
+ * "validating"/"unavailable" flag is the race this method removes.
283
+ *
284
+ * Optional: a provider with no restorable session has nothing to validate.
285
+ */
286
+ validateSession?(): Promise<void>;
287
+ /**
288
+ * Resolves when the session restore started by `initialize()` has settled — the point
289
+ * after which the client knows whether this visitor is signed in.
290
+ *
291
+ * Resolves IMMEDIATELY when no restore is in flight: before `initialize()` is ever
292
+ * called, and after it has finished. A restore that failed resolves too (it settled the
293
+ * question), so an HTTP client can await this before every request without risking one
294
+ // … truncated — the full declaration is in node_modules/@molecule/app-angular/dist
280
295
  ```
281
296
 
282
297
  #### `FormController`
@@ -358,7 +373,13 @@ interface FormController<T extends Record<string, unknown> = Record<string, unkn
358
373
  */
359
374
  validate(): Promise<boolean>
360
375
  /**
361
- * Resets the form to initial values.
376
+ * Resets the form and clears errors, touched fields and submission state.
377
+ *
378
+ * With no argument it restores the current baseline — the initial
379
+ * `defaultValues`, or the values a previous `reset(values)` established.
380
+ * Passing `values` merges them over the current baseline and makes the
381
+ * result the new baseline, so a form reset to freshly loaded or saved data
382
+ * reads clean (`isDirty === false`).
362
383
  */
363
384
  reset(values?: Partial<T>): void
364
385
  /**
@@ -390,7 +411,8 @@ Form creation options.
390
411
  ```typescript
391
412
  interface FormOptions<T extends Record<string, unknown>> {
392
413
  /**
393
- * Default values.
414
+ * Default values, captured as the form's clean baseline when the controller
415
+ * is created. Mutating this object afterwards does not change the baseline.
394
416
  */
395
417
  defaultValues?: Partial<T>
396
418
  /**
@@ -431,7 +453,8 @@ interface HttpClient {
431
453
  */
432
454
  defaultHeaders: Record<string, string>
433
455
  /**
434
- * Makes a generic HTTP request.
456
+ * Makes a generic HTTP request. `data` is a field of THIS config object — the same body
457
+ * `post(url, data, config)` takes as its second argument.
435
458
  */
436
459
  request<T = unknown>(config: FullRequestConfig): Promise<HttpResponse<T>>
437
460
  /**
@@ -439,15 +462,19 @@ interface HttpClient {
439
462
  */
440
463
  get<T = unknown>(url: string, config?: RequestConfig): Promise<HttpResponse<T>>
441
464
  /**
442
- * Makes a POST request.
465
+ * Makes a POST request. `data` is the request BODY, not a config object: the wire body is
466
+ * `data` itself, so `post(url, { data: body })` sends `{"data":body}` — pass `body` directly.
467
+ * A config (headers, params) is the THIRD argument.
443
468
  */
444
469
  post<T = unknown>(url: string, data?: unknown, config?: RequestConfig): Promise<HttpResponse<T>>
445
470
  /**
446
- * Makes a PUT request.
471
+ * Makes a PUT request. `data` is the request BODY (second argument) — never `{ data: body }`,
472
+ * which sends `{"data":body}`; a config is the THIRD argument.
447
473
  */
448
474
  put<T = unknown>(url: string, data?: unknown, config?: RequestConfig): Promise<HttpResponse<T>>
449
475
  /**
450
- * Makes a PATCH request.
476
+ * Makes a PATCH request. `data` is the request BODY (second argument) — never `{ data: body }`,
477
+ * which sends `{"data":body}`; a config is the THIRD argument.
451
478
  */
452
479
  patch<T = unknown>(url: string, data?: unknown, config?: RequestConfig): Promise<HttpResponse<T>>
453
480
  /**
@@ -486,18 +513,6 @@ interface HttpClient {
486
513
  }
487
514
  ```
488
515
 
489
- #### `HttpState`
490
-
491
- State for HTTP requests.
492
-
493
- ```typescript
494
- interface HttpState<T> {
495
- data: T | null
496
- loading: boolean
497
- error: Error | null
498
- }
499
- ```
500
-
501
516
  #### `I18nProvider`
502
517
 
503
518
  i18n provider interface.
@@ -756,244 +771,55 @@ interface LoggerProvider {
756
771
  }
757
772
  ```
758
773
 
759
- #### `LoginStateManager`
760
-
761
- Login state manager.
762
-
763
- ```typescript
764
- interface LoginStateManager<T = unknown> {
765
- state$: Observable<PromiseState<AuthResult<T>>>
766
- getState: () => PromiseState<AuthResult<T>>
767
- login: (credentials: LoginCredentials) => Promise<AuthResult<T>>
768
- reset: () => void
769
- destroy: () => void
770
- }
771
- ```
772
-
773
- #### `MoleculeModuleConfig`
774
+ #### `RouteLocation`
774
775
 
775
- Configuration for molecule Angular module.
776
+ Current URL decomposed into pathname, search string, hash, navigation state, and unique key.
776
777
 
777
778
  ```typescript
778
- interface MoleculeModuleConfig {
779
- state?: StateProvider
780
- auth?: AuthClient<unknown>
781
- theme?: ThemeProvider
782
- router?: Router
783
- i18n?: I18nProvider
784
- http?: HttpClient
785
- storage?: StorageProvider
786
- logger?: LoggerProvider
779
+ interface RouteLocation {
780
+ /**
781
+ * Current pathname.
782
+ */
783
+ pathname: string
784
+ /**
785
+ * Query string (including leading ?).
786
+ */
787
+ search: string
788
+ /**
789
+ * Hash (including leading #).
790
+ */
791
+ hash: string
792
+ /**
793
+ * State data passed with navigation.
794
+ */
795
+ state?: unknown
796
+ /**
797
+ * Unique key for this location.
798
+ */
799
+ key?: string
787
800
  }
788
801
  ```
789
802
 
790
- #### `MoleculeTokens`
791
-
792
- Injection tokens for molecule services.
793
-
794
- ```typescript
795
- interface MoleculeTokens {
796
- STATE_PROVIDER: symbol
797
- AUTH_CLIENT: symbol
798
- THEME_PROVIDER: symbol
799
- ROUTER: symbol
800
- I18N_PROVIDER: symbol
801
- HTTP_CLIENT: symbol
802
- STORAGE_PROVIDER: symbol
803
- LOGGER_PROVIDER: symbol
804
- }
805
- ```
803
+ #### `Router`
806
804
 
807
- #### `OAuthOptions`
805
+ Client-side router providing navigation, guards, route matching, and history control.
808
806
 
809
- OAuth configuration options.
807
+ All routing providers must implement this interface.
810
808
 
811
809
  ```typescript
812
- interface OAuthOptions {
813
- /** Base URL for the API server (e.g. `https://api.example.com`). */
814
- baseURL?: string
815
- /** List of supported OAuth provider names (e.g. `['github', 'google']`). */
816
- oauthProviders?: string[]
817
- /** Path prefix for OAuth initiation routes. Defaults to `/oauth`. */
818
- oauthEndpoint?: string
819
- /** Path for the OAuth login POST endpoint. Defaults to `/users/log-in/oauth`. */
820
- loginEndpoint?: string
821
- /** Called after a successful OAuth login (session established). */
822
- onSuccess?: () => void
810
+ interface Router {
823
811
  /**
824
- * Called with a failure message when the OAuth login fails. Failures are
825
- * also emitted on {@link OAuthStateManager.error$}.
812
+ * Returns the current route location (pathname, search, hash, state).
826
813
  */
827
- onError?: (error: string) => void
814
+ getLocation(): RouteLocation
828
815
  /**
829
- * Auth client used to establish the session after the code exchange. This
830
- * is THE way to wire session establishment in Angular: inject the
831
- * `AUTH_CLIENT` token and pass the client here. When omitted, the helper
832
- * falls back to the server-established httpOnly-cookie session (see
833
- * {@link OAuthStateManager.handleCallback}).
816
+ * Gets the current route params.
834
817
  */
835
- authClient?: AuthClient<unknown>
836
- }
837
- ```
838
-
839
- #### `OAuthStateManager`
840
-
841
- OAuth state manager.
842
-
843
- ```typescript
844
- interface OAuthStateManager {
845
- /** Observable of the configured OAuth provider names. */
846
- providers$: Observable<string[]>
818
+ getParams<T extends RouteParams = RouteParams>(): T
847
819
  /**
848
- * Observable of OAuth failure messages (from the callback code exchange).
849
- * Completed by {@link OAuthStateManager.destroy}.
820
+ * Gets the current query params.
850
821
  */
851
- error$: Observable<string>
852
- /** Returns the configured OAuth provider names. */
853
- getProviders: () => string[]
854
- /** Builds the OAuth initiation URL for a provider. */
855
- getOAuthUrl: (provider: string) => string
856
- /** Starts the full-page redirect flow for a provider. */
857
- redirect: (provider: string) => void
858
- /**
859
- * Handles the OAuth callback: exchanges the `code` URL parameter for a
860
- * session. Invoked automatically at creation (see {@link createOAuthState});
861
- * exposed for callers that need to re-run it manually. No-ops unless
862
- * running in a browser with a `code` URL parameter and a stashed provider.
863
- *
864
- * When an auth client was provided, the session is established locally
865
- * (`setAccessToken` + `setUser` + `initialize`). When no client is
866
- * available, the server has already established the httpOnly-cookie
867
- * session during the exchange, so a user-carrying response still counts
868
- * as success.
869
- */
870
- handleCallback: () => Promise<void>
871
- /** Completes the `providers$` and `error$` observables. */
872
- destroy: () => void
873
- }
874
- ```
875
-
876
- #### `PasswordResetStateManager`
877
-
878
- Password reset state manager.
879
-
880
- ```typescript
881
- interface PasswordResetStateManager {
882
- requestState$: Observable<PromiseState<void>>
883
- confirmState$: Observable<PromiseState<void>>
884
- getRequestState: () => PromiseState<void>
885
- getConfirmState: () => PromiseState<void>
886
- requestReset: (data: PasswordResetRequest) => Promise<void>
887
- confirmReset: (data: PasswordResetConfirm) => Promise<void>
888
- reset: () => void
889
- destroy: () => void
890
- }
891
- ```
892
-
893
- #### `PlatformService`
894
-
895
- Platform service interface.
896
-
897
- ```typescript
898
- interface PlatformService {
899
- platform: Platform
900
- isNative: boolean
901
- isMobile: boolean
902
- isDesktop: boolean
903
- isWeb: boolean
904
- isDevelopment: boolean
905
- isProduction: boolean
906
- isPlatform: (...platforms: Platform[]) => boolean
907
- }
908
- ```
909
-
910
- #### `PromiseStateManager`
911
-
912
- Promise state manager.
913
-
914
- ```typescript
915
- interface PromiseStateManager<T> {
916
- state$: Observable<PromiseState<T>>
917
- getState: () => PromiseState<T>
918
- call: (...args: any[]) => Promise<T>
919
- cancel: (message?: string) => void
920
- reset: () => void
921
- destroy: () => void
922
- }
923
- ```
924
-
925
- #### `PushService`
926
-
927
- Push service interface.
928
-
929
- ```typescript
930
- interface PushService {
931
- permission$: Observable<PermissionStatus | null>
932
- token$: Observable<PushToken | null>
933
- getPermission: () => PermissionStatus | null
934
- getToken: () => PushToken | null
935
- checkPermission: () => Promise<PermissionStatus>
936
- requestPermission: () => Promise<PermissionStatus>
937
- register: (options?: PushRegisterOptions) => Promise<PushToken>
938
- unregister: () => Promise<void>
939
- onNotificationReceived: (listener: NotificationReceivedListener) => () => void
940
- onNotificationAction: (listener: NotificationActionListener) => () => void
941
- onTokenChange: (listener: TokenChangeListener) => () => void
942
- setBadge: (count: number) => Promise<void>
943
- clearBadge: () => Promise<void>
944
- destroy: () => void
945
- }
946
- ```
947
-
948
- #### `RouteLocation`
949
-
950
- Current URL decomposed into pathname, search string, hash, navigation state, and unique key.
951
-
952
- ```typescript
953
- interface RouteLocation {
954
- /**
955
- * Current pathname.
956
- */
957
- pathname: string
958
- /**
959
- * Query string (including leading ?).
960
- */
961
- search: string
962
- /**
963
- * Hash (including leading #).
964
- */
965
- hash: string
966
- /**
967
- * State data passed with navigation.
968
- */
969
- state?: unknown
970
- /**
971
- * Unique key for this location.
972
- */
973
- key?: string
974
- }
975
- ```
976
-
977
- #### `Router`
978
-
979
- Client-side router providing navigation, guards, route matching, and history control.
980
-
981
- All routing providers must implement this interface.
982
-
983
- ```typescript
984
- interface Router {
985
- /**
986
- * Returns the current route location (pathname, search, hash, state).
987
- */
988
- getLocation(): RouteLocation
989
- /**
990
- * Gets the current route params.
991
- */
992
- getParams<T extends RouteParams = RouteParams>(): T
993
- /**
994
- * Gets the current query params.
995
- */
996
- getQuery(): QueryParams
822
+ getQuery(): QueryParams
997
823
  /**
998
824
  * Gets a specific query parameter.
999
825
  */
@@ -1079,17 +905,52 @@ interface Router {
1079
905
  }
1080
906
  ```
1081
907
 
1082
- #### `SignupStateManager`
908
+ #### `StoreConfig`
1083
909
 
1084
- Signup state manager.
910
+ Configuration for creating a store (initial state, optional name, and middleware chain).
1085
911
 
1086
912
  ```typescript
1087
- interface SignupStateManager<T = unknown> {
1088
- state$: Observable<PromiseState<AuthResult<T>>>
1089
- getState: () => PromiseState<AuthResult<T>>
1090
- signup: (data: RegisterData) => Promise<AuthResult<T>>
1091
- reset: () => void
1092
- destroy: () => void
913
+ interface StoreConfig<T> {
914
+ /**
915
+ * Initial state value.
916
+ */
917
+ initialState: T
918
+ /**
919
+ * Optional name for debugging.
920
+ */
921
+ name?: string
922
+ /**
923
+ * Optional middleware functions.
924
+ */
925
+ middleware?: StoreMiddleware<T>[]
926
+ }
927
+ ```
928
+
929
+ #### `Store`
930
+
931
+ Reactive state container with getState, setState, subscribe, and destroy.
932
+
933
+ All state management providers must implement this interface.
934
+
935
+ ```typescript
936
+ interface Store<T> {
937
+ /**
938
+ * Gets the current state.
939
+ */
940
+ getState(): T
941
+ /**
942
+ * Sets the state (partial or via updater function).
943
+ */
944
+ setState(partial: Partial<T> | ((state: T) => Partial<T>)): void
945
+ /**
946
+ * Subscribes to state changes.
947
+ * Returns an unsubscribe function.
948
+ */
949
+ subscribe(listener: StateListener<T>): () => void
950
+ /**
951
+ * Destroys the store and cleans up subscriptions.
952
+ */
953
+ destroy(): void
1093
954
  }
1094
955
  ```
1095
956
 
@@ -1150,246 +1011,1748 @@ interface StorageProvider {
1150
1011
  }
1151
1012
  ```
1152
1013
 
1153
- #### `StorageValueState`
1154
-
1155
- State for a storage value.
1156
-
1157
- ```typescript
1158
- interface StorageValueState<T> {
1159
- value: T | undefined
1160
- loading: boolean
1161
- error: Error | null
1162
- }
1163
- ```
1164
-
1165
- #### `Store`
1166
-
1167
- Reactive state container with getState, setState, subscribe, and destroy.
1014
+ #### `ThemeProvider`
1168
1015
 
1169
- All state management providers must implement this interface.
1016
+ Manages theme state including the active theme, mode toggling,
1017
+ and change subscriptions.
1170
1018
 
1171
1019
  ```typescript
1172
- interface Store<T> {
1020
+ interface ThemeProvider {
1173
1021
  /**
1174
- * Gets the current state.
1022
+ * Returns the currently active theme.
1175
1023
  */
1176
- getState(): T
1024
+ getTheme(): Theme
1177
1025
  /**
1178
- * Sets the state (partial or via updater function).
1026
+ * Sets the active theme by reference or by name.
1027
+ *
1028
+ * @param theme - A `Theme` object or a theme name string to activate.
1179
1029
  */
1180
- setState(partial: Partial<T> | ((state: T) => Partial<T>)): void
1030
+ setTheme(theme: Theme | string): void
1181
1031
  /**
1182
- * Subscribes to state changes.
1183
- * Returns an unsubscribe function.
1032
+ * Toggles between light and dark mode for the active theme.
1184
1033
  */
1185
- subscribe(listener: StateListener<T>): () => void
1034
+ toggleMode(): void
1186
1035
  /**
1187
- * Destroys the store and cleans up subscriptions.
1036
+ * Subscribes to theme changes. The callback fires whenever
1037
+ * `setTheme()` or `toggleMode()` is called.
1038
+ *
1039
+ * @param callback - Invoked with the new theme after each change.
1040
+ * @returns An unsubscribe function.
1188
1041
  */
1189
- destroy(): void
1042
+ subscribe(callback: (theme: Theme) => void): () => void
1043
+ /**
1044
+ * Returns all registered themes. Optional — not all providers
1045
+ * support multiple themes.
1046
+ */
1047
+ getThemes?(): Theme[]
1048
+ /**
1049
+ * Returns the theme a render WITHOUT a browser produces — a server or
1050
+ * build-time render, which has no storage and no `prefers-color-scheme`
1051
+ * to read. A hydrating client renders this theme first, so its markup
1052
+ * matches what the server sent, and switches to `getTheme()` right
1053
+ * after. Optional — a provider whose theme cannot differ between the two
1054
+ * need not implement it.
1055
+ */
1056
+ getServerTheme?(): Theme
1190
1057
  }
1191
1058
  ```
1192
1059
 
1193
- #### `StoreConfig`
1060
+ #### `Theme`
1194
1061
 
1195
- Configuration for creating a store (initial state, optional name, and middleware chain).
1062
+ Complete theme definition.
1196
1063
 
1197
1064
  ```typescript
1198
- interface StoreConfig<T> {
1199
- /**
1200
- * Initial state value.
1201
- */
1202
- initialState: T
1203
- /**
1204
- * Optional name for debugging.
1065
+ interface Theme {
1066
+ name: string
1067
+ mode: 'light' | 'dark'
1068
+ colors: ThemeColors
1069
+ breakpoints: ThemeBreakpoints
1070
+ spacing: ThemeSpacing
1071
+ typography: ThemeTypography
1072
+ borderRadius: ThemeBorderRadius
1073
+ shadows: ThemeShadows
1074
+ transitions: ThemeTransitions
1075
+ zIndex: ThemeZIndex
1076
+ }
1077
+ ```
1078
+
1079
+ #### `AsyncStateManager`
1080
+
1081
+ Async state manager.
1082
+
1083
+ ```typescript
1084
+ interface AsyncStateManager<T> {
1085
+ state$: Observable<T>
1086
+ getState: () => T
1087
+ setState: (value: T | ((prev: T) => T) | Promise<T | ((prev: T) => T)>) => void
1088
+ extendState: (
1089
+ partial:
1090
+ Partial<T> | ((prev: T) => Partial<T>) | Promise<Partial<T> | ((prev: T) => Partial<T>)>,
1091
+ ) => void
1092
+ destroy: () => void
1093
+ }
1094
+ ```
1095
+
1096
+ #### `CapacitorAppManager`
1097
+
1098
+ Capacitor app manager interface.
1099
+
1100
+ ```typescript
1101
+ interface CapacitorAppManager {
1102
+ state$: Observable<CapacitorAppState>
1103
+ ready$: Observable<boolean>
1104
+ initialize: () => Promise<void>
1105
+ destroy: () => void
1106
+ }
1107
+ ```
1108
+
1109
+ #### `ChangePasswordStateManager`
1110
+
1111
+ Change password state manager.
1112
+
1113
+ ```typescript
1114
+ interface ChangePasswordStateManager {
1115
+ state$: Observable<PromiseState<void>>
1116
+ getState: () => PromiseState<void>
1117
+ changePassword: (oldPassword: string, newPassword: string) => Promise<void>
1118
+ reset: () => void
1119
+ destroy: () => void
1120
+ }
1121
+ ```
1122
+
1123
+ #### `DeviceService`
1124
+
1125
+ Device service interface.
1126
+
1127
+ ```typescript
1128
+ interface DeviceService {
1129
+ deviceInfo: DeviceInfo
1130
+ screenInfo: ScreenInfo
1131
+ hardwareInfo: HardwareInfo
1132
+ featureSupport: FeatureSupport
1133
+ supports: (feature: keyof FeatureSupport) => boolean
1134
+ isOnline: () => boolean
1135
+ isStandalone: () => boolean
1136
+ language: string
1137
+ languages: string[]
1138
+ }
1139
+ ```
1140
+
1141
+ #### `HttpState`
1142
+
1143
+ State for HTTP requests.
1144
+
1145
+ ```typescript
1146
+ interface HttpState<T> {
1147
+ data: T | null
1148
+ loading: boolean
1149
+ error: Error | null
1150
+ }
1151
+ ```
1152
+
1153
+ #### `LoginStateManager`
1154
+
1155
+ Login state manager.
1156
+
1157
+ ```typescript
1158
+ interface LoginStateManager<T = unknown> {
1159
+ state$: Observable<PromiseState<AuthResult<T>>>
1160
+ getState: () => PromiseState<AuthResult<T>>
1161
+ login: (credentials: LoginCredentials) => Promise<AuthResult<T>>
1162
+ reset: () => void
1163
+ destroy: () => void
1164
+ }
1165
+ ```
1166
+
1167
+ #### `OAuthOptions`
1168
+
1169
+ OAuth configuration options.
1170
+
1171
+ ```typescript
1172
+ interface OAuthOptions {
1173
+ /** Base URL for the API server (e.g. `https://api.example.com`). */
1174
+ baseURL?: string
1175
+ /** List of supported OAuth provider names (e.g. `['github', 'google']`). */
1176
+ oauthProviders?: string[]
1177
+ /** Path prefix for OAuth initiation routes. Defaults to `/oauth`. */
1178
+ oauthEndpoint?: string
1179
+ /** Path for the OAuth login POST endpoint. Defaults to `/users/log-in/oauth`. */
1180
+ loginEndpoint?: string
1181
+ /** Called after a successful OAuth login (session established). */
1182
+ onSuccess?: () => void
1183
+ /**
1184
+ * Called with a failure message when the OAuth login fails. Failures are
1185
+ * also emitted on {@link OAuthStateManager.error$}.
1186
+ */
1187
+ onError?: (error: string) => void
1188
+ /**
1189
+ * Auth client used to establish the session after the code exchange. This
1190
+ * is THE way to wire session establishment in Angular: inject the
1191
+ * `AUTH_CLIENT` token and pass the client here. When omitted, the helper
1192
+ * falls back to the server-established httpOnly-cookie session (see
1193
+ * {@link OAuthStateManager.handleCallback}).
1194
+ */
1195
+ authClient?: AuthClient<unknown>
1196
+ }
1197
+ ```
1198
+
1199
+ #### `OAuthStateManager`
1200
+
1201
+ OAuth state manager.
1202
+
1203
+ ```typescript
1204
+ interface OAuthStateManager {
1205
+ /** Observable of the configured OAuth provider names. */
1206
+ providers$: Observable<string[]>
1207
+ /**
1208
+ * Observable of OAuth failure messages (from the callback code exchange).
1209
+ * Completed by {@link OAuthStateManager.destroy}.
1210
+ */
1211
+ error$: Observable<string>
1212
+ /** Returns the configured OAuth provider names. */
1213
+ getProviders: () => string[]
1214
+ /** Builds the OAuth initiation URL for a provider. */
1215
+ getOAuthUrl: (provider: string) => string
1216
+ /** Starts the full-page redirect flow for a provider. */
1217
+ redirect: (provider: string) => void
1218
+ /**
1219
+ * Handles the OAuth callback: exchanges the `code` URL parameter for a
1220
+ * session. Invoked automatically at creation (see {@link createOAuthState});
1221
+ * exposed for callers that need to re-run it manually. No-ops unless
1222
+ * running in a browser with a `code` URL parameter and a stashed provider.
1223
+ *
1224
+ * When an auth client was provided, the session is established locally
1225
+ * (`setAccessToken` + `setUser` + `initialize`). When no client is
1226
+ * available, the server has already established the httpOnly-cookie
1227
+ * session during the exchange, so a user-carrying response still counts
1228
+ * as success.
1229
+ */
1230
+ handleCallback: () => Promise<void>
1231
+ /** Completes the `providers$` and `error$` observables. */
1232
+ destroy: () => void
1233
+ }
1234
+ ```
1235
+
1236
+ #### `PasswordResetStateManager`
1237
+
1238
+ Password reset state manager.
1239
+
1240
+ ```typescript
1241
+ interface PasswordResetStateManager {
1242
+ requestState$: Observable<PromiseState<void>>
1243
+ confirmState$: Observable<PromiseState<void>>
1244
+ getRequestState: () => PromiseState<void>
1245
+ getConfirmState: () => PromiseState<void>
1246
+ requestReset: (data: PasswordResetRequest) => Promise<void>
1247
+ confirmReset: (data: PasswordResetConfirm) => Promise<void>
1248
+ reset: () => void
1249
+ destroy: () => void
1250
+ }
1251
+ ```
1252
+
1253
+ #### `PlatformService`
1254
+
1255
+ Platform service interface.
1256
+
1257
+ ```typescript
1258
+ interface PlatformService {
1259
+ platform: Platform
1260
+ isNative: boolean
1261
+ isMobile: boolean
1262
+ isDesktop: boolean
1263
+ isWeb: boolean
1264
+ isDevelopment: boolean
1265
+ isProduction: boolean
1266
+ isPlatform: (...platforms: Platform[]) => boolean
1267
+ }
1268
+ ```
1269
+
1270
+ #### `PromiseStateManager`
1271
+
1272
+ Promise state manager.
1273
+
1274
+ ```typescript
1275
+ interface PromiseStateManager<T> {
1276
+ state$: Observable<PromiseState<T>>
1277
+ getState: () => PromiseState<T>
1278
+ call: (...args: any[]) => Promise<T>
1279
+ cancel: (message?: string) => void
1280
+ reset: () => void
1281
+ destroy: () => void
1282
+ }
1283
+ ```
1284
+
1285
+ **Returns:** The result.
1286
+
1287
+ #### `PushService`
1288
+
1289
+ Push service interface.
1290
+
1291
+ ```typescript
1292
+ interface PushService {
1293
+ permission$: Observable<PermissionStatus | null>
1294
+ token$: Observable<PushToken | null>
1295
+ getPermission: () => PermissionStatus | null
1296
+ getToken: () => PushToken | null
1297
+ checkPermission: () => Promise<PermissionStatus>
1298
+ requestPermission: () => Promise<PermissionStatus>
1299
+ register: (options?: PushRegisterOptions) => Promise<PushToken>
1300
+ unregister: () => Promise<void>
1301
+ onNotificationReceived: (listener: NotificationReceivedListener) => () => void
1302
+ onNotificationAction: (listener: NotificationActionListener) => () => void
1303
+ onTokenChange: (listener: TokenChangeListener) => () => void
1304
+ setBadge: (count: number) => Promise<void>
1305
+ clearBadge: () => Promise<void>
1306
+ destroy: () => void
1307
+ }
1308
+ ```
1309
+
1310
+ #### `SignupStateManager`
1311
+
1312
+ Signup state manager.
1313
+
1314
+ ```typescript
1315
+ interface SignupStateManager<T = unknown> {
1316
+ state$: Observable<PromiseState<AuthResult<T>>>
1317
+ getState: () => PromiseState<AuthResult<T>>
1318
+ signup: (data: RegisterData) => Promise<AuthResult<T>>
1319
+ reset: () => void
1320
+ destroy: () => void
1321
+ }
1322
+ ```
1323
+
1324
+ #### `StorageValueState`
1325
+
1326
+ State for a storage value.
1327
+
1328
+ ```typescript
1329
+ interface StorageValueState<T> {
1330
+ value: T | undefined
1331
+ loading: boolean
1332
+ error: Error | null
1333
+ }
1334
+ ```
1335
+
1336
+ #### `VersionService`
1337
+
1338
+ Version service interface.
1339
+
1340
+ ```typescript
1341
+ interface VersionService {
1342
+ state$: Observable<VersionState>
1343
+ isUpdateAvailable$: Observable<boolean>
1344
+ isChecking$: Observable<boolean>
1345
+ isServiceWorkerWaiting$: Observable<boolean>
1346
+ newVersion$: Observable<string | undefined>
1347
+ getState: () => VersionState
1348
+ checkForUpdates: () => Promise<boolean>
1349
+ applyUpdate: (options?: { force?: boolean }) => void
1350
+ dismissUpdate: () => void
1351
+ startPeriodicChecks: (options?: UpdateCheckOptions) => void
1352
+ stopPeriodicChecks: () => void
1353
+ destroy: () => void
1354
+ }
1355
+ ```
1356
+
1357
+ #### `MoleculeModuleConfig`
1358
+
1359
+ Configuration for molecule Angular module.
1360
+
1361
+ ```typescript
1362
+ interface MoleculeModuleConfig {
1363
+ state?: StateProvider
1364
+ auth?: AuthClient<unknown>
1365
+ theme?: ThemeProvider
1366
+ router?: Router
1367
+ i18n?: I18nProvider
1368
+ http?: HttpClient
1369
+ storage?: StorageProvider
1370
+ logger?: LoggerProvider
1371
+ }
1372
+ ```
1373
+
1374
+ #### `MoleculeTokens`
1375
+
1376
+ Injection tokens for molecule services.
1377
+
1378
+ ```typescript
1379
+ interface MoleculeTokens {
1380
+ STATE_PROVIDER: symbol
1381
+ AUTH_CLIENT: symbol
1382
+ THEME_PROVIDER: symbol
1383
+ ROUTER: symbol
1384
+ I18N_PROVIDER: symbol
1385
+ HTTP_CLIENT: symbol
1386
+ STORAGE_PROVIDER: symbol
1387
+ LOGGER_PROVIDER: symbol
1388
+ }
1389
+ ```
1390
+
1391
+ ### Types
1392
+
1393
+ #### `RouteParams`
1394
+
1395
+ URL path parameter key-value map extracted from dynamic route segments (e.g. `{ id: '123' }`).
1396
+
1397
+ ```typescript
1398
+ type RouteParams = Record<string, string>
1399
+ ```
1400
+
1401
+ #### `QueryParams`
1402
+
1403
+ URL query string parameter map (single values or arrays for repeated keys).
1404
+
1405
+ ```typescript
1406
+ type QueryParams = Record<string, string | string[] | undefined>
1407
+ ```
1408
+
1409
+ ### Classes
1410
+
1411
+ #### `MoleculeAuthService`
1412
+
1413
+ Angular service for authentication.
1414
+
1415
+ Wraps molecule auth client and exposes state as RxJS observables.
1416
+
1417
+ ```typescript
1418
+ @Injectable({
1419
+ providedIn: 'root',
1420
+ })
1421
+ export class MoleculeAuthService<
1422
+ T = unknown,
1423
+ TRegister extends RegisterExtraShape<TRegister> = Record<never, never>,
1424
+ > implements OnDestroy {
1425
+ private stateSubject: BehaviorSubject<AuthState<T>>
1426
+ private unsubscribe: (() => void) | null = null
1427
+
1428
+ /**
1429
+ * Observable of the full auth state.
1430
+ */
1431
+ state$: Observable<AuthState<T>>
1432
+
1433
+ /**
1434
+ * Observable of the authenticated user.
1435
+ */
1436
+ user$: Observable<T | null>
1437
+
1438
+ /**
1439
+ * Observable of authentication status.
1440
+ */
1441
+ isAuthenticated$: Observable<boolean>
1442
+
1443
+ /**
1444
+ * Observable of loading status.
1445
+ */
1446
+ isLoading$: Observable<boolean>
1447
+
1448
+ constructor(@Inject(AUTH_CLIENT) private client: AuthClient<T, TRegister>) {
1449
+ this.stateSubject = new BehaviorSubject<AuthState<T>>(client.getState())
1450
+
1451
+ this.unsubscribe = client.onAuthChange(() => {
1452
+ this.stateSubject.next(client.getState())
1453
+ })
1454
+
1455
+ this.state$ = this.stateSubject.asObservable()
1456
+
1457
+ this.user$ = this.state$.pipe(
1458
+ map((state) => state.user),
1459
+ distinctUntilChanged(),
1460
+ )
1461
+
1462
+ this.isAuthenticated$ = this.state$.pipe(
1463
+ map((state) => state.authenticated),
1464
+ distinctUntilChanged(),
1465
+ )
1466
+
1467
+ this.isLoading$ = this.state$.pipe(
1468
+ map((state) => state.loading),
1469
+ distinctUntilChanged(),
1470
+ )
1471
+ }
1472
+
1473
+ /**
1474
+ * Get the current auth state snapshot.
1475
+ * @returns The current authentication state including user, loading, and authenticated flags.
1476
+ */
1477
+ get state(): AuthState<T> {
1478
+ return this.client.getState()
1479
+ }
1480
+
1481
+ /**
1482
+ * Get the current user snapshot.
1483
+ * @returns The authenticated user object, or `null` if not authenticated.
1484
+ */
1485
+ get user(): T | null {
1486
+ return this.state.user
1487
+ }
1488
+
1489
+ /**
1490
+ * Check if user is currently authenticated.
1491
+ * @returns `true` if the user is authenticated, `false` otherwise.
1492
+ */
1493
+ get isAuthenticated(): boolean {
1494
+ return this.state.authenticated
1495
+ }
1496
+
1497
+ /**
1498
+ * Login with credentials.
1499
+ *
1500
+ * @param credentials - The email/password or provider-specific login credentials.
1501
+ * @returns A promise that resolves to the authentication result with user data and tokens.
1502
+ */
1503
+ login(credentials: LoginCredentials): Promise<AuthResult<T>> {
1504
+ return this.client.login(credentials)
1505
+ }
1506
+
1507
+ /**
1508
+ * Logout the current user.
1509
+ * @returns A promise that resolves when the user has been logged out.
1510
+ */
1511
+ logout(): Promise<void> {
1512
+ return this.client.logout()
1513
+ }
1514
+
1515
+ /**
1516
+ * Register a new user.
1517
+ *
1518
+ * @param data - The registration payload including email, password, and profile fields.
1519
+ * @returns A promise that resolves to the authentication result for the newly registered user.
1520
+ */
1521
+ register(data: RegisterData<TRegister>): Promise<AuthResult<T>> {
1522
+ return this.client.register(data)
1523
+ }
1524
+
1525
+ /**
1526
+ * Refresh the authentication token.
1527
+ * @returns A promise that resolves to the authentication result with refreshed tokens.
1528
+ */
1529
+ refresh(): Promise<AuthResult<T>> {
1530
+ return this.client.refresh()
1531
+ }
1532
+
1533
+ /**
1534
+ * Clean up auth state subscription on service destruction.
1535
+ */
1536
+ ngOnDestroy(): void {
1537
+ this.unsubscribe?.()
1538
+ this.stateSubject.complete()
1539
+ }
1540
+ }
1541
+ ```
1542
+
1543
+ #### `MoleculeFormInstance`
1544
+
1545
+ Wrapper around a FormController that provides RxJS observables and
1546
+ synchronous accessors for use in Angular components.
1547
+
1548
+ ```typescript
1549
+ class MoleculeFormInstance<T extends Record<string, unknown>> {
1550
+ private stateSubject: BehaviorSubject<FormState<T>>
1551
+ private unsubscribe: (() => void) | null = null
1552
+
1553
+ /**
1554
+ * Observable of the full form state.
1555
+ */
1556
+ state$: Observable<FormState<T>>
1557
+
1558
+ /**
1559
+ * Observable of form validity.
1560
+ */
1561
+ isValid$: Observable<boolean>
1562
+
1563
+ /**
1564
+ * Observable of form dirty state.
1565
+ */
1566
+ isDirty$: Observable<boolean>
1567
+
1568
+ /**
1569
+ * Observable of form submitting state.
1570
+ */
1571
+ isSubmitting$: Observable<boolean>
1572
+
1573
+ /**
1574
+ * Observable of field errors.
1575
+ */
1576
+ errors$: Observable<Partial<Record<keyof T, string>>>
1577
+
1578
+ constructor(private controller: FormController<T>) {
1579
+ this.stateSubject = new BehaviorSubject<FormState<T>>(controller.getState())
1580
+
1581
+ this.unsubscribe = controller.subscribe((state: FormState<T>) => {
1582
+ this.stateSubject.next(state)
1583
+ })
1584
+
1585
+ this.state$ = this.stateSubject.asObservable()
1586
+
1587
+ this.isValid$ = this.state$.pipe(
1588
+ map((state) => state.isValid),
1589
+ distinctUntilChanged(),
1590
+ )
1591
+
1592
+ this.isDirty$ = this.state$.pipe(
1593
+ map((state) => state.isDirty),
1594
+ distinctUntilChanged(),
1595
+ )
1596
+
1597
+ this.isSubmitting$ = this.state$.pipe(
1598
+ map((state) => state.isSubmitting),
1599
+ distinctUntilChanged(),
1600
+ )
1601
+
1602
+ this.errors$ = this.state$.pipe(
1603
+ map((state) => state.errors),
1604
+ distinctUntilChanged(),
1605
+ )
1606
+ }
1607
+
1608
+ /**
1609
+ * Get the current form state snapshot.
1610
+ * @returns The current form state including values, errors, validity, and submission status.
1611
+ */
1612
+ get state(): FormState<T> {
1613
+ return this.controller.getState()
1614
+ }
1615
+
1616
+ /**
1617
+ * Get the current form validity snapshot.
1618
+ * @returns `true` if all fields pass validation, `false` otherwise.
1619
+ */
1620
+ get isValid(): boolean {
1621
+ return this.controller.getState().isValid
1622
+ }
1623
+
1624
+ /**
1625
+ * Get the current form dirty state snapshot.
1626
+ * @returns `true` if any field has been modified from its default value, `false` otherwise.
1627
+ */
1628
+ get isDirty(): boolean {
1629
+ return this.controller.getState().isDirty
1630
+ }
1631
+
1632
+ /**
1633
+ * Get the current form submitting state snapshot.
1634
+ * @returns `true` if the form is currently being submitted, `false` otherwise.
1635
+ */
1636
+ get isSubmitting(): boolean {
1637
+ return this.controller.getState().isSubmitting
1638
+ }
1639
+
1640
+ /**
1641
+ * Register a field for form management.
1642
+ *
1643
+ * @param name - The field name corresponding to a key in the form values type.
1644
+ * @param options - Registration options including validation rules and default value.
1645
+ * @returns The field registration with value, onChange, onBlur, and ref bindings.
1646
+ */
1647
+ register(name: keyof T, options?: Omit<RegisterOptions, 'name'>): FieldRegistration {
1648
+ return this.controller.register({ ...options, name: name as string } as RegisterOptions)
1649
+ }
1650
+
1651
+ /**
1652
+ * Get the value of a specific field.
1653
+ *
1654
+ * @param name - The field name corresponding to a key in the form values type.
1655
+ * @returns The current value of the specified field.
1656
+ */
1657
+ getValue<K extends keyof T>(name: K): T[K] {
1658
+ return this.controller.getValue(name)
1659
+ }
1660
+
1661
+ /**
1662
+ * Set the value of a specific field.
1663
+ *
1664
+ * @param name - The field name corresponding to a key in the form values type.
1665
+ * @param value - The new value to set for the field.
1666
+ */
1667
+ setValue<K extends keyof T>(name: K, value: T[K]): void {
1668
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1669
+ this.controller.setValue(name as any, value as any)
1670
+ }
1671
+
1672
+ /**
1673
+ * Get the error for a specific field.
1674
+ *
1675
+ * @param name - The field name to retrieve the error for.
1676
+ * @returns The error message for the field, or `undefined` if the field has no error.
1677
+ */
1678
+ getError(name: keyof T): string | undefined {
1679
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1680
+ return this.controller.getError(name as any)
1681
+ }
1682
+
1683
+ /**
1684
+ * Set the error for a specific field.
1685
+ *
1686
+ * @param name - The field name to set or clear the error for.
1687
+ * @param error - The error message to set, or `undefined` to clear the error.
1688
+ */
1689
+ setError(name: keyof T, error: string | undefined): void {
1690
+ if (error !== undefined) {
1691
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1692
+ this.controller.setError(name as any, error)
1693
+ } else {
1694
+ this.controller.clearError(name)
1695
+ }
1696
+ }
1697
+
1698
+ /**
1699
+ * Clear all field errors.
1700
+ */
1701
+ clearErrors(): void {
1702
+ this.controller.clearErrors()
1703
+ }
1704
+
1705
+ /**
1706
+ * Create a submit handler function.
1707
+ *
1708
+ * @param onSubmit - Callback invoked with validated form values on successful submission.
1709
+ * @param onError - Optional callback invoked with field errors when validation fails.
1710
+ * @returns A submit handler function that can be called from form submit events.
1711
+ */
1712
+ handleSubmit(
1713
+ onSubmit: (values: T) => void | Promise<void>,
1714
+ onError?: (errors: Partial<Record<keyof T, string>>) => void,
1715
+ ): (event?: { preventDefault?: () => void }) => Promise<void> {
1716
+ return this.controller.handleSubmit(onSubmit, onError)
1717
+ }
1718
+
1719
+ /**
1720
+ * Reset the form to initial values.
1721
+ *
1722
+ * @param values - Optional partial values to use as the new initial state.
1723
+ */
1724
+ reset(values?: Partial<T>): void {
1725
+ this.controller.reset(values)
1726
+ }
1727
+
1728
+ /**
1729
+ * Validate all fields.
1730
+ *
1731
+ * @returns A promise that resolves to `true` if all fields are valid, `false` otherwise.
1732
+ */
1733
+ validate(): Promise<boolean> {
1734
+ return this.controller.validate()
1735
+ }
1736
+
1737
+ /**
1738
+ * Destroy the form instance and clean up subscriptions.
1739
+ */
1740
+ destroy(): void {
1741
+ this.unsubscribe?.()
1742
+ this.unsubscribe = null
1743
+ this.stateSubject.complete()
1744
+ this.controller.destroy()
1745
+ }
1746
+ }
1747
+ ```
1748
+
1749
+ #### `MoleculeFormsService`
1750
+
1751
+ Angular service for form handling.
1752
+
1753
+ Wraps molecule form providers and creates form instances that expose
1754
+ state as RxJS observables.
1755
+
1756
+ ```typescript
1757
+ @Injectable({
1758
+ providedIn: 'root',
1759
+ })
1760
+ export class MoleculeFormsService implements OnDestroy {
1761
+ private instances: MoleculeFormInstance<Record<string, unknown>>[] = []
1762
+
1763
+ /**
1764
+ * Create a form instance from a provider.
1765
+ *
1766
+ * @param provider - The form provider that creates form controllers.
1767
+ * @param options - Form configuration including default values and validation mode.
1768
+ * @returns A new form instance with RxJS observables for reactive state tracking.
1769
+ */
1770
+ createForm<T extends Record<string, unknown>>(
1771
+ provider: FormProvider,
1772
+ options: FormOptions<T>,
1773
+ ): MoleculeFormInstance<T> {
1774
+ const controller = provider.createForm(options)
1775
+ const instance = new MoleculeFormInstance<T>(controller)
1776
+ this.instances.push(instance as MoleculeFormInstance<Record<string, unknown>>)
1777
+ return instance
1778
+ }
1779
+
1780
+ /**
1781
+ * Destroy all managed form instances on service destruction.
1782
+ */
1783
+ ngOnDestroy(): void {
1784
+ for (const instance of this.instances) {
1785
+ instance.destroy()
1786
+ }
1787
+ this.instances = []
1788
+ }
1789
+ }
1790
+ ```
1791
+
1792
+ #### `MoleculeHttpService`
1793
+
1794
+ Angular service for HTTP requests.
1795
+
1796
+ Wraps molecule HTTP client and returns RxJS observables.
1797
+
1798
+ ```typescript
1799
+ @Injectable({
1800
+ providedIn: 'root',
1801
+ })
1802
+ /**
1803
+ * Molecule Http Service class.
1804
+ */
1805
+ export class MoleculeHttpService {
1806
+ constructor(@Inject(HTTP_CLIENT) private client: HttpClient) {}
1807
+
1808
+ /**
1809
+ * Make a GET request.
1810
+ *
1811
+ * @param url - Request URL
1812
+ * @param config - Request configuration
1813
+ * @returns Observable of response data
1814
+ */
1815
+ get<T>(url: string, config?: RequestConfig): Observable<T> {
1816
+ return from(this.client.get<T>(url, config)).pipe(map((res) => res.data))
1817
+ }
1818
+
1819
+ /**
1820
+ * Make a POST request.
1821
+ *
1822
+ * @param url - Request URL
1823
+ * @param data - Request body
1824
+ * @param config - Request configuration
1825
+ * @returns Observable of response data
1826
+ */
1827
+ post<T>(url: string, data?: unknown, config?: RequestConfig): Observable<T> {
1828
+ return from(this.client.post<T>(url, data, config)).pipe(map((res) => res.data))
1829
+ }
1830
+
1831
+ /**
1832
+ * Make a PUT request.
1833
+ *
1834
+ * @param url - Request URL
1835
+ * @param data - Request body
1836
+ * @param config - Request configuration
1837
+ * @returns Observable of response data
1838
+ */
1839
+ put<T>(url: string, data?: unknown, config?: RequestConfig): Observable<T> {
1840
+ return from(this.client.put<T>(url, data, config)).pipe(map((res) => res.data))
1841
+ }
1842
+
1843
+ /**
1844
+ * Make a PATCH request.
1845
+ *
1846
+ * @param url - Request URL
1847
+ * @param data - Request body
1848
+ * @param config - Request configuration
1849
+ * @returns Observable of response data
1850
+ */
1851
+ patch<T>(url: string, data?: unknown, config?: RequestConfig): Observable<T> {
1852
+ return from(this.client.patch<T>(url, data, config)).pipe(map((res) => res.data))
1853
+ }
1854
+
1855
+ /**
1856
+ * Make a DELETE request.
1857
+ *
1858
+ * @param url - Request URL
1859
+ * @param config - Request configuration
1860
+ * @returns Observable of response data
1861
+ */
1862
+ delete<T>(url: string, config?: RequestConfig): Observable<T> {
1863
+ return from(this.client.delete<T>(url, config)).pipe(map((res) => res.data))
1864
+ }
1865
+
1866
+ /**
1867
+ * Get the full response (including headers, status, etc.).
1868
+ *
1869
+ * @param method - HTTP method
1870
+ * @param url - Request URL
1871
+ * @param config - Request configuration
1872
+ * @returns Observable of full response
1873
+ */
1874
+ request<T>(
1875
+ method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE',
1876
+ url: string,
1877
+ config?: RequestConfig,
1878
+ ): Observable<HttpResponse<T>> {
1879
+ const promise = (async () => {
1880
+ switch (method) {
1881
+ case 'GET':
1882
+ return this.client.get<T>(url, config)
1883
+ case 'POST':
1884
+ return this.client.post<T>(
1885
+ url,
1886
+ (config as RequestConfig & { data?: unknown })?.data,
1887
+ config,
1888
+ )
1889
+ case 'PUT':
1890
+ return this.client.put<T>(
1891
+ url,
1892
+ (config as RequestConfig & { data?: unknown })?.data,
1893
+ config,
1894
+ )
1895
+ case 'PATCH':
1896
+ return this.client.patch<T>(
1897
+ url,
1898
+ (config as RequestConfig & { data?: unknown })?.data,
1899
+ config,
1900
+ )
1901
+ case 'DELETE':
1902
+ return this.client.delete<T>(url, config)
1903
+ }
1904
+ })()
1905
+
1906
+ return from(promise)
1907
+ }
1908
+
1909
+ /**
1910
+ * Create a state subject for managing request state.
1911
+ *
1912
+ * @returns The created instance.
1913
+ */
1914
+ createState<T>(): BehaviorSubject<HttpState<T>> {
1915
+ return new BehaviorSubject<HttpState<T>>({
1916
+ data: null,
1917
+ loading: false,
1918
+ error: null,
1919
+ })
1920
+ }
1921
+
1922
+ /**
1923
+ * Make a GET request with state management.
1924
+ *
1925
+ * @param url - Request URL
1926
+ * @param state - State subject to update
1927
+ * @param config - Request configuration
1928
+ */
1929
+ getWithState<T>(url: string, state: BehaviorSubject<HttpState<T>>, config?: RequestConfig): void {
1930
+ state.next({ ...state.value, loading: true, error: null })
1931
+
1932
+ this.get<T>(url, config)
1933
+ .pipe(
1934
+ tap((data) => state.next({ data, loading: false, error: null })),
1935
+ catchError((error) => {
1936
+ state.next({ data: null, loading: false, error })
1937
+ throw error
1938
+ }),
1939
+ )
1940
+ .subscribe()
1941
+ }
1942
+ }
1943
+ ```
1944
+
1945
+ #### `MoleculeI18nService`
1946
+
1947
+ Angular service for internationalization.
1948
+
1949
+ Wraps molecule i18n provider and exposes state as RxJS observables.
1950
+
1951
+ ```typescript
1952
+ @Injectable({
1953
+ providedIn: 'root',
1954
+ })
1955
+ export class MoleculeI18nService implements OnDestroy {
1956
+ private localeSubject: BehaviorSubject<string>
1957
+ private directionSubject: BehaviorSubject<'ltr' | 'rtl'>
1958
+ private localesSubject: BehaviorSubject<LocaleConfig[]>
1959
+ private unsubscribe: (() => void) | null = null
1960
+
1961
+ /**
1962
+ * Observable of the current locale code.
1963
+ */
1964
+ locale$: Observable<string>
1965
+
1966
+ /**
1967
+ * Observable of the text direction.
1968
+ */
1969
+ direction$: Observable<'ltr' | 'rtl'>
1970
+
1971
+ /**
1972
+ * Observable of available locales.
1973
+ */
1974
+ locales$: Observable<LocaleConfig[]>
1975
+
1976
+ constructor(@Inject(I18N_PROVIDER) private provider: I18nProvider) {
1977
+ this.localeSubject = new BehaviorSubject<string>(provider.getLocale())
1978
+ this.directionSubject = new BehaviorSubject<'ltr' | 'rtl'>(provider.getDirection())
1979
+ this.localesSubject = new BehaviorSubject<LocaleConfig[]>(provider.getLocales())
1980
+
1981
+ this.unsubscribe = provider.onLocaleChange(() => {
1982
+ this.localeSubject.next(provider.getLocale())
1983
+ this.directionSubject.next(provider.getDirection())
1984
+ this.localesSubject.next(provider.getLocales())
1985
+ })
1986
+
1987
+ this.locale$ = this.localeSubject.asObservable()
1988
+ this.direction$ = this.directionSubject.asObservable()
1989
+ this.locales$ = this.localesSubject.asObservable()
1990
+ }
1991
+
1992
+ /**
1993
+ * Get the current locale code snapshot.
1994
+ * @returns The active locale code (e.g. `'en'`, `'fr'`).
1995
+ */
1996
+ get locale(): string {
1997
+ return this.provider.getLocale()
1998
+ }
1999
+
2000
+ /**
2001
+ * Get the current text direction snapshot.
2002
+ * @returns The text direction for the active locale, either `'ltr'` or `'rtl'`.
2003
+ */
2004
+ get direction(): 'ltr' | 'rtl' {
2005
+ return this.provider.getDirection()
2006
+ }
2007
+
2008
+ /**
2009
+ * Translate a key.
2010
+ *
2011
+ * @param key - The dot-notation translation key to look up.
2012
+ * @param values - Optional interpolation values to substitute into the translated string.
2013
+ * @returns The translated and interpolated string for the current locale.
2014
+ */
2015
+ t(key: string, values?: InterpolationValues): string {
2016
+ return this.provider.t(key, values)
2017
+ }
2018
+
2019
+ /**
2020
+ * Set the current locale.
2021
+ *
2022
+ * @param code - The locale code to switch to (e.g. `'en'`, `'fr'`).
2023
+ */
2024
+ setLocale(code: string): void {
2025
+ this.provider.setLocale(code)
2026
+ }
2027
+
2028
+ /**
2029
+ * Format a number.
2030
+ *
2031
+ * @param value - The number to format.
2032
+ * @param options - Locale-aware formatting options such as style, currency, and precision.
2033
+ * @returns The locale-formatted number string.
2034
+ */
2035
+ formatNumber(value: number, options?: NumberFormatOptions): string {
2036
+ return this.provider.formatNumber(value, options)
2037
+ }
2038
+
2039
+ /**
2040
+ * Format a date.
2041
+ *
2042
+ * @param value - The date to format, as a Date object, timestamp, or ISO string.
2043
+ * @param options - Locale-aware formatting options such as date style and time zone.
2044
+ * @returns The locale-formatted date string.
2045
+ */
2046
+ formatDate(value: Date | number | string, options?: DateFormatOptions): string {
2047
+ return this.provider.formatDate(value, options)
2048
+ }
2049
+
2050
+ /**
2051
+ * Clean up locale change subscription and complete all subjects on service destruction.
2052
+ */
2053
+ ngOnDestroy(): void {
2054
+ this.unsubscribe?.()
2055
+ this.localeSubject.complete()
2056
+ this.directionSubject.complete()
2057
+ this.localesSubject.complete()
2058
+ }
2059
+ }
2060
+ ```
2061
+
2062
+ #### `MoleculeLoggerService`
2063
+
2064
+ Angular service for logging.
2065
+
2066
+ Wraps molecule logger provider.
2067
+
2068
+ ```typescript
2069
+ @Injectable({
2070
+ providedIn: 'root',
2071
+ })
2072
+ export class MoleculeLoggerService {
2073
+ constructor(@Inject(LOGGER_PROVIDER) private provider: LoggerProvider) {}
2074
+
2075
+ /**
2076
+ * Get a logger instance.
2077
+ *
2078
+ * @param name - Logger name (usually component/service name)
2079
+ * @returns The result.
2080
+ */
2081
+ getLogger(name?: string): Logger {
2082
+ return this.provider.getLogger(name)
2083
+ }
2084
+
2085
+ /**
2086
+ * Create a logger with custom configuration.
2087
+ *
2088
+ * @param config - Logger configuration
2089
+ * @returns The created instance.
2090
+ */
2091
+ createLogger(config: LoggerConfig): Logger {
2092
+ return this.provider.createLogger(config)
2093
+ }
2094
+
2095
+ /**
2096
+ * Set the global log level.
2097
+ *
2098
+ * @param level - Log level
2099
+ */
2100
+ setLevel(level: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'silent'): void {
2101
+ this.provider.setLevel(level)
2102
+ }
2103
+
2104
+ /**
2105
+ * Get the current global log level.
2106
+ *
2107
+ * @returns The result.
2108
+ */
2109
+ getLevel(): string {
2110
+ return this.provider.getLevel()
2111
+ }
2112
+
2113
+ /**
2114
+ * Enable logging.
2115
+ */
2116
+ enable(): void {
2117
+ this.provider.enable()
2118
+ }
2119
+
2120
+ /**
2121
+ * Disable logging.
2122
+ */
2123
+ disable(): void {
2124
+ this.provider.disable()
2125
+ }
2126
+
2127
+ /**
2128
+ * Check if logging is enabled.
2129
+ *
2130
+ * @returns Whether logging is enabled
2131
+ */
2132
+ isEnabled(): boolean {
2133
+ return this.provider.isEnabled()
2134
+ }
2135
+ }
2136
+ ```
2137
+
2138
+ #### `MoleculeRouterService`
2139
+
2140
+ Angular service for routing.
2141
+
2142
+ Wraps molecule router and exposes state as RxJS observables.
2143
+
2144
+ ```typescript
2145
+ @Injectable({
2146
+ providedIn: 'root',
2147
+ })
2148
+ /**
2149
+ * Molecule Router Service class.
2150
+ */
2151
+ export class MoleculeRouterService implements OnDestroy {
2152
+ private locationSubject: BehaviorSubject<RouteLocation>
2153
+ private paramsSubject: BehaviorSubject<RouteParams>
2154
+ private querySubject: BehaviorSubject<QueryParams>
2155
+ private unsubscribe: (() => void) | null = null
2156
+
2157
+ /**
2158
+ * Observable of the current location.
2159
+ */
2160
+ location$: Observable<RouteLocation>
2161
+
2162
+ /**
2163
+ * Observable of route parameters.
2164
+ */
2165
+ params$: Observable<RouteParams>
2166
+
2167
+ /**
2168
+ * Observable of query parameters.
2169
+ */
2170
+ query$: Observable<QueryParams>
2171
+
2172
+ /**
2173
+ * Observable of the current pathname.
2174
+ */
2175
+ pathname$: Observable<string>
2176
+
2177
+ constructor(@Inject(ROUTER) private router: Router) {
2178
+ this.locationSubject = new BehaviorSubject<RouteLocation>(router.getLocation())
2179
+ this.paramsSubject = new BehaviorSubject<RouteParams>(router.getParams())
2180
+ this.querySubject = new BehaviorSubject<QueryParams>(router.getQuery())
2181
+
2182
+ this.unsubscribe = router.subscribe(() => {
2183
+ this.locationSubject.next(router.getLocation())
2184
+ this.paramsSubject.next(router.getParams())
2185
+ this.querySubject.next(router.getQuery())
2186
+ })
2187
+
2188
+ this.location$ = this.locationSubject.asObservable()
2189
+ this.params$ = this.paramsSubject.asObservable()
2190
+ this.query$ = this.querySubject.asObservable()
2191
+
2192
+ this.pathname$ = this.location$.pipe(
2193
+ map((loc) => loc.pathname),
2194
+ distinctUntilChanged(),
2195
+ )
2196
+ }
2197
+
2198
+ /**
2199
+ * Get the current location snapshot.
2200
+ * @returns The result.
2201
+ */
2202
+ get location(): RouteLocation {
2203
+ return this.router.getLocation()
2204
+ }
2205
+
2206
+ /**
2207
+ * Get the current params snapshot.
2208
+ * @returns The result.
1205
2209
  */
1206
- name?: string
2210
+ get params(): RouteParams {
2211
+ return this.router.getParams()
2212
+ }
2213
+
1207
2214
  /**
1208
- * Optional middleware functions.
2215
+ * Get the current query snapshot.
2216
+ * @returns The result.
1209
2217
  */
1210
- middleware?: StoreMiddleware<T>[]
2218
+ get query(): QueryParams {
2219
+ return this.router.getQuery()
2220
+ }
2221
+
2222
+ /**
2223
+ * Navigate to a path.
2224
+ *
2225
+ * @param path - Target path
2226
+ * @param options - Navigation options
2227
+ */
2228
+ navigate(path: string, options?: NavigateOptions): void {
2229
+ this.router.navigate(path, options)
2230
+ }
2231
+
2232
+ /**
2233
+ * Navigate to a named route.
2234
+ *
2235
+ * @param name - Route name
2236
+ * @param params - Route parameters
2237
+ * @param query - Query parameters
2238
+ * @param options - Navigation options
2239
+ */
2240
+ navigateTo(
2241
+ name: string,
2242
+ params?: RouteParams,
2243
+ query?: QueryParams,
2244
+ options?: NavigateOptions,
2245
+ ): void {
2246
+ this.router.navigateTo(name, params, query, options)
2247
+ }
2248
+
2249
+ /**
2250
+ * Go back in history.
2251
+ */
2252
+ back(): void {
2253
+ this.router.back()
2254
+ }
2255
+
2256
+ /**
2257
+ * Go forward in history.
2258
+ */
2259
+ forward(): void {
2260
+ this.router.forward()
2261
+ }
2262
+
2263
+ /**
2264
+ * Check if a path is currently active.
2265
+ *
2266
+ * @param path - Path to check
2267
+ * @param exact - Whether to match exactly
2268
+ * @returns The result.
2269
+ */
2270
+ isActive(path: string, exact = false): boolean {
2271
+ return this.router.isActive(path, exact)
2272
+ }
2273
+
2274
+ /**
2275
+ * Cleans up subscriptions and completes subjects on service destruction.
2276
+ */
2277
+ ngOnDestroy(): void {
2278
+ this.unsubscribe?.()
2279
+ this.locationSubject.complete()
2280
+ this.paramsSubject.complete()
2281
+ this.querySubject.complete()
2282
+ }
1211
2283
  }
1212
2284
  ```
1213
2285
 
1214
- #### `Theme`
2286
+ #### `MoleculeStateService`
1215
2287
 
1216
- Complete theme definition.
2288
+ Angular service for state management.
2289
+
2290
+ Wraps molecule state stores and exposes them as RxJS observables.
1217
2291
 
1218
2292
  ```typescript
1219
- interface Theme {
1220
- name: string
1221
- mode: 'light' | 'dark'
1222
- colors: ThemeColors
1223
- breakpoints: ThemeBreakpoints
1224
- spacing: ThemeSpacing
1225
- typography: ThemeTypography
1226
- borderRadius: ThemeBorderRadius
1227
- shadows: ThemeShadows
1228
- transitions: ThemeTransitions
1229
- zIndex: ThemeZIndex
2293
+ @Injectable({
2294
+ providedIn: 'root',
2295
+ })
2296
+ /**
2297
+ * Molecule State Service class.
2298
+ */
2299
+ export class MoleculeStateService implements OnDestroy {
2300
+ private subscriptions = new Map<Store<unknown>, () => void>()
2301
+ private subjects = new Map<Store<unknown>, BehaviorSubject<unknown>>()
2302
+
2303
+ constructor(@Inject(STATE_PROVIDER) private provider: StateProvider) {}
2304
+
2305
+ /**
2306
+ * Get an observable of the full store state.
2307
+ *
2308
+ * @param store - The store to observe
2309
+ * @returns Observable of the store state
2310
+ */
2311
+ observe<T>(store: Store<T>): Observable<T> {
2312
+ return this.getOrCreateSubject(store).asObservable() as Observable<T>
2313
+ }
2314
+
2315
+ /**
2316
+ * Select a piece of state from a store.
2317
+ *
2318
+ * @param store - The store to select from
2319
+ * @param selector - Selector function
2320
+ * @returns Observable of the selected state
2321
+ */
2322
+ select<T, S>(store: Store<T>, selector: (state: T) => S): Observable<S> {
2323
+ return this.observe(store).pipe(map(selector), distinctUntilChanged())
2324
+ }
2325
+
2326
+ /**
2327
+ * Get the current snapshot of a store's state.
2328
+ *
2329
+ * @param store - The store
2330
+ * @returns The result.
2331
+ */
2332
+ snapshot<T>(store: Store<T>): T {
2333
+ return store.getState()
2334
+ }
2335
+
2336
+ /**
2337
+ * Update a store's state.
2338
+ *
2339
+ * @param store - The store to update
2340
+ * @param partial - Partial state or updater function
2341
+ */
2342
+ update<T>(store: Store<T>, partial: Partial<T> | ((state: T) => Partial<T>)): void {
2343
+ store.setState(partial)
2344
+ }
2345
+
2346
+ /**
2347
+ * Gets or creates a BehaviorSubject for the given store.
2348
+ * @param store - The data store to observe.
2349
+ * @returns The BehaviorSubject tracking the store state.
2350
+ */
2351
+ private getOrCreateSubject<T>(store: Store<T>): BehaviorSubject<T> {
2352
+ let subject = this.subjects.get(store as Store<unknown>) as BehaviorSubject<T> | undefined
2353
+
2354
+ if (!subject) {
2355
+ subject = new BehaviorSubject<T>(store.getState())
2356
+ this.subjects.set(store as Store<unknown>, subject as BehaviorSubject<unknown>)
2357
+
2358
+ const unsubscribe = store.subscribe(() => {
2359
+ subject!.next(store.getState())
2360
+ })
2361
+ this.subscriptions.set(store as Store<unknown>, unsubscribe)
2362
+ }
2363
+
2364
+ return subject
2365
+ }
2366
+
2367
+ /**
2368
+ * Cleans up all store subscriptions and completes subjects on service destruction.
2369
+ */
2370
+ ngOnDestroy(): void {
2371
+ // Clean up all subscriptions
2372
+ this.subscriptions.forEach((unsubscribe) => unsubscribe())
2373
+ this.subscriptions.clear()
2374
+
2375
+ // Complete all subjects
2376
+ this.subjects.forEach((subject) => subject.complete())
2377
+ this.subjects.clear()
2378
+ }
1230
2379
  }
1231
2380
  ```
1232
2381
 
1233
- #### `ThemeProvider`
2382
+ #### `MoleculeStorageService`
1234
2383
 
1235
- Manages theme state including the active theme, mode toggling,
1236
- and change subscriptions.
2384
+ Angular service for storage.
2385
+
2386
+ Wraps molecule storage provider and returns RxJS observables.
1237
2387
 
1238
2388
  ```typescript
1239
- interface ThemeProvider {
2389
+ @Injectable({
2390
+ providedIn: 'root',
2391
+ })
2392
+ /**
2393
+ * Molecule Storage Service class.
2394
+ */
2395
+ export class MoleculeStorageService {
2396
+ constructor(@Inject(STORAGE_PROVIDER) private provider: StorageProvider) {}
2397
+
1240
2398
  /**
1241
- * Returns the currently active theme.
2399
+ * Get a value from storage.
2400
+ *
2401
+ * @param key - Storage key
2402
+ * @returns Observable of the value
1242
2403
  */
1243
- getTheme(): Theme
2404
+ get<T>(key: string): Observable<T | null> {
2405
+ return from(this.provider.get<T>(key))
2406
+ }
2407
+
1244
2408
  /**
1245
- * Sets the active theme by reference or by name.
2409
+ * Set a value in storage.
1246
2410
  *
1247
- * @param theme - A `Theme` object or a theme name string to activate.
2411
+ * @param key - Storage key
2412
+ * @param value - Value to store
2413
+ * @returns Observable that completes when done
1248
2414
  */
1249
- setTheme(theme: Theme | string): void
2415
+ set<T>(key: string, value: T): Observable<void> {
2416
+ return from(this.provider.set(key, value))
2417
+ }
2418
+
1250
2419
  /**
1251
- * Toggles between light and dark mode for the active theme.
2420
+ * Remove a value from storage.
2421
+ *
2422
+ * @param key - Storage key
2423
+ * @returns Observable that completes when done
1252
2424
  */
1253
- toggleMode(): void
2425
+ remove(key: string): Observable<void> {
2426
+ return from(this.provider.remove(key))
2427
+ }
2428
+
1254
2429
  /**
1255
- * Subscribes to theme changes. The callback fires whenever
1256
- * `setTheme()` or `toggleMode()` is called.
2430
+ * Clear all values from storage.
1257
2431
  *
1258
- * @param callback - Invoked with the new theme after each change.
1259
- * @returns An unsubscribe function.
2432
+ * @returns Observable that completes when done
1260
2433
  */
1261
- subscribe(callback: (theme: Theme) => void): () => void
2434
+ clear(): Observable<void> {
2435
+ return from(this.provider.clear())
2436
+ }
2437
+
1262
2438
  /**
1263
- * Returns all registered themes. Optional — not all providers
1264
- * support multiple themes.
2439
+ * Get all keys in storage.
2440
+ *
2441
+ * @returns Observable of key array
1265
2442
  */
1266
- getThemes?(): Theme[]
2443
+ keys(): Observable<string[]> {
2444
+ return from(this.provider.keys())
2445
+ }
2446
+
1267
2447
  /**
1268
- * Returns the theme a render WITHOUT a browser produces — a server or
1269
- * build-time render, which has no storage and no `prefers-color-scheme`
1270
- * to read. A hydrating client renders this theme first, so its markup
1271
- * matches what the server sent, and switches to `getTheme()` right
1272
- * after. Optional — a provider whose theme cannot differ between the two
1273
- * need not implement it.
1274
- */
1275
- getServerTheme?(): Theme
2448
+ * Create a reactive storage value with auto-loading.
2449
+ *
2450
+ * @param key - Storage key
2451
+ * @param defaultValue - Default value if not found
2452
+ * @returns BehaviorSubject that syncs with storage
2453
+ */
2454
+ createValue<T>(key: string, defaultValue: T): BehaviorSubject<StorageValueState<T>> {
2455
+ const state = new BehaviorSubject<StorageValueState<T>>({
2456
+ value: defaultValue,
2457
+ loading: true,
2458
+ error: null,
2459
+ })
2460
+
2461
+ // Load initial value
2462
+ this.get<T>(key).subscribe({
2463
+ next: (value) => {
2464
+ state.next({
2465
+ value: value ?? defaultValue,
2466
+ loading: false,
2467
+ error: null,
2468
+ })
2469
+ },
2470
+ error: (error) => {
2471
+ state.next({
2472
+ value: defaultValue,
2473
+ loading: false,
2474
+ error,
2475
+ })
2476
+ },
2477
+ })
2478
+
2479
+ return state
2480
+ }
2481
+
2482
+ /**
2483
+ * Update a reactive storage value.
2484
+ *
2485
+ * @param key - Storage key
2486
+ * @param value - New value
2487
+ * @param state - State subject to update
2488
+ */
2489
+ updateValue<T>(key: string, value: T, state: BehaviorSubject<StorageValueState<T>>): void {
2490
+ state.next({ ...state.value, loading: true })
2491
+
2492
+ this.set(key, value).subscribe({
2493
+ next: () => {
2494
+ state.next({ value, loading: false, error: null })
2495
+ },
2496
+ error: (error) => {
2497
+ state.next({ ...state.value, loading: false, error })
2498
+ },
2499
+ })
2500
+ }
1276
2501
  }
1277
2502
  ```
1278
2503
 
1279
- #### `VersionService`
2504
+ #### `MoleculeThemeService`
1280
2505
 
1281
- Version service interface.
2506
+ Angular service for theming.
2507
+
2508
+ Wraps molecule theme provider and exposes state as RxJS observables.
1282
2509
 
1283
2510
  ```typescript
1284
- interface VersionService {
1285
- state$: Observable<VersionState>
1286
- isUpdateAvailable$: Observable<boolean>
1287
- isChecking$: Observable<boolean>
1288
- isServiceWorkerWaiting$: Observable<boolean>
1289
- newVersion$: Observable<string | undefined>
1290
- getState: () => VersionState
1291
- checkForUpdates: () => Promise<boolean>
1292
- applyUpdate: (options?: { force?: boolean }) => void
1293
- dismissUpdate: () => void
1294
- startPeriodicChecks: (options?: UpdateCheckOptions) => void
1295
- stopPeriodicChecks: () => void
1296
- destroy: () => void
2511
+ @Injectable({
2512
+ providedIn: 'root',
2513
+ })
2514
+ /**
2515
+ * Molecule Theme Service class.
2516
+ */
2517
+ export class MoleculeThemeService implements OnDestroy {
2518
+ private themeSubject: BehaviorSubject<Theme>
2519
+ private themeNameSubject: BehaviorSubject<string>
2520
+ private unsubscribe: (() => void) | null = null
2521
+
2522
+ /**
2523
+ * Observable of the current theme.
2524
+ */
2525
+ theme$: Observable<Theme>
2526
+
2527
+ /**
2528
+ * Observable of the current theme name.
2529
+ */
2530
+ themeName$: Observable<string>
2531
+
2532
+ /**
2533
+ * Observable of the theme mode (light/dark).
2534
+ */
2535
+ mode$: Observable<'light' | 'dark'>
2536
+
2537
+ constructor(@Inject(THEME_PROVIDER) private provider: ThemeProvider) {
2538
+ this.themeSubject = new BehaviorSubject<Theme>(provider.getTheme())
2539
+ this.themeNameSubject = new BehaviorSubject<string>(provider.getTheme().name)
2540
+
2541
+ this.unsubscribe = provider.subscribe(() => {
2542
+ this.themeSubject.next(provider.getTheme())
2543
+ this.themeNameSubject.next(provider.getTheme().name)
2544
+ })
2545
+
2546
+ this.theme$ = this.themeSubject.asObservable()
2547
+ this.themeName$ = this.themeNameSubject.asObservable()
2548
+
2549
+ this.mode$ = this.theme$.pipe(
2550
+ map((theme: Theme) => theme.mode),
2551
+ distinctUntilChanged(),
2552
+ )
2553
+ }
2554
+
2555
+ /**
2556
+ * Get the current theme snapshot.
2557
+ * @returns The result.
2558
+ */
2559
+ get theme(): Theme {
2560
+ return this.provider.getTheme()
2561
+ }
2562
+
2563
+ /**
2564
+ * Get the current theme name snapshot.
2565
+ * @returns The result.
2566
+ */
2567
+ get themeName(): string {
2568
+ return this.provider.getTheme().name
2569
+ }
2570
+
2571
+ /**
2572
+ * Get available themes.
2573
+ * @returns The result.
2574
+ */
2575
+ get themes(): Theme[] {
2576
+ return this.provider.getThemes?.() ?? []
2577
+ }
2578
+
2579
+ /**
2580
+ * Set the current theme.
2581
+ *
2582
+ * @param name - Theme name
2583
+ */
2584
+ setTheme(name: string): void {
2585
+ this.provider.setTheme(name)
2586
+ }
2587
+
2588
+ /**
2589
+ * Toggle to the next theme.
2590
+ */
2591
+ toggleTheme(): void {
2592
+ const themes = this.themes
2593
+ const currentName = this.themeName
2594
+ const currentIndex = themes.findIndex((t: Theme) => t.name === currentName)
2595
+ const nextIndex = (currentIndex + 1) % themes.length
2596
+ this.provider.setTheme(themes[nextIndex])
2597
+ }
2598
+
2599
+ /**
2600
+ * Cleans up subscriptions and completes subjects on service destruction.
2601
+ */
2602
+ ngOnDestroy(): void {
2603
+ this.unsubscribe?.()
2604
+ this.themeSubject.complete()
2605
+ this.themeNameSubject.complete()
2606
+ }
1297
2607
  }
1298
2608
  ```
1299
2609
 
1300
- ### Types
2610
+ ### Functions
1301
2611
 
1302
- #### `QueryParams`
2612
+ #### `bumpLocaleVersion()`
1303
2613
 
1304
- URL query string parameter map (single values or arrays for repeated keys).
2614
+ Bump the locale version signal, causing all template bindings that use
2615
+ the reactive `t()` to re-evaluate on the next change detection cycle.
2616
+
2617
+ Called automatically by the `ENVIRONMENT_INITIALIZER` in `provideMolecule`.
1305
2618
 
1306
2619
  ```typescript
1307
- type QueryParams = Record<string, string | string[] | undefined>
2620
+ function bumpLocaleVersion(): void
1308
2621
  ```
1309
2622
 
1310
- #### `RouteParams`
2623
+ #### `t(key, values, options)`
1311
2624
 
1312
- URL path parameter key-value map extracted from dynamic route segments (e.g. `{ id: '123' }`).
2625
+ Translate a key using the current locale.
2626
+
2627
+ This is a signal-aware wrapper around `@molecule/app-i18n`'s `t()`.
2628
+ Reading the internal locale signal establishes an Angular reactivity
2629
+ dependency, so template bindings that call this function will be
2630
+ re-evaluated when the locale changes.
1313
2631
 
1314
2632
  ```typescript
1315
- type RouteParams = Record<string, string>
2633
+ function t(
2634
+ key: string,
2635
+ values?: InterpolationValues,
2636
+ options?: { defaultValue?: string; count?: number },
2637
+ ): string
1316
2638
  ```
1317
2639
 
1318
- ### Classes
2640
+ - `key` — Translation key
2641
+ - `values` — Interpolation values
2642
+ - `options` — Options (defaultValue, count).
2643
+ - `options.defaultValue` — Fallback string when no translation is found.
2644
+ - `options.count` — Pluralization count.
2645
+
2646
+ **Returns:** Translated string
2647
+
2648
+ #### `provideState(provider)`
2649
+
2650
+ Provide state management.
2651
+
2652
+ ```typescript
2653
+ function provideState(provider: StateProvider): EnvironmentProviders
2654
+ ```
2655
+
2656
+ - `provider` — State provider
2657
+
2658
+ **Returns:** Environment providers
2659
+
2660
+ #### `provideAuth(client)`
2661
+
2662
+ Registers an AuthClient as an Angular environment provider for dependency injection.
2663
+
2664
+ ```typescript
2665
+ function provideAuth(client: AuthClient<T, Record<never, never>>): EnvironmentProviders
2666
+ ```
2667
+
2668
+ - `client` — Auth client
1319
2669
 
1320
- #### `MoleculeAuthService`
2670
+ **Returns:** Environment providers
1321
2671
 
1322
- Angular service for authentication.
2672
+ #### `provideTheme(provider)`
1323
2673
 
1324
- Wraps molecule auth client and exposes state as RxJS observables.
2674
+ Registers a ThemeProvider as an Angular environment provider for dependency injection.
1325
2675
 
1326
- #### `MoleculeFormInstance`
2676
+ ```typescript
2677
+ function provideTheme(provider: ThemeProvider): EnvironmentProviders
2678
+ ```
1327
2679
 
1328
- Wrapper around a FormController that provides RxJS observables and
1329
- synchronous accessors for use in Angular components.
2680
+ - `provider` — Theme provider
1330
2681
 
1331
- #### `MoleculeFormsService`
2682
+ **Returns:** Environment providers
1332
2683
 
1333
- Angular service for form handling.
2684
+ #### `provideRouter(router)`
1334
2685
 
1335
- Wraps molecule form providers and creates form instances that expose
1336
- state as RxJS observables.
2686
+ Registers a Router as an Angular environment provider for dependency injection.
1337
2687
 
1338
- #### `MoleculeHttpService`
2688
+ ```typescript
2689
+ function provideRouter(router: Router): EnvironmentProviders
2690
+ ```
1339
2691
 
1340
- Angular service for HTTP requests.
2692
+ - `router` — Router instance
1341
2693
 
1342
- Wraps molecule HTTP client and returns RxJS observables.
2694
+ **Returns:** Environment providers
1343
2695
 
1344
- #### `MoleculeI18nService`
2696
+ #### `provideI18n(provider)`
1345
2697
 
1346
- Angular service for internationalization.
2698
+ Registers an I18nProvider as an Angular environment provider for dependency injection.
1347
2699
 
1348
- Wraps molecule i18n provider and exposes state as RxJS observables.
2700
+ ```typescript
2701
+ function provideI18n(provider: I18nProvider): EnvironmentProviders
2702
+ ```
1349
2703
 
1350
- #### `MoleculeLoggerService`
2704
+ - `provider` — I18n provider
1351
2705
 
1352
- Angular service for logging.
2706
+ **Returns:** Environment providers
1353
2707
 
1354
- Wraps molecule logger provider.
2708
+ #### `provideHttp(client)`
1355
2709
 
1356
- #### `MoleculeRouterService`
2710
+ Provide HTTP client.
1357
2711
 
1358
- Angular service for routing.
2712
+ ```typescript
2713
+ function provideHttp(client: HttpClient): EnvironmentProviders
2714
+ ```
1359
2715
 
1360
- Wraps molecule router and exposes state as RxJS observables.
2716
+ - `client` — HTTP client
1361
2717
 
1362
- #### `MoleculeStateService`
2718
+ **Returns:** Environment providers
1363
2719
 
1364
- Angular service for state management.
2720
+ #### `provideStorage(provider)`
1365
2721
 
1366
- Wraps molecule state stores and exposes them as RxJS observables.
2722
+ Registers a StorageProvider as an Angular environment provider for dependency injection.
1367
2723
 
1368
- #### `MoleculeStorageService`
2724
+ ```typescript
2725
+ function provideStorage(provider: StorageProvider): EnvironmentProviders
2726
+ ```
1369
2727
 
1370
- Angular service for storage.
2728
+ - `provider` — Storage provider
1371
2729
 
1372
- Wraps molecule storage provider and returns RxJS observables.
2730
+ **Returns:** Environment providers
1373
2731
 
1374
- #### `MoleculeThemeService`
2732
+ #### `provideLogger(provider)`
1375
2733
 
1376
- Angular service for theming.
2734
+ Registers a LoggerProvider as an Angular environment provider for dependency injection.
1377
2735
 
1378
- Wraps molecule theme provider and exposes state as RxJS observables.
2736
+ ```typescript
2737
+ function provideLogger(provider: LoggerProvider): EnvironmentProviders
2738
+ ```
1379
2739
 
1380
- ### Functions
2740
+ - `provider` — Logger provider
1381
2741
 
1382
- #### `bumpLocaleVersion()`
2742
+ **Returns:** Environment providers
1383
2743
 
1384
- Bump the locale version signal, causing all template bindings that use
1385
- the reactive `t()` to re-evaluate on the next change detection cycle.
2744
+ #### `provideMolecule(config)`
1386
2745
 
1387
- Called automatically by the `ENVIRONMENT_INITIALIZER` in `provideMolecule`.
2746
+ Provide all molecule services at once.
1388
2747
 
1389
2748
  ```typescript
1390
- function bumpLocaleVersion(): void
2749
+ function provideMolecule(config: MoleculeModuleConfig): EnvironmentProviders
1391
2750
  ```
1392
2751
 
2752
+ - `config` — Configuration with all providers
2753
+
2754
+ **Returns:** Environment providers
2755
+
1393
2756
  #### `createAsyncState(initialState)`
1394
2757
 
1395
2758
  Creates an async-capable state manager.
@@ -1422,7 +2785,9 @@ function createCapacitorAppState(options?: CapacitorAppOptions): CapacitorAppMan
1422
2785
  Creates a change password state manager with async state tracking.
1423
2786
 
1424
2787
  ```typescript
1425
- function createChangePasswordState(client: AuthClient<UserProfile>): ChangePasswordStateManager
2788
+ function createChangePasswordState(
2789
+ client: AuthClient<UserProfile, Record<never, never>>,
2790
+ ): ChangePasswordStateManager
1426
2791
  ```
1427
2792
 
1428
2793
  - `client` — Auth client
@@ -1444,7 +2809,7 @@ function createDeviceService(): DeviceService
1444
2809
  Creates a login state manager with async state tracking.
1445
2810
 
1446
2811
  ```typescript
1447
- function createLoginState(client: AuthClient<T>): LoginStateManager<T>
2812
+ function createLoginState(client: AuthClient<T, Record<never, never>>): LoginStateManager<T>
1448
2813
  ```
1449
2814
 
1450
2815
  - `client` — Auth client
@@ -1472,7 +2837,9 @@ function createOAuthState(options?: OAuthOptions): OAuthStateManager
1472
2837
  Creates a password reset state manager with async state tracking.
1473
2838
 
1474
2839
  ```typescript
1475
- function createPasswordResetState(client: AuthClient<UserProfile>): PasswordResetStateManager
2840
+ function createPasswordResetState(
2841
+ client: AuthClient<UserProfile, Record<never, never>>,
2842
+ ): PasswordResetStateManager
1476
2843
  ```
1477
2844
 
1478
2845
  - `client` — Auth client
@@ -1516,7 +2883,7 @@ function createPushService(): PushService
1516
2883
  Creates a signup state manager with async state tracking.
1517
2884
 
1518
2885
  ```typescript
1519
- function createSignupState(client: AuthClient<T>): SignupStateManager<T>
2886
+ function createSignupState(client: AuthClient<T, Record<never, never>>): SignupStateManager<T>
1520
2887
  ```
1521
2888
 
1522
2889
  - `client` — Auth client
@@ -1533,155 +2900,38 @@ function createVersionService(): VersionService
1533
2900
 
1534
2901
  **Returns:** The created instance.
1535
2902
 
1536
- #### `provideAuth(client)`
1537
-
1538
- Registers an AuthClient as an Angular environment provider for dependency injection.
1539
-
1540
- ```typescript
1541
- function provideAuth(client: AuthClient<T>): EnvironmentProviders
1542
- ```
1543
-
1544
- - `client` — Auth client
1545
-
1546
- **Returns:** Environment providers
1547
-
1548
- #### `provideHttp(client)`
1549
-
1550
- Provide HTTP client.
1551
-
1552
- ```typescript
1553
- function provideHttp(client: HttpClient): EnvironmentProviders
1554
- ```
1555
-
1556
- - `client` — HTTP client
1557
-
1558
- **Returns:** Environment providers
1559
-
1560
- #### `provideI18n(provider)`
1561
-
1562
- Registers an I18nProvider as an Angular environment provider for dependency injection.
1563
-
1564
- ```typescript
1565
- function provideI18n(provider: I18nProvider): EnvironmentProviders
1566
- ```
1567
-
1568
- - `provider` — I18n provider
1569
-
1570
- **Returns:** Environment providers
1571
-
1572
- #### `provideLogger(provider)`
1573
-
1574
- Registers a LoggerProvider as an Angular environment provider for dependency injection.
1575
-
1576
- ```typescript
1577
- function provideLogger(provider: LoggerProvider): EnvironmentProviders
1578
- ```
1579
-
1580
- - `provider` — Logger provider
1581
-
1582
- **Returns:** Environment providers
1583
-
1584
- #### `provideMolecule(config)`
1585
-
1586
- Provide all molecule services at once.
1587
-
1588
- ```typescript
1589
- function provideMolecule(config: MoleculeModuleConfig): EnvironmentProviders
1590
- ```
1591
-
1592
- - `config` — Configuration with all providers
1593
-
1594
- **Returns:** Environment providers
1595
-
1596
- #### `provideRouter(router)`
1597
-
1598
- Registers a Router as an Angular environment provider for dependency injection.
1599
-
1600
- ```typescript
1601
- function provideRouter(router: Router): EnvironmentProviders
1602
- ```
1603
-
1604
- - `router` — Router instance
1605
-
1606
- **Returns:** Environment providers
1607
-
1608
- #### `provideState(provider)`
1609
-
1610
- Provide state management.
1611
-
1612
- ```typescript
1613
- function provideState(provider: StateProvider): EnvironmentProviders
1614
- ```
1615
-
1616
- - `provider` — State provider
1617
-
1618
- **Returns:** Environment providers
1619
-
1620
- #### `provideStorage(provider)`
1621
-
1622
- Registers a StorageProvider as an Angular environment provider for dependency injection.
1623
-
1624
- ```typescript
1625
- function provideStorage(provider: StorageProvider): EnvironmentProviders
1626
- ```
1627
-
1628
- - `provider` — Storage provider
1629
-
1630
- **Returns:** Environment providers
2903
+ ### Constants
1631
2904
 
1632
- #### `provideTheme(provider)`
2905
+ #### `STATE_PROVIDER`
1633
2906
 
1634
- Registers a ThemeProvider as an Angular environment provider for dependency injection.
2907
+ Injection token for state provider.
1635
2908
 
1636
2909
  ```typescript
1637
- function provideTheme(provider: ThemeProvider): EnvironmentProviders
2910
+ const STATE_PROVIDER: InjectionToken<StateProvider>
1638
2911
  ```
1639
2912
 
1640
- - `provider` — Theme provider
1641
-
1642
- **Returns:** Environment providers
1643
-
1644
- #### `t(key, values, options)`
1645
-
1646
- Translate a key using the current locale.
2913
+ #### `AUTH_CLIENT`
1647
2914
 
1648
- This is a signal-aware wrapper around `@molecule/app-i18n`'s `t()`.
1649
- Reading the internal locale signal establishes an Angular reactivity
1650
- dependency, so template bindings that call this function will be
1651
- re-evaluated when the locale changes.
2915
+ Injection token for auth client.
1652
2916
 
1653
2917
  ```typescript
1654
- function t(
1655
- key: string,
1656
- values?: InterpolationValues,
1657
- options?: { defaultValue?: string; count?: number },
1658
- ): string
2918
+ const AUTH_CLIENT: InjectionToken<AuthClient<unknown, Record<never, never>>>
1659
2919
  ```
1660
2920
 
1661
- - `key` — Translation key
1662
- - `values` — Interpolation values
1663
- - `options` — Options (defaultValue, count).
1664
- - `options.defaultValue` — Fallback string when no translation is found.
1665
- - `options.count` — Pluralization count.
1666
-
1667
- **Returns:** Translated string
1668
-
1669
- ### Constants
1670
-
1671
- #### `AUTH_CLIENT`
2921
+ #### `THEME_PROVIDER`
1672
2922
 
1673
- Injection token for auth client.
2923
+ Injection token for theme provider.
1674
2924
 
1675
2925
  ```typescript
1676
- const AUTH_CLIENT: InjectionToken<AuthClient<unknown>>
2926
+ const THEME_PROVIDER: InjectionToken<ThemeProvider>
1677
2927
  ```
1678
2928
 
1679
- #### `HTTP_CLIENT`
2929
+ #### `ROUTER`
1680
2930
 
1681
- Injection token for HTTP client.
2931
+ Injection token for router.
1682
2932
 
1683
2933
  ```typescript
1684
- const HTTP_CLIENT: InjectionToken<HttpClient>
2934
+ const ROUTER: InjectionToken<Router>
1685
2935
  ```
1686
2936
 
1687
2937
  #### `I18N_PROVIDER`
@@ -1692,28 +2942,12 @@ Injection token for i18n provider.
1692
2942
  const I18N_PROVIDER: InjectionToken<I18nProvider>
1693
2943
  ```
1694
2944
 
1695
- #### `LOGGER_PROVIDER`
1696
-
1697
- Injection token for logger provider.
1698
-
1699
- ```typescript
1700
- const LOGGER_PROVIDER: InjectionToken<LoggerProvider>
1701
- ```
1702
-
1703
- #### `ROUTER`
1704
-
1705
- Injection token for router.
1706
-
1707
- ```typescript
1708
- const ROUTER: InjectionToken<Router>
1709
- ```
1710
-
1711
- #### `STATE_PROVIDER`
2945
+ #### `HTTP_CLIENT`
1712
2946
 
1713
- Injection token for state provider.
2947
+ Injection token for HTTP client.
1714
2948
 
1715
2949
  ```typescript
1716
- const STATE_PROVIDER: InjectionToken<StateProvider>
2950
+ const HTTP_CLIENT: InjectionToken<HttpClient>
1717
2951
  ```
1718
2952
 
1719
2953
  #### `STORAGE_PROVIDER`
@@ -1724,12 +2958,12 @@ Injection token for storage provider.
1724
2958
  const STORAGE_PROVIDER: InjectionToken<StorageProvider>
1725
2959
  ```
1726
2960
 
1727
- #### `THEME_PROVIDER`
2961
+ #### `LOGGER_PROVIDER`
1728
2962
 
1729
- Injection token for theme provider.
2963
+ Injection token for logger provider.
1730
2964
 
1731
2965
  ```typescript
1732
- const THEME_PROVIDER: InjectionToken<ThemeProvider>
2966
+ const LOGGER_PROVIDER: InjectionToken<LoggerProvider>
1733
2967
  ```
1734
2968
 
1735
2969
  ## Injection Notes