feat: optimize query path and add smoke test/demo assets
This commit is contained in:
@@ -78,7 +78,12 @@ export class Yonexus {
|
||||
if (this.registrars.size > 0 && !this.registrars.has(actor.agentId)) {
|
||||
throw new YonexusError("REGISTRAR_DENIED", `registrar_denied: ${actor.agentId}`);
|
||||
}
|
||||
authorize("register_agent", actor, {}, this.store);
|
||||
|
||||
const isBootstrap = this.store.listAgents().length === 0 && actor.agentId === agentId;
|
||||
if (!isBootstrap) {
|
||||
authorize("register_agent", actor, {}, this.store);
|
||||
}
|
||||
|
||||
if (this.store.findAgent(agentId)) {
|
||||
throw new YonexusError("ALREADY_EXISTS", `agent_exists: ${agentId}`, { agentId });
|
||||
}
|
||||
|
||||
@@ -4,6 +4,25 @@ import type { Identity, QueryFilter, QueryInput, QueryOptions, YonexusSchema } f
|
||||
const DEFAULT_LIMIT = 20;
|
||||
const MAX_LIMIT = 100;
|
||||
|
||||
const regexCache = new Map<string, RegExp>();
|
||||
const containsCache = new Map<string, string>();
|
||||
|
||||
function getRegex(pattern: string): RegExp {
|
||||
const cached = regexCache.get(pattern);
|
||||
if (cached) return cached;
|
||||
const created = new RegExp(pattern);
|
||||
regexCache.set(pattern, created);
|
||||
return created;
|
||||
}
|
||||
|
||||
function normalizeNeedle(value: string): string {
|
||||
const cached = containsCache.get(value);
|
||||
if (cached) return cached;
|
||||
const normalized = value.toLowerCase();
|
||||
containsCache.set(value, normalized);
|
||||
return normalized;
|
||||
}
|
||||
|
||||
function isQueryable(field: string, schema: YonexusSchema): boolean {
|
||||
return Boolean(schema[field]?.queryable);
|
||||
}
|
||||
@@ -15,9 +34,9 @@ function matchFilter(identity: Identity, filter: QueryFilter): boolean {
|
||||
case "eq":
|
||||
return raw === filter.value;
|
||||
case "contains":
|
||||
return raw.toLowerCase().includes(filter.value.toLowerCase());
|
||||
return raw.toLowerCase().includes(normalizeNeedle(filter.value));
|
||||
case "regex": {
|
||||
const re = new RegExp(filter.value);
|
||||
const re = getRegex(filter.value);
|
||||
return re.test(raw);
|
||||
}
|
||||
default:
|
||||
@@ -31,6 +50,15 @@ function normalizeOptions(options?: QueryOptions): Required<QueryOptions> {
|
||||
return { limit, offset };
|
||||
}
|
||||
|
||||
function sortFilters(filters: QueryFilter[]): QueryFilter[] {
|
||||
const weight = (f: QueryFilter): number => {
|
||||
if (f.op === 'eq') return 1;
|
||||
if (f.op === 'contains') return 2;
|
||||
return 3;
|
||||
};
|
||||
return [...filters].sort((a, b) => weight(a) - weight(b));
|
||||
}
|
||||
|
||||
export function queryIdentities(identities: Identity[], input: QueryInput, schema: YonexusSchema): Identity[] {
|
||||
for (const filter of input.filters) {
|
||||
if (!isQueryable(filter.field, schema)) {
|
||||
@@ -40,7 +68,8 @@ export function queryIdentities(identities: Identity[], input: QueryInput, schem
|
||||
}
|
||||
}
|
||||
|
||||
const filtered = identities.filter((identity) => input.filters.every((f) => matchFilter(identity, f)));
|
||||
const orderedFilters = sortFilters(input.filters);
|
||||
const filtered = identities.filter((identity) => orderedFilters.every((f) => matchFilter(identity, f)));
|
||||
const { limit, offset } = normalizeOptions(input.options);
|
||||
return filtered.slice(offset, offset + limit);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user