string fixes, static server improvements
This commit is contained in:
parent
4d0d5b6757
commit
da5281ba0a
@ -1,7 +1,5 @@
|
|||||||
package metadata
|
package metadata
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Version = "1.0.0"
|
Version = "1.0.0"
|
||||||
CommitHash = "placeholder"
|
|
||||||
BuildDate = "25/07/2025"
|
|
||||||
)
|
)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package http
|
package http
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"Moonshark/metadata"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
@ -49,6 +50,7 @@ func http_create_server(s *luajit.State) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
globalServer = &fasthttp.Server{
|
globalServer = &fasthttp.Server{
|
||||||
|
Name: "Moonshark/" + metadata.Version,
|
||||||
Handler: handleRequest,
|
Handler: handleRequest,
|
||||||
ReadTimeout: 30 * time.Second,
|
ReadTimeout: 30 * time.Second,
|
||||||
WriteTimeout: 30 * time.Second,
|
WriteTimeout: 30 * time.Second,
|
||||||
@ -174,6 +176,7 @@ func http_register_static(s *luajit.State) int {
|
|||||||
|
|
||||||
urlPrefix := s.ToString(1)
|
urlPrefix := s.ToString(1)
|
||||||
rootPath := s.ToString(2)
|
rootPath := s.ToString(2)
|
||||||
|
noCache := s.ToBoolean(3)
|
||||||
|
|
||||||
// Ensure prefix starts with /
|
// Ensure prefix starts with /
|
||||||
if !strings.HasPrefix(urlPrefix, "/") {
|
if !strings.HasPrefix(urlPrefix, "/") {
|
||||||
@ -188,7 +191,7 @@ func http_register_static(s *luajit.State) int {
|
|||||||
return 2
|
return 2
|
||||||
}
|
}
|
||||||
|
|
||||||
RegisterStaticHandler(urlPrefix, absPath)
|
RegisterStaticHandler(urlPrefix, absPath, noCache)
|
||||||
s.PushBoolean(true)
|
s.PushBoolean(true)
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
@ -293,16 +296,36 @@ func isLikelyStaticFile(path string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RegisterStaticHandler adds a static file handler
|
// RegisterStaticHandler adds a static file handler
|
||||||
func RegisterStaticHandler(urlPrefix, rootPath string) {
|
func RegisterStaticHandler(urlPrefix, rootPath string, noCache bool) {
|
||||||
staticMu.Lock()
|
staticMu.Lock()
|
||||||
defer staticMu.Unlock()
|
defer staticMu.Unlock()
|
||||||
|
|
||||||
|
var cacheDuration time.Duration
|
||||||
|
var compress bool
|
||||||
|
if noCache {
|
||||||
|
cacheDuration = 0
|
||||||
|
compress = false
|
||||||
|
} else {
|
||||||
|
cacheDuration = 3600 * time.Second
|
||||||
|
compress = true
|
||||||
|
}
|
||||||
|
|
||||||
fs := &fasthttp.FS{
|
fs := &fasthttp.FS{
|
||||||
Root: rootPath,
|
Root: rootPath,
|
||||||
|
CompressRoot: rootPath + "/.cache",
|
||||||
IndexNames: []string{"index.html"},
|
IndexNames: []string{"index.html"},
|
||||||
GenerateIndexPages: false,
|
GenerateIndexPages: false,
|
||||||
Compress: true,
|
Compress: compress,
|
||||||
|
CompressBrotli: compress,
|
||||||
|
CompressZstd: compress,
|
||||||
|
CacheDuration: cacheDuration,
|
||||||
AcceptByteRange: true,
|
AcceptByteRange: true,
|
||||||
|
PathNotFound: func(ctx *fasthttp.RequestCtx) {
|
||||||
|
path := ctx.Path()
|
||||||
|
fmt.Printf("404 not found: %s\n", path)
|
||||||
|
ctx.SetStatusCode(fasthttp.StatusNotFound)
|
||||||
|
ctx.SetBodyString("404 not found")
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
staticHandlers[urlPrefix] = fs
|
staticHandlers[urlPrefix] = fs
|
||||||
|
@ -920,14 +920,16 @@ function Server:close()
|
|||||||
return _G.__IS_WORKER or moonshark.http_close_server()
|
return _G.__IS_WORKER or moonshark.http_close_server()
|
||||||
end
|
end
|
||||||
|
|
||||||
function Server:static(root_path, url_prefix)
|
function Server:static(root_path, url_prefix, no_cache)
|
||||||
|
if not no_cache or no_cache ~= true then no_cache = false end
|
||||||
|
|
||||||
url_prefix = url_prefix or "/"
|
url_prefix = url_prefix or "/"
|
||||||
if not string.starts_with(url_prefix, "/") then
|
if not string.starts_with(url_prefix, "/") then
|
||||||
url_prefix = "/" .. url_prefix
|
url_prefix = "/" .. url_prefix
|
||||||
end
|
end
|
||||||
|
|
||||||
if not _G.__IS_WORKER then
|
if not _G.__IS_WORKER then
|
||||||
local success, err = moonshark.http_register_static(url_prefix, root_path)
|
local success, err = moonshark.http_register_static(url_prefix, root_path, no_cache)
|
||||||
if not success then
|
if not success then
|
||||||
error("Failed to register static handler: " .. (err or "unknown error"))
|
error("Failed to register static handler: " .. (err or "unknown error"))
|
||||||
end
|
end
|
||||||
|
@ -670,3 +670,398 @@ function string.random(length, charset)
|
|||||||
end
|
end
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Template processing with code execution
|
||||||
|
function string.render(template_str, env)
|
||||||
|
local function is_control_structure(code)
|
||||||
|
-- Check if code is a control structure that doesn't produce output
|
||||||
|
local trimmed = code:match("^%s*(.-)%s*$")
|
||||||
|
return trimmed == "else" or
|
||||||
|
trimmed == "end" or
|
||||||
|
trimmed:match("^if%s") or
|
||||||
|
trimmed:match("^elseif%s") or
|
||||||
|
trimmed:match("^for%s") or
|
||||||
|
trimmed:match("^while%s") or
|
||||||
|
trimmed:match("^repeat%s*$") or
|
||||||
|
trimmed:match("^until%s") or
|
||||||
|
trimmed:match("^do%s*$") or
|
||||||
|
trimmed:match("^local%s") or
|
||||||
|
trimmed:match("^function%s") or
|
||||||
|
trimmed:match(".*=%s*function%s*%(") or
|
||||||
|
trimmed:match(".*then%s*$") or
|
||||||
|
trimmed:match(".*do%s*$")
|
||||||
|
end
|
||||||
|
|
||||||
|
local pos, chunks = 1, {}
|
||||||
|
while pos <= #template_str do
|
||||||
|
local unescaped_start = template_str:find("{{{", pos, true)
|
||||||
|
local escaped_start = template_str:find("{{", pos, true)
|
||||||
|
|
||||||
|
local start, tag_type, open_len
|
||||||
|
if unescaped_start and (not escaped_start or unescaped_start <= escaped_start) then
|
||||||
|
start, tag_type, open_len = unescaped_start, "-", 3
|
||||||
|
elseif escaped_start then
|
||||||
|
start, tag_type, open_len = escaped_start, "=", 2
|
||||||
|
else
|
||||||
|
table.insert(chunks, template_str:sub(pos))
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
if start > pos then
|
||||||
|
table.insert(chunks, template_str:sub(pos, start-1))
|
||||||
|
end
|
||||||
|
|
||||||
|
pos = start + open_len
|
||||||
|
local close_tag = tag_type == "-" and "}}}" or "}}"
|
||||||
|
local close_start, close_stop = template_str:find(close_tag, pos, true)
|
||||||
|
if not close_start then
|
||||||
|
error("Failed to find closing tag at position " .. pos)
|
||||||
|
end
|
||||||
|
|
||||||
|
local code = template_str:sub(pos, close_start-1):match("^%s*(.-)%s*$")
|
||||||
|
local is_control = is_control_structure(code)
|
||||||
|
|
||||||
|
table.insert(chunks, {tag_type, code, pos, is_control})
|
||||||
|
pos = close_stop + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
local buffer = {"local _tostring, _escape, _b, _b_i = ...\n"}
|
||||||
|
for _, chunk in ipairs(chunks) do
|
||||||
|
local t = type(chunk)
|
||||||
|
if t == "string" then
|
||||||
|
table.insert(buffer, "_b_i = _b_i + 1\n")
|
||||||
|
table.insert(buffer, "_b[_b_i] = " .. string.format("%q", chunk) .. "\n")
|
||||||
|
else
|
||||||
|
local tag_type, code, pos, is_control = chunk[1], chunk[2], chunk[3], chunk[4]
|
||||||
|
|
||||||
|
if is_control then
|
||||||
|
-- Control structure - just insert as raw Lua code
|
||||||
|
table.insert(buffer, "--[[" .. pos .. "]] " .. code .. "\n")
|
||||||
|
elseif tag_type == "=" then
|
||||||
|
-- Simple variable check
|
||||||
|
if code:match("^[%w_]+$") then
|
||||||
|
table.insert(buffer, "_b_i = _b_i + 1\n")
|
||||||
|
table.insert(buffer, "--[[" .. pos .. "]] _b[_b_i] = _escape(_tostring(" .. code .. "))\n")
|
||||||
|
else
|
||||||
|
-- Expression output with escaping
|
||||||
|
table.insert(buffer, "_b_i = _b_i + 1\n")
|
||||||
|
table.insert(buffer, "--[[" .. pos .. "]] _b[_b_i] = _escape(_tostring(" .. code .. "))\n")
|
||||||
|
end
|
||||||
|
elseif tag_type == "-" then
|
||||||
|
-- Unescaped output
|
||||||
|
table.insert(buffer, "_b_i = _b_i + 1\n")
|
||||||
|
table.insert(buffer, "--[[" .. pos .. "]] _b[_b_i] = _tostring(" .. code .. ")\n")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
table.insert(buffer, "return _b")
|
||||||
|
|
||||||
|
local generated_code = table.concat(buffer)
|
||||||
|
|
||||||
|
-- DEBUG: Uncomment to see generated code
|
||||||
|
-- print("Generated Lua code:")
|
||||||
|
-- print(generated_code)
|
||||||
|
-- print("---")
|
||||||
|
|
||||||
|
local fn, err = loadstring(generated_code)
|
||||||
|
if not fn then
|
||||||
|
print("Generated code that failed to compile:")
|
||||||
|
print(generated_code)
|
||||||
|
error(err)
|
||||||
|
end
|
||||||
|
|
||||||
|
env = env or {}
|
||||||
|
local runtime_env = setmetatable({}, {__index = function(_, k) return env[k] or _G[k] end})
|
||||||
|
setfenv(fn, runtime_env)
|
||||||
|
|
||||||
|
local output_buffer = {}
|
||||||
|
fn(tostring, html_special_chars, output_buffer, 0)
|
||||||
|
return table.concat(output_buffer)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Named placeholder processing
|
||||||
|
function string.parse(template_str, env)
|
||||||
|
local pos, output = 1, {}
|
||||||
|
env = env or {}
|
||||||
|
|
||||||
|
while pos <= #template_str do
|
||||||
|
local unescaped_start, unescaped_end, unescaped_name = template_str:find("{{{%s*([%w_]+)%s*}}}", pos)
|
||||||
|
local escaped_start, escaped_end, escaped_name = template_str:find("{{%s*([%w_]+)%s*}}", pos)
|
||||||
|
|
||||||
|
local next_pos, placeholder_end, name, escaped
|
||||||
|
if unescaped_start and (not escaped_start or unescaped_start <= escaped_start) then
|
||||||
|
next_pos, placeholder_end, name, escaped = unescaped_start, unescaped_end, unescaped_name, false
|
||||||
|
elseif escaped_start then
|
||||||
|
next_pos, placeholder_end, name, escaped = escaped_start, escaped_end, escaped_name, true
|
||||||
|
else
|
||||||
|
local text = template_str:sub(pos)
|
||||||
|
if text and #text > 0 then
|
||||||
|
table.insert(output, text)
|
||||||
|
end
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
local text = template_str:sub(pos, next_pos - 1)
|
||||||
|
if text and #text > 0 then
|
||||||
|
table.insert(output, text)
|
||||||
|
end
|
||||||
|
|
||||||
|
local value = env[name]
|
||||||
|
local str = tostring(value or "")
|
||||||
|
if escaped then
|
||||||
|
str:html_special_chars()
|
||||||
|
end
|
||||||
|
table.insert(output, str)
|
||||||
|
|
||||||
|
pos = placeholder_end + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
return table.concat(output)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Indexed placeholder processing
|
||||||
|
function string.iparse(template_str, values)
|
||||||
|
local pos, output, value_index = 1, {}, 1
|
||||||
|
values = values or {}
|
||||||
|
|
||||||
|
while pos <= #template_str do
|
||||||
|
local unescaped_start, unescaped_end = template_str:find("{{{}}}", pos, true)
|
||||||
|
local escaped_start, escaped_end = template_str:find("{{}}", pos, true)
|
||||||
|
|
||||||
|
local next_pos, placeholder_end, escaped
|
||||||
|
if unescaped_start and (not escaped_start or unescaped_start <= escaped_start) then
|
||||||
|
next_pos, placeholder_end, escaped = unescaped_start, unescaped_end, false
|
||||||
|
elseif escaped_start then
|
||||||
|
next_pos, placeholder_end, escaped = escaped_start, escaped_end, true
|
||||||
|
else
|
||||||
|
local text = template_str:sub(pos)
|
||||||
|
if text and #text > 0 then
|
||||||
|
table.insert(output, text)
|
||||||
|
end
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
local text = template_str:sub(pos, next_pos - 1)
|
||||||
|
if text and #text > 0 then
|
||||||
|
table.insert(output, text)
|
||||||
|
end
|
||||||
|
|
||||||
|
local value = values[value_index]
|
||||||
|
local str = tostring(value or "")
|
||||||
|
if escaped then
|
||||||
|
str:html_special_chars()
|
||||||
|
end
|
||||||
|
table.insert(output, str)
|
||||||
|
|
||||||
|
pos = placeholder_end + 1
|
||||||
|
value_index = value_index + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
return table.concat(output)
|
||||||
|
end
|
||||||
|
|
||||||
|
string.special_chars_pattern = '[&<>"\']'
|
||||||
|
string.entity_decode_pattern = '&[#%w]+;'
|
||||||
|
|
||||||
|
string.special_encode_map = {
|
||||||
|
['&'] = '&',
|
||||||
|
['<'] = '<',
|
||||||
|
['>'] = '>',
|
||||||
|
['"'] = '"',
|
||||||
|
["'"] = '''
|
||||||
|
}
|
||||||
|
|
||||||
|
string.special_decode_map = {
|
||||||
|
['&'] = '&',
|
||||||
|
['<'] = '<',
|
||||||
|
['>'] = '>',
|
||||||
|
['"'] = '"',
|
||||||
|
['''] = "'",
|
||||||
|
['''] = "'"
|
||||||
|
}
|
||||||
|
|
||||||
|
string.extended_encode_map = {
|
||||||
|
-- Special chars
|
||||||
|
['&'] = '&',
|
||||||
|
['<'] = '<',
|
||||||
|
['>'] = '>',
|
||||||
|
['"'] = '"',
|
||||||
|
["'"] = ''',
|
||||||
|
-- Extended characters
|
||||||
|
[' '] = ' ',
|
||||||
|
['¡'] = '¡',
|
||||||
|
['¢'] = '¢',
|
||||||
|
['£'] = '£',
|
||||||
|
['¤'] = '¤',
|
||||||
|
['¥'] = '¥',
|
||||||
|
['¦'] = '¦',
|
||||||
|
['§'] = '§',
|
||||||
|
['¨'] = '¨',
|
||||||
|
['©'] = '©',
|
||||||
|
['ª'] = 'ª',
|
||||||
|
['«'] = '«',
|
||||||
|
['¬'] = '¬',
|
||||||
|
['®'] = '®',
|
||||||
|
['¯'] = '¯',
|
||||||
|
['°'] = '°',
|
||||||
|
['±'] = '±',
|
||||||
|
['²'] = '²',
|
||||||
|
['³'] = '³',
|
||||||
|
['´'] = '´',
|
||||||
|
['µ'] = 'µ',
|
||||||
|
['¶'] = '¶',
|
||||||
|
['·'] = '·',
|
||||||
|
['¸'] = '¸',
|
||||||
|
['¹'] = '¹',
|
||||||
|
['º'] = 'º',
|
||||||
|
['»'] = '»',
|
||||||
|
['¼'] = '¼',
|
||||||
|
['½'] = '½',
|
||||||
|
['¾'] = '¾',
|
||||||
|
['¿'] = '¿',
|
||||||
|
['À'] = 'À',
|
||||||
|
['Á'] = 'Á',
|
||||||
|
['Â'] = 'Â',
|
||||||
|
['Ã'] = 'Ã',
|
||||||
|
['Ä'] = 'Ä',
|
||||||
|
['Å'] = 'Å',
|
||||||
|
['Æ'] = 'Æ',
|
||||||
|
['Ç'] = 'Ç',
|
||||||
|
['È'] = 'È',
|
||||||
|
['É'] = 'É',
|
||||||
|
['Ê'] = 'Ê',
|
||||||
|
['Ë'] = 'Ë',
|
||||||
|
['Ì'] = 'Ì',
|
||||||
|
['Í'] = 'Í',
|
||||||
|
['Î'] = 'Î',
|
||||||
|
['Ï'] = 'Ï',
|
||||||
|
['Ð'] = 'Ð',
|
||||||
|
['Ñ'] = 'Ñ',
|
||||||
|
['Ò'] = 'Ò',
|
||||||
|
['Ó'] = 'Ó',
|
||||||
|
['Ô'] = 'Ô',
|
||||||
|
['Õ'] = 'Õ',
|
||||||
|
['Ö'] = 'Ö',
|
||||||
|
['×'] = '×',
|
||||||
|
['Ø'] = 'Ø',
|
||||||
|
['Ù'] = 'Ù',
|
||||||
|
['Ú'] = 'Ú',
|
||||||
|
['Û'] = 'Û',
|
||||||
|
['Ü'] = 'Ü',
|
||||||
|
['Ý'] = 'Ý',
|
||||||
|
['Þ'] = 'Þ',
|
||||||
|
['ß'] = 'ß',
|
||||||
|
['à'] = 'à',
|
||||||
|
['á'] = 'á',
|
||||||
|
['â'] = 'â',
|
||||||
|
['ã'] = 'ã',
|
||||||
|
['ä'] = 'ä',
|
||||||
|
['å'] = 'å',
|
||||||
|
['æ'] = 'æ',
|
||||||
|
['ç'] = 'ç',
|
||||||
|
['è'] = 'è',
|
||||||
|
['é'] = 'é',
|
||||||
|
['ê'] = 'ê',
|
||||||
|
['ë'] = 'ë',
|
||||||
|
['ì'] = 'ì',
|
||||||
|
['í'] = 'í',
|
||||||
|
['î'] = 'î',
|
||||||
|
['ï'] = 'ï',
|
||||||
|
['ð'] = 'ð',
|
||||||
|
['ñ'] = 'ñ',
|
||||||
|
['ò'] = 'ò',
|
||||||
|
['ó'] = 'ó',
|
||||||
|
['ô'] = 'ô',
|
||||||
|
['õ'] = 'õ',
|
||||||
|
['ö'] = 'ö',
|
||||||
|
['÷'] = '÷',
|
||||||
|
['ø'] = 'ø',
|
||||||
|
['ù'] = 'ù',
|
||||||
|
['ú'] = 'ú',
|
||||||
|
['û'] = 'û',
|
||||||
|
['ü'] = 'ü',
|
||||||
|
['ý'] = 'ý',
|
||||||
|
['þ'] = 'þ',
|
||||||
|
['ÿ'] = 'ÿ'
|
||||||
|
}
|
||||||
|
|
||||||
|
string.extended_decode_map = {}
|
||||||
|
for char, entity in pairs(string.extended_encode_map) do
|
||||||
|
string.extended_decode_map[entity] = char
|
||||||
|
end
|
||||||
|
-- Add common named entities not in extended_encode_map
|
||||||
|
string.extended_decode_map['''] = "'"
|
||||||
|
string.extended_decode_map[' '] = ' '
|
||||||
|
|
||||||
|
-- Converts HTML special characters (&, <, >, ", ') to entities
|
||||||
|
function string.html_special_chars(str)
|
||||||
|
if not str then return nil end
|
||||||
|
if not str:find(string.special_chars_pattern) then return str end
|
||||||
|
return (str:gsub(string.special_chars_pattern, string.special_encode_map))
|
||||||
|
end
|
||||||
|
getmetatable("").__index.html_special_chars = string.html_special_chars
|
||||||
|
|
||||||
|
-- Decodes HTML special character entities back to characters
|
||||||
|
function string.html_special_chars_decode(str)
|
||||||
|
if not str then return nil end
|
||||||
|
|
||||||
|
return (str:gsub('&[lg]t;', string.special_decode_map)
|
||||||
|
:gsub('"', '"')
|
||||||
|
:gsub(''', "'")
|
||||||
|
:gsub(''', "'")
|
||||||
|
:gsub('&', '&')) -- Must be last to avoid double-decoding
|
||||||
|
end
|
||||||
|
getmetatable("").__index.html_special_chars_decode = string.html_special_chars_decode
|
||||||
|
|
||||||
|
-- More comprehensive HTML entity encoding
|
||||||
|
-- Handles special chars plus extended Latin-1 characters
|
||||||
|
function string.html_entities(str)
|
||||||
|
if not str then return nil end
|
||||||
|
|
||||||
|
local result = {}
|
||||||
|
local result_len = 0
|
||||||
|
|
||||||
|
for i = 1, #str do
|
||||||
|
local char = str:sub(i, i)
|
||||||
|
local entity = string.extended_encode_map[char]
|
||||||
|
|
||||||
|
if entity then
|
||||||
|
result_len = result_len + 1
|
||||||
|
result[result_len] = entity
|
||||||
|
else
|
||||||
|
local byte = string.byte(char)
|
||||||
|
if byte > 127 then
|
||||||
|
-- Encode high-bit characters as numeric entities
|
||||||
|
result_len = result_len + 1
|
||||||
|
result[result_len] = '&#' .. byte .. ';'
|
||||||
|
else
|
||||||
|
result_len = result_len + 1
|
||||||
|
result[result_len] = char
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return table.concat(result)
|
||||||
|
end
|
||||||
|
getmetatable("").__index.html_entities = string.html_entities
|
||||||
|
|
||||||
|
function string.html_entities_decode(str)
|
||||||
|
if not str then return nil end
|
||||||
|
|
||||||
|
-- Handle numeric entities first
|
||||||
|
local result = str:gsub('&#(%d+);', function(num)
|
||||||
|
local n = tonumber(num)
|
||||||
|
if n and n >= 0 and n <= 255 then
|
||||||
|
return string.char(n)
|
||||||
|
end
|
||||||
|
return '&#' .. num .. ';' -- Return unchanged if invalid
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Handle named entities
|
||||||
|
result = result:gsub(string.entity_decode_pattern, function(entity)
|
||||||
|
return string.extended_decode_map[entity] or entity
|
||||||
|
end)
|
||||||
|
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
getmetatable("").__index.html_entities_decode = string.html_entities_decode
|
||||||
|
@ -16,7 +16,7 @@ import (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
watchFlag = flag.Bool("watch", false, "Watch script files for changes and restart")
|
watchFlag = flag.Bool("watch", false, "Watch script files for changes and restart")
|
||||||
wFlag = flag.Bool("w", false, "Watch script files for changes and restart (short)")
|
wFlag = flag.Bool("w", false, "Watch script files for changes and restart")
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user