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.
- package/LICENSE +21 -0
- package/README.md +798 -2
- package/dist/bin/emit.d.ts +5 -0
- package/dist/bin/emit.d.ts.map +1 -0
- package/dist/bin/emit.js +63 -0
- package/dist/bin/emit.js.map +1 -0
- package/dist/cli/commands/new.d.ts +4 -0
- package/dist/cli/commands/new.d.ts.map +1 -0
- package/dist/cli/commands/new.js +49 -0
- package/dist/cli/commands/new.js.map +1 -0
- package/dist/cli/commands/profile.d.ts +3 -0
- package/dist/cli/commands/profile.d.ts.map +1 -0
- package/dist/cli/commands/profile.js +104 -0
- package/dist/cli/commands/profile.js.map +1 -0
- package/dist/cli/commands/pull.d.ts +4 -0
- package/dist/cli/commands/pull.d.ts.map +1 -0
- package/dist/cli/commands/pull.js +157 -0
- package/dist/cli/commands/pull.js.map +1 -0
- package/dist/cli/commands/push.d.ts +4 -0
- package/dist/cli/commands/push.d.ts.map +1 -0
- package/dist/cli/commands/push.js +107 -0
- package/dist/cli/commands/push.js.map +1 -0
- package/dist/cli/commands/status.d.ts +4 -0
- package/dist/cli/commands/status.d.ts.map +1 -0
- package/dist/cli/commands/status.js +100 -0
- package/dist/cli/commands/status.js.map +1 -0
- package/dist/cli/context.d.ts +15 -0
- package/dist/cli/context.d.ts.map +1 -0
- package/dist/cli/context.js +75 -0
- package/dist/cli/context.js.map +1 -0
- package/dist/client/api.d.ts +19 -0
- package/dist/client/api.d.ts.map +1 -0
- package/dist/client/api.js +96 -0
- package/dist/client/api.js.map +1 -0
- package/dist/client/auth.d.ts +48 -0
- package/dist/client/auth.d.ts.map +1 -0
- package/dist/client/auth.js +165 -0
- package/dist/client/auth.js.map +1 -0
- package/dist/client/error.d.ts +22 -0
- package/dist/client/error.d.ts.map +1 -0
- package/dist/client/error.js +57 -0
- package/dist/client/error.js.map +1 -0
- package/dist/client/http.d.ts +31 -0
- package/dist/client/http.d.ts.map +1 -0
- package/dist/client/http.js +97 -0
- package/dist/client/http.js.map +1 -0
- package/dist/client/lock.d.ts +40 -0
- package/dist/client/lock.d.ts.map +1 -0
- package/dist/client/lock.js +104 -0
- package/dist/client/lock.js.map +1 -0
- package/dist/client/mapper.d.ts +37 -0
- package/dist/client/mapper.d.ts.map +1 -0
- package/dist/client/mapper.js +142 -0
- package/dist/client/mapper.js.map +1 -0
- package/dist/client/portable-text.d.ts +94 -0
- package/dist/client/portable-text.d.ts.map +1 -0
- package/dist/client/portable-text.js +428 -0
- package/dist/client/portable-text.js.map +1 -0
- package/dist/client/schemas.d.ts +1928 -0
- package/dist/client/schemas.d.ts.map +1 -0
- package/dist/client/schemas.js +123 -0
- package/dist/client/schemas.js.map +1 -0
- package/dist/db/cache.d.ts +60 -0
- package/dist/db/cache.d.ts.map +1 -0
- package/dist/db/cache.js +303 -0
- package/dist/db/cache.js.map +1 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +45 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/context.d.ts +14 -0
- package/dist/mcp/context.d.ts.map +1 -0
- package/dist/mcp/context.js +23 -0
- package/dist/mcp/context.js.map +1 -0
- package/dist/mcp/server.d.ts +12 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +106 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/tools/profile.d.ts +6 -0
- package/dist/mcp/tools/profile.d.ts.map +1 -0
- package/dist/mcp/tools/profile.js +81 -0
- package/dist/mcp/tools/profile.js.map +1 -0
- package/dist/mcp/tools/sync-diff.d.ts +4 -0
- package/dist/mcp/tools/sync-diff.d.ts.map +1 -0
- package/dist/mcp/tools/sync-diff.js +115 -0
- package/dist/mcp/tools/sync-diff.js.map +1 -0
- package/dist/mcp/tools/sync-new.d.ts +4 -0
- package/dist/mcp/tools/sync-new.d.ts.map +1 -0
- package/dist/mcp/tools/sync-new.js +54 -0
- package/dist/mcp/tools/sync-new.js.map +1 -0
- package/dist/mcp/tools/sync-pull.d.ts +4 -0
- package/dist/mcp/tools/sync-pull.d.ts.map +1 -0
- package/dist/mcp/tools/sync-pull.js +180 -0
- package/dist/mcp/tools/sync-pull.js.map +1 -0
- package/dist/mcp/tools/sync-push.d.ts +4 -0
- package/dist/mcp/tools/sync-push.d.ts.map +1 -0
- package/dist/mcp/tools/sync-push.js +105 -0
- package/dist/mcp/tools/sync-push.js.map +1 -0
- package/dist/mcp/tools/sync-status.d.ts +4 -0
- package/dist/mcp/tools/sync-status.d.ts.map +1 -0
- package/dist/mcp/tools/sync-status.js +45 -0
- package/dist/mcp/tools/sync-status.js.map +1 -0
- package/dist/sync/bylines.d.ts +23 -0
- package/dist/sync/bylines.d.ts.map +1 -0
- package/dist/sync/bylines.js +112 -0
- package/dist/sync/bylines.js.map +1 -0
- package/dist/sync/diff.d.ts +16 -0
- package/dist/sync/diff.d.ts.map +1 -0
- package/dist/sync/diff.js +68 -0
- package/dist/sync/diff.js.map +1 -0
- package/dist/sync/media.d.ts +37 -0
- package/dist/sync/media.d.ts.map +1 -0
- package/dist/sync/media.js +191 -0
- package/dist/sync/media.js.map +1 -0
- package/dist/sync/merge.d.ts +36 -0
- package/dist/sync/merge.d.ts.map +1 -0
- package/dist/sync/merge.js +112 -0
- package/dist/sync/merge.js.map +1 -0
- package/dist/sync/new.d.ts +24 -0
- package/dist/sync/new.d.ts.map +1 -0
- package/dist/sync/new.js +75 -0
- package/dist/sync/new.js.map +1 -0
- package/dist/sync/orchestrator.d.ts +55 -0
- package/dist/sync/orchestrator.d.ts.map +1 -0
- package/dist/sync/orchestrator.js +222 -0
- package/dist/sync/orchestrator.js.map +1 -0
- package/dist/sync/preflight.d.ts +25 -0
- package/dist/sync/preflight.d.ts.map +1 -0
- package/dist/sync/preflight.js +128 -0
- package/dist/sync/preflight.js.map +1 -0
- package/dist/sync/pull.d.ts +67 -0
- package/dist/sync/pull.d.ts.map +1 -0
- package/dist/sync/pull.js +405 -0
- package/dist/sync/pull.js.map +1 -0
- package/dist/sync/push.d.ts +33 -0
- package/dist/sync/push.d.ts.map +1 -0
- package/dist/sync/push.js +432 -0
- package/dist/sync/push.js.map +1 -0
- package/dist/sync/status.d.ts +24 -0
- package/dist/sync/status.d.ts.map +1 -0
- package/dist/sync/status.js +85 -0
- package/dist/sync/status.js.map +1 -0
- package/dist/sync/taxonomies.d.ts +49 -0
- package/dist/sync/taxonomies.d.ts.map +1 -0
- package/dist/sync/taxonomies.js +220 -0
- package/dist/sync/taxonomies.js.map +1 -0
- package/dist/utils/config.d.ts +18 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +79 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/frontmatter.d.ts +115 -0
- package/dist/utils/frontmatter.d.ts.map +1 -0
- package/dist/utils/frontmatter.js +147 -0
- package/dist/utils/frontmatter.js.map +1 -0
- package/dist/utils/naming.d.ts +28 -0
- package/dist/utils/naming.d.ts.map +1 -0
- package/dist/utils/naming.js +94 -0
- package/dist/utils/naming.js.map +1 -0
- package/dist/utils/profile.d.ts +72 -0
- package/dist/utils/profile.d.ts.map +1 -0
- package/dist/utils/profile.js +180 -0
- package/dist/utils/profile.js.map +1 -0
- package/dist/utils/version.d.ts +5 -0
- package/dist/utils/version.d.ts.map +1 -0
- package/dist/utils/version.js +43 -0
- package/dist/utils/version.js.map +1 -0
- package/dist/utils/workspace.d.ts +22 -0
- package/dist/utils/workspace.d.ts.map +1 -0
- package/dist/utils/workspace.js +74 -0
- package/dist/utils/workspace.js.map +1 -0
- package/package.json +68 -4
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { CacheDatabase } from '../db/cache.js';
|
|
2
|
+
import { type MapperOptions } from '../client/mapper.js';
|
|
3
|
+
import { type TaxonomyCandidates } from '../utils/frontmatter.js';
|
|
4
|
+
import { type PreflightError } from './preflight.js';
|
|
5
|
+
export interface PushOptions extends MapperOptions {
|
|
6
|
+
workspaceRoot: string;
|
|
7
|
+
cache: CacheDatabase;
|
|
8
|
+
origin: string;
|
|
9
|
+
token: string;
|
|
10
|
+
refreshToken?: string;
|
|
11
|
+
fetchFn?: typeof fetch;
|
|
12
|
+
candidates?: TaxonomyCandidates;
|
|
13
|
+
knownTaxonomies?: Record<string, string[]>;
|
|
14
|
+
}
|
|
15
|
+
export interface PushResult {
|
|
16
|
+
success: boolean;
|
|
17
|
+
id: string;
|
|
18
|
+
rev: string;
|
|
19
|
+
filePath: string;
|
|
20
|
+
conflict: boolean;
|
|
21
|
+
warnings: string[];
|
|
22
|
+
errors?: PreflightError[];
|
|
23
|
+
}
|
|
24
|
+
export declare class PushValidationError extends Error {
|
|
25
|
+
errors: PreflightError[];
|
|
26
|
+
constructor(message: string, errors: PreflightError[]);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Pushes a local markdown article to remote EmDash with full preflight guards,
|
|
30
|
+
* two-tier lock lease protection, and automatic 3-way merge on conflict.
|
|
31
|
+
*/
|
|
32
|
+
export declare function pushArticle(targetFilePath: string, options: PushOptions): Promise<PushResult>;
|
|
33
|
+
//# sourceMappingURL=push.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"push.d.ts","sourceRoot":"","sources":["../../src/sync/push.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAEpD,OAAO,EAAyC,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAIhG,OAAO,EAML,KAAK,kBAAkB,EACxB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAKzE,MAAM,WAAW,WAAY,SAAQ,aAAa;IAChD,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,aAAa,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;IACvB,UAAU,CAAC,EAAE,kBAAkB,CAAC;IAChC,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;CAC5C;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,MAAM,CAAC,EAAE,cAAc,EAAE,CAAC;CAC3B;AAED,qBAAa,mBAAoB,SAAQ,KAAK;IACrC,MAAM,EAAE,cAAc,EAAE,CAAC;IAEhC,YAAY,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,EAIpD;CACF;AAmDD;;;GAGG;AACH,wBAAsB,WAAW,CAC/B,cAAc,EAAE,MAAM,EACtB,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,UAAU,CAAC,CAqdrB"}
|
|
@@ -0,0 +1,432 @@
|
|
|
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 { withEntryLock } from '../client/lock.js';
|
|
25
|
+
import { apiResponseToLocal, localToApiPayload } from '../client/mapper.js';
|
|
26
|
+
import { ApiError } from '../client/error.js';
|
|
27
|
+
import { createAuthedFetch } from '../client/http.js';
|
|
28
|
+
import { unwrapItem, extractRev } from '../client/api.js';
|
|
29
|
+
import { computeArticleRelativePath, extractDatePrefixFromPath, parseMarkdown, serializeMarkdown, } from '../utils/frontmatter.js';
|
|
30
|
+
import { loadLocalTaxonomyMaps } from './taxonomies.js';
|
|
31
|
+
import { runPreflightChecks } from './preflight.js';
|
|
32
|
+
import { calculateHash } from './pull.js';
|
|
33
|
+
import { hybrid3WayMerge } from './merge.js';
|
|
34
|
+
import { syncRemoteBylines } from './bylines.js';
|
|
35
|
+
export class PushValidationError extends Error {
|
|
36
|
+
errors;
|
|
37
|
+
constructor(message, errors) {
|
|
38
|
+
super(message);
|
|
39
|
+
this.name = 'PushValidationError';
|
|
40
|
+
this.errors = errors;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Helper to transition content status via EmDash /publish or /unpublish endpoints.
|
|
45
|
+
*/
|
|
46
|
+
async function transitionContentStatus(collection, id, targetStatus, currentRev, publishedAt, options, fetchFn) {
|
|
47
|
+
const isPublishing = targetStatus === 'published';
|
|
48
|
+
const endpoint = new URL(`/_emdash/api/content/${collection}/${id}/${isPublishing ? 'publish' : 'unpublish'}`, options.origin).toString();
|
|
49
|
+
const body = { _rev: currentRev };
|
|
50
|
+
if (isPublishing && publishedAt) {
|
|
51
|
+
body.publishedAt = publishedAt;
|
|
52
|
+
}
|
|
53
|
+
const res = await fetchFn(endpoint, {
|
|
54
|
+
method: 'POST',
|
|
55
|
+
headers: {
|
|
56
|
+
Authorization: `Bearer ${options.token}`,
|
|
57
|
+
'Content-Type': 'application/json',
|
|
58
|
+
},
|
|
59
|
+
body: JSON.stringify(body),
|
|
60
|
+
});
|
|
61
|
+
if (!res.ok) {
|
|
62
|
+
const errText = await res.text().catch(() => '');
|
|
63
|
+
throw new ApiError({
|
|
64
|
+
message: `Failed to ${isPublishing ? 'publish' : 'unpublish'} content: ${res.statusText}${errText ? ` - ${errText}` : ''}`,
|
|
65
|
+
code: isPublishing ? 'PUBLISH_FAILED' : 'UNPUBLISH_FAILED',
|
|
66
|
+
status: res.status,
|
|
67
|
+
endpoint,
|
|
68
|
+
suggestion: isPublishing
|
|
69
|
+
? 'Verify publish permissions and revision state.'
|
|
70
|
+
: 'Verify unpublish permissions and revision state.',
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
const json = (await res.json().catch(() => ({})));
|
|
74
|
+
return extractRev(json, currentRev);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Pushes a local markdown article to remote EmDash with full preflight guards,
|
|
78
|
+
* two-tier lock lease protection, and automatic 3-way merge on conflict.
|
|
79
|
+
*/
|
|
80
|
+
export async function pushArticle(targetFilePath, options) {
|
|
81
|
+
const fetchFn = createAuthedFetch(options);
|
|
82
|
+
const fullPath = path.isAbsolute(targetFilePath)
|
|
83
|
+
? targetFilePath
|
|
84
|
+
: path.join(options.workspaceRoot, targetFilePath);
|
|
85
|
+
if (!fs.existsSync(fullPath)) {
|
|
86
|
+
throw new Error(`Target file not found: ${targetFilePath}`);
|
|
87
|
+
}
|
|
88
|
+
const rawContent = fs.readFileSync(fullPath, 'utf8');
|
|
89
|
+
const parsed = parseMarkdown(rawContent);
|
|
90
|
+
const id = parsed.frontmatter.id;
|
|
91
|
+
const collection = parsed.frontmatter.collection;
|
|
92
|
+
// Retrieve sync state and base snapshot
|
|
93
|
+
const syncState = options.cache.getSyncState(id);
|
|
94
|
+
const baseSnapshot = options.cache.getBaseSnapshot(id);
|
|
95
|
+
// 1. Run Preflight Guards G2 - G9
|
|
96
|
+
const preflight = runPreflightChecks(targetFilePath, parsed, {
|
|
97
|
+
allowPublish: options.allowPublish,
|
|
98
|
+
knownTaxonomies: options.knownTaxonomies,
|
|
99
|
+
baseMarkdown: baseSnapshot?.bodyMarkdown,
|
|
100
|
+
previousStatus: syncState?.status || 'draft',
|
|
101
|
+
});
|
|
102
|
+
if (!preflight.valid) {
|
|
103
|
+
throw new PushValidationError(`Push blocked by Preflight Guards (${preflight.errors.length} error(s)).`, preflight.errors);
|
|
104
|
+
}
|
|
105
|
+
// Update parsed with cleaned body (e.g. trimmed duplicate heading)
|
|
106
|
+
parsed.body = preflight.cleanedBody;
|
|
107
|
+
const currentRev = syncState?.remoteRev || baseSnapshot?.rev || '';
|
|
108
|
+
const warnings = [...preflight.warnings];
|
|
109
|
+
// Load local bylines map for converting frontmatter byline slugs to ULIDs
|
|
110
|
+
let bylineSlugToId = options.bylineSlugToId;
|
|
111
|
+
if (!bylineSlugToId) {
|
|
112
|
+
const cachedMaps = options.cache.getBylineMaps();
|
|
113
|
+
if (Object.keys(cachedMaps.slugToId).length > 0) {
|
|
114
|
+
bylineSlugToId = cachedMaps.slugToId;
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
const freshMaps = await syncRemoteBylines({
|
|
118
|
+
origin: options.origin,
|
|
119
|
+
token: options.token,
|
|
120
|
+
refreshToken: options.refreshToken,
|
|
121
|
+
cache: options.cache,
|
|
122
|
+
fetchFn: options.fetchFn,
|
|
123
|
+
}).catch(() => ({ slugToId: {}, idToSlug: {} }));
|
|
124
|
+
bylineSlugToId = freshMaps.slugToId;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
const effectiveOptions = {
|
|
128
|
+
...options,
|
|
129
|
+
bylineSlugToId,
|
|
130
|
+
};
|
|
131
|
+
// 2. Determine whether this is a new article create (POST) or update (PUT)
|
|
132
|
+
const isNewArticle = !syncState;
|
|
133
|
+
// Helper to synchronize taxonomy assignments
|
|
134
|
+
const syncArticleTaxonomies = async (articleId) => {
|
|
135
|
+
if (!parsed.frontmatter.taxonomies)
|
|
136
|
+
return;
|
|
137
|
+
const taxMaps = loadLocalTaxonomyMaps(options.workspaceRoot);
|
|
138
|
+
const updateTaxonomy = async (taxonomyName, termSlugsOrIds) => {
|
|
139
|
+
const slugMap = taxMaps.slugToId[taxonomyName] || {};
|
|
140
|
+
const resolvedTermIds = termSlugsOrIds
|
|
141
|
+
.map((item) => slugMap[item] || item)
|
|
142
|
+
.filter((tid) => Boolean(tid));
|
|
143
|
+
const termsUrl = new URL(`/_emdash/api/content/${collection}/${articleId}/terms/${taxonomyName}`, options.origin).toString();
|
|
144
|
+
const termRes = await fetchFn(termsUrl, {
|
|
145
|
+
method: 'POST',
|
|
146
|
+
headers: {
|
|
147
|
+
Authorization: `Bearer ${options.token}`,
|
|
148
|
+
'Content-Type': 'application/json',
|
|
149
|
+
},
|
|
150
|
+
body: JSON.stringify({ termIds: resolvedTermIds }),
|
|
151
|
+
});
|
|
152
|
+
if (!termRes.ok) {
|
|
153
|
+
const errBody = await termRes.text().catch(() => '');
|
|
154
|
+
warnings.push(`Failed to update ${taxonomyName} terms: ${termRes.statusText} ${errBody}`);
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
if (parsed.frontmatter.taxonomies.categories) {
|
|
158
|
+
await updateTaxonomy('category', parsed.frontmatter.taxonomies.categories);
|
|
159
|
+
}
|
|
160
|
+
if (parsed.frontmatter.taxonomies.tags) {
|
|
161
|
+
await updateTaxonomy('tag', parsed.frontmatter.taxonomies.tags);
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
if (isNewArticle) {
|
|
165
|
+
let payload = localToApiPayload(parsed, '', effectiveOptions);
|
|
166
|
+
const postUrl = new URL(`/_emdash/api/content/${collection}`, options.origin).toString();
|
|
167
|
+
const res = await fetchFn(postUrl, {
|
|
168
|
+
method: 'POST',
|
|
169
|
+
headers: {
|
|
170
|
+
Authorization: `Bearer ${options.token}`,
|
|
171
|
+
'Content-Type': 'application/json',
|
|
172
|
+
},
|
|
173
|
+
body: JSON.stringify(payload),
|
|
174
|
+
});
|
|
175
|
+
if (!res.ok) {
|
|
176
|
+
const errBody = await res.text().catch(() => '');
|
|
177
|
+
throw new ApiError({
|
|
178
|
+
message: `Create push failed: ${res.statusText} - ${errBody}`,
|
|
179
|
+
code: 'PUSH_CREATE_FAILED',
|
|
180
|
+
status: res.status,
|
|
181
|
+
endpoint: postUrl,
|
|
182
|
+
suggestion: 'Check if an article with this slug already exists or if token has create permissions.',
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
const createdJson = await res.json();
|
|
186
|
+
const createdItem = unwrapItem(createdJson);
|
|
187
|
+
const actualId = String(createdItem.id || id);
|
|
188
|
+
const newRev = extractRev(createdJson, '1');
|
|
189
|
+
// Update parsed frontmatter with the actual ID returned by EmDash
|
|
190
|
+
parsed.frontmatter.id = actualId;
|
|
191
|
+
// Sync taxonomies for newly created article
|
|
192
|
+
await syncArticleTaxonomies(actualId);
|
|
193
|
+
const currentPrefix = extractDatePrefixFromPath(fullPath);
|
|
194
|
+
const dateInput = parsed.frontmatter.publishedAt || currentPrefix || createdItem.createdAt || null;
|
|
195
|
+
const newRelPath = computeArticleRelativePath(parsed.frontmatter.collection, parsed.frontmatter.status, actualId, parsed.frontmatter.title || parsed.frontmatter.slug, dateInput);
|
|
196
|
+
const newFullPath = path.join(options.workspaceRoot, newRelPath);
|
|
197
|
+
const serializedContent = serializeMarkdown(parsed.frontmatter, parsed.body, options.candidates);
|
|
198
|
+
const newHash = calculateHash(serializedContent);
|
|
199
|
+
// Transition status to published if requested and permitted
|
|
200
|
+
let finalRev = newRev;
|
|
201
|
+
if (parsed.frontmatter.status === 'published' && options.allowPublish) {
|
|
202
|
+
finalRev = await transitionContentStatus(collection, actualId, 'published', newRev, parsed.frontmatter.publishedAt, options, fetchFn);
|
|
203
|
+
}
|
|
204
|
+
// Save newly serialized file (with actual server ID) and clean up provisional file
|
|
205
|
+
fs.mkdirSync(path.dirname(newFullPath), { recursive: true });
|
|
206
|
+
fs.writeFileSync(newFullPath, serializedContent, 'utf8');
|
|
207
|
+
if (fullPath !== newFullPath && fs.existsSync(fullPath)) {
|
|
208
|
+
try {
|
|
209
|
+
fs.unlinkSync(fullPath);
|
|
210
|
+
}
|
|
211
|
+
catch (unlinkErr) {
|
|
212
|
+
warnings.push(`Failed to remove provisional file at ${fullPath}: ${unlinkErr.message}.`);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
// Update Base snapshot and Sync state
|
|
216
|
+
options.cache.saveBaseSnapshot({
|
|
217
|
+
id: actualId,
|
|
218
|
+
rev: finalRev,
|
|
219
|
+
bodyMarkdown: parsed.body,
|
|
220
|
+
frontmatterYaml: JSON.stringify(parsed.frontmatter),
|
|
221
|
+
hash: newHash,
|
|
222
|
+
savedAt: Math.floor(Date.now() / 1000),
|
|
223
|
+
});
|
|
224
|
+
options.cache.saveSyncState({
|
|
225
|
+
id: actualId,
|
|
226
|
+
localFilePath: newRelPath,
|
|
227
|
+
localHash: newHash,
|
|
228
|
+
remoteRev: finalRev,
|
|
229
|
+
status: parsed.frontmatter.status,
|
|
230
|
+
slug: parsed.frontmatter.slug,
|
|
231
|
+
lastSyncedAt: Math.floor(Date.now() / 1000),
|
|
232
|
+
});
|
|
233
|
+
return {
|
|
234
|
+
success: true,
|
|
235
|
+
id: actualId,
|
|
236
|
+
rev: finalRev,
|
|
237
|
+
filePath: newRelPath,
|
|
238
|
+
conflict: false,
|
|
239
|
+
warnings,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
// Existing article: execute within full 7-minute entry lock lease
|
|
243
|
+
return await withEntryLock(collection, id, options, async () => {
|
|
244
|
+
let payload = localToApiPayload(parsed, currentRev, effectiveOptions);
|
|
245
|
+
const putUrl = new URL(`/_emdash/api/content/${collection}/${id}`, options.origin).toString();
|
|
246
|
+
let res = await fetchFn(putUrl, {
|
|
247
|
+
method: 'PUT',
|
|
248
|
+
headers: {
|
|
249
|
+
Authorization: `Bearer ${options.token}`,
|
|
250
|
+
'Content-Type': 'application/json',
|
|
251
|
+
},
|
|
252
|
+
body: JSON.stringify(payload),
|
|
253
|
+
});
|
|
254
|
+
// 3. Handle 409 Conflict
|
|
255
|
+
if (res.status === 409) {
|
|
256
|
+
const errJson = (await res.json().catch(() => ({})));
|
|
257
|
+
const errorCode = errJson.error?.code || 'CONFLICT';
|
|
258
|
+
if (errorCode === 'ENTRY_LOCKED') {
|
|
259
|
+
throw new ApiError({
|
|
260
|
+
message: `Entry is currently locked by another user. (${errJson.error?.message || ''})`,
|
|
261
|
+
code: 'ENTRY_LOCKED',
|
|
262
|
+
status: 409,
|
|
263
|
+
endpoint: putUrl,
|
|
264
|
+
suggestion: 'Wait for the other user to finish editing or release their lock before retrying.',
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
// True revision mismatch conflict: fetch remote latest and run Hybrid 3-Way Merge
|
|
268
|
+
const getUrl = new URL(`/_emdash/api/content/${collection}/${id}`, options.origin).toString();
|
|
269
|
+
const getRes = await fetchFn(getUrl, {
|
|
270
|
+
method: 'GET',
|
|
271
|
+
headers: {
|
|
272
|
+
Authorization: `Bearer ${options.token}`,
|
|
273
|
+
Accept: 'application/json',
|
|
274
|
+
},
|
|
275
|
+
});
|
|
276
|
+
if (!getRes.ok) {
|
|
277
|
+
throw new ApiError({
|
|
278
|
+
message: `Failed to fetch latest remote version during conflict resolution: ${getRes.statusText}`,
|
|
279
|
+
code: 'CONFLICT_REMOTE_FETCH_FAILED',
|
|
280
|
+
status: getRes.status,
|
|
281
|
+
endpoint: getUrl,
|
|
282
|
+
suggestion: 'Check remote server availability to resolve conflict.',
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
const remoteItem = (await getRes.json());
|
|
286
|
+
const remoteParsed = apiResponseToLocal(remoteItem, options);
|
|
287
|
+
// Perform Hybrid 3-Way Merge
|
|
288
|
+
const baseInfo = baseSnapshot
|
|
289
|
+
? {
|
|
290
|
+
frontmatter: JSON.parse(baseSnapshot.frontmatterYaml),
|
|
291
|
+
body: baseSnapshot.bodyMarkdown,
|
|
292
|
+
}
|
|
293
|
+
: null;
|
|
294
|
+
const mergeResult = hybrid3WayMerge(parsed, baseInfo, {
|
|
295
|
+
frontmatter: remoteParsed.frontmatter,
|
|
296
|
+
body: remoteParsed.body,
|
|
297
|
+
});
|
|
298
|
+
warnings.push(...mergeResult.warnings);
|
|
299
|
+
if (mergeResult.hasConflict) {
|
|
300
|
+
// Unresolvable conflict in body: write conflict markers to local file for AI/human resolution
|
|
301
|
+
const conflictedMarkdown = serializeMarkdown(mergeResult.frontmatter, mergeResult.body, options.candidates);
|
|
302
|
+
fs.writeFileSync(fullPath, conflictedMarkdown, 'utf8');
|
|
303
|
+
// Update sync state with latest remoteRev and update base snapshot to remote state
|
|
304
|
+
// so that once conflict markers are resolved, the subsequent push sends the correct _rev
|
|
305
|
+
options.cache.saveSyncState({
|
|
306
|
+
id,
|
|
307
|
+
localFilePath: targetFilePath,
|
|
308
|
+
localHash: calculateHash(conflictedMarkdown),
|
|
309
|
+
remoteRev: remoteParsed.rev,
|
|
310
|
+
status: parsed.frontmatter.status,
|
|
311
|
+
slug: parsed.frontmatter.slug,
|
|
312
|
+
lastSyncedAt: Math.floor(Date.now() / 1000),
|
|
313
|
+
});
|
|
314
|
+
options.cache.saveBaseSnapshot({
|
|
315
|
+
id,
|
|
316
|
+
rev: remoteParsed.rev,
|
|
317
|
+
bodyMarkdown: remoteParsed.body,
|
|
318
|
+
frontmatterYaml: JSON.stringify(remoteParsed.frontmatter),
|
|
319
|
+
hash: calculateHash(serializeMarkdown(remoteParsed.frontmatter, remoteParsed.body)),
|
|
320
|
+
savedAt: Math.floor(Date.now() / 1000),
|
|
321
|
+
});
|
|
322
|
+
return {
|
|
323
|
+
success: false,
|
|
324
|
+
id,
|
|
325
|
+
rev: remoteParsed.rev,
|
|
326
|
+
filePath: targetFilePath,
|
|
327
|
+
conflict: true,
|
|
328
|
+
warnings,
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
// Auto-merged cleanly: retry PUT with remote's _rev
|
|
332
|
+
parsed.frontmatter = mergeResult.frontmatter;
|
|
333
|
+
parsed.body = mergeResult.body;
|
|
334
|
+
payload = localToApiPayload(parsed, remoteParsed.rev, effectiveOptions);
|
|
335
|
+
res = await fetchFn(putUrl, {
|
|
336
|
+
method: 'PUT',
|
|
337
|
+
headers: {
|
|
338
|
+
Authorization: `Bearer ${options.token}`,
|
|
339
|
+
'Content-Type': 'application/json',
|
|
340
|
+
},
|
|
341
|
+
body: JSON.stringify(payload),
|
|
342
|
+
});
|
|
343
|
+
if (!res.ok) {
|
|
344
|
+
const errText = await res.text().catch(() => '');
|
|
345
|
+
throw new ApiError({
|
|
346
|
+
message: `Retry push after auto-merge failed: ${res.statusText}${errText ? ` - ${errText}` : ''}`,
|
|
347
|
+
code: 'PUSH_RETRY_FAILED',
|
|
348
|
+
status: res.status,
|
|
349
|
+
endpoint: putUrl,
|
|
350
|
+
suggestion: 'Another change might have been committed in the meantime. Try pulling again.',
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
if (!res.ok) {
|
|
355
|
+
const errText = await res.text().catch(() => '');
|
|
356
|
+
throw new ApiError({
|
|
357
|
+
message: `Push failed: ${res.statusText}${errText ? ` - ${errText}` : ''}`,
|
|
358
|
+
code: 'PUSH_FAILED',
|
|
359
|
+
status: res.status,
|
|
360
|
+
endpoint: putUrl,
|
|
361
|
+
suggestion: 'Verify network connectivity, token permissions, and article status.',
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
const updatedJson = await res.json().catch(() => ({}));
|
|
365
|
+
const newRev = extractRev(updatedJson, currentRev);
|
|
366
|
+
// 4. Post-push updates & Path tracking
|
|
367
|
+
// Sync taxonomies on updated article
|
|
368
|
+
await syncArticleTaxonomies(id);
|
|
369
|
+
const currentPrefix = extractDatePrefixFromPath(fullPath);
|
|
370
|
+
const existingSync = options.cache.getSyncState(parsed.frontmatter.id);
|
|
371
|
+
const dateInput = parsed.frontmatter.publishedAt ||
|
|
372
|
+
currentPrefix ||
|
|
373
|
+
(existingSync?.lastSyncedAt ? new Date(existingSync.lastSyncedAt * 1000) : null);
|
|
374
|
+
const newRelPath = computeArticleRelativePath(parsed.frontmatter.collection, parsed.frontmatter.status, parsed.frontmatter.id, parsed.frontmatter.title || parsed.frontmatter.slug, dateInput);
|
|
375
|
+
const newFullPath = path.join(options.workspaceRoot, newRelPath);
|
|
376
|
+
const serializedContent = serializeMarkdown(parsed.frontmatter, parsed.body, options.candidates);
|
|
377
|
+
const newHash = calculateHash(serializedContent);
|
|
378
|
+
// Transition status (publish or unpublish) via dedicated endpoints if needed
|
|
379
|
+
let finalRev = newRev;
|
|
380
|
+
const previousStatus = syncState?.status || 'draft';
|
|
381
|
+
if (parsed.frontmatter.status === 'published' && options.allowPublish) {
|
|
382
|
+
finalRev = await transitionContentStatus(collection, id, 'published', newRev, parsed.frontmatter.publishedAt, options, fetchFn);
|
|
383
|
+
}
|
|
384
|
+
else if (parsed.frontmatter.status === 'draft' && previousStatus === 'published') {
|
|
385
|
+
finalRev = await transitionContentStatus(collection, id, 'draft', newRev, null, options, fetchFn);
|
|
386
|
+
}
|
|
387
|
+
// Only move/rename file on disk AFTER remote update & status transition have succeeded!
|
|
388
|
+
if (fullPath !== newFullPath) {
|
|
389
|
+
fs.mkdirSync(path.dirname(newFullPath), { recursive: true });
|
|
390
|
+
fs.writeFileSync(newFullPath, serializedContent, 'utf8');
|
|
391
|
+
if (fs.existsSync(fullPath)) {
|
|
392
|
+
try {
|
|
393
|
+
fs.unlinkSync(fullPath);
|
|
394
|
+
}
|
|
395
|
+
catch (unlinkErr) {
|
|
396
|
+
warnings.push(`Failed to remove old file at ${fullPath} after path transition: ${unlinkErr.message}. Please remove it manually to prevent duplicates.`);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
else {
|
|
401
|
+
fs.writeFileSync(fullPath, serializedContent, 'utf8');
|
|
402
|
+
}
|
|
403
|
+
// Update Base snapshot
|
|
404
|
+
options.cache.saveBaseSnapshot({
|
|
405
|
+
id,
|
|
406
|
+
rev: finalRev,
|
|
407
|
+
bodyMarkdown: parsed.body,
|
|
408
|
+
frontmatterYaml: JSON.stringify(parsed.frontmatter),
|
|
409
|
+
hash: newHash,
|
|
410
|
+
savedAt: Math.floor(Date.now() / 1000),
|
|
411
|
+
});
|
|
412
|
+
// Update sync state
|
|
413
|
+
options.cache.saveSyncState({
|
|
414
|
+
id,
|
|
415
|
+
localFilePath: newRelPath,
|
|
416
|
+
localHash: newHash,
|
|
417
|
+
remoteRev: finalRev,
|
|
418
|
+
status: parsed.frontmatter.status,
|
|
419
|
+
slug: parsed.frontmatter.slug,
|
|
420
|
+
lastSyncedAt: Math.floor(Date.now() / 1000),
|
|
421
|
+
});
|
|
422
|
+
return {
|
|
423
|
+
success: true,
|
|
424
|
+
id,
|
|
425
|
+
rev: finalRev,
|
|
426
|
+
filePath: newRelPath,
|
|
427
|
+
conflict: false,
|
|
428
|
+
warnings,
|
|
429
|
+
};
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
//# sourceMappingURL=push.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"push.js","sourceRoot":"","sources":["../../src/sync/push.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { CacheDatabase } from '../db/cache.js';
|
|
2
|
+
export interface ArticleStatusItem {
|
|
3
|
+
id: string;
|
|
4
|
+
slug: string;
|
|
5
|
+
title: string;
|
|
6
|
+
status: 'draft' | 'published';
|
|
7
|
+
syncStatus: 'synced' | 'modified' | 'untracked';
|
|
8
|
+
filePath: string;
|
|
9
|
+
rev?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface WorkspaceStatusResult {
|
|
12
|
+
items: ArticleStatusItem[];
|
|
13
|
+
summary: {
|
|
14
|
+
total: number;
|
|
15
|
+
synced: number;
|
|
16
|
+
modified: number;
|
|
17
|
+
untracked: number;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Scans the workspace directory and compares files against cache.db to compute sync status.
|
|
22
|
+
*/
|
|
23
|
+
export declare function getWorkspaceStatus(workspaceRoot: string, cache: CacheDatabase, collection?: string): WorkspaceStatusResult;
|
|
24
|
+
//# sourceMappingURL=status.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/sync/status.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAIpD,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,OAAO,GAAG,WAAW,CAAC;IAC9B,UAAU,EAAE,QAAQ,GAAG,UAAU,GAAG,WAAW,CAAC;IAChD,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,iBAAiB,EAAE,CAAC;IAC3B,OAAO,EAAE;QACP,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;CACH;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,aAAa,EAAE,MAAM,EACrB,KAAK,EAAE,aAAa,EACpB,UAAU,GAAE,MAAgB,GAC3B,qBAAqB,CA4DvB"}
|
|
@@ -0,0 +1,85 @@
|
|
|
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 { parseMarkdown } from '../utils/frontmatter.js';
|
|
25
|
+
import { calculateHash } from './pull.js';
|
|
26
|
+
/**
|
|
27
|
+
* Scans the workspace directory and compares files against cache.db to compute sync status.
|
|
28
|
+
*/
|
|
29
|
+
export function getWorkspaceStatus(workspaceRoot, cache, collection = 'posts') {
|
|
30
|
+
const collectionDir = path.join(workspaceRoot, collection);
|
|
31
|
+
const items = [];
|
|
32
|
+
if (!fs.existsSync(collectionDir)) {
|
|
33
|
+
return {
|
|
34
|
+
items: [],
|
|
35
|
+
summary: { total: 0, synced: 0, modified: 0, untracked: 0 },
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
// Scan draft/ and published/ subdirectories
|
|
39
|
+
const subdirs = ['draft', 'published'];
|
|
40
|
+
for (const sub of subdirs) {
|
|
41
|
+
const subDirPath = path.join(collectionDir, sub);
|
|
42
|
+
if (!fs.existsSync(subDirPath))
|
|
43
|
+
continue;
|
|
44
|
+
const files = fs.readdirSync(subDirPath).filter((f) => f.endsWith('.md'));
|
|
45
|
+
for (const file of files) {
|
|
46
|
+
const relPath = path.join(collection, sub, file);
|
|
47
|
+
const fullPath = path.join(workspaceRoot, relPath);
|
|
48
|
+
const content = fs.readFileSync(fullPath, 'utf8');
|
|
49
|
+
try {
|
|
50
|
+
const parsed = parseMarkdown(content);
|
|
51
|
+
const currentHash = calculateHash(content);
|
|
52
|
+
const syncState = cache.getSyncState(parsed.frontmatter.id);
|
|
53
|
+
let syncStatus = 'untracked';
|
|
54
|
+
if (syncState) {
|
|
55
|
+
if (syncState.localHash === currentHash) {
|
|
56
|
+
syncStatus = 'synced';
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
syncStatus = 'modified';
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
items.push({
|
|
63
|
+
id: parsed.frontmatter.id,
|
|
64
|
+
slug: parsed.frontmatter.slug,
|
|
65
|
+
title: parsed.frontmatter.title,
|
|
66
|
+
status: parsed.frontmatter.status,
|
|
67
|
+
syncStatus,
|
|
68
|
+
filePath: relPath,
|
|
69
|
+
rev: syncState?.remoteRev,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
// Skip invalid markdown files
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
const summary = {
|
|
78
|
+
total: items.length,
|
|
79
|
+
synced: items.filter((i) => i.syncStatus === 'synced').length,
|
|
80
|
+
modified: items.filter((i) => i.syncStatus === 'modified').length,
|
|
81
|
+
untracked: items.filter((i) => i.syncStatus === 'untracked').length,
|
|
82
|
+
};
|
|
83
|
+
return { items, summary };
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/sync/status.ts"],"names":[],"mappings":"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"}
|