Files
dbx/apps/desktop/src/stores/sqlExecutionDangerStore.ts
T

79 lines
2.4 KiB
TypeScript

import { defineStore } from "pinia";
import { ref } from "vue";
import type { DatabaseType } from "@/types/database";
export interface SqlExecutionDangerRequest {
sql: string;
kind: "sql" | "redis";
connectionName?: string;
database?: string;
targetLabel?: string;
databaseType?: DatabaseType;
/** Identifies a cancellable execution batch without coupling this store to its orchestrator. */
scopeId?: string;
}
interface QueuedDangerRequest {
request: SqlExecutionDangerRequest;
resolve: (confirmed: boolean) => void;
}
export const useSqlExecutionDangerStore = defineStore("sqlExecutionDanger", () => {
const pending = ref<SqlExecutionDangerRequest>();
const queue: QueuedDangerRequest[] = [];
let resolvePending: ((confirmed: boolean) => void) | undefined;
function requestConfirmation(request: SqlExecutionDangerRequest): Promise<boolean> {
return new Promise<boolean>((resolve) => {
if (pending.value) {
queue.push({ request, resolve });
return;
}
begin(request, resolve);
});
}
function begin(request: SqlExecutionDangerRequest, resolve: (confirmed: boolean) => void): void {
pending.value = request;
resolvePending = resolve;
}
function settle(confirmed: boolean): void {
const resolve = resolvePending;
resolvePending = undefined;
pending.value = undefined;
resolve?.(confirmed);
const next = queue.shift();
if (next) begin(next.request, next.resolve);
}
function confirm(): void {
settle(true);
}
function cancel(): void {
settle(false);
}
function cancelAll(): void {
settle(false);
while (queue.length > 0) queue.shift()?.resolve(false);
}
function cancelScope(scopeId: string): void {
// Remove queued requests before settling the active one. `settle` promotes
// the next queue entry, so removing afterwards could promote a request from
// this same scope and leave its promise unresolved.
const retained: QueuedDangerRequest[] = [];
const cancelled: QueuedDangerRequest[] = [];
for (const entry of queue) {
(entry.request.scopeId === scopeId ? cancelled : retained).push(entry);
}
queue.splice(0, queue.length, ...retained);
cancelled.forEach((entry) => entry.resolve(false));
if (pending.value?.scopeId === scopeId) settle(false);
}
return { pending, requestConfirmation, confirm, cancel, cancelAll, cancelScope };
});