emdash-emit 0.1.7 → 0.1.9

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 (125) hide show
  1. package/README.md +237 -82
  2. package/dist/cli/commands/new.d.ts.map +1 -1
  3. package/dist/cli/commands/new.js +3 -1
  4. package/dist/cli/commands/new.js.map +1 -1
  5. package/dist/cli/commands/profile.d.ts.map +1 -1
  6. package/dist/cli/commands/profile.js +67 -13
  7. package/dist/cli/commands/profile.js.map +1 -1
  8. package/dist/cli/commands/pull.d.ts +5 -0
  9. package/dist/cli/commands/pull.d.ts.map +1 -1
  10. package/dist/cli/commands/pull.js +96 -39
  11. package/dist/cli/commands/pull.js.map +1 -1
  12. package/dist/cli/commands/push.d.ts.map +1 -1
  13. package/dist/cli/commands/push.js +17 -2
  14. package/dist/cli/commands/push.js.map +1 -1
  15. package/dist/cli/commands/status.d.ts +19 -0
  16. package/dist/cli/commands/status.d.ts.map +1 -1
  17. package/dist/cli/commands/status.js +93 -4
  18. package/dist/cli/commands/status.js.map +1 -1
  19. package/dist/cli/context.d.ts +35 -0
  20. package/dist/cli/context.d.ts.map +1 -1
  21. package/dist/cli/context.js +112 -10
  22. package/dist/cli/context.js.map +1 -1
  23. package/dist/client/auth.d.ts +1 -0
  24. package/dist/client/auth.d.ts.map +1 -1
  25. package/dist/client/auth.js +1 -0
  26. package/dist/client/auth.js.map +1 -1
  27. package/dist/client/http.d.ts.map +1 -1
  28. package/dist/client/http.js +27 -10
  29. package/dist/client/http.js.map +1 -1
  30. package/dist/client/mapper.d.ts +12 -0
  31. package/dist/client/mapper.d.ts.map +1 -1
  32. package/dist/client/mapper.js +65 -0
  33. package/dist/client/mapper.js.map +1 -1
  34. package/dist/client/media.d.ts +141 -0
  35. package/dist/client/media.d.ts.map +1 -0
  36. package/dist/client/media.js +582 -0
  37. package/dist/client/media.js.map +1 -0
  38. package/dist/client/portable-text.d.ts +22 -1
  39. package/dist/client/portable-text.d.ts.map +1 -1
  40. package/dist/client/portable-text.js +85 -5
  41. package/dist/client/portable-text.js.map +1 -1
  42. package/dist/db/cache.d.ts +16 -0
  43. package/dist/db/cache.d.ts.map +1 -1
  44. package/dist/db/cache.js +67 -0
  45. package/dist/db/cache.js.map +1 -1
  46. package/dist/index.d.ts +1 -0
  47. package/dist/index.d.ts.map +1 -1
  48. package/dist/index.js +1 -0
  49. package/dist/index.js.map +1 -1
  50. package/dist/mcp/context.d.ts +1 -0
  51. package/dist/mcp/context.d.ts.map +1 -1
  52. package/dist/mcp/server.d.ts +8 -0
  53. package/dist/mcp/server.d.ts.map +1 -1
  54. package/dist/mcp/server.js +60 -15
  55. package/dist/mcp/server.js.map +1 -1
  56. package/dist/mcp/tool-errors.d.ts +11 -0
  57. package/dist/mcp/tool-errors.d.ts.map +1 -0
  58. package/dist/mcp/tool-errors.js +62 -0
  59. package/dist/mcp/tool-errors.js.map +1 -0
  60. package/dist/mcp/tools/profile.d.ts +9 -2
  61. package/dist/mcp/tools/profile.d.ts.map +1 -1
  62. package/dist/mcp/tools/profile.js +49 -36
  63. package/dist/mcp/tools/profile.js.map +1 -1
  64. package/dist/mcp/tools/sync-diff.d.ts.map +1 -1
  65. package/dist/mcp/tools/sync-diff.js +18 -70
  66. package/dist/mcp/tools/sync-diff.js.map +1 -1
  67. package/dist/mcp/tools/sync-new.d.ts.map +1 -1
  68. package/dist/mcp/tools/sync-new.js +40 -21
  69. package/dist/mcp/tools/sync-new.js.map +1 -1
  70. package/dist/mcp/tools/sync-pull.d.ts.map +1 -1
  71. package/dist/mcp/tools/sync-pull.js +110 -86
  72. package/dist/mcp/tools/sync-pull.js.map +1 -1
  73. package/dist/mcp/tools/sync-push.d.ts.map +1 -1
  74. package/dist/mcp/tools/sync-push.js +24 -32
  75. package/dist/mcp/tools/sync-push.js.map +1 -1
  76. package/dist/mcp/tools/sync-status.d.ts.map +1 -1
  77. package/dist/mcp/tools/sync-status.js +55 -4
  78. package/dist/mcp/tools/sync-status.js.map +1 -1
  79. package/dist/sync/diff.d.ts +36 -0
  80. package/dist/sync/diff.d.ts.map +1 -1
  81. package/dist/sync/diff.js +118 -0
  82. package/dist/sync/diff.js.map +1 -1
  83. package/dist/sync/media.d.ts +16 -0
  84. package/dist/sync/media.d.ts.map +1 -1
  85. package/dist/sync/media.js +180 -26
  86. package/dist/sync/media.js.map +1 -1
  87. package/dist/sync/merge.d.ts.map +1 -1
  88. package/dist/sync/merge.js +1 -0
  89. package/dist/sync/merge.js.map +1 -1
  90. package/dist/sync/orchestrator.d.ts +6 -0
  91. package/dist/sync/orchestrator.d.ts.map +1 -1
  92. package/dist/sync/orchestrator.js +52 -27
  93. package/dist/sync/orchestrator.js.map +1 -1
  94. package/dist/sync/preflight.d.ts +30 -2
  95. package/dist/sync/preflight.d.ts.map +1 -1
  96. package/dist/sync/preflight.js +274 -10
  97. package/dist/sync/preflight.js.map +1 -1
  98. package/dist/sync/pull.d.ts +47 -0
  99. package/dist/sync/pull.d.ts.map +1 -1
  100. package/dist/sync/pull.js +148 -8
  101. package/dist/sync/pull.js.map +1 -1
  102. package/dist/sync/push.d.ts +2 -0
  103. package/dist/sync/push.d.ts.map +1 -1
  104. package/dist/sync/push.js +332 -6
  105. package/dist/sync/push.js.map +1 -1
  106. package/dist/sync/status.d.ts +11 -1
  107. package/dist/sync/status.d.ts.map +1 -1
  108. package/dist/sync/status.js +40 -6
  109. package/dist/sync/status.js.map +1 -1
  110. package/dist/sync/taxonomies.d.ts.map +1 -1
  111. package/dist/sync/taxonomies.js +39 -4
  112. package/dist/sync/taxonomies.js.map +1 -1
  113. package/dist/utils/config.d.ts +11 -0
  114. package/dist/utils/config.d.ts.map +1 -1
  115. package/dist/utils/config.js +14 -4
  116. package/dist/utils/config.js.map +1 -1
  117. package/dist/utils/frontmatter.d.ts +16 -1
  118. package/dist/utils/frontmatter.d.ts.map +1 -1
  119. package/dist/utils/frontmatter.js +66 -7
  120. package/dist/utils/frontmatter.js.map +1 -1
  121. package/dist/utils/profile.d.ts +3 -3
  122. package/dist/utils/profile.d.ts.map +1 -1
  123. package/dist/utils/profile.js +105 -8
  124. package/dist/utils/profile.js.map +1 -1
  125. package/package.json +4 -1
@@ -0,0 +1,582 @@
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 crypto from 'node:crypto';
23
+ import fs from 'node:fs';
24
+ import path from 'node:path';
25
+ import { unwrapEnvelope } from './api.js';
26
+ import { ApiError } from './error.js';
27
+ import { createAuthedFetch } from './http.js';
28
+ /**
29
+ * Maximum media upload size allowed by EmDash OpenAPI 3.1.0 schema (MediaUploadUrlBody.size.maximum).
30
+ * 50 MiB = 52,428,800 bytes.
31
+ */
32
+ export const MAX_MEDIA_UPLOAD_BYTES = 52_428_800;
33
+ const SUPPORTED_IMAGE_MIME_TYPES = {
34
+ '.jpg': 'image/jpeg',
35
+ '.jpeg': 'image/jpeg',
36
+ '.png': 'image/png',
37
+ '.webp': 'image/webp',
38
+ '.gif': 'image/gif',
39
+ '.avif': 'image/avif',
40
+ '.svg': 'image/svg+xml',
41
+ };
42
+ export function resolveImageMimeType(filename) {
43
+ const ext = path.extname(filename).toLowerCase();
44
+ return SUPPORTED_IMAGE_MIME_TYPES[ext] ?? null;
45
+ }
46
+ export function computeMediaHashes(buffer) {
47
+ const localHash = crypto.createHash('sha256').update(buffer).digest('hex');
48
+ const sha1Hex = crypto.createHash('sha1').update(buffer).digest('hex');
49
+ return {
50
+ localHash,
51
+ serverContentHash: `sha1:${sha1Hex}`,
52
+ };
53
+ }
54
+ /**
55
+ * Normalizes a media file URL into the canonical root-relative form:
56
+ * `/_emdash/api/media/file/<storageKey>`
57
+ *
58
+ * Note: `storageKey` may be either `<ULID>.<ext>` (legacy migrated media) or
59
+ * `<ULID_1>.<ULID_2>.<ext>` (native EmDash uploads after confirmation). Treat it as an opaque key.
60
+ */
61
+ export function toRootRelativeMediaUrl(rawUrl, storageKey, origin) {
62
+ if (rawUrl && typeof rawUrl === 'string' && rawUrl.trim().length > 0) {
63
+ const trimmed = rawUrl.trim();
64
+ if (trimmed.startsWith('/')) {
65
+ return trimmed;
66
+ }
67
+ try {
68
+ const parsed = new URL(trimmed, origin);
69
+ return parsed.pathname;
70
+ }
71
+ catch {
72
+ // Fall through to storageKey construction
73
+ }
74
+ }
75
+ return `/_emdash/api/media/file/${storageKey}`;
76
+ }
77
+ /**
78
+ * Fetches authoritative metadata for a single media record via `GET /_emdash/api/media/{mediaId}`.
79
+ * Returns:
80
+ * - `{ exists: true, item }` when the server returns 200 OK
81
+ * - `{ exists: false }` when the server returns 404 Not Found (deleted on remote)
82
+ * - `{ exists: 'unknown', reason }` on transient errors or when the server does not implement GET
83
+ */
84
+ export async function fetchRemoteMediaDetail(mediaId, options) {
85
+ const itemUrl = new URL(`/_emdash/api/media/${mediaId}`, options.origin).toString();
86
+ try {
87
+ const res = await options.fetchFn(itemUrl, {
88
+ method: 'GET',
89
+ headers: {
90
+ Authorization: `Bearer ${options.token}`,
91
+ Accept: 'application/json',
92
+ },
93
+ });
94
+ if (res.status === 404) {
95
+ return { exists: false };
96
+ }
97
+ if (!res.ok) {
98
+ return {
99
+ exists: 'unknown',
100
+ reason: `GET /_emdash/api/media/${mediaId} returned HTTP ${res.status} ${res.statusText}`,
101
+ };
102
+ }
103
+ const json = await res.json();
104
+ const data = unwrapEnvelope(json);
105
+ const raw = ((data.item && typeof data.item === 'object' ? data.item : data) ?? {});
106
+ return {
107
+ exists: true,
108
+ item: {
109
+ id: typeof raw.id === 'string' && raw.id ? raw.id : mediaId,
110
+ filename: typeof raw.filename === 'string' ? raw.filename : undefined,
111
+ mimeType: typeof raw.mimeType === 'string' ? raw.mimeType : undefined,
112
+ size: typeof raw.size === 'number' ? raw.size : undefined,
113
+ width: typeof raw.width === 'number' ? raw.width : null,
114
+ height: typeof raw.height === 'number' ? raw.height : null,
115
+ alt: typeof raw.alt === 'string' ? raw.alt : null,
116
+ caption: typeof raw.caption === 'string' ? raw.caption : null,
117
+ storageKey: typeof raw.storageKey === 'string' ? raw.storageKey : undefined,
118
+ contentHash: typeof raw.contentHash === 'string' ? raw.contentHash : null,
119
+ status: typeof raw.status === 'string' ? raw.status : undefined,
120
+ createdAt: typeof raw.createdAt === 'string' ? raw.createdAt : undefined,
121
+ url: typeof raw.url === 'string' ? raw.url : undefined,
122
+ },
123
+ };
124
+ }
125
+ catch (err) {
126
+ return {
127
+ exists: 'unknown',
128
+ reason: `GET /_emdash/api/media/${mediaId} failed: ` + (err?.message || 'network error'),
129
+ };
130
+ }
131
+ }
132
+ /**
133
+ * Best-effort update of `alt` / `caption` on the remote media record via `PUT /_emdash/api/media/{mediaId}`.
134
+ */
135
+ async function syncRemoteMediaMetadata(mediaId, metadata, options) {
136
+ const body = {};
137
+ if (metadata.alt && metadata.alt.trim().length > 0) {
138
+ body.alt = metadata.alt.trim();
139
+ }
140
+ if (metadata.caption && metadata.caption.trim().length > 0) {
141
+ body.caption = metadata.caption.trim();
142
+ }
143
+ if (Object.keys(body).length === 0)
144
+ return;
145
+ const itemUrl = new URL(`/_emdash/api/media/${mediaId}`, options.origin).toString();
146
+ try {
147
+ await options.fetchFn(itemUrl, {
148
+ method: 'PUT',
149
+ headers: {
150
+ Authorization: `Bearer ${options.token}`,
151
+ 'Content-Type': 'application/json',
152
+ Accept: 'application/json',
153
+ },
154
+ body: JSON.stringify(body),
155
+ });
156
+ }
157
+ catch {
158
+ // Non-fatal: alt text is already embedded in the Portable Text image block
159
+ }
160
+ }
161
+ /**
162
+ * Compensating transaction for failed uploads after `POST /_emdash/api/media/upload-url`
163
+ * has created a pending record on the server.
164
+ *
165
+ * 1. Reads `GET /_emdash/api/media/{mediaId}` to inspect `status`.
166
+ * 2. If `status` is `pending` or `failed`, calls `DELETE /_emdash/api/media/{mediaId}`.
167
+ * 3. If `status` is `ready`, preserves the record and returns `alreadyReadyItem`.
168
+ */
169
+ export async function compensateOrphanMedia(mediaId, options) {
170
+ const { origin, token, fetchFn } = options;
171
+ const itemUrl = new URL(`/_emdash/api/media/${mediaId}`, origin).toString();
172
+ const authHeaders = {
173
+ Authorization: `Bearer ${token}`,
174
+ Accept: 'application/json',
175
+ };
176
+ try {
177
+ const getRes = await fetchFn(itemUrl, { method: 'GET', headers: authHeaders });
178
+ if (getRes.status === 404) {
179
+ return { compensated: true };
180
+ }
181
+ if (getRes.ok) {
182
+ const json = await getRes.json();
183
+ const data = unwrapEnvelope(json);
184
+ const item = ((data.item && typeof data.item === 'object' ? data.item : data) ??
185
+ {});
186
+ if (item.status === 'ready') {
187
+ return { compensated: false, alreadyReadyItem: item };
188
+ }
189
+ }
190
+ }
191
+ catch {
192
+ // Proceed to attempt DELETE even if GET failed due to transient network error
193
+ }
194
+ try {
195
+ const delRes = await fetchFn(itemUrl, { method: 'DELETE', headers: authHeaders });
196
+ if (delRes.ok || delRes.status === 404) {
197
+ return { compensated: true };
198
+ }
199
+ return { compensated: false };
200
+ }
201
+ catch {
202
+ return { compensated: false };
203
+ }
204
+ }
205
+ /**
206
+ * Requests an upload target from `POST /_emdash/api/media/upload-url`.
207
+ */
208
+ async function requestUploadTarget(params, options) {
209
+ const endpoint = new URL('/_emdash/api/media/upload-url', options.origin).toString();
210
+ const res = await options.fetchFn(endpoint, {
211
+ method: 'POST',
212
+ headers: {
213
+ Authorization: `Bearer ${options.token}`,
214
+ 'Content-Type': 'application/json',
215
+ Accept: 'application/json',
216
+ },
217
+ body: JSON.stringify({
218
+ filename: params.filename,
219
+ contentType: params.contentType,
220
+ size: params.size,
221
+ deduplicate: true,
222
+ contentHash: params.contentHash,
223
+ }),
224
+ });
225
+ if (!res.ok) {
226
+ throw new ApiError({
227
+ message: `Failed to obtain media upload URL for "${params.filename}": ${res.statusText}`,
228
+ code: 'MEDIA_UPLOAD_URL_FAILED',
229
+ status: res.status,
230
+ endpoint,
231
+ suggestion: 'Verify authentication token permissions and network connectivity.',
232
+ });
233
+ }
234
+ const json = await res.json();
235
+ const data = unwrapEnvelope(json);
236
+ if (data.existing === true) {
237
+ return {
238
+ existing: true,
239
+ mediaId: String(data.mediaId),
240
+ storageKey: String(data.storageKey),
241
+ url: String(data.url || `/_emdash/api/media/file/${String(data.storageKey)}`),
242
+ };
243
+ }
244
+ return {
245
+ uploadUrl: String(data.uploadUrl),
246
+ method: 'PUT',
247
+ headers: data.headers && typeof data.headers === 'object'
248
+ ? data.headers
249
+ : {},
250
+ mediaId: String(data.mediaId),
251
+ storageKey: String(data.storageKey),
252
+ expiresAt: String(data.expiresAt || ''),
253
+ };
254
+ }
255
+ /**
256
+ * Executes the confirmed 4-phase EmDash media upload contract with server-side
257
+ * deduplication, local `media_state` idempotency, and orphan-pending compensation.
258
+ */
259
+ export async function uploadMediaFile(options) {
260
+ const fetchFn = createAuthedFetch(options);
261
+ const rawFetchFn = options.fetchFn || fetch;
262
+ const buffer = options.buffer ?? (options.filePath ? fs.readFileSync(options.filePath) : undefined);
263
+ if (!buffer) {
264
+ throw new Error('Either buffer or filePath must be provided to uploadMediaFile.');
265
+ }
266
+ const mimeType = options.contentType || resolveImageMimeType(options.filename);
267
+ if (!mimeType) {
268
+ throw new ApiError({
269
+ message: `Unsupported image MIME type for "${options.filename}".`,
270
+ code: 'INVALID_LOCAL_IMAGE_TYPE',
271
+ endpoint: '/_emdash/api/media/upload-url',
272
+ suggestion: 'Use a supported image format (.jpg, .jpeg, .png, .webp, .gif, .avif, .svg).',
273
+ });
274
+ }
275
+ if (buffer.length > MAX_MEDIA_UPLOAD_BYTES) {
276
+ throw new ApiError({
277
+ message: `Image "${options.filename}" (${buffer.length} bytes) exceeds maximum upload size of ${MAX_MEDIA_UPLOAD_BYTES} bytes (50 MiB).`,
278
+ code: 'LOCAL_IMAGE_SIZE_EXCEEDED',
279
+ endpoint: '/_emdash/api/media/upload-url',
280
+ suggestion: 'Compress or resize the image below 50 MiB before pushing.',
281
+ });
282
+ }
283
+ const { localHash, serverContentHash } = computeMediaHashes(buffer);
284
+ // Check local media_state for recovery from the "upload succeeded -> article PUT failed" window,
285
+ // verifying against GET /_emdash/api/media/{mediaId} that the record has not been deleted on remote (B-4).
286
+ if (options.cache) {
287
+ const existingLocal = options.cache.getMediaState(localHash);
288
+ if (existingLocal &&
289
+ (existingLocal.status === 'ready' || existingLocal.status === 'uploaded') &&
290
+ existingLocal.mediaId &&
291
+ existingLocal.storageKey &&
292
+ existingLocal.url) {
293
+ const remoteCheck = await fetchRemoteMediaDetail(existingLocal.mediaId, {
294
+ origin: options.origin,
295
+ token: options.token,
296
+ fetchFn,
297
+ });
298
+ if (remoteCheck.exists === false ||
299
+ (remoteCheck.exists === true &&
300
+ remoteCheck.item.status &&
301
+ remoteCheck.item.status !== 'ready')) {
302
+ // Remote record was deleted or invalidated; purge stale local state and proceed to Phase A
303
+ options.cache.deleteMediaState(localHash);
304
+ }
305
+ else {
306
+ const detail = remoteCheck.exists === true ? remoteCheck.item : undefined;
307
+ const confirmedStorageKey = detail?.storageKey || existingLocal.storageKey;
308
+ const confirmedUrl = toRootRelativeMediaUrl(detail?.url || existingLocal.url, confirmedStorageKey, options.origin);
309
+ return {
310
+ mediaId: existingLocal.mediaId,
311
+ storageKey: confirmedStorageKey,
312
+ url: confirmedUrl,
313
+ localHash,
314
+ contentHash: detail?.contentHash || serverContentHash,
315
+ size: detail?.size ?? buffer.length,
316
+ mimeType: detail?.mimeType || mimeType,
317
+ filename: detail?.filename || options.filename,
318
+ width: detail?.width ?? null,
319
+ height: detail?.height ?? null,
320
+ createdAt: detail?.createdAt,
321
+ deduplicated: true,
322
+ remoteVerification: remoteCheck.exists === true ? 'verified' : 'unknown',
323
+ remoteVerificationNote: remoteCheck.exists === 'unknown' ? remoteCheck.reason : undefined,
324
+ };
325
+ }
326
+ }
327
+ }
328
+ // [Phase A] Request upload target (or existing deduplicated record)
329
+ const initialTarget = await requestUploadTarget({
330
+ filename: options.filename,
331
+ contentType: mimeType,
332
+ size: buffer.length,
333
+ contentHash: serverContentHash,
334
+ }, { origin: options.origin, token: options.token, fetchFn });
335
+ // Handle server-side deduplication hit (B-1: fetch authoritative createdAt/dimensions via GET)
336
+ if ('existing' in initialTarget) {
337
+ const remoteCheck = await fetchRemoteMediaDetail(initialTarget.mediaId, {
338
+ origin: options.origin,
339
+ token: options.token,
340
+ fetchFn,
341
+ });
342
+ const detail = remoteCheck.exists === true ? remoteCheck.item : undefined;
343
+ const confirmedStorageKey = detail?.storageKey || initialTarget.storageKey;
344
+ const canonicalUrl = toRootRelativeMediaUrl(detail?.url || initialTarget.url, confirmedStorageKey, options.origin);
345
+ options.cache?.saveMediaState({
346
+ localHash,
347
+ mediaId: initialTarget.mediaId,
348
+ storageKey: confirmedStorageKey,
349
+ url: canonicalUrl,
350
+ status: 'uploaded',
351
+ });
352
+ return {
353
+ mediaId: initialTarget.mediaId,
354
+ storageKey: confirmedStorageKey,
355
+ url: canonicalUrl,
356
+ localHash,
357
+ contentHash: detail?.contentHash || serverContentHash,
358
+ size: detail?.size ?? buffer.length,
359
+ mimeType: detail?.mimeType || mimeType,
360
+ filename: detail?.filename || options.filename,
361
+ width: detail?.width ?? null,
362
+ height: detail?.height ?? null,
363
+ createdAt: detail?.createdAt,
364
+ deduplicated: true,
365
+ remoteVerification: remoteCheck.exists === true ? 'verified' : 'unknown',
366
+ remoteVerificationNote: remoteCheck.exists === 'unknown' ? remoteCheck.reason : undefined,
367
+ };
368
+ }
369
+ let target = initialTarget;
370
+ // Check if expiresAt is already expired; if so, clean up old pending mediaId and re-request once
371
+ if (target.expiresAt) {
372
+ const expMs = Date.parse(target.expiresAt);
373
+ if (!Number.isNaN(expMs) && expMs <= Date.now()) {
374
+ await compensateOrphanMedia(target.mediaId, {
375
+ origin: options.origin,
376
+ token: options.token,
377
+ fetchFn,
378
+ });
379
+ const retriedTarget = await requestUploadTarget({
380
+ filename: options.filename,
381
+ contentType: mimeType,
382
+ size: buffer.length,
383
+ contentHash: serverContentHash,
384
+ }, { origin: options.origin, token: options.token, fetchFn });
385
+ if ('existing' in retriedTarget) {
386
+ const remoteCheck = await fetchRemoteMediaDetail(retriedTarget.mediaId, {
387
+ origin: options.origin,
388
+ token: options.token,
389
+ fetchFn,
390
+ });
391
+ const detail = remoteCheck.exists === true ? remoteCheck.item : undefined;
392
+ const confirmedStorageKey = detail?.storageKey || retriedTarget.storageKey;
393
+ const canonicalUrl = toRootRelativeMediaUrl(detail?.url || retriedTarget.url, confirmedStorageKey, options.origin);
394
+ options.cache?.saveMediaState({
395
+ localHash,
396
+ mediaId: retriedTarget.mediaId,
397
+ storageKey: confirmedStorageKey,
398
+ url: canonicalUrl,
399
+ status: 'uploaded',
400
+ });
401
+ return {
402
+ mediaId: retriedTarget.mediaId,
403
+ storageKey: confirmedStorageKey,
404
+ url: canonicalUrl,
405
+ localHash,
406
+ contentHash: detail?.contentHash || serverContentHash,
407
+ size: detail?.size ?? buffer.length,
408
+ mimeType: detail?.mimeType || mimeType,
409
+ filename: detail?.filename || options.filename,
410
+ width: detail?.width ?? null,
411
+ height: detail?.height ?? null,
412
+ createdAt: detail?.createdAt,
413
+ deduplicated: true,
414
+ remoteVerification: remoteCheck.exists === true ? 'verified' : 'unknown',
415
+ remoteVerificationNote: remoteCheck.exists === 'unknown' ? remoteCheck.reason : undefined,
416
+ };
417
+ }
418
+ target = retriedTarget;
419
+ }
420
+ }
421
+ const pendingMediaId = target.mediaId;
422
+ const initialStorageKey = target.storageKey;
423
+ // Record pending state immediately so we never lose track of the created server record
424
+ options.cache?.saveMediaState({
425
+ localHash,
426
+ mediaId: pendingMediaId,
427
+ storageKey: initialStorageKey,
428
+ url: toRootRelativeMediaUrl(undefined, initialStorageKey, options.origin),
429
+ status: 'pending',
430
+ });
431
+ try {
432
+ // [Phase B] Stream binary via PUT (either same-origin streaming or signed external URL)
433
+ const putUrlObj = new URL(target.uploadUrl, options.origin);
434
+ const originUrlObj = new URL(options.origin);
435
+ const isSameOrigin = putUrlObj.origin === originUrlObj.origin;
436
+ const putHeaders = {
437
+ ...target.headers,
438
+ };
439
+ if (!putHeaders['Content-Type'] && !putHeaders['content-type']) {
440
+ putHeaders['Content-Type'] = mimeType;
441
+ }
442
+ if (isSameOrigin) {
443
+ putHeaders['Authorization'] = `Bearer ${options.token}`;
444
+ }
445
+ const putFn = isSameOrigin ? fetchFn : rawFetchFn;
446
+ const putRes = await putFn(putUrlObj.toString(), {
447
+ method: target.method || 'PUT',
448
+ headers: putHeaders,
449
+ body: new Uint8Array(buffer),
450
+ });
451
+ if (!putRes.ok) {
452
+ throw new ApiError({
453
+ message: `Failed to upload binary for "${options.filename}": ${putRes.statusText}`,
454
+ code: 'MEDIA_BINARY_UPLOAD_FAILED',
455
+ status: putRes.status,
456
+ endpoint: putUrlObj.toString(),
457
+ suggestion: 'Check network stability and retry pushing.',
458
+ });
459
+ }
460
+ // [Phase C] Confirm upload (`POST /_emdash/api/media/{mediaId}/confirm`)
461
+ const confirmEndpoint = new URL(`/_emdash/api/media/${pendingMediaId}/confirm`, options.origin).toString();
462
+ const confirmRes = await fetchFn(confirmEndpoint, {
463
+ method: 'POST',
464
+ headers: {
465
+ Authorization: `Bearer ${options.token}`,
466
+ 'Content-Type': 'application/json',
467
+ Accept: 'application/json',
468
+ },
469
+ body: JSON.stringify({
470
+ size: buffer.length,
471
+ }),
472
+ });
473
+ if (!confirmRes.ok) {
474
+ throw new ApiError({
475
+ message: `Failed to confirm media "${options.filename}" (${pendingMediaId}): ${confirmRes.statusText}`,
476
+ code: 'MEDIA_CONFIRM_FAILED',
477
+ status: confirmRes.status,
478
+ endpoint: confirmEndpoint,
479
+ suggestion: 'The uploaded file could not be confirmed by EmDash.',
480
+ });
481
+ }
482
+ const confirmJson = await confirmRes.json();
483
+ const confirmData = unwrapEnvelope(confirmJson);
484
+ const item = ((confirmData.item && typeof confirmData.item === 'object'
485
+ ? confirmData.item
486
+ : confirmData) ?? {});
487
+ if (item.status && item.status !== 'ready') {
488
+ throw new ApiError({
489
+ message: `Media "${options.filename}" (${pendingMediaId}) confirmed with unexpected status "${String(item.status)}".`,
490
+ code: 'MEDIA_NOT_READY',
491
+ endpoint: confirmEndpoint,
492
+ suggestion: 'Retry pushing the article.',
493
+ });
494
+ }
495
+ // IMPORTANT: Use the confirmed storageKey and url (EmDash rewrites storageKey to a double-ULID on confirm)
496
+ const confirmedMediaId = typeof item.id === 'string' && item.id ? item.id : pendingMediaId;
497
+ const confirmedStorageKey = typeof item.storageKey === 'string' && item.storageKey ? item.storageKey : initialStorageKey;
498
+ const confirmedUrl = toRootRelativeMediaUrl(typeof item.url === 'string' ? item.url : undefined, confirmedStorageKey, options.origin);
499
+ options.cache?.saveMediaState({
500
+ localHash,
501
+ mediaId: confirmedMediaId,
502
+ storageKey: confirmedStorageKey,
503
+ url: confirmedUrl,
504
+ status: 'uploaded',
505
+ });
506
+ // Best-effort sync of alt / caption to the media record itself (B-2)
507
+ await syncRemoteMediaMetadata(confirmedMediaId, { alt: options.alt, caption: options.caption }, { origin: options.origin, token: options.token, fetchFn });
508
+ return {
509
+ mediaId: confirmedMediaId,
510
+ storageKey: confirmedStorageKey,
511
+ url: confirmedUrl,
512
+ localHash,
513
+ contentHash: typeof item.contentHash === 'string' && item.contentHash
514
+ ? item.contentHash
515
+ : serverContentHash,
516
+ size: typeof item.size === 'number' ? item.size : buffer.length,
517
+ mimeType: typeof item.mimeType === 'string' ? item.mimeType : mimeType,
518
+ filename: typeof item.filename === 'string' ? item.filename : options.filename,
519
+ width: typeof item.width === 'number' ? item.width : null,
520
+ height: typeof item.height === 'number' ? item.height : null,
521
+ createdAt: typeof item.createdAt === 'string' ? item.createdAt : undefined,
522
+ deduplicated: false,
523
+ };
524
+ }
525
+ catch (err) {
526
+ // [Compensation] Clean up orphan pending/failed record on server
527
+ const comp = await compensateOrphanMedia(pendingMediaId, {
528
+ origin: options.origin,
529
+ token: options.token,
530
+ fetchFn,
531
+ });
532
+ if (comp.alreadyReadyItem) {
533
+ const readyItem = comp.alreadyReadyItem;
534
+ const confirmedMediaId = typeof readyItem.id === 'string' && readyItem.id ? readyItem.id : pendingMediaId;
535
+ const confirmedStorageKey = typeof readyItem.storageKey === 'string' && readyItem.storageKey
536
+ ? readyItem.storageKey
537
+ : initialStorageKey;
538
+ const confirmedUrl = toRootRelativeMediaUrl(typeof readyItem.url === 'string' ? readyItem.url : undefined, confirmedStorageKey, options.origin);
539
+ options.cache?.saveMediaState({
540
+ localHash,
541
+ mediaId: confirmedMediaId,
542
+ storageKey: confirmedStorageKey,
543
+ url: confirmedUrl,
544
+ status: 'uploaded',
545
+ });
546
+ return {
547
+ mediaId: confirmedMediaId,
548
+ storageKey: confirmedStorageKey,
549
+ url: confirmedUrl,
550
+ localHash,
551
+ contentHash: serverContentHash,
552
+ size: buffer.length,
553
+ mimeType,
554
+ filename: options.filename,
555
+ deduplicated: false,
556
+ };
557
+ }
558
+ if (comp.compensated) {
559
+ options.cache?.deleteMediaState(localHash);
560
+ }
561
+ else {
562
+ options.cache?.saveMediaState({
563
+ localHash,
564
+ mediaId: pendingMediaId,
565
+ storageKey: initialStorageKey,
566
+ url: toRootRelativeMediaUrl(undefined, initialStorageKey, options.origin),
567
+ status: 'failed',
568
+ });
569
+ if (err instanceof ApiError) {
570
+ throw new ApiError({
571
+ message: `${err.message} (Orphan pending media record ${pendingMediaId} could not be automatically deleted).`,
572
+ code: err.code,
573
+ status: err.status,
574
+ endpoint: err.endpoint,
575
+ suggestion: `Manually delete pending media ID "${pendingMediaId}" via EmDash Admin (/admin/media) or DELETE /_emdash/api/media/${pendingMediaId}.`,
576
+ });
577
+ }
578
+ }
579
+ throw err;
580
+ }
581
+ }
582
+ //# sourceMappingURL=media.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media.js","sourceRoot":"","sources":["../../src/client/media.ts"],"names":[],"mappings":"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"}
@@ -72,8 +72,29 @@ export declare function convertDataForRead(data: Record<string, unknown>, fields
72
72
  * in `portableText` fields inside `blocks` arrays.
73
73
  */
74
74
  export declare function convertDataForWrite(data: Record<string, unknown>, fields: FieldSchema[]): Record<string, unknown>;
75
+ export interface CodeFenceState {
76
+ /** The line is a code fence delimiter (opening or closing) line. */
77
+ isFenceDelimiter: boolean;
78
+ /** The line is body content located inside an open code fence. */
79
+ insideCodeFence: boolean;
80
+ }
75
81
  /**
76
- * Strips code fences (```...```) from markdown to prevent false positives in regex checks.
82
+ * Classifies every line of a Markdown body with respect to backtick code fences.
83
+ *
84
+ * Fences of any length are recognized and matched against a closing fence of equal or
85
+ * greater length, so a body that documents Markdown syntax itself (a ```` ```md ````
86
+ * block quoting `![alt](path)` examples) is never inspected as real markup. Lines that
87
+ * merely *contain* three backticks are not delimiters, and an unterminated fence keeps
88
+ * the rest of the body fenced.
89
+ */
90
+ export declare function classifyCodeFenceLines(body: string): CodeFenceState[];
91
+ /**
92
+ * Removes fenced code block content from a Markdown body so that regex-based guards
93
+ * (image paths, unsupported syntax, draft images) never inspect quoted Markdown or HTML.
94
+ * Fence delimiters and their content collapse into a single sentinel line.
95
+ *
96
+ * Note: this function is for *inspection* only. It is never used to produce stored or
97
+ * pushed Markdown, which keeps fenced content intact.
77
98
  */
78
99
  export declare function stripCodeBlocks(markdown: string): string;
79
100
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"portable-text.d.ts","sourceRoot":"","sources":["../../src/client/portable-text.ts"],"names":[],"mappings":"AAsBA;;;;;;;;;GASG;AAMH,wCAAwC;AACxC,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;IACrB,QAAQ,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC9B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,UAAU,gBAAgB;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,UAAU,OAAO;IACf,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAWD;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,iBAAiB,EAAE,GAAG,MAAM,CAwC1E;AAiFD;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,iBAAiB,EAAE,CAiG5E;AAwFD,6CAA6C;AAC7C,wBAAgB,eAAe,IAAI,IAAI,CAEtC;AAMD,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,UAAU,CAAC,EAAE,KAAK,CAAC;QACjB,IAAI,EAAE,MAAM,CAAC;QACb,cAAc,EAAE,MAAM,CAAC;QACvB,QAAQ,EAAE,KAAK,CAAC;YACd,OAAO,EAAE,MAAM,CAAC;YAChB,MAAM,EAAE,KAAK,CAAC;gBAAE,IAAI,EAAE,MAAM,CAAC;gBAAC,IAAI,EAAE,MAAM,CAAA;aAAE,CAAC,CAAC;SAC/C,CAAC,CAAC;KACJ,CAAC,CAAC;CACJ;AA0CD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,MAAM,EAAE,WAAW,EAAE,EACrB,GAAG,GAAE,OAAe,GACnB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAYzB;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,MAAM,EAAE,WAAW,EAAE,GACpB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAUzB;AAMD;;GAEG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAExD;AAED;;;GAGG;AACH,wBAAgB,+BAA+B,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAmB1E;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAGlE;AAED;;;GAGG;AACH,wBAAgB,4BAA4B,CAAC,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAQjG"}
1
+ {"version":3,"file":"portable-text.d.ts","sourceRoot":"","sources":["../../src/client/portable-text.ts"],"names":[],"mappings":"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"}