diff --git a/packages/package_validator/seed/scripts/cli.lua b/packages/package_validator/seed/scripts/cli.lua index b5bd55443..877d70a31 100644 --- a/packages/package_validator/seed/scripts/cli.lua +++ b/packages/package_validator/seed/scripts/cli.lua @@ -1,130 +1,30 @@ --- CLI tool for validating packages --- Usage: lua cli.lua [options] +--- CLI tool facade for validating packages +--- Usage: lua cli.lua [options] +---@module cli local validate = require("validate") +local parse_args = require("parse_args") +local print_help = require("print_help") +local output_json = require("output_json") +local output_human = require("output_human") +---@class CLI local M = {} --- Parse command line arguments -function M.parse_args(args) - local options = { - package_name = nil, - skipStructure = false, - skipLua = false, - verbose = false, - json_output = false - } +M.parse_args = parse_args +M.print_help = print_help +M.output_json = output_json +M.output_human = output_human - local i = 1 - while i <= #args do - local arg = args[i] - - if arg == "--skip-structure" then - options.skipStructure = true - elseif arg == "--skip-lua" then - options.skipLua = true - elseif arg == "--verbose" or arg == "-v" then - options.verbose = true - elseif arg == "--json" then - options.json_output = true - elseif arg == "--help" or arg == "-h" then - M.print_help() - os.exit(0) - elseif not options.package_name then - options.package_name = arg - end - - i = i + 1 - end - - return options -end - --- Print help message -function M.print_help() - print([[ -Package Validator CLI - -Usage: - lua cli.lua [options] - -Arguments: - package_name Name of the package to validate - -Options: - --skip-structure Skip folder structure validation - --skip-lua Skip Lua file validation - --verbose, -v Show detailed validation information - --json Output results as JSON - --help, -h Show this help message - -Examples: - # Validate audit_log package - lua cli.lua audit_log - - # Validate with verbose output - lua cli.lua audit_log --verbose - - # Skip structure validation - lua cli.lua audit_log --skip-structure - - # Output as JSON (for CI/CD integration) - lua cli.lua audit_log --json - -Exit Codes: - 0 - Validation passed - 1 - Validation failed - 2 - Invalid arguments or package not found -]]) -end - --- Output results as JSON -function M.output_json(results) - local json_output = { - valid = results.valid, - errors = results.errors, - warnings = results.warnings - } - - -- Simple JSON serialization - local function serialize_array(arr) - local items = {} - for _, item in ipairs(arr) do - table.insert(items, '"' .. item:gsub('"', '\\"') .. '"') - end - return "[" .. table.concat(items, ",") .. "]" - end - - print("{") - print(' "valid": ' .. tostring(results.valid) .. ',') - print(' "errors": ' .. serialize_array(results.errors) .. ',') - print(' "warnings": ' .. serialize_array(results.warnings)) - print("}") -end - --- Output results in human-readable format -function M.output_human(results, verbose) - local output = validate.format_results(results) - print(output) - - if verbose and #results.errors > 0 then - print("\n--- Detailed Error Information ---") - for i, err in ipairs(results.errors) do - print(i .. ". " .. err) - end - end - - if verbose and #results.warnings > 0 then - print("\n--- Detailed Warning Information ---") - for i, warn in ipairs(results.warnings) do - print(i .. ". " .. warn) - end - end -end - --- Main entry point +--- Main entry point +---@param args string[] Command line arguments function M.run(args) - local options = M.parse_args(args) + local options = parse_args(args) + + if options.show_help then + print_help() + os.exit(0) + end if not options.package_name then print("Error: Package name is required") @@ -151,9 +51,9 @@ function M.run(args) -- Output results if options.json_output then - M.output_json(results) + output_json(results) else - M.output_human(results, options.verbose) + output_human(results, options.verbose) end -- Exit with appropriate code diff --git a/packages/package_validator/seed/scripts/component_schema.lua b/packages/package_validator/seed/scripts/component_schema.lua index c5c678ac7..9989f359d 100644 --- a/packages/package_validator/seed/scripts/component_schema.lua +++ b/packages/package_validator/seed/scripts/component_schema.lua @@ -1,126 +1,30 @@ --- Component JSON schema definitions +--- Component JSON schema validation facade +--- Re-exports component validation functions for backward compatibility +---@module component_schema + +local validate_component = require("validate_component") +local validate_layout = require("validate_layout") +local validate_components = require("validate_components") + +---@class ComponentSchema local M = {} --- Validate a single component structure -function M.validate_component(component, index) - local errors = {} - local prefix = index and ("components[" .. index .. "]") or "component" +--- Validate a single component structure +---@param component Component The component to validate +---@param index? number Optional component index for error messages +---@return string[] errors List of validation errors +M.validate_component = validate_component - -- Required fields - if not component.id then - table.insert(errors, prefix .. ": Missing required field 'id'") - elseif type(component.id) ~= "string" then - table.insert(errors, prefix .. ": 'id' must be a string") - end +--- Validate layout structure recursively +---@param layout ComponentLayout The layout to validate +---@param path string The current path for error messages +---@return string[] errors List of validation errors +M.validate_layout = validate_layout - if not component.type then - table.insert(errors, prefix .. ": Missing required field 'type'") - elseif type(component.type) ~= "string" then - table.insert(errors, prefix .. ": 'type' must be a string") - end - - -- Optional but recommended fields - if component.name and type(component.name) ~= "string" then - table.insert(errors, prefix .. ": 'name' must be a string") - end - - if component.description and type(component.description) ~= "string" then - table.insert(errors, prefix .. ": 'description' must be a string") - end - - -- Validate props if present - if component.props then - if type(component.props) ~= "table" then - table.insert(errors, prefix .. ": 'props' must be an object") - end - end - - -- Validate layout if present - if component.layout then - if type(component.layout) ~= "table" then - table.insert(errors, prefix .. ": 'layout' must be an object") - else - local layout_errors = M.validate_layout(component.layout, prefix .. ".layout") - for _, err in ipairs(layout_errors) do - table.insert(errors, err) - end - end - end - - -- Validate scripts if present - if component.scripts then - if type(component.scripts) ~= "table" then - table.insert(errors, prefix .. ": 'scripts' must be an object") - end - end - - -- Validate bindings if present - if component.bindings then - if type(component.bindings) ~= "table" then - table.insert(errors, prefix .. ": 'bindings' must be an object") - else - if component.bindings.dbal ~= nil and type(component.bindings.dbal) ~= "boolean" then - table.insert(errors, prefix .. ": 'bindings.dbal' must be a boolean") - end - if component.bindings.browser ~= nil and type(component.bindings.browser) ~= "boolean" then - table.insert(errors, prefix .. ": 'bindings.browser' must be a boolean") - end - end - end - - return errors -end - --- Validate layout structure recursively -function M.validate_layout(layout, path) - local errors = {} - - if not layout.type then - table.insert(errors, path .. ": Missing required field 'type'") - elseif type(layout.type) ~= "string" then - table.insert(errors, path .. ": 'type' must be a string") - end - - if layout.props and type(layout.props) ~= "table" then - table.insert(errors, path .. ": 'props' must be an object") - end - - if layout.children then - if type(layout.children) ~= "table" then - table.insert(errors, path .. ": 'children' must be an array") - else - for i, child in ipairs(layout.children) do - if type(child) == "table" then - local child_errors = M.validate_layout(child, path .. ".children[" .. i .. "]") - for _, err in ipairs(child_errors) do - table.insert(errors, err) - end - end - end - end - end - - return errors -end - --- Validate components.json (array of components) -function M.validate_components(components) - local errors = {} - - if type(components) ~= "table" then - table.insert(errors, "components.json must be an array") - return false, errors - end - - -- Validate each component - for i, component in ipairs(components) do - local comp_errors = M.validate_component(component, i) - for _, err in ipairs(comp_errors) do - table.insert(errors, err) - end - end - - return #errors == 0, errors -end +--- Validate components.json (array of components) +---@param components Component[] Array of component definitions +---@return boolean valid Whether all components are valid +---@return string[] errors List of validation errors +M.validate_components = validate_components return M diff --git a/packages/package_validator/seed/scripts/init.lua b/packages/package_validator/seed/scripts/init.lua index da6cb2ca7..3077f92cd 100644 --- a/packages/package_validator/seed/scripts/init.lua +++ b/packages/package_validator/seed/scripts/init.lua @@ -1,9 +1,14 @@ --- Schema Validator initialization +--- Schema Validator initialization +--- Package validator entry point providing validation utilities +---@module init + local validate = require("validate") +---@class PackageValidator local M = {} --- Initialize the validator +--- Initialize the validator +---@return { name: string, version: string, description: string } info Validator info function M.init() return { name = "Schema Validator", @@ -12,18 +17,26 @@ function M.init() } end --- Main validation entry point +--- Main validation entry point +---@param package_id string Package identifier to validate +---@return ValidationResult results Validation results function M.validate_package(package_id) local package_path = "packages/" .. package_id .. "/seed" return validate.validate_package(package_path) end --- Quick validation for metadata +--- Quick validation for metadata +---@param metadata Metadata Metadata to validate +---@return boolean valid Whether valid +---@return string[] errors List of errors function M.validate_metadata(metadata) return validate.validate_metadata_only(metadata) end --- Quick validation for components +--- Quick validation for components +---@param components Component[] Components to validate +---@return boolean valid Whether valid +---@return string[] errors List of errors function M.validate_components(components) return validate.validate_components_only(components) end diff --git a/packages/package_validator/seed/scripts/lua_validator.lua b/packages/package_validator/seed/scripts/lua_validator.lua index e68c5bd36..ddd891064 100644 --- a/packages/package_validator/seed/scripts/lua_validator.lua +++ b/packages/package_validator/seed/scripts/lua_validator.lua @@ -1,146 +1,24 @@ --- Lua file validation +--- Lua file validation facade +--- Re-exports Lua validation functions for backward compatibility +---@module lua_validator + +local validate_lua_syntax = require("validate_lua_syntax") +local validate_lua_structure = require("validate_lua_structure") +local validate_test_file = require("validate_test_file") +local validate_script_exports = require("validate_script_exports") +local validate_package_lua_files = require("validate_package_lua_files") +local check_lua_quality = require("check_lua_quality") +local validate_lua_requires = require("validate_lua_requires") + +---@class LuaValidator local M = {} --- Check if Lua file has valid syntax -function M.validate_lua_syntax(filepath, content) - local errors = {} - - -- Try to load the Lua content - local func, err = loadstring(content) - - if not func then - table.insert(errors, filepath .. ": Syntax error - " .. (err or "unknown error")) - return false, errors - end - - return true, errors -end - --- Check if Lua file follows common patterns -function M.validate_lua_structure(filepath, content) - local warnings = {} - - -- Check for module pattern - if not string.match(content, "local%s+M%s*=%s*{}") and - not string.match(content, "local%s+[%w_]+%s*=%s*{}") then - table.insert(warnings, filepath .. ": Missing module pattern (local M = {})") - end - - -- Check for return statement - if not string.match(content, "return%s+[%w_]+") then - table.insert(warnings, filepath .. ": Missing return statement") - end - - return warnings -end - --- Validate test file structure -function M.validate_test_file(filepath, content) - local errors = {} - local warnings = {} - - -- Check for describe blocks - if not string.match(content, "describe%(") then - table.insert(warnings, filepath .. ": Missing describe() blocks") - end - - -- Check for it/test blocks - if not string.match(content, "it%(") and not string.match(content, "test%(") then - table.insert(warnings, filepath .. ": Missing it() or test() blocks") - end - - -- Check for expect assertions - if not string.match(content, "expect%(") then - table.insert(warnings, filepath .. ": Missing expect() assertions") - end - - return errors, warnings -end - --- Validate script exports match metadata -function M.validate_script_exports(package_path, metadata) - local errors = {} - local warnings = {} - - if not metadata.exports or not metadata.exports.scripts then - return errors, warnings - end - - local scripts_path = package_path .. "/scripts" - - -- Check each exported script exists - for _, script_name in ipairs(metadata.exports.scripts) do - local script_file = scripts_path .. "/" .. script_name .. ".lua" - - -- Check if file exists - local file = io.open(script_file, "r") - if not file then - table.insert(errors, "Exported script not found: " .. script_name .. ".lua") - else - file:close() - end - end - - return errors, warnings -end - --- Validate all Lua files in a package -function M.validate_package_lua_files(package_path) - local results = { - valid = true, - errors = {}, - warnings = {} - } - - local scripts_path = package_path .. "/scripts" - - -- Find all Lua files - local lua_files = {} - -- Note: In real implementation, this would recursively find all .lua files - -- For now, we'll validate the pattern - - if not file_exists(scripts_path) then - table.insert(results.warnings, "No scripts directory found") - return results - end - - return results -end - --- Check for common Lua anti-patterns -function M.check_lua_quality(filepath, content) - local warnings = {} - - -- Check for global variables (potential issue) - if string.match(content, "[^%w_]function%s+[%w_]+%(") then - table.insert(warnings, filepath .. ": Global function definition found (consider local)") - end - - -- Check for TODO comments - if string.match(content, "TODO") or string.match(content, "FIXME") then - table.insert(warnings, filepath .. ": Contains TODO/FIXME comments") - end - - -- Check for print statements (should use proper logging) - local print_count = select(2, string.gsub(content, "print%(", "")) - if print_count > 0 then - table.insert(warnings, filepath .. ": Contains " .. print_count .. " print() statements") - end - - return warnings -end - --- Validate Lua file dependencies -function M.validate_lua_requires(filepath, content) - local errors = {} - local requires = {} - - -- Extract all require statements - for req in string.gmatch(content, 'require%s*%(%s*["\']([^"\']+)["\']%s*%)') do - table.insert(requires, req) - end - - return requires, errors -end +M.validate_lua_syntax = validate_lua_syntax +M.validate_lua_structure = validate_lua_structure +M.validate_test_file = validate_test_file +M.validate_script_exports = validate_script_exports +M.validate_package_lua_files = validate_package_lua_files +M.check_lua_quality = check_lua_quality +M.validate_lua_requires = validate_lua_requires return M diff --git a/packages/package_validator/seed/scripts/metadata_schema.lua b/packages/package_validator/seed/scripts/metadata_schema.lua index c28e01a33..32afa8173 100644 --- a/packages/package_validator/seed/scripts/metadata_schema.lua +++ b/packages/package_validator/seed/scripts/metadata_schema.lua @@ -1,126 +1,16 @@ --- Metadata JSON schema definitions +--- Metadata JSON schema validation facade +--- Re-exports validate_metadata for backward compatibility +---@module metadata_schema + +local validate_metadata = require("validate_metadata") + +---@class MetadataSchema local M = {} --- Validate metadata.json structure -function M.validate_metadata(metadata) - local errors = {} - - -- Required fields - if not metadata.packageId then - table.insert(errors, "Missing required field: packageId") - elseif type(metadata.packageId) ~= "string" then - table.insert(errors, "packageId must be a string") - elseif not string.match(metadata.packageId, "^[a-z_]+$") then - table.insert(errors, "packageId must contain only lowercase letters and underscores") - end - - if not metadata.name then - table.insert(errors, "Missing required field: name") - elseif type(metadata.name) ~= "string" then - table.insert(errors, "name must be a string") - end - - if not metadata.version then - table.insert(errors, "Missing required field: version") - elseif type(metadata.version) ~= "string" then - table.insert(errors, "version must be a string") - elseif not string.match(metadata.version, "^%d+%.%d+%.%d+$") then - table.insert(errors, "version must follow semantic versioning (e.g., 1.0.0)") - end - - if not metadata.description then - table.insert(errors, "Missing required field: description") - elseif type(metadata.description) ~= "string" then - table.insert(errors, "description must be a string") - end - - if not metadata.author then - table.insert(errors, "Missing required field: author") - elseif type(metadata.author) ~= "string" then - table.insert(errors, "author must be a string") - end - - if not metadata.category then - table.insert(errors, "Missing required field: category") - elseif type(metadata.category) ~= "string" then - table.insert(errors, "category must be a string") - end - - -- Optional but must be correct type if present - if metadata.dependencies then - if type(metadata.dependencies) ~= "table" then - table.insert(errors, "dependencies must be an array") - else - for i, dep in ipairs(metadata.dependencies) do - if type(dep) ~= "string" then - table.insert(errors, "dependencies[" .. i .. "] must be a string") - end - end - end - end - - -- Validate devDependencies if present - if metadata.devDependencies then - if type(metadata.devDependencies) ~= "table" then - table.insert(errors, "devDependencies must be an array") - else - for i, dep in ipairs(metadata.devDependencies) do - if type(dep) ~= "string" then - table.insert(errors, "devDependencies[" .. i .. "] must be a string") - end - end - end - end - - if metadata.exports then - if type(metadata.exports) ~= "table" then - table.insert(errors, "exports must be an object") - else - -- Validate exports.components - if metadata.exports.components then - if type(metadata.exports.components) ~= "table" then - table.insert(errors, "exports.components must be an array") - end - end - - -- Validate exports.scripts - if metadata.exports.scripts then - if type(metadata.exports.scripts) ~= "table" then - table.insert(errors, "exports.scripts must be an array") - end - end - - -- Validate exports.pages - if metadata.exports.pages then - if type(metadata.exports.pages) ~= "table" then - table.insert(errors, "exports.pages must be an array") - end - end - end - end - - if metadata.minLevel then - if type(metadata.minLevel) ~= "number" then - table.insert(errors, "minLevel must be a number") - elseif metadata.minLevel < 1 or metadata.minLevel > 6 then - table.insert(errors, "minLevel must be between 1 and 6") - end - end - - if metadata.bindings then - if type(metadata.bindings) ~= "table" then - table.insert(errors, "bindings must be an object") - else - if metadata.bindings.dbal ~= nil and type(metadata.bindings.dbal) ~= "boolean" then - table.insert(errors, "bindings.dbal must be a boolean") - end - if metadata.bindings.browser ~= nil and type(metadata.bindings.browser) ~= "boolean" then - table.insert(errors, "bindings.browser must be a boolean") - end - end - end - - return #errors == 0, errors -end +--- Validate metadata.json structure +---@param metadata Metadata The metadata object to validate +---@return boolean valid Whether the metadata is valid +---@return string[] errors List of validation errors +M.validate_metadata = validate_metadata return M diff --git a/packages/package_validator/seed/scripts/structure_validator.lua b/packages/package_validator/seed/scripts/structure_validator.lua index a462cb1a4..9d2ea3465 100644 --- a/packages/package_validator/seed/scripts/structure_validator.lua +++ b/packages/package_validator/seed/scripts/structure_validator.lua @@ -1,284 +1,32 @@ --- Package folder structure validation +--- Structure validation facade +--- Re-exports structure validation functions for backward compatibility +---@module structure_validator + +local structure_config = require("structure_config") +local validate_structure = require("validate_structure") +local validate_scripts_structure = require("validate_scripts_structure") +local validate_static_content = require("validate_static_content") +local validate_naming_conventions = require("validate_naming_conventions") +local validate_test_structure = require("validate_test_structure") +local validate_package_structure = require("validate_package_structure") + +---@class StructureValidator local M = {} --- Expected package structure -M.REQUIRED_STRUCTURE = { - ["seed/metadata.json"] = true, - ["seed/components.json"] = true -} +-- Structure configuration +M.REQUIRED_STRUCTURE = structure_config.REQUIRED +M.OPTIONAL_STRUCTURE = structure_config.OPTIONAL -M.OPTIONAL_STRUCTURE = { - ["seed/scripts/"] = "directory", - ["seed/scripts/init.lua"] = "file", - ["seed/scripts/tests/"] = "directory", - ["static_content/"] = "directory", - ["static_content/icon.svg"] = "file", - ["README.md"] = "file", - ["examples/"] = "directory" -} - --- Validate basic folder structure -function M.validate_structure(package_path) - local errors = {} - local warnings = {} - - -- Check required files - for path, _ in pairs(M.REQUIRED_STRUCTURE) do - local full_path = package_path .. "/" .. path - local file = io.open(full_path, "r") - - if not file then - table.insert(errors, "Required file missing: " .. path) - else - file:close() - end - end - - -- Check optional but recommended files - for path, type in pairs(M.OPTIONAL_STRUCTURE) do - local full_path = package_path .. "/" .. path - - if type == "file" then - local file = io.open(full_path, "r") - if not file then - table.insert(warnings, "Recommended file missing: " .. path) - else - file:close() - end - elseif type == "directory" then - -- Note: Directory checking would be done with OS-specific commands - -- This is a placeholder for the pattern - end - end - - return errors, warnings -end - --- Validate scripts directory structure -function M.validate_scripts_structure(package_path, metadata) - local errors = {} - local warnings = {} - - local scripts_path = package_path .. "/scripts" - - -- Check if scripts directory exists when exports.scripts is defined - if metadata.exports and metadata.exports.scripts and #metadata.exports.scripts > 0 then - local dir_exists = false - local test_file = io.open(scripts_path .. "/init.lua", "r") - if test_file then - test_file:close() - dir_exists = true - end - - if not dir_exists then - table.insert(errors, "scripts/ directory required when exports.scripts is defined") - end - - -- Check for init.lua - local init_file = io.open(scripts_path .. "/init.lua", "r") - if not init_file then - table.insert(warnings, "scripts/init.lua is recommended as entry point") - else - init_file:close() - end - - -- Check for tests directory if lua_test is a devDependency - local has_lua_test = false - if metadata.devDependencies then - for _, dep in ipairs(metadata.devDependencies) do - if dep == "lua_test" then - has_lua_test = true - break - end - end - end - - if has_lua_test then - -- lua_test is a devDependency, so tests are expected - local test_init = io.open(scripts_path .. "/tests/metadata.test.lua", "r") - if not test_init then - table.insert(warnings, "scripts/tests/ directory with test files recommended when lua_test is a devDependency") - else - test_init:close() - end - end - end - - return errors, warnings -end - --- Validate static content structure -function M.validate_static_content(package_path, metadata) - local errors = {} - local warnings = {} - - if metadata.icon then - local icon_path = package_path .. "/" .. metadata.icon - local icon_file = io.open(icon_path, "r") - - if not icon_file then - table.insert(errors, "Icon file not found: " .. metadata.icon) - else - icon_file:close() - end - else - table.insert(warnings, "No icon defined in metadata") - end - - return errors, warnings -end - --- Check for orphaned files (files not referenced in metadata) -function M.check_orphaned_files(package_path, metadata) - local warnings = {} - - -- This would scan the package directory and check if files are referenced - -- Placeholder for the pattern - - return warnings -end - --- Validate naming conventions -function M.validate_naming_conventions(package_path, metadata) - local errors = {} - local warnings = {} - - -- Package directory should match packageId - local dir_name = package_path:match("([^/]+)$") - if dir_name ~= metadata.packageId then - table.insert(errors, "Package directory name '" .. dir_name .. "' does not match packageId '" .. metadata.packageId .. "'") - end - - -- Check script file naming - if metadata.exports and metadata.exports.scripts then - for _, script_name in ipairs(metadata.exports.scripts) do - -- Script names should be lowercase with underscores - if not string.match(script_name, "^[a-z_]+$") then - table.insert(warnings, "Script name '" .. script_name .. "' should use lowercase and underscores only") - end - end - end - - -- Check component naming - if metadata.exports and metadata.exports.components then - for _, component_name in ipairs(metadata.exports.components) do - -- Component names can be PascalCase or snake_case - local is_valid = string.match(component_name, "^[A-Z][a-zA-Z]+$") or - string.match(component_name, "^[a-z_]+$") - if not is_valid then - table.insert(warnings, "Component name '" .. component_name .. "' should use PascalCase or snake_case") - end - end - end - - return errors, warnings -end - --- Validate test files structure -function M.validate_test_structure(package_path, metadata) - local errors = {} - local warnings = {} - - -- Check if lua_test is a devDependency - local has_lua_test = false - if metadata.devDependencies then - for _, dep in ipairs(metadata.devDependencies) do - if dep == "lua_test" then - has_lua_test = true - break - end - end - end - - if not has_lua_test then - -- No lua_test, tests are optional - return errors, warnings - end - - local tests_path = package_path .. "/scripts/tests" - - -- Check for common test files - local common_tests = { - "metadata.test.lua", - "components.test.lua" - } - - local found_tests = false - for _, test_file in ipairs(common_tests) do - local test_path = tests_path .. "/" .. test_file - local file = io.open(test_path, "r") - if file then - file:close() - found_tests = true - end - end - - if not found_tests then - table.insert(warnings, "No test files found (metadata.test.lua, components.test.lua) despite lua_test devDependency") - end - - return errors, warnings -end - --- Validate complete package structure -function M.validate_package_structure(package_path, metadata) - local results = { - valid = true, - errors = {}, - warnings = {} - } - - -- Basic structure - local struct_errors, struct_warnings = M.validate_structure(package_path) - for _, err in ipairs(struct_errors) do - table.insert(results.errors, err) - results.valid = false - end - for _, warn in ipairs(struct_warnings) do - table.insert(results.warnings, warn) - end - - -- Scripts structure - local script_errors, script_warnings = M.validate_scripts_structure(package_path, metadata) - for _, err in ipairs(script_errors) do - table.insert(results.errors, err) - results.valid = false - end - for _, warn in ipairs(script_warnings) do - table.insert(results.warnings, warn) - end - - -- Static content - local static_errors, static_warnings = M.validate_static_content(package_path, metadata) - for _, err in ipairs(static_errors) do - table.insert(results.errors, err) - results.valid = false - end - for _, warn in ipairs(static_warnings) do - table.insert(results.warnings, warn) - end - - -- Naming conventions - local naming_errors, naming_warnings = M.validate_naming_conventions(package_path, metadata) - for _, err in ipairs(naming_errors) do - table.insert(results.errors, err) - results.valid = false - end - for _, warn in ipairs(naming_warnings) do - table.insert(results.warnings, warn) - end - - -- Test structure (only if lua_test is a devDependency) - local test_errors, test_warnings = M.validate_test_structure(package_path, metadata) - for _, err in ipairs(test_errors) do - table.insert(results.errors, err) - results.valid = false - end - for _, warn in ipairs(test_warnings) do - table.insert(results.warnings, warn) - end - - return results +-- Validation functions +M.validate_structure = validate_structure +M.validate_scripts_structure = validate_scripts_structure +M.validate_static_content = validate_static_content +M.check_orphaned_files = function(package_path, metadata) + -- Placeholder for orphaned file checking + return {} end +M.validate_naming_conventions = validate_naming_conventions +M.validate_test_structure = validate_test_structure +M.validate_package_structure = validate_package_structure return M diff --git a/packages/package_validator/seed/scripts/validate.lua b/packages/package_validator/seed/scripts/validate.lua index 3d6d8c0e7..eea23e789 100644 --- a/packages/package_validator/seed/scripts/validate.lua +++ b/packages/package_validator/seed/scripts/validate.lua @@ -1,132 +1,15 @@ --- Main validation orchestrator -local metadata_schema = require("metadata_schema") -local component_schema = require("component_schema") -local structure_validator = require("structure_validator") -local lua_validator = require("lua_validator") +--- Main validation orchestrator facade +--- Re-exports validation functions for backward compatibility +---@module validate +local validate_package_module = require("validate_package") + +---@class Validate local M = {} --- Validate a complete package -function M.validate_package(package_path, options) - options = options or {} - local results = { - valid = true, - errors = {}, - warnings = {} - } - - -- Load and validate metadata.json - local metadata_path = package_path .. "/metadata.json" - local metadata_content = load_file(metadata_path) - - if not metadata_content then - table.insert(results.errors, "Failed to load metadata.json") - results.valid = false - return results - end - - local metadata = parse_json(metadata_content) - if not metadata then - table.insert(results.errors, "Failed to parse metadata.json") - results.valid = false - return results - end - - -- 1. Validate metadata schema - local metadata_valid, metadata_errors = metadata_schema.validate_metadata(metadata) - if not metadata_valid then - for _, err in ipairs(metadata_errors) do - table.insert(results.errors, "metadata.json: " .. err) - end - results.valid = false - end - - -- 2. Validate components.json - local components_path = package_path .. "/components.json" - local components_content = load_file(components_path) - - if components_content then - local components = parse_json(components_content) - if components then - local components_valid, component_errors = component_schema.validate_components(components) - if not components_valid then - for _, err in ipairs(component_errors) do - table.insert(results.errors, "components.json: " .. err) - end - results.valid = false - end - else - table.insert(results.errors, "Failed to parse components.json") - results.valid = false - end - else - table.insert(results.warnings, "components.json not found (optional)") - end - - -- 3. Validate folder structure - if not options.skipStructure then - local structure_results = structure_validator.validate_package_structure(package_path, metadata) - if not structure_results.valid then - results.valid = false - end - for _, err in ipairs(structure_results.errors) do - table.insert(results.errors, "Structure: " .. err) - end - for _, warn in ipairs(structure_results.warnings) do - table.insert(results.warnings, "Structure: " .. warn) - end - end - - -- 4. Validate Lua script exports - if not options.skipLua and metadata.exports and metadata.exports.scripts then - local script_errors, script_warnings = lua_validator.validate_script_exports(package_path, metadata) - for _, err in ipairs(script_errors) do - table.insert(results.errors, "Lua: " .. err) - results.valid = false - end - for _, warn in ipairs(script_warnings) do - table.insert(results.warnings, "Lua: " .. warn) - end - end - - return results -end - --- Validate just metadata -function M.validate_metadata_only(metadata) - return metadata_schema.validate_metadata(metadata) -end - --- Validate just components -function M.validate_components_only(components) - return component_schema.validate_components(components) -end - --- Format validation results for display -function M.format_results(results) - local output = {} - - if results.valid then - table.insert(output, "✓ Validation passed") - else - table.insert(output, "✗ Validation failed") - end - - if #results.errors > 0 then - table.insert(output, "\nErrors:") - for _, err in ipairs(results.errors) do - table.insert(output, " • " .. err) - end - end - - if #results.warnings > 0 then - table.insert(output, "\nWarnings:") - for _, warn in ipairs(results.warnings) do - table.insert(output, " • " .. warn) - end - end - - return table.concat(output, "\n") -end +M.validate_package = validate_package_module.validate_package +M.validate_metadata_only = validate_package_module.validate_metadata_only +M.validate_components_only = validate_package_module.validate_components_only +M.format_results = validate_package_module.format_results return M diff --git a/packages/quick_guide/seed/scripts/add_step.lua b/packages/quick_guide/seed/scripts/add_step.lua new file mode 100644 index 000000000..cb878671a --- /dev/null +++ b/packages/quick_guide/seed/scripts/add_step.lua @@ -0,0 +1,17 @@ +local create_step = require("create_step") + +--- Add a new step to the list +---@param steps Step[] Array of steps +---@return Step[] Updated steps array +---@return Step New step that was added +local function add_step(steps) + local newStep = create_step() + local result = {} + for i, step in ipairs(steps) do + result[i] = step + end + result[#result + 1] = newStep + return result, newStep +end + +return add_step diff --git a/packages/quick_guide/seed/scripts/create_step.lua b/packages/quick_guide/seed/scripts/create_step.lua new file mode 100644 index 000000000..13bca5c1f --- /dev/null +++ b/packages/quick_guide/seed/scripts/create_step.lua @@ -0,0 +1,15 @@ +local generate_step_id = require("generate_step_id") + +--- Create a new empty step +---@return Step New step with default values +local function create_step() + return { + id = generate_step_id(), + title = "New step", + description = "Describe what happens in this step.", + duration = "1-2 min", + mediaUrl = nil + } +end + +return create_step diff --git a/packages/quick_guide/seed/scripts/generate_step_id.lua b/packages/quick_guide/seed/scripts/generate_step_id.lua new file mode 100644 index 000000000..a9fb41967 --- /dev/null +++ b/packages/quick_guide/seed/scripts/generate_step_id.lua @@ -0,0 +1,7 @@ +--- Generate a unique step ID +---@return string Unique step identifier +local function generate_step_id() + return "step_" .. tostring(os.time()) .. "_" .. math.random(1000, 9999) +end + +return generate_step_id diff --git a/packages/quick_guide/seed/scripts/handle_thumbnail_change.lua b/packages/quick_guide/seed/scripts/handle_thumbnail_change.lua new file mode 100644 index 000000000..17623107b --- /dev/null +++ b/packages/quick_guide/seed/scripts/handle_thumbnail_change.lua @@ -0,0 +1,19 @@ +local is_valid_url = require("is_valid_url") +local is_image_url = require("is_image_url") + +--- Handle thumbnail URL change +---@param state MediaState Current state +---@param newUrl string New thumbnail URL +---@return MediaState Updated state +local function handle_thumbnail_change(state, newUrl) + return { + thumbnailUrl = newUrl, + videoUrl = state.videoUrl, + thumbnailValid = is_valid_url(newUrl), + videoValid = state.videoValid, + thumbnailIsImage = is_image_url(newUrl), + videoIsVideo = state.videoIsVideo + } +end + +return handle_thumbnail_change diff --git a/packages/quick_guide/seed/scripts/handle_video_change.lua b/packages/quick_guide/seed/scripts/handle_video_change.lua new file mode 100644 index 000000000..7473c648f --- /dev/null +++ b/packages/quick_guide/seed/scripts/handle_video_change.lua @@ -0,0 +1,19 @@ +local is_valid_url = require("is_valid_url") +local is_video_url = require("is_video_url") + +--- Handle video URL change +---@param state MediaState Current state +---@param newUrl string New video URL +---@return MediaState Updated state +local function handle_video_change(state, newUrl) + return { + thumbnailUrl = state.thumbnailUrl, + videoUrl = newUrl, + thumbnailValid = state.thumbnailValid, + videoValid = is_valid_url(newUrl), + thumbnailIsImage = state.thumbnailIsImage, + videoIsVideo = is_video_url(newUrl) + } +end + +return handle_video_change diff --git a/packages/quick_guide/seed/scripts/init.lua b/packages/quick_guide/seed/scripts/init.lua index ab997246e..ca84117bb 100644 --- a/packages/quick_guide/seed/scripts/init.lua +++ b/packages/quick_guide/seed/scripts/init.lua @@ -1,9 +1,16 @@ -- Quick Guide package initialization + +---@class QuickGuideModule +---@field name string Package name +---@field version string Package version +---@field init fun(): boolean Initialize the module local M = {} M.name = "quick_guide" M.version = "1.0.0" +---Initialize the quick guide module +---@return boolean Success status function M.init() log("Quick Guide package initialized") return true diff --git a/packages/quick_guide/seed/scripts/is_image_url.lua b/packages/quick_guide/seed/scripts/is_image_url.lua new file mode 100644 index 000000000..e1ccd8bc3 --- /dev/null +++ b/packages/quick_guide/seed/scripts/is_image_url.lua @@ -0,0 +1,19 @@ +local is_valid_url = require("is_valid_url") + +--- Check if URL is an image +---@param url? string URL to check +---@return boolean Whether URL points to an image +local function is_image_url(url) + if not is_valid_url(url) then + return false + end + local patterns = { "%.png$", "%.jpg$", "%.jpeg$", "%.gif$", "%.webp$", "%.svg$" } + for _, pattern in ipairs(patterns) do + if string.match(url:lower(), pattern) then + return true + end + end + return false +end + +return is_image_url diff --git a/packages/quick_guide/seed/scripts/is_valid_url.lua b/packages/quick_guide/seed/scripts/is_valid_url.lua new file mode 100644 index 000000000..3feaa43e0 --- /dev/null +++ b/packages/quick_guide/seed/scripts/is_valid_url.lua @@ -0,0 +1,11 @@ +--- Validate a URL (basic check) +---@param url? string URL to validate +---@return boolean Whether URL is valid +local function is_valid_url(url) + if not url or url == "" then + return false + end + return string.match(url, "^https?://") ~= nil +end + +return is_valid_url diff --git a/packages/quick_guide/seed/scripts/is_video_url.lua b/packages/quick_guide/seed/scripts/is_video_url.lua new file mode 100644 index 000000000..022f9f131 --- /dev/null +++ b/packages/quick_guide/seed/scripts/is_video_url.lua @@ -0,0 +1,19 @@ +local is_valid_url = require("is_valid_url") + +--- Check if URL is a video embed +---@param url? string URL to check +---@return boolean Whether URL points to a video +local function is_video_url(url) + if not is_valid_url(url) then + return false + end + local patterns = { "youtube%.com", "vimeo%.com", "%.mp4$", "%.webm$" } + for _, pattern in ipairs(patterns) do + if string.match(url:lower(), pattern) then + return true + end + end + return false +end + +return is_video_url diff --git a/packages/quick_guide/seed/scripts/media.lua b/packages/quick_guide/seed/scripts/media.lua index e0d686946..c86e12475 100644 --- a/packages/quick_guide/seed/scripts/media.lua +++ b/packages/quick_guide/seed/scripts/media.lua @@ -1,77 +1,22 @@ --- Media pane logic for quick guides +--- Media pane logic facade for quick guides +--- Re-exports media functions for backward compatibility +---@module media + +local is_valid_url = require("is_valid_url") +local is_image_url = require("is_image_url") +local is_video_url = require("is_video_url") +local prepare_media_state = require("prepare_media_state") +local handle_thumbnail_change = require("handle_thumbnail_change") +local handle_video_change = require("handle_video_change") + +---@class MediaModule local M = {} --- Validate a URL (basic check) -function M.isValidUrl(url) - if not url or url == "" then - return false - end - return string.match(url, "^https?://") ~= nil -end - --- Check if URL is an image -function M.isImageUrl(url) - if not M.isValidUrl(url) then - return false - end - local patterns = { "%.png$", "%.jpg$", "%.jpeg$", "%.gif$", "%.webp$", "%.svg$" } - for _, pattern in ipairs(patterns) do - if string.match(url:lower(), pattern) then - return true - end - end - return false -end - --- Check if URL is a video embed -function M.isVideoUrl(url) - if not M.isValidUrl(url) then - return false - end - local patterns = { "youtube%.com", "vimeo%.com", "%.mp4$", "%.webm$" } - for _, pattern in ipairs(patterns) do - if string.match(url:lower(), pattern) then - return true - end - end - return false -end - --- Prepare media state -function M.prepareMediaState(props) - props = props or {} - return { - thumbnailUrl = props.thumbnailUrl or "", - videoUrl = props.videoUrl or "", - thumbnailValid = M.isValidUrl(props.thumbnailUrl), - videoValid = M.isValidUrl(props.videoUrl), - thumbnailIsImage = M.isImageUrl(props.thumbnailUrl), - videoIsVideo = M.isVideoUrl(props.videoUrl) - } -end - --- Handle thumbnail change -function M.handleThumbnailChange(state, newUrl) - return { - thumbnailUrl = newUrl, - videoUrl = state.videoUrl, - thumbnailValid = M.isValidUrl(newUrl), - videoValid = state.videoValid, - thumbnailIsImage = M.isImageUrl(newUrl), - videoIsVideo = state.videoIsVideo - } -end - --- Handle video change -function M.handleVideoChange(state, newUrl) - return { - thumbnailUrl = state.thumbnailUrl, - videoUrl = newUrl, - thumbnailValid = state.thumbnailValid, - videoValid = M.isValidUrl(newUrl), - thumbnailIsImage = state.thumbnailIsImage, - videoIsVideo = M.isVideoUrl(newUrl) - } -end +M.isValidUrl = is_valid_url +M.isImageUrl = is_image_url +M.isVideoUrl = is_video_url +M.prepareMediaState = prepare_media_state +M.handleThumbnailChange = handle_thumbnail_change +M.handleVideoChange = handle_video_change return M diff --git a/packages/quick_guide/seed/scripts/prepare_media_state.lua b/packages/quick_guide/seed/scripts/prepare_media_state.lua new file mode 100644 index 000000000..b422e0611 --- /dev/null +++ b/packages/quick_guide/seed/scripts/prepare_media_state.lua @@ -0,0 +1,20 @@ +local is_valid_url = require("is_valid_url") +local is_image_url = require("is_image_url") +local is_video_url = require("is_video_url") + +--- Prepare media state from props +---@param props? MediaProps Input props +---@return MediaState Initial media state +local function prepare_media_state(props) + props = props or {} + return { + thumbnailUrl = props.thumbnailUrl or "", + videoUrl = props.videoUrl or "", + thumbnailValid = is_valid_url(props.thumbnailUrl), + videoValid = is_valid_url(props.videoUrl), + thumbnailIsImage = is_image_url(props.thumbnailUrl), + videoIsVideo = is_video_url(props.videoUrl) + } +end + +return prepare_media_state diff --git a/packages/quick_guide/seed/scripts/remove_step.lua b/packages/quick_guide/seed/scripts/remove_step.lua new file mode 100644 index 000000000..00ef6ce1a --- /dev/null +++ b/packages/quick_guide/seed/scripts/remove_step.lua @@ -0,0 +1,15 @@ +--- Remove a step from the list +---@param steps Step[] Array of steps +---@param stepId string ID of step to remove +---@return Step[] Updated steps array +local function remove_step(steps, stepId) + local result = {} + for _, step in ipairs(steps) do + if step.id ~= stepId then + result[#result + 1] = step + end + end + return result +end + +return remove_step diff --git a/packages/quick_guide/seed/scripts/reset_ordering.lua b/packages/quick_guide/seed/scripts/reset_ordering.lua new file mode 100644 index 000000000..88b94d18a --- /dev/null +++ b/packages/quick_guide/seed/scripts/reset_ordering.lua @@ -0,0 +1,17 @@ +--- Reset step IDs to sequential order +---@param steps Step[] Array of steps +---@return Step[] Steps with reset IDs +local function reset_ordering(steps) + local result = {} + for i, step in ipairs(steps) do + local resetStep = {} + for k, v in pairs(step) do + resetStep[k] = v + end + resetStep.id = "step_" .. tostring(i) + result[i] = resetStep + end + return result +end + +return reset_ordering diff --git a/packages/quick_guide/seed/scripts/steps.lua b/packages/quick_guide/seed/scripts/steps.lua index ef3094dcb..6b3fa2866 100644 --- a/packages/quick_guide/seed/scripts/steps.lua +++ b/packages/quick_guide/seed/scripts/steps.lua @@ -1,107 +1,26 @@ --- Steps editor logic for quick guides +--- Steps editor logic facade for quick guides +--- Re-exports step functions for backward compatibility +---@module steps + +local generate_step_id = require("generate_step_id") +local create_step = require("create_step") +local update_step = require("update_step") +local remove_step = require("remove_step") +local add_step = require("add_step") +local reset_ordering = require("reset_ordering") +local validate_step = require("validate_step") +local validate_all_steps = require("validate_all_steps") + +---@class StepsModule local M = {} --- Generate a unique step ID -function M.generateStepId() - return "step_" .. tostring(os.time()) .. "_" .. math.random(1000, 9999) -end - --- Create a new empty step -function M.createStep() - return { - id = M.generateStepId(), - title = "New step", - description = "Describe what happens in this step.", - duration = "1-2 min", - mediaUrl = nil - } -end - --- Update a step in the list -function M.updateStep(steps, stepId, updates) - local result = {} - for i, step in ipairs(steps) do - if step.id == stepId then - local updatedStep = {} - for k, v in pairs(step) do - updatedStep[k] = v - end - for k, v in pairs(updates) do - updatedStep[k] = v - end - result[i] = updatedStep - else - result[i] = step - end - end - return result -end - --- Remove a step from the list -function M.removeStep(steps, stepId) - local result = {} - for _, step in ipairs(steps) do - if step.id ~= stepId then - result[#result + 1] = step - end - end - return result -end - --- Add a new step to the list -function M.addStep(steps) - local newStep = M.createStep() - local result = {} - for i, step in ipairs(steps) do - result[i] = step - end - result[#result + 1] = newStep - return result, newStep -end - --- Reset step IDs to sequential order -function M.resetOrdering(steps) - local result = {} - for i, step in ipairs(steps) do - local resetStep = {} - for k, v in pairs(step) do - resetStep[k] = v - end - resetStep.id = "step_" .. tostring(i) - result[i] = resetStep - end - return result -end - --- Validate a step -function M.validateStep(step) - local errors = {} - - if not step.title or step.title == "" then - errors.title = "Title is required" - end - - if not step.description or step.description == "" then - errors.description = "Description is required" - end - - return { valid = next(errors) == nil, errors = errors } -end - --- Validate all steps -function M.validateAllSteps(steps) - local allErrors = {} - local valid = true - - for i, step in ipairs(steps) do - local result = M.validateStep(step) - if not result.valid then - valid = false - allErrors[step.id] = result.errors - end - end - - return { valid = valid, errors = allErrors } -end +M.generateStepId = generate_step_id +M.createStep = create_step +M.updateStep = update_step +M.removeStep = remove_step +M.addStep = add_step +M.resetOrdering = reset_ordering +M.validateStep = validate_step +M.validateAllSteps = validate_all_steps return M diff --git a/packages/quick_guide/seed/scripts/types.lua b/packages/quick_guide/seed/scripts/types.lua new file mode 100644 index 000000000..7a127f0b8 --- /dev/null +++ b/packages/quick_guide/seed/scripts/types.lua @@ -0,0 +1,35 @@ +---@meta +-- Type definitions for quick_guide package + +---@class Step +---@field id string Unique step identifier +---@field title string Step title +---@field description string Step description +---@field duration string Estimated duration +---@field mediaUrl? string Optional media URL + +---@class StepValidationErrors +---@field title? string Title error message +---@field description? string Description error message + +---@class StepValidationResult +---@field valid boolean Whether step is valid +---@field errors StepValidationErrors Validation errors + +---@class AllStepsValidationResult +---@field valid boolean Whether all steps are valid +---@field errors table Errors by step ID + +---@class MediaState +---@field thumbnailUrl string Thumbnail URL +---@field videoUrl string Video URL +---@field thumbnailValid boolean Whether thumbnail URL is valid +---@field videoValid boolean Whether video URL is valid +---@field thumbnailIsImage boolean Whether thumbnail is an image URL +---@field videoIsVideo boolean Whether video is a video URL + +---@class MediaProps +---@field thumbnailUrl? string Initial thumbnail URL +---@field videoUrl? string Initial video URL + +return {} diff --git a/packages/quick_guide/seed/scripts/update_step.lua b/packages/quick_guide/seed/scripts/update_step.lua new file mode 100644 index 000000000..46d50557a --- /dev/null +++ b/packages/quick_guide/seed/scripts/update_step.lua @@ -0,0 +1,25 @@ +--- Update a step in the list +---@param steps Step[] Array of steps +---@param stepId string ID of step to update +---@param updates table Partial step updates +---@return Step[] Updated steps array +local function update_step(steps, stepId, updates) + local result = {} + for i, step in ipairs(steps) do + if step.id == stepId then + local updatedStep = {} + for k, v in pairs(step) do + updatedStep[k] = v + end + for k, v in pairs(updates) do + updatedStep[k] = v + end + result[i] = updatedStep + else + result[i] = step + end + end + return result +end + +return update_step diff --git a/packages/quick_guide/seed/scripts/validate_all_steps.lua b/packages/quick_guide/seed/scripts/validate_all_steps.lua new file mode 100644 index 000000000..62101f0c4 --- /dev/null +++ b/packages/quick_guide/seed/scripts/validate_all_steps.lua @@ -0,0 +1,21 @@ +local validate_step = require("validate_step") + +--- Validate all steps +---@param steps Step[] Array of steps to validate +---@return AllStepsValidationResult Validation result for all steps +local function validate_all_steps(steps) + local allErrors = {} + local valid = true + + for _, step in ipairs(steps) do + local result = validate_step(step) + if not result.valid then + valid = false + allErrors[step.id] = result.errors + end + end + + return { valid = valid, errors = allErrors } +end + +return validate_all_steps diff --git a/packages/quick_guide/seed/scripts/validate_step.lua b/packages/quick_guide/seed/scripts/validate_step.lua new file mode 100644 index 000000000..e916d7080 --- /dev/null +++ b/packages/quick_guide/seed/scripts/validate_step.lua @@ -0,0 +1,18 @@ +--- Validate a single step +---@param step Step Step to validate +---@return StepValidationResult Validation result +local function validate_step(step) + local errors = {} + + if not step.title or step.title == "" then + errors.title = "Title is required" + end + + if not step.description or step.description == "" then + errors.description = "Description is required" + end + + return { valid = next(errors) == nil, errors = errors } +end + +return validate_step diff --git a/packages/schema_editor/seed/scripts/fields.lua b/packages/schema_editor/seed/scripts/fields.lua index f20a9ad0e..c9cadb3d1 100644 --- a/packages/schema_editor/seed/scripts/fields.lua +++ b/packages/schema_editor/seed/scripts/fields.lua @@ -1,4 +1,33 @@ -- Schema field types + +---@alias FieldType "string" | "integer" | "float" | "boolean" | "date" | "datetime" | "text" | "json" + +---@class FieldOptions +---@field nullable? boolean Whether field can be null +---@field default? any Default value for field +---@field unique? boolean Whether field must be unique + +---@class FieldReference +---@field table string Referenced table name +---@field field string Referenced field name + +---@class FieldDefinition +---@field name string Field name +---@field type FieldType Field type +---@field nullable boolean Whether field can be null +---@field default? any Default value +---@field unique boolean Whether field must be unique +---@field references? FieldReference Foreign key reference + +---@class FieldsModule +---@field STRING string +---@field INTEGER string +---@field FLOAT string +---@field BOOLEAN string +---@field DATE string +---@field DATETIME string +---@field TEXT string +---@field JSON string local M = {} M.STRING = "string" @@ -10,6 +39,11 @@ M.DATETIME = "datetime" M.TEXT = "text" M.JSON = "json" +---Define a schema field +---@param name string Field name +---@param type FieldType Field type +---@param options? FieldOptions Field options +---@return FieldDefinition function M.define(name, type, options) return { name = name, @@ -20,10 +54,18 @@ function M.define(name, type, options) } end +---Create a primary key field +---@param name string Field name +---@return FieldDefinition function M.primary_key(name) return M.define(name, M.INTEGER, { nullable = false, unique = true }) end +---Create a foreign key field +---@param name string Field name +---@param ref_table string Referenced table name +---@param ref_field string Referenced field name +---@return FieldDefinition function M.foreign_key(name, ref_table, ref_field) local field = M.define(name, M.INTEGER, { nullable = true }) field.references = { table = ref_table, field = ref_field } diff --git a/packages/schema_editor/seed/scripts/init.lua b/packages/schema_editor/seed/scripts/init.lua index 88835f903..5d5cd4894 100644 --- a/packages/schema_editor/seed/scripts/init.lua +++ b/packages/schema_editor/seed/scripts/init.lua @@ -1,9 +1,21 @@ -- Schema Editor initialization + +---@class SchemaEditorModule +---@field name string Package name +---@field version string Package version +---@field init fun(): SchemaEditorInfo Initialize the module local M = {} M.name = "schema_editor" M.version = "1.0.0" +---@class SchemaEditorInfo +---@field name string Package name +---@field version string Package version +---@field loaded boolean Whether module is loaded + +---Initialize the schema editor module +---@return SchemaEditorInfo function M.init() return { name = M.name, diff --git a/packages/schema_editor/seed/scripts/relations.lua b/packages/schema_editor/seed/scripts/relations.lua index 36fc73aa2..67df78d06 100644 --- a/packages/schema_editor/seed/scripts/relations.lua +++ b/packages/schema_editor/seed/scripts/relations.lua @@ -1,10 +1,39 @@ -- Table relationships + +---@alias RelationType "one_to_one" | "one_to_many" | "many_to_many" + +---@class RelationEndpoint +---@field table string Table name +---@field field string Field name + +---@class RelationOptions +---@field cascade? boolean Whether to cascade deletes + +---@class Relation +---@field type RelationType Relation type +---@field from_table string Source table name +---@field from_field string Source field name +---@field to_table string Target table name +---@field to_field string Target field name +---@field cascade boolean Whether to cascade deletes +---@field pivot_table? string Pivot table for many-to-many + +---@class RelationsModule +---@field ONE_TO_ONE string +---@field ONE_TO_MANY string +---@field MANY_TO_MANY string local M = {} M.ONE_TO_ONE = "one_to_one" M.ONE_TO_MANY = "one_to_many" M.MANY_TO_MANY = "many_to_many" +---Define a table relationship +---@param type RelationType Relation type +---@param from RelationEndpoint Source endpoint +---@param to RelationEndpoint Target endpoint +---@param options? RelationOptions Relation options +---@return Relation function M.define(type, from, to, options) return { type = type, @@ -16,14 +45,27 @@ function M.define(type, from, to, options) } end +---Create a one-to-one relationship +---@param from RelationEndpoint Source endpoint +---@param to RelationEndpoint Target endpoint +---@return Relation function M.has_one(from, to) return M.define(M.ONE_TO_ONE, from, to) end +---Create a one-to-many relationship +---@param from RelationEndpoint Source endpoint +---@param to RelationEndpoint Target endpoint +---@return Relation function M.has_many(from, to) return M.define(M.ONE_TO_MANY, from, to) end +---Create a many-to-many relationship +---@param from RelationEndpoint Source endpoint +---@param to RelationEndpoint Target endpoint +---@param pivot string Pivot table name +---@return Relation function M.belongs_to_many(from, to, pivot) local rel = M.define(M.MANY_TO_MANY, from, to) rel.pivot_table = pivot diff --git a/packages/schema_editor/seed/scripts/tables.lua b/packages/schema_editor/seed/scripts/tables.lua index 1e5845d65..6122c4967 100644 --- a/packages/schema_editor/seed/scripts/tables.lua +++ b/packages/schema_editor/seed/scripts/tables.lua @@ -1,6 +1,39 @@ -- Table management + +---@class CreateTableAction +---@field action "create_table" +---@field name string Table name +---@field fields FieldDefinition[] Array of field definitions + +---@class AddFieldAction +---@field action "add_field" +---@field table string Table name +---@field field FieldDefinition Field to add + +---@class RemoveFieldAction +---@field action "remove_field" +---@field table string Table name +---@field field string Field name to remove + +---@class TableEditorProps +---@field name string Table name +---@field fields FieldDefinition[] Array of field definitions + +---@class TableEditorComponent +---@field type "table_editor" +---@field props TableEditorProps + +---@class TableDefinition +---@field name string Table name +---@field fields FieldDefinition[] Array of field definitions + +---@class TablesModule local M = {} +---Create a new table definition +---@param name string Table name +---@param fields? FieldDefinition[] Array of field definitions +---@return CreateTableAction function M.create(name, fields) return { action = "create_table", @@ -9,6 +42,9 @@ function M.create(name, fields) } end +---Render a table editor component +---@param table_def TableDefinition Table definition +---@return TableEditorComponent function M.render(table_def) return { type = "table_editor", @@ -19,6 +55,10 @@ function M.render(table_def) } end +---Create an add field action +---@param table_name string Table name +---@param field FieldDefinition Field to add +---@return AddFieldAction function M.add_field(table_name, field) return { action = "add_field", @@ -27,6 +67,10 @@ function M.add_field(table_name, field) } end +---Create a remove field action +---@param table_name string Table name +---@param field_name string Field name to remove +---@return RemoveFieldAction function M.remove_field(table_name, field_name) return { action = "remove_field", diff --git a/packages/workflow_editor/seed/scripts/run.lua b/packages/workflow_editor/seed/scripts/run.lua index bab2e9561..e144566ec 100644 --- a/packages/workflow_editor/seed/scripts/run.lua +++ b/packages/workflow_editor/seed/scripts/run.lua @@ -1,6 +1,37 @@ -- Workflow run card utilities + +---@class WorkflowRun +---@field workflow_name string Workflow name +---@field run_number integer Run number +---@field started_at string Start timestamp +---@field duration? string Run duration (or nil if running) +---@field status string Run status + +---@class CardChild +---@field type string Child component type +---@field content? string Text content +---@field status? string Status for badge + +---@class RunCardProps +---@field title string Card title +---@field subtitle string Card subtitle + +---@class RunCard +---@field type "card" +---@field props RunCardProps +---@field children CardChild[] + +---@class RunListGrid +---@field type "grid" +---@field columns integer Number of columns +---@field children RunCard[] + +---@class RunModule local M = {} +---Render a workflow run card +---@param run WorkflowRun Workflow run data +---@return RunCard function M.render(run) return { type = "card", @@ -16,6 +47,9 @@ function M.render(run) } end +---Render a list of workflow runs as a grid +---@param runs WorkflowRun[] Array of workflow runs +---@return RunListGrid function M.render_list(runs) local cards = {} for _, run in ipairs(runs) do