@burojs/config 0.2.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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Maksim Kuznetsov
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,61 @@
1
+ # @burojs/config
2
+
3
+ Inert JSON and YAML declaration sources for [buro](https://github.com/buro/buro).
4
+
5
+ Parses a raw JSON or YAML string against a host-owned `SourcePolicy` and
6
+ produces a portable declaration layer — or a list of diagnostics if the
7
+ source is malformed, oversized, or requests something its policy doesn't
8
+ allow. The output can only ever be data: no functions, components, schemas,
9
+ credentials, or provider/action/mapper bindings survive the parse.
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ pnpm add @burojs/config
15
+ ```
16
+
17
+ ## Usage
18
+
19
+ ```ts
20
+ import { parseJsonLayer, type SourcePolicy } from '@burojs/config';
21
+
22
+ const policy: SourcePolicy = {
23
+ allowedKinds: ['resource'],
24
+ allowedNamespaces: ['crm'],
25
+ allowedTransformOps: [],
26
+ maxTransformScope: 'layer',
27
+ allowedCapabilities: {},
28
+ limits: { maxBytes: 16_000, maxDeclarations: 12, maxDepth: 16, maxStringLength: 1_000, maxDiagnostics: 8 },
29
+ };
30
+
31
+ const source = JSON.stringify({
32
+ id: 'static-catalog',
33
+ priority: 0,
34
+ defaultNamespace: 'crm',
35
+ revision: 'r1',
36
+ declarations: [
37
+ { kind: 'resource', ref: 'company', schemaVersion: 1, value: { name: 'company' } },
38
+ ],
39
+ });
40
+
41
+ const result = parseJsonLayer(source, policy);
42
+ if (!result.ok) throw new Error(result.diagnostics.map((d) => d.code).join(', '));
43
+ // result.value is a LayerInput, ready for runtime.publishLayer(...)
44
+ ```
45
+
46
+ To load a source lazily instead of publishing it up front, wrap the parser in
47
+ `createConfigRuntimeSource` and hand the result to a core `LayerRuntime` via
48
+ `registerSource`/`loadSource`.
49
+
50
+ ## Main exports
51
+
52
+ - **`parseJsonLayer` / `parseYamlLayer`** — parse a source string against a `SourcePolicy`, returning a `ParseResult`.
53
+ - **`createConfigRuntimeSource`** — builds a core `RuntimeSource` that fetches, parses, and adapts a source on demand.
54
+ - **`adaptRawConfig` / `parseResultAdapter`** — the pure adapter boundary between a raw source shape and the layer the runtime commits.
55
+ - **`SourcePolicy` / `SourceLimits`** — the allow-list and resource limits a host defines for one untrusted source.
56
+
57
+ See [`docs/raw-config-manifests.md`](../../docs/raw-config-manifests.md) for the full source-policy boundary and how it composes with a `LayerRuntime`.
58
+
59
+ ## License
60
+
61
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,563 @@
1
+ 'use strict';
2
+
3
+ var yaml = require('yaml');
4
+
5
+ // src/parse.ts
6
+ var RESERVED_KEYS = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]);
7
+ var FORBIDDEN_VALUE_CATEGORIES = /* @__PURE__ */ new Map([
8
+ ["action", "action"],
9
+ ["actions", "action"],
10
+ ["provider", "provider"],
11
+ ["providers", "provider"],
12
+ ["mapper", "mapper"],
13
+ ["mappers", "mapper"],
14
+ ["service", "service"],
15
+ ["services", "service"],
16
+ ["credential", "credential"],
17
+ ["credentials", "credential"],
18
+ ["schema", "schema"],
19
+ ["schemas", "schema"],
20
+ ["component", "component"],
21
+ ["components", "component"],
22
+ ["attachment", "attachment"],
23
+ ["attachments", "attachment"],
24
+ ["validator", "validator"],
25
+ ["validators", "validator"]
26
+ ]);
27
+ var LAYER_KEYS = /* @__PURE__ */ new Set([
28
+ "id",
29
+ "priority",
30
+ "defaultNamespace",
31
+ "revision",
32
+ "declarations",
33
+ "transforms"
34
+ ]);
35
+ var DECLARATION_KEYS = /* @__PURE__ */ new Set(["kind", "ref", "schemaVersion", "value"]);
36
+ var TRANSFORM_KEYS = /* @__PURE__ */ new Set(["id", "scope", "select", "operation"]);
37
+ var SELECTOR_KEYS = /* @__PURE__ */ new Set(["kinds", "namespaces", "layers", "refs", "excludeRefs", "tags"]);
38
+ var OPERATION_KEYS = {
39
+ merge: /* @__PURE__ */ new Set(["op", "value"]),
40
+ replace: /* @__PURE__ */ new Set(["op", "value", "ifMissing"]),
41
+ disable: /* @__PURE__ */ new Set(["op", "reason"]),
42
+ delete: /* @__PURE__ */ new Set(["op"])
43
+ };
44
+ var ATTACHMENT_TAG = "@burojs/core/trusted-attachment";
45
+ var Diagnostics = class {
46
+ constructor(limit) {
47
+ this.limit = limit;
48
+ }
49
+ limit;
50
+ values = [];
51
+ limited = false;
52
+ add(code) {
53
+ if (this.limited) return;
54
+ if (this.values.length + 1 < this.limit) {
55
+ this.values.push(Object.freeze({ code, message: messageFor(code) }));
56
+ return;
57
+ }
58
+ this.limited = true;
59
+ this.values.push(
60
+ Object.freeze({
61
+ code: "source.diagnostic-limit",
62
+ message: messageFor("source.diagnostic-limit")
63
+ })
64
+ );
65
+ }
66
+ };
67
+ function messageFor(code) {
68
+ switch (code) {
69
+ case "source.invalid-json":
70
+ return "The source is not valid JSON.";
71
+ case "source.invalid-yaml":
72
+ return "The source is not valid YAML.";
73
+ case "source.invalid-policy":
74
+ return "The source policy is invalid.";
75
+ case "source.byte-limit":
76
+ return "The source exceeds its byte limit.";
77
+ case "source.declaration-limit":
78
+ return "The source exceeds its declaration limit.";
79
+ case "source.depth-limit":
80
+ return "The source exceeds its nesting-depth limit.";
81
+ case "source.string-limit":
82
+ return "The source contains an overlong string.";
83
+ case "source.diagnostic-limit":
84
+ return "Additional source diagnostics were omitted.";
85
+ case "source.unknown-key":
86
+ return "The source contains an unknown key.";
87
+ case "source.reserved-key":
88
+ return "The source contains a reserved key.";
89
+ case "source.accessor":
90
+ return "The source must use data properties.";
91
+ case "source.cyclic-value":
92
+ return "The source must not contain cyclic values.";
93
+ case "source.invalid-layer":
94
+ return "The source layer is invalid.";
95
+ case "source.invalid-declaration":
96
+ return "The source declaration is invalid.";
97
+ case "source.invalid-transform":
98
+ return "The source transform is invalid.";
99
+ case "source.forbidden-kind":
100
+ return "The source requests a forbidden declaration kind.";
101
+ case "source.forbidden-namespace":
102
+ return "The source requests a forbidden namespace.";
103
+ case "source.forbidden-transform-op":
104
+ return "The source requests a forbidden transform operation.";
105
+ case "source.forbidden-transform-scope":
106
+ return "The source requests a forbidden transform scope.";
107
+ case "source.forbidden-capability":
108
+ return "The source requests a forbidden capability.";
109
+ case "source.forbidden-action":
110
+ return "The source requests a forbidden action binding.";
111
+ case "source.forbidden-provider":
112
+ return "The source requests a forbidden provider binding.";
113
+ case "source.forbidden-mapper":
114
+ return "The source requests a forbidden mapper binding.";
115
+ case "source.forbidden-service":
116
+ return "The source requests a forbidden service value.";
117
+ case "source.forbidden-credential":
118
+ return "The source requests a forbidden credential value.";
119
+ case "source.forbidden-schema":
120
+ return "The source requests a forbidden schema value.";
121
+ case "source.forbidden-component":
122
+ return "The source requests a forbidden component value.";
123
+ case "source.forbidden-attachment":
124
+ return "The source requests a forbidden attachment value.";
125
+ case "source.forbidden-validator":
126
+ return "The source requests a forbidden validator value.";
127
+ default:
128
+ return "The source is invalid.";
129
+ }
130
+ }
131
+ function validLimits(limits) {
132
+ return Object.values(limits).every((value) => Number.isSafeInteger(value) && value > 0);
133
+ }
134
+ function validCapabilities(value) {
135
+ return value !== null && typeof value === "object" && !Array.isArray(value) && Object.values(value).every(
136
+ (capabilities) => Array.isArray(capabilities) && capabilities.every((capability) => typeof capability === "string")
137
+ );
138
+ }
139
+ function validPolicy(policy) {
140
+ return policy !== null && typeof policy === "object" && Array.isArray(policy.allowedKinds) && Array.isArray(policy.allowedNamespaces) && Array.isArray(policy.allowedTransformOps) && (policy.maxTransformScope === "layer" || policy.maxTransformScope === "namespace" || policy.maxTransformScope === "application") && validCapabilities(policy.allowedCapabilities) && validLimits(policy.limits);
141
+ }
142
+ function failure(diagnostics) {
143
+ return { ok: false, diagnostics: Object.freeze(diagnostics.values) };
144
+ }
145
+ function plainObject(value) {
146
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
147
+ const prototype = Object.getPrototypeOf(value);
148
+ return prototype === Object.prototype || prototype === null;
149
+ }
150
+ function ownData(value, key) {
151
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
152
+ return descriptor !== void 0 && "value" in descriptor ? descriptor.value : void 0;
153
+ }
154
+ function hasData(value, key) {
155
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
156
+ return descriptor !== void 0 && "value" in descriptor;
157
+ }
158
+ function clonePortable(value, limits, diagnostics) {
159
+ const ancestors = /* @__PURE__ */ new WeakSet();
160
+ function copy(current, depth) {
161
+ if (depth > limits.maxDepth) {
162
+ diagnostics.add("source.depth-limit");
163
+ return null;
164
+ }
165
+ if (current === null || typeof current === "boolean" || typeof current === "number")
166
+ return current;
167
+ if (typeof current === "string") {
168
+ if ([...current].length > limits.maxStringLength) diagnostics.add("source.string-limit");
169
+ return current;
170
+ }
171
+ if (typeof current !== "object") {
172
+ diagnostics.add("source.invalid-layer");
173
+ return null;
174
+ }
175
+ if (ancestors.has(current)) {
176
+ diagnostics.add("source.cyclic-value");
177
+ return null;
178
+ }
179
+ ancestors.add(current);
180
+ if (Array.isArray(current)) {
181
+ const output2 = [];
182
+ for (const key of Reflect.ownKeys(current)) {
183
+ if (typeof key !== "string" || key !== "length" && !/^(0|[1-9]\d*)$/u.test(key)) {
184
+ diagnostics.add("source.invalid-layer");
185
+ continue;
186
+ }
187
+ if (key === "length") continue;
188
+ if (!hasData(current, key)) diagnostics.add("source.accessor");
189
+ }
190
+ const length = ownData(current, "length");
191
+ const arrayLength = typeof length === "number" && Number.isSafeInteger(length) && length >= 0 ? length : void 0;
192
+ if (arrayLength === void 0) diagnostics.add("source.invalid-layer");
193
+ else
194
+ for (let index = 0; index < arrayLength; index += 1) {
195
+ if (!hasData(current, String(index))) diagnostics.add("source.accessor");
196
+ output2.push(copy(ownData(current, String(index)), depth + 1));
197
+ }
198
+ ancestors.delete(current);
199
+ return Object.freeze(output2);
200
+ }
201
+ if (!plainObject(current)) diagnostics.add("source.invalid-layer");
202
+ const output = /* @__PURE__ */ Object.create(null);
203
+ for (const key of Reflect.ownKeys(current)) {
204
+ if (typeof key !== "string") {
205
+ diagnostics.add("source.invalid-layer");
206
+ continue;
207
+ }
208
+ if (RESERVED_KEYS.has(key)) diagnostics.add("source.reserved-key");
209
+ if ([...key].length > limits.maxStringLength) diagnostics.add("source.string-limit");
210
+ if (!hasData(current, key)) {
211
+ diagnostics.add("source.accessor");
212
+ continue;
213
+ }
214
+ output[key] = copy(ownData(current, key), depth + 1);
215
+ }
216
+ ancestors.delete(current);
217
+ return Object.freeze(output);
218
+ }
219
+ return copy(value, 0);
220
+ }
221
+ function unknownKeys(value, allowed, diagnostics) {
222
+ for (const key of Object.keys(value))
223
+ if (!allowed.has(key)) diagnostics.add("source.unknown-key");
224
+ }
225
+ function allowedNamespace(value, policy, diagnostics) {
226
+ if (typeof value !== "string" || !policy.allowedNamespaces.includes(value)) {
227
+ diagnostics.add("source.forbidden-namespace");
228
+ return false;
229
+ }
230
+ return true;
231
+ }
232
+ function namespaceFor(ref, defaultNamespace, diagnostics) {
233
+ if (typeof ref === "string") {
234
+ const separator = ref.indexOf(":");
235
+ return separator === -1 ? defaultNamespace : ref.slice(0, separator);
236
+ }
237
+ if (!plainObject(ref) || !hasData(ref, "id")) {
238
+ diagnostics.add("source.invalid-declaration");
239
+ return void 0;
240
+ }
241
+ for (const key of Object.keys(ref))
242
+ if (key !== "id" && key !== "namespace") diagnostics.add("source.unknown-key");
243
+ const namespace = hasData(ref, "namespace") ? ownData(ref, "namespace") : defaultNamespace;
244
+ if (typeof namespace !== "string") diagnostics.add("source.invalid-declaration");
245
+ return typeof namespace === "string" ? namespace : void 0;
246
+ }
247
+ function validateValue(value, kind, policy, diagnostics) {
248
+ if (value === null || typeof value !== "object") return;
249
+ if (Array.isArray(value)) {
250
+ for (const item of value) validateValue(item, kind, policy, diagnostics);
251
+ return;
252
+ }
253
+ if (!plainObject(value)) return;
254
+ if (value.$buro === ATTACHMENT_TAG) {
255
+ const capability = ownData(value, "capability");
256
+ if (typeof capability !== "string" || !policy.allowedCapabilities[kind]?.includes(capability)) {
257
+ diagnostics.add("source.forbidden-capability");
258
+ }
259
+ }
260
+ for (const key of Object.keys(value)) {
261
+ const category = FORBIDDEN_VALUE_CATEGORIES.get(key);
262
+ if (category !== void 0) diagnostics.add(`source.forbidden-${category}`);
263
+ validateValue(ownData(value, key), kind, policy, diagnostics);
264
+ }
265
+ }
266
+ function scopeRank(scope) {
267
+ return scope === "layer" ? 0 : scope === "namespace" ? 1 : 2;
268
+ }
269
+ function transformOperation(value) {
270
+ if (!plainObject(value)) return void 0;
271
+ const operation = ownData(value, "op");
272
+ return operation === "merge" || operation === "replace" || operation === "disable" || operation === "delete" ? operation : void 0;
273
+ }
274
+ function validateSelectorRefs(value, defaultNamespace, policy, diagnostics) {
275
+ if (value === void 0) return;
276
+ if (!Array.isArray(value)) {
277
+ diagnostics.add("source.invalid-transform");
278
+ return;
279
+ }
280
+ for (const ref of value) {
281
+ const namespace = namespaceFor(ref, defaultNamespace, diagnostics);
282
+ if (namespace !== void 0) allowedNamespace(namespace, policy, diagnostics);
283
+ }
284
+ }
285
+ function validateTransforms(value, defaultNamespace, policy, diagnostics) {
286
+ if (value === void 0) return;
287
+ if (!Array.isArray(value)) {
288
+ diagnostics.add("source.invalid-transform");
289
+ return;
290
+ }
291
+ for (const transform of value) {
292
+ if (!plainObject(transform)) {
293
+ diagnostics.add("source.invalid-transform");
294
+ continue;
295
+ }
296
+ unknownKeys(transform, TRANSFORM_KEYS, diagnostics);
297
+ const scope = ownData(transform, "scope") ?? "layer";
298
+ if (scope !== "layer" && scope !== "application") diagnostics.add("source.invalid-transform");
299
+ else if (scopeRank(scope) > scopeRank(policy.maxTransformScope))
300
+ diagnostics.add("source.forbidden-transform-scope");
301
+ const select = ownData(transform, "select");
302
+ if (!plainObject(select)) diagnostics.add("source.invalid-transform");
303
+ else {
304
+ unknownKeys(select, SELECTOR_KEYS, diagnostics);
305
+ const selectedKinds = ownData(select, "kinds");
306
+ if (selectedKinds !== void 0) {
307
+ if (!Array.isArray(selectedKinds)) diagnostics.add("source.invalid-transform");
308
+ else
309
+ for (const kind of selectedKinds) {
310
+ if (typeof kind !== "string") diagnostics.add("source.invalid-transform");
311
+ else if (!policy.allowedKinds.includes(kind)) diagnostics.add("source.forbidden-kind");
312
+ }
313
+ }
314
+ const selectedNamespaces = ownData(select, "namespaces");
315
+ if (selectedNamespaces !== void 0) {
316
+ if (!Array.isArray(selectedNamespaces)) diagnostics.add("source.invalid-transform");
317
+ else
318
+ for (const namespace of selectedNamespaces)
319
+ allowedNamespace(namespace, policy, diagnostics);
320
+ }
321
+ validateSelectorRefs(ownData(select, "refs"), defaultNamespace, policy, diagnostics);
322
+ validateSelectorRefs(ownData(select, "excludeRefs"), defaultNamespace, policy, diagnostics);
323
+ }
324
+ const operation = ownData(transform, "operation");
325
+ const op = transformOperation(operation);
326
+ const rawOp = plainObject(operation) ? ownData(operation, "op") : void 0;
327
+ if (typeof rawOp === "string" && op === void 0)
328
+ diagnostics.add("source.forbidden-transform-op");
329
+ else if (op === void 0) diagnostics.add("source.invalid-transform");
330
+ else {
331
+ if (!policy.allowedTransformOps.includes(op))
332
+ diagnostics.add("source.forbidden-transform-op");
333
+ if (plainObject(operation)) {
334
+ unknownKeys(operation, OPERATION_KEYS[op], diagnostics);
335
+ if (hasData(operation, "value"))
336
+ validateValue(ownData(operation, "value"), "", policy, diagnostics);
337
+ }
338
+ }
339
+ }
340
+ }
341
+ function validateLayerValue(value, policy) {
342
+ if (!validPolicy(policy)) {
343
+ return {
344
+ ok: false,
345
+ diagnostics: Object.freeze([
346
+ Object.freeze({
347
+ code: "source.invalid-policy",
348
+ message: messageFor("source.invalid-policy")
349
+ })
350
+ ])
351
+ };
352
+ }
353
+ const diagnostics = new Diagnostics(policy.limits.maxDiagnostics);
354
+ const cloned = clonePortable(value, policy.limits, diagnostics);
355
+ if (diagnostics.values.length > 0) return failure(diagnostics);
356
+ if (!plainObject(cloned)) {
357
+ diagnostics.add("source.invalid-layer");
358
+ return failure(diagnostics);
359
+ }
360
+ unknownKeys(cloned, LAYER_KEYS, diagnostics);
361
+ const id = ownData(cloned, "id");
362
+ const priority = ownData(cloned, "priority");
363
+ const declarations = ownData(cloned, "declarations");
364
+ if (typeof id !== "string" || !Number.isSafeInteger(priority) || !Array.isArray(declarations)) {
365
+ diagnostics.add("source.invalid-layer");
366
+ return failure(diagnostics);
367
+ }
368
+ if (declarations.length > policy.limits.maxDeclarations)
369
+ diagnostics.add("source.declaration-limit");
370
+ const rawDefaultNamespace = ownData(cloned, "defaultNamespace");
371
+ const defaultNamespace = rawDefaultNamespace === void 0 ? "default" : rawDefaultNamespace;
372
+ if (typeof defaultNamespace !== "string") diagnostics.add("source.invalid-layer");
373
+ else allowedNamespace(defaultNamespace, policy, diagnostics);
374
+ for (const declaration of declarations) {
375
+ if (!plainObject(declaration)) {
376
+ diagnostics.add("source.invalid-declaration");
377
+ continue;
378
+ }
379
+ unknownKeys(declaration, DECLARATION_KEYS, diagnostics);
380
+ const kind = ownData(declaration, "kind");
381
+ if (typeof kind !== "string") diagnostics.add("source.invalid-declaration");
382
+ else if (!policy.allowedKinds.includes(kind)) diagnostics.add("source.forbidden-kind");
383
+ const namespace = namespaceFor(
384
+ ownData(declaration, "ref"),
385
+ typeof defaultNamespace === "string" ? defaultNamespace : "default",
386
+ diagnostics
387
+ );
388
+ if (namespace !== void 0) allowedNamespace(namespace, policy, diagnostics);
389
+ if (!Number.isSafeInteger(ownData(declaration, "schemaVersion")) || !hasData(declaration, "value")) {
390
+ diagnostics.add("source.invalid-declaration");
391
+ }
392
+ if (typeof kind === "string" && hasData(declaration, "value")) {
393
+ validateValue(ownData(declaration, "value"), kind, policy, diagnostics);
394
+ }
395
+ }
396
+ validateTransforms(
397
+ ownData(cloned, "transforms"),
398
+ typeof defaultNamespace === "string" ? defaultNamespace : "default",
399
+ policy,
400
+ diagnostics
401
+ );
402
+ if (diagnostics.values.length > 0) return failure(diagnostics);
403
+ return { ok: true, value: cloned };
404
+ }
405
+ function byteLength(value) {
406
+ return new TextEncoder().encode(value).byteLength;
407
+ }
408
+ function parseSource(source, policy, format) {
409
+ if (!validPolicy(policy)) return validateLayerValue(null, policy);
410
+ const diagnostics = new Diagnostics(policy.limits.maxDiagnostics);
411
+ if (byteLength(source) > policy.limits.maxBytes) {
412
+ diagnostics.add("source.byte-limit");
413
+ return failure(diagnostics);
414
+ }
415
+ try {
416
+ if (format === "json") return validateLayerValue(JSON.parse(source), policy);
417
+ const document = yaml.parseDocument(source, { prettyErrors: false, strict: true, uniqueKeys: true });
418
+ if (document.errors.length > 0) {
419
+ diagnostics.add("source.invalid-yaml");
420
+ return failure(diagnostics);
421
+ }
422
+ return validateLayerValue(document.toJS({ maxAliasCount: 0 }), policy);
423
+ } catch {
424
+ diagnostics.add(format === "json" ? "source.invalid-json" : "source.invalid-yaml");
425
+ return failure(diagnostics);
426
+ }
427
+ }
428
+ function parseJsonLayer(source, policy) {
429
+ return parseSource(source, policy, "json");
430
+ }
431
+ function parseYamlLayer(source, policy) {
432
+ return parseSource(source, policy, "yaml");
433
+ }
434
+
435
+ // src/adapter.ts
436
+ function diagnostic(code, message) {
437
+ return Object.freeze({ code, message });
438
+ }
439
+ function diagnosticLimit(policy) {
440
+ const candidate = policy?.limits?.maxDiagnostics;
441
+ return typeof candidate === "number" && Number.isSafeInteger(candidate) && candidate > 0 ? candidate : 1;
442
+ }
443
+ function frozenPolicy(policy) {
444
+ const capabilities = /* @__PURE__ */ Object.create(null);
445
+ for (const [kind, values] of Object.entries(policy.allowedCapabilities)) {
446
+ capabilities[kind] = Object.freeze([...values]);
447
+ }
448
+ const limits = Object.freeze({ ...policy.limits });
449
+ return Object.freeze({
450
+ allowedKinds: Object.freeze([...policy.allowedKinds]),
451
+ allowedNamespaces: Object.freeze([...policy.allowedNamespaces]),
452
+ allowedTransformOps: Object.freeze([...policy.allowedTransformOps]),
453
+ maxTransformScope: policy.maxTransformScope,
454
+ allowedCapabilities: Object.freeze(capabilities),
455
+ limits
456
+ });
457
+ }
458
+ function copyDiagnostic(value) {
459
+ if (value !== null && typeof value === "object" && typeof value.code === "string" && typeof value.message === "string") {
460
+ return diagnostic(
461
+ value.code,
462
+ value.message
463
+ );
464
+ }
465
+ return diagnostic(
466
+ "source.invalid-adapter-diagnostic",
467
+ "The source adapter returned an invalid diagnostic."
468
+ );
469
+ }
470
+ function adaptRawConfig(input, adapter, policy) {
471
+ const policyValidation = validateLayerValue(null, policy);
472
+ if (!policyValidation.ok && policyValidation.diagnostics.some((entry) => entry.code === "source.invalid-policy")) {
473
+ return policyValidation;
474
+ }
475
+ const ownedPolicy = frozenPolicy(policy);
476
+ const diagnostics = [];
477
+ const maxDiagnostics = diagnosticLimit(ownedPolicy);
478
+ let limited = false;
479
+ const add = (value) => {
480
+ if (limited) return;
481
+ if (diagnostics.length + 1 < maxDiagnostics) {
482
+ diagnostics.push(value);
483
+ return;
484
+ }
485
+ limited = true;
486
+ diagnostics.push(
487
+ diagnostic("source.diagnostic-limit", "Additional source diagnostics were omitted.")
488
+ );
489
+ };
490
+ const context = Object.freeze({
491
+ policy: ownedPolicy,
492
+ limits: ownedPolicy.limits,
493
+ diagnose(code, message) {
494
+ add(copyDiagnostic({ code, message }));
495
+ }
496
+ });
497
+ const result = adapter.adapt(input, context);
498
+ if (result === null || typeof result !== "object") {
499
+ add(
500
+ diagnostic("source.invalid-adapter-result", "The source adapter returned an invalid result.")
501
+ );
502
+ } else if (result.diagnostics !== void 0) {
503
+ if (!Array.isArray(result.diagnostics)) {
504
+ add(
505
+ diagnostic(
506
+ "source.invalid-adapter-result",
507
+ "The source adapter returned an invalid result."
508
+ )
509
+ );
510
+ } else {
511
+ for (const entry of result.diagnostics) add(copyDiagnostic(entry));
512
+ }
513
+ }
514
+ if (diagnostics.length > 0) {
515
+ return { ok: false, diagnostics: Object.freeze(diagnostics) };
516
+ }
517
+ if (result === null || typeof result !== "object" || result.layer === void 0) {
518
+ return {
519
+ ok: false,
520
+ diagnostics: Object.freeze([
521
+ diagnostic(
522
+ "source.invalid-adapter-result",
523
+ "The source adapter did not return a declaration layer."
524
+ )
525
+ ])
526
+ };
527
+ }
528
+ return validateLayerValue(result.layer, ownedPolicy);
529
+ }
530
+ var parseResultAdapter = Object.freeze({
531
+ adapt(input) {
532
+ return input.ok ? { layer: input.value } : { diagnostics: input.diagnostics };
533
+ }
534
+ });
535
+
536
+ // src/runtime-source.ts
537
+ function adaptationFailure(diagnostics) {
538
+ return Object.freeze({
539
+ status: "rejected",
540
+ diagnostics: Object.freeze(
541
+ diagnostics.map((entry) => Object.freeze({ code: entry.code, message: entry.message }))
542
+ )
543
+ });
544
+ }
545
+ function createConfigRuntimeSource(id, fetchRaw, adapter, policy) {
546
+ return Object.freeze({
547
+ id,
548
+ async load(context) {
549
+ const raw = await fetchRaw(context);
550
+ const adapted = adaptRawConfig(raw, adapter, policy);
551
+ if (!adapted.ok) return adaptationFailure(adapted.diagnostics);
552
+ return adapted.value;
553
+ }
554
+ });
555
+ }
556
+
557
+ exports.adaptRawConfig = adaptRawConfig;
558
+ exports.createConfigRuntimeSource = createConfigRuntimeSource;
559
+ exports.parseJsonLayer = parseJsonLayer;
560
+ exports.parseResultAdapter = parseResultAdapter;
561
+ exports.parseYamlLayer = parseYamlLayer;
562
+ //# sourceMappingURL=index.cjs.map
563
+ //# sourceMappingURL=index.cjs.map