jopi-toolkit 3.0.24 → 3.1.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "3.0.24",
2
+ "version": "3.1.22",
3
3
  "name": "jopi-toolkit",
4
4
  "license": "MIT",
5
5
  "homepage": "https://github.com/johanpiquet/jopi-toolkit",
@@ -17,90 +17,102 @@
17
17
  "src"
18
18
  ],
19
19
  "scripts": {
20
- "build:priority:1": "npx tsc",
21
- "clean": "rm -rf ./dist .turbo tsconfig.tsbuildinfo",
22
- "tsc": "tsc",
23
- "bun-publish": "npx tsc && bun publish",
24
- "untrack": "git rm -r --cached -- ./dist/**;",
25
20
  "jopiLink": "npx jopi-mono link-add",
26
- "dev:tsc": "npx tsc --watch"
21
+ "bunLink": "bun link",
22
+ "dev:build": "tsc",
23
+ "dev:tsc-watch": "tsc --watch",
24
+ "dev:tsc-check": "tsc --noEmit"
27
25
  },
28
- "devDependencies": {
26
+ "dependencies": {
29
27
  "@types/adm-zip": "^0.5.7",
30
28
  "@types/bun": "latest",
31
29
  "@types/mime-types": "latest",
32
- "@types/node": "latest"
33
- },
34
- "dependencies": {
30
+ "@types/node": "latest",
35
31
  "@types/ws": "^8.18.1",
36
32
  "adm-zip": "^0.5.9",
37
33
  "mime-types": "^3.0.1",
38
34
  "ws": "^8.18.3"
39
35
  },
40
- "publishedDate": "12/20/2025",
41
- "publicPublishedDate": "12/20/2025",
36
+ "publishedDate": "1/12/2026",
37
+ "publicPublishedDate": "1/12/2026",
42
38
  "exports": {
43
39
  "./jk_schema": {
40
+ "types": "./src/jk_schemas/index.ts",
44
41
  "bun": "./src/jk_schemas/index.ts",
45
42
  "default": "./dist/jk_schemas/index.js"
46
43
  },
47
44
  "./jk_tools": {
45
+ "types": "./src/jk_tools/index.ts",
48
46
  "bun": "./src/jk_tools/index.ts",
49
47
  "default": "./dist/jk_tools/index.js"
50
48
  },
51
49
  "./jk_process": {
50
+ "types": "./src/jk_process/index.ts",
52
51
  "bun": "./src/jk_process/index.ts",
53
52
  "default": "./dist/jk_process/index.js"
54
53
  },
55
54
  "./jk_data": {
55
+ "types": "./src/jk_data/index.ts",
56
56
  "bun": "./src/jk_data/index.ts",
57
57
  "default": "./dist/jk_data/index.js"
58
58
  },
59
59
  "./jk_what": {
60
+ "types": "./src/jk_what/index.ts",
60
61
  "bun": "./src/jk_what/index.ts",
61
62
  "default": "./dist/jk_what/index.js"
62
63
  },
63
64
  "./jk_os": {
65
+ "types": "./src/jk_os/index.ts",
64
66
  "bun": "./src/jk_os/index.ts",
65
67
  "default": "./dist/jk_os/index.js"
66
68
  },
67
69
  "./jk_crypto": {
70
+ "types": "./src/jk_crypto/index.ts",
68
71
  "bun": "./src/jk_crypto/index.ts",
69
72
  "default": "./dist/jk_crypto/index.js"
70
73
  },
71
74
  "./jk_thread": {
75
+ "types": "./src/jk_thread/index.ts",
72
76
  "bun": "./src/jk_thread/index.ts",
73
77
  "default": "./dist/jk_thread/index.js"
74
78
  },
75
79
  "./jk_events": {
80
+ "types": "./src/jk_events/index.ts",
76
81
  "bun": "./src/jk_events/index.ts",
77
82
  "default": "./dist/jk_events/index.js"
78
83
  },
79
84
  "./jk_fs": {
85
+ "types": "./src/jk_fs/index.ts",
80
86
  "bun": "./src/jk_fs/index.ts",
81
87
  "default": "./dist/jk_fs/index.js"
82
88
  },
83
89
  "./jk_timer": {
90
+ "types": "./src/jk_timer/index.ts",
84
91
  "bun": "./src/jk_timer/index.ts",
85
92
  "default": "./dist/jk_timer/index.js"
86
93
  },
87
94
  "./jk_term": {
95
+ "types": "./src/jk_term/index.ts",
88
96
  "bun": "./src/jk_term/index.ts",
89
97
  "default": "./dist/jk_term/index.js"
90
98
  },
91
99
  "./jk_app": {
100
+ "types": "./src/jk_app/index.ts",
92
101
  "bun": "./src/jk_app/index.ts",
93
102
  "default": "./dist/jk_app/index.js"
94
103
  },
95
104
  "./jk_compress": {
105
+ "types": "./src/jk_compress/index.ts",
96
106
  "bun": "./src/jk_compress/index.ts",
97
107
  "default": "./dist/jk_compress/index.js"
98
108
  },
99
109
  "./jk_webSocket": {
110
+ "types": "./src/jk_webSocket/index.ts",
100
111
  "bun": "./src/jk_webSocket/index.ts",
101
112
  "default": "./dist/jk_webSocket/index.js"
102
113
  },
103
114
  "./jk_logs": {
115
+ "types": "./src/jk_logs/index.ts",
104
116
  "bun": "./src/jk_logs/index.ts",
105
117
  "default": "./dist/jk_logs/index.js"
106
118
  }
@@ -258,31 +258,47 @@ var gIsExited = false;
258
258
  var gTempDir;
259
259
  //endregion
260
260
  //region Resolving
261
- export function findNodePackageDir(packageName, useLinuxPathFormat) {
262
- if (useLinuxPathFormat === void 0) { useLinuxPathFormat = true; }
263
- var currentDir = jk_fs.dirname(findPackageJson());
264
- while (true) {
265
- var packagePath = jk_fs.join(currentDir, 'node_modules', packageName);
266
- if (jk_fs.isDirectorySync(packagePath)) {
267
- if (useLinuxPathFormat)
268
- return jk_fs.win32ToLinuxPath(packagePath);
269
- return packagePath;
270
- }
271
- var parentDir = jk_fs.dirname(currentDir);
272
- // Reached root directory
273
- if (parentDir === currentDir) {
274
- break;
275
- }
276
- currentDir = parentDir;
277
- }
278
- return undefined;
261
+ export function findNodePackageDir(packageName_1) {
262
+ return __awaiter(this, arguments, void 0, function (packageName, searchFromDir) {
263
+ var currentDir, packagePath, parentDir;
264
+ if (searchFromDir === void 0) { searchFromDir = getCodeSourceDirHint(); }
265
+ return __generator(this, function (_a) {
266
+ switch (_a.label) {
267
+ case 0:
268
+ currentDir = jk_fs.resolve(searchFromDir);
269
+ _a.label = 1;
270
+ case 1:
271
+ if (!true) return [3 /*break*/, 3];
272
+ packagePath = jk_fs.join(currentDir, 'node_modules', packageName);
273
+ return [4 /*yield*/, jk_fs.isDirectory(packagePath)];
274
+ case 2:
275
+ if (_a.sent())
276
+ return [2 /*return*/, packagePath];
277
+ parentDir = jk_fs.dirname(currentDir);
278
+ if (parentDir === currentDir)
279
+ return [3 /*break*/, 3];
280
+ currentDir = parentDir;
281
+ return [3 /*break*/, 1];
282
+ case 3: return [2 /*return*/, undefined];
283
+ }
284
+ });
285
+ });
279
286
  }
280
- export function requireNodePackageDir(packageName, useLinuxPathFormat) {
281
- if (useLinuxPathFormat === void 0) { useLinuxPathFormat = true; }
282
- var pkgDir = findNodePackageDir(packageName, useLinuxPathFormat);
283
- if (!pkgDir)
284
- throw new Error("Package '" + packageName + "' not found");
285
- return pkgDir;
287
+ export function requireNodePackageDir(packageName_1) {
288
+ return __awaiter(this, arguments, void 0, function (packageName, searchFromDir) {
289
+ var pkgDir;
290
+ if (searchFromDir === void 0) { searchFromDir = getCodeSourceDirHint(); }
291
+ return __generator(this, function (_a) {
292
+ switch (_a.label) {
293
+ case 0: return [4 /*yield*/, findNodePackageDir(packageName, searchFromDir)];
294
+ case 1:
295
+ pkgDir = _a.sent();
296
+ if (!pkgDir)
297
+ throw new Error("Package '" + packageName + "' not found");
298
+ return [2 /*return*/, pkgDir];
299
+ }
300
+ });
301
+ });
286
302
  }
287
303
  export function findPackageJsonDir(searchFromDir) {
288
304
  if (searchFromDir === void 0) { searchFromDir = getCodeSourceDirHint(); }
@@ -164,6 +164,10 @@ export function getTempDir(): string {
164
164
  return gTempDir;
165
165
  }
166
166
 
167
+ export function setTempDir(dir: string) {
168
+ gTempDir = dir;
169
+ }
170
+
167
171
  let gIsExited = false;
168
172
  let gTempDir: string|undefined;
169
173
 
@@ -191,13 +195,27 @@ export async function requireNodePackageDir(packageName: string, searchFromDir =
191
195
  return pkgDir;
192
196
  }
193
197
 
194
- export function findPackageJsonDir(searchFromDir = getCodeSourceDirHint()): string {
198
+ export function findRequiredPackageJsonDir(searchFromDir = getCodeSourceDirHint()): string {
199
+ let pkgJsonPath = findRequiredPackageJson(searchFromDir);
200
+ return jk_fs.dirname(pkgJsonPath);
201
+ }
202
+
203
+ export function findPackageJsonDir(searchFromDir = getCodeSourceDirHint()): string|undefined {
195
204
  let pkgJsonPath = findPackageJson(searchFromDir);
205
+ if (!pkgJsonPath) return undefined;
196
206
  return jk_fs.dirname(pkgJsonPath);
197
207
  }
198
208
 
199
- export function findPackageJson(searchFromDir = getCodeSourceDirHint()): string {
200
- if (!searchFromDir && (gPackageJsonPath!==undefined)) return gPackageJsonPath;
209
+ export function findRequiredPackageJson(searchFromDir = getCodeSourceDirHint()): string {
210
+ let res = findPackageJson(searchFromDir);
211
+ if (!res) throw new Error("No package.json found.");
212
+ return res;
213
+ }
214
+
215
+ export function findPackageJson(searchFromDir = getCodeSourceDirHint()): string|undefined {
216
+ if (!searchFromDir && (gPackageJsonPath!==undefined)) {
217
+ return gPackageJsonPath;
218
+ }
201
219
 
202
220
  let currentDir = searchFromDir;
203
221
 
@@ -214,7 +232,7 @@ export function findPackageJson(searchFromDir = getCodeSourceDirHint()): string
214
232
  currentDir = parentDir;
215
233
  }
216
234
 
217
- throw "No package.json found."
235
+ return undefined;
218
236
  }
219
237
  //
220
238
  let gPackageJsonPath: string|undefined;
@@ -236,7 +254,7 @@ export function getCodeSourceDirHint() {
236
254
  export function getSourceCodeDir(): string {
237
255
  if (gSourceCodeDir) return gSourceCodeDir;
238
256
 
239
- let pkgJsonPath = findPackageJson();
257
+ let pkgJsonPath = findRequiredPackageJson();
240
258
  let dirName = jk_fs.join(jk_fs.dirname(pkgJsonPath), "src");
241
259
 
242
260
  if (jk_fs.isDirectorySync(dirName)) {
@@ -259,7 +277,7 @@ export function getCompiledCodeDir(): string {
259
277
  return gCompiledSourcesDir = sourceCodeDir;
260
278
  }
261
279
 
262
- let pkgJsonPath = findPackageJson();
280
+ let pkgJsonPath = findRequiredPackageJson();
263
281
 
264
282
  let rootDir = jk_fs.dirname(pkgJsonPath);
265
283
 
@@ -273,7 +291,7 @@ export function getCompiledCodeDir(): string {
273
291
  return rootDir;
274
292
  }
275
293
 
276
- export function getCompiledFilePathFor(sourceFilePath: string): string {
294
+ export function getCompiledFilePathFor(sourceFilePath: string, replaceExtension = true): string {
277
295
  const compiledCodeDir = getCompiledCodeDir();
278
296
  const sourceCodeDir = getSourceCodeDir();
279
297
 
@@ -283,9 +301,16 @@ export function getCompiledFilePathFor(sourceFilePath: string): string {
283
301
 
284
302
  let filePath = sourceFilePath.substring(sourceCodeDir.length);
285
303
 
286
- if (isNodeJS && !filePath.endsWith(".js")) {
304
+ if (replaceExtension && !filePath.endsWith(".js")) {
287
305
  let idx = filePath.lastIndexOf(".");
288
- if (idx !== -1) filePath = filePath.substring(0, idx) + ".js";
306
+ let ext = filePath.substring(idx);
307
+
308
+ // This avoid case where there is no extension but a punct in the name.
309
+ // Ex: importing "@/lib/jopijs.menu.getManager"
310
+ //
311
+ if ((ext === ".ts") || (ext === ".tsx")) {
312
+ filePath = filePath.substring(0, idx) + ".js";
313
+ }
289
314
  }
290
315
 
291
316
  return jk_fs.join(compiledCodeDir, filePath);
@@ -1,15 +1,19 @@
1
- import {PriorityLevel as EventPriority} from "jopi-toolkit/jk_tools";
1
+ import { PriorityLevel as EventPriority } from "jopi-toolkit/jk_tools";
2
+ import { isBrowser } from "jopi-toolkit/jk_what";
2
3
 
3
4
  // Warning: it's export.
4
- export {PriorityLevel as EventPriority} from "jopi-toolkit/jk_tools";
5
+ export { PriorityLevel as EventPriority } from "jopi-toolkit/jk_tools";
5
6
 
6
7
  // noinspection JSUnusedGlobalSymbols
7
8
 
8
- export type EventListener<T = any> = (e: T, eventName: string) => void|Promise<void>;
9
+ export type EventListener<T = any> = (e: T, eventName: string) => void | Promise<void>;
9
10
  export type SyncEventListener<T = any> = (e: T, eventName: string) => void;
10
11
 
11
12
  export type EventListenerProvider = () => Promise<EventListener[]>;
12
13
 
14
+ let gStaticEvents: Record<string, any> = {};
15
+ let gStaticEventsThisValue: any;
16
+
13
17
  export class EventGroup {
14
18
  private readonly listenersFor: Record<string, PriorityArray<EventListener>> = {};
15
19
  private evenSpy: undefined | ((eventName: string, data?: any) => void);
@@ -28,7 +32,7 @@ export class EventGroup {
28
32
  if (events) events.remove(listener);
29
33
  }
30
34
 
31
- sendEvent(eventName: string, e?: any|undefined): void {
35
+ sendEvent(eventName: string, e?: any | undefined): void {
32
36
  if (this.evenSpy) this.evenSpy(eventName, e);
33
37
 
34
38
  const events = this.listenersFor[eventName];
@@ -50,8 +54,8 @@ export class EventGroup {
50
54
  }
51
55
  }
52
56
 
53
- async sendAsyncEvent(eventName: string, e?: any|undefined): Promise<void> {
54
- if (eventName[0]!=='@') {
57
+ async sendAsyncEvent(eventName: string, e?: any | undefined): Promise<void> {
58
+ if (eventName[0] !== '@') {
55
59
  throw new Error(`Async events ${eventName} must start with @`);
56
60
  }
57
61
 
@@ -82,7 +86,7 @@ export class EventGroup {
82
86
  }
83
87
  }
84
88
 
85
- addListener<T = any|undefined>(eventName: string, priorityOrListener: EventPriority | EventListener<T>, listener?: EventListener<T>): void {
89
+ addListener<T = any | undefined>(eventName: string, priorityOrListener: EventPriority | EventListener<T>, listener?: EventListener<T>): void {
86
90
  let priority: EventPriority;
87
91
  let actualListener: EventListener;
88
92
 
@@ -117,11 +121,11 @@ export class EventGroup {
117
121
  */
118
122
  class PriorityArray<T> {
119
123
  private entries: PriorityArrayEntry<T>[] = [];
120
- private build: T[]|undefined;
124
+ private build: T[] | undefined;
121
125
 
122
126
  add(priority: EventPriority, value: T) {
123
127
  this.build = undefined;
124
- this.entries.push({priority, value});
128
+ this.entries.push({ priority, value });
125
129
  }
126
130
 
127
131
  remove(value: T) {
@@ -135,7 +139,7 @@ class PriorityArray<T> {
135
139
  }
136
140
 
137
141
  return this.build = this.entries
138
- .sort((a,b) => Number(a.priority) - Number(b.priority))
142
+ .sort((a, b) => Number(a.priority) - Number(b.priority))
139
143
  .map(e => e.value);
140
144
  }
141
145
  }
@@ -149,6 +153,7 @@ interface PriorityArrayEntry<T> {
149
153
 
150
154
  export interface StaticEvent {
151
155
  send<T>(data: T): T;
156
+ setThisValue(value: any): void;
152
157
  }
153
158
 
154
159
  export interface SEventController {
@@ -156,13 +161,17 @@ export interface SEventController {
156
161
  }
157
162
 
158
163
  class StaticEventImpl implements StaticEvent, SEventController {
164
+ private thisValue: any;
165
+
159
166
  constructor(public readonly eventName: string, private readonly eventItems: SyncEventListener[]) {
160
167
  gStaticEvents[eventName] = this;
161
168
  }
162
169
 
163
170
  send<T>(data: T): T {
171
+ let thisValue = this.thisValue ?? gStaticEventsThisValue;
172
+
164
173
  for (const listener of this.eventItems) {
165
- listener(data, this.eventName);
174
+ listener.call(thisValue, data, this.eventName);
166
175
  }
167
176
 
168
177
  return data;
@@ -176,12 +185,25 @@ class StaticEventImpl implements StaticEvent, SEventController {
176
185
  if (idx >= 0) this.eventItems.splice(idx, 1);
177
186
  };
178
187
  }
188
+
189
+ setThisValue(thisValue: any): void {
190
+ if (isBrowser) {
191
+ this.thisValue = thisValue;
192
+ }
193
+ }
179
194
  }
180
195
 
181
196
  export function createStaticEvent(eventName: string, eventItems: SyncEventListener[]): StaticEvent {
182
197
  return new StaticEventImpl(eventName, eventItems);
183
198
  }
184
199
 
200
+ export function setStaticEventsThisValue(thisValue: any) {
201
+ if (isBrowser) {
202
+ gStaticEventsThisValue = thisValue;
203
+ }
204
+ }
205
+
206
+
185
207
  export const defaultEventGroup = new EventGroup();
186
208
 
187
209
  export function newEventGroup(): EventGroup {
@@ -193,8 +215,6 @@ export const removeListener = defaultEventGroup.removeListener.bind(defaultEvent
193
215
  export const sendEvent = defaultEventGroup.sendEvent.bind(defaultEventGroup);
194
216
  export const sendAsyncEvent = defaultEventGroup.sendAsyncEvent.bind(defaultEventGroup);
195
217
 
196
- export function addListener<T = any|undefined>(eventName: string, priorityOrListener: EventPriority | EventListener<T>, listener?: EventListener<T>): void {
218
+ export function addListener<T = any | undefined>(eventName: string, priorityOrListener: EventPriority | EventListener<T>, listener?: EventListener<T>): void {
197
219
  defaultEventGroup.addListener(eventName, priorityOrListener as EventPriority, listener as EventListener<T>);
198
220
  }
199
-
200
- let gStaticEvents: Record<string, StaticEventImpl> = {};
@@ -68,6 +68,14 @@ export function readTextFromFileSync(filePath: string): string {
68
68
  throw new Error("Not implemented");
69
69
  }
70
70
 
71
+ export async function readJsonFromFile<T = any>(filePath: string, throwError: boolean = false): Promise<T> {
72
+ throw new Error("Not implemented");
73
+ }
74
+
75
+ export function readJsonFromFileSync<T = any>(filePath: string, throwError: boolean = false): T {
76
+ throw new Error("Not implemented");
77
+ }
78
+
71
79
  export async function isFile(filePath: string): Promise<boolean> {
72
80
  throw new Error("Not implemented");
73
81
  }
@@ -195,13 +195,27 @@ export async function readTextFromFile(filePath: string, throwError: boolean = f
195
195
  }
196
196
  }
197
197
 
198
+ export function readTextFromFileSync(filePath: string, throwError: boolean = false): string {
199
+ if (throwError) {
200
+ return fss.readFileSync(filePath, 'utf8');
201
+ }
202
+
203
+ try {
204
+ return fss.readFileSync(filePath, 'utf8');
205
+ }
206
+ catch {
207
+ // @ts-ignore
208
+ return undefined;
209
+ }
210
+ }
211
+
198
212
  export async function readJsonFromFile<T = any>(filePath: string, throwError: boolean = false): Promise<T> {
199
213
  if (throwError) {
200
- let txt = await fs.readFile(filePath, 'utf8');
214
+ let txt = await readTextFromFile(filePath, true);
201
215
  return JSON.parse(txt) as T;
202
216
  } else {
203
217
  try {
204
- let txt = await fs.readFile(filePath, 'utf8');
218
+ let txt = await readTextFromFile(filePath);
205
219
  return JSON.parse(txt) as T;
206
220
  }
207
221
  catch {
@@ -211,8 +225,20 @@ export async function readJsonFromFile<T = any>(filePath: string, throwError: bo
211
225
  }
212
226
  }
213
227
 
214
- export function readTextFromFileSync(filePath: string): string {
215
- return fss.readFileSync(filePath, 'utf8');
228
+ export function readJsonFromFileSync<T = any>(filePath: string, throwError: boolean = false): T {
229
+ if (throwError) {
230
+ let txt = readTextFromFileSync(filePath);
231
+ return JSON.parse(txt) as T;
232
+ } else {
233
+ try {
234
+ let txt = readTextFromFileSync(filePath);
235
+ return JSON.parse(txt) as T;
236
+ }
237
+ catch {
238
+ // @ts-ignore
239
+ return undefined;
240
+ }
241
+ }
216
242
  }
217
243
 
218
244
  export async function isFile(filePath: string): Promise<boolean> {
@@ -4,6 +4,12 @@ import type {LogInitializer} from "./index.ts";
4
4
 
5
5
  export function init(init: LogInitializer) {
6
6
  const mainDir = jk_app.findPackageJsonDir();
7
+
8
+ // Possible that no package.json is found.
9
+ // For exemple, when using a command line tool (ex: jopi init).
10
+ //
11
+ if (!mainDir) return;
12
+
7
13
  const filePath = jk_fs.join(mainDir, "logConfig.json");
8
14
 
9
15
  if (!jk_fs.isFileSync(filePath)) return;
@@ -1,2 +1 @@
1
1
  export * from "./jBundler_ifServer.ts";
2
- export * from "./common.ts";
@@ -1,4 +1,38 @@
1
+ import * as inspector from "node:inspector";
2
+
1
3
  export const argv = process.argv;
2
4
  export const env = process.env as Record<string, string>;
3
5
  export const isProduction = process.env.NODE_ENV === 'production';
4
- export const isDevelopment: boolean = !isProduction;
6
+ export const isDevelopment: boolean = !isProduction;
7
+
8
+ /**
9
+ * Detect if Node.js or Bun.js is running with the debugger launcher.
10
+ */
11
+ export function isLaunchedWithDebugger(): boolean {
12
+ const args = process.execArgv;
13
+ if (args.some((arg) => arg.includes("--inspect") || arg.includes("--debug") || arg.includes("bootloader.js"))) return true;
14
+
15
+ // Check environment variables
16
+ if (process.env.VSCODE_INSPECTOR_OPTIONS) return true;
17
+
18
+ if (process.env.NODE_OPTIONS) {
19
+ const nodeOptions = process.env.NODE_OPTIONS;
20
+ if (nodeOptions.includes("--inspect") ||
21
+ nodeOptions.includes("--debug") ||
22
+ nodeOptions.includes("bootloader.js") ||
23
+ nodeOptions.includes("js-debug")) {
24
+ return true;
25
+ }
26
+ }
27
+
28
+ // Check Bun environment variables
29
+ if (process.env.BUN_INSPECT || process.env.BUN_INSPECT_BRK || process.env.BUN_INSPECT_WAIT) return true;
30
+
31
+
32
+ // Check inspector url. This is the most reliable way to detect if a debugger is attached.
33
+ try {
34
+ if (inspector.url()) return true;
35
+ } catch { /* ignore */ }
36
+
37
+ return false;
38
+ }
@@ -106,9 +106,11 @@ export function killPort(port: string = '3000'): Promise<void> {
106
106
  return new Promise<void>((resolve) => {
107
107
  const isWindows = os.platform() === 'win32';
108
108
 
109
+ // On Windows, we use netstat to find the PID of the process using the port as LOCAL address.
110
+ // On Unix, we use lsof with -sTCP:LISTEN to only get the listening process (avoiding killing clients/browsers).
109
111
  const command = isWindows
110
112
  ? `netstat -ano | findstr :${port}`
111
- : `lsof -ti :${port}`;
113
+ : `lsof -ti :${port} -sTCP:LISTEN`;
112
114
 
113
115
  exec(command, (_error, stdout) => {
114
116
  if (!stdout) {
@@ -119,9 +121,17 @@ export function killPort(port: string = '3000'): Promise<void> {
119
121
  const pids = isWindows
120
122
  ? [...new Set(stdout.trim().split('\n').map(line => {
121
123
  const parts = line.trim().split(/\s+/);
122
- return parts[parts.length - 1];
123
- }).filter(pid => pid && /^\d+$/.test(pid) && pid !== '0'))]
124
- : stdout.trim().split('\n');
124
+ // netstat -ano output: Proto, Local Address, Foreign Address, State, PID
125
+ const localAddress = parts[1] || "";
126
+ const pid = parts[parts.length - 1];
127
+ // Check if it's exactly the port (avoiding :3000 matching :30000)
128
+ // and ensure it's the local address (avoiding killing clients connected to this port)
129
+ if (localAddress.endsWith(`:${port}`)) {
130
+ return pid;
131
+ }
132
+ return null;
133
+ }).filter((pid): pid is string => !!(pid && /^\d+$/.test(pid) && pid !== '0')))]
134
+ : stdout.trim().split('\n').filter(pid => pid && /^\d+$/.test(pid));
125
135
 
126
136
  const killCmd = isWindows ? 'taskkill /PID' : 'kill -9';
127
137
  let pending = pids.length;
@@ -133,8 +143,11 @@ export function killPort(port: string = '3000'): Promise<void> {
133
143
 
134
144
  pids.forEach(pid => {
135
145
  exec(`${killCmd} ${pid}${isWindows ? ' /F' : ''}`, async (err) => {
136
- if (err) console.error(`Failed to kill process ${pid}`);
137
- else console.log(`⚠️ Process ${pid} automatically killed to free the port ${port}`);
146
+ if (err) {
147
+ // Process might have already exited
148
+ } else {
149
+ console.log(`⚠️ Process ${pid} automatically killed to free the port ${port}`);
150
+ }
138
151
 
139
152
  if (--pending === 0) {
140
153
  await jk_timer.tick(250);
@@ -1,5 +1,8 @@
1
- export var isNodeJS = typeof (Bun) === "undefined";
2
- export var isBunJS = typeof (Bun) !== "undefined";
3
- export var isServerSide = true;
4
- export var isBrowser = false;
5
- export var serverType = isNodeJS ? "node" : "bun";
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.serverType = exports.isBrowser = exports.isServerSide = exports.isBunJS = exports.isNodeJS = void 0;
4
+ exports.isNodeJS = typeof (Bun) === "undefined";
5
+ exports.isBunJS = typeof (Bun) !== "undefined";
6
+ exports.isServerSide = true;
7
+ exports.isBrowser = false;
8
+ exports.serverType = exports.isNodeJS ? "node" : "bun";
@@ -1 +0,0 @@
1
- export {};