@wishknish/knishio-client-js 0.7.4 → 0.7.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wishknish/knishio-client-js",
3
- "version": "0.7.4",
3
+ "version": "0.7.5",
4
4
  "type": "module",
5
5
  "productName": "Knish.IO Javascript SDK Client",
6
6
  "description": "JavaScript implementation of the Knish.IO SDK to consume Knish.IO GraphQL APIs.",
@@ -54,6 +54,7 @@ import {
54
54
  } from './libraries/crypto.js'
55
55
  import Molecule from './Molecule.js'
56
56
  import Wallet from './Wallet.js'
57
+ import TokenUnit from './TokenUnit.js'
57
58
  import AuthToken from './AuthToken.js'
58
59
  import QueryContinuId from './query/QueryContinuId.js'
59
60
  import QueryWalletBundle from './query/QueryWalletBundle.js'
@@ -93,6 +94,7 @@ import AuthorizationRejectedException from './exception/AuthorizationRejectedExc
93
94
  import QueryAtom from './query/QueryAtom.js'
94
95
  import QueryPolicy from './query/QueryPolicy.js'
95
96
  import QueryMetaTypeViaAtom from './query/QueryMetaTypeViaAtom.js'
97
+ import QueryMetaTypeViaMolecule from './query/QueryMetaTypeViaMolecule.js'
96
98
  import MutationCreateRule from './mutation/MutationCreateRule.js'
97
99
  import MutationDepositBufferToken from './mutation/MutationDepositBufferToken.js'
98
100
  import MutationWithdrawBufferToken from './mutation/MutationWithdrawBufferToken.js'
@@ -777,10 +779,11 @@ export default class KnishIOClient {
777
779
  * @param {string|null} count
778
780
  * @param {string|null} countBy
779
781
  * @param {boolean} throughAtom
782
+ * @param {boolean} throughMolecule
780
783
  * @param {array|null} values
781
784
  * @param {array|null} keys
782
785
  * @param {array|null} atomValues
783
- * @return {Promise<ResponseMetaType>}
786
+ * @return {Promise<ResponseMetaType|ResponseMetaTypeViaAtom|ResponseMetaTypeViaMolecule>}
784
787
  */
785
788
  queryMeta ({
786
789
  metaType,
@@ -794,6 +797,7 @@ export default class KnishIOClient {
794
797
  count = null,
795
798
  countBy = null,
796
799
  throughAtom = true,
800
+ throughMolecule = false,
797
801
  values = null,
798
802
  keys = null,
799
803
  atomValues = null
@@ -803,7 +807,26 @@ export default class KnishIOClient {
803
807
  let query
804
808
  let variables
805
809
 
806
- if (throughAtom) {
810
+ if (throughMolecule) {
811
+ /**
812
+ * @type {QueryMetaTypeViaMolecule}
813
+ */
814
+ query = this.createQuery(QueryMetaTypeViaMolecule)
815
+ variables = QueryMetaTypeViaMolecule.createVariables({
816
+ metaType,
817
+ metaId,
818
+ key,
819
+ value,
820
+ latest,
821
+ filter,
822
+ queryArgs,
823
+ countBy,
824
+ values,
825
+ keys,
826
+ atomValues,
827
+ cellSlug: this.getCellSlug()
828
+ })
829
+ } else if (throughAtom) {
807
830
  /**
808
831
  * @type {QueryMetaTypeViaAtom}
809
832
  */
@@ -844,6 +867,23 @@ export default class KnishIOClient {
844
867
  return this.executeQuery(query, variables)
845
868
  }
846
869
 
870
+ /**
871
+ * Queries meta assets and verifies cryptographic integrity of associated molecules.
872
+ * Returns the same response as queryMeta(), with an additional `integrity` field on the payload
873
+ * containing verification results for each molecule.
874
+ *
875
+ * @param {object} params - Same parameters as queryMeta()
876
+ * @return {Promise<ResponseMetaType|ResponseMetaTypeViaAtom|ResponseMetaTypeViaMolecule>}
877
+ */
878
+ async queryMetaVerified (params) {
879
+ const response = await this.queryMeta(params)
880
+ const payload = response.payload()
881
+ if (payload) {
882
+ payload.integrity = response.verifyIntegrity()
883
+ }
884
+ return response
885
+ }
886
+
847
887
  /**
848
888
  * Query batch to get cascading meta instances by batchID
849
889
  *
@@ -2022,6 +2062,11 @@ export default class KnishIOClient {
2022
2062
  // Split token units (fused)
2023
2063
  sourceWallet.splitUnits(fusedTokenUnitIds, remainderWallet)
2024
2064
 
2065
+ // Coerce string newTokenUnit to TokenUnit object
2066
+ if (typeof newTokenUnit === 'string') {
2067
+ newTokenUnit = new TokenUnit(newTokenUnit, newTokenUnit, {})
2068
+ }
2069
+
2025
2070
  // Set recipient new fused token unit
2026
2071
  newTokenUnit.metas.fusedTokenUnits = sourceWallet.getTokenUnitsData()
2027
2072
  recipientWallet.tokenUnits = [newTokenUnit]
package/src/Molecule.js CHANGED
@@ -96,6 +96,7 @@ export default class Molecule {
96
96
  this.bundle = bundle
97
97
  this.sourceWallet = sourceWallet
98
98
  this.atoms = []
99
+ this.parentHashes = []
99
100
  if (version !== null && Object.prototype.hasOwnProperty.call(versions, version)) {
100
101
  this.version = String(version)
101
102
  }
@@ -112,6 +113,17 @@ export default class Molecule {
112
113
  }
113
114
  }
114
115
 
116
+ /**
117
+ * Sets parent molecular hashes for DAG linkage
118
+ *
119
+ * @param {string[]} hashes - Array of parent molecular hash strings
120
+ * @return {Molecule} this instance for chaining
121
+ */
122
+ withParentHashes (hashes) {
123
+ this.parentHashes = Array.isArray(hashes) ? [...hashes] : []
124
+ return this
125
+ }
126
+
115
127
  /**
116
128
  * Returns the cell slug delimiter
117
129
  *
@@ -327,20 +339,21 @@ export default class Molecule {
327
339
  })
328
340
  }
329
341
 
330
- // Build ContinuID chain metadata (matches Rust SDK GAP-07-002)
342
+ // ContinuID metadata for chain integrity validation (matching Rust SDK GAP-07-002).
343
+ // These are atom meta fields (not core fields), so they don't affect molecular hash.
331
344
  const continuIdMeta = {}
332
345
 
333
- // previousPosition: source wallet position being consumed
346
+ // previousPosition: the source wallet's position being consumed
334
347
  if (this.sourceWallet && this.sourceWallet.position) {
335
348
  continuIdMeta.previousPosition = this.sourceWallet.position
336
349
  }
337
350
 
338
- // pubkey: remainder wallet's public key for chain verification
351
+ // pubkey: ML-KEM public key for encrypted communication
339
352
  if (this.remainderWallet.pubkey) {
340
353
  continuIdMeta.pubkey = this.remainderWallet.pubkey
341
354
  }
342
355
 
343
- // characters: encoding format
356
+ // characters: encoding format for the wallet
344
357
  if (this.remainderWallet.characters) {
345
358
  continuIdMeta.characters = this.remainderWallet.characters
346
359
  }
@@ -493,28 +506,28 @@ export default class Molecule {
493
506
  for (const unit of units) {
494
507
  this.remainderWallet.tokenUnits.push(unit)
495
508
  }
496
- this.remainderWallet.balance = this.remainderWallet.tokenUnits.length
509
+ this.remainderWallet.balance = String(this.remainderWallet.tokenUnits.length)
497
510
 
498
511
  // Override first atom's token units to replenish values
499
512
  this.sourceWallet.tokenUnits = units
500
- this.sourceWallet.balance = this.sourceWallet.tokenUnits.length
513
+ this.sourceWallet.balance = String(this.sourceWallet.tokenUnits.length)
501
514
  } else {
502
515
  // Update wallet's balances
503
- this.remainderWallet.balance = this.sourceWallet.balance + amount
504
- this.sourceWallet.balance = amount
516
+ this.remainderWallet.balance = String(Number(this.sourceWallet.balance) + amount)
517
+ this.sourceWallet.balance = String(amount)
505
518
  }
506
519
 
507
520
  // Initializing a new Atom to remove tokens from source
508
521
  this.addAtom(Atom.create({
509
522
  isotope: 'V',
510
523
  wallet: this.sourceWallet,
511
- value: this.sourceWallet.balance
524
+ value: Number(this.sourceWallet.balance)
512
525
  }))
513
526
 
514
527
  this.addAtom(Atom.create({
515
528
  isotope: 'V',
516
529
  wallet: this.remainderWallet,
517
- value: this.remainderWallet.balance,
530
+ value: Number(this.remainderWallet.balance),
518
531
  metaType: 'walletBundle',
519
532
  metaId: this.remainderWallet.bundle
520
533
  }))
@@ -564,6 +577,60 @@ export default class Molecule {
564
577
  return this
565
578
  }
566
579
 
580
+ /**
581
+ * Creates a stackable V-isotope transfer with 3 atoms:
582
+ * source debit, recipient credit, remainder.
583
+ * Propagates batchId from source wallet.
584
+ *
585
+ * @param {Wallet} recipientWallet - wallet receiving the tokens
586
+ * @param {number} amount - amount to transfer
587
+ * @return {Molecule}
588
+ */
589
+ addStackableTransfer ({
590
+ recipientWallet,
591
+ amount
592
+ }) {
593
+ if (amount <= 0) {
594
+ throw new NegativeAmountException('Molecule::addStackableTransfer() - Amount must be positive!')
595
+ }
596
+
597
+ if (this.sourceWallet.balance - amount < 0) {
598
+ throw new BalanceInsufficientException()
599
+ }
600
+
601
+ const batchId = this.sourceWallet.batchId || generateBatchId({})
602
+
603
+ // Source debit atom
604
+ this.addAtom(Atom.create({
605
+ isotope: 'V',
606
+ wallet: this.sourceWallet,
607
+ value: -amount,
608
+ batchId
609
+ }))
610
+
611
+ // Recipient credit atom
612
+ this.addAtom(Atom.create({
613
+ isotope: 'V',
614
+ wallet: recipientWallet,
615
+ value: amount,
616
+ metaType: 'walletBundle',
617
+ metaId: recipientWallet.bundle,
618
+ batchId: generateBatchId({})
619
+ }))
620
+
621
+ // Remainder atom
622
+ this.addAtom(Atom.create({
623
+ isotope: 'V',
624
+ wallet: this.remainderWallet,
625
+ value: this.sourceWallet.balance - amount,
626
+ metaType: 'walletBundle',
627
+ metaId: this.remainderWallet.bundle,
628
+ batchId
629
+ }))
630
+
631
+ return this
632
+ }
633
+
567
634
  /**
568
635
  *
569
636
  * @param amount
@@ -1053,6 +1120,20 @@ export default class Molecule {
1053
1120
  return lastPosition
1054
1121
  }
1055
1122
 
1123
+ /**
1124
+ * Synchronous signing — identical to sign() since all operations are CPU-bound.
1125
+ * Provided for API parity with Rust SDK's sign_sync().
1126
+ *
1127
+ * @param {object} options
1128
+ * @param {string|null} options.bundle
1129
+ * @param {boolean} options.anonymous
1130
+ * @param {boolean} options.compressed
1131
+ * @return {string|null}
1132
+ */
1133
+ signSync (options = {}) {
1134
+ return this.sign(options)
1135
+ }
1136
+
1056
1137
  /**
1057
1138
  * Returns the base cell slug portion
1058
1139
  *
@@ -1096,6 +1177,12 @@ export default class Molecule {
1096
1177
  }))
1097
1178
  };
1098
1179
 
1180
+ // Parent molecular hashes for DAG linkage (only include when non-empty
1181
+ // to maintain backward compatibility with servers that don't support it)
1182
+ if (this.parentHashes && this.parentHashes.length > 0) {
1183
+ serialized.parentHashes = this.parentHashes
1184
+ }
1185
+
1099
1186
  // Extended context for Rust validator and local validation
1100
1187
  if (includeValidationContext) {
1101
1188
  serialized.cellSlugOrigin = this.cellSlugOrigin
@@ -1106,7 +1193,7 @@ export default class Molecule {
1106
1193
  address: this.sourceWallet.address,
1107
1194
  position: this.sourceWallet.position,
1108
1195
  token: this.sourceWallet.token,
1109
- balance: this.sourceWallet.balance || 0,
1196
+ balance: this.sourceWallet.balance || '0',
1110
1197
  bundle: this.sourceWallet.bundle,
1111
1198
  batchId: this.sourceWallet.batchId || null,
1112
1199
  characters: this.sourceWallet.characters || 'BASE64',
@@ -1122,7 +1209,7 @@ export default class Molecule {
1122
1209
  address: this.remainderWallet.address,
1123
1210
  position: this.remainderWallet.position,
1124
1211
  token: this.remainderWallet.token,
1125
- balance: this.remainderWallet.balance || 0,
1212
+ balance: this.remainderWallet.balance || '0',
1126
1213
  bundle: this.remainderWallet.bundle,
1127
1214
  batchId: this.remainderWallet.batchId || null,
1128
1215
  characters: this.remainderWallet.characters || 'BASE64',
@@ -1184,6 +1271,7 @@ export default class Molecule {
1184
1271
  molecule.molecularHash = data.molecularHash;
1185
1272
  molecule.createdAt = data.createdAt || String(+new Date());
1186
1273
  molecule.cellSlugOrigin = data.cellSlugOrigin;
1274
+ molecule.parentHashes = Array.isArray(data.parentHashes) ? [...data.parentHashes] : [];
1187
1275
 
1188
1276
  // Reconstruct atoms array with proper Atom instances
1189
1277
  if (Array.isArray(data.atoms)) {
@@ -1210,7 +1298,7 @@ export default class Molecule {
1210
1298
  });
1211
1299
 
1212
1300
  // Set additional properties for validation context
1213
- molecule.sourceWallet.balance = data.sourceWallet.balance || 0;
1301
+ molecule.sourceWallet.balance = String(data.sourceWallet.balance != null ? data.sourceWallet.balance : 0);
1214
1302
  molecule.sourceWallet.address = data.sourceWallet.address;
1215
1303
  if (data.sourceWallet.pubkey) {
1216
1304
  molecule.sourceWallet.pubkey = data.sourceWallet.pubkey;
@@ -1232,7 +1320,7 @@ export default class Molecule {
1232
1320
  });
1233
1321
 
1234
1322
  // Set additional properties for validation context
1235
- molecule.remainderWallet.balance = data.remainderWallet.balance || 0;
1323
+ molecule.remainderWallet.balance = String(data.remainderWallet.balance != null ? data.remainderWallet.balance : 0);
1236
1324
  molecule.remainderWallet.address = data.remainderWallet.address;
1237
1325
  if (data.remainderWallet.pubkey) {
1238
1326
  molecule.remainderWallet.pubkey = data.remainderWallet.pubkey;
package/src/Wallet.js CHANGED
@@ -87,7 +87,7 @@ export default class Wallet {
87
87
  characters = null
88
88
  }) {
89
89
  this.token = token
90
- this.balance = 0
90
+ this.balance = '0'
91
91
  this.molecules = {}
92
92
 
93
93
  // Empty values
@@ -208,6 +208,13 @@ export default class Wallet {
208
208
  token,
209
209
  position
210
210
  }) {
211
+ if (!secret) {
212
+ throw new WalletCredentialException('Wallet::generateKey() - Secret is required!')
213
+ }
214
+ if (!position) {
215
+ throw new WalletCredentialException('Wallet::generateKey() - Position is required!')
216
+ }
217
+
211
218
  // Normalize non-hex secret via SHAKE256 (matching Rust/Kotlin behavior)
212
219
  const secretHex = isHex(secret) ? secret : shake256(secret, 1024)
213
220
  // Normalize non-hex position via SHAKE256
@@ -301,6 +308,46 @@ export default class Wallet {
301
308
  return new Uint8Array(binaryString.length).map((_, i) => binaryString.charCodeAt(i))
302
309
  }
303
310
 
311
+ /**
312
+ * Returns balance as a Number for arithmetic operations.
313
+ * WARNING: Precision loss for values > 2^53.
314
+ *
315
+ * @return {number}
316
+ */
317
+ balanceAsNumber () {
318
+ return Number(this.balance)
319
+ }
320
+
321
+ /**
322
+ * Returns balance as a BigInt for precision-safe integer arithmetic.
323
+ * Truncates any fractional component.
324
+ *
325
+ * @return {bigint}
326
+ */
327
+ balanceAsBigInt () {
328
+ const str = String(this.balance)
329
+ const intPart = str.includes('.') ? str.split('.')[0] : str
330
+ return BigInt(intPart || '0')
331
+ }
332
+
333
+ /**
334
+ * Sets balance from a BigInt value
335
+ *
336
+ * @param {bigint} value
337
+ */
338
+ setBalanceBigInt (value) {
339
+ this.balance = value.toString()
340
+ }
341
+
342
+ /**
343
+ * Sets balance from a Number value, storing as String
344
+ *
345
+ * @param {number} value
346
+ */
347
+ setBalanceNumber (value) {
348
+ this.balance = String(value)
349
+ }
350
+
304
351
  /**
305
352
  *
306
353
  * @returns {*[]}
package/src/index.js CHANGED
@@ -53,8 +53,10 @@ import Meta from './Meta.js'
53
53
  import KnishIOClient from './KnishIOClient.js'
54
54
  import MutationPeering from './mutation/MutationPeering.js'
55
55
  import MutationAppendRequest from './mutation/MutationAppendRequest.js'
56
+ import QueryMetaTypeViaMolecule from './query/QueryMetaTypeViaMolecule.js'
56
57
  import ResponsePeering from './response/ResponsePeering.js'
57
58
  import ResponseAppendRequest from './response/ResponseAppendRequest.js'
59
+ import ResponseMetaTypeViaMolecule from './response/ResponseMetaTypeViaMolecule.js'
58
60
  import {
59
61
  base64ToHex,
60
62
  bufferToHexString,
@@ -78,6 +80,9 @@ export {
78
80
  Meta,
79
81
  KnishIOClient,
80
82
 
83
+ // queries
84
+ QueryMetaTypeViaMolecule,
85
+
81
86
  // mutations
82
87
  MutationPeering,
83
88
  MutationAppendRequest,
@@ -85,6 +90,7 @@ export {
85
90
  // responses
86
91
  ResponsePeering,
87
92
  ResponseAppendRequest,
93
+ ResponseMetaTypeViaMolecule,
88
94
 
89
95
  // strings
90
96
  chunkSubstr,