UNPKG

@adonis-agora/filter

Version:

Server-side query filtering/sorting/pagination for AdonisJS — Spatie-style input, a Lucid adapter, and field allow-listing. Part of the Agora ecosystem.

65 lines 2.63 kB
/** * Field names that would resolve to inherited `Object.prototype` members * (`__proto__`, `constructor`, `prototype`, `toString`, `valueOf`) if looked up * on a plain-object `aliases` map without an own-property check. A * client-supplied field name is never trusted as an alias-map lookup key without * this guard, even though the `Object.hasOwn` check below already excludes * inherited properties; kept as explicit defense-in-depth. */ const BLOCKED_ALIAS_KEYS = new Set([ '__proto__', 'constructor', 'prototype', 'toString', 'valueOf', ]); /** * Resolves a client-supplied field name to its declared `aliases` target, or * returns `field` unchanged when no alias applies. * * This is the single choke point for alias resolution — call it wherever a * client-supplied field name is about to be resolved against the entity (column * filters and sort). It must run FIRST, before any validation (allow-listing, * field-charset checks) — those all evaluate the resolved target, never the * alias key. * * Aliases do not cascade: the returned target is never re-run through the alias * map, even when it happens to also be a declared alias key itself — this is * what makes alias cycles structurally impossible. */ export function resolveFieldAlias(aliases, field) { if (!aliases || BLOCKED_ALIAS_KEYS.has(field) || !Object.hasOwn(aliases, field)) { return field; } return aliases[field] ?? field; } /** * Returns a copy of a {@link ColumnFilter} with its own field and every nested * AND/OR child field resolved through the alias map. Pure — the input tree is * left untouched. */ export function remapFilterAliases(filter, aliases) { const next = { field: resolveFieldAlias(aliases, filter.field), operator: filter.operator, }; if (filter.value !== undefined) next.value = filter.value; if (filter.AND) next.AND = filter.AND.map((f) => remapFilterAliases(f, aliases)); if (filter.OR) next.OR = filter.OR.map((f) => remapFilterAliases(f, aliases)); return next; } /** Resolves every sort directive's field through the alias map (returns a new array). */ export function remapSortAliases(sorts, aliases) { return sorts.map((sort) => ({ field: resolveFieldAlias(aliases, sort.field), direction: sort.direction, })); } /** Resolves every distinct field name through the alias map (returns a new array). */ export function remapDistinctAliases(fields, aliases) { return fields.map((field) => resolveFieldAlias(aliases, field)); } //# sourceMappingURL=field_aliases.js.map