datanotes 1.5.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 +10 -7
- package/dist/api/index.js +10 -7
- package/dist/datanotes.cjs +254 -853
- package/package.json +3 -2
package/dist/datanotes.cjs
CHANGED
|
@@ -431,9 +431,9 @@ var require_anchors = __commonJS({
|
|
|
431
431
|
}
|
|
432
432
|
return true;
|
|
433
433
|
}
|
|
434
|
-
function anchorNames(
|
|
434
|
+
function anchorNames(root) {
|
|
435
435
|
const anchors = /* @__PURE__ */ new Set();
|
|
436
|
-
visit.visit(
|
|
436
|
+
visit.visit(root, {
|
|
437
437
|
Value(_key, node) {
|
|
438
438
|
if (node.anchor)
|
|
439
439
|
anchors.add(node.anchor);
|
|
@@ -7233,7 +7233,7 @@ var require_public_api = __commonJS({
|
|
|
7233
7233
|
}
|
|
7234
7234
|
return doc;
|
|
7235
7235
|
}
|
|
7236
|
-
function
|
|
7236
|
+
function parse2(src, reviver, options) {
|
|
7237
7237
|
let _reviver = void 0;
|
|
7238
7238
|
if (typeof reviver === "function") {
|
|
7239
7239
|
_reviver = reviver;
|
|
@@ -7274,7 +7274,7 @@ var require_public_api = __commonJS({
|
|
|
7274
7274
|
return value.toString(options);
|
|
7275
7275
|
return new Document.Document(value, _replacer, options).toString(options);
|
|
7276
7276
|
}
|
|
7277
|
-
exports2.parse =
|
|
7277
|
+
exports2.parse = parse2;
|
|
7278
7278
|
exports2.parseAllDocuments = parseAllDocuments;
|
|
7279
7279
|
exports2.parseDocument = parseDocument3;
|
|
7280
7280
|
exports2.stringify = stringify2;
|
|
@@ -7413,9 +7413,6 @@ function dot(a, b) {
|
|
|
7413
7413
|
|
|
7414
7414
|
// src/core/settings.ts
|
|
7415
7415
|
var DEFAULT_SETTINGS = {
|
|
7416
|
-
templateProperty: "template",
|
|
7417
|
-
migrationsProperty: "_migrations",
|
|
7418
|
-
journalFolder: "_meta/migrations",
|
|
7419
7416
|
storeFolder: ".datanotes",
|
|
7420
7417
|
journalRetentionDays: 90,
|
|
7421
7418
|
fileLogEnabled: true,
|
|
@@ -7428,7 +7425,6 @@ var DEFAULT_SETTINGS = {
|
|
|
7428
7425
|
telemetryHeaders: {},
|
|
7429
7426
|
telemetryProtocol: "http/protobuf",
|
|
7430
7427
|
autoRecompute: true,
|
|
7431
|
-
viewNotes: true,
|
|
7432
7428
|
semanticEnabled: false,
|
|
7433
7429
|
semanticProvider: "ollama",
|
|
7434
7430
|
semanticBaseUrl: "",
|
|
@@ -7440,9 +7436,6 @@ var DEFAULT_SETTINGS = {
|
|
|
7440
7436
|
httpServerToken: ""
|
|
7441
7437
|
};
|
|
7442
7438
|
var KEYS = [
|
|
7443
|
-
"templateProperty",
|
|
7444
|
-
"migrationsProperty",
|
|
7445
|
-
"journalFolder",
|
|
7446
7439
|
"storeFolder",
|
|
7447
7440
|
"journalRetentionDays",
|
|
7448
7441
|
"fileLogEnabled",
|
|
@@ -7455,7 +7448,6 @@ var KEYS = [
|
|
|
7455
7448
|
"telemetryHeaders",
|
|
7456
7449
|
"telemetryProtocol",
|
|
7457
7450
|
"autoRecompute",
|
|
7458
|
-
"viewNotes",
|
|
7459
7451
|
"semanticEnabled",
|
|
7460
7452
|
"semanticProvider",
|
|
7461
7453
|
"semanticBaseUrl",
|
|
@@ -7466,19 +7458,6 @@ var KEYS = [
|
|
|
7466
7458
|
"httpServerPort",
|
|
7467
7459
|
"httpServerToken"
|
|
7468
7460
|
];
|
|
7469
|
-
var RESERVED_MIGRATIONS_KEYS = /* @__PURE__ */ new Set([
|
|
7470
|
-
"_protected",
|
|
7471
|
-
"_extend",
|
|
7472
|
-
"_inherits",
|
|
7473
|
-
"_table",
|
|
7474
|
-
"_columns"
|
|
7475
|
-
]);
|
|
7476
|
-
function normalizeMigrationsProperty(out) {
|
|
7477
|
-
const m = out.migrationsProperty;
|
|
7478
|
-
if (m.length === 0 || m === out.templateProperty || RESERVED_MIGRATIONS_KEYS.has(m)) {
|
|
7479
|
-
out.migrationsProperty = DEFAULT_SETTINGS.migrationsProperty;
|
|
7480
|
-
}
|
|
7481
|
-
}
|
|
7482
7461
|
function mergeWithDefaults(loaded) {
|
|
7483
7462
|
const out = { ...DEFAULT_SETTINGS };
|
|
7484
7463
|
for (const key of KEYS) {
|
|
@@ -7486,18 +7465,6 @@ function mergeWithDefaults(loaded) {
|
|
|
7486
7465
|
const raw = loaded[key];
|
|
7487
7466
|
if (raw === void 0) continue;
|
|
7488
7467
|
switch (key) {
|
|
7489
|
-
case "templateProperty": {
|
|
7490
|
-
if (typeof raw === "string" && raw.length > 0) {
|
|
7491
|
-
out.templateProperty = raw;
|
|
7492
|
-
}
|
|
7493
|
-
break;
|
|
7494
|
-
}
|
|
7495
|
-
case "migrationsProperty": {
|
|
7496
|
-
if (typeof raw === "string" && raw.length > 0) {
|
|
7497
|
-
out.migrationsProperty = raw;
|
|
7498
|
-
}
|
|
7499
|
-
break;
|
|
7500
|
-
}
|
|
7501
7468
|
case "telemetryEndpoint": {
|
|
7502
7469
|
if (typeof raw === "string" && /^https?:\/\/\S+$/.test(raw.trim())) {
|
|
7503
7470
|
out.telemetryEndpoint = raw.trim().replace(/\/+$/, "");
|
|
@@ -7544,12 +7511,6 @@ function mergeWithDefaults(loaded) {
|
|
|
7544
7511
|
if (typeof raw === "number" && Number.isInteger(raw) && raw >= 1) out[key] = raw;
|
|
7545
7512
|
break;
|
|
7546
7513
|
}
|
|
7547
|
-
case "journalFolder": {
|
|
7548
|
-
if (typeof raw === "string" && raw.trim().replace(/^\/+|\/+$/g, "").length > 0) {
|
|
7549
|
-
out.journalFolder = raw.trim().replace(/^\/+|\/+$/g, "");
|
|
7550
|
-
}
|
|
7551
|
-
break;
|
|
7552
|
-
}
|
|
7553
7514
|
case "semanticProvider": {
|
|
7554
7515
|
if (typeof raw === "string" && raw in PROVIDER_PRESETS) out.semanticProvider = raw;
|
|
7555
7516
|
break;
|
|
@@ -7585,8 +7546,7 @@ function mergeWithDefaults(loaded) {
|
|
|
7585
7546
|
case "telemetryEnabled":
|
|
7586
7547
|
case "fileLogEnabled":
|
|
7587
7548
|
case "semanticEnabled":
|
|
7588
|
-
case "autoRecompute":
|
|
7589
|
-
case "viewNotes": {
|
|
7549
|
+
case "autoRecompute": {
|
|
7590
7550
|
if (typeof raw === "boolean") {
|
|
7591
7551
|
out[key] = raw;
|
|
7592
7552
|
}
|
|
@@ -7594,7 +7554,6 @@ function mergeWithDefaults(loaded) {
|
|
|
7594
7554
|
}
|
|
7595
7555
|
}
|
|
7596
7556
|
}
|
|
7597
|
-
normalizeMigrationsProperty(out);
|
|
7598
7557
|
return out;
|
|
7599
7558
|
}
|
|
7600
7559
|
|
|
@@ -7834,14 +7793,6 @@ function valuesFromFilename(pattern, basename3) {
|
|
|
7834
7793
|
|
|
7835
7794
|
// src/core/frontmatter.ts
|
|
7836
7795
|
var import_yaml = __toESM(require_dist(), 1);
|
|
7837
|
-
var DEFAULT_MIGRATIONS_KEY = "_migrations";
|
|
7838
|
-
var UnsupportedNestedMappingError = class extends Error {
|
|
7839
|
-
constructor(message, keyPath) {
|
|
7840
|
-
super(message);
|
|
7841
|
-
this.keyPath = keyPath;
|
|
7842
|
-
this.name = "UnsupportedNestedMappingError";
|
|
7843
|
-
}
|
|
7844
|
-
};
|
|
7845
7796
|
var FRONTMATTER_BLOCK = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n([\s\S]*))?$/;
|
|
7846
7797
|
function splitFrontmatter(markdown) {
|
|
7847
7798
|
const m = markdown.match(FRONTMATTER_BLOCK);
|
|
@@ -7850,131 +7801,6 @@ function splitFrontmatter(markdown) {
|
|
|
7850
7801
|
if (yamlText === void 0) return null;
|
|
7851
7802
|
return { yamlText, body: m[2] ?? "" };
|
|
7852
7803
|
}
|
|
7853
|
-
function normalizeFlowStyleOpKeys(entry) {
|
|
7854
|
-
const compound = /^(op|from|to|key):(.+)$/;
|
|
7855
|
-
const out = { ...entry };
|
|
7856
|
-
const toDelete = [];
|
|
7857
|
-
for (const k of Object.keys(out)) {
|
|
7858
|
-
const m = k.match(compound);
|
|
7859
|
-
if (m == null) continue;
|
|
7860
|
-
const fieldRaw = m[1];
|
|
7861
|
-
const suffixRaw = m[2];
|
|
7862
|
-
if (fieldRaw === void 0 || suffixRaw === void 0) continue;
|
|
7863
|
-
const field = fieldRaw;
|
|
7864
|
-
const suffix = suffixRaw.trim();
|
|
7865
|
-
if (suffix.length === 0) continue;
|
|
7866
|
-
const raw = out[k];
|
|
7867
|
-
const preferred = raw !== null && raw !== void 0 && raw !== "" ? raw : suffix;
|
|
7868
|
-
const cur = out[field];
|
|
7869
|
-
if (cur !== void 0 && cur !== null && String(cur) !== String(preferred)) {
|
|
7870
|
-
continue;
|
|
7871
|
-
}
|
|
7872
|
-
out[field] = preferred;
|
|
7873
|
-
toDelete.push(k);
|
|
7874
|
-
}
|
|
7875
|
-
for (const k of toDelete) {
|
|
7876
|
-
delete out[k];
|
|
7877
|
-
}
|
|
7878
|
-
return out;
|
|
7879
|
-
}
|
|
7880
|
-
function migrationEntryMapToObject(map, entryPath, listRoot) {
|
|
7881
|
-
const out = {};
|
|
7882
|
-
for (const pair of map.items) {
|
|
7883
|
-
const kn = pair.key;
|
|
7884
|
-
const k = (0, import_yaml.isScalar)(kn) ? String(kn.value) : String(kn);
|
|
7885
|
-
const v = pair.value;
|
|
7886
|
-
if (v == null) {
|
|
7887
|
-
out[k] = null;
|
|
7888
|
-
continue;
|
|
7889
|
-
}
|
|
7890
|
-
if ((0, import_yaml.isScalar)(v)) {
|
|
7891
|
-
out[k] = v.value;
|
|
7892
|
-
continue;
|
|
7893
|
-
}
|
|
7894
|
-
throw new UnsupportedNestedMappingError(
|
|
7895
|
-
`Invalid value in ${listRoot} entry (path: ${entryPath}.${k})`,
|
|
7896
|
-
`${entryPath}.${k}`
|
|
7897
|
-
);
|
|
7898
|
-
}
|
|
7899
|
-
return normalizeFlowStyleOpKeys(out);
|
|
7900
|
-
}
|
|
7901
|
-
function parseMigrationsSequence(node, listRoot) {
|
|
7902
|
-
if (node == null) return [];
|
|
7903
|
-
if (!(node instanceof import_yaml.YAMLSeq)) {
|
|
7904
|
-
throw new UnsupportedNestedMappingError(
|
|
7905
|
-
`${listRoot} must be a YAML sequence (path: ${listRoot})`,
|
|
7906
|
-
listRoot
|
|
7907
|
-
);
|
|
7908
|
-
}
|
|
7909
|
-
return node.items.map((item, i) => {
|
|
7910
|
-
const p = `${listRoot}[${i}]`;
|
|
7911
|
-
if (item == null || !(item instanceof import_yaml.YAMLMap)) {
|
|
7912
|
-
throw new Error(`${p}: expected a mapping`);
|
|
7913
|
-
}
|
|
7914
|
-
return migrationEntryMapToObject(item, p, listRoot);
|
|
7915
|
-
});
|
|
7916
|
-
}
|
|
7917
|
-
function tableEntryMapToObject(map, entryPath) {
|
|
7918
|
-
const out = {};
|
|
7919
|
-
for (const pair of map.items) {
|
|
7920
|
-
const kn = pair.key;
|
|
7921
|
-
const k = (0, import_yaml.isScalar)(kn) ? String(kn.value) : String(kn);
|
|
7922
|
-
const v = pair.value;
|
|
7923
|
-
if (v == null) {
|
|
7924
|
-
out[k] = null;
|
|
7925
|
-
} else if ((0, import_yaml.isScalar)(v)) {
|
|
7926
|
-
out[k] = v.value;
|
|
7927
|
-
} else if (v instanceof import_yaml.YAMLSeq) {
|
|
7928
|
-
out[k] = v.items.map((item, i) => {
|
|
7929
|
-
if (item == null) return null;
|
|
7930
|
-
if ((0, import_yaml.isScalar)(item)) return item.value;
|
|
7931
|
-
throw new UnsupportedNestedMappingError(
|
|
7932
|
-
`Invalid list item in ${entryPath}.${k}[${i}]`,
|
|
7933
|
-
`${entryPath}.${k}[${i}]`
|
|
7934
|
-
);
|
|
7935
|
-
});
|
|
7936
|
-
} else {
|
|
7937
|
-
throw new UnsupportedNestedMappingError(
|
|
7938
|
-
`Invalid value in ${entryPath}.${k}`,
|
|
7939
|
-
`${entryPath}.${k}`
|
|
7940
|
-
);
|
|
7941
|
-
}
|
|
7942
|
-
}
|
|
7943
|
-
return out;
|
|
7944
|
-
}
|
|
7945
|
-
function parseTableEntriesSequence(node, listRoot) {
|
|
7946
|
-
if (node == null || (0, import_yaml.isScalar)(node) && node.value == null) return [];
|
|
7947
|
-
if (!(node instanceof import_yaml.YAMLSeq)) {
|
|
7948
|
-
throw new UnsupportedNestedMappingError(
|
|
7949
|
-
`${listRoot} must be a YAML sequence (path: ${listRoot})`,
|
|
7950
|
-
listRoot
|
|
7951
|
-
);
|
|
7952
|
-
}
|
|
7953
|
-
return node.items.map((item, i) => {
|
|
7954
|
-
const p = `${listRoot}[${i}]`;
|
|
7955
|
-
if (item == null || !(item instanceof import_yaml.YAMLMap)) {
|
|
7956
|
-
throw new Error(`${p}: expected a mapping`);
|
|
7957
|
-
}
|
|
7958
|
-
return tableEntryMapToObject(item, p);
|
|
7959
|
-
});
|
|
7960
|
-
}
|
|
7961
|
-
var TABLE_SEQUENCE_KEYS = /* @__PURE__ */ new Set(["_table", "_columns"]);
|
|
7962
|
-
function assertPlainValueAtPath(node, keyPath) {
|
|
7963
|
-
if (node == null) return null;
|
|
7964
|
-
if ((0, import_yaml.isScalar)(node)) return node.value;
|
|
7965
|
-
if (node instanceof import_yaml.YAMLMap) {
|
|
7966
|
-
throw new UnsupportedNestedMappingError(
|
|
7967
|
-
`Nested YAML mapping is not supported in v1 (path: ${keyPath})`,
|
|
7968
|
-
keyPath
|
|
7969
|
-
);
|
|
7970
|
-
}
|
|
7971
|
-
if (node instanceof import_yaml.YAMLSeq) {
|
|
7972
|
-
return node.items.map(
|
|
7973
|
-
(item, i) => assertPlainValueAtPath(item, `${keyPath}[${i}]`)
|
|
7974
|
-
);
|
|
7975
|
-
}
|
|
7976
|
-
throw new Error(`Unsupported YAML node at ${keyPath}`);
|
|
7977
|
-
}
|
|
7978
7804
|
function coerceMisparsedYamlWikilinkToString(value) {
|
|
7979
7805
|
let cur = value;
|
|
7980
7806
|
const steps = [];
|
|
@@ -7990,7 +7816,7 @@ function coerceMisparsedYamlWikilinkToString(value) {
|
|
|
7990
7816
|
}
|
|
7991
7817
|
return `[[${cur}]]`;
|
|
7992
7818
|
}
|
|
7993
|
-
function
|
|
7819
|
+
function yamlNodeToPlain(node, keyPath) {
|
|
7994
7820
|
if (node == null) return null;
|
|
7995
7821
|
if ((0, import_yaml.isScalar)(node)) return node.value;
|
|
7996
7822
|
if (node instanceof import_yaml.YAMLMap) {
|
|
@@ -7998,55 +7824,34 @@ function yamlNodeToPlainForChildNote(node, keyPath) {
|
|
|
7998
7824
|
for (const pair of node.items) {
|
|
7999
7825
|
const kn = pair.key;
|
|
8000
7826
|
const k = (0, import_yaml.isScalar)(kn) ? String(kn.value) : String(kn);
|
|
8001
|
-
out[k] =
|
|
7827
|
+
out[k] = yamlNodeToPlain(pair.value, `${keyPath}.${k}`);
|
|
8002
7828
|
}
|
|
8003
7829
|
return out;
|
|
8004
7830
|
}
|
|
8005
7831
|
if (node instanceof import_yaml.YAMLSeq) {
|
|
8006
7832
|
return node.items.map(
|
|
8007
|
-
(item, i) =>
|
|
7833
|
+
(item, i) => yamlNodeToPlain(item, `${keyPath}[${i}]`)
|
|
8008
7834
|
);
|
|
8009
7835
|
}
|
|
8010
7836
|
throw new Error(`Unsupported YAML node at ${keyPath}`);
|
|
8011
7837
|
}
|
|
8012
|
-
function parseYamlBlockToOrdered(yamlText,
|
|
8013
|
-
if (yamlText.trim() === "") return [];
|
|
8014
|
-
const doc = (0, import_yaml.parseDocument)(yamlText);
|
|
8015
|
-
if (doc.errors.length > 0) {
|
|
8016
|
-
const first = doc.errors[0];
|
|
8017
|
-
throw new Error(first?.message ?? "YAML parse error");
|
|
8018
|
-
}
|
|
8019
|
-
const root2 = doc.contents;
|
|
8020
|
-
if (root2 == null) return [];
|
|
8021
|
-
if (!(root2 instanceof import_yaml.YAMLMap)) {
|
|
8022
|
-
throw new Error("Frontmatter root must be a YAML mapping (object)");
|
|
8023
|
-
}
|
|
8024
|
-
const entries = [];
|
|
8025
|
-
for (const pair of root2.items) {
|
|
8026
|
-
const kn = pair.key;
|
|
8027
|
-
const key = (0, import_yaml.isScalar)(kn) ? String(kn.value) : String(kn);
|
|
8028
|
-
const value = key === migrationsKey || key === "_inherits" ? parseMigrationsSequence(pair.value, key) : TABLE_SEQUENCE_KEYS.has(key) ? parseTableEntriesSequence(pair.value, key) : assertPlainValueAtPath(pair.value, key);
|
|
8029
|
-
entries.push([key, value]);
|
|
8030
|
-
}
|
|
8031
|
-
return entries;
|
|
8032
|
-
}
|
|
8033
|
-
function parseYamlBlockToOrderedForChildNote(yamlText, migrationsKey = DEFAULT_MIGRATIONS_KEY, wikilinkStringKeys = /* @__PURE__ */ new Set(["template"])) {
|
|
7838
|
+
function parseYamlBlockToOrdered(yamlText, wikilinkStringKeys = /* @__PURE__ */ new Set()) {
|
|
8034
7839
|
if (yamlText.trim() === "") return [];
|
|
8035
7840
|
const doc = (0, import_yaml.parseDocument)(yamlText);
|
|
8036
7841
|
if (doc.errors.length > 0) {
|
|
8037
7842
|
const first = doc.errors[0];
|
|
8038
7843
|
throw new Error(first?.message ?? "YAML parse error");
|
|
8039
7844
|
}
|
|
8040
|
-
const
|
|
8041
|
-
if (
|
|
8042
|
-
if (!(
|
|
7845
|
+
const root = doc.contents;
|
|
7846
|
+
if (root == null) return [];
|
|
7847
|
+
if (!(root instanceof import_yaml.YAMLMap)) {
|
|
8043
7848
|
throw new Error("Frontmatter root must be a YAML mapping (object)");
|
|
8044
7849
|
}
|
|
8045
7850
|
const entries = [];
|
|
8046
|
-
for (const pair of
|
|
7851
|
+
for (const pair of root.items) {
|
|
8047
7852
|
const kn = pair.key;
|
|
8048
7853
|
const key = (0, import_yaml.isScalar)(kn) ? String(kn.value) : String(kn);
|
|
8049
|
-
let value =
|
|
7854
|
+
let value = yamlNodeToPlain(pair.value, key);
|
|
8050
7855
|
if (wikilinkStringKeys.has(key)) {
|
|
8051
7856
|
value = coerceMisparsedYamlWikilinkToString(value);
|
|
8052
7857
|
}
|
|
@@ -8054,27 +7859,13 @@ function parseYamlBlockToOrderedForChildNote(yamlText, migrationsKey = DEFAULT_M
|
|
|
8054
7859
|
}
|
|
8055
7860
|
return entries;
|
|
8056
7861
|
}
|
|
8057
|
-
function
|
|
8058
|
-
const split2 = splitFrontmatter(markdown);
|
|
8059
|
-
if (!split2) {
|
|
8060
|
-
return { ordered: [], body: markdown };
|
|
8061
|
-
}
|
|
8062
|
-
return {
|
|
8063
|
-
ordered: parseYamlBlockToOrderedForChildNote(
|
|
8064
|
-
split2.yamlText,
|
|
8065
|
-
migrationsKey,
|
|
8066
|
-
wikilinkStringKeys
|
|
8067
|
-
),
|
|
8068
|
-
body: split2.body
|
|
8069
|
-
};
|
|
8070
|
-
}
|
|
8071
|
-
function readOrderedFromMarkdown(markdown, migrationsKey = DEFAULT_MIGRATIONS_KEY) {
|
|
7862
|
+
function readOrderedFromMarkdown(markdown, wikilinkStringKeys = /* @__PURE__ */ new Set()) {
|
|
8072
7863
|
const split2 = splitFrontmatter(markdown);
|
|
8073
7864
|
if (!split2) {
|
|
8074
7865
|
return { ordered: [], body: markdown };
|
|
8075
7866
|
}
|
|
8076
7867
|
return {
|
|
8077
|
-
ordered: parseYamlBlockToOrdered(split2.yamlText,
|
|
7868
|
+
ordered: parseYamlBlockToOrdered(split2.yamlText, wikilinkStringKeys),
|
|
8078
7869
|
body: split2.body
|
|
8079
7870
|
};
|
|
8080
7871
|
}
|
|
@@ -8149,21 +7940,9 @@ function fmReorder(ordered, keysInOrder) {
|
|
|
8149
7940
|
}
|
|
8150
7941
|
return out;
|
|
8151
7942
|
}
|
|
8152
|
-
async function writeFrontmatter(app, file, mutator,
|
|
7943
|
+
async function writeFrontmatter(app, file, mutator, wikilinkStringKeys = /* @__PURE__ */ new Set()) {
|
|
8153
7944
|
const text2 = await app.vault.read(file);
|
|
8154
|
-
const { ordered: before } = readOrderedFromMarkdown(text2,
|
|
8155
|
-
const after = mutator(before);
|
|
8156
|
-
await app.fileManager.processFrontMatter(file, (fm) => {
|
|
8157
|
-
applyOrderedFrontmatter(fm, after);
|
|
8158
|
-
});
|
|
8159
|
-
}
|
|
8160
|
-
async function writeChildFrontmatter(app, file, mutator, migrationsKey = DEFAULT_MIGRATIONS_KEY, wikilinkStringKeys = /* @__PURE__ */ new Set(["template"])) {
|
|
8161
|
-
const text2 = await app.vault.read(file);
|
|
8162
|
-
const { ordered: before } = readOrderedFromMarkdownForChildNote(
|
|
8163
|
-
text2,
|
|
8164
|
-
migrationsKey,
|
|
8165
|
-
wikilinkStringKeys
|
|
8166
|
-
);
|
|
7945
|
+
const { ordered: before } = readOrderedFromMarkdown(text2, wikilinkStringKeys);
|
|
8167
7946
|
const after = mutator(before);
|
|
8168
7947
|
await app.fileManager.processFrontMatter(file, (fm) => {
|
|
8169
7948
|
applyOrderedFrontmatter(fm, after);
|
|
@@ -8202,16 +7981,7 @@ var DEFAULT_TODAY = "{today}";
|
|
|
8202
7981
|
var DEFAULT_SCHEMA = "public";
|
|
8203
7982
|
|
|
8204
7983
|
// src/core/reserved-keys.ts
|
|
8205
|
-
|
|
8206
|
-
return [
|
|
8207
|
-
settings.migrationsProperty,
|
|
8208
|
-
"_protected",
|
|
8209
|
-
"_extend",
|
|
8210
|
-
"_inherits",
|
|
8211
|
-
TABLE_KEY,
|
|
8212
|
-
COLUMNS_KEY
|
|
8213
|
-
];
|
|
8214
|
-
}
|
|
7984
|
+
var TABLE_META_KEYS = [TABLE_KEY, COLUMNS_KEY];
|
|
8215
7985
|
|
|
8216
7986
|
// src/core/validate.ts
|
|
8217
7987
|
var DATE_RE = /^(\d{4})-(\d{2})-(\d{2})$/;
|
|
@@ -8344,7 +8114,7 @@ function validateRow(table, row, options, ctx = {}) {
|
|
|
8344
8114
|
const known = /* @__PURE__ */ new Set([
|
|
8345
8115
|
...table.columns.map((c) => c.name),
|
|
8346
8116
|
...table.untypedFields,
|
|
8347
|
-
options.
|
|
8117
|
+
options.tableProperty,
|
|
8348
8118
|
...options.extraAllowedKeys ?? []
|
|
8349
8119
|
]);
|
|
8350
8120
|
for (const [k, v] of row) {
|
|
@@ -8503,7 +8273,7 @@ var COLUMN_ATTRS = /* @__PURE__ */ new Set([
|
|
|
8503
8273
|
"vocabulary",
|
|
8504
8274
|
"inverse"
|
|
8505
8275
|
]);
|
|
8506
|
-
var TABLE_ATTRS = /* @__PURE__ */ new Set(["name", "
|
|
8276
|
+
var TABLE_ATTRS = /* @__PURE__ */ new Set(["name", "partition", "filename", "archive", "exclude", "description", "aliases"]);
|
|
8507
8277
|
var COLUMN_NAME_RE = /^[A-Za-z][A-Za-z0-9_-]*$/;
|
|
8508
8278
|
function isScalarType(t) {
|
|
8509
8279
|
return typeof t === "string" && SCALAR_COLUMN_TYPES.includes(t);
|
|
@@ -8747,43 +8517,17 @@ function parseTableOptions(raw) {
|
|
|
8747
8517
|
}
|
|
8748
8518
|
return out;
|
|
8749
8519
|
}
|
|
8750
|
-
function basenameWithoutMd(path6) {
|
|
8751
|
-
const base = path6.split("/").pop() ?? path6;
|
|
8752
|
-
return base.replace(/\.md$/i, "");
|
|
8753
|
-
}
|
|
8754
|
-
var SCHEMA_BASENAME = "_schema";
|
|
8755
8520
|
var DEFAULT_DATABASE_FOLDER = "Database";
|
|
8756
|
-
function schemaOfTable(schemaPath, databaseFolder = DEFAULT_DATABASE_FOLDER) {
|
|
8757
|
-
const root2 = databaseFolder.replace(/^\/+|\/+$/g, "");
|
|
8758
|
-
if (!schemaPath.startsWith(`${root2}/`)) return DEFAULT_SCHEMA;
|
|
8759
|
-
const rel = schemaPath.slice(root2.length + 1).split("/");
|
|
8760
|
-
return rel.length >= 3 && !rel[0].startsWith("_") ? rel[0] : DEFAULT_SCHEMA;
|
|
8761
|
-
}
|
|
8762
|
-
function isDatabaseTable(schemaPath, databaseFolder = DEFAULT_DATABASE_FOLDER) {
|
|
8763
|
-
const root2 = databaseFolder.replace(/^\/+|\/+$/g, "");
|
|
8764
|
-
if (!schemaPath.startsWith(`${root2}/`)) return false;
|
|
8765
|
-
const rel = schemaPath.slice(root2.length + 1).split("/");
|
|
8766
|
-
return rel[rel.length - 1] === `${SCHEMA_BASENAME}.md` && (rel.length === 2 || rel.length === 3) && !rel[0].startsWith("_");
|
|
8767
|
-
}
|
|
8768
|
-
function tableLayout(schemaPath, options, databaseFolder = DEFAULT_DATABASE_FOLDER) {
|
|
8769
|
-
const cut = schemaPath.lastIndexOf("/");
|
|
8770
|
-
const folder = cut < 0 ? "" : schemaPath.slice(0, cut);
|
|
8771
|
-
const inDatabase = isDatabaseTable(schemaPath, databaseFolder);
|
|
8772
|
-
const join = (a, b) => a === "" ? b : `${a}/${b}`;
|
|
8773
|
-
let rowPath;
|
|
8774
|
-
if (options.path) rowPath = options.path;
|
|
8775
|
-
else if (inDatabase) rowPath = options.partition ? join(folder, options.partition) : folder;
|
|
8776
|
-
let archive;
|
|
8777
|
-
if (options.archive) archive = inDatabase && !options.archive.includes("/") ? join(folder, options.archive) : options.archive;
|
|
8778
|
-
else archive = join(folder, inDatabase ? DEFAULT_ARCHIVE_FOLDER : "_archivio");
|
|
8779
|
-
return { folder, inDatabase, ...rowPath !== void 0 ? { rowPath } : {}, archive };
|
|
8780
|
-
}
|
|
8781
8521
|
var DEFAULT_ARCHIVE_FOLDER = "_archive";
|
|
8782
|
-
function
|
|
8783
|
-
const
|
|
8784
|
-
|
|
8785
|
-
|
|
8786
|
-
|
|
8522
|
+
function catalogLayout(root, ref2, options) {
|
|
8523
|
+
const folder = `${root}/${ref2.catalog}/${ref2.schema}/${ref2.table}`;
|
|
8524
|
+
const archiveName = options.archive && !options.archive.includes("/") ? options.archive : DEFAULT_ARCHIVE_FOLDER;
|
|
8525
|
+
return {
|
|
8526
|
+
folder,
|
|
8527
|
+
inDatabase: true,
|
|
8528
|
+
rowPath: options.partition ? `${folder}/${options.partition}` : folder,
|
|
8529
|
+
archive: options.archive && options.archive.includes("/") ? options.archive : `${folder}/${archiveName}`
|
|
8530
|
+
};
|
|
8787
8531
|
}
|
|
8788
8532
|
function checkGeneratedColumns(specs, known) {
|
|
8789
8533
|
specs.forEach((c, index) => {
|
|
@@ -8811,8 +8555,8 @@ function checkGeneratedColumns(specs, known) {
|
|
|
8811
8555
|
}
|
|
8812
8556
|
});
|
|
8813
8557
|
}
|
|
8814
|
-
function tableDefinitionFromOrdered(schemaPath, ordered, settings) {
|
|
8815
|
-
const reserved =
|
|
8558
|
+
function tableDefinitionFromOrdered(ref2, schemaPath, ordered, settings) {
|
|
8559
|
+
const reserved = new Set(TABLE_META_KEYS);
|
|
8816
8560
|
const options = parseTableOptions(fmGet(ordered, TABLE_KEY));
|
|
8817
8561
|
const specs = parseColumns(fmGet(ordered, COLUMNS_KEY), reserved);
|
|
8818
8562
|
const fieldKeys = ordered.map(([k]) => k).filter((k) => !reserved.has(k));
|
|
@@ -8822,9 +8566,10 @@ function tableDefinitionFromOrdered(schemaPath, ordered, settings) {
|
|
|
8822
8566
|
checkGeneratedColumns(specs, /* @__PURE__ */ new Set([...columnNames, ...fieldKeys]));
|
|
8823
8567
|
return {
|
|
8824
8568
|
schemaPath,
|
|
8825
|
-
name:
|
|
8826
|
-
schema:
|
|
8827
|
-
|
|
8569
|
+
name: ref2.table,
|
|
8570
|
+
schema: ref2.schema,
|
|
8571
|
+
catalog: ref2.catalog,
|
|
8572
|
+
layout: catalogLayout(settings.databaseFolder.replace(/^\/+|\/+$/g, ""), ref2, options),
|
|
8828
8573
|
options,
|
|
8829
8574
|
columns,
|
|
8830
8575
|
untypedFields: fieldKeys.filter((k) => !columnNames.has(k))
|
|
@@ -9052,16 +8797,6 @@ function rowTableFull(model, value) {
|
|
|
9052
8797
|
}
|
|
9053
8798
|
}
|
|
9054
8799
|
var TABLE_PROPERTY_KEYS = ["partition", "filename", "archive", "exclude"];
|
|
9055
|
-
function catalogLayout(root2, ref2, options) {
|
|
9056
|
-
const folder = `${root2}/${ref2.catalog}/${ref2.schema}/${ref2.table}`;
|
|
9057
|
-
const archiveName = options.archive && !options.archive.includes("/") ? options.archive : "_archive";
|
|
9058
|
-
return {
|
|
9059
|
-
folder,
|
|
9060
|
-
inDatabase: true,
|
|
9061
|
-
rowPath: options.partition ? `${folder}/${options.partition}` : folder,
|
|
9062
|
-
archive: options.archive && options.archive.includes("/") ? options.archive : `${folder}/${archiveName}`
|
|
9063
|
-
};
|
|
9064
|
-
}
|
|
9065
8800
|
function entryToOrdered(ref2, entry, settings, model) {
|
|
9066
8801
|
const columns = model ? effectiveColumns(model, ref2, entry) : entry.columns.map((c) => withSelf(c, ref2.full));
|
|
9067
8802
|
const ordered = [[settings.tableProperty, ref2.full]];
|
|
@@ -9103,7 +8838,7 @@ function orderedToEntry(ordered, previous, settings, ctx) {
|
|
|
9103
8838
|
const options = parseTableOptions(fmGet(ordered, TABLE_KEY));
|
|
9104
8839
|
const columnsRaw = fmGet(ordered, COLUMNS_KEY) ?? [];
|
|
9105
8840
|
const columnNames = new Set(columnsRaw.map((c) => String(c.name)));
|
|
9106
|
-
const reserved = /* @__PURE__ */ new Set([settings.tableProperty,
|
|
8841
|
+
const reserved = /* @__PURE__ */ new Set([settings.tableProperty, ...TABLE_META_KEYS]);
|
|
9107
8842
|
const dense = /* @__PURE__ */ new Set();
|
|
9108
8843
|
const defaults = {};
|
|
9109
8844
|
for (const [k, v] of ordered) {
|
|
@@ -9314,11 +9049,11 @@ async function readHashed(app, f) {
|
|
|
9314
9049
|
e.hash ??= contentHash(e.content);
|
|
9315
9050
|
return { content: e.content, hash: e.hash };
|
|
9316
9051
|
}
|
|
9317
|
-
async function readParsed(app, f, key,
|
|
9052
|
+
async function readParsed(app, f, key, parse2) {
|
|
9318
9053
|
const e = await entryFor(app, f);
|
|
9319
9054
|
e.parsed ??= /* @__PURE__ */ new Map();
|
|
9320
9055
|
let p = e.parsed.get(key);
|
|
9321
|
-
if (!p) e.parsed.set(key, p =
|
|
9056
|
+
if (!p) e.parsed.set(key, p = parse2(e.content));
|
|
9322
9057
|
return { ordered: p.ordered.map(([k, v]) => [k, v]), body: p.body };
|
|
9323
9058
|
}
|
|
9324
9059
|
function invalidateFiles(app, paths) {
|
|
@@ -9441,24 +9176,9 @@ async function findInStore(app, settings, id) {
|
|
|
9441
9176
|
}
|
|
9442
9177
|
return entry ? { entry: await unpack(app, settings, entry), undone, path: `${path6}#${id}` } : null;
|
|
9443
9178
|
}
|
|
9444
|
-
var JSON_BLOCK = /```json\n([\s\S]*?)\n```/;
|
|
9445
|
-
function parseLegacyNote(text2, path6) {
|
|
9446
|
-
const m = text2.match(JSON_BLOCK);
|
|
9447
|
-
if (!m) throw new Error(`Journal ${path6} has no JSON block`);
|
|
9448
|
-
const entry = JSON.parse(m[1]);
|
|
9449
|
-
const { ordered } = readOrderedFromMarkdownForChildNote(text2);
|
|
9450
|
-
const undone = ordered.some(([k, v]) => k === "undone" && v === true);
|
|
9451
|
-
const by = ordered.find(([k]) => k === "undone_by")?.[1];
|
|
9452
|
-
return { entry, undone, ...typeof by === "string" ? { undoneBy: by } : {} };
|
|
9453
|
-
}
|
|
9454
9179
|
async function readJournal(app, settings, migrationId) {
|
|
9455
9180
|
const hit = await findInStore(app, settings, migrationId);
|
|
9456
9181
|
if (hit) return hit;
|
|
9457
|
-
const f = app.vault.getAbstractFileByPath(`${settings.journalFolder}/${migrationId}.md`);
|
|
9458
|
-
if (f != null && "extension" in f) {
|
|
9459
|
-
const { entry, undone } = parseLegacyNote(await app.vault.read(f), f.path);
|
|
9460
|
-
return { entry, undone, path: f.path };
|
|
9461
|
-
}
|
|
9462
9182
|
throw new MigrationNotFoundError(migrationId);
|
|
9463
9183
|
}
|
|
9464
9184
|
async function markUndone(app, settings, migrationId, undoneBy, now) {
|
|
@@ -9531,41 +9251,6 @@ async function compactJournal(app, settings, now = /* @__PURE__ */ new Date()) {
|
|
|
9531
9251
|
}
|
|
9532
9252
|
return res;
|
|
9533
9253
|
}
|
|
9534
|
-
async function importLegacyJournal(app, settings, opts = {}) {
|
|
9535
|
-
const res = { imported: 0, skipped: 0, removed: 0, errors: [] };
|
|
9536
|
-
const folder = settings.journalFolder.replace(/^\/+|\/+$/g, "");
|
|
9537
|
-
if (!folder) return res;
|
|
9538
|
-
const notes = app.vault.getMarkdownFiles().filter((f) => f.path.startsWith(`${folder}/`) && !f.path.slice(folder.length + 1).includes("/")).sort((a, b) => a.path.localeCompare(b.path));
|
|
9539
|
-
const known = /* @__PURE__ */ new Map();
|
|
9540
|
-
const done = [];
|
|
9541
|
-
for (const f of notes) {
|
|
9542
|
-
try {
|
|
9543
|
-
const { entry, undone, undoneBy } = parseLegacyNote(await app.vault.read(f), f.path);
|
|
9544
|
-
const file = journalFileFor(settings, entry.migration_id);
|
|
9545
|
-
if (!known.has(file)) known.set(file, await idsIn(app, file));
|
|
9546
|
-
if (known.get(file).has(entry.migration_id)) {
|
|
9547
|
-
res.skipped++;
|
|
9548
|
-
} else {
|
|
9549
|
-
await appendEntry(app, settings, entry);
|
|
9550
|
-
if (undone) await markUndone(app, settings, entry.migration_id, undoneBy ?? "", new Date(entry.executed_at));
|
|
9551
|
-
known.get(file).add(entry.migration_id);
|
|
9552
|
-
res.imported++;
|
|
9553
|
-
}
|
|
9554
|
-
done.push(f);
|
|
9555
|
-
} catch (e) {
|
|
9556
|
-
res.errors.push({ path: f.path, error: e instanceof Error ? e.message : String(e) });
|
|
9557
|
-
}
|
|
9558
|
-
}
|
|
9559
|
-
if (opts.remove !== false) {
|
|
9560
|
-
for (const f of done) {
|
|
9561
|
-
await app.vault.delete(f);
|
|
9562
|
-
res.removed++;
|
|
9563
|
-
}
|
|
9564
|
-
const dir = app.vault.getAbstractFileByPath(folder);
|
|
9565
|
-
if (dir && Array.isArray(dir.children) && dir.children.length === 0) await app.vault.delete(dir, true);
|
|
9566
|
-
}
|
|
9567
|
-
return res;
|
|
9568
|
-
}
|
|
9569
9254
|
|
|
9570
9255
|
// src/core/changeset.ts
|
|
9571
9256
|
function bodyChanges(e) {
|
|
@@ -9657,22 +9342,8 @@ async function ensureFolder(app, folder) {
|
|
|
9657
9342
|
await ensureFolder(app, parentFolder(folder));
|
|
9658
9343
|
await app.vault.createFolder(folder);
|
|
9659
9344
|
}
|
|
9660
|
-
async function readOrderedFor(app, file
|
|
9661
|
-
|
|
9662
|
-
return strict ? readOrderedFromMarkdown(text2, settings.migrationsProperty).ordered : readOrderedFromMarkdownForChildNote(text2, settings.migrationsProperty, /* @__PURE__ */ new Set([settings.templateProperty])).ordered;
|
|
9663
|
-
}
|
|
9664
|
-
async function writeOrderedDiff(app, file, strict, settings, mutate) {
|
|
9665
|
-
if (strict) {
|
|
9666
|
-
await writeFrontmatter(app, file, mutate, settings.migrationsProperty);
|
|
9667
|
-
} else {
|
|
9668
|
-
await writeChildFrontmatter(
|
|
9669
|
-
app,
|
|
9670
|
-
file,
|
|
9671
|
-
mutate,
|
|
9672
|
-
settings.migrationsProperty,
|
|
9673
|
-
/* @__PURE__ */ new Set([settings.templateProperty])
|
|
9674
|
-
);
|
|
9675
|
-
}
|
|
9345
|
+
async function readOrderedFor(app, file) {
|
|
9346
|
+
return readOrderedFromMarkdown(await app.vault.read(file)).ordered;
|
|
9676
9347
|
}
|
|
9677
9348
|
async function moveFile(app, from, to) {
|
|
9678
9349
|
if (from.startsWith(".")) return storeRename(app, from, to);
|
|
@@ -9733,12 +9404,11 @@ async function applyChangeSet(app, settings, cs, now = /* @__PURE__ */ new Date(
|
|
|
9733
9404
|
const order = e.after.map(([k]) => k);
|
|
9734
9405
|
const fmChanged = d.set.length > 0 || d.remove.length > 0 || !same(e.before.map(([k]) => k), order);
|
|
9735
9406
|
if (fmChanged) {
|
|
9736
|
-
await
|
|
9407
|
+
await writeFrontmatter(app, file, (fm) => applyDiff(fm, d.set, d.remove, order));
|
|
9737
9408
|
}
|
|
9738
9409
|
if (bodyChanges(e)) await writeBody(app, file, e.bodyBefore, e.bodyAfter);
|
|
9739
9410
|
rows.push({
|
|
9740
9411
|
path: e.path,
|
|
9741
|
-
strict: e.strict,
|
|
9742
9412
|
before: d.beforeValues,
|
|
9743
9413
|
before_absent: d.beforeAbsent,
|
|
9744
9414
|
before_order: e.before.map(([k]) => k),
|
|
@@ -9830,7 +9500,7 @@ async function undoMigration(app, settings, migrationId, now = /* @__PURE__ */ n
|
|
|
9830
9500
|
conflicts.push({ path: r.path, message: "note no longer exists" });
|
|
9831
9501
|
continue;
|
|
9832
9502
|
}
|
|
9833
|
-
const cur = new Map(await readOrderedFor(app, f
|
|
9503
|
+
const cur = new Map(await readOrderedFor(app, f));
|
|
9834
9504
|
for (const [k, v] of Object.entries(r.after)) {
|
|
9835
9505
|
if (!cur.has(k) || !same(cur.get(k), v)) {
|
|
9836
9506
|
conflicts.push({ path: r.path, message: `"${k}" changed since the operation` });
|
|
@@ -9875,18 +9545,15 @@ async function undoMigration(app, settings, migrationId, now = /* @__PURE__ */ n
|
|
|
9875
9545
|
for (const r of entry.rows) {
|
|
9876
9546
|
const f = fileOrNull(app, currentPath(r.path));
|
|
9877
9547
|
if (Object.keys(r.after).length > 0 || r.after_absent.length > 0 || Object.keys(r.before).length > 0) {
|
|
9878
|
-
await
|
|
9548
|
+
await writeFrontmatter(
|
|
9879
9549
|
app,
|
|
9880
9550
|
f,
|
|
9881
|
-
r.strict,
|
|
9882
|
-
settings,
|
|
9883
9551
|
(fm) => applyDiff(fm, Object.entries(r.before), r.before_absent, r.before_order)
|
|
9884
9552
|
);
|
|
9885
9553
|
}
|
|
9886
9554
|
if (r.body_after !== void 0) await writeBody(app, f, r.body_after, r.body_before);
|
|
9887
9555
|
undoRows.push({
|
|
9888
9556
|
path: currentPath(r.path),
|
|
9889
|
-
strict: r.strict,
|
|
9890
9557
|
before: r.after,
|
|
9891
9558
|
before_absent: r.after_absent,
|
|
9892
9559
|
before_order: [],
|
|
@@ -10104,14 +9771,14 @@ async function readCurrentStates(app, settings, model, touched, only) {
|
|
|
10104
9771
|
const fm = app.metadataCache.getFileCache(f)?.frontmatter;
|
|
10105
9772
|
if (fm && fm[settings.tableProperty] != null) candidates.add(f.path);
|
|
10106
9773
|
}
|
|
10107
|
-
const key =
|
|
9774
|
+
const key = "member-tolerant";
|
|
10108
9775
|
const resolved = /* @__PURE__ */ new Map();
|
|
10109
9776
|
for (const path6 of [...candidates].sort()) {
|
|
10110
9777
|
const file = app.vault.getAbstractFileByPath(path6);
|
|
10111
9778
|
if (file == null || !("extension" in file)) continue;
|
|
10112
9779
|
let raw;
|
|
10113
9780
|
try {
|
|
10114
|
-
const { ordered } = await readParsed(app, file, key, (c) =>
|
|
9781
|
+
const { ordered } = await readParsed(app, file, key, (c) => readOrderedFromMarkdown(c));
|
|
10115
9782
|
raw = fmGet(ordered, settings.tableProperty);
|
|
10116
9783
|
} catch {
|
|
10117
9784
|
continue;
|
|
@@ -10307,13 +9974,13 @@ function linkTo(app, path6, source) {
|
|
|
10307
9974
|
return `[[${clash ? path6.replace(/\.md$/i, "") : base}]]`;
|
|
10308
9975
|
}
|
|
10309
9976
|
function rowKey(settings) {
|
|
10310
|
-
return `row|${settings.
|
|
9977
|
+
return `row|${settings.tableProperty}`;
|
|
10311
9978
|
}
|
|
10312
9979
|
async function readRowFrontmatter(app, settings, path6) {
|
|
10313
9980
|
const f = app.vault.getAbstractFileByPath(path6);
|
|
10314
9981
|
if (!f || !("extension" in f)) return null;
|
|
10315
|
-
const keep = /* @__PURE__ */ new Set([settings.
|
|
10316
|
-
const { ordered } = await readParsed(app, f, rowKey(settings), (t) =>
|
|
9982
|
+
const keep = /* @__PURE__ */ new Set([settings.tableProperty]);
|
|
9983
|
+
const { ordered } = await readParsed(app, f, rowKey(settings), (t) => readOrderedFromMarkdown(t, keep));
|
|
10317
9984
|
return ordered;
|
|
10318
9985
|
}
|
|
10319
9986
|
function inverseTargets(app, specs, ordered, source) {
|
|
@@ -10418,8 +10085,8 @@ async function inverseEditsFor(app, settings, rows, finalPath) {
|
|
|
10418
10085
|
return planInverse(app, settings, ms.model, changes, { known, finalPath });
|
|
10419
10086
|
}
|
|
10420
10087
|
function withFrontmatter(content, ordered, settings) {
|
|
10421
|
-
const keep = /* @__PURE__ */ new Set([settings.
|
|
10422
|
-
const { body } =
|
|
10088
|
+
const keep = /* @__PURE__ */ new Set([settings.tableProperty]);
|
|
10089
|
+
const { body } = readOrderedFromMarkdown(content, keep);
|
|
10423
10090
|
return buildMarkdownWithFrontmatter(ordered, body);
|
|
10424
10091
|
}
|
|
10425
10092
|
var InverseWatcher = class {
|
|
@@ -10521,14 +10188,6 @@ var TableNotFoundError = class extends Error {
|
|
|
10521
10188
|
this.name = "TableNotFoundError";
|
|
10522
10189
|
}
|
|
10523
10190
|
};
|
|
10524
|
-
var AmbiguousTableError = class extends Error {
|
|
10525
|
-
constructor(ref2, paths) {
|
|
10526
|
-
super(`Table name "${ref2}" matches several tables (${paths.join(", ")}); use schema.table`);
|
|
10527
|
-
this.ref = ref2;
|
|
10528
|
-
this.paths = paths;
|
|
10529
|
-
this.name = "AmbiguousTableError";
|
|
10530
|
-
}
|
|
10531
|
-
};
|
|
10532
10191
|
function frontmatterOf(app, file) {
|
|
10533
10192
|
const fm = app.metadataCache.getFileCache(file)?.frontmatter;
|
|
10534
10193
|
return fm && typeof fm === "object" ? fm : void 0;
|
|
@@ -10566,8 +10225,8 @@ function vocabularyOf(app, settings, table) {
|
|
|
10566
10225
|
}
|
|
10567
10226
|
const hit = entry.sets.get(table);
|
|
10568
10227
|
if (hit) return hit;
|
|
10569
|
-
const ms = cachedMetastore(app, { databaseFolder: settings.databaseFolder
|
|
10570
|
-
if (!ms
|
|
10228
|
+
const ms = cachedMetastore(app, { databaseFolder: settings.databaseFolder });
|
|
10229
|
+
if (!ms) return null;
|
|
10571
10230
|
const names = /* @__PURE__ */ new Set();
|
|
10572
10231
|
for (const f of app.vault.getMarkdownFiles()) {
|
|
10573
10232
|
const fm = frontmatterOf(app, f);
|
|
@@ -10586,8 +10245,8 @@ function validationContextFor(app, settings, sourcePath) {
|
|
|
10586
10245
|
tableOfPath: (path6) => {
|
|
10587
10246
|
const f = markdownFile(app, path6);
|
|
10588
10247
|
if (!f) return null;
|
|
10589
|
-
const ms = cachedMetastore(app, { databaseFolder: settings.databaseFolder
|
|
10590
|
-
const full = ms
|
|
10248
|
+
const ms = cachedMetastore(app, { databaseFolder: settings.databaseFolder });
|
|
10249
|
+
const full = ms ? rowTableFull(ms, frontmatterOf(app, f)?.[settings.tableProperty]) : null;
|
|
10591
10250
|
return full ?? null;
|
|
10592
10251
|
}
|
|
10593
10252
|
};
|
|
@@ -10651,6 +10310,25 @@ function resolveDateTokens(where, tz, now) {
|
|
|
10651
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);
|
|
10652
10311
|
return walk(where);
|
|
10653
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
|
+
}
|
|
10654
10332
|
|
|
10655
10333
|
// src/core/database.ts
|
|
10656
10334
|
var DatabaseConfigError = class extends Error {
|
|
@@ -10734,7 +10412,7 @@ async function writeDatabaseConfig(app, settings, cfg) {
|
|
|
10734
10412
|
primeMetastore(app, settings, after);
|
|
10735
10413
|
}
|
|
10736
10414
|
async function describeDatabase(app, settings) {
|
|
10737
|
-
const
|
|
10415
|
+
const root = databaseRoot(settings);
|
|
10738
10416
|
const config = await readDatabaseConfig(app, settings);
|
|
10739
10417
|
const ms = await loadMetastore(app, settings);
|
|
10740
10418
|
const catalogs = [];
|
|
@@ -10768,7 +10446,7 @@ async function describeDatabase(app, settings) {
|
|
|
10768
10446
|
}
|
|
10769
10447
|
}
|
|
10770
10448
|
return {
|
|
10771
|
-
root
|
|
10449
|
+
root,
|
|
10772
10450
|
metastore: metastorePath(settings),
|
|
10773
10451
|
journal: storePath(settings, "journal"),
|
|
10774
10452
|
config,
|
|
@@ -10831,9 +10509,6 @@ async function configureDatabase(app, settings, input, saveSettings) {
|
|
|
10831
10509
|
}
|
|
10832
10510
|
}
|
|
10833
10511
|
settings.databaseFolder = newRoot;
|
|
10834
|
-
if (settings.journalFolder === oldRoot || settings.journalFolder.startsWith(`${oldRoot}/`)) {
|
|
10835
|
-
settings.journalFolder = newRoot + settings.journalFolder.slice(oldRoot.length);
|
|
10836
|
-
}
|
|
10837
10512
|
await saveSettings();
|
|
10838
10513
|
}
|
|
10839
10514
|
}
|
|
@@ -10851,15 +10526,8 @@ var OperationInputError = class extends Error {
|
|
|
10851
10526
|
function catalogLoadedTable(settings, ms, ref2) {
|
|
10852
10527
|
const entry = ms.model.catalogs[ref2.catalog].schemas[ref2.schema].tables[ref2.table];
|
|
10853
10528
|
const ordered = entryToOrdered(ref2, entry, settings, ms.model);
|
|
10854
|
-
const
|
|
10855
|
-
|
|
10856
|
-
...base,
|
|
10857
|
-
name: ref2.table,
|
|
10858
|
-
schema: ref2.schema,
|
|
10859
|
-
catalog: ref2.catalog,
|
|
10860
|
-
layout: catalogLayout(settings.databaseFolder.replace(/^\/+|\/+$/g, ""), ref2, base.options)
|
|
10861
|
-
};
|
|
10862
|
-
return { file: ms.file, ordered, def, protectedKeys: /* @__PURE__ */ new Set(), catalog: { ref: ref2, entry, text: ms.text, model: ms.model } };
|
|
10529
|
+
const def = tableDefinitionFromOrdered(ref2, catalogTablePath(settings, ref2.full), ordered, settings);
|
|
10530
|
+
return { file: ms.file, ordered, def, catalog: { ref: ref2, entry, text: ms.text, model: ms.model } };
|
|
10863
10531
|
}
|
|
10864
10532
|
var INDEX_RETRY = { attempts: 8, delayMs: 150 };
|
|
10865
10533
|
async function loadTable(app, settings, ref2) {
|
|
@@ -10871,23 +10539,20 @@ async function loadTable(app, settings, ref2) {
|
|
|
10871
10539
|
await new Promise((r) => setTimeout(r, INDEX_RETRY.delayMs));
|
|
10872
10540
|
}
|
|
10873
10541
|
}
|
|
10874
|
-
function memberKey(settings, t) {
|
|
10875
|
-
return settings.tableProperty;
|
|
10876
|
-
}
|
|
10877
10542
|
function memberEntry(settings, t) {
|
|
10878
10543
|
return [settings.tableProperty, t.catalog.ref.full];
|
|
10879
10544
|
}
|
|
10880
10545
|
async function loadRows(app, settings, t) {
|
|
10881
10546
|
const out = [];
|
|
10882
10547
|
const files = catalogRowFiles(app, settings, t.catalog.ref.full, t.def);
|
|
10883
|
-
const key = `row|${settings.
|
|
10884
|
-
const keep = /* @__PURE__ */ new Set([settings.
|
|
10548
|
+
const key = `row|${settings.tableProperty}`;
|
|
10549
|
+
const keep = /* @__PURE__ */ new Set([settings.tableProperty]);
|
|
10885
10550
|
for (const f of files) {
|
|
10886
10551
|
const { ordered, body } = await readParsed(
|
|
10887
10552
|
app,
|
|
10888
10553
|
f,
|
|
10889
10554
|
key,
|
|
10890
|
-
(text2) =>
|
|
10555
|
+
(text2) => readOrderedFromMarkdown(text2, keep)
|
|
10891
10556
|
);
|
|
10892
10557
|
out.push({ file: f, ordered, body });
|
|
10893
10558
|
}
|
|
@@ -10916,12 +10581,7 @@ function uniqueViolations(columns, rows) {
|
|
|
10916
10581
|
}
|
|
10917
10582
|
return [...byPath.entries()].map(([path6, violations]) => ({ path: path6, violations }));
|
|
10918
10583
|
}
|
|
10919
|
-
|
|
10920
|
-
return new Set(rowReservedKeys(settings));
|
|
10921
|
-
}
|
|
10922
|
-
function reservedNames(settings) {
|
|
10923
|
-
return /* @__PURE__ */ new Set([...rowReservedKeys(settings), settings.templateProperty]);
|
|
10924
|
-
}
|
|
10584
|
+
var META_KEYS = new Set(TABLE_META_KEYS);
|
|
10925
10585
|
function serializeColumn(c) {
|
|
10926
10586
|
const out = { name: c.name, type: c.type };
|
|
10927
10587
|
if (c.items !== void 0) out.items = c.items;
|
|
@@ -10949,7 +10609,7 @@ function serializeColumn(c) {
|
|
|
10949
10609
|
}
|
|
10950
10610
|
function serializeOptions(o) {
|
|
10951
10611
|
const entry = {};
|
|
10952
|
-
for (const k of ["name", "description", "
|
|
10612
|
+
for (const k of ["name", "description", "partition", "filename", "archive"]) {
|
|
10953
10613
|
if (o[k] !== void 0) entry[k] = o[k];
|
|
10954
10614
|
}
|
|
10955
10615
|
if (o.aliases && o.aliases.length > 0) entry.aliases = [...o.aliases];
|
|
@@ -11047,11 +10707,11 @@ async function finish(app, settings, cs, conflicts, violations, opts) {
|
|
|
11047
10707
|
var INVERSE_OPS = /* @__PURE__ */ new Set(["insert_row", "update_rows", "merge_rows", "adopt_rows", "move_rows", "recompute"]);
|
|
11048
10708
|
async function addInverseEdits(app, settings, cs, conflicts) {
|
|
11049
10709
|
if (cs.skipInverse || !INVERSE_OPS.has(cs.op)) return;
|
|
11050
|
-
const keep = /* @__PURE__ */ new Set([settings.
|
|
10710
|
+
const keep = /* @__PURE__ */ new Set([settings.tableProperty]);
|
|
11051
10711
|
const created = new Map(cs.creates.map((c) => [c.path, c]));
|
|
11052
10712
|
const rows = [
|
|
11053
|
-
...cs.edits.
|
|
11054
|
-
...cs.creates.map((c) => ({ path: c.path, before: [], after:
|
|
10713
|
+
...cs.edits.map((e) => ({ path: e.path, before: e.before, after: e.after })),
|
|
10714
|
+
...cs.creates.map((c) => ({ path: c.path, before: [], after: readOrderedFromMarkdown(c.content, keep).ordered }))
|
|
11055
10715
|
];
|
|
11056
10716
|
const moved = new Map(cs.moves.map((m) => [m.from, m.to]));
|
|
11057
10717
|
const plan = await inverseEditsFor(app, settings, rows, (p) => moved.get(p) ?? p);
|
|
@@ -11062,7 +10722,7 @@ async function addInverseEdits(app, settings, cs, conflicts) {
|
|
|
11062
10722
|
const create = created.get(path6);
|
|
11063
10723
|
if (own) own.after = w.after;
|
|
11064
10724
|
else if (create) create.content = withFrontmatter(create.content, w.after, settings);
|
|
11065
|
-
else cs.edits.push({ path: path6,
|
|
10725
|
+
else cs.edits.push({ path: path6, before: w.before, after: w.after });
|
|
11066
10726
|
}
|
|
11067
10727
|
}
|
|
11068
10728
|
var DELTA_OPERATIONS = {
|
|
@@ -11151,7 +10811,7 @@ function addSchemaChange(cs, t, after, settings, target) {
|
|
|
11151
10811
|
return !prev || prev.ref !== c.ref || prev.hierarchy !== c.hierarchy || prev.type !== c.type || prev.items !== c.items || prev.vocabulary !== c.vocabulary;
|
|
11152
10812
|
});
|
|
11153
10813
|
checkColumnRefs(t.catalog.model, ref2.full, touched, ref2.full);
|
|
11154
|
-
def = tableDefinitionFromOrdered(catalogTablePath(settings, ref2.full), entryToOrdered(ref2, entry, settings, t.catalog.model), settings);
|
|
10814
|
+
def = tableDefinitionFromOrdered(ref2, catalogTablePath(settings, ref2.full), entryToOrdered(ref2, entry, settings, t.catalog.model), settings);
|
|
11155
10815
|
} catch (e) {
|
|
11156
10816
|
if (e instanceof MetastoreError) throw new OperationInputError(e.message);
|
|
11157
10817
|
throw e;
|
|
@@ -11247,7 +10907,7 @@ async function createTableImpl(app, settings, input, opts = {}) {
|
|
|
11247
10907
|
}
|
|
11248
10908
|
input = { ...input, columns: [{ ...SYSTEM_ID_SPEC, dense: true }, ...input.columns.filter((c) => c.name !== SYSTEM_ID_COLUMN)] };
|
|
11249
10909
|
}
|
|
11250
|
-
const reserved =
|
|
10910
|
+
const reserved = META_KEYS;
|
|
11251
10911
|
const conflicts = [];
|
|
11252
10912
|
const columns = input.columns.map((raw, i) => {
|
|
11253
10913
|
const { dense: _dense, ...spec } = raw;
|
|
@@ -11273,10 +10933,11 @@ async function createTableImpl(app, settings, input, opts = {}) {
|
|
|
11273
10933
|
throw new OperationInputError(`"time_column" must be a date or datetime column of the table`);
|
|
11274
10934
|
}
|
|
11275
10935
|
if (cfg.default_partition !== "") extra.partition = `{${tc.name}:${cfg.default_partition}}`;
|
|
11276
|
-
|
|
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}}`;
|
|
11277
10938
|
}
|
|
11278
10939
|
const given = { ...input.options ?? {} };
|
|
11279
|
-
if (
|
|
10940
|
+
if ("path" in given) throw new OperationInputError(`database tables take "partition", not "path"`);
|
|
11280
10941
|
const catOptions = parseTableOptions(serializeOptions({ ...extra, ...given, name: ref2.table }));
|
|
11281
10942
|
if (!catOptions.filename) {
|
|
11282
10943
|
throw new OperationInputError(`pass "time_column" or options.filename (e.g. "{name}") so rows get file names`);
|
|
@@ -11298,7 +10959,7 @@ async function createTableImpl(app, settings, input, opts = {}) {
|
|
|
11298
10959
|
});
|
|
11299
10960
|
cat = fmSet(cat, TABLE_KEY, serializeOptions(catOptions));
|
|
11300
10961
|
cat = withColumns(cat, columns);
|
|
11301
|
-
tableDefinitionFromOrdered(pseudo, cat, settings);
|
|
10962
|
+
tableDefinitionFromOrdered(ref2, pseudo, cat, settings);
|
|
11302
10963
|
const entry = orderedToEntry(cat, void 0, settings);
|
|
11303
10964
|
if (typeof input.body === "string" && input.body.trim() !== "") entry.row_template = input.body;
|
|
11304
10965
|
checkColumnRefs(model, ref2.full, entry.columns, ref2.full);
|
|
@@ -11339,21 +11000,22 @@ function columnExists(t, name) {
|
|
|
11339
11000
|
}
|
|
11340
11001
|
async function addColumnImpl(app, settings, input, opts = {}) {
|
|
11341
11002
|
const t = await loadTable(app, settings, input.table);
|
|
11342
|
-
const reserved =
|
|
11003
|
+
const reserved = META_KEYS;
|
|
11343
11004
|
const spec = parseColumn(input.column, t.def.columns.length, reserved);
|
|
11344
11005
|
if (columnExists(t, spec.name)) {
|
|
11345
11006
|
throw new OperationInputError(`column "${spec.name}" already exists in table "${t.def.name}"`);
|
|
11346
11007
|
}
|
|
11347
11008
|
const dense = input.dense === true;
|
|
11348
11009
|
let after = withColumns(t.ordered, [...t.def.columns, spec]);
|
|
11349
|
-
if (dense) after = addFieldBeforeMeta(after, spec.name, spec.default ?? null,
|
|
11010
|
+
if (dense) after = addFieldBeforeMeta(after, spec.name, spec.default ?? null, META_KEYS);
|
|
11350
11011
|
const cs = emptyChangeSet("add_column", `add column ${spec.name} to ${t.def.name}`, t.file.path);
|
|
11351
11012
|
addSchemaChange(cs, t, after, settings);
|
|
11352
11013
|
const violations = [];
|
|
11014
|
+
const fill = resolveDefault(spec.default, /* @__PURE__ */ new Date(), (await readDatabaseConfig(app, settings)).timezone);
|
|
11353
11015
|
for (const r of await loadRows(app, settings, t)) {
|
|
11354
11016
|
const cur = fmGet(r.ordered, spec.name);
|
|
11355
11017
|
const has = r.ordered.some(([k]) => k === spec.name);
|
|
11356
|
-
const value = has ? cur :
|
|
11018
|
+
const value = has ? cur : fill;
|
|
11357
11019
|
if (isEmptyValue(value)) {
|
|
11358
11020
|
if (spec.required) {
|
|
11359
11021
|
violations.push({
|
|
@@ -11367,9 +11029,8 @@ async function addColumnImpl(app, settings, input, opts = {}) {
|
|
|
11367
11029
|
if (dense && !has) {
|
|
11368
11030
|
cs.edits.push({
|
|
11369
11031
|
path: r.file.path,
|
|
11370
|
-
strict: false,
|
|
11371
11032
|
before: r.ordered,
|
|
11372
|
-
after: fmAdd(r.ordered, spec.name,
|
|
11033
|
+
after: fmAdd(r.ordered, spec.name, fill ?? null)
|
|
11373
11034
|
});
|
|
11374
11035
|
}
|
|
11375
11036
|
}
|
|
@@ -11382,12 +11043,11 @@ async function renameColumnImpl(app, settings, input, opts = {}) {
|
|
|
11382
11043
|
const t = await loadTable(app, settings, input.table);
|
|
11383
11044
|
const from = input.from;
|
|
11384
11045
|
const to = assertColumnName(input.to, `"to"`);
|
|
11385
|
-
const reserved =
|
|
11046
|
+
const reserved = META_KEYS;
|
|
11386
11047
|
if (!columnExists(t, from)) throw new OperationInputError(`column "${from}" not found in table "${t.def.name}"`);
|
|
11387
11048
|
if (reserved.has(to)) throw new OperationInputError(`"${to}" is a reserved key`);
|
|
11388
11049
|
if (columnExists(t, to)) throw new OperationInputError(`column "${to}" already exists in table "${t.def.name}"`);
|
|
11389
11050
|
const conflicts = [];
|
|
11390
|
-
if (t.protectedKeys.has(from)) conflicts.push({ path: t.file.path, message: `"${from}" is protected` });
|
|
11391
11051
|
const columns = t.def.columns.map((c) => c.name === from ? { ...c, name: to } : c);
|
|
11392
11052
|
let after = t.def.columns.some((c) => c.name === from) ? withColumns(t.ordered, columns) : t.ordered;
|
|
11393
11053
|
after = fmRename(after, from, to);
|
|
@@ -11406,7 +11066,7 @@ async function renameColumnImpl(app, settings, input, opts = {}) {
|
|
|
11406
11066
|
}
|
|
11407
11067
|
next = fmDelete(next, to);
|
|
11408
11068
|
}
|
|
11409
|
-
cs.edits.push({ path: r.file.path,
|
|
11069
|
+
cs.edits.push({ path: r.file.path, before: r.ordered, after: fmRename(next, from, to) });
|
|
11410
11070
|
}
|
|
11411
11071
|
return finish(app, settings, cs, conflicts, [], opts);
|
|
11412
11072
|
}
|
|
@@ -11427,7 +11087,6 @@ async function renameValueImpl(app, settings, input, opts = {}) {
|
|
|
11427
11087
|
}
|
|
11428
11088
|
const conflicts = [];
|
|
11429
11089
|
if (values.includes(to)) conflicts.push({ path: t.file.path, message: `"${to}" is already a value of ${col.name}` });
|
|
11430
|
-
if (t.protectedKeys.has(col.name)) conflicts.push({ path: t.file.path, message: `"${col.name}" is protected` });
|
|
11431
11090
|
const spec = {
|
|
11432
11091
|
...col,
|
|
11433
11092
|
values: values.map((v) => v === from ? to : v),
|
|
@@ -11444,7 +11103,7 @@ async function renameValueImpl(app, settings, input, opts = {}) {
|
|
|
11444
11103
|
if (isEmptyValue(v)) continue;
|
|
11445
11104
|
const next = swap(v);
|
|
11446
11105
|
if (JSON.stringify(next) === JSON.stringify(v)) continue;
|
|
11447
|
-
cs.edits.push({ path: r.file.path,
|
|
11106
|
+
cs.edits.push({ path: r.file.path, before: r.ordered, after: fmSet(r.ordered, col.name, next) });
|
|
11448
11107
|
}
|
|
11449
11108
|
return finish(app, settings, cs, conflicts, [], opts);
|
|
11450
11109
|
}
|
|
@@ -11453,14 +11112,13 @@ async function dropColumnImpl(app, settings, input, opts = {}) {
|
|
|
11453
11112
|
const name = input.name;
|
|
11454
11113
|
if (!columnExists(t, name)) throw new OperationInputError(`column "${name}" not found in table "${t.def.name}"`);
|
|
11455
11114
|
const conflicts = [];
|
|
11456
|
-
if (t.protectedKeys.has(name)) conflicts.push({ path: t.file.path, message: `"${name}" is protected` });
|
|
11457
11115
|
let after = t.def.columns.some((c) => c.name === name) ? withColumns(t.ordered, t.def.columns.filter((c) => c.name !== name)) : t.ordered;
|
|
11458
11116
|
after = fmDelete(after, name);
|
|
11459
11117
|
const cs = emptyChangeSet("drop_column", `drop column ${name} from ${t.def.name}`, t.file.path);
|
|
11460
11118
|
addSchemaChange(cs, t, after, settings);
|
|
11461
11119
|
for (const r of await loadRows(app, settings, t)) {
|
|
11462
11120
|
if (!r.ordered.some(([k]) => k === name)) continue;
|
|
11463
|
-
cs.edits.push({ path: r.file.path,
|
|
11121
|
+
cs.edits.push({ path: r.file.path, before: r.ordered, after: fmDelete(r.ordered, name) });
|
|
11464
11122
|
}
|
|
11465
11123
|
return finish(app, settings, cs, conflicts, [], opts);
|
|
11466
11124
|
}
|
|
@@ -11478,12 +11136,12 @@ async function alterColumnImpl(app, settings, input, opts = {}) {
|
|
|
11478
11136
|
if (v === null) delete merged[k];
|
|
11479
11137
|
else merged[k] = v;
|
|
11480
11138
|
}
|
|
11481
|
-
const spec = parseColumn(merged, idx,
|
|
11139
|
+
const spec = parseColumn(merged, idx, META_KEYS);
|
|
11482
11140
|
const columns = t.def.columns.map((c, i) => i === idx ? spec : c);
|
|
11483
|
-
const reserved =
|
|
11141
|
+
const reserved = META_KEYS;
|
|
11484
11142
|
let after = withColumns(t.ordered, columns);
|
|
11485
11143
|
const makeDense = dense === true && !old.dense;
|
|
11486
|
-
if (makeDense) after = addFieldBeforeMeta(after, spec.name, spec.default ?? null,
|
|
11144
|
+
if (makeDense) after = addFieldBeforeMeta(after, spec.name, spec.default ?? null, META_KEYS);
|
|
11487
11145
|
if (dense === false && old.dense) after = fmDelete(after, spec.name);
|
|
11488
11146
|
const cs = emptyChangeSet("alter_column", `alter column ${spec.name} in ${t.def.name}`, t.file.path);
|
|
11489
11147
|
addSchemaChange(cs, t, after, settings);
|
|
@@ -11491,12 +11149,13 @@ async function alterColumnImpl(app, settings, input, opts = {}) {
|
|
|
11491
11149
|
const widen = old.type === "date" && spec.type === "datetime";
|
|
11492
11150
|
const tz = widen ? (await readDatabaseConfig(app, settings)).timezone : "";
|
|
11493
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;
|
|
11494
11153
|
for (const r of existing) {
|
|
11495
11154
|
const has = r.ordered.some(([k]) => k === spec.name);
|
|
11496
11155
|
let v = fmGet(r.ordered, spec.name);
|
|
11497
11156
|
if (widen && typeof v === "string" && /^\d{4}-\d{2}-\d{2}$/.test(v)) {
|
|
11498
11157
|
const cast = normalizeDatetime(`${v}T00:00`, tz);
|
|
11499
|
-
cs.edits.push({ path: r.file.path,
|
|
11158
|
+
cs.edits.push({ path: r.file.path, before: r.ordered, after: fmSet(r.ordered, spec.name, cast) });
|
|
11500
11159
|
v = cast;
|
|
11501
11160
|
}
|
|
11502
11161
|
if (isEmptyValue(v)) {
|
|
@@ -11507,7 +11166,7 @@ async function alterColumnImpl(app, settings, input, opts = {}) {
|
|
|
11507
11166
|
violations.push({ path: r.file.path, violations: validateValue(spec, v, validationContextFor(app, settings, r.file.path)) });
|
|
11508
11167
|
}
|
|
11509
11168
|
if (makeDense && !has) {
|
|
11510
|
-
cs.edits.push({ path: r.file.path,
|
|
11169
|
+
cs.edits.push({ path: r.file.path, before: r.ordered, after: fmAdd(r.ordered, spec.name, fill ?? null) });
|
|
11511
11170
|
}
|
|
11512
11171
|
}
|
|
11513
11172
|
return finish(app, settings, cs, [], violations, opts);
|
|
@@ -11669,10 +11328,9 @@ async function loadTableAsOf(app, settings, table, asOf2) {
|
|
|
11669
11328
|
const file = ms?.file ?? pseudoFile(metastorePath(settings));
|
|
11670
11329
|
const t = catalogLoadedTable(settings, { file, text: "", model: modelWith(ref2, state, ms?.model.bases ?? {}) }, ref2);
|
|
11671
11330
|
const rows = [...state.files.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([path6, f]) => {
|
|
11672
|
-
const { ordered, body } =
|
|
11331
|
+
const { ordered, body } = readOrderedFromMarkdown(
|
|
11673
11332
|
f.content,
|
|
11674
|
-
settings.
|
|
11675
|
-
/* @__PURE__ */ new Set([settings.templateProperty, settings.tableProperty])
|
|
11333
|
+
/* @__PURE__ */ new Set([settings.tableProperty])
|
|
11676
11334
|
);
|
|
11677
11335
|
return { file: pseudoFile(path6), ordered, body };
|
|
11678
11336
|
});
|
|
@@ -11714,12 +11372,11 @@ var restoreTable = instrumented("restore_table", restoreTableImpl, (i) => ({
|
|
|
11714
11372
|
timestamp: i.timestamp
|
|
11715
11373
|
}));
|
|
11716
11374
|
function rowValues(content, settings) {
|
|
11717
|
-
const { ordered } =
|
|
11375
|
+
const { ordered } = readOrderedFromMarkdown(
|
|
11718
11376
|
content,
|
|
11719
|
-
settings.
|
|
11720
|
-
/* @__PURE__ */ new Set([settings.templateProperty, settings.tableProperty])
|
|
11377
|
+
/* @__PURE__ */ new Set([settings.tableProperty])
|
|
11721
11378
|
);
|
|
11722
|
-
return Object.fromEntries(ordered.filter(([k]) => k !== settings.tableProperty
|
|
11379
|
+
return Object.fromEntries(ordered.filter(([k]) => k !== settings.tableProperty));
|
|
11723
11380
|
}
|
|
11724
11381
|
async function tableChanges(app, settings, input) {
|
|
11725
11382
|
const ref2 = await historyTableName(app, settings, input.table);
|
|
@@ -11886,82 +11543,6 @@ function resolveThisTokens(query, host) {
|
|
|
11886
11543
|
return walk(query);
|
|
11887
11544
|
}
|
|
11888
11545
|
|
|
11889
|
-
// src/core/view-notes.ts
|
|
11890
|
-
var VIEW_NOTE_KEY = "view";
|
|
11891
|
-
var GENERATED_MARK = "Generated by Datanotes";
|
|
11892
|
-
function root(settings) {
|
|
11893
|
-
return settings.databaseFolder.replace(/^\/+|\/+$/g, "");
|
|
11894
|
-
}
|
|
11895
|
-
function viewNotePath(settings, ref2) {
|
|
11896
|
-
return `${root(settings)}/${ref2.catalog}/${ref2.schema}/${ref2.table}.md`;
|
|
11897
|
-
}
|
|
11898
|
-
function viewNoteText(ref2, view) {
|
|
11899
|
-
const lines = ["---", `${VIEW_NOTE_KEY}: ${ref2.full}`];
|
|
11900
|
-
if (view.aliases && view.aliases.length > 0) {
|
|
11901
|
-
lines.push("aliases:", ...view.aliases.map((a) => ` - ${JSON.stringify(a)}`));
|
|
11902
|
-
}
|
|
11903
|
-
lines.push("---", "");
|
|
11904
|
-
if (view.comment) lines.push(...view.comment.split("\n").map((l) => `> ${l}`), "");
|
|
11905
|
-
lines.push("```datanotes", `view: ${ref2.full}`, "```", "");
|
|
11906
|
-
lines.push(`%% ${GENERATED_MARK} from the view in database.yaml: change the view (create_view with replace, drop_view), not this note. %%`, "");
|
|
11907
|
-
return lines.join("\n");
|
|
11908
|
-
}
|
|
11909
|
-
function viewOfNote(text2) {
|
|
11910
|
-
const m = /^---\r?\n([\s\S]*?)\r?\n---/.exec(text2);
|
|
11911
|
-
if (!m) return null;
|
|
11912
|
-
const line = m[1].split(/\r?\n/).find((l) => l.startsWith(`${VIEW_NOTE_KEY}:`));
|
|
11913
|
-
return line ? line.slice(VIEW_NOTE_KEY.length + 1).trim().replace(/^["']|["']$/g, "") || null : null;
|
|
11914
|
-
}
|
|
11915
|
-
async function viewNoteBlocked(app, settings, ref2) {
|
|
11916
|
-
if (settings.viewNotes === false) return null;
|
|
11917
|
-
const path6 = viewNotePath(settings, ref2);
|
|
11918
|
-
const f = app.vault.getAbstractFileByPath(path6);
|
|
11919
|
-
if (f == null) return null;
|
|
11920
|
-
if (!("extension" in f)) return path6;
|
|
11921
|
-
return viewOfNote(await app.vault.read(f)) === ref2.full ? null : path6;
|
|
11922
|
-
}
|
|
11923
|
-
async function syncViewNotes(app, settings, model) {
|
|
11924
|
-
const out = { created: [], updated: [], removed: [], skipped: [] };
|
|
11925
|
-
const ms = model ?? (await loadMetastore(app, settings))?.model ?? null;
|
|
11926
|
-
if (!ms) return out;
|
|
11927
|
-
const wanted = /* @__PURE__ */ new Map();
|
|
11928
|
-
if (settings.viewNotes !== false) {
|
|
11929
|
-
for (const { ref: ref2, view } of catalogViews(ms)) wanted.set(viewNotePath(settings, ref2), viewNoteText(ref2, view));
|
|
11930
|
-
}
|
|
11931
|
-
for (const [path6, text2] of wanted) {
|
|
11932
|
-
const f = app.vault.getAbstractFileByPath(path6);
|
|
11933
|
-
if (f == null) {
|
|
11934
|
-
await ensureFolder(app, parentFolder(path6));
|
|
11935
|
-
await app.vault.create(path6, text2);
|
|
11936
|
-
out.created.push(path6);
|
|
11937
|
-
continue;
|
|
11938
|
-
}
|
|
11939
|
-
if (!("extension" in f)) {
|
|
11940
|
-
out.skipped.push(path6);
|
|
11941
|
-
continue;
|
|
11942
|
-
}
|
|
11943
|
-
const current2 = await app.vault.read(f);
|
|
11944
|
-
const expected = viewOfNote(text2);
|
|
11945
|
-
if (viewOfNote(current2) !== expected) {
|
|
11946
|
-
out.skipped.push(path6);
|
|
11947
|
-
continue;
|
|
11948
|
-
}
|
|
11949
|
-
if (current2 !== text2) {
|
|
11950
|
-
await app.vault.process(f, () => text2);
|
|
11951
|
-
out.updated.push(path6);
|
|
11952
|
-
}
|
|
11953
|
-
}
|
|
11954
|
-
const depth = root(settings).split("/").length + 3;
|
|
11955
|
-
for (const f of app.vault.getMarkdownFiles()) {
|
|
11956
|
-
if (wanted.has(f.path) || !f.path.startsWith(`${root(settings)}/`) || f.path.split("/").length !== depth) continue;
|
|
11957
|
-
const text2 = await app.vault.read(f);
|
|
11958
|
-
if (viewOfNote(text2) == null || !text2.includes(GENERATED_MARK)) continue;
|
|
11959
|
-
await trash(app, f);
|
|
11960
|
-
out.removed.push(f.path);
|
|
11961
|
-
}
|
|
11962
|
-
return out;
|
|
11963
|
-
}
|
|
11964
|
-
|
|
11965
11546
|
// src/core/catalog-ops.ts
|
|
11966
11547
|
var IDENT2 = /^[a-z][a-z0-9_]*$/;
|
|
11967
11548
|
function ident(kind, value) {
|
|
@@ -12063,8 +11644,6 @@ async function createViewImpl(app, settings, input, opts = {}) {
|
|
|
12063
11644
|
const conflicts = [];
|
|
12064
11645
|
if (sch?.tables[ref2.table]) conflicts.push({ path: ref2.full, message: `${ref2.full} is a table` });
|
|
12065
11646
|
if (sch?.views[ref2.table] && input.replace !== true) conflicts.push({ path: ref2.full, message: `${ref2.full} already exists (pass replace)` });
|
|
12066
|
-
const blocked = await viewNoteBlocked(app, settings, ref2);
|
|
12067
|
-
if (blocked) conflicts.push({ path: blocked, message: `${blocked} exists and is not this view's note: rename it or choose another view name` });
|
|
12068
11647
|
if (hasThisTokens(input.query)) viewQueryToSelect(input.query);
|
|
12069
11648
|
else await select(app, settings, { ...viewQueryToSelect(input.query), limit: 1 });
|
|
12070
11649
|
for (const [k, v] of Object.entries(input.labels ?? {})) {
|
|
@@ -12078,11 +11657,7 @@ async function createViewImpl(app, settings, input, opts = {}) {
|
|
|
12078
11657
|
};
|
|
12079
11658
|
const cs = emptyChangeSet("create_view", `create view ${ref2.full}`, `${metastorePath(settings)}#${ref2.full}`);
|
|
12080
11659
|
cs.texts.push({ path: metastorePath(settings), before: text2, after: setViewEntry(text2, ref2, view) });
|
|
12081
|
-
return
|
|
12082
|
-
}
|
|
12083
|
-
async function withViewNotes(app, settings, r) {
|
|
12084
|
-
if (r.ok && !r.dryRun) await syncViewNotes(app, settings);
|
|
12085
|
-
return r;
|
|
11660
|
+
return finish(app, settings, cs, conflicts, [], opts);
|
|
12086
11661
|
}
|
|
12087
11662
|
async function dropViewImpl(app, settings, input, opts = {}) {
|
|
12088
11663
|
const { text: text2, model } = await metastoreOrEmpty(app, settings);
|
|
@@ -12090,7 +11665,7 @@ async function dropViewImpl(app, settings, input, opts = {}) {
|
|
|
12090
11665
|
if (!ref2) throw new TableNotFoundError(input.view);
|
|
12091
11666
|
const cs = emptyChangeSet("drop_view", `drop view ${ref2.full}`, `${metastorePath(settings)}#${ref2.full}`);
|
|
12092
11667
|
cs.texts.push({ path: metastorePath(settings), before: text2, after: setViewEntry(text2, ref2, null) });
|
|
12093
|
-
return
|
|
11668
|
+
return finish(app, settings, cs, [], [], opts);
|
|
12094
11669
|
}
|
|
12095
11670
|
var createView = instrumented("create_view", createViewImpl, (i) => ({ name: i.name, catalog: i.catalog, schema: i.schema }));
|
|
12096
11671
|
var dropView = instrumented("drop_view", dropViewImpl, (i) => ({ view: i.view }));
|
|
@@ -12343,13 +11918,15 @@ async function planInheritedRows(app, settings, model, text2, refs, rewrite, cs)
|
|
|
12343
11918
|
checkColumnRefs(model, ref2.full, effectiveColumns(model, ref2, t.catalog.entry).filter((c) => !own.has(String(c.name))), ref2.full);
|
|
12344
11919
|
const old = await loadTable(app, settings, ref2.full);
|
|
12345
11920
|
const dense = t.def.columns.filter((c) => c.dense);
|
|
11921
|
+
const now = /* @__PURE__ */ new Date();
|
|
11922
|
+
const tz = (await readDatabaseConfig(app, settings)).timezone;
|
|
12346
11923
|
for (const r of await loadRows(app, settings, old)) {
|
|
12347
11924
|
let after = rewrite(r.ordered);
|
|
12348
|
-
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);
|
|
12349
11926
|
after = await fillIdentities(app, settings, t, after, r.file.path);
|
|
12350
|
-
const v = validateRow(t.def, after, {
|
|
11927
|
+
const v = validateRow(t.def, after, { tableProperty: settings.tableProperty }, validationContextFor(app, settings, r.file.path));
|
|
12351
11928
|
if (v.length > 0) violations.push({ path: r.file.path, violations: v });
|
|
12352
|
-
if (JSON.stringify(after) !== JSON.stringify(r.ordered)) cs.edits.push({ path: r.file.path,
|
|
11929
|
+
if (JSON.stringify(after) !== JSON.stringify(r.ordered)) cs.edits.push({ path: r.file.path, before: r.ordered, after });
|
|
12353
11930
|
}
|
|
12354
11931
|
}
|
|
12355
11932
|
return violations;
|
|
@@ -12604,25 +12181,6 @@ function renderPathTemplate(template, values) {
|
|
|
12604
12181
|
return sanitizeFileName(scalarForPath(v));
|
|
12605
12182
|
});
|
|
12606
12183
|
}
|
|
12607
|
-
function wallClock(now, tz) {
|
|
12608
|
-
const parts = new Intl.DateTimeFormat("en-CA", {
|
|
12609
|
-
timeZone: tz || "UTC",
|
|
12610
|
-
year: "numeric",
|
|
12611
|
-
month: "2-digit",
|
|
12612
|
-
day: "2-digit",
|
|
12613
|
-
hour: "2-digit",
|
|
12614
|
-
minute: "2-digit",
|
|
12615
|
-
second: "2-digit",
|
|
12616
|
-
hourCycle: "h23"
|
|
12617
|
-
}).formatToParts(now);
|
|
12618
|
-
const g = (type) => parts.find((p) => p.type === type)?.value ?? "00";
|
|
12619
|
-
return `${g("year")}-${g("month")}-${g("day")}T${g("hour")}:${g("minute")}:${g("second")}`;
|
|
12620
|
-
}
|
|
12621
|
-
function resolveDefault(value, now, tz) {
|
|
12622
|
-
if (value === DEFAULT_NOW) return tz === "" ? `${now.toISOString().slice(0, 19)}Z` : normalizeDatetime(wallClock(now, tz), tz);
|
|
12623
|
-
if (value === DEFAULT_TODAY) return wallClock(now, tz).slice(0, 10);
|
|
12624
|
-
return value;
|
|
12625
|
-
}
|
|
12626
12184
|
function computedColumnNames(t) {
|
|
12627
12185
|
return new Set(t.def.columns.filter((c) => c.generated !== void 0 || c.identity !== void 0).map((c) => c.name));
|
|
12628
12186
|
}
|
|
@@ -12801,7 +12359,7 @@ function knownKeys(t, settings) {
|
|
|
12801
12359
|
return /* @__PURE__ */ new Set([
|
|
12802
12360
|
...t.def.columns.map((c) => c.name),
|
|
12803
12361
|
...t.def.untypedFields,
|
|
12804
|
-
|
|
12362
|
+
settings.tableProperty,
|
|
12805
12363
|
"path"
|
|
12806
12364
|
]);
|
|
12807
12365
|
}
|
|
@@ -12881,18 +12439,18 @@ async function resolveWithin(app, settings, t, where) {
|
|
|
12881
12439
|
const rows = await loadRows(app, settings, target);
|
|
12882
12440
|
const names = new Set(rows.map((r) => basenameOf(r.file.path)));
|
|
12883
12441
|
const raw = cond.within;
|
|
12884
|
-
let
|
|
12885
|
-
if (
|
|
12442
|
+
let root = linkName(raw) ?? (typeof raw === "string" ? basenameOf(raw.trim()) : null);
|
|
12443
|
+
if (root != null && !names.has(root)) {
|
|
12886
12444
|
const hit = rows.find((r) => r.ordered.some(([k, v]) => (k === "name" || k === "title") && v === raw));
|
|
12887
|
-
|
|
12445
|
+
root = hit ? basenameOf(hit.file.path) : null;
|
|
12888
12446
|
}
|
|
12889
|
-
if (
|
|
12447
|
+
if (root == null) throw new OperationInputError(`"within": no row of ${spec.ref} named ${JSON.stringify(raw)}`);
|
|
12890
12448
|
const children = /* @__PURE__ */ new Map();
|
|
12891
12449
|
for (const r of rows) {
|
|
12892
12450
|
const p = linkName(fmGet(r.ordered, h.name));
|
|
12893
12451
|
if (p) children.set(p, [...children.get(p) ?? [], basenameOf(r.file.path)]);
|
|
12894
12452
|
}
|
|
12895
|
-
const subtree = /* @__PURE__ */ new Set([
|
|
12453
|
+
const subtree = /* @__PURE__ */ new Set([root]);
|
|
12896
12454
|
for (const n of subtree) for (const c of children.get(n) ?? []) subtree.add(c);
|
|
12897
12455
|
const { within: _w, ...rest } = cond;
|
|
12898
12456
|
out ??= { ...where };
|
|
@@ -13066,7 +12624,7 @@ async function selectOn(app, settings, input, hist) {
|
|
|
13066
12624
|
const tz = (await readDatabaseConfig(app, settings)).timezone;
|
|
13067
12625
|
const now = /* @__PURE__ */ new Date();
|
|
13068
12626
|
const get = rowGetter(app, t, settings, tz);
|
|
13069
|
-
const hidden2 = /* @__PURE__ */ new Set([settings.
|
|
12627
|
+
const hidden2 = /* @__PURE__ */ new Set([settings.tableProperty]);
|
|
13070
12628
|
const specs = new Map(t.def.columns.map((c) => [c.name, c]));
|
|
13071
12629
|
for (const c of [...input.columns ?? [], ...input.expand ?? []]) {
|
|
13072
12630
|
if (!known.has(c)) throw new OperationInputError(`unknown column: "${c}"`);
|
|
@@ -13209,7 +12767,7 @@ async function checkImpl(app, settings, input) {
|
|
|
13209
12767
|
);
|
|
13210
12768
|
for (const r of rows) {
|
|
13211
12769
|
const v = [
|
|
13212
|
-
...validateRow(t.def, r.ordered, {
|
|
12770
|
+
...validateRow(t.def, r.ordered, { tableProperty: settings.tableProperty }, validationContextFor(app, settings, r.file.path)),
|
|
13213
12771
|
...unique.get(r.file.path) ?? [],
|
|
13214
12772
|
...constraintViolations(t, settings, r.file.path, r.ordered),
|
|
13215
12773
|
...generatedMismatches(t, settings, r)
|
|
@@ -13253,11 +12811,12 @@ function rowTemplate(t, values) {
|
|
|
13253
12811
|
function planInsert(app, settings, t, input, env) {
|
|
13254
12812
|
const known = knownKeys(t, settings);
|
|
13255
12813
|
known.delete("path");
|
|
13256
|
-
known.delete(
|
|
12814
|
+
known.delete(settings.tableProperty);
|
|
13257
12815
|
for (const k of Object.keys(input.values)) {
|
|
13258
12816
|
if (!known.has(k)) throw new OperationInputError(`unknown column "${k}" for table "${t.def.name}"`);
|
|
13259
12817
|
}
|
|
13260
12818
|
const computed = computedColumnNames(t);
|
|
12819
|
+
for (const c of t.def.columns) if (c.identity === "filename") computed.delete(c.name);
|
|
13261
12820
|
for (const k of Object.keys(input.values)) {
|
|
13262
12821
|
if (computed.has(k)) throw new OperationInputError(`"${k}" is a generated column: its value is computed`);
|
|
13263
12822
|
}
|
|
@@ -13286,8 +12845,8 @@ function planInsert(app, settings, t, input, env) {
|
|
|
13286
12845
|
const values = new Map(ordered);
|
|
13287
12846
|
let path6 = input.path;
|
|
13288
12847
|
if (path6 == null) {
|
|
13289
|
-
if (
|
|
13290
|
-
throw new OperationInputError(`table "${t.def.name}" has no
|
|
12848
|
+
if (!t.def.options.filename) {
|
|
12849
|
+
throw new OperationInputError(`table "${t.def.name}" has no filename pattern; pass "path"`);
|
|
13291
12850
|
}
|
|
13292
12851
|
const base = `${renderPathTemplate(t.def.layout.rowPath, values)}/${renderPathTemplate(t.def.options.filename, values)}`;
|
|
13293
12852
|
path6 = `${base}.md`;
|
|
@@ -13300,7 +12859,7 @@ function planInsert(app, settings, t, input, env) {
|
|
|
13300
12859
|
ordered = withIdentity(t, ordered, path6, false, { now, tz });
|
|
13301
12860
|
ordered = withGenerated(t, settings, ordered, path6);
|
|
13302
12861
|
const violations = [
|
|
13303
|
-
...validateRow(t.def, ordered, {
|
|
12862
|
+
...validateRow(t.def, ordered, { tableProperty: settings.tableProperty }, validationContextFor(app, settings, path6)),
|
|
13304
12863
|
...constraintViolations(t, settings, path6, ordered)
|
|
13305
12864
|
];
|
|
13306
12865
|
return { path: path6, ordered, conflicts, violations };
|
|
@@ -13368,7 +12927,7 @@ async function mergeRowsImpl(app, settings, input, opts = {}) {
|
|
|
13368
12927
|
let after = r.ordered;
|
|
13369
12928
|
for (const [k, v] of normalized) {
|
|
13370
12929
|
if (input.on.includes(k)) continue;
|
|
13371
|
-
if (!known.has(k) || k === "path" || k ===
|
|
12930
|
+
if (!known.has(k) || k === "path" || k === settings.tableProperty) throw new OperationInputError(`unknown column "${k}" for table "${t.def.name}"`);
|
|
13372
12931
|
if (computed.has(k)) throw new OperationInputError(`"${k}" is a generated column: its value is computed`);
|
|
13373
12932
|
after = fmSet(after, k, v);
|
|
13374
12933
|
}
|
|
@@ -13383,7 +12942,7 @@ async function mergeRowsImpl(app, settings, input, opts = {}) {
|
|
|
13383
12942
|
return validateValue(spec, v, ctx);
|
|
13384
12943
|
});
|
|
13385
12944
|
violations.push({ path: r.file.path, violations: [...rowViolations, ...constraintViolations(t, settings, r.file.path, after)] });
|
|
13386
|
-
cs.edits.push({ path: r.file.path,
|
|
12945
|
+
cs.edits.push({ path: r.file.path, before: r.ordered, after });
|
|
13387
12946
|
continue;
|
|
13388
12947
|
}
|
|
13389
12948
|
if (input.whenNotMatched === "ignore") continue;
|
|
@@ -13395,7 +12954,7 @@ async function mergeRowsImpl(app, settings, input, opts = {}) {
|
|
|
13395
12954
|
}
|
|
13396
12955
|
if (t.def.columns.some((c) => c.unique)) {
|
|
13397
12956
|
const afterByPath = new Map(cs.edits.map((e) => [e.path, e.after]));
|
|
13398
|
-
const created = cs.creates.map((c) => ({ path: c.path, ordered:
|
|
12957
|
+
const created = cs.creates.map((c) => ({ path: c.path, ordered: readOrderedFromMarkdown(c.content, /* @__PURE__ */ new Set([settings.tableProperty])).ordered }));
|
|
13399
12958
|
const touched = /* @__PURE__ */ new Set([...afterByPath.keys(), ...created.map((c) => c.path)]);
|
|
13400
12959
|
violations.push(
|
|
13401
12960
|
...uniqueViolations(t.def.columns, [
|
|
@@ -13482,7 +13041,7 @@ async function recomputeImpl(app, settings, input, opts = {}) {
|
|
|
13482
13041
|
return isEmptyValue(v) ? [] : validateValue(c, v, ctx);
|
|
13483
13042
|
})
|
|
13484
13043
|
});
|
|
13485
|
-
cs.edits.push({ path: r.file.path,
|
|
13044
|
+
cs.edits.push({ path: r.file.path, before: r.ordered, after });
|
|
13486
13045
|
}
|
|
13487
13046
|
}
|
|
13488
13047
|
if (cs.edits.length === 0) return { ...emptyResult("recompute", opts.dryRun === true), ok: true };
|
|
@@ -13493,19 +13052,17 @@ async function updateRowsImpl(app, settings, input, opts = {}) {
|
|
|
13493
13052
|
const t = await loadTable(app, settings, input.table);
|
|
13494
13053
|
const known = knownKeys(t, settings);
|
|
13495
13054
|
known.delete("path");
|
|
13496
|
-
known.delete(
|
|
13055
|
+
known.delete(settings.tableProperty);
|
|
13497
13056
|
const set = input.set ?? {};
|
|
13498
13057
|
const unset = input.unset ?? [];
|
|
13499
13058
|
if (Object.keys(set).length === 0 && unset.length === 0 && input.body === void 0) {
|
|
13500
13059
|
throw new OperationInputError(`nothing to update: pass "set", "unset" or "body"`);
|
|
13501
13060
|
}
|
|
13502
|
-
const reservedOnRows = new Set(rowReservedKeys(settings));
|
|
13503
13061
|
for (const k of Object.keys(set)) {
|
|
13504
13062
|
if (!known.has(k)) throw new OperationInputError(`unknown column "${k}" for table "${t.def.name}"`);
|
|
13505
13063
|
}
|
|
13506
13064
|
for (const k of unset) {
|
|
13507
|
-
if (k ===
|
|
13508
|
-
void reservedOnRows;
|
|
13065
|
+
if (k === settings.tableProperty || k === "path") throw new OperationInputError(`cannot unset "${k}"`);
|
|
13509
13066
|
}
|
|
13510
13067
|
const computed = computedColumnNames(t);
|
|
13511
13068
|
for (const k of [...Object.keys(set), ...unset]) {
|
|
@@ -13524,7 +13081,7 @@ async function updateRowsImpl(app, settings, input, opts = {}) {
|
|
|
13524
13081
|
if (input.body !== void 0 && matched.length !== 1) {
|
|
13525
13082
|
throw new OperationInputError(`"body" updates exactly one row; "where" matched ${matched.length}`);
|
|
13526
13083
|
}
|
|
13527
|
-
const nameTemplate = t.def.
|
|
13084
|
+
const nameTemplate = t.def.options.filename ? `${t.def.layout.rowPath}/${t.def.options.filename}` : null;
|
|
13528
13085
|
const nameCols = new Set([...(nameTemplate ?? "").matchAll(/\{([A-Za-z][A-Za-z0-9_-]*)(?::[^}]+)?\}/g)].map((m) => m[1]));
|
|
13529
13086
|
const renames = [...Object.keys(set), ...unset].some((k) => nameCols.has(k));
|
|
13530
13087
|
const movedTo = /* @__PURE__ */ new Set();
|
|
@@ -13572,7 +13129,6 @@ async function updateRowsImpl(app, settings, input, opts = {}) {
|
|
|
13572
13129
|
violations.push({ path: r.file.path, violations: rowViolations });
|
|
13573
13130
|
cs.edits.push({
|
|
13574
13131
|
path: r.file.path,
|
|
13575
|
-
strict: false,
|
|
13576
13132
|
before: r.ordered,
|
|
13577
13133
|
after,
|
|
13578
13134
|
...input.body !== void 0 ? { bodyBefore: r.body, bodyAfter: input.body } : {}
|
|
@@ -13673,12 +13229,23 @@ async function deleteRowsImpl(app, settings, input, opts = {}) {
|
|
|
13673
13229
|
sources.forEach((s) => referrers.add(s));
|
|
13674
13230
|
}
|
|
13675
13231
|
if (policy === "set_null") {
|
|
13232
|
+
const model = cachedMetastore(app, settings);
|
|
13676
13233
|
for (const src of [...referrers].sort()) {
|
|
13677
13234
|
const f = app.vault.getAbstractFileByPath(src);
|
|
13678
13235
|
if (!f || !("extension" in f) || f.extension !== "md") continue;
|
|
13679
13236
|
const text2 = await app.vault.read(f);
|
|
13680
|
-
const before =
|
|
13681
|
-
|
|
13237
|
+
const before = readOrderedFromMarkdown(text2).ordered;
|
|
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 });
|
|
13682
13249
|
}
|
|
13683
13250
|
}
|
|
13684
13251
|
const folder = archiveFolder(t.def);
|
|
@@ -13697,7 +13264,7 @@ async function deleteRowsImpl(app, settings, input, opts = {}) {
|
|
|
13697
13264
|
}
|
|
13698
13265
|
function checkRetarget(dst, settings, r) {
|
|
13699
13266
|
const dstKnown = knownKeys(dst, settings);
|
|
13700
|
-
const membership = /* @__PURE__ */ new Set([settings.
|
|
13267
|
+
const membership = /* @__PURE__ */ new Set([settings.tableProperty]);
|
|
13701
13268
|
const computed = computedColumnNames(dst);
|
|
13702
13269
|
for (const [from, to] of Object.entries(r.rename)) {
|
|
13703
13270
|
if (typeof to !== "string" || !dstKnown.has(to)) {
|
|
@@ -13717,7 +13284,7 @@ function checkRetarget(dst, settings, r) {
|
|
|
13717
13284
|
}
|
|
13718
13285
|
async function retargetRows(app, settings, dst, notes, r, cs, opts) {
|
|
13719
13286
|
const dstKnown = knownKeys(dst, settings);
|
|
13720
|
-
const membership = /* @__PURE__ */ new Set([settings.
|
|
13287
|
+
const membership = /* @__PURE__ */ new Set([settings.tableProperty]);
|
|
13721
13288
|
const tz = (await readDatabaseConfig(app, settings)).timezone;
|
|
13722
13289
|
const violations = [];
|
|
13723
13290
|
const conflicts = [];
|
|
@@ -13749,11 +13316,14 @@ async function retargetRows(app, settings, dst, notes, r, cs, opts) {
|
|
|
13749
13316
|
after = withGenerated(dst, settings, after, n.path);
|
|
13750
13317
|
let to = n.path;
|
|
13751
13318
|
if (r.relocate) {
|
|
13752
|
-
if (
|
|
13319
|
+
if (!dst.def.options.filename) {
|
|
13753
13320
|
throw new OperationInputError(`table "${dst.def.name}" has no filename pattern: cannot relocate rows`);
|
|
13754
13321
|
}
|
|
13755
13322
|
try {
|
|
13756
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
|
+
}
|
|
13757
13327
|
const base = `${renderPathTemplate(dst.def.layout.rowPath, values)}/${renderPathTemplate(dst.def.options.filename, values)}`;
|
|
13758
13328
|
to = `${base}.md`;
|
|
13759
13329
|
for (let k = 2; to !== n.path && (taken.has(to) || app.vault.getAbstractFileByPath(to) != null) && k <= MAX_NAME_SUFFIX; k++) {
|
|
@@ -13772,11 +13342,11 @@ async function retargetRows(app, settings, dst, notes, r, cs, opts) {
|
|
|
13772
13342
|
violations.push({
|
|
13773
13343
|
path: n.path,
|
|
13774
13344
|
violations: [
|
|
13775
|
-
...validateRow(dst.def, after, {
|
|
13345
|
+
...validateRow(dst.def, after, { tableProperty: settings.tableProperty }, validationContextFor(app, settings, n.path)),
|
|
13776
13346
|
...constraintViolations(dst, settings, n.path, after)
|
|
13777
13347
|
]
|
|
13778
13348
|
});
|
|
13779
|
-
cs.edits.push({ path: n.path,
|
|
13349
|
+
cs.edits.push({ path: n.path, before: n.ordered, after });
|
|
13780
13350
|
if (to !== n.path) cs.moves.push({ from: n.path, to });
|
|
13781
13351
|
}
|
|
13782
13352
|
if (dst.def.columns.some((c) => c.unique)) {
|
|
@@ -13826,10 +13396,9 @@ async function adoptRowsImpl(app, settings, input, opts = {}) {
|
|
|
13826
13396
|
for (const p of paths) {
|
|
13827
13397
|
const f = app.vault.getAbstractFileByPath(p);
|
|
13828
13398
|
if (f == null || !("extension" in f)) throw new OperationInputError(`note not found: ${p}`);
|
|
13829
|
-
const { ordered } =
|
|
13399
|
+
const { ordered } = readOrderedFromMarkdown(
|
|
13830
13400
|
await app.vault.read(f),
|
|
13831
|
-
settings.
|
|
13832
|
-
/* @__PURE__ */ new Set([settings.templateProperty, settings.tableProperty])
|
|
13401
|
+
/* @__PURE__ */ new Set([settings.tableProperty])
|
|
13833
13402
|
);
|
|
13834
13403
|
const owner = model ? rowTableFull(model, fmGet(ordered, settings.tableProperty)) : null;
|
|
13835
13404
|
if (owner) conflicts.push({ path: p, message: `already a row of ${owner}: use move_rows` });
|
|
@@ -13862,8 +13431,7 @@ async function relocateTable(app, settings, t, dest, changes, opts, properties =
|
|
|
13862
13431
|
if (v === null) delete merged[k];
|
|
13863
13432
|
else merged[k] = v;
|
|
13864
13433
|
}
|
|
13865
|
-
const
|
|
13866
|
-
const membership = /* @__PURE__ */ new Set([tp, settings.tableProperty]);
|
|
13434
|
+
const membership = /* @__PURE__ */ new Set([settings.tableProperty]);
|
|
13867
13435
|
let options;
|
|
13868
13436
|
let layout;
|
|
13869
13437
|
let member;
|
|
@@ -13871,7 +13439,6 @@ async function relocateTable(app, settings, t, dest, changes, opts, properties =
|
|
|
13871
13439
|
let cs;
|
|
13872
13440
|
{
|
|
13873
13441
|
const ref2 = dest.catalogRef;
|
|
13874
|
-
delete merged.path;
|
|
13875
13442
|
options = parseTableOptions([{ ...merged, name: ref2.table }]);
|
|
13876
13443
|
layout = catalogLayout(databaseRoot(settings), ref2, options);
|
|
13877
13444
|
member = (o) => withRowId([[settings.tableProperty, ref2.full], ...o.filter(([k]) => !membership.has(k))]);
|
|
@@ -13933,14 +13500,14 @@ async function relocateTable(app, settings, t, dest, changes, opts, properties =
|
|
|
13933
13500
|
if (!list2.includes(oldName)) after = fmSet(after, "aliases", [...list2, oldName]);
|
|
13934
13501
|
}
|
|
13935
13502
|
if (JSON.stringify(after) !== JSON.stringify(r.ordered)) {
|
|
13936
|
-
cs.edits.push({ path: r.path,
|
|
13503
|
+
cs.edits.push({ path: r.path, before: r.ordered, after });
|
|
13937
13504
|
}
|
|
13938
13505
|
if (to !== r.path) cs.moves.push({ from: r.path, to });
|
|
13939
13506
|
};
|
|
13940
13507
|
const excluded = (p) => (options.exclude ?? []).some((dir) => p.startsWith(`${dir.replace(/\/+$/, "")}/`));
|
|
13941
13508
|
const rows = (await loadRows(app, settings, t)).filter((r) => !excluded(r.file.path));
|
|
13942
13509
|
const bases = /* @__PURE__ */ new Map();
|
|
13943
|
-
if (
|
|
13510
|
+
if (options.filename) {
|
|
13944
13511
|
for (const r of rows) {
|
|
13945
13512
|
try {
|
|
13946
13513
|
const values = new Map(r.ordered);
|
|
@@ -13965,14 +13532,14 @@ async function relocateTable(app, settings, t, dest, changes, opts, properties =
|
|
|
13965
13532
|
}
|
|
13966
13533
|
for (const r of rows) {
|
|
13967
13534
|
const base = bases.get(r.file.path);
|
|
13968
|
-
if (
|
|
13535
|
+
if (options.filename && base === void 0) continue;
|
|
13969
13536
|
const to = base === void 0 ? r.file.path : base.startsWith("\0") ? base.slice(1) : claim(base, r.file.path);
|
|
13970
13537
|
relink({ path: r.file.path, ordered: r.ordered }, to);
|
|
13971
13538
|
}
|
|
13972
13539
|
const oldArchive = t.def.layout.archive;
|
|
13973
13540
|
const newArchive = layout.archive;
|
|
13974
13541
|
for (const f of archivedRowFiles(app, settings, t)) {
|
|
13975
|
-
const { ordered } =
|
|
13542
|
+
const { ordered } = readOrderedFromMarkdown(await app.vault.read(f), membership);
|
|
13976
13543
|
const to = newArchive === oldArchive ? f.path : claim(`${newArchive}/${basenameOf(f.path)}`, f.path);
|
|
13977
13544
|
relink({ path: f.path, ordered }, to);
|
|
13978
13545
|
}
|
|
@@ -13981,25 +13548,15 @@ async function relocateTable(app, settings, t, dest, changes, opts, properties =
|
|
|
13981
13548
|
async function alterTableImpl(app, settings, input, opts = {}) {
|
|
13982
13549
|
const t = await loadTable(app, settings, input.table);
|
|
13983
13550
|
const cfg = await readDatabaseConfig(app, settings);
|
|
13984
|
-
const schema = assertIdentifier("schema", input.schema ??
|
|
13551
|
+
const schema = assertIdentifier("schema", input.schema ?? t.def.schema);
|
|
13985
13552
|
const name = assertIdentifier("table", input.name ?? t.def.name);
|
|
13986
|
-
const catalog = assertIdentifier("catalog", input.catalog ?? t.def.catalog
|
|
13553
|
+
const catalog = assertIdentifier("catalog", input.catalog ?? t.def.catalog);
|
|
13554
|
+
const changes = { name: null };
|
|
13555
|
+
if (input.partition !== void 0) changes.partition = input.partition ? input.partition : null;
|
|
13987
13556
|
const cur = t.def.options;
|
|
13988
|
-
const changes = { path: null, name: null };
|
|
13989
|
-
let partition = input.partition ?? cur.partition;
|
|
13990
|
-
if (input.partition === void 0 && cur.path) {
|
|
13991
|
-
const own = t.def.layout.folder;
|
|
13992
|
-
if (cur.path === own) partition = "";
|
|
13993
|
-
else if (cur.path.startsWith(`${own}/`)) partition = cur.path.slice(own.length + 1);
|
|
13994
|
-
else {
|
|
13995
|
-
const brace = cur.path.indexOf("{");
|
|
13996
|
-
partition = brace < 0 ? "" : cur.path.slice(cur.path.lastIndexOf("/", brace) + 1);
|
|
13997
|
-
}
|
|
13998
|
-
}
|
|
13999
|
-
changes.partition = partition ? partition : null;
|
|
14000
13557
|
if (!cur.archive || cur.archive.includes("/")) {
|
|
14001
13558
|
const own = t.def.layout.archive;
|
|
14002
|
-
changes.archive =
|
|
13559
|
+
changes.archive = own.startsWith(`${t.def.layout.folder}/`) && !own.slice(t.def.layout.folder.length + 1).includes("/") ? own.slice(t.def.layout.folder.length + 1) : cfg.archive_folder;
|
|
14003
13560
|
}
|
|
14004
13561
|
if (input.filename !== void 0) changes.filename = input.filename;
|
|
14005
13562
|
if (input.description !== void 0) changes.description = input.description;
|
|
@@ -14040,7 +13597,7 @@ async function syncRelations(app, settings, changes, opts = {}) {
|
|
|
14040
13597
|
if (plan.edits.size === 0) return null;
|
|
14041
13598
|
const cs = emptyChangeSet("sync_relations", `mirror links of ${changes.map((c) => c.path).join(", ")}`.slice(0, 200));
|
|
14042
13599
|
cs.skipInverse = true;
|
|
14043
|
-
for (const [path6, w] of plan.edits) cs.edits.push({ path: path6,
|
|
13600
|
+
for (const [path6, w] of plan.edits) cs.edits.push({ path: path6, before: w.before, after: w.after });
|
|
14044
13601
|
return finish(app, settings, cs, plan.conflicts, [], opts);
|
|
14045
13602
|
}
|
|
14046
13603
|
async function alignRelationsImpl(app, settings, input, opts = {}) {
|
|
@@ -14058,7 +13615,7 @@ async function alignRelationsImpl(app, settings, input, opts = {}) {
|
|
|
14058
13615
|
const plan = await planInverse(app, settings, t.catalog.model, changes, { keepSingles: true });
|
|
14059
13616
|
const cs = emptyChangeSet("align_relations", `align inverse links of ${t.catalog.ref.full}`, t.def.schemaPath);
|
|
14060
13617
|
cs.skipInverse = true;
|
|
14061
|
-
for (const [path6, w] of plan.edits) cs.edits.push({ path: path6,
|
|
13618
|
+
for (const [path6, w] of plan.edits) cs.edits.push({ path: path6, before: w.before, after: w.after });
|
|
14062
13619
|
return finish(app, settings, cs, plan.conflicts, [], opts);
|
|
14063
13620
|
}
|
|
14064
13621
|
function alignRelations(app, settings, input, opts = {}) {
|
|
@@ -14094,7 +13651,7 @@ async function adoptCreatedNote(app, settings, file) {
|
|
|
14094
13651
|
const target = await adoptTargetFor(app, settings, file.path);
|
|
14095
13652
|
if (!target) return { adopted: false };
|
|
14096
13653
|
const text2 = await app.vault.read(file);
|
|
14097
|
-
const { ordered, body } =
|
|
13654
|
+
const { ordered, body } = readOrderedFromMarkdown(text2, /* @__PURE__ */ new Set([settings.tableProperty]));
|
|
14098
13655
|
const declared = ordered.find(([k]) => k === settings.tableProperty)?.[1];
|
|
14099
13656
|
if (declared !== void 0) {
|
|
14100
13657
|
if (declared !== target.table && !target.table.endsWith(`.${String(declared)}`)) return { adopted: false };
|
|
@@ -14110,6 +13667,10 @@ async function adoptCreatedNote(app, settings, file) {
|
|
|
14110
13667
|
const present = new Set(ordered.map(([k]) => k));
|
|
14111
13668
|
const values = Object.fromEntries(Object.entries(target.values).filter(([k]) => !present.has(k)));
|
|
14112
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
|
+
}
|
|
14113
13674
|
if (c.external_default !== void 0 && !present.has(c.name) && values[c.name] === void 0) values[c.name] = c.external_default;
|
|
14114
13675
|
}
|
|
14115
13676
|
const r = await adoptRows(app, settings, { table: target.table, paths: [file.path], perRow: { [file.path]: values }, relocate: false });
|
|
@@ -14534,7 +14095,7 @@ function frontmatter(app, f) {
|
|
|
14534
14095
|
}
|
|
14535
14096
|
async function inferTable(app, settings, input) {
|
|
14536
14097
|
const maxEnum = input.maxEnumValues ?? 12;
|
|
14537
|
-
const skip = /* @__PURE__ */ new Set([
|
|
14098
|
+
const skip = /* @__PURE__ */ new Set([settings.tableProperty, "id", ...SKIP_KEYS]);
|
|
14538
14099
|
const notes = [];
|
|
14539
14100
|
if (input.table !== void 0) {
|
|
14540
14101
|
const t = await loadTable(app, settings, input.table);
|
|
@@ -15429,139 +14990,6 @@ var OtlpSink = class {
|
|
|
15429
14990
|
}
|
|
15430
14991
|
};
|
|
15431
14992
|
|
|
15432
|
-
// src/core/view-block.ts
|
|
15433
|
-
var import_yaml3 = __toESM(require_dist(), 1);
|
|
15434
|
-
var BLOCK_KEYS = /* @__PURE__ */ new Set(["view", "table", "columns", "where", "order_by", "limit", "join", "group_by", "aggregates", "expand", "labels", "row_link", "chart", "with_table"]);
|
|
15435
|
-
function parseChart(raw) {
|
|
15436
|
-
if (raw == null || typeof raw !== "object" || Array.isArray(raw)) throw new OperationInputError(`datanotes block: "chart" must be a mapping (type, x, y)`);
|
|
15437
|
-
const c = raw;
|
|
15438
|
-
for (const k of Object.keys(c)) {
|
|
15439
|
-
if (!["type", "x", "y", "targets", "title"].includes(k)) throw new OperationInputError(`datanotes block: unknown chart key "${k}" (allowed: type, x, y, targets, title)`);
|
|
15440
|
-
}
|
|
15441
|
-
const type = c.type ?? "line";
|
|
15442
|
-
if (type !== "line" && type !== "bar") throw new OperationInputError(`datanotes block: chart type must be line or bar`);
|
|
15443
|
-
if (typeof c.x !== "string" || !c.x.trim()) throw new OperationInputError(`datanotes block: chart needs "x" (the column along the axis)`);
|
|
15444
|
-
const y = typeof c.y === "string" ? [c.y] : Array.isArray(c.y) ? c.y.map(String) : [];
|
|
15445
|
-
if (!y.length) throw new OperationInputError(`datanotes block: chart needs "y" (a column or a list of columns)`);
|
|
15446
|
-
const targets = {};
|
|
15447
|
-
if (c.targets != null) {
|
|
15448
|
-
if (typeof c.targets !== "object" || Array.isArray(c.targets)) throw new OperationInputError(`datanotes block: chart "targets" must be a mapping label \u2192 number`);
|
|
15449
|
-
for (const [k, v] of Object.entries(c.targets)) {
|
|
15450
|
-
const n = Number(v);
|
|
15451
|
-
if (!Number.isFinite(n)) throw new OperationInputError(`datanotes block: chart target "${k}" must be a number`);
|
|
15452
|
-
targets[k] = n;
|
|
15453
|
-
}
|
|
15454
|
-
}
|
|
15455
|
-
return { type, x: c.x.trim(), y, targets, ...typeof c.title === "string" ? { title: c.title } : {} };
|
|
15456
|
-
}
|
|
15457
|
-
function parseViewBlock(source) {
|
|
15458
|
-
let raw;
|
|
15459
|
-
try {
|
|
15460
|
-
raw = (0, import_yaml3.parse)(source);
|
|
15461
|
-
} catch (e) {
|
|
15462
|
-
throw new OperationInputError(`datanotes block: invalid YAML (${e instanceof Error ? e.message : String(e)})`);
|
|
15463
|
-
}
|
|
15464
|
-
if (raw == null || typeof raw !== "object" || Array.isArray(raw)) {
|
|
15465
|
-
throw new OperationInputError(`datanotes block: write "view: <catalog.schema.view>" or a query ("table: \u2026")`);
|
|
15466
|
-
}
|
|
15467
|
-
const b = raw;
|
|
15468
|
-
for (const k of Object.keys(b)) {
|
|
15469
|
-
if (!BLOCK_KEYS.has(k)) throw new OperationInputError(`datanotes block: unknown key "${k}" (allowed: ${[...BLOCK_KEYS].join(", ")})`);
|
|
15470
|
-
}
|
|
15471
|
-
if (b.view !== void 0 && b.table !== void 0) throw new OperationInputError(`datanotes block: pass "view" or "table", not both`);
|
|
15472
|
-
const target = b.view ?? b.table;
|
|
15473
|
-
if (typeof target !== "string" || target.trim() === "") throw new OperationInputError(`datanotes block needs "view" or "table"`);
|
|
15474
|
-
const { view: _v, labels, row_link, chart, with_table, ...query } = b;
|
|
15475
|
-
const labelMap = {};
|
|
15476
|
-
if (labels != null) {
|
|
15477
|
-
if (typeof labels !== "object" || Array.isArray(labels)) throw new OperationInputError(`datanotes block: "labels" must be a mapping`);
|
|
15478
|
-
for (const [k, v] of Object.entries(labels)) labelMap[k] = String(v);
|
|
15479
|
-
}
|
|
15480
|
-
const select2 = viewQueryToSelect({ ...query, table: target.trim() });
|
|
15481
|
-
return {
|
|
15482
|
-
select: select2,
|
|
15483
|
-
labels: labelMap,
|
|
15484
|
-
rowLink: row_link !== false,
|
|
15485
|
-
...select2.columns ? { columns: select2.columns } : {},
|
|
15486
|
-
...chart !== void 0 ? { chart: parseChart(chart) } : {},
|
|
15487
|
-
withTable: with_table === true
|
|
15488
|
-
};
|
|
15489
|
-
}
|
|
15490
|
-
function chartValue(v) {
|
|
15491
|
-
if (v === null || v === void 0 || v === "") return "null";
|
|
15492
|
-
const n = Number(v);
|
|
15493
|
-
return Number.isFinite(n) ? String(Math.round(n * 100) / 100) : "null";
|
|
15494
|
-
}
|
|
15495
|
-
function chartLabel(v) {
|
|
15496
|
-
const text2 = cell(v);
|
|
15497
|
-
const d = text2.match(/^(\d{4})-(\d{2})-(\d{2})(?: (\d{2}:\d{2}))?$/);
|
|
15498
|
-
const shown = d ? `${d[3]}/${d[2]}${d[4] ? ` ${d[4]}` : ""}` : text2.replace(/\[\[(?:[^|\]]*\|)?([^\]]*)\]\]/g, "$1");
|
|
15499
|
-
return JSON.stringify(shown);
|
|
15500
|
-
}
|
|
15501
|
-
function chartMarkdown(result, chart, labels) {
|
|
15502
|
-
if (result.rows.length === 0) return "_No data to chart._";
|
|
15503
|
-
const missing = [chart.x, ...chart.y].filter((c) => !(c in result.rows[0]));
|
|
15504
|
-
if (missing.length) return `> [!warning] datanotes
|
|
15505
|
-
> chart: no column ${missing.map((m) => `"${m}"`).join(", ")} in the rows (columns: ${Object.keys(result.rows[0]).filter((k) => k !== "path").join(", ")})`;
|
|
15506
|
-
const lines = ["```chart", `type: ${chart.type}`, `labels: [${result.rows.map((r) => chartLabel(r[chart.x])).join(", ")}]`, "series:"];
|
|
15507
|
-
for (const col of chart.y) {
|
|
15508
|
-
lines.push(` - title: ${JSON.stringify(labels[col] ?? col)}`, ` data: [${result.rows.map((r) => chartValue(r[col])).join(", ")}]`);
|
|
15509
|
-
}
|
|
15510
|
-
for (const [label, value] of Object.entries(chart.targets)) {
|
|
15511
|
-
lines.push(` - title: ${JSON.stringify(label)}`, ` data: [${result.rows.map(() => String(value)).join(", ")}]`);
|
|
15512
|
-
}
|
|
15513
|
-
lines.push("tension: 0.3", "width: 100%", "fill: false", "beginAtZero: false", "spanGaps: true", "```");
|
|
15514
|
-
if (chart.title) lines.unshift(`**${chart.title}**`, "");
|
|
15515
|
-
return lines.join("\n");
|
|
15516
|
-
}
|
|
15517
|
-
function cell(v) {
|
|
15518
|
-
if (v === null || v === void 0) return "";
|
|
15519
|
-
if (Array.isArray(v)) return v.map(cell).filter((x) => x !== "").join(", ");
|
|
15520
|
-
if (typeof v === "boolean") return v ? "\u2713" : "";
|
|
15521
|
-
if (typeof v === "object") return JSON.stringify(v).replace(/\|/g, "\\|");
|
|
15522
|
-
const text2 = String(v);
|
|
15523
|
-
const dt = text2.match(/^(\d{4}-\d{2}-\d{2})T(\d{2}:\d{2}):\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/);
|
|
15524
|
-
if (dt) return `${dt[1]} ${dt[2]}`;
|
|
15525
|
-
return text2.replace(/\r?\n/g, " ").replace(/\|/g, "\\|");
|
|
15526
|
-
}
|
|
15527
|
-
function viewMarkdown(result, opts) {
|
|
15528
|
-
if (result.rows.length === 0) return "_No rows._";
|
|
15529
|
-
const grouped = !("path" in result.rows[0]);
|
|
15530
|
-
let columns = opts.columns ?? Object.keys(result.rows[0]).filter((k) => k !== "path");
|
|
15531
|
-
if (grouped) columns = Object.keys(result.rows[0]);
|
|
15532
|
-
const link = !grouped && opts.rowLink && !columns.includes("file_link");
|
|
15533
|
-
const heads = [...link ? [opts.labels.file_link ?? "Note"] : [], ...columns.map((c) => opts.labels[c] ?? c)];
|
|
15534
|
-
const lines = [`| ${heads.map(cell).join(" | ")} |`, `| ${heads.map(() => "---").join(" | ")} |`];
|
|
15535
|
-
for (const r of result.rows) {
|
|
15536
|
-
const path6 = typeof r.path === "string" ? r.path : "";
|
|
15537
|
-
const name = path6.split("/").pop().replace(/\.md$/i, "");
|
|
15538
|
-
const cells = [...link ? [cell(`[[${path6.replace(/\.md$/i, "")}|${name}]]`)] : [], ...columns.map((c) => cell(r[c]))];
|
|
15539
|
-
lines.push(`| ${cells.join(" | ")} |`);
|
|
15540
|
-
}
|
|
15541
|
-
const shown = result.rows.length;
|
|
15542
|
-
lines.push("", `<small>${result.total === shown ? `${shown} ${shown === 1 ? "row" : "rows"}` : `${shown} of ${result.total} rows`}${result.view ? ` \xB7 view ${result.view}` : ""}</small>`);
|
|
15543
|
-
return lines.join("\n");
|
|
15544
|
-
}
|
|
15545
|
-
async function renderViewBlock(app, settings, source, host) {
|
|
15546
|
-
try {
|
|
15547
|
-
const block = parseViewBlock(source);
|
|
15548
|
-
const result = await select(app, settings, { ...block.select, ...host ? { host } : {} });
|
|
15549
|
-
let labels = block.labels;
|
|
15550
|
-
if (result.view) {
|
|
15551
|
-
const ms = await loadMetastore(app, settings);
|
|
15552
|
-
const ref2 = ms ? resolveCatalogRef(ms.model, result.view, "view") : null;
|
|
15553
|
-
const own = ref2 ? ms.model.catalogs[ref2.catalog].schemas[ref2.schema].views[ref2.table].labels : void 0;
|
|
15554
|
-
labels = { ...own ?? {}, ...block.labels };
|
|
15555
|
-
}
|
|
15556
|
-
const table = () => viewMarkdown(result, { labels, rowLink: block.rowLink, ...block.select.columns ? { columns: block.select.columns } : {} });
|
|
15557
|
-
const markdown = block.chart ? [chartMarkdown(result, block.chart, labels), ...block.withTable ? ["", table()] : []].join("\n") : table();
|
|
15558
|
-
return { markdown, total: result.total, ...result.view ? { view: result.view } : {} };
|
|
15559
|
-
} catch (e) {
|
|
15560
|
-
return { markdown: `> [!warning] datanotes
|
|
15561
|
-
> ${(e instanceof Error ? e.message : String(e)).replace(/\n/g, "\n> ")}`, total: 0 };
|
|
15562
|
-
}
|
|
15563
|
-
}
|
|
15564
|
-
|
|
15565
14993
|
// src/server/rest.ts
|
|
15566
14994
|
var ROUTE_PREFIX = "/datanotes";
|
|
15567
14995
|
var BadRequest = class extends Error {
|
|
@@ -15630,9 +15058,8 @@ function errorResponse(e) {
|
|
|
15630
15058
|
return { status: 404, json: { error: message } };
|
|
15631
15059
|
}
|
|
15632
15060
|
if (e instanceof Forbidden) return { status: 403, json: { error: message } };
|
|
15633
|
-
if (e instanceof AmbiguousTableError) return { status: 409, json: { error: message, paths: e.paths } };
|
|
15634
15061
|
if (e instanceof AmbiguousCatalogRefError) return { status: 409, json: { error: message, tables: e.candidates } };
|
|
15635
|
-
if (e instanceof BadRequest || e instanceof OperationInputError || e instanceof MalformedColumnError || e instanceof MalformedTableOptionsError || e instanceof
|
|
15062
|
+
if (e instanceof BadRequest || e instanceof OperationInputError || e instanceof MalformedColumnError || e instanceof MalformedTableOptionsError || e instanceof QueryInputError || e instanceof DatabaseConfigError || e instanceof EmbeddingError || e instanceof MetastoreError) {
|
|
15636
15063
|
return { status: 400, json: { error: message } };
|
|
15637
15064
|
}
|
|
15638
15065
|
return { status: 500, json: { error: message } };
|
|
@@ -15816,16 +15243,6 @@ function createHandlers(app, getSettings, saveSettings = async () => {
|
|
|
15816
15243
|
},
|
|
15817
15244
|
"/tables/describe/": async (body) => ({ status: 200, json: await describeTable(app, s(), str(body, "table")) }),
|
|
15818
15245
|
"/ddl/": ddl,
|
|
15819
|
-
// The engine's API itself, method by method (camelCase inputs as in public-api.ts), for the
|
|
15820
|
-
// owner: `remote-api.ts` is its client.
|
|
15821
|
-
"/api/": async (body) => {
|
|
15822
|
-
const method = str(body, "method");
|
|
15823
|
-
const args = list(body, "args") ?? [];
|
|
15824
|
-
const fn = pub[method];
|
|
15825
|
-
if (typeof fn !== "function" || API_EXCLUDED.has(method)) throw new BadRequest(`"method" must be a method of the engine API`);
|
|
15826
|
-
const result = await fn.apply(pub, args);
|
|
15827
|
-
return { status: 200, json: { result: result === void 0 ? null : result } };
|
|
15828
|
-
},
|
|
15829
15246
|
"/batch/": async (body) => {
|
|
15830
15247
|
const ops = list(body, "operations");
|
|
15831
15248
|
if (!ops || ops.length === 0) throw new BadRequest(`"operations" must be a non-empty list`);
|
|
@@ -15907,22 +15324,6 @@ function createHandlers(app, getSettings, saveSettings = async () => {
|
|
|
15907
15324
|
throw new BadRequest(e instanceof Error ? e.message : String(e));
|
|
15908
15325
|
}
|
|
15909
15326
|
},
|
|
15910
|
-
"/views/render/": async (body) => {
|
|
15911
|
-
const blockText = str(body, "block", false);
|
|
15912
|
-
const view = str(body, "view", false);
|
|
15913
|
-
if (!blockText && !view) throw new BadRequest(`pass "view" (a view name) or "block" (the text of an datanotes block)`);
|
|
15914
|
-
const where = obj(body, "where", false);
|
|
15915
|
-
const source = blockText ?? `view: ${view}${where ? `
|
|
15916
|
-
where: ${JSON.stringify(where)}` : ""}`;
|
|
15917
|
-
const notePath = str(body, "note", false);
|
|
15918
|
-
let host;
|
|
15919
|
-
if (notePath) {
|
|
15920
|
-
const f = app.vault.getAbstractFileByPath(notePath);
|
|
15921
|
-
if (!f || !("extension" in f)) throw new BadRequest(`note not found: ${notePath}`);
|
|
15922
|
-
host = { path: notePath, frontmatter: app.metadataCache.getFileCache(f)?.frontmatter ?? {} };
|
|
15923
|
-
}
|
|
15924
|
-
return { status: 200, json: await renderViewBlock(app, s(), source, host) };
|
|
15925
|
-
},
|
|
15926
15327
|
"/webhooks/": async () => {
|
|
15927
15328
|
try {
|
|
15928
15329
|
return { status: 200, json: { webhooks: pub.listWebhooks() } };
|
|
@@ -16233,7 +15634,6 @@ where: ${JSON.stringify(where)}` : ""}`;
|
|
|
16233
15634
|
};
|
|
16234
15635
|
return handlers;
|
|
16235
15636
|
}
|
|
16236
|
-
var API_EXCLUDED = /* @__PURE__ */ new Set(["withActor"]);
|
|
16237
15637
|
function batchArgs(op, args) {
|
|
16238
15638
|
const out = { ...args };
|
|
16239
15639
|
const move = (from, to) => {
|
|
@@ -16597,7 +15997,7 @@ var OPERATIONS = [
|
|
|
16597
15997
|
},
|
|
16598
15998
|
"path": {
|
|
16599
15999
|
"type": "string",
|
|
16600
|
-
"description": "database.yaml#catalog.schema.table
|
|
16000
|
+
"description": "database.yaml#catalog.schema.table."
|
|
16601
16001
|
},
|
|
16602
16002
|
"description": {
|
|
16603
16003
|
"type": "string",
|
|
@@ -16612,6 +16012,7 @@ var OPERATIONS = [
|
|
|
16612
16012
|
},
|
|
16613
16013
|
"required": [
|
|
16614
16014
|
"name",
|
|
16015
|
+
"catalog",
|
|
16615
16016
|
"schema",
|
|
16616
16017
|
"qualified",
|
|
16617
16018
|
"path"
|
|
@@ -16709,7 +16110,8 @@ var OPERATIONS = [
|
|
|
16709
16110
|
"description": "Folder of the table's rows."
|
|
16710
16111
|
},
|
|
16711
16112
|
"inDatabase": {
|
|
16712
|
-
"type": "boolean"
|
|
16113
|
+
"type": "boolean",
|
|
16114
|
+
"description": "Always true."
|
|
16713
16115
|
},
|
|
16714
16116
|
"rowPath": {
|
|
16715
16117
|
"type": "string",
|
|
@@ -16723,6 +16125,7 @@ var OPERATIONS = [
|
|
|
16723
16125
|
"required": [
|
|
16724
16126
|
"folder",
|
|
16725
16127
|
"inDatabase",
|
|
16128
|
+
"rowPath",
|
|
16726
16129
|
"archive"
|
|
16727
16130
|
],
|
|
16728
16131
|
"additionalProperties": true
|
|
@@ -16809,7 +16212,7 @@ var OPERATIONS = [
|
|
|
16809
16212
|
},
|
|
16810
16213
|
"dense": {
|
|
16811
16214
|
"type": "boolean",
|
|
16812
|
-
"description": "
|
|
16215
|
+
"description": "Written on every row (with its default)."
|
|
16813
16216
|
}
|
|
16814
16217
|
},
|
|
16815
16218
|
"required": [
|
|
@@ -16828,7 +16231,7 @@ var OPERATIONS = [
|
|
|
16828
16231
|
"items": {
|
|
16829
16232
|
"type": "string"
|
|
16830
16233
|
},
|
|
16831
|
-
"description": "
|
|
16234
|
+
"description": "Table defaults without a column (written on every new row), and the table property."
|
|
16832
16235
|
},
|
|
16833
16236
|
"qualified": {
|
|
16834
16237
|
"type": "string",
|
|
@@ -23969,7 +23372,7 @@ var OPERATIONS = [
|
|
|
23969
23372
|
},
|
|
23970
23373
|
{
|
|
23971
23374
|
"name": "infer_table",
|
|
23972
|
-
"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.",
|
|
23973
23376
|
"inputSchema": {
|
|
23974
23377
|
"additionalProperties": false,
|
|
23975
23378
|
"properties": {
|
|
@@ -26768,7 +26171,7 @@ var OPERATIONS = [
|
|
|
26768
26171
|
},
|
|
26769
26172
|
{
|
|
26770
26173
|
"name": "create_view",
|
|
26771
|
-
"description": "Create (or `replace`) a view: a named select stored in database.yaml. Name it with the prefix\n`vw_` (e.g. `vw_open_tasks`): views share names and folders with tables. `query` takes the\nselect_rows parameters (table, columns, where, order_by, limit, join, group_by, aggregates, expand;\nrelative dates keep it current) and must run now. `comment` and `aliases` let agents find it;\n`labels` are column headings.
|
|
26174
|
+
"description": "Create (or `replace`) a view: a named select stored in database.yaml. Name it with the prefix\n`vw_` (e.g. `vw_open_tasks`): views share names and folders with tables. `query` takes the\nselect_rows parameters (table, columns, where, order_by, limit, join, group_by, aggregates, expand;\nrelative dates keep it current) and must run now. `comment` and `aliases` let agents find it;\n`labels` are column headings. Views are selected with select_rows(table=<view>), which may narrow them further\n(where, columns, order_by, limit). A view writes no note: it lives only in database.yaml.",
|
|
26772
26175
|
"inputSchema": {
|
|
26773
26176
|
"additionalProperties": false,
|
|
26774
26177
|
"properties": {
|
|
@@ -28623,7 +28026,6 @@ var READ_ROUTES = /* @__PURE__ */ new Set([
|
|
|
28623
28026
|
"/tables/infer/",
|
|
28624
28027
|
"/select/",
|
|
28625
28028
|
"/check/",
|
|
28626
|
-
"/views/render/",
|
|
28627
28029
|
"/changes/",
|
|
28628
28030
|
"/search/semantic/",
|
|
28629
28031
|
"/search/semantic/status/",
|
|
@@ -28902,22 +28304,6 @@ function openApiDocument(version) {
|
|
|
28902
28304
|
body: { type: "object", properties: { rebuild: { type: "boolean", default: false } } },
|
|
28903
28305
|
result: resultOf("semantic_index") ?? { type: "object" }
|
|
28904
28306
|
},
|
|
28905
|
-
{
|
|
28906
|
-
route: "/views/render/",
|
|
28907
|
-
operationId: "render_view",
|
|
28908
|
-
summary: "Render a view, or the text of a ```datanotes block, as Markdown (what a note shows).",
|
|
28909
|
-
body: {
|
|
28910
|
-
type: "object",
|
|
28911
|
-
properties: {
|
|
28912
|
-
view: { type: "string", description: "A view's name." },
|
|
28913
|
-
block: { type: "string", description: "The text of a ```datanotes block (instead of `view`)." },
|
|
28914
|
-
where: { type: "object", description: "Extra filter (with `view`)." },
|
|
28915
|
-
note: { type: "string", description: "The note the block is in: its frontmatter fills `{this.*}`." }
|
|
28916
|
-
}
|
|
28917
|
-
},
|
|
28918
|
-
result: { type: "object", additionalProperties: true },
|
|
28919
|
-
namesTable: true
|
|
28920
|
-
},
|
|
28921
28307
|
{
|
|
28922
28308
|
route: "/sessions/identify/",
|
|
28923
28309
|
operationId: "identify_session",
|
|
@@ -28939,17 +28325,6 @@ function openApiDocument(version) {
|
|
|
28939
28325
|
result: ok,
|
|
28940
28326
|
writes: true
|
|
28941
28327
|
},
|
|
28942
|
-
{
|
|
28943
|
-
route: "/api/",
|
|
28944
|
-
operationId: "engine_api",
|
|
28945
|
-
summary: "Call the engine's API method by method (owner only): what the remote engine client uses.",
|
|
28946
|
-
description: "`method` is a method of the engine's public API (camelCase, as in `src/core/public-api.ts`), `args` its positional arguments. Answers `{result}`.",
|
|
28947
|
-
body: { type: "object", required: ["method"], properties: { method: { type: "string" }, args: { type: "array", items: {} } } },
|
|
28948
|
-
bodyRequired: true,
|
|
28949
|
-
result: { type: "object", properties: { result: {} } },
|
|
28950
|
-
namesTable: true,
|
|
28951
|
-
writes: true
|
|
28952
|
-
},
|
|
28953
28328
|
{
|
|
28954
28329
|
route: "/openapi/",
|
|
28955
28330
|
operationId: "openapi_post",
|
|
@@ -30360,7 +29735,7 @@ var AppRegistry = class {
|
|
|
30360
29735
|
return null;
|
|
30361
29736
|
}
|
|
30362
29737
|
const keys = ROW_ROUTES[route];
|
|
30363
|
-
if (!keys) return a.maintain && !OWNER_ADMIN_ROUTES.has(route)
|
|
29738
|
+
if (!keys) return a.maintain && !OWNER_ADMIN_ROUTES.has(route) ? null : `app ${a.name} may not call ${route} (reserved to the owner)`;
|
|
30364
29739
|
const model = (await loadMetastore(this.deps.app, this.deps.getSettings()).catch(() => null))?.model ?? null;
|
|
30365
29740
|
for (const k of keys) {
|
|
30366
29741
|
const ref2 = b[k];
|
|
@@ -30692,22 +30067,13 @@ var EngineRuntime = class {
|
|
|
30692
30067
|
return this.httpServer !== null;
|
|
30693
30068
|
}
|
|
30694
30069
|
/**
|
|
30695
|
-
* Housekeeping of the hidden store: journal
|
|
30696
|
-
*
|
|
30070
|
+
* Housekeeping of the hidden store: journal entries and log files past their retention are
|
|
30071
|
+
* dropped.
|
|
30697
30072
|
*/
|
|
30698
30073
|
async maintainStore() {
|
|
30699
30074
|
const app = this.o.app;
|
|
30700
30075
|
const s = this.o.getSettings();
|
|
30701
30076
|
try {
|
|
30702
|
-
const imported = await importLegacyJournal(app, s);
|
|
30703
|
-
if (imported.imported > 0 || imported.errors.length > 0) {
|
|
30704
|
-
telemetry().log(imported.errors.length > 0 ? "WARN" : "INFO", "datanotes imported journal notes into the store", {
|
|
30705
|
-
imported: imported.imported,
|
|
30706
|
-
skipped: imported.skipped,
|
|
30707
|
-
removed: imported.removed,
|
|
30708
|
-
errors: imported.errors.length
|
|
30709
|
-
});
|
|
30710
|
-
}
|
|
30711
30077
|
const compacted = await compactJournal(app, s);
|
|
30712
30078
|
const logs = await pruneLogs(app, s);
|
|
30713
30079
|
if (compacted.dropped > 0 || compacted.blobsRemoved > 0 || logs.length > 0) {
|
|
@@ -30726,7 +30092,7 @@ var EngineRuntime = class {
|
|
|
30726
30092
|
const refreshMetastore = () => loadMetastore(app, s()).then((ms) => {
|
|
30727
30093
|
this.inverse.init();
|
|
30728
30094
|
return ms;
|
|
30729
|
-
}).
|
|
30095
|
+
}).catch((e) => telemetry().log("WARN", "datanotes could not read database.yaml", { error: errorText(e) }));
|
|
30730
30096
|
const logSoon = () => {
|
|
30731
30097
|
if (this.logTimer) clearTimeout(this.logTimer);
|
|
30732
30098
|
this.logTimer = setTimeout(() => {
|
|
@@ -30734,7 +30100,7 @@ var EngineRuntime = class {
|
|
|
30734
30100
|
void logExternalEdits(app, s());
|
|
30735
30101
|
}, EXTERNAL_EDIT_DELAY_MS);
|
|
30736
30102
|
};
|
|
30737
|
-
const watched = (path6) => path6 === metastorePath(s()) || path6.endsWith(".md")
|
|
30103
|
+
const watched = (path6) => path6 === metastorePath(s()) || path6.endsWith(".md");
|
|
30738
30104
|
const v = app.vault;
|
|
30739
30105
|
const mc = app.metadataCache;
|
|
30740
30106
|
this.listen(v, "modify", (f) => {
|
|
@@ -31211,8 +30577,8 @@ var DEFAULT_VAULT_CONFIG = {
|
|
|
31211
30577
|
trashOption: "system",
|
|
31212
30578
|
userIgnoreFilters: []
|
|
31213
30579
|
};
|
|
31214
|
-
function readVaultConfig(
|
|
31215
|
-
const file = import_node_path2.default.join(
|
|
30580
|
+
function readVaultConfig(root) {
|
|
30581
|
+
const file = import_node_path2.default.join(root, ".obsidian", "app.json");
|
|
31216
30582
|
let raw = {};
|
|
31217
30583
|
try {
|
|
31218
30584
|
raw = JSON.parse(import_node_fs2.default.readFileSync(file, "utf8"));
|
|
@@ -31230,7 +30596,6 @@ function readVaultConfig(root2) {
|
|
|
31230
30596
|
}
|
|
31231
30597
|
|
|
31232
30598
|
// src/node/node-app.ts
|
|
31233
|
-
var KEEP_ALL = "\0";
|
|
31234
30599
|
var NodeFile = class {
|
|
31235
30600
|
constructor(path6, stat) {
|
|
31236
30601
|
this.path = path6;
|
|
@@ -31283,19 +30648,19 @@ async function atomicWrite(abs, data) {
|
|
|
31283
30648
|
}
|
|
31284
30649
|
}
|
|
31285
30650
|
}
|
|
31286
|
-
function resolveInVault(
|
|
30651
|
+
function resolveInVault(root, rel) {
|
|
31287
30652
|
const parts = String(rel).split("/").filter((s) => s !== "" && s !== ".");
|
|
31288
30653
|
for (const s of parts) {
|
|
31289
30654
|
if (s === ".." || s.includes("\\") || s.includes("\0") || /^[A-Za-z]:/.test(s)) throw new Error(`path outside the vault refused: ${rel}`);
|
|
31290
30655
|
}
|
|
31291
|
-
const base = import_node_path3.default.resolve(
|
|
30656
|
+
const base = import_node_path3.default.resolve(root);
|
|
31292
30657
|
const abs = import_node_path3.default.resolve(base, ...parts);
|
|
31293
30658
|
if (abs !== base && !abs.startsWith(base + import_node_path3.default.sep)) throw new Error(`path outside the vault refused: ${rel}`);
|
|
31294
30659
|
return abs;
|
|
31295
30660
|
}
|
|
31296
30661
|
var NodeAdapter = class {
|
|
31297
|
-
constructor(
|
|
31298
|
-
this.root =
|
|
30662
|
+
constructor(root) {
|
|
30663
|
+
this.root = root;
|
|
31299
30664
|
}
|
|
31300
30665
|
abs(p) {
|
|
31301
30666
|
return resolveInVault(this.root, p);
|
|
@@ -31341,6 +30706,16 @@ var NodeVault = class {
|
|
|
31341
30706
|
this.files = /* @__PURE__ */ new Map();
|
|
31342
30707
|
this.folders = /* @__PURE__ */ new Map();
|
|
31343
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();
|
|
31344
30719
|
this.adapter = new NodeAdapter(basePath);
|
|
31345
30720
|
this.events.setMaxListeners(0);
|
|
31346
30721
|
this.folders.set("", this.root);
|
|
@@ -31426,12 +30801,25 @@ var NodeVault = class {
|
|
|
31426
30801
|
async refreshStat(f) {
|
|
31427
30802
|
f.stat = statsOf(await import_promises.default.stat(this.abs(f.path)));
|
|
31428
30803
|
}
|
|
31429
|
-
|
|
31430
|
-
|
|
31431
|
-
|
|
31432
|
-
|
|
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
|
+
}
|
|
31433
30820
|
syncPath(rel) {
|
|
31434
30821
|
if (rel === "" || hidden(rel)) return null;
|
|
30822
|
+
if (this.isBusy(rel)) return null;
|
|
31435
30823
|
let st = null;
|
|
31436
30824
|
try {
|
|
31437
30825
|
st = import_node_fs3.default.statSync(this.abs(rel));
|
|
@@ -31523,8 +30911,10 @@ var NodeVault = class {
|
|
|
31523
30911
|
}
|
|
31524
30912
|
/** Write a file's text (internal: also used by the file manager). */
|
|
31525
30913
|
async writeText(file, data) {
|
|
31526
|
-
await
|
|
31527
|
-
|
|
30914
|
+
await this.guarded([file.path], async () => {
|
|
30915
|
+
await atomicWrite(this.abs(file.path), data);
|
|
30916
|
+
await this.refreshStat(file);
|
|
30917
|
+
});
|
|
31528
30918
|
this.emit("modify", file);
|
|
31529
30919
|
}
|
|
31530
30920
|
async process(file, fn) {
|
|
@@ -31537,9 +30927,11 @@ var NodeVault = class {
|
|
|
31537
30927
|
async create(p, data) {
|
|
31538
30928
|
const rel = p.replace(/^\/+/, "");
|
|
31539
30929
|
if (import_node_fs3.default.existsSync(this.abs(rel))) throw new Error(`File already exists: ${rel}`);
|
|
31540
|
-
await this.
|
|
31541
|
-
|
|
31542
|
-
|
|
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
|
+
});
|
|
31543
30935
|
this.emit("create", f);
|
|
31544
30936
|
return f;
|
|
31545
30937
|
}
|
|
@@ -31556,6 +30948,9 @@ var NodeVault = class {
|
|
|
31556
30948
|
return this.folders.get(rel);
|
|
31557
30949
|
}
|
|
31558
30950
|
async delete(file, force) {
|
|
30951
|
+
return this.guarded([file.path], () => this.deleteNow(file, force));
|
|
30952
|
+
}
|
|
30953
|
+
async deleteNow(file, force) {
|
|
31559
30954
|
const rel = file.path;
|
|
31560
30955
|
if (this.folders.has(rel)) {
|
|
31561
30956
|
const gone = [...this.files.values()].filter((f2) => f2.path.startsWith(`${rel}/`));
|
|
@@ -31570,8 +30965,11 @@ var NodeVault = class {
|
|
|
31570
30965
|
if (f) this.emit("delete", f);
|
|
31571
30966
|
}
|
|
31572
30967
|
/** Move to `.trash/` (Obsidian's local trash; also for "system", which Node cannot reach) or delete. */
|
|
31573
|
-
async trash(file,
|
|
30968
|
+
async trash(file, system) {
|
|
31574
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) {
|
|
31575
30973
|
const target = await this.freeName(`.trash/${basename2(file.path)}`);
|
|
31576
30974
|
const isFolder = this.folders.has(file.path);
|
|
31577
30975
|
const gone = isFolder ? [...this.files.values()].filter((f) => f.path.startsWith(`${file.path}/`)) : [];
|
|
@@ -31603,6 +31001,9 @@ var NodeVault = class {
|
|
|
31603
31001
|
const from = item.path;
|
|
31604
31002
|
const to = newPath.replace(/^\/+|\/+$/g, "");
|
|
31605
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) {
|
|
31606
31007
|
if (import_node_fs3.default.existsSync(this.abs(to)) && from.toLowerCase() !== to.toLowerCase()) throw new Error(`Destination exists: ${to}`);
|
|
31607
31008
|
await this.ensureFolder(dirname(to));
|
|
31608
31009
|
await import_promises.default.rename(this.abs(from), this.abs(to));
|
|
@@ -31686,7 +31087,7 @@ var NodeMetadataCache = class {
|
|
|
31686
31087
|
let frontmatter2;
|
|
31687
31088
|
let body = text2;
|
|
31688
31089
|
try {
|
|
31689
|
-
const r =
|
|
31090
|
+
const r = readOrderedFromMarkdown(text2);
|
|
31690
31091
|
body = r.body;
|
|
31691
31092
|
if (r.ordered.length > 0) frontmatter2 = Object.fromEntries(r.ordered);
|
|
31692
31093
|
} catch {
|
|
@@ -31835,7 +31236,7 @@ var NodeFileManager = class {
|
|
|
31835
31236
|
const f = this.vault.getAbstractFileByPath(file.path);
|
|
31836
31237
|
if (!f) throw new Error(`file not found: ${file.path}`);
|
|
31837
31238
|
const text2 = await this.vault.read(f);
|
|
31838
|
-
const { ordered, body } =
|
|
31239
|
+
const { ordered, body } = readOrderedFromMarkdown(text2);
|
|
31839
31240
|
const fm = Object.fromEntries(ordered);
|
|
31840
31241
|
const before = JSON.stringify(fm);
|
|
31841
31242
|
fn(fm);
|
|
@@ -31847,8 +31248,8 @@ var NodeFileManager = class {
|
|
|
31847
31248
|
return this.vault.trash(file, true);
|
|
31848
31249
|
}
|
|
31849
31250
|
};
|
|
31850
|
-
function openNodeVault(
|
|
31851
|
-
const vault = new NodeVault(import_node_path3.default.resolve(
|
|
31251
|
+
function openNodeVault(root, config) {
|
|
31252
|
+
const vault = new NodeVault(import_node_path3.default.resolve(root), { ...readVaultConfig(root), ...config ?? {} });
|
|
31852
31253
|
vault.scanSync();
|
|
31853
31254
|
const metadataCache = new NodeMetadataCache(vault);
|
|
31854
31255
|
metadataCache.indexAll();
|
|
@@ -31923,7 +31324,7 @@ var NodeWatcher = class {
|
|
|
31923
31324
|
};
|
|
31924
31325
|
|
|
31925
31326
|
// src/node/service.ts
|
|
31926
|
-
var VERSION = true ? "
|
|
31327
|
+
var VERSION = true ? "2.0.1" : "dev";
|
|
31927
31328
|
function parseArgs(argv, env = process.env) {
|
|
31928
31329
|
const get = (name) => {
|
|
31929
31330
|
const i = argv.indexOf(`--${name}`);
|
|
@@ -32098,7 +31499,7 @@ Docs: https://datanotes.dev`;
|
|
|
32098
31499
|
}
|
|
32099
31500
|
|
|
32100
31501
|
// src/node/cli.ts
|
|
32101
|
-
var VERSION2 = true ? "
|
|
31502
|
+
var VERSION2 = true ? "2.0.1" : "dev";
|
|
32102
31503
|
async function main2(argv = process.argv.slice(2)) {
|
|
32103
31504
|
const [command, ...rest] = argv;
|
|
32104
31505
|
if (command === "--version" || command === "-v" || command === "version") return void process.stdout.write(`${VERSION2}
|