crumbs API
This commit is contained in:
257
flumi/Scripts/Utils/Lua/Crumbs.gd
Normal file
257
flumi/Scripts/Utils/Lua/Crumbs.gd
Normal file
@@ -0,0 +1,257 @@
|
|||||||
|
class_name LuaCrumbsUtils
|
||||||
|
extends RefCounted
|
||||||
|
|
||||||
|
const CRUMBS_DIR_PATH = "user://crumbs/"
|
||||||
|
|
||||||
|
class Crumb:
|
||||||
|
var name: String
|
||||||
|
var value: String
|
||||||
|
var created_at: float
|
||||||
|
var lifespan: float = -1.0 # -1 = no expiry, otherwise lifespan in seconds
|
||||||
|
|
||||||
|
func _init(n: String, v: String, lifetime: float = -1.0):
|
||||||
|
name = n
|
||||||
|
value = v
|
||||||
|
created_at = Time.get_unix_time_from_system()
|
||||||
|
lifespan = lifetime
|
||||||
|
|
||||||
|
func is_expired() -> bool:
|
||||||
|
if lifespan < 0:
|
||||||
|
return false
|
||||||
|
var current_time = Time.get_unix_time_from_system()
|
||||||
|
return current_time > (created_at + lifespan)
|
||||||
|
|
||||||
|
func get_expiry_time() -> float:
|
||||||
|
if lifespan < 0:
|
||||||
|
return -1.0
|
||||||
|
return created_at + lifespan
|
||||||
|
|
||||||
|
func to_dict() -> Dictionary:
|
||||||
|
return {
|
||||||
|
"name": name,
|
||||||
|
"value": value,
|
||||||
|
"created_at": created_at,
|
||||||
|
"lifespan": lifespan
|
||||||
|
}
|
||||||
|
|
||||||
|
static func from_dict(data: Dictionary) -> Crumb:
|
||||||
|
var crumb = Crumb.new(data.get("name", ""), data.get("value", ""))
|
||||||
|
crumb.created_at = data.get("created_at", Time.get_unix_time_from_system())
|
||||||
|
crumb.lifespan = data.get("lifespan", -1.0)
|
||||||
|
return crumb
|
||||||
|
|
||||||
|
static func setup_crumbs_api(vm: LuauVM):
|
||||||
|
# Ensure crumbs directory exists
|
||||||
|
if not DirAccess.dir_exists_absolute(CRUMBS_DIR_PATH):
|
||||||
|
DirAccess.make_dir_recursive_absolute(CRUMBS_DIR_PATH)
|
||||||
|
|
||||||
|
vm.lua_newtable()
|
||||||
|
|
||||||
|
vm.lua_pushcallable(_crumbs_set_handler, "gurt.crumbs.set")
|
||||||
|
vm.lua_setfield(-2, "set")
|
||||||
|
|
||||||
|
vm.lua_pushcallable(_crumbs_get_handler, "gurt.crumbs.get")
|
||||||
|
vm.lua_setfield(-2, "get")
|
||||||
|
|
||||||
|
vm.lua_pushcallable(_crumbs_delete_handler, "gurt.crumbs.delete")
|
||||||
|
vm.lua_setfield(-2, "delete")
|
||||||
|
|
||||||
|
vm.lua_pushcallable(_crumbs_get_all_handler, "gurt.crumbs.getAll")
|
||||||
|
vm.lua_setfield(-2, "getAll")
|
||||||
|
|
||||||
|
vm.lua_getglobal("gurt")
|
||||||
|
if vm.lua_isnil(-1):
|
||||||
|
vm.lua_pop(1)
|
||||||
|
vm.lua_newtable()
|
||||||
|
vm.lua_setglobal("gurt")
|
||||||
|
vm.lua_getglobal("gurt")
|
||||||
|
|
||||||
|
vm.lua_pushvalue(-2)
|
||||||
|
vm.lua_setfield(-2, "crumbs")
|
||||||
|
vm.lua_pop(2)
|
||||||
|
|
||||||
|
static func get_current_domain() -> String:
|
||||||
|
var main_node = Engine.get_main_loop().current_scene
|
||||||
|
if main_node and main_node.has_method("get_current_url"):
|
||||||
|
var current_url = main_node.get_current_url()
|
||||||
|
return sanitize_domain_for_filename(current_url)
|
||||||
|
return "default"
|
||||||
|
|
||||||
|
static func sanitize_domain_for_filename(domain: String) -> String:
|
||||||
|
# Remove protocol prefix
|
||||||
|
if domain.begins_with("gurt://"):
|
||||||
|
domain = domain.substr(7)
|
||||||
|
elif domain.contains("://"):
|
||||||
|
var parts = domain.split("://")
|
||||||
|
if parts.size() > 1:
|
||||||
|
domain = parts[1]
|
||||||
|
|
||||||
|
# Extract only the domain part (remove path)
|
||||||
|
if domain.contains("/"):
|
||||||
|
domain = domain.split("/")[0]
|
||||||
|
|
||||||
|
# Replace invalid filename characters (mainly colons for ports)
|
||||||
|
domain = domain.replace(":", "_")
|
||||||
|
domain = domain.replace("\\", "_")
|
||||||
|
domain = domain.replace("*", "_")
|
||||||
|
domain = domain.replace("?", "_")
|
||||||
|
domain = domain.replace("\"", "_")
|
||||||
|
domain = domain.replace("<", "_")
|
||||||
|
domain = domain.replace(">", "_")
|
||||||
|
domain = domain.replace("|", "_")
|
||||||
|
|
||||||
|
# Ensure it's not empty
|
||||||
|
if domain.is_empty():
|
||||||
|
domain = "default"
|
||||||
|
|
||||||
|
return domain
|
||||||
|
|
||||||
|
static func get_domain_file_path(domain: String) -> String:
|
||||||
|
return CRUMBS_DIR_PATH + domain + ".json"
|
||||||
|
|
||||||
|
static func _crumbs_set_handler(vm: LuauVM) -> int:
|
||||||
|
vm.luaL_checktype(1, vm.LUA_TTABLE)
|
||||||
|
|
||||||
|
vm.lua_getfield(1, "name")
|
||||||
|
if vm.lua_isnil(-1):
|
||||||
|
vm.luaL_error("crumb 'name' field is required")
|
||||||
|
return 0
|
||||||
|
var name: String = vm.lua_tostring(-1)
|
||||||
|
vm.lua_pop(1)
|
||||||
|
|
||||||
|
vm.lua_getfield(1, "value")
|
||||||
|
if vm.lua_isnil(-1):
|
||||||
|
vm.luaL_error("crumb 'value' field is required")
|
||||||
|
return 0
|
||||||
|
var value: String = vm.lua_tostring(-1)
|
||||||
|
vm.lua_pop(1)
|
||||||
|
|
||||||
|
var lifetime: float = -1.0
|
||||||
|
vm.lua_getfield(1, "lifetime")
|
||||||
|
if not vm.lua_isnil(-1):
|
||||||
|
lifetime = vm.lua_tonumber(-1)
|
||||||
|
vm.lua_pop(1)
|
||||||
|
|
||||||
|
var domain = get_current_domain()
|
||||||
|
var crumb = Crumb.new(name, value, lifetime)
|
||||||
|
save_crumb(domain, crumb)
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
|
static func _crumbs_get_handler(vm: LuauVM) -> int:
|
||||||
|
var name: String = vm.luaL_checkstring(1)
|
||||||
|
var domain = get_current_domain()
|
||||||
|
var crumb = load_crumb(domain, name)
|
||||||
|
|
||||||
|
if crumb and not crumb.is_expired():
|
||||||
|
vm.lua_pushstring(crumb.value)
|
||||||
|
else:
|
||||||
|
vm.lua_pushnil()
|
||||||
|
|
||||||
|
return 1
|
||||||
|
|
||||||
|
static func _crumbs_delete_handler(vm: LuauVM) -> int:
|
||||||
|
var name: String = vm.luaL_checkstring(1)
|
||||||
|
var domain = get_current_domain()
|
||||||
|
var existed = delete_crumb(domain, name)
|
||||||
|
|
||||||
|
vm.lua_pushboolean(existed)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
static func _crumbs_get_all_handler(vm: LuauVM) -> int:
|
||||||
|
var domain = get_current_domain()
|
||||||
|
var all_crumbs = load_all_crumbs(domain)
|
||||||
|
|
||||||
|
vm.lua_newtable()
|
||||||
|
|
||||||
|
for crumb_name in all_crumbs:
|
||||||
|
var crumb = all_crumbs[crumb_name]
|
||||||
|
if not crumb.is_expired():
|
||||||
|
vm.lua_newtable()
|
||||||
|
vm.lua_pushstring(crumb.name)
|
||||||
|
vm.lua_setfield(-2, "name")
|
||||||
|
vm.lua_pushstring(crumb.value)
|
||||||
|
vm.lua_setfield(-2, "value")
|
||||||
|
|
||||||
|
# Include expiry time if it exists (but not created_at)
|
||||||
|
var expiry_time = crumb.get_expiry_time()
|
||||||
|
if expiry_time > 0:
|
||||||
|
vm.lua_pushnumber(expiry_time)
|
||||||
|
vm.lua_setfield(-2, "expiry")
|
||||||
|
|
||||||
|
vm.lua_setfield(-2, crumb_name)
|
||||||
|
|
||||||
|
return 1
|
||||||
|
|
||||||
|
static func load_all_crumbs(domain: String) -> Dictionary:
|
||||||
|
var file_path = get_domain_file_path(domain)
|
||||||
|
if not FileAccess.file_exists(file_path):
|
||||||
|
return {}
|
||||||
|
|
||||||
|
var file = FileAccess.open(file_path, FileAccess.READ)
|
||||||
|
if not file:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
var json_string = file.get_as_text()
|
||||||
|
file.close()
|
||||||
|
|
||||||
|
var json = JSON.new()
|
||||||
|
var parse_result = json.parse(json_string)
|
||||||
|
if parse_result != OK:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
var crumbs_data = json.data
|
||||||
|
if not crumbs_data is Dictionary:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
var crumbs = {}
|
||||||
|
var current_time = Time.get_ticks_msec() / 1000.0
|
||||||
|
var changed = false
|
||||||
|
|
||||||
|
for crumb_name in crumbs_data:
|
||||||
|
var crumb_dict = crumbs_data[crumb_name]
|
||||||
|
if crumb_dict is Dictionary:
|
||||||
|
var crumb = Crumb.from_dict(crumb_dict)
|
||||||
|
if crumb.is_expired():
|
||||||
|
changed = true
|
||||||
|
else:
|
||||||
|
crumbs[crumb_name] = crumb
|
||||||
|
|
||||||
|
# Save back if we removed expired crumbs
|
||||||
|
if changed:
|
||||||
|
save_all_crumbs(domain, crumbs)
|
||||||
|
|
||||||
|
return crumbs
|
||||||
|
|
||||||
|
static func save_all_crumbs(domain: String, crumbs: Dictionary):
|
||||||
|
var crumbs_data = {}
|
||||||
|
for crumb_name in crumbs:
|
||||||
|
var crumb = crumbs[crumb_name]
|
||||||
|
crumbs_data[crumb_name] = crumb.to_dict()
|
||||||
|
|
||||||
|
var file_path = get_domain_file_path(domain)
|
||||||
|
var file = FileAccess.open(file_path, FileAccess.WRITE)
|
||||||
|
if not file:
|
||||||
|
push_error("Failed to open crumbs file for writing: " + file_path)
|
||||||
|
return
|
||||||
|
|
||||||
|
var json_string = JSON.stringify(crumbs_data)
|
||||||
|
file.store_string(json_string)
|
||||||
|
file.close()
|
||||||
|
|
||||||
|
static func load_crumb(domain: String, name: String) -> Crumb:
|
||||||
|
var all_crumbs = load_all_crumbs(domain)
|
||||||
|
return all_crumbs.get(name, null)
|
||||||
|
|
||||||
|
static func save_crumb(domain: String, crumb: Crumb):
|
||||||
|
var all_crumbs = load_all_crumbs(domain)
|
||||||
|
all_crumbs[crumb.name] = crumb
|
||||||
|
save_all_crumbs(domain, all_crumbs)
|
||||||
|
|
||||||
|
static func delete_crumb(domain: String, name: String) -> bool:
|
||||||
|
var all_crumbs = load_all_crumbs(domain)
|
||||||
|
var existed = all_crumbs.has(name)
|
||||||
|
if existed:
|
||||||
|
all_crumbs.erase(name)
|
||||||
|
save_all_crumbs(domain, all_crumbs)
|
||||||
|
return existed
|
||||||
1
flumi/Scripts/Utils/Lua/Crumbs.gd.uid
Normal file
1
flumi/Scripts/Utils/Lua/Crumbs.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://bf63lfs770plv
|
||||||
@@ -120,9 +120,8 @@ static func handle_element_append(operation: Dictionary, dom_parser: HTMLParser,
|
|||||||
# Handle visual rendering if parent is already rendered
|
# Handle visual rendering if parent is already rendered
|
||||||
var parent_dom_node: Node = null
|
var parent_dom_node: Node = null
|
||||||
if parent_id == "body":
|
if parent_id == "body":
|
||||||
var main_scene = lua_api.get_node("/root/Main")
|
var main_scene = Engine.get_main_loop().current_scene
|
||||||
if main_scene:
|
parent_dom_node = main_scene.website_container
|
||||||
parent_dom_node = main_scene.website_container
|
|
||||||
else:
|
else:
|
||||||
parent_dom_node = dom_parser.parse_result.dom_nodes.get(parent_id, null)
|
parent_dom_node = dom_parser.parse_result.dom_nodes.get(parent_id, null)
|
||||||
|
|
||||||
@@ -184,7 +183,7 @@ static func handle_insert_before(operation: Dictionary, dom_parser: HTMLParser,
|
|||||||
# Handle visual rendering
|
# Handle visual rendering
|
||||||
var parent_dom_node: Node = null
|
var parent_dom_node: Node = null
|
||||||
if parent_id == "body":
|
if parent_id == "body":
|
||||||
var main_scene = lua_api.get_node("/root/Main")
|
var main_scene = Engine.get_main_loop().current_scene
|
||||||
if main_scene:
|
if main_scene:
|
||||||
parent_dom_node = main_scene.website_container
|
parent_dom_node = main_scene.website_container
|
||||||
else:
|
else:
|
||||||
@@ -223,7 +222,7 @@ static func handle_insert_after(operation: Dictionary, dom_parser: HTMLParser, l
|
|||||||
# Handle visual rendering
|
# Handle visual rendering
|
||||||
var parent_dom_node: Node = null
|
var parent_dom_node: Node = null
|
||||||
if parent_id == "body":
|
if parent_id == "body":
|
||||||
var main_scene = lua_api.get_node("/root/Main")
|
var main_scene = Engine.get_main_loop().current_scene
|
||||||
if main_scene:
|
if main_scene:
|
||||||
parent_dom_node = main_scene.website_container
|
parent_dom_node = main_scene.website_container
|
||||||
else:
|
else:
|
||||||
@@ -265,7 +264,7 @@ static func handle_replace_child(operation: Dictionary, dom_parser: HTMLParser,
|
|||||||
|
|
||||||
static func render_new_element(element: HTMLParser.HTMLElement, parent_node: Node, dom_parser: HTMLParser, lua_api) -> void:
|
static func render_new_element(element: HTMLParser.HTMLElement, parent_node: Node, dom_parser: HTMLParser, lua_api) -> void:
|
||||||
# Get reference to main scene for rendering
|
# Get reference to main scene for rendering
|
||||||
var main_scene = lua_api.get_node("/root/Main")
|
var main_scene = Engine.get_main_loop().current_scene
|
||||||
if not main_scene:
|
if not main_scene:
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -318,7 +317,7 @@ static func clone_element(element: HTMLParser.HTMLElement, deep: bool) -> HTMLPa
|
|||||||
static func handle_visual_insertion_by_reference(parent_element_id: String, new_child_element: HTMLParser.HTMLElement, reference_element_id: String, insert_before: bool, dom_parser: HTMLParser, lua_api) -> void:
|
static func handle_visual_insertion_by_reference(parent_element_id: String, new_child_element: HTMLParser.HTMLElement, reference_element_id: String, insert_before: bool, dom_parser: HTMLParser, lua_api) -> void:
|
||||||
var parent_dom_node: Node = null
|
var parent_dom_node: Node = null
|
||||||
if parent_element_id == "body":
|
if parent_element_id == "body":
|
||||||
var main_scene = lua_api.get_node("/root/Main")
|
var main_scene = Engine.get_main_loop().current_scene
|
||||||
if main_scene:
|
if main_scene:
|
||||||
parent_dom_node = main_scene.website_container
|
parent_dom_node = main_scene.website_container
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -344,6 +344,7 @@ func _setup_additional_lua_apis():
|
|||||||
LuaJSONUtils.setup_json_api(lua_vm)
|
LuaJSONUtils.setup_json_api(lua_vm)
|
||||||
LuaWebSocketUtils.setup_websocket_api(lua_vm)
|
LuaWebSocketUtils.setup_websocket_api(lua_vm)
|
||||||
LuaAudioUtils.setup_audio_api(lua_vm)
|
LuaAudioUtils.setup_audio_api(lua_vm)
|
||||||
|
LuaCrumbsUtils.setup_crumbs_api(lua_vm)
|
||||||
|
|
||||||
func _table_tostring_handler(vm: LuauVM) -> int:
|
func _table_tostring_handler(vm: LuauVM) -> int:
|
||||||
vm.luaL_checktype(1, vm.LUA_TTABLE)
|
vm.luaL_checktype(1, vm.LUA_TTABLE)
|
||||||
|
|||||||
@@ -135,6 +135,16 @@ func render() -> void:
|
|||||||
|
|
||||||
func render_content(html_bytes: PackedByteArray) -> void:
|
func render_content(html_bytes: PackedByteArray) -> void:
|
||||||
|
|
||||||
|
var existing_lua_apis = []
|
||||||
|
for child in get_children():
|
||||||
|
if child is LuaAPI:
|
||||||
|
existing_lua_apis.append(child)
|
||||||
|
|
||||||
|
for lua_api in existing_lua_apis:
|
||||||
|
lua_api.kill_script_execution()
|
||||||
|
remove_child(lua_api)
|
||||||
|
lua_api.queue_free()
|
||||||
|
|
||||||
# Clear existing content
|
# Clear existing content
|
||||||
for child in website_container.get_children():
|
for child in website_container.get_children():
|
||||||
child.queue_free()
|
child.queue_free()
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ config_version=5
|
|||||||
config/name="Flumi"
|
config/name="Flumi"
|
||||||
config/version="1.0.0"
|
config/version="1.0.0"
|
||||||
run/main_scene="uid://bytm7bt2s4ak8"
|
run/main_scene="uid://bytm7bt2s4ak8"
|
||||||
|
config/use_custom_user_dir=true
|
||||||
config/features=PackedStringArray("4.4", "Forward Plus")
|
config/features=PackedStringArray("4.4", "Forward Plus")
|
||||||
boot_splash/show_image=false
|
boot_splash/show_image=false
|
||||||
config/icon="uid://ctpe0lbehepen"
|
config/icon="uid://ctpe0lbehepen"
|
||||||
|
|||||||
223
tests/crumbs.html
Normal file
223
tests/crumbs.html
Normal file
@@ -0,0 +1,223 @@
|
|||||||
|
<head>
|
||||||
|
<title>Crumbs API Test</title>
|
||||||
|
<style>
|
||||||
|
.test-section { bg-[#f8fafc] p-4 rounded mb-4 border border-[#e2e8f0] }
|
||||||
|
.test-button { bg-[#3b82f6] text-white px-4 py-2 rounded mr-2 mb-2 cursor-pointer hover:bg-[#2563eb] }
|
||||||
|
.success { text-[#059669] font-bold }
|
||||||
|
.error { text-[#dc2626] font-bold }
|
||||||
|
.crumb-item { bg-[#e0e7ef] text-[#22223b] rounded p-2 mb-2 border-l-4 border-[#3b82f6] }
|
||||||
|
.expired { bg-[#fef2f2] border-[#dc2626] text-[#991b1b] }
|
||||||
|
#log { bg-[#1f2937] text-[#f9fafb] p-3 rounded text-sm font-mono max-h-[300px] overflow-y-auto }
|
||||||
|
</style>
|
||||||
|
<script>
|
||||||
|
local log = gurt.select("#log")
|
||||||
|
local crumbsList = gurt.select("#crumbs-list")
|
||||||
|
|
||||||
|
local function log_msg(msg, type)
|
||||||
|
type = type or "info"
|
||||||
|
local color = type == "success" and "#10b981" or (type == "error" and "#ef4444" or "#6b7280")
|
||||||
|
log.text = log.text .. "[" .. Time.format(Time.now(), '%H:%M:%S') .. "] " .. msg .. "\\n"
|
||||||
|
end
|
||||||
|
|
||||||
|
local function clear_log()
|
||||||
|
log.text = ""
|
||||||
|
end
|
||||||
|
|
||||||
|
local function refresh_crumbs_display()
|
||||||
|
local all_crumbs = gurt.crumbs.getAll()
|
||||||
|
crumbsList.text = ""
|
||||||
|
|
||||||
|
local count = 0
|
||||||
|
for name, crumb in pairs(all_crumbs) do
|
||||||
|
count = count + 1
|
||||||
|
local expiry_text = ""
|
||||||
|
if crumb.expiry then
|
||||||
|
local remaining = crumb.expiry - (Time.now() / 1000)
|
||||||
|
if remaining > 0 then
|
||||||
|
expiry_text = " (expires in " .. math.floor(remaining) .. "s)"
|
||||||
|
else
|
||||||
|
expiry_text = " (EXPIRED)"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
expiry_text = " (permanent)"
|
||||||
|
end
|
||||||
|
|
||||||
|
local newDiv = gurt.create("div", {
|
||||||
|
class = "crumb-item",
|
||||||
|
text = name .. " = " .. crumb.value .. expiry_text
|
||||||
|
})
|
||||||
|
crumbsList:append(newDiv)
|
||||||
|
end
|
||||||
|
|
||||||
|
if count == 0 then
|
||||||
|
local emptyDiv = gurt.create("div", {
|
||||||
|
class = "crumb-item",
|
||||||
|
text = "No crumbs stored"
|
||||||
|
})
|
||||||
|
crumbsList:append(emptyDiv)
|
||||||
|
end
|
||||||
|
|
||||||
|
log_msg("Refreshed display: " .. count .. " crumbs found")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Test: Set a permanent crumb
|
||||||
|
gurt.select("#btn-set-permanent"):on("click", function()
|
||||||
|
gurt.crumbs.set({
|
||||||
|
name = "username",
|
||||||
|
value = "gurted_user",
|
||||||
|
})
|
||||||
|
log_msg("Set permanent crumb: username = gurted_user", "success")
|
||||||
|
refresh_crumbs_display()
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Test: Set a temporary crumb (10 seconds)
|
||||||
|
gurt.select("#btn-set-temp"):on("click", function()
|
||||||
|
gurt.crumbs.set({
|
||||||
|
name = "session_token",
|
||||||
|
value = "abc123def456",
|
||||||
|
lifetime = 10
|
||||||
|
})
|
||||||
|
log_msg("Set temporary crumb: session_token (expires in 10s)", "success")
|
||||||
|
refresh_crumbs_display()
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Test: Set a very short-lived crumb (3 seconds)
|
||||||
|
gurt.select("#btn-set-short"):on("click", function()
|
||||||
|
gurt.crumbs.set({
|
||||||
|
name = "temp_data",
|
||||||
|
value = "will_expire_soon",
|
||||||
|
lifetime = 3
|
||||||
|
})
|
||||||
|
log_msg("Set short-lived crumb: temp_data (expires in 3s)", "success")
|
||||||
|
refresh_crumbs_display()
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Test: Get a specific crumb
|
||||||
|
gurt.select("#btn-get-username"):on("click", function()
|
||||||
|
local value = gurt.crumbs.get("username")
|
||||||
|
if value then
|
||||||
|
log_msg("Retrieved username: " .. value, "success")
|
||||||
|
else
|
||||||
|
log_msg("Username crumb not found", "error")
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Test: Get non-existent crumb
|
||||||
|
gurt.select("#btn-get-nonexistent"):on("click", function()
|
||||||
|
local value = gurt.crumbs.get("nonexistent")
|
||||||
|
if value then
|
||||||
|
log_msg("Unexpected: found nonexistent crumb: " .. value, "error")
|
||||||
|
else
|
||||||
|
log_msg("Correctly returned nil for nonexistent crumb", "success")
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Test: Delete a crumb
|
||||||
|
gurt.select("#btn-delete-session"):on("click", function()
|
||||||
|
local existed = gurt.crumbs.delete("session_token")
|
||||||
|
if existed then
|
||||||
|
log_msg("Deleted session_token crumb", "success")
|
||||||
|
else
|
||||||
|
log_msg("session_token crumb was not found", "error")
|
||||||
|
end
|
||||||
|
refresh_crumbs_display()
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Test: Try to delete non-existent crumb
|
||||||
|
gurt.select("#btn-delete-nonexistent"):on("click", function()
|
||||||
|
local existed = gurt.crumbs.delete("nonexistent")
|
||||||
|
if existed then
|
||||||
|
log_msg("Unexpected: deleted nonexistent crumb", "error")
|
||||||
|
else
|
||||||
|
log_msg("Correctly returned false for nonexistent crumb deletion", "success")
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Test: Set multiple crumbs at once
|
||||||
|
gurt.select("#btn-set-multiple"):on("click", function()
|
||||||
|
gurt.crumbs.set({ name = "theme", value = "dark" })
|
||||||
|
gurt.crumbs.set({ name = "language", value = "en" })
|
||||||
|
gurt.crumbs.set({ name = "notifications", value = "enabled" })
|
||||||
|
log_msg("Set multiple crumbs: theme, language, notifications", "success")
|
||||||
|
refresh_crumbs_display()
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Test: Clear all crumbs
|
||||||
|
gurt.select("#btn-clear-all"):on("click", function()
|
||||||
|
local all_crumbs = gurt.crumbs.getAll()
|
||||||
|
local count = 0
|
||||||
|
for name, _ in pairs(all_crumbs) do
|
||||||
|
gurt.crumbs.delete(name)
|
||||||
|
count = count + 1
|
||||||
|
end
|
||||||
|
log_msg("Cleared " .. count .. " crumbs", "success")
|
||||||
|
refresh_crumbs_display()
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Test: Auto-refresh every 2 seconds to show expiry
|
||||||
|
gurt.setInterval(function()
|
||||||
|
refresh_crumbs_display()
|
||||||
|
end, 2000)
|
||||||
|
|
||||||
|
-- Clear log button
|
||||||
|
gurt.select("#btn-clear-log"):on("click", function()
|
||||||
|
clear_log()
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Initialize display
|
||||||
|
log_msg("Crumbs API Test initialized")
|
||||||
|
refresh_crumbs_display()
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Crumbs API Test Suite</h1>
|
||||||
|
|
||||||
|
<div class="test-section">
|
||||||
|
<h2>Set Crumbs</h2>
|
||||||
|
<p>Test setting crumbs with different lifetimes</p>
|
||||||
|
<button id="btn-set-permanent" class="test-button">Set Permanent Crumb</button>
|
||||||
|
<button id="btn-set-temp" class="test-button">Set 10s Temporary Crumb</button>
|
||||||
|
<button id="btn-set-short" class="test-button">Set 3s Short-lived Crumb</button>
|
||||||
|
<button id="btn-set-multiple" class="test-button">Set Multiple Crumbs</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="test-section">
|
||||||
|
<h2>Get Crumbs</h2>
|
||||||
|
<p>Test retrieving existing and non-existent crumbs</p>
|
||||||
|
<button id="btn-get-username" class="test-button">Get 'username' Crumb</button>
|
||||||
|
<button id="btn-get-nonexistent" class="test-button">Get Non-existent Crumb</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="test-section">
|
||||||
|
<h2>Delete Crumbs</h2>
|
||||||
|
<p>Test deleting crumbs and handling non-existent deletions</p>
|
||||||
|
<button id="btn-delete-session" class="test-button">Delete 'session_token'</button>
|
||||||
|
<button id="btn-delete-nonexistent" class="test-button">Delete Non-existent</button>
|
||||||
|
<button id="btn-clear-all" class="test-button">Clear All Crumbs</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="test-section">
|
||||||
|
<h2>Current Crumbs (Auto-refreshing)</h2>
|
||||||
|
<p>All stored crumbs (updates every 2 seconds to show expiry)</p>
|
||||||
|
<div id="crumbs-list"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="test-section">
|
||||||
|
<h2>Test Log</h2>
|
||||||
|
<button id="btn-clear-log" class="test-button">Clear Log</button>
|
||||||
|
<pre id="log"></pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="mt-6 p-4 bg-[#fef3c7] rounded">
|
||||||
|
<h3>Test Instructions:</h3>
|
||||||
|
<ol style="ml-4">
|
||||||
|
<li>Click "Set Permanent Crumb" to create a persistent cookie</li>
|
||||||
|
<li>Click "Set 10s Temporary Crumb" and watch it expire in the display</li>
|
||||||
|
<li>Click "Set 3s Short-lived Crumb" for quick expiry testing</li>
|
||||||
|
<li>Use "Get" buttons to test retrieval</li>
|
||||||
|
<li>Use "Delete" buttons to test removal</li>
|
||||||
|
<li>Refresh the page to verify persistence across sessions</li>
|
||||||
|
<li>Check user://gurt_crumbs.json file to see the storage format</li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
Reference in New Issue
Block a user