@cerefox/memory 1.9.1 → 1.10.0
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.
- package/dist/bin/cerefox.js +264 -92
- package/dist/frontend/assets/index-i67RH1VY.js +121 -0
- package/dist/frontend/assets/index-i67RH1VY.js.map +1 -0
- package/dist/frontend/index.html +1 -1
- package/dist/server-assets/_shared/ef-meta/index.ts +3 -3
- package/dist/server-assets/_shared/mcp-tools/_document-meta.ts +377 -0
- package/dist/server-assets/_shared/mcp-tools/_projects.ts +13 -32
- package/dist/server-assets/_shared/mcp-tools/_utils.ts +9 -2
- package/dist/server-assets/_shared/mcp-tools/types.ts +6 -2
- package/dist/server-assets/db/migrations/0030_rename_document_rpc.sql +80 -0
- package/dist/server-assets/db/rpcs.sql +63 -1
- package/dist/server-assets/db/schema.sql +1 -1
- package/docs/guides/upgrading.md +16 -3
- package/package.json +1 -1
- package/dist/frontend/assets/index-DWT7wZMR.js +0 -121
- package/dist/frontend/assets/index-DWT7wZMR.js.map +0 -1
package/dist/bin/cerefox.js
CHANGED
|
@@ -7438,7 +7438,7 @@ var exports_meta = {};
|
|
|
7438
7438
|
__export(exports_meta, {
|
|
7439
7439
|
PKG_VERSION: () => PKG_VERSION
|
|
7440
7440
|
});
|
|
7441
|
-
var PKG_VERSION = "1.
|
|
7441
|
+
var PKG_VERSION = "1.10.0";
|
|
7442
7442
|
var init_meta = () => {};
|
|
7443
7443
|
|
|
7444
7444
|
// ../../_shared/config/paths.ts
|
|
@@ -23530,9 +23530,9 @@ function isAuditCheckError(message) {
|
|
|
23530
23530
|
function isDuplicateKeyError(message) {
|
|
23531
23531
|
return /duplicate key|unique constraint|23505/i.test(message);
|
|
23532
23532
|
}
|
|
23533
|
-
function storeWriteRemediation(message, fnName) {
|
|
23533
|
+
function storeWriteRemediation(message, fnName, requiredSchema = "0.14.0") {
|
|
23534
23534
|
if (isMissingFunctionError(message, fnName)) {
|
|
23535
|
-
return
|
|
23535
|
+
return `The deployed server predates schema ${requiredSchema} — or PostgREST's schema cache ` + "is stale right after a deploy. If you just deployed, retry in a few " + "seconds; otherwise run `cerefox server deploy`.";
|
|
23536
23536
|
}
|
|
23537
23537
|
if (isAuditCheckError(message)) {
|
|
23538
23538
|
return "The server's audit-log constraint predates migration 0028 (partial " + "deploy). Run `cerefox server deploy` to apply pending migrations, then retry.";
|
|
@@ -25598,6 +25598,221 @@ var init_src = __esm(() => {
|
|
|
25598
25598
|
src_default = Postgres;
|
|
25599
25599
|
});
|
|
25600
25600
|
|
|
25601
|
+
// ../../_shared/mcp-tools/_document-meta.ts
|
|
25602
|
+
function stableStringify(value) {
|
|
25603
|
+
if (Array.isArray(value))
|
|
25604
|
+
return `[${value.map(stableStringify).join(",")}]`;
|
|
25605
|
+
if (value !== null && typeof value === "object") {
|
|
25606
|
+
const keys = Object.keys(value).sort();
|
|
25607
|
+
return `{${keys.map((k) => `${JSON.stringify(k)}:${stableStringify(value[k])}`).join(",")}}`;
|
|
25608
|
+
}
|
|
25609
|
+
return JSON.stringify(value);
|
|
25610
|
+
}
|
|
25611
|
+
function normalizeMetadata(value) {
|
|
25612
|
+
const out = {};
|
|
25613
|
+
for (const [k, v] of Object.entries(value)) {
|
|
25614
|
+
if (v !== null)
|
|
25615
|
+
out[k] = v;
|
|
25616
|
+
}
|
|
25617
|
+
return out;
|
|
25618
|
+
}
|
|
25619
|
+
async function changeDocumentTitle(supabase, documentId, newTitle, who) {
|
|
25620
|
+
const trimmed = (newTitle ?? "").trim();
|
|
25621
|
+
if (!trimmed)
|
|
25622
|
+
throw new FacetValidationError("Title cannot be empty.");
|
|
25623
|
+
const { data, error } = await supabase.rpc("cerefox_rename_document", {
|
|
25624
|
+
p_document_id: documentId,
|
|
25625
|
+
p_new_title: trimmed,
|
|
25626
|
+
p_author: who.author,
|
|
25627
|
+
p_author_type: who.authorType
|
|
25628
|
+
});
|
|
25629
|
+
if (error) {
|
|
25630
|
+
const msg = error.message ?? String(error);
|
|
25631
|
+
if (/not found/i.test(msg))
|
|
25632
|
+
throw new FacetNotFoundError(msg);
|
|
25633
|
+
if (/cannot be empty/i.test(msg))
|
|
25634
|
+
throw new FacetValidationError(msg);
|
|
25635
|
+
const remediation = storeWriteRemediation(msg, "cerefox_rename_document", "0.15.0");
|
|
25636
|
+
if (remediation)
|
|
25637
|
+
throw new Error(`Title update failed: ${remediation}`);
|
|
25638
|
+
throw new Error(`Title update failed: ${msg}`);
|
|
25639
|
+
}
|
|
25640
|
+
const row = data?.[0];
|
|
25641
|
+
return { changed: row?.renamed ?? false, title: row?.new_title ?? trimmed };
|
|
25642
|
+
}
|
|
25643
|
+
async function applyMembershipReplace(supabase, opts) {
|
|
25644
|
+
const { data: current, error: curErr } = await supabase.from("cerefox_document_projects").select("project_id").eq("document_id", opts.documentId);
|
|
25645
|
+
if (curErr)
|
|
25646
|
+
throw new Error(`Membership read failed: ${curErr.message}`);
|
|
25647
|
+
const currentSet = new Set((current ?? []).map((r) => r.project_id));
|
|
25648
|
+
if (opts.projectIds.length === currentSet.size && opts.projectIds.every((id) => currentSet.has(id))) {
|
|
25649
|
+
return { changed: false };
|
|
25650
|
+
}
|
|
25651
|
+
const { error: delErr } = await supabase.from("cerefox_document_projects").delete().eq("document_id", opts.documentId);
|
|
25652
|
+
if (delErr)
|
|
25653
|
+
throw new Error(`Membership replace failed: ${delErr.message}`);
|
|
25654
|
+
if (opts.projectIds.length > 0) {
|
|
25655
|
+
const rows = opts.projectIds.map((pid) => ({
|
|
25656
|
+
document_id: opts.documentId,
|
|
25657
|
+
project_id: pid
|
|
25658
|
+
}));
|
|
25659
|
+
const { error: insErr } = await supabase.from("cerefox_document_projects").insert(rows);
|
|
25660
|
+
if (insErr)
|
|
25661
|
+
throw new Error(`Membership replace failed: ${insErr.message}`);
|
|
25662
|
+
}
|
|
25663
|
+
const { error: auditErr } = await supabase.rpc("cerefox_create_audit_entry", {
|
|
25664
|
+
p_document_id: opts.documentId,
|
|
25665
|
+
p_version_id: null,
|
|
25666
|
+
p_operation: "update-metadata",
|
|
25667
|
+
p_author: opts.author,
|
|
25668
|
+
p_author_type: opts.authorType,
|
|
25669
|
+
p_size_before: null,
|
|
25670
|
+
p_size_after: null,
|
|
25671
|
+
p_description: opts.projectNames.length > 0 ? `Set document projects to [${opts.projectNames.join(", ")}]` : "Cleared all project memberships"
|
|
25672
|
+
});
|
|
25673
|
+
if (auditErr)
|
|
25674
|
+
console.warn("applyMembershipReplace: audit entry failed", auditErr.message);
|
|
25675
|
+
logUsage(supabase, {
|
|
25676
|
+
operation: "set-document-projects",
|
|
25677
|
+
accessPath: opts.accessPath,
|
|
25678
|
+
requestor: opts.author,
|
|
25679
|
+
document_id: opts.documentId,
|
|
25680
|
+
result_count: opts.projectIds.length
|
|
25681
|
+
});
|
|
25682
|
+
return { changed: true };
|
|
25683
|
+
}
|
|
25684
|
+
async function assertDocumentLive(supabase, documentId) {
|
|
25685
|
+
const { data, error } = await supabase.from("cerefox_documents").select("id").eq("id", documentId).is("deleted_at", null).limit(1);
|
|
25686
|
+
if (error)
|
|
25687
|
+
throw new Error(`Document read failed: ${error.message}`);
|
|
25688
|
+
if (!data?.length) {
|
|
25689
|
+
throw new FacetNotFoundError(`Document not found (or in the trash): ${documentId}`);
|
|
25690
|
+
}
|
|
25691
|
+
}
|
|
25692
|
+
async function setDocumentProjectsByIds(supabase, opts) {
|
|
25693
|
+
const wanted = [...new Set(opts.projectIds)];
|
|
25694
|
+
const { data: current, error: curErr } = await supabase.from("cerefox_document_projects").select("project_id").eq("document_id", opts.documentId);
|
|
25695
|
+
if (curErr)
|
|
25696
|
+
throw new Error(`Membership read failed: ${curErr.message}`);
|
|
25697
|
+
const currentSet = new Set((current ?? []).map((r2) => r2.project_id));
|
|
25698
|
+
if (wanted.length === currentSet.size && wanted.every((id) => currentSet.has(id))) {
|
|
25699
|
+
return { changed: false, names: [] };
|
|
25700
|
+
}
|
|
25701
|
+
await assertDocumentLive(supabase, opts.documentId);
|
|
25702
|
+
let names = [];
|
|
25703
|
+
if (wanted.length > 0) {
|
|
25704
|
+
const { data: found, error: valErr } = await supabase.from("cerefox_projects").select("id, name").in("id", wanted);
|
|
25705
|
+
if (valErr)
|
|
25706
|
+
throw new Error(`Project validation failed: ${valErr.message}`);
|
|
25707
|
+
const byId = new Map((found ?? []).map((r2) => [r2.id, r2.name]));
|
|
25708
|
+
const missing = wanted.filter((id) => !byId.has(id));
|
|
25709
|
+
if (missing.length > 0) {
|
|
25710
|
+
throw new FacetValidationError(`Unknown project id(s): ${missing.join(", ")} — memberships left unchanged.`);
|
|
25711
|
+
}
|
|
25712
|
+
names = wanted.map((id) => byId.get(id));
|
|
25713
|
+
}
|
|
25714
|
+
const r = await applyMembershipReplace(supabase, {
|
|
25715
|
+
documentId: opts.documentId,
|
|
25716
|
+
projectIds: wanted,
|
|
25717
|
+
projectNames: names,
|
|
25718
|
+
accessPath: opts.accessPath,
|
|
25719
|
+
author: opts.author,
|
|
25720
|
+
authorType: opts.authorType
|
|
25721
|
+
});
|
|
25722
|
+
return { changed: r.changed, names };
|
|
25723
|
+
}
|
|
25724
|
+
async function updateDocumentFacets(supabase, opts) {
|
|
25725
|
+
const result = {
|
|
25726
|
+
titleChanged: false,
|
|
25727
|
+
metadataChanged: false,
|
|
25728
|
+
projectsChanged: false
|
|
25729
|
+
};
|
|
25730
|
+
const who = { author: opts.author, authorType: opts.authorType };
|
|
25731
|
+
const step = async (fn) => {
|
|
25732
|
+
try {
|
|
25733
|
+
return await fn();
|
|
25734
|
+
} catch (err) {
|
|
25735
|
+
throw err instanceof FacetUpdateError ? err : new FacetUpdateError(result, err instanceof Error ? err : new Error(String(err)));
|
|
25736
|
+
}
|
|
25737
|
+
};
|
|
25738
|
+
if (opts.metadata !== undefined) {
|
|
25739
|
+
await step(async () => {
|
|
25740
|
+
const { data: doc, error } = await supabase.from("cerefox_documents").select("metadata").eq("id", opts.documentId).is("deleted_at", null).limit(1);
|
|
25741
|
+
if (error)
|
|
25742
|
+
throw new Error(`Metadata read failed: ${error.message}`);
|
|
25743
|
+
if (!doc?.length) {
|
|
25744
|
+
throw new FacetNotFoundError(`Document not found (or in the trash): ${opts.documentId}`);
|
|
25745
|
+
}
|
|
25746
|
+
const stored = doc[0].metadata ?? {};
|
|
25747
|
+
const wanted = normalizeMetadata(opts.metadata);
|
|
25748
|
+
if (stableStringify(stored) !== stableStringify(wanted)) {
|
|
25749
|
+
const { error: rpcErr } = await supabase.rpc("cerefox_set_document_metadata", {
|
|
25750
|
+
p_document_id: opts.documentId,
|
|
25751
|
+
p_metadata: wanted,
|
|
25752
|
+
p_replace: true,
|
|
25753
|
+
p_author: opts.author,
|
|
25754
|
+
p_author_type: opts.authorType
|
|
25755
|
+
});
|
|
25756
|
+
if (rpcErr) {
|
|
25757
|
+
const msg = rpcErr.message ?? String(rpcErr);
|
|
25758
|
+
if (/not found/i.test(msg))
|
|
25759
|
+
throw new FacetNotFoundError(msg);
|
|
25760
|
+
throw new Error(`Metadata update failed: ${msg}`);
|
|
25761
|
+
}
|
|
25762
|
+
result.metadataChanged = true;
|
|
25763
|
+
}
|
|
25764
|
+
});
|
|
25765
|
+
}
|
|
25766
|
+
if (opts.projectIds !== undefined) {
|
|
25767
|
+
await step(async () => {
|
|
25768
|
+
const r = await setDocumentProjectsByIds(supabase, {
|
|
25769
|
+
documentId: opts.documentId,
|
|
25770
|
+
projectIds: opts.projectIds,
|
|
25771
|
+
accessPath: opts.accessPath,
|
|
25772
|
+
...who
|
|
25773
|
+
});
|
|
25774
|
+
result.projectsChanged = r.changed;
|
|
25775
|
+
});
|
|
25776
|
+
}
|
|
25777
|
+
if (opts.title !== undefined) {
|
|
25778
|
+
await step(async () => {
|
|
25779
|
+
const r = await changeDocumentTitle(supabase, opts.documentId, opts.title, who);
|
|
25780
|
+
result.titleChanged = r.changed;
|
|
25781
|
+
});
|
|
25782
|
+
}
|
|
25783
|
+
return result;
|
|
25784
|
+
}
|
|
25785
|
+
var FacetNotFoundError, FacetValidationError, FacetUpdateError;
|
|
25786
|
+
var init__document_meta = __esm(() => {
|
|
25787
|
+
init__utils();
|
|
25788
|
+
FacetNotFoundError = class FacetNotFoundError extends Error {
|
|
25789
|
+
constructor(message) {
|
|
25790
|
+
super(message);
|
|
25791
|
+
this.name = "FacetNotFoundError";
|
|
25792
|
+
}
|
|
25793
|
+
};
|
|
25794
|
+
FacetValidationError = class FacetValidationError extends Error {
|
|
25795
|
+
constructor(message) {
|
|
25796
|
+
super(message);
|
|
25797
|
+
this.name = "FacetValidationError";
|
|
25798
|
+
}
|
|
25799
|
+
};
|
|
25800
|
+
FacetUpdateError = class FacetUpdateError extends Error {
|
|
25801
|
+
applied;
|
|
25802
|
+
constructor(applied, cause) {
|
|
25803
|
+
const done = [
|
|
25804
|
+
applied.metadataChanged ? "metadata" : null,
|
|
25805
|
+
applied.projectsChanged ? "projects" : null,
|
|
25806
|
+
applied.titleChanged ? "title" : null
|
|
25807
|
+
].filter(Boolean);
|
|
25808
|
+
super(done.length > 0 ? `${cause.message} (already applied before the failure: ${done.join(", ")})` : cause.message);
|
|
25809
|
+
this.name = "FacetUpdateError";
|
|
25810
|
+
this.applied = applied;
|
|
25811
|
+
this.cause = cause;
|
|
25812
|
+
}
|
|
25813
|
+
};
|
|
25814
|
+
});
|
|
25815
|
+
|
|
25601
25816
|
// ../../_shared/mcp-tools/_projects.ts
|
|
25602
25817
|
async function resolveOrCreateProject(supabase, projectName, audit) {
|
|
25603
25818
|
const { data, error } = await supabase.rpc("cerefox_create_project", {
|
|
@@ -25670,31 +25885,13 @@ async function replaceDocumentProjects(supabase, opts) {
|
|
|
25670
25885
|
throw new Error(`Could not resolve project(s): ${failed.join(", ")} — memberships left unchanged.`);
|
|
25671
25886
|
}
|
|
25672
25887
|
const projectIds = resolved.map((r) => r.projectId);
|
|
25673
|
-
await supabase
|
|
25674
|
-
|
|
25675
|
-
|
|
25676
|
-
|
|
25677
|
-
}
|
|
25678
|
-
try {
|
|
25679
|
-
await supabase.rpc("cerefox_create_audit_entry", {
|
|
25680
|
-
p_document_id: documentId,
|
|
25681
|
-
p_version_id: null,
|
|
25682
|
-
p_operation: "update-metadata",
|
|
25683
|
-
p_author: author,
|
|
25684
|
-
p_author_type: authorType,
|
|
25685
|
-
p_size_before: null,
|
|
25686
|
-
p_size_after: null,
|
|
25687
|
-
p_description: cleanNames.length > 0 ? `Set document projects to [${cleanNames.join(", ")}]` : "Cleared all project memberships"
|
|
25688
|
-
});
|
|
25689
|
-
} catch (err) {
|
|
25690
|
-
console.warn("replaceDocumentProjects: audit entry failed", err);
|
|
25691
|
-
}
|
|
25692
|
-
logUsage(supabase, {
|
|
25693
|
-
operation: "set-document-projects",
|
|
25888
|
+
await applyMembershipReplace(supabase, {
|
|
25889
|
+
documentId,
|
|
25890
|
+
projectIds,
|
|
25891
|
+
projectNames: cleanNames,
|
|
25694
25892
|
accessPath,
|
|
25695
|
-
|
|
25696
|
-
|
|
25697
|
-
result_count: projectIds.length
|
|
25893
|
+
author,
|
|
25894
|
+
authorType
|
|
25698
25895
|
});
|
|
25699
25896
|
return { documentTitle: doc[0].title, cleanNames, projectIds };
|
|
25700
25897
|
}
|
|
@@ -25705,6 +25902,7 @@ async function lookupProjectId(supabase, projectName) {
|
|
|
25705
25902
|
return data[0].id;
|
|
25706
25903
|
}
|
|
25707
25904
|
var init__projects = __esm(() => {
|
|
25905
|
+
init__document_meta();
|
|
25708
25906
|
init__utils();
|
|
25709
25907
|
});
|
|
25710
25908
|
|
|
@@ -25802,7 +26000,7 @@ var init_bundled_docs = __esm(() => {
|
|
|
25802
26000
|
});
|
|
25803
26001
|
|
|
25804
26002
|
// ../../_shared/ef-meta/index.ts
|
|
25805
|
-
var EF_VERSION = "1.
|
|
26003
|
+
var EF_VERSION = "1.10.0", CEREFOX_VERSION = "1.10.0", EF_LAST_CHANGED = "1.10.0";
|
|
25806
26004
|
var init_ef_meta = () => {};
|
|
25807
26005
|
|
|
25808
26006
|
// ../../_shared/compatibility/index.ts
|
|
@@ -73413,6 +73611,7 @@ Proceed with deployment to Supabase?`, true);
|
|
|
73413
73611
|
println(c.green(`
|
|
73414
73612
|
✓ Server deploy complete.`));
|
|
73415
73613
|
println(c.dim("Verify with: cerefox doctor"));
|
|
73614
|
+
println(c.dim("Refresh the bundled guides: cerefox guides ingest"));
|
|
73416
73615
|
}
|
|
73417
73616
|
function registerDeployServer(program2) {
|
|
73418
73617
|
program2.command("deploy-server").description("Deploy/update the Cerefox server side (schema + RPCs + Edge Functions) on Supabase.").option("--dry-run", "Print the plan + pre-flight without deploying.").option("--schema-only", "Deploy/update only the schema + RPCs (skip Edge Functions).").option("--functions-only", "Deploy only the Edge Functions (skip the schema/RPCs).").option("--project-ref <ref>", "Supabase project ref for Edge Function deploys (default: derived from CEREFOX_SUPABASE_URL).").option("--yes", "Non-interactive (skip the deployment confirmation).").action(action10);
|
|
@@ -73420,6 +73619,7 @@ function registerDeployServer(program2) {
|
|
|
73420
73619
|
|
|
73421
73620
|
// src/cli/commands/document-edit.ts
|
|
73422
73621
|
init_cli_core();
|
|
73622
|
+
init__document_meta();
|
|
73423
73623
|
init_client();
|
|
73424
73624
|
function parseMetaPair(pair) {
|
|
73425
73625
|
const eq = pair.indexOf("=");
|
|
@@ -73483,27 +73683,17 @@ async function action11(documentId, options) {
|
|
|
73483
73683
|
throw systemError(`Metadata update failed: ${metaErr.message}`);
|
|
73484
73684
|
}
|
|
73485
73685
|
}
|
|
73486
|
-
if (hasTitle) {
|
|
73487
|
-
|
|
73488
|
-
|
|
73489
|
-
|
|
73490
|
-
|
|
73491
|
-
|
|
73492
|
-
|
|
73493
|
-
|
|
73494
|
-
|
|
73495
|
-
|
|
73496
|
-
|
|
73497
|
-
throw systemError(`Title updated but FTS refresh failed: ${ftsErr.message}`);
|
|
73498
|
-
}
|
|
73499
|
-
if (titleChanged) {
|
|
73500
|
-
await client.raw.rpc("cerefox_create_audit_entry", {
|
|
73501
|
-
p_document_id: documentId,
|
|
73502
|
-
p_operation: "update-metadata",
|
|
73503
|
-
p_author: author,
|
|
73504
|
-
p_author_type: authorType,
|
|
73505
|
-
p_description: "Edited title"
|
|
73506
|
-
});
|
|
73686
|
+
if (hasTitle && titleChanged) {
|
|
73687
|
+
try {
|
|
73688
|
+
await changeDocumentTitle(client.raw, documentId, newTitle, { author, authorType });
|
|
73689
|
+
} catch (err) {
|
|
73690
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
73691
|
+
if (err instanceof FacetNotFoundError)
|
|
73692
|
+
throw notFound(msg);
|
|
73693
|
+
if (err instanceof FacetValidationError)
|
|
73694
|
+
throw userError(msg);
|
|
73695
|
+
throw systemError(msg);
|
|
73696
|
+
}
|
|
73507
73697
|
}
|
|
73508
73698
|
println(c.green(`✓ Edited "${newTitle}" (id: ${documentId}).`));
|
|
73509
73699
|
if (titleChanged) {
|
|
@@ -80660,14 +80850,10 @@ async function action33(options) {
|
|
|
80660
80850
|
println("");
|
|
80661
80851
|
println(c.green(`✓ Upgraded to ${target}.`));
|
|
80662
80852
|
println("");
|
|
80663
|
-
println(
|
|
80664
|
-
|
|
80665
|
-
|
|
80666
|
-
|
|
80667
|
-
if (sync.status !== 0) {
|
|
80668
|
-
println(cErr.yellow("⚠ ") + "Could not refresh self-docs (see output above).");
|
|
80669
|
-
println(c.dim(" Run `cerefox guides ingest` manually after a successful `cerefox init`."));
|
|
80670
|
-
}
|
|
80853
|
+
println("Next steps:");
|
|
80854
|
+
println(" 1. " + c.bold("cerefox server deploy") + " apply this release's schema/RPC/EF updates");
|
|
80855
|
+
println(" 2. " + c.bold("cerefox guides ingest") + " re-sync the bundled guides into your KB");
|
|
80856
|
+
println(c.dim(" (order matters: the new client may require the new schema — run the deploy first)"));
|
|
80671
80857
|
}
|
|
80672
80858
|
function registerSelfUpdate(program2) {
|
|
80673
80859
|
const desc = "Upgrade Cerefox in place. Alias: `cerefox upgrade`.";
|
|
@@ -85301,6 +85487,7 @@ function registerDocumentReadRoutes(app, ctx) {
|
|
|
85301
85487
|
});
|
|
85302
85488
|
}
|
|
85303
85489
|
// src/web/routes/documents-write.ts
|
|
85490
|
+
init__document_meta();
|
|
85304
85491
|
init__utils();
|
|
85305
85492
|
async function createAuditEntry(ctx, args) {
|
|
85306
85493
|
try {
|
|
@@ -85316,16 +85503,6 @@ async function createAuditEntry(ctx, args) {
|
|
|
85316
85503
|
});
|
|
85317
85504
|
} catch {}
|
|
85318
85505
|
}
|
|
85319
|
-
async function assignDocumentProjects(ctx, documentId, projectIds) {
|
|
85320
|
-
await ctx.supabase.from("cerefox_document_projects").delete().eq("document_id", documentId);
|
|
85321
|
-
if (projectIds.length > 0) {
|
|
85322
|
-
const rows = projectIds.map((pid) => ({
|
|
85323
|
-
document_id: documentId,
|
|
85324
|
-
project_id: pid
|
|
85325
|
-
}));
|
|
85326
|
-
await ctx.supabase.from("cerefox_document_projects").insert(rows);
|
|
85327
|
-
}
|
|
85328
|
-
}
|
|
85329
85506
|
async function getCurrentDoc(ctx, documentId) {
|
|
85330
85507
|
const { data } = await ctx.supabase.from("cerefox_documents").select("*").eq("id", documentId).maybeSingle();
|
|
85331
85508
|
return data ?? null;
|
|
@@ -85378,7 +85555,7 @@ function registerDocumentWriteRoutes(app, ctx) {
|
|
|
85378
85555
|
title: title || doc2.title,
|
|
85379
85556
|
source: "manual",
|
|
85380
85557
|
projectIds: Array.isArray(body.project_ids) ? body.project_ids : undefined,
|
|
85381
|
-
metadata:
|
|
85558
|
+
metadata: body.metadata != null ? metadata : undefined,
|
|
85382
85559
|
author: "web-ui",
|
|
85383
85560
|
authorType: "user",
|
|
85384
85561
|
expectedContentHash: typeof body.expected_content_hash === "string" ? body.expected_content_hash : null
|
|
@@ -85419,33 +85596,28 @@ function registerDocumentWriteRoutes(app, ctx) {
|
|
|
85419
85596
|
return c2.json({ success: false, error: msg }, 500);
|
|
85420
85597
|
}
|
|
85421
85598
|
}
|
|
85422
|
-
|
|
85423
|
-
|
|
85424
|
-
updates.title = title;
|
|
85425
|
-
}
|
|
85426
|
-
if (Object.keys(metadata).length > 0) {
|
|
85427
|
-
updates.metadata = metadata;
|
|
85428
|
-
}
|
|
85429
|
-
if (Object.keys(updates).length > 0) {
|
|
85430
|
-
updates.updated_at = new Date().toISOString();
|
|
85431
|
-
const { error: error3 } = await ctx.supabase.from("cerefox_documents").update(updates).eq("id", documentId);
|
|
85432
|
-
if (error3)
|
|
85433
|
-
return c2.json({ success: false, error: error3.message }, 500);
|
|
85434
|
-
}
|
|
85435
|
-
const projectsTouched = Array.isArray(body.project_ids);
|
|
85436
|
-
if (projectsTouched) {
|
|
85437
|
-
await assignDocumentProjects(ctx, documentId, projectIds);
|
|
85438
|
-
}
|
|
85439
|
-
const anythingChanged = "title" in updates || "metadata" in updates || projectsTouched;
|
|
85440
|
-
if (anythingChanged) {
|
|
85441
|
-
await createAuditEntry(ctx, {
|
|
85442
|
-
operation: "update-metadata",
|
|
85443
|
-
author: "web-ui",
|
|
85599
|
+
try {
|
|
85600
|
+
const facets = await updateDocumentFacets(ctx.supabase, {
|
|
85444
85601
|
documentId,
|
|
85445
|
-
|
|
85602
|
+
title: body.title !== undefined && title !== doc2.title ? title : undefined,
|
|
85603
|
+
metadata: body.metadata != null ? metadata : undefined,
|
|
85604
|
+
projectIds: Array.isArray(body.project_ids) ? projectIds : undefined,
|
|
85605
|
+
author: "web-ui",
|
|
85606
|
+
authorType: "user",
|
|
85607
|
+
accessPath: "webapp"
|
|
85446
85608
|
});
|
|
85609
|
+
return c2.json({ success: true, reindexed: false, ...facets });
|
|
85610
|
+
} catch (err) {
|
|
85611
|
+
const cause = err instanceof FacetUpdateError ? err.cause : err;
|
|
85612
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
85613
|
+
if (cause instanceof FacetNotFoundError) {
|
|
85614
|
+
return c2.json({ success: false, error: msg, detail: msg }, 404);
|
|
85615
|
+
}
|
|
85616
|
+
if (cause instanceof FacetValidationError) {
|
|
85617
|
+
return c2.json({ success: false, error: msg, detail: msg }, 400);
|
|
85618
|
+
}
|
|
85619
|
+
return c2.json({ success: false, error: msg, detail: msg }, 500);
|
|
85447
85620
|
}
|
|
85448
|
-
return c2.json({ success: true, reindexed: false });
|
|
85449
85621
|
});
|
|
85450
85622
|
app.delete("/api/v1/documents/:document_id", async (c2) => {
|
|
85451
85623
|
const documentId = c2.req.param("document_id");
|