@goatlab/typesense
Version:
Modern TypeScript wrapper for Typesense search engine API
101 lines • 3.63 kB
JavaScript
;
/**
* Tenant utility functions for multitenancy support
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.sanitizeTenantId = sanitizeTenantId;
exports.createFQCN = createFQCN;
exports.parseFQCN = parseFQCN;
exports.isTenantCollection = isTenantCollection;
exports.filterCollectionsByTenant = filterCollectionsByTenant;
exports.createTenantQualifiedName = createTenantQualifiedName;
/**
* Validates and sanitizes a tenant ID for use in collection names
* - Enforces allowed characters: a-zA-Z0-9_-
* - Converts to lowercase to avoid case-sensitivity issues
* - Enforces max length of 128 characters
*
* @param tenantId - Raw tenant ID to validate
* @returns Sanitized tenant ID
* @throws Error if tenant ID is invalid
*/
function sanitizeTenantId(tenantId) {
if (!tenantId || typeof tenantId !== 'string') {
throw new Error('Tenant ID must be a non-empty string');
}
// Trim whitespace
const trimmed = tenantId.trim();
if (!trimmed) {
throw new Error('Tenant ID cannot be empty or whitespace only');
}
// Check allowed characters
const allowedPattern = /^[a-zA-Z0-9_\-]+$/;
if (!allowedPattern.test(trimmed)) {
throw new Error('Tenant ID can only contain alphanumeric characters, underscores, and hyphens');
}
// Check max length
if (trimmed.length > 128) {
throw new Error('Tenant ID cannot exceed 128 characters');
}
// Convert to lowercase
return trimmed.toLowerCase();
}
/**
* Creates a fully qualified collection name by prepending tenant ID
* Format: <tenantId>__<baseCollectionName>
*
* @param tenantId - Sanitized tenant ID
* @param baseCollectionName - Base collection name without tenant prefix
* @returns Fully qualified collection name
*/
function createFQCN(tenantId, baseCollectionName) {
return `${tenantId}__${baseCollectionName}`;
}
/**
* Extracts tenant ID and base collection name from a fully qualified collection name
*
* @param fqcn - Fully qualified collection name
* @returns Object with tenantId and baseCollectionName, or null if not a tenant collection
*/
function parseFQCN(fqcn) {
const separatorIndex = fqcn.indexOf('__');
if (separatorIndex === -1 || separatorIndex === 0) {
return null;
}
return {
tenantId: fqcn.substring(0, separatorIndex),
baseCollectionName: fqcn.substring(separatorIndex + 2)
};
}
/**
* Checks if a collection name is tenant-prefixed
*
* @param collectionName - Collection name to check
* @returns True if the collection has a tenant prefix
*/
function isTenantCollection(collectionName) {
return collectionName.includes('__') && !collectionName.startsWith('__');
}
/**
* Filters collections by tenant ID
*
* @param collections - Array of collection names
* @param tenantId - Tenant ID to filter by
* @returns Collections belonging to the specified tenant
*/
function filterCollectionsByTenant(collections, tenantId) {
const prefix = `${tenantId}__`;
return collections.filter(name => name.startsWith(prefix));
}
/**
* Creates a tenant-qualified resource name for aliases, synonyms, presets, etc.
* This ensures tenant isolation for all Typesense resources, not just collections
*
* @param tenantId - Optional tenant ID (if not provided, returns the resource name as-is)
* @param resourceName - Base resource name
* @returns Tenant-qualified resource name or original name
*/
function createTenantQualifiedName(tenantId, resourceName) {
return tenantId ? `${tenantId}__${resourceName}` : resourceName;
}
//# sourceMappingURL=tenant.js.map