@psavelis/enterprise-blockchain 0.1.0 → 1.1.1
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/README.md +15 -2
- package/dist/aid-settlement/application/reconciler.d.ts +13 -0
- package/dist/aid-settlement/application/reconciler.d.ts.map +1 -0
- package/dist/aid-settlement/application/reconciler.js +77 -0
- package/dist/aid-settlement/domain/entities.d.ts +24 -0
- package/dist/aid-settlement/domain/entities.d.ts.map +1 -0
- package/dist/aid-settlement/domain/entities.js +1 -0
- package/dist/aid-settlement/domain/ports.d.ts +10 -0
- package/dist/aid-settlement/domain/ports.d.ts.map +1 -0
- package/dist/aid-settlement/domain/ports.js +1 -0
- package/dist/aid-settlement/index.d.ts +19 -0
- package/dist/aid-settlement/index.d.ts.map +1 -0
- package/dist/aid-settlement/index.js +23 -0
- package/dist/aid-settlement/infrastructure/in-memory-store.d.ts +12 -0
- package/dist/aid-settlement/infrastructure/in-memory-store.d.ts.map +1 -0
- package/dist/aid-settlement/infrastructure/in-memory-store.js +17 -0
- package/dist/credentialing/application/clearance-evaluator.d.ts +10 -0
- package/dist/credentialing/application/clearance-evaluator.d.ts.map +1 -0
- package/dist/credentialing/application/clearance-evaluator.js +63 -0
- package/dist/credentialing/domain/entities.d.ts +28 -0
- package/dist/credentialing/domain/entities.d.ts.map +1 -0
- package/dist/credentialing/domain/entities.js +1 -0
- package/dist/credentialing/domain/ports.d.ts +9 -0
- package/dist/credentialing/domain/ports.d.ts.map +1 -0
- package/dist/credentialing/domain/ports.js +1 -0
- package/dist/credentialing/index.d.ts +19 -0
- package/dist/credentialing/index.d.ts.map +1 -0
- package/dist/credentialing/index.js +23 -0
- package/dist/credentialing/infrastructure/in-memory-store.d.ts +11 -0
- package/dist/credentialing/infrastructure/in-memory-store.d.ts.map +1 -0
- package/dist/credentialing/infrastructure/in-memory-store.js +14 -0
- package/dist/hsm/application/asymmetric-key-service.d.ts +23 -0
- package/dist/hsm/application/asymmetric-key-service.d.ts.map +1 -0
- package/dist/hsm/application/asymmetric-key-service.js +109 -0
- package/dist/hsm/application/envelope-encryption-service.d.ts +18 -0
- package/dist/hsm/application/envelope-encryption-service.d.ts.map +1 -0
- package/dist/hsm/application/envelope-encryption-service.js +59 -0
- package/dist/hsm/application/symmetric-key-service.d.ts +34 -0
- package/dist/hsm/application/symmetric-key-service.d.ts.map +1 -0
- package/dist/hsm/application/symmetric-key-service.js +107 -0
- package/dist/hsm/domain/entities.d.ts +104 -0
- package/dist/hsm/domain/entities.d.ts.map +1 -0
- package/dist/hsm/domain/entities.js +10 -0
- package/dist/hsm/domain/ports.d.ts +20 -0
- package/dist/hsm/domain/ports.d.ts.map +1 -0
- package/dist/hsm/domain/ports.js +1 -0
- package/dist/hsm/index.d.ts +48 -0
- package/dist/hsm/index.d.ts.map +1 -0
- package/dist/hsm/index.js +97 -0
- package/dist/hsm/infrastructure/audit-log-factory.d.ts +59 -0
- package/dist/hsm/infrastructure/audit-log-factory.d.ts.map +1 -0
- package/dist/hsm/infrastructure/audit-log-factory.js +95 -0
- package/dist/hsm/infrastructure/audit-log.d.ts +8 -0
- package/dist/hsm/infrastructure/audit-log.d.ts.map +1 -0
- package/dist/hsm/infrastructure/audit-log.js +18 -0
- package/dist/hsm/infrastructure/file-audit-log.d.ts +55 -0
- package/dist/hsm/infrastructure/file-audit-log.d.ts.map +1 -0
- package/dist/hsm/infrastructure/file-audit-log.js +128 -0
- package/dist/hsm/infrastructure/key-store.d.ts +9 -0
- package/dist/hsm/infrastructure/key-store.d.ts.map +1 -0
- package/dist/hsm/infrastructure/key-store.js +12 -0
- package/dist/hsm/infrastructure/syslog-audit-log.d.ts +64 -0
- package/dist/hsm/infrastructure/syslog-audit-log.d.ts.map +1 -0
- package/dist/hsm/infrastructure/syslog-audit-log.js +167 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +37 -0
- package/dist/integrations/besu-client/error-mapper.d.ts +9 -0
- package/dist/integrations/besu-client/error-mapper.d.ts.map +1 -0
- package/dist/integrations/besu-client/error-mapper.js +22 -0
- package/dist/integrations/besu-client/index.d.ts +65 -0
- package/dist/integrations/besu-client/index.d.ts.map +1 -0
- package/dist/integrations/besu-client/index.js +276 -0
- package/dist/integrations/besu-client/ports.d.ts +44 -0
- package/dist/integrations/besu-client/ports.d.ts.map +1 -0
- package/dist/integrations/besu-client/ports.js +1 -0
- package/dist/integrations/corda-gateway/index.d.ts +37 -0
- package/dist/integrations/corda-gateway/index.d.ts.map +1 -0
- package/dist/integrations/corda-gateway/index.js +234 -0
- package/dist/integrations/corda-gateway/ports.d.ts +33 -0
- package/dist/integrations/corda-gateway/ports.d.ts.map +1 -0
- package/dist/integrations/corda-gateway/ports.js +1 -0
- package/dist/integrations/fabric-gateway/index.d.ts +78 -0
- package/dist/integrations/fabric-gateway/index.d.ts.map +1 -0
- package/dist/integrations/fabric-gateway/index.js +214 -0
- package/dist/integrations/fabric-gateway/ports.d.ts +50 -0
- package/dist/integrations/fabric-gateway/ports.d.ts.map +1 -0
- package/dist/integrations/fabric-gateway/ports.js +1 -0
- package/dist/integrations/index.d.ts +19 -0
- package/dist/integrations/index.d.ts.map +1 -0
- package/dist/integrations/index.js +19 -0
- package/dist/integrations/shared/env.d.ts +4 -0
- package/dist/integrations/shared/env.d.ts.map +1 -0
- package/dist/integrations/shared/env.js +24 -0
- package/dist/integrations/shared/retry.d.ts +79 -0
- package/dist/integrations/shared/retry.d.ts.map +1 -0
- package/dist/integrations/shared/retry.js +315 -0
- package/dist/mpc/adapters.d.ts +36 -0
- package/dist/mpc/adapters.d.ts.map +1 -0
- package/dist/mpc/adapters.js +46 -0
- package/dist/mpc/crypto.d.ts +2 -0
- package/dist/mpc/crypto.d.ts.map +1 -0
- package/dist/mpc/crypto.js +2 -0
- package/dist/mpc/dsa.d.ts +134 -0
- package/dist/mpc/dsa.d.ts.map +1 -0
- package/dist/mpc/dsa.js +127 -0
- package/dist/mpc/field.d.ts +127 -0
- package/dist/mpc/field.d.ts.map +1 -0
- package/dist/mpc/field.js +209 -0
- package/dist/mpc/hybrid-kem.d.ts +96 -0
- package/dist/mpc/hybrid-kem.d.ts.map +1 -0
- package/dist/mpc/hybrid-kem.js +136 -0
- package/dist/mpc/index.d.ts +135 -0
- package/dist/mpc/index.d.ts.map +1 -0
- package/dist/mpc/index.js +348 -0
- package/dist/mpc/kyber.d.ts +134 -0
- package/dist/mpc/kyber.d.ts.map +1 -0
- package/dist/mpc/kyber.js +143 -0
- package/dist/mpc/ports.d.ts +67 -0
- package/dist/mpc/ports.d.ts.map +1 -0
- package/dist/mpc/ports.js +9 -0
- package/dist/mpc/quantum.d.ts +80 -0
- package/dist/mpc/quantum.d.ts.map +1 -0
- package/dist/mpc/quantum.js +180 -0
- package/dist/p2mr/adapters.d.ts +31 -0
- package/dist/p2mr/adapters.d.ts.map +1 -0
- package/dist/p2mr/adapters.js +35 -0
- package/dist/p2mr/index.d.ts +63 -0
- package/dist/p2mr/index.d.ts.map +1 -0
- package/dist/p2mr/index.js +59 -0
- package/dist/p2mr/merkle-tree.d.ts +109 -0
- package/dist/p2mr/merkle-tree.d.ts.map +1 -0
- package/dist/p2mr/merkle-tree.js +239 -0
- package/dist/p2mr/p2mr-output.d.ts +142 -0
- package/dist/p2mr/p2mr-output.d.ts.map +1 -0
- package/dist/p2mr/p2mr-output.js +150 -0
- package/dist/p2mr/ports.d.ts +52 -0
- package/dist/p2mr/ports.d.ts.map +1 -0
- package/dist/p2mr/ports.js +9 -0
- package/dist/p2mr/script-interpreter.d.ts +92 -0
- package/dist/p2mr/script-interpreter.d.ts.map +1 -0
- package/dist/p2mr/script-interpreter.js +535 -0
- package/dist/p2mr/script-leaf.d.ts +70 -0
- package/dist/p2mr/script-leaf.d.ts.map +1 -0
- package/dist/p2mr/script-leaf.js +203 -0
- package/dist/p2mr/spend-proof.d.ts +95 -0
- package/dist/p2mr/spend-proof.d.ts.map +1 -0
- package/dist/p2mr/spend-proof.js +358 -0
- package/dist/p2mr/types.d.ts +209 -0
- package/dist/p2mr/types.d.ts.map +1 -0
- package/dist/p2mr/types.js +9 -0
- package/dist/privacy/application/view-projector.d.ts +13 -0
- package/dist/privacy/application/view-projector.d.ts.map +1 -0
- package/dist/privacy/application/view-projector.js +85 -0
- package/dist/privacy/domain/entities.d.ts +26 -0
- package/dist/privacy/domain/entities.d.ts.map +1 -0
- package/dist/privacy/domain/entities.js +1 -0
- package/dist/privacy/domain/ports.d.ts +7 -0
- package/dist/privacy/domain/ports.d.ts.map +1 -0
- package/dist/privacy/domain/ports.js +1 -0
- package/dist/privacy/index.d.ts +21 -0
- package/dist/privacy/index.d.ts.map +1 -0
- package/dist/privacy/index.js +25 -0
- package/dist/privacy/infrastructure/in-memory-store.d.ts +8 -0
- package/dist/privacy/infrastructure/in-memory-store.d.ts.map +1 -0
- package/dist/privacy/infrastructure/in-memory-store.js +7 -0
- package/dist/protocols/besu-port.d.ts +80 -0
- package/dist/protocols/besu-port.d.ts.map +1 -0
- package/dist/protocols/besu-port.js +1 -0
- package/dist/protocols/corda-port.d.ts +103 -0
- package/dist/protocols/corda-port.d.ts.map +1 -0
- package/dist/protocols/corda-port.js +9 -0
- package/dist/protocols/credentialing-port.d.ts +11 -0
- package/dist/protocols/credentialing-port.d.ts.map +1 -0
- package/dist/protocols/credentialing-port.js +1 -0
- package/dist/protocols/fabric-port.d.ts +89 -0
- package/dist/protocols/fabric-port.d.ts.map +1 -0
- package/dist/protocols/fabric-port.js +9 -0
- package/dist/protocols/index.d.ts +14 -0
- package/dist/protocols/index.d.ts.map +1 -0
- package/dist/protocols/index.js +7 -0
- package/dist/protocols/p2mr-port.d.ts +159 -0
- package/dist/protocols/p2mr-port.d.ts.map +1 -0
- package/dist/protocols/p2mr-port.js +12 -0
- package/dist/protocols/privacy-port.d.ts +9 -0
- package/dist/protocols/privacy-port.d.ts.map +1 -0
- package/dist/protocols/privacy-port.js +1 -0
- package/dist/protocols/traceability-port.d.ts +12 -0
- package/dist/protocols/traceability-port.d.ts.map +1 -0
- package/dist/protocols/traceability-port.js +1 -0
- package/dist/shared/collection-store.d.ts +12 -0
- package/dist/shared/collection-store.d.ts.map +1 -0
- package/dist/shared/collection-store.js +26 -0
- package/dist/shared/commit.d.ts +24 -0
- package/dist/shared/commit.d.ts.map +1 -0
- package/dist/shared/commit.js +50 -0
- package/dist/shared/crypto.d.ts +2 -0
- package/dist/shared/crypto.d.ts.map +1 -0
- package/dist/shared/crypto.js +4 -0
- package/dist/shared/date.d.ts +2 -0
- package/dist/shared/date.d.ts.map +1 -0
- package/dist/shared/date.js +3 -0
- package/dist/shared/index.d.ts +9 -0
- package/dist/shared/index.d.ts.map +1 -0
- package/dist/shared/index.js +11 -0
- package/dist/shared/logger.d.ts +37 -0
- package/dist/shared/logger.d.ts.map +1 -0
- package/dist/shared/logger.js +45 -0
- package/dist/shared/store.d.ts +25 -0
- package/dist/shared/store.d.ts.map +1 -0
- package/dist/shared/store.js +18 -0
- package/dist/shared/telemetry-sdk.d.ts +26 -0
- package/dist/shared/telemetry-sdk.d.ts.map +1 -0
- package/dist/shared/telemetry-sdk.js +97 -0
- package/dist/shared/telemetry.d.ts +86 -0
- package/dist/shared/telemetry.d.ts.map +1 -0
- package/dist/shared/telemetry.js +137 -0
- package/dist/stark-settlement/application/aggregator-service.d.ts +112 -0
- package/dist/stark-settlement/application/aggregator-service.d.ts.map +1 -0
- package/dist/stark-settlement/application/aggregator-service.js +256 -0
- package/dist/stark-settlement/application/ledger-service.d.ts +114 -0
- package/dist/stark-settlement/application/ledger-service.d.ts.map +1 -0
- package/dist/stark-settlement/application/ledger-service.js +318 -0
- package/dist/stark-settlement/application/settlement-service.d.ts +104 -0
- package/dist/stark-settlement/application/settlement-service.d.ts.map +1 -0
- package/dist/stark-settlement/application/settlement-service.js +251 -0
- package/dist/stark-settlement/domain/entities.d.ts +365 -0
- package/dist/stark-settlement/domain/entities.d.ts.map +1 -0
- package/dist/stark-settlement/domain/entities.js +29 -0
- package/dist/stark-settlement/domain/ports.d.ts +485 -0
- package/dist/stark-settlement/domain/ports.d.ts.map +1 -0
- package/dist/stark-settlement/domain/ports.js +14 -0
- package/dist/stark-settlement/domain/value-objects.d.ts +268 -0
- package/dist/stark-settlement/domain/value-objects.d.ts.map +1 -0
- package/dist/stark-settlement/domain/value-objects.js +500 -0
- package/dist/stark-settlement/index.d.ts +172 -0
- package/dist/stark-settlement/index.d.ts.map +1 -0
- package/dist/stark-settlement/index.js +193 -0
- package/dist/stark-settlement/infrastructure/adapters/audit-adapter.d.ts +52 -0
- package/dist/stark-settlement/infrastructure/adapters/audit-adapter.d.ts.map +1 -0
- package/dist/stark-settlement/infrastructure/adapters/audit-adapter.js +154 -0
- package/dist/stark-settlement/infrastructure/adapters/bitcoin-adapter.d.ts +88 -0
- package/dist/stark-settlement/infrastructure/adapters/bitcoin-adapter.d.ts.map +1 -0
- package/dist/stark-settlement/infrastructure/adapters/bitcoin-adapter.js +187 -0
- package/dist/stark-settlement/infrastructure/adapters/clock-adapter.d.ts +59 -0
- package/dist/stark-settlement/infrastructure/adapters/clock-adapter.d.ts.map +1 -0
- package/dist/stark-settlement/infrastructure/adapters/clock-adapter.js +85 -0
- package/dist/stark-settlement/infrastructure/adapters/dilithium-adapter.d.ts +60 -0
- package/dist/stark-settlement/infrastructure/adapters/dilithium-adapter.d.ts.map +1 -0
- package/dist/stark-settlement/infrastructure/adapters/dilithium-adapter.js +104 -0
- package/dist/stark-settlement/infrastructure/adapters/event-emitter-adapter.d.ts +115 -0
- package/dist/stark-settlement/infrastructure/adapters/event-emitter-adapter.d.ts.map +1 -0
- package/dist/stark-settlement/infrastructure/adapters/event-emitter-adapter.js +191 -0
- package/dist/stark-settlement/infrastructure/adapters/fiat-adapter.d.ts +65 -0
- package/dist/stark-settlement/infrastructure/adapters/fiat-adapter.d.ts.map +1 -0
- package/dist/stark-settlement/infrastructure/adapters/fiat-adapter.js +207 -0
- package/dist/stark-settlement/infrastructure/adapters/mock-stark-adapter.d.ts +73 -0
- package/dist/stark-settlement/infrastructure/adapters/mock-stark-adapter.d.ts.map +1 -0
- package/dist/stark-settlement/infrastructure/adapters/mock-stark-adapter.js +287 -0
- package/dist/stark-settlement/infrastructure/adapters/solana-adapter.d.ts +78 -0
- package/dist/stark-settlement/infrastructure/adapters/solana-adapter.d.ts.map +1 -0
- package/dist/stark-settlement/infrastructure/adapters/solana-adapter.js +172 -0
- package/dist/stark-settlement/infrastructure/adapters/starknet-proof-adapter.d.ts +56 -0
- package/dist/stark-settlement/infrastructure/adapters/starknet-proof-adapter.d.ts.map +1 -0
- package/dist/stark-settlement/infrastructure/adapters/starknet-proof-adapter.js +261 -0
- package/dist/stark-settlement/infrastructure/adapters/stone-proof-adapter.d.ts +125 -0
- package/dist/stark-settlement/infrastructure/adapters/stone-proof-adapter.d.ts.map +1 -0
- package/dist/stark-settlement/infrastructure/adapters/stone-proof-adapter.js +416 -0
- package/dist/stark-settlement/infrastructure/persistence/ledger-store.d.ts +68 -0
- package/dist/stark-settlement/infrastructure/persistence/ledger-store.d.ts.map +1 -0
- package/dist/stark-settlement/infrastructure/persistence/ledger-store.js +238 -0
- package/dist/stark-settlement/infrastructure/persistence/offset-store.d.ts +30 -0
- package/dist/stark-settlement/infrastructure/persistence/offset-store.d.ts.map +1 -0
- package/dist/stark-settlement/infrastructure/persistence/offset-store.js +57 -0
- package/dist/stark-settlement/infrastructure/persistence/outbox-store.d.ts +45 -0
- package/dist/stark-settlement/infrastructure/persistence/outbox-store.d.ts.map +1 -0
- package/dist/stark-settlement/infrastructure/persistence/outbox-store.js +171 -0
- package/dist/traceability/application/recall-assessor.d.ts +13 -0
- package/dist/traceability/application/recall-assessor.d.ts.map +1 -0
- package/dist/traceability/application/recall-assessor.js +74 -0
- package/dist/traceability/domain/entities.d.ts +23 -0
- package/dist/traceability/domain/entities.d.ts.map +1 -0
- package/dist/traceability/domain/entities.js +1 -0
- package/dist/traceability/domain/ports.d.ts +23 -0
- package/dist/traceability/domain/ports.d.ts.map +1 -0
- package/dist/traceability/domain/ports.js +1 -0
- package/dist/traceability/domain/recall.d.ts +12 -0
- package/dist/traceability/domain/recall.d.ts.map +1 -0
- package/dist/traceability/domain/recall.js +1 -0
- package/dist/traceability/index.d.ts +22 -0
- package/dist/traceability/index.d.ts.map +1 -0
- package/dist/traceability/index.js +26 -0
- package/dist/traceability/infrastructure/in-memory-store.d.ts +13 -0
- package/dist/traceability/infrastructure/in-memory-store.d.ts.map +1 -0
- package/dist/traceability/infrastructure/in-memory-store.js +24 -0
- package/package.json +12 -9
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-Memory Ledger Store
|
|
3
|
+
*
|
|
4
|
+
* Implements LedgerPersistencePort for testing and demos.
|
|
5
|
+
* Stores all data in memory with no persistence.
|
|
6
|
+
*
|
|
7
|
+
* @see domain/ports.ts for interface definition
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* In-memory implementation of the ledger persistence port.
|
|
11
|
+
*/
|
|
12
|
+
export class InMemoryLedgerStore {
|
|
13
|
+
accounts = new Map();
|
|
14
|
+
accountsByAddress = new Map(); // address:asset -> accountId
|
|
15
|
+
transactions = new Map();
|
|
16
|
+
transactionsByIdempotencyKey = new Map();
|
|
17
|
+
baseProofs = new Map();
|
|
18
|
+
tier1Proofs = new Map();
|
|
19
|
+
tier2Proofs = new Map();
|
|
20
|
+
aggregatedBaseProofIds = new Set();
|
|
21
|
+
aggregatedTier1ProofIds = new Set();
|
|
22
|
+
// ─── Accounts ───────────────────────────────────────────────────────────
|
|
23
|
+
async createAccount(account) {
|
|
24
|
+
if (this.accounts.has(account.id)) {
|
|
25
|
+
throw new Error(`Account ${account.id} already exists`);
|
|
26
|
+
}
|
|
27
|
+
const addressKey = `${account.externalAddress}:${account.assetType}`;
|
|
28
|
+
if (this.accountsByAddress.has(addressKey)) {
|
|
29
|
+
throw new Error(`Account for ${account.externalAddress}:${account.assetType} already exists`);
|
|
30
|
+
}
|
|
31
|
+
this.accounts.set(account.id, account);
|
|
32
|
+
this.accountsByAddress.set(addressKey, account.id);
|
|
33
|
+
}
|
|
34
|
+
async getAccount(accountId) {
|
|
35
|
+
return this.accounts.get(accountId) ?? null;
|
|
36
|
+
}
|
|
37
|
+
async getAccountByAddress(externalAddress, assetType) {
|
|
38
|
+
const addressKey = `${externalAddress}:${assetType}`;
|
|
39
|
+
const accountId = this.accountsByAddress.get(addressKey);
|
|
40
|
+
if (!accountId)
|
|
41
|
+
return null;
|
|
42
|
+
return this.accounts.get(accountId) ?? null;
|
|
43
|
+
}
|
|
44
|
+
async updateAccountBalance(accountId, delta, newProofRoot) {
|
|
45
|
+
const account = this.accounts.get(accountId);
|
|
46
|
+
if (!account) {
|
|
47
|
+
throw new Error(`Account ${accountId} not found`);
|
|
48
|
+
}
|
|
49
|
+
const newBalance = account.balance + delta;
|
|
50
|
+
if (newBalance < 0n) {
|
|
51
|
+
throw new Error(`Insufficient balance: ${account.balance} + ${delta} = ${newBalance}`);
|
|
52
|
+
}
|
|
53
|
+
const updatedAccount = {
|
|
54
|
+
...account,
|
|
55
|
+
balance: newBalance,
|
|
56
|
+
lastProofRoot: newProofRoot,
|
|
57
|
+
updatedAt: Date.now(),
|
|
58
|
+
};
|
|
59
|
+
this.accounts.set(accountId, updatedAccount);
|
|
60
|
+
}
|
|
61
|
+
async getAccountsByAssetType(assetType) {
|
|
62
|
+
const accounts = [];
|
|
63
|
+
for (const account of this.accounts.values()) {
|
|
64
|
+
if (account.assetType === assetType) {
|
|
65
|
+
accounts.push(account);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return accounts;
|
|
69
|
+
}
|
|
70
|
+
// ─── Transactions ───────────────────────────────────────────────────────
|
|
71
|
+
async appendTransaction(tx) {
|
|
72
|
+
if (this.transactions.has(tx.txId)) {
|
|
73
|
+
throw new Error(`Transaction ${tx.txId} already exists`);
|
|
74
|
+
}
|
|
75
|
+
this.transactions.set(tx.txId, tx);
|
|
76
|
+
// Index by idempotency key
|
|
77
|
+
const existingTxIds = this.transactionsByIdempotencyKey.get(tx.idempotencyKey) ?? [];
|
|
78
|
+
this.transactionsByIdempotencyKey.set(tx.idempotencyKey, [
|
|
79
|
+
...existingTxIds,
|
|
80
|
+
tx.txId,
|
|
81
|
+
]);
|
|
82
|
+
}
|
|
83
|
+
async getTransaction(txId) {
|
|
84
|
+
return this.transactions.get(txId) ?? null;
|
|
85
|
+
}
|
|
86
|
+
async getTransactionsByIdempotencyKey(idempotencyKey) {
|
|
87
|
+
const txIds = this.transactionsByIdempotencyKey.get(idempotencyKey) ?? [];
|
|
88
|
+
const transactions = [];
|
|
89
|
+
for (const txId of txIds) {
|
|
90
|
+
const tx = this.transactions.get(txId);
|
|
91
|
+
if (tx)
|
|
92
|
+
transactions.push(tx);
|
|
93
|
+
}
|
|
94
|
+
return transactions;
|
|
95
|
+
}
|
|
96
|
+
async getPendingTransactions(limit) {
|
|
97
|
+
if (limit <= 0)
|
|
98
|
+
return [];
|
|
99
|
+
const pending = [];
|
|
100
|
+
for (const tx of this.transactions.values()) {
|
|
101
|
+
if (tx.status === "pending") {
|
|
102
|
+
pending.push(tx);
|
|
103
|
+
if (pending.length >= limit)
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
// Sort by createdAt for deterministic ordering
|
|
108
|
+
return pending.sort((a, b) => a.createdAt - b.createdAt);
|
|
109
|
+
}
|
|
110
|
+
async updateTransactionStatus(txId, status) {
|
|
111
|
+
const tx = this.transactions.get(txId);
|
|
112
|
+
if (!tx) {
|
|
113
|
+
throw new Error(`Transaction ${txId} not found`);
|
|
114
|
+
}
|
|
115
|
+
const updatedTx = {
|
|
116
|
+
...tx,
|
|
117
|
+
status,
|
|
118
|
+
updatedAt: Date.now(),
|
|
119
|
+
};
|
|
120
|
+
this.transactions.set(txId, updatedTx);
|
|
121
|
+
}
|
|
122
|
+
// ─── Base Proofs ────────────────────────────────────────────────────────
|
|
123
|
+
async saveBaseProof(proof) {
|
|
124
|
+
if (this.baseProofs.has(proof.proofId)) {
|
|
125
|
+
throw new Error(`Base proof ${proof.proofId} already exists`);
|
|
126
|
+
}
|
|
127
|
+
this.baseProofs.set(proof.proofId, proof);
|
|
128
|
+
}
|
|
129
|
+
async getBaseProof(proofId) {
|
|
130
|
+
return this.baseProofs.get(proofId) ?? null;
|
|
131
|
+
}
|
|
132
|
+
async getUnaggregatedBaseProofs(limit) {
|
|
133
|
+
if (limit <= 0)
|
|
134
|
+
return [];
|
|
135
|
+
const proofs = [];
|
|
136
|
+
for (const proof of this.baseProofs.values()) {
|
|
137
|
+
if (!this.aggregatedBaseProofIds.has(proof.proofId)) {
|
|
138
|
+
proofs.push(proof);
|
|
139
|
+
if (proofs.length >= limit)
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
// Sort by createdAt for deterministic ordering
|
|
144
|
+
return proofs.sort((a, b) => a.createdAt - b.createdAt);
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Mark base proofs as aggregated (internal method for aggregator).
|
|
148
|
+
*/
|
|
149
|
+
markBaseProofsAggregated(proofIds) {
|
|
150
|
+
for (const proofId of proofIds) {
|
|
151
|
+
this.aggregatedBaseProofIds.add(proofId);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
// ─── Tier-1 Proofs ──────────────────────────────────────────────────────
|
|
155
|
+
async saveTier1Proof(proof) {
|
|
156
|
+
if (this.tier1Proofs.has(proof.proofId)) {
|
|
157
|
+
throw new Error(`Tier-1 proof ${proof.proofId} already exists`);
|
|
158
|
+
}
|
|
159
|
+
this.tier1Proofs.set(proof.proofId, proof);
|
|
160
|
+
// Mark base proofs as aggregated
|
|
161
|
+
this.markBaseProofsAggregated(proof.baseProofIds);
|
|
162
|
+
}
|
|
163
|
+
async getTier1Proof(proofId) {
|
|
164
|
+
return this.tier1Proofs.get(proofId) ?? null;
|
|
165
|
+
}
|
|
166
|
+
async getUnaggregatedTier1Proofs(limit) {
|
|
167
|
+
if (limit <= 0)
|
|
168
|
+
return [];
|
|
169
|
+
const proofs = [];
|
|
170
|
+
for (const proof of this.tier1Proofs.values()) {
|
|
171
|
+
if (!this.aggregatedTier1ProofIds.has(proof.proofId)) {
|
|
172
|
+
proofs.push(proof);
|
|
173
|
+
if (proofs.length >= limit)
|
|
174
|
+
break;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
// Sort by createdAt for deterministic ordering
|
|
178
|
+
return proofs.sort((a, b) => a.createdAt - b.createdAt);
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Mark Tier-1 proofs as aggregated (internal method for aggregator).
|
|
182
|
+
*/
|
|
183
|
+
markTier1ProofsAggregated(proofIds) {
|
|
184
|
+
for (const proofId of proofIds) {
|
|
185
|
+
this.aggregatedTier1ProofIds.add(proofId);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
// ─── Tier-2 Proofs ──────────────────────────────────────────────────────
|
|
189
|
+
async saveTier2Proof(proof) {
|
|
190
|
+
if (this.tier2Proofs.has(proof.blockProofId)) {
|
|
191
|
+
throw new Error(`Tier-2 proof ${proof.blockProofId} already exists`);
|
|
192
|
+
}
|
|
193
|
+
this.tier2Proofs.set(proof.blockProofId, proof);
|
|
194
|
+
// Mark Tier-1 proofs as aggregated
|
|
195
|
+
this.markTier1ProofsAggregated(proof.tier1ProofIds);
|
|
196
|
+
}
|
|
197
|
+
async getTier2Proof(blockProofId) {
|
|
198
|
+
return this.tier2Proofs.get(blockProofId) ?? null;
|
|
199
|
+
}
|
|
200
|
+
async getLatestBlockProof() {
|
|
201
|
+
let latest = null;
|
|
202
|
+
for (const proof of this.tier2Proofs.values()) {
|
|
203
|
+
if (!latest || proof.blockNumber > latest.blockNumber) {
|
|
204
|
+
latest = proof;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return latest;
|
|
208
|
+
}
|
|
209
|
+
// ─── Utilities ──────────────────────────────────────────────────────────
|
|
210
|
+
/**
|
|
211
|
+
* Get statistics about the store (for debugging/monitoring).
|
|
212
|
+
*/
|
|
213
|
+
getStats() {
|
|
214
|
+
return {
|
|
215
|
+
accounts: this.accounts.size,
|
|
216
|
+
transactions: this.transactions.size,
|
|
217
|
+
baseProofs: this.baseProofs.size,
|
|
218
|
+
tier1Proofs: this.tier1Proofs.size,
|
|
219
|
+
tier2Proofs: this.tier2Proofs.size,
|
|
220
|
+
aggregatedBaseProofs: this.aggregatedBaseProofIds.size,
|
|
221
|
+
aggregatedTier1Proofs: this.aggregatedTier1ProofIds.size,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Clear all data (for testing).
|
|
226
|
+
*/
|
|
227
|
+
clear() {
|
|
228
|
+
this.accounts.clear();
|
|
229
|
+
this.accountsByAddress.clear();
|
|
230
|
+
this.transactions.clear();
|
|
231
|
+
this.transactionsByIdempotencyKey.clear();
|
|
232
|
+
this.baseProofs.clear();
|
|
233
|
+
this.tier1Proofs.clear();
|
|
234
|
+
this.tier2Proofs.clear();
|
|
235
|
+
this.aggregatedBaseProofIds.clear();
|
|
236
|
+
this.aggregatedTier1ProofIds.clear();
|
|
237
|
+
}
|
|
238
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-Memory Offset Store
|
|
3
|
+
*
|
|
4
|
+
* Implements OffsetTrackingPort for exactly-once consumption.
|
|
5
|
+
* Tracks the last committed offset per consumer per asset type.
|
|
6
|
+
*
|
|
7
|
+
* @see domain/ports.ts for interface definition
|
|
8
|
+
*/
|
|
9
|
+
import type { AssetType } from "../../domain/entities.js";
|
|
10
|
+
import type { OffsetTrackingPort } from "../../domain/ports.js";
|
|
11
|
+
/**
|
|
12
|
+
* In-memory implementation of the offset tracking port.
|
|
13
|
+
*/
|
|
14
|
+
export declare class InMemoryOffsetStore implements OffsetTrackingPort {
|
|
15
|
+
private readonly offsets;
|
|
16
|
+
private static readonly INITIAL_OFFSET;
|
|
17
|
+
private makeKey;
|
|
18
|
+
getOffset(consumerId: string, assetType: AssetType): Promise<bigint>;
|
|
19
|
+
commitOffset(consumerId: string, assetType: AssetType, offset: bigint): Promise<void>;
|
|
20
|
+
getConsumerOffsets(assetType: AssetType): Promise<ReadonlyMap<string, bigint>>;
|
|
21
|
+
/**
|
|
22
|
+
* Get all offsets (for debugging).
|
|
23
|
+
*/
|
|
24
|
+
getAllOffsets(): ReadonlyMap<string, bigint>;
|
|
25
|
+
/**
|
|
26
|
+
* Clear all offsets (for testing).
|
|
27
|
+
*/
|
|
28
|
+
clear(): void;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=offset-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"offset-store.d.ts","sourceRoot":"","sources":["../../../../src/stark-settlement/infrastructure/persistence/offset-store.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAEhE;;GAEG;AACH,qBAAa,mBAAoB,YAAW,kBAAkB;IAG5D,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA6B;IACrD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAO;IAE7C,OAAO,CAAC,OAAO;IAIT,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC;IAKpE,YAAY,CAChB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,SAAS,EACpB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC;IAeV,kBAAkB,CACtB,SAAS,EAAE,SAAS,GACnB,OAAO,CAAC,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAgBvC;;OAEG;IACH,aAAa,IAAI,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC;IAI5C;;OAEG;IACH,KAAK,IAAI,IAAI;CAGd"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-Memory Offset Store
|
|
3
|
+
*
|
|
4
|
+
* Implements OffsetTrackingPort for exactly-once consumption.
|
|
5
|
+
* Tracks the last committed offset per consumer per asset type.
|
|
6
|
+
*
|
|
7
|
+
* @see domain/ports.ts for interface definition
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* In-memory implementation of the offset tracking port.
|
|
11
|
+
*/
|
|
12
|
+
export class InMemoryOffsetStore {
|
|
13
|
+
// Key: "consumerId:assetType", Value: last committed offset
|
|
14
|
+
// Initial value is -1n so that offset 0n can be committed (0n > -1n)
|
|
15
|
+
offsets = new Map();
|
|
16
|
+
static INITIAL_OFFSET = -1n;
|
|
17
|
+
makeKey(consumerId, assetType) {
|
|
18
|
+
return `${consumerId}:${assetType}`;
|
|
19
|
+
}
|
|
20
|
+
async getOffset(consumerId, assetType) {
|
|
21
|
+
const key = this.makeKey(consumerId, assetType);
|
|
22
|
+
return this.offsets.get(key) ?? InMemoryOffsetStore.INITIAL_OFFSET;
|
|
23
|
+
}
|
|
24
|
+
async commitOffset(consumerId, assetType, offset) {
|
|
25
|
+
const key = this.makeKey(consumerId, assetType);
|
|
26
|
+
const currentOffset = this.offsets.get(key) ?? InMemoryOffsetStore.INITIAL_OFFSET;
|
|
27
|
+
// Offset must be monotonically increasing (strictly greater than current)
|
|
28
|
+
if (offset <= currentOffset) {
|
|
29
|
+
throw new Error(`Cannot commit offset ${offset}: current offset is ${currentOffset}`);
|
|
30
|
+
}
|
|
31
|
+
this.offsets.set(key, offset);
|
|
32
|
+
}
|
|
33
|
+
async getConsumerOffsets(assetType) {
|
|
34
|
+
const result = new Map();
|
|
35
|
+
const suffix = `:${assetType}`;
|
|
36
|
+
for (const [key, offset] of this.offsets) {
|
|
37
|
+
if (key.endsWith(suffix)) {
|
|
38
|
+
const consumerId = key.slice(0, -suffix.length);
|
|
39
|
+
result.set(consumerId, offset);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return result;
|
|
43
|
+
}
|
|
44
|
+
// ─── Utilities ──────────────────────────────────────────────────────────
|
|
45
|
+
/**
|
|
46
|
+
* Get all offsets (for debugging).
|
|
47
|
+
*/
|
|
48
|
+
getAllOffsets() {
|
|
49
|
+
return new Map(this.offsets);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Clear all offsets (for testing).
|
|
53
|
+
*/
|
|
54
|
+
clear() {
|
|
55
|
+
this.offsets.clear();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-Memory Outbox Store
|
|
3
|
+
*
|
|
4
|
+
* Implements OutboxPort for the settlement queue.
|
|
5
|
+
* Append-only queue with exactly-once consumption semantics.
|
|
6
|
+
*
|
|
7
|
+
* @see domain/ports.ts for interface definition
|
|
8
|
+
*/
|
|
9
|
+
import type { AssetType, OutboxEntry } from "../../domain/entities.js";
|
|
10
|
+
import type { OutboxPort } from "../../domain/ports.js";
|
|
11
|
+
/**
|
|
12
|
+
* In-memory implementation of the outbox port.
|
|
13
|
+
*/
|
|
14
|
+
export declare class InMemoryOutboxStore implements OutboxPort {
|
|
15
|
+
private readonly entries;
|
|
16
|
+
private readonly entriesByAsset;
|
|
17
|
+
private nextOffset;
|
|
18
|
+
appendEntry(entry: OutboxEntry): Promise<OutboxEntry>;
|
|
19
|
+
getPendingEntries(assetType: AssetType, limit: number): Promise<readonly OutboxEntry[]>;
|
|
20
|
+
getEntry(entryId: string): Promise<OutboxEntry | null>;
|
|
21
|
+
markProcessing(entryId: string): Promise<void>;
|
|
22
|
+
markSettled(entryId: string, settlementTxId: string): Promise<void>;
|
|
23
|
+
markFailed(entryId: string, errorMessage: string): Promise<void>;
|
|
24
|
+
getRetryableEntries(assetType: AssetType, limit: number): Promise<readonly OutboxEntry[]>;
|
|
25
|
+
/**
|
|
26
|
+
* Get statistics about the outbox (for debugging/monitoring).
|
|
27
|
+
*/
|
|
28
|
+
getStats(): {
|
|
29
|
+
total: number;
|
|
30
|
+
pending: number;
|
|
31
|
+
processing: number;
|
|
32
|
+
settled: number;
|
|
33
|
+
failed: number;
|
|
34
|
+
byAsset: Record<AssetType, number>;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Get the next offset value (for testing).
|
|
38
|
+
*/
|
|
39
|
+
getNextOffset(): bigint;
|
|
40
|
+
/**
|
|
41
|
+
* Clear all entries (for testing).
|
|
42
|
+
*/
|
|
43
|
+
clear(): void;
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=outbox-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"outbox-store.d.ts","sourceRoot":"","sources":["../../../../src/stark-settlement/infrastructure/persistence/outbox-store.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAExD;;GAEG;AACH,qBAAa,mBAAoB,YAAW,UAAU;IACpD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAkC;IAC1D,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAkC;IACjE,OAAO,CAAC,UAAU,CAAM;IAElB,WAAW,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;IA4BrD,iBAAiB,CACrB,SAAS,EAAE,SAAS,EACpB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,SAAS,WAAW,EAAE,CAAC;IAkB5B,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAItD,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAoB9C,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAsBnE,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAsBhE,mBAAmB,CACvB,SAAS,EAAE,SAAS,EACpB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,SAAS,WAAW,EAAE,CAAC;IAwBlC;;OAEG;IACH,QAAQ,IAAI;QACV,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;QACnB,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;KACpC;IAmCD;;OAEG;IACH,aAAa,IAAI,MAAM;IAIvB;;OAEG;IACH,KAAK,IAAI,IAAI;CAKd"}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-Memory Outbox Store
|
|
3
|
+
*
|
|
4
|
+
* Implements OutboxPort for the settlement queue.
|
|
5
|
+
* Append-only queue with exactly-once consumption semantics.
|
|
6
|
+
*
|
|
7
|
+
* @see domain/ports.ts for interface definition
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* In-memory implementation of the outbox port.
|
|
11
|
+
*/
|
|
12
|
+
export class InMemoryOutboxStore {
|
|
13
|
+
entries = new Map();
|
|
14
|
+
entriesByAsset = new Map();
|
|
15
|
+
nextOffset = 0n;
|
|
16
|
+
async appendEntry(entry) {
|
|
17
|
+
if (this.entries.has(entry.entryId)) {
|
|
18
|
+
throw new Error(`Outbox entry ${entry.entryId} already exists`);
|
|
19
|
+
}
|
|
20
|
+
// Always assign offsets in the store to preserve append-order FIFO semantics.
|
|
21
|
+
// This ensures entries are correctly ordered regardless of what offset value was passed.
|
|
22
|
+
const offset = this.nextOffset;
|
|
23
|
+
this.nextOffset += 1n;
|
|
24
|
+
const entryWithOffset = {
|
|
25
|
+
...entry,
|
|
26
|
+
offset,
|
|
27
|
+
};
|
|
28
|
+
this.entries.set(entry.entryId, entryWithOffset);
|
|
29
|
+
// Index by asset type - mutate existing array to avoid O(n) copies
|
|
30
|
+
let assetEntries = this.entriesByAsset.get(entry.assetType);
|
|
31
|
+
if (!assetEntries) {
|
|
32
|
+
assetEntries = [];
|
|
33
|
+
this.entriesByAsset.set(entry.assetType, assetEntries);
|
|
34
|
+
}
|
|
35
|
+
assetEntries.push(entry.entryId);
|
|
36
|
+
return entryWithOffset;
|
|
37
|
+
}
|
|
38
|
+
async getPendingEntries(assetType, limit) {
|
|
39
|
+
if (limit <= 0)
|
|
40
|
+
return [];
|
|
41
|
+
const entryIds = this.entriesByAsset.get(assetType) ?? [];
|
|
42
|
+
const pending = [];
|
|
43
|
+
// entryIds are appended in FIFO order, so no sorting needed
|
|
44
|
+
for (const entryId of entryIds) {
|
|
45
|
+
const entry = this.entries.get(entryId);
|
|
46
|
+
if (entry && entry.status === "pending") {
|
|
47
|
+
pending.push(entry);
|
|
48
|
+
if (pending.length >= limit)
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return pending;
|
|
53
|
+
}
|
|
54
|
+
async getEntry(entryId) {
|
|
55
|
+
return this.entries.get(entryId) ?? null;
|
|
56
|
+
}
|
|
57
|
+
async markProcessing(entryId) {
|
|
58
|
+
const entry = this.entries.get(entryId);
|
|
59
|
+
if (!entry) {
|
|
60
|
+
throw new Error(`Outbox entry ${entryId} not found`);
|
|
61
|
+
}
|
|
62
|
+
if (entry.status !== "pending" && entry.status !== "failed") {
|
|
63
|
+
throw new Error(`Cannot mark entry ${entryId} as processing: current status is ${entry.status}`);
|
|
64
|
+
}
|
|
65
|
+
const updatedEntry = {
|
|
66
|
+
...entry,
|
|
67
|
+
status: "processing",
|
|
68
|
+
};
|
|
69
|
+
this.entries.set(entryId, updatedEntry);
|
|
70
|
+
}
|
|
71
|
+
async markSettled(entryId, settlementTxId) {
|
|
72
|
+
const entry = this.entries.get(entryId);
|
|
73
|
+
if (!entry) {
|
|
74
|
+
throw new Error(`Outbox entry ${entryId} not found`);
|
|
75
|
+
}
|
|
76
|
+
if (entry.status !== "processing") {
|
|
77
|
+
throw new Error(`Cannot mark entry ${entryId} as settled: current status is ${entry.status}`);
|
|
78
|
+
}
|
|
79
|
+
const updatedEntry = {
|
|
80
|
+
...entry,
|
|
81
|
+
status: "settled",
|
|
82
|
+
settledAt: Date.now(),
|
|
83
|
+
settlementTxId,
|
|
84
|
+
};
|
|
85
|
+
this.entries.set(entryId, updatedEntry);
|
|
86
|
+
}
|
|
87
|
+
async markFailed(entryId, errorMessage) {
|
|
88
|
+
const entry = this.entries.get(entryId);
|
|
89
|
+
if (!entry) {
|
|
90
|
+
throw new Error(`Outbox entry ${entryId} not found`);
|
|
91
|
+
}
|
|
92
|
+
if (entry.status !== "processing") {
|
|
93
|
+
throw new Error(`Cannot mark entry ${entryId} as failed: current status is ${entry.status}`);
|
|
94
|
+
}
|
|
95
|
+
const updatedEntry = {
|
|
96
|
+
...entry,
|
|
97
|
+
status: "failed",
|
|
98
|
+
retryCount: entry.retryCount + 1,
|
|
99
|
+
errorMessage,
|
|
100
|
+
};
|
|
101
|
+
this.entries.set(entryId, updatedEntry);
|
|
102
|
+
}
|
|
103
|
+
async getRetryableEntries(assetType, limit) {
|
|
104
|
+
if (limit <= 0)
|
|
105
|
+
return [];
|
|
106
|
+
const entryIds = this.entriesByAsset.get(assetType) ?? [];
|
|
107
|
+
const retryable = [];
|
|
108
|
+
// entryIds are appended in FIFO order, so no sorting needed
|
|
109
|
+
for (const entryId of entryIds) {
|
|
110
|
+
const entry = this.entries.get(entryId);
|
|
111
|
+
if (entry &&
|
|
112
|
+
entry.status === "failed" &&
|
|
113
|
+
entry.retryCount < entry.maxRetries) {
|
|
114
|
+
retryable.push(entry);
|
|
115
|
+
if (retryable.length >= limit)
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return retryable;
|
|
120
|
+
}
|
|
121
|
+
// ─── Utilities ──────────────────────────────────────────────────────────
|
|
122
|
+
/**
|
|
123
|
+
* Get statistics about the outbox (for debugging/monitoring).
|
|
124
|
+
*/
|
|
125
|
+
getStats() {
|
|
126
|
+
let pending = 0;
|
|
127
|
+
let processing = 0;
|
|
128
|
+
let settled = 0;
|
|
129
|
+
let failed = 0;
|
|
130
|
+
const byAsset = { SOL: 0, BTC: 0, USD: 0 };
|
|
131
|
+
for (const entry of this.entries.values()) {
|
|
132
|
+
switch (entry.status) {
|
|
133
|
+
case "pending":
|
|
134
|
+
pending++;
|
|
135
|
+
break;
|
|
136
|
+
case "processing":
|
|
137
|
+
processing++;
|
|
138
|
+
break;
|
|
139
|
+
case "settled":
|
|
140
|
+
settled++;
|
|
141
|
+
break;
|
|
142
|
+
case "failed":
|
|
143
|
+
failed++;
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
byAsset[entry.assetType]++;
|
|
147
|
+
}
|
|
148
|
+
return {
|
|
149
|
+
total: this.entries.size,
|
|
150
|
+
pending,
|
|
151
|
+
processing,
|
|
152
|
+
settled,
|
|
153
|
+
failed,
|
|
154
|
+
byAsset,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Get the next offset value (for testing).
|
|
159
|
+
*/
|
|
160
|
+
getNextOffset() {
|
|
161
|
+
return this.nextOffset;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Clear all entries (for testing).
|
|
165
|
+
*/
|
|
166
|
+
clear() {
|
|
167
|
+
this.entries.clear();
|
|
168
|
+
this.entriesByAsset.clear();
|
|
169
|
+
this.nextOffset = 0n;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { RecallAssessment, RecallRule } from "../domain/recall.js";
|
|
2
|
+
import type { TraceabilityRepository } from "../domain/ports.js";
|
|
3
|
+
import type { Logger } from "../../shared/logger.js";
|
|
4
|
+
export declare class RecallAssessor {
|
|
5
|
+
private readonly repo;
|
|
6
|
+
private readonly logger;
|
|
7
|
+
constructor(repo: TraceabilityRepository, logger?: Logger);
|
|
8
|
+
assess(rule: RecallRule): RecallAssessment;
|
|
9
|
+
private flagLots;
|
|
10
|
+
private flagBreachedShipments;
|
|
11
|
+
private propagateImpact;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=recall-assessor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recall-assessor.d.ts","sourceRoot":"","sources":["../../../src/traceability/application/recall-assessor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACxE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAGrD,qBAAa,cAAc;IAIvB,OAAO,CAAC,QAAQ,CAAC,IAAI;IAHvB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAGb,IAAI,EAAE,sBAAsB,EAC7C,MAAM,CAAC,EAAE,MAAM;IAKjB,MAAM,CAAC,IAAI,EAAE,UAAU,GAAG,gBAAgB;IA0C1C,OAAO,CAAC,QAAQ;IAuBhB,OAAO,CAAC,qBAAqB;IAwB7B,OAAO,CAAC,eAAe;CAYxB"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { noopLogger } from "../../shared/logger.js";
|
|
2
|
+
export class RecallAssessor {
|
|
3
|
+
repo;
|
|
4
|
+
logger;
|
|
5
|
+
constructor(repo, logger) {
|
|
6
|
+
this.repo = repo;
|
|
7
|
+
this.logger = logger ?? noopLogger;
|
|
8
|
+
}
|
|
9
|
+
assess(rule) {
|
|
10
|
+
const start = Date.now();
|
|
11
|
+
this.logger.info("assessment started", {
|
|
12
|
+
operation: "RecallAssessor.assess",
|
|
13
|
+
flaggedLotCount: rule.flaggedLotIds.length,
|
|
14
|
+
flaggedLotIdsCsv: rule.flaggedLotIds.join(","),
|
|
15
|
+
});
|
|
16
|
+
const lotIds = new Set();
|
|
17
|
+
const shipmentIds = new Set();
|
|
18
|
+
const destinations = new Set();
|
|
19
|
+
const reasons = new Set();
|
|
20
|
+
this.flagLots(rule, lotIds, reasons);
|
|
21
|
+
this.flagBreachedShipments(rule, lotIds, shipmentIds, destinations, reasons);
|
|
22
|
+
this.propagateImpact(lotIds, shipmentIds, destinations);
|
|
23
|
+
const result = {
|
|
24
|
+
impactedLotIds: [...lotIds].sort(),
|
|
25
|
+
impactedShipmentIds: [...shipmentIds].sort(),
|
|
26
|
+
impactedDestinations: [...destinations].sort(),
|
|
27
|
+
reasons: [...reasons],
|
|
28
|
+
};
|
|
29
|
+
this.logger.info("assessment completed", {
|
|
30
|
+
operation: "RecallAssessor.assess",
|
|
31
|
+
flaggedLotCount: rule.flaggedLotIds.length,
|
|
32
|
+
flaggedLotIdsCsv: rule.flaggedLotIds.join(","),
|
|
33
|
+
result: result.impactedLotIds.length > 0 ? "impacted" : "safe",
|
|
34
|
+
durationMs: Date.now() - start,
|
|
35
|
+
impactedLots: result.impactedLotIds.length,
|
|
36
|
+
});
|
|
37
|
+
return result;
|
|
38
|
+
}
|
|
39
|
+
flagLots(rule, lotIds, reasons) {
|
|
40
|
+
// Convert arrays to Sets for O(1) lookups instead of O(n) includes()
|
|
41
|
+
const flaggedLotIdSet = new Set(rule.flaggedLotIds);
|
|
42
|
+
const suspectSupplierSet = new Set(rule.suspectSuppliers);
|
|
43
|
+
for (const lot of this.repo.lots.values()) {
|
|
44
|
+
if (flaggedLotIdSet.has(lot.id)) {
|
|
45
|
+
lotIds.add(lot.id);
|
|
46
|
+
reasons.add(`Lot ${lot.id} was explicitly flagged by quality assurance.`);
|
|
47
|
+
}
|
|
48
|
+
if (suspectSupplierSet.has(lot.supplier)) {
|
|
49
|
+
lotIds.add(lot.id);
|
|
50
|
+
reasons.add(`Supplier ${lot.supplier} was placed under investigation.`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
flagBreachedShipments(rule, lotIds, shipmentIds, destinations, reasons) {
|
|
55
|
+
for (const shipment of this.repo.shipments.values()) {
|
|
56
|
+
const readings = this.repo.getTelemetry(shipment.id);
|
|
57
|
+
const breached = readings.some((r) => r.temperatureCelsius > rule.maxTemperatureCelsius);
|
|
58
|
+
if (breached) {
|
|
59
|
+
lotIds.add(shipment.lotId);
|
|
60
|
+
shipmentIds.add(shipment.id);
|
|
61
|
+
destinations.add(shipment.to);
|
|
62
|
+
reasons.add(`Shipment ${shipment.id} exceeded ${rule.maxTemperatureCelsius}C cold-chain limits.`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
propagateImpact(lotIds, shipmentIds, destinations) {
|
|
67
|
+
for (const shipment of this.repo.shipments.values()) {
|
|
68
|
+
if (lotIds.has(shipment.lotId)) {
|
|
69
|
+
shipmentIds.add(shipment.id);
|
|
70
|
+
destinations.add(shipment.to);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface ProductLot {
|
|
2
|
+
readonly id: string;
|
|
3
|
+
readonly productName: string;
|
|
4
|
+
readonly supplier: string;
|
|
5
|
+
readonly originCountry: string;
|
|
6
|
+
readonly harvestDate: string;
|
|
7
|
+
readonly expirationDate: string;
|
|
8
|
+
}
|
|
9
|
+
export interface Shipment {
|
|
10
|
+
readonly id: string;
|
|
11
|
+
readonly lotId: string;
|
|
12
|
+
readonly from: string;
|
|
13
|
+
readonly to: string;
|
|
14
|
+
readonly departedAt: string;
|
|
15
|
+
readonly receivedAt?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface TelemetryReading {
|
|
18
|
+
readonly shipmentId: string;
|
|
19
|
+
readonly timestamp: string;
|
|
20
|
+
readonly temperatureCelsius: number;
|
|
21
|
+
readonly location: string;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=entities.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entities.d.ts","sourceRoot":"","sources":["../../../src/traceability/domain/entities.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACjC;AAED,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|