emdash-emit 0.1.14 → 0.1.16

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 (144) hide show
  1. package/CHANGELOG.md +96 -1
  2. package/README.md +259 -188
  3. package/dist/bin/emit.d.ts.map +1 -1
  4. package/dist/bin/emit.js +2 -0
  5. package/dist/bin/emit.js.map +1 -1
  6. package/dist/cli/commands/doctor.d.ts.map +1 -1
  7. package/dist/cli/commands/doctor.js +6 -0
  8. package/dist/cli/commands/doctor.js.map +1 -1
  9. package/dist/cli/commands/media.d.ts +4 -0
  10. package/dist/cli/commands/media.d.ts.map +1 -0
  11. package/dist/cli/commands/media.js +128 -0
  12. package/dist/cli/commands/media.js.map +1 -0
  13. package/dist/cli/commands/pull.d.ts +32 -0
  14. package/dist/cli/commands/pull.d.ts.map +1 -1
  15. package/dist/cli/commands/pull.js +136 -9
  16. package/dist/cli/commands/pull.js.map +1 -1
  17. package/dist/cli/commands/push.d.ts.map +1 -1
  18. package/dist/cli/commands/push.js +18 -0
  19. package/dist/cli/commands/push.js.map +1 -1
  20. package/dist/cli/commands/status.d.ts.map +1 -1
  21. package/dist/cli/commands/status.js +16 -5
  22. package/dist/cli/commands/status.js.map +1 -1
  23. package/dist/client/media.d.ts.map +1 -1
  24. package/dist/client/media.js +16 -5
  25. package/dist/client/media.js.map +1 -1
  26. package/dist/db/cache.d.ts +19 -4
  27. package/dist/db/cache.d.ts.map +1 -1
  28. package/dist/db/cache.js +127 -16
  29. package/dist/db/cache.js.map +1 -1
  30. package/dist/mcp/server.d.ts.map +1 -1
  31. package/dist/mcp/server.js +7 -3
  32. package/dist/mcp/server.js.map +1 -1
  33. package/dist/mcp/tools/sync-doctor.d.ts.map +1 -1
  34. package/dist/mcp/tools/sync-doctor.js +12 -0
  35. package/dist/mcp/tools/sync-doctor.js.map +1 -1
  36. package/dist/mcp/tools/sync-media-search.d.ts +4 -0
  37. package/dist/mcp/tools/sync-media-search.d.ts.map +1 -0
  38. package/dist/mcp/tools/sync-media-search.js +102 -0
  39. package/dist/mcp/tools/sync-media-search.js.map +1 -0
  40. package/dist/mcp/tools/sync-pull.d.ts.map +1 -1
  41. package/dist/mcp/tools/sync-pull.js +85 -13
  42. package/dist/mcp/tools/sync-pull.js.map +1 -1
  43. package/dist/mcp/tools/sync-push.d.ts.map +1 -1
  44. package/dist/mcp/tools/sync-push.js +5 -0
  45. package/dist/mcp/tools/sync-push.js.map +1 -1
  46. package/dist/mcp/tools/sync-status.d.ts.map +1 -1
  47. package/dist/mcp/tools/sync-status.js +6 -2
  48. package/dist/mcp/tools/sync-status.js.map +1 -1
  49. package/dist/sync/delete.d.ts +2 -1
  50. package/dist/sync/delete.d.ts.map +1 -1
  51. package/dist/sync/delete.js +6 -19
  52. package/dist/sync/delete.js.map +1 -1
  53. package/dist/sync/diff.d.ts.map +1 -1
  54. package/dist/sync/diff.js +18 -2
  55. package/dist/sync/diff.js.map +1 -1
  56. package/dist/sync/doctor.d.ts +27 -1
  57. package/dist/sync/doctor.d.ts.map +1 -1
  58. package/dist/sync/doctor.js +309 -26
  59. package/dist/sync/doctor.js.map +1 -1
  60. package/dist/sync/guards.d.ts +45 -0
  61. package/dist/sync/guards.d.ts.map +1 -0
  62. package/dist/sync/guards.js +236 -0
  63. package/dist/sync/guards.js.map +1 -0
  64. package/dist/sync/media/body_scan.d.ts +15 -0
  65. package/dist/sync/media/body_scan.d.ts.map +1 -0
  66. package/dist/sync/media/body_scan.js +132 -0
  67. package/dist/sync/media/body_scan.js.map +1 -0
  68. package/dist/sync/media/companions.d.ts +32 -0
  69. package/dist/sync/media/companions.d.ts.map +1 -0
  70. package/dist/sync/media/companions.js +260 -0
  71. package/dist/sync/media/companions.js.map +1 -0
  72. package/dist/sync/media/index.d.ts +9 -0
  73. package/dist/sync/media/index.d.ts.map +1 -0
  74. package/dist/sync/media/index.js +9 -0
  75. package/dist/sync/media/index.js.map +1 -0
  76. package/dist/sync/media/mirror_item.d.ts +27 -0
  77. package/dist/sync/media/mirror_item.d.ts.map +1 -0
  78. package/dist/sync/media/mirror_item.js +207 -0
  79. package/dist/sync/media/mirror_item.js.map +1 -0
  80. package/dist/sync/media/naming_mirror.d.ts +36 -0
  81. package/dist/sync/media/naming_mirror.d.ts.map +1 -0
  82. package/dist/sync/media/naming_mirror.js +143 -0
  83. package/dist/sync/media/naming_mirror.js.map +1 -0
  84. package/dist/sync/media/pull_article.d.ts +37 -0
  85. package/dist/sync/media/pull_article.d.ts.map +1 -0
  86. package/dist/sync/media/pull_article.js +273 -0
  87. package/dist/sync/media/pull_article.js.map +1 -0
  88. package/dist/sync/media/pull_library.d.ts +12 -0
  89. package/dist/sync/media/pull_library.d.ts.map +1 -0
  90. package/dist/sync/media/pull_library.js +255 -0
  91. package/dist/sync/media/pull_library.js.map +1 -0
  92. package/dist/sync/media/remote_index.d.ts +49 -0
  93. package/dist/sync/media/remote_index.d.ts.map +1 -0
  94. package/dist/sync/media/remote_index.js +241 -0
  95. package/dist/sync/media/remote_index.js.map +1 -0
  96. package/dist/sync/media/types.d.ts +237 -0
  97. package/dist/sync/media/types.d.ts.map +1 -0
  98. package/dist/sync/media/types.js +11 -0
  99. package/dist/sync/media/types.js.map +1 -0
  100. package/dist/sync/media.d.ts +1 -74
  101. package/dist/sync/media.d.ts.map +1 -1
  102. package/dist/sync/media.js +1 -592
  103. package/dist/sync/media.js.map +1 -1
  104. package/dist/sync/orchestrator.d.ts +23 -0
  105. package/dist/sync/orchestrator.d.ts.map +1 -1
  106. package/dist/sync/orchestrator.js +85 -1
  107. package/dist/sync/orchestrator.js.map +1 -1
  108. package/dist/sync/preflight.d.ts +1 -1
  109. package/dist/sync/preflight.d.ts.map +1 -1
  110. package/dist/sync/preflight.js +129 -17
  111. package/dist/sync/preflight.js.map +1 -1
  112. package/dist/sync/pull.d.ts +19 -0
  113. package/dist/sync/pull.d.ts.map +1 -1
  114. package/dist/sync/pull.js +169 -15
  115. package/dist/sync/pull.js.map +1 -1
  116. package/dist/sync/push.d.ts +7 -0
  117. package/dist/sync/push.d.ts.map +1 -1
  118. package/dist/sync/push.js +37 -10
  119. package/dist/sync/push.js.map +1 -1
  120. package/dist/sync/status.d.ts +4 -0
  121. package/dist/sync/status.d.ts.map +1 -1
  122. package/dist/sync/status.js +68 -4
  123. package/dist/sync/status.js.map +1 -1
  124. package/dist/utils/config.d.ts +25 -0
  125. package/dist/utils/config.d.ts.map +1 -1
  126. package/dist/utils/config.js +28 -2
  127. package/dist/utils/config.js.map +1 -1
  128. package/dist/utils/frontmatter.d.ts +1 -1
  129. package/dist/utils/frontmatter.d.ts.map +1 -1
  130. package/dist/utils/frontmatter.js +15 -4
  131. package/dist/utils/frontmatter.js.map +1 -1
  132. package/dist/utils/media-body-refs.d.ts +81 -0
  133. package/dist/utils/media-body-refs.d.ts.map +1 -0
  134. package/dist/utils/media-body-refs.js +240 -0
  135. package/dist/utils/media-body-refs.js.map +1 -0
  136. package/dist/utils/media-refs.d.ts +44 -0
  137. package/dist/utils/media-refs.d.ts.map +1 -0
  138. package/dist/utils/media-refs.js +101 -0
  139. package/dist/utils/media-refs.js.map +1 -0
  140. package/dist/utils/naming.d.ts +31 -0
  141. package/dist/utils/naming.d.ts.map +1 -1
  142. package/dist/utils/naming.js +58 -0
  143. package/dist/utils/naming.js.map +1 -1
  144. package/package.json +1 -1
@@ -0,0 +1,260 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import yaml from 'js-yaml';
4
+ import { toRootRelativeMediaUrl } from '../../client/media.js';
5
+ import { MEDIA_FILE_ROUTE_PREFIX } from '../../utils/media-refs.js';
6
+ import { storageKeyToMarkdownRef } from '../../utils/naming.js';
7
+ export function buildMediaMetaDoc(item, origin, existingMeta) {
8
+ const storageKey = (typeof item.storageKey === 'string' && item.storageKey ? item.storageKey : undefined) ??
9
+ (typeof existingMeta?.storageKey === 'string' && existingMeta.storageKey
10
+ ? existingMeta.storageKey
11
+ : '');
12
+ const canonicalUrl = item.url ||
13
+ (storageKey
14
+ ? `${MEDIA_FILE_ROUTE_PREFIX}${storageKey}`
15
+ : typeof existingMeta?.url === 'string'
16
+ ? existingMeta.url
17
+ : '');
18
+ const rootRelativeUrl = canonicalUrl
19
+ ? toRootRelativeMediaUrl(canonicalUrl, storageKey, origin)
20
+ : '';
21
+ const resolvedAlt = item.alt !== undefined && item.alt !== null && item.alt !== ''
22
+ ? item.alt
23
+ : typeof existingMeta?.alt === 'string'
24
+ ? existingMeta.alt
25
+ : '';
26
+ const resolvedCaption = item.caption !== undefined && item.caption !== null && item.caption !== ''
27
+ ? item.caption
28
+ : typeof existingMeta?.caption === 'string'
29
+ ? existingMeta.caption
30
+ : '';
31
+ const resolvedContentHash = typeof item.contentHash === 'string' && item.contentHash
32
+ ? item.contentHash
33
+ : typeof existingMeta?.contentHash === 'string' && existingMeta.contentHash
34
+ ? existingMeta.contentHash
35
+ : null;
36
+ return {
37
+ id: item.id,
38
+ filename: item.filename,
39
+ mimeType: item.mimeType,
40
+ size: item.size,
41
+ width: item.width ?? (typeof existingMeta?.width === 'number' ? existingMeta.width : null),
42
+ height: item.height ?? (typeof existingMeta?.height === 'number' ? existingMeta.height : null),
43
+ alt: resolvedAlt,
44
+ caption: resolvedCaption,
45
+ storageKey,
46
+ contentHash: resolvedContentHash,
47
+ status: item.status ?? (typeof existingMeta?.status === 'string' ? existingMeta.status : 'ready'),
48
+ url: rootRelativeUrl,
49
+ createdAt: item.createdAt || (typeof existingMeta?.createdAt === 'string' ? existingMeta.createdAt : ''),
50
+ };
51
+ }
52
+ /**
53
+ * Recovers the storageKey a companion YAML actually refers to. Older mirrors — most visibly the ones
54
+ * written before Emit persisted the field — carry no `storageKey:` key at all, yet their `url:` is the
55
+ * canonical `/_emdash/api/media/file/<storageKey>` route, which is confirmed metadata written from the
56
+ * server response and not an inference from a body reference. Absolute and root-relative forms are both
57
+ * accepted because either can appear depending on how the record was mirrored. `legacy_*` residue is
58
+ * never read here: it backs no real record, so it must not serve as verification evidence.
59
+ */
60
+ export function storageKeyFromMediaMeta(doc) {
61
+ if (typeof doc.storageKey === 'string' && doc.storageKey.trim()) {
62
+ return doc.storageKey.trim();
63
+ }
64
+ if (typeof doc.url !== 'string' || !doc.url.trim())
65
+ return undefined;
66
+ const match = doc.url.match(/(?:^|\/)media\/file\/([^?#]+)/);
67
+ return match ? match[1] : undefined;
68
+ }
69
+ /**
70
+ * Indexes every locally mirrored media asset (`media/<markdownRef>.yaml`, plus legacy companion names) so a body
71
+ * storageKey that the canonical id lookup failed to resolve can be re-checked against confirmed local
72
+ * metadata instead of being declared missing. Leftover `legacy_*` residue is skipped because it backs
73
+ * no real record. The result maps each ULID found in a mirror (record id or storageKey) to its entry,
74
+ * which is what lets Emit tell "the record exists under a different id" apart from "no record exists".
75
+ */
76
+ export function collectLocalMediaMirrorCandidates(mediaDir) {
77
+ const byUlid = new Map();
78
+ if (!fs.existsSync(mediaDir))
79
+ return byUlid;
80
+ for (const file of fs.readdirSync(mediaDir)) {
81
+ if (!file.endsWith('.yaml') || file.startsWith('legacy_'))
82
+ continue;
83
+ let doc = null;
84
+ try {
85
+ doc = yaml.load(fs.readFileSync(path.join(mediaDir, file), 'utf8'));
86
+ }
87
+ catch {
88
+ continue;
89
+ }
90
+ if (!doc)
91
+ continue;
92
+ const storageKey = storageKeyFromMediaMeta(doc);
93
+ const stem = file.replace(/\.yaml$/, '');
94
+ const idMatch = stem.replace(/^\d{8}_/, '').match(/^([0-9A-HJ-NP-Z]{26})/i);
95
+ const docId = typeof doc.id === 'string' && /^[0-9A-HJ-NP-Z]{26}$/i.test(doc.id.trim())
96
+ ? doc.id.trim().toUpperCase()
97
+ : undefined;
98
+ const mediaId = docId || (idMatch ? idMatch[1].toUpperCase() : '');
99
+ if (!mediaId)
100
+ continue;
101
+ const candidate = { mediaId, storageKey, metaFile: file };
102
+ if (!byUlid.has(mediaId))
103
+ byUlid.set(mediaId, candidate);
104
+ for (const ulid of storageKey?.matchAll(/[0-9A-HJ-NP-Z]{26}/gi) ?? []) {
105
+ const key = ulid[0].toUpperCase();
106
+ if (!byUlid.has(key))
107
+ byUlid.set(key, candidate);
108
+ }
109
+ }
110
+ return byUlid;
111
+ }
112
+ /**
113
+ * Builds a local mirror index mapping storageKey -> LocalMirrorEntry.
114
+ *
115
+ * Driven by cache `media_state` (status 'ready' or 'uploaded') when available.
116
+ * Resolves companion YAML metadata (filename, alt, caption) strictly from companion YAML,
117
+ * never by filename guessing. If companion YAML is absent, metadata is null.
118
+ *
119
+ * For cache-less workspaces, scans `media/*.yaml` and recovers storageKey via storageKeyFromMediaMeta.
120
+ * Leftover `legacy_*` residue is strictly ignored.
121
+ */
122
+ export function collectLocalMirrorIndex(workspaceRoot, cache) {
123
+ const index = new Map();
124
+ const mediaDir = path.join(workspaceRoot, 'media');
125
+ if (cache) {
126
+ const states = cache.listMediaStates();
127
+ for (const record of states) {
128
+ if (record.status !== 'ready' && record.status !== 'uploaded')
129
+ continue;
130
+ if (!record.storageKey)
131
+ continue;
132
+ let stem;
133
+ if (record.markdownRef) {
134
+ stem = path.basename(record.markdownRef);
135
+ }
136
+ else {
137
+ stem = storageKeyToMarkdownRef(record.storageKey) || record.storageKey;
138
+ }
139
+ let filename = null;
140
+ let alt = null;
141
+ let caption = null;
142
+ const yamlPath = path.join(mediaDir, `${stem}.yaml`);
143
+ let loadedDoc = null;
144
+ if (fs.existsSync(yamlPath)) {
145
+ try {
146
+ loadedDoc = yaml.load(fs.readFileSync(yamlPath, 'utf8'));
147
+ }
148
+ catch {
149
+ loadedDoc = null;
150
+ }
151
+ }
152
+ else if (record.workspacePath) {
153
+ const altYamlPath = path.join(workspaceRoot, record.workspacePath.replace(/\.[^.]+$/, '.yaml'));
154
+ if (fs.existsSync(altYamlPath)) {
155
+ try {
156
+ loadedDoc = yaml.load(fs.readFileSync(altYamlPath, 'utf8'));
157
+ }
158
+ catch {
159
+ loadedDoc = null;
160
+ }
161
+ }
162
+ }
163
+ if (loadedDoc) {
164
+ filename =
165
+ typeof loadedDoc.filename === 'string' && loadedDoc.filename ? loadedDoc.filename : null;
166
+ alt = typeof loadedDoc.alt === 'string' && loadedDoc.alt ? loadedDoc.alt : null;
167
+ caption =
168
+ typeof loadedDoc.caption === 'string' && loadedDoc.caption ? loadedDoc.caption : null;
169
+ }
170
+ else if (fs.existsSync(mediaDir)) {
171
+ // Fallback: search for any companion that confirms this storageKey or mediaId
172
+ for (const f of fs.readdirSync(mediaDir)) {
173
+ if (!f.endsWith('.yaml') || f.startsWith('legacy_'))
174
+ continue;
175
+ try {
176
+ const candidate = yaml.load(fs.readFileSync(path.join(mediaDir, f), 'utf8'));
177
+ if (candidate &&
178
+ (storageKeyFromMediaMeta(candidate) === record.storageKey ||
179
+ (candidate.id && String(candidate.id).toUpperCase() === record.mediaId))) {
180
+ filename =
181
+ typeof candidate.filename === 'string' && candidate.filename
182
+ ? candidate.filename
183
+ : null;
184
+ alt = typeof candidate.alt === 'string' && candidate.alt ? candidate.alt : null;
185
+ caption =
186
+ typeof candidate.caption === 'string' && candidate.caption
187
+ ? candidate.caption
188
+ : null;
189
+ break;
190
+ }
191
+ }
192
+ catch { }
193
+ }
194
+ }
195
+ let localPath = null;
196
+ if (record.workspacePath && fs.existsSync(path.join(workspaceRoot, record.workspacePath))) {
197
+ localPath = record.workspacePath;
198
+ }
199
+ else if (fs.existsSync(mediaDir)) {
200
+ const files = fs.readdirSync(mediaDir);
201
+ for (const f of files) {
202
+ if (f.replace(/\.[^.]+$/, '') === stem && !f.endsWith('.yaml')) {
203
+ localPath = path.posix.join('media', f);
204
+ break;
205
+ }
206
+ }
207
+ }
208
+ index.set(record.storageKey, {
209
+ mediaId: record.mediaId,
210
+ storageKey: record.storageKey,
211
+ localPath,
212
+ filename,
213
+ alt,
214
+ caption,
215
+ });
216
+ }
217
+ return index;
218
+ }
219
+ // Cache-less fallback
220
+ if (fs.existsSync(mediaDir)) {
221
+ const files = fs.readdirSync(mediaDir);
222
+ for (const file of files) {
223
+ if (!file.endsWith('.yaml') || file.startsWith('legacy_'))
224
+ continue;
225
+ let doc = null;
226
+ try {
227
+ doc = yaml.load(fs.readFileSync(path.join(mediaDir, file), 'utf8'));
228
+ }
229
+ catch {
230
+ continue;
231
+ }
232
+ if (!doc)
233
+ continue;
234
+ const storageKey = storageKeyFromMediaMeta(doc);
235
+ if (!storageKey)
236
+ continue;
237
+ const stem = file.replace(/\.yaml$/, '');
238
+ const docId = typeof doc.id === 'string' && /^[0-9A-HJ-NP-Z]{26}$/i.test(doc.id.trim())
239
+ ? doc.id.trim().toUpperCase()
240
+ : '';
241
+ let binaryPath = null;
242
+ for (const f of files) {
243
+ if (f !== file && f.replace(/\.[^.]+$/, '') === stem && !f.endsWith('.yaml')) {
244
+ binaryPath = path.posix.join('media', f);
245
+ break;
246
+ }
247
+ }
248
+ index.set(storageKey, {
249
+ mediaId: docId,
250
+ storageKey,
251
+ localPath: binaryPath,
252
+ filename: typeof doc.filename === 'string' && doc.filename ? doc.filename : null,
253
+ alt: typeof doc.alt === 'string' && doc.alt ? doc.alt : null,
254
+ caption: typeof doc.caption === 'string' && doc.caption ? doc.caption : null,
255
+ });
256
+ }
257
+ }
258
+ return index;
259
+ }
260
+ //# sourceMappingURL=companions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"companions.js","sourceRoot":"","sources":["../../../src/sync/media/companions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ export * from './types.js';
2
+ export * from './naming_mirror.js';
3
+ export * from './companions.js';
4
+ export * from './body_scan.js';
5
+ export * from './mirror_item.js';
6
+ export * from './pull_article.js';
7
+ export * from './pull_library.js';
8
+ export * from './remote_index.js';
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/sync/media/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC"}
@@ -0,0 +1,9 @@
1
+ export * from './types.js';
2
+ export * from './naming_mirror.js';
3
+ export * from './companions.js';
4
+ export * from './body_scan.js';
5
+ export * from './mirror_item.js';
6
+ export * from './pull_article.js';
7
+ export * from './pull_library.js';
8
+ export * from './remote_index.js';
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/sync/media/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC"}
@@ -0,0 +1,27 @@
1
+ import type { CacheDatabase } from '../../db/cache.js';
2
+ import type { EmDashMediaItem, PromoteDraftMediaOptions, PromotedMediaFile } from './types.js';
3
+ /**
4
+ * Persists YAML companion metadata and downloads the binary file for a single media item.
5
+ */
6
+ export declare function syncSingleMediaItem(options: {
7
+ item: EmDashMediaItem;
8
+ mediaDir: string;
9
+ origin: string;
10
+ token: string;
11
+ downloadFiles: boolean;
12
+ fetchFn: typeof fetch;
13
+ cache?: CacheDatabase;
14
+ }): Promise<{
15
+ downloaded: boolean;
16
+ skipped: boolean;
17
+ baseName: string;
18
+ }>;
19
+ /**
20
+ * Promotes a local draft image from media/drafts/<name> into the canonical
21
+ * media/<markdownRef>.<ext> location alongside its matching media/<markdownRef>.yaml companion, where
22
+ * markdownRef is derived from the storageKey the article body will reference. Returns both the workspace-relative
23
+ * path of the promoted binary and the shared `media/<markdownRef>` stem, so the caller can record the mirror in
24
+ * `media_state.markdown_ref`.
25
+ */
26
+ export declare function promoteDraftMediaFile(options: PromoteDraftMediaOptions): PromotedMediaFile;
27
+ //# sourceMappingURL=mirror_item.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mirror_item.d.ts","sourceRoot":"","sources":["../../../src/sync/media/mirror_item.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAQvD,OAAO,KAAK,EAAE,eAAe,EAAE,wBAAwB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAE/F;;GAEG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,EAAE;IACjD,IAAI,EAAE,eAAe,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,OAAO,CAAC;IACvB,OAAO,EAAE,OAAO,KAAK,CAAC;IACtB,KAAK,CAAC,EAAE,aAAa,CAAC;CACvB,GAAG,OAAO,CAAC;IAAE,UAAU,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAmKvE;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,wBAAwB,GAAG,iBAAiB,CAyD1F"}
@@ -0,0 +1,207 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import yaml from 'js-yaml';
4
+ import { MEDIA_FILE_ROUTE_PREFIX } from '../../utils/media-refs.js';
5
+ import { buildMediaMetaDoc } from './companions.js';
6
+ import { cleanupDuplicateMediaFilesForId, mediaMirrorNames, moveFileSafe, } from './naming_mirror.js';
7
+ /**
8
+ * Persists YAML companion metadata and downloads the binary file for a single media item.
9
+ */
10
+ export async function syncSingleMediaItem(options) {
11
+ const { item, mediaDir, origin, token, downloadFiles, fetchFn, cache } = options;
12
+ const names = mediaMirrorNames(item, mediaDir);
13
+ const baseName = names.markdownRef;
14
+ const yamlPath = path.join(mediaDir, `${baseName}.yaml`);
15
+ // Read any existing companion for this record id first, whatever stem it currently occupies. The
16
+ // storageKey-named companion is the expected location; the legacy stems are where an older workspace still
17
+ // holds the same asset. `legacy_<storageKey>.yaml` is skipped unconditionally: it carries a matching ULID
18
+ // but fabricates its metadata from a file route, so it must never seed a real record.
19
+ let existingMeta = null;
20
+ let existingMetaPath = null;
21
+ const mediaFiles = fs.readdirSync(mediaDir);
22
+ const companionCandidates = [yamlPath];
23
+ for (const legacy of names.legacyBaseNames) {
24
+ companionCandidates.push(path.join(mediaDir, `${legacy}.yaml`));
25
+ }
26
+ for (const candidate of companionCandidates) {
27
+ if (!fs.existsSync(candidate))
28
+ continue;
29
+ try {
30
+ existingMeta = yaml.load(fs.readFileSync(candidate, 'utf8'));
31
+ existingMetaPath = candidate;
32
+ break;
33
+ }
34
+ catch {
35
+ existingMeta = null;
36
+ }
37
+ }
38
+ if (!existingMeta) {
39
+ const matchingYaml = mediaFiles.find((f) => f.endsWith('.yaml') &&
40
+ !f.startsWith('legacy_') &&
41
+ f !== path.basename(yamlPath) &&
42
+ (f.includes(`_${item.id}-`) || f.startsWith(`${item.id}-`)));
43
+ if (matchingYaml) {
44
+ try {
45
+ existingMeta = yaml.load(fs.readFileSync(path.join(mediaDir, matchingYaml), 'utf8'));
46
+ existingMetaPath = path.join(mediaDir, matchingYaml);
47
+ }
48
+ catch {
49
+ existingMeta = null;
50
+ }
51
+ }
52
+ }
53
+ // 1. Write YAML metadata under the storageKey-derived stem
54
+ const metaDoc = buildMediaMetaDoc(item, origin, existingMeta);
55
+ fs.writeFileSync(yamlPath, yaml.dump(metaDoc, { indent: 2, quotingType: '"', forceQuotes: false, lineWidth: -1 }), 'utf8');
56
+ let downloaded = false;
57
+ let skipped = false;
58
+ // 2. Download binary file if enabled and url is present
59
+ const canonicalDownloadPath = item.url || (item.storageKey ? `${MEDIA_FILE_ROUTE_PREFIX}${item.storageKey}` : '');
60
+ const fileExt = names.ext || (item.mimeType.includes('png') ? '.png' : '.jpg');
61
+ const targetFilePath = path.join(mediaDir, `${baseName}${fileExt}`);
62
+ // Rename an existing mirror into the new name before considering a download, so migrating a workspace
63
+ // costs no bandwidth. Size must match: a same-stem file of a different size is not this asset.
64
+ if (!fs.existsSync(targetFilePath)) {
65
+ const candidates = names.legacyBaseNames
66
+ .map((legacy) => path.join(mediaDir, `${legacy}${fileExt}`))
67
+ .concat(mediaFiles
68
+ .filter((f) => !f.endsWith('.yaml') &&
69
+ !f.startsWith('legacy_') &&
70
+ (f.includes(`_${item.id}-`) || f.startsWith(`${item.id}-`)))
71
+ .map((f) => path.join(mediaDir, f)));
72
+ for (const candidate of candidates) {
73
+ if (candidate === targetFilePath || !fs.existsSync(candidate))
74
+ continue;
75
+ try {
76
+ const stat = fs.statSync(candidate);
77
+ if (stat.size === item.size) {
78
+ moveFileSafe(candidate, targetFilePath);
79
+ break;
80
+ }
81
+ }
82
+ catch { }
83
+ }
84
+ }
85
+ if (downloadFiles && canonicalDownloadPath) {
86
+ let shouldDownload = true;
87
+ if (fs.existsSync(targetFilePath)) {
88
+ const stat = fs.statSync(targetFilePath);
89
+ if (stat.size === item.size) {
90
+ shouldDownload = false;
91
+ skipped = true;
92
+ }
93
+ }
94
+ if (shouldDownload) {
95
+ try {
96
+ const downloadUrl = new URL(canonicalDownloadPath, origin).toString();
97
+ const fileRes = await fetchFn(downloadUrl, {
98
+ headers: {
99
+ Authorization: `Bearer ${token}`,
100
+ },
101
+ });
102
+ if (fileRes.ok) {
103
+ const buffer = Buffer.from(await fileRes.arrayBuffer());
104
+ fs.writeFileSync(targetFilePath, buffer);
105
+ downloaded = true;
106
+ }
107
+ }
108
+ catch {
109
+ // Ignore individual download failures
110
+ }
111
+ }
112
+ }
113
+ // Register the mirrored asset in media_state. Before storageKey naming, only the push/upload path wrote
114
+ // this table, so a pulled workspace had no index at all and every lookup fell back to scanning YAML. With
115
+ // the mirror name no longer carrying the record id, that index is what makes a body reference resolvable
116
+ // without re-reading thousands of companions.
117
+ if (cache && item.storageKey) {
118
+ const mirrorUrl = typeof metaDoc.url === 'string' && metaDoc.url
119
+ ? metaDoc.url
120
+ : `${MEDIA_FILE_ROUTE_PREFIX}${item.storageKey}`;
121
+ cache.saveMediaState({
122
+ localHash: `media-record:${item.id}`,
123
+ mediaId: item.id,
124
+ storageKey: item.storageKey,
125
+ url: mirrorUrl,
126
+ workspacePath: path.posix.join('media', `${baseName}${fileExt}`),
127
+ markdownRef: path.posix.join('media', baseName),
128
+ status: 'ready',
129
+ });
130
+ }
131
+ // Drop the companion this asset occupied under a legacy stem. Done after the binary rename so an
132
+ // interrupted run never leaves a record whose metadata and bytes disagree about the name.
133
+ if (existingMetaPath && existingMetaPath !== yamlPath && fs.existsSync(existingMetaPath)) {
134
+ try {
135
+ fs.unlinkSync(existingMetaPath);
136
+ }
137
+ catch { }
138
+ }
139
+ for (const legacy of names.legacyBaseNames) {
140
+ const legacyYaml = path.join(mediaDir, `${legacy}.yaml`);
141
+ if (legacyYaml !== yamlPath && fs.existsSync(legacyYaml)) {
142
+ try {
143
+ fs.unlinkSync(legacyYaml);
144
+ }
145
+ catch { }
146
+ }
147
+ }
148
+ // Clean up any duplicate files with the same media ID but a different datePrefix/baseName
149
+ cleanupDuplicateMediaFilesForId(mediaDir, item.id, baseName);
150
+ return { downloaded, skipped, baseName };
151
+ }
152
+ /**
153
+ * Promotes a local draft image from media/drafts/<name> into the canonical
154
+ * media/<markdownRef>.<ext> location alongside its matching media/<markdownRef>.yaml companion, where
155
+ * markdownRef is derived from the storageKey the article body will reference. Returns both the workspace-relative
156
+ * path of the promoted binary and the shared `media/<markdownRef>` stem, so the caller can record the mirror in
157
+ * `media_state.markdown_ref`.
158
+ */
159
+ export function promoteDraftMediaFile(options) {
160
+ const { workspaceRoot, origin, draftAbsPath, item, removeDraft = true } = options;
161
+ const mediaDir = path.join(workspaceRoot, 'media');
162
+ fs.mkdirSync(mediaDir, { recursive: true });
163
+ const ext = path.extname(item.filename) ||
164
+ path.extname(draftAbsPath) ||
165
+ (item.mimeType.includes('png') ? '.png' : '.jpg');
166
+ const names = mediaMirrorNames({ ...item, filename: item.filename || path.basename(draftAbsPath) }, mediaDir);
167
+ const baseName = names.markdownRef;
168
+ const yamlPath = path.join(mediaDir, `${baseName}.yaml`);
169
+ let existingMeta = null;
170
+ if (fs.existsSync(yamlPath)) {
171
+ try {
172
+ existingMeta = yaml.load(fs.readFileSync(yamlPath, 'utf8'));
173
+ }
174
+ catch {
175
+ existingMeta = null;
176
+ }
177
+ }
178
+ const metaDoc = buildMediaMetaDoc(item, origin, existingMeta);
179
+ fs.writeFileSync(yamlPath, yaml.dump(metaDoc, { indent: 2, quotingType: '"', forceQuotes: false, lineWidth: -1 }), 'utf8');
180
+ const targetFilePath = path.join(mediaDir, `${baseName}${ext}`);
181
+ if (fs.existsSync(draftAbsPath)) {
182
+ if (removeDraft) {
183
+ moveFileSafe(draftAbsPath, targetFilePath);
184
+ }
185
+ else {
186
+ fs.copyFileSync(draftAbsPath, targetFilePath);
187
+ }
188
+ }
189
+ // Retire any earlier id-named mirror of the same record so one asset never occupies two names.
190
+ for (const legacy of names.legacyBaseNames) {
191
+ for (const suffix of [ext, '.yaml']) {
192
+ const legacyPath = path.join(mediaDir, `${legacy}${suffix}`);
193
+ if (legacyPath !== yamlPath && legacyPath !== targetFilePath && fs.existsSync(legacyPath)) {
194
+ try {
195
+ fs.unlinkSync(legacyPath);
196
+ }
197
+ catch { }
198
+ }
199
+ }
200
+ }
201
+ cleanupDuplicateMediaFilesForId(mediaDir, item.id, baseName);
202
+ return {
203
+ filePath: path.posix.join('media', `${baseName}${ext}`),
204
+ markdownRef: path.posix.join('media', baseName),
205
+ };
206
+ }
207
+ //# sourceMappingURL=mirror_item.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mirror_item.js","sourceRoot":"","sources":["../../../src/sync/media/mirror_item.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ import { type EmDashMediaItem, type MediaMirrorNames } from './types.js';
2
+ /**
3
+ * Moves a file from src to dest, falling back to copy + unlink when src and dest
4
+ * reside on different filesystems (`EXDEV`).
5
+ */
6
+ export declare function moveFileSafe(src: string, dest: string): void;
7
+ /**
8
+ * Reads the media record id a mirror belongs to from its companion YAML, accepting both the storageKey-named
9
+ * stem and the legacy `<yyyyMMdd>_<id>-<name>` form. Returns `null` when no companion exists or it names no
10
+ * valid ULID, which is how Emit refuses to prune a file it cannot attribute to a record. A companion whose
11
+ * id is explicitly null (or any non-ULID value) proves the mirror backs no real record, so the stem's own
12
+ * leading characters are never trusted as an id — that fallback would let fabricated fixture ids reach
13
+ * record-keyed logic.
14
+ */
15
+ export declare function mediaRecordIdForStem(mediaDir: string, stem: string): string | null;
16
+ /**
17
+ * Removes any stale duplicate files in `media/` that belong to the same 26-char `mediaId` but sit under a
18
+ * different stem — a leftover date-prefixed name from an earlier local promotion, for instance. Ownership is
19
+ * read from each file's companion YAML rather than from the filename, because a storageKey-named mirror
20
+ * carries no record id at all and must never be matched (or deleted) by a coincidental ULID prefix.
21
+ */
22
+ export declare function cleanupDuplicateMediaFilesForId(mediaDir: string, mediaId: string, canonicalBaseName: string): void;
23
+ /**
24
+ * Resolves the storageKey-derived stem against the names already taken in `media/`. Two distinct keys can
25
+ * sanitize to the same basename — `a/sample.png` and `b/sample.png` both yield `sample` — and a second asset
26
+ * must never overwrite the first. A name already owned by this same record is reused as-is, so re-pulling is
27
+ * idempotent; any other collision gets the deterministic `<stem>--<mediaId>` suffix.
28
+ */
29
+ export declare function resolveMarkdownRefCollision(mediaDir: string, baseRef: string, item: EmDashMediaItem): string;
30
+ /**
31
+ * Computes the canonical mirror name for one remote media item. Falls back to the record-id based stem only
32
+ * when the storageKey carries no usable basename, so a malformed migrated key cannot produce an unnameable
33
+ * file. Pass `mediaDir` to resolve a stem collision against the mirrors already on disk.
34
+ */
35
+ export declare function mediaMirrorNames(item: EmDashMediaItem, mediaDir?: string): MediaMirrorNames;
36
+ //# sourceMappingURL=naming_mirror.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"naming_mirror.d.ts","sourceRoot":"","sources":["../../../src/sync/media/naming_mirror.ts"],"names":[],"mappings":"AASA,OAAO,EAA2B,KAAK,eAAe,EAAE,KAAK,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAElG;;;GAGG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAW5D;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAmBlF;AAED;;;;;GAKG;AACH,wBAAgB,+BAA+B,CAC7C,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,iBAAiB,EAAE,MAAM,GACxB,IAAI,CAwBN;AAED;;;;;GAKG;AACH,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,eAAe,GACpB,MAAM,CAgBR;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,eAAe,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,gBAAgB,CAkB3F"}