feat: scaffold yonexus MVP core with storage, auth, query, and scope memory
This commit is contained in:
43
src/tools/query.ts
Normal file
43
src/tools/query.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import type { Identity, QueryFilter, QueryInput, QueryOptions, YonexusSchema } from "../models/types";
|
||||
|
||||
const DEFAULT_LIMIT = 20;
|
||||
const MAX_LIMIT = 100;
|
||||
|
||||
function isQueryable(field: string, schema: YonexusSchema): boolean {
|
||||
return Boolean(schema[field]?.queryable);
|
||||
}
|
||||
|
||||
function matchFilter(identity: Identity, filter: QueryFilter): boolean {
|
||||
const raw = identity.meta[filter.field] ?? "";
|
||||
|
||||
switch (filter.op) {
|
||||
case "eq":
|
||||
return raw === filter.value;
|
||||
case "contains":
|
||||
return raw.toLowerCase().includes(filter.value.toLowerCase());
|
||||
case "regex": {
|
||||
const re = new RegExp(filter.value);
|
||||
return re.test(raw);
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeOptions(options?: QueryOptions): Required<QueryOptions> {
|
||||
const limit = Math.min(Math.max(1, options?.limit ?? DEFAULT_LIMIT), MAX_LIMIT);
|
||||
const offset = Math.max(0, options?.offset ?? 0);
|
||||
return { limit, offset };
|
||||
}
|
||||
|
||||
export function queryIdentities(identities: Identity[], input: QueryInput, schema: YonexusSchema): Identity[] {
|
||||
for (const filter of input.filters) {
|
||||
if (!isQueryable(filter.field, schema)) {
|
||||
throw new Error(`field_not_queryable: ${filter.field}`);
|
||||
}
|
||||
}
|
||||
|
||||
const filtered = identities.filter((identity) => input.filters.every((f) => matchFilter(identity, f)));
|
||||
const { limit, offset } = normalizeOptions(input.options);
|
||||
return filtered.slice(offset, offset + limit);
|
||||
}
|
||||
Reference in New Issue
Block a user