Files
openclaw/src/routing/account-id.ts
2026-03-02 21:58:35 +00:00

71 lines
2.1 KiB
TypeScript

import { isBlockedObjectKey } from "../infra/prototype-keys.js";
export const DEFAULT_ACCOUNT_ID = "default";
const VALID_ID_RE = /^[a-z0-9][a-z0-9_-]{0,63}$/i;
const INVALID_CHARS_RE = /[^a-z0-9_-]+/g;
const LEADING_DASH_RE = /^-+/;
const TRAILING_DASH_RE = /-+$/;
const ACCOUNT_ID_CACHE_MAX = 512;
const normalizeAccountIdCache = new Map<string, string>();
const normalizeOptionalAccountIdCache = new Map<string, string | undefined>();
function canonicalizeAccountId(value: string): string {
if (VALID_ID_RE.test(value)) {
return value.toLowerCase();
}
return value
.toLowerCase()
.replace(INVALID_CHARS_RE, "-")
.replace(LEADING_DASH_RE, "")
.replace(TRAILING_DASH_RE, "")
.slice(0, 64);
}
function normalizeCanonicalAccountId(value: string): string | undefined {
const canonical = canonicalizeAccountId(value);
if (!canonical || isBlockedObjectKey(canonical)) {
return undefined;
}
return canonical;
}
export function normalizeAccountId(value: string | undefined | null): string {
const trimmed = (value ?? "").trim();
if (!trimmed) {
return DEFAULT_ACCOUNT_ID;
}
const cached = normalizeAccountIdCache.get(trimmed);
if (cached) {
return cached;
}
const normalized = normalizeCanonicalAccountId(trimmed) || DEFAULT_ACCOUNT_ID;
setNormalizeCache(normalizeAccountIdCache, trimmed, normalized);
return normalized;
}
export function normalizeOptionalAccountId(value: string | undefined | null): string | undefined {
const trimmed = (value ?? "").trim();
if (!trimmed) {
return undefined;
}
if (normalizeOptionalAccountIdCache.has(trimmed)) {
return normalizeOptionalAccountIdCache.get(trimmed);
}
const normalized = normalizeCanonicalAccountId(trimmed) || undefined;
setNormalizeCache(normalizeOptionalAccountIdCache, trimmed, normalized);
return normalized;
}
function setNormalizeCache<T>(cache: Map<string, T>, key: string, value: T): void {
cache.set(key, value);
if (cache.size <= ACCOUNT_ID_CACHE_MAX) {
return;
}
const oldest = cache.keys().next();
if (!oldest.done) {
cache.delete(oldest.value);
}
}