Member Junction
    Preparing search index...

    Catalog of all entities across all schemas. Contains comprehensive metadata about each entity including its database mappings, security settings, and UI preferences.

    Hierarchy (View Summary)

    Index

    Constructors

    Properties

    Accessors

    Methods

    Constructors

    Properties

    __mj_CreatedAt: Date = null

    Date and time when this entity was created

    __mj_UpdatedAt: Date = null

    Date and time when this entity was last updated

    _configuration: string = null

    Raw string representation of Configuration from metadata.

    _floatCount: number = 0
    _hasIdField: boolean = false
    _manyToManyCount: number = 0
    _oneToManyCount: number = 0
    _virtualCount: number = 0
    AllowAllRowsAPI: boolean = false

    If true, allows querying all rows without pagination limits via API

    AllowCaching: boolean = false

    Controls whether this entity participates in server-side and client-side caching at all. When false (default for non-__mj entities), the entire cache code path is short-circuited: no PreRunView cache check, no auto-cache storage, no HandleBaseEntityEvent fingerprint scan, no client-side IndexedDB cache. Zero overhead on hot save/query paths.

    AllowCreateAPI: boolean = false

    Global flag controlling whether records can be created via API

    AllowDeleteAPI: boolean = false

    Global flag controlling whether records can be deleted via API

    AllowDirectSQLDelete: boolean = false

    Whether rows may be DELETEd by SQL that does not go through BaseEntity.Delete() — purge and retention routines, or integration sync reconciling against a remote source.

    false (the default) means all deletes are expected to flow through BaseEntity. See AllowDirectSQLInsert for the full rationale and the TrackRecordChanges / TrustServerCacheCompletely requirement.

    Additionally requires DeleteType to be 'Hard': a direct DELETE removes the row outright rather than setting DeletedAt, so sanctioning it on a soft-delete entity would quietly defeat soft delete. A database CHECK refuses the combination.

    AllowDirectSQLInsert: boolean = false

    Whether rows may be INSERTed by SQL that does not go through BaseEntity.Save() — bulk loads, ETL/integration sync, or rows created as a side effect of a stored procedure.

    false (the default, and every entity that has not opted in) means all inserts are expected to flow through BaseEntity, which is the only path where record-change tracking, entity actions, validation and cache invalidation actually run.

    This DECLARES intent; it enforces nothing. No constraint, trigger or grant prevents anyone from executing SQL. It exists so the code paths and tooling that choose to honour the platform contract — bulk/ETL and integration sync, record-set processing, and generators or agents authoring SQL — have one authoritative answer instead of tribal knowledge.

    A database CHECK requires TrackRecordChanges and TrustServerCacheCompletely to both be false when this is set, because a direct insert produces neither an audit row nor a cache-invalidation event — leaving either on yields an audit trail that looks complete but is not, and a server cache that serves stale rows indefinitely.

    AllowDirectSQLUpdate: boolean = false

    Whether rows may be UPDATEd by SQL that does not go through BaseEntity.Save() — bulk backfills, integration sync, or maintenance routines.

    false (the default) means all updates are expected to flow through BaseEntity. See AllowDirectSQLInsert for the full rationale, the "declares, does not enforce" caveat, and the TrackRecordChanges / TrustServerCacheCompletely requirement.

    AllowMultipleSubtypes: boolean = false

    When false (default), child types are disjoint — a record can only be one child type at a time. When true, a record can simultaneously exist as multiple child types (e.g., a Person can be both a Member and a Volunteer). This flag is set on the parent entity and controls whether its children are exclusive.

    AllowRecordMerge: boolean = null

    Whether records in this entity can be merged together

    AllowUpdateAPI: boolean = false

    Global flag controlling whether records can be updated via API

    AllowUserSearchAPI: boolean = false

    Whether users can search this entity through the search API

    AuditRecordAccess: boolean = null

    Whether to audit when users access records from this entity

    AuditViewRuns: boolean = null

    Whether to audit when views are run against this entity

    AutoRowCountFrequency: number = null
    • Field Name: AutoRowCountFrequency
    • Display Name: Auto Row Count Frequency
    • SQL Data Type: int
    • Description: Frequency in hours for automatically performing row counts on this entity. If NULL, automatic row counting is disabled. If greater than 0, schedules recurring SELECT COUNT(*) queries at the specified interval.
    AutoUpdateAllowUserSearchAPI: boolean = true

    Whether CodeGen automatically updates AllowUserSearchAPI from schema rules.

    AutoUpdateDescription: boolean = true
    • Field Name: AutoUpdateDescription
    • Display Name: Auto Update Description
    • SQL Data Type: bit
    • Default Value: 1
    • Description: When set to 1 (default), whenever a description is modified in the underlying view (first choice) or table (second choice), the Description column in the entity definition will be automatically updated. If you never set metadata in the database directly, you can leave this alone. However, if you have metadata set in the database level for description, and you want to provide a DIFFERENT description in this entity definition, turn this bit off and then set the Description field and future CodeGen runs will NOT override the Description field here.
    AutoUpdateFullTextSearch: boolean = true

    Whether CodeGen automatically updates FullTextSearchEnabled from database catalog/index availability.

    AutoUpdateSupportsGeoCoding: boolean = true

    When true (default), CodeGen can automatically set SupportsGeoCoding based on LLM analysis of entity fields. Set to false to lock the value.

    BaseTable: string = null

    The underlying database table name this entity maps to

    BaseTableCodeName: string = null
    BaseView: string = null

    The database view used as a "wrapper" for accessing this entity's data

    BaseViewGenerated: boolean = null

    Whether the base view is generated by CodeGen (true) or manually created (false)

    CanonicalSchemaName: string = null

    Case-stable canonical schema name, sourced from the app manifest (mj-app.json schema.name) and persisted on SchemaInfo. When non-null it is used in place of SchemaName to derive the schema prefix for the entity ClassName/CodeName and GraphQL type name, so PostgreSQL installs (whose physical SchemaName is folded to lowercase) still produce PascalCase prefixes matching the published, hand-cased entity packages. NULL means "no override" -> the prefix falls back to SchemaName (every existing install, the core __mj schema, and SQL Server).

    CascadeDeletes: boolean = null

    Whether to automatically delete related records when a parent is deleted

    ClassName: string = null
    CodeName: string = null

    CodeName is a unique name that can be used for various programatic purposes, singular version of the entity name but modified from entity name in some cases to remove whitespace and prefix with _ in the event that the entity name begins with a number or other non-alpha character

    CustomResolverAPI: boolean = false

    If true, uses a custom resolver for GraphQL operations instead of standard CRUD

    DeleteType: "Hard" | "Soft" = 'Hard'

    Type of delete operation: Hard (physical delete) or Soft (mark as deleted)

    Description: string = null

    Detailed description of the entity's purpose and contents

    DetectExternalChanges: boolean = false

    Whether external changes to records are detected.

    DisplayName: string = null

    Optional display name for the entity. If not provided, the entity Name will be used for display purposes.

    EnableFieldLevelSecurity: boolean = false

    Whether field-level (column-level) security is enforced for this entity.

    This is the single gate every field-security enforcement point checks first, and it is explicit — never inferred from whether permission rows happen to exist. It is false for nearly every entity in nearly every deployment, so enforcement collapses to one boolean test: no field iteration, no aggregation, no allocation.

    Turning it on snapshots the entity's existing entity-level permissions into per-field rows, so enabling changes no behavior until an administrator tightens a field. Turning it off leaves the rows in place, inactive, so re-enabling does not lose the configuration.

    EntityObjectSubclassImport: string = null

    Import statement for the entity's TypeScript subclass

    EntityObjectSubclassName: string = null

    Name of the TypeScript subclass for this entity if custom behavior is needed

    ExternalDataSourceID: string = null

    If set, this entity is backed by an external data source (Snowflake, MongoDB, external SQL/PostgreSQL/MySQL, ...) and is read-only. Reads are proxied live through the registered ExternalDataSourceReadRouter. Null = backed by the MJ database.

    ExternalObjectName: string = null

    Remote object name (table/view/collection) on the external system that backs this entity. Resolved against the data source defaults when unqualified. Only meaningful when ExternalDataSourceID is set.

    FullTextCatalog: string = null

    Name of the SQL Server full-text catalog used for searching

    FullTextCatalogGenerated: boolean = true

    Whether the full-text catalog is generated by CodeGen

    FullTextIndex: string = null

    Name of the full-text index on this entity

    FullTextIndexGenerated: boolean = true

    Whether the full-text index is generated by CodeGen

    FullTextSearchEnabled: boolean = null

    Whether full text search is enabled for this entity

    FullTextSearchFunction: string = null

    Name of the function used for full-text searching

    FullTextSearchFunctionGenerated: boolean = true

    Whether the full-text search function is generated by CodeGen

    GeneratedBaseViewName: string = null

    When set, CodeGen generates the entity's full base view under THIS name instead of BaseView, and the application owns BaseView — which is expected to wrap it:

    CREATE VIEW vwOrderHeaders AS
    SELECT g.*, CASE WHEN ... END AS IsOverdue
    FROM   vwOrderHeadersGenerated g
    

    This is how an entity gets a custom base view WITHOUT inheriting the generated SQL. With BaseViewGenerated = 0 alone the application takes over the whole view — every related-entity display join, the geo join, the recursive root-ID apply — and must hand-maintain it forever; a foreign key added later then silently never appears, because nothing regenerates the join. Naming an inner view keeps all of that regenerating underneath a thin, reviewable custom layer.

    NULL (the default, and every pre-existing entity) preserves the original behaviour exactly: BaseViewGenerated alone decides whether CodeGen writes BaseView, and there is no second view.

    BaseView remains the public surface either way — field discovery, permissions and the generated CRUD procedures all target it, so a column added by the custom layer becomes a first-class virtual EntityField.

    Icon: string = null

    CSS class or icon identifier for displaying this entity in the UI

    ID: string = null

    Unique identifier for the entity

    IncludeInAPI: boolean = false

    Whether this entity is available through the GraphQL API

    Name: string = null

    Unique name of the entity used throughout the system

    NameSuffix: string = null

    Optional suffix appended to entity names for display purposes

    ParentBaseTable: string = null
    ParentBaseView: string = null
    ParentEntity: number = null
    ParentID: string = null

    Reserved for future use - parent entity for hierarchical relationships

    PreferredCommunicationField: string = null

    Field name that contains the preferred communication method (email, phone, etc.)

    RelationshipDefaultDisplayType: "Search" | "Dropdown" = null

    Default display type for relationships: Search (type-ahead) or Dropdown

    RowCount: number = null
    • Field Name: RowCount
    • Display Name: Row Count
    • SQL Data Type: bigint
    • Description: Cached row count for this entity, populated by automatic row count processes when AutoRowCountFrequency is configured.
    RowCountRunAt: Date = null
    • Field Name: RowCountRunAt
    • Display Name: Row Count Run At
    • SQL Data Type: datetimeoffset
    • Description: Timestamp indicating when the last automatic row count was performed for this entity.
    RowsToPackSampleCount: number = 0
    • Field Name: RowsToPackSampleCount
    • Display Name: Rows To Pack Sample Count
    • SQL Data Type: int
    • Default Value: 0
    • Description: The number of rows to pack when RowsToPackWithSchema is set to Sample, based on the designated sampling method. Defaults to 0.
    RowsToPackSampleMethod: "random" | "top n" | "bottom n" = 'random'
    • Field Name: RowsToPackSampleMethod
    • Display Name: Rows To Pack Sample Method
    • SQL Data Type: nvarchar(20)
    • Default Value: random
    • Value List Type: List
    • Possible Values
      • random
      • top n
      • bottom n
    • Description: Defines the sampling method for row packing when RowsToPackWithSchema is set to Sample. Options include random, top n, and bottom n. Defaults to random.
    RowsToPackSampleOrder: string = null
    • Field Name: RowsToPackSampleOrder
    • Display Name: Rows To Pack Sample Order
    • SQL Data Type: nvarchar(MAX)
    • Description: An optional ORDER BY clause for row packing when RowsToPackWithSchema is set to Sample. Allows custom ordering for selected entity data when using top n and bottom n.
    RowsToPackWithSchema: "None" | "All" | "Sample" = 'None'
    • Field Name: RowsToPackWithSchema
    • Display Name: Rows To Pack With Schema
    • SQL Data Type: nvarchar(20)
    • Default Value: None
    • Value List Type: List
    • Possible Values
      • None
      • Sample
      • All
    • Description: Determines how entity rows should be packaged for external use. Options include None, Sample, and All. Defaults to None.
    SchemaName: string = null

    Database schema that contains this entity's table and view

    ScopeDefault: string = null
    • Field Name: ScopeDefault
    • Display Name: Scope Default
    • SQL Data Type: nvarchar(100)
    • Description: Optional, comma-delimited string indicating the default scope for entity visibility. Options include Users, Admins, AI, and All. Defaults to All when NULL. This is used for simple defaults for filtering entity visibility, not security enforcement.
    spCreate: string = null

    Name of the stored procedure for creating records

    spCreateGenerated: boolean = null

    Whether the create stored procedure is generated by CodeGen

    spDelete: string = null

    Name of the stored procedure for deleting records

    spDeleteGenerated: boolean = null

    Whether the delete stored procedure is generated by CodeGen

    spMatch: string = null

    Name of the stored procedure used for matching/duplicate detection

    spUpdate: string = null

    Name of the stored procedure for updating records

    spUpdateGenerated: boolean = null

    Whether the update stored procedure is generated by CodeGen

    Status: "Active" | "Disabled" | "Deprecated" = 'Active'
    • Field Name: Status
    • Display Name: Status
    • SQL Data Type: nvarchar(25)
    • Default Value: Active
    • Description: Status of the entity. Active: fully functional; Deprecated: functional but generates console warnings when used; Disabled: not available for use even though metadata and physical table remain.
    SubtypeSelector: string = null

    Optional JSON configuration specifying declarative prospective subtype resolution on an entity. Stored in the SubtypeSelector column of Entity (shape = IEntitySubtypeSelectorConfig).

    SupportsGeoCoding: boolean = false

    When true, this entity participates in geo read features: map view, distance calculations, and similar. That is independent of whether GeoCodeSyncService runs on Save — the service only fires when HasWritableGeoSourceFields is true. Auto-set by CodeGen when LLM detects geo-capable fields.

    TrackRecordChanges: boolean = null

    Whether to track all changes to records in the RecordChange table

    TrustServerCacheCompletely: boolean = true

    When true (default), the server-side RunView cache will store and return cached results for this entity, trusting that all mutations flow through BaseEntity.Save() which fires cache invalidation events. Set to false for entities whose rows are created as side-effects of other operations via raw SQL (e.g., Record Changes created by spCreateRecordChange_Internal), since those inserts bypass BaseEntity and never trigger cache invalidation.

    UserFormGenerated: boolean = null

    Whether the user form for this entity is generated by CodeGen

    UserViewMaxRows: number = null

    Maximum number of rows to return in user views to prevent performance issues

    VirtualEntity: boolean = null

    If true, this is a virtual entity not backed by a physical database table

    Accessors

    • get ChildEntities(): EntityInfo[]

      Returns all child entities that have their ParentID set to this entity's ID. These represent IS-A type specializations of this entity. Example: For "Products" entity, might return [Meetings, Publications].

      When AllowMultipleSubtypes is true on this entity, multiple children can coexist for the same parent record (overlapping subtypes). When false (default), only one child type is allowed per parent record (disjoint subtypes).

      Returns EntityInfo[]

    • get DatetimeFields(): EntityFieldInfo[]

      Returns an array of all fields whose TypeScript type is Date. Cached — used per query by the data providers' row post-processing to convert datetime values; recomputing the scan (and the per-field TSType classification) on every query is wasteful.

      Returns EntityFieldInfo[]

      Array of date/datetime fields

    • get DescendantEntities(): EntityInfo[]

      Every entity BELOW this one in the IS-A graph, at any depth — the downward twin of ParentChain, which already walks upward.

      ChildEntities is DIRECT children only, and that distinction is a trap: on Products → Meetings → Webinars, Products.ChildEntities omits Webinars entirely, so a "find every subtype" written against it misses everything past the first level. Not cached, because subtypes are discovered by scanning all entities and this is not a hot path; guarded against cycles the same way ParentChain is.

      Returns EntityInfo[]

    • get FieldCategories(): Record<string, FieldCategoryInfo>

      Gets the parsed FieldCategoryInfo map for this entity, keyed by category name. Auto-populated from the 'FieldCategoryInfo' EntitySetting (with legacy 'FieldCategoryIcons' fallback) during EntityInfo construction. Returns null if no category info is configured.

      Returns Record<string, FieldCategoryInfo>

    • get FirstPrimaryKey(): EntityFieldInfo

      Returns the primary key field for the entity. For entities with a composite primary key, use the PrimaryKeys property which returns all. In the case of a composite primary key, the PrimaryKey property will return the first field in the sequence of the primary key fields.

      This is a single-column convenience for the places MJ is single-column by design — foreign-key targets, keyset ORDER BY, IS-A shared keys, and the bare-value shorthand CompositeKey.LoadFromURLSegment accepts. Do not use it to construct a load key for an arbitrary entity: that silently drops every column but the first on a composite key. Build keys with CompositeKey.FromURLSegment(entityInfo, recordId) or CompositeKey.FromEntityRecord(entityInfo, row), which honor all of PrimaryKeys.

      Returns EntityFieldInfo

    • get GeneratedViewName(): string

      The view CodeGen actually WRITES for this entity.

      Normally BaseView. When GeneratedBaseViewName is set, the generated SQL goes there instead and BaseView belongs to the application, which layers over it — see HasLayeredBaseView.

      Resolved in ONE place because the two names must never drift: several call sites decide where to write the view, what to call the emitted file, and which object to refresh, and a disagreement between any two of them produces a view that exists under a name nothing reads.

      Derived FROM HasLayeredBaseView rather than re-testing GeneratedBaseViewName, so the two getters cannot disagree by construction. Testing the raw column here would diverge on a name that differs from BaseView only by case: HasLayeredBaseView would say "not layered" (it compares case-insensitively, because SQL Server object names are) while this getter returned the differently-cased string — leaving CodeGen writing to one object while every layering-gated code path believed there was no second view at all.

      Returns string

    • get HasInactiveFields(): boolean

      Returns true if ANY field on this entity is Deprecated or Disabled (i.e. not Active).

      Computed once on first access and cached for the lifetime of this EntityInfo. The value is a property of the entity definition (shared across every record instance), so the common case — an entity whose fields are all Active — is a single cached boolean.

      This is the fast-path gate for active-status enforcement in BaseEntity.Get/Set/SetMany: when it is false those paths skip the per-field status lookup entirely, keeping hot read/write loops free of any deprecation-check overhead.

      Returns boolean

    • get HasLayeredBaseView(): boolean

      True when this entity has a generated inner view with an application-owned BaseView on top.

      In that arrangement CodeGen still generates everything — related-entity display fields, geo columns, recursive root-ID columns — into GeneratedViewName, so the custom layer stays thin and does not go stale when the schema gains a foreign key.

      Returns boolean

    • get HasOverlappingSubtypes(): boolean

      Convenience alias: returns true when this entity is a parent type that allows overlapping (non-disjoint) subtypes. Equivalent to checking both IsParentType and AllowMultipleSubtypes.

      Returns boolean

    • get HasWritableGeoSourceFields(): boolean

      True when at least one field is a writable Geo* source (street and/or native lat/lng). Person/Org PrimaryAddress* are virtual display fields and do not count.

      Returns boolean

    • get IsARole(): "None" | "Root" | "Intermediate" | "Leaf"

      This entity's role in the IS-A graph as ONE value, for the common "what is this?" lookup.

      Intermediate is the case that makes booleans awkward: an entity can be a child AND a parent at once (Webinars IS-A Meetings IS-A Products makes Meetings both), so code that branches on IsChildType alone quietly mishandles the middle of every chain deeper than two.

      Returns "None" | "Root" | "Intermediate" | "Leaf"

    • get IsLeafType(): boolean

      Returns true when this entity is a LEAF of an IS-A hierarchy: it has a parent type and no subtypes of its own. A leaf is the only kind of IS-A entity that can be created by promotion without also being something else's parent.

      Returns boolean

    • get IsRootType(): boolean

      Returns true when this entity is the ROOT of an IS-A hierarchy: it has subtypes below it and no parent type above it. The root is where the shared primary key originates and where AllowMultipleSubtypes is decided, so it is the row most IS-A questions resolve back to.

      Returns boolean

    • get NameField(): EntityFieldInfo

      Returns the EntityField object for the Field that has IsNameField set to true. If multiple fields have IsNameField on, the function will return the first field (by sequence) that matches. If no fields match, if there is a field called "Name", that is returned. If there is no field called "Name", null is returned.

      Returns EntityFieldInfo

    • get ParentChain(): EntityInfo[]

      Walks the IS-A chain upward from this entity to the root, returning all parent entities. Does NOT include this entity itself. Example: For Webinars (IS-A Meetings IS-A Products), returns [Meetings, Products]. Results are cached after first computation for performance.

      Returns EntityInfo[]

    • get ParentEntityFieldNames(): Set<string>

      Returns a cached Set of field names that belong to parent entities in the IS-A chain, including the shared primary key(s). Used for efficient field routing in BaseEntity.Set/Get/SetMany/Hydrate operations. The Set enables O(1) lookup to determine if a field should be routed to the parent entity.

      Note: AllParentFields excludes PKs (they aren't "inherited data" fields), but the routing set must include them so that SetMany and Hydrate can forward the shared IS-A primary key to parent entities.

      Returns Set<string>

    • get ParentEntityInfo(): EntityInfo

      Returns the parent EntityInfo for IS-A type inheritance, or null if this entity has no parent type. Uses the existing ParentID column on the Entity table. Example: For "Meetings" entity with ParentID pointing to "Products", returns the Products EntityInfo.

      Returns EntityInfo

    • get ParticipatesInIsA(): boolean

      Returns true when this entity takes part in an IS-A hierarchy at all, in any role.

      The cheap guard for "does IS-A apply here?", which otherwise gets written as IsChildType || IsParentType at every call site — and gets written as just IsChildType about half the time, which silently skips every root and intermediate type.

      Returns boolean

    • get RootEntityInfo(): EntityInfo

      The ROOT entity of this entity's IS-A hierarchy — itself when it is already the root, and null when it takes part in no hierarchy.

      Saves every caller the "walk up until ParentEntityInfo is null" loop, which is where the cycle guard gets forgotten. Backed by ParentChain, which is cached and cycle-safe.

      Returns EntityInfo

    • get CreatedAtFieldName(): string

      Returns the name of the special reserved field that is used to store the CreatedAt timestamp across all of MJ. This is only used when an entity has TrackRecordChanges turned on

      Returns string

    • get DeletedAtFieldName(): string

      Returns the name of the special reserved field that is used to store the DeletedAt timestamp across all of MJ. This is only used when an entity has DeleteType=Soft

      Returns string

    • get UpdatedAtFieldName(): string

      Returns the name of the special reserved field that is used to store the UpdatedAt timestamp across all of MJ. This is only used when an entity has TrackRecordChanges turned on

      Returns string

    Methods

    • Copies 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.

      Parameters

      • initData: any

        The initialization data object

      Returns void

    • O(1) case-insensitive field lookup by name. Use this instead of Fields.find(f => f.Name === name) on hot paths — it builds a lowercased+trimmed Map once (lazily) and reuses it.

      NOTE: this is a field-within-entity index, distinct from the entity-level "Map-backed entity lookups" that were evaluated and skipped (~500 entities, negligible). Here a single entity can be read field-by-field in tight loops, so the index is worthwhile.

      Parameters

      • name: string

        field name (matched case-insensitively, whitespace-trimmed)

      Returns EntityFieldInfo

      the matching EntityFieldInfo, or undefined if not found

    • The set of field names this user may NOT supply a value for when CREATING a record. Same per-request precompute contract as GetDeniedReadFields.

      Unlike the update set, this does not drive a rejection: a value supplied for a create-denied field is dropped and the column takes its default.

      Parameters

      Returns Set<string>

    • The set of field names this user may NOT READ on this entity — the per-request primitive every field-security enforcement point is built on.

      Compute this ONCE per (entity, user) per request and pass the Set into any row loop. GetUserFieldPermissions is the per-FIELD primitive; calling it per row costs fields x rows aggregations (40,000 for a 1,000-row x 40-column result), each of which re-scans user.UserRoles and allocates. A Set lookup costs neither.

      Names are lowercased so callers can match case-insensitively, consistent with ProjectRowsToFields. Returns an EMPTY set — never null — both when the entity has field security switched off and when the user is denied nothing, so callers can treat size === 0 as the single "nothing to do" condition.

      Parameters

      Returns Set<string>

    • The set of field names this user may NOT UPDATE on this entity. Same per-request precompute contract as GetDeniedReadFields.

      A field can be readable and not updatable. The reverse cannot happen — Read is required for Update — so denied-read is always a subset of denied-update, but ask for the set you actually need rather than relying on that.

      Parameters

      Returns Set<string>

    • The EFFECTIVE row-filter clause for a user + permission type: role RLS (subject to the role exemption) AND the API-key row filters carried on the session (NOT subject to the role exemption — a key ceiling exists precisely to bind principals whose roles are unrestricted; see UserExemptFromRowLevelSecurity, which exempts off the mere presence of a filter-less permission row). Composition: OR within the role layer (roles are additive), AND across layers (no layer can widen another). Returns '' only when no layer contributes.

      This is THE method every enforcement point must call. Deterministic by construction — key-filter clauses render in FilterID order and list tokens sort their elements — because the identical clause participates in the RunView cache fingerprint (INV-2): any nondeterminism silently splits or merges cache slots.

      The application-ceiling layer (APIApplicationScope.RowFilterID) is deferred to v2 by design decision; the column ships unused and the term composes here when it lands.

      Parameters

      Returns string

    • Returns the Permissions for this entity for a given user, based on the roles the user is part of.

      Allow rows are OR-aggregated across all of the user's matching roles; any single Allow on an action yields permission for that action. Deny rows from any matching role then subtract from the aggregated Allow set — so a Deny on CanDelete overrides a Delete grant that the user otherwise has from another role. This lets administrators carve out specific role exclusions without restructuring the Allow hierarchy. Rows with a missing/unknown Type default to Allow for backwards compatibility with data written before the Type column existed (Phase 2b).

      Parameters

      Returns EntityUserPermissionInfo

    • Returns RLS security info attributes for a given user and permission type.

      Only permission rows that GRANT the operation contribute a filter: an Allow row whose matching Can* flag is true. Deny rows are skipped outright — on a Deny row a set Can* flag means "deny that operation", and a user carrying one fails the permission gate before this runs (GetUserPermisions subtracts Deny from Allow), so reading it as a grant would be wrong even though it is unreachable. The filters of a user's roles are OR'd together by the caller, so a filter collected from a row that does not grant the operation would WIDEN the clause: a user granted Create by role A (bound to filter F1) would create against F1 OR F2 when role B keeps a leftover CreateRLSFilterID = F2 beside CanCreate = false. GetUserPermisions aggregates the flags across roles, so such a user passes the permission gate on role A alone; nothing else stops F2 from applying. A user with no granting row gets no clause here — and no permission either.

      Parameters

      Returns RowLevelSecurityFilterInfo[]

    • Generates a where clause for SQL filtering for a given entity for a given user and permission type. If there is no RLS for a given entity or the user is exempt from RLS for the entity, a blank string is returned.

      Parameters

      Returns string

      ROLE RLS ONLY — this method is subject to the role-RLS exemption and silently omits API-key row filters, so a caller reaching for this familiar name gets a clause that is fail-open for filtered API-key sessions. Use GetEffectiveRowFilterWhereClause, which composes every filter layer. A repo test asserts no non-test caller exists outside that method.

    • Whether this user may supply a value for the named field when CREATING a record. Companion to IsFieldReadableByUser. Fails open on the same three conditions.

      Note the server does not REJECT a create-denied value — it drops it and takes the column default. So a UI that leaves such a field editable on a new record silently discards what the user typed, which is the case this exists to prevent.

      Parameters

      Returns boolean

    • Whether this user may READ the named field — the single-field question, answered the same way GetDeniedReadFields answers it in bulk.

      Exists for display code that is about to read a value it did not choose: a form toolbar rendering the entity's Name field, an FK control rendering the joined display column, an IS-A card walking a sibling record's fields. BaseEntity.Get() throws for a denied field, so those call sites have to ask before they read or they take out the whole screen instead of hiding one value.

      This is a PREDICATE, deliberately — not a value accessor that quietly returns nothing. The caller still decides what to render in place of the value, which is the part that differs per surface and should not be hidden inside a getter.

      Fails open on a missing user, a missing field name, or an entity with field security switched off — matching BaseEntity's own gate and MjFormFieldComponent. The server is the real boundary; a UI that blanked out fields because no user had resolved yet would be worse than one that shows them.

      PERFORMANCE: this delegates to GetDeniedReadFields, which walks every field on the entity. Fine for the handful of chrome reads it exists for; do NOT call it per row in a grid loop — compute the denied set once and test against it.

      Parameters

      • fieldName: string

        the field about to be read

      • user: UserInfo

        the acting user

      Returns boolean

    • Whether this user may UPDATE the named field. Companion to IsFieldReadableByUser, for UI that needs to render a control read-only rather than let a user type into something the server will reject on save. Fails open on the same three conditions.

      Parameters

      Returns boolean

    • 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).

      Returns Record<string, unknown>

    • Determines if a given user, for a given permission type, is exempt from RowLevelSecurity or not.

      A permission row confers an exemption only for an operation it GRANTS (the matching Can* flag is true) and leaves unfiltered. A row that does not grant the operation has no filter for it either, and that absence means "not applicable", not "unrestricted" — so it must not lift a filter that another of the user's roles binds. Without the Can* check, a role that grants only reads (CanCreate=false, hence CreateRLSFilterID=null) made every holder of that role exempt from CREATE row-level security, which is the shape of the 'UI' role every authenticated user holds on nearly every entity.

      Parameters

      Returns boolean

    • This static factory method is used to check to see if the entity 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 being either Deprecated or Disabled.

      Parameters

      • entity: EntityInfo

        the EntityInfo object to check the status of

      • callerName: string

        the name of the caller that is calling this method, used for logging purposes such as BaseEntity::constructor as an example.

      Returns void

    • Default field values for a new related record, setting every listed join field to the parent key. Use this when one grid filters on several FKs (Bill-To OR Ship-To) so "New" still auto-links the child to this parent.

      Parameters

      Returns Record<string, unknown>