@wishknish/knishio-client-ts 0.7.4 → 0.7.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.
Files changed (43) hide show
  1. package/dist/index.cjs +2702 -1033
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.cts +339 -106
  4. package/dist/index.d.ts +339 -106
  5. package/dist/index.iife.js +22052 -19964
  6. package/dist/index.iife.js.map +1 -1
  7. package/dist/index.js +2699 -1034
  8. package/dist/index.js.map +1 -1
  9. package/package.json +10 -4
  10. package/src/AuthToken.ts +8 -2
  11. package/src/KnishIOClient.ts +346 -51
  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 +607 -141
  17. package/src/core/Wallet.ts +19 -24
  18. package/src/index.ts +8 -0
  19. package/src/instance/rules/Rule.ts +128 -19
  20. package/src/libraries/CheckMolecule.ts +88 -1
  21. package/src/libraries/crypto.ts +14 -30
  22. package/src/libraries/strings.ts +26 -25
  23. package/src/mutation/MutationAppendRequest.ts +52 -0
  24. package/src/mutation/MutationCreateRule.ts +2 -2
  25. package/src/mutation/MutationPeering.ts +37 -0
  26. package/src/mutation/MutationRequestAuthorization.ts +3 -3
  27. package/src/query/QueryActiveSession.ts +0 -1
  28. package/src/query/QueryContinuId.ts +8 -0
  29. package/src/query/QueryMetaTypeViaMolecule.ts +221 -0
  30. package/src/query/QueryWalletList.ts +2 -2
  31. package/src/response/ResponseAppendRequest.ts +17 -0
  32. package/src/response/ResponseBalance.ts +8 -3
  33. package/src/response/ResponseContinuId.ts +1 -1
  34. package/src/response/ResponseMetaType.ts +32 -19
  35. package/src/response/ResponseMetaTypeViaMolecule.ts +224 -0
  36. package/src/response/ResponsePeering.ts +17 -0
  37. package/src/response/ResponsePolicy.ts +2 -3
  38. package/src/response/ResponseProposeMolecule.ts +0 -1
  39. package/src/response/ResponseRequestAuthorization.ts +32 -2
  40. package/src/response/ResponseRequestAuthorizationGuest.ts +25 -1
  41. package/src/response/ResponseWalletList.ts +1 -1
  42. package/src/types/guards.ts +18 -5
  43. package/src/types/index.ts +3 -3
@@ -0,0 +1,221 @@
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 Query from './Query'
50
+ import ResponseMetaTypeViaMolecule from '../response/ResponseMetaTypeViaMolecule'
51
+ import { gql } from '@urql/core'
52
+ import type { GraphQLClient } from '../types/graphql'
53
+ import type KnishIOClient from '../KnishIOClient'
54
+
55
+ /**
56
+ * Query for retrieving Meta Asset information via Molecule data.
57
+ *
58
+ * Unlike QueryMetaTypeViaAtom, this query includes molecule data with atoms
59
+ * (including otsFragment and metasJson) for integrity verification and
60
+ * client-side metadata extraction.
61
+ */
62
+ export default class QueryMetaTypeViaMolecule extends Query {
63
+ /**
64
+ * Create new QueryMetaTypeViaMolecule instance
65
+ */
66
+ constructor(graphQLClient: GraphQLClient, knishIOClient: KnishIOClient) {
67
+ super(graphQLClient, knishIOClient)
68
+
69
+ this.$__query = gql`query ($metaTypes: [String!], $metaIds: [String!], $values: [String!], $keys: [String!], $latest: Boolean, $filter: [MetaFilter!], $queryArgs: QueryArgs, $countBy: String, $atomValues: [String!], $cellSlugs: [String!] ) {
70
+ MetaTypeViaAtom(
71
+ metaTypes: $metaTypes
72
+ metaIds: $metaIds
73
+ atomValues: $atomValues
74
+ cellSlugs: $cellSlugs
75
+ filter: $filter,
76
+ latest: $latest,
77
+ queryArgs: $queryArgs
78
+ countBy: $countBy
79
+ ) {
80
+ metaType,
81
+ instanceCount {
82
+ key,
83
+ value
84
+ },
85
+ instances {
86
+ metaType,
87
+ metaId,
88
+ createdAt,
89
+ metas( values: $values, keys: $keys ) {
90
+ molecularHash,
91
+ position,
92
+ key,
93
+ value,
94
+ createdAt
95
+ },
96
+ molecule {
97
+ molecularHash,
98
+ bundleHash,
99
+ cellSlug,
100
+ status,
101
+ createdAt,
102
+ atoms {
103
+ position,
104
+ walletAddress,
105
+ isotope,
106
+ tokenSlug,
107
+ value,
108
+ batchId,
109
+ metaType,
110
+ metaId,
111
+ index,
112
+ createdAt,
113
+ otsFragment,
114
+ metasJson
115
+ }
116
+ }
117
+ },
118
+ paginatorInfo {
119
+ currentPage,
120
+ total
121
+ }
122
+ }
123
+ }`
124
+ }
125
+
126
+ /**
127
+ * Builds a GraphQL-friendly variables object based on input fields
128
+ */
129
+ static createVariables({
130
+ metaType = null,
131
+ metaId = null,
132
+ key = null,
133
+ value = null,
134
+ keys = null,
135
+ values = null,
136
+ atomValues = null,
137
+ latest = null,
138
+ filter = null,
139
+ queryArgs = null,
140
+ countBy = null,
141
+ cellSlug = null
142
+ }: {
143
+ metaType?: string | string[] | null
144
+ metaId?: string | string[] | null
145
+ key?: string | null
146
+ value?: string | null
147
+ keys?: string[] | null
148
+ values?: string[] | null
149
+ atomValues?: string[] | null
150
+ latest?: boolean | null
151
+ filter?: any[] | null
152
+ queryArgs?: any | null
153
+ countBy?: string | null
154
+ cellSlug?: string | string[] | null
155
+ } = {}): Record<string, any> {
156
+ const variables: Record<string, any> = {}
157
+
158
+ if (atomValues) {
159
+ variables.atomValues = atomValues
160
+ }
161
+
162
+ if (keys) {
163
+ variables.keys = keys
164
+ }
165
+
166
+ if (values) {
167
+ variables.values = values
168
+ }
169
+
170
+ if (metaType) {
171
+ variables.metaTypes = typeof metaType === 'string' ? [metaType] : metaType
172
+ }
173
+
174
+ if (metaId) {
175
+ variables.metaIds = typeof metaId === 'string' ? [metaId] : metaId
176
+ }
177
+
178
+ if (cellSlug) {
179
+ variables.cellSlugs = typeof cellSlug === 'string' ? [cellSlug] : cellSlug
180
+ }
181
+
182
+ if (countBy) {
183
+ variables.countBy = countBy
184
+ }
185
+
186
+ if (filter) {
187
+ variables.filter = filter
188
+ }
189
+
190
+ if (key && value) {
191
+ variables.filter = variables.filter || []
192
+ variables.filter.push({
193
+ key,
194
+ value,
195
+ comparison: '='
196
+ })
197
+ }
198
+
199
+ variables.latest = latest === true
200
+
201
+ if (queryArgs) {
202
+ if (typeof queryArgs.limit === 'undefined' || queryArgs.limit === 0) {
203
+ queryArgs.limit = '*'
204
+ }
205
+
206
+ variables.queryArgs = queryArgs
207
+ }
208
+
209
+ return variables
210
+ }
211
+
212
+ /**
213
+ * Returns a Response object
214
+ */
215
+ override createResponse(json: any): ResponseMetaTypeViaMolecule {
216
+ return new ResponseMetaTypeViaMolecule({
217
+ query: this,
218
+ json
219
+ })
220
+ }
221
+ }
@@ -64,8 +64,8 @@ export default class QueryWalletList extends Query {
64
64
  constructor(graphQLClient: GraphQLClient, knishIOClient: KnishIOClient) {
65
65
  super(graphQLClient, knishIOClient)
66
66
 
67
- this.$__query = gql`query( $bundleHash: String, $tokenSlug: String ) {
68
- Wallet( bundleHash: $bundleHash, tokenSlug: $tokenSlug ) {
67
+ this.$__query = gql`query( $bundleHash: String, $token: String ) {
68
+ Wallet( bundleHash: $bundleHash, token: $token ) {
69
69
  address,
70
70
  bundleHash,
71
71
  token {
@@ -0,0 +1,17 @@
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 ResponseProposeMolecule from './ResponseProposeMolecule'
10
+
11
+ /**
12
+ * Response for append requests via A-isotope
13
+ * Matches JavaScript SDK ResponseAppendRequest implementation exactly
14
+ */
15
+ export default class ResponseAppendRequest extends ResponseProposeMolecule {
16
+
17
+ }
@@ -77,7 +77,10 @@ export default class ResponseBalance extends Response {
77
77
  * Matches JavaScript SDK payload() method exactly
78
78
  */
79
79
  override payload(): Wallet | null {
80
- const walletData = this.data() as any
80
+ const balanceData = this.data() as any
81
+
82
+ // Balance query returns an array, get the first result
83
+ const walletData = Array.isArray(balanceData) ? balanceData[0] : balanceData
81
84
 
82
85
  if (!walletData || !walletData.bundleHash || !walletData.tokenSlug) {
83
86
  return null
@@ -102,7 +105,9 @@ export default class ResponseBalance extends Response {
102
105
  }): Wallet {
103
106
  let wallet: Wallet
104
107
 
105
- if (data.position === null || typeof data.position === 'undefined') {
108
+ // Handle null, undefined, or empty string positions
109
+ // Empty positions occur for shadow wallets created before this fix
110
+ if (!data.position) {
106
111
  wallet = Wallet.create({
107
112
  bundle: data.bundleHash,
108
113
  token: data.tokenSlug,
@@ -144,7 +149,7 @@ export default class ResponseBalance extends Response {
144
149
  }
145
150
 
146
151
  // Set balance and other properties
147
- wallet.balance = Number(data.amount || 0)
152
+ wallet.balance = String(data.amount != null ? data.amount : 0)
148
153
  wallet.pubkey = data.pubkey
149
154
  wallet.createdAt = data.createdAt
150
155
 
@@ -93,7 +93,7 @@ export default class ResponseContinuId extends Response {
93
93
  wallet.batchId = continuId.batchId
94
94
  wallet.characters = continuId.characters
95
95
  wallet.pubkey = continuId.pubkey
96
- wallet.balance = continuId.amount * 1.0
96
+ wallet.balance = String(continuId.amount != null ? continuId.amount : 0)
97
97
  }
98
98
 
99
99
  return wallet
@@ -72,37 +72,50 @@ export default class ResponseMetaType extends Response {
72
72
  })
73
73
  }
74
74
 
75
+ /**
76
+ * Extracts payload from metaData object
77
+ */
78
+ private extractPayload(metaData: any): any {
79
+ return {
80
+ instances: metaData?.instances || [],
81
+ instanceCount: metaData?.instanceCount || [],
82
+ paginatorInfo: metaData?.paginatorInfo || {}
83
+ }
84
+ }
85
+
75
86
  /**
76
87
  * Returns meta type instance results
77
- * Matches JavaScript SDK payload method exactly
88
+ * Handles various response formats from server:
89
+ * - Array of results (normal case)
90
+ * - Single object (filtered queries)
91
+ * - null/undefined (no results)
92
+ * - Empty array (no matching data)
78
93
  */
79
94
  override payload(): any | null {
80
95
  const metaTypeData = this.data()
81
96
 
82
- if (!metaTypeData || metaTypeData.length === 0) {
97
+ // Handle null/undefined responses
98
+ if (metaTypeData === null || metaTypeData === undefined) {
83
99
  return null
84
100
  }
85
101
 
86
- const response: any = {
87
- instances: {},
88
- instanceCount: {},
89
- paginatorInfo: {}
90
- }
91
-
92
- const metaData = metaTypeData.pop()
93
-
94
- if (metaData.instances) {
95
- response.instances = metaData.instances
96
- }
97
-
98
- if (metaData.instanceCount) {
99
- response.instanceCount = metaData.instanceCount
102
+ // Handle array response (expected format)
103
+ if (Array.isArray(metaTypeData)) {
104
+ if (metaTypeData.length === 0) {
105
+ // Return empty structure instead of null for empty results
106
+ return this.extractPayload(null)
107
+ }
108
+ // Get last element without mutating the array
109
+ const metaData = metaTypeData[metaTypeData.length - 1]
110
+ return this.extractPayload(metaData)
100
111
  }
101
112
 
102
- if (metaData.paginatorInfo) {
103
- response.paginatorInfo = metaData.paginatorInfo
113
+ // Handle single object response (some filtered queries)
114
+ if (typeof metaTypeData === 'object') {
115
+ return this.extractPayload(metaTypeData)
104
116
  }
105
117
 
106
- return response
118
+ // Unknown format, return null
119
+ return null
107
120
  }
108
121
  }
@@ -0,0 +1,224 @@
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 type Query from '../query/Query'
50
+ import Response from './Response'
51
+ import CheckMolecule from '../libraries/CheckMolecule'
52
+
53
+ /**
54
+ * Response for MetaType queries via Molecule data.
55
+ *
56
+ * Instead of relying solely on the instance-level `metas` field,
57
+ * this response can extract metadata from molecule atoms' `metasJson`,
58
+ * and provides cryptographic integrity verification via `verifyIntegrity()`.
59
+ */
60
+ export default class ResponseMetaTypeViaMolecule extends Response {
61
+ /**
62
+ * Class constructor
63
+ */
64
+ constructor({
65
+ query,
66
+ json
67
+ }: {
68
+ query: Query
69
+ json: any
70
+ }) {
71
+ super({
72
+ query,
73
+ json,
74
+ dataKey: 'data.MetaTypeViaAtom'
75
+ })
76
+ }
77
+
78
+ /**
79
+ * Extracts metas from a molecule's atoms' metasJson for a specific instance.
80
+ * Filters atoms by matching metaType and metaId, then parses metasJson.
81
+ */
82
+ static extractMetasFromMolecule(
83
+ molecule: any,
84
+ metaType: string,
85
+ metaId: string
86
+ ): Array<{
87
+ molecularHash: string
88
+ position: string
89
+ key: string
90
+ value: string
91
+ createdAt: string
92
+ }> {
93
+ if (!molecule || !molecule.atoms) {
94
+ return []
95
+ }
96
+
97
+ const metas: Array<{
98
+ molecularHash: string
99
+ position: string
100
+ key: string
101
+ value: string
102
+ createdAt: string
103
+ }> = []
104
+
105
+ for (const atom of molecule.atoms) {
106
+ if (atom.metaType !== metaType || atom.metaId !== metaId) {
107
+ continue
108
+ }
109
+
110
+ if (!atom.metasJson) {
111
+ continue
112
+ }
113
+
114
+ let parsed: any
115
+ try {
116
+ parsed = JSON.parse(atom.metasJson)
117
+ if (!Array.isArray(parsed)) {
118
+ continue
119
+ }
120
+ } catch (e) {
121
+ continue
122
+ }
123
+
124
+ for (const entry of parsed) {
125
+ metas.push({
126
+ molecularHash: molecule.molecularHash,
127
+ position: atom.position,
128
+ key: entry.key,
129
+ value: entry.value,
130
+ createdAt: atom.createdAt
131
+ })
132
+ }
133
+ }
134
+
135
+ return metas
136
+ }
137
+
138
+ /**
139
+ * Returns meta type instance results with metas synthesized from molecule data.
140
+ * Produces the same payload format as ResponseMetaType and ResponseMetaTypeViaAtom:
141
+ * { instances, instanceCount, paginatorInfo }
142
+ */
143
+ override payload(): any | null {
144
+ const metaTypeData = this.data()
145
+
146
+ if (!metaTypeData || metaTypeData.length === 0) {
147
+ return null
148
+ }
149
+
150
+ const response: any = {
151
+ instances: {},
152
+ instanceCount: {},
153
+ paginatorInfo: {}
154
+ }
155
+
156
+ const metaData = metaTypeData.pop()
157
+
158
+ if (metaData.instances) {
159
+ response.instances = metaData.instances.map((instance: any) => {
160
+ // Prefer server-filtered metas when available
161
+ let metas = instance.metas
162
+ if (!metas || metas.length === 0) {
163
+ // Fallback: synthesize from molecule atoms' metasJson
164
+ metas = ResponseMetaTypeViaMolecule.extractMetasFromMolecule(
165
+ instance.molecule,
166
+ instance.metaType,
167
+ instance.metaId
168
+ )
169
+ }
170
+
171
+ return {
172
+ ...instance,
173
+ metas
174
+ }
175
+ })
176
+ }
177
+
178
+ if (metaData.instanceCount) {
179
+ response.instanceCount = metaData.instanceCount
180
+ }
181
+
182
+ if (metaData.paginatorInfo) {
183
+ response.paginatorInfo = metaData.paginatorInfo
184
+ }
185
+
186
+ return response
187
+ }
188
+
189
+ /**
190
+ * Verifies the cryptographic integrity of all molecules associated
191
+ * with meta instances in this response.
192
+ */
193
+ verifyIntegrity(): {
194
+ verified: boolean
195
+ molecules: Array<{
196
+ molecularHash: string | null
197
+ verified: boolean
198
+ error: string | null
199
+ }>
200
+ } {
201
+ const results: Array<{
202
+ molecularHash: string | null
203
+ verified: boolean
204
+ error: string | null
205
+ }> = []
206
+ const metaTypeData = this.data()
207
+
208
+ if (!metaTypeData || metaTypeData.length === 0) {
209
+ return { verified: true, molecules: results }
210
+ }
211
+
212
+ const instances = metaTypeData[metaTypeData.length - 1]?.instances || []
213
+
214
+ for (const instance of instances) {
215
+ if (!instance.molecule) continue
216
+ results.push(CheckMolecule.verifyFromServerData(instance.molecule))
217
+ }
218
+
219
+ return {
220
+ verified: results.length === 0 || results.every((r: any) => r.verified),
221
+ molecules: results
222
+ }
223
+ }
224
+ }
@@ -0,0 +1,17 @@
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 ResponseProposeMolecule from './ResponseProposeMolecule'
10
+
11
+ /**
12
+ * Response for peer registration via P-isotope
13
+ * Matches JavaScript SDK ResponsePeering implementation exactly
14
+ */
15
+ export default class ResponsePeering extends ResponseProposeMolecule {
16
+
17
+ }
@@ -65,10 +65,9 @@ export default class ResponsePolicy extends Response {
65
65
  }) {
66
66
  super({
67
67
  query,
68
- json
68
+ json,
69
+ dataKey: 'data.Policy'
69
70
  })
70
- this.dataKey = 'data.Policy'
71
- this.init()
72
71
  }
73
72
 
74
73
  /**
@@ -74,7 +74,6 @@ export default class ResponseProposeMolecule extends Response {
74
74
  dataKey: 'data.ProposeMolecule'
75
75
  })
76
76
  this.$__clientMolecule = (query as any).molecule()
77
- this.init()
78
77
  }
79
78
 
80
79
  /**
@@ -75,13 +75,43 @@ export default class ResponseRequestAuthorization extends ResponseProposeMolecul
75
75
  }
76
76
 
77
77
  /**
78
- * Returns timestamp
79
- * Matches JavaScript SDK time method exactly
78
+ * Returns raw time value from payload
80
79
  */
81
80
  time(): string {
82
81
  return this.payloadKey('time')
83
82
  }
84
83
 
84
+ /**
85
+ * Returns the expiration timestamp as Unix seconds.
86
+ * Handles both server formats:
87
+ * - PHP server: time = lifetime in ms, expiresAt = Unix timestamp in payload
88
+ * - Rust server: time = Unix timestamp in seconds
89
+ */
90
+ expiresAt(): number {
91
+ // Try the explicit expiresAt payload key first (PHP server provides this)
92
+ try {
93
+ const ea = this.payloadKey('expiresAt')
94
+ if (ea) {
95
+ return Number(ea)
96
+ }
97
+ } catch (_e) {
98
+ // Not available in payload, fall back
99
+ }
100
+
101
+ // Use time field with heuristic detection
102
+ const timeValue = Number(this.time())
103
+
104
+ // If timeValue looks like a valid Unix timestamp (>= year 2020), use directly
105
+ // Rust server sets time = Unix timestamp in seconds
106
+ if (timeValue >= 1577836800) {
107
+ return timeValue
108
+ }
109
+
110
+ // Otherwise, time is a lifetime in milliseconds (PHP server format)
111
+ // Convert to Unix timestamp: now_seconds + lifetime_seconds
112
+ return Math.floor(Date.now() / 1000) + Math.floor(timeValue / 1000)
113
+ }
114
+
85
115
  /**
86
116
  * Returns encryption info
87
117
  * Matches JavaScript SDK encrypt method exactly