spectrum-ts 0.7.0 → 0.8.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.
@@ -292,6 +292,29 @@ function createPlatformInstance(def, runtime) {
292
292
  const isPlatformUser = (value) => {
293
293
  return typeof value === "object" && value !== null && "__platform" in value && value.__platform === def.name;
294
294
  };
295
+ const resolveUserID = async (userID) => {
296
+ const resolved = await def.user.resolve({
297
+ input: { userID },
298
+ client: runtime.client,
299
+ config: runtime.config
300
+ });
301
+ return {
302
+ ...resolved,
303
+ __platform: def.name
304
+ };
305
+ };
306
+ const resolveStringUsers = async (args) => {
307
+ const convertArg = async (arg) => {
308
+ if (typeof arg === "string") {
309
+ return await resolveUserID(arg);
310
+ }
311
+ if (Array.isArray(arg)) {
312
+ return await Promise.all(arg.map(convertArg));
313
+ }
314
+ return arg;
315
+ };
316
+ return await Promise.all(args.map(convertArg));
317
+ };
295
318
  const normalizeSpaceArgs = (args) => {
296
319
  if (args.length === 0) {
297
320
  return { users: [], params: void 0 };
@@ -334,7 +357,8 @@ function createPlatformInstance(def, runtime) {
334
357
  };
335
358
  },
336
359
  async space(...args) {
337
- const { users, params } = normalizeSpaceArgs(args);
360
+ const convertedArgs = await resolveStringUsers(args);
361
+ const { users, params } = normalizeSpaceArgs(convertedArgs);
338
362
  let parsedParams = params;
339
363
  if (params !== void 0 && def.space.params) {
340
364
  parsedParams = def.space.params.parse(params);
@@ -0,0 +1,141 @@
1
+ import {
2
+ bufferToStream,
3
+ readSchema,
4
+ streamSchema
5
+ } from "./chunk-ZNUORCLB.js";
6
+
7
+ // src/content/richlink.ts
8
+ import z from "zod";
9
+
10
+ // src/utils/link-metadata.ts
11
+ import ogs from "open-graph-scraper";
12
+ var DEFAULT_TIMEOUT_MS = 5e3;
13
+ var USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 14_0) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Safari/605.1.15 spectrum-ts/richlink";
14
+ var normaliseImageUrl = (raw, base) => {
15
+ try {
16
+ return new URL(raw, base).toString();
17
+ } catch {
18
+ return;
19
+ }
20
+ };
21
+ var cleanString = (v) => {
22
+ if (typeof v !== "string") {
23
+ return;
24
+ }
25
+ const trimmed = v.trim();
26
+ return trimmed.length > 0 ? trimmed : void 0;
27
+ };
28
+ var fetchLinkMetadata = async (url) => {
29
+ try {
30
+ const result = await ogs({
31
+ url,
32
+ timeout: DEFAULT_TIMEOUT_MS,
33
+ fetchOptions: { headers: { "User-Agent": USER_AGENT } }
34
+ });
35
+ if (result.error) {
36
+ return {};
37
+ }
38
+ const {
39
+ ogTitle,
40
+ ogDescription,
41
+ ogImage,
42
+ twitterTitle,
43
+ twitterDescription,
44
+ twitterImage
45
+ } = result.result;
46
+ const title = cleanString(ogTitle) ?? cleanString(twitterTitle);
47
+ const summary = cleanString(ogDescription) ?? cleanString(twitterDescription);
48
+ const imageCandidate = ogImage?.[0] ?? twitterImage?.[0];
49
+ const resolved = imageCandidate ? normaliseImageUrl(imageCandidate.url, url) : void 0;
50
+ const image = imageCandidate && resolved ? {
51
+ url: resolved,
52
+ mimeType: "type" in imageCandidate && typeof imageCandidate.type === "string" ? imageCandidate.type : void 0
53
+ } : void 0;
54
+ return { title, summary, image };
55
+ } catch {
56
+ return {};
57
+ }
58
+ };
59
+ var fetchImage = async (url) => {
60
+ const controller = new AbortController();
61
+ const timer = setTimeout(() => controller.abort(), DEFAULT_TIMEOUT_MS);
62
+ try {
63
+ const res = await fetch(url, {
64
+ signal: controller.signal,
65
+ headers: { "User-Agent": USER_AGENT }
66
+ });
67
+ if (!res.ok) {
68
+ throw new Error(`image fetch ${url} returned ${res.status}`);
69
+ }
70
+ const data = Buffer.from(await res.arrayBuffer());
71
+ const mimeType = res.headers.get("content-type") ?? void 0;
72
+ return { data, mimeType };
73
+ } finally {
74
+ clearTimeout(timer);
75
+ }
76
+ };
77
+
78
+ // src/content/richlink.ts
79
+ var richlinkCoverSchema = z.object({
80
+ mimeType: z.string().min(1).optional(),
81
+ read: readSchema,
82
+ stream: streamSchema
83
+ });
84
+ var optionalStringAccessor = z.function({
85
+ input: [],
86
+ output: z.promise(z.string().min(1).optional())
87
+ });
88
+ var coverAccessor = z.function({
89
+ input: [],
90
+ output: z.promise(richlinkCoverSchema.optional())
91
+ });
92
+ var richlinkSchema = z.object({
93
+ type: z.literal("richlink"),
94
+ url: z.url(),
95
+ title: optionalStringAccessor,
96
+ summary: optionalStringAccessor,
97
+ cover: coverAccessor
98
+ });
99
+ var memoize = (factory) => {
100
+ let cached;
101
+ return () => {
102
+ cached ??= factory();
103
+ return cached;
104
+ };
105
+ };
106
+ var buildCover = (image) => {
107
+ const read = memoize(
108
+ () => fetchImage(image.url).then((r) => r.data).catch(() => Buffer.alloc(0))
109
+ );
110
+ return {
111
+ mimeType: image.mimeType,
112
+ read,
113
+ stream: async () => bufferToStream(await read())
114
+ };
115
+ };
116
+ var asRichlink = (input) => {
117
+ const getMetadata = memoize(() => fetchLinkMetadata(input.url));
118
+ const getCover = memoize(async () => {
119
+ const { image } = await getMetadata();
120
+ return image ? buildCover(image) : void 0;
121
+ });
122
+ const title = async () => (await getMetadata()).title;
123
+ const summary = async () => (await getMetadata()).summary;
124
+ return richlinkSchema.parse({
125
+ type: "richlink",
126
+ url: input.url,
127
+ title,
128
+ summary,
129
+ cover: getCover
130
+ });
131
+ };
132
+ function richlink(url) {
133
+ return {
134
+ build: async () => asRichlink({ url })
135
+ };
136
+ }
137
+
138
+ export {
139
+ asRichlink,
140
+ richlink
141
+ };
@@ -557,11 +557,13 @@ function custom(raw) {
557
557
  import { Repeater } from "@repeaterjs/repeater";
558
558
  function stream(setup) {
559
559
  const repeater = new Repeater(async (push, stop) => {
560
- const emit = (value) => {
561
- Promise.resolve(push(value)).catch((error) => {
560
+ const emit = async (value) => {
561
+ try {
562
+ await push(value);
563
+ } catch (error) {
562
564
  stop(error);
563
- return void 0;
564
- });
565
+ throw error;
566
+ }
565
567
  };
566
568
  const end = (error) => {
567
569
  stop(error);
@@ -589,7 +591,7 @@ function mergeStreams(streams) {
589
591
  const workers = streams.map(async (source) => {
590
592
  try {
591
593
  for await (const value of source) {
592
- emit(value);
594
+ await emit(value);
593
595
  }
594
596
  } catch (error) {
595
597
  end(error);
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
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-B5tTx5hc.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-B5tTx5hc.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-DLrsDzV-.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-DLrsDzV-.js';
3
3
  import vCard from 'vcf';
4
4
  import z__default from 'zod';
5
- export { M as ManagedStream, m as mergeStreams, s as stream } from './stream-DGy4geUK.js';
5
+ export { M as ManagedStream, m as mergeStreams, s as stream } from './stream-B55k7W8-.js';
6
6
  import 'hotscript';
7
7
 
8
8
  declare function attachment(input: string | Buffer, options?: {
@@ -124,6 +124,20 @@ declare function custom(raw: unknown): ContentBuilder;
124
124
 
125
125
  declare const resolveContents: (items: readonly ContentInput[]) => Promise<Content[]>;
126
126
 
127
+ declare const richlinkSchema: z__default.ZodObject<{
128
+ type: z__default.ZodLiteral<"richlink">;
129
+ url: z__default.ZodURL;
130
+ title: z__default.ZodFunction<z__default.ZodTuple<readonly [], null>, z__default.ZodPromise<z__default.ZodOptional<z__default.ZodString>>>;
131
+ summary: z__default.ZodFunction<z__default.ZodTuple<readonly [], null>, z__default.ZodPromise<z__default.ZodOptional<z__default.ZodString>>>;
132
+ cover: z__default.ZodFunction<z__default.ZodTuple<readonly [], null>, z__default.ZodPromise<z__default.ZodOptional<z__default.ZodObject<{
133
+ mimeType: z__default.ZodOptional<z__default.ZodString>;
134
+ read: z__default.ZodFunction<z__default.ZodTuple<readonly [], null>, z__default.ZodPromise<z__default.ZodCustom<Buffer<ArrayBufferLike>, Buffer<ArrayBufferLike>>>>;
135
+ stream: z__default.ZodFunction<z__default.ZodTuple<readonly [], null>, z__default.ZodPromise<z__default.ZodCustom<ReadableStream<unknown>, ReadableStream<unknown>>>>;
136
+ }, z__default.core.$strip>>>>;
137
+ }, z__default.core.$strip>;
138
+ type Richlink = z__default.infer<typeof richlinkSchema>;
139
+ declare function richlink(url: string): ContentBuilder;
140
+
127
141
  declare function text(text: string): ContentBuilder;
128
142
 
129
143
  declare const voiceSchema: z__default.ZodObject<{
@@ -244,4 +258,4 @@ declare class UnsupportedError extends Error {
244
258
  declare const fromVCard: (vcf: string) => ContactInput;
245
259
  declare const toVCard: (contact: Contact) => Promise<string>;
246
260
 
247
- 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, UnsupportedError, type UnsupportedKind, User, type Voice, attachment, cloud, contact, custom, definePlatform, fromVCard, resolveContents, text, toVCard, voice };
261
+ 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 Richlink, type SharedTokenData, Space, Spectrum, SpectrumCloudError, type SpectrumInstance, type SubscriptionData, type SubscriptionStatus, type TokenData, UnsupportedError, type UnsupportedKind, User, type Voice, attachment, cloud, contact, custom, definePlatform, fromVCard, resolveContents, richlink, text, toVCard, voice };
package/dist/index.js CHANGED
@@ -1,3 +1,6 @@
1
+ import {
2
+ richlink
3
+ } from "./chunk-H5XYVRHM.js";
1
4
  import {
2
5
  SpectrumCloudError,
3
6
  attachment,
@@ -11,7 +14,7 @@ import {
11
14
  stream,
12
15
  streamSchema,
13
16
  toVCard
14
- } from "./chunk-GX3JCGSD.js";
17
+ } from "./chunk-ZNUORCLB.js";
15
18
  import {
16
19
  UnsupportedError,
17
20
  buildMessage,
@@ -19,7 +22,7 @@ import {
19
22
  definePlatform,
20
23
  resolveContents,
21
24
  text
22
- } from "./chunk-6URE4AYH.js";
25
+ } from "./chunk-4O6MQC5Z.js";
23
26
 
24
27
  // src/content/voice.ts
25
28
  import { createReadStream } from "fs";
@@ -173,11 +176,11 @@ async function Spectrum(options) {
173
176
  const adaptIterable = (iterable) => {
174
177
  return stream((emit, end) => {
175
178
  const iterator = iterable[Symbol.asyncIterator]();
176
- (async () => {
179
+ const pump = (async () => {
177
180
  try {
178
181
  let result = await iterator.next();
179
182
  while (!result.done) {
180
- emit(result.value);
183
+ await emit(result.value);
181
184
  result = await iterator.next();
182
185
  }
183
186
  end();
@@ -187,6 +190,7 @@ async function Spectrum(options) {
187
190
  })();
188
191
  return async () => {
189
192
  await iterator.return?.();
193
+ await pump;
190
194
  };
191
195
  });
192
196
  };
@@ -235,14 +239,14 @@ async function Spectrum(options) {
235
239
  return adaptIterable(bindSend());
236
240
  };
237
241
  const createMessagesStream = () => {
238
- return stream(async (emit, end) => {
242
+ return stream((emit, end) => {
239
243
  const merged = mergeStreams(
240
244
  Array.from(platformStates.values(), createProviderMessagesStream)
241
245
  );
242
- (async () => {
246
+ const pump = (async () => {
243
247
  try {
244
248
  for await (const value of merged) {
245
- emit(value);
249
+ await emit(value);
246
250
  }
247
251
  end();
248
252
  } catch (error) {
@@ -251,11 +255,12 @@ async function Spectrum(options) {
251
255
  })();
252
256
  return async () => {
253
257
  await merged.close();
258
+ await pump;
254
259
  };
255
260
  });
256
261
  };
257
262
  const createCustomEventStream = (eventName) => {
258
- return stream(async (emit, end) => {
263
+ return stream((emit, end) => {
259
264
  const providerStreams = Array.from(platformStates.values(), (state) => {
260
265
  const { client, config, definition } = state;
261
266
  const producer = definition.events[eventName];
@@ -273,10 +278,10 @@ async function Spectrum(options) {
273
278
  (value) => value !== void 0
274
279
  );
275
280
  const merged = mergeStreams(providerStreams);
276
- (async () => {
281
+ const pump = (async () => {
277
282
  try {
278
283
  for await (const value of merged) {
279
- emit(value);
284
+ await emit(value);
280
285
  }
281
286
  end();
282
287
  } catch (error) {
@@ -285,6 +290,7 @@ async function Spectrum(options) {
285
290
  })();
286
291
  return async () => {
287
292
  await merged.close();
293
+ await pump;
288
294
  };
289
295
  });
290
296
  };
@@ -375,6 +381,7 @@ export {
375
381
  fromVCard,
376
382
  mergeStreams,
377
383
  resolveContents,
384
+ richlink,
378
385
  stream,
379
386
  text,
380
387
  toVCard,
@@ -1,9 +1,9 @@
1
- import { M as ManagedStream } from '../../stream-DGy4geUK.js';
1
+ import { M as ManagedStream } from '../../stream-B55k7W8-.js';
2
2
  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-B5tTx5hc.js';
6
+ import { l as SchemaMessage, d as Platform, c as PlatformDef, P as ProviderMessage } from '../../types-DLrsDzV-.js';
7
7
  import * as zod_v4_core from 'zod/v4/core';
8
8
  import 'hotscript';
9
9
 
@@ -1,3 +1,6 @@
1
+ import {
2
+ asRichlink
3
+ } from "../../chunk-H5XYVRHM.js";
1
4
  import {
2
5
  asAttachment,
3
6
  asContact,
@@ -7,12 +10,12 @@ import {
7
10
  mergeStreams,
8
11
  stream,
9
12
  toVCard
10
- } from "../../chunk-GX3JCGSD.js";
13
+ } from "../../chunk-ZNUORCLB.js";
11
14
  import {
12
15
  UnsupportedError,
13
16
  asText,
14
17
  definePlatform
15
- } from "../../chunk-6URE4AYH.js";
18
+ } from "../../chunk-4O6MQC5Z.js";
16
19
 
17
20
  // src/providers/imessage/index.ts
18
21
  import { createClient as createClient2, directChat } from "@photon-ai/advanced-imessage";
@@ -191,9 +194,9 @@ var messages = (client) => stream((emit, end) => {
191
194
  let lastPromise = Promise.resolve();
192
195
  const startPromise = client.startWatching({
193
196
  onIncomingMessage: (message) => {
194
- lastPromise = lastPromise.then(() => toMessages(message)).then((ms) => {
197
+ lastPromise = lastPromise.then(() => toMessages(message)).then(async (ms) => {
195
198
  for (const m of ms) {
196
- emit(m);
199
+ await emit(m);
197
200
  }
198
201
  }).catch(end);
199
202
  },
@@ -378,6 +381,7 @@ var ensureM4a = async (buffer, mimeType) => {
378
381
 
379
382
  // src/providers/imessage/remote.ts
380
383
  var PLATFORM = "iMessage";
384
+ var URL_BALLOON_BUNDLE_ID = "com.apple.messages.URLBalloonProvider";
381
385
  var unsupportedContent = (type) => UnsupportedError.content(type, PLATFORM);
382
386
  var toSendResult = (receipt) => ({
383
387
  id: receipt.guid,
@@ -422,9 +426,29 @@ var toVCardContent2 = async (client, info) => {
422
426
  return toAttachmentContent2(client, info);
423
427
  }
424
428
  };
429
+ var getBalloonBundleId = (message) => {
430
+ const raw = message._raw;
431
+ const id = raw?.balloonBundleId;
432
+ return typeof id === "string" ? id : void 0;
433
+ };
434
+ var toRichlinkMessage = (event, base, id) => {
435
+ const url = event.message.text ?? "";
436
+ try {
437
+ return { ...base, id, content: asRichlink({ url }) };
438
+ } catch {
439
+ return {
440
+ ...base,
441
+ id,
442
+ content: url ? asText(url) : asCustom(event.message)
443
+ };
444
+ }
445
+ };
425
446
  var toMessages2 = async (client, event) => {
426
447
  const base = baseMessage(event);
427
448
  const messageGuidStr = event.message.guid;
449
+ if (getBalloonBundleId(event.message) === URL_BALLOON_BUNDLE_ID) {
450
+ return [toRichlinkMessage(event, base, messageGuidStr)];
451
+ }
428
452
  if (event.message.attachments.length > 0) {
429
453
  return Promise.all(
430
454
  event.message.attachments.map(async (info) => ({
@@ -446,14 +470,14 @@ var toMessages2 = async (client, event) => {
446
470
  var clientStream = (client) => {
447
471
  const sub = client.messages.subscribe("message.received");
448
472
  return stream((emit, end) => {
449
- (async () => {
473
+ const pump = (async () => {
450
474
  try {
451
475
  for await (const event of sub) {
452
476
  if (event.message.isFromMe) {
453
477
  continue;
454
478
  }
455
479
  for (const message of await toMessages2(client, event)) {
456
- emit(message);
480
+ await emit(message);
457
481
  }
458
482
  }
459
483
  end();
@@ -461,7 +485,10 @@ var clientStream = (client) => {
461
485
  end(e);
462
486
  }
463
487
  })();
464
- return () => sub.close();
488
+ return async () => {
489
+ sub.close();
490
+ await pump;
491
+ };
465
492
  });
466
493
  };
467
494
  var sendVCardAttachment = (remote, name, vcf) => remote.attachments.upload({
@@ -502,6 +529,10 @@ var send2 = async (clients, spaceId, content) => {
502
529
  switch (content.type) {
503
530
  case "text":
504
531
  return toSendResult(await remote.messages.send(chat, content.text));
532
+ case "richlink":
533
+ return toSendResult(
534
+ await remote.messages.send(chat, content.url, { richLink: true })
535
+ );
505
536
  case "attachment": {
506
537
  const attachment = await remote.attachments.upload({
507
538
  data: await content.read(),
@@ -551,6 +582,13 @@ var replyToMessage = async (clients, spaceId, msgId, content) => {
551
582
  return toSendResult(
552
583
  await remote.messages.send(chat, content.text, { replyTo })
553
584
  );
585
+ case "richlink":
586
+ return toSendResult(
587
+ await remote.messages.send(chat, content.url, {
588
+ richLink: true,
589
+ replyTo
590
+ })
591
+ );
554
592
  case "attachment": {
555
593
  const attachment = await remote.attachments.upload({
556
594
  data: await content.read(),
@@ -1,4 +1,4 @@
1
- import { d as Platform, c as PlatformDef, P as ProviderMessage } from '../../types-B5tTx5hc.js';
1
+ import { d as Platform, c as PlatformDef, P as ProviderMessage } from '../../types-DLrsDzV-.js';
2
2
  import * as node_readline from 'node:readline';
3
3
  import z__default from 'zod';
4
4
  import 'hotscript';
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  UnsupportedError,
3
3
  definePlatform
4
- } from "../../chunk-6URE4AYH.js";
4
+ } from "../../chunk-4O6MQC5Z.js";
5
5
 
6
6
  // src/providers/terminal/index.ts
7
7
  import { createInterface } from "readline";
@@ -1,8 +1,8 @@
1
- import { M as ManagedStream } from '../../stream-DGy4geUK.js';
1
+ import { M as ManagedStream } from '../../stream-B55k7W8-.js';
2
2
  import { WhatsAppClient } from '@photon-ai/whatsapp-business';
3
3
  import * as z from 'zod';
4
4
  import z__default from 'zod';
5
- import { l as SchemaMessage, d as Platform, c as PlatformDef, P as ProviderMessage } from '../../types-B5tTx5hc.js';
5
+ import { l as SchemaMessage, d as Platform, c as PlatformDef, P as ProviderMessage } from '../../types-DLrsDzV-.js';
6
6
  import * as zod_v4_core from 'zod/v4/core';
7
7
  import 'hotscript';
8
8
 
@@ -5,12 +5,12 @@ import {
5
5
  cloud,
6
6
  mergeStreams,
7
7
  stream
8
- } from "../../chunk-GX3JCGSD.js";
8
+ } from "../../chunk-ZNUORCLB.js";
9
9
  import {
10
10
  UnsupportedError,
11
11
  asText,
12
12
  definePlatform
13
- } from "../../chunk-6URE4AYH.js";
13
+ } from "../../chunk-4O6MQC5Z.js";
14
14
 
15
15
  // src/providers/whatsapp-business/index.ts
16
16
  import { createClient as createClient2 } from "@photon-ai/whatsapp-business";
@@ -174,7 +174,7 @@ var pumpOnce = async (ctx) => {
174
174
  ctx.setActive(sub);
175
175
  try {
176
176
  for await (const event of sub) {
177
- ctx.emit(event);
177
+ await ctx.emit(event);
178
178
  }
179
179
  return true;
180
180
  } catch {
@@ -195,7 +195,7 @@ var resubscribableStream = (state, options) => {
195
195
  active = s;
196
196
  }
197
197
  };
198
- (async () => {
198
+ const pump = (async () => {
199
199
  while (!closed) {
200
200
  await pumpOnce(ctx);
201
201
  if (!closed) {
@@ -204,11 +204,12 @@ var resubscribableStream = (state, options) => {
204
204
  }
205
205
  end();
206
206
  })();
207
- return () => {
207
+ return async () => {
208
208
  closed = true;
209
209
  active?.close().catch(() => void 0);
210
210
  active = void 0;
211
211
  state.subscriptions.delete(subscription);
212
+ await pump;
212
213
  };
213
214
  });
214
215
  const subscription = {
@@ -518,11 +519,11 @@ var clientStream = (client) => {
518
519
  (e) => e.type === "message"
519
520
  );
520
521
  return stream((emit, end) => {
521
- (async () => {
522
+ const pump = (async () => {
522
523
  try {
523
524
  for await (const event of eventStream) {
524
525
  for (const m of toMessages(client, event.message)) {
525
- emit(m);
526
+ await emit(m);
526
527
  }
527
528
  }
528
529
  end();
@@ -530,7 +531,10 @@ var clientStream = (client) => {
530
531
  end(e);
531
532
  }
532
533
  })();
533
- return () => eventStream.close();
534
+ return async () => {
535
+ await eventStream.close();
536
+ await pump;
537
+ };
534
538
  });
535
539
  };
536
540
  var messages = (clients) => mergeStreams(clients.map(clientStream));
@@ -2,7 +2,7 @@ interface ManagedStream<T> extends AsyncIterable<T> {
2
2
  close(): Promise<void>;
3
3
  }
4
4
  type StreamCleanup = void | (() => void | Promise<void>);
5
- declare function stream<T>(setup: (emit: (value: T) => void, end: (error?: unknown) => void) => StreamCleanup | Promise<StreamCleanup>): ManagedStream<T>;
5
+ declare function stream<T>(setup: (emit: (value: T) => Promise<void>, end: (error?: unknown) => void) => StreamCleanup | Promise<StreamCleanup>): ManagedStream<T>;
6
6
  declare function mergeStreams<T>(streams: readonly ManagedStream<T>[]): ManagedStream<T>;
7
7
 
8
8
  export { type ManagedStream as M, mergeStreams as m, stream as s };
@@ -78,6 +78,16 @@ declare const contentSchema: z__default.ZodDiscriminatedUnion<[z__default.ZodObj
78
78
  size: z__default.ZodOptional<z__default.ZodNumber>;
79
79
  read: z__default.ZodFunction<z__default.ZodTuple<readonly [], null>, z__default.ZodPromise<z__default.ZodCustom<Buffer<ArrayBufferLike>, Buffer<ArrayBufferLike>>>>;
80
80
  stream: z__default.ZodFunction<z__default.ZodTuple<readonly [], null>, z__default.ZodPromise<z__default.ZodCustom<ReadableStream<unknown>, ReadableStream<unknown>>>>;
81
+ }, z__default.core.$strip>, z__default.ZodObject<{
82
+ type: z__default.ZodLiteral<"richlink">;
83
+ url: z__default.ZodURL;
84
+ title: z__default.ZodFunction<z__default.ZodTuple<readonly [], null>, z__default.ZodPromise<z__default.ZodOptional<z__default.ZodString>>>;
85
+ summary: z__default.ZodFunction<z__default.ZodTuple<readonly [], null>, z__default.ZodPromise<z__default.ZodOptional<z__default.ZodString>>>;
86
+ cover: z__default.ZodFunction<z__default.ZodTuple<readonly [], null>, z__default.ZodPromise<z__default.ZodOptional<z__default.ZodObject<{
87
+ mimeType: z__default.ZodOptional<z__default.ZodString>;
88
+ read: z__default.ZodFunction<z__default.ZodTuple<readonly [], null>, z__default.ZodPromise<z__default.ZodCustom<Buffer<ArrayBufferLike>, Buffer<ArrayBufferLike>>>>;
89
+ stream: z__default.ZodFunction<z__default.ZodTuple<readonly [], null>, z__default.ZodPromise<z__default.ZodCustom<ReadableStream<unknown>, ReadableStream<unknown>>>>;
90
+ }, z__default.core.$strip>>>>;
81
91
  }, z__default.core.$strip>], "type">;
82
92
  type Content = z__default.infer<typeof contentSchema>;
83
93
  interface ContentBuilder {
@@ -310,12 +320,13 @@ type SpaceShapeOf<Def extends AnyPlatformDef> = [SchemaSpaceOf<Def>] extends [
310
320
  never
311
321
  ] ? ResolvedSpaceOf<Def> : SchemaSpaceOf<Def>;
312
322
  type SpaceParamsInputOf<Def extends AnyPlatformDef> = InputSchema<Def["space"]["params"]>;
323
+ type SpaceUserLike<Def extends AnyPlatformDef> = PlatformUser<Def> | string;
313
324
  type SpaceArrayArgs<Def extends AnyPlatformDef> = [
314
325
  SpaceParamsInputOf<Def>
315
- ] extends [never] ? [users: PlatformUser<Def>[]] : [users: PlatformUser<Def>[]] | [users: PlatformUser<Def>[], params: SpaceParamsInputOf<Def>] | [params: SpaceParamsInputOf<Def>];
326
+ ] extends [never] ? [users: SpaceUserLike<Def>[]] : [users: SpaceUserLike<Def>[]] | [users: SpaceUserLike<Def>[], params: SpaceParamsInputOf<Def>] | [params: SpaceParamsInputOf<Def>];
316
327
  type SpaceVarargArgs<Def extends AnyPlatformDef> = [
317
328
  SpaceParamsInputOf<Def>
318
- ] extends [never] ? PlatformUser<Def>[] : PlatformUser<Def>[] | [...PlatformUser<Def>[], SpaceParamsInputOf<Def>];
329
+ ] extends [never] ? SpaceUserLike<Def>[] : SpaceUserLike<Def>[] | [...SpaceUserLike<Def>[], SpaceParamsInputOf<Def>];
319
330
  type SpaceArgs<Def extends AnyPlatformDef> = SpaceArrayArgs<Def> | SpaceVarargArgs<Def>;
320
331
  type PlatformSpace<Def extends AnyPlatformDef> = Omit<SpaceShapeOf<Def>, keyof Space> & Space;
321
332
  type PlatformMessage<Def extends AnyPlatformDef> = Omit<SchemaInfer<Def["message"]>, keyof Message> & Message<Def["name"], PlatformUser<Def>, PlatformSpace<Def>>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spectrum-ts",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -25,6 +25,7 @@
25
25
  "@repeaterjs/repeater": "^3.0.6",
26
26
  "better-grpc": "^0.3.2",
27
27
  "mime-types": "^3.0.1",
28
+ "open-graph-scraper": "^6.11.0",
28
29
  "type-fest": "^5.4.1",
29
30
  "vcf": "^2.1.2",
30
31
  "zod": "^4.2.1"