@git-stunts/git-warp 11.2.1 → 11.3.3
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/bin/cli/commands/check.js +2 -2
- package/bin/cli/commands/doctor/checks.js +12 -12
- package/bin/cli/commands/doctor/index.js +2 -2
- package/bin/cli/commands/doctor/types.js +1 -1
- package/bin/cli/commands/history.js +12 -5
- package/bin/cli/commands/install-hooks.js +5 -5
- package/bin/cli/commands/materialize.js +2 -2
- package/bin/cli/commands/patch.js +142 -0
- package/bin/cli/commands/path.js +4 -4
- package/bin/cli/commands/query.js +54 -13
- package/bin/cli/commands/registry.js +4 -0
- package/bin/cli/commands/seek.js +17 -11
- package/bin/cli/commands/tree.js +230 -0
- package/bin/cli/commands/trust.js +3 -3
- package/bin/cli/commands/verify-audit.js +8 -7
- package/bin/cli/commands/view.js +6 -5
- package/bin/cli/infrastructure.js +26 -12
- package/bin/cli/shared.js +2 -2
- package/bin/cli/types.js +19 -8
- package/bin/presenters/index.js +35 -9
- package/bin/presenters/json.js +14 -12
- package/bin/presenters/text.js +155 -33
- package/index.d.ts +82 -22
- package/package.json +3 -2
- package/src/domain/WarpGraph.js +4 -1
- package/src/domain/crdt/ORSet.js +8 -8
- package/src/domain/errors/EmptyMessageError.js +2 -2
- package/src/domain/errors/ForkError.js +1 -1
- package/src/domain/errors/IndexError.js +1 -1
- package/src/domain/errors/OperationAbortedError.js +1 -1
- package/src/domain/errors/QueryError.js +1 -1
- package/src/domain/errors/SchemaUnsupportedError.js +1 -1
- package/src/domain/errors/ShardCorruptionError.js +2 -2
- package/src/domain/errors/ShardLoadError.js +2 -2
- package/src/domain/errors/ShardValidationError.js +4 -4
- package/src/domain/errors/StorageError.js +2 -2
- package/src/domain/errors/SyncError.js +1 -1
- package/src/domain/errors/TraversalError.js +1 -1
- package/src/domain/errors/TrustError.js +1 -1
- package/src/domain/errors/WarpError.js +2 -2
- package/src/domain/errors/WormholeError.js +1 -1
- package/src/domain/services/AuditReceiptService.js +6 -6
- package/src/domain/services/AuditVerifierService.js +52 -38
- package/src/domain/services/BitmapIndexBuilder.js +3 -3
- package/src/domain/services/BitmapIndexReader.js +28 -19
- package/src/domain/services/BoundaryTransitionRecord.js +18 -17
- package/src/domain/services/CheckpointSerializerV5.js +17 -16
- package/src/domain/services/CheckpointService.js +2 -2
- package/src/domain/services/CommitDagTraversalService.js +13 -13
- package/src/domain/services/DagPathFinding.js +7 -7
- package/src/domain/services/DagTopology.js +1 -1
- package/src/domain/services/DagTraversal.js +1 -1
- package/src/domain/services/HealthCheckService.js +1 -1
- package/src/domain/services/HookInstaller.js +1 -1
- package/src/domain/services/HttpSyncServer.js +92 -41
- package/src/domain/services/IndexRebuildService.js +7 -7
- package/src/domain/services/IndexStalenessChecker.js +4 -3
- package/src/domain/services/JoinReducer.js +11 -11
- package/src/domain/services/LogicalTraversal.js +1 -1
- package/src/domain/services/MessageCodecInternal.js +1 -1
- package/src/domain/services/MigrationService.js +1 -1
- package/src/domain/services/ObserverView.js +8 -8
- package/src/domain/services/PatchBuilderV2.js +42 -26
- package/src/domain/services/ProvenanceIndex.js +1 -1
- package/src/domain/services/ProvenancePayload.js +1 -1
- package/src/domain/services/QueryBuilder.js +3 -3
- package/src/domain/services/StateDiff.js +14 -11
- package/src/domain/services/StateSerializerV5.js +2 -2
- package/src/domain/services/StreamingBitmapIndexBuilder.js +26 -24
- package/src/domain/services/SyncAuthService.js +3 -2
- package/src/domain/services/SyncProtocol.js +25 -11
- package/src/domain/services/TemporalQuery.js +9 -6
- package/src/domain/services/TranslationCost.js +7 -5
- package/src/domain/services/WormholeService.js +16 -7
- package/src/domain/trust/TrustCanonical.js +3 -3
- package/src/domain/trust/TrustEvaluator.js +18 -3
- package/src/domain/trust/TrustRecordService.js +30 -23
- package/src/domain/trust/TrustStateBuilder.js +21 -8
- package/src/domain/trust/canonical.js +6 -6
- package/src/domain/types/TickReceipt.js +1 -1
- package/src/domain/types/WarpErrors.js +45 -0
- package/src/domain/types/WarpOptions.js +29 -0
- package/src/domain/types/WarpPersistence.js +41 -0
- package/src/domain/types/WarpTypes.js +2 -2
- package/src/domain/types/WarpTypesV2.js +2 -2
- package/src/domain/utils/MinHeap.js +6 -5
- package/src/domain/utils/canonicalStringify.js +5 -4
- package/src/domain/utils/roaring.js +31 -5
- package/src/domain/warp/PatchSession.js +9 -18
- package/src/domain/warp/_wiredMethods.d.ts +199 -45
- package/src/domain/warp/checkpoint.methods.js +5 -1
- package/src/domain/warp/fork.methods.js +2 -2
- package/src/domain/warp/materialize.methods.js +55 -5
- package/src/domain/warp/materializeAdvanced.methods.js +15 -4
- package/src/domain/warp/patch.methods.js +54 -29
- package/src/domain/warp/provenance.methods.js +5 -3
- package/src/domain/warp/query.methods.js +6 -5
- package/src/domain/warp/sync.methods.js +16 -11
- package/src/globals.d.ts +64 -0
- package/src/infrastructure/adapters/BunHttpAdapter.js +14 -9
- package/src/infrastructure/adapters/CasSeekCacheAdapter.js +9 -4
- package/src/infrastructure/adapters/DenoHttpAdapter.js +5 -6
- package/src/infrastructure/adapters/GitGraphAdapter.js +14 -12
- package/src/infrastructure/adapters/NodeHttpAdapter.js +2 -2
- package/src/infrastructure/adapters/WebCryptoAdapter.js +2 -2
- package/src/visualization/layouts/converters.js +2 -2
- package/src/visualization/layouts/elkAdapter.js +1 -1
- package/src/visualization/layouts/elkLayout.js +10 -7
- package/src/visualization/layouts/index.js +1 -1
- package/src/visualization/renderers/ascii/seek.js +16 -6
- package/src/visualization/renderers/svg/index.js +1 -1
|
@@ -30,9 +30,35 @@
|
|
|
30
30
|
*/
|
|
31
31
|
const NOT_CHECKED = Symbol('NOT_CHECKED');
|
|
32
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Shape of the lazily-loaded roaring module after ESM/CJS unwrapping.
|
|
35
|
+
* Uses Function instead of typeof import('roaring').RoaringBitmap32 to avoid
|
|
36
|
+
* duplicate import() references that crash Deno's JSR rewriter (deno_ast
|
|
37
|
+
* overlapping TextChange bug). The single import() reference lives on
|
|
38
|
+
* getRoaringBitmap32()'s @returns tag.
|
|
39
|
+
* @typedef {Object} RoaringModule
|
|
40
|
+
* @property {Function & { isNativelyInstalled?: () => boolean }} RoaringBitmap32
|
|
41
|
+
* @property {{ RoaringBitmap32: Function }} [default]
|
|
42
|
+
* @property {boolean} [isNativelyInstalled]
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Minimum structural contract of a RoaringBitmap32 as used by bitmap index code.
|
|
47
|
+
* Named "Subset" because it only covers methods actually called by index builders/readers.
|
|
48
|
+
* Using import('roaring').RoaringBitmap32 directly fails under checkJs + skipLibCheck
|
|
49
|
+
* because tsc doesn't fully resolve inherited methods from ReadonlyRoaringBitmap32.
|
|
50
|
+
* @typedef {Object} RoaringBitmapSubset
|
|
51
|
+
* @property {number} size
|
|
52
|
+
* @property {function(number): void} add
|
|
53
|
+
* @property {function(number): boolean} has
|
|
54
|
+
* @property {function(Iterable<number>): void} orInPlace
|
|
55
|
+
* @property {function(boolean): Uint8Array} serialize
|
|
56
|
+
* @property {function(): number[]} toArray
|
|
57
|
+
*/
|
|
58
|
+
|
|
33
59
|
/**
|
|
34
60
|
* Cached reference to the loaded roaring module.
|
|
35
|
-
* @type {
|
|
61
|
+
* @type {RoaringModule | null}
|
|
36
62
|
* @private
|
|
37
63
|
*/
|
|
38
64
|
let roaringModule = null;
|
|
@@ -51,7 +77,7 @@ let nativeAvailability = NOT_CHECKED;
|
|
|
51
77
|
* Uses a top-level-await-friendly pattern with dynamic import.
|
|
52
78
|
* The module is cached after first load.
|
|
53
79
|
*
|
|
54
|
-
* @returns {
|
|
80
|
+
* @returns {RoaringModule} The roaring module exports
|
|
55
81
|
* @throws {Error} If the roaring package is not installed or fails to load
|
|
56
82
|
* @private
|
|
57
83
|
*/
|
|
@@ -67,7 +93,7 @@ function loadRoaring() {
|
|
|
67
93
|
* This is called automatically via top-level await when the module is imported,
|
|
68
94
|
* but can also be called manually with a pre-loaded module for testing.
|
|
69
95
|
*
|
|
70
|
-
* @param {
|
|
96
|
+
* @param {RoaringModule} [mod] - Pre-loaded roaring module (for testing/DI)
|
|
71
97
|
* @returns {Promise<void>}
|
|
72
98
|
*/
|
|
73
99
|
export async function initRoaring(mod) {
|
|
@@ -76,7 +102,7 @@ export async function initRoaring(mod) {
|
|
|
76
102
|
return;
|
|
77
103
|
}
|
|
78
104
|
if (!roaringModule) {
|
|
79
|
-
roaringModule = await import('roaring');
|
|
105
|
+
roaringModule = /** @type {RoaringModule} */ (await import('roaring'));
|
|
80
106
|
// Handle both ESM default export and CJS module.exports
|
|
81
107
|
if (roaringModule.default && roaringModule.default.RoaringBitmap32) {
|
|
82
108
|
roaringModule = roaringModule.default;
|
|
@@ -116,7 +142,7 @@ try {
|
|
|
116
142
|
* const intersection = RoaringBitmap32.and(a, b); // [2, 3]
|
|
117
143
|
*/
|
|
118
144
|
export function getRoaringBitmap32() {
|
|
119
|
-
return loadRoaring().RoaringBitmap32;
|
|
145
|
+
return /** @type {typeof import('roaring').RoaringBitmap32} */ (loadRoaring().RoaringBitmap32);
|
|
120
146
|
}
|
|
121
147
|
|
|
122
148
|
/**
|
|
@@ -120,7 +120,7 @@ export class PatchSession {
|
|
|
120
120
|
*
|
|
121
121
|
* @param {string} nodeId - The node ID
|
|
122
122
|
* @param {string} key - Property key
|
|
123
|
-
* @param {
|
|
123
|
+
* @param {unknown} value - Property value (must be JSON-serializable)
|
|
124
124
|
* @returns {this} This session for chaining
|
|
125
125
|
* @throws {Error} If this session has already been committed
|
|
126
126
|
*/
|
|
@@ -137,7 +137,7 @@ export class PatchSession {
|
|
|
137
137
|
* @param {string} to - Target node ID
|
|
138
138
|
* @param {string} label - Edge label/type
|
|
139
139
|
* @param {string} key - Property key
|
|
140
|
-
* @param {
|
|
140
|
+
* @param {unknown} value - Property value (must be JSON-serializable)
|
|
141
141
|
* @returns {this} This session for chaining
|
|
142
142
|
* @throws {Error} If this session has already been committed
|
|
143
143
|
*/
|
|
@@ -194,23 +194,14 @@ export class PatchSession {
|
|
|
194
194
|
const sha = await this._builder.commit();
|
|
195
195
|
this._committed = true;
|
|
196
196
|
return sha;
|
|
197
|
-
} catch (
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
err.message,
|
|
204
|
-
err
|
|
205
|
-
);
|
|
197
|
+
} catch (err) {
|
|
198
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
199
|
+
const cause = err instanceof Error ? err : undefined;
|
|
200
|
+
if (errMsg.includes('Concurrent commit detected') ||
|
|
201
|
+
errMsg.includes('has advanced')) {
|
|
202
|
+
throw new WriterError('WRITER_REF_ADVANCED', errMsg, cause);
|
|
206
203
|
}
|
|
207
|
-
|
|
208
|
-
// Wrap other errors
|
|
209
|
-
throw new WriterError(
|
|
210
|
-
'PERSIST_WRITE_FAILED',
|
|
211
|
-
`Failed to persist patch: ${err.message}`,
|
|
212
|
-
err
|
|
213
|
-
);
|
|
204
|
+
throw new WriterError('PERSIST_WRITE_FAILED', `Failed to persist patch: ${errMsg}`, cause);
|
|
214
205
|
}
|
|
215
206
|
}
|
|
216
207
|
|
|
@@ -5,10 +5,153 @@
|
|
|
5
5
|
* via wireWarpMethods(). This declaration file makes them visible to tsc.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
9
|
-
|
|
10
8
|
import type { PatchBuilderV2 } from '../services/PatchBuilderV2.js';
|
|
11
9
|
import type { Writer } from './Writer.js';
|
|
10
|
+
import type { WarpStateV5 } from '../services/JoinReducer.js';
|
|
11
|
+
import type { PatchV2 } from '../types/WarpTypesV2.js';
|
|
12
|
+
import type { StateDiffResult } from '../services/StateDiff.js';
|
|
13
|
+
import type { TickReceipt } from '../types/TickReceipt.js';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Observer configuration for view creation and translation cost.
|
|
17
|
+
*/
|
|
18
|
+
interface ObserverConfig {
|
|
19
|
+
match: string;
|
|
20
|
+
expose?: string[];
|
|
21
|
+
redact?: string[];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Translation cost result.
|
|
26
|
+
*/
|
|
27
|
+
interface TranslationCostResult {
|
|
28
|
+
cost: number;
|
|
29
|
+
breakdown: { nodeLoss: number; edgeLoss: number; propLoss: number };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Lightweight status snapshot.
|
|
34
|
+
*/
|
|
35
|
+
interface WarpGraphStatus {
|
|
36
|
+
cachedState: 'fresh' | 'stale' | 'none';
|
|
37
|
+
patchesSinceCheckpoint: number;
|
|
38
|
+
tombstoneRatio: number;
|
|
39
|
+
writers: number;
|
|
40
|
+
frontier: Record<string, string>;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Sync request message.
|
|
45
|
+
*/
|
|
46
|
+
interface SyncRequest {
|
|
47
|
+
type: 'sync-request';
|
|
48
|
+
frontier: Record<string, string>;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Sync response message.
|
|
53
|
+
*/
|
|
54
|
+
interface SyncResponse {
|
|
55
|
+
type: 'sync-response';
|
|
56
|
+
frontier: Record<string, string>;
|
|
57
|
+
patches: Array<{ writerId: string; sha: string; patch: unknown }>;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Result of applySyncResponse().
|
|
62
|
+
*/
|
|
63
|
+
interface ApplySyncResult {
|
|
64
|
+
state: WarpStateV5;
|
|
65
|
+
frontier: Map<string, number>;
|
|
66
|
+
applied: number;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Sync options for syncWith().
|
|
71
|
+
*/
|
|
72
|
+
interface SyncWithOptions {
|
|
73
|
+
path?: string;
|
|
74
|
+
retries?: number;
|
|
75
|
+
baseDelayMs?: number;
|
|
76
|
+
maxDelayMs?: number;
|
|
77
|
+
timeoutMs?: number;
|
|
78
|
+
signal?: AbortSignal;
|
|
79
|
+
onStatus?: (event: {
|
|
80
|
+
type: string;
|
|
81
|
+
attempt: number;
|
|
82
|
+
durationMs?: number;
|
|
83
|
+
status?: number;
|
|
84
|
+
error?: Error;
|
|
85
|
+
}) => void;
|
|
86
|
+
auth?: { secret: string; keyId?: string };
|
|
87
|
+
/** Auto-materialize after sync; when true, result includes `state` */
|
|
88
|
+
materialize?: boolean;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* GC execution result.
|
|
93
|
+
*/
|
|
94
|
+
interface GCExecuteResult {
|
|
95
|
+
nodesCompacted: number;
|
|
96
|
+
edgesCompacted: number;
|
|
97
|
+
tombstonesRemoved: number;
|
|
98
|
+
durationMs: number;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* GC metrics.
|
|
103
|
+
*/
|
|
104
|
+
interface GCMetrics {
|
|
105
|
+
nodeCount: number;
|
|
106
|
+
edgeCount: number;
|
|
107
|
+
tombstoneCount: number;
|
|
108
|
+
tombstoneRatio: number;
|
|
109
|
+
patchesSinceCompaction: number;
|
|
110
|
+
lastCompactionTime: number;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Result of maybeRunGC().
|
|
115
|
+
*/
|
|
116
|
+
interface MaybeGCResult {
|
|
117
|
+
ran: boolean;
|
|
118
|
+
result: GCExecuteResult | null;
|
|
119
|
+
reasons: string[];
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Join receipt from CRDT merge.
|
|
124
|
+
*/
|
|
125
|
+
interface JoinReceipt {
|
|
126
|
+
nodesAdded: number;
|
|
127
|
+
nodesRemoved: number;
|
|
128
|
+
edgesAdded: number;
|
|
129
|
+
edgesRemoved: number;
|
|
130
|
+
propsChanged: number;
|
|
131
|
+
frontierMerged: boolean;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Wormhole edge.
|
|
136
|
+
*/
|
|
137
|
+
interface WormholeEdge {
|
|
138
|
+
fromSha: string;
|
|
139
|
+
toSha: string;
|
|
140
|
+
writerId: string;
|
|
141
|
+
payload: unknown;
|
|
142
|
+
patchCount: number;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Checkpoint data returned by _loadLatestCheckpoint.
|
|
147
|
+
*/
|
|
148
|
+
interface CheckpointData {
|
|
149
|
+
state: WarpStateV5;
|
|
150
|
+
frontier: Map<string, string>;
|
|
151
|
+
stateHash: string;
|
|
152
|
+
schema: number;
|
|
153
|
+
provenanceIndex?: unknown;
|
|
154
|
+
}
|
|
12
155
|
|
|
13
156
|
export {};
|
|
14
157
|
|
|
@@ -16,85 +159,96 @@ declare module '../WarpGraph.js' {
|
|
|
16
159
|
export default interface WarpGraph {
|
|
17
160
|
// ── query.methods.js ──────────────────────────────────────────────────
|
|
18
161
|
hasNode(nodeId: string): Promise<boolean>;
|
|
19
|
-
getNodeProps(nodeId: string): Promise<Map<string,
|
|
20
|
-
getEdgeProps(from: string, to: string, label: string): Promise<Record<string,
|
|
162
|
+
getNodeProps(nodeId: string): Promise<Map<string, unknown> | null>;
|
|
163
|
+
getEdgeProps(from: string, to: string, label: string): Promise<Record<string, unknown> | null>;
|
|
21
164
|
neighbors(nodeId: string, direction?: 'outgoing' | 'incoming' | 'both', edgeLabel?: string): Promise<Array<{ nodeId: string; label: string; direction: 'outgoing' | 'incoming' }>>;
|
|
22
|
-
getStateSnapshot(): Promise<
|
|
165
|
+
getStateSnapshot(): Promise<WarpStateV5 | null>;
|
|
23
166
|
getNodes(): Promise<string[]>;
|
|
24
|
-
getEdges(): Promise<Array<{ from: string; to: string; label: string; props: Record<string,
|
|
167
|
+
getEdges(): Promise<Array<{ from: string; to: string; label: string; props: Record<string, unknown> }>>;
|
|
25
168
|
getPropertyCount(): Promise<number>;
|
|
26
|
-
query():
|
|
27
|
-
observer(name: string, config:
|
|
28
|
-
translationCost(configA:
|
|
169
|
+
query(): import('../services/QueryBuilder.js').default;
|
|
170
|
+
observer(name: string, config: ObserverConfig): Promise<import('../services/ObserverView.js').default>;
|
|
171
|
+
translationCost(configA: ObserverConfig, configB: ObserverConfig): Promise<TranslationCostResult>;
|
|
29
172
|
|
|
30
173
|
// ── subscribe.methods.js ──────────────────────────────────────────────
|
|
31
|
-
subscribe(options: { onChange:
|
|
32
|
-
watch(pattern: string, options: { onChange:
|
|
33
|
-
_notifySubscribers(diff:
|
|
174
|
+
subscribe(options: { onChange: (diff: StateDiffResult) => void; onError?: (error: Error) => void; replay?: boolean }): { unsubscribe: () => void };
|
|
175
|
+
watch(pattern: string, options: { onChange: (diff: StateDiffResult) => void; onError?: (error: Error) => void; poll?: number }): { unsubscribe: () => void };
|
|
176
|
+
_notifySubscribers(diff: StateDiffResult, currentState: WarpStateV5): void;
|
|
34
177
|
|
|
35
178
|
// ── provenance.methods.js ─────────────────────────────────────────────
|
|
36
179
|
patchesFor(entityId: string): Promise<string[]>;
|
|
37
|
-
materializeSlice(nodeId: string, options?:
|
|
180
|
+
materializeSlice(nodeId: string, options?: { receipts?: boolean }): Promise<{ state: WarpStateV5; patchCount: number; receipts?: TickReceipt[] }>;
|
|
181
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- internal method; `any` avoids breaking provenance.methods.js callers
|
|
38
182
|
_computeBackwardCone(nodeId: string): Promise<Map<string, any>>;
|
|
39
|
-
loadPatchBySha(sha: string): Promise<
|
|
40
|
-
_loadPatchBySha(sha: string): Promise<
|
|
41
|
-
_loadPatchesBySha(shas: string[]): Promise<Array<{ patch:
|
|
42
|
-
|
|
183
|
+
loadPatchBySha(sha: string): Promise<{ patch: PatchV2; sha: string }>;
|
|
184
|
+
_loadPatchBySha(sha: string): Promise<{ patch: PatchV2; sha: string }>;
|
|
185
|
+
_loadPatchesBySha(shas: string[]): Promise<Array<{ patch: PatchV2; sha: string }>>;
|
|
186
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- internal method; `any` avoids breaking provenance.methods.js callers
|
|
187
|
+
_sortPatchesCausally(patches: Array<{ patch: any; sha: string }>): Array<{ patch: any; sha: string }>;
|
|
43
188
|
|
|
44
189
|
// ── fork.methods.js ───────────────────────────────────────────────────
|
|
45
190
|
fork(options: { from: string; at: string; forkName?: string; forkWriterId?: string }): Promise<WarpGraph>;
|
|
46
|
-
createWormhole(fromSha: string, toSha: string): Promise<
|
|
191
|
+
createWormhole(fromSha: string, toSha: string): Promise<WormholeEdge>;
|
|
47
192
|
_isAncestor(ancestorSha: string, descendantSha: string): Promise<boolean>;
|
|
48
193
|
_relationToCheckpointHead(ckHead: string, incomingSha: string): Promise<string>;
|
|
49
|
-
_validatePatchAgainstCheckpoint(writerId: string, incomingSha: string, checkpoint:
|
|
194
|
+
_validatePatchAgainstCheckpoint(writerId: string, incomingSha: string, checkpoint: unknown): Promise<void>;
|
|
50
195
|
|
|
51
196
|
// ── sync.methods.js ───────────────────────────────────────────────────
|
|
52
197
|
getFrontier(): Promise<Map<string, string>>;
|
|
53
198
|
hasFrontierChanged(): Promise<boolean>;
|
|
54
|
-
status(): Promise<
|
|
55
|
-
createSyncRequest(): Promise<
|
|
56
|
-
processSyncRequest(request:
|
|
57
|
-
applySyncResponse(response:
|
|
58
|
-
syncNeeded(remoteFrontier:
|
|
59
|
-
syncWith(remote:
|
|
60
|
-
serve(options
|
|
199
|
+
status(): Promise<WarpGraphStatus>;
|
|
200
|
+
createSyncRequest(): Promise<SyncRequest>;
|
|
201
|
+
processSyncRequest(request: SyncRequest): Promise<SyncResponse>;
|
|
202
|
+
applySyncResponse(response: SyncResponse): ApplySyncResult;
|
|
203
|
+
syncNeeded(remoteFrontier: Map<string, string>): Promise<boolean>;
|
|
204
|
+
syncWith(remote: string | WarpGraph, options?: SyncWithOptions): Promise<{ applied: number; attempts: number; state?: WarpStateV5 }>;
|
|
205
|
+
serve(options: {
|
|
206
|
+
port: number;
|
|
207
|
+
host?: string;
|
|
208
|
+
path?: string;
|
|
209
|
+
maxRequestBytes?: number;
|
|
210
|
+
httpPort: unknown;
|
|
211
|
+
auth?: unknown;
|
|
212
|
+
allowedWriters?: string[];
|
|
213
|
+
}): Promise<{ close(): Promise<void>; url: string }>;
|
|
61
214
|
|
|
62
215
|
// ── checkpoint.methods.js ─────────────────────────────────────────────
|
|
63
216
|
createCheckpoint(): Promise<string>;
|
|
64
217
|
syncCoverage(): Promise<void>;
|
|
65
|
-
_loadLatestCheckpoint(): Promise<
|
|
66
|
-
_loadPatchesSince(checkpoint:
|
|
218
|
+
_loadLatestCheckpoint(): Promise<CheckpointData | null>;
|
|
219
|
+
_loadPatchesSince(checkpoint: CheckpointData): Promise<Array<{ patch: PatchV2; sha: string }>>;
|
|
67
220
|
_validateMigrationBoundary(): Promise<void>;
|
|
68
221
|
_hasSchema1Patches(): Promise<boolean>;
|
|
69
|
-
_maybeRunGC(state:
|
|
70
|
-
maybeRunGC():
|
|
71
|
-
runGC():
|
|
72
|
-
getGCMetrics():
|
|
222
|
+
_maybeRunGC(state: WarpStateV5): void;
|
|
223
|
+
maybeRunGC(): MaybeGCResult;
|
|
224
|
+
runGC(): GCExecuteResult;
|
|
225
|
+
getGCMetrics(): GCMetrics | null;
|
|
73
226
|
|
|
74
227
|
// ── patch.methods.js ──────────────────────────────────────────────────
|
|
75
228
|
createPatch(): Promise<PatchBuilderV2>;
|
|
76
229
|
patch(build: (p: PatchBuilderV2) => void | Promise<void>): Promise<string>;
|
|
77
230
|
_nextLamport(): Promise<{ lamport: number; parentSha: string | null }>;
|
|
78
|
-
_loadWriterPatches(writerId: string, stopAtSha?: string | null): Promise<Array<{ patch:
|
|
79
|
-
getWriterPatches(writerId: string, stopAtSha?: string | null): Promise<Array<{ patch:
|
|
80
|
-
_onPatchCommitted(writerId: string, opts?: { patch?:
|
|
231
|
+
_loadWriterPatches(writerId: string, stopAtSha?: string | null): Promise<Array<{ patch: PatchV2; sha: string }>>;
|
|
232
|
+
getWriterPatches(writerId: string, stopAtSha?: string | null): Promise<Array<{ patch: PatchV2; sha: string }>>;
|
|
233
|
+
_onPatchCommitted(writerId: string, opts?: { patch?: PatchV2; sha?: string }): Promise<void>;
|
|
81
234
|
writer(writerId?: string): Promise<Writer>;
|
|
82
|
-
createWriter(opts?:
|
|
235
|
+
createWriter(opts?: { persist?: 'config' | 'none'; alias?: string }): Promise<Writer>;
|
|
83
236
|
_ensureFreshState(): Promise<void>;
|
|
84
237
|
discoverWriters(): Promise<string[]>;
|
|
85
238
|
discoverTicks(): Promise<{ ticks: number[]; maxTick: number; perWriter: Map<string, { ticks: number[]; tipSha: string | null; tickShas: Record<number, string> }> }>;
|
|
86
|
-
join(otherState:
|
|
87
|
-
_frontierEquals(a:
|
|
239
|
+
join(otherState: WarpStateV5): { state: WarpStateV5; receipt: JoinReceipt };
|
|
240
|
+
_frontierEquals(a: Map<string, number>, b: Map<string, number>): boolean;
|
|
88
241
|
|
|
89
242
|
// ── materialize.methods.js ────────────────────────────────────────────
|
|
90
|
-
materialize(options?:
|
|
91
|
-
|
|
243
|
+
materialize(options: { receipts: true; ceiling?: number | null }): Promise<{ state: WarpStateV5; receipts: TickReceipt[] }>;
|
|
244
|
+
materialize(options?: { receipts?: false; ceiling?: number | null }): Promise<WarpStateV5>;
|
|
245
|
+
_materializeGraph(): Promise<{ state: WarpStateV5; stateHash: string; adjacency: unknown }>;
|
|
92
246
|
|
|
93
247
|
// ── materializeAdvanced.methods.js ────────────────────────────────────
|
|
94
|
-
_resolveCeiling(options
|
|
95
|
-
_buildAdjacency(state:
|
|
96
|
-
_setMaterializedState(state:
|
|
97
|
-
_materializeWithCeiling(ceiling:
|
|
98
|
-
materializeAt(checkpointSha: string): Promise<
|
|
248
|
+
_resolveCeiling(options?: { ceiling?: number | null }): number | null;
|
|
249
|
+
_buildAdjacency(state: WarpStateV5): { outgoing: Map<string, Array<{ neighborId: string; label: string }>>; incoming: Map<string, Array<{ neighborId: string; label: string }>> };
|
|
250
|
+
_setMaterializedState(state: WarpStateV5): Promise<{ state: WarpStateV5; stateHash: string; adjacency: unknown }>;
|
|
251
|
+
_materializeWithCeiling(ceiling: number, collectReceipts: boolean, t0: number): Promise<WarpStateV5 | { state: WarpStateV5; receipts: TickReceipt[] }>;
|
|
252
|
+
materializeAt(checkpointSha: string): Promise<WarpStateV5>;
|
|
99
253
|
}
|
|
100
254
|
}
|
|
@@ -16,6 +16,8 @@ import { shouldRunGC, executeGC } from '../services/GCPolicy.js';
|
|
|
16
16
|
import { collectGCMetrics } from '../services/GCMetrics.js';
|
|
17
17
|
import { computeAppliedVV } from '../services/CheckpointSerializerV5.js';
|
|
18
18
|
|
|
19
|
+
/** @typedef {import('../types/WarpPersistence.js').CorePersistence} CorePersistence */
|
|
20
|
+
|
|
19
21
|
/**
|
|
20
22
|
* Creates a checkpoint of the current graph state.
|
|
21
23
|
*
|
|
@@ -59,8 +61,10 @@ export async function createCheckpoint() {
|
|
|
59
61
|
}
|
|
60
62
|
|
|
61
63
|
// 4. Call CheckpointService.create() with provenance index if available
|
|
64
|
+
/** @type {CorePersistence} */
|
|
65
|
+
const persistence = this._persistence;
|
|
62
66
|
const checkpointSha = await createCheckpointCommit({
|
|
63
|
-
persistence
|
|
67
|
+
persistence,
|
|
64
68
|
graphName: this._graphName,
|
|
65
69
|
state,
|
|
66
70
|
frontier,
|
|
@@ -201,13 +201,13 @@ export async function createWormhole(fromSha, toSha) {
|
|
|
201
201
|
const t0 = this._clock.now();
|
|
202
202
|
|
|
203
203
|
try {
|
|
204
|
-
const wormhole = await createWormholeImpl(
|
|
204
|
+
const wormhole = await createWormholeImpl({
|
|
205
205
|
persistence: this._persistence,
|
|
206
206
|
graphName: this._graphName,
|
|
207
207
|
fromSha,
|
|
208
208
|
toSha,
|
|
209
209
|
codec: this._codec,
|
|
210
|
-
})
|
|
210
|
+
});
|
|
211
211
|
|
|
212
212
|
this._logTiming('createWormhole', t0, {
|
|
213
213
|
metrics: `${wormhole.patchCount} patches from=${fromSha.slice(0, 7)} to=${toSha.slice(0, 7)}`,
|
|
@@ -9,6 +9,47 @@
|
|
|
9
9
|
import { reduceV5, createEmptyStateV5, cloneStateV5 } from '../services/JoinReducer.js';
|
|
10
10
|
import { ProvenanceIndex } from '../services/ProvenanceIndex.js';
|
|
11
11
|
import { diffStates, isEmptyDiff } from '../services/StateDiff.js';
|
|
12
|
+
import { decodePatchMessage, detectMessageKind } from '../services/WarpMessageCodec.js';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Scans the checkpoint frontier's tip commits for the maximum observed Lamport tick.
|
|
16
|
+
* Updates `graph._maxObservedLamport` in-place; best-effort (skips unreadable commits).
|
|
17
|
+
*
|
|
18
|
+
* @param {import('../WarpGraph.js').default} graph
|
|
19
|
+
* @param {Map<string, string>} frontier
|
|
20
|
+
* @returns {Promise<void>}
|
|
21
|
+
*/
|
|
22
|
+
async function scanFrontierForMaxLamport(graph, frontier) {
|
|
23
|
+
for (const tipSha of frontier.values()) {
|
|
24
|
+
try {
|
|
25
|
+
const msg = await graph._persistence.showNode(tipSha);
|
|
26
|
+
if (detectMessageKind(msg) === 'patch') {
|
|
27
|
+
const { lamport } = decodePatchMessage(msg);
|
|
28
|
+
if (lamport > graph._maxObservedLamport) {
|
|
29
|
+
graph._maxObservedLamport = lamport;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
} catch {
|
|
33
|
+
// best-effort: skip unreadable frontier commits
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Scans a list of patch entries for the maximum observed Lamport tick.
|
|
40
|
+
* Updates `graph._maxObservedLamport` in-place.
|
|
41
|
+
*
|
|
42
|
+
* @param {import('../WarpGraph.js').default} graph
|
|
43
|
+
* @param {Array<{patch: {lamport?: number}}>} patches
|
|
44
|
+
*/
|
|
45
|
+
function scanPatchesForMaxLamport(graph, patches) {
|
|
46
|
+
for (const { patch } of patches) {
|
|
47
|
+
const tick = patch.lamport ?? 0;
|
|
48
|
+
if (tick > graph._maxObservedLamport) {
|
|
49
|
+
graph._maxObservedLamport = tick;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
12
53
|
|
|
13
54
|
/**
|
|
14
55
|
* Materializes the current graph state.
|
|
@@ -63,17 +104,24 @@ export async function materialize(options) {
|
|
|
63
104
|
// If checkpoint exists, use incremental materialization
|
|
64
105
|
if (checkpoint?.schema === 2 || checkpoint?.schema === 3) {
|
|
65
106
|
const patches = await this._loadPatchesSince(checkpoint);
|
|
107
|
+
// Update max observed Lamport so _nextLamport() issues globally-monotonic ticks.
|
|
108
|
+
// Read the checkpoint frontier's tip commit messages to capture the pre-checkpoint max,
|
|
109
|
+
// then scan the incremental patches for anything newer.
|
|
110
|
+
if (checkpoint.frontier instanceof Map) {
|
|
111
|
+
await scanFrontierForMaxLamport(this, checkpoint.frontier);
|
|
112
|
+
}
|
|
113
|
+
scanPatchesForMaxLamport(this, patches);
|
|
66
114
|
if (collectReceipts) {
|
|
67
|
-
const result = /** @type {{state: import('../services/JoinReducer.js').WarpStateV5, receipts: import('../types/TickReceipt.js').TickReceipt[]}} */ (reduceV5(/** @type {
|
|
115
|
+
const result = /** @type {{state: import('../services/JoinReducer.js').WarpStateV5, receipts: import('../types/TickReceipt.js').TickReceipt[]}} */ (reduceV5(/** @type {Parameters<typeof reduceV5>[0]} */ (patches), checkpoint.state, { receipts: true }));
|
|
68
116
|
state = result.state;
|
|
69
117
|
receipts = result.receipts;
|
|
70
118
|
} else {
|
|
71
|
-
state = /** @type {import('../services/JoinReducer.js').WarpStateV5} */ (reduceV5(/** @type {
|
|
119
|
+
state = /** @type {import('../services/JoinReducer.js').WarpStateV5} */ (reduceV5(/** @type {Parameters<typeof reduceV5>[0]} */ (patches), checkpoint.state));
|
|
72
120
|
}
|
|
73
121
|
patchCount = patches.length;
|
|
74
122
|
|
|
75
123
|
// Build provenance index: start from checkpoint index if present, then add new patches
|
|
76
|
-
const ckPI = /** @type {
|
|
124
|
+
const ckPI = /** @type {{provenanceIndex?: import('../services/ProvenanceIndex.js').ProvenanceIndex}} */ (checkpoint).provenanceIndex;
|
|
77
125
|
this._provenanceIndex = ckPI
|
|
78
126
|
? ckPI.clone()
|
|
79
127
|
: new ProvenanceIndex();
|
|
@@ -109,13 +157,15 @@ export async function materialize(options) {
|
|
|
109
157
|
receipts = [];
|
|
110
158
|
}
|
|
111
159
|
} else {
|
|
160
|
+
// Update max observed Lamport from all loaded patches.
|
|
161
|
+
scanPatchesForMaxLamport(this, allPatches);
|
|
112
162
|
// 5. Reduce all patches to state
|
|
113
163
|
if (collectReceipts) {
|
|
114
|
-
const result = /** @type {{state: import('../services/JoinReducer.js').WarpStateV5, receipts: import('../types/TickReceipt.js').TickReceipt[]}} */ (reduceV5(/** @type {
|
|
164
|
+
const result = /** @type {{state: import('../services/JoinReducer.js').WarpStateV5, receipts: import('../types/TickReceipt.js').TickReceipt[]}} */ (reduceV5(/** @type {Parameters<typeof reduceV5>[0]} */ (allPatches), undefined, { receipts: true }));
|
|
115
165
|
state = result.state;
|
|
116
166
|
receipts = result.receipts;
|
|
117
167
|
} else {
|
|
118
|
-
state = /** @type {import('../services/JoinReducer.js').WarpStateV5} */ (reduceV5(/** @type {
|
|
168
|
+
state = /** @type {import('../services/JoinReducer.js').WarpStateV5} */ (reduceV5(/** @type {Parameters<typeof reduceV5>[0]} */ (allPatches)));
|
|
119
169
|
}
|
|
120
170
|
patchCount = allPatches.length;
|
|
121
171
|
|
|
@@ -18,6 +18,15 @@ import { serializeFullStateV5, deserializeFullStateV5 } from '../services/Checkp
|
|
|
18
18
|
import { buildSeekCacheKey } from '../utils/seekCacheKey.js';
|
|
19
19
|
import { materializeIncremental } from '../services/CheckpointService.js';
|
|
20
20
|
import { createFrontier, updateFrontier } from '../services/Frontier.js';
|
|
21
|
+
|
|
22
|
+
/** @typedef {import('../types/WarpPersistence.js').CorePersistence} CorePersistence */
|
|
23
|
+
/** @typedef {import('../services/JoinReducer.js').WarpStateV5} WarpStateV5 */
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @typedef {{ outgoing: Map<string, Array<{neighborId: string, label: string}>>, incoming: Map<string, Array<{neighborId: string, label: string}>> }} AdjacencyMap
|
|
27
|
+
* @typedef {{ state: WarpStateV5, stateHash: string, adjacency: AdjacencyMap }} MaterializedResult
|
|
28
|
+
*/
|
|
29
|
+
|
|
21
30
|
import { buildWriterRef } from '../utils/RefLayout.js';
|
|
22
31
|
import { decodePatchMessage, detectMessageKind } from '../services/WarpMessageCodec.js';
|
|
23
32
|
|
|
@@ -97,7 +106,7 @@ export function _buildAdjacency(state) {
|
|
|
97
106
|
*
|
|
98
107
|
* @this {import('../WarpGraph.js').default}
|
|
99
108
|
* @param {import('../services/JoinReducer.js').WarpStateV5} state
|
|
100
|
-
* @returns {Promise<
|
|
109
|
+
* @returns {Promise<MaterializedResult>}
|
|
101
110
|
* @private
|
|
102
111
|
*/
|
|
103
112
|
export async function _setMaterializedState(state) {
|
|
@@ -226,11 +235,11 @@ export async function _materializeWithCeiling(ceiling, collectReceipts, t0) {
|
|
|
226
235
|
receipts = [];
|
|
227
236
|
}
|
|
228
237
|
} else if (collectReceipts) {
|
|
229
|
-
const result = /** @type {{state: import('../services/JoinReducer.js').WarpStateV5, receipts: import('../types/TickReceipt.js').TickReceipt[]}} */ (reduceV5(/** @type {
|
|
238
|
+
const result = /** @type {{state: import('../services/JoinReducer.js').WarpStateV5, receipts: import('../types/TickReceipt.js').TickReceipt[]}} */ (reduceV5(/** @type {Parameters<typeof reduceV5>[0]} */ (allPatches), undefined, { receipts: true }));
|
|
230
239
|
state = result.state;
|
|
231
240
|
receipts = result.receipts;
|
|
232
241
|
} else {
|
|
233
|
-
state = /** @type {import('../services/JoinReducer.js').WarpStateV5} */ (reduceV5(/** @type {
|
|
242
|
+
state = /** @type {import('../services/JoinReducer.js').WarpStateV5} */ (reduceV5(/** @type {Parameters<typeof reduceV5>[0]} */ (allPatches)));
|
|
234
243
|
}
|
|
235
244
|
|
|
236
245
|
this._provenanceIndex = new ProvenanceIndex();
|
|
@@ -326,8 +335,10 @@ export async function materializeAt(checkpointSha) {
|
|
|
326
335
|
};
|
|
327
336
|
|
|
328
337
|
// 4. Call materializeIncremental with the checkpoint and target frontier
|
|
338
|
+
/** @type {CorePersistence} */
|
|
339
|
+
const persistence = this._persistence;
|
|
329
340
|
const state = await materializeIncremental({
|
|
330
|
-
persistence
|
|
341
|
+
persistence,
|
|
331
342
|
graphName: this._graphName,
|
|
332
343
|
checkpointSha,
|
|
333
344
|
targetFrontier,
|