@wishknish/knishio-client-js 0.7.5 → 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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wishknish/knishio-client-js",
3
- "version": "0.7.5",
3
+ "version": "0.7.7",
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.",
@@ -73,6 +73,7 @@ import MutationClaimShadowWallet from './mutation/MutationClaimShadowWallet.js'
73
73
  import MutationCreateMeta from './mutation/MutationCreateMeta.js'
74
74
  import MutationPeering from './mutation/MutationPeering.js'
75
75
  import MutationAppendRequest from './mutation/MutationAppendRequest.js'
76
+ import MutationLinkIdentifier from './mutation/MutationLinkIdentifier.js'
76
77
  import MutationCreateWallet from './mutation/MutationCreateWallet.js'
77
78
  import MutationRequestAuthorizationGuest from './mutation/MutationRequestAuthorizationGuest.js'
78
79
  import TransferBalanceException from './exception/TransferBalanceException.js'
@@ -456,6 +457,26 @@ export default class KnishIOClient {
456
457
  secret = secret || this.getSecret()
457
458
  bundle = bundle || this.getBundle()
458
459
 
460
+ // For non-USER source wallets (V/B isotopes), capture the current USER
461
+ // ContinuID position BEFORE overwriting the client's remainder wallet.
462
+ // This position is needed by addContinuIdAtom() for previousPosition metadata.
463
+ let continuIdPosition = null
464
+ if (sourceWallet && sourceWallet.token !== 'USER') {
465
+ if (this.lastMoleculeQuery &&
466
+ this.getRemainderWallet() &&
467
+ this.getRemainderWallet().token === 'USER' &&
468
+ this.lastMoleculeQuery.response() &&
469
+ this.lastMoleculeQuery.response().success()
470
+ ) {
471
+ // Carry-forward: use last successful USER remainder wallet position
472
+ continuIdPosition = this.getRemainderWallet().position
473
+ } else {
474
+ // Query server for current ContinuID position
475
+ const userWallet = await this.getSourceWallet()
476
+ continuIdPosition = userWallet ? userWallet.position : null
477
+ }
478
+ }
479
+
459
480
  // Sets the source wallet as the last remainder wallet (to maintain ContinuID)
460
481
  if (!sourceWallet &&
461
482
  this.lastMoleculeQuery &&
@@ -485,7 +506,8 @@ export default class KnishIOClient {
485
506
  sourceWallet,
486
507
  remainderWallet: this.getRemainderWallet(),
487
508
  cellSlug: this.getCellSlug(),
488
- version: this.getServerSdkVersion()
509
+ version: this.getServerSdkVersion(),
510
+ continuIdPosition
489
511
  })
490
512
  }
491
513
 
@@ -1248,8 +1270,7 @@ export default class KnishIOClient {
1248
1270
  const query = await this.createMoleculeMutation({
1249
1271
  mutationClass: MutationCreateRule,
1250
1272
  molecule: await this.createMolecule({
1251
- secret: this.getSecret(),
1252
- sourceWallet: await this.getSourceWallet()
1273
+ secret: this.getSecret()
1253
1274
  })
1254
1275
  }
1255
1276
  )
@@ -1285,8 +1306,7 @@ export default class KnishIOClient {
1285
1306
  const query = await this.createMoleculeMutation({
1286
1307
  mutationClass: MutationCreateMeta,
1287
1308
  molecule: await this.createMolecule({
1288
- secret: this.getSecret(),
1289
- sourceWallet: await this.getSourceWallet()
1309
+ secret: this.getSecret()
1290
1310
  })
1291
1311
  }
1292
1312
  )
@@ -1318,8 +1338,7 @@ export default class KnishIOClient {
1318
1338
  const query = await this.createMoleculeMutation({
1319
1339
  mutationClass: MutationPeering,
1320
1340
  molecule: await this.createMolecule({
1321
- secret: this.getSecret(),
1322
- sourceWallet: await this.getSourceWallet()
1341
+ secret: this.getSecret()
1323
1342
  })
1324
1343
  })
1325
1344
 
@@ -1351,8 +1370,7 @@ export default class KnishIOClient {
1351
1370
  const query = await this.createMoleculeMutation({
1352
1371
  mutationClass: MutationAppendRequest,
1353
1372
  molecule: await this.createMolecule({
1354
- secret: this.getSecret(),
1355
- sourceWallet: await this.getSourceWallet()
1373
+ secret: this.getSecret()
1356
1374
  })
1357
1375
  })
1358
1376
 
@@ -1395,6 +1413,25 @@ export default class KnishIOClient {
1395
1413
  return await this.executeQuery(query)
1396
1414
  }
1397
1415
 
1416
+ /**
1417
+ * Links an identifier to the current wallet bundle
1418
+ *
1419
+ * @param {string} type - The type of the identifier.
1420
+ * @param {string} contact - The contact associated with the identifier.
1421
+ * @returns {Promise<ResponseLinkIdentifier>} - A promise that resolves to the link result.
1422
+ */
1423
+ async linkIdentifier ({
1424
+ type,
1425
+ contact
1426
+ }) {
1427
+ const query = this.createQuery(MutationLinkIdentifier)
1428
+ return await this.executeQuery(query, {
1429
+ bundle: this.getBundle(),
1430
+ type,
1431
+ content: contact
1432
+ })
1433
+ }
1434
+
1398
1435
  /**
1399
1436
  * Creates a policy for a given metaType and metaId.
1400
1437
  *
@@ -1473,7 +1510,7 @@ export default class KnishIOClient {
1473
1510
 
1474
1511
  return this.executeQuery(walletQuery, {
1475
1512
  bundleHash: bundle || this.getBundle(),
1476
- tokenSlug: token,
1513
+ token,
1477
1514
  unspent
1478
1515
  })
1479
1516
  .then((response) => {
@@ -1948,7 +1985,7 @@ export default class KnishIOClient {
1948
1985
  molecule.sign({
1949
1986
  bundle: this.getBundle()
1950
1987
  })
1951
- molecule.check()
1988
+ molecule.check(sourceWallet)
1952
1989
 
1953
1990
  // Create & execute a mutation
1954
1991
  const query = await this.createMoleculeMutation({
@@ -2131,7 +2168,7 @@ export default class KnishIOClient {
2131
2168
  // Map server payload field names (time/key) to AuthToken constructor names (expiresAt/pubkey)
2132
2169
  const authToken = AuthToken.create({
2133
2170
  token: response.token(),
2134
- expiresAt: response.time(),
2171
+ expiresAt: response.expiresAt(),
2135
2172
  pubkey: response.pubKey(),
2136
2173
  encrypt: response.encrypt()
2137
2174
  }, wallet)
@@ -2188,7 +2225,7 @@ export default class KnishIOClient {
2188
2225
  // Map server payload field names (time/key) to AuthToken constructor names (expiresAt/pubkey)
2189
2226
  const authToken = AuthToken.create({
2190
2227
  token: response.token(),
2191
- expiresAt: response.time(),
2228
+ expiresAt: response.expiresAt(),
2192
2229
  pubkey: response.pubKey(),
2193
2230
  encrypt: response.encrypt()
2194
2231
  }, wallet)
package/src/Molecule.js CHANGED
@@ -86,7 +86,8 @@ export default class Molecule {
86
86
  sourceWallet = null,
87
87
  remainderWallet = null,
88
88
  cellSlug = null,
89
- version = null
89
+ version = null,
90
+ continuIdPosition = null
90
91
  }) {
91
92
  this.status = null
92
93
  this.molecularHash = null
@@ -95,6 +96,7 @@ export default class Molecule {
95
96
  this.secret = secret
96
97
  this.bundle = bundle
97
98
  this.sourceWallet = sourceWallet
99
+ this.continuIdPosition = continuIdPosition
98
100
  this.atoms = []
99
101
  this.parentHashes = []
100
102
  if (version !== null && Object.prototype.hasOwnProperty.call(versions, version)) {
@@ -343,8 +345,12 @@ export default class Molecule {
343
345
  // These are atom meta fields (not core fields), so they don't affect molecular hash.
344
346
  const continuIdMeta = {}
345
347
 
346
- // previousPosition: the source wallet's position being consumed
347
- if (this.sourceWallet && this.sourceWallet.position) {
348
+ // previousPosition: the current USER ContinuID position being consumed.
349
+ // For non-USER source wallets (V/B isotopes), use the explicitly passed
350
+ // continuIdPosition from createMolecule() instead of the TOKEN wallet position.
351
+ if (this.continuIdPosition) {
352
+ continuIdMeta.previousPosition = this.continuIdPosition
353
+ } else if (this.sourceWallet && this.sourceWallet.position) {
348
354
  continuIdMeta.previousPosition = this.sourceWallet.position
349
355
  }
350
356
 
@@ -464,11 +470,29 @@ export default class Molecule {
464
470
  throw new BalanceInsufficientException()
465
471
  }
466
472
 
473
+ // Create burn address wallet (null bundle = token destruction)
474
+ const burnWallet = new Wallet({
475
+ bundle: '0000000000000000000000000000000000000000000000000000000000000000',
476
+ token: this.sourceWallet.token
477
+ })
478
+
479
+ // V-atom 1: Debit full balance from source
467
480
  this.addAtom(Atom.create({
468
481
  isotope: 'V',
469
482
  wallet: this.sourceWallet,
470
- value: -amount
483
+ value: -this.sourceWallet.balance
471
484
  }))
485
+
486
+ // V-atom 2: Credit burn amount to burn address
487
+ this.addAtom(Atom.create({
488
+ isotope: 'V',
489
+ wallet: burnWallet,
490
+ value: amount,
491
+ metaType: 'walletBundle',
492
+ metaId: burnWallet.bundle
493
+ }))
494
+
495
+ // V-atom 3: Remainder back to source identity
472
496
  this.addAtom(Atom.create({
473
497
  isotope: 'V',
474
498
  wallet: this.remainderWallet,
package/src/index.js CHANGED
@@ -46,17 +46,112 @@ Please visit https://github.com/WishKnish/KnishIO-Client-JS for information.
46
46
  License: https://github.com/WishKnish/KnishIO-Client-JS/blob/master/LICENSE
47
47
  */
48
48
 
49
+ // =============================================================================
50
+ // CORE CLASSES
51
+ // =============================================================================
52
+
49
53
  import Atom from './Atom.js'
54
+ import AtomMeta from './AtomMeta.js'
50
55
  import Molecule from './Molecule.js'
51
56
  import Wallet from './Wallet.js'
52
57
  import Meta from './Meta.js'
58
+ import AuthToken from './AuthToken.js'
59
+ import TokenUnit from './TokenUnit.js'
60
+ import PolicyMeta from './PolicyMeta.js'
53
61
  import KnishIOClient from './KnishIOClient.js'
54
- import MutationPeering from './mutation/MutationPeering.js'
55
- import MutationAppendRequest from './mutation/MutationAppendRequest.js'
62
+
63
+ // Validation
64
+ import CheckMolecule from './libraries/CheckMolecule.js'
65
+
66
+ // Utilities
67
+ import Dot from './libraries/Dot.js'
68
+ import Decimal from './libraries/Decimal.js'
69
+
70
+ // =============================================================================
71
+ // BASE CLASSES
72
+ // =============================================================================
73
+
74
+ import Query from './query/Query.js'
75
+ import Mutation from './mutation/Mutation.js'
76
+ import Response from './response/Response.js'
77
+
78
+ // =============================================================================
79
+ // QUERY CLASSES
80
+ // =============================================================================
81
+
82
+ import QueryActiveSession from './query/QueryActiveSession.js'
83
+ import QueryAtom from './query/QueryAtom.js'
84
+ import QueryBalance from './query/QueryBalance.js'
85
+ import QueryBatch from './query/QueryBatch.js'
86
+ import QueryBatchHistory from './query/QueryBatchHistory.js'
87
+ import QueryContinuId from './query/QueryContinuId.js'
88
+ import QueryMetaType from './query/QueryMetaType.js'
89
+ import QueryMetaTypeViaAtom from './query/QueryMetaTypeViaAtom.js'
56
90
  import QueryMetaTypeViaMolecule from './query/QueryMetaTypeViaMolecule.js'
57
- import ResponsePeering from './response/ResponsePeering.js'
91
+ import QueryPolicy from './query/QueryPolicy.js'
92
+ import QueryToken from './query/QueryToken.js'
93
+ import QueryUserActivity from './query/QueryUserActivity.js'
94
+ import QueryWalletBundle from './query/QueryWalletBundle.js'
95
+ import QueryWalletList from './query/QueryWalletList.js'
96
+
97
+ // =============================================================================
98
+ // MUTATION CLASSES
99
+ // =============================================================================
100
+
101
+ import MutationActiveSession from './mutation/MutationActiveSession.js'
102
+ import MutationAppendRequest from './mutation/MutationAppendRequest.js'
103
+ import MutationClaimShadowWallet from './mutation/MutationClaimShadowWallet.js'
104
+ import MutationCreateIdentifier from './mutation/MutationCreateIdentifier.js'
105
+ import MutationCreateMeta from './mutation/MutationCreateMeta.js'
106
+ import MutationCreateRule from './mutation/MutationCreateRule.js'
107
+ import MutationCreateToken from './mutation/MutationCreateToken.js'
108
+ import MutationCreateWallet from './mutation/MutationCreateWallet.js'
109
+ import MutationDepositBufferToken from './mutation/MutationDepositBufferToken.js'
110
+ import MutationLinkIdentifier from './mutation/MutationLinkIdentifier.js'
111
+ import MutationPeering from './mutation/MutationPeering.js'
112
+ import MutationProposeMolecule from './mutation/MutationProposeMolecule.js'
113
+ import MutationRequestAuthorization from './mutation/MutationRequestAuthorization.js'
114
+ import MutationRequestAuthorizationGuest from './mutation/MutationRequestAuthorizationGuest.js'
115
+ import MutationRequestTokens from './mutation/MutationRequestTokens.js'
116
+ import MutationTransferTokens from './mutation/MutationTransferTokens.js'
117
+ import MutationWithdrawBufferToken from './mutation/MutationWithdrawBufferToken.js'
118
+
119
+ // =============================================================================
120
+ // RESPONSE CLASSES
121
+ // =============================================================================
122
+
123
+ import ResponseActiveSession from './response/ResponseActiveSession.js'
58
124
  import ResponseAppendRequest from './response/ResponseAppendRequest.js'
125
+ import ResponseAtom from './response/ResponseAtom.js'
126
+ import ResponseAuthorizationGuest from './response/ResponseAuthorizationGuest.js'
127
+ import ResponseBalance from './response/ResponseBalance.js'
128
+ import ResponseClaimShadowWallet from './response/ResponseClaimShadowWallet.js'
129
+ import ResponseContinuId from './response/ResponseContinuId.js'
130
+ import ResponseCreateIdentifier from './response/ResponseCreateIdentifier.js'
131
+ import ResponseCreateMeta from './response/ResponseCreateMeta.js'
132
+ import ResponseCreateRule from './response/ResponseCreateRule.js'
133
+ import ResponseCreateToken from './response/ResponseCreateToken.js'
134
+ import ResponseCreateWallet from './response/ResponseCreateWallet.js'
135
+ import ResponseLinkIdentifier from './response/ResponseLinkIdentifier.js'
136
+ import ResponseMetaType from './response/ResponseMetaType.js'
137
+ import ResponseMetaTypeViaAtom from './response/ResponseMetaTypeViaAtom.js'
59
138
  import ResponseMetaTypeViaMolecule from './response/ResponseMetaTypeViaMolecule.js'
139
+ import ResponsePeering from './response/ResponsePeering.js'
140
+ import ResponsePolicy from './response/ResponsePolicy.js'
141
+ import ResponseProposeMolecule from './response/ResponseProposeMolecule.js'
142
+ import ResponseQueryActiveSession from './response/ResponseQueryActiveSession.js'
143
+ import ResponseQueryUserActivity from './response/ResponseQueryUserActivity.js'
144
+ import ResponseRequestAuthorization from './response/ResponseRequestAuthorization.js'
145
+ import ResponseRequestAuthorizationGuest from './response/ResponseRequestAuthorizationGuest.js'
146
+ import ResponseRequestTokens from './response/ResponseRequestTokens.js'
147
+ import ResponseTransferTokens from './response/ResponseTransferTokens.js'
148
+ import ResponseWalletBundle from './response/ResponseWalletBundle.js'
149
+ import ResponseWalletList from './response/ResponseWalletList.js'
150
+
151
+ // =============================================================================
152
+ // LIBRARY FUNCTIONS
153
+ // =============================================================================
154
+
60
155
  import {
61
156
  base64ToHex,
62
157
  bufferToHexString,
@@ -65,34 +160,149 @@ import {
65
160
  hexStringToBuffer,
66
161
  hexToBase64,
67
162
  isHex,
163
+ isNumeric,
68
164
  randomString
69
165
  } from './libraries/strings.js'
166
+
70
167
  import {
168
+ generateBatchId,
71
169
  generateBundleHash,
72
170
  generateSecret,
73
171
  shake256
74
172
  } from './libraries/crypto.js'
75
173
 
174
+ import {
175
+ chunkArray,
176
+ deepCloning,
177
+ diff,
178
+ intersect
179
+ } from './libraries/array.js'
180
+
181
+ // =============================================================================
182
+ // EXCEPTION SYSTEM
183
+ // =============================================================================
184
+
76
185
  export {
186
+ AtomIndexException,
187
+ AtomsMissingException,
188
+ AuthorizationRejectedException,
189
+ BalanceInsufficientException,
190
+ BatchIdException,
191
+ CodeException,
192
+ InvalidResponseException,
193
+ MetaMissingException,
194
+ MolecularHashMismatchException,
195
+ MolecularHashMissingException,
196
+ NegativeAmountException,
197
+ PolicyInvalidException,
198
+ SignatureMalformedException,
199
+ SignatureMismatchException,
200
+ StackableUnitAmountException,
201
+ StackableUnitDecimalsException,
202
+ TransferBalanceException,
203
+ TransferMalformedException,
204
+ TransferMismatchedException,
205
+ TransferRemainderException,
206
+ TransferToSelfException,
207
+ TransferUnbalancedException,
208
+ UnauthenticatedException,
209
+ WalletShadowException,
210
+ WrongTokenTypeException
211
+ } from './exception/index.js'
212
+
213
+ // =============================================================================
214
+ // NAMED EXPORTS
215
+ // =============================================================================
216
+
217
+ export {
218
+ // Core classes
77
219
  Atom,
220
+ AtomMeta,
78
221
  Molecule,
79
222
  Wallet,
80
223
  Meta,
224
+ AuthToken,
225
+ TokenUnit,
226
+ PolicyMeta,
81
227
  KnishIOClient,
82
228
 
83
- // queries
229
+ // Validation
230
+ CheckMolecule,
231
+
232
+ // Utilities
233
+ Dot,
234
+ Decimal,
235
+
236
+ // Base classes
237
+ Query,
238
+ Mutation,
239
+ Response,
240
+
241
+ // Queries
242
+ QueryActiveSession,
243
+ QueryAtom,
244
+ QueryBalance,
245
+ QueryBatch,
246
+ QueryBatchHistory,
247
+ QueryContinuId,
248
+ QueryMetaType,
249
+ QueryMetaTypeViaAtom,
84
250
  QueryMetaTypeViaMolecule,
251
+ QueryPolicy,
252
+ QueryToken,
253
+ QueryUserActivity,
254
+ QueryWalletBundle,
255
+ QueryWalletList,
85
256
 
86
- // mutations
87
- MutationPeering,
257
+ // Mutations
258
+ MutationActiveSession,
88
259
  MutationAppendRequest,
260
+ MutationClaimShadowWallet,
261
+ MutationCreateIdentifier,
262
+ MutationCreateMeta,
263
+ MutationCreateRule,
264
+ MutationCreateToken,
265
+ MutationCreateWallet,
266
+ MutationDepositBufferToken,
267
+ MutationLinkIdentifier,
268
+ MutationPeering,
269
+ MutationProposeMolecule,
270
+ MutationRequestAuthorization,
271
+ MutationRequestAuthorizationGuest,
272
+ MutationRequestTokens,
273
+ MutationTransferTokens,
274
+ MutationWithdrawBufferToken,
89
275
 
90
- // responses
91
- ResponsePeering,
276
+ // Responses
277
+ ResponseActiveSession,
92
278
  ResponseAppendRequest,
279
+ ResponseAtom,
280
+ ResponseAuthorizationGuest,
281
+ ResponseBalance,
282
+ ResponseClaimShadowWallet,
283
+ ResponseContinuId,
284
+ ResponseCreateIdentifier,
285
+ ResponseCreateMeta,
286
+ ResponseCreateRule,
287
+ ResponseCreateToken,
288
+ ResponseCreateWallet,
289
+ ResponseLinkIdentifier,
290
+ ResponseMetaType,
291
+ ResponseMetaTypeViaAtom,
93
292
  ResponseMetaTypeViaMolecule,
293
+ ResponsePeering,
294
+ ResponsePolicy,
295
+ ResponseProposeMolecule,
296
+ ResponseQueryActiveSession,
297
+ ResponseQueryUserActivity,
298
+ ResponseRequestAuthorization,
299
+ ResponseRequestAuthorizationGuest,
300
+ ResponseRequestTokens,
301
+ ResponseTransferTokens,
302
+ ResponseWalletBundle,
303
+ ResponseWalletList,
94
304
 
95
- // strings
305
+ // String utilities
96
306
  chunkSubstr,
97
307
  base64ToHex,
98
308
  bufferToHexString,
@@ -100,10 +310,18 @@ export {
100
310
  hexStringToBuffer,
101
311
  hexToBase64,
102
312
  isHex,
313
+ isNumeric,
103
314
  randomString,
104
315
 
105
- // crypto
316
+ // Crypto utilities
106
317
  generateSecret,
107
318
  generateBundleHash,
108
- shake256
319
+ generateBatchId,
320
+ shake256,
321
+
322
+ // Array utilities
323
+ chunkArray,
324
+ deepCloning,
325
+ diff,
326
+ intersect
109
327
  }
@@ -75,6 +75,14 @@ export default class QueryContinuId extends Query {
75
75
  }`
76
76
  }
77
77
 
78
+ /**
79
+ * Force network-only for ContinuID queries to prevent URQL cache from
80
+ * returning stale wallet positions after mutations advance the ContinuID chain
81
+ */
82
+ createQueryContext () {
83
+ return { requestPolicy: 'network-only' }
84
+ }
85
+
78
86
  /**
79
87
  * Returns a Response object
80
88
  *
@@ -60,8 +60,8 @@ export default class QueryWalletList extends Query {
60
60
  constructor (graphQLClient, knishIOClient) {
61
61
  super(graphQLClient, knishIOClient)
62
62
 
63
- this.$__query = gql`query( $bundleHash: String, $tokenSlug: String ) {
64
- Wallet( bundleHash: $bundleHash, tokenSlug: $tokenSlug ) {
63
+ this.$__query = gql`query( $bundleHash: String, $token: String ) {
64
+ Wallet( bundleHash: $bundleHash, token: $token ) {
65
65
  address,
66
66
  bundleHash,
67
67
  token {
@@ -76,7 +76,7 @@ export default class ResponseRequestAuthorization extends ResponseProposeMolecul
76
76
  }
77
77
 
78
78
  /**
79
- * Returns timestamp
79
+ * Returns raw time value from payload
80
80
  *
81
81
  * @return {string}
82
82
  */
@@ -84,6 +84,39 @@ export default class ResponseRequestAuthorization extends ResponseProposeMolecul
84
84
  return this.payloadKey('time')
85
85
  }
86
86
 
87
+ /**
88
+ * Returns the expiration timestamp as Unix seconds.
89
+ * Handles both server formats:
90
+ * - PHP server: time = lifetime in ms, expiresAt = Unix timestamp in payload
91
+ * - Rust server: time = Unix timestamp in seconds
92
+ *
93
+ * @return {number}
94
+ */
95
+ expiresAt () {
96
+ // Try the explicit expiresAt payload key first (PHP server provides this)
97
+ try {
98
+ const ea = this.payloadKey('expiresAt')
99
+ if (ea) {
100
+ return Number(ea)
101
+ }
102
+ } catch (_e) {
103
+ // Not available in payload, fall back
104
+ }
105
+
106
+ // Use time field with heuristic detection
107
+ const timeValue = Number(this.time())
108
+
109
+ // If timeValue looks like a valid Unix timestamp (>= year 2020), use directly
110
+ // Rust server sets time = Unix timestamp in seconds
111
+ if (timeValue >= 1577836800) {
112
+ return timeValue
113
+ }
114
+
115
+ // Otherwise, time is a lifetime in milliseconds (PHP server format)
116
+ // Convert to Unix timestamp: now_seconds + lifetime_seconds
117
+ return Math.floor(Date.now() / 1000) + Math.floor(timeValue / 1000)
118
+ }
119
+
87
120
  /**
88
121
  *
89
122
  * @return {string}
@@ -120,7 +120,7 @@ export default class ResponseRequestAuthorizationGuest extends Response {
120
120
  }
121
121
 
122
122
  /**
123
- * Returns timestamp
123
+ * Returns raw time value from payload
124
124
  *
125
125
  * @return {*}
126
126
  */
@@ -128,6 +128,37 @@ export default class ResponseRequestAuthorizationGuest extends Response {
128
128
  return this.payloadKey('time')
129
129
  }
130
130
 
131
+ /**
132
+ * Returns the expiration timestamp as Unix seconds.
133
+ * Handles both server formats:
134
+ * - PHP server: time = lifetime in ms, expiresAt = Unix timestamp in payload
135
+ * - Rust server: time = Unix timestamp in seconds
136
+ *
137
+ * @return {number}
138
+ */
139
+ expiresAt () {
140
+ // Try the explicit expiresAt payload key first (PHP server provides this)
141
+ try {
142
+ const ea = this.payloadKey('expiresAt')
143
+ if (ea) {
144
+ return Number(ea)
145
+ }
146
+ } catch (_e) {
147
+ // Not available in payload, fall back
148
+ }
149
+
150
+ // Use time field with heuristic detection
151
+ const timeValue = Number(this.time())
152
+
153
+ // If timeValue looks like a valid Unix timestamp (>= year 2020), use directly
154
+ if (timeValue >= 1577836800) {
155
+ return timeValue
156
+ }
157
+
158
+ // Otherwise, time is a lifetime in milliseconds (PHP server format)
159
+ return Math.floor(Date.now() / 1000) + Math.floor(timeValue / 1000)
160
+ }
161
+
131
162
  /**
132
163
  * Returns timestamp
133
164
  *