@hardkas/artifacts 0.2.2-alpha.1 → 0.4.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.
Files changed (3) hide show
  1. package/dist/index.d.ts +115 -14
  2. package/dist/index.js +253 -92
  3. package/package.json +3 -3
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
- import { ArtifactType, NetworkId, ExecutionMode, ContentHash, WorkflowId, ArtifactId, LineageId, EventSequence, KaspaAddress, TxId, EventEnvelope } from '@hardkas/core';
1
+ import { ArtifactType, NetworkId, ExecutionMode, ContentHash, WorkflowId, ArtifactId, LineageId, EventSequence, TxId, KaspaAddress, CorruptionCode, CorruptionSeverity, EventEnvelope } from '@hardkas/core';
2
2
  import { z } from 'zod';
3
3
  import { TxOutput, Utxo, TxPlan as TxPlan$1 } from '@hardkas/tx-builder';
4
4
 
5
- declare const HARDKAS_VERSION = "0.2.2-alpha.1";
5
+ declare const HARDKAS_VERSION = "0.4.0-alpha";
6
6
  declare const ARTIFACT_SCHEMAS: {
7
7
  readonly LOCALNET_STATE: "hardkas.localnetState.v1";
8
8
  readonly REAL_ACCOUNT_STORE: "hardkas.realAccountStore.v1";
@@ -1058,6 +1058,7 @@ type Snapshot = z.infer<typeof SnapshotSchema>;
1058
1058
  type TxReceipt = z.infer<typeof TxReceiptSchema>;
1059
1059
  type SignedTx = z.infer<typeof SignedTxSchema>;
1060
1060
  type TxTrace = z.infer<typeof TxTraceSchema>;
1061
+ type DagContext$1 = z.infer<typeof DagContextSchema>;
1061
1062
 
1062
1063
  interface ArtifactValidationResult {
1063
1064
  ok: boolean;
@@ -1385,18 +1386,70 @@ interface TxTraceArtifact extends BaseArtifact<"txTrace"> {
1385
1386
  dagContext?: DagContext | undefined;
1386
1387
  }
1387
1388
 
1389
+ interface DeploymentRecord extends HardkasArtifactBase {
1390
+ schema: "hardkas.deployment.v1";
1391
+ /** Human-readable label (e.g., "initial-funding", "vault-covenant-v1") */
1392
+ label: string;
1393
+ /** Network where this was deployed */
1394
+ networkId: NetworkId;
1395
+ /** Deployment status */
1396
+ status: "planned" | "sent" | "confirmed" | "failed" | "unknown";
1397
+ /** The transaction ID (if sent) */
1398
+ txId?: TxId;
1399
+ /** Reference to the plan artifact that produced this deployment */
1400
+ planArtifactId?: ArtifactId;
1401
+ /** Reference to the receipt artifact (if confirmed) */
1402
+ receiptArtifactId?: ArtifactId;
1403
+ /** Deployed addresses or outputs (for covenant/contract deployments) */
1404
+ deployedAddresses?: string[];
1405
+ /** Deployment metadata */
1406
+ deployer?: string;
1407
+ /** Content hash of the deployed payload (bytecode, script, or tx content) */
1408
+ payloadHash?: string;
1409
+ /** Timestamp of deployment */
1410
+ deployedAt: string;
1411
+ /** HardKAS version used */
1412
+ hardkasVersion: string;
1413
+ /** Canonical content hash of this record */
1414
+ contentHash?: ContentHash;
1415
+ /** Notes */
1416
+ notes?: string;
1417
+ }
1418
+ interface DeploymentIndex extends HardkasArtifactBase {
1419
+ schema: "hardkas.deploymentIndex.v1";
1420
+ networkId: NetworkId;
1421
+ deployments: DeploymentSummary[];
1422
+ lastUpdated: string;
1423
+ }
1424
+ interface DeploymentSummary {
1425
+ label: string;
1426
+ networkId: NetworkId;
1427
+ status: string;
1428
+ txId?: string;
1429
+ deployedAt: string;
1430
+ contentHash: string;
1431
+ }
1432
+
1433
+ declare const SEMANTIC_EXCLUSIONS: Set<string>;
1434
+ /**
1435
+ * Current canonicalization version.
1436
+ * v1: BigInt(123) -> "123" (Collision with String "123")
1437
+ * v2: BigInt(123) -> "n:123" (Distinguishable)
1438
+ * v3: String normalization (\r\n -> \n, NFC) for cross-platform stability.
1439
+ */
1440
+ declare const CURRENT_HASH_VERSION = 3;
1388
1441
  /**
1389
1442
  * Deterministically stringifies an object by sorting keys recursively.
1390
- * Handles BigInt by converting to string.
1391
- * Excludes 'contentHash', 'artifactId', and 'lineage' fields during serialization.
1443
+ * Handles BigInt by converting to string with type marker.
1444
+ * Excludes fields in SEMANTIC_EXCLUSIONS during serialization.
1392
1445
  * Skips keys with undefined values (matching JSON.stringify behavior).
1393
1446
  */
1394
- declare function canonicalStringify(obj: any): string;
1447
+ declare function canonicalStringify(obj: any, version?: number): string;
1395
1448
  /**
1396
1449
  * Calculates a SHA-256 hash of the canonical JSON representation.
1397
- * Always excludes the 'contentHash' field from the calculation.
1450
+ * Always excludes fields in SEMANTIC_EXCLUSIONS from the calculation.
1398
1451
  */
1399
- declare function calculateContentHash(obj: any): string;
1452
+ declare function calculateContentHash(obj: any, version?: number): string;
1400
1453
 
1401
1454
  interface Clock {
1402
1455
  now(): number;
@@ -1408,12 +1461,13 @@ interface VerificationContext {
1408
1461
  networkId?: NetworkId;
1409
1462
  parent?: unknown;
1410
1463
  }
1411
- type VerificationSeverity = "info" | "warning" | "error" | "critical";
1464
+ type VerificationSeverity = CorruptionSeverity | "info" | "critical";
1412
1465
  type VerificationIssue = {
1413
- code: string;
1466
+ code: CorruptionCode | string;
1414
1467
  severity: VerificationSeverity;
1415
1468
  message: string;
1416
1469
  path?: string | undefined;
1470
+ pathStr?: string | undefined;
1417
1471
  artifactId?: string | undefined;
1418
1472
  };
1419
1473
  type ArtifactVerificationResult = {
@@ -1428,7 +1482,7 @@ type ArtifactVerificationResult = {
1428
1482
  /**
1429
1483
  * Sorts UTXOs deterministically by outpoint (transactionId:index).
1430
1484
  */
1431
- declare function sortUtxosByOutpoint(utxos: unknown[]): unknown[];
1485
+ declare function sortUtxosByOutpoint<T>(utxos: T[]): T[];
1432
1486
  /**
1433
1487
  * Verifies an artifact's integrity.
1434
1488
  * Can take a raw object or a file path.
@@ -1440,9 +1494,9 @@ declare function verifyArtifactIntegrity(artifactOrPath: unknown): Promise<Artif
1440
1494
  declare function verifyArtifactSemantics(artifact: unknown, context?: VerificationContext): ArtifactVerificationResult;
1441
1495
  /**
1442
1496
  * Verifies an artifact's replay consistency.
1443
- * Contract/Stub for Phase 4.
1497
+ * Honest implementation: reports as unsupported or not implemented.
1444
1498
  */
1445
- declare function verifyArtifactReplay(artifact: unknown, context?: VerificationContext): Promise<ArtifactVerificationResult>;
1499
+ declare function verifyArtifactReplay(artifact: unknown, _context?: VerificationContext): Promise<ArtifactVerificationResult>;
1446
1500
  /**
1447
1501
  * @deprecated Use verifyArtifactIntegrity instead.
1448
1502
  */
@@ -1651,6 +1705,9 @@ declare function createSimulatedTxReceipt(plan: TxPlan, txId: string, extra?: {
1651
1705
  spentUtxoIds?: string[];
1652
1706
  createdUtxoIds?: string[];
1653
1707
  daaScore?: string;
1708
+ preStateHash?: string;
1709
+ postStateHash?: string;
1710
+ dagContext?: DagContext$1;
1654
1711
  }): TxReceipt;
1655
1712
  /**
1656
1713
  * Validates and extracts the raw transaction from a signed artifact.
@@ -1748,9 +1805,53 @@ interface LineageValidationResult {
1748
1805
  ok: boolean;
1749
1806
  issues: VerificationIssue[];
1750
1807
  }
1808
+ interface LineageOptions {
1809
+ strict?: boolean;
1810
+ }
1751
1811
  /**
1752
1812
  * Validates the lineage relationship between an artifact and its parent.
1753
1813
  */
1754
- declare function verifyLineage(artifact: any, parent?: any): LineageValidationResult;
1814
+ declare function verifyLineage(artifact: any, parent?: any, options?: LineageOptions): LineageValidationResult;
1815
+
1816
+ interface DiffEntry {
1817
+ path: string;
1818
+ kind: "added" | "removed" | "changed";
1819
+ left?: any;
1820
+ right?: any;
1821
+ }
1822
+ interface ArtifactDiff {
1823
+ identical: boolean;
1824
+ entries: DiffEntry[];
1825
+ }
1826
+ /**
1827
+ * Performs a semantic diff between two artifacts, ignoring volatile metadata.
1828
+ * Uses the same exclusion rules as canonical hashing.
1829
+ * Redacts secrets from the output.
1830
+ */
1831
+ declare function diffArtifacts(left: any, right: any): ArtifactDiff;
1832
+
1833
+ declare function createDeploymentRecord(opts: {
1834
+ label: string;
1835
+ networkId: NetworkId;
1836
+ status?: "planned" | "sent" | "confirmed" | "failed" | "unknown";
1837
+ txId?: TxId;
1838
+ planArtifactId?: ArtifactId;
1839
+ receiptArtifactId?: ArtifactId;
1840
+ deployedAddresses?: string[];
1841
+ deployer?: string;
1842
+ payloadHash?: string;
1843
+ notes?: string;
1844
+ }): DeploymentRecord;
1845
+ declare function updateDeploymentStatus(record: DeploymentRecord, newStatus: DeploymentRecord["status"], txId?: TxId): DeploymentRecord;
1846
+
1847
+ /**
1848
+ * Manages deployment records on the filesystem.
1849
+ * Storage: .hardkas/deployments/<networkId>/<label>.json
1850
+ */
1851
+ declare function saveDeployment(rootDir: string, record: DeploymentRecord): Promise<string>;
1852
+ declare function loadDeployment(rootDir: string, networkId: string, label: string): Promise<DeploymentRecord | null>;
1853
+ declare function listDeployments(rootDir: string, networkId?: string): Promise<DeploymentSummary[]>;
1854
+ declare function updateDeployment(rootDir: string, networkId: string, label: string, update: Partial<DeploymentRecord>): Promise<DeploymentRecord>;
1855
+ declare function deleteDeployment(rootDir: string, networkId: string, label: string): Promise<boolean>;
1755
1856
 
1756
- export { ARTIFACT_SCHEMAS, ARTIFACT_VERSION, AccountRefSchema, type ArtifactExplanation, ArtifactLineageSchema, type ArtifactLookup, type ArtifactValidationResult, type ArtifactVerificationResult, type AssumptionLevel, type BaseArtifact, BaseArtifactSchema, BasicCorrelationInvariant, BasicLineageInvariant, type Clock, type CreateTxPlanArtifactOptions, type DagContext, DagContextSchema, type FeeAuditResult, HARDKAS_VERSION, type HardkasArtifactBase, type HardkasArtifactMode, type HardkasArtifactSchema, HashInvariant, type IgraSignedTxArtifact, type IgraTxPlanArtifact, type IgraTxReceiptArtifact, type IgraTxRequestArtifact, type Invariant, type InvariantContext, type InvariantViolation, InvariantWatcher, LifecycleInvariant, type LineageValidationResult, LocalnetUtxoSchemaV2, NetworkInvariant, ReplayInvariant, SchemaInvariant, type SignedTx, type SignedTxArtifact, type SignedTxArtifactV1, SignedTxSchema, type Snapshot, type SnapshotArtifact, SnapshotSchema, type TxOutputArtifact, type TxPlan, type TxPlanArtifact, type TxPlanArtifactV1, TxPlanSchema, type TxReceipt, type TxReceiptArtifact, type TxReceiptArtifactV1, TxReceiptSchema, type TxTrace, type TxTraceArtifact, type TxTraceArtifactV1, TxTraceSchema, type UtxoArtifact, type VerificationContext, type VerificationIssue, type VerificationSeverity, type WatcherOptions, assertDecimalBigIntString, assertEvmAddress, assertEvmTxHash, assertHexData, assertValidIgraSignedTxArtifact, assertValidIgraTxPlanArtifact, assertValidIgraTxReceiptArtifact, assertValidSignedTxArtifact, assertValidTxPlanArtifact, assertValidTxReceiptArtifact, bigIntReplacer, calculateContentHash, canonicalStringify, createIgraDeployPlanId, createIgraPlanId, createIgraSignedId, createSimulatedSignedTxArtifact, createSimulatedTxReceipt, createTxPlanArtifact, defaultClock, explainArtifact, formatSignedTxArtifact, formatTxPlanArtifact, formatTxReceiptArtifact, getBroadcastableSignedTransaction, getDefaultL2ReceiptsDir, getDefaultReceiptPath, getL2ReceiptPath, isIgraTxPlanArtifact, listIgraTxReceiptArtifacts, loadIgraTxReceiptArtifact, migrateToCanonical, readArtifact, readSignedTxArtifact, readTxPlanArtifact, readTxReceiptArtifact, recomputeMass, saveIgraTxReceiptArtifact, sortUtxosByOutpoint, txOutputFromArtifact, txOutputToArtifact, utxoFromArtifact, utxoToArtifact, validateArtifact, validateIgraSignedTxArtifact, validateIgraTxPlanArtifact, validateIgraTxReceiptArtifact, validateSignedTxArtifact, validateTxPlanArtifact, validateTxReceiptArtifact, verifyArtifact, verifyArtifactFile, verifyArtifactIntegrity, verifyArtifactReplay, verifyArtifactSemantics, verifyFeeSemantics, verifyLineage, writeArtifact };
1857
+ export { ARTIFACT_SCHEMAS, ARTIFACT_VERSION, AccountRefSchema, type ArtifactDiff, type ArtifactExplanation, ArtifactLineageSchema, type ArtifactLookup, type ArtifactValidationResult, type ArtifactVerificationResult, type AssumptionLevel, type BaseArtifact, BaseArtifactSchema, BasicCorrelationInvariant, BasicLineageInvariant, CURRENT_HASH_VERSION, type Clock, type CreateTxPlanArtifactOptions, type DagContext, DagContextSchema, type DeploymentIndex, type DeploymentRecord, type DeploymentSummary, type DiffEntry, type FeeAuditResult, HARDKAS_VERSION, type HardkasArtifactBase, type HardkasArtifactMode, type HardkasArtifactSchema, HashInvariant, type IgraSignedTxArtifact, type IgraTxPlanArtifact, type IgraTxReceiptArtifact, type IgraTxRequestArtifact, type Invariant, type InvariantContext, type InvariantViolation, InvariantWatcher, LifecycleInvariant, type LineageOptions, type LineageValidationResult, LocalnetUtxoSchemaV2, NetworkInvariant, ReplayInvariant, SEMANTIC_EXCLUSIONS, SchemaInvariant, type SignedTx, type SignedTxArtifact, type SignedTxArtifactV1, SignedTxSchema, type Snapshot, type SnapshotArtifact, SnapshotSchema, type TxOutputArtifact, type TxPlan, type TxPlanArtifact, type TxPlanArtifactV1, TxPlanSchema, type TxReceipt, type TxReceiptArtifact, type TxReceiptArtifactV1, TxReceiptSchema, type TxTrace, type TxTraceArtifact, type TxTraceArtifactV1, TxTraceSchema, type UtxoArtifact, type VerificationContext, type VerificationIssue, type VerificationSeverity, type WatcherOptions, assertDecimalBigIntString, assertEvmAddress, assertEvmTxHash, assertHexData, assertValidIgraSignedTxArtifact, assertValidIgraTxPlanArtifact, assertValidIgraTxReceiptArtifact, assertValidSignedTxArtifact, assertValidTxPlanArtifact, assertValidTxReceiptArtifact, bigIntReplacer, calculateContentHash, canonicalStringify, createDeploymentRecord, createIgraDeployPlanId, createIgraPlanId, createIgraSignedId, createSimulatedSignedTxArtifact, createSimulatedTxReceipt, createTxPlanArtifact, defaultClock, deleteDeployment, diffArtifacts, explainArtifact, formatSignedTxArtifact, formatTxPlanArtifact, formatTxReceiptArtifact, getBroadcastableSignedTransaction, getDefaultL2ReceiptsDir, getDefaultReceiptPath, getL2ReceiptPath, isIgraTxPlanArtifact, listDeployments, listIgraTxReceiptArtifacts, loadDeployment, loadIgraTxReceiptArtifact, migrateToCanonical, readArtifact, readSignedTxArtifact, readTxPlanArtifact, readTxReceiptArtifact, recomputeMass, saveDeployment, saveIgraTxReceiptArtifact, sortUtxosByOutpoint, txOutputFromArtifact, txOutputToArtifact, updateDeployment, updateDeploymentStatus, utxoFromArtifact, utxoToArtifact, validateArtifact, validateIgraSignedTxArtifact, validateIgraTxPlanArtifact, validateIgraTxReceiptArtifact, validateSignedTxArtifact, validateTxPlanArtifact, validateTxReceiptArtifact, verifyArtifact, verifyArtifactFile, verifyArtifactIntegrity, verifyArtifactReplay, verifyArtifactSemantics, verifyFeeSemantics, verifyLineage, writeArtifact };
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/constants.ts
2
- var HARDKAS_VERSION = "0.2.2-alpha.1";
2
+ var HARDKAS_VERSION = "0.4.0-alpha";
3
3
  var ARTIFACT_SCHEMAS = {
4
4
  LOCALNET_STATE: "hardkas.localnetState.v1",
5
5
  REAL_ACCOUNT_STORE: "hardkas.realAccountStore.v1",
@@ -133,42 +133,52 @@ function createIgraDeployPlanId(hash) {
133
133
 
134
134
  // src/canonical.ts
135
135
  import { createHash } from "crypto";
136
- function canonicalStringify(obj) {
136
+ var SEMANTIC_EXCLUSIONS = /* @__PURE__ */ new Set([
137
+ "contentHash",
138
+ "artifactId",
139
+ "planId",
140
+ "lineage",
141
+ "createdAt",
142
+ "rpcUrl",
143
+ "indexedAt",
144
+ "file_path",
145
+ "file_mtime_ms",
146
+ "hardkasVersion",
147
+ "hashVersion",
148
+ // Exclude hash version from hash
149
+ "parentArtifactId",
150
+ "signedId",
151
+ "deployedAt"
152
+ ]);
153
+ var CURRENT_HASH_VERSION = 3;
154
+ function canonicalStringify(obj, version = CURRENT_HASH_VERSION) {
137
155
  if (obj === null || typeof obj !== "object") {
138
156
  if (typeof obj === "bigint") {
139
- return obj.toString();
157
+ if (version >= 2) {
158
+ return JSON.stringify(`n:${obj.toString()}`);
159
+ }
160
+ return JSON.stringify(obj.toString());
161
+ }
162
+ if (typeof obj === "string" && version >= 3) {
163
+ const normalized = obj.normalize("NFC").replace(/\r\n/g, "\n");
164
+ return JSON.stringify(normalized);
140
165
  }
141
166
  return JSON.stringify(obj);
142
167
  }
143
168
  if (Array.isArray(obj)) {
144
- return "[" + obj.map((item) => canonicalStringify(item)).join(",") + "]";
145
- }
146
- const SEMANTIC_EXCLUSIONS = /* @__PURE__ */ new Set([
147
- "contentHash",
148
- "artifactId",
149
- "planId",
150
- "lineage",
151
- "createdAt",
152
- "rpcUrl",
153
- "indexedAt",
154
- "file_path",
155
- "file_mtime_ms",
156
- "hardkasVersion",
157
- "version",
158
- "parentArtifactId",
159
- "signedId"
160
- ]);
169
+ return "[" + obj.map((item) => canonicalStringify(item, version)).join(",") + "]";
170
+ }
161
171
  const sortedKeys = Object.keys(obj).filter(
162
172
  (key) => !SEMANTIC_EXCLUSIONS.has(key) && obj[key] !== void 0
163
173
  ).sort();
164
174
  const result = sortedKeys.map((key) => {
165
175
  const value = obj[key];
166
- return JSON.stringify(key) + ":" + canonicalStringify(value);
176
+ return JSON.stringify(key) + ":" + canonicalStringify(value, version);
167
177
  }).join(",");
168
178
  return "{" + result + "}";
169
179
  }
170
- function calculateContentHash(obj) {
171
- const canonical = canonicalStringify(obj);
180
+ function calculateContentHash(obj, version = CURRENT_HASH_VERSION) {
181
+ const canonical = canonicalStringify(obj, version);
172
182
  return createHash("sha256").update(canonical).digest("hex");
173
183
  }
174
184
 
@@ -397,14 +407,15 @@ function verifyFeeSemantics(artifact) {
397
407
  }
398
408
 
399
409
  // src/lineage.ts
400
- function verifyLineage(artifact, parent) {
410
+ function verifyLineage(artifact, parent, options = {}) {
401
411
  const issues = [];
402
412
  const addIssue = (code, message, severity = "error") => {
403
413
  issues.push({ code, severity, message });
404
414
  };
405
415
  const lineage = artifact.lineage;
406
416
  if (!lineage) {
407
- addIssue("MISSING_LINEAGE", "Artifact has no lineage metadata", "warning");
417
+ const severity = options.strict ? "error" : "warning";
418
+ addIssue("MISSING_LINEAGE", "Artifact has no lineage metadata", severity);
408
419
  return {
409
420
  ok: issues.every((i) => i.severity !== "error"),
410
421
  issues
@@ -429,26 +440,32 @@ function verifyLineage(artifact, parent) {
429
440
  if (!parentLineage) {
430
441
  addIssue("PARENT_MISSING_LINEAGE", "Parent artifact has no lineage metadata");
431
442
  } else {
443
+ if (!lineage.parentArtifactId) {
444
+ addIssue("MISSING_PARENT_ID", "Artifact is missing parentArtifactId reference, but parent was provided for verification.");
445
+ } else if (lineage.parentArtifactId !== parentLineage.artifactId) {
446
+ addIssue("PARENT_ID_MISMATCH", `Parent Artifact ID mismatch: expected ${parentLineage.artifactId}, got ${lineage.parentArtifactId}`);
447
+ }
432
448
  if (lineage.lineageId !== parentLineage.lineageId) {
433
449
  addIssue("LINEAGE_ID_MISMATCH", `Lineage ID mismatch: expected ${parentLineage.lineageId}, got ${lineage.lineageId}`);
434
450
  }
435
451
  if (lineage.rootArtifactId !== parentLineage.rootArtifactId) {
436
- addIssue("ROOT_ID_MISMATCH", `Root Artifact ID mismatch: expected ${parentLineage.rootArtifactId}, got ${lineage.rootArtifactId}`);
437
- }
438
- if (lineage.parentArtifactId && lineage.parentArtifactId !== parentLineage.artifactId) {
439
- addIssue("PARENT_ID_MISMATCH", `Parent Artifact ID mismatch: expected ${parentLineage.artifactId}, got ${lineage.parentArtifactId}`);
452
+ addIssue("ROOT_ARTIFACT_ID_MISMATCH", `Root Artifact ID mismatch: expected ${parentLineage.rootArtifactId}, got ${lineage.rootArtifactId}`);
440
453
  }
441
454
  if (lineage.sequence !== void 0 && parentLineage.sequence !== void 0) {
442
455
  if (lineage.sequence <= parentLineage.sequence) {
443
- addIssue("INVALID_SEQUENCE", `Invalid sequence: current (${lineage.sequence}) must be greater than parent (${parentLineage.sequence})`);
456
+ const severity = options.strict ? "error" : "warning";
457
+ addIssue("NON_MONOTONIC_SEQUENCE", `Non-monotonic sequence: current (${lineage.sequence}) <= parent (${parentLineage.sequence}).`, severity);
444
458
  }
445
459
  }
446
460
  }
447
461
  if (artifact.networkId !== parent.networkId) {
448
- addIssue("NETWORK_CONTAMINATION", `Network mismatch: parent is ${parent.networkId}, current is ${artifact.networkId}`);
462
+ addIssue("NETWORK_MISMATCH", `Network mismatch: parent is ${parent.networkId}, current is ${artifact.networkId}`);
449
463
  }
450
464
  if (artifact.mode !== parent.mode) {
451
- addIssue("MODE_CONTAMINATION", `Mode mismatch: parent is ${parent.mode}, current is ${artifact.mode}`);
465
+ addIssue("MODE_MISMATCH", `Mode mismatch: parent is ${parent.mode}, current is ${artifact.mode}`);
466
+ }
467
+ if (lineage.artifactId === lineage.parentArtifactId) {
468
+ addIssue("SELF_PARENT", "Artifact cannot be its own parent.");
452
469
  }
453
470
  }
454
471
  if (parent) {
@@ -485,9 +502,9 @@ async function verifyArtifactIntegrity(artifactOrPath) {
485
502
  errors: [],
486
503
  issues: []
487
504
  };
488
- const addError = (code, message, path3) => {
505
+ const addError = (code, message, path4) => {
489
506
  result.errors.push(message);
490
- result.issues.push({ code, severity: "error", message, path: path3 });
507
+ result.issues.push({ code, severity: "error", message, path: path4 });
491
508
  };
492
509
  let artifact;
493
510
  try {
@@ -506,21 +523,22 @@ async function verifyArtifactIntegrity(artifactOrPath) {
506
523
  result.version = v.version;
507
524
  result.expectedHash = v.contentHash;
508
525
  if (!v.version || !v.schema) {
509
- addError("MISSING_METADATA", "Missing version or schema (Artifact might be v1 or legacy)");
526
+ addError("ARTIFACT_SCHEMA_MISSING", "Missing version or schema (Artifact might be v1 or legacy)");
510
527
  return result;
511
528
  }
512
529
  const [currentMajor] = ARTIFACT_VERSION.split(".");
513
530
  const [artifactMajor] = v.version.split(".");
514
531
  if (currentMajor !== artifactMajor) {
515
- addError("INCOMPATIBLE_VERSION", `Incompatible version: current system is v${currentMajor}, artifact is v${artifactMajor}`);
532
+ addError("ARTIFACT_SCHEMA_INVALID", `Incompatible version: current system is v${currentMajor}, artifact is v${artifactMajor}`);
516
533
  return result;
517
534
  }
518
- const actualHash = calculateContentHash(v);
535
+ const hashVersion = v.hashVersion || 1;
536
+ const actualHash = calculateContentHash(v, hashVersion);
519
537
  result.actualHash = actualHash;
520
538
  if (!v.contentHash) {
521
- addError("MISSING_HASH", "Missing contentHash field");
539
+ addError("ARTIFACT_HASH_MISMATCH", "Missing contentHash field");
522
540
  } else if (actualHash !== v.contentHash) {
523
- addError("HASH_MISMATCH", `Hash mismatch: expected ${v.contentHash}, got ${actualHash}`);
541
+ addError("ARTIFACT_HASH_MISMATCH", `Hash mismatch: expected ${v.contentHash}, got ${actualHash}`);
524
542
  }
525
543
  let schema;
526
544
  switch (v.schema) {
@@ -545,16 +563,20 @@ async function verifyArtifactIntegrity(artifactOrPath) {
545
563
  if (!validation.success) {
546
564
  validation.error.issues.forEach((e) => {
547
565
  const pathStr = e.path.join(".");
548
- addError("SCHEMA_VALIDATION_ERROR", `${pathStr}: ${e.message}`, pathStr);
566
+ addError("ARTIFACT_SCHEMA_INVALID", `${pathStr}: ${e.message}`, pathStr);
549
567
  });
550
568
  }
551
569
  } else {
552
- addError("UNSUPPORTED_SCHEMA", `Unsupported or unknown artifact schema: ${v.schema}`);
570
+ addError("ARTIFACT_SCHEMA_INVALID", `Unsupported or unknown artifact schema: ${v.schema}`);
553
571
  }
554
572
  result.ok = result.issues.every((i) => i.severity !== "error" && i.severity !== "critical");
555
573
  return result;
556
574
  } catch (e) {
557
- addError("UNEXPECTED_ERROR", `Integrity verification error: ${e.message}`);
575
+ if (e instanceof SyntaxError) {
576
+ addError("ARTIFACT_JSON_INVALID", `Invalid JSON: ${e.message}`);
577
+ } else {
578
+ addError("ARTIFACT_ID_INVALID", `Unexpected verification error: ${e.message}`);
579
+ }
558
580
  return result;
559
581
  }
560
582
  }
@@ -600,22 +622,10 @@ function verifyArtifactSemantics(artifact, context = {}) {
600
622
  });
601
623
  }
602
624
  }
603
- if (v.schema === "hardkas.signedTx" && v.mode === "simulated") {
604
- }
605
- const lineageAudit = verifyLineage(v, context.parent);
625
+ const lineageAudit = verifyLineage(v, context.parent, { strict });
606
626
  if (!lineageAudit.ok || strict && !v.lineage) {
607
- if (!v.lineage && strict) {
608
- addIssue({
609
- code: "MISSING_LINEAGE",
610
- severity: "error",
611
- message: "Strict mode requires formal lineage metadata."
612
- });
613
- }
614
627
  lineageAudit.issues.forEach((issue) => {
615
- addIssue({
616
- ...issue,
617
- severity: strict ? "error" : "warning"
618
- });
628
+ addIssue(issue);
619
629
  });
620
630
  }
621
631
  if (strict) {
@@ -642,31 +652,17 @@ function verifyArtifactSemantics(artifact, context = {}) {
642
652
  });
643
653
  }
644
654
  }
645
- const lineage = v.lineage;
646
- if (lineage) {
647
- const { artifactId, parentArtifactId, rootArtifactId } = lineage;
648
- if (artifactId === parentArtifactId) {
649
- addIssue({
650
- code: "LINEAGE_INCONSISTENCY",
651
- severity: "error",
652
- message: "Artifact cannot be its own parent."
653
- });
654
- }
655
- if (!parentArtifactId && artifactId !== rootArtifactId) {
656
- addIssue({
657
- code: "LINEAGE_INCONSISTENCY",
658
- severity: "error",
659
- message: "Root artifactId must match artifactId when no parent exists."
660
- });
661
- }
662
- }
663
655
  return result;
664
656
  }
665
- async function verifyArtifactReplay(artifact, context = {}) {
657
+ async function verifyArtifactReplay(artifact, _context = {}) {
666
658
  return {
667
- ok: true,
668
- issues: [],
669
- errors: []
659
+ ok: false,
660
+ issues: [{
661
+ code: "REPLAY_UNSUPPORTED_CHECK",
662
+ severity: "warning",
663
+ message: "Replay verification (full consensus simulation) is currently unsupported in this build."
664
+ }],
665
+ errors: ["Replay verification (consensus) unsupported"]
670
666
  };
671
667
  }
672
668
  var verifyArtifact = verifyArtifactIntegrity;
@@ -890,16 +886,11 @@ function migrateToCanonical(v1Artifact) {
890
886
  // src/io.ts
891
887
  import fs2 from "fs/promises";
892
888
  import path from "path";
889
+ import { writeFileAtomic } from "@hardkas/core";
893
890
  var bigIntReplacer = (_key, value) => typeof value === "bigint" ? value.toString() : value;
894
891
  async function writeArtifact(filePath, artifact) {
895
- try {
896
- const dir = path.dirname(filePath);
897
- await fs2.mkdir(dir, { recursive: true });
898
- const content = typeof artifact === "string" ? artifact : JSON.stringify(artifact, bigIntReplacer, 2) + "\n";
899
- await fs2.writeFile(filePath, content, "utf-8");
900
- } catch (error) {
901
- throw new Error(`Failed to write artifact at ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
902
- }
892
+ const content = typeof artifact === "string" ? artifact : JSON.stringify(artifact, bigIntReplacer, 2) + "\n";
893
+ await writeFileAtomic(filePath, content);
903
894
  }
904
895
  function getDefaultReceiptPath(txId, cwd = process.cwd()) {
905
896
  return path.join(cwd, "artifacts", "receipts", `${txId}.json`);
@@ -1061,6 +1052,7 @@ function createTxPlanArtifact(options) {
1061
1052
  schema: "hardkas.txPlan",
1062
1053
  hardkasVersion: HARDKAS_VERSION,
1063
1054
  version: ARTIFACT_VERSION,
1055
+ hashVersion: CURRENT_HASH_VERSION,
1064
1056
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1065
1057
  networkId: options.networkId,
1066
1058
  mode: options.mode,
@@ -1095,7 +1087,7 @@ function createTxPlanArtifact(options) {
1095
1087
  amountSompi: options.plan.change.amountSompi.toString()
1096
1088
  };
1097
1089
  }
1098
- const hash = calculateContentHash(artifact);
1090
+ const hash = calculateContentHash(artifact, CURRENT_HASH_VERSION);
1099
1091
  artifact.planId = `plan-${hash.slice(0, 16)}`;
1100
1092
  artifact.contentHash = hash;
1101
1093
  return artifact;
@@ -1107,7 +1099,7 @@ function createSimulatedSignedTxArtifact(plan, payload) {
1107
1099
  schema: "hardkas.signedTx",
1108
1100
  hardkasVersion: HARDKAS_VERSION,
1109
1101
  version: ARTIFACT_VERSION,
1110
- hashVersion: 1,
1102
+ hashVersion: CURRENT_HASH_VERSION,
1111
1103
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1112
1104
  status: "signed",
1113
1105
  sourcePlanId: plan.planId,
@@ -1121,7 +1113,7 @@ function createSimulatedSignedTxArtifact(plan, payload) {
1121
1113
  payload
1122
1114
  }
1123
1115
  };
1124
- const hash = calculateContentHash(artifact);
1116
+ const hash = calculateContentHash(artifact, CURRENT_HASH_VERSION);
1125
1117
  artifact.signedId = `signed-${hash.slice(0, 16)}`;
1126
1118
  artifact.txId = `simulated-${plan.planId}-${hash.slice(0, 8)}`;
1127
1119
  artifact.contentHash = hash;
@@ -1132,7 +1124,7 @@ function createSimulatedTxReceipt(plan, txId, extra) {
1132
1124
  schema: "hardkas.txReceipt",
1133
1125
  hardkasVersion: HARDKAS_VERSION,
1134
1126
  version: ARTIFACT_VERSION,
1135
- hashVersion: 1,
1127
+ hashVersion: CURRENT_HASH_VERSION,
1136
1128
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1137
1129
  txId,
1138
1130
  status: "accepted",
@@ -1145,9 +1137,12 @@ function createSimulatedTxReceipt(plan, txId, extra) {
1145
1137
  changeSompi: plan.change?.amountSompi,
1146
1138
  spentUtxoIds: extra?.spentUtxoIds,
1147
1139
  createdUtxoIds: extra?.createdUtxoIds,
1148
- daaScore: extra?.daaScore
1140
+ daaScore: extra?.daaScore,
1141
+ preStateHash: extra?.preStateHash,
1142
+ postStateHash: extra?.postStateHash,
1143
+ dagContext: extra?.dagContext
1149
1144
  };
1150
- const hash = calculateContentHash(artifact);
1145
+ const hash = calculateContentHash(artifact, CURRENT_HASH_VERSION);
1151
1146
  artifact.contentHash = hash;
1152
1147
  return artifact;
1153
1148
  }
@@ -1372,6 +1367,162 @@ function validateTxHash(txHash) {
1372
1367
  throw new Error(`Invalid EVM txHash: must be a 0x-prefixed 64-character hex string. Got: ${txHash}`);
1373
1368
  }
1374
1369
  }
1370
+
1371
+ // src/diff.ts
1372
+ import { maskSecrets } from "@hardkas/core";
1373
+ function diffArtifacts(left, right) {
1374
+ const entries = [];
1375
+ const leftRedacted = maskSecrets(left);
1376
+ const rightRedacted = maskSecrets(right);
1377
+ compareRecursive(leftRedacted, rightRedacted, "$", entries);
1378
+ return {
1379
+ identical: entries.length === 0,
1380
+ entries
1381
+ };
1382
+ }
1383
+ function compareRecursive(left, right, path4, entries) {
1384
+ if (isPrimitive(left) || isPrimitive(right)) {
1385
+ if (left !== right) {
1386
+ entries.push({ path: path4, kind: "changed", left, right });
1387
+ }
1388
+ return;
1389
+ }
1390
+ if (Array.isArray(left) || Array.isArray(right)) {
1391
+ if (!Array.isArray(left) || !Array.isArray(right)) {
1392
+ entries.push({ path: path4, kind: "changed", left, right });
1393
+ return;
1394
+ }
1395
+ const maxLength = Math.max(left.length, right.length);
1396
+ for (let i = 0; i < maxLength; i++) {
1397
+ if (i >= left.length) {
1398
+ entries.push({ path: `${path4}[${i}]`, kind: "added", right: right[i] });
1399
+ } else if (i >= right.length) {
1400
+ entries.push({ path: `${path4}[${i}]`, kind: "removed", left: left[i] });
1401
+ } else {
1402
+ compareRecursive(left[i], right[i], `${path4}[${i}]`, entries);
1403
+ }
1404
+ }
1405
+ return;
1406
+ }
1407
+ const leftKeys = Object.keys(left).filter((k) => !SEMANTIC_EXCLUSIONS.has(k));
1408
+ const rightKeys = Object.keys(right).filter((k) => !SEMANTIC_EXCLUSIONS.has(k));
1409
+ const allKeys = /* @__PURE__ */ new Set([...leftKeys, ...rightKeys]);
1410
+ for (const key of allKeys) {
1411
+ const nextPath = path4 === "$" ? key : `${path4}.${key}`;
1412
+ if (!leftKeys.includes(key)) {
1413
+ entries.push({ path: nextPath, kind: "added", right: right[key] });
1414
+ } else if (!rightKeys.includes(key)) {
1415
+ entries.push({ path: nextPath, kind: "removed", left: left[key] });
1416
+ } else {
1417
+ compareRecursive(left[key], right[key], nextPath, entries);
1418
+ }
1419
+ }
1420
+ }
1421
+ function isPrimitive(val) {
1422
+ if (val === null) return true;
1423
+ return typeof val !== "object" && typeof val !== "function";
1424
+ }
1425
+
1426
+ // src/deployment.ts
1427
+ function createDeploymentRecord(opts) {
1428
+ const record = {
1429
+ schema: "hardkas.deployment.v1",
1430
+ label: opts.label,
1431
+ networkId: opts.networkId,
1432
+ status: opts.status || "planned",
1433
+ deployedAt: (/* @__PURE__ */ new Date()).toISOString(),
1434
+ hardkasVersion: HARDKAS_VERSION,
1435
+ version: ARTIFACT_VERSION,
1436
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1437
+ mode: "real"
1438
+ };
1439
+ if (opts.txId) record.txId = opts.txId;
1440
+ if (opts.planArtifactId) record.planArtifactId = opts.planArtifactId;
1441
+ if (opts.receiptArtifactId) record.receiptArtifactId = opts.receiptArtifactId;
1442
+ if (opts.deployedAddresses) record.deployedAddresses = opts.deployedAddresses;
1443
+ if (opts.deployer) record.deployer = opts.deployer;
1444
+ if (opts.payloadHash) record.payloadHash = opts.payloadHash;
1445
+ if (opts.notes) record.notes = opts.notes;
1446
+ record.contentHash = calculateContentHash(record, CURRENT_HASH_VERSION);
1447
+ return record;
1448
+ }
1449
+ function updateDeploymentStatus(record, newStatus, txId) {
1450
+ const updated = {
1451
+ ...record,
1452
+ status: newStatus,
1453
+ deployedAt: (/* @__PURE__ */ new Date()).toISOString()
1454
+ };
1455
+ if (txId) updated.txId = txId;
1456
+ delete updated.contentHash;
1457
+ updated.contentHash = calculateContentHash(updated, CURRENT_HASH_VERSION);
1458
+ return updated;
1459
+ }
1460
+
1461
+ // src/deployment-store.ts
1462
+ import fs4 from "fs/promises";
1463
+ import { existsSync } from "fs";
1464
+ import path3 from "path";
1465
+ import { writeFileAtomic as writeFileAtomic2 } from "@hardkas/core";
1466
+ async function saveDeployment(rootDir, record) {
1467
+ const deploymentsDir = path3.join(rootDir, ".hardkas", "deployments", record.networkId);
1468
+ const targetPath = path3.join(deploymentsDir, `${record.label}.json`);
1469
+ await writeFileAtomic2(targetPath, JSON.stringify(record, null, 2));
1470
+ return targetPath;
1471
+ }
1472
+ async function loadDeployment(rootDir, networkId, label) {
1473
+ const targetPath = path3.join(rootDir, ".hardkas", "deployments", networkId, `${label}.json`);
1474
+ if (!existsSync(targetPath)) return null;
1475
+ const content = await fs4.readFile(targetPath, "utf-8");
1476
+ return JSON.parse(content);
1477
+ }
1478
+ async function listDeployments(rootDir, networkId) {
1479
+ const baseDir = path3.join(rootDir, ".hardkas", "deployments");
1480
+ if (!existsSync(baseDir)) return [];
1481
+ const summaries = [];
1482
+ const networks = networkId ? [networkId] : await fs4.readdir(baseDir);
1483
+ for (const net of networks) {
1484
+ const netDir = path3.join(baseDir, net);
1485
+ if (!existsSync(netDir)) continue;
1486
+ const files = await fs4.readdir(netDir);
1487
+ for (const file of files) {
1488
+ if (!file.endsWith(".json")) continue;
1489
+ try {
1490
+ const content = await fs4.readFile(path3.join(netDir, file), "utf-8");
1491
+ const record = JSON.parse(content);
1492
+ const summary = {
1493
+ label: record.label,
1494
+ networkId: record.networkId,
1495
+ status: record.status,
1496
+ deployedAt: record.deployedAt,
1497
+ contentHash: record.contentHash || ""
1498
+ };
1499
+ if (record.txId) summary.txId = record.txId;
1500
+ summaries.push(summary);
1501
+ } catch (e) {
1502
+ }
1503
+ }
1504
+ }
1505
+ return summaries.sort((a, b) => new Date(b.deployedAt).getTime() - new Date(a.deployedAt).getTime());
1506
+ }
1507
+ async function updateDeployment(rootDir, networkId, label, update) {
1508
+ const existing = await loadDeployment(rootDir, networkId, label);
1509
+ if (!existing) {
1510
+ throw new Error(`Deployment '${label}' not found on network '${networkId}'.`);
1511
+ }
1512
+ const updated = {
1513
+ ...existing,
1514
+ ...update,
1515
+ deployedAt: (/* @__PURE__ */ new Date()).toISOString()
1516
+ };
1517
+ await saveDeployment(rootDir, updated);
1518
+ return updated;
1519
+ }
1520
+ async function deleteDeployment(rootDir, networkId, label) {
1521
+ const targetPath = path3.join(rootDir, ".hardkas", "deployments", networkId, `${label}.json`);
1522
+ if (!existsSync(targetPath)) return false;
1523
+ await fs4.unlink(targetPath);
1524
+ return true;
1525
+ }
1375
1526
  export {
1376
1527
  ARTIFACT_SCHEMAS,
1377
1528
  ARTIFACT_VERSION,
@@ -1380,6 +1531,7 @@ export {
1380
1531
  BaseArtifactSchema,
1381
1532
  BasicCorrelationInvariant,
1382
1533
  BasicLineageInvariant,
1534
+ CURRENT_HASH_VERSION,
1383
1535
  DagContextSchema,
1384
1536
  HARDKAS_VERSION,
1385
1537
  HashInvariant,
@@ -1388,6 +1540,7 @@ export {
1388
1540
  LocalnetUtxoSchemaV2,
1389
1541
  NetworkInvariant,
1390
1542
  ReplayInvariant,
1543
+ SEMANTIC_EXCLUSIONS,
1391
1544
  SchemaInvariant,
1392
1545
  SignedTxSchema,
1393
1546
  SnapshotSchema,
@@ -1407,6 +1560,7 @@ export {
1407
1560
  bigIntReplacer,
1408
1561
  calculateContentHash,
1409
1562
  canonicalStringify,
1563
+ createDeploymentRecord,
1410
1564
  createIgraDeployPlanId,
1411
1565
  createIgraPlanId,
1412
1566
  createIgraSignedId,
@@ -1414,6 +1568,8 @@ export {
1414
1568
  createSimulatedTxReceipt,
1415
1569
  createTxPlanArtifact,
1416
1570
  defaultClock,
1571
+ deleteDeployment,
1572
+ diffArtifacts,
1417
1573
  explainArtifact,
1418
1574
  formatSignedTxArtifact,
1419
1575
  formatTxPlanArtifact,
@@ -1423,7 +1579,9 @@ export {
1423
1579
  getDefaultReceiptPath,
1424
1580
  getL2ReceiptPath,
1425
1581
  isIgraTxPlanArtifact,
1582
+ listDeployments,
1426
1583
  listIgraTxReceiptArtifacts,
1584
+ loadDeployment,
1427
1585
  loadIgraTxReceiptArtifact,
1428
1586
  migrateToCanonical,
1429
1587
  readArtifact,
@@ -1431,10 +1589,13 @@ export {
1431
1589
  readTxPlanArtifact,
1432
1590
  readTxReceiptArtifact,
1433
1591
  recomputeMass,
1592
+ saveDeployment,
1434
1593
  saveIgraTxReceiptArtifact,
1435
1594
  sortUtxosByOutpoint,
1436
1595
  txOutputFromArtifact,
1437
1596
  txOutputToArtifact,
1597
+ updateDeployment,
1598
+ updateDeploymentStatus,
1438
1599
  utxoFromArtifact,
1439
1600
  utxoToArtifact,
1440
1601
  validateArtifact,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hardkas/artifacts",
3
- "version": "0.2.2-alpha.1",
3
+ "version": "0.4.0-alpha",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "author": "Javier Rodriguez",
@@ -24,8 +24,8 @@
24
24
  "types": "./dist/index.d.ts",
25
25
  "dependencies": {
26
26
  "zod": "^3.24.1",
27
- "@hardkas/core": "0.2.2-alpha.1",
28
- "@hardkas/tx-builder": "0.2.2-alpha.1"
27
+ "@hardkas/core": "0.4.0-alpha",
28
+ "@hardkas/tx-builder": "0.4.0-alpha"
29
29
  },
30
30
  "devDependencies": {
31
31
  "tsup": "^8.3.5",