refactoring

This commit is contained in:
Sky Johnson 2025-05-01 15:01:55 -05:00
parent b13908b93f
commit efcad468ad
9 changed files with 57 additions and 142 deletions

View File

@ -10,12 +10,12 @@ runner = {
} }
dirs = { dirs = {
routes = "./routes", routes = "routes",
static = "./static", static = "public",
fs = "./fs", fs = "fs",
data = "./data", data = "data",
override = "./override", override = "override",
libs = { libs = {
"./libs" "libs"
} }
} }

View File

@ -107,14 +107,14 @@ func (s *Server) processRequest(ctx *fasthttp.RequestCtx) {
logger.Debug("Processing request %s %s", method, path) logger.Debug("Processing request %s %s", method, path)
params := &routers.Params{} params := &routers.Params{}
bytecode, scriptPath, found := s.luaRouter.GetBytecode(method, path, params) bytecode, scriptPath, routeErr, found := s.luaRouter.GetRouteInfo(method, path, params)
// Check if we found a route but it has no valid bytecode (compile error) // Check if we found a route but it has an error or no valid bytecode
if found && len(bytecode) == 0 { if found && (len(bytecode) == 0 || routeErr != nil) {
errorMsg := "Route exists but failed to compile. Check server logs for details." errorMsg := "Route exists but failed to compile. Check server logs for details."
if node, _ := s.luaRouter.GetNodeWithError(method, path, params); node != nil && node.Error != nil { if routeErr != nil {
errorMsg = node.Error.Error() errorMsg = routeErr.Error()
} }
logger.Error("%s %s - %s", method, path, errorMsg) logger.Error("%s %s - %s", method, path, errorMsg)

View File

@ -17,7 +17,6 @@ import (
func LogRequest(statusCode int, method, path string, duration time.Duration) { func LogRequest(statusCode int, method, path string, duration time.Duration) {
var statusColor, resetColor, methodColor string var statusColor, resetColor, methodColor string
// Status code colors
if statusCode >= 200 && statusCode < 300 { if statusCode >= 200 && statusCode < 300 {
statusColor = "\u001b[32m" // Green for 2xx statusColor = "\u001b[32m" // Green for 2xx
} else if statusCode >= 300 && statusCode < 400 { } else if statusCode >= 300 && statusCode < 400 {
@ -28,7 +27,6 @@ func LogRequest(statusCode int, method, path string, duration time.Duration) {
statusColor = "\u001b[31m" // Red for 5xx and others statusColor = "\u001b[31m" // Red for 5xx and others
} }
// Method colors
switch method { switch method {
case "GET": case "GET":
methodColor = "\u001b[32m" // Green methodColor = "\u001b[32m" // Green
@ -44,7 +42,6 @@ func LogRequest(statusCode int, method, path string, duration time.Duration) {
resetColor = "\u001b[0m" resetColor = "\u001b[0m"
// Format duration
var durationStr string var durationStr string
if duration.Milliseconds() < 1 { if duration.Milliseconds() < 1 {
durationStr = fmt.Sprintf("%.2fµs", float64(duration.Microseconds())) durationStr = fmt.Sprintf("%.2fµs", float64(duration.Microseconds()))
@ -54,7 +51,6 @@ func LogRequest(statusCode int, method, path string, duration time.Duration) {
durationStr = fmt.Sprintf("%.2fs", duration.Seconds()) durationStr = fmt.Sprintf("%.2fs", duration.Seconds())
} }
// Log with colors
logger.Server("%s%d%s %s%s%s %s %s", logger.Server("%s%d%s %s%s%s %s %s",
statusColor, statusCode, resetColor, statusColor, statusCode, resetColor,
methodColor, method, resetColor, methodColor, method, resetColor,
@ -65,23 +61,17 @@ func LogRequest(statusCode int, method, path string, duration time.Duration) {
func QueryToLua(ctx *fasthttp.RequestCtx) map[string]any { func QueryToLua(ctx *fasthttp.RequestCtx) map[string]any {
queryMap := make(map[string]any) queryMap := make(map[string]any)
// Visit all query parameters
ctx.QueryArgs().VisitAll(func(key, value []byte) { ctx.QueryArgs().VisitAll(func(key, value []byte) {
// Convert to string
k := string(key) k := string(key)
v := string(value) v := string(value)
// Check if this key already exists as an array
if existing, ok := queryMap[k]; ok { if existing, ok := queryMap[k]; ok {
// If it's already an array, append to it
if arr, ok := existing.([]string); ok { if arr, ok := existing.([]string); ok {
queryMap[k] = append(arr, v) queryMap[k] = append(arr, v)
} else if str, ok := existing.(string); ok { } else if str, ok := existing.(string); ok {
// Convert existing string to array and append new value
queryMap[k] = []string{str, v} queryMap[k] = []string{str, v}
} }
} else { } else {
// New key, store as string
queryMap[k] = v queryMap[k] = v
} }
}) })
@ -93,27 +83,21 @@ func QueryToLua(ctx *fasthttp.RequestCtx) map[string]any {
func ParseForm(ctx *fasthttp.RequestCtx) (map[string]any, error) { func ParseForm(ctx *fasthttp.RequestCtx) (map[string]any, error) {
formData := make(map[string]any) formData := make(map[string]any)
// Check if multipart form
if strings.Contains(string(ctx.Request.Header.ContentType()), "multipart/form-data") { if strings.Contains(string(ctx.Request.Header.ContentType()), "multipart/form-data") {
return parseMultipartForm(ctx) return parseMultipartForm(ctx)
} }
// Regular form
ctx.PostArgs().VisitAll(func(key, value []byte) { ctx.PostArgs().VisitAll(func(key, value []byte) {
k := string(key) k := string(key)
v := string(value) v := string(value)
// Check if this key already exists
if existing, ok := formData[k]; ok { if existing, ok := formData[k]; ok {
// If it's already an array, append to it
if arr, ok := existing.([]string); ok { if arr, ok := existing.([]string); ok {
formData[k] = append(arr, v) formData[k] = append(arr, v)
} else if str, ok := existing.(string); ok { } else if str, ok := existing.(string); ok {
// Convert existing string to array and append new value
formData[k] = []string{str, v} formData[k] = []string{str, v}
} }
} else { } else {
// New key, store as string
formData[k] = v formData[k] = v
} }
}) })
@ -125,13 +109,11 @@ func ParseForm(ctx *fasthttp.RequestCtx) (map[string]any, error) {
func parseMultipartForm(ctx *fasthttp.RequestCtx) (map[string]any, error) { func parseMultipartForm(ctx *fasthttp.RequestCtx) (map[string]any, error) {
formData := make(map[string]any) formData := make(map[string]any)
// Parse multipart form
form, err := ctx.MultipartForm() form, err := ctx.MultipartForm()
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Process form values
for key, values := range form.Value { for key, values := range form.Value {
if len(values) == 1 { if len(values) == 1 {
formData[key] = values[0] formData[key] = values[0]
@ -140,7 +122,6 @@ func parseMultipartForm(ctx *fasthttp.RequestCtx) (map[string]any, error) {
} }
} }
// Process files (store file info, not the content)
if len(form.File) > 0 { if len(form.File) > 0 {
files := make(map[string]any) files := make(map[string]any)

View File

@ -214,10 +214,8 @@ func getBytecodeKey(handlerPath string) []byte {
// Match finds a handler for the given method and path // Match finds a handler for the given method and path
// Uses the pre-allocated params struct to avoid allocations // Uses the pre-allocated params struct to avoid allocations
func (r *LuaRouter) Match(method, path string, params *Params) (*node, bool) { func (r *LuaRouter) Match(method, path string, params *Params) (*node, bool) {
// Reset params
params.Count = 0 params.Count = 0
// Get route tree for method
r.mu.RLock() r.mu.RLock()
root, exists := r.routes[method] root, exists := r.routes[method]
r.mu.RUnlock() r.mu.RUnlock()
@ -226,10 +224,8 @@ func (r *LuaRouter) Match(method, path string, params *Params) (*node, bool) {
return nil, false return nil, false
} }
// Split path
segments := strings.Split(strings.Trim(path, "/"), "/") segments := strings.Split(strings.Trim(path, "/"), "/")
// Match path
return r.matchPath(root, segments, params, 0) return r.matchPath(root, segments, params, 0)
} }
@ -279,122 +275,100 @@ func (r *LuaRouter) compileHandler(n *node) error {
return nil return nil
} }
// Read the Lua file
content, err := os.ReadFile(n.handler) content, err := os.ReadFile(n.handler)
if err != nil { if err != nil {
n.err = err // Store the error in the node n.err = err
return err return err
} }
// Compile to bytecode
state := luajit.New() state := luajit.New()
if state == nil { if state == nil {
compileErr := errors.New("failed to create Lua state") compileErr := errors.New("failed to create Lua state")
n.err = compileErr // Store the error in the node n.err = compileErr
return compileErr return compileErr
} }
defer state.Close() defer state.Close()
bytecode, err := state.CompileBytecode(string(content), n.handler) bytecode, err := state.CompileBytecode(string(content), n.handler)
if err != nil { if err != nil {
n.err = err // Store the error in the node n.err = err
return err return err
} }
// Store bytecode in cache
bytecodeKey := getBytecodeKey(n.handler) bytecodeKey := getBytecodeKey(n.handler)
r.bytecodeCache.Set(bytecodeKey, bytecode) r.bytecodeCache.Set(bytecodeKey, bytecode)
n.err = nil // Clear any previous error n.err = nil
return nil return nil
} }
// GetBytecode returns the compiled bytecode for a matched route // GetRouteInfo returns the bytecode, script path, and any error for a matched route
// Uses FastCache for both route matching and bytecode retrieval func (r *LuaRouter) GetRouteInfo(method, path string, params *Params) ([]byte, string, error, bool) {
func (r *LuaRouter) GetBytecode(method, path string, params *Params) ([]byte, string, bool) {
// Check route cache first
routeCacheKey := getCacheKey(method, path) routeCacheKey := getCacheKey(method, path)
routeCacheData := r.routeCache.Get(nil, routeCacheKey) routeCacheData := r.routeCache.Get(nil, routeCacheKey)
if len(routeCacheData) > 0 { if len(routeCacheData) > 0 {
// Cache hit - first 8 bytes are bytecode hash, rest is handler path
handlerPath := string(routeCacheData[8:]) handlerPath := string(routeCacheData[8:])
bytecodeKey := routeCacheData[:8] bytecodeKey := routeCacheData[:8]
// Get bytecode from cache
bytecode := r.bytecodeCache.Get(nil, bytecodeKey) bytecode := r.bytecodeCache.Get(nil, bytecodeKey)
if len(bytecode) > 0 {
return bytecode, handlerPath, true
}
// Bytecode not found, check if file was modified
n, exists := r.nodeForHandler(handlerPath) n, exists := r.nodeForHandler(handlerPath)
if !exists { if !exists {
// Handler no longer exists
r.routeCache.Del(routeCacheKey) r.routeCache.Del(routeCacheKey)
return nil, "", false return nil, "", nil, false
}
if len(bytecode) > 0 {
return bytecode, handlerPath, n.err, true
} }
// Check if file was modified
fileInfo, err := os.Stat(handlerPath) fileInfo, err := os.Stat(handlerPath)
if err != nil || fileInfo.ModTime().After(n.modTime) { if err != nil || fileInfo.ModTime().After(n.modTime) {
// Recompile if file was modified
if err := r.compileHandler(n); err != nil { if err := r.compileHandler(n); err != nil {
return nil, handlerPath, true // Return with error return nil, handlerPath, n.err, true
} }
// Update cache
newBytecodeKey := getBytecodeKey(handlerPath) newBytecodeKey := getBytecodeKey(handlerPath)
bytecode = r.bytecodeCache.Get(nil, newBytecodeKey) bytecode = r.bytecodeCache.Get(nil, newBytecodeKey)
// Update route cache
newCacheData := make([]byte, 8+len(handlerPath)) newCacheData := make([]byte, 8+len(handlerPath))
copy(newCacheData[:8], newBytecodeKey) copy(newCacheData[:8], newBytecodeKey)
copy(newCacheData[8:], handlerPath) copy(newCacheData[8:], handlerPath)
r.routeCache.Set(routeCacheKey, newCacheData) r.routeCache.Set(routeCacheKey, newCacheData)
return bytecode, handlerPath, true return bytecode, handlerPath, n.err, true
} }
// Strange case - bytecode not in cache but file not modified // Strange case - bytecode not in cache but file not modified; recompile
// Recompile
if err := r.compileHandler(n); err != nil { if err := r.compileHandler(n); err != nil {
return nil, handlerPath, true return nil, handlerPath, n.err, true
} }
bytecode = r.bytecodeCache.Get(nil, bytecodeKey) bytecode = r.bytecodeCache.Get(nil, bytecodeKey)
return bytecode, handlerPath, true return bytecode, handlerPath, n.err, true
} }
// Cache miss - do normal routing
node, found := r.Match(method, path, params) node, found := r.Match(method, path, params)
if !found { if !found {
return nil, "", false return nil, "", nil, false
} }
// If the route exists but has a compilation error
if node.err != nil {
return nil, node.handler, true
}
// Get bytecode from cache
bytecodeKey := getBytecodeKey(node.handler) bytecodeKey := getBytecodeKey(node.handler)
bytecode := r.bytecodeCache.Get(nil, bytecodeKey) bytecode := r.bytecodeCache.Get(nil, bytecodeKey)
if len(bytecode) == 0 { if len(bytecode) == 0 {
// Compile if not in cache
if err := r.compileHandler(node); err != nil { if err := r.compileHandler(node); err != nil {
return nil, node.handler, true return nil, node.handler, node.err, true
} }
bytecode = r.bytecodeCache.Get(nil, bytecodeKey) bytecode = r.bytecodeCache.Get(nil, bytecodeKey)
} }
// Add to route cache
cacheData := make([]byte, 8+len(node.handler)) cacheData := make([]byte, 8+len(node.handler))
copy(cacheData[:8], bytecodeKey) copy(cacheData[:8], bytecodeKey)
copy(cacheData[8:], node.handler) copy(cacheData[8:], node.handler)
r.routeCache.Set(routeCacheKey, cacheData) r.routeCache.Set(routeCacheKey, cacheData)
return bytecode, node.handler, true return bytecode, node.handler, node.err, true
} }
// nodeForHandler finds a node by its handler path // nodeForHandler finds a node by its handler path
@ -428,7 +402,6 @@ func findNodeByHandler(current *node, handlerPath string) *node {
} }
} }
// Check param child
if current.paramChild != nil { if current.paramChild != nil {
if node := findNodeByHandler(current.paramChild, handlerPath); node != nil { if node := findNodeByHandler(current.paramChild, handlerPath); node != nil {
return node return node
@ -443,20 +416,16 @@ func (r *LuaRouter) Refresh() error {
r.mu.Lock() r.mu.Lock()
defer r.mu.Unlock() defer r.mu.Unlock()
// Reset routes
for method := range r.routes { for method := range r.routes {
r.routes[method] = &node{ r.routes[method] = &node{
staticChild: make(map[string]*node), staticChild: make(map[string]*node),
} }
} }
// Clear failed routes
r.failedRoutes = make(map[string]*RouteError) r.failedRoutes = make(map[string]*RouteError)
// Rebuild routes
err := r.buildRoutes() err := r.buildRoutes()
// If some routes failed to compile, return a warning error
if len(r.failedRoutes) > 0 { if len(r.failedRoutes) > 0 {
return ErrRoutesCompilationErrors return ErrRoutesCompilationErrors
} }
@ -484,14 +453,14 @@ func (r *LuaRouter) ClearCache() {
} }
// GetCacheStats returns statistics about the cache // GetCacheStats returns statistics about the cache
func (r *LuaRouter) GetCacheStats() map[string]interface{} { func (r *LuaRouter) GetCacheStats() map[string]any {
var routeStats fastcache.Stats var routeStats fastcache.Stats
var bytecodeStats fastcache.Stats var bytecodeStats fastcache.Stats
r.routeCache.UpdateStats(&routeStats) r.routeCache.UpdateStats(&routeStats)
r.bytecodeCache.UpdateStats(&bytecodeStats) r.bytecodeCache.UpdateStats(&bytecodeStats)
return map[string]interface{}{ return map[string]any{
"routeEntries": routeStats.EntriesCount, "routeEntries": routeStats.EntriesCount,
"routeBytes": routeStats.BytesSize, "routeBytes": routeStats.BytesSize,
"routeCollisions": routeStats.Collisions, "routeCollisions": routeStats.Collisions,
@ -509,7 +478,6 @@ func (r *LuaRouter) GetRouteStats() (int, int64) {
routeCount := 0 routeCount := 0
bytecodeBytes := int64(0) bytecodeBytes := int64(0)
// Count routes and estimate bytecode size
for _, root := range r.routes { for _, root := range r.routes {
count, bytes := countNodesAndBytecode(root) count, bytes := countNodesAndBytecode(root)
routeCount += count routeCount += count
@ -525,21 +493,18 @@ func countNodesAndBytecode(n *node) (count int, bytecodeBytes int64) {
return 0, 0 return 0, 0
} }
// Count this node if it has a handler
if n.handler != "" { if n.handler != "" {
count = 1 count = 1
// Estimate bytecode size (average of 2KB per script) // Average of 2KB per script
bytecodeBytes = 2048 bytecodeBytes = 2048
} }
// Count static children
for _, child := range n.staticChild { for _, child := range n.staticChild {
childCount, childBytes := countNodesAndBytecode(child) childCount, childBytes := countNodesAndBytecode(child)
count += childCount count += childCount
bytecodeBytes += childBytes bytecodeBytes += childBytes
} }
// Count parameter child
if n.paramChild != nil { if n.paramChild != nil {
childCount, childBytes := countNodesAndBytecode(n.paramChild) childCount, childBytes := countNodesAndBytecode(n.paramChild)
count += childCount count += childCount
@ -553,35 +518,3 @@ type NodeWithError struct {
ScriptPath string ScriptPath string
Error error Error error
} }
// GetNodeWithError returns the node with its error for a given path
func (r *LuaRouter) GetNodeWithError(method, path string, params *Params) (*NodeWithError, bool) {
// Try route cache first
routeCacheKey := getCacheKey(method, path)
routeCacheData := r.routeCache.Get(nil, routeCacheKey)
if len(routeCacheData) > 0 {
// Cache hit - get handler path
handlerPath := string(routeCacheData[8:])
// Find the node for this handler
node, found := r.nodeForHandler(handlerPath)
if found {
return &NodeWithError{
ScriptPath: node.handler,
Error: node.err,
}, true
}
}
// Cache miss - do normal routing
node, found := r.Match(method, path, params)
if !found {
return nil, false
}
return &NodeWithError{
ScriptPath: node.handler,
Error: node.err,
}, true
}

View File

@ -26,7 +26,6 @@ type StaticRouter struct {
// NewStaticRouter creates a new StaticRouter instance // NewStaticRouter creates a new StaticRouter instance
func NewStaticRouter(rootDir string) (*StaticRouter, error) { func NewStaticRouter(rootDir string) (*StaticRouter, error) {
// Verify root directory exists
info, err := os.Stat(rootDir) info, err := os.Stat(rootDir)
if err != nil { if err != nil {
return nil, err return nil, err
@ -49,7 +48,7 @@ func NewStaticRouter(rootDir string) (*StaticRouter, error) {
r := &StaticRouter{ r := &StaticRouter{
fs: fs, fs: fs,
urlPrefix: "/", urlPrefix: "",
rootDir: rootDir, rootDir: rootDir,
log: false, log: false,
useBrotli: true, useBrotli: true,

View File

@ -56,12 +56,12 @@ func New() *Config {
config.Runner.PoolSize = runtime.GOMAXPROCS(0) config.Runner.PoolSize = runtime.GOMAXPROCS(0)
// Dirs defaults // Dirs defaults
config.Dirs.Routes = "./routes" config.Dirs.Routes = "routes"
config.Dirs.Static = "./static" config.Dirs.Static = "public"
config.Dirs.FS = "./fs" config.Dirs.FS = "fs"
config.Dirs.Data = "./data" config.Dirs.Data = "data"
config.Dirs.Override = "./override" config.Dirs.Override = "override"
config.Dirs.Libs = []string{"./libs"} config.Dirs.Libs = []string{"libs"}
return config return config
} }

12
go.mod
View File

@ -3,22 +3,22 @@ module Moonshark
go 1.24.1 go 1.24.1
require ( require (
git.sharkk.net/Sky/LuaJIT-to-Go v0.0.0 git.sharkk.net/Sky/LuaJIT-to-Go v0.1.2
github.com/VictoriaMetrics/fastcache v1.12.2 github.com/VictoriaMetrics/fastcache v1.12.2
github.com/deneonet/benc v1.1.7 github.com/deneonet/benc v1.1.7
github.com/goccy/go-json v0.10.5 github.com/goccy/go-json v0.10.5
github.com/matoous/go-nanoid/v2 v2.1.0 github.com/matoous/go-nanoid/v2 v2.1.0
github.com/valyala/bytebufferpool v1.0.0 github.com/valyala/bytebufferpool v1.0.0
github.com/valyala/fasthttp v1.60.0 github.com/valyala/fasthttp v1.61.0
) )
require ( require (
github.com/andybalholm/brotli v1.1.1 // indirect github.com/andybalholm/brotli v1.1.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/golang/snappy v0.0.4 // indirect github.com/golang/snappy v1.0.0 // indirect
github.com/klauspost/compress v1.18.0 // indirect github.com/klauspost/compress v1.18.0 // indirect
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect
golang.org/x/sys v0.31.0 // indirect golang.org/x/sys v0.32.0 // indirect
) )
replace git.sharkk.net/Sky/LuaJIT-to-Go => ./luajit replace git.sharkk.net/Sky/LuaJIT-to-Go => ./luajit

18
go.sum
View File

@ -4,8 +4,9 @@ github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA= github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -13,8 +14,9 @@ github.com/deneonet/benc v1.1.7 h1:0XPxTTVJZq/ulxXvMn2Mzjx5XquekVky3wX6eTgA0vA=
github.com/deneonet/benc v1.1.7/go.mod h1:UCfkM5Od0B2huwv/ZItvtUb7QnALFt9YXtX8NXX4Lts= github.com/deneonet/benc v1.1.7/go.mod h1:UCfkM5Od0B2huwv/ZItvtUb7QnALFt9YXtX8NXX4Lts=
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/matoous/go-nanoid/v2 v2.1.0 h1:P64+dmq21hhWdtvZfEAofnvJULaRR1Yib0+PnU669bE= github.com/matoous/go-nanoid/v2 v2.1.0 h1:P64+dmq21hhWdtvZfEAofnvJULaRR1Yib0+PnU669bE=
@ -27,14 +29,14 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.60.0 h1:kBRYS0lOhVJ6V+bYN8PqAHELKHtXqwq9zNMLKx1MBsw= github.com/valyala/fasthttp v1.61.0 h1:VV08V0AfoRaFurP1EWKvQQdPTZHiUzaVoulX1aBDgzU=
github.com/valyala/fasthttp v1.60.0/go.mod h1:iY4kDgV3Gc6EqhRZ8icqcmlG6bqhcDXfuHgTO4FXCvc= github.com/valyala/fasthttp v1.61.0/go.mod h1:wRIV/4cMwUPWnRcDno9hGnYZGh78QzODFfo1LTUhBog=
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ= golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM=
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8=
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

2
luajit

@ -1 +1 @@
Subproject commit a2b4b1c9272f849d9c1c913366f822e0be904ba2 Subproject commit 6b9e2a0e201bdd34fba0972441434354aa5c67c5