@burojs/config 0.4.0 → 0.5.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/README.md CHANGED
@@ -3,10 +3,13 @@
3
3
  Inert JSON and YAML declaration sources for [buro](https://github.com/buro/buro).
4
4
 
5
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.
6
+ produces a portable declaration layer that carries the policy's grants — or a
7
+ list of diagnostics if the source is malformed, oversized, or requests
8
+ something its policy doesn't allow. *Source* here is a configuration source — a
9
+ JSON or YAML document a host loads into a layer runtime — not a screen's `with`
10
+ source. The output can only ever be data: no
11
+ functions, components, schemas, credentials, or provider/action/mapper bindings
12
+ survive the parse.
10
13
 
11
14
  ## Install
12
15
 
@@ -20,12 +23,24 @@ pnpm add @burojs/config
20
23
  import { parseJsonLayer, type SourcePolicy } from '@burojs/config';
21
24
 
22
25
  const policy: SourcePolicy = {
23
- allowedKinds: ['resource'],
24
- allowedNamespaces: ['crm'],
25
- allowedTransformOps: [],
26
- maxTransformScope: 'layer',
26
+ grants: {
27
+ allowedKinds: ['resource'],
28
+ allowedNamespaces: ['crm'],
29
+ allowedTransformOps: [],
30
+ maxTransformScope: 'layer',
31
+ writePaths: {},
32
+ allowedResources: [],
33
+ allowedQueries: [],
34
+ allowedFlows: [],
35
+ allowedBackends: ['company'],
36
+ allowedBlocks: [],
37
+ allowedFrames: [],
38
+ allowedInlineFlows: [],
39
+ allowedFieldImplementations: [],
40
+ priority: { min: 0, max: 0 },
41
+ },
27
42
  allowedCapabilities: {},
28
- limits: { maxBytes: 16_000, maxDeclarations: 12, maxDepth: 16, maxStringLength: 1_000, maxDiagnostics: 8 },
43
+ limits: { maxBytes: 16_000, maxDeclarations: 12, maxDepth: 16, maxNodes: 1_000, maxStringLength: 1_000, maxDiagnostics: 8 },
29
44
  };
30
45
 
31
46
  const source = JSON.stringify({
@@ -34,25 +49,45 @@ const source = JSON.stringify({
34
49
  defaultNamespace: 'crm',
35
50
  revision: 'r1',
36
51
  declarations: [
37
- { kind: 'resource', ref: 'company', schemaVersion: 1, value: { name: 'company' } },
52
+ { kind: 'resource', ref: 'company', schemaVersion: 2, value: { name: 'company' } },
38
53
  ],
39
54
  });
40
55
 
41
56
  const result = parseJsonLayer(source, policy);
42
57
  if (!result.ok) throw new Error(result.diagnostics.map((d) => d.code).join(', '));
43
- // result.value is a LayerInput, ready for runtime.publishLayer(...)
58
+ // result.value is a ParsedLayer carrying the grants of `policy`:
59
+ await runtime.publishLayer(result.value);
44
60
  ```
45
61
 
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`.
62
+ To load a source lazily instead of publishing it up front, give
63
+ `createConfigRuntimeSource` the policy and a `load` that returns the document's
64
+ text; it parses each load under the policy, and returns the runtime source with
65
+ the trust to register it under:
66
+
67
+ ```ts
68
+ const configured = createConfigRuntimeSource('catalog', {
69
+ policy,
70
+ load: async () => ({ format: 'json', text: await fetch('/config/catalog.json').then((r) => r.text()) }),
71
+ });
72
+ runtime.registerSource(configured.source, configured.trust);
73
+ ```
74
+
75
+ The parser checks keys (it refuses `action`, `component`, `validator` and the
76
+ other code-bearing keys), the trusted-attachment capabilities in
77
+ `allowedCapabilities`, and the `limits`; those checks are parser-only. The
78
+ compiler enforces the `grants` — the kinds, namespaces and transform operations
79
+ a layer uses, the paths a transform may write (`writePaths`, no default), its
80
+ targets, its references (`allowedResources`, `allowedQueries`,
81
+ `allowedFlows`), its backend bindings (`allowedBackends`) and its priority
82
+ band. Use a band strictly away from your code's priorities to fix whether a
83
+ parsed layer applies before or after your code. See the "Grants and trust"
84
+ section of the guide linked below.
49
85
 
50
86
  ## Main exports
51
87
 
52
88
  - **`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.
89
+ - **`createConfigRuntimeSource(id, { policy, load })`** — returns `{ source, trust }`: a core `RuntimeSource` that loads and parses a document on demand, and the policy's grants to register it with.
90
+ - **`SourcePolicy` / `SourceLimits`** — `{ grants, allowedCapabilities, limits }`, what a host allows one untrusted source: `grants` (the core `LayerGrants`) is what its parsed layers carry; `SCREEN_SOURCE_LIMITS` is a preset for sources that carry whole screen trees.
56
91
 
57
92
  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
93
 
package/dist/index.cjs CHANGED
@@ -1,31 +1,19 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ let _burojs_core = require("@burojs/core");
3
+ let _burojs_core_internal = require("@burojs/core/internal");
2
4
  let yaml = require("yaml");
5
+ //#region src/source-policy.ts
6
+ const MAX_SOURCE_DEPTH = _burojs_core.DEFAULT_COMPILER_LIMITS.maxPortableDepth;
7
+ const SCREEN_SOURCE_LIMITS = Object.freeze({
8
+ maxBytes: 256e3,
9
+ maxDeclarations: 64,
10
+ maxDepth: MAX_SOURCE_DEPTH,
11
+ maxNodes: 5e3,
12
+ maxStringLength: 4096,
13
+ maxDiagnostics: 32
14
+ });
15
+ //#endregion
3
16
  //#region src/parse.ts
4
- const RESERVED_KEYS = new Set([
5
- "__proto__",
6
- "prototype",
7
- "constructor"
8
- ]);
9
- const FORBIDDEN_VALUE_CATEGORIES = new Map([
10
- ["action", "action"],
11
- ["actions", "action"],
12
- ["provider", "provider"],
13
- ["providers", "provider"],
14
- ["mapper", "mapper"],
15
- ["mappers", "mapper"],
16
- ["service", "service"],
17
- ["services", "service"],
18
- ["credential", "credential"],
19
- ["credentials", "credential"],
20
- ["schema", "schema"],
21
- ["schemas", "schema"],
22
- ["component", "component"],
23
- ["components", "component"],
24
- ["attachment", "attachment"],
25
- ["attachments", "attachment"],
26
- ["validator", "validator"],
27
- ["validators", "validator"]
28
- ]);
29
17
  const LAYER_KEYS = new Set([
30
18
  "id",
31
19
  "priority",
@@ -96,6 +84,7 @@ function messageFor(code) {
96
84
  case "source.byte-limit": return "The source exceeds its byte limit.";
97
85
  case "source.declaration-limit": return "The source exceeds its declaration limit.";
98
86
  case "source.depth-limit": return "The source exceeds its nesting-depth limit.";
87
+ case "source.node-limit": return "The source exceeds its value-count limit.";
99
88
  case "source.string-limit": return "The source contains an overlong string.";
100
89
  case "source.diagnostic-limit": return "Additional source diagnostics were omitted.";
101
90
  case "source.unknown-key": return "The source contains an unknown key.";
@@ -110,7 +99,7 @@ function messageFor(code) {
110
99
  case "source.forbidden-transform-op": return "The source requests a forbidden transform operation.";
111
100
  case "source.forbidden-transform-scope": return "The source requests a forbidden transform scope.";
112
101
  case "source.forbidden-capability": return "The source requests a forbidden capability.";
113
- case "source.forbidden-action": return "The source requests a forbidden action binding.";
102
+ case "source.forbidden-action": return "The source requests a forbidden action binding; a resource's flows are written under toolbar.";
114
103
  case "source.forbidden-provider": return "The source requests a forbidden provider binding.";
115
104
  case "source.forbidden-mapper": return "The source requests a forbidden mapper binding.";
116
105
  case "source.forbidden-service": return "The source requests a forbidden service value.";
@@ -122,14 +111,68 @@ function messageFor(code) {
122
111
  default: return "The source is invalid.";
123
112
  }
124
113
  }
114
+ const LIMIT_KEYS = [
115
+ "maxBytes",
116
+ "maxDeclarations",
117
+ "maxDepth",
118
+ "maxNodes",
119
+ "maxStringLength",
120
+ "maxDiagnostics"
121
+ ];
122
+ const LIMIT_KEY_SET = new Set(LIMIT_KEYS);
125
123
  function validLimits(limits) {
126
- return Object.values(limits).every((value) => Number.isSafeInteger(value) && value > 0);
124
+ return plainObject(limits) && Object.keys(limits).every((key) => LIMIT_KEY_SET.has(key)) && LIMIT_KEYS.every((key) => Number.isSafeInteger(limits[key]) && limits[key] > 0);
127
125
  }
128
126
  function validCapabilities(value) {
129
127
  return value !== null && typeof value === "object" && !Array.isArray(value) && Object.values(value).every((capabilities) => Array.isArray(capabilities) && capabilities.every((capability) => typeof capability === "string"));
130
128
  }
131
- function validPolicy(policy) {
132
- 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);
129
+ function grantsError(policy) {
130
+ try {
131
+ (0, _burojs_core.normalizeLayerGrants)(policy.grants);
132
+ return;
133
+ } catch (error) {
134
+ return error instanceof TypeError ? error.message : "";
135
+ }
136
+ }
137
+ function policyDiagnostic(policy) {
138
+ const generic = messageFor("source.invalid-policy");
139
+ const stray = strayPolicyKey(policy);
140
+ if (stray !== void 0) return Object.freeze({
141
+ code: "source.invalid-policy",
142
+ message: `The source policy is invalid: it carries ${stray} at the top level; a policy is { grants, allowedCapabilities, limits }, with every grant field inside grants.`
143
+ });
144
+ if (!validPolicyShape(policy)) return Object.freeze({
145
+ code: "source.invalid-policy",
146
+ message: generic
147
+ });
148
+ const reason = grantsError(policy);
149
+ if (reason === void 0) return void 0;
150
+ return Object.freeze({
151
+ code: "source.invalid-policy",
152
+ message: reason === "" ? generic : `The source policy is invalid: ${reason}`
153
+ });
154
+ }
155
+ const POLICY_KEYS = new Set([
156
+ "grants",
157
+ "allowedCapabilities",
158
+ "limits"
159
+ ]);
160
+ function strayPolicyKey(policy) {
161
+ return plainObject(policy) ? Object.keys(policy).find((key) => !POLICY_KEYS.has(key)) : void 0;
162
+ }
163
+ function validPolicyShape(policy) {
164
+ return plainObject(policy) && plainObject(policy.grants) && validCapabilities(policy.allowedCapabilities) && validLimits(policy.limits);
165
+ }
166
+ function ownedPolicy(policy) {
167
+ const invalid = policyDiagnostic(policy);
168
+ if (invalid !== void 0) throw new TypeError(invalid.message);
169
+ const capabilities = Object.create(null);
170
+ for (const [kind, values] of Object.entries(policy.allowedCapabilities)) capabilities[kind] = Object.freeze([...values]);
171
+ return Object.freeze({
172
+ grants: (0, _burojs_core.normalizeLayerGrants)(policy.grants),
173
+ allowedCapabilities: Object.freeze(capabilities),
174
+ limits: Object.freeze({ ...policy.limits })
175
+ });
133
176
  }
134
177
  function failure(diagnostics) {
135
178
  return {
@@ -150,10 +193,37 @@ function hasData(value, key) {
150
193
  const descriptor = Object.getOwnPropertyDescriptor(value, key);
151
194
  return descriptor !== void 0 && "value" in descriptor;
152
195
  }
196
+ const MAX_ENVELOPE_DEPTH = 8;
197
+ function childRole(role, key) {
198
+ switch (role) {
199
+ case "root": return key === "declarations" ? "declarations" : key === "transforms" ? "transforms" : "envelope";
200
+ case "declarations": return "declaration";
201
+ case "transforms": return "transform";
202
+ case "declaration": return key === "value" ? "value" : "envelope";
203
+ case "transform": return key === "operation" ? "operation" : "envelope";
204
+ case "operation": return key === "value" ? "value" : "envelope";
205
+ default: return role;
206
+ }
207
+ }
153
208
  function clonePortable(value, limits, diagnostics) {
154
209
  const ancestors = new WeakSet();
155
- function copy(current, depth) {
156
- if (depth > limits.maxDepth) {
210
+ const maxDepth = Math.min(limits.maxDepth, MAX_SOURCE_DEPTH);
211
+ let nodes = 0;
212
+ let nodesExceeded = false;
213
+ function overNodeBudget(children) {
214
+ if (children <= limits.maxNodes - nodes) return false;
215
+ if (!nodesExceeded) diagnostics.add("source.node-limit");
216
+ nodesExceeded = true;
217
+ return true;
218
+ }
219
+ function copy(current, depth, role) {
220
+ nodes += 1;
221
+ if (nodes > limits.maxNodes) {
222
+ if (!nodesExceeded) diagnostics.add("source.node-limit");
223
+ nodesExceeded = true;
224
+ return null;
225
+ }
226
+ if (depth > (role === "value" ? maxDepth : MAX_ENVELOPE_DEPTH)) {
157
227
  diagnostics.add("source.depth-limit");
158
228
  return null;
159
229
  }
@@ -173,6 +243,12 @@ function clonePortable(value, limits, diagnostics) {
173
243
  ancestors.add(current);
174
244
  if (Array.isArray(current)) {
175
245
  const output = [];
246
+ const length = ownData(current, "length");
247
+ const arrayLength = typeof length === "number" && Number.isSafeInteger(length) && length >= 0 ? length : void 0;
248
+ if (arrayLength !== void 0 && overNodeBudget(arrayLength)) {
249
+ ancestors.delete(current);
250
+ return null;
251
+ }
176
252
  for (const key of Reflect.ownKeys(current)) {
177
253
  if (typeof key !== "string" || key !== "length" && !/^(0|[1-9]\d*)$/u.test(key)) {
178
254
  diagnostics.add("source.invalid-layer");
@@ -181,41 +257,47 @@ function clonePortable(value, limits, diagnostics) {
181
257
  if (key === "length") continue;
182
258
  if (!hasData(current, key)) diagnostics.add("source.accessor");
183
259
  }
184
- const length = ownData(current, "length");
185
- const arrayLength = typeof length === "number" && Number.isSafeInteger(length) && length >= 0 ? length : void 0;
186
260
  if (arrayLength === void 0) diagnostics.add("source.invalid-layer");
187
- else for (let index = 0; index < arrayLength; index += 1) {
261
+ else for (let index = 0; index < arrayLength && !nodesExceeded; index += 1) {
188
262
  if (!hasData(current, String(index))) diagnostics.add("source.accessor");
189
- output.push(copy(ownData(current, String(index)), depth + 1));
263
+ output.push(copy(ownData(current, String(index)), depth + 1, childRole(role, void 0)));
190
264
  }
191
265
  ancestors.delete(current);
192
266
  return Object.freeze(output);
193
267
  }
194
268
  if (!plainObject(current)) diagnostics.add("source.invalid-layer");
195
269
  const output = Object.create(null);
196
- for (const key of Reflect.ownKeys(current)) {
270
+ const keys = Reflect.ownKeys(current);
271
+ if (overNodeBudget(keys.length)) {
272
+ ancestors.delete(current);
273
+ return null;
274
+ }
275
+ for (const key of keys) {
276
+ if (nodesExceeded) break;
197
277
  if (typeof key !== "string") {
198
278
  diagnostics.add("source.invalid-layer");
199
279
  continue;
200
280
  }
201
- if (RESERVED_KEYS.has(key)) diagnostics.add("source.reserved-key");
281
+ if ((0, _burojs_core_internal.isReservedSegment)(key)) diagnostics.add("source.reserved-key");
202
282
  if ([...key].length > limits.maxStringLength) diagnostics.add("source.string-limit");
203
283
  if (!hasData(current, key)) {
204
284
  diagnostics.add("source.accessor");
205
285
  continue;
206
286
  }
207
- output[key] = copy(ownData(current, key), depth + 1);
287
+ const next = childRole(role, key);
288
+ const nextDepth = next === "value" && role !== "value" ? 0 : depth + 1;
289
+ output[key] = copy(ownData(current, key), nextDepth, next);
208
290
  }
209
291
  ancestors.delete(current);
210
292
  return Object.freeze(output);
211
293
  }
212
- return copy(value, 0);
294
+ return copy(value, 0, "root");
213
295
  }
214
296
  function unknownKeys(value, allowed, diagnostics) {
215
297
  for (const key of Object.keys(value)) if (!allowed.has(key)) diagnostics.add("source.unknown-key");
216
298
  }
217
299
  function allowedNamespace(value, policy, diagnostics) {
218
- if (typeof value !== "string" || !policy.allowedNamespaces.includes(value)) {
300
+ if (typeof value !== "string" || !policy.grants.allowedNamespaces.includes(value)) {
219
301
  diagnostics.add("source.forbidden-namespace");
220
302
  return false;
221
303
  }
@@ -244,17 +326,15 @@ function validateValue(value, kind, policy, diagnostics) {
244
326
  if (!plainObject(value)) return;
245
327
  if (value.$buro === ATTACHMENT_TAG) {
246
328
  const capability = ownData(value, "capability");
247
- if (typeof capability !== "string" || !policy.allowedCapabilities[kind]?.includes(capability)) diagnostics.add("source.forbidden-capability");
329
+ const allowed = Object.hasOwn(policy.allowedCapabilities, kind) ? policy.allowedCapabilities[kind] : void 0;
330
+ if (typeof capability !== "string" || !allowed?.includes(capability)) diagnostics.add("source.forbidden-capability");
248
331
  }
249
332
  for (const key of Object.keys(value)) {
250
- const category = FORBIDDEN_VALUE_CATEGORIES.get(key);
333
+ const category = (0, _burojs_core_internal.blacklistedKeyCategory)(key);
251
334
  if (category !== void 0) diagnostics.add(`source.forbidden-${category}`);
252
335
  validateValue(ownData(value, key), kind, policy, diagnostics);
253
336
  }
254
337
  }
255
- function scopeRank(scope) {
256
- return scope === "layer" ? 0 : scope === "namespace" ? 1 : 2;
257
- }
258
338
  function transformOperation(value) {
259
339
  if (!plainObject(value)) return void 0;
260
340
  const operation = ownData(value, "op");
@@ -285,7 +365,7 @@ function validateTransforms(value, defaultNamespace, policy, diagnostics) {
285
365
  unknownKeys(transform, TRANSFORM_KEYS, diagnostics);
286
366
  const scope = ownData(transform, "scope") ?? "layer";
287
367
  if (scope !== "layer" && scope !== "application") diagnostics.add("source.invalid-transform");
288
- else if (scopeRank(scope) > scopeRank(policy.maxTransformScope)) diagnostics.add("source.forbidden-transform-scope");
368
+ else if ((0, _burojs_core_internal.scopeRank)(scope) > (0, _burojs_core_internal.scopeRank)(policy.grants.maxTransformScope)) diagnostics.add("source.forbidden-transform-scope");
289
369
  const select = ownData(transform, "select");
290
370
  if (!plainObject(select)) diagnostics.add("source.invalid-transform");
291
371
  else {
@@ -294,7 +374,7 @@ function validateTransforms(value, defaultNamespace, policy, diagnostics) {
294
374
  if (selectedKinds !== void 0) {
295
375
  if (!Array.isArray(selectedKinds)) diagnostics.add("source.invalid-transform");
296
376
  else for (const kind of selectedKinds) if (typeof kind !== "string") diagnostics.add("source.invalid-transform");
297
- else if (!policy.allowedKinds.includes(kind)) diagnostics.add("source.forbidden-kind");
377
+ else if (!policy.grants.allowedKinds.includes(kind)) diagnostics.add("source.forbidden-kind");
298
378
  }
299
379
  const selectedNamespaces = ownData(select, "namespaces");
300
380
  if (selectedNamespaces !== void 0) {
@@ -309,7 +389,7 @@ function validateTransforms(value, defaultNamespace, policy, diagnostics) {
309
389
  if (typeof (plainObject(operation) ? ownData(operation, "op") : void 0) === "string" && op === void 0) diagnostics.add("source.forbidden-transform-op");
310
390
  else if (op === void 0) diagnostics.add("source.invalid-transform");
311
391
  else {
312
- if (!policy.allowedTransformOps.includes(op)) diagnostics.add("source.forbidden-transform-op");
392
+ if (!policy.grants.allowedTransformOps.includes(op)) diagnostics.add("source.forbidden-transform-op");
313
393
  if (plainObject(operation)) {
314
394
  unknownKeys(operation, OPERATION_KEYS[op], diagnostics);
315
395
  if (hasData(operation, "value")) validateValue(ownData(operation, "value"), "", policy, diagnostics);
@@ -318,12 +398,10 @@ function validateTransforms(value, defaultNamespace, policy, diagnostics) {
318
398
  }
319
399
  }
320
400
  function validateLayerValue(value, policy) {
321
- if (!validPolicy(policy)) return {
401
+ const invalidPolicy = policyDiagnostic(policy);
402
+ if (invalidPolicy !== void 0) return {
322
403
  ok: false,
323
- diagnostics: Object.freeze([Object.freeze({
324
- code: "source.invalid-policy",
325
- message: messageFor("source.invalid-policy")
326
- })])
404
+ diagnostics: Object.freeze([invalidPolicy])
327
405
  };
328
406
  const diagnostics = new Diagnostics(policy.limits.maxDiagnostics);
329
407
  const cloned = clonePortable(value, policy.limits, diagnostics);
@@ -353,7 +431,7 @@ function validateLayerValue(value, policy) {
353
431
  unknownKeys(declaration, DECLARATION_KEYS, diagnostics);
354
432
  const kind = ownData(declaration, "kind");
355
433
  if (typeof kind !== "string") diagnostics.add("source.invalid-declaration");
356
- else if (!policy.allowedKinds.includes(kind)) diagnostics.add("source.forbidden-kind");
434
+ else if (!policy.grants.allowedKinds.includes(kind)) diagnostics.add("source.forbidden-kind");
357
435
  const namespace = namespaceFor(ownData(declaration, "ref"), typeof defaultNamespace === "string" ? defaultNamespace : "default", diagnostics);
358
436
  if (namespace !== void 0) allowedNamespace(namespace, policy, diagnostics);
359
437
  if (!Number.isSafeInteger(ownData(declaration, "schemaVersion")) || !hasData(declaration, "value")) diagnostics.add("source.invalid-declaration");
@@ -363,14 +441,14 @@ function validateLayerValue(value, policy) {
363
441
  if (diagnostics.values.length > 0) return failure(diagnostics);
364
442
  return {
365
443
  ok: true,
366
- value: cloned
444
+ value: (0, _burojs_core_internal.createParsedLayer)(cloned, (0, _burojs_core.normalizeLayerGrants)(policy.grants))
367
445
  };
368
446
  }
369
447
  function byteLength(value) {
370
448
  return new TextEncoder().encode(value).byteLength;
371
449
  }
372
450
  function parseSource(source, policy, format) {
373
- if (!validPolicy(policy)) return validateLayerValue(null, policy);
451
+ if (policyDiagnostic(policy) !== void 0) return validateLayerValue(null, policy);
374
452
  const diagnostics = new Diagnostics(policy.limits.maxDiagnostics);
375
453
  if (byteLength(source) > policy.limits.maxBytes) {
376
454
  diagnostics.add("source.byte-limit");
@@ -400,82 +478,12 @@ function parseYamlLayer(source, policy) {
400
478
  return parseSource(source, policy, "yaml");
401
479
  }
402
480
  //#endregion
403
- //#region src/adapter.ts
404
- function diagnostic(code, message) {
405
- return Object.freeze({
406
- code,
407
- message
408
- });
409
- }
410
- function diagnosticLimit(policy) {
411
- const candidate = policy?.limits?.maxDiagnostics;
412
- return typeof candidate === "number" && Number.isSafeInteger(candidate) && candidate > 0 ? candidate : 1;
413
- }
414
- function frozenPolicy(policy) {
415
- const capabilities = Object.create(null);
416
- for (const [kind, values] of Object.entries(policy.allowedCapabilities)) capabilities[kind] = Object.freeze([...values]);
417
- const limits = Object.freeze({ ...policy.limits });
418
- return Object.freeze({
419
- allowedKinds: Object.freeze([...policy.allowedKinds]),
420
- allowedNamespaces: Object.freeze([...policy.allowedNamespaces]),
421
- allowedTransformOps: Object.freeze([...policy.allowedTransformOps]),
422
- maxTransformScope: policy.maxTransformScope,
423
- allowedCapabilities: Object.freeze(capabilities),
424
- limits
425
- });
426
- }
427
- function copyDiagnostic(value) {
428
- if (value !== null && typeof value === "object" && typeof value.code === "string" && typeof value.message === "string") return diagnostic(value.code, value.message);
429
- return diagnostic("source.invalid-adapter-diagnostic", "The source adapter returned an invalid diagnostic.");
430
- }
431
- function adaptRawConfig(input, adapter, policy) {
432
- const policyValidation = validateLayerValue(null, policy);
433
- if (!policyValidation.ok && policyValidation.diagnostics.some((entry) => entry.code === "source.invalid-policy")) return policyValidation;
434
- const ownedPolicy = frozenPolicy(policy);
435
- const diagnostics = [];
436
- const maxDiagnostics = diagnosticLimit(ownedPolicy);
437
- let limited = false;
438
- const add = (value) => {
439
- if (limited) return;
440
- if (diagnostics.length + 1 < maxDiagnostics) {
441
- diagnostics.push(value);
442
- return;
443
- }
444
- limited = true;
445
- diagnostics.push(diagnostic("source.diagnostic-limit", "Additional source diagnostics were omitted."));
446
- };
447
- const context = Object.freeze({
448
- policy: ownedPolicy,
449
- limits: ownedPolicy.limits,
450
- diagnose(code, message) {
451
- add(copyDiagnostic({
452
- code,
453
- message
454
- }));
455
- }
456
- });
457
- const result = adapter.adapt(input, context);
458
- if (result === null || typeof result !== "object") add(diagnostic("source.invalid-adapter-result", "The source adapter returned an invalid result."));
459
- else if (result.diagnostics !== void 0) {
460
- if (!Array.isArray(result.diagnostics)) add(diagnostic("source.invalid-adapter-result", "The source adapter returned an invalid result."));
461
- else for (const entry of result.diagnostics) add(copyDiagnostic(entry));
462
- }
463
- if (diagnostics.length > 0) return {
464
- ok: false,
465
- diagnostics: Object.freeze(diagnostics)
466
- };
467
- if (result === null || typeof result !== "object" || result.layer === void 0) return {
468
- ok: false,
469
- diagnostics: Object.freeze([diagnostic("source.invalid-adapter-result", "The source adapter did not return a declaration layer.")])
470
- };
471
- return validateLayerValue(result.layer, ownedPolicy);
472
- }
473
- const parseResultAdapter = Object.freeze({ adapt(input) {
474
- return input.ok ? { layer: input.value } : { diagnostics: input.diagnostics };
475
- } });
476
- //#endregion
477
481
  //#region src/runtime-source.ts
478
- function adaptationFailure(diagnostics) {
482
+ const INVALID_FORMAT = Object.freeze({
483
+ code: "source.invalid-format",
484
+ message: "The source format must be json or yaml."
485
+ });
486
+ function rejection(diagnostics) {
479
487
  return Object.freeze({
480
488
  status: "rejected",
481
489
  diagnostics: Object.freeze(diagnostics.map((entry) => Object.freeze({
@@ -484,21 +492,28 @@ function adaptationFailure(diagnostics) {
484
492
  })))
485
493
  });
486
494
  }
487
- function createConfigRuntimeSource(id, fetchRaw, adapter, policy) {
488
- return Object.freeze({
495
+ function createConfigRuntimeSource(id, options) {
496
+ const policy = ownedPolicy(options.policy);
497
+ const { load } = options;
498
+ const source = Object.freeze({
489
499
  id,
490
500
  async load(context) {
491
- const adapted = adaptRawConfig(await fetchRaw(context), adapter, policy);
492
- if (!adapted.ok) return adaptationFailure(adapted.diagnostics);
493
- return adapted.value;
501
+ const raw = await load(context);
502
+ const parsed = raw.format === "json" ? parseJsonLayer(raw.text, policy) : raw.format === "yaml" ? parseYamlLayer(raw.text, policy) : void 0;
503
+ if (parsed === void 0) return rejection([INVALID_FORMAT]);
504
+ return parsed.ok ? parsed.value : rejection(parsed.diagnostics);
494
505
  }
495
506
  });
507
+ return Object.freeze({
508
+ source,
509
+ trust: policy.grants
510
+ });
496
511
  }
497
512
  //#endregion
498
- exports.adaptRawConfig = adaptRawConfig;
513
+ exports.MAX_SOURCE_DEPTH = MAX_SOURCE_DEPTH;
514
+ exports.SCREEN_SOURCE_LIMITS = SCREEN_SOURCE_LIMITS;
499
515
  exports.createConfigRuntimeSource = createConfigRuntimeSource;
500
516
  exports.parseJsonLayer = parseJsonLayer;
501
- exports.parseResultAdapter = parseResultAdapter;
502
517
  exports.parseYamlLayer = parseYamlLayer;
503
518
 
504
519
  //# sourceMappingURL=index.cjs.map