refactor: runtimes into xurfer/runtime + better URI errorhandling + added lua runtime

This commit is contained in:
Leon van Kammen 2026-06-24 15:56:15 +02:00
parent a11a0d1889
commit d25a33a160
124 changed files with 244 additions and 90 deletions

View file

@ -10,7 +10,7 @@ Developers can run it via [Lua](https://lua.org), [LÖVR](https://lovr.org) or [
$ cd xurfer
$ love . <url_or_file> [..] # if you have LÖVE2D installed (>v12)
$ lovr . <url_or_file> [..] # if you have LÖVR installed
$ lua main.lua <url_or_file> [..] # if you have lua installed
$ ./main.lua <url_or_file> [..] # if you have lua installed
```
**Example**: lovr xurfer <a href="https://snips.sh/f/_U5-XctEVE?r=1">https://snips.sh/f/_U5-XctEVE?r=1</a>
@ -39,8 +39,9 @@ The aim of this project is providing **a [XR Hypermedia browser](https://xrhf.is
## very hackable extensions
* XURFER does everything via browser extensions.<br>
* XURFER is the total sum of browser extensions.<br>
* XURFER does everything via browser extensions.
* XURFER is the total sum of browser extensions.
* XURFER can proxy missing api-calls to cli-cmds (like [wget/curl](xurfer/runtime/lua/main.lua) via the [unixy](xurfer/ext/unixy/main.lua) extension)
So you can strip/bloat however you want, or build one yourself:
@ -51,3 +52,9 @@ $ ./lovr xurfer
[i] loading 'myextension'
...
```
## Security
Instead of the traditional sandbox-in-a-browser-model (Chromium/Firefox e.g.), we suggest running XURFER via [bubblewrap](https://github.com/meta-quest/bubblewrap)
> TODO: suggest cli flags

View file

@ -1,14 +1,16 @@
-- sync lovr/love positional cli arguments
if arg[0] == nil and arg[1] ~= nil then arg[0] = arg[1] end
runtime = ""
package.path = package.path .. ';' ..
'?.lua;' ..
'?/init.lua;' ..
'lib/?.lua;'
if lovr ~= nil then runtime = "lovr" end
if love ~= nil then runtime = "love" end
local runtime = require("runtime/detect")
package.path = package.path .. ';' ..
arg[0] .. '/' .. runtime .. '/?.lua;' ..
arg[0] .. '/' .. runtime .. '/?/init.lua;' ..
arg[0] .. '/' .. runtime.path .. '/?.lua;' ..
arg[0] .. '/' .. runtime.path .. '/?/init.lua;' ..
arg[0] .. '/lib/?.lua'
require( runtime .. "/conf")
require( runtime.path .. "/conf")

View file

@ -1,6 +1,6 @@
local ecs = require("tiny-ecs")
ecs.init = function()
ecs.init = function(api)
print("[i] loading ecs")
baseEntify = ecs.processingSystem()
baseEntify.filter = ecs.rejectAll('commit')
@ -24,6 +24,7 @@ ecs.init = function()
end
ecs.clear = function()
trace("[ecs] ecs.clear() scene")
api.ext.exec("onClear")
api.ecs.clearEntities( api.world )
api.world.commit()

View file

@ -28,7 +28,7 @@ return {
api.ecs.add( api.world, obj )
obj.commit('on3DFile') -- notify systems
else
print("[3DFile] error: could not load " .. obj.URL.string )
print("[3DFile] error: could not load " .. obj.URL.string .. ": " .. obj.URLResponse.data )
end
end
end

View file

@ -43,8 +43,8 @@ http = {
req.headers = headers
channel:pop() -- remove last
channel:push(req) -- re-push but with status (which thread ignores)
lovr.timer.sleep(.1)
end
lovr.timer.sleep(.1)
end
]]
http.channel = lovr.thread.getChannel('http')
@ -57,7 +57,11 @@ http = {
local msg
local res, present = http.channel:peek()
if present and res.status ~= nil then
if present then
if res.status == nil and res.data == "Could not connect to server" then
res.status = 503 -- service unavailable
end
if res.status ~= nil then
msg = http.channel:pop()
-- we got our urlrequest response
if http.cache[ msg.url ] then
@ -67,6 +71,7 @@ http = {
error("msg-object was not found in cache")
end
end
end
end
end,

View file

@ -26,11 +26,11 @@ return {
sample.load({
gameSunsetImage = lovr.graphics.newTexture(
"lovr/sample/assets/game-sunset.png", {}
"runtime/lovr/sample/assets/game-sunset.png", {}
),
nineSliceImage = {
image = lovr.graphics.newTexture(
"lovr/sample/assets/ui-box-slice.png", { mipmaps = false }
"runtime/lovr/sample/assets/ui-box-slice.png", { mipmaps = false }
),
l = 8,
t = 8,
@ -40,14 +40,14 @@ return {
smileMSDFLayeredImage = {
{
image = lovr.graphics.newTexture(
"lovr/sample/assets/smile-bg.png",
"runtime/lovr/sample/assets/smile-bg.png",
{ linear = true, mipmaps = false }
),
color = iui.newColor(0.944, 0.794, 0.468)
},
{
image = lovr.graphics.newTexture(
"lovr/sample/assets/smile-fg.png",
"runtime/lovr/sample/assets/smile-fg.png",
{ linear = true, mipmaps = false }
),
color = iui.newColor(0.157, 0.157, 0.157)
@ -57,7 +57,7 @@ return {
{
image = {
image = lovr.graphics.newTexture(
"lovr/sample/assets/nine-slice-interior.png",
"runtime/lovr/sample/assets/nine-slice-interior.png",
{ linear = true, mipmaps = false }
),
l = 16,
@ -70,7 +70,7 @@ return {
{
image = {
image = lovr.graphics.newTexture(
"lovr/sample/assets/nine-slice-frame.png",
"runtime/lovr/sample/assets/nine-slice-frame.png",
{ linear = true, mipmaps = false }
),
l = 16,

View file

@ -16,7 +16,7 @@ return {
load = function()
--if api.iui.idiom == "vr" then
envTex = api.graphics.newTexture("lovr/assets/img/env.png", {})
envTex = api.graphics.newTexture("runtime/lovr/assets/img/env.png", {})
api.world.skybox = api.graphics.newTexture({
px = 'ext/startupscene/skybox/Dayright.jpg', -- 'px.png',
nx = 'ext/startupscene/skybox/Dayleft.jpg', -- 'nx.png',

View file

@ -39,10 +39,16 @@ unixy = {
init = function()
end,
proxyCLI = function( cmdfunc, callback )
proxyCLI = function( cmd, cmdfunc, callback )
if api.util.cmdExist(cmd) then
print("[i] attaching ".. cmd .. " cli-cmd to api")
return function(...)
return unixy.addjob( cmdfunc, callback, ...)
end
else
print("[!] could not attach ".. cmd .. " cli-cmd to api: not found")
return function()end
end
end,
update = function(dt)
@ -79,7 +85,6 @@ unixy = {
callback = callback
})
trace("[unixy] start job with pid " .. pid .. " => 1> " .. stdout_file .. " 2> " .. stderr_file)
print_r(unixy.jobs)
end,
check_jobs = function()
@ -98,7 +103,7 @@ unixy = {
-- Clean up
os.remove(job.stdout_file)
os.remove(job.stderr_file)
job.callback(stdout, stderr, retcode, unpack(job.args) )
job.callback(stdout, stderr, retcode, table.unpack(job.args) )
table.remove(unixy.jobs, i)
else
io.write(".")

View file

@ -113,6 +113,7 @@ end
-- utility function to traverse recursive table
xrf.traverse = function(arr, cb, key)
if arr == nil then return end
if key == nil then key = 'children' end
for k, child in pairs(arr) do
if type(child) == 'table' then

View file

@ -1,5 +1,3 @@
module(..., package.seeall)
---------------------------------------------------------------------------------
---------------------------------------------------------------------------------
--

22
xurfer/main.lua Normal file → Executable file
View file

@ -1,3 +1,4 @@
#!/usr/bin/env lua
-- -- GLB URLS
-- ./lovr xurfer https://coderofsalvation.codeberg.page/xrfragment-haxe/example/assets/example.glb?bar=1&f=2#foo'
-- ./lovr xurfer https://codeberg.org/coderofsalvation/xrfragment/raw/branch/main/assets/template/website/website.glb'
@ -9,23 +10,18 @@
-- ./lovr xurfer https://snips.sh/f/rHFLg-cewi?r=1' -- cube
-- ./lovr xurfer https://snips.sh/f/_U5-XctEVE?r=1' -- cube, monkey, scene
local runtime = nil
print("\n X U R F Ξ R")
print(" ⠭ ⠥ ⠗ ⠋ ⠑ ⠗\n")
if lovr ~= nil then
runtime = { path = "lovr", api = lovr }
elseif love ~= nil then
runtime = { path = "love", api = love };
end
if runtime == nil then
package.path = package.path .. ';' .. 'xurfer/?.lua'
package.path = package.path .. ';' .. '?.lua;'
require('conf')
end
local runtime = require("runtime/detect")
local util = require("util")
local ecs = require("ecs")
api = {
runtime = runtime,
parser = {
json = require("json"),
xml = require("xmlSimple"),
@ -46,7 +42,8 @@ require( runtime.path .. "/main")
util.loaddir( "ext", api, api.ext )
util.loaddir( "media", api, api.media )
ecs.init()
ecs.init(api)
api.ext.exec('init')
-- load urls passed on the cli
@ -55,3 +52,6 @@ foreach( arg, function(k,uri)
api.ecs.add( api.world, { URI = { url = uri, method = 'GET', target = '_top' } })
end
end)
-- for runtimes (runtime/lua) which dont automatically run an update-loop (like love/lovr)
api.ext.exec('main')

17
xurfer/runtime/detect.lua Normal file
View file

@ -0,0 +1,17 @@
local runtime = nil
-- determine runtime
if lovr ~= nil then
runtime = { name = "lovr", path = "runtime/lovr", api = lovr }
elseif love ~= nil then
runtime = { name = "lua", path = "runtime/love", api = love }
else
runtime = { name = "lua", path = "runtime/lua", api = require("runtime/lua/api") }
end
runtime.os = "unixy"
if package.config:sub(1,1) == "\\" then
runtime.os = "windows"
end
return runtime

View file

Before

Width:  |  Height:  |  Size: 175 KiB

After

Width:  |  Height:  |  Size: 175 KiB

View file

Before

Width:  |  Height:  |  Size: 599 B

After

Width:  |  Height:  |  Size: 599 B

View file

Before

Width:  |  Height:  |  Size: 232 B

After

Width:  |  Height:  |  Size: 232 B

View file

Before

Width:  |  Height:  |  Size: 405 B

After

Width:  |  Height:  |  Size: 405 B

View file

Before

Width:  |  Height:  |  Size: 628 B

After

Width:  |  Height:  |  Size: 628 B

View file

Before

Width:  |  Height:  |  Size: 617 B

After

Width:  |  Height:  |  Size: 617 B

View file

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -9,8 +9,8 @@ local iui = require "lib.iui"
local backend = require "lib.lovr-iui"
-- modify for nested lovr path for api
iui.resourcePath = "lovr/" .. iui.resourcePath
backend.resourcePath = "lovr/" .. backend.resourcePath
iui.resourcePath = "runtime/lovr/" .. iui.resourcePath
backend.resourcePath = "runtime/lovr/" .. backend.resourcePath
api.iui = iui
api.backend = backend
-- decorate api

View file

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 82 KiB

View file

Before

Width:  |  Height:  |  Size: 636 B

After

Width:  |  Height:  |  Size: 636 B

View file

Before

Width:  |  Height:  |  Size: 191 B

After

Width:  |  Height:  |  Size: 191 B

View file

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

Before

Width:  |  Height:  |  Size: 919 B

After

Width:  |  Height:  |  Size: 919 B

Some files were not shown because too many files have changed in this diff Show more