spectrum-ts 0.9.0 → 1.0.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.
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { C as ContentBuilder, U as User, M as Message, 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-B8g0pvfg.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-B8g0pvfg.js';
1
+ import { C as ContentBuilder, U as User, M as Message, 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-D5KhSXLy.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-D5KhSXLy.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-B55k7W8-.js';
@@ -122,21 +122,39 @@ declare function contact(input: string | ContactInput | vCard): ContentBuilder;
122
122
 
123
123
  declare function custom(raw: unknown): ContentBuilder;
124
124
 
125
+ /**
126
+ * A `group` bundles multiple messages into one logical unit (e.g. an album
127
+ * of images sent together). Each item is a full `Message` — addressable by
128
+ * id, reactable via `.react()`, replyable via `.reply()`.
129
+ *
130
+ * Groups do not nest, and reactions cannot be group members. Enforced by the
131
+ * `group()` builder; platforms may additionally reject unsupported item
132
+ * content types at send time.
133
+ */
134
+ declare const groupSchema: z__default.ZodObject<{
135
+ type: z__default.ZodLiteral<"group">;
136
+ items: z__default.ZodArray<z__default.ZodCustom<Message, Message>>;
137
+ }, z__default.core.$strip>;
138
+ type Group = z__default.infer<typeof groupSchema>;
139
+ declare function group(...items: [ContentInput, ContentInput, ...ContentInput[]]): ContentBuilder;
140
+
125
141
  declare const reactionSchema: z__default.ZodObject<{
126
142
  type: z__default.ZodLiteral<"reaction">;
127
143
  emoji: z__default.ZodString;
128
- target: z__default.ZodString;
144
+ target: z__default.ZodCustom<Message, Message>;
129
145
  }, z__default.core.$strip>;
130
146
  type Reaction = z__default.infer<typeof reactionSchema>;
131
147
  /**
132
- * Construct a `reaction` content value. Passing a `Message` extracts its id;
133
- * a string is treated as the target message id directly.
148
+ * Construct a `reaction` content value targeting the given message.
134
149
  *
135
150
  * `space.send(reaction(emoji, message))` is sugar for `message.react(emoji)`.
136
151
  * Reactions are fire-and-forget — the returned `OutboundMessage` will be
137
152
  * `undefined` because platforms do not surface a message id for reactions.
153
+ *
154
+ * To react to a message known only by id, resolve it first via
155
+ * `space.getMessage(id)`.
138
156
  */
139
- declare function reaction(emoji: string, target: Message | string): ContentBuilder;
157
+ declare function reaction(emoji: string, target: Message): ContentBuilder;
140
158
 
141
159
  declare const resolveContents: (items: readonly ContentInput[]) => Promise<Content[]>;
142
160
 
@@ -2122,14 +2140,32 @@ type SpectrumInstance<Providers extends PlatformProviderConfig[] = PlatformProvi
2122
2140
  edit(message: OutboundMessage, newContent: ContentInput): Promise<void>;
2123
2141
  responding<T>(space: Space, fn: () => T | Promise<T>): Promise<T>;
2124
2142
  };
2143
+ /**
2144
+ * Runtime behavior tweaks for a Spectrum instance.
2145
+ */
2146
+ interface SpectrumOptions {
2147
+ /**
2148
+ * When `true`, inbound `group` messages are never delivered whole. Instead,
2149
+ * each group item is yielded from `spectrum.messages` as its own
2150
+ * `[space, message]` tuple, in order. Items retain their individual
2151
+ * `id`, `sender`, `timestamp`, and `.react()` / `.reply()` methods.
2152
+ *
2153
+ * Does not affect outbound `group(...)` sends or `space.getMessage(id)`.
2154
+ *
2155
+ * @default false
2156
+ */
2157
+ flattenGroups?: boolean;
2158
+ }
2125
2159
  declare function Spectrum<const Providers extends PlatformProviderConfig[]>(options: {
2126
2160
  projectId: string;
2127
2161
  projectSecret: string;
2128
2162
  providers: [...Providers];
2163
+ options?: SpectrumOptions;
2129
2164
  } | {
2130
2165
  projectId?: never;
2131
2166
  projectSecret?: never;
2132
2167
  providers: [...Providers];
2168
+ options?: SpectrumOptions;
2133
2169
  }): Promise<SpectrumInstance<Providers>>;
2134
2170
 
2135
2171
  type SubscriptionStatus = "active" | "canceled" | "past_due";
@@ -2198,4 +2234,4 @@ declare class UnsupportedError extends Error {
2198
2234
  declare const fromVCard: (vcf: string) => ContactInput;
2199
2235
  declare const toVCard: (contact: Contact) => Promise<string>;
2200
2236
 
2201
- export { type CloudPlatform, type Contact, type ContactAddress, type ContactDetails, type ContactEmail, type ContactInput, type ContactName, type ContactOrg, type ContactPhone, Content, ContentBuilder, ContentInput, type DedicatedTokenData, Emoji, type EmojiKey, type ImessageInfoData, Message, Platform, PlatformDef, PlatformProviderConfig, type PlatformStatus, type PlatformsData, type Reaction, 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, reaction, resolveContents, richlink, text, toVCard, voice };
2237
+ export { type CloudPlatform, type Contact, type ContactAddress, type ContactDetails, type ContactEmail, type ContactInput, type ContactName, type ContactOrg, type ContactPhone, Content, ContentBuilder, ContentInput, type DedicatedTokenData, Emoji, type EmojiKey, type Group, type ImessageInfoData, Message, Platform, PlatformDef, PlatformProviderConfig, type PlatformStatus, type PlatformsData, type Reaction, 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, group, reaction, resolveContents, richlink, text, toVCard, voice };
package/dist/index.js CHANGED
@@ -1,136 +1,30 @@
1
1
  import {
2
+ group,
2
3
  richlink
3
- } from "./chunk-6ZOLTQDN.js";
4
+ } from "./chunk-LAGNM6I7.js";
5
+ import {
6
+ voice
7
+ } from "./chunk-7Q7KJKGL.js";
4
8
  import {
5
9
  SpectrumCloudError,
6
- attachment,
7
- bufferToStream,
8
10
  cloud,
9
- contact,
10
- custom,
11
- fromVCard,
12
11
  mergeStreams,
13
- reaction,
14
- readSchema,
15
- stream,
16
- streamSchema,
17
- toVCard
18
- } from "./chunk-CZIWNTXP.js";
12
+ stream
13
+ } from "./chunk-2Y5GBI6W.js";
19
14
  import {
20
15
  UnsupportedError,
21
- buildMessage,
16
+ attachment,
22
17
  buildSpace,
18
+ contact,
19
+ custom,
23
20
  definePlatform,
21
+ fromVCard,
22
+ reaction,
24
23
  resolveContents,
25
- text
26
- } from "./chunk-PLJI5FTO.js";
27
-
28
- // src/content/voice.ts
29
- import { createReadStream } from "fs";
30
- import { readFile, stat } from "fs/promises";
31
- import { basename } from "path";
32
- import { Readable } from "stream";
33
- import { lookup as lookupMimeType } from "mime-types";
34
- import z from "zod";
35
- var AUDIO_MIME_PATTERN = /^audio\//i;
36
- var audioMimeSchema = z.string().nonempty().regex(AUDIO_MIME_PATTERN, "voice content requires an audio/* MIME type");
37
- var voiceSchema = z.object({
38
- type: z.literal("voice"),
39
- name: z.string().nonempty().optional(),
40
- mimeType: audioMimeSchema,
41
- duration: z.number().nonnegative().optional(),
42
- size: z.number().int().nonnegative().optional(),
43
- read: readSchema,
44
- stream: streamSchema
45
- });
46
- var resolveVoiceName = (input, name) => {
47
- if (name) {
48
- return name;
49
- }
50
- if (typeof input === "string") {
51
- return basename(input);
52
- }
53
- return void 0;
54
- };
55
- var resolveVoiceMimeType = (name, mimeType) => {
56
- if (mimeType) {
57
- if (!AUDIO_MIME_PATTERN.test(mimeType)) {
58
- throw new Error(
59
- `voice content requires an audio/* MIME type, got "${mimeType}".`
60
- );
61
- }
62
- return mimeType;
63
- }
64
- if (name) {
65
- const resolved = lookupMimeType(name);
66
- if (resolved && AUDIO_MIME_PATTERN.test(resolved)) {
67
- return resolved;
68
- }
69
- if (resolved) {
70
- throw new Error(
71
- `Resolved non-audio MIME type "${resolved}" from name "${name}". Pass options.mimeType explicitly with an audio/* type.`
72
- );
73
- }
74
- }
75
- throw new Error(
76
- "Unable to resolve MIME type for voice content. Pass options.mimeType explicitly."
77
- );
78
- };
79
- var asVoice = (input) => {
80
- let cached;
81
- const read = () => {
82
- cached ??= input.read().catch((err) => {
83
- cached = void 0;
84
- throw err;
85
- });
86
- return cached;
87
- };
88
- const stream2 = input.stream ?? (async () => bufferToStream(await read()));
89
- return voiceSchema.parse({
90
- type: "voice",
91
- name: input.name,
92
- mimeType: input.mimeType,
93
- duration: input.duration,
94
- size: input.size,
95
- read,
96
- stream: stream2
97
- });
98
- };
99
- function voice(input, options) {
100
- return {
101
- build: async () => {
102
- const name = resolveVoiceName(input, options?.name);
103
- const mimeHint = typeof input === "string" ? basename(input) : name;
104
- const mimeType = resolveVoiceMimeType(mimeHint, options?.mimeType);
105
- if (typeof input === "string") {
106
- const stats = await stat(input);
107
- if (!stats.isFile()) {
108
- throw new Error(
109
- `voice content path "${input}" is not a regular file.`
110
- );
111
- }
112
- return asVoice({
113
- name,
114
- mimeType,
115
- duration: options?.duration,
116
- size: stats.size,
117
- read: () => readFile(input),
118
- stream: async () => Readable.toWeb(
119
- createReadStream(input)
120
- )
121
- });
122
- }
123
- return asVoice({
124
- name,
125
- mimeType,
126
- duration: options?.duration,
127
- size: input.byteLength,
128
- read: async () => input,
129
- stream: async () => bufferToStream(input)
130
- });
131
- }
132
- };
133
- }
24
+ text,
25
+ toVCard,
26
+ wrapProviderMessage
27
+ } from "./chunk-XMAI2AAN.js";
134
28
 
135
29
  // src/emoji/generated.ts
136
30
  var GeneratedEmoji = {
@@ -2062,29 +1956,33 @@ var aliases = {
2062
1956
  var Emoji = { ...GeneratedEmoji, ...aliases };
2063
1957
 
2064
1958
  // src/spectrum.ts
2065
- import z2 from "zod";
2066
- var providerMessageCoreKeys = /* @__PURE__ */ new Set([
2067
- "content",
2068
- "id",
2069
- "sender",
2070
- "space",
2071
- "timestamp"
2072
- ]);
2073
- var spectrumConfigSchema = z2.union([
2074
- z2.object({
2075
- projectId: z2.string().min(1),
2076
- projectSecret: z2.string().min(1),
2077
- providers: z2.array(z2.custom())
1959
+ import z from "zod";
1960
+ var spectrumOptionsSchema = z.object({
1961
+ flattenGroups: z.boolean().optional()
1962
+ }).optional();
1963
+ var spectrumConfigSchema = z.union([
1964
+ z.object({
1965
+ projectId: z.string().min(1),
1966
+ projectSecret: z.string().min(1),
1967
+ providers: z.array(z.custom()),
1968
+ options: spectrumOptionsSchema
2078
1969
  }),
2079
- z2.object({
2080
- projectId: z2.undefined().optional(),
2081
- projectSecret: z2.undefined().optional(),
2082
- providers: z2.array(z2.custom())
1970
+ z.object({
1971
+ projectId: z.undefined().optional(),
1972
+ projectSecret: z.undefined().optional(),
1973
+ providers: z.array(z.custom()),
1974
+ options: spectrumOptionsSchema
2083
1975
  })
2084
1976
  ]);
2085
1977
  async function Spectrum(options) {
2086
1978
  spectrumConfigSchema.parse(options);
2087
- const { projectId, projectSecret, providers } = options;
1979
+ const {
1980
+ projectId,
1981
+ projectSecret,
1982
+ providers,
1983
+ options: runtimeOptions
1984
+ } = options;
1985
+ const flattenGroups = runtimeOptions?.flattenGroups ?? false;
2088
1986
  const platformStates = /* @__PURE__ */ new Map();
2089
1987
  const customEventStreams = /* @__PURE__ */ new Map();
2090
1988
  let stopped = false;
@@ -2132,11 +2030,6 @@ async function Spectrum(options) {
2132
2030
  });
2133
2031
  const bindSend = async function* () {
2134
2032
  for await (const msg of raw) {
2135
- const extraEntries = Object.entries(msg).filter(
2136
- ([key]) => !providerMessageCoreKeys.has(key)
2137
- );
2138
- const extra = Object.fromEntries(extraEntries);
2139
- const parsedExtra = definition.message?.schema ? definition.message.schema.parse(extra) : {};
2140
2033
  const spaceRef = {
2141
2034
  ...msg.space,
2142
2035
  __platform: definition.name
@@ -2150,19 +2043,19 @@ async function Spectrum(options) {
2150
2043
  client,
2151
2044
  config
2152
2045
  });
2153
- const normalizedMessage = buildMessage({
2154
- id: msg.id,
2155
- content: msg.content,
2156
- sender: msg.sender,
2157
- timestamp: msg.timestamp ?? /* @__PURE__ */ new Date(),
2158
- extras: parsedExtra,
2159
- spaceRef,
2160
- space,
2161
- definition,
2046
+ const normalizedMessage = wrapProviderMessage(msg, {
2162
2047
  client,
2163
2048
  config,
2164
- direction: "inbound"
2049
+ definition,
2050
+ space,
2051
+ spaceRef
2165
2052
  });
2053
+ if (flattenGroups && normalizedMessage.content.type === "group") {
2054
+ for (const item of normalizedMessage.content.items) {
2055
+ yield [space, item];
2056
+ }
2057
+ continue;
2058
+ }
2166
2059
  yield [space, normalizedMessage];
2167
2060
  }
2168
2061
  };
@@ -2310,6 +2203,7 @@ export {
2310
2203
  custom,
2311
2204
  definePlatform,
2312
2205
  fromVCard,
2206
+ group,
2313
2207
  mergeStreams,
2314
2208
  reaction,
2315
2209
  resolveContents,
@@ -1,10 +1,10 @@
1
1
  import { M as ManagedStream } from '../../stream-B55k7W8-.js';
2
- import { AdvancedIMessage } from '@photon-ai/advanced-imessage';
3
- import { IMessageSDK } from '@photon-ai/imessage-kit';
2
+ import { l as SchemaMessage, d as Platform, c as PlatformDef, P as ProviderMessage } from '../../types-D5KhSXLy.js';
3
+ import * as zod_v4_core from 'zod/v4/core';
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-B8g0pvfg.js';
7
- import * as zod_v4_core from 'zod/v4/core';
6
+ import { AdvancedIMessage } from '@photon-ai/advanced-imessage';
7
+ import { IMessageSDK } from '@photon-ai/imessage-kit';
8
8
  import 'hotscript';
9
9
 
10
10
  type IMessageClient = IMessageSDK | AdvancedIMessage[];
@@ -16,7 +16,10 @@ declare const spaceSchema: z__default.ZodObject<{
16
16
  group: "group";
17
17
  }>;
18
18
  }, z__default.core.$strip>;
19
- type IMessageMessage = SchemaMessage<typeof userSchema, typeof spaceSchema>;
19
+ type IMessageMessage = SchemaMessage<typeof userSchema, typeof spaceSchema> & {
20
+ partIndex?: number;
21
+ parentId?: string;
22
+ };
20
23
 
21
24
  declare const imessage: Platform<PlatformDef<"iMessage", z.ZodUnion<readonly [z.ZodObject<{
22
25
  local: z.ZodLiteral<true>;
@@ -43,7 +46,10 @@ declare const imessage: Platform<PlatformDef<"iMessage", z.ZodUnion<readonly [z.
43
46
  } | {
44
47
  id: string;
45
48
  type: "group";
46
- }, undefined, ProviderMessage<{
49
+ }, z.ZodObject<{
50
+ partIndex: z.ZodOptional<z.ZodNumber>;
51
+ parentId: z.ZodOptional<z.ZodString>;
52
+ }, zod_v4_core.$strip>, ProviderMessage<{
47
53
  id: string;
48
54
  }, {
49
55
  id: string;
@@ -51,7 +57,10 @@ declare const imessage: Platform<PlatformDef<"iMessage", z.ZodUnion<readonly [z.
51
57
  } | {
52
58
  id: string;
53
59
  type: "group";
54
- }, Record<never, never>>, {
60
+ }, {
61
+ partIndex?: number | undefined;
62
+ parentId?: string | undefined;
63
+ }>, {
55
64
  messages: ({ client }: {
56
65
  client: IMessageClient;
57
66
  config: {