@wishknish/knishio-client-ts 0.9.6 → 0.9.8

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.cjs CHANGED
@@ -527,6 +527,55 @@ var init_TransferBalanceException = __esm({
527
527
  }
528
528
  });
529
529
 
530
+ // src/exception/SecretStorageException.ts
531
+ exports.SecretStorageException = void 0;
532
+ var init_SecretStorageException = __esm({
533
+ "src/exception/SecretStorageException.ts"() {
534
+ init_BaseException();
535
+ exports.SecretStorageException = class _SecretStorageException extends exports.BaseException {
536
+ constructor(message = "Secret storage operation failed", options = {}) {
537
+ super("WALLET_CREDENTIAL_ERROR", message, {
538
+ code: "SECRET_STORAGE_ERROR",
539
+ ...options
540
+ });
541
+ }
542
+ /**
543
+ * Secret not found for the requested bundle hash
544
+ */
545
+ static notFound(bundleHash) {
546
+ return new _SecretStorageException(`Secret not found for bundle: ${bundleHash}`, {
547
+ code: "SECRET_NOT_FOUND",
548
+ details: { bundleHash }
549
+ });
550
+ }
551
+ /**
552
+ * Decryption failed (wrong passphrase or corrupted payload)
553
+ */
554
+ static decryptionFailed(reason) {
555
+ return new _SecretStorageException(
556
+ `Failed to decrypt master secret: ${reason || "Invalid passphrase or corrupted ciphertext"}`,
557
+ {
558
+ code: "DECRYPTION_FAILED",
559
+ details: { reason }
560
+ }
561
+ );
562
+ }
563
+ /**
564
+ * Provider is unavailable in current platform
565
+ */
566
+ static unavailable(provider, reason) {
567
+ return new _SecretStorageException(
568
+ `Secret storage provider '${provider}' is unavailable: ${reason || "Hardware or API not accessible"}`,
569
+ {
570
+ code: "STORAGE_UNAVAILABLE",
571
+ details: { provider, reason }
572
+ }
573
+ );
574
+ }
575
+ };
576
+ }
577
+ });
578
+
530
579
  // src/exception/InvalidResponseException.ts
531
580
  exports.InvalidResponseException = void 0;
532
581
  var init_InvalidResponseException = __esm({
@@ -989,6 +1038,7 @@ var init_exception = __esm({
989
1038
  init_SignatureMismatchException();
990
1039
  init_TransferBalanceException();
991
1040
  init_WalletCredentialException();
1041
+ init_SecretStorageException();
992
1042
  init_InvalidResponseException();
993
1043
  init_BalanceInsufficientException();
994
1044
  init_BatchIdException();
@@ -4115,7 +4165,8 @@ zod.z.object({
4115
4165
  socket: zod.z.unknown().optional(),
4116
4166
  serverSdkVersion: zod.z.number().int().min(1).optional(),
4117
4167
  logging: zod.z.boolean().optional(),
4118
- defaultRequestPolicy: zod.z.enum(["cache-first", "cache-only", "network-only", "cache-and-network"]).nullable().optional()
4168
+ defaultRequestPolicy: zod.z.enum(["cache-first", "cache-only", "network-only", "cache-and-network"]).nullable().optional(),
4169
+ secretStorage: zod.z.unknown().optional()
4119
4170
  }).strict();
4120
4171
  zod.z.object({
4121
4172
  token: zod.z.string().min(1, "Auth token cannot be empty"),
@@ -6915,7 +6966,9 @@ var KnishIOClientConfigSchema2 = zod.z.object({
6915
6966
  // Optional default urql request policy for reads (server/sync clients pass
6916
6967
  // 'network-only'). Permitted by the strict schema so the constructor option
6917
6968
  // isn't rejected.
6918
- defaultRequestPolicy: zod.z.enum(["cache-first", "cache-only", "network-only", "cache-and-network"]).nullable().optional()
6969
+ defaultRequestPolicy: zod.z.enum(["cache-first", "cache-only", "network-only", "cache-and-network"]).nullable().optional(),
6970
+ // Pluggable hardware envelope encryption secret storage provider
6971
+ secretStorage: zod.z.unknown().optional()
6919
6972
  }).strict();
6920
6973
  var EnvironmentConfigSchema = zod.z.object({
6921
6974
  NODE_ENV: zod.z.enum(["development", "production", "test"]).optional(),
@@ -9489,6 +9542,13 @@ var MutationProposeMolecule = class extends Mutation {
9489
9542
  molecule() {
9490
9543
  return this.$__molecule.toJSON ? this.$__molecule.toJSON() : this.$__molecule;
9491
9544
  }
9545
+ /**
9546
+ * Fills the molecule with specific mutation data.
9547
+ * Subclasses override to build domain-specific atoms.
9548
+ * Default implementation is a no-op for pre-assembled molecules.
9549
+ */
9550
+ fillMolecule(_params) {
9551
+ }
9492
9552
  };
9493
9553
 
9494
9554
  // src/response/ResponseCreateToken.ts
@@ -10453,9 +10513,134 @@ var ActiveSessionSubscribe = class extends Subscribe {
10453
10513
 
10454
10514
  // src/KnishIOClient.ts
10455
10515
  init_exception();
10516
+
10517
+ // src/storage/MemorySecretStorageProvider.ts
10518
+ init_SecretStorageException();
10519
+
10520
+ // src/libraries/secureMemory.ts
10521
+ var textEncoder = new TextEncoder();
10522
+ function zeroizeBytes(buffer) {
10523
+ if (buffer instanceof Uint8Array) {
10524
+ buffer.fill(0);
10525
+ } else if (Array.isArray(buffer)) {
10526
+ for (let i = 0; i < buffer.length; i++) {
10527
+ buffer[i] = 0;
10528
+ }
10529
+ }
10530
+ }
10531
+ async function withSecureBytes(bytes, fn) {
10532
+ try {
10533
+ return await fn(bytes);
10534
+ } finally {
10535
+ zeroizeBytes(bytes);
10536
+ }
10537
+ }
10538
+ async function withSecureString(secret, fn) {
10539
+ const bytes = textEncoder.encode(secret);
10540
+ try {
10541
+ return await fn(secret);
10542
+ } finally {
10543
+ zeroizeBytes(bytes);
10544
+ }
10545
+ }
10546
+ function constantTimeCompare(a, b) {
10547
+ const bytesA = typeof a === "string" ? textEncoder.encode(a) : a;
10548
+ const bytesB = typeof b === "string" ? textEncoder.encode(b) : b;
10549
+ let result = bytesA.length === bytesB.length ? 0 : 1;
10550
+ const len = Math.min(bytesA.length, bytesB.length);
10551
+ for (let i = 0; i < len; i++) {
10552
+ const byteA = bytesA[i] ?? 0;
10553
+ const byteB = bytesB[i] ?? 0;
10554
+ result |= byteA ^ byteB;
10555
+ }
10556
+ if (typeof a === "string") zeroizeBytes(bytesA);
10557
+ if (typeof b === "string") zeroizeBytes(bytesB);
10558
+ return result === 0;
10559
+ }
10560
+
10561
+ // src/storage/MemorySecretStorageProvider.ts
10562
+ var MemorySecretStorageProvider = class {
10563
+ providerType = "memory";
10564
+ secrets = /* @__PURE__ */ new Map();
10565
+ /**
10566
+ * Memory storage is not hardware backed
10567
+ */
10568
+ isHardwareBacked() {
10569
+ return false;
10570
+ }
10571
+ /**
10572
+ * Memory storage is always available
10573
+ */
10574
+ async isAvailable() {
10575
+ return true;
10576
+ }
10577
+ /**
10578
+ * Store a secret in memory
10579
+ */
10580
+ async storeSecret(bundleHash, secret, options) {
10581
+ if (!bundleHash) {
10582
+ throw new exports.SecretStorageException("Bundle hash cannot be empty");
10583
+ }
10584
+ if (!secret) {
10585
+ throw new exports.SecretStorageException("Secret cannot be empty");
10586
+ }
10587
+ const metadata = {
10588
+ bundleHash,
10589
+ label: options?.label,
10590
+ createdAt: Date.now(),
10591
+ hardwareBacked: false,
10592
+ providerType: this.providerType
10593
+ };
10594
+ this.secrets.set(bundleHash, { secret, metadata });
10595
+ }
10596
+ /**
10597
+ * Retrieve a secret from memory
10598
+ */
10599
+ async retrieveSecret(bundleHash) {
10600
+ const entry = this.secrets.get(bundleHash);
10601
+ return entry ? entry.secret : null;
10602
+ }
10603
+ /**
10604
+ * Delete a stored secret
10605
+ */
10606
+ async deleteSecret(bundleHash) {
10607
+ return this.secrets.delete(bundleHash);
10608
+ }
10609
+ /**
10610
+ * Check if a secret exists
10611
+ */
10612
+ async hasSecret(bundleHash) {
10613
+ return this.secrets.has(bundleHash);
10614
+ }
10615
+ /**
10616
+ * List all stored secret metadata
10617
+ */
10618
+ async listSecrets() {
10619
+ return Array.from(this.secrets.values()).map((entry) => ({ ...entry.metadata }));
10620
+ }
10621
+ /**
10622
+ * Execute callback with unwrapped secret and ensure cleanup
10623
+ */
10624
+ async withSecret(bundleHash, fn) {
10625
+ const entry = this.secrets.get(bundleHash);
10626
+ if (!entry) {
10627
+ throw exports.SecretStorageException.notFound(bundleHash);
10628
+ }
10629
+ return withSecureString(entry.secret, fn);
10630
+ }
10631
+ /**
10632
+ * Clear all secrets from memory
10633
+ */
10634
+ clear() {
10635
+ this.secrets.clear();
10636
+ }
10637
+ };
10638
+
10639
+ // src/KnishIOClient.ts
10456
10640
  var KnishIOClient = class {
10457
10641
  $__secret = "";
10458
10642
  $__bundle = "";
10643
+ $__secretStorage = null;
10459
10644
  $__cellSlug = null;
10460
10645
  $__encrypt = false;
10461
10646
  $__uris = [];
@@ -10515,6 +10700,9 @@ var KnishIOClient = class {
10515
10700
  logging,
10516
10701
  defaultRequestPolicy
10517
10702
  });
10703
+ if (config.secretStorage) {
10704
+ this.$__secretStorage = config.secretStorage;
10705
+ }
10518
10706
  }
10519
10707
  /**
10520
10708
  * Initializes a new Knish.IO client session
@@ -10616,6 +10804,7 @@ var KnishIOClient = class {
10616
10804
  reset() {
10617
10805
  this.$__secret = "";
10618
10806
  this.$__bundle = "";
10807
+ this.$__secretStorage = null;
10619
10808
  this.$__encrypt = false;
10620
10809
  this.$__cellSlug = null;
10621
10810
  this.$__authToken = null;
@@ -10677,7 +10866,7 @@ var KnishIOClient = class {
10677
10866
  * Returns whether a secret is stored for this session
10678
10867
  */
10679
10868
  hasSecret() {
10680
- return !!this.$__secret && this.$__secret.length > 0;
10869
+ return !!this.$__secret && this.$__secret.length > 0 || !!this.$__secretStorage && !!this.$__bundle && this.$__bundle.length > 0;
10681
10870
  }
10682
10871
  /**
10683
10872
  * Returns the stored secret
@@ -10688,6 +10877,33 @@ var KnishIOClient = class {
10688
10877
  }
10689
10878
  return this.$__secret;
10690
10879
  }
10880
+ /**
10881
+ * Sets the secret storage provider and optionally sets the bundle hash
10882
+ */
10883
+ setSecretStorage(storage, bundleHash) {
10884
+ this.$__secretStorage = storage;
10885
+ if (bundleHash) {
10886
+ this.$__bundle = bundleHash;
10887
+ }
10888
+ }
10889
+ /**
10890
+ * Returns current secret storage provider
10891
+ */
10892
+ getSecretStorage() {
10893
+ return this.$__secretStorage;
10894
+ }
10895
+ /**
10896
+ * Asynchronously retrieves the secret from storage or returns in-memory secret
10897
+ */
10898
+ async retrieveSecret(options) {
10899
+ if (this.$__secret && this.$__secret.length > 0) {
10900
+ return this.$__secret;
10901
+ }
10902
+ if (this.$__secretStorage && this.$__bundle && this.$__bundle.length > 0) {
10903
+ return await this.$__secretStorage.retrieveSecret(this.$__bundle, options);
10904
+ }
10905
+ return null;
10906
+ }
10691
10907
  /**
10692
10908
  * Returns whether a bundle hash is being stored for this session
10693
10909
  */
@@ -10726,6 +10942,13 @@ var KnishIOClient = class {
10726
10942
  remainderWallet = null
10727
10943
  } = {}) {
10728
10944
  this.log("info", "KnishIOClient::createMolecule() - Creating a new molecule...");
10945
+ if (!secret) {
10946
+ if (this.$__secret && this.$__secret.length > 0) {
10947
+ secret = this.getSecret();
10948
+ } else if (this.$__secretStorage && this.$__bundle && this.$__bundle.length > 0) {
10949
+ secret = await this.$__secretStorage.retrieveSecret(this.$__bundle);
10950
+ }
10951
+ }
10729
10952
  secret = secret || this.getSecret();
10730
10953
  bundle = bundle || this.getBundle();
10731
10954
  let continuIdPosition = null;
@@ -10755,6 +10978,7 @@ var KnishIOClient = class {
10755
10978
  }));
10756
10979
  return new Molecule({
10757
10980
  secret,
10981
+ bundle,
10758
10982
  sourceWallet,
10759
10983
  remainderWallet: this.getRemainderWallet(),
10760
10984
  cellSlug: this.getCellSlug(),
@@ -10801,8 +11025,9 @@ var KnishIOClient = class {
10801
11025
  async executeQuery(query, variables = null, context = {}) {
10802
11026
  if (this.$__authToken && this.$__authToken.isExpired() && !this.$__authInProcess) {
10803
11027
  this.log("info", "KnishIOClient::executeQuery() - Access token is expired. Getting new one...");
11028
+ const authSecret = this.$__secret || await this.retrieveSecret() || "";
10804
11029
  await this.requestAuthToken({
10805
- secret: this.$__secret,
11030
+ secret: authSecret,
10806
11031
  cellSlug: this.$__cellSlug,
10807
11032
  encrypt: this.$__encrypt
10808
11033
  });
@@ -10842,6 +11067,13 @@ var KnishIOClient = class {
10842
11067
  setSecret(secret) {
10843
11068
  this.$__secret = secret;
10844
11069
  this.$__bundle = generateBundleHash(secret);
11070
+ if (!this.$__secretStorage) {
11071
+ const memStorage = new MemorySecretStorageProvider();
11072
+ memStorage.storeSecret(this.$__bundle, secret);
11073
+ this.$__secretStorage = memStorage;
11074
+ } else {
11075
+ this.$__secretStorage.storeSecret(this.$__bundle, secret);
11076
+ }
10845
11077
  }
10846
11078
  /**
10847
11079
  * Sets the auth token for this session
@@ -11025,6 +11257,9 @@ var KnishIOClient = class {
11025
11257
  if (secret === null && seed) {
11026
11258
  secret = generateSecret(seed);
11027
11259
  }
11260
+ if (secret === null && this.$__secretStorage && this.$__bundle) {
11261
+ secret = await this.$__secretStorage.retrieveSecret(this.$__bundle);
11262
+ }
11028
11263
  if (cellSlug) {
11029
11264
  this.setCellSlug(cellSlug);
11030
11265
  }
@@ -11870,15 +12105,30 @@ var KnishIOClient = class {
11870
12105
  async createPolicy({
11871
12106
  metaType,
11872
12107
  metaId,
11873
- policy = null
12108
+ policy = {}
11874
12109
  }) {
11875
12110
  this.log("info", `KnishIOClient::createPolicy() - Creating policy for ${metaType}:${metaId}...`);
11876
- return this.createMeta({
12111
+ const molecule = await this.createMolecule({});
12112
+ molecule.addPolicyAtom({
11877
12113
  metaType,
11878
12114
  metaId,
11879
- meta: null,
11880
- policy
12115
+ meta: {},
12116
+ policy: policy || {}
12117
+ });
12118
+ molecule.addContinuIdAtom();
12119
+ molecule.sign({
12120
+ bundle: this.getBundle()
11881
12121
  });
12122
+ molecule.check();
12123
+ const query = await this.createMoleculeMutation({
12124
+ mutationClass: MutationProposeMolecule,
12125
+ molecule
12126
+ });
12127
+ const response = await this.executeQuery(query);
12128
+ if (!response) {
12129
+ throw new CodeException("Policy creation failed");
12130
+ }
12131
+ return response;
11882
12132
  }
11883
12133
  /**
11884
12134
  * Create an identifier
@@ -12162,7 +12412,297 @@ var KnishIOClient = class {
12162
12412
  // src/index.ts
12163
12413
  init_Response();
12164
12414
  init_exception();
12165
- var SDK_VERSION = "0.9.6";
12415
+
12416
+ // src/storage/WebCryptoSecretStorageProvider.ts
12417
+ init_SecretStorageException();
12418
+ var MemoryStorageBackend = class {
12419
+ store = /* @__PURE__ */ new Map();
12420
+ getItem(key) {
12421
+ return this.store.get(key) ?? null;
12422
+ }
12423
+ setItem(key, value) {
12424
+ this.store.set(key, value);
12425
+ }
12426
+ removeItem(key) {
12427
+ return this.store.delete(key);
12428
+ }
12429
+ keys() {
12430
+ return Array.from(this.store.keys());
12431
+ }
12432
+ };
12433
+ function uint8ArrayToBase64(bytes) {
12434
+ let binary = "";
12435
+ const len = bytes.byteLength;
12436
+ for (let i = 0; i < len; i++) {
12437
+ const byte = bytes[i];
12438
+ if (byte !== void 0) {
12439
+ binary += String.fromCharCode(byte);
12440
+ }
12441
+ }
12442
+ return btoa(binary);
12443
+ }
12444
+ function base64ToUint8Array(base64) {
12445
+ const binary = atob(base64);
12446
+ const len = binary.length;
12447
+ const bytes = new Uint8Array(len);
12448
+ for (let i = 0; i < len; i++) {
12449
+ bytes[i] = binary.charCodeAt(i);
12450
+ }
12451
+ return bytes;
12452
+ }
12453
+ var textEncoder2 = new TextEncoder();
12454
+ var textDecoder = new TextDecoder();
12455
+ var KEY_PREFIX = "knishio:secret:";
12456
+ var DEFAULT_ITERATIONS = 1e5;
12457
+ var WebCryptoSecretStorageProvider = class {
12458
+ providerType = "webcrypto-aes-gcm";
12459
+ backend;
12460
+ defaultPassphrase;
12461
+ hardwareBacked;
12462
+ constructor(options = {}) {
12463
+ this.backend = options.backend ?? new MemoryStorageBackend();
12464
+ this.defaultPassphrase = options.defaultPassphrase;
12465
+ this.hardwareBacked = options.hardwareBacked ?? false;
12466
+ }
12467
+ /**
12468
+ * Whether this provider is backed by hardware (e.g. WebAuthn PRF wrapping)
12469
+ */
12470
+ isHardwareBacked() {
12471
+ return this.hardwareBacked;
12472
+ }
12473
+ /**
12474
+ * Check if WebCrypto subtle API is available
12475
+ */
12476
+ async isAvailable() {
12477
+ return typeof globalThis.crypto !== "undefined" && typeof globalThis.crypto.subtle !== "undefined";
12478
+ }
12479
+ /**
12480
+ * Derive an AES-GCM CryptoKey from a passphrase and salt using PBKDF2
12481
+ */
12482
+ async deriveKey(passphrase, salt, iterations = DEFAULT_ITERATIONS) {
12483
+ if (!await this.isAvailable()) {
12484
+ throw exports.SecretStorageException.unavailable(this.providerType, "WebCrypto API is not available");
12485
+ }
12486
+ const passphraseBytes = textEncoder2.encode(passphrase);
12487
+ try {
12488
+ const baseKey = await globalThis.crypto.subtle.importKey(
12489
+ "raw",
12490
+ passphraseBytes,
12491
+ "PBKDF2",
12492
+ false,
12493
+ ["deriveKey"]
12494
+ );
12495
+ return await globalThis.crypto.subtle.deriveKey(
12496
+ {
12497
+ name: "PBKDF2",
12498
+ salt,
12499
+ iterations,
12500
+ hash: "SHA-256"
12501
+ },
12502
+ baseKey,
12503
+ { name: "AES-GCM", length: 256 },
12504
+ false,
12505
+ ["encrypt", "decrypt"]
12506
+ );
12507
+ } finally {
12508
+ zeroizeBytes(passphraseBytes);
12509
+ }
12510
+ }
12511
+ /**
12512
+ * Store and encrypt a master secret
12513
+ */
12514
+ async storeSecret(bundleHash, secret, options) {
12515
+ if (!bundleHash) {
12516
+ throw new exports.SecretStorageException("Bundle hash cannot be empty");
12517
+ }
12518
+ if (!secret) {
12519
+ throw new exports.SecretStorageException("Secret cannot be empty");
12520
+ }
12521
+ const passphrase = options?.passphrase ?? this.defaultPassphrase;
12522
+ if (!passphrase) {
12523
+ throw new exports.SecretStorageException("Passphrase required for envelope encryption");
12524
+ }
12525
+ const salt = new Uint8Array(16);
12526
+ const iv = new Uint8Array(12);
12527
+ globalThis.crypto.getRandomValues(salt);
12528
+ globalThis.crypto.getRandomValues(iv);
12529
+ const key = await this.deriveKey(passphrase, salt, DEFAULT_ITERATIONS);
12530
+ const secretBytes = textEncoder2.encode(secret);
12531
+ try {
12532
+ const encryptedBuffer = await globalThis.crypto.subtle.encrypt(
12533
+ {
12534
+ name: "AES-GCM",
12535
+ iv
12536
+ },
12537
+ key,
12538
+ secretBytes
12539
+ );
12540
+ const ciphertext = uint8ArrayToBase64(new Uint8Array(encryptedBuffer));
12541
+ const metadata = {
12542
+ bundleHash,
12543
+ label: options?.label,
12544
+ createdAt: Date.now(),
12545
+ hardwareBacked: this.hardwareBacked,
12546
+ providerType: this.providerType
12547
+ };
12548
+ const payload = {
12549
+ version: 1,
12550
+ ciphertext,
12551
+ iv: uint8ArrayToBase64(iv),
12552
+ salt: uint8ArrayToBase64(salt),
12553
+ algorithm: "AES-GCM",
12554
+ iterations: DEFAULT_ITERATIONS,
12555
+ metadata
12556
+ };
12557
+ await this.backend.setItem(`${KEY_PREFIX}${bundleHash}`, JSON.stringify(payload));
12558
+ } catch (err) {
12559
+ const msg = err instanceof Error ? err.message : String(err);
12560
+ throw new exports.SecretStorageException(`Encryption failed: ${msg}`);
12561
+ } finally {
12562
+ zeroizeBytes(secretBytes);
12563
+ }
12564
+ }
12565
+ /**
12566
+ * Retrieve and decrypt the master secret
12567
+ */
12568
+ async retrieveSecret(bundleHash, options) {
12569
+ const raw = await this.backend.getItem(`${KEY_PREFIX}${bundleHash}`);
12570
+ if (!raw) {
12571
+ return null;
12572
+ }
12573
+ let payload;
12574
+ try {
12575
+ payload = JSON.parse(raw);
12576
+ } catch {
12577
+ throw exports.SecretStorageException.decryptionFailed("Corrupted payload format");
12578
+ }
12579
+ const passphrase = options?.passphrase ?? this.defaultPassphrase;
12580
+ if (!passphrase) {
12581
+ throw new exports.SecretStorageException("Passphrase required for secret decryption");
12582
+ }
12583
+ const salt = base64ToUint8Array(payload.salt);
12584
+ const iv = base64ToUint8Array(payload.iv);
12585
+ const ciphertext = base64ToUint8Array(payload.ciphertext);
12586
+ try {
12587
+ const key = await this.deriveKey(passphrase, salt, payload.iterations ?? DEFAULT_ITERATIONS);
12588
+ const decryptedBuffer = await globalThis.crypto.subtle.decrypt(
12589
+ {
12590
+ name: "AES-GCM",
12591
+ iv
12592
+ },
12593
+ key,
12594
+ ciphertext
12595
+ );
12596
+ const decryptedBytes = new Uint8Array(decryptedBuffer);
12597
+ try {
12598
+ return textDecoder.decode(decryptedBytes);
12599
+ } finally {
12600
+ zeroizeBytes(decryptedBytes);
12601
+ }
12602
+ } catch (err) {
12603
+ const msg = err instanceof Error ? err.message : String(err);
12604
+ throw exports.SecretStorageException.decryptionFailed(msg);
12605
+ }
12606
+ }
12607
+ /**
12608
+ * Delete a stored secret
12609
+ */
12610
+ async deleteSecret(bundleHash) {
12611
+ const key = `${KEY_PREFIX}${bundleHash}`;
12612
+ const result = await this.backend.removeItem(key);
12613
+ return result !== false;
12614
+ }
12615
+ /**
12616
+ * Check if a secret exists
12617
+ */
12618
+ async hasSecret(bundleHash) {
12619
+ const raw = await this.backend.getItem(`${KEY_PREFIX}${bundleHash}`);
12620
+ return raw !== null;
12621
+ }
12622
+ /**
12623
+ * List all stored secret metadata
12624
+ */
12625
+ async listSecrets() {
12626
+ const keys = await this.backend.keys();
12627
+ const matchingKeys = keys.filter((k) => k.startsWith(KEY_PREFIX));
12628
+ const results = [];
12629
+ for (const key of matchingKeys) {
12630
+ const raw = await this.backend.getItem(key);
12631
+ if (raw) {
12632
+ try {
12633
+ const payload = JSON.parse(raw);
12634
+ if (payload.metadata) {
12635
+ results.push(payload.metadata);
12636
+ }
12637
+ } catch {
12638
+ }
12639
+ }
12640
+ }
12641
+ return results;
12642
+ }
12643
+ /**
12644
+ * Execute callback with unwrapped secret, zeroizing the decrypted buffer upon completion
12645
+ */
12646
+ async withSecret(bundleHash, fn, options) {
12647
+ const raw = await this.backend.getItem(`${KEY_PREFIX}${bundleHash}`);
12648
+ if (!raw) {
12649
+ throw exports.SecretStorageException.notFound(bundleHash);
12650
+ }
12651
+ let payload;
12652
+ try {
12653
+ payload = JSON.parse(raw);
12654
+ } catch {
12655
+ throw exports.SecretStorageException.decryptionFailed("Corrupted payload format");
12656
+ }
12657
+ const passphrase = options?.passphrase ?? this.defaultPassphrase;
12658
+ if (!passphrase) {
12659
+ throw new exports.SecretStorageException("Passphrase required for secret decryption");
12660
+ }
12661
+ const salt = base64ToUint8Array(payload.salt);
12662
+ const iv = base64ToUint8Array(payload.iv);
12663
+ const ciphertext = base64ToUint8Array(payload.ciphertext);
12664
+ try {
12665
+ const key = await this.deriveKey(passphrase, salt, payload.iterations ?? DEFAULT_ITERATIONS);
12666
+ const decryptedBuffer = await globalThis.crypto.subtle.decrypt(
12667
+ {
12668
+ name: "AES-GCM",
12669
+ iv
12670
+ },
12671
+ key,
12672
+ ciphertext
12673
+ );
12674
+ const decryptedBytes = new Uint8Array(decryptedBuffer);
12675
+ return await withSecureBytes(decryptedBytes, async (bytes) => {
12676
+ const secretString = textDecoder.decode(bytes);
12677
+ return await fn(secretString);
12678
+ });
12679
+ } catch (err) {
12680
+ if (err instanceof exports.SecretStorageException) {
12681
+ throw err;
12682
+ }
12683
+ const msg = err instanceof Error ? err.message : String(err);
12684
+ throw exports.SecretStorageException.decryptionFailed(msg);
12685
+ }
12686
+ }
12687
+ };
12688
+
12689
+ // src/storage/index.ts
12690
+ function createDefaultSecretStorage(options = {}) {
12691
+ if (options.type === "memory") {
12692
+ return new MemorySecretStorageProvider();
12693
+ }
12694
+ if (typeof globalThis.crypto !== "undefined" && typeof globalThis.crypto.subtle !== "undefined") {
12695
+ return new WebCryptoSecretStorageProvider({
12696
+ backend: options.backend,
12697
+ defaultPassphrase: options.defaultPassphrase,
12698
+ hardwareBacked: options.hardwareBacked
12699
+ });
12700
+ }
12701
+ return new MemorySecretStorageProvider();
12702
+ }
12703
+
12704
+ // src/index.ts
12705
+ var SDK_VERSION = "0.9.8";
12166
12706
  var SDK_NAME = "KnishIO-Client-TS";
12167
12707
  var COMPATIBLE_SERVER_VERSIONS = [4, 5];
12168
12708
  var SDK_INFO = {
@@ -12263,6 +12803,8 @@ exports.EXTENDED_COMPATIBILITY_TEST_VECTORS = EXTENDED_COMPATIBILITY_TEST_VECTOR
12263
12803
  exports.GraphQLClient = GraphQLClient;
12264
12804
  exports.KnishIO = KnishIO;
12265
12805
  exports.KnishIOClient = KnishIOClient;
12806
+ exports.MemorySecretStorageProvider = MemorySecretStorageProvider;
12807
+ exports.MemoryStorageBackend = MemoryStorageBackend;
12266
12808
  exports.Meta = Meta;
12267
12809
  exports.Molecule = Molecule;
12268
12810
  exports.Mutation = Mutation;
@@ -12308,6 +12850,7 @@ exports.SDK_NAME = SDK_NAME;
12308
12850
  exports.SDK_VERSION = SDK_VERSION;
12309
12851
  exports.TokenUnit = TokenUnit;
12310
12852
  exports.Wallet = Wallet;
12853
+ exports.WebCryptoSecretStorageProvider = WebCryptoSecretStorageProvider;
12311
12854
  exports.base64ToHex = base64ToHex;
12312
12855
  exports.bufferToHexString = bufferToHexString;
12313
12856
  exports.capitalize = capitalize;
@@ -12315,8 +12858,10 @@ exports.charsetBaseConvert = charsetBaseConvert;
12315
12858
  exports.chunkArray = chunkArray;
12316
12859
  exports.chunkSubstr = chunkSubstr;
12317
12860
  exports.configureSDK = configureSDK;
12861
+ exports.constantTimeCompare = constantTimeCompare;
12318
12862
  exports.convertToBase17 = convertToBase17;
12319
12863
  exports.createBundleHash = createBundleHash;
12864
+ exports.createDefaultSecretStorage = createDefaultSecretStorage;
12320
12865
  exports.createMolecularHash = createMolecularHash;
12321
12866
  exports.createPosition = createPosition;
12322
12867
  exports.createTokenSlug = createTokenSlug;
@@ -12358,5 +12903,8 @@ exports.validatePosition = validatePosition;
12358
12903
  exports.validateSecret = validateSecret;
12359
12904
  exports.validateWalletAddress = validateWalletAddress;
12360
12905
  exports.verifyOTSSignature = verifyOTSSignature;
12906
+ exports.withSecureBytes = withSecureBytes;
12907
+ exports.withSecureString = withSecureString;
12908
+ exports.zeroizeBytes = zeroizeBytes;
12361
12909
  //# sourceMappingURL=index.cjs.map
12362
12910
  //# sourceMappingURL=index.cjs.map