Protected_Raw string representation of Configuration from metadata.
Collection of all joined field mappings from the related entity.
Mirror of IsComputed on the related entity's Name Field. Tracked alongside
_RelatedEntityNameFieldIsVirtual so that base-view JOIN-target selection can
prefer the related entity's base table when the Name Field is a SQL computed/
generated column (physically present in the base table even though IsVirtual=1).
Controls whether encrypted fields are decrypted when returned via API.
When true:
When false:
Whether CodeGen automatically updates Description from the underlying schema object.
Whether CodeGen automatically updates FullTextSearchEnabled from schema indexes.
Whether CodeGen automatically updates UserSearchPredicateAPI from schema heuristics.
Optional property that provides the display name for the field, if null, use the Name property. The DisplayNameOrName() method is a helper function that does this for you with a single method call.
Optional JSON policy object declaring this foreign-key field as a first-class
embedded record — a 1:1 peer that loads, validates and persists as one
unit with its owner. Shape is IEmbeddedRecordConfig (OnClear, LoadNested).
RelatedEntityID and this field's Name are the join; they are deliberately
not repeated inside the JSON. AllowsNull on this same field decides whether
GetEntityObject provisions the object (required FK) or the caller uses
{FieldName}_EnsureObject() (nullable FK).
null (the default, and every pre-feature row) means the field is an ordinary
FK: nothing is generated and nothing is constructed at GetEntityObject time.
When true, this field will be encrypted at rest using the specified EncryptionKeyID. Encrypted fields:
References the encryption key to use when Encrypt is true. Must point to an active record in the "MJ: Encryption Keys" entity. Required if Encrypt is true.
Foreign key to the Entities entity.
Primary Key
When true, this field is a SQL Server computed column or PostgreSQL generated column — physically present in the base table but read-only at the SQL layer. Distinct from IsVirtual, which is also set to 1 for these fields (because they are read-only at the API layer) but is additionally set for view-only columns that don't exist in the base table at all (e.g., joined name lookups in the base view). Use the combination to disambiguate: IsVirtual=0, IsComputed=0 → regular base-table column (writable) IsVirtual=1, IsComputed=0 → view-only column (no physical storage) IsVirtual=1, IsComputed=1 → computed/generated column (physical, read-only in SQL) Only relevant downstream consumer that branches on this is base-view JOIN target selection in CodeGen — when an FK's related Name Field is IsComputed=1, the join targets the related entity's base table (not its view).
If true, the field is the primary key for the entity. There must be one primary key field per entity.
Indicates this field is a soft foreign key (metadata-defined, not a database constraint). When set to 1, RelatedEntityID and RelatedEntityFieldName are preserved and not overwritten by CodeGen schema sync.
Indicates this field is a soft primary key (metadata-defined, not a database constraint). The view ORs this with IsPrimaryKey, so existing code checking IsPrimaryKey works automatically.
If true, the field is a unique key for the entity. There can be zero to many unique key fields per entity.
The name of the TypeScript interface/type for this JSON field. When set, CodeGen will emit a strongly-typed getter/setter using this type instead of the default string getter/setter.
Raw TypeScript code emitted by CodeGen above the entity class definition. Typically contains the interface/type definition referenced by JSONType. Can include imports, multiple types, or any valid TypeScript.
If true, the field holds a JSON array of JSONType items. The getter returns JSONType[] | null and the setter accepts JSONType[] | null.
JSON configuration for additional fields to join from the related entity. Parsed from the RelatedEntityJoinFields column.
When AllowDecryptInAPI is false, controls what value is returned to clients.
When true:
When false:
The sequence of the field within the entity, typically the intended display order
Search predicate controlling how user-search queries match against this field in the LIKE-based search path used when the entity does not have FullTextSearchEnabled. Valid values: 'BeginsWith' | 'Contains' | 'EndsWith' | 'Exact'. Default 'Contains'. Honored by GenericDatabaseProvider.createViewUserSearchSQL.
Static ReadonlyExtendedRuntime domain for ExtendedType. Same array as EntityFieldExtendedTypes.
Static ReadonlyMaxUpper bound on how many legal values a validation message enumerates before it truncates. A long list would otherwise produce an error message no user can read.
Optional JSON configuration bag (shape = IEntityFieldConfiguration). Defines field-level configurations such as Hierarchy options (IsHierarchy, MaxDepth). Parsed lazily on first access and cached using SafeJSONParse.
Parsed Configuration. Null when the column is empty or not valid JSON.
Returns the DisplayName if it exists, otherwise returns the Name.
Field-level (column-level) security records configured for THIS field, across all roles. Empty for the overwhelming majority of fields — see HasFieldPermissions.
Returns true if the field's SQL type is fixed-width / space-padded
(SQL Server char/nchar, PostgreSQL char/character/bpchar).
Authoritative source is @memberjunction/sql-dialect so the list of
fixed-width types stays in one place per dialect. BaseEntity reads
this on value-set to rtrim padding the DB returned, preventing
spurious dirty-flagging when application code stores the logical
(un-padded) form of the value.
Returns true if the field has a default value set
True when at least one EntityFieldPermissionInfo record exists for this field.
Not an enforcement gate — it answers "does any configuration target this field", which CodeGen's DB-tier emission and the system-user entanglement guard both need. The access decision is EntityInfo.EnableFieldLevelSecurity plus the aggregation; on an enabled entity a field with no records is denied, not open.
Maximum recursion depth configured for this hierarchy field (defaults to 100).
Returns true if the field type is a binary type such as binary, varbinary, or image.
Returns true if the field is the CreatedAt field, a special field that is used to track the creation date of a record. This is only used when the entity has TrackRecordChanges=1
Returns true if the field is the DeletedAt field, a special field that is used to track the deletion date of a record. This is only used when the entity has DeleteType=Soft
True when ExtendedType is any Geo* tag (Geo, GeoLatitude, GeoAddress, …).
Used by maps, distance, and GeoCodeSyncService. Display-only virtuals still count.
Returns true if this field is explicitly configured as an intentional recursive tree hierarchy.
Native (table) latitude column — ExtendedType=GeoLatitude, or legacy Geo named Latitude.
Native (table) longitude column — ExtendedType=GeoLongitude, or legacy Geo named Long*.
True when this field belongs to an entity that field-level security may never restrict. See EntityFieldInfo.UnrestrictableEntityNames for the rationale.
Helper method that returns true if the field is one of the special reserved MJ date fields for tracking CreatedAt and UpdatedAt timestamps as well as the DeletedAt timestamp used for entities that have DeleteType=Soft. This is only used when the entity has TrackRecordChanges=1 or for entities where DeleteType=Soft
Returns true if the field is a GUID/UUID column in the database.
Accepts both the SQL Server type name (uniqueidentifier) and the
PostgreSQL type name (uuid) — on PG the metadata Type is reported as
uuid, so a uniqueidentifier-only check would miss every UUID column.
True for fields that must remain readable regardless of any permission record:
primary keys (hard or soft) and __mj_ system columns.
Stripping a primary key from a result breaks entity load, CompositeKey construction, relationship resolution, and cache fingerprinting — the failure surfaces far from the permission record that caused it. This is enforced here AND at save time on the permission record itself, so a row inserted outside the entity path still cannot take a primary key out of a result set.
Returns true if the field is the UpdatedAt field, a special field that is used to track the last update date of a record. This is only used when the entity has TrackRecordChanges=1
A Geo* field that can be written on Save. GeoCodeSyncService only runs when the
entity has at least one of these. Virtual / AllowUpdateAPI=0 fields (PrimaryAddress*,
__mj_Latitude, embedded __mj_Latitude_{FK}) are display-only — maps still use them.
Returns true when the field's spUpdate / spCreate procedure
exposes a <Param>_Clear companion parameter — i.e. the field is
nullable and has a non-NULL database default.
Codegen emits the companion so a caller can disambiguate
"leave unchanged / apply default" (omit the parameter) from
"explicitly set this column to NULL" (<Param>_Clear = 1).
Without it, the SP body's ISNULL(@Param, [Col]) merge silently
substitutes the existing value or default, and a literal NULL
could never be persisted.
Save-time callers in the data providers use this to decide whether
to also emit the _Clear companion parameter when the entity
intentionally sets such a field to NULL. Stays in sync with
CodeGenLib's needsClearCompanion.
Note: relies on DefaultValue already being normalized by
ExtractActualDefaultValue at populate time — that helper strips
the DB's wrapping parens and converts a literal NULL default to
JS null. So if HasDefaultValue is true, the default is
guaranteed to be non-NULL.
Returns true if the field type requires quotes around the value when used in a SQL statement
JSON configuration for additional fields to join from the related entity. Parsed from the RelatedEntityJoinFields column. Uses lazy initialization and caching to avoid repeated JSON.parse calls. If parsing fails, it won't be attempted again.
Returns true if the field type requires a Unicode prefix (N) when used in a SQL statement.
Returns true if the field is a "special" field (see list below) and is handled inside the DB layer and should be ignored in validation by the BaseEntity architecture Also, we skip validation if we have a field that is:
Returns a string with the full SQL data type that combines, as appropriate, Type, Length, Precision and Scale where these attributes are relevant to the Type
Provides the TypeScript type for a given Entity Field. This is useful to map a wide array of database types to a narrower set of TypeScript types.
Memoized: this getter is read per-field on extremely hot paths (the EntityField
value getter/setter, BaseEntity.Set/SetMany, dirty-tracking, hydration, and the
raw-mode Get() date check). Recomputing the SQL→TS classification (which runs
several string-matching helpers + toLowerCase) on every access is wasteful since
Type never changes after load.
Returns the Unicode prefix (N) if the field type requires it, otherwise returns an empty string.
Returns the ValueListType using the EntityFieldValueListType enum.
This field's legal values formatted for a validation message, truncated past MaxValueListValuesInErrorMessage. Memoized — a bad bulk load fails on the same field repeatedly, and the string never changes.
ProtectedcopyCopies initialization data from a plain object to the class instance. Only copies properties that already exist on the class to prevent creating new fields. Special handling for DefaultValue fields to extract actual values from SQL Server syntax.
The initialization data object
Formats a value based on the parameters passed in. This is a wrapper utility method that already know the SQL type from the entity field definition and simply calls the generic FormatValue() function that is also exported by @memberjunction/core
Value to format
Number of decimals to show, defaults to 2
Currency to use when formatting, defaults to USD
Maximum length of the string to return, if the formatted value is longer than this length then the string will be truncated and the trailingChars will be appended to the end of the string
Only used if maxLength is > 0 and the string being formatted is > maxLength, this is the string that will be appended to the end of the string to indicate that it was truncated, defaults to "..."
either the original string value or a formatted version. If the format cannot be applied an an exception occurs it is captured and the error is put to the log, and the original value is returned
Returns the effective field-level access this user has to this field, aggregating the field's permission records across every role the user holds.
PRECONDITION: the caller has already established that the parent entity has
EntityInfo.EnableFieldLevelSecurity set. The flag is a required parameter rather
than something this method looks up, because EntityFieldInfo holds its entity's NAME and
not a reference to the EntityInfo — and a method that silently answered "denied" for a
field on a non-FLS entity would be a trap. Pass false and every field comes back fully
open.
Per verb, across the user's matching roles:
effective = (any row Allows) AND NOT (any row Denies). Deny is absorbing and No Access
is the identity, so three states collapse to that one expression.
Outcomes:
There is no exempt user — not even the MJ system user. Every account, including the
one the server runs its own background work as, gets its access from the rows. The system
user stays working because it holds the standard roles (UI, Developer, Integration),
snapshot initialization writes them Allow rows like any other role holding entity read,
and the save-time configuration guards refuse a Deny aimed at a role it holds. That is a
constraint on what can be CONFIGURED, which an administrator can see and reason about —
unlike a runtime bypass, which is invisible at the point where access is decided and has
to be trusted rather than checked.
PERFORMANCE: this is the per-FIELD primitive. Enforcement points must never call it
inside a per-row loop — MapFieldNamesToCodeNames runs once per row, so a naive call
site costs fields x rows aggregations. Compute the denied-field Set once per
(entity, user) per request and pass it into the row loop.
the user whose effective access is being resolved
the parent entity's EnableFieldLevelSecurity flag
Returns true when this field appears as a parameter in the entity's
spCreate (when isUpdate=false) or spUpdate (when isUpdate=true)
stored procedure.
This is the single source of truth for the SP parameter contract, consumed by both:
• CodeGen, when emitting the SP body (which @params to declare
and which columns to INSERT/UPDATE), and
• Runtime data providers, via the RenderSaveCallBinding hook
implemented by SQLServerDataProvider and PostgreSQLDataProvider
(orchestrated by GenericDatabaseProvider.GenerateSaveSQL), when
building the EXEC / parameter list passed to the SP.
Keeping both sides on the same predicate guarantees the SP signature and the call-site argument list always agree. Drift between them surfaces as a SQL Server "Procedure or function ... has too many arguments specified" error at save time (or its PG equivalent).
Exclusion rules (in order):
• IsVirtual — view-only / joined columns. Not present in the base
table; the SP body never references them. Also covers IS-A parent
fields on child entities — those are saved via the parent's SP,
not the child's, so they must not appear in the child's SP params.
• IsComputed — SQL Server computed columns and PG generated
columns. Physically present in the base table but read-only at
the SQL layer (INSERT/UPDATE cannot target them). Today
IsComputed=1 implies IsVirtual=1, but checking both is
defensive against future decoupling of the flags.
• IsSpecialDateField — __mj_CreatedAt, __mj_UpdatedAt,
__mj_DeletedAt. Set inside the SP body / trigger from
GETUTCDATE() rather than by the caller.
• Non-PK fields without AllowUpdateAPI — by definition not
callable via the SP signature.
PK handling:
• On update, the PK is always a parameter (required to identify
the row).
• On create, the PK is a parameter only when it's NOT
auto-increment — the SP body declares it with a default so the
caller can either supply a value or let the database default fire
(e.g. NEWSEQUENTIALID()). For auto-increment PKs, the SP
intentionally doesn't expose the PK as a parameter.
true for spUpdate, false for spCreate.
Default JSON serialization for BaseInfo subclasses.
Emits all non-underscored direct field declarations. For _-prefixed private backing fields
(the MJ pattern for collection storage — e.g. _Fields, _RelatedEntities, _OrganicKeys),
emits the value of the corresponding same-named public getter instead. Purely computed getters
without a backing field (display-name formatters, derived flags) are intentionally skipped —
they can throw when source fields are null and don't belong on the wire anyway.
A _-backed getter that throws is omitted from the output rather than aborting the whole
serialization. That omission is safe ONLY because such getters are recomputable: the backing
field is a lazy cache over other serialized state (e.g. QueryInfo.CategoryPath over CategoryID),
so the value is rebuilt on the next access after a warm boot. Do not add a _-backed getter
whose value cannot be recomputed from the serialized fields — a throw would silently drop it.
Nested BaseInfo instances and arrays of them unwrap automatically via JSON.stringify's native toJSON() protocol.
Subclasses may override to emit a filtered subset or custom shape (see EntityFieldValueInfo).
Whether value is permitted by this field's exhaustive value list (MJ issue #3969).
A field whose ValueListType is List carries an exhaustive set of legal values in
__mj.EntityFieldValue, and for an IN (...) CHECK constraint that list is the ONLY runtime
representation CodeGen produces — ParseCheckConstraints emits the value list rather than a
generated Validate() method, since the list is also what the UI needs to render a dropdown.
So this is the only place such a constraint can be caught before the database refuses it as a
raw violation attributed to no field.
The normalized set is built ONCE per field and reused, because it derives from metadata that
is immutable after load and is shared by every EntityField instance of this field — at
import scale (thousands to millions of rows) rebuilding it per record is pure waste.
Four boundaries keep the rule safe to apply everywhere:
ListOrUserEntry is never checked — that mode exists precisely to permit values outside
the list, so validating it would break every field that opted into free text.List field with no EntityFieldValue rows permits everything. Strictly it describes
a field where nothing is legal, which should never exist; it means the metadata is
broken, not that every value is wrong, so this logs loudly (once per EntityFieldInfo
instance, which means it re-arms after a metadata refresh rather than being once ever)
and permits rather than failing every save on the field.'' and ' ' are the same value to a CHECK constraint and it refuses both. Skipping
them would leave a hole exactly where a blanked-out field lands.CK_EntityField_ValueListType_New constrains the
mode, not the column type), but in practice every one is a string column: measured on a
current 6.x instance, 455 nvarchar + 7 nchar and nothing else, which follows from
CodeGen's constraint parser only ever extracting quoted literals. number is admitted
because the generated union type anticipates a non-quoted list via NeedsQuotes.
Booleans and Dates are excluded deliberately: a bit column carrying a '1'/'0' list
would see String(true) === 'true' and reject every legal value, and a Date has no sane
string form to compare — so guessing there would break saves rather than guard them.TWO PRODUCERS, ONE OF WHICH HAS NO DATABASE FLOOR. A CHECK-derived list is safe by
construction: the database refuses anything this rung refuses, so validating can only move a
failure earlier. The other producer is applyValueListConfig in CodeGen, which applies
DBAutoDoc's LLM enum detection from additionalSchemaInfo — those fields have NO CHECK
constraint, so for them this rung converts a sampled, confidence-scored guess into a hard save
refusal for any value the model did not see. It is opt-in (the config must exist) and arguably
the intended reading of List as a closed set, with ListOrUserEntry available when unsure —
but it means "MJ never refuses what the database would accept" holds for the first producer
only.
the field's current runtime value
true when the value is permitted, INCLUDING when the rule does not apply
StaticAggregateThe same aggregation GetUserFieldPermissions performs, over a rule list the caller supplies rather than this field's stored one.
Exists so save-time guards can evaluate a prospective outcome — the rules as they would
stand after a proposed insert, edit or delete — instead of classifying a single row in
isolation. That distinction is load-bearing: whether a change restricts a user is a property
of the AGGREGATE across all the roles they hold, not of any one rule. A rule reading
No Access restricts nobody on its own, yet setting every one of a user's roles to
No Access leaves no Allow standing and denies the field outright.
the rules to aggregate — any shape carrying a RoleID and the three verbs
the user whose roles select which rules apply
StaticAssertThis static factory method is used to check to see if the entity field in question is active or not If it is not active, it will throw an exception or log a warning depending on the status of the entity field being either Deprecated or Disabled.
the EntityFieldInfo object to check the status of
the name of the caller that is calling this method, used for logging purposes such as EntityField::constructor as an example.
StaticIsChecks if a default value is a SQL Server function that returns the current date/time
The default value to check
true if the default value is a SQL current date/time function, false otherwise
StaticNormalizeNormalizes a value for comparison against a value list: stringified, trimmed, lower-cased.
Each part earns its place, and the reasons are NOT equally strong — stated precisely, because a future maintainer will use this to decide whether to tighten the comparison:
EntityField's constructor assigns
DefaultValue when no value is supplied), and two MJ core fields have a default that
matches their value list by CASE ALONE — MJ: Entity AI Actions.TriggerEvent defaults to
'After Save' against a list of before save | after save, and its OutputType defaults to
'FIeld' against entity | field. Under a case-sensitive comparison, creating either
record at its database default would fail validation. Separately, SQL Server's default
collation is case-insensitive, so Status = 'active' is accepted by
CHECK (Status IN ('Active', ...)) and refusing it here would turn a save that succeeds
today into a failure. (PostgreSQL IS case-sensitive, so on PG a case variant is still
refused — by its CHECK, not by this rung.)EntityFieldValue.Value is always a string in metadata while
the field's runtime value may be a number, so a strict === would reject every legal value
on a numeric list. It is not lossless: String(1.0) is '1', so a metadata value written
as '1.0' would fail closed. No numeric value lists exist today (CodeGen cannot produce
one — see the note in ValueIsPermittedByValueList), so this is recorded rather than solved.nchar columns (MJ: Action Params.Type, MJ: Record Changes.Status
and others). That measurement was taken over RAW SQL ROWS and does not describe this code
path: EntityField's value setter already strips trailing padding on fixed-width columns
(see FixedWidthColumn), and hydration assigns through that setter, so the padding is gone
before Validate() ever reads the value. Trimming is kept because it still covers LEADING
whitespace, stray spaces on the metadata side, and any caller that assigns a padded value
directly — none of which the setter handles.
List of all fields within each entity with metadata about each field. Includes data types, relationships, defaults, and UI display preferences.