@le-space/playwright 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/index.d.ts +18 -0
  2. package/index.js +12 -5
  3. package/package.json +3 -3
package/index.d.ts CHANGED
@@ -95,6 +95,17 @@ interface AlephRunnerJanitorSelection {
95
95
  retained: Array<AlephRunnerInstanceCandidate & {
96
96
  reason: string;
97
97
  }>;
98
+ /**
99
+ * Instances the janitor may not touch — their name matches none of the
100
+ * configured prefixes — that are nevertheless past the TTL and still
101
+ * allocated. These are the invisible credit leaks: two `simple-todo-e2e-*`
102
+ * VMs burned ~811k credits over 38 h while seven consecutive janitor runs
103
+ * reported success, because "name I do not recognise" was indistinguishable
104
+ * from "nothing to do" (NiKrause/simple-todo#88).
105
+ */
106
+ unswept: Array<AlephRunnerInstanceCandidate & {
107
+ ageMs: number;
108
+ }>;
98
109
  }
99
110
  interface RelayAddressPolicy {
100
111
  allowWebTransport?: boolean;
@@ -206,6 +217,13 @@ declare function selectExpiredAlephPlaywrightRunners(options: {
206
217
  repository: string;
207
218
  now?: number;
208
219
  ttlMs?: number;
220
+ /**
221
+ * Extra exact name prefixes to sweep beyond `playwright-<repository>-`.
222
+ * Ephemeral VMs that a repository names by its own scheme (simple-todo's
223
+ * relay runners are `simple-todo-e2e-<timestamp>`) never matched the
224
+ * repository prefix and so leaked forever (#88).
225
+ */
226
+ additionalNamePrefixes?: readonly string[];
209
227
  }): AlephRunnerJanitorSelection;
210
228
  declare function waitForPubsubSubscriber(page: Page, options: WaitForPubsubSubscriberOptions): Promise<string[]>;
211
229
  /**
package/index.js CHANGED
@@ -156,22 +156,29 @@ function selectExpiredAlephPlaywrightRunners(options) {
156
156
  const repository = options.repository.trim().toLowerCase().replace(/[^a-z0-9]+/gu, "-");
157
157
  if (!/^0x[a-f0-9]{40}$/u.test(owner)) throw new Error("Janitor requires an exact EVM owner address");
158
158
  if (!repository) throw new Error("Janitor requires a repository name");
159
- const prefix = `playwright-${repository}-`;
159
+ const prefixes = [
160
+ `playwright-${repository}-`,
161
+ ...(options.additionalNamePrefixes ?? []).map((value) => value.trim().toLowerCase()).filter(Boolean)
162
+ ];
160
163
  const now = options.now ?? Date.now();
161
164
  const ttlMs = options.ttlMs ?? 60 * 6e4;
162
165
  if (!Number.isFinite(ttlMs) || ttlMs < 15 * 6e4) throw new Error("Janitor TTL must be at least 15 minutes");
163
- const selection = { expired: [], retained: [] };
166
+ const selection = { expired: [], retained: [], unswept: [] };
164
167
  for (const candidate of options.candidates) {
165
168
  let reason = "";
166
169
  const createdAt = Date.parse(candidate.createdAt);
167
170
  if (!/^[a-f0-9]{64}$/iu.test(candidate.itemHash)) reason = "invalid exact INSTANCE hash";
168
171
  else if (candidate.ownerAddress.toLowerCase() !== owner) reason = "different owner";
169
- else if (!candidate.instanceName.toLowerCase().startsWith(prefix)) reason = "name outside repository scope";
172
+ else if (!prefixes.some((prefix) => candidate.instanceName.toLowerCase().startsWith(prefix)))
173
+ reason = "name outside repository scope";
170
174
  else if (!Number.isFinite(createdAt)) reason = "invalid creation timestamp";
171
175
  else if (now - createdAt < ttlMs) reason = "within TTL";
172
176
  else if (!["processed", "pending"].includes(candidate.status.toLowerCase())) reason = `terminal status ${candidate.status}`;
173
- if (reason) selection.retained.push({ ...candidate, reason });
174
- else selection.expired.push(candidate);
177
+ if (reason) {
178
+ selection.retained.push({ ...candidate, reason });
179
+ if (reason === "name outside repository scope" && Number.isFinite(createdAt) && now - createdAt >= ttlMs && ["processed", "pending"].includes(candidate.status.toLowerCase()))
180
+ selection.unswept.push({ ...candidate, ageMs: now - createdAt });
181
+ } else selection.expired.push(candidate);
175
182
  }
176
183
  return selection;
177
184
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@le-space/playwright",
3
- "version": "0.7.0",
3
+ "version": "0.9.0",
4
4
  "description": "Reusable Playwright fixtures and Relay Button lifecycle helpers.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -27,8 +27,8 @@
27
27
  "url": "https://github.com/NiKrause/relay-button/issues"
28
28
  },
29
29
  "dependencies": {
30
- "@le-space/aleph-bootstrap": "0.7.0",
31
- "@le-space/core": "0.7.0"
30
+ "@le-space/aleph-bootstrap": "0.9.0",
31
+ "@le-space/core": "0.9.0"
32
32
  },
33
33
  "peerDependencies": {
34
34
  "@playwright/test": ">=1.61.1 <1.62.0"