From da72b687f66c61498436904d3a138045c616ac9a Mon Sep 17 00:00:00 2001 From: Daniel Salazar Date: Thu, 5 Mar 2026 16:01:53 -0800 Subject: [PATCH] chore: typeFixes --- extensions/api.d.ts | 14 +++- .../src/ExtensionController.ts | 79 ++++++++++--------- .../metering/controllers/UsageController.ts | 27 +++++-- extensions/serverInfo/index.ts | 9 ++- package-lock.json | 2 +- src/puter-js/package-lock.json | 4 +- src/puter-js/package.json | 2 +- src/puter-js/src/init.cjs | 1 - src/puter-js/src/init.d.cts | 4 + 9 files changed, 89 insertions(+), 53 deletions(-) create mode 100644 src/puter-js/src/init.d.cts diff --git a/extensions/api.d.ts b/extensions/api.d.ts index 20d4bd38de..1052976f0e 100644 --- a/extensions/api.d.ts +++ b/extensions/api.d.ts @@ -1,3 +1,4 @@ + import type APIError from '@heyputer/backend/src/api/APIError.js'; import type query from '@heyputer/backend/src/om/query/query'; import type { Actor } from '@heyputer/backend/src/services/auth/Actor.js'; @@ -32,7 +33,7 @@ declare global { export type { Cluster } from 'ioredis'; -interface EndpointOptions { +export interface EndpointOptions { allowedMethods?: string[]; subdomain?: string; noauth?: boolean; @@ -57,7 +58,16 @@ interface DriverInterface { methods: Record; } -type HttpMethod = 'get' | 'post' | 'put' | 'delete' | 'patch'; +export type HttpMethod = 'get' | 'post' | 'put' | 'delete' | 'patch'; + +export type ExtensionRequestHandler = RequestHandler< + Record, + unknown, + unknown +>; +export type ExtensionRequest = Parameters[0]; +export type ExtensionResponse = Parameters[1]; +export type ExtensionNextFunction = Parameters[2]; export type AddRouteFunction = ( path: string, diff --git a/extensions/extensionController/src/ExtensionController.ts b/extensions/extensionController/src/ExtensionController.ts index 70340d67d8..3f1506b4d1 100644 --- a/extensions/extensionController/src/ExtensionController.ts +++ b/extensions/extensionController/src/ExtensionController.ts @@ -131,43 +131,48 @@ export class ExtensionController { logger.log(`Registering route: [${route.method.toUpperCase()}] ${fullPath}`); (extension[route.method] as RouterMethods[HttpMethod])( - fullPath, - route.options || {}, - async (req, res, next) => { - try { - if ( adminsForRoute || allowedAppIds ) { - if ( ! req.actor ) { - throw new HttpError(StatusCodes.UNAUTHORIZED, 'Unauthenticated'); - } - } - if ( adminsForRoute ) { - if ( ! adminsForRoute.includes(req.actor!.type.user.username) ) { - throw new HttpError(StatusCodes.FORBIDDEN, - 'Only admins may request this resource.'); - } - } - if ( allowedAppIds ) { - if ( ( req.actor!.type?.app?.uid && !allowedAppIds.includes(req.actor!.type.app.uid) ) ) { - throw new HttpError(StatusCodes.FORBIDDEN, - 'This app may not request this resource.'); - } - } - await route.handler.bind(this)(req, res, next); - } catch ( error ) { - if ( error instanceof HttpError ) { - res.status(error.statusCode).send({ error: error.message }); - logger.warn('httpError:', error); - return; - } - if ( error instanceof Error ) { - res.status(StatusCodes.INTERNAL_SERVER_ERROR).send({ error: error.message }); - logger.error('Non-http error:', error); - return; - } - res.status(StatusCodes.INTERNAL_SERVER_ERROR).send({ error: 'An unknown error occurred' }); - logger.error('An unknown error occurred:', error); - } - }); + fullPath, + route.options || {}, + async (req, res, next) => { + try { + if ( adminsForRoute || allowedAppIds ) { + if ( ! req.actor ) { + throw new HttpError(StatusCodes.UNAUTHORIZED, 'Unauthenticated'); + } + } + if ( adminsForRoute ) { + if ( ! adminsForRoute.includes(req.actor!.type.user.username) ) { + throw new HttpError( + StatusCodes.FORBIDDEN, + 'Only admins may request this resource.', + ); + } + } + if ( allowedAppIds ) { + if ( ( req.actor!.type?.app?.uid && !allowedAppIds.includes(req.actor!.type.app.uid) ) ) { + throw new HttpError( + StatusCodes.FORBIDDEN, + 'This app may not request this resource.', + ); + } + } + await route.handler.bind(this)(req, res, next); + } catch ( error ) { + if ( error instanceof HttpError ) { + res.status(error.statusCode).send({ error: error.message }); + logger.warn('httpError:', error); + return; + } + if ( error instanceof Error ) { + res.status(StatusCodes.INTERNAL_SERVER_ERROR).send({ error: error.message }); + logger.error('Non-http error:', error); + return; + } + res.status(StatusCodes.INTERNAL_SERVER_ERROR).send({ error: 'An unknown error occurred' }); + logger.error('An unknown error occurred:', error); + } + }, + ); } } } diff --git a/extensions/metering/controllers/UsageController.ts b/extensions/metering/controllers/UsageController.ts index 93f20270ee..8ab44bb5be 100644 --- a/extensions/metering/controllers/UsageController.ts +++ b/extensions/metering/controllers/UsageController.ts @@ -1,5 +1,10 @@ +/* global extension */ import type { BaseDatabaseAccessService } from '@heyputer/backend/src/services/database/BaseDatabaseAccessService.js'; import type { MeteringService } from '@heyputer/backend/src/services/MeteringService/MeteringService.js'; +import type { + ExtensionRequest, + ExtensionResponse, +} from '../../api.d.ts'; const { Controller, Get, ExtensionController } = extension.import('extensionController'); @@ -18,7 +23,7 @@ export class UsageController extends ExtensionController { } @Get('usage', { subdomain: 'api' }) - async getUsage (req, res) { + async getUsage (req: ExtensionRequest, res: ExtensionResponse) { const actor = req.actor; if ( ! actor ) { throw Error('actor not found in context'); @@ -35,7 +40,7 @@ export class UsageController extends ExtensionController { } @Get('usage/:appIdOrName', { subdomain: 'api' }) - async getUsageByApp (req, res) { + async getUsageByApp (req: ExtensionRequest, res: ExtensionResponse) { const actor = req.actor; if ( ! actor ) { throw Error('actor not found in context'); @@ -45,12 +50,18 @@ export class UsageController extends ExtensionController { res.status(400).json({ error: 'appId parameter is required' }); return; } + if ( typeof appIdOrName !== 'string' ) { + res.status(400).json({ error: 'appId parameter must be a string' }); + return; + } let appId = appIdOrName; if ( !appIdOrName.startsWith('app-') || !/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(appIdOrName.split('app-')[1]) ) { // Check if the part after 'app-' is a valid UUID (v4) - const appRows = await this.#sqlClient.read('SELECT `uid` FROM `apps` WHERE `name` = ? LIMIT 1', - [appIdOrName]); + const appRows = await this.#sqlClient.read( + 'SELECT `uid` FROM `apps` WHERE `name` = ? LIMIT 1', + [appIdOrName], + ); if ( appRows.length > 0 ) { appId = appRows[0].uid; } else { @@ -62,15 +73,17 @@ export class UsageController extends ExtensionController { } const appUsage = - await this.#meteringService.getActorCurrentMonthAppUsageDetails(actor, - appId); + await this.#meteringService.getActorCurrentMonthAppUsageDetails( + actor, + appId, + ); res.status(200).json(appUsage); return; } @Get('globalUsage', { subdomain: 'api' }, extension.config.allowedGlobalUsageUsers || []) - async getGlobalUsage (req, res) { + async getGlobalUsage (req: ExtensionRequest, res: ExtensionResponse) { const actor = req.actor; if ( ! actor ) { throw Error('actor not found in context'); diff --git a/extensions/serverInfo/index.ts b/extensions/serverInfo/index.ts index ab7a017e22..0a2a2c5ae6 100644 --- a/extensions/serverInfo/index.ts +++ b/extensions/serverInfo/index.ts @@ -1,11 +1,16 @@ +/* global config, extension */ import fs from 'fs/promises'; import os from 'os'; +import type { + ExtensionRequest, + ExtensionResponse, +} from '../api.d.ts'; const { Controller, Get, ExtensionController } = extension.import('extensionController'); @Controller('/serverInfo', [...config.allowedUsernames]) class ServerInfoController extends ExtensionController { @Get('', { subdomain: 'api' }) - async getServerInfo (req, res) { + async getServerInfo (_req: ExtensionRequest, res: ExtensionResponse) { const osData = { platform: os.platform(), type: os.type(), @@ -60,4 +65,4 @@ class ServerInfoController extends ExtensionController { } } -(new ServerInfoController()).registerRoutes(); \ No newline at end of file +(new ServerInfoController()).registerRoutes(); diff --git a/package-lock.json b/package-lock.json index dd824cc0a0..1086a14a3b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22508,7 +22508,7 @@ }, "src/puter-js": { "name": "@heyputer/puter.js", - "version": "2.2.10", + "version": "2.2.11", "license": "Apache-2.0", "dependencies": { "@heyputer/kv.js": "^0.2.1", diff --git a/src/puter-js/package-lock.json b/src/puter-js/package-lock.json index 7ca78286c0..19af60ee45 100644 --- a/src/puter-js/package-lock.json +++ b/src/puter-js/package-lock.json @@ -1,12 +1,12 @@ { "name": "puter", - "version": "2.2.10", + "version": "2.2.11", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "puter", - "version": "2.2.10", + "version": "2.2.11", "license": "Apache-2.0", "dependencies": { "@heyputer/kv.js": "^0.1.92", diff --git a/src/puter-js/package.json b/src/puter-js/package.json index 496c61e2f4..60b823efd0 100644 --- a/src/puter-js/package.json +++ b/src/puter-js/package.json @@ -1,6 +1,6 @@ { "name": "@heyputer/puter.js", - "version": "2.2.10", + "version": "2.2.11", "description": "Puter.js - A JavaScript library for interacting with Puter services.", "homepage": "https://developer.puter.com", "main": "src/index.js", diff --git a/src/puter-js/src/init.cjs b/src/puter-js/src/init.cjs index a4f0cb2feb..41c6c8626c 100644 --- a/src/puter-js/src/init.cjs +++ b/src/puter-js/src/init.cjs @@ -1,7 +1,6 @@ const { readFileSync } = require('node:fs'); const vm = require('node:vm'); const { resolve } = require('node:path'); -const { IncomingMessage } = require('node:http'); const open = require('open'); /** * Method for loading puter.js in Node.js environment with auth token diff --git a/src/puter-js/src/init.d.cts b/src/puter-js/src/init.d.cts new file mode 100644 index 0000000000..b6ed80151e --- /dev/null +++ b/src/puter-js/src/init.d.cts @@ -0,0 +1,4 @@ +import type { Puter } from '../types/puter.d.ts'; + +export declare function init(authToken?: string): Puter; +export declare function getAuthToken(guiOrigin?: string): Promise;