mirror of
https://github.com/SteamDeckHomebrew/decky-loader.git
synced 2026-06-17 00:37:49 +00:00
implement fetch and external resource request apis
This commit is contained in:
@@ -42,7 +42,7 @@ const FilePicker = lazy(() => import('./components/modals/filepicker'));
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
__DECKY_SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED_deckyPluginBackendAPIInit?: {
|
||||
__DECKY_SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED_deckyLoaderAPIInit?: {
|
||||
connect: (version: number, key: string) => any; // Returns the backend API used above, no real point adding types to this.
|
||||
};
|
||||
}
|
||||
@@ -51,6 +51,10 @@ declare global {
|
||||
/** Map of event names to event listeners */
|
||||
type listenerMap = Map<string, Set<(...args: any) => any>>;
|
||||
|
||||
interface DeckyRequestInit extends RequestInit {
|
||||
excludedHeaders: string[];
|
||||
}
|
||||
|
||||
const callPluginMethod = DeckyBackend.callable<[pluginName: string, method: string, ...args: any], any>(
|
||||
'loader/call_plugin_method',
|
||||
);
|
||||
@@ -357,7 +361,7 @@ class PluginLoader extends Logger {
|
||||
let res = await fetch(`http://127.0.0.1:1337/plugins/${name}/frontend_bundle`, {
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
Authentication: deckyAuthToken,
|
||||
'X-Decky-Auth': deckyAuthToken,
|
||||
},
|
||||
});
|
||||
if (res.ok) {
|
||||
@@ -484,12 +488,31 @@ class PluginLoader extends Logger {
|
||||
});
|
||||
}
|
||||
|
||||
/* TODO replace with the following flow (or similar) so we can reuse the JS Fetch API
|
||||
frontend --request URL only--> backend (ws method)
|
||||
backend --new temporary backend URL--> frontend (ws response)
|
||||
frontend <--> backend <--> target URL (over http!)
|
||||
*/
|
||||
async fetchNoCors(url: string, request: any = {}) {
|
||||
// Useful for audio/video streams
|
||||
getExternalResourceURL(url: string) {
|
||||
return `http://127.0.0.1:1337/fetch?auth=${deckyAuthToken}&fetch_url=${encodeURIComponent(url)}`;
|
||||
}
|
||||
|
||||
// Same syntax as fetch but only supports the url-based syntax and an object for headers since it's the most common usage pattern
|
||||
fetch(input: string, init?: DeckyRequestInit | undefined): Promise<Response> {
|
||||
const headers: { [name: string]: string } = {
|
||||
...(init?.headers as { [name: string]: string }),
|
||||
'X-Decky-Auth': deckyAuthToken,
|
||||
'X-Decky-Fetch-URL': input,
|
||||
};
|
||||
|
||||
if (init?.excludedHeaders) {
|
||||
headers['X-Decky-Fetch-Excluded-Headers'] = init.excludedHeaders.join(', ');
|
||||
}
|
||||
|
||||
return fetch('http://127.0.0.1:1337/fetch', {
|
||||
...init,
|
||||
credentials: 'include',
|
||||
headers,
|
||||
});
|
||||
}
|
||||
|
||||
async legacyFetchNoCors(url: string, request: any = {}) {
|
||||
let method: string;
|
||||
const req = { headers: {}, ...request, data: request.body };
|
||||
req?.body && delete req.body;
|
||||
@@ -513,10 +536,10 @@ class PluginLoader extends Logger {
|
||||
|
||||
initPluginBackendAPI() {
|
||||
// Things will break *very* badly if plugin code touches this outside of @decky/backend, so lets make that clear.
|
||||
window.__DECKY_SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED_deckyPluginBackendAPIInit = {
|
||||
window.__DECKY_SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED_deckyLoaderAPIInit = {
|
||||
connect: (version: number, pluginName: string) => {
|
||||
if (version <= 0) {
|
||||
throw new Error(`Plugin ${pluginName} requested invalid backend api version ${version}.`);
|
||||
if (version < 1 || version > 1) {
|
||||
throw new Error(`Plugin ${pluginName} requested unsupported backend api version ${version}.`);
|
||||
}
|
||||
|
||||
const eventListeners: listenerMap = new Map();
|
||||
@@ -543,9 +566,22 @@ class PluginLoader extends Logger {
|
||||
set?.delete(listener);
|
||||
}
|
||||
},
|
||||
openFilePicker: this.openFilePicker.bind(this),
|
||||
executeInTab: DeckyBackend.callable<
|
||||
[tab: String, runAsync: Boolean, code: string],
|
||||
{ success: boolean; result: any }
|
||||
>('utilities/execute_in_tab'),
|
||||
fetch: this.fetch.bind(this),
|
||||
getExternalResourceURL: this.getExternalResourceURL.bind(this),
|
||||
injectCssIntoTab: DeckyBackend.callable<[tab: string, style: string], string>(
|
||||
'utilities/inject_css_into_tab',
|
||||
),
|
||||
removeCssFromTab: DeckyBackend.callable<[tab: string, cssId: string]>('utilities/remove_css_from_tab'),
|
||||
routerHook: this.routerHook,
|
||||
toaster: this.toaster,
|
||||
};
|
||||
|
||||
this.debug(`${pluginName} connected to backend API.`);
|
||||
this.debug(`${pluginName} connected to loader API.`);
|
||||
return backendAPI;
|
||||
},
|
||||
};
|
||||
@@ -591,7 +627,7 @@ class PluginLoader extends Logger {
|
||||
args,
|
||||
);
|
||||
},
|
||||
fetchNoCors: this.fetchNoCors,
|
||||
fetchNoCors: this.legacyFetchNoCors,
|
||||
executeInTab: DeckyBackend.callable<
|
||||
[tab: String, runAsync: Boolean, code: string],
|
||||
{ success: boolean; result: any }
|
||||
|
||||
@@ -32,7 +32,7 @@ declare global {
|
||||
backend: {
|
||||
loadPath: 'http://127.0.0.1:1337/locales/{{lng}}.json',
|
||||
customHeaders: {
|
||||
Authentication: deckyAuthToken,
|
||||
'X-Decky-Auth': deckyAuthToken,
|
||||
},
|
||||
requestOptions: {
|
||||
credentials: 'include',
|
||||
|
||||
@@ -30,7 +30,7 @@ interface ReplyMessage {
|
||||
|
||||
interface ErrorMessage {
|
||||
type: MessageType.ERROR;
|
||||
error: { name: string; message: string; traceback: string | null };
|
||||
error: { name: string; error: string; traceback: string | null };
|
||||
id: number;
|
||||
}
|
||||
|
||||
@@ -40,8 +40,8 @@ interface ErrorMessage {
|
||||
export class PyError extends Error {
|
||||
pythonTraceback: string | null;
|
||||
|
||||
constructor(name: string, message: string, traceback: string | null) {
|
||||
super(message);
|
||||
constructor(name: string, error: string, traceback: string | null) {
|
||||
super(error);
|
||||
this.name = `Python ${name}`;
|
||||
if (traceback) {
|
||||
// traceback will always start with `Traceback (most recent call last):`
|
||||
@@ -142,7 +142,7 @@ export class WSRouter extends Logger {
|
||||
|
||||
case MessageType.ERROR:
|
||||
if (this.runningCalls.has(data.id)) {
|
||||
let err = new PyError(data.error.name, data.error.message, data.error.traceback);
|
||||
let err = new PyError(data.error.name, data.error.error, data.error.traceback);
|
||||
this.runningCalls.get(data.id)!.reject(err);
|
||||
this.runningCalls.delete(data.id);
|
||||
this.debug(`Rejected PY call ${data.id} with error`, data.error);
|
||||
|
||||
Reference in New Issue
Block a user