omgbase 0.25.0 → 0.26.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/build-info.json +2 -2
- package/dist/src/cmd/_mutate.d.ts +3 -1
- package/dist/src/cmd/_mutate.js +6 -4
- package/dist/src/cmd/_remote.d.ts +7 -5
- package/dist/src/cmd/_remote.js +7 -5
- package/dist/src/cmd/cat.js +5 -5
- package/dist/src/cmd/diff.js +4 -4
- package/dist/src/cmd/docs.js +0 -0
- package/dist/src/cmd/edit.js +2 -2
- package/dist/src/cmd/find.js +6 -5
- package/dist/src/cmd/hist.js +1 -1
- package/dist/src/cmd/links.js +10 -7
- package/dist/src/cmd/ls.js +6 -6
- package/dist/src/cmd/mutate.js +23 -19
- package/dist/src/cmd/node.js +2 -2
- package/dist/src/cmd/oqx.d.ts +2 -2
- package/dist/src/cmd/oqx.js +36 -33
- package/dist/src/cmd/outline.js +2 -2
- package/dist/src/cmd/retarget.js +1 -1
- package/dist/src/cmd/run.js +2 -2
- package/dist/src/cmd/show.js +16 -16
- package/dist/src/cmd/update.js +6 -5
- package/dist/src/output.d.ts +6 -6
- package/dist/src/output.js +11 -11
- package/dist/src/shell/refs.js +17 -12
- package/dist/src/shell/session.js +2 -2
- package/package.json +3 -3
package/dist/build-info.json
CHANGED
|
@@ -52,7 +52,9 @@ export declare function runSugar(cli: Cli, ws: Workspace, repo: RepoRow, sugar:
|
|
|
52
52
|
* `refs` are the refs the verb received, refused there when one is a locator.
|
|
53
53
|
*/
|
|
54
54
|
export declare function runSugarRemote(cli: Cli, tool: string, toolArgs: Record<string, unknown>, effects: () => unknown[] | Promise<unknown[]>, refs?: string[]): Promise<number>;
|
|
55
|
-
/** A ref as an effect target for a remote `plan
|
|
55
|
+
/** A ref as an effect target for a remote `plan` (spec/mutate §11, 4.0: the address is the
|
|
56
|
+
* row's engine attribute): a `b_` id is the block (`_id`), a `d_` id the document (`_id`),
|
|
57
|
+
* anything else a document path (`_path`). */
|
|
56
58
|
export declare function targetOf(ref: string): EffectTarget;
|
|
57
59
|
/**
|
|
58
60
|
* `apply` given `{ effects }`: plan + execute as one transaction
|
package/dist/src/cmd/_mutate.js
CHANGED
|
@@ -162,13 +162,15 @@ export async function runSugarRemote(cli, tool, toolArgs, effects, refs = []) {
|
|
|
162
162
|
return renderPlan(cli, await remoteCall(cli, "plan", { effects: await effects(), diffs: true }));
|
|
163
163
|
return renderReceipt(cli, await remoteSugar(cli, tool, toolArgs, effects, refs));
|
|
164
164
|
}
|
|
165
|
-
/** A ref as an effect target for a remote `plan
|
|
165
|
+
/** A ref as an effect target for a remote `plan` (spec/mutate §11, 4.0: the address is the
|
|
166
|
+
* row's engine attribute): a `b_` id is the block (`_id`), a `d_` id the document (`_id`),
|
|
167
|
+
* anything else a document path (`_path`). */
|
|
166
168
|
export function targetOf(ref) {
|
|
167
169
|
if (isValidId(ref, "b"))
|
|
168
|
-
return { kind: "block",
|
|
170
|
+
return { kind: "block", _id: ref };
|
|
169
171
|
if (isValidId(ref, "d"))
|
|
170
|
-
return { kind: "document",
|
|
171
|
-
return { kind: "document",
|
|
172
|
+
return { kind: "document", _id: ref };
|
|
173
|
+
return { kind: "document", _path: ref };
|
|
172
174
|
}
|
|
173
175
|
// ---- the Effect layer (ADR-021; spec/cli 3.2 `apply -f { effects }`, `execute`) ----
|
|
174
176
|
/**
|
|
@@ -25,13 +25,15 @@ export declare function isLocator(ref: string): boolean;
|
|
|
25
25
|
* usage error there rather than a bogus document path.
|
|
26
26
|
*/
|
|
27
27
|
export declare function remoteSugar<T>(cli: Cli, tool: string, toolArgs: Record<string, unknown>, effects: () => unknown[] | Promise<unknown[]>, refs?: string[]): Promise<T>;
|
|
28
|
-
/** A document ref as the surface's locator (spec/surface §4 "Arguments",
|
|
29
|
-
* a `d_` id is the `
|
|
30
|
-
* `docs_*` tool takes (one of them required
|
|
28
|
+
/** A document ref as the surface's locator (spec/surface §4 "Arguments", 5.0):
|
|
29
|
+
* a `d_` id is the `_id` argument, anything else the `_path` — the two names a
|
|
30
|
+
* `docs_*` tool takes (one of them required; the bare `id`/`path` are refused
|
|
31
|
+
* there as `filter_invalid`). The same pair is a document effect target's
|
|
32
|
+
* address (`{ kind: "document", _id | _path }`, spec/mutate §11). */
|
|
31
33
|
export declare function docLocator(ref: string): {
|
|
32
|
-
|
|
34
|
+
_id: string;
|
|
33
35
|
} | {
|
|
34
|
-
|
|
36
|
+
_path: string;
|
|
35
37
|
};
|
|
36
38
|
/** The live document a ref names, for a LOCAL verb, or the surface's own
|
|
37
39
|
* `doc_missing` ("no document with id or path "<ref>"", `data` the locator) —
|
package/dist/src/cmd/_remote.js
CHANGED
|
@@ -85,11 +85,13 @@ export async function remoteSugar(cli, tool, toolArgs, effects, refs = []) {
|
|
|
85
85
|
const reason = typeof toolArgs.reason === "string" ? toolArgs.reason : tool;
|
|
86
86
|
return remoteCall(cli, "apply", { effects: await effects(), reason });
|
|
87
87
|
}
|
|
88
|
-
/** A document ref as the surface's locator (spec/surface §4 "Arguments",
|
|
89
|
-
* a `d_` id is the `
|
|
90
|
-
* `docs_*` tool takes (one of them required
|
|
88
|
+
/** A document ref as the surface's locator (spec/surface §4 "Arguments", 5.0):
|
|
89
|
+
* a `d_` id is the `_id` argument, anything else the `_path` — the two names a
|
|
90
|
+
* `docs_*` tool takes (one of them required; the bare `id`/`path` are refused
|
|
91
|
+
* there as `filter_invalid`). The same pair is a document effect target's
|
|
92
|
+
* address (`{ kind: "document", _id | _path }`, spec/mutate §11). */
|
|
91
93
|
export function docLocator(ref) {
|
|
92
|
-
return isValidId(ref, "d") ? {
|
|
94
|
+
return isValidId(ref, "d") ? { _id: ref } : { _path: ref };
|
|
93
95
|
}
|
|
94
96
|
/** The live document a ref names, for a LOCAL verb, or the surface's own
|
|
95
97
|
* `doc_missing` ("no document with id or path "<ref>"", `data` the locator) —
|
|
@@ -105,7 +107,7 @@ export function requireDoc(store, repoId, ref) {
|
|
|
105
107
|
export function requireNode(store, repoId, nodeId) {
|
|
106
108
|
const row = store.db.prepare("SELECT 1 FROM nodes WHERE node_id = ? AND repo_id = ?").get(nodeId, repoId);
|
|
107
109
|
if (!row)
|
|
108
|
-
throw new EngineErrorLike("block_missing", `no node with id ${JSON.stringify(nodeId)}`, { data: {
|
|
110
|
+
throw new EngineErrorLike("block_missing", `no node with id ${JSON.stringify(nodeId)}`, { data: { _id: nodeId } });
|
|
109
111
|
return nodeId;
|
|
110
112
|
}
|
|
111
113
|
/** Close the remote connection if one was opened (idempotent). Called after a
|
package/dist/src/cmd/cat.js
CHANGED
|
@@ -49,12 +49,12 @@ async function catOne(cli, ref, resolution) {
|
|
|
49
49
|
if (cli.flags.server) {
|
|
50
50
|
const tool = blockReadTool(ref);
|
|
51
51
|
if (tool) {
|
|
52
|
-
const r = await remoteCall(cli, tool, {
|
|
53
|
-
return { text: String(r.
|
|
52
|
+
const r = await remoteCall(cli, tool, { _id: ref, resolution });
|
|
53
|
+
return { text: String(r._raw ?? r._text ?? r.label ?? ""), json: r, capture: r };
|
|
54
54
|
}
|
|
55
55
|
const r = await remoteCall(cli, "docs_read", docLocator(ref));
|
|
56
56
|
warnDocResolution(cli, ref, resolution);
|
|
57
|
-
return { text: String(r.
|
|
57
|
+
return { text: String(r._raw ?? ""), json: r, capture: r._raw };
|
|
58
58
|
}
|
|
59
59
|
const ws = cli.workspace();
|
|
60
60
|
const repo = cli.repo(ws);
|
|
@@ -72,13 +72,13 @@ async function catOne(cli, ref, resolution) {
|
|
|
72
72
|
throw new EngineErrorLike("doc_missing", `no document ${ref}`);
|
|
73
73
|
warnDocResolution(cli, ref, resolution);
|
|
74
74
|
// shell: the bytes (a string; @_ only). `--json` is the `docs_read` result.
|
|
75
|
-
return { text: res.
|
|
75
|
+
return { text: res._raw, json: res, capture: res._raw };
|
|
76
76
|
}
|
|
77
77
|
const node = nodesGet(ws.store, resolved.docId, resolved.blockId, { resolution });
|
|
78
78
|
if (!node)
|
|
79
79
|
throw new EngineErrorLike("block_missing", `no block ${ref}`);
|
|
80
80
|
// shell: the block (single entity)
|
|
81
|
-
return { text: node.
|
|
81
|
+
return { text: node._raw ?? node._text ?? node.label ?? "", json: node, capture: node };
|
|
82
82
|
}
|
|
83
83
|
async function runCat(cli, args) {
|
|
84
84
|
const { values, positionals } = parseArgs({
|
package/dist/src/cmd/diff.js
CHANGED
|
@@ -3,7 +3,7 @@ import { findDoc, docDiffUnified, diffBlocks } from "@omgbase/core";
|
|
|
3
3
|
import { CliUsageError, EngineErrorLike, EXIT_OK, emitMachine, renderHelp } from "../output.js";
|
|
4
4
|
import { remoteCall, docLocator } from "./_remote.js";
|
|
5
5
|
// `omg diff <doc>` (11 §5.5) — `docs_diff_unified`: the unified diff, `--json` the
|
|
6
|
-
// tool's `{
|
|
6
|
+
// tool's `{ _id, _path, from, to, diff }` (spec/surface §3, 5.0). With no revisions: current vs previous
|
|
7
7
|
// ("what did the last commit do here"). `--blocks` is the block-grain `diff`
|
|
8
8
|
// tool: added/removed/changed blocks between the same two revisions.
|
|
9
9
|
async function runDiff(cli, args) {
|
|
@@ -54,9 +54,9 @@ async function runDiff(cli, args) {
|
|
|
54
54
|
if (cli.flags.server)
|
|
55
55
|
entries = await remoteCall(cli, "docs_diff", { ...docLocator(ref), from_rev: result.from, to_rev: result.to });
|
|
56
56
|
else
|
|
57
|
-
entries = diffBlocks(cli.workspace().store, result.
|
|
57
|
+
entries = diffBlocks(cli.workspace().store, result._id, result.from, result.to);
|
|
58
58
|
if (cli.flags.mode !== "human")
|
|
59
|
-
return emitMachine(cli, entries, { items: entries, ids: entries.map((e) => e.
|
|
59
|
+
return emitMachine(cli, entries, { items: entries, ids: entries.map((e) => e._id) });
|
|
60
60
|
if (entries.length === 0) {
|
|
61
61
|
io.err(style.dim(" no changes"));
|
|
62
62
|
return EXIT_OK;
|
|
@@ -64,7 +64,7 @@ async function runDiff(cli, args) {
|
|
|
64
64
|
for (const e of entries) {
|
|
65
65
|
const mark = e.kind === "added" ? style.ok("+") : e.kind === "removed" ? style.err("-") : style.warn("~");
|
|
66
66
|
const preview = (e.after ?? e.before ?? "").split("\n")[0] ?? "";
|
|
67
|
-
io.out(`${mark} ${style.id(e.
|
|
67
|
+
io.out(`${mark} ${style.id(e._id)} ${preview}`.trimEnd());
|
|
68
68
|
}
|
|
69
69
|
return EXIT_OK;
|
|
70
70
|
}
|
package/dist/src/cmd/docs.js
CHANGED
|
Binary file
|
package/dist/src/cmd/edit.js
CHANGED
|
@@ -38,10 +38,10 @@ function runEdit(cli, args) {
|
|
|
38
38
|
const repo = cli.repo(ws);
|
|
39
39
|
const resolved = resolveRef(ws.store, repo.repoId, ref);
|
|
40
40
|
if (!resolved || resolved.kind !== "block")
|
|
41
|
-
throw new EngineErrorLike("block_missing", `no block with id ${JSON.stringify(ref)}`, { data: {
|
|
41
|
+
throw new EngineErrorLike("block_missing", `no block with id ${JSON.stringify(ref)}`, { data: { _id: ref } });
|
|
42
42
|
const blockId = resolved.blockId;
|
|
43
43
|
const node = nodesGet(ws.store, resolved.docId, blockId, { resolution: "raw" });
|
|
44
|
-
const before = node?.
|
|
44
|
+
const before = node?._raw ?? "";
|
|
45
45
|
const preHash = rawHashOf(ws, blockId); // pin BEFORE the editor opens
|
|
46
46
|
const editor = process.env.OMG_EDITOR ?? process.env.VISUAL ?? process.env.EDITOR;
|
|
47
47
|
if (!editor)
|
package/dist/src/cmd/find.js
CHANGED
|
@@ -3,8 +3,9 @@ import { resolve as resolveThing, surfaceResolveHits } from "@omgbase/core";
|
|
|
3
3
|
import { CliUsageError, EXIT_OK, emitMachine, renderHelp } from "../output.js";
|
|
4
4
|
import { loadEmbedding } from "./_embed.js";
|
|
5
5
|
import { remoteCall } from "./_remote.js";
|
|
6
|
-
// `omg find <text>` (11 §5.2) — resolve: ranked {
|
|
7
|
-
// evidence}. `-1` prints the top hit's
|
|
6
|
+
// `omg find <text>` (11 §5.2) — resolve: ranked {_id, locator, preview,
|
|
7
|
+
// evidence} (spec/surface §4 `blocks_resolve`, 5.0). `-1` prints the top hit's
|
|
8
|
+
// id alone (pipe fuel: omg cat $(omg find … -1)).
|
|
8
9
|
async function runFind(cli, args) {
|
|
9
10
|
const { values, positionals } = parseArgs({
|
|
10
11
|
args,
|
|
@@ -65,18 +66,18 @@ async function runFind(cli, args) {
|
|
|
65
66
|
// -1: bare top id.
|
|
66
67
|
if (values.one) {
|
|
67
68
|
if (hits[0])
|
|
68
|
-
cli.io.out(hits[0].
|
|
69
|
+
cli.io.out(hits[0]._id);
|
|
69
70
|
return EXIT_OK;
|
|
70
71
|
}
|
|
71
72
|
if (cli.flags.mode !== "human")
|
|
72
|
-
return emitMachine(cli, hits, { items: hits, ids: hits.map((h) => h.
|
|
73
|
+
return emitMachine(cli, hits, { items: hits, ids: hits.map((h) => h._id) });
|
|
73
74
|
const { style, io } = cli;
|
|
74
75
|
if (hits.length === 0) {
|
|
75
76
|
io.err(style.dim(" no matches"));
|
|
76
77
|
return EXIT_OK;
|
|
77
78
|
}
|
|
78
79
|
for (const h of hits) {
|
|
79
|
-
io.out(`${style.id(h.
|
|
80
|
+
io.out(`${style.id(h._id)} ${style.accent(h.locator)} ${h.preview}`);
|
|
80
81
|
if (values.verbose)
|
|
81
82
|
io.err(style.dim(` ${JSON.stringify(h.evidence)}`));
|
|
82
83
|
}
|
package/dist/src/cmd/hist.js
CHANGED
|
@@ -24,7 +24,7 @@ async function runHist(cli, args) {
|
|
|
24
24
|
if (cli.flags.server) {
|
|
25
25
|
// Remote: blocks_history keys on a block id (globally unique). Pass the ref
|
|
26
26
|
// through — a locator would need a local resolve, so remote hist wants an id.
|
|
27
|
-
changes = await remoteCall(cli, "blocks_history", {
|
|
27
|
+
changes = await remoteCall(cli, "blocks_history", { _id: ref, ...(values.n ? { limit: Number(values.n) } : {}) });
|
|
28
28
|
}
|
|
29
29
|
else {
|
|
30
30
|
const ws = cli.workspace();
|
package/dist/src/cmd/links.js
CHANGED
|
@@ -3,7 +3,10 @@ import { resolveRef, docLinks } from "@omgbase/core";
|
|
|
3
3
|
import { CliUsageError, EngineErrorLike, EXIT_OK, emitMachine, renderHelp } from "../output.js";
|
|
4
4
|
// `omg links <node>` (11 §5.4) — open links touching the node; default both
|
|
5
5
|
// directions, grouped; doc-grain by default, --blocks for block-grain (one row
|
|
6
|
-
// per edge, naming the source block). Backlinks = --in.
|
|
6
|
+
// per edge, naming the source block). Backlinks = --in. A row is
|
|
7
|
+
// `{ _predicate, _doc_id, kind, count, samples? | _block_id }` (spec/cli 5.0):
|
|
8
|
+
// the keys that carry a links-row attribute are `_`-prefixed; `kind` (what the
|
|
9
|
+
// far node is), `count` (the rollup) and `samples` name roles and stay bare.
|
|
7
10
|
function runLinks(cli, args) {
|
|
8
11
|
const { values, positionals } = parseArgs({
|
|
9
12
|
args,
|
|
@@ -47,7 +50,7 @@ function runLinks(cli, args) {
|
|
|
47
50
|
cli.capture?.(result); // shell: edges (out+in) become the addressable frame
|
|
48
51
|
// `{ out, in }` is not a list: `--jsonl` prints the document; `--ids` the far nodes.
|
|
49
52
|
if (cli.flags.mode !== "human")
|
|
50
|
-
return emitMachine(cli, result, { ids: [...result.out, ...result.in].map((e) => e.
|
|
53
|
+
return emitMachine(cli, result, { ids: [...result.out, ...result.in].map((e) => e._doc_id) });
|
|
51
54
|
const { render, style, io } = cli;
|
|
52
55
|
const g = render.g;
|
|
53
56
|
if (result.out.length === 0 && result.in.length === 0) {
|
|
@@ -56,23 +59,23 @@ function runLinks(cli, args) {
|
|
|
56
59
|
}
|
|
57
60
|
// Doc-grain: one row per (predicate, node) with its rollup count. Block-grain:
|
|
58
61
|
// one row per edge, the source block beside the far node.
|
|
59
|
-
const src = (e) => style.dim(`(${e.
|
|
62
|
+
const src = (e) => style.dim(`(${e._block_id ?? "frontmatter"})`);
|
|
60
63
|
if (result.out.length > 0) {
|
|
61
64
|
io.out(style.dim(" out"));
|
|
62
65
|
for (const e of result.out) {
|
|
63
66
|
if (values.blocks)
|
|
64
|
-
io.out(` ${style.accent(e.
|
|
67
|
+
io.out(` ${style.accent(e._predicate)} ${style.dim(g.arrow)} ${style.id(e._doc_id)} ${src(e)}`);
|
|
65
68
|
else
|
|
66
|
-
io.out(` ${style.accent(e.
|
|
69
|
+
io.out(` ${style.accent(e._predicate)} ${style.dim(g.arrow)} ${style.id(e._doc_id)} ${style.dim(`×${e.count}`)}`);
|
|
67
70
|
}
|
|
68
71
|
}
|
|
69
72
|
if (result.in.length > 0) {
|
|
70
73
|
io.out(style.dim(" in (backlinks)"));
|
|
71
74
|
for (const e of result.in) {
|
|
72
75
|
if (values.blocks)
|
|
73
|
-
io.out(` ${style.id(e.
|
|
76
|
+
io.out(` ${style.id(e._doc_id)} ${src(e)} ${style.dim(g.arrow)} ${style.accent(e._predicate)}`);
|
|
74
77
|
else
|
|
75
|
-
io.out(` ${style.id(e.
|
|
78
|
+
io.out(` ${style.id(e._doc_id)} ${style.dim(g.arrow)} ${style.accent(e._predicate)} ${style.dim(`×${e.count}`)}`);
|
|
76
79
|
}
|
|
77
80
|
}
|
|
78
81
|
return EXIT_OK;
|
package/dist/src/cmd/ls.js
CHANGED
|
@@ -38,20 +38,20 @@ async function runLs(cli, args) {
|
|
|
38
38
|
rows.push(...page.items);
|
|
39
39
|
cursor = page.truncated ? page.cursor : null;
|
|
40
40
|
} while (cursor);
|
|
41
|
-
cli.capture?.(rows); // shell: docs become the addressable frame (ref =
|
|
42
|
-
// `--ids` prints the documents' `
|
|
43
|
-
// stand-in for an identity the row did not carry).
|
|
41
|
+
cli.capture?.(rows); // shell: docs become the addressable frame (ref = _id, label _path)
|
|
42
|
+
// `--ids` prints the documents' `_id`s (spec/cli 5.0 — the row's engine
|
|
43
|
+
// attribute; the paths before 3.0, a stand-in for an identity the row did not carry).
|
|
44
44
|
if (cli.flags.mode !== "human")
|
|
45
|
-
return emitMachine(cli, rows, { items: rows, ids: rows.map((r) => r.
|
|
45
|
+
return emitMachine(cli, rows, { items: rows, ids: rows.map((r) => r._id) });
|
|
46
46
|
const { style, io } = cli;
|
|
47
47
|
if (rows.length === 0) {
|
|
48
48
|
io.err(style.dim(" no documents"));
|
|
49
49
|
return EXIT_OK;
|
|
50
50
|
}
|
|
51
51
|
const table = rows.map((r) => [
|
|
52
|
-
style.accent(r.
|
|
52
|
+
style.accent(r._path),
|
|
53
53
|
style.dim(`${r.blocks} blocks`),
|
|
54
|
-
style.dim(relTime(r.
|
|
54
|
+
style.dim(relTime(r._updated_at)),
|
|
55
55
|
]);
|
|
56
56
|
for (const line of columns(table, [{}, { align: "right" }, { align: "right" }]))
|
|
57
57
|
io.out(line);
|
package/dist/src/cmd/mutate.js
CHANGED
|
@@ -12,34 +12,38 @@ import { remoteCall, remoteHasTool } from "./_remote.js";
|
|
|
12
12
|
// conflict rendering). `apply` is the primitive — a changeset of `effects`
|
|
13
13
|
// (ADR-021, the intentional-write vocabulary; the kernel `ops` input is retired
|
|
14
14
|
// at cli 4.0) — and `execute` commits a frozen plan; the rest are sugar.
|
|
15
|
+
// Error data spells a row attribute as the attribute (spec/surface §4, 5.0):
|
|
16
|
+
// `block_missing { _id }`, and the CLI's own `type_mismatch` names the block's
|
|
17
|
+
// `_id` and `_type`.
|
|
15
18
|
function block(cli, ws, repoId, ref) {
|
|
16
19
|
const r = resolveRef(ws.store, repoId, ref);
|
|
17
20
|
if (!r || r.kind !== "block")
|
|
18
|
-
throw new EngineErrorLike("block_missing", `no block with id ${JSON.stringify(ref)}`, { data: {
|
|
21
|
+
throw new EngineErrorLike("block_missing", `no block with id ${JSON.stringify(ref)}`, { data: { _id: ref } });
|
|
19
22
|
return r.blockId;
|
|
20
23
|
}
|
|
21
24
|
// A parent ref for insert/move: a block ref names that block as the parent; a
|
|
22
25
|
// document ref (id or path) names the document's top level — the same reading
|
|
23
|
-
// the server gives `blocks_insert.to` / `blocks_move.to`.
|
|
26
|
+
// the server gives `blocks_insert.to` / `blocks_move.to`. The target's address
|
|
27
|
+
// is the row's `_id` (spec/mutate §11, 4.0).
|
|
24
28
|
function parent(ws, repoId, ref) {
|
|
25
29
|
const r = resolveRef(ws.store, repoId, ref);
|
|
26
30
|
if (!r)
|
|
27
|
-
throw new EngineErrorLike("block_missing", `no block with id ${JSON.stringify(ref)}`, { data: {
|
|
31
|
+
throw new EngineErrorLike("block_missing", `no block with id ${JSON.stringify(ref)}`, { data: { _id: ref } });
|
|
28
32
|
if (r.kind === "document")
|
|
29
|
-
return { kind: "document",
|
|
30
|
-
return { kind: "block",
|
|
33
|
+
return { kind: "document", _id: r.docId };
|
|
34
|
+
return { kind: "block", _id: r.blockId };
|
|
31
35
|
}
|
|
32
36
|
// `done` addresses task blocks only (spec/cli §6): any other live block type is
|
|
33
37
|
// a type_mismatch before an op is built — the macro itself would record a no-op
|
|
34
|
-
// `edited` commit (its `
|
|
38
|
+
// `edited` commit (its `_attrs.checked` only rewrites a task's marker).
|
|
35
39
|
function taskBlock(ws, ref, blockId) {
|
|
36
40
|
const row = ws.store.db
|
|
37
41
|
.prepare("SELECT type FROM blocks WHERE block_id = ? AND deleted_commit IS NULL")
|
|
38
42
|
.get(blockId);
|
|
39
43
|
if (!row)
|
|
40
|
-
throw new EngineErrorLike("block_missing", `no block with id ${JSON.stringify(ref)}`, { data: {
|
|
44
|
+
throw new EngineErrorLike("block_missing", `no block with id ${JSON.stringify(ref)}`, { data: { _id: ref } });
|
|
41
45
|
if (row.type !== "task") {
|
|
42
|
-
throw new EngineErrorLike("type_mismatch", `not a task: ${ref} is a ${row.type}`, { data: {
|
|
46
|
+
throw new EngineErrorLike("type_mismatch", `not a task: ${ref} is a ${row.type}`, { data: { _id: blockId, _type: row.type } });
|
|
43
47
|
}
|
|
44
48
|
return blockId;
|
|
45
49
|
}
|
|
@@ -198,7 +202,7 @@ async function runUpdate(cli, args) {
|
|
|
198
202
|
const expect = values.expect ? { content_hash: values.expect } : undefined;
|
|
199
203
|
// Remote: the server pins CAS from current bytes when --expect is absent.
|
|
200
204
|
if (cli.flags.server) {
|
|
201
|
-
return runSugarRemote(cli, "blocks_update", {
|
|
205
|
+
return runSugarRemote(cli, "blocks_update", { _id: ref, markdown, ...(expect ? { expect } : {}) }, () => updateBlockEffects(ref, { markdown, expect }).effects, [ref]);
|
|
202
206
|
}
|
|
203
207
|
const ws = cli.workspace();
|
|
204
208
|
const repo = cli.repo(ws);
|
|
@@ -306,7 +310,7 @@ async function runDone(cli, args) {
|
|
|
306
310
|
throw new CliUsageError("done requires one or more task blocks (or - for stdin)");
|
|
307
311
|
const checked = !values.undo;
|
|
308
312
|
if (cli.flags.server)
|
|
309
|
-
return runSugarRemote(cli, "blocks_check", {
|
|
313
|
+
return runSugarRemote(cli, "blocks_check", { _ids: refs, ...(values.undo ? { checked: false } : {}) }, () => checkEffects(refs, checked).effects, refs);
|
|
310
314
|
const ws = cli.workspace();
|
|
311
315
|
const repo = cli.repo(ws);
|
|
312
316
|
const blocks = refs.map((r) => taskBlock(ws, r, block(cli, ws, repo.repoId, r)));
|
|
@@ -341,13 +345,13 @@ async function runAppend(cli, args) {
|
|
|
341
345
|
// Remote: blocks_append resolves the ref and decides by the block's kind
|
|
342
346
|
// server-side (spec/surface §4); a remote dry run reads the kind first.
|
|
343
347
|
if (cli.flags.server) {
|
|
344
|
-
return runSugarRemote(cli, "blocks_append", {
|
|
345
|
-
// The block's
|
|
348
|
+
return runSugarRemote(cli, "blocks_append", { _id: target, text: markdown }, async () => {
|
|
349
|
+
// The block's `_type` decides the effect; `blocks_read` reads it on a full
|
|
346
350
|
// server, a `query` over the blocks target on a minimal one (§2.3).
|
|
347
351
|
const type = (await remoteHasTool(cli, "blocks_read"))
|
|
348
|
-
? (await remoteCall(cli, "blocks_read", {
|
|
349
|
-
: (await remoteCall(cli, "query", { query: `select
|
|
350
|
-
return appendEffects(type === "heading" ? { kind: "section", heading_id: target } : { kind: "block",
|
|
352
|
+
? (await remoteCall(cli, "blocks_read", { _id: target, resolution: "skeleton" }))._type
|
|
353
|
+
: (await remoteCall(cli, "query", { query: `select _type from blocks where _id == ${JSON.stringify(target)}` })).hits[0]?._type;
|
|
354
|
+
return appendEffects(type === "heading" ? { kind: "section", heading_id: target } : { kind: "block", _id: target }, markdown).effects;
|
|
351
355
|
}, [target]);
|
|
352
356
|
}
|
|
353
357
|
const ws = cli.workspace();
|
|
@@ -363,13 +367,13 @@ function appendSugar(store, blockId, markdown) {
|
|
|
363
367
|
.prepare("SELECT b.type AS type, d.format AS format, (SELECT count(*) FROM blocks c WHERE c.parent_block = b.block_id AND c.deleted_commit IS NULL) AS children FROM blocks b JOIN docs d ON d.doc_id = b.doc_id WHERE b.block_id = ? AND b.deleted_commit IS NULL")
|
|
364
368
|
.get(blockId);
|
|
365
369
|
if (!row)
|
|
366
|
-
throw new EngineErrorLike("block_missing", `no block with id ${JSON.stringify(blockId)}`, { data: {
|
|
370
|
+
throw new EngineErrorLike("block_missing", `no block with id ${JSON.stringify(blockId)}`, { data: { _id: blockId } });
|
|
367
371
|
if (row.type === "heading")
|
|
368
372
|
return appendEffects({ kind: "section", heading_id: blockId }, markdown);
|
|
369
373
|
if (isContainerBlock(row.format ?? "markdown", { type: row.type, children: new Array(row.children) })) {
|
|
370
|
-
return appendEffects({ kind: "block",
|
|
374
|
+
return appendEffects({ kind: "block", _id: blockId }, markdown);
|
|
371
375
|
}
|
|
372
|
-
throw new EngineErrorLike("block_not_container", `block ${blockId} is a ${row.type}, not a heading or a container; append means into — insert after it with blocks_insert { to: <its parent>, at: { after: ${JSON.stringify(blockId)} } }`, { data: {
|
|
376
|
+
throw new EngineErrorLike("block_not_container", `block ${blockId} is a ${row.type}, not a heading or a container; append means into — insert after it with blocks_insert { to: <its parent>, at: { after: ${JSON.stringify(blockId)} } }`, { data: { _id: blockId, _type: row.type } });
|
|
373
377
|
}
|
|
374
378
|
// ---- split / merge ----------------------------------------------------------
|
|
375
379
|
async function runSplit(cli, args) {
|
|
@@ -403,7 +407,7 @@ async function runSplit(cli, args) {
|
|
|
403
407
|
return n;
|
|
404
408
|
});
|
|
405
409
|
if (cli.flags.server)
|
|
406
|
-
return runSugarRemote(cli, "blocks_split", {
|
|
410
|
+
return runSugarRemote(cli, "blocks_split", { _id: ref, at }, () => splitEffects(ref, at).effects, [ref]);
|
|
407
411
|
const ws = cli.workspace();
|
|
408
412
|
const repo = cli.repo(ws);
|
|
409
413
|
const blockId = block(cli, ws, repo.repoId, ref);
|
package/dist/src/cmd/node.js
CHANGED
|
@@ -39,9 +39,9 @@ async function runNode(cli, args) {
|
|
|
39
39
|
if (!nodeId || !prop || valueParts.length === 0)
|
|
40
40
|
throw new CliUsageError("node set <nodeId> <prop> <value>");
|
|
41
41
|
const value = valueParts.join(" ");
|
|
42
|
-
// Remote: nodes_set resolves node → block + rewrites the span server-side.
|
|
42
|
+
// Remote: nodes_set (`_id` the node, spec/surface §4 "Arguments", 5.0) resolves node → block + rewrites the span server-side.
|
|
43
43
|
if (cli.flags.server)
|
|
44
|
-
return runSugarRemote(cli, "nodes_set", {
|
|
44
|
+
return runSugarRemote(cli, "nodes_set", { _id: nodeId, prop, value }, () => nodeSetEffects(nodeId, prop, value).effects, [nodeId]);
|
|
45
45
|
const ws = cli.workspace();
|
|
46
46
|
const repo = cli.repo(ws);
|
|
47
47
|
return runSugar(cli, ws, repo, nodeSetEffects(requireNode(ws.store, repo.repoId, nodeId), prop, value), values.actor ? { actor: values.actor } : {});
|
package/dist/src/cmd/oqx.d.ts
CHANGED
|
@@ -5,8 +5,8 @@ import type { Cli } from "../context.js";
|
|
|
5
5
|
* Render an `OqxResult` in the current mode (shared with `run`). `--json` is the
|
|
6
6
|
* whole result verbatim (incl. consumer + any scalar), so count/exists round-trip
|
|
7
7
|
* without special-casing; `--jsonl` streams the hits (or the values, as JSON);
|
|
8
|
-
* `--ids` the
|
|
9
|
-
* document, and so does a `bare` result (its hits carry no envelope). The
|
|
8
|
+
* `--ids` the hits' `_id`s — a scalar result has neither, so those modes print
|
|
9
|
+
* the document, and so does a `bare` result (its hits carry no envelope). The
|
|
10
10
|
* truncation footer prints in every mode but `--json`.
|
|
11
11
|
*/
|
|
12
12
|
export declare function renderOqxResult(cli: Cli, result: OqxResult, bare?: boolean): number;
|
package/dist/src/cmd/oqx.js
CHANGED
|
@@ -6,15 +6,17 @@ import { loadEmbedding } from "./_embed.js";
|
|
|
6
6
|
import { remoteCall } from "./_remote.js";
|
|
7
7
|
import { readStdin } from "./_mutate.js";
|
|
8
8
|
// `omg query <source>` — OQX composable query. Dot navigation belongs to the host
|
|
9
|
-
// object model (`
|
|
10
|
-
//
|
|
9
|
+
// object model (`_doc.layer`, `$it._blocks`; spec/surface §1.2, 5.0: an engine
|
|
10
|
+
// name is `_`-prefixed, a bare name is an authored key, `$it` is the self);
|
|
11
|
+
// whitespace query directives belong to OQX (`<receiver>
|
|
12
|
+
// collect|exists|count|first|single { <block> }`).
|
|
11
13
|
// `from E` selects + flattens a relation relative to the current source scope
|
|
12
14
|
// (top-level `from docs` = the repository's docs; `docs collect { from
|
|
13
15
|
// nodes … }` re-projects each doc through its nodes). Clause order is fixed
|
|
14
16
|
// (ADR-020): `select … from … where … follow … order by … limit … offset …`,
|
|
15
17
|
// each at most once, at the top level and inside every block. Only `select` may
|
|
16
|
-
// drop its keyword, and only as the first clause (`
|
|
17
|
-
// projects); a predicate always needs `where` (`
|
|
18
|
+
// drop its keyword, and only as the first clause (`_nodes collect { _name }`
|
|
19
|
+
// projects); a predicate always needs `where` (`_nodes exists { where _kind ==
|
|
18
20
|
// "md:task" }`), so a bare boolean property projects rather than filters.
|
|
19
21
|
// `where` may use the body's select aliases. Features: a boolean where
|
|
20
22
|
// tree over scalar predicates and consumer directives, count comparisons
|
|
@@ -29,13 +31,13 @@ import { readStdin } from "./_mutate.js";
|
|
|
29
31
|
// `docs count { … }` / `docs exists { … }` reduce to a scalar,
|
|
30
32
|
// `docs first { … }` / `docs single { … }` to zero-or-one row (bare
|
|
31
33
|
// `from …` = collect). Coexists with `omg q` (CEL). Source is a positional
|
|
32
|
-
// string or -f file|-. Human output: one hit per line (
|
|
33
|
-
//
|
|
34
|
-
// count/exists/none (see output.ts renderHits); --json/--jsonl/--ids.
|
|
35
|
-
// is the tool's `bare` passed through (ADR-012: a thin client;
|
|
36
|
-
// §1.4, 4.2): every hit is its projection alone, rendered as a
|
|
37
|
-
// the
|
|
38
|
-
// print and falls back to the document, like a scalar result).
|
|
34
|
+
// string or -f file|-. Human output: one hit per line (_id + _path), or an
|
|
35
|
+
// aligned _id/_path/columns table when the query projects (`select`), or the
|
|
36
|
+
// scalar for count/exists/none (see output.ts renderHits); --json/--jsonl/--ids.
|
|
37
|
+
// `--bare` is the tool's `bare` passed through (ADR-012: a thin client;
|
|
38
|
+
// spec/surface §1.4, 4.2): every hit is its projection alone, rendered as a
|
|
39
|
+
// table without the _id/_path columns (`--json`/`--jsonl` the bare hits;
|
|
40
|
+
// `--ids` has no id to print and falls back to the document, like a scalar result).
|
|
39
41
|
async function runOqx(cli, args) {
|
|
40
42
|
const { values, positionals } = parseArgs({
|
|
41
43
|
args,
|
|
@@ -58,39 +60,40 @@ async function runOqx(cli, args) {
|
|
|
58
60
|
["-f <file|->", "read the query from a file, or stdin with `-`"],
|
|
59
61
|
["-n <N>", "page size"],
|
|
60
62
|
["--cursor <c>", "continue a truncated result"],
|
|
61
|
-
["--bare", "hits as the projection only — no automatic
|
|
63
|
+
["--bare", "hits as the projection only — no automatic _id/_path columns (needs a `select`; `values` is the bare-values form)"],
|
|
62
64
|
["--ids", `bare ids, one per line — pipe fuel (\`… --ids | ${cli.prog} cat -\`)`],
|
|
63
65
|
],
|
|
64
66
|
notes: [
|
|
65
67
|
`semantic ranking: \`order by semantic("phrase") desc\` or \`where semantic("phrase") > 0.6\` (needs embedding.provider; see \`${cli.prog} embed --help\`).`,
|
|
66
68
|
"full syntax: the examples below, the MCP `query_syntax` tool, and docs/query-language.md.",
|
|
69
|
+
"names: an engine field is `_`-prefixed (_id _path _title _tags _updated_at _type _text _doc …); a bare name is an authored key (title, tags, layer); `$it` is the row itself.",
|
|
67
70
|
"examples:",
|
|
68
71
|
],
|
|
69
72
|
});
|
|
70
|
-
cli.io.out(" e.g. query 'from docs where
|
|
71
|
-
cli.io.out(" query 'select items:
|
|
72
|
-
cli.io.out(" query 'select
|
|
73
|
-
cli.io.out(" query 'select owner_id, owner: ^nodes single { where
|
|
73
|
+
cli.io.out(" e.g. query 'from docs where _nodes count { where _kind == \"md:task\" } >= 2'");
|
|
74
|
+
cli.io.out(" query 'select items: _blocks collect { where _type == \"list_item\" } from nodes where _kind == \"md:section\"'");
|
|
75
|
+
cli.io.out(" query 'select _title, open from docs where _nodes collect { ^open: _value where _kind == \"md:task\" and not checked }'");
|
|
76
|
+
cli.io.out(" query 'select owner_id, owner: ^nodes single { where _kind == \"person\" and id == ^owner_id } from docs' # ^docs/^nodes/^blocks/^links: the repository (the root row) from inside a row; 0^docs from any depth; ^_id / 0^_id = the repository id");
|
|
74
77
|
cli.io.out(" query 'docs count { where layer == \"canon\" }' # scalar; also <target> exists/none/first/single { … } at the top level");
|
|
75
|
-
cli.io.out(" query 'from docs where
|
|
78
|
+
cli.io.out(" query 'from docs where _nodes none { where _kind == \"md:task\" and not checked }' # none = zero rows (≡ not … exists; \"every\" = none over the complement)");
|
|
76
79
|
cli.io.out(" query 'from docs where era is not null and (layer == \"canon\" or layer == \"working\")' # and / or / not are the connectives (&& || ! accepted); x is y / x is not y = identity, x is null = absent");
|
|
77
|
-
cli.io.out(" query 'docs { type == \"substance\" and not verified }' # sugar: a block without a consumer is collect; a leading predicate drops `where` (bare names still project — filter a bare field with `is x`); after a follow destination too: follow
|
|
78
|
-
cli.io.out(" query 'select subject, process: ^docs[slug == ^subject].
|
|
79
|
-
cli.io.out(" query 'select
|
|
80
|
-
cli.io.out(" query 'select
|
|
80
|
+
cli.io.out(" query 'docs { type == \"substance\" and not verified }' # sugar: a block without a consumer is collect; a leading predicate drops `where` (bare names still project — filter a bare field with `is x`); after a follow destination too: follow _children { depth 2 } is its block");
|
|
81
|
+
cli.io.out(" query 'select subject, process: ^docs[slug == ^subject]._path from docs where type == \"lab-note\"' # x[p] = first match or absent; x[p]! = exactly one, required; x[n] = positional; _title! = the value or a loud error; a select item may use the items to its left");
|
|
82
|
+
cli.io.out(" query 'select _title, $depth from docs where _path == \"/timeline/review.md\" follow refs(before), refs(after)' # refs(field): the live documents a property's references name (either path form or a d_ id; dangling dropped) — a receiver, a source or a follow destination");
|
|
83
|
+
cli.io.out(" query 'select _title, next: ^docs { ^_path in list(before) } from docs where type == \"milestone\"' # paths are /-rooted on the surface (_path == \"/x.md\"; inputs accept both forms), so a property's reference compares with ^_path directly");
|
|
81
84
|
cli.io.out(" query 'from docs where type == \"practitioner\" order by era desc limit 2 offset 1' # limit/offset bound the set (after order/distinct, before the consumer)");
|
|
82
|
-
cli.io.out(" query 'select fm: entries(
|
|
83
|
-
cli.io.out(" query 'select
|
|
84
|
-
cli.io.out(" query 'select
|
|
85
|
-
cli.io.out(" query 'docs collect { from
|
|
85
|
+
cli.io.out(" query 'select fm: entries(_frontmatter) collect { k: $key, v: $it } from docs where _path == \"/x.md\"' # a record as a collection ($key/$it); also entries(_data), entries(_attrs), entries(_inline)");
|
|
86
|
+
cli.io.out(" query 'select _path values from docs where layer == \"canon\"' # `values`: bare values, no {_id,_path} hits (one item only)");
|
|
87
|
+
cli.io.out(" query 'select _title, tags: _tags collect { $it values where $it != \"draft\" } from docs' # $it = the current item (here: each tag); _tags = authored tags ∪ #hashtags, bare `tags` the authored key alone");
|
|
88
|
+
cli.io.out(" query 'docs collect { from _nodes where _kind == \"md:task\" }' # `from E` re-projects the source (→ nodes)");
|
|
86
89
|
cli.io.out(" query 'from docs where text(\"philosophers stone\") and layer == \"canon\"' # full-text prune");
|
|
87
90
|
cli.io.out(" query 'select s: semantic(\"the great work\") from blocks where semantic(\"the great work\") > 0.6' # embedding score (needs a provider)");
|
|
88
91
|
cli.io.out(" query 'from docs where type == \"practitioner\" order by era desc' # order by <expr> [asc|desc]");
|
|
89
|
-
cli.io.out(" query 'select t:
|
|
90
|
-
cli.io.out(" query 'from nodes where
|
|
91
|
-
cli.io.out(" query 'select p:
|
|
92
|
-
cli.io.out(" query 'from blocks where
|
|
93
|
-
cli.io.out(" query 'select src:
|
|
92
|
+
cli.io.out(" query 'select t: _text, d: $depth, s: $stop from blocks where _id == \"b_x\" follow _children' # recursive walk ($depth/$stop metadata)");
|
|
93
|
+
cli.io.out(" query 'from nodes where _name == \"Overview\" follow _subsections { depth 3 }' # follow [distinct] <dest> [{ where … frontier … depth n }] — a brace after any destination is that destination's block (identity is $key: project $key: <expr> to rekey a walk)");
|
|
94
|
+
cli.io.out(" query 'select p: _doc._path, d: $depth, s: $stop from docs where _path == \"/index.md\" follow _out_links collect { from _dst }' # citation graph (cyclic-safe: $stop=cycle)");
|
|
95
|
+
cli.io.out(" query 'from blocks where _type == \"list_item\" and $leaf follow _children' # $leaf/$depth/$stop filter the walk result post-walk");
|
|
96
|
+
cli.io.out(" query 'select src: _doc_id, to: _dst_path from links where _predicate == \"depends_on\"' # the links target: _predicate/_provenance/_dst_kind + _doc_id/_dst_path/_uri");
|
|
94
97
|
return EXIT_OK;
|
|
95
98
|
}
|
|
96
99
|
const source = values.file
|
|
@@ -153,8 +156,8 @@ async function runOqx(cli, args) {
|
|
|
153
156
|
* Render an `OqxResult` in the current mode (shared with `run`). `--json` is the
|
|
154
157
|
* whole result verbatim (incl. consumer + any scalar), so count/exists round-trip
|
|
155
158
|
* without special-casing; `--jsonl` streams the hits (or the values, as JSON);
|
|
156
|
-
* `--ids` the
|
|
157
|
-
* document, and so does a `bare` result (its hits carry no envelope). The
|
|
159
|
+
* `--ids` the hits' `_id`s — a scalar result has neither, so those modes print
|
|
160
|
+
* the document, and so does a `bare` result (its hits carry no envelope). The
|
|
158
161
|
* truncation footer prints in every mode but `--json`.
|
|
159
162
|
*/
|
|
160
163
|
export function renderOqxResult(cli, result, bare = false) {
|
|
@@ -163,7 +166,7 @@ export function renderOqxResult(cli, result, bare = false) {
|
|
|
163
166
|
const list = result.values ?? (result.consumer === "collect" || result.consumer === "first" || result.consumer === "single" ? result.hits : undefined);
|
|
164
167
|
return emitMachine(cli, result, {
|
|
165
168
|
...(list ? { items: list } : {}),
|
|
166
|
-
...(list && !result.values && !bare ? { ids: result.hits.map((h) => h.
|
|
169
|
+
...(list && !result.values && !bare ? { ids: result.hits.map((h) => h._id) } : {}),
|
|
167
170
|
cursor,
|
|
168
171
|
});
|
|
169
172
|
}
|
package/dist/src/cmd/outline.js
CHANGED
|
@@ -32,8 +32,8 @@ async function runOutline(cli, args) {
|
|
|
32
32
|
let result;
|
|
33
33
|
let header;
|
|
34
34
|
if (cli.flags.server) {
|
|
35
|
-
// Remote: the docs_outline tool resolves the locator (`
|
|
36
|
-
// else `
|
|
35
|
+
// Remote: the docs_outline tool resolves the locator (`_id` for a `d_` id,
|
|
36
|
+
// else `_path` — spec/surface §4 "Arguments", 5.0) + returns the same OutlineResult. The header shows the ref you passed (no local path lookup):
|
|
37
37
|
// a path ref rooted (spec/surface §1 "Paths"), a `d_` id verbatim.
|
|
38
38
|
result = await remoteCall(cli, "docs_outline", {
|
|
39
39
|
...docLocator(ref),
|
package/dist/src/cmd/retarget.js
CHANGED
|
@@ -70,7 +70,7 @@ function renderHits(cli, from, to, hits) {
|
|
|
70
70
|
}
|
|
71
71
|
io.err(style.dim(` plan — ${hits.length} block(s) would change; re-run with --apply to commit`));
|
|
72
72
|
for (const h of hits) {
|
|
73
|
-
io.out(style.id(h.
|
|
73
|
+
io.out(style.id(h._id));
|
|
74
74
|
for (const line of unifiedDiffLines(cli, h.oldRaw, h.newRaw))
|
|
75
75
|
io.out(line);
|
|
76
76
|
io.out("");
|
package/dist/src/cmd/run.js
CHANGED
|
@@ -99,8 +99,8 @@ async function runRun(cli, args) {
|
|
|
99
99
|
else {
|
|
100
100
|
result = oqxRun(ws.store, repo.repoId, source);
|
|
101
101
|
}
|
|
102
|
-
// Same renderer as `omg query`, in every mode: `<
|
|
103
|
-
// column table when the fence projects (a selected `
|
|
102
|
+
// Same renderer as `omg query`, in every mode: `<_id> <_path>` lines, or a
|
|
103
|
+
// column table when the fence projects (a selected `_text` shows as its first line).
|
|
104
104
|
return renderOqxResult(cli, result);
|
|
105
105
|
}
|
|
106
106
|
export const cmdRun = { name: "run", summary: "Evaluate an ```omg fence (OQX, inert, read-only)", run: (c, a) => runRun(c, a) };
|
package/dist/src/cmd/show.js
CHANGED
|
@@ -15,14 +15,14 @@ function showOne(cli, ref, include) {
|
|
|
15
15
|
if (!res)
|
|
16
16
|
throw new EngineErrorLike("doc_missing", `no document ${ref}`);
|
|
17
17
|
const links = docLinks(ws.store, resolved.docId, { direction: "both" });
|
|
18
|
-
// The authored keys, merged (frontmatter + inline — the `
|
|
19
|
-
// computed rows are rendered under their surface names (`
|
|
20
|
-
// spec/surface §1.2 — the store's `$title`/`$tags` keys never reach the
|
|
18
|
+
// The authored keys, merged (frontmatter + inline — the `_data` view); the
|
|
19
|
+
// computed rows are rendered under their surface names (`_title`/`_tags`,
|
|
20
|
+
// spec/surface §1.2, 5.0 — the store's `$title`/`$tags` keys never reach the
|
|
21
21
|
// terminal) from the re-keyed `properties.computed` the read carries.
|
|
22
22
|
const properties = Object.fromEntries(Object.entries(docPropertiesMerged(ws.store.db, resolved.docId)).filter(([k]) => !k.startsWith("$")));
|
|
23
23
|
const computed = res.properties.computed ?? {};
|
|
24
24
|
const payload = { kind: "document", ...res };
|
|
25
|
-
return { payload, render: () => renderDocCard(cli, res.
|
|
25
|
+
return { payload, render: () => renderDocCard(cli, res._path, properties, computed, links) };
|
|
26
26
|
}
|
|
27
27
|
const node = nodesGet(ws.store, resolved.docId, resolved.blockId, { resolution: "full" });
|
|
28
28
|
if (!node)
|
|
@@ -74,8 +74,8 @@ function renderDocCard(cli, path, fm, computed, links) {
|
|
|
74
74
|
for (const k of keys)
|
|
75
75
|
io.out(` ${style.accent(k)} ${style.dim("=")} ${fmt(fm[k])}`);
|
|
76
76
|
}
|
|
77
|
-
// The computed rows under their surface names (`
|
|
78
|
-
// §1.2), their own section so a computed title never masquerades as authored.
|
|
77
|
+
// The computed rows under their surface names (`_title`, `_tags` — spec/surface
|
|
78
|
+
// §1.2, 5.0), their own section so a computed title never masquerades as authored.
|
|
79
79
|
const computedKeys = Object.keys(computed);
|
|
80
80
|
if (computedKeys.length > 0) {
|
|
81
81
|
io.out(` ${style.dim("computed")}`);
|
|
@@ -85,28 +85,28 @@ function renderDocCard(cli, path, fm, computed, links) {
|
|
|
85
85
|
if (links.out.length > 0) {
|
|
86
86
|
io.out(` ${style.dim("out links")}`);
|
|
87
87
|
for (const e of links.out)
|
|
88
|
-
io.out(` ${style.accent(e.
|
|
88
|
+
io.out(` ${style.accent(e._predicate)} ${style.dim(render.g.arrow)} ${style.id(e._doc_id)} ${style.dim(`×${e.count}`)}`);
|
|
89
89
|
}
|
|
90
90
|
if (links.in.length > 0) {
|
|
91
91
|
io.out(` ${style.dim("backlinks")}`);
|
|
92
92
|
for (const e of links.in)
|
|
93
|
-
io.out(` ${style.id(e.
|
|
93
|
+
io.out(` ${style.id(e._doc_id)} ${style.dim(render.g.arrow)} ${style.accent(e._predicate)} ${style.dim(`×${e.count}`)}`);
|
|
94
94
|
}
|
|
95
95
|
}
|
|
96
96
|
function renderBlockCard(cli, node, history) {
|
|
97
97
|
const { render, style, io } = cli;
|
|
98
|
-
io.out(` ${render.typeGlyph(node.
|
|
98
|
+
io.out(` ${render.typeGlyph(node._type)} ${style.bold(node._type)} ${style.id(node._id)}`);
|
|
99
99
|
io.out(render.rule(40));
|
|
100
|
-
// The placement is flattened onto the row (
|
|
101
|
-
if (node.
|
|
102
|
-
io.out(` ${style.dim("placement")} parent=${style.id(node.parent_id ?? "—")} ordinal=${node.
|
|
103
|
-
if (node.
|
|
100
|
+
// The placement is flattened onto the row (5.0: `parent_id`/`_ordinal`/`_depth`); the line reads the same.
|
|
101
|
+
if (node._ordinal !== undefined)
|
|
102
|
+
io.out(` ${style.dim("placement")} parent=${style.id(node.parent_id ?? "—")} ordinal=${node._ordinal} depth=${node._depth}`);
|
|
103
|
+
if (node._attrs && Object.keys(node._attrs).length > 0) {
|
|
104
104
|
io.out(` ${style.dim("attrs")}`);
|
|
105
|
-
for (const [k, v] of Object.entries(node.
|
|
105
|
+
for (const [k, v] of Object.entries(node._attrs))
|
|
106
106
|
io.out(` ${style.accent(k)} ${style.dim("=")} ${fmt(v)}`);
|
|
107
107
|
}
|
|
108
|
-
if (node.
|
|
109
|
-
io.out(` ${style.dim("text")} ${node.
|
|
108
|
+
if (node._text)
|
|
109
|
+
io.out(` ${style.dim("text")} ${node._text}`);
|
|
110
110
|
if (history && history.length > 0) {
|
|
111
111
|
io.out(` ${style.dim("history")}`);
|
|
112
112
|
for (const h of history)
|
package/dist/src/cmd/update.js
CHANGED
|
@@ -71,7 +71,7 @@ async function runDocUpdate(cli, args) {
|
|
|
71
71
|
const repo = cli.repo(ws);
|
|
72
72
|
if (!repo.rootPath)
|
|
73
73
|
throw new CliUsageError(`repo '${repo.slug}' has no filesystem source; 'update' needs a working tree`);
|
|
74
|
-
const effects = [{ op: "set", target: { kind: "document",
|
|
74
|
+
const effects = [{ op: "set", target: { kind: "document", _id: requireDoc(ws.store, repo.repoId, doc) }, field: "content", value: bytes }];
|
|
75
75
|
const req = {
|
|
76
76
|
repoId: repo.repoId,
|
|
77
77
|
rootPath: repo.rootPath,
|
|
@@ -93,11 +93,12 @@ async function runDocUpdate(cli, args) {
|
|
|
93
93
|
return EXIT_OK;
|
|
94
94
|
}
|
|
95
95
|
if (receipt === undefined) {
|
|
96
|
-
// The plan: its one effect names the document
|
|
97
|
-
//
|
|
96
|
+
// The plan: its one effect names the document (a resolved target carries
|
|
97
|
+
// `_id` and `_path`, spec/mutate §11); `preview.identity[<id>]` is the opset
|
|
98
|
+
// plan (one line per op, an empty line, the summary).
|
|
98
99
|
const target = plan.effects[0] && "target" in plan.effects[0] ? plan.effects[0].target : undefined;
|
|
99
|
-
const docId = target && target.kind === "document" ? target.
|
|
100
|
-
const path = target && target.kind === "document" ? referencePath(target.
|
|
100
|
+
const docId = target && target.kind === "document" ? target._id : undefined;
|
|
101
|
+
const path = target && target.kind === "document" ? referencePath(target._path) : doc;
|
|
101
102
|
const identity = docId !== undefined ? plan.preview?.identity?.[docId] : undefined;
|
|
102
103
|
cli.io.err(cli.style.dim(` plan for ${cli.style.accent(path)}`));
|
|
103
104
|
if (identity)
|
package/dist/src/output.d.ts
CHANGED
|
@@ -83,8 +83,8 @@ interface MachineIO {
|
|
|
83
83
|
/** Print `doc` in the current machine mode (`--json`/`--jsonl`/`--ids`); returns EXIT_OK. */
|
|
84
84
|
export declare function emitMachine(cli: MachineIO, doc: unknown, shape?: MachineShape): number;
|
|
85
85
|
export interface HitLike {
|
|
86
|
-
|
|
87
|
-
|
|
86
|
+
_id: string;
|
|
87
|
+
_path: string;
|
|
88
88
|
[k: string]: unknown;
|
|
89
89
|
}
|
|
90
90
|
export declare const HIT_CELL_MAX = 60;
|
|
@@ -93,19 +93,19 @@ export declare function truncateCell(s: string, max?: number): string;
|
|
|
93
93
|
/** Render one projected value as a table cell (unstyled). */
|
|
94
94
|
export declare function hitCell(v: unknown): string;
|
|
95
95
|
/** The projected column names across `hits`: every key but the envelope's
|
|
96
|
-
* `
|
|
96
|
+
* `_id`/`_path` (spec/surface §1.4, 5.0 — a `_path` projection collapses onto
|
|
97
97
|
* the envelope key, so it is never a column), in first-seen order. Empty when
|
|
98
98
|
* nothing was projected. */
|
|
99
99
|
export declare function hitColumns(hits: HitLike[]): string[];
|
|
100
|
-
/** Human-tier hit list: `<
|
|
101
|
-
* (header `
|
|
100
|
+
/** Human-tier hit list: `<_id> <_path>` lines, or an aligned table when projected
|
|
101
|
+
* (header `_id`, `_path`, then the projected keys — spec/cli §4, 5.0). */
|
|
102
102
|
export declare function renderHits(cli: {
|
|
103
103
|
io: IO;
|
|
104
104
|
style: Style;
|
|
105
105
|
}, hits: HitLike[]): void;
|
|
106
106
|
/** The bare hit table (`query --bare`, spec/cli §4, 4.1): a hit is its
|
|
107
107
|
* projection alone (`spec/surface` §1.4, 4.2), so the columns are every key in
|
|
108
|
-
* first-seen order across the hits — a projected `
|
|
108
|
+
* first-seen order across the hits — a projected `_id`/`_path` is an ordinary
|
|
109
109
|
* column, rendered as any other cell — with no envelope columns before them.
|
|
110
110
|
* Header row, then one row per hit; cells as the enveloped table's. */
|
|
111
111
|
export declare function renderBareHits(cli: {
|
package/dist/src/output.js
CHANGED
|
@@ -191,12 +191,12 @@ export function hitCell(v) {
|
|
|
191
191
|
return truncateCell(JSON.stringify(v) ?? String(v));
|
|
192
192
|
}
|
|
193
193
|
/** The projected column names across `hits`: every key but the envelope's
|
|
194
|
-
* `
|
|
194
|
+
* `_id`/`_path` (spec/surface §1.4, 5.0 — a `_path` projection collapses onto
|
|
195
195
|
* the envelope key, so it is never a column), in first-seen order. Empty when
|
|
196
196
|
* nothing was projected. */
|
|
197
197
|
export function hitColumns(hits) {
|
|
198
198
|
const cols = [];
|
|
199
|
-
const seen = new Set(["
|
|
199
|
+
const seen = new Set(["_id", "_path"]);
|
|
200
200
|
for (const h of hits) {
|
|
201
201
|
for (const k of Object.keys(h)) {
|
|
202
202
|
if (seen.has(k))
|
|
@@ -207,24 +207,24 @@ export function hitColumns(hits) {
|
|
|
207
207
|
}
|
|
208
208
|
return cols;
|
|
209
209
|
}
|
|
210
|
-
/** Human-tier hit list: `<
|
|
211
|
-
* (header `
|
|
210
|
+
/** Human-tier hit list: `<_id> <_path>` lines, or an aligned table when projected
|
|
211
|
+
* (header `_id`, `_path`, then the projected keys — spec/cli §4, 5.0). */
|
|
212
212
|
export function renderHits(cli, hits) {
|
|
213
213
|
const { io, style } = cli;
|
|
214
214
|
const projected = hitColumns(hits);
|
|
215
215
|
if (projected.length === 0) {
|
|
216
216
|
for (const h of hits)
|
|
217
|
-
io.out(`${style.id(h.
|
|
217
|
+
io.out(`${style.id(h._id)} ${style.accent(h._path)}`.trimEnd());
|
|
218
218
|
return;
|
|
219
219
|
}
|
|
220
|
-
const cols = ["
|
|
220
|
+
const cols = ["_id", "_path", ...projected];
|
|
221
221
|
const rows = [cols.map((c) => style.dim(c))];
|
|
222
222
|
for (const h of hits) {
|
|
223
223
|
rows.push(cols.map((c) => {
|
|
224
|
-
if (c === "
|
|
225
|
-
return style.id(h.
|
|
226
|
-
if (c === "
|
|
227
|
-
return style.accent(h.
|
|
224
|
+
if (c === "_id")
|
|
225
|
+
return style.id(h._id);
|
|
226
|
+
if (c === "_path")
|
|
227
|
+
return style.accent(h._path);
|
|
228
228
|
return hitCell(h[c]);
|
|
229
229
|
}));
|
|
230
230
|
}
|
|
@@ -233,7 +233,7 @@ export function renderHits(cli, hits) {
|
|
|
233
233
|
}
|
|
234
234
|
/** The bare hit table (`query --bare`, spec/cli §4, 4.1): a hit is its
|
|
235
235
|
* projection alone (`spec/surface` §1.4, 4.2), so the columns are every key in
|
|
236
|
-
* first-seen order across the hits — a projected `
|
|
236
|
+
* first-seen order across the hits — a projected `_id`/`_path` is an ordinary
|
|
237
237
|
* column, rendered as any other cell — with no envelope columns before them.
|
|
238
238
|
* Header row, then one row per hit; cells as the enveloped table's. */
|
|
239
239
|
export function renderBareHits(cli, hits) {
|
package/dist/src/shell/refs.js
CHANGED
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
// signal to use OQX instead.
|
|
11
11
|
export class RefError extends Error {
|
|
12
12
|
}
|
|
13
|
-
// `.field` reads any projected key (`@1.
|
|
14
|
-
// spec/surface §1.
|
|
15
|
-
// `$` is admitted for an OQX-named projection (`$depth`).
|
|
13
|
+
// `.field` reads any projected key (`@1._id`, `@t[1]._path`, `@1.layer` —
|
|
14
|
+
// spec/surface §1.2, 5.0: an engine field is `_`-prefixed, a bare name is an
|
|
15
|
+
// authored key); a leading `$` is admitted for an OQX-named projection (`$depth`).
|
|
16
16
|
const REF_RE = /^@(\d+|_|[A-Za-z][\w-]*)(?:\[(\d+)\])?(?:\.(\$?[A-Za-z_]\w*))?$/;
|
|
17
17
|
/** Parse a whole-token reference; null if the token isn't a reference. */
|
|
18
18
|
export function parseRef(token) {
|
|
@@ -48,7 +48,7 @@ export function deriveRows(value) {
|
|
|
48
48
|
return o.ids.filter((id) => typeof id === "string").map((id) => ({ ref: id, label: id, value: id }));
|
|
49
49
|
}
|
|
50
50
|
if (Array.isArray(o.in) || Array.isArray(o.out)) {
|
|
51
|
-
// links: both directions, each edge's far node is the selectable ref.
|
|
51
|
+
// links: both directions, each edge's far node (`_doc_id`) is the selectable ref.
|
|
52
52
|
const edges = [...(o.out ?? []), ...(o.in ?? [])];
|
|
53
53
|
return edges.map(rowOf);
|
|
54
54
|
}
|
|
@@ -61,19 +61,24 @@ function rowOf(el) {
|
|
|
61
61
|
if (el && typeof el === "object") {
|
|
62
62
|
const o = el;
|
|
63
63
|
const ref = firstString(o, REF_KEYS);
|
|
64
|
-
const label = firstString(o,
|
|
64
|
+
const label = firstString(o, LABEL_KEYS) ?? ref ?? preview(o);
|
|
65
65
|
return { ref: ref ?? label ?? "", label: label ?? "", value: el };
|
|
66
66
|
}
|
|
67
67
|
const s = String(el);
|
|
68
68
|
return { ref: s, label: s, value: el };
|
|
69
69
|
}
|
|
70
70
|
// The keys that make an object an entity ref, in precedence order: a row's
|
|
71
|
-
// `
|
|
72
|
-
// the attribute
|
|
73
|
-
//
|
|
74
|
-
// `
|
|
75
|
-
// `
|
|
76
|
-
|
|
71
|
+
// `_id` (spec/surface §1.2, 5.0 — every tool result spells a row's identity as
|
|
72
|
+
// the engine attribute, `_`-prefixed), a `links` row's far node (`_doc_id`) and
|
|
73
|
+
// an edge's `_block_id`, then the role names `node` and `block` (a `node props`
|
|
74
|
+
// card's `node`; a role key that names a block), then `locator` and the
|
|
75
|
+
// document's `_path`. The
|
|
76
|
+
// bare `id`/`path` (4.x) and `$id`/`$path` tolerance is gone (spec/cli 5.0): a
|
|
77
|
+
// bare key is an authored property, never the row's identity.
|
|
78
|
+
const REF_KEYS = ["_id", "_doc_id", "_block_id", "node", "block", "locator", "_path"];
|
|
79
|
+
// The human label of a row, in precedence order: its `_path`, a `locator`, a
|
|
80
|
+
// `name` (a repo, a source), then the id-like keys.
|
|
81
|
+
const LABEL_KEYS = ["_path", "locator", "name", "_id", "node", "_doc_id"];
|
|
77
82
|
function firstString(o, keys) {
|
|
78
83
|
for (const k of keys)
|
|
79
84
|
if (typeof o[k] === "string")
|
|
@@ -107,7 +112,7 @@ export function coerce(value) {
|
|
|
107
112
|
// A collection-shaped result object (e.g. {hits:[…]}) can't be one arg.
|
|
108
113
|
if (deriveRows(value))
|
|
109
114
|
throw new RefError("reference is a collection; select one with [i]");
|
|
110
|
-
throw new RefError("reference has no
|
|
115
|
+
throw new RefError("reference has no _id/locator to use as an argument");
|
|
111
116
|
}
|
|
112
117
|
throw new RefError("reference is not usable as an argument");
|
|
113
118
|
}
|
|
@@ -141,7 +141,7 @@ export class ShellSession {
|
|
|
141
141
|
line(style.dim("run any omg command; results become addressable:"));
|
|
142
142
|
line(`${style.accent("@1 @2 …")} ${style.dim("rows of the last displayed collection")}`);
|
|
143
143
|
line(`${style.accent("@_")} ${style.dim("the previous command's result")}`);
|
|
144
|
-
line(`${style.accent("@name")} ${style.dim("a named binding (also @name[i], @name.field)")}`);
|
|
144
|
+
line(`${style.accent("@name")} ${style.dim("a named binding (also @name[i], @name.field — e.g. @1._id, @t[1]._path)")}`);
|
|
145
145
|
io.out("");
|
|
146
146
|
line(`${style.accent("@x = <cmd|@ref>")} ${style.dim("bind a snapshot of a result")}`);
|
|
147
147
|
line(`${style.accent("unset x")} ${style.dim("drop a binding")}`);
|
|
@@ -295,7 +295,7 @@ export class ShellSession {
|
|
|
295
295
|
return String(v);
|
|
296
296
|
if (v && typeof v === "object") {
|
|
297
297
|
const o = v;
|
|
298
|
-
const id = o.
|
|
298
|
+
const id = o._id ?? o.node ?? o._path ?? o.locator; // a query hit leads with _id/_path (surface 5.0: engine names are `_`-prefixed)
|
|
299
299
|
return id ? String(id) : "record";
|
|
300
300
|
}
|
|
301
301
|
return "empty";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "omgbase",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.26.1",
|
|
4
4
|
"description": "Open Markdown Graph Base — CLI",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -45,9 +45,9 @@
|
|
|
45
45
|
"omg": "./dist/src/main.js"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@omgbase/core": "^0.
|
|
48
|
+
"@omgbase/core": "^0.29.1",
|
|
49
49
|
"@omgbase/fs-adapter": "^0.2.0",
|
|
50
|
-
"@omgbase/sync": "^0.6.
|
|
50
|
+
"@omgbase/sync": "^0.6.2",
|
|
51
51
|
"yaml": "^2.9.0"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|