changeset 13897:6d067ec8ec4a

Merge 13.0->trunk
author Kim Alvefur <zash@zash.se>
date Wed, 18 Jun 2025 15:02:23 +0200
parents b003c4c921ff (diff) 17b5a10bd9c9 (current diff)
children aac30c5ee334
files
diffstat 19 files changed, 175 insertions(+), 181 deletions(-) [+]
line wrap: on
line diff
--- a/GNUmakefile	Wed Jun 18 15:02:02 2025 +0200
+++ b/GNUmakefile	Wed Jun 18 15:02:23 2025 +0200
@@ -141,9 +141,10 @@
 vpath %.tl teal-src/prosody
 %.lua: %.tl
 	tl -I teal-src/ --gen-compat off --gen-target 5.1 gen $^ -o $@
-	-lua-format -i $@
+	-lua-format -i --no-keep-simple-control-block-one-line --no-keep-simple-function-one-line $@
+	sed -i "1i-- This file is generated from $<" $@
 
-teal: util/jsonschema.lua util/datamapper.lua util/jsonpointer.lua
+teal: util/jsonschema.lua util/datamapper.lua util/jsonpointer.lua plugins/mod_cron.lua
 
 util/%.so:
 	$(MAKE) install -C util-src
--- a/plugins/mod_cron.lua	Wed Jun 18 15:02:02 2025 +0200
+++ b/plugins/mod_cron.lua	Wed Jun 18 15:02:23 2025 +0200
@@ -1,3 +1,4 @@
+-- This file is generated from teal-src/prosody/plugins/mod_cron.tl
 module:set_global();
 
 local async = require("prosody.util.async");
@@ -9,7 +10,7 @@
 local active_hosts = {}
 
 if prosody.process_type == "prosodyctl" then
-	return; -- Yes, it happens...
+	return
 end
 
 function module.add_host(host_module)
@@ -17,14 +18,24 @@
 	local last_run_times = host_module:open_store("cron", "map");
 	active_hosts[host_module.host] = true;
 
-	local function save_task(task, started_at) last_run_times:set(nil, task.id, started_at); end
+	local function save_task(task, started_at)
+		last_run_times:set(nil, task.id, started_at);
+	end
 
-	local function restore_task(task) if task.last == nil then task.last = last_run_times:get(nil, task.id); end end
+	local function restore_task(task)
+		if task.last == nil then
+			task.last = last_run_times:get(nil, task.id);
+		end
+	end
 
 	local function task_added(event)
 		local task = event.item;
-		if task.name == nil then task.name = task.when; end
-		if task.id == nil then task.id = event.source.name .. "/" .. task.name:gsub("%W", "_"):lower(); end
+		if task.name == nil then
+			task.name = task.when;
+		end
+		if task.id == nil then
+			task.id = event.source.name .. "/" .. task.name:gsub("%W", "_"):lower();
+		end
 		task.period = host_module:get_option_period(task.id:gsub("/", "_") .. "_period", "1" .. task.when, 60, 86400 * 7 * 53);
 		task.restore = restore_task;
 		task.save = save_task;
@@ -40,14 +51,20 @@
 
 	host_module:handle_items("task", task_added, task_removed, true);
 
-	function host_module.unload() active_hosts[host_module.host] = nil; end
+	function host_module.unload()
+		active_hosts[host_module.host] = nil;
+	end
 end
 
-local function should_run(task, last) return not last or last + task.period * 0.995 <= os.time() end
+local function should_run(task, last)
+	return not last or last + task.period * 0.995 <= os.time()
+end
 
 local function run_task(task)
 	task:restore();
-	if not should_run(task, task.last) then return end
+	if not should_run(task, task.last) then
+		return
+	end
 	local started_at = os.time();
 	task:run(started_at);
 	task.last = started_at;
@@ -55,7 +72,7 @@
 end
 
 local function spread(t, factor)
-	return t * (1 - factor + 2*factor*math.random());
+	return t * (1 - factor + 2 * factor * math.random())
 end
 
 local task_runner = async.runner(run_task);
@@ -64,7 +81,9 @@
 	local delay = spread(cron_check_delay, cron_spread_factor);
 	for host in pairs(active_hosts) do
 		module:log("debug", "Running periodic tasks for host %s", host);
-		for _, task in ipairs(module:context(host):get_host_items("task")) do task_runner:run(task); end
+		for _, task in ipairs(module:context(host):get_host_items("task")) do
+			task_runner:run(task);
+		end
 	end
 	module:log("debug", "Wait %gs", delay);
 	return delay
--- a/plugins/mod_http_files.lua	Wed Jun 18 15:02:02 2025 +0200
+++ b/plugins/mod_http_files.lua	Wed Jun 18 15:02:23 2025 +0200
@@ -11,7 +11,7 @@
 local open = io.open;
 local fileserver = require"prosody.net.http.files";
 
-local base_path = module:get_option_path("http_files_dir", module:get_option_path("http_path"));
+local base_path = assert(module:get_option_path("http_files_dir", module:get_option_path("http_path")), "missing required setting 'http_files_dir'");
 local cache_size = module:get_option_integer("http_files_cache_size", 128, 1);
 local cache_max_file_size = module:get_option_integer("http_files_cache_max_file_size", 4096, 1);
 local dir_indices = module:get_option_array("http_index_files", { "index.html", "index.htm" });
@@ -55,37 +55,12 @@
 end
 
 -- COMPAT -- TODO deprecate
-function serve(opts)
-	if type(opts) ~= "table" then -- assume path string
-		opts = { path = opts };
-	end
-	if opts.directory_index == nil then
-		opts.directory_index = directory_index;
-	end
-	if opts.mime_map == nil then
-		opts.mime_map = mime_map;
-	end
-	if opts.cache_size == nil then
-		opts.cache_size = cache_size;
-	end
-	if opts.cache_max_file_size == nil then
-		opts.cache_max_file_size = cache_max_file_size;
-	end
-	if opts.index_files == nil then
-		opts.index_files = dir_indices;
-	end
-	module:log("warn", "%s should be updated to use 'prosody.net.http.files' instead of mod_http_files", get_calling_module());
-	return fileserver.serve(opts);
+function serve()
+	error(string.format("%s should be updated to use 'prosody.net.http.files' instead of mod_http_files", get_calling_module()));
 end
 
-function wrap_route(routes)
-	module:log("debug", "%s should be updated to use 'prosody.net.http.files' instead of mod_http_files", get_calling_module());
-	for route,handler in pairs(routes) do
-		if type(handler) ~= "function" then
-			routes[route] = fileserver.serve(handler);
-		end
-	end
-	return routes;
+function wrap_route()
+	error(string.format("%s should be updated to use 'prosody.net.http.files' instead of mod_http_files", get_calling_module()));
 end
 
 module:provides("http", {
--- a/teal-src/prosody/plugins/mod_cron.tl	Wed Jun 18 15:02:02 2025 +0200
+++ b/teal-src/prosody/plugins/mod_cron.tl	Wed Jun 18 15:02:23 2025 +0200
@@ -36,6 +36,10 @@
 
 local active_hosts : { string : boolean } = {  }
 
+if prosody.process_type == "prosodyctl" then
+	return -- Yes, it happens...
+end
+
 function module.add_host(host_module : moduleapi)
 
 	local last_run_times = host_module:open_store("cron", "map") as map_store<string,integer>;
@@ -63,13 +67,13 @@
 		task.restore = restore_task;
 		task.save = save_task;
 		module:log("debug", "%s task %s added", task.when, task.id);
-		return true;
+		return true
 	end
 
 	local function task_removed(event : task_event) : boolean
 		local task = event.item;
 		host_module:log("debug", "Task %s removed", task.id);
-		return true;
+		return true
 	end
 
 	host_module:handle_items("task", task_added, task_removed, true);
@@ -80,13 +84,13 @@
 end
 
 local function should_run(task : task_spec, last : integer) : boolean
-	return not last or last + task.period * 0.995 <= os.time();
+	return not last or last + task.period * 0.995 <= os.time()
 end
 
 local function run_task(task : task_spec)
 	task:restore();
 	if not should_run(task, task.last) then
-		return;
+		return
 	end
 	local started_at = os.time();
 	task:run(started_at);
@@ -95,7 +99,7 @@
 end
 
 local function spread(t : number, factor : number) : number
-	return t * (1 - factor + 2*factor*math.random());
+	return t * (1 - factor + 2*factor*math.random())
 end
 
 local task_runner : async.runner_t<task_spec> = async.runner(run_task);
@@ -109,7 +113,7 @@
 		end
 	end
 	module:log("debug", "Wait %gs", delay);
-	return delay;
+	return delay
 end);
 
 -- TODO measure load, pick a good time to do stuff
@@ -122,7 +126,7 @@
 	args = {};
 	handler = function (self, filter_host : string)
 		local format_table = require "prosody.util.human.io".table;
-		local it = require "util.iterators";
+		local it = require "prosody.util.iterators";
 		local row = format_table({
 			{ title = "Host", width = "2p" };
 			{ title = "Task", width = "3p" };
--- a/teal-src/prosody/util/array.d.tl	Wed Jun 18 15:02:02 2025 +0200
+++ b/teal-src/prosody/util/array.d.tl	Wed Jun 18 15:02:23 2025 +0200
@@ -1,9 +1,10 @@
-local record array_t<T>
-	{ T }
+local record array_t<T> is { T }
+	-- TODO methods
 end
 
 local record lib
 	metamethod __call : function () : array_t
+	-- TODO library functions
 end
 
 return lib
--- a/teal-src/prosody/util/crypto.d.tl	Wed Jun 18 15:02:02 2025 +0200
+++ b/teal-src/prosody/util/crypto.d.tl	Wed Jun 18 15:02:23 2025 +0200
@@ -2,7 +2,9 @@
 	record key
 		private_pem : function (key) : string
 		public_pem : function (key) : string
+		public_raw : function (key) : string
 		get_type : function (key) : string
+		derive : function (key, key) : string
 	end
 
 	type base_evp_sign = function (key, message : string) : string
@@ -44,9 +46,11 @@
 	aes_256_ctr_decrypt : Levp_decrypt
 
 	generate_ed25519_keypair : function () : key
+	generate_p256_keypair : function () : key
 
 	import_private_pem : function (string) : key
 	import_public_pem : function (string) : key
+	import_public_ec_raw : function (string, string) : key
 
 	parse_ecdsa_signature : function (string, integer) : string, string
 	build_ecdsa_signature : function (r : string, s : string) : string
--- a/teal-src/prosody/util/dataforms.d.tl	Wed Jun 18 15:02:02 2025 +0200
+++ b/teal-src/prosody/util/dataforms.d.tl	Wed Jun 18 15:02:23 2025 +0200
@@ -5,7 +5,7 @@
 		title : string
 		instructions : string
 
-		record form_field
+		record form_field is { form_field }
 
 			enum field_type
 				"boolean"
@@ -35,7 +35,6 @@
 			options : table
 		end
 
-		{ form_field }
 
 		enum form_type
 			"form"
--- a/teal-src/prosody/util/datamapper.tl	Wed Jun 18 15:02:02 2025 +0200
+++ b/teal-src/prosody/util/datamapper.tl	Wed Jun 18 15:02:23 2025 +0200
@@ -25,7 +25,7 @@
 local json = require "prosody.util.json"
 local pointer = require "prosody.util.jsonpointer";
 
-local json_type_name = json.json_type_name;
+local type json_type_name = json.json_type_name;
 local json_schema_object = require "prosody.util.jsonschema"
 local type schema_t = boolean | json_schema_object
 
@@ -42,7 +42,7 @@
 local function totype(t : json_type_name, s : string) : any
 	if not s then return nil end
 	if t == "string" then
-		return s;
+		return s
 	elseif t == "boolean" then
 		return toboolean(s)
 	elseif t == "number" or t == "integer" then
@@ -63,10 +63,10 @@
 local function resolve_schema(schema  : schema_t, root : json_schema_object) : schema_t
 	if schema is json_schema_object then
 		if schema["$ref"] and schema["$ref"]:sub(1, 1) == "#" then
-			return pointer.resolve(root as table, schema["$ref"]:sub(2)) as schema_t;
+			return pointer.resolve(root as table, schema["$ref"]:sub(2)) as schema_t
 		end
 	end
-	return schema;
+	return schema
 end
 
 local function guess_schema_type(schema : json_schema_object) : json_type_name
@@ -95,8 +95,6 @@
 
 	if propschema is json_schema_object then
 		proptype = guess_schema_type(propschema);
-	elseif propschema is string then -- Teal says this can never be a string, but it could before so best be sure
-		error("schema as string is not supported: "..propschema.." {"..current_ns.."}"..propname)
 	end
 
 	if proptype == "object" or proptype == "array" then
@@ -158,7 +156,7 @@
 			c = s:get_child(nil, namespace);
 		end
 		if c then
-			return c.name;
+			return c.name
 		end
 	elseif value_where == "in_attribute" then
 		local attr = name
@@ -250,7 +248,7 @@
 			table.insert(out, totype(proptype, value));
 		end
 	end
-	return out;
+	return out
 end
 
 local function parse (schema : json_schema_object, s : st.stanza_t) : table
@@ -269,7 +267,7 @@
 		return  v
 	elseif proptype == "number" and v is number then
 		return  string.format("%g", v)
-	elseif proptype == "integer" and v is number then -- TODO is integer
+	elseif proptype == "integer" and v is integer then
 		return  string.format("%d", v)
 	elseif proptype == "boolean" then
 		return  v and "1" or "0"
@@ -361,7 +359,7 @@
 				unparse_property(out, v, proptype, propschema, value_where, name, namespace, current_ns, prefix, single_attribute, root)
 			end
 		end
-		return out;
+		return out
 
 	elseif s_type == "array" then
 		local itemschema = resolve_schema(schema.items, root)
@@ -369,7 +367,7 @@
 		for _, item in ipairs(t as { string }) do
 			unparse_property(out, item, proptype, itemschema, value_where, name, namespace, current_ns, prefix, single_attribute, root)
 		end
-		return out;
+		return out
 	end
 end
 
--- a/teal-src/prosody/util/jsonschema.tl	Wed Jun 18 15:02:02 2025 +0200
+++ b/teal-src/prosody/util/jsonschema.tl	Wed Jun 18 15:02:23 2025 +0200
@@ -10,12 +10,8 @@
 
 if not math.type then require "prosody.util.mathcompat" end
 
-
-local utf8_enc = rawget(_G, "utf8") or require"prosody.util.encodings".utf8;
-local utf8_len = utf8_enc.len or function(s : string) : integer
-	local _, count = s:gsub("[%z\001-\127\194-\253][\128-\191]*", "");
-	return count;
-end;
+-- XXX util.encodings seems to count differently from the Lua builtin
+local utf8_len = rawget(_G, "utf8") and utf8.len or require"prosody.util.encodings".utf8.length;
 
 local json = require "prosody.util.json"
 local null = json.null;
@@ -166,7 +162,7 @@
 end
 local type errors = { validation_error }
 local function mkerr(sloc:string,iloc:string,err:string) : validation_error
-	return { schemaLocation = sloc; instanceLocation = iloc; error = err };
+	return { schemaLocation = sloc; instanceLocation = iloc; error = err }
 end
 
 local function validate (schema : schema_t, data : any, root : json_schema_object, sloc : string, iloc : string, errs:errors) : boolean, errors
@@ -186,14 +182,14 @@
 		if referenced ~= nil and referenced ~= root and referenced ~= schema then
 			if not validate(referenced, data, root, schema["$ref"], iloc, errs) then
 				table.insert(errs, mkerr(sloc.."/$ref", iloc, "Subschema failed validation"))
-				return false, errs;
+				return false, errs
 			end
 		end
 	end
 
 	if not simple_validate(schema.type, data) then
 		table.insert(errs, mkerr(sloc.."/type", iloc, "unexpected type"));
-		return false, errs;
+		return false, errs
 	end
 
 	if schema.type == "object" then
@@ -202,7 +198,7 @@
 			for k in pairs(data) do
 				if not k is string then
 					table.insert(errs, mkerr(sloc.."/type", iloc, "'object' had non-string keys"));
-					return false, errs;
+					return false, errs
 				end
 			end
 		end
@@ -214,7 +210,7 @@
 			for i in pairs(data) do
 				if not i is integer then
 					table.insert(errs, mkerr(sloc.."/type", iloc, "'array' had non-integer keys"));
-					return false, errs;
+					return false, errs
 				end
 			end
 		end
@@ -231,7 +227,7 @@
 		end
 		if not match then
 			table.insert(errs, mkerr(sloc.."/enum", iloc, "not one of the enumerated values"));
-			return false, errs;
+			return false, errs
 		end
 	end
 
@@ -240,42 +236,42 @@
 	if data is string then
 		if schema.maxLength and utf8_len(data) > schema.maxLength then
 			table.insert(errs, mkerr(sloc.."/maxLength", iloc, "string too long"))
-			return false, errs;
+			return false, errs
 		end
 		if schema.minLength and utf8_len(data) < schema.minLength then
 			table.insert(errs, mkerr(sloc.."/maxLength", iloc, "string too short"))
-			return false, errs;
+			return false, errs
 		end
 		if schema.luaPattern and not data:match(schema.luaPattern) then
 			table.insert(errs, mkerr(sloc.."/luaPattern", iloc, "string does not match pattern"))
-			return false, errs;
+			return false, errs
 		end
 	end
 
 	if data is number then
 		if schema.multipleOf and (data == 0 or data % schema.multipleOf ~= 0) then
 			table.insert(errs, mkerr(sloc.."/luaPattern", iloc, "not a multiple"))
-			return false, errs;
+			return false, errs
 		end
 
 		if schema.maximum and not ( data <= schema.maximum ) then
 			table.insert(errs, mkerr(sloc.."/maximum", iloc, "number exceeds maximum"))
-			return false, errs;
+			return false, errs
 		end
 
 		if schema.exclusiveMaximum and not ( data < schema.exclusiveMaximum ) then
 			table.insert(errs, mkerr(sloc.."/exclusiveMaximum", iloc, "number exceeds exclusive maximum"))
-			return false, errs;
+			return false, errs
 		end
 
 		if schema.minimum and not ( data >= schema.minimum ) then
 			table.insert(errs, mkerr(sloc.."/minimum", iloc, "number below minimum"))
-			return false, errs;
+			return false, errs
 		end
 
 		if schema.exclusiveMinimum and not ( data > schema.exclusiveMinimum ) then
 			table.insert(errs, mkerr(sloc.."/exclusiveMinimum", iloc, "number below exclusive minimum"))
-			return false, errs;
+			return false, errs
 		end
 	end
 
@@ -283,7 +279,7 @@
 		for i, sub in ipairs(schema.allOf) do
 			if not validate(sub, data, root, sloc.."/allOf/"..i, iloc, errs) then
 				table.insert(errs, mkerr(sloc.."/allOf", iloc, "did not match all subschemas"))
-				return false, errs;
+				return false, errs
 			end
 		end
 	end
@@ -297,7 +293,7 @@
 		end
 		if valid ~= 1 then
 			table.insert(errs, mkerr(sloc.."/oneOf", iloc, "did not match exactly one subschema"))
-			return false, errs;
+			return false, errs
 		end
 	end
 
@@ -311,14 +307,14 @@
 		end
 		if not match then
 			table.insert(errs, mkerr(sloc.."/anyOf", iloc, "did not match any subschema"))
-			return false, errs;
+			return false, errs
 		end
 	end
 
 	if schema["not"] ~= nil then
 		if validate(schema["not"], data, root, sloc.."/not", iloc, errs) then
 			table.insert(errs, mkerr(sloc.."/not", iloc, "did match subschema"))
-			return false, errs;
+			return false, errs
 		end
 	end
 
@@ -327,14 +323,14 @@
 			if schema["then"] ~= nil then
 				if not validate(schema["then"], data, root, sloc.."/then", iloc, errs) then
 					table.insert(errs, mkerr(sloc.."/then", iloc, "did not match subschema"))
-					return false, errs;
+					return false, errs
 				end
 			end
 		else
 			if schema["else"] ~= nil then
 				if not validate(schema["else"], data, root, sloc.."/else", iloc, errs) then
 					table.insert(errs, mkerr(sloc.."/else", iloc, "did not match subschema"))
-					return false, errs;
+					return false, errs
 				end
 			end
 		end
@@ -342,7 +338,7 @@
 
 	if schema.const ~= nil and schema.const ~= data then
 		table.insert(errs, mkerr(sloc.."/const", iloc, "did not match constant value"))
-		return false, errs;
+		return false, errs
 	end
 
 	if data is table then
@@ -352,19 +348,19 @@
 
 		if schema.maxItems and #(data as {any}) > schema.maxItems then
 			table.insert(errs, mkerr(sloc.."/maxItems", iloc, "too many items"))
-			return false, errs;
+			return false, errs
 		end
 
 		if schema.minItems and #(data as {any}) < schema.minItems then
 			table.insert(errs, mkerr(sloc.."/minItems", iloc, "too few items"))
-			return false, errs;
+			return false, errs
 		end
 
 		if schema.required then
 			for _, k in ipairs(schema.required) do
 				if data[k] == nil then
 					table.insert(errs, mkerr(sloc.."/required", iloc.."/"..tostring(k), "missing required property"))
-					return false, errs;
+					return false, errs
 				end
 			end
 		end
@@ -375,7 +371,7 @@
 					for _, req in ipairs(reqs) do
 						if data[req] == nil then
 							table.insert(errs, mkerr(sloc.."/dependentRequired", iloc, "missing dependent required property"))
-							return false, errs;
+							return false, errs
 						end
 					end
 				end
@@ -387,7 +383,7 @@
 			for k in pairs(data) do
 				if not validate(schema.propertyNames, k, root, sloc.."/propertyNames", iloc.."/"..tostring(k), errs) then
 					table.insert(errs, mkerr(sloc.."/propertyNames", iloc.."/"..tostring(k), "a property name did not match subschema"))
-					return false, errs;
+					return false, errs
 				end
 			end
 		end
@@ -401,7 +397,7 @@
 			for k, sub in pairs(schema.properties) do
 				if data[k] ~= nil and not validate(sub, data[k], root, sloc.."/"..tostring(k), iloc.."/"..tostring(k), errs) then
 					table.insert(errs, mkerr(sloc.."/"..tostring(k), iloc.."/"..tostring(k), "a property did not match subschema"))
-					return false, errs;
+					return false, errs
 				end
 				seen_properties[k] = true
 			end
@@ -414,7 +410,7 @@
 					if k is string and k:match(pattern) then
 						if not validate(sub, data[k], root, sloc.."/luaPatternProperties", iloc, errs) then
 							table.insert(errs, mkerr(sloc.."/luaPatternProperties/"..pattern, iloc.."/"..tostring(k), "a property did not match subschema"))
-							return false, errs;
+							return false, errs
 						end
 						seen_properties[k] = true
 					end
@@ -427,7 +423,7 @@
 				if not seen_properties[k as string] then
 					if not validate(schema.additionalProperties, v, root, sloc.."/additionalProperties", iloc.."/"..tostring(k), errs) then
 						table.insert(errs, mkerr(sloc.."/additionalProperties", iloc.."/"..tostring(k), "additional property did not match subschema"))
-						return false, errs;
+						return false, errs
 					end
 				end
 			end
@@ -437,7 +433,7 @@
 			for k, sub in pairs(schema.dependentSchemas) do
 				if data[k] ~= nil and not validate(sub, data, root, sloc.."/dependentSchemas/"..k, iloc, errs) then
 					table.insert(errs, mkerr(sloc.."/dependentSchemas", iloc.."/"..tostring(k), "did not match dependent subschema"))
-					return false, errs;
+					return false, errs
 				end
 			end
 		end
@@ -448,7 +444,7 @@
 			for _, v in pairs(data) do
 				if values[v] then
 					table.insert(errs, mkerr(sloc.."/uniqueItems", iloc, "had duplicate items"))
-					return false, errs;
+					return false, errs
 				end
 				values[v] = true
 			end
@@ -463,7 +459,7 @@
 					p = i
 				else
 					table.insert(errs, mkerr(sloc.."/prefixItems/"..i, iloc.."/"..tostring(i), "did not match subschema"))
-					return false, errs;
+					return false, errs
 				end
 			end
 		end
@@ -472,7 +468,7 @@
 			for i = p+1, #(data as {any}) do
 				if not validate(schema.items, data[i], root, sloc, iloc.."/"..i, errs) then
 					table.insert(errs, mkerr(sloc.."/prefixItems/"..i, iloc.."/"..i, "did not match subschema"))
-					return false, errs;
+					return false, errs
 				end
 			end
 		end
@@ -488,18 +484,18 @@
 			end
 			if found < (schema.minContains or 1) then
 				table.insert(errs, mkerr(sloc.."/minContains", iloc, "too few matches"))
-				return false, errs;
+				return false, errs
 			elseif found > (schema.maxContains or math.huge) then
 				table.insert(errs, mkerr(sloc.."/maxContains", iloc, "too many matches"))
-				return false, errs;
+				return false, errs
 			end
 		end
 	end
 
-	return true;
+	return true
 end
 
 
 json_schema_object.validate = validate;
 
-return json_schema_object;
+return json_schema_object
--- a/teal-src/prosody/util/pposix.d.tl	Wed Jun 18 15:02:02 2025 +0200
+++ b/teal-src/prosody/util/pposix.d.tl	Wed Jun 18 15:02:23 2025 +0200
@@ -86,6 +86,14 @@
 
 	mkdir : function (dir : string) : boolean, string
 
+	enum pipe_flag_names
+		"cloexec"
+		"direct"
+		"nonblock"
+	end
+	pipe : function (... : pipe_flag_names) : integer, integer
+	fdopen : function (integer, string) : FILE, string
+
 	setrlimit : function (resource : ulimit_resource, soft : ulimit_limit, hard : ulimit_limit) : boolean, string
 	getrlimit : function (resource : ulimit_resource) : boolean, ulimit_limit, ulimit_limit
 	getrlimit : function (resource : ulimit_resource) : boolean, string
@@ -103,7 +111,7 @@
 
 	ENOENT : integer
 	_NAME : string
-	_VESRION : string
+	_VERSION : string
 end
 
 return pposix
--- a/teal-src/prosody/util/queue.d.tl	Wed Jun 18 15:02:02 2025 +0200
+++ b/teal-src/prosody/util/queue.d.tl	Wed Jun 18 15:02:23 2025 +0200
@@ -18,4 +18,4 @@
 
 	new : function<T> (size:integer, allow_wrapping:boolean) : queue<T>
 end
-return lib;
+return lib
--- a/teal-src/prosody/util/set.d.tl	Wed Jun 18 15:02:02 2025 +0200
+++ b/teal-src/prosody/util/set.d.tl	Wed Jun 18 15:02:23 2025 +0200
@@ -1,22 +1,22 @@
 local record lib
 	record Set<T>
-		add : function<T> (Set<T>, T)
-		contains : function<T> (Set<T>, T) : boolean
-		contains_set : function<T> (Set<T>, Set<T>) : boolean
-		items :  function<T> (Set<T>) : function<T> (Set<T>, T) : T
-		remove : function<T> (Set<T>, T)
-		add_list : function<T> (Set<T>, { T })
-		include : function<T> (Set<T>, Set<T>)
-		exclude : function<T> (Set<T>, Set<T>)
-		empty : function<T> (Set<T>) : boolean
+		add : function (Set, T)
+		contains : function (Set, T) : boolean
+		contains_set : function (Set, Set) : boolean
+		items :  function (Set) : function (Set, T) : T
+		remove : function (Set, T)
+		add_list : function (Set, { T })
+		include : function (Set, Set)
+		exclude : function (Set, Set)
+		empty : function (Set) : boolean
 	end
 
 	new : function<T> ({ T }) : Set<T>
 	is_set : function (any) : boolean
-	union : function<T> (Set<T>, Set<T>) : Set <T>
-	difference : function<T> (Set<T>, Set<T>) : Set <T>
-	intersection : function<T> (Set<T>, Set<T>) : Set <T>
-	xor : function<T> (Set<T>, Set<T>) : Set <T>
+	union : function (Set, Set) : Set
+	difference : function (Set, Set) : Set
+	intersection : function (Set, Set) : Set
+	xor : function (Set, Set) : Set
 end
 
 return lib
--- a/teal-src/prosody/util/signal.d.tl	Wed Jun 18 15:02:02 2025 +0200
+++ b/teal-src/prosody/util/signal.d.tl	Wed Jun 18 15:02:23 2025 +0200
@@ -36,6 +36,7 @@
 	signal : function (integer | Signal, function, boolean) : boolean
 	raise : function (integer | Signal)
 	kill : function (integer, integer | Signal)
+	signalfd : function (integer) : FILE
 	-- enum : integer
 end
 return lib
--- a/teal-src/prosody/util/smqueue.tl	Wed Jun 18 15:02:02 2025 +0200
+++ b/teal-src/prosody/util/smqueue.tl	Wed Jun 18 15:02:23 2025 +0200
@@ -12,12 +12,11 @@
 			"head"
 			"pop"
 		end
-		push : function (smqueue, T)
-		ack : function (smqueue, integer) : { T }, ack_errors
+		push : function (smqueue<T>, T)
+		ack : function (smqueue<T>, integer) : { T }, ack_errors
 		resumable : function (smqueue<T>) : boolean
 		resume : function (smqueue<T>)  : queue.queue.iterator, any, integer
-		type consume_iter = function (smqueue<T>) : T
-		consume : function (smqueue<T>) : consume_iter
+		consume : function (smqueue<T>) : function() : T
 
 		table : function (smqueue<T>) : { T }
 	end
@@ -26,17 +25,17 @@
 
 local type smqueue = lib.smqueue;
 
-function smqueue:push(v)
+function smqueue:push(v : T)
 	self._head = self._head + 1;
 	-- Wraps instead of errors
 	assert(self._queue:push(v));
 end
 
-function smqueue:ack(h : integer) : { any }, smqueue.ack_errors
+function smqueue:ack(h : integer) : { T }, smqueue.ack_errors
 	if h < self._tail then
-		return nil, "tail";
+		return nil, "tail"
 	elseif h > self._head then
-		return nil, "head";
+		return nil, "head"
 	end
 	-- TODO optimize? cache table fields
 	local acked = {};
@@ -44,39 +43,41 @@
 	local expect = self._head - self._tail;
 	while expect < self._queue:count() do
 		local v = self._queue:pop();
-		if not v then return nil, "pop"; end
+		if not v then
+			return nil, "pop"
+		end
 		table.insert(acked, v);
 	end
-	return acked;
+	return acked
 end
 
 function smqueue:count_unacked() : integer
-	return self._head - self._tail;
+	return self._head - self._tail
 end
 
 function smqueue:count_acked() : integer
-	return self._tail;
+	return self._tail
 end
 
 function smqueue:resumable() : boolean
-	return self._queue:count() >= (self._head - self._tail);
+	return self._queue:count() >= (self._head - self._tail)
 end
 
 function smqueue:resume() : queue.queue.iterator, any, integer
-	return self._queue:items();
+	return self._queue:items()
 end
 
-function smqueue:consume() : queue.queue.consume_iter
-	return self._queue:consume()
+function smqueue:consume() : (function() : T)
+	return self._queue:consume() as (function() : T)
 end
 
 -- Compatibility layer, plain ol' table
-function smqueue:table() : { any }
-	local t : { any } = {};
+function smqueue:table() : { T }
+	local t : { T } = {};
 	for i, v in self:resume() do
 		t[i] = v;
 	end
-	return t;
+	return t
 end
 
 local function freeze(q : smqueue<any>) : { string:integer }
@@ -91,9 +92,9 @@
 	__freeze = freeze;
 }
 
-function lib.new<T>(size : integer) : queue.queue<T>
+function lib.new<T>(size : integer) : smqueue<T>
 	assert(size>0);
-	return setmetatable({ _head = 0; _tail = 0; _queue = queue.new(size, true) }, queue_mt);
+	return setmetatable({ _head = 0; _tail = 0; _queue = queue.new(size, true) }, queue_mt)
 end
 
-return lib;
+return lib
--- a/teal-src/prosody/util/stanza.d.tl	Wed Jun 18 15:02:02 2025 +0200
+++ b/teal-src/prosody/util/stanza.d.tl	Wed Jun 18 15:02:23 2025 +0200
@@ -37,10 +37,9 @@
 		"unexpected-request"
 	end
 
-	record stanza_t
+	record stanza_t is { stanza_t | string }
 		name : string
 		attr : { string : string }
-		{ stanza_t | string }
 		tags : { stanza_t }
 
 		query : function ( stanza_t, string ) : stanza_t
@@ -77,10 +76,9 @@
 		indent : function ( stanza_t, integer, string ) : stanza_t
 	end
 
-	record serialized_stanza_t
+	record serialized_stanza_t is { serialized_stanza_t | string }
 		name : string
 		attr : { string : string }
-		{ serialized_stanza_t | string }
 	end
 
 	record message_attr
--- a/teal-src/prosody/util/xtemplate.tl	Wed Jun 18 15:02:02 2025 +0200
+++ b/teal-src/prosody/util/xtemplate.tl	Wed Jun 18 15:02:23 2025 +0200
@@ -56,7 +56,7 @@
 
 			if func == "each" and tmpl then
 				if not st.is_stanza(value) then
-					return pre_blank..post_blank;
+					return pre_blank..post_blank
 				end
 				if not args then value, args = root, path; end
 				local ns, name = s_match(args, "^(%b{})(.*)$");
@@ -94,15 +94,15 @@
 
 		if value is string then
 			if not is_escaped then value = escape(value); end
-			return pre_blank .. value .. post_blank;
+			return pre_blank .. (value as string) .. post_blank
 		elseif st.is_stanza(value) then
 			value = value:get_text();
 			if value then
-				return pre_blank .. escape(value) .. post_blank;
+				return pre_blank .. escape(value) .. post_blank
 			end
 		end
-		return pre_blank .. post_blank;
-	end));
+		return pre_blank .. post_blank
+	end))
 end
 
-return { render = render };
+return { render = render }
--- a/util/datamapper.lua	Wed Jun 18 15:02:02 2025 +0200
+++ b/util/datamapper.lua	Wed Jun 18 15:02:23 2025 +0200
@@ -1,13 +1,13 @@
--- This file is generated from teal-src/util/datamapper.lua
-
+-- This file is generated from teal-src/prosody/util/datamapper.tl
 if not math.type then
 	require("prosody.util.mathcompat")
 end
 
 local st = require("prosody.util.stanza");
+local json = require("prosody.util.json")
 local pointer = require("prosody.util.jsonpointer");
 
-local schema_t = {}
+local json_schema_object = require("prosody.util.jsonschema")
 
 local function toboolean(s)
 	if s == "true" or s == "1" then
@@ -32,8 +32,6 @@
 	end
 end
 
-local value_goes = {}
-
 local function resolve_schema(schema, root)
 	if type(schema) == "table" then
 		if schema["$ref"] and schema["$ref"]:sub(1, 1) == "#" then
@@ -69,8 +67,6 @@
 
 	if type(propschema) == "table" then
 		proptype = guess_schema_type(propschema);
-	elseif type(propschema) == "string" then
-		error("schema as string is not supported: " .. propschema .. " {" .. current_ns .. "}" .. propname)
 	end
 
 	if proptype == "object" or proptype == "array" then
@@ -103,7 +99,7 @@
 			end
 		end
 		if propschema["const"] then
-			enums = {propschema["const"]}
+			enums = { propschema["const"] }
 		elseif propschema["enum"] then
 			enums = propschema["enum"]
 		end
@@ -137,7 +133,7 @@
 	elseif value_where == "in_attribute" then
 		local attr = name
 		if prefix then
-			attr = prefix .. ":" .. name
+			attr = prefix .. ':' .. name
 		elseif namespace and namespace ~= s.attr.xmlns then
 			attr = namespace .. "\1" .. name
 		end
@@ -243,7 +239,7 @@
 		return v
 	elseif proptype == "number" and type(v) == "number" then
 		return string.format("%g", v)
-	elseif proptype == "integer" and type(v) == "number" then
+	elseif proptype == "integer" and math.type(v) == "integer" then
 		return string.format("%d", v)
 	elseif proptype == "boolean" then
 		return v and "1" or "0"
@@ -252,13 +248,12 @@
 
 local unparse
 
-local function unparse_property(out, v, proptype, propschema, value_where, name, namespace, current_ns, prefix,
-	single_attribute, root)
+local function unparse_property(out, v, proptype, propschema, value_where, name, namespace, current_ns, prefix, single_attribute, root)
 
 	if value_where == "in_attribute" then
 		local attr = name
 		if prefix then
-			attr = prefix .. ":" .. name
+			attr = prefix .. ':' .. name
 		elseif namespace and namespace ~= current_ns then
 			attr = namespace .. "\1" .. name
 		end
@@ -280,7 +275,7 @@
 	else
 		local propattr
 		if namespace ~= current_ns then
-			propattr = {xmlns = namespace}
+			propattr = { xmlns = namespace }
 		end
 		if value_where == "in_tag_name" then
 			if proptype == "string" and type(v) == "string" then
@@ -324,7 +319,7 @@
 
 	end
 
-	local out = ctx or st.stanza(current_name, {xmlns = current_ns})
+	local out = ctx or st.stanza(current_name, { xmlns = current_ns })
 
 	local s_type = guess_schema_type(schema)
 	if s_type == "object" then
@@ -350,4 +345,4 @@
 	end
 end
 
-return {parse = parse; unparse = unparse}
+return { parse = parse; unparse = unparse }
--- a/util/jsonpointer.lua	Wed Jun 18 15:02:02 2025 +0200
+++ b/util/jsonpointer.lua	Wed Jun 18 15:02:23 2025 +0200
@@ -1,5 +1,4 @@
-local m_type = math.type;
-
+-- This file is generated from teal-src/prosody/util/jsonpointer.tl
 local function unescape_token(escaped_token)
 	local unescaped = escaped_token:gsub("~1", "/"):gsub("~0", "~")
 	return unescaped
@@ -17,10 +16,10 @@
 
 		if type(idx) == "string" then
 			new_ref = ref[token]
-		elseif m_type(idx) == "integer" then
+		elseif math.type(idx) == "integer" then
 			local i = tonumber(token)
 			if token == "-" then
-				i = #ref + 1
+				i = #(ref) + 1
 			end
 			new_ref = ref[i + 1]
 		else
--- a/util/jsonschema.lua	Wed Jun 18 15:02:02 2025 +0200
+++ b/util/jsonschema.lua	Wed Jun 18 15:02:23 2025 +0200
@@ -1,14 +1,9 @@
--- This file is generated from teal-src/util/jsonschema.lua
-
+-- This file is generated from teal-src/prosody/util/jsonschema.tl
 if not math.type then
 	require("prosody.util.mathcompat")
 end
 
-local utf8_enc = rawget(_G, "utf8") or require("prosody.util.encodings").utf8;
-local utf8_len = utf8_enc.len or function(s)
-	local _, count = s:gsub("[%z\001-\127\194-\253][\128-\191]*", "");
-	return count
-end;
+local utf8_len = rawget(_G, "utf8") and utf8.len or require("prosody.util.encodings").utf8.length;
 
 local json = require("prosody.util.json")
 local null = json.null;