emdash-emit 0.1.17 → 0.1.25

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 (216) hide show
  1. package/CHANGELOG.md +152 -0
  2. package/README.md +22 -11
  3. package/dist/cli/commands/new.d.ts.map +1 -1
  4. package/dist/cli/commands/new.js +30 -0
  5. package/dist/cli/commands/new.js.map +1 -1
  6. package/dist/cli/commands/pull.d.ts +2 -10
  7. package/dist/cli/commands/pull.d.ts.map +1 -1
  8. package/dist/cli/commands/pull.js +12 -30
  9. package/dist/cli/commands/pull.js.map +1 -1
  10. package/dist/cli/commands/status.d.ts.map +1 -1
  11. package/dist/cli/commands/status.js +7 -2
  12. package/dist/cli/commands/status.js.map +1 -1
  13. package/dist/client/api.d.ts +20 -0
  14. package/dist/client/api.d.ts.map +1 -1
  15. package/dist/client/api.js +89 -0
  16. package/dist/client/api.js.map +1 -1
  17. package/dist/client/auth.d.ts.map +1 -1
  18. package/dist/client/auth.js +2 -1
  19. package/dist/client/auth.js.map +1 -1
  20. package/dist/client/endpoints.d.ts +19 -0
  21. package/dist/client/endpoints.d.ts.map +1 -0
  22. package/dist/client/endpoints.js +69 -0
  23. package/dist/client/endpoints.js.map +1 -0
  24. package/dist/client/http.d.ts +4 -2
  25. package/dist/client/http.d.ts.map +1 -1
  26. package/dist/client/http.js.map +1 -1
  27. package/dist/client/lock.d.ts.map +1 -1
  28. package/dist/client/lock.js +4 -3
  29. package/dist/client/lock.js.map +1 -1
  30. package/dist/client/mapper.d.ts.map +1 -1
  31. package/dist/client/mapper.js +5 -3
  32. package/dist/client/mapper.js.map +1 -1
  33. package/dist/client/media-id.d.ts +2 -0
  34. package/dist/client/media-id.d.ts.map +1 -0
  35. package/dist/client/media-id.js +23 -0
  36. package/dist/client/media-id.js.map +1 -0
  37. package/dist/client/media.d.ts +8 -7
  38. package/dist/client/media.d.ts.map +1 -1
  39. package/dist/client/media.js +58 -17
  40. package/dist/client/media.js.map +1 -1
  41. package/dist/db/baseline.d.ts +38 -0
  42. package/dist/db/baseline.d.ts.map +1 -0
  43. package/dist/db/baseline.js +74 -0
  44. package/dist/db/baseline.js.map +1 -0
  45. package/dist/db/cache.d.ts +8 -2
  46. package/dist/db/cache.d.ts.map +1 -1
  47. package/dist/db/cache.js +24 -2
  48. package/dist/db/cache.js.map +1 -1
  49. package/dist/index.d.ts +4 -0
  50. package/dist/index.d.ts.map +1 -1
  51. package/dist/index.js +4 -0
  52. package/dist/index.js.map +1 -1
  53. package/dist/mcp/tools/sync-new.d.ts.map +1 -1
  54. package/dist/mcp/tools/sync-new.js +42 -1
  55. package/dist/mcp/tools/sync-new.js.map +1 -1
  56. package/dist/mcp/tools/sync-pull.d.ts.map +1 -1
  57. package/dist/mcp/tools/sync-pull.js +15 -2
  58. package/dist/mcp/tools/sync-pull.js.map +1 -1
  59. package/dist/mcp/tools/sync-status.d.ts.map +1 -1
  60. package/dist/mcp/tools/sync-status.js +1 -0
  61. package/dist/mcp/tools/sync-status.js.map +1 -1
  62. package/dist/presentation/article_report.d.ts +40 -0
  63. package/dist/presentation/article_report.d.ts.map +1 -0
  64. package/dist/presentation/article_report.js +80 -0
  65. package/dist/presentation/article_report.js.map +1 -0
  66. package/dist/sync/bylines.d.ts.map +1 -1
  67. package/dist/sync/bylines.js +2 -1
  68. package/dist/sync/bylines.js.map +1 -1
  69. package/dist/sync/conflicts.js +1 -1
  70. package/dist/sync/conflicts.js.map +1 -1
  71. package/dist/sync/delete.d.ts +0 -5
  72. package/dist/sync/delete.d.ts.map +1 -1
  73. package/dist/sync/delete.js +5 -11
  74. package/dist/sync/delete.js.map +1 -1
  75. package/dist/sync/diff.d.ts.map +1 -1
  76. package/dist/sync/diff.js +13 -9
  77. package/dist/sync/diff.js.map +1 -1
  78. package/dist/sync/doctor.d.ts.map +1 -1
  79. package/dist/sync/doctor.js +11 -14
  80. package/dist/sync/doctor.js.map +1 -1
  81. package/dist/sync/guards.d.ts +5 -0
  82. package/dist/sync/guards.d.ts.map +1 -1
  83. package/dist/sync/guards.js +62 -1
  84. package/dist/sync/guards.js.map +1 -1
  85. package/dist/sync/media/body_scan.d.ts.map +1 -1
  86. package/dist/sync/media/body_scan.js +9 -3
  87. package/dist/sync/media/body_scan.js.map +1 -1
  88. package/dist/sync/media/companions.d.ts +2 -9
  89. package/dist/sync/media/companions.d.ts.map +1 -1
  90. package/dist/sync/media/companions.js +5 -23
  91. package/dist/sync/media/companions.js.map +1 -1
  92. package/dist/sync/media/mirror_item.d.ts.map +1 -1
  93. package/dist/sync/media/mirror_item.js +33 -21
  94. package/dist/sync/media/mirror_item.js.map +1 -1
  95. package/dist/sync/media/pull_article.d.ts.map +1 -1
  96. package/dist/sync/media/pull_article.js +3 -2
  97. package/dist/sync/media/pull_article.js.map +1 -1
  98. package/dist/sync/media/pull_library.d.ts.map +1 -1
  99. package/dist/sync/media/pull_library.js +16 -11
  100. package/dist/sync/media/pull_library.js.map +1 -1
  101. package/dist/sync/media/remote_index.d.ts.map +1 -1
  102. package/dist/sync/media/remote_index.js +2 -1
  103. package/dist/sync/media/remote_index.js.map +1 -1
  104. package/dist/sync/media/types.d.ts +4 -3
  105. package/dist/sync/media/types.d.ts.map +1 -1
  106. package/dist/sync/media/types.js.map +1 -1
  107. package/dist/sync/merge.d.ts +1 -1
  108. package/dist/sync/merge.d.ts.map +1 -1
  109. package/dist/sync/merge.js +50 -17
  110. package/dist/sync/merge.js.map +1 -1
  111. package/dist/sync/new.d.ts +11 -0
  112. package/dist/sync/new.d.ts.map +1 -1
  113. package/dist/sync/new.js +81 -4
  114. package/dist/sync/new.js.map +1 -1
  115. package/dist/sync/preflight.d.ts +10 -2
  116. package/dist/sync/preflight.d.ts.map +1 -1
  117. package/dist/sync/preflight.js +119 -17
  118. package/dist/sync/preflight.js.map +1 -1
  119. package/dist/sync/pull.d.ts +6 -44
  120. package/dist/sync/pull.d.ts.map +1 -1
  121. package/dist/sync/pull.js +154 -207
  122. package/dist/sync/pull.js.map +1 -1
  123. package/dist/sync/push/commit.d.ts +61 -0
  124. package/dist/sync/push/commit.d.ts.map +1 -0
  125. package/dist/sync/push/commit.js +258 -0
  126. package/dist/sync/push/commit.js.map +1 -0
  127. package/dist/sync/push/guards.d.ts +33 -0
  128. package/dist/sync/push/guards.d.ts.map +1 -0
  129. package/dist/sync/push/guards.js +110 -0
  130. package/dist/sync/push/guards.js.map +1 -0
  131. package/dist/sync/push/index.d.ts +9 -0
  132. package/dist/sync/push/index.d.ts.map +1 -0
  133. package/dist/sync/push/index.js +372 -0
  134. package/dist/sync/push/index.js.map +1 -0
  135. package/dist/sync/push/media_stage.d.ts +56 -0
  136. package/dist/sync/push/media_stage.d.ts.map +1 -0
  137. package/dist/sync/push/media_stage.js +338 -0
  138. package/dist/sync/push/media_stage.js.map +1 -0
  139. package/dist/sync/push/merge_stage.d.ts +37 -0
  140. package/dist/sync/push/merge_stage.d.ts.map +1 -0
  141. package/dist/sync/push/merge_stage.js +257 -0
  142. package/dist/sync/push/merge_stage.js.map +1 -0
  143. package/dist/sync/push/preflight_stage.d.ts +6 -0
  144. package/dist/sync/push/preflight_stage.d.ts.map +1 -0
  145. package/dist/sync/push/preflight_stage.js +40 -0
  146. package/dist/sync/push/preflight_stage.js.map +1 -0
  147. package/dist/sync/push/tax_audit.d.ts +24 -0
  148. package/dist/sync/push/tax_audit.d.ts.map +1 -0
  149. package/dist/sync/push/tax_audit.js +70 -0
  150. package/dist/sync/push/tax_audit.js.map +1 -0
  151. package/dist/sync/push/taxonomy_plan.d.ts +36 -0
  152. package/dist/sync/push/taxonomy_plan.d.ts.map +1 -0
  153. package/dist/sync/push/taxonomy_plan.js +136 -0
  154. package/dist/sync/push/taxonomy_plan.js.map +1 -0
  155. package/dist/sync/push/transport.d.ts +14 -0
  156. package/dist/sync/push/transport.d.ts.map +1 -0
  157. package/dist/sync/push/transport.js +74 -0
  158. package/dist/sync/push/transport.js.map +1 -0
  159. package/dist/sync/push/types.d.ts +62 -0
  160. package/dist/sync/push/types.d.ts.map +1 -0
  161. package/dist/sync/push/types.js +23 -0
  162. package/dist/sync/push/types.js.map +1 -0
  163. package/dist/sync/push.d.ts +2 -57
  164. package/dist/sync/push.d.ts.map +1 -1
  165. package/dist/sync/push.js +2 -1184
  166. package/dist/sync/push.js.map +1 -1
  167. package/dist/sync/status.d.ts +3 -0
  168. package/dist/sync/status.d.ts.map +1 -1
  169. package/dist/sync/status.js +18 -8
  170. package/dist/sync/status.js.map +1 -1
  171. package/dist/sync/taxonomies.d.ts.map +1 -1
  172. package/dist/sync/taxonomies.js +21 -9
  173. package/dist/sync/taxonomies.js.map +1 -1
  174. package/dist/sync/terms.d.ts.map +1 -1
  175. package/dist/sync/terms.js +2 -1
  176. package/dist/sync/terms.js.map +1 -1
  177. package/dist/sync/workspace/commit.d.ts +46 -0
  178. package/dist/sync/workspace/commit.d.ts.map +1 -0
  179. package/dist/sync/workspace/commit.js +85 -0
  180. package/dist/sync/workspace/commit.js.map +1 -0
  181. package/dist/sync/workspace/index.d.ts +42 -0
  182. package/dist/sync/workspace/index.d.ts.map +1 -0
  183. package/dist/sync/workspace/index.js +125 -0
  184. package/dist/sync/workspace/index.js.map +1 -0
  185. package/dist/utils/disk-spelling.d.ts +26 -0
  186. package/dist/utils/disk-spelling.d.ts.map +1 -0
  187. package/dist/utils/disk-spelling.js +51 -0
  188. package/dist/utils/disk-spelling.js.map +1 -0
  189. package/dist/utils/draft-revision.d.ts +15 -0
  190. package/dist/utils/draft-revision.d.ts.map +1 -0
  191. package/dist/utils/draft-revision.js +36 -0
  192. package/dist/utils/draft-revision.js.map +1 -0
  193. package/dist/utils/frontmatter.d.ts +21 -2
  194. package/dist/utils/frontmatter.d.ts.map +1 -1
  195. package/dist/utils/frontmatter.js +71 -10
  196. package/dist/utils/frontmatter.js.map +1 -1
  197. package/dist/utils/media-body-refs.d.ts +24 -0
  198. package/dist/utils/media-body-refs.d.ts.map +1 -1
  199. package/dist/utils/media-body-refs.js +51 -0
  200. package/dist/utils/media-body-refs.js.map +1 -1
  201. package/dist/utils/media-companion.d.ts +13 -0
  202. package/dist/utils/media-companion.d.ts.map +1 -0
  203. package/dist/utils/media-companion.js +42 -0
  204. package/dist/utils/media-companion.js.map +1 -0
  205. package/dist/utils/media-id.d.ts +46 -0
  206. package/dist/utils/media-id.d.ts.map +1 -0
  207. package/dist/utils/media-id.js +84 -0
  208. package/dist/utils/media-id.js.map +1 -0
  209. package/dist/utils/naming.d.ts.map +1 -1
  210. package/dist/utils/naming.js +3 -2
  211. package/dist/utils/naming.js.map +1 -1
  212. package/dist/utils/ulid.d.ts +31 -0
  213. package/dist/utils/ulid.d.ts.map +1 -0
  214. package/dist/utils/ulid.js +65 -0
  215. package/dist/utils/ulid.js.map +1 -0
  216. package/package.json +1 -1
@@ -0,0 +1,372 @@
1
+ /*
2
+ * Copyright (C) 2026 SPORT-SERVICE RS★R
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in all
12
+ * copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+ import fs from 'node:fs';
23
+ import path from 'node:path';
24
+ import { extractRev, unwrapItem } from '../../client/api.js';
25
+ import { ApiError } from '../../client/error.js';
26
+ import { createAuthedFetch } from '../../client/http.js';
27
+ import { withEntryLock } from '../../client/lock.js';
28
+ import { localToApiPayload } from '../../client/mapper.js';
29
+ import { articleContentEndpoint } from '../../client/endpoints.js';
30
+ import { parseMarkdown } from '../../utils/frontmatter.js';
31
+ import { syncRemoteBylines } from '../bylines.js';
32
+ import { loadLocalTaxonomyCandidates, loadLocalTaxonomyMaps } from '../taxonomies.js';
33
+ import { PushValidationError, assertNotQuarantinedCopy, evaluatePrePreflightGuards, evaluatePostPreflightGuards, } from './guards.js';
34
+ import { executePreflightStage } from './preflight_stage.js';
35
+ import { canonicalizeFrontmatterTaxonomies, syncArticleTaxonomies } from './taxonomy_plan.js';
36
+ import { preserveOrResolveFeaturedImage, resolveFeaturedImageMediaId, uploadAndRewriteDraftImages, } from './media_stage.js';
37
+ import { auditRemoteTaxonomies } from './tax_audit.js';
38
+ import { resolveConflictAndMerge } from './merge_stage.js';
39
+ import { commitPostPush } from './commit.js';
40
+ import { throwRemoteArticleDeleted } from './transport.js';
41
+ export { PushValidationError, };
42
+ /**
43
+ * Pushes a local markdown article to remote EmDash with full preflight guards,
44
+ * two-tier lock lease protection, and automatic 3-way merge on conflict.
45
+ */
46
+ export async function pushArticle(targetFilePath, options) {
47
+ const fetchFn = createAuthedFetch(options);
48
+ const fullPath = path.isAbsolute(targetFilePath)
49
+ ? targetFilePath
50
+ : path.join(options.workspaceRoot, targetFilePath);
51
+ if (!fs.existsSync(fullPath)) {
52
+ throw new Error(`Target file not found: ${targetFilePath}`);
53
+ }
54
+ const originalRelPath = path.relative(options.workspaceRoot, fullPath);
55
+ // Rescue copies under "<collection>/conflicts/" are deliberately unpushable.
56
+ assertNotQuarantinedCopy(originalRelPath);
57
+ const rawContent = fs.readFileSync(fullPath, 'utf8');
58
+ const parsed = parseMarkdown(rawContent, targetFilePath);
59
+ const id = parsed.frontmatter.id;
60
+ const collection = parsed.frontmatter.collection;
61
+ // Retrieve sync state and base snapshot
62
+ const syncState = options.cache.getSyncState(id);
63
+ const baseSnapshot = options.cache.getBaseSnapshot(id);
64
+ // Pre-load local taxonomy maps once for canonicalization, G4 guard, and remote sync
65
+ const taxonomyMaps = loadLocalTaxonomyMaps(options.workspaceRoot);
66
+ // Canonicalize frontmatter taxonomy terms in-place before preflight and payload creation
67
+ const { resolvedTaxonomies } = canonicalizeFrontmatterTaxonomies(parsed, taxonomyMaps);
68
+ // Pre-build taxonomy candidates with slugToName for frontmatter serialization with inline '# <Name>' comments
69
+ const candidates = options.candidates || loadLocalTaxonomyCandidates(options.workspaceRoot);
70
+ // If knownTaxonomies is not explicitly passed, populate from local categories/ and tags/ (if present)
71
+ let knownTaxonomies = options.knownTaxonomies;
72
+ if (!knownTaxonomies) {
73
+ const knownCats = Object.keys(taxonomyMaps.slugToId.category);
74
+ const knownTags = Object.keys(taxonomyMaps.slugToId.tag);
75
+ if (knownCats.length > 0 || knownTags.length > 0) {
76
+ knownTaxonomies = {
77
+ ...(knownCats.length > 0 ? { categories: knownCats } : {}),
78
+ ...(knownTags.length > 0 ? { tags: knownTags } : {}),
79
+ };
80
+ }
81
+ }
82
+ // 1. Guard against blind re-push when article is marked delete_pending (G15)
83
+ evaluatePrePreflightGuards(syncState, options, collection, id, targetFilePath);
84
+ // 2. Preflight Guards (including G6: UNRESOLVED_CONFLICT_MARKERS - T2-1 invariant)
85
+ const preflight = executePreflightStage(targetFilePath, parsed, options, syncState, baseSnapshot, knownTaxonomies);
86
+ // 3 & 4. Post-preflight guards (G16: taxonomy conflict pending, G17: taxonomy apply pending)
87
+ evaluatePostPreflightGuards(syncState, options, collection, id, targetFilePath);
88
+ // Track 1 (Transport Token): baseSnapshot holds the raw token from server. Prioritize it to prevent 409 conflicts.
89
+ const currentRev = baseSnapshot?.rev || syncState?.remoteRev || '';
90
+ const warnings = [...preflight.warnings];
91
+ // Load local bylines map for converting frontmatter byline slugs to ULIDs
92
+ let bylineSlugToId = options.bylineSlugToId;
93
+ if (!bylineSlugToId) {
94
+ const cachedMaps = options.cache.getBylineMaps();
95
+ if (Object.keys(cachedMaps.slugToId).length > 0) {
96
+ bylineSlugToId = cachedMaps.slugToId;
97
+ }
98
+ else {
99
+ const freshMaps = await syncRemoteBylines({
100
+ origin: options.origin,
101
+ token: options.token,
102
+ refreshToken: options.refreshToken,
103
+ cache: options.cache,
104
+ fetchFn: options.fetchFn,
105
+ }).catch(() => ({ slugToId: {}, idToSlug: {} }));
106
+ bylineSlugToId = freshMaps.slugToId;
107
+ }
108
+ }
109
+ const effectiveOptions = {
110
+ ...options,
111
+ bylineSlugToId,
112
+ };
113
+ const isNewArticle = !syncState;
114
+ if (isNewArticle) {
115
+ // Upload/resolve standalone draft images and rewrite body
116
+ const { body: rewrittenBody, uploadedByAbsPath } = await uploadAndRewriteDraftImages(parsed.body, preflight.draftImages, options, fetchFn);
117
+ parsed.body = rewrittenBody;
118
+ const fiResolution = resolveFeaturedImageMediaId(parsed.frontmatter.featuredImage, uploadedByAbsPath, targetFilePath, options.workspaceRoot);
119
+ if (fiResolution) {
120
+ parsed.frontmatter.featuredImage = fiResolution.mediaId;
121
+ }
122
+ else if (parsed.frontmatter.featuredImage) {
123
+ // Defensive guard (W1): preflight guarantees resolution, but fail-fast if an unresolved value reaches push
124
+ throw new Error(`Failed to resolve featuredImage '${parsed.frontmatter.featuredImage}' to a media record ID during push.`);
125
+ }
126
+ const payload = localToApiPayload(parsed, '', effectiveOptions);
127
+ warnings.push(...preserveOrResolveFeaturedImage(payload, parsed.frontmatter.featuredImage, options.cache, id, options.workspaceRoot, { skipCompanionCheck: fiResolution?.autoUploaded }));
128
+ const postUrl = articleContentEndpoint(options.origin, collection);
129
+ const res = await fetchFn(postUrl, {
130
+ method: 'POST',
131
+ headers: {
132
+ Authorization: `Bearer ${options.token}`,
133
+ 'Content-Type': 'application/json',
134
+ },
135
+ body: JSON.stringify(payload),
136
+ });
137
+ if (!res.ok) {
138
+ const errBody = await res.text().catch(() => '');
139
+ const slugConflictDetail = 'slug-conflict-detail';
140
+ let remoteSlug;
141
+ let conflictSignal = false;
142
+ try {
143
+ const parsedJson = JSON.parse(errBody);
144
+ const errObj = parsedJson?.error || parsedJson;
145
+ remoteSlug = errObj?.details?.slug;
146
+ if (errObj?.code === 'SLUG_CONFLICT') {
147
+ conflictSignal = true;
148
+ }
149
+ else if (remoteSlug !== undefined) {
150
+ conflictSignal = slugConflictDetail;
151
+ }
152
+ else if (!errObj?.code && (res.status === 409 || res.status === 422)) {
153
+ conflictSignal = /slug/i.test(errObj?.message || '');
154
+ }
155
+ }
156
+ catch {
157
+ conflictSignal =
158
+ (res.status === 409 || res.status === 422 ? /slug/i.test(errBody) : false) ||
159
+ /slug.*(?:already exists|conflict)|unique constraint.*slug/i.test(errBody);
160
+ }
161
+ const isSlugConflict = conflictSignal === true || conflictSignal === slugConflictDetail;
162
+ const suggestion = isSlugConflict
163
+ ? [
164
+ `An article with slug "${remoteSlug || parsed.frontmatter.slug}" already exists on remote.`,
165
+ 'Change the "slug" field in frontmatter and re-run',
166
+ `"emit push ${targetFilePath}" (Emit will automatically update the filename and cache).`,
167
+ 'Note: EmDash keeps slugs reserved for trashed entries too, so a slug freed by deletion may still be taken.',
168
+ ].join(' ')
169
+ : 'Check if an article with this slug already exists or if token has create permissions.';
170
+ throw new ApiError({
171
+ message: `Create push failed: ${res.statusText} - ${errBody}`,
172
+ code: 'PUSH_CREATE_FAILED',
173
+ status: res.status,
174
+ endpoint: postUrl,
175
+ suggestion,
176
+ });
177
+ }
178
+ const createdJson = await res.json();
179
+ const createdItem = unwrapItem(createdJson);
180
+ const actualId = String(createdItem.id || id);
181
+ const newRev = extractRev(createdJson, '1');
182
+ parsed.frontmatter.id = actualId;
183
+ const taxFailures = await syncArticleTaxonomies({
184
+ articleId: actualId,
185
+ collection,
186
+ parsed,
187
+ taxonomyMaps,
188
+ resolvedTaxonomies,
189
+ baseSnapshot,
190
+ options,
191
+ fetchFn,
192
+ warnings,
193
+ });
194
+ return await commitPostPush({
195
+ mode: 'create',
196
+ actualId,
197
+ originalId: id,
198
+ originalRelPath,
199
+ fullPath,
200
+ newRev,
201
+ parsed,
202
+ rawContent,
203
+ draftImages: preflight.draftImages,
204
+ uploadedByAbsPath,
205
+ candidates,
206
+ taxFailures,
207
+ resolvedTaxonomies,
208
+ options,
209
+ fetchFn,
210
+ warnings,
211
+ createdItemCreatedAt: createdItem.createdAt || null,
212
+ updatedRawItem: createdJson,
213
+ });
214
+ }
215
+ // Existing article: execute within full 7-minute entry lock lease
216
+ return await withEntryLock(collection, id, options, async () => {
217
+ const { body: rewrittenBody, uploadedByAbsPath } = await uploadAndRewriteDraftImages(parsed.body, preflight.draftImages, options, fetchFn);
218
+ parsed.body = rewrittenBody;
219
+ const fiResolution = resolveFeaturedImageMediaId(parsed.frontmatter.featuredImage, uploadedByAbsPath, targetFilePath, options.workspaceRoot);
220
+ if (fiResolution) {
221
+ parsed.frontmatter.featuredImage = fiResolution.mediaId;
222
+ }
223
+ else if (parsed.frontmatter.featuredImage) {
224
+ // Defensive guard (W1): preflight guarantees resolution, but fail-fast if an unresolved value reaches push
225
+ throw new Error(`Failed to resolve featuredImage '${parsed.frontmatter.featuredImage}' to a media record ID during push.`);
226
+ }
227
+ let payload = localToApiPayload(parsed, currentRev, effectiveOptions);
228
+ warnings.push(...preserveOrResolveFeaturedImage(payload, parsed.frontmatter.featuredImage, options.cache, id, options.workspaceRoot, { skipCompanionCheck: fiResolution?.autoUploaded }));
229
+ const putUrl = articleContentEndpoint(options.origin, collection, id);
230
+ // Pre-PUT Taxonomy Audit
231
+ const { remoteTaxAudit, taxonomyDivergenceDetected } = await auditRemoteTaxonomies({
232
+ collection,
233
+ id,
234
+ parsed,
235
+ syncState,
236
+ baseSnapshot,
237
+ options,
238
+ fetchFn,
239
+ warnings,
240
+ });
241
+ let res = null;
242
+ if (!taxonomyDivergenceDetected) {
243
+ res = await fetchFn(putUrl, {
244
+ method: 'PUT',
245
+ headers: {
246
+ Authorization: `Bearer ${options.token}`,
247
+ 'Content-Type': 'application/json',
248
+ },
249
+ body: JSON.stringify(payload),
250
+ });
251
+ if (res.status === 404 || res.status === 410) {
252
+ throwRemoteArticleDeleted(collection, id, targetFilePath, putUrl);
253
+ }
254
+ }
255
+ // Handle 409 Conflict or detected taxonomy divergence
256
+ if (taxonomyDivergenceDetected || res?.status === 409) {
257
+ const conflictOutcome = await resolveConflictAndMerge({
258
+ collection,
259
+ id,
260
+ targetFilePath,
261
+ fullPath,
262
+ putUrl,
263
+ res,
264
+ taxonomyDivergenceDetected,
265
+ parsed,
266
+ syncState,
267
+ baseSnapshot,
268
+ remoteTaxAudit,
269
+ candidates,
270
+ options,
271
+ fetchFn,
272
+ warnings,
273
+ });
274
+ if (conflictOutcome.handled && conflictOutcome.pushResult) {
275
+ return conflictOutcome.pushResult;
276
+ }
277
+ if (conflictOutcome.mergedParsed && conflictOutcome.remoteParsedRev) {
278
+ parsed.frontmatter = conflictOutcome.mergedParsed.frontmatter;
279
+ parsed.body = conflictOutcome.mergedParsed.body;
280
+ payload = localToApiPayload(parsed, conflictOutcome.remoteParsedRev, effectiveOptions);
281
+ warnings.push(...preserveOrResolveFeaturedImage(payload, parsed.frontmatter.featuredImage, options.cache, id, options.workspaceRoot, {
282
+ skipCompanionCheck: fiResolution?.mediaId === parsed.frontmatter.featuredImage &&
283
+ fiResolution?.autoUploaded,
284
+ }));
285
+ res = await fetchFn(putUrl, {
286
+ method: 'PUT',
287
+ headers: {
288
+ Authorization: `Bearer ${options.token}`,
289
+ 'Content-Type': 'application/json',
290
+ },
291
+ body: JSON.stringify(payload),
292
+ });
293
+ if (!res.ok) {
294
+ if (res.status === 404 || res.status === 410) {
295
+ throwRemoteArticleDeleted(collection, id, targetFilePath, putUrl);
296
+ }
297
+ const errText = await res.text().catch(() => '');
298
+ const mergeResult = conflictOutcome.mergeResult;
299
+ if ((res.status === 400 || res.status === 422) &&
300
+ ((mergeResult?.autoMergedTaxonomies && mergeResult.autoMergedTaxonomies.length > 0) ||
301
+ (mergeResult?.strategyAdoptions && mergeResult.strategyAdoptions.length > 0))) {
302
+ const wasStrategy = mergeResult.strategyAdoptions && mergeResult.strategyAdoptions.length > 0;
303
+ throw new ApiError({
304
+ message: `Remote server rejected the merged taxonomy union (HTTP ${res.status}): ${errText || res.statusText}`,
305
+ code: 'TAXONOMY_UNION_REJECTED',
306
+ status: res.status,
307
+ endpoint: putUrl,
308
+ suggestion: wasStrategy
309
+ ? 'The remote server rejected the taxonomy adopted by your strategy (e.g. single-category constraint). Switch strategy with "--resolve-taxonomy=remote" or edit the frontmatter manually before pushing.'
310
+ : 'The remote server enforces constraints that reject combining local and remote taxonomy terms (e.g. single-category constraint). Resolve by explicitly choosing a side: "emit push <file> --resolve-taxonomy=local" (or remote), or edit the frontmatter manually.',
311
+ });
312
+ }
313
+ throw new ApiError({
314
+ message: `Retry push after auto-merge failed: ${res.statusText}${errText ? ` - ${errText}` : ''}`,
315
+ code: 'PUSH_RETRY_FAILED',
316
+ status: res.status,
317
+ endpoint: putUrl,
318
+ suggestion: 'Another change might have been committed in the meantime. Try pulling again.',
319
+ });
320
+ }
321
+ }
322
+ }
323
+ if (!res || !res.ok) {
324
+ if (res && (res.status === 404 || res.status === 410)) {
325
+ throwRemoteArticleDeleted(collection, id, targetFilePath, putUrl);
326
+ }
327
+ const errText = res ? await res.text().catch(() => '') : '';
328
+ throw new ApiError({
329
+ message: `Push failed: ${res ? res.statusText : 'Request failed'}${errText ? ` - ${errText}` : ''}`,
330
+ code: 'PUSH_FAILED',
331
+ status: res ? res.status : 500,
332
+ endpoint: putUrl,
333
+ suggestion: 'Verify network connectivity, token permissions, and article status.',
334
+ });
335
+ }
336
+ const updatedJson = await res.json().catch(() => ({}));
337
+ const newRev = extractRev(updatedJson, currentRev);
338
+ const taxFailures = await syncArticleTaxonomies({
339
+ articleId: id,
340
+ collection,
341
+ parsed,
342
+ taxonomyMaps,
343
+ resolvedTaxonomies,
344
+ baseSnapshot,
345
+ options,
346
+ fetchFn,
347
+ warnings,
348
+ });
349
+ return await commitPostPush({
350
+ mode: 'update',
351
+ actualId: id,
352
+ originalId: id,
353
+ originalRelPath,
354
+ fullPath,
355
+ newRev,
356
+ parsed,
357
+ rawContent,
358
+ draftImages: preflight.draftImages,
359
+ uploadedByAbsPath,
360
+ candidates,
361
+ taxFailures,
362
+ resolvedTaxonomies,
363
+ options,
364
+ fetchFn,
365
+ warnings,
366
+ syncState,
367
+ baseSnapshot,
368
+ updatedRawItem: updatedJson,
369
+ });
370
+ });
371
+ }
372
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/sync/push/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ import { type localToApiPayload } from '../../client/mapper.js';
2
+ import { type UploadMediaResult } from '../../client/media.js';
3
+ import type { CacheDatabase } from '../../db/cache.js';
4
+ import { type DraftImageReference } from '../preflight.js';
5
+ import type { PushOptions } from './types.js';
6
+ export interface FeaturedImageResolveOptions {
7
+ skipCompanionCheck?: boolean;
8
+ }
9
+ /**
10
+ * Resolves the canonical media record ULID for frontmatter featuredImage.
11
+ *
12
+ * If featuredImage is a local path (draft or promoted), it resolves the mediaId from
13
+ * the shared uploadedByAbsPath map. Returns autoUploaded: true when resolved from
14
+ * a newly uploaded or reused draft image reference so that advisory verification
15
+ * warnings can be suppressed.
16
+ */
17
+ export declare function resolveFeaturedImageMediaId(featuredImage: string | null | undefined, uploadedByAbsPath: Map<string, UploadMediaResult>, filePath: string, workspaceRoot?: string): {
18
+ mediaId: string;
19
+ autoUploaded: boolean;
20
+ } | null;
21
+ /**
22
+ * Featured-image preservation / resolution helper.
23
+ *
24
+ * EmDash stores `featured_image: { id: "<ULID>" }`.
25
+ * When pushing an article whose frontmatter sets `featuredImage: <ULID>`, Emit cross-checks that the
26
+ * referenced media exists locally (or was mirrored), and preserves the exact remote object structure
27
+ * from the cache when unchanged.
28
+ */
29
+ export declare function preserveOrResolveFeaturedImage(payload: ReturnType<typeof localToApiPayload>, featuredImage: string | null | undefined, cache: CacheDatabase | undefined, id: string, workspaceRoot?: string, options?: FeaturedImageResolveOptions): string[];
30
+ /**
31
+ * Rewrites standalone local draft image lines in Markdown body to their confirmed
32
+ * canonical root-relative URLs (`/_emdash/api/media/file/<storageKey>`), skipping
33
+ * fenced code blocks and opaque block fences.
34
+ */
35
+ export declare function rewriteDraftImagesInBody(body: string, draftImages: DraftImageReference[], uploadedByAbsPath: Map<string, UploadMediaResult>): string;
36
+ /**
37
+ * Promotes uploaded draft images from `media/drafts/<filename>` into
38
+ * `media/<markdownRef>.<ext>` along with companion `.yaml` metadata, where markdownRef is derived from the
39
+ * and commits `status = 'ready'` in `media_state`.
40
+ *
41
+ * Protected by try/catch per asset (C-2) so that any local filesystem failure
42
+ * after a successful remote article PUT/POST is downgraded to a warning rather than
43
+ * aborting `saveBaseSnapshot` / `saveSyncState`.
44
+ */
45
+ export declare function finalizePromotedDraftImages(draftImages: DraftImageReference[], uploadedByAbsPath: Map<string, UploadMediaResult>, options: PushOptions, warnings: string[]): void;
46
+ /**
47
+ * Executes P2–P4 (upload/resolve draft images and rewrite Markdown body in-memory).
48
+ * Already-promoted assets are verified against the remote before their canonical URL is
49
+ * baked into the body, so a media record deleted on the EmDash side can never produce an
50
+ * article that renders HTTP 404 images in production.
51
+ */
52
+ export declare function uploadAndRewriteDraftImages(body: string, draftImages: DraftImageReference[], options: PushOptions, fetchFn: typeof fetch): Promise<{
53
+ body: string;
54
+ uploadedByAbsPath: Map<string, UploadMediaResult>;
55
+ }>;
56
+ //# sourceMappingURL=media_stage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media_stage.d.ts","sourceRoot":"","sources":["../../../src/sync/push/media_stage.ts"],"names":[],"mappings":"AAyBA,OAAO,EAAoB,KAAK,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAClF,OAAO,EAKL,KAAK,iBAAiB,EACvB,MAAM,uBAAuB,CAAC;AAI/B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAyB,KAAK,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAElF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,MAAM,WAAW,2BAA2B;IAC1C,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED;;;;;;;GAOG;AACH,wBAAgB,2BAA2B,CACzC,aAAa,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACxC,iBAAiB,EAAE,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,EACjD,QAAQ,EAAE,MAAM,EAChB,aAAa,CAAC,EAAE,MAAM,GACrB;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,CAkBnD;AAED;;;;;;;GAOG;AACH,wBAAgB,8BAA8B,CAC5C,OAAO,EAAE,UAAU,CAAC,OAAO,iBAAiB,CAAC,EAC7C,aAAa,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACxC,KAAK,EAAE,aAAa,GAAG,SAAS,EAChC,EAAE,EAAE,MAAM,EACV,aAAa,CAAC,EAAE,MAAM,EACtB,OAAO,CAAC,EAAE,2BAA2B,GACpC,MAAM,EAAE,CA0FV;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,mBAAmB,EAAE,EAClC,iBAAiB,EAAE,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,GAChD,MAAM,CA8CR;AAED;;;;;;;;GAQG;AACH,wBAAgB,2BAA2B,CACzC,WAAW,EAAE,mBAAmB,EAAE,EAClC,iBAAiB,EAAE,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,EACjD,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,MAAM,EAAE,GACjB,IAAI,CA4FN;AAED;;;;;GAKG;AACH,wBAAsB,2BAA2B,CAC/C,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,mBAAmB,EAAE,EAClC,OAAO,EAAE,WAAW,EACpB,OAAO,EAAE,OAAO,KAAK,GACpB,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,iBAAiB,EAAE,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAA;CAAE,CAAC,CAwE9E"}