@hardkas/core 0.5.5-alpha → 0.6.0-alpha
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/dist/index.d.ts +182 -14
- package/dist/index.js +162 -6
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -70,7 +70,7 @@ declare const asEventId: (id: string) => EventId;
|
|
|
70
70
|
declare const asCorrelationId: (id: string) => CorrelationId;
|
|
71
71
|
declare const asKaspaAddress: (addr: string) => KaspaAddress;
|
|
72
72
|
declare const asRpcEndpointId: (id: string) => RpcEndpointId;
|
|
73
|
-
declare const asNetworkId: (id:
|
|
73
|
+
declare const asNetworkId: <T extends string>(id: T) => Brand<T, "NetworkId">;
|
|
74
74
|
declare const asEventSequence: (seq: number) => EventSequence;
|
|
75
75
|
declare const asDaaScore: (score: number | bigint) => DaaScore;
|
|
76
76
|
|
|
@@ -81,7 +81,7 @@ type EventDomain = "workflow" | "integrity" | "rpc" | "dag" | "replay" | "localn
|
|
|
81
81
|
/**
|
|
82
82
|
* HardKAS Core Event Kinds.
|
|
83
83
|
*/
|
|
84
|
-
type EventKind = "workflow.plan.created" | "workflow.signed" | "workflow.submitted" | "workflow.receipt" | "workflow.started" | "workflow.completed" | "workflow.failed" | "integrity.hash_mismatch" | "integrity.schema_violation" | "integrity.lineage_break" | "integrity.violation" | "dag.conflict" | "dag.displacement" | "dag.sink_moved" | "rpc.health" | "rpc.error" | "rpc.stale" | "replay.divergence" | "replay.verified" | "localnet.started" | "localnet.stopped" | "l2.deposit.planned" | "l2.withdrawal.planned";
|
|
84
|
+
type EventKind = "workflow.plan.created" | "workflow.signed" | "workflow.submitted" | "workflow.receipt" | "workflow.started" | "workflow.completed" | "workflow.failed" | "integrity.hash_mismatch" | "integrity.schema_violation" | "integrity.lineage_break" | "integrity.violation" | "dag.conflict" | "dag.displacement" | "dag.sink_moved" | "rpc.health" | "rpc.error" | "rpc.stale" | "replay.divergence" | "replay.verified" | "localnet.started" | "localnet.stopped" | "l2.deposit.planned" | "l2.withdrawal.planned" | "artifact.written" | "artifact.indexed" | "artifact.corrupted" | "sqlite.commit" | "replay.invalidated" | "replay.completed" | "replay.excluded" | "sse.emitted" | "dashboard.cache_invalidated" | "dashboard.refetch_started" | "dashboard.refetch_completed" | "query_store.sync_started" | "query_store.sync_completed" | "lineage.verification_failed";
|
|
85
85
|
/**
|
|
86
86
|
* Payload mapping for each event kind.
|
|
87
87
|
*/
|
|
@@ -192,6 +192,60 @@ interface EventPayloadByKind {
|
|
|
192
192
|
amount: bigint;
|
|
193
193
|
from: string;
|
|
194
194
|
};
|
|
195
|
+
"artifact.written": {
|
|
196
|
+
artifactId: ArtifactId;
|
|
197
|
+
path: string;
|
|
198
|
+
};
|
|
199
|
+
"artifact.indexed": {
|
|
200
|
+
artifactId: ArtifactId;
|
|
201
|
+
schema: string;
|
|
202
|
+
};
|
|
203
|
+
"artifact.corrupted": {
|
|
204
|
+
artifactId: ArtifactId;
|
|
205
|
+
path: string;
|
|
206
|
+
issue: string;
|
|
207
|
+
};
|
|
208
|
+
"sqlite.commit": {
|
|
209
|
+
transactionId: string;
|
|
210
|
+
rowCount: number;
|
|
211
|
+
};
|
|
212
|
+
"replay.invalidated": {
|
|
213
|
+
artifactId: ArtifactId;
|
|
214
|
+
reason: string;
|
|
215
|
+
};
|
|
216
|
+
"replay.completed": {
|
|
217
|
+
targetArtifactId: ArtifactId;
|
|
218
|
+
success: boolean;
|
|
219
|
+
};
|
|
220
|
+
"replay.excluded": {
|
|
221
|
+
artifactId: ArtifactId;
|
|
222
|
+
reason: string;
|
|
223
|
+
};
|
|
224
|
+
"sse.emitted": {
|
|
225
|
+
eventId: EventId;
|
|
226
|
+
channel: string;
|
|
227
|
+
};
|
|
228
|
+
"dashboard.cache_invalidated": {
|
|
229
|
+
key: string;
|
|
230
|
+
};
|
|
231
|
+
"dashboard.refetch_started": {
|
|
232
|
+
key: string;
|
|
233
|
+
};
|
|
234
|
+
"dashboard.refetch_completed": {
|
|
235
|
+
key: string;
|
|
236
|
+
success: boolean;
|
|
237
|
+
};
|
|
238
|
+
"query_store.sync_started": {
|
|
239
|
+
syncId: string;
|
|
240
|
+
};
|
|
241
|
+
"query_store.sync_completed": {
|
|
242
|
+
syncId: string;
|
|
243
|
+
stats: Record<string, number>;
|
|
244
|
+
};
|
|
245
|
+
"lineage.verification_failed": {
|
|
246
|
+
artifactId: ArtifactId;
|
|
247
|
+
missingParentId: ArtifactId;
|
|
248
|
+
};
|
|
195
249
|
}
|
|
196
250
|
/**
|
|
197
251
|
* Formal Event Envelope (v1).
|
|
@@ -205,7 +259,10 @@ interface EventEnvelope<K extends EventKind = EventKind> {
|
|
|
205
259
|
domain: EventDomain;
|
|
206
260
|
kind: K;
|
|
207
261
|
timestamp: string;
|
|
208
|
-
|
|
262
|
+
emittedAt: string;
|
|
263
|
+
sequenceNumber: EventSequence;
|
|
264
|
+
globalOffset?: number;
|
|
265
|
+
sourceSubsystem: string;
|
|
209
266
|
workflowId: WorkflowId;
|
|
210
267
|
correlationId: CorrelationId;
|
|
211
268
|
causationId?: EventId;
|
|
@@ -256,7 +313,9 @@ declare function createEventEnvelope<K extends EventKind>(params: {
|
|
|
256
313
|
artifactId?: ArtifactId;
|
|
257
314
|
txId?: TxId;
|
|
258
315
|
eventId?: EventId;
|
|
259
|
-
|
|
316
|
+
sequenceNumber: EventSequence;
|
|
317
|
+
globalOffset?: number;
|
|
318
|
+
sourceSubsystem: string;
|
|
260
319
|
}): EventEnvelope<K>;
|
|
261
320
|
/**
|
|
262
321
|
* Lightweight runtime validation for event envelopes.
|
|
@@ -334,6 +393,37 @@ interface CorruptionIssue {
|
|
|
334
393
|
*/
|
|
335
394
|
declare function formatCorruptionIssue(issue: CorruptionIssue): string;
|
|
336
395
|
|
|
396
|
+
type IntegrityStatus = "verified" | "corrupted" | "invalid_json" | "unknown";
|
|
397
|
+
/**
|
|
398
|
+
* Explains the causal origin and validation state of a derived value.
|
|
399
|
+
*/
|
|
400
|
+
interface StateProvenance {
|
|
401
|
+
/**
|
|
402
|
+
* The architectural authority that asserts this state (e.g. "query-store projection", "filesystem artifact", "memory cache")
|
|
403
|
+
*/
|
|
404
|
+
authority: string;
|
|
405
|
+
/**
|
|
406
|
+
* The source artifact ID from which this state was derived (if applicable)
|
|
407
|
+
*/
|
|
408
|
+
derivedFrom?: ArtifactId;
|
|
409
|
+
/**
|
|
410
|
+
* The absolute or relative file path of the source artifact
|
|
411
|
+
*/
|
|
412
|
+
originalPath?: string;
|
|
413
|
+
/**
|
|
414
|
+
* Current deterministic integrity of the source
|
|
415
|
+
*/
|
|
416
|
+
integrity: IntegrityStatus;
|
|
417
|
+
/**
|
|
418
|
+
* The replay scope indicating where this state is valid (e.g., "local-only", "global")
|
|
419
|
+
*/
|
|
420
|
+
replayScope: "local-only" | "global" | "unknown";
|
|
421
|
+
/**
|
|
422
|
+
* True if this state has been independently verified against network consensus
|
|
423
|
+
*/
|
|
424
|
+
consensusValidated: boolean;
|
|
425
|
+
}
|
|
426
|
+
|
|
337
427
|
/**
|
|
338
428
|
* HardKAS Lock Metadata schema v1
|
|
339
429
|
*/
|
|
@@ -406,16 +496,94 @@ declare function clearLock(rootDir: string, name: string, options?: {
|
|
|
406
496
|
reason?: string;
|
|
407
497
|
};
|
|
408
498
|
|
|
499
|
+
interface StructuralDiff {
|
|
500
|
+
missingArtifacts: string[];
|
|
501
|
+
excludedArtifacts: string[];
|
|
502
|
+
missingProjections: string[];
|
|
503
|
+
}
|
|
504
|
+
interface DeterministicDiff {
|
|
505
|
+
stateRootDiverged: boolean;
|
|
506
|
+
lineageDiverged: boolean;
|
|
507
|
+
graphDiverged: boolean;
|
|
508
|
+
differences: Array<{
|
|
509
|
+
path: string;
|
|
510
|
+
a: any;
|
|
511
|
+
b: any;
|
|
512
|
+
}>;
|
|
513
|
+
}
|
|
514
|
+
interface RuntimeNoiseDiff {
|
|
515
|
+
timestampShifts: Array<{
|
|
516
|
+
path: string;
|
|
517
|
+
shiftMs: number;
|
|
518
|
+
}>;
|
|
519
|
+
eventOrderingShifts: string[];
|
|
520
|
+
metadataDrift: string[];
|
|
521
|
+
}
|
|
522
|
+
interface LayeredReplayDiff {
|
|
523
|
+
schema: "hardkas.replayDiff.v1";
|
|
524
|
+
structural: StructuralDiff;
|
|
525
|
+
deterministic: DeterministicDiff;
|
|
526
|
+
observational: RuntimeNoiseDiff;
|
|
527
|
+
}
|
|
528
|
+
declare function diffReplays(replayA: any, replayB: any): LayeredReplayDiff;
|
|
529
|
+
|
|
530
|
+
interface SnapshotManifest {
|
|
531
|
+
snapshotVersion: number;
|
|
532
|
+
createdAt: string;
|
|
533
|
+
hardkasVersion: string;
|
|
534
|
+
stateAuthority: "filesystem";
|
|
535
|
+
projectionAuthority: "sqlite";
|
|
536
|
+
deterministicScope: "local-only" | "consensus-validated";
|
|
537
|
+
consensusValidated: boolean;
|
|
538
|
+
includedArtifacts: number;
|
|
539
|
+
excludedArtifacts: number;
|
|
540
|
+
corruptedArtifacts: number;
|
|
541
|
+
}
|
|
542
|
+
interface CreateSnapshotOptions {
|
|
543
|
+
hardkasDir: string;
|
|
544
|
+
outputDir: string;
|
|
545
|
+
deterministicScope?: "local-only" | "consensus-validated";
|
|
546
|
+
}
|
|
547
|
+
declare function createSnapshot(options: CreateSnapshotOptions): Promise<SnapshotManifest>;
|
|
548
|
+
declare function readSnapshotManifest(snapshotDir: string): Promise<SnapshotManifest>;
|
|
549
|
+
|
|
550
|
+
/**
|
|
551
|
+
* Deterministic comparison utility for cross-platform string sorting.
|
|
552
|
+
* Avoids localeCompare() which is dependent on the host machine's ICU version and OS locale.
|
|
553
|
+
*/
|
|
554
|
+
declare function deterministicCompare(a: string, b: string): number;
|
|
555
|
+
|
|
556
|
+
interface DeterministicClock {
|
|
557
|
+
now(): number;
|
|
558
|
+
}
|
|
559
|
+
interface DeterministicRandom {
|
|
560
|
+
next(): number;
|
|
561
|
+
}
|
|
562
|
+
interface IdProvider {
|
|
563
|
+
execution(): string;
|
|
564
|
+
workflow(): string;
|
|
565
|
+
}
|
|
566
|
+
interface RuntimeContext {
|
|
567
|
+
clock: DeterministicClock;
|
|
568
|
+
random: DeterministicRandom;
|
|
569
|
+
ids: IdProvider;
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* A default system runtime context (for non-deterministic contexts like dev server or CLI entry points)
|
|
573
|
+
* This should NOT be used directly in pure canonical domain logic (e.g. artifacts, replays).
|
|
574
|
+
*/
|
|
575
|
+
declare const systemRuntimeContext: RuntimeContext;
|
|
576
|
+
|
|
409
577
|
declare const SOMPI_PER_KAS = 100000000n;
|
|
410
|
-
declare const kaspaNetworkIdSchema: z.ZodEnum<["mainnet", "testnet-10", "testnet-11", "testnet-12", "simnet", "simnet-1", "devnet"]>;
|
|
578
|
+
declare const kaspaNetworkIdSchema: z.ZodEnum<["mainnet", "testnet-10", "testnet-11", "testnet-12", "simnet", "simnet-1", "devnet", "simulated"]>;
|
|
411
579
|
type NetworkId = Brand<z.infer<typeof kaspaNetworkIdSchema>, "NetworkId">;
|
|
412
580
|
declare const executionModeSchema: z.ZodEnum<["simulated", "real", "readonly"]>;
|
|
413
581
|
type ExecutionMode = z.infer<typeof executionModeSchema>;
|
|
414
|
-
declare const artifactTypeSchema: z.ZodEnum<["txPlan", "signedTx", "txReceipt", "txTrace", "snapshot"]>;
|
|
582
|
+
declare const artifactTypeSchema: z.ZodEnum<["txPlan", "signedTx", "txReceipt", "txTrace", "snapshot", "workflow.v1"]>;
|
|
415
583
|
type ArtifactType = z.infer<typeof artifactTypeSchema>;
|
|
416
|
-
declare const NetworkIdSchema: z.ZodEnum<["mainnet", "testnet-10", "testnet-11", "testnet-12", "simnet", "simnet-1", "devnet"]>;
|
|
584
|
+
declare const NetworkIdSchema: z.ZodEnum<["mainnet", "testnet-10", "testnet-11", "testnet-12", "simnet", "simnet-1", "devnet", "simulated"]>;
|
|
417
585
|
declare const ExecutionModeSchema: z.ZodEnum<["simulated", "real", "readonly"]>;
|
|
418
|
-
declare const ArtifactTypeSchema: z.ZodEnum<["txPlan", "signedTx", "txReceipt", "txTrace", "snapshot"]>;
|
|
586
|
+
declare const ArtifactTypeSchema: z.ZodEnum<["txPlan", "signedTx", "txReceipt", "txTrace", "snapshot", "workflow.v1"]>;
|
|
419
587
|
declare const hardkasConfigSchema: z.ZodObject<{
|
|
420
588
|
project: z.ZodObject<{
|
|
421
589
|
name: z.ZodString;
|
|
@@ -428,13 +596,13 @@ declare const hardkasConfigSchema: z.ZodObject<{
|
|
|
428
596
|
root: string;
|
|
429
597
|
}>;
|
|
430
598
|
network: z.ZodObject<{
|
|
431
|
-
id: z.ZodEnum<["mainnet", "testnet-10", "testnet-11", "testnet-12", "simnet", "simnet-1", "devnet"]>;
|
|
599
|
+
id: z.ZodEnum<["mainnet", "testnet-10", "testnet-11", "testnet-12", "simnet", "simnet-1", "devnet", "simulated"]>;
|
|
432
600
|
rpcUrl: z.ZodOptional<z.ZodString>;
|
|
433
601
|
}, "strip", z.ZodTypeAny, {
|
|
434
|
-
id: "mainnet" | "testnet-10" | "testnet-11" | "testnet-12" | "simnet" | "simnet-1" | "devnet";
|
|
602
|
+
id: "mainnet" | "testnet-10" | "testnet-11" | "testnet-12" | "simnet" | "simnet-1" | "devnet" | "simulated";
|
|
435
603
|
rpcUrl?: string | undefined;
|
|
436
604
|
}, {
|
|
437
|
-
id: "mainnet" | "testnet-10" | "testnet-11" | "testnet-12" | "simnet" | "simnet-1" | "devnet";
|
|
605
|
+
id: "mainnet" | "testnet-10" | "testnet-11" | "testnet-12" | "simnet" | "simnet-1" | "devnet" | "simulated";
|
|
438
606
|
rpcUrl?: string | undefined;
|
|
439
607
|
}>;
|
|
440
608
|
localnet: z.ZodDefault<z.ZodObject<{
|
|
@@ -453,7 +621,7 @@ declare const hardkasConfigSchema: z.ZodObject<{
|
|
|
453
621
|
root: string;
|
|
454
622
|
};
|
|
455
623
|
network: {
|
|
456
|
-
id: "mainnet" | "testnet-10" | "testnet-11" | "testnet-12" | "simnet" | "simnet-1" | "devnet";
|
|
624
|
+
id: "mainnet" | "testnet-10" | "testnet-11" | "testnet-12" | "simnet" | "simnet-1" | "devnet" | "simulated";
|
|
457
625
|
rpcUrl?: string | undefined;
|
|
458
626
|
};
|
|
459
627
|
localnet: {
|
|
@@ -466,7 +634,7 @@ declare const hardkasConfigSchema: z.ZodObject<{
|
|
|
466
634
|
root: string;
|
|
467
635
|
};
|
|
468
636
|
network: {
|
|
469
|
-
id: "mainnet" | "testnet-10" | "testnet-11" | "testnet-12" | "simnet" | "simnet-1" | "devnet";
|
|
637
|
+
id: "mainnet" | "testnet-10" | "testnet-11" | "testnet-12" | "simnet" | "simnet-1" | "devnet" | "simulated";
|
|
470
638
|
rpcUrl?: string | undefined;
|
|
471
639
|
};
|
|
472
640
|
localnet?: {
|
|
@@ -496,4 +664,4 @@ declare function parseHardkasConfig(input: unknown): HardkasConfig;
|
|
|
496
664
|
declare function parseKasToSompi(input: string): bigint;
|
|
497
665
|
declare function formatSompi(amountSompi: bigint): string;
|
|
498
666
|
|
|
499
|
-
export { type AcquireLockArgs, type ArtifactId, type ArtifactType, ArtifactTypeSchema, type Brand, type Branded, type ContentHash, type CoreEvent, type CoreEventListener, type CorrelationId, type CorruptionCode, type CorruptionIssue, type CorruptionSeverity, type DaaScore, type EventDomain, type EventEnvelope, type EventId, type EventKind, type EventPayloadByKind, type EventSequence, type ExecutionMode, ExecutionModeSchema, type HardkasConfig, HardkasError, type InvariantDomain, type InvariantSeverity, InvariantViolationError, type KaspaAddress, LOCK_ORDER, type LineageId, type LockHandle, type LockMetadata, type NetworkId, NetworkIdSchema, type RpcEndpointId, SOMPI_PER_KAS, type StampedEvent, type TxId, type UnknownEventPayload, type WorkflowId, type WriteFileAtomicOptions, acquireLock, artifactTypeSchema, asArtifactId, asContentHash, asCorrelationId, asDaaScore, asEventId, asEventSequence, asKaspaAddress, asLineageId, asNetworkId, asRpcEndpointId, asTxId, asWorkflowId, clearLock, coreEvents, createEventEnvelope, executionModeSchema, formatCorruptionIssue, formatSompi, hardkasConfigSchema, isProcessAlive, kaspaNetworkIdSchema, listLocks, maskSecrets, parseHardkasConfig, parseKasToSompi, redactSecret, validateEventEnvelope, withLock, withLocks, writeFileAtomic, writeFileAtomicSync };
|
|
667
|
+
export { type AcquireLockArgs, type ArtifactId, type ArtifactType, ArtifactTypeSchema, type Brand, type Branded, type ContentHash, type CoreEvent, type CoreEventListener, type CorrelationId, type CorruptionCode, type CorruptionIssue, type CorruptionSeverity, type CreateSnapshotOptions, type DaaScore, type DeterministicClock, type DeterministicDiff, type DeterministicRandom, type EventDomain, type EventEnvelope, type EventId, type EventKind, type EventPayloadByKind, type EventSequence, type ExecutionMode, ExecutionModeSchema, type HardkasConfig, HardkasError, type IdProvider, type IntegrityStatus, type InvariantDomain, type InvariantSeverity, InvariantViolationError, type KaspaAddress, LOCK_ORDER, type LayeredReplayDiff, type LineageId, type LockHandle, type LockMetadata, type NetworkId, NetworkIdSchema, type RpcEndpointId, type RuntimeContext, type RuntimeNoiseDiff, SOMPI_PER_KAS, type SnapshotManifest, type StampedEvent, type StateProvenance, type StructuralDiff, type TxId, type UnknownEventPayload, type WorkflowId, type WriteFileAtomicOptions, acquireLock, artifactTypeSchema, asArtifactId, asContentHash, asCorrelationId, asDaaScore, asEventId, asEventSequence, asKaspaAddress, asLineageId, asNetworkId, asRpcEndpointId, asTxId, asWorkflowId, clearLock, coreEvents, createEventEnvelope, createSnapshot, deterministicCompare, diffReplays, executionModeSchema, formatCorruptionIssue, formatSompi, hardkasConfigSchema, isProcessAlive, kaspaNetworkIdSchema, listLocks, maskSecrets, parseHardkasConfig, parseKasToSompi, readSnapshotManifest, redactSecret, systemRuntimeContext, validateEventEnvelope, withLock, withLocks, writeFileAtomic, writeFileAtomicSync };
|
package/dist/index.js
CHANGED
|
@@ -37,13 +37,16 @@ var CoreEventBus = class {
|
|
|
37
37
|
};
|
|
38
38
|
var coreEvents = new CoreEventBus();
|
|
39
39
|
function createEventEnvelope(params) {
|
|
40
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
40
41
|
return {
|
|
41
42
|
schema: "hardkas.event",
|
|
42
43
|
version: "1.0.0",
|
|
43
44
|
eventId: params.eventId || crypto.randomUUID(),
|
|
44
45
|
domain: params.domain,
|
|
45
46
|
kind: params.kind,
|
|
46
|
-
timestamp
|
|
47
|
+
timestamp,
|
|
48
|
+
emittedAt: timestamp,
|
|
49
|
+
sourceSubsystem: params.sourceSubsystem,
|
|
47
50
|
workflowId: params.workflowId,
|
|
48
51
|
correlationId: params.correlationId,
|
|
49
52
|
causationId: params.causationId,
|
|
@@ -51,7 +54,8 @@ function createEventEnvelope(params) {
|
|
|
51
54
|
txId: params.txId,
|
|
52
55
|
networkId: params.networkId,
|
|
53
56
|
payload: params.payload,
|
|
54
|
-
|
|
57
|
+
sequenceNumber: params.sequenceNumber,
|
|
58
|
+
globalOffset: params.globalOffset
|
|
55
59
|
};
|
|
56
60
|
}
|
|
57
61
|
function validateEventEnvelope(event) {
|
|
@@ -114,10 +118,11 @@ function redactSecret(value) {
|
|
|
114
118
|
// src/fs.ts
|
|
115
119
|
import fs from "fs";
|
|
116
120
|
import path from "path";
|
|
121
|
+
import crypto2 from "crypto";
|
|
117
122
|
async function writeFileAtomic(targetPath, data, options = {}) {
|
|
118
123
|
const dir = path.dirname(targetPath);
|
|
119
124
|
const base = path.basename(targetPath);
|
|
120
|
-
const tempPath = path.join(dir, `.tmp.${base}.${
|
|
125
|
+
const tempPath = path.join(dir, `.tmp.${base}.${crypto2.randomUUID()}`);
|
|
121
126
|
let fd = null;
|
|
122
127
|
try {
|
|
123
128
|
if (!fs.existsSync(dir)) {
|
|
@@ -179,7 +184,7 @@ async function writeFileAtomic(targetPath, data, options = {}) {
|
|
|
179
184
|
function writeFileAtomicSync(targetPath, data, options = {}) {
|
|
180
185
|
const dir = path.dirname(targetPath);
|
|
181
186
|
const base = path.basename(targetPath);
|
|
182
|
-
const tempPath = path.join(dir, `.tmp.${base}.${
|
|
187
|
+
const tempPath = path.join(dir, `.tmp.${base}.${crypto2.randomUUID()}`);
|
|
183
188
|
let fd = null;
|
|
184
189
|
try {
|
|
185
190
|
if (!fs.existsSync(dir)) {
|
|
@@ -420,6 +425,150 @@ function clearLock(rootDir, name, options = {}) {
|
|
|
420
425
|
return { cleared: true };
|
|
421
426
|
}
|
|
422
427
|
|
|
428
|
+
// src/replay.ts
|
|
429
|
+
function diffReplays(replayA, replayB) {
|
|
430
|
+
const diff = {
|
|
431
|
+
schema: "hardkas.replayDiff.v1",
|
|
432
|
+
structural: {
|
|
433
|
+
missingArtifacts: [],
|
|
434
|
+
excludedArtifacts: [],
|
|
435
|
+
missingProjections: []
|
|
436
|
+
},
|
|
437
|
+
deterministic: {
|
|
438
|
+
stateRootDiverged: false,
|
|
439
|
+
lineageDiverged: false,
|
|
440
|
+
graphDiverged: false,
|
|
441
|
+
differences: []
|
|
442
|
+
},
|
|
443
|
+
observational: {
|
|
444
|
+
timestampShifts: [],
|
|
445
|
+
eventOrderingShifts: [],
|
|
446
|
+
metadataDrift: []
|
|
447
|
+
}
|
|
448
|
+
};
|
|
449
|
+
if (replayA.artifacts?.length !== replayB.artifacts?.length) {
|
|
450
|
+
diff.structural.missingArtifacts.push("artifact_count_mismatch");
|
|
451
|
+
}
|
|
452
|
+
const stateA = replayA.stateRoot || replayA.postStateHash;
|
|
453
|
+
const stateB = replayB.stateRoot || replayB.postStateHash;
|
|
454
|
+
if (stateA !== stateB) {
|
|
455
|
+
diff.deterministic.stateRootDiverged = true;
|
|
456
|
+
diff.deterministic.differences.push({
|
|
457
|
+
path: "stateRoot",
|
|
458
|
+
a: stateA,
|
|
459
|
+
b: stateB
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
if (replayA.amountSompi !== void 0 && replayB.amountSompi !== void 0 && replayA.amountSompi !== replayB.amountSompi) {
|
|
463
|
+
diff.deterministic.differences.push({
|
|
464
|
+
path: "amountSompi",
|
|
465
|
+
a: replayA.amountSompi,
|
|
466
|
+
b: replayB.amountSompi
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
const tsA = replayA.timestamp || replayA.createdAt;
|
|
470
|
+
const tsB = replayB.timestamp || replayB.createdAt;
|
|
471
|
+
if (tsA && tsB) {
|
|
472
|
+
const timeA = new Date(tsA).getTime();
|
|
473
|
+
const timeB = new Date(tsB).getTime();
|
|
474
|
+
if (timeA !== timeB) {
|
|
475
|
+
diff.observational.timestampShifts.push({ path: "timestamp", shiftMs: Math.abs(timeA - timeB) });
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
return diff;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// src/snapshot.ts
|
|
482
|
+
import fs3 from "fs/promises";
|
|
483
|
+
import path3 from "path";
|
|
484
|
+
async function createSnapshot(options) {
|
|
485
|
+
const { hardkasDir, outputDir, deterministicScope = "local-only" } = options;
|
|
486
|
+
await fs3.mkdir(outputDir, { recursive: true });
|
|
487
|
+
await fs3.mkdir(path3.join(outputDir, "artifacts"), { recursive: true });
|
|
488
|
+
await fs3.mkdir(path3.join(outputDir, "projections"), { recursive: true });
|
|
489
|
+
await fs3.mkdir(path3.join(outputDir, "events"), { recursive: true });
|
|
490
|
+
await fs3.mkdir(path3.join(outputDir, "replay"), { recursive: true });
|
|
491
|
+
await fs3.mkdir(path3.join(outputDir, "metadata"), { recursive: true });
|
|
492
|
+
let included = 0;
|
|
493
|
+
let excluded = 0;
|
|
494
|
+
let corrupted = 0;
|
|
495
|
+
const artifactsDir = path3.join(hardkasDir, "artifacts");
|
|
496
|
+
try {
|
|
497
|
+
const list = await fs3.readdir(artifactsDir);
|
|
498
|
+
for (const f of list) {
|
|
499
|
+
if (f.endsWith(".json")) {
|
|
500
|
+
const src = path3.join(artifactsDir, f);
|
|
501
|
+
const dest = path3.join(outputDir, "artifacts", f);
|
|
502
|
+
try {
|
|
503
|
+
const content = await fs3.readFile(src, "utf-8");
|
|
504
|
+
const parsed = JSON.parse(content);
|
|
505
|
+
if (parsed.schema && parsed.schema.startsWith("hardkas.")) {
|
|
506
|
+
await fs3.copyFile(src, dest);
|
|
507
|
+
included++;
|
|
508
|
+
} else {
|
|
509
|
+
excluded++;
|
|
510
|
+
}
|
|
511
|
+
} catch {
|
|
512
|
+
corrupted++;
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
} catch {
|
|
517
|
+
}
|
|
518
|
+
try {
|
|
519
|
+
const eventsLog = path3.join(hardkasDir, "events.jsonl");
|
|
520
|
+
await fs3.copyFile(eventsLog, path3.join(outputDir, "events", "events.jsonl"));
|
|
521
|
+
} catch {
|
|
522
|
+
}
|
|
523
|
+
try {
|
|
524
|
+
const dbPath = path3.join(hardkasDir, "store.db");
|
|
525
|
+
await fs3.copyFile(dbPath, path3.join(outputDir, "projections", "store.db"));
|
|
526
|
+
} catch {
|
|
527
|
+
}
|
|
528
|
+
const manifest = {
|
|
529
|
+
snapshotVersion: 1,
|
|
530
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
531
|
+
hardkasVersion: "0.6.0-alpha",
|
|
532
|
+
stateAuthority: "filesystem",
|
|
533
|
+
projectionAuthority: "sqlite",
|
|
534
|
+
deterministicScope,
|
|
535
|
+
consensusValidated: deterministicScope === "consensus-validated",
|
|
536
|
+
includedArtifacts: included,
|
|
537
|
+
excludedArtifacts: excluded,
|
|
538
|
+
corruptedArtifacts: corrupted
|
|
539
|
+
};
|
|
540
|
+
await fs3.writeFile(
|
|
541
|
+
path3.join(outputDir, "manifest.json"),
|
|
542
|
+
JSON.stringify(manifest, null, 2),
|
|
543
|
+
"utf-8"
|
|
544
|
+
);
|
|
545
|
+
return manifest;
|
|
546
|
+
}
|
|
547
|
+
async function readSnapshotManifest(snapshotDir) {
|
|
548
|
+
const manifestPath = path3.join(snapshotDir, "manifest.json");
|
|
549
|
+
const content = await fs3.readFile(manifestPath, "utf-8");
|
|
550
|
+
return JSON.parse(content);
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// src/deterministic.ts
|
|
554
|
+
function deterministicCompare(a, b) {
|
|
555
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// src/runtime-context.ts
|
|
559
|
+
var systemRuntimeContext = {
|
|
560
|
+
clock: {
|
|
561
|
+
now: () => Date.now()
|
|
562
|
+
},
|
|
563
|
+
random: {
|
|
564
|
+
next: () => Math.random()
|
|
565
|
+
},
|
|
566
|
+
ids: {
|
|
567
|
+
execution: () => `exec_${Date.now().toString(36)}`,
|
|
568
|
+
workflow: () => `wf_${Date.now().toString(36)}`
|
|
569
|
+
}
|
|
570
|
+
};
|
|
571
|
+
|
|
423
572
|
// src/index.ts
|
|
424
573
|
var SOMPI_PER_KAS = 100000000n;
|
|
425
574
|
var kaspaNetworkIdSchema = z.enum([
|
|
@@ -429,7 +578,8 @@ var kaspaNetworkIdSchema = z.enum([
|
|
|
429
578
|
"testnet-12",
|
|
430
579
|
"simnet",
|
|
431
580
|
"simnet-1",
|
|
432
|
-
"devnet"
|
|
581
|
+
"devnet",
|
|
582
|
+
"simulated"
|
|
433
583
|
]);
|
|
434
584
|
var executionModeSchema = z.enum([
|
|
435
585
|
"simulated",
|
|
@@ -441,7 +591,8 @@ var artifactTypeSchema = z.enum([
|
|
|
441
591
|
"signedTx",
|
|
442
592
|
"txReceipt",
|
|
443
593
|
"txTrace",
|
|
444
|
-
"snapshot"
|
|
594
|
+
"snapshot",
|
|
595
|
+
"workflow.v1"
|
|
445
596
|
]);
|
|
446
597
|
var NetworkIdSchema = kaspaNetworkIdSchema;
|
|
447
598
|
var ExecutionModeSchema = executionModeSchema;
|
|
@@ -534,6 +685,9 @@ export {
|
|
|
534
685
|
clearLock,
|
|
535
686
|
coreEvents,
|
|
536
687
|
createEventEnvelope,
|
|
688
|
+
createSnapshot,
|
|
689
|
+
deterministicCompare,
|
|
690
|
+
diffReplays,
|
|
537
691
|
executionModeSchema,
|
|
538
692
|
formatCorruptionIssue,
|
|
539
693
|
formatSompi,
|
|
@@ -544,7 +698,9 @@ export {
|
|
|
544
698
|
maskSecrets,
|
|
545
699
|
parseHardkasConfig,
|
|
546
700
|
parseKasToSompi,
|
|
701
|
+
readSnapshotManifest,
|
|
547
702
|
redactSecret,
|
|
703
|
+
systemRuntimeContext,
|
|
548
704
|
validateEventEnvelope,
|
|
549
705
|
withLock,
|
|
550
706
|
withLocks,
|