@wizzlethorpe/vaults 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +125 -0
  3. package/dist/api.js +42 -0
  4. package/dist/api.js.map +1 -0
  5. package/dist/auth.js +62 -0
  6. package/dist/auth.js.map +1 -0
  7. package/dist/build.js +758 -0
  8. package/dist/build.js.map +1 -0
  9. package/dist/commands/build.js +23 -0
  10. package/dist/commands/build.js.map +1 -0
  11. package/dist/commands/init.js +67 -0
  12. package/dist/commands/init.js.map +1 -0
  13. package/dist/commands/password.js +74 -0
  14. package/dist/commands/password.js.map +1 -0
  15. package/dist/commands/preview.js +60 -0
  16. package/dist/commands/preview.js.map +1 -0
  17. package/dist/commands/push.js +191 -0
  18. package/dist/commands/push.js.map +1 -0
  19. package/dist/commands/role.js +122 -0
  20. package/dist/commands/role.js.map +1 -0
  21. package/dist/config.js +79 -0
  22. package/dist/config.js.map +1 -0
  23. package/dist/favicon.js +91 -0
  24. package/dist/favicon.js.map +1 -0
  25. package/dist/images.js +47 -0
  26. package/dist/images.js.map +1 -0
  27. package/dist/index.js +154 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/obsidian.js +47 -0
  30. package/dist/obsidian.js.map +1 -0
  31. package/dist/render/auth-template.js +677 -0
  32. package/dist/render/auth-template.js.map +1 -0
  33. package/dist/render/callouts.js +65 -0
  34. package/dist/render/callouts.js.map +1 -0
  35. package/dist/render/embed.js +190 -0
  36. package/dist/render/embed.js.map +1 -0
  37. package/dist/render/layout.js +414 -0
  38. package/dist/render/layout.js.map +1 -0
  39. package/dist/render/mcp-template.js +239 -0
  40. package/dist/render/mcp-template.js.map +1 -0
  41. package/dist/render/pipeline.js +59 -0
  42. package/dist/render/pipeline.js.map +1 -0
  43. package/dist/render/preview.js +81 -0
  44. package/dist/render/preview.js.map +1 -0
  45. package/dist/render/slug.js +12 -0
  46. package/dist/render/slug.js.map +1 -0
  47. package/dist/render/styles.js +383 -0
  48. package/dist/render/styles.js.map +1 -0
  49. package/dist/render/types.js +2 -0
  50. package/dist/render/types.js.map +1 -0
  51. package/dist/render/wikilink.js +55 -0
  52. package/dist/render/wikilink.js.map +1 -0
  53. package/dist/scan.js +45 -0
  54. package/dist/scan.js.map +1 -0
  55. package/dist/settings.js +157 -0
  56. package/dist/settings.js.map +1 -0
  57. package/dist/util.js +60 -0
  58. package/dist/util.js.map +1 -0
  59. package/package.json +64 -0
@@ -0,0 +1,122 @@
1
+ import { createInterface } from "node:readline/promises";
2
+ import { stdin, stdout } from "node:process";
3
+ import { hashPassword } from "../auth.js";
4
+ import { loadConfig, saveConfig } from "../config.js";
5
+ export async function roleAdd(name, vaultPath) {
6
+ if (!/^[a-z][a-z0-9_-]*$/i.test(name)) {
7
+ throw new Error(`Invalid role name '${name}'. Use letters, digits, '_' or '-' (must start with a letter).`);
8
+ }
9
+ const cfg = await loadConfig(vaultPath, {});
10
+ if (cfg.roles.includes(name))
11
+ throw new Error(`Role '${name}' already exists.`);
12
+ // First role added (or first ever role) is the default — no password.
13
+ // Subsequent roles need a password to gate access.
14
+ const isDefault = cfg.roles.length === 0;
15
+ cfg.roles.push(name);
16
+ if (!isDefault) {
17
+ console.log(`Adding role '${name}'. Set a password to gate access:`);
18
+ const pw = await readPassword();
19
+ cfg.rolePasswords[name] = await hashPassword(pw);
20
+ }
21
+ await saveConfig(vaultPath, cfg);
22
+ console.log(`Added role '${name}'${isDefault ? " (default)" : ""}.`);
23
+ console.log(` Mark pages with 'role: ${name}' frontmatter or callouts with '> [!${name}]' to gate them.`);
24
+ }
25
+ export async function roleRemove(name, vaultPath) {
26
+ const cfg = await loadConfig(vaultPath, {});
27
+ if (!cfg.roles.includes(name)) {
28
+ throw new Error(`Role '${name}' is not configured (${cfg.roles.join(", ") || "empty"}).`);
29
+ }
30
+ if (cfg.roles[0] === name && cfg.roles.length > 1) {
31
+ throw new Error(`Can't remove '${name}' — it's the default role. Remove the other roles first.`);
32
+ }
33
+ cfg.roles = cfg.roles.filter((r) => r !== name);
34
+ delete cfg.rolePasswords[name];
35
+ await saveConfig(vaultPath, cfg);
36
+ console.log(`Removed role '${name}'.`);
37
+ console.log(` Pages tagged 'role: ${name}' will fall back to the default role on next build.`);
38
+ }
39
+ export async function rolePromote(name, vaultPath) {
40
+ await reorderRole(name, vaultPath, +1);
41
+ }
42
+ export async function roleDemote(name, vaultPath) {
43
+ await reorderRole(name, vaultPath, -1);
44
+ }
45
+ async function reorderRole(name, vaultPath, delta) {
46
+ const cfg = await loadConfig(vaultPath, {});
47
+ const roles = cfg.roles;
48
+ const i = roles.indexOf(name);
49
+ if (i === -1)
50
+ throw new Error(`Role '${name}' is not configured (${roles.join(", ") || "empty"}).`);
51
+ if (i === 0)
52
+ throw new Error(`Can't reorder '${name}' — it's the default role.`);
53
+ const j = i + delta;
54
+ if (j < 1 || j >= roles.length) {
55
+ throw new Error(`'${name}' is already at the ${delta > 0 ? "highest" : "lowest non-default"} rank.`);
56
+ }
57
+ [roles[i], roles[j]] = [roles[j], roles[i]];
58
+ await saveConfig(vaultPath, cfg);
59
+ const action = delta > 0 ? "Promoted" : "Demoted";
60
+ console.log(`${action} '${name}' to rank ${j} of ${roles.length - 1}.`);
61
+ console.log(` New order: ${roles.join(" < ")}`);
62
+ }
63
+ export async function roleList(vaultPath) {
64
+ const cfg = await loadConfig(vaultPath, {});
65
+ if (cfg.roles.length === 0) {
66
+ console.log("No roles configured. Run `vaults role add <name>` to add one.");
67
+ return;
68
+ }
69
+ console.log("Roles (lowest → highest):");
70
+ cfg.roles.forEach((r, i) => {
71
+ const isDefault = i === 0;
72
+ const hasPw = cfg.rolePasswords[r] != null;
73
+ const tag = isDefault ? " (default, public)" : hasPw ? "" : " (no password set!)";
74
+ console.log(` ${r}${tag}`);
75
+ });
76
+ }
77
+ async function readPassword() {
78
+ const isTty = !!stdin.isTTY;
79
+ if (!isTty) {
80
+ const chunks = [];
81
+ for await (const chunk of stdin)
82
+ chunks.push(chunk);
83
+ const lines = Buffer.concat(chunks).toString("utf8").split(/\r?\n/);
84
+ const pw = lines[0] ?? "";
85
+ const confirm = lines[1] ?? "";
86
+ if (!pw)
87
+ throw new Error("Empty password.");
88
+ if (pw !== confirm)
89
+ throw new Error("Passwords don't match.");
90
+ return pw;
91
+ }
92
+ const rl = createInterface({ input: stdin, output: stdout });
93
+ try {
94
+ const pw = await maskedRead(rl, "Password: ");
95
+ if (!pw)
96
+ throw new Error("Empty password.");
97
+ const confirm = await maskedRead(rl, "Confirm: ");
98
+ if (pw !== confirm)
99
+ throw new Error("Passwords don't match.");
100
+ return pw;
101
+ }
102
+ finally {
103
+ rl.close();
104
+ }
105
+ }
106
+ async function maskedRead(rl, prompt) {
107
+ stdout.write(prompt);
108
+ const realWrite = stdout.write.bind(stdout);
109
+ let muted = true;
110
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
111
+ stdout.write = ((chunk, ...rest) => muted ? true : realWrite(chunk, ...rest));
112
+ try {
113
+ return await rl.question("");
114
+ }
115
+ finally {
116
+ muted = false;
117
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
118
+ stdout.write = realWrite;
119
+ stdout.write("\n");
120
+ }
121
+ }
122
+ //# sourceMappingURL=role.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"role.js","sourceRoot":"","sources":["../../src/commands/role.ts"],"names":[],"mappings":"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"}
package/dist/config.js ADDED
@@ -0,0 +1,79 @@
1
+ import { readFile, writeFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ const DEFAULT_CONFIG = {
4
+ imageQuality: 85,
5
+ maxFileBytes: 25 * 1024 * 1024,
6
+ roles: ["public"],
7
+ authType: "password",
8
+ rolePasswords: {},
9
+ };
10
+ const CONFIG_FILE = ".vaultrc.json";
11
+ export async function loadConfig(vaultPath, overrides) {
12
+ const fileConfig = await readFileConfig(vaultPath);
13
+ const envConfig = readEnvConfig();
14
+ const merged = {
15
+ ...DEFAULT_CONFIG,
16
+ ...fileConfig,
17
+ ...envConfig,
18
+ ...overrides,
19
+ };
20
+ // Deep-clone the mutable fields so callers can mutate (push to roles,
21
+ // assign to rolePasswords) without mutating DEFAULT_CONFIG by reference.
22
+ return {
23
+ ...merged,
24
+ roles: [...merged.roles],
25
+ rolePasswords: { ...merged.rolePasswords },
26
+ };
27
+ }
28
+ /**
29
+ * Persist the full config back to .vaultrc.json. The CLI uses this whenever
30
+ * it mutates auth state (role add/remove/promote/demote, password reset,
31
+ * sessionSecret generation, projectName saved on first push).
32
+ */
33
+ export async function saveConfig(vaultPath, cfg) {
34
+ // Strip default-equal fields so the on-disk file stays minimal.
35
+ const out = {};
36
+ for (const k of Object.keys(cfg)) {
37
+ const v = cfg[k];
38
+ if (deepEqual(v, DEFAULT_CONFIG[k]))
39
+ continue;
40
+ out[k] = v;
41
+ }
42
+ await writeFile(join(vaultPath, CONFIG_FILE), JSON.stringify(out, null, 2) + "\n");
43
+ }
44
+ export async function saveSessionSecret(vaultPath, secret) {
45
+ const cfg = await loadConfig(vaultPath, {});
46
+ cfg.sessionSecret = secret;
47
+ await saveConfig(vaultPath, cfg);
48
+ }
49
+ async function readFileConfig(vaultPath) {
50
+ try {
51
+ const raw = await readFile(join(vaultPath, CONFIG_FILE), "utf8");
52
+ return JSON.parse(raw);
53
+ }
54
+ catch {
55
+ return {};
56
+ }
57
+ }
58
+ function readEnvConfig() {
59
+ const out = {};
60
+ if (process.env.VAULT_PROJECT_NAME)
61
+ out.projectName = process.env.VAULT_PROJECT_NAME;
62
+ return out;
63
+ }
64
+ function deepEqual(a, b) {
65
+ if (a === b)
66
+ return true;
67
+ if (Array.isArray(a) && Array.isArray(b)) {
68
+ return a.length === b.length && a.every((x, i) => deepEqual(x, b[i]));
69
+ }
70
+ if (a && b && typeof a === "object" && typeof b === "object") {
71
+ const ak = Object.keys(a);
72
+ const bk = Object.keys(b);
73
+ if (ak.length !== bk.length)
74
+ return false;
75
+ return ak.every((k) => deepEqual(a[k], b[k]));
76
+ }
77
+ return false;
78
+ }
79
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,91 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { join, isAbsolute } from "node:path";
3
+ import sharp from "sharp";
4
+ const ICON_SIZE = 32;
5
+ /**
6
+ * Render the favicon for a vault to an ICO buffer. If the user pointed
7
+ * `settings.favicon` at a real file, we resize that image; otherwise we
8
+ * generate a default — a rounded square in the vault's accent colour with
9
+ * a single uppercase letter centred on it.
10
+ */
11
+ export async function buildFavicon(opts) {
12
+ const png = opts.faviconPath
13
+ ? await renderUserImage(opts.vaultPath, opts.faviconPath)
14
+ : await renderDefaultIcon(opts.letter, opts.accentColor);
15
+ return wrapPngAsIco(png, ICON_SIZE);
16
+ }
17
+ async function renderUserImage(vaultPath, faviconPath) {
18
+ const abs = isAbsolute(faviconPath) ? faviconPath : join(vaultPath, faviconPath);
19
+ const source = await readFile(abs);
20
+ return sharp(source).resize(ICON_SIZE, ICON_SIZE, { fit: "cover" }).png().toBuffer();
21
+ }
22
+ async function renderDefaultIcon(letter, accent) {
23
+ const fg = bestForeground(accent);
24
+ // Round-cornered square with a single letter centred. Embedded as an
25
+ // SVG so sharp rasterises it cleanly at the target size.
26
+ const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
27
+ <rect width="32" height="32" rx="5" fill="${escAttr(accent)}"/>
28
+ <text x="16" y="22" font-family="Iowan Old Style, Palatino Linotype, Georgia, serif"
29
+ font-size="22" font-weight="700" text-anchor="middle"
30
+ fill="${escAttr(fg)}">${escText(letter)}</text>
31
+ </svg>`;
32
+ return sharp(Buffer.from(svg)).resize(ICON_SIZE, ICON_SIZE).png().toBuffer();
33
+ }
34
+ /**
35
+ * Wrap a PNG buffer in an ICO container. Modern browsers accept PNG-in-ICO
36
+ * for any size; the dirent's width/height bytes are advisory.
37
+ *
38
+ * Layout:
39
+ * ICONDIR (6 bytes) reserved=0, type=1, count=1
40
+ * ICONDIRENTRY (16 bytes) width, height, ..., size, offset
41
+ * <PNG bytes>
42
+ */
43
+ function wrapPngAsIco(png, size) {
44
+ const HEADER = 6 + 16;
45
+ const buf = Buffer.alloc(HEADER + png.length);
46
+ buf.writeUInt16LE(0, 0); // reserved
47
+ buf.writeUInt16LE(1, 2); // type: icon
48
+ buf.writeUInt16LE(1, 4); // image count
49
+ buf.writeUInt8(size === 256 ? 0 : size, 6); // width (0 = 256)
50
+ buf.writeUInt8(size === 256 ? 0 : size, 7); // height
51
+ buf.writeUInt8(0, 8); // palette
52
+ buf.writeUInt8(0, 9); // reserved
53
+ buf.writeUInt16LE(1, 10); // colour planes
54
+ buf.writeUInt16LE(32, 12); // bits per pixel
55
+ buf.writeUInt32LE(png.length, 14); // bytes of image data
56
+ buf.writeUInt32LE(HEADER, 18); // offset to image data
57
+ png.copy(buf, HEADER);
58
+ return buf;
59
+ }
60
+ /**
61
+ * Pick black or white as the letter colour against `accent` so the icon
62
+ * stays readable for whatever colour the user picked. Uses W3C relative
63
+ * luminance.
64
+ */
65
+ function bestForeground(accent) {
66
+ const rgb = parseColor(accent);
67
+ if (!rgb)
68
+ return "#ffffff";
69
+ const lin = (c) => {
70
+ const s = c / 255;
71
+ return s <= 0.03928 ? s / 12.92 : Math.pow((s + 0.055) / 1.055, 2.4);
72
+ };
73
+ const L = 0.2126 * lin(rgb[0]) + 0.7152 * lin(rgb[1]) + 0.0722 * lin(rgb[2]);
74
+ return L > 0.5 ? "#1d1a17" : "#ffffff";
75
+ }
76
+ function parseColor(s) {
77
+ const hex = /^#?([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(s.trim());
78
+ if (!hex)
79
+ return null;
80
+ let h = hex[1];
81
+ if (h.length === 3)
82
+ h = h.split("").map((c) => c + c).join("");
83
+ return [parseInt(h.slice(0, 2), 16), parseInt(h.slice(2, 4), 16), parseInt(h.slice(4, 6), 16)];
84
+ }
85
+ function escAttr(s) {
86
+ return s.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;");
87
+ }
88
+ function escText(s) {
89
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
90
+ }
91
+ //# sourceMappingURL=favicon.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"favicon.js","sourceRoot":"","sources":["../src/favicon.ts"],"names":[],"mappings":"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"}
package/dist/images.js ADDED
@@ -0,0 +1,47 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import sharp from "sharp";
3
+ export const COMPRESSIBLE_EXT_RE = /\.(png|jpe?g|webp|gif|tiff?|avif)$/i;
4
+ /**
5
+ * Reads an image from disk, converts to webp at the given quality, and returns
6
+ * the new buffer + path. Animated GIFs are preserved as animated webp.
7
+ */
8
+ export async function compressImage(absolutePath, vaultRelPath, quality) {
9
+ const input = await readFile(absolutePath);
10
+ const isAnimated = /\.gif$/i.test(absolutePath);
11
+ const body = await sharp(input, { animated: isAnimated })
12
+ .webp({ quality })
13
+ .toBuffer();
14
+ const outputPath = vaultRelPath.replace(COMPRESSIBLE_EXT_RE, ".webp");
15
+ return { body, contentType: "image/webp", outputPath };
16
+ }
17
+ /**
18
+ * Best-effort content-type lookup by extension. Falls back to octet-stream.
19
+ */
20
+ export function contentTypeFor(path) {
21
+ const ext = path.split(".").pop()?.toLowerCase() ?? "";
22
+ const map = {
23
+ md: "text/markdown; charset=utf-8",
24
+ markdown: "text/markdown; charset=utf-8",
25
+ txt: "text/plain; charset=utf-8",
26
+ json: "application/json",
27
+ yaml: "application/yaml",
28
+ yml: "application/yaml",
29
+ html: "text/html; charset=utf-8",
30
+ css: "text/css; charset=utf-8",
31
+ png: "image/png",
32
+ jpg: "image/jpeg",
33
+ jpeg: "image/jpeg",
34
+ webp: "image/webp",
35
+ gif: "image/gif",
36
+ svg: "image/svg+xml",
37
+ avif: "image/avif",
38
+ mp3: "audio/mpeg",
39
+ wav: "audio/wav",
40
+ ogg: "audio/ogg",
41
+ m4a: "audio/mp4",
42
+ mp4: "video/mp4",
43
+ pdf: "application/pdf",
44
+ };
45
+ return map[ext] ?? "application/octet-stream";
46
+ }
47
+ //# sourceMappingURL=images.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"images.js","sourceRoot":"","sources":["../src/images.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,CAAC,MAAM,mBAAmB,GAAG,qCAAqC,CAAC;AASzE;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,YAAoB,EAAE,YAAoB,EAAE,OAAe;IAC7F,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,YAAY,CAAC,CAAC;IAC3C,MAAM,UAAU,GAAG,SAAS,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAChD,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,KAAK,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;SACtD,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC;SACjB,QAAQ,EAAE,CAAC;IAEd,MAAM,UAAU,GAAG,YAAY,CAAC,OAAO,CAAC,mBAAmB,EAAE,OAAO,CAAC,CAAC;IACtE,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,YAAY,EAAE,UAAU,EAAE,CAAC;AACzD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;IACvD,MAAM,GAAG,GAA2B;QAClC,EAAE,EAAE,8BAA8B;QAClC,QAAQ,EAAE,8BAA8B;QACxC,GAAG,EAAE,2BAA2B;QAChC,IAAI,EAAE,kBAAkB;QACxB,IAAI,EAAE,kBAAkB;QACxB,GAAG,EAAE,kBAAkB;QACvB,IAAI,EAAE,0BAA0B;QAChC,GAAG,EAAE,yBAAyB;QAC9B,GAAG,EAAE,WAAW;QAChB,GAAG,EAAE,YAAY;QACjB,IAAI,EAAE,YAAY;QAClB,IAAI,EAAE,YAAY;QAClB,GAAG,EAAE,WAAW;QAChB,GAAG,EAAE,eAAe;QACpB,IAAI,EAAE,YAAY;QAClB,GAAG,EAAE,YAAY;QACjB,GAAG,EAAE,WAAW;QAChB,GAAG,EAAE,WAAW;QAChB,GAAG,EAAE,WAAW;QAChB,GAAG,EAAE,WAAW;QAChB,GAAG,EAAE,iBAAiB;KACvB,CAAC;IACF,OAAO,GAAG,CAAC,GAAG,CAAC,IAAI,0BAA0B,CAAC;AAChD,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,154 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from "commander";
3
+ import { push } from "./commands/push.js";
4
+ import { build } from "./commands/build.js";
5
+ import { preview } from "./commands/preview.js";
6
+ import { init } from "./commands/init.js";
7
+ import { password } from "./commands/password.js";
8
+ import { roleAdd, roleDemote, roleList, rolePromote, roleRemove } from "./commands/role.js";
9
+ const program = new Command();
10
+ // Default vault path: honour $VAULT_PATH first so users can drop
11
+ // `export VAULT_PATH=~/Documents/MyVault` in their shell rc and not pass it
12
+ // to every command. Falls back to cwd.
13
+ const VAULT_PATH_DEFAULT = process.env.VAULT_PATH ?? process.cwd();
14
+ program
15
+ .name("vaults")
16
+ .description("Sync an Obsidian vault to a Cloudflare-hosted wiki")
17
+ .version("0.1.0");
18
+ const role = program
19
+ .command("role")
20
+ .description("Manage roles (access tiers) in the vault");
21
+ role
22
+ .command("add")
23
+ .description("Add a role and (for non-default roles) set its password")
24
+ .argument("<name>", "Role name")
25
+ .argument("[vault-path]", "Path to the Obsidian vault", VAULT_PATH_DEFAULT)
26
+ .action(async (name, vaultPath) => {
27
+ try {
28
+ await roleAdd(name, vaultPath);
29
+ }
30
+ catch (err) {
31
+ console.error(err instanceof Error ? err.message : err);
32
+ process.exit(1);
33
+ }
34
+ });
35
+ role
36
+ .command("remove")
37
+ .description("Remove a role and its password")
38
+ .argument("<name>", "Role name")
39
+ .argument("[vault-path]", "Path to the Obsidian vault", VAULT_PATH_DEFAULT)
40
+ .action(async (name, vaultPath) => {
41
+ try {
42
+ await roleRemove(name, vaultPath);
43
+ }
44
+ catch (err) {
45
+ console.error(err instanceof Error ? err.message : err);
46
+ process.exit(1);
47
+ }
48
+ });
49
+ role
50
+ .command("list")
51
+ .description("Show roles configured for this vault")
52
+ .argument("[vault-path]", "Path to the Obsidian vault", VAULT_PATH_DEFAULT)
53
+ .action(async (vaultPath) => {
54
+ try {
55
+ await roleList(vaultPath);
56
+ }
57
+ catch (err) {
58
+ console.error(err instanceof Error ? err.message : err);
59
+ process.exit(1);
60
+ }
61
+ });
62
+ role
63
+ .command("promote")
64
+ .description("Increase a role's rank by one (move toward the highest tier)")
65
+ .argument("<name>", "Role name")
66
+ .argument("[vault-path]", "Path to the Obsidian vault", VAULT_PATH_DEFAULT)
67
+ .action(async (name, vaultPath) => {
68
+ try {
69
+ await rolePromote(name, vaultPath);
70
+ }
71
+ catch (err) {
72
+ console.error(err instanceof Error ? err.message : err);
73
+ process.exit(1);
74
+ }
75
+ });
76
+ role
77
+ .command("demote")
78
+ .description("Decrease a role's rank by one (move toward the default)")
79
+ .argument("<name>", "Role name")
80
+ .argument("[vault-path]", "Path to the Obsidian vault", VAULT_PATH_DEFAULT)
81
+ .action(async (name, vaultPath) => {
82
+ try {
83
+ await roleDemote(name, vaultPath);
84
+ }
85
+ catch (err) {
86
+ console.error(err instanceof Error ? err.message : err);
87
+ process.exit(1);
88
+ }
89
+ });
90
+ program
91
+ .command("password")
92
+ .description("Reset the password for an existing role")
93
+ .argument("<role>", "Role name (must already exist)")
94
+ .argument("[vault-path]", "Path to the Obsidian vault", VAULT_PATH_DEFAULT)
95
+ .action(async (role, vaultPath) => {
96
+ try {
97
+ await password(vaultPath, role, {});
98
+ }
99
+ catch (err) {
100
+ console.error(err instanceof Error ? err.message : err);
101
+ process.exit(1);
102
+ }
103
+ });
104
+ program
105
+ .command("init")
106
+ .description("Initialise a vault with a settings.md file")
107
+ .argument("[vault-path]", "Path to the Obsidian vault", VAULT_PATH_DEFAULT)
108
+ .option("-f, --force", "Overwrite an existing settings.md")
109
+ .action(wrap(init));
110
+ program
111
+ .command("build")
112
+ .description("Render the vault to a local output directory")
113
+ .argument("[vault-path]", "Path to the Obsidian vault", VAULT_PATH_DEFAULT)
114
+ .option("-o, --output <dir>", "Output directory (default: <vault>/.vault-cache/rendered)")
115
+ .option("-q, --image-quality <n>", "WebP image quality (0 = no compression)", (v) => parseInt(v, 10))
116
+ .option("-n, --vault-name <name>", "Display name for the vault", "Vault")
117
+ .option("--all-warnings", "Print every page with warnings instead of truncating at 20")
118
+ .action(wrap(build));
119
+ program
120
+ .command("preview")
121
+ .description("Render the vault and serve it locally via `wrangler pages dev` (Functions run)")
122
+ .argument("[vault-path]", "Path to the Obsidian vault", VAULT_PATH_DEFAULT)
123
+ .option("-o, --output <dir>", "Output directory (default: <vault>/.vault-cache/rendered)")
124
+ .option("-p, --port <n>", "Port for the preview server", (v) => parseInt(v, 10), 4173)
125
+ .option("-q, --image-quality <n>", "WebP image quality (0 = no compression)", (v) => parseInt(v, 10))
126
+ .option("-n, --vault-name <name>", "Display name for the vault", "Vault")
127
+ .action(wrap(preview));
128
+ program
129
+ .command("push")
130
+ .description("Render and deploy the vault to Cloudflare Pages")
131
+ .argument("[vault-path]", "Path to the Obsidian vault", VAULT_PATH_DEFAULT)
132
+ .option("-p, --project-name <name>", "Cloudflare Pages project name")
133
+ .option("-q, --image-quality <n>", "WebP image quality (0 = no compression)", (v) => parseInt(v, 10))
134
+ .option("-n, --vault-name <name>", "Display name for the vault", "Vault")
135
+ .option("--dry-run", "Render without deploying")
136
+ .option("--rotate-secret", "Generate a fresh SESSION_SECRET, invalidating all issued tokens")
137
+ .option("--all-warnings", "Print every page with warnings instead of truncating at 20")
138
+ .action(wrap(push));
139
+ program.parseAsync().catch((err) => {
140
+ console.error(err);
141
+ process.exit(1);
142
+ });
143
+ function wrap(fn) {
144
+ return async (...args) => {
145
+ try {
146
+ await fn(...args);
147
+ }
148
+ catch (err) {
149
+ console.error(err instanceof Error ? err.message : err);
150
+ process.exit(1);
151
+ }
152
+ };
153
+ }
154
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ import { readdir, readFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ /**
4
+ * Load Obsidian CSS snippets from <vault>/.obsidian/snippets/*.css.
5
+ *
6
+ * If .obsidian/appearance.json exists, only snippets listed in
7
+ * `enabledCssSnippets` are included. If it's missing, all snippets are
8
+ * included — matches Obsidian's behaviour when the user hasn't configured
9
+ * anything, and gives users a "drop a CSS file in and it works" workflow.
10
+ *
11
+ * Returns a single concatenated CSS string, or empty if no snippets.
12
+ */
13
+ export async function loadObsidianSnippets(vaultPath) {
14
+ const snippetsDir = join(vaultPath, ".obsidian", "snippets");
15
+ let files;
16
+ try {
17
+ files = (await readdir(snippetsDir)).filter((f) => f.endsWith(".css"));
18
+ }
19
+ catch {
20
+ return "";
21
+ }
22
+ if (files.length === 0)
23
+ return "";
24
+ const enabled = await readEnabledList(vaultPath);
25
+ const include = enabled
26
+ ? files.filter((f) => enabled.includes(f.replace(/\.css$/i, "")))
27
+ : files;
28
+ if (include.length === 0)
29
+ return "";
30
+ const parts = [];
31
+ for (const file of include.sort()) {
32
+ const body = await readFile(join(snippetsDir, file), "utf8");
33
+ parts.push(`/* === ${file} === */\n${body}\n`);
34
+ }
35
+ return parts.join("\n");
36
+ }
37
+ async function readEnabledList(vaultPath) {
38
+ try {
39
+ const raw = await readFile(join(vaultPath, ".obsidian", "appearance.json"), "utf8");
40
+ const parsed = JSON.parse(raw);
41
+ return Array.isArray(parsed.enabledCssSnippets) ? parsed.enabledCssSnippets : null;
42
+ }
43
+ catch {
44
+ return null;
45
+ }
46
+ }
47
+ //# sourceMappingURL=obsidian.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"obsidian.js","sourceRoot":"","sources":["../src/obsidian.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAMjC;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,SAAiB;IAC1D,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,EAAE,WAAW,EAAE,UAAU,CAAC,CAAC;IAC7D,IAAI,KAAe,CAAC;IACpB,IAAI,CAAC;QACH,KAAK,GAAG,CAAC,MAAM,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;IACzE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAElC,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC,SAAS,CAAC,CAAC;IACjD,MAAM,OAAO,GAAG,OAAO;QACrB,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,CAAC;QACjE,CAAC,CAAC,KAAK,CAAC;IAEV,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAEpC,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC;QAClC,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC;QAC7D,KAAK,CAAC,IAAI,CAAC,UAAU,IAAI,YAAY,IAAI,IAAI,CAAC,CAAC;IACjD,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,KAAK,UAAU,eAAe,CAAC,SAAiB;IAC9C,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,SAAS,EAAE,WAAW,EAAE,iBAAiB,CAAC,EAAE,MAAM,CAAC,CAAC;QACpF,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAmB,CAAC;QACjD,OAAO,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,kBAAkB,CAAC,CAAC,CAAC,IAAI,CAAC;IACrF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}