spectrum-ts 0.1.1 → 0.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.
@@ -155,12 +155,13 @@ function definePlatform(name, def) {
155
155
  }
156
156
  throw new Error("Invalid input to platform narrowing function");
157
157
  });
158
- narrower.config = (config = {}) => {
158
+ narrower.config = (config) => {
159
+ const resolvedConfig = config ?? {};
159
160
  return {
160
161
  __tag: "PlatformProviderConfig",
161
162
  __def: void 0,
162
163
  __name: name,
163
- config,
164
+ config: resolvedConfig,
164
165
  __definition: fullDef
165
166
  };
166
167
  };
@@ -0,0 +1,67 @@
1
+ // src/utils/cloud.ts
2
+ var SPECTRUM_CLOUD_URL = `https://${process.env.SPECTRUM_CLOUD_URL ?? "spectrum-cloud.photon.codes"}`;
3
+ var SpectrumCloudError = class extends Error {
4
+ status;
5
+ code;
6
+ constructor(status, code, message) {
7
+ super(message);
8
+ this.name = "SpectrumCloudError";
9
+ this.status = status;
10
+ this.code = code;
11
+ }
12
+ };
13
+ var request = async (path, init) => {
14
+ const response = await fetch(`${SPECTRUM_CLOUD_URL}${path}`, init);
15
+ if (!response.ok) {
16
+ const body = await response.text().catch(() => "");
17
+ try {
18
+ const parsed = JSON.parse(body);
19
+ throw new SpectrumCloudError(
20
+ response.status,
21
+ parsed.code,
22
+ parsed.message
23
+ );
24
+ } catch (error) {
25
+ if (error instanceof SpectrumCloudError) {
26
+ throw error;
27
+ }
28
+ throw new SpectrumCloudError(
29
+ response.status,
30
+ "UNKNOWN",
31
+ body || response.statusText
32
+ );
33
+ }
34
+ }
35
+ const json = await response.json();
36
+ if (!json.succeed) {
37
+ throw new SpectrumCloudError(
38
+ response.status,
39
+ "UNKNOWN",
40
+ "Server returned succeed=false"
41
+ );
42
+ }
43
+ return json.data;
44
+ };
45
+ var basicAuth = (projectId, projectSecret) => `Basic ${btoa(`${projectId}:${projectSecret}`)}`;
46
+ var cloud = {
47
+ getSubscription: (projectId) => request(`/projects/${projectId}/billing/subscription`),
48
+ issueImessageTokens: (projectId, projectSecret) => request(`/projects/${projectId}/imessage/tokens`, {
49
+ method: "POST",
50
+ headers: { Authorization: basicAuth(projectId, projectSecret) }
51
+ }),
52
+ getImessageInfo: (projectId) => request(`/projects/${projectId}/imessage/`),
53
+ getPlatforms: (projectId) => request(`/projects/${projectId}/platforms/`),
54
+ togglePlatform: (projectId, projectSecret, platform, enabled) => request(`/projects/${projectId}/platforms/`, {
55
+ method: "PATCH",
56
+ headers: {
57
+ Authorization: basicAuth(projectId, projectSecret),
58
+ "Content-Type": "application/json"
59
+ },
60
+ body: JSON.stringify({ platform, enabled })
61
+ })
62
+ };
63
+
64
+ export {
65
+ SpectrumCloudError,
66
+ cloud
67
+ };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import z__default from 'zod';
2
- import { P as ProviderMessage, a as PlatformDef, b as Platform, c as PlatformProviderConfig, S as SpectrumLike, C as CustomEventStreams, d as Space, M as Message, e as ContentBuilder } from './types-DiKuSemh.js';
3
- export { A as AnyPlatformDef, f as Content, E as EventProducer, g as PlatformInstance, h as PlatformMessage, i as PlatformSpace, j as PlatformUser, k as SchemaMessage, U as User, l as attachment, m as custom, t as text } from './types-DiKuSemh.js';
2
+ import { P as ProviderMessage, a as PlatformDef, b as Platform, c as PlatformProviderConfig, S as SpectrumLike, C as CustomEventStreams, d as Space, M as Message, e as ContentBuilder } from './types-DQE0dQT4.js';
3
+ export { A as AnyPlatformDef, f as Content, E as EventProducer, g as PlatformInstance, h as PlatformMessage, i as PlatformSpace, j as PlatformUser, k as SchemaMessage, U as User, l as attachment, m as custom, t as text } from './types-DQE0dQT4.js';
4
4
  export { M as ManagedStream, m as mergeStreams, s as stream } from './stream-DGy4geUK.js';
5
5
  import 'hotscript';
6
6
  import 'type-fest';
@@ -29,8 +29,51 @@ type SpectrumInstance<Providers extends PlatformProviderConfig[] = PlatformProvi
29
29
  send(space: Space, ...content: [ContentBuilder, ...ContentBuilder[]]): Promise<void>;
30
30
  responding<T>(space: Space, fn: () => T | Promise<T>): Promise<T>;
31
31
  };
32
- declare function Spectrum<const Providers extends PlatformProviderConfig[]>(projectId: string, projectSecret: string, options: {
32
+ declare function Spectrum<const Providers extends PlatformProviderConfig[]>(options: {
33
+ projectId: string;
34
+ projectSecret: string;
35
+ providers: [...Providers];
36
+ } | {
37
+ projectId?: never;
38
+ projectSecret?: never;
33
39
  providers: [...Providers];
34
40
  }): Promise<SpectrumInstance<Providers>>;
35
41
 
36
- export { ContentBuilder, Message, Platform, PlatformDef, PlatformProviderConfig, Space, Spectrum, type SpectrumInstance, definePlatform };
42
+ type SubscriptionStatus = "active" | "canceled" | "past_due";
43
+ interface SubscriptionData {
44
+ status: SubscriptionStatus | null;
45
+ tier: string;
46
+ }
47
+ interface SharedTokenData {
48
+ expiresIn: number;
49
+ token: string;
50
+ type: "shared";
51
+ }
52
+ interface DedicatedTokenData {
53
+ auth: Record<string, string>;
54
+ expiresIn: number;
55
+ type: "dedicated";
56
+ }
57
+ type TokenData = SharedTokenData | DedicatedTokenData;
58
+ type CloudPlatform = "imessage" | "whatsapp_business";
59
+ interface PlatformStatus {
60
+ enabled: boolean;
61
+ }
62
+ type PlatformsData = Record<CloudPlatform, PlatformStatus>;
63
+ interface ImessageInfoData {
64
+ type: "shared" | "dedicated";
65
+ }
66
+ declare class SpectrumCloudError extends Error {
67
+ readonly status: number;
68
+ readonly code: string;
69
+ constructor(status: number, code: string, message: string);
70
+ }
71
+ declare const cloud: {
72
+ getSubscription: (projectId: string) => Promise<SubscriptionData>;
73
+ issueImessageTokens: (projectId: string, projectSecret: string) => Promise<TokenData>;
74
+ getImessageInfo: (projectId: string) => Promise<ImessageInfoData>;
75
+ getPlatforms: (projectId: string) => Promise<PlatformsData>;
76
+ togglePlatform: (projectId: string, projectSecret: string, platform: CloudPlatform, enabled: boolean) => Promise<PlatformsData>;
77
+ };
78
+
79
+ export { type CloudPlatform, ContentBuilder, type DedicatedTokenData, type ImessageInfoData, Message, Platform, PlatformDef, PlatformProviderConfig, type PlatformStatus, type PlatformsData, type SharedTokenData, Space, Spectrum, SpectrumCloudError, type SpectrumInstance, type SubscriptionData, type SubscriptionStatus, type TokenData, cloud, definePlatform };
package/dist/index.js CHANGED
@@ -1,10 +1,14 @@
1
+ import {
2
+ SpectrumCloudError,
3
+ cloud
4
+ } from "./chunk-XOBTWTFC.js";
1
5
  import {
2
6
  mergeStreams,
3
7
  stream
4
8
  } from "./chunk-3TBRO2J7.js";
5
9
  import {
6
10
  definePlatform
7
- } from "./chunk-UZ2CXPOD.js";
11
+ } from "./chunk-LIRM7SBA.js";
8
12
 
9
13
  // src/spectrum.ts
10
14
  import z from "zod";
@@ -15,21 +19,25 @@ var providerMessageCoreKeys = /* @__PURE__ */ new Set([
15
19
  "space",
16
20
  "timestamp"
17
21
  ]);
18
- var spectrumConfigSchema = z.object({
19
- projectId: z.string().min(1),
20
- projectSecret: z.string().min(1),
21
- providers: z.array(z.custom())
22
- });
23
- async function Spectrum(projectId, projectSecret, options) {
24
- spectrumConfigSchema.parse({
25
- projectId,
26
- projectSecret,
27
- providers: options.providers
28
- });
22
+ var spectrumConfigSchema = z.union([
23
+ z.object({
24
+ projectId: z.string().min(1),
25
+ projectSecret: z.string().min(1),
26
+ providers: z.array(z.custom())
27
+ }),
28
+ z.object({
29
+ projectId: z.undefined().optional(),
30
+ projectSecret: z.undefined().optional(),
31
+ providers: z.array(z.custom())
32
+ })
33
+ ]);
34
+ async function Spectrum(options) {
35
+ spectrumConfigSchema.parse(options);
36
+ const { projectId, projectSecret, providers } = options;
29
37
  const platformStates = /* @__PURE__ */ new Map();
30
38
  const customEventStreams = /* @__PURE__ */ new Map();
31
39
  let stopped = false;
32
- for (const provider of options.providers) {
40
+ for (const provider of providers) {
33
41
  const providerConfig = provider;
34
42
  const def = providerConfig.__definition;
35
43
  const userConfig = def.config.parse(providerConfig.config);
@@ -250,7 +258,7 @@ async function Spectrum(projectId, projectSecret, options) {
250
258
  }
251
259
  );
252
260
  const base = {
253
- __providers: options.providers,
261
+ __providers: providers,
254
262
  __internal: { platforms: platformStates },
255
263
  messages,
256
264
  stop: stopOnce,
@@ -335,7 +343,9 @@ function attachment(input, options) {
335
343
  }
336
344
  export {
337
345
  Spectrum,
346
+ SpectrumCloudError,
338
347
  attachment,
348
+ cloud,
339
349
  custom,
340
350
  definePlatform,
341
351
  mergeStreams,
@@ -3,7 +3,7 @@ import { AdvancedIMessage } from '@photon-ai/advanced-imessage';
3
3
  import { IMessageSDK } from '@photon-ai/imessage-kit';
4
4
  import * as z from 'zod';
5
5
  import z__default from 'zod';
6
- import { k as SchemaMessage, b as Platform, a as PlatformDef, P as ProviderMessage } from '../../types-DiKuSemh.js';
6
+ import { k as SchemaMessage, b as Platform, a as PlatformDef, P as ProviderMessage } from '../../types-DQE0dQT4.js';
7
7
  import * as zod_v4_core from 'zod/v4/core';
8
8
  import 'hotscript';
9
9
  import 'type-fest';
@@ -1,10 +1,13 @@
1
+ import {
2
+ cloud
3
+ } from "../../chunk-XOBTWTFC.js";
1
4
  import {
2
5
  mergeStreams,
3
6
  stream
4
7
  } from "../../chunk-3TBRO2J7.js";
5
8
  import {
6
9
  definePlatform
7
- } from "../../chunk-UZ2CXPOD.js";
10
+ } from "../../chunk-LIRM7SBA.js";
8
11
 
9
12
  // src/providers/imessage/index.ts
10
13
  import { createClient as createClient2, directChat } from "@photon-ai/advanced-imessage";
@@ -14,40 +17,12 @@ import { IMessageSDK as IMessageSDK2 } from "@photon-ai/imessage-kit";
14
17
  import {
15
18
  createClient
16
19
  } from "@photon-ai/advanced-imessage";
17
-
18
- // src/utils/cloud.ts
19
- var SPECTRUM_CLOUD_URL = `https://${process.env.SPECTRUM_CLOUD_URL ?? "spectrum-cloud.photon.codes"}`;
20
-
21
- // src/providers/imessage/auth.ts
22
20
  var RENEWAL_RATIO = 0.8;
23
21
  var EXPIRY_BUFFER_MS = 3e4;
24
22
  var RETRY_DELAY_MS = 3e4;
25
23
  var cloudAuthState = /* @__PURE__ */ new WeakMap();
26
- async function fetchTokens(projectId, projectSecret) {
27
- const url = `${SPECTRUM_CLOUD_URL}/${projectId}/imessage/tokens`;
28
- const credentials = btoa(`${projectId}:${projectSecret}`);
29
- const response = await fetch(url, {
30
- method: "POST",
31
- headers: {
32
- Authorization: `Basic ${credentials}`
33
- }
34
- });
35
- if (!response.ok) {
36
- const body = await response.text().catch(() => "");
37
- throw new Error(
38
- `Spectrum Cloud authentication failed (${response.status}): ${body || response.statusText}`
39
- );
40
- }
41
- const json = await response.json();
42
- if (!json.succeed) {
43
- throw new Error(
44
- "Spectrum Cloud authentication failed: server returned succeed=false"
45
- );
46
- }
47
- return json.data;
48
- }
49
24
  async function createCloudClients(projectId, projectSecret) {
50
- let tokenData = await fetchTokens(projectId, projectSecret);
25
+ let tokenData = await cloud.issueImessageTokens(projectId, projectSecret);
51
26
  let tokenExpiresAt = Date.now() + tokenData.expiresIn * 1e3;
52
27
  let disposed = false;
53
28
  let renewalTimer;
@@ -59,7 +34,7 @@ async function createCloudClients(projectId, projectSecret) {
59
34
  const renewInMs = Math.max(ttlMs * RENEWAL_RATIO, 5e3);
60
35
  renewalTimer = setTimeout(async () => {
61
36
  try {
62
- tokenData = await fetchTokens(projectId, projectSecret);
37
+ tokenData = await cloud.issueImessageTokens(projectId, projectSecret);
63
38
  tokenExpiresAt = Date.now() + tokenData.expiresIn * 1e3;
64
39
  scheduleRenewal();
65
40
  } catch {
@@ -74,7 +49,7 @@ async function createCloudClients(projectId, projectSecret) {
74
49
  if (Date.now() < tokenExpiresAt - EXPIRY_BUFFER_MS) {
75
50
  return;
76
51
  }
77
- tokenData = await fetchTokens(projectId, projectSecret);
52
+ tokenData = await cloud.issueImessageTokens(projectId, projectSecret);
78
53
  tokenExpiresAt = Date.now() + tokenData.expiresIn * 1e3;
79
54
  scheduleRenewal();
80
55
  };
@@ -356,6 +331,11 @@ var imessage = definePlatform("iMessage", {
356
331
  (e) => createClient2({ address: e.address, tls: true, token: e.token })
357
332
  );
358
333
  }
334
+ if (!(projectId && projectSecret)) {
335
+ throw new Error(
336
+ "iMessage requires projectId and projectSecret. Either pass credentials to Spectrum(), use local mode: imessage.config({ local: true }), or provide explicit client config: imessage.config({ clients: [...] })"
337
+ );
338
+ }
359
339
  return await createCloudClients(projectId, projectSecret);
360
340
  },
361
341
  destroyClient: async ({ client }) => {
@@ -1,4 +1,4 @@
1
- import { b as Platform, a as PlatformDef, P as ProviderMessage } from '../../types-DiKuSemh.js';
1
+ import { b as Platform, a as PlatformDef, P as ProviderMessage } from '../../types-DQE0dQT4.js';
2
2
  import * as node_readline from 'node:readline';
3
3
  import z__default from 'zod';
4
4
  import 'hotscript';
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  definePlatform
3
- } from "../../chunk-UZ2CXPOD.js";
3
+ } from "../../chunk-LIRM7SBA.js";
4
4
 
5
5
  // src/providers/terminal/index.ts
6
6
  import { createInterface } from "readline";
@@ -0,0 +1,41 @@
1
+ import { M as ManagedStream } from '../../stream-DGy4geUK.js';
2
+ import * as z from 'zod';
3
+ import z__default from 'zod';
4
+ import { k as SchemaMessage, b as Platform, a as PlatformDef, P as ProviderMessage } from '../../types-DQE0dQT4.js';
5
+ import * as zod_v4_core from 'zod/v4/core';
6
+ import { WhatsAppClient } from '@photon-ai/whatsapp-business';
7
+ import 'hotscript';
8
+ import 'type-fest';
9
+
10
+ declare const userSchema: z__default.ZodObject<{}, z__default.core.$strip>;
11
+ declare const spaceSchema: z__default.ZodObject<{
12
+ id: z__default.ZodString;
13
+ }, z__default.core.$strip>;
14
+ type WhatsAppMessage = SchemaMessage<typeof userSchema, typeof spaceSchema>;
15
+
16
+ declare const whatsappBusiness: Platform<PlatformDef<"WhatsApp Business", z.ZodObject<{
17
+ accessToken: z.ZodString;
18
+ phoneNumberId: z.ZodString;
19
+ appSecret: z.ZodOptional<z.ZodString>;
20
+ }, zod_v4_core.$strip>, z.ZodType<object, unknown, zod_v4_core.$ZodTypeInternals<object, unknown>> | undefined, z.ZodObject<{
21
+ id: z.ZodString;
22
+ }, zod_v4_core.$strip>, z.ZodType<object, unknown, zod_v4_core.$ZodTypeInternals<object, unknown>> | undefined, WhatsAppClient, {
23
+ id: string;
24
+ }, {
25
+ id: string;
26
+ }, undefined, ProviderMessage<{
27
+ id: string;
28
+ }, {
29
+ id: string;
30
+ }, Record<never, never>>, {
31
+ messages: ({ client }: {
32
+ client: WhatsAppClient;
33
+ config: {
34
+ accessToken: string;
35
+ phoneNumberId: string;
36
+ appSecret?: string | undefined;
37
+ };
38
+ }) => ManagedStream<WhatsAppMessage>;
39
+ }>> & Readonly<Record<never, never>>;
40
+
41
+ export { whatsappBusiness };
@@ -0,0 +1,280 @@
1
+ import {
2
+ stream
3
+ } from "../../chunk-3TBRO2J7.js";
4
+ import {
5
+ definePlatform
6
+ } from "../../chunk-LIRM7SBA.js";
7
+
8
+ // src/providers/whatsapp-business/index.ts
9
+ import {
10
+ createClient
11
+ } from "@photon-ai/whatsapp-business";
12
+
13
+ // src/providers/whatsapp-business/messages.ts
14
+ var toMessage = async (client, msg) => {
15
+ const content = await mapContent(client, msg.content);
16
+ return {
17
+ id: msg.id,
18
+ content,
19
+ sender: { id: msg.from },
20
+ space: { id: msg.from },
21
+ timestamp: msg.timestamp
22
+ };
23
+ };
24
+ var mapContent = async (client, content) => {
25
+ switch (content.type) {
26
+ case "text":
27
+ return [{ type: "plain_text", text: content.body }];
28
+ case "image":
29
+ case "video":
30
+ case "audio":
31
+ case "document":
32
+ return [await downloadMedia(client, content.media)];
33
+ case "sticker":
34
+ return [
35
+ {
36
+ type: "custom",
37
+ raw: { whatsapp_type: "sticker", ...content.sticker }
38
+ }
39
+ ];
40
+ case "location":
41
+ return [
42
+ {
43
+ type: "custom",
44
+ raw: { whatsapp_type: "location", ...content.location }
45
+ }
46
+ ];
47
+ case "contacts":
48
+ return [
49
+ {
50
+ type: "custom",
51
+ raw: { whatsapp_type: "contacts", contacts: content.contacts }
52
+ }
53
+ ];
54
+ case "reaction":
55
+ return [
56
+ {
57
+ type: "custom",
58
+ raw: { whatsapp_type: "reaction", ...content.reaction }
59
+ }
60
+ ];
61
+ case "interactive":
62
+ return [
63
+ {
64
+ type: "custom",
65
+ raw: { whatsapp_type: "interactive", ...content.interactive }
66
+ }
67
+ ];
68
+ case "button":
69
+ return [
70
+ {
71
+ type: "custom",
72
+ raw: { whatsapp_type: "button", ...content.button }
73
+ }
74
+ ];
75
+ case "order":
76
+ return [
77
+ { type: "custom", raw: { whatsapp_type: "order", ...content.order } }
78
+ ];
79
+ case "system":
80
+ return [
81
+ {
82
+ type: "custom",
83
+ raw: { whatsapp_type: "system", ...content.system }
84
+ }
85
+ ];
86
+ default:
87
+ return [{ type: "custom", raw: { whatsapp_type: "unknown" } }];
88
+ }
89
+ };
90
+ var downloadMedia = async (client, media) => {
91
+ try {
92
+ const { url } = await client.media.getUrl(media.id);
93
+ const response = await fetch(url);
94
+ if (!response.ok) {
95
+ throw new Error(`Media download failed: ${response.status}`);
96
+ }
97
+ const data = Buffer.from(await response.arrayBuffer());
98
+ return {
99
+ type: "attachment",
100
+ data,
101
+ mimeType: media.mimeType,
102
+ name: media.filename ?? `media-${media.id}`
103
+ };
104
+ } catch {
105
+ return {
106
+ type: "custom",
107
+ raw: {
108
+ whatsapp_type: "media_error",
109
+ mediaId: media.id,
110
+ mimeType: media.mimeType
111
+ }
112
+ };
113
+ }
114
+ };
115
+ var mimeToMediaType = (mimeType) => {
116
+ if (mimeType.startsWith("image/")) {
117
+ return "image";
118
+ }
119
+ if (mimeType.startsWith("video/")) {
120
+ return "video";
121
+ }
122
+ if (mimeType.startsWith("audio/")) {
123
+ return "audio";
124
+ }
125
+ return "document";
126
+ };
127
+ var messages = (client) => {
128
+ const eventStream = client.events.subscribe().filter(
129
+ (e) => e.type === "message"
130
+ );
131
+ return stream((emit, end) => {
132
+ (async () => {
133
+ try {
134
+ for await (const event of eventStream) {
135
+ const msg = await toMessage(client, event.message);
136
+ emit(msg);
137
+ }
138
+ end();
139
+ } catch (e) {
140
+ end(e);
141
+ }
142
+ })();
143
+ return () => eventStream.close();
144
+ });
145
+ };
146
+ var send = async (client, spaceId, content) => {
147
+ switch (content.type) {
148
+ case "plain_text":
149
+ await client.messages.send({ to: spaceId, text: content.text });
150
+ break;
151
+ case "attachment": {
152
+ const { mediaId } = await client.media.upload({
153
+ file: content.data,
154
+ mimeType: content.mimeType,
155
+ filename: content.name
156
+ });
157
+ const mediaType = mimeToMediaType(content.mimeType);
158
+ const mediaPayload = mediaType === "document" ? { id: mediaId, filename: content.name } : { id: mediaId };
159
+ await client.messages.send({
160
+ to: spaceId,
161
+ [mediaType]: mediaPayload
162
+ });
163
+ break;
164
+ }
165
+ default:
166
+ break;
167
+ }
168
+ };
169
+ var reactToMessage = async (client, spaceId, messageId, reaction) => {
170
+ await client.messages.send({
171
+ to: spaceId,
172
+ reaction: { messageId, emoji: reaction }
173
+ });
174
+ };
175
+ var replyToMessage = async (client, spaceId, messageId, content) => {
176
+ switch (content.type) {
177
+ case "plain_text":
178
+ await client.messages.send({
179
+ to: spaceId,
180
+ replyTo: messageId,
181
+ text: content.text
182
+ });
183
+ break;
184
+ case "attachment": {
185
+ const { mediaId } = await client.media.upload({
186
+ file: content.data,
187
+ mimeType: content.mimeType,
188
+ filename: content.name
189
+ });
190
+ const mediaType = mimeToMediaType(content.mimeType);
191
+ const mediaPayload = mediaType === "document" ? { id: mediaId, filename: content.name } : { id: mediaId };
192
+ await client.messages.send({
193
+ to: spaceId,
194
+ replyTo: messageId,
195
+ [mediaType]: mediaPayload
196
+ });
197
+ break;
198
+ }
199
+ default:
200
+ break;
201
+ }
202
+ };
203
+
204
+ // src/providers/whatsapp-business/types.ts
205
+ import z from "zod";
206
+ var configSchema = z.object({
207
+ accessToken: z.string().min(1),
208
+ phoneNumberId: z.string().min(1),
209
+ appSecret: z.string().optional()
210
+ });
211
+ var userSchema = z.object({});
212
+ var spaceSchema = z.object({
213
+ id: z.string()
214
+ });
215
+
216
+ // src/providers/whatsapp-business/index.ts
217
+ var whatsappBusiness = definePlatform("WhatsApp Business", {
218
+ config: configSchema,
219
+ user: {
220
+ resolve: async ({ input }) => ({ id: input.userID })
221
+ },
222
+ space: {
223
+ schema: spaceSchema,
224
+ resolve: async ({ input }) => {
225
+ if (input.users.length === 0) {
226
+ throw new Error("WhatsApp space creation requires at least one user");
227
+ }
228
+ if (input.users.length > 1) {
229
+ throw new Error(
230
+ "WhatsApp Business API only supports 1:1 conversations"
231
+ );
232
+ }
233
+ const user = input.users[0];
234
+ if (!user) {
235
+ throw new Error("WhatsApp space creation requires a user");
236
+ }
237
+ return { id: user.id };
238
+ }
239
+ },
240
+ lifecycle: {
241
+ createClient: async ({ config }) => {
242
+ return createClient({
243
+ accessToken: config.accessToken,
244
+ phoneNumberId: config.phoneNumberId,
245
+ appSecret: config.appSecret ?? ""
246
+ });
247
+ },
248
+ destroyClient: async ({ client }) => {
249
+ await client.close();
250
+ }
251
+ },
252
+ events: {
253
+ messages: ({ client }) => messages(client)
254
+ },
255
+ actions: {
256
+ send: async ({ space, content, client }) => {
257
+ const wa = client;
258
+ for (const item of content) {
259
+ await send(wa, space.id, item);
260
+ }
261
+ },
262
+ reactToMessage: async ({ space, messageId, reaction, client }) => {
263
+ await reactToMessage(
264
+ client,
265
+ space.id,
266
+ messageId,
267
+ reaction
268
+ );
269
+ },
270
+ replyToMessage: async ({ space, messageId, content, client }) => {
271
+ const wa = client;
272
+ for (const item of content) {
273
+ await replyToMessage(wa, space.id, messageId, item);
274
+ }
275
+ }
276
+ }
277
+ });
278
+ export {
279
+ whatsappBusiness
280
+ };
@@ -117,8 +117,8 @@ interface PlatformDef<_Name extends string = string, _ConfigSchema extends z__de
117
117
  lifecycle: {
118
118
  createClient: (ctx: {
119
119
  config: z__default.infer<_ConfigSchema>;
120
- projectId: string;
121
- projectSecret: string;
120
+ projectId: string | undefined;
121
+ projectSecret: string | undefined;
122
122
  }) => Promise<_Client>;
123
123
  destroyClient: (ctx: {
124
124
  client: _Client;
@@ -252,7 +252,7 @@ interface SpectrumLike<Providers extends PlatformProviderConfig[] = PlatformProv
252
252
  readonly __providers: Providers;
253
253
  }
254
254
  interface Platform<Def extends AnyPlatformDef> {
255
- config(config?: z__default.input<Def["config"]>): PlatformProviderConfig<Def>;
255
+ config(...args: Record<string, never> extends z__default.input<Def["config"]> ? [config?: z__default.input<Def["config"]>] : [config: z__default.input<Def["config"]>]): PlatformProviderConfig<Def>;
256
256
  <Providers extends PlatformProviderConfig[]>(spectrum: SpectrumLike<Providers>): HasProvider<Providers, Def["name"]> extends true ? PlatformInstance<Def> : never;
257
257
  (space: Space): PlatformSpace<Def>;
258
258
  (message: Message): PlatformMessage<Def>;