release-2.9.2 (#1566)

* fix: stop full database rewrites on every plugin db call

* fix: restore release video link in 2.9.2 notes

* fix: use TERMIX_PAT in workflows
This commit is contained in:
Luke Gustafson authored and GitHub committed 2026-10-05 20:24:46 -05:00
1 parent 40df00bd95
commit 2b309e9582
19 files changed
+342 -87

No files matched your search

+18 -8
View File
@@ -109,6 +109,11 @@ interface AuditOptions {
action: string;
/** Short description of the call shape. Never a value. */
details?: () => string;
/**
* Audit only the first success. For handles plugins fetch before every
* query: an audit row per call rewrote the whole database each time.
*/
once?: boolean;
}
/**
@@ -124,6 +129,7 @@ function guarded<Args extends unknown[], Result>(
fn: (...args: Args) => Promise<Result>,
options: AuditOptions,
): (...args: Args) => Promise<Result> {
let audited = false;
return async (...args: Args): Promise<Result> => {
// A refusal is audited inside assertCapability, under this action.
await assertCapability(
@@ -135,7 +141,10 @@ function guarded<Args extends unknown[], Result>(
try {
const result = await fn(...args);
await writeAudit(manifest, options, { success: true });
if (!options.once || !audited) {
audited = true;
await writeAudit(manifest, options, { success: true });
}
return result;
} catch (error) {
await writeAudit(manifest, options, {
@@ -242,7 +251,7 @@ export function createPluginContext(
return null;
}
},
{ action: "kv_get" },
{ action: "kv_get", once: true },
);
const kvSet = guarded(
@@ -296,7 +305,7 @@ export function createPluginContext(
await import("../database/repositories/factory.js");
return createCurrentPluginStorageRepository().listKeys(pluginId);
},
{ action: "kv_list" },
{ action: "kv_list", once: true },
);
const filesDataDir = guarded(
@@ -309,7 +318,7 @@ export function createPluginContext(
await fs.mkdir(dir, { recursive: true });
return dir;
},
{ action: "files_data_dir" },
{ action: "files_data_dir", once: true },
);
const dbDefine = guarded(
@@ -329,7 +338,7 @@ export function createPluginContext(
const { getDb } = await import("../database/db/index.js");
return getDb();
},
{ action: "db_client" },
{ action: "db_client", once: true },
);
// The one settings call that leaves the plugin's own namespace, so the one
@@ -341,6 +350,7 @@ export function createPluginContext(
{
action: "settings_read_core",
details: () => "read a core server setting",
once: true,
},
);
@@ -359,7 +369,7 @@ export function createPluginContext(
userRoles: schema.userRoles,
};
},
{ action: "db_refs" },
{ action: "db_refs", once: true },
);
const desktopOpenIsolatedWindow = guarded(
@@ -488,8 +498,8 @@ export function createPluginContext(
define: (definition) => dbDefine(definition) as never,
client: () => dbClient() as never,
refs: () => dbRefs() as never,
// Gated but not audited: it follows every write, and the write itself
// went through a client() call that already left an audit line.
// Gated but not audited: it follows every write, and client() was
// already audited when the plugin first asked for it.
persist: async (options) => {
await assertCapability(pluginId, "db:own", manifest.capabilities);
if (!needsExplicitPersist(resolveDatabaseDialect())) return;