@@ -218,12 +218,12 @@ export function lowerRelationSite(site: RelationFilterSite, ids: readonly unknow
218218 } ;
219219}
220220
221- /** How the words name the object beneath the field : its keys, never its values. */
221+ /** How the words name the condition : its keys, never its values. */
222222function describeKeys ( keys : readonly string [ ] ) : string {
223- if ( keys . length === 0 ) return 'an empty object {} ' ;
223+ if ( keys . length === 0 ) return 'no keys ' ;
224224 const shown = keys . slice ( 0 , 3 ) . map ( ( key ) => JSON . stringify ( key ) ) . join ( ', ' ) ;
225225 const more = keys . length > 3 ? `, and ${ keys . length - 3 } more` : '' ;
226- return `an object with no operator key ( keys ${ shown } ${ more } ) ` ;
226+ return `keys ${ shown } ${ more } ` ;
227227}
228228
229229/** The ids route every driver serves: `$in` on a single-valued relation, `$contains` per id on a multi-valued one. */
@@ -236,54 +236,49 @@ function idsRoute(field: string, multiple: boolean): string {
236236/**
237237 * The words of a structural refusal ({@link admitRelationCondition}): the
238238 * position, the verdict and the route first — the REST door bounds a 4xx
239- * message at 500 characters by truncation — and the reasoning after.
239+ * message at 500 characters by truncation — and nothing after the route that a
240+ * caller needs.
240241 */
241242export function relationConditionRefusalMessage ( refusal : RelationConditionRefusal , context : string ) : string {
242243 const { field, type, target, multiple, path, key } = refusal ;
243244 const related = target === undefined ? 'the related object' : `the related object '${ target } '` ;
244- const readTarget = target === undefined ? 'the related object' : `'${ target } '` ;
245+ const named = target === undefined ? 'the related object' : `'${ target } '` ;
245246 const head =
246- `${ context } : filter on '${ field } ' puts ${ describeKeys ( refusal . keys ) } at ${ path } , a nested-relation `
247- + `condition beneath the declared ${ type } field '${ field } '` ;
248- const route = `match '${ field } ' against the ids it returns: ${ idsRoute ( field , multiple ) } ` ;
247+ `${ context } : filter on '${ field } ' puts a nested-relation condition (${ describeKeys ( refusal . keys ) } ) at `
248+ + `${ path } , beneath the declared ${ type } field '${ field } '` ;
249+ const verdict = 'The filter was NOT applied.' ;
250+ const twoStep = `then match '${ field } ' against its ids: ${ idsRoute ( field , multiple ) } .` ;
249251 switch ( refusal . reason ) {
250252 case 'unregistered-target' :
251253 return (
252254 head
253255 + ( target === undefined
254- ? ' — and the field declares no related object, so there is nothing to read. '
255- : ` — and no object '${ target } ' is registered here, so there is nothing to read. ` )
256- + 'The filter was NOT applied. Match the field against ids you already hold: '
257- + `${ idsRoute ( field , multiple ) } .`
256+ ? ', and the field declares no related object to read. '
257+ : `, and no object '${ target } ' is registered here to read. ` )
258+ + `${ verdict } Match '${ field } ' against ids you hold: ${ idsRoute ( field , multiple ) } .`
258259 ) ;
259260 case 'empty' :
260261 return (
261- head
262- + ` that names no field of ${ related } . The filter was NOT applied. Name a field of ${ related } `
263- + `({ "${ field } ": { "FIELD": VALUE } }), or test that '${ field } ' has a value with `
262+ `${ head } , and it names no field of ${ related } . ${ verdict } Name one `
263+ + `({ "${ field } ": { "FIELD": VALUE } }), or test that '${ field } ' has a value: `
264264 + `{ "${ field } ": { "$null": false } }.`
265265 ) ;
266266 case 'undeclared-key' :
267267 return (
268- head
269- + `, and '${ key } ' is not a field of ${ related } . The filter was NOT applied. Name a field `
270- + `${ related } declares: { "${ field } ": { "FIELD": VALUE } }. No driver matches a key the `
271- + 'related object does not declare the same way, and an empty answer would read exactly like a real one.'
268+ `${ head } , and '${ key } ' is not a field of ${ related } . ${ verdict } Name a field it declares: `
269+ + `{ "${ field } ": { "FIELD": VALUE } }. A key it does not declare would read as a real empty answer.`
272270 ) ;
273271 case 'dotted-key' :
274272 return (
275- head
276- + `, and '${ key } ' is a dotted path — the condition reaches one level only, the fields of `
277- + `${ related } itself. The filter was NOT applied. Read ${ readTarget } with that condition `
278- + `yourself (one level there), then ${ route } .`
273+ `${ head } , and '${ key } ' is a dotted path: the condition reaches one level only. ${ verdict } `
274+ + `Read ${ named } with that condition yourself, ${ twoStep } `
279275 ) ;
280276 case 'second-level' :
281277 default :
282278 return (
283- head
284- + `, and '${ key } ' is itself a ${ refusal . keyType ?? 'relation' } field of ${ related } holding a `
285- + 'condition of its own — the condition reaches one level only. The filter was NOT applied. '
286- + `Read ${ readTarget } with { "${ key } ": { … } } yourself (one level there), then ${ route } .`
279+ `${ head } , and '${ key } ' is itself a ${ refusal . keyType ?? 'relation' } field of ${ related } holding a `
280+ + `condition of its own: one level only. ${ verdict } Read ${ named } with { "${ key } ": { … } } yourself, `
281+ + twoStep
287282 ) ;
288283 }
289284}
@@ -300,13 +295,10 @@ export function relationConditionError(refusal: RelationConditionRefusal, contex
300295 * matching rows. The words name the cap, the relation and the two-step route.
301296 */
302297export function relationFilterCapError ( site : RelationFilterSite , context : string , cap : number ) : Error {
303- const keys = Object . keys ( site . condition ) . map ( ( key ) => JSON . stringify ( key ) ) . join ( ', ' ) ;
304298 return invalidFilterError (
305299 `${ context } : the nested-relation condition at ${ site . path } matched more than ${ cap } records of `
306- + `the related object '${ site . target } ' — past the engine's cap on the ids one condition may feed `
307- + `the filter — so the filter was NOT applied: a cut-off id list would silently drop matching rows. `
308- + `Narrow the condition, or run the two steps yourself: read '${ site . target } ' with the same `
309- + `condition (keys ${ keys } ) page by page, then match '${ site . field } ' against the ids it returns: `
310- + `${ idsRoute ( site . field , site . multiple ) } .` ,
300+ + `the related object '${ site . target } ' (the engine's cap), so the filter was NOT applied: a cut-off `
301+ + `id list would silently drop matching rows. Narrow the condition, or read '${ site . target } ' yourself `
302+ + `page by page and match '${ site . field } ' against its ids: ${ idsRoute ( site . field , site . multiple ) } .` ,
311303 ) ;
312304}
0 commit comments