@empiricalrun/playwright-utils 0.45.2 → 0.45.3

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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @empiricalrun/playwright-utils
2
2
 
3
+ ## 0.45.3
4
+
5
+ ### Patch Changes
6
+
7
+ - 7078a69: fix: attachment clean up from disk using lru
8
+
3
9
  ## 0.45.2
4
10
 
5
11
  ### Patch Changes
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Tracks uploaded attachments and evicts the oldest ones from disk
3
+ * when total size exceeds the configured limit.
4
+ *
5
+ * Why this exists:
6
+ * Test attachments (videos, traces, screenshots) accumulate on disk during a
7
+ * test run. On Fargate workers with 50 GB ephemeral storage, large test suites
8
+ * can exhaust disk space. Since attachments are already uploaded to R2, we can
9
+ * safely delete them from disk — but we can't delete them immediately because
10
+ * Playwright's blob reporter reads them during onEnd to embed as resources/.
11
+ *
12
+ * Instead, we use an LRU approach: only evict the oldest files when total size
13
+ * exceeds the limit. The blob reporter's statSync guard (Playwright 1.57+)
14
+ * gracefully skips missing files, and patchBlobZip replaces resources/ with
15
+ * _empirical_urls.json pointing to R2 URLs anyway.
16
+ *
17
+ * Future: when the incremental blob reporter is mature enough to replace the
18
+ * standard blob reporter for all runs (not just spot instances), this class
19
+ * can be removed — the incremental reporter never embeds attachments as resources.
20
+ */
21
+ export declare class AttachmentCleanup {
22
+ private _entries;
23
+ private _totalSize;
24
+ private _limitBytes;
25
+ constructor(limitBytes?: number);
26
+ track(attachment: {
27
+ path?: string | undefined;
28
+ }): void;
29
+ private _evict;
30
+ }
31
+ //# sourceMappingURL=attachment-cleanup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attachment-cleanup.d.ts","sourceRoot":"","sources":["../../src/reporter/attachment-cleanup.ts"],"names":[],"mappings":"AAKA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAwC;IACxD,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,WAAW,CAAS;gBAEhB,UAAU,GAAE,MAAiC;IAIzD,KAAK,CAAC,UAAU,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE;IAc/C,OAAO,CAAC,MAAM;CAcf"}
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.AttachmentCleanup = void 0;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const logger_1 = require("../logger");
9
+ const DEFAULT_DISK_LIMIT_BYTES = 10 * 1024 * 1024 * 1024; // 10 GB
10
+ /**
11
+ * Tracks uploaded attachments and evicts the oldest ones from disk
12
+ * when total size exceeds the configured limit.
13
+ *
14
+ * Why this exists:
15
+ * Test attachments (videos, traces, screenshots) accumulate on disk during a
16
+ * test run. On Fargate workers with 50 GB ephemeral storage, large test suites
17
+ * can exhaust disk space. Since attachments are already uploaded to R2, we can
18
+ * safely delete them from disk — but we can't delete them immediately because
19
+ * Playwright's blob reporter reads them during onEnd to embed as resources/.
20
+ *
21
+ * Instead, we use an LRU approach: only evict the oldest files when total size
22
+ * exceeds the limit. The blob reporter's statSync guard (Playwright 1.57+)
23
+ * gracefully skips missing files, and patchBlobZip replaces resources/ with
24
+ * _empirical_urls.json pointing to R2 URLs anyway.
25
+ *
26
+ * Future: when the incremental blob reporter is mature enough to replace the
27
+ * standard blob reporter for all runs (not just spot instances), this class
28
+ * can be removed — the incremental reporter never embeds attachments as resources.
29
+ */
30
+ class AttachmentCleanup {
31
+ _entries = [];
32
+ _totalSize = 0;
33
+ _limitBytes;
34
+ constructor(limitBytes = DEFAULT_DISK_LIMIT_BYTES) {
35
+ this._limitBytes = limitBytes;
36
+ }
37
+ track(attachment) {
38
+ try {
39
+ if (attachment.path) {
40
+ const stat = fs_1.default.statSync(attachment.path);
41
+ const sizeBytes = stat.size;
42
+ this._entries.push({ path: attachment.path, size: sizeBytes });
43
+ this._totalSize += sizeBytes;
44
+ this._evict();
45
+ }
46
+ }
47
+ catch {
48
+ // Ignore errors
49
+ }
50
+ }
51
+ _evict() {
52
+ while (this._totalSize > this._limitBytes && this._entries.length > 0) {
53
+ const oldest = this._entries.shift();
54
+ try {
55
+ fs_1.default.unlinkSync(oldest.path);
56
+ logger_1.logger.debug(`[AttachmentCleanup] Evicted ${oldest.path} (${oldest.size} bytes)`);
57
+ }
58
+ catch {
59
+ // File may already be gone
60
+ }
61
+ this._totalSize -= oldest.size;
62
+ }
63
+ }
64
+ }
65
+ exports.AttachmentCleanup = AttachmentCleanup;
@@ -7,6 +7,7 @@ declare class EmpiricalReporter implements Reporter {
7
7
  private _attachmentUrlMap;
8
8
  private _testRetryCounts;
9
9
  private _uploader;
10
+ private _attachmentCleanup;
10
11
  constructor();
11
12
  private enqueTestAttachmentUploadTask;
12
13
  onTestEnd(test: TestCase, result: TestResult): void;
@@ -1 +1 @@
1
- {"version":3,"file":"empirical-reporter.d.ts","sourceRoot":"","sources":["../../src/reporter/empirical-reporter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,UAAU,EACV,QAAQ,EACR,QAAQ,EACR,UAAU,EACX,MAAM,2BAA2B,CAAC;AAsBnC,cAAM,iBAAkB,YAAW,QAAQ;IACzC,OAAO,CAAC,kBAAkB,CAAyB;IACnD,OAAO,CAAC,oBAAoB,CAG1B;IACF,OAAO,CAAC,sBAAsB,CAAuB;IACrD,OAAO,CAAC,YAAY,CAAgD;IACpE,OAAO,CAAC,iBAAiB,CAAyC;IAClE,OAAO,CAAC,gBAAgB,CAAkC;IAC1D,OAAO,CAAC,SAAS,CAAyB;;IAM1C,OAAO,CAAC,6BAA6B,CAkCnC;IAEF,SAAS,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU;IAwGtC,KAAK,CAAC,MAAM,EAAE,UAAU;IAkG9B,OAAO,CAAC,qBAAqB;YAkBf,gBAAgB;YAOhB,iBAAiB;CAmChC;AAED,eAAe,iBAAiB,CAAC"}
1
+ {"version":3,"file":"empirical-reporter.d.ts","sourceRoot":"","sources":["../../src/reporter/empirical-reporter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,UAAU,EACV,QAAQ,EACR,QAAQ,EACR,UAAU,EACX,MAAM,2BAA2B,CAAC;AAuBnC,cAAM,iBAAkB,YAAW,QAAQ;IACzC,OAAO,CAAC,kBAAkB,CAAyB;IACnD,OAAO,CAAC,oBAAoB,CAG1B;IACF,OAAO,CAAC,sBAAsB,CAAuB;IACrD,OAAO,CAAC,YAAY,CAAgD;IACpE,OAAO,CAAC,iBAAiB,CAAyC;IAClE,OAAO,CAAC,gBAAgB,CAAkC;IAC1D,OAAO,CAAC,SAAS,CAAyB;IAC1C,OAAO,CAAC,kBAAkB,CAA2B;;IAMrD,OAAO,CAAC,6BAA6B,CAmCnC;IAEF,SAAS,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU;IAwGtC,KAAK,CAAC,MAAM,EAAE,UAAU;IAkG9B,OAAO,CAAC,qBAAqB;YAkBf,gBAAgB;YAOhB,iBAAiB;CAmChC;AAED,eAAe,iBAAiB,CAAC"}
@@ -7,6 +7,7 @@ const fs_1 = __importDefault(require("fs"));
7
7
  const path_1 = __importDefault(require("path"));
8
8
  const logger_1 = require("../logger");
9
9
  const telemetry_1 = require("../telemetry");
10
+ const attachment_cleanup_1 = require("./attachment-cleanup");
10
11
  const blob_utils_1 = require("./blob-utils");
11
12
  const failing_line_1 = require("./failing-line");
12
13
  const incremental_blob_reporter_1 = require("./incremental-blob-reporter");
@@ -21,6 +22,7 @@ class EmpiricalReporter {
21
22
  _attachmentUrlMap = new Map();
22
23
  _testRetryCounts = new Map();
23
24
  _uploader = null;
25
+ _attachmentCleanup = new attachment_cleanup_1.AttachmentCleanup();
24
26
  constructor() {
25
27
  this._uploader = (0, uploader_1.getUploader)();
26
28
  }
@@ -40,6 +42,7 @@ class EmpiricalReporter {
40
42
  const destinationPath = `data/${relativePath}`;
41
43
  const publicUrl = await this._uploader.uploadFile(attachment.path, destinationPath);
42
44
  if (publicUrl) {
45
+ this._attachmentCleanup.track(attachment);
43
46
  return { [attachment.path]: publicUrl };
44
47
  }
45
48
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@empiricalrun/playwright-utils",
3
- "version": "0.45.2",
3
+ "version": "0.45.3",
4
4
  "publishConfig": {
5
5
  "registry": "https://registry.npmjs.org/",
6
6
  "access": "public"
@@ -1 +1 @@
1
- {"root":["./src/email.ts","./src/index.ts","./src/kv.ts","./src/logger.ts","./src/mailosaur-client.ts","./src/playwright-extensions.ts","./src/postgres.ts","./src/telemetry.ts","./src/webhook.ts","./src/auth/google.ts","./src/auth/index.ts","./src/auth/types.ts","./src/captcha/index.ts","./src/config/index.ts","./src/config/proxy.ts","./src/config/devices/types.ts","./src/overlay-tests/cache.spec.ts","./src/overlay-tests/click.spec.ts","./src/overlay-tests/fixtures.ts","./src/overlay-tests/patch.spec.ts","./src/reporter/blob-utils.ts","./src/reporter/empirical-reporter.ts","./src/reporter/failing-line.ts","./src/reporter/harness.ts","./src/reporter/incremental-blob-reporter.ts","./src/reporter/local-test.ts","./src/reporter/reporter-state.ts","./src/reporter/uploader.ts","./src/reporter/util.ts","./src/test/constants.ts","./src/test/coverage.ts","./src/test/index.ts","./src/test/types.ts","./src/test/video-labels.ts","./src/test/expect/index.ts","./src/test/expect/types.ts","./src/test/expect/visual.ts","./src/test/expect/webhook.ts","./src/test/scripts/agent-capabilities.ts","./src/test/scripts/index.ts","./src/test/scripts/locator-highlights.ts","./src/test/scripts/locator-vision.ts","./src/test/scripts/mouse-pointer.ts","./src/test/scripts/types.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/cache.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/index.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/prompt.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/types.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/utils.ts","./src/test/scripts/pw-locator-patch/highlight/click.ts","./src/test/scripts/pw-locator-patch/highlight/expect.ts","./src/test/scripts/pw-locator-patch/highlight/hover.ts","./src/test/scripts/pw-locator-patch/highlight/inner-text.ts","./src/test/scripts/pw-locator-patch/highlight/input-value.ts","./src/test/scripts/pw-locator-patch/highlight/is-checked.ts","./src/test/scripts/pw-locator-patch/highlight/is-disabled.ts","./src/test/scripts/pw-locator-patch/highlight/is-editable.ts","./src/test/scripts/pw-locator-patch/highlight/text-content.ts","./src/test/scripts/pw-locator-patch/utils/index.ts","./src/test/scripts/pw-locator-patch/vision/query.ts"],"version":"5.8.3"}
1
+ {"root":["./src/email.ts","./src/index.ts","./src/kv.ts","./src/logger.ts","./src/mailosaur-client.ts","./src/playwright-extensions.ts","./src/postgres.ts","./src/telemetry.ts","./src/webhook.ts","./src/auth/google.ts","./src/auth/index.ts","./src/auth/types.ts","./src/captcha/index.ts","./src/config/index.ts","./src/config/proxy.ts","./src/config/devices/types.ts","./src/overlay-tests/cache.spec.ts","./src/overlay-tests/click.spec.ts","./src/overlay-tests/fixtures.ts","./src/overlay-tests/patch.spec.ts","./src/reporter/attachment-cleanup.ts","./src/reporter/blob-utils.ts","./src/reporter/empirical-reporter.ts","./src/reporter/failing-line.ts","./src/reporter/harness.ts","./src/reporter/incremental-blob-reporter.ts","./src/reporter/local-test.ts","./src/reporter/reporter-state.ts","./src/reporter/uploader.ts","./src/reporter/util.ts","./src/test/constants.ts","./src/test/coverage.ts","./src/test/index.ts","./src/test/types.ts","./src/test/video-labels.ts","./src/test/expect/index.ts","./src/test/expect/types.ts","./src/test/expect/visual.ts","./src/test/expect/webhook.ts","./src/test/scripts/agent-capabilities.ts","./src/test/scripts/index.ts","./src/test/scripts/locator-highlights.ts","./src/test/scripts/locator-vision.ts","./src/test/scripts/mouse-pointer.ts","./src/test/scripts/types.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/cache.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/index.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/prompt.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/types.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/utils.ts","./src/test/scripts/pw-locator-patch/highlight/click.ts","./src/test/scripts/pw-locator-patch/highlight/expect.ts","./src/test/scripts/pw-locator-patch/highlight/hover.ts","./src/test/scripts/pw-locator-patch/highlight/inner-text.ts","./src/test/scripts/pw-locator-patch/highlight/input-value.ts","./src/test/scripts/pw-locator-patch/highlight/is-checked.ts","./src/test/scripts/pw-locator-patch/highlight/is-disabled.ts","./src/test/scripts/pw-locator-patch/highlight/is-editable.ts","./src/test/scripts/pw-locator-patch/highlight/text-content.ts","./src/test/scripts/pw-locator-patch/utils/index.ts","./src/test/scripts/pw-locator-patch/vision/query.ts"],"version":"5.8.3"}