🚀 Socket Launch Week Day 5:Introducing Repository Access Permissions and Custom Roles.Learn more
Sign In

@backstage/config-loader

Package Overview
Dependencies
Maintainers
3
Versions
1162
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@backstage/config-loader - npm Package Compare versions

Comparing version
1.10.12
to
1.11.0-next.0
+6
-0
CHANGELOG.md
# @backstage/config-loader
## 1.11.0-next.0
### Minor Changes
- 4a7240b: Configuration schemas declared in TypeScript now resolve and validate imported types instead of treating them as unconstrained values. Invalid imports now cause schema loading to fail.
## 1.10.12

@@ -4,0 +10,0 @@

@@ -92,2 +92,8 @@ import { JSONSchema7 } from 'json-schema';

packagePaths?: string[];
/**
* Whether to exclude schemas from package dependencies.
*
* Defaults to `false`.
*/
excludePackageDependencies?: boolean;
} | {

@@ -94,0 +100,0 @@ serialized: JsonObject;

+167
-60
'use strict';
var fs = require('fs-extra');
var node_os = require('node:os');
var node_crypto = require('node:crypto');
var node_path = require('node:path');
var errors = require('@backstage/errors');

@@ -18,3 +17,3 @@ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }

};
async function collectConfigSchemas(packageNames, packagePaths) {
async function collectConfigSchemas(packageNames, packagePaths, options) {
const schemas = new Array();

@@ -99,7 +98,9 @@ const tsSchemaPaths = new Array();

}
await Promise.all(
depNames.map(
(depName) => processItem({ name: depName, parentPath: pkgPath })
)
);
if (!options?.excludePackageDependencies) {
await Promise.all(
depNames.map(
(depName) => processItem({ name: depName, parentPath: pkgPath })
)
);
}
}

@@ -122,2 +123,57 @@ await Promise.all([

}
function namespaceSchemaDefinitions(schema, namespace) {
const definitions = schema.definitions;
if (!definitions || typeof definitions !== "object" || Array.isArray(definitions) || Object.keys(definitions).length === 0) {
delete schema.definitions;
return schema;
}
const renamedDefinitions = Object.fromEntries(
Object.entries(definitions).map(([name, definition]) => [
`${namespace}-${name}`,
definition
])
);
const renamedRefs = /* @__PURE__ */ new Map();
for (const name of Object.keys(definitions)) {
const renamed = `${namespace}-${name}`;
renamedRefs.set(`#/definitions/${name}`, `#/definitions/${renamed}`);
renamedRefs.set(
`#/definitions/${encodeURIComponent(name)}`,
`#/definitions/${encodeURIComponent(renamed)}`
);
}
function rewriteRefs(value) {
if (Array.isArray(value)) {
value.forEach(rewriteRefs);
return;
}
if (!value || typeof value !== "object") {
return;
}
const object = value;
if (typeof object.$ref === "string") {
object.$ref = renamedRefs.get(object.$ref) ?? object.$ref;
}
Object.values(object).forEach(rewriteRefs);
}
schema.definitions = renamedDefinitions;
rewriteRefs(schema);
return schema;
}
function parseNestedSchemaAnnotation(annotation) {
if (typeof annotation !== "string") {
return void 0;
}
const match = annotation.match(/^\.([\w-]+)\s+([\s\S]+)$/);
if (!match) {
return void 0;
}
const [, key, text] = match;
let value = text.trim();
try {
value = JSON.parse(value);
} catch {
}
return { key, value };
}
async function compileTsSchemas(entries) {

@@ -127,59 +183,110 @@ if (entries.length === 0) {

}
const { getProgramFromFiles, buildGenerator } = require("typescript-json-schema");
const program = getProgramFromFiles(
entries.map(({ path }) => path),
{
incremental: false,
isolatedModules: true,
lib: ["ES5"],
// Skipping most libs speeds processing up a lot, we just need the primitive types anyway
noEmit: true,
noResolve: true,
skipLibCheck: true,
// Skipping lib checks speeds things up
skipDefaultLibCheck: true,
strict: true,
typeRoots: [],
// Do not include any additional types
types: []
}
);
const tsSchemas = entries.map(({ path, packageName }) => {
let value;
try {
const generator = buildGenerator(
program,
// This enables the use of these tags in TSDoc comments
{
required: true,
validationKeywords: ["visibility", "deepVisibility", "deprecated"]
},
[path.split(node_path.sep).join("/")]
// Unix paths are expected for all OSes here
);
value = generator?.getSchemaForSymbol("Config");
const userSymbols = new Set(generator?.getUserSymbols());
userSymbols.delete("Config");
if (userSymbols.size !== 0) {
const names = Array.from(userSymbols).join("', '");
throw new Error(
`Invalid configuration schema in ${path}, additional symbol definitions are not allowed, found '${names}'`
);
const ts = require("typescript");
const {
AnnotatedTypeFormatter,
createFormatter,
createParser,
DEFAULT_CONFIG,
SchemaGenerator
} = require("ts-json-schema-generator");
const currentDir = process.cwd();
const rootNames = entries.map(({ path }) => node_path.resolve(currentDir, path));
const compilerOptions = {
incremental: false,
jsx: ts.JsxEmit.Preserve,
module: ts.ModuleKind.ESNext,
moduleResolution: ts.ModuleResolutionKind.Bundler,
noEmit: true,
noResolve: false,
skipDefaultLibCheck: true,
skipLibCheck: false,
strict: true,
target: ts.ScriptTarget.ES2022,
types: []
};
const program = ts.createProgram(rootNames, compilerOptions);
const diagnostics = [
...program.getOptionsDiagnostics(),
...program.getGlobalDiagnostics(),
...rootNames.flatMap((rootName) => {
const sourceFile = program.getSourceFile(rootName);
return sourceFile ? [
...program.getSyntacticDiagnostics(sourceFile),
...program.getSemanticDiagnostics(sourceFile)
] : [];
})
];
if (diagnostics.length > 0) {
const message = ts.formatDiagnostics(diagnostics, {
getCanonicalFileName: (fileName) => fileName,
getCurrentDirectory: () => currentDir,
getNewLine: () => "\n"
});
throw new Error(`Invalid TypeScript configuration schema:
${message}`);
}
const generatorConfig = {
...DEFAULT_CONFIG,
additionalProperties: true,
expose: "none",
extraTags: ["visibility", "deepVisibility", "deprecated", "items"],
jsDoc: "extended",
skipTypeCheck: true,
topRef: false,
tsProgram: program
};
const parser = createParser(program, generatorConfig);
class NestedAnnotationsFormatter extends AnnotatedTypeFormatter {
getDefinition(type) {
const annotations = type.getAnnotations();
const itemsAnnotation = annotations.items;
const nestedItems = parseNestedSchemaAnnotation(itemsAnnotation);
if (!nestedItems) {
return super.getDefinition(type);
}
const reffedDefs = Object.keys(generator?.ReffedDefinitions ?? {});
if (reffedDefs.length !== 0) {
const lines = reffedDefs.join(`${node_os.EOL} `);
throw new Error(
`Invalid configuration schema in ${path}, the following definitions are not supported:${node_os.EOL}${node_os.EOL} ${lines}`
);
delete annotations.items;
try {
const definition = super.getDefinition(type);
const items = definition.items;
if (items && typeof items === "object" && !Array.isArray(items)) {
Object.assign(items, { [nestedItems.key]: nestedItems.value });
}
return definition;
} finally {
annotations.items = itemsAnnotation;
}
} catch (error) {
const err = errors.toError(error);
if (err.message !== "type Config not found") {
throw err;
}
}
if (!value) {
}
const formatter = createFormatter(
generatorConfig,
(chainFormatter, circularReferenceFormatter) => {
chainFormatter.addTypeFormatter(
new NestedAnnotationsFormatter(circularReferenceFormatter)
);
}
);
const generator = new SchemaGenerator(
program,
parser,
formatter,
generatorConfig
);
const tsSchemas = entries.map(({ path, packageName }, index) => {
const sourceFile = program.getSourceFile(rootNames[index]);
if (!sourceFile) {
throw new Error(`Invalid schema in ${path}, missing Config export`);
}
const configNode = sourceFile.statements.find(
(statement) => (ts.isInterfaceDeclaration(statement) || ts.isTypeAliasDeclaration(statement)) && statement.name.text === "Config"
);
if (!configNode) {
throw new Error(`Invalid schema in ${path}, missing Config export`);
}
const namespace = node_crypto.createHash("sha256").update(packageName).update("\0").update(path.split(node_path.sep).join("/")).digest("hex");
const value = namespaceSchemaDefinitions(
structuredClone(
generator.createSchemaFromNodes([configNode])
),
namespace
);
return { path, value, packageName };

@@ -186,0 +293,0 @@ });

@@ -1,1 +0,1 @@

{"version":3,"file":"collect.cjs.js","sources":["../../src/schema/collect.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport fs from 'fs-extra';\nimport { EOL } from 'node:os';\nimport {\n resolve as resolvePath,\n relative as relativePath,\n dirname,\n sep,\n} from 'node:path';\nimport { ConfigSchemaPackageEntry } from './types';\nimport { JsonObject } from '@backstage/types';\nimport { toError } from '@backstage/errors';\n\ntype Item = {\n name?: string;\n parentPath?: string;\n packagePath?: string;\n};\n\nconst req =\n typeof __non_webpack_require__ === 'undefined'\n ? require\n : __non_webpack_require__;\n\n/**\n * Exported for test mocking. Jest 30's module resolver has issues with\n * nested node_modules, requiring tests to use an alternative resolution strategy.\n * @internal\n */\nexport const internal = {\n resolvePackagePath(name: string, options?: { paths: string[] }): string {\n return req.resolve(name, options);\n },\n};\n\n/**\n * This collects all known config schemas across all dependencies of the app.\n */\nexport async function collectConfigSchemas(\n packageNames: string[],\n packagePaths: string[],\n): Promise<ConfigSchemaPackageEntry[]> {\n const schemas = new Array<ConfigSchemaPackageEntry>();\n const tsSchemaPaths = new Array<{ packageName: string; path: string }>();\n const visitedPackageVersions = new Map<string, Set<string>>(); // pkgName: [versions...]\n\n const currentDir = await fs.realpath(process.cwd());\n\n async function processItem(item: Item) {\n let pkgPath = item.packagePath;\n\n if (pkgPath) {\n const pkgExists = await fs.pathExists(pkgPath);\n if (!pkgExists) {\n return;\n }\n } else if (item.name) {\n const { name, parentPath } = item;\n\n try {\n pkgPath = internal.resolvePackagePath(\n `${name}/package.json`,\n parentPath ? { paths: [parentPath] } : undefined,\n );\n } catch {\n // We can somewhat safely ignore packages that don't export package.json,\n // as they are likely not part of the Backstage ecosystem anyway.\n }\n }\n if (!pkgPath) {\n return;\n }\n\n const pkg = await fs.readJson(pkgPath);\n\n // Ensures that we only process the same version of each package once.\n let versions = visitedPackageVersions.get(pkg.name);\n if (versions?.has(pkg.version)) {\n return;\n }\n if (!versions) {\n versions = new Set();\n visitedPackageVersions.set(pkg.name, versions);\n }\n versions.add(pkg.version);\n\n const depNames = [\n ...Object.keys(pkg.dependencies ?? {}),\n ...Object.keys(pkg.devDependencies ?? {}),\n ...Object.keys(pkg.optionalDependencies ?? {}),\n ...Object.keys(pkg.peerDependencies ?? {}),\n ];\n\n // TODO(Rugvip): Trying this out to avoid having to traverse the full dependency graph,\n // since that's pretty slow. We probably need a better way to determine when\n // we've left the Backstage ecosystem, but this will do for now.\n const hasSchema = 'configSchema' in pkg;\n const hasBackstageDep = depNames.some(_ => _.startsWith('@backstage/'));\n if (!hasSchema && !hasBackstageDep) {\n return;\n }\n if (hasSchema) {\n if (typeof pkg.configSchema === 'string') {\n const isJson = pkg.configSchema.endsWith('.json');\n const isDts = pkg.configSchema.endsWith('.d.ts');\n if (!isJson && !isDts) {\n throw new Error(\n `Config schema files must be .json or .d.ts, got ${pkg.configSchema}`,\n );\n }\n if (isDts) {\n tsSchemaPaths.push({\n path: relativePath(\n currentDir,\n resolvePath(dirname(pkgPath), pkg.configSchema),\n ),\n packageName: pkg.name,\n });\n } else {\n const path = resolvePath(dirname(pkgPath), pkg.configSchema);\n const value = await fs.readJson(path);\n schemas.push({\n packageName: pkg.name,\n value,\n path: relativePath(currentDir, path),\n });\n }\n } else {\n schemas.push({\n packageName: pkg.name,\n value: pkg.configSchema,\n path: relativePath(currentDir, pkgPath),\n });\n }\n }\n\n await Promise.all(\n depNames.map(depName =>\n processItem({ name: depName, parentPath: pkgPath }),\n ),\n );\n }\n\n await Promise.all([\n ...packageNames.map(name => processItem({ name, parentPath: currentDir })),\n ...packagePaths.map(path => processItem({ name: path, packagePath: path })),\n ]);\n\n const tsSchemas = await compileTsSchemas(tsSchemaPaths);\n const allSchemas = schemas.concat(tsSchemas);\n\n const hasBackendDefaults = allSchemas.some(\n ({ packageName }) => packageName === '@backstage/backend-defaults',\n );\n\n if (hasBackendDefaults) {\n // We filter out backend-common schemas here to avoid issues with\n // schema merging over different versions of the same schema.\n // led to issues such as https://github.com/backstage/backstage/issues/28170\n return allSchemas.filter(\n ({ packageName }) => packageName !== '@backstage/backend-common',\n );\n }\n\n return allSchemas;\n}\n\n// This handles the support of TypeScript .d.ts config schema declarations.\n// We collect all typescript schema definition and compile them all in one go.\n// This is much faster than compiling them separately.\nasync function compileTsSchemas(\n entries: { path: string; packageName: string }[],\n) {\n if (entries.length === 0) {\n return [];\n }\n\n // Lazy loaded, because this brings up all of TypeScript and we don't\n // want that eagerly loaded in tests\n const { getProgramFromFiles, buildGenerator } =\n require('typescript-json-schema') as typeof import('typescript-json-schema');\n\n const program = getProgramFromFiles(\n entries.map(({ path }) => path),\n {\n incremental: false,\n isolatedModules: true,\n lib: ['ES5'], // Skipping most libs speeds processing up a lot, we just need the primitive types anyway\n noEmit: true,\n noResolve: true,\n skipLibCheck: true, // Skipping lib checks speeds things up\n skipDefaultLibCheck: true,\n strict: true,\n typeRoots: [], // Do not include any additional types\n types: [],\n },\n );\n\n const tsSchemas = entries.map(({ path, packageName }) => {\n let value;\n try {\n const generator = buildGenerator(\n program,\n // This enables the use of these tags in TSDoc comments\n {\n required: true,\n validationKeywords: ['visibility', 'deepVisibility', 'deprecated'],\n },\n [path.split(sep).join('/')], // Unix paths are expected for all OSes here\n );\n\n // All schemas should export a `Config` symbol\n value = generator?.getSchemaForSymbol('Config') as JsonObject | null;\n\n // This makes sure that no additional symbols are defined in the schema. We don't allow\n // this because they share a global namespace and will be merged together, leading to\n // unpredictable behavior.\n const userSymbols = new Set(generator?.getUserSymbols());\n userSymbols.delete('Config');\n if (userSymbols.size !== 0) {\n const names = Array.from(userSymbols).join(\"', '\");\n throw new Error(\n `Invalid configuration schema in ${path}, additional symbol definitions are not allowed, found '${names}'`,\n );\n }\n\n // This makes sure that no unsupported types are used in the schema, for example `Record<,>`.\n // The generator will extract these as a schema reference, which will in turn be broken for our usage.\n const reffedDefs = Object.keys(generator?.ReffedDefinitions ?? {});\n if (reffedDefs.length !== 0) {\n const lines = reffedDefs.join(`${EOL} `);\n throw new Error(\n `Invalid configuration schema in ${path}, the following definitions are not supported:${EOL}${EOL} ${lines}`,\n );\n }\n } catch (error) {\n const err = toError(error);\n if (err.message !== 'type Config not found') {\n throw err;\n }\n }\n\n if (!value) {\n throw new Error(`Invalid schema in ${path}, missing Config export`);\n }\n return { path, value, packageName };\n });\n\n return tsSchemas;\n}\n"],"names":["fs","relativePath","resolvePath","dirname","sep","EOL","toError"],"mappings":"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"}
{"version":3,"file":"collect.cjs.js","sources":["../../src/schema/collect.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport fs from 'fs-extra';\nimport type * as TsJsonSchemaGenerator from 'ts-json-schema-generator';\nimport type * as TypeScript from 'typescript';\nimport { createHash } from 'node:crypto';\nimport {\n resolve as resolvePath,\n relative as relativePath,\n dirname,\n sep,\n} from 'node:path';\nimport { ConfigSchemaPackageEntry } from './types';\nimport type { JsonObject } from '@backstage/types';\n\ntype Item = {\n name?: string;\n parentPath?: string;\n packagePath?: string;\n};\n\nconst req =\n typeof __non_webpack_require__ === 'undefined'\n ? require\n : __non_webpack_require__;\n\n/**\n * Exported for test mocking. Jest 30's module resolver has issues with\n * nested node_modules, requiring tests to use an alternative resolution strategy.\n * @internal\n */\nexport const internal = {\n resolvePackagePath(name: string, options?: { paths: string[] }): string {\n return req.resolve(name, options);\n },\n};\n\n/**\n * This collects all known config schemas across all dependencies of the app.\n */\nexport async function collectConfigSchemas(\n packageNames: string[],\n packagePaths: string[],\n options?: { excludePackageDependencies?: boolean },\n): Promise<ConfigSchemaPackageEntry[]> {\n const schemas = new Array<ConfigSchemaPackageEntry>();\n const tsSchemaPaths = new Array<{ packageName: string; path: string }>();\n const visitedPackageVersions = new Map<string, Set<string>>(); // pkgName: [versions...]\n\n const currentDir = await fs.realpath(process.cwd());\n\n async function processItem(item: Item) {\n let pkgPath = item.packagePath;\n\n if (pkgPath) {\n const pkgExists = await fs.pathExists(pkgPath);\n if (!pkgExists) {\n return;\n }\n } else if (item.name) {\n const { name, parentPath } = item;\n\n try {\n pkgPath = internal.resolvePackagePath(\n `${name}/package.json`,\n parentPath ? { paths: [parentPath] } : undefined,\n );\n } catch {\n // We can somewhat safely ignore packages that don't export package.json,\n // as they are likely not part of the Backstage ecosystem anyway.\n }\n }\n if (!pkgPath) {\n return;\n }\n\n const pkg = await fs.readJson(pkgPath);\n\n // Ensures that we only process the same version of each package once.\n let versions = visitedPackageVersions.get(pkg.name);\n if (versions?.has(pkg.version)) {\n return;\n }\n if (!versions) {\n versions = new Set();\n visitedPackageVersions.set(pkg.name, versions);\n }\n versions.add(pkg.version);\n\n const depNames = [\n ...Object.keys(pkg.dependencies ?? {}),\n ...Object.keys(pkg.devDependencies ?? {}),\n ...Object.keys(pkg.optionalDependencies ?? {}),\n ...Object.keys(pkg.peerDependencies ?? {}),\n ];\n\n // TODO(Rugvip): Trying this out to avoid having to traverse the full dependency graph,\n // since that's pretty slow. We probably need a better way to determine when\n // we've left the Backstage ecosystem, but this will do for now.\n const hasSchema = 'configSchema' in pkg;\n const hasBackstageDep = depNames.some(_ => _.startsWith('@backstage/'));\n if (!hasSchema && !hasBackstageDep) {\n return;\n }\n if (hasSchema) {\n if (typeof pkg.configSchema === 'string') {\n const isJson = pkg.configSchema.endsWith('.json');\n const isDts = pkg.configSchema.endsWith('.d.ts');\n if (!isJson && !isDts) {\n throw new Error(\n `Config schema files must be .json or .d.ts, got ${pkg.configSchema}`,\n );\n }\n if (isDts) {\n tsSchemaPaths.push({\n path: relativePath(\n currentDir,\n resolvePath(dirname(pkgPath), pkg.configSchema),\n ),\n packageName: pkg.name,\n });\n } else {\n const path = resolvePath(dirname(pkgPath), pkg.configSchema);\n const value = await fs.readJson(path);\n schemas.push({\n packageName: pkg.name,\n value,\n path: relativePath(currentDir, path),\n });\n }\n } else {\n schemas.push({\n packageName: pkg.name,\n value: pkg.configSchema,\n path: relativePath(currentDir, pkgPath),\n });\n }\n }\n\n if (!options?.excludePackageDependencies) {\n await Promise.all(\n depNames.map(depName =>\n processItem({ name: depName, parentPath: pkgPath }),\n ),\n );\n }\n }\n\n await Promise.all([\n ...packageNames.map(name => processItem({ name, parentPath: currentDir })),\n ...packagePaths.map(path => processItem({ name: path, packagePath: path })),\n ]);\n\n const tsSchemas = await compileTsSchemas(tsSchemaPaths);\n const allSchemas = schemas.concat(tsSchemas);\n\n const hasBackendDefaults = allSchemas.some(\n ({ packageName }) => packageName === '@backstage/backend-defaults',\n );\n\n if (hasBackendDefaults) {\n // We filter out backend-common schemas here to avoid issues with\n // schema merging over different versions of the same schema.\n // led to issues such as https://github.com/backstage/backstage/issues/28170\n return allSchemas.filter(\n ({ packageName }) => packageName !== '@backstage/backend-common',\n );\n }\n\n return allSchemas;\n}\n\nfunction namespaceSchemaDefinitions(\n schema: JsonObject,\n namespace: string,\n): JsonObject {\n const definitions = schema.definitions;\n if (\n !definitions ||\n typeof definitions !== 'object' ||\n Array.isArray(definitions) ||\n Object.keys(definitions).length === 0\n ) {\n delete schema.definitions;\n return schema;\n }\n\n const renamedDefinitions = Object.fromEntries(\n Object.entries(definitions).map(([name, definition]) => [\n `${namespace}-${name}`,\n definition,\n ]),\n );\n const renamedRefs = new Map<string, string>();\n for (const name of Object.keys(definitions)) {\n const renamed = `${namespace}-${name}`;\n renamedRefs.set(`#/definitions/${name}`, `#/definitions/${renamed}`);\n renamedRefs.set(\n `#/definitions/${encodeURIComponent(name)}`,\n `#/definitions/${encodeURIComponent(renamed)}`,\n );\n }\n\n function rewriteRefs(value: unknown): void {\n if (Array.isArray(value)) {\n value.forEach(rewriteRefs);\n return;\n }\n if (!value || typeof value !== 'object') {\n return;\n }\n\n const object = value as Record<string, unknown>;\n if (typeof object.$ref === 'string') {\n object.$ref = renamedRefs.get(object.$ref) ?? object.$ref;\n }\n Object.values(object).forEach(rewriteRefs);\n }\n\n schema.definitions = renamedDefinitions;\n rewriteRefs(schema);\n return schema;\n}\n\nfunction parseNestedSchemaAnnotation(annotation: unknown) {\n if (typeof annotation !== 'string') {\n return undefined;\n }\n\n const match = annotation.match(/^\\.([\\w-]+)\\s+([\\s\\S]+)$/);\n if (!match) {\n return undefined;\n }\n\n const [, key, text] = match;\n let value: unknown = text.trim();\n try {\n value = JSON.parse(value as string);\n } catch {\n // Plain strings such as visibility values are not JSON encoded.\n }\n return { key, value };\n}\n\n// This handles the support of TypeScript .d.ts config schema declarations.\n// We collect all TypeScript schema definitions and compile them in one shared\n// program, which avoids repeatedly resolving and parsing imported types.\nasync function compileTsSchemas(\n entries: { path: string; packageName: string }[],\n) {\n if (entries.length === 0) {\n return [];\n }\n\n // Lazy loaded, because these bring up all of TypeScript and we don't want\n // that eagerly loaded when collecting JSON schemas.\n const ts: typeof TypeScript = require('typescript');\n const {\n AnnotatedTypeFormatter,\n createFormatter,\n createParser,\n DEFAULT_CONFIG,\n SchemaGenerator,\n }: typeof TsJsonSchemaGenerator = require('ts-json-schema-generator');\n\n const currentDir = process.cwd();\n const rootNames = entries.map(({ path }) => resolvePath(currentDir, path));\n const compilerOptions: TypeScript.CompilerOptions = {\n incremental: false,\n jsx: ts.JsxEmit.Preserve,\n module: ts.ModuleKind.ESNext,\n moduleResolution: ts.ModuleResolutionKind.Bundler,\n noEmit: true,\n noResolve: false,\n skipDefaultLibCheck: true,\n skipLibCheck: false,\n strict: true,\n target: ts.ScriptTarget.ES2022,\n types: [],\n };\n\n const program = ts.createProgram(rootNames, compilerOptions);\n const diagnostics = [\n ...program.getOptionsDiagnostics(),\n ...program.getGlobalDiagnostics(),\n ...rootNames.flatMap(rootName => {\n const sourceFile = program.getSourceFile(rootName);\n return sourceFile\n ? [\n ...program.getSyntacticDiagnostics(sourceFile),\n ...program.getSemanticDiagnostics(sourceFile),\n ]\n : [];\n }),\n ];\n if (diagnostics.length > 0) {\n const message = ts.formatDiagnostics(diagnostics, {\n getCanonicalFileName: fileName => fileName,\n getCurrentDirectory: () => currentDir,\n getNewLine: () => '\\n',\n });\n throw new Error(`Invalid TypeScript configuration schema:\\n${message}`);\n }\n\n const generatorConfig = {\n ...DEFAULT_CONFIG,\n additionalProperties: true,\n expose: 'none' as const,\n extraTags: ['visibility', 'deepVisibility', 'deprecated', 'items'],\n jsDoc: 'extended' as const,\n skipTypeCheck: true,\n topRef: false,\n tsProgram: program,\n };\n const parser = createParser(program, generatorConfig);\n class NestedAnnotationsFormatter extends AnnotatedTypeFormatter {\n override getDefinition(type: TsJsonSchemaGenerator.AnnotatedType) {\n const annotations = type.getAnnotations();\n const itemsAnnotation = annotations.items;\n const nestedItems = parseNestedSchemaAnnotation(itemsAnnotation);\n if (!nestedItems) {\n return super.getDefinition(type);\n }\n\n delete annotations.items;\n try {\n const definition = super.getDefinition(type);\n const items = definition.items;\n if (items && typeof items === 'object' && !Array.isArray(items)) {\n Object.assign(items, { [nestedItems.key]: nestedItems.value });\n }\n return definition;\n } finally {\n annotations.items = itemsAnnotation;\n }\n }\n }\n const formatter = createFormatter(\n generatorConfig,\n (chainFormatter, circularReferenceFormatter) => {\n chainFormatter.addTypeFormatter(\n new NestedAnnotationsFormatter(circularReferenceFormatter),\n );\n },\n );\n const generator = new SchemaGenerator(\n program,\n parser,\n formatter,\n generatorConfig,\n );\n\n const tsSchemas = entries.map(({ path, packageName }, index) => {\n const sourceFile = program.getSourceFile(rootNames[index]);\n if (!sourceFile) {\n throw new Error(`Invalid schema in ${path}, missing Config export`);\n }\n const configNode = sourceFile.statements.find(\n statement =>\n (ts.isInterfaceDeclaration(statement) ||\n ts.isTypeAliasDeclaration(statement)) &&\n statement.name.text === 'Config',\n );\n if (!configNode) {\n throw new Error(`Invalid schema in ${path}, missing Config export`);\n }\n\n const namespace = createHash('sha256')\n .update(packageName)\n .update('\\0')\n .update(path.split(sep).join('/'))\n .digest('hex');\n const value = namespaceSchemaDefinitions(\n structuredClone(\n generator.createSchemaFromNodes([configNode]),\n ) as JsonObject,\n namespace,\n );\n\n return { path, value, packageName };\n });\n\n return tsSchemas;\n}\n"],"names":["fs","relativePath","resolvePath","dirname","createHash","sep"],"mappings":"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"}

@@ -25,3 +25,6 @@ 'use strict';

options.dependencies,
options.packagePaths ?? []
options.packagePaths ?? [],
{
excludePackageDependencies: options.excludePackageDependencies
}
);

@@ -28,0 +31,0 @@ } else {

@@ -1,1 +0,1 @@

{"version":3,"file":"load.cjs.js","sources":["../../src/schema/load.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { AppConfig } from '@backstage/config';\nimport { JsonObject } from '@backstage/types';\nimport { compileConfigSchemas } from './compile';\nimport { collectConfigSchemas } from './collect';\nimport { filterByVisibility, filterErrorsByVisibility } from './filtering';\nimport {\n ValidationError,\n ConfigSchema,\n ConfigSchemaPackageEntry,\n CONFIG_VISIBILITIES,\n} from './types';\nimport { normalizeAjvPath } from './utils';\n\n/**\n * Options that control the loading of configuration schema files in the backend.\n *\n * @public\n */\nexport type LoadConfigSchemaOptions =\n | (\n | {\n dependencies: string[];\n packagePaths?: string[];\n }\n | {\n serialized: JsonObject;\n }\n ) & {\n noUndeclaredProperties?: boolean;\n };\n\nfunction errorsToError(errors: ValidationError[]): Error {\n const messages = errors.map(({ instancePath, message, params }) => {\n const paramStr = Object.entries(params)\n .map(([name, value]) => `${name}=${value}`)\n .join(' ');\n return `Config ${message || ''} { ${paramStr} } at ${normalizeAjvPath(\n instancePath,\n )}`;\n });\n const error = new Error(`Config validation failed, ${messages.join('; ')}`);\n (error as any).messages = messages;\n return error;\n}\n\n/**\n * Loads config schema for a Backstage instance.\n *\n * @public\n */\nexport async function loadConfigSchema(\n options: LoadConfigSchemaOptions,\n): Promise<ConfigSchema> {\n let schemas: ConfigSchemaPackageEntry[];\n\n if ('dependencies' in options) {\n schemas = await collectConfigSchemas(\n options.dependencies,\n options.packagePaths ?? [],\n );\n } else {\n const { serialized } = options;\n if (serialized?.backstageConfigSchemaVersion !== 1) {\n throw new Error(\n 'Serialized configuration schema is invalid or has an invalid version number',\n );\n }\n schemas = serialized.schemas as ConfigSchemaPackageEntry[];\n }\n\n const validate = compileConfigSchemas(schemas, {\n noUndeclaredProperties: options.noUndeclaredProperties,\n });\n\n return {\n process(\n configs: AppConfig[],\n {\n visibility,\n valueTransform,\n withFilteredKeys,\n withDeprecatedKeys,\n ignoreSchemaErrors,\n } = {},\n ): AppConfig[] {\n const result = validate(configs);\n\n if (!ignoreSchemaErrors) {\n const visibleErrors = filterErrorsByVisibility(\n result.errors,\n visibility,\n result.visibilityByDataPath,\n result.visibilityBySchemaPath,\n );\n if (visibleErrors.length > 0) {\n throw errorsToError(visibleErrors);\n }\n }\n\n let processedConfigs = configs;\n\n if (visibility) {\n processedConfigs = processedConfigs.map(({ data, context }) => ({\n context,\n ...filterByVisibility(\n data,\n visibility,\n result.visibilityByDataPath,\n result.deepVisibilityByDataPath,\n result.deprecationByDataPath,\n valueTransform,\n withFilteredKeys,\n withDeprecatedKeys,\n ),\n }));\n } else if (valueTransform) {\n processedConfigs = processedConfigs.map(({ data, context }) => ({\n context,\n ...filterByVisibility(\n data,\n Array.from(CONFIG_VISIBILITIES),\n result.visibilityByDataPath,\n result.deepVisibilityByDataPath,\n result.deprecationByDataPath,\n valueTransform,\n withFilteredKeys,\n withDeprecatedKeys,\n ),\n }));\n }\n\n return processedConfigs;\n },\n serialize(): JsonObject {\n return {\n schemas,\n backstageConfigSchemaVersion: 1,\n };\n },\n };\n}\n"],"names":["normalizeAjvPath","collectConfigSchemas","compileConfigSchemas","filterErrorsByVisibility","filterByVisibility","CONFIG_VISIBILITIES"],"mappings":"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"}
{"version":3,"file":"load.cjs.js","sources":["../../src/schema/load.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { AppConfig } from '@backstage/config';\nimport { JsonObject } from '@backstage/types';\nimport { compileConfigSchemas } from './compile';\nimport { collectConfigSchemas } from './collect';\nimport { filterByVisibility, filterErrorsByVisibility } from './filtering';\nimport {\n ValidationError,\n ConfigSchema,\n ConfigSchemaPackageEntry,\n CONFIG_VISIBILITIES,\n} from './types';\nimport { normalizeAjvPath } from './utils';\n\n/**\n * Options that control the loading of configuration schema files in the backend.\n *\n * @public\n */\nexport type LoadConfigSchemaOptions =\n | (\n | {\n dependencies: string[];\n packagePaths?: string[];\n /**\n * Whether to exclude schemas from package dependencies.\n *\n * Defaults to `false`.\n */\n excludePackageDependencies?: boolean;\n }\n | {\n serialized: JsonObject;\n }\n ) & {\n noUndeclaredProperties?: boolean;\n };\n\nfunction errorsToError(errors: ValidationError[]): Error {\n const messages = errors.map(({ instancePath, message, params }) => {\n const paramStr = Object.entries(params)\n .map(([name, value]) => `${name}=${value}`)\n .join(' ');\n return `Config ${message || ''} { ${paramStr} } at ${normalizeAjvPath(\n instancePath,\n )}`;\n });\n const error = new Error(`Config validation failed, ${messages.join('; ')}`);\n (error as any).messages = messages;\n return error;\n}\n\n/**\n * Loads config schema for a Backstage instance.\n *\n * @public\n */\nexport async function loadConfigSchema(\n options: LoadConfigSchemaOptions,\n): Promise<ConfigSchema> {\n let schemas: ConfigSchemaPackageEntry[];\n\n if ('dependencies' in options) {\n schemas = await collectConfigSchemas(\n options.dependencies,\n options.packagePaths ?? [],\n {\n excludePackageDependencies: options.excludePackageDependencies,\n },\n );\n } else {\n const { serialized } = options;\n if (serialized?.backstageConfigSchemaVersion !== 1) {\n throw new Error(\n 'Serialized configuration schema is invalid or has an invalid version number',\n );\n }\n schemas = serialized.schemas as ConfigSchemaPackageEntry[];\n }\n\n const validate = compileConfigSchemas(schemas, {\n noUndeclaredProperties: options.noUndeclaredProperties,\n });\n\n return {\n process(\n configs: AppConfig[],\n {\n visibility,\n valueTransform,\n withFilteredKeys,\n withDeprecatedKeys,\n ignoreSchemaErrors,\n } = {},\n ): AppConfig[] {\n const result = validate(configs);\n\n if (!ignoreSchemaErrors) {\n const visibleErrors = filterErrorsByVisibility(\n result.errors,\n visibility,\n result.visibilityByDataPath,\n result.visibilityBySchemaPath,\n );\n if (visibleErrors.length > 0) {\n throw errorsToError(visibleErrors);\n }\n }\n\n let processedConfigs = configs;\n\n if (visibility) {\n processedConfigs = processedConfigs.map(({ data, context }) => ({\n context,\n ...filterByVisibility(\n data,\n visibility,\n result.visibilityByDataPath,\n result.deepVisibilityByDataPath,\n result.deprecationByDataPath,\n valueTransform,\n withFilteredKeys,\n withDeprecatedKeys,\n ),\n }));\n } else if (valueTransform) {\n processedConfigs = processedConfigs.map(({ data, context }) => ({\n context,\n ...filterByVisibility(\n data,\n Array.from(CONFIG_VISIBILITIES),\n result.visibilityByDataPath,\n result.deepVisibilityByDataPath,\n result.deprecationByDataPath,\n valueTransform,\n withFilteredKeys,\n withDeprecatedKeys,\n ),\n }));\n }\n\n return processedConfigs;\n },\n serialize(): JsonObject {\n return {\n schemas,\n backstageConfigSchemaVersion: 1,\n };\n },\n };\n}\n"],"names":["normalizeAjvPath","collectConfigSchemas","compileConfigSchemas","filterErrorsByVisibility","filterByVisibility","CONFIG_VISIBILITIES"],"mappings":"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"}
{
"name": "@backstage/config-loader",
"version": "1.10.12",
"version": "1.11.0-next.0",
"description": "Config loading functionality used by Backstage backend, and CLI",

@@ -39,6 +39,6 @@ "backstage": {

"dependencies": {
"@backstage/cli-common": "^0.2.2",
"@backstage/config": "^1.3.8",
"@backstage/errors": "^1.3.1",
"@backstage/types": "^1.2.2",
"@backstage/cli-common": "0.2.2",
"@backstage/config": "1.3.8",
"@backstage/errors": "1.3.1",
"@backstage/types": "1.2.2",
"@types/json-schema": "^7.0.6",

@@ -52,8 +52,9 @@ "ajv": "^8.10.0",

"minimist": "^1.2.5",
"typescript-json-schema": "^0.67.0",
"ts-json-schema-generator": "^2.9.0",
"typescript": "^5.9.3",
"yaml": "^2.0.0"
},
"devDependencies": {
"@backstage/backend-test-utils": "^1.11.4",
"@backstage/cli": "^0.36.3",
"@backstage/backend-test-utils": "1.11.5-next.0",
"@backstage/cli": "0.36.4-next.0",
"@types/json-schema-merge-allof": "^0.6.0",

@@ -60,0 +61,0 @@ "@types/minimist": "^1.2.5",