Revision control
Copy as Markdown
Other Tools
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
import initialSchema from "./policies-schema.json" with { type: "json" };
// The schema shares definitions (url, origin, ...) via $ref. Policies are
// validated one at a time against their own subschema, which would not carry
// the root "definitions", so resolve all internal $refs into a self-contained
// schema once here. The on-disk file stays DRY for other tooling (e.g. the
// enterprise console); the runtime schema is fully inlined.
function refName(ref) {
for (let prefix of ["#/definitions/", "#/$defs/"]) {
if (ref.startsWith(prefix)) {
return ref.slice(prefix.length);
}
}
return null;
}
// `seen` holds the names being resolved on the current path so a self- or
// mutually-recursive definition fails loudly instead of overflowing the stack
// (such a definition cannot be inlined; it would need the validator to resolve
// it lazily). It is not a problem today: no definition references another.
function dereference(node, definitions, seen = new Set()) {
if (Array.isArray(node)) {
return node.map(item => dereference(item, definitions, seen));
}
if (!node || typeof node != "object") {
return node;
}
if (typeof node.$ref == "string") {
let name = refName(node.$ref);
if (name === null || !Object.hasOwn(definitions, name)) {
throw new Error(`Unresolvable schema $ref "${node.$ref}".`);
}
if (seen.has(name)) {
throw new Error(`Cyclic schema $ref "${node.$ref}" cannot be inlined.`);
}
let resolved = dereference(
definitions[name],
definitions,
new Set(seen).add(name)
);
// Preserve any keywords that sit next to the $ref (e.g. description).
let siblings = {};
for (let [key, value] of Object.entries(node)) {
if (key != "$ref") {
siblings[key] = dereference(value, definitions, seen);
}
}
return { ...resolved, ...siblings };
}
let result = {};
for (let [key, value] of Object.entries(node)) {
if (key == "definitions") {
continue;
}
result[key] = dereference(value, definitions, seen);
}
return result;
}
const resolvedSchema = dereference(
initialSchema,
initialSchema.definitions || {}
);
export let schema = resolvedSchema;
export function modifySchemaForTests(customSchema) {
if (customSchema) {
schema = customSchema;
} else {
schema = resolvedSchema;
}
}