mirror of
https://github.com/johndoe6345789/metabuilder.git
synced 2026-04-24 13:54:57 +00:00
refactor: remove LuaScript entity and related operations
- Deleted all LuaScript CRUD operations from Client and entities. - Removed LuaScript validation functions and related files. - Updated InMemoryStore to remove LuaScript storage. - Cleaned up tests by removing LuaScript related test cases. - Adjusted documentation and quick start guide to reflect the removal of LuaScript functionality.
This commit is contained in:
@@ -37,12 +37,6 @@ public:
|
||||
virtual Result<bool> deleteSession(const std::string& id) = 0;
|
||||
virtual Result<std::vector<Session>> listSessions(const ListOptions& options) = 0;
|
||||
|
||||
virtual Result<LuaScript> createLuaScript(const CreateLuaScriptInput& input) = 0;
|
||||
virtual Result<LuaScript> getLuaScript(const std::string& id) = 0;
|
||||
virtual Result<LuaScript> updateLuaScript(const std::string& id, const UpdateLuaScriptInput& input) = 0;
|
||||
virtual Result<bool> deleteLuaScript(const std::string& id) = 0;
|
||||
virtual Result<std::vector<LuaScript>> listLuaScripts(const ListOptions& options) = 0;
|
||||
|
||||
virtual Result<InstalledPackage> createPackage(const CreatePackageInput& input) = 0;
|
||||
virtual Result<InstalledPackage> getPackage(const std::string& id) = 0;
|
||||
virtual Result<InstalledPackage> updatePackage(const std::string& id, const UpdatePackageInput& input) = 0;
|
||||
|
||||
@@ -85,15 +85,6 @@ public:
|
||||
Result<bool> deleteSession(const std::string& id);
|
||||
Result<std::vector<Session>> listSessions(const ListOptions& options);
|
||||
|
||||
Result<LuaScript> createLuaScript(const CreateLuaScriptInput& input);
|
||||
Result<LuaScript> getLuaScript(const std::string& id);
|
||||
Result<LuaScript> updateLuaScript(const std::string& id, const UpdateLuaScriptInput& input);
|
||||
Result<bool> deleteLuaScript(const std::string& id);
|
||||
Result<std::vector<LuaScript>> listLuaScripts(const ListOptions& options);
|
||||
Result<std::vector<LuaScript>> searchLuaScripts(const std::string& query,
|
||||
const std::optional<std::string>& createdBy = std::nullopt,
|
||||
int limit = 20);
|
||||
|
||||
Result<InstalledPackage> createPackage(const CreatePackageInput& input);
|
||||
Result<InstalledPackage> getPackage(const std::string& id);
|
||||
Result<InstalledPackage> updatePackage(const std::string& id, const UpdatePackageInput& input);
|
||||
|
||||
@@ -158,38 +158,6 @@ struct UpdateSessionInput {
|
||||
std::optional<std::string> userAgent;
|
||||
};
|
||||
|
||||
struct CreateLuaScriptInput {
|
||||
std::optional<std::string> tenantId;
|
||||
std::string name;
|
||||
std::optional<std::string> description;
|
||||
std::string code;
|
||||
std::string parameters;
|
||||
std::optional<std::string> returnType;
|
||||
bool isSandboxed = true;
|
||||
std::string allowedGlobals;
|
||||
int timeoutMs = 5000;
|
||||
int version = 1;
|
||||
std::optional<Timestamp> createdAt;
|
||||
std::optional<Timestamp> updatedAt;
|
||||
std::optional<std::string> createdBy;
|
||||
};
|
||||
|
||||
struct UpdateLuaScriptInput {
|
||||
std::optional<std::string> tenantId;
|
||||
std::optional<std::string> name;
|
||||
std::optional<std::string> description;
|
||||
std::optional<std::string> code;
|
||||
std::optional<std::string> parameters;
|
||||
std::optional<std::string> returnType;
|
||||
std::optional<bool> isSandboxed;
|
||||
std::optional<std::string> allowedGlobals;
|
||||
std::optional<int> timeoutMs;
|
||||
std::optional<int> version;
|
||||
std::optional<Timestamp> createdAt;
|
||||
std::optional<Timestamp> updatedAt;
|
||||
std::optional<std::string> createdBy;
|
||||
};
|
||||
|
||||
struct CreatePackageInput {
|
||||
std::string packageId;
|
||||
std::optional<std::string> tenantId;
|
||||
|
||||
@@ -199,32 +199,6 @@ Result<std::vector<Session>> Client::listSessions(const ListOptions& options) {
|
||||
return entities::session::list(getStore(), options);
|
||||
}
|
||||
|
||||
Result<LuaScript> Client::createLuaScript(const CreateLuaScriptInput& input) {
|
||||
return entities::lua_script::create(getStore(), input);
|
||||
}
|
||||
|
||||
Result<LuaScript> Client::getLuaScript(const std::string& id) {
|
||||
return entities::lua_script::get(getStore(), id);
|
||||
}
|
||||
|
||||
Result<LuaScript> Client::updateLuaScript(const std::string& id, const UpdateLuaScriptInput& input) {
|
||||
return entities::lua_script::update(getStore(), id, input);
|
||||
}
|
||||
|
||||
Result<bool> Client::deleteLuaScript(const std::string& id) {
|
||||
return entities::lua_script::remove(getStore(), id);
|
||||
}
|
||||
|
||||
Result<std::vector<LuaScript>> Client::listLuaScripts(const ListOptions& options) {
|
||||
return entities::lua_script::list(getStore(), options);
|
||||
}
|
||||
|
||||
Result<std::vector<LuaScript>> Client::searchLuaScripts(const std::string& query,
|
||||
const std::optional<std::string>& createdBy,
|
||||
int limit) {
|
||||
return entities::lua_script::search(getStore(), query, createdBy, limit);
|
||||
}
|
||||
|
||||
Result<InstalledPackage> Client::createPackage(const CreatePackageInput& input) {
|
||||
return entities::package::create(getStore(), input);
|
||||
}
|
||||
|
||||
@@ -10,8 +10,7 @@
|
||||
#include "user_operations.hpp"
|
||||
#include "page_operations.hpp"
|
||||
#include "workflow_operations.hpp"
|
||||
#include "session_operations.hpp"
|
||||
#include "lua_script_operations.hpp"
|
||||
#include "package_operations.hpp"
|
||||
#include "session_operations.hpp"
|
||||
#include "package_operations.hpp"
|
||||
|
||||
#endif
|
||||
|
||||
@@ -8,13 +8,12 @@
|
||||
#ifndef DBAL_ENTITIES_INDEX_HPP
|
||||
#define DBAL_ENTITIES_INDEX_HPP
|
||||
|
||||
#include "user/index.hpp"
|
||||
#include "page/index.hpp"
|
||||
#include "component/index.hpp"
|
||||
#include "workflow/index.hpp"
|
||||
#include "session/index.hpp"
|
||||
#include "lua_script/index.hpp"
|
||||
#include "package/index.hpp"
|
||||
#include "user/index.hpp"
|
||||
#include "page/index.hpp"
|
||||
#include "component/index.hpp"
|
||||
#include "workflow/index.hpp"
|
||||
#include "session/index.hpp"
|
||||
#include "package/index.hpp"
|
||||
#include "credential/index.hpp"
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
/**
|
||||
* @file create_lua_script.hpp
|
||||
* @brief Create Lua script operation
|
||||
*/
|
||||
#ifndef DBAL_CREATE_LUA_SCRIPT_HPP
|
||||
#define DBAL_CREATE_LUA_SCRIPT_HPP
|
||||
|
||||
#include "dbal/types.hpp"
|
||||
#include "dbal/errors.hpp"
|
||||
#include "../../../store/in_memory_store.hpp"
|
||||
#include "../../../validation/entity/lua_script_validation.hpp"
|
||||
|
||||
namespace dbal {
|
||||
namespace entities {
|
||||
namespace lua_script {
|
||||
|
||||
/**
|
||||
* Create a new Lua script in the store
|
||||
*/
|
||||
inline Result<LuaScript> create(InMemoryStore& store, const CreateLuaScriptInput& input) {
|
||||
if (!validation::isValidLuaScriptName(input.name)) {
|
||||
return Error::validationError("Lua script name must be 1-255 characters");
|
||||
}
|
||||
if (!validation::isValidLuaScriptCode(input.code)) {
|
||||
return Error::validationError("Lua script code must be a non-empty string");
|
||||
}
|
||||
if (!validation::isValidLuaTimeout(input.timeoutMs)) {
|
||||
return Error::validationError("Timeout must be between 100 and 30000 ms");
|
||||
}
|
||||
std::string globals_error;
|
||||
if (!validation::validateLuaAllowedGlobals(input.allowedGlobals, globals_error)) {
|
||||
return Error::validationError(globals_error);
|
||||
}
|
||||
|
||||
if (store.lua_script_names.find(input.name) != store.lua_script_names.end()) {
|
||||
return Error::conflict("Lua script name already exists: " + input.name);
|
||||
}
|
||||
|
||||
LuaScript script;
|
||||
script.id = store.generateId("lua", ++store.lua_script_counter);
|
||||
script.tenantId = input.tenantId;
|
||||
script.name = input.name;
|
||||
script.description = input.description;
|
||||
script.code = input.code;
|
||||
script.parameters = input.parameters;
|
||||
script.returnType = input.returnType;
|
||||
script.isSandboxed = input.isSandboxed;
|
||||
script.allowedGlobals = input.allowedGlobals;
|
||||
script.timeoutMs = input.timeoutMs;
|
||||
script.version = input.version;
|
||||
script.createdAt = input.createdAt.value_or(std::chrono::system_clock::now());
|
||||
script.updatedAt = input.updatedAt.value_or(script.createdAt);
|
||||
script.createdBy = input.createdBy;
|
||||
|
||||
store.lua_scripts[script.id] = script;
|
||||
store.lua_script_names[script.name] = script.id;
|
||||
|
||||
return Result<LuaScript>(script);
|
||||
}
|
||||
|
||||
} // namespace lua_script
|
||||
} // namespace entities
|
||||
} // namespace dbal
|
||||
|
||||
#endif
|
||||
@@ -1,39 +0,0 @@
|
||||
/**
|
||||
* @file delete_lua_script.hpp
|
||||
* @brief Delete Lua script operation
|
||||
*/
|
||||
#ifndef DBAL_DELETE_LUA_SCRIPT_HPP
|
||||
#define DBAL_DELETE_LUA_SCRIPT_HPP
|
||||
|
||||
#include "dbal/types.hpp"
|
||||
#include "dbal/errors.hpp"
|
||||
#include "../../../store/in_memory_store.hpp"
|
||||
|
||||
namespace dbal {
|
||||
namespace entities {
|
||||
namespace lua_script {
|
||||
|
||||
/**
|
||||
* Delete a Lua script by ID
|
||||
*/
|
||||
inline Result<bool> remove(InMemoryStore& store, const std::string& id) {
|
||||
if (id.empty()) {
|
||||
return Error::validationError("Lua script ID cannot be empty");
|
||||
}
|
||||
|
||||
auto it = store.lua_scripts.find(id);
|
||||
if (it == store.lua_scripts.end()) {
|
||||
return Error::notFound("Lua script not found: " + id);
|
||||
}
|
||||
|
||||
store.lua_script_names.erase(it->second.name);
|
||||
store.lua_scripts.erase(it);
|
||||
|
||||
return Result<bool>(true);
|
||||
}
|
||||
|
||||
} // namespace lua_script
|
||||
} // namespace entities
|
||||
} // namespace dbal
|
||||
|
||||
#endif
|
||||
@@ -1,36 +0,0 @@
|
||||
/**
|
||||
* @file get_lua_script.hpp
|
||||
* @brief Get Lua script by ID operation
|
||||
*/
|
||||
#ifndef DBAL_GET_LUA_SCRIPT_HPP
|
||||
#define DBAL_GET_LUA_SCRIPT_HPP
|
||||
|
||||
#include "dbal/types.hpp"
|
||||
#include "dbal/errors.hpp"
|
||||
#include "../../../store/in_memory_store.hpp"
|
||||
|
||||
namespace dbal {
|
||||
namespace entities {
|
||||
namespace lua_script {
|
||||
|
||||
/**
|
||||
* Get a Lua script by ID
|
||||
*/
|
||||
inline Result<LuaScript> get(InMemoryStore& store, const std::string& id) {
|
||||
if (id.empty()) {
|
||||
return Error::validationError("Lua script ID cannot be empty");
|
||||
}
|
||||
|
||||
auto it = store.lua_scripts.find(id);
|
||||
if (it == store.lua_scripts.end()) {
|
||||
return Error::notFound("Lua script not found: " + id);
|
||||
}
|
||||
|
||||
return Result<LuaScript>(it->second);
|
||||
}
|
||||
|
||||
} // namespace lua_script
|
||||
} // namespace entities
|
||||
} // namespace dbal
|
||||
|
||||
#endif
|
||||
@@ -1,67 +0,0 @@
|
||||
/**
|
||||
* @file list_lua_scripts.hpp
|
||||
* @brief List Lua scripts with filtering and pagination
|
||||
*/
|
||||
#ifndef DBAL_LIST_LUA_SCRIPTS_HPP
|
||||
#define DBAL_LIST_LUA_SCRIPTS_HPP
|
||||
|
||||
#include "dbal/types.hpp"
|
||||
#include "dbal/errors.hpp"
|
||||
#include "../../../store/in_memory_store.hpp"
|
||||
#include <algorithm>
|
||||
|
||||
namespace dbal {
|
||||
namespace entities {
|
||||
namespace lua_script {
|
||||
|
||||
/**
|
||||
* List Lua scripts with filtering and pagination
|
||||
*/
|
||||
inline Result<std::vector<LuaScript>> list(InMemoryStore& store, const ListOptions& options) {
|
||||
std::vector<LuaScript> scripts;
|
||||
|
||||
for (const auto& [id, script] : store.lua_scripts) {
|
||||
bool matches = true;
|
||||
|
||||
if (options.filter.find("createdBy") != options.filter.end()) {
|
||||
if (!script.createdBy.has_value() || script.createdBy.value() != options.filter.at("createdBy")) {
|
||||
matches = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (options.filter.find("isSandboxed") != options.filter.end()) {
|
||||
bool filter_sandboxed = options.filter.at("isSandboxed") == "true";
|
||||
if (script.isSandboxed != filter_sandboxed) matches = false;
|
||||
}
|
||||
|
||||
if (matches) {
|
||||
scripts.push_back(script);
|
||||
}
|
||||
}
|
||||
|
||||
if (options.sort.find("name") != options.sort.end()) {
|
||||
std::sort(scripts.begin(), scripts.end(), [](const LuaScript& a, const LuaScript& b) {
|
||||
return a.name < b.name;
|
||||
});
|
||||
} else if (options.sort.find("createdAt") != options.sort.end()) {
|
||||
std::sort(scripts.begin(), scripts.end(), [](const LuaScript& a, const LuaScript& b) {
|
||||
return a.createdAt.value_or(std::chrono::system_clock::time_point()) <
|
||||
b.createdAt.value_or(std::chrono::system_clock::time_point());
|
||||
});
|
||||
}
|
||||
|
||||
int start = (options.page - 1) * options.limit;
|
||||
int end = std::min(start + options.limit, static_cast<int>(scripts.size()));
|
||||
|
||||
if (start < static_cast<int>(scripts.size())) {
|
||||
return Result<std::vector<LuaScript>>(std::vector<LuaScript>(scripts.begin() + start, scripts.begin() + end));
|
||||
}
|
||||
|
||||
return Result<std::vector<LuaScript>>(std::vector<LuaScript>());
|
||||
}
|
||||
|
||||
} // namespace lua_script
|
||||
} // namespace entities
|
||||
} // namespace dbal
|
||||
|
||||
#endif
|
||||
@@ -1,71 +0,0 @@
|
||||
#ifndef DBAL_SEARCH_LUA_SCRIPTS_HPP
|
||||
#define DBAL_SEARCH_LUA_SCRIPTS_HPP
|
||||
|
||||
#include "dbal/errors.hpp"
|
||||
#include "../../../store/in_memory_store.hpp"
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <iterator>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace dbal {
|
||||
namespace entities {
|
||||
namespace lua_script {
|
||||
|
||||
namespace {
|
||||
|
||||
inline std::string toLower(const std::string& value) {
|
||||
std::string lowered;
|
||||
lowered.reserve(value.size());
|
||||
std::transform(value.begin(), value.end(), std::back_inserter(lowered), [](unsigned char c) {
|
||||
return static_cast<char>(std::tolower(c));
|
||||
});
|
||||
return lowered;
|
||||
}
|
||||
|
||||
inline bool containsInsensitive(const std::string& text, const std::string& query) {
|
||||
if (query.empty()) {
|
||||
return false;
|
||||
}
|
||||
return toLower(text).find(toLower(query)) != std::string::npos;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
inline Result<std::vector<LuaScript>> search(InMemoryStore& store,
|
||||
const std::string& query,
|
||||
const std::optional<std::string>& createdBy = std::nullopt,
|
||||
int limit = 20) {
|
||||
if (query.empty()) {
|
||||
return Error::validationError("search query is required");
|
||||
}
|
||||
|
||||
std::vector<LuaScript> matches;
|
||||
for (const auto& [id, script] : store.lua_scripts) {
|
||||
(void)id;
|
||||
if (createdBy.has_value() && (!script.createdBy.has_value() || script.createdBy.value() != createdBy.value())) {
|
||||
continue;
|
||||
}
|
||||
if (containsInsensitive(script.name, query) || containsInsensitive(script.code, query)) {
|
||||
matches.push_back(script);
|
||||
}
|
||||
}
|
||||
|
||||
std::sort(matches.begin(), matches.end(), [](const LuaScript& a, const LuaScript& b) {
|
||||
return a.name < b.name;
|
||||
});
|
||||
|
||||
if (limit > 0 && static_cast<int>(matches.size()) > limit) {
|
||||
matches.resize(limit);
|
||||
}
|
||||
|
||||
return Result<std::vector<LuaScript>>(matches);
|
||||
}
|
||||
|
||||
} // namespace lua_script
|
||||
} // namespace entities
|
||||
} // namespace dbal
|
||||
|
||||
#endif
|
||||
@@ -1,113 +0,0 @@
|
||||
/**
|
||||
* @file update_lua_script.hpp
|
||||
* @brief Update Lua script operation
|
||||
*/
|
||||
#ifndef DBAL_UPDATE_LUA_SCRIPT_HPP
|
||||
#define DBAL_UPDATE_LUA_SCRIPT_HPP
|
||||
|
||||
#include "dbal/types.hpp"
|
||||
#include "dbal/errors.hpp"
|
||||
#include "../../../store/in_memory_store.hpp"
|
||||
#include "../../../validation/entity/lua_script_validation.hpp"
|
||||
|
||||
namespace dbal {
|
||||
namespace entities {
|
||||
namespace lua_script {
|
||||
|
||||
/**
|
||||
* Update an existing Lua script
|
||||
*/
|
||||
inline Result<LuaScript> update(InMemoryStore& store, const std::string& id, const UpdateLuaScriptInput& input) {
|
||||
if (id.empty()) {
|
||||
return Error::validationError("Lua script ID cannot be empty");
|
||||
}
|
||||
|
||||
auto it = store.lua_scripts.find(id);
|
||||
if (it == store.lua_scripts.end()) {
|
||||
return Error::notFound("Lua script not found: " + id);
|
||||
}
|
||||
|
||||
LuaScript& script = it->second;
|
||||
std::string old_name = script.name;
|
||||
|
||||
if (input.name.has_value()) {
|
||||
if (!validation::isValidLuaScriptName(input.name.value())) {
|
||||
return Error::validationError("Lua script name must be 1-255 characters");
|
||||
}
|
||||
auto name_it = store.lua_script_names.find(input.name.value());
|
||||
if (name_it != store.lua_script_names.end() && name_it->second != id) {
|
||||
return Error::conflict("Lua script name already exists: " + input.name.value());
|
||||
}
|
||||
store.lua_script_names.erase(old_name);
|
||||
store.lua_script_names[input.name.value()] = id;
|
||||
script.name = input.name.value();
|
||||
}
|
||||
|
||||
if (input.description.has_value()) {
|
||||
script.description = input.description.value();
|
||||
}
|
||||
|
||||
if (input.code.has_value()) {
|
||||
if (!validation::isValidLuaScriptCode(input.code.value())) {
|
||||
return Error::validationError("Lua script code must be a non-empty string");
|
||||
}
|
||||
script.code = input.code.value();
|
||||
}
|
||||
|
||||
if (input.parameters.has_value()) {
|
||||
script.parameters = input.parameters.value();
|
||||
}
|
||||
|
||||
if (input.returnType.has_value()) {
|
||||
script.returnType = input.returnType.value();
|
||||
}
|
||||
|
||||
if (input.isSandboxed.has_value()) {
|
||||
script.isSandboxed = input.isSandboxed.value();
|
||||
}
|
||||
|
||||
if (input.allowedGlobals.has_value()) {
|
||||
std::string globals_error;
|
||||
if (!validation::validateLuaAllowedGlobals(input.allowedGlobals.value(), globals_error)) {
|
||||
return Error::validationError(globals_error);
|
||||
}
|
||||
script.allowedGlobals = input.allowedGlobals.value();
|
||||
}
|
||||
|
||||
if (input.timeoutMs.has_value()) {
|
||||
if (!validation::isValidLuaTimeout(input.timeoutMs.value())) {
|
||||
return Error::validationError("Timeout must be between 100 and 30000 ms");
|
||||
}
|
||||
script.timeoutMs = input.timeoutMs.value();
|
||||
}
|
||||
|
||||
if (input.version.has_value()) {
|
||||
script.version = input.version.value();
|
||||
}
|
||||
|
||||
if (input.createdAt.has_value()) {
|
||||
script.createdAt = input.createdAt.value();
|
||||
}
|
||||
|
||||
if (input.updatedAt.has_value()) {
|
||||
script.updatedAt = input.updatedAt.value();
|
||||
} else {
|
||||
script.updatedAt = std::chrono::system_clock::now();
|
||||
}
|
||||
|
||||
if (input.createdBy.has_value()) {
|
||||
script.createdBy = input.createdBy.value();
|
||||
}
|
||||
|
||||
if (input.tenantId.has_value()) {
|
||||
script.tenantId = input.tenantId.value();
|
||||
}
|
||||
|
||||
return Result<LuaScript>(script);
|
||||
}
|
||||
|
||||
} // namespace lua_script
|
||||
} // namespace entities
|
||||
} // namespace dbal
|
||||
|
||||
#endif
|
||||
@@ -1,15 +0,0 @@
|
||||
/**
|
||||
* @file index.hpp
|
||||
* @brief Barrel include for Lua script operations
|
||||
*/
|
||||
#ifndef DBAL_LUA_SCRIPT_INDEX_HPP
|
||||
#define DBAL_LUA_SCRIPT_INDEX_HPP
|
||||
|
||||
#include "crud/create_lua_script.hpp"
|
||||
#include "crud/get_lua_script.hpp"
|
||||
#include "crud/update_lua_script.hpp"
|
||||
#include "crud/delete_lua_script.hpp"
|
||||
#include "crud/list_lua_scripts.hpp"
|
||||
#include "crud/search_lua_scripts.hpp"
|
||||
|
||||
#endif
|
||||
@@ -24,7 +24,6 @@ struct InMemoryStore {
|
||||
std::map<std::string, PageConfig> pages;
|
||||
std::map<std::string, Workflow> workflows;
|
||||
std::map<std::string, Session> sessions;
|
||||
std::map<std::string, LuaScript> lua_scripts;
|
||||
std::map<std::string, InstalledPackage> packages;
|
||||
std::map<std::string, Credential> credentials;
|
||||
|
||||
@@ -32,7 +31,6 @@ struct InMemoryStore {
|
||||
std::map<std::string, std::string> page_paths; // path -> id
|
||||
std::map<std::string, std::string> workflow_names; // name -> id
|
||||
std::map<std::string, std::string> session_tokens; // token -> id
|
||||
std::map<std::string, std::string> lua_script_names; // name -> id
|
||||
std::map<std::string, std::string> package_keys; // packageId -> id
|
||||
|
||||
// Entity counters for ID generation
|
||||
@@ -40,7 +38,6 @@ struct InMemoryStore {
|
||||
int page_counter = 0;
|
||||
int workflow_counter = 0;
|
||||
int session_counter = 0;
|
||||
int lua_script_counter = 0;
|
||||
int package_counter = 0;
|
||||
int credential_counter = 0;
|
||||
|
||||
@@ -69,8 +66,6 @@ struct InMemoryStore {
|
||||
workflow_names.clear();
|
||||
sessions.clear();
|
||||
session_tokens.clear();
|
||||
lua_scripts.clear();
|
||||
lua_script_names.clear();
|
||||
packages.clear();
|
||||
package_keys.clear();
|
||||
credentials.clear();
|
||||
@@ -82,7 +77,6 @@ struct InMemoryStore {
|
||||
page_counter = 0;
|
||||
workflow_counter = 0;
|
||||
session_counter = 0;
|
||||
lua_script_counter = 0;
|
||||
package_counter = 0;
|
||||
credential_counter = 0;
|
||||
component_counter = 0;
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
/**
|
||||
* @file lua_script_validation.hpp
|
||||
* @brief Validation functions for LuaScript entity
|
||||
*/
|
||||
#ifndef DBAL_LUA_SCRIPT_VALIDATION_HPP
|
||||
#define DBAL_LUA_SCRIPT_VALIDATION_HPP
|
||||
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace dbal {
|
||||
namespace validation {
|
||||
|
||||
/**
|
||||
* Validate Lua script name (1-255 characters)
|
||||
*/
|
||||
inline bool isValidLuaScriptName(const std::string& name) {
|
||||
return !name.empty() && name.length() <= 255;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate Lua script code (non-empty)
|
||||
*/
|
||||
inline bool isValidLuaScriptCode(const std::string& code) {
|
||||
return !code.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate Lua script timeout (100-30000 ms)
|
||||
*/
|
||||
inline bool isValidLuaTimeout(int timeoutMs) {
|
||||
return timeoutMs >= 100 && timeoutMs <= 30000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a global name is allowed in the Lua sandbox
|
||||
*/
|
||||
inline bool isAllowedLuaGlobal(const std::string& name) {
|
||||
static const std::unordered_set<std::string> allowed = {
|
||||
"assert", "error", "ipairs", "next", "pairs", "pcall", "select",
|
||||
"tonumber", "tostring", "type", "unpack", "xpcall",
|
||||
"string", "table", "math", "bit32", "print", "log"
|
||||
};
|
||||
return allowed.find(name) != allowed.end();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate allowed globals list for Lua scripts
|
||||
*/
|
||||
inline bool validateLuaAllowedGlobals(const std::string& globals, std::string& error) {
|
||||
if (globals.empty()) {
|
||||
error = "allowedGlobals must be a JSON string";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace validation
|
||||
} // namespace dbal
|
||||
|
||||
#endif
|
||||
@@ -1,4 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "entity/lua_script_validation.hpp"
|
||||
|
||||
@@ -10,9 +10,8 @@
|
||||
#include "user_validation.hpp"
|
||||
#include "page_validation.hpp"
|
||||
#include "entity/component_validation.hpp"
|
||||
#include "workflow_validation.hpp"
|
||||
#include "lua_script_validation.hpp"
|
||||
#include "package_validation.hpp"
|
||||
#include "workflow_validation.hpp"
|
||||
#include "package_validation.hpp"
|
||||
#include "entity/credential_validation.hpp"
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1155,148 +1155,6 @@ void test_session_validation() {
|
||||
std::cout << " ✓ Duplicate token rejected" << std::endl;
|
||||
}
|
||||
|
||||
void test_lua_script_crud() {
|
||||
std::cout << "Testing Lua script CRUD operations..." << std::endl;
|
||||
|
||||
dbal::ClientConfig config;
|
||||
config.adapter = "sqlite";
|
||||
config.database_url = ":memory:";
|
||||
dbal::Client client(config);
|
||||
|
||||
dbal::CreateUserInput userInput;
|
||||
userInput.username = "lua_owner";
|
||||
userInput.email = "lua_owner@example.com";
|
||||
auto userResult = client.createUser(userInput);
|
||||
assert(userResult.isOk());
|
||||
|
||||
dbal::CreateLuaScriptInput input;
|
||||
input.name = "health_check";
|
||||
input.description = "Health check";
|
||||
input.code = "return true";
|
||||
input.parameters = "[]";
|
||||
input.isSandboxed = true;
|
||||
input.allowedGlobals = "[]";
|
||||
input.timeoutMs = 1000;
|
||||
input.createdBy = userResult.value().id;
|
||||
|
||||
auto createResult = client.createLuaScript(input);
|
||||
assert(createResult.isOk());
|
||||
std::string scriptId = createResult.value().id;
|
||||
std::cout << " ✓ Lua script created with ID: " << scriptId << std::endl;
|
||||
|
||||
auto getResult = client.getLuaScript(scriptId);
|
||||
assert(getResult.isOk());
|
||||
assert(getResult.value().name == "health_check");
|
||||
std::cout << " ✓ Retrieved Lua script by ID" << std::endl;
|
||||
|
||||
dbal::UpdateLuaScriptInput updateInput;
|
||||
updateInput.timeoutMs = 2000;
|
||||
updateInput.isSandboxed = false;
|
||||
auto updateResult = client.updateLuaScript(scriptId, updateInput);
|
||||
assert(updateResult.isOk());
|
||||
assert(updateResult.value().timeoutMs == 2000);
|
||||
std::cout << " ✓ Lua script updated" << std::endl;
|
||||
|
||||
dbal::ListOptions listOptions;
|
||||
listOptions.filter["isSandboxed"] = "false";
|
||||
auto listResult = client.listLuaScripts(listOptions);
|
||||
assert(listResult.isOk());
|
||||
assert(listResult.value().size() >= 1);
|
||||
std::cout << " ✓ Listed Lua scripts (filtered by isSandboxed=false)" << std::endl;
|
||||
|
||||
auto deleteResult = client.deleteLuaScript(scriptId);
|
||||
assert(deleteResult.isOk());
|
||||
|
||||
auto notFoundResult = client.getLuaScript(scriptId);
|
||||
assert(notFoundResult.isError());
|
||||
std::cout << " ✓ Lua script deleted" << std::endl;
|
||||
}
|
||||
|
||||
void test_lua_script_validation() {
|
||||
std::cout << "Testing Lua script validation..." << std::endl;
|
||||
|
||||
dbal::ClientConfig config;
|
||||
config.adapter = "sqlite";
|
||||
config.database_url = ":memory:";
|
||||
dbal::Client client(config);
|
||||
|
||||
dbal::CreateUserInput userInput;
|
||||
userInput.username = "lua_validator";
|
||||
userInput.email = "lua_validator@example.com";
|
||||
auto userResult = client.createUser(userInput);
|
||||
assert(userResult.isOk());
|
||||
|
||||
dbal::CreateLuaScriptInput input1;
|
||||
input1.name = "invalid-timeout";
|
||||
input1.code = "return true";
|
||||
input1.parameters = "[]";
|
||||
input1.isSandboxed = true;
|
||||
input1.allowedGlobals = "[]";
|
||||
input1.timeoutMs = 50;
|
||||
input1.createdBy = userResult.value().id;
|
||||
auto result1 = client.createLuaScript(input1);
|
||||
assert(result1.isError());
|
||||
assert(result1.error().code() == dbal::ErrorCode::ValidationError);
|
||||
std::cout << " ✓ Invalid timeout rejected" << std::endl;
|
||||
|
||||
dbal::CreateLuaScriptInput input2;
|
||||
input2.name = "duplicate-script";
|
||||
input2.code = "return true";
|
||||
input2.parameters = "[]";
|
||||
input2.isSandboxed = true;
|
||||
input2.allowedGlobals = "[]";
|
||||
input2.timeoutMs = 1000;
|
||||
input2.createdBy = userResult.value().id;
|
||||
auto result2 = client.createLuaScript(input2);
|
||||
assert(result2.isOk());
|
||||
|
||||
dbal::CreateLuaScriptInput input3 = input2;
|
||||
auto result3 = client.createLuaScript(input3);
|
||||
assert(result3.isError());
|
||||
assert(result3.error().code() == dbal::ErrorCode::Conflict);
|
||||
std::cout << " ✓ Duplicate script name rejected" << std::endl;
|
||||
}
|
||||
|
||||
void test_lua_script_search() {
|
||||
std::cout << "Testing Lua script search..." << std::endl;
|
||||
|
||||
dbal::ClientConfig config;
|
||||
config.adapter = "sqlite";
|
||||
config.database_url = ":memory:";
|
||||
dbal::Client client(config);
|
||||
|
||||
dbal::CreateUserInput userInput;
|
||||
userInput.username = "lua_search_owner";
|
||||
userInput.email = "lua_search_owner@example.com";
|
||||
auto userResult = client.createUser(userInput);
|
||||
assert(userResult.isOk());
|
||||
|
||||
dbal::CreateLuaScriptInput scriptInput;
|
||||
scriptInput.name = "search_script";
|
||||
scriptInput.code = "return 'search'";
|
||||
scriptInput.parameters = "[]";
|
||||
scriptInput.allowedGlobals = "[]";
|
||||
scriptInput.createdBy = userResult.value().id;
|
||||
auto createResult = client.createLuaScript(scriptInput);
|
||||
assert(createResult.isOk());
|
||||
|
||||
dbal::CreateLuaScriptInput otherInput = scriptInput;
|
||||
otherInput.name = "other_script";
|
||||
otherInput.code = "return 'other'";
|
||||
auto otherResult = client.createLuaScript(otherInput);
|
||||
assert(otherResult.isOk());
|
||||
|
||||
auto searchResult = client.searchLuaScripts("search", userResult.value().id, 10);
|
||||
assert(searchResult.isOk());
|
||||
assert(searchResult.value().size() == 1);
|
||||
std::cout << " ✓ Script name search works" << std::endl;
|
||||
|
||||
auto codeSearch = client.searchLuaScripts("return 'other'", std::nullopt, 10);
|
||||
assert(codeSearch.isOk());
|
||||
assert(codeSearch.value().size() >= 1);
|
||||
std::cout << " ✓ Script code search works" << std::endl;
|
||||
}
|
||||
|
||||
void test_package_crud() {
|
||||
std::cout << "Testing package CRUD operations..." << std::endl;
|
||||
|
||||
@@ -1316,7 +1174,7 @@ void test_package_crud() {
|
||||
input.version = "1.2.3";
|
||||
input.installedAt = std::chrono::system_clock::now();
|
||||
input.enabled = false;
|
||||
input.config = "{\"entry\":\"index.lua\"}";
|
||||
input.config = "{\"entry\":\"index.js\"}";
|
||||
|
||||
auto createResult = client.createPackage(input);
|
||||
assert(createResult.isOk());
|
||||
@@ -1498,9 +1356,6 @@ int main() {
|
||||
test_workflow_validation();
|
||||
test_session_crud();
|
||||
test_session_validation();
|
||||
test_lua_script_crud();
|
||||
test_lua_script_validation();
|
||||
test_lua_script_search();
|
||||
test_package_crud();
|
||||
test_package_validation();
|
||||
test_package_batch_operations();
|
||||
@@ -1508,7 +1363,7 @@ int main() {
|
||||
|
||||
std::cout << std::endl;
|
||||
std::cout << "==================================================" << std::endl;
|
||||
std::cout << "✅ All 33 test suites passed!" << std::endl;
|
||||
std::cout << "✅ All test suites passed!" << std::endl;
|
||||
std::cout << "==================================================" << std::endl;
|
||||
return 0;
|
||||
} catch (const std::exception& e) {
|
||||
|
||||
@@ -195,63 +195,6 @@
|
||||
status: success
|
||||
output: true
|
||||
|
||||
- name: "LuaScript CRUD operations"
|
||||
description: "Test basic create, read, update, delete operations for LuaScript entity"
|
||||
operations:
|
||||
- action: create
|
||||
entity: User
|
||||
input:
|
||||
username: "lua_owner"
|
||||
email: "lua_owner@example.com"
|
||||
role: "admin"
|
||||
expected:
|
||||
status: success
|
||||
|
||||
- action: create
|
||||
entity: LuaScript
|
||||
input:
|
||||
name: "health_check"
|
||||
description: "Simple health check"
|
||||
code: "return true"
|
||||
isSandboxed: true
|
||||
allowedGlobals: ["math"]
|
||||
timeoutMs: 1000
|
||||
createdBy: "$steps[0].id"
|
||||
expected:
|
||||
status: success
|
||||
output:
|
||||
name: "health_check"
|
||||
isSandboxed: true
|
||||
|
||||
- action: read
|
||||
entity: LuaScript
|
||||
input:
|
||||
id: "$steps[1].id"
|
||||
expected:
|
||||
status: success
|
||||
output:
|
||||
name: "health_check"
|
||||
|
||||
- action: update
|
||||
entity: LuaScript
|
||||
input:
|
||||
id: "$steps[1].id"
|
||||
isSandboxed: false
|
||||
timeoutMs: 2000
|
||||
expected:
|
||||
status: success
|
||||
output:
|
||||
isSandboxed: false
|
||||
timeoutMs: 2000
|
||||
|
||||
- action: delete
|
||||
entity: LuaScript
|
||||
input:
|
||||
id: "$steps[1].id"
|
||||
expected:
|
||||
status: success
|
||||
output: true
|
||||
|
||||
- name: "Package CRUD operations"
|
||||
description: "Test basic create, read, update, delete operations for Package entity"
|
||||
operations:
|
||||
@@ -271,8 +214,8 @@
|
||||
version: "1.2.3"
|
||||
description: "Forum package"
|
||||
author: "MetaBuilder"
|
||||
manifest:
|
||||
entry: "index.lua"
|
||||
manifest:
|
||||
entry: "index.js"
|
||||
isInstalled: false
|
||||
expected:
|
||||
status: success
|
||||
|
||||
@@ -125,14 +125,13 @@ Check browser console for `[DBAL Audit]` logs.
|
||||
Full TypeScript support:
|
||||
|
||||
```typescript
|
||||
import type { User, PageConfig, ComponentNode, Workflow, LuaScript, InstalledPackage, Session } from '../../dbal/development/src'
|
||||
import type { User, PageConfig, ComponentNode, Workflow, InstalledPackage, Session } from '../../dbal/development/src'
|
||||
|
||||
// Type-safe entities
|
||||
const user: User = await client.users.create({ ... })
|
||||
const page: PageConfig = await client.pageConfigs.create({ ... })
|
||||
const component: ComponentNode = await client.componentNodes.create({ ... })
|
||||
const workflow: Workflow = await client.workflows.create({ ... })
|
||||
const script: LuaScript = await client.luaScripts.create({ ... })
|
||||
const pkg: InstalledPackage = await client.installedPackages.create({ ... })
|
||||
const session: Session = await client.sessions.create({ ... })
|
||||
|
||||
@@ -182,28 +181,6 @@ client.workflows.delete(id)
|
||||
client.workflows.list(options)
|
||||
```
|
||||
|
||||
### Lua Scripts
|
||||
```typescript
|
||||
client.luaScripts.create(data)
|
||||
client.luaScripts.read(id)
|
||||
client.luaScripts.update(id, data)
|
||||
client.luaScripts.delete(id)
|
||||
client.luaScripts.list(options)
|
||||
```
|
||||
|
||||
```typescript
|
||||
const script = await client.luaScripts.create({
|
||||
name: 'health_check',
|
||||
description: 'Simple health check',
|
||||
code: 'return true',
|
||||
parameters: JSON.stringify([]),
|
||||
isSandboxed: true,
|
||||
allowedGlobals: JSON.stringify(['math']),
|
||||
timeoutMs: 1000,
|
||||
createdBy: '11111111-1111-1111-1111-111111111111',
|
||||
})
|
||||
```
|
||||
|
||||
### Packages
|
||||
```typescript
|
||||
client.installedPackages.create(data)
|
||||
|
||||
@@ -28,8 +28,8 @@ export const metadata: Metadata = {
|
||||
template: '%s | MetaBuilder',
|
||||
},
|
||||
description:
|
||||
'A data-driven, multi-tenant application platform where 95% of functionality is defined through JSON and Lua.',
|
||||
keywords: ['metabuilder', 'low-code', 'no-code', 'lua', 'platform', 'multi-tenant'],
|
||||
'A data-driven, multi-tenant platform where every experience is powered by JSON packages.',
|
||||
keywords: ['metabuilder', 'low-code', 'no-code', 'platform', 'multi-tenant'],
|
||||
authors: [{ name: 'MetaBuilder Team' }],
|
||||
creator: 'MetaBuilder',
|
||||
icons: {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { Metadata } from 'next'
|
||||
import { notFound } from 'next/navigation'
|
||||
|
||||
import type { JSONComponent } from '@/lib/packages/json/types'
|
||||
import { UIPageRenderer } from '@/components/ui-page-renderer/UIPageRenderer'
|
||||
import { loadPageFromDb } from '@/lib/ui-pages/load-page-from-db'
|
||||
import { loadPageFromLuaPackages } from '@/lib/ui-pages/load-page-from-lua-packages'
|
||||
|
||||
interface PageProps {
|
||||
params: Promise<{
|
||||
@@ -17,33 +17,32 @@ interface PageProps {
|
||||
*
|
||||
* Flow:
|
||||
* 1. JSON seed data → Database (via import-ui-pages.ts)
|
||||
* 2. Database → Load page record
|
||||
* 3. Lua actions → Execute if present
|
||||
* 4. UIPageRenderer → Generate React components
|
||||
* 5. User sees rendered page
|
||||
* 2. Database → Load component tree
|
||||
* 3. UIPageRenderer → Generate React components
|
||||
* 4. User sees rendered page
|
||||
*/
|
||||
export default async function DynamicUIPage({ params }: PageProps) {
|
||||
const resolvedParams = await params
|
||||
const slug = resolvedParams.slug ?? []
|
||||
const path = '/' + slug.join('/')
|
||||
|
||||
// Prefer Lua package-based UI pages, fallback to database-backed pages
|
||||
const rawPageData = (await loadPageFromLuaPackages(path)) ?? (await loadPageFromDb(path))
|
||||
const rawPageData = await loadPageFromDb(path)
|
||||
|
||||
if (rawPageData === null) {
|
||||
notFound()
|
||||
}
|
||||
|
||||
// Transform PageConfig to UIPageData
|
||||
const pageData = {
|
||||
layout: rawPageData,
|
||||
actions: {},
|
||||
const componentTree = rawPageData.componentTree
|
||||
if (componentTree === null || componentTree === undefined) {
|
||||
notFound()
|
||||
}
|
||||
|
||||
// Check authentication if required
|
||||
// TODO: Add auth check based on pageData.requireAuth and pageData.requiredRole
|
||||
const layout = componentTree as JSONComponent
|
||||
if (typeof layout !== 'object' || Array.isArray(layout)) {
|
||||
notFound()
|
||||
}
|
||||
|
||||
return <UIPageRenderer pageData={pageData} />
|
||||
return <UIPageRenderer layout={layout} actions={{}} />
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -54,7 +53,7 @@ export async function generateMetadata({ params }: PageProps): Promise<Metadata>
|
||||
const slug = resolvedParams.slug ?? []
|
||||
const path = '/' + slug.join('/')
|
||||
|
||||
const pageData = (await loadPageFromLuaPackages(path)) ?? (await loadPageFromDb(path))
|
||||
const pageData = await loadPageFromDb(path)
|
||||
|
||||
if (pageData === null) {
|
||||
return {
|
||||
|
||||
@@ -2,28 +2,25 @@
|
||||
|
||||
import React from 'react'
|
||||
|
||||
import { generateComponentTree } from '@/lib/lua/ui/generate-component-tree'
|
||||
import type { LuaUIComponent } from '@/lib/lua/ui/types/lua-ui-package'
|
||||
import type { LuaActionHandler, UIPageData } from '@/lib/ui-pages/load-page-from-db'
|
||||
import { renderJSONComponent } from '@/lib/packages/json/render-json-component'
|
||||
import type { JSONComponent } from '@/lib/packages/json/types'
|
||||
|
||||
type PageActionHandler = (action: string, data: Record<string, unknown>) => void | Promise<void>
|
||||
|
||||
interface UIPageRendererProps {
|
||||
pageData: UIPageData
|
||||
layout: JSONComponent
|
||||
actions?: Record<string, PageActionHandler>
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic TSX renderer for database-loaded UI pages
|
||||
* Flow: Database → Lua → This Component → React Elements → User
|
||||
* Flow: Database → JSON component → React Elements → User
|
||||
*/
|
||||
export function UIPageRenderer({ pageData }: UIPageRendererProps) {
|
||||
// Convert JSON layout to LuaUIComponent structure
|
||||
const layout = pageData.layout as LuaUIComponent
|
||||
export function UIPageRenderer({ layout, actions = {} }: UIPageRendererProps) {
|
||||
const elements = React.useMemo(() => renderJSONComponent(layout), [layout])
|
||||
|
||||
// Create React elements from component tree
|
||||
const elements = generateComponentTree(layout)
|
||||
|
||||
// Provide action handlers via context
|
||||
return (
|
||||
<UIPageActionsContext.Provider value={pageData.actions ?? {}}>
|
||||
<UIPageActionsContext.Provider value={actions}>
|
||||
{elements}
|
||||
</UIPageActionsContext.Provider>
|
||||
)
|
||||
@@ -33,7 +30,7 @@ export function UIPageRenderer({ pageData }: UIPageRendererProps) {
|
||||
* Context for action handlers
|
||||
* Components can access these via useUIPageActions hook
|
||||
*/
|
||||
const UIPageActionsContext = React.createContext<Record<string, LuaActionHandler>>({})
|
||||
const UIPageActionsContext = React.createContext<Record<string, PageActionHandler>>({})
|
||||
|
||||
/**
|
||||
* Hook to access page action handlers
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
/**
|
||||
* Generate React component tree from Lua UI component definitions
|
||||
*
|
||||
* Transforms LuaUIComponent structures into React elements using
|
||||
* the JSON component renderer infrastructure.
|
||||
*/
|
||||
|
||||
import React, { type ReactNode } from 'react'
|
||||
import type { LuaUIComponent } from './types/lua-ui-package'
|
||||
|
||||
export interface ComponentTree {
|
||||
type: string
|
||||
props?: Record<string, unknown>
|
||||
children?: ComponentTree[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Map of basic HTML elements that can be rendered directly
|
||||
*/
|
||||
const HTML_ELEMENTS = new Set([
|
||||
'div', 'span', 'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
|
||||
'section', 'article', 'header', 'footer', 'main', 'aside', 'nav',
|
||||
'ul', 'ol', 'li', 'dl', 'dt', 'dd',
|
||||
'table', 'thead', 'tbody', 'tfoot', 'tr', 'th', 'td',
|
||||
'form', 'input', 'button', 'select', 'option', 'textarea', 'label',
|
||||
'a', 'img', 'video', 'audio', 'canvas', 'svg',
|
||||
'strong', 'em', 'code', 'pre', 'blockquote',
|
||||
])
|
||||
|
||||
/**
|
||||
* Render a single LuaUIComponent to a React element
|
||||
*/
|
||||
function renderComponent(component: LuaUIComponent, key?: string | number): ReactNode {
|
||||
const { type, props = {}, children } = component
|
||||
|
||||
// Render children recursively
|
||||
const renderedChildren = children?.map((child, index) =>
|
||||
renderComponent(child, index)
|
||||
)
|
||||
|
||||
// Handle text content
|
||||
if (type === 'text' && typeof props.content === 'string') {
|
||||
return props.content
|
||||
}
|
||||
|
||||
// Handle HTML elements
|
||||
if (HTML_ELEMENTS.has(type)) {
|
||||
return React.createElement(
|
||||
type,
|
||||
{ ...props, key },
|
||||
renderedChildren
|
||||
)
|
||||
}
|
||||
|
||||
// Handle custom components - wrap in div with data attribute for future component registry
|
||||
return React.createElement(
|
||||
'div',
|
||||
{
|
||||
'data-component': type,
|
||||
className: `component-${type}`,
|
||||
...props,
|
||||
key,
|
||||
},
|
||||
renderedChildren ?? (
|
||||
<span className="component-placeholder">
|
||||
[{type}]
|
||||
</span>
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a complete React component tree from a Lua UI component definition.
|
||||
*
|
||||
* @param luaComponent - The root LuaUIComponent to render
|
||||
* @returns React node tree, or null if input is invalid
|
||||
*/
|
||||
export function generateComponentTree(luaComponent: unknown): ReactNode {
|
||||
// Validate input
|
||||
if (luaComponent === null || luaComponent === undefined) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (typeof luaComponent !== 'object') {
|
||||
return null
|
||||
}
|
||||
|
||||
const component = luaComponent as LuaUIComponent
|
||||
|
||||
// Must have a type to render
|
||||
if (typeof component.type !== 'string' || component.type.length === 0) {
|
||||
return (
|
||||
<div className="component-error">
|
||||
Invalid component: missing type
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return renderComponent(component)
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
/**
|
||||
* Lua UI types (stub)
|
||||
*/
|
||||
|
||||
export interface LuaUIComponent {
|
||||
type: string
|
||||
props?: Record<string, unknown>
|
||||
children?: LuaUIComponent[]
|
||||
}
|
||||
|
||||
export interface LuaUIPackage {
|
||||
id: string
|
||||
name: string
|
||||
components: unknown[]
|
||||
}
|
||||
@@ -5,13 +5,6 @@
|
||||
import type { PageConfig } from '../types/level-types'
|
||||
import { prisma } from '@/lib/config/prisma'
|
||||
|
||||
export type LuaActionHandler = (action: string, data: Record<string, unknown>) => void | Promise<void>
|
||||
|
||||
export interface UIPageData {
|
||||
layout: unknown
|
||||
actions?: Record<string, LuaActionHandler>
|
||||
}
|
||||
|
||||
export async function loadPageFromDb(path: string, tenantId?: string): Promise<PageConfig | null> {
|
||||
// Prisma client typing is generated; suppress lint in environments without generated types.
|
||||
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
/**
|
||||
* Load UI page from Lua packages (stub)
|
||||
*/
|
||||
|
||||
import type { PageConfig } from '../types/level-types'
|
||||
|
||||
export function loadPageFromLuaPackages(_b_path: string): Promise<PageConfig | null> {
|
||||
// TODO: Implement page loading from Lua packages
|
||||
return Promise.resolve(null)
|
||||
}
|
||||
Reference in New Issue
Block a user