@stoneforge/quarry 0.1.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/LICENSE +13 -0
- package/README.md +160 -0
- package/dist/api/index.d.ts +8 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/index.js +8 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/quarry-api.d.ts +268 -0
- package/dist/api/quarry-api.d.ts.map +1 -0
- package/dist/api/quarry-api.js +3905 -0
- package/dist/api/quarry-api.js.map +1 -0
- package/dist/api/types.d.ts +1359 -0
- package/dist/api/types.d.ts.map +1 -0
- package/dist/api/types.js +204 -0
- package/dist/api/types.js.map +1 -0
- package/dist/bin/sf.d.ts +3 -0
- package/dist/bin/sf.d.ts.map +1 -0
- package/dist/bin/sf.js +9 -0
- package/dist/bin/sf.js.map +1 -0
- package/dist/cli/commands/admin.d.ts +11 -0
- package/dist/cli/commands/admin.d.ts.map +1 -0
- package/dist/cli/commands/admin.js +465 -0
- package/dist/cli/commands/admin.js.map +1 -0
- package/dist/cli/commands/alias.d.ts +8 -0
- package/dist/cli/commands/alias.d.ts.map +1 -0
- package/dist/cli/commands/alias.js +70 -0
- package/dist/cli/commands/alias.js.map +1 -0
- package/dist/cli/commands/channel.d.ts +13 -0
- package/dist/cli/commands/channel.d.ts.map +1 -0
- package/dist/cli/commands/channel.js +680 -0
- package/dist/cli/commands/channel.js.map +1 -0
- package/dist/cli/commands/completion.d.ts +8 -0
- package/dist/cli/commands/completion.d.ts.map +1 -0
- package/dist/cli/commands/completion.js +87 -0
- package/dist/cli/commands/completion.js.map +1 -0
- package/dist/cli/commands/config.d.ts +12 -0
- package/dist/cli/commands/config.d.ts.map +1 -0
- package/dist/cli/commands/config.js +242 -0
- package/dist/cli/commands/config.js.map +1 -0
- package/dist/cli/commands/crud.d.ts +64 -0
- package/dist/cli/commands/crud.d.ts.map +1 -0
- package/dist/cli/commands/crud.js +805 -0
- package/dist/cli/commands/crud.js.map +1 -0
- package/dist/cli/commands/dep.d.ts +16 -0
- package/dist/cli/commands/dep.d.ts.map +1 -0
- package/dist/cli/commands/dep.js +499 -0
- package/dist/cli/commands/dep.js.map +1 -0
- package/dist/cli/commands/document.d.ts +12 -0
- package/dist/cli/commands/document.d.ts.map +1 -0
- package/dist/cli/commands/document.js +1039 -0
- package/dist/cli/commands/document.js.map +1 -0
- package/dist/cli/commands/embeddings.d.ts +12 -0
- package/dist/cli/commands/embeddings.d.ts.map +1 -0
- package/dist/cli/commands/embeddings.js +273 -0
- package/dist/cli/commands/embeddings.js.map +1 -0
- package/dist/cli/commands/entity.d.ts +16 -0
- package/dist/cli/commands/entity.d.ts.map +1 -0
- package/dist/cli/commands/entity.js +522 -0
- package/dist/cli/commands/entity.js.map +1 -0
- package/dist/cli/commands/gc.d.ts +10 -0
- package/dist/cli/commands/gc.d.ts.map +1 -0
- package/dist/cli/commands/gc.js +257 -0
- package/dist/cli/commands/gc.js.map +1 -0
- package/dist/cli/commands/help.d.ts +11 -0
- package/dist/cli/commands/help.d.ts.map +1 -0
- package/dist/cli/commands/help.js +169 -0
- package/dist/cli/commands/help.js.map +1 -0
- package/dist/cli/commands/history.d.ts +9 -0
- package/dist/cli/commands/history.d.ts.map +1 -0
- package/dist/cli/commands/history.js +160 -0
- package/dist/cli/commands/history.js.map +1 -0
- package/dist/cli/commands/identity.d.ts +18 -0
- package/dist/cli/commands/identity.d.ts.map +1 -0
- package/dist/cli/commands/identity.js +698 -0
- package/dist/cli/commands/identity.js.map +1 -0
- package/dist/cli/commands/inbox.d.ts +20 -0
- package/dist/cli/commands/inbox.d.ts.map +1 -0
- package/dist/cli/commands/inbox.js +493 -0
- package/dist/cli/commands/inbox.js.map +1 -0
- package/dist/cli/commands/init.d.ts +20 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +144 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/install.d.ts +9 -0
- package/dist/cli/commands/install.d.ts.map +1 -0
- package/dist/cli/commands/install.js +200 -0
- package/dist/cli/commands/install.js.map +1 -0
- package/dist/cli/commands/library.d.ts +12 -0
- package/dist/cli/commands/library.d.ts.map +1 -0
- package/dist/cli/commands/library.js +665 -0
- package/dist/cli/commands/library.js.map +1 -0
- package/dist/cli/commands/message.d.ts +11 -0
- package/dist/cli/commands/message.d.ts.map +1 -0
- package/dist/cli/commands/message.js +608 -0
- package/dist/cli/commands/message.js.map +1 -0
- package/dist/cli/commands/plan.d.ts +17 -0
- package/dist/cli/commands/plan.d.ts.map +1 -0
- package/dist/cli/commands/plan.js +698 -0
- package/dist/cli/commands/plan.js.map +1 -0
- package/dist/cli/commands/playbook.d.ts +12 -0
- package/dist/cli/commands/playbook.d.ts.map +1 -0
- package/dist/cli/commands/playbook.js +730 -0
- package/dist/cli/commands/playbook.js.map +1 -0
- package/dist/cli/commands/reset.d.ts +12 -0
- package/dist/cli/commands/reset.d.ts.map +1 -0
- package/dist/cli/commands/reset.js +306 -0
- package/dist/cli/commands/reset.js.map +1 -0
- package/dist/cli/commands/serve.d.ts +11 -0
- package/dist/cli/commands/serve.d.ts.map +1 -0
- package/dist/cli/commands/serve.js +106 -0
- package/dist/cli/commands/serve.js.map +1 -0
- package/dist/cli/commands/stats.d.ts +8 -0
- package/dist/cli/commands/stats.d.ts.map +1 -0
- package/dist/cli/commands/stats.js +82 -0
- package/dist/cli/commands/stats.js.map +1 -0
- package/dist/cli/commands/sync.d.ts +14 -0
- package/dist/cli/commands/sync.d.ts.map +1 -0
- package/dist/cli/commands/sync.js +370 -0
- package/dist/cli/commands/sync.js.map +1 -0
- package/dist/cli/commands/task.d.ts +25 -0
- package/dist/cli/commands/task.d.ts.map +1 -0
- package/dist/cli/commands/task.js +1153 -0
- package/dist/cli/commands/task.js.map +1 -0
- package/dist/cli/commands/team.d.ts +13 -0
- package/dist/cli/commands/team.d.ts.map +1 -0
- package/dist/cli/commands/team.js +471 -0
- package/dist/cli/commands/team.js.map +1 -0
- package/dist/cli/commands/workflow.d.ts +16 -0
- package/dist/cli/commands/workflow.d.ts.map +1 -0
- package/dist/cli/commands/workflow.js +753 -0
- package/dist/cli/commands/workflow.js.map +1 -0
- package/dist/cli/completion.d.ts +28 -0
- package/dist/cli/completion.d.ts.map +1 -0
- package/dist/cli/completion.js +295 -0
- package/dist/cli/completion.js.map +1 -0
- package/dist/cli/db.d.ts +38 -0
- package/dist/cli/db.d.ts.map +1 -0
- package/dist/cli/db.js +90 -0
- package/dist/cli/db.js.map +1 -0
- package/dist/cli/formatter.d.ts +87 -0
- package/dist/cli/formatter.d.ts.map +1 -0
- package/dist/cli/formatter.js +464 -0
- package/dist/cli/formatter.js.map +1 -0
- package/dist/cli/index.d.ts +33 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +38 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/parser.d.ts +45 -0
- package/dist/cli/parser.d.ts.map +1 -0
- package/dist/cli/parser.js +256 -0
- package/dist/cli/parser.js.map +1 -0
- package/dist/cli/plugin-loader.d.ts +39 -0
- package/dist/cli/plugin-loader.d.ts.map +1 -0
- package/dist/cli/plugin-loader.js +165 -0
- package/dist/cli/plugin-loader.js.map +1 -0
- package/dist/cli/plugin-registry.d.ts +50 -0
- package/dist/cli/plugin-registry.d.ts.map +1 -0
- package/dist/cli/plugin-registry.js +206 -0
- package/dist/cli/plugin-registry.js.map +1 -0
- package/dist/cli/plugin-types.d.ts +106 -0
- package/dist/cli/plugin-types.d.ts.map +1 -0
- package/dist/cli/plugin-types.js +103 -0
- package/dist/cli/plugin-types.js.map +1 -0
- package/dist/cli/runner.d.ts +35 -0
- package/dist/cli/runner.d.ts.map +1 -0
- package/dist/cli/runner.js +340 -0
- package/dist/cli/runner.js.map +1 -0
- package/dist/cli/suggest.d.ts +15 -0
- package/dist/cli/suggest.d.ts.map +1 -0
- package/dist/cli/suggest.js +49 -0
- package/dist/cli/suggest.js.map +1 -0
- package/dist/cli/types.d.ts +138 -0
- package/dist/cli/types.d.ts.map +1 -0
- package/dist/cli/types.js +63 -0
- package/dist/cli/types.js.map +1 -0
- package/dist/config/config.d.ts +86 -0
- package/dist/config/config.d.ts.map +1 -0
- package/dist/config/config.js +348 -0
- package/dist/config/config.js.map +1 -0
- package/dist/config/defaults.d.ts +66 -0
- package/dist/config/defaults.d.ts.map +1 -0
- package/dist/config/defaults.js +114 -0
- package/dist/config/defaults.js.map +1 -0
- package/dist/config/duration.d.ts +75 -0
- package/dist/config/duration.d.ts.map +1 -0
- package/dist/config/duration.js +190 -0
- package/dist/config/duration.js.map +1 -0
- package/dist/config/env.d.ts +67 -0
- package/dist/config/env.d.ts.map +1 -0
- package/dist/config/env.js +207 -0
- package/dist/config/env.js.map +1 -0
- package/dist/config/file.d.ts +97 -0
- package/dist/config/file.d.ts.map +1 -0
- package/dist/config/file.js +365 -0
- package/dist/config/file.js.map +1 -0
- package/dist/config/index.d.ts +35 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +41 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/merge.d.ts +53 -0
- package/dist/config/merge.d.ts.map +1 -0
- package/dist/config/merge.js +226 -0
- package/dist/config/merge.js.map +1 -0
- package/dist/config/types.d.ts +257 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +72 -0
- package/dist/config/types.js.map +1 -0
- package/dist/config/validation.d.ts +55 -0
- package/dist/config/validation.d.ts.map +1 -0
- package/dist/config/validation.js +251 -0
- package/dist/config/validation.js.map +1 -0
- package/dist/http/index.d.ts +8 -0
- package/dist/http/index.d.ts.map +1 -0
- package/dist/http/index.js +12 -0
- package/dist/http/index.js.map +1 -0
- package/dist/http/sync-handlers.d.ts +162 -0
- package/dist/http/sync-handlers.d.ts.map +1 -0
- package/dist/http/sync-handlers.js +271 -0
- package/dist/http/sync-handlers.js.map +1 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +69 -0
- package/dist/index.js.map +1 -0
- package/dist/server/index.d.ts +34 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +3329 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/static.d.ts +18 -0
- package/dist/server/static.d.ts.map +1 -0
- package/dist/server/static.js +71 -0
- package/dist/server/static.js.map +1 -0
- package/dist/server/ws/broadcaster.d.ts +8 -0
- package/dist/server/ws/broadcaster.d.ts.map +1 -0
- package/dist/server/ws/broadcaster.js +7 -0
- package/dist/server/ws/broadcaster.js.map +1 -0
- package/dist/server/ws/handler.d.ts +55 -0
- package/dist/server/ws/handler.d.ts.map +1 -0
- package/dist/server/ws/handler.js +160 -0
- package/dist/server/ws/handler.js.map +1 -0
- package/dist/services/blocked-cache.d.ts +297 -0
- package/dist/services/blocked-cache.d.ts.map +1 -0
- package/dist/services/blocked-cache.js +755 -0
- package/dist/services/blocked-cache.js.map +1 -0
- package/dist/services/dependency.d.ts +205 -0
- package/dist/services/dependency.d.ts.map +1 -0
- package/dist/services/dependency.js +566 -0
- package/dist/services/dependency.js.map +1 -0
- package/dist/services/embeddings/fusion.d.ts +33 -0
- package/dist/services/embeddings/fusion.d.ts.map +1 -0
- package/dist/services/embeddings/fusion.js +34 -0
- package/dist/services/embeddings/fusion.js.map +1 -0
- package/dist/services/embeddings/index.d.ts +12 -0
- package/dist/services/embeddings/index.d.ts.map +1 -0
- package/dist/services/embeddings/index.js +10 -0
- package/dist/services/embeddings/index.js.map +1 -0
- package/dist/services/embeddings/local-provider.d.ts +31 -0
- package/dist/services/embeddings/local-provider.d.ts.map +1 -0
- package/dist/services/embeddings/local-provider.js +80 -0
- package/dist/services/embeddings/local-provider.js.map +1 -0
- package/dist/services/embeddings/service.d.ts +76 -0
- package/dist/services/embeddings/service.d.ts.map +1 -0
- package/dist/services/embeddings/service.js +153 -0
- package/dist/services/embeddings/service.js.map +1 -0
- package/dist/services/embeddings/types.d.ts +70 -0
- package/dist/services/embeddings/types.d.ts.map +1 -0
- package/dist/services/embeddings/types.js +8 -0
- package/dist/services/embeddings/types.js.map +1 -0
- package/dist/services/id-length-cache.d.ts +156 -0
- package/dist/services/id-length-cache.d.ts.map +1 -0
- package/dist/services/id-length-cache.js +197 -0
- package/dist/services/id-length-cache.js.map +1 -0
- package/dist/services/inbox.d.ts +147 -0
- package/dist/services/inbox.d.ts.map +1 -0
- package/dist/services/inbox.js +428 -0
- package/dist/services/inbox.js.map +1 -0
- package/dist/services/index.d.ts +10 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +10 -0
- package/dist/services/index.js.map +1 -0
- package/dist/services/priority-service.d.ts +145 -0
- package/dist/services/priority-service.d.ts.map +1 -0
- package/dist/services/priority-service.js +272 -0
- package/dist/services/priority-service.js.map +1 -0
- package/dist/services/search-utils.d.ts +47 -0
- package/dist/services/search-utils.d.ts.map +1 -0
- package/dist/services/search-utils.js +83 -0
- package/dist/services/search-utils.js.map +1 -0
- package/dist/sync/hash.d.ts +48 -0
- package/dist/sync/hash.d.ts.map +1 -0
- package/dist/sync/hash.js +136 -0
- package/dist/sync/hash.js.map +1 -0
- package/dist/sync/index.d.ts +11 -0
- package/dist/sync/index.d.ts.map +1 -0
- package/dist/sync/index.js +16 -0
- package/dist/sync/index.js.map +1 -0
- package/dist/sync/merge.d.ts +80 -0
- package/dist/sync/merge.d.ts.map +1 -0
- package/dist/sync/merge.js +310 -0
- package/dist/sync/merge.js.map +1 -0
- package/dist/sync/serialization.d.ts +132 -0
- package/dist/sync/serialization.d.ts.map +1 -0
- package/dist/sync/serialization.js +306 -0
- package/dist/sync/serialization.js.map +1 -0
- package/dist/sync/service.d.ts +102 -0
- package/dist/sync/service.d.ts.map +1 -0
- package/dist/sync/service.js +493 -0
- package/dist/sync/service.js.map +1 -0
- package/dist/sync/types.d.ts +275 -0
- package/dist/sync/types.d.ts.map +1 -0
- package/dist/sync/types.js +76 -0
- package/dist/sync/types.js.map +1 -0
- package/dist/systems/identity.d.ts +479 -0
- package/dist/systems/identity.d.ts.map +1 -0
- package/dist/systems/identity.js +817 -0
- package/dist/systems/identity.js.map +1 -0
- package/dist/systems/index.d.ts +8 -0
- package/dist/systems/index.d.ts.map +1 -0
- package/dist/systems/index.js +29 -0
- package/dist/systems/index.js.map +1 -0
- package/package.json +121 -0
- package/web/assets/charts-vendor-D1YcbGux.js +55 -0
- package/web/assets/dnd-vendor-DmxE-_ZH.js +5 -0
- package/web/assets/editor-vendor-BxraAWts.js +279 -0
- package/web/assets/index-B77vv208.js +341 -0
- package/web/assets/index-CF_XnVLh.css +1 -0
- package/web/assets/router-vendor-BCKpRBrB.js +41 -0
- package/web/assets/ui-vendor-DUahGnbT.js +45 -0
- package/web/assets/utils-vendor-CfYKiENT.js +813 -0
- package/web/favicon.ico +0 -0
- package/web/index.html +23 -0
- package/web/logo.png +0 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Merge Strategy - Conflict resolution for sync operations
|
|
3
|
+
*
|
|
4
|
+
* Implements Last-Write-Wins (LWW) strategy with special handling for:
|
|
5
|
+
* - Tombstones (soft deletes)
|
|
6
|
+
* - Status fields (closed wins over open)
|
|
7
|
+
* - Tags (set union merge)
|
|
8
|
+
* - Dependencies (removal is authoritative)
|
|
9
|
+
*/
|
|
10
|
+
import type { Element, Dependency } from '@stoneforge/core';
|
|
11
|
+
import { MergeResolution, TombstoneStatus, type ConflictRecord, type DependencyConflictRecord } from './types.js';
|
|
12
|
+
/**
|
|
13
|
+
* Merge result for elements
|
|
14
|
+
*/
|
|
15
|
+
export interface ElementMergeResult {
|
|
16
|
+
/** The winning element */
|
|
17
|
+
element: Element;
|
|
18
|
+
/** How the merge was resolved */
|
|
19
|
+
resolution: MergeResolution;
|
|
20
|
+
/** Whether local element was modified */
|
|
21
|
+
localModified: boolean;
|
|
22
|
+
/** Conflict record if there was a conflict */
|
|
23
|
+
conflict?: ConflictRecord;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Merge two versions of an element using Last-Write-Wins strategy
|
|
27
|
+
*
|
|
28
|
+
* Special cases:
|
|
29
|
+
* - Identical content hash: Skip (no conflict)
|
|
30
|
+
* - Tombstone handling: Fresh tombstone wins over live
|
|
31
|
+
* - Status merge: Closed wins over open states
|
|
32
|
+
* - Tags merge: Union of both tag sets
|
|
33
|
+
*
|
|
34
|
+
* @param local - Local element
|
|
35
|
+
* @param remote - Remote element (from import)
|
|
36
|
+
* @param tombstoneTtl - Tombstone TTL in milliseconds (default: 30 days)
|
|
37
|
+
* @returns Merge result
|
|
38
|
+
*/
|
|
39
|
+
export declare function mergeElements(local: Element, remote: Element, tombstoneTtl?: number): ElementMergeResult;
|
|
40
|
+
/**
|
|
41
|
+
* Get tombstone status for an element
|
|
42
|
+
*/
|
|
43
|
+
export declare function getTombstoneStatus(element: Element, ttlMs: number): TombstoneStatus;
|
|
44
|
+
/**
|
|
45
|
+
* Merge two tag arrays using set union
|
|
46
|
+
* Never loses a tag in merge
|
|
47
|
+
*
|
|
48
|
+
* @param localTags - Local tags
|
|
49
|
+
* @param remoteTags - Remote tags
|
|
50
|
+
* @returns Merged tags (sorted for determinism)
|
|
51
|
+
*/
|
|
52
|
+
export declare function mergeTags(localTags: string[], remoteTags: string[]): string[];
|
|
53
|
+
/**
|
|
54
|
+
* Dependency merge result
|
|
55
|
+
*/
|
|
56
|
+
export interface DependencyMergeResult {
|
|
57
|
+
/** Dependencies to keep */
|
|
58
|
+
keep: Dependency[];
|
|
59
|
+
/** Dependencies that were added */
|
|
60
|
+
added: Dependency[];
|
|
61
|
+
/** Dependencies that were removed */
|
|
62
|
+
removed: Dependency[];
|
|
63
|
+
/** Conflict records */
|
|
64
|
+
conflicts: DependencyConflictRecord[];
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Merge dependencies between local and remote
|
|
68
|
+
*
|
|
69
|
+
* Rules:
|
|
70
|
+
* - Removal is authoritative (if one side removed, it's removed)
|
|
71
|
+
* - Additions from both sides kept
|
|
72
|
+
* - No duplicate dependencies
|
|
73
|
+
*
|
|
74
|
+
* @param localDeps - Local dependencies
|
|
75
|
+
* @param remoteDeps - Remote dependencies
|
|
76
|
+
* @param originalDeps - Original dependencies (baseline for detecting removals)
|
|
77
|
+
* @returns Merge result
|
|
78
|
+
*/
|
|
79
|
+
export declare function mergeDependencies(localDeps: Dependency[], remoteDeps: Dependency[], originalDeps?: Dependency[]): DependencyMergeResult;
|
|
80
|
+
//# sourceMappingURL=merge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge.d.ts","sourceRoot":"","sources":["../../src/sync/merge.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAkB,MAAM,kBAAkB,CAAC;AAE5E,OAAO,EACL,eAAe,EACf,eAAe,EACf,KAAK,cAAc,EACnB,KAAK,wBAAwB,EAC9B,MAAM,YAAY,CAAC;AAOpB;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,0BAA0B;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,iCAAiC;IACjC,UAAU,EAAE,eAAe,CAAC;IAC5B,yCAAyC;IACzC,aAAa,EAAE,OAAO,CAAC;IACvB,8CAA8C;IAC9C,QAAQ,CAAC,EAAE,cAAc,CAAC;CAC3B;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,OAAO,EACd,MAAM,EAAE,OAAO,EACf,YAAY,GAAE,MAAiC,GAC9C,kBAAkB,CAgDpB;AA0DD;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,eAAe,CAenF;AA2ED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAG7E;AAMD;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,2BAA2B;IAC3B,IAAI,EAAE,UAAU,EAAE,CAAC;IACnB,mCAAmC;IACnC,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,qCAAqC;IACrC,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,uBAAuB;IACvB,SAAS,EAAE,wBAAwB,EAAE,CAAC;CACvC;AASD;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,UAAU,EAAE,EACvB,UAAU,EAAE,UAAU,EAAE,EACxB,YAAY,GAAE,UAAU,EAAO,GAC9B,qBAAqB,CAkEvB"}
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Merge Strategy - Conflict resolution for sync operations
|
|
3
|
+
*
|
|
4
|
+
* Implements Last-Write-Wins (LWW) strategy with special handling for:
|
|
5
|
+
* - Tombstones (soft deletes)
|
|
6
|
+
* - Status fields (closed wins over open)
|
|
7
|
+
* - Tags (set union merge)
|
|
8
|
+
* - Dependencies (removal is authoritative)
|
|
9
|
+
*/
|
|
10
|
+
import { createTimestamp } from '@stoneforge/core';
|
|
11
|
+
import { MergeResolution, TombstoneStatus, } from './types.js';
|
|
12
|
+
import { computeContentHashSync } from './hash.js';
|
|
13
|
+
/**
|
|
14
|
+
* Merge two versions of an element using Last-Write-Wins strategy
|
|
15
|
+
*
|
|
16
|
+
* Special cases:
|
|
17
|
+
* - Identical content hash: Skip (no conflict)
|
|
18
|
+
* - Tombstone handling: Fresh tombstone wins over live
|
|
19
|
+
* - Status merge: Closed wins over open states
|
|
20
|
+
* - Tags merge: Union of both tag sets
|
|
21
|
+
*
|
|
22
|
+
* @param local - Local element
|
|
23
|
+
* @param remote - Remote element (from import)
|
|
24
|
+
* @param tombstoneTtl - Tombstone TTL in milliseconds (default: 30 days)
|
|
25
|
+
* @returns Merge result
|
|
26
|
+
*/
|
|
27
|
+
export function mergeElements(local, remote, tombstoneTtl = 30 * 24 * 60 * 60 * 1000) {
|
|
28
|
+
// Compute content hashes
|
|
29
|
+
const localHash = computeContentHashSync(local);
|
|
30
|
+
const remoteHash = computeContentHashSync(remote);
|
|
31
|
+
// Same content - no conflict
|
|
32
|
+
if (localHash.hash === remoteHash.hash) {
|
|
33
|
+
return {
|
|
34
|
+
element: local,
|
|
35
|
+
resolution: MergeResolution.IDENTICAL,
|
|
36
|
+
localModified: false,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
// Get tombstone status for both
|
|
40
|
+
const localTombstone = getTombstoneStatus(local, tombstoneTtl);
|
|
41
|
+
const remoteTombstone = getTombstoneStatus(remote, tombstoneTtl);
|
|
42
|
+
// Apply tombstone merge rules
|
|
43
|
+
const tombstoneResolution = resolveTombstoneConflict(localTombstone, remoteTombstone);
|
|
44
|
+
if (tombstoneResolution !== null) {
|
|
45
|
+
const winner = tombstoneResolution === 'local' ? local : remote;
|
|
46
|
+
const resolution = tombstoneResolution === 'local' ? MergeResolution.LOCAL_WINS : MergeResolution.REMOTE_WINS;
|
|
47
|
+
return {
|
|
48
|
+
element: winner,
|
|
49
|
+
resolution,
|
|
50
|
+
localModified: tombstoneResolution === 'remote',
|
|
51
|
+
conflict: createConflictRecord(local, remote, localHash.hash, remoteHash.hash, resolution),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
// Both are live elements - apply LWW with special handling
|
|
55
|
+
const merged = mergeLiveElements(local, remote);
|
|
56
|
+
return {
|
|
57
|
+
element: merged.element,
|
|
58
|
+
resolution: merged.resolution,
|
|
59
|
+
localModified: merged.resolution !== MergeResolution.LOCAL_WINS,
|
|
60
|
+
conflict: createConflictRecord(local, remote, localHash.hash, remoteHash.hash, merged.resolution),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Merge two live (non-tombstone) elements
|
|
65
|
+
*/
|
|
66
|
+
function mergeLiveElements(local, remote) {
|
|
67
|
+
// Apply status merge rules first
|
|
68
|
+
const statusResolution = resolveStatusConflict(local, remote);
|
|
69
|
+
if (statusResolution !== null) {
|
|
70
|
+
return {
|
|
71
|
+
element: statusResolution === 'local' ? local : remote,
|
|
72
|
+
resolution: statusResolution === 'local' ? MergeResolution.LOCAL_WINS : MergeResolution.REMOTE_WINS,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
// LWW by updatedAt timestamp
|
|
76
|
+
const localTime = new Date(local.updatedAt).getTime();
|
|
77
|
+
const remoteTime = new Date(remote.updatedAt).getTime();
|
|
78
|
+
if (localTime >= remoteTime) {
|
|
79
|
+
// Local wins - but merge tags
|
|
80
|
+
const mergedTags = mergeTags(local.tags, remote.tags);
|
|
81
|
+
if (arraysEqual(mergedTags, local.tags)) {
|
|
82
|
+
return {
|
|
83
|
+
element: local,
|
|
84
|
+
resolution: MergeResolution.LOCAL_WINS,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
// Tags were merged
|
|
88
|
+
return {
|
|
89
|
+
element: { ...local, tags: mergedTags },
|
|
90
|
+
resolution: MergeResolution.TAGS_MERGED,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
// Remote wins - but merge tags
|
|
94
|
+
const mergedTags = mergeTags(local.tags, remote.tags);
|
|
95
|
+
if (arraysEqual(mergedTags, remote.tags)) {
|
|
96
|
+
return {
|
|
97
|
+
element: remote,
|
|
98
|
+
resolution: MergeResolution.REMOTE_WINS,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
// Tags were merged
|
|
102
|
+
return {
|
|
103
|
+
element: { ...remote, tags: mergedTags },
|
|
104
|
+
resolution: MergeResolution.TAGS_MERGED,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
// ============================================================================
|
|
108
|
+
// Tombstone Handling
|
|
109
|
+
// ============================================================================
|
|
110
|
+
/**
|
|
111
|
+
* Get tombstone status for an element
|
|
112
|
+
*/
|
|
113
|
+
export function getTombstoneStatus(element, ttlMs) {
|
|
114
|
+
// Check for deletedAt field (tombstone marker)
|
|
115
|
+
const record = element;
|
|
116
|
+
const deletedAt = record.deletedAt;
|
|
117
|
+
if (!deletedAt || typeof deletedAt !== 'string') {
|
|
118
|
+
return TombstoneStatus.LIVE;
|
|
119
|
+
}
|
|
120
|
+
// Check if within TTL
|
|
121
|
+
const deletedTime = new Date(deletedAt).getTime();
|
|
122
|
+
const now = Date.now();
|
|
123
|
+
const age = now - deletedTime;
|
|
124
|
+
return age <= ttlMs ? TombstoneStatus.FRESH : TombstoneStatus.EXPIRED;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Resolve conflict between elements with different tombstone statuses
|
|
128
|
+
*
|
|
129
|
+
* @returns 'local', 'remote', or null if both live/need further resolution
|
|
130
|
+
*/
|
|
131
|
+
function resolveTombstoneConflict(local, remote) {
|
|
132
|
+
// Both live - no tombstone conflict
|
|
133
|
+
if (local === TombstoneStatus.LIVE && remote === TombstoneStatus.LIVE) {
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
// Fresh tombstone wins over live
|
|
137
|
+
if (local === TombstoneStatus.FRESH && remote === TombstoneStatus.LIVE) {
|
|
138
|
+
return 'local';
|
|
139
|
+
}
|
|
140
|
+
if (remote === TombstoneStatus.FRESH && local === TombstoneStatus.LIVE) {
|
|
141
|
+
return 'remote';
|
|
142
|
+
}
|
|
143
|
+
// Expired tombstone loses to live
|
|
144
|
+
if (local === TombstoneStatus.EXPIRED && remote === TombstoneStatus.LIVE) {
|
|
145
|
+
return 'remote';
|
|
146
|
+
}
|
|
147
|
+
if (remote === TombstoneStatus.EXPIRED && local === TombstoneStatus.LIVE) {
|
|
148
|
+
return 'local';
|
|
149
|
+
}
|
|
150
|
+
// Both tombstones - later deletedAt wins (handled by LWW in caller)
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
// ============================================================================
|
|
154
|
+
// Status Merge
|
|
155
|
+
// ============================================================================
|
|
156
|
+
/**
|
|
157
|
+
* Resolve status conflict - closed always wins over open states
|
|
158
|
+
*
|
|
159
|
+
* @returns 'local', 'remote', or null if no status-based resolution
|
|
160
|
+
*/
|
|
161
|
+
function resolveStatusConflict(local, remote) {
|
|
162
|
+
const localStatus = local.status;
|
|
163
|
+
const remoteStatus = remote.status;
|
|
164
|
+
// Only applies to elements with status
|
|
165
|
+
if (typeof localStatus !== 'string' || typeof remoteStatus !== 'string') {
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
const closedStatuses = ['closed', 'tombstone'];
|
|
169
|
+
const localClosed = closedStatuses.includes(localStatus);
|
|
170
|
+
const remoteClosed = closedStatuses.includes(remoteStatus);
|
|
171
|
+
// Closed wins over open
|
|
172
|
+
if (localClosed && !remoteClosed) {
|
|
173
|
+
return 'local';
|
|
174
|
+
}
|
|
175
|
+
if (remoteClosed && !localClosed) {
|
|
176
|
+
return 'remote';
|
|
177
|
+
}
|
|
178
|
+
// Both same state - no status-based resolution
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
// ============================================================================
|
|
182
|
+
// Tags Merge
|
|
183
|
+
// ============================================================================
|
|
184
|
+
/**
|
|
185
|
+
* Merge two tag arrays using set union
|
|
186
|
+
* Never loses a tag in merge
|
|
187
|
+
*
|
|
188
|
+
* @param localTags - Local tags
|
|
189
|
+
* @param remoteTags - Remote tags
|
|
190
|
+
* @returns Merged tags (sorted for determinism)
|
|
191
|
+
*/
|
|
192
|
+
export function mergeTags(localTags, remoteTags) {
|
|
193
|
+
const merged = new Set([...localTags, ...remoteTags]);
|
|
194
|
+
return [...merged].sort();
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Dependency key for comparison
|
|
198
|
+
*/
|
|
199
|
+
function getDependencyKey(dep) {
|
|
200
|
+
return `${dep.blockedId}|${dep.blockerId}|${dep.type}`;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Merge dependencies between local and remote
|
|
204
|
+
*
|
|
205
|
+
* Rules:
|
|
206
|
+
* - Removal is authoritative (if one side removed, it's removed)
|
|
207
|
+
* - Additions from both sides kept
|
|
208
|
+
* - No duplicate dependencies
|
|
209
|
+
*
|
|
210
|
+
* @param localDeps - Local dependencies
|
|
211
|
+
* @param remoteDeps - Remote dependencies
|
|
212
|
+
* @param originalDeps - Original dependencies (baseline for detecting removals)
|
|
213
|
+
* @returns Merge result
|
|
214
|
+
*/
|
|
215
|
+
export function mergeDependencies(localDeps, remoteDeps, originalDeps = []) {
|
|
216
|
+
const localMap = new Map(localDeps.map((d) => [getDependencyKey(d), d]));
|
|
217
|
+
const remoteMap = new Map(remoteDeps.map((d) => [getDependencyKey(d), d]));
|
|
218
|
+
const originalMap = new Map(originalDeps.map((d) => [getDependencyKey(d), d]));
|
|
219
|
+
const keep = [];
|
|
220
|
+
const added = [];
|
|
221
|
+
const removed = [];
|
|
222
|
+
const conflicts = [];
|
|
223
|
+
// Track all keys
|
|
224
|
+
const allKeys = new Set([...localMap.keys(), ...remoteMap.keys()]);
|
|
225
|
+
for (const key of allKeys) {
|
|
226
|
+
const local = localMap.get(key);
|
|
227
|
+
const remote = remoteMap.get(key);
|
|
228
|
+
const original = originalMap.get(key);
|
|
229
|
+
if (local && remote) {
|
|
230
|
+
// Both have it - keep (prefer remote if different for consistency)
|
|
231
|
+
keep.push(remote);
|
|
232
|
+
}
|
|
233
|
+
else if (local && !remote) {
|
|
234
|
+
// Only local has it
|
|
235
|
+
if (original) {
|
|
236
|
+
// Was in original, remote removed it - honor removal
|
|
237
|
+
removed.push(local);
|
|
238
|
+
conflicts.push({
|
|
239
|
+
blockedId: local.blockedId,
|
|
240
|
+
blockerId: local.blockerId,
|
|
241
|
+
type: local.type,
|
|
242
|
+
resolution: 'removed',
|
|
243
|
+
resolvedAt: createTimestamp(),
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
// New in local - keep
|
|
248
|
+
keep.push(local);
|
|
249
|
+
added.push(local);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
else if (!local && remote) {
|
|
253
|
+
// Only remote has it
|
|
254
|
+
if (original) {
|
|
255
|
+
// Was in original, local removed it - honor removal
|
|
256
|
+
removed.push(remote);
|
|
257
|
+
conflicts.push({
|
|
258
|
+
blockedId: remote.blockedId,
|
|
259
|
+
blockerId: remote.blockerId,
|
|
260
|
+
type: remote.type,
|
|
261
|
+
resolution: 'removed',
|
|
262
|
+
resolvedAt: createTimestamp(),
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
else {
|
|
266
|
+
// New in remote - add
|
|
267
|
+
keep.push(remote);
|
|
268
|
+
added.push(remote);
|
|
269
|
+
conflicts.push({
|
|
270
|
+
blockedId: remote.blockedId,
|
|
271
|
+
blockerId: remote.blockerId,
|
|
272
|
+
type: remote.type,
|
|
273
|
+
resolution: 'added',
|
|
274
|
+
resolvedAt: createTimestamp(),
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
return { keep, added, removed, conflicts };
|
|
280
|
+
}
|
|
281
|
+
// ============================================================================
|
|
282
|
+
// Helpers
|
|
283
|
+
// ============================================================================
|
|
284
|
+
/**
|
|
285
|
+
* Create a conflict record
|
|
286
|
+
*/
|
|
287
|
+
function createConflictRecord(local, remote, localHash, remoteHash, resolution) {
|
|
288
|
+
return {
|
|
289
|
+
elementId: local.id,
|
|
290
|
+
localHash,
|
|
291
|
+
remoteHash,
|
|
292
|
+
resolution,
|
|
293
|
+
localUpdatedAt: local.updatedAt,
|
|
294
|
+
remoteUpdatedAt: remote.updatedAt,
|
|
295
|
+
resolvedAt: createTimestamp(),
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Check if two string arrays are equal (order-sensitive)
|
|
300
|
+
*/
|
|
301
|
+
function arraysEqual(a, b) {
|
|
302
|
+
if (a.length !== b.length)
|
|
303
|
+
return false;
|
|
304
|
+
for (let i = 0; i < a.length; i++) {
|
|
305
|
+
if (a[i] !== b[i])
|
|
306
|
+
return false;
|
|
307
|
+
}
|
|
308
|
+
return true;
|
|
309
|
+
}
|
|
310
|
+
//# sourceMappingURL=merge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge.js","sourceRoot":"","sources":["../../src/sync/merge.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EACL,eAAe,EACf,eAAe,GAGhB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAC;AAoBnD;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,aAAa,CAC3B,KAAc,EACd,MAAe,EACf,eAAuB,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI;IAE/C,yBAAyB;IACzB,MAAM,SAAS,GAAG,sBAAsB,CAAC,KAAK,CAAC,CAAC;IAChD,MAAM,UAAU,GAAG,sBAAsB,CAAC,MAAM,CAAC,CAAC;IAElD,6BAA6B;IAC7B,IAAI,SAAS,CAAC,IAAI,KAAK,UAAU,CAAC,IAAI,EAAE,CAAC;QACvC,OAAO;YACL,OAAO,EAAE,KAAK;YACd,UAAU,EAAE,eAAe,CAAC,SAAS;YACrC,aAAa,EAAE,KAAK;SACrB,CAAC;IACJ,CAAC;IAED,gCAAgC;IAChC,MAAM,cAAc,GAAG,kBAAkB,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;IAC/D,MAAM,eAAe,GAAG,kBAAkB,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IAEjE,8BAA8B;IAC9B,MAAM,mBAAmB,GAAG,wBAAwB,CAAC,cAAc,EAAE,eAAe,CAAC,CAAC;IACtF,IAAI,mBAAmB,KAAK,IAAI,EAAE,CAAC;QACjC,MAAM,MAAM,GAAG,mBAAmB,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;QAChE,MAAM,UAAU,GACd,mBAAmB,KAAK,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC,CAAC,eAAe,CAAC,WAAW,CAAC;QAE7F,OAAO;YACL,OAAO,EAAE,MAAM;YACf,UAAU;YACV,aAAa,EAAE,mBAAmB,KAAK,QAAQ;YAC/C,QAAQ,EAAE,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,CAAC,IAAI,EAAE,UAAU,CAAC,IAAI,EAAE,UAAU,CAAC;SAC3F,CAAC;IACJ,CAAC;IAED,2DAA2D;IAC3D,MAAM,MAAM,GAAG,iBAAiB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IAEhD,OAAO;QACL,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,aAAa,EAAE,MAAM,CAAC,UAAU,KAAK,eAAe,CAAC,UAAU;QAC/D,QAAQ,EAAE,oBAAoB,CAC5B,KAAK,EACL,MAAM,EACN,SAAS,CAAC,IAAI,EACd,UAAU,CAAC,IAAI,EACf,MAAM,CAAC,UAAU,CAClB;KACF,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAS,iBAAiB,CACxB,KAAc,EACd,MAAe;IAEf,iCAAiC;IACjC,MAAM,gBAAgB,GAAG,qBAAqB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IAC9D,IAAI,gBAAgB,KAAK,IAAI,EAAE,CAAC;QAC9B,OAAO;YACL,OAAO,EAAE,gBAAgB,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM;YACtD,UAAU,EACR,gBAAgB,KAAK,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC,CAAC,eAAe,CAAC,WAAW;SAC1F,CAAC;IACJ,CAAC;IAED,6BAA6B;IAC7B,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,CAAC;IACtD,MAAM,UAAU,GAAG,IAAI,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,CAAC;IAExD,IAAI,SAAS,IAAI,UAAU,EAAE,CAAC;QAC5B,8BAA8B;QAC9B,MAAM,UAAU,GAAG,SAAS,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;QACtD,IAAI,WAAW,CAAC,UAAU,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YACxC,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,UAAU,EAAE,eAAe,CAAC,UAAU;aACvC,CAAC;QACJ,CAAC;QACD,mBAAmB;QACnB,OAAO;YACL,OAAO,EAAE,EAAE,GAAG,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE;YACvC,UAAU,EAAE,eAAe,CAAC,WAAW;SACxC,CAAC;IACJ,CAAC;IAED,+BAA+B;IAC/B,MAAM,UAAU,GAAG,SAAS,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;IACtD,IAAI,WAAW,CAAC,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;QACzC,OAAO;YACL,OAAO,EAAE,MAAM;YACf,UAAU,EAAE,eAAe,CAAC,WAAW;SACxC,CAAC;IACJ,CAAC;IACD,mBAAmB;IACnB,OAAO;QACL,OAAO,EAAE,EAAE,GAAG,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE;QACxC,UAAU,EAAE,eAAe,CAAC,WAAW;KACxC,CAAC;AACJ,CAAC;AAED,+EAA+E;AAC/E,qBAAqB;AACrB,+EAA+E;AAE/E;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAAgB,EAAE,KAAa;IAChE,+CAA+C;IAC/C,MAAM,MAAM,GAAG,OAA6C,CAAC;IAC7D,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC;IAEnC,IAAI,CAAC,SAAS,IAAI,OAAO,SAAS,KAAK,QAAQ,EAAE,CAAC;QAChD,OAAO,eAAe,CAAC,IAAI,CAAC;IAC9B,CAAC;IAED,sBAAsB;IACtB,MAAM,WAAW,GAAG,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,CAAC;IAClD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,MAAM,GAAG,GAAG,GAAG,GAAG,WAAW,CAAC;IAE9B,OAAO,GAAG,IAAI,KAAK,CAAC,CAAC,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC,eAAe,CAAC,OAAO,CAAC;AACxE,CAAC;AAED;;;;GAIG;AACH,SAAS,wBAAwB,CAC/B,KAAsB,EACtB,MAAuB;IAEvB,oCAAoC;IACpC,IAAI,KAAK,KAAK,eAAe,CAAC,IAAI,IAAI,MAAM,KAAK,eAAe,CAAC,IAAI,EAAE,CAAC;QACtE,OAAO,IAAI,CAAC;IACd,CAAC;IAED,iCAAiC;IACjC,IAAI,KAAK,KAAK,eAAe,CAAC,KAAK,IAAI,MAAM,KAAK,eAAe,CAAC,IAAI,EAAE,CAAC;QACvE,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,IAAI,MAAM,KAAK,eAAe,CAAC,KAAK,IAAI,KAAK,KAAK,eAAe,CAAC,IAAI,EAAE,CAAC;QACvE,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,kCAAkC;IAClC,IAAI,KAAK,KAAK,eAAe,CAAC,OAAO,IAAI,MAAM,KAAK,eAAe,CAAC,IAAI,EAAE,CAAC;QACzE,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,IAAI,MAAM,KAAK,eAAe,CAAC,OAAO,IAAI,KAAK,KAAK,eAAe,CAAC,IAAI,EAAE,CAAC;QACzE,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,oEAAoE;IACpE,OAAO,IAAI,CAAC;AACd,CAAC;AAED,+EAA+E;AAC/E,eAAe;AACf,+EAA+E;AAE/E;;;;GAIG;AACH,SAAS,qBAAqB,CAAC,KAAc,EAAE,MAAe;IAC5D,MAAM,WAAW,GAAI,KAA4C,CAAC,MAAM,CAAC;IACzE,MAAM,YAAY,GAAI,MAA6C,CAAC,MAAM,CAAC;IAE3E,uCAAuC;IACvC,IAAI,OAAO,WAAW,KAAK,QAAQ,IAAI,OAAO,YAAY,KAAK,QAAQ,EAAE,CAAC;QACxE,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,cAAc,GAAG,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;IAE/C,MAAM,WAAW,GAAG,cAAc,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IACzD,MAAM,YAAY,GAAG,cAAc,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;IAE3D,wBAAwB;IACxB,IAAI,WAAW,IAAI,CAAC,YAAY,EAAE,CAAC;QACjC,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,IAAI,YAAY,IAAI,CAAC,WAAW,EAAE,CAAC;QACjC,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,+CAA+C;IAC/C,OAAO,IAAI,CAAC;AACd,CAAC;AAED,+EAA+E;AAC/E,aAAa;AACb,+EAA+E;AAE/E;;;;;;;GAOG;AACH,MAAM,UAAU,SAAS,CAAC,SAAmB,EAAE,UAAoB;IACjE,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,SAAS,EAAE,GAAG,UAAU,CAAC,CAAC,CAAC;IACtD,OAAO,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;AAC5B,CAAC;AAoBD;;GAEG;AACH,SAAS,gBAAgB,CAAC,GAAe;IACvC,OAAO,GAAG,GAAG,CAAC,SAAS,IAAI,GAAG,CAAC,SAAS,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC;AACzD,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,iBAAiB,CAC/B,SAAuB,EACvB,UAAwB,EACxB,eAA6B,EAAE;IAE/B,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IACzE,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3E,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAE/E,MAAM,IAAI,GAAiB,EAAE,CAAC;IAC9B,MAAM,KAAK,GAAiB,EAAE,CAAC;IAC/B,MAAM,OAAO,GAAiB,EAAE,CAAC;IACjC,MAAM,SAAS,GAA+B,EAAE,CAAC;IAEjD,iBAAiB;IACjB,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,QAAQ,CAAC,IAAI,EAAE,EAAE,GAAG,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IAEnE,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;QAC1B,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAChC,MAAM,MAAM,GAAG,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAClC,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAEtC,IAAI,KAAK,IAAI,MAAM,EAAE,CAAC;YACpB,mEAAmE;YACnE,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACpB,CAAC;aAAM,IAAI,KAAK,IAAI,CAAC,MAAM,EAAE,CAAC;YAC5B,oBAAoB;YACpB,IAAI,QAAQ,EAAE,CAAC;gBACb,qDAAqD;gBACrD,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBACpB,SAAS,CAAC,IAAI,CAAC;oBACb,SAAS,EAAE,KAAK,CAAC,SAAS;oBAC1B,SAAS,EAAE,KAAK,CAAC,SAAS;oBAC1B,IAAI,EAAE,KAAK,CAAC,IAAsB;oBAClC,UAAU,EAAE,SAAS;oBACrB,UAAU,EAAE,eAAe,EAAE;iBAC9B,CAAC,CAAC;YACL,CAAC;iBAAM,CAAC;gBACN,sBAAsB;gBACtB,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBACjB,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACpB,CAAC;QACH,CAAC;aAAM,IAAI,CAAC,KAAK,IAAI,MAAM,EAAE,CAAC;YAC5B,qBAAqB;YACrB,IAAI,QAAQ,EAAE,CAAC;gBACb,oDAAoD;gBACpD,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBACrB,SAAS,CAAC,IAAI,CAAC;oBACb,SAAS,EAAE,MAAM,CAAC,SAAS;oBAC3B,SAAS,EAAE,MAAM,CAAC,SAAS;oBAC3B,IAAI,EAAE,MAAM,CAAC,IAAsB;oBACnC,UAAU,EAAE,SAAS;oBACrB,UAAU,EAAE,eAAe,EAAE;iBAC9B,CAAC,CAAC;YACL,CAAC;iBAAM,CAAC;gBACN,sBAAsB;gBACtB,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBAClB,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBACnB,SAAS,CAAC,IAAI,CAAC;oBACb,SAAS,EAAE,MAAM,CAAC,SAAS;oBAC3B,SAAS,EAAE,MAAM,CAAC,SAAS;oBAC3B,IAAI,EAAE,MAAM,CAAC,IAAsB;oBACnC,UAAU,EAAE,OAAO;oBACnB,UAAU,EAAE,eAAe,EAAE;iBAC9B,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;AAC7C,CAAC;AAED,+EAA+E;AAC/E,UAAU;AACV,+EAA+E;AAE/E;;GAEG;AACH,SAAS,oBAAoB,CAC3B,KAAc,EACd,MAAe,EACf,SAAiB,EACjB,UAAkB,EAClB,UAA2B;IAE3B,OAAO;QACL,SAAS,EAAE,KAAK,CAAC,EAAE;QACnB,SAAS;QACT,UAAU;QACV,UAAU;QACV,cAAc,EAAE,KAAK,CAAC,SAAS;QAC/B,eAAe,EAAE,MAAM,CAAC,SAAS;QACjC,UAAU,EAAE,eAAe,EAAE;KAC9B,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAS,WAAW,CAAC,CAAW,EAAE,CAAW;IAC3C,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IACxC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAClC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAAE,OAAO,KAAK,CAAC;IAClC,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sync Serialization - JSONL format serialization and parsing
|
|
3
|
+
*
|
|
4
|
+
* Handles conversion between Element/Dependency objects and JSONL format.
|
|
5
|
+
* All timestamps are normalized to ISO 8601 format.
|
|
6
|
+
*/
|
|
7
|
+
import type { Element, Dependency } from '@stoneforge/core';
|
|
8
|
+
import type { SerializedElement, SerializedDependency } from './types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Serialize an element to JSONL format
|
|
11
|
+
*
|
|
12
|
+
* @param element - Element to serialize
|
|
13
|
+
* @returns JSON string ready for JSONL file
|
|
14
|
+
*/
|
|
15
|
+
export declare function serializeElement(element: Element): string;
|
|
16
|
+
/**
|
|
17
|
+
* Parse a JSONL line into an element
|
|
18
|
+
*
|
|
19
|
+
* @param line - JSON string from JSONL file
|
|
20
|
+
* @returns Parsed element
|
|
21
|
+
* @throws ValidationError if parsing fails
|
|
22
|
+
*/
|
|
23
|
+
export declare function parseElement(line: string): Element;
|
|
24
|
+
/**
|
|
25
|
+
* Try to parse a JSONL line into an element
|
|
26
|
+
*
|
|
27
|
+
* @param line - JSON string from JSONL file
|
|
28
|
+
* @returns Parsed element or null if invalid
|
|
29
|
+
*/
|
|
30
|
+
export declare function tryParseElement(line: string): Element | null;
|
|
31
|
+
/**
|
|
32
|
+
* Serialize a dependency to JSONL format
|
|
33
|
+
*
|
|
34
|
+
* @param dependency - Dependency to serialize
|
|
35
|
+
* @returns JSON string ready for JSONL file
|
|
36
|
+
*/
|
|
37
|
+
export declare function serializeDependency(dependency: Dependency): string;
|
|
38
|
+
/**
|
|
39
|
+
* Parse a JSONL line into a dependency
|
|
40
|
+
*
|
|
41
|
+
* @param line - JSON string from JSONL file
|
|
42
|
+
* @returns Parsed dependency
|
|
43
|
+
* @throws ValidationError if parsing fails
|
|
44
|
+
*/
|
|
45
|
+
export declare function parseDependency(line: string): Dependency;
|
|
46
|
+
/**
|
|
47
|
+
* Try to parse a JSONL line into a dependency
|
|
48
|
+
*
|
|
49
|
+
* @param line - JSON string from JSONL file
|
|
50
|
+
* @returns Parsed dependency or null if invalid
|
|
51
|
+
*/
|
|
52
|
+
export declare function tryParseDependency(line: string): Dependency | null;
|
|
53
|
+
/**
|
|
54
|
+
* Serialize multiple elements to JSONL content
|
|
55
|
+
*
|
|
56
|
+
* @param elements - Elements to serialize
|
|
57
|
+
* @returns JSONL content (multiple lines)
|
|
58
|
+
*/
|
|
59
|
+
export declare function serializeElements(elements: Element[]): string;
|
|
60
|
+
/**
|
|
61
|
+
* Serialize multiple dependencies to JSONL content
|
|
62
|
+
*
|
|
63
|
+
* @param dependencies - Dependencies to serialize
|
|
64
|
+
* @returns JSONL content (multiple lines)
|
|
65
|
+
*/
|
|
66
|
+
export declare function serializeDependencies(dependencies: Dependency[]): string;
|
|
67
|
+
/**
|
|
68
|
+
* Parse JSONL content into elements
|
|
69
|
+
*
|
|
70
|
+
* @param content - JSONL content (multiple lines)
|
|
71
|
+
* @returns Array of parsed elements and any errors
|
|
72
|
+
*/
|
|
73
|
+
export declare function parseElements(content: string): {
|
|
74
|
+
elements: Element[];
|
|
75
|
+
errors: ParseError[];
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* Parse JSONL content into dependencies
|
|
79
|
+
*
|
|
80
|
+
* @param content - JSONL content (multiple lines)
|
|
81
|
+
* @returns Array of parsed dependencies and any errors
|
|
82
|
+
*/
|
|
83
|
+
export declare function parseDependencies(content: string): {
|
|
84
|
+
dependencies: Dependency[];
|
|
85
|
+
errors: ParseError[];
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* Parse error info
|
|
89
|
+
*/
|
|
90
|
+
export interface ParseError {
|
|
91
|
+
/** Line number (1-indexed) */
|
|
92
|
+
line: number;
|
|
93
|
+
/** Error message */
|
|
94
|
+
message: string;
|
|
95
|
+
/** Truncated line content */
|
|
96
|
+
content: string;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Sort elements for export ordering
|
|
100
|
+
*
|
|
101
|
+
* Order by:
|
|
102
|
+
* 1. Type (entities first for references)
|
|
103
|
+
* 2. Creation time
|
|
104
|
+
* 3. ID (for stability)
|
|
105
|
+
*
|
|
106
|
+
* @param elements - Elements to sort
|
|
107
|
+
* @returns Sorted elements (new array)
|
|
108
|
+
*/
|
|
109
|
+
export declare function sortElementsForExport(elements: Element[]): Element[];
|
|
110
|
+
/**
|
|
111
|
+
* Sort dependencies for export ordering
|
|
112
|
+
*
|
|
113
|
+
* Order by:
|
|
114
|
+
* 1. Creation time
|
|
115
|
+
* 2. Blocked ID
|
|
116
|
+
* 3. Blocker ID
|
|
117
|
+
* 4. Type (for stability)
|
|
118
|
+
*
|
|
119
|
+
* @param dependencies - Dependencies to sort
|
|
120
|
+
* @returns Sorted dependencies (new array)
|
|
121
|
+
*/
|
|
122
|
+
export declare function sortDependenciesForExport(dependencies: Dependency[]): Dependency[];
|
|
123
|
+
/**
|
|
124
|
+
* Check if an object looks like a serialized element
|
|
125
|
+
* (less strict than isElement, for initial parsing)
|
|
126
|
+
*/
|
|
127
|
+
export declare function isSerializedElement(value: unknown): value is SerializedElement;
|
|
128
|
+
/**
|
|
129
|
+
* Check if an object looks like a serialized dependency
|
|
130
|
+
*/
|
|
131
|
+
export declare function isSerializedDependency(value: unknown): value is SerializedDependency;
|
|
132
|
+
//# sourceMappingURL=serialization.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serialization.d.ts","sourceRoot":"","sources":["../../src/sync/serialization.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE5D,OAAO,KAAK,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAO1E;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAuBzD;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAsBlD;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CAM5D;AAMD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM,CAwBlE;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CA8BxD;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAMlE;AAMD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,MAAM,CAE7D;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,YAAY,EAAE,UAAU,EAAE,GAAG,MAAM,CAExE;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;IAAC,MAAM,EAAE,UAAU,EAAE,CAAA;CAAE,CAmB5F;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG;IAClD,YAAY,EAAE,UAAU,EAAE,CAAC;IAC3B,MAAM,EAAE,UAAU,EAAE,CAAC;CACtB,CAmBA;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,8BAA8B;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,oBAAoB;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,6BAA6B;IAC7B,OAAO,EAAE,MAAM,CAAC;CACjB;AAMD;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,OAAO,EAAE,CAapE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,yBAAyB,CAAC,YAAY,EAAE,UAAU,EAAE,GAAG,UAAU,EAAE,CAiBlF;AAMD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,iBAAiB,CAkB9E;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,oBAAoB,CAepF"}
|