@omgbase/core 0.11.1 → 0.19.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/README.md +1 -1
- package/dist/src/core/ids.d.ts +4 -0
- package/dist/src/core/ids.js +17 -0
- package/dist/src/core/ingest.d.ts +39 -3
- package/dist/src/core/ingest.js +233 -202
- package/dist/src/core/store/schema.d.ts +3 -2
- package/dist/src/core/store/schema.js +28 -3
- package/dist/src/core/store/size.d.ts +76 -0
- package/dist/src/core/store/size.js +223 -0
- package/dist/src/core/store/store.d.ts +9 -0
- package/dist/src/core/store/store.js +11 -0
- package/dist/src/graph/history.js +15 -12
- package/dist/src/index.d.ts +12 -5
- package/dist/src/index.js +18 -7
- package/dist/src/mcp/arguments.d.ts +9 -2
- package/dist/src/mcp/arguments.js +15 -2
- package/dist/src/mcp/errors.d.ts +1 -1
- package/dist/src/mcp/errors.js +3 -0
- package/dist/src/mcp/reference.d.ts +1 -1
- package/dist/src/mcp/reference.js +27 -6
- package/dist/src/mcp/server.d.ts +56 -1
- package/dist/src/mcp/server.js +365 -203
- package/dist/src/mutate/apply.d.ts +93 -12
- package/dist/src/mutate/apply.js +203 -113
- package/dist/src/mutate/canonical.d.ts +27 -0
- package/dist/src/mutate/canonical.js +138 -0
- package/dist/src/mutate/commit.d.ts +150 -0
- package/dist/src/mutate/commit.js +349 -0
- package/dist/src/mutate/doc-store.d.ts +3 -3
- package/dist/src/mutate/doc-store.js +21 -6
- package/dist/src/mutate/docs.d.ts +42 -87
- package/dist/src/mutate/docs.js +50 -204
- package/dist/src/mutate/effects.d.ts +363 -0
- package/dist/src/mutate/effects.js +1161 -0
- package/dist/src/mutate/materialize.d.ts +49 -0
- package/dist/src/mutate/materialize.js +174 -0
- package/dist/src/mutate/opset.d.ts +27 -2
- package/dist/src/mutate/opset.js +12 -5
- package/dist/src/mutate/plan-update.d.ts +1 -46
- package/dist/src/mutate/plan-update.js +19 -67
- package/dist/src/mutate/plan.d.ts +210 -0
- package/dist/src/mutate/plan.js +512 -0
- package/dist/src/mutate/select.d.ts +89 -0
- package/dist/src/mutate/select.js +879 -0
- package/dist/src/mutate/sugar.d.ts +135 -0
- package/dist/src/mutate/sugar.js +167 -0
- package/dist/src/mutate/tree.d.ts +4 -2
- package/dist/src/oqx-js/planner.js +4 -0
- package/dist/src/oqx-js/run.d.ts +28 -1
- package/dist/src/oqx-js/run.js +85 -22
- package/dist/src/spec-versions.d.ts +6 -6
- package/dist/src/spec-versions.js +5 -5
- package/dist/src/surface-paths.d.ts +17 -26
- package/dist/src/surface-paths.js +27 -22
- package/dist/src/sync/checkpoint.d.ts +5 -2
- package/dist/src/sync/checkpoint.js +10 -6
- package/dist/src/sync/freshness.d.ts +10 -7
- package/dist/src/sync/freshness.js +15 -17
- package/dist/src/sync/observe.d.ts +4 -1
- package/dist/src/sync/observe.js +56 -5
- package/dist/src/sync/recovery.d.ts +4 -0
- package/dist/src/sync/recovery.js +4 -1
- package/dist/src/sync/stat-cache.d.ts +7 -0
- package/dist/src/sync/stat-cache.js +22 -0
- package/dist/src/sync/tombstone.js +23 -19
- package/dist/src/sync/watcher.d.ts +2 -0
- package/dist/src/sync/watcher.js +4 -1
- package/dist/src/sync/writer-lock.d.ts +15 -0
- package/dist/src/sync/writer-lock.js +104 -17
- package/dist/src/version.d.ts +7 -2
- package/dist/src/version.js +5 -2
- package/package.json +2 -2
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { openSync, closeSync, readFileSync, writeFileSync, unlinkSync, existsSync, mkdirSync } from "node:fs";
|
|
2
|
-
import { join } from "node:path";
|
|
1
|
+
import { openSync, closeSync, readFileSync, writeFileSync, unlinkSync, existsSync, mkdirSync, realpathSync } from "node:fs";
|
|
2
|
+
import { join, resolve } from "node:path";
|
|
3
3
|
// Cross-process writer lock (11 §3.2). The write protocol's per-repo writer
|
|
4
4
|
// mutex (04 §6 step 1) generalizes to an advisory lock on
|
|
5
5
|
// <workspace>/.omgbase/writer.lock, held for steps 2–7. All writers — one-shot
|
|
@@ -72,33 +72,120 @@ const SLEEP_CELL = new Int32Array(new SharedArrayBuffer(4));
|
|
|
72
72
|
function sleepSync(ms) {
|
|
73
73
|
Atomics.wait(SLEEP_CELL, 0, 0, ms);
|
|
74
74
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
75
|
+
// Re-entrancy (one acquire per host write, spec/sync §7). The lock is a
|
|
76
|
+
// per-PROCESS claim — the record names a pid and liveness is the pid's — so a
|
|
77
|
+
// second `withWriterLock` on the same directory from the holder must not poll
|
|
78
|
+
// against itself (it would see its own live pid and time out). The process
|
|
79
|
+
// keeps a depth per lock path: the first entry acquires the file, nested
|
|
80
|
+
// entries run their fn directly, and the exit that brings the depth back to
|
|
81
|
+
// zero releases. Nesting happens by design — a tool-boundary lock around a
|
|
82
|
+
// whole MCP call (`mcp/server.ts`) encloses the kernel's own commit-phase lock
|
|
83
|
+
// (`mutate/apply.ts`), and a watcher checkpoint may run while a verb holds the
|
|
84
|
+
// lock — and since the file is one claim per process, the depth also lets an
|
|
85
|
+
// async holder (a checkpoint awaiting the adapter) and a tool call interleave
|
|
86
|
+
// on the event loop without either releasing the other's claim. Cross-process
|
|
87
|
+
// semantics are unchanged: a foreign live holder is waited for, never stolen.
|
|
88
|
+
const held = new Map();
|
|
89
|
+
// The depth is keyed by the directory's real path, so two spellings of one
|
|
90
|
+
// workspace (relative/absolute, a symlinked tree) are one claim; a directory
|
|
91
|
+
// that does not exist yet (a probe before the first write) keys by its resolved path.
|
|
92
|
+
function lockPathOf(omgbaseDir) {
|
|
93
|
+
let dir;
|
|
94
|
+
try {
|
|
95
|
+
dir = realpathSync.native(omgbaseDir);
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
dir = resolve(omgbaseDir);
|
|
99
|
+
}
|
|
100
|
+
return join(dir, LOCK_NAME);
|
|
101
|
+
}
|
|
102
|
+
// Process-wide defaults for the wait (spec/sync §7: 5 s, polled every 25 ms).
|
|
103
|
+
// A host may override them for the whole process — the CLI's
|
|
104
|
+
// `OMGBASE_WRITER_LOCK_TIMEOUT_MS` test seam does, so a verb's kernel-level lock
|
|
105
|
+
// and `omg mcp`'s tool-boundary lock shorten together, like the Rust binary's.
|
|
106
|
+
// Per-call `opts` still win.
|
|
107
|
+
let defaults = { timeoutMs: 5000, pollMs: 25 };
|
|
108
|
+
/** Override the process-wide wait defaults (a test seam; the poll never outlasts the timeout). */
|
|
109
|
+
export function setWriterLockDefaults(opts) {
|
|
80
110
|
const timeoutMs = opts.timeoutMs ?? 5000;
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
111
|
+
defaults = { timeoutMs, pollMs: Math.min(opts.pollMs ?? 25, timeoutMs) };
|
|
112
|
+
}
|
|
113
|
+
/** The process-wide wait defaults in force. */
|
|
114
|
+
export function writerLockDefaults() {
|
|
115
|
+
return { ...defaults };
|
|
116
|
+
}
|
|
117
|
+
function acquire(lockPath, opts) {
|
|
118
|
+
const timeoutMs = opts.timeoutMs ?? defaults.timeoutMs;
|
|
119
|
+
const pollMs = opts.pollMs ?? defaults.pollMs;
|
|
84
120
|
const deadline = Date.now() + timeoutMs;
|
|
85
121
|
while (!tryAcquire(lockPath)) {
|
|
86
122
|
if (Date.now() >= deadline)
|
|
87
123
|
throw new WriterLockTimeout(lockPath, readHolderPid(lockPath));
|
|
88
124
|
sleepSync(pollMs);
|
|
89
125
|
}
|
|
126
|
+
}
|
|
127
|
+
function release(lockPath) {
|
|
128
|
+
try {
|
|
129
|
+
if (existsSync(lockPath))
|
|
130
|
+
unlinkSync(lockPath);
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
/* best-effort release */
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/** Enter the lock: acquire the file at depth 0, otherwise just deepen. */
|
|
137
|
+
function enter(omgbaseDir, opts) {
|
|
138
|
+
mkdirSync(omgbaseDir, { recursive: true });
|
|
139
|
+
const lockPath = lockPathOf(omgbaseDir);
|
|
140
|
+
const depth = held.get(lockPath) ?? 0;
|
|
141
|
+
if (depth === 0)
|
|
142
|
+
acquire(lockPath, opts);
|
|
143
|
+
held.set(lockPath, depth + 1);
|
|
144
|
+
return lockPath;
|
|
145
|
+
}
|
|
146
|
+
/** Leave the lock: shallow, and release the file when the depth reaches 0. */
|
|
147
|
+
function exit(lockPath) {
|
|
148
|
+
const depth = (held.get(lockPath) ?? 1) - 1;
|
|
149
|
+
if (depth > 0) {
|
|
150
|
+
held.set(lockPath, depth);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
held.delete(lockPath);
|
|
154
|
+
release(lockPath);
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Run fn while holding the workspace writer lock. Blocks (polling) until the
|
|
158
|
+
* lock is free or the timeout elapses, then releases the lock even if fn throws.
|
|
159
|
+
* Re-entrant within the process: a nested call on the same directory runs fn
|
|
160
|
+
* directly and the outermost exit releases.
|
|
161
|
+
*/
|
|
162
|
+
export function withWriterLock(omgbaseDir, fn, opts = {}) {
|
|
163
|
+
const lockPath = enter(omgbaseDir, opts);
|
|
90
164
|
try {
|
|
91
165
|
return fn();
|
|
92
166
|
}
|
|
93
167
|
finally {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
168
|
+
exit(lockPath);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* The async form: the lock is held until the promise `fn` returns settles (a
|
|
173
|
+
* sync `withWriterLock` around an async fn would release at the first await).
|
|
174
|
+
* Acquisition still polls synchronously — the wait is the same bounded
|
|
175
|
+
* contention window as the sync form's. Same re-entrancy.
|
|
176
|
+
*/
|
|
177
|
+
export async function withWriterLockAsync(omgbaseDir, fn, opts = {}) {
|
|
178
|
+
const lockPath = enter(omgbaseDir, opts);
|
|
179
|
+
try {
|
|
180
|
+
return await fn();
|
|
101
181
|
}
|
|
182
|
+
finally {
|
|
183
|
+
exit(lockPath);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
/** True while THIS process holds the lock (at any depth) — a test seam for "ran under the lock". */
|
|
187
|
+
export function writerLockHeld(omgbaseDir) {
|
|
188
|
+
return (held.get(lockPathOf(omgbaseDir)) ?? 0) > 0;
|
|
102
189
|
}
|
|
103
190
|
/** True if no live writer holds the lock (probe without acquiring). */
|
|
104
191
|
export function writerLockFree(omgbaseDir) {
|
package/dist/src/version.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Store } from "./core/store/store.js";
|
|
2
2
|
import { SPEC_VERSIONS } from "./spec-versions.js";
|
|
3
|
+
import type { ToolsProfile } from "./mcp/server.js";
|
|
3
4
|
export interface HostInfo {
|
|
4
5
|
/** the serving binary's own version — `omgbase`'s package.json (what `--version` prints) */
|
|
5
6
|
version: string;
|
|
@@ -25,6 +26,8 @@ export interface VersionInfo {
|
|
|
25
26
|
runtime: string;
|
|
26
27
|
commit: string | null;
|
|
27
28
|
built: string | null;
|
|
29
|
+
/** the catalog profile the server serves (spec/surface §4 "Profiles", 4.3): `omg version` itself reports `full` */
|
|
30
|
+
profile: ToolsProfile;
|
|
28
31
|
}
|
|
29
32
|
/** The omgbase packages the reference binary is built from (spec/surface §4). */
|
|
30
33
|
export declare const COMPONENT_PACKAGES: readonly ["omgbase", "@omgbase/core", "@omgbase/oqx", "@omgbase/sync", "@omgbase/fs-adapter"];
|
|
@@ -34,6 +37,8 @@ export declare function nearestPackageVersion(fromUrl: string, name: string): st
|
|
|
34
37
|
export declare function coreVersion(): string;
|
|
35
38
|
/**
|
|
36
39
|
* The `version` result. `store` supplies `schema`; `host` the binary's own
|
|
37
|
-
* version, build info and resolution anchor (see the module comment)
|
|
40
|
+
* version, build info and resolution anchor (see the module comment);
|
|
41
|
+
* `profile` the catalog profile served (the MCP server's `--tools`; the CLI's
|
|
42
|
+
* own `version` verb is the full library and says `full`).
|
|
38
43
|
*/
|
|
39
|
-
export declare function versionInfo(store?: Store | null, host?: HostInfo): VersionInfo;
|
|
44
|
+
export declare function versionInfo(store?: Store | null, host?: HostInfo, profile?: ToolsProfile): VersionInfo;
|
package/dist/src/version.js
CHANGED
|
@@ -73,9 +73,11 @@ export function coreVersion() {
|
|
|
73
73
|
}
|
|
74
74
|
/**
|
|
75
75
|
* The `version` result. `store` supplies `schema`; `host` the binary's own
|
|
76
|
-
* version, build info and resolution anchor (see the module comment)
|
|
76
|
+
* version, build info and resolution anchor (see the module comment);
|
|
77
|
+
* `profile` the catalog profile served (the MCP server's `--tools`; the CLI's
|
|
78
|
+
* own `version` verb is the full library and says `full`).
|
|
77
79
|
*/
|
|
78
|
-
export function versionInfo(store, host) {
|
|
80
|
+
export function versionInfo(store, host, profile = "full") {
|
|
79
81
|
const anchors = [...(host?.resolveFrom ? [host.resolveFrom] : []), import.meta.url];
|
|
80
82
|
const components = {};
|
|
81
83
|
for (const name of COMPONENT_PACKAGES) {
|
|
@@ -98,6 +100,7 @@ export function versionInfo(store, host) {
|
|
|
98
100
|
runtime: `node ${process.versions.node}`,
|
|
99
101
|
commit: host?.commit ?? null,
|
|
100
102
|
built: host?.built ?? null,
|
|
103
|
+
profile,
|
|
101
104
|
};
|
|
102
105
|
}
|
|
103
106
|
//# sourceMappingURL=version.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@omgbase/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"description": "Open Markdown Graph Base — embedded engine",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
57
|
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
58
|
-
"@omgbase/oqx": "^0.
|
|
58
|
+
"@omgbase/oqx": "^0.21.0",
|
|
59
59
|
"better-sqlite3": "^13.0.3",
|
|
60
60
|
"mdast-util-from-markdown": "^2.0.2",
|
|
61
61
|
"mdast-util-frontmatter": "^2.0.1",
|