@omgbase/core 0.5.0 → 0.6.1
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/src/graph/history.d.ts +10 -2
- package/dist/src/graph/history.js +20 -4
- package/dist/src/mcp/server.js +3 -3
- package/dist/src/mutate/docs.d.ts +19 -9
- package/dist/src/mutate/docs.js +7 -4
- package/dist/src/spec-versions.d.ts +2 -2
- package/dist/src/spec-versions.js +2 -2
- package/package.json +1 -1
|
@@ -25,8 +25,16 @@ export interface DiffEntry {
|
|
|
25
25
|
after?: string;
|
|
26
26
|
}
|
|
27
27
|
export declare function diffBlocks(store: Store, docId: string, fromRev: string, toRev: string): DiffEntry[];
|
|
28
|
-
/**
|
|
29
|
-
*
|
|
28
|
+
/**
|
|
29
|
+
* `diff_unified` (spec/surface §3): a unified diff of the two revisions'
|
|
30
|
+
* **reconstructed file text** — exactly the bytes `docs_read_at` returns for
|
|
31
|
+
* each revision ({@link readDocumentAtRevision}: frontmatter, trivia and
|
|
32
|
+
* top-level order included) — via {@link unifiedDiff}, so hunk line numbers
|
|
33
|
+
* are real file lines. Until 1.4.x this diffed the Merkle-tree walk (every
|
|
34
|
+
* live raw at every depth joined by `\n`), which repeated each nested block
|
|
35
|
+
* after its container's raw and put an appended list item before the list's
|
|
36
|
+
* first item (§9). An unknown revision is {@link RevisionNotFound}.
|
|
37
|
+
*/
|
|
30
38
|
export declare function diffUnified(store: Store, docId: string, fromRev: string, toRev: string): string;
|
|
31
39
|
/** The `diff_unified` tool's result (spec/surface §3): the resolved revision pair and the diff. */
|
|
32
40
|
export interface DocDiffUnified {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { findDocByRef } from "../core/read/reader.js";
|
|
2
|
+
import { readDocumentAtRevision } from "../core/read/document.js";
|
|
2
3
|
import { isValidId } from "../core/ids.js";
|
|
3
4
|
import { EngineError } from "../mcp/errors.js";
|
|
4
5
|
/** history_node: a block's biography from block_changes + commits (06 §3). */
|
|
@@ -50,7 +51,9 @@ export function diffBlocks(store, docId, fromRev, toRev) {
|
|
|
50
51
|
// core/hash.ts serializeTreeEntries): [blockId, rawHashHex, childTreeHashHex,
|
|
51
52
|
// type, attrs, triviaHashHex]. Diff needs only the first three; whole-document
|
|
52
53
|
// byte-faithful reconstruction (which also needs trivia + top-level order) lives
|
|
53
|
-
// in core/read/document.ts readDocumentAtRevision.
|
|
54
|
+
// in core/read/document.ts readDocumentAtRevision. This map is NOT a rendering
|
|
55
|
+
// of the document: a container's raw already holds its children's text, so
|
|
56
|
+
// joining the values would repeat every nested block (spec/surface §9).
|
|
54
57
|
function blocksAtRevision(store, docId, revId) {
|
|
55
58
|
const rev = store.db.prepare("SELECT root_tree FROM revisions WHERE rev_id = ? AND doc_id = ?").get(revId, docId);
|
|
56
59
|
if (!rev)
|
|
@@ -73,10 +76,23 @@ function blocksAtRevision(store, docId, revId) {
|
|
|
73
76
|
walk(rev.root_tree.toString("hex"));
|
|
74
77
|
return out;
|
|
75
78
|
}
|
|
76
|
-
/**
|
|
77
|
-
*
|
|
79
|
+
/**
|
|
80
|
+
* `diff_unified` (spec/surface §3): a unified diff of the two revisions'
|
|
81
|
+
* **reconstructed file text** — exactly the bytes `docs_read_at` returns for
|
|
82
|
+
* each revision ({@link readDocumentAtRevision}: frontmatter, trivia and
|
|
83
|
+
* top-level order included) — via {@link unifiedDiff}, so hunk line numbers
|
|
84
|
+
* are real file lines. Until 1.4.x this diffed the Merkle-tree walk (every
|
|
85
|
+
* live raw at every depth joined by `\n`), which repeated each nested block
|
|
86
|
+
* after its container's raw and put an appended list item before the list's
|
|
87
|
+
* first item (§9). An unknown revision is {@link RevisionNotFound}.
|
|
88
|
+
*/
|
|
78
89
|
export function diffUnified(store, docId, fromRev, toRev) {
|
|
79
|
-
const rendered = (rev) =>
|
|
90
|
+
const rendered = (rev) => {
|
|
91
|
+
const at = readDocumentAtRevision(store, docId, rev);
|
|
92
|
+
if (!at)
|
|
93
|
+
throw new RevisionNotFound(docId, rev);
|
|
94
|
+
return at.content;
|
|
95
|
+
};
|
|
80
96
|
return unifiedDiff(rendered(fromRev), rendered(toRev));
|
|
81
97
|
}
|
|
82
98
|
/**
|
package/dist/src/mcp/server.js
CHANGED
|
@@ -809,11 +809,11 @@ export function buildServer(ctx) {
|
|
|
809
809
|
}
|
|
810
810
|
});
|
|
811
811
|
server.registerTool("docs_move", {
|
|
812
|
-
description: "Rename a document to a new repo-relative path; block identity and history are preserved
|
|
812
|
+
description: "Rename a document to a new repo-relative path; block identity and history are preserved, and the inbound links FOLLOW it: every link written against the old path (Markdown links/images, wikilinks, bare-path inline fields — anchors, link text, titles and code spans preserved, absolute vs relative style kept) is rewritten to the new path in the same call as one CAS-checked changeset, so a move never leaves the graph broken. Fails path_taken if the destination exists. The result carries `retargeted` (the touched blocks/docs) and `dangling` — what could NOT be rewritten, i.e. frontmatter relations (block null; fix with docs_set_meta). Pass `retarget_inbound:false` to opt out for the rarer intent that the old path become unbound: the links are left as written, listed in `dangling` ({doc, path, block, target, anchor, field?} per occurrence) and reported by links_stale (their edges become `phantom:<old path>`); links already written against the new path start resolving to this doc either way. dry_run:true validates and returns the plan — `dangling`, `retargeted`, and the per-file `diffs` (the old path emptied, the new path filled, plus the rewritten sources) — without renaming or committing.",
|
|
813
813
|
inputSchema: { doc: z.string(), to_path: z.string(), retarget_inbound: z.boolean().optional(), dry_run: z.boolean().optional(), ...REPO_ARG },
|
|
814
814
|
}, async (args) => {
|
|
815
815
|
try {
|
|
816
|
-
return docResult(docsMove(store, docCtx(repoScope(args.repo), args.dry_run), args.doc, args.to_path, { retargetInbound: args.retarget_inbound
|
|
816
|
+
return docResult(docsMove(store, docCtx(repoScope(args.repo), args.dry_run), args.doc, args.to_path, { retargetInbound: args.retarget_inbound !== false }), args.dry_run);
|
|
817
817
|
}
|
|
818
818
|
catch (e) {
|
|
819
819
|
return fail(e);
|
|
@@ -940,7 +940,7 @@ export function buildServer(ctx) {
|
|
|
940
940
|
}
|
|
941
941
|
});
|
|
942
942
|
server.registerTool("diff_unified", {
|
|
943
|
-
description: "Unified diff between two revisions of a document (the `omg diff` rendering): returns `{ doc, path, from, to, diff }` where `diff` is a unified diff of the two
|
|
943
|
+
description: "Unified diff between two revisions of a document (the `omg diff` rendering): returns `{ doc, path, from, to, diff }` where `diff` is a unified diff of the two revisions' file texts — for each revision exactly the bytes docs_read_at returns, so hunk line numbers are real file lines (Myers shortest edit script; `@@ -a,b +c,d @@` hunks with 3 lines of context; lines prefixed `-`, `+` or a space; no file header; identical texts → \"\"). `from_rev`/`to_rev` default to the previous and current revisions ('what did the last commit change here'). Contrast `diff`, which returns block-grain added/removed/changed entries.",
|
|
944
944
|
inputSchema: { doc: z.string(), from_rev: z.string().optional(), to_rev: z.string().optional(), ...REPO_ARG },
|
|
945
945
|
}, async (args) => {
|
|
946
946
|
try {
|
|
@@ -40,22 +40,30 @@ export interface DocOpResult {
|
|
|
40
40
|
}
|
|
41
41
|
export interface DocMoveOptions {
|
|
42
42
|
/**
|
|
43
|
-
*
|
|
44
|
-
*
|
|
43
|
+
* Rewrite the inbound links that named the OLD path so they point at the new
|
|
44
|
+
* one — a destination-aware rewrite (anchors, link text, titles, and code
|
|
45
45
|
* spans preserved; absolute/relative style kept) applied as a CAS-checked
|
|
46
|
-
* `update` per source block in one follow-up api changeset.
|
|
47
|
-
*
|
|
46
|
+
* `update` per source block in one follow-up api changeset. **Default `true`**
|
|
47
|
+
* (spec/mutate 1.3): a move means "same document, new place", so the authored
|
|
48
|
+
* links follow it; pass `false` for the rarer intent that the old path become
|
|
49
|
+
* unbound. Frontmatter-level relations are never rewritten (reported in
|
|
50
|
+
* `dangling`).
|
|
48
51
|
*/
|
|
49
52
|
retargetInbound?: boolean;
|
|
50
53
|
}
|
|
51
54
|
export interface DocMoveResult extends DocOpResult {
|
|
52
55
|
/**
|
|
53
56
|
* Inbound links still pointing at the OLD path after this call — the source
|
|
54
|
-
* blocks `links_stale` will now report (
|
|
55
|
-
*
|
|
57
|
+
* blocks `links_stale` will now report (with the default retarget: only the
|
|
58
|
+
* frontmatter relations, which are never rewritten). Computed from the open
|
|
59
|
+
* edge index before the move.
|
|
56
60
|
*/
|
|
57
61
|
dangling: InboundLink[];
|
|
58
|
-
/**
|
|
62
|
+
/**
|
|
63
|
+
* The source blocks rewritten and the docs re-ingested (empty lists when the
|
|
64
|
+
* rewrite ran but only frontmatter relations linked in); `null` when the
|
|
65
|
+
* retarget was opted out (`retargetInbound: false`) or nothing linked in.
|
|
66
|
+
*/
|
|
59
67
|
retargeted: {
|
|
60
68
|
blocks: string[];
|
|
61
69
|
docs: string[];
|
|
@@ -68,8 +76,10 @@ export declare function docsCreate(store: Store, ctx: DocOpContext, path: string
|
|
|
68
76
|
* index follows the path, not the identity: links written against the OLD path
|
|
69
77
|
* now dangle (their open edges are re-pointed at `phantom:<old>`, exactly what
|
|
70
78
|
* re-extracting the source would produce, so `links_stale` sees them), and
|
|
71
|
-
* phantom edges already written against the NEW path are adopted.
|
|
72
|
-
*
|
|
79
|
+
* phantom edges already written against the NEW path are adopted. By default
|
|
80
|
+
* the dangling inbound links are then rewritten to the new path in one
|
|
81
|
+
* follow-up changeset (spec/mutate 1.3); `retargetInbound: false` leaves them
|
|
82
|
+
* as written and the result lists them.
|
|
73
83
|
*/
|
|
74
84
|
export declare function docsMove(store: Store, ctx: DocOpContext, docRef: string, toPath: string, opts?: DocMoveOptions): DocMoveResult;
|
|
75
85
|
/** docs_delete: mark a document deleted and remove its file (resurrection-poolable). */
|
package/dist/src/mutate/docs.js
CHANGED
|
@@ -60,10 +60,13 @@ export function docsCreate(store, ctx, path, markdown, frontmatter) {
|
|
|
60
60
|
* index follows the path, not the identity: links written against the OLD path
|
|
61
61
|
* now dangle (their open edges are re-pointed at `phantom:<old>`, exactly what
|
|
62
62
|
* re-extracting the source would produce, so `links_stale` sees them), and
|
|
63
|
-
* phantom edges already written against the NEW path are adopted.
|
|
64
|
-
*
|
|
63
|
+
* phantom edges already written against the NEW path are adopted. By default
|
|
64
|
+
* the dangling inbound links are then rewritten to the new path in one
|
|
65
|
+
* follow-up changeset (spec/mutate 1.3); `retargetInbound: false` leaves them
|
|
66
|
+
* as written and the result lists them.
|
|
65
67
|
*/
|
|
66
68
|
export function docsMove(store, ctx, docRef, toPath, opts = {}) {
|
|
69
|
+
const retargetInbound = opts.retargetInbound !== false;
|
|
67
70
|
const info = findDocByRef(store, ctx.repoId, docRef);
|
|
68
71
|
if (!info)
|
|
69
72
|
throw new MutationError("doc_missing", `no document ${docRef}`);
|
|
@@ -79,7 +82,7 @@ export function docsMove(store, ctx, docRef, toPath, opts = {}) {
|
|
|
79
82
|
const content = docStore.read(info.path) ?? "";
|
|
80
83
|
const diffs = { [info.path]: { before: content, after: "" }, [toRel]: { before: "", after: content } };
|
|
81
84
|
const planned = { docId: info.docId, path: toRel, committed: false, diffs };
|
|
82
|
-
if (!
|
|
85
|
+
if (!retargetInbound || inbound.length === 0)
|
|
83
86
|
return { ...planned, dangling: inbound, retargeted: null };
|
|
84
87
|
const plan = planInboundRetarget(store, info.docId, info.path, toRel, inbound);
|
|
85
88
|
if (plan.ops.length > 0) {
|
|
@@ -134,7 +137,7 @@ export function docsMove(store, ctx, docRef, toPath, opts = {}) {
|
|
|
134
137
|
}
|
|
135
138
|
return { docId: info.docId, path: toRel, committed: true };
|
|
136
139
|
});
|
|
137
|
-
if (!
|
|
140
|
+
if (!retargetInbound || inbound.length === 0)
|
|
138
141
|
return { ...moved, dangling: inbound, retargeted: null };
|
|
139
142
|
// Rewrite the dangling links block by block (one coalesced update per block,
|
|
140
143
|
// CAS on the block's current hash) and apply as one changeset; the re-ingest
|
|
@@ -6,9 +6,9 @@ export declare const SPEC_VERSIONS: {
|
|
|
6
6
|
readonly properties: "1.1";
|
|
7
7
|
readonly graph: "1.1";
|
|
8
8
|
readonly search: "1.2";
|
|
9
|
-
readonly mutate: "1.
|
|
9
|
+
readonly mutate: "1.3";
|
|
10
10
|
readonly sync: "1.3";
|
|
11
11
|
readonly surface: "1.4";
|
|
12
|
-
readonly cli: "1.
|
|
12
|
+
readonly cli: "1.2";
|
|
13
13
|
};
|
|
14
14
|
export type SpecName = keyof typeof SPEC_VERSIONS;
|