spectrum-ts 0.5.0 → 0.6.1

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.
@@ -0,0 +1,341 @@
1
+ // src/content/text.ts
2
+ import z from "zod";
3
+ var textSchema = z.object({
4
+ type: z.literal("text"),
5
+ text: z.string().nonempty()
6
+ });
7
+ var asText = (text2) => textSchema.parse({ type: "text", text: text2 });
8
+ function text(text2) {
9
+ return {
10
+ build: async () => asText(text2)
11
+ };
12
+ }
13
+
14
+ // src/content/resolve.ts
15
+ var resolveContents = (items) => Promise.all(
16
+ items.map((c) => typeof c === "string" ? text(c).build() : c.build())
17
+ );
18
+
19
+ // src/platform/build.ts
20
+ function buildSpace(params) {
21
+ const { spaceRef, extras, typingCtx, definition, client, config } = params;
22
+ let space;
23
+ async function sendImpl(...content) {
24
+ const resolved = await resolveContents(content);
25
+ const results = [];
26
+ for (const item of resolved) {
27
+ const sendResult = await definition.actions.send({
28
+ ...typingCtx,
29
+ content: item
30
+ });
31
+ if (!sendResult?.id) {
32
+ throw new Error(
33
+ `Platform "${definition.name}" send did not return a message id`
34
+ );
35
+ }
36
+ results.push(
37
+ buildMessage({
38
+ id: sendResult.id,
39
+ content: item,
40
+ sender: sendResult.sender,
41
+ timestamp: sendResult.timestamp ?? /* @__PURE__ */ new Date(),
42
+ extras: {},
43
+ spaceRef,
44
+ space,
45
+ definition,
46
+ client,
47
+ config,
48
+ direction: "outbound"
49
+ })
50
+ );
51
+ }
52
+ return content.length === 1 && results[0] ? results[0] : results;
53
+ }
54
+ space = {
55
+ ...extras,
56
+ ...spaceRef,
57
+ send: sendImpl,
58
+ edit: async (message, newContent) => {
59
+ await message.edit(newContent);
60
+ },
61
+ startTyping: async () => {
62
+ await definition.actions.startTyping?.(typingCtx);
63
+ },
64
+ stopTyping: async () => {
65
+ await definition.actions.stopTyping?.(typingCtx);
66
+ },
67
+ responding: async (fn) => {
68
+ await definition.actions.startTyping?.(typingCtx);
69
+ try {
70
+ return await fn();
71
+ } finally {
72
+ await definition.actions.stopTyping?.(typingCtx).catch(() => {
73
+ });
74
+ }
75
+ }
76
+ };
77
+ return space;
78
+ }
79
+ function buildMessage(params) {
80
+ const { definition, client, config, spaceRef, space } = params;
81
+ const react = async (reaction) => {
82
+ if (!definition.actions.reactToMessage) {
83
+ return;
84
+ }
85
+ await definition.actions.reactToMessage({
86
+ space: spaceRef,
87
+ messageId: params.id,
88
+ reaction,
89
+ client,
90
+ config
91
+ });
92
+ };
93
+ async function reply(...content) {
94
+ if (!definition.actions.replyToMessage) {
95
+ throw new Error(
96
+ `Platform "${definition.name}" does not support replying to messages`
97
+ );
98
+ }
99
+ const resolved = await resolveContents(content);
100
+ const results = [];
101
+ for (const item of resolved) {
102
+ const sendResult = await definition.actions.replyToMessage({
103
+ space: spaceRef,
104
+ messageId: params.id,
105
+ content: item,
106
+ client,
107
+ config
108
+ });
109
+ if (!sendResult?.id) {
110
+ throw new Error(
111
+ `Platform "${definition.name}" reply did not return a message id`
112
+ );
113
+ }
114
+ results.push(
115
+ buildMessage({
116
+ id: sendResult.id,
117
+ content: item,
118
+ sender: sendResult.sender,
119
+ timestamp: sendResult.timestamp ?? /* @__PURE__ */ new Date(),
120
+ extras: {},
121
+ spaceRef,
122
+ space,
123
+ definition,
124
+ client,
125
+ config,
126
+ direction: "outbound"
127
+ })
128
+ );
129
+ }
130
+ return content.length === 1 && results[0] ? results[0] : results;
131
+ }
132
+ const senderWithPlatform = params.sender === void 0 ? void 0 : { ...params.sender, __platform: definition.name };
133
+ if (params.direction === "outbound") {
134
+ return {
135
+ ...params.extras,
136
+ id: params.id,
137
+ content: params.content,
138
+ direction: "outbound",
139
+ platform: definition.name,
140
+ react,
141
+ reply,
142
+ edit: async (newContent) => {
143
+ if (!definition.actions.editMessage) {
144
+ throw new Error(
145
+ `Platform "${definition.name}" does not support editing messages`
146
+ );
147
+ }
148
+ const [resolved] = await resolveContents([newContent]);
149
+ if (!resolved) {
150
+ return;
151
+ }
152
+ await definition.actions.editMessage({
153
+ space: spaceRef,
154
+ messageId: params.id,
155
+ content: resolved,
156
+ client,
157
+ config
158
+ });
159
+ },
160
+ sender: senderWithPlatform,
161
+ space,
162
+ timestamp: params.timestamp
163
+ };
164
+ }
165
+ return {
166
+ ...params.extras,
167
+ id: params.id,
168
+ content: params.content,
169
+ direction: "inbound",
170
+ platform: definition.name,
171
+ react,
172
+ reply,
173
+ sender: senderWithPlatform,
174
+ space,
175
+ timestamp: params.timestamp
176
+ };
177
+ }
178
+
179
+ // src/platform/define.ts
180
+ function createPlatformInstance(def, runtime) {
181
+ const isPlatformUser = (value) => {
182
+ return typeof value === "object" && value !== null && "__platform" in value && value.__platform === def.name;
183
+ };
184
+ const normalizeSpaceArgs = (args) => {
185
+ if (args.length === 0) {
186
+ return { users: [], params: void 0 };
187
+ }
188
+ const [first, ...rest] = args;
189
+ if (Array.isArray(first)) {
190
+ return {
191
+ users: first,
192
+ params: rest[0]
193
+ };
194
+ }
195
+ if (!isPlatformUser(first)) {
196
+ return {
197
+ users: [],
198
+ params: first
199
+ };
200
+ }
201
+ const last = args.at(-1);
202
+ if (last !== void 0 && !isPlatformUser(last)) {
203
+ return {
204
+ users: args.slice(0, -1),
205
+ params: last
206
+ };
207
+ }
208
+ return {
209
+ users: args,
210
+ params: void 0
211
+ };
212
+ };
213
+ const base = {
214
+ async user(userID) {
215
+ const resolved = await def.user.resolve({
216
+ input: { userID },
217
+ client: runtime.client,
218
+ config: runtime.config
219
+ });
220
+ return {
221
+ ...resolved,
222
+ __platform: def.name
223
+ };
224
+ },
225
+ async space(...args) {
226
+ const { users, params } = normalizeSpaceArgs(args);
227
+ let parsedParams = params;
228
+ if (params !== void 0 && def.space.params) {
229
+ parsedParams = def.space.params.parse(params);
230
+ }
231
+ const resolved = await def.space.resolve({
232
+ input: { users, params: parsedParams },
233
+ client: runtime.client,
234
+ config: runtime.config
235
+ });
236
+ const parsedSpace = def.space.schema ? def.space.schema.parse(resolved) : resolved;
237
+ const spaceRef = {
238
+ id: parsedSpace.id,
239
+ __platform: def.name
240
+ };
241
+ const typingCtx = {
242
+ space: spaceRef,
243
+ client: runtime.client,
244
+ config: runtime.config
245
+ };
246
+ return buildSpace({
247
+ spaceRef,
248
+ extras: parsedSpace,
249
+ typingCtx,
250
+ definition: def,
251
+ client: runtime.client,
252
+ config: runtime.config
253
+ });
254
+ }
255
+ };
256
+ const eventProperties = {};
257
+ for (const eventName of Object.keys(def.events)) {
258
+ if (eventName === "messages") {
259
+ continue;
260
+ }
261
+ const producer = def.events[eventName];
262
+ if (producer) {
263
+ eventProperties[eventName] = producer({
264
+ client: runtime.client,
265
+ config: runtime.config
266
+ });
267
+ }
268
+ }
269
+ return Object.assign(base, eventProperties);
270
+ }
271
+ function definePlatform(name, def) {
272
+ const fullDef = { name, ...def };
273
+ const platformCache = /* @__PURE__ */ new WeakMap();
274
+ const narrowSpectrum = (spectrum) => {
275
+ const cached = platformCache.get(spectrum);
276
+ if (cached) {
277
+ return cached;
278
+ }
279
+ const runtime = spectrum.__internal.platforms.get(name);
280
+ if (!runtime) {
281
+ throw new Error(`Platform "${name}" is not registered`);
282
+ }
283
+ const instance = createPlatformInstance(
284
+ fullDef,
285
+ runtime
286
+ );
287
+ platformCache.set(spectrum, instance);
288
+ return instance;
289
+ };
290
+ const narrowSpace = (input) => {
291
+ if (input.__platform !== name) {
292
+ throw new Error(
293
+ `Expected space from "${name}", got "${input.__platform}"`
294
+ );
295
+ }
296
+ return input;
297
+ };
298
+ const narrowMessage = (input) => {
299
+ if (input.platform !== name) {
300
+ throw new Error(
301
+ `Expected message from "${name}", got "${input.platform}"`
302
+ );
303
+ }
304
+ return input;
305
+ };
306
+ const narrower = ((input) => {
307
+ if ("__providers" in input && "__internal" in input) {
308
+ return narrowSpectrum(input);
309
+ }
310
+ if ("__platform" in input && "send" in input) {
311
+ return narrowSpace(input);
312
+ }
313
+ if ("platform" in input && "sender" in input && "space" in input) {
314
+ return narrowMessage(input);
315
+ }
316
+ throw new Error("Invalid input to platform narrowing function");
317
+ });
318
+ narrower.config = (config) => {
319
+ const resolvedConfig = config ?? {};
320
+ return {
321
+ __tag: "PlatformProviderConfig",
322
+ __def: void 0,
323
+ __name: name,
324
+ config: resolvedConfig,
325
+ __definition: fullDef
326
+ };
327
+ };
328
+ if (def.static) {
329
+ Object.assign(narrower, def.static);
330
+ }
331
+ return narrower;
332
+ }
333
+
334
+ export {
335
+ asText,
336
+ text,
337
+ resolveContents,
338
+ buildSpace,
339
+ buildMessage,
340
+ definePlatform
341
+ };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { C as ContentBuilder, U as User, a as ContentInput, b as Content, P as ProviderMessage, c as PlatformDef, d as Platform, e as PlatformProviderConfig, S as SpectrumLike, f as CustomEventStreams, g as Space, M as Message } from './types-CfiD_00g.js';
2
- export { A as AnyPlatformDef, E as EventProducer, h as PlatformInstance, i as PlatformMessage, j as PlatformSpace, k as PlatformUser, l as SchemaMessage } from './types-CfiD_00g.js';
1
+ import { C as ContentBuilder, U as User, a as ContentInput, b as Content, P as ProviderMessage, c as PlatformDef, d as Platform, e as PlatformProviderConfig, S as SpectrumLike, f as CustomEventStreams, g as Space, I as InboundMessage, O as OutboundMessage } from './types-DZMHfgYQ.js';
2
+ export { A as AnyPlatformDef, E as EventProducer, M as Message, h as PlatformInstance, i as PlatformMessage, j as PlatformSpace, k as PlatformUser, l as SchemaMessage } from './types-DZMHfgYQ.js';
3
3
  import vCard from 'vcf';
4
4
  import z__default from 'zod';
5
5
  export { M as ManagedStream, m as mergeStreams, s as stream } from './stream-DGy4geUK.js';
@@ -161,9 +161,11 @@ declare function definePlatform<_Name extends string, _ConfigSchema extends z__d
161
161
  }): Platform<PlatformDef<_Name, _ConfigSchema, _UserSchema, _SpaceSchema, _SpaceParamsSchema, _Client, _ResolvedUser, _ResolvedSpace, _MessageSchema, _MessageType, _Events>> & Readonly<_Static>;
162
162
 
163
163
  type SpectrumInstance<Providers extends PlatformProviderConfig[] = PlatformProviderConfig[]> = SpectrumLike<Providers> & CustomEventStreams<Providers> & {
164
- readonly messages: AsyncIterable<[Space, Message]>;
164
+ readonly messages: AsyncIterable<[Space, InboundMessage]>;
165
165
  stop(): Promise<void>;
166
- send(space: Space, ...content: [ContentInput, ...ContentInput[]]): Promise<void>;
166
+ send(space: Space, content: ContentInput): Promise<OutboundMessage>;
167
+ send(space: Space, ...content: [ContentInput, ContentInput, ...ContentInput[]]): Promise<OutboundMessage[]>;
168
+ edit(message: OutboundMessage, newContent: ContentInput): Promise<void>;
167
169
  responding<T>(space: Space, fn: () => T | Promise<T>): Promise<T>;
168
170
  };
169
171
  declare function Spectrum<const Providers extends PlatformProviderConfig[]>(options: {
@@ -216,4 +218,4 @@ declare const cloud: {
216
218
  declare const fromVCard: (vcf: string) => ContactInput;
217
219
  declare const toVCard: (contact: Contact) => Promise<string>;
218
220
 
219
- export { type CloudPlatform, type Contact, type ContactAddress, type ContactDetails, type ContactEmail, type ContactInput, type ContactName, type ContactOrg, type ContactPhone, Content, ContentBuilder, ContentInput, 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, User, type Voice, attachment, cloud, contact, custom, definePlatform, fromVCard, resolveContents, text, toVCard, voice };
221
+ export { type CloudPlatform, type Contact, type ContactAddress, type ContactDetails, type ContactEmail, type ContactInput, type ContactName, type ContactOrg, type ContactPhone, Content, ContentBuilder, ContentInput, type DedicatedTokenData, type ImessageInfoData, Platform, PlatformDef, PlatformProviderConfig, type PlatformStatus, type PlatformsData, type SharedTokenData, Space, Spectrum, SpectrumCloudError, type SpectrumInstance, type SubscriptionData, type SubscriptionStatus, type TokenData, User, type Voice, attachment, cloud, contact, custom, definePlatform, fromVCard, resolveContents, text, toVCard, voice };
package/dist/index.js CHANGED
@@ -15,10 +15,12 @@ import {
15
15
  toVCard
16
16
  } from "./chunk-UZWRB3FZ.js";
17
17
  import {
18
+ buildMessage,
19
+ buildSpace,
18
20
  definePlatform,
19
21
  resolveContents,
20
22
  text
21
- } from "./chunk-XEEDIGVK.js";
23
+ } from "./chunk-XZTTLPHE.js";
22
24
 
23
25
  // src/content/voice.ts
24
26
  import { createReadStream } from "fs";
@@ -207,72 +209,27 @@ async function Spectrum(options) {
207
209
  __platform: definition.name
208
210
  };
209
211
  const typingCtx = { space: spaceRef, client, config };
210
- const space = {
211
- ...spaceRef,
212
- send: async (...content) => {
213
- const resolved = await resolveContents(content);
214
- for (const item of resolved) {
215
- await definition.actions.send({
216
- ...typingCtx,
217
- content: item
218
- });
219
- }
220
- },
221
- startTyping: async () => {
222
- await definition.actions.startTyping?.(typingCtx);
223
- },
224
- stopTyping: async () => {
225
- await definition.actions.stopTyping?.(typingCtx);
226
- },
227
- responding: async (fn) => {
228
- await definition.actions.startTyping?.(typingCtx);
229
- try {
230
- return await fn();
231
- } finally {
232
- await definition.actions.stopTyping?.(typingCtx).catch(() => {
233
- });
234
- }
235
- }
236
- };
237
- const normalizedMessage = {
238
- ...parsedExtra,
212
+ const space = buildSpace({
213
+ spaceRef,
214
+ extras: {},
215
+ typingCtx,
216
+ definition,
217
+ client,
218
+ config
219
+ });
220
+ const normalizedMessage = buildMessage({
239
221
  id: msg.id,
240
222
  content: msg.content,
241
- platform: definition.name,
242
- react: async (reaction) => {
243
- if (!definition.actions.reactToMessage) {
244
- return;
245
- }
246
- await definition.actions.reactToMessage({
247
- space: spaceRef,
248
- messageId: msg.id,
249
- reaction,
250
- client,
251
- config
252
- });
253
- },
254
- reply: async (...content) => {
255
- if (!definition.actions.replyToMessage) {
256
- return;
257
- }
258
- const resolved = await resolveContents(content);
259
- for (const item of resolved) {
260
- await definition.actions.replyToMessage({
261
- space: spaceRef,
262
- messageId: msg.id,
263
- content: item,
264
- client,
265
- config
266
- });
267
- }
268
- },
269
- sender: {
270
- ...msg.sender,
271
- __platform: definition.name
272
- },
223
+ sender: msg.sender,
224
+ timestamp: msg.timestamp ?? /* @__PURE__ */ new Date(),
225
+ extras: parsedExtra,
226
+ spaceRef,
273
227
  space,
274
- timestamp: msg.timestamp ?? /* @__PURE__ */ new Date()
275
- };
228
+ definition,
229
+ client,
230
+ config,
231
+ direction: "inbound"
232
+ });
276
233
  yield [space, normalizedMessage];
277
234
  }
278
235
  };
@@ -383,8 +340,13 @@ async function Spectrum(options) {
383
340
  __internal: { platforms: platformStates },
384
341
  messages,
385
342
  stop: stopOnce,
386
- send: async (space, ...content) => {
387
- await space.send(...content);
343
+ send: (async (space, ...content) => {
344
+ return content.length === 1 ? await space.send(content[0]) : await space.send(
345
+ ...content
346
+ );
347
+ }),
348
+ edit: async (message, newContent) => {
349
+ await message.edit(newContent);
388
350
  },
389
351
  responding: async (space, fn) => {
390
352
  return space.responding(fn);
@@ -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 { l as SchemaMessage, d as Platform, c as PlatformDef, P as ProviderMessage } from '../../types-CfiD_00g.js';
6
+ import { l as SchemaMessage, d as Platform, c as PlatformDef, P as ProviderMessage } from '../../types-DZMHfgYQ.js';
7
7
  import * as zod_v4_core from 'zod/v4/core';
8
8
  import 'hotscript';
9
9