nodejs-insta-private-api-mqt 1.3.73 → 1.3.75

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.
@@ -264,7 +264,7 @@ class Request {
264
264
  if (!url) return;
265
265
  const path = url.toLowerCase();
266
266
 
267
- if (path.includes('/accounts/login/') || path.includes('/launcher/sync/')) {
267
+ if (path.includes('/accounts/login/') || path.includes('/launcher/sync/') || path.includes('/bloks/async_action/com.bloks.www.bloks.caa.login')) {
268
268
  this.navChain.simulateAppOpen();
269
269
  return;
270
270
  }
@@ -65,23 +65,15 @@ class MQTToTClient extends mqtts_1.MqttClient {
65
65
  this.registerListeners();
66
66
  this.requirePayload = options.requirePayload;
67
67
 
68
- this._keepaliveMs = (typeof options.keepaliveMs === 'number') ? options.keepaliveMs : (30 * 1000); // default ~30s (mobile-like) to stay well within CONNECT keepAlive=60
68
+ this._keepaliveMs = (typeof options.keepaliveMs === 'number') ? options.keepaliveMs : (8 * 60 * 1000);
69
69
  this._consecutivePingFailures = 0;
70
-
71
- // Stall detection (mobile-like): if we don't receive any traffic for too long,
72
- // force a reconnect. This helps recover from half-open sockets.
73
- this._lastRxTs = Date.now();
74
- this._stallMs = (typeof options.stallMs === 'number') ? options.stallMs : (180 * 1000); // 3 min default
75
- this._stallCheckMs = (typeof options.stallCheckMs === 'number') ? options.stallCheckMs : (30 * 1000); // check every 30s
76
- this._startStallWatchdog();
77
-
78
70
  this._startKeepalive();
79
71
  }
80
72
 
81
73
  _startKeepalive() {
82
74
  try {
83
75
  if (this._keepaliveTimer) clearInterval(this._keepaliveTimer);
84
- const jitter = Math.floor(Math.random() * 3000);
76
+ const jitter = Math.floor(Math.random() * 30000);
85
77
  this._keepaliveTimer = setInterval(() => {
86
78
  try {
87
79
  if (typeof this.ping === 'function') {
@@ -112,41 +104,7 @@ class MQTToTClient extends mqtts_1.MqttClient {
112
104
  }
113
105
  }
114
106
 
115
-
116
- _startStallWatchdog() {
117
- try {
118
- if (this._stallTimer) clearInterval(this._stallTimer);
119
- this._stallTimer = setInterval(() => {
120
- try {
121
- // only check if we appear connected
122
- const now = Date.now();
123
- const last = this._lastRxTs || 0;
124
- if (last && (now - last) > this._stallMs) {
125
- this.mqttotDebug(`Stall detected: no inbound traffic for ${Math.round((now - last) / 1000)}s (threshold ${Math.round(this._stallMs/1000)}s). Forcing reconnect.`);
126
- this._lastRxTs = now;
127
- this.emit('disconnect', 'stall_timeout');
128
- }
129
- } catch (e) {
130
- this.mqttotDebug(`Stall watchdog error: ${e?.message || e}`);
131
- }
132
- }, this._stallCheckMs);
133
- } catch (e) {
134
- this.mqttotDebug(`Stall watchdog setup error: ${e?.message || e}`);
135
- }
136
- }
137
-
138
- _stopStallWatchdog() {
139
- try {
140
- if (this._stallTimer) {
141
- clearInterval(this._stallTimer);
142
- this._stallTimer = null;
143
- }
144
- } catch (e) {
145
- // ignore
146
- }
147
- }
148
-
149
- /**
107
+ /**
150
108
  * Stop keepalive timer (call on explicit close/disconnect)
151
109
  */
152
110
  _stopKeepalive() {
@@ -179,14 +137,9 @@ class MQTToTClient extends mqtts_1.MqttClient {
179
137
  // Attach diagnostics
180
138
  this.on('error', printErrorOrWarning('Error'));
181
139
  this.on('warning', printErrorOrWarning('Warning'));
182
- this.on('connect', () => { try { this._lastRxTs = Date.now(); } catch (e) {} });
183
-
184
- // Update last-receive timestamp for stall detection
185
- this.on('message', () => { try { this._lastRxTs = Date.now(); } catch (e) {} });
186
140
 
187
141
  // Listen to ping responses if the library emits them
188
142
  this.on('pingresp', () => {
189
- try { this._lastRxTs = Date.now(); } catch (e) {}
190
143
  this.mqttotDebug('Received PINGRESP (keepalive ok)');
191
144
  });
192
145
 
@@ -194,7 +147,6 @@ class MQTToTClient extends mqtts_1.MqttClient {
194
147
  try {
195
148
  this.mqttotDebug(`Disconnected. Reason: ${reason}`);
196
149
  this._stopKeepalive();
197
- this._stopStallWatchdog();
198
150
 
199
151
  if (this._options && this._options.autoReconnect === false) {
200
152
  this.mqttotDebug('autoReconnect disabled; will not attempt reconnect.');
@@ -212,8 +164,6 @@ class MQTToTClient extends mqtts_1.MqttClient {
212
164
  await this.connect(this._options);
213
165
  this.mqttotDebug('Reconnected successfully');
214
166
  this._consecutivePingFailures = 0;
215
- this._lastRxTs = Date.now();
216
- this._startStallWatchdog();
217
167
  this._startKeepalive();
218
168
  return;
219
169
  } catch (err) {
@@ -326,7 +276,6 @@ class MQTToTClient extends mqtts_1.MqttClient {
326
276
  async gracefulClose() {
327
277
  try {
328
278
  this._stopKeepalive();
329
- this._stopStallWatchdog();
330
279
  if (typeof super.close === 'function') {
331
280
  // some libs provide close() or end()
332
281
  await super.close();
@@ -6,4 +6,4 @@ function writeConnectRequestPacket(stream, options) {
6
6
  return {};
7
7
  }
8
8
  exports.writeConnectRequestPacket = writeConnectRequestPacket;
9
- //# sourceMappingURL=mqttot.connect.request.packet.js.map
9
+ //# sourceMappingURL=mqttot.connect.request.packet.js.map
@@ -71,9 +71,7 @@ MQTToTConnection.thriftConfig = [
71
71
  thrift_1.ThriftDescriptors.binary('fbnsDeviceSecret', 25),
72
72
  thrift_1.ThriftDescriptors.int64('anotherUnknown', 26),
73
73
  ]),
74
- thrift_1.ThriftDescriptors.binary('password', 5),
75
- thrift_1.ThriftDescriptors.int16('unknown', 5),
76
- thrift_1.ThriftDescriptors.listOfBinary('getDiffsRequests', 6),
74
+ thrift_1.ThriftDescriptors.binary('password', 5), thrift_1.ThriftDescriptors.listOfBinary('getDiffsRequests', 6),
77
75
  thrift_1.ThriftDescriptors.binary('zeroRatingTokenHash', 9),
78
76
  thrift_1.ThriftDescriptors.mapBinaryBinary('appSpecificInfo', 10),
79
77
  ];
@@ -77,4 +77,4 @@ module.exports = {
77
77
  Topics,
78
78
  RealtimeTopicsArray,
79
79
  REALTIME
80
- };
80
+ };
@@ -45,22 +45,84 @@ class AccountRepository extends Repository {
45
45
  const { encrypted, time } = this.encryptPassword(password);
46
46
 
47
47
  return this.requestWithRetry(async () => {
48
- const countryCode = this.client.state.countryCode || 1;
48
+ const aacInitTimestamp = Math.floor(Date.now() / 1000) - Math.floor(Math.random() * 50);
49
+ const aacjid = crypto.randomUUID ? crypto.randomUUID() : require('uuid').v4();
50
+ const aaccs = crypto.randomBytes(32).toString('base64url');
51
+
52
+ const clientInputParams = {
53
+ aac: JSON.stringify({
54
+ aac_init_timestamp: aacInitTimestamp,
55
+ aacjid: aacjid,
56
+ aaccs: aaccs,
57
+ }),
58
+ sim_phones: [],
59
+ aymh_accounts: [],
60
+ network_bssid: null,
61
+ secure_family_device_id: '',
62
+ has_granted_read_contacts_permissions: 0,
63
+ auth_secure_device_id: '',
64
+ has_whatsapp_installed: 1,
65
+ password: `#PWD_INSTAGRAM:4:${time}:${encrypted}`,
66
+ sso_token_map_json_string: '{}',
67
+ block_store_machine_id: '',
68
+ ig_vetted_device_nonces: '{}',
69
+ contact_point: username,
70
+ machine_id: this.client.state.mid || '',
71
+ login_attempt_count: '0',
72
+ reg_flow_taken: 'phone',
73
+ device_id: this.client.state.uuid,
74
+ phone_id: this.client.state.phoneId,
75
+ family_device_id: this.client.state.phoneId,
76
+ encryption_enabled: '1',
77
+ has_dbl_tap_login: 0,
78
+ jazoest: AccountRepository.createJazoest(this.client.state.phoneId),
79
+ openid_tokens: '{}',
80
+ };
81
+
82
+ const serverParams = {
83
+ credential_type: 'password',
84
+ device_id: this.client.state.uuid,
85
+ };
86
+
87
+ const paramsJson = JSON.stringify({
88
+ client_input_params: clientInputParams,
89
+ server_params: serverParams,
90
+ });
91
+
92
+ const attestParams = AccountRepository.generateAttestParams(this.client.state);
93
+
94
+ const bloksHeaders = {
95
+ 'X-Bloks-Prism-Ax-Base-Colors-Enabled': 'false',
96
+ 'X-Bloks-Prism-Button-Version': 'CONTROL',
97
+ 'X-Bloks-Prism-Colors-Enabled': 'true',
98
+ 'X-Bloks-Prism-Font-Enabled': 'false',
99
+ 'X-Bloks-Prism-Indigo-Link-Version': '0',
100
+ 'X-FB-Friendly-Name': 'IgApi: bloks/async_action/com.bloks.www.bloks.caa.login.async.send_login_request/',
101
+ 'X-FB-Request-Analytics-Tags': JSON.stringify({
102
+ network_tags: {
103
+ product: this.client.state.fbAnalyticsApplicationId,
104
+ purpose: 'fetch',
105
+ surface: 'undefined',
106
+ request_category: 'api',
107
+ retry_attempt: '0',
108
+ },
109
+ }),
110
+ 'X-IG-Client-Endpoint': 'com.bloks.www.caa.login.aymh_single_profile_screen_entry',
111
+ 'X-Tigon-Is-Retry': 'False',
112
+ 'X-FB-RMD': 'state=URL_ELIGIBLE',
113
+ 'X-IG-Attest-Params': JSON.stringify(attestParams),
114
+ 'X-FB-Connection-Type': 'MOBILE.LTE',
115
+ 'X-FB-Network-Properties': 'VPN;Metered;Validated;LocalAddrs=/10.0.0.2,;',
116
+ 'X-FB-Conn-UUID-Client': crypto.randomBytes(16).toString('hex'),
117
+ 'Accept-Encoding': 'zstd',
118
+ 'X-FB-HTTP-Engine': 'MNS/TCP',
119
+ };
120
+
49
121
  const response = await this.client.request.send({
50
122
  method: 'POST',
51
- url: '/api/v1/accounts/login/',
52
- form: this.client.request.sign({
53
- jazoest: AccountRepository.createJazoest(this.client.state.phoneId),
54
- country_codes: JSON.stringify([{ country_code: String(countryCode), source: ['default'] }]),
55
- phone_id: this.client.state.phoneId,
56
- enc_password: `#PWD_INSTAGRAM:4:${time}:${encrypted}`,
57
- username,
58
- adid: this.client.state.adid,
59
- guid: this.client.state.uuid,
60
- device_id: this.client.state.deviceId,
61
- google_tokens: '[]',
62
- login_attempt_count: '0',
63
- }),
123
+ url: '/api/v1/bloks/async_action/com.bloks.www.bloks.caa.login.async.send_login_request/',
124
+ form: { params: paramsJson },
125
+ headers: bloksHeaders,
64
126
  });
65
127
 
66
128
  const body = response.body;
@@ -88,7 +150,51 @@ class AccountRepository extends Repository {
88
150
  throw err;
89
151
  }
90
152
 
91
- return body.logged_in_user;
153
+ if (body.layout) {
154
+ const layoutStr = typeof body.layout === 'string' ? body.layout : JSON.stringify(body.layout);
155
+
156
+ if (layoutStr.includes('two_factor_required') || layoutStr.includes('"two_factor_info"')) {
157
+ let twoFactorInfo = null;
158
+ try {
159
+ const match = layoutStr.match(/"two_factor_info"\s*:\s*(\{[^}]+\})/);
160
+ if (match) twoFactorInfo = JSON.parse(match[1]);
161
+ } catch (e) {}
162
+ const err = new Error('Two factor authentication required');
163
+ err.name = 'IgLoginTwoFactorRequiredError';
164
+ err.twoFactorInfo = twoFactorInfo;
165
+ throw err;
166
+ }
167
+
168
+ if (layoutStr.includes('bad_password') || layoutStr.includes('incorrect_password')) {
169
+ const err = new Error('Bad password');
170
+ err.name = 'IgLoginBadPasswordError';
171
+ throw err;
172
+ }
173
+
174
+ if (layoutStr.includes('invalid_user') || layoutStr.includes('user_not_found')) {
175
+ const err = new Error('Invalid user');
176
+ err.name = 'IgLoginInvalidUserError';
177
+ throw err;
178
+ }
179
+
180
+ if (layoutStr.includes('challenge_required')) {
181
+ const err = new Error('Challenge required');
182
+ err.name = 'IgCheckpointError';
183
+ err.challengeInfo = body.challenge || null;
184
+ throw err;
185
+ }
186
+ }
187
+
188
+ if (body.logged_in_user) {
189
+ return body.logged_in_user;
190
+ }
191
+
192
+ try {
193
+ const userInfo = await this.currentUser();
194
+ return userInfo.user || userInfo;
195
+ } catch (e) {
196
+ return body;
197
+ }
92
198
  });
93
199
  }
94
200
 
@@ -459,6 +565,96 @@ class AccountRepository extends Repository {
459
565
  });
460
566
  }
461
567
 
568
+ static generateAttestParams(state) {
569
+ const challengeNonce = crypto.randomBytes(24).toString('base64url');
570
+
571
+ const { privateKey, publicKey } = crypto.generateKeyPairSync('ec', {
572
+ namedCurve: 'prime256v1',
573
+ });
574
+
575
+ const signedData = crypto.sign(null, Buffer.from(challengeNonce), privateKey);
576
+ const signedNonce = signedData.toString('base64');
577
+
578
+ const publicKeyDer = publicKey.export({ type: 'spki', format: 'der' });
579
+ const keyHash = crypto.createHash('sha256').update(publicKeyDer).digest('hex');
580
+
581
+ const leafCertPem = AccountRepository._generateSelfSignedCert(privateKey, publicKey, 'Android Keystore Key');
582
+ const intermediateCertPem = AccountRepository._generateSelfSignedCert(privateKey, publicKey, 'TEE');
583
+
584
+ const certificateChain = leafCertPem + '\n' + intermediateCertPem;
585
+
586
+ return {
587
+ attestation: [{
588
+ version: 2,
589
+ type: 'keystore',
590
+ errors: [0],
591
+ challenge_nonce: challengeNonce,
592
+ signed_nonce: signedNonce,
593
+ key_hash: keyHash,
594
+ certificate_chain: certificateChain,
595
+ }],
596
+ };
597
+ }
598
+
599
+ static _generateSelfSignedCert(privateKey, publicKey, cn) {
600
+ const publicKeyDer = publicKey.export({ type: 'spki', format: 'der' });
601
+ const serialNumber = crypto.randomBytes(8);
602
+ const now = new Date();
603
+ const notBefore = new Date(now.getTime() - 365 * 24 * 60 * 60 * 1000);
604
+ const notAfter = new Date(now.getTime() + 10 * 365 * 24 * 60 * 60 * 1000);
605
+
606
+ const cnBytes = Buffer.from(cn, 'utf8');
607
+ const cnSeq = Buffer.concat([
608
+ Buffer.from([0x30, cnBytes.length + 13]),
609
+ Buffer.from([0x31, cnBytes.length + 11]),
610
+ Buffer.from([0x30, cnBytes.length + 9]),
611
+ Buffer.from([0x06, 0x03, 0x55, 0x04, 0x03]),
612
+ Buffer.from([0x0c, cnBytes.length]),
613
+ cnBytes,
614
+ ]);
615
+
616
+ function encodeTime(date) {
617
+ const str = date.toISOString().replace(/[-:T]/g, '').slice(2, 14) + 'Z';
618
+ return Buffer.concat([Buffer.from([0x17, str.length]), Buffer.from(str)]);
619
+ }
620
+
621
+ const validityBuf = Buffer.concat([
622
+ encodeTime(notBefore),
623
+ encodeTime(notAfter),
624
+ ]);
625
+ const validity = Buffer.concat([Buffer.from([0x30, validityBuf.length]), validityBuf]);
626
+
627
+ const tbs = Buffer.concat([
628
+ Buffer.from([0xa0, 0x03, 0x02, 0x01, 0x02]),
629
+ Buffer.from([0x02, serialNumber.length]), serialNumber,
630
+ Buffer.from([0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02]),
631
+ cnSeq,
632
+ validity,
633
+ cnSeq,
634
+ publicKeyDer,
635
+ ]);
636
+
637
+ const tbsSeq = Buffer.concat([Buffer.from([0x30, 0x82]), Buffer.alloc(2), tbs]);
638
+ tbsSeq.writeUInt16BE(tbs.length, 2);
639
+
640
+ const signature = crypto.sign(null, tbsSeq, privateKey);
641
+
642
+ const sigBitString = Buffer.concat([
643
+ Buffer.from([0x03, signature.length + 1, 0x00]),
644
+ signature,
645
+ ]);
646
+
647
+ const algId = Buffer.from([0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02]);
648
+
649
+ const certBody = Buffer.concat([tbsSeq, algId, sigBitString]);
650
+ const cert = Buffer.concat([Buffer.from([0x30, 0x82]), Buffer.alloc(2), certBody]);
651
+ cert.writeUInt16BE(certBody.length, 2);
652
+
653
+ const b64 = cert.toString('base64');
654
+ const lines = b64.match(/.{1,76}/g) || [b64];
655
+ return '-----BEGIN CERTIFICATE-----\n' + lines.join('\n') + '\n-----END CERTIFICATE-----';
656
+ }
657
+
462
658
  static createJazoest(input) {
463
659
  const buf = Buffer.from(input, 'ascii');
464
660
  let sum = 0;
@@ -1,101 +1,111 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.int64ToNumber = exports.ThriftDescriptors = exports.isThriftBoolean = exports.ThriftTypes = void 0;
3
+ exports.isSafeInt64 = exports.int64ToBigInt = exports.int64ToNumberSafe = exports.int64ToNumber = exports.ThriftDescriptors = exports.isThriftBoolean = exports.ThriftTypes = void 0;
4
+
5
+ /**
6
+ * Thrift Compact Protocol (subset) helpers used by nodejs-insta-private-api-mqt.
7
+ * Notes:
8
+ * - Instagram's MQTT-over-Thrift payloads can contain INT_64 values that exceed JS Number safe range.
9
+ * - This module keeps backward compatibility (int64ToNumber) but also exposes safe helpers.
10
+ */
11
+
4
12
  exports.ThriftTypes = {
5
- STOP: 0x00,
6
- TRUE: 0x01,
7
- FALSE: 0x02,
8
- BYTE: 0x03,
9
- INT_16: 0x04,
10
- INT_32: 0x05,
11
- INT_64: 0x06,
12
- DOUBLE: 0x07,
13
- BINARY: 0x08,
14
- LIST: 0x09,
15
- SET: 0x0a,
16
- MAP: 0x0b,
17
- STRUCT: 0x0c,
18
- LIST_INT_16: (0x04 << 8) | 0x09,
19
- LIST_INT_32: (0x05 << 8) | 0x09,
20
- LIST_INT_64: (0x06 << 8) | 0x09,
21
- LIST_BINARY: (0x08 << 8) | 0x09,
22
- MAP_BINARY_BINARY: (0x88 << 8) | 0x0b,
23
- // internal!
24
- BOOLEAN: 0xa1,
13
+ STOP: 0x00,
14
+ TRUE: 0x01,
15
+ FALSE: 0x02,
16
+ BYTE: 0x03,
17
+ INT_16: 0x04,
18
+ INT_32: 0x05,
19
+ INT_64: 0x06,
20
+ DOUBLE: 0x07,
21
+ BINARY: 0x08,
22
+ LIST: 0x09,
23
+ SET: 0x0a,
24
+ MAP: 0x0b,
25
+ STRUCT: 0x0c,
26
+
27
+ // Common "packed" meta-types used by this project:
28
+ LIST_INT_16: (0x04 << 8) | 0x09,
29
+ LIST_INT_32: (0x05 << 8) | 0x09,
30
+ LIST_INT_64: (0x06 << 8) | 0x09,
31
+ LIST_BINARY: (0x08 << 8) | 0x09,
32
+ MAP_BINARY_BINARY: (0x88 << 8) | 0x0b,
33
+
34
+ // internal!
35
+ BOOLEAN: 0xa1,
25
36
  };
37
+
26
38
  function isThriftBoolean(type) {
27
- type &= 0x0f;
28
- return type === exports.ThriftTypes.TRUE || type === exports.ThriftTypes.FALSE || type === exports.ThriftTypes.BOOLEAN;
39
+ type &= 0x0f;
40
+ return (
41
+ type === exports.ThriftTypes.TRUE ||
42
+ type === exports.ThriftTypes.FALSE ||
43
+ type === exports.ThriftTypes.BOOLEAN
44
+ );
29
45
  }
30
46
  exports.isThriftBoolean = isThriftBoolean;
47
+
31
48
  exports.ThriftDescriptors = {
32
- boolean: (fieldName, field) => ({
33
- field,
34
- fieldName,
35
- type: exports.ThriftTypes.BOOLEAN,
36
- }),
37
- byte: (fieldName, field) => ({ field, fieldName, type: exports.ThriftTypes.BYTE }),
38
- int16: (fieldName, field) => ({
39
- field,
40
- fieldName,
41
- type: exports.ThriftTypes.INT_16,
42
- }),
43
- int32: (fieldName, field) => ({
44
- field,
45
- fieldName,
46
- type: exports.ThriftTypes.INT_32,
47
- }),
48
- int64: (fieldName, field) => ({
49
- field,
50
- fieldName,
51
- type: exports.ThriftTypes.INT_64,
52
- }),
53
- double: (fieldName, field) => ({
54
- field,
55
- fieldName,
56
- type: exports.ThriftTypes.DOUBLE,
57
- }),
58
- binary: (fieldName, field) => ({
59
- field,
60
- fieldName,
61
- type: exports.ThriftTypes.BINARY,
62
- }),
63
- listOfInt16: (fieldName, field) => ({
64
- field,
65
- fieldName,
66
- type: exports.ThriftTypes.LIST_INT_16,
67
- }),
68
- listOfInt32: (fieldName, field) => ({
69
- field,
70
- fieldName,
71
- type: exports.ThriftTypes.LIST_INT_32,
72
- }),
73
- listOfInt64: (fieldName, field) => ({
74
- field,
75
- fieldName,
76
- type: exports.ThriftTypes.LIST_INT_64,
77
- }),
78
- listOfBinary: (fieldName, field) => ({
79
- field,
80
- fieldName,
81
- type: exports.ThriftTypes.LIST_BINARY,
82
- }),
83
- mapBinaryBinary: (fieldName, field) => ({
84
- field,
85
- fieldName,
86
- type: exports.ThriftTypes.MAP_BINARY_BINARY,
87
- }),
88
- struct: (fieldName, field, descriptors) => ({
89
- field,
90
- fieldName,
91
- type: exports.ThriftTypes.STRUCT,
92
- structDescriptors: descriptors,
93
- }),
49
+ boolean: (fieldName, field) => ({ field, fieldName, type: exports.ThriftTypes.BOOLEAN }),
50
+ byte: (fieldName, field) => ({ field, fieldName, type: exports.ThriftTypes.BYTE }),
51
+ int16: (fieldName, field) => ({ field, fieldName, type: exports.ThriftTypes.INT_16 }),
52
+ int32: (fieldName, field) => ({ field, fieldName, type: exports.ThriftTypes.INT_32 }),
53
+ int64: (fieldName, field) => ({ field, fieldName, type: exports.ThriftTypes.INT_64 }),
54
+ double: (fieldName, field) => ({ field, fieldName, type: exports.ThriftTypes.DOUBLE }),
55
+ binary: (fieldName, field) => ({ field, fieldName, type: exports.ThriftTypes.BINARY }),
56
+ listOfInt16: (fieldName, field) => ({ field, fieldName, type: exports.ThriftTypes.LIST_INT_16 }),
57
+ listOfInt32: (fieldName, field) => ({ field, fieldName, type: exports.ThriftTypes.LIST_INT_32 }),
58
+ listOfInt64: (fieldName, field) => ({ field, fieldName, type: exports.ThriftTypes.LIST_INT_64 }),
59
+ listOfBinary: (fieldName, field) => ({ field, fieldName, type: exports.ThriftTypes.LIST_BINARY }),
60
+ mapBinaryBinary: (fieldName, field) => ({ field, fieldName, type: exports.ThriftTypes.MAP_BINARY_BINARY }),
61
+ struct: (fieldName, field, descriptors) => ({
62
+ field,
63
+ fieldName,
64
+ type: exports.ThriftTypes.STRUCT,
65
+ structDescriptors: descriptors,
66
+ }),
94
67
  };
68
+
69
+ function int64ToBigInt(i64) {
70
+ if (typeof i64 === "bigint") return i64;
71
+ if (typeof i64 === "number") return BigInt(i64);
72
+ if (typeof i64 === "string") return BigInt(i64);
73
+ if (i64 && typeof i64 === "object") {
74
+ // Support patterns: { int: bigint } (our reader), Long-like, etc.
75
+ if (typeof i64.int === "bigint") return i64.int;
76
+ if (typeof i64.toString === "function") return BigInt(i64.toString());
77
+ }
78
+ // Fallback (may throw)
79
+ return BigInt(i64);
80
+ }
81
+ exports.int64ToBigInt = int64ToBigInt;
82
+
83
+ function isSafeInt64(i64) {
84
+ const bi = int64ToBigInt(i64);
85
+ const max = BigInt(Number.MAX_SAFE_INTEGER);
86
+ const min = BigInt(Number.MIN_SAFE_INTEGER);
87
+ return bi <= max && bi >= min;
88
+ }
89
+ exports.isSafeInt64 = isSafeInt64;
90
+
91
+ /**
92
+ * Backward compatible: converts to Number (may lose precision if out of range).
93
+ */
95
94
  function int64ToNumber(i64) {
96
- if (typeof i64 === 'number')
97
- return i64;
98
- return Number(i64);
95
+ if (typeof i64 === "number") return i64;
96
+ // Preserve previous behavior (Number(...) fallback)
97
+ return Number(i64 && typeof i64 === "object" && "int" in i64 ? i64.int : i64);
99
98
  }
100
99
  exports.int64ToNumber = int64ToNumber;
101
- //# sourceMappingURL=thrift.js.map
100
+
101
+ /**
102
+ * Safe conversion: throws if out of safe range.
103
+ */
104
+ function int64ToNumberSafe(i64) {
105
+ const bi = int64ToBigInt(i64);
106
+ if (!isSafeInt64(bi)) {
107
+ throw new RangeError(`int64 out of safe Number range: ${bi.toString()}`);
108
+ }
109
+ return Number(bi);
110
+ }
111
+ exports.int64ToNumberSafe = int64ToNumberSafe;