@olegkuibar/plunk 0.2.3 → 0.3.0-canary.0807dec

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.
Files changed (78) hide show
  1. package/dist/add-4LHIITHV.mjs +14 -0
  2. package/dist/chokidar-LVDD2IK4.mjs +4 -0
  3. package/dist/chunk-2VCW5RWI.mjs +3 -0
  4. package/dist/chunk-4QWBGXB5.mjs +15 -0
  5. package/dist/chunk-4TNTNSAB.mjs +4 -0
  6. package/dist/chunk-6YX2BGO2.mjs +3 -0
  7. package/dist/chunk-7JG555TZ.mjs +3 -0
  8. package/dist/chunk-AR7AKSKG.mjs +3 -0
  9. package/dist/chunk-HW7AEGI3.mjs +3 -0
  10. package/dist/chunk-KVVUZ6KL.mjs +4 -0
  11. package/dist/chunk-LI4HG26F.mjs +4 -0
  12. package/dist/chunk-MBKCCWSD.mjs +3 -0
  13. package/dist/chunk-OL7SNXMX.mjs +7 -0
  14. package/dist/chunk-QUI5JAIH.mjs +3 -0
  15. package/dist/chunk-RITKSDMP.mjs +3 -0
  16. package/dist/chunk-UAGG5WMD.mjs +3 -0
  17. package/dist/chunk-UDONDVIM.mjs +7 -0
  18. package/dist/chunk-ZAUWE3N7.mjs +12 -0
  19. package/dist/chunk-ZIXSXQRE.mjs +3 -0
  20. package/dist/chunk-ZJMZKJNW.mjs +3 -0
  21. package/dist/clean-EWBL6FW2.mjs +3 -0
  22. package/dist/cli.mjs +2 -56
  23. package/dist/dev-NCLIWE2E.mjs +3 -0
  24. package/dist/doctor-PXC45S34.mjs +4 -0
  25. package/dist/index.d.ts +19 -2
  26. package/dist/index.mjs +572 -244
  27. package/dist/init-TZ76GAWN.mjs +7 -0
  28. package/dist/list-CCR7QD7N.mjs +5 -0
  29. package/dist/migrate-6RZ3SEU5.mjs +8 -0
  30. package/dist/publish-4TDT6TTS.mjs +3 -0
  31. package/dist/push-M4RSFETI.mjs +3 -0
  32. package/dist/remove-R55FIXPL.mjs +3 -0
  33. package/dist/restore-XBFOHISI.mjs +11 -0
  34. package/dist/status-GCATYK2I.mjs +4 -0
  35. package/dist/tailwind-source-VPNLLSRJ.mjs +5 -0
  36. package/dist/update-6PLGM37S.mjs +3 -0
  37. package/dist/vite-config-ZMDIOTXC.mjs +10 -0
  38. package/dist/vite-plugin.mjs +2 -1
  39. package/dist/workspace-VREEJZ76.mjs +3 -0
  40. package/package.json +2 -8
  41. package/dist/add-5ZRFUL6Z.mjs +0 -258
  42. package/dist/chokidar-XGAEDN45.mjs +0 -1746
  43. package/dist/chunk-34UXZ622.mjs +0 -98
  44. package/dist/chunk-4O2QOKVO.mjs +0 -1958
  45. package/dist/chunk-CSMZ6DZA.mjs +0 -367
  46. package/dist/chunk-CZM4TNAI.mjs +0 -292
  47. package/dist/chunk-EDUXIQ5W.mjs +0 -1729
  48. package/dist/chunk-GAAB2TLH.mjs +0 -160
  49. package/dist/chunk-HKNM3UWU.mjs +0 -496
  50. package/dist/chunk-I6SN7BBN.mjs +0 -1131
  51. package/dist/chunk-KYDBD2KQ.mjs +0 -39
  52. package/dist/chunk-LKQINKH4.mjs +0 -130
  53. package/dist/chunk-PUSXMPOF.mjs +0 -82
  54. package/dist/chunk-S4HJSJ32.mjs +0 -69
  55. package/dist/chunk-W3C72UKC.mjs +0 -113
  56. package/dist/chunk-WSECI6M7.mjs +0 -85
  57. package/dist/chunk-XMIZ7OUZ.mjs +0 -26
  58. package/dist/chunk-XZK5T4GK.mjs +0 -23
  59. package/dist/chunk-ZOYNYK5Y.mjs +0 -23
  60. package/dist/chunk-ZQCGJUBJ.mjs +0 -92
  61. package/dist/clean-LTR5MZTY.mjs +0 -84
  62. package/dist/dev-LFXQP6SA.mjs +0 -194
  63. package/dist/dist-DUFCZSIL.mjs +0 -813
  64. package/dist/doctor-R7ZVR7PY.mjs +0 -230
  65. package/dist/hash-worker.mjs +0 -65
  66. package/dist/init-SWCNRISY.mjs +0 -310
  67. package/dist/list-B77L2F34.mjs +0 -119
  68. package/dist/migrate-X5TIC5SS.mjs +0 -124
  69. package/dist/prompt-HTPH6HQ7.mjs +0 -756
  70. package/dist/publish-UXCLPNM6.mjs +0 -63
  71. package/dist/push-JI6HGCFG.mjs +0 -197
  72. package/dist/remove-DCR7KKD5.mjs +0 -149
  73. package/dist/restore-SUN3WGSW.mjs +0 -124
  74. package/dist/status-MESRBH54.mjs +0 -103
  75. package/dist/tailwind-source-JBBEIXIJ.mjs +0 -89
  76. package/dist/update-SKDSA673.mjs +0 -100
  77. package/dist/vite-config-BAK67JHB.mjs +0 -128
  78. package/dist/workspace-76HJPAK2.mjs +0 -97
package/dist/index.mjs CHANGED
@@ -8,8 +8,120 @@ var __export = (target, all) => {
8
8
  __defProp(target, name, { get: all[name], enumerable: true });
9
9
  };
10
10
 
11
+ // src/utils/console.ts
12
+ import pc from "picocolors";
13
+ import { createInterface } from "readline/promises";
14
+ import { stdin, stdout } from "process";
15
+ async function prompt(message, options) {
16
+ const rl = createInterface({ input: stdin, output: stdout });
17
+ try {
18
+ if (options.type === "confirm") {
19
+ const hint = options.initial !== false ? "Y/n" : "y/N";
20
+ const answer2 = await rl.question(
21
+ `${pc.cyan("?")} ${message} ${pc.dim(`(${hint})`)} `
22
+ );
23
+ const val = answer2.trim().toLowerCase();
24
+ if (val === "") return options.initial !== false;
25
+ return val === "y" || val === "yes";
26
+ }
27
+ if (options.type === "text") {
28
+ const hint = options.default ? pc.dim(` (${options.default})`) : "";
29
+ const answer2 = await rl.question(`${pc.cyan("?")} ${message}${hint} `);
30
+ return answer2.trim() || options.default || "";
31
+ }
32
+ console.log(`${pc.cyan("?")} ${message}`);
33
+ for (let i = 0; i < options.options.length; i++) {
34
+ console.log(` ${pc.cyan(`${i + 1}.`)} ${options.options[i].label}`);
35
+ }
36
+ const answer = await rl.question(`${pc.dim("Enter number:")} `);
37
+ const idx = parseInt(answer, 10) - 1;
38
+ if (idx >= 0 && idx < options.options.length) {
39
+ return options.options[idx].value;
40
+ }
41
+ return options.options[0].value;
42
+ } finally {
43
+ rl.close();
44
+ }
45
+ }
46
+ var _level, consola;
47
+ var init_console = __esm({
48
+ "src/utils/console.ts"() {
49
+ "use strict";
50
+ _level = 3;
51
+ consola = {
52
+ get level() {
53
+ return _level;
54
+ },
55
+ set level(n) {
56
+ _level = n;
57
+ },
58
+ info(msg, ...args) {
59
+ if (_level >= 3) console.log(pc.cyan("\u2139"), msg, ...args);
60
+ },
61
+ success(msg, ...args) {
62
+ if (_level >= 3) console.log(pc.green("\u2714"), msg, ...args);
63
+ },
64
+ warn(msg, ...args) {
65
+ if (_level >= 2) console.warn(pc.yellow("\u26A0"), msg, ...args);
66
+ },
67
+ error(msg, ...args) {
68
+ if (_level >= 1) console.error(pc.red("\u2716"), msg, ...args);
69
+ },
70
+ start(msg, ...args) {
71
+ if (_level >= 3) console.log(pc.cyan("\u25D0"), msg, ...args);
72
+ },
73
+ debug(msg, ...args) {
74
+ if (_level >= 4) console.debug(pc.dim("D"), msg, ...args);
75
+ },
76
+ /** Plain log (no icon) that still respects level suppression */
77
+ log(msg, ...args) {
78
+ if (_level >= 3) console.log(msg, ...args);
79
+ },
80
+ prompt
81
+ };
82
+ }
83
+ });
84
+
85
+ // src/utils/concurrency.ts
86
+ function pLimit(concurrency) {
87
+ let active = 0;
88
+ const queue = [];
89
+ const next = () => {
90
+ if (queue.length > 0 && active < concurrency) {
91
+ active++;
92
+ queue.shift()();
93
+ }
94
+ };
95
+ return (fn) => new Promise((resolve6, reject) => {
96
+ const run = () => {
97
+ fn().then(
98
+ (val) => {
99
+ active--;
100
+ resolve6(val);
101
+ next();
102
+ },
103
+ (err) => {
104
+ active--;
105
+ reject(err);
106
+ next();
107
+ }
108
+ );
109
+ };
110
+ if (active < concurrency) {
111
+ active++;
112
+ run();
113
+ } else {
114
+ queue.push(run);
115
+ }
116
+ });
117
+ }
118
+ var init_concurrency = __esm({
119
+ "src/utils/concurrency.ts"() {
120
+ "use strict";
121
+ }
122
+ });
123
+
11
124
  // src/utils/logger.ts
12
- import { consola } from "consola";
13
125
  function isDryRun() {
14
126
  return _dryRun;
15
127
  }
@@ -20,6 +132,7 @@ var _verbose, _dryRun;
20
132
  var init_logger = __esm({
21
133
  "src/utils/logger.ts"() {
22
134
  "use strict";
135
+ init_console();
23
136
  _verbose = false;
24
137
  _dryRun = false;
25
138
  }
@@ -30,33 +143,7 @@ import { createHash } from "crypto";
30
143
  import { createReadStream } from "fs";
31
144
  import { readFile, stat } from "fs/promises";
32
145
  import { relative } from "path";
33
- import pLimit from "p-limit";
34
146
  import { availableParallelism } from "os";
35
- import xxhash from "xxhash-wasm";
36
- async function getXxhash() {
37
- if (!xxh) xxh = await xxhash();
38
- return xxh;
39
- }
40
- async function getPool() {
41
- if (poolFailed) return null;
42
- if (pool) return pool;
43
- try {
44
- const { default: Tinypool } = await import("tinypool");
45
- const poolSize = Math.min(Math.max(availableParallelism(), 2), 8);
46
- const workerUrl = new URL("./hash-worker.mjs", import.meta.url);
47
- pool = new Tinypool({
48
- filename: workerUrl.href,
49
- minThreads: 0,
50
- maxThreads: poolSize
51
- });
52
- verbose(`[hash] Worker pool initialized (max ${poolSize} threads)`);
53
- return pool;
54
- } catch {
55
- poolFailed = true;
56
- verbose("[hash] Worker pool unavailable, using main-thread hashing");
57
- return null;
58
- }
59
- }
60
147
  async function computeContentHash(files, baseDir) {
61
148
  const sorted = [...files].sort((a, b) => {
62
149
  const relA = relative(baseDir, a).replace(/\\/g, "/");
@@ -73,60 +160,43 @@ async function computeContentHash(files, baseDir) {
73
160
  );
74
161
  verbose(`[hash] Computing content hash for ${files.length} files`);
75
162
  const hash = createHash("sha256");
163
+ const lenBuf = Buffer.alloc(4);
76
164
  for (const { rel, content } of contents) {
77
165
  hash.update(rel);
78
166
  hash.update("\0");
167
+ lenBuf.writeUInt32LE(content.length);
168
+ hash.update(lenBuf);
79
169
  hash.update(content);
80
170
  }
81
- const result = "sha256:" + hash.digest("hex");
171
+ const result = "sha256v2:" + hash.digest("hex");
82
172
  verbose(`[hash] Result: ${result.slice(0, 20)}...`);
83
173
  return result;
84
174
  }
85
175
  async function hashFile(filePath, knownSize) {
86
176
  const size = knownSize ?? (await stat(filePath)).size;
87
- if (size <= WORKER_THRESHOLD) {
88
- return hashFileMainThread(filePath, size);
89
- }
90
- const p = await getPool();
91
- if (p) {
92
- try {
93
- return await p.run([filePath, size]);
94
- } catch {
95
- }
96
- }
97
- return hashFileMainThread(filePath, size);
98
- }
99
- async function hashFileMainThread(filePath, size) {
100
177
  if (size > STREAM_THRESHOLD) {
101
178
  return hashFileStream(filePath);
102
179
  }
103
- const { h64Raw } = await getXxhash();
104
180
  const content = await readFile(filePath);
105
- return h64Raw(content).toString(16);
181
+ return createHash("sha256").update(content).digest("hex");
106
182
  }
107
183
  function hashFileStream(filePath) {
108
- return new Promise((resolve4, reject) => {
184
+ return new Promise((resolve6, reject) => {
109
185
  const hash = createHash("sha256");
110
186
  const stream = createReadStream(filePath);
111
187
  stream.on("data", (chunk) => hash.update(chunk));
112
- stream.on("end", () => resolve4(hash.digest("hex")));
188
+ stream.on("end", () => resolve6(hash.digest("hex")));
113
189
  stream.on("error", reject);
114
190
  });
115
191
  }
116
- var STREAM_THRESHOLD, WORKER_THRESHOLD, limit, xxh, pool, poolFailed;
192
+ var STREAM_THRESHOLD, limit;
117
193
  var init_hash = __esm({
118
194
  "src/utils/hash.ts"() {
119
195
  "use strict";
196
+ init_concurrency();
120
197
  init_logger();
121
198
  STREAM_THRESHOLD = 1024 * 1024;
122
- WORKER_THRESHOLD = 64 * 1024;
123
199
  limit = pLimit(Math.max(availableParallelism(), 8));
124
- xxh = null;
125
- pool = null;
126
- poolFailed = false;
127
- process.on("exit", () => {
128
- pool?.destroy();
129
- });
130
200
  }
131
201
  });
132
202
 
@@ -143,7 +213,6 @@ import {
143
213
  constants
144
214
  } from "fs/promises";
145
215
  import { join as join2, dirname, relative as relative2, parse as parsePath } from "path";
146
- import pLimit2 from "p-limit";
147
216
  import { availableParallelism as availableParallelism2 } from "os";
148
217
  function isNodeError(err) {
149
218
  return err instanceof Error && "code" in err;
@@ -178,21 +247,8 @@ async function copyWithCoW(src, dest) {
178
247
  }
179
248
  }
180
249
  async function collectFiles(dir) {
181
- const entries = await readdir(dir, { recursive: true });
182
- const results = await Promise.all(
183
- entries.map(
184
- (entry) => ioLimit(async () => {
185
- const full = join2(dir, entry);
186
- try {
187
- const s = await stat2(full);
188
- return s.isFile() ? full : null;
189
- } catch {
190
- return null;
191
- }
192
- })
193
- )
194
- );
195
- return results.filter((f) => f !== null);
250
+ const entries = await readdir(dir, { recursive: true, withFileTypes: true });
251
+ return entries.filter((e) => e.isFile()).map((e) => join2(e.parentPath, e.name));
196
252
  }
197
253
  async function incrementalCopy(srcDir, destDir) {
198
254
  const srcFiles = await collectFiles(srcDir);
@@ -246,16 +302,18 @@ async function incrementalCopy(srcDir, destDir) {
246
302
  try {
247
303
  const destFiles = await collectFiles(destDir);
248
304
  const srcRelPaths = new Set(srcFiles.map((f) => relative2(srcDir, f)));
249
- for (const destFile of destFiles) {
250
- const rel = relative2(destDir, destFile);
251
- if (!srcRelPaths.has(rel)) {
252
- verbose(`[remove] ${rel} (no longer in source)`);
253
- if (!isDryRun()) {
254
- await rm(destFile);
255
- }
256
- removed++;
257
- }
258
- }
305
+ const filesToRemove = destFiles.filter(
306
+ (f) => !srcRelPaths.has(relative2(destDir, f))
307
+ );
308
+ await Promise.all(
309
+ filesToRemove.map(
310
+ (destFile) => ioLimit(async () => {
311
+ verbose(`[remove] ${relative2(destDir, destFile)} (no longer in source)`);
312
+ if (!isDryRun()) await rm(destFile);
313
+ })
314
+ )
315
+ );
316
+ removed = filesToRemove.length;
259
317
  } catch (err) {
260
318
  if (isNodeError(err) && err.code === "ENOENT") {
261
319
  } else {
@@ -313,10 +371,11 @@ var ioLimit, reflinkSupported;
313
371
  var init_fs = __esm({
314
372
  "src/utils/fs.ts"() {
315
373
  "use strict";
374
+ init_concurrency();
316
375
  init_hash();
317
376
  init_logger();
318
377
  init_logger();
319
- ioLimit = pLimit2(Math.max(availableParallelism2(), 8));
378
+ ioLimit = pLimit(Math.max(availableParallelism2(), 8));
320
379
  reflinkSupported = /* @__PURE__ */ new Map();
321
380
  }
322
381
  });
@@ -324,11 +383,13 @@ var init_fs = __esm({
324
383
  // src/utils/workspace.ts
325
384
  var workspace_exports = {};
326
385
  __export(workspace_exports, {
386
+ findWorkspacePackages: () => findWorkspacePackages,
327
387
  findWorkspaceRoot: () => findWorkspaceRoot,
328
388
  parseCatalogs: () => parseCatalogs
329
389
  });
330
- import { readFile as readFile4 } from "fs/promises";
331
- import { join as join5, dirname as dirname3 } from "path";
390
+ import { readFile as readFile4, readdir as readdir4 } from "fs/promises";
391
+ import { join as join5, dirname as dirname3, resolve as resolve2, relative as relative4 } from "path";
392
+ import picomatch2 from "picomatch";
332
393
  async function findWorkspaceRoot(startDir) {
333
394
  let dir = startDir;
334
395
  for (; ; ) {
@@ -401,6 +462,130 @@ async function parseCatalogs(workspaceRoot) {
401
462
  }
402
463
  return result;
403
464
  }
465
+ async function findWorkspacePackages(startDir) {
466
+ const pnpmRoot = await findWorkspaceRoot(startDir);
467
+ if (pnpmRoot) {
468
+ const patterns = await parsePnpmWorkspacePackages(pnpmRoot);
469
+ if (patterns.length > 0) {
470
+ return resolveWorkspaceGlobs(pnpmRoot, patterns);
471
+ }
472
+ }
473
+ const rootDir = pnpmRoot ?? await findPackageJsonWorkspaceRoot(startDir);
474
+ if (!rootDir) return [];
475
+ try {
476
+ const rootPkg = JSON.parse(await readFile4(join5(rootDir, "package.json"), "utf-8"));
477
+ const workspaces = Array.isArray(rootPkg.workspaces) ? rootPkg.workspaces : rootPkg.workspaces?.packages ?? [];
478
+ if (workspaces.length === 0) return [];
479
+ return resolveWorkspaceGlobs(rootDir, workspaces);
480
+ } catch {
481
+ return [];
482
+ }
483
+ }
484
+ async function parsePnpmWorkspacePackages(workspaceRoot) {
485
+ const filePath = join5(workspaceRoot, "pnpm-workspace.yaml");
486
+ let content;
487
+ try {
488
+ content = await readFile4(filePath, "utf-8");
489
+ } catch {
490
+ return [];
491
+ }
492
+ const patterns = [];
493
+ let inPackages = false;
494
+ for (const line of content.split(/\r?\n/)) {
495
+ const trimmed = line.trim();
496
+ if (trimmed === "" || trimmed.startsWith("#")) continue;
497
+ const indent = line.length - line.trimStart().length;
498
+ if (indent === 0) {
499
+ inPackages = trimmed === "packages:";
500
+ continue;
501
+ }
502
+ if (inPackages && indent >= 2) {
503
+ const match = trimmed.match(/^-\s+["']?([^"']+)["']?$/);
504
+ if (match) {
505
+ if (!match[1].startsWith("!")) {
506
+ patterns.push(match[1]);
507
+ }
508
+ }
509
+ }
510
+ }
511
+ return patterns;
512
+ }
513
+ async function findPackageJsonWorkspaceRoot(startDir) {
514
+ let dir = startDir;
515
+ for (; ; ) {
516
+ try {
517
+ const pkg = JSON.parse(await readFile4(join5(dir, "package.json"), "utf-8"));
518
+ if (pkg.workspaces) return dir;
519
+ } catch {
520
+ }
521
+ const parent = dirname3(dir);
522
+ if (parent === dir) return null;
523
+ dir = parent;
524
+ }
525
+ }
526
+ async function resolveWorkspaceGlobs(rootDir, patterns) {
527
+ const results = [];
528
+ for (const pattern of patterns) {
529
+ if (pattern.includes("*")) {
530
+ const parts = pattern.split("/");
531
+ let staticPrefix = rootDir;
532
+ const globParts = [];
533
+ let foundGlob = false;
534
+ for (const part of parts) {
535
+ if (foundGlob || part.includes("*")) {
536
+ foundGlob = true;
537
+ globParts.push(part);
538
+ } else {
539
+ staticPrefix = join5(staticPrefix, part);
540
+ }
541
+ }
542
+ if (globParts.length === 1 && globParts[0] === "*") {
543
+ try {
544
+ const entries = await readdir4(staticPrefix, { withFileTypes: true });
545
+ for (const entry of entries) {
546
+ if (entry.isDirectory()) {
547
+ const pkgDir = join5(staticPrefix, entry.name);
548
+ if (await exists(join5(pkgDir, "package.json"))) {
549
+ results.push(pkgDir);
550
+ }
551
+ }
552
+ }
553
+ } catch {
554
+ }
555
+ } else {
556
+ const isMatch = picomatch2(pattern);
557
+ const candidates = await collectDirs(rootDir, 8);
558
+ for (const candidate of candidates) {
559
+ const rel = relative4(rootDir, candidate).replace(/\\/g, "/");
560
+ if (isMatch(rel) && await exists(join5(candidate, "package.json"))) {
561
+ results.push(candidate);
562
+ }
563
+ }
564
+ }
565
+ } else {
566
+ const pkgDir = resolve2(rootDir, pattern);
567
+ if (await exists(join5(pkgDir, "package.json"))) {
568
+ results.push(pkgDir);
569
+ }
570
+ }
571
+ }
572
+ return [...new Set(results)];
573
+ }
574
+ async function collectDirs(dir, maxDepth) {
575
+ if (maxDepth <= 0) return [];
576
+ const results = [];
577
+ try {
578
+ const entries = await readdir4(dir, { withFileTypes: true });
579
+ for (const entry of entries) {
580
+ if (!entry.isDirectory() || entry.name === "node_modules" || entry.name === ".git") continue;
581
+ const full = join5(dir, entry.name);
582
+ results.push(full);
583
+ results.push(...await collectDirs(full, maxDepth - 1));
584
+ }
585
+ } catch {
586
+ }
587
+ return results;
588
+ }
404
589
  function parseKeyValue(line) {
405
590
  const trimmed = line.trim();
406
591
  const colonIdx = trimmed.indexOf(":");
@@ -419,13 +604,13 @@ var init_workspace = __esm({
419
604
  });
420
605
 
421
606
  // src/core/publisher.ts
422
- import { readFile as readFile5, writeFile as writeFile3, rename as rename2 } from "fs/promises";
607
+ init_console();
608
+ init_concurrency();
609
+ import { readFile as readFile5, writeFile as writeFile2, rename as rename2, stat as stat5 } from "fs/promises";
423
610
  import { randomBytes } from "crypto";
424
- import { join as join6, relative as relative4, dirname as dirname4 } from "path";
611
+ import { join as join6, relative as relative5, dirname as dirname4, resolve as resolve3 } from "path";
425
612
  import { spawn } from "child_process";
426
613
  import { platform } from "os";
427
- import { consola as consola4 } from "consola";
428
- import pLimit3 from "p-limit";
429
614
  import { availableParallelism as availableParallelism3 } from "os";
430
615
 
431
616
  // src/utils/paths.ts
@@ -475,23 +660,23 @@ function getNodeModulesPackagePath(consumerPath, packageName) {
475
660
  }
476
661
 
477
662
  // src/utils/pack-list.ts
663
+ init_console();
478
664
  init_fs();
479
665
  import { readFile as readFile2, readdir as readdir2, stat as stat3 } from "fs/promises";
480
666
  import { join as join3, relative as relative3, resolve, sep } from "path";
481
667
  import picomatch from "picomatch";
482
- import { consola as consola2 } from "consola";
483
668
  async function resolvePackFiles(packageDir, pkg) {
484
669
  const files = [];
485
670
  const absDir = resolve(packageDir);
486
671
  files.push(join3(absDir, "package.json"));
487
- const allFiles = await collectAllFiles(absDir);
672
+ const allFiles = await collectAllFiles(absDir, absDir);
488
673
  const allRelPaths = allFiles.map((f) => relative3(absDir, f).replace(/\\/g, "/"));
489
674
  if (pkg.files && pkg.files.length > 0) {
490
675
  for (const pattern of pkg.files) {
491
676
  const target = join3(absDir, pattern);
492
677
  const resolved = resolve(target);
493
678
  if (!resolved.startsWith(absDir + sep) && resolved !== absDir) {
494
- consola2.warn(`files pattern "${pattern}" escapes package directory, skipping`);
679
+ consola.warn(`files pattern "${pattern}" escapes package directory, skipping`);
495
680
  continue;
496
681
  }
497
682
  let matched = false;
@@ -526,7 +711,7 @@ async function resolvePackFiles(packageDir, pkg) {
526
711
  }
527
712
  }
528
713
  if (globMatched === 0) {
529
- consola2.warn(`files pattern "${pattern}" matched no files`);
714
+ consola.warn(`files pattern "${pattern}" matched no files`);
530
715
  }
531
716
  }
532
717
  }
@@ -583,6 +768,9 @@ var DEFAULT_IGNORES = /* @__PURE__ */ new Set([
583
768
  "vitest.config.js"
584
769
  ]);
585
770
  function shouldIgnore(relPath, matchers) {
771
+ for (const isMatch of matchers.negations) {
772
+ if (isMatch(relPath)) return false;
773
+ }
586
774
  const parts = relPath.split(/[\\/]/);
587
775
  for (const part of parts) {
588
776
  if (DEFAULT_IGNORES.has(part)) return true;
@@ -592,9 +780,6 @@ function shouldIgnore(relPath, matchers) {
592
780
  for (const isMatch of matchers.patterns) {
593
781
  if (isMatch(relPath)) return true;
594
782
  }
595
- for (const isMatch of matchers.negations) {
596
- if (isMatch(relPath)) return false;
597
- }
598
783
  return false;
599
784
  }
600
785
  async function loadNpmIgnore(dir) {
@@ -627,16 +812,17 @@ async function loadNpmIgnore(dir) {
627
812
  function hasGlobChars(pattern) {
628
813
  return /[*?[\]{}()]/.test(pattern);
629
814
  }
630
- async function collectAllFiles(dir) {
815
+ async function collectAllFiles(dir, rootDir) {
631
816
  const results = [];
632
817
  try {
633
818
  const entries = await readdir2(dir, { withFileTypes: true });
634
819
  for (const entry of entries) {
635
820
  const full = join3(dir, entry.name);
636
821
  if (entry.isDirectory()) {
637
- if (entry.name === "node_modules" || entry.name === ".git") continue;
822
+ if (entry.name === ".git") continue;
823
+ if (dir === rootDir && entry.name === "node_modules") continue;
638
824
  if (entry.isSymbolicLink()) continue;
639
- results.push(...await collectAllFiles(full));
825
+ results.push(...await collectAllFiles(full, rootDir));
640
826
  } else if (!entry.isSymbolicLink()) {
641
827
  results.push(full);
642
828
  }
@@ -655,16 +841,16 @@ init_hash();
655
841
  init_fs();
656
842
 
657
843
  // src/core/store.ts
844
+ init_console();
658
845
  import { readFile as readFile3, readdir as readdir3 } from "fs/promises";
659
846
  import "path";
660
- import { consola as consola3 } from "consola";
661
847
  init_fs();
662
848
 
663
849
  // src/utils/validators.ts
664
850
  function isPlunkMeta(value) {
665
851
  if (typeof value !== "object" || value === null) return false;
666
852
  const v = value;
667
- return typeof v.contentHash === "string" && typeof v.publishedAt === "string" && typeof v.sourcePath === "string" && (v.buildId === void 0 || typeof v.buildId === "string");
853
+ return typeof v.contentHash === "string" && typeof v.publishedAt === "string" && typeof v.sourcePath === "string" && (v.buildId === void 0 || typeof v.buildId === "string") && (v.schemaVersion === void 0 || typeof v.schemaVersion === "number");
668
854
  }
669
855
  function isLinkEntry(value) {
670
856
  if (typeof value !== "object" || value === null) return false;
@@ -697,20 +883,27 @@ function isConsumersRegistry(value) {
697
883
  // src/core/store.ts
698
884
  async function readMeta(name, version) {
699
885
  const metaPath = getStoreMetaPath(name, version);
700
- try {
701
- const content = await readFile3(metaPath, "utf-8");
702
- const parsed = JSON.parse(content);
703
- if (!isPlunkMeta(parsed)) {
704
- consola3.warn(`Invalid metadata for ${name}@${version}, ignoring`);
886
+ for (let attempt = 0; attempt < 2; attempt++) {
887
+ try {
888
+ const content = await readFile3(metaPath, "utf-8");
889
+ const parsed = JSON.parse(content);
890
+ if (!isPlunkMeta(parsed)) {
891
+ consola.warn(`Invalid metadata for ${name}@${version}, ignoring`);
892
+ return null;
893
+ }
894
+ return parsed;
895
+ } catch (err) {
896
+ if (isNodeError(err) && err.code === "ENOENT" && attempt === 0) {
897
+ await new Promise((r) => setTimeout(r, 100));
898
+ continue;
899
+ }
900
+ if (isNodeError(err) && err.code !== "ENOENT") {
901
+ consola.warn(`Failed to read metadata for ${name}@${version}: ${err instanceof Error ? err.message : String(err)}`);
902
+ }
705
903
  return null;
706
904
  }
707
- return parsed;
708
- } catch (err) {
709
- if (isNodeError(err) && err.code !== "ENOENT") {
710
- consola3.warn(`Failed to read metadata for ${name}@${version}: ${err}`);
711
- }
712
- return null;
713
905
  }
906
+ return null;
714
907
  }
715
908
  async function getStoreEntry(name, version) {
716
909
  const packageDir = getStorePackagePath(name, version);
@@ -742,10 +935,9 @@ async function findStoreEntry(name) {
742
935
  );
743
936
  const matching = results.filter((r) => r !== null);
744
937
  if (matching.length === 0) return null;
745
- matching.sort(
746
- (a, b) => new Date(b.meta.publishedAt).getTime() - new Date(a.meta.publishedAt).getTime()
938
+ return matching.reduce(
939
+ (latest, entry) => new Date(entry.meta.publishedAt).getTime() > new Date(latest.meta.publishedAt).getTime() ? entry : latest
747
940
  );
748
- return matching[0];
749
941
  }
750
942
  async function listStoreEntries() {
751
943
  const storePath = getStorePath();
@@ -777,44 +969,64 @@ async function listStoreEntries() {
777
969
 
778
970
  // src/utils/lockfile.ts
779
971
  init_fs();
780
- import { lock } from "proper-lockfile";
972
+ import { mkdir as mkdir2, stat as stat4, rm as rm2 } from "fs/promises";
781
973
  import { dirname as dirname2 } from "path";
782
- import { mkdir as mkdir2, writeFile as writeFile2 } from "fs/promises";
783
- var LOCK_OPTIONS = {
784
- retries: {
785
- retries: 5,
786
- minTimeout: 100,
787
- maxTimeout: 1e3,
788
- factor: 2
789
- },
790
- stale: 1e4,
791
- realpath: false
974
+ import { setTimeout as sleep } from "timers/promises";
975
+ var DEFAULTS = {
976
+ retries: 5,
977
+ minTimeout: 100,
978
+ maxTimeout: 1e3,
979
+ factor: 2,
980
+ stale: 1e4
792
981
  };
793
982
  async function withFileLock(filePath, fn, lockOptions) {
794
983
  await mkdir2(dirname2(filePath), { recursive: true });
795
- try {
796
- await writeFile2(filePath, "", { flag: "wx" });
797
- } catch (err) {
798
- if (isNodeError(err) && err.code === "EEXIST") {
799
- } else {
800
- throw err;
984
+ const lockDir = filePath + ".lk";
985
+ const stale = lockOptions?.stale ?? DEFAULTS.stale;
986
+ let acquired = false;
987
+ for (let attempt = 0; attempt <= DEFAULTS.retries; attempt++) {
988
+ try {
989
+ await mkdir2(lockDir);
990
+ acquired = true;
991
+ break;
992
+ } catch (err) {
993
+ if (isNodeError(err) && err.code === "EEXIST") {
994
+ try {
995
+ const s = await stat4(lockDir);
996
+ if (Date.now() - s.mtimeMs > stale) {
997
+ await rm2(lockDir, { recursive: true, force: true });
998
+ continue;
999
+ }
1000
+ } catch {
1001
+ continue;
1002
+ }
1003
+ if (attempt < DEFAULTS.retries) {
1004
+ const delay = Math.min(
1005
+ DEFAULTS.minTimeout * DEFAULTS.factor ** attempt,
1006
+ DEFAULTS.maxTimeout
1007
+ );
1008
+ await sleep(delay);
1009
+ }
1010
+ } else {
1011
+ throw err;
1012
+ }
801
1013
  }
802
1014
  }
803
- const opts = lockOptions ? { ...LOCK_OPTIONS, ...lockOptions } : LOCK_OPTIONS;
804
- let release;
1015
+ if (!acquired) {
1016
+ throw new Error(
1017
+ `Failed to acquire lock after ${DEFAULTS.retries} attempts. Another plunk process may be running. If this persists, delete ${lockDir} and retry.`
1018
+ );
1019
+ }
805
1020
  try {
806
- release = await lock(filePath, opts);
807
1021
  return await fn();
808
1022
  } finally {
809
- if (release) {
810
- await release();
811
- }
1023
+ await rm2(lockDir, { recursive: true, force: true });
812
1024
  }
813
1025
  }
814
1026
 
815
1027
  // src/core/publisher.ts
816
1028
  init_logger();
817
- var copyLimit = pLimit3(Math.max(availableParallelism3(), 8));
1029
+ var copyLimit = pLimit(Math.max(availableParallelism3(), 8));
818
1030
  async function publish(packageDir, options = {}) {
819
1031
  const pkgPath = join6(packageDir, "package.json");
820
1032
  let pkgContent;
@@ -832,16 +1044,37 @@ async function publish(packageDir, options = {}) {
832
1044
  );
833
1045
  }
834
1046
  await runLifecycleHook(packageDir, pkg, "preplunk");
835
- const files = await resolvePackFiles(packageDir, pkg);
1047
+ if (options.runScripts !== false) {
1048
+ await runLifecycleHook(packageDir, pkg, "prepack");
1049
+ }
1050
+ let publishDir = packageDir;
1051
+ if (pkg.publishConfig?.directory) {
1052
+ publishDir = resolve3(packageDir, pkg.publishConfig.directory);
1053
+ try {
1054
+ const s = await stat5(publishDir);
1055
+ if (!s.isDirectory()) {
1056
+ throw new Error(`publishConfig.directory "${pkg.publishConfig.directory}" is not a directory`);
1057
+ }
1058
+ } catch (err) {
1059
+ if (err instanceof Error && "code" in err && err.code === "ENOENT") {
1060
+ throw new Error(`publishConfig.directory "${pkg.publishConfig.directory}" does not exist`);
1061
+ }
1062
+ throw err;
1063
+ }
1064
+ verbose(`[publish] Using publishConfig.directory: ${publishDir}`);
1065
+ }
1066
+ const filePkg = publishDir !== packageDir ? JSON.parse(await readFile5(join6(publishDir, "package.json"), "utf-8").catch(() => JSON.stringify(pkg))) : pkg;
1067
+ const files = await resolvePackFiles(publishDir, filePkg);
836
1068
  if (files.length === 0) {
837
1069
  throw new Error("No publishable files found");
838
1070
  }
839
1071
  verbose(`[publish] Resolved ${files.length} files for ${pkg.name}@${pkg.version}`);
840
- const contentHash = await computeContentHash(files, packageDir);
1072
+ const contentHash = await computeContentHash(files, publishDir);
1073
+ await preloadWorkspaceVersions(pkg, packageDir);
841
1074
  await preloadCatalogs(pkg, packageDir);
842
1075
  const existingMeta = await readMeta(pkg.name, pkg.version);
843
1076
  if (existingMeta && existingMeta.contentHash === contentHash) {
844
- consola4.info(`${pkg.name}@${pkg.version} already up to date`);
1077
+ consola.info(`${pkg.name}@${pkg.version} already up to date (no changes since last publish)`);
845
1078
  return {
846
1079
  name: pkg.name,
847
1080
  version: pkg.version,
@@ -857,7 +1090,7 @@ async function publish(packageDir, options = {}) {
857
1090
  async () => {
858
1091
  const metaUnderLock = await readMeta(pkg.name, pkg.version);
859
1092
  if (metaUnderLock && metaUnderLock.contentHash === contentHash) {
860
- consola4.info(`${pkg.name}@${pkg.version} already up to date`);
1093
+ consola.info(`${pkg.name}@${pkg.version} already up to date (no changes since last publish)`);
861
1094
  return {
862
1095
  name: pkg.name,
863
1096
  version: pkg.version,
@@ -872,32 +1105,40 @@ async function publish(packageDir, options = {}) {
872
1105
  const buildId = randomBytes(4).toString("hex");
873
1106
  try {
874
1107
  await ensurePrivateDir(tmpPackageDir);
875
- const processedPkg = rewriteProtocolVersions(pkg, packageDir);
1108
+ let processedPkg = rewriteProtocolVersions(pkg, packageDir);
1109
+ processedPkg = applyPublishConfig(processedPkg);
876
1110
  verbose(`[publish] Copying files to temp store...`);
877
1111
  const uniqueDirs = new Set(
878
- files.map((file) => dirname4(join6(tmpPackageDir, relative4(packageDir, file))))
1112
+ files.map((file) => dirname4(join6(tmpPackageDir, relative5(publishDir, file))))
879
1113
  );
880
1114
  await Promise.all([...uniqueDirs].map((d) => ensureDir(d)));
881
1115
  await Promise.all(
882
1116
  files.map(
883
1117
  (file) => copyLimit(async () => {
884
- const rel = relative4(packageDir, file);
1118
+ const rel = relative5(publishDir, file);
885
1119
  const dest = join6(tmpPackageDir, rel);
886
1120
  if (rel === "package.json" && processedPkg !== pkg) {
887
- await writeFile3(dest, JSON.stringify(processedPkg, null, 2));
1121
+ await writeFile2(dest, JSON.stringify(processedPkg, null, 2));
888
1122
  } else {
889
1123
  await copyWithCoW(file, dest);
890
1124
  }
891
1125
  })
892
1126
  )
893
1127
  );
1128
+ if (publishDir !== packageDir) {
1129
+ await writeFile2(
1130
+ join6(tmpPackageDir, "package.json"),
1131
+ JSON.stringify(processedPkg, null, 2)
1132
+ );
1133
+ }
894
1134
  const meta = {
1135
+ schemaVersion: 1,
895
1136
  contentHash,
896
1137
  publishedAt: (/* @__PURE__ */ new Date()).toISOString(),
897
1138
  sourcePath: packageDir,
898
1139
  buildId
899
1140
  };
900
- await writeFile3(
1141
+ await writeFile2(
901
1142
  join6(tmpDir, ".plunk-meta.json"),
902
1143
  JSON.stringify(meta, null, 2)
903
1144
  );
@@ -923,8 +1164,11 @@ async function publish(packageDir, options = {}) {
923
1164
  { stale: 6e4 }
924
1165
  );
925
1166
  if (result.skipped) return result;
1167
+ if (options.runScripts !== false) {
1168
+ await runLifecycleHook(packageDir, pkg, "postpack");
1169
+ }
926
1170
  await runLifecycleHook(packageDir, pkg, "postplunk");
927
- consola4.success(
1171
+ consola.success(
928
1172
  `Published ${pkg.name}@${pkg.version} (${files.length} files) [${result.buildId}]`
929
1173
  );
930
1174
  return result;
@@ -934,7 +1178,7 @@ async function runLifecycleHook(packageDir, pkg, hookName) {
934
1178
  const script = pkg.scripts?.[hookName];
935
1179
  if (!script) return;
936
1180
  verbose(`[lifecycle] Running ${hookName}: ${script}`);
937
- return new Promise((resolve4, reject) => {
1181
+ return new Promise((resolve6, reject) => {
938
1182
  const isWin = platform() === "win32";
939
1183
  const shell = isWin ? "cmd" : "sh";
940
1184
  const shellFlag = isWin ? "/c" : "-c";
@@ -944,12 +1188,12 @@ async function runLifecycleHook(packageDir, pkg, hookName) {
944
1188
  });
945
1189
  const timer = setTimeout(() => {
946
1190
  child.kill("SIGTERM");
947
- reject(new Error(`${hookName} script timed out after ${HOOK_TIMEOUT}ms`));
1191
+ reject(new Error(`${hookName} script timed out after ${HOOK_TIMEOUT / 1e3}s. Increase PLUNK_HOOK_TIMEOUT env var if the script needs more time.`));
948
1192
  }, HOOK_TIMEOUT);
949
1193
  child.on("close", (code) => {
950
1194
  clearTimeout(timer);
951
1195
  if (code === 0) {
952
- resolve4();
1196
+ resolve6();
953
1197
  } else {
954
1198
  reject(new Error(`${hookName} script failed with exit code ${code}`));
955
1199
  }
@@ -960,6 +1204,26 @@ async function runLifecycleHook(packageDir, pkg, hookName) {
960
1204
  });
961
1205
  });
962
1206
  }
1207
+ var PUBLISH_CONFIG_OVERRIDES = [
1208
+ "main",
1209
+ "module",
1210
+ "exports",
1211
+ "types",
1212
+ "typings",
1213
+ "browser",
1214
+ "bin"
1215
+ ];
1216
+ function applyPublishConfig(pkg) {
1217
+ if (!pkg.publishConfig) return pkg;
1218
+ const result = { ...pkg };
1219
+ for (const field of PUBLISH_CONFIG_OVERRIDES) {
1220
+ if (field in pkg.publishConfig) {
1221
+ result[field] = pkg.publishConfig[field];
1222
+ }
1223
+ }
1224
+ delete result.publishConfig;
1225
+ return result;
1226
+ }
963
1227
  function rewriteProtocolVersions(pkg, packageDir) {
964
1228
  let changed = false;
965
1229
  const result = { ...pkg };
@@ -968,7 +1232,8 @@ function rewriteProtocolVersions(pkg, packageDir) {
968
1232
  for (const depField of [
969
1233
  "dependencies",
970
1234
  "devDependencies",
971
- "peerDependencies"
1235
+ "peerDependencies",
1236
+ "optionalDependencies"
972
1237
  ]) {
973
1238
  const deps = pkg[depField];
974
1239
  if (!deps) continue;
@@ -978,7 +1243,8 @@ function rewriteProtocolVersions(pkg, packageDir) {
978
1243
  if (version.startsWith("workspace:")) {
979
1244
  const versionPart = version.slice("workspace:".length);
980
1245
  if (versionPart === "*" || versionPart === "^" || versionPart === "~") {
981
- newDeps[name] = versionPart === "*" ? pkg.version : versionPart + pkg.version;
1246
+ const depVersion = _cachedWorkspaceVersions?.versions.get(name) ?? pkg.version;
1247
+ newDeps[name] = versionPart === "*" ? depVersion : versionPart + depVersion;
982
1248
  } else {
983
1249
  newDeps[name] = versionPart;
984
1250
  }
@@ -986,7 +1252,7 @@ function rewriteProtocolVersions(pkg, packageDir) {
986
1252
  changed = true;
987
1253
  } else if (version.startsWith("catalog:")) {
988
1254
  if (!catalogsLoaded) {
989
- catalogs = loadCatalogsSync(packageDir);
1255
+ catalogs = loadCatalogsFromCache();
990
1256
  catalogsLoaded = true;
991
1257
  }
992
1258
  if (catalogs) {
@@ -1016,12 +1282,48 @@ function resolveCatalogVersion(specifier, depName, catalogs) {
1016
1282
  }
1017
1283
  return catalogs.named[catalogRef]?.[depName] ?? null;
1018
1284
  }
1285
+ var _cachedWorkspaceVersions = null;
1286
+ async function preloadWorkspaceVersions(pkg, packageDir) {
1287
+ const hasWorkspace = [
1288
+ "dependencies",
1289
+ "devDependencies",
1290
+ "peerDependencies",
1291
+ "optionalDependencies"
1292
+ ].some((field) => {
1293
+ const deps = pkg[field];
1294
+ return deps && Object.values(deps).some((v) => v.startsWith("workspace:"));
1295
+ });
1296
+ if (!hasWorkspace) return;
1297
+ const { findWorkspaceRoot: findWorkspaceRoot2, findWorkspacePackages: findWorkspacePackages2 } = await Promise.resolve().then(() => (init_workspace(), workspace_exports));
1298
+ const root = await findWorkspaceRoot2(packageDir);
1299
+ if (!root) {
1300
+ _cachedWorkspaceVersions = null;
1301
+ return;
1302
+ }
1303
+ if (_cachedWorkspaceVersions?.root === root) return;
1304
+ const pkgDirs = await findWorkspacePackages2(root);
1305
+ const versions = /* @__PURE__ */ new Map();
1306
+ await Promise.all(
1307
+ pkgDirs.map(async (dir) => {
1308
+ try {
1309
+ const depPkg = JSON.parse(
1310
+ await readFile5(join6(dir, "package.json"), "utf-8")
1311
+ );
1312
+ if (depPkg.name && depPkg.version) {
1313
+ versions.set(depPkg.name, depPkg.version);
1314
+ }
1315
+ } catch {
1316
+ }
1317
+ })
1318
+ );
1319
+ _cachedWorkspaceVersions = { root, versions };
1320
+ }
1019
1321
  var _cachedCatalogs = null;
1020
- function loadCatalogsSync(packageDir) {
1322
+ function loadCatalogsFromCache() {
1021
1323
  return _cachedCatalogs?.catalogs ?? null;
1022
1324
  }
1023
1325
  async function preloadCatalogs(pkg, packageDir) {
1024
- const hasCatalog = ["dependencies", "devDependencies", "peerDependencies"].some(
1326
+ const hasCatalog = ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"].some(
1025
1327
  (field) => {
1026
1328
  const deps = pkg[field];
1027
1329
  return deps && Object.values(deps).some((v) => v.startsWith("catalog:"));
@@ -1034,24 +1336,26 @@ async function preloadCatalogs(pkg, packageDir) {
1034
1336
  _cachedCatalogs = null;
1035
1337
  return;
1036
1338
  }
1037
- if (_cachedCatalogs?.root === root) return;
1339
+ const workspaceFile = join6(root, "pnpm-workspace.yaml");
1340
+ const mtimeMs = (await stat5(workspaceFile).catch(() => null))?.mtimeMs ?? 0;
1341
+ if (_cachedCatalogs?.root === root && _cachedCatalogs.mtimeMs === mtimeMs) return;
1038
1342
  const catalogs = await parseCatalogs2(root);
1039
- _cachedCatalogs = { root, catalogs };
1343
+ _cachedCatalogs = { root, mtimeMs, catalogs };
1040
1344
  }
1041
1345
 
1042
1346
  // src/core/injector.ts
1043
- import { readFile as readFile7, readdir as readdir4, realpath, stat as stat5 } from "fs/promises";
1044
- import { join as join11, resolve as resolve3 } from "path";
1045
- import { consola as consola6 } from "consola";
1347
+ init_console();
1348
+ import { readFile as readFile7, readdir as readdir5, realpath, stat as stat7 } from "fs/promises";
1349
+ import { join as join11, resolve as resolve5 } from "path";
1046
1350
  init_fs();
1047
1351
 
1048
1352
  // src/utils/bin-linker.ts
1353
+ init_console();
1049
1354
  init_fs();
1050
1355
  init_logger();
1051
- import { mkdir as mkdir3, symlink, writeFile as writeFile4, chmod, rm as rm2 } from "fs/promises";
1052
- import { join as join7, relative as relative5, resolve as resolve2, sep as sep2 } from "path";
1356
+ import { mkdir as mkdir3, symlink, writeFile as writeFile3, chmod, rm as rm3 } from "fs/promises";
1357
+ import { join as join7, relative as relative6, resolve as resolve4, sep as sep2 } from "path";
1053
1358
  import { platform as platform2 } from "os";
1054
- import { consola as consola5 } from "consola";
1055
1359
  function resolveBinEntries(pkg) {
1056
1360
  if (!pkg.bin) return {};
1057
1361
  if (typeof pkg.bin === "string") {
@@ -1070,12 +1374,12 @@ async function createBinLinks(consumerPath, packageName, pkg) {
1070
1374
  for (const [binName, binPath] of Object.entries(entries)) {
1071
1375
  const packageRoot = join7(consumerPath, "node_modules", packageName);
1072
1376
  const targetAbsolute = join7(packageRoot, binPath);
1073
- const resolvedTarget = resolve2(targetAbsolute);
1074
- if (!resolvedTarget.startsWith(resolve2(packageRoot) + sep2) && resolvedTarget !== resolve2(packageRoot)) {
1075
- consola5.warn(`bin "${binName}" points outside package directory, skipping`);
1377
+ const resolvedTarget = resolve4(targetAbsolute);
1378
+ if (!resolvedTarget.startsWith(resolve4(packageRoot) + sep2) && resolvedTarget !== resolve4(packageRoot)) {
1379
+ consola.warn(`bin "${binName}" points outside package directory, skipping`);
1076
1380
  continue;
1077
1381
  }
1078
- const targetRelative = relative5(binDir, targetAbsolute).replace(
1382
+ const targetRelative = relative6(binDir, targetAbsolute).replace(
1079
1383
  /\\/g,
1080
1384
  "/"
1081
1385
  );
@@ -1090,16 +1394,16 @@ EXIT /b\r
1090
1394
  CALL :find_dp0\r
1091
1395
  "%dp0%\\${targetRelative}" %*\r
1092
1396
  `;
1093
- await writeFile4(cmdPath, cmdContent);
1397
+ await writeFile3(cmdPath, cmdContent);
1094
1398
  const shPath = join7(binDir, binName);
1095
1399
  const shContent = `#!/bin/sh
1096
1400
  exec node "${targetRelative}" "$@"
1097
1401
  `;
1098
- await writeFile4(shPath, shContent);
1402
+ await writeFile3(shPath, shContent);
1099
1403
  } else {
1100
1404
  const linkPath = join7(binDir, binName);
1101
1405
  try {
1102
- await rm2(linkPath, { force: true });
1406
+ await rm3(linkPath, { force: true });
1103
1407
  } catch {
1104
1408
  }
1105
1409
  try {
@@ -1111,7 +1415,7 @@ exec node "${targetRelative}" "$@"
1111
1415
  const shContent = `#!/bin/sh
1112
1416
  exec node "${targetRelative}" "$@"
1113
1417
  `;
1114
- await writeFile4(linkPath, shContent);
1418
+ await writeFile3(linkPath, shContent);
1115
1419
  await chmod(linkPath, 493);
1116
1420
  } else {
1117
1421
  throw err;
@@ -1128,9 +1432,9 @@ async function removeBinLinks(consumerPath, pkg) {
1128
1432
  const isWindows = platform2() === "win32";
1129
1433
  for (const binName of Object.keys(entries)) {
1130
1434
  try {
1131
- await rm2(join7(binDir, binName), { force: true });
1435
+ await rm3(join7(binDir, binName), { force: true });
1132
1436
  if (isWindows) {
1133
- await rm2(join7(binDir, `${binName}.cmd`), { force: true });
1437
+ await rm3(join7(binDir, `${binName}.cmd`), { force: true });
1134
1438
  }
1135
1439
  } catch {
1136
1440
  }
@@ -1141,8 +1445,8 @@ async function removeBinLinks(consumerPath, pkg) {
1141
1445
  init_logger();
1142
1446
 
1143
1447
  // src/utils/pm-detect.ts
1144
- import { readFile as readFile6, stat as stat4 } from "fs/promises";
1145
- import { join as join8 } from "path";
1448
+ import { readFile as readFile6, stat as stat6 } from "fs/promises";
1449
+ import { join as join8, dirname as dirname5 } from "path";
1146
1450
  var LOCKFILES = [
1147
1451
  ["pnpm-lock.yaml", "pnpm"],
1148
1452
  ["bun.lockb", "bun"],
@@ -1151,37 +1455,50 @@ var LOCKFILES = [
1151
1455
  ["package-lock.json", "npm"]
1152
1456
  ];
1153
1457
  async function detectPackageManager(projectDir) {
1154
- const results = await Promise.all(
1155
- LOCKFILES.map(async ([lockfile, pm]) => {
1156
- try {
1157
- await stat4(join8(projectDir, lockfile));
1158
- return pm;
1159
- } catch {
1160
- return null;
1161
- }
1162
- })
1163
- );
1164
- return results.find((pm) => pm !== null) ?? "npm";
1458
+ let dir = projectDir;
1459
+ for (; ; ) {
1460
+ const results = await Promise.all(
1461
+ LOCKFILES.map(async ([lockfile, pm]) => {
1462
+ try {
1463
+ await stat6(join8(dir, lockfile));
1464
+ return pm;
1465
+ } catch {
1466
+ return null;
1467
+ }
1468
+ })
1469
+ );
1470
+ const found = results.find((pm) => pm !== null);
1471
+ if (found) return found;
1472
+ const parent = dirname5(dir);
1473
+ if (parent === dir) return "npm";
1474
+ dir = parent;
1475
+ }
1165
1476
  }
1166
1477
  async function detectYarnNodeLinker(projectDir) {
1167
- let content;
1168
- try {
1169
- content = await readFile6(join8(projectDir, ".yarnrc.yml"), "utf-8");
1170
- } catch {
1171
- return null;
1172
- }
1173
- for (const line of content.split("\n")) {
1174
- const trimmed = line.trim();
1175
- if (trimmed.startsWith("#") || !trimmed.includes("nodeLinker")) continue;
1176
- const match = trimmed.match(/^nodeLinker:\s*(.+)$/);
1177
- if (match) {
1178
- const value = match[1].trim().replace(/^["']|["']$/g, "");
1179
- if (value === "node-modules" || value === "pnpm" || value === "pnp") {
1180
- return value;
1478
+ let dir = projectDir;
1479
+ for (; ; ) {
1480
+ let content;
1481
+ try {
1482
+ content = await readFile6(join8(dir, ".yarnrc.yml"), "utf-8");
1483
+ } catch {
1484
+ const parent = dirname5(dir);
1485
+ if (parent === dir) return null;
1486
+ dir = parent;
1487
+ continue;
1488
+ }
1489
+ for (const line of content.split("\n")) {
1490
+ const trimmed = line.trim();
1491
+ if (trimmed.startsWith("#") || !trimmed.includes("nodeLinker")) continue;
1492
+ const match = trimmed.match(/^nodeLinker:\s*(.+)$/);
1493
+ if (match) {
1494
+ const value = match[1].trim().replace(/^["']|["']$/g, "");
1495
+ if (value === "node-modules" || value === "pnpm" || value === "pnp") {
1496
+ return value;
1497
+ }
1181
1498
  }
1182
1499
  }
1500
+ return null;
1183
1501
  }
1184
- return null;
1185
1502
  }
1186
1503
 
1187
1504
  // src/utils/bundler-cache.ts
@@ -1218,8 +1535,13 @@ var CACHE_DIRS = {
1218
1535
  next: [".next/cache"],
1219
1536
  webpack: ["node_modules/.cache"]
1220
1537
  };
1538
+ var bundlerCache = /* @__PURE__ */ new Map();
1221
1539
  async function invalidateBundlerCache(consumerPath) {
1222
- const bundlers = await detectAllBundlers(consumerPath);
1540
+ let bundlers = bundlerCache.get(consumerPath);
1541
+ if (!bundlers) {
1542
+ bundlers = await detectAllBundlers(consumerPath);
1543
+ bundlerCache.set(consumerPath, bundlers);
1544
+ }
1223
1545
  for (const bundler of bundlers) {
1224
1546
  if (!bundler.type) continue;
1225
1547
  const dirs = CACHE_DIRS[bundler.type];
@@ -1300,14 +1622,14 @@ async function checkMissingDeps(storeEntry, consumerPath) {
1300
1622
  )
1301
1623
  };
1302
1624
  if (Object.keys(allDeps).length === 0) return [];
1303
- const missing = [];
1304
- for (const dep of Object.keys(allDeps)) {
1305
- const depPath = join11(consumerPath, "node_modules", dep);
1306
- if (!await exists(depPath)) {
1307
- missing.push(dep);
1308
- }
1309
- }
1310
- return missing;
1625
+ const depNames = Object.keys(allDeps);
1626
+ const results = await Promise.all(
1627
+ depNames.map(async (dep) => ({
1628
+ dep,
1629
+ installed: await exists(join11(consumerPath, "node_modules", dep))
1630
+ }))
1631
+ );
1632
+ return results.filter((r) => !r.installed).map((r) => r.dep);
1311
1633
  }
1312
1634
  async function resolveTargetDir(consumerPath, packageName, pm, version) {
1313
1635
  const directPath = getNodeModulesPackagePath(consumerPath, packageName);
@@ -1317,13 +1639,13 @@ async function resolveTargetDir(consumerPath, packageName, pm, version) {
1317
1639
  }
1318
1640
  try {
1319
1641
  const realPath = await resolveRealPath(directPath);
1320
- if (realPath !== resolve3(directPath)) {
1642
+ if (realPath !== resolve5(directPath)) {
1321
1643
  verbose(`[inject] pnpm: resolved symlink \u2192 ${realPath}`);
1322
1644
  return realPath;
1323
1645
  }
1324
1646
  } catch (err) {
1325
1647
  if (isNodeError(err) && err.code !== "ENOENT") {
1326
- consola6.debug(`pnpm symlink resolution error: ${err}`);
1648
+ consola.debug(`pnpm symlink resolution error: ${err instanceof Error ? err.message : String(err)}`);
1327
1649
  }
1328
1650
  }
1329
1651
  const pnpmDir = join11(consumerPath, "node_modules", ".pnpm");
@@ -1338,7 +1660,7 @@ async function resolveTargetDir(consumerPath, packageName, pm, version) {
1338
1660
  return candidate;
1339
1661
  }
1340
1662
  }
1341
- const entries = await readdir4(pnpmDir);
1663
+ const entries = await readdir5(pnpmDir);
1342
1664
  for (const entry of entries) {
1343
1665
  if (entry.startsWith(encodedName + "@")) {
1344
1666
  const candidate = join11(
@@ -1354,16 +1676,16 @@ async function resolveTargetDir(consumerPath, packageName, pm, version) {
1354
1676
  }
1355
1677
  }
1356
1678
  }
1357
- consola6.warn(`pnpm: falling back to direct node_modules path for ${packageName}`);
1679
+ consola.warn(`pnpm: Could not find ${packageName} in .pnpm/ virtual store, using direct node_modules path`);
1358
1680
  return directPath;
1359
1681
  }
1360
1682
  async function resolveRealPath(linkPath) {
1361
1683
  try {
1362
- await stat5(linkPath);
1684
+ await stat7(linkPath);
1363
1685
  return await realpath(linkPath);
1364
1686
  } catch (err) {
1365
1687
  if (isNodeError(err) && err.code === "ENOENT") {
1366
- return resolve3(linkPath);
1688
+ return resolve5(linkPath);
1367
1689
  }
1368
1690
  throw err;
1369
1691
  }
@@ -1374,16 +1696,16 @@ async function readPackageJson(dir) {
1374
1696
  return JSON.parse(content);
1375
1697
  } catch (err) {
1376
1698
  if (isNodeError(err) && err.code !== "ENOENT") {
1377
- consola6.warn(`Failed to read package.json in ${dir}: ${err}`);
1699
+ consola.warn(`Failed to read package.json in ${dir}: ${err instanceof Error ? err.message : String(err)}`);
1378
1700
  }
1379
1701
  return null;
1380
1702
  }
1381
1703
  }
1382
1704
 
1383
1705
  // src/core/tracker.ts
1706
+ init_console();
1384
1707
  import { readFile as readFile8 } from "fs/promises";
1385
- import { dirname as dirname5 } from "path";
1386
- import { consola as consola7 } from "consola";
1708
+ import { dirname as dirname6 } from "path";
1387
1709
  init_fs();
1388
1710
  async function readConsumerState(consumerPath) {
1389
1711
  const statePath = getConsumerStatePath(consumerPath);
@@ -1391,13 +1713,13 @@ async function readConsumerState(consumerPath) {
1391
1713
  const content = await readFile8(statePath, "utf-8");
1392
1714
  const parsed = JSON.parse(content);
1393
1715
  if (!isConsumerState(parsed)) {
1394
- consola7.warn(`Invalid consumer state in ${statePath}, using defaults`);
1716
+ consola.warn(`Invalid consumer state in ${statePath}, using defaults`);
1395
1717
  return { version: "1", links: {} };
1396
1718
  }
1397
1719
  return parsed;
1398
1720
  } catch (err) {
1399
1721
  if (isNodeError(err) && err.code !== "ENOENT") {
1400
- consola7.warn(`Failed to read consumer state: ${err}`);
1722
+ consola.warn(`Failed to read consumer state: ${err instanceof Error ? err.message : String(err)}`);
1401
1723
  }
1402
1724
  return { version: "1", links: {} };
1403
1725
  }
@@ -1429,20 +1751,20 @@ async function readConsumersRegistry() {
1429
1751
  const content = await readFile8(regPath, "utf-8");
1430
1752
  const parsed = JSON.parse(content);
1431
1753
  if (!isConsumersRegistry(parsed)) {
1432
- consola7.warn(`Invalid consumers registry, using empty registry`);
1754
+ consola.warn(`Invalid consumers registry, using empty registry`);
1433
1755
  return {};
1434
1756
  }
1435
1757
  return parsed;
1436
1758
  } catch (err) {
1437
1759
  if (isNodeError(err) && err.code !== "ENOENT") {
1438
- consola7.warn(`Failed to read consumers registry: ${err}`);
1760
+ consola.warn(`Failed to read consumers registry: ${err instanceof Error ? err.message : String(err)}`);
1439
1761
  }
1440
1762
  return {};
1441
1763
  }
1442
1764
  }
1443
1765
  async function writeConsumersRegistry(registry) {
1444
1766
  const regPath = getConsumersPath();
1445
- await ensurePrivateDir(dirname5(getConsumersPath()));
1767
+ await ensurePrivateDir(dirname6(getConsumersPath()));
1446
1768
  await atomicWriteFile(regPath, JSON.stringify(registry, null, 2));
1447
1769
  }
1448
1770
  async function registerConsumer(packageName, consumerPath) {
@@ -1486,14 +1808,14 @@ async function cleanStaleConsumers() {
1486
1808
  const registry = await readConsumersRegistry();
1487
1809
  const updated = {};
1488
1810
  for (const [pkgName, consumers] of Object.entries(registry)) {
1489
- const validConsumers = [];
1490
- for (const consumerPath of consumers) {
1491
- if (await exists(consumerPath)) {
1492
- validConsumers.push(consumerPath);
1493
- } else {
1494
- removedConsumers++;
1495
- }
1496
- }
1811
+ const results = await Promise.all(
1812
+ consumers.map(async (consumerPath) => ({
1813
+ consumerPath,
1814
+ valid: await exists(consumerPath)
1815
+ }))
1816
+ );
1817
+ const validConsumers = results.filter((r) => r.valid).map((r) => r.consumerPath);
1818
+ removedConsumers += consumers.length - validConsumers.length;
1497
1819
  if (validConsumers.length > 0) {
1498
1820
  updated[pkgName] = validConsumers;
1499
1821
  } else {
@@ -1506,9 +1828,9 @@ async function cleanStaleConsumers() {
1506
1828
  }
1507
1829
 
1508
1830
  // src/core/watcher.ts
1831
+ init_console();
1509
1832
  import { spawn as spawn2 } from "child_process";
1510
1833
  import { platform as platform3 } from "os";
1511
- import { consola as consola8 } from "consola";
1512
1834
  var activeChild = null;
1513
1835
  var activeWatcher = null;
1514
1836
  function killActiveBuild() {
@@ -1525,12 +1847,14 @@ async function startWatcher(watchDir, options, onChange) {
1525
1847
  );
1526
1848
  const debounceMs = options.debounce ?? 100;
1527
1849
  let coalesceTimer = null;
1850
+ let closed = false;
1528
1851
  let running = false;
1529
1852
  let pendingWhileRunning = false;
1530
1853
  const scheduleFlush = () => {
1531
1854
  if (coalesceTimer) return;
1532
1855
  coalesceTimer = setTimeout(async () => {
1533
1856
  coalesceTimer = null;
1857
+ if (closed) return;
1534
1858
  if (running) {
1535
1859
  pendingWhileRunning = true;
1536
1860
  return;
@@ -1540,13 +1864,13 @@ async function startWatcher(watchDir, options, onChange) {
1540
1864
  if (options.buildCmd) {
1541
1865
  const success = await runBuildCommand(options.buildCmd, watchDir);
1542
1866
  if (!success) {
1543
- consola8.warn("Build failed, skipping push");
1867
+ consola.warn("Build failed (see output above), skipping push");
1544
1868
  return;
1545
1869
  }
1546
1870
  }
1547
1871
  await onChange();
1548
1872
  } catch (err) {
1549
- consola8.error("Push failed:", err);
1873
+ consola.error(`Push failed: ${err instanceof Error ? err.message : String(err)}`);
1550
1874
  } finally {
1551
1875
  running = false;
1552
1876
  if (pendingWhileRunning) {
@@ -1579,8 +1903,12 @@ async function startWatcher(watchDir, options, onChange) {
1579
1903
  watcher.on("change", onFileEvent);
1580
1904
  watcher.on("add", onFileEvent);
1581
1905
  watcher.on("unlink", onFileEvent);
1906
+ watcher.on("error", (err) => {
1907
+ consola.error(`Watcher error: ${err instanceof Error ? err.message : String(err)}`);
1908
+ });
1582
1909
  const watcherHandle = {
1583
1910
  close: async () => {
1911
+ closed = true;
1584
1912
  if (coalesceTimer) clearTimeout(coalesceTimer);
1585
1913
  killActiveBuild();
1586
1914
  await watcher.close();
@@ -1589,21 +1917,21 @@ async function startWatcher(watchDir, options, onChange) {
1589
1917
  };
1590
1918
  activeWatcher = watcherHandle;
1591
1919
  const cleanup = async () => {
1592
- consola8.info("Stopping watcher...");
1920
+ consola.info("Stopping watcher...");
1593
1921
  await watcherHandle.close();
1594
1922
  process.exit(0);
1595
1923
  };
1596
1924
  process.once("SIGINT", cleanup);
1597
1925
  process.once("SIGTERM", cleanup);
1598
- consola8.info(`Watching for changes in: ${patterns.join(", ")}`);
1926
+ consola.info(`Watching for changes in: ${patterns.join(", ")}`);
1599
1927
  return watcherHandle;
1600
1928
  }
1601
1929
  function runBuildCommand(cmd, cwd) {
1602
- return new Promise((resolve4) => {
1930
+ return new Promise((resolve6) => {
1603
1931
  const isWin = platform3() === "win32";
1604
1932
  const shell = isWin ? "cmd" : "sh";
1605
1933
  const shellFlag = isWin ? "/c" : "-c";
1606
- consola8.start(`Running: ${cmd}`);
1934
+ consola.start(`Running: ${cmd}`);
1607
1935
  const child = spawn2(shell, [shellFlag, cmd], {
1608
1936
  cwd,
1609
1937
  stdio: "inherit"
@@ -1612,17 +1940,17 @@ function runBuildCommand(cmd, cwd) {
1612
1940
  child.on("close", (code) => {
1613
1941
  activeChild = null;
1614
1942
  if (code === 0) {
1615
- consola8.success("Build succeeded");
1616
- resolve4(true);
1943
+ consola.success("Build succeeded");
1944
+ resolve6(true);
1617
1945
  } else {
1618
- consola8.error(`Build failed with code ${code}`);
1619
- resolve4(false);
1946
+ consola.error(`Build failed with code ${code}`);
1947
+ resolve6(false);
1620
1948
  }
1621
1949
  });
1622
1950
  child.on("error", (err) => {
1623
1951
  activeChild = null;
1624
- consola8.error(`Build error: ${err.message}`);
1625
- resolve4(false);
1952
+ consola.error(`Build error: ${err.message}`);
1953
+ resolve6(false);
1626
1954
  });
1627
1955
  });
1628
1956
  }