@vaultctl/core 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +167 -0
- package/dist/tags.js +1 -1
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# @vaultctl/core
|
|
2
|
+
|
|
3
|
+
Core library for structured Obsidian vault operations. Provides frontmatter parsing, wikilink resolution, tag management, search, health checks, and template rendering — all as pure TypeScript with zero CLI dependencies.
|
|
4
|
+
|
|
5
|
+
This is the library package. For the CLI tool, see [`vaultctl`](https://www.npmjs.com/package/vaultctl).
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @vaultctl/core
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import {
|
|
17
|
+
loadVault,
|
|
18
|
+
searchNotes,
|
|
19
|
+
listTags,
|
|
20
|
+
findByTag,
|
|
21
|
+
addTag,
|
|
22
|
+
removeTag,
|
|
23
|
+
renameTag,
|
|
24
|
+
checkHealth,
|
|
25
|
+
listTemplates,
|
|
26
|
+
renderTemplate,
|
|
27
|
+
parseNote,
|
|
28
|
+
safeJoin,
|
|
29
|
+
} from '@vaultctl/core';
|
|
30
|
+
|
|
31
|
+
// Load all notes from a vault
|
|
32
|
+
const notes = await loadVault('/path/to/vault');
|
|
33
|
+
|
|
34
|
+
// Search notes by content and frontmatter
|
|
35
|
+
const results = searchNotes(notes, {
|
|
36
|
+
query: 'meeting notes',
|
|
37
|
+
type: 'project',
|
|
38
|
+
status: 'active',
|
|
39
|
+
tag: 'work',
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// Tag operations
|
|
43
|
+
const tags = listTags(notes); // all tags with counts
|
|
44
|
+
const tagged = findByTag(notes, 'status/active');
|
|
45
|
+
await addTag('/path/to/vault', '01_Projects/my-project.md', 'priority/high');
|
|
46
|
+
await removeTag('/path/to/vault', '01_Projects/my-project.md', 'priority/high');
|
|
47
|
+
const renamed = await renameTag('/path/to/vault', notes, 'old-tag', 'new-tag');
|
|
48
|
+
|
|
49
|
+
// Health checks
|
|
50
|
+
const issues = await checkHealth('/path/to/vault', notes);
|
|
51
|
+
const linksOnly = await checkHealth('/path/to/vault', notes, ['broken-links']);
|
|
52
|
+
|
|
53
|
+
// Templates
|
|
54
|
+
const templates = await listTemplates('/path/to/vault');
|
|
55
|
+
const content = await renderTemplate('/path/to/vault', 'project', 'My Project');
|
|
56
|
+
|
|
57
|
+
// Parse a single note
|
|
58
|
+
const note = parseNote('/path/to/vault', 'relative/path.md', fileContent);
|
|
59
|
+
|
|
60
|
+
// Safe path joining (prevents path traversal)
|
|
61
|
+
const safePath = safeJoin('/path/to/vault', 'notes/file.md');
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## API
|
|
65
|
+
|
|
66
|
+
### Vault
|
|
67
|
+
|
|
68
|
+
| Function | Description |
|
|
69
|
+
|----------|-------------|
|
|
70
|
+
| `loadVault(path)` | Load and parse all `.md` files in a vault |
|
|
71
|
+
| `resolveVaultPath(options?)` | Find vault root via flag, env, or directory walk |
|
|
72
|
+
| `safeJoin(vaultPath, relativePath)` | Join paths with traversal protection |
|
|
73
|
+
|
|
74
|
+
### Search
|
|
75
|
+
|
|
76
|
+
| Function | Description |
|
|
77
|
+
|----------|-------------|
|
|
78
|
+
| `searchNotes(notes, options)` | Filter notes by query, type, status, and/or tag |
|
|
79
|
+
|
|
80
|
+
**`SearchOptions`**: `{ query?, type?, status?, tag? }`
|
|
81
|
+
|
|
82
|
+
### Tags
|
|
83
|
+
|
|
84
|
+
| Function | Description |
|
|
85
|
+
|----------|-------------|
|
|
86
|
+
| `listTags(notes)` | List all tags with usage counts |
|
|
87
|
+
| `findByTag(notes, tag)` | Find notes containing a tag |
|
|
88
|
+
| `addTag(vault, path, tag)` | Add a tag to a note's frontmatter |
|
|
89
|
+
| `removeTag(vault, path, tag)` | Remove a tag from a note |
|
|
90
|
+
| `renameTag(vault, notes, old, new)` | Rename a tag across the vault |
|
|
91
|
+
|
|
92
|
+
Tags are validated on write — must start with a letter and contain only letters, numbers, hyphens, underscores, or `/` for hierarchy.
|
|
93
|
+
|
|
94
|
+
### Health
|
|
95
|
+
|
|
96
|
+
| Function | Description |
|
|
97
|
+
|----------|-------------|
|
|
98
|
+
| `checkHealth(vault, notes, checks?)` | Run health checks on the vault |
|
|
99
|
+
|
|
100
|
+
**`CheckName`**: `'broken-links' | 'orphans' | 'stale' | 'frontmatter'`
|
|
101
|
+
|
|
102
|
+
### Templates
|
|
103
|
+
|
|
104
|
+
| Function | Description |
|
|
105
|
+
|----------|-------------|
|
|
106
|
+
| `listTemplates(vault)` | List available `_templates/tpl-*.md` templates |
|
|
107
|
+
| `renderTemplate(vault, name, title?)` | Render a template with date/title substitution |
|
|
108
|
+
|
|
109
|
+
### Parsing
|
|
110
|
+
|
|
111
|
+
| Function | Description |
|
|
112
|
+
|----------|-------------|
|
|
113
|
+
| `parseNote(vault, path, content)` | Parse a markdown file into a `Note` object |
|
|
114
|
+
| `parseFrontmatter(content)` | Extract YAML frontmatter |
|
|
115
|
+
| `stringifyFrontmatter(fm, body)` | Serialize frontmatter + body back to markdown |
|
|
116
|
+
| `parseWikilinks(content)` | Extract `[[wikilinks]]` from content |
|
|
117
|
+
| `parseInlineTags(content)` | Extract `#hashtags` from content |
|
|
118
|
+
|
|
119
|
+
## Types
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
interface Note {
|
|
123
|
+
path: string;
|
|
124
|
+
filename: string;
|
|
125
|
+
folder: string;
|
|
126
|
+
frontmatter: Frontmatter;
|
|
127
|
+
body: string;
|
|
128
|
+
wikilinks: WikiLink[];
|
|
129
|
+
inlineTags: string[];
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
interface Frontmatter {
|
|
133
|
+
type?: string;
|
|
134
|
+
status?: string;
|
|
135
|
+
tags?: string[];
|
|
136
|
+
created?: string;
|
|
137
|
+
updated?: string;
|
|
138
|
+
[key: string]: unknown;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
interface WikiLink {
|
|
142
|
+
raw: string;
|
|
143
|
+
target: string;
|
|
144
|
+
alias?: string;
|
|
145
|
+
resolved?: string | null;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
interface HealthIssue {
|
|
149
|
+
check: string;
|
|
150
|
+
path: string;
|
|
151
|
+
message: string;
|
|
152
|
+
severity: 'error' | 'warning' | 'info';
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
interface VaultStats {
|
|
156
|
+
totalNotes: number;
|
|
157
|
+
notesByType: Record<string, number>;
|
|
158
|
+
notesByStatus: Record<string, number>;
|
|
159
|
+
totalTags: number;
|
|
160
|
+
brokenLinks: number;
|
|
161
|
+
orphanedNotes: number;
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## License
|
|
166
|
+
|
|
167
|
+
MIT
|
package/dist/tags.js
CHANGED
|
@@ -15,7 +15,7 @@ export function findByTag(notes, tag) {
|
|
|
15
15
|
return allTags.includes(tag);
|
|
16
16
|
});
|
|
17
17
|
}
|
|
18
|
-
const VALID_TAG_RE = /^[a-zA-Z][\w
|
|
18
|
+
const VALID_TAG_RE = /^[a-zA-Z][\w-]*(?:\/[a-zA-Z][\w-]*)*$/;
|
|
19
19
|
function validateTag(tag) {
|
|
20
20
|
if (!tag || !VALID_TAG_RE.test(tag)) {
|
|
21
21
|
throw new Error(`Invalid tag: "${tag}" — tags must start with a letter and contain only letters, numbers, hyphens, underscores, or slashes`);
|
package/package.json
CHANGED