@lorion-org/runtime-config 1.0.0-beta.0 → 1.0.0-beta.5

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.
@@ -0,0 +1,126 @@
1
+ import Ajv, { type ErrorObject, type ValidateFunction } from 'ajv';
2
+ import type { RuntimeConfigContext } from './types';
3
+
4
+ export type RuntimeConfigValidationMode = 'none' | 'optional' | 'startup' | 'onUse';
5
+
6
+ export type RuntimeConfigValidationPolicy = {
7
+ validation?: RuntimeConfigValidationMode;
8
+ };
9
+
10
+ export type RuntimeConfigValidationSchemaRegistry = Record<string, object>;
11
+
12
+ export type RuntimeConfigValidationErrorTarget = {
13
+ scopeId: string;
14
+ };
15
+
16
+ export type RuntimeConfigValidationErrorFormatter = (
17
+ target: RuntimeConfigValidationErrorTarget,
18
+ validationError: ErrorObject,
19
+ ) => Error;
20
+
21
+ export type RuntimeConfigValidationPolicyInput =
22
+ | RuntimeConfigValidationMode
23
+ | RuntimeConfigValidationPolicy
24
+ | undefined;
25
+
26
+ export const defaultRuntimeConfigValidationMode: RuntimeConfigValidationMode = 'optional';
27
+
28
+ export function resolveRuntimeConfigValidationMode(
29
+ input: RuntimeConfigValidationPolicyInput,
30
+ ): RuntimeConfigValidationMode {
31
+ return typeof input === 'string'
32
+ ? input
33
+ : (input?.validation ?? defaultRuntimeConfigValidationMode);
34
+ }
35
+
36
+ export function shouldRegisterRuntimeConfigValidationSchema(
37
+ input: RuntimeConfigValidationPolicyInput,
38
+ ): boolean {
39
+ return resolveRuntimeConfigValidationMode(input) !== 'none';
40
+ }
41
+
42
+ export function shouldValidateRuntimeConfigAtStartup(
43
+ input: RuntimeConfigValidationPolicyInput,
44
+ ): boolean {
45
+ const mode = resolveRuntimeConfigValidationMode(input);
46
+
47
+ return mode !== 'none' && mode !== 'onUse';
48
+ }
49
+
50
+ export function shouldRequireRuntimeConfigAtStartup(
51
+ input: RuntimeConfigValidationPolicyInput,
52
+ ): boolean {
53
+ return resolveRuntimeConfigValidationMode(input) === 'startup';
54
+ }
55
+
56
+ function formatDefaultRuntimeConfigValidationError(
57
+ target: RuntimeConfigValidationErrorTarget,
58
+ validationError: ErrorObject,
59
+ ): Error {
60
+ const jsonPath = validationError.instancePath || '/';
61
+ const schemaError = `${validationError.keyword}${validationError.message ? `: ${validationError.message}` : ''}`;
62
+
63
+ return new Error(
64
+ [
65
+ 'RuntimeConfig schema validation failed.',
66
+ `Scope: ${target.scopeId}`,
67
+ `JSON path: ${jsonPath}`,
68
+ `Schema error: ${schemaError}`,
69
+ ].join('\n'),
70
+ );
71
+ }
72
+
73
+ export class RuntimeConfigValidatorRegistry {
74
+ readonly #cache = new Map<string, ValidateFunction>();
75
+ readonly #formatError: RuntimeConfigValidationErrorFormatter;
76
+ readonly #schemas: RuntimeConfigValidationSchemaRegistry;
77
+
78
+ constructor(
79
+ schemas: RuntimeConfigValidationSchemaRegistry,
80
+ options: {
81
+ formatError?: RuntimeConfigValidationErrorFormatter;
82
+ } = {},
83
+ ) {
84
+ this.#formatError = options.formatError ?? formatDefaultRuntimeConfigValidationError;
85
+ this.#schemas = schemas;
86
+ }
87
+
88
+ get(scopeId: string): ValidateFunction {
89
+ const schema = this.#schemas[scopeId];
90
+
91
+ if (!schema) {
92
+ throw new Error(`RuntimeConfig validation schema not registered for scope "${scopeId}".`);
93
+ }
94
+
95
+ const cached = this.#cache.get(scopeId);
96
+ if (cached) return cached;
97
+
98
+ const validate = new Ajv({ strict: false, allErrors: false }).compile(schema);
99
+ this.#cache.set(scopeId, validate);
100
+
101
+ return validate;
102
+ }
103
+
104
+ assert(scopeId: string, fragment: RuntimeConfigContext): void {
105
+ const validate = this.get(scopeId);
106
+
107
+ if (validate(fragment)) return;
108
+
109
+ const validationError = validate.errors?.[0];
110
+
111
+ if (!validationError) {
112
+ throw new Error(`RuntimeConfig schema validation failed for scope "${scopeId}".`);
113
+ }
114
+
115
+ throw this.#formatError({ scopeId }, validationError);
116
+ }
117
+ }
118
+
119
+ export function createRuntimeConfigValidatorRegistry(
120
+ schemas: RuntimeConfigValidationSchemaRegistry,
121
+ options: {
122
+ formatError?: RuntimeConfigValidationErrorFormatter;
123
+ } = {},
124
+ ): RuntimeConfigValidatorRegistry {
125
+ return new RuntimeConfigValidatorRegistry(schemas, options);
126
+ }
@@ -1,46 +0,0 @@
1
- import {
2
- projectSectionedRuntimeConfig,
3
- resolveRuntimeConfigValue,
4
- } from '@lorion-org/runtime-config';
5
-
6
- const runtimeConfig = projectSectionedRuntimeConfig(
7
- [
8
- {
9
- scopeId: 'billing',
10
- config: {
11
- public: {
12
- apiBase: '/api/billing',
13
- },
14
- tenants: {
15
- tenantA: {
16
- public: {
17
- apiBase: '/tenant-a/billing',
18
- },
19
- },
20
- },
21
- },
22
- },
23
- ],
24
- {
25
- contextInputKey: 'tenants',
26
- contextOutputKey: '__tenants',
27
- },
28
- );
29
-
30
- console.log(runtimeConfig.public);
31
- // {
32
- // billingApiBase: '/api/billing',
33
- // __tenants: {
34
- // tenantA: {
35
- // billingApiBase: '/tenant-a/billing'
36
- // }
37
- // }
38
- // }
39
-
40
- console.log(
41
- resolveRuntimeConfigValue(runtimeConfig.public, 'billing', 'apiBase', {
42
- contextId: 'tenantA',
43
- contextOutputKey: '__tenants',
44
- }),
45
- );
46
- // '/tenant-a/billing'
@@ -1,25 +0,0 @@
1
- import {
2
- runtimeEnvVarsToShellAssignments,
3
- runtimeEnvVarsToString,
4
- toRuntimeEnvVars,
5
- } from '@lorion-org/runtime-config';
6
-
7
- const envVars = toRuntimeEnvVars(
8
- {
9
- public: {
10
- billingApiBase: '/api/billing',
11
- },
12
- private: {
13
- billingApiSecret: 'secret',
14
- },
15
- },
16
- 'APP',
17
- );
18
-
19
- console.log(runtimeEnvVarsToString(envVars));
20
- // APP_PUBLIC_BILLING_API_BASE=/api/billing
21
- // APP_PRIVATE_BILLING_API_SECRET=secret
22
-
23
- console.log(runtimeEnvVarsToShellAssignments(envVars));
24
- // APP_PUBLIC_BILLING_API_BASE='"/api/billing"'
25
- // APP_PRIVATE_BILLING_API_SECRET='"secret"'
@@ -1,50 +0,0 @@
1
- import {
2
- projectRuntimeConfigNamespace,
3
- projectRuntimeConfigNamespaces,
4
- } from '@lorion-org/runtime-config';
5
-
6
- const billingRuntimeConfig = projectRuntimeConfigNamespace('billing', {
7
- public: {
8
- apiBase: '/api/billing',
9
- },
10
- private: {
11
- token: 'billing-token',
12
- },
13
- });
14
-
15
- console.log(billingRuntimeConfig.public.billing);
16
- // { apiBase: '/api/billing' }
17
-
18
- console.log(billingRuntimeConfig.billing);
19
- // { token: 'billing-token' }
20
-
21
- const combinedRuntimeConfig = projectRuntimeConfigNamespaces([
22
- {
23
- scopeId: 'billing',
24
- config: {
25
- public: {
26
- apiBase: '/api/billing',
27
- },
28
- private: {
29
- token: 'billing-token',
30
- },
31
- },
32
- },
33
- {
34
- scopeId: 'mail',
35
- config: {
36
- public: {
37
- apiBase: '/api/mail',
38
- },
39
- },
40
- },
41
- ]);
42
-
43
- console.log(combinedRuntimeConfig.public.billing);
44
- // { apiBase: '/api/billing' }
45
-
46
- console.log(combinedRuntimeConfig.public.mail);
47
- // { apiBase: '/api/mail' }
48
-
49
- console.log(combinedRuntimeConfig.billing);
50
- // { token: 'billing-token' }
@@ -1,32 +0,0 @@
1
- import {
2
- getPrivateRuntimeConfigScope,
3
- getPublicRuntimeConfigScope,
4
- projectRuntimeConfigFragment,
5
- toRuntimeEnvVars,
6
- } from '@lorion-org/runtime-config';
7
-
8
- const runtimeConfig = projectRuntimeConfigFragment('auth', {
9
- public: {
10
- url: 'https://auth.example.test',
11
- realm: 'main',
12
- },
13
- private: {
14
- clientSecret: 'secret',
15
- },
16
- });
17
-
18
- console.log(runtimeConfig.public.authUrl);
19
- // 'https://auth.example.test'
20
-
21
- console.log(toRuntimeEnvVars(runtimeConfig, 'APP'));
22
- // {
23
- // APP_PUBLIC_AUTH_URL: 'https://auth.example.test',
24
- // APP_PUBLIC_AUTH_REALM: 'main',
25
- // APP_PRIVATE_AUTH_CLIENT_SECRET: 'secret'
26
- // }
27
-
28
- console.log(getPublicRuntimeConfigScope(runtimeConfig, 'auth'));
29
- // { url: 'https://auth.example.test', realm: 'main' }
30
-
31
- console.log(getPrivateRuntimeConfigScope(runtimeConfig, 'auth'));
32
- // { clientSecret: 'secret' }
@@ -1,52 +0,0 @@
1
- import {
2
- getPublicRuntimeConfigScope,
3
- projectSectionedRuntimeConfig,
4
- resolveRuntimeConfigValue,
5
- type RuntimeConfigFragmentMap,
6
- } from '@lorion-org/runtime-config';
7
-
8
- const fragments: RuntimeConfigFragmentMap = new Map([
9
- [
10
- 'billing',
11
- {
12
- public: {
13
- apiBase: '/api/billing',
14
- },
15
- private: {
16
- apiSecret: 'secret',
17
- },
18
- contexts: {
19
- tenantA: {
20
- public: {
21
- apiBase: '/tenant-a/billing',
22
- },
23
- },
24
- },
25
- },
26
- ],
27
- [
28
- 'mail',
29
- {
30
- public: {
31
- apiBase: '/api/mail',
32
- },
33
- },
34
- ],
35
- ]);
36
-
37
- const runtimeConfig = projectSectionedRuntimeConfig(fragments);
38
- const tenantApiBase = resolveRuntimeConfigValue(runtimeConfig.public, 'billing', 'apiBase', {
39
- contextId: 'tenantA',
40
- });
41
-
42
- console.log(runtimeConfig.public.billingApiBase);
43
- // '/api/billing'
44
-
45
- console.log(runtimeConfig.private.billingApiSecret);
46
- // 'secret'
47
-
48
- console.log(tenantApiBase);
49
- // '/tenant-a/billing'
50
-
51
- console.log(getPublicRuntimeConfigScope(runtimeConfig, 'billing'));
52
- // { apiBase: '/api/billing' }