spectrum-ts 0.2.2 → 0.4.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.
@@ -1,308 +0,0 @@
1
- import type z from "zod";
2
- import type { ContentBuilder } from "../types/content";
3
- import type { Message } from "../types/message";
4
- import type { Space } from "../types/space";
5
- import type {
6
- AnyPlatformDef,
7
- Platform,
8
- PlatformDef,
9
- PlatformInstance,
10
- PlatformMessage,
11
- PlatformProviderConfig,
12
- PlatformSpace,
13
- PlatformUser,
14
- ProviderMessage,
15
- SpectrumLike,
16
- } from "./types";
17
-
18
- function createPlatformInstance<
19
- Def extends AnyPlatformDef,
20
- _Client,
21
- _ConfigSchema extends z.ZodType<object>,
22
- >(
23
- def: Def,
24
- runtime: { client: unknown; config: unknown }
25
- ): PlatformInstance<Def> {
26
- const isPlatformUser = (value: unknown): value is PlatformUser<Def> => {
27
- return (
28
- typeof value === "object" &&
29
- value !== null &&
30
- "__platform" in value &&
31
- (value as { __platform?: unknown }).__platform === def.name
32
- );
33
- };
34
-
35
- const normalizeSpaceArgs = (
36
- args: unknown[]
37
- ): { users: PlatformUser<Def>[]; params: unknown } => {
38
- if (args.length === 0) {
39
- return { users: [], params: undefined };
40
- }
41
-
42
- const [first, ...rest] = args;
43
- if (Array.isArray(first)) {
44
- return {
45
- users: first as PlatformUser<Def>[],
46
- params: rest[0],
47
- };
48
- }
49
-
50
- if (!isPlatformUser(first)) {
51
- return {
52
- users: [],
53
- params: first,
54
- };
55
- }
56
-
57
- const last = args.at(-1);
58
- if (last !== undefined && !isPlatformUser(last)) {
59
- return {
60
- users: args.slice(0, -1) as PlatformUser<Def>[],
61
- params: last,
62
- };
63
- }
64
-
65
- return {
66
- users: args as PlatformUser<Def>[],
67
- params: undefined,
68
- };
69
- };
70
-
71
- const base = {
72
- async user(userID: string) {
73
- const resolved = await def.user.resolve({
74
- input: { userID },
75
- client: runtime.client as _Client,
76
- config: runtime.config as z.infer<_ConfigSchema>,
77
- });
78
- return {
79
- ...resolved,
80
- __platform: def.name,
81
- } as PlatformUser<Def>;
82
- },
83
-
84
- async space(...args: unknown[]) {
85
- const { users, params } = normalizeSpaceArgs(args);
86
- let parsedParams = params;
87
- if (params !== undefined && def.space.params) {
88
- parsedParams = def.space.params.parse(params);
89
- }
90
- const resolved = await def.space.resolve({
91
- input: { users, params: parsedParams },
92
- client: runtime.client as _Client,
93
- config: runtime.config as z.infer<_ConfigSchema>,
94
- });
95
- const parsedSpace = def.space.schema
96
- ? def.space.schema.parse(resolved)
97
- : resolved;
98
- const spaceRef = {
99
- id: parsedSpace.id,
100
- __platform: def.name,
101
- };
102
- const typingCtx = {
103
- space: spaceRef,
104
- client: runtime.client as _Client,
105
- config: runtime.config as z.infer<_ConfigSchema>,
106
- };
107
- return {
108
- ...parsedSpace,
109
- ...spaceRef,
110
- send: async (...content: [ContentBuilder, ...ContentBuilder[]]) => {
111
- const built = await Promise.all(content.map((c) => c.build()));
112
- await def.actions.send({
113
- ...typingCtx,
114
- content: built,
115
- });
116
- },
117
- startTyping: async () => {
118
- await def.actions.startTyping?.(typingCtx);
119
- },
120
- stopTyping: async () => {
121
- await def.actions.stopTyping?.(typingCtx);
122
- },
123
- responding: async <T>(fn: () => T | Promise<T>): Promise<T> => {
124
- await def.actions.startTyping?.(typingCtx);
125
- try {
126
- return await fn();
127
- } finally {
128
- await def.actions.stopTyping?.(typingCtx).catch(() => {});
129
- }
130
- },
131
- } as PlatformSpace<Def>;
132
- },
133
- };
134
-
135
- // Add flat event properties for custom events (non-messages)
136
- const eventProperties: Record<string, AsyncIterable<unknown>> = {};
137
- for (const eventName of Object.keys(def.events)) {
138
- if (eventName === "messages") {
139
- continue;
140
- }
141
- const producer = def.events[eventName] as
142
- | ((ctx: { client: unknown; config: unknown }) => AsyncIterable<unknown>)
143
- | undefined;
144
- if (producer) {
145
- eventProperties[eventName] = producer({
146
- client: runtime.client,
147
- config: runtime.config,
148
- });
149
- }
150
- }
151
-
152
- return Object.assign(base, eventProperties) as PlatformInstance<Def>;
153
- }
154
-
155
- export function definePlatform<
156
- _Name extends string,
157
- _ConfigSchema extends z.ZodType<object>,
158
- _UserSchema extends z.ZodType<object> | undefined,
159
- _SpaceSchema extends z.ZodType<object> | undefined,
160
- _SpaceParamsSchema extends z.ZodType<object> | undefined,
161
- _Client,
162
- _ResolvedUser extends { id: string },
163
- _ResolvedSpace extends { id: string },
164
- _MessageSchema extends z.ZodType<object> | undefined = undefined,
165
- _MessageType extends ProviderMessage<
166
- _ResolvedUser,
167
- _ResolvedSpace,
168
- _MessageSchema extends z.ZodType<object>
169
- ? z.infer<_MessageSchema>
170
- : Record<never, never>
171
- > = ProviderMessage<
172
- _ResolvedUser,
173
- _ResolvedSpace,
174
- _MessageSchema extends z.ZodType<object>
175
- ? z.infer<_MessageSchema>
176
- : Record<never, never>
177
- >,
178
- _Events extends {
179
- messages: (ctx: {
180
- client: _Client;
181
- config: z.infer<_ConfigSchema>;
182
- }) => AsyncIterable<_MessageType>;
183
- } = {
184
- messages: (ctx: {
185
- client: _Client;
186
- config: z.infer<_ConfigSchema>;
187
- }) => AsyncIterable<_MessageType>;
188
- },
189
- _Static extends Record<string, unknown> = Record<never, never>,
190
- >(
191
- name: _Name,
192
- def: Omit<
193
- PlatformDef<
194
- _Name,
195
- _ConfigSchema,
196
- _UserSchema,
197
- _SpaceSchema,
198
- _SpaceParamsSchema,
199
- _Client,
200
- _ResolvedUser,
201
- _ResolvedSpace,
202
- _MessageSchema,
203
- _MessageType,
204
- _Events
205
- >,
206
- "name"
207
- > & { static?: _Static }
208
- ): Platform<
209
- PlatformDef<
210
- _Name,
211
- _ConfigSchema,
212
- _UserSchema,
213
- _SpaceSchema,
214
- _SpaceParamsSchema,
215
- _Client,
216
- _ResolvedUser,
217
- _ResolvedSpace,
218
- _MessageSchema,
219
- _MessageType,
220
- _Events
221
- >
222
- > &
223
- Readonly<_Static> {
224
- type Def = PlatformDef<
225
- _Name,
226
- _ConfigSchema,
227
- _UserSchema,
228
- _SpaceSchema,
229
- _SpaceParamsSchema,
230
- _Client,
231
- _ResolvedUser,
232
- _ResolvedSpace,
233
- _MessageSchema,
234
- _MessageType,
235
- _Events
236
- >;
237
-
238
- const fullDef = { name, ...def };
239
-
240
- const platformCache = new WeakMap<SpectrumLike, PlatformInstance<Def>>();
241
-
242
- const narrowSpectrum = (spectrum: SpectrumLike) => {
243
- const cached = platformCache.get(spectrum);
244
- if (cached) {
245
- return cached;
246
- }
247
-
248
- const runtime = spectrum.__internal.platforms.get(name);
249
- if (!runtime) {
250
- throw new Error(`Platform "${name}" is not registered`);
251
- }
252
-
253
- const instance = createPlatformInstance<Def, _Client, _ConfigSchema>(
254
- fullDef as Def & AnyPlatformDef,
255
- runtime
256
- );
257
- platformCache.set(spectrum, instance);
258
- return instance;
259
- };
260
-
261
- const narrowSpace = (input: Space) => {
262
- if (input.__platform !== name) {
263
- throw new Error(
264
- `Expected space from "${name}", got "${input.__platform}"`
265
- );
266
- }
267
- return input as PlatformSpace<Def>;
268
- };
269
-
270
- const narrowMessage = (input: Message) => {
271
- if (input.platform !== name) {
272
- throw new Error(
273
- `Expected message from "${name}", got "${input.platform}"`
274
- );
275
- }
276
- return input as PlatformMessage<Def>;
277
- };
278
-
279
- const narrower = ((input: SpectrumLike | Space | Message) => {
280
- if ("__providers" in input && "__internal" in input) {
281
- return narrowSpectrum(input as SpectrumLike);
282
- }
283
- if ("__platform" in input && "send" in input) {
284
- return narrowSpace(input as Space);
285
- }
286
- if ("platform" in input && "sender" in input && "space" in input) {
287
- return narrowMessage(input as Message);
288
- }
289
- throw new Error("Invalid input to platform narrowing function");
290
- }) as Platform<Def>;
291
-
292
- narrower.config = (config?: z.input<_ConfigSchema>) => {
293
- const resolvedConfig = config ?? {};
294
- return {
295
- __tag: "PlatformProviderConfig" as const,
296
- __def: undefined as unknown as Def,
297
- __name: name,
298
- config: resolvedConfig,
299
- __definition: fullDef as AnyPlatformDef,
300
- } satisfies PlatformProviderConfig<Def> as PlatformProviderConfig<Def>;
301
- };
302
-
303
- if (def.static) {
304
- Object.assign(narrower, def.static);
305
- }
306
-
307
- return narrower as Platform<Def> & Readonly<_Static>;
308
- }