feat: add module contracts and discovery
CI / verify (push) Has been cancelled
CI / infrastructure-smoke (push) Has been cancelled

This commit is contained in:
ekklesia
2026-07-12 22:14:01 -05:00
parent d9489384c1
commit 57552a21a4
43 changed files with 2277 additions and 21 deletions
+60
View File
@@ -0,0 +1,60 @@
import { z } from "zod";
const localIdentifierPattern = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u;
const namespacedIdentifierPattern =
/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*(?:\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*)+$/u;
export const moduleIdSchema = z
.string()
.min(1)
.max(64)
.regex(localIdentifierPattern, "Must be a lowercase kebab-case module identifier.");
export const capabilityIdSchema = z
.string()
.min(3)
.max(160)
.regex(namespacedIdentifierPattern, "Must be a lowercase dot-namespaced capability identifier.");
export const permissionIdSchema = z
.string()
.min(3)
.max(160)
.regex(namespacedIdentifierPattern, "Must be a lowercase dot-namespaced permission identifier.");
export const logicalBucketIdSchema = z
.string()
.min(1)
.max(48)
.regex(localIdentifierPattern, "Must be a lowercase kebab-case logical bucket identifier.");
export const packageRelativePathSchema = z.string().superRefine((value, context) => {
if (!value.startsWith("./")) {
context.addIssue({
code: "custom",
message: "Must begin with ./ and remain inside the module package.",
});
return;
}
if (value.includes("\\") || value.includes("\0") || value.includes("?") || value.includes("#")) {
context.addIssue({
code: "custom",
message: "Must be a portable package-relative path without URLs, queries, or fragments.",
});
}
const segments = value.slice(2).split("/");
if (segments.some((segment) => segment === "" || segment === "." || segment === "..")) {
context.addIssue({
code: "custom",
message: "Must not contain empty, current-directory, or parent-directory segments.",
});
}
});
export type ModuleId = z.infer<typeof moduleIdSchema>;
export type CapabilityId = z.infer<typeof capabilityIdSchema>;
export type PermissionId = z.infer<typeof permissionIdSchema>;
export type LogicalBucketId = z.infer<typeof logicalBucketIdSchema>;
export type PackageRelativePath = z.infer<typeof packageRelativePathSchema>;