@reliverse/dler 2.0.26 → 2.0.28

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.
@@ -0,0 +1,2 @@
1
+ declare const _default: any;
2
+ export default _default;
@@ -0,0 +1,341 @@
1
+ import { $ } from "bun";
2
+ import fs from "fs/promises";
3
+ import { defineCmd, defineCmdArgs, defineCmdCfg } from "@reliverse/dler-launcher";
4
+ import { logger } from "@reliverse/dler-logger";
5
+ const isWindows = () => globalThis.Bun?.platform?.() === "win32" || process.platform === "win32";
6
+ const fileExists = async (path) => {
7
+ try {
8
+ await fs.access(path);
9
+ return true;
10
+ } catch {
11
+ return false;
12
+ }
13
+ };
14
+ const normalizePathEntries = (raw) => {
15
+ const sep = isWindows() ? ";" : ":";
16
+ return raw.split(sep).map((s) => s.trim()).filter(Boolean);
17
+ };
18
+ const joinPathEntries = (entries) => isWindows() ? entries.join(";") : entries.join(":");
19
+ const normalizeEntry = (entry) => {
20
+ const trimmed = entry.trim();
21
+ if (isWindows()) {
22
+ return trimmed.replaceAll("/", "\\");
23
+ }
24
+ return trimmed;
25
+ };
26
+ const toComparable = (entry) => {
27
+ const normalized = normalizeEntry(entry);
28
+ return isWindows() ? normalized.toLowerCase() : normalized;
29
+ };
30
+ const uniqueByComparable = (entries) => {
31
+ const seen = /* @__PURE__ */ new Set();
32
+ const result = [];
33
+ for (const e of entries) {
34
+ const key = toComparable(e);
35
+ if (!seen.has(key)) {
36
+ seen.add(key);
37
+ result.push(e);
38
+ }
39
+ }
40
+ return result;
41
+ };
42
+ const backupFile = async (path) => {
43
+ try {
44
+ if (!await fileExists(path)) return;
45
+ const now = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
46
+ const random = Math.random().toString(36).substring(2, 8);
47
+ const bak = `${path}.bak.${now}.${random}`;
48
+ await fs.copyFile(path, bak);
49
+ logger.info(`Backup created: ${bak}`);
50
+ } catch (error) {
51
+ logger.warn(`Failed to create backup for ${path}: ${error}`);
52
+ }
53
+ };
54
+ const escapePowerShellString = (str) => {
55
+ return str.replace(/'/g, "''");
56
+ };
57
+ const runPowerShellGetUser = async (name) => {
58
+ const safeName = escapePowerShellString(name);
59
+ const ps = await $`powershell -NoProfile -Command [Environment]::GetEnvironmentVariable('${safeName}', 'User')`.quiet().text();
60
+ return ps;
61
+ };
62
+ const runPowerShellSetUser = async (name, value) => {
63
+ const safeName = escapePowerShellString(name);
64
+ const safeValue = escapePowerShellString(value);
65
+ await $`powershell -NoProfile -Command [Environment]::SetEnvironmentVariable('${safeName}', '${safeValue}', 'User')`.quiet();
66
+ };
67
+ const getHomeDirectory = () => {
68
+ if (isWindows()) {
69
+ return process.env.USERPROFILE || process.env.HOME || "";
70
+ }
71
+ return process.env.HOME || "";
72
+ };
73
+ const persistPosix = async (name, value) => {
74
+ const home = getHomeDirectory();
75
+ if (!home) {
76
+ throw new Error("Could not determine home directory");
77
+ }
78
+ const profile = `${home}/.profile`;
79
+ const escapedName = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
80
+ const re = new RegExp(`^\\s*export\\s+${escapedName}=.*$`, "m");
81
+ try {
82
+ const exists = await fileExists(profile);
83
+ if (!exists) {
84
+ await fs.writeFile(profile, `# created by dler senv
85
+ export ${name}="${value}"
86
+ `);
87
+ logger.info(`Wrote new ${profile}`);
88
+ return;
89
+ }
90
+ await backupFile(profile);
91
+ const content = await fs.readFile(profile, "utf8");
92
+ const newContent = re.test(content) ? content.replace(re, `export ${name}="${value}"`) : content + `
93
+ # added by dler senv
94
+ export ${name}="${value}"
95
+ `;
96
+ await fs.writeFile(profile, newContent);
97
+ logger.info(`Updated ${profile}`);
98
+ } catch (e) {
99
+ logger.error("Failed to persist to ~/.profile:");
100
+ logger.error(String(e));
101
+ throw e;
102
+ }
103
+ };
104
+ const persistPosixEditPath = async (name, entry, action) => {
105
+ const home = getHomeDirectory();
106
+ if (!home) {
107
+ throw new Error("Could not determine home directory");
108
+ }
109
+ const profile = `${home}/.profile`;
110
+ const escapedName = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
111
+ const re = new RegExp(`^\\s*export\\s+${escapedName}=["']?(.*)["']?$`, "m");
112
+ await backupFile(profile);
113
+ const exists = await fileExists(profile);
114
+ const content = exists ? await fs.readFile(profile, "utf8") : "";
115
+ const match = content.match(re);
116
+ const current = match ? match[1] : process.env[name] || "";
117
+ let entries = normalizePathEntries(current || "").map(normalizeEntry);
118
+ const normalizedEntry = normalizeEntry(entry);
119
+ if (action === "append") {
120
+ const set = new Set(entries.map(toComparable));
121
+ if (!set.has(toComparable(normalizedEntry))) {
122
+ entries.push(normalizedEntry);
123
+ }
124
+ } else {
125
+ const targetKey = toComparable(normalizedEntry);
126
+ const idx = entries.findIndex((e) => toComparable(e) === targetKey);
127
+ if (idx >= 0) entries.splice(idx, 1);
128
+ }
129
+ entries = uniqueByComparable(entries);
130
+ const newVal = joinPathEntries(entries);
131
+ const next = re.test(content) ? content.replace(re, `export ${name}="${newVal}"`) : content + `
132
+ # added by dler senv
133
+ export ${name}="${newVal}"
134
+ `;
135
+ await fs.writeFile(profile, next);
136
+ logger.info(`Persisted ${name} in ${profile}`);
137
+ };
138
+ const senvCmdArgs = defineCmdArgs({
139
+ action: {
140
+ type: "string",
141
+ required: true,
142
+ description: "Operation to perform: list|get|set|append|remove|contains"
143
+ },
144
+ name: {
145
+ type: "string",
146
+ description: "Environment variable name (optional for list)"
147
+ },
148
+ value: {
149
+ type: "string",
150
+ description: "Value for set/append/remove/contains"
151
+ },
152
+ persist: {
153
+ type: "boolean",
154
+ description: "Persist change to user environment (default: true)"
155
+ },
156
+ yes: {
157
+ type: "boolean",
158
+ description: "Skip interactive confirmation message"
159
+ }
160
+ });
161
+ const senvCmdCfg = defineCmdCfg({
162
+ name: "senv",
163
+ description: "Inspect and modify environment variables (process and user-level)",
164
+ examples: [
165
+ "dler senv --action list",
166
+ "dler senv --action list --name Path",
167
+ "dler senv --action get --name Path",
168
+ "dler senv --action set --name Path --value C\\\\bin",
169
+ "dler senv --action append --name Path --value C\\\\msys64\\\\ucrt64\\\\bin --yes",
170
+ "dler senv --action contains --name Path --value C\\\\bin"
171
+ ]
172
+ });
173
+ const senvCmd = async (args) => {
174
+ try {
175
+ if (typeof process.versions.bun === "undefined") {
176
+ logger.error("\u274C This command requires Bun runtime. Sorry.");
177
+ process.exit(1);
178
+ }
179
+ const { action, name, value } = args;
180
+ const persist = args.persist ?? true;
181
+ const yes = args.yes ?? false;
182
+ const allowed = /* @__PURE__ */ new Set(["list", "get", "set", "append", "remove", "contains"]);
183
+ if (!allowed.has(action)) {
184
+ logger.error("Unknown action. Allowed: list, get, set, append, remove, contains");
185
+ process.exit(2);
186
+ }
187
+ if (action === "list") {
188
+ if (name) {
189
+ logger.log(`${name}=${process.env[name] ?? ""}`);
190
+ } else {
191
+ for (const k of Object.keys(process.env).sort()) {
192
+ logger.log(`${k}=${process.env[k]}`);
193
+ }
194
+ }
195
+ return;
196
+ }
197
+ if (!name) {
198
+ logger.error("Name is required for this action");
199
+ process.exit(2);
200
+ }
201
+ if (action === "get") {
202
+ logger.log(process.env[name] ?? "");
203
+ return;
204
+ }
205
+ if (action === "contains") {
206
+ if (!value) {
207
+ logger.error("Value required for contains");
208
+ process.exit(2);
209
+ }
210
+ const cur2 = process.env[name] ?? "";
211
+ const entries2 = normalizePathEntries(cur2).map(normalizeEntry);
212
+ const keys = new Set(entries2.map(toComparable));
213
+ process.exit(keys.has(toComparable(value)) ? 0 : 1);
214
+ }
215
+ if (action === "set") {
216
+ if (typeof value === "undefined") {
217
+ logger.error("Value required for set");
218
+ process.exit(2);
219
+ }
220
+ process.env[name] = value;
221
+ logger.info(`Set ${name} for current process.`);
222
+ if (persist) {
223
+ if (!yes) {
224
+ logger.info("Persisting to user environment (will create backup). Use --yes to skip this message.");
225
+ }
226
+ if (isWindows()) {
227
+ try {
228
+ await runPowerShellSetUser(name, value);
229
+ logger.success(`Persisted ${name} to User environment (Windows).`);
230
+ } catch (e) {
231
+ logger.error("Failed to persist via PowerShell:");
232
+ logger.error(String(e));
233
+ }
234
+ } else {
235
+ try {
236
+ await persistPosix(name, value);
237
+ } catch (e) {
238
+ logger.error("Failed to persist on POSIX:");
239
+ logger.error(String(e));
240
+ }
241
+ }
242
+ }
243
+ return;
244
+ }
245
+ if (!value) {
246
+ logger.error("Value required for append/remove");
247
+ process.exit(2);
248
+ }
249
+ const cur = process.env[name] ?? "";
250
+ const normalizedValue = normalizeEntry(value);
251
+ let entries = normalizePathEntries(cur).map(normalizeEntry);
252
+ if (action === "append") {
253
+ const keySet = new Set(entries.map(toComparable));
254
+ const targetKey = toComparable(normalizedValue);
255
+ if (keySet.has(targetKey)) {
256
+ logger.info("Entry already present \u2014 nothing to do.");
257
+ } else {
258
+ entries.push(normalizedValue);
259
+ entries = uniqueByComparable(entries);
260
+ const newVal = joinPathEntries(entries);
261
+ process.env[name] = newVal;
262
+ logger.info(`Appended to ${name} for current process.`);
263
+ if (persist) {
264
+ if (isWindows()) {
265
+ try {
266
+ const userVal = (await runPowerShellGetUser(name)).trim();
267
+ const userEntries = normalizePathEntries(userVal || "").map(normalizeEntry);
268
+ const uSet = new Set(userEntries.map(toComparable));
269
+ if (!uSet.has(targetKey)) {
270
+ userEntries.push(normalizedValue);
271
+ const uniqueEntries = uniqueByComparable(userEntries);
272
+ const joined = joinPathEntries(uniqueEntries);
273
+ await runPowerShellSetUser(name, joined);
274
+ logger.success(`Persisted append to User ${name} (Windows).`);
275
+ } else {
276
+ logger.info("User-level already contains the entry \u2014 no change.");
277
+ }
278
+ } catch (e) {
279
+ logger.error("Failed to persist append on Windows:");
280
+ logger.error(String(e));
281
+ }
282
+ } else {
283
+ try {
284
+ await persistPosixEditPath(name, value, "append");
285
+ } catch (e) {
286
+ logger.error("Failed to persist append on POSIX:");
287
+ logger.error(String(e));
288
+ }
289
+ }
290
+ }
291
+ }
292
+ return;
293
+ }
294
+ if (action === "remove") {
295
+ const targetKey = toComparable(normalizedValue);
296
+ const idx = entries.findIndex((e) => toComparable(e) === targetKey);
297
+ if (idx === -1) {
298
+ logger.info("Entry not present \u2014 nothing to remove.");
299
+ } else {
300
+ entries.splice(idx, 1);
301
+ entries = uniqueByComparable(entries);
302
+ const newVal = joinPathEntries(entries);
303
+ process.env[name] = newVal;
304
+ logger.info(`Removed entry from ${name} for current process.`);
305
+ if (persist) {
306
+ if (isWindows()) {
307
+ try {
308
+ const userVal = (await runPowerShellGetUser(name)).trim();
309
+ const userEntries = normalizePathEntries(userVal || "").map(normalizeEntry);
310
+ const i2 = userEntries.findIndex((e) => toComparable(e) === targetKey);
311
+ if (i2 >= 0) {
312
+ userEntries.splice(i2, 1);
313
+ const uniqueEntries = uniqueByComparable(userEntries);
314
+ await runPowerShellSetUser(name, joinPathEntries(uniqueEntries));
315
+ logger.success(`Persisted removal to User ${name} (Windows).`);
316
+ } else {
317
+ logger.info("User-level did not contain entry \u2014 no change.");
318
+ }
319
+ } catch (e) {
320
+ logger.error("Failed to persist removal on Windows:");
321
+ logger.error(String(e));
322
+ }
323
+ } else {
324
+ try {
325
+ await persistPosixEditPath(name, value, "remove");
326
+ } catch (e) {
327
+ logger.error("Failed to persist removal on POSIX:");
328
+ logger.error(String(e));
329
+ }
330
+ }
331
+ }
332
+ }
333
+ return;
334
+ }
335
+ } catch (e) {
336
+ logger.error("Fatal:");
337
+ logger.error(String(e));
338
+ process.exit(3);
339
+ }
340
+ };
341
+ export default defineCmd(senvCmd, senvCmdArgs, senvCmdCfg);
package/package.json CHANGED
@@ -2,27 +2,27 @@
2
2
  "name": "@reliverse/dler",
3
3
  "description": "@reliverse/dler is a framework which helps TypeScript and JavaScript developers create their libraries and CLI tools. It provides ready-to-use primitives, so you don't have to write them from scratch.",
4
4
  "author": "reliverse",
5
- "version": "2.0.26",
5
+ "version": "2.0.28",
6
6
  "private": false,
7
7
  "type": "module",
8
8
  "bin": {
9
9
  "dler": "dist/cli.js"
10
10
  },
11
11
  "dependencies": {
12
- "c12": "^3.3.0",
12
+ "c12": "^3.3.1",
13
13
  "semver": "^7.7.3",
14
14
  "lookpath": "^1.2.3",
15
15
  "clipboardy": "^5.0.0",
16
- "@reliverse/dler-publish": "2.0.26",
17
- "@reliverse/dler-bump": "2.0.26",
18
- "@reliverse/dler-build": "2.0.26",
19
- "@reliverse/dler-logger": "2.0.26",
20
- "@reliverse/dler-matcher": "2.0.26",
21
- "@reliverse/dler-launcher": "2.0.26",
22
- "@reliverse/dler-prompt": "2.0.26",
23
- "@reliverse/dler-helpers": "2.0.26",
24
- "@reliverse/dler-pkg-tsc": "2.0.26",
25
- "@reliverse/dler-mapper": "2.0.26"
16
+ "@reliverse/dler-publish": "2.0.28",
17
+ "@reliverse/dler-bump": "2.0.28",
18
+ "@reliverse/dler-build": "2.0.28",
19
+ "@reliverse/dler-logger": "2.0.28",
20
+ "@reliverse/dler-matcher": "2.0.28",
21
+ "@reliverse/dler-launcher": "2.0.28",
22
+ "@reliverse/dler-prompt": "2.0.28",
23
+ "@reliverse/dler-helpers": "2.0.28",
24
+ "@reliverse/dler-pkg-tsc": "2.0.28",
25
+ "@reliverse/dler-mapper": "2.0.28"
26
26
  },
27
27
  "keywords": [
28
28
  "dler",
@@ -36,4 +36,4 @@
36
36
  "package.json"
37
37
  ],
38
38
  "license": "MIT"
39
- }
39
+ }