@lorion-org/runtime-config 1.0.0-beta.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/LICENSE +21 -0
- package/README.md +309 -0
- package/dist/index.cjs +368 -0
- package/dist/index.d.cts +101 -0
- package/dist/index.d.ts +101 -0
- package/dist/index.js +319 -0
- package/examples/context-input-key.ts +46 -0
- package/examples/env-vars.ts +25 -0
- package/examples/namespaced-runtime-config.ts +50 -0
- package/examples/scope-view.ts +32 -0
- package/examples/sectioned-runtime-config.ts +52 -0
- package/package.json +58 -0
package/LICENSE
ADDED
|
@@ -0,0 +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.
|
package/README.md
ADDED
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
# @lorion-org/runtime-config
|
|
2
|
+
|
|
3
|
+
Pure runtime-config contracts and helpers.
|
|
4
|
+
|
|
5
|
+
This package is free from file-system and framework dependencies.
|
|
6
|
+
|
|
7
|
+
It models small runtime-config fragments, projects them into runtime objects, and
|
|
8
|
+
creates deterministic environment variable names for adapter layers.
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```shell
|
|
13
|
+
pnpm add @lorion-org/runtime-config
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## What it is
|
|
17
|
+
|
|
18
|
+
- typed contracts for runtime-config fragments
|
|
19
|
+
- deterministic scope/key normalization
|
|
20
|
+
- projection helpers for flat sectioned runtime config
|
|
21
|
+
- projection helpers for namespaced runtime config objects
|
|
22
|
+
- configurable context input keys for adapter-specific fragment shapes
|
|
23
|
+
- context-aware lookup helpers
|
|
24
|
+
- scope-view helpers for reading flat runtime config through unprefixed keys
|
|
25
|
+
- environment variable rendering helpers
|
|
26
|
+
|
|
27
|
+
## What it is not
|
|
28
|
+
|
|
29
|
+
- not a framework module
|
|
30
|
+
- not a file-system loader
|
|
31
|
+
- not a schema validator
|
|
32
|
+
- not a config-file parser
|
|
33
|
+
- not an application-specific naming policy
|
|
34
|
+
|
|
35
|
+
## Adapter integration
|
|
36
|
+
|
|
37
|
+
Framework adapters can wire this package into their runtime config transport.
|
|
38
|
+
For Nuxt, use `@lorion-org/nuxt`.
|
|
39
|
+
|
|
40
|
+
The intended adapter shape is:
|
|
41
|
+
|
|
42
|
+
- projects define local fragments with unprefixed keys
|
|
43
|
+
- adapters map those fragments into the target runtime transport
|
|
44
|
+
- usage code can read back unprefixed public/private scope views
|
|
45
|
+
- application-specific names, file names, and defaults stay in the consuming adapter
|
|
46
|
+
|
|
47
|
+
Adapters may accept existing fragment field names and map them to generic
|
|
48
|
+
contexts with `contextInputKey`. For example, a project can read `tenants`
|
|
49
|
+
from its files while this package still works with the generic `contexts`
|
|
50
|
+
model internally.
|
|
51
|
+
|
|
52
|
+
## The three runtime-config shapes
|
|
53
|
+
|
|
54
|
+
This package separates the local config shape from the runtime transport shape.
|
|
55
|
+
|
|
56
|
+
### 1. Fragment shape
|
|
57
|
+
|
|
58
|
+
Fragments are written in local scope vocabulary. The scope id is not repeated in
|
|
59
|
+
the keys.
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
const authFragment = {
|
|
63
|
+
public: {
|
|
64
|
+
url: 'https://auth.example.test',
|
|
65
|
+
realm: 'main',
|
|
66
|
+
},
|
|
67
|
+
private: {
|
|
68
|
+
clientSecret: 'secret',
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
This is the shape that JSON files and schemas usually describe.
|
|
74
|
+
|
|
75
|
+
### 2. Flat runtime shape
|
|
76
|
+
|
|
77
|
+
Some runtimes need one shared `public` and `private` object. The scope id becomes
|
|
78
|
+
a transport prefix so many fragments can coexist without key collisions.
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
const runtimeConfig = projectSectionedRuntimeConfig(new Map([['auth', authFragment]]));
|
|
82
|
+
|
|
83
|
+
// For a single fragment, use:
|
|
84
|
+
projectRuntimeConfigFragment('auth', authFragment);
|
|
85
|
+
|
|
86
|
+
runtimeConfig.public.authUrl;
|
|
87
|
+
// => 'https://auth.example.test'
|
|
88
|
+
runtimeConfig.private.authClientSecret;
|
|
89
|
+
// => 'secret'
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### 3. Environment variable shape
|
|
93
|
+
|
|
94
|
+
Environment variables keep the same transport prefix and add visibility.
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
toRuntimeEnvVars(runtimeConfig, 'APP');
|
|
98
|
+
// => {
|
|
99
|
+
// APP_PUBLIC_AUTH_URL: 'https://auth.example.test',
|
|
100
|
+
// APP_PUBLIC_AUTH_REALM: 'main',
|
|
101
|
+
// APP_PRIVATE_AUTH_CLIENT_SECRET: 'secret'
|
|
102
|
+
// }
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Usage code can read the flat runtime shape back through local keys:
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
const auth = getPublicRuntimeConfigScope(runtimeConfig, 'auth');
|
|
109
|
+
|
|
110
|
+
auth.url;
|
|
111
|
+
// => 'https://auth.example.test'
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Basic example
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
import {
|
|
118
|
+
getPublicRuntimeConfigScope,
|
|
119
|
+
projectSectionedRuntimeConfig,
|
|
120
|
+
resolveRuntimeConfigValue,
|
|
121
|
+
} from '@lorion-org/runtime-config';
|
|
122
|
+
|
|
123
|
+
const fragments = new Map([
|
|
124
|
+
[
|
|
125
|
+
'billing',
|
|
126
|
+
{
|
|
127
|
+
public: {
|
|
128
|
+
apiBase: '/api/billing',
|
|
129
|
+
},
|
|
130
|
+
private: {
|
|
131
|
+
apiSecret: 'secret',
|
|
132
|
+
},
|
|
133
|
+
contexts: {
|
|
134
|
+
tenantA: {
|
|
135
|
+
public: {
|
|
136
|
+
apiBase: '/tenant-a/billing',
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
],
|
|
142
|
+
]);
|
|
143
|
+
|
|
144
|
+
const runtimeConfig = projectSectionedRuntimeConfig(fragments);
|
|
145
|
+
|
|
146
|
+
runtimeConfig.public.billingApiBase;
|
|
147
|
+
// => '/api/billing'
|
|
148
|
+
|
|
149
|
+
resolveRuntimeConfigValue(runtimeConfig.public, 'billing', 'apiBase', {
|
|
150
|
+
contextId: 'tenantA',
|
|
151
|
+
});
|
|
152
|
+
// => '/tenant-a/billing'
|
|
153
|
+
|
|
154
|
+
getPublicRuntimeConfigScope(runtimeConfig, 'billing');
|
|
155
|
+
// => { apiBase: '/api/billing' }
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Example: custom context input key
|
|
159
|
+
|
|
160
|
+
```ts
|
|
161
|
+
import { projectSectionedRuntimeConfig } from '@lorion-org/runtime-config';
|
|
162
|
+
|
|
163
|
+
projectSectionedRuntimeConfig(
|
|
164
|
+
[
|
|
165
|
+
{
|
|
166
|
+
scopeId: 'billing',
|
|
167
|
+
config: {
|
|
168
|
+
public: {
|
|
169
|
+
apiBase: '/api/billing',
|
|
170
|
+
},
|
|
171
|
+
tenants: {
|
|
172
|
+
tenantA: {
|
|
173
|
+
public: {
|
|
174
|
+
apiBase: '/tenant-a/billing',
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
],
|
|
181
|
+
{
|
|
182
|
+
contextInputKey: 'tenants',
|
|
183
|
+
contextOutputKey: '__tenants',
|
|
184
|
+
},
|
|
185
|
+
);
|
|
186
|
+
// => {
|
|
187
|
+
// public: {
|
|
188
|
+
// billingApiBase: '/api/billing',
|
|
189
|
+
// __tenants: {
|
|
190
|
+
// tenantA: {
|
|
191
|
+
// billingApiBase: '/tenant-a/billing'
|
|
192
|
+
// }
|
|
193
|
+
// }
|
|
194
|
+
// },
|
|
195
|
+
// private: {}
|
|
196
|
+
// }
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Example: namespaced projection
|
|
200
|
+
|
|
201
|
+
Use `projectRuntimeConfigNamespace()` when one local fragment becomes one
|
|
202
|
+
runtime namespace. Use `projectRuntimeConfigNamespaces()` when combining many
|
|
203
|
+
fragments; in that case each item needs a `scopeId` so the output namespace is
|
|
204
|
+
explicit.
|
|
205
|
+
|
|
206
|
+
```ts
|
|
207
|
+
import {
|
|
208
|
+
projectRuntimeConfigNamespace,
|
|
209
|
+
projectRuntimeConfigNamespaces,
|
|
210
|
+
} from '@lorion-org/runtime-config';
|
|
211
|
+
|
|
212
|
+
const runtimeConfig = projectRuntimeConfigNamespace('mail', {
|
|
213
|
+
public: {
|
|
214
|
+
apiBase: '/api/mail',
|
|
215
|
+
},
|
|
216
|
+
private: {
|
|
217
|
+
token: 'mail-token',
|
|
218
|
+
},
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
runtimeConfig.public.mail;
|
|
222
|
+
// => { apiBase: '/api/mail' }
|
|
223
|
+
runtimeConfig.mail;
|
|
224
|
+
// => { token: 'mail-token' }
|
|
225
|
+
|
|
226
|
+
const combinedRuntimeConfig = projectRuntimeConfigNamespaces([
|
|
227
|
+
{
|
|
228
|
+
scopeId: 'mail',
|
|
229
|
+
config: {
|
|
230
|
+
public: {
|
|
231
|
+
apiBase: '/api/mail',
|
|
232
|
+
},
|
|
233
|
+
private: {
|
|
234
|
+
token: 'mail-token',
|
|
235
|
+
},
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
]);
|
|
239
|
+
|
|
240
|
+
combinedRuntimeConfig.public.mail;
|
|
241
|
+
// => { apiBase: '/api/mail' }
|
|
242
|
+
combinedRuntimeConfig.mail;
|
|
243
|
+
// => { token: 'mail-token' }
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## Example: environment variables
|
|
247
|
+
|
|
248
|
+
Adapters choose their own prefix. The default prefix is deliberately generic.
|
|
249
|
+
|
|
250
|
+
```ts
|
|
251
|
+
import {
|
|
252
|
+
projectRuntimeConfigEnvVars,
|
|
253
|
+
runtimeEnvVarsToShellAssignments,
|
|
254
|
+
runtimeEnvVarsToString,
|
|
255
|
+
toRuntimeEnvVars,
|
|
256
|
+
} from '@lorion-org/runtime-config';
|
|
257
|
+
|
|
258
|
+
const envVars = toRuntimeEnvVars(
|
|
259
|
+
{
|
|
260
|
+
public: {
|
|
261
|
+
billingApiBase: '/api/billing',
|
|
262
|
+
},
|
|
263
|
+
private: {
|
|
264
|
+
billingApiSecret: 'secret',
|
|
265
|
+
},
|
|
266
|
+
},
|
|
267
|
+
'APP',
|
|
268
|
+
);
|
|
269
|
+
|
|
270
|
+
runtimeEnvVarsToString(envVars);
|
|
271
|
+
// => APP_PUBLIC_BILLING_API_BASE=/api/billing
|
|
272
|
+
// => APP_PRIVATE_BILLING_API_SECRET=secret
|
|
273
|
+
|
|
274
|
+
runtimeEnvVarsToShellAssignments(envVars);
|
|
275
|
+
// => APP_PUBLIC_BILLING_API_BASE='"/api/billing"'
|
|
276
|
+
// => APP_PRIVATE_BILLING_API_SECRET='"secret"'
|
|
277
|
+
|
|
278
|
+
projectRuntimeConfigEnvVars(
|
|
279
|
+
new Map([
|
|
280
|
+
[
|
|
281
|
+
'billing',
|
|
282
|
+
{
|
|
283
|
+
public: {
|
|
284
|
+
apiBase: '/api/billing',
|
|
285
|
+
},
|
|
286
|
+
},
|
|
287
|
+
],
|
|
288
|
+
]),
|
|
289
|
+
{
|
|
290
|
+
prefix: 'APP',
|
|
291
|
+
},
|
|
292
|
+
);
|
|
293
|
+
// => {
|
|
294
|
+
// APP_PUBLIC_BILLING_API_BASE: '/api/billing'
|
|
295
|
+
// }
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
Runnable example files live in [`examples/`](./examples).
|
|
299
|
+
|
|
300
|
+
## Local commands
|
|
301
|
+
|
|
302
|
+
```shell
|
|
303
|
+
cd packages/runtime-config
|
|
304
|
+
pnpm build
|
|
305
|
+
pnpm test
|
|
306
|
+
pnpm coverage
|
|
307
|
+
pnpm typecheck
|
|
308
|
+
pnpm package:check
|
|
309
|
+
```
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
createRuntimeConfigEnvKey: () => createRuntimeConfigEnvKey,
|
|
24
|
+
createRuntimeConfigKey: () => createRuntimeConfigKey,
|
|
25
|
+
getPrivateRuntimeConfigScope: () => getPrivateRuntimeConfigScope,
|
|
26
|
+
getPublicRuntimeConfigScope: () => getPublicRuntimeConfigScope,
|
|
27
|
+
getRuntimeConfigFragment: () => getRuntimeConfigFragment,
|
|
28
|
+
getRuntimeConfigScope: () => getRuntimeConfigScope,
|
|
29
|
+
injectRuntimeEnvVars: () => injectRuntimeEnvVars,
|
|
30
|
+
normalizeRuntimeConfigFragments: () => normalizeRuntimeConfigFragments,
|
|
31
|
+
projectRuntimeConfigEnvVars: () => projectRuntimeConfigEnvVars,
|
|
32
|
+
projectRuntimeConfigFragment: () => projectRuntimeConfigFragment,
|
|
33
|
+
projectRuntimeConfigNamespace: () => projectRuntimeConfigNamespace,
|
|
34
|
+
projectRuntimeConfigNamespaces: () => projectRuntimeConfigNamespaces,
|
|
35
|
+
projectSectionedRuntimeConfig: () => projectSectionedRuntimeConfig,
|
|
36
|
+
resolveRuntimeConfigValue: () => resolveRuntimeConfigValue,
|
|
37
|
+
resolveRuntimeConfigValueFromRuntimeConfig: () => resolveRuntimeConfigValueFromRuntimeConfig,
|
|
38
|
+
runtimeConfigVisibilities: () => runtimeConfigVisibilities,
|
|
39
|
+
runtimeEnvValueToShellLiteral: () => runtimeEnvValueToShellLiteral,
|
|
40
|
+
runtimeEnvVarsToShellAssignments: () => runtimeEnvVarsToShellAssignments,
|
|
41
|
+
runtimeEnvVarsToString: () => runtimeEnvVarsToString,
|
|
42
|
+
stripRuntimeConfigScopePrefix: () => stripRuntimeConfigScopePrefix,
|
|
43
|
+
toRuntimeConfigFragment: () => toRuntimeConfigFragment,
|
|
44
|
+
toRuntimeEnvVars: () => toRuntimeEnvVars,
|
|
45
|
+
toSnakeUpperCase: () => toSnakeUpperCase
|
|
46
|
+
});
|
|
47
|
+
module.exports = __toCommonJS(index_exports);
|
|
48
|
+
|
|
49
|
+
// src/key.ts
|
|
50
|
+
function upperFirst(input) {
|
|
51
|
+
return input ? input[0].toUpperCase() + input.slice(1) : input;
|
|
52
|
+
}
|
|
53
|
+
function uncapitalize(input) {
|
|
54
|
+
return input ? input[0].toLowerCase() + input.slice(1) : input;
|
|
55
|
+
}
|
|
56
|
+
function camelCase(input) {
|
|
57
|
+
return input.replace(/['\u2019]/g, "").split(/[^A-Za-z0-9]+|(?=[A-Z][a-z])/).map((part) => part.trim()).filter(Boolean).map((part) => part.toLowerCase()).map((part, index) => index === 0 ? part : upperFirst(part)).join("");
|
|
58
|
+
}
|
|
59
|
+
function createRuntimeConfigKey(scopeId, key, options = {}) {
|
|
60
|
+
return options.keyStrategy?.(scopeId, key) ?? camelCase(`${scopeId} ${key}`);
|
|
61
|
+
}
|
|
62
|
+
function stripRuntimeConfigScopePrefix(scopeId, configKey, options = {}) {
|
|
63
|
+
const prefix = createRuntimeConfigKey(scopeId, "", options);
|
|
64
|
+
if (!prefix || !configKey.startsWith(prefix) || configKey.length <= prefix.length) {
|
|
65
|
+
return void 0;
|
|
66
|
+
}
|
|
67
|
+
return uncapitalize(configKey.slice(prefix.length));
|
|
68
|
+
}
|
|
69
|
+
function toSnakeUpperCase(input) {
|
|
70
|
+
return input.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^a-zA-Z0-9]+/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "").toUpperCase();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// src/fragment.ts
|
|
74
|
+
var isObject = (value) => {
|
|
75
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
76
|
+
};
|
|
77
|
+
function toRuntimeConfigFragment(config, options = {}) {
|
|
78
|
+
const contextInputKey = options.contextInputKey ?? "contexts";
|
|
79
|
+
const contexts = contextInputKey === "contexts" ? config.contexts : config.contexts ?? config[contextInputKey];
|
|
80
|
+
return {
|
|
81
|
+
...isObject(config.public) ? { public: config.public } : {},
|
|
82
|
+
...isObject(config.private) ? { private: config.private } : {},
|
|
83
|
+
...isObject(contexts) ? { contexts } : {}
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// src/types.ts
|
|
88
|
+
var runtimeConfigVisibilities = ["public", "private"];
|
|
89
|
+
|
|
90
|
+
// src/project.ts
|
|
91
|
+
function normalizeRuntimeConfigFragments(fragments, options = {}) {
|
|
92
|
+
const entries = fragments instanceof Map ? Array.from(fragments.entries()).map(([scopeId, config]) => ({ scopeId, config })) : Array.from(fragments);
|
|
93
|
+
return entries.map((fragment) => ({
|
|
94
|
+
scopeId: fragment.scopeId.trim(),
|
|
95
|
+
config: toRuntimeConfigFragment(fragment.config, options)
|
|
96
|
+
})).filter((fragment) => fragment.scopeId.length > 0).sort((left, right) => left.scopeId.localeCompare(right.scopeId));
|
|
97
|
+
}
|
|
98
|
+
function filterFragments(input) {
|
|
99
|
+
const scopeIdSet = input.scopeIds ? new Set(input.scopeIds.map((id) => id.trim()).filter(Boolean)) : void 0;
|
|
100
|
+
return normalizeRuntimeConfigFragments(input.fragments, {
|
|
101
|
+
...input.contextInputKey ? { contextInputKey: input.contextInputKey } : {}
|
|
102
|
+
}).filter((fragment) => !scopeIdSet || scopeIdSet.has(fragment.scopeId));
|
|
103
|
+
}
|
|
104
|
+
function getSection(config, visibility) {
|
|
105
|
+
return config?.[visibility];
|
|
106
|
+
}
|
|
107
|
+
function assignDefined(target, key, value) {
|
|
108
|
+
if (value !== void 0) {
|
|
109
|
+
target[key] = value;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function projectSectionedRuntimeConfig(fragments, options = {}) {
|
|
113
|
+
const result = {
|
|
114
|
+
public: {},
|
|
115
|
+
private: {}
|
|
116
|
+
};
|
|
117
|
+
const contextOutputKey = options.contextOutputKey ?? "__contexts";
|
|
118
|
+
const filteredFragments = filterFragments({
|
|
119
|
+
fragments,
|
|
120
|
+
...options.contextInputKey ? { contextInputKey: options.contextInputKey } : {},
|
|
121
|
+
...options.scopeIds ? { scopeIds: options.scopeIds } : {}
|
|
122
|
+
});
|
|
123
|
+
for (const fragment of filteredFragments) {
|
|
124
|
+
for (const visibility of runtimeConfigVisibilities) {
|
|
125
|
+
const section = getSection(fragment.config, visibility);
|
|
126
|
+
if (!section) continue;
|
|
127
|
+
for (const [key, value] of Object.entries(section)) {
|
|
128
|
+
assignDefined(
|
|
129
|
+
result[visibility],
|
|
130
|
+
createRuntimeConfigKey(fragment.scopeId, key, options),
|
|
131
|
+
value
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (options.includeContexts === false) continue;
|
|
136
|
+
for (const [contextId, context] of Object.entries(fragment.config.contexts ?? {})) {
|
|
137
|
+
for (const visibility of runtimeConfigVisibilities) {
|
|
138
|
+
const section = getSection(context, visibility);
|
|
139
|
+
if (!section) continue;
|
|
140
|
+
const contexts = result[visibility][contextOutputKey] ?? {};
|
|
141
|
+
const contextValues = contexts[contextId] ?? {};
|
|
142
|
+
for (const [key, value] of Object.entries(section)) {
|
|
143
|
+
assignDefined(
|
|
144
|
+
contextValues,
|
|
145
|
+
createRuntimeConfigKey(fragment.scopeId, key, options),
|
|
146
|
+
value
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
contexts[contextId] = contextValues;
|
|
150
|
+
result[visibility][contextOutputKey] = contexts;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return result;
|
|
155
|
+
}
|
|
156
|
+
function projectRuntimeConfigFragment(scopeId, config, options = {}) {
|
|
157
|
+
return projectSectionedRuntimeConfig(/* @__PURE__ */ new Map([[scopeId, config]]), options);
|
|
158
|
+
}
|
|
159
|
+
function projectRuntimeConfigNamespace(scopeId, config, options = {}) {
|
|
160
|
+
return projectRuntimeConfigNamespaces([{ scopeId, config }], options);
|
|
161
|
+
}
|
|
162
|
+
function projectRuntimeConfigNamespaces(fragments, options = {}) {
|
|
163
|
+
const result = {
|
|
164
|
+
public: {}
|
|
165
|
+
};
|
|
166
|
+
const namespaceStrategy = options.namespaceStrategy ?? "nested";
|
|
167
|
+
const filteredFragments = filterFragments({
|
|
168
|
+
fragments,
|
|
169
|
+
...options.contextInputKey ? { contextInputKey: options.contextInputKey } : {},
|
|
170
|
+
...options.scopeIds ? { scopeIds: options.scopeIds } : {}
|
|
171
|
+
});
|
|
172
|
+
for (const fragment of filteredFragments) {
|
|
173
|
+
const context = options.contextId ? fragment.config.contexts?.[options.contextId] : void 0;
|
|
174
|
+
const publicSection = {
|
|
175
|
+
...fragment.config.public ?? {},
|
|
176
|
+
...context?.public ?? {}
|
|
177
|
+
};
|
|
178
|
+
const privateSection = {
|
|
179
|
+
...fragment.config.private ?? {},
|
|
180
|
+
...context?.private ?? {}
|
|
181
|
+
};
|
|
182
|
+
if (namespaceStrategy === "flat") {
|
|
183
|
+
Object.assign(result.public, publicSection);
|
|
184
|
+
Object.assign(result, privateSection);
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
if (Object.keys(publicSection).length) {
|
|
188
|
+
result.public[fragment.scopeId] = {
|
|
189
|
+
...result.public[fragment.scopeId] ?? {},
|
|
190
|
+
...publicSection
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
if (Object.keys(privateSection).length) {
|
|
194
|
+
result[fragment.scopeId] = {
|
|
195
|
+
...result[fragment.scopeId] ?? {},
|
|
196
|
+
...privateSection
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return result;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// src/env.ts
|
|
204
|
+
function createRuntimeConfigEnvKey(input) {
|
|
205
|
+
return toSnakeUpperCase(
|
|
206
|
+
[input.prefix ?? "APP", input.visibility, input.scopeId, input.key].filter(Boolean).join("_")
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
function toRuntimeEnvVars(runtimeConfig, prefix = "APP") {
|
|
210
|
+
const envVars = {};
|
|
211
|
+
for (const visibility of runtimeConfigVisibilities) {
|
|
212
|
+
for (const [key, value] of Object.entries(runtimeConfig[visibility])) {
|
|
213
|
+
if (key.startsWith("__")) continue;
|
|
214
|
+
envVars[createRuntimeConfigEnvKey({ prefix, visibility, key })] = value;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return envVars;
|
|
218
|
+
}
|
|
219
|
+
function projectRuntimeConfigEnvVars(fragments, options = {}) {
|
|
220
|
+
const { prefix = "APP", ...projectOptions } = options;
|
|
221
|
+
return toRuntimeEnvVars(
|
|
222
|
+
projectSectionedRuntimeConfig(fragments, {
|
|
223
|
+
...projectOptions,
|
|
224
|
+
includeContexts: false
|
|
225
|
+
}),
|
|
226
|
+
prefix
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
function injectRuntimeEnvVars(envVars, processEnv) {
|
|
230
|
+
const items = {};
|
|
231
|
+
for (const key of Object.keys(envVars)) {
|
|
232
|
+
items[key] = processEnv[key] ?? envVars[key];
|
|
233
|
+
}
|
|
234
|
+
return items;
|
|
235
|
+
}
|
|
236
|
+
function runtimeEnvVarsToString(envVars) {
|
|
237
|
+
return Object.entries(envVars).map(([key, value]) => `${key}=${String(value)}`).join("\n");
|
|
238
|
+
}
|
|
239
|
+
function runtimeEnvValueToShellLiteral(value) {
|
|
240
|
+
const json = JSON.stringify(value) ?? "undefined";
|
|
241
|
+
return `'${json.replace(/'/g, "'\\''")}'`;
|
|
242
|
+
}
|
|
243
|
+
function runtimeEnvVarsToShellAssignments(envVars) {
|
|
244
|
+
return Object.entries(envVars).map(([key, value]) => `${key}=${runtimeEnvValueToShellLiteral(value)}`).join("\n");
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// src/resolve.ts
|
|
248
|
+
function resolveRuntimeConfigValue(scopeConfig, scopeId, key, options = {}) {
|
|
249
|
+
const configKey = createRuntimeConfigKey(scopeId, key, options);
|
|
250
|
+
const contextOutputKey = options.contextOutputKey ?? "__contexts";
|
|
251
|
+
if (options.contextId) {
|
|
252
|
+
const contexts = scopeConfig?.[contextOutputKey];
|
|
253
|
+
const contextValue = contexts?.[options.contextId]?.[configKey];
|
|
254
|
+
if (contextValue !== void 0) {
|
|
255
|
+
return contextValue;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
const globalValue = scopeConfig?.[configKey];
|
|
259
|
+
if (globalValue !== void 0) {
|
|
260
|
+
return globalValue;
|
|
261
|
+
}
|
|
262
|
+
return options.defaultValue;
|
|
263
|
+
}
|
|
264
|
+
function resolveRuntimeConfigValueFromRuntimeConfig(runtimeConfig, scopeId, key, options = {}) {
|
|
265
|
+
const { visibility = "public", ...resolveOptions } = options;
|
|
266
|
+
return resolveRuntimeConfigValue(runtimeConfig[visibility], scopeId, key, resolveOptions);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// src/scope.ts
|
|
270
|
+
function assignDefined2(target, key, value) {
|
|
271
|
+
if (value !== void 0) {
|
|
272
|
+
target[key] = value;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
function collectRuntimeConfigScopeValues(input) {
|
|
276
|
+
const values = {};
|
|
277
|
+
for (const [configKey, value] of Object.entries(input.scopeConfig ?? {})) {
|
|
278
|
+
if (configKey.startsWith("__")) continue;
|
|
279
|
+
const key = stripRuntimeConfigScopePrefix(input.scopeId, configKey, input.options);
|
|
280
|
+
if (key) assignDefined2(values, key, value);
|
|
281
|
+
}
|
|
282
|
+
return values;
|
|
283
|
+
}
|
|
284
|
+
function getRuntimeConfigScope(runtimeConfig, scopeId, options = {}) {
|
|
285
|
+
const {
|
|
286
|
+
contextId,
|
|
287
|
+
contextOutputKey = "__contexts",
|
|
288
|
+
keys,
|
|
289
|
+
visibility = "public",
|
|
290
|
+
...keyOptions
|
|
291
|
+
} = options;
|
|
292
|
+
const scopeConfig = runtimeConfig[visibility];
|
|
293
|
+
if (keys) {
|
|
294
|
+
return Object.fromEntries(
|
|
295
|
+
keys.map((key) => {
|
|
296
|
+
const value = resolveRuntimeConfigValue(scopeConfig, scopeId, key, {
|
|
297
|
+
...keyOptions,
|
|
298
|
+
...contextId ? { contextId } : {},
|
|
299
|
+
contextOutputKey
|
|
300
|
+
});
|
|
301
|
+
return [key, value];
|
|
302
|
+
}).filter(([, value]) => value !== void 0)
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
const values = collectRuntimeConfigScopeValues({
|
|
306
|
+
scopeConfig,
|
|
307
|
+
scopeId,
|
|
308
|
+
options: keyOptions
|
|
309
|
+
});
|
|
310
|
+
if (!contextId) return values;
|
|
311
|
+
const contexts = scopeConfig?.[contextOutputKey];
|
|
312
|
+
const contextValues = collectRuntimeConfigScopeValues({
|
|
313
|
+
scopeConfig: contexts?.[contextId],
|
|
314
|
+
scopeId,
|
|
315
|
+
options: keyOptions
|
|
316
|
+
});
|
|
317
|
+
return {
|
|
318
|
+
...values,
|
|
319
|
+
...contextValues
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
function getPublicRuntimeConfigScope(runtimeConfig, scopeId, options = {}) {
|
|
323
|
+
return getRuntimeConfigScope(runtimeConfig, scopeId, {
|
|
324
|
+
...options,
|
|
325
|
+
visibility: "public"
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
function getPrivateRuntimeConfigScope(runtimeConfig, scopeId, options = {}) {
|
|
329
|
+
return getRuntimeConfigScope(runtimeConfig, scopeId, {
|
|
330
|
+
...options,
|
|
331
|
+
visibility: "private"
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
function getRuntimeConfigFragment(runtimeConfig, scopeId, options = {}) {
|
|
335
|
+
const { keys, ...scopeOptions } = options;
|
|
336
|
+
const publicOptions = keys?.public ? { ...scopeOptions, keys: keys.public } : scopeOptions;
|
|
337
|
+
const privateOptions = keys?.private ? { ...scopeOptions, keys: keys.private } : scopeOptions;
|
|
338
|
+
return {
|
|
339
|
+
public: getPublicRuntimeConfigScope(runtimeConfig, scopeId, publicOptions),
|
|
340
|
+
private: getPrivateRuntimeConfigScope(runtimeConfig, scopeId, privateOptions)
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
344
|
+
0 && (module.exports = {
|
|
345
|
+
createRuntimeConfigEnvKey,
|
|
346
|
+
createRuntimeConfigKey,
|
|
347
|
+
getPrivateRuntimeConfigScope,
|
|
348
|
+
getPublicRuntimeConfigScope,
|
|
349
|
+
getRuntimeConfigFragment,
|
|
350
|
+
getRuntimeConfigScope,
|
|
351
|
+
injectRuntimeEnvVars,
|
|
352
|
+
normalizeRuntimeConfigFragments,
|
|
353
|
+
projectRuntimeConfigEnvVars,
|
|
354
|
+
projectRuntimeConfigFragment,
|
|
355
|
+
projectRuntimeConfigNamespace,
|
|
356
|
+
projectRuntimeConfigNamespaces,
|
|
357
|
+
projectSectionedRuntimeConfig,
|
|
358
|
+
resolveRuntimeConfigValue,
|
|
359
|
+
resolveRuntimeConfigValueFromRuntimeConfig,
|
|
360
|
+
runtimeConfigVisibilities,
|
|
361
|
+
runtimeEnvValueToShellLiteral,
|
|
362
|
+
runtimeEnvVarsToShellAssignments,
|
|
363
|
+
runtimeEnvVarsToString,
|
|
364
|
+
stripRuntimeConfigScopePrefix,
|
|
365
|
+
toRuntimeConfigFragment,
|
|
366
|
+
toRuntimeEnvVars,
|
|
367
|
+
toSnakeUpperCase
|
|
368
|
+
});
|