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

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/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2026
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the 'Software'), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2026
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the 'Software'), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -45,7 +45,7 @@ The intended adapter shape is:
45
45
  - application-specific names, file names, and defaults stay in the consuming adapter
46
46
 
47
47
  Adapters may accept existing fragment field names and map them to generic
48
- contexts with `contextInputKey`. For example, a project can read `tenants`
48
+ contexts with `contextInputKey`. For example, a project can read `stores`
49
49
  from its files while this package still works with the generic `contexts`
50
50
  model internally.
51
51
 
@@ -59,13 +59,13 @@ Fragments are written in local scope vocabulary. The scope id is not repeated in
59
59
  the keys.
60
60
 
61
61
  ```ts
62
- const authFragment = {
62
+ const checkoutFragment = {
63
63
  public: {
64
- url: 'https://auth.example.test',
65
- realm: 'main',
64
+ currency: 'EUR',
65
+ successPath: '/orders/confirmed',
66
66
  },
67
67
  private: {
68
- clientSecret: 'secret',
68
+ signingSecret: 'checkout_signing_secret_demo',
69
69
  },
70
70
  };
71
71
  ```
@@ -78,15 +78,15 @@ Some runtimes need one shared `public` and `private` object. The scope id become
78
78
  a transport prefix so many fragments can coexist without key collisions.
79
79
 
80
80
  ```ts
81
- const runtimeConfig = projectSectionedRuntimeConfig(new Map([['auth', authFragment]]));
81
+ const runtimeConfig = projectSectionedRuntimeConfig(new Map([['checkout', checkoutFragment]]));
82
82
 
83
83
  // For a single fragment, use:
84
- projectRuntimeConfigFragment('auth', authFragment);
84
+ projectRuntimeConfigFragment('checkout', checkoutFragment);
85
85
 
86
- runtimeConfig.public.authUrl;
87
- // => 'https://auth.example.test'
88
- runtimeConfig.private.authClientSecret;
89
- // => 'secret'
86
+ runtimeConfig.public.checkoutCurrency;
87
+ // => 'EUR'
88
+ runtimeConfig.private.checkoutSigningSecret;
89
+ // => 'checkout_signing_secret_demo'
90
90
  ```
91
91
 
92
92
  ### 3. Environment variable shape
@@ -96,19 +96,19 @@ Environment variables keep the same transport prefix and add visibility.
96
96
  ```ts
97
97
  toRuntimeEnvVars(runtimeConfig, 'APP');
98
98
  // => {
99
- // APP_PUBLIC_AUTH_URL: 'https://auth.example.test',
100
- // APP_PUBLIC_AUTH_REALM: 'main',
101
- // APP_PRIVATE_AUTH_CLIENT_SECRET: 'secret'
99
+ // APP_PUBLIC_CHECKOUT_CURRENCY: 'EUR',
100
+ // APP_PUBLIC_CHECKOUT_SUCCESS_PATH: '/orders/confirmed',
101
+ // APP_PRIVATE_CHECKOUT_SIGNING_SECRET: 'checkout_signing_secret_demo'
102
102
  // }
103
103
  ```
104
104
 
105
105
  Usage code can read the flat runtime shape back through local keys:
106
106
 
107
107
  ```ts
108
- const auth = getPublicRuntimeConfigScope(runtimeConfig, 'auth');
108
+ const checkout = getPublicRuntimeConfigScope(runtimeConfig, 'checkout');
109
109
 
110
- auth.url;
111
- // => 'https://auth.example.test'
110
+ checkout.successPath;
111
+ // => '/orders/confirmed'
112
112
  ```
113
113
 
114
114
  ## Basic example
@@ -122,18 +122,18 @@ import {
122
122
 
123
123
  const fragments = new Map([
124
124
  [
125
- 'billing',
125
+ 'checkout',
126
126
  {
127
127
  public: {
128
- apiBase: '/api/billing',
128
+ successPath: '/orders/confirmed',
129
129
  },
130
130
  private: {
131
- apiSecret: 'secret',
131
+ signingSecret: 'checkout_signing_secret_demo',
132
132
  },
133
133
  contexts: {
134
- tenantA: {
134
+ 'eu-store': {
135
135
  public: {
136
- apiBase: '/tenant-a/billing',
136
+ successPath: '/eu-store/orders/confirmed',
137
137
  },
138
138
  },
139
139
  },
@@ -143,16 +143,16 @@ const fragments = new Map([
143
143
 
144
144
  const runtimeConfig = projectSectionedRuntimeConfig(fragments);
145
145
 
146
- runtimeConfig.public.billingApiBase;
147
- // => '/api/billing'
146
+ runtimeConfig.public.checkoutSuccessPath;
147
+ // => '/orders/confirmed'
148
148
 
149
- resolveRuntimeConfigValue(runtimeConfig.public, 'billing', 'apiBase', {
150
- contextId: 'tenantA',
149
+ resolveRuntimeConfigValue(runtimeConfig.public, 'checkout', 'successPath', {
150
+ contextId: 'eu-store',
151
151
  });
152
- // => '/tenant-a/billing'
152
+ // => '/eu-store/orders/confirmed'
153
153
 
154
- getPublicRuntimeConfigScope(runtimeConfig, 'billing');
155
- // => { apiBase: '/api/billing' }
154
+ getPublicRuntimeConfigScope(runtimeConfig, 'checkout');
155
+ // => { successPath: '/orders/confirmed' }
156
156
  ```
157
157
 
158
158
  ## Example: custom context input key
@@ -163,15 +163,15 @@ import { projectSectionedRuntimeConfig } from '@lorion-org/runtime-config';
163
163
  projectSectionedRuntimeConfig(
164
164
  [
165
165
  {
166
- scopeId: 'billing',
166
+ scopeId: 'checkout',
167
167
  config: {
168
168
  public: {
169
- apiBase: '/api/billing',
169
+ successPath: '/orders/confirmed',
170
170
  },
171
- tenants: {
172
- tenantA: {
171
+ stores: {
172
+ 'eu-store': {
173
173
  public: {
174
- apiBase: '/tenant-a/billing',
174
+ successPath: '/eu-store/orders/confirmed',
175
175
  },
176
176
  },
177
177
  },
@@ -179,16 +179,16 @@ projectSectionedRuntimeConfig(
179
179
  },
180
180
  ],
181
181
  {
182
- contextInputKey: 'tenants',
183
- contextOutputKey: '__tenants',
182
+ contextInputKey: 'stores',
183
+ contextOutputKey: '__stores',
184
184
  },
185
185
  );
186
186
  // => {
187
187
  // public: {
188
- // billingApiBase: '/api/billing',
189
- // __tenants: {
190
- // tenantA: {
191
- // billingApiBase: '/tenant-a/billing'
188
+ // checkoutSuccessPath: '/orders/confirmed',
189
+ // __stores: {
190
+ // 'eu-store': {
191
+ // checkoutSuccessPath: '/eu-store/orders/confirmed'
192
192
  // }
193
193
  // }
194
194
  // },
@@ -209,38 +209,33 @@ import {
209
209
  projectRuntimeConfigNamespaces,
210
210
  } from '@lorion-org/runtime-config';
211
211
 
212
- const runtimeConfig = projectRuntimeConfigNamespace('mail', {
212
+ const runtimeConfig = projectRuntimeConfigNamespace('checkout', {
213
213
  public: {
214
- apiBase: '/api/mail',
214
+ successPath: '/orders/confirmed',
215
215
  },
216
216
  private: {
217
- token: 'mail-token',
217
+ signingSecret: 'checkout_signing_secret_demo',
218
218
  },
219
219
  });
220
220
 
221
- runtimeConfig.public.mail;
222
- // => { apiBase: '/api/mail' }
223
- runtimeConfig.mail;
224
- // => { token: 'mail-token' }
221
+ runtimeConfig.public.checkout;
222
+ // => { successPath: '/orders/confirmed' }
223
+ runtimeConfig.checkout;
224
+ // => { signingSecret: 'checkout_signing_secret_demo' }
225
225
 
226
226
  const combinedRuntimeConfig = projectRuntimeConfigNamespaces([
227
227
  {
228
- scopeId: 'mail',
228
+ scopeId: 'payments',
229
229
  config: {
230
230
  public: {
231
- apiBase: '/api/mail',
232
- },
233
- private: {
234
- token: 'mail-token',
231
+ configuredProvider: 'payment-provider-stripe',
235
232
  },
236
233
  },
237
234
  },
238
235
  ]);
239
236
 
240
- combinedRuntimeConfig.public.mail;
241
- // => { apiBase: '/api/mail' }
242
- combinedRuntimeConfig.mail;
243
- // => { token: 'mail-token' }
237
+ combinedRuntimeConfig.public.payments;
238
+ // => { configuredProvider: 'payment-provider-stripe' }
244
239
  ```
245
240
 
246
241
  ## Example: environment variables
@@ -258,30 +253,30 @@ import {
258
253
  const envVars = toRuntimeEnvVars(
259
254
  {
260
255
  public: {
261
- billingApiBase: '/api/billing',
256
+ checkoutSuccessPath: '/orders/confirmed',
262
257
  },
263
258
  private: {
264
- billingApiSecret: 'secret',
259
+ checkoutSigningSecret: 'checkout_signing_secret_demo',
265
260
  },
266
261
  },
267
262
  'APP',
268
263
  );
269
264
 
270
265
  runtimeEnvVarsToString(envVars);
271
- // => APP_PUBLIC_BILLING_API_BASE=/api/billing
272
- // => APP_PRIVATE_BILLING_API_SECRET=secret
266
+ // => APP_PUBLIC_CHECKOUT_SUCCESS_PATH=/orders/confirmed
267
+ // => APP_PRIVATE_CHECKOUT_SIGNING_SECRET=checkout_signing_secret_demo
273
268
 
274
269
  runtimeEnvVarsToShellAssignments(envVars);
275
- // => APP_PUBLIC_BILLING_API_BASE='"/api/billing"'
276
- // => APP_PRIVATE_BILLING_API_SECRET='"secret"'
270
+ // => APP_PUBLIC_CHECKOUT_SUCCESS_PATH='"/orders/confirmed"'
271
+ // => APP_PRIVATE_CHECKOUT_SIGNING_SECRET='"checkout_signing_secret_demo"'
277
272
 
278
273
  projectRuntimeConfigEnvVars(
279
274
  new Map([
280
275
  [
281
- 'billing',
276
+ 'payments',
282
277
  {
283
278
  public: {
284
- apiBase: '/api/billing',
279
+ configuredProvider: 'payment-provider-stripe',
285
280
  },
286
281
  },
287
282
  ],
@@ -291,7 +286,7 @@ projectRuntimeConfigEnvVars(
291
286
  },
292
287
  );
293
288
  // => {
294
- // APP_PUBLIC_BILLING_API_BASE: '/api/billing'
289
+ // APP_PUBLIC_PAYMENTS_CONFIGURED_PROVIDER: 'payment-provider-stripe'
295
290
  // }
296
291
  ```
297
292
 
package/dist/index.cjs CHANGED
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,13 +17,24 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
 
20
30
  // src/index.ts
21
31
  var index_exports = {};
22
32
  __export(index_exports, {
33
+ RuntimeConfigValidatorRegistry: () => RuntimeConfigValidatorRegistry,
23
34
  createRuntimeConfigEnvKey: () => createRuntimeConfigEnvKey,
24
35
  createRuntimeConfigKey: () => createRuntimeConfigKey,
36
+ createRuntimeConfigValidatorRegistry: () => createRuntimeConfigValidatorRegistry,
37
+ defaultRuntimeConfigValidationMode: () => defaultRuntimeConfigValidationMode,
25
38
  getPrivateRuntimeConfigScope: () => getPrivateRuntimeConfigScope,
26
39
  getPublicRuntimeConfigScope: () => getPublicRuntimeConfigScope,
27
40
  getRuntimeConfigFragment: () => getRuntimeConfigFragment,
@@ -33,12 +46,16 @@ __export(index_exports, {
33
46
  projectRuntimeConfigNamespace: () => projectRuntimeConfigNamespace,
34
47
  projectRuntimeConfigNamespaces: () => projectRuntimeConfigNamespaces,
35
48
  projectSectionedRuntimeConfig: () => projectSectionedRuntimeConfig,
49
+ resolveRuntimeConfigValidationMode: () => resolveRuntimeConfigValidationMode,
36
50
  resolveRuntimeConfigValue: () => resolveRuntimeConfigValue,
37
51
  resolveRuntimeConfigValueFromRuntimeConfig: () => resolveRuntimeConfigValueFromRuntimeConfig,
38
52
  runtimeConfigVisibilities: () => runtimeConfigVisibilities,
39
53
  runtimeEnvValueToShellLiteral: () => runtimeEnvValueToShellLiteral,
40
54
  runtimeEnvVarsToShellAssignments: () => runtimeEnvVarsToShellAssignments,
41
55
  runtimeEnvVarsToString: () => runtimeEnvVarsToString,
56
+ shouldRegisterRuntimeConfigValidationSchema: () => shouldRegisterRuntimeConfigValidationSchema,
57
+ shouldRequireRuntimeConfigAtStartup: () => shouldRequireRuntimeConfigAtStartup,
58
+ shouldValidateRuntimeConfigAtStartup: () => shouldValidateRuntimeConfigAtStartup,
42
59
  stripRuntimeConfigScopePrefix: () => stripRuntimeConfigScopePrefix,
43
60
  toRuntimeConfigFragment: () => toRuntimeConfigFragment,
44
61
  toRuntimeEnvVars: () => toRuntimeEnvVars,
@@ -340,10 +357,74 @@ function getRuntimeConfigFragment(runtimeConfig, scopeId, options = {}) {
340
357
  private: getPrivateRuntimeConfigScope(runtimeConfig, scopeId, privateOptions)
341
358
  };
342
359
  }
360
+
361
+ // src/validation.ts
362
+ var import_ajv = __toESM(require("ajv"), 1);
363
+ var defaultRuntimeConfigValidationMode = "optional";
364
+ function resolveRuntimeConfigValidationMode(input) {
365
+ return typeof input === "string" ? input : input?.validation ?? defaultRuntimeConfigValidationMode;
366
+ }
367
+ function shouldRegisterRuntimeConfigValidationSchema(input) {
368
+ return resolveRuntimeConfigValidationMode(input) !== "none";
369
+ }
370
+ function shouldValidateRuntimeConfigAtStartup(input) {
371
+ const mode = resolveRuntimeConfigValidationMode(input);
372
+ return mode !== "none" && mode !== "onUse";
373
+ }
374
+ function shouldRequireRuntimeConfigAtStartup(input) {
375
+ return resolveRuntimeConfigValidationMode(input) === "startup";
376
+ }
377
+ function formatDefaultRuntimeConfigValidationError(target, validationError) {
378
+ const jsonPath = validationError.instancePath || "/";
379
+ const schemaError = `${validationError.keyword}${validationError.message ? `: ${validationError.message}` : ""}`;
380
+ return new Error(
381
+ [
382
+ "RuntimeConfig schema validation failed.",
383
+ `Scope: ${target.scopeId}`,
384
+ `JSON path: ${jsonPath}`,
385
+ `Schema error: ${schemaError}`
386
+ ].join("\n")
387
+ );
388
+ }
389
+ var RuntimeConfigValidatorRegistry = class {
390
+ #cache = /* @__PURE__ */ new Map();
391
+ #formatError;
392
+ #schemas;
393
+ constructor(schemas, options = {}) {
394
+ this.#formatError = options.formatError ?? formatDefaultRuntimeConfigValidationError;
395
+ this.#schemas = schemas;
396
+ }
397
+ get(scopeId) {
398
+ const schema = this.#schemas[scopeId];
399
+ if (!schema) {
400
+ throw new Error(`RuntimeConfig validation schema not registered for scope "${scopeId}".`);
401
+ }
402
+ const cached = this.#cache.get(scopeId);
403
+ if (cached) return cached;
404
+ const validate = new import_ajv.default({ strict: false, allErrors: false }).compile(schema);
405
+ this.#cache.set(scopeId, validate);
406
+ return validate;
407
+ }
408
+ assert(scopeId, fragment) {
409
+ const validate = this.get(scopeId);
410
+ if (validate(fragment)) return;
411
+ const validationError = validate.errors?.[0];
412
+ if (!validationError) {
413
+ throw new Error(`RuntimeConfig schema validation failed for scope "${scopeId}".`);
414
+ }
415
+ throw this.#formatError({ scopeId }, validationError);
416
+ }
417
+ };
418
+ function createRuntimeConfigValidatorRegistry(schemas, options = {}) {
419
+ return new RuntimeConfigValidatorRegistry(schemas, options);
420
+ }
343
421
  // Annotate the CommonJS export names for ESM import in node:
344
422
  0 && (module.exports = {
423
+ RuntimeConfigValidatorRegistry,
345
424
  createRuntimeConfigEnvKey,
346
425
  createRuntimeConfigKey,
426
+ createRuntimeConfigValidatorRegistry,
427
+ defaultRuntimeConfigValidationMode,
347
428
  getPrivateRuntimeConfigScope,
348
429
  getPublicRuntimeConfigScope,
349
430
  getRuntimeConfigFragment,
@@ -355,12 +436,16 @@ function getRuntimeConfigFragment(runtimeConfig, scopeId, options = {}) {
355
436
  projectRuntimeConfigNamespace,
356
437
  projectRuntimeConfigNamespaces,
357
438
  projectSectionedRuntimeConfig,
439
+ resolveRuntimeConfigValidationMode,
358
440
  resolveRuntimeConfigValue,
359
441
  resolveRuntimeConfigValueFromRuntimeConfig,
360
442
  runtimeConfigVisibilities,
361
443
  runtimeEnvValueToShellLiteral,
362
444
  runtimeEnvVarsToShellAssignments,
363
445
  runtimeEnvVarsToString,
446
+ shouldRegisterRuntimeConfigValidationSchema,
447
+ shouldRequireRuntimeConfigAtStartup,
448
+ shouldValidateRuntimeConfigAtStartup,
364
449
  stripRuntimeConfigScopePrefix,
365
450
  toRuntimeConfigFragment,
366
451
  toRuntimeEnvVars,
package/dist/index.d.cts CHANGED
@@ -1,3 +1,5 @@
1
+ import { ErrorObject, ValidateFunction } from 'ajv';
2
+
1
3
  type RuntimeConfigKeyStrategy = (scopeId: string, key: string) => string;
2
4
  type RuntimeConfigKeyOptions = {
3
5
  keyStrategy?: RuntimeConfigKeyStrategy;
@@ -98,4 +100,31 @@ declare function getPublicRuntimeConfigScope<T extends RuntimeConfigSection = Ru
98
100
  declare function getPrivateRuntimeConfigScope<T extends RuntimeConfigSection = RuntimeConfigSection>(runtimeConfig: Partial<Record<ConfigVisibility, RuntimeConfigSection | undefined>>, scopeId: string, options?: Omit<GetRuntimeConfigScopeOptions, 'visibility'>): T;
99
101
  declare function getRuntimeConfigFragment(runtimeConfig: Partial<Record<ConfigVisibility, RuntimeConfigSection | undefined>>, scopeId: string, options?: GetRuntimeConfigFragmentOptions): RuntimeConfigContext;
100
102
 
101
- export { type ConfigVisibility, type GetRuntimeConfigFragmentOptions, type GetRuntimeConfigScopeOptions, type NamedRuntimeConfigFragment, type NormalizeRuntimeConfigFragmentOptions, type ProjectRuntimeConfigEnvVarsOptions, type ProjectRuntimeConfigFragmentOptions, type ProjectRuntimeConfigNamespaceOptions, type ProjectRuntimeConfigNamespacesOptions, type ProjectSectionedRuntimeConfigOptions, type ResolveRuntimeConfigValueFromRuntimeConfigOptions, type ResolveRuntimeConfigValueOptions, type RuntimeConfigContext, type RuntimeConfigFragment, type RuntimeConfigFragmentInput, type RuntimeConfigFragmentMap, type RuntimeConfigKeyOptions, type RuntimeConfigKeyStrategy, type RuntimeConfigNamespaceProjection, type RuntimeConfigSection, type RuntimeEnvVars, type SectionedRuntimeConfig, createRuntimeConfigEnvKey, createRuntimeConfigKey, getPrivateRuntimeConfigScope, getPublicRuntimeConfigScope, getRuntimeConfigFragment, getRuntimeConfigScope, injectRuntimeEnvVars, normalizeRuntimeConfigFragments, projectRuntimeConfigEnvVars, projectRuntimeConfigFragment, projectRuntimeConfigNamespace, projectRuntimeConfigNamespaces, projectSectionedRuntimeConfig, resolveRuntimeConfigValue, resolveRuntimeConfigValueFromRuntimeConfig, runtimeConfigVisibilities, runtimeEnvValueToShellLiteral, runtimeEnvVarsToShellAssignments, runtimeEnvVarsToString, stripRuntimeConfigScopePrefix, toRuntimeConfigFragment, toRuntimeEnvVars, toSnakeUpperCase };
103
+ type RuntimeConfigValidationMode = 'none' | 'optional' | 'startup' | 'onUse';
104
+ type RuntimeConfigValidationPolicy = {
105
+ validation?: RuntimeConfigValidationMode;
106
+ };
107
+ type RuntimeConfigValidationSchemaRegistry = Record<string, object>;
108
+ type RuntimeConfigValidationErrorTarget = {
109
+ scopeId: string;
110
+ };
111
+ type RuntimeConfigValidationErrorFormatter = (target: RuntimeConfigValidationErrorTarget, validationError: ErrorObject) => Error;
112
+ type RuntimeConfigValidationPolicyInput = RuntimeConfigValidationMode | RuntimeConfigValidationPolicy | undefined;
113
+ declare const defaultRuntimeConfigValidationMode: RuntimeConfigValidationMode;
114
+ declare function resolveRuntimeConfigValidationMode(input: RuntimeConfigValidationPolicyInput): RuntimeConfigValidationMode;
115
+ declare function shouldRegisterRuntimeConfigValidationSchema(input: RuntimeConfigValidationPolicyInput): boolean;
116
+ declare function shouldValidateRuntimeConfigAtStartup(input: RuntimeConfigValidationPolicyInput): boolean;
117
+ declare function shouldRequireRuntimeConfigAtStartup(input: RuntimeConfigValidationPolicyInput): boolean;
118
+ declare class RuntimeConfigValidatorRegistry {
119
+ #private;
120
+ constructor(schemas: RuntimeConfigValidationSchemaRegistry, options?: {
121
+ formatError?: RuntimeConfigValidationErrorFormatter;
122
+ });
123
+ get(scopeId: string): ValidateFunction;
124
+ assert(scopeId: string, fragment: RuntimeConfigContext): void;
125
+ }
126
+ declare function createRuntimeConfigValidatorRegistry(schemas: RuntimeConfigValidationSchemaRegistry, options?: {
127
+ formatError?: RuntimeConfigValidationErrorFormatter;
128
+ }): RuntimeConfigValidatorRegistry;
129
+
130
+ export { type ConfigVisibility, type GetRuntimeConfigFragmentOptions, type GetRuntimeConfigScopeOptions, type NamedRuntimeConfigFragment, type NormalizeRuntimeConfigFragmentOptions, type ProjectRuntimeConfigEnvVarsOptions, type ProjectRuntimeConfigFragmentOptions, type ProjectRuntimeConfigNamespaceOptions, type ProjectRuntimeConfigNamespacesOptions, type ProjectSectionedRuntimeConfigOptions, type ResolveRuntimeConfigValueFromRuntimeConfigOptions, type ResolveRuntimeConfigValueOptions, type RuntimeConfigContext, type RuntimeConfigFragment, type RuntimeConfigFragmentInput, type RuntimeConfigFragmentMap, type RuntimeConfigKeyOptions, type RuntimeConfigKeyStrategy, type RuntimeConfigNamespaceProjection, type RuntimeConfigSection, type RuntimeConfigValidationErrorFormatter, type RuntimeConfigValidationErrorTarget, type RuntimeConfigValidationMode, type RuntimeConfigValidationPolicy, type RuntimeConfigValidationPolicyInput, type RuntimeConfigValidationSchemaRegistry, RuntimeConfigValidatorRegistry, type RuntimeEnvVars, type SectionedRuntimeConfig, createRuntimeConfigEnvKey, createRuntimeConfigKey, createRuntimeConfigValidatorRegistry, defaultRuntimeConfigValidationMode, getPrivateRuntimeConfigScope, getPublicRuntimeConfigScope, getRuntimeConfigFragment, getRuntimeConfigScope, injectRuntimeEnvVars, normalizeRuntimeConfigFragments, projectRuntimeConfigEnvVars, projectRuntimeConfigFragment, projectRuntimeConfigNamespace, projectRuntimeConfigNamespaces, projectSectionedRuntimeConfig, resolveRuntimeConfigValidationMode, resolveRuntimeConfigValue, resolveRuntimeConfigValueFromRuntimeConfig, runtimeConfigVisibilities, runtimeEnvValueToShellLiteral, runtimeEnvVarsToShellAssignments, runtimeEnvVarsToString, shouldRegisterRuntimeConfigValidationSchema, shouldRequireRuntimeConfigAtStartup, shouldValidateRuntimeConfigAtStartup, stripRuntimeConfigScopePrefix, toRuntimeConfigFragment, toRuntimeEnvVars, toSnakeUpperCase };
package/dist/index.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { ErrorObject, ValidateFunction } from 'ajv';
2
+
1
3
  type RuntimeConfigKeyStrategy = (scopeId: string, key: string) => string;
2
4
  type RuntimeConfigKeyOptions = {
3
5
  keyStrategy?: RuntimeConfigKeyStrategy;
@@ -98,4 +100,31 @@ declare function getPublicRuntimeConfigScope<T extends RuntimeConfigSection = Ru
98
100
  declare function getPrivateRuntimeConfigScope<T extends RuntimeConfigSection = RuntimeConfigSection>(runtimeConfig: Partial<Record<ConfigVisibility, RuntimeConfigSection | undefined>>, scopeId: string, options?: Omit<GetRuntimeConfigScopeOptions, 'visibility'>): T;
99
101
  declare function getRuntimeConfigFragment(runtimeConfig: Partial<Record<ConfigVisibility, RuntimeConfigSection | undefined>>, scopeId: string, options?: GetRuntimeConfigFragmentOptions): RuntimeConfigContext;
100
102
 
101
- export { type ConfigVisibility, type GetRuntimeConfigFragmentOptions, type GetRuntimeConfigScopeOptions, type NamedRuntimeConfigFragment, type NormalizeRuntimeConfigFragmentOptions, type ProjectRuntimeConfigEnvVarsOptions, type ProjectRuntimeConfigFragmentOptions, type ProjectRuntimeConfigNamespaceOptions, type ProjectRuntimeConfigNamespacesOptions, type ProjectSectionedRuntimeConfigOptions, type ResolveRuntimeConfigValueFromRuntimeConfigOptions, type ResolveRuntimeConfigValueOptions, type RuntimeConfigContext, type RuntimeConfigFragment, type RuntimeConfigFragmentInput, type RuntimeConfigFragmentMap, type RuntimeConfigKeyOptions, type RuntimeConfigKeyStrategy, type RuntimeConfigNamespaceProjection, type RuntimeConfigSection, type RuntimeEnvVars, type SectionedRuntimeConfig, createRuntimeConfigEnvKey, createRuntimeConfigKey, getPrivateRuntimeConfigScope, getPublicRuntimeConfigScope, getRuntimeConfigFragment, getRuntimeConfigScope, injectRuntimeEnvVars, normalizeRuntimeConfigFragments, projectRuntimeConfigEnvVars, projectRuntimeConfigFragment, projectRuntimeConfigNamespace, projectRuntimeConfigNamespaces, projectSectionedRuntimeConfig, resolveRuntimeConfigValue, resolveRuntimeConfigValueFromRuntimeConfig, runtimeConfigVisibilities, runtimeEnvValueToShellLiteral, runtimeEnvVarsToShellAssignments, runtimeEnvVarsToString, stripRuntimeConfigScopePrefix, toRuntimeConfigFragment, toRuntimeEnvVars, toSnakeUpperCase };
103
+ type RuntimeConfigValidationMode = 'none' | 'optional' | 'startup' | 'onUse';
104
+ type RuntimeConfigValidationPolicy = {
105
+ validation?: RuntimeConfigValidationMode;
106
+ };
107
+ type RuntimeConfigValidationSchemaRegistry = Record<string, object>;
108
+ type RuntimeConfigValidationErrorTarget = {
109
+ scopeId: string;
110
+ };
111
+ type RuntimeConfigValidationErrorFormatter = (target: RuntimeConfigValidationErrorTarget, validationError: ErrorObject) => Error;
112
+ type RuntimeConfigValidationPolicyInput = RuntimeConfigValidationMode | RuntimeConfigValidationPolicy | undefined;
113
+ declare const defaultRuntimeConfigValidationMode: RuntimeConfigValidationMode;
114
+ declare function resolveRuntimeConfigValidationMode(input: RuntimeConfigValidationPolicyInput): RuntimeConfigValidationMode;
115
+ declare function shouldRegisterRuntimeConfigValidationSchema(input: RuntimeConfigValidationPolicyInput): boolean;
116
+ declare function shouldValidateRuntimeConfigAtStartup(input: RuntimeConfigValidationPolicyInput): boolean;
117
+ declare function shouldRequireRuntimeConfigAtStartup(input: RuntimeConfigValidationPolicyInput): boolean;
118
+ declare class RuntimeConfigValidatorRegistry {
119
+ #private;
120
+ constructor(schemas: RuntimeConfigValidationSchemaRegistry, options?: {
121
+ formatError?: RuntimeConfigValidationErrorFormatter;
122
+ });
123
+ get(scopeId: string): ValidateFunction;
124
+ assert(scopeId: string, fragment: RuntimeConfigContext): void;
125
+ }
126
+ declare function createRuntimeConfigValidatorRegistry(schemas: RuntimeConfigValidationSchemaRegistry, options?: {
127
+ formatError?: RuntimeConfigValidationErrorFormatter;
128
+ }): RuntimeConfigValidatorRegistry;
129
+
130
+ export { type ConfigVisibility, type GetRuntimeConfigFragmentOptions, type GetRuntimeConfigScopeOptions, type NamedRuntimeConfigFragment, type NormalizeRuntimeConfigFragmentOptions, type ProjectRuntimeConfigEnvVarsOptions, type ProjectRuntimeConfigFragmentOptions, type ProjectRuntimeConfigNamespaceOptions, type ProjectRuntimeConfigNamespacesOptions, type ProjectSectionedRuntimeConfigOptions, type ResolveRuntimeConfigValueFromRuntimeConfigOptions, type ResolveRuntimeConfigValueOptions, type RuntimeConfigContext, type RuntimeConfigFragment, type RuntimeConfigFragmentInput, type RuntimeConfigFragmentMap, type RuntimeConfigKeyOptions, type RuntimeConfigKeyStrategy, type RuntimeConfigNamespaceProjection, type RuntimeConfigSection, type RuntimeConfigValidationErrorFormatter, type RuntimeConfigValidationErrorTarget, type RuntimeConfigValidationMode, type RuntimeConfigValidationPolicy, type RuntimeConfigValidationPolicyInput, type RuntimeConfigValidationSchemaRegistry, RuntimeConfigValidatorRegistry, type RuntimeEnvVars, type SectionedRuntimeConfig, createRuntimeConfigEnvKey, createRuntimeConfigKey, createRuntimeConfigValidatorRegistry, defaultRuntimeConfigValidationMode, getPrivateRuntimeConfigScope, getPublicRuntimeConfigScope, getRuntimeConfigFragment, getRuntimeConfigScope, injectRuntimeEnvVars, normalizeRuntimeConfigFragments, projectRuntimeConfigEnvVars, projectRuntimeConfigFragment, projectRuntimeConfigNamespace, projectRuntimeConfigNamespaces, projectSectionedRuntimeConfig, resolveRuntimeConfigValidationMode, resolveRuntimeConfigValue, resolveRuntimeConfigValueFromRuntimeConfig, runtimeConfigVisibilities, runtimeEnvValueToShellLiteral, runtimeEnvVarsToShellAssignments, runtimeEnvVarsToString, shouldRegisterRuntimeConfigValidationSchema, shouldRequireRuntimeConfigAtStartup, shouldValidateRuntimeConfigAtStartup, stripRuntimeConfigScopePrefix, toRuntimeConfigFragment, toRuntimeEnvVars, toSnakeUpperCase };
package/dist/index.js CHANGED
@@ -292,9 +292,73 @@ function getRuntimeConfigFragment(runtimeConfig, scopeId, options = {}) {
292
292
  private: getPrivateRuntimeConfigScope(runtimeConfig, scopeId, privateOptions)
293
293
  };
294
294
  }
295
+
296
+ // src/validation.ts
297
+ import Ajv from "ajv";
298
+ var defaultRuntimeConfigValidationMode = "optional";
299
+ function resolveRuntimeConfigValidationMode(input) {
300
+ return typeof input === "string" ? input : input?.validation ?? defaultRuntimeConfigValidationMode;
301
+ }
302
+ function shouldRegisterRuntimeConfigValidationSchema(input) {
303
+ return resolveRuntimeConfigValidationMode(input) !== "none";
304
+ }
305
+ function shouldValidateRuntimeConfigAtStartup(input) {
306
+ const mode = resolveRuntimeConfigValidationMode(input);
307
+ return mode !== "none" && mode !== "onUse";
308
+ }
309
+ function shouldRequireRuntimeConfigAtStartup(input) {
310
+ return resolveRuntimeConfigValidationMode(input) === "startup";
311
+ }
312
+ function formatDefaultRuntimeConfigValidationError(target, validationError) {
313
+ const jsonPath = validationError.instancePath || "/";
314
+ const schemaError = `${validationError.keyword}${validationError.message ? `: ${validationError.message}` : ""}`;
315
+ return new Error(
316
+ [
317
+ "RuntimeConfig schema validation failed.",
318
+ `Scope: ${target.scopeId}`,
319
+ `JSON path: ${jsonPath}`,
320
+ `Schema error: ${schemaError}`
321
+ ].join("\n")
322
+ );
323
+ }
324
+ var RuntimeConfigValidatorRegistry = class {
325
+ #cache = /* @__PURE__ */ new Map();
326
+ #formatError;
327
+ #schemas;
328
+ constructor(schemas, options = {}) {
329
+ this.#formatError = options.formatError ?? formatDefaultRuntimeConfigValidationError;
330
+ this.#schemas = schemas;
331
+ }
332
+ get(scopeId) {
333
+ const schema = this.#schemas[scopeId];
334
+ if (!schema) {
335
+ throw new Error(`RuntimeConfig validation schema not registered for scope "${scopeId}".`);
336
+ }
337
+ const cached = this.#cache.get(scopeId);
338
+ if (cached) return cached;
339
+ const validate = new Ajv({ strict: false, allErrors: false }).compile(schema);
340
+ this.#cache.set(scopeId, validate);
341
+ return validate;
342
+ }
343
+ assert(scopeId, fragment) {
344
+ const validate = this.get(scopeId);
345
+ if (validate(fragment)) return;
346
+ const validationError = validate.errors?.[0];
347
+ if (!validationError) {
348
+ throw new Error(`RuntimeConfig schema validation failed for scope "${scopeId}".`);
349
+ }
350
+ throw this.#formatError({ scopeId }, validationError);
351
+ }
352
+ };
353
+ function createRuntimeConfigValidatorRegistry(schemas, options = {}) {
354
+ return new RuntimeConfigValidatorRegistry(schemas, options);
355
+ }
295
356
  export {
357
+ RuntimeConfigValidatorRegistry,
296
358
  createRuntimeConfigEnvKey,
297
359
  createRuntimeConfigKey,
360
+ createRuntimeConfigValidatorRegistry,
361
+ defaultRuntimeConfigValidationMode,
298
362
  getPrivateRuntimeConfigScope,
299
363
  getPublicRuntimeConfigScope,
300
364
  getRuntimeConfigFragment,
@@ -306,12 +370,16 @@ export {
306
370
  projectRuntimeConfigNamespace,
307
371
  projectRuntimeConfigNamespaces,
308
372
  projectSectionedRuntimeConfig,
373
+ resolveRuntimeConfigValidationMode,
309
374
  resolveRuntimeConfigValue,
310
375
  resolveRuntimeConfigValueFromRuntimeConfig,
311
376
  runtimeConfigVisibilities,
312
377
  runtimeEnvValueToShellLiteral,
313
378
  runtimeEnvVarsToShellAssignments,
314
379
  runtimeEnvVarsToString,
380
+ shouldRegisterRuntimeConfigValidationSchema,
381
+ shouldRequireRuntimeConfigAtStartup,
382
+ shouldValidateRuntimeConfigAtStartup,
315
383
  stripRuntimeConfigScopePrefix,
316
384
  toRuntimeConfigFragment,
317
385
  toRuntimeEnvVars,