feat(namespace): egress guard + validation speak the law — whereMatcher's resolver reads system.* from the record and bare names from the metadata bag only (the bare-system switch is dead); validateFindParams refuses cursor/includeRelations/writeOnly typed (accepted-and-ignored dies as a class), validates order, and parses every orderBy address
This commit is contained in:
parent
4679c89458
commit
c2fb28a2f7
3 changed files with 101 additions and 36 deletions
|
|
@ -17,6 +17,7 @@ import { findCallerLocation } from './callerLocation.js'
|
|||
// fallback branches that no supported runtime can reach.
|
||||
import * as os from 'node:os'
|
||||
import * as fs from 'node:fs'
|
||||
import { parseFieldAddress, UnsupportedFindOptionError } from '../db/fieldAddressing.js'
|
||||
|
||||
const getSystemMemory = (): number => {
|
||||
if (os) {
|
||||
|
|
@ -466,9 +467,31 @@ export function validateFindParams(params: FindParams): void {
|
|||
throw new Error('cannot specify both query and vector - they are mutually exclusive')
|
||||
}
|
||||
|
||||
// Universal truth: can't use both cursor and offset pagination
|
||||
if (params.cursor !== undefined && params.offset !== undefined) {
|
||||
throw new Error('cannot use both cursor and offset pagination simultaneously')
|
||||
// ACCEPTED-AND-IGNORED DIED AS A CLASS (sealed 2026-08-03): options the
|
||||
// engine does not implement REFUSE with a typed error instead of silently
|
||||
// doing nothing — a production consumer discovered a no-op by measurement
|
||||
// once; never again.
|
||||
if (params.cursor !== undefined) {
|
||||
throw new UnsupportedFindOptionError('cursor')
|
||||
}
|
||||
if ((params as Record<string, unknown>).includeRelations !== undefined) {
|
||||
throw new UnsupportedFindOptionError('includeRelations')
|
||||
}
|
||||
if ((params as Record<string, unknown>).writeOnly !== undefined) {
|
||||
throw new UnsupportedFindOptionError('writeOnly')
|
||||
}
|
||||
|
||||
// THE ONE ADDRESSING LAW: the orderBy address must PARSE (bare/metadata. =
|
||||
// user field, system.<field> = the ruled map, anything else refuses typed
|
||||
// with the valid map in the message) and order must be a real direction.
|
||||
if (params.orderBy !== undefined) {
|
||||
if (typeof params.orderBy !== 'string') {
|
||||
throw new Error('orderBy must be a string field address')
|
||||
}
|
||||
parseFieldAddress(params.orderBy, 'entity') // throws InvalidFieldAddressError on a bad address
|
||||
}
|
||||
if (params.order !== undefined && params.order !== 'asc' && params.order !== 'desc') {
|
||||
throw new Error(`order must be 'asc' or 'desc', got '${String(params.order)}'`)
|
||||
}
|
||||
|
||||
// Auto-limit query length based on memory
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue