@jra-arch/infrastructure-config 1.0.0 → 2.0.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.
|
@@ -1,22 +1,125 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
1
|
+
import { InjectionToken, signal, PLATFORM_ID, Optional } from '@angular/core';
|
|
2
|
+
import { isPlatformBrowser } from '@angular/common';
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}
|
|
4
|
+
function traverse(obj, path) {
|
|
5
|
+
let current = obj;
|
|
6
|
+
for (const key of path.split('.')) {
|
|
7
|
+
if (current != null &&
|
|
8
|
+
typeof current === 'object' &&
|
|
9
|
+
Object.prototype.hasOwnProperty.call(current, key)) {
|
|
10
|
+
current = current[key];
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
return { value: undefined, found: false };
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return { value: current, found: true };
|
|
17
|
+
}
|
|
18
|
+
function getByPath(obj, path) {
|
|
19
|
+
const { value, found } = traverse(obj, path);
|
|
20
|
+
return found ? (value ?? null) : null;
|
|
21
|
+
}
|
|
22
|
+
function requireByPath(obj, path, hint) {
|
|
23
|
+
if (!path) {
|
|
24
|
+
throw new Error(`Missing config path (empty). ${hint ?? ''}`.trim());
|
|
25
|
+
}
|
|
26
|
+
const { value, found } = traverse(obj, path);
|
|
27
|
+
if (!found) {
|
|
28
|
+
const msg = hint ? `Missing config "${path}". ${hint}` : `Missing config "${path}".`;
|
|
29
|
+
throw new Error(msg);
|
|
30
|
+
}
|
|
31
|
+
return value;
|
|
32
|
+
}
|
|
12
33
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
34
|
+
const APP_CONFIG_TOKEN = new InjectionToken('APP_CONFIG_TOKEN');
|
|
35
|
+
const providePreloadedConfig = (cfg) => ({
|
|
36
|
+
provide: APP_CONFIG_TOKEN,
|
|
37
|
+
useValue: cfg,
|
|
38
|
+
});
|
|
39
|
+
class ConfigService {
|
|
40
|
+
STORAGE_KEY = 'appConfig';
|
|
41
|
+
isBrowser;
|
|
42
|
+
_config;
|
|
43
|
+
config;
|
|
44
|
+
constructor(isBrowser, preloaded) {
|
|
45
|
+
this.isBrowser = isBrowser;
|
|
46
|
+
const initial = preloaded ?? this.readStorage();
|
|
47
|
+
this._config = signal(initial, ...(ngDevMode ? [{ debugName: "_config" }] : []));
|
|
48
|
+
this.config = this._config.asReadonly();
|
|
49
|
+
this.persist(initial);
|
|
50
|
+
}
|
|
51
|
+
raw() {
|
|
52
|
+
return this._config();
|
|
53
|
+
}
|
|
54
|
+
get(path) {
|
|
55
|
+
const src = this._config();
|
|
56
|
+
return src ? getByPath(src, path) : null;
|
|
57
|
+
}
|
|
58
|
+
require(path, hint) {
|
|
59
|
+
return requireByPath(this._config(), path, hint);
|
|
60
|
+
}
|
|
61
|
+
persist(cfg) {
|
|
62
|
+
if (!this.isBrowser)
|
|
63
|
+
return;
|
|
64
|
+
cfg
|
|
65
|
+
? sessionStorage.setItem(this.STORAGE_KEY, JSON.stringify(cfg))
|
|
66
|
+
: sessionStorage.removeItem(this.STORAGE_KEY);
|
|
67
|
+
}
|
|
68
|
+
readStorage() {
|
|
69
|
+
if (!this.isBrowser)
|
|
70
|
+
return null;
|
|
71
|
+
const raw = sessionStorage.getItem(this.STORAGE_KEY);
|
|
72
|
+
if (!raw || raw === 'undefined')
|
|
73
|
+
return null;
|
|
74
|
+
try {
|
|
75
|
+
return JSON.parse(raw);
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
sessionStorage.removeItem(this.STORAGE_KEY);
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function provideConfig() {
|
|
85
|
+
return [
|
|
86
|
+
{
|
|
87
|
+
provide: ConfigService,
|
|
88
|
+
useFactory: (platformId, preloaded) => new ConfigService(isPlatformBrowser(platformId), preloaded ?? null),
|
|
89
|
+
deps: [PLATFORM_ID, [new Optional(), APP_CONFIG_TOKEN]],
|
|
90
|
+
},
|
|
91
|
+
];
|
|
92
|
+
}
|
|
93
|
+
function provideConfigTesting(config = {}) {
|
|
94
|
+
return [
|
|
95
|
+
{ provide: APP_CONFIG_TOKEN, useValue: config },
|
|
96
|
+
{
|
|
97
|
+
provide: ConfigService,
|
|
98
|
+
useFactory: (preloaded) => new ConfigService(false, preloaded ?? null),
|
|
99
|
+
deps: [APP_CONFIG_TOKEN],
|
|
100
|
+
},
|
|
101
|
+
];
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function isBrowser() {
|
|
105
|
+
return (typeof globalThis !== 'undefined' &&
|
|
106
|
+
typeof globalThis['window'] === 'object' &&
|
|
107
|
+
typeof globalThis['document'] === 'object');
|
|
108
|
+
}
|
|
109
|
+
function prefetchConfig(url, timeoutMs = 2000) {
|
|
110
|
+
if (!isBrowser())
|
|
111
|
+
return Promise.resolve(null);
|
|
112
|
+
const ac = new AbortController();
|
|
113
|
+
const timer = setTimeout(() => ac.abort(), timeoutMs);
|
|
114
|
+
return fetch(url, { cache: 'no-store', signal: ac.signal })
|
|
115
|
+
.then((res) => (res.ok ? res.json() : null))
|
|
116
|
+
.catch(() => null)
|
|
117
|
+
.finally(() => clearTimeout(timer));
|
|
118
|
+
}
|
|
16
119
|
|
|
17
120
|
/**
|
|
18
121
|
* Generated bundle index. Do not edit.
|
|
19
122
|
*/
|
|
20
123
|
|
|
21
|
-
export {
|
|
124
|
+
export { APP_CONFIG_TOKEN, ConfigService, getByPath, prefetchConfig, provideConfig, provideConfigTesting, providePreloadedConfig, requireByPath };
|
|
22
125
|
//# sourceMappingURL=jra-arch-infrastructure-config.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jra-arch-infrastructure-config.mjs","sources":["../../../projects/jra-arch-infrastructure-config/src/lib/jra-arch-infrastructure-config.ts","../../../projects/jra-arch-infrastructure-config/src/
|
|
1
|
+
{"version":3,"file":"jra-arch-infrastructure-config.mjs","sources":["../../../projects/jra-arch-infrastructure-config/src/lib/path-utils.ts","../../../projects/jra-arch-infrastructure-config/src/lib/config.service.ts","../../../projects/jra-arch-infrastructure-config/src/lib/config.provider.ts","../../../projects/jra-arch-infrastructure-config/src/lib/prefetch.ts","../../../projects/jra-arch-infrastructure-config/src/jra-arch-infrastructure-config.ts"],"sourcesContent":["function traverse(obj: unknown, path: string): { value: unknown; found: boolean } {\n let current: unknown = obj;\n\n for (const key of path.split('.')) {\n if (\n current != null &&\n typeof current === 'object' &&\n Object.prototype.hasOwnProperty.call(current, key)\n ) {\n current = (current as Record<string, unknown>)[key];\n } else {\n return { value: undefined, found: false };\n }\n }\n\n return { value: current, found: true };\n}\n\nexport function getByPath<T>(obj: unknown, path: string): T | null {\n const { value, found } = traverse(obj, path);\n return found ? ((value as T) ?? null) : null;\n}\n\nexport function requireByPath<T = unknown>(obj: unknown, path: string, hint?: string): T {\n if (!path) {\n throw new Error(`Missing config path (empty). ${hint ?? ''}`.trim());\n }\n\n const { value, found } = traverse(obj, path);\n\n if (!found) {\n const msg = hint ? `Missing config \"${path}\". ${hint}` : `Missing config \"${path}\".`;\n throw new Error(msg);\n }\n\n return value as T;\n}\n","import {\n InjectionToken,\n signal,\n} from '@angular/core';\n\nimport type { AppConfig } from './models/app-config';\nimport { getByPath, requireByPath } from './path-utils';\n\nexport const APP_CONFIG_TOKEN = new InjectionToken<AppConfig | null>('APP_CONFIG_TOKEN');\n\nexport const providePreloadedConfig = (cfg: AppConfig | null) => ({\n provide: APP_CONFIG_TOKEN,\n useValue: cfg,\n});\n\nexport class ConfigService {\n private readonly STORAGE_KEY = 'appConfig';\n private readonly isBrowser: boolean;\n private readonly _config;\n readonly config;\n\n constructor(isBrowser: boolean, preloaded: AppConfig | null) {\n this.isBrowser = isBrowser;\n const initial = preloaded ?? this.readStorage();\n this._config = signal<AppConfig | null>(initial);\n this.config = this._config.asReadonly();\n this.persist(initial);\n }\n\n raw(): AppConfig | null {\n return this._config();\n }\n\n get<T = unknown>(path: string): T | null {\n const src = this._config();\n return src ? getByPath<T>(src, path) : null;\n }\n\n require<T = unknown>(path: string, hint?: string): T {\n return requireByPath<T>(this._config(), path, hint);\n }\n\n private persist(cfg: AppConfig | null): void {\n if (!this.isBrowser) return;\n cfg\n ? sessionStorage.setItem(this.STORAGE_KEY, JSON.stringify(cfg))\n : sessionStorage.removeItem(this.STORAGE_KEY);\n }\n\n private readStorage(): AppConfig | null {\n if (!this.isBrowser) return null;\n const raw = sessionStorage.getItem(this.STORAGE_KEY);\n if (!raw || raw === 'undefined') return null;\n try {\n return JSON.parse(raw);\n } catch {\n sessionStorage.removeItem(this.STORAGE_KEY);\n return null;\n }\n }\n}\n","import { isPlatformBrowser } from '@angular/common';\nimport {\n Optional,\n PLATFORM_ID,\n Provider,\n} from '@angular/core';\n\nimport type { AppConfig } from './models/app-config';\nimport { APP_CONFIG_TOKEN, ConfigService } from './config.service';\n\nexport function provideConfig(): Provider[] {\n return [\n {\n provide: ConfigService,\n useFactory: (platformId: object, preloaded: AppConfig | null) =>\n new ConfigService(isPlatformBrowser(platformId), preloaded ?? null),\n deps: [PLATFORM_ID, [new Optional(), APP_CONFIG_TOKEN]],\n },\n ];\n}\n\nexport function provideConfigTesting(config: Record<string, unknown> = {}): Provider[] {\n return [\n { provide: APP_CONFIG_TOKEN, useValue: config },\n {\n provide: ConfigService,\n useFactory: (preloaded: AppConfig | null) =>\n new ConfigService(false, preloaded ?? null),\n deps: [APP_CONFIG_TOKEN],\n },\n ];\n}\n","import type { AppConfig } from './models/app-config';\n\nfunction isBrowser(): boolean {\n return (\n typeof globalThis !== 'undefined' &&\n typeof (globalThis as Record<string, unknown>)['window'] === 'object' &&\n typeof (globalThis as Record<string, unknown>)['document'] === 'object'\n );\n}\n\nexport function prefetchConfig(url: string, timeoutMs = 2000): Promise<AppConfig | null> {\n if (!isBrowser()) return Promise.resolve(null);\n\n const ac = new AbortController();\n const timer = setTimeout(() => ac.abort(), timeoutMs);\n\n return fetch(url, { cache: 'no-store', signal: ac.signal })\n .then((res) => (res.ok ? res.json() : null))\n .catch(() => null)\n .finally(() => clearTimeout(timer));\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,8 +1,39 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as _angular_core from '@angular/core';
|
|
2
|
+
import { InjectionToken, Provider } from '@angular/core';
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Generic application configuration contract.
|
|
6
|
+
* The runtime config JSON is loaded into this shape.
|
|
7
|
+
*/
|
|
8
|
+
interface AppConfig {
|
|
9
|
+
[key: string]: unknown;
|
|
6
10
|
}
|
|
7
11
|
|
|
8
|
-
|
|
12
|
+
declare const APP_CONFIG_TOKEN: InjectionToken<AppConfig | null>;
|
|
13
|
+
declare const providePreloadedConfig: (cfg: AppConfig | null) => {
|
|
14
|
+
provide: InjectionToken<AppConfig | null>;
|
|
15
|
+
useValue: AppConfig | null;
|
|
16
|
+
};
|
|
17
|
+
declare class ConfigService {
|
|
18
|
+
private readonly STORAGE_KEY;
|
|
19
|
+
private readonly isBrowser;
|
|
20
|
+
private readonly _config;
|
|
21
|
+
readonly config: _angular_core.Signal<AppConfig | null>;
|
|
22
|
+
constructor(isBrowser: boolean, preloaded: AppConfig | null);
|
|
23
|
+
raw(): AppConfig | null;
|
|
24
|
+
get<T = unknown>(path: string): T | null;
|
|
25
|
+
require<T = unknown>(path: string, hint?: string): T;
|
|
26
|
+
private persist;
|
|
27
|
+
private readStorage;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
declare function provideConfig(): Provider[];
|
|
31
|
+
declare function provideConfigTesting(config?: Record<string, unknown>): Provider[];
|
|
32
|
+
|
|
33
|
+
declare function prefetchConfig(url: string, timeoutMs?: number): Promise<AppConfig | null>;
|
|
34
|
+
|
|
35
|
+
declare function getByPath<T>(obj: unknown, path: string): T | null;
|
|
36
|
+
declare function requireByPath<T = unknown>(obj: unknown, path: string, hint?: string): T;
|
|
37
|
+
|
|
38
|
+
export { APP_CONFIG_TOKEN, ConfigService, getByPath, prefetchConfig, provideConfig, provideConfigTesting, providePreloadedConfig, requireByPath };
|
|
39
|
+
export type { AppConfig };
|