@stoneforge/quarry 1.13.0 → 1.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/quarry-api.d.ts +9 -1
- package/dist/api/quarry-api.d.ts.map +1 -1
- package/dist/api/quarry-api.js +21 -2
- package/dist/api/quarry-api.js.map +1 -1
- package/dist/api/types.d.ts +8 -1
- package/dist/api/types.d.ts.map +1 -1
- package/dist/api/types.js.map +1 -1
- package/dist/cli/commands/auto-link-helper.d.ts.map +1 -1
- package/dist/cli/commands/auto-link-helper.js +1 -0
- package/dist/cli/commands/auto-link-helper.js.map +1 -1
- package/dist/cli/commands/crud.d.ts +2 -0
- package/dist/cli/commands/crud.d.ts.map +1 -1
- package/dist/cli/commands/crud.js +100 -10
- package/dist/cli/commands/crud.js.map +1 -1
- package/dist/cli/commands/docs.js +2 -2
- package/dist/cli/commands/docs.js.map +1 -1
- package/dist/cli/commands/document.js +1 -1
- package/dist/cli/commands/document.js.map +1 -1
- package/dist/cli/commands/entity.js +1 -1
- package/dist/cli/commands/entity.js.map +1 -1
- package/dist/cli/commands/external-sync.d.ts +6 -5
- package/dist/cli/commands/external-sync.d.ts.map +1 -1
- package/dist/cli/commands/external-sync.js +1032 -180
- package/dist/cli/commands/external-sync.js.map +1 -1
- package/dist/cli/commands/library.js +1 -1
- package/dist/cli/commands/library.js.map +1 -1
- package/dist/cli/commands/message.js +2 -2
- package/dist/cli/commands/message.js.map +1 -1
- package/dist/cli/commands/serve.d.ts.map +1 -1
- package/dist/cli/commands/serve.js +2 -0
- package/dist/cli/commands/serve.js.map +1 -1
- package/dist/cli/commands/task.d.ts.map +1 -1
- package/dist/cli/commands/task.js +7 -4
- package/dist/cli/commands/task.js.map +1 -1
- package/dist/cli/commands/team.js +1 -1
- package/dist/cli/commands/team.js.map +1 -1
- package/dist/cli/commands/workflow.js +1 -1
- package/dist/cli/commands/workflow.js.map +1 -1
- package/dist/cli/utils/progress.d.ts +30 -0
- package/dist/cli/utils/progress.d.ts.map +1 -0
- package/dist/cli/utils/progress.js +47 -0
- package/dist/cli/utils/progress.js.map +1 -0
- package/dist/config/config.d.ts.map +1 -1
- package/dist/config/config.js +6 -0
- package/dist/config/config.js.map +1 -1
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +1 -0
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/file.d.ts.map +1 -1
- package/dist/config/file.js +10 -0
- package/dist/config/file.js.map +1 -1
- package/dist/config/merge.d.ts.map +1 -1
- package/dist/config/merge.js +7 -1
- package/dist/config/merge.js.map +1 -1
- package/dist/config/types.d.ts +7 -2
- package/dist/config/types.d.ts.map +1 -1
- package/dist/config/types.js +3 -0
- package/dist/config/types.js.map +1 -1
- package/dist/config/validation.d.ts.map +1 -1
- package/dist/config/validation.js +13 -0
- package/dist/config/validation.js.map +1 -1
- package/dist/external-sync/adapters/document-sync-adapter.d.ts +150 -0
- package/dist/external-sync/adapters/document-sync-adapter.d.ts.map +1 -0
- package/dist/external-sync/adapters/document-sync-adapter.js +325 -0
- package/dist/external-sync/adapters/document-sync-adapter.js.map +1 -0
- package/dist/external-sync/index.d.ts +3 -0
- package/dist/external-sync/index.d.ts.map +1 -1
- package/dist/external-sync/index.js +4 -0
- package/dist/external-sync/index.js.map +1 -1
- package/dist/external-sync/provider-registry.d.ts +7 -3
- package/dist/external-sync/provider-registry.d.ts.map +1 -1
- package/dist/external-sync/provider-registry.js +20 -3
- package/dist/external-sync/provider-registry.js.map +1 -1
- package/dist/external-sync/providers/folder/folder-document-adapter.d.ts +97 -0
- package/dist/external-sync/providers/folder/folder-document-adapter.d.ts.map +1 -0
- package/dist/external-sync/providers/folder/folder-document-adapter.js +261 -0
- package/dist/external-sync/providers/folder/folder-document-adapter.js.map +1 -0
- package/dist/external-sync/providers/folder/folder-fs.d.ts +146 -0
- package/dist/external-sync/providers/folder/folder-fs.d.ts.map +1 -0
- package/dist/external-sync/providers/folder/folder-fs.js +300 -0
- package/dist/external-sync/providers/folder/folder-fs.js.map +1 -0
- package/dist/external-sync/providers/folder/folder-provider.d.ts +28 -0
- package/dist/external-sync/providers/folder/folder-provider.d.ts.map +1 -0
- package/dist/external-sync/providers/folder/folder-provider.js +87 -0
- package/dist/external-sync/providers/folder/folder-provider.js.map +1 -0
- package/dist/external-sync/providers/folder/index.d.ts +11 -0
- package/dist/external-sync/providers/folder/index.d.ts.map +1 -0
- package/dist/external-sync/providers/folder/index.js +13 -0
- package/dist/external-sync/providers/folder/index.js.map +1 -0
- package/dist/external-sync/providers/index.d.ts +4 -0
- package/dist/external-sync/providers/index.d.ts.map +1 -1
- package/dist/external-sync/providers/index.js +5 -0
- package/dist/external-sync/providers/index.js.map +1 -1
- package/dist/external-sync/providers/notion/index.d.ts +19 -0
- package/dist/external-sync/providers/notion/index.d.ts.map +1 -0
- package/dist/external-sync/providers/notion/index.js +20 -0
- package/dist/external-sync/providers/notion/index.js.map +1 -0
- package/dist/external-sync/providers/notion/notion-api.d.ts +253 -0
- package/dist/external-sync/providers/notion/notion-api.d.ts.map +1 -0
- package/dist/external-sync/providers/notion/notion-api.js +492 -0
- package/dist/external-sync/providers/notion/notion-api.js.map +1 -0
- package/dist/external-sync/providers/notion/notion-blocks.d.ts +93 -0
- package/dist/external-sync/providers/notion/notion-blocks.d.ts.map +1 -0
- package/dist/external-sync/providers/notion/notion-blocks.js +773 -0
- package/dist/external-sync/providers/notion/notion-blocks.js.map +1 -0
- package/dist/external-sync/providers/notion/notion-document-adapter.d.ts +176 -0
- package/dist/external-sync/providers/notion/notion-document-adapter.d.ts.map +1 -0
- package/dist/external-sync/providers/notion/notion-document-adapter.js +413 -0
- package/dist/external-sync/providers/notion/notion-document-adapter.js.map +1 -0
- package/dist/external-sync/providers/notion/notion-provider.d.ts +57 -0
- package/dist/external-sync/providers/notion/notion-provider.d.ts.map +1 -0
- package/dist/external-sync/providers/notion/notion-provider.js +159 -0
- package/dist/external-sync/providers/notion/notion-provider.js.map +1 -0
- package/dist/external-sync/providers/notion/notion-types.d.ts +388 -0
- package/dist/external-sync/providers/notion/notion-types.d.ts.map +1 -0
- package/dist/external-sync/providers/notion/notion-types.js +47 -0
- package/dist/external-sync/providers/notion/notion-types.js.map +1 -0
- package/dist/external-sync/sync-engine.d.ts +70 -4
- package/dist/external-sync/sync-engine.d.ts.map +1 -1
- package/dist/external-sync/sync-engine.js +436 -67
- package/dist/external-sync/sync-engine.js.map +1 -1
- package/dist/server/index.js +8 -8
- package/dist/server/index.js.map +1 -1
- package/package.json +4 -12
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Folder Document Sync Adapter
|
|
3
|
+
*
|
|
4
|
+
* Implements the DocumentSyncAdapter interface for local folder-based
|
|
5
|
+
* document synchronization. Maps between Stoneforge documents and
|
|
6
|
+
* markdown files with YAML frontmatter in a local directory tree.
|
|
7
|
+
*
|
|
8
|
+
* Uses folder-fs.ts for all filesystem operations (read, write, list).
|
|
9
|
+
*
|
|
10
|
+
* Conventions:
|
|
11
|
+
* - project = absolute folder path (base directory)
|
|
12
|
+
* - externalId = relative file path within the folder (e.g., 'notes/meeting.md')
|
|
13
|
+
* - URL format: file:///absolute/path/to/file.md
|
|
14
|
+
* - Filenames are generated from titles using slugification
|
|
15
|
+
*/
|
|
16
|
+
import * as path from 'node:path';
|
|
17
|
+
import { readFile, writeFile, listFiles, fileExists, } from './folder-fs.js';
|
|
18
|
+
// ============================================================================
|
|
19
|
+
// Slugify Utility
|
|
20
|
+
// ============================================================================
|
|
21
|
+
/**
|
|
22
|
+
* Converts a title string into a filename-safe slug.
|
|
23
|
+
*
|
|
24
|
+
* - Lowercases the string
|
|
25
|
+
* - Replaces non-alphanumeric characters (except hyphens) with hyphens
|
|
26
|
+
* - Collapses consecutive hyphens
|
|
27
|
+
* - Trims leading/trailing hyphens
|
|
28
|
+
* - Falls back to 'untitled' for empty results
|
|
29
|
+
*
|
|
30
|
+
* @param title - The document title to slugify
|
|
31
|
+
* @returns A filename-safe slug string (without extension)
|
|
32
|
+
*/
|
|
33
|
+
export function slugify(title) {
|
|
34
|
+
const slug = title
|
|
35
|
+
.toLowerCase()
|
|
36
|
+
.replace(/[^a-z0-9-]/g, '-')
|
|
37
|
+
.replace(/-+/g, '-')
|
|
38
|
+
.replace(/^-|-$/g, '');
|
|
39
|
+
return slug || 'untitled';
|
|
40
|
+
}
|
|
41
|
+
// ============================================================================
|
|
42
|
+
// URL Helpers
|
|
43
|
+
// ============================================================================
|
|
44
|
+
/**
|
|
45
|
+
* Builds a file:// URL from a base path and relative file path.
|
|
46
|
+
*
|
|
47
|
+
* @param basePath - Absolute base directory path
|
|
48
|
+
* @param relativePath - Relative file path within the base directory
|
|
49
|
+
* @returns A file:// URL string
|
|
50
|
+
*/
|
|
51
|
+
function buildFileUrl(basePath, relativePath) {
|
|
52
|
+
const absolutePath = path.resolve(basePath, relativePath);
|
|
53
|
+
return `file://${absolutePath}`;
|
|
54
|
+
}
|
|
55
|
+
// ============================================================================
|
|
56
|
+
// Folder Document Adapter
|
|
57
|
+
// ============================================================================
|
|
58
|
+
/**
|
|
59
|
+
* DocumentSyncAdapter implementation for local folder-based sync.
|
|
60
|
+
*
|
|
61
|
+
* Maps between Stoneforge ExternalDocument and markdown files with YAML
|
|
62
|
+
* frontmatter on the local filesystem.
|
|
63
|
+
*
|
|
64
|
+
* Usage:
|
|
65
|
+
* ```typescript
|
|
66
|
+
* const adapter = new FolderDocumentAdapter();
|
|
67
|
+
* const doc = await adapter.getPage('/path/to/docs', 'notes/meeting.md');
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
export class FolderDocumentAdapter {
|
|
71
|
+
/**
|
|
72
|
+
* Fetch a single document by its relative file path.
|
|
73
|
+
*
|
|
74
|
+
* @param project - Absolute path to the base directory
|
|
75
|
+
* @param externalId - Relative file path (e.g., 'notes/meeting.md')
|
|
76
|
+
* @returns The document as an ExternalDocument, or null if not found
|
|
77
|
+
*/
|
|
78
|
+
async getPage(project, externalId) {
|
|
79
|
+
try {
|
|
80
|
+
const result = await readFile(project, externalId);
|
|
81
|
+
return {
|
|
82
|
+
externalId,
|
|
83
|
+
url: buildFileUrl(project, externalId),
|
|
84
|
+
provider: 'folder',
|
|
85
|
+
project,
|
|
86
|
+
title: extractTitle(externalId, result.content, result.frontmatter),
|
|
87
|
+
content: result.content,
|
|
88
|
+
contentType: 'markdown',
|
|
89
|
+
updatedAt: new Date(result.mtime).toISOString(),
|
|
90
|
+
raw: { frontmatter: result.frontmatter },
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
catch (err) {
|
|
94
|
+
// Return null for missing files (ENOENT)
|
|
95
|
+
if (err &&
|
|
96
|
+
typeof err === 'object' &&
|
|
97
|
+
'code' in err &&
|
|
98
|
+
err.code === 'ENOENT') {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
// Re-throw other errors (permission issues, etc.)
|
|
102
|
+
throw err;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* List documents modified since a given timestamp.
|
|
107
|
+
*
|
|
108
|
+
* Reads each file to extract title and content, returning full
|
|
109
|
+
* ExternalDocument objects.
|
|
110
|
+
*
|
|
111
|
+
* @param project - Absolute path to the base directory
|
|
112
|
+
* @param since - ISO 8601 timestamp; only files modified after this are returned
|
|
113
|
+
* @returns Array of ExternalDocument objects for modified files
|
|
114
|
+
*/
|
|
115
|
+
async listPagesSince(project, since) {
|
|
116
|
+
const sinceMs = new Date(since).getTime();
|
|
117
|
+
const entries = await listFiles(project, { since: sinceMs });
|
|
118
|
+
const documents = [];
|
|
119
|
+
for (const entry of entries) {
|
|
120
|
+
const result = await readFile(project, entry.path);
|
|
121
|
+
documents.push({
|
|
122
|
+
externalId: entry.path,
|
|
123
|
+
url: buildFileUrl(project, entry.path),
|
|
124
|
+
provider: 'folder',
|
|
125
|
+
project,
|
|
126
|
+
title: extractTitle(entry.path, result.content, result.frontmatter),
|
|
127
|
+
content: result.content,
|
|
128
|
+
contentType: 'markdown',
|
|
129
|
+
updatedAt: new Date(entry.mtime).toISOString(),
|
|
130
|
+
raw: { frontmatter: result.frontmatter },
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
return documents;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Create a new document file in the folder.
|
|
137
|
+
*
|
|
138
|
+
* Generates a filename from the title using slugification, writes
|
|
139
|
+
* the content with frontmatter, and returns the created document.
|
|
140
|
+
*
|
|
141
|
+
* If the page has a `libraryPath`, the file is placed in the
|
|
142
|
+
* corresponding subdirectory. For example, a libraryPath of
|
|
143
|
+
* 'documentation/api' results in the file being created at
|
|
144
|
+
* 'documentation/api/my-doc.md'.
|
|
145
|
+
*
|
|
146
|
+
* @param project - Absolute path to the base directory
|
|
147
|
+
* @param page - Document input with title, content, and optional contentType
|
|
148
|
+
* @returns The created ExternalDocument
|
|
149
|
+
*/
|
|
150
|
+
async createPage(project, page) {
|
|
151
|
+
const baseSlug = slugify(page.title);
|
|
152
|
+
// Determine the directory prefix from library path
|
|
153
|
+
const dirPrefix = page.libraryPath ? `${page.libraryPath}/` : '';
|
|
154
|
+
let relativePath = `${dirPrefix}${baseSlug}.md`;
|
|
155
|
+
// Check for existing file and add numeric suffix if needed
|
|
156
|
+
let counter = 2;
|
|
157
|
+
while (await fileExists(project, relativePath)) {
|
|
158
|
+
relativePath = `${dirPrefix}${baseSlug}-${counter}.md`;
|
|
159
|
+
counter++;
|
|
160
|
+
}
|
|
161
|
+
const frontmatter = {
|
|
162
|
+
'synced-at': new Date().toISOString(),
|
|
163
|
+
};
|
|
164
|
+
await writeFile(project, relativePath, page.content, frontmatter);
|
|
165
|
+
// Read back to get the actual mtime
|
|
166
|
+
const result = await readFile(project, relativePath);
|
|
167
|
+
return {
|
|
168
|
+
externalId: relativePath,
|
|
169
|
+
url: buildFileUrl(project, relativePath),
|
|
170
|
+
provider: 'folder',
|
|
171
|
+
project,
|
|
172
|
+
title: page.title,
|
|
173
|
+
content: page.content,
|
|
174
|
+
contentType: 'markdown',
|
|
175
|
+
updatedAt: new Date(result.mtime).toISOString(),
|
|
176
|
+
raw: { frontmatter: result.frontmatter },
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Update an existing document file in the folder.
|
|
181
|
+
*
|
|
182
|
+
* Reads the existing file, merges the updates (preserving existing
|
|
183
|
+
* frontmatter), and writes back. Returns the updated document.
|
|
184
|
+
*
|
|
185
|
+
* @param project - Absolute path to the base directory
|
|
186
|
+
* @param externalId - Relative file path of the existing document
|
|
187
|
+
* @param updates - Partial document input with fields to update
|
|
188
|
+
* @returns The updated ExternalDocument
|
|
189
|
+
*/
|
|
190
|
+
async updatePage(project, externalId, updates) {
|
|
191
|
+
// Read the existing file to preserve frontmatter and content
|
|
192
|
+
const existing = await readFile(project, externalId);
|
|
193
|
+
// Merge content: use update if provided, otherwise keep existing
|
|
194
|
+
const mergedContent = updates.content !== undefined ? updates.content : existing.content;
|
|
195
|
+
// Merge frontmatter: preserve existing, update synced-at
|
|
196
|
+
const mergedFrontmatter = {
|
|
197
|
+
...existing.frontmatter,
|
|
198
|
+
'synced-at': new Date().toISOString(),
|
|
199
|
+
};
|
|
200
|
+
// If title is being updated, store it in frontmatter
|
|
201
|
+
if (updates.title !== undefined) {
|
|
202
|
+
mergedFrontmatter.title = updates.title;
|
|
203
|
+
}
|
|
204
|
+
await writeFile(project, externalId, mergedContent, mergedFrontmatter);
|
|
205
|
+
// Read back to get accurate mtime
|
|
206
|
+
const result = await readFile(project, externalId);
|
|
207
|
+
const title = updates.title ?? extractTitle(externalId, mergedContent, mergedFrontmatter);
|
|
208
|
+
return {
|
|
209
|
+
externalId,
|
|
210
|
+
url: buildFileUrl(project, externalId),
|
|
211
|
+
provider: 'folder',
|
|
212
|
+
project,
|
|
213
|
+
title,
|
|
214
|
+
content: mergedContent,
|
|
215
|
+
contentType: 'markdown',
|
|
216
|
+
updatedAt: new Date(result.mtime).toISOString(),
|
|
217
|
+
raw: { frontmatter: result.frontmatter },
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
// ============================================================================
|
|
222
|
+
// Internal Helpers
|
|
223
|
+
// ============================================================================
|
|
224
|
+
/**
|
|
225
|
+
* Extracts a document title from available sources, in priority order:
|
|
226
|
+
* 1. Frontmatter `title` field
|
|
227
|
+
* 2. First markdown heading (# Title)
|
|
228
|
+
* 3. Filename without extension
|
|
229
|
+
*
|
|
230
|
+
* @param relativePath - Relative file path for fallback
|
|
231
|
+
* @param content - Markdown content for heading extraction
|
|
232
|
+
* @param frontmatter - Parsed frontmatter for title field
|
|
233
|
+
* @returns The best available title
|
|
234
|
+
*/
|
|
235
|
+
function extractTitle(relativePath, content, frontmatter) {
|
|
236
|
+
// 1. Check frontmatter title
|
|
237
|
+
if (typeof frontmatter.title === 'string' &&
|
|
238
|
+
frontmatter.title.trim().length > 0) {
|
|
239
|
+
return frontmatter.title.trim();
|
|
240
|
+
}
|
|
241
|
+
// 2. Check first markdown heading
|
|
242
|
+
const headingMatch = content.match(/^#\s+(.+)$/m);
|
|
243
|
+
if (headingMatch) {
|
|
244
|
+
return headingMatch[1].trim();
|
|
245
|
+
}
|
|
246
|
+
// 3. Fall back to filename without extension
|
|
247
|
+
const basename = path.basename(relativePath, path.extname(relativePath));
|
|
248
|
+
return basename;
|
|
249
|
+
}
|
|
250
|
+
// ============================================================================
|
|
251
|
+
// Factory Function
|
|
252
|
+
// ============================================================================
|
|
253
|
+
/**
|
|
254
|
+
* Creates a new FolderDocumentAdapter instance.
|
|
255
|
+
*
|
|
256
|
+
* @returns A configured FolderDocumentAdapter
|
|
257
|
+
*/
|
|
258
|
+
export function createFolderDocumentAdapter() {
|
|
259
|
+
return new FolderDocumentAdapter();
|
|
260
|
+
}
|
|
261
|
+
//# sourceMappingURL=folder-document-adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"folder-document-adapter.js","sourceRoot":"","sources":["../../../../src/external-sync/providers/folder/folder-document-adapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Folder Filesystem Client
|
|
3
|
+
*
|
|
4
|
+
* Filesystem layer for the folder sync provider. Reads, writes, and lists
|
|
5
|
+
* markdown files with YAML frontmatter in a local directory tree.
|
|
6
|
+
*
|
|
7
|
+
* Follows the same API client pattern as github-api.ts — a focused,
|
|
8
|
+
* dependency-light module that handles I/O for a single provider.
|
|
9
|
+
*
|
|
10
|
+
* Uses the `yaml` package for frontmatter parsing/serialization.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* YAML frontmatter fields recognized by the folder sync provider.
|
|
14
|
+
* Additional fields are preserved as-is during round-trips.
|
|
15
|
+
*/
|
|
16
|
+
export interface FolderFrontmatter {
|
|
17
|
+
/** Stoneforge element ID linked to this file */
|
|
18
|
+
'stoneforge-id'?: string;
|
|
19
|
+
/** Document category */
|
|
20
|
+
category?: string;
|
|
21
|
+
/** Tags for classification */
|
|
22
|
+
tags?: string[];
|
|
23
|
+
/** ISO 8601 timestamp of last sync */
|
|
24
|
+
'synced-at'?: string;
|
|
25
|
+
/** Additional arbitrary frontmatter fields (preserved on round-trip) */
|
|
26
|
+
[key: string]: unknown;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Result of reading a markdown file with frontmatter
|
|
30
|
+
*/
|
|
31
|
+
export interface FolderFileReadResult {
|
|
32
|
+
/** Markdown body content (without frontmatter) */
|
|
33
|
+
readonly content: string;
|
|
34
|
+
/** Parsed YAML frontmatter (empty object if none) */
|
|
35
|
+
readonly frontmatter: FolderFrontmatter;
|
|
36
|
+
/** File modification time as Unix timestamp (milliseconds) */
|
|
37
|
+
readonly mtime: number;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Entry returned when listing files in a folder
|
|
41
|
+
*/
|
|
42
|
+
export interface FolderFileEntry {
|
|
43
|
+
/** Path relative to the base directory (forward slashes) */
|
|
44
|
+
readonly path: string;
|
|
45
|
+
/** File modification time as Unix timestamp (milliseconds) */
|
|
46
|
+
readonly mtime: number;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Options for listing files
|
|
50
|
+
*/
|
|
51
|
+
export interface ListFilesOptions {
|
|
52
|
+
/**
|
|
53
|
+
* Only return files modified after this Unix timestamp (milliseconds).
|
|
54
|
+
* Compares against the file's mtime.
|
|
55
|
+
*/
|
|
56
|
+
since?: number;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Typed error for folder filesystem failures.
|
|
60
|
+
*/
|
|
61
|
+
export declare class FolderFsError extends Error {
|
|
62
|
+
/** The operation that failed */
|
|
63
|
+
readonly operation: 'read' | 'write' | 'list';
|
|
64
|
+
/** The file path involved (if applicable) */
|
|
65
|
+
readonly filePath: string | null;
|
|
66
|
+
/** The underlying system error code (e.g., 'ENOENT', 'EACCES') */
|
|
67
|
+
readonly code: string | null;
|
|
68
|
+
constructor(message: string, operation: 'read' | 'write' | 'list', filePath?: string | null, code?: string | null, cause?: Error);
|
|
69
|
+
/**
|
|
70
|
+
* Whether this error is due to a missing file or directory
|
|
71
|
+
*/
|
|
72
|
+
get isNotFound(): boolean;
|
|
73
|
+
/**
|
|
74
|
+
* Whether this error is due to a permission issue
|
|
75
|
+
*/
|
|
76
|
+
get isPermissionError(): boolean;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Type guard for FolderFsError
|
|
80
|
+
*/
|
|
81
|
+
export declare function isFolderFsError(error: unknown): error is FolderFsError;
|
|
82
|
+
/**
|
|
83
|
+
* Parses YAML frontmatter from a markdown string.
|
|
84
|
+
*
|
|
85
|
+
* Expects the standard `---` delimited frontmatter block at the start of the file.
|
|
86
|
+
* Returns the body content and parsed frontmatter separately.
|
|
87
|
+
*
|
|
88
|
+
* @param raw - Raw file content (may or may not have frontmatter)
|
|
89
|
+
* @returns Parsed frontmatter and body content
|
|
90
|
+
*/
|
|
91
|
+
export declare function parseFrontmatter(raw: string): {
|
|
92
|
+
content: string;
|
|
93
|
+
frontmatter: FolderFrontmatter;
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* Serializes frontmatter and content into a markdown string with YAML front matter.
|
|
97
|
+
*
|
|
98
|
+
* @param content - Markdown body content
|
|
99
|
+
* @param frontmatter - Frontmatter key-value pairs
|
|
100
|
+
* @returns Complete file content with frontmatter block
|
|
101
|
+
*/
|
|
102
|
+
export declare function serializeFrontmatter(content: string, frontmatter: FolderFrontmatter): string;
|
|
103
|
+
/**
|
|
104
|
+
* Reads a markdown file with YAML frontmatter from a folder.
|
|
105
|
+
*
|
|
106
|
+
* @param basePath - Absolute path to the base directory
|
|
107
|
+
* @param relativePath - Path relative to basePath (e.g., 'notes/meeting.md')
|
|
108
|
+
* @returns Parsed file content, frontmatter, and modification time
|
|
109
|
+
* @throws {FolderFsError} If the file cannot be read
|
|
110
|
+
*/
|
|
111
|
+
export declare function readFile(basePath: string, relativePath: string): Promise<FolderFileReadResult>;
|
|
112
|
+
/**
|
|
113
|
+
* Writes a markdown file with YAML frontmatter to a folder.
|
|
114
|
+
*
|
|
115
|
+
* Writes atomically by first writing to a temporary file in the same directory,
|
|
116
|
+
* then renaming. This prevents partial writes from corrupting files.
|
|
117
|
+
*
|
|
118
|
+
* @param basePath - Absolute path to the base directory
|
|
119
|
+
* @param relativePath - Path relative to basePath (e.g., 'notes/meeting.md')
|
|
120
|
+
* @param content - Markdown body content
|
|
121
|
+
* @param frontmatter - YAML frontmatter key-value pairs
|
|
122
|
+
* @throws {FolderFsError} If the file cannot be written
|
|
123
|
+
*/
|
|
124
|
+
export declare function writeFile(basePath: string, relativePath: string, content: string, frontmatter: FolderFrontmatter): Promise<void>;
|
|
125
|
+
/**
|
|
126
|
+
* Recursively lists all `.md` files in a directory tree.
|
|
127
|
+
*
|
|
128
|
+
* Excludes:
|
|
129
|
+
* - Dotfiles (files starting with `.`)
|
|
130
|
+
* - Files inside dot-directories (directories starting with `.`)
|
|
131
|
+
*
|
|
132
|
+
* @param basePath - Absolute path to the base directory
|
|
133
|
+
* @param options - Optional filtering (e.g., `since` timestamp)
|
|
134
|
+
* @returns Array of file entries with relative paths and modification times
|
|
135
|
+
* @throws {FolderFsError} If the directory cannot be read
|
|
136
|
+
*/
|
|
137
|
+
export declare function listFiles(basePath: string, options?: ListFilesOptions): Promise<FolderFileEntry[]>;
|
|
138
|
+
/**
|
|
139
|
+
* Checks whether a file exists at the given relative path within a base directory.
|
|
140
|
+
*
|
|
141
|
+
* @param basePath - Absolute path to the base directory
|
|
142
|
+
* @param relativePath - Path relative to basePath (e.g., 'notes/meeting.md')
|
|
143
|
+
* @returns True if the file exists, false otherwise
|
|
144
|
+
*/
|
|
145
|
+
export declare function fileExists(basePath: string, relativePath: string): Promise<boolean>;
|
|
146
|
+
//# sourceMappingURL=folder-fs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"folder-fs.d.ts","sourceRoot":"","sources":["../../../../src/external-sync/providers/folder/folder-fs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAUH;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,gDAAgD;IAChD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,wBAAwB;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8BAA8B;IAC9B,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,sCAAsC;IACtC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wEAAwE;IACxE,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,kDAAkD;IAClD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,qDAAqD;IACrD,QAAQ,CAAC,WAAW,EAAE,iBAAiB,CAAC;IACxC,8DAA8D;IAC9D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,4DAA4D;IAC5D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,8DAA8D;IAC9D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAMD;;GAEG;AACH,qBAAa,aAAc,SAAQ,KAAK;IACtC,gCAAgC;IAChC,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;IAC9C,6CAA6C;IAC7C,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,kEAAkE;IAClE,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;gBAG3B,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,EACpC,QAAQ,GAAE,MAAM,GAAG,IAAW,EAC9B,IAAI,GAAE,MAAM,GAAG,IAAW,EAC1B,KAAK,CAAC,EAAE,KAAK;IAcf;;OAEG;IACH,IAAI,UAAU,IAAI,OAAO,CAExB;IAED;;OAEG;IACH,IAAI,iBAAiB,IAAI,OAAO,CAE/B;CACF;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,aAAa,CAEtE;AASD;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG;IAC7C,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,iBAAiB,CAAC;CAChC,CAsDA;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,iBAAiB,GAC7B,MAAM,CAYR;AAMD;;;;;;;GAOG;AACH,wBAAsB,QAAQ,CAC5B,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,oBAAoB,CAAC,CAqC/B;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,SAAS,CAC7B,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,iBAAiB,GAC7B,OAAO,CAAC,IAAI,CAAC,CA0Cf;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,SAAS,CAC7B,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,gBAAgB,GACzB,OAAO,CAAC,eAAe,EAAE,CAAC,CAqB5B;AAED;;;;;;GAMG;AACH,wBAAsB,UAAU,CAC9B,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,OAAO,CAAC,CAOlB"}
|