From 721cd6c8db830760f77cce7294b6ac3d3cc731fc Mon Sep 17 00:00:00 2001 From: Douglas Gubert Date: Thu, 16 Apr 2026 13:12:47 -0300 Subject: [PATCH 1/3] feat(apps-engine): export ENGINE_VERSION from definition layer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds `src/definition/version.ts` which reads the package version via `resolveJsonModule` and exports it as `ENGINE_VERSION`. Replaces `AppPackageParser.getEngineVersion()` — which resolved the version by traversing the filesystem relative to `__dirname` — with a direct import of `ENGINE_VERSION`. This removes the assumption that `package.json` lives at a predictable relative path, which will break when `AppPackageParser` moves to a different package during the apps-engine split. Co-Authored-By: Claude Sonnet 4.6 fix(apps-engine): fix ENGINE_VERSION runtime path in compiled output Static `import { version } from '../../package.json'` resolves correctly during TypeScript compilation (source lives at src/definition/) but the emitted require('../../package.json') exits the package root at runtime once compiled to definition/version.js (outDir='.', rootDir='./src'). Switching to require('../package.json') — which is the correct path relative to the compiled output — and bypassing TypeScript's compile-time module resolution avoids the path mismatch entirely. Co-Authored-By: Claude Sonnet 4.6 --- .../apps-engine/src/definition/version.ts | 11 ++++++++ .../src/server/compiler/AppPackageParser.ts | 27 +++---------------- packages/apps-engine/tsconfig.json | 1 + 3 files changed, 15 insertions(+), 24 deletions(-) create mode 100644 packages/apps-engine/src/definition/version.ts diff --git a/packages/apps-engine/src/definition/version.ts b/packages/apps-engine/src/definition/version.ts new file mode 100644 index 0000000000000..2c9883d2ba725 --- /dev/null +++ b/packages/apps-engine/src/definition/version.ts @@ -0,0 +1,11 @@ +/** + * The version of the Apps-Engine package. + * Consumed by host-side code (e.g. AppPackageParser) to validate app compatibility + * without relying on filesystem path traversal. + * + * Uses require() instead of a static import so TypeScript does not resolve the path + * at compile time. The compiled output lands at definition/version.js, so + * '../package.json' correctly points to the package root at runtime. + */ +// eslint-disable-next-line @typescript-eslint/no-require-imports +export const ENGINE_VERSION: string = (require('../package.json') as { version: string }).version; diff --git a/packages/apps-engine/src/server/compiler/AppPackageParser.ts b/packages/apps-engine/src/server/compiler/AppPackageParser.ts index 316f37079e75e..9d98cc0356310 100644 --- a/packages/apps-engine/src/server/compiler/AppPackageParser.ts +++ b/packages/apps-engine/src/server/compiler/AppPackageParser.ts @@ -1,4 +1,3 @@ -import * as fs from 'fs'; import * as path from 'path'; import * as AdmZip from 'adm-zip'; @@ -8,18 +7,15 @@ import { v4 as uuidv4 } from 'uuid'; import { AppImplements } from '.'; import type { IParseAppPackageResult } from './IParseAppPackageResult'; import type { IAppInfo } from '../../definition/metadata/IAppInfo'; +import { ENGINE_VERSION } from '../../definition/version'; import { RequiredApiVersionError } from '../errors'; export class AppPackageParser { - public static uuid4Regex = /^[0-9a-fA-f]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/; + public static uuid4Regex = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/; private allowedIconExts: Array = ['.png', '.jpg', '.jpeg', '.gif']; - private appsEngineVersion: string; - - constructor() { - this.appsEngineVersion = this.getEngineVersion(); - } + private appsEngineVersion: string = ENGINE_VERSION; public async unpackageApp(appPackage: Buffer): Promise { const zip = new AdmZip(appPackage); @@ -143,21 +139,4 @@ export class AppPackageParser { return `data:image/${ext.replace('.', '')};base64,${base64}`; } - - private getEngineVersion(): string { - const devLocation = path.join(__dirname, '../../../package.json'); - const prodLocation = path.join(__dirname, '../../package.json'); - - let info: { version: string }; - - if (fs.existsSync(devLocation)) { - info = JSON.parse(fs.readFileSync(devLocation, 'utf8')); - } else if (fs.existsSync(prodLocation)) { - info = JSON.parse(fs.readFileSync(prodLocation, 'utf8')); - } else { - throw new Error('Could not find the Apps TypeScript Definition Package Version!'); - } - - return info.version.replace(/^[^0-9]/, '').split('-')[0]; - } } diff --git a/packages/apps-engine/tsconfig.json b/packages/apps-engine/tsconfig.json index fe9e53b01cb85..2d70ec6e00a34 100644 --- a/packages/apps-engine/tsconfig.json +++ b/packages/apps-engine/tsconfig.json @@ -10,6 +10,7 @@ "emitDecoratorMetadata": true, "experimentalDecorators": true, "moduleResolution": "node", + "resolveJsonModule": true, "types": ["node"], "lib": ["es2017", "dom"], "rootDir": "./src", From 6f5aed5d30c313d7e8d22916df3171bf8db5abee Mon Sep 17 00:00:00 2001 From: Douglas Gubert Date: Wed, 22 Apr 2026 15:45:22 -0300 Subject: [PATCH 2/3] fix: path detection for version.ts --- packages/apps-engine/src/definition/version.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/apps-engine/src/definition/version.ts b/packages/apps-engine/src/definition/version.ts index 2c9883d2ba725..8feda95c5ad5d 100644 --- a/packages/apps-engine/src/definition/version.ts +++ b/packages/apps-engine/src/definition/version.ts @@ -4,8 +4,13 @@ * without relying on filesystem path traversal. * * Uses require() instead of a static import so TypeScript does not resolve the path - * at compile time. The compiled output lands at definition/version.js, so - * '../package.json' correctly points to the package root at runtime. + * at compile time. + * + * When running for tests, using ts-node, package.json is located two levels above the current file. + * When running in production, package.json is located one level above the compiled version of this file. */ -// eslint-disable-next-line @typescript-eslint/no-require-imports -export const ENGINE_VERSION: string = (require('../package.json') as { version: string }).version; +const runningFromSource = __dirname.endsWith('src/definition'); +const requirePath = runningFromSource ? '../../package.json' : '../package.json'; + +// eslint-disable-next-line import/no-dynamic-require, @typescript-eslint/no-require-imports -- Paths are bounded, we just need to decide which package.json file to target +export const ENGINE_VERSION: string = require(requirePath).version; From 048d05bbb0d457660b4ad03fd4be192c8f85d04c Mon Sep 17 00:00:00 2001 From: Douglas Gubert Date: Mon, 4 May 2026 16:49:00 -0300 Subject: [PATCH 3/3] docs: apps-engine-migration.md --- docs/apps-engine-migration.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/apps-engine-migration.md b/docs/apps-engine-migration.md index 309d4a64e3bf6..be700f394833d 100644 --- a/docs/apps-engine-migration.md +++ b/docs/apps-engine-migration.md @@ -23,3 +23,4 @@ Besides that, migrating app management code away from a public package enables u To make this migration easier to understand and review, we're using a stacked PR approach on Github - similar to a feature branch but disallowing sibling PRs. They are: - [40395](https://github.com/RocketChat/Rocket.Chat/pull/40395) The feature branch itself. It will accumulate the changes of the whole stack. +- [40183](https://github.com/RocketChat/Rocket.Chat/pull/40183) Replaces `AppPackageParser.getEngineVersion()` - which resolved the version by traversing the filesystem relative to `__dirname` - with a direct import of `ENGINE_VERSION`. This will support the migration of the `AppPackageParser` class itself.