Files
community-rule/lib/server/governanceCatalog.ts
T
adilalloandCursor bcbc6f30d9 Import the remade decks on stable ids and recommend one card per job, so a saved draft still loads after a chip is renamed.
The real-group profiles stay as a test, so a change that moves a pick fails.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-11-04 14:22:54 -07:00

139 lines
4.3 KiB
TypeScript

/**
* Public catalog DTOs for built-in governance methods and core values (CR-115).
* Source of truth: `messages/en/create/customRule/*.json` (English v1).
*/
import communicationMessages from "../../messages/en/create/customRule/communication.json";
import conflictManagementMessages from "../../messages/en/create/customRule/conflictManagement.json";
import coreValuesMessages from "../../messages/en/create/customRule/coreValues.json";
import decisionApproachesMessages from "../../messages/en/create/customRule/decisionApproaches.json";
import membershipMessages from "../../messages/en/create/customRule/membership.json";
import type { MethodFacetApiSectionId } from "../create/customRuleFacets";
export type CatalogMethodDto = {
/** Stable id — same as `methods[].id` in messages and `MethodFacet.slug`. */
slug: string;
label: string;
/** Card copy from messages `supportText`. */
description: string;
/** Section-specific modal blocks from messages `sections`. */
sections: Record<string, unknown>;
};
export type CatalogCoreValueDto = {
/** Stable id from `coreValues.json` `values[].id`. */
id: string;
label: string;
meaning: string;
signals: string;
};
export type CatalogSectionId = MethodFacetApiSectionId | "coreValues";
type MethodMessagesSource = {
methods?: unknown;
};
const METHOD_MESSAGES_BY_SECTION: Record<MethodFacetApiSectionId, unknown> = {
communication: communicationMessages,
membership: membershipMessages,
decisionApproaches: decisionApproachesMessages,
conflictManagement: conflictManagementMessages,
};
function readMethodRows(source: unknown): Array<{
id: string;
label: string;
supportText?: string;
sections?: Record<string, unknown>;
}> {
if (!source || typeof source !== "object") return [];
const methods = (source as MethodMessagesSource).methods;
if (!Array.isArray(methods)) return [];
const out: Array<{
id: string;
label: string;
supportText?: string;
sections?: Record<string, unknown>;
}> = [];
for (const raw of methods) {
if (!raw || typeof raw !== "object") continue;
const o = raw as Record<string, unknown>;
if (typeof o.id !== "string" || typeof o.label !== "string") continue;
out.push({
id: o.id,
label: o.label,
supportText:
typeof o.supportText === "string" ? o.supportText : undefined,
sections:
o.sections && typeof o.sections === "object"
? (o.sections as Record<string, unknown>)
: undefined,
});
}
return out;
}
function rowToCatalogMethod(row: {
id: string;
label: string;
supportText?: string;
sections?: Record<string, unknown>;
}): CatalogMethodDto {
return {
slug: row.id,
label: row.label,
description: row.supportText ?? "",
sections: row.sections ?? {},
};
}
/** All built-in methods for a card-deck section, in messages authoring order. */
export function listCatalogMethods(
section: MethodFacetApiSectionId,
): CatalogMethodDto[] {
return readMethodRows(METHOD_MESSAGES_BY_SECTION[section]).map(
rowToCatalogMethod,
);
}
export function getCatalogMethod(
section: MethodFacetApiSectionId,
slug: string,
): CatalogMethodDto | null {
const row = readMethodRows(METHOD_MESSAGES_BY_SECTION[section]).find(
(r) => r.id === slug,
);
return row ? rowToCatalogMethod(row) : null;
}
/** All preset core values, in `coreValues.json` order, keyed by stable id. */
export function listCatalogCoreValues(): CatalogCoreValueDto[] {
const values = (coreValuesMessages as { values?: unknown }).values;
if (!Array.isArray(values)) return [];
const out: CatalogCoreValueDto[] = [];
for (const row of values) {
if (!row || typeof row !== "object") continue;
const o = row as Record<string, unknown>;
if (typeof o.id !== "string" || typeof o.label !== "string") continue;
out.push({
id: o.id,
label: o.label,
meaning: typeof o.meaning === "string" ? o.meaning : "",
signals: typeof o.signals === "string" ? o.signals : "",
});
}
return out;
}
export function getCatalogCoreValue(id: string): CatalogCoreValueDto | null {
return listCatalogCoreValues().find((v) => v.id === id) ?? null;
}
/** Slugs for parity tests — same set as messages `methods[].id`. */
export function catalogMethodSlugsForSection(
section: MethodFacetApiSectionId,
): string[] {
return listCatalogMethods(section).map((m) => m.slug);
}