runtime-config-loader 6.0.0 → 6.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -6,53 +6,237 @@ Most applications require certain configuration values that can be changed at ru
6
6
 
7
7
  This library provides an easy way to load one or more JSON files with configuration values or make one or more HTTP GET calls to an API endpoint that returns those values. The config objects that are returned from the call(s) will be combined into a single configuration object. You can then use that configuration throughout the application. The default location of the JSON file is in the `assets` folder, at `./assets/config.json`. When the service loads the file, it stores that configuration object in a local variable which can be accessed via the `getConfig()` and `getConfigObjectKey(key: string)` methods. `getConfig` returns the entire configuration object; `getConfigObjectKey(key: string)` returns part of the configuration object, namely the part defined by the key passed in. In some cases, the `config.json` is not finished loading before other modules/services are, so the above methods will return null. If that is the case, subscribe to the `configSubject` and access the configuration object that way.
8
8
 
9
- ## How to Implement
9
+ ### How to Implement
10
10
 
11
- In your `app.module.ts` file, add the following to the `@NgModule` decorator:
11
+ In your Angular application's `app.config.ts` (or wherever you provide your environment providers), use `provideRuntimeConfig`:
12
12
 
13
13
  ```ts
14
- imports: [..., RuntimeConfigLoaderModule, ...],
14
+ import { provideRuntimeConfig } from 'runtime-config-loader';
15
+
16
+ export const appConfig: ApplicationConfig = {
17
+ providers: [
18
+ // ...,
19
+ provideRuntimeConfig({ configUrl: './assets/config.json' }),
20
+ // ...
21
+ ],
22
+ };
15
23
  ```
16
24
 
17
- That's it; it's that simple. In the `RuntimeConfigLoaderModule`, the `APP_INITIALIZER` token is used to run a function which loads the configuration from a file or an API endpoint that can be used throughout the application.
25
+ That's it; it's that simple. The `provideRuntimeConfig` function sets up the `APP_INITIALIZER` token to load the configuration from a file or an API endpoint before the application starts.
18
26
 
19
- If you implement the library exactly as it is above, the configuration file needs to be in the `./assets/config.json` location as mentioned above. If you'd like to load the file from a different location, provide that location in the `.forRoot()` method when importing the `RuntimeConfigLoaderModule`:
27
+ ### Type Safety with Generics
28
+
29
+ There are two main ways to use generics for type safety in your application.
30
+
31
+ #### Option 1: Using the `RUNTIME_CONFIG` Token (Recommended)
32
+
33
+ You can provide the type directly in `provideRuntimeConfig`. This also sets up the `RUNTIME_CONFIG` injection token, which you can use to inject the typed configuration object directly into your components or services.
34
+
35
+ ```ts
36
+ // app.config.ts
37
+ provideRuntimeConfig<MyConfig>({ configUrl: './assets/config.json' })
38
+
39
+ // component.ts
40
+ import { RUNTIME_CONFIG } from 'runtime-config-loader';
41
+
42
+ constructor(@Inject(RUNTIME_CONFIG) private config: MyConfig) {
43
+ console.log(this.config.apiUrl); // Fully typed!
44
+ }
45
+ ```
46
+
47
+ #### Option 2: Using the Service with a Type Alias
48
+
49
+ If you prefer to use the `RuntimeConfigLoaderService` methods (like `getConfigObjectKey`), you can define a type alias to avoid repeating the generic type everywhere.
50
+
51
+ ```ts
52
+ // config.service.ts
53
+ export type MyConfigService = RuntimeConfigLoaderService<MyConfig>;
54
+
55
+ // component.ts
56
+ constructor(private configSvc: MyConfigService) {
57
+ const apiUrl = this.configSvc.getConfigObjectKey('apiUrl'); // Typed as string | null
58
+ }
59
+ ```
60
+
61
+ #### Option 3: Specific Configuration Tokens
62
+
63
+ If you prefer your components to depend only on specific configuration values rather than the entire configuration object, you can use the `provideConfigToken` helper. This allows you to inject individual configuration properties cleanly and maintain strict boundaries.
64
+
65
+ ```ts
66
+ import { InjectionToken } from '@angular/core';
67
+ import { provideRuntimeConfig, provideConfigToken } from 'runtime-config-loader';
68
+
69
+ // 1. Define tokens for your specific configuration values
70
+ export const API_URL = new InjectionToken<string>('API_URL');
71
+
72
+ // 2. Provide them using the helper
73
+ export const appConfig: ApplicationConfig = {
74
+ providers: [
75
+ provideRuntimeConfig<MyConfig>({ configUrl: './assets/config.json' }),
76
+
77
+ // Provide the specific token mapping it to a key in your config
78
+ provideConfigToken(API_URL, 'apiUrl'),
79
+
80
+ // It also supports dot-notation for nested keys
81
+ // provideConfigToken(NESTED_TOKEN, 'api.baseUrl')
82
+ ],
83
+ };
84
+
85
+ // 3. Inject them directly
86
+ // component.ts
87
+ constructor(@Inject(API_URL) private apiUrl: string) {
88
+ console.log(this.apiUrl); // Typed as string | null
89
+ }
90
+ ```
91
+
92
+ ### Nested Key Access (Dot-Notation)
93
+
94
+ The `getConfigObjectKey` method supports dot-notation for accessing deeply nested configuration values.
95
+
96
+ ```ts
97
+ // For a config like: { api: { baseUrl: 'https://api.com' } }
98
+ const baseUrl = this.configSvc.getConfigObjectKey('api.baseUrl'); // Returns 'https://api.com'
99
+ ```
100
+
101
+ > [!NOTE]
102
+ > When using dot-notation for nested keys, the return type will be `any` unless you provide an explicit generic type to the call:
103
+ > `this.configSvc.getConfigObjectKey<string>('api.baseUrl')`
104
+
105
+ ### Configuration
106
+
107
+ The `provideRuntimeConfig` function accepts a configuration object with a `configUrl` property. This can be a single string or an array of strings.
108
+
109
+ #### Single Config File
110
+
111
+ The default location is `./assets/config.json`. If you'd like to load the file from a different location:
112
+
113
+ ```ts
114
+ provideRuntimeConfig({ configUrl: './path/to/config/config.json' });
115
+ ```
116
+
117
+ #### Multiple Config Files
118
+
119
+ You can load multiple files (or API endpoints) and they will be merged into a single configuration object.
120
+
121
+ ```ts
122
+ provideRuntimeConfig({
123
+ configUrl: [
124
+ './path/to/config/config-1.json',
125
+ './path/to/config/config-2.json',
126
+ ],
127
+ });
128
+ ```
129
+
130
+ If an attribute is repeated in multiple configuration files, the latest value is kept. For example, if `apiUrl` exists in both files above, the value from `config-2.json` will override the value from `config-1.json`.
131
+
132
+ #### Fallback Configuration
133
+
134
+ If you'd like the application to gracefully fall back to a default configuration in the event that the config file(s) fail to load (e.g., due to network errors or 404s), you can provide a `defaultConfig` object.
20
135
 
21
136
  ```ts
22
- imports: [
23
- ...,
24
- RuntimeConfigLoaderModule.forRoot(
25
- { configUrl: './path/to/config/config.json' }
26
- ),
27
- ...]
137
+ provideRuntimeConfig({
138
+ configUrl: './assets/config.json',
139
+ defaultConfig: {
140
+ apiUrl: 'https://fallback.api.com',
141
+ theme: 'light',
142
+ },
143
+ });
28
144
  ```
29
145
 
30
- If you want to load multiple files, the value of `configUrl` should be an array of strings:
146
+ When a failure occurs and `defaultConfig` is provided, the application will emit a console warning (`Falling back to default configuration`), load the provided object, and proceed with initialization.
147
+
148
+ ### HTTP Options and Headers
149
+
150
+ You can pass standard Angular `HttpClient` options (such as custom `HttpHeaders` or `HttpContext`) to the underlying HTTP request doing the fetching.
151
+
152
+ Pass an `options` object into the `provideRuntimeConfig` function:
31
153
 
32
154
  ```ts
33
- imports: [
34
- ...,
35
- RuntimeConfigLoaderModule.forRoot(
36
- { configUrl: ['./path/to/config/config-1.json', './path/to/config/config-2.json'] }
37
- ),
38
- ...]
155
+ import { HttpHeaders } from '@angular/common/http';
156
+
157
+ provideRuntimeConfig({
158
+ configUrl: './assets/config.json',
159
+ options: {
160
+ headers: new HttpHeaders({
161
+ Authorization: 'Bearer YOUR_TOKEN_HERE',
162
+ 'X-Custom-Header': 'custom-value',
163
+ }),
164
+ withCredentials: true,
165
+ },
166
+ });
39
167
  ```
40
168
 
41
- > Make sure that the path(s) you provide here is accessible by the Angular application, meaning that the file is somewhere the app can load it. In my opinion, the `assets` folder is the easiest place to work from.
169
+ This is useful for APIs that require authentication or tracking headers to return configuration data.
42
170
 
43
- ## Multiple Config Paths
171
+ ### Runtime Schema Validation
44
172
 
45
- One reason you may want to load multiple configuration objects is so that you can set the configuration on your machine without affecting anyone else. For example, you could have a `local.config.json` file that is not included in source control. Some of the values in that file would overwrite the values in a config file that everyone can use. Another use case is that some config values don't change between environments, and some do. The ones that don't change could go in one file, the ones that do change could go in a second file. Each developer/environment can provide the second file with values they want or need.
173
+ You can provide an optional `validator` function to verify the configuration before the application starts. If validation fails (returns `false`, throws an error, or emits an error), the configuration will be set to `null`.
46
174
 
47
- It's important to know that if an attribute is repeated in two configuration files, the latest value is kept. So, let's say you have `apiUrl` in both files, `config-1.json` and `config-2.json`. Let's assume the files are passed in to the `forRoot` method like this:
175
+ The validator supports synchronous results, `Promise<boolean>`, and `Observable<boolean>`.
48
176
 
49
177
  ```ts
50
- imports: [
51
- ...,
52
- RuntimeConfigLoaderModule.forRoot(
53
- { configUrl: ['./path/to/config/config-1.json', './path/to/config/config-2.json'] }
54
- ),
55
- ...]
178
+ provideRuntimeConfig<MyConfig>({
179
+ configUrl: './assets/config.json',
180
+ validator: (config) => {
181
+ return !!config.apiUrl && !!config.apiKey;
182
+ },
183
+ });
184
+ ```
185
+
186
+ #### Integration with Zod
187
+
188
+ This feature integrates well with schema validation libraries like [Zod](https://zod.dev):
189
+
190
+ ```ts
191
+ import { z } from 'zod';
192
+
193
+ const ConfigSchema = z.object({
194
+ apiUrl: z.string().url(),
195
+ apiKey: z.string().min(10),
196
+ });
197
+
198
+ provideRuntimeConfig({
199
+ configUrl: './assets/config.json',
200
+ validator: (config) => ConfigSchema.safeParse(config).success,
201
+ });
56
202
  ```
57
203
 
58
- In this case, the `apiUrl` value from `config-2.json` will override the value from `config-1.json`.
204
+ Make sure that the path(s) you provide are accessible by the Angular application. The `assets` folder is generally the easiest place to serve these files.
205
+
206
+ ### Testing
207
+
208
+ When writing unit tests for components or services that depend on `RUNTIME_CONFIG` or `RuntimeConfigLoaderService`, you can easily mock the configuration using the `provideMockRuntimeConfig` utility.
209
+
210
+ This avoids making HTTP requests and allows you to instantly return a mocked configuration object of your choosing.
211
+
212
+ ```ts
213
+ import { TestBed } from '@angular/core/testing';
214
+ import {
215
+ provideMockRuntimeConfig,
216
+ RUNTIME_CONFIG,
217
+ RuntimeConfigLoaderService,
218
+ } from 'runtime-config-loader';
219
+
220
+ describe('MyComponent', () => {
221
+ beforeEach(() => {
222
+ TestBed.configureTestingModule({
223
+ providers: [
224
+ // Provide the mock configuration object
225
+ provideMockRuntimeConfig({
226
+ apiUrl: 'https://mock-api.com',
227
+ theme: 'dark',
228
+ }),
229
+ ],
230
+ });
231
+ });
232
+
233
+ it('should use the mocked config', () => {
234
+ const config = TestBed.inject(RUNTIME_CONFIG);
235
+ expect(config.apiUrl).toBe('https://mock-api.com');
236
+
237
+ // If testing something that uses the service directly:
238
+ const service = TestBed.inject(RuntimeConfigLoaderService);
239
+ expect(service.getConfigObjectKey('theme')).toBe('dark');
240
+ });
241
+ });
242
+ ```
@@ -1,62 +1,102 @@
1
1
  import { HttpClient, provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
2
2
  import * as i0 from '@angular/core';
3
- import { inject, Injectable, provideAppInitializer, makeEnvironmentProviders } from '@angular/core';
4
- import { Subject, forkJoin, of } from 'rxjs';
5
- import { tap, catchError, take } from 'rxjs/operators';
3
+ import { inject, Injectable, InjectionToken, provideAppInitializer, makeEnvironmentProviders } from '@angular/core';
4
+ import { ReplaySubject, forkJoin, isObservable, of, from } from 'rxjs';
5
+ import { map, catchError, switchMap, tap, take } from 'rxjs/operators';
6
6
 
7
7
  class RuntimeConfig {
8
8
  constructor(obj = {}) {
9
9
  this.configUrl = obj.configUrl || './assets/config.json';
10
+ this.defaultConfig = obj.defaultConfig;
11
+ this.validator = obj.validator;
12
+ this.options = obj.options;
10
13
  }
11
14
  }
12
15
 
13
16
  class RuntimeConfigLoaderService {
14
17
  constructor() {
15
18
  this.configUrl = './assets/config.json';
16
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
17
19
  this.configObject = null;
18
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
19
- this.configSubject = new Subject();
20
+ this.configSubject = new ReplaySubject(1);
20
21
  this._http = inject(HttpClient);
21
22
  this._config = inject(RuntimeConfig, { optional: true });
22
23
  if (this._config) {
23
24
  this.configUrl = this._config.configUrl;
24
25
  }
25
26
  }
26
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
27
27
  loadConfig() {
28
28
  const urls = Array.isArray(this.configUrl)
29
29
  ? this.configUrl
30
30
  : [this.configUrl];
31
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
32
31
  const observables = urls.map((url) => this.makeHttpCall(url));
33
- return forkJoin(observables).pipe(
34
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
35
- tap((configDataArray) => {
36
- this.configObject = configDataArray.reduce(
37
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
38
- (acc, configData) => {
32
+ return forkJoin(observables).pipe(map((configDataArray) => {
33
+ const combinedConfig = configDataArray.reduce((acc, configData) => {
39
34
  return { ...acc, ...configData };
40
35
  }, {});
36
+ if (this._config && this._config.validator) {
37
+ const validationResult = this._config.validator(combinedConfig);
38
+ if (isObservable(validationResult)) {
39
+ return validationResult.pipe(map((isValid) => this.handleValidationResult(isValid, combinedConfig)), catchError((err) => {
40
+ console.error('Config validation error', err);
41
+ return of(this.handleValidationResult(false));
42
+ }));
43
+ }
44
+ else if (validationResult instanceof Promise) {
45
+ return from(validationResult).pipe(map((isValid) => this.handleValidationResult(isValid, combinedConfig)), catchError((err) => {
46
+ console.error('Config validation error', err);
47
+ return of(this.handleValidationResult(false));
48
+ }));
49
+ }
50
+ else {
51
+ const isValid = validationResult === undefined ||
52
+ validationResult === true;
53
+ return of(this.handleValidationResult(isValid, combinedConfig));
54
+ }
55
+ }
56
+ return of(this.handleValidationResult(true, combinedConfig));
57
+ }), switchMap((obs) => obs), tap((config) => {
58
+ this.configObject = config;
41
59
  this.configSubject.next(this.configObject);
42
- }),
43
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
44
- catchError((err) => {
45
- console.error('Error loading config: ', err);
46
- this.configObject = null;
60
+ }), catchError((err) => {
61
+ console.error('Error loading config', err);
62
+ if (this._config && this._config.defaultConfig !== undefined) {
63
+ console.warn('Falling back to default configuration');
64
+ this.configObject = this._config.defaultConfig;
65
+ }
66
+ else {
67
+ this.configObject = null;
68
+ }
47
69
  this.configSubject.next(this.configObject);
48
- return of(null);
70
+ return of(this.configObject);
49
71
  }));
50
72
  }
51
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
73
+ handleValidationResult(isValid, config) {
74
+ if (isValid === false) {
75
+ console.error('Config validation failed');
76
+ return null;
77
+ }
78
+ return config || null;
79
+ }
52
80
  makeHttpCall(url) {
53
- return this._http.get(url).pipe(take(1));
81
+ return this._http.get(url, this._config?.options).pipe(take(1));
54
82
  }
55
83
  getConfig() {
56
84
  return this.configObject;
57
85
  }
58
86
  getConfigObjectKey(key) {
59
- return this.configObject ? this.configObject[key] : null;
87
+ if (!this.configObject) {
88
+ return null;
89
+ }
90
+ if (typeof key === 'string' && key.indexOf('.') > -1) {
91
+ return this.getValueByPath(this.configObject, key);
92
+ }
93
+ return this.configObject[key] ?? null;
94
+ }
95
+ getValueByPath(obj, path) {
96
+ const value = path.split('.').reduce((prev, curr) => {
97
+ return prev ? prev[curr] : null;
98
+ }, obj);
99
+ return value ?? null;
60
100
  }
61
101
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: RuntimeConfigLoaderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
62
102
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: RuntimeConfigLoaderService }); }
@@ -65,6 +105,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
65
105
  type: Injectable
66
106
  }], ctorParameters: () => [] });
67
107
 
108
+ const RUNTIME_CONFIG = new InjectionToken('RUNTIME_CONFIG');
68
109
  function initConfig(configSvc) {
69
110
  return () => configSvc.loadConfig();
70
111
  }
@@ -75,6 +116,11 @@ function provideRuntimeConfig(config) {
75
116
  useValue: config,
76
117
  },
77
118
  RuntimeConfigLoaderService,
119
+ {
120
+ provide: RUNTIME_CONFIG,
121
+ useFactory: (configSvc) => configSvc.getConfig(),
122
+ deps: [RuntimeConfigLoaderService],
123
+ },
78
124
  provideAppInitializer(() => {
79
125
  const initializerFn = initConfig(inject(RuntimeConfigLoaderService));
80
126
  return initializerFn();
@@ -83,10 +129,75 @@ function provideRuntimeConfig(config) {
83
129
  ];
84
130
  return makeEnvironmentProviders(providers);
85
131
  }
132
+ function provideConfigToken(token, key) {
133
+ return {
134
+ provide: token,
135
+ useFactory: (configSvc) => configSvc.getConfigObjectKey(key),
136
+ deps: [RuntimeConfigLoaderService],
137
+ };
138
+ }
139
+
140
+ class MockRuntimeConfigLoaderService {
141
+ constructor() {
142
+ this.configObject = null;
143
+ this.configSubject = new ReplaySubject(1);
144
+ }
145
+ loadConfig() {
146
+ this.configSubject.next(this.configObject);
147
+ return of(this.configObject);
148
+ }
149
+ getConfig() {
150
+ return this.configObject;
151
+ }
152
+ getConfigObjectKey(key) {
153
+ if (!this.configObject) {
154
+ return null;
155
+ }
156
+ if (typeof key === 'string' && key.indexOf('.') > -1) {
157
+ return this.getValueByPath(this.configObject, key);
158
+ }
159
+ return this.configObject[key] ?? null;
160
+ }
161
+ getValueByPath(obj, path) {
162
+ const value = path.split('.').reduce((prev, curr) => {
163
+ return prev ? prev[curr] : null;
164
+ }, obj);
165
+ return value ?? null;
166
+ }
167
+ setMockConfig(config) {
168
+ this.configObject = config;
169
+ this.configSubject.next(this.configObject);
170
+ }
171
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: MockRuntimeConfigLoaderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
172
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: MockRuntimeConfigLoaderService }); }
173
+ }
174
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: MockRuntimeConfigLoaderService, decorators: [{
175
+ type: Injectable
176
+ }] });
177
+
178
+ function provideMockRuntimeConfig(mockConfig) {
179
+ const mockService = new MockRuntimeConfigLoaderService();
180
+ mockService.setMockConfig(mockConfig);
181
+ const providers = [
182
+ {
183
+ provide: RuntimeConfigLoaderService,
184
+ useValue: mockService,
185
+ },
186
+ {
187
+ provide: MockRuntimeConfigLoaderService,
188
+ useValue: mockService,
189
+ },
190
+ {
191
+ provide: RUNTIME_CONFIG,
192
+ useValue: mockConfig,
193
+ },
194
+ ];
195
+ return makeEnvironmentProviders(providers);
196
+ }
86
197
 
87
198
  /**
88
199
  * Generated bundle index. Do not edit.
89
200
  */
90
201
 
91
- export { RuntimeConfig, RuntimeConfigLoaderService, initConfig, provideRuntimeConfig };
202
+ export { MockRuntimeConfigLoaderService, RUNTIME_CONFIG, RuntimeConfig, RuntimeConfigLoaderService, initConfig, provideConfigToken, provideMockRuntimeConfig, provideRuntimeConfig };
92
203
  //# sourceMappingURL=runtime-config-loader.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"runtime-config-loader.mjs","sources":["../../../../libs/runtime-config-loader/src/lib/runtime-config.ts","../../../../libs/runtime-config-loader/src/lib/runtime-config-loader/runtime-config-loader.service.ts","../../../../libs/runtime-config-loader/src/lib/runtime-config-loader.provider.ts","../../../../libs/runtime-config-loader/src/runtime-config-loader.ts"],"sourcesContent":["export class RuntimeConfig {\n\tconfigUrl: string | string[];\n\n\tconstructor(obj: { configUrl?: string | string[] } = {}) {\n\t\tthis.configUrl = obj.configUrl || './assets/config.json';\n\t}\n}\n","import { HttpClient } from '@angular/common/http';\nimport { Injectable, inject } from '@angular/core';\nimport { RuntimeConfig } from '../runtime-config';\nimport { forkJoin, Observable, of, Subject } from 'rxjs';\nimport { catchError, take, tap } from 'rxjs/operators';\n\n@Injectable()\nexport class RuntimeConfigLoaderService {\n\tprivate configUrl: string | string[] = './assets/config.json';\n\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\tprivate configObject: any = null;\n\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\tpublic configSubject: Subject<any> = new Subject<any>();\n\n\tprivate _http = inject(HttpClient);\n\tprivate _config = inject(RuntimeConfig, { optional: true });\n\n\tconstructor() {\n\t\tif (this._config) {\n\t\t\tthis.configUrl = this._config.configUrl;\n\t\t}\n\t}\n\n\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\tloadConfig(): Observable<any> {\n\t\tconst urls: string[] = Array.isArray(this.configUrl)\n\t\t\t? this.configUrl\n\t\t\t: [this.configUrl];\n\n\t\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\t\tconst observables: Observable<any>[] = urls.map((url) =>\n\t\t\tthis.makeHttpCall(url)\n\t\t);\n\n\t\treturn forkJoin(observables).pipe(\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\t\t\ttap((configDataArray: any[]) => {\n\t\t\t\tthis.configObject = configDataArray.reduce(\n\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\t\t\t\t\t(acc, configData) => {\n\t\t\t\t\t\treturn { ...acc, ...configData };\n\t\t\t\t\t},\n\t\t\t\t\t{}\n\t\t\t\t);\n\n\t\t\t\tthis.configSubject.next(this.configObject);\n\t\t\t}),\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\t\t\tcatchError((err: any) => {\n\t\t\t\tconsole.error('Error loading config: ', err);\n\t\t\t\tthis.configObject = null;\n\t\t\t\tthis.configSubject.next(this.configObject);\n\t\t\t\treturn of(null);\n\t\t\t})\n\t\t);\n\t}\n\n\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\tprivate makeHttpCall(url: string): Observable<any> {\n\t\treturn this._http.get(url).pipe(take(1));\n\t}\n\n\tgetConfig() {\n\t\treturn this.configObject;\n\t}\n\n\tgetConfigObjectKey(key: string) {\n\t\treturn this.configObject ? this.configObject[key] : null;\n\t}\n}\n","import {\n\tEnvironmentProviders,\n\tProvider,\n\tinject,\n\tmakeEnvironmentProviders,\n\tprovideAppInitializer,\n} from '@angular/core';\nimport {\n\tprovideHttpClient,\n\twithInterceptorsFromDi,\n} from '@angular/common/http';\nimport { RuntimeConfig } from './runtime-config';\nimport { RuntimeConfigLoaderService } from './runtime-config-loader/runtime-config-loader.service';\n\nexport function initConfig(configSvc: RuntimeConfigLoaderService) {\n\treturn () => configSvc.loadConfig();\n}\n\nexport function provideRuntimeConfig(\n\tconfig: RuntimeConfig\n): EnvironmentProviders {\n\tconst providers: (Provider | EnvironmentProviders)[] = [\n\t\t{\n\t\t\tprovide: RuntimeConfig,\n\t\t\tuseValue: config,\n\t\t},\n\t\tRuntimeConfigLoaderService,\n\t\tprovideAppInitializer(() => {\n\t\t\tconst initializerFn = initConfig(\n\t\t\t\tinject(RuntimeConfigLoaderService)\n\t\t\t);\n\t\t\treturn initializerFn();\n\t\t}),\n\t\tprovideHttpClient(withInterceptorsFromDi()),\n\t];\n\n\treturn makeEnvironmentProviders(providers);\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"runtime-config-loader.mjs","sources":["../../../../libs/runtime-config-loader/src/lib/runtime-config.ts","../../../../libs/runtime-config-loader/src/lib/runtime-config-loader/runtime-config-loader.service.ts","../../../../libs/runtime-config-loader/src/lib/runtime-config-loader.provider.ts","../../../../libs/runtime-config-loader/src/lib/testing/mock-runtime-config-loader.service.ts","../../../../libs/runtime-config-loader/src/lib/testing/provide-mock-runtime-config.ts","../../../../libs/runtime-config-loader/src/runtime-config-loader.ts"],"sourcesContent":["import { Observable } from 'rxjs';\nimport { HttpContext, HttpHeaders, HttpParams } from '@angular/common/http';\n\nexport class RuntimeConfig<T = any> {\n\tconfigUrl: string | string[];\n\tdefaultConfig?: T;\n\tvalidator?: (\n\t\tconfig: T\n\t) => boolean | Promise<boolean> | Observable<boolean> | void;\n\toptions?: {\n\t\theaders?: HttpHeaders | { [header: string]: string | string[] };\n\t\tcontext?: HttpContext;\n\t\tobserve?: 'body';\n\t\tparams?:\n\t\t\t| HttpParams\n\t\t\t| {\n\t\t\t\t\t[param: string]:\n\t\t\t\t\t\t| string\n\t\t\t\t\t\t| number\n\t\t\t\t\t\t| boolean\n\t\t\t\t\t\t| ReadonlyArray<string | number | boolean>;\n\t\t\t };\n\t\treportProgress?: boolean;\n\t\tresponseType?: 'json';\n\t\twithCredentials?: boolean;\n\t};\n\n\tconstructor(\n\t\tobj: {\n\t\t\tconfigUrl?: string | string[];\n\t\t\tdefaultConfig?: T;\n\t\t\tvalidator?: (\n\t\t\t\tconfig: T\n\t\t\t) => boolean | Promise<boolean> | Observable<boolean> | void;\n\t\t\toptions?: {\n\t\t\t\theaders?: HttpHeaders | { [header: string]: string | string[] };\n\t\t\t\tcontext?: HttpContext;\n\t\t\t\tobserve?: 'body';\n\t\t\t\tparams?:\n\t\t\t\t\t| HttpParams\n\t\t\t\t\t| {\n\t\t\t\t\t\t\t[param: string]:\n\t\t\t\t\t\t\t\t| string\n\t\t\t\t\t\t\t\t| number\n\t\t\t\t\t\t\t\t| boolean\n\t\t\t\t\t\t\t\t| ReadonlyArray<string | number | boolean>;\n\t\t\t\t\t };\n\t\t\t\treportProgress?: boolean;\n\t\t\t\tresponseType?: 'json';\n\t\t\t\twithCredentials?: boolean;\n\t\t\t};\n\t\t} = {}\n\t) {\n\t\tthis.configUrl = obj.configUrl || './assets/config.json';\n\t\tthis.defaultConfig = obj.defaultConfig;\n\t\tthis.validator = obj.validator;\n\t\tthis.options = obj.options;\n\t}\n}\n","import { HttpClient } from '@angular/common/http';\nimport { Injectable, inject } from '@angular/core';\nimport { RuntimeConfig } from '../runtime-config';\nimport {\n\tforkJoin,\n\tfrom,\n\tisObservable,\n\tObservable,\n\tof,\n\tReplaySubject,\n\tSubject,\n} from 'rxjs';\nimport { catchError, map, take, tap, switchMap } from 'rxjs/operators';\n\n@Injectable()\nexport class RuntimeConfigLoaderService<T = any> {\n\tprivate configUrl: string | string[] = './assets/config.json';\n\tprivate configObject: T | null = null;\n\tpublic configSubject: Subject<T | null> = new ReplaySubject<T | null>(1);\n\n\tprivate _http = inject(HttpClient);\n\tprivate _config = inject(RuntimeConfig, { optional: true });\n\n\tconstructor() {\n\t\tif (this._config) {\n\t\t\tthis.configUrl = this._config.configUrl;\n\t\t}\n\t}\n\n\tloadConfig(): Observable<T | null> {\n\t\tconst urls: string[] = Array.isArray(this.configUrl)\n\t\t\t? this.configUrl\n\t\t\t: [this.configUrl];\n\n\t\tconst observables: Observable<any>[] = urls.map((url) =>\n\t\t\tthis.makeHttpCall(url)\n\t\t);\n\n\t\treturn forkJoin(observables).pipe(\n\t\t\tmap((configDataArray: any[]) => {\n\t\t\t\tconst combinedConfig = configDataArray.reduce(\n\t\t\t\t\t(acc, configData) => {\n\t\t\t\t\t\treturn { ...acc, ...configData };\n\t\t\t\t\t},\n\t\t\t\t\t{} as T\n\t\t\t\t);\n\n\t\t\t\tif (this._config && this._config.validator) {\n\t\t\t\t\tconst validationResult =\n\t\t\t\t\t\tthis._config.validator(combinedConfig);\n\n\t\t\t\t\tif (isObservable(validationResult)) {\n\t\t\t\t\t\treturn validationResult.pipe(\n\t\t\t\t\t\t\tmap((isValid) =>\n\t\t\t\t\t\t\t\tthis.handleValidationResult(\n\t\t\t\t\t\t\t\t\tisValid,\n\t\t\t\t\t\t\t\t\tcombinedConfig\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\tcatchError((err) => {\n\t\t\t\t\t\t\t\tconsole.error('Config validation error', err);\n\t\t\t\t\t\t\t\treturn of(this.handleValidationResult(false));\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t);\n\t\t\t\t\t} else if (validationResult instanceof Promise) {\n\t\t\t\t\t\treturn from(validationResult).pipe(\n\t\t\t\t\t\t\tmap((isValid) =>\n\t\t\t\t\t\t\t\tthis.handleValidationResult(\n\t\t\t\t\t\t\t\t\tisValid,\n\t\t\t\t\t\t\t\t\tcombinedConfig\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\tcatchError((err) => {\n\t\t\t\t\t\t\t\tconsole.error('Config validation error', err);\n\t\t\t\t\t\t\t\treturn of(this.handleValidationResult(false));\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tconst isValid =\n\t\t\t\t\t\t\tvalidationResult === undefined ||\n\t\t\t\t\t\t\tvalidationResult === true;\n\t\t\t\t\t\treturn of(\n\t\t\t\t\t\t\tthis.handleValidationResult(isValid, combinedConfig)\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn of(this.handleValidationResult(true, combinedConfig));\n\t\t\t}),\n\t\t\tswitchMap((obs) => obs),\n\t\t\ttap((config) => {\n\t\t\t\tthis.configObject = config;\n\t\t\t\tthis.configSubject.next(this.configObject);\n\t\t\t}),\n\t\t\tcatchError((err) => {\n\t\t\t\tconsole.error('Error loading config', err);\n\n\t\t\t\tif (this._config && this._config.defaultConfig !== undefined) {\n\t\t\t\t\tconsole.warn('Falling back to default configuration');\n\t\t\t\t\tthis.configObject = this._config.defaultConfig;\n\t\t\t\t} else {\n\t\t\t\t\tthis.configObject = null;\n\t\t\t\t}\n\n\t\t\t\tthis.configSubject.next(this.configObject);\n\t\t\t\treturn of(this.configObject);\n\t\t\t})\n\t\t) as Observable<T | null>;\n\t}\n\n\tprivate handleValidationResult(\n\t\tisValid: boolean | void,\n\t\tconfig?: T\n\t): T | null {\n\t\tif (isValid === false) {\n\t\t\tconsole.error('Config validation failed');\n\t\t\treturn null;\n\t\t}\n\t\treturn config || null;\n\t}\n\n\tprivate makeHttpCall(url: string): Observable<any> {\n\t\treturn this._http.get(url, this._config?.options).pipe(take(1));\n\t}\n\n\tgetConfig(): T | null {\n\t\treturn this.configObject;\n\t}\n\n\tgetConfigObjectKey<K extends keyof T>(key: K): T[K] | null;\n\tgetConfigObjectKey<R = any>(key: string): R | null;\n\tgetConfigObjectKey(key: any): any {\n\t\tif (!this.configObject) {\n\t\t\treturn null;\n\t\t}\n\n\t\tif (typeof key === 'string' && key.indexOf('.') > -1) {\n\t\t\treturn this.getValueByPath(this.configObject, key);\n\t\t}\n\n\t\treturn (this.configObject as any)[key] ?? null;\n\t}\n\n\tprivate getValueByPath(obj: any, path: string): any {\n\t\tconst value = path.split('.').reduce((prev, curr) => {\n\t\t\treturn prev ? prev[curr] : null;\n\t\t}, obj);\n\t\treturn value ?? null;\n\t}\n}\n","import {\n\tEnvironmentProviders,\n\tInjectionToken,\n\tProvider,\n\tinject,\n\tmakeEnvironmentProviders,\n\tprovideAppInitializer,\n} from '@angular/core';\nimport {\n\tprovideHttpClient,\n\twithInterceptorsFromDi,\n} from '@angular/common/http';\nimport { RuntimeConfig } from './runtime-config';\nimport { RuntimeConfigLoaderService } from './runtime-config-loader/runtime-config-loader.service';\n\nexport const RUNTIME_CONFIG = new InjectionToken<any>('RUNTIME_CONFIG');\n\nexport function initConfig(configSvc: RuntimeConfigLoaderService) {\n\treturn () => configSvc.loadConfig();\n}\n\nexport function provideRuntimeConfig<T = any>(\n\tconfig: RuntimeConfig<T>\n): EnvironmentProviders {\n\tconst providers: (Provider | EnvironmentProviders)[] = [\n\t\t{\n\t\t\tprovide: RuntimeConfig,\n\t\t\tuseValue: config,\n\t\t},\n\t\tRuntimeConfigLoaderService,\n\t\t{\n\t\t\tprovide: RUNTIME_CONFIG,\n\t\t\tuseFactory: (configSvc: RuntimeConfigLoaderService<T>) =>\n\t\t\t\tconfigSvc.getConfig(),\n\t\t\tdeps: [RuntimeConfigLoaderService],\n\t\t},\n\t\tprovideAppInitializer(() => {\n\t\t\tconst initializerFn = initConfig(\n\t\t\t\tinject(RuntimeConfigLoaderService)\n\t\t\t);\n\t\t\treturn initializerFn();\n\t\t}),\n\t\tprovideHttpClient(withInterceptorsFromDi()),\n\t];\n\n\treturn makeEnvironmentProviders(providers);\n}\n\nexport function provideConfigToken<T, K extends keyof T>(\n\ttoken: InjectionToken<T[K]>,\n\tkey: K\n): Provider;\nexport function provideConfigToken<R = any>(\n\ttoken: InjectionToken<R>,\n\tpath: string\n): Provider;\nexport function provideConfigToken(\n\ttoken: InjectionToken<any>,\n\tkey: any\n): Provider {\n\treturn {\n\t\tprovide: token,\n\t\tuseFactory: (configSvc: RuntimeConfigLoaderService) =>\n\t\t\tconfigSvc.getConfigObjectKey(key),\n\t\tdeps: [RuntimeConfigLoaderService],\n\t};\n}\n","import { Injectable } from '@angular/core';\nimport { Observable, of, Subject, ReplaySubject } from 'rxjs';\nimport { RuntimeConfigLoaderService } from '../runtime-config-loader/runtime-config-loader.service';\n\n@Injectable()\nexport class MockRuntimeConfigLoaderService<T = any>\n\timplements Partial<RuntimeConfigLoaderService<T>>\n{\n\tprivate configObject: T | null = null;\n\tpublic configSubject: Subject<T | null> = new ReplaySubject<T | null>(1);\n\n\tloadConfig(): Observable<T | null> {\n\t\tthis.configSubject.next(this.configObject);\n\t\treturn of(this.configObject);\n\t}\n\n\tgetConfig(): T | null {\n\t\treturn this.configObject;\n\t}\n\n\tgetConfigObjectKey<K extends keyof T>(key: K): T[K] | null;\n\tgetConfigObjectKey<R = any>(key: string): R | null;\n\tgetConfigObjectKey(key: any): any {\n\t\tif (!this.configObject) {\n\t\t\treturn null;\n\t\t}\n\n\t\tif (typeof key === 'string' && key.indexOf('.') > -1) {\n\t\t\treturn this.getValueByPath(this.configObject, key);\n\t\t}\n\n\t\treturn (this.configObject as any)[key] ?? null;\n\t}\n\n\tprivate getValueByPath(obj: any, path: string): any {\n\t\tconst value = path.split('.').reduce((prev, curr) => {\n\t\t\treturn prev ? prev[curr] : null;\n\t\t}, obj);\n\t\treturn value ?? null;\n\t}\n\n\tsetMockConfig(config: T): void {\n\t\tthis.configObject = config;\n\t\tthis.configSubject.next(this.configObject);\n\t}\n}\n","import {\n\tEnvironmentProviders,\n\tProvider,\n\tmakeEnvironmentProviders,\n} from '@angular/core';\nimport { RUNTIME_CONFIG } from '../runtime-config-loader.provider';\nimport { RuntimeConfigLoaderService } from '../runtime-config-loader/runtime-config-loader.service';\nimport { MockRuntimeConfigLoaderService } from './mock-runtime-config-loader.service';\n\nexport function provideMockRuntimeConfig<T = any>(\n\tmockConfig: T\n): EnvironmentProviders {\n\tconst mockService = new MockRuntimeConfigLoaderService<T>();\n\tmockService.setMockConfig(mockConfig);\n\n\tconst providers: Provider[] = [\n\t\t{\n\t\t\tprovide: RuntimeConfigLoaderService,\n\t\t\tuseValue: mockService,\n\t\t},\n\t\t{\n\t\t\tprovide: MockRuntimeConfigLoaderService,\n\t\t\tuseValue: mockService,\n\t\t},\n\t\t{\n\t\t\tprovide: RUNTIME_CONFIG,\n\t\t\tuseValue: mockConfig,\n\t\t},\n\t];\n\n\treturn makeEnvironmentProviders(providers);\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "runtime-config-loader",
3
- "version": "6.0.0",
3
+ "version": "6.1.0",
4
4
  "author": {
5
5
  "email": "preston.j.lamb@gmail.com",
6
6
  "name": "Preston Lamb",
Binary file
@@ -1,31 +1,83 @@
1
1
  import * as rxjs from 'rxjs';
2
- import { Subject, Observable } from 'rxjs';
2
+ import { Observable, Subject } from 'rxjs';
3
+ import { HttpHeaders, HttpContext, HttpParams } from '@angular/common/http';
3
4
  import * as i0 from '@angular/core';
4
- import { EnvironmentProviders } from '@angular/core';
5
+ import { InjectionToken, Provider, EnvironmentProviders } from '@angular/core';
5
6
 
6
- declare class RuntimeConfig {
7
+ declare class RuntimeConfig<T = any> {
7
8
  configUrl: string | string[];
9
+ defaultConfig?: T;
10
+ validator?: (config: T) => boolean | Promise<boolean> | Observable<boolean> | void;
11
+ options?: {
12
+ headers?: HttpHeaders | {
13
+ [header: string]: string | string[];
14
+ };
15
+ context?: HttpContext;
16
+ observe?: 'body';
17
+ params?: HttpParams | {
18
+ [param: string]: string | number | boolean | ReadonlyArray<string | number | boolean>;
19
+ };
20
+ reportProgress?: boolean;
21
+ responseType?: 'json';
22
+ withCredentials?: boolean;
23
+ };
8
24
  constructor(obj?: {
9
25
  configUrl?: string | string[];
26
+ defaultConfig?: T;
27
+ validator?: (config: T) => boolean | Promise<boolean> | Observable<boolean> | void;
28
+ options?: {
29
+ headers?: HttpHeaders | {
30
+ [header: string]: string | string[];
31
+ };
32
+ context?: HttpContext;
33
+ observe?: 'body';
34
+ params?: HttpParams | {
35
+ [param: string]: string | number | boolean | ReadonlyArray<string | number | boolean>;
36
+ };
37
+ reportProgress?: boolean;
38
+ responseType?: 'json';
39
+ withCredentials?: boolean;
40
+ };
10
41
  });
11
42
  }
12
43
 
13
- declare class RuntimeConfigLoaderService {
44
+ declare class RuntimeConfigLoaderService<T = any> {
14
45
  private configUrl;
15
46
  private configObject;
16
- configSubject: Subject<any>;
47
+ configSubject: Subject<T | null>;
17
48
  private _http;
18
49
  private _config;
19
50
  constructor();
20
- loadConfig(): Observable<any>;
51
+ loadConfig(): Observable<T | null>;
52
+ private handleValidationResult;
21
53
  private makeHttpCall;
22
- getConfig(): any;
23
- getConfigObjectKey(key: string): any;
24
- static ɵfac: i0.ɵɵFactoryDeclaration<RuntimeConfigLoaderService, never>;
25
- static ɵprov: i0.ɵɵInjectableDeclaration<RuntimeConfigLoaderService>;
54
+ getConfig(): T | null;
55
+ getConfigObjectKey<K extends keyof T>(key: K): T[K] | null;
56
+ getConfigObjectKey<R = any>(key: string): R | null;
57
+ private getValueByPath;
58
+ static ɵfac: i0.ɵɵFactoryDeclaration<RuntimeConfigLoaderService<any>, never>;
59
+ static ɵprov: i0.ɵɵInjectableDeclaration<RuntimeConfigLoaderService<any>>;
26
60
  }
27
61
 
62
+ declare const RUNTIME_CONFIG: InjectionToken<any>;
28
63
  declare function initConfig(configSvc: RuntimeConfigLoaderService): () => rxjs.Observable<any>;
29
- declare function provideRuntimeConfig(config: RuntimeConfig): EnvironmentProviders;
64
+ declare function provideRuntimeConfig<T = any>(config: RuntimeConfig<T>): EnvironmentProviders;
65
+ declare function provideConfigToken<T, K extends keyof T>(token: InjectionToken<T[K]>, key: K): Provider;
66
+ declare function provideConfigToken<R = any>(token: InjectionToken<R>, path: string): Provider;
30
67
 
31
- export { RuntimeConfig, RuntimeConfigLoaderService, initConfig, provideRuntimeConfig };
68
+ declare class MockRuntimeConfigLoaderService<T = any> implements Partial<RuntimeConfigLoaderService<T>> {
69
+ private configObject;
70
+ configSubject: Subject<T | null>;
71
+ loadConfig(): Observable<T | null>;
72
+ getConfig(): T | null;
73
+ getConfigObjectKey<K extends keyof T>(key: K): T[K] | null;
74
+ getConfigObjectKey<R = any>(key: string): R | null;
75
+ private getValueByPath;
76
+ setMockConfig(config: T): void;
77
+ static ɵfac: i0.ɵɵFactoryDeclaration<MockRuntimeConfigLoaderService<any>, never>;
78
+ static ɵprov: i0.ɵɵInjectableDeclaration<MockRuntimeConfigLoaderService<any>>;
79
+ }
80
+
81
+ declare function provideMockRuntimeConfig<T = any>(mockConfig: T): EnvironmentProviders;
82
+
83
+ export { MockRuntimeConfigLoaderService, RUNTIME_CONFIG, RuntimeConfig, RuntimeConfigLoaderService, initConfig, provideConfigToken, provideMockRuntimeConfig, provideRuntimeConfig };
Binary file