altd 0.0.6 → 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/altd.js CHANGED
@@ -18,6 +18,14 @@ export default class AccessLogTailDispatcher {
18
18
  ignoreInitial: true,
19
19
  persistent: true,
20
20
  });
21
+ this.maxConcurrent = opts.maxConcurrent ?? Infinity;
22
+ this.minIntervalMs = opts.minIntervalMs ?? 0;
23
+ this.maxParts = opts.maxParts ?? 32;
24
+ this.maxPartLength = opts.maxPartLength ?? 200;
25
+ this.maxArgLength = opts.maxArgLength ?? this.maxPartLength;
26
+ this.maxPathLength = opts.maxPathLength ?? 2048;
27
+ this.activeCount = 0;
28
+ this.lastExecAt = Number.NEGATIVE_INFINITY;
21
29
  }
22
30
 
23
31
  /**
@@ -37,15 +45,17 @@ export default class AccessLogTailDispatcher {
37
45
 
38
46
  const rawTarget = m[2];
39
47
 
40
- if (rawTarget.startsWith("http://") || rawTarget.startsWith("https://")) {
41
- try {
42
- return new URL(rawTarget).pathname || "";
43
- } catch {
44
- return "";
45
- }
48
+ try {
49
+ const base = rawTarget.startsWith("http://")
50
+ || rawTarget.startsWith("https://")
51
+ ? undefined
52
+ : "http://localhost";
53
+ const url = base ? new URL(rawTarget, base) : new URL(rawTarget);
54
+ if (!url.pathname || url.pathname.length > this.maxPathLength) return "";
55
+ return url.pathname;
56
+ } catch {
57
+ return "";
46
58
  }
47
-
48
- return rawTarget;
49
59
  }
50
60
 
51
61
  /**
@@ -59,11 +69,15 @@ export default class AccessLogTailDispatcher {
59
69
 
60
70
  const parts = pathname.split("/").filter(Boolean);
61
71
  if (parts.length === 0) return [];
72
+ if (parts.length > this.maxParts) return [];
62
73
 
63
74
  const decoded = [];
64
75
  for (const p of parts) {
76
+ if (p.length > this.maxPartLength) return [];
65
77
  try {
66
- decoded.push(decodeURIComponent(p));
78
+ const value = decodeURIComponent(p);
79
+ if (value.length > this.maxPartLength) return [];
80
+ decoded.push(value);
67
81
  } catch {
68
82
  return [];
69
83
  }
@@ -87,20 +101,33 @@ export default class AccessLogTailDispatcher {
87
101
  const buildArgs = entry.buildArgs ?? ((args) => args);
88
102
  const args = buildArgs(rawArgs);
89
103
  if (!Array.isArray(args)) return null;
104
+ if (args.some((arg) => arg.length > this.maxArgLength)) return null;
90
105
 
91
106
  return { execPath: entry.execPath, args };
92
107
  }
93
108
 
94
109
  spawnCommand(execPath, args) {
110
+ if (this.activeCount >= this.maxConcurrent) return;
111
+ const now = Date.now();
112
+ if (now - this.lastExecAt < this.minIntervalMs) return;
113
+
95
114
  const proc = this.spawnImpl(execPath, args, {
96
115
  shell: false,
97
116
  windowsHide: true,
98
117
  stdio: "inherit",
99
118
  });
119
+ this.activeCount += 1;
120
+ this.lastExecAt = now;
100
121
 
101
122
  proc.on("error", (err) => {
102
123
  console.error("[spawn error]", err);
103
124
  });
125
+ proc.on("close", () => {
126
+ this.activeCount = Math.max(0, this.activeCount - 1);
127
+ });
128
+ proc.on("exit", () => {
129
+ this.activeCount = Math.max(0, this.activeCount - 1);
130
+ });
104
131
  }
105
132
 
106
133
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "altd",
3
- "version": "0.0.6",
3
+ "version": "0.0.7",
4
4
  "description": "Access log tail dispatcher",
5
5
  "type": "module",
6
6
  "bin": {
package/src/altd.js CHANGED
@@ -18,6 +18,14 @@ export default class AccessLogTailDispatcher {
18
18
  ignoreInitial: true,
19
19
  persistent: true,
20
20
  });
21
+ this.maxConcurrent = opts.maxConcurrent ?? Infinity;
22
+ this.minIntervalMs = opts.minIntervalMs ?? 0;
23
+ this.maxParts = opts.maxParts ?? 32;
24
+ this.maxPartLength = opts.maxPartLength ?? 200;
25
+ this.maxArgLength = opts.maxArgLength ?? this.maxPartLength;
26
+ this.maxPathLength = opts.maxPathLength ?? 2048;
27
+ this.activeCount = 0;
28
+ this.lastExecAt = Number.NEGATIVE_INFINITY;
21
29
  }
22
30
 
23
31
  /**
@@ -37,15 +45,17 @@ export default class AccessLogTailDispatcher {
37
45
 
38
46
  const rawTarget = m[2];
39
47
 
40
- if (rawTarget.startsWith("http://") || rawTarget.startsWith("https://")) {
41
- try {
42
- return new URL(rawTarget).pathname || "";
43
- } catch {
44
- return "";
45
- }
48
+ try {
49
+ const base = rawTarget.startsWith("http://")
50
+ || rawTarget.startsWith("https://")
51
+ ? undefined
52
+ : "http://localhost";
53
+ const url = base ? new URL(rawTarget, base) : new URL(rawTarget);
54
+ if (!url.pathname || url.pathname.length > this.maxPathLength) return "";
55
+ return url.pathname;
56
+ } catch {
57
+ return "";
46
58
  }
47
-
48
- return rawTarget;
49
59
  }
50
60
 
51
61
  /**
@@ -59,11 +69,15 @@ export default class AccessLogTailDispatcher {
59
69
 
60
70
  const parts = pathname.split("/").filter(Boolean);
61
71
  if (parts.length === 0) return [];
72
+ if (parts.length > this.maxParts) return [];
62
73
 
63
74
  const decoded = [];
64
75
  for (const p of parts) {
76
+ if (p.length > this.maxPartLength) return [];
65
77
  try {
66
- decoded.push(decodeURIComponent(p));
78
+ const value = decodeURIComponent(p);
79
+ if (value.length > this.maxPartLength) return [];
80
+ decoded.push(value);
67
81
  } catch {
68
82
  return [];
69
83
  }
@@ -87,20 +101,33 @@ export default class AccessLogTailDispatcher {
87
101
  const buildArgs = entry.buildArgs ?? ((args) => args);
88
102
  const args = buildArgs(rawArgs);
89
103
  if (!Array.isArray(args)) return null;
104
+ if (args.some((arg) => arg.length > this.maxArgLength)) return null;
90
105
 
91
106
  return { execPath: entry.execPath, args };
92
107
  }
93
108
 
94
109
  spawnCommand(execPath, args) {
110
+ if (this.activeCount >= this.maxConcurrent) return;
111
+ const now = Date.now();
112
+ if (now - this.lastExecAt < this.minIntervalMs) return;
113
+
95
114
  const proc = this.spawnImpl(execPath, args, {
96
115
  shell: false,
97
116
  windowsHide: true,
98
117
  stdio: "inherit",
99
118
  });
119
+ this.activeCount += 1;
120
+ this.lastExecAt = now;
100
121
 
101
122
  proc.on("error", (err) => {
102
123
  console.error("[spawn error]", err);
103
124
  });
125
+ proc.on("close", () => {
126
+ this.activeCount = Math.max(0, this.activeCount - 1);
127
+ });
128
+ proc.on("exit", () => {
129
+ this.activeCount = Math.max(0, this.activeCount - 1);
130
+ });
104
131
  }
105
132
 
106
133
  /**