@beignet/core
Version:
Core framework primitives for Beignet
66 lines (55 loc) • 1.58 kB
text/typescript
import {
type PathTemplateSegment,
parsePathTemplate,
} from "./path-template.js";
import type { HttpMethod } from "./types.js";
const methodNameMap: Record<HttpMethod, string> = {
GET: "get",
POST: "create",
PUT: "update",
PATCH: "patch",
DELETE: "delete",
HEAD: "head",
OPTIONS: "options",
};
function toPascalCase(value: string): string {
return value
.replace(/([a-z0-9])([A-Z])/g, "$1 $2")
.split(/[^a-zA-Z0-9]+/)
.filter(Boolean)
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
.join("");
}
function getMeaningfulPathSegments(path: string): PathTemplateSegment[] {
const segments = parsePathTemplate(path).segments;
if (
segments[0]?.kind === "static" &&
segments[0].value.toLowerCase() === "api"
) {
return segments.slice(1);
}
return segments;
}
/**
* Generate a stable contract name from an HTTP method and path.
*
* `/api` is ignored as a leading path segment so generated names focus on the
* resource shape.
*/
export function generateContractName(method: HttpMethod, path: string): string {
const methodPrefix = methodNameMap[method];
const segments = getMeaningfulPathSegments(path);
if (segments.length === 0) {
return `${methodPrefix}Root`;
}
const nameSuffix = segments
.map((segment) => {
if (segment.kind === "dynamic") {
const paramName = toPascalCase(segment.name);
return `By${paramName || "Param"}`;
}
return toPascalCase(segment.value) || "Resource";
})
.join("");
return `${methodPrefix}${nameSuffix}`;
}