@lenne.tech/cli 1.37.1 → 1.38.0

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.
@@ -26,7 +26,12 @@ const workspace_integration_1 = require("../../lib/workspace-integration");
26
26
  * `lt ticket stop --keep-db` are never touched.
27
27
  * 2. STALE SHARD TEST DATABASES (`<base>-test-<n>` from `lt dev test --shard`)
28
28
  * when no test session is running — they are reset on every run anyway.
29
- * 3. DEAD REGISTRY ENTRIES (all projects): path no longer exists → the entry and
29
+ * 3. ORPHANED SMOKE-TEST DATABASES (global): the `/lt-dev:fullstack:smoke-test`
30
+ * throwaway projects use the reserved `lt-smoke-test` slug; their DBs are
31
+ * ephemeral by convention. Leftovers (e.g. a blocked drop during the run's
32
+ * cleanup) would be REUSED by the next run's stack and leak state between
33
+ * test runs — swept here whenever no live smoke-test run is registered.
34
+ * 4. DEAD REGISTRY ENTRIES (all projects): path no longer exists → the entry and
30
35
  * its reserved ports are reclaimed. Databases of dead MAIN projects are NEVER
31
36
  * dropped (a deleted folder is not consent to destroy data).
32
37
  *
@@ -62,9 +67,9 @@ const PruneCommand = {
62
67
  projectDevDb,
63
68
  slug,
64
69
  });
65
- const dbTargets = [...new Set([...plan.orphan.targets, ...plan.shardTargets])];
70
+ const dbTargets = [...new Set([...plan.orphan.targets, ...plan.shardTargets, ...plan.smokeTargets])];
66
71
  if (dbTargets.length === 0 && plan.registryPrune.length === 0) {
67
- success('Nothing to prune — no orphaned ticket DBs, no stale shard DBs, no dead registry entries.');
72
+ success('Nothing to prune — no orphaned ticket DBs, no stale shard DBs, no smoke-test DBs, no dead registry entries.');
68
73
  if (plan.orphan.protected.length > 0) {
69
74
  info(colors.dim(` kept (registry keptDbs / referenced): ${plan.orphan.protected.join(', ')}`));
70
75
  }
@@ -80,6 +85,10 @@ const PruneCommand = {
80
85
  info(colors.bold('Stale shard test databases (no test session running):'));
81
86
  plan.shardTargets.forEach((db) => info(colors.dim(` • ${db}`)));
82
87
  }
88
+ if (plan.smokeTargets.length > 0) {
89
+ info(colors.bold('Orphaned smoke-test databases (reserved lt-smoke-test prefix, no live run):'));
90
+ plan.smokeTargets.forEach((db) => info(colors.dim(` • ${db}`)));
91
+ }
83
92
  if (plan.orphan.protected.length > 0) {
84
93
  info(colors.dim(`Kept (recorded via --keep-db or still referenced): ${plan.orphan.protected.join(', ')}`));
85
94
  }
@@ -458,7 +458,7 @@ const UpCommand = {
458
458
  projectDevDb: (0, dev_project_1.deriveDbName)(mainLayout.apiDir, baseSlug),
459
459
  slug: baseSlug,
460
460
  });
461
- const dbTargets = [...new Set([...plan.orphan.targets, ...plan.shardTargets])];
461
+ const dbTargets = [...new Set([...plan.orphan.targets, ...plan.shardTargets, ...plan.smokeTargets])];
462
462
  if (dbTargets.length > 0) {
463
463
  const { dropped, reason } = (0, dev_ticket_1.dropDatabases)(dbTargets, undefined, [mainLayout.apiDir, mainRepoRoot]);
464
464
  if (dropped.length > 0) {
@@ -1,10 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SMOKE_TEST_DB_PREFIX = void 0;
3
4
  exports.collectDevPrunePlan = collectDevPrunePlan;
4
5
  exports.listLiveTicketIds = listLiveTicketIds;
5
6
  exports.listPastTicketIds = listPastTicketIds;
6
7
  exports.planOrphanTicketDbSweep = planOrphanTicketDbSweep;
7
8
  exports.planRegistryPrune = planRegistryPrune;
9
+ exports.planSmokeTestDbSweep = planSmokeTestDbSweep;
8
10
  exports.planStaleShardDbSweep = planStaleShardDbSweep;
9
11
  /**
10
12
  * Garbage collection for `lt dev` / `lt ticket` leftovers.
@@ -44,6 +46,13 @@ const dev_project_1 = require("./dev-project");
44
46
  const dev_ticket_1 = require("./dev-ticket");
45
47
  /** Branch prefix `lt ticket start` creates worktrees on. */
46
48
  const TICKET_BRANCH_PREFIX = 'feat/';
49
+ /**
50
+ * Slug convention of the lt-dev fullstack smoke test (`/lt-dev:fullstack:smoke-test`).
51
+ * Its throwaway projects are named `lt-smoke-test`, so every database under this
52
+ * prefix is ephemeral BY CONVENTION — reserve the prefix for smoke-test runs and
53
+ * never name a real project like this.
54
+ */
55
+ exports.SMOKE_TEST_DB_PREFIX = 'lt-smoke-test';
47
56
  /**
48
57
  * Collect the full prune plan for one project. Reads the registry and the Mongo
49
58
  * database listing; performs NO destructive action.
@@ -66,6 +75,7 @@ function collectDevPrunePlan(args) {
66
75
  projectDevDb,
67
76
  projectRoot: mainRepoRoot,
68
77
  }),
78
+ smokeTargets: planSmokeTestDbSweep({ observedDbNames, registry }),
69
79
  };
70
80
  }
71
81
  /** Ticket ids that still have a live environment (worktree and/or live registry entry). */
@@ -182,6 +192,47 @@ function planRegistryPrune(registry) {
182
192
  .filter(([, entry]) => entry.path && !(0, fs_1.existsSync)(entry.path))
183
193
  .map(([key]) => key);
184
194
  }
195
+ /**
196
+ * Orphaned smoke-test databases. The `/lt-dev:fullstack:smoke-test` command
197
+ * scaffolds throwaway projects under the reserved {@link SMOKE_TEST_DB_PREFIX}
198
+ * slug; their databases (`lt-smoke-test-local`, `lt-smoke-test-test`, ticket
199
+ * variants, …) are ephemeral by convention. When the run's cleanup could not
200
+ * drop them (e.g. a blocking policy hook) they linger and — worse — get
201
+ * REUSED by the next run's stack, leaking state between test runs (observed:
202
+ * a previous run's user made a fresh sign-up probe fail with
203
+ * "Email already registered").
204
+ *
205
+ * Fail-closed gates, in line with the rest of this module:
206
+ * - No server listing → empty plan.
207
+ * - A LIVE registry entry under the prefix (path exists ⇒ a smoke test is
208
+ * running right now) → empty plan; never pull the DB out from under an
209
+ * active run.
210
+ * - Names recorded as kept (`keptDbs`) or referenced by any live entry's
211
+ * `dbName` are never touched.
212
+ */
213
+ function planSmokeTestDbSweep(args) {
214
+ var _a;
215
+ const { observedDbNames, registry } = args;
216
+ if (!observedDbNames || observedDbNames.length === 0)
217
+ return [];
218
+ const entries = Object.entries(registry.projects);
219
+ const smokeRunLive = entries.some(([key, entry]) => (key === exports.SMOKE_TEST_DB_PREFIX || key.startsWith(`${exports.SMOKE_TEST_DB_PREFIX}-`)) &&
220
+ entry.path &&
221
+ (0, fs_1.existsSync)(entry.path));
222
+ if (smokeRunLive)
223
+ return [];
224
+ const protectedNames = new Set();
225
+ for (const [, entry] of entries) {
226
+ if (entry.dbName && entry.path && (0, fs_1.existsSync)(entry.path)) {
227
+ protectedNames.add(entry.dbName);
228
+ protectedNames.add((0, dev_project_1.deriveTestDbName)(entry.dbName));
229
+ }
230
+ for (const kept of (_a = entry.keptDbs) !== null && _a !== void 0 ? _a : []) {
231
+ protectedNames.add(kept);
232
+ }
233
+ }
234
+ return observedDbNames.filter((name) => (name === exports.SMOKE_TEST_DB_PREFIX || name.startsWith(`${exports.SMOKE_TEST_DB_PREFIX}-`)) && !protectedNames.has(name));
235
+ }
185
236
  /**
186
237
  * Stale sharded-test databases of the project itself (`<base>-test-<n>` from
187
238
  * `lt dev test --shard`). They are ephemeral by definition — every shard run
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lenne.tech/cli",
3
- "version": "1.37.1",
3
+ "version": "1.38.0",
4
4
  "description": "lenne.Tech CLI: lt",
5
5
  "keywords": [
6
6
  "lenne.Tech",
@@ -61,19 +61,19 @@
61
61
  "bin"
62
62
  ],
63
63
  "dependencies": {
64
- "@aws-sdk/client-s3": "3.1053.0",
64
+ "@aws-sdk/client-s3": "3.1090.0",
65
65
  "@lenne.tech/cli-plugin-helper": "0.0.14",
66
- "axios": "1.16.1",
66
+ "axios": "1.18.1",
67
67
  "bcrypt": "6.0.0",
68
- "defuddle": "0.18.1",
68
+ "defuddle": "0.19.1",
69
69
  "glob": "13.0.6",
70
70
  "gluegun": "5.2.2",
71
71
  "js-sha256": "0.11.1",
72
- "js-yaml": "4.2.0",
72
+ "js-yaml": "4.3.0",
73
73
  "jsdom": "29.1.1",
74
74
  "lodash": "4.18.1",
75
75
  "open": "11.0.0",
76
- "playwright-core": "1.60.0",
76
+ "playwright-core": "1.61.1",
77
77
  "ts-morph": "28.0.0",
78
78
  "ts-node": "10.9.2",
79
79
  "turndown": "7.2.4",
@@ -88,9 +88,9 @@
88
88
  "@types/js-yaml": "4.0.9",
89
89
  "@types/jsdom": "28.0.1",
90
90
  "@types/lodash": "4.17.24",
91
- "@types/node": "25.9.1",
91
+ "@types/node": "26.1.1",
92
92
  "@types/turndown": "5.0.6",
93
- "ejs": "5.0.2",
93
+ "ejs": "6.0.1",
94
94
  "eslint": "9.39.4",
95
95
  "husky": "9.1.7",
96
96
  "jest": "30.4.2",
@@ -100,20 +100,10 @@
100
100
  "ts-jest": "29.4.11"
101
101
  },
102
102
  "//overrides": {
103
- "brace-expansion@5.0.2 - 5.0.5": "Security fix: GHSA-jxxr-4gwj-5jf2 (large numeric range defeats max DoS protection) in brace-expansion 5.0.2-5.0.5 - transitive via minimatch under glob, @ts-morph/common, @typescript-eslint/typescript-estree. Remove once those parents resolve minimatch to a brace-expansion >=5.0.6.",
104
- "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.",
105
- "js-yaml": "Security fix: GHSA-h67p-54hq-rp68 (quadratic-complexity DoS in merge-key handling) in js-yaml <=4.1.1. Forces 4.2.0 everywhere, incl. the 3.14.2 pulled transitively by @istanbuljs/load-nyc-config under babel-plugin-istanbul (jest coverage); that loader only runs js-yaml when reading a YAML nyc config, which this project does not use. Remove once the jest toolchain resolves js-yaml >=4.2.0 itself.",
106
- "form-data": "Security fix: GHSA-hmw2-7cc7-3qxx (CRLF injection via unescaped multipart field names) in form-data 4.0.0-4.0.5 - transitive via axios. Remove once axios pins form-data >=4.0.6.",
107
- "@babel/core": "Security fix: GHSA-4x5r-pxfx-6jf8 (arbitrary file read via sourceMappingURL) in @babel/core <=7.29.0 - transitive via the jest toolchain. Remove once jest resolves @babel/core >=7.29.6.",
108
- "undici": "Security fix: GHSA-vmh5-mc38-953g (TLS certificate validation bypass via dropped requestTls in SOCKS5 ProxyAgent) and GHSA-pr7r-676h-xcf6 (cross-user info disclosure via shared cache whitespace bypass) in undici 7.0.0-7.27.2 - transitive via jsdom (jest test environment). Forces 7.28.0, which satisfies jsdom's ^7.25.0 range. Remove once jsdom resolves undici >=7.28.0."
103
+ "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."
109
104
  },
110
105
  "overrides": {
111
- "@babel/core": "7.29.7",
112
- "brace-expansion@5.0.2 - 5.0.5": "5.0.6",
113
- "form-data": "4.0.6",
114
- "js-yaml": "4.2.0",
115
- "semver@*": "7.8.1",
116
- "undici": "7.28.0"
106
+ "semver@*": "7.8.5"
117
107
  },
118
108
  "jest": {
119
109
  "testEnvironment": "node",
@@ -130,36 +120,5 @@
130
120
  }
131
121
  ]
132
122
  }
133
- },
134
- "pnpm": {
135
- "overrides": {
136
- "@apollo/server": "5.5.1",
137
- "@babel/plugin-transform-modules-systemjs": "7.29.4",
138
- "@hono/node-server": "1.19.14",
139
- "@protobufjs/utf8": "1.1.1",
140
- "ajv": "8.20.0",
141
- "axios": "1.16.0",
142
- "brace-expansion@<1.1.13": "1.1.14",
143
- "brace-expansion@>=2.0.0 <2.0.3": "2.1.0",
144
- "brace-expansion@>=5.0.0 <5.0.6": "5.0.6",
145
- "effect": "3.21.2",
146
- "fast-uri": "3.1.2",
147
- "hono": "4.12.22",
148
- "lodash": "4.18.1",
149
- "picomatch": "4.0.4",
150
- "qs": "6.15.2",
151
- "uuid": "14.0.0",
152
- "vite": "8.0.10",
153
- "ws@>=8.0.0 <8.20.1": "8.21.0"
154
- },
155
- "onlyBuiltDependencies": [
156
- "@apollo/protobufjs",
157
- "@compodoc/compodoc",
158
- "@nestjs/core",
159
- "@scarf/scarf",
160
- "@swc/core",
161
- "bcrypt",
162
- "esbuild"
163
- ]
164
123
  }
165
124
  }