feat(namespace): conformance green 19/19 — data-aware did-you-mean on unindexed bare addresses, ordering contract on the column top-K path (never drop, nulls last, ties by id), shape-complete addressed reads (entity views AND raw storage shapes, shadow-proof both scopes), per-key source matching for dotted addresses; refusal classes unified under UnresolvableFieldError
This commit is contained in:
parent
7492b6cb59
commit
8e962dabda
4 changed files with 134 additions and 61 deletions
|
|
@ -167,10 +167,39 @@ export function readEntityFieldAddress(
|
|||
entity: HNSWNounWithMetadata,
|
||||
address: FieldAddress
|
||||
): unknown {
|
||||
const rec = entity as unknown as Record<string, unknown>
|
||||
const bag =
|
||||
rec.metadata && typeof rec.metadata === 'object'
|
||||
? (rec.metadata as Record<string, unknown>)
|
||||
: null
|
||||
|
||||
if (address.scope === 'system') {
|
||||
return (entity as unknown as Record<string, unknown>)[address.field]
|
||||
// Entity views carry system scalars top-level; raw storage shapes carry
|
||||
// them inside the stored metadata record (where `type` is spelled `noun`).
|
||||
// Read top-level first, then the record — never the user's namespace.
|
||||
const top = rec[address.field]
|
||||
if (top !== undefined) return top
|
||||
if (bag) {
|
||||
if (address.field === 'type') return bag.type ?? bag.noun
|
||||
return bag[address.field]
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
return entity.metadata?.[address.field]
|
||||
|
||||
// User scope. The write-path remap guarantees the user can never OWN a
|
||||
// field named like a system scalar (those lift top-level at write), so a
|
||||
// bare system name reads as ABSENT — reading the stored record's reserved
|
||||
// key here would re-create the shadow this module exists to kill. Same for
|
||||
// plumbing and the legacy 'noun' spelling.
|
||||
if (
|
||||
SYSTEM_ENTITY_SCALARS.has(address.field) ||
|
||||
PLUMBING_FIELDS.has(address.field) ||
|
||||
address.field === 'noun'
|
||||
) {
|
||||
return undefined
|
||||
}
|
||||
if (bag) return bag[address.field]
|
||||
return rec[address.field]
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -222,29 +251,6 @@ export function buildUnresolvableMessage(
|
|||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Refusal for a malformed or out-of-map field ADDRESS —
|
||||
* `system.<anything-not-in-the-map>` (including all plumbing), an empty
|
||||
* name, or a bare `metadata.` prefix. The message carries the full valid
|
||||
* system map so the fix never needs a docs lookup.
|
||||
*/
|
||||
export class InvalidFieldAddressError extends Error {
|
||||
public readonly raw: string
|
||||
public readonly kind: FieldAddressKind
|
||||
|
||||
constructor(raw: string, kind: FieldAddressKind, systemMap: ReadonlySet<string>) {
|
||||
const valid = [...systemMap].map((f) => `system.${f}`).join(', ')
|
||||
super(
|
||||
`'${raw}' is not an addressable ${kind} field. Bare names address your own ` +
|
||||
`metadata fields; engine fields are exactly: ${valid}. Engine plumbing ` +
|
||||
`(vector, connections, level, data, _rev) is not part of the query surface.`
|
||||
)
|
||||
this.name = 'InvalidFieldAddressError'
|
||||
this.raw = raw
|
||||
this.kind = kind
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Refusal for a syntactically valid address that resolves to
|
||||
* NOTHING — a bare name no user field carries. Carries the did-you-mean
|
||||
|
|
@ -256,14 +262,35 @@ export class UnresolvableFieldError extends Error {
|
|||
public readonly raw: string
|
||||
public readonly kind: FieldAddressKind
|
||||
|
||||
constructor(raw: string, kind: FieldAddressKind) {
|
||||
super(buildUnresolvableMessage(raw, kind))
|
||||
constructor(raw: string, kind: FieldAddressKind, messageOverride?: string) {
|
||||
super(messageOverride ?? buildUnresolvableMessage(raw, kind))
|
||||
this.name = 'UnresolvableFieldError'
|
||||
this.raw = raw
|
||||
this.kind = kind
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Refusal for a malformed or out-of-map field ADDRESS —
|
||||
* `system.<anything-not-in-the-map>` (including all plumbing), an empty
|
||||
* name, or a bare `metadata.` prefix. The message carries the full valid
|
||||
* system map so the fix never needs a docs lookup.
|
||||
*/
|
||||
export class InvalidFieldAddressError extends UnresolvableFieldError {
|
||||
constructor(raw: string, kind: FieldAddressKind, systemMap: ReadonlySet<string>) {
|
||||
const valid = [...systemMap].map((f) => `system.${f}`).join(', ')
|
||||
super(
|
||||
raw,
|
||||
kind,
|
||||
`'${raw}' is not an addressable ${kind} field. Bare names address your own ` +
|
||||
`metadata fields; engine fields are exactly: ${valid}. Engine plumbing ` +
|
||||
`(vector, connections, level, data, _rev) is not part of the query surface.`
|
||||
)
|
||||
this.name = 'InvalidFieldAddressError'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @description Refusal for a find() option that is accepted by the type
|
||||
* surface but NOT implemented — an accepted option must work or refuse;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue