@inductionai/framework
Version:
Build-time authorization framework using TypeScript conditional types
113 lines • 3.52 kB
JavaScript
/**
* Generic registry implementation for build-time authorization.
* Fully type-safe with complete generics.
*/
/**
* Creates a type-safe authorization registry.
*
* @param config - Configuration with policy map
* @returns Registry functions: register, authorize, and utilities
*
* @example
* const { register, authorize } = createRegistry<
* CallerId,
* CalleeId,
* typeof policy,
* CalleeRegistry
* >({ policy });
*
* // Register implementations
* register('MyService', implementation);
*
* // Authorize access (compile-time checked)
* const service = authorize('AdminUser', 'MyService');
*/
export function createRegistry(config) {
/**
* Runtime storage for callee implementations.
*/
const implementations = {};
/**
* Register a callee implementation.
*/
function register(id, implementation) {
if (implementations[id]) {
throw new Error(`Callee '${String(id)}' is already registered`);
}
implementations[id] = implementation;
}
/**
* Get a callee implementation by ID (internal use only).
*/
function getImpl(id) {
return implementations[id];
}
/**
* Main authorization function.
* Enforces policy at compile-time using conditional types.
*
* @param caller - The ID of the caller requesting access
* @param callee - The ID of the callee being accessed
* @returns The callee's API if authorized, never if forbidden
*/
function authorize(caller, callee) {
const impl = getImpl(callee);
if (!impl) {
const registered = Object.keys(implementations);
throw new Error(`Callee '${String(callee)}' is not registered.\n\n` +
`Did you forget to call wireApplication()?\n` +
`Available deciders: ${registered.length > 0 ? registered.join(', ') : 'none'}\n\n` +
`Tip: Import wiring and call wireApplication() before using authorize().`);
}
// The cast is safe - TypeScript has verified authorization via the return type
return impl;
}
/**
* Clear all registrations (useful for testing).
*/
function clearRegistry() {
for (const key in implementations) {
delete implementations[key];
}
}
/**
* Get all registered callee IDs.
*/
function getRegisteredIds() {
return Object.keys(implementations);
}
/**
* Check if a callee is registered.
*/
function isRegistered(id) {
return id in implementations;
}
/**
* Debug utility to print registry status.
*/
function debugRegistry() {
const registered = getRegisteredIds();
console.log('\n[DEBUG] Registry Status:');
console.log(` Total registered: ${registered.length}`);
console.log(` Registered IDs:`, registered);
const allIds = Object.keys(config.policy).flatMap(caller => config.policy[caller]);
const unique = [...new Set(allIds)];
const missing = unique.filter(id => !isRegistered(id));
if (missing.length > 0) {
console.log(` ⚠️ Missing:`, missing);
}
else {
console.log(` ✅ All expected callees registered`);
}
console.log('');
}
return {
register,
authorize,
clearRegistry,
getRegisteredIds,
isRegistered,
debugRegistry,
};
}
//# sourceMappingURL=registry-core.js.map