spectrum-ts 0.8.0 → 0.9.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.
@@ -2,10 +2,38 @@ import {
2
2
  bufferToStream,
3
3
  readSchema,
4
4
  streamSchema
5
- } from "./chunk-ZNUORCLB.js";
5
+ } from "./chunk-OIXH5S65.js";
6
+ import {
7
+ resolveContents
8
+ } from "./chunk-U6WCQVVX.js";
6
9
 
7
- // src/content/richlink.ts
10
+ // src/content/group.ts
8
11
  import z from "zod";
12
+ var isMessage = (v) => typeof v === "object" && v !== null && "id" in v && "content" in v;
13
+ var groupSchema = z.object({
14
+ type: z.literal("group"),
15
+ items: z.array(z.custom(isMessage)).min(2)
16
+ });
17
+ var asGroup = (input) => groupSchema.parse({ type: "group", items: input.items });
18
+ var stubOutboundMessage = (content) => ({ id: "", content });
19
+ function group(...items) {
20
+ return {
21
+ build: async () => {
22
+ const resolved = await resolveContents(items);
23
+ const members = [];
24
+ for (const item of resolved) {
25
+ if (item.type === "group" || item.type === "reaction") {
26
+ throw new Error(`group() cannot contain "${item.type}" items`);
27
+ }
28
+ members.push(stubOutboundMessage(item));
29
+ }
30
+ return asGroup({ items: members });
31
+ }
32
+ };
33
+ }
34
+
35
+ // src/content/richlink.ts
36
+ import z2 from "zod";
9
37
 
10
38
  // src/utils/link-metadata.ts
11
39
  import ogs from "open-graph-scraper";
@@ -76,22 +104,22 @@ var fetchImage = async (url) => {
76
104
  };
77
105
 
78
106
  // src/content/richlink.ts
79
- var richlinkCoverSchema = z.object({
80
- mimeType: z.string().min(1).optional(),
107
+ var richlinkCoverSchema = z2.object({
108
+ mimeType: z2.string().min(1).optional(),
81
109
  read: readSchema,
82
110
  stream: streamSchema
83
111
  });
84
- var optionalStringAccessor = z.function({
112
+ var optionalStringAccessor = z2.function({
85
113
  input: [],
86
- output: z.promise(z.string().min(1).optional())
114
+ output: z2.promise(z2.string().min(1).optional())
87
115
  });
88
- var coverAccessor = z.function({
116
+ var coverAccessor = z2.function({
89
117
  input: [],
90
- output: z.promise(richlinkCoverSchema.optional())
118
+ output: z2.promise(richlinkCoverSchema.optional())
91
119
  });
92
- var richlinkSchema = z.object({
93
- type: z.literal("richlink"),
94
- url: z.url(),
120
+ var richlinkSchema = z2.object({
121
+ type: z2.literal("richlink"),
122
+ url: z2.url(),
95
123
  title: optionalStringAccessor,
96
124
  summary: optionalStringAccessor,
97
125
  cover: coverAccessor
@@ -136,6 +164,8 @@ function richlink(url) {
136
164
  }
137
165
 
138
166
  export {
167
+ asGroup,
168
+ group,
139
169
  asRichlink,
140
170
  richlink
141
171
  };
@@ -553,6 +553,21 @@ function custom(raw) {
553
553
  };
554
554
  }
555
555
 
556
+ // src/content/reaction.ts
557
+ import z5 from "zod";
558
+ var isMessage = (v) => typeof v === "object" && v !== null && "id" in v && "content" in v;
559
+ var reactionSchema = z5.object({
560
+ type: z5.literal("reaction"),
561
+ emoji: z5.string().min(1),
562
+ target: z5.custom(isMessage, {
563
+ message: "reaction target must be a Message"
564
+ })
565
+ });
566
+ var asReaction = (input) => reactionSchema.parse({ type: "reaction", ...input });
567
+ function reaction(emoji, target) {
568
+ return { build: async () => asReaction({ emoji, target }) };
569
+ }
570
+
556
571
  // src/utils/stream.ts
557
572
  import { Repeater } from "@repeaterjs/repeater";
558
573
  function stream(setup) {
@@ -688,6 +703,8 @@ export {
688
703
  contact,
689
704
  asCustom,
690
705
  custom,
706
+ asReaction,
707
+ reaction,
691
708
  stream,
692
709
  mergeStreams,
693
710
  SpectrumCloudError,
@@ -88,37 +88,117 @@ var warnUnsupported = (err, fallbackPlatform) => {
88
88
  supportsAnsiColor() ? `${ANSI_YELLOW}${body}${ANSI_RESET}` : body
89
89
  );
90
90
  };
91
+ var providerMessageCoreKeys = /* @__PURE__ */ new Set([
92
+ "content",
93
+ "id",
94
+ "sender",
95
+ "space",
96
+ "timestamp"
97
+ ]);
98
+ var extractExtras = (raw, definition) => {
99
+ const entries = Object.entries(raw).filter(
100
+ ([key]) => !providerMessageCoreKeys.has(key)
101
+ );
102
+ const extra = Object.fromEntries(entries);
103
+ return definition.message?.schema ? definition.message.schema.parse(extra) : extra;
104
+ };
105
+ function wrapProviderMessage(raw, ctx) {
106
+ const wrappedContent = wrapNestedContent(raw.content, ctx);
107
+ return buildMessage({
108
+ id: raw.id,
109
+ content: wrappedContent,
110
+ sender: raw.sender,
111
+ timestamp: raw.timestamp ?? /* @__PURE__ */ new Date(),
112
+ extras: extractExtras(raw, ctx.definition),
113
+ spaceRef: ctx.spaceRef,
114
+ space: ctx.space,
115
+ definition: ctx.definition,
116
+ client: ctx.client,
117
+ config: ctx.config,
118
+ direction: "inbound"
119
+ });
120
+ }
121
+ var wrapNestedContent = (content, ctx) => {
122
+ if (content.type === "reaction") {
123
+ const target = content.target;
124
+ if (isRawProviderRecord(target)) {
125
+ return {
126
+ ...content,
127
+ target: wrapProviderMessage(target, ctx)
128
+ };
129
+ }
130
+ return content;
131
+ }
132
+ if (content.type === "group") {
133
+ const items = content.items.map((item) => {
134
+ const raw = item;
135
+ return isRawProviderRecord(raw) ? wrapProviderMessage(raw, ctx) : item;
136
+ });
137
+ return { ...content, items };
138
+ }
139
+ return content;
140
+ };
141
+ var isRawProviderRecord = (v) => {
142
+ if (typeof v !== "object" || v === null) {
143
+ return false;
144
+ }
145
+ const record = v;
146
+ return "id" in record && "content" in record && typeof record.react !== "function" && typeof record.reply !== "function";
147
+ };
91
148
  function buildSpace(params) {
92
149
  const { spaceRef, extras, typingCtx, definition, client, config } = params;
93
150
  let space;
94
- async function sendImpl(...content) {
95
- const resolved = await resolveContents(content);
96
- const results = [];
97
- for (const item of resolved) {
98
- let sendResult;
99
- try {
100
- sendResult = await definition.actions.send({
101
- ...typingCtx,
102
- content: item
103
- });
104
- } catch (err) {
105
- if (err instanceof UnsupportedError) {
106
- warnUnsupported(err, definition.name);
107
- continue;
108
- }
109
- throw err;
151
+ async function dispatchReaction(item) {
152
+ try {
153
+ if (!definition.actions.reactToMessage) {
154
+ throw UnsupportedError.action("react", definition.name);
110
155
  }
111
- if (!sendResult?.id) {
112
- throw new Error(
113
- `Platform "${definition.name}" send did not return a message id`
114
- );
156
+ await definition.actions.reactToMessage({
157
+ space: spaceRef,
158
+ target: item.target,
159
+ reaction: item.emoji,
160
+ client,
161
+ config
162
+ });
163
+ } catch (err) {
164
+ if (err instanceof UnsupportedError) {
165
+ warnUnsupported(err, definition.name);
166
+ return;
115
167
  }
116
- results.push(
117
- buildMessage({
118
- id: sendResult.id,
119
- content: item,
120
- sender: sendResult.sender,
121
- timestamp: sendResult.timestamp ?? /* @__PURE__ */ new Date(),
168
+ throw err;
169
+ }
170
+ }
171
+ async function dispatchSend(item) {
172
+ let sendResult;
173
+ try {
174
+ sendResult = await definition.actions.send({
175
+ ...typingCtx,
176
+ content: item
177
+ });
178
+ } catch (err) {
179
+ if (err instanceof UnsupportedError) {
180
+ warnUnsupported(err, definition.name);
181
+ return;
182
+ }
183
+ throw err;
184
+ }
185
+ if (!sendResult?.id) {
186
+ throw new Error(
187
+ `Platform "${definition.name}" send did not return a message id`
188
+ );
189
+ }
190
+ const outboundContent = item.type === "group" && sendResult.groupMembers ? {
191
+ ...item,
192
+ items: item.items.map((stub, idx) => {
193
+ const member = sendResult?.groupMembers?.[idx];
194
+ if (!member?.id) {
195
+ return stub;
196
+ }
197
+ return buildMessage({
198
+ id: member.id,
199
+ content: stub.content,
200
+ sender: member.sender,
201
+ timestamp: member.timestamp ?? /* @__PURE__ */ new Date(),
122
202
  extras: {},
123
203
  spaceRef,
124
204
  space,
@@ -126,14 +206,75 @@ function buildSpace(params) {
126
206
  client,
127
207
  config,
128
208
  direction: "outbound"
129
- })
130
- );
209
+ });
210
+ })
211
+ } : item;
212
+ return buildMessage({
213
+ id: sendResult.id,
214
+ content: outboundContent,
215
+ sender: sendResult.sender,
216
+ timestamp: sendResult.timestamp ?? /* @__PURE__ */ new Date(),
217
+ extras: {},
218
+ spaceRef,
219
+ space,
220
+ definition,
221
+ client,
222
+ config,
223
+ direction: "outbound"
224
+ });
225
+ }
226
+ async function sendImpl(...content) {
227
+ const resolved = await resolveContents(content);
228
+ const results = [];
229
+ for (const item of resolved) {
230
+ if (item.type === "reaction") {
231
+ await dispatchReaction(item);
232
+ continue;
233
+ }
234
+ const sent = await dispatchSend(item);
235
+ if (sent) {
236
+ results.push(sent);
237
+ }
131
238
  }
132
239
  if (content.length === 1) {
133
240
  return results[0];
134
241
  }
135
242
  return results;
136
243
  }
244
+ async function getMessageImpl(id) {
245
+ if (!definition.actions.getMessage) {
246
+ warnUnsupported(
247
+ UnsupportedError.action("getMessage", definition.name),
248
+ definition.name
249
+ );
250
+ return;
251
+ }
252
+ let raw;
253
+ try {
254
+ raw = await definition.actions.getMessage({
255
+ space: spaceRef,
256
+ messageId: id,
257
+ client,
258
+ config
259
+ });
260
+ } catch (err) {
261
+ if (err instanceof UnsupportedError) {
262
+ warnUnsupported(err, definition.name);
263
+ return;
264
+ }
265
+ throw err;
266
+ }
267
+ if (!raw) {
268
+ return;
269
+ }
270
+ return wrapProviderMessage(raw, {
271
+ client,
272
+ config,
273
+ definition,
274
+ space,
275
+ spaceRef
276
+ });
277
+ }
137
278
  space = {
138
279
  ...extras,
139
280
  ...spaceRef,
@@ -141,6 +282,7 @@ function buildSpace(params) {
141
282
  edit: async (message, newContent) => {
142
283
  await message.edit(newContent);
143
284
  },
285
+ getMessage: getMessageImpl,
144
286
  startTyping: async () => {
145
287
  await definition.actions.startTyping?.(typingCtx);
146
288
  },
@@ -161,6 +303,7 @@ function buildSpace(params) {
161
303
  }
162
304
  function buildMessage(params) {
163
305
  const { definition, client, config, spaceRef, space } = params;
306
+ let self;
164
307
  const react = async (reaction) => {
165
308
  if (!definition.actions.reactToMessage) {
166
309
  warnUnsupported(
@@ -169,13 +312,26 @@ function buildMessage(params) {
169
312
  );
170
313
  return;
171
314
  }
172
- await definition.actions.reactToMessage({
173
- space: spaceRef,
174
- messageId: params.id,
175
- reaction,
176
- client,
177
- config
178
- });
315
+ if (!self) {
316
+ throw new Error(
317
+ "react() called before message construction completed (internal bug)"
318
+ );
319
+ }
320
+ try {
321
+ await definition.actions.reactToMessage({
322
+ space: spaceRef,
323
+ target: self,
324
+ reaction,
325
+ client,
326
+ config
327
+ });
328
+ } catch (err) {
329
+ if (err instanceof UnsupportedError) {
330
+ warnUnsupported(err, definition.name);
331
+ return;
332
+ }
333
+ throw err;
334
+ }
179
335
  };
180
336
  async function reply(...content) {
181
337
  if (!definition.actions.replyToMessage) {
@@ -232,7 +388,7 @@ function buildMessage(params) {
232
388
  }
233
389
  const senderWithPlatform = params.sender === void 0 ? void 0 : { ...params.sender, __platform: definition.name };
234
390
  if (params.direction === "outbound") {
235
- return {
391
+ const outbound = {
236
392
  ...params.extras,
237
393
  id: params.id,
238
394
  content: params.content,
@@ -272,8 +428,10 @@ function buildMessage(params) {
272
428
  space,
273
429
  timestamp: params.timestamp
274
430
  };
431
+ self = outbound;
432
+ return outbound;
275
433
  }
276
- return {
434
+ const inbound = {
277
435
  ...params.extras,
278
436
  id: params.id,
279
437
  content: params.content,
@@ -285,6 +443,8 @@ function buildMessage(params) {
285
443
  space,
286
444
  timestamp: params.timestamp
287
445
  };
446
+ self = inbound;
447
+ return inbound;
288
448
  }
289
449
 
290
450
  // src/platform/define.ts
@@ -471,7 +631,7 @@ export {
471
631
  text,
472
632
  resolveContents,
473
633
  UnsupportedError,
634
+ wrapProviderMessage,
474
635
  buildSpace,
475
- buildMessage,
476
636
  definePlatform
477
637
  };