UNPKG

donobu

Version:

Create browser automations with an LLM agent and replay them as Playwright scripts.

140 lines 6.04 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.SuitesManager = void 0; const crypto_1 = require("crypto"); const SuiteNotFoundException_1 = require("../exceptions/SuiteNotFoundException"); const buildProvenance_1 = require("../utils/buildProvenance"); const FederatedPagination_1 = require("./FederatedPagination"); class SuitesManager { constructor(suitesPersistenceRegistry, testsPersistenceRegistry) { this.suitesPersistenceRegistry = suitesPersistenceRegistry; this.testsPersistenceRegistry = testsPersistenceRegistry; } async createSuite(params) { const suiteId = (0, crypto_1.randomUUID)(); const suiteMetadata = { id: suiteId, metadataVersion: 1, name: params.name, description: params.description ?? null, target: params.target, web: params.web ? { browser: params.web.browser ?? { using: { type: 'device' } }, targetWebsite: params.web.targetWebsite, } : undefined, envVars: params.envVars ?? null, customTools: params.customTools ?? null, callbackUrl: params.callbackUrl ?? null, allowedTools: params.allowedTools ?? undefined, resultJsonSchema: params.resultJsonSchema ?? null, maxToolCalls: params.maxToolCalls ?? null, videoDisabled: params.videoDisabled, provenance: (0, buildProvenance_1.buildProvenance)('DONOBU_STUDIO'), }; // Create in the primary persistence layer only. const primary = await this.suitesPersistenceRegistry.get(); await primary.createSuite(suiteMetadata); return suiteMetadata; } async getSuiteById(suiteId) { for (const persistence of await this.suitesPersistenceRegistry.getAll()) { try { return await persistence.getSuiteById(suiteId); } catch (error) { if (!(error instanceof SuiteNotFoundException_1.SuiteNotFoundException)) { throw error; } } } throw SuiteNotFoundException_1.SuiteNotFoundException.forId(suiteId); } async getSuites(query) { const layers = (await this.suitesPersistenceRegistry.getAll()).map((persistence) => ({ getItems: (q) => persistence.getSuites(q), })); const sortBy = query.sortBy ?? 'created_at'; const desc = (query.sortOrder ?? 'desc') === 'desc'; // SuiteMetadata has no `created_at` field — fall back to "now" so newly // returned items sort to the most-recent end. const fieldFor = (suite) => { switch (sortBy) { case 'created_at': return Date.now(); case 'name': return suite.name ?? ''; default: return ''; } }; return (0, FederatedPagination_1.federatedList)(layers, query, (a, b) => { const aKey = fieldFor(a); const bKey = fieldFor(b); if (aKey < bKey) { return desc ? 1 : -1; } if (aKey > bKey) { return desc ? -1 : 1; } return 0; }); } /** * Update a suite in the persistence layer where it exists. * Iterates layers, updates in the first one that has it, ignores * SuiteNotFoundExceptions from others. */ async updateSuite(suiteMetadata) { for (const persistence of await this.suitesPersistenceRegistry.getAll()) { try { await persistence.updateSuite(suiteMetadata); return; } catch (error) { if (!(error instanceof SuiteNotFoundException_1.SuiteNotFoundException)) { throw error; } } } throw SuiteNotFoundException_1.SuiteNotFoundException.forId(suiteMetadata.id); } /** * Delete a suite from all persistence layers. * * Orphans tests that belong to the suite first, then deletes the suite * row. The orphan pass must run before the suite delete: SQLite has an * ON DELETE SET NULL FK on test_metadata.suite_id, which clears the * column synchronously inside DELETE FROM suite_metadata. If we deleted * first, the subsequent getTests({ suiteId }) filter would find zero * rows in the SQLite layer (column already null) and skip the JSON-blob * rewrite, leaving each affected test's `metadata.suiteId` pointed at a * suite that no longer exists. Orphaning first rewrites both the SQL * column and the JSON blob via updateTest, so the FK cascade is then a * no-op. Non-DB layers (Volatile, S3, GCS) have no FK and rely on this * pass for the cascade behavior in either ordering. */ async deleteSuite(suiteId) { for (const { key, persistence, } of await this.suitesPersistenceRegistry.getEntries()) { try { // Pair by key — the suites and tests registries can have different // sets of layers (e.g. plugin-only suites persistence) so positional // indexing isn't safe. const testsPersistence = await this.testsPersistenceRegistry.getByKey(key); if (testsPersistence) { const testsResult = await testsPersistence.getTests({ suiteId }); for (const test of testsResult.items) { await testsPersistence.updateTest({ ...test, suiteId: null }); } } await persistence.deleteSuite(suiteId); } catch { // Ignore errors from layers that don't have this suite. } } } } exports.SuitesManager = SuitesManager; //# sourceMappingURL=SuitesManager.js.map