refactor: split oversized modules and start server src/stores layout

Consolidate format helpers under lib/format, split React Query hooks by domain, extract AuthPicker/ConfigRefHint and HTTP trigger routing, demote file-private exports, and move KV/secrets/variables/profiles/http-auths stores under src/stores with root re-exports.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-22 06:16:38 +07:00
co-authored by Cursor
parent 368a2ca365
commit db884808f2
56 changed files with 2690 additions and 2578 deletions
+1 -244
View File
@@ -1,244 +1 @@
import { randomUUID } from "node:crypto";
import yaml from "yaml";
import { db } from "./db.js";
import { assertOwner, listOwnerYamlFiles, readWorkflowYaml } from "./fs-store.js";
const MAX_NAME_LENGTH = 128;
const MAX_DESCRIPTION_LENGTH = 500;
const MAX_CONFIG_BYTES = 64 * 1024;
const PROFILE_NAME_RE = /^[A-Za-z0-9._-]+$/;
function nowIso() {
return new Date().toISOString();
}
function httpError(message, statusCode = 400) {
const err = new Error(message);
err.statusCode = statusCode;
return err;
}
/**
* @param {unknown} name
* @returns {string}
*/
export function assertProfileName(name) {
if (typeof name !== "string" || !PROFILE_NAME_RE.test(name)) {
throw httpError("invalid profile name");
}
if (name.length > MAX_NAME_LENGTH) {
throw httpError(`profile name must be at most ${MAX_NAME_LENGTH} characters`);
}
return name;
}
/**
* @param {unknown} script
* @returns {string}
*/
export function assertProfileScript(script) {
if (typeof script !== "string" || script.trim().length === 0) {
throw httpError("script is required");
}
const trimmed = script.trim();
if (trimmed.length > 256) {
throw httpError("script name is too long");
}
return trimmed;
}
/**
* @param {unknown} description
* @returns {string}
*/
export function assertProfileDescription(description) {
if (description == null) return "";
if (typeof description !== "string") {
throw httpError("description must be a string");
}
if (description.length > MAX_DESCRIPTION_LENGTH) {
throw httpError(`description must be at most ${MAX_DESCRIPTION_LENGTH} characters`);
}
return description;
}
/**
* @param {unknown} config
* @returns {string}
*/
export function encodeProfileConfig(config) {
if (config == null) return "{}";
if (typeof config !== "object" || Array.isArray(config)) {
throw httpError("config must be an object");
}
let encoded;
try {
encoded = JSON.stringify(config);
} catch {
throw httpError("config must be JSON-serializable");
}
if (Buffer.byteLength(encoded, "utf8") > MAX_CONFIG_BYTES) {
throw httpError(`config exceeds ${MAX_CONFIG_BYTES} byte limit`);
}
return encoded;
}
/**
* @param {string} stored
* @returns {Record<string, unknown>}
*/
export function decodeProfileConfig(stored) {
if (stored == null || stored === "") return {};
try {
const parsed = JSON.parse(stored);
if (parsed != null && typeof parsed === "object" && !Array.isArray(parsed)) {
return parsed;
}
} catch {
throw new Error(`corrupt profile config: ${JSON.stringify(stored).slice(0, 80)}`);
}
throw new Error("corrupt profile config: not an object");
}
/**
* @param {Record<string, unknown>} row
*/
function publicProfile(row) {
return {
id: row.id,
owner: row.owner,
name: row.name,
script: row.script,
config: decodeProfileConfig(String(row.config ?? "{}")),
description: row.description == null ? "" : String(row.description),
created_at: row.created_at,
updated_at: row.updated_at,
};
}
/**
* @param {{ owner?: string }} [filters]
*/
export async function listProfiles(filters = {}) {
let q = db("profiles")
.select("id", "owner", "name", "script", "config", "description", "created_at", "updated_at")
.orderBy("owner", "asc")
.orderBy("name", "asc");
if (filters.owner) {
q = q.where("owner", assertOwner(filters.owner));
}
const rows = await q;
return rows.map((row) => publicProfile(row));
}
/**
* @param {string} id
*/
export async function getProfileById(id) {
const row = await db("profiles").where({ id }).first();
return row ? publicProfile(row) : null;
}
/**
* @param {string} owner
* @param {string} name
*/
export async function getProfilePlain(owner, name) {
const ownerName = assertOwner(owner);
const profileName = assertProfileName(name);
const row = await db("profiles").where({ owner: ownerName, name: profileName }).first();
return row ? publicProfile(row) : null;
}
/**
* @param {{
* owner: string,
* name: string,
* script: unknown,
* config?: unknown,
* description?: unknown,
* }} opts
*/
export async function upsertProfile({ owner, name, script, config, description }) {
const ownerName = assertOwner(owner);
const profileName = assertProfileName(name);
const scriptName = assertProfileScript(script);
const encoded = encodeProfileConfig(config ?? {});
const desc = assertProfileDescription(description);
const now = nowIso();
const existing = await db("profiles").where({ owner: ownerName, name: profileName }).first();
if (existing) {
await db("profiles")
.where({ id: existing.id })
.update({
script: scriptName,
config: encoded,
description: desc,
updated_at: now,
});
return getProfileById(existing.id);
}
const id = randomUUID();
await db("profiles").insert({
id,
owner: ownerName,
name: profileName,
script: scriptName,
config: encoded,
description: desc,
created_at: now,
updated_at: now,
});
return getProfileById(id);
}
/**
* @param {string} id
* @returns {Promise<boolean>}
*/
export async function deleteProfile(id) {
const n = await db("profiles").where({ id }).del();
return n > 0;
}
/**
* Workflows (same owner) whose YAML steps reference this profile name.
* @param {string} owner
* @param {string} name
* @returns {{ file: string, name: string, steps: number }[]}
*/
export function listProfileUsages(owner, name) {
const ownerName = assertOwner(owner);
const profileName = assertProfileName(name);
/** @type {{ file: string, name: string, steps: number }[]} */
const usages = [];
for (const file of listOwnerYamlFiles(ownerName)) {
const content = readWorkflowYaml(ownerName, file);
if (content == null) continue;
let parsed;
try {
parsed = yaml.parse(content);
} catch {
continue;
}
if (parsed == null || typeof parsed !== "object" || Array.isArray(parsed)) continue;
const scripts = parsed.scripts;
if (!Array.isArray(scripts)) continue;
let steps = 0;
for (const step of scripts) {
if (step != null && typeof step === "object" && !Array.isArray(step) && step.profile === profileName) {
steps += 1;
}
}
if (steps > 0) {
usages.push({
file,
name: typeof parsed.name === "string" && parsed.name ? parsed.name : file,
steps,
});
}
}
return usages;
}
export * from "./src/stores/profiles-store.js";