@woosau/dsh-custom-env 0.1.8 → 0.1.10
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/README.md +28 -11
- package/README.zh-CN.md +28 -11
- package/lib/client.js +682 -408
- package/lib/index.js +337 -19
- package/lib/typert.host.js +407 -465
- package/lib/typert.remote-client.d.ts +2 -1
- package/lib/typert.remote-client.js +371 -373
- package/lib/types/client/EnvSettingsSection.d.ts +7 -3
- package/lib/types/client/locales.d.ts +22 -2
- package/lib/types/group-ids.d.ts +5 -0
- package/lib/types/groups-store.d.ts +73 -0
- package/lib/types/index.d.ts +40 -6
- package/lib/types/os-user-env/write-user-env-child.d.ts +11 -0
- package/lib/types/os-user-env/write-user-env.child.d.ts +4 -0
- package/lib/types/types.d.ts +22 -0
- package/lib/write-user-env.child.js +158 -0
- package/package.json +2 -1
package/lib/index.js
CHANGED
|
@@ -951,6 +951,171 @@ function removeEnvEntries(names) {
|
|
|
951
951
|
return removed;
|
|
952
952
|
}
|
|
953
953
|
|
|
954
|
+
// src/groups-store.ts
|
|
955
|
+
import { randomBytes } from "node:crypto";
|
|
956
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync4 } from "node:fs";
|
|
957
|
+
import { dirname as dirname6, join as join8 } from "node:path";
|
|
958
|
+
import { resolveDshHome as resolveDshHome2 } from "@deepseek-ai/dsh-home-paths";
|
|
959
|
+
|
|
960
|
+
// src/group-ids.ts
|
|
961
|
+
var DEFAULT_GROUP_ID = "default";
|
|
962
|
+
var DEFAULT_GROUP_TITLE = "\u9ED8\u8BA4\u5206\u7EC4";
|
|
963
|
+
|
|
964
|
+
// src/groups-store.ts
|
|
965
|
+
function groupsFilePath() {
|
|
966
|
+
return join8(resolveDshHome2(), ".dsh-custom-env-groups.json");
|
|
967
|
+
}
|
|
968
|
+
function defaultFile() {
|
|
969
|
+
return {
|
|
970
|
+
version: 1,
|
|
971
|
+
groups: [{ id: DEFAULT_GROUP_ID, title: DEFAULT_GROUP_TITLE }],
|
|
972
|
+
membership: {}
|
|
973
|
+
};
|
|
974
|
+
}
|
|
975
|
+
function isRecord(value) {
|
|
976
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
977
|
+
}
|
|
978
|
+
function normalizeGroupsFile(raw) {
|
|
979
|
+
if (!isRecord(raw)) return defaultFile();
|
|
980
|
+
const groupsIn = Array.isArray(raw.groups) ? raw.groups : [];
|
|
981
|
+
const groups = [];
|
|
982
|
+
const seen = /* @__PURE__ */ new Set();
|
|
983
|
+
let hasDefault = false;
|
|
984
|
+
for (const item of groupsIn) {
|
|
985
|
+
if (!isRecord(item)) continue;
|
|
986
|
+
const id = typeof item.id === "string" ? item.id.trim() : "";
|
|
987
|
+
const title = typeof item.title === "string" ? item.title.trim() : "";
|
|
988
|
+
if (id === "" || title === "" || seen.has(id)) continue;
|
|
989
|
+
seen.add(id);
|
|
990
|
+
if (id === DEFAULT_GROUP_ID) {
|
|
991
|
+
hasDefault = true;
|
|
992
|
+
groups.unshift({ id: DEFAULT_GROUP_ID, title: DEFAULT_GROUP_TITLE });
|
|
993
|
+
continue;
|
|
994
|
+
}
|
|
995
|
+
groups.push({ id, title });
|
|
996
|
+
}
|
|
997
|
+
if (!hasDefault) groups.unshift({ id: DEFAULT_GROUP_ID, title: DEFAULT_GROUP_TITLE });
|
|
998
|
+
const groupIds = new Set(groups.map((group) => group.id));
|
|
999
|
+
const membership = {};
|
|
1000
|
+
if (isRecord(raw.membership)) {
|
|
1001
|
+
for (const [entryId, groupId] of Object.entries(raw.membership)) {
|
|
1002
|
+
if (typeof groupId !== "string" || entryId.trim() === "" || groupId.trim() === "") continue;
|
|
1003
|
+
membership[entryId] = groupIds.has(groupId) ? groupId : DEFAULT_GROUP_ID;
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
return { version: 1, groups, membership };
|
|
1007
|
+
}
|
|
1008
|
+
function readGroupsFile() {
|
|
1009
|
+
const path = groupsFilePath();
|
|
1010
|
+
if (!existsSync8(path)) return defaultFile();
|
|
1011
|
+
try {
|
|
1012
|
+
return normalizeGroupsFile(JSON.parse(readFileSync3(path, "utf8")));
|
|
1013
|
+
} catch {
|
|
1014
|
+
return defaultFile();
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
function writeGroupsFile(file) {
|
|
1018
|
+
const path = groupsFilePath();
|
|
1019
|
+
mkdirSync4(dirname6(path), { recursive: true });
|
|
1020
|
+
const normalized = normalizeGroupsFile(file);
|
|
1021
|
+
writeFileSync4(path, `${JSON.stringify(normalized, null, 2)}
|
|
1022
|
+
`, "utf8");
|
|
1023
|
+
}
|
|
1024
|
+
function pruneMembership(file, liveIds) {
|
|
1025
|
+
let changed = false;
|
|
1026
|
+
const membership = {};
|
|
1027
|
+
for (const [entryId, groupId] of Object.entries(file.membership)) {
|
|
1028
|
+
if (!liveIds.has(entryId)) {
|
|
1029
|
+
changed = true;
|
|
1030
|
+
continue;
|
|
1031
|
+
}
|
|
1032
|
+
membership[entryId] = groupId;
|
|
1033
|
+
}
|
|
1034
|
+
return changed ? { ...file, membership } : file;
|
|
1035
|
+
}
|
|
1036
|
+
function resolveGroupId(file, entryId) {
|
|
1037
|
+
const groupId = file.membership[entryId];
|
|
1038
|
+
if (groupId === void 0) return DEFAULT_GROUP_ID;
|
|
1039
|
+
return file.groups.some((group) => group.id === groupId) ? groupId : DEFAULT_GROUP_ID;
|
|
1040
|
+
}
|
|
1041
|
+
function setMembership(file, entryId, groupId) {
|
|
1042
|
+
const nextId = file.groups.some((group) => group.id === groupId) ? groupId : DEFAULT_GROUP_ID;
|
|
1043
|
+
if (file.membership[entryId] === nextId) return file;
|
|
1044
|
+
return { ...file, membership: { ...file.membership, [entryId]: nextId } };
|
|
1045
|
+
}
|
|
1046
|
+
function clearMembership(file, entryId) {
|
|
1047
|
+
if (file.membership[entryId] === void 0) return file;
|
|
1048
|
+
const membership = { ...file.membership };
|
|
1049
|
+
delete membership[entryId];
|
|
1050
|
+
return { ...file, membership };
|
|
1051
|
+
}
|
|
1052
|
+
function clearMembershipMany(file, entryIds) {
|
|
1053
|
+
let next = file;
|
|
1054
|
+
for (const entryId of entryIds) next = clearMembership(next, entryId);
|
|
1055
|
+
return next;
|
|
1056
|
+
}
|
|
1057
|
+
function allocGroupId(existing) {
|
|
1058
|
+
for (; ; ) {
|
|
1059
|
+
const id = `g-${randomBytes(6).toString("hex")}`;
|
|
1060
|
+
if (!existing.has(id)) return id;
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
function createGroup(file, title) {
|
|
1064
|
+
const trimmed = title.trim();
|
|
1065
|
+
if (trimmed === "") throw new Error("empty group title");
|
|
1066
|
+
const existing = new Set(file.groups.map((group2) => group2.id));
|
|
1067
|
+
const group = { id: allocGroupId(existing), title: trimmed };
|
|
1068
|
+
return { file: { ...file, groups: [...file.groups, group] }, group };
|
|
1069
|
+
}
|
|
1070
|
+
function renameGroup(file, id, title) {
|
|
1071
|
+
if (id === DEFAULT_GROUP_ID) throw new Error("cannot rename default group");
|
|
1072
|
+
const trimmed = title.trim();
|
|
1073
|
+
if (trimmed === "") throw new Error("empty group title");
|
|
1074
|
+
const index = file.groups.findIndex((group) => group.id === id);
|
|
1075
|
+
if (index < 0) throw new Error(`unknown group "${id}"`);
|
|
1076
|
+
const groups = file.groups.map((group, i) => i === index ? { id, title: trimmed } : group);
|
|
1077
|
+
return { ...file, groups };
|
|
1078
|
+
}
|
|
1079
|
+
function deleteGroupMeta(file, id, moveToDefault) {
|
|
1080
|
+
if (id === DEFAULT_GROUP_ID) throw new Error("cannot delete default group");
|
|
1081
|
+
if (!file.groups.some((group) => group.id === id)) throw new Error(`unknown group "${id}"`);
|
|
1082
|
+
const memberIds = Object.entries(file.membership).filter(([, groupId]) => groupId === id).map(([entryId]) => entryId);
|
|
1083
|
+
const membership = { ...file.membership };
|
|
1084
|
+
for (const entryId of memberIds) {
|
|
1085
|
+
if (moveToDefault) membership[entryId] = DEFAULT_GROUP_ID;
|
|
1086
|
+
else delete membership[entryId];
|
|
1087
|
+
}
|
|
1088
|
+
return {
|
|
1089
|
+
file: {
|
|
1090
|
+
...file,
|
|
1091
|
+
groups: file.groups.filter((group) => group.id !== id),
|
|
1092
|
+
membership
|
|
1093
|
+
},
|
|
1094
|
+
memberIds
|
|
1095
|
+
};
|
|
1096
|
+
}
|
|
1097
|
+
function hasGroup(file, groupId) {
|
|
1098
|
+
return file.groups.some((group) => group.id === groupId);
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
// src/os-user-env/write-user-env-child.ts
|
|
1102
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
1103
|
+
import { existsSync as existsSync9 } from "node:fs";
|
|
1104
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
1105
|
+
function startWriteUserEnvChild(entries) {
|
|
1106
|
+
const scriptUrl = new URL("./write-user-env.child.js", import.meta.url);
|
|
1107
|
+
const script = fileURLToPath2(scriptUrl);
|
|
1108
|
+
if (!existsSync9(script)) return void 0;
|
|
1109
|
+
const child = spawn2(process.execPath, [script], {
|
|
1110
|
+
stdio: ["pipe", "ignore", "pipe"],
|
|
1111
|
+
env: { ...process.env, ELECTRON_RUN_AS_NODE: "1" },
|
|
1112
|
+
windowsHide: true
|
|
1113
|
+
});
|
|
1114
|
+
child.stdin?.end(JSON.stringify(entries), "utf8");
|
|
1115
|
+
child.stderr?.pipe(process.stderr);
|
|
1116
|
+
return child;
|
|
1117
|
+
}
|
|
1118
|
+
|
|
954
1119
|
// src/types.ts
|
|
955
1120
|
function classifyEntry(id, name, value, baselineById) {
|
|
956
1121
|
const base = baselineById.get(id);
|
|
@@ -975,6 +1140,36 @@ function applyOsUserEnv(entries) {
|
|
|
975
1140
|
`);
|
|
976
1141
|
}
|
|
977
1142
|
}
|
|
1143
|
+
function scheduleOsUserEnvSync(ctx, entries) {
|
|
1144
|
+
if (Object.keys(entries).length === 0) return;
|
|
1145
|
+
let child;
|
|
1146
|
+
const handle = setImmediate(() => {
|
|
1147
|
+
try {
|
|
1148
|
+
child = startWriteUserEnvChild(entries);
|
|
1149
|
+
if (child === void 0) {
|
|
1150
|
+
applyOsUserEnv(entries);
|
|
1151
|
+
return;
|
|
1152
|
+
}
|
|
1153
|
+
child.on("error", (error) => {
|
|
1154
|
+
process.stderr.write(
|
|
1155
|
+
`custom-env: OS user env child failed (${asMessage(error)}); falling back to sync write
|
|
1156
|
+
`
|
|
1157
|
+
);
|
|
1158
|
+
applyOsUserEnv(entries);
|
|
1159
|
+
});
|
|
1160
|
+
} catch (error) {
|
|
1161
|
+
process.stderr.write(
|
|
1162
|
+
`custom-env: could not start OS user env child (${asMessage(error)}); falling back to sync write
|
|
1163
|
+
`
|
|
1164
|
+
);
|
|
1165
|
+
applyOsUserEnv(entries);
|
|
1166
|
+
}
|
|
1167
|
+
});
|
|
1168
|
+
ctx.effect(() => () => {
|
|
1169
|
+
clearImmediate(handle);
|
|
1170
|
+
child?.kill();
|
|
1171
|
+
}, "custom-env: deferred OS env sync");
|
|
1172
|
+
}
|
|
978
1173
|
function writeLive(entries) {
|
|
979
1174
|
applyProcessEnv(entries);
|
|
980
1175
|
applyOsUserEnv(entries);
|
|
@@ -994,8 +1189,8 @@ function removeLives(names) {
|
|
|
994
1189
|
function removeLive(name) {
|
|
995
1190
|
removeLives([name]);
|
|
996
1191
|
}
|
|
997
|
-
var _cliInstall_dec, _cliStatus_dec, _restore_dec, _deleteEntry_dec, _removeVar_dec, _removeVars_dec, _setVar_dec, _setVars_dec, _upsert_dec, _list_dec, _a, _init;
|
|
998
|
-
var CustomEnvController = class extends (_a = TypertRemoteService, _list_dec = [Remote("list")], _upsert_dec = [Remote("upsert")], _setVars_dec = [Remote("setVars")], _setVar_dec = [Remote("setVar")], _removeVars_dec = [Remote("removeVars")], _removeVar_dec = [Remote("removeVar")], _deleteEntry_dec = [Remote("deleteEntry")], _restore_dec = [Remote("restore")], _cliStatus_dec = [Remote("cliStatus")], _cliInstall_dec = [Remote("cliInstall")], _a) {
|
|
1192
|
+
var _cliInstall_dec, _cliStatus_dec, _moveEntry_dec, _deleteGroup_dec, _renameGroup_dec, _createGroup_dec, _restore_dec, _deleteEntry_dec, _removeVar_dec, _removeVars_dec, _setVar_dec, _setVars_dec, _upsert_dec, _list_dec, _a, _init;
|
|
1193
|
+
var CustomEnvController = class extends (_a = TypertRemoteService, _list_dec = [Remote("list")], _upsert_dec = [Remote("upsert")], _setVars_dec = [Remote("setVars")], _setVar_dec = [Remote("setVar")], _removeVars_dec = [Remote("removeVars")], _removeVar_dec = [Remote("removeVar")], _deleteEntry_dec = [Remote("deleteEntry")], _restore_dec = [Remote("restore")], _createGroup_dec = [Remote("createGroup")], _renameGroup_dec = [Remote("renameGroup")], _deleteGroup_dec = [Remote("deleteGroup")], _moveEntry_dec = [Remote("moveEntry")], _cliStatus_dec = [Remote("cliStatus")], _cliInstall_dec = [Remote("cliInstall")], _a) {
|
|
999
1194
|
constructor(ctx) {
|
|
1000
1195
|
super(ctx, "customEnv");
|
|
1001
1196
|
__runInitializers(_init, 5, this);
|
|
@@ -1010,14 +1205,7 @@ var CustomEnvController = class extends (_a = TypertRemoteService, _list_dec = [
|
|
|
1010
1205
|
this.baselineById.set(id, { name, value });
|
|
1011
1206
|
}
|
|
1012
1207
|
applyProcessEnv(loaded);
|
|
1013
|
-
|
|
1014
|
-
const handle = setImmediate(() => {
|
|
1015
|
-
applyOsUserEnv(loaded);
|
|
1016
|
-
});
|
|
1017
|
-
ctx.effect(() => () => {
|
|
1018
|
-
clearImmediate(handle);
|
|
1019
|
-
}, "custom-env: deferred OS env sync");
|
|
1020
|
-
}
|
|
1208
|
+
scheduleOsUserEnvSync(ctx, loaded);
|
|
1021
1209
|
}
|
|
1022
1210
|
allocId() {
|
|
1023
1211
|
return `env-${++this.idSeq}`;
|
|
@@ -1040,6 +1228,19 @@ var CustomEnvController = class extends (_a = TypertRemoteService, _list_dec = [
|
|
|
1040
1228
|
}
|
|
1041
1229
|
return name;
|
|
1042
1230
|
}
|
|
1231
|
+
loadGroups() {
|
|
1232
|
+
return readGroupsFile();
|
|
1233
|
+
}
|
|
1234
|
+
saveGroups(file) {
|
|
1235
|
+
writeGroupsFile(file);
|
|
1236
|
+
}
|
|
1237
|
+
requireGroup(file, groupId) {
|
|
1238
|
+
const id = groupId.trim() === "" ? DEFAULT_GROUP_ID : groupId;
|
|
1239
|
+
if (!hasGroup(file, id)) {
|
|
1240
|
+
throw new RemoteError("custom-env/invalid-group", `unknown group "${id}"`, { group: id });
|
|
1241
|
+
}
|
|
1242
|
+
return id;
|
|
1243
|
+
}
|
|
1043
1244
|
/**
|
|
1044
1245
|
* Write `name=value` replacing `from` in place; sync process.env / OS.
|
|
1045
1246
|
* Empty values create missing keys only, unless `forceEmpty` (restore).
|
|
@@ -1066,6 +1267,7 @@ var CustomEnvController = class extends (_a = TypertRemoteService, _list_dec = [
|
|
|
1066
1267
|
for (const [name, id] of [...this.idByName]) {
|
|
1067
1268
|
if (!present.has(name)) this.unbind(id);
|
|
1068
1269
|
}
|
|
1270
|
+
let groupsFile = this.loadGroups();
|
|
1069
1271
|
const entries = raw.map(({ name, value }) => {
|
|
1070
1272
|
let id = this.idByName.get(name);
|
|
1071
1273
|
if (id === void 0) {
|
|
@@ -1073,31 +1275,58 @@ var CustomEnvController = class extends (_a = TypertRemoteService, _list_dec = [
|
|
|
1073
1275
|
this.bind(id, name);
|
|
1074
1276
|
}
|
|
1075
1277
|
const classified = classifyEntry(id, name, value, this.baselineById);
|
|
1076
|
-
|
|
1278
|
+
const groupId = resolveGroupId(groupsFile, id);
|
|
1279
|
+
return { id, name, value, groupId, kind: classified.kind, previous: classified.previous };
|
|
1077
1280
|
});
|
|
1078
|
-
|
|
1281
|
+
const liveIds = new Set(entries.map((entry) => entry.id));
|
|
1282
|
+
const pruned = pruneMembership(groupsFile, liveIds);
|
|
1283
|
+
if (pruned !== groupsFile) {
|
|
1284
|
+
this.saveGroups(pruned);
|
|
1285
|
+
groupsFile = pruned;
|
|
1286
|
+
}
|
|
1287
|
+
return { path, exists, groups: groupsFile.groups, entries };
|
|
1079
1288
|
}
|
|
1080
|
-
async upsert(id, name, value) {
|
|
1289
|
+
async upsert(id, name, value, groupId) {
|
|
1081
1290
|
if (id === "") {
|
|
1291
|
+
let groupsFile = this.loadGroups();
|
|
1292
|
+
const target = this.requireGroup(groupsFile, groupId ?? DEFAULT_GROUP_ID);
|
|
1082
1293
|
this.commit(name, name, value);
|
|
1083
|
-
|
|
1294
|
+
let rowId = this.idByName.get(name);
|
|
1295
|
+
if (rowId === void 0) {
|
|
1296
|
+
rowId = this.allocId();
|
|
1297
|
+
this.bind(rowId, name);
|
|
1298
|
+
}
|
|
1299
|
+
groupsFile = setMembership(groupsFile, rowId, target);
|
|
1300
|
+
this.saveGroups(groupsFile);
|
|
1084
1301
|
return this.list();
|
|
1085
1302
|
}
|
|
1086
1303
|
const from = this.requireName(id, name);
|
|
1087
1304
|
this.commit(from, name, value);
|
|
1088
1305
|
this.bind(id, name);
|
|
1306
|
+
if (groupId !== void 0) {
|
|
1307
|
+
let groupsFile = this.loadGroups();
|
|
1308
|
+
const target = this.requireGroup(groupsFile, groupId);
|
|
1309
|
+
groupsFile = setMembership(groupsFile, id, target);
|
|
1310
|
+
this.saveGroups(groupsFile);
|
|
1311
|
+
}
|
|
1089
1312
|
return this.list();
|
|
1090
1313
|
}
|
|
1091
1314
|
async setVars(entries) {
|
|
1092
1315
|
if (entries.length === 0) return;
|
|
1316
|
+
let groupsFile = this.loadGroups();
|
|
1093
1317
|
const lastByName = /* @__PURE__ */ new Map();
|
|
1094
1318
|
for (const entry of entries) {
|
|
1095
1319
|
if (!isWritableEnvName(entry.name)) {
|
|
1096
1320
|
throw new RemoteError("custom-env/invalid-name", `refused name "${entry.name}"`, { name: entry.name });
|
|
1097
1321
|
}
|
|
1098
|
-
|
|
1322
|
+
if (entry.group !== void 0) {
|
|
1323
|
+
const group = this.requireGroup(groupsFile, entry.group);
|
|
1324
|
+
lastByName.set(entry.name, { value: entry.value, group });
|
|
1325
|
+
} else {
|
|
1326
|
+
lastByName.set(entry.name, { value: entry.value });
|
|
1327
|
+
}
|
|
1099
1328
|
}
|
|
1100
|
-
const normalized = [...lastByName].map(([name, value]) => ({ name, value }));
|
|
1329
|
+
const normalized = [...lastByName].map(([name, { value }]) => ({ name, value }));
|
|
1101
1330
|
let written;
|
|
1102
1331
|
try {
|
|
1103
1332
|
written = writeEnvEntries(normalized);
|
|
@@ -1107,14 +1336,29 @@ var CustomEnvController = class extends (_a = TypertRemoteService, _list_dec = [
|
|
|
1107
1336
|
}
|
|
1108
1337
|
if (written.length === 0) return;
|
|
1109
1338
|
const live = {};
|
|
1339
|
+
let membershipChanged = false;
|
|
1110
1340
|
for (const entry of written) {
|
|
1111
1341
|
live[entry.name] = entry.value;
|
|
1112
|
-
|
|
1342
|
+
let rowId = this.idByName.get(entry.name);
|
|
1343
|
+
const isNewRow = rowId === void 0;
|
|
1344
|
+
if (rowId === void 0) {
|
|
1345
|
+
rowId = this.allocId();
|
|
1346
|
+
this.bind(rowId, entry.name);
|
|
1347
|
+
}
|
|
1348
|
+
const explicitGroup = lastByName.get(entry.name)?.group;
|
|
1349
|
+
if (explicitGroup !== void 0) {
|
|
1350
|
+
groupsFile = setMembership(groupsFile, rowId, explicitGroup);
|
|
1351
|
+
membershipChanged = true;
|
|
1352
|
+
} else if (isNewRow) {
|
|
1353
|
+
groupsFile = setMembership(groupsFile, rowId, DEFAULT_GROUP_ID);
|
|
1354
|
+
membershipChanged = true;
|
|
1355
|
+
}
|
|
1113
1356
|
}
|
|
1357
|
+
if (membershipChanged) this.saveGroups(groupsFile);
|
|
1114
1358
|
writeLive(live);
|
|
1115
1359
|
}
|
|
1116
|
-
async setVar(name, value) {
|
|
1117
|
-
await this.setVars([{ name, value }]);
|
|
1360
|
+
async setVar(name, value, group) {
|
|
1361
|
+
await this.setVars([{ name, value, group }]);
|
|
1118
1362
|
}
|
|
1119
1363
|
async removeVars(names) {
|
|
1120
1364
|
if (names.length === 0) return 0;
|
|
@@ -1128,6 +1372,11 @@ var CustomEnvController = class extends (_a = TypertRemoteService, _list_dec = [
|
|
|
1128
1372
|
seen.add(name);
|
|
1129
1373
|
unique.push(name);
|
|
1130
1374
|
}
|
|
1375
|
+
const dropIds = [];
|
|
1376
|
+
for (const name of unique) {
|
|
1377
|
+
const id = this.idByName.get(name);
|
|
1378
|
+
if (id !== void 0) dropIds.push(id);
|
|
1379
|
+
}
|
|
1131
1380
|
let removed;
|
|
1132
1381
|
try {
|
|
1133
1382
|
removed = removeEnvEntries(unique);
|
|
@@ -1140,6 +1389,9 @@ var CustomEnvController = class extends (_a = TypertRemoteService, _list_dec = [
|
|
|
1140
1389
|
const id = this.idByName.get(name);
|
|
1141
1390
|
if (id !== void 0) this.unbind(id);
|
|
1142
1391
|
}
|
|
1392
|
+
if (dropIds.length > 0) {
|
|
1393
|
+
this.saveGroups(clearMembershipMany(this.loadGroups(), dropIds));
|
|
1394
|
+
}
|
|
1143
1395
|
return removed;
|
|
1144
1396
|
}
|
|
1145
1397
|
async removeVar(name) {
|
|
@@ -1169,6 +1421,66 @@ var CustomEnvController = class extends (_a = TypertRemoteService, _list_dec = [
|
|
|
1169
1421
|
this.bind(id, base.name);
|
|
1170
1422
|
return this.list();
|
|
1171
1423
|
}
|
|
1424
|
+
async createGroup(title) {
|
|
1425
|
+
try {
|
|
1426
|
+
const { file, group } = createGroup(this.loadGroups(), title);
|
|
1427
|
+
this.saveGroups(file);
|
|
1428
|
+
return group;
|
|
1429
|
+
} catch (error) {
|
|
1430
|
+
throw new RemoteError("custom-env/invalid-group", asMessage(error), { group: title }, { cause: error });
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
async renameGroup(id, title) {
|
|
1434
|
+
try {
|
|
1435
|
+
const file = renameGroup(this.loadGroups(), id, title);
|
|
1436
|
+
this.saveGroups(file);
|
|
1437
|
+
const group = file.groups.find((item) => item.id === id);
|
|
1438
|
+
if (group === void 0) {
|
|
1439
|
+
throw new RemoteError("custom-env/invalid-group", `unknown group "${id}"`, { group: id });
|
|
1440
|
+
}
|
|
1441
|
+
return group;
|
|
1442
|
+
} catch (error) {
|
|
1443
|
+
if (error instanceof RemoteError) throw error;
|
|
1444
|
+
throw new RemoteError("custom-env/invalid-group", asMessage(error), { group: id }, { cause: error });
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
async deleteGroup(id, options) {
|
|
1448
|
+
let meta;
|
|
1449
|
+
try {
|
|
1450
|
+
meta = deleteGroupMeta(this.loadGroups(), id, options.moveToDefault);
|
|
1451
|
+
} catch (error) {
|
|
1452
|
+
throw new RemoteError("custom-env/invalid-group", asMessage(error), { group: id }, { cause: error });
|
|
1453
|
+
}
|
|
1454
|
+
if (!options.moveToDefault && meta.memberIds.length > 0) {
|
|
1455
|
+
const names = [];
|
|
1456
|
+
for (const entryId of meta.memberIds) {
|
|
1457
|
+
const name = this.nameById.get(entryId);
|
|
1458
|
+
if (name !== void 0) names.push(name);
|
|
1459
|
+
}
|
|
1460
|
+
if (names.length > 0) {
|
|
1461
|
+
try {
|
|
1462
|
+
removeEnvEntries(names);
|
|
1463
|
+
} catch (error) {
|
|
1464
|
+
throw new RemoteError("custom-env/invalid-name", asMessage(error), { name: names[0] }, { cause: error });
|
|
1465
|
+
}
|
|
1466
|
+
removeLives(names);
|
|
1467
|
+
for (const name of names) {
|
|
1468
|
+
const entryId = this.idByName.get(name);
|
|
1469
|
+
if (entryId !== void 0) this.unbind(entryId);
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
this.saveGroups(meta.file);
|
|
1474
|
+
return this.list();
|
|
1475
|
+
}
|
|
1476
|
+
async moveEntry(id, groupId) {
|
|
1477
|
+
this.requireName(id, id);
|
|
1478
|
+
let groupsFile = this.loadGroups();
|
|
1479
|
+
const target = this.requireGroup(groupsFile, groupId);
|
|
1480
|
+
groupsFile = setMembership(groupsFile, id, target);
|
|
1481
|
+
this.saveGroups(groupsFile);
|
|
1482
|
+
return this.list();
|
|
1483
|
+
}
|
|
1172
1484
|
async cliStatus() {
|
|
1173
1485
|
return cliStatus();
|
|
1174
1486
|
}
|
|
@@ -1190,10 +1502,16 @@ __decorateElement(_init, 1, "removeVars", _removeVars_dec, CustomEnvController);
|
|
|
1190
1502
|
__decorateElement(_init, 1, "removeVar", _removeVar_dec, CustomEnvController);
|
|
1191
1503
|
__decorateElement(_init, 1, "deleteEntry", _deleteEntry_dec, CustomEnvController);
|
|
1192
1504
|
__decorateElement(_init, 1, "restore", _restore_dec, CustomEnvController);
|
|
1505
|
+
__decorateElement(_init, 1, "createGroup", _createGroup_dec, CustomEnvController);
|
|
1506
|
+
__decorateElement(_init, 1, "renameGroup", _renameGroup_dec, CustomEnvController);
|
|
1507
|
+
__decorateElement(_init, 1, "deleteGroup", _deleteGroup_dec, CustomEnvController);
|
|
1508
|
+
__decorateElement(_init, 1, "moveEntry", _moveEntry_dec, CustomEnvController);
|
|
1193
1509
|
__decorateElement(_init, 1, "cliStatus", _cliStatus_dec, CustomEnvController);
|
|
1194
1510
|
__decorateElement(_init, 1, "cliInstall", _cliInstall_dec, CustomEnvController);
|
|
1195
1511
|
__decoratorMetadata(_init, CustomEnvController);
|
|
1196
1512
|
export {
|
|
1513
|
+
DEFAULT_GROUP_ID,
|
|
1514
|
+
DEFAULT_GROUP_TITLE,
|
|
1197
1515
|
classifyEntry,
|
|
1198
1516
|
CustomEnvController as default,
|
|
1199
1517
|
envFilePath
|