|
| 1 | +--- |
| 2 | +'@objectstack/spec': minor |
| 3 | +'@objectstack/lint': minor |
| 4 | +'@objectstack/cli': minor |
| 5 | +--- |
| 6 | + |
| 7 | +Reference-integrity validation for object and action names (issue #3583) |
| 8 | + |
| 9 | +A HotCRM audit found ~20 shipped instances of one bug class — metadata naming |
| 10 | +something that does not exist — all passing `objectstack validate` / `lint` |
| 11 | +cleanly and failing silently at runtime. This closes the object-name and |
| 12 | +action-name half of that class. |
| 13 | + |
| 14 | +**New — `@objectstack/spec`:** `PLATFORM_PROVIDED_OBJECT_NAMES`, a curated |
| 15 | +registry of every object name contributed by a platform package, official |
| 16 | +plugin, or the cloud runtime, plus `isPlatformProvidedObjectName()` and |
| 17 | +`hasPlatformObjectPrefix()`. This replaces the `startsWith('sys_')` prefix guess |
| 18 | +that could not tell `sys_user` (real) from `sys_approval_process` (fictional — |
| 19 | +removed by ADR-0019, registered by nothing), which is why every fictional |
| 20 | +platform-prefixed reference shipped. A conformance test scans each package's |
| 21 | +`*.object.ts` declarations and fails if the registry drifts. |
| 22 | + |
| 23 | +**New lint rules** (wired into both `os validate` and `os lint`): |
| 24 | + |
| 25 | +- `validate-object-references` — action-param `reference` / `objectOverride`, |
| 26 | + dashboard `globalFilters[].optionsFrom.object`, and navigation |
| 27 | + `requiresObject` gates. Severity follows resolvability: an unresolved |
| 28 | + *unprefixed* name is a typo (**error** — `object: 'user'` where the platform |
| 29 | + object is `sys_user`); an unresolved *platform-prefixed* name is **advisory**, |
| 30 | + since a third-party package may still provide it. |
| 31 | +- `validate-action-name-refs` — the surfaces that bind an action BY NAME: |
| 32 | + list-view `bulkActions` / `rowActions`, page `record:quick_actions` |
| 33 | + `actionNames`, and nav action items. A name matching no defined action is an |
| 34 | + **error** (the button renders and does nothing), matching the existing |
| 35 | + dashboard-action-target rule. |
| 36 | + |
| 37 | +**Fixes:** |
| 38 | + |
| 39 | +- `defineStack` cross-reference validation now walks `app.areas[].navigation` — |
| 40 | + an areas-based app previously got no navigation checking at all — and recurses |
| 41 | + into `children` on `object` nav items, not only `group` ones. |
| 42 | +- `os lint` i18n coverage now reads field `options` in the canonical |
| 43 | + `{value,label}[]` array shape; it only handled the record map, so option-label |
| 44 | + coverage silently never fired for canonically-shaped select fields. |
| 45 | +- Hook `condition` expressions are now field-checked when `object` is an ARRAY |
| 46 | + of targets (previously only a single string target was checked, so a |
| 47 | + multi-target hook filtering on a nonexistent field passed clean). Per-target |
| 48 | + diagnostics are de-duplicated. |
| 49 | +- A dashboard widget binding no `dataset` at all is now reported instead of |
| 50 | + silently bypassing every binding and chart check on the raw-config |
| 51 | + (`lint`/`doctor`) paths. `dataset` is schema-required, so this matches what |
| 52 | + the parsed paths already enforce. |
0 commit comments