@stoneforge/quarry 1.12.0 → 1.14.0
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/README.md +2 -0
- package/dist/api/quarry-api.d.ts +9 -1
- package/dist/api/quarry-api.d.ts.map +1 -1
- package/dist/api/quarry-api.js +21 -2
- package/dist/api/quarry-api.js.map +1 -1
- package/dist/api/types.d.ts +8 -1
- package/dist/api/types.d.ts.map +1 -1
- package/dist/api/types.js.map +1 -1
- package/dist/cli/commands/auto-link-helper.d.ts +33 -0
- package/dist/cli/commands/auto-link-helper.d.ts.map +1 -0
- package/dist/cli/commands/auto-link-helper.js +74 -0
- package/dist/cli/commands/auto-link-helper.js.map +1 -0
- package/dist/cli/commands/crud.d.ts +3 -0
- package/dist/cli/commands/crud.d.ts.map +1 -1
- package/dist/cli/commands/crud.js +144 -15
- package/dist/cli/commands/crud.js.map +1 -1
- package/dist/cli/commands/docs.js +2 -2
- package/dist/cli/commands/docs.js.map +1 -1
- package/dist/cli/commands/document.js +1 -1
- package/dist/cli/commands/document.js.map +1 -1
- package/dist/cli/commands/entity.js +1 -1
- package/dist/cli/commands/entity.js.map +1 -1
- package/dist/cli/commands/external-sync.d.ts +18 -0
- package/dist/cli/commands/external-sync.d.ts.map +1 -0
- package/dist/cli/commands/external-sync.js +2499 -0
- package/dist/cli/commands/external-sync.js.map +1 -0
- package/dist/cli/commands/library.js +1 -1
- package/dist/cli/commands/library.js.map +1 -1
- package/dist/cli/commands/message.js +2 -2
- package/dist/cli/commands/message.js.map +1 -1
- package/dist/cli/commands/serve.d.ts.map +1 -1
- package/dist/cli/commands/serve.js +2 -0
- package/dist/cli/commands/serve.js.map +1 -1
- package/dist/cli/commands/task.d.ts.map +1 -1
- package/dist/cli/commands/task.js +7 -4
- package/dist/cli/commands/task.js.map +1 -1
- package/dist/cli/commands/team.js +1 -1
- package/dist/cli/commands/team.js.map +1 -1
- package/dist/cli/commands/workflow.js +1 -1
- package/dist/cli/commands/workflow.js.map +1 -1
- package/dist/cli/runner.d.ts.map +1 -1
- package/dist/cli/runner.js +3 -0
- package/dist/cli/runner.js.map +1 -1
- package/dist/cli/utils/progress.d.ts +30 -0
- package/dist/cli/utils/progress.d.ts.map +1 -0
- package/dist/cli/utils/progress.js +47 -0
- package/dist/cli/utils/progress.js.map +1 -0
- package/dist/config/config.d.ts.map +1 -1
- package/dist/config/config.js +34 -0
- package/dist/config/config.js.map +1 -1
- package/dist/config/defaults.d.ts +13 -1
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +22 -0
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/file.d.ts.map +1 -1
- package/dist/config/file.js +71 -0
- package/dist/config/file.js.map +1 -1
- package/dist/config/index.d.ts +3 -3
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +2 -2
- package/dist/config/index.js.map +1 -1
- package/dist/config/merge.d.ts.map +1 -1
- package/dist/config/merge.js +52 -1
- package/dist/config/merge.js.map +1 -1
- package/dist/config/types.d.ts +68 -1
- package/dist/config/types.d.ts.map +1 -1
- package/dist/config/types.js +33 -0
- package/dist/config/types.js.map +1 -1
- package/dist/config/validation.d.ts.map +1 -1
- package/dist/config/validation.js +64 -1
- package/dist/config/validation.js.map +1 -1
- package/dist/external-sync/adapters/document-sync-adapter.d.ts +150 -0
- package/dist/external-sync/adapters/document-sync-adapter.d.ts.map +1 -0
- package/dist/external-sync/adapters/document-sync-adapter.js +325 -0
- package/dist/external-sync/adapters/document-sync-adapter.js.map +1 -0
- package/dist/external-sync/adapters/task-sync-adapter.d.ts +177 -0
- package/dist/external-sync/adapters/task-sync-adapter.d.ts.map +1 -0
- package/dist/external-sync/adapters/task-sync-adapter.js +353 -0
- package/dist/external-sync/adapters/task-sync-adapter.js.map +1 -0
- package/dist/external-sync/auto-link.d.ts +66 -0
- package/dist/external-sync/auto-link.d.ts.map +1 -0
- package/dist/external-sync/auto-link.js +98 -0
- package/dist/external-sync/auto-link.js.map +1 -0
- package/dist/external-sync/conflict-resolver.d.ts +170 -0
- package/dist/external-sync/conflict-resolver.d.ts.map +1 -0
- package/dist/external-sync/conflict-resolver.js +580 -0
- package/dist/external-sync/conflict-resolver.js.map +1 -0
- package/dist/external-sync/index.d.ts +23 -0
- package/dist/external-sync/index.d.ts.map +1 -0
- package/dist/external-sync/index.js +24 -0
- package/dist/external-sync/index.js.map +1 -0
- package/dist/external-sync/provider-registry.d.ts +113 -0
- package/dist/external-sync/provider-registry.d.ts.map +1 -0
- package/dist/external-sync/provider-registry.js +205 -0
- package/dist/external-sync/provider-registry.js.map +1 -0
- package/dist/external-sync/providers/folder/folder-document-adapter.d.ts +97 -0
- package/dist/external-sync/providers/folder/folder-document-adapter.d.ts.map +1 -0
- package/dist/external-sync/providers/folder/folder-document-adapter.js +261 -0
- package/dist/external-sync/providers/folder/folder-document-adapter.js.map +1 -0
- package/dist/external-sync/providers/folder/folder-fs.d.ts +146 -0
- package/dist/external-sync/providers/folder/folder-fs.d.ts.map +1 -0
- package/dist/external-sync/providers/folder/folder-fs.js +300 -0
- package/dist/external-sync/providers/folder/folder-fs.js.map +1 -0
- package/dist/external-sync/providers/folder/folder-provider.d.ts +28 -0
- package/dist/external-sync/providers/folder/folder-provider.d.ts.map +1 -0
- package/dist/external-sync/providers/folder/folder-provider.js +87 -0
- package/dist/external-sync/providers/folder/folder-provider.js.map +1 -0
- package/dist/external-sync/providers/folder/index.d.ts +11 -0
- package/dist/external-sync/providers/folder/index.d.ts.map +1 -0
- package/dist/external-sync/providers/folder/index.js +13 -0
- package/dist/external-sync/providers/folder/index.js.map +1 -0
- package/dist/external-sync/providers/github/github-api.d.ts +271 -0
- package/dist/external-sync/providers/github/github-api.d.ts.map +1 -0
- package/dist/external-sync/providers/github/github-api.js +366 -0
- package/dist/external-sync/providers/github/github-api.js.map +1 -0
- package/dist/external-sync/providers/github/github-field-map.d.ts +76 -0
- package/dist/external-sync/providers/github/github-field-map.d.ts.map +1 -0
- package/dist/external-sync/providers/github/github-field-map.js +157 -0
- package/dist/external-sync/providers/github/github-field-map.js.map +1 -0
- package/dist/external-sync/providers/github/github-provider.d.ts +36 -0
- package/dist/external-sync/providers/github/github-provider.d.ts.map +1 -0
- package/dist/external-sync/providers/github/github-provider.js +212 -0
- package/dist/external-sync/providers/github/github-provider.js.map +1 -0
- package/dist/external-sync/providers/github/github-task-adapter.d.ts +135 -0
- package/dist/external-sync/providers/github/github-task-adapter.d.ts.map +1 -0
- package/dist/external-sync/providers/github/github-task-adapter.js +374 -0
- package/dist/external-sync/providers/github/github-task-adapter.js.map +1 -0
- package/dist/external-sync/providers/github/index.d.ts +12 -0
- package/dist/external-sync/providers/github/index.d.ts.map +1 -0
- package/dist/external-sync/providers/github/index.js +15 -0
- package/dist/external-sync/providers/github/index.js.map +1 -0
- package/dist/external-sync/providers/index.d.ts +13 -0
- package/dist/external-sync/providers/index.d.ts.map +1 -0
- package/dist/external-sync/providers/index.js +15 -0
- package/dist/external-sync/providers/index.js.map +1 -0
- package/dist/external-sync/providers/linear/index.d.ts +19 -0
- package/dist/external-sync/providers/linear/index.d.ts.map +1 -0
- package/dist/external-sync/providers/linear/index.js +19 -0
- package/dist/external-sync/providers/linear/index.js.map +1 -0
- package/dist/external-sync/providers/linear/linear-api.d.ts +252 -0
- package/dist/external-sync/providers/linear/linear-api.d.ts.map +1 -0
- package/dist/external-sync/providers/linear/linear-api.js +522 -0
- package/dist/external-sync/providers/linear/linear-api.js.map +1 -0
- package/dist/external-sync/providers/linear/linear-field-map.d.ts +135 -0
- package/dist/external-sync/providers/linear/linear-field-map.d.ts.map +1 -0
- package/dist/external-sync/providers/linear/linear-field-map.js +338 -0
- package/dist/external-sync/providers/linear/linear-field-map.js.map +1 -0
- package/dist/external-sync/providers/linear/linear-provider.d.ts +52 -0
- package/dist/external-sync/providers/linear/linear-provider.d.ts.map +1 -0
- package/dist/external-sync/providers/linear/linear-provider.js +169 -0
- package/dist/external-sync/providers/linear/linear-provider.js.map +1 -0
- package/dist/external-sync/providers/linear/linear-task-adapter.d.ts +190 -0
- package/dist/external-sync/providers/linear/linear-task-adapter.d.ts.map +1 -0
- package/dist/external-sync/providers/linear/linear-task-adapter.js +521 -0
- package/dist/external-sync/providers/linear/linear-task-adapter.js.map +1 -0
- package/dist/external-sync/providers/linear/linear-types.d.ts +114 -0
- package/dist/external-sync/providers/linear/linear-types.d.ts.map +1 -0
- package/dist/external-sync/providers/linear/linear-types.js +10 -0
- package/dist/external-sync/providers/linear/linear-types.js.map +1 -0
- package/dist/external-sync/providers/notion/index.d.ts +19 -0
- package/dist/external-sync/providers/notion/index.d.ts.map +1 -0
- package/dist/external-sync/providers/notion/index.js +20 -0
- package/dist/external-sync/providers/notion/index.js.map +1 -0
- package/dist/external-sync/providers/notion/notion-api.d.ts +253 -0
- package/dist/external-sync/providers/notion/notion-api.d.ts.map +1 -0
- package/dist/external-sync/providers/notion/notion-api.js +492 -0
- package/dist/external-sync/providers/notion/notion-api.js.map +1 -0
- package/dist/external-sync/providers/notion/notion-blocks.d.ts +93 -0
- package/dist/external-sync/providers/notion/notion-blocks.d.ts.map +1 -0
- package/dist/external-sync/providers/notion/notion-blocks.js +773 -0
- package/dist/external-sync/providers/notion/notion-blocks.js.map +1 -0
- package/dist/external-sync/providers/notion/notion-document-adapter.d.ts +176 -0
- package/dist/external-sync/providers/notion/notion-document-adapter.d.ts.map +1 -0
- package/dist/external-sync/providers/notion/notion-document-adapter.js +413 -0
- package/dist/external-sync/providers/notion/notion-document-adapter.js.map +1 -0
- package/dist/external-sync/providers/notion/notion-provider.d.ts +57 -0
- package/dist/external-sync/providers/notion/notion-provider.d.ts.map +1 -0
- package/dist/external-sync/providers/notion/notion-provider.js +159 -0
- package/dist/external-sync/providers/notion/notion-provider.js.map +1 -0
- package/dist/external-sync/providers/notion/notion-types.d.ts +388 -0
- package/dist/external-sync/providers/notion/notion-types.d.ts.map +1 -0
- package/dist/external-sync/providers/notion/notion-types.js +47 -0
- package/dist/external-sync/providers/notion/notion-types.js.map +1 -0
- package/dist/external-sync/sync-engine.d.ts +364 -0
- package/dist/external-sync/sync-engine.d.ts.map +1 -0
- package/dist/external-sync/sync-engine.js +1154 -0
- package/dist/external-sync/sync-engine.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/server/index.js +8 -8
- package/dist/server/index.js.map +1 -1
- package/dist/services/inbox.js +1 -1
- package/dist/sync/hash.d.ts +5 -0
- package/dist/sync/hash.d.ts.map +1 -1
- package/dist/sync/hash.js +21 -2
- package/dist/sync/hash.js.map +1 -1
- package/package.json +10 -12
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Document Sync Adapter Utilities
|
|
3
|
+
*
|
|
4
|
+
* Shared field mapping logic for converting between Stoneforge documents and
|
|
5
|
+
* external document representations (e.g., Notion pages, Obsidian notes).
|
|
6
|
+
*
|
|
7
|
+
* These utilities are provider-agnostic — they handle the common conversion
|
|
8
|
+
* logic that all document sync providers need.
|
|
9
|
+
*
|
|
10
|
+
* Key functions:
|
|
11
|
+
* - documentToExternalDocumentInput: Convert a Stoneforge Document → ExternalDocumentInput for push
|
|
12
|
+
* - externalDocumentToDocumentUpdates: Convert an ExternalDocument → Partial<Document> for pull
|
|
13
|
+
* - diffDocumentUpdates: Return only changed fields between existing and updated document
|
|
14
|
+
* - computeExternalDocumentHash: Deterministic hash for change detection
|
|
15
|
+
*/
|
|
16
|
+
import { createHash } from 'crypto';
|
|
17
|
+
// ============================================================================
|
|
18
|
+
// System Categories
|
|
19
|
+
// ============================================================================
|
|
20
|
+
/**
|
|
21
|
+
* Document categories that are system-managed and should be excluded from
|
|
22
|
+
* external sync. These documents are structural (task descriptions, message
|
|
23
|
+
* content) and are synced through their parent element's sync adapter.
|
|
24
|
+
*/
|
|
25
|
+
export const SYSTEM_CATEGORIES = new Set([
|
|
26
|
+
'task-description',
|
|
27
|
+
'message-content',
|
|
28
|
+
]);
|
|
29
|
+
/**
|
|
30
|
+
* Checks whether a document category is a system category that should
|
|
31
|
+
* be excluded from document sync.
|
|
32
|
+
*/
|
|
33
|
+
export function isSystemCategory(category) {
|
|
34
|
+
return SYSTEM_CATEGORIES.has(category);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Checks whether a document should be included in external sync operations
|
|
38
|
+
* (link-all, push, pull). A document is syncable if:
|
|
39
|
+
* - It does not have a system category (task-description, message-content)
|
|
40
|
+
* - It has a non-empty title (null, undefined, or whitespace-only titles are excluded)
|
|
41
|
+
*
|
|
42
|
+
* Documents without titles are typically system-generated (messages, task descriptions)
|
|
43
|
+
* that happen to not have the system category set, or scratch documents. They all
|
|
44
|
+
* slugify to "untitled.md" and overwrite each other, so they must be excluded.
|
|
45
|
+
*/
|
|
46
|
+
export function isSyncableDocument(doc) {
|
|
47
|
+
if (isSystemCategory(doc.category))
|
|
48
|
+
return false;
|
|
49
|
+
if (!doc.title || doc.title.trim().length === 0)
|
|
50
|
+
return false;
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
// ============================================================================
|
|
54
|
+
// Content Type Mapping
|
|
55
|
+
// ============================================================================
|
|
56
|
+
/**
|
|
57
|
+
* Maps Stoneforge ContentType to external document content type.
|
|
58
|
+
*
|
|
59
|
+
* Stoneforge uses 'markdown', 'text', and 'json'.
|
|
60
|
+
* External systems use 'markdown', 'text', and 'html'.
|
|
61
|
+
* JSON content is mapped to 'text' for external systems since most
|
|
62
|
+
* document providers don't have a native JSON content type.
|
|
63
|
+
*/
|
|
64
|
+
export function mapContentTypeToExternal(contentType) {
|
|
65
|
+
switch (contentType) {
|
|
66
|
+
case 'markdown':
|
|
67
|
+
return 'markdown';
|
|
68
|
+
case 'text':
|
|
69
|
+
return 'text';
|
|
70
|
+
case 'json':
|
|
71
|
+
// JSON doesn't have a direct external equivalent; map to text
|
|
72
|
+
return 'text';
|
|
73
|
+
default:
|
|
74
|
+
return 'text';
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Maps external document content type to Stoneforge ContentType.
|
|
79
|
+
*
|
|
80
|
+
* External systems use 'markdown', 'text', and 'html'.
|
|
81
|
+
* HTML content is mapped to 'text' since Stoneforge doesn't have an HTML
|
|
82
|
+
* content type — the content is stored as-is, just categorized as text.
|
|
83
|
+
*/
|
|
84
|
+
export function mapContentTypeFromExternal(contentType) {
|
|
85
|
+
switch (contentType) {
|
|
86
|
+
case 'markdown':
|
|
87
|
+
return 'markdown';
|
|
88
|
+
case 'text':
|
|
89
|
+
return 'text';
|
|
90
|
+
case 'html':
|
|
91
|
+
// HTML doesn't have a direct Stoneforge equivalent; map to text
|
|
92
|
+
return 'text';
|
|
93
|
+
default:
|
|
94
|
+
return 'text';
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Slugifies a library name for use as a directory name.
|
|
99
|
+
* Uses the same algorithm as the folder adapter's slugify function:
|
|
100
|
+
* lowercase, replace non-alphanumeric chars with hyphens, collapse
|
|
101
|
+
* consecutive hyphens, trim leading/trailing hyphens.
|
|
102
|
+
*
|
|
103
|
+
* @param name - Library name to slugify
|
|
104
|
+
* @returns Slugified directory name
|
|
105
|
+
*/
|
|
106
|
+
function slugifyLibraryName(name) {
|
|
107
|
+
const slug = name
|
|
108
|
+
.toLowerCase()
|
|
109
|
+
.replace(/[^a-z0-9-]/g, '-')
|
|
110
|
+
.replace(/-+/g, '-')
|
|
111
|
+
.replace(/^-|-$/g, '');
|
|
112
|
+
return slug || 'untitled';
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Resolves the library path for a document by querying its parent-child
|
|
116
|
+
* dependencies. Builds a hierarchical path from the root library down
|
|
117
|
+
* to the document's immediate parent library.
|
|
118
|
+
*
|
|
119
|
+
* For example, if a document belongs to library "API Reference" which
|
|
120
|
+
* is a child of library "Documentation", the returned path would be
|
|
121
|
+
* "documentation/api-reference".
|
|
122
|
+
*
|
|
123
|
+
* Design decisions:
|
|
124
|
+
* - If a document belongs to multiple libraries, the first one found is used
|
|
125
|
+
* - Library names are slugified for directory names
|
|
126
|
+
* - If the document has no library parent, returns undefined
|
|
127
|
+
*
|
|
128
|
+
* @param api - API for querying dependencies and elements
|
|
129
|
+
* @param documentId - The document's element ID
|
|
130
|
+
* @returns The slugified library path (e.g., 'documentation/api-reference'), or undefined if not in a library
|
|
131
|
+
*/
|
|
132
|
+
export async function resolveDocumentLibraryPath(api, documentId) {
|
|
133
|
+
// Find parent-child dependencies where document is the child (blockedId)
|
|
134
|
+
const deps = await api.getDependencies(documentId, ['parent-child']);
|
|
135
|
+
if (deps.length === 0) {
|
|
136
|
+
return undefined;
|
|
137
|
+
}
|
|
138
|
+
// Find the first library parent
|
|
139
|
+
let libraryId;
|
|
140
|
+
for (const dep of deps) {
|
|
141
|
+
const parent = await api.get(dep.blockerId);
|
|
142
|
+
if (parent && parent.type === 'library') {
|
|
143
|
+
libraryId = dep.blockerId;
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (!libraryId) {
|
|
148
|
+
return undefined;
|
|
149
|
+
}
|
|
150
|
+
// Walk up the library hierarchy to build the full path
|
|
151
|
+
const pathSegments = [];
|
|
152
|
+
let currentId = libraryId;
|
|
153
|
+
const visited = new Set(); // Prevent infinite loops from cycles
|
|
154
|
+
while (currentId && !visited.has(currentId)) {
|
|
155
|
+
visited.add(currentId);
|
|
156
|
+
const library = await api.get(currentId);
|
|
157
|
+
if (!library || library.type !== 'library') {
|
|
158
|
+
break;
|
|
159
|
+
}
|
|
160
|
+
pathSegments.unshift(slugifyLibraryName(library.name));
|
|
161
|
+
// Find the parent library of this library
|
|
162
|
+
const parentDeps = await api.getDependencies(currentId, ['parent-child']);
|
|
163
|
+
let foundParentLibrary = false;
|
|
164
|
+
for (const dep of parentDeps) {
|
|
165
|
+
const parent = await api.get(dep.blockerId);
|
|
166
|
+
if (parent && parent.type === 'library') {
|
|
167
|
+
currentId = dep.blockerId;
|
|
168
|
+
foundParentLibrary = true;
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
if (!foundParentLibrary) {
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return pathSegments.length > 0 ? pathSegments.join('/') : undefined;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Batch-resolves library paths for multiple documents.
|
|
180
|
+
* More efficient than calling resolveDocumentLibraryPath individually
|
|
181
|
+
* since it can reuse cached library lookups.
|
|
182
|
+
*
|
|
183
|
+
* @param api - API for querying dependencies and elements
|
|
184
|
+
* @param documentIds - Array of document element IDs
|
|
185
|
+
* @returns Map from document ID to library path (undefined entries omitted)
|
|
186
|
+
*/
|
|
187
|
+
export async function resolveDocumentLibraryPaths(api, documentIds) {
|
|
188
|
+
const result = new Map();
|
|
189
|
+
for (const docId of documentIds) {
|
|
190
|
+
const libraryPath = await resolveDocumentLibraryPath(api, docId);
|
|
191
|
+
if (libraryPath) {
|
|
192
|
+
result.set(docId, libraryPath);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return result;
|
|
196
|
+
}
|
|
197
|
+
// ============================================================================
|
|
198
|
+
// Push: Stoneforge Document → External Document
|
|
199
|
+
// ============================================================================
|
|
200
|
+
/**
|
|
201
|
+
* Converts a Stoneforge Document into an ExternalDocumentInput for
|
|
202
|
+
* creating/updating a page in an external system.
|
|
203
|
+
*
|
|
204
|
+
* Handles:
|
|
205
|
+
* - Title mapping (1:1, falls back to empty string if undefined)
|
|
206
|
+
* - Content mapping (1:1)
|
|
207
|
+
* - ContentType mapping (markdown/text → markdown/text, json → text)
|
|
208
|
+
* - Optional library path for directory-based organization
|
|
209
|
+
*
|
|
210
|
+
* @param doc - The Stoneforge document to convert
|
|
211
|
+
* @param libraryPath - Optional pre-computed library path for the document
|
|
212
|
+
* @returns ExternalDocumentInput ready for the provider adapter
|
|
213
|
+
*/
|
|
214
|
+
export function documentToExternalDocumentInput(doc, libraryPath) {
|
|
215
|
+
return {
|
|
216
|
+
title: doc.title ?? '',
|
|
217
|
+
content: doc.content,
|
|
218
|
+
contentType: mapContentTypeToExternal(doc.contentType),
|
|
219
|
+
...(doc.category !== undefined && { category: doc.category }),
|
|
220
|
+
...(doc.tags !== undefined && doc.tags.length > 0 && { tags: doc.tags }),
|
|
221
|
+
...(libraryPath !== undefined && { libraryPath }),
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
// ============================================================================
|
|
225
|
+
// Pull: External Document → Stoneforge Document Updates
|
|
226
|
+
// ============================================================================
|
|
227
|
+
/**
|
|
228
|
+
* Converts an ExternalDocument into a partial Document update object for
|
|
229
|
+
* applying external changes to a local Stoneforge document.
|
|
230
|
+
*
|
|
231
|
+
* Handles:
|
|
232
|
+
* - Title mapping (1:1)
|
|
233
|
+
* - Content mapping (1:1)
|
|
234
|
+
* - ContentType mapping (markdown/text/html → markdown/text/text)
|
|
235
|
+
*
|
|
236
|
+
* If existingDoc is provided, only changed fields are returned (diff mode).
|
|
237
|
+
* If existingDoc is undefined, all mappable fields are returned (create mode).
|
|
238
|
+
*
|
|
239
|
+
* @param externalDoc - The external document to convert
|
|
240
|
+
* @param existingDoc - The existing local document (undefined for new documents)
|
|
241
|
+
* @returns Partial<Document> with only the changed fields (or all fields if no existingDoc)
|
|
242
|
+
*/
|
|
243
|
+
export function externalDocumentToDocumentUpdates(externalDoc, existingDoc) {
|
|
244
|
+
const contentType = mapContentTypeFromExternal(externalDoc.contentType);
|
|
245
|
+
const fullUpdate = {
|
|
246
|
+
title: externalDoc.title,
|
|
247
|
+
content: externalDoc.content,
|
|
248
|
+
contentType,
|
|
249
|
+
};
|
|
250
|
+
// If no existing document, return full create input
|
|
251
|
+
if (!existingDoc) {
|
|
252
|
+
return fullUpdate;
|
|
253
|
+
}
|
|
254
|
+
// Diff mode: only return changed fields
|
|
255
|
+
return diffDocumentUpdates(existingDoc, fullUpdate);
|
|
256
|
+
}
|
|
257
|
+
// ============================================================================
|
|
258
|
+
// Diff Utilities
|
|
259
|
+
// ============================================================================
|
|
260
|
+
/**
|
|
261
|
+
* Compares a full update against an existing document and returns only
|
|
262
|
+
* the fields that actually changed.
|
|
263
|
+
*
|
|
264
|
+
* This prevents unnecessary updates when pulling changes from external
|
|
265
|
+
* systems where the data hasn't actually changed.
|
|
266
|
+
*
|
|
267
|
+
* Compared fields: title, content, contentType, category, tags
|
|
268
|
+
*/
|
|
269
|
+
export function diffDocumentUpdates(existing, updates) {
|
|
270
|
+
const diff = {};
|
|
271
|
+
if (updates.title !== undefined && updates.title !== existing.title) {
|
|
272
|
+
diff.title = updates.title;
|
|
273
|
+
}
|
|
274
|
+
if (updates.content !== undefined && updates.content !== existing.content) {
|
|
275
|
+
diff.content = updates.content;
|
|
276
|
+
}
|
|
277
|
+
if (updates.contentType !== undefined &&
|
|
278
|
+
updates.contentType !== existing.contentType) {
|
|
279
|
+
diff.contentType = updates.contentType;
|
|
280
|
+
}
|
|
281
|
+
if (updates.category !== undefined &&
|
|
282
|
+
updates.category !== existing.category) {
|
|
283
|
+
diff.category = updates.category;
|
|
284
|
+
}
|
|
285
|
+
if (updates.tags !== undefined && !arraysEqual(updates.tags, existing.tags)) {
|
|
286
|
+
diff.tags = updates.tags;
|
|
287
|
+
}
|
|
288
|
+
return diff;
|
|
289
|
+
}
|
|
290
|
+
// ============================================================================
|
|
291
|
+
// Hash Computation
|
|
292
|
+
// ============================================================================
|
|
293
|
+
/**
|
|
294
|
+
* Computes a deterministic hash of an ExternalDocument for change detection.
|
|
295
|
+
*
|
|
296
|
+
* The hash is based on the document's title, content, and contentType.
|
|
297
|
+
* Used by the sync engine to detect whether an external document has
|
|
298
|
+
* changed since the last sync, avoiding unnecessary pull operations.
|
|
299
|
+
*
|
|
300
|
+
* @param doc - The external document to hash
|
|
301
|
+
* @returns A hex-encoded SHA-256 hash string
|
|
302
|
+
*/
|
|
303
|
+
export function computeExternalDocumentHash(doc) {
|
|
304
|
+
const hash = createHash('sha256');
|
|
305
|
+
hash.update(doc.title);
|
|
306
|
+
hash.update('\0'); // null byte separator to avoid collisions
|
|
307
|
+
hash.update(doc.content);
|
|
308
|
+
hash.update('\0');
|
|
309
|
+
hash.update(doc.contentType);
|
|
310
|
+
return hash.digest('hex');
|
|
311
|
+
}
|
|
312
|
+
// ============================================================================
|
|
313
|
+
// Internal Helpers
|
|
314
|
+
// ============================================================================
|
|
315
|
+
/**
|
|
316
|
+
* Compares two arrays for equality (order-independent).
|
|
317
|
+
*/
|
|
318
|
+
function arraysEqual(a, b) {
|
|
319
|
+
if (a.length !== b.length)
|
|
320
|
+
return false;
|
|
321
|
+
const sortedA = [...a].sort();
|
|
322
|
+
const sortedB = [...b].sort();
|
|
323
|
+
return sortedA.every((val, idx) => val === sortedB[idx]);
|
|
324
|
+
}
|
|
325
|
+
//# sourceMappingURL=document-sync-adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"document-sync-adapter.js","sourceRoot":"","sources":["../../../src/external-sync/adapters/document-sync-adapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task Sync Adapter Utilities
|
|
3
|
+
*
|
|
4
|
+
* Shared field mapping logic for converting between Stoneforge tasks and
|
|
5
|
+
* external task representations (e.g., GitHub Issues, Linear issues).
|
|
6
|
+
*
|
|
7
|
+
* These utilities are provider-agnostic — each provider supplies its own
|
|
8
|
+
* TaskSyncFieldMapConfig that tells the adapter how to map statuses,
|
|
9
|
+
* priorities, task types, and labels to/from the external system.
|
|
10
|
+
*
|
|
11
|
+
* Key functions:
|
|
12
|
+
* - taskToExternalTask: Convert a Stoneforge Task → ExternalTaskInput for push
|
|
13
|
+
* - externalTaskToTaskUpdates: Convert an ExternalTask → Partial<Task> for pull
|
|
14
|
+
*/
|
|
15
|
+
import type { Task, TaskStatus, Priority, TaskTypeValue, DocumentId, Element, ElementId } from '@stoneforge/core';
|
|
16
|
+
import type { ExternalTask, ExternalTaskInput } from '@stoneforge/core';
|
|
17
|
+
/**
|
|
18
|
+
* Minimal API interface used by task sync adapter utilities.
|
|
19
|
+
* Only requires `get()` for description hydration.
|
|
20
|
+
* Both QuarryAPI and SyncEngineAPI satisfy this interface structurally.
|
|
21
|
+
*/
|
|
22
|
+
export interface TaskSyncAPI {
|
|
23
|
+
get<T extends Element>(id: ElementId): Promise<T | null>;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Provider-specific configuration for mapping task fields between
|
|
27
|
+
* Stoneforge and an external system.
|
|
28
|
+
*
|
|
29
|
+
* Each provider (GitHub, Linear, etc.) supplies its own config that
|
|
30
|
+
* describes how Stoneforge concepts map to the provider's label/state model.
|
|
31
|
+
*/
|
|
32
|
+
export interface TaskSyncFieldMapConfig {
|
|
33
|
+
/**
|
|
34
|
+
* Maps Stoneforge priority values to external label strings.
|
|
35
|
+
* e.g., { 1: 'priority:critical', 2: 'priority:high', 3: 'priority:medium', ... }
|
|
36
|
+
*/
|
|
37
|
+
readonly priorityLabels: Record<Priority, string>;
|
|
38
|
+
/**
|
|
39
|
+
* Maps Stoneforge task type values to external label strings.
|
|
40
|
+
* e.g., { bug: 'type:bug', feature: 'type:feature', task: 'type:task', chore: 'type:chore' }
|
|
41
|
+
*/
|
|
42
|
+
readonly taskTypeLabels: Record<TaskTypeValue, string>;
|
|
43
|
+
/**
|
|
44
|
+
* Optional: Maps Stoneforge TaskStatus values to external label strings.
|
|
45
|
+
* When present, status labels are pushed alongside priority and type labels,
|
|
46
|
+
* providing granular status visibility on external systems that only have
|
|
47
|
+
* binary open/closed states (e.g., GitHub).
|
|
48
|
+
*
|
|
49
|
+
* Optional because some providers (e.g., Linear) map status natively
|
|
50
|
+
* via workflow states and don't need label-based status mapping.
|
|
51
|
+
*
|
|
52
|
+
* e.g., { open: 'status:open', in_progress: 'status:in-progress', ... }
|
|
53
|
+
*/
|
|
54
|
+
readonly statusLabels?: Record<string, string>;
|
|
55
|
+
/**
|
|
56
|
+
* Prefix for sync-managed labels in the external system.
|
|
57
|
+
* Labels with this prefix are managed by the sync system and will be
|
|
58
|
+
* added/removed automatically. User labels without this prefix are preserved.
|
|
59
|
+
* e.g., 'sf:' would produce labels like 'sf:priority:critical', 'sf:type:bug'
|
|
60
|
+
*/
|
|
61
|
+
readonly syncLabelPrefix: string;
|
|
62
|
+
/**
|
|
63
|
+
* Converts a Stoneforge TaskStatus to the external system's state.
|
|
64
|
+
* Most external systems only have 'open' and 'closed' states.
|
|
65
|
+
* e.g., 'in_progress' → 'open', 'closed' → 'closed'
|
|
66
|
+
*/
|
|
67
|
+
readonly statusToState: (status: TaskStatus) => 'open' | 'closed';
|
|
68
|
+
/**
|
|
69
|
+
* Converts an external state + labels back to a Stoneforge TaskStatus.
|
|
70
|
+
* Labels are provided to allow more granular status inference
|
|
71
|
+
* (e.g., external 'open' + label 'status:in-progress' → 'in_progress').
|
|
72
|
+
*/
|
|
73
|
+
readonly stateToStatus: (state: 'open' | 'closed', labels: string[]) => TaskStatus;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Converts a Stoneforge Task into an ExternalTaskInput for creating/updating
|
|
77
|
+
* an issue in an external system.
|
|
78
|
+
*
|
|
79
|
+
* Handles:
|
|
80
|
+
* - Title mapping (1:1)
|
|
81
|
+
* - Status → state mapping via config.statusToState
|
|
82
|
+
* - Tags → labels (user tags preserved, sync-managed labels added)
|
|
83
|
+
* - Priority → label via config.priorityLabels
|
|
84
|
+
* - Task type → label via config.taskTypeLabels
|
|
85
|
+
* - Description hydration from descriptionRef document via api.get()
|
|
86
|
+
*
|
|
87
|
+
* Note: Assignees are intentionally NOT written to external systems.
|
|
88
|
+
* Stoneforge assignees are ephemeral agents (e.g., el-xxxx) that don't
|
|
89
|
+
* correspond to valid users on external platforms like GitHub.
|
|
90
|
+
*
|
|
91
|
+
* @param task - The Stoneforge task to convert
|
|
92
|
+
* @param config - Provider-specific field mapping configuration
|
|
93
|
+
* @param api - API with get() for hydrating description documents
|
|
94
|
+
* @returns ExternalTaskInput ready for the provider adapter
|
|
95
|
+
*/
|
|
96
|
+
export declare function taskToExternalTask(task: Task, config: TaskSyncFieldMapConfig, api: TaskSyncAPI): Promise<ExternalTaskInput>;
|
|
97
|
+
/**
|
|
98
|
+
* Converts an ExternalTask into a partial Task update object for applying
|
|
99
|
+
* external changes to a local Stoneforge task.
|
|
100
|
+
*
|
|
101
|
+
* Handles:
|
|
102
|
+
* - Title mapping (1:1)
|
|
103
|
+
* - State + labels → status mapping via config.stateToStatus
|
|
104
|
+
* - Labels → priority, taskType, tags (separating sync-managed from user labels)
|
|
105
|
+
* - Assignees (first assignee mapped if present)
|
|
106
|
+
*
|
|
107
|
+
* If existingTask is provided, only changed fields are returned (diff mode).
|
|
108
|
+
* If existingTask is undefined, all fields are returned (create mode).
|
|
109
|
+
*
|
|
110
|
+
* Note: Description (body) handling is NOT included in the returned partial.
|
|
111
|
+
* The caller (sync engine) is responsible for creating/updating the description
|
|
112
|
+
* Document and linking via descriptionRef, since that requires document creation
|
|
113
|
+
* which is a separate API operation.
|
|
114
|
+
*
|
|
115
|
+
* @param externalTask - The external task to convert
|
|
116
|
+
* @param existingTask - The existing local task (undefined for new tasks)
|
|
117
|
+
* @param config - Provider-specific field mapping configuration
|
|
118
|
+
* @returns Partial<Task> with only the changed fields (or all fields if no existingTask)
|
|
119
|
+
*/
|
|
120
|
+
export declare function externalTaskToTaskUpdates(externalTask: ExternalTask, existingTask: Task | undefined, config: TaskSyncFieldMapConfig): Partial<Task>;
|
|
121
|
+
/**
|
|
122
|
+
* Builds the complete set of external labels for a task.
|
|
123
|
+
*
|
|
124
|
+
* Combines:
|
|
125
|
+
* 1. Sync-managed priority label (prefixed)
|
|
126
|
+
* 2. Sync-managed task type label (prefixed)
|
|
127
|
+
* 3. Sync-managed status label (prefixed, when config.statusLabels is present)
|
|
128
|
+
* 4. User tags from the task (not prefixed — these are user-owned)
|
|
129
|
+
*/
|
|
130
|
+
export declare function buildExternalLabels(task: Task, config: TaskSyncFieldMapConfig): string[];
|
|
131
|
+
/**
|
|
132
|
+
* Result of parsing external labels into structured task fields
|
|
133
|
+
*/
|
|
134
|
+
export interface ParsedExternalLabels {
|
|
135
|
+
/** Extracted priority value, or undefined if no priority label found */
|
|
136
|
+
priority: Priority | undefined;
|
|
137
|
+
/** Extracted task type value, or undefined if no task type label found */
|
|
138
|
+
taskType: TaskTypeValue | undefined;
|
|
139
|
+
/** Extracted status value, or undefined if no status label found */
|
|
140
|
+
status: TaskStatus | undefined;
|
|
141
|
+
/** Labels that are not sync-managed (user tags) */
|
|
142
|
+
userTags: string[];
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Parses external labels into structured task field values.
|
|
146
|
+
*
|
|
147
|
+
* Separates sync-managed labels (prefixed) from user labels,
|
|
148
|
+
* and extracts priority, task type, and status values from the managed labels.
|
|
149
|
+
*/
|
|
150
|
+
export declare function parseExternalLabels(labels: readonly string[], config: TaskSyncFieldMapConfig): ParsedExternalLabels;
|
|
151
|
+
/**
|
|
152
|
+
* Hydrates a task's description from its descriptionRef document.
|
|
153
|
+
*
|
|
154
|
+
* If the task has a descriptionRef, fetches the document via the API
|
|
155
|
+
* and returns its content as the body string. Returns undefined if
|
|
156
|
+
* no descriptionRef is set or the document is not found.
|
|
157
|
+
*/
|
|
158
|
+
export declare function hydrateDescription(descriptionRef: DocumentId | undefined, api: TaskSyncAPI): Promise<string | undefined>;
|
|
159
|
+
/**
|
|
160
|
+
* Compares a full update against an existing task and returns only
|
|
161
|
+
* the fields that actually changed.
|
|
162
|
+
*
|
|
163
|
+
* This prevents unnecessary updates when pulling changes from external
|
|
164
|
+
* systems where the data hasn't actually changed.
|
|
165
|
+
*/
|
|
166
|
+
export declare function diffTaskUpdates(existingTask: Task, fullUpdate: Partial<Task>): Partial<Task>;
|
|
167
|
+
/**
|
|
168
|
+
* Returns the TaskSyncFieldMapConfig for a given provider name.
|
|
169
|
+
*
|
|
170
|
+
* Shared utility used by both the CLI link-all command and the sync engine
|
|
171
|
+
* push path to get the correct field mapping for a provider.
|
|
172
|
+
*
|
|
173
|
+
* @param providerName - The provider name (e.g., 'github', 'linear')
|
|
174
|
+
* @returns The provider-specific TaskSyncFieldMapConfig
|
|
175
|
+
*/
|
|
176
|
+
export declare function getFieldMapConfigForProvider(providerName: string): TaskSyncFieldMapConfig;
|
|
177
|
+
//# sourceMappingURL=task-sync-adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-sync-adapter.d.ts","sourceRoot":"","sources":["../../../src/external-sync/adapters/task-sync-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EACV,IAAI,EACJ,UAAU,EACV,QAAQ,EACR,aAAa,EACb,UAAU,EAEV,OAAO,EAEP,SAAS,EACV,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AASxE;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B,GAAG,CAAC,CAAC,SAAS,OAAO,EAAE,EAAE,EAAE,SAAS,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;CAC1D;AAMD;;;;;;GAMG;AACH,MAAM,WAAW,sBAAsB;IACrC;;;OAGG;IACH,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAElD;;;OAGG;IACH,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;IAEvD;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAE/C;;;;;OAKG;IACH,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IAEjC;;;;OAIG;IACH,QAAQ,CAAC,aAAa,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,MAAM,GAAG,QAAQ,CAAC;IAElE;;;;OAIG;IACH,QAAQ,CAAC,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,UAAU,CAAC;CACpF;AAMD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,sBAAsB,EAC9B,GAAG,EAAE,WAAW,GACf,OAAO,CAAC,iBAAiB,CAAC,CAuB5B;AAMD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,yBAAyB,CACvC,YAAY,EAAE,YAAY,EAC1B,YAAY,EAAE,IAAI,GAAG,SAAS,EAC9B,MAAM,EAAE,sBAAsB,GAC7B,OAAO,CAAC,IAAI,CAAC,CA8Cf;AAMD;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,sBAAsB,GAC7B,MAAM,EAAE,CAwCV;AAMD;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,wEAAwE;IACxE,QAAQ,EAAE,QAAQ,GAAG,SAAS,CAAC;IAC/B,0EAA0E;IAC1E,QAAQ,EAAE,aAAa,GAAG,SAAS,CAAC;IACpC,oEAAoE;IACpE,MAAM,EAAE,UAAU,GAAG,SAAS,CAAC;IAC/B,mDAAmD;IACnD,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,SAAS,MAAM,EAAE,EACzB,MAAM,EAAE,sBAAsB,GAC7B,oBAAoB,CAiDtB;AAMD;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CACtC,cAAc,EAAE,UAAU,GAAG,SAAS,EACtC,GAAG,EAAE,WAAW,GACf,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAW7B;AAqCD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,YAAY,EAAE,IAAI,EAClB,UAAU,EAAE,OAAO,CAAC,IAAI,CAAC,GACxB,OAAO,CAAC,IAAI,CAAC,CAoCf;AAsCD;;;;;;;;GAQG;AACH,wBAAgB,4BAA4B,CAAC,YAAY,EAAE,MAAM,GAAG,sBAAsB,CAUzF"}
|