UNPKG

@inductionai/framework

Version:

Build-time authorization framework using TypeScript conditional types

113 lines 3.52 kB
/** * 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