Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/smooth-dryers-occur.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@naverpay/prometheus-core": minor
---

fix(core): pm2 peer 범위 확대 및 optional 전환

PR: [fix(core): pm2 peer 범위 확대 및 optional 전환](https://github.com/NaverPayDev/prometheus/pull/20)
4 changes: 2 additions & 2 deletions packages/core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -316,9 +316,9 @@ interface PM2GetMessageOptions {

## 요구 사항

- Node.js 16.0.0 이상
- Node.js 16.0.0 이상 (PM2 7.x 사용 시 18.0.0 이상)
- TypeScript 4.5 이상
- PM2 사용 시: PM2 5.0.0 이상
- PM2는 선택적(optional) peer dependency이며, PM2 클러스터 모드 사용 시에만 필요합니다 (PM2 5.3.0 이상, 6.x·7.x 포함)

## 라이센스

Expand Down
7 changes: 6 additions & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,12 @@
"vite": "catalog:"
},
"peerDependencies": {
"pm2": ">=5.3.0 <6.0.0"
"pm2": ">=5.3.0"
},
"peerDependenciesMeta": {
"pm2": {
"optional": true

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[issue] optional: true 설정만으로는 pm2 미설치 환경에서의 강제 설치/크래시 문제가 실제로 해소되지 않는 것 같습니다.

  • packages/core/src/pm2/promisify.tsimport pm2 from 'pm2'가 top-level로 있고, src/index.tspm2/promisify.tspm2/messages.ts를 무조건 re-export하고 있습니다.
  • Node 런타임(번들러 tree-shaking이 적용되지 않는 서버 환경)에서는 @naverpay/prometheus-koa(또는 hono/next)를 import만 해도 core의 index.ts를 거쳐 pm2/promisify.ts가 즉시 로드되고, 그 안의 import pm2 from 'pm2'가 실행됩니다.
  • 이 상태에서 pm2가 node_modules에 없으면 peerDependenciesMeta.pm2.optional: true와 무관하게 Cannot find module 'pm2'로 즉시 크래시됩니다. optional: true는 pnpm/npm의 peer warning만 제거해줄 뿐, 런타임 로딩 문제는 그대로인 것 같습니다.

근거: PR 설명에서 목표로 삼은 "pm2 사용 안 하는 프로젝트의 강제 설치 문제 해소"가 peer 범위·optional 플래그 조정만으로는 실제로 달성되지 않고, pm2 미설치 상태로 다른 패키지를 사용하면 여전히 런타임 에러가 발생할 것으로 보입니다.

이점: pm2 관련 모듈(pm2/promisify.ts, pm2/messages.ts)을 동적 import로 전환해 pm2Connector.connect() 호출 시점에만 로드하거나, subpath export로 분리하면 pm2 미사용 프로젝트에서 설치 없이도 나머지 기능을 안전하게 사용할 수 있습니다.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

optional 전환이 이 PR의 주요한 목적이 아니었다면 그냥 넘어가셔도 무방할 것 같아요.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kyungmi 님, 겸사겸사 챙긴거긴 했는데요, 설치할때 말고 import할때도 문제되지 않도록 해주는게 더 좋을 것 같아서 이번에 수정해주었습니다! 한번만 더 확인 부탁드려요!

}
},
"packageManager": "pnpm@10.6.5"
}
41 changes: 33 additions & 8 deletions packages/core/src/pm2/promisify.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,48 @@
import {promisify} from 'node:util'

import pm2 from 'pm2'

import type {PM2RequestPacket} from './types'
import type {PM2ProcessDescription, PM2RequestPacket} from './types'
import type {EventEmitter} from 'node:events'
import type PM2 from 'pm2'

let pm2Module: typeof PM2 | undefined

/**
* Lazily loads the pm2 module so that consumers who don't use PM2-based
* features never trigger `Cannot find module 'pm2'` at import time
* @returns Loaded pm2 module
*/
async function loadPm2(): Promise<typeof PM2> {
if (!pm2Module) {
const imported = await import('pm2')
pm2Module = imported.default
}
return pm2Module
}

/** Promisified PM2 connect function */
const connect = promisify(pm2.connect.bind(pm2))
async function connect(): Promise<void> {
const pm2 = await loadPm2()
return promisify(pm2.connect.bind(pm2))()
}

/** Promisified PM2 disconnect function */
const disconnect = promisify(pm2.disconnect.bind(pm2))
async function disconnect(): Promise<void> {
const pm2 = await loadPm2()
await promisify(pm2.disconnect.bind(pm2))()
}

/** Promisified PM2 list function */
const list = promisify(pm2.list.bind(pm2))
async function list(): Promise<PM2ProcessDescription[]> {
const pm2 = await loadPm2()
return promisify(pm2.list.bind(pm2))()
}

/**
* Promisified PM2 launchBus function
* @returns Promise that resolves to EventEmitter for PM2 bus communication
*/
function launchBus(): Promise<EventEmitter> {
async function launchBus(): Promise<EventEmitter> {
const pm2 = await loadPm2()
return new Promise((resolve, reject) => {
pm2.launchBus((error, bus) => {
if (error) {
Expand All @@ -35,7 +59,8 @@ function launchBus(): Promise<EventEmitter> {
* @param packet - Data packet to send
* @returns Promise that resolves when data is sent
*/
function sendDataToProcessId(pmId: number, packet: PM2RequestPacket): Promise<void> {
async function sendDataToProcessId(pmId: number, packet: PM2RequestPacket): Promise<void> {
const pm2 = await loadPm2()
return new Promise((resolve, reject) => {
pm2.sendDataToProcessId(pmId, packet, (error: Error) => {
if (error) {
Expand Down
14 changes: 12 additions & 2 deletions packages/core/src/pm2/types.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,24 @@
/* eslint-disable @typescript-eslint/no-explicit-any */

import type {ProcessDescription} from 'pm2'
/**
* Minimal subset of pm2's `ProcessDescription` fields actually used by this
* package, redeclared locally so consumers without pm2 installed don't hit
* `Cannot find module 'pm2'` when resolving this package's type declarations
*/
export interface PM2ProcessDescription {
/** Process name registered in PM2 */
name?: string
/** PM2-assigned process ID */
pm_id?: number
}

/** Handler function type for PM2 messages */
export type PM2MessageHandler<T = any, R = any> = (data: T) => Promise<R> | R

/** Configuration options for getting PM2 messages */
export interface PM2GetMessageOptions {
/** Filter function to select which processes to query */
filter?: (process: ProcessDescription) => boolean
filter?: (process: PM2ProcessDescription) => boolean
/** Whether to include self if not managed by PM2 */
includeSelfIfUnmanaged?: boolean
/** Timeout in milliseconds for message collection */
Expand Down
Loading
Loading