@hardkas/sdk 0.7.6-alpha → 0.7.8-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 +92 -3
- package/dist/index.js +287 -35
- package/package.json +15 -14
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import * as _hardkas_config from '@hardkas/config';
|
|
|
2
2
|
import { LoadedHardkasConfig } from '@hardkas/config';
|
|
3
3
|
export { defineHardkasConfig } from '@hardkas/config';
|
|
4
4
|
import { KaspaRpcClient } from '@hardkas/kaspa-rpc';
|
|
5
|
-
import { NetworkId } from '@hardkas/core';
|
|
5
|
+
import { EventEnvelope, NetworkId } from '@hardkas/core';
|
|
6
6
|
export { ArtifactId, HardkasError, KaspaAddress, LineageId, NetworkId, SOMPI_PER_KAS, TxId, formatSompi, parseKasToSompi } from '@hardkas/core';
|
|
7
7
|
import { TxPlanArtifact, SignedTxArtifact, TxReceiptArtifact, HardkasArtifactBase, WorkflowArtifact } from '@hardkas/artifacts';
|
|
8
8
|
export { ARTIFACT_SCHEMAS, HARDKAS_VERSION, SignedTxArtifact, TxPlanArtifact, TxReceiptArtifact, TxTraceArtifact, createTxPlanArtifact, writeArtifact } from '@hardkas/artifacts';
|
|
@@ -30,6 +30,24 @@ declare class HardkasAccounts {
|
|
|
30
30
|
sompi: bigint;
|
|
31
31
|
formatted: string;
|
|
32
32
|
}>;
|
|
33
|
+
/**
|
|
34
|
+
* Alias for getBalance.
|
|
35
|
+
*/
|
|
36
|
+
balance(accountNameOrAddress: string): Promise<{
|
|
37
|
+
sompi: bigint;
|
|
38
|
+
formatted: string;
|
|
39
|
+
}>;
|
|
40
|
+
/**
|
|
41
|
+
* Lists all configured account names.
|
|
42
|
+
*/
|
|
43
|
+
list(): Promise<string[]>;
|
|
44
|
+
/**
|
|
45
|
+
* Funds an account from another account (defaults to 'default' account).
|
|
46
|
+
*/
|
|
47
|
+
fund(accountNameOrAddress: string, options?: {
|
|
48
|
+
from?: string;
|
|
49
|
+
amount?: string | bigint;
|
|
50
|
+
}): Promise<any>;
|
|
33
51
|
}
|
|
34
52
|
|
|
35
53
|
/**
|
|
@@ -73,6 +91,14 @@ declare class HardkasTx {
|
|
|
73
91
|
receipt: TxReceiptArtifact;
|
|
74
92
|
receiptPath: string;
|
|
75
93
|
}>;
|
|
94
|
+
/**
|
|
95
|
+
* Explicitly appends a signature to a partially signed transaction.
|
|
96
|
+
*/
|
|
97
|
+
appendSignature(plan: SignedTxArtifact, account?: HardkasAccount | string): Promise<SignedTxArtifact>;
|
|
98
|
+
/**
|
|
99
|
+
* Fetches the current status of a transaction by ID.
|
|
100
|
+
*/
|
|
101
|
+
status(txId: string): Promise<any>;
|
|
76
102
|
}
|
|
77
103
|
|
|
78
104
|
/**
|
|
@@ -105,6 +131,21 @@ declare class HardkasQuery {
|
|
|
105
131
|
* Internal lazy-loaded query engine.
|
|
106
132
|
*/
|
|
107
133
|
private getEngine;
|
|
134
|
+
/**
|
|
135
|
+
* Synchronizes the query store with the filesystem artifacts.
|
|
136
|
+
*/
|
|
137
|
+
sync(options?: {
|
|
138
|
+
force?: boolean;
|
|
139
|
+
}): Promise<any>;
|
|
140
|
+
/**
|
|
141
|
+
* Fetches events from the query store.
|
|
142
|
+
*/
|
|
143
|
+
events(filter?: {
|
|
144
|
+
domain?: string;
|
|
145
|
+
kind?: string;
|
|
146
|
+
correlationId?: string;
|
|
147
|
+
artifactId?: string;
|
|
148
|
+
}): Promise<readonly EventEnvelope[]>;
|
|
108
149
|
}
|
|
109
150
|
|
|
110
151
|
/**
|
|
@@ -147,7 +188,28 @@ declare class HardkasReplay {
|
|
|
147
188
|
* Verifies the deterministic artifact lineage of a transaction replay
|
|
148
189
|
* against the mathematically reconstructed localnet state.
|
|
149
190
|
*/
|
|
150
|
-
verify(
|
|
191
|
+
verify(targetOrOptions?: string | {
|
|
192
|
+
schema?: string;
|
|
193
|
+
artifactId?: string;
|
|
194
|
+
} | ReplayVerifyOptions, options?: ReplayVerifyOptions): Promise<ReplayVerifyResult>;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* HardKAS Lineage Module
|
|
199
|
+
* @alpha
|
|
200
|
+
*/
|
|
201
|
+
declare class HardkasLineage {
|
|
202
|
+
private sdk;
|
|
203
|
+
constructor(sdk: Hardkas);
|
|
204
|
+
/**
|
|
205
|
+
* Traces the lineage of an artifact, identifying ancestors and descendants.
|
|
206
|
+
*/
|
|
207
|
+
trace(target: string | {
|
|
208
|
+
artifactId?: string;
|
|
209
|
+
contentHash?: string;
|
|
210
|
+
}, options?: {
|
|
211
|
+
direction?: "ancestors" | "descendants";
|
|
212
|
+
}): Promise<any>;
|
|
151
213
|
}
|
|
152
214
|
|
|
153
215
|
/**
|
|
@@ -217,6 +279,23 @@ declare class HardkasArtifactsManager {
|
|
|
217
279
|
* Reads an artifact by path or ID/hash from the workspace.
|
|
218
280
|
*/
|
|
219
281
|
read(id: string): Promise<any>;
|
|
282
|
+
/**
|
|
283
|
+
* Alias for read().
|
|
284
|
+
*/
|
|
285
|
+
get(id: string): Promise<any>;
|
|
286
|
+
/**
|
|
287
|
+
* Lists all artifacts in the workspace.
|
|
288
|
+
*/
|
|
289
|
+
list(): Promise<any[]>;
|
|
290
|
+
/**
|
|
291
|
+
* Cryptographically verifies the determinism and integrity of an artifact.
|
|
292
|
+
* Throws an error with details if corruption or mismatch is found.
|
|
293
|
+
*/
|
|
294
|
+
verify(target: string | {
|
|
295
|
+
schema?: string;
|
|
296
|
+
artifactId?: string;
|
|
297
|
+
contentHash?: string;
|
|
298
|
+
}): Promise<any>;
|
|
220
299
|
}
|
|
221
300
|
|
|
222
301
|
interface WorkflowRunOptions {
|
|
@@ -256,6 +335,15 @@ interface HardkasOptions {
|
|
|
256
335
|
cwd?: string;
|
|
257
336
|
configPath?: string;
|
|
258
337
|
mode?: "developer" | "agent";
|
|
338
|
+
network?: string;
|
|
339
|
+
autoBootstrap?: boolean;
|
|
340
|
+
logger?: {
|
|
341
|
+
info: (msg: string) => void;
|
|
342
|
+
warn: (msg: string) => void;
|
|
343
|
+
error: (msg: string) => void;
|
|
344
|
+
debug: (msg: string) => void;
|
|
345
|
+
[key: string]: any;
|
|
346
|
+
};
|
|
259
347
|
policy?: {
|
|
260
348
|
allowNetwork?: boolean;
|
|
261
349
|
allowMainnet?: boolean;
|
|
@@ -279,6 +367,7 @@ declare class Hardkas {
|
|
|
279
367
|
readonly query: HardkasQuery;
|
|
280
368
|
readonly localnet: HardkasLocalnet;
|
|
281
369
|
readonly replay: HardkasReplay;
|
|
370
|
+
readonly lineage: HardkasLineage;
|
|
282
371
|
readonly workflow: HardkasWorkflow;
|
|
283
372
|
readonly mode: "developer" | "agent";
|
|
284
373
|
readonly policy: Required<NonNullable<HardkasOptions["policy"]>>;
|
|
@@ -306,4 +395,4 @@ declare class Hardkas {
|
|
|
306
395
|
enforcePolicy(action: "network" | "mainnet" | "external-wallet" | "mutation", context?: string): void;
|
|
307
396
|
}
|
|
308
397
|
|
|
309
|
-
export { Hardkas, HardkasAccounts, HardkasArtifactsManager, HardkasL2, HardkasLocalnet, type HardkasOptions, HardkasQuery, HardkasReplay, HardkasTx, HardkasWorkspace, type TaskArgs, type TaskContext, defineTask };
|
|
398
|
+
export { Hardkas, HardkasAccounts, HardkasArtifactsManager, HardkasL2, HardkasLineage, HardkasLocalnet, type HardkasOptions, HardkasQuery, HardkasReplay, HardkasTx, HardkasWorkspace, type TaskArgs, type TaskContext, defineTask };
|
package/dist/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
loadHardkasConfig as loadConfig
|
|
8
8
|
} from "@hardkas/config";
|
|
9
9
|
import { JsonWrpcKaspaClient } from "@hardkas/kaspa-rpc";
|
|
10
|
-
import { HardkasError as
|
|
10
|
+
import { HardkasError as HardkasError3 } from "@hardkas/core";
|
|
11
11
|
|
|
12
12
|
// src/accounts.ts
|
|
13
13
|
import { resolveHardkasAccount } from "@hardkas/accounts";
|
|
@@ -40,10 +40,55 @@ var HardkasAccounts = class {
|
|
|
40
40
|
formatted: formatSompi(sompi)
|
|
41
41
|
};
|
|
42
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* Alias for getBalance.
|
|
45
|
+
*/
|
|
46
|
+
async balance(accountNameOrAddress) {
|
|
47
|
+
return this.getBalance(accountNameOrAddress);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Lists all configured account names.
|
|
51
|
+
*/
|
|
52
|
+
async list() {
|
|
53
|
+
return Object.keys(this.sdk.config.config.accounts || {});
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Funds an account from another account (defaults to 'default' account).
|
|
57
|
+
*/
|
|
58
|
+
async fund(accountNameOrAddress, options) {
|
|
59
|
+
let from = options?.from;
|
|
60
|
+
const amount = options?.amount || "1000000000";
|
|
61
|
+
if (!from) {
|
|
62
|
+
const accounts = await this.list();
|
|
63
|
+
if (accounts.includes("faucet")) {
|
|
64
|
+
from = "faucet";
|
|
65
|
+
} else if (accounts.includes("simulated_faucet")) {
|
|
66
|
+
from = "simulated_faucet";
|
|
67
|
+
} else if (this.sdk.network === "simulated" && accounts.includes("alice")) {
|
|
68
|
+
from = "alice";
|
|
69
|
+
} else {
|
|
70
|
+
throw new Error("No funding account available.\nFor simulated mode, run Hardkas.create({ network: 'simulated', autoBootstrap: true })\nor call accounts.fund(target, { from: 'alice' }).");
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (from === accountNameOrAddress) {
|
|
74
|
+
throw new Error(`Cannot fund account '${accountNameOrAddress}' from itself.`);
|
|
75
|
+
}
|
|
76
|
+
const plan = await this.sdk.tx.plan({
|
|
77
|
+
from,
|
|
78
|
+
to: accountNameOrAddress,
|
|
79
|
+
amount
|
|
80
|
+
});
|
|
81
|
+
const signed = await this.sdk.tx.sign(plan, from);
|
|
82
|
+
if (this.sdk.network === "simulated") {
|
|
83
|
+
return this.sdk.tx.simulate(signed);
|
|
84
|
+
} else {
|
|
85
|
+
return this.sdk.tx.send(signed);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
43
88
|
};
|
|
44
89
|
|
|
45
90
|
// src/tx.ts
|
|
46
|
-
import { systemRuntimeContext } from "@hardkas/core";
|
|
91
|
+
import { systemRuntimeContext, deterministicCompare } from "@hardkas/core";
|
|
47
92
|
import {
|
|
48
93
|
buildPaymentPlan,
|
|
49
94
|
verifySignedTxSemantics
|
|
@@ -78,8 +123,12 @@ var HardkasTx = class {
|
|
|
78
123
|
if (!toAccount.address)
|
|
79
124
|
throw new Error(`To account ${toAccount.name} has no address.`);
|
|
80
125
|
const amountSompi = typeof options.amount === "string" ? parseKasToSompi(options.amount) : typeof options.amount === "number" ? BigInt(options.amount) : options.amount;
|
|
126
|
+
if (amountSompi === 0n) {
|
|
127
|
+
throw new Error("Kaspa value-transfer outputs require amount > 0.\nFor metadata/notary/DID marker transactions use --amount 1.\nFuture: hardkas tx anchor.");
|
|
128
|
+
}
|
|
81
129
|
let builderUtxos = [];
|
|
82
|
-
|
|
130
|
+
const activeNetwork = this.sdk.config.config.defaultNetwork || "simnet";
|
|
131
|
+
if (activeNetwork === "simulated" || this.sdk.config.config.networks?.[activeNetwork]?.kind === "simulated") {
|
|
83
132
|
const { loadOrCreateLocalnetState, getSpendableUtxos } = await import("@hardkas/localnet");
|
|
84
133
|
const localState = await loadOrCreateLocalnetState({
|
|
85
134
|
cwd: this.sdk.workspace.root
|
|
@@ -119,9 +168,10 @@ var HardkasTx = class {
|
|
|
119
168
|
],
|
|
120
169
|
feeRateSompiPerMass: options.feeRate ?? 1n
|
|
121
170
|
});
|
|
171
|
+
const isSimulated = activeNetwork === "simulated" || this.sdk.config.config.networks?.[activeNetwork]?.kind === "simulated";
|
|
122
172
|
return createTxPlanArtifact({
|
|
123
|
-
networkId:
|
|
124
|
-
mode: "simulated",
|
|
173
|
+
networkId: activeNetwork,
|
|
174
|
+
mode: isSimulated ? "simulated" : "real",
|
|
125
175
|
from: {
|
|
126
176
|
input: fromAccount.name || fromAccount.address,
|
|
127
177
|
address: fromAccount.address,
|
|
@@ -160,6 +210,9 @@ var HardkasTx = class {
|
|
|
160
210
|
"Cannot append signature to an already completed signed transaction."
|
|
161
211
|
);
|
|
162
212
|
}
|
|
213
|
+
if (options?.append === void 0 && plan.status === "partially_signed") {
|
|
214
|
+
options = { ...options, append: true };
|
|
215
|
+
}
|
|
163
216
|
if (!options?.append) {
|
|
164
217
|
throw new Error(
|
|
165
218
|
"Input file is a partially signed transaction. Use the --append flag to add your signature."
|
|
@@ -192,7 +245,7 @@ var HardkasTx = class {
|
|
|
192
245
|
signature: `simulated-signature-of-${signerAddress}`
|
|
193
246
|
};
|
|
194
247
|
const newSignatures = [...sigs, signatureEntry].sort(
|
|
195
|
-
(a, b) => a.signer
|
|
248
|
+
(a, b) => deterministicCompare(a.signer, b.signer)
|
|
196
249
|
);
|
|
197
250
|
const newMeta = [
|
|
198
251
|
...partialTx.signatureMetadata || [],
|
|
@@ -247,7 +300,12 @@ var HardkasTx = class {
|
|
|
247
300
|
if (!signerAddress) {
|
|
248
301
|
throw new Error(`Signer account '${resolvedAccount.name}' has no address.`);
|
|
249
302
|
}
|
|
250
|
-
const
|
|
303
|
+
const requiredSignersList = options?.requiredSigners || [signerAddress];
|
|
304
|
+
const requiredSigners = [];
|
|
305
|
+
for (const r of requiredSignersList) {
|
|
306
|
+
const acc = await this.sdk.accounts.resolve(r);
|
|
307
|
+
requiredSigners.push(acc.address || r);
|
|
308
|
+
}
|
|
251
309
|
if (!requiredSigners.includes(signerAddress)) {
|
|
252
310
|
throw new Error(
|
|
253
311
|
`Signer '${signerAddress}' is not an authorized signer for this transaction.`
|
|
@@ -258,7 +316,7 @@ var HardkasTx = class {
|
|
|
258
316
|
signature: `simulated-signature-of-${signerAddress}`
|
|
259
317
|
};
|
|
260
318
|
const signatures = [signatureEntry].sort(
|
|
261
|
-
(a, b) => a.signer
|
|
319
|
+
(a, b) => deterministicCompare(a.signer, b.signer)
|
|
262
320
|
);
|
|
263
321
|
const signatureMetadata = [
|
|
264
322
|
{
|
|
@@ -387,14 +445,16 @@ var HardkasTx = class {
|
|
|
387
445
|
{ cwd: this.sdk.workspace.root }
|
|
388
446
|
);
|
|
389
447
|
const tracePath = receiptPath.replace(".json", ".trace.json");
|
|
448
|
+
const activeNetwork = this.sdk.config.config.defaultNetwork || "simnet";
|
|
449
|
+
const isSimulated = activeNetwork === "simulated" || this.sdk.config.config.networks?.[activeNetwork]?.kind === "simulated";
|
|
390
450
|
const receiptBase = {
|
|
391
451
|
schema: ARTIFACT_SCHEMAS.TX_RECEIPT,
|
|
392
452
|
schemaVersion: "hardkas.receipt.v1",
|
|
393
453
|
hardkasVersion: HARDKAS_VERSION,
|
|
394
454
|
version: ARTIFACT_VERSION,
|
|
395
455
|
hashVersion: CURRENT_HASH_VERSION,
|
|
396
|
-
networkId:
|
|
397
|
-
mode: "simulated",
|
|
456
|
+
networkId: activeNetwork,
|
|
457
|
+
mode: isSimulated ? "simulated" : "real",
|
|
398
458
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
399
459
|
status: "confirmed",
|
|
400
460
|
txId: simResult.receipt.txId,
|
|
@@ -429,8 +489,8 @@ var HardkasTx = class {
|
|
|
429
489
|
hashVersion: CURRENT_HASH_VERSION,
|
|
430
490
|
createdAt: receipt.createdAt,
|
|
431
491
|
txId: receipt.txId,
|
|
432
|
-
mode: "simulated",
|
|
433
|
-
networkId:
|
|
492
|
+
mode: isSimulated ? "simulated" : "real",
|
|
493
|
+
networkId: activeNetwork,
|
|
434
494
|
steps: traceSteps
|
|
435
495
|
};
|
|
436
496
|
traceBase.contentHash = calculateContentHash(traceBase, CURRENT_HASH_VERSION);
|
|
@@ -514,6 +574,23 @@ var HardkasTx = class {
|
|
|
514
574
|
receiptPath
|
|
515
575
|
};
|
|
516
576
|
}
|
|
577
|
+
/**
|
|
578
|
+
* Explicitly appends a signature to a partially signed transaction.
|
|
579
|
+
*/
|
|
580
|
+
async appendSignature(plan, account) {
|
|
581
|
+
return this.sign(plan, account, { append: true });
|
|
582
|
+
}
|
|
583
|
+
/**
|
|
584
|
+
* Fetches the current status of a transaction by ID.
|
|
585
|
+
*/
|
|
586
|
+
async status(txId) {
|
|
587
|
+
const isLocal = txId.startsWith("simulated-");
|
|
588
|
+
if (isLocal) {
|
|
589
|
+
return { status: "simulated_confirmed" };
|
|
590
|
+
}
|
|
591
|
+
const result = await this.sdk.rpc.getTransaction(txId);
|
|
592
|
+
return result;
|
|
593
|
+
}
|
|
517
594
|
};
|
|
518
595
|
|
|
519
596
|
// src/l2.ts
|
|
@@ -546,11 +623,62 @@ var HardkasQuery = class {
|
|
|
546
623
|
async getEngine() {
|
|
547
624
|
if (this._engine) return this._engine;
|
|
548
625
|
const { QueryEngine } = await import("@hardkas/query");
|
|
549
|
-
this._engine =
|
|
550
|
-
artifactDir: this.sdk.
|
|
626
|
+
this._engine = await QueryEngine.create({
|
|
627
|
+
artifactDir: this.sdk.workspace.root,
|
|
628
|
+
autoSync: false
|
|
629
|
+
// We don't auto-sync on every getter
|
|
551
630
|
});
|
|
552
631
|
return this._engine;
|
|
553
632
|
}
|
|
633
|
+
/**
|
|
634
|
+
* Synchronizes the query store with the filesystem artifacts.
|
|
635
|
+
*/
|
|
636
|
+
async sync(options) {
|
|
637
|
+
const { HardkasStore, HardkasIndexer } = await import("@hardkas/query-store");
|
|
638
|
+
const { withLock } = await import("@hardkas/core");
|
|
639
|
+
const path4 = await import("path");
|
|
640
|
+
const dbPath = path4.join(this.sdk.workspace.root, ".hardkas", "store.db");
|
|
641
|
+
const store = new HardkasStore({ dbPath });
|
|
642
|
+
let stats;
|
|
643
|
+
await withLock(
|
|
644
|
+
{
|
|
645
|
+
rootDir: this.sdk.workspace.root,
|
|
646
|
+
name: "query-store",
|
|
647
|
+
command: "query-sync",
|
|
648
|
+
wait: true
|
|
649
|
+
},
|
|
650
|
+
async () => {
|
|
651
|
+
store.connect({ autoMigrate: true });
|
|
652
|
+
const indexer = new HardkasIndexer(store.getDatabase());
|
|
653
|
+
if (options?.force) {
|
|
654
|
+
stats = await indexer.rebuild();
|
|
655
|
+
} else {
|
|
656
|
+
stats = await indexer.sync();
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
);
|
|
660
|
+
return stats;
|
|
661
|
+
}
|
|
662
|
+
/**
|
|
663
|
+
* Fetches events from the query store.
|
|
664
|
+
*/
|
|
665
|
+
async events(filter) {
|
|
666
|
+
const engine = await this.getEngine();
|
|
667
|
+
const { createQueryRequest } = await import("@hardkas/query");
|
|
668
|
+
const filters = [];
|
|
669
|
+
if (filter) {
|
|
670
|
+
for (const [k, v] of Object.entries(filter)) {
|
|
671
|
+
if (v) filters.push({ field: k, op: "eq", value: v });
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
const request = createQueryRequest({
|
|
675
|
+
domain: "events",
|
|
676
|
+
op: "list",
|
|
677
|
+
filters
|
|
678
|
+
});
|
|
679
|
+
const result = await engine.execute(request);
|
|
680
|
+
return result.items;
|
|
681
|
+
}
|
|
554
682
|
};
|
|
555
683
|
|
|
556
684
|
// src/localnet.ts
|
|
@@ -594,6 +722,7 @@ import {
|
|
|
594
722
|
verifyArtifactIntegrity,
|
|
595
723
|
writeArtifact as writeArtifact2
|
|
596
724
|
} from "@hardkas/artifacts";
|
|
725
|
+
import { deterministicCompare as deterministicCompare2 } from "@hardkas/core";
|
|
597
726
|
function resolveReplayTargets(cwd, options) {
|
|
598
727
|
if (options.path) {
|
|
599
728
|
const fullPath = path.resolve(cwd, options.path);
|
|
@@ -670,7 +799,7 @@ function resolveFromDirectory(dir, source) {
|
|
|
670
799
|
Hint: Run a transaction first: hardkas tx send --from alice --to bob --amount 10 --network simulated --yes`
|
|
671
800
|
);
|
|
672
801
|
}
|
|
673
|
-
plans.sort((a, b) => b.createdAt
|
|
802
|
+
plans.sort((a, b) => deterministicCompare2(b.createdAt, a.createdAt));
|
|
674
803
|
for (const plan of plans) {
|
|
675
804
|
const matchingReceipt = receipts.find(
|
|
676
805
|
(r) => (
|
|
@@ -721,16 +850,24 @@ var HardkasReplay = class {
|
|
|
721
850
|
* Verifies the deterministic artifact lineage of a transaction replay
|
|
722
851
|
* against the mathematically reconstructed localnet state.
|
|
723
852
|
*/
|
|
724
|
-
async verify(options) {
|
|
725
|
-
|
|
853
|
+
async verify(targetOrOptions, options) {
|
|
854
|
+
let opts = options || {};
|
|
855
|
+
if (typeof targetOrOptions === "string") {
|
|
856
|
+
opts.path = targetOrOptions;
|
|
857
|
+
} else if (targetOrOptions && "artifactId" in targetOrOptions) {
|
|
858
|
+
opts.path = targetOrOptions.artifactId;
|
|
859
|
+
} else if (targetOrOptions) {
|
|
860
|
+
opts = { ...opts, ...targetOrOptions };
|
|
861
|
+
}
|
|
862
|
+
const targets = resolveReplayTargets(this.sdk.config.cwd, opts);
|
|
726
863
|
let { planPath, receiptPath, artifactDir } = targets;
|
|
727
864
|
if (!fs.existsSync(path.join(this.sdk.config.cwd, "hardkas.config.ts"))) {
|
|
728
865
|
throw new Error(`Workspace not found at ${this.sdk.config.cwd}`);
|
|
729
866
|
}
|
|
730
867
|
const canonicalDirs = [
|
|
731
|
-
path.join(
|
|
732
|
-
path.join(
|
|
733
|
-
path.join(
|
|
868
|
+
path.join(this.sdk.workspace.hardkasDir, "receipts"),
|
|
869
|
+
path.join(this.sdk.workspace.hardkasDir, "traces"),
|
|
870
|
+
path.join(this.sdk.workspace.hardkasDir, "deployments")
|
|
734
871
|
];
|
|
735
872
|
const files = [];
|
|
736
873
|
for (const dir of canonicalDirs) {
|
|
@@ -793,9 +930,9 @@ var HardkasReplay = class {
|
|
|
793
930
|
let receipt;
|
|
794
931
|
let verifyErrorMsg;
|
|
795
932
|
let report = null;
|
|
796
|
-
if (
|
|
933
|
+
if (opts.workflowId) {
|
|
797
934
|
try {
|
|
798
|
-
const wfArtifactPath = fs.readdirSync(this.sdk.workspace.artifactsDir).find((f) => f.includes(
|
|
935
|
+
const wfArtifactPath = fs.readdirSync(this.sdk.workspace.artifactsDir).find((f) => f.includes(opts.workflowId) && f.endsWith(".json"));
|
|
799
936
|
if (!wfArtifactPath) throw new Error("Workflow artifact not found");
|
|
800
937
|
const wfArtifactStr = fs.readFileSync(
|
|
801
938
|
path.join(this.sdk.workspace.artifactsDir, wfArtifactPath),
|
|
@@ -803,7 +940,7 @@ var HardkasReplay = class {
|
|
|
803
940
|
);
|
|
804
941
|
const wfArtifact = JSON.parse(wfArtifactStr);
|
|
805
942
|
if (wfArtifact.schema !== "hardkas.workflow.v1") {
|
|
806
|
-
throw new Error(`Artifact ${
|
|
943
|
+
throw new Error(`Artifact ${opts.workflowId} is not a workflow artifact`);
|
|
807
944
|
}
|
|
808
945
|
const childArtifacts = wfArtifact.producedArtifacts || [];
|
|
809
946
|
for (const childId of childArtifacts) {
|
|
@@ -882,6 +1019,33 @@ var HardkasReplay = class {
|
|
|
882
1019
|
}
|
|
883
1020
|
};
|
|
884
1021
|
|
|
1022
|
+
// src/lineage.ts
|
|
1023
|
+
var HardkasLineage = class {
|
|
1024
|
+
constructor(sdk) {
|
|
1025
|
+
this.sdk = sdk;
|
|
1026
|
+
}
|
|
1027
|
+
sdk;
|
|
1028
|
+
/**
|
|
1029
|
+
* Traces the lineage of an artifact, identifying ancestors and descendants.
|
|
1030
|
+
*/
|
|
1031
|
+
async trace(target, options) {
|
|
1032
|
+
const anchor = typeof target === "string" ? target : target.artifactId || target.contentHash || "";
|
|
1033
|
+
if (!anchor) throw new Error("No anchor target provided for lineage trace.");
|
|
1034
|
+
const { createQueryRequest, QueryEngine } = await import("@hardkas/query");
|
|
1035
|
+
const engine = await QueryEngine.create({
|
|
1036
|
+
artifactDir: this.sdk.workspace.root,
|
|
1037
|
+
autoSync: false
|
|
1038
|
+
});
|
|
1039
|
+
const request = createQueryRequest({
|
|
1040
|
+
domain: "lineage",
|
|
1041
|
+
op: "chain",
|
|
1042
|
+
params: { anchor, direction: options?.direction || "ancestors" }
|
|
1043
|
+
});
|
|
1044
|
+
const result = await engine.execute(request);
|
|
1045
|
+
return result.items[0];
|
|
1046
|
+
}
|
|
1047
|
+
};
|
|
1048
|
+
|
|
885
1049
|
// src/workspace.ts
|
|
886
1050
|
import path2 from "path";
|
|
887
1051
|
import fs2 from "fs";
|
|
@@ -897,7 +1061,7 @@ var HardkasWorkspace = class {
|
|
|
897
1061
|
return path2.join(this.hardkasDir, "artifacts");
|
|
898
1062
|
}
|
|
899
1063
|
get localnetStatePath() {
|
|
900
|
-
return path2.join(this.hardkasDir, "localnet
|
|
1064
|
+
return path2.join(this.hardkasDir, "localnet.json");
|
|
901
1065
|
}
|
|
902
1066
|
get keystoreDir() {
|
|
903
1067
|
return path2.join(this.hardkasDir, "keystore");
|
|
@@ -927,6 +1091,7 @@ var HardkasWorkspace = class {
|
|
|
927
1091
|
// src/artifacts-manager.ts
|
|
928
1092
|
import path3 from "path";
|
|
929
1093
|
import fs3 from "fs";
|
|
1094
|
+
import { HardkasError } from "@hardkas/core";
|
|
930
1095
|
var HardkasArtifactsManager = class {
|
|
931
1096
|
constructor(workspace) {
|
|
932
1097
|
this.workspace = workspace;
|
|
@@ -1001,6 +1166,15 @@ var HardkasArtifactsManager = class {
|
|
|
1001
1166
|
}
|
|
1002
1167
|
const { readArtifact } = await import("@hardkas/artifacts");
|
|
1003
1168
|
let filePath = id;
|
|
1169
|
+
let resolvedPath = path3.resolve(this.workspace.root, filePath);
|
|
1170
|
+
if (fs3.existsSync(resolvedPath)) {
|
|
1171
|
+
resolvedPath = fs3.realpathSync(resolvedPath);
|
|
1172
|
+
}
|
|
1173
|
+
const rootRel = path3.relative(this.workspace.root, resolvedPath);
|
|
1174
|
+
const artifactsRel = path3.relative(this.workspace.artifactsDir, resolvedPath);
|
|
1175
|
+
if ((rootRel.startsWith("..") || path3.isAbsolute(rootRel)) && (artifactsRel.startsWith("..") || path3.isAbsolute(artifactsRel))) {
|
|
1176
|
+
throw new HardkasError("PATH_TRAVERSAL", "Artifact path escapes workspace boundary");
|
|
1177
|
+
}
|
|
1004
1178
|
if (!fs3.existsSync(filePath)) {
|
|
1005
1179
|
filePath = path3.join(this.workspace.artifactsDir, `${id}.json`);
|
|
1006
1180
|
if (!fs3.existsSync(filePath)) {
|
|
@@ -1019,11 +1193,53 @@ var HardkasArtifactsManager = class {
|
|
|
1019
1193
|
}
|
|
1020
1194
|
return readArtifact(filePath);
|
|
1021
1195
|
}
|
|
1196
|
+
/**
|
|
1197
|
+
* Alias for read().
|
|
1198
|
+
*/
|
|
1199
|
+
async get(id) {
|
|
1200
|
+
return this.read(id);
|
|
1201
|
+
}
|
|
1202
|
+
/**
|
|
1203
|
+
* Lists all artifacts in the workspace.
|
|
1204
|
+
*/
|
|
1205
|
+
async list() {
|
|
1206
|
+
if (!fs3.existsSync(this.workspace.artifactsDir)) {
|
|
1207
|
+
return [];
|
|
1208
|
+
}
|
|
1209
|
+
const { readArtifact } = await import("@hardkas/artifacts");
|
|
1210
|
+
const files = fs3.readdirSync(this.workspace.artifactsDir);
|
|
1211
|
+
const artifacts = [];
|
|
1212
|
+
for (const file of files) {
|
|
1213
|
+
if (file.endsWith(".json")) {
|
|
1214
|
+
try {
|
|
1215
|
+
const artifact = await readArtifact(path3.join(this.workspace.artifactsDir, file));
|
|
1216
|
+
artifacts.push(artifact);
|
|
1217
|
+
} catch (e) {
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
1221
|
+
return artifacts;
|
|
1222
|
+
}
|
|
1223
|
+
/**
|
|
1224
|
+
* Cryptographically verifies the determinism and integrity of an artifact.
|
|
1225
|
+
* Throws an error with details if corruption or mismatch is found.
|
|
1226
|
+
*/
|
|
1227
|
+
async verify(target) {
|
|
1228
|
+
const id = typeof target === "string" ? target : target.artifactId || target.contentHash || "";
|
|
1229
|
+
if (!id) throw new Error("No artifact target provided for verification.");
|
|
1230
|
+
const artifact = await this.read(id);
|
|
1231
|
+
const { verifyArtifactIntegrity: verifyArtifactIntegrity2 } = await import("@hardkas/artifacts");
|
|
1232
|
+
const result = await verifyArtifactIntegrity2(artifact);
|
|
1233
|
+
if (!result.ok) {
|
|
1234
|
+
throw new Error(`Artifact ${id} corrupted or invalid: ` + JSON.stringify(result.issues, null, 2));
|
|
1235
|
+
}
|
|
1236
|
+
return result;
|
|
1237
|
+
}
|
|
1022
1238
|
};
|
|
1023
1239
|
|
|
1024
1240
|
// src/workflow.ts
|
|
1025
1241
|
import { HARDKAS_VERSION as HARDKAS_VERSION2 } from "@hardkas/artifacts";
|
|
1026
|
-
import { HardkasError, deterministicCompare } from "@hardkas/core";
|
|
1242
|
+
import { HardkasError as HardkasError2, deterministicCompare as deterministicCompare3 } from "@hardkas/core";
|
|
1027
1243
|
var HardkasWorkflow = class {
|
|
1028
1244
|
constructor(sdk) {
|
|
1029
1245
|
this.sdk = sdk;
|
|
@@ -1070,12 +1286,12 @@ var HardkasWorkflow = class {
|
|
|
1070
1286
|
try {
|
|
1071
1287
|
if (step.type === "simulate-failure") {
|
|
1072
1288
|
if (this.sdk.mode === "agent") {
|
|
1073
|
-
throw new
|
|
1289
|
+
throw new HardkasError2(
|
|
1074
1290
|
"POLICY_DENIED",
|
|
1075
1291
|
"simulate-failure is strictly prohibited in agent mode"
|
|
1076
1292
|
);
|
|
1077
1293
|
}
|
|
1078
|
-
throw new
|
|
1294
|
+
throw new HardkasError2("MOCKED_FAIL", "Simulated failure for contract tests");
|
|
1079
1295
|
}
|
|
1080
1296
|
let producedArtifactId = void 0;
|
|
1081
1297
|
let result = void 0;
|
|
@@ -1236,9 +1452,9 @@ var HardkasWorkflow = class {
|
|
|
1236
1452
|
artifactId: workflowId,
|
|
1237
1453
|
status,
|
|
1238
1454
|
steps: artifactSteps,
|
|
1239
|
-
parentArtifacts: parentArtifacts.sort(
|
|
1455
|
+
parentArtifacts: parentArtifacts.sort(deterministicCompare3),
|
|
1240
1456
|
producedArtifacts: Array.from(new Set(producedArtifacts)).sort(
|
|
1241
|
-
|
|
1457
|
+
deterministicCompare3
|
|
1242
1458
|
),
|
|
1243
1459
|
generationRange: {
|
|
1244
1460
|
start: generationStart,
|
|
@@ -1308,7 +1524,7 @@ import {
|
|
|
1308
1524
|
} from "@hardkas/artifacts";
|
|
1309
1525
|
import {
|
|
1310
1526
|
SOMPI_PER_KAS,
|
|
1311
|
-
HardkasError as
|
|
1527
|
+
HardkasError as HardkasError4,
|
|
1312
1528
|
parseKasToSompi as parseKasToSompi2,
|
|
1313
1529
|
formatSompi as formatSompi2
|
|
1314
1530
|
} from "@hardkas/core";
|
|
@@ -1333,6 +1549,7 @@ var Hardkas = class _Hardkas {
|
|
|
1333
1549
|
this.query = new HardkasQuery(this);
|
|
1334
1550
|
this.localnet = new HardkasLocalnet(this);
|
|
1335
1551
|
this.replay = new HardkasReplay(this);
|
|
1552
|
+
this.lineage = new HardkasLineage(this);
|
|
1336
1553
|
this.workflow = new HardkasWorkflow(this);
|
|
1337
1554
|
}
|
|
1338
1555
|
config;
|
|
@@ -1344,6 +1561,7 @@ var Hardkas = class _Hardkas {
|
|
|
1344
1561
|
query;
|
|
1345
1562
|
localnet;
|
|
1346
1563
|
replay;
|
|
1564
|
+
lineage;
|
|
1347
1565
|
workflow;
|
|
1348
1566
|
mode;
|
|
1349
1567
|
policy;
|
|
@@ -1362,6 +1580,39 @@ var Hardkas = class _Hardkas {
|
|
|
1362
1580
|
static async open(dirOrOptions = ".") {
|
|
1363
1581
|
const options = typeof dirOrOptions === "string" ? { cwd: dirOrOptions } : dirOrOptions;
|
|
1364
1582
|
const loaded = await loadConfig(options);
|
|
1583
|
+
const activeNetwork = options.network || loaded.config.defaultNetwork || "simnet";
|
|
1584
|
+
const isSimulated = activeNetwork === "simulated" || loaded.config.networks?.[activeNetwork]?.kind === "simulated";
|
|
1585
|
+
const autoBootstrap = options.autoBootstrap ?? (isSimulated ? true : false);
|
|
1586
|
+
const fs4 = await import("fs");
|
|
1587
|
+
const path4 = await import("path");
|
|
1588
|
+
const cwd = options.cwd || process.cwd();
|
|
1589
|
+
const hardkasDir = path4.join(cwd, ".hardkas");
|
|
1590
|
+
if (autoBootstrap) {
|
|
1591
|
+
if (!isSimulated) {
|
|
1592
|
+
if (options.logger) {
|
|
1593
|
+
options.logger.warn("[HardKAS] autoBootstrap ignored for non-simulated network");
|
|
1594
|
+
}
|
|
1595
|
+
} else {
|
|
1596
|
+
if (!fs4.existsSync(hardkasDir)) {
|
|
1597
|
+
if (options.logger) {
|
|
1598
|
+
options.logger.info("[HardKAS] Auto-bootstrapping simulated workspace");
|
|
1599
|
+
}
|
|
1600
|
+
fs4.mkdirSync(hardkasDir, { recursive: true });
|
|
1601
|
+
}
|
|
1602
|
+
try {
|
|
1603
|
+
const { loadOrCreateLocalnetState } = await import("@hardkas/localnet");
|
|
1604
|
+
await loadOrCreateLocalnetState({ cwd });
|
|
1605
|
+
} catch {
|
|
1606
|
+
}
|
|
1607
|
+
}
|
|
1608
|
+
} else {
|
|
1609
|
+
if (!fs4.existsSync(hardkasDir)) {
|
|
1610
|
+
throw new HardkasError3("NOT_INITIALIZED", "Workspace not initialized. Run npx hardkas init . or pass autoBootstrap: true.");
|
|
1611
|
+
}
|
|
1612
|
+
}
|
|
1613
|
+
if (options.network) {
|
|
1614
|
+
loaded.config.defaultNetwork = options.network;
|
|
1615
|
+
}
|
|
1365
1616
|
return new _Hardkas(loaded, options);
|
|
1366
1617
|
}
|
|
1367
1618
|
/**
|
|
@@ -1392,19 +1643,19 @@ var Hardkas = class _Hardkas {
|
|
|
1392
1643
|
switch (action) {
|
|
1393
1644
|
case "network":
|
|
1394
1645
|
if (!this.policy.allowNetwork)
|
|
1395
|
-
throw new
|
|
1646
|
+
throw new HardkasError3("POLICY_VIOLATION", msg("allowNetwork"));
|
|
1396
1647
|
break;
|
|
1397
1648
|
case "mainnet":
|
|
1398
1649
|
if (!this.policy.allowMainnet)
|
|
1399
|
-
throw new
|
|
1650
|
+
throw new HardkasError3("POLICY_VIOLATION", msg("allowMainnet"));
|
|
1400
1651
|
break;
|
|
1401
1652
|
case "external-wallet":
|
|
1402
1653
|
if (!this.policy.allowExternalWallet)
|
|
1403
|
-
throw new
|
|
1654
|
+
throw new HardkasError3("POLICY_VIOLATION", msg("allowExternalWallet"));
|
|
1404
1655
|
break;
|
|
1405
1656
|
case "mutation":
|
|
1406
1657
|
if (this.policy.requireDryRun)
|
|
1407
|
-
throw new
|
|
1658
|
+
throw new HardkasError3("POLICY_VIOLATION", msg("requireDryRun"));
|
|
1408
1659
|
break;
|
|
1409
1660
|
}
|
|
1410
1661
|
}
|
|
@@ -1415,8 +1666,9 @@ export {
|
|
|
1415
1666
|
Hardkas,
|
|
1416
1667
|
HardkasAccounts,
|
|
1417
1668
|
HardkasArtifactsManager,
|
|
1418
|
-
|
|
1669
|
+
HardkasError4 as HardkasError,
|
|
1419
1670
|
HardkasL2,
|
|
1671
|
+
HardkasLineage,
|
|
1420
1672
|
HardkasLocalnet,
|
|
1421
1673
|
HardkasQuery,
|
|
1422
1674
|
HardkasReplay,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hardkas/sdk",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.8-alpha",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist",
|
|
@@ -23,23 +23,24 @@
|
|
|
23
23
|
}
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@hardkas/
|
|
27
|
-
"@hardkas/
|
|
28
|
-
"@hardkas/
|
|
29
|
-
"@hardkas/
|
|
30
|
-
"@hardkas/l2": "0.7.
|
|
31
|
-
"@hardkas/
|
|
32
|
-
"@hardkas/
|
|
33
|
-
"@hardkas/query": "0.7.
|
|
34
|
-
"@hardkas/tx-builder": "0.7.
|
|
35
|
-
"@hardkas/
|
|
36
|
-
"@hardkas/
|
|
26
|
+
"@hardkas/artifacts": "0.7.8-alpha",
|
|
27
|
+
"@hardkas/core": "0.7.8-alpha",
|
|
28
|
+
"@hardkas/config": "0.7.8-alpha",
|
|
29
|
+
"@hardkas/kaspa-rpc": "0.7.8-alpha",
|
|
30
|
+
"@hardkas/l2": "0.7.8-alpha",
|
|
31
|
+
"@hardkas/localnet": "0.7.8-alpha",
|
|
32
|
+
"@hardkas/accounts": "0.7.8-alpha",
|
|
33
|
+
"@hardkas/query": "0.7.8-alpha",
|
|
34
|
+
"@hardkas/tx-builder": "0.7.8-alpha",
|
|
35
|
+
"@hardkas/simulator": "0.7.8-alpha",
|
|
36
|
+
"@hardkas/query-store": "0.7.8-alpha",
|
|
37
|
+
"@hardkas/wallet-adapter": "0.7.8-alpha"
|
|
37
38
|
},
|
|
38
39
|
"devDependencies": {
|
|
39
40
|
"tsup": "^8.3.5",
|
|
41
|
+
"@types/node": "^20.12.7",
|
|
40
42
|
"typescript": "^5.7.2",
|
|
41
|
-
"vitest": "^2.1.8"
|
|
42
|
-
"@hardkas/query-store": "0.7.6-alpha"
|
|
43
|
+
"vitest": "^2.1.8"
|
|
43
44
|
},
|
|
44
45
|
"license": "MIT",
|
|
45
46
|
"author": "Javier Rodriguez",
|