Gesamte Mono-Repo des Baukastens (alle apps/clients, examples/tests und packages)
dh_heyart
vor 7 Std. 7b1747b3214ec3de482f775cede2649c26e836a9
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
import { readFile, access } from "node:fs/promises";
import { join } from "node:path";
import {
    GLOBAL_CUSTOMER_NUMBER,
    type ConfigurationReference,
    type JsonValue,
    type ResolvedConfiguration,
} from "@dh-software/baukasten-types";
 
/** Returns true if the given filesystem path exists. */
async function pathExists(targetPath: string): Promise<boolean> {
    try {
        await access(targetPath);
        return true;
    } catch {
        return false;
    }
}
 
/**
 * Resolves a reference to a concrete file + its location, or null when no file
 * exists. This is the SINGLE source of truth for baukasten path resolution — the
 * permissions package reuses it to classify 14243 public/private access.
 *
 *   Normal customers:    <baseDir>/<customerNumber>/configurations/<key>.json
 *   Global 14243 folder: <baseDir>/14243/public/<key>.json   (preferred)
 *                        <baseDir>/14243/private/<key>.json
 *
 * If a global key exists in BOTH public/ and private/, the public file wins and
 * a warning is logged to the server console.
 */
export async function tryResolveConfiguration(
    baseDir: string,
    reference: ConfigurationReference,
): Promise<ResolvedConfiguration | null> {
    const { customerNumber, key } = reference;
 
    if (customerNumber === GLOBAL_CUSTOMER_NUMBER) {
        const publicPath = join(baseDir, GLOBAL_CUSTOMER_NUMBER, "public", `${key}.json`);
        const privatePath = join(baseDir, GLOBAL_CUSTOMER_NUMBER, "private", `${key}.json`);
        const publicExists = await pathExists(publicPath);
        const privateExists = await pathExists(privatePath);
 
        if (publicExists && privateExists) {
            console.warn(
                `[baukasten] Configuration "${key}" exists in both public/ and private/ of ` +
                `customer ${GLOBAL_CUSTOMER_NUMBER}; using the public file.`,
            );
        }
        if (publicExists) return { absolutePath: publicPath, location: "public" };
        if (privateExists) return { absolutePath: privatePath, location: "private" };
        return null;
    }
 
    const configPath = join(baseDir, customerNumber, "configurations", `${key}.json`);
    if (await pathExists(configPath)) return { absolutePath: configPath, location: "normal" };
    return null;
}
 
/** Resolves a reference to its absolute file path; throws when no file exists. */
export async function resolveConfigurationPath(
    baseDir: string,
    reference: ConfigurationReference,
): Promise<string> {
    const resolved = await tryResolveConfiguration(baseDir, reference);
    if (resolved === null) {
        throw new Error(
            `[baukasten] Configuration "${reference.key}" of customer ` +
            `${reference.customerNumber} was not found.`,
        );
    }
    return resolved.absolutePath;
}
 
/** Reads and parses a single referenced configuration JSON file. */
export async function readConfiguration(
    baseDir: string,
    reference: ConfigurationReference,
): Promise<JsonValue> {
    const configPath = await resolveConfigurationPath(baseDir, reference);
    const raw = await readFile(configPath, "utf-8");
    try {
        return JSON.parse(raw) as JsonValue;
    } catch (cause) {
        throw new Error(
            `[baukasten] Failed to parse configuration at ${configPath}: ` +
            `${(cause as Error).message}`,
        );
    }
}
 
/**
 * Reads and parses an ordered list of references, preserving order so the result
 * can be handed to the existing layout merge as the merge sequence.
 */
export async function readConfigurations(
    baseDir: string,
    references: ConfigurationReference[],
): Promise<JsonValue[]> {
    return Promise.all(references.map((reference) => readConfiguration(baseDir, reference)));
}