Reference
Field policy exports and semantics
This page explains the @happyvertical/smrt-fields exports, object names, wire encoding, write validation, and result caches.
Entry points
The package root holds the model, the collection, the resolver, and the helpers. The /svelte subpath holds the eleven components and the browser-side helpers. /manifest exposes the generated manifest and /playground the shared playground module.
- Models and collection: FieldPolicy, FieldPolicyCollection.
- Resolution: resolveFieldPolicy, resolveFieldPolicyExplained, resolveSurvivingTenantChainIds.
- Cache control: clearFieldPolicyCache, invalidateFieldPolicyCache, getFieldPolicyCacheTtlMs.
- Permissions: MANAGE_FIELD_POLICY_PERMISSION, PERSONALIZE_FIELD_POLICY_PERMISSION, FIELD_POLICY_PERMISSION_DEFINITIONS, ensureFieldPolicyPermissionsRegistered.
- Field helpers: isPolicyAddressableField, isRequiredField, isSensitiveField, isTransientField, getCodeDefault, buildCodeSeedDelta, buildCodeSeedVisibility, sanitizeFieldUIHints, assertDefaultValueMatchesFieldType.
- Control panel: buildFieldPolicySettingsCatalog, parseFieldPolicyCatalogQuery, fieldPolicyCatalogItemId.
import {
FieldPolicy,
FieldPolicyCollection,
MANAGE_FIELD_POLICY_PERMISSION,
resolveFieldPolicy,
resolveFieldPolicyExplained
} from '@happyvertical/smrt-fields';
import {
createFieldInputRegistry,
FieldPolicyControlPanel,
FieldPolicyGearProvider,
ObjectForm,
policyToVisibleColumnIds
} from '@happyvertical/smrt-fields/svelte';Components in the /svelte subpath
Eleven components ship in the /svelte subpath, each with a matching Props type. They are layout-neutral: they contribute behavior and leave markup decisions to the application.
- Form primitives: FieldPolicyProvider, PolicyField, ModeSwitch, AdvancedFields, FormHelp.
- Generated forms: ObjectForm, ObjectFormSourceProvider.
- Administration: FieldPolicyGearProvider, FieldPolicyGearButton, FieldPolicyEditor, FieldPolicyControlPanel.
Naming an object
Every policy row and every resolve call names its object with the canonical qualified class name: the package name, a colon, then the class name. It is the same identifier the manifest uses, so it survives renames of tables and routes.
'@happyvertical/smrt-content:Article'
'@happyvertical/smrt-commerce:Invoice'
'@acme/app-objects:StarterAppSetting'Two channels for a default value
defaultValue is the encoded channel: already JSON, exactly as the column stores it. defaultValueRaw is the plain channel: any value, always serialized. They are mutually exclusive and passing both throws, because one option cannot distinguish the string TBD from the encoded JSON string that contains it.
- Generated write routes hand the request body straight to the constructor, so the wire contract has to stay encoded.
- setDefaultValue is the method-level plain channel.
- The sort column is displayOrder; the resolved output exposes it as order.
// Encoded channel — what a generated route or the gear posts
new FieldPolicy({ defaultValue: JSON.stringify('Net 30') });
// Plain channel — what application code usually wants
new FieldPolicy({ defaultValueRaw: 'Net 30' });
// Passing both throws; { defaultValue: 'Net 30' } is a parse error
// whose message names defaultValueRaw.What a write is validated against
Writes validate against the live object registry rather than a checked-in manifest file. An unknown object or field is rejected, a default is type-checked against the field type, and defaults are refused on transient, sensitive, and read-permission-gated fields.
- System fields, relationship pseudo-fields, and single-table-inheritance meta storage fields are not policy-addressable.
- Required fields may only leave the basic tier when a usable default resolves, checked at write time and re-checked at resolution.
- locked may be written on app and tenant rows only.
Generated surface
FieldPolicy generates create, update, and delete routes. Generated list and get are deliberately closed: the model is not tenant-scoped, so a browsable read would enumerate every tenant and user row. Reads happen through three collection actions or through the server-side resolver.
- POST <collection>/resolve — resolveBatch, the context-scoped policy read used to bootstrap forms.
- POST <collection>/editor-state — getEditorState, the gear bootstrap.
- POST <collection>/policy-audit — policyAudit, the manage-gated organization roll-up.
- MCP is closed entirely. The generated CLI supports only create, update, and delete. A CLI command needs an API route, and the read routes do not exist. The collection configuration also closes the runtime CLI surface.
Caching and invalidation
Resolved results are cached for a short TTL per database namespace, object reference, tenant, user, and hierarchy loader. The loader is part of the key because an injected loader yields a different ancestor chain and therefore different defaults and locks.
- Saving or deleting a row invalidates every entry for that object, because a parent-tenant row affects each descendant.
- Policy rows do not ride the client change feed, so a browser learns about a change on its next resolve.
- Write-time validation resolves projected state outside the shared cache so a hypothetical result can never poison a real read.
Version prerequisite
@happyvertical/smrt-fields first appeared in the 0.40.5x line and is not part of 0.39.x. The package pins smrt-core, smrt-tenancy, smrt-ui, and smrt-users to its exact version. Install it at the same version as the other s-m-r-t packages. A mismatch installs a second object registry, and policy resolution stops recognizing your objects.
- smrt-users is a required runtime dependency, not an optional one: the permission catalog and operation guard back every write and gear action.
- The usage-learning loop is not in any published release yet.