From dcf7b47113f0e5f724646748df030738caf51032 Mon Sep 17 00:00:00 2001 From: "Leon.Schmidt" Date: Wed, 19 Aug 2026 01:27:38 +0200 Subject: [PATCH] FIX - Add detailed custom-app debug logging Enable verbose tracing for custom apps and wire diagnostics through the stack. Sets Config.CustomApps.Debug = true, adds a debug_custom_app logger exposed as SkyPhoneApps.Debug, and instruments client and shared compatibility code (registration, normalization, provider exports, catalog sync, lifecycle hooks, resource stop, and app add/update/remove) with structured debug messages. Frontend sends a debug flag with the catalog and will call back to NUI (custom-app:catalog-debug) when debug is enabled. Tests updated to enable debug and assert fixBlur preservation and NUI catalog ACK handling. This provides detailed traces for exports, registration, catalog sync and lifecycle events. --- frontend/src/App.vue | 12 + sky_phone/config/config.lua | 1 + sky_phone/source/client/custom_app_compat.lua | 65 ++++ sky_phone/source/client/custom_apps.lua | 298 +++++++++++++++++- sky_phone/source/shared/custom_app_compat.lua | 30 +- tests/custom_app_compat.lua | 2 + tests/custom_app_compat_client.lua | 19 +- 7 files changed, 410 insertions(+), 17 deletions(-) diff --git a/frontend/src/App.vue b/frontend/src/App.vue index b5433a6..dc8cd20 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -650,6 +650,18 @@ function onMessage(event: MessageEvent): void { if (event.data?.type === 'custom-apps:catalog') { appCatalog.replaceCatalog(event.data.data) + const catalogPayload = event.data.data as + | { apps?: unknown; debug?: unknown } + | undefined + if (catalogPayload?.debug === true) { + void nuiCall('custom-app:catalog-debug', { + acceptedCount: appCatalog.externalApps.length, + acceptedIds: appCatalog.externalApps.map((app) => app.id), + receivedCount: Array.isArray(catalogPayload.apps) + ? catalogPayload.apps.length + : -1, + }) + } const activeAppId = route.params.appId if (typeof activeAppId === 'string' && !isPhoneAppId(activeAppId)) { void router.push('/') diff --git a/sky_phone/config/config.lua b/sky_phone/config/config.lua index abdd5f0..a6e1175 100644 --- a/sky_phone/config/config.lua +++ b/sky_phone/config/config.lua @@ -44,6 +44,7 @@ Config.CustomApps = { Enabled = true, BundledApps = true, ExternalApps = true, + Debug = true, -- detailed client traces for exports, registration, catalog sync and lifecycle events ReadyTimeoutMs = 8000, MaximumMessageBytes = 65536, MaximumStorageBytesPerApp = 262144, diff --git a/sky_phone/source/client/custom_app_compat.lua b/sky_phone/source/client/custom_app_compat.lua index 80ff2be..e605634 100644 --- a/sky_phone/source/client/custom_app_compat.lua +++ b/sky_phone/source/client/custom_app_compat.lua @@ -1,6 +1,7 @@ local RESOURCE_NAME = GetCurrentResourceName() local providers = SkyPhoneCompatibility.Providers local core = SkyPhoneApps.CompatibilityCore +local debug_custom_app = SkyPhoneApps.Debug or function() end local provider_apps = {} local high_client_apps = {} local high_server_apps = {} @@ -33,7 +34,24 @@ end local function register_provider_app(provider, owner_resource, definition, vendor_data) local app_id = definition.id local existing = provider_apps[app_id] + debug_custom_app( + "provider", + "registration requested provider=%s id=%s owner=%s operation=%s", + tostring(provider), + tostring(app_id), + tostring(owner_resource), + existing and "update" or "add" + ) if existing and (existing.owner_resource ~= owner_resource or existing.provider ~= provider) then + debug_custom_app( + "provider", + "registration rejected provider=%s id=%s owner=%s existing_provider=%s existing_owner=%s error=duplicate_app_id", + tostring(provider), + tostring(app_id), + tostring(owner_resource), + tostring(existing.provider), + tostring(existing.owner_resource) + ) return false, "duplicate_app_id" end @@ -51,6 +69,15 @@ local function register_provider_app(provider, owner_resource, definition, vendo vendor_data = vendor_data, } end + debug_custom_app( + "provider", + "registration result provider=%s id=%s owner=%s success=%s error=%s", + tostring(provider), + tostring(app_id), + tostring(owner_resource), + tostring(success), + tostring(error_message) + ) return success, error_message end @@ -82,6 +109,14 @@ local function remove_provider_app(owner_resource, app_id, allowed_providers) end function SkyPhoneApps.RegisterCompatibilityExport(owner_resource, app_data) + debug_custom_app( + "provider", + "compatibility export received owner=%s identifier=%s key=%s data_type=%s", + tostring(owner_resource), + type(app_data) == "table" and tostring(app_data.identifier) or "nil", + type(app_data) == "table" and tostring(app_data.key) or "nil", + type(app_data) + ) local provider local definition local definition_error @@ -92,10 +127,22 @@ function SkyPhoneApps.RegisterCompatibilityExport(owner_resource, app_data) provider = providers.yseries definition, definition_error = SkyPhoneCompatibility.BuildYSeriesDefinition(app_data) else + debug_custom_app( + "provider", + "compatibility export rejected owner=%s error=unknown_app_provider", + tostring(owner_resource) + ) return false, "unknown_app_provider" end if not definition then + debug_custom_app( + "provider", + "definition rejected provider=%s owner=%s error=%s", + tostring(provider), + tostring(owner_resource), + tostring(definition_error) + ) Bridge.Debug("warn", "[%s] %s registration rejected for %s: %s.", RESOURCE_NAME, provider, @@ -104,6 +151,17 @@ function SkyPhoneApps.RegisterCompatibilityExport(owner_resource, app_data) ) return false, definition_error end + debug_custom_app( + "provider", + "definition built provider=%s id=%s owner=%s ui=%s icon=%s default_installed=%s fix_blur=%s", + tostring(provider), + tostring(definition.id), + tostring(owner_resource), + tostring(definition.ui), + tostring(definition.icon), + tostring(definition.defaultInstalled), + definition.compatibility and tostring(definition.compatibility.fixBlur) or "nil" + ) return register_provider_app(provider, owner_resource, definition, copy_record_data(app_data)) end @@ -666,6 +724,7 @@ SkyPhoneCompatibility.RegisterExportAlias("yseries", "GetDataLoaded", function() end) CreateThread(function() + debug_custom_app("provider", "requesting provider snapshots and emitting compatibility ready signals") TriggerServerEvent("sky_phone:compat:high:server:requestSnapshot") TriggerEvent("17mov_Phone:Client:Ready") @@ -677,6 +736,12 @@ CreateThread(function() "yseries", } for index = 1, #provider_resources do + debug_custom_app( + "provider", + "emitting onResourceStart compatibility signal for %s (state=%s)", + provider_resources[index], + tostring(GetResourceState(provider_resources[index])) + ) TriggerEvent("onResourceStart", provider_resources[index]) end end) diff --git a/sky_phone/source/client/custom_apps.lua b/sky_phone/source/client/custom_apps.lua index 86d5b57..91f4a4e 100644 --- a/sky_phone/source/client/custom_apps.lua +++ b/sky_phone/source/client/custom_apps.lua @@ -28,6 +28,21 @@ local phone_open = false local queued_messages = {} local default_icon_url = ("https://cfx-nui-%s/img/custom-app.svg"):format(current_resource) +local function debug_custom_app(area, message, ...) + if Config.CustomApps.Debug ~= true then + return + end + + local formatted = select("#", ...) > 0 and message:format(...) or message + Bridge.Debug( + "debug", + ("[sky_phone][custom-apps][%s] %s"):format(area, formatted), + { notice = true } + ) +end + +SkyPhoneApps.Debug = debug_custom_app + local function trim(value) return value:match("^%s*(.-)%s*$") end @@ -300,7 +315,7 @@ local function validate_optional_text(value, error_code, maximum_length) return normalized end -local function normalize_external_definition(owner_resource, adapter_resource, definition) +local function normalize_external_definition_value(owner_resource, adapter_resource, definition) if type(definition) ~= "table" then return nil, "invalid_definition" end @@ -476,6 +491,43 @@ local function normalize_external_definition(owner_resource, adapter_resource, d } end +local function normalize_external_definition(owner_resource, adapter_resource, definition) + local requested_id = type(definition) == "table" and definition.id or "" + debug_custom_app( + "normalize", + "begin id=%s owner=%s adapter=%s definition_type=%s", + tostring(requested_id), + tostring(owner_resource), + tostring(adapter_resource), + type(definition) + ) + + local app, definition_error = normalize_external_definition_value(owner_resource, adapter_resource, definition) + if not app then + debug_custom_app( + "normalize", + "rejected id=%s owner=%s error=%s", + tostring(requested_id), + tostring(owner_resource), + tostring(definition_error) + ) + return nil, definition_error + end + + debug_custom_app( + "normalize", + "accepted id=%s owner=%s provider=%s ui=%s icon=%s default_installed=%s removable=%s", + app.catalog.id, + app.ownerResource, + app.catalog.compatibility and tostring(app.catalog.compatibility.provider) or "native", + app.catalog.ui, + app.catalog.icon, + tostring(app.catalog.defaultInstalled), + tostring(app.catalog.removable) + ) + return app +end + local function normalize_bundled_manifest(manifest) local resource_base_path = ("custom_apps/%s/"):format(manifest.folder) local base_url = ("https://cfx-nui-%s/%s"):format(current_resource, resource_base_path) @@ -539,17 +591,37 @@ local function get_catalog() end local function send_catalog() + local catalog = get_catalog() + local entries = {} + for index = 1, #catalog do + local app = catalog[index] + entries[index] = ("%s(owner=%s,default=%s)"):format( + app.id, + app.ownerResource, + tostring(app.defaultInstalled) + ) + end + debug_custom_app( + "catalog", + "sending %s app(s) to NUI: %s", + #catalog, + #entries > 0 and table.concat(entries, ", ") or "" + ) SendNUIMessage({ type = "custom-apps:catalog", data = { - apps = get_catalog(), + apps = catalog, + debug = Config.CustomApps.Debug == true, }, }) end local function sync_catalog_if_open() if phone_open then + debug_custom_app("catalog", "phone is open; synchronizing catalog") send_catalog() + else + debug_custom_app("catalog", "phone is closed; catalog sync deferred") end end @@ -578,6 +650,13 @@ local function register_app(app) local app_id = app.catalog.id local existing = apps_by_id[app_id] if existing then + debug_custom_app( + "register", + "duplicate id=%s requested_owner=%s existing_owner=%s", + app_id, + app.ownerResource, + existing.ownerResource + ) Bridge.Debug( "warn", "[sky_phone] Rejected duplicate custom app '%s' from '%s'; it is already owned by '%s'.", @@ -593,6 +672,16 @@ local function register_app(app) if app.adapterResource then add_owner_index(app_ids_by_adapter, app.adapterResource, app_id) end + debug_custom_app( + "register", + "success id=%s owner=%s adapter=%s provider=%s default_installed=%s phone_open=%s", + app_id, + app.ownerResource, + tostring(app.adapterResource), + app.catalog.compatibility and tostring(app.catalog.compatibility.provider) or "native", + tostring(app.catalog.defaultInstalled), + tostring(phone_open) + ) sync_catalog_if_open() return true end @@ -661,6 +750,7 @@ end local function remove_registered_app(app_id, invoke_close_hook, synchronize) local app = apps_by_id[app_id] if not app then + debug_custom_app("remove", "rejected id=%s error=app_not_found", tostring(app_id)) return false, "app_not_found" end @@ -680,20 +770,41 @@ local function remove_registered_app(app_id, invoke_close_hook, synchronize) if synchronize then sync_catalog_if_open() end + debug_custom_app( + "remove", + "success id=%s owner=%s synchronize=%s", + app_id, + app.ownerResource, + tostring(synchronize) + ) return true end local function get_direct_owner() local owner_resource = GetInvokingResource() if not owner_resource then + debug_custom_app("owner", "rejected export call: GetInvokingResource returned nil") return nil, "missing_invoking_resource" end local valid_owner, owner_error = validate_owner_resource(owner_resource, false) if not valid_owner then + debug_custom_app( + "owner", + "rejected invoking_resource=%s state=%s error=%s", + owner_resource, + tostring(GetResourceState(owner_resource)), + tostring(owner_error) + ) return nil, owner_error end + debug_custom_app( + "owner", + "accepted invoking_resource=%s state=%s", + owner_resource, + tostring(GetResourceState(owner_resource)) + ) return owner_resource end @@ -772,21 +883,53 @@ local function add_custom_app_from_adapter(owner_resource, definition) end local function add_compatibility_app(owner_resource, definition) + local app_id = type(definition) == "table" and definition.id or "" + debug_custom_app( + "compat-core", + "add requested id=%s owner=%s", + tostring(app_id), + tostring(owner_resource) + ) if not Config.CustomApps.Enabled or not Config.CustomApps.ExternalApps then + debug_custom_app("compat-core", "add rejected id=%s error=external_apps_disabled", tostring(app_id)) return false, "external_apps_disabled" end local valid_owner, owner_error = validate_owner_resource(owner_resource, false) if not valid_owner then + debug_custom_app( + "compat-core", + "add rejected id=%s owner=%s state=%s error=%s", + tostring(app_id), + tostring(owner_resource), + tostring(GetResourceState(owner_resource)), + tostring(owner_error) + ) return false, owner_error end local app, definition_error = normalize_external_definition(owner_resource, nil, definition) if not app then + debug_custom_app( + "compat-core", + "add rejected id=%s owner=%s error=%s", + tostring(app_id), + tostring(owner_resource), + tostring(definition_error) + ) return false, definition_error end - return register_app(app) + local success, register_error = register_app(app) + debug_custom_app( + "compat-core", + "add result id=%s owner=%s success=%s error=%s", + tostring(app_id), + tostring(owner_resource), + tostring(success), + tostring(register_error) + ) + return success, register_error end local function replace_registered_app(app, adapter_resource) @@ -806,6 +949,14 @@ local function replace_registered_app(app, adapter_resource) end apps_by_id[app_id] = app + debug_custom_app( + "update", + "success id=%s owner=%s adapter=%s default_installed=%s", + app_id, + app.ownerResource, + tostring(adapter_resource), + tostring(app.catalog.defaultInstalled) + ) sync_catalog_if_open() return true end @@ -849,6 +1000,13 @@ local function update_custom_app_from_adapter(owner_resource, definition) end local function update_compatibility_app(owner_resource, definition) + local app_id = type(definition) == "table" and definition.id or "" + debug_custom_app( + "compat-core", + "update requested id=%s owner=%s", + tostring(app_id), + tostring(owner_resource) + ) if not Config.CustomApps.Enabled or not Config.CustomApps.ExternalApps then return false, "external_apps_disabled" end @@ -860,9 +1018,25 @@ local function update_compatibility_app(owner_resource, definition) local app, definition_error = normalize_external_definition(owner_resource, nil, definition) if not app then + debug_custom_app( + "compat-core", + "update rejected id=%s owner=%s error=%s", + tostring(app_id), + tostring(owner_resource), + tostring(definition_error) + ) return false, definition_error end - return replace_registered_app(app, nil) + local success, update_error = replace_registered_app(app, nil) + debug_custom_app( + "compat-core", + "update result id=%s owner=%s success=%s error=%s", + tostring(app_id), + tostring(owner_resource), + tostring(success), + tostring(update_error) + ) + return success, update_error end local function remove_custom_app(app_id) @@ -1252,6 +1426,13 @@ end local function set_phone_open(is_open) phone_open = is_open + debug_custom_app( + "phone", + "state changed open=%s active_app=%s registered_apps=%s", + tostring(phone_open), + tostring(active_app_id), + #get_catalog() + ) if phone_open then send_catalog() return @@ -1307,6 +1488,15 @@ if Config.CustomApps.Enabled and Config.CustomApps.BundledApps then end local function add_custom_app_export(definition) + debug_custom_app( + "dispatch", + "AddCustomApp received type=%s id=%s identifier=%s key=%s invoking_resource=%s", + type(definition), + type(definition) == "table" and tostring(definition.id) or "nil", + type(definition) == "table" and tostring(definition.identifier) or "nil", + type(definition) == "table" and tostring(definition.key) or "nil", + tostring(GetInvokingResource()) + ) if type(definition) ~= "table" then return add_custom_app(definition) end @@ -1322,19 +1512,32 @@ local function add_custom_app_export(definition) provider_markers = provider_markers + 1 end if provider_markers > 1 then + debug_custom_app("dispatch", "AddCustomApp rejected error=ambiguous_app_provider") return false, "ambiguous_app_provider" end if definition.identifier == nil and definition.key == nil then + debug_custom_app("dispatch", "routing AddCustomApp through native sky_phone registration") return add_custom_app(definition) end local owner_resource, owner_error = get_direct_owner() if not owner_resource then + debug_custom_app( + "dispatch", + "compatibility AddCustomApp rejected error=%s", + tostring(owner_error) + ) return false, owner_error end if not SkyPhoneApps.RegisterCompatibilityExport then + debug_custom_app("dispatch", "compatibility AddCustomApp rejected error=compatibility_not_ready") return false, "compatibility_not_ready" end + debug_custom_app( + "dispatch", + "routing AddCustomApp through compatibility provider owner=%s", + owner_resource + ) return SkyPhoneApps.RegisterCompatibilityExport(owner_resource, definition) end @@ -1371,15 +1574,66 @@ SkyPhoneCompatibility.RegisterExportAlias("lb-phone", "SendCustomAppMessage", se SkyPhoneCompatibility.RegisterExportAlias("yseries", "AddCustomApp", add_custom_app_export) SkyPhoneCompatibility.RegisterExportAlias("yseries", "RemoveCustomApp", remove_custom_app) +RegisterNUICallback("custom-app:catalog-debug", function(data, cb) + local accepted_ids = type(data) == "table" and data.acceptedIds or nil + local ids = {} + if type(accepted_ids) == "table" then + for index = 1, #accepted_ids do + if type(accepted_ids[index]) == "string" then + ids[#ids + 1] = accepted_ids[index] + end + end + end + + debug_custom_app( + "nui", + "catalog applied received=%s accepted=%s ids=%s", + type(data) == "table" and tostring(data.receivedCount) or "invalid", + type(data) == "table" and tostring(data.acceptedCount) or "invalid", + #ids > 0 and table.concat(ids, ", ") or "" + ) + cb({ success = true }) +end) + RegisterNUICallback("custom-app:lifecycle", function(data, cb) + local requested_app_id = type(data) == "table" and data.appId or nil + local requested_event = type(data) == "table" and data.event or nil + local function respond(success, error_message) + debug_custom_app( + "lifecycle", + "result app=%s event=%s success=%s error=%s active_app=%s ready=%s phone_open=%s", + tostring(requested_app_id), + tostring(requested_event), + tostring(success), + tostring(error_message), + tostring(active_app_id), + tostring(active_app_ready), + tostring(phone_open) + ) + local response = { success = success } + if error_message then + response.error = error_message + end + cb(response) + end + + debug_custom_app( + "lifecycle", + "request app=%s event=%s phone_open=%s active_app=%s ready=%s", + tostring(requested_app_id), + tostring(requested_event), + tostring(phone_open), + tostring(active_app_id), + tostring(active_app_ready) + ) if type(data) ~= "table" then - cb({ success = false, error = "invalid_lifecycle_request" }) + respond(false, "invalid_lifecycle_request") return end local valid_id, id_error = SkyPhoneApps.ValidateAppId(data.appId) if not valid_id then - cb({ success = false, error = id_error }) + respond(false, id_error) return end @@ -1389,7 +1643,7 @@ RegisterNUICallback("custom-app:lifecycle", function(data, cb) and lifecycle_event ~= "ready" and lifecycle_event ~= "close" then - cb({ success = false, error = "invalid_lifecycle_event" }) + respond(false, "invalid_lifecycle_event") return end @@ -1397,15 +1651,15 @@ RegisterNUICallback("custom-app:lifecycle", function(data, cb) if lifecycle_event == "close" and (not phone_open or not app or active_app_id ~= data.appId) then - cb({ success = true }) + respond(true) return end if not phone_open then - cb({ success = false, error = "phone_closed" }) + respond(false, "phone_closed") return end if not app then - cb({ success = false, error = "app_not_found" }) + respond(false, "app_not_found") return end @@ -1414,7 +1668,7 @@ RegisterNUICallback("custom-app:lifecycle", function(data, cb) Config.CustomApps.MaximumMessageBytes ) if payload_error then - cb({ success = false, error = payload_error }) + respond(false, payload_error) return end @@ -1431,11 +1685,11 @@ RegisterNUICallback("custom-app:lifecycle", function(data, cb) queued_messages[data.appId] = {} hook_success = invoke_or_defer_hook(app, "onOpen", lifecycle_payload, deferred_hooks) elseif active_app_id ~= data.appId then - cb({ success = false, error = "app_not_active" }) + respond(false, "app_not_active") return elseif lifecycle_event == "ready" then if active_app_ready then - cb({ success = true }) + respond(true) return end active_app_ready = true @@ -1451,10 +1705,10 @@ RegisterNUICallback("custom-app:lifecycle", function(data, cb) end if not hook_success then - cb({ success = false, error = "hook_failed" }) + respond(false, "hook_failed") return end - cb({ success = true }) + respond(true) invoke_deferred_hooks(deferred_hooks) end) @@ -1463,6 +1717,14 @@ AddEventHandler("onClientResourceStop", function(resource_name) return end + debug_custom_app( + "resource", + "stop detected resource=%s owner_apps=%s adapter_apps=%s", + tostring(resource_name), + tostring(app_ids_by_owner[resource_name] ~= nil), + tostring(app_ids_by_adapter[resource_name] ~= nil) + ) + local app_ids = {} local owner_apps = app_ids_by_owner[resource_name] if owner_apps then @@ -1486,4 +1748,10 @@ AddEventHandler("onClientResourceStop", function(resource_name) if removed then sync_catalog_if_open() end + debug_custom_app( + "resource", + "stop cleanup resource=%s removed_any=%s", + tostring(resource_name), + tostring(removed) + ) end) diff --git a/sky_phone/source/shared/custom_app_compat.lua b/sky_phone/source/shared/custom_app_compat.lua index 514ddff..ec12d0d 100644 --- a/sky_phone/source/shared/custom_app_compat.lua +++ b/sky_phone/source/shared/custom_app_compat.lua @@ -6,7 +6,34 @@ function SkyPhoneCompatibility.RegisterExportAlias(resource_name, export_name, h assert(type(handler) == "function", "Export alias handler must be a function") AddEventHandler(("__cfx_export_%s_%s"):format(resource_name, export_name), function(set_callback) - set_callback(handler) + set_callback(function(...) + local debug_custom_app = SkyPhoneApps and SkyPhoneApps.Debug + local invoking_resource = GetInvokingResource and GetInvokingResource() or nil + if debug_custom_app then + debug_custom_app( + "export", + "call provider=%s export=%s invoking_resource=%s argument_count=%s", + resource_name, + export_name, + tostring(invoking_resource), + select("#", ...) + ) + end + + local results = table.pack(handler(...)) + if debug_custom_app then + debug_custom_app( + "export", + "result provider=%s export=%s invoking_resource=%s success=%s error=%s", + resource_name, + export_name, + tostring(invoking_resource), + tostring(results[1]), + tostring(results[2]) + ) + end + return table.unpack(results, 1, results.n) + end) end) end @@ -137,6 +164,7 @@ function SkyPhoneCompatibility.BuildLbDefinition(owner_resource, app_data) compatibility = { provider = SkyPhoneCompatibility.Providers.lb, apiVersion = 1, + fixBlur = app_data.fixBlur == true, resourceName = type(app_data.resource) == "string" and app_data.resource or owner_resource, }, } diff --git a/tests/custom_app_compat.lua b/tests/custom_app_compat.lua index 2613ad3..77eda5b 100644 --- a/tests/custom_app_compat.lua +++ b/tests/custom_app_compat.lua @@ -6,6 +6,7 @@ local lb_definition = assert(SkyPhoneCompatibility.BuildLbDefinition("lb_app", { description = "Dispatch terminal", ui = "ui/index.html", defaultApp = true, + fixBlur = true, landscape = true, onUse = function() end, })) @@ -14,6 +15,7 @@ assert(lb_definition.ui == "ui/index.html", "LB relative UI must remain owner-re assert(lb_definition.orientation == "landscape", "LB landscape flag must be preserved") assert(type(lb_definition.onOpen) == "function", "LB onUse must map to the open lifecycle") assert(lb_definition.compatibility.resourceName == "lb_app", "LB callbacks must target the registering resource") +assert(lb_definition.compatibility.fixBlur, "LB fixBlur must be preserved") local bridged_lb_definition = assert(SkyPhoneCompatibility.BuildLbDefinition("phone_adapter", { identifier = "bridged", diff --git a/tests/custom_app_compat_client.lua b/tests/custom_app_compat_client.lua index 2b45a36..7b6d4a4 100644 --- a/tests/custom_app_compat_client.lua +++ b/tests/custom_app_compat_client.lua @@ -12,6 +12,7 @@ Config = { CustomApps = { AllowRemoteOrigins = {}, BundledApps = false, + Debug = true, Enabled = true, ExternalApps = true, MaximumMessageBytes = 65536, @@ -23,17 +24,19 @@ Config = { }, } +local encoded_json_value = nil json = { decode = function(encoded) if encoded == "null" then return nil, 5 end - return {} + return encoded_json_value end, encode = function(value) if value == nil then return "null" end + encoded_json_value = value return "{}" end, } @@ -83,6 +86,7 @@ function CreateThread(handler) handler() end +dofile("sky_phone/source/bridge/shared.lua") dofile("sky_phone/source/shared/custom_apps.lua") dofile("sky_phone/source/shared/custom_app_compat.lua") dofile("sky_phone/source/client/custom_apps.lua") @@ -186,6 +190,8 @@ local lb_definition = { identifier = "dispatch", name = "Dispatch", description = "Dispatch terminal", + defaultApp = false, + fixBlur = true, ui = "ui/index.html", onInstall = make_cfx_function_reference("install-hook", function() install_hook_after_response = lifecycle_response_delivered @@ -214,6 +220,17 @@ for index = 1, #retry_catalog.data.apps do end end assert(retried_app and retried_app.name == "Dispatch Updated", "same-owner retry must publish the updated app") +assert(not retried_app.defaultInstalled, "LB defaultApp=false must keep the app available for App Store installation") +assert(retried_app.compatibility.fixBlur, "LB fixBlur must be preserved for every provider app") +local catalog_debug_response +assert(registered_nui_callbacks["custom-app:catalog-debug"])({ + acceptedCount = 1, + acceptedIds = { "dispatch" }, + receivedCount = 1, +}, function(response) + catalog_debug_response = response +end) +assert(catalog_debug_response.success, "the NUI catalog acknowledgement must be accepted") lifecycle_response_delivered = false lifecycle_callback({ appId = "dispatch", event = "install" }, function(response) lifecycle_response = response