@wishknish/knishio-client-ts 0.7.0

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 (141) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +425 -0
  3. package/dist/index.cjs +9961 -0
  4. package/dist/index.cjs.map +1 -0
  5. package/dist/index.iife.js +32241 -0
  6. package/dist/index.iife.js.map +1 -0
  7. package/dist/index.js +9853 -0
  8. package/dist/index.js.map +1 -0
  9. package/package.json +113 -0
  10. package/src/AuthToken.ts +214 -0
  11. package/src/KnishIOClient.ts +2020 -0
  12. package/src/constants.ts +398 -0
  13. package/src/core/Atom.ts +646 -0
  14. package/src/core/AtomMeta.ts +278 -0
  15. package/src/core/Meta.ts +428 -0
  16. package/src/core/Molecule.ts +825 -0
  17. package/src/core/PolicyMeta.ts +130 -0
  18. package/src/core/TokenUnit.ts +148 -0
  19. package/src/core/Wallet.ts +467 -0
  20. package/src/exception/AtomIndexException.ts +97 -0
  21. package/src/exception/AtomsMissingException.ts +109 -0
  22. package/src/exception/AuthorizationRejectedException.ts +63 -0
  23. package/src/exception/BalanceInsufficientException.ts +58 -0
  24. package/src/exception/BaseException.ts +275 -0
  25. package/src/exception/BatchIdException.ts +58 -0
  26. package/src/exception/CodeException.ts +119 -0
  27. package/src/exception/DecryptionKeyException.ts +65 -0
  28. package/src/exception/InvalidResponseException.ts +112 -0
  29. package/src/exception/MetaMissingException.ts +58 -0
  30. package/src/exception/MolecularHashMismatchException.ts +115 -0
  31. package/src/exception/MolecularHashMissingException.ts +58 -0
  32. package/src/exception/NegativeAmountException.ts +58 -0
  33. package/src/exception/PolicyInvalidException.ts +58 -0
  34. package/src/exception/SignatureMalformedException.ts +58 -0
  35. package/src/exception/SignatureMismatchException.ts +98 -0
  36. package/src/exception/StackableUnitAmountException.ts +65 -0
  37. package/src/exception/StackableUnitDecimalsException.ts +65 -0
  38. package/src/exception/TransferBalanceException.ts +98 -0
  39. package/src/exception/TransferMalformedException.ts +58 -0
  40. package/src/exception/TransferMismatchedException.ts +58 -0
  41. package/src/exception/TransferRemainderException.ts +58 -0
  42. package/src/exception/TransferToSelfException.ts +58 -0
  43. package/src/exception/TransferUnbalancedException.ts +58 -0
  44. package/src/exception/UnauthenticatedException.ts +147 -0
  45. package/src/exception/WalletCredentialException.ts +108 -0
  46. package/src/exception/WalletShadowException.ts +65 -0
  47. package/src/exception/WrongTokenTypeException.ts +58 -0
  48. package/src/exception/index.ts +272 -0
  49. package/src/index.ts +512 -0
  50. package/src/instance/rules/Callback.ts +257 -0
  51. package/src/instance/rules/Condition.ts +96 -0
  52. package/src/instance/rules/Meta.ts +163 -0
  53. package/src/instance/rules/Rule.ts +29 -0
  54. package/src/instance/rules/exception/RuleArgumentException.ts +65 -0
  55. package/src/libraries/CheckMolecule.ts +581 -0
  56. package/src/libraries/Decimal.ts +94 -0
  57. package/src/libraries/Dot.ts +202 -0
  58. package/src/libraries/GraphQLClient.ts +276 -0
  59. package/src/libraries/Hex.ts +155 -0
  60. package/src/libraries/UrqlClientWrapper.ts +336 -0
  61. package/src/libraries/array.ts +91 -0
  62. package/src/libraries/crypto.ts +816 -0
  63. package/src/libraries/strings.ts +458 -0
  64. package/src/mutation/Mutation.ts +103 -0
  65. package/src/mutation/MutationActiveSession.ts +108 -0
  66. package/src/mutation/MutationClaimShadowWallet.ts +91 -0
  67. package/src/mutation/MutationCreateIdentifier.ts +86 -0
  68. package/src/mutation/MutationCreateMeta.ts +92 -0
  69. package/src/mutation/MutationCreateRule.ts +90 -0
  70. package/src/mutation/MutationCreateToken.ts +92 -0
  71. package/src/mutation/MutationCreateWallet.ts +78 -0
  72. package/src/mutation/MutationDepositBufferToken.ts +72 -0
  73. package/src/mutation/MutationLinkIdentifier.ts +87 -0
  74. package/src/mutation/MutationProposeMolecule.ts +147 -0
  75. package/src/mutation/MutationRequestAuthorization.ts +77 -0
  76. package/src/mutation/MutationRequestAuthorizationGuest.ts +85 -0
  77. package/src/mutation/MutationRequestTokens.ts +98 -0
  78. package/src/mutation/MutationTransferTokens.ts +87 -0
  79. package/src/mutation/MutationWithdrawBufferToken.ts +73 -0
  80. package/src/query/Query.ts +187 -0
  81. package/src/query/QueryActiveSession.ts +89 -0
  82. package/src/query/QueryAtom.ts +275 -0
  83. package/src/query/QueryBalance.ts +102 -0
  84. package/src/query/QueryBatch.ts +145 -0
  85. package/src/query/QueryBatchHistory.ts +86 -0
  86. package/src/query/QueryContinuId.ts +91 -0
  87. package/src/query/QueryMetaType.ts +177 -0
  88. package/src/query/QueryMetaTypeViaAtom.ts +199 -0
  89. package/src/query/QueryPolicy.ts +91 -0
  90. package/src/query/QueryToken.ts +90 -0
  91. package/src/query/QueryUserActivity.ts +153 -0
  92. package/src/query/QueryWalletBundle.ts +92 -0
  93. package/src/query/QueryWalletList.ts +106 -0
  94. package/src/response/EnhancedResponse.ts +345 -0
  95. package/src/response/Response.ts +254 -0
  96. package/src/response/ResponseActiveSession.ts +72 -0
  97. package/src/response/ResponseAtom.ts +132 -0
  98. package/src/response/ResponseAuthorizationGuest.ts +119 -0
  99. package/src/response/ResponseBalance.ts +153 -0
  100. package/src/response/ResponseClaimShadowWallet.ts +56 -0
  101. package/src/response/ResponseContinuId.ts +101 -0
  102. package/src/response/ResponseCreateIdentifier.ts +56 -0
  103. package/src/response/ResponseCreateMeta.ts +58 -0
  104. package/src/response/ResponseCreateRule.ts +56 -0
  105. package/src/response/ResponseCreateToken.ts +58 -0
  106. package/src/response/ResponseCreateWallet.ts +58 -0
  107. package/src/response/ResponseLinkIdentifier.ts +87 -0
  108. package/src/response/ResponseMetaBatch.ts +72 -0
  109. package/src/response/ResponseMetaType.ts +108 -0
  110. package/src/response/ResponseMetaTypeViaAtom.ts +108 -0
  111. package/src/response/ResponsePolicy.ts +90 -0
  112. package/src/response/ResponseProposeMolecule.ts +155 -0
  113. package/src/response/ResponseQueryActiveSession.ts +105 -0
  114. package/src/response/ResponseQueryUserActivity.ts +89 -0
  115. package/src/response/ResponseRequestAuthorization.ts +100 -0
  116. package/src/response/ResponseRequestAuthorizationGuest.ts +133 -0
  117. package/src/response/ResponseRequestTokens.ts +58 -0
  118. package/src/response/ResponseTransferTokens.ts +72 -0
  119. package/src/response/ResponseWalletBundle.ts +95 -0
  120. package/src/response/ResponseWalletList.ts +165 -0
  121. package/src/schemas/index.ts +457 -0
  122. package/src/subscribe/ActiveSessionSubscribe.ts +72 -0
  123. package/src/subscribe/ActiveWalletSubscribe.ts +99 -0
  124. package/src/subscribe/CreateMoleculeSubscribe.ts +106 -0
  125. package/src/subscribe/Subscribe.ts +182 -0
  126. package/src/subscribe/WalletStatusSubscribe.ts +70 -0
  127. package/src/subscribe/index.ts +59 -0
  128. package/src/types/assertions.ts +722 -0
  129. package/src/types/client.ts +567 -0
  130. package/src/types/crypto.ts +541 -0
  131. package/src/types/graphql.ts +630 -0
  132. package/src/types/guards.ts +659 -0
  133. package/src/types/index.ts +614 -0
  134. package/src/types/response.ts +133 -0
  135. package/src/types/template-literals.ts +382 -0
  136. package/src/validation/UNIVERSAL_CONFIGURATION_INTERFACES.ts +580 -0
  137. package/src/validation/ValidationService.ts +607 -0
  138. package/src/validation/schemas.ts +447 -0
  139. package/src/versions/HashAtom.ts +170 -0
  140. package/src/versions/Version4.ts +120 -0
  141. package/src/versions/index.ts +80 -0
@@ -0,0 +1,646 @@
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
+ /**
50
+ * Atom class - The fundamental unit of KnishIO DLT transactions
51
+ *
52
+ * Atoms represent single, monodirectional actions in the distributed ledger.
53
+ * They are the building blocks of Molecules and maintain strict type safety
54
+ * while ensuring perfect compatibility with other SDK implementations.
55
+ */
56
+
57
+ import { shake256, convertToBase17 } from '@/libraries/crypto'
58
+ import JsSHA from 'jssha'
59
+ import { PROTOCOL_CONFIG } from '@/constants'
60
+ import { handleIsotope, isWalletAddress, isPosition } from '@/types/guards'
61
+ import type {
62
+ AtomIsotope,
63
+ AtomParams,
64
+ WalletAddress,
65
+ TokenSlug,
66
+ MetaType,
67
+ MetaId,
68
+ BatchId
69
+ } from '@/types'
70
+
71
+ // =============================================================================
72
+ // ATOM CONSTANTS WITH CONST ASSERTIONS (2025 TYPESCRIPT)
73
+ // =============================================================================
74
+
75
+ /**
76
+ * Atom default values with const assertions for immutability
77
+ */
78
+ export const ATOM_DEFAULTS = {
79
+ POSITION: '',
80
+ WALLET_ADDRESS: '',
81
+ ISOTOPE: 'C',
82
+ TOKEN: 'USER',
83
+ VALUE: null,
84
+ BATCH_ID: null,
85
+ META_TYPE: null,
86
+ META_ID: null,
87
+ META: [],
88
+ OTS_FRAGMENT: null,
89
+ INDEX: null,
90
+ VERSION: PROTOCOL_CONFIG.DEFAULT_SDK_VERSION
91
+ } as const satisfies {
92
+ readonly POSITION: string
93
+ readonly WALLET_ADDRESS: string
94
+ readonly ISOTOPE: AtomIsotope
95
+ readonly TOKEN: string
96
+ readonly VALUE: null
97
+ readonly BATCH_ID: null
98
+ readonly META_TYPE: null
99
+ readonly META_ID: null
100
+ readonly META: readonly []
101
+ readonly OTS_FRAGMENT: null
102
+ readonly INDEX: null
103
+ readonly VERSION: number
104
+ }
105
+
106
+ /**
107
+ * Atom validation constants with const assertions
108
+ */
109
+ export const ATOM_VALIDATION = {
110
+ MIN_INDEX: 0,
111
+ MAX_INDEX: Number.MAX_SAFE_INTEGER,
112
+ REQUIRED_FIELDS: ['position', 'walletAddress', 'isotope'] as const,
113
+ OPTIONAL_FIELDS: ['token', 'value', 'batchId', 'metaType', 'metaId', 'meta'] as const
114
+ } as const
115
+
116
+ // =============================================================================
117
+ // ATOM INTERFACE AND TYPES
118
+ // =============================================================================
119
+
120
+ export interface AtomMetaData {
121
+ readonly key: string
122
+ readonly value: string | number | boolean | null
123
+ readonly [additionalProps: string]: unknown
124
+ }
125
+
126
+ export interface AtomCreationParams extends AtomParams {
127
+ version?: number
128
+ }
129
+
130
+ export interface AtomHashableData {
131
+ position: string
132
+ walletAddress: string
133
+ isotope: AtomIsotope
134
+ token: string
135
+ value: string | null
136
+ batchId: string | null
137
+ metaType: string | null
138
+ metaId: string | null
139
+ meta: AtomMetaData[] | null
140
+ createdAt: string
141
+ index: number
142
+ }
143
+
144
+ // =============================================================================
145
+ // ATOM CLASS IMPLEMENTATION
146
+ // =============================================================================
147
+
148
+ /**
149
+ * Atom - Represents a single, atomic operation in the distributed ledger
150
+ * Maintains full compatibility with JavaScript SDK implementation
151
+ */
152
+ export default class Atom {
153
+ // Core properties matching JS SDK exactly
154
+ public position: string
155
+ public walletAddress: WalletAddress | string
156
+ public isotope: AtomIsotope
157
+ public token: TokenSlug | string
158
+ public value: string | number | null
159
+ public batchId: BatchId | string | null
160
+ public metaType: MetaType | string | null
161
+ public metaId: MetaId | string | null
162
+ public meta: AtomMetaData[]
163
+ public otsFragment: string | null
164
+ public index: number | null
165
+ public createdAt: string
166
+ public version: number
167
+
168
+ /**
169
+ * Create a new Atom instance
170
+ * Constructor signature matches JavaScript SDK exactly
171
+ */
172
+ constructor({
173
+ position = ATOM_DEFAULTS.POSITION,
174
+ walletAddress = ATOM_DEFAULTS.WALLET_ADDRESS as any,
175
+ isotope = ATOM_DEFAULTS.ISOTOPE as AtomIsotope,
176
+ token = ATOM_DEFAULTS.TOKEN,
177
+ value = ATOM_DEFAULTS.VALUE,
178
+ batchId = ATOM_DEFAULTS.BATCH_ID,
179
+ metaType = ATOM_DEFAULTS.META_TYPE,
180
+ metaId = ATOM_DEFAULTS.META_ID,
181
+ meta = null,
182
+ otsFragment = ATOM_DEFAULTS.OTS_FRAGMENT,
183
+ index = ATOM_DEFAULTS.INDEX,
184
+ createdAt = null,
185
+ version = ATOM_DEFAULTS.VERSION
186
+ }: AtomCreationParams = {}) {
187
+
188
+ // Use const assertion values with nullish coalescing (2025 pattern)
189
+ this.position = position ?? ATOM_DEFAULTS.POSITION
190
+ this.walletAddress = walletAddress ?? ATOM_DEFAULTS.WALLET_ADDRESS
191
+ this.isotope = isotope ?? ATOM_DEFAULTS.ISOTOPE
192
+ this.token = token ?? ATOM_DEFAULTS.TOKEN
193
+ this.value = value
194
+ this.batchId = batchId
195
+ this.metaType = metaType
196
+ this.metaId = metaId
197
+ this.meta = meta ? [...meta] : [...ATOM_DEFAULTS.META]
198
+ this.otsFragment = otsFragment
199
+ this.index = index
200
+ this.version = version ?? ATOM_DEFAULTS.VERSION
201
+ // CRITICAL: Use provided createdAt or generate timestamp in milliseconds as string (Implementation Guide requirement)
202
+ this.createdAt = createdAt || String(+new Date())
203
+ }
204
+
205
+ // =============================================================================
206
+ // INSTANCE METHODS - MATCH JAVASCRIPT SDK
207
+ // =============================================================================
208
+
209
+ /**
210
+ * Get aggregated metadata for this atom
211
+ * Matches JavaScript SDK Meta.aggregateMeta functionality
212
+ */
213
+ public aggregatedMeta(): Record<string, unknown> {
214
+ const aggregated: Record<string, unknown> = {}
215
+
216
+ for (const metaItem of this.meta) {
217
+ if (metaItem && typeof metaItem === 'object' && 'key' in metaItem) {
218
+ aggregated[metaItem.key] = metaItem.value
219
+ }
220
+ }
221
+
222
+ return aggregated
223
+ }
224
+
225
+ /**
226
+ * Get values that will be used for hashing
227
+ * Must match JavaScript SDK getHashableValues exactly - returns array of strings!
228
+ */
229
+ public getHashableValues(): string[] {
230
+ const hashableValues: string[] = []
231
+ for (const property of Atom.getHashableProps()) {
232
+ const value = (this as any)[property]
233
+
234
+ // All nullable values are not hashed (only custom keys)
235
+ if (value === null && !['position', 'walletAddress'].includes(property)) {
236
+ continue
237
+ }
238
+
239
+ // Hashing individual meta keys and values
240
+ if (property === 'meta') {
241
+ for (const meta of value) {
242
+ if (typeof meta.value !== 'undefined' && meta.value !== null) {
243
+ hashableValues.push(String(meta.key))
244
+ hashableValues.push(String(meta.value))
245
+ }
246
+ }
247
+ } else {
248
+ // Default value
249
+ hashableValues.push(value === null ? '' : String(value))
250
+ }
251
+ }
252
+ return hashableValues
253
+ }
254
+
255
+ /**
256
+ * Get structured data for JSON serialization
257
+ * Separate from hashable values to maintain proper typing
258
+ */
259
+ public getStructuredData(): AtomHashableData {
260
+ return {
261
+ position: this.position || '',
262
+ walletAddress: this.walletAddress?.toString() || '',
263
+ isotope: this.isotope,
264
+ token: this.token?.toString() || '',
265
+ value: this.value !== null ? this.value.toString() : null,
266
+ batchId: this.batchId?.toString() || null,
267
+ metaType: this.metaType?.toString() || null,
268
+ metaId: this.metaId?.toString() || null,
269
+ meta: this.meta.length > 0 ? [...this.meta] : null,
270
+ createdAt: this.createdAt,
271
+ index: this.index !== null ? this.index : 0
272
+ }
273
+ }
274
+
275
+ /**
276
+ * Returns JSON-ready object for cross-SDK compatibility (2025 TS best practices)
277
+ *
278
+ * Provides clean serialization of atomic operations with optional OTS fragments.
279
+ * Follows 2025 TypeScript best practices with proper type safety and validation.
280
+ *
281
+ * @param options - Serialization options
282
+ * @param options.includeOtsFragments - Include OTS signature fragments (default: true)
283
+ * @param options.validateFields - Validate required fields (default: false)
284
+ * @return JSON-serializable object
285
+ * @throws Error if atom is in invalid state for serialization
286
+ */
287
+ public toJSON(options: {
288
+ includeOtsFragments?: boolean
289
+ validateFields?: boolean
290
+ } = {}): AtomHashableData & { otsFragment?: string | null } {
291
+ const {
292
+ includeOtsFragments = true,
293
+ validateFields = false
294
+ } = options
295
+
296
+ try {
297
+ // Validate required fields if requested
298
+ if (validateFields) {
299
+ const requiredFields = ['position', 'walletAddress', 'isotope', 'token'] as const
300
+ for (const field of requiredFields) {
301
+ if (!this[field]) {
302
+ throw new Error(`Required field '${field}' is missing or empty`)
303
+ }
304
+ }
305
+ }
306
+
307
+ // Core atom properties (always included) - use structured data for JSON
308
+ const structuredData = this.getStructuredData()
309
+ const serialized: AtomHashableData & { otsFragment?: string | null } = {
310
+ ...structuredData
311
+ }
312
+
313
+ // Optional OTS fragments (can be large, so optional)
314
+ if (includeOtsFragments && this.otsFragment) {
315
+ serialized.otsFragment = this.otsFragment
316
+ }
317
+
318
+ return serialized
319
+
320
+ } catch (error) {
321
+ throw new Error(`Atom serialization failed: ${(error as Error).message}`)
322
+ }
323
+ }
324
+
325
+ /**
326
+ * Create a deep copy of this atom
327
+ */
328
+ public clone(): Atom {
329
+ return new Atom({
330
+ position: this.position,
331
+ walletAddress: this.walletAddress as any,
332
+ isotope: this.isotope,
333
+ token: this.token,
334
+ value: this.value,
335
+ batchId: this.batchId,
336
+ metaType: this.metaType,
337
+ metaId: this.metaId,
338
+ meta: this.meta.map(m => ({ ...m })) as any,
339
+ otsFragment: this.otsFragment,
340
+ index: this.index,
341
+ version: this.version
342
+ })
343
+ }
344
+
345
+ /**
346
+ * Check if atom is valid for inclusion in molecules
347
+ * TypeScript 2025: Uses exhaustive type guards for compile-time completeness
348
+ */
349
+ public isValid(): boolean {
350
+ // TypeScript 2025: Enhanced validation with type guards
351
+ if (!isPosition(this.position) || !isWalletAddress(this.walletAddress)) {
352
+ return false
353
+ }
354
+
355
+ // Exhaustive isotope validation with compile-time completeness
356
+ return handleIsotope(this.isotope, {
357
+ V: () => this.value !== null && !isNaN(Number(this.value)), // Value atoms must have numeric value
358
+ M: () => Boolean(this.metaType && this.metaId), // Meta atoms must have metaType and metaId
359
+ C: () => true, // Continue atoms are always valid if position/address present
360
+ U: () => true, // User atoms
361
+ T: () => true, // Token atoms
362
+ I: () => true, // Identity atoms
363
+ R: () => true, // Rule atoms
364
+ B: () => true, // Buffer atoms
365
+ F: () => true // Fuse atoms
366
+ })
367
+ }
368
+
369
+ // =============================================================================
370
+ // STATIC METHODS - MATCH JAVASCRIPT SDK EXACTLY
371
+ // =============================================================================
372
+
373
+ /**
374
+ * Get properties that should be included in hash calculation
375
+ * MUST match JavaScript SDK implementation exactly
376
+ */
377
+ static getHashableProps(): string[] {
378
+ return [
379
+ 'position',
380
+ 'walletAddress',
381
+ 'isotope',
382
+ 'token',
383
+ 'value',
384
+ 'batchId',
385
+ 'metaType',
386
+ 'metaId',
387
+ 'meta',
388
+ 'createdAt'
389
+ ]
390
+ // NOTE: 'index' excluded - matches JavaScript canonical exactly
391
+ // Index is for atom ordering, not hash calculation
392
+ }
393
+
394
+ /**
395
+ * Get properties for unclaimed shadow wallets
396
+ * Matches JavaScript SDK getUnclaimedProps
397
+ */
398
+ static getUnclaimedProps(): string[] {
399
+ return [
400
+ 'position',
401
+ 'walletAddress',
402
+ 'isotope',
403
+ 'token',
404
+ 'value',
405
+ 'batchId',
406
+ 'metaType',
407
+ 'metaId',
408
+ 'meta',
409
+ 'createdAt'
410
+ ]
411
+ }
412
+
413
+ /**
414
+ * Create atom from parameters - factory method
415
+ * Matches JavaScript SDK Atom.create static method
416
+ */
417
+ static create(params: AtomCreationParams): Atom {
418
+ return new Atom(params)
419
+ }
420
+
421
+ /**
422
+ * Creates an Atom instance from JSON data (2025 TS best practices)
423
+ *
424
+ * Handles cross-SDK atom deserialization with robust error handling.
425
+ * Essential for reconstructing atoms from other SDK implementations.
426
+ *
427
+ * @param json - JSON string or object to deserialize
428
+ * @param options - Deserialization options
429
+ * @param options.validateStructure - Validate required fields (default: true)
430
+ * @param options.strictMode - Strict validation mode (default: false)
431
+ * @return Reconstructed atom instance
432
+ * @throws Error if JSON is invalid or required fields are missing
433
+ */
434
+ static fromJSON(json: string | Record<string, unknown>, options: {
435
+ validateStructure?: boolean
436
+ strictMode?: boolean
437
+ } = {}): Atom {
438
+ const {
439
+ validateStructure = true,
440
+ strictMode = false
441
+ } = options
442
+
443
+ try {
444
+ // Parse JSON safely
445
+ const data = typeof json === 'string' ? JSON.parse(json) : json
446
+
447
+ // Validate required fields in strict mode
448
+ if (strictMode || validateStructure) {
449
+ const requiredFields = ['position', 'walletAddress', 'isotope', 'token'] as const
450
+ for (const field of requiredFields) {
451
+ if (!data[field]) {
452
+ throw new Error(`Required field '${field}' is missing or empty`)
453
+ }
454
+ }
455
+ }
456
+
457
+ // Create atom instance with required fields
458
+ const atom = new Atom({
459
+ position: data.position as string,
460
+ walletAddress: data.walletAddress as any,
461
+ isotope: data.isotope as AtomIsotope,
462
+ token: data.token as string,
463
+ value: data.value as string | number | null,
464
+ batchId: data.batchId as string | null,
465
+ metaType: data.metaType as string | null,
466
+ metaId: data.metaId as string | null,
467
+ meta: data.meta as any,
468
+ index: data.index as number | null,
469
+ version: data.version as number
470
+ })
471
+
472
+ // Set additional properties that may not be in constructor
473
+ if (data.otsFragment) {
474
+ atom.otsFragment = data.otsFragment as string
475
+ }
476
+ if (data.createdAt) {
477
+ atom.createdAt = data.createdAt as string
478
+ }
479
+
480
+ return atom
481
+
482
+ } catch (error) {
483
+ throw new Error(`Atom deserialization failed: ${(error as Error).message}`)
484
+ }
485
+ }
486
+
487
+ /**
488
+ * Convert JSON object back to Atom instance
489
+ * Matches JavaScript SDK jsonToObject method - kept for compatibility
490
+ */
491
+ static jsonToObject(jsonData: Record<string, unknown>): Atom {
492
+ return Atom.fromJSON(jsonData)
493
+ }
494
+
495
+ /**
496
+ * Hash multiple atoms to create molecular hash
497
+ * MUST match JavaScript SDK algorithm exactly for cross-platform compatibility
498
+ *
499
+ * CRITICAL: This implementation must exactly mirror the JavaScript SDK approach:
500
+ * 1. Sort atoms by index property
501
+ * 2. For each sorted atom: ADD number of atoms to hashableValues array
502
+ * 3. CONCATENATE all atom's hashable values to the array
503
+ * 4. Iterate through hashableValues array and update SHAKE256 sponge
504
+ */
505
+ static hashAtoms({ atoms }: { atoms: Atom[] }): string {
506
+ if (!atoms || atoms.length === 0) {
507
+ return ''
508
+ }
509
+
510
+ // Step 1: Sort atoms by index to ensure deterministic hashing (matches JS SDK)
511
+ const atomList = Atom.sortAtoms(atoms)
512
+ const numberOfAtoms = String(atoms.length)
513
+ let hashableValues: string[] = []
514
+
515
+ // Step 2: Build hashableValues array exactly like JavaScript SDK
516
+ for (const atom of atomList) {
517
+ // Add number of atoms (matching JS SDK comment: "Add number of atoms (???)")
518
+ hashableValues.push(numberOfAtoms)
519
+
520
+ // Add atom's properties - concatenate the array returned by getHashableValues
521
+ hashableValues = hashableValues.concat(atom.getHashableValues())
522
+ }
523
+
524
+ // Step 3: Create molecular hash using SHAKE256 exactly like JS SDK (iterative updates)
525
+ const molecularSponge = new JsSHA('SHAKE256', 'TEXT')
526
+
527
+ // CRITICAL FIX: Use iterative updates like JavaScript, not string concatenation
528
+ for (const hashableValue of hashableValues) {
529
+ molecularSponge.update(hashableValue) // Match JavaScript SDK exactly
530
+ }
531
+
532
+ // Step 4: Get hex hash (256 bits = 64 hex chars) and convert to base17
533
+ const hexHash = molecularSponge.getHash('HEX', { outputLen: 256 })
534
+
535
+ // Step 5: Convert hex hash to base17 format (Implementation Guide requirement)
536
+ return convertToBase17(hexHash.toLowerCase())
537
+ }
538
+
539
+ /**
540
+ * Sort atoms by index
541
+ * Matches JavaScript SDK sortAtoms method
542
+ */
543
+ static sortAtoms(atoms: Atom[]): Atom[] {
544
+ return [...atoms].sort((a, b) => {
545
+ const indexA = a.index !== null ? a.index : 0
546
+ const indexB = b.index !== null ? b.index : 0
547
+ return indexA - indexB
548
+ })
549
+ }
550
+
551
+ /**
552
+ * Generate next atom index for a collection
553
+ * Matches JavaScript SDK generateNextAtomIndex method
554
+ */
555
+ static generateNextAtomIndex(atoms: Atom[]): number {
556
+ if (!atoms || atoms.length === 0) {
557
+ return 0
558
+ }
559
+
560
+ const indices = atoms
561
+ .map(atom => atom.index !== null ? atom.index : 0)
562
+ .filter(index => typeof index === 'number')
563
+
564
+ return indices.length > 0 ? Math.max(...indices) + 1 : 0
565
+ }
566
+
567
+ /**
568
+ * Filter atoms by isotope(s)
569
+ * Matches JavaScript SDK isotopeFilter method
570
+ */
571
+ static isotopeFilter(isotopes: AtomIsotope | AtomIsotope[], atoms: Atom[]): Atom[] {
572
+ const targetIsotopes = Array.isArray(isotopes) ? isotopes : [isotopes]
573
+ return atoms.filter(atom => targetIsotopes.includes(atom.isotope))
574
+ }
575
+
576
+ // =============================================================================
577
+ // VALIDATION AND UTILITY METHODS
578
+ // =============================================================================
579
+
580
+ /**
581
+ * Validate atom data structure
582
+ */
583
+ static validateAtom(atom: unknown): atom is Atom {
584
+ if (!atom || typeof atom !== 'object') {
585
+ return false
586
+ }
587
+
588
+ const atomObj = atom as Record<string, unknown>
589
+
590
+ return typeof atomObj.position === 'string' &&
591
+ typeof atomObj.walletAddress === 'string' &&
592
+ typeof atomObj.isotope === 'string' &&
593
+ typeof atomObj.token === 'string' &&
594
+ (atomObj.value === null || typeof atomObj.value === 'string' || typeof atomObj.value === 'number') &&
595
+ Array.isArray(atomObj.meta)
596
+ }
597
+
598
+ /**
599
+ * Check if atom is a specific isotope type
600
+ */
601
+ static isIsotope(atom: Atom, isotope: AtomIsotope): boolean {
602
+ return atom.isotope === isotope
603
+ }
604
+
605
+ /**
606
+ * Get all unique isotopes from atom collection
607
+ */
608
+ static getUniqueIsotopes(atoms: Atom[]): AtomIsotope[] {
609
+ const isotopes = new Set<AtomIsotope>()
610
+ for (const atom of atoms) {
611
+ isotopes.add(atom.isotope)
612
+ }
613
+ return Array.from(isotopes)
614
+ }
615
+
616
+ /**
617
+ * Calculate total value for value atoms
618
+ */
619
+ static calculateTotalValue(atoms: Atom[]): number {
620
+ return atoms
621
+ .filter(atom => atom.isotope === 'V' && atom.value !== null)
622
+ .reduce((total, atom) => total + Number(atom.value), 0)
623
+ }
624
+
625
+ /**
626
+ * Group atoms by isotope
627
+ */
628
+ static groupByIsotope(atoms: Atom[]): Record<AtomIsotope, Atom[]> {
629
+ const groups: Partial<Record<AtomIsotope, Atom[]>> = {}
630
+
631
+ for (const atom of atoms) {
632
+ if (!groups[atom.isotope]) {
633
+ groups[atom.isotope] = []
634
+ }
635
+ groups[atom.isotope]!.push(atom)
636
+ }
637
+
638
+ return groups as Record<AtomIsotope, Atom[]>
639
+ }
640
+ }
641
+
642
+ // =============================================================================
643
+ // TYPE EXPORTS
644
+ // =============================================================================
645
+
646
+ // Types are already exported from the types package