@imjp/writenex-astro 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 (141) hide show
  1. package/README.md +539 -0
  2. package/dist/chunk-5PM6EQE5.js +151 -0
  3. package/dist/chunk-5PM6EQE5.js.map +1 -0
  4. package/dist/chunk-7XU5X6CW.js +1331 -0
  5. package/dist/chunk-7XU5X6CW.js.map +1 -0
  6. package/dist/chunk-AAOQHQPU.js +574 -0
  7. package/dist/chunk-AAOQHQPU.js.map +1 -0
  8. package/dist/chunk-CF2XXJFF.js +1410 -0
  9. package/dist/chunk-CF2XXJFF.js.map +1 -0
  10. package/dist/chunk-CRPZUUDU.js +52 -0
  11. package/dist/chunk-CRPZUUDU.js.map +1 -0
  12. package/dist/chunk-CYLDJ3HZ.js +310 -0
  13. package/dist/chunk-CYLDJ3HZ.js.map +1 -0
  14. package/dist/chunk-KIKIPIFA.js +1 -0
  15. package/dist/chunk-KIKIPIFA.js.map +1 -0
  16. package/dist/chunk-XNTQTTJU.js +145 -0
  17. package/dist/chunk-XNTQTTJU.js.map +1 -0
  18. package/dist/client/index.css +2 -0
  19. package/dist/client/index.css.map +1 -0
  20. package/dist/client/index.js +375 -0
  21. package/dist/client/index.js.map +1 -0
  22. package/dist/client/styles.css +584 -0
  23. package/dist/client/variables.css +304 -0
  24. package/dist/config/index.d.ts +54 -0
  25. package/dist/config/index.js +38 -0
  26. package/dist/config/index.js.map +1 -0
  27. package/dist/config-BmEdBDo_.d.ts +220 -0
  28. package/dist/content-BWR52vD-.d.ts +64 -0
  29. package/dist/discovery/index.d.ts +310 -0
  30. package/dist/discovery/index.js +38 -0
  31. package/dist/discovery/index.js.map +1 -0
  32. package/dist/errors-C0iYiDTv.d.ts +107 -0
  33. package/dist/filesystem/index.d.ts +1292 -0
  34. package/dist/filesystem/index.js +203 -0
  35. package/dist/filesystem/index.js.map +1 -0
  36. package/dist/image-FP7w5ZIs.d.ts +47 -0
  37. package/dist/index.d.ts +64 -0
  38. package/dist/index.js +151 -0
  39. package/dist/index.js.map +1 -0
  40. package/dist/loader-55LWCXHA.js +12 -0
  41. package/dist/loader-55LWCXHA.js.map +1 -0
  42. package/dist/loader-CrdnaAWR.d.ts +327 -0
  43. package/dist/server/index.d.ts +357 -0
  44. package/dist/server/index.js +37 -0
  45. package/dist/server/index.js.map +1 -0
  46. package/package.json +94 -0
  47. package/src/client/App.tsx +900 -0
  48. package/src/client/components/ConfigPanel/ConfigPanel.css +553 -0
  49. package/src/client/components/ConfigPanel/ConfigPanel.tsx +396 -0
  50. package/src/client/components/ConfigPanel/index.ts +6 -0
  51. package/src/client/components/CreateContentModal/CreateContentModal.css +327 -0
  52. package/src/client/components/CreateContentModal/CreateContentModal.tsx +216 -0
  53. package/src/client/components/CreateContentModal/index.ts +7 -0
  54. package/src/client/components/Editor/Editor.css +885 -0
  55. package/src/client/components/Editor/Editor.tsx +484 -0
  56. package/src/client/components/Editor/ImageDialog.css +344 -0
  57. package/src/client/components/Editor/ImageDialog.tsx +367 -0
  58. package/src/client/components/Editor/LinkDialog.css +326 -0
  59. package/src/client/components/Editor/LinkDialog.tsx +332 -0
  60. package/src/client/components/Editor/index.ts +6 -0
  61. package/src/client/components/FrontmatterForm/FrontmatterForm.css +468 -0
  62. package/src/client/components/FrontmatterForm/FrontmatterForm.tsx +914 -0
  63. package/src/client/components/FrontmatterForm/index.ts +7 -0
  64. package/src/client/components/Header/Header.css +300 -0
  65. package/src/client/components/Header/Header.tsx +300 -0
  66. package/src/client/components/Header/index.ts +7 -0
  67. package/src/client/components/KeyboardShortcuts/KeyboardShortcuts.css +239 -0
  68. package/src/client/components/KeyboardShortcuts/KeyboardShortcuts.tsx +151 -0
  69. package/src/client/components/KeyboardShortcuts/index.ts +6 -0
  70. package/src/client/components/LazyEditor.tsx +75 -0
  71. package/src/client/components/LiveRegion/LiveRegion.css +19 -0
  72. package/src/client/components/LiveRegion/LiveRegion.tsx +60 -0
  73. package/src/client/components/LiveRegion/index.ts +7 -0
  74. package/src/client/components/SearchReplace/SearchReplacePanel.css +300 -0
  75. package/src/client/components/SearchReplace/SearchReplacePanel.tsx +332 -0
  76. package/src/client/components/SearchReplace/index.ts +7 -0
  77. package/src/client/components/SelectCollectionModal/SelectCollectionModal.css +308 -0
  78. package/src/client/components/SelectCollectionModal/SelectCollectionModal.tsx +223 -0
  79. package/src/client/components/SelectCollectionModal/index.ts +7 -0
  80. package/src/client/components/Sidebar/Sidebar.css +570 -0
  81. package/src/client/components/Sidebar/Sidebar.tsx +617 -0
  82. package/src/client/components/Sidebar/index.ts +7 -0
  83. package/src/client/components/SkipLink/SkipLink.css +51 -0
  84. package/src/client/components/SkipLink/SkipLink.tsx +67 -0
  85. package/src/client/components/SkipLink/index.ts +7 -0
  86. package/src/client/components/UnsavedChangesModal/UnsavedChangesModal.css +233 -0
  87. package/src/client/components/UnsavedChangesModal/UnsavedChangesModal.tsx +160 -0
  88. package/src/client/components/UnsavedChangesModal/index.ts +1 -0
  89. package/src/client/components/VersionHistory/DiffViewer.css +430 -0
  90. package/src/client/components/VersionHistory/DiffViewer.tsx +383 -0
  91. package/src/client/components/VersionHistory/VersionActions.css +318 -0
  92. package/src/client/components/VersionHistory/VersionActions.tsx +277 -0
  93. package/src/client/components/VersionHistory/VersionHistoryPanel.css +369 -0
  94. package/src/client/components/VersionHistory/VersionHistoryPanel.tsx +469 -0
  95. package/src/client/components/VersionHistory/index.ts +9 -0
  96. package/src/client/context/ApiContext.tsx +154 -0
  97. package/src/client/context/ThemeContext.tsx +172 -0
  98. package/src/client/hooks/useAnnounce.ts +201 -0
  99. package/src/client/hooks/useApi.ts +374 -0
  100. package/src/client/hooks/useArrowNavigation.ts +286 -0
  101. package/src/client/hooks/useAutosave.ts +241 -0
  102. package/src/client/hooks/useFocusTrap.ts +178 -0
  103. package/src/client/hooks/useKeyboardShortcuts.ts +203 -0
  104. package/src/client/hooks/useSearch.ts +206 -0
  105. package/src/client/hooks/useVersionHistory.ts +451 -0
  106. package/src/client/index.tsx +70 -0
  107. package/src/client/styles.css +584 -0
  108. package/src/client/utils/focus.ts +57 -0
  109. package/src/client/utils/openInEditor.ts +130 -0
  110. package/src/client/variables.css +304 -0
  111. package/src/config/defaults.ts +109 -0
  112. package/src/config/index.ts +32 -0
  113. package/src/config/loader.ts +174 -0
  114. package/src/config/schema.ts +161 -0
  115. package/src/core/constants.ts +39 -0
  116. package/src/core/errors.ts +739 -0
  117. package/src/core/index.ts +11 -0
  118. package/src/discovery/collections.ts +216 -0
  119. package/src/discovery/index.ts +33 -0
  120. package/src/discovery/patterns.ts +702 -0
  121. package/src/discovery/schema.ts +453 -0
  122. package/src/filesystem/images.ts +798 -0
  123. package/src/filesystem/index.ts +107 -0
  124. package/src/filesystem/reader.ts +452 -0
  125. package/src/filesystem/version-config.ts +390 -0
  126. package/src/filesystem/versions.ts +1339 -0
  127. package/src/filesystem/watcher.ts +226 -0
  128. package/src/filesystem/writer.ts +540 -0
  129. package/src/index.ts +61 -0
  130. package/src/integration.ts +228 -0
  131. package/src/server/assets.ts +254 -0
  132. package/src/server/cache.ts +355 -0
  133. package/src/server/index.ts +33 -0
  134. package/src/server/middleware.ts +209 -0
  135. package/src/server/routes.ts +1428 -0
  136. package/src/types/api.ts +61 -0
  137. package/src/types/config.ts +134 -0
  138. package/src/types/content.ts +64 -0
  139. package/src/types/image.ts +48 -0
  140. package/src/types/index.ts +58 -0
  141. package/src/types/version.ts +117 -0
@@ -0,0 +1,1292 @@
1
+ import { C as ContentItem, a as ContentSummary } from '../content-BWR52vD-.js';
2
+ import { V as VersionHistoryConfig, d as SaveVersionOptions, e as VersionResult, f as VersionEntry, g as Version, R as RestoreVersionOptions, h as RestoreResult, i as VersionManifest, I as ImageConfig } from '../config-BmEdBDo_.js';
3
+ import { C as ContentConflictError } from '../errors-C0iYiDTv.js';
4
+ import { I as ImageDiscoveryOptions, a as ImageDiscoveryResult, D as DiscoveredImage } from '../image-FP7w5ZIs.js';
5
+
6
+ /**
7
+ * @fileoverview Filesystem reader for content collections
8
+ *
9
+ * This module provides functions for reading content files from the filesystem,
10
+ * parsing frontmatter, and extracting content metadata.
11
+ *
12
+ * ## Features:
13
+ * - Read individual content files with frontmatter parsing
14
+ * - List all content files in a collection
15
+ * - Generate content summaries for listing
16
+ * - Support for .md and .mdx files
17
+ *
18
+ * @module @writenex/astro/filesystem/reader
19
+ */
20
+
21
+ /**
22
+ * Options for reading content
23
+ */
24
+ interface ReadContentOptions {
25
+ /** Include draft content in listings */
26
+ includeDrafts?: boolean;
27
+ /** Sort field for listings */
28
+ sortBy?: string;
29
+ /** Sort order */
30
+ sortOrder?: "asc" | "desc";
31
+ }
32
+ /**
33
+ * Result of reading a content file
34
+ */
35
+ interface ReadFileResult {
36
+ /** Whether the read was successful */
37
+ success: boolean;
38
+ /** The content item (if successful) */
39
+ content?: ContentItem;
40
+ /** Error message (if failed) */
41
+ error?: string;
42
+ }
43
+ /**
44
+ * Check if a file is a content file based on extension
45
+ *
46
+ * @param filename - The filename to check
47
+ * @returns True if the file is a content file
48
+ */
49
+ declare function isContentFile(filename: string): boolean;
50
+ /**
51
+ * Extract slug from a content file path
52
+ *
53
+ * Handles various file patterns:
54
+ * - `my-post.md` -> `my-post`
55
+ * - `2024-01-15-my-post.md` -> `2024-01-15-my-post`
56
+ * - `my-post/index.md` -> `my-post`
57
+ *
58
+ * @param filePath - Path to the content file
59
+ * @param collectionPath - Path to the collection directory
60
+ * @returns The extracted slug
61
+ */
62
+ declare function extractSlug(filePath: string, collectionPath: string): string;
63
+ /**
64
+ * Generate an excerpt from markdown content
65
+ *
66
+ * @param body - The markdown body content
67
+ * @param maxLength - Maximum excerpt length
68
+ * @returns The generated excerpt
69
+ */
70
+ declare function generateExcerpt(body: string, maxLength?: number): string;
71
+ /**
72
+ * Read and parse a single content file
73
+ *
74
+ * @param filePath - Absolute path to the content file
75
+ * @param collectionPath - Path to the collection directory
76
+ * @returns ReadFileResult with the parsed content or error
77
+ *
78
+ * @example
79
+ * ```typescript
80
+ * const result = await readContentFile(
81
+ * '/project/src/content/blog/my-post.md',
82
+ * '/project/src/content/blog'
83
+ * );
84
+ *
85
+ * if (result.success) {
86
+ * console.log(result.content.frontmatter.title);
87
+ * }
88
+ * ```
89
+ */
90
+ declare function readContentFile(filePath: string, collectionPath: string): Promise<ReadFileResult>;
91
+ /**
92
+ * Read all content files in a collection
93
+ *
94
+ * @param collectionPath - Absolute path to the collection directory
95
+ * @param options - Read options
96
+ * @returns Array of content items
97
+ *
98
+ * @example
99
+ * ```typescript
100
+ * const items = await readCollection('/project/src/content/blog', {
101
+ * includeDrafts: false,
102
+ * sortBy: 'pubDate',
103
+ * sortOrder: 'desc',
104
+ * });
105
+ * ```
106
+ */
107
+ declare function readCollection(collectionPath: string, options?: ReadContentOptions): Promise<ContentItem[]>;
108
+ /**
109
+ * Convert a content item to a summary for listing
110
+ *
111
+ * @param item - The full content item
112
+ * @returns Content summary with essential fields
113
+ */
114
+ declare function toContentSummary(item: ContentItem): ContentSummary;
115
+ /**
116
+ * Get content summaries for a collection
117
+ *
118
+ * @param collectionPath - Absolute path to the collection directory
119
+ * @param options - Read options
120
+ * @returns Array of content summaries
121
+ */
122
+ declare function getCollectionSummaries(collectionPath: string, options?: ReadContentOptions): Promise<ContentSummary[]>;
123
+ /**
124
+ * Get the count of content files in a collection
125
+ *
126
+ * @param collectionPath - Absolute path to the collection directory
127
+ * @returns Number of content files
128
+ */
129
+ declare function getCollectionCount(collectionPath: string): Promise<number>;
130
+ /**
131
+ * Check if a collection directory exists and contains content
132
+ *
133
+ * @param collectionPath - Absolute path to the collection directory
134
+ * @returns Object with exists and hasContent flags
135
+ */
136
+ declare function checkCollection(collectionPath: string): Promise<{
137
+ exists: boolean;
138
+ hasContent: boolean;
139
+ count: number;
140
+ }>;
141
+ /**
142
+ * Get file stats for a content file
143
+ *
144
+ * @param filePath - Path to the content file
145
+ * @returns File stats or null if file doesn't exist
146
+ */
147
+ declare function getFileStats(filePath: string): Promise<{
148
+ size: number;
149
+ mtime: Date;
150
+ ctime: Date;
151
+ } | null>;
152
+ /**
153
+ * Get the file path for a content item by ID
154
+ *
155
+ * Searches for the content file in the collection directory,
156
+ * handling different content structures:
157
+ * - Folder-based: `slug/index.md` or `slug/index.mdx`
158
+ * - Flat file: `slug.md` or `slug.mdx`
159
+ *
160
+ * @param collectionPath - Path to the collection directory
161
+ * @param contentId - Content ID (slug)
162
+ * @returns File path if found, null otherwise
163
+ *
164
+ * @example
165
+ * ```typescript
166
+ * const filePath = getContentFilePath('/project/src/content/blog', 'my-post');
167
+ * // Returns: '/project/src/content/blog/my-post.md' or
168
+ * // '/project/src/content/blog/my-post/index.md'
169
+ * ```
170
+ */
171
+ declare function getContentFilePath(collectionPath: string, contentId: string): string | null;
172
+
173
+ /**
174
+ * @fileoverview Filesystem writer for content operations
175
+ *
176
+ * This module provides functions for creating, updating, and deleting
177
+ * content files in Astro content collections.
178
+ *
179
+ * ## Features:
180
+ * - Create new content files with frontmatter
181
+ * - Update existing content files
182
+ * - Delete content files
183
+ * - Generate unique slugs to avoid collisions
184
+ * - Support for different file patterns (flat, folder-based, date-prefixed)
185
+ * - Automatic version history creation before updates
186
+ *
187
+ * @module @writenex/astro/filesystem/writer
188
+ */
189
+
190
+ /**
191
+ * Options for creating content
192
+ */
193
+ interface CreateContentOptions {
194
+ /** Frontmatter data */
195
+ frontmatter: Record<string, unknown>;
196
+ /** Markdown body content */
197
+ body: string;
198
+ /** Custom slug (optional, generated from title if not provided) */
199
+ slug?: string;
200
+ /** File pattern for the collection (e.g., "{slug}/index.md", "{date}-{slug}.md") */
201
+ filePattern?: string;
202
+ /** Custom token values to override automatic resolution */
203
+ customTokens?: Record<string, string>;
204
+ }
205
+ /**
206
+ * Options for updating content
207
+ */
208
+ interface UpdateContentOptions {
209
+ /** Updated frontmatter data */
210
+ frontmatter?: Record<string, unknown>;
211
+ /** Updated markdown body content */
212
+ body?: string;
213
+ /** Project root for version history (required for version creation) */
214
+ projectRoot?: string;
215
+ /** Collection name for version history */
216
+ collection?: string;
217
+ /** Version history configuration */
218
+ versionHistoryConfig?: Required<VersionHistoryConfig>;
219
+ /**
220
+ * Expected modification time for conflict detection.
221
+ * If provided and the file's mtime differs, the update will fail with a conflict error.
222
+ */
223
+ expectedMtime?: number;
224
+ }
225
+ /**
226
+ * Result of a write operation
227
+ */
228
+ interface WriteResult {
229
+ /** Whether the operation was successful */
230
+ success: boolean;
231
+ /** The content ID (slug) */
232
+ id?: string;
233
+ /** The file path */
234
+ path?: string;
235
+ /** Error message if failed */
236
+ error?: string;
237
+ /** New modification time after write (for conflict detection) */
238
+ mtime?: number;
239
+ /** Conflict error if update failed due to external modification */
240
+ conflict?: ContentConflictError;
241
+ }
242
+ /**
243
+ * Generate a URL-safe slug from a string
244
+ *
245
+ * @param text - Text to slugify
246
+ * @returns URL-safe slug
247
+ */
248
+ declare function generateSlug(text: string): string;
249
+ /**
250
+ * Generate a unique slug that doesn't conflict with existing files
251
+ *
252
+ * @param baseSlug - The base slug to start with
253
+ * @param collectionPath - Path to the collection directory
254
+ * @param filePattern - File pattern (default: "{slug}.md")
255
+ * @returns A unique slug
256
+ */
257
+ declare function generateUniqueSlug(baseSlug: string, collectionPath: string, filePattern?: string): Promise<string>;
258
+ /**
259
+ * Create a new content file in a collection
260
+ *
261
+ * Supports various file patterns with automatic token resolution:
262
+ * - `{slug}.md` - Simple flat structure (default)
263
+ * - `{slug}/index.md` - Folder-based content
264
+ * - `{date}-{slug}.md` - Date-prefixed naming
265
+ * - `{year}/{slug}.md` - Year folder structure
266
+ * - `{year}/{month}/{slug}.md` - Year/month folder structure
267
+ * - `{year}/{month}/{day}/{slug}.md` - Full date folder structure
268
+ * - `{lang}/{slug}.md` - Language-prefixed (i18n)
269
+ * - `{category}/{slug}.md` - Category folder structure
270
+ * - `{author}/{slug}.md` - Author folder structure
271
+ * - Any custom pattern with tokens from frontmatter
272
+ *
273
+ * Token resolution priority:
274
+ * 1. Custom tokens (explicitly provided via customTokens)
275
+ * 2. Known token resolvers (date, year, month, lang, category, etc.)
276
+ * 3. Frontmatter values (for custom tokens)
277
+ * 4. Default values
278
+ *
279
+ * @param collectionPath - Absolute path to the collection directory
280
+ * @param options - Content creation options
281
+ * @returns WriteResult with success status and file info
282
+ *
283
+ * @example
284
+ * ```typescript
285
+ * // Flat structure
286
+ * const result = await createContent('/project/src/content/blog', {
287
+ * frontmatter: { title: 'My New Post', pubDate: new Date() },
288
+ * body: '# Hello World',
289
+ * });
290
+ *
291
+ * // Folder-based structure
292
+ * const result = await createContent('/project/src/content/blog', {
293
+ * frontmatter: { title: 'My New Post', pubDate: new Date() },
294
+ * body: '# Hello World',
295
+ * filePattern: '{slug}/index.md',
296
+ * });
297
+ *
298
+ * // i18n structure with custom token
299
+ * const result = await createContent('/project/src/content/blog', {
300
+ * frontmatter: { title: 'My New Post', lang: 'id' },
301
+ * body: '# Hello World',
302
+ * filePattern: '{lang}/{slug}.md',
303
+ * });
304
+ *
305
+ * // Custom pattern with explicit token
306
+ * const result = await createContent('/project/src/content/blog', {
307
+ * frontmatter: { title: 'My New Post' },
308
+ * body: '# Hello World',
309
+ * filePattern: '{category}/{slug}.md',
310
+ * customTokens: { category: 'tutorials' },
311
+ * });
312
+ * ```
313
+ */
314
+ declare function createContent(collectionPath: string, options: CreateContentOptions): Promise<WriteResult>;
315
+ /**
316
+ * Update an existing content file
317
+ *
318
+ * Creates a version snapshot of the current content before updating
319
+ * when version history is configured. Skips both version creation and
320
+ * file write if the new content is identical to the current file content.
321
+ * Version creation errors are logged but do not fail the save operation.
322
+ *
323
+ * @param filePath - Absolute path to the content file
324
+ * @param collectionPath - Path to the collection directory
325
+ * @param options - Update options including version history config
326
+ * @returns WriteResult with success status
327
+ *
328
+ * @example
329
+ * ```typescript
330
+ * const result = await updateContent(
331
+ * '/project/src/content/blog/my-post.md',
332
+ * '/project/src/content/blog',
333
+ * {
334
+ * frontmatter: { title: 'Updated Title' },
335
+ * body: '# Updated Content',
336
+ * projectRoot: '/project',
337
+ * collection: 'blog',
338
+ * versionHistoryConfig: { enabled: true, maxVersions: 20, storagePath: '.writenex/versions' },
339
+ * }
340
+ * );
341
+ * ```
342
+ */
343
+ declare function updateContent(filePath: string, collectionPath: string, options: UpdateContentOptions): Promise<WriteResult>;
344
+ /**
345
+ * Delete a content file
346
+ *
347
+ * @param filePath - Absolute path to the content file
348
+ * @returns WriteResult with success status
349
+ *
350
+ * @example
351
+ * ```typescript
352
+ * const result = await deleteContent('/project/src/content/blog/my-post.md');
353
+ * ```
354
+ */
355
+ declare function deleteContent(filePath: string): Promise<WriteResult>;
356
+
357
+ /**
358
+ * @fileoverview File watcher for detecting external changes
359
+ *
360
+ * This module provides file watching capabilities to detect when
361
+ * content files are modified outside of the Writenex editor
362
+ * (e.g., in VS Code or another editor).
363
+ *
364
+ * @module @writenex/astro/filesystem/watcher
365
+ */
366
+ /**
367
+ * File change event types
368
+ */
369
+ type FileChangeType = "add" | "change" | "unlink";
370
+ /**
371
+ * File change event
372
+ */
373
+ interface FileChangeEvent {
374
+ type: FileChangeType;
375
+ path: string;
376
+ collection: string;
377
+ }
378
+ /**
379
+ * Watcher options
380
+ */
381
+ interface WatcherOptions {
382
+ /** Callback when a file changes */
383
+ onChange?: (event: FileChangeEvent) => void;
384
+ /** Debounce delay in milliseconds */
385
+ debounceMs?: number;
386
+ /** Patterns to ignore */
387
+ ignored?: string[];
388
+ }
389
+ /**
390
+ * Content file watcher
391
+ *
392
+ * Watches the src/content directory for changes and emits events
393
+ * when files are added, modified, or deleted.
394
+ */
395
+ declare class ContentWatcher {
396
+ private watcher;
397
+ private projectRoot;
398
+ private contentDir;
399
+ private options;
400
+ private debounceTimers;
401
+ constructor(projectRoot: string, contentDir?: string, options?: WatcherOptions);
402
+ /**
403
+ * Start watching for file changes
404
+ */
405
+ start(): void;
406
+ /**
407
+ * Stop watching for file changes
408
+ */
409
+ stop(): Promise<void>;
410
+ /**
411
+ * Handle a file change event
412
+ */
413
+ private handleChange;
414
+ /**
415
+ * Emit a file change event
416
+ */
417
+ private emitChange;
418
+ /**
419
+ * Check if the watcher is running
420
+ */
421
+ isWatching(): boolean;
422
+ }
423
+ /**
424
+ * Track file modification times for conflict detection
425
+ */
426
+ declare class FileModificationTracker {
427
+ private mtimes;
428
+ /**
429
+ * Record the current modification time of a file
430
+ */
431
+ track(filePath: string): Promise<void>;
432
+ /**
433
+ * Check if a file has been modified externally
434
+ */
435
+ hasExternalChanges(filePath: string): Promise<boolean>;
436
+ /**
437
+ * Clear tracking for a file
438
+ */
439
+ untrack(filePath: string): void;
440
+ /**
441
+ * Clear all tracking
442
+ */
443
+ clear(): void;
444
+ }
445
+ /**
446
+ * Create a content watcher instance
447
+ */
448
+ declare function createContentWatcher(projectRoot: string, options?: WatcherOptions): ContentWatcher;
449
+
450
+ /**
451
+ * @fileoverview Version history management for content files
452
+ *
453
+ * This module provides functions for creating, reading, and managing
454
+ * version history (shadow copies) of content files. Versions are stored
455
+ * as markdown files in a hidden directory structure with a JSON manifest
456
+ * tracking metadata.
457
+ *
458
+ * ## Storage Structure:
459
+ * ```
460
+ * .writenex/versions/
461
+ * ├── .gitignore # Contains "*" to exclude from Git
462
+ * └── {collection}/
463
+ * └── {contentId}/
464
+ * ├── manifest.json # Version metadata
465
+ * └── {timestamp}.md # Version files
466
+ * ```
467
+ *
468
+ * @module @writenex/astro/filesystem/versions
469
+ * @see {@link VersionEntry} - Version metadata type
470
+ * @see {@link VersionManifest} - Manifest structure type
471
+ */
472
+
473
+ /**
474
+ * Generate a unique version ID based on current timestamp with random suffix.
475
+ *
476
+ * The ID is an ISO-8601 timestamp with colons replaced by hyphens,
477
+ * plus a 4-character random suffix to ensure uniqueness even when
478
+ * multiple versions are created within the same millisecond.
479
+ *
480
+ * Format: YYYY-MM-DDTHH-MM-SS.mmmZ-xxxx
481
+ * Where xxxx is a random alphanumeric suffix.
482
+ *
483
+ * @returns Version ID string (e.g., "2024-12-11T10-30-00.000Z-a1b2")
484
+ *
485
+ * @example
486
+ * ```typescript
487
+ * const id = generateVersionId();
488
+ * // Returns: "2024-12-11T10-30-00.000Z-a1b2"
489
+ * ```
490
+ */
491
+ declare function generateVersionId(): string;
492
+ /**
493
+ * Parse a version ID back to a Date object.
494
+ *
495
+ * Handles both old format (without suffix) and new format (with random suffix).
496
+ *
497
+ * @param versionId - Version ID string
498
+ * @returns Date object or null if invalid
499
+ */
500
+ declare function parseVersionId(versionId: string): Date | null;
501
+ /**
502
+ * Get the storage path for version files of a content item.
503
+ *
504
+ * @param projectRoot - Absolute path to project root
505
+ * @param collection - Collection name
506
+ * @param contentId - Content item ID (slug)
507
+ * @param config - Version history configuration
508
+ * @returns Absolute path to version storage directory
509
+ *
510
+ * @example
511
+ * ```typescript
512
+ * const path = getVersionStoragePath(
513
+ * '/project',
514
+ * 'blog',
515
+ * 'my-post',
516
+ * { storagePath: '.writenex/versions' }
517
+ * );
518
+ * // Returns: "/project/.writenex/versions/blog/my-post"
519
+ * ```
520
+ */
521
+ declare function getVersionStoragePath(projectRoot: string, collection: string, contentId: string, config: Required<VersionHistoryConfig>): string;
522
+ /**
523
+ * Get the path to a specific version file.
524
+ *
525
+ * @param storagePath - Version storage directory path
526
+ * @param versionId - Version ID
527
+ * @returns Absolute path to version file
528
+ */
529
+ declare function getVersionFilePath(storagePath: string, versionId: string): string;
530
+ /**
531
+ * Get the path to the manifest file for a content item.
532
+ *
533
+ * @param storagePath - Version storage directory path
534
+ * @returns Absolute path to manifest file
535
+ */
536
+ declare function getManifestPath(storagePath: string): string;
537
+ /**
538
+ * Generate a preview string from content.
539
+ *
540
+ * Extracts the first 100 characters of the content body,
541
+ * stripping frontmatter if present.
542
+ *
543
+ * @param content - Full markdown content
544
+ * @returns Preview string (max 100 characters)
545
+ *
546
+ * @example
547
+ * ```typescript
548
+ * const preview = generatePreview("---\ntitle: Test\n---\n\n# Hello World\n\nThis is content.");
549
+ * // Returns: "# Hello World\n\nThis is content."
550
+ * ```
551
+ */
552
+ declare function generatePreview(content: string): string;
553
+ /**
554
+ * Ensure the .gitignore file exists in the version storage root.
555
+ *
556
+ * Creates a .gitignore file with "*" pattern to exclude all version
557
+ * files from Git tracking.
558
+ *
559
+ * @param projectRoot - Absolute path to project root
560
+ * @param config - Version history configuration
561
+ *
562
+ * @example
563
+ * ```typescript
564
+ * await ensureGitignore('/project', { storagePath: '.writenex/versions' });
565
+ * // Creates: /project/.writenex/versions/.gitignore with content "*"
566
+ * ```
567
+ */
568
+ declare function ensureGitignore(projectRoot: string, config: Required<VersionHistoryConfig>): Promise<void>;
569
+ /**
570
+ * Ensure the version storage directory exists for a content item.
571
+ *
572
+ * @param storagePath - Version storage directory path
573
+ */
574
+ declare function ensureStorageDirectory(storagePath: string): Promise<void>;
575
+ /**
576
+ * Read the version manifest for a content item.
577
+ *
578
+ * @param storagePath - Version storage directory path
579
+ * @returns Version manifest or null if not found/corrupted
580
+ *
581
+ * @example
582
+ * ```typescript
583
+ * const manifest = await readManifest('/project/.writenex/versions/blog/my-post');
584
+ * if (manifest) {
585
+ * console.log(`Found ${manifest.versions.length} versions`);
586
+ * }
587
+ * ```
588
+ */
589
+ declare function readManifest(storagePath: string): Promise<VersionManifest | null>;
590
+ /**
591
+ * Write the version manifest for a content item.
592
+ *
593
+ * @param storagePath - Version storage directory path
594
+ * @param manifest - Version manifest to write
595
+ *
596
+ * @example
597
+ * ```typescript
598
+ * await writeManifest('/project/.writenex/versions/blog/my-post', {
599
+ * contentId: 'my-post',
600
+ * collection: 'blog',
601
+ * versions: [],
602
+ * updatedAt: new Date().toISOString(),
603
+ * });
604
+ * ```
605
+ */
606
+ declare function writeManifest(storagePath: string, manifest: VersionManifest): Promise<void>;
607
+ /**
608
+ * Create an empty manifest for a content item.
609
+ *
610
+ * @param collection - Collection name
611
+ * @param contentId - Content item ID
612
+ * @returns New empty manifest
613
+ */
614
+ declare function createEmptyManifest(collection: string, contentId: string): VersionManifest;
615
+ /**
616
+ * Recover manifest by scanning version files in the storage directory.
617
+ *
618
+ * This function rebuilds the manifest from existing version files
619
+ * when the manifest is corrupted or missing.
620
+ *
621
+ * @param storagePath - Version storage directory path
622
+ * @param collection - Collection name
623
+ * @param contentId - Content item ID
624
+ * @returns Recovered manifest
625
+ *
626
+ * @example
627
+ * ```typescript
628
+ * const manifest = await recoverManifest(
629
+ * '/project/.writenex/versions/blog/my-post',
630
+ * 'blog',
631
+ * 'my-post'
632
+ * );
633
+ * ```
634
+ */
635
+ declare function recoverManifest(storagePath: string, collection: string, contentId: string): Promise<VersionManifest>;
636
+ /**
637
+ * Get or recover manifest for a content item.
638
+ *
639
+ * Attempts to read existing manifest, falls back to recovery if corrupted.
640
+ *
641
+ * @param storagePath - Version storage directory path
642
+ * @param collection - Collection name
643
+ * @param contentId - Content item ID
644
+ * @returns Version manifest
645
+ */
646
+ declare function getOrRecoverManifest(storagePath: string, collection: string, contentId: string): Promise<VersionManifest>;
647
+ /**
648
+ * Save a version snapshot of content.
649
+ *
650
+ * Creates a new version file with the provided content and updates
651
+ * the manifest. Automatically prunes old versions if the limit is exceeded.
652
+ *
653
+ * @param projectRoot - Absolute path to project root
654
+ * @param collection - Collection name
655
+ * @param contentId - Content item ID (slug)
656
+ * @param content - Full markdown content to save
657
+ * @param config - Version history configuration
658
+ * @param options - Save options
659
+ * @returns Result of the save operation
660
+ *
661
+ * @example
662
+ * ```typescript
663
+ * const result = await saveVersion(
664
+ * '/project',
665
+ * 'blog',
666
+ * 'my-post',
667
+ * '---\ntitle: My Post\n---\n\nContent here...',
668
+ * { enabled: true, maxVersions: 20, storagePath: '.writenex/versions' }
669
+ * );
670
+ *
671
+ * if (result.success) {
672
+ * console.log(`Created version: ${result.version?.id}`);
673
+ * }
674
+ * ```
675
+ */
676
+ declare function saveVersion(projectRoot: string, collection: string, contentId: string, content: string, config: Required<VersionHistoryConfig>, options?: SaveVersionOptions): Promise<VersionResult>;
677
+ /**
678
+ * Get all versions for a content item.
679
+ *
680
+ * Returns versions sorted by timestamp in descending order (newest first).
681
+ * Handles missing or corrupted manifests gracefully.
682
+ *
683
+ * @param projectRoot - Absolute path to project root
684
+ * @param collection - Collection name
685
+ * @param contentId - Content item ID (slug)
686
+ * @param config - Version history configuration
687
+ * @returns Array of version entries
688
+ *
689
+ * @example
690
+ * ```typescript
691
+ * const versions = await getVersions(
692
+ * '/project',
693
+ * 'blog',
694
+ * 'my-post',
695
+ * { enabled: true, maxVersions: 20, storagePath: '.writenex/versions' }
696
+ * );
697
+ *
698
+ * console.log(`Found ${versions.length} versions`);
699
+ * ```
700
+ */
701
+ declare function getVersions(projectRoot: string, collection: string, contentId: string, config: Required<VersionHistoryConfig>): Promise<VersionEntry[]>;
702
+ /**
703
+ * Get a specific version with full content.
704
+ *
705
+ * Reads the version file and parses it to return structured data
706
+ * with frontmatter and body separated.
707
+ *
708
+ * @param projectRoot - Absolute path to project root
709
+ * @param collection - Collection name
710
+ * @param contentId - Content item ID (slug)
711
+ * @param versionId - Version ID to retrieve
712
+ * @param config - Version history configuration
713
+ * @returns Full version data or null if not found
714
+ *
715
+ * @example
716
+ * ```typescript
717
+ * const version = await getVersion(
718
+ * '/project',
719
+ * 'blog',
720
+ * 'my-post',
721
+ * '2024-12-11T10-30-00-000Z',
722
+ * { enabled: true, maxVersions: 20, storagePath: '.writenex/versions' }
723
+ * );
724
+ *
725
+ * if (version) {
726
+ * console.log(`Title: ${version.frontmatter.title}`);
727
+ * console.log(`Body: ${version.body}`);
728
+ * }
729
+ * ```
730
+ */
731
+ declare function getVersion(projectRoot: string, collection: string, contentId: string, versionId: string, config: Required<VersionHistoryConfig>): Promise<Version | null>;
732
+ /**
733
+ * Delete a specific version.
734
+ *
735
+ * Removes the version file from the filesystem and updates the manifest.
736
+ *
737
+ * @param projectRoot - Absolute path to project root
738
+ * @param collection - Collection name
739
+ * @param contentId - Content item ID (slug)
740
+ * @param versionId - Version ID to delete
741
+ * @param config - Version history configuration
742
+ * @returns Result of the delete operation
743
+ *
744
+ * @example
745
+ * ```typescript
746
+ * const result = await deleteVersion(
747
+ * '/project',
748
+ * 'blog',
749
+ * 'my-post',
750
+ * '2024-12-11T10-30-00-000Z',
751
+ * { enabled: true, maxVersions: 20, storagePath: '.writenex/versions' }
752
+ * );
753
+ *
754
+ * if (result.success) {
755
+ * console.log('Version deleted');
756
+ * }
757
+ * ```
758
+ */
759
+ declare function deleteVersion(projectRoot: string, collection: string, contentId: string, versionId: string, config: Required<VersionHistoryConfig>): Promise<VersionResult>;
760
+ /**
761
+ * Clear all versions for a content item.
762
+ *
763
+ * Deletes all version files and resets the manifest to empty state.
764
+ *
765
+ * @param projectRoot - Absolute path to project root
766
+ * @param collection - Collection name
767
+ * @param contentId - Content item ID (slug)
768
+ * @param config - Version history configuration
769
+ * @returns Result of the clear operation
770
+ *
771
+ * @example
772
+ * ```typescript
773
+ * const result = await clearVersions(
774
+ * '/project',
775
+ * 'blog',
776
+ * 'my-post',
777
+ * { enabled: true, maxVersions: 20, storagePath: '.writenex/versions' }
778
+ * );
779
+ *
780
+ * if (result.success) {
781
+ * console.log('All versions cleared');
782
+ * }
783
+ * ```
784
+ */
785
+ declare function clearVersions(projectRoot: string, collection: string, contentId: string, config: Required<VersionHistoryConfig>): Promise<VersionResult>;
786
+ declare function pruneVersions(projectRoot: string, collection: string, contentId: string, config: Required<VersionHistoryConfig>): Promise<VersionResult>;
787
+ /**
788
+ * Restore a version to current content.
789
+ *
790
+ * This function:
791
+ * 1. Creates a safety snapshot of the current content before restoring
792
+ * 2. Reads the version content to restore
793
+ * 3. Overwrites the current content file with the version content
794
+ *
795
+ * Note: Cache invalidation should be handled by the caller (API route)
796
+ * since the cache is managed at the server level.
797
+ *
798
+ * @param projectRoot - Absolute path to project root
799
+ * @param collection - Collection name
800
+ * @param contentId - Content item ID (slug)
801
+ * @param versionId - Version ID to restore
802
+ * @param contentFilePath - Absolute path to the current content file
803
+ * @param config - Version history configuration
804
+ * @param options - Restore options
805
+ * @returns Result of the restore operation
806
+ *
807
+ * @example
808
+ * ```typescript
809
+ * const result = await restoreVersion(
810
+ * '/project',
811
+ * 'blog',
812
+ * 'my-post',
813
+ * '2024-12-11T10-30-00-000Z',
814
+ * '/project/src/content/blog/my-post.md',
815
+ * { enabled: true, maxVersions: 20, storagePath: '.writenex/versions' }
816
+ * );
817
+ *
818
+ * if (result.success) {
819
+ * console.log('Restored content:', result.content);
820
+ * if (result.safetySnapshot) {
821
+ * console.log('Safety snapshot created:', result.safetySnapshot.id);
822
+ * }
823
+ * }
824
+ * ```
825
+ */
826
+ declare function restoreVersion(projectRoot: string, collection: string, contentId: string, versionId: string, contentFilePath: string, config: Required<VersionHistoryConfig>, options?: RestoreVersionOptions): Promise<RestoreResult>;
827
+
828
+ /**
829
+ * @fileoverview Config-aware wrappers for version history operations
830
+ *
831
+ * This module provides wrapper functions that automatically apply configuration
832
+ * defaults and check the enabled flag before performing version operations.
833
+ * These wrappers simplify usage by accepting partial configuration and handling
834
+ * all the configuration resolution internally.
835
+ *
836
+ * @module @writenex/astro/filesystem/version-config
837
+ * @see {@link saveVersion} - Core save function
838
+ * @see {@link getVersions} - Core list function
839
+ */
840
+
841
+ /**
842
+ * Resolve version history configuration with defaults applied.
843
+ *
844
+ * Takes a partial configuration and merges it with defaults to produce
845
+ * a complete configuration object.
846
+ *
847
+ * @param config - Partial version history configuration
848
+ * @returns Complete configuration with all defaults applied
849
+ *
850
+ * @example
851
+ * ```typescript
852
+ * const resolved = resolveVersionConfig({ maxVersions: 50 });
853
+ * // Returns: { enabled: true, maxVersions: 50, storagePath: '.writenex/versions' }
854
+ * ```
855
+ */
856
+ declare function resolveVersionConfig(config?: VersionHistoryConfig): Required<VersionHistoryConfig>;
857
+ /**
858
+ * Check if version history is enabled in the configuration.
859
+ *
860
+ * @param config - Version history configuration (partial or full)
861
+ * @returns True if version history is enabled
862
+ *
863
+ * @example
864
+ * ```typescript
865
+ * if (isVersionHistoryEnabled({ enabled: false })) {
866
+ * // This won't execute
867
+ * }
868
+ * ```
869
+ */
870
+ declare function isVersionHistoryEnabled(config?: VersionHistoryConfig): boolean;
871
+ /**
872
+ * Save a version with automatic configuration resolution.
873
+ *
874
+ * This wrapper automatically applies configuration defaults and checks
875
+ * the enabled flag before delegating to the core saveVersion function.
876
+ *
877
+ * @param projectRoot - Absolute path to project root
878
+ * @param collection - Collection name
879
+ * @param contentId - Content item ID (slug)
880
+ * @param content - Full markdown content to save
881
+ * @param config - Partial version history configuration
882
+ * @param options - Save options
883
+ * @returns Result of the save operation
884
+ *
885
+ * @example
886
+ * ```typescript
887
+ * // With partial config - defaults are applied automatically
888
+ * const result = await saveVersionWithConfig(
889
+ * '/project',
890
+ * 'blog',
891
+ * 'my-post',
892
+ * '---\ntitle: My Post\n---\n\nContent...',
893
+ * { maxVersions: 50 } // enabled and storagePath use defaults
894
+ * );
895
+ * ```
896
+ */
897
+ declare function saveVersionWithConfig(projectRoot: string, collection: string, contentId: string, content: string, config?: VersionHistoryConfig, options?: SaveVersionOptions): Promise<VersionResult>;
898
+ /**
899
+ * Get all versions with automatic configuration resolution.
900
+ *
901
+ * This wrapper automatically applies configuration defaults and checks
902
+ * the enabled flag before delegating to the core getVersions function.
903
+ *
904
+ * @param projectRoot - Absolute path to project root
905
+ * @param collection - Collection name
906
+ * @param contentId - Content item ID (slug)
907
+ * @param config - Partial version history configuration
908
+ * @returns Array of version entries (empty if disabled)
909
+ *
910
+ * @example
911
+ * ```typescript
912
+ * const versions = await getVersionsWithConfig(
913
+ * '/project',
914
+ * 'blog',
915
+ * 'my-post',
916
+ * { storagePath: 'custom/versions' }
917
+ * );
918
+ * ```
919
+ */
920
+ declare function getVersionsWithConfig(projectRoot: string, collection: string, contentId: string, config?: VersionHistoryConfig): Promise<VersionEntry[]>;
921
+ /**
922
+ * Get a specific version with automatic configuration resolution.
923
+ *
924
+ * This wrapper automatically applies configuration defaults and checks
925
+ * the enabled flag before delegating to the core getVersion function.
926
+ *
927
+ * @param projectRoot - Absolute path to project root
928
+ * @param collection - Collection name
929
+ * @param contentId - Content item ID (slug)
930
+ * @param versionId - Version ID to retrieve
931
+ * @param config - Partial version history configuration
932
+ * @returns Full version data or null if not found/disabled
933
+ *
934
+ * @example
935
+ * ```typescript
936
+ * const version = await getVersionWithConfig(
937
+ * '/project',
938
+ * 'blog',
939
+ * 'my-post',
940
+ * '2024-12-11T10-30-00-000Z'
941
+ * );
942
+ * ```
943
+ */
944
+ declare function getVersionWithConfig(projectRoot: string, collection: string, contentId: string, versionId: string, config?: VersionHistoryConfig): Promise<Version | null>;
945
+ /**
946
+ * Delete a version with automatic configuration resolution.
947
+ *
948
+ * This wrapper automatically applies configuration defaults before
949
+ * delegating to the core deleteVersion function.
950
+ *
951
+ * @param projectRoot - Absolute path to project root
952
+ * @param collection - Collection name
953
+ * @param contentId - Content item ID (slug)
954
+ * @param versionId - Version ID to delete
955
+ * @param config - Partial version history configuration
956
+ * @returns Result of the delete operation
957
+ *
958
+ * @example
959
+ * ```typescript
960
+ * const result = await deleteVersionWithConfig(
961
+ * '/project',
962
+ * 'blog',
963
+ * 'my-post',
964
+ * '2024-12-11T10-30-00-000Z'
965
+ * );
966
+ * ```
967
+ */
968
+ declare function deleteVersionWithConfig(projectRoot: string, collection: string, contentId: string, versionId: string, config?: VersionHistoryConfig): Promise<VersionResult>;
969
+ /**
970
+ * Clear all versions with automatic configuration resolution.
971
+ *
972
+ * This wrapper automatically applies configuration defaults before
973
+ * delegating to the core clearVersions function.
974
+ *
975
+ * @param projectRoot - Absolute path to project root
976
+ * @param collection - Collection name
977
+ * @param contentId - Content item ID (slug)
978
+ * @param config - Partial version history configuration
979
+ * @returns Result of the clear operation
980
+ *
981
+ * @example
982
+ * ```typescript
983
+ * const result = await clearVersionsWithConfig(
984
+ * '/project',
985
+ * 'blog',
986
+ * 'my-post'
987
+ * );
988
+ * ```
989
+ */
990
+ declare function clearVersionsWithConfig(projectRoot: string, collection: string, contentId: string, config?: VersionHistoryConfig): Promise<VersionResult>;
991
+ /**
992
+ * Prune old versions with automatic configuration resolution.
993
+ *
994
+ * This wrapper automatically applies configuration defaults before
995
+ * delegating to the core pruneVersions function. Uses the configured
996
+ * maxVersions value for determining how many versions to keep.
997
+ *
998
+ * @param projectRoot - Absolute path to project root
999
+ * @param collection - Collection name
1000
+ * @param contentId - Content item ID (slug)
1001
+ * @param config - Partial version history configuration
1002
+ * @returns Result of the prune operation
1003
+ *
1004
+ * @example
1005
+ * ```typescript
1006
+ * // Uses custom maxVersions
1007
+ * const result = await pruneVersionsWithConfig(
1008
+ * '/project',
1009
+ * 'blog',
1010
+ * 'my-post',
1011
+ * { maxVersions: 10 }
1012
+ * );
1013
+ * ```
1014
+ */
1015
+ declare function pruneVersionsWithConfig(projectRoot: string, collection: string, contentId: string, config?: VersionHistoryConfig): Promise<VersionResult>;
1016
+ /**
1017
+ * Restore a version with automatic configuration resolution.
1018
+ *
1019
+ * This wrapper automatically applies configuration defaults and checks
1020
+ * the enabled flag before delegating to the core restoreVersion function.
1021
+ *
1022
+ * @param projectRoot - Absolute path to project root
1023
+ * @param collection - Collection name
1024
+ * @param contentId - Content item ID (slug)
1025
+ * @param versionId - Version ID to restore
1026
+ * @param contentFilePath - Absolute path to the current content file
1027
+ * @param config - Partial version history configuration
1028
+ * @param options - Restore options
1029
+ * @returns Result of the restore operation
1030
+ *
1031
+ * @example
1032
+ * ```typescript
1033
+ * const result = await restoreVersionWithConfig(
1034
+ * '/project',
1035
+ * 'blog',
1036
+ * 'my-post',
1037
+ * '2024-12-11T10-30-00-000Z',
1038
+ * '/project/src/content/blog/my-post.md'
1039
+ * );
1040
+ * ```
1041
+ */
1042
+ declare function restoreVersionWithConfig(projectRoot: string, collection: string, contentId: string, versionId: string, contentFilePath: string, config?: VersionHistoryConfig, options?: RestoreVersionOptions): Promise<RestoreResult>;
1043
+
1044
+ /**
1045
+ * @fileoverview Image handling for content collections
1046
+ *
1047
+ * This module provides functions for uploading and managing images
1048
+ * in content collections with support for different storage strategies.
1049
+ *
1050
+ * ## Strategies:
1051
+ * - colocated: Images stored alongside content files
1052
+ * - public: Images stored in public directory
1053
+ * - custom: User-defined storage paths
1054
+ *
1055
+ * @module @writenex/astro/filesystem/images
1056
+ */
1057
+
1058
+ /**
1059
+ * Default image configuration
1060
+ */
1061
+ declare const DEFAULT_IMAGE_CONFIG: ImageConfig;
1062
+ /**
1063
+ * Result of image upload operation
1064
+ */
1065
+ interface ImageUploadResult {
1066
+ success: boolean;
1067
+ /** Markdown-compatible path for the image */
1068
+ path?: string;
1069
+ /** Public URL for the image */
1070
+ url?: string;
1071
+ /** Error message if failed */
1072
+ error?: string;
1073
+ }
1074
+ /**
1075
+ * Options for image upload
1076
+ */
1077
+ interface ImageUploadOptions {
1078
+ /** Original filename */
1079
+ filename: string;
1080
+ /** Image binary data */
1081
+ data: Buffer;
1082
+ /** Collection name */
1083
+ collection: string;
1084
+ /** Content ID (slug) */
1085
+ contentId: string;
1086
+ /** Project root path */
1087
+ projectRoot: string;
1088
+ /** Image configuration */
1089
+ config?: ImageConfig;
1090
+ }
1091
+ /**
1092
+ * Validate image file
1093
+ *
1094
+ * @param filename - Original filename
1095
+ * @returns True if valid image file
1096
+ */
1097
+ declare function isValidImageFile(filename: string): boolean;
1098
+ /**
1099
+ * Upload an image file
1100
+ *
1101
+ * @param options - Upload options
1102
+ * @returns Upload result with paths
1103
+ *
1104
+ * @example
1105
+ * ```typescript
1106
+ * const result = await uploadImage({
1107
+ * filename: "hero.jpg",
1108
+ * data: imageBuffer,
1109
+ * collection: "blog",
1110
+ * contentId: "my-post",
1111
+ * projectRoot: "/path/to/project",
1112
+ * });
1113
+ *
1114
+ * if (result.success) {
1115
+ * console.log(result.path); // "./my-post/hero-abc123.jpg"
1116
+ * }
1117
+ * ```
1118
+ */
1119
+ declare function uploadImage(options: ImageUploadOptions): Promise<ImageUploadResult>;
1120
+ /**
1121
+ * Parse multipart form data for image upload
1122
+ *
1123
+ * Simple parser for multipart/form-data with single file upload.
1124
+ * For production, consider using a proper multipart parser library.
1125
+ *
1126
+ * @param body - Raw request body
1127
+ * @param contentType - Content-Type header
1128
+ * @returns Parsed file data and fields
1129
+ */
1130
+ declare function parseMultipartFormData(body: Buffer, contentType: string): {
1131
+ file?: {
1132
+ filename: string;
1133
+ data: Buffer;
1134
+ contentType: string;
1135
+ };
1136
+ fields: Record<string, string>;
1137
+ };
1138
+ /**
1139
+ * Content structure type detected from file path
1140
+ */
1141
+ type ContentStructure = "flat" | "folder-based" | "date-prefixed";
1142
+ /**
1143
+ * Result of content structure detection
1144
+ */
1145
+ interface ContentStructureResult {
1146
+ /** Detected structure type */
1147
+ structure: ContentStructure;
1148
+ /** Path to the image folder (null if doesn't exist) */
1149
+ imageFolderPath: string | null;
1150
+ }
1151
+ /**
1152
+ * Detect content structure and get the image folder path
1153
+ *
1154
+ * Handles three content structures:
1155
+ * - Flat file: `my-post.md` -> looks for `my-post/` sibling folder
1156
+ * - Folder-based: `slug/index.md` -> uses `slug/` parent folder
1157
+ * - Date-prefixed: `2024-01-15-my-post.md` -> looks for `2024-01-15-my-post/` sibling folder
1158
+ *
1159
+ * @param collectionPath - Absolute path to the collection directory
1160
+ * @param contentId - Content ID (slug)
1161
+ * @param contentFilePath - Absolute path to the content file
1162
+ * @returns Path to the image folder, or null if no image folder exists
1163
+ *
1164
+ * @example
1165
+ * ```typescript
1166
+ * // Flat file structure
1167
+ * const folder = getContentImageFolder(
1168
+ * '/project/src/content/blog',
1169
+ * 'my-post',
1170
+ * '/project/src/content/blog/my-post.md'
1171
+ * );
1172
+ * // Returns: '/project/src/content/blog/my-post' (if exists)
1173
+ *
1174
+ * // Folder-based structure
1175
+ * const folder = getContentImageFolder(
1176
+ * '/project/src/content/blog',
1177
+ * 'my-post',
1178
+ * '/project/src/content/blog/my-post/index.md'
1179
+ * );
1180
+ * // Returns: '/project/src/content/blog/my-post'
1181
+ * ```
1182
+ */
1183
+ declare function getContentImageFolder(collectionPath: string, contentId: string, contentFilePath: string): string | null;
1184
+ /**
1185
+ * Detect the content structure type from a content file path
1186
+ *
1187
+ * @param contentFilePath - Absolute path to the content file
1188
+ * @returns The detected content structure type
1189
+ */
1190
+ declare function detectContentStructure(contentFilePath: string): ContentStructure;
1191
+ /**
1192
+ * Options for recursive directory scanning
1193
+ */
1194
+ interface ScanOptions {
1195
+ /** Maximum recursion depth */
1196
+ maxDepth: number;
1197
+ /** Current recursion depth */
1198
+ currentDepth: number;
1199
+ /** Base path for calculating relative paths */
1200
+ basePath: string;
1201
+ }
1202
+ /**
1203
+ * Scan a directory recursively for image files
1204
+ *
1205
+ * Recursively scans the given directory for image files with supported extensions.
1206
+ * Skips hidden folders (starting with .) and special folders (starting with _).
1207
+ * Limits recursion to the specified maxDepth.
1208
+ *
1209
+ * @param dirPath - Absolute path to the directory to scan
1210
+ * @param basePath - Base path for calculating relative paths
1211
+ * @param options - Scan options including maxDepth and currentDepth
1212
+ * @returns Array of discovered images
1213
+ *
1214
+ * @example
1215
+ * ```typescript
1216
+ * const images = await scanDirectoryForImages(
1217
+ * '/project/src/content/blog/my-post',
1218
+ * '/project/src/content/blog/my-post',
1219
+ * { maxDepth: 5, currentDepth: 0, basePath: '/project/src/content/blog/my-post' }
1220
+ * );
1221
+ * ```
1222
+ */
1223
+ declare function scanDirectoryForImages(dirPath: string, basePath: string, options: ScanOptions): Promise<DiscoveredImage[]>;
1224
+ /**
1225
+ * Calculate relative path from content file to image file
1226
+ *
1227
+ * Calculates the path that can be used in markdown to reference an image
1228
+ * relative to the content file's location. The path always starts with ./
1229
+ * to ensure it's treated as a relative reference.
1230
+ *
1231
+ * @param contentFilePath - Absolute path to the content file (e.g., /project/src/content/blog/my-post.md)
1232
+ * @param imagePath - Absolute path to the image file (e.g., /project/src/content/blog/my-post/images/hero.jpg)
1233
+ * @returns Relative path starting with ./ (e.g., ./my-post/images/hero.jpg)
1234
+ *
1235
+ * @example
1236
+ * ```typescript
1237
+ * // Flat file structure
1238
+ * const relPath = calculateRelativePath(
1239
+ * '/project/src/content/blog/my-post.md',
1240
+ * '/project/src/content/blog/my-post/hero.jpg'
1241
+ * );
1242
+ * // Returns: './my-post/hero.jpg'
1243
+ *
1244
+ * // Folder-based structure
1245
+ * const relPath = calculateRelativePath(
1246
+ * '/project/src/content/blog/my-post/index.md',
1247
+ * '/project/src/content/blog/my-post/images/hero.jpg'
1248
+ * );
1249
+ * // Returns: './images/hero.jpg'
1250
+ *
1251
+ * // Nested subfolder
1252
+ * const relPath = calculateRelativePath(
1253
+ * '/project/src/content/blog/my-post/index.md',
1254
+ * '/project/src/content/blog/my-post/assets/photos/hero.jpg'
1255
+ * );
1256
+ * // Returns: './assets/photos/hero.jpg'
1257
+ * ```
1258
+ */
1259
+ declare function calculateRelativePath(contentFilePath: string, imagePath: string): string;
1260
+ /**
1261
+ * Discover all images associated with a content item
1262
+ *
1263
+ * This is the main entry point for image discovery. It:
1264
+ * 1. Locates the content file using getContentFilePath
1265
+ * 2. Determines the image folder using getContentImageFolder
1266
+ * 3. Scans the folder recursively using scanDirectoryForImages
1267
+ * 4. Calculates relative paths for all discovered images
1268
+ *
1269
+ * @param collectionPath - Absolute path to the collection directory
1270
+ * @param contentId - Content ID (slug)
1271
+ * @param options - Optional discovery options
1272
+ * @returns ImageDiscoveryResult with discovered images or error
1273
+ *
1274
+ * @example
1275
+ * ```typescript
1276
+ * const result = await discoverContentImages(
1277
+ * '/project/src/content/blog',
1278
+ * 'my-post',
1279
+ * { maxDepth: 3 }
1280
+ * );
1281
+ *
1282
+ * if (result.success) {
1283
+ * console.log(`Found ${result.images.length} images`);
1284
+ * for (const img of result.images) {
1285
+ * console.log(`- ${img.relativePath}`);
1286
+ * }
1287
+ * }
1288
+ * ```
1289
+ */
1290
+ declare function discoverContentImages(collectionPath: string, contentId: string, options?: ImageDiscoveryOptions): Promise<ImageDiscoveryResult>;
1291
+
1292
+ export { type ContentStructure, type ContentStructureResult, ContentWatcher, type CreateContentOptions, DEFAULT_IMAGE_CONFIG, type FileChangeEvent, type FileChangeType, FileModificationTracker, type ImageUploadOptions, type ImageUploadResult, type ReadContentOptions, type ReadFileResult, type UpdateContentOptions, type WatcherOptions, type WriteResult, calculateRelativePath, checkCollection, clearVersions, clearVersionsWithConfig, createContent, createContentWatcher, createEmptyManifest, deleteContent, deleteVersion, deleteVersionWithConfig, detectContentStructure, discoverContentImages, ensureGitignore, ensureStorageDirectory, extractSlug, generateExcerpt, generatePreview, generateSlug, generateUniqueSlug, generateVersionId, getCollectionCount, getCollectionSummaries, getContentFilePath, getContentImageFolder, getFileStats, getManifestPath, getOrRecoverManifest, getVersion, getVersionFilePath, getVersionStoragePath, getVersionWithConfig, getVersions, getVersionsWithConfig, isContentFile, isValidImageFile, isVersionHistoryEnabled, parseMultipartFormData, parseVersionId, pruneVersions, pruneVersionsWithConfig, readCollection, readContentFile, readManifest, recoverManifest, resolveVersionConfig, restoreVersion, restoreVersionWithConfig, saveVersion, saveVersionWithConfig, scanDirectoryForImages, toContentSummary, updateContent, uploadImage, writeManifest };