fix(resources): URL attribute-suffix routing for programmatic static-properties Resources - #1933
Conversation
… static-properties Resources (#1922) `Resource.parsePath` resolved a `/id.attr` suffix by scanning `this.attributes`, so a programmatic Resource declaring only `static properties` (no `attributes` Array) never matched — `/Widget/id.label` silently didn't route to the attribute. Fall back to an OWN-key lookup on the `static properties` Record (keyed by attribute name): O(1), so no per-request projection. The own-key check (Object.hasOwn) avoids matching inherited Object.prototype members like `constructor`/`toString` that a plain `properties[property]` would. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0188G62J9fZQg4J9rVuqLzjy
There was a problem hiding this comment.
Code Review
This pull request introduces support for URL attribute-suffix routing on programmatic resources that declare static properties instead of an attributes array, utilizing Object.hasOwn to prevent matching inherited Object.prototype members. It also adds comprehensive unit tests to verify this routing behavior. The feedback suggests removing a redundant any cast when accessing this.properties in Resource.ts to preserve TypeScript's type safety, as properties is already declared as a static property on the class.
|
Reviewed; no blockers found. |
…review) `static properties` is declared on Resource, so `this.properties` is typed in the static method — no cast needed. Raised by gemini-code-assist. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0188G62J9fZQg4J9rVuqLzjy
|
Docs companion opened: HarperFast/documentation#605 — Comment generated by kAIle (Claude Opus 4.8). |
Summary
Closes #1922 (follow-up from #1921's review).
Resource.parsePathresolves a URL attribute suffix (/Widget/id.label) by scanningthis.attributesfor a matching name. A programmatic Resource that declares onlystatic properties(a Record, noattributesArray — the shape #1921 makes first-class for schemas) hasattributes === undefined, so that routing silently never matched.Fix: fall back to an own-key lookup on the
static propertiesRecord, which is keyed by attribute name. It's O(1), so there's no per-request projection on this hot path (the issue floated a memoized projection; a direct key check is simpler and needs no cache or invalidation).Where to look
resources/Resource.ts(parsePath) — one added clause:... || (properties != null && Object.hasOwn(properties, property)).Object.hasOwn(notproperties[property]) is deliberate — a plain index would match inheritedObject.prototypemembers (constructor,toString, …) and mistake/id.constructorfor an attribute suffix. Table-backed routing is unchanged (theattributes.findcheck still runs first).Tests
unitTests/resources/parsePath.test.js— attribute-suffix routing on a programmaticstatic propertiesResource (with/without a query object), an unknown suffix left untouched, and the inherited-prototype guard (constructor/toString/hasOwnProperty/__proto__are not treated as attributes).Self-review caught the inherited-prototype issue; given the change is a single additive clause I did not run the full cross-model orchestration.
PR generated by kAIle (Claude Opus 4.8).