zapo-js 1.8.2 → 1.10.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.
- package/README.md +14 -13
- package/dist/auth/credentials-flow.js +2 -3
- package/dist/client/WaClient.d.ts +4 -0
- package/dist/client/WaClient.js +5 -1
- package/dist/client/coordinators/WaMessageCoordinator.d.ts +11 -4
- package/dist/client/coordinators/WaMessageDispatchCoordinator.d.ts +11 -0
- package/dist/client/coordinators/WaMessageDispatchCoordinator.js +15 -5
- package/dist/client/messaging/ignore-key.d.ts +2 -1
- package/dist/client/messaging/ignore-key.js +24 -3
- package/dist/esm/auth/credentials-flow.js +3 -4
- package/dist/esm/client/WaClient.js +5 -1
- package/dist/esm/client/coordinators/WaMessageDispatchCoordinator.js +14 -5
- package/dist/esm/client/messaging/ignore-key.js +25 -4
- package/dist/esm/message/encode/content.js +4 -1
- package/dist/esm/store/cache/identity.cache.js +30 -5
- package/dist/esm/store/cache/privacy-token.cache.js +30 -5
- package/dist/esm/store/cache/sender-key.cache.js +38 -7
- package/dist/esm/store/cache/session.cache.js +30 -4
- package/dist/esm/store/createStore.js +39 -4
- package/dist/esm/store/memory/identity.store.js +28 -4
- package/dist/esm/store/memory/privacy-token.store.js +24 -7
- package/dist/esm/store/memory/sender-key.store.js +36 -7
- package/dist/esm/store/memory/session.store.js +41 -7
- package/dist/esm/transport/node/WaMobileTcpSocket.js +69 -9
- package/dist/esm/transport/noise/WaMobileClientPayload.js +27 -7
- package/dist/esm/transport/proxy.js +83 -0
- package/dist/esm/util/collections.js +94 -0
- package/dist/esm/util/index.js +2 -2
- package/dist/message/encode/content.js +4 -1
- package/dist/message/primitives/media-retry.d.ts +11 -0
- package/dist/store/cache/identity.cache.d.ts +6 -4
- package/dist/store/cache/identity.cache.js +30 -5
- package/dist/store/cache/privacy-token.cache.d.ts +6 -4
- package/dist/store/cache/privacy-token.cache.js +30 -5
- package/dist/store/cache/sender-key.cache.d.ts +5 -3
- package/dist/store/cache/sender-key.cache.js +38 -7
- package/dist/store/cache/session.cache.d.ts +6 -3
- package/dist/store/cache/session.cache.js +30 -4
- package/dist/store/createStore.js +39 -4
- package/dist/store/memory/identity.store.d.ts +7 -1
- package/dist/store/memory/identity.store.js +28 -4
- package/dist/store/memory/privacy-token.store.d.ts +7 -1
- package/dist/store/memory/privacy-token.store.js +24 -7
- package/dist/store/memory/sender-key.store.d.ts +12 -2
- package/dist/store/memory/sender-key.store.js +36 -7
- package/dist/store/memory/session.store.d.ts +7 -1
- package/dist/store/memory/session.store.js +41 -7
- package/dist/store/types.d.ts +43 -4
- package/dist/transport/node/WaMobileTcpSocket.d.ts +3 -1
- package/dist/transport/node/WaMobileTcpSocket.js +68 -8
- package/dist/transport/noise/WaMobileClientPayload.d.ts +22 -0
- package/dist/transport/noise/WaMobileClientPayload.js +27 -7
- package/dist/transport/proxy.d.ts +24 -0
- package/dist/transport/proxy.js +85 -0
- package/dist/util/collections.d.ts +46 -0
- package/dist/util/collections.js +98 -0
- package/dist/util/index.d.ts +2 -2
- package/dist/util/index.js +3 -1
- package/package.json +1 -1
package/dist/transport/proxy.js
CHANGED
|
@@ -5,6 +5,9 @@ exports.isProxyAgent = isProxyAgent;
|
|
|
5
5
|
exports.isProxyTransport = isProxyTransport;
|
|
6
6
|
exports.toProxyDispatcher = toProxyDispatcher;
|
|
7
7
|
exports.toProxyAgent = toProxyAgent;
|
|
8
|
+
exports.toTcpProxyEndpoint = toTcpProxyEndpoint;
|
|
9
|
+
exports.assertTcpProxySupported = assertTcpProxySupported;
|
|
10
|
+
const bytes_1 = require("../util/bytes");
|
|
8
11
|
/** Type guard for an undici-style proxy dispatcher (has `dispatch` method). */
|
|
9
12
|
function isProxyDispatcher(value) {
|
|
10
13
|
return (typeof value === 'object' &&
|
|
@@ -37,3 +40,85 @@ function toProxyAgent(proxy) {
|
|
|
37
40
|
}
|
|
38
41
|
return proxy;
|
|
39
42
|
}
|
|
43
|
+
const TCP_PROXY_HINT = 'socketOptions.proxy.ws must hold an http.Agent-style proxy pointing at an http: url (e.g. new HttpProxyAgent("http://host:port")) to tunnel raw TCP';
|
|
44
|
+
/**
|
|
45
|
+
* Resolves the proxy endpoint a raw TCP transport tunnels through with HTTP
|
|
46
|
+
* CONNECT.
|
|
47
|
+
*
|
|
48
|
+
* Returns `undefined` only when no proxy is configured. Proxy shapes the tunnel
|
|
49
|
+
* cannot honour throw instead of resolving to `undefined`, because dropping one
|
|
50
|
+
* silently would dial the destination directly – the opposite of what a
|
|
51
|
+
* deployment that pins its egress to a proxy asked for.
|
|
52
|
+
*/
|
|
53
|
+
function toTcpProxyEndpoint(proxy) {
|
|
54
|
+
if (!proxy) {
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
if (!isProxyAgent(proxy)) {
|
|
58
|
+
if (isProxyDispatcher(proxy)) {
|
|
59
|
+
throw new Error(`undici-style proxy dispatchers cannot tunnel raw TCP – ${TCP_PROXY_HINT}`);
|
|
60
|
+
}
|
|
61
|
+
throw new Error(`unsupported proxy transport – ${TCP_PROXY_HINT}`);
|
|
62
|
+
}
|
|
63
|
+
const url = readAgentProxyUrl(proxy);
|
|
64
|
+
if (url.protocol !== 'http:') {
|
|
65
|
+
throw new Error(`proxy protocol ${url.protocol} is not supported by the raw TCP tunnel – ${TCP_PROXY_HINT}`);
|
|
66
|
+
}
|
|
67
|
+
const username = decodeProxyUserInfo(url.username, 'username');
|
|
68
|
+
const password = decodeProxyUserInfo(url.password, 'password');
|
|
69
|
+
return {
|
|
70
|
+
hostname: url.hostname,
|
|
71
|
+
port: url.port ? Number(url.port) : 80,
|
|
72
|
+
authorization: username || password
|
|
73
|
+
? `Basic ${(0, bytes_1.bytesToBase64)(bytes_1.TEXT_ENCODER.encode(`${username}:${password}`))}`
|
|
74
|
+
: undefined
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
/** Throws when {@link toTcpProxyEndpoint} cannot honour `proxy`. */
|
|
78
|
+
function assertTcpProxySupported(proxy) {
|
|
79
|
+
toTcpProxyEndpoint(proxy);
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Decodes one userinfo component of a proxy url. `new URL()` keeps invalid
|
|
83
|
+
* percent escapes verbatim, so a credential holding a literal `%` would reach
|
|
84
|
+
* `decodeURIComponent` and throw a bare `URIError`. The value never reaches the
|
|
85
|
+
* message – only the field name does.
|
|
86
|
+
*/
|
|
87
|
+
function decodeProxyUserInfo(value, field) {
|
|
88
|
+
try {
|
|
89
|
+
return decodeURIComponent(value);
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
throw new Error(`proxy url ${field} contains a malformed percent escape`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Reads the proxy url an `http.Agent`-style proxy exposes. Every `*-proxy-agent`
|
|
97
|
+
* package keeps it on `.proxy`, but only the http/https ones store a `URL`
|
|
98
|
+
* there – `socks-proxy-agent` stores a parsed `{ host, port, type }` endpoint.
|
|
99
|
+
*/
|
|
100
|
+
function readAgentProxyUrl(agent) {
|
|
101
|
+
const value = agent.proxy;
|
|
102
|
+
if (value instanceof URL) {
|
|
103
|
+
return value;
|
|
104
|
+
}
|
|
105
|
+
if (typeof value === 'string') {
|
|
106
|
+
try {
|
|
107
|
+
return new URL(value);
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
throw new Error(`proxy agent exposes an unparseable proxy url ${JSON.stringify(value)}`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (isSocksProxyEndpoint(value)) {
|
|
114
|
+
throw new Error(`socks proxy agents cannot tunnel raw TCP – ${TCP_PROXY_HINT}`);
|
|
115
|
+
}
|
|
116
|
+
throw new Error(`proxy agent exposes no proxy url – ${TCP_PROXY_HINT}`);
|
|
117
|
+
}
|
|
118
|
+
/** Matches the `{ host, port, type }` endpoint `socks-proxy-agent` keeps on `.proxy`. */
|
|
119
|
+
function isSocksProxyEndpoint(value) {
|
|
120
|
+
return (typeof value === 'object' &&
|
|
121
|
+
value !== null &&
|
|
122
|
+
typeof value.host === 'string' &&
|
|
123
|
+
typeof value.port === 'number');
|
|
124
|
+
}
|
|
@@ -8,4 +8,50 @@ export declare function createPeriodicCleanup(ttlMs: number, run: () => void): P
|
|
|
8
8
|
* `defaultLimit` when `limit` is undefined. Throws on invalid input.
|
|
9
9
|
*/
|
|
10
10
|
export declare function normalizeQueryLimit(limit: number | undefined, defaultLimit: number): number;
|
|
11
|
+
/**
|
|
12
|
+
* Sweep period shared by a set of idle TTLs: the smallest `ttlMs / 2`,
|
|
13
|
+
* clamped to [1 s, 60 s].
|
|
14
|
+
*/
|
|
15
|
+
export declare function resolveIdleSweepPeriodMs(ttlsMs: readonly number[]): number;
|
|
16
|
+
/**
|
|
17
|
+
* Coarse clock for idle expiry: one `unref`'d interval bumps `tick` and runs
|
|
18
|
+
* every registered sweep, and only while a sweep is registered. Wall-clock
|
|
19
|
+
* steps never move `tick`.
|
|
20
|
+
*/
|
|
21
|
+
export declare class IdleSweepClock {
|
|
22
|
+
readonly periodMs: number;
|
|
23
|
+
private currentTick;
|
|
24
|
+
private readonly sweeps;
|
|
25
|
+
private timer;
|
|
26
|
+
constructor(periodMs: number);
|
|
27
|
+
get tick(): number;
|
|
28
|
+
/** Runs `sweep` on every tick until the returned function is called. */
|
|
29
|
+
register(sweep: (tick: number) => void): () => void;
|
|
30
|
+
private readonly advance;
|
|
31
|
+
}
|
|
32
|
+
export interface IdleExpiry {
|
|
33
|
+
readonly clock: IdleSweepClock;
|
|
34
|
+
readonly ttlMs: number;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Idle expiry and LRU order for the entries of `data`. A touch moves the key
|
|
38
|
+
* to the young end of the index and of `data`, at most once per tick, so the
|
|
39
|
+
* sweep stops at the first live key and `data` iterates least recently used
|
|
40
|
+
* first. A key expires after `ceil(ttlMs / periodMs) + 1` untouched ticks:
|
|
41
|
+
* idle for at least `ttlMs` and, timer lateness aside, under `ttlMs + 2 * periodMs`.
|
|
42
|
+
*/
|
|
43
|
+
export declare class IdleExpiryIndex<K, V> {
|
|
44
|
+
private readonly data;
|
|
45
|
+
private readonly clock;
|
|
46
|
+
private readonly idleTicks;
|
|
47
|
+
private readonly touchedAt;
|
|
48
|
+
private readonly unregister;
|
|
49
|
+
constructor(data: Map<K, V>, expiry: IdleExpiry);
|
|
50
|
+
touch(key: K): void;
|
|
51
|
+
delete(key: K): void;
|
|
52
|
+
clear(): void;
|
|
53
|
+
/** Stops sweeping; the entries stay until the owner clears them. */
|
|
54
|
+
detach(): void;
|
|
55
|
+
private readonly sweep;
|
|
56
|
+
}
|
|
11
57
|
export declare function setBoundedMapEntry<K, V>(map: Map<K, V>, key: K, value: V, maxEntries: number, onEvict?: (key: K, value: V) => void): void;
|
package/dist/util/collections.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.IdleExpiryIndex = exports.IdleSweepClock = void 0;
|
|
3
4
|
exports.resolveCleanupIntervalMs = resolveCleanupIntervalMs;
|
|
4
5
|
exports.createPeriodicCleanup = createPeriodicCleanup;
|
|
5
6
|
exports.normalizeQueryLimit = normalizeQueryLimit;
|
|
7
|
+
exports.resolveIdleSweepPeriodMs = resolveIdleSweepPeriodMs;
|
|
6
8
|
exports.setBoundedMapEntry = setBoundedMapEntry;
|
|
7
9
|
function resolveCleanupIntervalMs(ttlMs, maxIntervalMs = 60000) {
|
|
8
10
|
if (ttlMs <= 1000) {
|
|
@@ -30,6 +32,102 @@ function normalizeQueryLimit(limit, defaultLimit) {
|
|
|
30
32
|
}
|
|
31
33
|
return limit;
|
|
32
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* Sweep period shared by a set of idle TTLs: the smallest `ttlMs / 2`,
|
|
37
|
+
* clamped to [1 s, 60 s].
|
|
38
|
+
*/
|
|
39
|
+
function resolveIdleSweepPeriodMs(ttlsMs) {
|
|
40
|
+
let periodMs = 60000;
|
|
41
|
+
for (let i = 0; i < ttlsMs.length; i += 1) {
|
|
42
|
+
periodMs = Math.min(periodMs, Math.max(1000, Math.floor(ttlsMs[i] / 2)));
|
|
43
|
+
}
|
|
44
|
+
return periodMs;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Coarse clock for idle expiry: one `unref`'d interval bumps `tick` and runs
|
|
48
|
+
* every registered sweep, and only while a sweep is registered. Wall-clock
|
|
49
|
+
* steps never move `tick`.
|
|
50
|
+
*/
|
|
51
|
+
class IdleSweepClock {
|
|
52
|
+
constructor(periodMs) {
|
|
53
|
+
this.advance = () => {
|
|
54
|
+
this.currentTick += 1;
|
|
55
|
+
for (const sweep of this.sweeps)
|
|
56
|
+
sweep(this.currentTick);
|
|
57
|
+
};
|
|
58
|
+
this.periodMs = periodMs;
|
|
59
|
+
this.currentTick = 0;
|
|
60
|
+
this.sweeps = new Set();
|
|
61
|
+
this.timer = null;
|
|
62
|
+
}
|
|
63
|
+
get tick() {
|
|
64
|
+
return this.currentTick;
|
|
65
|
+
}
|
|
66
|
+
/** Runs `sweep` on every tick until the returned function is called. */
|
|
67
|
+
register(sweep) {
|
|
68
|
+
this.sweeps.add(sweep);
|
|
69
|
+
if (this.timer === null) {
|
|
70
|
+
this.timer = setInterval(this.advance, this.periodMs);
|
|
71
|
+
this.timer.unref?.();
|
|
72
|
+
}
|
|
73
|
+
return () => {
|
|
74
|
+
this.sweeps.delete(sweep);
|
|
75
|
+
if (this.sweeps.size > 0 || this.timer === null)
|
|
76
|
+
return;
|
|
77
|
+
clearInterval(this.timer);
|
|
78
|
+
this.timer = null;
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
exports.IdleSweepClock = IdleSweepClock;
|
|
83
|
+
/**
|
|
84
|
+
* Idle expiry and LRU order for the entries of `data`. A touch moves the key
|
|
85
|
+
* to the young end of the index and of `data`, at most once per tick, so the
|
|
86
|
+
* sweep stops at the first live key and `data` iterates least recently used
|
|
87
|
+
* first. A key expires after `ceil(ttlMs / periodMs) + 1` untouched ticks:
|
|
88
|
+
* idle for at least `ttlMs` and, timer lateness aside, under `ttlMs + 2 * periodMs`.
|
|
89
|
+
*/
|
|
90
|
+
class IdleExpiryIndex {
|
|
91
|
+
constructor(data, expiry) {
|
|
92
|
+
this.sweep = (tick) => {
|
|
93
|
+
const cutoff = tick - this.idleTicks;
|
|
94
|
+
for (const [key, touchedAt] of this.touchedAt) {
|
|
95
|
+
if (touchedAt > cutoff)
|
|
96
|
+
return;
|
|
97
|
+
this.touchedAt.delete(key);
|
|
98
|
+
this.data.delete(key);
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
this.data = data;
|
|
102
|
+
this.clock = expiry.clock;
|
|
103
|
+
this.idleTicks = Math.ceil(expiry.ttlMs / expiry.clock.periodMs) + 1;
|
|
104
|
+
this.touchedAt = new Map();
|
|
105
|
+
this.unregister = expiry.clock.register(this.sweep);
|
|
106
|
+
}
|
|
107
|
+
touch(key) {
|
|
108
|
+
const tick = this.clock.tick;
|
|
109
|
+
if (this.touchedAt.get(key) === tick)
|
|
110
|
+
return;
|
|
111
|
+
this.touchedAt.delete(key);
|
|
112
|
+
this.touchedAt.set(key, tick);
|
|
113
|
+
const value = this.data.get(key);
|
|
114
|
+
if (value === undefined)
|
|
115
|
+
return;
|
|
116
|
+
this.data.delete(key);
|
|
117
|
+
this.data.set(key, value);
|
|
118
|
+
}
|
|
119
|
+
delete(key) {
|
|
120
|
+
this.touchedAt.delete(key);
|
|
121
|
+
}
|
|
122
|
+
clear() {
|
|
123
|
+
this.touchedAt.clear();
|
|
124
|
+
}
|
|
125
|
+
/** Stops sweeping; the entries stay until the owner clears them. */
|
|
126
|
+
detach() {
|
|
127
|
+
this.unregister();
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
exports.IdleExpiryIndex = IdleExpiryIndex;
|
|
33
131
|
function setBoundedMapEntry(map, key, value, maxEntries, onEvict) {
|
|
34
132
|
map.delete(key);
|
|
35
133
|
map.set(key, value);
|
package/dist/util/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { delay } from './async';
|
|
2
2
|
export { base64ToBytes, bytesToBase64, bytesToBase64UrlSafe, bytesToHex, concatBytes, decodeBase64Url, EMPTY_BYTES, hexToBytes, TEXT_DECODER, TEXT_ENCODER, toBytesView, uint8Equal, uint8TimingSafeEqual } from './bytes';
|
|
3
|
-
export { asBytes, asNumber, asOptionalBytes, asOptionalNumber, asOptionalString, asString, resolveOptionalPositive, resolvePositive, toBoolOrUndef } from './coercion';
|
|
4
|
-
export { normalizeQueryLimit } from './collections';
|
|
3
|
+
export { asBytes, asNumber, asOptionalBytes, asOptionalNumber, asOptionalString, asString, resolveOptionalPositive, resolvePositive, toBoolOrUndef, tryAsNumber } from './coercion';
|
|
4
|
+
export { normalizeQueryLimit, setBoundedMapEntry } from './collections';
|
|
5
5
|
export { toError, toSafeNumber } from './primitives';
|
|
6
6
|
export { isBunRuntime } from './runtime';
|
package/dist/util/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.isBunRuntime = exports.toSafeNumber = exports.toError = exports.normalizeQueryLimit = exports.toBoolOrUndef = exports.resolvePositive = exports.resolveOptionalPositive = exports.asString = exports.asOptionalString = exports.asOptionalNumber = exports.asOptionalBytes = exports.asNumber = exports.asBytes = exports.uint8TimingSafeEqual = exports.uint8Equal = exports.toBytesView = exports.TEXT_ENCODER = exports.TEXT_DECODER = exports.hexToBytes = exports.EMPTY_BYTES = exports.decodeBase64Url = exports.concatBytes = exports.bytesToHex = exports.bytesToBase64UrlSafe = exports.bytesToBase64 = exports.base64ToBytes = exports.delay = void 0;
|
|
3
|
+
exports.isBunRuntime = exports.toSafeNumber = exports.toError = exports.setBoundedMapEntry = exports.normalizeQueryLimit = exports.tryAsNumber = exports.toBoolOrUndef = exports.resolvePositive = exports.resolveOptionalPositive = exports.asString = exports.asOptionalString = exports.asOptionalNumber = exports.asOptionalBytes = exports.asNumber = exports.asBytes = exports.uint8TimingSafeEqual = exports.uint8Equal = exports.toBytesView = exports.TEXT_ENCODER = exports.TEXT_DECODER = exports.hexToBytes = exports.EMPTY_BYTES = exports.decodeBase64Url = exports.concatBytes = exports.bytesToHex = exports.bytesToBase64UrlSafe = exports.bytesToBase64 = exports.base64ToBytes = exports.delay = void 0;
|
|
4
4
|
var async_1 = require("./async");
|
|
5
5
|
Object.defineProperty(exports, "delay", { enumerable: true, get: function () { return async_1.delay; } });
|
|
6
6
|
var bytes_1 = require("./bytes");
|
|
@@ -27,8 +27,10 @@ Object.defineProperty(exports, "asString", { enumerable: true, get: function ()
|
|
|
27
27
|
Object.defineProperty(exports, "resolveOptionalPositive", { enumerable: true, get: function () { return coercion_1.resolveOptionalPositive; } });
|
|
28
28
|
Object.defineProperty(exports, "resolvePositive", { enumerable: true, get: function () { return coercion_1.resolvePositive; } });
|
|
29
29
|
Object.defineProperty(exports, "toBoolOrUndef", { enumerable: true, get: function () { return coercion_1.toBoolOrUndef; } });
|
|
30
|
+
Object.defineProperty(exports, "tryAsNumber", { enumerable: true, get: function () { return coercion_1.tryAsNumber; } });
|
|
30
31
|
var collections_1 = require("./collections");
|
|
31
32
|
Object.defineProperty(exports, "normalizeQueryLimit", { enumerable: true, get: function () { return collections_1.normalizeQueryLimit; } });
|
|
33
|
+
Object.defineProperty(exports, "setBoundedMapEntry", { enumerable: true, get: function () { return collections_1.setBoundedMapEntry; } });
|
|
32
34
|
var primitives_1 = require("./primitives");
|
|
33
35
|
Object.defineProperty(exports, "toError", { enumerable: true, get: function () { return primitives_1.toError; } });
|
|
34
36
|
Object.defineProperty(exports, "toSafeNumber", { enumerable: true, get: function () { return primitives_1.toSafeNumber; } });
|