Jump to content

Module:ConfigRepository: Difference between revisions

HARD FIX
Tag: Reverted
// via Wikitext Extension for VSCode
 
(251 intermediate revisions by the same user not shown)
Line 1: Line 1:
-- Module:ConfigRepository -- 2025-04-24 “hardened” edition
-- Module:ConfigRepository
-- Single source-of-truth for template configurations in ICANNWiki.
-- Single source of truth for all template configurations across the ICANNWiki ecosystem
----------------------------------------------------------------------
--
require('strict')
-- This module centralizes configuration data to ensure consistency, eliminate duplication,
-- and provide standardized structure for all templates. Works with the Template Blueprint
-- Framework to power rendering and semantic processing.
--
-- This module consists of three main parts:
-- 1. Global constants and properties shared across templates
-- 2. Template-specific configurations with standardized structure
-- 3. Configuration access functions for retrieving and standardizing configurations
--
-- Each template configuration provides a standardized structure with the following components:
--
-- meta:        Template metadata including description and rendering options
-- categories:  Base and conditional categories for the template
-- patterns:    Regular expression patterns for validation and parsing
-- fields:      Maps template field names in article source to display labels
-- mappings:    Canonical mappings for normalizing user input values
-- constants:  Template-specific constant values
-- semantics:  Semantic MediaWiki integration with the following sub-components:
--   properties:          Maps semantic properties to template fields (1:1 mapping)
--   additionalProperties: Maps properties to multiple possible template fields
--   transforms:          Functions to transform field values before storing as properties
--   skipProperties:      Properties to exclude from automatic processing


----------------------------------------------------------------------
local p = {}
-- reusable singletons ------------------------------------------------
local ErrorHandling = require('Module:ErrorHandling')
----------------------------------------------------------------------
local ConfigHelpers = require('Module:ConfigHelpers')
local DateFmt        = require('Module:NormalizationDate')
local CanonicalForms = require('Module:CanonicalForms')


-- large constants live in separate, read-only data blobs
------------------------------------------------------------------------------
local ClassicTLDs    = mw.loadData('Module:ConfigRepository/Data/ClassicTLDs')
-- Global Constants
------------------------------------------------------------------------------


----------------------------------------------------------------------
-- Global field labels used in multiple templates
local p = {}
p.fieldLabels = {
    -- Geographic
    city = "City",
    country = "Country",
    region = "Region",
 
    -- Temporal
    date = "Date",
    ending = "End",
    start = "Start",
 
    -- Others
    process = "Process",
    website = "Websites"
 
    -- IMPORTANT: "Logo" is processed automatically whenever it is called as a key by the Template Blueprint, in such a way that it does not need to be declared for any templates in this repository
}
 
-- Global field patterns used in multiple templates
p.fieldPatterns = {
    itemDelimiter = ";%s*",  -- Matches semicolon followed by optional whitespace
    websitePattern = "^https?://[^%s]+"  -- Matches URLs starting with http:// or https://
}


----------------------------------------------------------------------
-- Global field processors for reusable field definitions
-- Global constants ---------------------------------------------------
p.fieldProcessors = {
----------------------------------------------------------------------
     website = {
p.fieldLabels = {
        list = { mode = 'bullet_custom', bulletChar = '🔗', postProcess = 'website' }
     region  = 'Region',
     }
    country = 'Country',
    date    = 'Date',
     website = 'Website',
}
}


p.dateFormatting = { useShortMonthNames = true }
-- Global canonical mappings used in multiple templates
p.mappings = {
    -- PROCESS (Property:Has process connection)
    process = {
        {canonical = "AFRINIC",
        category  = "AFRINIC"},
        {canonical = "APNIC",
        category  = "APNIC"},
        {canonical = "ARIN",
        category  = "ARIN"},
        {canonical = "DNS-OARC",
        category  = "DNS-OARC"},
        {canonical = "EuroDIG",
        category  = "EuroDIG"},
        {canonical = "ICANN",
        category  = "ICANN"},
        {canonical = "IEEE",
        category  = "IEEE"},
        {canonical = "IETF",
        category  = "IETF"},
        {canonical = "IGF",
        category  = "IGF"},
        {canonical = "ITU",
        category  = "ITU"},
        {canonical = "LACNIC",
        category  = "LACNIC"},
        {canonical = "RIPE NCC",
        category  = "RIPE NCC"},
        {canonical = "W3C",
        category  = "W3C"},
        {canonical = "WSIS",
        category  = "WSIS"}
    }
}


-- Global semantic property names used in multiple templates (Cross-template semantic queries)
p.semanticProperties = {
p.semanticProperties = {
     region  = 'Has ICANN region',
     country = "Has country",
     country = 'Has country',
     region = "Has ICANN region",
     language= 'Knows language',
     language = "Knows language",
     person = 'Has person',
     person = "Has person",
}
}


----------------------------------------------------------------------
-- Global date formatting configuration
-- Template definitions ----------------------------------------------
-- When true, use "Apr. 21, 2025" format; when false, use "April 21, 2025"
----------------------------------------------------------------------
p.dateFormatting = {
p.templates = {}
    useShortMonthNames = true
}


----------------------------------------------------------------------
-- Global campaigns configuration
-- EVENT --------------------------------------------------------------
-- Campaigns are defined centrally and specify which templates they can be applied to
----------------------------------------------------------------------
p.campaigns = {
do
     ASP2025 = {
     local t = {
         name = "ASP 2025 Pro Bono Service Provider",
         meta = { description = 'Event template' },
        applicable_templates = {"Person", "Organization"},
        json_config = "ASP2025"
    }
}


         categories = { base = { 'Events' } },
-- Global page creator fields for TemplateStarter form generation
p.pageCreatorFields = {
    PAGE_NAME = {
        key = "PAGE_NAME",
        label = "Page Name",
        placeholder = "Enter page name...",
        required = true
    },
    COUNTRY = {
        key = "COUNTRY",
        label = "Country",
         placeholder = "Enter country (optional)...",
        required = false
    },
    YEAR = {
        key = "YEAR",
        label = "Year",
        placeholder = "Enter year (optional)...",
        required = false
    },
    ORGANIZATION_TYPE = {
        key = "ORGANIZATION_TYPE",
        label = "Organization Type",
        placeholder = "Enter organization type (optional)...",
        required = false
    },
    OCCUPATION = {
        key = "OCCUPATION",
        label = "Occupation",
        placeholder = "Enter occupation (optional)...",
        required = false
    },
    NORM_TYPE = {
        key = "NORM_TYPE",
        label = "Type of Norm",
        placeholder = "Enter norm type (optional)...",
        required = false
    }
}


------------------------------------------------------------------------------
-- Transform registry mapping string keys to functions
------------------------------------------------------------------------------
p.transformRegistry = {
    date = function(value)
        return tostring(require('Module:NormalizationDate').formatDate(value))
    end,
    normalize = function(value, mappings)
        -- Return original if mappings invalid
        if type(mappings) ~= 'table' or #mappings == 0 then
            return value
        end
        local CanonicalForms = require('Module:CanonicalForms')
        local canonical = select(1, CanonicalForms.normalize(value, mappings))
        return canonical or value
    end,
    semantic = function(value, templateName)
        return require('Module:TemplateHelpers').extractSemanticValue(value, templateName)
    end,
    boolean = function(value)
        if value and value ~= "" then
            return "true"
        end
    end,
    raw = function(value)
        return value
    end,
    string = function(value)
        return tostring(value or "")
    end
}
------------------------------------------------------------------------------
-- TEMPLATE-SPECIFIC CONFIGURATIONS
------------------------------------------------------------------------------
p.templates = {
------------------------------------------------------------------------------
-- ANCHOR: EVENT TEMPLATE
------------------------------------------------------------------------------
    Event = {
        meta = {
            description = "Event template"
        },
        categories = { -- Default categories
            base = {"Events"}
        },
        boilerplate = {
            intro = "'''$PAGE_NAME$''' was an event that took place in $COUNTRY$ in $YEAR$."
        },
        creatorFields = {"PAGE_NAME", "COUNTRY", "YEAR"},
        mappings = {
            -- Process (Property:Has process connection)
            process = p.mappings.process
        },
         patterns = {
         patterns = {
             seriesNumber = '^([%w%s]+)%s+(%d+)$',     -- e.g. ICANN 76
            -- Patterns for event navigation detection
             seriesYear   = '^([%w%s]+)%s+(%d%d%d%d)$', -- e.g. IGF 2023
             seriesNumber = "^([%w%s]+)%s+(%d+)$",     -- e.g., "ICANN 76"
             seriesYear = "^([%w%s]+)%s+(%d%d%d%d)$", -- e.g., "IGF 2023"
            itemDelimiter = p.fieldPatterns.itemDelimiter,
            websitePattern = p.fieldPatterns.websitePattern
        },
        fields = {
            {key="process", label=p.fieldLabels.process, autoWikiLink=true},
            {key="start", label=p.fieldLabels.start},
            {key="ending", label=p.fieldLabels.ending},
            {key="region", label=p.fieldLabels.region},
            {key="country", label=p.fieldLabels.country},
            {key="city", label=p.fieldLabels.city},
            {key="venue", label="Venue"},
            {key="organizer", label="Organizer"},
            {keys={"website", "url"}, label=p.fieldLabels.website, list=p.fieldProcessors.website.list},
         },
         },
        semantics = {
            properties = {
                ["Has process connection"] = "process",
                ["Has start date"] = "start",
                ["Has end date"] = "ending",
                ["Has city"] = "city",
                ["Has venue"] = "venue",
                ["Has event organizer"] = "organizer"
            },
            -- Normalizations applied
            transforms = {
                ["Has start date"] = "date",
                ["Has end date"] = "date"
            },
            fixedProperties = {
                ["Has entity type"] = "Event"
            }
        }
    },


------------------------------------------------------------------------------
-- ANCHOR: LIBRARY INTERVIEW TEMPLATE
------------------------------------------------------------------------------
    LibraryInterview = {
        meta = {
            description = "Library Interview template",
            page_creator = false
        },
        categories = { -- Default categories
            base = {
                "Internet & Digital Governance Library",
                "ICANNWiki Interviews"
            }
        },
        constants = { -- REVIEW
            title = "Internet & Digital Governance Library",
            tableClass = "library-box"
        },
         fields = {
         fields = {
             {key='logo'    ,label='Logo'},
             {key="title", label="Title"},
             {key='process'  ,label='Process'},
             {key="format_interview", label="Format"},
             {key='start'    ,label='Start Date'},
             {key="date", label=p.fieldLabels.date},
            {key='end'      ,label='End Date'},
             {key="interviewer", label="Interviewer", autoWikiLink=true},
            {key='region'  ,label=p.fieldLabels.region},
             {key="interviewee", label="Interviewee", autoWikiLink=true},
            {keys={'country','territory'},label=p.fieldLabels.country},
             {key="id", label="Permanent ID"}
             {key='city'    ,label='City'},
            {key='venue'    ,label='Venue'},
             {key='organizer',label='Organizer'},
            {keys={'website','url'},label=p.fieldLabels.website},
             {keys={'subject','category'},label='Subject'},
         },
         },
         semantics = {
         semantics = {
            -- Direct property mappings (1:1 relationship between property and field)
             properties = {
             properties = {
                 ['Has start date'  ] = 'start',
                 ["Has interview format"] = "format_interview",
                 ['Has end date'    ] = 'end',
                 ["Has date"] = "date",
                ['Part of process'  ] = 'process',
                 ["Has interviewer"] = "interviewer",
                 ['Has city'        ] = 'city',
                 ["Has interviewee"] = "interviewee",
                 ['Has venue'        ] = 'venue',
                 ["Permanent ID"] = "id"
                 ['Has event organizer'] = 'organizer',
             },
             },
 
            -- Properties that map to multiple possible fields
             additionalProperties = {
             additionalProperties = {
                 ['Has country'] = {'country','territory'},
                 ["Has person"] = {"interviewer", "interviewee"} -- Links interviews to person pages
             },
             },
 
            -- Normalizations applied
             transforms = {
             transforms = {
                 ['Has start date'] = function(v) return tostring(DateFmt.formatDate(v)) end,
                 ["Has date"] = "date",
                 ['Has end date'  ] = function(v) return tostring(DateFmt.formatDate(v)) end,
                 ["Permanent ID"] = "string"
             },
             },
            fixedProperties = {
                ["Has entity type"] = "Library"
            }
        }
    },
------------------------------------------------------------------------------
-- ANCHOR: NORM TEMPLATE
------------------------------------------------------------------------------


             skipProperties = {
    Norm = {
                ['Has country'     ] = true,
        meta = {
                 ['Has ICANN region' ] = true,
            description = "Norm template for laws, regulations, standards, etc."
                 ['Has event subject'] = true,
        },
        categories = {
             base = {"Norms"}
        },
        boilerplate = {
            intro = "'''$PAGE_NAME$''' is a $NORM_TYPE$ proposed in $YEAR$."
        },
        creatorFields = {"PAGE_NAME", "NORM_TYPE", "YEAR"},
        mappings = {
            norm_type = {
                 {canonical = "Act", category = "Acts"},
                {canonical = "Agreement", category = "Agreements"},
                {canonical = "Framework", category = "Frameworks"},
                {canonical = "Law", category = "Laws"},
                {canonical = "Regulation", category = "Regulations"},
                {canonical = "Standard", category = "Standards"},
                {canonical = "Policy", category = "Policies"},
                {canonical = "Guideline", category = "Guidelines"},
                {canonical = "Treaty", category = "Treaties"},
                {canonical = "Decree", category = "Decrees"},
                {canonical = "Ordinance", category = "Ordinances"},
                {canonical = "By-law", category = "By-laws"},
                {canonical = "Code", category = "Codes"},
                {canonical = "Convention", category = "Conventions"},
                {canonical = "Directive", category = "Directives"}
            },
            scope_geo = {
                {canonical = "Global", category = "Global Norms"},
                {canonical = "Regional", category = "Regional Norms"},
                 {canonical = "National", category = "National Norms"}
             },
             },
            norm_status = { -- Key used in templatedata
                {canonical = "Active", category = "Active Norms"},
                {canonical = "Repealed", category = "Repealed Norms"},
                {canonical = "Draft", category = "Draft Norms"},
                {canonical = "Proposed", category = "Proposed Norms"},
                {canonical = "Superseded", category = "Superseded Norms"},
                {canonical = "Amended", category = "Amended Norms"}
            }
        },
        fields = {
            {key="norm_title", label="Norm title"},
            {key="alternative_title", label="Alternative name"}, -- Key used in templatedata
            {key="norm_type", label="Type of norm"},
            {key="issuing_body", label="Issuing body", autoWikiLink=true},
            {key="scope_geo", label="Geographic scope"},
            {key="country", label=p.fieldLabels.country},
            {key="norm_status", label="Status"}, -- Key used in templatedata
            {key="official_text", label="Official text"},
            {key="related_norm", label="Related Norms", list="bullet"},
            {key="parent_framework", label="Parent legal framework", autoWikiLink=true},
            {key="norm_language", label="Language"}
         },
         },
     }
        semantics = {
            properties = {
                ["Has alternative title"] = "alternative_title",
                ["Has norm type"] = "norm_type",
                ["Has issuing body"] = "issuing_body",
                ["Has geographic scope"] = "scope_geo",
                ["Has status"] = "norm_status",
                ["Links to official text"] = "official_text",
                ["Is related to norm"] = "related_norm",
                ["Is part of framework"] = "parent_framework",
                ["Has language"] = "norm_language"
            },
            transforms = {
                ["Has norm type"] = "normalize",
                ["Has geographic scope"] = "normalize",
                ["Has status"] = "normalize",
                ["Has issuing body"] = "semantic",
                ["Is related to norm"] = "semantic",
                ["Is part of framework"] = "semantic",
                ["Links to official text"] = "string",
                ["Has language"] = "string"
            },
            fixedProperties = {
                ["Has entity type"] = "Norm"
            }
        }
     },


    p.templates.Event = t
------------------------------------------------------------------------------
end
-- ANCHOR: ORGANIZATION TEMPLATE
------------------------------------------------------------------------------


----------------------------------------------------------------------
    Organization = {
-- PERSON -------------------------------------------------------------
        meta = {
----------------------------------------------------------------------
            description = "Organization template"
do
        },
     local communityMap -- forward declaration for up-value capture
        categories = { -- Default categories
            base = {"Organization"}
        },
        boilerplate = {
            intro = "'''$PAGE_NAME$''' is a $ORGANIZATION_TYPE$ founded in $YEAR$."
        },
        creatorFields = {"PAGE_NAME", "ORGANIZATION_TYPE", "YEAR"},
        mappings = {
            organization_type = {
            -- organization_type (Property:Has organization type)
                {canonical = "Academic institution",
                    synonyms = {"university", "college", "academia", "institute", "school"},
                    category = "Academic institution"},
                {canonical = "ALS",
                    synonyms = {"at-large structure", "at large structure", "at-large", "at large"},
                    category = "ALS"},
                {canonical = "Commercial",
                    synonyms = {"for-profit", "for profit", "business", "private", "private company", "privately held", "company", "publicly held", "public", "joint stock company", "corporation", "corp", "firm", "enterprise", "limited liability", "llc", "gmbh"},
                    category = "Commercial"},
                {canonical = "Community network",
                    synonyms = {"community"},
                    category = "Community network"},
                {canonical = "Consortium",
                    synonyms = {"alliance", "joint venture", "joint-venture"},
                    category = "Consortium"},
                {canonical = "Cooperative",
                    synonyms = {"co-op", "coop", "co-operative"},
                    category = "Cooperative"},
                {canonical = "Government agency",
                    synonyms = {"government", "govt", "regulator", "regulatory authority", "nra", "bureau"},
                    category = "Government agency"},
                {canonical = "Industry association",
                    synonyms = {"trade association", "industry group", "trade group"},
                    category = "Industry association"},
                {canonical = "Intergovernmental organization",
                    synonyms = {"igo", "ingo"},
                    category = "Intergovernmental organization"},
                {canonical = "Internet Society Chapter",
                    synonyms = {"isoc chapter"},
                    category = "Internet Society Chapter"},
                {canonical = "Non-profit",
                    synonyms = {"nonprofit", "not-for-profit", "not for profit", "charity", "foundation", "economic forum", "ngo", "association", "non-profit organization"},
                    category = "Non-profit"},
                {canonical = "Public-private partnership",
                    synonyms = {"public-private partnership", "ppp"},
                    category = "Public-private partnership"},
                {canonical = "Research institute",
                    synonyms = {"research", "institute", "think tank"},
                    category = "Research institute"},
                {canonical = "Standards development organization",
                    synonyms = {"sdo", "standards body", "standards organisation, standards"},
                    category = "Standards development organization"}
            }
        },
        patterns = {
            itemDelimiter = p.fieldPatterns.itemDelimiter,
            websitePattern = p.fieldPatterns.websitePattern
        },
        fields = {
            {key="organization_type", label="Type"},
            {key="focus", label="Focus"},
            {key="region", label=p.fieldLabels.region},
            {key="country", label=p.fieldLabels.country},
            {key="city", label=p.fieldLabels.city},
            {key="date_founded", label="Founded"},
            {key="founders", label="Founders", autoWikiLink=true, list="bullet"},
            {key="ownership", label="Ownership", autoWikiLink=true},
            {key="subsidiaries", label="Subsidiaries", list="bullet"},
            {keys={"website", "url"}, label=p.fieldLabels.website, list=p.fieldProcessors.website.list},
        },
        semantics = {
            properties = {
                ["Has organization type"] = "organization_type",
                ["Has focus"] = "focus",
                ["Has city"] = "city",
                ["Date founded"] = "date_founded",
                ["Has owner"] = "ownership",
                ["Has subsidiary"] = "subsidiaries"
            },
            -- Normalizations applied
            transforms = {
                -- None
            },
            fixedProperties = {
                ["Has entity type"] = "Organization"
            }
        }
     },


    local t = {
------------------------------------------------------------------------------
        meta = { description = 'Person template' },
-- ANCHOR: PERSON TEMPLATE
 
------------------------------------------------------------------------------
        categories = { base = { 'Person' } },


    Person = {
        meta = {
            description = "Person template"
        },
        categories = { -- Default categories
            base = {"Person"}
        },
        boilerplate = {
            intro = "'''$PAGE_NAME$''' is a $OCCUPATION$ based in $COUNTRY$."
        },
        creatorFields = {"PAGE_NAME", "OCCUPATION", "COUNTRY"},
         mappings = {
         mappings = {
             community = { -- shortened for brevity; full list unchanged
            -- REVIEW Community (Property:Has governance community)
                 { canonical = 'ICANN Community', synonyms = {'icann','community'}, category = 'ICANN Community' },
             community = {
                 { canonical = 'ICANN Staff'    , synonyms = {'staff','icann org'}, category = 'ICANN Staff' },
                -- Broad definition
                 -- … (other entries identical) …
                 {canonical = "Internet Governance Community",
             },
                    synonyms = {"ig", "internet governance"},
                    category = "Internet Governance Community"},
                -- ICANN-related communities
                {canonical = "ICANN Community",
                    synonyms = {"icann", "community"},
                    category = "ICANN Community"},
                 {canonical = "ICANN Staff", -- Category provided via "badge-icann-staff"
                    synonyms = {"staff", "icann org"},},
                -- Technical communities
                {canonical = "Technical Community",
                    synonyms = {"technical"},
                    category = "Technical Community"},
                {canonical = "Root Server Operator Community",
                    synonyms = {"root server operator", "rso"},
                    category = "Root Server Operator Community"},
                {canonical = "RIR Community",
                    synonyms = {"rir"},
                    category = "RIR Community"},
                {canonical = "Universal Acceptance Community",
                    synonyms = {"universal acceptance", "ua", "uasg", "idn", "idn community"},
                    category = "Universal Acceptance Community"},
                -- Internet governance organizations
                {canonical = "ISOC Community",
                    synonyms = {"isoc", "internet society", "isoc member"},
                    category = "ISOC Community"},
                {canonical = "IETF Community",
                    synonyms = {"ietf", "ietf member"},
                    category = "IETF Community"},
                {canonical = "IGF Community",
                    synonyms = {"igf", "nri", "youth igf"},
                    category = "IGF Community"},
                {canonical = "W3C Community",
                    synonyms = {"w3c", "w3c member"},
                    category = "W3C Community"},
                 -- Government-related
                {canonical = "Governmental",
                    synonyms = {"government", "public sector", "gac"},
                    category = "Governmental"},
                {canonical = "Intergovernmental",
                    synonyms = {"igo", "ingo"},
                    category = "Intergovernmental"}
             }
         },
         },
         fields = {
         fields = {
             {key='community,label='Community'},
             {key="community", label="Community"},
             {key='affiliation',label='ICANN group'},
             {key="icann_group", label="ICANN SO/AC", autoWikiLink=true}, -- (Property:Has ICANN affiliation)
             {key='organization',label='Organization'},
             {key="organization", label="Organization", autoWikiLink=true, list="bullet"},
             {key='region'    ,label=p.fieldLabels.region},
             {key="region", label=p.fieldLabels.region, list="bullet"},
             {key='country'    ,label=p.fieldLabels.country},
             {key="country", label=p.fieldLabels.country},
             {key='languages' ,label='Languages'},
             {key="languages", label="Languages", list = { mode = 'bullet_custom', bulletChar = '🗣️', postProcess = 'language' }},
             {key='website'    ,label=p.fieldLabels.website},
             {key="website", label=p.fieldLabels.website, list=p.fieldProcessors.website.list},
             {key='soi'        ,label='SOI'},
             {key="soi", label="SOI"},
             {key='userbox'    ,label='Achievements'},
             {key="userbox", label="Achievements"},
            {key="user", label="User", hidden=true}
         },
         },
         patterns = {
         patterns = {
             itemDelimiter = ';%s*',
             itemDelimiter = p.fieldPatterns.itemDelimiter,
             websitePattern = '^https?://[^%s]+',
             websitePattern = p.fieldPatterns.websitePattern
         },
         },
        semantics = {
            properties = {
                ["Has governance community"] = "community",
                ["Has ICANN affiliation"] = "icann_group",
                ["Has organization"] = "organization",
                ["Has username"] = "user"
            },
            -- Normalizations applied
            transforms = {
                ["Has governance community"] = "normalize"
            },
            fixedProperties = {
                ["Has entity type"] = "Person"
            }
        }
    },
------------------------------------------------------------------------------
-- ANCHOR: PROCESS TEMPLATE
------------------------------------------------------------------------------


    Process = {
        meta = {
            description = "Process Template",
            templateId = "process"
        },
        categories = {
            base = {"Processes", "Events"}
        },
        boilerplate = {
            intro = "'''$PAGE_NAME$''' is a process started in $YEAR$."
        },
        creatorFields = {"PAGE_NAME", "YEAR"},
        mappings = {
            -- Process (Property:Has process connection)
            process = p.mappings.process,
            -- Model (Property:Has process model)
            model = {
                {canonical = "Multistakeholder",
                    synonyms = {"multi-stakeholder", "multi stakeholder"},
                    category = "Multistakeholder Processes"},
                {canonical = "Single stakeholder",
                    synonyms = {"single-stakeholder", "intergovernmental"},
                    category = "Single Stakeholder Processes"}
            },
            -- Status (Property:Has process status)
            status = {
                {canonical = "Active",
                    synonyms = {"ongoing"},
                    category = "Active Processes"},
                {canonical = "Paused",
                    synonyms = {"inactive", "dormant"},
                    category = "Dormant Processes"},
                {canonical = "Concluded",
                    synonyms = {"completed", "ended", "finished"},
                    category = "Concluded Processes"}
            },
            -- Frequency (Property:Has frequency)
            frequency = {
                {canonical = "One-off",
                    synonyms = {"one off", "single"},
                    category = "One-off Processes"},
                {canonical = "Ad-hoc",
                    synonyms = {"ad hoc", "eventual"},
                    category = "Ad-hoc Processes"},
                {canonical = "Periodic",
                    synonyms = {"recurring", "regular", "annual", "yearly"},
                    category = "Periodic Processes"},
                {canonical = "Continuous",
                    synonyms = {"permanent"},
                    category = "Continuous Processes"}
            },
            -- Scope (Property:Has geographic scope)
            scope_geo = {
                {canonical = "Global",
                    synonyms = {"worldwide", "world", "international"},
                    category = "Global Processes"},
                {canonical = "Regional",
                    synonyms = {"continent", "continental"},
                    category = "Regional Processes"},
                {canonical = "National",
                    synonyms = {"country", "domestic"},
                    category = "National Processes"}
            }
        },
        fields = {
            {key="process", label=p.fieldLabels.process, autoWikiLink=true},
            {key="model", label="Model"},
            {key="start", label=p.fieldLabels.start},
            {key="ending", label=p.fieldLabels.ending},
            {key="process_status", label="Status"},
            {key="governing_body", label="Governing Body", autoWikiLink=true},
            {key="secretariat", label="Secretariat", autoWikiLink=true},
            {key="frequency", label="Frequency"},
            {key="scope_geo", label="Scope"},
            {key="region", label=p.fieldLabels.region},
            {key="country", label=p.fieldLabels.country},
            {key="website", label=p.fieldLabels.website},
            {key="has_previous", label="Preceded By", hidden=true},
            {key="has_next", label="Succeeded By", hidden=true},
        },
         semantics = {
         semantics = {
             properties = {
             properties = {
                 ['Has governance community'] = 'community',
                 ["Has start date"] = "start",
                 ['Has ICANN affiliation']   = 'affiliation',
                 ["Has end date"] = "ending",
                 ['Has organization']         = 'organization',
                 ["Has process status"] = "process_status",
                ["Has governing body"] = "governing_body",
                ["Has event organizer"] = "secretariat",
                ["Has process model"] = "model",
                ["Has frequency"] = "frequency",
                ["Has geographic scope"] = "scope_geo",
                ["Has process connection"] = "process"
             },
             },
             additionalProperties = {
             additionalProperties = {
                 ['Has country']     = {'country'},
                 ["Has process connection"] = {"process", "has_previous", "has_next"}
                ['Has ICANN region'] = {'region'},
             },
             },
 
            -- Normalizations applied
             transforms = {
             transforms = {
                 ['Has governance community'] = function(v)
                 ["Has start date"] = "date",
                    return select(1, CanonicalForms.normalize(v, communityMap)) or v
                ["Has end date"] = "date",
                 end,
                ["Has process status"] = "normalize",
                 ['Knows language'] = function(v) return v end,
                ["Has process model"] = "normalize",
                 ["Has frequency"] = "normalize",
                 ["Has geographic scope"] = "normalize",
                ["Has process connection"] = "semantic"
             },
             },
            fixedProperties = {
                ["Has entity type"] = "Process"
            }
        }
    },
------------------------------------------------------------------------------
-- ANCHOR: TLD TEMPLATE
------------------------------------------------------------------------------


             skipProperties = {
    TLD = {
                 ['Has country']      = true,
        meta = {
                 ['Has ICANN region'] = true,
            description = "TLD Template"
        },
        categories = {
             base = {}, -- No default category for TLDs as they use type-based categories
            conditional = {
                 rvc = "TLDs with RVCs",
                 idn = "IDN",
                idn_cctld = "IDN ccTLD"
             },
             },
         },
         },
    }
         boilerplate = {
 
             intro = "'''$PAGE_NAME$''' is a TLD proposed in $YEAR$."
    communityMap = t.mappings.community
    p.templates.Person = t
end
 
----------------------------------------------------------------------
-- TLD ----------------------------------------------------------------
----------------------------------------------------------------------
do
    local typeMap, subtypeMap
 
    local t = {
        meta = { description = 'TLD/ccTLD template' },
 
         categories = {
             base      = {},
            conditional= { rvc='TLDs with RVCs', idn='IDN', idn_cctld='IDN ccTLD' },
         },
         },
 
        creatorFields = {"PAGE_NAME", "YEAR"},
         mappings = {
         mappings = {
             type = {
             -- Type (Property:Has TLD type)
                 {canonical='gTLD', synonyms={'generic','tld', 'generic top level domain','generic top-level domain','generic tld'}, category='gTLD'},
            tld_type = {
                 {canonical='ccTLD',synonyms={'country','cc','country code top level domain','country code top-level domain','country tld'}, category='ccTLD'},
                 {canonical = "gTLD",
            },
                    synonyms = {"generic", "tld", "generic top level domain", "generic top-level domain", "generic tld"},
            subtype = {
                    category = "gTLD"},
                {canonical='geoTLD',          synonyms={'geo tld','geo', 'geographic','geographical','geographic top level domain','geographic top-level domain','geographic tld'}, css='tld-template-subtype-geotld',            category='geoTLD'},
                 {canonical = "ccTLD",
                {canonical='dotBrand',        synonyms={'brand','brandtld','brand tld','brand top level domain','brand top-level domain'},                                              css='tld-template-subtype-brandtld',        category='dotBrand'},
                    synonyms = {"country", "cc", "country code top level domain", "country code top-level domain", "country tld"},
                {canonical='Sponsored TLD',  synonyms={'sponsored','sponsored top level domain','sponsored top-level domain'},                                                        css='tld-template-subtype-sponsored',      category='Sponsored TLD'},
                    category = "ccTLD"}
                {canonical='Legacy TLD',      synonyms={'legacy','legacy top level domain','legacy top-level domain'},                                                                css='tld-template-subtype-legacytld',      category='Legacy TLD'},
                {canonical='2012 gTLD Round', synonyms={'gtld round 2012','2012 ngtld round','2012 ngtld','ngtld 2012','ngtld','2012'},                                                css='tld-template-subtype-ngtld-round-2012',category='2012 gTLD Round'},
             },
             },
            -- Subtype (Property:Has TLD subtype)
            tld_subtype = {
                {canonical="geoTLD",
                    synonyms={"geo tld","geo","geographic","geographical","geographic top level domain","geographic top-level domain","geographic tld"},
                    css="tld-template-subtype-geotld",
                    category="geoTLD"},
                {canonical="dotBrand",
                    synonyms={"brand","brandtld","brand tld","brand top level domain","brand top-level domain"},
                    css="tld-template-subtype-brandtld",
                    category="dotBrand"},
                {canonical="Sponsored TLD",
                    synonyms={"sponsored","sponsored top level domain","sponsored top-level domain"},
                    css="tld-template-subtype-sponsored",
                    category="Sponsored TLD"},
                {canonical="Legacy TLD",
                    synonyms={"legacy","legacy top level domain","legacy top-level domain"},
                    css="tld-template-subtype-legacytld",
                    category="Legacy TLD"},
                {canonical="2012 gTLD Round",
                    synonyms={"gtld round 2012","2012 ngtld round","2012 ngtld","ngtld 2012","ngtld","2012"},
                    css="tld-template-subtype-ngtld-round-2012",
                    category="2012 gTLD Round"}
            }
         },
         },
        constants = {
            classicTLDs = ClassicTLDs,
        },
         patterns = {
         patterns = {
             tldExtension   = '%.([^%.]+)$',
             tldExtension="%.([^%.]+)$", -- Extracts the TLD part after the last dot (e.g., "com" from "example.com")
             countryDelimiter= '([^;]+)',
             countryDelimiter="([^;]+)",  -- Matches any sequence of characters not containing semicolons; used for country parsing
            itemDelimiter = p.fieldPatterns.itemDelimiter,
            websitePattern = p.fieldPatterns.websitePattern
         },
         },
         fields = {
         fields = {
             {key='type'        ,label='Type'},
             {key="tld_type", label="Type"},
             {key='subtype'    ,label='Subtype'},
             {key="tld_subtype", label="Subtype"},
             {key='status'      ,label='Status'},
             {key="tld_status", label="Status"}, -- (Property:Has TLD status)
             {keys={'country','territory'},label=p.fieldLabels.country},
             {key="region", label=p.fieldLabels.region},
             {key='introduced,label='Introduced'},
            {key="country", label=p.fieldLabels.country},
             {keys={'date','implemented'},label='Implemented'},
             {key="introduced", label="Introduced"},
             {keys={'script','language'},label='Script'},
             {key="implemented", label="Implemented"},
             {key='translation' ,label='English version'},
             {key="script", label="Script"},
             {key='ascii'      ,label='Punycode'},
            {key="language", label="Language"},
             {keys={'registry','registryprovider'},label='Registry'},
             {key="translation", label="In English"},
             {key='website'    ,label=p.fieldLabels.website},
             {key="ascii", label="Punycode"},
             {keys={'RVC','PIC'},label='PIC/RVC'},
             {key="registry", label="Registry", autoWikiLink=true},
             {key="website", label=p.fieldLabels.website, list=p.fieldProcessors.website.list},
             {keys={"RVC", "PIC"}, label = "PIC/RVC"}
         },
         },
         semantics = {
         semantics = {
             properties = {
             properties = {
                 ['Has TLD type']       = 'type',
                 ["Has TLD type"] = "tld_type",
                 ['Has TLD subtype']     = 'subtype',
                 ["Has TLD subtype"] = "tld_subtype",
                 ['Has TLD status']     = 'status',
                 ["Has TLD status"] = "tld_status",
                 ['Date introduced']     = 'introduced',
                 ["Date introduced"] = "introduced",
                 ['Date implemented']   = 'date',
                 ["Date implemented"] = "implemented",
                 ['Uses writing script'] = 'script',
                 ["Uses writing script"] = "script",
                 ['Has registry operator']= 'registry',
                 ["Has registry operator"] = "registry",
                 ['Has PIC or RVC']     = 'RVC',
                ["Has language"] = "language",
                 ["Has PIC or RVC"] = "RVC", -- Will also handle PIC
                ["Is IDN"] = "idn", -- Boolean
             },
             },
             additionalProperties = {
             additionalProperties = {
                 ['Has country']        = {'country','territory'},
                 ["Has PIC or RVC"] = {"RVC", "PIC"}
                ['Date implemented']    = {'date','implemented'},
                ['Uses writing script'] = {'script','language'},
                ['Has registry operator']= {'registry','registryprovider'},
                ['Has PIC or RVC']     = {'RVC','PIC'},
             },
             },
 
            -- Normalizations applied
             transforms = {
             transforms = {
                 ['Has TLD type']     = function(v) return select(1, CanonicalForms.normalize(v, typeMap  )) or v end,
                 ["Has TLD type"] = "normalize",
                 ['Has TLD subtype'] = function(v) return select(1, CanonicalForms.normalize(v, subtypeMap)) or v end,
                 ["Has TLD subtype"] = "normalize",
                 ['Date introduced'] = function(v) return tostring(DateFmt.formatDate(v)) end,
                 ["Date introduced"] = "date",
                 ['Date implemented'] = function(v) return tostring(DateFmt.formatDate(v)) end,
                 ["Date implemented"] = "date",
                 ['Has PIC or RVC']   = function(v) if v and v ~= '' then return 'true' end end,
                 ["Has PIC or RVC"] = "boolean",
                ["Is IDN"] = "boolean",
             },
             },
 
             fixedProperties = {
             skipProperties = { ['Has country'] = true },
                ["Has entity type"] = "TLD"
         },
            }
         }
     }
     }
}


    typeMap, subtypeMap = t.mappings.type, t.mappings.subtype
------------------------------------------------------------------------------
    p.templates.TLD = t
-- CONFIGURATION ACCESS FUNCTIONS
end
------------------------------------------------------------------------------


----------------------------------------------------------------------
-- Get configuration for a specific template type
-- LIBRARY INTERVIEW --------------------------------------------------
----------------------------------------------------------------------
do
    local t = {
        meta = { description = 'Library Interview template' },


        categories = { base = { 'Internet & Digital Governance Library', 'ICANNWiki Interviews' } },
function p.getConfig(templateType)
    return p.templates[templateType] or {}
end


        constants = { title = 'Internet & Digital Governance Library', tableClass = 'library-box' },
-- Local references to ConfigHelpers functions for better readability
local deepMerge = ConfigHelpers.deepMerge
local fieldHasKey = ConfigHelpers.fieldHasKey


        fields = {
-- Creates a standardized configuration structure for template modules
            {key='Title'      ,label='Title'},
function p.createStandardConfig(config, customOverrides, templateType)
            {key='Format'     ,label='Format'},
     config = config or {}
            {key='Date'      ,label=p.fieldLabels.date},
    customOverrides = customOverrides or {}
            {key='Interviewer',label='Interviewer',autoWikiLink=true},
   
            {key='Interviewee',label='Interviewee',autoWikiLink=true},
    -- Initialize with defaults from config
             {key='ID'        ,label='Permanent ID'},
    local standardConfig = {
        meta = config.meta or {
             description = "Template module configuration"
         },
         },
 
         mappings = config.mappings or {},
         semantics = {
        fields = config.fields or {},
            properties = {
         semantics = config.semantics or {
                ['Has interview format'] = 'Format',
             properties = {},
                ['Has date']            = 'Date',
             transforms = {},
                ['Has interviewer']      = 'Interviewer',
             additionalProperties = {}
                ['Has interviewee']      = 'Interviewee',
                ['Permanent ID']         = 'ID',
             },
 
             additionalProperties = { ['Has person'] = {'Interviewer','Interviewee'} },
 
             transforms = {
                ['Has date']    = function(v) return tostring(DateFmt.formatDate(v)) end,
                ['Permanent ID'] = function(v) return tostring(v or '') end,
            },
         },
         },
        constants = config.constants or {},
        patterns = config.patterns or {},
        categories = config.categories or {} -- Add categories field to preserve base categories
     }
     }
   
    -- Deep-merge customOverrides into standardConfig
    standardConfig = deepMerge(standardConfig, customOverrides)
   
    -- Auto-inject country/region semantic mappings if fields exist
    if standardConfig.fields and #standardConfig.fields > 0 then
        local hasCountryField = false
        local hasRegionField = false
       
        -- Check if country or region fields exist
        for _, field in ipairs(standardConfig.fields) do
            if fieldHasKey(field, "country") or fieldHasKey(field, "territory") then
                hasCountryField = true
            end
           
            if fieldHasKey(field, "region") then
                hasRegionField = true
            end
        end
       
        -- Initialize additionalProperties if it doesn't exist
        standardConfig.semantics.additionalProperties = standardConfig.semantics.additionalProperties or {}
       
        -- Auto-inject country mapping if a country field exists
        if hasCountryField then
            standardConfig.semantics.additionalProperties[p.semanticProperties.country] =
                standardConfig.semantics.additionalProperties[p.semanticProperties.country] or {"country", "territory"}
        end
       
        -- Auto-inject region mapping if a region field exists
        if hasRegionField then
            standardConfig.semantics.additionalProperties[p.semanticProperties.region] =
                standardConfig.semantics.additionalProperties[p.semanticProperties.region] or {"region"}
        end
    end


     p.templates.LibraryInterview = t
     -- Inject built-in transform functions for declarative transforms
end
    for propertyName, transformIdentifier in pairs(standardConfig.semantics.transforms) do
        if type(transformIdentifier) == "string" then
            local registryFn = p.transformRegistry[transformIdentifier]
            if registryFn then
                standardConfig.semantics.transforms[propertyName] = function(value)
                if transformIdentifier == "normalize" then
                    local mappingKey = standardConfig.semantics.properties[propertyName]
                    local mappingTable = standardConfig.mappings[mappingKey]
                    if not mappingTable then
                        local suffix = mappingKey:match(".*_(.*)")
                        mappingTable = suffix and standardConfig.mappings[suffix] or mappingTable
                    end
                    return registryFn(value, mappingTable)
                elseif transformIdentifier == "semantic" then
                        return registryFn(value, templateType)
                    else
                        return registryFn(value)
                    end
                end
            else
                ErrorHandling.addError(
                    ErrorHandling.createContext(templateType),
                    "TransformInjection",
                    "Unknown transform type",
                    transformIdentifier,
                    false
                )
            end
        end
    end


----------------------------------------------------------------------
     return standardConfig
-- API ----------------------------------------------------------------
----------------------------------------------------------------------
function p.getConfig(templateType)
     return p.templates[templateType] or {}
end
end


function p.createStandardConfig(cfg)
-- Get a standard config object for use with existing template modules
     cfg = cfg or {}
function p.getStandardConfig(templateType, customOverrides)
     return {
     -- Get the base configuration
        meta      = cfg.meta      or { description = 'Template module configuration' },
     local baseConfig = p.getConfig(templateType)
        mappings  = cfg.mappings  or {},
   
        fields     = cfg.fields     or {},
     -- Create a standard config
        semantics  = cfg.semantics  or { properties={}, transforms={}, additionalProperties={} },
     return p.createStandardConfig(baseConfig, customOverrides, templateType)
        constants  = cfg.constants  or {},
        patterns  = cfg.patterns  or {},
        categories = cfg.categories or {},
    }
end
end


function p.getStandardConfig(templateType, overrides)
function p.getSemanticPropertyName(propertyKey)
     return p.createStandardConfig(p.getConfig(templateType), overrides)
     for templateName, templateConfig in pairs(p.templates) do
        if templateConfig.semantics and templateConfig.semantics.additionalProperties then
            if templateConfig.semantics.additionalProperties[propertyKey] then
                return propertyKey
            end
        end
    end
    return nil
end
end


----------------------------------------------------------------------
mw.log('ConfigRepository loaded; heap '..collectgarbage('count')..' kB')
return p
return p

Latest revision as of 16:02, 11 September 2025

Documentation for this module may be created at Module:ConfigRepository/doc

-- Module:ConfigRepository
-- Single source of truth for all template configurations across the ICANNWiki ecosystem
--
-- This module centralizes configuration data to ensure consistency, eliminate duplication, 
-- and provide standardized structure for all templates. Works with the Template Blueprint 
-- Framework to power rendering and semantic processing.
--
-- This module consists of three main parts:
-- 1. Global constants and properties shared across templates
-- 2. Template-specific configurations with standardized structure
-- 3. Configuration access functions for retrieving and standardizing configurations
--
-- Each template configuration provides a standardized structure with the following components:
--
-- meta:        Template metadata including description and rendering options
-- categories:  Base and conditional categories for the template
-- patterns:    Regular expression patterns for validation and parsing
-- fields:      Maps template field names in article source to display labels
-- mappings:    Canonical mappings for normalizing user input values
-- constants:   Template-specific constant values
-- semantics:   Semantic MediaWiki integration with the following sub-components:
--   properties:           Maps semantic properties to template fields (1:1 mapping)
--   additionalProperties: Maps properties to multiple possible template fields
--   transforms:           Functions to transform field values before storing as properties
--   skipProperties:       Properties to exclude from automatic processing

local p = {}
local ErrorHandling = require('Module:ErrorHandling')
local ConfigHelpers = require('Module:ConfigHelpers')

------------------------------------------------------------------------------
-- Global Constants
------------------------------------------------------------------------------

-- Global field labels used in multiple templates
p.fieldLabels = {
    -- Geographic
    city = "City",
    country = "Country",
    region = "Region",

    -- Temporal
    date = "Date",
    ending = "End",
    start = "Start",

    -- Others
    process = "Process",
    website = "Websites"

    -- IMPORTANT: "Logo" is processed automatically whenever it is called as a key by the Template Blueprint, in such a way that it does not need to be declared for any templates in this repository
}

-- Global field patterns used in multiple templates
p.fieldPatterns = {
    itemDelimiter = ";%s*",  -- Matches semicolon followed by optional whitespace
    websitePattern = "^https?://[^%s]+"  -- Matches URLs starting with http:// or https://
}

-- Global field processors for reusable field definitions
p.fieldProcessors = {
    website = {
        list = { mode = 'bullet_custom', bulletChar = '🔗', postProcess = 'website' }
    }
}

-- Global canonical mappings used in multiple templates
p.mappings = {
    -- PROCESS (Property:Has process connection)
    process = {
        {canonical = "AFRINIC",
        category  = "AFRINIC"},
        {canonical = "APNIC",
        category  = "APNIC"},
        {canonical = "ARIN",
        category  = "ARIN"},
        {canonical = "DNS-OARC",
        category  = "DNS-OARC"},
        {canonical = "EuroDIG",
        category  = "EuroDIG"},
        {canonical = "ICANN",
        category  = "ICANN"},
        {canonical = "IEEE",
        category  = "IEEE"},
        {canonical = "IETF",
        category  = "IETF"},
        {canonical = "IGF",
        category  = "IGF"},
        {canonical = "ITU",
        category  = "ITU"},
        {canonical = "LACNIC",
        category  = "LACNIC"},
        {canonical = "RIPE NCC",
        category  = "RIPE NCC"},
        {canonical = "W3C",
        category  = "W3C"},
        {canonical = "WSIS",
        category  = "WSIS"}
    }
}

-- Global semantic property names used in multiple templates (Cross-template semantic queries)
p.semanticProperties = {
    country = "Has country",
    region = "Has ICANN region",
    language = "Knows language",
    person = "Has person",
}

-- Global date formatting configuration
-- When true, use "Apr. 21, 2025" format; when false, use "April 21, 2025"
p.dateFormatting = {
    useShortMonthNames = true
}

-- Global campaigns configuration
-- Campaigns are defined centrally and specify which templates they can be applied to
p.campaigns = {
    ASP2025 = {
        name = "ASP 2025 Pro Bono Service Provider",
        applicable_templates = {"Person", "Organization"},
        json_config = "ASP2025"
    }
}

-- Global page creator fields for TemplateStarter form generation
p.pageCreatorFields = {
    PAGE_NAME = {
        key = "PAGE_NAME",
        label = "Page Name",
        placeholder = "Enter page name...",
        required = true
    },
    COUNTRY = {
        key = "COUNTRY", 
        label = "Country",
        placeholder = "Enter country (optional)...",
        required = false
    },
    YEAR = {
        key = "YEAR",
        label = "Year", 
        placeholder = "Enter year (optional)...",
        required = false
    },
    ORGANIZATION_TYPE = {
        key = "ORGANIZATION_TYPE",
        label = "Organization Type",
        placeholder = "Enter organization type (optional)...",
        required = false
    },
    OCCUPATION = {
        key = "OCCUPATION",
        label = "Occupation",
        placeholder = "Enter occupation (optional)...",
        required = false
    },
    NORM_TYPE = {
        key = "NORM_TYPE",
        label = "Type of Norm",
        placeholder = "Enter norm type (optional)...",
        required = false
    }
}

------------------------------------------------------------------------------
-- Transform registry mapping string keys to functions
------------------------------------------------------------------------------

p.transformRegistry = {
    date = function(value)
        return tostring(require('Module:NormalizationDate').formatDate(value))
    end,
    normalize = function(value, mappings)
        -- Return original if mappings invalid
        if type(mappings) ~= 'table' or #mappings == 0 then
            return value
        end
        local CanonicalForms = require('Module:CanonicalForms')
        local canonical = select(1, CanonicalForms.normalize(value, mappings))
        return canonical or value
    end,
    semantic = function(value, templateName)
        return require('Module:TemplateHelpers').extractSemanticValue(value, templateName)
    end,
    boolean = function(value)
        if value and value ~= "" then
            return "true"
        end
    end,
    raw = function(value)
        return value
    end,
    string = function(value)
        return tostring(value or "")
    end
}

------------------------------------------------------------------------------
-- TEMPLATE-SPECIFIC CONFIGURATIONS
------------------------------------------------------------------------------
p.templates = {

------------------------------------------------------------------------------
-- ANCHOR: EVENT TEMPLATE
------------------------------------------------------------------------------

    Event = {
        meta = {
            description = "Event template"
        },
        categories = { -- Default categories
            base = {"Events"}
        },
        boilerplate = {
            intro = "'''$PAGE_NAME$''' was an event that took place in $COUNTRY$ in $YEAR$."
        },
        creatorFields = {"PAGE_NAME", "COUNTRY", "YEAR"},
        mappings = {
            -- Process (Property:Has process connection)
            process = p.mappings.process
        },
        patterns = {
            -- Patterns for event navigation detection
            seriesNumber = "^([%w%s]+)%s+(%d+)$",     -- e.g., "ICANN 76"
            seriesYear = "^([%w%s]+)%s+(%d%d%d%d)$",  -- e.g., "IGF 2023"
            itemDelimiter = p.fieldPatterns.itemDelimiter,
            websitePattern = p.fieldPatterns.websitePattern
        },
        fields = {
            {key="process", label=p.fieldLabels.process, autoWikiLink=true},
            {key="start", label=p.fieldLabels.start},
            {key="ending", label=p.fieldLabels.ending},
            {key="region", label=p.fieldLabels.region},
            {key="country", label=p.fieldLabels.country},
            {key="city", label=p.fieldLabels.city},
            {key="venue", label="Venue"},
            {key="organizer", label="Organizer"},
            {keys={"website", "url"}, label=p.fieldLabels.website, list=p.fieldProcessors.website.list},
        },
        semantics = {
            properties = {
                ["Has process connection"] = "process",
                ["Has start date"] = "start",
                ["Has end date"] = "ending",
                ["Has city"] = "city",
                ["Has venue"] = "venue",
                ["Has event organizer"] = "organizer"
            },
            -- Normalizations applied
            transforms = {
                ["Has start date"] = "date",
                ["Has end date"] = "date"
            },
            fixedProperties = {
                ["Has entity type"] = "Event"
            }
        }
    },

------------------------------------------------------------------------------
-- ANCHOR: LIBRARY INTERVIEW TEMPLATE
------------------------------------------------------------------------------

    LibraryInterview = {
        meta = {
            description = "Library Interview template",
            page_creator = false
        },
        categories = { -- Default categories
            base = {
                "Internet & Digital Governance Library",
                "ICANNWiki Interviews"
            }
        },
        constants = { -- REVIEW
            title = "Internet & Digital Governance Library",
            tableClass = "library-box"
        },
        fields = {
            {key="title", label="Title"},
            {key="format_interview", label="Format"},
            {key="date", label=p.fieldLabels.date},
            {key="interviewer", label="Interviewer", autoWikiLink=true},
            {key="interviewee", label="Interviewee", autoWikiLink=true},
            {key="id", label="Permanent ID"}
        },
        semantics = {
            -- Direct property mappings (1:1 relationship between property and field)
            properties = {
                ["Has interview format"] = "format_interview",
                ["Has date"] = "date",
                ["Has interviewer"] = "interviewer",
                ["Has interviewee"] = "interviewee",
                ["Permanent ID"] = "id"
            },
            -- Properties that map to multiple possible fields
            additionalProperties = {
                ["Has person"] = {"interviewer", "interviewee"} -- Links interviews to person pages
            },
            -- Normalizations applied
            transforms = {
                ["Has date"] = "date",
                ["Permanent ID"] = "string"
            },
            fixedProperties = {
                ["Has entity type"] = "Library"
            }
        }
    },

------------------------------------------------------------------------------
-- ANCHOR: NORM TEMPLATE
------------------------------------------------------------------------------

    Norm = {
        meta = {
            description = "Norm template for laws, regulations, standards, etc."
        },
        categories = {
            base = {"Norms"}
        },
        boilerplate = {
            intro = "'''$PAGE_NAME$''' is a $NORM_TYPE$ proposed in $YEAR$."
        },
        creatorFields = {"PAGE_NAME", "NORM_TYPE", "YEAR"},
        mappings = {
            norm_type = {
                {canonical = "Act", category = "Acts"},
                {canonical = "Agreement", category = "Agreements"},
                {canonical = "Framework", category = "Frameworks"},
                {canonical = "Law", category = "Laws"},
                {canonical = "Regulation", category = "Regulations"},
                {canonical = "Standard", category = "Standards"},
                {canonical = "Policy", category = "Policies"},
                {canonical = "Guideline", category = "Guidelines"},
                {canonical = "Treaty", category = "Treaties"},
                {canonical = "Decree", category = "Decrees"},
                {canonical = "Ordinance", category = "Ordinances"},
                {canonical = "By-law", category = "By-laws"},
                {canonical = "Code", category = "Codes"},
                {canonical = "Convention", category = "Conventions"},
                {canonical = "Directive", category = "Directives"}
            },
            scope_geo = {
                {canonical = "Global", category = "Global Norms"},
                {canonical = "Regional", category = "Regional Norms"},
                {canonical = "National", category = "National Norms"}
            },
            norm_status = { -- Key used in templatedata
                {canonical = "Active", category = "Active Norms"},
                {canonical = "Repealed", category = "Repealed Norms"},
                {canonical = "Draft", category = "Draft Norms"},
                {canonical = "Proposed", category = "Proposed Norms"},
                {canonical = "Superseded", category = "Superseded Norms"},
                {canonical = "Amended", category = "Amended Norms"}
            }
        },
        fields = {
            {key="norm_title", label="Norm title"},
            {key="alternative_title", label="Alternative name"}, -- Key used in templatedata
            {key="norm_type", label="Type of norm"},
            {key="issuing_body", label="Issuing body", autoWikiLink=true},
            {key="scope_geo", label="Geographic scope"},
            {key="country", label=p.fieldLabels.country},
            {key="norm_status", label="Status"}, -- Key used in templatedata
            {key="official_text", label="Official text"},
            {key="related_norm", label="Related Norms", list="bullet"},
            {key="parent_framework", label="Parent legal framework", autoWikiLink=true},
            {key="norm_language", label="Language"}
        },
        semantics = {
            properties = {
                ["Has alternative title"] = "alternative_title",
                ["Has norm type"] = "norm_type",
                ["Has issuing body"] = "issuing_body",
                ["Has geographic scope"] = "scope_geo",
                ["Has status"] = "norm_status",
                ["Links to official text"] = "official_text",
                ["Is related to norm"] = "related_norm",
                ["Is part of framework"] = "parent_framework",
                ["Has language"] = "norm_language"
            },
            transforms = {
                ["Has norm type"] = "normalize",
                ["Has geographic scope"] = "normalize",
                ["Has status"] = "normalize",
                ["Has issuing body"] = "semantic",
                ["Is related to norm"] = "semantic",
                ["Is part of framework"] = "semantic",
                ["Links to official text"] = "string",
                ["Has language"] = "string"
            },
            fixedProperties = {
                ["Has entity type"] = "Norm"
            }
        }
    },

------------------------------------------------------------------------------
-- ANCHOR: ORGANIZATION TEMPLATE
------------------------------------------------------------------------------

    Organization = {
        meta = {
            description = "Organization template"
        },
        categories = { -- Default categories
            base = {"Organization"}
        },
        boilerplate = {
            intro = "'''$PAGE_NAME$''' is a $ORGANIZATION_TYPE$ founded in $YEAR$."
        },
        creatorFields = {"PAGE_NAME", "ORGANIZATION_TYPE", "YEAR"},
        mappings = {
            organization_type = {
            -- organization_type (Property:Has organization type)
                {canonical = "Academic institution",
                    synonyms = {"university", "college", "academia", "institute", "school"},
                    category = "Academic institution"},
                {canonical = "ALS",
                    synonyms = {"at-large structure", "at large structure", "at-large", "at large"},
                    category = "ALS"},
                {canonical = "Commercial",
                    synonyms = {"for-profit", "for profit", "business", "private", "private company", "privately held", "company", "publicly held", "public", "joint stock company", "corporation", "corp", "firm", "enterprise", "limited liability", "llc", "gmbh"},
                    category = "Commercial"},
                {canonical = "Community network",
                    synonyms = {"community"},
                    category = "Community network"},
                {canonical = "Consortium",
                    synonyms = {"alliance", "joint venture", "joint-venture"},
                    category = "Consortium"},
                {canonical = "Cooperative",
                    synonyms = {"co-op", "coop", "co-operative"},
                    category = "Cooperative"},
                {canonical = "Government agency",
                    synonyms = {"government", "govt", "regulator", "regulatory authority", "nra", "bureau"},
                    category = "Government agency"},
                {canonical = "Industry association",
                    synonyms = {"trade association", "industry group", "trade group"},
                    category = "Industry association"},
                {canonical = "Intergovernmental organization",
                    synonyms = {"igo", "ingo"},
                    category = "Intergovernmental organization"},
                {canonical = "Internet Society Chapter",
                    synonyms = {"isoc chapter"},
                    category = "Internet Society Chapter"},
                {canonical = "Non-profit",
                    synonyms = {"nonprofit", "not-for-profit", "not for profit", "charity", "foundation", "economic forum", "ngo", "association", "non-profit organization"},
                    category = "Non-profit"},
                {canonical = "Public-private partnership",
                    synonyms = {"public-private partnership", "ppp"},
                    category = "Public-private partnership"},
                {canonical = "Research institute",
                    synonyms = {"research", "institute", "think tank"},
                    category = "Research institute"},
                {canonical = "Standards development organization",
                    synonyms = {"sdo", "standards body", "standards organisation, standards"},
                    category = "Standards development organization"}
            }
        },
        patterns = {
            itemDelimiter = p.fieldPatterns.itemDelimiter,
            websitePattern = p.fieldPatterns.websitePattern
        },
        fields = {
            {key="organization_type", label="Type"},
            {key="focus", label="Focus"},
            {key="region", label=p.fieldLabels.region},
            {key="country", label=p.fieldLabels.country},
            {key="city", label=p.fieldLabels.city},
            {key="date_founded", label="Founded"},
            {key="founders", label="Founders", autoWikiLink=true, list="bullet"},
            {key="ownership", label="Ownership", autoWikiLink=true},
            {key="subsidiaries", label="Subsidiaries", list="bullet"},
            {keys={"website", "url"}, label=p.fieldLabels.website, list=p.fieldProcessors.website.list},
        },
        semantics = {
            properties = {
                ["Has organization type"] = "organization_type",
                ["Has focus"] = "focus",
                ["Has city"] = "city",
                ["Date founded"] = "date_founded",
                ["Has owner"] = "ownership",
                ["Has subsidiary"] = "subsidiaries"
            },
            -- Normalizations applied
            transforms = {
                -- None
            },
            fixedProperties = {
                ["Has entity type"] = "Organization"
            }
        }
    },

------------------------------------------------------------------------------
-- ANCHOR: PERSON TEMPLATE
------------------------------------------------------------------------------

    Person = {
        meta = {
            description = "Person template"
        },
        categories = { -- Default categories
            base = {"Person"}
        },
        boilerplate = {
            intro = "'''$PAGE_NAME$''' is a $OCCUPATION$ based in $COUNTRY$."
        },
        creatorFields = {"PAGE_NAME", "OCCUPATION", "COUNTRY"},
        mappings = {
            -- REVIEW Community (Property:Has governance community)
            community = {
                -- Broad definition
                {canonical = "Internet Governance Community",
                    synonyms = {"ig", "internet governance"},
                    category = "Internet Governance Community"},
                -- ICANN-related communities
                {canonical = "ICANN Community",
                    synonyms = {"icann", "community"},
                    category = "ICANN Community"},
                {canonical = "ICANN Staff", -- Category provided via "badge-icann-staff"
                    synonyms = {"staff", "icann org"},},
                -- Technical communities
                {canonical = "Technical Community",
                    synonyms = {"technical"},
                    category = "Technical Community"},
                {canonical = "Root Server Operator Community",
                    synonyms = {"root server operator", "rso"},
                    category = "Root Server Operator Community"},
                {canonical = "RIR Community",
                    synonyms = {"rir"},
                    category = "RIR Community"},
                {canonical = "Universal Acceptance Community",
                    synonyms = {"universal acceptance", "ua", "uasg", "idn", "idn community"},
                    category = "Universal Acceptance Community"},
                -- Internet governance organizations
                {canonical = "ISOC Community",
                    synonyms = {"isoc", "internet society", "isoc member"},
                    category = "ISOC Community"},
                {canonical = "IETF Community",
                    synonyms = {"ietf", "ietf member"},
                    category = "IETF Community"},
                {canonical = "IGF Community",
                    synonyms = {"igf", "nri", "youth igf"},
                    category = "IGF Community"},
                {canonical = "W3C Community",
                    synonyms = {"w3c", "w3c member"},
                    category = "W3C Community"},
                -- Government-related
                {canonical = "Governmental",
                    synonyms = {"government", "public sector", "gac"},
                    category = "Governmental"},
                {canonical = "Intergovernmental",
                    synonyms = {"igo", "ingo"},
                    category = "Intergovernmental"}
            }
        },
        fields = {
            {key="community", label="Community"},
            {key="icann_group", label="ICANN SO/AC", autoWikiLink=true}, -- (Property:Has ICANN affiliation)
            {key="organization", label="Organization", autoWikiLink=true, list="bullet"},
            {key="region", label=p.fieldLabels.region, list="bullet"},
            {key="country", label=p.fieldLabels.country},
            {key="languages", label="Languages", list = { mode = 'bullet_custom', bulletChar = '🗣️', postProcess = 'language' }},
            {key="website", label=p.fieldLabels.website, list=p.fieldProcessors.website.list},
            {key="soi", label="SOI"},
            {key="userbox", label="Achievements"},
            {key="user", label="User", hidden=true}
        },
        patterns = {
            itemDelimiter = p.fieldPatterns.itemDelimiter,
            websitePattern = p.fieldPatterns.websitePattern
        },
        semantics = {
            properties = {
                ["Has governance community"] = "community",
                ["Has ICANN affiliation"] = "icann_group",
                ["Has organization"] = "organization",
                ["Has username"] = "user"
            },
            -- Normalizations applied
            transforms = {
                ["Has governance community"] = "normalize"
            },
            fixedProperties = {
                ["Has entity type"] = "Person"
            }
        }
    },

------------------------------------------------------------------------------
-- ANCHOR: PROCESS TEMPLATE
------------------------------------------------------------------------------

    Process = {
        meta = {
            description = "Process Template",
            templateId = "process"
        },
        categories = {
            base = {"Processes", "Events"}
        },
        boilerplate = {
            intro = "'''$PAGE_NAME$''' is a process started in $YEAR$."
        },
        creatorFields = {"PAGE_NAME", "YEAR"},
        mappings = {
            -- Process (Property:Has process connection)
            process = p.mappings.process,
            -- Model (Property:Has process model)
            model = {
                {canonical = "Multistakeholder",
                    synonyms = {"multi-stakeholder", "multi stakeholder"},
                    category = "Multistakeholder Processes"},
                {canonical = "Single stakeholder",
                    synonyms = {"single-stakeholder", "intergovernmental"},
                    category = "Single Stakeholder Processes"}
            },
            -- Status (Property:Has process status)
            status = {
                {canonical = "Active",
                    synonyms = {"ongoing"},
                    category = "Active Processes"},
                {canonical = "Paused",
                    synonyms = {"inactive", "dormant"},
                    category = "Dormant Processes"},
                {canonical = "Concluded",
                    synonyms = {"completed", "ended", "finished"},
                    category = "Concluded Processes"}
            },
            -- Frequency (Property:Has frequency)
            frequency = {
                {canonical = "One-off",
                    synonyms = {"one off", "single"},
                    category = "One-off Processes"},
                {canonical = "Ad-hoc",
                    synonyms = {"ad hoc", "eventual"},
                    category = "Ad-hoc Processes"},
                {canonical = "Periodic",
                    synonyms = {"recurring", "regular", "annual", "yearly"},
                    category = "Periodic Processes"},
                {canonical = "Continuous",
                    synonyms = {"permanent"},
                    category = "Continuous Processes"}
            },
            -- Scope (Property:Has geographic scope)
            scope_geo = {
                {canonical = "Global",
                    synonyms = {"worldwide", "world", "international"},
                    category = "Global Processes"},
                {canonical = "Regional",
                    synonyms = {"continent", "continental"},
                    category = "Regional Processes"},
                {canonical = "National",
                    synonyms = {"country", "domestic"},
                    category = "National Processes"}
            }
        },
        fields = {
            {key="process", label=p.fieldLabels.process, autoWikiLink=true},
            {key="model", label="Model"},
            {key="start", label=p.fieldLabels.start},
            {key="ending", label=p.fieldLabels.ending},
            {key="process_status", label="Status"},
            {key="governing_body", label="Governing Body", autoWikiLink=true},
            {key="secretariat", label="Secretariat", autoWikiLink=true},
            {key="frequency", label="Frequency"},
            {key="scope_geo", label="Scope"},
            {key="region", label=p.fieldLabels.region},
            {key="country", label=p.fieldLabels.country},
            {key="website", label=p.fieldLabels.website},
            {key="has_previous", label="Preceded By", hidden=true},
            {key="has_next", label="Succeeded By", hidden=true},
        },
        semantics = {
            properties = {
                ["Has start date"] = "start",
                ["Has end date"] = "ending",
                ["Has process status"] = "process_status",
                ["Has governing body"] = "governing_body",
                ["Has event organizer"] = "secretariat",
                ["Has process model"] = "model",
                ["Has frequency"] = "frequency",
                ["Has geographic scope"] = "scope_geo",
                ["Has process connection"] = "process"
            },
            additionalProperties = {
                ["Has process connection"] = {"process", "has_previous", "has_next"}
            },
            -- Normalizations applied
            transforms = {
                ["Has start date"] = "date",
                ["Has end date"] = "date",
                ["Has process status"] = "normalize",
                ["Has process model"] = "normalize",
                ["Has frequency"] = "normalize",
                ["Has geographic scope"] = "normalize",
                ["Has process connection"] = "semantic"
            },
            fixedProperties = {
                ["Has entity type"] = "Process"
            }
        }
    },

------------------------------------------------------------------------------
-- ANCHOR: TLD TEMPLATE
------------------------------------------------------------------------------

    TLD = {
        meta = {
            description = "TLD Template"
        },
        categories = {
            base = {}, -- No default category for TLDs as they use type-based categories
            conditional = {
                rvc = "TLDs with RVCs",
                idn = "IDN",
                idn_cctld = "IDN ccTLD"
            },
        },
        boilerplate = {
            intro = "'''$PAGE_NAME$''' is a TLD proposed in $YEAR$."
        },
        creatorFields = {"PAGE_NAME", "YEAR"},
        mappings = {
            -- Type (Property:Has TLD type)
            tld_type = {
                {canonical = "gTLD",
                    synonyms = {"generic", "tld", "generic top level domain", "generic top-level domain", "generic tld"},
                    category = "gTLD"},
                {canonical = "ccTLD",
                    synonyms = {"country", "cc", "country code top level domain", "country code top-level domain", "country tld"},
                    category = "ccTLD"}
            },
            -- Subtype (Property:Has TLD subtype)
            tld_subtype = {
                {canonical="geoTLD",
                    synonyms={"geo tld","geo","geographic","geographical","geographic top level domain","geographic top-level domain","geographic tld"},
                    css="tld-template-subtype-geotld",
                    category="geoTLD"},
                {canonical="dotBrand",
                    synonyms={"brand","brandtld","brand tld","brand top level domain","brand top-level domain"},
                    css="tld-template-subtype-brandtld",
                    category="dotBrand"},
                {canonical="Sponsored TLD",
                    synonyms={"sponsored","sponsored top level domain","sponsored top-level domain"},
                    css="tld-template-subtype-sponsored",
                    category="Sponsored TLD"},
                {canonical="Legacy TLD",
                    synonyms={"legacy","legacy top level domain","legacy top-level domain"},
                    css="tld-template-subtype-legacytld",
                    category="Legacy TLD"},
                {canonical="2012 gTLD Round",
                    synonyms={"gtld round 2012","2012 ngtld round","2012 ngtld","ngtld 2012","ngtld","2012"},
                    css="tld-template-subtype-ngtld-round-2012",
                    category="2012 gTLD Round"}
            }
        },
        patterns = {
            tldExtension="%.([^%.]+)$",  -- Extracts the TLD part after the last dot (e.g., "com" from "example.com")
            countryDelimiter="([^;]+)",   -- Matches any sequence of characters not containing semicolons; used for country parsing
            itemDelimiter = p.fieldPatterns.itemDelimiter,
            websitePattern = p.fieldPatterns.websitePattern
        },
        fields = {
            {key="tld_type", label="Type"},
            {key="tld_subtype", label="Subtype"},
            {key="tld_status", label="Status"}, -- (Property:Has TLD status)
            {key="region", label=p.fieldLabels.region},
            {key="country", label=p.fieldLabels.country},
            {key="introduced", label="Introduced"},
            {key="implemented", label="Implemented"},
            {key="script", label="Script"},
            {key="language", label="Language"},
            {key="translation", label="In English"},
            {key="ascii", label="Punycode"},
            {key="registry", label="Registry", autoWikiLink=true},
            {key="website", label=p.fieldLabels.website, list=p.fieldProcessors.website.list},
            {keys={"RVC", "PIC"}, label = "PIC/RVC"}
        },
        semantics = {
            properties = {
                ["Has TLD type"] = "tld_type",
                ["Has TLD subtype"] = "tld_subtype",
                ["Has TLD status"] = "tld_status",
                ["Date introduced"] = "introduced",
                ["Date implemented"] = "implemented",
                ["Uses writing script"] = "script",
                ["Has registry operator"] = "registry",
                ["Has language"] = "language",
                ["Has PIC or RVC"] = "RVC", -- Will also handle PIC
                ["Is IDN"] = "idn", -- Boolean
            },
            additionalProperties = {
                ["Has PIC or RVC"] = {"RVC", "PIC"}
            },
            -- Normalizations applied
            transforms = {
                ["Has TLD type"] = "normalize",
                ["Has TLD subtype"] = "normalize",
                ["Date introduced"] = "date",
                ["Date implemented"] = "date",
                ["Has PIC or RVC"] = "boolean",
                ["Is IDN"] = "boolean",
            },
            fixedProperties = {
                ["Has entity type"] = "TLD"
            }
        }
    }
}

------------------------------------------------------------------------------
-- CONFIGURATION ACCESS FUNCTIONS
------------------------------------------------------------------------------

-- Get configuration for a specific template type

function p.getConfig(templateType)
    return p.templates[templateType] or {}
end

-- Local references to ConfigHelpers functions for better readability
local deepMerge = ConfigHelpers.deepMerge
local fieldHasKey = ConfigHelpers.fieldHasKey

-- Creates a standardized configuration structure for template modules
function p.createStandardConfig(config, customOverrides, templateType)
    config = config or {}
    customOverrides = customOverrides or {}
    
    -- Initialize with defaults from config
    local standardConfig = {
        meta = config.meta or {
            description = "Template module configuration"
        },
        mappings = config.mappings or {},
        fields = config.fields or {},
        semantics = config.semantics or {
            properties = {},
            transforms = {},
            additionalProperties = {}
        },
        constants = config.constants or {},
        patterns = config.patterns or {},
        categories = config.categories or {} -- Add categories field to preserve base categories
    }
    
    -- Deep-merge customOverrides into standardConfig
    standardConfig = deepMerge(standardConfig, customOverrides)
    
    -- Auto-inject country/region semantic mappings if fields exist
    if standardConfig.fields and #standardConfig.fields > 0 then
        local hasCountryField = false
        local hasRegionField = false
        
        -- Check if country or region fields exist
        for _, field in ipairs(standardConfig.fields) do
            if fieldHasKey(field, "country") or fieldHasKey(field, "territory") then
                hasCountryField = true
            end
            
            if fieldHasKey(field, "region") then
                hasRegionField = true
            end
        end
        
        -- Initialize additionalProperties if it doesn't exist
        standardConfig.semantics.additionalProperties = standardConfig.semantics.additionalProperties or {}
        
        -- Auto-inject country mapping if a country field exists
        if hasCountryField then
            standardConfig.semantics.additionalProperties[p.semanticProperties.country] = 
                standardConfig.semantics.additionalProperties[p.semanticProperties.country] or {"country", "territory"}
        end
        
        -- Auto-inject region mapping if a region field exists
        if hasRegionField then
            standardConfig.semantics.additionalProperties[p.semanticProperties.region] = 
                standardConfig.semantics.additionalProperties[p.semanticProperties.region] or {"region"}
        end
    end

    -- Inject built-in transform functions for declarative transforms
    for propertyName, transformIdentifier in pairs(standardConfig.semantics.transforms) do
        if type(transformIdentifier) == "string" then
            local registryFn = p.transformRegistry[transformIdentifier]
            if registryFn then
                standardConfig.semantics.transforms[propertyName] = function(value)
                if transformIdentifier == "normalize" then
                    local mappingKey = standardConfig.semantics.properties[propertyName]
                    local mappingTable = standardConfig.mappings[mappingKey]
                    if not mappingTable then
                        local suffix = mappingKey:match(".*_(.*)")
                        mappingTable = suffix and standardConfig.mappings[suffix] or mappingTable
                    end
                    return registryFn(value, mappingTable)
                elseif transformIdentifier == "semantic" then
                        return registryFn(value, templateType)
                    else
                        return registryFn(value)
                    end
                end
            else
                ErrorHandling.addError(
                    ErrorHandling.createContext(templateType),
                    "TransformInjection",
                    "Unknown transform type",
                    transformIdentifier,
                    false
                )
            end
        end
    end

    return standardConfig
end

-- Get a standard config object for use with existing template modules
function p.getStandardConfig(templateType, customOverrides)
    -- Get the base configuration
    local baseConfig = p.getConfig(templateType)
    
    -- Create a standard config
    return p.createStandardConfig(baseConfig, customOverrides, templateType)
end

function p.getSemanticPropertyName(propertyKey)
    for templateName, templateConfig in pairs(p.templates) do
        if templateConfig.semantics and templateConfig.semantics.additionalProperties then
            if templateConfig.semantics.additionalProperties[propertyKey] then
                return propertyKey
            end
        end
    end
    return nil
end

return p