@elementspanel/epanel-plugin-file 0.0.0-stage → 1.0.1

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,4395 @@
1
+ /******/ (() => { // webpackBootstrap
2
+ /******/ "use strict";
3
+ /******/ var __webpack_modules__ = ({
4
+
5
+ /***/ "../common/src/array.ts"
6
+ (__unused_webpack_module, exports) {
7
+
8
+
9
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
10
+ exports.arrayUnique = arrayUnique;
11
+ function arrayUnique(arr, felidName) {
12
+ if (!felidName)
13
+ return Array.from(new Set(arr));
14
+ const map = new Map();
15
+ return arr.filter((v) => !map.has(v[felidName]) && map.set(v[felidName], v));
16
+ }
17
+
18
+
19
+ /***/ },
20
+
21
+ /***/ "../common/src/docker_utils.ts"
22
+ (__unused_webpack_module, exports) {
23
+
24
+
25
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
26
+ exports.normalizeDockerArchitecture = normalizeDockerArchitecture;
27
+ exports.normalizeDockerOS = normalizeDockerOS;
28
+ exports.normalizeDockerPlatform = normalizeDockerPlatform;
29
+ /**
30
+ * Normalizes Docker platform architecture names to standard format
31
+ * @param architecture The architecture string from Docker manifest
32
+ * @returns Normalized architecture string (amd64, arm64, arm, or original if not recognized)
33
+ */
34
+ function normalizeDockerArchitecture(architecture) {
35
+ if (!architecture) {
36
+ return "amd64"; // default fallback
37
+ }
38
+ // Return original if not one of the standard architectures
39
+ return architecture;
40
+ }
41
+ /**
42
+ * Normalizes Docker platform OS name
43
+ * @param os The OS string from Docker manifest
44
+ * @returns Normalized OS string (defaults to "linux" if not provided)
45
+ */
46
+ function normalizeDockerOS(os) {
47
+ return os || "linux";
48
+ }
49
+ /**
50
+ * Normalizes a Docker platform from manifest format to standard platform string
51
+ * @param platform Platform object from Docker manifest with os and architecture
52
+ * @returns Normalized platform string in format "os/arch" (e.g., "linux/amd64")
53
+ */
54
+ function normalizeDockerPlatform(platform) {
55
+ const os = normalizeDockerOS(platform.os);
56
+ const arch = normalizeDockerArchitecture(platform.architecture);
57
+ return `${os}/${arch}`;
58
+ }
59
+
60
+
61
+ /***/ },
62
+
63
+ /***/ "../common/src/global_variable.ts"
64
+ (__unused_webpack_module, exports) {
65
+
66
+
67
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
68
+ class GlobalVariable {
69
+ static map = new Map();
70
+ static set(k, v) {
71
+ GlobalVariable.map.set(k, v);
72
+ }
73
+ static get(k, def) {
74
+ if (GlobalVariable.map.has(k)) {
75
+ return GlobalVariable.map.get(k);
76
+ }
77
+ else {
78
+ return def;
79
+ }
80
+ }
81
+ static del(k) {
82
+ return GlobalVariable.map.delete(k);
83
+ }
84
+ }
85
+ exports["default"] = GlobalVariable;
86
+
87
+
88
+ /***/ },
89
+
90
+ /***/ "../common/src/index.ts"
91
+ (__unused_webpack_module, exports, __webpack_require__) {
92
+
93
+
94
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
95
+ if (k2 === undefined) k2 = k;
96
+ var desc = Object.getOwnPropertyDescriptor(m, k);
97
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
98
+ desc = { enumerable: true, get: function() { return m[k]; } };
99
+ }
100
+ Object.defineProperty(o, k2, desc);
101
+ }) : (function(o, m, k, k2) {
102
+ if (k2 === undefined) k2 = k;
103
+ o[k2] = m[k];
104
+ }));
105
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
106
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
107
+ };
108
+ var __importDefault = (this && this.__importDefault) || function (mod) {
109
+ return (mod && mod.__esModule) ? mod : { "default": mod };
110
+ };
111
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
112
+ exports.StorageSubsystem = exports.MCServerStatus = exports.InstanceStreamListener = exports.GlobalVariable = exports.normalizeDockerPlatform = exports.normalizeDockerOS = exports.normalizeDockerArchitecture = exports.removeTrail = exports.uninstallNpmPlugin = exports.isEssentialNpmPlugin = exports.npmPluginOperationPending = exports.npmPluginRoots = exports.listNpmPlugins = exports.installNpmPlugin = exports.currentProjectRoot = exports.currentPackageRoot = exports.sortPlugins = exports.resolvePluginEntry = exports.readPluginManifest = exports.createFrontendPluginMetadata = exports.discoverPluginsFromRoots = exports.discoverPlugins = exports.arrayUnique = exports.toText = exports.toNumber = exports.toBoolean = exports.supposeValue = exports.isEmpty = exports.configureEntityParams = exports.systemInfo = exports.QueryWrapper = exports.QueryMapWrapper = exports.MySqlSource = exports.LocalFileSource = exports.killProcess = exports.ProcessWrapper = void 0;
113
+ const global_variable_1 = __importDefault(__webpack_require__("../common/src/global_variable.ts"));
114
+ exports.GlobalVariable = global_variable_1.default;
115
+ const instance_stream_1 = __importDefault(__webpack_require__("../common/src/instance_stream.ts"));
116
+ exports.InstanceStreamListener = instance_stream_1.default;
117
+ const system_storage_1 = __importDefault(__webpack_require__("../common/src/system_storage.ts"));
118
+ exports.StorageSubsystem = system_storage_1.default;
119
+ const mcping_1 = __importDefault(__webpack_require__("../common/src/mcping.ts"));
120
+ exports.MCServerStatus = mcping_1.default;
121
+ var process_tools_1 = __webpack_require__("../common/src/process_tools.ts");
122
+ Object.defineProperty(exports, "ProcessWrapper", ({ enumerable: true, get: function () { return process_tools_1.ProcessWrapper; } }));
123
+ Object.defineProperty(exports, "killProcess", ({ enumerable: true, get: function () { return process_tools_1.killProcess; } }));
124
+ var query_wrapper_1 = __webpack_require__("../common/src/query_wrapper.ts");
125
+ Object.defineProperty(exports, "LocalFileSource", ({ enumerable: true, get: function () { return query_wrapper_1.LocalFileSource; } }));
126
+ Object.defineProperty(exports, "MySqlSource", ({ enumerable: true, get: function () { return query_wrapper_1.MySqlSource; } }));
127
+ Object.defineProperty(exports, "QueryMapWrapper", ({ enumerable: true, get: function () { return query_wrapper_1.QueryMapWrapper; } }));
128
+ Object.defineProperty(exports, "QueryWrapper", ({ enumerable: true, get: function () { return query_wrapper_1.QueryWrapper; } }));
129
+ var system_info_1 = __webpack_require__("../common/src/system_info.ts");
130
+ Object.defineProperty(exports, "systemInfo", ({ enumerable: true, get: function () { return system_info_1.systemInfo; } }));
131
+ var typecheck_1 = __webpack_require__("../common/src/typecheck.ts");
132
+ Object.defineProperty(exports, "configureEntityParams", ({ enumerable: true, get: function () { return typecheck_1.configureEntityParams; } }));
133
+ Object.defineProperty(exports, "isEmpty", ({ enumerable: true, get: function () { return typecheck_1.isEmpty; } }));
134
+ Object.defineProperty(exports, "supposeValue", ({ enumerable: true, get: function () { return typecheck_1.supposeValue; } }));
135
+ Object.defineProperty(exports, "toBoolean", ({ enumerable: true, get: function () { return typecheck_1.toBoolean; } }));
136
+ Object.defineProperty(exports, "toNumber", ({ enumerable: true, get: function () { return typecheck_1.toNumber; } }));
137
+ Object.defineProperty(exports, "toText", ({ enumerable: true, get: function () { return typecheck_1.toText; } }));
138
+ var array_1 = __webpack_require__("../common/src/array.ts");
139
+ Object.defineProperty(exports, "arrayUnique", ({ enumerable: true, get: function () { return array_1.arrayUnique; } }));
140
+ __exportStar(__webpack_require__("../common/src/plugin_contract.ts"), exports);
141
+ __exportStar(__webpack_require__("../common/src/plugin_overrides.ts"), exports);
142
+ __exportStar(__webpack_require__("../common/src/plugin_lifecycle.ts"), exports);
143
+ __exportStar(__webpack_require__("../common/src/plugin_revision.ts"), exports);
144
+ __exportStar(__webpack_require__("../common/src/plugin_modules.ts"), exports);
145
+ __exportStar(__webpack_require__("../common/src/plugin_watch.ts"), exports);
146
+ __exportStar(__webpack_require__("../common/src/runtime_paths.ts"), exports);
147
+ var plugin_manifest_1 = __webpack_require__("../common/src/plugin_manifest.ts");
148
+ Object.defineProperty(exports, "discoverPlugins", ({ enumerable: true, get: function () { return plugin_manifest_1.discoverPlugins; } }));
149
+ Object.defineProperty(exports, "discoverPluginsFromRoots", ({ enumerable: true, get: function () { return plugin_manifest_1.discoverPluginsFromRoots; } }));
150
+ Object.defineProperty(exports, "createFrontendPluginMetadata", ({ enumerable: true, get: function () { return plugin_manifest_1.createFrontendPluginMetadata; } }));
151
+ Object.defineProperty(exports, "readPluginManifest", ({ enumerable: true, get: function () { return plugin_manifest_1.readPluginManifest; } }));
152
+ Object.defineProperty(exports, "resolvePluginEntry", ({ enumerable: true, get: function () { return plugin_manifest_1.resolvePluginEntry; } }));
153
+ Object.defineProperty(exports, "sortPlugins", ({ enumerable: true, get: function () { return plugin_manifest_1.sortPlugins; } }));
154
+ var plugin_npm_1 = __webpack_require__("../common/src/plugin_npm.ts");
155
+ Object.defineProperty(exports, "currentPackageRoot", ({ enumerable: true, get: function () { return plugin_npm_1.currentPackageRoot; } }));
156
+ Object.defineProperty(exports, "currentProjectRoot", ({ enumerable: true, get: function () { return plugin_npm_1.currentProjectRoot; } }));
157
+ Object.defineProperty(exports, "installNpmPlugin", ({ enumerable: true, get: function () { return plugin_npm_1.installNpmPlugin; } }));
158
+ Object.defineProperty(exports, "listNpmPlugins", ({ enumerable: true, get: function () { return plugin_npm_1.listNpmPlugins; } }));
159
+ Object.defineProperty(exports, "npmPluginRoots", ({ enumerable: true, get: function () { return plugin_npm_1.npmPluginRoots; } }));
160
+ Object.defineProperty(exports, "npmPluginOperationPending", ({ enumerable: true, get: function () { return plugin_npm_1.npmPluginOperationPending; } }));
161
+ Object.defineProperty(exports, "isEssentialNpmPlugin", ({ enumerable: true, get: function () { return plugin_npm_1.isEssentialNpmPlugin; } }));
162
+ Object.defineProperty(exports, "uninstallNpmPlugin", ({ enumerable: true, get: function () { return plugin_npm_1.uninstallNpmPlugin; } }));
163
+ var string_utils_1 = __webpack_require__("../common/src/string_utils.ts");
164
+ Object.defineProperty(exports, "removeTrail", ({ enumerable: true, get: function () { return string_utils_1.removeTrail; } }));
165
+ var docker_utils_1 = __webpack_require__("../common/src/docker_utils.ts");
166
+ Object.defineProperty(exports, "normalizeDockerArchitecture", ({ enumerable: true, get: function () { return docker_utils_1.normalizeDockerArchitecture; } }));
167
+ Object.defineProperty(exports, "normalizeDockerOS", ({ enumerable: true, get: function () { return docker_utils_1.normalizeDockerOS; } }));
168
+ Object.defineProperty(exports, "normalizeDockerPlatform", ({ enumerable: true, get: function () { return docker_utils_1.normalizeDockerPlatform; } }));
169
+
170
+
171
+ /***/ },
172
+
173
+ /***/ "../common/src/instance_stream.ts"
174
+ (__unused_webpack_module, exports) {
175
+
176
+
177
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
178
+ // Application instance data stream forwarding adapter
179
+ class InstanceStreamListener {
180
+ listenMap = new Map();
181
+ disconnectHandlers = new Map();
182
+ requestForward(socket, instanceUuid) {
183
+ const sockets = this.listenMap.get(instanceUuid) ?? [];
184
+ if (sockets.some((listener) => listener.id === socket.id))
185
+ return;
186
+ sockets.push(socket);
187
+ this.listenMap.set(instanceUuid, sockets);
188
+ if (!this.disconnectHandlers.has(socket)) {
189
+ const disconnect = () => {
190
+ for (const uuid of this.listenMap.keys())
191
+ this.cannelForward(socket, uuid);
192
+ };
193
+ this.disconnectHandlers.set(socket, disconnect);
194
+ socket.once("disconnect", disconnect);
195
+ }
196
+ }
197
+ cannelForward(socket, instanceUuid) {
198
+ const remaining = this.listenMap
199
+ .get(instanceUuid)
200
+ ?.filter((listener) => listener.id !== socket.id);
201
+ if (remaining?.length)
202
+ this.listenMap.set(instanceUuid, remaining);
203
+ else
204
+ this.listenMap.delete(instanceUuid);
205
+ if (![...this.listenMap.values()].some((sockets) => sockets.includes(socket))) {
206
+ const disconnect = this.disconnectHandlers.get(socket);
207
+ if (disconnect)
208
+ socket.off("disconnect", disconnect);
209
+ this.disconnectHandlers.delete(socket);
210
+ }
211
+ }
212
+ forward(instanceUuid, data) {
213
+ this.forwardViaCallback(instanceUuid, (socket) => socket.emit("instance/stdout", data));
214
+ }
215
+ forwardViaCallback(instanceUuid, callback) {
216
+ for (const socket of this.listenMap.get(instanceUuid) ?? []) {
217
+ if (socket.connected)
218
+ callback(socket);
219
+ else
220
+ this.cannelForward(socket, instanceUuid);
221
+ }
222
+ }
223
+ hasListenInstance(instanceUuid) {
224
+ return this.listenMap.get(instanceUuid)?.some((socket) => socket.connected) ?? false;
225
+ }
226
+ }
227
+ exports["default"] = InstanceStreamListener;
228
+
229
+
230
+ /***/ },
231
+
232
+ /***/ "../common/src/mcping.ts"
233
+ (__unused_webpack_module, exports, __webpack_require__) {
234
+
235
+
236
+ var __importDefault = (this && this.__importDefault) || function (mod) {
237
+ return (mod && mod.__esModule) ? mod : { "default": mod };
238
+ };
239
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
240
+ const net_1 = __importDefault(__webpack_require__("net"));
241
+ class PingMinecraftServer {
242
+ port;
243
+ host;
244
+ status;
245
+ client;
246
+ pending;
247
+ constructor(port, host) {
248
+ this.port = port;
249
+ this.host = host;
250
+ this.status = this.offlineStatus();
251
+ }
252
+ offlineStatus() {
253
+ return {
254
+ online: false,
255
+ host: this.host,
256
+ port: this.port,
257
+ version: "",
258
+ motd: "",
259
+ current_players: 0,
260
+ max_players: 0,
261
+ latency: 0
262
+ };
263
+ }
264
+ getStatus() {
265
+ if (this.pending)
266
+ return this.pending;
267
+ this.status = this.offlineStatus();
268
+ this.pending = new Promise((resolve, reject) => {
269
+ const startedAt = Date.now();
270
+ const client = net_1.default.connect({ host: this.host, port: this.port });
271
+ this.client = client;
272
+ let received = Buffer.alloc(0);
273
+ let settled = false;
274
+ const finish = (error) => {
275
+ if (settled)
276
+ return;
277
+ settled = true;
278
+ clearTimeout(timeout);
279
+ client.destroy();
280
+ this.client = undefined;
281
+ if (error)
282
+ reject(error);
283
+ else
284
+ resolve(this.status);
285
+ };
286
+ const timeout = setTimeout(() => finish(new Error("Minecraft status request timed out")), 15000);
287
+ client.once("connect", () => client.write(Buffer.from([0xfe, 0x01])));
288
+ client.on("data", (chunk) => {
289
+ if (settled)
290
+ return;
291
+ try {
292
+ received = Buffer.concat([received, chunk]);
293
+ // Legacy status packets contain an unsigned UTF-16 character count.
294
+ if (received.length > 3 + 0xffff * 2)
295
+ throw new Error("Minecraft status response is too large");
296
+ if (received.length < 3)
297
+ return;
298
+ if (received[0] !== 0xff)
299
+ throw new Error("Invalid Minecraft status packet");
300
+ const length = 3 + received.readUInt16BE(1) * 2;
301
+ if (received.length < length)
302
+ return;
303
+ const fields = Buffer.from(received.subarray(3, length))
304
+ .swap16()
305
+ .toString("utf16le")
306
+ .split("\0");
307
+ if (fields.length !== 6 ||
308
+ fields[0] !== "§1" ||
309
+ !/^\d+$/.test(fields[4]) ||
310
+ !/^\d+$/.test(fields[5])) {
311
+ throw new Error("Invalid Minecraft status response");
312
+ }
313
+ const currentPlayers = Number(fields[4]);
314
+ const maxPlayers = Number(fields[5]);
315
+ if (!Number.isSafeInteger(currentPlayers) || !Number.isSafeInteger(maxPlayers)) {
316
+ throw new Error("Invalid Minecraft player count");
317
+ }
318
+ this.status = {
319
+ host: this.host,
320
+ port: this.port,
321
+ online: true,
322
+ version: fields[2],
323
+ motd: fields[3],
324
+ current_players: currentPlayers,
325
+ max_players: maxPlayers,
326
+ latency: Date.now() - startedAt
327
+ };
328
+ finish();
329
+ }
330
+ catch (error) {
331
+ finish(error instanceof Error ? error : new Error(String(error)));
332
+ }
333
+ });
334
+ client.once("error", finish);
335
+ client.once("close", () => finish(received.length ? new Error("Incomplete Minecraft status response") : undefined));
336
+ }).finally(() => {
337
+ this.pending = undefined;
338
+ });
339
+ return this.pending;
340
+ }
341
+ asyncStatus() {
342
+ return this.getStatus();
343
+ }
344
+ }
345
+ exports["default"] = PingMinecraftServer;
346
+
347
+
348
+ /***/ },
349
+
350
+ /***/ "../common/src/plugin_contract.ts"
351
+ (__unused_webpack_module, exports) {
352
+
353
+
354
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
355
+ exports.PLUGIN_SDK_VERSION = exports.PLUGIN_API_VERSION = void 0;
356
+ exports.validatePluginCompatibility = validatePluginCompatibility;
357
+ exports.resolvePluginDependencyOrder = resolvePluginDependencyOrder;
358
+ exports.createPluginQueue = createPluginQueue;
359
+ exports.validatePluginSettings = validatePluginSettings;
360
+ /** Versioned contract shared by the host, plugin compiler and package registry. */
361
+ exports.PLUGIN_API_VERSION = 1;
362
+ exports.PLUGIN_SDK_VERSION = "1.0.0";
363
+ /** Omitted metadata denotes a legacy package. Unknown versions never execute. */
364
+ function validatePluginCompatibility(value) {
365
+ if (value === undefined)
366
+ return;
367
+ if (!value || typeof value !== "object" || Array.isArray(value))
368
+ throw new Error("Invalid ElementsPanel plugin compatibility metadata.");
369
+ const contract = value;
370
+ if (contract.api !== exports.PLUGIN_API_VERSION || (contract.sdk !== undefined && contract.sdk !== 1))
371
+ throw new Error(`Unsupported ElementsPanel plugin API/SDK: ${JSON.stringify(value)}`);
372
+ }
373
+ /**
374
+ * Stable topological ordering shared by plugin hosts.
375
+ *
376
+ * Invalid nodes are kept out of `ordered`: direct missing dependencies are
377
+ * reported first, then anything depending on an invalid node, and finally
378
+ * cycles (plus nodes blocked behind a cycle). Independent nodes retain the
379
+ * caller's priority/id comparator.
380
+ */
381
+ function resolvePluginDependencyOrder(items, options) {
382
+ const sorted = [...items].sort(options.compare);
383
+ const byId = new Map(sorted.map((item) => [options.id(item), item]));
384
+ const dependencies = new Map(sorted.map((item) => [
385
+ options.id(item),
386
+ [...new Set(options.dependencies(item) || [])].filter(Boolean)
387
+ ]));
388
+ const issues = new Map();
389
+ for (const item of sorted) {
390
+ const id = options.id(item);
391
+ const missing = dependencies.get(id).filter((dependency) => !byId.has(dependency));
392
+ if (!missing.length)
393
+ continue;
394
+ issues.set(id, {
395
+ type: "missing-dependency",
396
+ dependencies: missing,
397
+ message: `Missing frontend plugin dependencies: ${missing.join(", ")}`
398
+ });
399
+ }
400
+ const markUnavailable = () => {
401
+ let changed = false;
402
+ for (const item of sorted) {
403
+ const id = options.id(item);
404
+ if (issues.has(id))
405
+ continue;
406
+ const unavailable = dependencies.get(id).filter((dependency) => issues.has(dependency));
407
+ if (!unavailable.length)
408
+ continue;
409
+ issues.set(id, {
410
+ type: "dependency-unavailable",
411
+ dependencies: unavailable,
412
+ message: `Frontend plugin dependencies are unavailable: ${unavailable.join(", ")}`
413
+ });
414
+ changed = true;
415
+ }
416
+ return changed;
417
+ };
418
+ while (markUnavailable()) {
419
+ // Propagate direct graph failures before sorting the remaining nodes.
420
+ }
421
+ const candidates = sorted.filter((item) => !issues.has(options.id(item)));
422
+ const candidateIds = new Set(candidates.map(options.id));
423
+ const indegree = new Map(candidates.map((item) => [options.id(item), 0]));
424
+ const dependents = new Map();
425
+ for (const item of candidates) {
426
+ const id = options.id(item);
427
+ for (const dependency of dependencies.get(id)) {
428
+ if (!candidateIds.has(dependency))
429
+ continue;
430
+ indegree.set(id, indegree.get(id) + 1);
431
+ const bucket = dependents.get(dependency) || [];
432
+ bucket.push(item);
433
+ dependents.set(dependency, bucket);
434
+ }
435
+ }
436
+ const ready = candidates.filter((item) => indegree.get(options.id(item)) === 0);
437
+ ready.sort(options.compare);
438
+ const ordered = [];
439
+ while (ready.length) {
440
+ const item = ready.shift();
441
+ const id = options.id(item);
442
+ ordered.push(item);
443
+ for (const dependent of dependents.get(id) || []) {
444
+ const dependentId = options.id(dependent);
445
+ const next = indegree.get(dependentId) - 1;
446
+ indegree.set(dependentId, next);
447
+ if (next === 0) {
448
+ ready.push(dependent);
449
+ ready.sort(options.compare);
450
+ }
451
+ }
452
+ }
453
+ const unresolved = candidates.filter((item) => !ordered.includes(item));
454
+ if (unresolved.length) {
455
+ const unresolvedIds = new Set(unresolved.map(options.id));
456
+ for (const item of unresolved) {
457
+ const id = options.id(item);
458
+ const blockedBy = dependencies.get(id).filter((dependency) => unresolvedIds.has(dependency));
459
+ issues.set(id, {
460
+ type: "dependency-cycle",
461
+ dependencies: blockedBy,
462
+ message: `Frontend plugin dependency cycle: ${[id, ...blockedBy].join(" -> ")}`
463
+ });
464
+ }
465
+ }
466
+ return { ordered, issues };
467
+ }
468
+ /** A queue survives failures, so a failed change cannot block subsequent repairs. */
469
+ function createPluginQueue() {
470
+ let tail = Promise.resolve();
471
+ return (operation) => {
472
+ const next = tail.then(operation);
473
+ tail = next.catch(() => undefined);
474
+ return next;
475
+ };
476
+ }
477
+ /** Validate the form contract on the server before invoking plugin-owned validation. */
478
+ function validatePluginSettings(fields, values) {
479
+ if (!values || typeof values !== "object" || Array.isArray(values))
480
+ throw new Error("Invalid plugin settings.");
481
+ const input = values;
482
+ for (const field of fields) {
483
+ if (!field.key || field.type === "link")
484
+ continue;
485
+ const value = input[field.key];
486
+ if (value === undefined || value === null) {
487
+ if (field.required)
488
+ throw new Error(`${field.title || field.key}: required`);
489
+ continue;
490
+ }
491
+ if (field.type === "list") {
492
+ if (!Array.isArray(value) || value.length > (field.maxItems ?? 100))
493
+ throw new Error(`${field.title || field.key}: invalid list`);
494
+ for (const item of value)
495
+ validatePluginSettings(field.fields || [], item);
496
+ continue;
497
+ }
498
+ const valid = field.type === "number"
499
+ ? typeof value === "number" &&
500
+ Number.isFinite(value) &&
501
+ (field.min === undefined || value >= field.min) &&
502
+ (field.max === undefined || value <= field.max)
503
+ : field.type === "boolean"
504
+ ? typeof value === "boolean"
505
+ : field.type === "select"
506
+ ? field.options?.some((option) => option.value === value)
507
+ : typeof value === "string";
508
+ if (!valid)
509
+ throw new Error(`${field.title || field.key}: invalid value`);
510
+ }
511
+ }
512
+
513
+
514
+ /***/ },
515
+
516
+ /***/ "../common/src/plugin_lifecycle.ts"
517
+ (__unused_webpack_module, exports) {
518
+
519
+
520
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
521
+ exports.trackPluginDisposal = trackPluginDisposal;
522
+ /** Cordis 3 starts async disposers without awaiting them. Track them per host. */
523
+ function trackPluginDisposal(ctx) {
524
+ const pending = new Set();
525
+ const failures = [];
526
+ const tracked = new WeakSet();
527
+ const scopes = new WeakSet();
528
+ const observe = (scope) => {
529
+ if (scopes.has(scope))
530
+ return;
531
+ scopes.add(scope);
532
+ const reset = scope.reset;
533
+ scope.reset = function () {
534
+ this.disposables = this.disposables.map((dispose) => {
535
+ if (tracked.has(dispose))
536
+ return dispose;
537
+ const wrapper = (...args) => {
538
+ let result;
539
+ try {
540
+ result = dispose(...args);
541
+ }
542
+ catch (error) {
543
+ failures.push(error);
544
+ throw error;
545
+ }
546
+ if (result && typeof result.then === "function") {
547
+ const task = Promise.resolve(result)
548
+ .catch((error) => {
549
+ failures.push(error);
550
+ })
551
+ .finally(() => pending.delete(task));
552
+ pending.add(task);
553
+ }
554
+ return result;
555
+ };
556
+ // Cordis marks persistent disposers with a symbol; preserve its semantics.
557
+ for (const key of Object.getOwnPropertySymbols(dispose))
558
+ Object.defineProperty(wrapper, key, Object.getOwnPropertyDescriptor(dispose, key));
559
+ tracked.add(wrapper);
560
+ return wrapper;
561
+ });
562
+ return reset.call(this);
563
+ };
564
+ };
565
+ ctx.on("internal/runtime", observe);
566
+ ctx.on("internal/fork", observe);
567
+ return async () => {
568
+ do {
569
+ await ctx.events.flush();
570
+ await Promise.all([...pending]);
571
+ } while (pending.size);
572
+ if (failures.length) {
573
+ const errors = failures.splice(0);
574
+ throw new Error(`Plugin cleanup failed: ${errors.map(String).join("; ")}`);
575
+ }
576
+ };
577
+ }
578
+
579
+
580
+ /***/ },
581
+
582
+ /***/ "../common/src/plugin_manifest.ts"
583
+ (__unused_webpack_module, exports, __webpack_require__) {
584
+
585
+
586
+ var __importDefault = (this && this.__importDefault) || function (mod) {
587
+ return (mod && mod.__esModule) ? mod : { "default": mod };
588
+ };
589
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
590
+ exports.createFrontendPluginMetadata = createFrontendPluginMetadata;
591
+ exports.readPluginManifest = readPluginManifest;
592
+ exports.resolvePluginEntry = resolvePluginEntry;
593
+ exports.discoverPlugins = discoverPlugins;
594
+ exports.discoverPluginsFromRoots = discoverPluginsFromRoots;
595
+ exports.sortPlugins = sortPlugins;
596
+ const node_fs_1 = __importDefault(__webpack_require__("node:fs"));
597
+ const node_path_1 = __importDefault(__webpack_require__("node:path"));
598
+ function stringArray(value) {
599
+ if (!Array.isArray(value))
600
+ return undefined;
601
+ const result = value.filter((item) => typeof item === "string" && !!item.trim());
602
+ return result.length ? [...new Set(result.map((item) => item.trim()))] : undefined;
603
+ }
604
+ /**
605
+ * Redacts a full plugin manifest to the fields a browser is allowed to see.
606
+ * Backend `config` may contain credentials, so only `frontendConfig` crosses
607
+ * this boundary.
608
+ */
609
+ function createFrontendPluginMetadata(manifest, extras = {}) {
610
+ const frontendInject = stringArray(manifest.frontendInject);
611
+ return {
612
+ id: manifest.id,
613
+ ...(manifest.version === undefined ? {} : { version: manifest.version }),
614
+ ...(manifest.description === undefined ? {} : { description: manifest.description }),
615
+ ...(manifest.priority === undefined ? {} : { priority: manifest.priority }),
616
+ ...(manifest.elements === undefined ? {} : { elements: manifest.elements }),
617
+ ...(frontendInject === undefined ? {} : { frontendInject }),
618
+ ...(manifest.frontendImmediate === undefined
619
+ ? {}
620
+ : { frontendImmediate: Boolean(manifest.frontendImmediate) }),
621
+ ...(manifest.frontendRequired === undefined
622
+ ? {}
623
+ : { frontendRequired: Boolean(manifest.frontendRequired) }),
624
+ ...(manifest.frontendConfig === undefined ? {} : { config: manifest.frontendConfig }),
625
+ ...(extras.restartRequired === undefined ? {} : extras)
626
+ };
627
+ }
628
+ const MANIFEST_FILES = ["plugin.json", "manifest.json", "package.json"];
629
+ const ENTRY_FIELDS = ["backend", "frontend", "panel", "daemon", "main", "entry", "ui"];
630
+ function objectValue(value) {
631
+ return value && typeof value === "object" && !Array.isArray(value)
632
+ ? value
633
+ : {};
634
+ }
635
+ /**
636
+ * Source plugins keep package.json beside plugin.json. Published npm plugins
637
+ * keep it at the package root, with runtime metadata under elementspanel.<side>.
638
+ * Stop at the first package boundary so a host's metadata cannot reach a plugin.
639
+ */
640
+ function readPackageMetadata(directory, id) {
641
+ const pluginDirectory = node_path_1.default.resolve(directory);
642
+ let current = pluginDirectory;
643
+ while (true) {
644
+ const filename = node_path_1.default.join(current, "package.json");
645
+ if (node_fs_1.default.existsSync(filename)) {
646
+ const { elementspanel, ...metadata } = objectValue(JSON.parse(node_fs_1.default.readFileSync(filename, "utf8")));
647
+ const { id: packageId, panel, daemon, ...shared } = objectValue(elementspanel);
648
+ const definition = [panel, daemon]
649
+ .map(objectValue)
650
+ .find((side) => typeof side.directory === "string" &&
651
+ node_path_1.default.resolve(current, side.directory) === pluginDirectory);
652
+ if (current !== pluginDirectory && (packageId !== id || !definition))
653
+ return {};
654
+ const { directory: _directory, folder: _folder, ...sideMetadata } = definition ?? {};
655
+ const result = { ...metadata, ...shared, ...sideMetadata };
656
+ // npm's main/entry fields do not define a side's execution entry.
657
+ for (const field of ["dual", ...ENTRY_FIELDS])
658
+ delete result[field];
659
+ return result;
660
+ }
661
+ const parent = node_path_1.default.dirname(current);
662
+ if (parent === current)
663
+ return {};
664
+ current = parent;
665
+ }
666
+ }
667
+ /** Merges package metadata with a plugin's identity and entries. Legacy manifests still load. */
668
+ function readPluginManifest(directory, onWarning, fallbackId = node_path_1.default.basename(directory)) {
669
+ for (const file of MANIFEST_FILES) {
670
+ const filePath = node_path_1.default.join(directory, file);
671
+ if (!node_fs_1.default.existsSync(filePath))
672
+ continue;
673
+ try {
674
+ const value = JSON.parse(node_fs_1.default.readFileSync(filePath, "utf8"));
675
+ if (!value || typeof value !== "object" || Array.isArray(value))
676
+ return null;
677
+ const id = typeof value.id === "string" ? value.id : fallbackId;
678
+ if (!id.trim())
679
+ return null;
680
+ const manifest = {
681
+ ...value,
682
+ ...readPackageMetadata(directory, id.trim()),
683
+ id: id.trim()
684
+ };
685
+ // Identity and execution paths always belong to plugin.json. Package
686
+ // metadata wins for descriptions, priority, configuration and dependencies.
687
+ for (const field of ["version", "dual", ...ENTRY_FIELDS]) {
688
+ if (value[field] !== undefined)
689
+ manifest[field] = value[field];
690
+ }
691
+ return manifest;
692
+ }
693
+ catch (error) {
694
+ onWarning?.(`Failed to read plugin manifest: ${filePath}`, error);
695
+ return null;
696
+ }
697
+ }
698
+ return null;
699
+ }
700
+ /**
701
+ * Resolves one plugin's entry module. Returns null when the manifest names an
702
+ * entry that does not exist, or when none of the fallback candidates is present.
703
+ * Paths that would escape the plugin directory are rejected.
704
+ */
705
+ function resolvePluginEntry(directory, manifest, options) {
706
+ const configured = options.entryFields
707
+ .map((field) => manifest[field])
708
+ .find((entry) => typeof entry === "string" && entry.length > 0);
709
+ const candidates = configured ? [configured] : options.entryCandidates ?? [];
710
+ const root = node_path_1.default.resolve(directory);
711
+ for (const candidate of candidates) {
712
+ const entry = node_path_1.default.resolve(directory, candidate);
713
+ if (entry.startsWith(`${root}${node_path_1.default.sep}`) && node_fs_1.default.existsSync(entry))
714
+ return entry;
715
+ }
716
+ return null;
717
+ }
718
+ function discoverPluginDirectory(directory, options, folder = node_path_1.default.basename(directory)) {
719
+ // Npm packages expose a side directory; its name is not the public plugin id.
720
+ const manifest = readPluginManifest(directory, options.onWarning, folder);
721
+ if (!manifest)
722
+ return null;
723
+ if (manifest.enabled === false && !options.includeDisabled)
724
+ return null;
725
+ const entry = resolvePluginEntry(directory, manifest, options);
726
+ if (!entry && options.entryFields.some((field) => typeof manifest[field] === "string")) {
727
+ options.onWarning?.(`Plugin "${manifest.id}" has no valid entry module.`);
728
+ return null;
729
+ }
730
+ return { manifest, directory, folder, entry: entry ?? undefined };
731
+ }
732
+ /**
733
+ * Lists the usable plugins below `root`, in load order: ascending `priority`,
734
+ * then by id so the order is stable. Duplicate ids are dropped, and disabled
735
+ * ones too unless `includeDisabled` says otherwise. A plugin whose manifest names an entry that cannot be resolved is
736
+ * dropped too; one that names none keeps `entry` undefined, because a plugin may
737
+ * legitimately contribute to only one side of the panel.
738
+ */
739
+ function discoverPlugins(root, options) {
740
+ if (!node_fs_1.default.existsSync(root))
741
+ return [];
742
+ const plugins = [];
743
+ const seenIds = new Set();
744
+ for (const item of node_fs_1.default.readdirSync(root, { withFileTypes: true })) {
745
+ if (!item.isDirectory())
746
+ continue;
747
+ const directory = node_path_1.default.join(root, item.name);
748
+ const plugin = discoverPluginDirectory(directory, options, item.name);
749
+ if (!plugin || seenIds.has(plugin.manifest.id)) {
750
+ if (plugin && seenIds.has(plugin.manifest.id)) {
751
+ options.onWarning?.(`Ignoring duplicate plugin id: ${plugin.manifest.id}`);
752
+ }
753
+ continue;
754
+ }
755
+ seenIds.add(plugin.manifest.id);
756
+ plugins.push(plugin);
757
+ }
758
+ return sortPlugins(plugins);
759
+ }
760
+ /**
761
+ * Discovers plugins from several roots while keeping the first root's plugin
762
+ * when ids collide. The built-in root is intentionally passed first by the
763
+ * panel and daemon loaders, so an installed market plugin cannot shadow a
764
+ * built-in npm dependency. A root can also force an installed project plugin
765
+ * into the disabled state; the user-owned override layer may enable it later.
766
+ */
767
+ function discoverPluginsFromRoots(roots, options) {
768
+ const discovered = [];
769
+ const seenIds = new Set();
770
+ for (const root of roots) {
771
+ const isDirectPlugin = MANIFEST_FILES.some((file) => node_fs_1.default.existsSync(node_path_1.default.join(root.directory, file)));
772
+ const plugins = isDirectPlugin
773
+ ? [discoverPluginDirectory(root.directory, options, root.folder)]
774
+ : discoverPlugins(root.directory, options);
775
+ for (const plugin of plugins) {
776
+ if (!plugin)
777
+ continue;
778
+ const manifest = root.defaultEnabled === false ? { ...plugin.manifest, enabled: false } : plugin.manifest;
779
+ if (manifest.enabled === false && !options.includeDisabled)
780
+ continue;
781
+ if (seenIds.has(plugin.manifest.id)) {
782
+ options.onWarning?.(`Ignoring duplicate plugin id: ${plugin.manifest.id}`);
783
+ continue;
784
+ }
785
+ seenIds.add(plugin.manifest.id);
786
+ discovered.push({
787
+ ...plugin,
788
+ manifest,
789
+ ...(root.packageRoot
790
+ ? {
791
+ packageRoot: root.packageRoot,
792
+ packageName: root.packageName,
793
+ npmSource: root.npmSource
794
+ }
795
+ : {}),
796
+ ...(root.folder ? { folder: root.folder } : {})
797
+ });
798
+ }
799
+ }
800
+ return sortPlugins(discovered);
801
+ }
802
+ /** Load order: ascending `priority`, then by id so it never depends on the filesystem. */
803
+ function sortPlugins(plugins) {
804
+ return plugins.sort((a, b) => (Number(a.manifest.priority) || 0) - (Number(b.manifest.priority) || 0) ||
805
+ a.manifest.id.localeCompare(b.manifest.id));
806
+ }
807
+
808
+
809
+ /***/ },
810
+
811
+ /***/ "../common/src/plugin_modules.ts"
812
+ (__unused_webpack_module, exports, __webpack_require__) {
813
+
814
+
815
+ var __importDefault = (this && this.__importDefault) || function (mod) {
816
+ return (mod && mod.__esModule) ? mod : { "default": mod };
817
+ };
818
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
819
+ exports.pluginBackendRevision = pluginBackendRevision;
820
+ exports.forgetPluginModules = forgetPluginModules;
821
+ exports.loadPluginModule = loadPluginModule;
822
+ const node_fs_1 = __importDefault(__webpack_require__("node:fs"));
823
+ const node_path_1 = __importDefault(__webpack_require__("node:path"));
824
+ const node_url_1 = __webpack_require__("node:url");
825
+ const node_crypto_1 = __webpack_require__("node:crypto");
826
+ const node_module_1 = __webpack_require__("node:module");
827
+ const plugin_revision_1 = __webpack_require__("../common/src/plugin_revision.ts");
828
+ const REVISION = "epanelPluginRevision";
829
+ const graphs = new Map();
830
+ const previousRoots = new Map();
831
+ let hooksInstalled = false;
832
+ const sharedModules = new Set([
833
+ "cordis",
834
+ "@cordisjs/core",
835
+ "@cordisjs/logger",
836
+ "@cordisjs/timer",
837
+ "@cordisjs/schema",
838
+ "cosmokit",
839
+ "schemastery",
840
+ "reggol"
841
+ ]);
842
+ function dependencyRoots(directory) {
843
+ const visited = new Set([directory]);
844
+ const walk = (root) => {
845
+ const filename = node_path_1.default.join(root, "package.json");
846
+ if (!node_fs_1.default.existsSync(filename))
847
+ return;
848
+ const metadata = JSON.parse(node_fs_1.default.readFileSync(filename, "utf8"));
849
+ const localRequire = (0, node_module_1.createRequire)(filename);
850
+ for (const name of Object.keys({
851
+ ...metadata.dependencies,
852
+ ...metadata.optionalDependencies
853
+ })) {
854
+ if (sharedModules.has(name))
855
+ continue;
856
+ const target = (localRequire.resolve.paths(name) || [])
857
+ .map((directory) => node_path_1.default.join(directory, name, "package.json"))
858
+ .find((candidate) => node_fs_1.default.existsSync(candidate));
859
+ if (!target)
860
+ continue;
861
+ const dependency = node_fs_1.default.realpathSync(node_path_1.default.dirname(target));
862
+ if (visited.has(dependency))
863
+ continue;
864
+ visited.add(dependency);
865
+ walk(dependency);
866
+ }
867
+ };
868
+ walk(directory);
869
+ visited.delete(directory);
870
+ return [...visited];
871
+ }
872
+ /** Include transitive dependency versions so npm updates replace the affected graph. */
873
+ function pluginBackendRevision(entry, directory) {
874
+ const hash = (0, node_crypto_1.createHash)("sha256").update((0, plugin_revision_1.pluginDirectoryRevision)(node_path_1.default.dirname(entry)));
875
+ for (const root of [directory, ...dependencyRoots(directory)]) {
876
+ const filename = node_path_1.default.join(root, "package.json");
877
+ if (node_fs_1.default.existsSync(filename))
878
+ hash.update(root).update(node_fs_1.default.readFileSync(filename));
879
+ }
880
+ return hash.digest("hex").slice(0, 24);
881
+ }
882
+ function within(root, filename) {
883
+ const relative = node_path_1.default.relative(root, filename);
884
+ return (relative === "" ||
885
+ (!relative.startsWith(`..${node_path_1.default.sep}`) && relative !== ".." && !node_path_1.default.isAbsolute(relative)));
886
+ }
887
+ /** Clear the plugin's complete CJS graph, keeping shared host modules intact. */
888
+ function forgetPluginModules(directory) {
889
+ const runtimeRequire = eval("require");
890
+ const owned = [directory, ...(previousRoots.get(directory) || []), ...dependencyRoots(directory)];
891
+ previousRoots.delete(directory);
892
+ const belongs = (filename) => {
893
+ const parts = filename.split(`${node_path_1.default.sep}node_modules${node_path_1.default.sep}`);
894
+ if (parts.slice(1).some((part) => {
895
+ const name = part.startsWith("@")
896
+ ? part.split(node_path_1.default.sep).slice(0, 2).join("/")
897
+ : part.split(node_path_1.default.sep)[0];
898
+ return sharedModules.has(name);
899
+ }))
900
+ return false;
901
+ return owned.some((root) => within(root, filename));
902
+ };
903
+ for (const filename of Object.keys(runtimeRequire.cache)) {
904
+ if (belongs(filename))
905
+ delete runtimeRequire.cache[filename];
906
+ }
907
+ for (const mod of Object.values(runtimeRequire.cache)) {
908
+ if (mod)
909
+ mod.children = mod.children.filter((child) => !belongs(child.filename));
910
+ }
911
+ // npm can move a dependency or change its main entry. Node's CJS resolver
912
+ // retains those paths separately from require.cache.
913
+ const pathCache = runtimeRequire("node:module")._pathCache;
914
+ for (const [key, filename] of Object.entries(pathCache)) {
915
+ if (belongs(filename) || owned.some((root) => key.includes(root)))
916
+ delete pathCache[key];
917
+ }
918
+ }
919
+ function isEsm(entry) {
920
+ if (entry.endsWith(".mjs"))
921
+ return true;
922
+ if (entry.endsWith(".cjs"))
923
+ return false;
924
+ let directory = node_path_1.default.dirname(entry);
925
+ while (true) {
926
+ const filename = node_path_1.default.join(directory, "package.json");
927
+ if (node_fs_1.default.existsSync(filename))
928
+ return JSON.parse(node_fs_1.default.readFileSync(filename, "utf8")).type === "module";
929
+ const parent = node_path_1.default.dirname(directory);
930
+ if (parent === directory)
931
+ return false;
932
+ directory = parent;
933
+ }
934
+ }
935
+ /** Give an ESM entry and its local imports the same immutable revision URL. */
936
+ async function loadPluginModule(entry, directory, revision) {
937
+ const runtimeRequire = eval("require");
938
+ previousRoots.set(directory, dependencyRoots(directory));
939
+ if (!isEsm(entry)) {
940
+ try {
941
+ return runtimeRequire(entry);
942
+ }
943
+ catch (error) {
944
+ if (error?.code !== "ERR_REQUIRE_ESM")
945
+ throw error;
946
+ }
947
+ }
948
+ graphs.set(revision, [directory, ...dependencyRoots(directory)]);
949
+ if (!hooksInstalled) {
950
+ const { registerHooks } = runtimeRequire("node:module");
951
+ if (typeof registerHooks !== "function")
952
+ throw new Error("ESM plugin updates require Node.js 22.15 or newer.");
953
+ registerHooks({
954
+ resolve(specifier, context, nextResolve) {
955
+ const resolved = nextResolve(specifier, context);
956
+ if (!context.parentURL?.startsWith("file:") || !resolved.url.startsWith("file:"))
957
+ return resolved;
958
+ const parent = new URL(context.parentURL);
959
+ const parentRevision = parent.searchParams.get(REVISION);
960
+ if (!parentRevision)
961
+ return resolved;
962
+ const roots = graphs.get(parentRevision);
963
+ if (!roots?.some((root) => within(root, (0, node_url_1.fileURLToPath)(parent))) ||
964
+ !roots.some((root) => within(root, (0, node_url_1.fileURLToPath)(resolved.url))))
965
+ return resolved;
966
+ const url = new URL(resolved.url);
967
+ url.searchParams.set(REVISION, parentRevision);
968
+ return { ...resolved, url: url.href };
969
+ }
970
+ });
971
+ hooksInstalled = true;
972
+ }
973
+ const url = (0, node_url_1.pathToFileURL)(entry);
974
+ url.searchParams.set(REVISION, revision);
975
+ const dynamicImport = new Function("specifier", "return import(specifier)");
976
+ return dynamicImport(url.href);
977
+ }
978
+
979
+
980
+ /***/ },
981
+
982
+ /***/ "../common/src/plugin_npm.ts"
983
+ (__unused_webpack_module, exports, __webpack_require__) {
984
+
985
+
986
+ var __importDefault = (this && this.__importDefault) || function (mod) {
987
+ return (mod && mod.__esModule) ? mod : { "default": mod };
988
+ };
989
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
990
+ exports.currentPackageRoot = currentPackageRoot;
991
+ exports.currentProjectRoot = currentProjectRoot;
992
+ exports.npmPluginRoots = npmPluginRoots;
993
+ exports.listNpmPlugins = listNpmPlugins;
994
+ exports.npmPluginOperationPending = npmPluginOperationPending;
995
+ exports.isEssentialNpmPlugin = isEssentialNpmPlugin;
996
+ exports.installNpmPlugin = installNpmPlugin;
997
+ exports.uninstallNpmPlugin = uninstallNpmPlugin;
998
+ const child_process_1 = __webpack_require__("child_process");
999
+ const fs_extra_1 = __importDefault(__webpack_require__("fs-extra"));
1000
+ const path_1 = __importDefault(__webpack_require__("path"));
1001
+ const module_1 = __webpack_require__("module");
1002
+ const plugin_manifest_1 = __webpack_require__("../common/src/plugin_manifest.ts");
1003
+ const plugin_contract_1 = __webpack_require__("../common/src/plugin_contract.ts");
1004
+ const PLUGIN_ID = /^[a-z][a-z0-9_-]{1,63}$/;
1005
+ const PACKAGE_NAME = /^(?:@[a-z0-9_.-]+\/)?epanel-plugin-[a-z0-9_.-]+$/;
1006
+ const VERSION = /^[a-zA-Z0-9][a-zA-Z0-9._+-]{0,63}$/;
1007
+ function readJson(filename) {
1008
+ try {
1009
+ const parsed = JSON.parse(fs_extra_1.default.readFileSync(filename, "utf8"));
1010
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed)
1011
+ ? parsed
1012
+ : null;
1013
+ }
1014
+ catch (error) {
1015
+ if (error?.code === "ENOENT")
1016
+ return null;
1017
+ throw error;
1018
+ }
1019
+ }
1020
+ function objectValue(value) {
1021
+ return value && typeof value === "object" && !Array.isArray(value)
1022
+ ? value
1023
+ : undefined;
1024
+ }
1025
+ function stringValue(value) {
1026
+ return typeof value === "string" ? value : "";
1027
+ }
1028
+ function currentPackageRoot() {
1029
+ return path_1.default.resolve(process.env.EPANEL_PACKAGE_ROOT || process.cwd());
1030
+ }
1031
+ function currentProjectRoot(packageRoot = currentPackageRoot()) {
1032
+ // The npm worker sets EPANEL_PROJECT_ROOT to the installation directory. In a
1033
+ // development checkout the runtime package sits directly under the project.
1034
+ if (process.env.EPANEL_PROJECT_ROOT)
1035
+ return path_1.default.resolve(process.env.EPANEL_PROJECT_ROOT);
1036
+ const scope = path_1.default.resolve(packageRoot, "..");
1037
+ const nodeModules = path_1.default.dirname(scope);
1038
+ if (path_1.default.basename(nodeModules) === "node_modules" && path_1.default.basename(scope).startsWith("@"))
1039
+ return path_1.default.dirname(nodeModules);
1040
+ return scope;
1041
+ }
1042
+ function npmPluginRoots(options = {}) {
1043
+ const packageRoot = path_1.default.resolve(options.packageRoot ?? currentPackageRoot());
1044
+ return {
1045
+ packageRoot,
1046
+ projectRoot: path_1.default.resolve(options.projectRoot ?? currentProjectRoot(packageRoot))
1047
+ };
1048
+ }
1049
+ function projectManifestFile(projectRoot) {
1050
+ return path_1.default.join(projectRoot, "package.json");
1051
+ }
1052
+ function readProjectManifest(projectRoot) {
1053
+ return (readJson(projectManifestFile(projectRoot)) ?? {
1054
+ private: true,
1055
+ dependencies: {},
1056
+ elementspanel: { plugins: {} }
1057
+ });
1058
+ }
1059
+ function writeProjectManifest(projectRoot, manifest) {
1060
+ fs_extra_1.default.writeFileSync(projectManifestFile(projectRoot), `${JSON.stringify(manifest, null, 2)}\n`);
1061
+ }
1062
+ function dependenciesOf(manifest) {
1063
+ return objectValue(manifest?.dependencies) ?? {};
1064
+ }
1065
+ function mappingsOf(manifest) {
1066
+ return objectValue(objectValue(manifest?.elementspanel)?.plugins) ?? {};
1067
+ }
1068
+ function pluginRecord(pluginId, name, source, packageRoot, directory, folder, version) {
1069
+ return { pluginId, name, version, sides: [], directory, folder, packageRoot, source };
1070
+ }
1071
+ function readPackageEntry(packageRoot, name) {
1072
+ // Read package boundaries directly: Node caches require.resolve() paths even
1073
+ // after npm moves an installation between nested and hoisted node_modules.
1074
+ let manifestPath;
1075
+ for (const root of [packageRoot, currentProjectRoot(packageRoot)]) {
1076
+ const localRequire = (0, module_1.createRequire)(path_1.default.join(root, "package.json"));
1077
+ manifestPath = (localRequire.resolve.paths(name) || [])
1078
+ .map((directory) => path_1.default.join(directory, name, "package.json"))
1079
+ .find((filename) => fs_extra_1.default.existsSync(filename));
1080
+ if (manifestPath)
1081
+ break;
1082
+ }
1083
+ if (!manifestPath)
1084
+ throw new Error(`Missing npm plugin package: ${name}.`);
1085
+ manifestPath = fs_extra_1.default.realpathSync(manifestPath);
1086
+ const metadata = readJson(manifestPath);
1087
+ if (!metadata)
1088
+ throw new Error(`Missing package metadata for ${name}.`);
1089
+ return { root: path_1.default.dirname(manifestPath), metadata };
1090
+ }
1091
+ function sideDirectory(packageRoot, metadata, side) {
1092
+ const elements = objectValue(metadata.elementspanel);
1093
+ const definition = objectValue(elements?.[side]);
1094
+ const directory = stringValue(definition?.directory);
1095
+ if (!definition || !directory)
1096
+ return undefined;
1097
+ const target = path_1.default.resolve(packageRoot, directory);
1098
+ const relative = path_1.default.relative(packageRoot, target);
1099
+ if (relative === "" ||
1100
+ relative === ".." ||
1101
+ relative.startsWith(`..${path_1.default.sep}`) ||
1102
+ path_1.default.isAbsolute(relative))
1103
+ return undefined;
1104
+ return target;
1105
+ }
1106
+ function recordFromPackage(pluginId, name, source, packageRoot, metadata, side) {
1107
+ const elements = objectValue(metadata.elementspanel);
1108
+ if (!elements || elements.id !== pluginId)
1109
+ return undefined;
1110
+ const version = stringValue(metadata.version);
1111
+ const sides = ["panel", "daemon"].filter((item) => Boolean(sideDirectory(packageRoot, metadata, item)));
1112
+ if (!sides.length)
1113
+ return undefined;
1114
+ const target = side ?? sides[0];
1115
+ if (!sides.includes(target))
1116
+ return undefined;
1117
+ const directory = sideDirectory(packageRoot, metadata, target);
1118
+ const folder = stringValue(objectValue(elements[target])?.folder);
1119
+ if (!directory || !PLUGIN_ID.test(folder))
1120
+ return undefined;
1121
+ return {
1122
+ ...pluginRecord(pluginId, name, source, packageRoot, directory, folder, version),
1123
+ sides
1124
+ };
1125
+ }
1126
+ /**
1127
+ * Every npm plugin the runtime can load for a side, built-in and project-installed.
1128
+ *
1129
+ * Built-in mappings win over a project mapping for the same id: a third-party
1130
+ * package must not shadow a plugin that ships with the runtime. A project package
1131
+ * that does not describe the requested side is skipped instead of failing, which
1132
+ * is what lets a two-sided project share one dependency list between sides.
1133
+ */
1134
+ function listNpmPlugins(side, options = {}) {
1135
+ const { packageRoot, projectRoot } = npmPluginRoots(options);
1136
+ const host = readJson(path_1.default.join(packageRoot, "package.json"));
1137
+ if (!host)
1138
+ throw new Error(`Missing runtime package metadata: ${packageRoot}`);
1139
+ const project = path_1.default.resolve(projectRoot) === path_1.default.resolve(packageRoot)
1140
+ ? null
1141
+ : readProjectManifest(projectRoot);
1142
+ const hostMappings = mappingsOf(host);
1143
+ const projectMappings = { ...mappingsOf(project) };
1144
+ for (const name of Object.keys(dependenciesOf(project))) {
1145
+ if (!PACKAGE_NAME.test(name) || Object.values(projectMappings).includes(name))
1146
+ continue;
1147
+ try {
1148
+ const entry = readPackageEntry(projectRoot, name);
1149
+ const id = stringValue(objectValue(entry.metadata.elementspanel)?.id);
1150
+ if (PLUGIN_ID.test(id) && projectMappings[id] === undefined)
1151
+ projectMappings[id] = name;
1152
+ }
1153
+ catch {
1154
+ /* npm may still be writing the package. */
1155
+ }
1156
+ }
1157
+ const mappings = new Map();
1158
+ for (const [pluginId, name] of Object.entries(projectMappings)) {
1159
+ if (!PLUGIN_ID.test(pluginId) || typeof name !== "string" || !PACKAGE_NAME.test(name))
1160
+ continue;
1161
+ mappings.set(pluginId, { name, source: "project" });
1162
+ }
1163
+ for (const [pluginId, name] of Object.entries(hostMappings)) {
1164
+ if (!PLUGIN_ID.test(pluginId) || typeof name !== "string" || !PACKAGE_NAME.test(name))
1165
+ continue;
1166
+ if (dependenciesOf(host)[name] === undefined)
1167
+ continue;
1168
+ mappings.set(pluginId, { name, source: "bundled" });
1169
+ }
1170
+ const hostDependencies = dependenciesOf(host);
1171
+ const projectDependencies = dependenciesOf(project);
1172
+ const folders = new Set();
1173
+ const records = [];
1174
+ for (const [pluginId, mapping] of mappings) {
1175
+ const declared = hostDependencies[mapping.name] ?? projectDependencies[mapping.name];
1176
+ if (declared === undefined) {
1177
+ if (options.skipInvalid)
1178
+ continue;
1179
+ throw new Error(`Missing npm plugin dependency: ${mapping.name}. Run npm install in the project directory.`);
1180
+ }
1181
+ let entry;
1182
+ try {
1183
+ entry = readPackageEntry(mapping.source === "project" ? projectRoot : packageRoot, mapping.name);
1184
+ }
1185
+ catch (error) {
1186
+ if (options.skipInvalid)
1187
+ continue;
1188
+ throw error;
1189
+ }
1190
+ const record = recordFromPackage(pluginId, mapping.name, mapping.source, entry.root, entry.metadata, side);
1191
+ if (!record) {
1192
+ if (options.skipInvalid || mapping.source === "project")
1193
+ continue;
1194
+ throw new Error(`Invalid ${side ?? "plugin"} entry in npm plugin ${mapping.name}.`);
1195
+ }
1196
+ const folder = record.folder;
1197
+ if (folders.has(folder)) {
1198
+ if (options.skipInvalid)
1199
+ continue;
1200
+ throw new Error(`Duplicate npm plugin asset directory: ${folder}.`);
1201
+ }
1202
+ folders.add(folder);
1203
+ records.push(record);
1204
+ }
1205
+ return records;
1206
+ }
1207
+ function runNpm(args, cwd) {
1208
+ return new Promise((resolve, reject) => {
1209
+ const npmCLI = process.env.npm_execpath;
1210
+ const invocation = npmCLI && /npm(?:-cli)?\.(?:c?js|mjs)$/.test(npmCLI)
1211
+ ? { command: process.execPath, args: [npmCLI, ...args], shell: false }
1212
+ : process.platform === "win32"
1213
+ ? {
1214
+ command: process.env.ComSpec || "cmd.exe",
1215
+ args: [
1216
+ "/d",
1217
+ "/s",
1218
+ "/c",
1219
+ `npm ${args.map((item) => `"${item.replace(/"/g, '""')}"`).join(" ")}`
1220
+ ],
1221
+ shell: false
1222
+ }
1223
+ : { command: "npm", args, shell: false };
1224
+ const child = (0, child_process_1.spawn)(invocation.command, invocation.args, {
1225
+ cwd,
1226
+ shell: invocation.shell,
1227
+ windowsHide: true,
1228
+ stdio: ["ignore", "pipe", "pipe"],
1229
+ env: { ...process.env, npm_config_update_notifier: "false" }
1230
+ });
1231
+ let output = "";
1232
+ const append = (data) => {
1233
+ output += data.toString();
1234
+ if (output.length > 16000)
1235
+ output = output.slice(-16000);
1236
+ };
1237
+ child.stdout?.on("data", append);
1238
+ child.stderr?.on("data", append);
1239
+ const action = args.find((argument) => argument === "install" || argument === "uninstall") || "operation";
1240
+ const timer = setTimeout(() => {
1241
+ child.kill("SIGKILL");
1242
+ reject(new Error(`npm ${action} timed out.`));
1243
+ }, 10 * 60 * 1000);
1244
+ timer.unref?.();
1245
+ child.once("error", (error) => {
1246
+ clearTimeout(timer);
1247
+ reject(error);
1248
+ });
1249
+ child.once("close", (code) => {
1250
+ clearTimeout(timer);
1251
+ if (code === 0)
1252
+ return resolve(output);
1253
+ reject(new Error(`npm ${action} failed (${code}): ${output.trim()}`));
1254
+ });
1255
+ });
1256
+ }
1257
+ const operationsKey = Symbol.for("elementspanel.npm.operations");
1258
+ const shared = globalThis;
1259
+ const operations = (shared[operationsKey] ??= new Map());
1260
+ const LOCK_NAME = ".elementspanel-npm.lock";
1261
+ async function acquireProjectLock(projectRoot) {
1262
+ await fs_extra_1.default.ensureDir(projectRoot);
1263
+ const directory = path_1.default.join(projectRoot, LOCK_NAME);
1264
+ const deadline = Date.now() + 11 * 60 * 1000;
1265
+ while (true) {
1266
+ try {
1267
+ await fs_extra_1.default.mkdir(directory);
1268
+ await fs_extra_1.default.writeJson(path_1.default.join(directory, "owner.json"), { pid: process.pid });
1269
+ return () => fs_extra_1.default.remove(directory);
1270
+ }
1271
+ catch (error) {
1272
+ if (error.code !== "EEXIST")
1273
+ throw error;
1274
+ try {
1275
+ const owner = await fs_extra_1.default.readJson(path_1.default.join(directory, "owner.json"));
1276
+ try {
1277
+ process.kill(owner.pid, 0);
1278
+ }
1279
+ catch (failure) {
1280
+ if (failure.code === "ESRCH") {
1281
+ await fs_extra_1.default.remove(directory);
1282
+ continue;
1283
+ }
1284
+ }
1285
+ }
1286
+ catch {
1287
+ if (Date.now() - ((await fs_extra_1.default.stat(directory).catch(() => null))?.mtimeMs ?? Date.now()) >
1288
+ 60_000) {
1289
+ await fs_extra_1.default.remove(directory);
1290
+ continue;
1291
+ }
1292
+ }
1293
+ if (Date.now() > deadline)
1294
+ throw new Error("Another npm plugin operation is still running.");
1295
+ await new Promise((resolve) => setTimeout(resolve, 200));
1296
+ }
1297
+ }
1298
+ }
1299
+ function npmPluginOperationPending() {
1300
+ const { packageRoot, projectRoot } = npmPluginRoots();
1301
+ return (operations.size > 0 ||
1302
+ [packageRoot, projectRoot].some((root) => fs_extra_1.default.existsSync(path_1.default.join(root, LOCK_NAME))));
1303
+ }
1304
+ function withProjectLock(projectRoot, operation) {
1305
+ const key = process.platform === "win32" ? projectRoot.toLowerCase() : projectRoot;
1306
+ const previous = operations.get(key) ?? Promise.resolve();
1307
+ const result = previous.then(async () => {
1308
+ const release = await acquireProjectLock(projectRoot);
1309
+ try {
1310
+ return await operation();
1311
+ }
1312
+ finally {
1313
+ await release();
1314
+ }
1315
+ });
1316
+ const settled = result.then(() => { }, () => { });
1317
+ operations.set(key, settled);
1318
+ return result.finally(() => {
1319
+ if (operations.get(key) === settled)
1320
+ operations.delete(key);
1321
+ });
1322
+ }
1323
+ /** Core services keep the connection and management interface available. */
1324
+ function isEssentialNpmPlugin(pluginId) {
1325
+ return ["i18n", "storage", "runtime", "server", "console", "config"].includes(pluginId);
1326
+ }
1327
+ /** npm changes dependencies and lockfiles; only append our plugin mapping afterwards. */
1328
+ async function installNpmPlugin(options) {
1329
+ const { name, side } = options;
1330
+ const version = options.version || "latest";
1331
+ if (!PACKAGE_NAME.test(name))
1332
+ throw new Error(`Invalid npm plugin package: ${name}`);
1333
+ if (options.pluginId && !PLUGIN_ID.test(options.pluginId))
1334
+ throw new Error(`Invalid plugin id: ${options.pluginId}`);
1335
+ if (!VERSION.test(version))
1336
+ throw new Error(`Invalid plugin version: ${version}`);
1337
+ const { packageRoot, projectRoot } = npmPluginRoots(options);
1338
+ return withProjectLock(projectRoot, async () => {
1339
+ const host = readJson(path_1.default.join(packageRoot, "package.json"));
1340
+ if (!host)
1341
+ throw new Error(`Missing runtime package metadata: ${packageRoot}`);
1342
+ const hostId = Object.entries(mappingsOf(host)).find(([, value]) => value === name && dependenciesOf(host)[name] !== undefined)?.[0];
1343
+ const targetRoot = hostId ? packageRoot : projectRoot;
1344
+ if (hostId && isEssentialNpmPlugin(hostId))
1345
+ throw new Error(`The essential plugin "${hostId}" cannot be replaced while running.`);
1346
+ if (options.pluginId &&
1347
+ mappingsOf(host)[options.pluginId] &&
1348
+ dependenciesOf(host)[String(mappingsOf(host)[options.pluginId])] !== undefined &&
1349
+ mappingsOf(host)[options.pluginId] !== name)
1350
+ throw new Error(`BUNDLED:${options.pluginId}`);
1351
+ const original = readProjectManifest(targetRoot);
1352
+ const previous = dependenciesOf(original)[name];
1353
+ let previousVersion = previous;
1354
+ if (previous) {
1355
+ try {
1356
+ previousVersion = readPackageEntry(targetRoot, name).metadata.version || previous;
1357
+ }
1358
+ catch {
1359
+ /* Keep the declared spec if the previous package is incomplete. */
1360
+ }
1361
+ }
1362
+ const backups = new Map();
1363
+ for (const filename of ["package.json", "package-lock.json", "npm-shrinkwrap.json"]) {
1364
+ const target = path_1.default.join(targetRoot, filename);
1365
+ backups.set(target, fs_extra_1.default.existsSync(target) ? fs_extra_1.default.readFileSync(target) : null);
1366
+ }
1367
+ try {
1368
+ await runNpm([
1369
+ "--prefix",
1370
+ targetRoot,
1371
+ "install",
1372
+ `${name}@${version}`,
1373
+ "--save",
1374
+ "--save-exact",
1375
+ "--no-fund",
1376
+ "--no-audit",
1377
+ "--ignore-scripts"
1378
+ ], targetRoot);
1379
+ const entry = readPackageEntry(targetRoot, name);
1380
+ const pluginId = options.pluginId || stringValue(objectValue(entry.metadata.elementspanel)?.id);
1381
+ if (!PLUGIN_ID.test(pluginId))
1382
+ throw new Error(`Invalid plugin id in ${name}.`);
1383
+ if (mappingsOf(host)[pluginId] &&
1384
+ dependenciesOf(host)[String(mappingsOf(host)[pluginId])] !== undefined &&
1385
+ mappingsOf(host)[pluginId] !== name)
1386
+ throw new Error(`Plugin id ${pluginId} belongs to a bundled dependency.`);
1387
+ if (hostId && pluginId !== hostId)
1388
+ throw new Error(`The package ${name} changed its plugin id from ${hostId} to ${pluginId}.`);
1389
+ const mapped = mappingsOf(readProjectManifest(targetRoot))[pluginId];
1390
+ if (mapped !== undefined && mapped !== name)
1391
+ throw new Error(`Plugin id ${pluginId} is already installed as ${String(mapped)}.`);
1392
+ const record = recordFromPackage(pluginId, name, hostId ? "bundled" : "project", entry.root, entry.metadata, side);
1393
+ if (!record)
1394
+ throw new Error(`The package ${name} does not provide a ${side} plugin named ${pluginId}.`);
1395
+ if (/^[0-9]/.test(version) && record.version !== version)
1396
+ throw new Error(`The installed package ${name} is ${record.version}, expected ${version}.`);
1397
+ if (!process.env.EPANEL_PACKAGE_ROOT &&
1398
+ (0, plugin_manifest_1.discoverPlugins)(path_1.default.join(packageRoot, "plugins"), {
1399
+ entryFields: [],
1400
+ includeDisabled: true
1401
+ }).some((plugin) => plugin.manifest.id === pluginId))
1402
+ throw new Error(`Source plugin ${pluginId} cannot be replaced with an npm dependency.`);
1403
+ const manifest = (0, plugin_manifest_1.readPluginManifest)(record.directory);
1404
+ if (!manifest || manifest.id !== pluginId || manifest.version !== record.version)
1405
+ throw new Error(`The plugin manifest in ${name} does not match its package.`);
1406
+ (0, plugin_contract_1.validatePluginCompatibility)(manifest.elements);
1407
+ for (const field of ["backend", "frontend", "panel", "daemon", "main", "entry", "ui"]) {
1408
+ if (typeof manifest[field] === "string" &&
1409
+ !(0, plugin_manifest_1.resolvePluginEntry)(record.directory, manifest, { entryFields: [field] }))
1410
+ throw new Error(`Invalid ${field} entry in ${name}.`);
1411
+ }
1412
+ const others = listNpmPlugins(side, { packageRoot, projectRoot, skipInvalid: true });
1413
+ if (others.some((plugin) => plugin.pluginId === pluginId && plugin.name !== name))
1414
+ throw new Error(`Plugin id ${pluginId} is already provided by another dependency.`);
1415
+ const collision = others.find((plugin) => plugin.pluginId !== pluginId && plugin.folder === record.folder);
1416
+ if (collision)
1417
+ throw new Error(`Plugin asset directory ${record.folder} is already used by ${collision.name}.`);
1418
+ const updated = readProjectManifest(targetRoot);
1419
+ updated.dependencies = { ...dependenciesOf(updated), [name]: record.version };
1420
+ updated.elementspanel = {
1421
+ ...objectValue(updated.elementspanel),
1422
+ plugins: { ...mappingsOf(updated), [pluginId]: name }
1423
+ };
1424
+ writeProjectManifest(targetRoot, updated);
1425
+ return record;
1426
+ }
1427
+ catch (error) {
1428
+ try {
1429
+ await runNpm(previous
1430
+ ? [
1431
+ "--prefix",
1432
+ targetRoot,
1433
+ "install",
1434
+ `${name}@${String(previousVersion)}`,
1435
+ "--save",
1436
+ "--no-fund",
1437
+ "--no-audit",
1438
+ "--ignore-scripts"
1439
+ ]
1440
+ : [
1441
+ "--prefix",
1442
+ targetRoot,
1443
+ "uninstall",
1444
+ name,
1445
+ "--no-fund",
1446
+ "--no-audit",
1447
+ "--ignore-scripts"
1448
+ ], targetRoot);
1449
+ }
1450
+ catch (rollback) {
1451
+ throw new Error(`${String(error)}; npm rollback failed: ${String(rollback)}`);
1452
+ }
1453
+ finally {
1454
+ for (const [filename, content] of backups) {
1455
+ if (content === null)
1456
+ await fs_extra_1.default.remove(filename);
1457
+ else
1458
+ await fs_extra_1.default.writeFile(filename, content);
1459
+ }
1460
+ }
1461
+ throw error;
1462
+ }
1463
+ });
1464
+ }
1465
+ /** Remove the dependency from the manifest that owns it, preserving user configuration. */
1466
+ async function uninstallNpmPlugin(pluginId, options = {}) {
1467
+ if (!PLUGIN_ID.test(pluginId))
1468
+ throw new Error(`Invalid plugin id: ${pluginId}`);
1469
+ if (isEssentialNpmPlugin(pluginId))
1470
+ throw new Error(`The essential plugin "${pluginId}" cannot be uninstalled.`);
1471
+ const { packageRoot, projectRoot } = npmPluginRoots(options);
1472
+ return withProjectLock(projectRoot, async () => {
1473
+ const host = readJson(path_1.default.join(packageRoot, "package.json"));
1474
+ const bundledName = mappingsOf(host)[pluginId];
1475
+ const targetRoot = typeof bundledName === "string" && dependenciesOf(host)[bundledName] !== undefined
1476
+ ? packageRoot
1477
+ : projectRoot;
1478
+ const manifest = readProjectManifest(targetRoot);
1479
+ const name = mappingsOf(manifest)[pluginId] ??
1480
+ listNpmPlugins(undefined, { packageRoot, projectRoot, skipInvalid: true }).find((item) => item.pluginId === pluginId)?.name;
1481
+ if (typeof name !== "string" || !PACKAGE_NAME.test(name))
1482
+ return null;
1483
+ const record = listNpmPlugins(undefined, { packageRoot, projectRoot, skipInvalid: true }).find((item) => item.pluginId === pluginId);
1484
+ await runNpm(["--prefix", targetRoot, "uninstall", name, "--no-fund", "--no-audit", "--ignore-scripts"], targetRoot);
1485
+ const updated = readProjectManifest(targetRoot);
1486
+ const plugins = { ...mappingsOf(updated) };
1487
+ delete plugins[pluginId];
1488
+ updated.elementspanel = { ...objectValue(updated.elementspanel), plugins };
1489
+ writeProjectManifest(targetRoot, updated);
1490
+ return record ?? null;
1491
+ });
1492
+ }
1493
+
1494
+
1495
+ /***/ },
1496
+
1497
+ /***/ "../common/src/plugin_overrides.ts"
1498
+ (__unused_webpack_module, exports, __webpack_require__) {
1499
+
1500
+
1501
+ var __importDefault = (this && this.__importDefault) || function (mod) {
1502
+ return (mod && mod.__esModule) ? mod : { "default": mod };
1503
+ };
1504
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
1505
+ exports.pluginOverridesPath = pluginOverridesPath;
1506
+ exports.readPluginOverrides = readPluginOverrides;
1507
+ exports.validatePluginOverride = validatePluginOverride;
1508
+ exports.applyPluginOverrides = applyPluginOverrides;
1509
+ exports.writePluginOverride = writePluginOverride;
1510
+ const node_fs_1 = __importDefault(__webpack_require__("node:fs"));
1511
+ const node_path_1 = __importDefault(__webpack_require__("node:path"));
1512
+ const node_crypto_1 = __webpack_require__("node:crypto");
1513
+ function pluginOverridesPath() {
1514
+ return node_path_1.default.resolve(process.cwd(), "data", "plugin-overrides.json");
1515
+ }
1516
+ /** Package defaults stay immutable; only the user's last layer lives in data. */
1517
+ function readPluginOverrides(filename = pluginOverridesPath()) {
1518
+ let raw;
1519
+ try {
1520
+ raw = node_fs_1.default.readFileSync(filename, "utf8");
1521
+ }
1522
+ catch (error) {
1523
+ if (error.code === "ENOENT")
1524
+ return { version: 1, plugins: {} };
1525
+ throw error;
1526
+ }
1527
+ const data = JSON.parse(raw);
1528
+ if (data?.version !== 1 ||
1529
+ !data.plugins ||
1530
+ typeof data.plugins !== "object" ||
1531
+ Array.isArray(data.plugins))
1532
+ throw new Error("Invalid plugin-overrides.json");
1533
+ for (const entry of Object.values(data.plugins))
1534
+ validatePluginOverride(entry);
1535
+ return data;
1536
+ }
1537
+ function validatePluginOverride(value) {
1538
+ if (!value || typeof value !== "object" || Array.isArray(value))
1539
+ throw new Error("Invalid plugin override.");
1540
+ const entry = value;
1541
+ if (entry.enabled !== undefined && typeof entry.enabled !== "boolean")
1542
+ throw new Error("Invalid plugin enablement.");
1543
+ if (entry.config !== undefined &&
1544
+ (!entry.config || typeof entry.config !== "object" || Array.isArray(entry.config)))
1545
+ throw new Error("Plugin config must be an object.");
1546
+ if (Object.keys(value).some((key) => !["enabled", "config"].includes(key)))
1547
+ throw new Error("Unknown plugin override field.");
1548
+ }
1549
+ function applyPluginOverrides(plugins, filename = pluginOverridesPath()) {
1550
+ const overrides = readPluginOverrides(filename).plugins;
1551
+ return plugins.map((plugin) => ({
1552
+ ...plugin,
1553
+ manifest: {
1554
+ ...plugin.manifest,
1555
+ ...(Object.prototype.hasOwnProperty.call(overrides, plugin.manifest.id)
1556
+ ? overrides[plugin.manifest.id]
1557
+ : {})
1558
+ }
1559
+ }));
1560
+ }
1561
+ /** Call inside the host's change queue; rename keeps readers off partial JSON. */
1562
+ function writePluginOverride(id, patch, filename = pluginOverridesPath()) {
1563
+ validatePluginOverride(patch);
1564
+ if (!id || ["__proto__", "constructor", "prototype"].includes(id))
1565
+ throw new Error("Invalid plugin id.");
1566
+ const data = readPluginOverrides(filename);
1567
+ data.plugins[id] = {
1568
+ ...(Object.prototype.hasOwnProperty.call(data.plugins, id) ? data.plugins[id] : {}),
1569
+ ...patch
1570
+ };
1571
+ node_fs_1.default.mkdirSync(node_path_1.default.dirname(filename), { recursive: true });
1572
+ const temporary = `${filename}.${(0, node_crypto_1.randomBytes)(8).toString("hex")}.tmp`;
1573
+ try {
1574
+ node_fs_1.default.writeFileSync(temporary, `${JSON.stringify(data, null, 2)}\n`, { mode: 0o600, flag: "wx" });
1575
+ node_fs_1.default.renameSync(temporary, filename);
1576
+ }
1577
+ finally {
1578
+ node_fs_1.default.rmSync(temporary, { force: true });
1579
+ }
1580
+ }
1581
+
1582
+
1583
+ /***/ },
1584
+
1585
+ /***/ "../common/src/plugin_revision.ts"
1586
+ (__unused_webpack_module, exports, __webpack_require__) {
1587
+
1588
+
1589
+ var __importDefault = (this && this.__importDefault) || function (mod) {
1590
+ return (mod && mod.__esModule) ? mod : { "default": mod };
1591
+ };
1592
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
1593
+ exports.pluginDirectoryRevision = pluginDirectoryRevision;
1594
+ const node_fs_1 = __importDefault(__webpack_require__("node:fs"));
1595
+ const node_path_1 = __importDefault(__webpack_require__("node:path"));
1596
+ const node_crypto_1 = __webpack_require__("node:crypto");
1597
+ const cache = new Map();
1598
+ /** One revision addresses the complete module graph, including CSS and nested chunks. */
1599
+ function pluginDirectoryRevision(directory) {
1600
+ const files = [];
1601
+ const walk = (root) => {
1602
+ if (!node_fs_1.default.existsSync(root))
1603
+ return;
1604
+ for (const item of node_fs_1.default
1605
+ .readdirSync(root, { withFileTypes: true })
1606
+ .sort((a, b) => a.name.localeCompare(b.name))) {
1607
+ const absolute = node_path_1.default.join(root, item.name);
1608
+ if (item.isDirectory())
1609
+ walk(absolute);
1610
+ else if (item.isFile())
1611
+ files.push({
1612
+ relative: node_path_1.default.relative(directory, absolute),
1613
+ absolute,
1614
+ stat: node_fs_1.default.statSync(absolute)
1615
+ });
1616
+ }
1617
+ };
1618
+ walk(directory);
1619
+ const signature = JSON.stringify(files.map(({ relative, stat }) => [relative, stat.size, stat.mtimeMs, stat.ctimeMs]));
1620
+ const previous = cache.get(directory);
1621
+ if (previous?.signature === signature)
1622
+ return previous.revision;
1623
+ const hash = (0, node_crypto_1.createHash)("sha256");
1624
+ for (const file of files)
1625
+ hash
1626
+ .update(file.relative)
1627
+ .update("\0")
1628
+ .update(new Uint8Array(node_fs_1.default.readFileSync(file.absolute)))
1629
+ .update("\0");
1630
+ const revision = hash.digest("hex").slice(0, 24);
1631
+ if (cache.size > 1024)
1632
+ cache.clear();
1633
+ cache.set(directory, { signature, revision });
1634
+ return revision;
1635
+ }
1636
+
1637
+
1638
+ /***/ },
1639
+
1640
+ /***/ "../common/src/plugin_watch.ts"
1641
+ (__unused_webpack_module, exports, __webpack_require__) {
1642
+
1643
+
1644
+ var __importDefault = (this && this.__importDefault) || function (mod) {
1645
+ return (mod && mod.__esModule) ? mod : { "default": mod };
1646
+ };
1647
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
1648
+ exports.watchNpmPlugins = watchNpmPlugins;
1649
+ const node_fs_1 = __importDefault(__webpack_require__("node:fs"));
1650
+ const node_path_1 = __importDefault(__webpack_require__("node:path"));
1651
+ const plugin_npm_1 = __webpack_require__("../common/src/plugin_npm.ts");
1652
+ /** Poll package boundaries, allowing npm to finish replacing files before reconciliation. */
1653
+ function watchNpmPlugins(ctx, side, reload, changed) {
1654
+ const stamp = () => {
1655
+ const { packageRoot, projectRoot } = (0, plugin_npm_1.npmPluginRoots)();
1656
+ if (!node_fs_1.default.existsSync(node_path_1.default.join(packageRoot, "package.json")))
1657
+ return "";
1658
+ const files = [];
1659
+ for (const root of [packageRoot, projectRoot])
1660
+ for (const name of [
1661
+ "package.json",
1662
+ "package-lock.json",
1663
+ "npm-shrinkwrap.json",
1664
+ "node_modules/.package-lock.json"
1665
+ ])
1666
+ files.push(node_path_1.default.join(root, name));
1667
+ for (const plugin of (0, plugin_npm_1.listNpmPlugins)(side, { skipInvalid: true }))
1668
+ files.push(node_path_1.default.join(plugin.packageRoot, "package.json"), node_path_1.default.join(plugin.directory, "plugin.json"));
1669
+ return JSON.stringify(files.map((filename) => {
1670
+ try {
1671
+ const stat = node_fs_1.default.statSync(filename);
1672
+ return [filename, stat.size, stat.mtimeMs, stat.ctimeMs];
1673
+ }
1674
+ catch {
1675
+ return [filename, null];
1676
+ }
1677
+ }));
1678
+ };
1679
+ let applied = stamp();
1680
+ let candidate = applied;
1681
+ let busy = false;
1682
+ ctx.setInterval(() => {
1683
+ if (busy || (0, plugin_npm_1.npmPluginOperationPending)())
1684
+ return;
1685
+ try {
1686
+ const next = stamp();
1687
+ if (next === applied) {
1688
+ candidate = next;
1689
+ return;
1690
+ }
1691
+ if (next !== candidate) {
1692
+ candidate = next;
1693
+ return;
1694
+ }
1695
+ busy = true;
1696
+ void reload()
1697
+ .then(() => {
1698
+ applied = next;
1699
+ candidate = next;
1700
+ changed?.();
1701
+ })
1702
+ .catch((error) => ctx.logger.warn("Failed to synchronize npm plugins", error))
1703
+ .finally(() => {
1704
+ busy = false;
1705
+ });
1706
+ }
1707
+ catch (error) {
1708
+ ctx.logger.warn("Failed to inspect npm plugins", error);
1709
+ }
1710
+ }, 2000);
1711
+ }
1712
+
1713
+
1714
+ /***/ },
1715
+
1716
+ /***/ "../common/src/process_tools.ts"
1717
+ (__unused_webpack_module, exports, __webpack_require__) {
1718
+
1719
+
1720
+ var __importDefault = (this && this.__importDefault) || function (mod) {
1721
+ return (mod && mod.__esModule) ? mod : { "default": mod };
1722
+ };
1723
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
1724
+ exports.ProcessWrapper = exports.StartError = void 0;
1725
+ exports.killProcess = killProcess;
1726
+ const child_process_1 = __webpack_require__("child_process");
1727
+ const os_1 = __importDefault(__webpack_require__("os"));
1728
+ const child_process_2 = __importDefault(__webpack_require__("child_process"));
1729
+ const path_1 = __importDefault(__webpack_require__("path"));
1730
+ const events_1 = __importDefault(__webpack_require__("events"));
1731
+ const iconv_lite_1 = __importDefault(__webpack_require__("iconv-lite"));
1732
+ const process_1 = __importDefault(__webpack_require__("process"));
1733
+ class StartError extends Error {
1734
+ }
1735
+ exports.StartError = StartError;
1736
+ class ProcessWrapper extends events_1.default {
1737
+ file;
1738
+ args;
1739
+ cwd;
1740
+ timeout;
1741
+ code;
1742
+ option;
1743
+ process;
1744
+ pid;
1745
+ lastExitCode;
1746
+ completed = false;
1747
+ errMsg = {
1748
+ timeoutErr: "task timeout!",
1749
+ exitErr: "task error!",
1750
+ startErr: "task start error!"
1751
+ };
1752
+ constructor(file, args, cwd, timeout = 0, code = "utf-8", option = {}) {
1753
+ super();
1754
+ this.file = file;
1755
+ this.args = args;
1756
+ this.cwd = cwd;
1757
+ this.timeout = timeout;
1758
+ this.code = code;
1759
+ this.option = option;
1760
+ }
1761
+ setErrMsg(errMsg) {
1762
+ this.errMsg = errMsg;
1763
+ }
1764
+ start() {
1765
+ if (this.process)
1766
+ return Promise.reject(new Error("The process is already running"));
1767
+ return new Promise((resolve, reject) => {
1768
+ const stdoutDecoder = iconv_lite_1.default.decodeStream(this.code);
1769
+ const stderrDecoder = iconv_lite_1.default.decodeStream(this.code);
1770
+ let timeTask;
1771
+ let settled = false;
1772
+ let spawned = false;
1773
+ const settle = (error) => {
1774
+ if (settled)
1775
+ return;
1776
+ settled = true;
1777
+ if (timeTask)
1778
+ clearTimeout(timeTask);
1779
+ if (error)
1780
+ reject(error);
1781
+ else
1782
+ resolve(true);
1783
+ };
1784
+ const subProcess = child_process_2.default.spawn(this.file, this.args, {
1785
+ stdio: "pipe",
1786
+ windowsHide: true,
1787
+ cwd: path_1.default.normalize(this.cwd),
1788
+ ...this.option
1789
+ });
1790
+ this.process = subProcess;
1791
+ this.pid = subProcess.pid;
1792
+ this.lastExitCode = undefined;
1793
+ this.completed = false;
1794
+ const fail = (error) => {
1795
+ if (settled)
1796
+ return;
1797
+ settle(error);
1798
+ if (subProcess.pid && subProcess.exitCode === null && subProcess.signalCode === null) {
1799
+ try {
1800
+ killProcess(subProcess.pid, subProcess);
1801
+ }
1802
+ catch (error) {
1803
+ console.error("[ProcessWrapper kill error]", error);
1804
+ }
1805
+ }
1806
+ };
1807
+ subProcess.once("spawn", () => {
1808
+ spawned = true;
1809
+ this.emit("start", subProcess.pid);
1810
+ });
1811
+ for (const [source, decoder] of [
1812
+ [subProcess.stdout, stdoutDecoder],
1813
+ [subProcess.stderr, stderrDecoder]
1814
+ ]) {
1815
+ decoder.on("data", (text) => this.emit("data", text));
1816
+ decoder.on("error", fail);
1817
+ source.on("error", fail);
1818
+ source.pipe(decoder);
1819
+ }
1820
+ subProcess.stdin.on("error", fail);
1821
+ subProcess.once("error", (error) => {
1822
+ fail(new Error(`${spawned ? this.errMsg.exitErr : this.errMsg.startErr} ${error.message}`));
1823
+ });
1824
+ subProcess.once("exit", (code) => {
1825
+ this.lastExitCode = code;
1826
+ this.emit("exit", code);
1827
+ });
1828
+ // `close` follows the final stdout/stderr data and also follows a failed spawn.
1829
+ subProcess.once("close", (code) => {
1830
+ this.lastExitCode = code;
1831
+ this.completed = true;
1832
+ this.process = undefined;
1833
+ settle(code === 0 ? undefined : new Error(this.errMsg.exitErr));
1834
+ });
1835
+ if (this.timeout > 0) {
1836
+ timeTask = setTimeout(() => {
1837
+ fail(new Error(this.errMsg.timeoutErr));
1838
+ }, 1000 * this.timeout);
1839
+ }
1840
+ });
1841
+ }
1842
+ getPid() {
1843
+ return this.process?.pid;
1844
+ }
1845
+ write(data) {
1846
+ return this.process?.stdin?.write(iconv_lite_1.default.encode(data, this.code));
1847
+ }
1848
+ kill() {
1849
+ if (this.process?.pid)
1850
+ killProcess(this.process?.pid, this.process);
1851
+ }
1852
+ status() {
1853
+ return this.completed;
1854
+ }
1855
+ exitCode() {
1856
+ return this.process?.exitCode ?? this.lastExitCode;
1857
+ }
1858
+ }
1859
+ exports.ProcessWrapper = ProcessWrapper;
1860
+ function killProcess(pid, process, signal) {
1861
+ const numericPid = Number(pid);
1862
+ if (!Number.isSafeInteger(numericPid) || numericPid <= 0) {
1863
+ throw new Error("Invalid process ID");
1864
+ }
1865
+ try {
1866
+ if (os_1.default.platform() === "win32") {
1867
+ (0, child_process_1.execFileSync)("taskkill", ["/PID", String(numericPid), "/T", "/F"], { windowsHide: true });
1868
+ return true;
1869
+ }
1870
+ return process_1.default.kill(numericPid, signal || "SIGKILL");
1871
+ }
1872
+ catch (err) {
1873
+ return signal ? process.kill(signal) : process.kill("SIGKILL");
1874
+ }
1875
+ }
1876
+
1877
+
1878
+ /***/ },
1879
+
1880
+ /***/ "../common/src/query_wrapper.ts"
1881
+ (__unused_webpack_module, exports) {
1882
+
1883
+
1884
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
1885
+ exports.QueryWrapper = exports.LocalFileSource = exports.MySqlSource = exports.QueryMapWrapper = void 0;
1886
+ function paginate(data, page, pageSize) {
1887
+ if (!Number.isSafeInteger(page) || page < 1 || !Number.isSafeInteger(pageSize) || pageSize < 1) {
1888
+ throw new RangeError("page and pageSize must be positive integers");
1889
+ }
1890
+ const start = (page - 1) * pageSize;
1891
+ return {
1892
+ page,
1893
+ pageSize,
1894
+ maxPage: Math.ceil(data.length / pageSize),
1895
+ total: data.length,
1896
+ data: data.slice(start, start + pageSize)
1897
+ };
1898
+ }
1899
+ // Provide the MAP query interface used by the routing layer
1900
+ class QueryMapWrapper {
1901
+ map;
1902
+ constructor(map) {
1903
+ this.map = map;
1904
+ }
1905
+ select(condition) {
1906
+ const result = [];
1907
+ this.map.forEach((v) => {
1908
+ if (condition(v))
1909
+ result.push(v);
1910
+ });
1911
+ return result;
1912
+ }
1913
+ page(data, page = 1, pageSize = 10) {
1914
+ return paginate(data, page, pageSize);
1915
+ }
1916
+ }
1917
+ exports.QueryMapWrapper = QueryMapWrapper;
1918
+ // MYSQL data source
1919
+ class MySqlSource {
1920
+ data;
1921
+ constructor(data) {
1922
+ this.data = data;
1923
+ }
1924
+ selectPage(condition, page, pageSize) {
1925
+ return {
1926
+ page,
1927
+ pageSize,
1928
+ maxPage: 0,
1929
+ total: 0,
1930
+ data: []
1931
+ };
1932
+ }
1933
+ select(condition) {
1934
+ return [];
1935
+ }
1936
+ update(condition, data) { }
1937
+ delete(condition) { }
1938
+ insert(data) { }
1939
+ }
1940
+ exports.MySqlSource = MySqlSource;
1941
+ // local file data source (embedded microdatabase)
1942
+ class LocalFileSource {
1943
+ data;
1944
+ constructor(data) {
1945
+ this.data = data;
1946
+ }
1947
+ selectPage(condition, page = 1, pageSize = 10) {
1948
+ return this.page(this.select(condition), page, pageSize);
1949
+ }
1950
+ page(data, page = 1, pageSize = 10) {
1951
+ return paginate(data, page, pageSize);
1952
+ }
1953
+ select(condition = {}) {
1954
+ const result = [];
1955
+ const entries = Object.entries(condition);
1956
+ this.data.forEach((v) => {
1957
+ for (const [key, targetValue] of entries) {
1958
+ const dataValue = v[key];
1959
+ if (typeof targetValue === "string" &&
1960
+ targetValue.startsWith("%") &&
1961
+ targetValue.endsWith("%")) {
1962
+ if (typeof dataValue !== "string" || !dataValue.includes(targetValue.slice(1, -1)))
1963
+ return;
1964
+ }
1965
+ else if (targetValue !== dataValue) {
1966
+ return;
1967
+ }
1968
+ }
1969
+ result.push(v);
1970
+ });
1971
+ return result;
1972
+ }
1973
+ update(condition, data) { }
1974
+ delete(condition) { }
1975
+ insert(data) { }
1976
+ }
1977
+ exports.LocalFileSource = LocalFileSource;
1978
+ // Provide the unified data query interface used by the routing layer
1979
+ class QueryWrapper {
1980
+ dataSource;
1981
+ constructor(dataSource) {
1982
+ this.dataSource = dataSource;
1983
+ }
1984
+ selectPage(condition, page = 1, pageSize = 10) {
1985
+ return this.dataSource.selectPage(condition, page, pageSize);
1986
+ }
1987
+ }
1988
+ exports.QueryWrapper = QueryWrapper;
1989
+
1990
+
1991
+ /***/ },
1992
+
1993
+ /***/ "../common/src/runtime_paths.ts"
1994
+ (__unused_webpack_module, exports, __webpack_require__) {
1995
+
1996
+
1997
+ var __importDefault = (this && this.__importDefault) || function (mod) {
1998
+ return (mod && mod.__esModule) ? mod : { "default": mod };
1999
+ };
2000
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
2001
+ exports.isPackagedRuntime = isPackagedRuntime;
2002
+ exports.packagePath = packagePath;
2003
+ exports.packagePluginRoots = packagePluginRoots;
2004
+ exports.isPackagePlugin = isPackagePlugin;
2005
+ exports.bundledToolPath = bundledToolPath;
2006
+ exports.ensureExecutable = ensureExecutable;
2007
+ const node_fs_1 = __importDefault(__webpack_require__("node:fs"));
2008
+ const node_os_1 = __importDefault(__webpack_require__("node:os"));
2009
+ const node_path_1 = __importDefault(__webpack_require__("node:path"));
2010
+ /** Set by the npm worker before any application module is loaded. */
2011
+ function isPackagedRuntime() {
2012
+ return Boolean(process.env.EPANEL_PACKAGE_ROOT);
2013
+ }
2014
+ /** Code and bundled assets belong to the package; cwd always belongs to the user. */
2015
+ function packagePath(...parts) {
2016
+ return node_path_1.default.resolve(process.env.EPANEL_PACKAGE_ROOT || process.cwd(), ...parts);
2017
+ }
2018
+ function npmPluginRoots(side) {
2019
+ // The same resolver is used by the CLI before the bundled application starts.
2020
+ // A development checkout keeps it in the repository, beside the side packages,
2021
+ // so project-installed npm plugins work there as well.
2022
+ const runtimeRequire = eval("require");
2023
+ const resolver = isPackagedRuntime()
2024
+ ? packagePath("runtime/npm-plugins.cjs")
2025
+ : node_path_1.default.resolve(packagePath(), "..", "scripts", "runtime", "npm-plugins.cjs");
2026
+ // A development process may run from a fixture directory without a runtime
2027
+ // package; there is nothing to resolve in that case.
2028
+ if (!node_fs_1.default.existsSync(node_path_1.default.join(packagePath(), "package.json")))
2029
+ return [];
2030
+ if (!isPackagedRuntime() && !node_fs_1.default.existsSync(resolver))
2031
+ return [];
2032
+ const { resolveNpmPlugins } = runtimeRequire(resolver);
2033
+ return resolveNpmPlugins(packagePath(), side, { skipInvalid: true }).map((record) => ({
2034
+ directory: record.directory,
2035
+ packageRoot: record.packageRoot,
2036
+ packageName: record.name,
2037
+ npmSource: record.source,
2038
+ ...(record.folder ? { folder: record.folder } : {}),
2039
+ // Built-in packages are part of the runtime's default configuration. A
2040
+ // project dependency is installed but remains dormant until the user
2041
+ // enables it through the plugin configuration service.
2042
+ defaultEnabled: record.defaultEnabled !== false
2043
+ }));
2044
+ }
2045
+ function packagePluginRoots(side) {
2046
+ const roots = isPackagedRuntime()
2047
+ ? []
2048
+ : [{ directory: packagePath("plugins"), defaultEnabled: true }];
2049
+ // Project-installed npm plugins load in development too: they are dependencies
2050
+ // of the development project, not of the published runtime package.
2051
+ roots.push(...npmPluginRoots(side));
2052
+ return roots;
2053
+ }
2054
+ function isPackagePlugin(directory) {
2055
+ return npmPluginRoots().some((root) => {
2056
+ const relative = node_path_1.default.relative(root.directory, node_path_1.default.resolve(directory));
2057
+ return (relative === "" ||
2058
+ (!relative.startsWith(`..${node_path_1.default.sep}`) && relative !== ".." && !node_path_1.default.isAbsolute(relative)));
2059
+ });
2060
+ }
2061
+ function bundledToolPath(name) {
2062
+ const platform = node_os_1.default.platform();
2063
+ const arch = node_os_1.default.arch();
2064
+ const suffix = platform === "win32" ? ".exe" : "";
2065
+ const filename = `${name}_${platform}_${arch}${suffix}`;
2066
+ // Published daemon packages download native tools into the installation's
2067
+ // writable lib directory on first start. Development checkouts use the same
2068
+ // repository-local sibling of data and logs.
2069
+ const root = isPackagedRuntime() ? node_path_1.default.resolve(process.cwd(), "lib") : packagePath("lib");
2070
+ const primary = node_path_1.default.join(root, filename);
2071
+ // Zip-Tools names its Intel macOS binary "amd64", while Node reports "x64".
2072
+ if (!node_fs_1.default.existsSync(primary) && name === "file_zip" && platform === "darwin" && arch === "x64") {
2073
+ const alternate = node_path_1.default.join(root, "file_zip_darwin_amd64");
2074
+ if (node_fs_1.default.existsSync(alternate))
2075
+ return alternate;
2076
+ }
2077
+ return primary;
2078
+ }
2079
+ /** Published binaries already carry executable permissions, including in read-only installs. */
2080
+ function ensureExecutable(filename) {
2081
+ if (node_os_1.default.platform() === "win32")
2082
+ return;
2083
+ try {
2084
+ node_fs_1.default.accessSync(filename, node_fs_1.default.constants.X_OK);
2085
+ }
2086
+ catch {
2087
+ node_fs_1.default.chmodSync(filename, 0o755);
2088
+ }
2089
+ }
2090
+
2091
+
2092
+ /***/ },
2093
+
2094
+ /***/ "../common/src/string_utils.ts"
2095
+ (__unused_webpack_module, exports) {
2096
+
2097
+
2098
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
2099
+ exports.removeTrail = removeTrail;
2100
+ function removeTrail(origin, trail) {
2101
+ if (origin.endsWith(trail)) {
2102
+ return origin.slice(0, origin.length - trail.length);
2103
+ }
2104
+ else {
2105
+ return origin;
2106
+ }
2107
+ }
2108
+
2109
+
2110
+ /***/ },
2111
+
2112
+ /***/ "../common/src/system_info.ts"
2113
+ (__unused_webpack_module, exports, __webpack_require__) {
2114
+
2115
+
2116
+ var __importDefault = (this && this.__importDefault) || function (mod) {
2117
+ return (mod && mod.__esModule) ? mod : { "default": mod };
2118
+ };
2119
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
2120
+ exports.systemInfo = systemInfo;
2121
+ const os_1 = __importDefault(__webpack_require__("os"));
2122
+ const os_utils_1 = __importDefault(__webpack_require__("os-utils"));
2123
+ const fs_1 = __importDefault(__webpack_require__("fs"));
2124
+ // System details are updated every time
2125
+ const info = {
2126
+ type: os_1.default.type(),
2127
+ hostname: os_1.default.hostname(),
2128
+ platform: os_1.default.platform(),
2129
+ release: os_1.default.release(),
2130
+ uptime: os_1.default.uptime(),
2131
+ cwd: process.cwd(),
2132
+ loadavg: os_1.default.loadavg(),
2133
+ freemem: 0,
2134
+ cpuUsage: 0,
2135
+ memUsage: 0,
2136
+ totalmem: 0,
2137
+ processCpu: 0,
2138
+ processMem: 0
2139
+ };
2140
+ let previousCpu = process.cpuUsage();
2141
+ let previousTime = process.hrtime.bigint();
2142
+ // periodically refresh the cache
2143
+ function refresh() {
2144
+ const now = process.hrtime.bigint();
2145
+ const cpu = process.cpuUsage();
2146
+ const elapsed = Number(now - previousTime) / 1000;
2147
+ info.processCpu =
2148
+ elapsed > 0 ? (cpu.user - previousCpu.user + cpu.system - previousCpu.system) / elapsed : 0;
2149
+ previousCpu = cpu;
2150
+ previousTime = now;
2151
+ info.uptime = os_1.default.uptime();
2152
+ info.loadavg = os_1.default.loadavg();
2153
+ info.processMem = process.memoryUsage().rss;
2154
+ if (os_1.default.platform() === "linux") {
2155
+ return setLinuxSystemInfo();
2156
+ }
2157
+ return otherSystemInfo();
2158
+ }
2159
+ refresh();
2160
+ setInterval(refresh, 3000).unref();
2161
+ function otherSystemInfo() {
2162
+ info.freemem = os_1.default.freemem();
2163
+ info.totalmem = os_1.default.totalmem();
2164
+ info.memUsage = (os_1.default.totalmem() - os_1.default.freemem()) / os_1.default.totalmem();
2165
+ os_utils_1.default.cpuUsage((p) => (info.cpuUsage = p));
2166
+ }
2167
+ function setLinuxSystemInfo() {
2168
+ try {
2169
+ // read memory data based on /proc/meminfo
2170
+ const data = fs_1.default.readFileSync("/proc/meminfo", { encoding: "utf-8" });
2171
+ const list = data.split("\n");
2172
+ const infoTable = {};
2173
+ list.forEach((line) => {
2174
+ const kv = line.split(":");
2175
+ if (kv.length === 2) {
2176
+ const k = kv[0].replace(/ /gim, "").replace(/\t/gim, "").trim().toLowerCase();
2177
+ let v = kv[1].replace(/ /gim, "").replace(/\t/gim, "").trim().toLowerCase();
2178
+ v = v.replace(/kb/gim, "").replace(/mb/gim, "").replace(/gb/gim, "");
2179
+ let vNumber = parseInt(v);
2180
+ if (isNaN(vNumber))
2181
+ vNumber = 0;
2182
+ infoTable[k] = vNumber;
2183
+ }
2184
+ });
2185
+ const memAvailable = infoTable["memavailable"] ?? infoTable["memfree"];
2186
+ const memTotal = infoTable["memtotal"];
2187
+ info.freemem = memAvailable * 1024;
2188
+ info.totalmem = memTotal * 1024;
2189
+ info.memUsage = (info.totalmem - info.freemem) / info.totalmem;
2190
+ os_utils_1.default.cpuUsage((p) => (info.cpuUsage = p));
2191
+ }
2192
+ catch (error) {
2193
+ // If the reading is wrong, the default general reading method is automatically used
2194
+ otherSystemInfo();
2195
+ }
2196
+ }
2197
+ function systemInfo() {
2198
+ return info;
2199
+ }
2200
+
2201
+
2202
+ /***/ },
2203
+
2204
+ /***/ "../common/src/system_storage.ts"
2205
+ (__unused_webpack_module, exports, __webpack_require__) {
2206
+
2207
+
2208
+ var __importDefault = (this && this.__importDefault) || function (mod) {
2209
+ return (mod && mod.__esModule) ? mod : { "default": mod };
2210
+ };
2211
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
2212
+ const crypto_1 = __webpack_require__("crypto");
2213
+ const path_1 = __importDefault(__webpack_require__("path"));
2214
+ const fs_extra_1 = __importDefault(__webpack_require__("fs-extra"));
2215
+ class StorageSubsystem {
2216
+ static DATA_PATH = path_1.default.resolve(process.cwd(), "data");
2217
+ static INDEX_PATH = path_1.default.join(StorageSubsystem.DATA_PATH, "Index");
2218
+ resolvePath(name) {
2219
+ if (typeof name !== "string" ||
2220
+ !name ||
2221
+ /[\0:]/.test(name) ||
2222
+ path_1.default.isAbsolute(name) ||
2223
+ path_1.default.win32.isAbsolute(name)) {
2224
+ throw new Error(`Invalid storage path: ${name}`);
2225
+ }
2226
+ const target = path_1.default.resolve(StorageSubsystem.DATA_PATH, name);
2227
+ const relative = path_1.default.relative(StorageSubsystem.DATA_PATH, target);
2228
+ if (!relative ||
2229
+ relative === ".." ||
2230
+ relative.startsWith(`..${path_1.default.sep}`) ||
2231
+ path_1.default.isAbsolute(relative)) {
2232
+ throw new Error(`Invalid storage path: ${name}`);
2233
+ }
2234
+ const realRelative = path_1.default.relative(this.realPath(StorageSubsystem.DATA_PATH), this.realPath(target));
2235
+ if (realRelative === ".." ||
2236
+ realRelative.startsWith(`..${path_1.default.sep}`) ||
2237
+ path_1.default.isAbsolute(realRelative)) {
2238
+ throw new Error(`Invalid storage path: ${name}`);
2239
+ }
2240
+ return target;
2241
+ }
2242
+ realPath(target) {
2243
+ if (fs_extra_1.default.existsSync(target))
2244
+ return fs_extra_1.default.realpathSync(target);
2245
+ const parent = path_1.default.dirname(target);
2246
+ return parent === target ? target : path_1.default.join(this.realPath(parent), path_1.default.basename(target));
2247
+ }
2248
+ entityPath(category, uuid) {
2249
+ if (typeof uuid !== "string" || !uuid || /[\\/\0:]/.test(uuid) || uuid.includes("..")) {
2250
+ throw new Error(`UUID ${uuid} does not conform to specification`);
2251
+ }
2252
+ this.resolvePath(category);
2253
+ return path_1.default.join(category, `${uuid}.json`);
2254
+ }
2255
+ writeFile(name, data) {
2256
+ const targetPath = this.resolvePath(name);
2257
+ fs_extra_1.default.ensureDirSync(path_1.default.dirname(targetPath));
2258
+ const temporaryPath = `${targetPath}.${(0, crypto_1.randomUUID)()}.tmp`;
2259
+ const mode = fs_extra_1.default.existsSync(targetPath) ? fs_extra_1.default.statSync(targetPath).mode : 0o600;
2260
+ try {
2261
+ fs_extra_1.default.writeFileSync(temporaryPath, data, { encoding: "utf8", flag: "wx", mode });
2262
+ fs_extra_1.default.renameSync(temporaryPath, targetPath);
2263
+ }
2264
+ finally {
2265
+ if (fs_extra_1.default.existsSync(temporaryPath))
2266
+ fs_extra_1.default.unlinkSync(temporaryPath);
2267
+ }
2268
+ }
2269
+ readFile(name) {
2270
+ return fs_extra_1.default.readFileSync(this.resolvePath(name), "utf8");
2271
+ }
2272
+ readDir(dirName) {
2273
+ const targetPath = this.resolvePath(dirName);
2274
+ if (!fs_extra_1.default.existsSync(targetPath))
2275
+ return [];
2276
+ return fs_extra_1.default.readdirSync(targetPath).map((name) => path_1.default.join(dirName, name));
2277
+ }
2278
+ deleteFile(name) {
2279
+ fs_extra_1.default.removeSync(this.resolvePath(name));
2280
+ }
2281
+ fileExists(name) {
2282
+ return fs_extra_1.default.existsSync(this.resolvePath(name));
2283
+ }
2284
+ store(category, uuid, object) {
2285
+ this.writeFile(this.entityPath(category, uuid), JSON.stringify(object, null, 4));
2286
+ }
2287
+ // Restore declared fields while keeping defaults for fields absent in older data.
2288
+ defineAttr(target, object) {
2289
+ for (const key of Object.keys(target)) {
2290
+ if (!Object.prototype.hasOwnProperty.call(object, key) || object[key] === undefined)
2291
+ continue;
2292
+ const value = object[key];
2293
+ if (value !== null &&
2294
+ typeof value === "object" &&
2295
+ !Array.isArray(value) &&
2296
+ target[key] !== null &&
2297
+ typeof target[key] === "object" &&
2298
+ !Array.isArray(target[key])) {
2299
+ this.defineAttr(target[key], value);
2300
+ }
2301
+ else {
2302
+ target[key] = value;
2303
+ }
2304
+ }
2305
+ return target;
2306
+ }
2307
+ load(category, classz, uuid) {
2308
+ const file = this.entityPath(category, uuid);
2309
+ if (!this.fileExists(file))
2310
+ return null;
2311
+ const object = JSON.parse(this.readFile(file));
2312
+ if (!object || typeof object !== "object" || Array.isArray(object)) {
2313
+ throw new Error(`Invalid stored entity: ${file}`);
2314
+ }
2315
+ return this.defineAttr(new classz(), object);
2316
+ }
2317
+ list(category) {
2318
+ const directory = this.resolvePath(category);
2319
+ if (!fs_extra_1.default.existsSync(directory))
2320
+ return [];
2321
+ return fs_extra_1.default
2322
+ .readdirSync(directory, { withFileTypes: true })
2323
+ .filter((entry) => entry.isFile() && entry.name.endsWith(".json"))
2324
+ .map((entry) => entry.name.slice(0, -5));
2325
+ }
2326
+ delete(category, uuid) {
2327
+ this.deleteFile(this.entityPath(category, uuid));
2328
+ }
2329
+ }
2330
+ exports["default"] = StorageSubsystem;
2331
+
2332
+
2333
+ /***/ },
2334
+
2335
+ /***/ "../common/src/typecheck.ts"
2336
+ (__unused_webpack_module, exports) {
2337
+
2338
+
2339
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
2340
+ exports.configureEntityParams = configureEntityParams;
2341
+ exports.toText = toText;
2342
+ exports.toNumber = toNumber;
2343
+ exports.toBoolean = toBoolean;
2344
+ exports.isEmpty = isEmpty;
2345
+ exports.supposeValue = supposeValue;
2346
+ function configureEntityParams(self, args, key, typeFn) {
2347
+ const selfDefaultValue = self[key] ?? null;
2348
+ const v = args[key] != null ? args[key] : selfDefaultValue;
2349
+ if (typeFn === Number) {
2350
+ if (v === "" || v == null) {
2351
+ self[key] = null;
2352
+ }
2353
+ else {
2354
+ if (isNaN(Number(v)))
2355
+ throw new Error(`ConfigureEntityParams Error: Expected type to be Number, but got ${typeof v}`);
2356
+ self[key] = Number(v);
2357
+ }
2358
+ return;
2359
+ }
2360
+ if (typeFn === String) {
2361
+ if (v == null) {
2362
+ self[key] = null;
2363
+ }
2364
+ else {
2365
+ self[key] = String(v);
2366
+ }
2367
+ return;
2368
+ }
2369
+ if (typeFn === Boolean) {
2370
+ if (v == null) {
2371
+ self[key] = false;
2372
+ }
2373
+ else {
2374
+ self[key] = Boolean(v);
2375
+ }
2376
+ return;
2377
+ }
2378
+ if (typeFn) {
2379
+ self[key] = typeFn(v);
2380
+ }
2381
+ else {
2382
+ self[key] = v;
2383
+ }
2384
+ }
2385
+ function toText(v) {
2386
+ if (isEmpty(v))
2387
+ return null;
2388
+ return String(v);
2389
+ }
2390
+ function toNumber(v) {
2391
+ if (isEmpty(v))
2392
+ return null;
2393
+ if (isNaN(Number(v)))
2394
+ return null;
2395
+ return Number(v);
2396
+ }
2397
+ function toBoolean(v) {
2398
+ if (isEmpty(v))
2399
+ return null;
2400
+ return Boolean(v);
2401
+ }
2402
+ function isEmpty(v) {
2403
+ return v === null || v === undefined;
2404
+ }
2405
+ function supposeValue(v, def) {
2406
+ if (isEmpty(v))
2407
+ return def;
2408
+ return v;
2409
+ }
2410
+
2411
+
2412
+ /***/ },
2413
+
2414
+ /***/ "./plugins/file/src/backend/file_router.ts"
2415
+ (__unused_webpack_module, exports, __webpack_require__) {
2416
+
2417
+
2418
+ var __importDefault = (this && this.__importDefault) || function (mod) {
2419
+ return (mod && mod.__esModule) ? mod : { "default": mod };
2420
+ };
2421
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
2422
+ exports.registerFileEvents = registerFileEvents;
2423
+ const os_1 = __importDefault(__webpack_require__("os"));
2424
+ const file_service_1 = __webpack_require__("./plugins/file/src/backend/file_service.ts");
2425
+ const runtime_1 = __webpack_require__("./plugins/file/src/backend/runtime.ts");
2426
+ const upload_manager_1 = __importDefault(__webpack_require__("./plugins/file/src/backend/upload_manager.ts"));
2427
+ const url_1 = __webpack_require__("./plugins/file/src/backend/url.ts");
2428
+ /**
2429
+ * Every `file/*` protocol event, plus the middleware that gates them.
2430
+ *
2431
+ * Registered from `apply()`, so all of it — the middleware included — leaves with
2432
+ * the plugin. The middleware runs after `plugins/auth`'s, which loads first.
2433
+ */
2434
+ function registerFileEvents() {
2435
+ // Some routers operate router authentication middleware
2436
+ (0, runtime_1.protocol)().use((event, ctx, data, next) => {
2437
+ if (event.startsWith("file/")) {
2438
+ const instanceUuid = data.instanceUuid;
2439
+ const instance = (0, runtime_1.instances)().subsystem.getInstance(instanceUuid);
2440
+ if (!instance) {
2441
+ return (0, runtime_1.protocol)().error(ctx, event, {
2442
+ instanceUuid: instanceUuid,
2443
+ err: (0, runtime_1.$t)("TXT_CODE_file_router.instanceNotExist", { instanceUuid: instanceUuid })
2444
+ });
2445
+ }
2446
+ if ([(0, runtime_1.instances)().Instance.STATUS_BUSY, (0, runtime_1.instances)().Instance.STATUS_STARTING].includes(instance.status()) &&
2447
+ !["file/list", "file/status"].includes(event)) {
2448
+ return (0, runtime_1.protocol)().error(ctx, event, {
2449
+ instanceUuid: instanceUuid,
2450
+ err: (0, runtime_1.$t)("TXT_CODE_bbedcf29")
2451
+ });
2452
+ }
2453
+ }
2454
+ next();
2455
+ });
2456
+ // List the files in the specified instance working directory
2457
+ (0, runtime_1.protocol)().on("file/list", async (ctx, data) => {
2458
+ try {
2459
+ const fileManager = (0, file_service_1.getFileManager)(data.instanceUuid);
2460
+ const { page, pageSize, target, fileName } = data;
2461
+ fileManager.cd(target);
2462
+ const overview = await fileManager.list(page, pageSize, fileName);
2463
+ (0, runtime_1.protocol)().response(ctx, overview);
2464
+ }
2465
+ catch (error) {
2466
+ (0, runtime_1.protocol)().responseError(ctx, error);
2467
+ }
2468
+ });
2469
+ // Preview archive contents without extracting the archive
2470
+ (0, runtime_1.protocol)().on("file/preview", async (ctx, data) => {
2471
+ const maxFileTask = (0, runtime_1.settings)().config.maxFileTask;
2472
+ const instance = (0, runtime_1.instances)().subsystem.getInstance(data.instanceUuid);
2473
+ let taskStarted = false;
2474
+ try {
2475
+ if (!instance)
2476
+ throw new Error((0, runtime_1.$t)("TXT_CODE_3bfb9e04"));
2477
+ if (instance.info.fileLock >= maxFileTask) {
2478
+ throw new Error((0, runtime_1.$t)("TXT_CODE_file_router.unzipLimit", {
2479
+ maxFileTask,
2480
+ fileLock: instance.info.fileLock
2481
+ }));
2482
+ }
2483
+ const fileManager = (0, file_service_1.getFileManager)(data.instanceUuid);
2484
+ instance.info.fileLock++;
2485
+ runtime_1.fileTasks.count++;
2486
+ taskStarted = true;
2487
+ const entries = await fileManager.previewArchive(data.target, data.code);
2488
+ (0, runtime_1.protocol)().response(ctx, { items: entries, total: entries.length });
2489
+ }
2490
+ catch (error) {
2491
+ (0, runtime_1.protocol)().responseError(ctx, error);
2492
+ }
2493
+ finally {
2494
+ if (instance && taskStarted) {
2495
+ instance.info.fileLock = Math.max(0, instance.info.fileLock - 1);
2496
+ runtime_1.fileTasks.count = Math.max(0, runtime_1.fileTasks.count - 1);
2497
+ }
2498
+ }
2499
+ });
2500
+ // File chmod (only Linux)
2501
+ (0, runtime_1.protocol)().on("file/chmod", async (ctx, data) => {
2502
+ try {
2503
+ const fileManager = (0, file_service_1.getFileManager)(data.instanceUuid);
2504
+ const { chmod, target, deep } = data;
2505
+ await fileManager.chmod(target, chmod, deep);
2506
+ (0, runtime_1.protocol)().response(ctx, true);
2507
+ }
2508
+ catch (error) {
2509
+ (0, runtime_1.protocol)().responseError(ctx, error);
2510
+ }
2511
+ });
2512
+ (0, runtime_1.protocol)().on("file/chmod_batch", async (ctx, data) => {
2513
+ try {
2514
+ const fileManager = (0, file_service_1.getFileManager)(data.instanceUuid);
2515
+ const { chmod, targets, deep: rawDeep } = data;
2516
+ const deep = Boolean(rawDeep);
2517
+ const results = [];
2518
+ let success = 0;
2519
+ let failed = 0;
2520
+ for (const target of targets || []) {
2521
+ const currentTarget = String(target);
2522
+ try {
2523
+ await fileManager.chmod(currentTarget, chmod, deep);
2524
+ success += 1;
2525
+ results.push({
2526
+ target: currentTarget,
2527
+ success: true
2528
+ });
2529
+ }
2530
+ catch (error) {
2531
+ failed += 1;
2532
+ results.push({
2533
+ target: currentTarget,
2534
+ success: false,
2535
+ error: error?.message || String(error)
2536
+ });
2537
+ }
2538
+ }
2539
+ (0, runtime_1.protocol)().response(ctx, {
2540
+ success,
2541
+ failed,
2542
+ total: results.length,
2543
+ results
2544
+ });
2545
+ }
2546
+ catch (error) {
2547
+ (0, runtime_1.protocol)().responseError(ctx, error);
2548
+ }
2549
+ });
2550
+ // Query the status of the file management system
2551
+ (0, runtime_1.protocol)().on("file/status", async (ctx, data) => {
2552
+ try {
2553
+ const instance = (0, runtime_1.instances)().subsystem.getInstance(data.instanceUuid);
2554
+ if (!instance)
2555
+ throw new Error((0, runtime_1.$t)("TXT_CODE_3bfb9e04"));
2556
+ const downloads = (0, runtime_1.transfer)().downloads;
2557
+ const downloadTasks = [];
2558
+ if (downloads.task) {
2559
+ downloadTasks.push({
2560
+ path: downloads.task.path,
2561
+ total: downloads.task.total,
2562
+ current: downloads.task.current,
2563
+ status: downloads.task.status,
2564
+ error: downloads.task.error
2565
+ });
2566
+ }
2567
+ (0, runtime_1.protocol)().response(ctx, {
2568
+ instanceFileTask: instance.info.fileLock ?? 0,
2569
+ globalFileTask: runtime_1.fileTasks.count ?? 0,
2570
+ downloadFileFromURLTask: downloads.downloadingCount,
2571
+ downloadTasks,
2572
+ platform: os_1.default.platform(),
2573
+ isGlobalInstance: data.instanceUuid === (0, runtime_1.instances)().subsystem.GLOBAL_INSTANCE_UUID,
2574
+ disks: (0, file_service_1.getWindowsDisks)()
2575
+ });
2576
+ }
2577
+ catch (error) {
2578
+ (0, runtime_1.protocol)().responseError(ctx, error);
2579
+ }
2580
+ });
2581
+ // Create a new file
2582
+ (0, runtime_1.protocol)().on("file/create-text", async (ctx, data) => {
2583
+ try {
2584
+ if (typeof data.target !== "string" ||
2585
+ !data.target ||
2586
+ data.target.length > 1024 ||
2587
+ typeof data.text !== "string" ||
2588
+ Buffer.byteLength(data.text, "utf8") > 64 * 1024 ||
2589
+ /[\x00-\x08\x0b\x0c\x0e-\x1f\ufffd]/.test(data.text))
2590
+ throw new Error("Invalid text file request");
2591
+ const result = await (0, file_service_1.getFileManager)(data.instanceUuid).createTextFile(data.target, data.text);
2592
+ (0, runtime_1.protocol)().response(ctx, result);
2593
+ }
2594
+ catch (error) {
2595
+ (0, runtime_1.protocol)().responseError(ctx, error);
2596
+ }
2597
+ });
2598
+ (0, runtime_1.protocol)().on("file/remove-file", async (ctx, data) => {
2599
+ try {
2600
+ if (typeof data.target !== "string" || !data.target || data.target.length > 1024)
2601
+ throw new Error("Invalid file request");
2602
+ const result = await (0, file_service_1.getFileManager)(data.instanceUuid).removeFile(data.target);
2603
+ (0, runtime_1.protocol)().response(ctx, result);
2604
+ }
2605
+ catch (error) {
2606
+ (0, runtime_1.protocol)().responseError(ctx, error);
2607
+ }
2608
+ });
2609
+ (0, runtime_1.protocol)().on("file/touch", (ctx, data) => {
2610
+ try {
2611
+ const target = data.target;
2612
+ const fileManager = (0, file_service_1.getFileManager)(data.instanceUuid);
2613
+ fileManager.newFile(target);
2614
+ (0, runtime_1.protocol)().response(ctx, true);
2615
+ }
2616
+ catch (error) {
2617
+ (0, runtime_1.protocol)().responseError(ctx, error);
2618
+ }
2619
+ });
2620
+ // Create a directory
2621
+ (0, runtime_1.protocol)().on("file/mkdir", (ctx, data) => {
2622
+ try {
2623
+ const target = data.target;
2624
+ const fileManager = (0, file_service_1.getFileManager)(data.instanceUuid);
2625
+ fileManager.mkdir(target);
2626
+ (0, runtime_1.protocol)().response(ctx, true);
2627
+ }
2628
+ catch (error) {
2629
+ (0, runtime_1.protocol)().responseError(ctx, error);
2630
+ }
2631
+ });
2632
+ // download a file from url
2633
+ (0, runtime_1.protocol)().on("file/download_from_url", async (ctx, data) => {
2634
+ try {
2635
+ const url = data.url;
2636
+ const fileName = data.fileName;
2637
+ if (!(0, url_1.checkSafeUrl)(url) || (data.fallbackUrl && !(0, url_1.checkSafeUrl)(data.fallbackUrl))) {
2638
+ (0, runtime_1.protocol)().responseError(ctx, (0, runtime_1.$t)("TXT_CODE_3fe1b194"), {
2639
+ disablePrint: true
2640
+ });
2641
+ return;
2642
+ }
2643
+ const fileManager = (0, file_service_1.getFileManager)(data.instanceUuid);
2644
+ if (!fileManager.checkPath(fileName))
2645
+ throw new Error("Access denied: Invalid destination");
2646
+ const targetPath = fileManager.toAbsolutePath(fileName);
2647
+ // Start download in background
2648
+ const fallbackUrl = data.fallbackUrl;
2649
+ const maxDownloadFromUrlFileCount = (0, runtime_1.settings)().config.maxDownloadFromUrlFileCount;
2650
+ if ((data.ifIdle === true && (0, runtime_1.transfer)().downloads.downloadingCount > 0) ||
2651
+ (maxDownloadFromUrlFileCount > 0 &&
2652
+ (0, runtime_1.transfer)().downloads.downloadingCount >= maxDownloadFromUrlFileCount)) {
2653
+ (0, runtime_1.protocol)().responseError(ctx, (0, runtime_1.$t)("TXT_CODE_821a742e", {
2654
+ count: data.ifIdle === true ? 1 : maxDownloadFromUrlFileCount
2655
+ }), {
2656
+ disablePrint: true
2657
+ });
2658
+ return;
2659
+ }
2660
+ (0, runtime_1.transfer)()
2661
+ .downloads.downloadFromUrl(url, targetPath, fallbackUrl)
2662
+ .catch((err) => {
2663
+ (0, runtime_1.logger)().error(`Download failed: ${url} -> ${targetPath}`, err);
2664
+ });
2665
+ (0, runtime_1.protocol)().response(ctx, {});
2666
+ }
2667
+ catch (error) {
2668
+ (0, runtime_1.protocol)().responseError(ctx, error);
2669
+ }
2670
+ });
2671
+ // stop download from url
2672
+ (0, runtime_1.protocol)().on("file/download_stop", (ctx, data) => {
2673
+ try {
2674
+ const fileManager = (0, file_service_1.getFileManager)(data.instanceUuid);
2675
+ if (!fileManager.checkPath(data.fileName))
2676
+ throw new Error("Access denied: Invalid destination");
2677
+ const targetPath = fileManager.toAbsolutePath(data.fileName);
2678
+ const result = (0, runtime_1.transfer)().downloads.stop(targetPath);
2679
+ (0, runtime_1.protocol)().response(ctx, result);
2680
+ }
2681
+ catch (error) {
2682
+ (0, runtime_1.protocol)().responseError(ctx, error);
2683
+ }
2684
+ });
2685
+ // copy the file
2686
+ (0, runtime_1.protocol)().on("file/copy", async (ctx, data) => {
2687
+ try {
2688
+ // [["a.txt","b.txt"],["cxz","zzz"]]
2689
+ const targets = data.targets;
2690
+ const fileManager = (0, file_service_1.getFileManager)(data.instanceUuid);
2691
+ for (const target of targets) {
2692
+ await fileManager.copy(target[0], target[1]);
2693
+ }
2694
+ (0, runtime_1.protocol)().response(ctx, true);
2695
+ }
2696
+ catch (error) {
2697
+ (0, runtime_1.protocol)().responseError(ctx, error);
2698
+ }
2699
+ });
2700
+ // move the file
2701
+ (0, runtime_1.protocol)().on("file/move", async (ctx, data) => {
2702
+ try {
2703
+ // [["a.txt","b.txt"],["cxz","zzz"]]
2704
+ const targets = data.targets;
2705
+ const fileManager = (0, file_service_1.getFileManager)(data.instanceUuid);
2706
+ for (const target of targets) {
2707
+ await fileManager.move(target[0], target[1]);
2708
+ }
2709
+ (0, runtime_1.protocol)().response(ctx, true);
2710
+ }
2711
+ catch (error) {
2712
+ (0, runtime_1.protocol)().responseError(ctx, error);
2713
+ }
2714
+ });
2715
+ // Delete Files
2716
+ (0, runtime_1.protocol)().on("file/delete", async (ctx, data) => {
2717
+ try {
2718
+ const targets = data.targets;
2719
+ const fileManager = (0, file_service_1.getFileManager)(data.instanceUuid);
2720
+ for (const target of targets) {
2721
+ const path = fileManager.toAbsolutePath(target);
2722
+ const uploadTask = upload_manager_1.default.getByPath(path);
2723
+ if (uploadTask != undefined) {
2724
+ upload_manager_1.default.delete(uploadTask.id);
2725
+ await uploadTask.writer.stop();
2726
+ }
2727
+ else {
2728
+ // async delete
2729
+ await fileManager.delete(target);
2730
+ }
2731
+ }
2732
+ (0, runtime_1.protocol)().response(ctx, true);
2733
+ }
2734
+ catch (error) {
2735
+ (0, runtime_1.protocol)().responseError(ctx, error);
2736
+ }
2737
+ });
2738
+ // edit file
2739
+ (0, runtime_1.protocol)().on("file/edit", async (ctx, data) => {
2740
+ try {
2741
+ const target = data.target;
2742
+ const text = data.text;
2743
+ const fileManager = (0, file_service_1.getFileManager)(data.instanceUuid);
2744
+ const result = await fileManager.edit(target, text);
2745
+ (0, runtime_1.protocol)().response(ctx, result ? result : true);
2746
+ }
2747
+ catch (error) {
2748
+ (0, runtime_1.protocol)().responseError(ctx, error);
2749
+ }
2750
+ });
2751
+ // compress/decompress the file
2752
+ (0, runtime_1.protocol)().on("file/compress", async (ctx, data) => {
2753
+ const maxFileTask = (0, runtime_1.settings)().config.maxFileTask;
2754
+ try {
2755
+ const source = data.source;
2756
+ const targets = data.targets;
2757
+ const type = data.type;
2758
+ const code = data.code;
2759
+ const fileManager = (0, file_service_1.getFileManager)(data.instanceUuid);
2760
+ const instance = (0, runtime_1.instances)().subsystem.getInstance(data.instanceUuid);
2761
+ if (!instance)
2762
+ throw new Error((0, runtime_1.$t)("TXT_CODE_3bfb9e04"));
2763
+ if (instance.info.fileLock >= maxFileTask) {
2764
+ throw new Error((0, runtime_1.$t)("TXT_CODE_file_router.unzipLimit", {
2765
+ maxFileTask: maxFileTask,
2766
+ fileLock: instance.info.fileLock
2767
+ }));
2768
+ }
2769
+ // Statistics of the number of tasks in a single instance file and the number of tasks in the entire daemon process
2770
+ function fileTaskStart() {
2771
+ if (instance) {
2772
+ instance.info.fileLock++;
2773
+ runtime_1.fileTasks.count++;
2774
+ }
2775
+ }
2776
+ function fileTaskEnd() {
2777
+ if (instance) {
2778
+ instance.info.fileLock--;
2779
+ runtime_1.fileTasks.count--;
2780
+ }
2781
+ }
2782
+ // start decompressing or compressing the file
2783
+ fileTaskStart();
2784
+ try {
2785
+ if (type === 1) {
2786
+ await fileManager.zip(source, targets, code);
2787
+ }
2788
+ else {
2789
+ await fileManager.unzip(source, targets, code);
2790
+ }
2791
+ (0, runtime_1.protocol)().response(ctx, true);
2792
+ }
2793
+ catch (error) {
2794
+ throw error;
2795
+ }
2796
+ finally {
2797
+ fileTaskEnd();
2798
+ }
2799
+ }
2800
+ catch (error) {
2801
+ (0, runtime_1.protocol)().responseError(ctx, error);
2802
+ }
2803
+ });
2804
+ }
2805
+
2806
+
2807
+ /***/ },
2808
+
2809
+ /***/ "./plugins/file/src/backend/file_service.ts"
2810
+ (__unused_webpack_module, exports, __webpack_require__) {
2811
+
2812
+
2813
+ var __importDefault = (this && this.__importDefault) || function (mod) {
2814
+ return (mod && mod.__esModule) ? mod : { "default": mod };
2815
+ };
2816
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
2817
+ exports.getFileManager = getFileManager;
2818
+ exports.getWindowsDisks = getWindowsDisks;
2819
+ const fs_extra_1 = __importDefault(__webpack_require__("fs-extra"));
2820
+ const system_file_1 = __importDefault(__webpack_require__("./plugins/file/src/backend/system_file.ts"));
2821
+ const runtime_1 = __webpack_require__("./plugins/file/src/backend/runtime.ts");
2822
+ const os_1 = __importDefault(__webpack_require__("os"));
2823
+ function getFileManager(instanceUuid) {
2824
+ // Initialize a file manager for the instance, and assign codes, restrictions, etc.
2825
+ const instance = (0, runtime_1.instances)().subsystem.getInstance(instanceUuid);
2826
+ if (!instance)
2827
+ throw new Error((0, runtime_1.$t)("TXT_CODE_file_router_service.instanceNotExit", { uuid: instanceUuid }));
2828
+ const fileCode = instance.config?.fileCode;
2829
+ return new system_file_1.default(instance.absoluteCwdPath(), fileCode);
2830
+ }
2831
+ let cacheDisks = [];
2832
+ function getWindowsDisks() {
2833
+ if (os_1.default.platform() !== "win32")
2834
+ return [];
2835
+ if (cacheDisks.length > 0)
2836
+ return cacheDisks;
2837
+ const res = [];
2838
+ // A - Z
2839
+ for (let i = 65; i <= 90; i++) {
2840
+ const letter = String.fromCharCode(i);
2841
+ try {
2842
+ if (fs_extra_1.default.existsSync(`${letter}:\\`))
2843
+ res.push(letter);
2844
+ }
2845
+ catch (error) {
2846
+ // ignore error
2847
+ }
2848
+ }
2849
+ cacheDisks = res;
2850
+ return res;
2851
+ }
2852
+
2853
+
2854
+ /***/ },
2855
+
2856
+ /***/ "./plugins/file/src/backend/file_writer.ts"
2857
+ (__unused_webpack_module, exports, __webpack_require__) {
2858
+
2859
+
2860
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
2861
+ if (k2 === undefined) k2 = k;
2862
+ var desc = Object.getOwnPropertyDescriptor(m, k);
2863
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
2864
+ desc = { enumerable: true, get: function() { return m[k]; } };
2865
+ }
2866
+ Object.defineProperty(o, k2, desc);
2867
+ }) : (function(o, m, k, k2) {
2868
+ if (k2 === undefined) k2 = k;
2869
+ o[k2] = m[k];
2870
+ }));
2871
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
2872
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
2873
+ }) : function(o, v) {
2874
+ o["default"] = v;
2875
+ });
2876
+ var __importStar = (this && this.__importStar) || (function () {
2877
+ var ownKeys = function(o) {
2878
+ ownKeys = Object.getOwnPropertyNames || function (o) {
2879
+ var ar = [];
2880
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
2881
+ return ar;
2882
+ };
2883
+ return ownKeys(o);
2884
+ };
2885
+ return function (mod) {
2886
+ if (mod && mod.__esModule) return mod;
2887
+ var result = {};
2888
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
2889
+ __setModuleDefault(result, mod);
2890
+ return result;
2891
+ };
2892
+ })();
2893
+ var __importDefault = (this && this.__importDefault) || function (mod) {
2894
+ return (mod && mod.__esModule) ? mod : { "default": mod };
2895
+ };
2896
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
2897
+ const fs_extra_1 = __importDefault(__webpack_require__("fs-extra"));
2898
+ const path_1 = __importDefault(__webpack_require__("path"));
2899
+ const lockfile = __importStar(__webpack_require__("proper-lockfile"));
2900
+ const system_file_1 = __importDefault(__webpack_require__("./plugins/file/src/backend/system_file.ts"));
2901
+ const runtime_1 = __webpack_require__("./plugins/file/src/backend/runtime.ts");
2902
+ const upload_manager_1 = __importDefault(__webpack_require__("./plugins/file/src/backend/upload_manager.ts"));
2903
+ class FileWriter {
2904
+ cwd;
2905
+ filename;
2906
+ size;
2907
+ unzip;
2908
+ zipCode;
2909
+ path;
2910
+ id;
2911
+ releaseLock;
2912
+ fd = null;
2913
+ operations = Promise.resolve();
2914
+ completed = false;
2915
+ received = [];
2916
+ lastUpdate = Date.now();
2917
+ onFinish;
2918
+ get cancellable() {
2919
+ return !this.completed;
2920
+ }
2921
+ constructor(cwd, filename, size, unzip, zipCode, filePath) {
2922
+ this.cwd = cwd;
2923
+ this.filename = filename;
2924
+ this.size = size;
2925
+ this.unzip = unzip;
2926
+ this.zipCode = zipCode;
2927
+ if (!system_file_1.default.checkFileName(path_1.default.basename(this.filename)))
2928
+ throw new Error("Access denied: Malformed file name");
2929
+ if (!Number.isSafeInteger(size) || size < 0)
2930
+ throw new Error("Invalid file size");
2931
+ this.path = filePath;
2932
+ }
2933
+ static async getPath(cwd, dir, filename, overwrite) {
2934
+ const fileManager = new system_file_1.default(cwd);
2935
+ const ext = path_1.default.extname(filename);
2936
+ const basename = path_1.default.basename(filename, ext);
2937
+ let tempFileSaveName = basename + ext;
2938
+ let counter = 1;
2939
+ const checkFile = async (name) => {
2940
+ const absolutePath = fileManager.toAbsolutePath(path_1.default.normalize(path_1.default.join(dir, name)));
2941
+ const isLock = await lockfile
2942
+ .check(absolutePath)
2943
+ .then((isLock) => isLock)
2944
+ .catch(() => false);
2945
+ const isAccess = await fs_extra_1.default
2946
+ .access(absolutePath)
2947
+ .then(() => true)
2948
+ .catch(() => false);
2949
+ return isAccess && !isLock && !overwrite;
2950
+ };
2951
+ while (await checkFile(tempFileSaveName)) {
2952
+ if (counter == 1) {
2953
+ tempFileSaveName = `${basename}-copy${ext}`;
2954
+ }
2955
+ else {
2956
+ tempFileSaveName = `${basename}-copy-${counter}${ext}`;
2957
+ }
2958
+ counter++;
2959
+ if (counter > 100) {
2960
+ throw new Error("Access denied: File name already exists!");
2961
+ }
2962
+ }
2963
+ const fileSaveRelativePath = path_1.default.normalize(path_1.default.join(dir, tempFileSaveName));
2964
+ if (!fileManager.checkPath(fileSaveRelativePath))
2965
+ throw new Error("Access denied: Invalid destination");
2966
+ return fileManager.toAbsolutePath(fileSaveRelativePath);
2967
+ }
2968
+ async init() {
2969
+ if (this.fd != null)
2970
+ return;
2971
+ try {
2972
+ // Lock before opening with w+: a competing upload must not truncate the file.
2973
+ this.releaseLock = await lockfile.lock(this.path, { realpath: false });
2974
+ this.fd = await fs_extra_1.default.open(this.path, "w+");
2975
+ await fs_extra_1.default.ftruncate(this.fd, this.size);
2976
+ }
2977
+ catch (e) {
2978
+ try {
2979
+ await this.close();
2980
+ }
2981
+ finally {
2982
+ await this.unlock();
2983
+ }
2984
+ throw e;
2985
+ }
2986
+ }
2987
+ enqueue(operation) {
2988
+ const pending = this.operations.then(operation);
2989
+ this.operations = pending.catch(() => { });
2990
+ return pending;
2991
+ }
2992
+ write(offset, chunk) {
2993
+ return this.enqueue(async () => {
2994
+ if (!Number.isSafeInteger(offset) || offset < 0 || offset + chunk.length > this.size) {
2995
+ throw new Error("Write exceeds file size limit");
2996
+ }
2997
+ if (this.fd === null || this.completed)
2998
+ throw new Error("File is not opened");
2999
+ this.lastUpdate = Date.now();
3000
+ let written = 0;
3001
+ while (written < chunk.length) {
3002
+ const { bytesWritten } = await fs_extra_1.default.write(this.fd, chunk, written, chunk.length - written, offset + written);
3003
+ if (bytesWritten === 0)
3004
+ throw new Error("Unable to write upload chunk");
3005
+ written += bytesWritten;
3006
+ }
3007
+ this.addWrittenRange(offset, offset + chunk.length);
3008
+ if (this.isFullyCovered())
3009
+ await this.finish(false);
3010
+ });
3011
+ }
3012
+ done() {
3013
+ return this.enqueue(() => this.finish(false));
3014
+ }
3015
+ stop() {
3016
+ return this.enqueue(() => this.finish(true));
3017
+ }
3018
+ async close() {
3019
+ const fd = this.fd;
3020
+ this.fd = null;
3021
+ if (fd != null)
3022
+ await fs_extra_1.default.close(fd);
3023
+ }
3024
+ async unlock() {
3025
+ const releaseLock = this.releaseLock;
3026
+ this.releaseLock = undefined;
3027
+ await releaseLock?.();
3028
+ }
3029
+ async finish(cancelled) {
3030
+ if (this.completed)
3031
+ return;
3032
+ this.completed = true;
3033
+ let failure;
3034
+ try {
3035
+ await this.close();
3036
+ if (cancelled) {
3037
+ await fs_extra_1.default.remove(this.path);
3038
+ (0, runtime_1.logger)().info("Browser Upload Task Stopped:", this.path);
3039
+ }
3040
+ else {
3041
+ (0, runtime_1.logger)().info("Browser Uploaded File:", this.path);
3042
+ if (this.unzip) {
3043
+ const instanceFiles = new system_file_1.default(this.cwd);
3044
+ await instanceFiles.unzip(this.path, ".", this.zipCode);
3045
+ (0, runtime_1.logger)().info("File unzipped:", this.path);
3046
+ }
3047
+ }
3048
+ }
3049
+ catch (error) {
3050
+ failure = error;
3051
+ throw error;
3052
+ }
3053
+ finally {
3054
+ try {
3055
+ // Keep ownership until removal/extraction completes, so a newer upload
3056
+ // cannot start using the same path while this task is still changing it.
3057
+ await this.unlock();
3058
+ }
3059
+ catch (error) {
3060
+ failure ||= error;
3061
+ throw error;
3062
+ }
3063
+ finally {
3064
+ if (this.id != null)
3065
+ upload_manager_1.default.delete(this.id);
3066
+ this.onFinish?.(failure, cancelled);
3067
+ }
3068
+ }
3069
+ }
3070
+ addWrittenRange(start, end) {
3071
+ if (start >= end)
3072
+ return;
3073
+ let i = 0;
3074
+ let ranges = this.received;
3075
+ while (i < ranges.length && ranges[i].end < start)
3076
+ i++;
3077
+ let mergeStart = start, mergeEnd = end;
3078
+ let removeCount = 0;
3079
+ while (i + removeCount < ranges.length && ranges[i + removeCount].start <= mergeEnd) {
3080
+ mergeStart = Math.min(mergeStart, ranges[i + removeCount].start);
3081
+ mergeEnd = Math.max(mergeEnd, ranges[i + removeCount].end);
3082
+ removeCount++;
3083
+ }
3084
+ ranges.splice(i, removeCount, { start: mergeStart, end: mergeEnd });
3085
+ }
3086
+ isFullyCovered() {
3087
+ return (this.received.length === 1 &&
3088
+ this.received[0].start === 0 &&
3089
+ this.received[0].end === this.size);
3090
+ }
3091
+ }
3092
+ exports["default"] = FileWriter;
3093
+
3094
+
3095
+ /***/ },
3096
+
3097
+ /***/ "./plugins/file/src/backend/filepath.ts"
3098
+ (__unused_webpack_module, exports, __webpack_require__) {
3099
+
3100
+
3101
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3102
+ return (mod && mod.__esModule) ? mod : { "default": mod };
3103
+ };
3104
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
3105
+ exports.checkFileName = checkFileName;
3106
+ exports.clearUploadFiles = clearUploadFiles;
3107
+ exports.normalizedJoin = normalizedJoin;
3108
+ const fs_extra_1 = __importDefault(__webpack_require__("fs-extra"));
3109
+ const os_1 = __importDefault(__webpack_require__("os"));
3110
+ const path_1 = __importDefault(__webpack_require__("path"));
3111
+ function checkFileName(fileName) {
3112
+ return (typeof fileName === "string" &&
3113
+ fileName.length > 0 &&
3114
+ fileName !== "." &&
3115
+ fileName !== ".." &&
3116
+ !/[\\/|?*><;":\0]/.test(fileName));
3117
+ }
3118
+ function clearUploadFiles(file) {
3119
+ if (!file)
3120
+ return;
3121
+ if (file instanceof Array) {
3122
+ file.forEach((v) => {
3123
+ fs_extra_1.default.remove(v.filepath, () => { });
3124
+ });
3125
+ }
3126
+ else {
3127
+ fs_extra_1.default.remove(file.filepath, () => { });
3128
+ }
3129
+ }
3130
+ function normalizedJoin(...paths) {
3131
+ if (os_1.default.platform() === "win32") {
3132
+ return joinWin32(...paths);
3133
+ }
3134
+ return path_1.default.normalize(path_1.default.join(...paths));
3135
+ }
3136
+ function joinWin32(...paths) {
3137
+ if (!paths.length)
3138
+ return ".";
3139
+ if (paths.length == 1)
3140
+ return paths[0];
3141
+ const parts = path_1.default.win32.parse(paths[1]);
3142
+ let p;
3143
+ if (parts.root) {
3144
+ p = paths[1];
3145
+ }
3146
+ else {
3147
+ p = path_1.default.normalize(path_1.default.join(paths[0], paths[1]));
3148
+ }
3149
+ if (paths.length <= 2)
3150
+ return p;
3151
+ return joinWin32(p, ...paths.slice(2));
3152
+ }
3153
+
3154
+
3155
+ /***/ },
3156
+
3157
+ /***/ "./plugins/file/src/backend/http_router.ts"
3158
+ (__unused_webpack_module, exports, __webpack_require__) {
3159
+
3160
+
3161
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3162
+ return (mod && mod.__esModule) ? mod : { "default": mod };
3163
+ };
3164
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
3165
+ exports.registerHttpRoutes = registerHttpRoutes;
3166
+ const fs_extra_1 = __importDefault(__webpack_require__("fs-extra"));
3167
+ const path_1 = __importDefault(__webpack_require__("path"));
3168
+ const file_writer_1 = __importDefault(__webpack_require__("./plugins/file/src/backend/file_writer.ts"));
3169
+ const filepath_1 = __webpack_require__("./plugins/file/src/backend/filepath.ts");
3170
+ const runtime_1 = __webpack_require__("./plugins/file/src/backend/runtime.ts");
3171
+ const system_file_1 = __importDefault(__webpack_require__("./plugins/file/src/backend/system_file.ts"));
3172
+ const upload_manager_1 = __importDefault(__webpack_require__("./plugins/file/src/backend/upload_manager.ts"));
3173
+ /**
3174
+ * The upload and download HTTP routes.
3175
+ *
3176
+ * Both are authorized by a passport the panel registered over the protocol first
3177
+ * — the server plugin issues those (`passport/register`) — and the upload routes
3178
+ * sit behind the runtime plugin's upload middleware, which the web server mounts ahead of
3179
+ * the body parser.
3180
+ */
3181
+ function registerHttpRoutes() {
3182
+ const router = (0, runtime_1.core)().koa.router();
3183
+ // File download route
3184
+ router.get("/download/:key/:fileName", async (ctx) => {
3185
+ const key = ctx.params.key;
3186
+ const paramsFileName = ctx.params.fileName;
3187
+ try {
3188
+ // Get the task from the task center
3189
+ const mission = (0, runtime_1.transfer)().passports.getMission(key, "download");
3190
+ if (!mission)
3191
+ throw new Error((ctx.body = "Access denied: No task found"));
3192
+ const instance = (0, runtime_1.instances)().subsystem.getInstance(mission.parameter.instanceUuid);
3193
+ if (!instance)
3194
+ throw new Error((0, runtime_1.$t)("TXT_CODE_http_router.instanceNotExist"));
3195
+ if (!system_file_1.default.checkFileName(paramsFileName))
3196
+ throw new Error((0, runtime_1.$t)("TXT_CODE_http_router.fileNameNotSpec"));
3197
+ const cwd = instance.absoluteCwdPath();
3198
+ const fileRelativePath = mission.parameter.fileName;
3199
+ // Check for file cross-directory security risks
3200
+ const fileManager = new system_file_1.default(cwd);
3201
+ if (!fileManager.check(fileRelativePath))
3202
+ throw new Error((ctx.body = "Access denied: Invalid destination"));
3203
+ const fileAbsPath = fileManager.toAbsolutePath(fileRelativePath);
3204
+ await (0, runtime_1.transfer)().sendFile(ctx, fileAbsPath);
3205
+ }
3206
+ catch (error) {
3207
+ if (!ctx.res.headersSent) {
3208
+ ctx.body = (0, runtime_1.$t)("TXT_CODE_http_router.downloadErr", { error: error.message });
3209
+ ctx.status = 500;
3210
+ }
3211
+ }
3212
+ finally {
3213
+ (0, runtime_1.transfer)().passports.deleteMission(key);
3214
+ }
3215
+ });
3216
+ // Old version upload route
3217
+ router.post("/upload/:key", async (ctx) => {
3218
+ const key = String(ctx.params.key);
3219
+ const unzip = ctx.query.unzip === "true";
3220
+ const zipCode = String(ctx.query.code);
3221
+ const tmpFiles = ctx.request.files?.file;
3222
+ try {
3223
+ const mission = (0, runtime_1.transfer)().passports.getMission(key, "upload");
3224
+ if (!mission)
3225
+ throw new Error("Access denied: No task found");
3226
+ const instance = (0, runtime_1.instances)().subsystem.getInstance(mission.parameter.instanceUuid);
3227
+ if (!instance)
3228
+ throw new Error("Access denied: No instance found");
3229
+ const uploadDir = mission.parameter.uploadDir;
3230
+ const cwd = instance.absoluteCwdPath();
3231
+ if (tmpFiles) {
3232
+ let uploadedFile;
3233
+ if (tmpFiles instanceof Array) {
3234
+ uploadedFile = tmpFiles[0];
3235
+ }
3236
+ else if (tmpFiles) {
3237
+ uploadedFile = tmpFiles;
3238
+ }
3239
+ else {
3240
+ throw new Error("Empty File!");
3241
+ }
3242
+ const originFileName = uploadedFile.originalFilename || "";
3243
+ if (!system_file_1.default.checkFileName(path_1.default.basename(originFileName)))
3244
+ throw new Error("Access denied: Malformed file name");
3245
+ const fileManager = new system_file_1.default(cwd);
3246
+ const ext = path_1.default.extname(originFileName);
3247
+ const basename = path_1.default.basename(originFileName, ext);
3248
+ let tempFileSaveName = basename + ext;
3249
+ let counter = 1;
3250
+ while (fs_extra_1.default.existsSync(fileManager.toAbsolutePath(path_1.default.normalize(path_1.default.join(uploadDir, tempFileSaveName)))) &&
3251
+ ctx.query.overwrite === "false") {
3252
+ if (counter == 1) {
3253
+ tempFileSaveName = `${basename}-copy${ext}`;
3254
+ }
3255
+ else {
3256
+ tempFileSaveName = `${basename}-copy-${counter}${ext}`;
3257
+ }
3258
+ counter++;
3259
+ }
3260
+ let fileSaveRelativePath = path_1.default.normalize(path_1.default.join(uploadDir, tempFileSaveName));
3261
+ if (!fileManager.checkPath(fileSaveRelativePath))
3262
+ throw new Error("Access denied: Invalid destination");
3263
+ const fileSaveAbsolutePath = fileManager.toAbsolutePath(fileSaveRelativePath);
3264
+ await fs_extra_1.default.move(uploadedFile.filepath, fileSaveAbsolutePath, {
3265
+ overwrite: true
3266
+ });
3267
+ if (unzip) {
3268
+ const instanceFiles = new system_file_1.default(instance.absoluteCwdPath());
3269
+ await instanceFiles.unzip(fileSaveAbsolutePath, ".", zipCode);
3270
+ }
3271
+ ctx.body = "OK";
3272
+ return;
3273
+ }
3274
+ ctx.body = "Access denied: No file found";
3275
+ ctx.status = 500;
3276
+ }
3277
+ catch (error) {
3278
+ ctx.body = error.message;
3279
+ ctx.status = 500;
3280
+ }
3281
+ finally {
3282
+ (0, runtime_1.transfer)().passports.deleteMission(key);
3283
+ if (tmpFiles)
3284
+ (0, filepath_1.clearUploadFiles)(tmpFiles);
3285
+ }
3286
+ });
3287
+ router.post("/upload-new/:key", async (ctx) => {
3288
+ const key = String(ctx.params.key);
3289
+ const unzip = ctx.query.unzip === "true";
3290
+ const zipCode = String(ctx.query.code);
3291
+ const filename = String(ctx.query.filename);
3292
+ const size = Number(ctx.query.size);
3293
+ if (ctx.query.stop) {
3294
+ const writer = upload_manager_1.default.get(key);
3295
+ if (writer) {
3296
+ await writer.stop();
3297
+ upload_manager_1.default.delete(key);
3298
+ ctx.body = "OK";
3299
+ return;
3300
+ }
3301
+ else {
3302
+ ctx.body = "Access denied: No task found";
3303
+ ctx.status = 500;
3304
+ return;
3305
+ }
3306
+ }
3307
+ try {
3308
+ const mission = (0, runtime_1.transfer)().passports.getMission(key, "upload");
3309
+ if (!mission)
3310
+ throw new Error("Access denied: No task found");
3311
+ const instance = (0, runtime_1.instances)().subsystem.getInstance(mission.parameter.instanceUuid);
3312
+ if (!instance)
3313
+ throw new Error("Access denied: No instance found");
3314
+ const uploadDir = mission.parameter.uploadDir;
3315
+ const cwd = instance.absoluteCwdPath();
3316
+ const overwrite = ctx.query.overwrite !== "false";
3317
+ if (!Number.isSafeInteger(size) || size < 0)
3318
+ throw new Error("Invalid file size");
3319
+ const filePath = await file_writer_1.default.getPath(cwd, uploadDir, filename, overwrite);
3320
+ let fr = upload_manager_1.default.getByPath(filePath);
3321
+ if (fr && size != fr.writer.size) {
3322
+ upload_manager_1.default.delete(fr.id);
3323
+ await fr.writer.stop();
3324
+ fr = undefined;
3325
+ }
3326
+ if (!fr) {
3327
+ const fileWriter = new file_writer_1.default(cwd, filename, size, unzip, zipCode, filePath);
3328
+ await fileWriter.init();
3329
+ const id = upload_manager_1.default.add(fileWriter);
3330
+ fr = { id, writer: fileWriter };
3331
+ const tasks = (0, runtime_1.core)().get("backgroundTasks");
3332
+ tasks?.runRequest(unzip ? "file/upload_extract" : "file/upload", {
3333
+ _taskOwner: mission.parameter._taskOwner,
3334
+ instanceUuid: mission.parameter.instanceUuid,
3335
+ fileName: filename
3336
+ }, () => {
3337
+ tasks.progress(() => ({
3338
+ current: fileWriter.received.reduce((sum, range) => sum + range.end - range.start, 0),
3339
+ total: size,
3340
+ cancellable: fileWriter.cancellable
3341
+ }), () => { void fileWriter.stop().catch((err) => (0, runtime_1.core)().logger.warn(err)); });
3342
+ tasks.follow(new Promise((resolve, reject) => {
3343
+ fileWriter.onFinish = (error, cancelled) => {
3344
+ if (error)
3345
+ reject(error);
3346
+ else if (cancelled)
3347
+ reject(Object.assign(new Error("Upload cancelled"), { code: "ABORT_ERR" }));
3348
+ else
3349
+ resolve();
3350
+ };
3351
+ }));
3352
+ });
3353
+ }
3354
+ if (size === 0)
3355
+ await fr.writer.done();
3356
+ ctx.body = {
3357
+ data: {
3358
+ id: fr.id,
3359
+ received: fr.writer.received
3360
+ },
3361
+ status: 200
3362
+ };
3363
+ return;
3364
+ }
3365
+ catch (error) {
3366
+ ctx.body = error.message;
3367
+ ctx.status = 500;
3368
+ }
3369
+ finally {
3370
+ (0, runtime_1.transfer)().passports.deleteMission(key);
3371
+ }
3372
+ });
3373
+ router.post("/upload-piece/:id", async (ctx) => {
3374
+ const id = String(ctx.params.id);
3375
+ const offset = Number(ctx.query.offset);
3376
+ const tmpFiles = ctx.request.files?.file;
3377
+ try {
3378
+ if (!tmpFiles) {
3379
+ ctx.body = "Access denied: No file found";
3380
+ ctx.status = 500;
3381
+ }
3382
+ let uploadedFile;
3383
+ if (tmpFiles instanceof Array) {
3384
+ uploadedFile = tmpFiles[0];
3385
+ }
3386
+ else if (tmpFiles) {
3387
+ uploadedFile = tmpFiles;
3388
+ }
3389
+ else {
3390
+ throw new Error("Empty File!");
3391
+ }
3392
+ const writer = upload_manager_1.default.get(id);
3393
+ if (!writer)
3394
+ throw new Error("Access denied: No task found");
3395
+ const chunk = await fs_extra_1.default.readFile(uploadedFile.filepath);
3396
+ await writer.write(offset, chunk);
3397
+ ctx.body = "OK";
3398
+ return;
3399
+ }
3400
+ catch (error) {
3401
+ ctx.body = error.message;
3402
+ ctx.status = 500;
3403
+ }
3404
+ finally {
3405
+ (0, filepath_1.clearUploadFiles)(tmpFiles);
3406
+ }
3407
+ });
3408
+ }
3409
+
3410
+
3411
+ /***/ },
3412
+
3413
+ /***/ "./plugins/file/src/backend/index.ts"
3414
+ (__unused_webpack_module, exports, __webpack_require__) {
3415
+
3416
+
3417
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3418
+ return (mod && mod.__esModule) ? mod : { "default": mod };
3419
+ };
3420
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
3421
+ exports.inject = void 0;
3422
+ exports.apply = apply;
3423
+ const i18n_1 = __webpack_require__("./plugins/file/src/i18n/index.ts");
3424
+ const file_router_1 = __webpack_require__("./plugins/file/src/backend/file_router.ts");
3425
+ const file_service_1 = __webpack_require__("./plugins/file/src/backend/file_service.ts");
3426
+ const http_router_1 = __webpack_require__("./plugins/file/src/backend/http_router.ts");
3427
+ const runtime_1 = __webpack_require__("./plugins/file/src/backend/runtime.ts");
3428
+ const system_file_1 = __importDefault(__webpack_require__("./plugins/file/src/backend/system_file.ts"));
3429
+ const upload_manager_1 = __importDefault(__webpack_require__("./plugins/file/src/backend/upload_manager.ts"));
3430
+ const settings_1 = __webpack_require__("./plugins/file/src/backend/settings.ts");
3431
+ // The daemon's file subsystem.
3432
+ //
3433
+ // Everything about reading and writing instance files lives here: the
3434
+ // `FileManager` that resolves and sandboxes a path, the chunked upload manager,
3435
+ // the fourteen `file/*` protocol events and the upload/download HTTP routes.
3436
+ //
3437
+ // The primitives are handed to the rest of the daemon as `ctx.files`, because
3438
+ // instance creation, the Java manager, SteamCMD and the mod service all need to
3439
+ // touch files too. Consumers resolve the service through their plugin context,
3440
+ // so removing this plugin removes the daemon's
3441
+ // ability to touch instance files rather than breaking the build.
3442
+ // File routes resolve the instance service when a request arrives. Keeping the
3443
+ // dependency lazy avoids a startup cycle: the instance plugin depends on the
3444
+ // file primitives, while the file routes only need instances at request time.
3445
+ exports.inject = {
3446
+ i18n: { required: true },
3447
+ settings: { required: true },
3448
+ protocol: { required: true },
3449
+ archive: { required: true },
3450
+ transfer: { required: true },
3451
+ koa: { required: true },
3452
+ // File routes resolve the instance subsystem lazily to avoid a startup
3453
+ // cycle: the instance plugin requires the file service in return.
3454
+ instances: { required: false }
3455
+ };
3456
+ function apply(ctx) {
3457
+ // Before anything else: the modules below read the logger, the configuration,
3458
+ // the instances and `$t` through this handle.
3459
+ (0, runtime_1.setPluginContext)(ctx);
3460
+ ctx.i18n.define(i18n_1.localeMessages);
3461
+ (0, settings_1.registerSettings)(ctx);
3462
+ // `ctx.set()` from inside a plugin belongs to that plugin: the primitives — and
3463
+ // with them the daemon's access to instance files — leave when it unloads.
3464
+ ctx.set("files", {
3465
+ FileManager: system_file_1.default,
3466
+ uploads: upload_manager_1.default,
3467
+ getFileManager: file_service_1.getFileManager,
3468
+ getWindowsDisks: file_service_1.getWindowsDisks
3469
+ });
3470
+ (0, file_router_1.registerFileEvents)();
3471
+ (0, http_router_1.registerHttpRoutes)();
3472
+ // Chunked uploads hold open file handles and lock files, so they are stopped
3473
+ // with the plugin. The daemon used to do this from its own shutdown path.
3474
+ ctx.on("dispose", () => upload_manager_1.default.exit());
3475
+ }
3476
+
3477
+
3478
+ /***/ },
3479
+
3480
+ /***/ "./plugins/file/src/backend/runtime.ts"
3481
+ (__unused_webpack_module, exports) {
3482
+
3483
+
3484
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
3485
+ exports.fileTasks = exports.instances = exports.$t = exports.transfer = exports.archive = exports.protocol = exports.settings = exports.logger = void 0;
3486
+ exports.setPluginContext = setPluginContext;
3487
+ exports.core = core;
3488
+ /**
3489
+ * This plugin's private handle on its cordis context.
3490
+ *
3491
+ * The daemon core is bundled into `app.js`, so a plugin cannot import its
3492
+ * singletons. The only channel is the context `apply()` receives, and this
3493
+ * plugin is six modules deep — a `FileManager` throws translated errors, a
3494
+ * `FileWriter` logs from inside a stream callback — so threading a parameter
3495
+ * through all of them would say nothing the accessors below do not.
3496
+ *
3497
+ * A plugin with only a handful of such call sites should pass `ctx` as an
3498
+ * argument instead.
3499
+ */
3500
+ let context;
3501
+ function setPluginContext(value) {
3502
+ context = value;
3503
+ }
3504
+ function core() {
3505
+ if (!context)
3506
+ throw new Error("The file plugin has not been initialized yet.");
3507
+ return context;
3508
+ }
3509
+ /**
3510
+ * `ctx.logger` is bound to this plugin's own cordis scope, so it is gone once
3511
+ * that scope is disposed — and the disposer stops every upload, a path that
3512
+ * logs. Shutting down must not fail on a log line.
3513
+ */
3514
+ const SILENT = {
3515
+ info() { },
3516
+ warn() { },
3517
+ error() { },
3518
+ debug() { },
3519
+ success() { },
3520
+ extend: () => SILENT
3521
+ };
3522
+ const logger = () => core().logger ?? SILENT;
3523
+ exports.logger = logger;
3524
+ const settings = () => core().settings;
3525
+ exports.settings = settings;
3526
+ const protocol = () => core().protocol;
3527
+ exports.protocol = protocol;
3528
+ const archive = () => core().archive;
3529
+ exports.archive = archive;
3530
+ const transfer = () => core().transfer;
3531
+ exports.transfer = transfer;
3532
+ const $t = (key, options) => (core().get("i18n")?.$t(key, options) ?? key);
3533
+ exports.$t = $t;
3534
+ /**
3535
+ * The file plugin can load before the instance plugin. `get()` avoids Cordis'
3536
+ * undeclared-property warning while keeping the existing lazy dependency.
3537
+ */
3538
+ const instances = () => {
3539
+ const service = core().get("instances");
3540
+ if (!service)
3541
+ throw new Error("Instance access requires the daemon instance plugin.");
3542
+ return service;
3543
+ };
3544
+ exports.instances = instances;
3545
+ /**
3546
+ * How many file tasks are running across the whole daemon. It used to be
3547
+ * `globalEnv` in the core's configuration module, read by nothing but the file
3548
+ * routes, so it lives with them now.
3549
+ */
3550
+ exports.fileTasks = { count: 0 };
3551
+
3552
+
3553
+ /***/ },
3554
+
3555
+ /***/ "./plugins/file/src/backend/settings.ts"
3556
+ (__unused_webpack_module, exports) {
3557
+
3558
+
3559
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
3560
+ exports.registerSettings = registerSettings;
3561
+ function registerSettings(ctx) {
3562
+ const config = ctx.settings.config;
3563
+ const $t = ctx.i18n.$t;
3564
+ const speedOptions = (current) => [...new Set([0, 5, 10, 16, 32, 64, 96, 128, 160, 240, 320, 480, current])].map((value) => ({
3565
+ value,
3566
+ label: value === 0
3567
+ ? $t("TXT_CODE_e3a77a77")
3568
+ : value < 16
3569
+ ? `${value * 64}KB/s`
3570
+ : `${value / 16}MB/s`
3571
+ }));
3572
+ ctx.inject(["settingsForm"], (settingsCtx) => settingsCtx.settingsForm.declare({
3573
+ fields: () => [
3574
+ {
3575
+ key: "uploadSpeedRate",
3576
+ type: "select",
3577
+ title: $t("TXT_CODE_fde31068"),
3578
+ description: $t("TXT_CODE_d8d19932"),
3579
+ options: speedOptions(config.uploadSpeedRate)
3580
+ },
3581
+ {
3582
+ key: "downloadSpeedRate",
3583
+ type: "select",
3584
+ title: $t("TXT_CODE_785a0fcf"),
3585
+ description: $t("TXT_CODE_b9fc604c"),
3586
+ options: speedOptions(config.downloadSpeedRate)
3587
+ },
3588
+ {
3589
+ key: "maxDownloadFromUrlFileCount",
3590
+ type: "number",
3591
+ title: $t("TXT_CODE_a15fca22"),
3592
+ description: $t("TXT_CODE_ecaf78a2"),
3593
+ min: 0
3594
+ }
3595
+ ],
3596
+ read: () => ({
3597
+ uploadSpeedRate: config.uploadSpeedRate,
3598
+ downloadSpeedRate: config.downloadSpeedRate,
3599
+ maxDownloadFromUrlFileCount: config.maxDownloadFromUrlFileCount
3600
+ }),
3601
+ write: (values) => {
3602
+ const count = values.maxDownloadFromUrlFileCount;
3603
+ if (count != null && (!Number.isInteger(count) || Number(count) < 0)) {
3604
+ throw new Error($t("TXT_CODE_ecaf78a2"));
3605
+ }
3606
+ if (values.uploadSpeedRate != null)
3607
+ config.uploadSpeedRate = Number(values.uploadSpeedRate);
3608
+ if (values.downloadSpeedRate != null)
3609
+ config.downloadSpeedRate = Number(values.downloadSpeedRate);
3610
+ if (count != null)
3611
+ config.maxDownloadFromUrlFileCount = Number(count);
3612
+ ctx.settings.save();
3613
+ }
3614
+ }));
3615
+ }
3616
+
3617
+
3618
+ /***/ },
3619
+
3620
+ /***/ "./plugins/file/src/backend/system_file.ts"
3621
+ (__unused_webpack_module, exports, __webpack_require__) {
3622
+
3623
+
3624
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3625
+ return (mod && mod.__esModule) ? mod : { "default": mod };
3626
+ };
3627
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
3628
+ const fs_extra_1 = __importDefault(__webpack_require__("fs-extra"));
3629
+ const iconv_lite_1 = __importDefault(__webpack_require__("iconv-lite"));
3630
+ const mcsmanager_common_1 = __webpack_require__("../common/src/index.ts");
3631
+ const os_1 = __importDefault(__webpack_require__("os"));
3632
+ const path_1 = __importDefault(__webpack_require__("path"));
3633
+ const filepath_1 = __webpack_require__("./plugins/file/src/backend/filepath.ts");
3634
+ const runtime_1 = __webpack_require__("./plugins/file/src/backend/runtime.ts");
3635
+ /** Resolved per use: the module is required before `apply()` runs. */
3636
+ const ERROR_MSG_01 = () => (0, runtime_1.$t)("TXT_CODE_system_file.illegalAccess");
3637
+ const MAX_EDIT_SIZE = 1024 * 1024 * 5;
3638
+ class FileManager {
3639
+ topPath;
3640
+ fileCode;
3641
+ cwd = ".";
3642
+ constructor(topPath = "", fileCode) {
3643
+ this.topPath = topPath;
3644
+ this.fileCode = fileCode;
3645
+ if (!path_1.default.isAbsolute(topPath)) {
3646
+ this.topPath = path_1.default.normalize(path_1.default.join(process.cwd(), topPath));
3647
+ }
3648
+ else {
3649
+ this.topPath = path_1.default.normalize(topPath);
3650
+ }
3651
+ if (!fileCode) {
3652
+ this.fileCode = "utf-8";
3653
+ if ((0, runtime_1.settings)().config.language === "zh_cn")
3654
+ this.fileCode = "gbk";
3655
+ }
3656
+ }
3657
+ isRootTopRath() {
3658
+ return this.topPath === "/" || this.topPath === "\\";
3659
+ }
3660
+ toAbsolutePath(fileName = "") {
3661
+ const topAbsolutePath = this.topPath;
3662
+ if (path_1.default.normalize(fileName).indexOf(topAbsolutePath) === 0) {
3663
+ const resolved = path_1.default.resolve(fileName);
3664
+ this.assertInsideRoot(resolved);
3665
+ return resolved;
3666
+ }
3667
+ let finalPath = "";
3668
+ if (os_1.default.platform() === "win32") {
3669
+ const reg = new RegExp("^[A-Za-z]{1}:[\\\\/]{1}");
3670
+ if (reg.test(this.cwd)) {
3671
+ finalPath = path_1.default.normalize(path_1.default.join(this.cwd, fileName));
3672
+ }
3673
+ else if (reg.test(fileName)) {
3674
+ finalPath = path_1.default.normalize(fileName);
3675
+ }
3676
+ }
3677
+ if (!finalPath) {
3678
+ finalPath = path_1.default.normalize(path_1.default.join(this.topPath, this.cwd, fileName));
3679
+ }
3680
+ this.assertInsideRoot(finalPath);
3681
+ return finalPath;
3682
+ }
3683
+ assertInsideRoot(target) {
3684
+ if (this.isRootTopRath())
3685
+ return;
3686
+ const isInside = (root, candidate) => {
3687
+ const relative = path_1.default.relative(root, candidate);
3688
+ return (relative !== ".." && !relative.startsWith(`..${path_1.default.sep}`) && !path_1.default.isAbsolute(relative));
3689
+ };
3690
+ if (!isInside(this.topPath, target)) {
3691
+ throw new Error(ERROR_MSG_01());
3692
+ }
3693
+ // New files need the same protection as existing files: resolve the nearest
3694
+ // existing parent so a symlink/junction cannot escape an instance directory.
3695
+ if (!fs_extra_1.default.existsSync(this.topPath))
3696
+ return;
3697
+ const realRoot = fs_extra_1.default.realpathSync(this.topPath);
3698
+ let ancestor = target;
3699
+ while (true) {
3700
+ try {
3701
+ fs_extra_1.default.lstatSync(ancestor);
3702
+ break;
3703
+ }
3704
+ catch (error) {
3705
+ if (error.code !== "ENOENT")
3706
+ throw error;
3707
+ }
3708
+ const parent = path_1.default.dirname(ancestor);
3709
+ if (parent === ancestor)
3710
+ throw new Error(ERROR_MSG_01());
3711
+ ancestor = parent;
3712
+ }
3713
+ if (!isInside(realRoot, fs_extra_1.default.realpathSync(ancestor)))
3714
+ throw new Error(ERROR_MSG_01());
3715
+ }
3716
+ checkPath(fileNameOrPath) {
3717
+ if (this.isRootTopRath())
3718
+ return true;
3719
+ const destAbsolutePath = path_1.default.normalize(this.toAbsolutePath(fileNameOrPath));
3720
+ const topAbsolutePath = path_1.default.normalize(this.topPath);
3721
+ const destPath = destAbsolutePath.endsWith(path_1.default.sep)
3722
+ ? destAbsolutePath.slice(0, -1)
3723
+ : destAbsolutePath;
3724
+ const topPath = topAbsolutePath.endsWith(path_1.default.sep)
3725
+ ? topAbsolutePath.slice(0, -1)
3726
+ : topAbsolutePath;
3727
+ if (destPath.startsWith(topPath)) {
3728
+ const parts = destPath.split(path_1.default.sep);
3729
+ return topPath.split(path_1.default.sep).every((part, index) => {
3730
+ return part === parts[index];
3731
+ });
3732
+ }
3733
+ return false;
3734
+ }
3735
+ check(destPath) {
3736
+ if (this.isRootTopRath())
3737
+ return true;
3738
+ return this.checkPath(destPath) && fs_extra_1.default.existsSync(this.toAbsolutePath(destPath));
3739
+ }
3740
+ cd(dirName) {
3741
+ if (!this.check(dirName))
3742
+ throw new Error(ERROR_MSG_01());
3743
+ this.cwd = (0, filepath_1.normalizedJoin)(this.cwd, dirName);
3744
+ }
3745
+ async list(page, pageSize = 40, searchFileName) {
3746
+ if (pageSize > 100 || pageSize <= 0 || page < 0)
3747
+ throw new Error("Beyond the value limit");
3748
+ // Use withFileTypes option to get file type directly, reducing stat calls
3749
+ const dirents = await fs_extra_1.default.readdir(this.toAbsolutePath(), { withFileTypes: true });
3750
+ // Filter search results and create basic file info with type
3751
+ let filteredItems = dirents
3752
+ .filter((dirent) => !searchFileName || dirent.name.toLowerCase().includes(searchFileName.toLowerCase()))
3753
+ .map((dirent) => ({
3754
+ name: dirent.name,
3755
+ type: dirent.isFile() ? 1 : 0
3756
+ }));
3757
+ const total = filteredItems.length;
3758
+ // Sort: directories first (type 0), then files (type 1), both alphabetically
3759
+ filteredItems.sort((a, b) => {
3760
+ if (a.type !== b.type)
3761
+ return a.type - b.type;
3762
+ return a.name.localeCompare(b.name);
3763
+ });
3764
+ const sliceStart = page * pageSize;
3765
+ const sliceEnd = sliceStart + pageSize;
3766
+ const targetItems = filteredItems.slice(sliceStart, sliceEnd);
3767
+ const statPromises = targetItems.map(async (item) => {
3768
+ try {
3769
+ const info = await fs_extra_1.default.stat(this.toAbsolutePath(item.name));
3770
+ const mode = parseInt(String(parseInt(info.mode?.toString(8), 10)).slice(-3));
3771
+ return {
3772
+ name: item.name,
3773
+ size: info.isFile() ? info.size : 0,
3774
+ time: info.atime.toString(),
3775
+ mode,
3776
+ type: item.type
3777
+ };
3778
+ }
3779
+ catch (error) {
3780
+ return {
3781
+ name: item.name,
3782
+ size: 0,
3783
+ time: new Date().toString(),
3784
+ mode: 0,
3785
+ type: item.type
3786
+ };
3787
+ }
3788
+ });
3789
+ // Execute all stat operations concurrently
3790
+ const resultList = await Promise.all(statPromises);
3791
+ return {
3792
+ items: resultList,
3793
+ page,
3794
+ pageSize,
3795
+ total,
3796
+ absolutePath: this.toAbsolutePath()
3797
+ };
3798
+ }
3799
+ async chmod(fileName, chmodValue, deep) {
3800
+ if (!this.check(fileName) || isNaN(parseInt(chmodValue)))
3801
+ throw new Error(ERROR_MSG_01());
3802
+ const absPath = this.toAbsolutePath(fileName);
3803
+ const defaultPath = "/bin/chmod";
3804
+ let file = "chmod";
3805
+ if (fs_extra_1.default.existsSync(defaultPath))
3806
+ file = defaultPath;
3807
+ const params = [];
3808
+ if (deep)
3809
+ params.push("-R");
3810
+ params.push(String(chmodValue));
3811
+ params.push(absPath);
3812
+ return await new mcsmanager_common_1.ProcessWrapper(file, params, ".", 60 * 10).start();
3813
+ }
3814
+ async readFile(fileName) {
3815
+ if (!this.check(fileName))
3816
+ throw new Error(ERROR_MSG_01());
3817
+ const absPath = this.toAbsolutePath(fileName);
3818
+ const buf = await fs_extra_1.default.readFile(absPath);
3819
+ const text = iconv_lite_1.default.decode(buf, this.fileCode || "utf-8");
3820
+ return text;
3821
+ }
3822
+ async writeFile(fileName, data) {
3823
+ if (!this.check(fileName))
3824
+ throw new Error(ERROR_MSG_01());
3825
+ const absPath = this.toAbsolutePath(fileName);
3826
+ const buf = iconv_lite_1.default.encode(data, this.fileCode || "utf-8");
3827
+ return await fs_extra_1.default.writeFile(absPath, buf);
3828
+ }
3829
+ async newFile(fileName) {
3830
+ // if (!FileManager.checkFileName(fileName)) throw new Error(ERROR_MSG_01());
3831
+ if (!this.checkPath(fileName))
3832
+ throw new Error(ERROR_MSG_01());
3833
+ const target = this.toAbsolutePath(fileName);
3834
+ await fs_extra_1.default.createFile(target);
3835
+ }
3836
+ /** Exclusive creation: never truncate an existing file or follow its symlink. */
3837
+ async createTextFile(target, text) {
3838
+ if (!this.checkPath(target))
3839
+ throw new Error(ERROR_MSG_01());
3840
+ const content = iconv_lite_1.default.encode(text, this.fileCode || "utf-8");
3841
+ if (content.length > 64 * 1024)
3842
+ throw new Error("File content exceeds 64 KiB");
3843
+ await fs_extra_1.default.writeFile(this.toAbsolutePath(target), content, { flag: "wx" });
3844
+ return true;
3845
+ }
3846
+ /** Unlink one regular file; never recursively remove directories. */
3847
+ async removeFile(target) {
3848
+ if (!this.check(target))
3849
+ throw new Error(ERROR_MSG_01());
3850
+ const absolute = this.toAbsolutePath(target);
3851
+ if (!(await fs_extra_1.default.lstat(absolute)).isFile())
3852
+ throw new Error("Expected a regular file");
3853
+ await fs_extra_1.default.unlink(absolute);
3854
+ return true;
3855
+ }
3856
+ async copy(target1, target2) {
3857
+ if (!this.checkPath(target2) || !this.check(target1))
3858
+ throw new Error(ERROR_MSG_01());
3859
+ const targetPath = this.toAbsolutePath(target1);
3860
+ target2 = this.toAbsolutePath(target2);
3861
+ return await fs_extra_1.default.copy(targetPath, target2);
3862
+ }
3863
+ mkdir(target) {
3864
+ if (!this.checkPath(target))
3865
+ throw new Error(ERROR_MSG_01());
3866
+ const targetPath = this.toAbsolutePath(target);
3867
+ return fs_extra_1.default.mkdirSync(targetPath);
3868
+ }
3869
+ async delete(target) {
3870
+ if (!this.check(target))
3871
+ throw new Error(ERROR_MSG_01());
3872
+ const targetPath = this.toAbsolutePath(target);
3873
+ return new Promise((r, j) => {
3874
+ fs_extra_1.default.remove(targetPath, (err) => {
3875
+ if (!err)
3876
+ r(true);
3877
+ else
3878
+ j(err);
3879
+ });
3880
+ });
3881
+ }
3882
+ async move(target, destPath) {
3883
+ if (!this.check(target))
3884
+ throw new Error(ERROR_MSG_01());
3885
+ if (!this.checkPath(destPath))
3886
+ throw new Error(ERROR_MSG_01());
3887
+ const targetPath = this.toAbsolutePath(target);
3888
+ destPath = this.toAbsolutePath(destPath);
3889
+ await fs_extra_1.default.move(targetPath, destPath);
3890
+ }
3891
+ zipFileCheck(path) {
3892
+ const fileInfo = fs_extra_1.default.statSync(path);
3893
+ const MAX_ZIP_GB = (0, runtime_1.settings)().config.maxZipFileSize;
3894
+ if (fileInfo.size > 1024 * 1024 * 1024 * MAX_ZIP_GB)
3895
+ throw new Error((0, runtime_1.$t)("TXT_CODE_system_file.unzipLimit", { max: MAX_ZIP_GB }));
3896
+ }
3897
+ async unzip(sourceZip, destDir, code) {
3898
+ if (!code)
3899
+ code = this.fileCode;
3900
+ if (!this.check(sourceZip) || !this.checkPath(destDir))
3901
+ throw new Error(ERROR_MSG_01());
3902
+ this.zipFileCheck(this.toAbsolutePath(sourceZip));
3903
+ return await (0, runtime_1.archive)().decompress(this.toAbsolutePath(sourceZip), this.toAbsolutePath(destDir), code);
3904
+ }
3905
+ async previewArchive(sourceZip, code) {
3906
+ if (!this.check(sourceZip))
3907
+ throw new Error(ERROR_MSG_01());
3908
+ this.zipFileCheck(this.toAbsolutePath(sourceZip));
3909
+ return await (0, runtime_1.archive)().listArchiveEntries(this.toAbsolutePath(sourceZip), code || this.fileCode);
3910
+ }
3911
+ async zip(sourceZip, files, code) {
3912
+ if (!code)
3913
+ code = this.fileCode;
3914
+ if (!this.checkPath(sourceZip))
3915
+ throw new Error(ERROR_MSG_01());
3916
+ const MAX_ZIP_GB = (0, runtime_1.settings)().config.maxZipFileSize;
3917
+ const MAX_TOTAL_FIELS_SIZE = 1024 * 1024 * 1024 * MAX_ZIP_GB;
3918
+ const sourceZipPath = this.toAbsolutePath(sourceZip);
3919
+ const filesPath = [];
3920
+ let totalSize = 0;
3921
+ for (const iterator of files) {
3922
+ if (this.check(iterator)) {
3923
+ filesPath.push(this.toAbsolutePath(iterator));
3924
+ try {
3925
+ totalSize += fs_extra_1.default.statSync(this.toAbsolutePath(iterator))?.size;
3926
+ }
3927
+ catch (error) { }
3928
+ }
3929
+ }
3930
+ if (totalSize > MAX_TOTAL_FIELS_SIZE)
3931
+ throw new Error((0, runtime_1.$t)("TXT_CODE_system_file.unzipLimit", { max: MAX_ZIP_GB }));
3932
+ return await (0, runtime_1.archive)().compress(sourceZipPath, filesPath, code);
3933
+ }
3934
+ async edit(target, data) {
3935
+ if (!this.check(target))
3936
+ throw new Error(ERROR_MSG_01());
3937
+ if (data || typeof data === "string") {
3938
+ return await this.writeFile(target, data);
3939
+ }
3940
+ else {
3941
+ const absPath = this.toAbsolutePath(target);
3942
+ const info = fs_extra_1.default.statSync(absPath);
3943
+ if (info.size > MAX_EDIT_SIZE) {
3944
+ throw new Error((0, runtime_1.$t)("TXT_CODE_system_file.execLimit"));
3945
+ }
3946
+ return await this.readFile(target);
3947
+ }
3948
+ }
3949
+ rename(target, newName) {
3950
+ if (!this.check(target))
3951
+ throw new Error(ERROR_MSG_01());
3952
+ if (!this.checkPath(newName))
3953
+ throw new Error(ERROR_MSG_01());
3954
+ const targetPath = this.toAbsolutePath(target);
3955
+ const newPath = this.toAbsolutePath(newName);
3956
+ fs_extra_1.default.renameSync(targetPath, newPath);
3957
+ }
3958
+ static checkFileName(fileName) {
3959
+ return (0, filepath_1.checkFileName)(fileName);
3960
+ }
3961
+ }
3962
+ exports["default"] = FileManager;
3963
+
3964
+
3965
+ /***/ },
3966
+
3967
+ /***/ "./plugins/file/src/backend/upload_manager.ts"
3968
+ (__unused_webpack_module, exports, __webpack_require__) {
3969
+
3970
+
3971
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
3972
+ const uuid_1 = __webpack_require__("uuid");
3973
+ class UploadManager {
3974
+ uploads = new Map();
3975
+ static timeout = 60 * 60 * 1000; // 1 hour
3976
+ cleanupTimer;
3977
+ startCleanup() {
3978
+ if (this.cleanupTimer)
3979
+ return;
3980
+ this.cleanupTimer = setInterval(async () => {
3981
+ await this.clearExpired();
3982
+ }, 5 * 60 * 1000 // 5 min
3983
+ );
3984
+ this.cleanupTimer.unref();
3985
+ }
3986
+ add(file) {
3987
+ this.startCleanup();
3988
+ const key = (0, uuid_1.v4)();
3989
+ file.id = key;
3990
+ this.uploads.set(key, file);
3991
+ return key;
3992
+ }
3993
+ getUploads() {
3994
+ return this.uploads.entries();
3995
+ }
3996
+ get(key) {
3997
+ return this.uploads.get(key);
3998
+ }
3999
+ delete(key) {
4000
+ return this.uploads.delete(key);
4001
+ }
4002
+ getByPath(path) {
4003
+ for (const [id, writer] of this.uploads.entries()) {
4004
+ if (writer.path == path) {
4005
+ return { id, writer };
4006
+ }
4007
+ }
4008
+ return undefined;
4009
+ }
4010
+ async clearExpired() {
4011
+ const now = Date.now();
4012
+ for (const [key, writer] of this.uploads.entries()) {
4013
+ if (now - writer.lastUpdate > UploadManager.timeout) {
4014
+ try {
4015
+ await writer.stop();
4016
+ this.uploads.delete(key);
4017
+ }
4018
+ catch (e) {
4019
+ console.error(`Failed to stop upload writer for key ${key}:`, e);
4020
+ }
4021
+ }
4022
+ }
4023
+ }
4024
+ async exit() {
4025
+ clearInterval(this.cleanupTimer);
4026
+ this.cleanupTimer = undefined;
4027
+ for (const [key, writer] of this.uploads.entries()) {
4028
+ try {
4029
+ await writer.stop();
4030
+ this.uploads.delete(key);
4031
+ }
4032
+ catch (e) {
4033
+ console.error(`Failed to stop upload writer for key ${key}:`, e);
4034
+ }
4035
+ }
4036
+ }
4037
+ }
4038
+ const uploadManager = new UploadManager();
4039
+ exports["default"] = uploadManager;
4040
+
4041
+
4042
+ /***/ },
4043
+
4044
+ /***/ "./plugins/file/src/backend/url.ts"
4045
+ (__unused_webpack_module, exports) {
4046
+
4047
+
4048
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
4049
+ exports.checkSafeUrl = checkSafeUrl;
4050
+ function checkSafeUrl(url) {
4051
+ try {
4052
+ const urlObj = new URL(url);
4053
+ if (!["http:", "https:"].includes(urlObj.protocol) || urlObj.username || urlObj.password) {
4054
+ return false;
4055
+ }
4056
+ const hostname = urlObj.hostname.toLowerCase();
4057
+ // Reject IPv6 addresses (IPv6 addresses are wrapped in brackets by URL object)
4058
+ if (hostname.startsWith("[") && hostname.endsWith("]")) {
4059
+ return false;
4060
+ }
4061
+ // Reject IPv4 address format
4062
+ const ipv4Regex = /^(\d{1,3}\.){3}\d{1,3}$/;
4063
+ if (ipv4Regex.test(hostname)) {
4064
+ return false;
4065
+ }
4066
+ // Reject local domains and loopback addresses
4067
+ const localDomains = ["localhost", "127.0.0.1", "0.0.0.0", "::1"];
4068
+ if (localDomains.includes(hostname)) {
4069
+ return false;
4070
+ }
4071
+ // Reject .local domains
4072
+ if (hostname.endsWith(".local")) {
4073
+ return false;
4074
+ }
4075
+ // Must contain at least one dot (ensure it's a valid domain, not a single word)
4076
+ if (!hostname.includes(".")) {
4077
+ return false;
4078
+ }
4079
+ // Domain must have at least a top-level domain (exclude single dot cases)
4080
+ const parts = hostname.split(".");
4081
+ if (parts.length < 2 || parts.some((part) => part.length === 0)) {
4082
+ return false;
4083
+ }
4084
+ return true;
4085
+ }
4086
+ catch (error) {
4087
+ return false;
4088
+ }
4089
+ }
4090
+
4091
+
4092
+ /***/ },
4093
+
4094
+ /***/ "./plugins/file/src/i18n/index.ts"
4095
+ (__unused_webpack_module, exports, __webpack_require__) {
4096
+
4097
+
4098
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4099
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4100
+ };
4101
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
4102
+ exports.localeMessages = void 0;
4103
+ const de_DE_json_1 = __importDefault(__webpack_require__("./plugins/file/src/i18n/de_DE.json"));
4104
+ const en_US_json_1 = __importDefault(__webpack_require__("./plugins/file/src/i18n/en_US.json"));
4105
+ const es_ES_json_1 = __importDefault(__webpack_require__("./plugins/file/src/i18n/es_ES.json"));
4106
+ const fr_FR_json_1 = __importDefault(__webpack_require__("./plugins/file/src/i18n/fr_FR.json"));
4107
+ const ja_JP_json_1 = __importDefault(__webpack_require__("./plugins/file/src/i18n/ja_JP.json"));
4108
+ const ko_KR_json_1 = __importDefault(__webpack_require__("./plugins/file/src/i18n/ko_KR.json"));
4109
+ const pt_BR_json_1 = __importDefault(__webpack_require__("./plugins/file/src/i18n/pt_BR.json"));
4110
+ const ru_RU_json_1 = __importDefault(__webpack_require__("./plugins/file/src/i18n/ru_RU.json"));
4111
+ const th_TH_json_1 = __importDefault(__webpack_require__("./plugins/file/src/i18n/th_TH.json"));
4112
+ const tr_TR_json_1 = __importDefault(__webpack_require__("./plugins/file/src/i18n/tr_TR.json"));
4113
+ const zh_CN_json_1 = __importDefault(__webpack_require__("./plugins/file/src/i18n/zh_CN.json"));
4114
+ const zh_TW_json_1 = __importDefault(__webpack_require__("./plugins/file/src/i18n/zh_TW.json"));
4115
+ // The translated errors the file subsystem throws: path sandbox violations, the
4116
+ // per-instance task limit and the archive size limit. Registered at setup, so the
4117
+ // foundational `plugins/i18n` catalogue carries nothing file-specific.
4118
+ exports.localeMessages = {
4119
+ de_de: de_DE_json_1.default,
4120
+ en_us: en_US_json_1.default,
4121
+ es_es: es_ES_json_1.default,
4122
+ fr_fr: fr_FR_json_1.default,
4123
+ ja_jp: ja_JP_json_1.default,
4124
+ ko_kr: ko_KR_json_1.default,
4125
+ pt_br: pt_BR_json_1.default,
4126
+ ru_ru: ru_RU_json_1.default,
4127
+ th_th: th_TH_json_1.default,
4128
+ tr_tr: tr_TR_json_1.default,
4129
+ zh_cn: zh_CN_json_1.default,
4130
+ zh_tw: zh_TW_json_1.default
4131
+ };
4132
+
4133
+
4134
+ /***/ },
4135
+
4136
+ /***/ "events"
4137
+ (module) {
4138
+
4139
+ module.exports = require("events");
4140
+
4141
+ /***/ },
4142
+
4143
+ /***/ "fs-extra"
4144
+ (module) {
4145
+
4146
+ module.exports = require("fs-extra");
4147
+
4148
+ /***/ },
4149
+
4150
+ /***/ "iconv-lite"
4151
+ (module) {
4152
+
4153
+ module.exports = require("iconv-lite");
4154
+
4155
+ /***/ },
4156
+
4157
+ /***/ "os-utils"
4158
+ (module) {
4159
+
4160
+ module.exports = require("os-utils");
4161
+
4162
+ /***/ },
4163
+
4164
+ /***/ "proper-lockfile"
4165
+ (module) {
4166
+
4167
+ module.exports = require("proper-lockfile");
4168
+
4169
+ /***/ },
4170
+
4171
+ /***/ "uuid"
4172
+ (module) {
4173
+
4174
+ module.exports = require("uuid");
4175
+
4176
+ /***/ },
4177
+
4178
+ /***/ "child_process"
4179
+ (module) {
4180
+
4181
+ module.exports = require("child_process");
4182
+
4183
+ /***/ },
4184
+
4185
+ /***/ "crypto"
4186
+ (module) {
4187
+
4188
+ module.exports = require("crypto");
4189
+
4190
+ /***/ },
4191
+
4192
+ /***/ "fs"
4193
+ (module) {
4194
+
4195
+ module.exports = require("fs");
4196
+
4197
+ /***/ },
4198
+
4199
+ /***/ "module"
4200
+ (module) {
4201
+
4202
+ module.exports = require("module");
4203
+
4204
+ /***/ },
4205
+
4206
+ /***/ "net"
4207
+ (module) {
4208
+
4209
+ module.exports = require("net");
4210
+
4211
+ /***/ },
4212
+
4213
+ /***/ "node:crypto"
4214
+ (module) {
4215
+
4216
+ module.exports = require("node:crypto");
4217
+
4218
+ /***/ },
4219
+
4220
+ /***/ "node:fs"
4221
+ (module) {
4222
+
4223
+ module.exports = require("node:fs");
4224
+
4225
+ /***/ },
4226
+
4227
+ /***/ "node:module"
4228
+ (module) {
4229
+
4230
+ module.exports = require("node:module");
4231
+
4232
+ /***/ },
4233
+
4234
+ /***/ "node:os"
4235
+ (module) {
4236
+
4237
+ module.exports = require("node:os");
4238
+
4239
+ /***/ },
4240
+
4241
+ /***/ "node:path"
4242
+ (module) {
4243
+
4244
+ module.exports = require("node:path");
4245
+
4246
+ /***/ },
4247
+
4248
+ /***/ "node:url"
4249
+ (module) {
4250
+
4251
+ module.exports = require("node:url");
4252
+
4253
+ /***/ },
4254
+
4255
+ /***/ "os"
4256
+ (module) {
4257
+
4258
+ module.exports = require("os");
4259
+
4260
+ /***/ },
4261
+
4262
+ /***/ "path"
4263
+ (module) {
4264
+
4265
+ module.exports = require("path");
4266
+
4267
+ /***/ },
4268
+
4269
+ /***/ "process"
4270
+ (module) {
4271
+
4272
+ module.exports = require("process");
4273
+
4274
+ /***/ },
4275
+
4276
+ /***/ "./plugins/file/src/i18n/de_DE.json"
4277
+ (module) {
4278
+
4279
+ module.exports = /*#__PURE__*/JSON.parse('{"TXT_CODE_file_router.instanceNotExist":"Instanz {{instanceUuid}} existiert nicht","TXT_CODE_file_router.unzipLimit":"Die maximale Anzahl gleichzeitiger Dekomprimierungsaufgaben wurde überschritten. Maximal sind {{maxFileTask}} zulässig; derzeit werden {{fileLock}} Aufgaben ausgeführt.","TXT_CODE_file_router_service.instanceNotExit":"Instanz {{uuid}} existiert nicht","TXT_CODE_system_file.execLimit":"Maximale Dateibearbeitungsgrenze überschritten","TXT_CODE_system_file.illegalAccess":"Falscher Zugriffspfad","TXT_CODE_system_file.unzipLimit":"Die Dateidekomprimierung unterstützt nur die Dekomprimierung von Dateien bis zu {{max}} GB. Wenn Sie die Obergrenze ändern müssen, gehen Sie bitte zur Datei data/Config/global.json.","TXT_CODE_e3a77a77":"Unbegrenzt","TXT_CODE_fde31068":"Upload -Rate für Benutzerdatei","TXT_CODE_d8d19932":"Begrenzung pro HTTP","TXT_CODE_785a0fcf":"Benutzerdatei -Download -Rate","TXT_CODE_b9fc604c":"Limit pro HTTP -Anforderung, Maximale Datei -Download -Geschwindigkeit","TXT_CODE_a15fca22":"Maximale Downloads von URL","TXT_CODE_ecaf78a2":"0 für unbegrenzt"}');
4280
+
4281
+ /***/ },
4282
+
4283
+ /***/ "./plugins/file/src/i18n/en_US.json"
4284
+ (module) {
4285
+
4286
+ module.exports = /*#__PURE__*/JSON.parse('{"TXT_CODE_file_router.instanceNotExist":"Instance {{instanceUuid}} does not exist","TXT_CODE_file_router.unzipLimit":"Exceeded the maximum number of concurrent decompression tasks. The maximum allowed is {{maxFileTask}}, and currently, {{fileLock}} tasks are in progress. Please be patient.","TXT_CODE_file_router_service.instanceNotExit":"Instance {{uuid}} does not exist","TXT_CODE_system_file.execLimit":"Exceeded maximum file editing limit","TXT_CODE_system_file.illegalAccess":"Illegal access path","TXT_CODE_system_file.unzipLimit":"File decompression only supports file up to {{max}}GB. To change this limit, please go to the data/Config/global.json file.","TXT_CODE_e3a77a77":"Unlimited","TXT_CODE_fde31068":"User file upload rate","TXT_CODE_d8d19932":"Limit per HTTP request, maximum file upload speed","TXT_CODE_785a0fcf":"User file download rate","TXT_CODE_b9fc604c":"Limit per HTTP request, maximum file download speed","TXT_CODE_a15fca22":"Maximum of download from URL","TXT_CODE_ecaf78a2":"Set to 0 to unlimited"}');
4287
+
4288
+ /***/ },
4289
+
4290
+ /***/ "./plugins/file/src/i18n/es_ES.json"
4291
+ (module) {
4292
+
4293
+ module.exports = /*#__PURE__*/JSON.parse('{"TXT_CODE_file_router.instanceNotExist":"La instancia {{instanceUuid}} no existe","TXT_CODE_file_router.unzipLimit":"Se ha excedido el número máximo de tareas de descompresión simultáneas. El máximo permitido es {{maxFileTask}}. Actualmente hay {{fileLock}} tareas en progreso.","TXT_CODE_file_router_service.instanceNotExit":"La instancia {{uuid}} no existe","TXT_CODE_system_file.execLimit":"Se superó el límite máximo de edición de archivos","TXT_CODE_system_file.illegalAccess":"Ruta de acceso incorrecta","TXT_CODE_system_file.unzipLimit":"La descompresión de archivos solo admite la descompresión de archivos de hasta {{max}} GB. Si necesita cambiar el límite superior, vaya al archivo data/Config/global.json.","TXT_CODE_e3a77a77":"Ilimitado","TXT_CODE_fde31068":"Tasa de carga de archivo de usuario","TXT_CODE_d8d19932":"Límite por solicitud HTTP, velocidad máxima de carga de archivo","TXT_CODE_785a0fcf":"Tasa de descarga de archivos de usuario","TXT_CODE_b9fc604c":"Límite por solicitud HTTP, velocidad máxima de descarga de archivo","TXT_CODE_a15fca22":"Máximo de descargas desde URL","TXT_CODE_ecaf78a2":"Establecer en 0 para ilimitado"}');
4294
+
4295
+ /***/ },
4296
+
4297
+ /***/ "./plugins/file/src/i18n/fr_FR.json"
4298
+ (module) {
4299
+
4300
+ module.exports = /*#__PURE__*/JSON.parse('{"TXT_CODE_file_router.instanceNotExist":"L\'instance {{instanceUuid}} n\'existe pas","TXT_CODE_file_router.unzipLimit":"Le nombre maximum de tâches de décompression simultanées a été dépassé. Le maximum autorisé est de {{maxFileTask}}. Il y a actuellement {{fileLock}} tâches en cours.","TXT_CODE_file_router_service.instanceNotExit":"L\'instance {{uuid}} n\'existe pas","TXT_CODE_system_file.execLimit":"Limite maximale de modification de fichier dépassée","TXT_CODE_system_file.illegalAccess":"Mauvais chemin d\'accès","TXT_CODE_system_file.unzipLimit":"La décompression de fichiers ne prend en charge que la décompression de fichiers jusqu\'à {{max}} Go. Si vous devez modifier la limite supérieure, veuillez accéder au fichier data/Config/global.json.","TXT_CODE_e3a77a77":"Illimité","TXT_CODE_fde31068":"Taux de téléchargement de fichiers utilisateur","TXT_CODE_d8d19932":"Limite par demande HTTP, vitesse maximale de téléchargement de fichiers","TXT_CODE_785a0fcf":"Taux de téléchargement des fichiers utilisateur","TXT_CODE_b9fc604c":"Limite par demande HTTP, vitesse de téléchargement de fichiers maximum","TXT_CODE_a15fca22":"Maximum de téléchargements depuis l\'URL","TXT_CODE_ecaf78a2":"Mettre 0 pour illimité"}');
4301
+
4302
+ /***/ },
4303
+
4304
+ /***/ "./plugins/file/src/i18n/ja_JP.json"
4305
+ (module) {
4306
+
4307
+ module.exports = /*#__PURE__*/JSON.parse('{"TXT_CODE_file_router.instanceNotExist":"インスタンス {{instanceUuid}} は存在しません","TXT_CODE_file_router.unzipLimit":"同時解凍タスクの最大数は {{maxFileTask}} を超えています。現在進行中のタスクは {{fileLock}} です。しばらくお待ちください。","TXT_CODE_file_router_service.instanceNotExit":"インスタンス {{uuid}} は存在しません","TXT_CODE_system_file.execLimit":"ファイル編集の最大制限を超えました","TXT_CODE_system_file.illegalAccess":"間違ったアクセスパス","TXT_CODE_system_file.unzipLimit":"ファイルの解凍では、最大 {{max}} GB のファイルの解凍のみがサポートされます。上限を変更する必要がある場合は、data/Config/global.json ファイルに移動してください。","TXT_CODE_e3a77a77":"無制限","TXT_CODE_fde31068":"ユーザーファイルのアップロードレート","TXT_CODE_d8d19932":"HTTPリクエストごとに制限、最大ファイルアップロード速度","TXT_CODE_785a0fcf":"ユーザーファイルのダウンロードレート","TXT_CODE_b9fc604c":"HTTP要求ごとに制限、最大ファイルのダウンロード速度","TXT_CODE_a15fca22":"URLからの最大ダウンロード数","TXT_CODE_ecaf78a2":"0で無制限に設定"}');
4308
+
4309
+ /***/ },
4310
+
4311
+ /***/ "./plugins/file/src/i18n/ko_KR.json"
4312
+ (module) {
4313
+
4314
+ module.exports = /*#__PURE__*/JSON.parse('{"TXT_CODE_file_router.instanceNotExist":"{{instanceUuid}} 인스턴스가 존재하지 않습니다.","TXT_CODE_file_router.unzipLimit":"동시 압축 해제 작업의 최대 개수는 {{maxFileTask}}개를 초과했습니다. 현재 진행 중인 작업은 {{fileLock}}개입니다.","TXT_CODE_file_router_service.instanceNotExit":"{{uuid}} 인스턴스가 존재하지 않습니다.","TXT_CODE_system_file.execLimit":"최대 파일 편집 제한을 초과했습니다.","TXT_CODE_system_file.illegalAccess":"잘못된 액세스 경로","TXT_CODE_system_file.unzipLimit":"파일 압축 해제는 최대 {{max}}GB까지의 파일 압축 해제만 지원합니다. 상한을 변경해야 하는 경우 data/Config/global.json 파일로 이동하세요.","TXT_CODE_e3a77a77":"제한 없는","TXT_CODE_fde31068":"사용자 파일 업로드 속도","TXT_CODE_d8d19932":"HTTP 요청 당 제한, 최대 파일 업로드 속도","TXT_CODE_785a0fcf":"사용자 파일 다운로드 속도","TXT_CODE_b9fc604c":"HTTP 요청 당 제한, 최대 파일 다운로드 속도","TXT_CODE_a15fca22":"URL 최대 다운로드 수","TXT_CODE_ecaf78a2":"0으로 설정 시 무제한"}');
4315
+
4316
+ /***/ },
4317
+
4318
+ /***/ "./plugins/file/src/i18n/pt_BR.json"
4319
+ (module) {
4320
+
4321
+ module.exports = /*#__PURE__*/JSON.parse('{"TXT_CODE_file_router.instanceNotExist":"A instância {{instanceUuid}} não existe","TXT_CODE_file_router.unzipLimit":"Excedido o número máximo de tarefas de descompressão simultâneas. O máximo permitido é {{maxFileTask}}, e atualmente, {{fileLock}} tarefas estão em andamento. Por favor, seja paciente.","TXT_CODE_file_router_service.instanceNotExit":"A instância {{uuid}} não existe","TXT_CODE_system_file.execLimit":"Limite máximo de edição de arquivos excedido","TXT_CODE_system_file.illegalAccess":"Caminho de acesso ilegal","TXT_CODE_system_file.unzipLimit":"A descompactação de arquivos suporta apenas arquivos de até {{max}}GB. Para alterar esse limite, vá até o arquivo data/Config/global.json.","TXT_CODE_e3a77a77":"Ilimitado","TXT_CODE_fde31068":"Taxa de upload de arquivos do usuário","TXT_CODE_d8d19932":"Limite por solicitação HTTP, velocidade máxima de upload do arquivo","TXT_CODE_785a0fcf":"Taxa de download de arquivos do usuário","TXT_CODE_b9fc604c":"Limite por solicitação HTTP, velocidade máxima de download de arquivo","TXT_CODE_a15fca22":"Máximo de downloads por URL","TXT_CODE_ecaf78a2":"Defina 0 para ilimitado"}');
4322
+
4323
+ /***/ },
4324
+
4325
+ /***/ "./plugins/file/src/i18n/ru_RU.json"
4326
+ (module) {
4327
+
4328
+ module.exports = /*#__PURE__*/JSON.parse('{"TXT_CODE_file_router.instanceNotExist":"Экземпляр {{instanceUuid}} не существует","TXT_CODE_file_router.unzipLimit":"Превышено максимальное количество одновременных задач разархивирования. Максимально допустимое количество — {{maxFileTask}}, в настоящее время выполняется {{fileLock}} задач. Пожалуйста, будьте терпеливы.","TXT_CODE_file_router_service.instanceNotExit":"Экземпляр {{uuid}} не существует","TXT_CODE_system_file.execLimit":"Превышен лимит редактирования файлов","TXT_CODE_system_file.illegalAccess":"Незаконный путь доступа","TXT_CODE_system_file.unzipLimit":"Распаковка файлов поддерживается только для файлов до {{max}}GB. Чтобы изменить этот лимит, пожалуйста, перейдите в файл data/Config/global.json.","TXT_CODE_e3a77a77":"Неограничено","TXT_CODE_fde31068":"Пользовательская скорость загрузки файлов","TXT_CODE_d8d19932":"Ограничение на HTTP -запрос, максимальная скорость загрузки файлов","TXT_CODE_785a0fcf":"Пользовательская скорость загрузки файлов","TXT_CODE_b9fc604c":"Ограничение на HTTP -запрос, максимальная скорость загрузки файлов","TXT_CODE_a15fca22":"Максимум загрузок по URL","TXT_CODE_ecaf78a2":"0 - без ограничений"}');
4329
+
4330
+ /***/ },
4331
+
4332
+ /***/ "./plugins/file/src/i18n/th_TH.json"
4333
+ (module) {
4334
+
4335
+ module.exports = /*#__PURE__*/JSON.parse('{"TXT_CODE_file_router.instanceNotExist":"ไม่พบอินสแตนซ์ {{instanceUuid}}","TXT_CODE_file_router.unzipLimit":"เกินจำนวนงานคลายการบีบอัดพร้อมกันสูงสุด อนุญาตสูงสุด {{maxFileTask}} งาน และขณะนี้มี {{fileLock}} งานกำลังดำเนินการ กรุณารอสักครู่","TXT_CODE_file_router_service.instanceNotExit":"ไม่พบอินสแตนซ์ {{uuid}}","TXT_CODE_system_file.execLimit":"เกินขีดจำกัดการแก้ไขไฟล์สูงสุด","TXT_CODE_system_file.illegalAccess":"เส้นทางการเข้าถึงไม่ถูกต้อง","TXT_CODE_system_file.unzipLimit":"การแตกไฟล์รองรับไฟล์ขนาดไม่เกิน {{max}}GB หากต้องการเปลี่ยนขีดจำกัดนี้ กรุณาไปที่ไฟล์ data/Config/global.json","TXT_CODE_e3a77a77":"ไม่จำกัด","TXT_CODE_fde31068":"อัตราการอัปโหลดไฟล์ผู้ใช้","TXT_CODE_d8d19932":"ขีด จำกัด ต่อคำขอ HTTP ความเร็วในการอัปโหลดไฟล์สูงสุด","TXT_CODE_785a0fcf":"อัตราการดาวน์โหลดไฟล์ผู้ใช้","TXT_CODE_b9fc604c":"ขีด จำกัด ต่อคำขอ HTTP ความเร็วในการดาวน์โหลดไฟล์สูงสุด","TXT_CODE_a15fca22":"จำนวนสูงสุดของการดาวน์โหลดจาก URL","TXT_CODE_ecaf78a2":"ตั้งเป็น 0 สำหรับไม่จำกัด"}');
4336
+
4337
+ /***/ },
4338
+
4339
+ /***/ "./plugins/file/src/i18n/tr_TR.json"
4340
+ (module) {
4341
+
4342
+ module.exports = /*#__PURE__*/JSON.parse('{"TXT_CODE_file_router.instanceNotExist":"Örnek {{instanceUuid}} mevcut değil","TXT_CODE_file_router.unzipLimit":"Eşzamanlı dekompresyon görevlerinin maksimum sayısı aşıldı. İzin verilen maksimum sayı {{maxFileTask}} ve şu anda {{fileLock}} görevleri devam ediyor. Lütfen sabırlı olun.","TXT_CODE_file_router_service.instanceNotExit":"Örnek {{uuid}} mevcut değil","TXT_CODE_system_file.execLimit":"Maksimum dosya düzenleme sınırı aşıldı","TXT_CODE_system_file.illegalAccess":"Yasadışı erişim yolu","TXT_CODE_system_file.unzipLimit":"Dosya açma yalnızca {{max}}GB\'a kadar olan dosyaları destekler. Bu sınırı değiştirmek için lütfen data/Config/global.json dosyasına gidin.","TXT_CODE_e3a77a77":"Sınırsız","TXT_CODE_fde31068":"Kullanıcı Dosyası Yükleme Oranı","TXT_CODE_d8d19932":"HTTP isteği başına sınır, maksimum dosya yükleme hızı","TXT_CODE_785a0fcf":"Kullanıcı Dosyası İndirme Oranı","TXT_CODE_b9fc604c":"HTTP İsteği Başına Limit, Maksimum Dosya İndirme Hızı","TXT_CODE_a15fca22":"URL\'den maksimum indirme","TXT_CODE_ecaf78a2":"Sınırsız için 0\'a ayarla"}');
4343
+
4344
+ /***/ },
4345
+
4346
+ /***/ "./plugins/file/src/i18n/zh_CN.json"
4347
+ (module) {
4348
+
4349
+ module.exports = /*#__PURE__*/JSON.parse('{"TXT_CODE_file_router.instanceNotExist":"实例 {{instanceUuid}} 不存在","TXT_CODE_file_router.unzipLimit":"超出最大同时解压缩任务量,最大准许{{maxFileTask}}个,目前有{{fileLock}}个任务正在进行,请耐心等待","TXT_CODE_file_router_service.instanceNotExit":"实例 {{uuid}} 不存在","TXT_CODE_system_file.execLimit":"超出最大文件编辑限制","TXT_CODE_system_file.illegalAccess":"非法访问路径","TXT_CODE_system_file.unzipLimit":"文件解压缩只支持最大 {{max}}GB 文件的解压缩,如需改变上限请前往 data/Config/global.json 文件","TXT_CODE_e3a77a77":"无限制","TXT_CODE_fde31068":"用户上传文件速率","TXT_CODE_d8d19932":"限制每个 HTTP 请求,上传文件最大速度","TXT_CODE_785a0fcf":"用户下载文件速率","TXT_CODE_b9fc604c":"限制每个 HTTP 请求,下载文件最大速度","TXT_CODE_a15fca22":"从URL下载文件最大任务数","TXT_CODE_ecaf78a2":"设置为 0 为无限制"}');
4350
+
4351
+ /***/ },
4352
+
4353
+ /***/ "./plugins/file/src/i18n/zh_TW.json"
4354
+ (module) {
4355
+
4356
+ module.exports = /*#__PURE__*/JSON.parse('{"TXT_CODE_file_router.instanceNotExist":"實例 {{instanceUuid}} 不存在","TXT_CODE_file_router.unzipLimit":"超出最大同時解壓縮任務量,最大允許{{maxFileTask}}個,目前有{{fileLock}}個任務正在進行,請耐心等待","TXT_CODE_file_router_service.instanceNotExit":"實例 {{uuid}} 不存在","TXT_CODE_system_file.execLimit":"超出最大檔案編輯限制","TXT_CODE_system_file.illegalAccess":"錯誤存取路徑","TXT_CODE_system_file.unzipLimit":"檔案解壓縮只支援最大 {{max}}GB 檔案的解壓縮,如需改變上限請前往 data/Config/global.json 檔案","TXT_CODE_e3a77a77":"無限制","TXT_CODE_fde31068":"使用者上傳檔案速率","TXT_CODE_d8d19932":"限制每個 HTTP 請求,上傳檔案最大速度","TXT_CODE_785a0fcf":"使用者下載檔案速率","TXT_CODE_b9fc604c":"限制每個 HTTP 請求,下載檔案最大速度","TXT_CODE_a15fca22":"網址下載上限","TXT_CODE_ecaf78a2":"設為 0 表示無限制"}');
4357
+
4358
+ /***/ }
4359
+
4360
+ /******/ });
4361
+ /************************************************************************/
4362
+ /******/ // The module cache
4363
+ /******/ var __webpack_module_cache__ = {};
4364
+ /******/
4365
+ /******/ // The require function
4366
+ /******/ function __webpack_require__(moduleId) {
4367
+ /******/ // Check if module is in cache
4368
+ /******/ var cachedModule = __webpack_module_cache__[moduleId];
4369
+ /******/ if (cachedModule !== undefined) {
4370
+ /******/ return cachedModule.exports;
4371
+ /******/ }
4372
+ /******/ // Create a new module (and put it into the cache)
4373
+ /******/ var module = __webpack_module_cache__[moduleId] = {
4374
+ /******/ // no module.id needed
4375
+ /******/ // no module.loaded needed
4376
+ /******/ exports: {}
4377
+ /******/ };
4378
+ /******/
4379
+ /******/ // Execute the module function
4380
+ /******/ __webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__);
4381
+ /******/
4382
+ /******/ // Return the exports of the module
4383
+ /******/ return module.exports;
4384
+ /******/ }
4385
+ /******/
4386
+ /************************************************************************/
4387
+ /******/
4388
+ /******/ // startup
4389
+ /******/ // Load entry module and return exports
4390
+ /******/ // This entry module is referenced by other modules so it can't be inlined
4391
+ /******/ var __webpack_exports__ = __webpack_require__("./plugins/file/src/backend/index.ts");
4392
+ /******/ module.exports = __webpack_exports__;
4393
+ /******/
4394
+ /******/ })()
4395
+ ;