emdash-emit 0.1.13 → 0.1.15

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 (146) hide show
  1. package/CHANGELOG.md +108 -1
  2. package/README.md +235 -187
  3. package/dist/cli/commands/delete.d.ts.map +1 -1
  4. package/dist/cli/commands/delete.js +2 -0
  5. package/dist/cli/commands/delete.js.map +1 -1
  6. package/dist/cli/commands/doctor.d.ts.map +1 -1
  7. package/dist/cli/commands/doctor.js +9 -0
  8. package/dist/cli/commands/doctor.js.map +1 -1
  9. package/dist/cli/commands/pull.d.ts +32 -0
  10. package/dist/cli/commands/pull.d.ts.map +1 -1
  11. package/dist/cli/commands/pull.js +108 -9
  12. package/dist/cli/commands/pull.js.map +1 -1
  13. package/dist/cli/commands/push.d.ts.map +1 -1
  14. package/dist/cli/commands/push.js +18 -0
  15. package/dist/cli/commands/push.js.map +1 -1
  16. package/dist/cli/commands/status.d.ts.map +1 -1
  17. package/dist/cli/commands/status.js +15 -5
  18. package/dist/cli/commands/status.js.map +1 -1
  19. package/dist/client/media.d.ts.map +1 -1
  20. package/dist/client/media.js +16 -5
  21. package/dist/client/media.js.map +1 -1
  22. package/dist/db/cache.d.ts +24 -5
  23. package/dist/db/cache.d.ts.map +1 -1
  24. package/dist/db/cache.js +308 -61
  25. package/dist/db/cache.js.map +1 -1
  26. package/dist/mcp/server.d.ts.map +1 -1
  27. package/dist/mcp/server.js +5 -3
  28. package/dist/mcp/server.js.map +1 -1
  29. package/dist/mcp/tools/sync-delete.d.ts.map +1 -1
  30. package/dist/mcp/tools/sync-delete.js +5 -0
  31. package/dist/mcp/tools/sync-delete.js.map +1 -1
  32. package/dist/mcp/tools/sync-doctor.d.ts.map +1 -1
  33. package/dist/mcp/tools/sync-doctor.js +12 -0
  34. package/dist/mcp/tools/sync-doctor.js.map +1 -1
  35. package/dist/mcp/tools/sync-pull.d.ts.map +1 -1
  36. package/dist/mcp/tools/sync-pull.js +49 -12
  37. package/dist/mcp/tools/sync-pull.js.map +1 -1
  38. package/dist/mcp/tools/sync-push.d.ts.map +1 -1
  39. package/dist/mcp/tools/sync-push.js +5 -0
  40. package/dist/mcp/tools/sync-push.js.map +1 -1
  41. package/dist/mcp/tools/sync-status.d.ts.map +1 -1
  42. package/dist/mcp/tools/sync-status.js +6 -2
  43. package/dist/mcp/tools/sync-status.js.map +1 -1
  44. package/dist/sync/conflicts.d.ts.map +1 -1
  45. package/dist/sync/conflicts.js +44 -0
  46. package/dist/sync/conflicts.js.map +1 -1
  47. package/dist/sync/delete.d.ts +3 -1
  48. package/dist/sync/delete.d.ts.map +1 -1
  49. package/dist/sync/delete.js +7 -20
  50. package/dist/sync/delete.js.map +1 -1
  51. package/dist/sync/diff.d.ts.map +1 -1
  52. package/dist/sync/diff.js +18 -2
  53. package/dist/sync/diff.js.map +1 -1
  54. package/dist/sync/doctor.d.ts +28 -1
  55. package/dist/sync/doctor.d.ts.map +1 -1
  56. package/dist/sync/doctor.js +393 -45
  57. package/dist/sync/doctor.js.map +1 -1
  58. package/dist/sync/guards.d.ts +45 -0
  59. package/dist/sync/guards.d.ts.map +1 -0
  60. package/dist/sync/guards.js +235 -0
  61. package/dist/sync/guards.js.map +1 -0
  62. package/dist/sync/media/body_scan.d.ts +15 -0
  63. package/dist/sync/media/body_scan.d.ts.map +1 -0
  64. package/dist/sync/media/body_scan.js +132 -0
  65. package/dist/sync/media/body_scan.js.map +1 -0
  66. package/dist/sync/media/companions.d.ts +20 -0
  67. package/dist/sync/media/companions.d.ts.map +1 -0
  68. package/dist/sync/media/companions.js +111 -0
  69. package/dist/sync/media/companions.js.map +1 -0
  70. package/dist/sync/media/index.d.ts +9 -0
  71. package/dist/sync/media/index.d.ts.map +1 -0
  72. package/dist/sync/media/index.js +9 -0
  73. package/dist/sync/media/index.js.map +1 -0
  74. package/dist/sync/media/mirror_item.d.ts +27 -0
  75. package/dist/sync/media/mirror_item.d.ts.map +1 -0
  76. package/dist/sync/media/mirror_item.js +207 -0
  77. package/dist/sync/media/mirror_item.js.map +1 -0
  78. package/dist/sync/media/naming_mirror.d.ts +36 -0
  79. package/dist/sync/media/naming_mirror.d.ts.map +1 -0
  80. package/dist/sync/media/naming_mirror.js +143 -0
  81. package/dist/sync/media/naming_mirror.js.map +1 -0
  82. package/dist/sync/media/pull_article.d.ts +37 -0
  83. package/dist/sync/media/pull_article.d.ts.map +1 -0
  84. package/dist/sync/media/pull_article.js +273 -0
  85. package/dist/sync/media/pull_article.js.map +1 -0
  86. package/dist/sync/media/pull_library.d.ts +12 -0
  87. package/dist/sync/media/pull_library.d.ts.map +1 -0
  88. package/dist/sync/media/pull_library.js +207 -0
  89. package/dist/sync/media/pull_library.js.map +1 -0
  90. package/dist/sync/media/remote_index.d.ts +18 -0
  91. package/dist/sync/media/remote_index.d.ts.map +1 -0
  92. package/dist/sync/media/remote_index.js +94 -0
  93. package/dist/sync/media/remote_index.js.map +1 -0
  94. package/dist/sync/media/types.d.ts +191 -0
  95. package/dist/sync/media/types.d.ts.map +1 -0
  96. package/dist/sync/media/types.js +11 -0
  97. package/dist/sync/media/types.js.map +1 -0
  98. package/dist/sync/media.d.ts +1 -74
  99. package/dist/sync/media.d.ts.map +1 -1
  100. package/dist/sync/media.js +1 -592
  101. package/dist/sync/media.js.map +1 -1
  102. package/dist/sync/merge.d.ts +11 -0
  103. package/dist/sync/merge.d.ts.map +1 -1
  104. package/dist/sync/merge.js +53 -6
  105. package/dist/sync/merge.js.map +1 -1
  106. package/dist/sync/orchestrator.d.ts +23 -0
  107. package/dist/sync/orchestrator.d.ts.map +1 -1
  108. package/dist/sync/orchestrator.js +85 -1
  109. package/dist/sync/orchestrator.js.map +1 -1
  110. package/dist/sync/preflight.d.ts +1 -1
  111. package/dist/sync/preflight.d.ts.map +1 -1
  112. package/dist/sync/preflight.js +127 -15
  113. package/dist/sync/preflight.js.map +1 -1
  114. package/dist/sync/pull.d.ts +21 -0
  115. package/dist/sync/pull.d.ts.map +1 -1
  116. package/dist/sync/pull.js +278 -17
  117. package/dist/sync/pull.js.map +1 -1
  118. package/dist/sync/push.d.ts +7 -0
  119. package/dist/sync/push.d.ts.map +1 -1
  120. package/dist/sync/push.js +135 -58
  121. package/dist/sync/push.js.map +1 -1
  122. package/dist/sync/status.d.ts +4 -0
  123. package/dist/sync/status.d.ts.map +1 -1
  124. package/dist/sync/status.js +68 -4
  125. package/dist/sync/status.js.map +1 -1
  126. package/dist/utils/config.d.ts +25 -0
  127. package/dist/utils/config.d.ts.map +1 -1
  128. package/dist/utils/config.js +28 -2
  129. package/dist/utils/config.js.map +1 -1
  130. package/dist/utils/frontmatter.d.ts +1 -1
  131. package/dist/utils/frontmatter.d.ts.map +1 -1
  132. package/dist/utils/frontmatter.js +15 -4
  133. package/dist/utils/frontmatter.js.map +1 -1
  134. package/dist/utils/media-body-refs.d.ts +81 -0
  135. package/dist/utils/media-body-refs.d.ts.map +1 -0
  136. package/dist/utils/media-body-refs.js +240 -0
  137. package/dist/utils/media-body-refs.js.map +1 -0
  138. package/dist/utils/media-refs.d.ts +44 -0
  139. package/dist/utils/media-refs.d.ts.map +1 -0
  140. package/dist/utils/media-refs.js +101 -0
  141. package/dist/utils/media-refs.js.map +1 -0
  142. package/dist/utils/naming.d.ts +31 -0
  143. package/dist/utils/naming.d.ts.map +1 -1
  144. package/dist/utils/naming.js +58 -0
  145. package/dist/utils/naming.js.map +1 -1
  146. package/package.json +1 -1
@@ -0,0 +1,240 @@
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
+ /**
23
+ * Local Markdown body image references vs the canonical remote form.
24
+ *
25
+ * A body image has two spellings of the same asset:
26
+ *
27
+ * canonical `![alt](/_emdash/api/media/file/<storageKey>)` — what EmDash stores in Portable Text and
28
+ * what the live site serves
29
+ * local `![alt](../../media/<storageKey>)` — a reference spelled from the article's own
30
+ * directory, resolving to the mirrored file under
31
+ * `media/` at the workspace root
32
+ *
33
+ * The local spelling exists so an editor preview (VS Code, Obsidian) renders the image from the mirror. It is
34
+ * only ever a disk form: `base_snapshots.bodyMarkdown` and every payload sent to EmDash keep the canonical
35
+ * URL, because the baseline must stay byte-identical to what a remote pull writes.
36
+ *
37
+ * The depth is derived from the article's own path (`mediaRefPrefixForArticle`), so an article under
38
+ * `<collection>/draft|published/` carries `../../` while one at the workspace root carries `../`. That makes
39
+ * the link resolvable from the file that contains it, which a depth-free reference is not. The older depth-free
40
+ * form (`media/<storageKey>`) is still accepted wherever a local reference is read — workspaces built with it
41
+ * are re-spelled on the next pull rather than reported as edits.
42
+ */
43
+ import { storageKeyToMarkdownRef } from './naming.js';
44
+ /** Canonical root-relative media route prefix EmDash writes into Portable Text image blocks. */
45
+ export const MEDIA_FILE_ROUTE_PREFIX = '/_emdash/api/media/file/';
46
+ /** Workspace-relative directory a mirrored asset lives in. */
47
+ export const MEDIA_DIR_PREFIX = 'media/';
48
+ const CANONICAL_MEDIA_URL_REGEX = /(?:^|\/)media\/file\/([^?#\s)]+)/;
49
+ /** Image extensions a storageKey may carry, matching the mirror naming rule. */
50
+ const STORAGE_KEY_EXTENSION = /\.(jpg|jpeg|png|gif|webp|avif|svg)$/i;
51
+ /**
52
+ * A storageKey names its mirror outright: one path segment that is itself an image file. A reference may also
53
+ * carry the deterministic `<stem>--<mediaId>` collision suffix, which still names a real asset. An article may
54
+ * spell that mirror relative to its own directory (`../../media/<key>`), so leading `../` / `./` segments are
55
+ * part of the accepted shape — but nothing deeper than `media/` is: a staging draft (`media/drafts/x.png`) or
56
+ * an unrelated folder (`../../uploads/x.jpg`) is not a canonicalized local reference.
57
+ */
58
+ const LOCAL_MEDIA_REF_REGEX = /^(?:\.\.\/|\.\/)*media\/([^/?#\s]+)$/;
59
+ /** `storageKeyFromMirrorRef` needs no candidate list when the reference spells its storageKey outright. */
60
+ const EMPTY_STORAGE_KEY_CANDIDATES = new Set();
61
+ /** Extracts the storageKey from a canonical `/_emdash/api/media/file/<storageKey>` URL. */
62
+ export function storageKeyFromCanonicalUrl(url) {
63
+ const match = url.match(CANONICAL_MEDIA_URL_REGEX);
64
+ if (!match)
65
+ return null;
66
+ const key = match[1].trim();
67
+ return key.length > 0 ? key : null;
68
+ }
69
+ /** Reports whether a Markdown image URL already carries the canonical EmDash media file route. */
70
+ export function isCanonicalMediaUrl(url) {
71
+ return storageKeyFromCanonicalUrl(url) !== null;
72
+ }
73
+ /** The final path segment of a reference, or null when it carries no usable name. */
74
+ function refBasename(url) {
75
+ const match = url.trim().match(LOCAL_MEDIA_REF_REGEX);
76
+ if (!match)
77
+ return null;
78
+ return match[1];
79
+ }
80
+ /**
81
+ * Reports whether a Markdown image URL is a reference into `media/` — either workspace-relative (`media/<key>`)
82
+ * or spelled from the article's own directory (`../../media/<key>`). Deliberately strict so an arbitrary
83
+ * hand-written path (`./hero.png`, `../uploads/x.jpg`) or a staging draft (`media/drafts/x.png`) is never
84
+ * mistaken for a canonicalized local reference.
85
+ */
86
+ export function isLocalMediaRef(url) {
87
+ const name = refBasename(url);
88
+ return name !== null && STORAGE_KEY_EXTENSION.test(name);
89
+ }
90
+ /**
91
+ * Rewrites canonical body image URLs into local mirror references.
92
+ *
93
+ * Only a standalone Markdown image line whose URL is the canonical media route AND whose storageKey names an
94
+ * existing file under `media/` is rewritten. Anything else — an external `https://` src, a data URL, a
95
+ * reference whose asset is not mirrored, an inline image inside a paragraph, an opaque EmDash block fence —
96
+ * is left exactly as it arrived, so the transform never invents a broken local link and never rewrites a line
97
+ * Emit's push guards would refuse to restore.
98
+ *
99
+ * A line that already carries a local mirror reference is normalized rather than skipped: its storageKey is
100
+ * recovered through `storageKeyFromMirrorRef` and rewritten into this function's own spelling, so re-localizing
101
+ * a document is idempotent and a file written at another depth (a legacy workspace ref, or an article moved
102
+ * between `draft/` and `published/`) converges on the requested form instead of being reported as an edit.
103
+ *
104
+ * When `articleRelPath` is supplied the reference is spelled relative to that article's own directory
105
+ * (`../../media/<storageKey>` for `posts/draft/…`), which is what lets a Markdown preview resolve the mirror
106
+ * offline. Without it the reference stays workspace-relative (`media/<storageKey>`), the spelling older
107
+ * workspaces carry on disk.
108
+ */
109
+ export function canonicalToLocalMediaRefs(body, options) {
110
+ const prefix = options.articleRelPath
111
+ ? `${mediaRefPrefixForArticle(options.articleRelPath)}${MEDIA_DIR_PREFIX}`
112
+ : MEDIA_DIR_PREFIX;
113
+ const lines = body.split('\n');
114
+ const standaloneRegex = /^(!\[([^\]]*)\]\()([^)]+)(\))$/;
115
+ return lines
116
+ .map((line) => {
117
+ const hasCr = line.endsWith('\r');
118
+ const trimmed = (hasCr ? line.slice(0, -1) : line).trim();
119
+ if (!trimmed)
120
+ return line;
121
+ // Opaque EmDash block fences carry remote JSON inside and are managed by Guard G9.
122
+ if (/^<!--ec:block\s/.test(trimmed))
123
+ return line;
124
+ const match = trimmed.match(standaloneRegex);
125
+ if (!match)
126
+ return line;
127
+ // A canonical route names its storageKey outright; an already-local reference names it through the mirror
128
+ // spelling, which `storageKeyFromMirrorRef` recovers without a candidate list because the key is written
129
+ // verbatim in the line. Either way the transform rewrites into `prefix`, never leaving a stale depth.
130
+ const storageKey = storageKeyFromCanonicalUrl(match[3]) ??
131
+ storageKeyFromMirrorRef(match[3], EMPTY_STORAGE_KEY_CANDIDATES);
132
+ if (!storageKey)
133
+ return line;
134
+ if (!options.fs.existsSync(`${options.mediaDir}/${storageKey}`))
135
+ return line;
136
+ const rewritten = `${match[1]}${prefix}${storageKey}${match[4]}`;
137
+ return hasCr ? `${rewritten}\r` : rewritten;
138
+ })
139
+ .join('\n');
140
+ }
141
+ /**
142
+ * Resolves a local mirror reference back to its storageKey.
143
+ *
144
+ * A reference that spells a storageKey outright (`media/<storageKey>` or the article-relative
145
+ * `../../media/<storageKey>`) is returned verbatim: the key names its own mirror, so no lookup is needed. Any
146
+ * other name — a `<stem>--<mediaId>` collision suffix or a truncated stem — is recovered by asking
147
+ * `candidateStorageKeys`, the storageKeys Emit has confirmed through `media_state` and companion YAML, which
148
+ * one sanitizes to that stem. A reference no confirmed mirror owns yields null, so a hand-written path still
149
+ * reaches the push guards instead of being silently rewritten.
150
+ */
151
+ export function storageKeyFromMirrorRef(rawPath, candidateStorageKeys) {
152
+ const name = refBasename(rawPath);
153
+ if (!name)
154
+ return null;
155
+ // The reference may already be the storageKey itself. Confirming it against recorded metadata keeps an
156
+ // unmirrored key unresolved, but a bare storageKey needs no stem translation.
157
+ const confirmed = [...candidateStorageKeys];
158
+ if (confirmed.length === 0 && STORAGE_KEY_EXTENSION.test(name))
159
+ return name;
160
+ for (const key of confirmed) {
161
+ if (key === name)
162
+ return key;
163
+ }
164
+ const stem = name.replace(/\.[A-Za-z0-9]{1,11}$/, '');
165
+ if (!stem)
166
+ return null;
167
+ // A collision-suffixed mirror is named `<stem>--<mediaId>` by `collisionSuffixedMarkdownRef`, so the suffix
168
+ // must come off before the stem is matched against confirmed storageKeys.
169
+ const bareStem = stem.replace(/--[0-9A-HJ-NP-Z]{26}$/i, '');
170
+ for (const key of confirmed) {
171
+ const ref = storageKeyToMarkdownRef(key);
172
+ if (ref && (ref === stem || ref === bareStem))
173
+ return key;
174
+ }
175
+ return null;
176
+ }
177
+ /**
178
+ * Builds the `../../` depth an article at `articleRelPath` needs to reach `media/` from its own directory,
179
+ * matching the prefix the draft-image guards already accept.
180
+ */
181
+ export function mediaRefPrefixForArticle(articleRelPath) {
182
+ const dir = articleRelPath.split('/').slice(0, -1).join('/');
183
+ const depth = dir ? dir.split('/').length : 0;
184
+ return '../'.repeat(Math.max(depth, 1));
185
+ }
186
+ /** True when every standalone image line already carries `prefix` plus a mirrored storageKey. */
187
+ function isLocalizedIn(body, prefix, fs) {
188
+ let sawLocalRef = false;
189
+ const standaloneRegex = /^!?\[([^\]]*)\]\(([^)]+)\)$/;
190
+ for (const rawLine of body.split('\n')) {
191
+ const trimmed = (rawLine.endsWith('\r') ? rawLine.slice(0, -1) : rawLine).trim();
192
+ if (!trimmed || /^<!--ec:block\s/.test(trimmed))
193
+ continue;
194
+ const match = trimmed.match(standaloneRegex);
195
+ if (!match)
196
+ continue;
197
+ const ref = match[2].trim();
198
+ if (isCanonicalMediaUrl(ref))
199
+ continue; // An unmirrored reference carries no local spelling either way.
200
+ const name = refBasename(ref);
201
+ if (name === null || !STORAGE_KEY_EXTENSION.test(name))
202
+ return false;
203
+ if (!ref.startsWith(prefix))
204
+ return false;
205
+ sawLocalRef = true;
206
+ }
207
+ return sawLocalRef;
208
+ }
209
+ /**
210
+ * Chooses which local spelling a Markdown document should carry on disk.
211
+ *
212
+ * The canonical `/_emdash/api/media/file/<storageKey>` URL is what every baseline, cache hash and remote payload
213
+ * records; only the file an editor reads may be spelled locally. This helper decides that spelling from what
214
+ * the workspace already holds: a fresh write prefers the article-relative form (`../../media/<storageKey>`) so
215
+ * a Markdown preview resolves the mirror offline, while a document already written in the depth-free legacy form
216
+ * (`preferLegacyRef`, read back from disk) is reproduced in that same form. Reproducing the disk's own spelling
217
+ * is what keeps `sync_state.local_hash` and an unchanged article's diff honest — re-spelling a file the user
218
+ * never edited would report a media rewrite nobody made.
219
+ *
220
+ * A storageKey with no mirrored binary under `media/` stays canonical in every mode, so the transform can never
221
+ * invent a broken local link.
222
+ */
223
+ export function localizeArticleMediaRefs(body, options) {
224
+ // The transform inspects `media/<storageKey>` existence itself, so the media directory must stay absolute.
225
+ const mediaDir = `${options.workspaceRoot.replace(/\/$/, '')}/media`;
226
+ const depthPrefix = `${mediaRefPrefixForArticle(options.articleRelPath)}${MEDIA_DIR_PREFIX}`;
227
+ // Already written in the requested spelling: leave it byte-identical so its hash stays stable.
228
+ const targetPrefix = options.preferLegacyRef ? MEDIA_DIR_PREFIX : depthPrefix;
229
+ if (isLocalizedIn(body, targetPrefix, options.fs))
230
+ return body;
231
+ // Otherwise localize from canonical form into the requested spelling. A disk file that already carries the
232
+ // other local spelling is normalized here too — a draft→published move changes the article's depth, and
233
+ // re-spelling in the new location keeps the written file honest about where its mirror lives.
234
+ return canonicalToLocalMediaRefs(body, {
235
+ mediaDir,
236
+ fs: options.fs,
237
+ articleRelPath: options.preferLegacyRef ? undefined : options.articleRelPath,
238
+ });
239
+ }
240
+ //# sourceMappingURL=media-body-refs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media-body-refs.js","sourceRoot":"","sources":["../../src/utils/media-body-refs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Canonical media reference parsing and fence policy definitions.
3
+ *
4
+ * This module consolidates:
5
+ * 1. Root-relative media route prefix definition.
6
+ * 2. Fence handling policy (backtick-only for authoring guards, backtick-or-tilde for diagnostics).
7
+ * 3. Unified scanning of markdown and HTML image references across article bodies.
8
+ */
9
+ import { MEDIA_FILE_ROUTE_PREFIX } from './media-body-refs.js';
10
+ export { MEDIA_FILE_ROUTE_PREFIX };
11
+ /**
12
+ * Valid storageKey format: dot-separated 26-char ULID segments with a supported image extension.
13
+ */
14
+ export declare const VALID_MEDIA_FILE_KEY: RegExp;
15
+ /**
16
+ * Fence policy declared explicitly:
17
+ * - guard: 'backtick-only' (G5/G11 preflight guards strictly exempt only backtick code fences).
18
+ * - diagnostic: 'backtick-or-tilde' (doctor diagnosis treats both ``` and ~~~ as code fences).
19
+ */
20
+ export declare const FENCE_POLICY: {
21
+ readonly guard: 'backtick-only';
22
+ readonly diagnostic: 'backtick-or-tilde';
23
+ };
24
+ export type FencePolicyType = (typeof FENCE_POLICY)[keyof typeof FENCE_POLICY];
25
+ /**
26
+ * Checks whether a trimmed line starts a code fence according to the specified policy.
27
+ * - 'backtick-only': recognizes runs of 3 or more backticks (```).
28
+ * - 'backtick-or-tilde': recognizes runs of 3 or more backticks (```) or tildes (~~~).
29
+ */
30
+ export declare function isCodeFenceDelimiter(line: string, policy: FencePolicyType): string | null;
31
+ export interface ParsedMediaRefMatch {
32
+ raw: string;
33
+ url: string;
34
+ isMarkdownImage: boolean;
35
+ isHtmlImage: boolean;
36
+ line: number;
37
+ }
38
+ /**
39
+ * Unified scanner for Markdown images (![alt](url)) and HTML <img> tags in content.
40
+ * Filters out references located inside code fences (according to fencePolicy)
41
+ * or inside opaque block fences (<!--ec:block ... -->).
42
+ */
43
+ export declare function scanRawMediaReferences(bodyMarkdown: string, fencePolicy?: FencePolicyType): string[];
44
+ //# sourceMappingURL=media-refs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media-refs.d.ts","sourceRoot":"","sources":["../../src/utils/media-refs.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAE/D,OAAO,EAAE,uBAAuB,EAAE,CAAC;AAEnC;;GAEG;AACH,eAAO,MAAM,oBAAoB,QACiD,CAAC;AAEnF;;;;GAIG;AACH,eAAO,MAAM,YAAY;aACvB,KAAK,EAAE,eAAe;aACtB,UAAU,EAAE,mBAAmB;CACvB,CAAC;AAEX,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,OAAO,YAAY,CAAC,CAAC;AAE/E;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,GAAG,MAAM,GAAG,IAAI,CAYzF;AAED,MAAM,WAAW,mBAAmB;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,eAAe,EAAE,OAAO,CAAC;IACzB,WAAW,EAAE,OAAO,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,YAAY,EAAE,MAAM,EACpB,WAAW,GAAE,eAAoC,GAChD,MAAM,EAAE,CAyDV"}
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Canonical media reference parsing and fence policy definitions.
3
+ *
4
+ * This module consolidates:
5
+ * 1. Root-relative media route prefix definition.
6
+ * 2. Fence handling policy (backtick-only for authoring guards, backtick-or-tilde for diagnostics).
7
+ * 3. Unified scanning of markdown and HTML image references across article bodies.
8
+ */
9
+ import { MEDIA_FILE_ROUTE_PREFIX } from './media-body-refs.js';
10
+ export { MEDIA_FILE_ROUTE_PREFIX };
11
+ /**
12
+ * Valid storageKey format: dot-separated 26-char ULID segments with a supported image extension.
13
+ */
14
+ export const VALID_MEDIA_FILE_KEY = /^[0-9A-HJ-NP-Z]{26}(\.[0-9A-HJ-NP-Z]{26})*\.(jpg|jpeg|png|gif|webp|avif|svg)$/i;
15
+ /**
16
+ * Fence policy declared explicitly:
17
+ * - guard: 'backtick-only' (G5/G11 preflight guards strictly exempt only backtick code fences).
18
+ * - diagnostic: 'backtick-or-tilde' (doctor diagnosis treats both ``` and ~~~ as code fences).
19
+ */
20
+ export const FENCE_POLICY = {
21
+ guard: 'backtick-only',
22
+ diagnostic: 'backtick-or-tilde',
23
+ };
24
+ /**
25
+ * Checks whether a trimmed line starts a code fence according to the specified policy.
26
+ * - 'backtick-only': recognizes runs of 3 or more backticks (```).
27
+ * - 'backtick-or-tilde': recognizes runs of 3 or more backticks (```) or tildes (~~~).
28
+ */
29
+ export function isCodeFenceDelimiter(line, policy) {
30
+ const trimmed = line.trim();
31
+ if (policy === FENCE_POLICY.diagnostic) {
32
+ if (trimmed.startsWith('```') || trimmed.startsWith('~~~')) {
33
+ return trimmed.slice(0, 3);
34
+ }
35
+ return null;
36
+ }
37
+ if (trimmed.startsWith('```')) {
38
+ return trimmed.slice(0, 3);
39
+ }
40
+ return null;
41
+ }
42
+ /**
43
+ * Unified scanner for Markdown images (![alt](url)) and HTML <img> tags in content.
44
+ * Filters out references located inside code fences (according to fencePolicy)
45
+ * or inside opaque block fences (<!--ec:block ... -->).
46
+ */
47
+ export function scanRawMediaReferences(bodyMarkdown, fencePolicy = FENCE_POLICY.guard) {
48
+ const references = new Set();
49
+ const lines = bodyMarkdown.split('\n');
50
+ let inFence = false;
51
+ let fenceDelimiter = '';
52
+ let inOpaqueFence = false;
53
+ for (const rawLine of lines) {
54
+ const trimmed = rawLine.replace(/\r$/, '').trim();
55
+ // Check opaque fences (<!--ec:block ... -->)
56
+ if (/^<!--ec:block\s/.test(trimmed)) {
57
+ inOpaqueFence = !/^.*-->$/.test(trimmed);
58
+ continue;
59
+ }
60
+ if (inOpaqueFence) {
61
+ if (/^.*-->$/.test(trimmed)) {
62
+ inOpaqueFence = false;
63
+ }
64
+ continue;
65
+ }
66
+ // Check code fences
67
+ const delim = isCodeFenceDelimiter(trimmed, fencePolicy);
68
+ if (delim) {
69
+ if (!inFence) {
70
+ inFence = true;
71
+ fenceDelimiter = delim;
72
+ continue;
73
+ }
74
+ else if (trimmed.startsWith(fenceDelimiter)) {
75
+ inFence = false;
76
+ continue;
77
+ }
78
+ }
79
+ if (inFence)
80
+ continue;
81
+ // 1. Markdown image syntax: ![alt](url)
82
+ const mdRegex = /!\[.*?\]\(([^)\s]+)(?:\s+["'].*?["'])?\)/g;
83
+ let match;
84
+ while ((match = mdRegex.exec(trimmed)) !== null) {
85
+ const url = match[1].trim();
86
+ if (!url.startsWith('http://') && !url.startsWith('https://') && !url.startsWith('data:')) {
87
+ references.add(url);
88
+ }
89
+ }
90
+ // 2. HTML <img> tags: <img src="...">
91
+ const htmlRegex = /<img[^>]+src=["']([^"']+)["']/gi;
92
+ while ((match = htmlRegex.exec(trimmed)) !== null) {
93
+ const url = match[1].trim();
94
+ if (!url.startsWith('http://') && !url.startsWith('https://') && !url.startsWith('data:')) {
95
+ references.add(url);
96
+ }
97
+ }
98
+ }
99
+ return Array.from(references).sort();
100
+ }
101
+ //# sourceMappingURL=media-refs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media-refs.js","sourceRoot":"","sources":["../../src/utils/media-refs.ts"],"names":[],"mappings":"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"}
@@ -27,6 +27,37 @@ export declare function extractDatePrefixFromPath(filePath: string): string | nu
27
27
  * Strips any leading yyyyMMdd_ date prefix from a filename or path.
28
28
  */
29
29
  export declare function stripDatePrefix(filenameOrPath: string): string;
30
+ /**
31
+ * Maximum UTF-8 byte length for a mirrored media filename stem. Media mirrors keep the storageKey's
32
+ * own basename, which can be a long migrated path, so the stem is capped well below the 255-byte POSIX
33
+ * NAME_MAX to leave room for the extension and any collision suffix.
34
+ */
35
+ export declare const DEFAULT_MAX_MEDIA_REF_BYTES = 180;
36
+ /**
37
+ * Derives the canonical local mirror name (`markdownRef`) from an EmDash media `storageKey`.
38
+ *
39
+ * A mirror is named after the storageKey — the identifier the article body actually references — so a
40
+ * Markdown image URL resolves to a file of the same name on disk, and the binary plus its companion YAML
41
+ * form one visibly matching pair. The record id is deliberately NOT part of the name: migrated storageKeys
42
+ * are arbitrary paths rather than ULIDs, so an id can never be recovered from a filename, and Emit reads
43
+ * it from the companion YAML (`id:`) and `media_state.media_id` instead.
44
+ *
45
+ * Sanitizing rules (cross-platform, no symlinks):
46
+ * - Only the storageKey's basename is used; migrated keys such as `uploads/2026/11/sample.png` collapse
47
+ * to their final path segment.
48
+ * - Filesystem-invalid characters and control characters are removed, whitespace collapses to `-`.
49
+ * - Multi-ULID keys (`<ulid1>.<ulid2>.jpg`) keep their dot separators so the key stays recognizable.
50
+ * - The storageKey's own trailing extension is stripped, because callers append the extension they write:
51
+ * a storageKey already ends in one, and re-appending it would produce `<key>.jpg.jpg`.
52
+ * - The result is truncated on a UTF-8 code point boundary.
53
+ * Returns `null` when the storageKey carries no usable name at all.
54
+ */
55
+ export declare function storageKeyToMarkdownRef(storageKey: string, maxBytes?: number): string | null;
56
+ /**
57
+ * Disambiguates two storageKeys that sanitize to the same basename by appending the media record id.
58
+ * Deterministic: the same key and id always produce the same name, so re-running a pull never churns.
59
+ */
60
+ export declare function collisionSuffixedMarkdownRef(markdownRef: string, mediaId: string): string;
30
61
  /**
31
62
  * Extracts the 26-character ULID from a filename or path, stripping any date prefix.
32
63
  * Returns null if no valid ULID is present.
@@ -1 +1 @@
1
- {"version":3,"file":"naming.d.ts","sourceRoot":"","sources":["../../src/utils/naming.ts"],"names":[],"mappings":"AAwBA;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,MAAM,CAAC;AAE3C;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAYvE;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,MAAM,EACb,QAAQ,GAAE,MAAgC,GACzC,MAAM,CA+BR;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,MAAM,CAmBzE;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAIzE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAG9D;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAW7E;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CACxC,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACd,EAAE,EAAE,MAAM,EACV,WAAW,EAAE,MAAM,EACnB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,GAC/B,MAAM,CAIR;AAED;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAI/E"}
1
+ {"version":3,"file":"naming.d.ts","sourceRoot":"","sources":["../../src/utils/naming.ts"],"names":[],"mappings":"AAwBA;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,MAAM,CAAC;AAE3C;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAYvE;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,MAAM,EACb,QAAQ,GAAE,MAAgC,GACzC,MAAM,CA+BR;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,MAAM,CAmBzE;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAIzE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAG9D;AAED;;;;GAIG;AACH,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAE/C;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,uBAAuB,CACrC,UAAU,EAAE,MAAM,EAClB,QAAQ,GAAE,MAAoC,GAC7C,MAAM,GAAG,IAAI,CAyBf;AAED;;;GAGG;AACH,wBAAgB,4BAA4B,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAGzF;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAW7E;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CACxC,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACd,EAAE,EAAE,MAAM,EACV,WAAW,EAAE,MAAM,EACnB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,GAC/B,MAAM,CAIR;AAED;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAI/E"}
@@ -110,6 +110,64 @@ export function stripDatePrefix(filenameOrPath) {
110
110
  const base = path.basename(filenameOrPath);
111
111
  return base.replace(/^\d{8}_/, '');
112
112
  }
113
+ /**
114
+ * Maximum UTF-8 byte length for a mirrored media filename stem. Media mirrors keep the storageKey's
115
+ * own basename, which can be a long migrated path, so the stem is capped well below the 255-byte POSIX
116
+ * NAME_MAX to leave room for the extension and any collision suffix.
117
+ */
118
+ export const DEFAULT_MAX_MEDIA_REF_BYTES = 180;
119
+ /**
120
+ * Derives the canonical local mirror name (`markdownRef`) from an EmDash media `storageKey`.
121
+ *
122
+ * A mirror is named after the storageKey — the identifier the article body actually references — so a
123
+ * Markdown image URL resolves to a file of the same name on disk, and the binary plus its companion YAML
124
+ * form one visibly matching pair. The record id is deliberately NOT part of the name: migrated storageKeys
125
+ * are arbitrary paths rather than ULIDs, so an id can never be recovered from a filename, and Emit reads
126
+ * it from the companion YAML (`id:`) and `media_state.media_id` instead.
127
+ *
128
+ * Sanitizing rules (cross-platform, no symlinks):
129
+ * - Only the storageKey's basename is used; migrated keys such as `uploads/2026/11/sample.png` collapse
130
+ * to their final path segment.
131
+ * - Filesystem-invalid characters and control characters are removed, whitespace collapses to `-`.
132
+ * - Multi-ULID keys (`<ulid1>.<ulid2>.jpg`) keep their dot separators so the key stays recognizable.
133
+ * - The storageKey's own trailing extension is stripped, because callers append the extension they write:
134
+ * a storageKey already ends in one, and re-appending it would produce `<key>.jpg.jpg`.
135
+ * - The result is truncated on a UTF-8 code point boundary.
136
+ * Returns `null` when the storageKey carries no usable name at all.
137
+ */
138
+ export function storageKeyToMarkdownRef(storageKey, maxBytes = DEFAULT_MAX_MEDIA_REF_BYTES) {
139
+ if (!storageKey || !storageKey.trim())
140
+ return null;
141
+ const basename = storageKey.replace(/\\/g, '/').split('/').pop() || '';
142
+ let clean = basename
143
+ .replace(/[<>:"/\\|?*\x00-\x1F]/g, '')
144
+ .replace(/\s+/g, '-')
145
+ .trim();
146
+ // A leading dot would create a hidden file (or `.` / `..`), which no mirror may be.
147
+ clean = clean.replace(/^\.+/, '');
148
+ if (!clean)
149
+ return null;
150
+ // Drop the storageKey's own extension: the stem is shared by the binary and its `.yaml` companion, so
151
+ // every caller appends the extension it actually writes and a key that already ends in one must not
152
+ // produce `<key>.jpg.jpg`.
153
+ clean = clean.replace(/\.[A-Za-z0-9]{1,11}$/, '');
154
+ if (!clean)
155
+ return null;
156
+ // Trailing whitespace collapses to hyphens before the extension is known, so trim what the removal leaves.
157
+ clean = clean.replace(/^[-.\s]+|[-.\s]+$/g, '');
158
+ if (!clean)
159
+ return null;
160
+ clean = truncateUtf8Bytes(clean, maxBytes).replace(/[.\s-]+$/, '');
161
+ return clean || null;
162
+ }
163
+ /**
164
+ * Disambiguates two storageKeys that sanitize to the same basename by appending the media record id.
165
+ * Deterministic: the same key and id always produce the same name, so re-running a pull never churns.
166
+ */
167
+ export function collisionSuffixedMarkdownRef(markdownRef, mediaId) {
168
+ const id = mediaId.trim().toUpperCase();
169
+ return `${markdownRef}--${id}`;
170
+ }
113
171
  /**
114
172
  * Extracts the 26-character ULID from a filename or path, stripping any date prefix.
115
173
  * Returns null if no valid ULID is present.
@@ -1 +1 @@
1
- {"version":3,"file":"naming.js","sourceRoot":"","sources":["../../src/utils/naming.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"naming.js","sourceRoot":"","sources":["../../src/utils/naming.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "emdash-emit",
3
- "version": "0.1.13",
3
+ "version": "0.1.15",
4
4
  "description": "AI-First Local Workspace & Sync Tool for EmDash (Cloudflare Workers + D1)",
5
5
  "author": "hiroaki <45134003+aroooy@users.noreply.github.com>",
6
6
  "license": "MIT",