emdash-emit 0.0.0-stage → 0.1.2

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 (171) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +798 -2
  3. package/dist/bin/emit.d.ts +5 -0
  4. package/dist/bin/emit.d.ts.map +1 -0
  5. package/dist/bin/emit.js +63 -0
  6. package/dist/bin/emit.js.map +1 -0
  7. package/dist/cli/commands/new.d.ts +4 -0
  8. package/dist/cli/commands/new.d.ts.map +1 -0
  9. package/dist/cli/commands/new.js +49 -0
  10. package/dist/cli/commands/new.js.map +1 -0
  11. package/dist/cli/commands/profile.d.ts +3 -0
  12. package/dist/cli/commands/profile.d.ts.map +1 -0
  13. package/dist/cli/commands/profile.js +104 -0
  14. package/dist/cli/commands/profile.js.map +1 -0
  15. package/dist/cli/commands/pull.d.ts +4 -0
  16. package/dist/cli/commands/pull.d.ts.map +1 -0
  17. package/dist/cli/commands/pull.js +157 -0
  18. package/dist/cli/commands/pull.js.map +1 -0
  19. package/dist/cli/commands/push.d.ts +4 -0
  20. package/dist/cli/commands/push.d.ts.map +1 -0
  21. package/dist/cli/commands/push.js +107 -0
  22. package/dist/cli/commands/push.js.map +1 -0
  23. package/dist/cli/commands/status.d.ts +4 -0
  24. package/dist/cli/commands/status.d.ts.map +1 -0
  25. package/dist/cli/commands/status.js +100 -0
  26. package/dist/cli/commands/status.js.map +1 -0
  27. package/dist/cli/context.d.ts +15 -0
  28. package/dist/cli/context.d.ts.map +1 -0
  29. package/dist/cli/context.js +75 -0
  30. package/dist/cli/context.js.map +1 -0
  31. package/dist/client/api.d.ts +19 -0
  32. package/dist/client/api.d.ts.map +1 -0
  33. package/dist/client/api.js +96 -0
  34. package/dist/client/api.js.map +1 -0
  35. package/dist/client/auth.d.ts +48 -0
  36. package/dist/client/auth.d.ts.map +1 -0
  37. package/dist/client/auth.js +165 -0
  38. package/dist/client/auth.js.map +1 -0
  39. package/dist/client/error.d.ts +22 -0
  40. package/dist/client/error.d.ts.map +1 -0
  41. package/dist/client/error.js +57 -0
  42. package/dist/client/error.js.map +1 -0
  43. package/dist/client/http.d.ts +31 -0
  44. package/dist/client/http.d.ts.map +1 -0
  45. package/dist/client/http.js +97 -0
  46. package/dist/client/http.js.map +1 -0
  47. package/dist/client/lock.d.ts +40 -0
  48. package/dist/client/lock.d.ts.map +1 -0
  49. package/dist/client/lock.js +104 -0
  50. package/dist/client/lock.js.map +1 -0
  51. package/dist/client/mapper.d.ts +37 -0
  52. package/dist/client/mapper.d.ts.map +1 -0
  53. package/dist/client/mapper.js +142 -0
  54. package/dist/client/mapper.js.map +1 -0
  55. package/dist/client/portable-text.d.ts +94 -0
  56. package/dist/client/portable-text.d.ts.map +1 -0
  57. package/dist/client/portable-text.js +428 -0
  58. package/dist/client/portable-text.js.map +1 -0
  59. package/dist/client/schemas.d.ts +1928 -0
  60. package/dist/client/schemas.d.ts.map +1 -0
  61. package/dist/client/schemas.js +123 -0
  62. package/dist/client/schemas.js.map +1 -0
  63. package/dist/db/cache.d.ts +60 -0
  64. package/dist/db/cache.d.ts.map +1 -0
  65. package/dist/db/cache.js +303 -0
  66. package/dist/db/cache.js.map +1 -0
  67. package/dist/index.d.ts +24 -0
  68. package/dist/index.d.ts.map +1 -0
  69. package/dist/index.js +45 -0
  70. package/dist/index.js.map +1 -0
  71. package/dist/mcp/context.d.ts +14 -0
  72. package/dist/mcp/context.d.ts.map +1 -0
  73. package/dist/mcp/context.js +23 -0
  74. package/dist/mcp/context.js.map +1 -0
  75. package/dist/mcp/server.d.ts +12 -0
  76. package/dist/mcp/server.d.ts.map +1 -0
  77. package/dist/mcp/server.js +106 -0
  78. package/dist/mcp/server.js.map +1 -0
  79. package/dist/mcp/tools/profile.d.ts +6 -0
  80. package/dist/mcp/tools/profile.d.ts.map +1 -0
  81. package/dist/mcp/tools/profile.js +81 -0
  82. package/dist/mcp/tools/profile.js.map +1 -0
  83. package/dist/mcp/tools/sync-diff.d.ts +4 -0
  84. package/dist/mcp/tools/sync-diff.d.ts.map +1 -0
  85. package/dist/mcp/tools/sync-diff.js +115 -0
  86. package/dist/mcp/tools/sync-diff.js.map +1 -0
  87. package/dist/mcp/tools/sync-new.d.ts +4 -0
  88. package/dist/mcp/tools/sync-new.d.ts.map +1 -0
  89. package/dist/mcp/tools/sync-new.js +54 -0
  90. package/dist/mcp/tools/sync-new.js.map +1 -0
  91. package/dist/mcp/tools/sync-pull.d.ts +4 -0
  92. package/dist/mcp/tools/sync-pull.d.ts.map +1 -0
  93. package/dist/mcp/tools/sync-pull.js +180 -0
  94. package/dist/mcp/tools/sync-pull.js.map +1 -0
  95. package/dist/mcp/tools/sync-push.d.ts +4 -0
  96. package/dist/mcp/tools/sync-push.d.ts.map +1 -0
  97. package/dist/mcp/tools/sync-push.js +105 -0
  98. package/dist/mcp/tools/sync-push.js.map +1 -0
  99. package/dist/mcp/tools/sync-status.d.ts +4 -0
  100. package/dist/mcp/tools/sync-status.d.ts.map +1 -0
  101. package/dist/mcp/tools/sync-status.js +45 -0
  102. package/dist/mcp/tools/sync-status.js.map +1 -0
  103. package/dist/sync/bylines.d.ts +23 -0
  104. package/dist/sync/bylines.d.ts.map +1 -0
  105. package/dist/sync/bylines.js +112 -0
  106. package/dist/sync/bylines.js.map +1 -0
  107. package/dist/sync/diff.d.ts +16 -0
  108. package/dist/sync/diff.d.ts.map +1 -0
  109. package/dist/sync/diff.js +68 -0
  110. package/dist/sync/diff.js.map +1 -0
  111. package/dist/sync/media.d.ts +37 -0
  112. package/dist/sync/media.d.ts.map +1 -0
  113. package/dist/sync/media.js +191 -0
  114. package/dist/sync/media.js.map +1 -0
  115. package/dist/sync/merge.d.ts +36 -0
  116. package/dist/sync/merge.d.ts.map +1 -0
  117. package/dist/sync/merge.js +112 -0
  118. package/dist/sync/merge.js.map +1 -0
  119. package/dist/sync/new.d.ts +24 -0
  120. package/dist/sync/new.d.ts.map +1 -0
  121. package/dist/sync/new.js +75 -0
  122. package/dist/sync/new.js.map +1 -0
  123. package/dist/sync/orchestrator.d.ts +55 -0
  124. package/dist/sync/orchestrator.d.ts.map +1 -0
  125. package/dist/sync/orchestrator.js +222 -0
  126. package/dist/sync/orchestrator.js.map +1 -0
  127. package/dist/sync/preflight.d.ts +25 -0
  128. package/dist/sync/preflight.d.ts.map +1 -0
  129. package/dist/sync/preflight.js +128 -0
  130. package/dist/sync/preflight.js.map +1 -0
  131. package/dist/sync/pull.d.ts +67 -0
  132. package/dist/sync/pull.d.ts.map +1 -0
  133. package/dist/sync/pull.js +405 -0
  134. package/dist/sync/pull.js.map +1 -0
  135. package/dist/sync/push.d.ts +33 -0
  136. package/dist/sync/push.d.ts.map +1 -0
  137. package/dist/sync/push.js +432 -0
  138. package/dist/sync/push.js.map +1 -0
  139. package/dist/sync/status.d.ts +24 -0
  140. package/dist/sync/status.d.ts.map +1 -0
  141. package/dist/sync/status.js +85 -0
  142. package/dist/sync/status.js.map +1 -0
  143. package/dist/sync/taxonomies.d.ts +49 -0
  144. package/dist/sync/taxonomies.d.ts.map +1 -0
  145. package/dist/sync/taxonomies.js +220 -0
  146. package/dist/sync/taxonomies.js.map +1 -0
  147. package/dist/utils/config.d.ts +18 -0
  148. package/dist/utils/config.d.ts.map +1 -0
  149. package/dist/utils/config.js +79 -0
  150. package/dist/utils/config.js.map +1 -0
  151. package/dist/utils/frontmatter.d.ts +115 -0
  152. package/dist/utils/frontmatter.d.ts.map +1 -0
  153. package/dist/utils/frontmatter.js +147 -0
  154. package/dist/utils/frontmatter.js.map +1 -0
  155. package/dist/utils/naming.d.ts +28 -0
  156. package/dist/utils/naming.d.ts.map +1 -0
  157. package/dist/utils/naming.js +94 -0
  158. package/dist/utils/naming.js.map +1 -0
  159. package/dist/utils/profile.d.ts +72 -0
  160. package/dist/utils/profile.d.ts.map +1 -0
  161. package/dist/utils/profile.js +180 -0
  162. package/dist/utils/profile.js.map +1 -0
  163. package/dist/utils/version.d.ts +5 -0
  164. package/dist/utils/version.d.ts.map +1 -0
  165. package/dist/utils/version.js +43 -0
  166. package/dist/utils/version.js.map +1 -0
  167. package/dist/utils/workspace.d.ts +22 -0
  168. package/dist/utils/workspace.d.ts.map +1 -0
  169. package/dist/utils/workspace.js +74 -0
  170. package/dist/utils/workspace.js.map +1 -0
  171. package/package.json +68 -4
@@ -0,0 +1,49 @@
1
+ export interface TaxonomyTerm {
2
+ id: string;
3
+ name?: string;
4
+ slug: string;
5
+ label?: string;
6
+ parentId?: string | null;
7
+ count?: number;
8
+ locale?: string;
9
+ }
10
+ export interface PullTaxonomiesOptions {
11
+ workspaceRoot: string;
12
+ origin: string;
13
+ token: string;
14
+ refreshToken?: string;
15
+ fetchFn?: typeof fetch;
16
+ }
17
+ export interface PullTaxonomiesResult {
18
+ categoriesCount: number;
19
+ tagsCount: number;
20
+ deletedCategoriesCount: number;
21
+ deletedTagsCount: number;
22
+ }
23
+ /**
24
+ * Pulls categories and tags from EmDash API and saves them to local YAML files:
25
+ * categories/<id>-<sanitizedLabel>.yaml
26
+ * tags/<id>-<sanitizedLabel>.yaml
27
+ */
28
+ export declare function pullTaxonomies(options: PullTaxonomiesOptions): Promise<PullTaxonomiesResult>;
29
+ /**
30
+ * Loads local taxonomy mapping between term ULID and slug from categories/ and tags/ directories.
31
+ */
32
+ export declare function loadLocalTaxonomyMaps(workspaceRoot: string): {
33
+ slugToId: {
34
+ category: Record<string, string>;
35
+ tag: Record<string, string>;
36
+ };
37
+ idToSlug: {
38
+ category: Record<string, string>;
39
+ tag: Record<string, string>;
40
+ };
41
+ };
42
+ /**
43
+ * Loads local taxonomy candidate names (slugs or labels) from categories/ and tags/ directories.
44
+ */
45
+ export declare function loadLocalTaxonomyCandidates(workspaceRoot: string): {
46
+ categories: string[];
47
+ tags: string[];
48
+ };
49
+ //# sourceMappingURL=taxonomies.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"taxonomies.d.ts","sourceRoot":"","sources":["../../src/sync/taxonomies.ts"],"names":[],"mappings":"AA2BA,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAMD,MAAM,WAAW,qBAAqB;IACpC,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;CACxB;AAED,MAAM,WAAW,oBAAoB;IACnC,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,oBAAoB,CAAC,CA8H/B;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,aAAa,EAAE,MAAM,GAAG;IAC5D,QAAQ,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,CAAC;IAC5E,QAAQ,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,CAAC;CAC7E,CA0CA;AAED;;GAEG;AACH,wBAAgB,2BAA2B,CAAC,aAAa,EAAE,MAAM,GAAG;IAClE,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB,CA8BA"}
@@ -0,0 +1,220 @@
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 yaml from 'js-yaml';
25
+ import { sanitizeTitleForFilename } from '../utils/frontmatter.js';
26
+ import { ApiError } from '../client/error.js';
27
+ import { createAuthedFetch } from '../client/http.js';
28
+ import { unwrapEnvelope } from '../client/api.js';
29
+ /**
30
+ * Pulls categories and tags from EmDash API and saves them to local YAML files:
31
+ * categories/<id>-<sanitizedLabel>.yaml
32
+ * tags/<id>-<sanitizedLabel>.yaml
33
+ */
34
+ export async function pullTaxonomies(options) {
35
+ const fetchFn = createAuthedFetch(options);
36
+ const fetchTerms = async (taxName) => {
37
+ const url = new URL(`/_emdash/api/taxonomies/${taxName}/terms`, options.origin).toString();
38
+ const res = await fetchFn(url, {
39
+ method: 'GET',
40
+ headers: {
41
+ Authorization: `Bearer ${options.token}`,
42
+ Accept: 'application/json',
43
+ },
44
+ });
45
+ if (!res.ok) {
46
+ throw new ApiError({
47
+ message: `Failed to fetch ${taxName} terms: ${res.statusText}`,
48
+ code: 'TAXONOMY_FETCH_FAILED',
49
+ status: res.status,
50
+ endpoint: url,
51
+ suggestion: 'Verify network connection and that the taxonomies API is enabled.',
52
+ });
53
+ }
54
+ const json = await res.json();
55
+ const data = unwrapEnvelope(json);
56
+ const rawTerms = (Array.isArray(data.terms) ? data.terms : Array.isArray(data) ? data : []);
57
+ return rawTerms;
58
+ };
59
+ const [categories, tags] = await Promise.all([
60
+ fetchTerms('category').catch(() => []),
61
+ fetchTerms('tag').catch(() => []),
62
+ ]);
63
+ const categoriesDir = path.join(options.workspaceRoot, 'categories');
64
+ const tagsDir = path.join(options.workspaceRoot, 'tags');
65
+ fs.mkdirSync(categoriesDir, { recursive: true });
66
+ fs.mkdirSync(tagsDir, { recursive: true });
67
+ // Save categories and prune deleted ones
68
+ const activeCategoryFileNames = new Set();
69
+ for (const cat of categories) {
70
+ const label = cat.label || cat.slug;
71
+ const sanitized = sanitizeTitleForFilename(label);
72
+ const fileName = `${cat.id}-${sanitized}.yaml`;
73
+ activeCategoryFileNames.add(fileName);
74
+ const filePath = path.join(categoriesDir, fileName);
75
+ const doc = {
76
+ id: cat.id,
77
+ name: label,
78
+ slug: cat.slug,
79
+ taxonomy: 'category',
80
+ parentId: cat.parentId || null,
81
+ count: cat.count ?? 0,
82
+ };
83
+ fs.writeFileSync(filePath, yaml.dump(doc, { indent: 2, quotingType: '"', forceQuotes: false }), 'utf8');
84
+ }
85
+ // Prune local category YAMLs not present on remote
86
+ let deletedCategoriesCount = 0;
87
+ if (fs.existsSync(categoriesDir)) {
88
+ for (const file of fs.readdirSync(categoriesDir)) {
89
+ if (file.endsWith('.yaml') && !activeCategoryFileNames.has(file)) {
90
+ try {
91
+ fs.unlinkSync(path.join(categoriesDir, file));
92
+ deletedCategoriesCount++;
93
+ }
94
+ catch {
95
+ // Ignore unlink errors
96
+ }
97
+ }
98
+ }
99
+ }
100
+ // Save tags and prune deleted ones
101
+ const activeTagFileNames = new Set();
102
+ for (const tag of tags) {
103
+ const label = tag.label || tag.slug;
104
+ const sanitized = sanitizeTitleForFilename(label);
105
+ const fileName = `${tag.id}-${sanitized}.yaml`;
106
+ activeTagFileNames.add(fileName);
107
+ const filePath = path.join(tagsDir, fileName);
108
+ const doc = {
109
+ id: tag.id,
110
+ name: label,
111
+ slug: tag.slug,
112
+ taxonomy: 'tag',
113
+ parentId: tag.parentId || null,
114
+ count: tag.count ?? 0,
115
+ };
116
+ fs.writeFileSync(filePath, yaml.dump(doc, { indent: 2, quotingType: '"', forceQuotes: false }), 'utf8');
117
+ }
118
+ // Prune local tag YAMLs not present on remote
119
+ let deletedTagsCount = 0;
120
+ if (fs.existsSync(tagsDir)) {
121
+ for (const file of fs.readdirSync(tagsDir)) {
122
+ if (file.endsWith('.yaml') && !activeTagFileNames.has(file)) {
123
+ try {
124
+ fs.unlinkSync(path.join(tagsDir, file));
125
+ deletedTagsCount++;
126
+ }
127
+ catch {
128
+ // Ignore unlink errors
129
+ }
130
+ }
131
+ }
132
+ }
133
+ return {
134
+ categoriesCount: categories.length,
135
+ tagsCount: tags.length,
136
+ deletedCategoriesCount,
137
+ deletedTagsCount,
138
+ };
139
+ }
140
+ /**
141
+ * Loads local taxonomy mapping between term ULID and slug from categories/ and tags/ directories.
142
+ */
143
+ export function loadLocalTaxonomyMaps(workspaceRoot) {
144
+ const categoriesDir = path.join(workspaceRoot, 'categories');
145
+ const tagsDir = path.join(workspaceRoot, 'tags');
146
+ const readDirMap = (dir) => {
147
+ const slugToId = {};
148
+ const idToSlug = {};
149
+ if (!fs.existsSync(dir))
150
+ return { slugToId, idToSlug };
151
+ try {
152
+ const files = fs.readdirSync(dir).filter((f) => f.endsWith('.yaml'));
153
+ for (const file of files) {
154
+ try {
155
+ const content = fs.readFileSync(path.join(dir, file), 'utf8');
156
+ const data = yaml.load(content);
157
+ if (data && typeof data.id === 'string' && typeof data.slug === 'string') {
158
+ slugToId[data.slug] = data.id;
159
+ idToSlug[data.id] = data.slug;
160
+ }
161
+ }
162
+ catch {
163
+ // Ignore malformed files
164
+ }
165
+ }
166
+ }
167
+ catch {
168
+ // Ignore directory read errors
169
+ }
170
+ return { slugToId, idToSlug };
171
+ };
172
+ const catMap = readDirMap(categoriesDir);
173
+ const tagMap = readDirMap(tagsDir);
174
+ return {
175
+ slugToId: {
176
+ category: catMap.slugToId,
177
+ tag: tagMap.slugToId,
178
+ },
179
+ idToSlug: {
180
+ category: catMap.idToSlug,
181
+ tag: tagMap.idToSlug,
182
+ },
183
+ };
184
+ }
185
+ /**
186
+ * Loads local taxonomy candidate names (slugs or labels) from categories/ and tags/ directories.
187
+ */
188
+ export function loadLocalTaxonomyCandidates(workspaceRoot) {
189
+ const categoriesDir = path.join(workspaceRoot, 'categories');
190
+ const tagsDir = path.join(workspaceRoot, 'tags');
191
+ const readDirCandidates = (dir) => {
192
+ if (!fs.existsSync(dir))
193
+ return [];
194
+ try {
195
+ const files = fs.readdirSync(dir).filter((f) => f.endsWith('.yaml'));
196
+ const candidates = [];
197
+ for (const file of files) {
198
+ try {
199
+ const content = fs.readFileSync(path.join(dir, file), 'utf8');
200
+ const data = yaml.load(content);
201
+ if (data && typeof data.slug === 'string') {
202
+ candidates.push(data.slug);
203
+ }
204
+ }
205
+ catch {
206
+ // Ignore malformed files
207
+ }
208
+ }
209
+ return candidates;
210
+ }
211
+ catch {
212
+ return [];
213
+ }
214
+ };
215
+ return {
216
+ categories: readDirCandidates(categoriesDir),
217
+ tags: readDirCandidates(tagsDir),
218
+ };
219
+ }
220
+ //# sourceMappingURL=taxonomies.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"taxonomies.js","sourceRoot":"","sources":["../../src/sync/taxonomies.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ export interface EmitConfig {
2
+ origin: string;
3
+ defaultCollection: string;
4
+ defaultLocale: string;
5
+ syncLimit?: number;
6
+ postsSyncLimit?: number;
7
+ mediaSyncLimit?: number;
8
+ concurrency?: number;
9
+ }
10
+ /**
11
+ * Initializes a new .emit workspace directory with default config.
12
+ */
13
+ export declare function initWorkspace(workspaceRoot: string, initialConfig?: Partial<EmitConfig>): void;
14
+ /**
15
+ * Loads the EmitConfig from the active workspace.
16
+ */
17
+ export declare function loadConfig(workspaceRoot?: string): EmitConfig;
18
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":"AA0BA,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,iBAAiB,EAAE,MAAM,CAAC;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAWD;;GAEG;AACH,wBAAgB,aAAa,CAAC,aAAa,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,GAAG,IAAI,CAkB9F;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,UAAU,CAyB7D"}
@@ -0,0 +1,79 @@
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 { getWorkspacePaths, requireWorkspaceRoot } from './workspace.js';
25
+ const DEFAULT_CONFIG = {
26
+ origin: 'https://example.com',
27
+ defaultCollection: 'posts',
28
+ defaultLocale: 'ja',
29
+ postsSyncLimit: 100,
30
+ mediaSyncLimit: 300,
31
+ concurrency: 8,
32
+ };
33
+ /**
34
+ * Initializes a new .emit workspace directory with default config.
35
+ */
36
+ export function initWorkspace(workspaceRoot, initialConfig) {
37
+ const paths = getWorkspacePaths(workspaceRoot);
38
+ if (!fs.existsSync(paths.emitDir)) {
39
+ fs.mkdirSync(paths.emitDir, { recursive: true });
40
+ }
41
+ const postsDraftDir = path.join(paths.postsDir, 'draft');
42
+ const postsPublishedDir = path.join(paths.postsDir, 'published');
43
+ fs.mkdirSync(postsDraftDir, { recursive: true });
44
+ fs.mkdirSync(postsPublishedDir, { recursive: true });
45
+ const config = {
46
+ ...DEFAULT_CONFIG,
47
+ ...initialConfig,
48
+ };
49
+ fs.writeFileSync(paths.configPath, JSON.stringify(config, null, 2), 'utf8');
50
+ }
51
+ /**
52
+ * Loads the EmitConfig from the active workspace.
53
+ */
54
+ export function loadConfig(workspaceRoot) {
55
+ const root = workspaceRoot || requireWorkspaceRoot();
56
+ const paths = getWorkspacePaths(root);
57
+ if (!fs.existsSync(paths.configPath)) {
58
+ return {
59
+ ...DEFAULT_CONFIG,
60
+ origin: process.env.EMDASH_ORIGIN || DEFAULT_CONFIG.origin,
61
+ };
62
+ }
63
+ try {
64
+ const raw = fs.readFileSync(paths.configPath, 'utf8');
65
+ const parsed = JSON.parse(raw);
66
+ return {
67
+ origin: process.env.EMDASH_ORIGIN || parsed.origin || DEFAULT_CONFIG.origin,
68
+ defaultCollection: parsed.defaultCollection || DEFAULT_CONFIG.defaultCollection,
69
+ defaultLocale: parsed.defaultLocale || DEFAULT_CONFIG.defaultLocale,
70
+ postsSyncLimit: parsed.postsSyncLimit || DEFAULT_CONFIG.postsSyncLimit,
71
+ mediaSyncLimit: parsed.mediaSyncLimit || DEFAULT_CONFIG.mediaSyncLimit,
72
+ concurrency: parsed.concurrency || DEFAULT_CONFIG.concurrency,
73
+ };
74
+ }
75
+ catch {
76
+ return DEFAULT_CONFIG;
77
+ }
78
+ }
79
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAYzE,MAAM,cAAc,GAAe;IACjC,MAAM,EAAE,qBAAqB;IAC7B,iBAAiB,EAAE,OAAO;IAC1B,aAAa,EAAE,IAAI;IACnB,cAAc,EAAE,GAAG;IACnB,cAAc,EAAE,GAAG;IACnB,WAAW,EAAE,CAAC;CACf,CAAC;AAEF;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,aAAqB,EAAE,aAAmC;IACtF,MAAM,KAAK,GAAG,iBAAiB,CAAC,aAAa,CAAC,CAAC;IAE/C,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;QAClC,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACnD,CAAC;IAED,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IACzD,MAAM,iBAAiB,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;IACjE,EAAE,CAAC,SAAS,CAAC,aAAa,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACjD,EAAE,CAAC,SAAS,CAAC,iBAAiB,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAErD,MAAM,MAAM,GAAe;QACzB,GAAG,cAAc;QACjB,GAAG,aAAa;KACjB,CAAC;IAEF,EAAE,CAAC,aAAa,CAAC,KAAK,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;AAC9E,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,UAAU,CAAC,aAAsB;IAC/C,MAAM,IAAI,GAAG,aAAa,IAAI,oBAAoB,EAAE,CAAC;IACrD,MAAM,KAAK,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;IAEtC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC;QACrC,OAAO;YACL,GAAG,cAAc;YACjB,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,cAAc,CAAC,MAAM;SAC3D,CAAC;IACJ,CAAC;IAED,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QACtD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAwB,CAAC;QACtD,OAAO;YACL,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,MAAM,CAAC,MAAM,IAAI,cAAc,CAAC,MAAM;YAC3E,iBAAiB,EAAE,MAAM,CAAC,iBAAiB,IAAI,cAAc,CAAC,iBAAiB;YAC/E,aAAa,EAAE,MAAM,CAAC,aAAa,IAAI,cAAc,CAAC,aAAa;YACnE,cAAc,EAAE,MAAM,CAAC,cAAc,IAAI,cAAc,CAAC,cAAc;YACtE,cAAc,EAAE,MAAM,CAAC,cAAc,IAAI,cAAc,CAAC,cAAc;YACtE,WAAW,EAAE,MAAM,CAAC,WAAW,IAAI,cAAc,CAAC,WAAW;SAC9D,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,cAAc,CAAC;IACxB,CAAC;AACH,CAAC"}
@@ -0,0 +1,115 @@
1
+ import { z } from 'zod';
2
+ export declare const FrontmatterSeoSchema: z.ZodObject<{
3
+ title: z.ZodOptional<z.ZodNullable<z.ZodString>>;
4
+ description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
5
+ canonical: z.ZodOptional<z.ZodNullable<z.ZodString>>;
6
+ noIndex: z.ZodOptional<z.ZodBoolean>;
7
+ image: z.ZodOptional<z.ZodNullable<z.ZodString>>;
8
+ }, "strip", z.ZodTypeAny, {
9
+ title?: string | null | undefined;
10
+ description?: string | null | undefined;
11
+ canonical?: string | null | undefined;
12
+ noIndex?: boolean | undefined;
13
+ image?: string | null | undefined;
14
+ }, {
15
+ title?: string | null | undefined;
16
+ description?: string | null | undefined;
17
+ canonical?: string | null | undefined;
18
+ noIndex?: boolean | undefined;
19
+ image?: string | null | undefined;
20
+ }>;
21
+ export declare const FrontmatterSchema: z.ZodObject<{
22
+ id: z.ZodString;
23
+ slug: z.ZodString;
24
+ title: z.ZodString;
25
+ collection: z.ZodDefault<z.ZodString>;
26
+ status: z.ZodDefault<z.ZodEnum<["draft", "published"]>>;
27
+ publishedAt: z.ZodOptional<z.ZodNullable<z.ZodString>>;
28
+ locale: z.ZodDefault<z.ZodString>;
29
+ taxonomies: z.ZodDefault<z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString, "many">>>>;
30
+ seo: z.ZodOptional<z.ZodObject<{
31
+ title: z.ZodOptional<z.ZodNullable<z.ZodString>>;
32
+ description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
33
+ canonical: z.ZodOptional<z.ZodNullable<z.ZodString>>;
34
+ noIndex: z.ZodOptional<z.ZodBoolean>;
35
+ image: z.ZodOptional<z.ZodNullable<z.ZodString>>;
36
+ }, "strip", z.ZodTypeAny, {
37
+ title?: string | null | undefined;
38
+ description?: string | null | undefined;
39
+ canonical?: string | null | undefined;
40
+ noIndex?: boolean | undefined;
41
+ image?: string | null | undefined;
42
+ }, {
43
+ title?: string | null | undefined;
44
+ description?: string | null | undefined;
45
+ canonical?: string | null | undefined;
46
+ noIndex?: boolean | undefined;
47
+ image?: string | null | undefined;
48
+ }>>;
49
+ bylines: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>;
50
+ }, "strip", z.ZodTypeAny, {
51
+ id: string;
52
+ slug: string;
53
+ title: string;
54
+ collection: string;
55
+ status: "draft" | "published";
56
+ publishedAt?: string | null | undefined;
57
+ locale: string;
58
+ taxonomies: Record<string, string[]>;
59
+ seo?: {
60
+ title?: string | null | undefined;
61
+ description?: string | null | undefined;
62
+ canonical?: string | null | undefined;
63
+ noIndex?: boolean | undefined;
64
+ image?: string | null | undefined;
65
+ } | undefined;
66
+ bylines: string[];
67
+ }, {
68
+ id: string;
69
+ slug: string;
70
+ title: string;
71
+ collection?: string | undefined;
72
+ status?: "draft" | "published" | undefined;
73
+ publishedAt?: string | null | undefined;
74
+ locale?: string | undefined;
75
+ taxonomies?: Record<string, string[]> | undefined;
76
+ seo?: {
77
+ title?: string | null | undefined;
78
+ description?: string | null | undefined;
79
+ canonical?: string | null | undefined;
80
+ noIndex?: boolean | undefined;
81
+ image?: string | null | undefined;
82
+ } | undefined;
83
+ bylines?: string[] | undefined;
84
+ }>;
85
+ export type Frontmatter = z.infer<typeof FrontmatterSchema>;
86
+ export interface TaxonomyCandidates {
87
+ categories?: string[];
88
+ tags?: string[];
89
+ bylines?: string[];
90
+ [key: string]: string[] | undefined;
91
+ }
92
+ export interface ParsedMarkdown {
93
+ frontmatter: Frontmatter;
94
+ body: string;
95
+ rawYaml: string;
96
+ }
97
+ /**
98
+ * Parses markdown text into validated frontmatter and body.
99
+ */
100
+ export declare function parseMarkdown(content: string): ParsedMarkdown;
101
+ /**
102
+ * Formats a frontmatter object into clean YAML with helpful candidate comments.
103
+ */
104
+ export declare function formatFrontmatterYaml(data: Frontmatter, candidates?: TaxonomyCandidates): string;
105
+ /**
106
+ * Serializes frontmatter and markdown body into a full document.
107
+ */
108
+ export declare function serializeMarkdown(frontmatter: Frontmatter, body: string, candidates?: TaxonomyCandidates): string;
109
+ /**
110
+ * Sanitizes an article title for cross-platform safe filenames,
111
+ * preserving Japanese characters while replacing invalid filesystem chars and whitespace with hyphens.
112
+ */
113
+ export declare function sanitizeTitleForFilename(title: string): string;
114
+ export { formatDatePrefix, extractDatePrefixFromPath, computeArticleRelativePath, } from './naming.js';
115
+ //# sourceMappingURL=frontmatter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frontmatter.d.ts","sourceRoot":"","sources":["../../src/utils/frontmatter.ts"],"names":[],"mappings":"AAwBA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;EAM/B,CAAC;AAEH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAW5B,CAAC;AAEH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D,MAAM,WAAW,kBAAkB;IACjC,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC;CACrC;AAED,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,WAAW,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,CAS7D;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,WAAW,EAAE,UAAU,CAAC,EAAE,kBAAkB,GAAG,MAAM,CAsDhG;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,WAAW,EACxB,IAAI,EAAE,MAAM,EACZ,UAAU,CAAC,EAAE,kBAAkB,GAC9B,MAAM,CAIR;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CA2B9D;AAED,OAAO,EACL,gBAAgB,EAChB,yBAAyB,EACzB,0BAA0B,GAC3B,MAAM,aAAa,CAAC"}
@@ -0,0 +1,147 @@
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 matter from 'gray-matter';
23
+ import { z } from 'zod';
24
+ export const FrontmatterSeoSchema = z.object({
25
+ title: z.string().nullable().optional(),
26
+ description: z.string().nullable().optional(),
27
+ canonical: z.string().url().nullable().optional(),
28
+ noIndex: z.boolean().optional(),
29
+ image: z.string().nullable().optional(),
30
+ });
31
+ export const FrontmatterSchema = z.object({
32
+ id: z.string().length(26, 'ID must be a 26-character ULID'),
33
+ slug: z.string().min(1, 'Slug cannot be empty'),
34
+ title: z.string().min(1, 'Title cannot be empty'),
35
+ collection: z.string().default('posts'),
36
+ status: z.enum(['draft', 'published']).default('draft'),
37
+ publishedAt: z.string().nullable().optional(),
38
+ locale: z.string().default('ja'),
39
+ taxonomies: z.record(z.array(z.string())).optional().default({}),
40
+ seo: FrontmatterSeoSchema.optional(),
41
+ bylines: z.array(z.string()).optional().default([]),
42
+ });
43
+ /**
44
+ * Parses markdown text into validated frontmatter and body.
45
+ */
46
+ export function parseMarkdown(content) {
47
+ const parsed = matter(content);
48
+ const validated = FrontmatterSchema.parse(parsed.data);
49
+ return {
50
+ frontmatter: validated,
51
+ body: parsed.content,
52
+ rawYaml: matter.stringify('', parsed.data).trim(),
53
+ };
54
+ }
55
+ /**
56
+ * Formats a frontmatter object into clean YAML with helpful candidate comments.
57
+ */
58
+ export function formatFrontmatterYaml(data, candidates) {
59
+ const lines = ['---'];
60
+ lines.push(`id: ${data.id}`);
61
+ lines.push(`slug: ${data.slug}`);
62
+ lines.push(`title: ${JSON.stringify(data.title)}`);
63
+ lines.push(`collection: ${data.collection}`);
64
+ lines.push(`status: ${data.status}`);
65
+ lines.push(`publishedAt: ${data.publishedAt ? JSON.stringify(data.publishedAt) : 'null'}`);
66
+ lines.push(`locale: ${data.locale}`);
67
+ // Taxonomies
68
+ lines.push('taxonomies:');
69
+ const taxonomies = data.taxonomies || {};
70
+ const allTaxKeys = Array.from(new Set([...Object.keys(taxonomies), 'categories', 'tags']));
71
+ for (const taxKey of allTaxKeys) {
72
+ lines.push(` ${taxKey}:`);
73
+ const terms = taxonomies[taxKey] || [];
74
+ if (terms.length === 0) {
75
+ lines.push(' []');
76
+ }
77
+ else {
78
+ for (const term of terms) {
79
+ lines.push(` - ${term}`);
80
+ }
81
+ }
82
+ }
83
+ // SEO
84
+ if (data.seo) {
85
+ lines.push('seo:');
86
+ if (data.seo.title !== undefined)
87
+ lines.push(` title: ${JSON.stringify(data.seo.title)}`);
88
+ if (data.seo.description !== undefined)
89
+ lines.push(` description: ${JSON.stringify(data.seo.description)}`);
90
+ if (data.seo.canonical !== undefined)
91
+ lines.push(` canonical: ${JSON.stringify(data.seo.canonical)}`);
92
+ if (data.seo.noIndex !== undefined)
93
+ lines.push(` noIndex: ${data.seo.noIndex}`);
94
+ if (data.seo.image !== undefined)
95
+ lines.push(` image: ${JSON.stringify(data.seo.image)}`);
96
+ }
97
+ // Bylines
98
+ lines.push('bylines:');
99
+ const bylines = data.bylines || [];
100
+ if (bylines.length === 0) {
101
+ lines.push(' []');
102
+ }
103
+ else {
104
+ for (const byline of bylines) {
105
+ lines.push(` - ${byline}`);
106
+ }
107
+ }
108
+ lines.push('---');
109
+ return lines.join('\n');
110
+ }
111
+ /**
112
+ * Serializes frontmatter and markdown body into a full document.
113
+ */
114
+ export function serializeMarkdown(frontmatter, body, candidates) {
115
+ const yaml = formatFrontmatterYaml(frontmatter, candidates);
116
+ const cleanBody = body.startsWith('\n') ? body : `\n${body}`;
117
+ return `${yaml}${cleanBody.endsWith('\n') ? cleanBody : cleanBody + '\n'}`;
118
+ }
119
+ /**
120
+ * Sanitizes an article title for cross-platform safe filenames,
121
+ * preserving Japanese characters while replacing invalid filesystem chars and whitespace with hyphens.
122
+ */
123
+ export function sanitizeTitleForFilename(title) {
124
+ if (!title || !title.trim()) {
125
+ return 'untitled';
126
+ }
127
+ // Basic HTML entity decoding (e.g. &amp; -> &, &quot; -> ", &#8211; -> -)
128
+ let clean = title
129
+ .replace(/&amp;/g, '&')
130
+ .replace(/&quot;/g, '"')
131
+ .replace(/&#39;/g, "'")
132
+ .replace(/&lt;/g, '<')
133
+ .replace(/&gt;/g, '>')
134
+ .replace(/&#8211;/g, '-')
135
+ .replace(/&#8212;/g, '-');
136
+ // Replace whitespace and filesystem invalid characters (\ / : * ? " < > |) with hyphens
137
+ clean = clean.replace(/[\s/\\:*?"<>|]+/g, '-');
138
+ // Collapse multiple hyphens and trim leading/trailing hyphens
139
+ clean = clean.replace(/-{2,}/g, '-').replace(/^-+|-+$/g, '');
140
+ const maxLen = 100;
141
+ if (clean.length > maxLen) {
142
+ clean = clean.slice(0, maxLen).replace(/-+$/, '');
143
+ }
144
+ return clean || 'untitled';
145
+ }
146
+ export { formatDatePrefix, extractDatePrefixFromPath, computeArticleRelativePath, } from './naming.js';
147
+ //# sourceMappingURL=frontmatter.js.map