@zapo-js/fake-server 1.1.0 → 1.2.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.
Files changed (66) hide show
  1. package/README.md +89 -30
  2. package/dist/api/FakeMobilePrimary.d.ts +55 -0
  3. package/dist/api/FakeMobilePrimary.js +106 -0
  4. package/dist/api/FakePairingDriver.js +1 -3
  5. package/dist/api/FakeServerSession.d.ts +35 -2
  6. package/dist/api/FakeServerSession.js +65 -3
  7. package/dist/api/FakeWaServer.d.ts +73 -1
  8. package/dist/api/FakeWaServer.js +230 -4
  9. package/dist/api/ServerRegistries.d.ts +7 -0
  10. package/dist/api/ServerRegistries.js +20 -5
  11. package/dist/api/iq-handlers.d.ts +19 -1
  12. package/dist/api/iq-handlers.js +96 -1
  13. package/dist/esm/api/FakeMobilePrimary.js +102 -0
  14. package/dist/esm/api/FakePairingDriver.js +2 -4
  15. package/dist/esm/api/FakeServerSession.js +66 -4
  16. package/dist/esm/api/FakeWaServer.js +230 -4
  17. package/dist/esm/api/ServerRegistries.js +20 -5
  18. package/dist/esm/api/iq-handlers.js +96 -1
  19. package/dist/esm/index.js +8 -0
  20. package/dist/esm/infra/WaFakeConnection.js +8 -22
  21. package/dist/esm/infra/WaFakeConnectionPipeline.js +17 -5
  22. package/dist/esm/infra/WaFakeTcpServer.js +69 -0
  23. package/dist/esm/infra/WaFakeWsServer.js +2 -1
  24. package/dist/esm/infra/socket-adapters.js +79 -0
  25. package/dist/esm/protocol/auth/client-payload-validate.js +77 -1
  26. package/dist/esm/protocol/auth/pair-device.js +16 -1
  27. package/dist/esm/protocol/iq/companion-host.js +104 -0
  28. package/dist/esm/protocol/iq/link-code.js +90 -0
  29. package/dist/esm/protocol/iq/router.js +3 -3
  30. package/dist/esm/protocol/push/mobile-notification.js +97 -0
  31. package/dist/esm/state/fake-companion-host.js +158 -0
  32. package/dist/esm/transport/auth.js +7 -0
  33. package/dist/esm/transport/crypto.js +1 -1
  34. package/dist/esm/transport/util.js +1 -0
  35. package/dist/index.d.ts +17 -2
  36. package/dist/index.js +28 -1
  37. package/dist/infra/WaFakeConnection.d.ts +21 -3
  38. package/dist/infra/WaFakeConnection.js +8 -22
  39. package/dist/infra/WaFakeConnectionPipeline.d.ts +1 -0
  40. package/dist/infra/WaFakeConnectionPipeline.js +17 -5
  41. package/dist/infra/WaFakeTcpServer.d.ts +29 -0
  42. package/dist/infra/WaFakeTcpServer.js +73 -0
  43. package/dist/infra/WaFakeWsServer.js +2 -1
  44. package/dist/infra/socket-adapters.d.ts +18 -0
  45. package/dist/infra/socket-adapters.js +83 -0
  46. package/dist/protocol/auth/client-payload-validate.d.ts +24 -0
  47. package/dist/protocol/auth/client-payload-validate.js +77 -1
  48. package/dist/protocol/auth/pair-device.d.ts +15 -1
  49. package/dist/protocol/auth/pair-device.js +16 -0
  50. package/dist/protocol/iq/companion-host.d.ts +52 -0
  51. package/dist/protocol/iq/companion-host.js +109 -0
  52. package/dist/protocol/iq/link-code.d.ts +46 -0
  53. package/dist/protocol/iq/link-code.js +95 -0
  54. package/dist/protocol/iq/router.d.ts +15 -2
  55. package/dist/protocol/iq/router.js +3 -3
  56. package/dist/protocol/push/mobile-notification.d.ts +58 -0
  57. package/dist/protocol/push/mobile-notification.js +102 -0
  58. package/dist/state/fake-companion-host.d.ts +73 -0
  59. package/dist/state/fake-companion-host.js +163 -0
  60. package/dist/transport/auth.d.ts +8 -0
  61. package/dist/transport/auth.js +8 -0
  62. package/dist/transport/crypto.d.ts +1 -1
  63. package/dist/transport/crypto.js +2 -1
  64. package/dist/transport/util.d.ts +1 -0
  65. package/dist/transport/util.js +2 -1
  66. package/package.json +1 -1
@@ -0,0 +1,102 @@
1
+ "use strict";
2
+ /**
3
+ * Push notifications only a phone receives.
4
+ *
5
+ * `account_sync` carries the account's live device set: the primary keys its
6
+ * companion bookkeeping off it, allocating a fresh key index for the next link
7
+ * and dropping companions the server no longer lists. The registration
8
+ * notifications carry the login code and the takeover warning that fire while
9
+ * a number is being moved to another phone.
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.buildAccountSyncDevicesNotification = buildAccountSyncDevicesNotification;
13
+ exports.buildRegistrationCodeNotification = buildRegistrationCodeNotification;
14
+ exports.buildAccountTakeoverNotice = buildAccountTakeoverNotice;
15
+ /**
16
+ * Builds `<notification type="account_sync">` with the account's `<devices>`
17
+ * list. Every device the account still owns must be present: the primary reads
18
+ * this as authoritative and prunes anything missing from it.
19
+ */
20
+ function buildAccountSyncDevicesNotification(input) {
21
+ return {
22
+ tag: 'notification',
23
+ attrs: {
24
+ id: input.id ?? `account-sync-${Math.random().toString(36).slice(2, 10)}`,
25
+ from: input.from ?? 's.whatsapp.net',
26
+ type: 'account_sync',
27
+ t: String(input.timestampSeconds ?? Math.floor(Date.now() / 1000))
28
+ },
29
+ content: [
30
+ {
31
+ tag: 'devices',
32
+ attrs: {},
33
+ content: input.devices.map((device) => ({
34
+ tag: 'device',
35
+ attrs: {
36
+ jid: device.deviceJid,
37
+ ...(device.keyIndex !== undefined
38
+ ? { 'key-index': String(device.keyIndex) }
39
+ : {}),
40
+ ...(device.deviceLid !== undefined ? { lid: device.deviceLid } : {})
41
+ }
42
+ }))
43
+ }
44
+ ]
45
+ };
46
+ }
47
+ /**
48
+ * Builds the `<notification type="registration">` carrying a login code, which
49
+ * the client surfaces as `mobile_registration_code`.
50
+ */
51
+ function buildRegistrationCodeNotification(input) {
52
+ const nowSeconds = Math.floor(Date.now() / 1000);
53
+ return {
54
+ tag: 'notification',
55
+ attrs: {
56
+ id: input.id ?? `registration-${Math.random().toString(36).slice(2, 10)}`,
57
+ from: input.from ?? 's.whatsapp.net',
58
+ type: 'registration'
59
+ },
60
+ content: [
61
+ {
62
+ tag: 'wa_old_registration',
63
+ attrs: {
64
+ code: input.code,
65
+ expiry_t: String(input.expirySeconds ?? nowSeconds + 300),
66
+ device_id: input.fromDeviceId
67
+ }
68
+ }
69
+ ]
70
+ };
71
+ }
72
+ /**
73
+ * Builds the `<notification type="registration">` warning that the number is
74
+ * being registered on another phone, surfaced as
75
+ * `mobile_account_takeover_notice`.
76
+ */
77
+ function buildAccountTakeoverNotice(input) {
78
+ return {
79
+ tag: 'notification',
80
+ attrs: {
81
+ id: input.id ?? `takeover-${Math.random().toString(36).slice(2, 10)}`,
82
+ from: input.from ?? 's.whatsapp.net',
83
+ type: 'registration'
84
+ },
85
+ content: [
86
+ {
87
+ tag: 'device_logout',
88
+ attrs: {
89
+ id: input.serverToken,
90
+ t: String(input.attemptTimestampSeconds ?? Math.floor(Date.now() / 1000)),
91
+ ...(input.newDeviceName !== undefined ? { device: input.newDeviceName } : {}),
92
+ ...(input.newDevicePlatform !== undefined
93
+ ? { new_device_platform: input.newDevicePlatform }
94
+ : {}),
95
+ ...(input.newDeviceAppVersion !== undefined
96
+ ? { new_device_app_version: input.newDeviceAppVersion }
97
+ : {})
98
+ }
99
+ }
100
+ ]
101
+ };
102
+ }
@@ -0,0 +1,73 @@
1
+ /** Per-session account state for a mobile primary hosting companion devices. */
2
+ /** Identity of the mobile primary that owns this session's account. */
3
+ export interface FakeMobilePrimaryIdentity {
4
+ /** Phone number, digits only – the user part of every device jid. */
5
+ readonly username: string;
6
+ /** `<phone>@s.whatsapp.net` – the primary itself, always device 0. */
7
+ readonly jid: string;
8
+ }
9
+ export interface FakeLinkedCompanion {
10
+ readonly deviceJid: string;
11
+ readonly deviceId: number;
12
+ /** Pairing ref the companion advertised, echoed by the primary's upload. */
13
+ readonly ref: string;
14
+ /** ADV key index the primary allocated, decoded from the signed identity. */
15
+ readonly keyIndex: number;
16
+ readonly deviceIdentityBytes: Uint8Array;
17
+ readonly linkedAtSeconds: number;
18
+ }
19
+ export interface FakePublishedKeyIndexList {
20
+ readonly bytes: Uint8Array;
21
+ readonly timestampSeconds: number;
22
+ /** Indexes the primary declared valid, decoded from the signed list. */
23
+ readonly validIndexes: readonly number[];
24
+ readonly currentIndex: number;
25
+ }
26
+ export declare class FakeCompanionHostState {
27
+ private primaryIdentity;
28
+ private readonly companions;
29
+ private readonly reservedDeviceIds;
30
+ private keyIndexList;
31
+ private readonly linkListeners;
32
+ private readonly revokeListeners;
33
+ /** The mobile primary bound to this session, or `null` for a web session. */
34
+ get primary(): FakeMobilePrimaryIdentity | null;
35
+ /**
36
+ * Records the primary at login. The first account to bind owns the session
37
+ * for good: a later login under a different number would otherwise mint
38
+ * device jids for one account while the companions of another are still
39
+ * tracked here.
40
+ */
41
+ bindPrimary(identity: FakeMobilePrimaryIdentity): void;
42
+ /** Companions linked from this account, in link order. */
43
+ linkedCompanions(): readonly FakeLinkedCompanion[];
44
+ /** Last key-index list the primary published, or `null` if it never did. */
45
+ publishedKeyIndexList(): FakePublishedKeyIndexList | null;
46
+ /**
47
+ * Reserves the next free device slot. The primary owns device 0, so
48
+ * companions start at 1 and a revoked slot is reused only once no live
49
+ * companion holds it. The slot is held from here until the link is either
50
+ * recorded or released, because a link spans an await and two overlapping
51
+ * pairings would otherwise be handed the same jid.
52
+ */
53
+ allocateDeviceId(): number;
54
+ /** Frees a slot whose link never completed. */
55
+ releaseDeviceId(deviceId: number): void;
56
+ recordCompanion(companion: FakeLinkedCompanion): void;
57
+ /**
58
+ * Drops companions and notifies listeners. Returns the jids actually
59
+ * removed, so a revoke of an untracked device is observable as a no-op.
60
+ */
61
+ removeCompanions(deviceJids: readonly string[]): readonly string[];
62
+ removeAllCompanions(): readonly string[];
63
+ recordKeyIndexList(bytes: Uint8Array, timestampSeconds: number): void;
64
+ onCompanionLinked(listener: (companion: FakeLinkedCompanion) => void): () => void;
65
+ onCompanionsRevoked(listener: (deviceJids: readonly string[]) => void): () => void;
66
+ }
67
+ /**
68
+ * Reads the ADV key index out of the `ADVSignedDeviceIdentityHMAC` the primary
69
+ * uploaded. Returns 0 when the payload does not decode, which keeps a
70
+ * malformed upload observable in the recorded companion instead of throwing
71
+ * inside the IQ handler.
72
+ */
73
+ export declare function readCompanionKeyIndex(deviceIdentityBytes: Uint8Array): number;
@@ -0,0 +1,163 @@
1
+ "use strict";
2
+ /** Per-session account state for a mobile primary hosting companion devices. */
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.FakeCompanionHostState = void 0;
5
+ exports.readCompanionKeyIndex = readCompanionKeyIndex;
6
+ const protos_1 = require("../transport/protos");
7
+ class FakeCompanionHostState {
8
+ constructor() {
9
+ this.primaryIdentity = null;
10
+ this.companions = new Map();
11
+ this.reservedDeviceIds = new Set();
12
+ this.keyIndexList = null;
13
+ this.linkListeners = new Set();
14
+ this.revokeListeners = new Set();
15
+ }
16
+ /** The mobile primary bound to this session, or `null` for a web session. */
17
+ get primary() {
18
+ return this.primaryIdentity;
19
+ }
20
+ /**
21
+ * Records the primary at login. The first account to bind owns the session
22
+ * for good: a later login under a different number would otherwise mint
23
+ * device jids for one account while the companions of another are still
24
+ * tracked here.
25
+ */
26
+ bindPrimary(identity) {
27
+ if (this.primaryIdentity) {
28
+ return;
29
+ }
30
+ this.primaryIdentity = identity;
31
+ }
32
+ /** Companions linked from this account, in link order. */
33
+ linkedCompanions() {
34
+ return [...this.companions.values()];
35
+ }
36
+ /** Last key-index list the primary published, or `null` if it never did. */
37
+ publishedKeyIndexList() {
38
+ return this.keyIndexList;
39
+ }
40
+ /**
41
+ * Reserves the next free device slot. The primary owns device 0, so
42
+ * companions start at 1 and a revoked slot is reused only once no live
43
+ * companion holds it. The slot is held from here until the link is either
44
+ * recorded or released, because a link spans an await and two overlapping
45
+ * pairings would otherwise be handed the same jid.
46
+ */
47
+ allocateDeviceId() {
48
+ const taken = new Set([0, ...this.reservedDeviceIds]);
49
+ for (const companion of this.companions.values()) {
50
+ taken.add(companion.deviceId);
51
+ }
52
+ let candidate = 1;
53
+ while (taken.has(candidate)) {
54
+ candidate += 1;
55
+ }
56
+ this.reservedDeviceIds.add(candidate);
57
+ return candidate;
58
+ }
59
+ /** Frees a slot whose link never completed. */
60
+ releaseDeviceId(deviceId) {
61
+ this.reservedDeviceIds.delete(deviceId);
62
+ }
63
+ recordCompanion(companion) {
64
+ this.reservedDeviceIds.delete(companion.deviceId);
65
+ this.companions.set(companion.deviceJid, companion);
66
+ for (const listener of this.linkListeners) {
67
+ try {
68
+ listener(companion);
69
+ }
70
+ catch (error) {
71
+ void error;
72
+ }
73
+ }
74
+ }
75
+ /**
76
+ * Drops companions and notifies listeners. Returns the jids actually
77
+ * removed, so a revoke of an untracked device is observable as a no-op.
78
+ */
79
+ removeCompanions(deviceJids) {
80
+ const removed = [];
81
+ for (const deviceJid of deviceJids) {
82
+ if (this.companions.delete(deviceJid)) {
83
+ removed.push(deviceJid);
84
+ }
85
+ }
86
+ if (removed.length > 0) {
87
+ for (const listener of this.revokeListeners) {
88
+ try {
89
+ listener(removed);
90
+ }
91
+ catch (error) {
92
+ void error;
93
+ }
94
+ }
95
+ }
96
+ return removed;
97
+ }
98
+ removeAllCompanions() {
99
+ return this.removeCompanions([...this.companions.keys()]);
100
+ }
101
+ recordKeyIndexList(bytes, timestampSeconds) {
102
+ const decoded = decodeKeyIndexList(bytes);
103
+ this.keyIndexList = {
104
+ bytes,
105
+ timestampSeconds,
106
+ validIndexes: decoded.validIndexes,
107
+ currentIndex: decoded.currentIndex
108
+ };
109
+ }
110
+ onCompanionLinked(listener) {
111
+ this.linkListeners.add(listener);
112
+ return () => {
113
+ this.linkListeners.delete(listener);
114
+ };
115
+ }
116
+ onCompanionsRevoked(listener) {
117
+ this.revokeListeners.add(listener);
118
+ return () => {
119
+ this.revokeListeners.delete(listener);
120
+ };
121
+ }
122
+ }
123
+ exports.FakeCompanionHostState = FakeCompanionHostState;
124
+ /**
125
+ * Reads the ADV key index out of the `ADVSignedDeviceIdentityHMAC` the primary
126
+ * uploaded. Returns 0 when the payload does not decode, which keeps a
127
+ * malformed upload observable in the recorded companion instead of throwing
128
+ * inside the IQ handler.
129
+ */
130
+ function readCompanionKeyIndex(deviceIdentityBytes) {
131
+ try {
132
+ const hmacWrapper = protos_1.proto.ADVSignedDeviceIdentityHMAC.decode(deviceIdentityBytes);
133
+ if (!hmacWrapper.details) {
134
+ return 0;
135
+ }
136
+ const signed = protos_1.proto.ADVSignedDeviceIdentity.decode(hmacWrapper.details);
137
+ if (!signed.details) {
138
+ return 0;
139
+ }
140
+ return protos_1.proto.ADVDeviceIdentity.decode(signed.details).keyIndex ?? 0;
141
+ }
142
+ catch (error) {
143
+ void error;
144
+ return 0;
145
+ }
146
+ }
147
+ function decodeKeyIndexList(bytes) {
148
+ try {
149
+ const signed = protos_1.proto.ADVSignedKeyIndexList.decode(bytes);
150
+ if (!signed.details) {
151
+ return { validIndexes: [], currentIndex: 0 };
152
+ }
153
+ const details = protos_1.proto.ADVKeyIndexList.decode(signed.details);
154
+ return {
155
+ validIndexes: details.validIndexes ?? [],
156
+ currentIndex: details.currentIndex ?? 0
157
+ };
158
+ }
159
+ catch (error) {
160
+ void error;
161
+ return { validIndexes: [], currentIndex: 0 };
162
+ }
163
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Layer 1 – credential/store type wrapper.
3
+ *
4
+ * Re-exports the auth and store types from zapo-js so the fixture layer can
5
+ * seed a registered session without importing zapo-js directly.
6
+ */
7
+ export type { WaAuthCredentials, WaMobileTransportDeviceInfo } from 'zapo-js/auth';
8
+ export type { WaStore } from 'zapo-js/store';
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ /**
3
+ * Layer 1 – credential/store type wrapper.
4
+ *
5
+ * Re-exports the auth and store types from zapo-js so the fixture layer can
6
+ * seed a registered session without importing zapo-js directly.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -4,6 +4,6 @@
4
4
  * Re-exports bit-exact cryptographic primitives from zapo-js. No protocol
5
5
  * interpretation, so reusing them does not create a test-vs-impl tautology.
6
6
  */
7
- export { aesCbcDecrypt, aesCbcEncrypt, aesGcmDecrypt, aesGcmEncrypt, Ed25519, hkdf, hkdfSplit, hmacSha256Sign, hmacSha512Sign, prependVersion, randomBytesAsync, readVersionedContent, sha256, sha512, toRawPubKey, toSerializedPubKey, X25519, xeddsaSign, xeddsaVerify } from 'zapo-js/crypto';
7
+ export { aesCbcDecrypt, aesCbcEncrypt, aesGcmDecrypt, aesGcmEncrypt, Ed25519, hkdf, hkdfSplit, hmacSha256Sign, hmacSha512Sign, prependVersion, randomBytesAsync, randomIntAsync, readVersionedContent, sha256, sha512, toRawPubKey, toSerializedPubKey, X25519, xeddsaSign, xeddsaVerify } from 'zapo-js/crypto';
8
8
  export type { SignalKeyPair } from 'zapo-js/crypto';
9
9
  export { WaMediaCrypto } from 'zapo-js/media';
@@ -6,7 +6,7 @@
6
6
  * interpretation, so reusing them does not create a test-vs-impl tautology.
7
7
  */
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.WaMediaCrypto = exports.xeddsaVerify = exports.xeddsaSign = exports.X25519 = exports.toSerializedPubKey = exports.toRawPubKey = exports.sha512 = exports.sha256 = exports.readVersionedContent = exports.randomBytesAsync = exports.prependVersion = exports.hmacSha512Sign = exports.hmacSha256Sign = exports.hkdfSplit = exports.hkdf = exports.Ed25519 = exports.aesGcmEncrypt = exports.aesGcmDecrypt = exports.aesCbcEncrypt = exports.aesCbcDecrypt = void 0;
9
+ exports.WaMediaCrypto = exports.xeddsaVerify = exports.xeddsaSign = exports.X25519 = exports.toSerializedPubKey = exports.toRawPubKey = exports.sha512 = exports.sha256 = exports.readVersionedContent = exports.randomIntAsync = exports.randomBytesAsync = exports.prependVersion = exports.hmacSha512Sign = exports.hmacSha256Sign = exports.hkdfSplit = exports.hkdf = exports.Ed25519 = exports.aesGcmEncrypt = exports.aesGcmDecrypt = exports.aesCbcEncrypt = exports.aesCbcDecrypt = void 0;
10
10
  var crypto_1 = require("zapo-js/crypto");
11
11
  Object.defineProperty(exports, "aesCbcDecrypt", { enumerable: true, get: function () { return crypto_1.aesCbcDecrypt; } });
12
12
  Object.defineProperty(exports, "aesCbcEncrypt", { enumerable: true, get: function () { return crypto_1.aesCbcEncrypt; } });
@@ -19,6 +19,7 @@ Object.defineProperty(exports, "hmacSha256Sign", { enumerable: true, get: functi
19
19
  Object.defineProperty(exports, "hmacSha512Sign", { enumerable: true, get: function () { return crypto_1.hmacSha512Sign; } });
20
20
  Object.defineProperty(exports, "prependVersion", { enumerable: true, get: function () { return crypto_1.prependVersion; } });
21
21
  Object.defineProperty(exports, "randomBytesAsync", { enumerable: true, get: function () { return crypto_1.randomBytesAsync; } });
22
+ Object.defineProperty(exports, "randomIntAsync", { enumerable: true, get: function () { return crypto_1.randomIntAsync; } });
22
23
  Object.defineProperty(exports, "readVersionedContent", { enumerable: true, get: function () { return crypto_1.readVersionedContent; } });
23
24
  Object.defineProperty(exports, "sha256", { enumerable: true, get: function () { return crypto_1.sha256; } });
24
25
  Object.defineProperty(exports, "sha512", { enumerable: true, get: function () { return crypto_1.sha512; } });
@@ -6,3 +6,4 @@
6
6
  */
7
7
  export { bytesToBase64, bytesToBase64UrlSafe, bytesToHex, decodeBase64Url, toError } from 'zapo-js/util';
8
8
  export declare const TEXT_ENCODER: import("util").TextEncoder;
9
+ export declare const TEXT_DECODER: import("util").TextDecoder;
@@ -6,7 +6,7 @@
6
6
  * server can use them without importing zapo-js directly.
7
7
  */
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.TEXT_ENCODER = exports.toError = exports.decodeBase64Url = exports.bytesToHex = exports.bytesToBase64UrlSafe = exports.bytesToBase64 = void 0;
9
+ exports.TEXT_DECODER = exports.TEXT_ENCODER = exports.toError = exports.decodeBase64Url = exports.bytesToHex = exports.bytesToBase64UrlSafe = exports.bytesToBase64 = void 0;
10
10
  var util_1 = require("zapo-js/util");
11
11
  Object.defineProperty(exports, "bytesToBase64", { enumerable: true, get: function () { return util_1.bytesToBase64; } });
12
12
  Object.defineProperty(exports, "bytesToBase64UrlSafe", { enumerable: true, get: function () { return util_1.bytesToBase64UrlSafe; } });
@@ -14,3 +14,4 @@ Object.defineProperty(exports, "bytesToHex", { enumerable: true, get: function (
14
14
  Object.defineProperty(exports, "decodeBase64Url", { enumerable: true, get: function () { return util_1.decodeBase64Url; } });
15
15
  Object.defineProperty(exports, "toError", { enumerable: true, get: function () { return util_1.toError; } });
16
16
  exports.TEXT_ENCODER = new TextEncoder();
17
+ exports.TEXT_DECODER = new TextDecoder();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zapo-js/fake-server",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "Fake WhatsApp Web server used to test zapo-js",
5
5
  "license": "MIT",
6
6
  "author": "vinikjkkj <contact@vinicius.email> (https://github.com/vinikjkkj)",