@wishknish/knishio-client-ts 0.9.5 → 0.9.7

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();
@@ -2178,11 +2228,9 @@ function verifyOTSSignature(otsFragments, molecularHash, signingAddress) {
2178
2228
  const base17Hash = convertToBase17(molecularHash);
2179
2229
  const enumerated = enumerateMolecularHash(base17Hash);
2180
2230
  const normalized = normalizeMolecularHash(enumerated);
2181
- let ots = otsFragments;
2231
+ const ots = otsFragments;
2182
2232
  if (ots.length !== 2048) {
2183
- if (ots.length !== 2048) {
2184
- return false;
2185
- }
2233
+ return false;
2186
2234
  }
2187
2235
  const otsChunks = [];
2188
2236
  for (let i = 0; i < ots.length; i += CRYPTO_CONSTANTS.KEY_FRAGMENT_SIZE) {
@@ -2685,6 +2733,123 @@ var Meta = class {
2685
2733
  });
2686
2734
  }
2687
2735
  };
2736
+
2737
+ // src/libraries/array.ts
2738
+ function deepCloning(o, h) {
2739
+ let i;
2740
+ let r;
2741
+ let x;
2742
+ const t = [Array, Date, Number, String, Boolean];
2743
+ const s = Object.prototype.toString;
2744
+ h = h || [];
2745
+ for (i = 0; i < h.length; i += 2) {
2746
+ if (o === h[i]) {
2747
+ return h[i + 1];
2748
+ }
2749
+ }
2750
+ if (!r && o && typeof o === "object") {
2751
+ r = {};
2752
+ for (i = 0; i < t.length; i++) {
2753
+ if (s.call(o) === s.call(x = new t[i](o))) {
2754
+ r = i ? x : [];
2755
+ }
2756
+ }
2757
+ h.push(o, r);
2758
+ for (i in o) {
2759
+ if (Object.prototype.hasOwnProperty.call(o, i)) {
2760
+ r[i] = deepCloning(o[i], h);
2761
+ }
2762
+ }
2763
+ }
2764
+ return r || o;
2765
+ }
2766
+ function chunkArray(arr, size) {
2767
+ const chunks = [];
2768
+ for (let i = 0; i < arr.length; i += size) {
2769
+ chunks.push(arr.slice(i, i + size));
2770
+ }
2771
+ return chunks;
2772
+ }
2773
+ function diff(...arrays) {
2774
+ return [].concat(...arrays.map((arr, i) => {
2775
+ const others = arrays.slice(0);
2776
+ others.splice(i, 1);
2777
+ const unique = [...new Set([].concat(...others))];
2778
+ return arr.filter((item) => !unique.includes(item));
2779
+ }));
2780
+ }
2781
+ function intersect(...arrays) {
2782
+ if (arrays.length === 0) return [];
2783
+ if (arrays.length === 1) return arrays[0];
2784
+ return arrays.reduce(
2785
+ (first, second) => first.filter((item) => second.includes(item))
2786
+ );
2787
+ }
2788
+
2789
+ // src/core/PolicyMeta.ts
2790
+ var PolicyMeta = class _PolicyMeta {
2791
+ policy;
2792
+ /**
2793
+ * Create new PolicyMeta instance
2794
+ * Matches JavaScript SDK constructor signature exactly
2795
+ */
2796
+ constructor(policy = {}, metaKeys = []) {
2797
+ this.policy = _PolicyMeta.normalizePolicy(policy);
2798
+ this.fillDefault(metaKeys);
2799
+ }
2800
+ /**
2801
+ * Normalize policy object structure
2802
+ * Matches JavaScript SDK normalizePolicy method exactly
2803
+ */
2804
+ static normalizePolicy(policy = {}) {
2805
+ const policyMeta = {};
2806
+ for (const [policyKey, value] of Object.entries(policy)) {
2807
+ if (value !== null && ["read", "write"].includes(policyKey)) {
2808
+ policyMeta[policyKey] = {};
2809
+ for (const [key, content] of Object.entries(value)) {
2810
+ policyMeta[policyKey][key] = content;
2811
+ }
2812
+ }
2813
+ }
2814
+ return policyMeta;
2815
+ }
2816
+ /**
2817
+ * Fill default policy values for metadata keys
2818
+ * Matches JavaScript SDK fillDefault method exactly
2819
+ */
2820
+ fillDefault(metaKeys = []) {
2821
+ const readPolicy = Array.from(this.policy).filter((item) => item.action === "read");
2822
+ const writePolicy = Array.from(this.policy).filter((item) => item.action === "write");
2823
+ for (const [type, value] of Object.entries({
2824
+ read: readPolicy,
2825
+ write: writePolicy
2826
+ })) {
2827
+ const policyKey = value.map((item) => item.key);
2828
+ if (!this.policy[type]) {
2829
+ this.policy[type] = {};
2830
+ }
2831
+ for (const key of diff(metaKeys, policyKey)) {
2832
+ if (!this.policy[type][key]) {
2833
+ this.policy[type][key] = type === "write" && !["characters", "pubkey"].includes(key) ? ["self"] : ["all"];
2834
+ }
2835
+ }
2836
+ }
2837
+ }
2838
+ /**
2839
+ * Get the policy object
2840
+ * Matches JavaScript SDK get method exactly
2841
+ */
2842
+ get() {
2843
+ return this.policy;
2844
+ }
2845
+ /**
2846
+ * Convert policy to JSON string
2847
+ * Matches JavaScript SDK toJson method exactly
2848
+ */
2849
+ toJson() {
2850
+ return JSON.stringify(this.get());
2851
+ }
2852
+ };
2688
2853
  var AtomMeta = class _AtomMeta {
2689
2854
  meta;
2690
2855
  /**
@@ -2780,8 +2945,9 @@ var AtomMeta = class _AtomMeta {
2780
2945
  * @return This instance for chaining
2781
2946
  */
2782
2947
  addPolicy(policy) {
2948
+ const policyMeta = new PolicyMeta(policy, Object.keys(this.meta));
2783
2949
  this.merge({
2784
- policy: JSON.stringify(policy)
2950
+ policy: policyMeta.toJson()
2785
2951
  });
2786
2952
  return this;
2787
2953
  }
@@ -3414,6 +3580,87 @@ function createMolecularHash(value) {
3414
3580
  }
3415
3581
  return value;
3416
3582
  }
3583
+
3584
+ // src/core/TokenUnit.ts
3585
+ var TokenUnit = class _TokenUnit {
3586
+ id;
3587
+ name;
3588
+ metas;
3589
+ /**
3590
+ * Create new TokenUnit instance
3591
+ * Matches JavaScript SDK constructor signature exactly
3592
+ */
3593
+ constructor(id, name, metas) {
3594
+ this.id = id;
3595
+ this.name = name;
3596
+ this.metas = metas || {};
3597
+ }
3598
+ /**
3599
+ * Create TokenUnit from GraphQL response data
3600
+ * Matches JavaScript SDK createFromGraphQL method exactly
3601
+ */
3602
+ static createFromGraphQL(data) {
3603
+ let metas = data.metas || {};
3604
+ if (Array.isArray(metas) && metas.length) {
3605
+ try {
3606
+ metas = JSON.parse(metas);
3607
+ if (!metas) {
3608
+ metas = {};
3609
+ }
3610
+ } catch (error) {
3611
+ metas = {};
3612
+ }
3613
+ }
3614
+ return new _TokenUnit(
3615
+ data.id,
3616
+ data.name,
3617
+ metas
3618
+ );
3619
+ }
3620
+ /**
3621
+ * Create TokenUnit from database array data
3622
+ * Matches JavaScript SDK createFromDB method exactly
3623
+ */
3624
+ static createFromDB(data) {
3625
+ return new _TokenUnit(
3626
+ data[0],
3627
+ data[1],
3628
+ data.length > 2 ? data[2] : {}
3629
+ );
3630
+ }
3631
+ /**
3632
+ * Get fragment zone from metadata
3633
+ * Matches JavaScript SDK getFragmentZone method exactly
3634
+ */
3635
+ getFragmentZone() {
3636
+ return this.metas.fragmentZone || null;
3637
+ }
3638
+ /**
3639
+ * Get fused token units from metadata
3640
+ * Matches JavaScript SDK getFusedTokenUnits method exactly
3641
+ */
3642
+ getFusedTokenUnits() {
3643
+ return this.metas.fusedTokenUnits || null;
3644
+ }
3645
+ /**
3646
+ * Convert to data array format
3647
+ * Matches JavaScript SDK toData method exactly
3648
+ */
3649
+ toData() {
3650
+ return [this.id, this.name, this.metas];
3651
+ }
3652
+ /**
3653
+ * Convert to GraphQL response format
3654
+ * Matches JavaScript SDK toGraphQLResponse method exactly
3655
+ */
3656
+ toGraphQLResponse() {
3657
+ return {
3658
+ id: this.id,
3659
+ name: this.name,
3660
+ metas: JSON.stringify(this.metas)
3661
+ };
3662
+ }
3663
+ };
3417
3664
  var Wallet = class _Wallet {
3418
3665
  token;
3419
3666
  balance;
@@ -3554,11 +3801,13 @@ var Wallet = class _Wallet {
3554
3801
  return typeof maybeBundleHash === "string" && isBundleHash(maybeBundleHash);
3555
3802
  }
3556
3803
  /**
3557
- * Get formatted token units from raw data
3558
- * Stub implementation for now
3804
+ * Map raw token-unit tuples to TokenUnit instances.
3805
+ * Matches JS SDK Wallet.getTokenUnits (Wallet.js:190-196). The serialised shape reaches hashed
3806
+ * atom meta via AtomMeta.setAtomWallet -> JSON.stringify(getTokenUnitsData()), so returning raw
3807
+ * tuples here would diverge from every other SDK.
3559
3808
  */
3560
3809
  static getTokenUnits(unitsData) {
3561
- return unitsData;
3810
+ return unitsData.map((unitData) => TokenUnit.createFromDB(unitData));
3562
3811
  }
3563
3812
  /**
3564
3813
  * Create a remainder wallet for transactions
@@ -3855,7 +4104,7 @@ zod.z.string().regex(base17HashRegex, "Molecular hash must be base17 format (0-9
3855
4104
  var TokenSlugSchema = zod.z.string().min(1, "Token slug cannot be empty").max(64, "Token slug cannot exceed 64 characters").transform((val) => val.toUpperCase()).brand();
3856
4105
  var MetaTypeSchema = zod.z.string().min(1, "Meta type cannot be empty").max(256, "Meta type cannot exceed 256 characters").brand();
3857
4106
  var MetaIdSchema = zod.z.string().min(1, "Meta ID cannot be empty").brand();
3858
- var BatchIdSchema = zod.z.string().min(1, "Batch ID cannot be empty").brand();
4107
+ var BatchIdSchema = zod.z.string().regex(/^[0-9a-fA-F]{64}$/, "Batch ID must be 64 hexadecimal characters").brand();
3859
4108
  var CellSlugSchema = zod.z.string().min(1, "Cell slug cannot be empty").max(64, "Cell slug cannot exceed 64 characters").brand();
3860
4109
  var AtomIsotopeSchema = zod.z.enum(["C", "V", "U", "T", "M", "I", "R", "B", "F"], {
3861
4110
  error: "Invalid atom isotope. Must be one of: C, V, U, T, M, I, R, B, F"
@@ -3916,7 +4165,8 @@ zod.z.object({
3916
4165
  socket: zod.z.unknown().optional(),
3917
4166
  serverSdkVersion: zod.z.number().int().min(1).optional(),
3918
4167
  logging: zod.z.boolean().optional(),
3919
- 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()
3920
4170
  }).strict();
3921
4171
  zod.z.object({
3922
4172
  token: zod.z.string().min(1, "Auth token cannot be empty"),
@@ -4179,58 +4429,6 @@ var RuleArgumentException = class extends exports.BaseException {
4179
4429
  };
4180
4430
  var RuleArgumentException_default = RuleArgumentException;
4181
4431
 
4182
- // src/libraries/array.ts
4183
- function deepCloning(o, h) {
4184
- let i;
4185
- let r;
4186
- let x;
4187
- const t = [Array, Date, Number, String, Boolean];
4188
- const s = Object.prototype.toString;
4189
- h = h || [];
4190
- for (i = 0; i < h.length; i += 2) {
4191
- if (o === h[i]) {
4192
- return h[i + 1];
4193
- }
4194
- }
4195
- if (!r && o && typeof o === "object") {
4196
- r = {};
4197
- for (i = 0; i < t.length; i++) {
4198
- if (s.call(o) === s.call(x = new t[i](o))) {
4199
- r = i ? x : [];
4200
- }
4201
- }
4202
- h.push(o, r);
4203
- for (i in o) {
4204
- if (Object.prototype.hasOwnProperty.call(o, i)) {
4205
- r[i] = deepCloning(o[i], h);
4206
- }
4207
- }
4208
- }
4209
- return r || o;
4210
- }
4211
- function chunkArray(arr, size) {
4212
- const chunks = [];
4213
- for (let i = 0; i < arr.length; i += size) {
4214
- chunks.push(arr.slice(i, i + size));
4215
- }
4216
- return chunks;
4217
- }
4218
- function diff(...arrays) {
4219
- return [].concat(...arrays.map((arr, i) => {
4220
- const others = arrays.slice(0);
4221
- others.splice(i, 1);
4222
- const unique = [...new Set([].concat(...others))];
4223
- return arr.filter((item) => !unique.includes(item));
4224
- }));
4225
- }
4226
- function intersect(...arrays) {
4227
- if (arrays.length === 0) return [];
4228
- if (arrays.length === 1) return arrays[0];
4229
- return arrays.reduce(
4230
- (first, second) => first.filter((item) => second.includes(item))
4231
- );
4232
- }
4233
-
4234
4432
  // src/instance/rules/Callback.ts
4235
4433
  init_exception();
4236
4434
  var CallbackParamsSchema = zod.z.object({
@@ -5561,6 +5759,51 @@ var Molecule = class _Molecule {
5561
5759
  }));
5562
5760
  return this;
5563
5761
  }
5762
+ /**
5763
+ * Replenishes non-finite token supplies.
5764
+ * Matches JS SDK Molecule.replenishToken (Molecule.js:521-566) exactly.
5765
+ *
5766
+ * Two orderings here are load-bearing for the molecular hash and must not be reordered:
5767
+ * the remainder balance is computed BEFORE the source balance is overwritten, and the
5768
+ * source V-atom is added BEFORE the remainder V-atom.
5769
+ */
5770
+ replenishToken({
5771
+ amount,
5772
+ units = []
5773
+ }) {
5774
+ if (amount < 0) {
5775
+ throw new NegativeAmountException("Molecule::replenishToken() - Amount to replenish must be positive!");
5776
+ }
5777
+ if (!this.sourceWallet || !this.remainderWallet) {
5778
+ throw new Error("Source and remainder wallets required for token replenishment");
5779
+ }
5780
+ if (units.length) {
5781
+ const formatted = Wallet.getTokenUnits(units);
5782
+ this.remainderWallet.tokenUnits = this.sourceWallet.tokenUnits;
5783
+ for (const unit of formatted) {
5784
+ this.remainderWallet.tokenUnits.push(unit);
5785
+ }
5786
+ this.remainderWallet.balance = String(this.remainderWallet.tokenUnits.length);
5787
+ this.sourceWallet.tokenUnits = formatted;
5788
+ this.sourceWallet.balance = String(this.sourceWallet.tokenUnits.length);
5789
+ } else {
5790
+ this.remainderWallet.balance = String(Number(this.sourceWallet.balance) + amount);
5791
+ this.sourceWallet.balance = String(amount);
5792
+ }
5793
+ this.addAtom(Atom.create({
5794
+ isotope: "V",
5795
+ wallet: this.sourceWallet,
5796
+ value: Number(this.sourceWallet.balance)
5797
+ }));
5798
+ this.addAtom(Atom.create({
5799
+ isotope: "V",
5800
+ wallet: this.remainderWallet,
5801
+ value: Number(this.remainderWallet.balance),
5802
+ metaType: "walletBundle",
5803
+ metaId: this.remainderWallet.bundle
5804
+ }));
5805
+ return this;
5806
+ }
5564
5807
  /**
5565
5808
  * Initialize authorization request
5566
5809
  * Creates U-isotope (authorization) atom for requesting auth token
@@ -6095,16 +6338,27 @@ var GraphQLClient = class {
6095
6338
  return core.createClient({
6096
6339
  url: serverUri,
6097
6340
  exchanges,
6098
- // PQ-transport Phase E: when encryption is on, route fetch through the CipherHash wrapper
6099
- // (encrypt the request body to the validator's ML-KEM pubkey, decrypt the response).
6100
- // Omitted → urql uses the global fetch (plaintext).
6101
- ...this.cipherLink ? { fetch: ((input, init) => this.cipherFetch(input, init)) } : {},
6341
+ // urql 5 had no default and always POSTed; urql 6 defaults to 'within-url-limit', which
6342
+ // URL-encodes short queries and sends NO body. That would silently disable the CipherHash
6343
+ // envelope below — cipherFetch's `typeof init.body === 'string'` guard fails on a GET, so
6344
+ // the query would leave as plaintext URL parameters with no error. Pin POST explicitly.
6345
+ preferGetMethod: false,
6346
+ // Always route through our own fetch. Two reasons: (1) PQ-transport Phase E — when
6347
+ // encryption is on, cipherFetch wraps the request body in the CipherHash envelope and
6348
+ // decrypts the response; (2) the 60s timeout. urql's makeFetchSource unconditionally
6349
+ // overwrites init.signal with its own AbortController, so a signal returned from
6350
+ // fetchOptions() is discarded and never fired. Combining it here is the only place it
6351
+ // survives.
6352
+ fetch: ((input, init) => {
6353
+ const timeoutSignal = AbortSignal.timeout(6e4);
6354
+ const signal = init?.signal && typeof AbortSignal.any === "function" ? AbortSignal.any([init.signal, timeoutSignal]) : init?.signal ?? timeoutSignal;
6355
+ const timedInit = { ...init, signal };
6356
+ return this.cipherLink ? this.cipherFetch(input, timedInit) : fetch(input, timedInit);
6357
+ }),
6102
6358
  fetchOptions: () => ({
6103
6359
  headers: {
6104
6360
  "X-Auth-Token": this.$__authToken
6105
- },
6106
- // Add 60 second timeout
6107
- signal: AbortSignal.timeout(6e4)
6361
+ }
6108
6362
  })
6109
6363
  });
6110
6364
  }
@@ -6617,12 +6871,12 @@ var TokenSlugSchema2 = createBrandedSchema(
6617
6871
  );
6618
6872
  var BatchIdSchema2 = createBrandedSchema(
6619
6873
  "BatchId",
6620
- // v3's `.uuid()` regex, inlined verbatim. Zod 4's `z.uuid()` additionally enforces the
6621
- // RFC 9562 version/variant nibbles and would reject batch IDs v3 accepted.
6622
- zod.z.string().regex(
6623
- /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/,
6624
- "Invalid batch ID format"
6625
- )
6874
+ // 64 hex characters. generateBatchId (src/libraries/crypto.ts) returns either
6875
+ // shake256(molecularHash + index, 256) — 256 bits, 64 hex chars — or randomString(64) over the
6876
+ // alphabet 'abcdef0123456789'. A UUID shape matches nothing this SDK can produce, so the
6877
+ // previous 8-4-4-4-12 regex rejected every real batch ID. Agrees with isBatchId in
6878
+ // src/types/guards.ts.
6879
+ zod.z.string().regex(/^[0-9a-fA-F]{64}$/, "Invalid batch ID format")
6626
6880
  );
6627
6881
  var CellSlugSchema2 = createBrandedSchema(
6628
6882
  "CellSlug",
@@ -6712,7 +6966,9 @@ var KnishIOClientConfigSchema2 = zod.z.object({
6712
6966
  // Optional default urql request policy for reads (server/sync clients pass
6713
6967
  // 'network-only'). Permitted by the strict schema so the constructor option
6714
6968
  // isn't rejected.
6715
- 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()
6716
6972
  }).strict();
6717
6973
  var EnvironmentConfigSchema = zod.z.object({
6718
6974
  NODE_ENV: zod.z.enum(["development", "production", "test"]).optional(),
@@ -7487,127 +7743,44 @@ var ConfigValidator = class _ConfigValidator {
7487
7743
  duration
7488
7744
  });
7489
7745
  if (this.performanceMetrics.length > 1e3) {
7490
- this.performanceMetrics = this.performanceMetrics.slice(-1e3);
7491
- }
7492
- }
7493
- /**
7494
- * Clean up validation cache
7495
- */
7496
- cleanupCache() {
7497
- if (this.validationCache.size > 500) {
7498
- const entries = Array.from(this.validationCache.entries());
7499
- const toKeep = entries.slice(-250);
7500
- this.validationCache.clear();
7501
- toKeep.forEach(([key, value]) => this.validationCache.set(key, value));
7502
- }
7503
- }
7504
- /**
7505
- * Get validation statistics
7506
- */
7507
- getValidationStats() {
7508
- const recent = this.performanceMetrics.slice(-100);
7509
- const avgTime = recent.reduce((sum, m) => sum + m.duration, 0) / recent.length || 0;
7510
- return {
7511
- totalValidations: this.performanceMetrics.length,
7512
- cacheSize: this.validationCache.size,
7513
- averageValidationTime: Math.round(avgTime * 100) / 100,
7514
- recentValidations: recent.length
7515
- };
7516
- }
7517
- /**
7518
- * Clear validation cache and metrics
7519
- */
7520
- clearCache() {
7521
- this.validationCache.clear();
7522
- this.performanceMetrics = [];
7523
- }
7524
- };
7525
-
7526
- // src/response/ResponseBalance.ts
7527
- init_Response();
7528
-
7529
- // src/core/TokenUnit.ts
7530
- var TokenUnit = class _TokenUnit {
7531
- id;
7532
- name;
7533
- metas;
7534
- /**
7535
- * Create new TokenUnit instance
7536
- * Matches JavaScript SDK constructor signature exactly
7537
- */
7538
- constructor(id, name, metas) {
7539
- this.id = id;
7540
- this.name = name;
7541
- this.metas = metas || {};
7542
- }
7543
- /**
7544
- * Create TokenUnit from GraphQL response data
7545
- * Matches JavaScript SDK createFromGraphQL method exactly
7546
- */
7547
- static createFromGraphQL(data) {
7548
- let metas = data.metas || {};
7549
- if (Array.isArray(metas) && metas.length) {
7550
- try {
7551
- metas = JSON.parse(metas);
7552
- if (!metas) {
7553
- metas = {};
7554
- }
7555
- } catch (error) {
7556
- metas = {};
7557
- }
7558
- }
7559
- return new _TokenUnit(
7560
- data.id,
7561
- data.name,
7562
- metas
7563
- );
7564
- }
7565
- /**
7566
- * Create TokenUnit from database array data
7567
- * Matches JavaScript SDK createFromDB method exactly
7568
- */
7569
- static createFromDB(data) {
7570
- return new _TokenUnit(
7571
- data[0],
7572
- data[1],
7573
- data.length > 2 ? data[2] : {}
7574
- );
7575
- }
7576
- /**
7577
- * Get fragment zone from metadata
7578
- * Matches JavaScript SDK getFragmentZone method exactly
7579
- */
7580
- getFragmentZone() {
7581
- return this.metas.fragmentZone || null;
7582
- }
7583
- /**
7584
- * Get fused token units from metadata
7585
- * Matches JavaScript SDK getFusedTokenUnits method exactly
7586
- */
7587
- getFusedTokenUnits() {
7588
- return this.metas.fusedTokenUnits || null;
7746
+ this.performanceMetrics = this.performanceMetrics.slice(-1e3);
7747
+ }
7589
7748
  }
7590
7749
  /**
7591
- * Convert to data array format
7592
- * Matches JavaScript SDK toData method exactly
7750
+ * Clean up validation cache
7593
7751
  */
7594
- toData() {
7595
- return [this.id, this.name, this.metas];
7752
+ cleanupCache() {
7753
+ if (this.validationCache.size > 500) {
7754
+ const entries = Array.from(this.validationCache.entries());
7755
+ const toKeep = entries.slice(-250);
7756
+ this.validationCache.clear();
7757
+ toKeep.forEach(([key, value]) => this.validationCache.set(key, value));
7758
+ }
7596
7759
  }
7597
7760
  /**
7598
- * Convert to GraphQL response format
7599
- * Matches JavaScript SDK toGraphQLResponse method exactly
7761
+ * Get validation statistics
7600
7762
  */
7601
- toGraphQLResponse() {
7763
+ getValidationStats() {
7764
+ const recent = this.performanceMetrics.slice(-100);
7765
+ const avgTime = recent.reduce((sum, m) => sum + m.duration, 0) / recent.length || 0;
7602
7766
  return {
7603
- id: this.id,
7604
- name: this.name,
7605
- metas: JSON.stringify(this.metas)
7767
+ totalValidations: this.performanceMetrics.length,
7768
+ cacheSize: this.validationCache.size,
7769
+ averageValidationTime: Math.round(avgTime * 100) / 100,
7770
+ recentValidations: recent.length
7606
7771
  };
7607
7772
  }
7773
+ /**
7774
+ * Clear validation cache and metrics
7775
+ */
7776
+ clearCache() {
7777
+ this.validationCache.clear();
7778
+ this.performanceMetrics = [];
7779
+ }
7608
7780
  };
7609
7781
 
7610
7782
  // src/response/ResponseBalance.ts
7783
+ init_Response();
7611
7784
  var ResponseBalance = class _ResponseBalance extends exports.Response {
7612
7785
  /**
7613
7786
  * Class constructor
@@ -10116,6 +10289,12 @@ var MutationAppendRequest = class extends MutationProposeMolecule {
10116
10289
  }
10117
10290
  };
10118
10291
 
10292
+ // src/mutation/MutationReplenishToken.ts
10293
+ var MutationReplenishToken = class extends MutationProposeMolecule {
10294
+ fillMolecule() {
10295
+ }
10296
+ };
10297
+
10119
10298
  // src/subscribe/Subscribe.ts
10120
10299
  init_exception();
10121
10300
  var Subscribe = class {
@@ -10327,9 +10506,134 @@ var ActiveSessionSubscribe = class extends Subscribe {
10327
10506
 
10328
10507
  // src/KnishIOClient.ts
10329
10508
  init_exception();
10509
+
10510
+ // src/storage/MemorySecretStorageProvider.ts
10511
+ init_SecretStorageException();
10512
+
10513
+ // src/libraries/secureMemory.ts
10514
+ var textEncoder = new TextEncoder();
10515
+ function zeroizeBytes(buffer) {
10516
+ if (buffer instanceof Uint8Array) {
10517
+ buffer.fill(0);
10518
+ } else if (Array.isArray(buffer)) {
10519
+ for (let i = 0; i < buffer.length; i++) {
10520
+ buffer[i] = 0;
10521
+ }
10522
+ }
10523
+ }
10524
+ async function withSecureBytes(bytes, fn) {
10525
+ try {
10526
+ return await fn(bytes);
10527
+ } finally {
10528
+ zeroizeBytes(bytes);
10529
+ }
10530
+ }
10531
+ async function withSecureString(secret, fn) {
10532
+ const bytes = textEncoder.encode(secret);
10533
+ try {
10534
+ return await fn(secret);
10535
+ } finally {
10536
+ zeroizeBytes(bytes);
10537
+ }
10538
+ }
10539
+ function constantTimeCompare(a, b) {
10540
+ const bytesA = typeof a === "string" ? textEncoder.encode(a) : a;
10541
+ const bytesB = typeof b === "string" ? textEncoder.encode(b) : b;
10542
+ let result = bytesA.length === bytesB.length ? 0 : 1;
10543
+ const len = Math.min(bytesA.length, bytesB.length);
10544
+ for (let i = 0; i < len; i++) {
10545
+ const byteA = bytesA[i] ?? 0;
10546
+ const byteB = bytesB[i] ?? 0;
10547
+ result |= byteA ^ byteB;
10548
+ }
10549
+ if (typeof a === "string") zeroizeBytes(bytesA);
10550
+ if (typeof b === "string") zeroizeBytes(bytesB);
10551
+ return result === 0;
10552
+ }
10553
+
10554
+ // src/storage/MemorySecretStorageProvider.ts
10555
+ var MemorySecretStorageProvider = class {
10556
+ providerType = "memory";
10557
+ secrets = /* @__PURE__ */ new Map();
10558
+ /**
10559
+ * Memory storage is not hardware backed
10560
+ */
10561
+ isHardwareBacked() {
10562
+ return false;
10563
+ }
10564
+ /**
10565
+ * Memory storage is always available
10566
+ */
10567
+ async isAvailable() {
10568
+ return true;
10569
+ }
10570
+ /**
10571
+ * Store a secret in memory
10572
+ */
10573
+ async storeSecret(bundleHash, secret, options) {
10574
+ if (!bundleHash) {
10575
+ throw new exports.SecretStorageException("Bundle hash cannot be empty");
10576
+ }
10577
+ if (!secret) {
10578
+ throw new exports.SecretStorageException("Secret cannot be empty");
10579
+ }
10580
+ const metadata = {
10581
+ bundleHash,
10582
+ label: options?.label,
10583
+ createdAt: Date.now(),
10584
+ hardwareBacked: false,
10585
+ providerType: this.providerType
10586
+ };
10587
+ this.secrets.set(bundleHash, { secret, metadata });
10588
+ }
10589
+ /**
10590
+ * Retrieve a secret from memory
10591
+ */
10592
+ async retrieveSecret(bundleHash) {
10593
+ const entry = this.secrets.get(bundleHash);
10594
+ return entry ? entry.secret : null;
10595
+ }
10596
+ /**
10597
+ * Delete a stored secret
10598
+ */
10599
+ async deleteSecret(bundleHash) {
10600
+ return this.secrets.delete(bundleHash);
10601
+ }
10602
+ /**
10603
+ * Check if a secret exists
10604
+ */
10605
+ async hasSecret(bundleHash) {
10606
+ return this.secrets.has(bundleHash);
10607
+ }
10608
+ /**
10609
+ * List all stored secret metadata
10610
+ */
10611
+ async listSecrets() {
10612
+ return Array.from(this.secrets.values()).map((entry) => ({ ...entry.metadata }));
10613
+ }
10614
+ /**
10615
+ * Execute callback with unwrapped secret and ensure cleanup
10616
+ */
10617
+ async withSecret(bundleHash, fn) {
10618
+ const entry = this.secrets.get(bundleHash);
10619
+ if (!entry) {
10620
+ throw exports.SecretStorageException.notFound(bundleHash);
10621
+ }
10622
+ return withSecureString(entry.secret, fn);
10623
+ }
10624
+ /**
10625
+ * Clear all secrets from memory
10626
+ */
10627
+ clear() {
10628
+ this.secrets.clear();
10629
+ }
10630
+ };
10631
+
10632
+ // src/KnishIOClient.ts
10330
10633
  var KnishIOClient = class {
10331
10634
  $__secret = "";
10332
10635
  $__bundle = "";
10636
+ $__secretStorage = null;
10333
10637
  $__cellSlug = null;
10334
10638
  $__encrypt = false;
10335
10639
  $__uris = [];
@@ -10389,6 +10693,9 @@ var KnishIOClient = class {
10389
10693
  logging,
10390
10694
  defaultRequestPolicy
10391
10695
  });
10696
+ if (config.secretStorage) {
10697
+ this.$__secretStorage = config.secretStorage;
10698
+ }
10392
10699
  }
10393
10700
  /**
10394
10701
  * Initializes a new Knish.IO client session
@@ -10490,6 +10797,7 @@ var KnishIOClient = class {
10490
10797
  reset() {
10491
10798
  this.$__secret = "";
10492
10799
  this.$__bundle = "";
10800
+ this.$__secretStorage = null;
10493
10801
  this.$__encrypt = false;
10494
10802
  this.$__cellSlug = null;
10495
10803
  this.$__authToken = null;
@@ -10551,7 +10859,7 @@ var KnishIOClient = class {
10551
10859
  * Returns whether a secret is stored for this session
10552
10860
  */
10553
10861
  hasSecret() {
10554
- return !!this.$__secret && this.$__secret.length > 0;
10862
+ return !!this.$__secret && this.$__secret.length > 0 || !!this.$__secretStorage && !!this.$__bundle && this.$__bundle.length > 0;
10555
10863
  }
10556
10864
  /**
10557
10865
  * Returns the stored secret
@@ -10562,6 +10870,33 @@ var KnishIOClient = class {
10562
10870
  }
10563
10871
  return this.$__secret;
10564
10872
  }
10873
+ /**
10874
+ * Sets the secret storage provider and optionally sets the bundle hash
10875
+ */
10876
+ setSecretStorage(storage, bundleHash) {
10877
+ this.$__secretStorage = storage;
10878
+ if (bundleHash) {
10879
+ this.$__bundle = bundleHash;
10880
+ }
10881
+ }
10882
+ /**
10883
+ * Returns current secret storage provider
10884
+ */
10885
+ getSecretStorage() {
10886
+ return this.$__secretStorage;
10887
+ }
10888
+ /**
10889
+ * Asynchronously retrieves the secret from storage or returns in-memory secret
10890
+ */
10891
+ async retrieveSecret(options) {
10892
+ if (this.$__secret && this.$__secret.length > 0) {
10893
+ return this.$__secret;
10894
+ }
10895
+ if (this.$__secretStorage && this.$__bundle && this.$__bundle.length > 0) {
10896
+ return await this.$__secretStorage.retrieveSecret(this.$__bundle, options);
10897
+ }
10898
+ return null;
10899
+ }
10565
10900
  /**
10566
10901
  * Returns whether a bundle hash is being stored for this session
10567
10902
  */
@@ -10600,6 +10935,13 @@ var KnishIOClient = class {
10600
10935
  remainderWallet = null
10601
10936
  } = {}) {
10602
10937
  this.log("info", "KnishIOClient::createMolecule() - Creating a new molecule...");
10938
+ if (!secret) {
10939
+ if (this.$__secret && this.$__secret.length > 0) {
10940
+ secret = this.getSecret();
10941
+ } else if (this.$__secretStorage && this.$__bundle && this.$__bundle.length > 0) {
10942
+ secret = await this.$__secretStorage.retrieveSecret(this.$__bundle);
10943
+ }
10944
+ }
10603
10945
  secret = secret || this.getSecret();
10604
10946
  bundle = bundle || this.getBundle();
10605
10947
  let continuIdPosition = null;
@@ -10629,6 +10971,7 @@ var KnishIOClient = class {
10629
10971
  }));
10630
10972
  return new Molecule({
10631
10973
  secret,
10974
+ bundle,
10632
10975
  sourceWallet,
10633
10976
  remainderWallet: this.getRemainderWallet(),
10634
10977
  cellSlug: this.getCellSlug(),
@@ -10675,8 +11018,9 @@ var KnishIOClient = class {
10675
11018
  async executeQuery(query, variables = null, context = {}) {
10676
11019
  if (this.$__authToken && this.$__authToken.isExpired() && !this.$__authInProcess) {
10677
11020
  this.log("info", "KnishIOClient::executeQuery() - Access token is expired. Getting new one...");
11021
+ const authSecret = this.$__secret || await this.retrieveSecret() || "";
10678
11022
  await this.requestAuthToken({
10679
- secret: this.$__secret,
11023
+ secret: authSecret,
10680
11024
  cellSlug: this.$__cellSlug,
10681
11025
  encrypt: this.$__encrypt
10682
11026
  });
@@ -10716,6 +11060,13 @@ var KnishIOClient = class {
10716
11060
  setSecret(secret) {
10717
11061
  this.$__secret = secret;
10718
11062
  this.$__bundle = generateBundleHash(secret);
11063
+ if (!this.$__secretStorage) {
11064
+ const memStorage = new MemorySecretStorageProvider();
11065
+ memStorage.storeSecret(this.$__bundle, secret);
11066
+ this.$__secretStorage = memStorage;
11067
+ } else {
11068
+ this.$__secretStorage.storeSecret(this.$__bundle, secret);
11069
+ }
10719
11070
  }
10720
11071
  /**
10721
11072
  * Sets the auth token for this session
@@ -10899,6 +11250,9 @@ var KnishIOClient = class {
10899
11250
  if (secret === null && seed) {
10900
11251
  secret = generateSecret(seed);
10901
11252
  }
11253
+ if (secret === null && this.$__secretStorage && this.$__bundle) {
11254
+ secret = await this.$__secretStorage.retrieveSecret(this.$__bundle);
11255
+ }
10902
11256
  if (cellSlug) {
10903
11257
  this.setCellSlug(cellSlug);
10904
11258
  }
@@ -11549,20 +11903,42 @@ var KnishIOClient = class {
11549
11903
  return response;
11550
11904
  }
11551
11905
  /**
11552
- * Replenish tokens
11906
+ * Replenish a non-finite token supply.
11907
+ * Matches JS SDK KnishIOClient.replenishToken (KnishIOClient.js:2195-2231).
11553
11908
  */
11554
11909
  async replenishToken({
11555
11910
  token,
11556
11911
  amount = null,
11557
11912
  units = null,
11558
- sourceWallet: _sourceWallet = null
11913
+ sourceWallet = null
11559
11914
  }) {
11560
11915
  this.log("info", `KnishIOClient::replenishToken() - Replenishing ${amount || "units"} of ${token}...`);
11561
- return this.requestTokens({
11562
- token,
11563
- amount,
11564
- units
11916
+ if (!sourceWallet) {
11917
+ sourceWallet = (await this.queryBalance({ token }))?.payload();
11918
+ }
11919
+ if (!sourceWallet) {
11920
+ throw new exports.TransferBalanceException("Source wallet is missing or invalid.");
11921
+ }
11922
+ const remainderWallet = sourceWallet.createRemainder(this.getSecret());
11923
+ const molecule = await this.createMolecule({
11924
+ sourceWallet,
11925
+ remainderWallet
11926
+ });
11927
+ molecule.replenishToken({
11928
+ amount: Number(amount ?? 0),
11929
+ units: units ?? []
11565
11930
  });
11931
+ molecule.sign({ bundle: this.getBundle() });
11932
+ molecule.check();
11933
+ const mutation = await this.createMoleculeMutation({
11934
+ mutationClass: MutationReplenishToken,
11935
+ molecule
11936
+ });
11937
+ const response = await this.executeQuery(mutation);
11938
+ if (!response) {
11939
+ throw new CodeException("Token replenishment failed");
11940
+ }
11941
+ return response;
11566
11942
  }
11567
11943
  /**
11568
11944
  * Fuse token units
@@ -12013,75 +12389,298 @@ var KnishIOClient = class {
12013
12389
 
12014
12390
  // src/index.ts
12015
12391
  init_Response();
12392
+ init_exception();
12016
12393
 
12017
- // src/core/PolicyMeta.ts
12018
- var PolicyMeta = class _PolicyMeta {
12019
- policy;
12394
+ // src/storage/WebCryptoSecretStorageProvider.ts
12395
+ init_SecretStorageException();
12396
+ var MemoryStorageBackend = class {
12397
+ store = /* @__PURE__ */ new Map();
12398
+ getItem(key) {
12399
+ return this.store.get(key) ?? null;
12400
+ }
12401
+ setItem(key, value) {
12402
+ this.store.set(key, value);
12403
+ }
12404
+ removeItem(key) {
12405
+ return this.store.delete(key);
12406
+ }
12407
+ keys() {
12408
+ return Array.from(this.store.keys());
12409
+ }
12410
+ };
12411
+ function uint8ArrayToBase64(bytes) {
12412
+ let binary = "";
12413
+ const len = bytes.byteLength;
12414
+ for (let i = 0; i < len; i++) {
12415
+ const byte = bytes[i];
12416
+ if (byte !== void 0) {
12417
+ binary += String.fromCharCode(byte);
12418
+ }
12419
+ }
12420
+ return btoa(binary);
12421
+ }
12422
+ function base64ToUint8Array(base64) {
12423
+ const binary = atob(base64);
12424
+ const len = binary.length;
12425
+ const bytes = new Uint8Array(len);
12426
+ for (let i = 0; i < len; i++) {
12427
+ bytes[i] = binary.charCodeAt(i);
12428
+ }
12429
+ return bytes;
12430
+ }
12431
+ var textEncoder2 = new TextEncoder();
12432
+ var textDecoder = new TextDecoder();
12433
+ var KEY_PREFIX = "knishio:secret:";
12434
+ var DEFAULT_ITERATIONS = 1e5;
12435
+ var WebCryptoSecretStorageProvider = class {
12436
+ providerType = "webcrypto-aes-gcm";
12437
+ backend;
12438
+ defaultPassphrase;
12439
+ hardwareBacked;
12440
+ constructor(options = {}) {
12441
+ this.backend = options.backend ?? new MemoryStorageBackend();
12442
+ this.defaultPassphrase = options.defaultPassphrase;
12443
+ this.hardwareBacked = options.hardwareBacked ?? false;
12444
+ }
12020
12445
  /**
12021
- * Create new PolicyMeta instance
12022
- * Matches JavaScript SDK constructor signature exactly
12446
+ * Whether this provider is backed by hardware (e.g. WebAuthn PRF wrapping)
12023
12447
  */
12024
- constructor(policy = {}, metaKeys = []) {
12025
- this.policy = _PolicyMeta.normalizePolicy(policy);
12026
- this.fillDefault(metaKeys);
12448
+ isHardwareBacked() {
12449
+ return this.hardwareBacked;
12027
12450
  }
12028
12451
  /**
12029
- * Normalize policy object structure
12030
- * Matches JavaScript SDK normalizePolicy method exactly
12452
+ * Check if WebCrypto subtle API is available
12031
12453
  */
12032
- static normalizePolicy(policy = {}) {
12033
- const policyMeta = {};
12034
- for (const [policyKey, value] of Object.entries(policy)) {
12035
- if (value !== null && ["read", "write"].includes(policyKey)) {
12036
- policyMeta[policyKey] = {};
12037
- for (const [key, content] of Object.entries(value)) {
12038
- policyMeta[policyKey][key] = content;
12039
- }
12040
- }
12454
+ async isAvailable() {
12455
+ return typeof globalThis.crypto !== "undefined" && typeof globalThis.crypto.subtle !== "undefined";
12456
+ }
12457
+ /**
12458
+ * Derive an AES-GCM CryptoKey from a passphrase and salt using PBKDF2
12459
+ */
12460
+ async deriveKey(passphrase, salt, iterations = DEFAULT_ITERATIONS) {
12461
+ if (!await this.isAvailable()) {
12462
+ throw exports.SecretStorageException.unavailable(this.providerType, "WebCrypto API is not available");
12463
+ }
12464
+ const passphraseBytes = textEncoder2.encode(passphrase);
12465
+ try {
12466
+ const baseKey = await globalThis.crypto.subtle.importKey(
12467
+ "raw",
12468
+ passphraseBytes,
12469
+ "PBKDF2",
12470
+ false,
12471
+ ["deriveKey"]
12472
+ );
12473
+ return await globalThis.crypto.subtle.deriveKey(
12474
+ {
12475
+ name: "PBKDF2",
12476
+ salt,
12477
+ iterations,
12478
+ hash: "SHA-256"
12479
+ },
12480
+ baseKey,
12481
+ { name: "AES-GCM", length: 256 },
12482
+ false,
12483
+ ["encrypt", "decrypt"]
12484
+ );
12485
+ } finally {
12486
+ zeroizeBytes(passphraseBytes);
12041
12487
  }
12042
- return policyMeta;
12043
12488
  }
12044
12489
  /**
12045
- * Fill default policy values for metadata keys
12046
- * Matches JavaScript SDK fillDefault method exactly
12490
+ * Store and encrypt a master secret
12047
12491
  */
12048
- fillDefault(metaKeys = []) {
12049
- const readPolicy = Array.from(this.policy).filter((item) => item.action === "read");
12050
- const writePolicy = Array.from(this.policy).filter((item) => item.action === "write");
12051
- for (const [type, value] of Object.entries({
12052
- read: readPolicy,
12053
- write: writePolicy
12054
- })) {
12055
- const policyKey = value.map((item) => item.key);
12056
- if (!this.policy[type]) {
12057
- this.policy[type] = {};
12058
- }
12059
- for (const key of diff(metaKeys, policyKey)) {
12060
- if (!this.policy[type][key]) {
12061
- this.policy[type][key] = type === "write" && !["characters", "pubkey"].includes(key) ? ["self"] : ["all"];
12062
- }
12492
+ async storeSecret(bundleHash, secret, options) {
12493
+ if (!bundleHash) {
12494
+ throw new exports.SecretStorageException("Bundle hash cannot be empty");
12495
+ }
12496
+ if (!secret) {
12497
+ throw new exports.SecretStorageException("Secret cannot be empty");
12498
+ }
12499
+ const passphrase = options?.passphrase ?? this.defaultPassphrase;
12500
+ if (!passphrase) {
12501
+ throw new exports.SecretStorageException("Passphrase required for envelope encryption");
12502
+ }
12503
+ const salt = new Uint8Array(16);
12504
+ const iv = new Uint8Array(12);
12505
+ globalThis.crypto.getRandomValues(salt);
12506
+ globalThis.crypto.getRandomValues(iv);
12507
+ const key = await this.deriveKey(passphrase, salt, DEFAULT_ITERATIONS);
12508
+ const secretBytes = textEncoder2.encode(secret);
12509
+ try {
12510
+ const encryptedBuffer = await globalThis.crypto.subtle.encrypt(
12511
+ {
12512
+ name: "AES-GCM",
12513
+ iv
12514
+ },
12515
+ key,
12516
+ secretBytes
12517
+ );
12518
+ const ciphertext = uint8ArrayToBase64(new Uint8Array(encryptedBuffer));
12519
+ const metadata = {
12520
+ bundleHash,
12521
+ label: options?.label,
12522
+ createdAt: Date.now(),
12523
+ hardwareBacked: this.hardwareBacked,
12524
+ providerType: this.providerType
12525
+ };
12526
+ const payload = {
12527
+ version: 1,
12528
+ ciphertext,
12529
+ iv: uint8ArrayToBase64(iv),
12530
+ salt: uint8ArrayToBase64(salt),
12531
+ algorithm: "AES-GCM",
12532
+ iterations: DEFAULT_ITERATIONS,
12533
+ metadata
12534
+ };
12535
+ await this.backend.setItem(`${KEY_PREFIX}${bundleHash}`, JSON.stringify(payload));
12536
+ } catch (err) {
12537
+ const msg = err instanceof Error ? err.message : String(err);
12538
+ throw new exports.SecretStorageException(`Encryption failed: ${msg}`);
12539
+ } finally {
12540
+ zeroizeBytes(secretBytes);
12541
+ }
12542
+ }
12543
+ /**
12544
+ * Retrieve and decrypt the master secret
12545
+ */
12546
+ async retrieveSecret(bundleHash, options) {
12547
+ const raw = await this.backend.getItem(`${KEY_PREFIX}${bundleHash}`);
12548
+ if (!raw) {
12549
+ return null;
12550
+ }
12551
+ let payload;
12552
+ try {
12553
+ payload = JSON.parse(raw);
12554
+ } catch {
12555
+ throw exports.SecretStorageException.decryptionFailed("Corrupted payload format");
12556
+ }
12557
+ const passphrase = options?.passphrase ?? this.defaultPassphrase;
12558
+ if (!passphrase) {
12559
+ throw new exports.SecretStorageException("Passphrase required for secret decryption");
12560
+ }
12561
+ const salt = base64ToUint8Array(payload.salt);
12562
+ const iv = base64ToUint8Array(payload.iv);
12563
+ const ciphertext = base64ToUint8Array(payload.ciphertext);
12564
+ try {
12565
+ const key = await this.deriveKey(passphrase, salt, payload.iterations ?? DEFAULT_ITERATIONS);
12566
+ const decryptedBuffer = await globalThis.crypto.subtle.decrypt(
12567
+ {
12568
+ name: "AES-GCM",
12569
+ iv
12570
+ },
12571
+ key,
12572
+ ciphertext
12573
+ );
12574
+ const decryptedBytes = new Uint8Array(decryptedBuffer);
12575
+ try {
12576
+ return textDecoder.decode(decryptedBytes);
12577
+ } finally {
12578
+ zeroizeBytes(decryptedBytes);
12063
12579
  }
12580
+ } catch (err) {
12581
+ const msg = err instanceof Error ? err.message : String(err);
12582
+ throw exports.SecretStorageException.decryptionFailed(msg);
12064
12583
  }
12065
12584
  }
12066
12585
  /**
12067
- * Get the policy object
12068
- * Matches JavaScript SDK get method exactly
12586
+ * Delete a stored secret
12069
12587
  */
12070
- get() {
12071
- return this.policy;
12588
+ async deleteSecret(bundleHash) {
12589
+ const key = `${KEY_PREFIX}${bundleHash}`;
12590
+ const result = await this.backend.removeItem(key);
12591
+ return result !== false;
12072
12592
  }
12073
12593
  /**
12074
- * Convert policy to JSON string
12075
- * Matches JavaScript SDK toJson method exactly
12594
+ * Check if a secret exists
12076
12595
  */
12077
- toJson() {
12078
- return JSON.stringify(this.get());
12596
+ async hasSecret(bundleHash) {
12597
+ const raw = await this.backend.getItem(`${KEY_PREFIX}${bundleHash}`);
12598
+ return raw !== null;
12599
+ }
12600
+ /**
12601
+ * List all stored secret metadata
12602
+ */
12603
+ async listSecrets() {
12604
+ const keys = await this.backend.keys();
12605
+ const matchingKeys = keys.filter((k) => k.startsWith(KEY_PREFIX));
12606
+ const results = [];
12607
+ for (const key of matchingKeys) {
12608
+ const raw = await this.backend.getItem(key);
12609
+ if (raw) {
12610
+ try {
12611
+ const payload = JSON.parse(raw);
12612
+ if (payload.metadata) {
12613
+ results.push(payload.metadata);
12614
+ }
12615
+ } catch {
12616
+ }
12617
+ }
12618
+ }
12619
+ return results;
12620
+ }
12621
+ /**
12622
+ * Execute callback with unwrapped secret, zeroizing the decrypted buffer upon completion
12623
+ */
12624
+ async withSecret(bundleHash, fn, options) {
12625
+ const raw = await this.backend.getItem(`${KEY_PREFIX}${bundleHash}`);
12626
+ if (!raw) {
12627
+ throw exports.SecretStorageException.notFound(bundleHash);
12628
+ }
12629
+ let payload;
12630
+ try {
12631
+ payload = JSON.parse(raw);
12632
+ } catch {
12633
+ throw exports.SecretStorageException.decryptionFailed("Corrupted payload format");
12634
+ }
12635
+ const passphrase = options?.passphrase ?? this.defaultPassphrase;
12636
+ if (!passphrase) {
12637
+ throw new exports.SecretStorageException("Passphrase required for secret decryption");
12638
+ }
12639
+ const salt = base64ToUint8Array(payload.salt);
12640
+ const iv = base64ToUint8Array(payload.iv);
12641
+ const ciphertext = base64ToUint8Array(payload.ciphertext);
12642
+ try {
12643
+ const key = await this.deriveKey(passphrase, salt, payload.iterations ?? DEFAULT_ITERATIONS);
12644
+ const decryptedBuffer = await globalThis.crypto.subtle.decrypt(
12645
+ {
12646
+ name: "AES-GCM",
12647
+ iv
12648
+ },
12649
+ key,
12650
+ ciphertext
12651
+ );
12652
+ const decryptedBytes = new Uint8Array(decryptedBuffer);
12653
+ return await withSecureBytes(decryptedBytes, async (bytes) => {
12654
+ const secretString = textDecoder.decode(bytes);
12655
+ return await fn(secretString);
12656
+ });
12657
+ } catch (err) {
12658
+ if (err instanceof exports.SecretStorageException) {
12659
+ throw err;
12660
+ }
12661
+ const msg = err instanceof Error ? err.message : String(err);
12662
+ throw exports.SecretStorageException.decryptionFailed(msg);
12663
+ }
12079
12664
  }
12080
12665
  };
12081
12666
 
12667
+ // src/storage/index.ts
12668
+ function createDefaultSecretStorage(options = {}) {
12669
+ if (options.type === "memory") {
12670
+ return new MemorySecretStorageProvider();
12671
+ }
12672
+ if (typeof globalThis.crypto !== "undefined" && typeof globalThis.crypto.subtle !== "undefined") {
12673
+ return new WebCryptoSecretStorageProvider({
12674
+ backend: options.backend,
12675
+ defaultPassphrase: options.defaultPassphrase,
12676
+ hardwareBacked: options.hardwareBacked
12677
+ });
12678
+ }
12679
+ return new MemorySecretStorageProvider();
12680
+ }
12681
+
12082
12682
  // src/index.ts
12083
- init_exception();
12084
- var SDK_VERSION = "0.9.5";
12683
+ var SDK_VERSION = "0.9.7";
12085
12684
  var SDK_NAME = "KnishIO-Client-TS";
12086
12685
  var COMPATIBLE_SERVER_VERSIONS = [4, 5];
12087
12686
  var SDK_INFO = {
@@ -12182,6 +12781,8 @@ exports.EXTENDED_COMPATIBILITY_TEST_VECTORS = EXTENDED_COMPATIBILITY_TEST_VECTOR
12182
12781
  exports.GraphQLClient = GraphQLClient;
12183
12782
  exports.KnishIO = KnishIO;
12184
12783
  exports.KnishIOClient = KnishIOClient;
12784
+ exports.MemorySecretStorageProvider = MemorySecretStorageProvider;
12785
+ exports.MemoryStorageBackend = MemoryStorageBackend;
12185
12786
  exports.Meta = Meta;
12186
12787
  exports.Molecule = Molecule;
12187
12788
  exports.Mutation = Mutation;
@@ -12227,6 +12828,7 @@ exports.SDK_NAME = SDK_NAME;
12227
12828
  exports.SDK_VERSION = SDK_VERSION;
12228
12829
  exports.TokenUnit = TokenUnit;
12229
12830
  exports.Wallet = Wallet;
12831
+ exports.WebCryptoSecretStorageProvider = WebCryptoSecretStorageProvider;
12230
12832
  exports.base64ToHex = base64ToHex;
12231
12833
  exports.bufferToHexString = bufferToHexString;
12232
12834
  exports.capitalize = capitalize;
@@ -12234,8 +12836,10 @@ exports.charsetBaseConvert = charsetBaseConvert;
12234
12836
  exports.chunkArray = chunkArray;
12235
12837
  exports.chunkSubstr = chunkSubstr;
12236
12838
  exports.configureSDK = configureSDK;
12839
+ exports.constantTimeCompare = constantTimeCompare;
12237
12840
  exports.convertToBase17 = convertToBase17;
12238
12841
  exports.createBundleHash = createBundleHash;
12842
+ exports.createDefaultSecretStorage = createDefaultSecretStorage;
12239
12843
  exports.createMolecularHash = createMolecularHash;
12240
12844
  exports.createPosition = createPosition;
12241
12845
  exports.createTokenSlug = createTokenSlug;
@@ -12277,5 +12881,8 @@ exports.validatePosition = validatePosition;
12277
12881
  exports.validateSecret = validateSecret;
12278
12882
  exports.validateWalletAddress = validateWalletAddress;
12279
12883
  exports.verifyOTSSignature = verifyOTSSignature;
12884
+ exports.withSecureBytes = withSecureBytes;
12885
+ exports.withSecureString = withSecureString;
12886
+ exports.zeroizeBytes = zeroizeBytes;
12280
12887
  //# sourceMappingURL=index.cjs.map
12281
12888
  //# sourceMappingURL=index.cjs.map