@whanext/core 0.19.7 → 0.19.8

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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.19.8
4
+
5
+ ### Fixed
6
+ - The Zapo mailbox (`messages`, `threads`, and `contacts`) is now persisted in the shared SQLite store and full history sync is enabled. This lets Zapo retain parent message secrets required to decrypt `secretEncryptedMessage` edit addons after restart/offline resume.
7
+ - History sync now hydrates the trusted-contact/privacy-token state used by Zapo when preparing outgoing messages, including history-derived token metadata and NCT salt that can be required for `tctoken`/`cstoken` generation. Historical/bootstrap messages are still filtered by WhaNext and do not become live command events unless `processOfflineMessages` is explicitly enabled.
8
+ - WhatsApp negative publish ACK `463` is normalized as `MESSAGE_REACHOUT_LOCKED` with `ackCode: 463` instead of surfacing as `UNKNOWN_ERROR`, allowing consumers to avoid retry/error-reply loops while the account is reach-out time-locked.
9
+
10
+ ### Compatibility
11
+ - Public message/edit/delete event shapes are unchanged.
12
+ - Existing shared multi-account stores are reused in place; the mailbox domains are created inside the same `state.sqlite` and remain isolated by `sessionId`.
13
+ - The first connection after this update may perform a larger history sync because mailbox persistence is now enabled deliberately.
14
+
3
15
  ## 0.19.7
4
16
 
5
17
  ### Fixed
package/dist/index.d.ts CHANGED
@@ -610,7 +610,7 @@ declare class DeferredReply {
610
610
  delete(): Promise<void>;
611
611
  }
612
612
 
613
- type WhaNextErrorCode = 'AUTH_INVALID_PHONE' | 'AUTH_EXPIRED' | 'AUTH_PASSKEY_REQUIRED' | 'CONNECTION_CLOSED' | 'CONNECTION_FAILED' | 'GROUP_NOT_FOUND' | 'MEMBER_NOT_FOUND' | 'BOT_NOT_ADMIN' | 'MESSAGE_NOT_FOUND' | 'MEDIA_NOT_AVAILABLE' | 'MUTE_DISABLED' | 'STORAGE_ERROR' | 'ARGUMENT_MISSING' | 'ARGUMENT_INVALID' | 'COMMAND_NOT_ALLOWED' | 'COMMAND_COOLDOWN' | 'COMMAND_BUSY' | 'COMMAND_LOAD_FAILED' | 'PROVIDER_ERROR' | 'UNKNOWN_ERROR';
613
+ type WhaNextErrorCode = 'AUTH_INVALID_PHONE' | 'AUTH_EXPIRED' | 'AUTH_PASSKEY_REQUIRED' | 'CONNECTION_CLOSED' | 'CONNECTION_FAILED' | 'GROUP_NOT_FOUND' | 'MEMBER_NOT_FOUND' | 'BOT_NOT_ADMIN' | 'MESSAGE_NOT_FOUND' | 'MEDIA_NOT_AVAILABLE' | 'MESSAGE_REACHOUT_LOCKED' | 'MUTE_DISABLED' | 'STORAGE_ERROR' | 'ARGUMENT_MISSING' | 'ARGUMENT_INVALID' | 'COMMAND_NOT_ALLOWED' | 'COMMAND_COOLDOWN' | 'COMMAND_BUSY' | 'COMMAND_LOAD_FAILED' | 'PROVIDER_ERROR' | 'UNKNOWN_ERROR';
614
614
  interface WhaNextErrorOptions {
615
615
  cause?: unknown;
616
616
  context?: Readonly<Record<string, unknown>>;
package/dist/index.js CHANGED
@@ -90,11 +90,40 @@ function toWhaNextError(error, context) {
90
90
  return error;
91
91
  }
92
92
  const message = error instanceof Error ? error.message : "An unknown error occurred.";
93
+ if (isReachoutTimelockError(error)) {
94
+ return new WhaNextError(
95
+ "MESSAGE_REACHOUT_LOCKED",
96
+ "WhatsApp rejected the outgoing message (ack 463: reach-out time-lock).",
97
+ {
98
+ cause: error,
99
+ context: {
100
+ ...context ?? {},
101
+ ackCode: 463
102
+ },
103
+ recoverable: false
104
+ }
105
+ );
106
+ }
93
107
  return new WhaNextError("UNKNOWN_ERROR", message, {
94
108
  cause: error,
95
109
  ...context ? { context } : {}
96
110
  });
97
111
  }
112
+ function isReachoutTimelockError(error) {
113
+ const seen = /* @__PURE__ */ new Set();
114
+ let current = error;
115
+ for (let depth = 0; depth < 8 && current !== void 0 && current !== null; depth += 1) {
116
+ if (seen.has(current)) return false;
117
+ seen.add(current);
118
+ const message = current instanceof Error ? current.message : typeof current === "string" ? current : void 0;
119
+ if (message && /negative publish ack:.*\berror=463\b/i.test(message)) {
120
+ return true;
121
+ }
122
+ if (typeof current !== "object") return false;
123
+ current = Reflect.get(current, "cause");
124
+ }
125
+ return false;
126
+ }
98
127
 
99
128
  // src/models/identity.ts
100
129
  function normalizeIdentity(identity) {
@@ -3044,7 +3073,7 @@ var ZapoProvider = class {
3044
3073
  markOnlineOnConnect: false,
3045
3074
  deviceBrowser: this.#deviceBrowser(),
3046
3075
  deviceOsDisplayName: this.#deviceOsDisplayName(),
3047
- history: { enabled: false },
3076
+ history: { enabled: true, requireFullSync: true },
3048
3077
  addons: {
3049
3078
  autoDecrypt: true,
3050
3079
  persistAllSecrets: true
@@ -4142,9 +4171,9 @@ function createZapoStoreEntry(storePath) {
4142
4171
  senderKey: "sqlite",
4143
4172
  appState: "sqlite",
4144
4173
  privacyToken: "sqlite",
4145
- messages: "none",
4146
- threads: "none",
4147
- contacts: "none"
4174
+ messages: "sqlite",
4175
+ threads: "sqlite",
4176
+ contacts: "sqlite"
4148
4177
  },
4149
4178
  cacheProviders: {
4150
4179
  messageSecret: "sqlite"