@spectrum-ts/whatsapp-business 12.0.0 → 12.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.
- package/dist/index.d.ts +3 -3
- package/dist/index.js +16 -72
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ type WhatsAppMessage = SchemaMessage<typeof userSchema, typeof spaceSchema>;
|
|
|
16
16
|
* WhatsApp-only template message content. Lives entirely under the WhatsApp
|
|
17
17
|
* Business provider — never enters the universal `Content` discriminated
|
|
18
18
|
* union. The framework recognizes it via the generic content-level platform
|
|
19
|
-
* contract: `__platform: "
|
|
19
|
+
* contract: `__platform: "whatsapp_business"` lets
|
|
20
20
|
* `findUnsupportedPlatformContent` warn-and-skip when a different platform
|
|
21
21
|
* receives it.
|
|
22
22
|
*
|
|
@@ -29,7 +29,7 @@ type WhatsAppMessage = SchemaMessage<typeof userSchema, typeof spaceSchema>;
|
|
|
29
29
|
*/
|
|
30
30
|
declare const whatsappTemplateSchema: z.ZodObject<{
|
|
31
31
|
type: z.ZodLiteral<"whatsapp-template">;
|
|
32
|
-
__platform: z.ZodLiteral<"
|
|
32
|
+
__platform: z.ZodLiteral<"whatsapp_business">;
|
|
33
33
|
name: z.ZodString;
|
|
34
34
|
languageCode: z.ZodString;
|
|
35
35
|
bodyParams: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
@@ -53,7 +53,7 @@ declare class WhatsAppPartialSendError extends Error {
|
|
|
53
53
|
}
|
|
54
54
|
//#endregion
|
|
55
55
|
//#region src/index.d.ts
|
|
56
|
-
declare const whatsappBusiness: import("@spectrum-ts/core").Platform<import("@spectrum-ts/core").PlatformDef<"
|
|
56
|
+
declare const whatsappBusiness: import("@spectrum-ts/core").Platform<import("@spectrum-ts/core").PlatformDef<"whatsapp_business", import("zod").ZodUnion<readonly [import("zod").ZodObject<{
|
|
57
57
|
accessToken: import("zod").ZodString;
|
|
58
58
|
appSecret: import("zod").ZodOptional<import("zod").ZodString>;
|
|
59
59
|
phoneNumberId: import("zod").ZodString;
|
package/dist/index.js
CHANGED
|
@@ -1,24 +1,15 @@
|
|
|
1
1
|
import { TypedEventStream, button, buttons, createClient, list } from "@photon-ai/whatsapp-business";
|
|
2
2
|
import { UnsupportedError, cloud, definePlatform, mergeStreams, stream } from "@spectrum-ts/core";
|
|
3
|
-
import { asAttachment, asContact, asCustom, asGroup, asPollOption, asReaction, asReply, asText, asUnsend, asVoice, createLogger, errorAttrs, tracedFetch } from "@spectrum-ts/core/authoring";
|
|
3
|
+
import { asAttachment, asContact, asCustom, asGroup, asPollOption, asReaction, asReply, asText, asUnsend, asVoice, createLogger, createTokenRenewal, errorAttrs, tracedFetch } from "@spectrum-ts/core/authoring";
|
|
4
4
|
import { extension } from "mime-types";
|
|
5
5
|
import z from "zod";
|
|
6
6
|
//#region src/auth.ts
|
|
7
|
-
const log = createLogger("spectrum.whatsapp.auth");
|
|
8
7
|
const streamLog$1 = createLogger("spectrum.whatsapp.stream");
|
|
9
|
-
const RENEWAL_RATIO = .8;
|
|
10
|
-
const EXPIRY_BUFFER_MS = 3e4;
|
|
11
|
-
const RETRY_DELAY_MS = 3e4;
|
|
12
8
|
const RESUBSCRIBE_BACKOFF_MS = 500;
|
|
13
9
|
const ignoreCleanupError = () => void 0;
|
|
14
10
|
const cloudAuthState = /* @__PURE__ */ new WeakMap();
|
|
15
11
|
async function createCloudClients(projectId, projectSecret) {
|
|
16
12
|
let tokenData = await cloud.issueWhatsappBusinessTokens(projectId, projectSecret);
|
|
17
|
-
let tokenExpiresAt = Date.now() + tokenData.expiresIn * 1e3;
|
|
18
|
-
let disposed = false;
|
|
19
|
-
let renewalTimer;
|
|
20
|
-
let refreshFailures = 0;
|
|
21
|
-
let refreshInFlight;
|
|
22
13
|
const lines = /* @__PURE__ */ new Map();
|
|
23
14
|
const buildRawClient = (phoneNumberId) => {
|
|
24
15
|
const accessToken = tokenData.auth[phoneNumberId];
|
|
@@ -31,7 +22,6 @@ async function createCloudClients(projectId, projectSecret) {
|
|
|
31
22
|
};
|
|
32
23
|
const refreshTokens = async () => {
|
|
33
24
|
tokenData = await cloud.issueWhatsappBusinessTokens(projectId, projectSecret);
|
|
34
|
-
tokenExpiresAt = Date.now() + tokenData.expiresIn * 1e3;
|
|
35
25
|
for (const [phoneNumberId, state] of lines) {
|
|
36
26
|
if (!tokenData.auth[phoneNumberId]) continue;
|
|
37
27
|
const old = state.current;
|
|
@@ -40,69 +30,21 @@ async function createCloudClients(projectId, projectSecret) {
|
|
|
40
30
|
await old.close().catch(() => void 0);
|
|
41
31
|
}
|
|
42
32
|
};
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
};
|
|
49
|
-
const onRefreshFailure = (error) => {
|
|
50
|
-
refreshFailures += 1;
|
|
51
|
-
log.warn("whatsapp token refresh failed; retrying", {
|
|
52
|
-
"spectrum.whatsapp.auth.attempt": refreshFailures,
|
|
53
|
-
"spectrum.whatsapp.auth.retry_in_ms": RETRY_DELAY_MS,
|
|
54
|
-
...errorAttrs(error)
|
|
55
|
-
}, error);
|
|
56
|
-
};
|
|
57
|
-
const clearRenewalTimer = () => {
|
|
58
|
-
if (renewalTimer !== void 0) {
|
|
59
|
-
clearTimeout(renewalTimer);
|
|
60
|
-
renewalTimer = void 0;
|
|
61
|
-
}
|
|
62
|
-
};
|
|
63
|
-
const refreshNow = async () => {
|
|
64
|
-
await refreshTokens();
|
|
65
|
-
onRefreshSuccess();
|
|
66
|
-
scheduleRenewal();
|
|
67
|
-
};
|
|
68
|
-
const coalescedRefresh = () => {
|
|
69
|
-
if (!refreshInFlight) refreshInFlight = refreshNow().finally(() => {
|
|
70
|
-
refreshInFlight = void 0;
|
|
71
|
-
});
|
|
72
|
-
return refreshInFlight;
|
|
73
|
-
};
|
|
74
|
-
const scheduleRenewal = () => {
|
|
75
|
-
if (disposed) return;
|
|
76
|
-
clearRenewalTimer();
|
|
77
|
-
const ttlMs = tokenData.expiresIn * 1e3;
|
|
78
|
-
const renewInMs = Math.max(ttlMs * RENEWAL_RATIO, 5e3);
|
|
79
|
-
const runScheduledRefresh = () => {
|
|
80
|
-
coalescedRefresh().catch((err) => {
|
|
81
|
-
onRefreshFailure(err);
|
|
82
|
-
if (disposed) return;
|
|
83
|
-
renewalTimer = setTimeout(runScheduledRefresh, RETRY_DELAY_MS);
|
|
84
|
-
renewalTimer?.unref?.();
|
|
85
|
-
});
|
|
86
|
-
};
|
|
87
|
-
renewalTimer = setTimeout(runScheduledRefresh, renewInMs);
|
|
88
|
-
renewalTimer?.unref?.();
|
|
89
|
-
};
|
|
90
|
-
const refreshIfNeeded = async () => {
|
|
91
|
-
if (Date.now() < tokenExpiresAt - EXPIRY_BUFFER_MS) return;
|
|
92
|
-
await coalescedRefresh();
|
|
93
|
-
};
|
|
94
|
-
scheduleRenewal();
|
|
33
|
+
const renewal = createTokenRenewal({
|
|
34
|
+
expiresInSeconds: () => tokenData.expiresIn,
|
|
35
|
+
name: "whatsapp",
|
|
36
|
+
refresh: refreshTokens
|
|
37
|
+
});
|
|
95
38
|
const clients = Object.keys(tokenData.auth).map((phoneNumberId) => {
|
|
96
39
|
const state = {
|
|
97
40
|
current: buildRawClient(phoneNumberId),
|
|
98
41
|
subscriptions: /* @__PURE__ */ new Set()
|
|
99
42
|
};
|
|
100
43
|
lines.set(phoneNumberId, state);
|
|
101
|
-
return buildClientProxy(state, refreshIfNeeded);
|
|
44
|
+
return buildClientProxy(state, renewal.refreshIfNeeded);
|
|
102
45
|
});
|
|
103
46
|
cloudAuthState.set(clients, { dispose: async () => {
|
|
104
|
-
|
|
105
|
-
clearRenewalTimer();
|
|
47
|
+
renewal.dispose();
|
|
106
48
|
for (const state of lines.values()) for (const sub of state.subscriptions) sub.close();
|
|
107
49
|
await Promise.allSettled(Array.from(lines.values()).map((s) => s.current.close()));
|
|
108
50
|
lines.clear();
|
|
@@ -256,7 +198,7 @@ const resubscribableStream = (state, options) => {
|
|
|
256
198
|
* WhatsApp-only template message content. Lives entirely under the WhatsApp
|
|
257
199
|
* Business provider — never enters the universal `Content` discriminated
|
|
258
200
|
* union. The framework recognizes it via the generic content-level platform
|
|
259
|
-
* contract: `__platform: "
|
|
201
|
+
* contract: `__platform: "whatsapp_business"` lets
|
|
260
202
|
* `findUnsupportedPlatformContent` warn-and-skip when a different platform
|
|
261
203
|
* receives it.
|
|
262
204
|
*
|
|
@@ -269,7 +211,7 @@ const resubscribableStream = (state, options) => {
|
|
|
269
211
|
*/
|
|
270
212
|
const whatsappTemplateSchema = z.object({
|
|
271
213
|
type: z.literal("whatsapp-template"),
|
|
272
|
-
__platform: z.literal("
|
|
214
|
+
__platform: z.literal("whatsapp_business"),
|
|
273
215
|
name: z.string().min(1),
|
|
274
216
|
languageCode: z.string().min(1),
|
|
275
217
|
bodyParams: z.array(z.string()).optional()
|
|
@@ -277,7 +219,7 @@ const whatsappTemplateSchema = z.object({
|
|
|
277
219
|
const isWhatsAppTemplate = (value) => whatsappTemplateSchema.safeParse(value).success;
|
|
278
220
|
const asWhatsAppTemplate = (input) => whatsappTemplateSchema.parse({
|
|
279
221
|
type: "whatsapp-template",
|
|
280
|
-
__platform: "
|
|
222
|
+
__platform: "whatsapp_business",
|
|
281
223
|
...input
|
|
282
224
|
});
|
|
283
225
|
function whatsappTemplate(input) {
|
|
@@ -948,9 +890,11 @@ const cloudConfig = z.object({}).strict();
|
|
|
948
890
|
const configSchema = z.union([directConfig, cloudConfig]);
|
|
949
891
|
const isCloudConfig = (config) => !("accessToken" in config);
|
|
950
892
|
z.object({});
|
|
893
|
+
const spaceSchema = z.object({ id: z.string() });
|
|
951
894
|
//#endregion
|
|
952
895
|
//#region src/index.ts
|
|
953
|
-
const
|
|
896
|
+
const PLATFORM_ID = "whatsapp_business";
|
|
897
|
+
const whatsappBusiness = definePlatform(PLATFORM_ID, {
|
|
954
898
|
config: configSchema,
|
|
955
899
|
lifecycle: {
|
|
956
900
|
createClient: async ({ config, projectId, projectSecret }) => {
|
|
@@ -969,10 +913,10 @@ const whatsappBusiness = definePlatform("WhatsApp Business", {
|
|
|
969
913
|
},
|
|
970
914
|
user: { resolve: async ({ input }) => ({ id: input.userID }) },
|
|
971
915
|
space: {
|
|
972
|
-
schema:
|
|
916
|
+
schema: spaceSchema,
|
|
973
917
|
create: async ({ input }) => {
|
|
974
918
|
if (input.users.length === 0) throw new Error("WhatsApp space creation requires at least one user");
|
|
975
|
-
if (input.users.length > 1) throw UnsupportedError.action("space.create",
|
|
919
|
+
if (input.users.length > 1) throw UnsupportedError.action("space.create", PLATFORM_ID, "only 1:1 conversations are supported");
|
|
976
920
|
const user = input.users[0];
|
|
977
921
|
if (!user) throw new Error("WhatsApp space creation requires a user");
|
|
978
922
|
return { id: user.id };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spectrum-ts/whatsapp-business",
|
|
3
|
-
"version": "12.
|
|
3
|
+
"version": "12.2.0",
|
|
4
4
|
"description": "WhatsApp Business provider for spectrum-ts.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -26,9 +26,9 @@
|
|
|
26
26
|
"access": "public"
|
|
27
27
|
},
|
|
28
28
|
"spectrum": {
|
|
29
|
-
"key": "
|
|
29
|
+
"key": "whatsapp_business",
|
|
30
30
|
"import": "whatsappBusiness",
|
|
31
|
-
"label": "
|
|
31
|
+
"label": "whatsapp_business"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@photon-ai/whatsapp-business": "^0.2.0",
|