@prosopo/provider 5.3.0 → 5.3.2
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/.turbo/turbo-build$colon$cjs.log +10 -10
- package/.turbo/turbo-build$colon$tsc.log +25 -25
- package/.turbo/turbo-build.log +11 -11
- package/CHANGELOG.md +28 -0
- package/dist/api/captcha/getFrictionlessCaptchaChallenge/handler.d.ts.map +1 -1
- package/dist/api/captcha/getFrictionlessCaptchaChallenge/handler.js +2 -1
- package/dist/api/captcha/getFrictionlessCaptchaChallenge/handler.js.map +1 -1
- package/dist/api/captcha/submitPoWCaptchaSolution.d.ts.map +1 -1
- package/dist/api/captcha/submitPoWCaptchaSolution.js +1 -1
- package/dist/api/captcha/submitPoWCaptchaSolution.js.map +1 -1
- package/dist/api/ja4.d.ts.map +1 -1
- package/dist/api/ja4.js +14 -147
- package/dist/api/ja4.js.map +1 -1
- package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/handler.cjs +2 -1
- package/dist/cjs/api/captcha/submitPoWCaptchaSolution.cjs +1 -1
- package/dist/cjs/api/ja4.cjs +13 -147
- package/dist/cjs/tasks/captchaManager.cjs +4 -2
- package/dist/cjs/tasks/detection/decodePayload.cjs +985 -1197
- package/dist/cjs/tasks/detection/decodePayload.js +1 -1
- package/dist/cjs/tasks/detection/getBotScore.cjs +2 -0
- package/dist/cjs/tasks/frictionless/frictionlessTasks.cjs +13 -5
- package/dist/cjs/tasks/imgCaptcha/imgCaptchaTasksUtils.cjs +19 -5
- package/dist/tasks/captchaManager.d.ts.map +1 -1
- package/dist/tasks/captchaManager.js +4 -2
- package/dist/tasks/captchaManager.js.map +1 -1
- package/dist/tasks/detection/decodePayload.d.ts +2 -2
- package/dist/tasks/detection/decodePayload.d.ts.map +1 -1
- package/dist/tasks/detection/decodePayload.js +1 -1
- package/dist/tasks/detection/decodePayload.js.map +1 -1
- package/dist/tasks/detection/getBotScore.d.ts +2 -0
- package/dist/tasks/detection/getBotScore.d.ts.map +1 -1
- package/dist/tasks/detection/getBotScore.js +4 -2
- package/dist/tasks/detection/getBotScore.js.map +1 -1
- package/dist/tasks/frictionless/frictionlessTasks.d.ts +2 -1
- package/dist/tasks/frictionless/frictionlessTasks.d.ts.map +1 -1
- package/dist/tasks/frictionless/frictionlessTasks.js +13 -5
- package/dist/tasks/frictionless/frictionlessTasks.js.map +1 -1
- package/dist/tasks/imgCaptcha/imgCaptchaTasksUtils.d.ts.map +1 -1
- package/dist/tasks/imgCaptcha/imgCaptchaTasksUtils.js +21 -6
- package/dist/tasks/imgCaptcha/imgCaptchaTasksUtils.js.map +1 -1
- package/dist/tests/unit/tasks/captchaManager.unit.test.js +33 -0
- package/dist/tests/unit/tasks/captchaManager.unit.test.js.map +1 -1
- package/dist/tests/unit/tasks/detection/getBotScore.unit.test.js +19 -0
- package/dist/tests/unit/tasks/detection/getBotScore.unit.test.js.map +1 -1
- package/dist/tests/unit/tasks/imgCaptcha/imgCaptchaTasksUtils.unit.test.js +30 -54
- package/dist/tests/unit/tasks/imgCaptcha/imgCaptchaTasksUtils.unit.test.js.map +1 -1
- package/package.json +16 -14
package/dist/api/ja4.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ja4.js","sourceRoot":"","sources":["../../src/api/ja4.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"ja4.js","sourceRoot":"","sources":["../../src/api/ja4.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAU5C,MAAM,GAAG,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC3C,MAAM,eAAe,GAAoB,CAAC,GAAG,EAAE;IAC9C,IAAI,CAAC;QACJ,OAAO,GAAG,CAAC,qBAAqB,CAAoB,CAAC;IACtD,CAAC;IAAC,MAAM,CAAC;QAIR,OAAO,GAAG,CAAC,2BAA2B,CAAoB,CAAC;IAC5D,CAAC;AACF,CAAC,CAAC,EAAE,CAAC;AACL,MAAM,kBAAkB,GAAG,eAAe,CAAC,YAAY,CAAC;AAExD,MAAM,OAAO,aAAc,SAAQ,KAAK;IACvC,YAAY,OAAe;QAC1B,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;IAC7B,CAAC;CACD;AAQD,MAAM,UAAU,YAAY,CAAC,IAAY;IACxC,IAAI,CAAC;QACJ,OAAO,kBAAkB,CAAC,IAAI,CAAC,CAAC;IACjC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,MAAM,IAAI,aAAa,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IAC3E,CAAC;AACF,CAAC"}
|
|
@@ -231,7 +231,7 @@ var handler_default = (env, userAccessRulesStorage) => async (req, res, next) =>
|
|
|
231
231
|
tasks.frictionlessManager.isValidRequest(clientRecord, _prosopo_types.CaptchaType.frictionless, env),
|
|
232
232
|
tasks.frictionlessManager.getPrioritisedAccessPolicies(userAccessRulesStorage, dapp, userScope)
|
|
233
233
|
]);
|
|
234
|
-
const { baseBotScore: rawBaseBotScore, timestamp: rawTimestamp, userId: rawUserId, userAgent: rawUserAgent, webView, iFrame, decryptedHeadHash, decryptionFailed: rawDecryptionFailed, triggeredDetectors, shadowDomPenalty, entropyMathRandomFingerprint, entropyCryptoFingerprint, entropyWallClockOffsetMs, entropyMathRandomFirst, g, sw, md, bn, fs, bundleId } = decryptedPayload;
|
|
234
|
+
const { baseBotScore: rawBaseBotScore, timestamp: rawTimestamp, userId: rawUserId, userAgent: rawUserAgent, webView, iFrame, decryptedHeadHash, decryptionFailed: rawDecryptionFailed, triggeredDetectors, shadowDomPenalty, entropyMathRandomFingerprint, entropyCryptoFingerprint, entropyWallClockOffsetMs, entropyMathRandomFirst, g, sw, md, bn, fs, s, bundleId } = decryptedPayload;
|
|
235
235
|
const detectorOverride = process.env.PROSOPO_TEST_FRICTIONLESS_DETECTOR_OVERRIDE === "1";
|
|
236
236
|
const baseBotScore = detectorOverride ? 0 : rawBaseBotScore;
|
|
237
237
|
const timestamp = detectorOverride ? Date.now() : rawTimestamp;
|
|
@@ -296,6 +296,7 @@ var handler_default = (env, userAccessRulesStorage) => async (req, res, next) =>
|
|
|
296
296
|
...md !== void 0 && { md },
|
|
297
297
|
...bn !== void 0 && { bn },
|
|
298
298
|
...fs !== void 0 && { fs },
|
|
299
|
+
...s !== void 0 && { s },
|
|
299
300
|
...req.tcpToChelloUs !== void 0 && { tcpToChelloUs: req.tcpToChelloUs },
|
|
300
301
|
...req.chelloToHandshakeUs !== void 0 && { chelloToHandshakeUs: req.chelloToHandshakeUs }
|
|
301
302
|
});
|
|
@@ -133,7 +133,7 @@ var buildEscalation = async (tasks, result, challenge, handshakeTiming) => {
|
|
|
133
133
|
const routed = result.routingOutput;
|
|
134
134
|
const escalatedType = require_puzzleRenderer.downgradePuzzleIfUnavailable(routed.captchaType);
|
|
135
135
|
const selectionReason = routed.reason ?? originSession.reason;
|
|
136
|
-
const newSession = await tasks.frictionlessManager.createSession(originSession.token, originSession.score, originSession.threshold, originSession.scoreComponents, originSession.ipAddress, escalatedType, originSession.siteKey ?? powRecord.dappAccount, escalatedType === _prosopo_types.CaptchaType.image ? routed.solvedImagesCount ?? originSession.solvedImagesCount : void 0, void 0, originSession.userSitekeyIpHash, originSession.webView, originSession.iFrame, originSession.decryptedHeadHash, selectionReason, void 0, void 0, originSession.ipInfo, originSession.headers, originSession.mode, originSession.simdReadings, originSession.entropyMathRandomFingerprint, originSession.entropyCryptoFingerprint, originSession.entropyWallClockOffsetMs, originSession.entropyMathRandomFirst, originSession.bundleId, originSession.currentUrl, handshakeTiming?.tcpToChelloUs, handshakeTiming?.chelloToHandshakeUs, true, originSession.iframeUrl, originSession.isProtect, originSession.sessionId, originSession.g, void 0, originSession.i, originSession.sw, originSession.md, originSession.bn, originSession.fs);
|
|
136
|
+
const newSession = await tasks.frictionlessManager.createSession(originSession.token, originSession.score, originSession.threshold, originSession.scoreComponents, originSession.ipAddress, escalatedType, originSession.siteKey ?? powRecord.dappAccount, escalatedType === _prosopo_types.CaptchaType.image ? routed.solvedImagesCount ?? originSession.solvedImagesCount : void 0, void 0, originSession.userSitekeyIpHash, originSession.webView, originSession.iFrame, originSession.decryptedHeadHash, selectionReason, void 0, void 0, originSession.ipInfo, originSession.headers, originSession.mode, originSession.simdReadings, originSession.entropyMathRandomFingerprint, originSession.entropyCryptoFingerprint, originSession.entropyWallClockOffsetMs, originSession.entropyMathRandomFirst, originSession.bundleId, originSession.currentUrl, handshakeTiming?.tcpToChelloUs, handshakeTiming?.chelloToHandshakeUs, true, originSession.iframeUrl, originSession.isProtect, originSession.sessionId, originSession.g, void 0, originSession.i, originSession.sw, originSession.md, originSession.bn, originSession.fs, originSession.s);
|
|
137
137
|
if (tasks.writeQueue) await tasks.writeQueue.cacheSessionEscalation(powRecord.sessionId, newSession.sessionId);
|
|
138
138
|
return {
|
|
139
139
|
captchaType: escalatedType,
|
package/dist/cjs/api/ja4.cjs
CHANGED
|
@@ -1,38 +1,12 @@
|
|
|
1
|
-
let node_crypto = require("node:crypto");
|
|
2
1
|
//#region src/api/ja4.ts
|
|
3
|
-
var
|
|
4
|
-
var
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
var EXT_ALPN = 16;
|
|
10
|
-
var EXT_SUPPORTED_VERSIONS = 43;
|
|
11
|
-
function isGrease(id) {
|
|
12
|
-
return (id & 3855) === 2570 && id >> 8 === (id & 255);
|
|
13
|
-
}
|
|
14
|
-
function hash12(input) {
|
|
15
|
-
return (0, node_crypto.createHash)("sha256").update(input).digest("hex").slice(0, 12);
|
|
16
|
-
}
|
|
17
|
-
function formatIdList(ids) {
|
|
18
|
-
return ids.map((id) => id.toString(16).padStart(4, "0")).join(",");
|
|
19
|
-
}
|
|
20
|
-
function alpnChar(b) {
|
|
21
|
-
if (b >= 48 && b <= 57 || b >= 65 && b <= 90 || b >= 97 && b <= 122) return String.fromCharCode(b);
|
|
22
|
-
return b.toString(16).padStart(2, "0");
|
|
23
|
-
}
|
|
24
|
-
function alpnFirstLast(protocol) {
|
|
25
|
-
return [protocol.length > 0 ? alpnChar(protocol.readUInt8(0)) : "0", protocol.length > 1 ? alpnChar(protocol.readUInt8(protocol.length - 1)) : "0"];
|
|
26
|
-
}
|
|
27
|
-
function tlsVersionStr(version) {
|
|
28
|
-
switch (version) {
|
|
29
|
-
case 769: return "10";
|
|
30
|
-
case 770: return "11";
|
|
31
|
-
case 771: return "12";
|
|
32
|
-
case 772: return "13";
|
|
33
|
-
default: return "00";
|
|
2
|
+
var req = (0, require("node:module").createRequire)(require("url").pathToFileURL(__filename).href);
|
|
3
|
+
var nativeCalculateJa4 = (() => {
|
|
4
|
+
try {
|
|
5
|
+
return req("@prosopo/native-ja4");
|
|
6
|
+
} catch {
|
|
7
|
+
return req("./prosopo-native-ja4.node");
|
|
34
8
|
}
|
|
35
|
-
}
|
|
9
|
+
})().calculateJa4;
|
|
36
10
|
var Ja4ParseError = class extends Error {
|
|
37
11
|
constructor(message) {
|
|
38
12
|
super(message);
|
|
@@ -42,123 +16,15 @@ var Ja4ParseError = class extends Error {
|
|
|
42
16
|
/**
|
|
43
17
|
* Compute a JA4 TLS fingerprint from raw ClientHello bytes.
|
|
44
18
|
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
* 4-byte handshake header (msg_type + msg_len)
|
|
48
|
-
* ClientHello body
|
|
49
|
-
*
|
|
50
|
-
* Throws Ja4ParseError for structural failures in the TLS record or handshake
|
|
51
|
-
* header (wrong record type, wrong message type, declared lengths exceeding the
|
|
52
|
-
* buffer, odd cipher-suite length).
|
|
53
|
-
*
|
|
54
|
-
* Extension-level malformation (truncated extension body, malformed ALPN
|
|
55
|
-
* entries) is handled defensively: parsing stops at the first bad entry and
|
|
56
|
-
* a fingerprint is produced from whatever was successfully parsed. This matches
|
|
57
|
-
* the Rust reference implementation and avoids turning extension parse errors
|
|
58
|
-
* into connection-fingerprinting failures.
|
|
19
|
+
* Delegates to the Rust implementation in @prosopo/native-ja4. Structural
|
|
20
|
+
* TLS parse failures are rethrown as Ja4ParseError.
|
|
59
21
|
*/
|
|
60
22
|
function calculateJa4(data) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
if (recordType !== HANDSHAKE_RECORD_TYPE) throw new Ja4ParseError(`Invalid record type: 0x${recordType.toString(16)}`);
|
|
66
|
-
offset += 2;
|
|
67
|
-
const recordLen = data.readUInt16BE(offset);
|
|
68
|
-
offset += 2;
|
|
69
|
-
if (data.length < RECORD_HEADER_LENGTH + recordLen) throw new Ja4ParseError("Record length exceeds buffer");
|
|
70
|
-
data = data.subarray(0, RECORD_HEADER_LENGTH + recordLen);
|
|
71
|
-
if (data.length < RECORD_HEADER_LENGTH + HANDSHAKE_HEADER_LENGTH) throw new Ja4ParseError("Record too short for handshake header");
|
|
72
|
-
const msgType = data.readUInt8(offset);
|
|
73
|
-
offset += 1;
|
|
74
|
-
if (msgType !== HANDSHAKE_MESSAGE_TYPE) throw new Ja4ParseError(`Not a ClientHello: handshake type 0x${msgType.toString(16)}`);
|
|
75
|
-
const msgLen = data.readUIntBE(offset, 3);
|
|
76
|
-
offset += 3;
|
|
77
|
-
if (data.length - offset < msgLen) throw new Ja4ParseError("Handshake message length exceeds record");
|
|
78
|
-
data = data.subarray(0, offset + msgLen);
|
|
79
|
-
if (data.length - offset < 35) throw new Ja4ParseError("ClientHello body too short");
|
|
80
|
-
let clientVersion = data.readUInt16BE(offset);
|
|
81
|
-
offset += 2;
|
|
82
|
-
offset += 32;
|
|
83
|
-
const sessionIdLen = data.readUInt8(offset);
|
|
84
|
-
offset += 1;
|
|
85
|
-
if (data.length - offset < sessionIdLen) throw new Ja4ParseError("Truncated at session ID");
|
|
86
|
-
offset += sessionIdLen;
|
|
87
|
-
if (data.length - offset < 2) throw new Ja4ParseError("Truncated at cipher suites length");
|
|
88
|
-
const cipherSuitesLen = data.readUInt16BE(offset);
|
|
89
|
-
offset += 2;
|
|
90
|
-
if (data.length - offset < cipherSuitesLen) throw new Ja4ParseError("Truncated at cipher suites");
|
|
91
|
-
if (cipherSuitesLen % 2 !== 0) throw new Ja4ParseError("Cipher suites length is not a multiple of 2");
|
|
92
|
-
const cipherSuites = [];
|
|
93
|
-
for (let i = 0; i < cipherSuitesLen; i += 2) cipherSuites.push(data.readUInt16BE(offset + i));
|
|
94
|
-
offset += cipherSuitesLen;
|
|
95
|
-
if (data.length - offset < 1) throw new Ja4ParseError("Truncated at compression methods");
|
|
96
|
-
const compressionLen = data.readUInt8(offset);
|
|
97
|
-
offset += 1;
|
|
98
|
-
if (data.length - offset < compressionLen) throw new Ja4ParseError("Truncated at compression methods data");
|
|
99
|
-
offset += compressionLen;
|
|
100
|
-
if (data.length - offset < 2) throw new Ja4ParseError("Truncated at extensions length");
|
|
101
|
-
const extensionsLen = data.readUInt16BE(offset);
|
|
102
|
-
offset += 2;
|
|
103
|
-
if (data.length - offset < extensionsLen) throw new Ja4ParseError("Truncated at extensions");
|
|
104
|
-
const extIds = [];
|
|
105
|
-
const sigAlgorithms = [];
|
|
106
|
-
const alpnProtocols = [];
|
|
107
|
-
let extOffset = offset;
|
|
108
|
-
const extEnd = offset + extensionsLen;
|
|
109
|
-
while (extOffset + 4 <= extEnd) {
|
|
110
|
-
const extId = data.readUInt16BE(extOffset);
|
|
111
|
-
extOffset += 2;
|
|
112
|
-
const extLen = data.readUInt16BE(extOffset);
|
|
113
|
-
extOffset += 2;
|
|
114
|
-
if (extOffset + extLen > extEnd) break;
|
|
115
|
-
const extData = data.subarray(extOffset, extOffset + extLen);
|
|
116
|
-
extOffset += extLen;
|
|
117
|
-
extIds.push(extId);
|
|
118
|
-
if (extId === EXT_SUPPORTED_VERSIONS) {
|
|
119
|
-
if (extData.length >= 1) {
|
|
120
|
-
const listLen = extData.readUInt8(0);
|
|
121
|
-
let best = 0;
|
|
122
|
-
for (let i = 1; i + 1 < extData.length && i + 1 <= listLen; i += 2) {
|
|
123
|
-
const v = extData.readUInt16BE(i);
|
|
124
|
-
if (!isGrease(v) && v > best) best = v;
|
|
125
|
-
}
|
|
126
|
-
if (best > 0) clientVersion = best;
|
|
127
|
-
}
|
|
128
|
-
} else if (extId === EXT_SIGNATURE_ALGORITHMS) {
|
|
129
|
-
if (extData.length >= 2) {
|
|
130
|
-
const algLen = extData.readUInt16BE(0);
|
|
131
|
-
for (let i = 2; i + 1 < extData.length && i + 1 < algLen + 2; i += 2) {
|
|
132
|
-
const alg = extData.readUInt16BE(i);
|
|
133
|
-
if (!isGrease(alg)) sigAlgorithms.push(alg);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
} else if (extId === EXT_ALPN) {
|
|
137
|
-
if (extData.length >= 2) {
|
|
138
|
-
const listLen = extData.readUInt16BE(0);
|
|
139
|
-
const listEnd = Math.min(extData.length, 2 + listLen);
|
|
140
|
-
let alpnOff = 2;
|
|
141
|
-
while (alpnOff < listEnd) {
|
|
142
|
-
const protoLen = extData.readUInt8(alpnOff);
|
|
143
|
-
alpnOff += 1;
|
|
144
|
-
if (alpnOff + protoLen > listEnd) break;
|
|
145
|
-
alpnProtocols.push(extData.subarray(alpnOff, alpnOff + protoLen));
|
|
146
|
-
alpnOff += protoLen;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
}
|
|
23
|
+
try {
|
|
24
|
+
return nativeCalculateJa4(data);
|
|
25
|
+
} catch (err) {
|
|
26
|
+
throw new Ja4ParseError(err instanceof Error ? err.message : String(err));
|
|
150
27
|
}
|
|
151
|
-
const versionStr = tlsVersionStr(clientVersion);
|
|
152
|
-
const sni = extIds.includes(EXT_SERVER_NAME) ? "d" : "i";
|
|
153
|
-
const firstAlpn = alpnProtocols[0];
|
|
154
|
-
const [alpnFirst, alpnLast] = firstAlpn ? alpnFirstLast(firstAlpn) : ["0", "0"];
|
|
155
|
-
const nonGreaseCiphers = cipherSuites.filter((c) => !isGrease(c));
|
|
156
|
-
const nonGreaseExts = extIds.filter((e) => !isGrease(e));
|
|
157
|
-
const firstChunk = `t${versionStr}${sni}${Math.min(99, nonGreaseCiphers.length).toString().padStart(2, "0")}${Math.min(99, nonGreaseExts.length).toString().padStart(2, "0")}${alpnFirst}${alpnLast}`;
|
|
158
|
-
const cipherHash = hash12(formatIdList([...nonGreaseCiphers].sort((a, b) => a - b)));
|
|
159
|
-
const extStr = formatIdList([...nonGreaseExts.filter((e) => e !== EXT_SERVER_NAME && e !== EXT_ALPN)].sort((a, b) => a - b));
|
|
160
|
-
const sigAlgStr = formatIdList(sigAlgorithms);
|
|
161
|
-
return `${firstChunk}_${cipherHash}_${hash12(sigAlgStr.length > 0 ? `${extStr}_${sigAlgStr}` : extStr)}`;
|
|
162
28
|
}
|
|
163
29
|
//#endregion
|
|
164
30
|
exports.Ja4ParseError = Ja4ParseError;
|
|
@@ -108,7 +108,8 @@ var CaptchaManager = class CaptchaManager {
|
|
|
108
108
|
const needsMd = session.md === void 0;
|
|
109
109
|
const needsBn = session.bn === void 0;
|
|
110
110
|
const needsFs = session.fs === void 0;
|
|
111
|
-
|
|
111
|
+
const needsS = session.s === void 0;
|
|
112
|
+
if (!needsSimd && !needsDns && !needsEntropyMath && !needsEntropyCrypto && !needsEntropyWall && !needsEntropyFirst && !needsG && !needsI && !needsSw && !needsMd && !needsBn && !needsFs && !needsS) return session;
|
|
112
113
|
const origin = await this.db.getSessionRecordBySessionId(session.originSessionId);
|
|
113
114
|
if (!origin) return session;
|
|
114
115
|
return {
|
|
@@ -124,7 +125,8 @@ var CaptchaManager = class CaptchaManager {
|
|
|
124
125
|
...needsSw && origin.sw !== void 0 && { sw: origin.sw },
|
|
125
126
|
...needsMd && origin.md !== void 0 && { md: origin.md },
|
|
126
127
|
...needsBn && origin.bn !== void 0 && { bn: origin.bn },
|
|
127
|
-
...needsFs && origin.fs !== void 0 && { fs: origin.fs }
|
|
128
|
+
...needsFs && origin.fs !== void 0 && { fs: origin.fs },
|
|
129
|
+
...needsS && origin.s !== void 0 && { s: origin.s }
|
|
128
130
|
};
|
|
129
131
|
}
|
|
130
132
|
/**
|