donobu 5.36.3 → 5.36.5

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.
@@ -342,7 +342,7 @@ exports.test = test_1.test.extend({
342
342
  return;
343
343
  }
344
344
  if (initialActive === 0) {
345
- Logger_1.appLogger.info('donobuFileUploadDrainGuard: no pending file uploads at end of test ' +
345
+ Logger_1.appLogger.debug('donobuFileUploadDrainGuard: no pending file uploads at end of test ' +
346
346
  'session; worker is exiting.');
347
347
  return;
348
348
  }
@@ -19,9 +19,17 @@ export declare class SuitesManager {
19
19
  /**
20
20
  * Delete a suite from all persistence layers.
21
21
  *
22
- * After deleting, orphans any tests that belong to this suite by setting
23
- * their suiteId to null. This mirrors the DB-level ON DELETE SET NULL
24
- * behavior for non-DB persistence layers (Volatile, S3, GCS).
22
+ * Orphans tests that belong to the suite first, then deletes the suite
23
+ * row. The orphan pass must run before the suite delete: SQLite has an
24
+ * ON DELETE SET NULL FK on test_metadata.suite_id, which clears the
25
+ * column synchronously inside DELETE FROM suite_metadata. If we deleted
26
+ * first, the subsequent getTests({ suiteId }) filter would find zero
27
+ * rows in the SQLite layer (column already null) and skip the JSON-blob
28
+ * rewrite, leaving each affected test's `metadata.suiteId` pointed at a
29
+ * suite that no longer exists. Orphaning first rewrites both the SQL
30
+ * column and the JSON blob via updateTest, so the FK cascade is then a
31
+ * no-op. Non-DB layers (Volatile, S3, GCS) have no FK and rely on this
32
+ * pass for the cascade behavior in either ordering.
25
33
  */
26
34
  deleteSuite(suiteId: string): Promise<void>;
27
35
  }
@@ -103,27 +103,32 @@ class SuitesManager {
103
103
  /**
104
104
  * Delete a suite from all persistence layers.
105
105
  *
106
- * After deleting, orphans any tests that belong to this suite by setting
107
- * their suiteId to null. This mirrors the DB-level ON DELETE SET NULL
108
- * behavior for non-DB persistence layers (Volatile, S3, GCS).
106
+ * Orphans tests that belong to the suite first, then deletes the suite
107
+ * row. The orphan pass must run before the suite delete: SQLite has an
108
+ * ON DELETE SET NULL FK on test_metadata.suite_id, which clears the
109
+ * column synchronously inside DELETE FROM suite_metadata. If we deleted
110
+ * first, the subsequent getTests({ suiteId }) filter would find zero
111
+ * rows in the SQLite layer (column already null) and skip the JSON-blob
112
+ * rewrite, leaving each affected test's `metadata.suiteId` pointed at a
113
+ * suite that no longer exists. Orphaning first rewrites both the SQL
114
+ * column and the JSON blob via updateTest, so the FK cascade is then a
115
+ * no-op. Non-DB layers (Volatile, S3, GCS) have no FK and rely on this
116
+ * pass for the cascade behavior in either ordering.
109
117
  */
110
118
  async deleteSuite(suiteId) {
111
119
  for (const { key, persistence, } of await this.suitesPersistenceRegistry.getEntries()) {
112
120
  try {
113
- await persistence.deleteSuite(suiteId);
114
- // Orphan tests in this layer after successfully deleting the suite.
115
- // This mirrors the DB-level ON DELETE SET NULL for non-DB layers.
116
121
  // Pair by key — the suites and tests registries can have different
117
122
  // sets of layers (e.g. plugin-only suites persistence) so positional
118
123
  // indexing isn't safe.
119
124
  const testsPersistence = await this.testsPersistenceRegistry.getByKey(key);
120
- if (!testsPersistence) {
121
- continue;
122
- }
123
- const testsResult = await testsPersistence.getTests({ suiteId });
124
- for (const test of testsResult.items) {
125
- await testsPersistence.updateTest({ ...test, suiteId: null });
125
+ if (testsPersistence) {
126
+ const testsResult = await testsPersistence.getTests({ suiteId });
127
+ for (const test of testsResult.items) {
128
+ await testsPersistence.updateTest({ ...test, suiteId: null });
129
+ }
126
130
  }
131
+ await persistence.deleteSuite(suiteId);
127
132
  }
128
133
  catch {
129
134
  // Ignore errors from layers that don't have this suite.
@@ -13,6 +13,7 @@ exports.renderHtml = renderHtml;
13
13
  const fs_1 = require("fs");
14
14
  const path_1 = require("path");
15
15
  const ansi_1 = require("../utils/ansi");
16
+ const MiscUtils_1 = require("../utils/MiscUtils");
16
17
  const reportWalk_1 = require("./reportWalk");
17
18
  // ---------------------------------------------------------------------------
18
19
  // Helpers
@@ -1994,7 +1995,7 @@ details.ai-invocation[open]>summary .native-step-chevron{transform:rotate(90deg)
1994
1995
 
1995
1996
  <div class="report-footer">
1996
1997
  <div class="footer-brand"><span class="logo">${LOGO_SVG}</span> Donobu</div>
1997
- <span>Report Generated By Donobu</span>
1998
+ <span>Report Generated By Donobu v${esc(MiscUtils_1.MiscUtils.DONOBU_VERSION)}</span>
1998
1999
  </div>
1999
2000
  </div>
2000
2001
 
@@ -342,7 +342,7 @@ exports.test = test_1.test.extend({
342
342
  return;
343
343
  }
344
344
  if (initialActive === 0) {
345
- Logger_1.appLogger.info('donobuFileUploadDrainGuard: no pending file uploads at end of test ' +
345
+ Logger_1.appLogger.debug('donobuFileUploadDrainGuard: no pending file uploads at end of test ' +
346
346
  'session; worker is exiting.');
347
347
  return;
348
348
  }
@@ -19,9 +19,17 @@ export declare class SuitesManager {
19
19
  /**
20
20
  * Delete a suite from all persistence layers.
21
21
  *
22
- * After deleting, orphans any tests that belong to this suite by setting
23
- * their suiteId to null. This mirrors the DB-level ON DELETE SET NULL
24
- * behavior for non-DB persistence layers (Volatile, S3, GCS).
22
+ * Orphans tests that belong to the suite first, then deletes the suite
23
+ * row. The orphan pass must run before the suite delete: SQLite has an
24
+ * ON DELETE SET NULL FK on test_metadata.suite_id, which clears the
25
+ * column synchronously inside DELETE FROM suite_metadata. If we deleted
26
+ * first, the subsequent getTests({ suiteId }) filter would find zero
27
+ * rows in the SQLite layer (column already null) and skip the JSON-blob
28
+ * rewrite, leaving each affected test's `metadata.suiteId` pointed at a
29
+ * suite that no longer exists. Orphaning first rewrites both the SQL
30
+ * column and the JSON blob via updateTest, so the FK cascade is then a
31
+ * no-op. Non-DB layers (Volatile, S3, GCS) have no FK and rely on this
32
+ * pass for the cascade behavior in either ordering.
25
33
  */
26
34
  deleteSuite(suiteId: string): Promise<void>;
27
35
  }
@@ -103,27 +103,32 @@ class SuitesManager {
103
103
  /**
104
104
  * Delete a suite from all persistence layers.
105
105
  *
106
- * After deleting, orphans any tests that belong to this suite by setting
107
- * their suiteId to null. This mirrors the DB-level ON DELETE SET NULL
108
- * behavior for non-DB persistence layers (Volatile, S3, GCS).
106
+ * Orphans tests that belong to the suite first, then deletes the suite
107
+ * row. The orphan pass must run before the suite delete: SQLite has an
108
+ * ON DELETE SET NULL FK on test_metadata.suite_id, which clears the
109
+ * column synchronously inside DELETE FROM suite_metadata. If we deleted
110
+ * first, the subsequent getTests({ suiteId }) filter would find zero
111
+ * rows in the SQLite layer (column already null) and skip the JSON-blob
112
+ * rewrite, leaving each affected test's `metadata.suiteId` pointed at a
113
+ * suite that no longer exists. Orphaning first rewrites both the SQL
114
+ * column and the JSON blob via updateTest, so the FK cascade is then a
115
+ * no-op. Non-DB layers (Volatile, S3, GCS) have no FK and rely on this
116
+ * pass for the cascade behavior in either ordering.
109
117
  */
110
118
  async deleteSuite(suiteId) {
111
119
  for (const { key, persistence, } of await this.suitesPersistenceRegistry.getEntries()) {
112
120
  try {
113
- await persistence.deleteSuite(suiteId);
114
- // Orphan tests in this layer after successfully deleting the suite.
115
- // This mirrors the DB-level ON DELETE SET NULL for non-DB layers.
116
121
  // Pair by key — the suites and tests registries can have different
117
122
  // sets of layers (e.g. plugin-only suites persistence) so positional
118
123
  // indexing isn't safe.
119
124
  const testsPersistence = await this.testsPersistenceRegistry.getByKey(key);
120
- if (!testsPersistence) {
121
- continue;
122
- }
123
- const testsResult = await testsPersistence.getTests({ suiteId });
124
- for (const test of testsResult.items) {
125
- await testsPersistence.updateTest({ ...test, suiteId: null });
125
+ if (testsPersistence) {
126
+ const testsResult = await testsPersistence.getTests({ suiteId });
127
+ for (const test of testsResult.items) {
128
+ await testsPersistence.updateTest({ ...test, suiteId: null });
129
+ }
126
130
  }
131
+ await persistence.deleteSuite(suiteId);
127
132
  }
128
133
  catch {
129
134
  // Ignore errors from layers that don't have this suite.
@@ -13,6 +13,7 @@ exports.renderHtml = renderHtml;
13
13
  const fs_1 = require("fs");
14
14
  const path_1 = require("path");
15
15
  const ansi_1 = require("../utils/ansi");
16
+ const MiscUtils_1 = require("../utils/MiscUtils");
16
17
  const reportWalk_1 = require("./reportWalk");
17
18
  // ---------------------------------------------------------------------------
18
19
  // Helpers
@@ -1994,7 +1995,7 @@ details.ai-invocation[open]>summary .native-step-chevron{transform:rotate(90deg)
1994
1995
 
1995
1996
  <div class="report-footer">
1996
1997
  <div class="footer-brand"><span class="logo">${LOGO_SVG}</span> Donobu</div>
1997
- <span>Report Generated By Donobu</span>
1998
+ <span>Report Generated By Donobu v${esc(MiscUtils_1.MiscUtils.DONOBU_VERSION)}</span>
1998
1999
  </div>
1999
2000
  </div>
2000
2001
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "donobu",
3
- "version": "5.36.3",
3
+ "version": "5.36.5",
4
4
  "description": "Create browser automations with an LLM agent and replay them as Playwright scripts.",
5
5
  "main": "dist/main.js",
6
6
  "module": "dist/esm/main.js",