Gesamte Mono-Repo des Baukastens (alle apps/clients, examples/tests und packages)
dh_heyart
vor 5 Std. 96438053d50d33b24c946cd290d57356b4a80b73
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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
import { tryResolveConfiguration } from "@dh-software/baukasten-config-reader";
import {
    GLOBAL_CUSTOMER_NUMBER,
    type ConfigurationEntry,
    type CustomerNumber,
    type PermissionOptions,
    type ResolvedPermission,
} from "@dh-software/baukasten-types";
import { listCustomerNumbers, readFileInfo } from "./fileinfo";
 
/**
 * Resolves the permission for a single key of the global 14243 folder by reusing
 * the config-reader's locator (the public/private rule lives there — single
 * source of truth). Returns null and warns when the key is listed in
 * fileinfo.json but no backing file exists.
 */
async function resolveGlobalPermission(
    baseDir: string,
    key: string,
): Promise<ResolvedPermission | null> {
    const resolved = await tryResolveConfiguration(baseDir, {
        customerNumber: GLOBAL_CUSTOMER_NUMBER,
        key,
    });
    if (resolved === null) {
        console.warn(
            `[baukasten] Configuration "${key}" is listed in the fileinfo.json of customer ` +
            `${GLOBAL_CUSTOMER_NUMBER} but no matching file was found in public/ or private/; ` +
            `skipping.`,
        );
        return null;
    }
    // For the global folder, location is always "public" or "private".
    return {
        owner: GLOBAL_CUSTOMER_NUMBER,
        consumers: [],
        blanket: resolved.location === "public" ? "public" : "private",
    };
}
 
/** Builds the catalog entries for one customer folder. */
export async function readCustomerCatalog(
    baseDir: string,
    customerNumber: CustomerNumber,
): Promise<ConfigurationEntry[]> {
    const fileInfo = await readFileInfo(baseDir, customerNumber);
    const entries: ConfigurationEntry[] = [];
 
    for (const [key, info] of Object.entries(fileInfo)) {
        let permission: ResolvedPermission | null;
 
        if (customerNumber === GLOBAL_CUSTOMER_NUMBER) {
            permission = await resolveGlobalPermission(baseDir, key);
            if (permission === null) continue;
        } else {
            const accessPermission = info["access-permission"];
            permission = {
                owner: customerNumber,
                consumers: accessPermission?.consumers ?? [],
                blanket: accessPermission?.["blanket-permission"] ?? "self",
            };
        }
 
        entries.push({
            customerNumber,
            key,
            displayName: info["display-name"],
            permission,
        });
    }
 
    return entries;
}
 
/** Builds the full catalog across all customer folders under `baseDir`. */
export async function readCatalog(baseDir: string): Promise<ConfigurationEntry[]> {
    const customerNumbers = await listCustomerNumbers(baseDir);
    const catalogs = await Promise.all(
        customerNumbers.map((customerNumber) => readCustomerCatalog(baseDir, customerNumber)),
    );
    return catalogs.flat();
}
 
/** Decides whether `requestingCustomerNumber` may select the given configuration. */
export function canSelect(
    entry: ConfigurationEntry,
    requestingCustomerNumber: CustomerNumber,
    options: PermissionOptions = {},
): boolean {
    const { owner, consumers, blanket } = entry.permission;
 
    if (requestingCustomerNumber === owner) return true;
 
    switch (blanket) {
        case "public":
            return true;
        case "private":
        case "self":
            // Beyond the owner (handled above), only explicit consumers.
            return consumers.includes(requestingCustomerNumber);
        case "stores":
            if (consumers.includes(requestingCustomerNumber)) return true;
            return options.isStoreMember?.(owner, requestingCustomerNumber) ?? false;
    }
 
    return false;
}
 
/** Filters a catalog to the entries `requestingCustomerNumber` may select. */
export function selectableFor(
    catalog: ConfigurationEntry[],
    requestingCustomerNumber: CustomerNumber,
    options: PermissionOptions = {},
): ConfigurationEntry[] {
    return catalog.filter((entry) => canSelect(entry, requestingCustomerNumber, options));
}
 
/** Convenience: read the catalog and filter it for one customer in a single call. */
export async function getSelectableConfigurations(
    baseDir: string,
    requestingCustomerNumber: CustomerNumber,
    options: PermissionOptions = {},
): Promise<ConfigurationEntry[]> {
    const catalog = await readCatalog(baseDir);
    return selectableFor(catalog, requestingCustomerNumber, options);
}