@stoneforge/quarry 1.12.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.
Files changed (199) hide show
  1. package/README.md +2 -0
  2. package/dist/api/quarry-api.d.ts +9 -1
  3. package/dist/api/quarry-api.d.ts.map +1 -1
  4. package/dist/api/quarry-api.js +21 -2
  5. package/dist/api/quarry-api.js.map +1 -1
  6. package/dist/api/types.d.ts +8 -1
  7. package/dist/api/types.d.ts.map +1 -1
  8. package/dist/api/types.js.map +1 -1
  9. package/dist/cli/commands/auto-link-helper.d.ts +33 -0
  10. package/dist/cli/commands/auto-link-helper.d.ts.map +1 -0
  11. package/dist/cli/commands/auto-link-helper.js +74 -0
  12. package/dist/cli/commands/auto-link-helper.js.map +1 -0
  13. package/dist/cli/commands/crud.d.ts +3 -0
  14. package/dist/cli/commands/crud.d.ts.map +1 -1
  15. package/dist/cli/commands/crud.js +144 -15
  16. package/dist/cli/commands/crud.js.map +1 -1
  17. package/dist/cli/commands/docs.js +2 -2
  18. package/dist/cli/commands/docs.js.map +1 -1
  19. package/dist/cli/commands/document.js +1 -1
  20. package/dist/cli/commands/document.js.map +1 -1
  21. package/dist/cli/commands/entity.js +1 -1
  22. package/dist/cli/commands/entity.js.map +1 -1
  23. package/dist/cli/commands/external-sync.d.ts +18 -0
  24. package/dist/cli/commands/external-sync.d.ts.map +1 -0
  25. package/dist/cli/commands/external-sync.js +2499 -0
  26. package/dist/cli/commands/external-sync.js.map +1 -0
  27. package/dist/cli/commands/library.js +1 -1
  28. package/dist/cli/commands/library.js.map +1 -1
  29. package/dist/cli/commands/message.js +2 -2
  30. package/dist/cli/commands/message.js.map +1 -1
  31. package/dist/cli/commands/serve.d.ts.map +1 -1
  32. package/dist/cli/commands/serve.js +2 -0
  33. package/dist/cli/commands/serve.js.map +1 -1
  34. package/dist/cli/commands/task.d.ts.map +1 -1
  35. package/dist/cli/commands/task.js +7 -4
  36. package/dist/cli/commands/task.js.map +1 -1
  37. package/dist/cli/commands/team.js +1 -1
  38. package/dist/cli/commands/team.js.map +1 -1
  39. package/dist/cli/commands/workflow.js +1 -1
  40. package/dist/cli/commands/workflow.js.map +1 -1
  41. package/dist/cli/runner.d.ts.map +1 -1
  42. package/dist/cli/runner.js +3 -0
  43. package/dist/cli/runner.js.map +1 -1
  44. package/dist/cli/utils/progress.d.ts +30 -0
  45. package/dist/cli/utils/progress.d.ts.map +1 -0
  46. package/dist/cli/utils/progress.js +47 -0
  47. package/dist/cli/utils/progress.js.map +1 -0
  48. package/dist/config/config.d.ts.map +1 -1
  49. package/dist/config/config.js +34 -0
  50. package/dist/config/config.js.map +1 -1
  51. package/dist/config/defaults.d.ts +13 -1
  52. package/dist/config/defaults.d.ts.map +1 -1
  53. package/dist/config/defaults.js +22 -0
  54. package/dist/config/defaults.js.map +1 -1
  55. package/dist/config/file.d.ts.map +1 -1
  56. package/dist/config/file.js +71 -0
  57. package/dist/config/file.js.map +1 -1
  58. package/dist/config/index.d.ts +3 -3
  59. package/dist/config/index.d.ts.map +1 -1
  60. package/dist/config/index.js +2 -2
  61. package/dist/config/index.js.map +1 -1
  62. package/dist/config/merge.d.ts.map +1 -1
  63. package/dist/config/merge.js +52 -1
  64. package/dist/config/merge.js.map +1 -1
  65. package/dist/config/types.d.ts +68 -1
  66. package/dist/config/types.d.ts.map +1 -1
  67. package/dist/config/types.js +33 -0
  68. package/dist/config/types.js.map +1 -1
  69. package/dist/config/validation.d.ts.map +1 -1
  70. package/dist/config/validation.js +64 -1
  71. package/dist/config/validation.js.map +1 -1
  72. package/dist/external-sync/adapters/document-sync-adapter.d.ts +150 -0
  73. package/dist/external-sync/adapters/document-sync-adapter.d.ts.map +1 -0
  74. package/dist/external-sync/adapters/document-sync-adapter.js +325 -0
  75. package/dist/external-sync/adapters/document-sync-adapter.js.map +1 -0
  76. package/dist/external-sync/adapters/task-sync-adapter.d.ts +177 -0
  77. package/dist/external-sync/adapters/task-sync-adapter.d.ts.map +1 -0
  78. package/dist/external-sync/adapters/task-sync-adapter.js +353 -0
  79. package/dist/external-sync/adapters/task-sync-adapter.js.map +1 -0
  80. package/dist/external-sync/auto-link.d.ts +66 -0
  81. package/dist/external-sync/auto-link.d.ts.map +1 -0
  82. package/dist/external-sync/auto-link.js +98 -0
  83. package/dist/external-sync/auto-link.js.map +1 -0
  84. package/dist/external-sync/conflict-resolver.d.ts +170 -0
  85. package/dist/external-sync/conflict-resolver.d.ts.map +1 -0
  86. package/dist/external-sync/conflict-resolver.js +580 -0
  87. package/dist/external-sync/conflict-resolver.js.map +1 -0
  88. package/dist/external-sync/index.d.ts +23 -0
  89. package/dist/external-sync/index.d.ts.map +1 -0
  90. package/dist/external-sync/index.js +24 -0
  91. package/dist/external-sync/index.js.map +1 -0
  92. package/dist/external-sync/provider-registry.d.ts +113 -0
  93. package/dist/external-sync/provider-registry.d.ts.map +1 -0
  94. package/dist/external-sync/provider-registry.js +205 -0
  95. package/dist/external-sync/provider-registry.js.map +1 -0
  96. package/dist/external-sync/providers/folder/folder-document-adapter.d.ts +97 -0
  97. package/dist/external-sync/providers/folder/folder-document-adapter.d.ts.map +1 -0
  98. package/dist/external-sync/providers/folder/folder-document-adapter.js +261 -0
  99. package/dist/external-sync/providers/folder/folder-document-adapter.js.map +1 -0
  100. package/dist/external-sync/providers/folder/folder-fs.d.ts +146 -0
  101. package/dist/external-sync/providers/folder/folder-fs.d.ts.map +1 -0
  102. package/dist/external-sync/providers/folder/folder-fs.js +300 -0
  103. package/dist/external-sync/providers/folder/folder-fs.js.map +1 -0
  104. package/dist/external-sync/providers/folder/folder-provider.d.ts +28 -0
  105. package/dist/external-sync/providers/folder/folder-provider.d.ts.map +1 -0
  106. package/dist/external-sync/providers/folder/folder-provider.js +87 -0
  107. package/dist/external-sync/providers/folder/folder-provider.js.map +1 -0
  108. package/dist/external-sync/providers/folder/index.d.ts +11 -0
  109. package/dist/external-sync/providers/folder/index.d.ts.map +1 -0
  110. package/dist/external-sync/providers/folder/index.js +13 -0
  111. package/dist/external-sync/providers/folder/index.js.map +1 -0
  112. package/dist/external-sync/providers/github/github-api.d.ts +271 -0
  113. package/dist/external-sync/providers/github/github-api.d.ts.map +1 -0
  114. package/dist/external-sync/providers/github/github-api.js +366 -0
  115. package/dist/external-sync/providers/github/github-api.js.map +1 -0
  116. package/dist/external-sync/providers/github/github-field-map.d.ts +76 -0
  117. package/dist/external-sync/providers/github/github-field-map.d.ts.map +1 -0
  118. package/dist/external-sync/providers/github/github-field-map.js +157 -0
  119. package/dist/external-sync/providers/github/github-field-map.js.map +1 -0
  120. package/dist/external-sync/providers/github/github-provider.d.ts +36 -0
  121. package/dist/external-sync/providers/github/github-provider.d.ts.map +1 -0
  122. package/dist/external-sync/providers/github/github-provider.js +212 -0
  123. package/dist/external-sync/providers/github/github-provider.js.map +1 -0
  124. package/dist/external-sync/providers/github/github-task-adapter.d.ts +135 -0
  125. package/dist/external-sync/providers/github/github-task-adapter.d.ts.map +1 -0
  126. package/dist/external-sync/providers/github/github-task-adapter.js +374 -0
  127. package/dist/external-sync/providers/github/github-task-adapter.js.map +1 -0
  128. package/dist/external-sync/providers/github/index.d.ts +12 -0
  129. package/dist/external-sync/providers/github/index.d.ts.map +1 -0
  130. package/dist/external-sync/providers/github/index.js +15 -0
  131. package/dist/external-sync/providers/github/index.js.map +1 -0
  132. package/dist/external-sync/providers/index.d.ts +13 -0
  133. package/dist/external-sync/providers/index.d.ts.map +1 -0
  134. package/dist/external-sync/providers/index.js +15 -0
  135. package/dist/external-sync/providers/index.js.map +1 -0
  136. package/dist/external-sync/providers/linear/index.d.ts +19 -0
  137. package/dist/external-sync/providers/linear/index.d.ts.map +1 -0
  138. package/dist/external-sync/providers/linear/index.js +19 -0
  139. package/dist/external-sync/providers/linear/index.js.map +1 -0
  140. package/dist/external-sync/providers/linear/linear-api.d.ts +252 -0
  141. package/dist/external-sync/providers/linear/linear-api.d.ts.map +1 -0
  142. package/dist/external-sync/providers/linear/linear-api.js +522 -0
  143. package/dist/external-sync/providers/linear/linear-api.js.map +1 -0
  144. package/dist/external-sync/providers/linear/linear-field-map.d.ts +135 -0
  145. package/dist/external-sync/providers/linear/linear-field-map.d.ts.map +1 -0
  146. package/dist/external-sync/providers/linear/linear-field-map.js +338 -0
  147. package/dist/external-sync/providers/linear/linear-field-map.js.map +1 -0
  148. package/dist/external-sync/providers/linear/linear-provider.d.ts +52 -0
  149. package/dist/external-sync/providers/linear/linear-provider.d.ts.map +1 -0
  150. package/dist/external-sync/providers/linear/linear-provider.js +169 -0
  151. package/dist/external-sync/providers/linear/linear-provider.js.map +1 -0
  152. package/dist/external-sync/providers/linear/linear-task-adapter.d.ts +190 -0
  153. package/dist/external-sync/providers/linear/linear-task-adapter.d.ts.map +1 -0
  154. package/dist/external-sync/providers/linear/linear-task-adapter.js +521 -0
  155. package/dist/external-sync/providers/linear/linear-task-adapter.js.map +1 -0
  156. package/dist/external-sync/providers/linear/linear-types.d.ts +114 -0
  157. package/dist/external-sync/providers/linear/linear-types.d.ts.map +1 -0
  158. package/dist/external-sync/providers/linear/linear-types.js +10 -0
  159. package/dist/external-sync/providers/linear/linear-types.js.map +1 -0
  160. package/dist/external-sync/providers/notion/index.d.ts +19 -0
  161. package/dist/external-sync/providers/notion/index.d.ts.map +1 -0
  162. package/dist/external-sync/providers/notion/index.js +20 -0
  163. package/dist/external-sync/providers/notion/index.js.map +1 -0
  164. package/dist/external-sync/providers/notion/notion-api.d.ts +253 -0
  165. package/dist/external-sync/providers/notion/notion-api.d.ts.map +1 -0
  166. package/dist/external-sync/providers/notion/notion-api.js +492 -0
  167. package/dist/external-sync/providers/notion/notion-api.js.map +1 -0
  168. package/dist/external-sync/providers/notion/notion-blocks.d.ts +93 -0
  169. package/dist/external-sync/providers/notion/notion-blocks.d.ts.map +1 -0
  170. package/dist/external-sync/providers/notion/notion-blocks.js +773 -0
  171. package/dist/external-sync/providers/notion/notion-blocks.js.map +1 -0
  172. package/dist/external-sync/providers/notion/notion-document-adapter.d.ts +176 -0
  173. package/dist/external-sync/providers/notion/notion-document-adapter.d.ts.map +1 -0
  174. package/dist/external-sync/providers/notion/notion-document-adapter.js +413 -0
  175. package/dist/external-sync/providers/notion/notion-document-adapter.js.map +1 -0
  176. package/dist/external-sync/providers/notion/notion-provider.d.ts +57 -0
  177. package/dist/external-sync/providers/notion/notion-provider.d.ts.map +1 -0
  178. package/dist/external-sync/providers/notion/notion-provider.js +159 -0
  179. package/dist/external-sync/providers/notion/notion-provider.js.map +1 -0
  180. package/dist/external-sync/providers/notion/notion-types.d.ts +388 -0
  181. package/dist/external-sync/providers/notion/notion-types.d.ts.map +1 -0
  182. package/dist/external-sync/providers/notion/notion-types.js +47 -0
  183. package/dist/external-sync/providers/notion/notion-types.js.map +1 -0
  184. package/dist/external-sync/sync-engine.d.ts +364 -0
  185. package/dist/external-sync/sync-engine.d.ts.map +1 -0
  186. package/dist/external-sync/sync-engine.js +1154 -0
  187. package/dist/external-sync/sync-engine.js.map +1 -0
  188. package/dist/index.d.ts +1 -0
  189. package/dist/index.d.ts.map +1 -1
  190. package/dist/index.js +2 -0
  191. package/dist/index.js.map +1 -1
  192. package/dist/server/index.js +8 -8
  193. package/dist/server/index.js.map +1 -1
  194. package/dist/services/inbox.js +1 -1
  195. package/dist/sync/hash.d.ts +5 -0
  196. package/dist/sync/hash.d.ts.map +1 -1
  197. package/dist/sync/hash.js +21 -2
  198. package/dist/sync/hash.js.map +1 -1
  199. package/package.json +10 -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"}