@wishknish/knishio-client-ts 0.7.2 → 0.7.6

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.
Files changed (51) hide show
  1. package/dist/index.cjs +2819 -1186
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.cts +378 -142
  4. package/dist/index.d.ts +378 -142
  5. package/dist/index.iife.js +22799 -20747
  6. package/dist/index.iife.js.map +1 -1
  7. package/dist/index.js +2817 -1185
  8. package/dist/index.js.map +1 -1
  9. package/package.json +12 -6
  10. package/src/AuthToken.ts +8 -2
  11. package/src/KnishIOClient.ts +367 -118
  12. package/src/constants.ts +5 -3
  13. package/src/core/Atom.ts +167 -100
  14. package/src/core/AtomMeta.ts +11 -28
  15. package/src/core/Meta.ts +18 -15
  16. package/src/core/Molecule.ts +582 -140
  17. package/src/core/Wallet.ts +19 -24
  18. package/src/index.ts +9 -10
  19. package/src/instance/rules/Rule.ts +128 -19
  20. package/src/libraries/CheckMolecule.ts +88 -1
  21. package/src/libraries/GraphQLClient.ts +17 -22
  22. package/src/libraries/crypto.ts +14 -30
  23. package/src/libraries/strings.ts +26 -25
  24. package/src/mutation/MutationAppendRequest.ts +52 -0
  25. package/src/mutation/MutationCreateRule.ts +2 -2
  26. package/src/mutation/MutationPeering.ts +37 -0
  27. package/src/mutation/MutationRequestAuthorization.ts +3 -3
  28. package/src/query/Query.ts +6 -6
  29. package/src/query/QueryActiveSession.ts +0 -1
  30. package/src/query/QueryAtom.ts +27 -27
  31. package/src/query/QueryContinuId.ts +8 -0
  32. package/src/query/QueryMetaType.ts +1 -1
  33. package/src/query/QueryMetaTypeViaMolecule.ts +221 -0
  34. package/src/query/QueryWalletList.ts +2 -2
  35. package/src/response/Response.ts +17 -1
  36. package/src/response/ResponseAppendRequest.ts +17 -0
  37. package/src/response/ResponseBalance.ts +8 -3
  38. package/src/response/ResponseContinuId.ts +1 -1
  39. package/src/response/ResponseMetaType.ts +32 -19
  40. package/src/response/ResponseMetaTypeViaMolecule.ts +224 -0
  41. package/src/response/ResponsePeering.ts +17 -0
  42. package/src/response/ResponsePolicy.ts +2 -3
  43. package/src/response/ResponseProposeMolecule.ts +0 -1
  44. package/src/response/ResponseRequestAuthorization.ts +32 -2
  45. package/src/response/ResponseRequestAuthorizationGuest.ts +25 -1
  46. package/src/response/ResponseWalletList.ts +1 -1
  47. package/src/schemas/index.ts +12 -1
  48. package/src/types/client.ts +3 -2
  49. package/src/types/graphql.ts +3 -2
  50. package/src/types/guards.ts +18 -5
  51. package/src/types/index.ts +19 -4
@@ -47,10 +47,10 @@ License: https://github.com/WishKnish/KnishIO-Client-TS/blob/master/LICENSE
47
47
  */
48
48
 
49
49
  import JsSHA from 'jssha'
50
- import { randomString, chunkSubstr } from '@/libraries/strings'
50
+ import { randomString, chunkSubstr, isHex } from '@/libraries/strings'
51
51
  import { generateBundleHash, generateSecret, shake256, generateBatchId } from '@/libraries/crypto'
52
52
  import WalletCredentialException from '@/exception/WalletCredentialException'
53
- import { isWalletAddress, isBundleHash, isPosition } from '@/types'
53
+ import { isBundleHash } from '@/types'
54
54
  // Post-quantum cryptography for ML-KEM768 key encapsulation
55
55
  import { ml_kem768 } from '@noble/post-quantum/ml-kem.js'
56
56
 
@@ -60,7 +60,7 @@ import { ml_kem768 } from '@noble/post-quantum/ml-kem.js'
60
60
  */
61
61
  export default class Wallet {
62
62
  public token: string
63
- public balance: number
63
+ public balance: string
64
64
  public address: string | null
65
65
  public position: string | null
66
66
  public bundle: string | null
@@ -97,18 +97,8 @@ export default class Wallet {
97
97
  batchId?: string | null
98
98
  characters?: string | null
99
99
  } = {}) {
100
- // TypeScript 2025: Exhaustive validation with type guards
101
- if (address !== null && !isWalletAddress(address)) {
102
- throw new WalletCredentialException('Invalid wallet address format')
103
- }
104
- if (bundle !== null && !isBundleHash(bundle)) {
105
- throw new WalletCredentialException('Invalid bundle hash format')
106
- }
107
- if (position !== null && !isPosition(position)) {
108
- throw new WalletCredentialException('Invalid position format')
109
- }
110
100
  this.token = token
111
- this.balance = 0
101
+ this.balance = '0'
112
102
  this.molecules = {}
113
103
 
114
104
  // Empty values
@@ -164,20 +154,21 @@ export default class Wallet {
164
154
  batchId?: string | null
165
155
  characters?: string | null
166
156
  }): Wallet {
167
- let position = null
168
-
157
+ let position: string | null = null
158
+
169
159
  // No credentials parameters provided?
170
160
  if (!secret && !bundle) {
171
161
  throw new WalletCredentialException()
172
162
  }
173
-
163
+
174
164
  // Secret, but no bundle?
175
165
  if (secret && !bundle) {
176
166
  position = Wallet.generatePosition()
177
167
  bundle = generateBundleHash(secret, 'Wallet::create')
178
168
  }
179
-
180
- // Wallet initialization
169
+
170
+ // Wallet initialization (matching JS SDK exactly)
171
+ // Bundle-only wallets (shadow) get no position/address generation
181
172
  return new Wallet({
182
173
  secret,
183
174
  bundle,
@@ -202,11 +193,15 @@ export default class Wallet {
202
193
  token: string
203
194
  position: string
204
195
  }): string {
196
+ // Normalize non-hex secret/position via SHAKE256 (matching JS/Rust SDKs)
197
+ const secretHex = isHex(secret) ? secret : shake256(secret, 1024)
198
+ const positionHex = isHex(position) ? position : shake256(position, 256)
199
+
205
200
  // Converting secret to bigInt
206
- const bigIntSecret = BigInt(`0x${secret}`)
207
-
201
+ const bigIntSecret = BigInt(`0x${secretHex}`)
202
+
208
203
  // Adding new position to the user secret to produce the indexed key
209
- const indexedKey = bigIntSecret + BigInt(`0x${position}`)
204
+ const indexedKey = bigIntSecret + BigInt(`0x${positionHex}`)
210
205
 
211
206
  // Hashing the indexed key to produce the intermediate key
212
207
  // CRITICAL FIX: Use sponge pattern exactly like JavaScript SDK
@@ -383,8 +378,8 @@ export default class Wallet {
383
378
  */
384
379
  initializeMLKEM(): void {
385
380
  // Generate a 64-byte (512-bit) seed from the Knish.IO private key
386
- // Use deterministic approach: generateSecret(key, 64) → matches JS SDK exactly
387
- const seedHex = generateSecret(this.key!, 64) // Matches JS SDK for cross-platform compatibility
381
+ // Use deterministic approach: generateSecret(key, 128) → 128 hex chars = 64 bytes
382
+ const seedHex = generateSecret(this.key!, 128) // 128 hex chars = 64 bytes, matches JS SDK
388
383
 
389
384
  // Convert the hex string to a Uint8Array
390
385
  const seed = new Uint8Array(64)
package/src/index.ts CHANGED
@@ -113,6 +113,10 @@ export { default as MutationCreateMeta } from './mutation/MutationCreateMeta'
113
113
  export { default as MutationCreateToken } from './mutation/MutationCreateToken'
114
114
  export { default as MutationRequestTokens } from './mutation/MutationRequestTokens'
115
115
 
116
+ // P-isotope and A-isotope Mutation classes
117
+ export { default as MutationPeering } from './mutation/MutationPeering'
118
+ export { default as MutationAppendRequest } from './mutation/MutationAppendRequest'
119
+
116
120
  // Essential Response classes (Phase 1)
117
121
  export { default as ResponseBalance } from './response/ResponseBalance'
118
122
  export { default as ResponseWalletBundle } from './response/ResponseWalletBundle'
@@ -134,6 +138,10 @@ export { default as ResponseMetaTypeViaAtom } from './response/ResponseMetaTypeV
134
138
  export { default as ResponseCreateToken } from './response/ResponseCreateToken'
135
139
  export { default as ResponseRequestTokens } from './response/ResponseRequestTokens'
136
140
 
141
+ // P-isotope and A-isotope Response classes
142
+ export { default as ResponsePeering } from './response/ResponsePeering'
143
+ export { default as ResponseAppendRequest } from './response/ResponseAppendRequest'
144
+
137
145
  // Phase 2 Tier 2 Priority 3: Essential Query Operations
138
146
  export { default as ResponseContinuId } from './response/ResponseContinuId'
139
147
  // QueryBatch uses base Response class directly
@@ -479,16 +487,7 @@ export const DevUtils = process.env.NODE_ENV === 'development' ? {
479
487
  // COMPATIBILITY WITH JAVASCRIPT SDK
480
488
  // =============================================================================
481
489
 
482
- /**
483
- * Legacy export names for compatibility with JavaScript SDK
484
- * These maintain the exact same naming convention
485
- */
486
- export {
487
- // Core exports (matching JS SDK exactly)
488
- shake256 as default, // The JS SDK doesn't export a default, but this maintains compatibility
489
- }
490
-
491
- // Maintain exact export structure as JS SDK index.js
490
+ // Maintain exact export structure as JS SDK index.js (named exports only)
492
491
  export const KnishIO = {
493
492
  shake256,
494
493
  generateSecret,
@@ -1,29 +1,138 @@
1
- /**
2
- * Rule class - Policy and rule management for KnishIO DLT
3
- * STUB IMPLEMENTATION - Full implementation pending
4
- */
1
+ /*
2
+ (
3
+ (/(
4
+ (//(
5
+ (///(
6
+ (/////(
7
+ (//////( )
8
+ (////////( (/)
9
+ (////////( (///)
10
+ (//////////( (////)
11
+ (//////////( (//////)
12
+ (////////////( (///////)
13
+ (/////////////( (/////////)
14
+ (//////////////( (///////////)
15
+ (///////////////( (/////////////)
16
+ (////////////////( (//////////////)
17
+ ((((((((((((((((((( (((((((((((((((
18
+ ((((((((((((((((((( ((((((((((((((
19
+ ((((((((((((((((((( ((((((((((((((
20
+ (((((((((((((((((((( (((((((((((((
21
+ (((((((((((((((((((( ((((((((((((
22
+ ((((((((((((((((((( ((((((((((((
23
+ ((((((((((((((((((( ((((((((((
24
+ ((((((((((((((((((/ (((((((((
25
+ (((((((((((((((((( ((((((((
26
+ ((((((((((((((((( (((((((
27
+ (((((((((((((((((( (((((
28
+ ################# ##
29
+ ################ #
30
+ ################# ##
31
+ %################ ###
32
+ ###############( ####
33
+ ############### ####
34
+ ############### ######
35
+ %#############( (#######
36
+ %############# #########
37
+ ############( ##########
38
+ ########### #############
39
+ ######### ##############
40
+ %######
41
+
42
+ Powered by Knish.IO: Connecting a Decentralized World
43
+
44
+ Please visit https://github.com/WishKnish/KnishIO-Client-TS for information.
45
+
46
+ License: https://github.com/WishKnish/KnishIO-Client-TS/blob/master/LICENSE
47
+ */
48
+
49
+ import Callback from './Callback'
50
+ import Condition from './Condition'
51
+ import RuleArgumentException from './exception/RuleArgumentException'
52
+ import { MetaMissingException } from '@/exception'
53
+
54
+ interface RuleParams {
55
+ condition?: Condition[]
56
+ callback?: Callback[]
57
+ }
58
+
59
+ interface RuleJSON {
60
+ condition: Condition[]
61
+ callback: Callback[]
62
+ }
63
+
5
64
  export default class Rule {
6
- public name: string
7
- public conditions: any[]
8
- public actions: any[]
9
-
10
- constructor(data: any = {}) {
11
- this.name = data.name || ''
12
- this.conditions = data.conditions || []
13
- this.actions = data.actions || []
65
+ private __condition: Condition[]
66
+ private __callback: Callback[]
67
+
68
+ constructor({ condition = [], callback = [] }: RuleParams) {
69
+ for (const element of condition) {
70
+ if (!(element instanceof Condition)) {
71
+ throw new RuleArgumentException()
72
+ }
73
+ }
74
+
75
+ for (const element of callback) {
76
+ if (!(element instanceof Callback)) {
77
+ throw new RuleArgumentException()
78
+ }
79
+ }
80
+
81
+ this.__condition = condition
82
+ this.__callback = callback
83
+ }
84
+
85
+ /**
86
+ * Adds a condition to this rule
87
+ */
88
+ set comparison(condition: Condition | Record<string, any>) {
89
+ this.__condition.push(
90
+ condition instanceof Condition ? condition : Condition.toObject(condition as any)
91
+ )
92
+ }
93
+
94
+ /**
95
+ * Adds a callback to this rule
96
+ */
97
+ set callback(callback: Callback | Record<string, any>) {
98
+ this.__callback.push(
99
+ callback instanceof Callback ? callback : Callback.toObject(callback as any)
100
+ )
14
101
  }
15
102
 
16
103
  /**
17
104
  * Convert rule data to Rule object
18
- * @param data - Rule data to convert
105
+ * @param object - Rule data to convert
19
106
  * @return Rule instance
20
107
  */
21
- static toObject(data: any): Rule {
22
- if (data instanceof Rule) {
23
- return data
108
+ static toObject(object: any): Rule {
109
+ if (!object.condition) {
110
+ throw new MetaMissingException('Rule::toObject() - Incorrect rule format! There is no condition field.')
111
+ }
112
+ if (!object.callback) {
113
+ throw new MetaMissingException('Rule::toObject() - Incorrect rule format! There is no callback field.')
114
+ }
115
+
116
+ const rule = new Rule({})
117
+
118
+ for (const condition of object.condition) {
119
+ rule.comparison = condition
24
120
  }
25
- return new Rule(data)
121
+
122
+ for (const callback of object.callback) {
123
+ rule.callback = callback
124
+ }
125
+
126
+ return rule
26
127
  }
27
128
 
28
- // TODO: Implement remaining Rule methods
29
- }
129
+ /**
130
+ * Converts to JSON representation
131
+ */
132
+ toJSON(): RuleJSON {
133
+ return {
134
+ condition: this.__condition,
135
+ callback: this.__callback
136
+ }
137
+ }
138
+ }
@@ -71,6 +71,7 @@ import Rule from '@/instance/rules/Rule'
71
71
  import { base64ToHex, chunkSubstr } from '@/libraries/strings'
72
72
  import { shake256 } from '@/libraries/crypto'
73
73
  import Dot from '@/libraries/Dot'
74
+ import Molecule from '@/core/Molecule'
74
75
 
75
76
  // Type definitions for Molecule structure
76
77
  interface MoleculeStructure {
@@ -461,7 +462,7 @@ export default class CheckMolecule {
461
462
  throw new TypeError('Invalid isotope "V" values')
462
463
  }
463
464
 
464
- const remainder = senderWallet.balance + firstValue
465
+ const remainder = Number(senderWallet.balance) + firstValue
465
466
 
466
467
  // Is there enough balance to send?
467
468
  if (remainder < 0) {
@@ -578,4 +579,90 @@ export default class CheckMolecule {
578
579
  // Looks like we passed all the tests!
579
580
  return true
580
581
  }
582
+
583
+ /**
584
+ * Reconstructs a Molecule instance from server-side GraphQL data.
585
+ * Maps server atom field names to SDK-expected format and parses metasJson.
586
+ */
587
+ static fromServerData({
588
+ molecularHash,
589
+ bundleHash,
590
+ cellSlug = null,
591
+ status = null,
592
+ createdAt = null,
593
+ atoms = []
594
+ }: {
595
+ molecularHash: string
596
+ bundleHash: string
597
+ cellSlug?: string | null
598
+ status?: string | null
599
+ createdAt?: string | null
600
+ atoms?: any[]
601
+ }): Molecule {
602
+ const mappedAtoms = atoms.map((serverAtom: any) => {
603
+ let meta: Array<{ key: string; value: string }> = []
604
+ if (serverAtom.metasJson) {
605
+ try {
606
+ const parsed = JSON.parse(serverAtom.metasJson)
607
+ if (Array.isArray(parsed)) {
608
+ meta = parsed
609
+ } else if (parsed && typeof parsed === 'object') {
610
+ meta = Object.entries(parsed).map(([key, value]) => ({ key, value: String(value) }))
611
+ }
612
+ } catch (e) {
613
+ meta = []
614
+ }
615
+ }
616
+
617
+ return {
618
+ position: serverAtom.position || null,
619
+ walletAddress: serverAtom.walletAddress || null,
620
+ isotope: serverAtom.isotope || null,
621
+ token: serverAtom.tokenSlug || serverAtom.token || null,
622
+ value: serverAtom.value != null ? String(serverAtom.value) : null,
623
+ batchId: serverAtom.batchId || null,
624
+ metaType: serverAtom.metaType || null,
625
+ metaId: serverAtom.metaId || null,
626
+ meta,
627
+ index: serverAtom.index != null ? serverAtom.index : null,
628
+ otsFragment: serverAtom.otsFragment || null,
629
+ createdAt: serverAtom.createdAt || null
630
+ }
631
+ })
632
+
633
+ return Molecule.fromJSON({
634
+ molecularHash,
635
+ bundle: bundleHash,
636
+ cellSlug,
637
+ status,
638
+ createdAt,
639
+ atoms: mappedAtoms
640
+ })
641
+ }
642
+
643
+ /**
644
+ * Verifies a molecule reconstructed from server-side GraphQL data.
645
+ * Returns an object with verification result and any error details.
646
+ */
647
+ static verifyFromServerData(moleculeData: any): {
648
+ molecularHash: string | null
649
+ verified: boolean
650
+ error: string | null
651
+ } {
652
+ try {
653
+ const molecule = CheckMolecule.fromServerData(moleculeData)
654
+ new CheckMolecule(molecule as unknown as MoleculeStructure).verify()
655
+ return {
656
+ molecularHash: moleculeData.molecularHash,
657
+ verified: true,
658
+ error: null
659
+ }
660
+ } catch (error) {
661
+ return {
662
+ molecularHash: moleculeData.molecularHash || null,
663
+ verified: false,
664
+ error: (error as Error).message || String(error)
665
+ }
666
+ }
667
+ }
581
668
  }
@@ -58,8 +58,7 @@ import { pipe, subscribe } from 'wonka'
58
58
  import type {
59
59
  GraphQLClient as IGraphQLClient,
60
60
  GraphQLRequest,
61
- GraphQLResponse,
62
- GraphQLVariables
61
+ GraphQLResponse
63
62
  } from '@/types'
64
63
  import type Wallet from '@/core/Wallet'
65
64
 
@@ -157,42 +156,38 @@ export default class GraphQLClient implements IGraphQLClient {
157
156
  })
158
157
  }
159
158
 
160
- async query<TResult = unknown, TVariables = GraphQLVariables>(
161
- request: GraphQLRequest,
162
- variables?: TVariables
159
+ async query<TResult = unknown>(
160
+ request: GraphQLRequest
163
161
  ): Promise<GraphQLResponse<TResult>> {
164
- const finalVariables = (variables || request.variables || {}) as Record<string, any>
165
- const result = await this.$__client.query(request.query, finalVariables).toPromise()
162
+ const { query, variables } = request
163
+ const result = await this.$__client.query(query, variables || {}).toPromise()
166
164
  return this.formatResponse<TResult>(result)
167
165
  }
168
166
 
169
- async mutation<TResult = unknown, TVariables = GraphQLVariables>(
170
- request: GraphQLRequest,
171
- variables?: TVariables
167
+ async mutation<TResult = unknown>(
168
+ request: GraphQLRequest
172
169
  ): Promise<GraphQLResponse<TResult>> {
173
- const finalVariables = (variables || request.variables || {}) as Record<string, any>
170
+ const { query, variables } = request
174
171
  // Support both query and mutation properties for backward compatibility
175
- const mutationString = (request as any).mutation || request.query
176
- const result = await this.$__client.mutation(mutationString, finalVariables).toPromise()
172
+ const mutationString = (request as any).mutation || query
173
+ const result = await this.$__client.mutation(mutationString, variables || {}).toPromise()
177
174
  return this.formatResponse<TResult>(result)
178
175
  }
179
176
 
180
177
  // Alias for mutation() to match JS SDK interface
181
- async mutate<TResult = unknown, TVariables = GraphQLVariables>(
182
- request: GraphQLRequest,
183
- variables?: TVariables
178
+ async mutate<TResult = unknown>(
179
+ request: GraphQLRequest
184
180
  ): Promise<GraphQLResponse<TResult>> {
185
- return this.mutation<TResult, TVariables>(request, variables)
181
+ return this.mutation<TResult>(request)
186
182
  }
187
183
 
188
- async *subscription<TResult = unknown, TVariables = GraphQLVariables>(
189
- request: GraphQLRequest,
190
- variables?: TVariables
184
+ async *subscription<TResult = unknown>(
185
+ request: GraphQLRequest
191
186
  ): AsyncIterableIterator<GraphQLResponse<TResult>> {
192
- const finalVariables = (variables || request.variables || {}) as Record<string, any>
187
+ const { query, variables } = request
193
188
 
194
189
  // Create subscription stream
195
- const subscription = this.$__client.subscription(request.query, finalVariables)
190
+ const subscription = this.$__client.subscription(query, variables || {})
196
191
 
197
192
  // Convert wonka stream to async iterator
198
193
  const results: GraphQLResponse<TResult>[] = []
@@ -176,19 +176,15 @@ export function generateSecretEnhanced(options: SecretGenerationOptions): string
176
176
  * Generate bundle hash from secret - MUST produce identical results to JS SDK
177
177
  * Uses SHAKE256 with 256-bit output (64 hex characters)
178
178
  */
179
- export function generateBundleHash(secret: string, source: string | null = null): BundleHash {
179
+ export function generateBundleHash(secret: string, _source: string | null = null): BundleHash {
180
180
  if (!secret || secret.length === 0) {
181
181
  throw new Error('Secret is required for bundle hash generation')
182
182
  }
183
183
 
184
- let input = secret
185
- if (source) {
186
- input = secret + source
187
- }
188
-
189
- // Fix: BUNDLE_HASH_LENGTH is 64 (hex chars), but shake256 expects bits
190
- // So we multiply by 4: 64 hex chars * 4 = 256 bits (matching JS SDK)
191
- const hash = shake256(input, CRYPTO_CONSTANTS.BUNDLE_HASH_LENGTH * 4)
184
+ // CRITICAL: source parameter must be IGNORED to match JS SDK behavior.
185
+ // JS SDK's generateBundleHash() accepts source but never uses it.
186
+ // The bundle hash is an identity anchor and must be deterministic from secret alone.
187
+ const hash = shake256(secret, CRYPTO_CONSTANTS.BUNDLE_HASH_LENGTH * 4)
192
188
  return hash as BundleHash
193
189
  }
194
190
 
@@ -251,30 +247,18 @@ export function generatePositionEnhanced(options: PositionGenerationOptions = {}
251
247
  * Generate batch ID from molecular hash and index
252
248
  * Uses SHAKE256 to create deterministic batch identifiers
253
249
  */
254
- export function generateBatchId({
255
- molecularHash = null,
256
- index = null
257
- }: {
250
+ export function generateBatchId({
251
+ molecularHash = null,
252
+ index = null
253
+ }: {
258
254
  molecularHash?: string | null
259
- index?: number | null
255
+ index?: number | null
260
256
  } = {}): string {
261
- let input = ''
262
-
263
- if (molecularHash) {
264
- input += molecularHash
257
+ // Match JS SDK: require BOTH molecularHash AND index for deterministic batch ID
258
+ if (molecularHash && index !== null) {
259
+ return shake256(molecularHash + index, 256) // 256 bits = 64 hex chars
265
260
  }
266
-
267
- if (index !== null) {
268
- input += index.toString()
269
- }
270
-
271
- // If no input provided, generate random batch ID
272
- if (input === '') {
273
- input = randomString(32)
274
- }
275
-
276
- // Use SHAKE256 to generate 64-character batch ID
277
- return shake256(input, 64)
261
+ return randomString(64)
278
262
  }
279
263
 
280
264
  // =============================================================================
@@ -78,7 +78,7 @@ export function chunkSubstr(str: string, size: number): string[] {
78
78
  */
79
79
  export function randomString(
80
80
  length = 32,
81
- alphabet = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
81
+ alphabet = 'abcdef0123456789'
82
82
  ): string {
83
83
  if (length <= 0) return ''
84
84
 
@@ -108,7 +108,8 @@ export function randomString(
108
108
 
109
109
  /**
110
110
  * Convert between different character sets/bases
111
- * Ported from JavaScript SDK for cross-platform compatibility
111
+ * Uses BigInt for arbitrary-precision arithmetic to handle 256-bit hashes
112
+ * MUST match JavaScript SDK charsetBaseConvert exactly
112
113
  */
113
114
  export function charsetBaseConvert(
114
115
  src: string,
@@ -120,37 +121,37 @@ export function charsetBaseConvert(
120
121
  if (!src || fromBase < 2 || toBase < 2 || fromBase > srcSymbolTable.length || toBase > destSymbolTable.length) {
121
122
  return false
122
123
  }
123
-
124
+
124
125
  try {
125
- // Convert from source base to decimal
126
- let decimalValue = 0
127
- const srcLength = src.length
128
-
129
- for (let i = 0; i < srcLength; i++) {
130
- const char = src.charAt(srcLength - 1 - i)
131
- const charIndex = srcSymbolTable.indexOf(char)
132
-
133
- if (charIndex === -1) {
126
+ // Convert from source base to BigInt (arbitrary precision)
127
+ let val = BigInt(0)
128
+ const bigFromBase = BigInt(fromBase)
129
+
130
+ for (let charIndex = 0; charIndex < src.length; charIndex++) {
131
+ const symbolIndex = srcSymbolTable.indexOf(src.charAt(charIndex))
132
+
133
+ if (symbolIndex === -1) {
134
134
  return false // Invalid character for source base
135
135
  }
136
-
137
- decimalValue += charIndex * Math.pow(fromBase, i)
136
+
137
+ val = val * bigFromBase + BigInt(symbolIndex)
138
138
  }
139
-
140
- // Convert from decimal to destination base
141
- if (decimalValue === 0) {
139
+
140
+ // Convert from BigInt to destination base
141
+ if (val === BigInt(0)) {
142
142
  return destSymbolTable.charAt(0)
143
143
  }
144
-
144
+
145
+ const bigToBase = BigInt(toBase)
145
146
  let result = ''
146
- while (decimalValue > 0) {
147
- const remainder = decimalValue % toBase
148
- result = destSymbolTable.charAt(remainder) + result
149
- decimalValue = Math.floor(decimalValue / toBase)
147
+ while (val > BigInt(0)) {
148
+ const remainder = val % bigToBase
149
+ result = destSymbolTable.charAt(Number(remainder)) + result
150
+ val = val / bigToBase
150
151
  }
151
-
152
- return result
153
-
152
+
153
+ return result || '0'
154
+
154
155
  } catch (error) {
155
156
  return false
156
157
  }
@@ -0,0 +1,52 @@
1
+ /*
2
+ Powered by Knish.IO: Connecting a Decentralized World
3
+
4
+ Please visit https://github.com/WishKnish/KnishIO-Client-TS for information.
5
+
6
+ License: https://github.com/WishKnish/KnishIO-Client-TS/blob/master/LICENSE
7
+ */
8
+
9
+ import MutationProposeMolecule from './MutationProposeMolecule'
10
+ import ResponseAppendRequest from '../response/ResponseAppendRequest'
11
+
12
+ /**
13
+ * Mutation for submitting an append request via the A-isotope
14
+ * Matches JavaScript SDK MutationAppendRequest implementation exactly
15
+ */
16
+ export default class MutationAppendRequest extends MutationProposeMolecule {
17
+ /**
18
+ * Fills a molecule with an A-isotope append request atom
19
+ * Matches JavaScript SDK fillMolecule method exactly
20
+ */
21
+ override fillMolecule({
22
+ metaType,
23
+ metaId,
24
+ action,
25
+ meta = {}
26
+ }: {
27
+ metaType: string
28
+ metaId: string
29
+ action: string
30
+ meta?: Record<string, any>
31
+ }): void {
32
+ (this.$__molecule as any).initAppendRequest({
33
+ metaType,
34
+ metaId,
35
+ action,
36
+ meta
37
+ });
38
+ (this.$__molecule as any).sign({});
39
+ (this.$__molecule as any).check()
40
+ }
41
+
42
+ /**
43
+ * Builds a Response object out of a JSON object
44
+ * Matches JavaScript SDK createResponse method exactly
45
+ */
46
+ override createResponse(json: any): ResponseAppendRequest {
47
+ return new ResponseAppendRequest({
48
+ query: this,
49
+ json
50
+ })
51
+ }
52
+ }