agent-smith/dist-in/commons/registry.js
2026-02-26 19:41:09 +01:00

165 lines
12 KiB
JavaScript

import { PRODUCT_ACTIONS } from '../config/products.js';
/**
* Central registry for all billable functions
* Manages configuration, costs, and metadata
*/
export class FunctionRegistry {
static registry = new Map();
static initialized = false;
/**
* Initialize the registry with default configurations
*/
static initialize() {
if (this.initialized)
return;
// Load legacy PRODUCT_ACTIONS
for (const [productId, actions] of Object.entries(PRODUCT_ACTIONS)) {
for (const [actionId, config] of Object.entries(actions)) {
this.register({
productId,
actionId,
...config
});
}
}
this.initialized = true;
}
/**
* Register a new billable function
*/
static register(config) {
const key = this.getKey(config.productId, config.actionId);
this.registry.set(key, config);
}
/**
* Get configuration for a specific function
*/
static get(productId, actionId) {
if (!this.initialized)
this.initialize();
const key = this.getKey(productId, actionId);
return this.registry.get(key) || null;
}
/**
* Get all registered functions
*/
static getAll() {
if (!this.initialized)
this.initialize();
return Array.from(this.registry.values());
}
/**
* Find a configuration by matching route endpoint and method
* (Used for middleware backward compatibility)
*/
static findByRoute(path, method) {
if (!this.initialized)
this.initialize();
for (const config of this.registry.values()) {
if (this.matchesRoute(path, config.endpoint) && method === config.method) {
return config;
}
}
return null;
}
static getKey(productId, actionId) {
return `${productId}:${actionId}`;
}
static matchesRoute(path, pattern) {
// Convert pattern to regex
// Handle both :param (Express/Hono style) and {param} (OpenAPI style)
// e.g., '/api/competitors/:place_id' or '/api/competitors/{place_id}' -> /^\/api\/competitors\/[^\/]+$/
const regexPattern = pattern
.replace(/:[^\\/]+/g, '[^/]+') // Replace :param with regex
.replace(/\{[^}]+\}/g, '[^/]+') // Replace {param} with regex
.replace(/\//g, '\\/');
// Allow optional trailing slash
const regex = new RegExp(`^${regexPattern}\\/?$`);
return regex.test(path);
}
}
/**
* Registry for public endpoints that don't require authentication
*/
export class PublicEndpointRegistry {
static registry = new Set();
static register(path, method) {
this.registry.add(`${method.toUpperCase()}:${path}`);
}
static getAll() {
return Array.from(this.registry).map(entry => {
// Split only on the FIRST colon (METHOD:PATH)
// Don't split on colons in path parameters like :identifier
const colonIndex = entry.indexOf(':');
const method = entry.substring(0, colonIndex);
const path = entry.substring(colonIndex + 1);
return { path, method };
});
}
static isPublic(path, method) {
const methodUpper = method.toUpperCase();
for (const registered of this.registry) {
// Split only on the FIRST colon (METHOD:PATH)
// Don't split on colons in path parameters like :identifier
const colonIndex = registered.indexOf(':');
const regMethod = registered.substring(0, colonIndex);
const regPath = registered.substring(colonIndex + 1);
if (regMethod !== methodUpper)
continue;
// Check if path matches pattern
if (this.matchesRoute(path, regPath)) {
return true;
}
}
// Debug: log when a route is not found as public
// console.log(`[PublicEndpointRegistry] Route not found as public: ${methodUpper} ${path}`);
// console.log(`[PublicEndpointRegistry] Registered routes:`, Array.from(this.registry));
return false;
}
static matchesRoute(path, pattern) {
// Convert pattern to regex
// Handle both :param (Express/Hono style) and {param} (OpenAPI style)
// e.g., '/api/competitors/:place_id' or '/api/competitors/{place_id}' -> /^\/api\/competitors\/[^\/]+$/
const regexPattern = pattern
.replace(/:[^\/]+/g, '[^/]+') // Replace :param with regex
.replace(/\{[^}]+\}/g, '[^/]+') // Replace {param} with regex
.replace(/\//g, '\\/');
// Allow optional trailing slash
const regex = new RegExp(`^${regexPattern}\\/?$`);
return regex.test(path);
}
}
/**
* Registry for admin-only endpoints
*/
export class AdminEndpointRegistry {
static registry = new Set();
static register(path, method) {
this.registry.add(`${method.toUpperCase()}:${path}`);
}
static isAdmin(path, method) {
const methodUpper = method.toUpperCase();
for (const registered of this.registry) {
const [regMethod, regPath] = registered.split(':');
if (regMethod !== methodUpper)
continue;
// Check if path matches pattern
if (this.matchesRoute(path, regPath)) {
return true;
}
}
return false;
}
static matchesRoute(path, pattern) {
// Convert pattern to regex
// Handle both :param (Express/Hono style) and {param} (OpenAPI style)
// e.g., '/api/competitors/:place_id' or '/api/competitors/{place_id}' -> /^\/api\/competitors\/[^\/]+$/
const regexPattern = pattern
.replace(/:[^\\/]+/g, '[^/]+') // Replace :param with regex
.replace(/\{[^}]+\}/g, '[^/]+') // Replace {param} with regex
.replace(/\//g, '\\/');
const regex = new RegExp(`^${regexPattern}\\/?$`);
return regex.test(path);
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVnaXN0cnkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvY29tbW9ucy9yZWdpc3RyeS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQXVCLGVBQWUsRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBTzdFOzs7R0FHRztBQUNILE1BQU0sT0FBTyxnQkFBZ0I7SUFDakIsTUFBTSxDQUFDLFFBQVEsR0FBRyxJQUFJLEdBQUcsRUFBa0MsQ0FBQztJQUM1RCxNQUFNLENBQUMsV0FBVyxHQUFHLEtBQUssQ0FBQztJQUVuQzs7T0FFRztJQUNILE1BQU0sQ0FBQyxVQUFVO1FBQ2IsSUFBSSxJQUFJLENBQUMsV0FBVztZQUFFLE9BQU87UUFFN0IsOEJBQThCO1FBQzlCLEtBQUssTUFBTSxDQUFDLFNBQVMsRUFBRSxPQUFPLENBQUMsSUFBSSxNQUFNLENBQUMsT0FBTyxDQUFDLGVBQWUsQ0FBQyxFQUFFLENBQUM7WUFDakUsS0FBSyxNQUFNLENBQUMsUUFBUSxFQUFFLE1BQU0sQ0FBQyxJQUFJLE1BQU0sQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQztnQkFDdkQsSUFBSSxDQUFDLFFBQVEsQ0FBQztvQkFDVixTQUFTO29CQUNULFFBQVE7b0JBQ1IsR0FBRyxNQUFNO2lCQUNaLENBQUMsQ0FBQztZQUNQLENBQUM7UUFDTCxDQUFDO1FBRUQsSUFBSSxDQUFDLFdBQVcsR0FBRyxJQUFJLENBQUM7SUFDNUIsQ0FBQztJQUVEOztPQUVHO0lBQ0gsTUFBTSxDQUFDLFFBQVEsQ0FBQyxNQUE4QjtRQUMxQyxNQUFNLEdBQUcsR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxTQUFTLEVBQUUsTUFBTSxDQUFDLFFBQVEsQ0FBQyxDQUFDO1FBQzNELElBQUksQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLEdBQUcsRUFBRSxNQUFNLENBQUMsQ0FBQztJQUNuQyxDQUFDO0lBRUQ7O09BRUc7SUFDSCxNQUFNLENBQUMsR0FBRyxDQUFDLFNBQWlCLEVBQUUsUUFBZ0I7UUFDMUMsSUFBSSxDQUFDLElBQUksQ0FBQyxXQUFXO1lBQUUsSUFBSSxDQUFDLFVBQVUsRUFBRSxDQUFDO1FBQ3pDLE1BQU0sR0FBRyxHQUFHLElBQUksQ0FBQyxNQUFNLENBQUMsU0FBUyxFQUFFLFFBQVEsQ0FBQyxDQUFDO1FBQzdDLE9BQU8sSUFBSSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLElBQUksSUFBSSxDQUFDO0lBQzFDLENBQUM7SUFFRDs7T0FFRztJQUNILE1BQU0sQ0FBQyxNQUFNO1FBQ1QsSUFBSSxDQUFDLElBQUksQ0FBQyxXQUFXO1lBQUUsSUFBSSxDQUFDLFVBQVUsRUFBRSxDQUFDO1FBQ3pDLE9BQU8sS0FBSyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUM7SUFDOUMsQ0FBQztJQUVEOzs7T0FHRztJQUNILE1BQU0sQ0FBQyxXQUFXLENBQUMsSUFBWSxFQUFFLE1BQWM7UUFDM0MsSUFBSSxDQUFDLElBQUksQ0FBQyxXQUFXO1lBQUUsSUFBSSxDQUFDLFVBQVUsRUFBRSxDQUFDO1FBRXpDLEtBQUssTUFBTSxNQUFNLElBQUksSUFBSSxDQUFDLFFBQVEsQ0FBQyxNQUFNLEVBQUUsRUFBRSxDQUFDO1lBQzFDLElBQUksSUFBSSxDQUFDLFlBQVksQ0FBQyxJQUFJLEVBQUUsTUFBTSxDQUFDLFFBQVEsQ0FBQyxJQUFJLE1BQU0sS0FBSyxNQUFNLENBQUMsTUFBTSxFQUFFLENBQUM7Z0JBQ3ZFLE9BQU8sTUFBTSxDQUFDO1lBQ2xCLENBQUM7UUFDTCxDQUFDO1FBQ0QsT0FBTyxJQUFJLENBQUM7SUFDaEIsQ0FBQztJQUVPLE1BQU0sQ0FBQyxNQUFNLENBQUMsU0FBaUIsRUFBRSxRQUFnQjtRQUNyRCxPQUFPLEdBQUcsU0FBUyxJQUFJLFFBQVEsRUFBRSxDQUFDO0lBQ3RDLENBQUM7SUFFTyxNQUFNLENBQUMsWUFBWSxDQUFDLElBQVksRUFBRSxPQUFlO1FBQ3JELDJCQUEyQjtRQUMzQixzRUFBc0U7UUFDdEUsd0dBQXdHO1FBQ3hHLE1BQU0sWUFBWSxHQUFHLE9BQU87YUFDdkIsT0FBTyxDQUFDLFdBQVcsRUFBRSxPQUFPLENBQUMsQ0FBTSw0QkFBNEI7YUFDL0QsT0FBTyxDQUFDLFlBQVksRUFBRSxPQUFPLENBQUMsQ0FBSyw2QkFBNkI7YUFDaEUsT0FBTyxDQUFDLEtBQUssRUFBRSxLQUFLLENBQUMsQ0FBQztRQUUzQixnQ0FBZ0M7UUFDaEMsTUFBTSxLQUFLLEdBQUcsSUFBSSxNQUFNLENBQUMsSUFBSSxZQUFZLE9BQU8sQ0FBQyxDQUFDO1FBQ2xELE9BQU8sS0FBSyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUM1QixDQUFDOztBQUdMOztHQUVHO0FBQ0gsTUFBTSxPQUFPLHNCQUFzQjtJQUN2QixNQUFNLENBQUMsUUFBUSxHQUFHLElBQUksR0FBRyxFQUFVLENBQUM7SUFFNUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxJQUFZLEVBQUUsTUFBYztRQUN4QyxJQUFJLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxHQUFHLE1BQU0sQ0FBQyxXQUFXLEVBQUUsSUFBSSxJQUFJLEVBQUUsQ0FBQyxDQUFDO0lBQ3pELENBQUM7SUFFRCxNQUFNLENBQUMsTUFBTTtRQUNULE9BQU8sS0FBSyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUMsR0FBRyxDQUFDLEtBQUssQ0FBQyxFQUFFO1lBQ3pDLDhDQUE4QztZQUM5Qyw0REFBNEQ7WUFDNUQsTUFBTSxVQUFVLEdBQUcsS0FBSyxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsQ0FBQztZQUN0QyxNQUFNLE1BQU0sR0FBRyxLQUFLLENBQUMsU0FBUyxDQUFDLENBQUMsRUFBRSxVQUFVLENBQUMsQ0FBQztZQUM5QyxNQUFNLElBQUksR0FBRyxLQUFLLENBQUMsU0FBUyxDQUFDLFVBQVUsR0FBRyxDQUFDLENBQUMsQ0FBQztZQUM3QyxPQUFPLEVBQUUsSUFBSSxFQUFFLE1BQU0sRUFBRSxDQUFDO1FBQzVCLENBQUMsQ0FBQyxDQUFDO0lBQ1AsQ0FBQztJQUVELE1BQU0sQ0FBQyxRQUFRLENBQUMsSUFBWSxFQUFFLE1BQWM7UUFDeEMsTUFBTSxXQUFXLEdBQUcsTUFBTSxDQUFDLFdBQVcsRUFBRSxDQUFDO1FBRXpDLEtBQUssTUFBTSxVQUFVLElBQUksSUFBSSxDQUFDLFFBQVEsRUFBRSxDQUFDO1lBQ3JDLDhDQUE4QztZQUM5Qyw0REFBNEQ7WUFDNUQsTUFBTSxVQUFVLEdBQUcsVUFBVSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsQ0FBQztZQUMzQyxNQUFNLFNBQVMsR0FBRyxVQUFVLENBQUMsU0FBUyxDQUFDLENBQUMsRUFBRSxVQUFVLENBQUMsQ0FBQztZQUN0RCxNQUFNLE9BQU8sR0FBRyxVQUFVLENBQUMsU0FBUyxDQUFDLFVBQVUsR0FBRyxDQUFDLENBQUMsQ0FBQztZQUVyRCxJQUFJLFNBQVMsS0FBSyxXQUFXO2dCQUFFLFNBQVM7WUFFeEMsZ0NBQWdDO1lBQ2hDLElBQUksSUFBSSxDQUFDLFlBQVksQ0FBQyxJQUFJLEVBQUUsT0FBTyxDQUFDLEVBQUUsQ0FBQztnQkFDbkMsT0FBTyxJQUFJLENBQUM7WUFDaEIsQ0FBQztRQUNMLENBQUM7UUFDRCxpREFBaUQ7UUFDakQsOEZBQThGO1FBQzlGLDBGQUEwRjtRQUMxRixPQUFPLEtBQUssQ0FBQztJQUNqQixDQUFDO0lBRU8sTUFBTSxDQUFDLFlBQVksQ0FBQyxJQUFZLEVBQUUsT0FBZTtRQUNyRCwyQkFBMkI7UUFDM0Isc0VBQXNFO1FBQ3RFLHdHQUF3RztRQUN4RyxNQUFNLFlBQVksR0FBRyxPQUFPO2FBQ3ZCLE9BQU8sQ0FBQyxVQUFVLEVBQUUsT0FBTyxDQUFDLENBQU0sNEJBQTRCO2FBQzlELE9BQU8sQ0FBQyxZQUFZLEVBQUUsT0FBTyxDQUFDLENBQUssNkJBQTZCO2FBQ2hFLE9BQU8sQ0FBQyxLQUFLLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFFM0IsZ0NBQWdDO1FBQ2hDLE1BQU0sS0FBSyxHQUFHLElBQUksTUFBTSxDQUFDLElBQUksWUFBWSxPQUFPLENBQUMsQ0FBQztRQUNsRCxPQUFPLEtBQUssQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDNUIsQ0FBQzs7QUFHTDs7R0FFRztBQUNILE1BQU0sT0FBTyxxQkFBcUI7SUFDdEIsTUFBTSxDQUFDLFFBQVEsR0FBRyxJQUFJLEdBQUcsRUFBVSxDQUFDO0lBRTVDLE1BQU0sQ0FBQyxRQUFRLENBQUMsSUFBWSxFQUFFLE1BQWM7UUFDeEMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsR0FBRyxNQUFNLENBQUMsV0FBVyxFQUFFLElBQUksSUFBSSxFQUFFLENBQUMsQ0FBQztJQUN6RCxDQUFDO0lBRUQsTUFBTSxDQUFDLE9BQU8sQ0FBQyxJQUFZLEVBQUUsTUFBYztRQUN2QyxNQUFNLFdBQVcsR0FBRyxNQUFNLENBQUMsV0FBVyxFQUFFLENBQUM7UUFFekMsS0FBSyxNQUFNLFVBQVUsSUFBSSxJQUFJLENBQUMsUUFBUSxFQUFFLENBQUM7WUFDckMsTUFBTSxDQUFDLFNBQVMsRUFBRSxPQUFPLENBQUMsR0FBRyxVQUFVLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDO1lBRW5ELElBQUksU0FBUyxLQUFLLFdBQVc7Z0JBQUUsU0FBUztZQUV4QyxnQ0FBZ0M7WUFDaEMsSUFBSSxJQUFJLENBQUMsWUFBWSxDQUFDLElBQUksRUFBRSxPQUFPLENBQUMsRUFBRSxDQUFDO2dCQUNuQyxPQUFPLElBQUksQ0FBQztZQUNoQixDQUFDO1FBQ0wsQ0FBQztRQUNELE9BQU8sS0FBSyxDQUFDO0lBQ2pCLENBQUM7SUFFTyxNQUFNLENBQUMsWUFBWSxDQUFDLElBQVksRUFBRSxPQUFlO1FBQ3JELDJCQUEyQjtRQUMzQixzRUFBc0U7UUFDdEUsd0dBQXdHO1FBQ3hHLE1BQU0sWUFBWSxHQUFHLE9BQU87YUFDdkIsT0FBTyxDQUFDLFdBQVcsRUFBRSxPQUFPLENBQUMsQ0FBTSw0QkFBNEI7YUFDL0QsT0FBTyxDQUFDLFlBQVksRUFBRSxPQUFPLENBQUMsQ0FBSyw2QkFBNkI7YUFDaEUsT0FBTyxDQUFDLEtBQUssRUFBRSxLQUFLLENBQUMsQ0FBQztRQUUzQixNQUFNLEtBQUssR0FBRyxJQUFJLE1BQU0sQ0FBQyxJQUFJLFlBQVksT0FBTyxDQUFDLENBQUM7UUFDbEQsT0FBTyxLQUFLLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQzVCLENBQUMifQ==