@lenne.tech/cli 1.50.0 → 1.50.2
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/build/commands/npm/reinit.js +6 -2
- package/build/config/vendor-runtime-deps.json +2 -2
- package/build/extensions/package-manager.js +0 -15
- package/build/extensions/server.js +2 -2
- package/build/lib/dev-log-tail.js +33 -3
- package/build/lib/dev-patches.js +35 -1
- package/build/lib/reinit-command.js +19 -0
- package/build/templates/check/check.mjs +74 -21
- package/build/templates/check/lt-monorepo-pin.json +4 -4
- package/package.json +25 -25
|
@@ -10,6 +10,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
const path_1 = require("path");
|
|
13
|
+
const reinit_command_1 = require("../../lib/reinit-command");
|
|
13
14
|
/**
|
|
14
15
|
* Reinitialize npm packages
|
|
15
16
|
*/
|
|
@@ -83,8 +84,11 @@ const NewCommand = {
|
|
|
83
84
|
if (system.which('rimraf')) {
|
|
84
85
|
yield system.run(pm.globalInstall('rimraf'));
|
|
85
86
|
}
|
|
86
|
-
|
|
87
|
-
|
|
87
|
+
yield system.run((0, reinit_command_1.buildReinitCommand)({
|
|
88
|
+
dir: (0, path_1.dirname)(path),
|
|
89
|
+
install: pm.install(detectedPm),
|
|
90
|
+
lockfile: pm.getLockfileName(detectedPm),
|
|
91
|
+
}));
|
|
88
92
|
reinitSpin.succeed();
|
|
89
93
|
if (data.scripts && data.scripts['test:e2e']) {
|
|
90
94
|
const testE2eSpin = spin('Run tests');
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$comment": "Upstream @lenne.tech/nest-server DEVDEPENDENCIES that must land in a vendored project's `dependencies`. Vendoring copies upstream `dependencies` verbatim and does not read `peerDependencies` at all, so a package the framework imports at runtime while keeping it in devDependencies would never reach the production runtime. Entries here are matched against upstream `devDependencies` only — see `isVendorRuntimeDep`, consulted in the `upstreamDevDeps` loop in `extensions/server.ts` — and promoted into `dependencies`, preferring the upstream peer RANGE over the exact devDep pin where one exists, so a generated project still receives patch-level security fixes. A package that appears ONLY in upstream `peerDependencies` cannot be promoted by this list: listing it here is a silent no-op.
|
|
3
|
-
"runtimeHelpers": ["@better-auth/core", "@better-auth/passkey", "better-auth"
|
|
2
|
+
"$comment": "Upstream @lenne.tech/nest-server DEVDEPENDENCIES that must land in a vendored project's `dependencies`. Vendoring copies upstream `dependencies` verbatim and does not read `peerDependencies` at all, so a package the framework imports at runtime while keeping it in devDependencies would never reach the production runtime. Entries here are matched against upstream `devDependencies` only — see `isVendorRuntimeDep`, consulted in the `upstreamDevDeps` loop in `extensions/server.ts` — and promoted into `dependencies`, preferring the upstream peer RANGE over the exact devDep pin where one exists, so a generated project still receives patch-level security fixes. A package that appears ONLY in upstream `peerDependencies` cannot be promoted by this list: listing it here is a silent no-op. One live case: the better-auth trio (declared as peerDependencies AND mirrored into devDependencies upstream; the mirror is what makes the promotion fire, so this list is the fallback if the starter ever stops declaring them itself). `find-file-up` was listed here until 2026-10-03 and never needed: no nest-server release from 8.x to 11.x imports it from src/ (only the dev script extras/sync-version.ts did), and 11.41.8 dropped the package altogether.",
|
|
3
|
+
"runtimeHelpers": ["@better-auth/core", "@better-auth/passkey", "better-auth"]
|
|
4
4
|
}
|
|
@@ -43,21 +43,6 @@ class PackageManager {
|
|
|
43
43
|
const manager = pm || this.detect();
|
|
44
44
|
return manager;
|
|
45
45
|
}
|
|
46
|
-
/**
|
|
47
|
-
* Get the cache clean command
|
|
48
|
-
* @param pm - Override detected package manager
|
|
49
|
-
*/
|
|
50
|
-
cacheClean(pm) {
|
|
51
|
-
const manager = pm || this.detect();
|
|
52
|
-
switch (manager) {
|
|
53
|
-
case 'pnpm':
|
|
54
|
-
return 'pnpm store prune';
|
|
55
|
-
case 'yarn':
|
|
56
|
-
return 'yarn cache clean';
|
|
57
|
-
default:
|
|
58
|
-
return 'npm cache clean --force';
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
46
|
/**
|
|
62
47
|
* Detect the package manager for the given directory
|
|
63
48
|
* Uses caching per directory for performance
|
|
@@ -1919,8 +1919,8 @@ class Server {
|
|
|
1919
1919
|
* dep in disguise that needs to live in `dependencies` after vendoring?
|
|
1920
1920
|
*
|
|
1921
1921
|
* `@lenne.tech/nest-server` keeps a few packages in devDependencies that
|
|
1922
|
-
* the framework code imports at runtime (e.g.
|
|
1923
|
-
*
|
|
1922
|
+
* the framework code imports at runtime (e.g. the better-auth trio, which
|
|
1923
|
+
* it mirrors from peerDependencies). When we vendor the framework source into a consumer
|
|
1924
1924
|
* project, those must end up in `dependencies` so the compiled/dist
|
|
1925
1925
|
* runtime has them available.
|
|
1926
1926
|
*
|
|
@@ -37,14 +37,23 @@ function describeLog(d) {
|
|
|
37
37
|
}
|
|
38
38
|
return [`Last lines of ${d.file}:`, ...d.tail.map((l) => ` ${l}`)];
|
|
39
39
|
}
|
|
40
|
-
/**
|
|
41
|
-
|
|
40
|
+
/**
|
|
41
|
+
* How much of a log's end is read. A dev log reached 1 GB (Vue warnings carry
|
|
42
|
+
* serialized vnode traces of several MB each), and reading all of it to show 20
|
|
43
|
+
* lines threw "Cannot create a string longer than 0x1fffffe8 characters".
|
|
44
|
+
*/
|
|
45
|
+
const TAIL_BYTES = 64 * 1024;
|
|
46
|
+
/** Longest line shown: one of those traces would otherwise flood the terminal. */
|
|
47
|
+
const MAX_LINE_LENGTH = 500;
|
|
48
|
+
/** Read the end of `file` and keep its last `lines` non-empty lines. */
|
|
49
|
+
function diagnoseLog(file, lines = 20, maxBytes = TAIL_BYTES) {
|
|
42
50
|
if (!(0, fs_1.existsSync)(file))
|
|
43
51
|
return { file, size: -1, state: 'missing', tail: [] };
|
|
44
52
|
const size = (0, fs_1.statSync)(file).size;
|
|
45
53
|
if (size === 0)
|
|
46
54
|
return { file, size, state: 'empty', tail: [] };
|
|
47
|
-
|
|
55
|
+
const tail = tailLines(readEnd(file, size, maxBytes), lines).map((l) => l.length > MAX_LINE_LENGTH ? `${l.slice(0, MAX_LINE_LENGTH)}…` : l);
|
|
56
|
+
return { file, size, state: 'ok', tail };
|
|
48
57
|
}
|
|
49
58
|
/**
|
|
50
59
|
* Components whose pid is gone within `budgetMs` of their start.
|
|
@@ -89,3 +98,24 @@ function tailLines(content, n) {
|
|
|
89
98
|
.filter((l) => l.trim().length > 0)
|
|
90
99
|
.slice(-n);
|
|
91
100
|
}
|
|
101
|
+
/**
|
|
102
|
+
* The last `maxBytes` of `file`. The first line of a cut window is partial and
|
|
103
|
+
* dropped, unless the window lies inside one line: then the fragment is kept,
|
|
104
|
+
* marked with a leading `…`.
|
|
105
|
+
*/
|
|
106
|
+
function readEnd(file, size, maxBytes) {
|
|
107
|
+
const length = Math.min(size, maxBytes);
|
|
108
|
+
const buffer = Buffer.alloc(length);
|
|
109
|
+
const fd = (0, fs_1.openSync)(file, 'r');
|
|
110
|
+
try {
|
|
111
|
+
(0, fs_1.readSync)(fd, buffer, 0, length, size - length);
|
|
112
|
+
}
|
|
113
|
+
finally {
|
|
114
|
+
(0, fs_1.closeSync)(fd);
|
|
115
|
+
}
|
|
116
|
+
const text = buffer.toString('utf8');
|
|
117
|
+
if (length === size)
|
|
118
|
+
return text;
|
|
119
|
+
const rest = text.slice(text.indexOf('\n') + 1);
|
|
120
|
+
return text.includes('\n') && rest.trim() ? rest : `…${text}`;
|
|
121
|
+
}
|
package/build/lib/dev-patches.js
CHANGED
|
@@ -180,7 +180,18 @@ function patchClaudeMd(file, options) {
|
|
|
180
180
|
if (startIdx !== -1 && endIdx !== -1 && endIdx > startIdx) {
|
|
181
181
|
const before = content.slice(0, startIdx);
|
|
182
182
|
const after = content.slice(endIdx + endMarker.length);
|
|
183
|
-
|
|
183
|
+
// Anything between the markers that is NOT this generated block is somebody's own writing,
|
|
184
|
+
// and it is kept — moved out in front of the block rather than overwritten.
|
|
185
|
+
//
|
|
186
|
+
// Replacing the span wholesale was silent data loss, observed in `offers`: a 29-line section
|
|
187
|
+
// of project documentation sat between the markers and one `lt dev up` deleted it, reporting
|
|
188
|
+
// only "updated CLAUDE.md URL block in 1 file(s)". Nothing about the markers suggests the
|
|
189
|
+
// span is owned — they are HTML comments, so they are invisible in every rendered view of the
|
|
190
|
+
// file, and a section appended after the preceding one lands inside them without anyone
|
|
191
|
+
// seeing it. The only reason it was noticed at all is that the file happened to be under
|
|
192
|
+
// review in the same minute.
|
|
193
|
+
const rescued = rescueForeignContent(content.slice(startIdx + startMarker.length, endIdx));
|
|
194
|
+
next = before + (rescued ? `${rescued}\n\n` : '') + block + after;
|
|
184
195
|
}
|
|
185
196
|
else {
|
|
186
197
|
const sep = content.endsWith('\n\n') ? '' : content.endsWith('\n') ? '\n' : '\n\n';
|
|
@@ -427,3 +438,26 @@ function normaliseBridgeBlock(s) {
|
|
|
427
438
|
.replace(/\s+/g, ' ')
|
|
428
439
|
.trim();
|
|
429
440
|
}
|
|
441
|
+
/**
|
|
442
|
+
* Pull out the sections between the lt-dev markers that this tool did not write.
|
|
443
|
+
*
|
|
444
|
+
* The generated block is one section, `## Local Development (lt dev)`. Every other `##` section
|
|
445
|
+
* found inside the markers was written by somebody else and is returned so the caller can place
|
|
446
|
+
* it outside the block. Whitespace-only remainders are dropped — that is the generated block's
|
|
447
|
+
* own padding, not content.
|
|
448
|
+
*
|
|
449
|
+
* Deliberately conservative about what counts as ours: a section is discarded only when its
|
|
450
|
+
* heading matches exactly. A renamed or hand-edited heading is treated as foreign and kept, which
|
|
451
|
+
* leaves a duplicate for a human to resolve. A duplicate is visible; a deletion is not.
|
|
452
|
+
*/
|
|
453
|
+
function rescueForeignContent(span) {
|
|
454
|
+
const own = '## Local Development (lt dev)';
|
|
455
|
+
const sections = span.split(/\n(?=## )/);
|
|
456
|
+
// Compare the whole heading line, not a prefix: `## Local Development (lt dev) — notes` is a
|
|
457
|
+
// section somebody wrote next to ours, and a prefix match would delete it.
|
|
458
|
+
const foreign = sections
|
|
459
|
+
.filter((section) => section.trimStart().split(/\r?\n/, 1)[0].trim() !== own)
|
|
460
|
+
.map((section) => section.trim())
|
|
461
|
+
.filter(Boolean);
|
|
462
|
+
return foreign.join('\n\n');
|
|
463
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildReinitCommand = buildReinitCommand;
|
|
4
|
+
/**
|
|
5
|
+
* Shell command for `lt npm reinit` in a project without its own `reinit` script:
|
|
6
|
+
* remove the lockfile and node_modules, then install fresh.
|
|
7
|
+
*
|
|
8
|
+
* Deliberately no cache step. `pnpm store prune` empties the machine-wide store on APFS
|
|
9
|
+
* (clone imports leave every store file with link count 1, which is exactly what prune
|
|
10
|
+
* deletes), and `npm cache clean --force` / `yarn cache clean` wipe the shared caches the
|
|
11
|
+
* same way — every other project then downloads again, and a parallel install loses the
|
|
12
|
+
* files it is linking. A fresh resolution needs neither: deleting the lockfile is what
|
|
13
|
+
* makes the package manager ask the registry again, and pnpm verifies store integrity
|
|
14
|
+
* on every install.
|
|
15
|
+
*/
|
|
16
|
+
function buildReinitCommand(opts) {
|
|
17
|
+
const { dir, install, lockfile } = opts;
|
|
18
|
+
return `cd ${dir} && rimraf ${lockfile} && rimraf node_modules && ${install}`;
|
|
19
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// @lt-check-wrapper 3.
|
|
2
|
+
// @lt-check-wrapper 3.14.1
|
|
3
3
|
/**
|
|
4
4
|
* Quiet, report-driven wrapper around the project `check` pipeline.
|
|
5
5
|
*
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
* scripts/check-wrapper-version.test.mjs fails when it drifts from
|
|
34
34
|
* package.json. In a generated project it keeps the release it came from.
|
|
35
35
|
*/
|
|
36
|
-
import { execFileSync,
|
|
36
|
+
import { execFileSync, spawn } from 'node:child_process';
|
|
37
37
|
import { readFileSync, realpathSync } from 'node:fs';
|
|
38
38
|
import { dirname, join } from 'node:path';
|
|
39
39
|
import { fileURLToPath } from 'node:url';
|
|
@@ -430,7 +430,21 @@ const IDLE_TIMEOUT_MS = (() => {
|
|
|
430
430
|
const RUNNING = new Set();
|
|
431
431
|
|
|
432
432
|
/**
|
|
433
|
-
*
|
|
433
|
+
* Whether a number may become a kill target at all.
|
|
434
|
+
*
|
|
435
|
+
* A spawn that failed leaves `child.pid` undefined. On POSIX that only made `pgrep` and
|
|
436
|
+
* `process.kill` throw, but on Windows it became `taskkill /PID undefined /T /F` — a kill
|
|
437
|
+
* command built from a word that is not a pid. And the small numbers are not processes you
|
|
438
|
+
* own: POSIX pid 1 is init/launchd, and 0 and -1 are the group and the kill(2) broadcast to
|
|
439
|
+
* every process of the user (the shape that rebooted a Mac on 2026-09-23). Windows reserves
|
|
440
|
+
* 0 (System Idle) and 4 (System). Nothing a check step spawns can have any of these pids.
|
|
441
|
+
*/
|
|
442
|
+
export function isKillablePid(pid, platform = process.platform) {
|
|
443
|
+
return Number.isInteger(pid) && pid > (platform === 'win32' ? 4 : 1);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* How to kill a process tree on this platform, or null when `pid` must not be touched.
|
|
434
448
|
*
|
|
435
449
|
* Windows has neither `pgrep` nor signals: `process.kill(pid, 'SIGTERM')` there ends the one
|
|
436
450
|
* process and orphans its children, and `taskkill /T` alone was measured to leave the tree
|
|
@@ -445,46 +459,85 @@ const RUNNING = new Set();
|
|
|
445
459
|
* Split out as a pure function so both branches can be tested from either platform.
|
|
446
460
|
*/
|
|
447
461
|
export function killTreePlan(pid, signal, platform = process.platform) {
|
|
462
|
+
if (!isKillablePid(pid, platform)) return null;
|
|
448
463
|
return platform === 'win32' ? { args: ['/PID', String(pid), '/T', '/F'], command: 'taskkill' } : { signal };
|
|
449
464
|
}
|
|
450
465
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
466
|
+
/**
|
|
467
|
+
* Kill `pid` and its whole process tree, with every effect injected.
|
|
468
|
+
*
|
|
469
|
+
* Injecting the platform alone is not enough: the effects would still run for real, against
|
|
470
|
+
* this machine, with whatever pid the test chose. So all four are required and nothing
|
|
471
|
+
* defaults — a test cannot reach a real `taskkill`, `pgrep` or `process.kill` by leaving one
|
|
472
|
+
* out. `killTree` below is the only caller that passes the real ones.
|
|
473
|
+
*
|
|
474
|
+
* - `run(command, args)` executes the Windows plan.
|
|
475
|
+
* - `childrenOf(pid)` returns the direct children of `pid` (POSIX).
|
|
476
|
+
* - `signal(pid, sig)` delivers `sig` to `pid` (POSIX).
|
|
477
|
+
*
|
|
478
|
+
* POSIX order is leaves first, so a parent cannot respawn a child that was just killed, and
|
|
479
|
+
* each pid is signalled at most once. Pids a lookup returns pass the same check as the root —
|
|
480
|
+
* a stray 1 in `pgrep` output must not become a signal to init.
|
|
481
|
+
*/
|
|
482
|
+
export function killTreeWith(pid, sig, { childrenOf, platform, run, signal }) {
|
|
483
|
+
for (const [name, fn] of Object.entries({ childrenOf, run, signal })) {
|
|
484
|
+
if (typeof fn !== 'function') throw new TypeError(`killTreeWith: \`${name}\` must be injected`);
|
|
485
|
+
}
|
|
486
|
+
if (typeof platform !== 'string') throw new TypeError('killTreeWith: `platform` must be injected');
|
|
487
|
+
const plan = killTreePlan(pid, sig, platform);
|
|
488
|
+
if (!plan) return;
|
|
457
489
|
if (plan.command) {
|
|
458
490
|
try {
|
|
459
|
-
|
|
491
|
+
run(plan.command, plan.args);
|
|
460
492
|
} catch {
|
|
461
493
|
/* already gone, or taskkill refused — nothing further to try */
|
|
462
494
|
}
|
|
463
495
|
return;
|
|
464
496
|
}
|
|
465
|
-
const
|
|
466
|
-
const
|
|
467
|
-
|
|
468
|
-
|
|
497
|
+
const seen = new Set();
|
|
498
|
+
const order = [];
|
|
499
|
+
const collect = (p) => {
|
|
500
|
+
if (seen.has(p) || !isKillablePid(p, platform)) return;
|
|
501
|
+
seen.add(p);
|
|
502
|
+
let children = [];
|
|
469
503
|
try {
|
|
470
|
-
|
|
471
|
-
.toString()
|
|
472
|
-
.trim();
|
|
504
|
+
children = childrenOf(p);
|
|
473
505
|
} catch {
|
|
474
506
|
/* no children */
|
|
475
507
|
}
|
|
476
|
-
|
|
508
|
+
for (const c of children) collect(c);
|
|
509
|
+
order.push(p);
|
|
477
510
|
};
|
|
478
|
-
collect(
|
|
479
|
-
for (const
|
|
511
|
+
collect(pid);
|
|
512
|
+
for (const p of order) {
|
|
480
513
|
try {
|
|
481
|
-
|
|
514
|
+
signal(p, plan.signal);
|
|
482
515
|
} catch {
|
|
483
516
|
/* already gone */
|
|
484
517
|
}
|
|
485
518
|
}
|
|
486
519
|
}
|
|
487
520
|
|
|
521
|
+
/** Direct children of `pid` via `pgrep -P`; empty when there are none (pgrep exits 1). */
|
|
522
|
+
function pgrepChildren(pid) {
|
|
523
|
+
const out = execFileSync('pgrep', ['-P', String(pid)], { stdio: ['ignore', 'pipe', 'ignore'] })
|
|
524
|
+
.toString()
|
|
525
|
+
.trim();
|
|
526
|
+
return out ? out.split('\n').map(Number) : [];
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
// Best-effort kill of a child's whole process tree (sh → pnpm → vitest →
|
|
530
|
+
// fork workers). Killing only the direct child orphans the tree — exactly the
|
|
531
|
+
// zombie workers a deadlock leaves behind.
|
|
532
|
+
function killTree(child, signal = 'SIGTERM') {
|
|
533
|
+
killTreeWith(child.pid, signal, {
|
|
534
|
+
childrenOf: pgrepChildren,
|
|
535
|
+
platform: process.platform,
|
|
536
|
+
run: (command, args) => execFileSync(command, args, { stdio: 'ignore' }),
|
|
537
|
+
signal: (pid, sig) => process.kill(pid, sig),
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
|
|
488
541
|
// idleTimeoutMs > 0 arms the no-output watchdog for this child; 0 (the default)
|
|
489
542
|
// runs it unwatched. Only callers that KNOW the child streams progress (test
|
|
490
543
|
// steps) should pass a timeout — see runGroup.
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
|
-
"commit": "
|
|
2
|
+
"commit": "26365f7c8acaa98bc48f360886055d0fe45c593d",
|
|
3
3
|
"files": {
|
|
4
4
|
"build-test-gate.mjs": "865bf1b9dd28151f53b9f2dfe4c3c46d29b9303fb1c2f497ce0e3136bcd37d71",
|
|
5
|
-
"check.mjs": "
|
|
5
|
+
"check.mjs": "6be5506f5fb63f53c65ea438c67d810de1afe1f3299828ef4531c37cc2457d4b",
|
|
6
6
|
"lib/ansi.mjs": "f997ee6e9bf1c7ee54ab612d4040fcc778064ec160eae19bf5c3960a75a6e2ff",
|
|
7
7
|
"lib/audit-report.mjs": "503825c3342dec1ee5d8d70e5af21b73e798faee320426b2492190cde0801df2",
|
|
8
8
|
"lib/workspace-packages.mjs": "ccdd133622a88186c924bc8271d35cf1f8357dc26d4a607998ca5e91fce5bf88"
|
|
9
9
|
},
|
|
10
|
-
"ref": "v3.
|
|
10
|
+
"ref": "v3.14.1",
|
|
11
11
|
"repository": "lenneTech/lt-monorepo",
|
|
12
|
-
"version": "3.
|
|
12
|
+
"version": "3.14.1"
|
|
13
13
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lenne.tech/cli",
|
|
3
|
-
"version": "1.50.
|
|
3
|
+
"version": "1.50.2",
|
|
4
4
|
"description": "lenne.Tech CLI: lt",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"lenne.Tech",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"lint:fix": "eslint \"./src/**/*.{ts,js,vue}\" --fix",
|
|
42
42
|
"prepublishOnly": "npm run build",
|
|
43
43
|
"preversion": "npm run lint",
|
|
44
|
-
"reinit": "npx rimraf package-lock.json && npx rimraf node_modules && npm
|
|
44
|
+
"reinit": "npx rimraf package-lock.json && npx rimraf node_modules && npm i && npm run build",
|
|
45
45
|
"snapupdate": "jest --updateSnapshot",
|
|
46
46
|
"start": "node bin/lt",
|
|
47
47
|
"start:build": "npm run build && node bin/lt --compiled-build",
|
|
@@ -58,12 +58,13 @@
|
|
|
58
58
|
"bin"
|
|
59
59
|
],
|
|
60
60
|
"dependencies": {
|
|
61
|
-
"@aws-sdk/client-s3": "3.
|
|
61
|
+
"@aws-sdk/client-s3": "3.1146.0",
|
|
62
62
|
"@lenne.tech/cli-plugin-helper": "0.0.14",
|
|
63
|
-
"axios": "1.
|
|
63
|
+
"axios": "1.20.0",
|
|
64
64
|
"bcrypt": "6.0.0",
|
|
65
65
|
"cross-spawn": "7.0.6",
|
|
66
|
-
"defuddle": "0.19.
|
|
66
|
+
"defuddle": "0.19.4",
|
|
67
|
+
"ejs": "6.0.1",
|
|
67
68
|
"glob": "13.0.6",
|
|
68
69
|
"gluegun": "5.2.2",
|
|
69
70
|
"js-sha256": "1.0.0",
|
|
@@ -71,8 +72,8 @@
|
|
|
71
72
|
"jsdom": "29.1.1",
|
|
72
73
|
"jsonc-parser": "3.3.1",
|
|
73
74
|
"lodash": "4.18.1",
|
|
74
|
-
"open": "11.0.
|
|
75
|
-
"playwright-core": "1.
|
|
75
|
+
"open": "11.0.4",
|
|
76
|
+
"playwright-core": "1.63.0",
|
|
76
77
|
"ts-morph": "28.0.0",
|
|
77
78
|
"ts-node": "10.9.2",
|
|
78
79
|
"turndown": "7.2.4",
|
|
@@ -84,8 +85,8 @@
|
|
|
84
85
|
"@emnapi/runtime": "Same as @emnapi/core."
|
|
85
86
|
},
|
|
86
87
|
"devDependencies": {
|
|
87
|
-
"@emnapi/core": "
|
|
88
|
-
"@emnapi/runtime": "
|
|
88
|
+
"@emnapi/core": "1.11.3",
|
|
89
|
+
"@emnapi/runtime": "1.11.3",
|
|
89
90
|
"@lenne.tech/eslint-config-ts": "2.3.0",
|
|
90
91
|
"@lenne.tech/npm-package-helper": "0.0.12",
|
|
91
92
|
"@types/cross-spawn": "6.0.6",
|
|
@@ -93,33 +94,32 @@
|
|
|
93
94
|
"@types/jest": "30.0.0",
|
|
94
95
|
"@types/js-yaml": "4.0.9",
|
|
95
96
|
"@types/jsdom": "28.0.1",
|
|
96
|
-
"@types/lodash": "4.17.
|
|
97
|
-
"@types/node": "26.
|
|
97
|
+
"@types/lodash": "4.17.25",
|
|
98
|
+
"@types/node": "26.6.4",
|
|
98
99
|
"@types/turndown": "5.0.6",
|
|
99
|
-
"
|
|
100
|
-
"eslint": "10.8.0",
|
|
100
|
+
"eslint": "10.12.0",
|
|
101
101
|
"husky": "9.1.7",
|
|
102
|
-
"jest": "30.
|
|
102
|
+
"jest": "30.5.2",
|
|
103
103
|
"minimatch": "10.2.6",
|
|
104
|
-
"prettier": "3.
|
|
104
|
+
"prettier": "3.9.9",
|
|
105
105
|
"rimraf": "6.1.3",
|
|
106
|
-
"ts-jest": "29.4.
|
|
106
|
+
"ts-jest": "29.4.14"
|
|
107
107
|
},
|
|
108
108
|
"//overrides": {
|
|
109
109
|
"semver@*": "Force latest semver 7.x across all sub-deps; gluegun@5.2.2 pins semver@7.7.0 which is stale - remove once gluegun updates its dep.",
|
|
110
|
-
"brace-expansion@<1.1.
|
|
111
|
-
"brace-expansion@>=2.0.0 <2.1.
|
|
112
|
-
"brace-expansion@>=5.0.0 <5.0.
|
|
113
|
-
"//minimatch-note": "brace-expansion 1.x and 2.x
|
|
114
|
-
"minimatch@>=4 <10": "Bounded raise of every minimatch 4.x-9.x install to 10.2.6, whose brace-expansion ^5.0.8 is patched.
|
|
115
|
-
"babel-plugin-istanbul > test-exclude@<8": "test-exclude 6.0.0 pins minimatch ^3.0.4 and glob ^7.
|
|
110
|
+
"brace-expansion@<1.1.21": "Five DoS advisories, all patched in the 1.x line by 1.1.21: exponential-time expansion of consecutive non-expanding {} groups (GHSA-3jxr-9vmj-r5cp, high, fixed 1.1.16), unbounded expansion length causing an OOM crash (GHSA-mh99-v99m-4gvg, high, fixed 1.1.17), uncontrolled recursion in parseCommaParts (GHSA-6j4f-fj2g-mc7p, high, fixed 1.1.19), uncontrolled recursion on nested brace groups (GHSA-qhr7-859c-m2p7, high, fixed 1.1.20) and quadratic-time `{a},b}` rewrite (GHSA-q2hr-2g5m-vwhr, medium, fixed 1.1.21). Raised from 1.1.18 on 2026-10-03: 1.1.18 was still open to the last three. Transitive via dotgitignore/eslint/fs-jetpack/glob/test-exclude/ts-morph > minimatch. Latent today: the minimatch raises below leave no 1.x install in the tree (only brace-expansion 5.0.12), so this key catches a future minimatch 3.x consumer. One bounded key per affected major so each can only raise a vulnerable version, never cap a patched one - remove once minimatch requests the patched ranges.",
|
|
111
|
+
"brace-expansion@>=2.0.0 <2.1.7": "Same five advisories, 2.x line (fixed 2.1.2 / 2.1.3 / 2.1.5 / 2.1.6 / 2.1.7). Raised from 2.1.4 on 2026-10-03. Latent today, like the 1.x key.",
|
|
112
|
+
"brace-expansion@>=5.0.0 <5.0.12": "Same advisories, 5.x line, plus three newer DoS advisories fixed only in 5.0.10-5.0.12 (GHSA-6j4f-fj2g-mc7p, GHSA-qhr7-859c-m2p7, GHSA-q2hr-2g5m-vwhr; all high). Reaches production via gluegun > fs-jetpack > minimatch. Floored at >=5.0.0 so a future 3.x/4.x dependency is not silently forced across two majors.",
|
|
113
|
+
"//minimatch-note": "Why the minimatch raises exist: when they were added, brace-expansion 1.x and 2.x were END OF LINE for GHSA-mh99-v99m-4gvg (advisory range <=5.0.7 across ALL majors, patched only 5.0.8+), so the only fix was to raise each CONSUMER off minimatch 3.x/5.x/9.x. That premise no longer holds: upstream has since backported every fix (GHSA-mh99 now reads <1.1.17 / >=2.0.0 <2.1.3, and 1.1.21 / 2.1.7 patch all five current advisories). Verified 2026-10-03 with fresh lockfile resolves: without `minimatch@>=4 <10` and `fs-jetpack > minimatch@<10` npm audit is still clean, the tree only gains minimatch 3.1.5 + 5.1.9 and brace-expansion 1.1.21 + 2.1.7 (7 packages). So both are now a DEDUPE measure (one minimatch 10.2.6, one brace-expansion 5.0.12), not a security fix - dropping them is a judgment call, it reopens no advisory. Forcing brace-expansion 5.x globally is still NOT an option: 5.x is ESM/tshy and exports an object ({ expand, ... }) while 1.x/2.x export the function itself, so minimatch 3.x/5.x would die on `expand is not a function`. Each scoped entry was verified against the consumer's actual call site before being added. The eslint / @eslint/eslintrc / @eslint/config-array entries were REMOVED in 1.42.0: eslint 10 requests minimatch ^10.2.5 itself, config-array ^10.2.4, and eslintrc is no longer in the tree at all.",
|
|
114
|
+
"minimatch@>=4 <10": "Bounded raise of every minimatch 4.x-9.x install to 10.2.6, whose brace-expansion ^5.0.8 is patched. As of 2026-10-03 the only consumer it still raises is filelist (gluegun > ejs 3.1.10 > jake > filelist, PRODUCTION-reachable, minimatch ^5.0.1): @typescript-eslint/typescript-estree 8.65 requests ^10.2.2 itself and the jest 30.5 packages no longer depend on minimatch. Verified safe: filelist calls only the static helper `minimatch.match(files, pat, opts)`. Floored at >=4 so the 3.x line is NOT swept in: minimatch 3.x is callable while 9.x/10.x export an object with __esModule but no `default` key, so sweeping it would break any consumer that calls the default export. Dedupe, not security (see //minimatch-note). Drop once filelist requests minimatch >=10.",
|
|
115
|
+
"babel-plugin-istanbul > test-exclude@<8": "Dedupe override, no longer a security fix. Added when babel-plugin-istanbul requested test-exclude ^6.0.0, whose 6.0.0 pins minimatch ^3.0.4 and glob ^7. Since jest 30.5 (babel-plugin-istanbul 8.0.0) the request is ^7.0.1, and test-exclude 7.0.2 already uses minimatch ^10.2.2: a fresh resolve without this entry audits clean (2026-10-03) but adds 23 packages (glob 10.5.0 and its subtree). 8.0.0 uses glob ^13, the major this project already ships, and is still CJS with the same `module.exports = TestExclude` class shape, so babel-plugin-istanbul 8's _interopRequireDefault + `new _testExclude.default(opts)` keeps working. Drop once babel-plugin-istanbul requests test-exclude ^8.",
|
|
116
116
|
"fs-jetpack > minimatch@<10": "Same chain, PRODUCTION-reachable (gluegun > fs-jetpack). Safe: lib/utils/matcher.js does `require('minimatch').Minimatch` and uses only `new Minimatch(pattern, { matchBase, nocomment, nocase, dot })`, `.negate` and `.match()` - all verified against 10.x. Not fixable by upgrading fs-jetpack: gluegun@5.2.2 is the latest release and fs-jetpack@5.1.0 still requests minimatch ^5.1.0 (brace-expansion ^2.0.1, also unpatched)."
|
|
117
117
|
},
|
|
118
118
|
"overrides": {
|
|
119
119
|
"semver@*": "7.8.5",
|
|
120
|
-
"brace-expansion@<1.1.
|
|
121
|
-
"brace-expansion@>=2.0.0 <2.1.
|
|
122
|
-
"brace-expansion@>=5.0.0 <5.0.
|
|
120
|
+
"brace-expansion@<1.1.21": "1.1.21",
|
|
121
|
+
"brace-expansion@>=2.0.0 <2.1.7": "2.1.7",
|
|
122
|
+
"brace-expansion@>=5.0.0 <5.0.12": "5.0.12",
|
|
123
123
|
"fs-jetpack": {
|
|
124
124
|
"minimatch@<10": "10.2.6"
|
|
125
125
|
},
|