datanotes 2.0.1 → 3.0.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/NOTICE +9 -0
- package/README.md +207 -28
- package/SECURITY.md +92 -45
- package/dist/THIRD-PARTY-NOTICES.txt +682 -0
- package/dist/api/catalog.d.cts +69 -0
- package/dist/api/catalog.d.ts +50 -2
- package/dist/api/cli-options.d.cts +39 -0
- package/dist/api/cli-options.d.ts +39 -0
- package/dist/api/client.d.cts +89 -0
- package/dist/api/client.d.ts +19 -30
- package/dist/api/docs.d.cts +211 -0
- package/dist/api/docs.d.ts +211 -0
- package/dist/api/guidance.d.cts +81 -0
- package/dist/api/guidance.d.ts +81 -0
- package/dist/api/index.cjs +6610 -2247
- package/dist/api/index.d.cts +13 -0
- package/dist/api/index.d.ts +10 -7
- package/dist/api/index.js +6610 -2247
- package/dist/api/local.cjs +118 -6
- package/dist/api/local.d.cts +95 -0
- package/dist/api/local.d.ts +70 -6
- package/dist/api/local.js +104 -6
- package/dist/api/mcp.cjs +17384 -0
- package/dist/api/mcp.d.cts +108 -0
- package/dist/api/mcp.d.ts +108 -0
- package/dist/api/mcp.js +17334 -0
- package/dist/api/otlp.d.cts +65 -0
- package/dist/api/redact.d.cts +17 -0
- package/dist/api/redact.d.ts +17 -0
- package/dist/api/requests.d.cts +26 -0
- package/dist/api/requests.d.ts +14 -16
- package/dist/api/results.d.cts +16 -0
- package/dist/api/results.d.ts +2 -1
- package/dist/api/tokens.d.cts +7 -0
- package/dist/datanotes.cjs +62511 -28069
- package/dist/docs/administration/configuration.md +59 -0
- package/dist/docs/administration/docker.md +80 -0
- package/dist/docs/administration/git-sync.md +71 -0
- package/dist/docs/administration/limits.md +69 -0
- package/dist/docs/administration/run-the-service.md +219 -0
- package/dist/docs/administration/status.md +61 -0
- package/dist/docs/administration/use-with-obsidian.md +75 -0
- package/dist/docs/api/operations/add_column.md +49 -0
- package/dist/docs/api/operations/add_constraint.md +55 -0
- package/dist/docs/api/operations/adopt_rows.md +50 -0
- package/dist/docs/api/operations/align_relations.md +41 -0
- package/dist/docs/api/operations/alter_column.md +46 -0
- package/dist/docs/api/operations/alter_table.md +67 -0
- package/dist/docs/api/operations/batch.md +48 -0
- package/dist/docs/api/operations/changes_stream.md +28 -0
- package/dist/docs/api/operations/check_table.md +29 -0
- package/dist/docs/api/operations/clone_table.md +48 -0
- package/dist/docs/api/operations/comment_on.md +42 -0
- package/dist/docs/api/operations/configure_database.md +52 -0
- package/dist/docs/api/operations/create_table.md +78 -0
- package/dist/docs/api/operations/create_view.md +54 -0
- package/dist/docs/api/operations/create_webhook.md +37 -0
- package/dist/docs/api/operations/define_base.md +52 -0
- package/dist/docs/api/operations/delete_rows.md +43 -0
- package/dist/docs/api/operations/delete_webhook.md +31 -0
- package/dist/docs/api/operations/describe_database.md +30 -0
- package/dist/docs/api/operations/describe_history.md +40 -0
- package/dist/docs/api/operations/describe_semantic_index.md +24 -0
- package/dist/docs/api/operations/describe_table.md +38 -0
- package/dist/docs/api/operations/docs.md +13 -0
- package/dist/docs/api/operations/drop_column.md +41 -0
- package/dist/docs/api/operations/drop_constraint.md +39 -0
- package/dist/docs/api/operations/drop_table.md +43 -0
- package/dist/docs/api/operations/drop_view.md +38 -0
- package/dist/docs/api/operations/engine_status.md +23 -0
- package/dist/docs/api/operations/graph_neighbors.md +32 -0
- package/dist/docs/api/operations/graph_path.md +32 -0
- package/dist/docs/api/operations/graph_traverse.md +43 -0
- package/dist/docs/api/operations/identify_session.md +24 -0
- package/dist/docs/api/operations/infer_table.md +43 -0
- package/dist/docs/api/operations/insert_row.md +48 -0
- package/dist/docs/api/operations/list_changes.md +35 -0
- package/dist/docs/api/operations/list_folder.md +28 -0
- package/dist/docs/api/operations/list_link_targets.md +44 -0
- package/dist/docs/api/operations/list_migrations.md +35 -0
- package/dist/docs/api/operations/list_tables.md +32 -0
- package/dist/docs/api/operations/list_webhooks.md +23 -0
- package/dist/docs/api/operations/manual_index.md +14 -0
- package/dist/docs/api/operations/manual_page.md +22 -0
- package/dist/docs/api/operations/merge_rows.md +44 -0
- package/dist/docs/api/operations/move_rows.md +52 -0
- package/dist/docs/api/operations/openapi.md +13 -0
- package/dist/docs/api/operations/openapi_post.md +20 -0
- package/dist/docs/api/operations/read_note.md +29 -0
- package/dist/docs/api/operations/recompute_columns.md +44 -0
- package/dist/docs/api/operations/record_tool_call.md +36 -0
- package/dist/docs/api/operations/rename_column.md +44 -0
- package/dist/docs/api/operations/rename_value.md +45 -0
- package/dist/docs/api/operations/restore_database.md +51 -0
- package/dist/docs/api/operations/restore_table.md +46 -0
- package/dist/docs/api/operations/search_notes.md +28 -0
- package/dist/docs/api/operations/select_rows.md +61 -0
- package/dist/docs/api/operations/semantic_search.md +34 -0
- package/dist/docs/api/operations/semantic_search_docs.md +26 -0
- package/dist/docs/api/operations/set_table_bases.md +44 -0
- package/dist/docs/api/operations/status.md +13 -0
- package/dist/docs/api/operations/stop_engine.md +23 -0
- package/dist/docs/api/operations/sync_semantic_index.md +33 -0
- package/dist/docs/api/operations/table_changes.md +31 -0
- package/dist/docs/api/operations/tags/auxiliary.md +14 -0
- package/dist/docs/api/operations/tags/change-feed.md +8 -0
- package/dist/docs/api/operations/tags/history.md +17 -0
- package/dist/docs/api/operations/tags/queries.md +15 -0
- package/dist/docs/api/operations/tags/rows-dml.md +16 -0
- package/dist/docs/api/operations/tags/schema-ddl.md +22 -0
- package/dist/docs/api/operations/tags/service.md +15 -0
- package/dist/docs/api/operations/tags/tokens.md +13 -0
- package/dist/docs/api/operations/tokens_create.md +34 -0
- package/dist/docs/api/operations/tokens_delete.md +29 -0
- package/dist/docs/api/operations/tokens_list.md +21 -0
- package/dist/docs/api/operations/tokens_me.md +20 -0
- package/dist/docs/api/operations/tokens_rotate.md +29 -0
- package/dist/docs/api/operations/tokens_update.md +34 -0
- package/dist/docs/api/operations/undo_migration.md +46 -0
- package/dist/docs/api/operations/update_row.md +60 -0
- package/dist/docs/api/operations/update_rows.md +46 -0
- package/dist/docs/api/operations/vacuum_table.md +37 -0
- package/dist/docs/api.md +135 -0
- package/dist/docs/develop/build-an-app.md +320 -0
- package/dist/docs/develop/build-on-datanotes.md +138 -0
- package/dist/docs/develop/change-feed-and-webhooks.md +70 -0
- package/dist/docs/develop/client.md +116 -0
- package/dist/docs/develop/concurrency.md +153 -0
- package/dist/docs/develop/http-api.md +79 -0
- package/dist/docs/develop/telemetry.md +86 -0
- package/dist/docs/guides/agents/docs-offline.md +99 -0
- package/dist/docs/guides/agents/how-agents-use-datanotes.md +124 -0
- package/dist/docs/guides/agents/prompts.md +495 -0
- package/dist/docs/guides/agents/reading-errors.md +112 -0
- package/dist/docs/guides/data-quality/check-constraints.md +379 -0
- package/dist/docs/guides/data-quality/check-table.md +63 -0
- package/dist/docs/guides/data-quality/hand-edits.md +507 -0
- package/dist/docs/guides/data-quality/sync-between-devices.md +51 -0
- package/dist/docs/guides/data-quality/validation.md +90 -0
- package/dist/docs/guides/database-objects/bases.md +480 -0
- package/dist/docs/guides/database-objects/catalogs-and-schemas.md +897 -0
- package/dist/docs/guides/database-objects/columns.md +1332 -0
- package/dist/docs/guides/database-objects/relations.md +1454 -0
- package/dist/docs/guides/database-objects/tables.md +1404 -0
- package/dist/docs/guides/database-objects/views.md +1003 -0
- package/dist/docs/guides/history/back-up.md +195 -0
- package/dist/docs/guides/history/change-data-feed.md +247 -0
- package/dist/docs/guides/history/journal-and-undo.md +262 -0
- package/dist/docs/guides/history/restore.md +341 -0
- package/dist/docs/guides/history/table-history.md +657 -0
- package/dist/docs/guides/history/vacuum.md +35 -0
- package/dist/docs/guides/read-and-write/automatic-links.md +768 -0
- package/dist/docs/guides/read-and-write/batch.md +139 -0
- package/dist/docs/guides/read-and-write/import-notes.md +896 -0
- package/dist/docs/guides/read-and-write/joins-and-aggregates.md +251 -0
- package/dist/docs/guides/read-and-write/read-and-filter.md +536 -0
- package/dist/docs/guides/read-and-write/write-rows.md +573 -0
- package/dist/docs/guides/search/link-graph.md +146 -0
- package/dist/docs/guides/search/semantic.md +133 -0
- package/dist/docs/guides/search/text.md +142 -0
- package/dist/docs/guides/security/security-model.md +110 -0
- package/dist/docs/guides/security/tokens.md +60 -0
- package/dist/docs/guides/security/who-did-what.md +42 -0
- package/dist/docs/index.md +113 -0
- package/dist/docs/llms.txt +33 -0
- package/dist/docs/reference/cli.md +136 -0
- package/dist/docs/reference/compatibility.md +63 -0
- package/dist/docs/reference/errors.md +72 -0
- package/dist/docs/reference/glossary.md +161 -0
- package/dist/docs/reference/operations/auxiliary.md +116 -0
- package/dist/docs/reference/operations/history.md +168 -0
- package/dist/docs/reference/operations/queries.md +169 -0
- package/dist/docs/reference/operations/rows.md +185 -0
- package/dist/docs/reference/operations/schema.md +316 -0
- package/dist/docs/reference/operations.md +96 -0
- package/dist/docs/reference/settings.md +31 -0
- package/dist/docs/release-notes.md +67 -0
- package/dist/docs/start/connect-agents.md +204 -0
- package/dist/docs/start/how-it-works.md +317 -0
- package/dist/docs/start/quick-start.md +126 -0
- package/package.json +58 -14
package/dist/api/local.cjs
CHANGED
|
@@ -30,8 +30,22 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/api/local.ts
|
|
31
31
|
var local_exports = {};
|
|
32
32
|
__export(local_exports, {
|
|
33
|
+
DEFAULT_DATABASE_FOLDER: () => DEFAULT_DATABASE_FOLDER,
|
|
34
|
+
LOCK_FILE: () => LOCK_FILE,
|
|
33
35
|
LOCK_STALE_MS: () => LOCK_STALE_MS,
|
|
36
|
+
METASTORE: () => METASTORE,
|
|
37
|
+
REGISTRY_FILE: () => REGISTRY_FILE,
|
|
38
|
+
SETTINGS_FILE: () => SETTINGS_FILE,
|
|
39
|
+
STORE_RECORD_FILE: () => STORE_RECORD_FILE,
|
|
40
|
+
dataHome: () => dataHome,
|
|
41
|
+
databaseIdIn: () => databaseIdIn,
|
|
34
42
|
localEngine: () => localEngine,
|
|
43
|
+
readDatabaseId: () => readDatabaseId,
|
|
44
|
+
readRegistry: () => readRegistry,
|
|
45
|
+
storeLocation: () => storeLocation,
|
|
46
|
+
vaultDatabaseFolder: () => vaultDatabaseFolder,
|
|
47
|
+
vaultKey: () => vaultKey,
|
|
48
|
+
vaultMetastores: () => vaultMetastores,
|
|
35
49
|
vaultSettingsPath: () => vaultSettingsPath
|
|
36
50
|
});
|
|
37
51
|
module.exports = __toCommonJS(local_exports);
|
|
@@ -39,29 +53,127 @@ var import_node_fs = __toESM(require("node:fs"), 1);
|
|
|
39
53
|
var import_node_os = __toESM(require("node:os"), 1);
|
|
40
54
|
var import_node_path = __toESM(require("node:path"), 1);
|
|
41
55
|
var LOCK_STALE_MS = 3e4;
|
|
42
|
-
|
|
43
|
-
|
|
56
|
+
var SETTINGS_FILE = "settings.json";
|
|
57
|
+
var LOCK_FILE = "engine.lock";
|
|
58
|
+
var STORE_RECORD_FILE = "store.json";
|
|
59
|
+
var REGISTRY_FILE = "vaults.json";
|
|
60
|
+
var DEFAULT_DATABASE_FOLDER = "datanotes";
|
|
61
|
+
var METASTORE = "metastore.yaml";
|
|
62
|
+
function dataHome(env = process.env, platform = process.platform, home = import_node_os.default.homedir()) {
|
|
63
|
+
if (env.DATANOTES_DATA_HOME) return import_node_path.default.resolve(env.DATANOTES_DATA_HOME);
|
|
64
|
+
if (platform === "win32") return import_node_path.default.join(env.LOCALAPPDATA || import_node_path.default.join(home, "AppData", "Local"), "Datanotes");
|
|
65
|
+
if (platform === "darwin") return import_node_path.default.join(home, "Library", "Application Support", "Datanotes");
|
|
66
|
+
return import_node_path.default.join(env.XDG_DATA_HOME || import_node_path.default.join(home, ".local", "share"), "datanotes");
|
|
67
|
+
}
|
|
68
|
+
var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
69
|
+
function readDatabaseId(text) {
|
|
70
|
+
const m = /^id:[ \t]*(.*)$/m.exec(text);
|
|
71
|
+
const value = m ? m[1].replace(/#.*$/, "").trim().replace(/^["']|["']$/g, "") : "";
|
|
72
|
+
return UUID.test(value) ? value.toLowerCase() : null;
|
|
73
|
+
}
|
|
74
|
+
function databaseIdIn(vault, database) {
|
|
75
|
+
try {
|
|
76
|
+
return readDatabaseId(import_node_fs.default.readFileSync(import_node_path.default.join(vault, ...database.split("/"), METASTORE), "utf8"));
|
|
77
|
+
} catch {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function vaultMetastores(vault) {
|
|
82
|
+
const out = [];
|
|
83
|
+
const walk = (dir, rel) => {
|
|
84
|
+
let ents;
|
|
85
|
+
try {
|
|
86
|
+
ents = import_node_fs.default.readdirSync(dir, { withFileTypes: true });
|
|
87
|
+
} catch {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
for (const e of ents) {
|
|
91
|
+
if (e.name.startsWith(".") || e.name === "node_modules") continue;
|
|
92
|
+
const r = rel ? `${rel}/${e.name}` : e.name;
|
|
93
|
+
if (e.isDirectory()) walk(import_node_path.default.join(dir, e.name), r);
|
|
94
|
+
else if (e.name === METASTORE) out.push(rel);
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
walk(vault, "");
|
|
98
|
+
return out.sort();
|
|
99
|
+
}
|
|
100
|
+
function vaultKey(vault, platform = process.platform) {
|
|
101
|
+
let real = import_node_path.default.resolve(vault);
|
|
102
|
+
try {
|
|
103
|
+
real = import_node_fs.default.realpathSync.native(real);
|
|
104
|
+
} catch {
|
|
105
|
+
}
|
|
106
|
+
return platform === "win32" ? real.toLowerCase() : real;
|
|
44
107
|
}
|
|
45
108
|
function readJson(file) {
|
|
46
109
|
try {
|
|
47
|
-
|
|
110
|
+
const v = JSON.parse(import_node_fs.default.readFileSync(file, "utf8"));
|
|
111
|
+
return v && typeof v === "object" && !Array.isArray(v) ? v : null;
|
|
48
112
|
} catch {
|
|
49
113
|
return null;
|
|
50
114
|
}
|
|
51
115
|
}
|
|
116
|
+
function readRegistry(home) {
|
|
117
|
+
const raw = readJson(import_node_path.default.join(home, REGISTRY_FILE)) ?? {};
|
|
118
|
+
const out = {};
|
|
119
|
+
for (const [k, v] of Object.entries(raw)) {
|
|
120
|
+
const r = v;
|
|
121
|
+
if (r && typeof r.id === "string" && UUID.test(r.id) && typeof r.database === "string") out[k] = { id: r.id, database: r.database };
|
|
122
|
+
}
|
|
123
|
+
return out;
|
|
124
|
+
}
|
|
125
|
+
function vaultDatabaseFolder(vault) {
|
|
126
|
+
const found = vaultMetastores(vault);
|
|
127
|
+
return found.length === 1 ? found[0] : DEFAULT_DATABASE_FOLDER;
|
|
128
|
+
}
|
|
129
|
+
function storeLocation(vault, opts = {}) {
|
|
130
|
+
const env = opts.env ?? process.env;
|
|
131
|
+
const explicit = opts.store ?? env.DATANOTES_STORE;
|
|
132
|
+
if (explicit) {
|
|
133
|
+
const store = import_node_path.default.resolve(explicit);
|
|
134
|
+
const rec = readJson(import_node_path.default.join(store, STORE_RECORD_FILE));
|
|
135
|
+
const database2 = typeof rec?.database === "string" ? rec.database : vaultDatabaseFolder(vault);
|
|
136
|
+
return { store, id: typeof rec?.id === "string" ? rec.id : databaseIdIn(vault, database2), database: database2 };
|
|
137
|
+
}
|
|
138
|
+
const home = dataHome(env);
|
|
139
|
+
const known = readRegistry(home)[vaultKey(vault)];
|
|
140
|
+
if (known) return { store: import_node_path.default.join(home, known.id), id: known.id, database: known.database };
|
|
141
|
+
const database = vaultDatabaseFolder(vault);
|
|
142
|
+
const id = databaseIdIn(vault, database);
|
|
143
|
+
return id ? { store: import_node_path.default.join(home, id), id, database } : null;
|
|
144
|
+
}
|
|
145
|
+
function vaultSettingsPath(vault, opts = {}) {
|
|
146
|
+
const loc = storeLocation(vault, opts);
|
|
147
|
+
return loc ? import_node_path.default.join(loc.store, SETTINGS_FILE) : null;
|
|
148
|
+
}
|
|
52
149
|
function localEngine(vault, opts = {}) {
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
const lock = readJson(import_node_path.default.join(
|
|
150
|
+
const loc = storeLocation(vault, opts);
|
|
151
|
+
if (!loc) return null;
|
|
152
|
+
const lock = readJson(import_node_path.default.join(loc.store, LOCK_FILE));
|
|
56
153
|
if (!lock || (opts.now ?? Date.now()) - Date.parse(lock.heartbeat) >= LOCK_STALE_MS) return null;
|
|
57
154
|
if (lock.host !== import_node_os.default.hostname()) throw new Error(`the vault is held by an engine on ${lock.host}; connect to it by its address`);
|
|
155
|
+
const settings = readJson(opts.settingsFile ?? import_node_path.default.join(loc.store, SETTINGS_FILE)) ?? {};
|
|
58
156
|
const token = typeof settings.httpServerToken === "string" ? settings.httpServerToken : "";
|
|
59
157
|
if (!token) return null;
|
|
60
158
|
return { url: `http://${opts.host ?? "127.0.0.1"}:${lock.port}`, token, lock };
|
|
61
159
|
}
|
|
62
160
|
// Annotate the CommonJS export names for ESM import in node:
|
|
63
161
|
0 && (module.exports = {
|
|
162
|
+
DEFAULT_DATABASE_FOLDER,
|
|
163
|
+
LOCK_FILE,
|
|
64
164
|
LOCK_STALE_MS,
|
|
165
|
+
METASTORE,
|
|
166
|
+
REGISTRY_FILE,
|
|
167
|
+
SETTINGS_FILE,
|
|
168
|
+
STORE_RECORD_FILE,
|
|
169
|
+
dataHome,
|
|
170
|
+
databaseIdIn,
|
|
65
171
|
localEngine,
|
|
172
|
+
readDatabaseId,
|
|
173
|
+
readRegistry,
|
|
174
|
+
storeLocation,
|
|
175
|
+
vaultDatabaseFolder,
|
|
176
|
+
vaultKey,
|
|
177
|
+
vaultMetastores,
|
|
66
178
|
vaultSettingsPath
|
|
67
179
|
});
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Find the engine running on a vault of this machine (Node only): its address and the owner's
|
|
3
|
+
* token, read from the database's store, where the engine keeps its settings and the lock it holds
|
|
4
|
+
* while it runs.
|
|
5
|
+
*
|
|
6
|
+
* The store is outside the vault, in the operating system's per-user data folder:
|
|
7
|
+
* `<data home>/<database id>/`, the id being `id` in the database's `metastore.yaml`. The data home
|
|
8
|
+
* is `%LOCALAPPDATA%\Datanotes` on Windows, `~/Library/Application Support/Datanotes` on macOS and
|
|
9
|
+
* `$XDG_DATA_HOME/datanotes` (`~/.local/share/datanotes`) elsewhere. `DATANOTES_DATA_HOME` moves the
|
|
10
|
+
* data home, `DATANOTES_STORE` (or `--store`) names the store folder itself. The engine finds a
|
|
11
|
+
* vault's database again through `vaults.json` in the data home (vault → id and database folder).
|
|
12
|
+
*/
|
|
13
|
+
/** The lock an engine writes in its store (`<store>/engine.lock`). */
|
|
14
|
+
export interface EngineLock {
|
|
15
|
+
kind: "service";
|
|
16
|
+
pid: number;
|
|
17
|
+
host: string;
|
|
18
|
+
port: number;
|
|
19
|
+
startedAt: string;
|
|
20
|
+
heartbeat: string;
|
|
21
|
+
}
|
|
22
|
+
/** A lock whose heartbeat is older than this belongs to an engine that stopped. */
|
|
23
|
+
export declare const LOCK_STALE_MS = 30000;
|
|
24
|
+
/** The engine's settings file, in the store. */
|
|
25
|
+
export declare const SETTINGS_FILE = "settings.json";
|
|
26
|
+
/** The lock, in the store. */
|
|
27
|
+
export declare const LOCK_FILE = "engine.lock";
|
|
28
|
+
/** The store's record of its database: id, vault, database folder. */
|
|
29
|
+
export declare const STORE_RECORD_FILE = "store.json";
|
|
30
|
+
/** In the data home: the vaults this machine's engines opened, with their database. */
|
|
31
|
+
export declare const REGISTRY_FILE = "vaults.json";
|
|
32
|
+
/** The database folder of a new database: a top-level folder of the vault. */
|
|
33
|
+
export declare const DEFAULT_DATABASE_FOLDER = "datanotes";
|
|
34
|
+
export declare const METASTORE = "metastore.yaml";
|
|
35
|
+
type Env = Record<string, string | undefined>;
|
|
36
|
+
/** The data home: the folder that holds the stores of this user's databases. */
|
|
37
|
+
export declare function dataHome(env?: Env, platform?: string, home?: string): string;
|
|
38
|
+
/** The database id of a `metastore.yaml` text (its top-level `id:`), or null. */
|
|
39
|
+
export declare function readDatabaseId(text: string): string | null;
|
|
40
|
+
/** The database id of a vault's database folder, or null (no metastore, or no id in it). */
|
|
41
|
+
export declare function databaseIdIn(vault: string, database: string): string | null;
|
|
42
|
+
/**
|
|
43
|
+
* The folders of a vault on disk that hold a `metastore.yaml` (vault-relative with `/`, sorted).
|
|
44
|
+
* A vault has exactly one database folder, anywhere ("" for the root: --database). Dot-folders and
|
|
45
|
+
* `node_modules` are left out.
|
|
46
|
+
*/
|
|
47
|
+
export declare function vaultMetastores(vault: string): string[];
|
|
48
|
+
/** A vault as the registry names it: its real path (lowercase on Windows, whose paths ignore case). */
|
|
49
|
+
export declare function vaultKey(vault: string, platform?: string): string;
|
|
50
|
+
export interface VaultRecord {
|
|
51
|
+
/** The database id. */
|
|
52
|
+
id: string;
|
|
53
|
+
/** The database folder, vault-relative. */
|
|
54
|
+
database: string;
|
|
55
|
+
}
|
|
56
|
+
/** The registry of a data home: vault key → database id and folder. */
|
|
57
|
+
export declare function readRegistry(home: string): Record<string, VaultRecord>;
|
|
58
|
+
export interface StoreLocation {
|
|
59
|
+
/** The store folder. */
|
|
60
|
+
store: string;
|
|
61
|
+
/** The database id (null: a store named by DATANOTES_STORE whose record does not say it). */
|
|
62
|
+
id: string | null;
|
|
63
|
+
/** The database folder, vault-relative. */
|
|
64
|
+
database: string;
|
|
65
|
+
}
|
|
66
|
+
export interface LocateOptions {
|
|
67
|
+
env?: Env;
|
|
68
|
+
/** The store folder itself (`--store`; else DATANOTES_STORE). */
|
|
69
|
+
store?: string;
|
|
70
|
+
}
|
|
71
|
+
/** The vault's one folder with a `metastore.yaml`, else `datanotes`. */
|
|
72
|
+
export declare function vaultDatabaseFolder(vault: string): string;
|
|
73
|
+
/**
|
|
74
|
+
* Where the store of a vault's database is, as the engine finds it: the store named by `--store`
|
|
75
|
+
* or DATANOTES_STORE; else the database this machine last opened on the vault (`vaults.json`);
|
|
76
|
+
* else the id in the vault's one `metastore.yaml`. Null when none of them says.
|
|
77
|
+
*/
|
|
78
|
+
export declare function storeLocation(vault: string, opts?: LocateOptions): StoreLocation | null;
|
|
79
|
+
export interface LocalEngine {
|
|
80
|
+
url: string;
|
|
81
|
+
token: string;
|
|
82
|
+
lock: EngineLock;
|
|
83
|
+
}
|
|
84
|
+
/** The settings file of a vault's engine, in its store; null while the vault has no store yet. */
|
|
85
|
+
export declare function vaultSettingsPath(vault: string, opts?: LocateOptions): string | null;
|
|
86
|
+
/**
|
|
87
|
+
* The engine holding `vault`, or null when none runs. Throws when the lock is fresh but held on
|
|
88
|
+
* another machine (a store on a shared folder): that engine is not reachable from here.
|
|
89
|
+
*/
|
|
90
|
+
export declare function localEngine(vault: string, opts?: LocateOptions & {
|
|
91
|
+
settingsFile?: string;
|
|
92
|
+
host?: string;
|
|
93
|
+
now?: number;
|
|
94
|
+
}): LocalEngine | null;
|
|
95
|
+
export {};
|
package/dist/api/local.d.ts
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Find the engine running on a vault of this machine (Node only): its address and the owner's
|
|
3
|
-
* token, read from the
|
|
3
|
+
* token, read from the database's store, where the engine keeps its settings and the lock it holds
|
|
4
|
+
* while it runs.
|
|
5
|
+
*
|
|
6
|
+
* The store is outside the vault, in the operating system's per-user data folder:
|
|
7
|
+
* `<data home>/<database id>/`, the id being `id` in the database's `metastore.yaml`. The data home
|
|
8
|
+
* is `%LOCALAPPDATA%\Datanotes` on Windows, `~/Library/Application Support/Datanotes` on macOS and
|
|
9
|
+
* `$XDG_DATA_HOME/datanotes` (`~/.local/share/datanotes`) elsewhere. `DATANOTES_DATA_HOME` moves the
|
|
10
|
+
* data home, `DATANOTES_STORE` (or `--store`) names the store folder itself. The engine finds a
|
|
11
|
+
* vault's database again through `vaults.json` in the data home (vault → id and database folder).
|
|
4
12
|
*/
|
|
5
|
-
/** The lock an engine writes in its store
|
|
13
|
+
/** The lock an engine writes in its store (`<store>/engine.lock`). */
|
|
6
14
|
export interface EngineLock {
|
|
7
15
|
kind: "service";
|
|
8
16
|
pid: number;
|
|
@@ -13,19 +21,75 @@ export interface EngineLock {
|
|
|
13
21
|
}
|
|
14
22
|
/** A lock whose heartbeat is older than this belongs to an engine that stopped. */
|
|
15
23
|
export declare const LOCK_STALE_MS = 30000;
|
|
24
|
+
/** The engine's settings file, in the store. */
|
|
25
|
+
export declare const SETTINGS_FILE = "settings.json";
|
|
26
|
+
/** The lock, in the store. */
|
|
27
|
+
export declare const LOCK_FILE = "engine.lock";
|
|
28
|
+
/** The store's record of its database: id, vault, database folder. */
|
|
29
|
+
export declare const STORE_RECORD_FILE = "store.json";
|
|
30
|
+
/** In the data home: the vaults this machine's engines opened, with their database. */
|
|
31
|
+
export declare const REGISTRY_FILE = "vaults.json";
|
|
32
|
+
/** The database folder of a new database: a top-level folder of the vault. */
|
|
33
|
+
export declare const DEFAULT_DATABASE_FOLDER = "datanotes";
|
|
34
|
+
export declare const METASTORE = "metastore.yaml";
|
|
35
|
+
type Env = Record<string, string | undefined>;
|
|
36
|
+
/** The data home: the folder that holds the stores of this user's databases. */
|
|
37
|
+
export declare function dataHome(env?: Env, platform?: string, home?: string): string;
|
|
38
|
+
/** The database id of a `metastore.yaml` text (its top-level `id:`), or null. */
|
|
39
|
+
export declare function readDatabaseId(text: string): string | null;
|
|
40
|
+
/** The database id of a vault's database folder, or null (no metastore, or no id in it). */
|
|
41
|
+
export declare function databaseIdIn(vault: string, database: string): string | null;
|
|
42
|
+
/**
|
|
43
|
+
* The folders of a vault on disk that hold a `metastore.yaml` (vault-relative with `/`, sorted).
|
|
44
|
+
* A vault has exactly one database folder, anywhere ("" for the root: --database). Dot-folders and
|
|
45
|
+
* `node_modules` are left out.
|
|
46
|
+
*/
|
|
47
|
+
export declare function vaultMetastores(vault: string): string[];
|
|
48
|
+
/** A vault as the registry names it: its real path (lowercase on Windows, whose paths ignore case). */
|
|
49
|
+
export declare function vaultKey(vault: string, platform?: string): string;
|
|
50
|
+
export interface VaultRecord {
|
|
51
|
+
/** The database id. */
|
|
52
|
+
id: string;
|
|
53
|
+
/** The database folder, vault-relative. */
|
|
54
|
+
database: string;
|
|
55
|
+
}
|
|
56
|
+
/** The registry of a data home: vault key → database id and folder. */
|
|
57
|
+
export declare function readRegistry(home: string): Record<string, VaultRecord>;
|
|
58
|
+
export interface StoreLocation {
|
|
59
|
+
/** The store folder. */
|
|
60
|
+
store: string;
|
|
61
|
+
/** The database id (null: a store named by DATANOTES_STORE whose record does not say it). */
|
|
62
|
+
id: string | null;
|
|
63
|
+
/** The database folder, vault-relative. */
|
|
64
|
+
database: string;
|
|
65
|
+
}
|
|
66
|
+
export interface LocateOptions {
|
|
67
|
+
env?: Env;
|
|
68
|
+
/** The store folder itself (`--store`; else DATANOTES_STORE). */
|
|
69
|
+
store?: string;
|
|
70
|
+
}
|
|
71
|
+
/** The vault's one folder with a `metastore.yaml`, else `datanotes`. */
|
|
72
|
+
export declare function vaultDatabaseFolder(vault: string): string;
|
|
73
|
+
/**
|
|
74
|
+
* Where the store of a vault's database is, as the engine finds it: the store named by `--store`
|
|
75
|
+
* or DATANOTES_STORE; else the database this machine last opened on the vault (`vaults.json`);
|
|
76
|
+
* else the id in the vault's one `metastore.yaml`. Null when none of them says.
|
|
77
|
+
*/
|
|
78
|
+
export declare function storeLocation(vault: string, opts?: LocateOptions): StoreLocation | null;
|
|
16
79
|
export interface LocalEngine {
|
|
17
80
|
url: string;
|
|
18
81
|
token: string;
|
|
19
82
|
lock: EngineLock;
|
|
20
83
|
}
|
|
21
|
-
/** The settings file of a vault's engine. */
|
|
22
|
-
export declare function vaultSettingsPath(vault: string): string;
|
|
84
|
+
/** The settings file of a vault's engine, in its store; null while the vault has no store yet. */
|
|
85
|
+
export declare function vaultSettingsPath(vault: string, opts?: LocateOptions): string | null;
|
|
23
86
|
/**
|
|
24
87
|
* The engine holding `vault`, or null when none runs. Throws when the lock is fresh but held on
|
|
25
|
-
* another machine (a
|
|
88
|
+
* another machine (a store on a shared folder): that engine is not reachable from here.
|
|
26
89
|
*/
|
|
27
|
-
export declare function localEngine(vault: string, opts?: {
|
|
90
|
+
export declare function localEngine(vault: string, opts?: LocateOptions & {
|
|
28
91
|
settingsFile?: string;
|
|
29
92
|
host?: string;
|
|
30
93
|
now?: number;
|
|
31
94
|
}): LocalEngine | null;
|
|
95
|
+
export {};
|
package/dist/api/local.js
CHANGED
|
@@ -3,28 +3,126 @@ import fs from "node:fs";
|
|
|
3
3
|
import os from "node:os";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
var LOCK_STALE_MS = 3e4;
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
var SETTINGS_FILE = "settings.json";
|
|
7
|
+
var LOCK_FILE = "engine.lock";
|
|
8
|
+
var STORE_RECORD_FILE = "store.json";
|
|
9
|
+
var REGISTRY_FILE = "vaults.json";
|
|
10
|
+
var DEFAULT_DATABASE_FOLDER = "datanotes";
|
|
11
|
+
var METASTORE = "metastore.yaml";
|
|
12
|
+
function dataHome(env = process.env, platform = process.platform, home = os.homedir()) {
|
|
13
|
+
if (env.DATANOTES_DATA_HOME) return path.resolve(env.DATANOTES_DATA_HOME);
|
|
14
|
+
if (platform === "win32") return path.join(env.LOCALAPPDATA || path.join(home, "AppData", "Local"), "Datanotes");
|
|
15
|
+
if (platform === "darwin") return path.join(home, "Library", "Application Support", "Datanotes");
|
|
16
|
+
return path.join(env.XDG_DATA_HOME || path.join(home, ".local", "share"), "datanotes");
|
|
17
|
+
}
|
|
18
|
+
var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
19
|
+
function readDatabaseId(text) {
|
|
20
|
+
const m = /^id:[ \t]*(.*)$/m.exec(text);
|
|
21
|
+
const value = m ? m[1].replace(/#.*$/, "").trim().replace(/^["']|["']$/g, "") : "";
|
|
22
|
+
return UUID.test(value) ? value.toLowerCase() : null;
|
|
23
|
+
}
|
|
24
|
+
function databaseIdIn(vault, database) {
|
|
25
|
+
try {
|
|
26
|
+
return readDatabaseId(fs.readFileSync(path.join(vault, ...database.split("/"), METASTORE), "utf8"));
|
|
27
|
+
} catch {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function vaultMetastores(vault) {
|
|
32
|
+
const out = [];
|
|
33
|
+
const walk = (dir, rel) => {
|
|
34
|
+
let ents;
|
|
35
|
+
try {
|
|
36
|
+
ents = fs.readdirSync(dir, { withFileTypes: true });
|
|
37
|
+
} catch {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
for (const e of ents) {
|
|
41
|
+
if (e.name.startsWith(".") || e.name === "node_modules") continue;
|
|
42
|
+
const r = rel ? `${rel}/${e.name}` : e.name;
|
|
43
|
+
if (e.isDirectory()) walk(path.join(dir, e.name), r);
|
|
44
|
+
else if (e.name === METASTORE) out.push(rel);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
walk(vault, "");
|
|
48
|
+
return out.sort();
|
|
49
|
+
}
|
|
50
|
+
function vaultKey(vault, platform = process.platform) {
|
|
51
|
+
let real = path.resolve(vault);
|
|
52
|
+
try {
|
|
53
|
+
real = fs.realpathSync.native(real);
|
|
54
|
+
} catch {
|
|
55
|
+
}
|
|
56
|
+
return platform === "win32" ? real.toLowerCase() : real;
|
|
8
57
|
}
|
|
9
58
|
function readJson(file) {
|
|
10
59
|
try {
|
|
11
|
-
|
|
60
|
+
const v = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
61
|
+
return v && typeof v === "object" && !Array.isArray(v) ? v : null;
|
|
12
62
|
} catch {
|
|
13
63
|
return null;
|
|
14
64
|
}
|
|
15
65
|
}
|
|
66
|
+
function readRegistry(home) {
|
|
67
|
+
const raw = readJson(path.join(home, REGISTRY_FILE)) ?? {};
|
|
68
|
+
const out = {};
|
|
69
|
+
for (const [k, v] of Object.entries(raw)) {
|
|
70
|
+
const r = v;
|
|
71
|
+
if (r && typeof r.id === "string" && UUID.test(r.id) && typeof r.database === "string") out[k] = { id: r.id, database: r.database };
|
|
72
|
+
}
|
|
73
|
+
return out;
|
|
74
|
+
}
|
|
75
|
+
function vaultDatabaseFolder(vault) {
|
|
76
|
+
const found = vaultMetastores(vault);
|
|
77
|
+
return found.length === 1 ? found[0] : DEFAULT_DATABASE_FOLDER;
|
|
78
|
+
}
|
|
79
|
+
function storeLocation(vault, opts = {}) {
|
|
80
|
+
const env = opts.env ?? process.env;
|
|
81
|
+
const explicit = opts.store ?? env.DATANOTES_STORE;
|
|
82
|
+
if (explicit) {
|
|
83
|
+
const store = path.resolve(explicit);
|
|
84
|
+
const rec = readJson(path.join(store, STORE_RECORD_FILE));
|
|
85
|
+
const database2 = typeof rec?.database === "string" ? rec.database : vaultDatabaseFolder(vault);
|
|
86
|
+
return { store, id: typeof rec?.id === "string" ? rec.id : databaseIdIn(vault, database2), database: database2 };
|
|
87
|
+
}
|
|
88
|
+
const home = dataHome(env);
|
|
89
|
+
const known = readRegistry(home)[vaultKey(vault)];
|
|
90
|
+
if (known) return { store: path.join(home, known.id), id: known.id, database: known.database };
|
|
91
|
+
const database = vaultDatabaseFolder(vault);
|
|
92
|
+
const id = databaseIdIn(vault, database);
|
|
93
|
+
return id ? { store: path.join(home, id), id, database } : null;
|
|
94
|
+
}
|
|
95
|
+
function vaultSettingsPath(vault, opts = {}) {
|
|
96
|
+
const loc = storeLocation(vault, opts);
|
|
97
|
+
return loc ? path.join(loc.store, SETTINGS_FILE) : null;
|
|
98
|
+
}
|
|
16
99
|
function localEngine(vault, opts = {}) {
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
const lock = readJson(path.join(
|
|
100
|
+
const loc = storeLocation(vault, opts);
|
|
101
|
+
if (!loc) return null;
|
|
102
|
+
const lock = readJson(path.join(loc.store, LOCK_FILE));
|
|
20
103
|
if (!lock || (opts.now ?? Date.now()) - Date.parse(lock.heartbeat) >= LOCK_STALE_MS) return null;
|
|
21
104
|
if (lock.host !== os.hostname()) throw new Error(`the vault is held by an engine on ${lock.host}; connect to it by its address`);
|
|
105
|
+
const settings = readJson(opts.settingsFile ?? path.join(loc.store, SETTINGS_FILE)) ?? {};
|
|
22
106
|
const token = typeof settings.httpServerToken === "string" ? settings.httpServerToken : "";
|
|
23
107
|
if (!token) return null;
|
|
24
108
|
return { url: `http://${opts.host ?? "127.0.0.1"}:${lock.port}`, token, lock };
|
|
25
109
|
}
|
|
26
110
|
export {
|
|
111
|
+
DEFAULT_DATABASE_FOLDER,
|
|
112
|
+
LOCK_FILE,
|
|
27
113
|
LOCK_STALE_MS,
|
|
114
|
+
METASTORE,
|
|
115
|
+
REGISTRY_FILE,
|
|
116
|
+
SETTINGS_FILE,
|
|
117
|
+
STORE_RECORD_FILE,
|
|
118
|
+
dataHome,
|
|
119
|
+
databaseIdIn,
|
|
28
120
|
localEngine,
|
|
121
|
+
readDatabaseId,
|
|
122
|
+
readRegistry,
|
|
123
|
+
storeLocation,
|
|
124
|
+
vaultDatabaseFolder,
|
|
125
|
+
vaultKey,
|
|
126
|
+
vaultMetastores,
|
|
29
127
|
vaultSettingsPath
|
|
30
128
|
};
|