{"version":3,"sources":["../../src/roleplay-templates/builder.ts"],"sourcesContent":["/**\n * Roleplay Template Plugin Builder utilities\n *\n * Provides helper functions for creating and validating roleplay template plugins.\n *\n * @module @quilltap/plugin-utils/roleplay-templates\n */\n\nimport type {\n  NarrationDelimiters,\n  RoleplayTemplateConfig,\n  RoleplayTemplateMetadata,\n  RoleplayTemplatePlugin,\n} from '@quilltap/plugin-types';\n\n// ============================================================================\n// BUILDER OPTIONS\n// ============================================================================\n\n/**\n * Options for creating a roleplay template plugin\n */\nexport interface CreateRoleplayTemplatePluginOptions {\n  /** Plugin metadata */\n  metadata: RoleplayTemplateMetadata;\n\n  /**\n   * One or more roleplay templates.\n   * Pass a single template object or an array of templates.\n   */\n  templates: RoleplayTemplateConfig | RoleplayTemplateConfig[];\n\n  /**\n   * Optional initialization function.\n   * Called when the plugin is loaded.\n   */\n  initialize?: () => void | Promise<void>;\n\n  /**\n   * Whether to enable debug logging.\n   * Defaults to false.\n   */\n  enableLogging?: boolean;\n}\n\n/**\n * Simplified options for plugins that provide a single template\n */\nexport interface CreateSingleTemplatePluginOptions {\n  /** Unique template identifier (lowercase, hyphens allowed) */\n  templateId: string;\n\n  /** Human-readable display name */\n  displayName: string;\n\n  /** Template description */\n  description?: string;\n\n  /**\n   * The system prompt that defines the formatting rules.\n   * This is prepended to character system prompts when the template is active.\n   */\n  systemPrompt: string;\n\n  /** Template author */\n  author?: string | {\n    name: string;\n    email?: string;\n    url?: string;\n  };\n\n  /** Tags for categorization and searchability */\n  tags?: string[];\n\n  /**\n   * Narration delimiters — required.\n   * Single string (same open/close, e.g., '*') or [open, close] tuple (e.g., ['[', ']']).\n   */\n  narrationDelimiters: NarrationDelimiters;\n\n  /** Template version */\n  version?: string;\n\n  /**\n   * Optional initialization function.\n   * Called when the plugin is loaded.\n   */\n  initialize?: () => void | Promise<void>;\n\n  /**\n   * Whether to enable debug logging.\n   * Defaults to false.\n   */\n  enableLogging?: boolean;\n}\n\n// ============================================================================\n// BUILDER FUNCTIONS\n// ============================================================================\n\n/**\n * Creates a roleplay template plugin with full control over metadata and templates.\n *\n * Use this when you want to provide multiple templates or have fine-grained\n * control over the plugin structure.\n *\n * @param options - Plugin configuration options\n * @returns A valid RoleplayTemplatePlugin instance\n *\n * @example\n * ```typescript\n * import { createRoleplayTemplatePlugin } from '@quilltap/plugin-utils';\n *\n * export const plugin = createRoleplayTemplatePlugin({\n *   metadata: {\n *     templateId: 'my-rp-format',\n *     displayName: 'My RP Format',\n *     description: 'A custom roleplay formatting style',\n *   },\n *   templates: [\n *     {\n *       name: 'My RP Format',\n *       description: 'Custom formatting with specific syntax',\n *       systemPrompt: '[FORMATTING INSTRUCTIONS]...',\n *       tags: ['custom'],\n *     },\n *   ],\n * });\n * ```\n */\nexport function createRoleplayTemplatePlugin(\n  options: CreateRoleplayTemplatePluginOptions\n): RoleplayTemplatePlugin {\n  const { metadata, templates, initialize, enableLogging: _enableLogging = false } = options;\n\n  // Normalize templates to array\n  const templateArray = Array.isArray(templates) ? templates : [templates];\n\n  // Validate templates\n  if (templateArray.length === 0) {\n    throw new Error('At least one template is required');\n  }\n\n  for (const template of templateArray) {\n    if (!template.name || template.name.trim() === '') {\n      throw new Error('Template name is required');\n    }\n    if (!template.systemPrompt || template.systemPrompt.trim() === '') {\n      throw new Error(`Template \"${template.name}\" requires a systemPrompt`);\n    }\n  }\n\n  // Create the plugin\n  const plugin: RoleplayTemplatePlugin = {\n    metadata: {\n      ...metadata,\n      // Ensure tags from templates are included in metadata if not already set\n      tags: metadata.tags ?? Array.from(\n        new Set(templateArray.flatMap(t => t.tags ?? []))\n      ),\n    },\n    templates: templateArray,\n  };\n\n  // Add initialize function if provided\n  if (initialize) {\n    plugin.initialize = async () => {\n      await initialize();\n    };\n  }\n\n  return plugin;\n}\n\n/**\n * Creates a simple roleplay template plugin with a single template.\n *\n * This is a convenience function for the common case of a plugin\n * that provides just one roleplay template.\n *\n * @param options - Simplified plugin configuration\n * @returns A valid RoleplayTemplatePlugin instance\n *\n * @example\n * ```typescript\n * import { createSingleTemplatePlugin } from '@quilltap/plugin-utils';\n *\n * export const plugin = createSingleTemplatePlugin({\n *   templateId: 'quilltap-rp',\n *   displayName: 'Quilltap RP',\n *   description: 'Custom formatting with [actions], {thoughts}, and // OOC',\n *   systemPrompt: `[FORMATTING INSTRUCTIONS]\n * 1. DIALOGUE: Write as bare text without quotes\n * 2. ACTIONS: Use [square brackets]\n * 3. THOUGHTS: Use {curly braces}\n * 4. OOC: Use // prefix`,\n *   tags: ['quilltap', 'custom'],\n * });\n * ```\n */\nexport function createSingleTemplatePlugin(\n  options: CreateSingleTemplatePluginOptions\n): RoleplayTemplatePlugin {\n  const {\n    templateId,\n    displayName,\n    description,\n    systemPrompt,\n    author,\n    tags,\n    narrationDelimiters,\n    version,\n    initialize,\n    enableLogging,\n  } = options;\n\n  return createRoleplayTemplatePlugin({\n    metadata: {\n      templateId,\n      displayName,\n      description,\n      author,\n      tags,\n      version,\n    },\n    templates: {\n      name: displayName,\n      description,\n      systemPrompt,\n      tags,\n      narrationDelimiters,\n    },\n    initialize,\n    enableLogging,\n  });\n}\n\n// ============================================================================\n// VALIDATION UTILITIES\n// ============================================================================\n\n/**\n * Validates a roleplay template configuration\n *\n * @param template - The template configuration to validate\n * @returns True if valid, throws Error if invalid\n */\nexport function validateTemplateConfig(template: RoleplayTemplateConfig): boolean {\n  if (!template.name || template.name.trim() === '') {\n    throw new Error('Template name is required');\n  }\n\n  if (template.name.length > 100) {\n    throw new Error('Template name must be 100 characters or less');\n  }\n\n  if (!template.systemPrompt || template.systemPrompt.trim() === '') {\n    throw new Error('Template systemPrompt is required');\n  }\n\n  if (template.description && template.description.length > 500) {\n    throw new Error('Template description must be 500 characters or less');\n  }\n\n  if (template.tags) {\n    if (!Array.isArray(template.tags)) {\n      throw new Error('Template tags must be an array');\n    }\n    for (const tag of template.tags) {\n      if (typeof tag !== 'string') {\n        throw new Error('All tags must be strings');\n      }\n    }\n  }\n\n  // Validate narrationDelimiters (required)\n  if (!template.narrationDelimiters) {\n    throw new Error('Template narrationDelimiters is required');\n  }\n  if (typeof template.narrationDelimiters === 'string') {\n    if (template.narrationDelimiters.length === 0) {\n      throw new Error('Template narrationDelimiters string must not be empty');\n    }\n  } else if (Array.isArray(template.narrationDelimiters)) {\n    if (template.narrationDelimiters.length !== 2) {\n      throw new Error('Template narrationDelimiters array must have exactly 2 elements [open, close]');\n    }\n    if (!template.narrationDelimiters[0] || !template.narrationDelimiters[1]) {\n      throw new Error('Template narrationDelimiters array elements must not be empty');\n    }\n  } else {\n    throw new Error('Template narrationDelimiters must be a string or [string, string] tuple');\n  }\n\n  return true;\n}\n\n/**\n * Validates a complete roleplay template plugin\n *\n * @param plugin - The plugin to validate\n * @returns True if valid, throws Error if invalid\n */\nexport function validateRoleplayTemplatePlugin(plugin: RoleplayTemplatePlugin): boolean {\n  // Validate metadata\n  if (!plugin.metadata) {\n    throw new Error('Plugin metadata is required');\n  }\n\n  if (!plugin.metadata.templateId || plugin.metadata.templateId.trim() === '') {\n    throw new Error('Plugin metadata.templateId is required');\n  }\n\n  if (!/^[a-z0-9-]+$/.test(plugin.metadata.templateId)) {\n    throw new Error('Plugin templateId must be lowercase alphanumeric with hyphens only');\n  }\n\n  if (!plugin.metadata.displayName || plugin.metadata.displayName.trim() === '') {\n    throw new Error('Plugin metadata.displayName is required');\n  }\n\n  // Validate templates\n  if (!plugin.templates || !Array.isArray(plugin.templates) || plugin.templates.length === 0) {\n    throw new Error('Plugin must have at least one template');\n  }\n\n  for (const template of plugin.templates) {\n    validateTemplateConfig(template);\n  }\n\n  return true;\n}\n"],"mappings":"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","names":[]}