feat(dev-server): load the authz catalog at boot

Adds loadAppAuthzCatalog(appDir) to @wrnexus/dev-server: discovers
app/authz/*.ts declarations via buildRouter, imports and merges them
into an AuthzCatalog, returning an empty catalog when the app has no
declarations. A declaration with no default export is skipped with a
warning; a genuine conflict between two declarations throws
WRN-AUTHZ-CONFLICT naming both source files.

Declared the missing @wrnexus/authz workspace dependency in
dev-server's package.json.
This commit is contained in:
2026-08-04 22:03:28 +05:30
parent bc5437063d
commit daea59cf5d
3 changed files with 94 additions and 0 deletions
+31
View File
@@ -0,0 +1,31 @@
import { pathToFileURL } from "node:url";
import { buildRouter } from "@wrnexus/router";
import {
emptyCatalog,
mergeCatalogs,
type AuthzCatalog,
type AuthzModule,
type CatalogSource,
} from "@wrnexus/authz";
/**
* Load and merge every `app/authz/*.ts` declaration. Conflicts throw so a
* misconfigured catalog fails the boot rather than silently changing who can
* do what. An app with no `app/authz/` directory gets an empty catalog rather
* than an error, since not every app uses permissions.
*/
export async function loadAppAuthzCatalog(appDir: string): Promise<AuthzCatalog> {
const router = buildRouter(appDir);
if (!router.authz.length) return emptyCatalog();
const sources: CatalogSource[] = [];
for (const entry of router.authz) {
// buildRouter already skips *.gen.ts, so only real declarations arrive here.
const imported = (await import(pathToFileURL(entry.file).href)) as { default?: AuthzModule };
if (!imported.default) {
console.warn(`[wrnexus] authz declaration ${entry.file} has no default export; skipping.`);
continue;
}
sources.push({ source: entry.file, module: imported.default });
}
return mergeCatalogs(sources);
}