datanotes 2.0.0 → 2.0.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/SECURITY.md +2 -2
- package/dist/api/index.cjs +2 -2
- package/dist/api/index.js +2 -2
- package/dist/datanotes.cjs +98 -38
- package/package.json +3 -2
package/SECURITY.md
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
## Reporting a vulnerability
|
|
4
4
|
|
|
5
|
-
Please report vulnerabilities privately,
|
|
6
|
-
|
|
5
|
+
Please report vulnerabilities privately, by email to the maintainer listed on the npm package
|
|
6
|
+
page ([npmjs.com/package/datanotes](https://www.npmjs.com/package/datanotes)), not in public. You will get an answer within a week.
|
|
7
7
|
Include the version, how to reproduce it and what an attacker gains.
|
|
8
8
|
|
|
9
9
|
## Security model
|
package/dist/api/index.cjs
CHANGED
|
@@ -7649,7 +7649,7 @@ var OPERATIONS = [
|
|
|
7649
7649
|
},
|
|
7650
7650
|
{
|
|
7651
7651
|
"name": "infer_table",
|
|
7652
|
-
"description": "Read-only schema inference from notes' frontmatter.\n- `folder` (recursive; \"\" = whole vault) and/or `paths`: notes not yet in a table. Returns\n `columns` (type, enum `values`, link `ref` to the table most targets belong to, `required`,\n `unique`, `dense`, `from` when the property name is not a valid column name, `stats`),\n `proposal` ready for create_table (`name`, `options.filename` or `timeColumn`, `columns`) and\n `adopt` ready for adopt_rows (`paths`, `rename`, `relocate: false`), plus `notes` to read.\n Review the proposal (names, enums, required) before creating; ask the user when unsure.\n- `table`: keys the table's rows use but the table does not declare (candidates for add_column,\n or keys to drop from rows).\nA string column becomes an enum
|
|
7652
|
+
"description": "Read-only schema inference from notes' frontmatter.\n- `folder` (recursive; \"\" = whole vault) and/or `paths`: notes not yet in a table. Returns\n `columns` (type, enum `values`, link `ref` to the table most targets belong to, `required`,\n `unique`, `dense`, `from` when the property name is not a valid column name, `stats`),\n `proposal` ready for create_table (`name`, `options.filename` or `timeColumn`, `columns`) and\n `adopt` ready for adopt_rows (`paths`, `rename`, `relocate: false`), plus `notes` to read.\n Review the proposal (names, enums, required) before creating; ask the user when unsure.\n- `table`: keys the table's rows use but the table does not declare (candidates for add_column,\n or keys to drop from rows).\nA string column becomes an enum when its values repeat: at least 3 notes, at most `max_enum_values`\ndistinct values (default 12), each used twice on average, short texts.",
|
|
7653
7653
|
"inputSchema": {
|
|
7654
7654
|
"additionalProperties": false,
|
|
7655
7655
|
"properties": {
|
|
@@ -12331,7 +12331,7 @@ function describeRefusal(data) {
|
|
|
12331
12331
|
const op = data.op ?? "operation";
|
|
12332
12332
|
const failed = (data.failed ?? []).length > 0;
|
|
12333
12333
|
let head = failed ? `${op} failed` : `${op} refused \u2014 nothing was written`;
|
|
12334
|
-
if (data.migrationId) head += ` (journal ${String(data.migrationId)}; use undo_migration to revert what was applied)`;
|
|
12334
|
+
if (failed && data.migrationId) head += ` (journal ${String(data.migrationId)}; use undo_migration to revert what was applied)`;
|
|
12335
12335
|
return head + (shown.length ? `:
|
|
12336
12336
|
${shown.join("\n")}` : "");
|
|
12337
12337
|
}
|
package/dist/api/index.js
CHANGED
|
@@ -7607,7 +7607,7 @@ var OPERATIONS = [
|
|
|
7607
7607
|
},
|
|
7608
7608
|
{
|
|
7609
7609
|
"name": "infer_table",
|
|
7610
|
-
"description": "Read-only schema inference from notes' frontmatter.\n- `folder` (recursive; \"\" = whole vault) and/or `paths`: notes not yet in a table. Returns\n `columns` (type, enum `values`, link `ref` to the table most targets belong to, `required`,\n `unique`, `dense`, `from` when the property name is not a valid column name, `stats`),\n `proposal` ready for create_table (`name`, `options.filename` or `timeColumn`, `columns`) and\n `adopt` ready for adopt_rows (`paths`, `rename`, `relocate: false`), plus `notes` to read.\n Review the proposal (names, enums, required) before creating; ask the user when unsure.\n- `table`: keys the table's rows use but the table does not declare (candidates for add_column,\n or keys to drop from rows).\nA string column becomes an enum
|
|
7610
|
+
"description": "Read-only schema inference from notes' frontmatter.\n- `folder` (recursive; \"\" = whole vault) and/or `paths`: notes not yet in a table. Returns\n `columns` (type, enum `values`, link `ref` to the table most targets belong to, `required`,\n `unique`, `dense`, `from` when the property name is not a valid column name, `stats`),\n `proposal` ready for create_table (`name`, `options.filename` or `timeColumn`, `columns`) and\n `adopt` ready for adopt_rows (`paths`, `rename`, `relocate: false`), plus `notes` to read.\n Review the proposal (names, enums, required) before creating; ask the user when unsure.\n- `table`: keys the table's rows use but the table does not declare (candidates for add_column,\n or keys to drop from rows).\nA string column becomes an enum when its values repeat: at least 3 notes, at most `max_enum_values`\ndistinct values (default 12), each used twice on average, short texts.",
|
|
7611
7611
|
"inputSchema": {
|
|
7612
7612
|
"additionalProperties": false,
|
|
7613
7613
|
"properties": {
|
|
@@ -12289,7 +12289,7 @@ function describeRefusal(data) {
|
|
|
12289
12289
|
const op = data.op ?? "operation";
|
|
12290
12290
|
const failed = (data.failed ?? []).length > 0;
|
|
12291
12291
|
let head = failed ? `${op} failed` : `${op} refused \u2014 nothing was written`;
|
|
12292
|
-
if (data.migrationId) head += ` (journal ${String(data.migrationId)}; use undo_migration to revert what was applied)`;
|
|
12292
|
+
if (failed && data.migrationId) head += ` (journal ${String(data.migrationId)}; use undo_migration to revert what was applied)`;
|
|
12293
12293
|
return head + (shown.length ? `:
|
|
12294
12294
|
${shown.join("\n")}` : "");
|
|
12295
12295
|
}
|
package/dist/datanotes.cjs
CHANGED
|
@@ -10310,6 +10310,25 @@ function resolveDateTokens(where, tz, now) {
|
|
|
10310
10310
|
const walk = (v) => Array.isArray(v) ? v.map(walk) : v && typeof v === "object" ? Object.fromEntries(Object.entries(v).map(([k, x]) => [k, walk(x)])) : resolveDateToken(v, tz, now);
|
|
10311
10311
|
return walk(where);
|
|
10312
10312
|
}
|
|
10313
|
+
function wallClock(now, tz) {
|
|
10314
|
+
const parts = new Intl.DateTimeFormat("en-CA", {
|
|
10315
|
+
timeZone: tz || "UTC",
|
|
10316
|
+
year: "numeric",
|
|
10317
|
+
month: "2-digit",
|
|
10318
|
+
day: "2-digit",
|
|
10319
|
+
hour: "2-digit",
|
|
10320
|
+
minute: "2-digit",
|
|
10321
|
+
second: "2-digit",
|
|
10322
|
+
hourCycle: "h23"
|
|
10323
|
+
}).formatToParts(now);
|
|
10324
|
+
const g = (type) => parts.find((p) => p.type === type)?.value ?? "00";
|
|
10325
|
+
return `${g("year")}-${g("month")}-${g("day")}T${g("hour")}:${g("minute")}:${g("second")}`;
|
|
10326
|
+
}
|
|
10327
|
+
function resolveDefault(value, now, tz) {
|
|
10328
|
+
if (value === DEFAULT_NOW) return tz === "" ? `${now.toISOString().slice(0, 19)}Z` : normalizeDatetime(wallClock(now, tz), tz);
|
|
10329
|
+
if (value === DEFAULT_TODAY) return wallClock(now, tz).slice(0, 10);
|
|
10330
|
+
return value;
|
|
10331
|
+
}
|
|
10313
10332
|
|
|
10314
10333
|
// src/core/database.ts
|
|
10315
10334
|
var DatabaseConfigError = class extends Error {
|
|
@@ -10914,7 +10933,8 @@ async function createTableImpl(app, settings, input, opts = {}) {
|
|
|
10914
10933
|
throw new OperationInputError(`"time_column" must be a date or datetime column of the table`);
|
|
10915
10934
|
}
|
|
10916
10935
|
if (cfg.default_partition !== "") extra.partition = `{${tc.name}:${cfg.default_partition}}`;
|
|
10917
|
-
|
|
10936
|
+
const format = tc.type === "date" ? cfg.filename.replace(/\s*(\[[^\]]*\])?\s*[Hh]{1,2}.*$/, "") || "YYYY-MM-DD" : cfg.filename;
|
|
10937
|
+
extra.filename = `{${tc.name}:${format}}`;
|
|
10918
10938
|
}
|
|
10919
10939
|
const given = { ...input.options ?? {} };
|
|
10920
10940
|
if ("path" in given) throw new OperationInputError(`database tables take "partition", not "path"`);
|
|
@@ -10991,10 +11011,11 @@ async function addColumnImpl(app, settings, input, opts = {}) {
|
|
|
10991
11011
|
const cs = emptyChangeSet("add_column", `add column ${spec.name} to ${t.def.name}`, t.file.path);
|
|
10992
11012
|
addSchemaChange(cs, t, after, settings);
|
|
10993
11013
|
const violations = [];
|
|
11014
|
+
const fill = resolveDefault(spec.default, /* @__PURE__ */ new Date(), (await readDatabaseConfig(app, settings)).timezone);
|
|
10994
11015
|
for (const r of await loadRows(app, settings, t)) {
|
|
10995
11016
|
const cur = fmGet(r.ordered, spec.name);
|
|
10996
11017
|
const has = r.ordered.some(([k]) => k === spec.name);
|
|
10997
|
-
const value = has ? cur :
|
|
11018
|
+
const value = has ? cur : fill;
|
|
10998
11019
|
if (isEmptyValue(value)) {
|
|
10999
11020
|
if (spec.required) {
|
|
11000
11021
|
violations.push({
|
|
@@ -11009,7 +11030,7 @@ async function addColumnImpl(app, settings, input, opts = {}) {
|
|
|
11009
11030
|
cs.edits.push({
|
|
11010
11031
|
path: r.file.path,
|
|
11011
11032
|
before: r.ordered,
|
|
11012
|
-
after: fmAdd(r.ordered, spec.name,
|
|
11033
|
+
after: fmAdd(r.ordered, spec.name, fill ?? null)
|
|
11013
11034
|
});
|
|
11014
11035
|
}
|
|
11015
11036
|
}
|
|
@@ -11128,6 +11149,7 @@ async function alterColumnImpl(app, settings, input, opts = {}) {
|
|
|
11128
11149
|
const widen = old.type === "date" && spec.type === "datetime";
|
|
11129
11150
|
const tz = widen ? (await readDatabaseConfig(app, settings)).timezone : "";
|
|
11130
11151
|
const violations = spec.unique ? uniqueViolations([spec], existing.map((r) => ({ path: r.file.path, ordered: r.ordered }))) : [];
|
|
11152
|
+
const fill = makeDense ? resolveDefault(spec.default, /* @__PURE__ */ new Date(), (await readDatabaseConfig(app, settings)).timezone) : void 0;
|
|
11131
11153
|
for (const r of existing) {
|
|
11132
11154
|
const has = r.ordered.some(([k]) => k === spec.name);
|
|
11133
11155
|
let v = fmGet(r.ordered, spec.name);
|
|
@@ -11144,7 +11166,7 @@ async function alterColumnImpl(app, settings, input, opts = {}) {
|
|
|
11144
11166
|
violations.push({ path: r.file.path, violations: validateValue(spec, v, validationContextFor(app, settings, r.file.path)) });
|
|
11145
11167
|
}
|
|
11146
11168
|
if (makeDense && !has) {
|
|
11147
|
-
cs.edits.push({ path: r.file.path, before: r.ordered, after: fmAdd(r.ordered, spec.name,
|
|
11169
|
+
cs.edits.push({ path: r.file.path, before: r.ordered, after: fmAdd(r.ordered, spec.name, fill ?? null) });
|
|
11148
11170
|
}
|
|
11149
11171
|
}
|
|
11150
11172
|
return finish(app, settings, cs, [], violations, opts);
|
|
@@ -11896,9 +11918,11 @@ async function planInheritedRows(app, settings, model, text2, refs, rewrite, cs)
|
|
|
11896
11918
|
checkColumnRefs(model, ref2.full, effectiveColumns(model, ref2, t.catalog.entry).filter((c) => !own.has(String(c.name))), ref2.full);
|
|
11897
11919
|
const old = await loadTable(app, settings, ref2.full);
|
|
11898
11920
|
const dense = t.def.columns.filter((c) => c.dense);
|
|
11921
|
+
const now = /* @__PURE__ */ new Date();
|
|
11922
|
+
const tz = (await readDatabaseConfig(app, settings)).timezone;
|
|
11899
11923
|
for (const r of await loadRows(app, settings, old)) {
|
|
11900
11924
|
let after = rewrite(r.ordered);
|
|
11901
|
-
for (const c of dense) if (!after.some(([k]) => k === c.name)) after = fmSet(after, c.name, c.default ?? null);
|
|
11925
|
+
for (const c of dense) if (!after.some(([k]) => k === c.name)) after = fmSet(after, c.name, resolveDefault(c.default, now, tz) ?? null);
|
|
11902
11926
|
after = await fillIdentities(app, settings, t, after, r.file.path);
|
|
11903
11927
|
const v = validateRow(t.def, after, { tableProperty: settings.tableProperty }, validationContextFor(app, settings, r.file.path));
|
|
11904
11928
|
if (v.length > 0) violations.push({ path: r.file.path, violations: v });
|
|
@@ -12157,25 +12181,6 @@ function renderPathTemplate(template, values) {
|
|
|
12157
12181
|
return sanitizeFileName(scalarForPath(v));
|
|
12158
12182
|
});
|
|
12159
12183
|
}
|
|
12160
|
-
function wallClock(now, tz) {
|
|
12161
|
-
const parts = new Intl.DateTimeFormat("en-CA", {
|
|
12162
|
-
timeZone: tz || "UTC",
|
|
12163
|
-
year: "numeric",
|
|
12164
|
-
month: "2-digit",
|
|
12165
|
-
day: "2-digit",
|
|
12166
|
-
hour: "2-digit",
|
|
12167
|
-
minute: "2-digit",
|
|
12168
|
-
second: "2-digit",
|
|
12169
|
-
hourCycle: "h23"
|
|
12170
|
-
}).formatToParts(now);
|
|
12171
|
-
const g = (type) => parts.find((p) => p.type === type)?.value ?? "00";
|
|
12172
|
-
return `${g("year")}-${g("month")}-${g("day")}T${g("hour")}:${g("minute")}:${g("second")}`;
|
|
12173
|
-
}
|
|
12174
|
-
function resolveDefault(value, now, tz) {
|
|
12175
|
-
if (value === DEFAULT_NOW) return tz === "" ? `${now.toISOString().slice(0, 19)}Z` : normalizeDatetime(wallClock(now, tz), tz);
|
|
12176
|
-
if (value === DEFAULT_TODAY) return wallClock(now, tz).slice(0, 10);
|
|
12177
|
-
return value;
|
|
12178
|
-
}
|
|
12179
12184
|
function computedColumnNames(t) {
|
|
12180
12185
|
return new Set(t.def.columns.filter((c) => c.generated !== void 0 || c.identity !== void 0).map((c) => c.name));
|
|
12181
12186
|
}
|
|
@@ -12811,6 +12816,7 @@ function planInsert(app, settings, t, input, env) {
|
|
|
12811
12816
|
if (!known.has(k)) throw new OperationInputError(`unknown column "${k}" for table "${t.def.name}"`);
|
|
12812
12817
|
}
|
|
12813
12818
|
const computed = computedColumnNames(t);
|
|
12819
|
+
for (const c of t.def.columns) if (c.identity === "filename") computed.delete(c.name);
|
|
12814
12820
|
for (const k of Object.keys(input.values)) {
|
|
12815
12821
|
if (computed.has(k)) throw new OperationInputError(`"${k}" is a generated column: its value is computed`);
|
|
12816
12822
|
}
|
|
@@ -13223,12 +13229,23 @@ async function deleteRowsImpl(app, settings, input, opts = {}) {
|
|
|
13223
13229
|
sources.forEach((s) => referrers.add(s));
|
|
13224
13230
|
}
|
|
13225
13231
|
if (policy === "set_null") {
|
|
13232
|
+
const model = cachedMetastore(app, settings);
|
|
13226
13233
|
for (const src of [...referrers].sort()) {
|
|
13227
13234
|
const f = app.vault.getAbstractFileByPath(src);
|
|
13228
13235
|
if (!f || !("extension" in f) || f.extension !== "md") continue;
|
|
13229
13236
|
const text2 = await app.vault.read(f);
|
|
13230
13237
|
const before = readOrderedFromMarkdown(text2).ordered;
|
|
13231
|
-
|
|
13238
|
+
const after = clearLinksTo(app, src, before, doomed);
|
|
13239
|
+
const owner = model ? rowTableFull(model, fmGet(after, settings.tableProperty)) : null;
|
|
13240
|
+
const ot = owner ? await loadTable(app, settings, owner) : null;
|
|
13241
|
+
if (ot && !src.startsWith(`${archiveFolder(ot.def)}/`)) {
|
|
13242
|
+
for (const c of ot.def.columns) {
|
|
13243
|
+
if (c.required && !isEmptyValue(fmGet(before, c.name)) && isEmptyValue(fmGet(after, c.name))) {
|
|
13244
|
+
conflicts.push({ path: src, message: `"${c.name}" is required and would be left empty: change or delete this row first, or use on_referenced "ignore"` });
|
|
13245
|
+
}
|
|
13246
|
+
}
|
|
13247
|
+
}
|
|
13248
|
+
cs.edits.push({ path: src, before, after });
|
|
13232
13249
|
}
|
|
13233
13250
|
}
|
|
13234
13251
|
const folder = archiveFolder(t.def);
|
|
@@ -13304,6 +13321,9 @@ async function retargetRows(app, settings, dst, notes, r, cs, opts) {
|
|
|
13304
13321
|
}
|
|
13305
13322
|
try {
|
|
13306
13323
|
const values = new Map(after);
|
|
13324
|
+
for (const c of dst.def.columns) {
|
|
13325
|
+
if (c.identity === "filename" && isEmptyValue(values.get(c.name))) values.set(c.name, basenameOf(n.path));
|
|
13326
|
+
}
|
|
13307
13327
|
const base = `${renderPathTemplate(dst.def.layout.rowPath, values)}/${renderPathTemplate(dst.def.options.filename, values)}`;
|
|
13308
13328
|
to = `${base}.md`;
|
|
13309
13329
|
for (let k = 2; to !== n.path && (taken.has(to) || app.vault.getAbstractFileByPath(to) != null) && k <= MAX_NAME_SUFFIX; k++) {
|
|
@@ -13647,6 +13667,10 @@ async function adoptCreatedNote(app, settings, file) {
|
|
|
13647
13667
|
const present = new Set(ordered.map(([k]) => k));
|
|
13648
13668
|
const values = Object.fromEntries(Object.entries(target.values).filter(([k]) => !present.has(k)));
|
|
13649
13669
|
for (const c of (await loadTable(app, settings, target.table)).def.columns) {
|
|
13670
|
+
if (c.generated !== void 0 || c.identity !== void 0) {
|
|
13671
|
+
delete values[c.name];
|
|
13672
|
+
continue;
|
|
13673
|
+
}
|
|
13650
13674
|
if (c.external_default !== void 0 && !present.has(c.name) && values[c.name] === void 0) values[c.name] = c.external_default;
|
|
13651
13675
|
}
|
|
13652
13676
|
const r = await adoptRows(app, settings, { table: target.table, paths: [file.path], perRow: { [file.path]: values }, relocate: false });
|
|
@@ -23348,7 +23372,7 @@ var OPERATIONS = [
|
|
|
23348
23372
|
},
|
|
23349
23373
|
{
|
|
23350
23374
|
"name": "infer_table",
|
|
23351
|
-
"description": "Read-only schema inference from notes' frontmatter.\n- `folder` (recursive; \"\" = whole vault) and/or `paths`: notes not yet in a table. Returns\n `columns` (type, enum `values`, link `ref` to the table most targets belong to, `required`,\n `unique`, `dense`, `from` when the property name is not a valid column name, `stats`),\n `proposal` ready for create_table (`name`, `options.filename` or `timeColumn`, `columns`) and\n `adopt` ready for adopt_rows (`paths`, `rename`, `relocate: false`), plus `notes` to read.\n Review the proposal (names, enums, required) before creating; ask the user when unsure.\n- `table`: keys the table's rows use but the table does not declare (candidates for add_column,\n or keys to drop from rows).\nA string column becomes an enum
|
|
23375
|
+
"description": "Read-only schema inference from notes' frontmatter.\n- `folder` (recursive; \"\" = whole vault) and/or `paths`: notes not yet in a table. Returns\n `columns` (type, enum `values`, link `ref` to the table most targets belong to, `required`,\n `unique`, `dense`, `from` when the property name is not a valid column name, `stats`),\n `proposal` ready for create_table (`name`, `options.filename` or `timeColumn`, `columns`) and\n `adopt` ready for adopt_rows (`paths`, `rename`, `relocate: false`), plus `notes` to read.\n Review the proposal (names, enums, required) before creating; ask the user when unsure.\n- `table`: keys the table's rows use but the table does not declare (candidates for add_column,\n or keys to drop from rows).\nA string column becomes an enum when its values repeat: at least 3 notes, at most `max_enum_values`\ndistinct values (default 12), each used twice on average, short texts.",
|
|
23352
23376
|
"inputSchema": {
|
|
23353
23377
|
"additionalProperties": false,
|
|
23354
23378
|
"properties": {
|
|
@@ -30682,6 +30706,16 @@ var NodeVault = class {
|
|
|
30682
30706
|
this.files = /* @__PURE__ */ new Map();
|
|
30683
30707
|
this.folders = /* @__PURE__ */ new Map();
|
|
30684
30708
|
this.root = new NodeFolder("");
|
|
30709
|
+
/**
|
|
30710
|
+
* Bring one path of the index in line with the disk (for the watcher). Emits create / modify /
|
|
30711
|
+
* delete when something changed; returns what happened.
|
|
30712
|
+
*/
|
|
30713
|
+
/**
|
|
30714
|
+
* Paths the vault is writing right now (and the folders above them). Between the change on disk
|
|
30715
|
+
* and the index update there is an `await`: a rescan landing there must not take the vault's own
|
|
30716
|
+
* write for a change made outside (a moved file would be dropped as deleted and added again).
|
|
30717
|
+
*/
|
|
30718
|
+
this.busy = /* @__PURE__ */ new Map();
|
|
30685
30719
|
this.adapter = new NodeAdapter(basePath);
|
|
30686
30720
|
this.events.setMaxListeners(0);
|
|
30687
30721
|
this.folders.set("", this.root);
|
|
@@ -30767,12 +30801,25 @@ var NodeVault = class {
|
|
|
30767
30801
|
async refreshStat(f) {
|
|
30768
30802
|
f.stat = statsOf(await import_promises.default.stat(this.abs(f.path)));
|
|
30769
30803
|
}
|
|
30770
|
-
|
|
30771
|
-
|
|
30772
|
-
|
|
30773
|
-
|
|
30804
|
+
async guarded(paths, fn) {
|
|
30805
|
+
for (const p of paths) this.busy.set(p, (this.busy.get(p) ?? 0) + 1);
|
|
30806
|
+
try {
|
|
30807
|
+
return await fn();
|
|
30808
|
+
} finally {
|
|
30809
|
+
for (const p of paths) {
|
|
30810
|
+
const n = (this.busy.get(p) ?? 1) - 1;
|
|
30811
|
+
if (n <= 0) this.busy.delete(p);
|
|
30812
|
+
else this.busy.set(p, n);
|
|
30813
|
+
}
|
|
30814
|
+
}
|
|
30815
|
+
}
|
|
30816
|
+
isBusy(rel) {
|
|
30817
|
+
for (const p of this.busy.keys()) if (rel === p || rel.startsWith(`${p}/`) || p.startsWith(`${rel}/`)) return true;
|
|
30818
|
+
return false;
|
|
30819
|
+
}
|
|
30774
30820
|
syncPath(rel) {
|
|
30775
30821
|
if (rel === "" || hidden(rel)) return null;
|
|
30822
|
+
if (this.isBusy(rel)) return null;
|
|
30776
30823
|
let st = null;
|
|
30777
30824
|
try {
|
|
30778
30825
|
st = import_node_fs3.default.statSync(this.abs(rel));
|
|
@@ -30864,8 +30911,10 @@ var NodeVault = class {
|
|
|
30864
30911
|
}
|
|
30865
30912
|
/** Write a file's text (internal: also used by the file manager). */
|
|
30866
30913
|
async writeText(file, data) {
|
|
30867
|
-
await
|
|
30868
|
-
|
|
30914
|
+
await this.guarded([file.path], async () => {
|
|
30915
|
+
await atomicWrite(this.abs(file.path), data);
|
|
30916
|
+
await this.refreshStat(file);
|
|
30917
|
+
});
|
|
30869
30918
|
this.emit("modify", file);
|
|
30870
30919
|
}
|
|
30871
30920
|
async process(file, fn) {
|
|
@@ -30878,9 +30927,11 @@ var NodeVault = class {
|
|
|
30878
30927
|
async create(p, data) {
|
|
30879
30928
|
const rel = p.replace(/^\/+/, "");
|
|
30880
30929
|
if (import_node_fs3.default.existsSync(this.abs(rel))) throw new Error(`File already exists: ${rel}`);
|
|
30881
|
-
await this.
|
|
30882
|
-
|
|
30883
|
-
|
|
30930
|
+
const f = await this.guarded([rel], async () => {
|
|
30931
|
+
await this.ensureFolder(dirname(rel));
|
|
30932
|
+
await atomicWrite(this.abs(rel), data);
|
|
30933
|
+
return this.addFile(rel, statsOf(await import_promises.default.stat(this.abs(rel))));
|
|
30934
|
+
});
|
|
30884
30935
|
this.emit("create", f);
|
|
30885
30936
|
return f;
|
|
30886
30937
|
}
|
|
@@ -30897,6 +30948,9 @@ var NodeVault = class {
|
|
|
30897
30948
|
return this.folders.get(rel);
|
|
30898
30949
|
}
|
|
30899
30950
|
async delete(file, force) {
|
|
30951
|
+
return this.guarded([file.path], () => this.deleteNow(file, force));
|
|
30952
|
+
}
|
|
30953
|
+
async deleteNow(file, force) {
|
|
30900
30954
|
const rel = file.path;
|
|
30901
30955
|
if (this.folders.has(rel)) {
|
|
30902
30956
|
const gone = [...this.files.values()].filter((f2) => f2.path.startsWith(`${rel}/`));
|
|
@@ -30911,8 +30965,11 @@ var NodeVault = class {
|
|
|
30911
30965
|
if (f) this.emit("delete", f);
|
|
30912
30966
|
}
|
|
30913
30967
|
/** Move to `.trash/` (Obsidian's local trash; also for "system", which Node cannot reach) or delete. */
|
|
30914
|
-
async trash(file,
|
|
30968
|
+
async trash(file, system) {
|
|
30915
30969
|
if (this.config.trashOption === "none") return this.delete(file, true);
|
|
30970
|
+
return this.guarded([file.path], () => this.trashNow(file, system));
|
|
30971
|
+
}
|
|
30972
|
+
async trashNow(file, _system) {
|
|
30916
30973
|
const target = await this.freeName(`.trash/${basename2(file.path)}`);
|
|
30917
30974
|
const isFolder = this.folders.has(file.path);
|
|
30918
30975
|
const gone = isFolder ? [...this.files.values()].filter((f) => f.path.startsWith(`${file.path}/`)) : [];
|
|
@@ -30944,6 +31001,9 @@ var NodeVault = class {
|
|
|
30944
31001
|
const from = item.path;
|
|
30945
31002
|
const to = newPath.replace(/^\/+|\/+$/g, "");
|
|
30946
31003
|
if (from === to) return /* @__PURE__ */ new Map();
|
|
31004
|
+
return this.guarded([from, to], () => this.moveNow(item, from, to));
|
|
31005
|
+
}
|
|
31006
|
+
async moveNow(item, from, to) {
|
|
30947
31007
|
if (import_node_fs3.default.existsSync(this.abs(to)) && from.toLowerCase() !== to.toLowerCase()) throw new Error(`Destination exists: ${to}`);
|
|
30948
31008
|
await this.ensureFolder(dirname(to));
|
|
30949
31009
|
await import_promises.default.rename(this.abs(from), this.abs(to));
|
|
@@ -31264,7 +31324,7 @@ var NodeWatcher = class {
|
|
|
31264
31324
|
};
|
|
31265
31325
|
|
|
31266
31326
|
// src/node/service.ts
|
|
31267
|
-
var VERSION = true ? "2.0.
|
|
31327
|
+
var VERSION = true ? "2.0.1" : "dev";
|
|
31268
31328
|
function parseArgs(argv, env = process.env) {
|
|
31269
31329
|
const get = (name) => {
|
|
31270
31330
|
const i = argv.indexOf(`--${name}`);
|
|
@@ -31439,7 +31499,7 @@ Docs: https://datanotes.dev`;
|
|
|
31439
31499
|
}
|
|
31440
31500
|
|
|
31441
31501
|
// src/node/cli.ts
|
|
31442
|
-
var VERSION2 = true ? "2.0.
|
|
31502
|
+
var VERSION2 = true ? "2.0.1" : "dev";
|
|
31443
31503
|
async function main2(argv = process.argv.slice(2)) {
|
|
31444
31504
|
const [command, ...rest] = argv;
|
|
31445
31505
|
if (command === "--version" || command === "-v" || command === "version") return void process.stdout.write(`${VERSION2}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "datanotes",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"description": "The engine of your second brain: Markdown notes as database tables, for you and your agents.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"markdown",
|
|
@@ -65,7 +65,8 @@
|
|
|
65
65
|
"build:service": "node scripts/check-layers.mjs && tsc -noEmit -skipLibCheck && node esbuild.service.mjs && npm run build:api",
|
|
66
66
|
"prepack": "npm run build:service",
|
|
67
67
|
"build:api": "tsc -p tsconfig.api.json && node esbuild.api.mjs",
|
|
68
|
-
"build": "npm run build:service"
|
|
68
|
+
"build": "npm run build:service",
|
|
69
|
+
"prepublishOnly": "node scripts/release-window.mjs"
|
|
69
70
|
},
|
|
70
71
|
"devDependencies": {
|
|
71
72
|
"@types/node": "^22.10.0",
|