spectrum-ts 4.2.0 → 5.0.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.
- package/README.md +29 -67
- package/dist/authoring.d.ts +1 -6
- package/dist/authoring.js +2 -36
- package/dist/elysia.d.ts +1 -94
- package/dist/elysia.js +2 -15
- package/dist/express.d.ts +1 -62
- package/dist/express.js +2 -19
- package/dist/hono.d.ts +1 -64
- package/dist/hono.js +2 -11
- package/dist/index.d.ts +1 -2851
- package/dist/index.js +2 -3763
- package/dist/manifest.json +5 -5
- package/dist/providers/imessage/index.d.ts +1 -222
- package/dist/providers/imessage/index.js +2 -25
- package/dist/providers/index.d.ts +6 -19
- package/dist/providers/index.js +6 -34
- package/dist/providers/slack/index.d.ts +1 -46
- package/dist/providers/slack/index.js +2 -11
- package/dist/providers/telegram/index.d.ts +1 -45
- package/dist/providers/telegram/index.js +2 -13
- package/dist/providers/terminal/index.d.ts +1 -119
- package/dist/providers/terminal/index.js +2 -13
- package/dist/providers/whatsapp-business/index.d.ts +1 -27
- package/dist/providers/whatsapp-business/index.js +2 -14
- package/package.json +11 -38
- package/dist/attachment-CnivEhr6.d.ts +0 -29
- package/dist/authoring-b9AhXgPI.d.ts +0 -304
- package/dist/chunk-2D27WW5B.js +0 -63
- package/dist/chunk-34FQGGD7.js +0 -34
- package/dist/chunk-3GEJYGZK.js +0 -84
- package/dist/chunk-5XEFJBN2.js +0 -197
- package/dist/chunk-6UZFVXQF.js +0 -374
- package/dist/chunk-A37PM5N2.js +0 -91
- package/dist/chunk-ARL2NOBO.js +0 -887
- package/dist/chunk-B52VPQO3.js +0 -1379
- package/dist/chunk-DMPDLSFU.js +0 -864
- package/dist/chunk-FAIFTUV2.js +0 -139
- package/dist/chunk-LZXPLXZF.js +0 -35
- package/dist/chunk-N6THJDZV.js +0 -929
- package/dist/chunk-NLMQ75LH.js +0 -2980
- package/dist/chunk-UXAKIXVM.js +0 -409
- package/dist/chunk-WXLQNANA.js +0 -539
- package/dist/chunk-ZR3TKZMT.js +0 -129
- package/dist/read-C4uvozGX.d.ts +0 -53
- package/dist/types-CyfLJXgu.d.ts +0 -1530
- package/dist/types-ZgFTj5hJ.d.ts +0 -87
package/dist/chunk-B52VPQO3.js
DELETED
|
@@ -1,1379 +0,0 @@
|
|
|
1
|
-
import { createRequire as __spectrumCreateRequire } from "node:module"; const require = __spectrumCreateRequire(import.meta.url);
|
|
2
|
-
import {
|
|
3
|
-
StreamConsumedError,
|
|
4
|
-
asText,
|
|
5
|
-
drainStreamText,
|
|
6
|
-
fetchUrlBytes,
|
|
7
|
-
reaction,
|
|
8
|
-
readSchema,
|
|
9
|
-
resolveContents,
|
|
10
|
-
streamTextBuilder
|
|
11
|
-
} from "./chunk-UXAKIXVM.js";
|
|
12
|
-
|
|
13
|
-
// src/content/avatar.ts
|
|
14
|
-
import z2 from "zod";
|
|
15
|
-
|
|
16
|
-
// src/utils/photo-content.ts
|
|
17
|
-
import { readFile } from "fs/promises";
|
|
18
|
-
import { basename } from "path";
|
|
19
|
-
import { lookup as lookupMimeType } from "mime-types";
|
|
20
|
-
import z from "zod";
|
|
21
|
-
var CLEAR_SENTINEL = "clear";
|
|
22
|
-
var photoActionSchema = z.discriminatedUnion("kind", [
|
|
23
|
-
z.object({
|
|
24
|
-
kind: z.literal("set"),
|
|
25
|
-
read: readSchema,
|
|
26
|
-
mimeType: z.string().nonempty()
|
|
27
|
-
}),
|
|
28
|
-
z.object({ kind: z.literal("clear") })
|
|
29
|
-
]);
|
|
30
|
-
var resolveMimeType = (input, mimeType, contentLabel) => {
|
|
31
|
-
if (mimeType) {
|
|
32
|
-
return mimeType;
|
|
33
|
-
}
|
|
34
|
-
if (input instanceof URL) {
|
|
35
|
-
const resolved = lookupMimeType(basename(input.pathname));
|
|
36
|
-
if (resolved) {
|
|
37
|
-
return resolved;
|
|
38
|
-
}
|
|
39
|
-
} else if (typeof input === "string") {
|
|
40
|
-
const resolved = lookupMimeType(basename(input));
|
|
41
|
-
if (resolved) {
|
|
42
|
-
return resolved;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
throw new Error(
|
|
46
|
-
`Unable to resolve MIME type for ${contentLabel}. Pass options.mimeType explicitly.`
|
|
47
|
-
);
|
|
48
|
-
};
|
|
49
|
-
var cachedRead = (read2) => {
|
|
50
|
-
let cached;
|
|
51
|
-
return () => {
|
|
52
|
-
cached ??= read2().catch((err) => {
|
|
53
|
-
cached = void 0;
|
|
54
|
-
throw err;
|
|
55
|
-
});
|
|
56
|
-
return cached;
|
|
57
|
-
};
|
|
58
|
-
};
|
|
59
|
-
var buildPhotoAction = (input, options, contentLabel) => {
|
|
60
|
-
if (input === CLEAR_SENTINEL) {
|
|
61
|
-
return { kind: "clear" };
|
|
62
|
-
}
|
|
63
|
-
const mimeType = resolveMimeType(input, options?.mimeType, contentLabel);
|
|
64
|
-
let read2;
|
|
65
|
-
if (input instanceof URL) {
|
|
66
|
-
read2 = cachedRead(async () => (await fetchUrlBytes(input)).data);
|
|
67
|
-
} else if (typeof input === "string") {
|
|
68
|
-
read2 = cachedRead(() => readFile(input));
|
|
69
|
-
} else {
|
|
70
|
-
const snapshot = Buffer.from(input);
|
|
71
|
-
read2 = cachedRead(async () => snapshot);
|
|
72
|
-
}
|
|
73
|
-
return { kind: "set", read: read2, mimeType };
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
// src/content/avatar.ts
|
|
77
|
-
var avatarSchema = z2.object({
|
|
78
|
-
type: z2.literal("avatar"),
|
|
79
|
-
action: photoActionSchema
|
|
80
|
-
});
|
|
81
|
-
function avatar(input, options) {
|
|
82
|
-
const action = buildPhotoAction(input, options, "avatar");
|
|
83
|
-
return {
|
|
84
|
-
build: async () => avatarSchema.parse({ type: "avatar", action })
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// src/content/edit.ts
|
|
89
|
-
import z3 from "zod";
|
|
90
|
-
var isMessage = (v) => typeof v === "object" && v !== null && "id" in v && "content" in v;
|
|
91
|
-
var isContent = (v) => typeof v === "object" && v !== null && "type" in v && typeof v.type === "string";
|
|
92
|
-
var editSchema = z3.object({
|
|
93
|
-
type: z3.literal("edit"),
|
|
94
|
-
content: z3.custom(isContent, {
|
|
95
|
-
message: "edit content must be a Content value"
|
|
96
|
-
}),
|
|
97
|
-
target: z3.custom(isMessage, {
|
|
98
|
-
message: "edit target must be a Message"
|
|
99
|
-
})
|
|
100
|
-
});
|
|
101
|
-
var asEdit = (input) => editSchema.parse({ type: "edit", ...input });
|
|
102
|
-
function edit(content, target) {
|
|
103
|
-
return {
|
|
104
|
-
build: async () => {
|
|
105
|
-
if (!target) {
|
|
106
|
-
throw new Error(
|
|
107
|
-
"edit() target is undefined \u2014 the targeted message was never sent (space.send resolves undefined when a platform skips unsupported content)"
|
|
108
|
-
);
|
|
109
|
-
}
|
|
110
|
-
if (target.direction !== "outbound") {
|
|
111
|
-
throw new Error(
|
|
112
|
-
`edit() target must be an outbound message (got direction "${target.direction}", message id "${target.id}")`
|
|
113
|
-
);
|
|
114
|
-
}
|
|
115
|
-
const [resolved] = await resolveContents([content]);
|
|
116
|
-
if (!resolved) {
|
|
117
|
-
throw new Error("edit() requires content");
|
|
118
|
-
}
|
|
119
|
-
if (resolved.type === "edit" || resolved.type === "reply" || resolved.type === "reaction" || resolved.type === "group" || resolved.type === "typing" || resolved.type === "rename" || resolved.type === "avatar" || resolved.type === "unsend" || resolved.type === "read") {
|
|
120
|
-
throw new Error(`edit() cannot wrap "${resolved.type}" content`);
|
|
121
|
-
}
|
|
122
|
-
return asEdit({ content: resolved, target });
|
|
123
|
-
}
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// src/content/markdown.ts
|
|
128
|
-
import z4 from "zod";
|
|
129
|
-
var markdownSchema = z4.object({
|
|
130
|
-
type: z4.literal("markdown"),
|
|
131
|
-
markdown: z4.string().nonempty()
|
|
132
|
-
});
|
|
133
|
-
var asMarkdown = (markdown2) => markdownSchema.parse({ type: "markdown", markdown: markdown2 });
|
|
134
|
-
function markdown(source, options) {
|
|
135
|
-
if (typeof source === "string") {
|
|
136
|
-
return { build: async () => asMarkdown(source) };
|
|
137
|
-
}
|
|
138
|
-
return streamTextBuilder("markdown", source, options);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// src/content/read.ts
|
|
142
|
-
import z5 from "zod";
|
|
143
|
-
var isMessage2 = (v) => typeof v === "object" && v !== null && "id" in v && "content" in v;
|
|
144
|
-
var readSchema2 = z5.object({
|
|
145
|
-
type: z5.literal("read"),
|
|
146
|
-
target: z5.custom(isMessage2, {
|
|
147
|
-
message: "read target must be a Message"
|
|
148
|
-
})
|
|
149
|
-
});
|
|
150
|
-
var asRead = (input) => readSchema2.parse({ type: "read", ...input });
|
|
151
|
-
function read(target) {
|
|
152
|
-
return {
|
|
153
|
-
build: async () => {
|
|
154
|
-
if (target.direction !== "inbound") {
|
|
155
|
-
throw new Error(
|
|
156
|
-
`read() target must be an inbound message (got direction "${target.direction}", message id "${target.id}")`
|
|
157
|
-
);
|
|
158
|
-
}
|
|
159
|
-
return asRead({ target });
|
|
160
|
-
}
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// src/content/rename.ts
|
|
165
|
-
import z6 from "zod";
|
|
166
|
-
var renameSchema = z6.object({
|
|
167
|
-
type: z6.literal("rename"),
|
|
168
|
-
displayName: z6.string().min(1, "rename() displayName must be non-empty")
|
|
169
|
-
});
|
|
170
|
-
function rename(displayName) {
|
|
171
|
-
return {
|
|
172
|
-
build: async () => renameSchema.parse({ type: "rename", displayName })
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// src/content/reply.ts
|
|
177
|
-
import z7 from "zod";
|
|
178
|
-
var isMessage3 = (v) => typeof v === "object" && v !== null && "id" in v && "content" in v;
|
|
179
|
-
var isContent2 = (v) => typeof v === "object" && v !== null && "type" in v && typeof v.type === "string";
|
|
180
|
-
var replySchema = z7.object({
|
|
181
|
-
type: z7.literal("reply"),
|
|
182
|
-
content: z7.custom(isContent2, {
|
|
183
|
-
message: "reply content must be a Content value"
|
|
184
|
-
}),
|
|
185
|
-
target: z7.custom(isMessage3, {
|
|
186
|
-
message: "reply target must be a Message"
|
|
187
|
-
})
|
|
188
|
-
});
|
|
189
|
-
var asReply = (input) => replySchema.parse({ type: "reply", ...input });
|
|
190
|
-
function reply(content, target) {
|
|
191
|
-
return {
|
|
192
|
-
build: async () => {
|
|
193
|
-
if (!target) {
|
|
194
|
-
throw new Error(
|
|
195
|
-
"reply() target is undefined \u2014 the targeted message was never sent (space.send resolves undefined when a platform skips unsupported content)"
|
|
196
|
-
);
|
|
197
|
-
}
|
|
198
|
-
const [resolved] = await resolveContents([content]);
|
|
199
|
-
if (!resolved) {
|
|
200
|
-
throw new Error("reply() requires content");
|
|
201
|
-
}
|
|
202
|
-
if (resolved.type === "reply" || resolved.type === "edit" || resolved.type === "reaction" || resolved.type === "group" || resolved.type === "typing" || resolved.type === "rename" || resolved.type === "avatar" || resolved.type === "unsend" || resolved.type === "read") {
|
|
203
|
-
throw new Error(`reply() cannot wrap "${resolved.type}" content`);
|
|
204
|
-
}
|
|
205
|
-
return asReply({ content: resolved, target });
|
|
206
|
-
}
|
|
207
|
-
};
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
// src/content/typing.ts
|
|
211
|
-
import z8 from "zod";
|
|
212
|
-
var typingSchema = z8.object({
|
|
213
|
-
type: z8.literal("typing"),
|
|
214
|
-
state: z8.enum(["start", "stop"])
|
|
215
|
-
});
|
|
216
|
-
function typing(state = "start") {
|
|
217
|
-
return {
|
|
218
|
-
build: async () => typingSchema.parse({ type: "typing", state })
|
|
219
|
-
};
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
// src/content/unsend.ts
|
|
223
|
-
import z9 from "zod";
|
|
224
|
-
var isMessage4 = (v) => typeof v === "object" && v !== null && "id" in v && "content" in v;
|
|
225
|
-
var unsendSchema = z9.object({
|
|
226
|
-
type: z9.literal("unsend"),
|
|
227
|
-
target: z9.custom(isMessage4, {
|
|
228
|
-
message: "unsend target must be a Message"
|
|
229
|
-
})
|
|
230
|
-
});
|
|
231
|
-
var asUnsend = (input) => unsendSchema.parse({ type: "unsend", ...input });
|
|
232
|
-
function unsend(target) {
|
|
233
|
-
return {
|
|
234
|
-
build: async () => {
|
|
235
|
-
if (!target) {
|
|
236
|
-
throw new Error(
|
|
237
|
-
"unsend() target is undefined \u2014 the targeted message was never sent (space.send resolves undefined when a platform skips unsupported content)"
|
|
238
|
-
);
|
|
239
|
-
}
|
|
240
|
-
if (target.direction !== "outbound") {
|
|
241
|
-
throw new Error(
|
|
242
|
-
`unsend() target must be an outbound message (got direction "${target.direction}", message id "${target.id}")`
|
|
243
|
-
);
|
|
244
|
-
}
|
|
245
|
-
return asUnsend({ target });
|
|
246
|
-
}
|
|
247
|
-
};
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// src/utils/errors.ts
|
|
251
|
-
var composeMessage = (opts) => {
|
|
252
|
-
const platform = opts.platform ?? "platform";
|
|
253
|
-
const subject = opts.kind === "content" ? `content type "${opts.contentType ?? "unknown"}"` : `action "${opts.action ?? "unknown"}"`;
|
|
254
|
-
const detail = opts.detail ? `: ${opts.detail}` : "";
|
|
255
|
-
return `${platform} does not support ${subject}${detail}`;
|
|
256
|
-
};
|
|
257
|
-
var UnsupportedError = class _UnsupportedError extends Error {
|
|
258
|
-
kind;
|
|
259
|
-
platform;
|
|
260
|
-
contentType;
|
|
261
|
-
action;
|
|
262
|
-
detail;
|
|
263
|
-
constructor(opts) {
|
|
264
|
-
super(composeMessage(opts));
|
|
265
|
-
this.name = "UnsupportedError";
|
|
266
|
-
this.kind = opts.kind;
|
|
267
|
-
this.platform = opts.platform;
|
|
268
|
-
this.contentType = opts.contentType;
|
|
269
|
-
this.action = opts.action;
|
|
270
|
-
this.detail = opts.detail;
|
|
271
|
-
}
|
|
272
|
-
static content(contentType, platform, detail) {
|
|
273
|
-
return new _UnsupportedError({
|
|
274
|
-
kind: "content",
|
|
275
|
-
contentType,
|
|
276
|
-
platform,
|
|
277
|
-
detail
|
|
278
|
-
});
|
|
279
|
-
}
|
|
280
|
-
static action(action, platform, detail) {
|
|
281
|
-
return new _UnsupportedError({ kind: "action", action, platform, detail });
|
|
282
|
-
}
|
|
283
|
-
withPlatform(platform) {
|
|
284
|
-
if (this.platform) {
|
|
285
|
-
return this;
|
|
286
|
-
}
|
|
287
|
-
return new _UnsupportedError({
|
|
288
|
-
kind: this.kind,
|
|
289
|
-
platform,
|
|
290
|
-
contentType: this.contentType,
|
|
291
|
-
action: this.action,
|
|
292
|
-
detail: this.detail
|
|
293
|
-
});
|
|
294
|
-
}
|
|
295
|
-
};
|
|
296
|
-
|
|
297
|
-
// src/platform/define.ts
|
|
298
|
-
import { createLogger as createLogger2, withSpan as withSpan2 } from "@photon-ai/otel";
|
|
299
|
-
|
|
300
|
-
// src/utils/identifier.ts
|
|
301
|
-
import { sanitizeEmail, sanitizePhone } from "@photon-ai/otel";
|
|
302
|
-
var PHONE_LIKE = /^\+?[\d\s()\-.]{7,}$/;
|
|
303
|
-
var EMAIL_LIKE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
304
|
-
function classifyIdentifier(s) {
|
|
305
|
-
if (EMAIL_LIKE.test(s)) {
|
|
306
|
-
return { kind: "email", identifier: sanitizeEmail(s) };
|
|
307
|
-
}
|
|
308
|
-
if (PHONE_LIKE.test(s) && s.replace(/\D/g, "").length >= 7) {
|
|
309
|
-
return { kind: "phone", identifier: sanitizePhone(s) };
|
|
310
|
-
}
|
|
311
|
-
return { kind: "unknown", identifier: s };
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
// src/platform/build.ts
|
|
315
|
-
import { createLogger, withSpan } from "@photon-ai/otel";
|
|
316
|
-
|
|
317
|
-
// src/utils/markdown.ts
|
|
318
|
-
import { Marked } from "marked";
|
|
319
|
-
var markdownLexer = new Marked();
|
|
320
|
-
var BULLET = "\u2022 ";
|
|
321
|
-
var HR_LINE = "\u2014\u2014\u2014";
|
|
322
|
-
var NESTED_LIST_INDENT = " ";
|
|
323
|
-
var BLOCK_SEPARATOR = "\n\n";
|
|
324
|
-
var TABLE_CELL_SEPARATOR = " | ";
|
|
325
|
-
var DEFAULT_LIST_START = 1;
|
|
326
|
-
var asMarkedToken = (token) => token;
|
|
327
|
-
var checkboxPrefix = (item) => {
|
|
328
|
-
if (!item.task) {
|
|
329
|
-
return "";
|
|
330
|
-
}
|
|
331
|
-
return item.checked ? "[x] " : "[ ] ";
|
|
332
|
-
};
|
|
333
|
-
var listMarker = (list, index) => {
|
|
334
|
-
if (!list.ordered) {
|
|
335
|
-
return BULLET;
|
|
336
|
-
}
|
|
337
|
-
const start = list.start === "" ? DEFAULT_LIST_START : list.start;
|
|
338
|
-
return `${start + index}. `;
|
|
339
|
-
};
|
|
340
|
-
var renderLink = (token) => {
|
|
341
|
-
if (token.text === token.href) {
|
|
342
|
-
return token.href;
|
|
343
|
-
}
|
|
344
|
-
return `${renderInlineTokens(token.tokens)} (${token.href})`;
|
|
345
|
-
};
|
|
346
|
-
var renderImage = (token) => token.text ? `${token.text} (${token.href})` : token.href;
|
|
347
|
-
var renderInlineToken = (token) => {
|
|
348
|
-
switch (token.type) {
|
|
349
|
-
case "strong":
|
|
350
|
-
case "em":
|
|
351
|
-
case "del":
|
|
352
|
-
return renderInlineTokens(token.tokens);
|
|
353
|
-
case "codespan":
|
|
354
|
-
return token.text;
|
|
355
|
-
case "br":
|
|
356
|
-
return "\n";
|
|
357
|
-
case "link":
|
|
358
|
-
return renderLink(token);
|
|
359
|
-
case "image":
|
|
360
|
-
return renderImage(token);
|
|
361
|
-
case "escape":
|
|
362
|
-
return token.text;
|
|
363
|
-
case "text":
|
|
364
|
-
return token.tokens ? renderInlineTokens(token.tokens) : token.text;
|
|
365
|
-
// Raw HTML in markdown source stays literal — plain text has no markup.
|
|
366
|
-
case "html":
|
|
367
|
-
return token.text;
|
|
368
|
-
// Task-item checkboxes are rendered from `ListItem.task`/`checked`.
|
|
369
|
-
case "checkbox":
|
|
370
|
-
return "";
|
|
371
|
-
default:
|
|
372
|
-
return "raw" in token ? String(token.raw) : "";
|
|
373
|
-
}
|
|
374
|
-
};
|
|
375
|
-
var renderInlineTokens = (tokens) => {
|
|
376
|
-
let out = "";
|
|
377
|
-
for (const token of tokens) {
|
|
378
|
-
out += renderInlineToken(asMarkedToken(token));
|
|
379
|
-
}
|
|
380
|
-
return out;
|
|
381
|
-
};
|
|
382
|
-
var renderBlockquote = (quote) => renderBlockTokens(quote.tokens).split("\n").map((line) => line ? `> ${line}` : ">").join("\n");
|
|
383
|
-
var renderList = (list) => {
|
|
384
|
-
const lines = [];
|
|
385
|
-
for (const [index, item] of list.items.entries()) {
|
|
386
|
-
const prefix = `${listMarker(list, index)}${checkboxPrefix(item)}`;
|
|
387
|
-
const blocks = [];
|
|
388
|
-
for (const token of item.tokens) {
|
|
389
|
-
const rendered = renderBlockToken(asMarkedToken(token));
|
|
390
|
-
if (rendered) {
|
|
391
|
-
blocks.push(rendered);
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
const [first = "", ...rest] = blocks.join("\n").split("\n");
|
|
395
|
-
lines.push(`${prefix}${first}`);
|
|
396
|
-
for (const line of rest) {
|
|
397
|
-
lines.push(`${NESTED_LIST_INDENT}${line}`);
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
return lines.join("\n");
|
|
401
|
-
};
|
|
402
|
-
var renderTable = (table) => {
|
|
403
|
-
const renderRow = (cells) => cells.map((cell) => renderInlineTokens(cell.tokens)).join(TABLE_CELL_SEPARATOR);
|
|
404
|
-
const lines = [renderRow(table.header)];
|
|
405
|
-
for (const row of table.rows) {
|
|
406
|
-
lines.push(renderRow(row));
|
|
407
|
-
}
|
|
408
|
-
return lines.join("\n");
|
|
409
|
-
};
|
|
410
|
-
var renderBlockToken = (token) => {
|
|
411
|
-
switch (token.type) {
|
|
412
|
-
case "heading":
|
|
413
|
-
case "paragraph":
|
|
414
|
-
return renderInlineTokens(token.tokens);
|
|
415
|
-
case "code":
|
|
416
|
-
return token.text;
|
|
417
|
-
case "blockquote":
|
|
418
|
-
return renderBlockquote(token);
|
|
419
|
-
case "list":
|
|
420
|
-
return renderList(token);
|
|
421
|
-
case "table":
|
|
422
|
-
return renderTable(token);
|
|
423
|
-
case "hr":
|
|
424
|
-
return HR_LINE;
|
|
425
|
-
case "space":
|
|
426
|
-
case "def":
|
|
427
|
-
return "";
|
|
428
|
-
default:
|
|
429
|
-
return renderInlineToken(token);
|
|
430
|
-
}
|
|
431
|
-
};
|
|
432
|
-
var renderBlockTokens = (tokens) => {
|
|
433
|
-
const blocks = [];
|
|
434
|
-
for (const token of tokens) {
|
|
435
|
-
const rendered = renderBlockToken(asMarkedToken(token));
|
|
436
|
-
if (rendered) {
|
|
437
|
-
blocks.push(rendered);
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
return blocks.join(BLOCK_SEPARATOR);
|
|
441
|
-
};
|
|
442
|
-
var markdownToPlainText = (markdown2) => renderBlockTokens(markdownLexer.lexer(markdown2)).trim();
|
|
443
|
-
|
|
444
|
-
// src/utils/telemetry.ts
|
|
445
|
-
var targetId = (target) => {
|
|
446
|
-
const id = target?.id;
|
|
447
|
-
return typeof id === "string" ? id : void 0;
|
|
448
|
-
};
|
|
449
|
-
var targetType = (target) => {
|
|
450
|
-
const type = target?.content?.type;
|
|
451
|
-
return typeof type === "string" ? type : void 0;
|
|
452
|
-
};
|
|
453
|
-
var replyOrEditAttrs = (content) => {
|
|
454
|
-
const target = content.target;
|
|
455
|
-
const inner = content.content;
|
|
456
|
-
const innerType = inner?.type;
|
|
457
|
-
return {
|
|
458
|
-
"spectrum.message.content.target.id": targetId(target),
|
|
459
|
-
"spectrum.message.content.target.type": targetType(target),
|
|
460
|
-
"spectrum.message.content.inner.type": typeof innerType === "string" ? innerType : void 0
|
|
461
|
-
};
|
|
462
|
-
};
|
|
463
|
-
var unsendAttrs = (content) => {
|
|
464
|
-
const target = content.target;
|
|
465
|
-
return {
|
|
466
|
-
"spectrum.message.content.target.id": targetId(target),
|
|
467
|
-
"spectrum.message.content.target.type": targetType(target)
|
|
468
|
-
};
|
|
469
|
-
};
|
|
470
|
-
var reactionAttrs = (content) => {
|
|
471
|
-
const target = content.target;
|
|
472
|
-
const emoji = content.emoji;
|
|
473
|
-
return {
|
|
474
|
-
"spectrum.message.content.target.id": targetId(target),
|
|
475
|
-
"spectrum.message.content.reaction.emoji": typeof emoji === "string" ? emoji : void 0
|
|
476
|
-
};
|
|
477
|
-
};
|
|
478
|
-
var groupAttrs = (content) => {
|
|
479
|
-
const items = content.items;
|
|
480
|
-
if (!Array.isArray(items)) {
|
|
481
|
-
return {};
|
|
482
|
-
}
|
|
483
|
-
const types = items.map((item) => {
|
|
484
|
-
const itemType = item?.content?.type;
|
|
485
|
-
return typeof itemType === "string" ? itemType : void 0;
|
|
486
|
-
}).filter((t) => t !== void 0);
|
|
487
|
-
return {
|
|
488
|
-
"spectrum.message.content.items.count": items.length,
|
|
489
|
-
"spectrum.message.content.items.types": types.length > 0 ? types.join(",") : void 0
|
|
490
|
-
};
|
|
491
|
-
};
|
|
492
|
-
var typingAttrs = (content) => {
|
|
493
|
-
const state = content.state;
|
|
494
|
-
return {
|
|
495
|
-
"spectrum.message.content.typing.state": typeof state === "string" ? state : void 0
|
|
496
|
-
};
|
|
497
|
-
};
|
|
498
|
-
var attachmentAttrs = (content) => {
|
|
499
|
-
const mime = content.mimeType;
|
|
500
|
-
const size = content.size;
|
|
501
|
-
return {
|
|
502
|
-
"spectrum.message.content.attachment.mime": typeof mime === "string" ? mime : void 0,
|
|
503
|
-
"spectrum.message.content.attachment.size": typeof size === "number" ? size : void 0
|
|
504
|
-
};
|
|
505
|
-
};
|
|
506
|
-
var voiceAttrs = (content) => {
|
|
507
|
-
const mime = content.mimeType;
|
|
508
|
-
const duration = content.duration;
|
|
509
|
-
const size = content.size;
|
|
510
|
-
return {
|
|
511
|
-
"spectrum.message.content.voice.mime": typeof mime === "string" ? mime : void 0,
|
|
512
|
-
"spectrum.message.content.voice.duration": typeof duration === "number" ? duration : void 0,
|
|
513
|
-
"spectrum.message.content.voice.size": typeof size === "number" ? size : void 0
|
|
514
|
-
};
|
|
515
|
-
};
|
|
516
|
-
var CONTENT_ATTR_HANDLERS = {
|
|
517
|
-
reply: replyOrEditAttrs,
|
|
518
|
-
edit: replyOrEditAttrs,
|
|
519
|
-
unsend: unsendAttrs,
|
|
520
|
-
reaction: reactionAttrs,
|
|
521
|
-
group: groupAttrs,
|
|
522
|
-
typing: typingAttrs,
|
|
523
|
-
attachment: attachmentAttrs,
|
|
524
|
-
voice: voiceAttrs
|
|
525
|
-
};
|
|
526
|
-
function contentAttrs(content) {
|
|
527
|
-
const type = content?.type;
|
|
528
|
-
if (!(content && type)) {
|
|
529
|
-
return { "spectrum.message.content.type": void 0 };
|
|
530
|
-
}
|
|
531
|
-
const handler = CONTENT_ATTR_HANDLERS[type];
|
|
532
|
-
return {
|
|
533
|
-
"spectrum.message.content.type": type,
|
|
534
|
-
...handler ? handler(content) : {}
|
|
535
|
-
};
|
|
536
|
-
}
|
|
537
|
-
function senderAttrs(sender) {
|
|
538
|
-
const id = sender?.id;
|
|
539
|
-
if (typeof id !== "string" || id.length === 0) {
|
|
540
|
-
return {};
|
|
541
|
-
}
|
|
542
|
-
const { kind, identifier } = classifyIdentifier(id);
|
|
543
|
-
return {
|
|
544
|
-
"spectrum.message.sender.id": identifier,
|
|
545
|
-
"spectrum.message.sender.kind": kind
|
|
546
|
-
};
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
// src/platform/build.ts
|
|
550
|
-
var platformLog = createLogger("spectrum.platform");
|
|
551
|
-
var ANSI_YELLOW = "\x1B[33m";
|
|
552
|
-
var ANSI_RESET = "\x1B[0m";
|
|
553
|
-
var supportsAnsiColor = () => {
|
|
554
|
-
if (typeof process === "undefined") {
|
|
555
|
-
return false;
|
|
556
|
-
}
|
|
557
|
-
if (process.env.NO_COLOR) {
|
|
558
|
-
return false;
|
|
559
|
-
}
|
|
560
|
-
const force = process.env.FORCE_COLOR;
|
|
561
|
-
if (force !== void 0) {
|
|
562
|
-
return force !== "" && force !== "0" && force !== "false";
|
|
563
|
-
}
|
|
564
|
-
return Boolean(process.stderr?.isTTY);
|
|
565
|
-
};
|
|
566
|
-
var FIRE_AND_FORGET_TYPES = /* @__PURE__ */ new Set([
|
|
567
|
-
"typing",
|
|
568
|
-
"edit",
|
|
569
|
-
"rename",
|
|
570
|
-
"avatar",
|
|
571
|
-
"unsend",
|
|
572
|
-
"read"
|
|
573
|
-
]);
|
|
574
|
-
var isFireAndForget = (item) => FIRE_AND_FORGET_TYPES.has(item.type) || item.__fireAndForget === true;
|
|
575
|
-
var RESERVED_SPACE_KEYS = /* @__PURE__ */ new Set([
|
|
576
|
-
"__platform",
|
|
577
|
-
"id",
|
|
578
|
-
"send",
|
|
579
|
-
"edit",
|
|
580
|
-
"unsend",
|
|
581
|
-
"read",
|
|
582
|
-
"getMessage",
|
|
583
|
-
"rename",
|
|
584
|
-
"avatar",
|
|
585
|
-
"startTyping",
|
|
586
|
-
"stopTyping",
|
|
587
|
-
"responding"
|
|
588
|
-
]);
|
|
589
|
-
var PLATFORM_WISE_ACTION_KEYS = /* @__PURE__ */ new Set(["getMessage"]);
|
|
590
|
-
var RESERVED_MESSAGE_KEYS = /* @__PURE__ */ new Set([
|
|
591
|
-
"content",
|
|
592
|
-
"direction",
|
|
593
|
-
"edit",
|
|
594
|
-
"id",
|
|
595
|
-
"platform",
|
|
596
|
-
"react",
|
|
597
|
-
"read",
|
|
598
|
-
"reply",
|
|
599
|
-
"sender",
|
|
600
|
-
"space",
|
|
601
|
-
"timestamp",
|
|
602
|
-
"unsend"
|
|
603
|
-
]);
|
|
604
|
-
var scopeLabel = (scope) => {
|
|
605
|
-
if (scope === "space") {
|
|
606
|
-
return "Space";
|
|
607
|
-
}
|
|
608
|
-
if (scope === "message") {
|
|
609
|
-
return "Message";
|
|
610
|
-
}
|
|
611
|
-
return "PlatformInstance";
|
|
612
|
-
};
|
|
613
|
-
var warnReservedAction = (scope, name, platform) => {
|
|
614
|
-
const body = `[spectrum-ts] ${platform} declared ${scope} action "${name}" which collides with a reserved ${scopeLabel(scope)} key; skipping.`;
|
|
615
|
-
console.warn(
|
|
616
|
-
supportsAnsiColor() ? `${ANSI_YELLOW}${body}${ANSI_RESET}` : body
|
|
617
|
-
);
|
|
618
|
-
};
|
|
619
|
-
var warnUnsupported = (err, fallbackPlatform) => {
|
|
620
|
-
const platform = err.platform ?? fallbackPlatform;
|
|
621
|
-
const subject = err.kind === "content" ? `content type "${err.contentType ?? "unknown"}"` : `action "${err.action ?? "unknown"}"`;
|
|
622
|
-
const detail = err.detail ? `: ${err.detail}` : "";
|
|
623
|
-
platformLog.warn(
|
|
624
|
-
`${platform} does not support ${subject}${detail}; skipping.`,
|
|
625
|
-
{
|
|
626
|
-
"spectrum.provider": platform,
|
|
627
|
-
"spectrum.unsupported.kind": err.kind,
|
|
628
|
-
"spectrum.unsupported.content_type": err.contentType,
|
|
629
|
-
"spectrum.unsupported.action": err.action
|
|
630
|
-
}
|
|
631
|
-
);
|
|
632
|
-
};
|
|
633
|
-
var contentPlatform = (content) => {
|
|
634
|
-
const platform = content.__platform;
|
|
635
|
-
return typeof platform === "string" ? platform : void 0;
|
|
636
|
-
};
|
|
637
|
-
var findUnsupportedPlatformContent = (content, platform) => {
|
|
638
|
-
const scopedPlatform = contentPlatform(content);
|
|
639
|
-
if (scopedPlatform && scopedPlatform !== platform) {
|
|
640
|
-
return scopedPlatform;
|
|
641
|
-
}
|
|
642
|
-
if (content.type === "reply" || content.type === "edit") {
|
|
643
|
-
return findUnsupportedPlatformContent(content.content, platform);
|
|
644
|
-
}
|
|
645
|
-
if (content.type !== "group") {
|
|
646
|
-
return;
|
|
647
|
-
}
|
|
648
|
-
for (const item of content.items) {
|
|
649
|
-
const nested = item.content;
|
|
650
|
-
if (typeof nested !== "object" || nested === null || !("type" in nested)) {
|
|
651
|
-
continue;
|
|
652
|
-
}
|
|
653
|
-
const unsupported = findUnsupportedPlatformContent(
|
|
654
|
-
nested,
|
|
655
|
-
platform
|
|
656
|
-
);
|
|
657
|
-
if (unsupported) {
|
|
658
|
-
return unsupported;
|
|
659
|
-
}
|
|
660
|
-
}
|
|
661
|
-
};
|
|
662
|
-
var unsupportedPlatformContentError = (content, platform) => {
|
|
663
|
-
const requiredPlatform = findUnsupportedPlatformContent(content, platform);
|
|
664
|
-
if (!requiredPlatform) {
|
|
665
|
-
return;
|
|
666
|
-
}
|
|
667
|
-
return UnsupportedError.content(
|
|
668
|
-
content.type,
|
|
669
|
-
platform,
|
|
670
|
-
`requires ${requiredPlatform}`
|
|
671
|
-
);
|
|
672
|
-
};
|
|
673
|
-
var findStreamText = (item) => {
|
|
674
|
-
if (item.type === "streamText") {
|
|
675
|
-
return item;
|
|
676
|
-
}
|
|
677
|
-
if ((item.type === "reply" || item.type === "edit") && item.content.type === "streamText") {
|
|
678
|
-
return item.content;
|
|
679
|
-
}
|
|
680
|
-
return;
|
|
681
|
-
};
|
|
682
|
-
var replaceStreamText = (item, source, full) => {
|
|
683
|
-
const inner = source.format === "markdown" ? asMarkdown(full) : asText(full);
|
|
684
|
-
if (item.type === "reply" || item.type === "edit") {
|
|
685
|
-
return { ...item, content: inner };
|
|
686
|
-
}
|
|
687
|
-
return inner;
|
|
688
|
-
};
|
|
689
|
-
var downgradeMarkdown = (md) => {
|
|
690
|
-
const plain = markdownToPlainText(md.markdown);
|
|
691
|
-
return plain ? asText(plain) : void 0;
|
|
692
|
-
};
|
|
693
|
-
var replaceMarkdown = (item) => {
|
|
694
|
-
if (item.type === "markdown") {
|
|
695
|
-
return downgradeMarkdown(item) ?? item;
|
|
696
|
-
}
|
|
697
|
-
if ((item.type === "reply" || item.type === "edit") && item.content.type === "markdown") {
|
|
698
|
-
const downgraded = downgradeMarkdown(item.content);
|
|
699
|
-
return downgraded ? { ...item, content: downgraded } : item;
|
|
700
|
-
}
|
|
701
|
-
if (item.type === "group") {
|
|
702
|
-
let changed = false;
|
|
703
|
-
const items = item.items.map((member) => {
|
|
704
|
-
if (member.content.type !== "markdown") {
|
|
705
|
-
return member;
|
|
706
|
-
}
|
|
707
|
-
const downgraded = downgradeMarkdown(member.content);
|
|
708
|
-
if (!downgraded) {
|
|
709
|
-
return member;
|
|
710
|
-
}
|
|
711
|
-
changed = true;
|
|
712
|
-
return { ...member, content: downgraded };
|
|
713
|
-
});
|
|
714
|
-
return changed ? { ...item, items } : item;
|
|
715
|
-
}
|
|
716
|
-
return item;
|
|
717
|
-
};
|
|
718
|
-
async function resendDrainedStream(send, item, source, platform, unsupported) {
|
|
719
|
-
platformLog.info(
|
|
720
|
-
`${platform} does not support streaming text; waiting for the stream to finish to send the full text as one message.`,
|
|
721
|
-
{
|
|
722
|
-
"spectrum.provider": platform,
|
|
723
|
-
"spectrum.stream_text.fallback": true
|
|
724
|
-
}
|
|
725
|
-
);
|
|
726
|
-
let full;
|
|
727
|
-
try {
|
|
728
|
-
full = await drainStreamText(source);
|
|
729
|
-
} catch (drainErr) {
|
|
730
|
-
if (drainErr instanceof StreamConsumedError) {
|
|
731
|
-
throw unsupported;
|
|
732
|
-
}
|
|
733
|
-
throw drainErr;
|
|
734
|
-
}
|
|
735
|
-
if (!full) {
|
|
736
|
-
throw unsupported;
|
|
737
|
-
}
|
|
738
|
-
return await sendWithFallbacks(
|
|
739
|
-
send,
|
|
740
|
-
replaceStreamText(item, source, full),
|
|
741
|
-
platform
|
|
742
|
-
);
|
|
743
|
-
}
|
|
744
|
-
async function sendWithFallbacks(send, item, platform) {
|
|
745
|
-
try {
|
|
746
|
-
return await send(item);
|
|
747
|
-
} catch (err) {
|
|
748
|
-
if (!(err instanceof UnsupportedError)) {
|
|
749
|
-
throw err;
|
|
750
|
-
}
|
|
751
|
-
const source = findStreamText(item);
|
|
752
|
-
if (source) {
|
|
753
|
-
return await resendDrainedStream(send, item, source, platform, err);
|
|
754
|
-
}
|
|
755
|
-
const downgraded = replaceMarkdown(item);
|
|
756
|
-
if (downgraded === item) {
|
|
757
|
-
throw err;
|
|
758
|
-
}
|
|
759
|
-
platformLog.info(
|
|
760
|
-
`${platform} does not support markdown; sending the content as plain text instead.`,
|
|
761
|
-
{
|
|
762
|
-
"spectrum.provider": platform,
|
|
763
|
-
"spectrum.markdown.fallback": true
|
|
764
|
-
}
|
|
765
|
-
);
|
|
766
|
-
return await send(downgraded);
|
|
767
|
-
}
|
|
768
|
-
}
|
|
769
|
-
var providerMessageCoreKeys = /* @__PURE__ */ new Set([
|
|
770
|
-
"content",
|
|
771
|
-
"direction",
|
|
772
|
-
"id",
|
|
773
|
-
"sender",
|
|
774
|
-
"space",
|
|
775
|
-
"timestamp"
|
|
776
|
-
]);
|
|
777
|
-
var extractExtras = (raw, definition) => {
|
|
778
|
-
const entries = Object.entries(raw).filter(
|
|
779
|
-
([key]) => !providerMessageCoreKeys.has(key)
|
|
780
|
-
);
|
|
781
|
-
const extra = Object.fromEntries(entries);
|
|
782
|
-
return definition.message?.schema ? definition.message.schema.parse(extra) : extra;
|
|
783
|
-
};
|
|
784
|
-
var rawDirection = (raw) => raw.direction === "inbound" || raw.direction === "outbound" ? raw.direction : void 0;
|
|
785
|
-
function wrapProviderMessage(raw, ctx, direction) {
|
|
786
|
-
const effectiveDirection = rawDirection(raw) ?? direction;
|
|
787
|
-
const wrappedContent = wrapNestedContent(
|
|
788
|
-
raw.content,
|
|
789
|
-
ctx,
|
|
790
|
-
effectiveDirection
|
|
791
|
-
);
|
|
792
|
-
const base = {
|
|
793
|
-
id: raw.id,
|
|
794
|
-
content: wrappedContent,
|
|
795
|
-
timestamp: raw.timestamp ?? /* @__PURE__ */ new Date(),
|
|
796
|
-
extras: extractExtras(raw, ctx.definition),
|
|
797
|
-
spaceRef: ctx.spaceRef,
|
|
798
|
-
space: ctx.space,
|
|
799
|
-
definition: ctx.definition,
|
|
800
|
-
client: ctx.client,
|
|
801
|
-
config: ctx.config,
|
|
802
|
-
store: ctx.store
|
|
803
|
-
};
|
|
804
|
-
if (effectiveDirection === "inbound") {
|
|
805
|
-
return buildMessage({ ...base, sender: raw.sender, direction: "inbound" });
|
|
806
|
-
}
|
|
807
|
-
return buildMessage({ ...base, sender: raw.sender, direction: "outbound" });
|
|
808
|
-
}
|
|
809
|
-
var wrapNestedContent = (content, ctx, direction) => {
|
|
810
|
-
if (content.type === "reaction") {
|
|
811
|
-
const target = content.target;
|
|
812
|
-
if (isRawProviderRecord(target)) {
|
|
813
|
-
return {
|
|
814
|
-
...content,
|
|
815
|
-
target: wrapProviderMessage(target, ctx, "inbound")
|
|
816
|
-
};
|
|
817
|
-
}
|
|
818
|
-
return content;
|
|
819
|
-
}
|
|
820
|
-
if (content.type === "edit") {
|
|
821
|
-
const target = content.target;
|
|
822
|
-
if (isRawProviderRecord(target)) {
|
|
823
|
-
return {
|
|
824
|
-
...content,
|
|
825
|
-
target: wrapProviderMessage(target, ctx, "outbound")
|
|
826
|
-
};
|
|
827
|
-
}
|
|
828
|
-
return content;
|
|
829
|
-
}
|
|
830
|
-
if (content.type === "group") {
|
|
831
|
-
const items = content.items.map((item) => {
|
|
832
|
-
const raw = item;
|
|
833
|
-
if (!isRawProviderRecord(raw)) {
|
|
834
|
-
return item;
|
|
835
|
-
}
|
|
836
|
-
return direction === "inbound" ? wrapProviderMessage(raw, ctx, "inbound") : wrapProviderMessage(raw, ctx, "outbound");
|
|
837
|
-
});
|
|
838
|
-
return { ...content, items };
|
|
839
|
-
}
|
|
840
|
-
return content;
|
|
841
|
-
};
|
|
842
|
-
var isRawProviderRecord = (v) => {
|
|
843
|
-
if (typeof v !== "object" || v === null) {
|
|
844
|
-
return false;
|
|
845
|
-
}
|
|
846
|
-
const record = v;
|
|
847
|
-
return "id" in record && "content" in record && typeof record.react !== "function" && typeof record.reply !== "function";
|
|
848
|
-
};
|
|
849
|
-
function buildSpace(params) {
|
|
850
|
-
const { spaceRef, extras, actionCtx, definition, client, config, store } = params;
|
|
851
|
-
let space;
|
|
852
|
-
async function dispatchSend(item) {
|
|
853
|
-
return withSpan(
|
|
854
|
-
"spectrum.message.send",
|
|
855
|
-
{
|
|
856
|
-
"spectrum.provider": definition.name,
|
|
857
|
-
"spectrum.space.id": spaceRef.id,
|
|
858
|
-
"spectrum.message.fire_and_forget": isFireAndForget(item),
|
|
859
|
-
...contentAttrs(item)
|
|
860
|
-
},
|
|
861
|
-
async () => {
|
|
862
|
-
const platformError = unsupportedPlatformContentError(
|
|
863
|
-
item,
|
|
864
|
-
definition.name
|
|
865
|
-
);
|
|
866
|
-
if (platformError) {
|
|
867
|
-
warnUnsupported(platformError, definition.name);
|
|
868
|
-
return;
|
|
869
|
-
}
|
|
870
|
-
const providerSend = async (content) => await definition.send({
|
|
871
|
-
...actionCtx,
|
|
872
|
-
content
|
|
873
|
-
});
|
|
874
|
-
let raw;
|
|
875
|
-
try {
|
|
876
|
-
raw = await sendWithFallbacks(providerSend, item, definition.name);
|
|
877
|
-
} catch (err) {
|
|
878
|
-
if (err instanceof UnsupportedError) {
|
|
879
|
-
warnUnsupported(err, definition.name);
|
|
880
|
-
return;
|
|
881
|
-
}
|
|
882
|
-
throw err;
|
|
883
|
-
}
|
|
884
|
-
if (!raw?.id) {
|
|
885
|
-
if (isFireAndForget(item)) {
|
|
886
|
-
return;
|
|
887
|
-
}
|
|
888
|
-
throw new Error(
|
|
889
|
-
`Platform "${definition.name}" send did not return a message id`
|
|
890
|
-
);
|
|
891
|
-
}
|
|
892
|
-
return wrapProviderMessage(
|
|
893
|
-
raw,
|
|
894
|
-
{ client, config, definition, space, spaceRef, store },
|
|
895
|
-
"outbound"
|
|
896
|
-
);
|
|
897
|
-
}
|
|
898
|
-
);
|
|
899
|
-
}
|
|
900
|
-
async function sendImpl(...content) {
|
|
901
|
-
const resolved = await resolveContents(content);
|
|
902
|
-
const results = [];
|
|
903
|
-
for (const item of resolved) {
|
|
904
|
-
const sent = await dispatchSend(item);
|
|
905
|
-
if (sent) {
|
|
906
|
-
results.push(sent);
|
|
907
|
-
}
|
|
908
|
-
}
|
|
909
|
-
if (content.length === 1) {
|
|
910
|
-
return results[0];
|
|
911
|
-
}
|
|
912
|
-
return results;
|
|
913
|
-
}
|
|
914
|
-
async function getMessageImpl(id) {
|
|
915
|
-
const getMessage = definition.actions?.getMessage;
|
|
916
|
-
if (!getMessage) {
|
|
917
|
-
throw UnsupportedError.action("getMessage", definition.name);
|
|
918
|
-
}
|
|
919
|
-
return withSpan(
|
|
920
|
-
"spectrum.message.get",
|
|
921
|
-
{
|
|
922
|
-
"spectrum.provider": definition.name,
|
|
923
|
-
"spectrum.space.id": spaceRef.id,
|
|
924
|
-
"spectrum.message.id": id
|
|
925
|
-
},
|
|
926
|
-
async () => {
|
|
927
|
-
const raw = await getMessage(
|
|
928
|
-
{ client, config, store },
|
|
929
|
-
spaceRef,
|
|
930
|
-
id
|
|
931
|
-
);
|
|
932
|
-
if (!raw) {
|
|
933
|
-
return;
|
|
934
|
-
}
|
|
935
|
-
return wrapProviderMessage(
|
|
936
|
-
raw,
|
|
937
|
-
{ client, config, definition, space, spaceRef, store },
|
|
938
|
-
"inbound"
|
|
939
|
-
);
|
|
940
|
-
}
|
|
941
|
-
);
|
|
942
|
-
}
|
|
943
|
-
const platformActions = {};
|
|
944
|
-
const declaredActions = definition.space.actions;
|
|
945
|
-
if (declaredActions) {
|
|
946
|
-
for (const [name, factory] of Object.entries(declaredActions)) {
|
|
947
|
-
if (RESERVED_SPACE_KEYS.has(name)) {
|
|
948
|
-
warnReservedAction("space", name, definition.name);
|
|
949
|
-
continue;
|
|
950
|
-
}
|
|
951
|
-
platformActions[name] = async (...args) => {
|
|
952
|
-
await factory(space, ...args);
|
|
953
|
-
};
|
|
954
|
-
}
|
|
955
|
-
}
|
|
956
|
-
space = {
|
|
957
|
-
...extras,
|
|
958
|
-
...spaceRef,
|
|
959
|
-
...platformActions,
|
|
960
|
-
send: sendImpl,
|
|
961
|
-
edit: async (message, newContent) => {
|
|
962
|
-
await space.send(edit(newContent, message));
|
|
963
|
-
},
|
|
964
|
-
unsend: async (message) => {
|
|
965
|
-
await space.send(unsend(message));
|
|
966
|
-
},
|
|
967
|
-
read: async (message) => {
|
|
968
|
-
await space.send(read(message));
|
|
969
|
-
},
|
|
970
|
-
getMessage: getMessageImpl,
|
|
971
|
-
rename: async (displayName) => {
|
|
972
|
-
await space.send(rename(displayName));
|
|
973
|
-
},
|
|
974
|
-
avatar: (async (input, options) => {
|
|
975
|
-
if (typeof input === "string" || input instanceof URL) {
|
|
976
|
-
await space.send(avatar(input, options));
|
|
977
|
-
return;
|
|
978
|
-
}
|
|
979
|
-
if (!options?.mimeType) {
|
|
980
|
-
throw new Error(
|
|
981
|
-
"space.avatar(Buffer) requires options.mimeType \u2014 pass { mimeType: '...' }"
|
|
982
|
-
);
|
|
983
|
-
}
|
|
984
|
-
await space.send(avatar(input, { mimeType: options.mimeType }));
|
|
985
|
-
}),
|
|
986
|
-
startTyping: async () => {
|
|
987
|
-
await space.send(typing("start"));
|
|
988
|
-
},
|
|
989
|
-
stopTyping: async () => {
|
|
990
|
-
await space.send(typing("stop"));
|
|
991
|
-
},
|
|
992
|
-
responding: async (fn) => {
|
|
993
|
-
await space.send(typing("start"));
|
|
994
|
-
try {
|
|
995
|
-
return await fn();
|
|
996
|
-
} finally {
|
|
997
|
-
await space.send(typing("stop")).catch(() => {
|
|
998
|
-
});
|
|
999
|
-
}
|
|
1000
|
-
}
|
|
1001
|
-
};
|
|
1002
|
-
return space;
|
|
1003
|
-
}
|
|
1004
|
-
function buildMessage(params) {
|
|
1005
|
-
const { definition, space } = params;
|
|
1006
|
-
let self;
|
|
1007
|
-
const requireBuiltMessage = (action) => {
|
|
1008
|
-
if (!self) {
|
|
1009
|
-
throw new Error(
|
|
1010
|
-
`${action}() called before message construction completed (internal bug)`
|
|
1011
|
-
);
|
|
1012
|
-
}
|
|
1013
|
-
return self;
|
|
1014
|
-
};
|
|
1015
|
-
const react = async (emoji) => {
|
|
1016
|
-
const target = requireBuiltMessage("react");
|
|
1017
|
-
return await space.send(reaction(emoji, target));
|
|
1018
|
-
};
|
|
1019
|
-
async function reply2(...content) {
|
|
1020
|
-
const target = requireBuiltMessage("reply");
|
|
1021
|
-
const wrapped = content.map((c) => reply(c, target));
|
|
1022
|
-
return space.send(...wrapped);
|
|
1023
|
-
}
|
|
1024
|
-
const edit2 = async (newContent) => {
|
|
1025
|
-
const target = requireBuiltMessage("edit");
|
|
1026
|
-
if (target.direction !== "outbound") {
|
|
1027
|
-
throw new Error(
|
|
1028
|
-
`cannot edit message ${target.id}: only outbound messages can be edited (direction: "${target.direction}")`
|
|
1029
|
-
);
|
|
1030
|
-
}
|
|
1031
|
-
await space.send(edit(newContent, target));
|
|
1032
|
-
};
|
|
1033
|
-
const unsend2 = async () => {
|
|
1034
|
-
const target = requireBuiltMessage("unsend");
|
|
1035
|
-
if (target.direction !== "outbound") {
|
|
1036
|
-
throw new Error(
|
|
1037
|
-
`cannot unsend message ${target.id}: only outbound messages can be unsent (direction: "${target.direction}")`
|
|
1038
|
-
);
|
|
1039
|
-
}
|
|
1040
|
-
await space.send(unsend(target));
|
|
1041
|
-
};
|
|
1042
|
-
const read2 = async () => {
|
|
1043
|
-
const target = requireBuiltMessage("read");
|
|
1044
|
-
if (target.direction !== "inbound") {
|
|
1045
|
-
throw new Error(
|
|
1046
|
-
`cannot mark message ${target.id} as read: only inbound messages can be marked read (direction: "${target.direction}")`
|
|
1047
|
-
);
|
|
1048
|
-
}
|
|
1049
|
-
await space.send(read(target));
|
|
1050
|
-
};
|
|
1051
|
-
const buildSenderWithPlatform = () => {
|
|
1052
|
-
if (params.sender === void 0) {
|
|
1053
|
-
return;
|
|
1054
|
-
}
|
|
1055
|
-
if (params.direction === "outbound") {
|
|
1056
|
-
return {
|
|
1057
|
-
...params.sender,
|
|
1058
|
-
__platform: definition.name,
|
|
1059
|
-
kind: "agent"
|
|
1060
|
-
};
|
|
1061
|
-
}
|
|
1062
|
-
return { ...params.sender, __platform: definition.name };
|
|
1063
|
-
};
|
|
1064
|
-
const senderWithPlatform = buildSenderWithPlatform();
|
|
1065
|
-
const messagePlatformActions = {};
|
|
1066
|
-
const declaredMessageActions = definition.message?.actions;
|
|
1067
|
-
if (declaredMessageActions) {
|
|
1068
|
-
for (const [name, factory] of Object.entries(declaredMessageActions)) {
|
|
1069
|
-
if (RESERVED_MESSAGE_KEYS.has(name)) {
|
|
1070
|
-
warnReservedAction("message", name, definition.name);
|
|
1071
|
-
continue;
|
|
1072
|
-
}
|
|
1073
|
-
messagePlatformActions[name] = async (...args) => {
|
|
1074
|
-
const target = requireBuiltMessage(name);
|
|
1075
|
-
await factory(target, ...args);
|
|
1076
|
-
};
|
|
1077
|
-
}
|
|
1078
|
-
}
|
|
1079
|
-
const message = {
|
|
1080
|
-
...params.extras,
|
|
1081
|
-
...messagePlatformActions,
|
|
1082
|
-
id: params.id,
|
|
1083
|
-
content: params.content,
|
|
1084
|
-
direction: params.direction,
|
|
1085
|
-
platform: definition.name,
|
|
1086
|
-
react,
|
|
1087
|
-
read: read2,
|
|
1088
|
-
reply: reply2,
|
|
1089
|
-
edit: edit2,
|
|
1090
|
-
unsend: unsend2,
|
|
1091
|
-
sender: senderWithPlatform,
|
|
1092
|
-
space,
|
|
1093
|
-
timestamp: params.timestamp
|
|
1094
|
-
};
|
|
1095
|
-
self = message;
|
|
1096
|
-
return message;
|
|
1097
|
-
}
|
|
1098
|
-
|
|
1099
|
-
// src/platform/define.ts
|
|
1100
|
-
var platformLog2 = createLogger2("spectrum.platform");
|
|
1101
|
-
function buildInstanceActions(platformName, declared, reservedKeys, buildCtx) {
|
|
1102
|
-
const out = {};
|
|
1103
|
-
for (const key of PLATFORM_WISE_ACTION_KEYS) {
|
|
1104
|
-
const override = declared?.[key];
|
|
1105
|
-
if (override && typeof override === "function") {
|
|
1106
|
-
out[key] = (...args) => override(buildCtx(), ...args);
|
|
1107
|
-
} else {
|
|
1108
|
-
out[key] = () => {
|
|
1109
|
-
throw UnsupportedError.action(key, platformName);
|
|
1110
|
-
};
|
|
1111
|
-
}
|
|
1112
|
-
}
|
|
1113
|
-
if (!declared) {
|
|
1114
|
-
return out;
|
|
1115
|
-
}
|
|
1116
|
-
for (const [name, factory] of Object.entries(declared)) {
|
|
1117
|
-
if (PLATFORM_WISE_ACTION_KEYS.has(name)) {
|
|
1118
|
-
continue;
|
|
1119
|
-
}
|
|
1120
|
-
if (reservedKeys.has(name)) {
|
|
1121
|
-
warnReservedAction("instance", name, platformName);
|
|
1122
|
-
continue;
|
|
1123
|
-
}
|
|
1124
|
-
if (typeof factory !== "function") {
|
|
1125
|
-
continue;
|
|
1126
|
-
}
|
|
1127
|
-
out[name] = (...args) => factory(buildCtx(), ...args);
|
|
1128
|
-
}
|
|
1129
|
-
return out;
|
|
1130
|
-
}
|
|
1131
|
-
function createPlatformInstance(def, runtime) {
|
|
1132
|
-
const resolveUserID = async (userID) => {
|
|
1133
|
-
const resolved = await def.user.resolve({
|
|
1134
|
-
input: { userID },
|
|
1135
|
-
client: runtime.client,
|
|
1136
|
-
config: runtime.config,
|
|
1137
|
-
store: runtime.store
|
|
1138
|
-
});
|
|
1139
|
-
return {
|
|
1140
|
-
...resolved,
|
|
1141
|
-
__platform: def.name
|
|
1142
|
-
};
|
|
1143
|
-
};
|
|
1144
|
-
const providerCtx = () => ({
|
|
1145
|
-
client: runtime.client,
|
|
1146
|
-
config: runtime.config,
|
|
1147
|
-
store: runtime.store
|
|
1148
|
-
});
|
|
1149
|
-
const parseSpaceParams = (params) => params !== void 0 && def.space.params ? def.space.params.parse(params) : params;
|
|
1150
|
-
const finalizeSpace = (resolved) => {
|
|
1151
|
-
const parsedSpace = def.space.schema ? def.space.schema.parse(resolved) : resolved;
|
|
1152
|
-
const spaceRef = {
|
|
1153
|
-
...parsedSpace,
|
|
1154
|
-
id: parsedSpace.id,
|
|
1155
|
-
__platform: def.name
|
|
1156
|
-
};
|
|
1157
|
-
const actionCtx = {
|
|
1158
|
-
space: spaceRef,
|
|
1159
|
-
...providerCtx()
|
|
1160
|
-
};
|
|
1161
|
-
return buildSpace({
|
|
1162
|
-
spaceRef,
|
|
1163
|
-
extras: parsedSpace,
|
|
1164
|
-
actionCtx,
|
|
1165
|
-
definition: def,
|
|
1166
|
-
client: runtime.client,
|
|
1167
|
-
config: runtime.config,
|
|
1168
|
-
store: runtime.store
|
|
1169
|
-
});
|
|
1170
|
-
};
|
|
1171
|
-
const base = {
|
|
1172
|
-
async user(userID) {
|
|
1173
|
-
return await resolveUserID(userID);
|
|
1174
|
-
},
|
|
1175
|
-
space: {
|
|
1176
|
-
create: async (users, params) => {
|
|
1177
|
-
const userList = Array.isArray(users) ? users : [users];
|
|
1178
|
-
const first = userList.length === 1 ? userList[0] : void 0;
|
|
1179
|
-
const single = typeof first === "string" ? classifyIdentifier(first) : void 0;
|
|
1180
|
-
const kind = userList.length > 1 ? "group" : single?.kind ?? "unknown";
|
|
1181
|
-
return await withSpan2(
|
|
1182
|
-
"spectrum.space.create",
|
|
1183
|
-
{
|
|
1184
|
-
"spectrum.provider": def.name,
|
|
1185
|
-
"spectrum.space.user_count": userList.length,
|
|
1186
|
-
"spectrum.space.identifier_kind": kind,
|
|
1187
|
-
"spectrum.space.identifier": kind === "unknown" ? void 0 : single?.identifier
|
|
1188
|
-
},
|
|
1189
|
-
async () => {
|
|
1190
|
-
const resolvedUsers = await Promise.all(
|
|
1191
|
-
userList.map(
|
|
1192
|
-
(u) => typeof u === "string" ? resolveUserID(u) : u
|
|
1193
|
-
)
|
|
1194
|
-
);
|
|
1195
|
-
const resolved = await def.space.create({
|
|
1196
|
-
input: { users: resolvedUsers, params: parseSpaceParams(params) },
|
|
1197
|
-
...providerCtx()
|
|
1198
|
-
});
|
|
1199
|
-
return finalizeSpace(resolved);
|
|
1200
|
-
}
|
|
1201
|
-
);
|
|
1202
|
-
},
|
|
1203
|
-
get: async (id, params) => await withSpan2(
|
|
1204
|
-
"spectrum.space.get",
|
|
1205
|
-
{
|
|
1206
|
-
"spectrum.provider": def.name,
|
|
1207
|
-
"spectrum.space.id": id
|
|
1208
|
-
},
|
|
1209
|
-
async () => {
|
|
1210
|
-
const parsedParams = parseSpaceParams(params);
|
|
1211
|
-
if (def.space.get) {
|
|
1212
|
-
const resolved = await def.space.get({
|
|
1213
|
-
input: { id, params: parsedParams },
|
|
1214
|
-
...providerCtx()
|
|
1215
|
-
});
|
|
1216
|
-
return finalizeSpace(resolved);
|
|
1217
|
-
}
|
|
1218
|
-
const candidate = { id };
|
|
1219
|
-
if (def.space.schema) {
|
|
1220
|
-
const parsed = def.space.schema.safeParse(candidate);
|
|
1221
|
-
if (!parsed.success) {
|
|
1222
|
-
throw new Error(
|
|
1223
|
-
`Platform "${def.name}" cannot construct a space from an id alone \u2014 its space schema requires more fields. Implement \`space.get\` in the "${def.name}" provider definition.`,
|
|
1224
|
-
{ cause: parsed.error }
|
|
1225
|
-
);
|
|
1226
|
-
}
|
|
1227
|
-
}
|
|
1228
|
-
return finalizeSpace(candidate);
|
|
1229
|
-
}
|
|
1230
|
-
)
|
|
1231
|
-
}
|
|
1232
|
-
};
|
|
1233
|
-
const eventProperties = {};
|
|
1234
|
-
const customEvents = def.events ?? {};
|
|
1235
|
-
for (const eventName of Object.keys(customEvents)) {
|
|
1236
|
-
const declared = customEvents[eventName];
|
|
1237
|
-
if (typeof declared === "function") {
|
|
1238
|
-
const producer = declared;
|
|
1239
|
-
eventProperties[eventName] = producer({
|
|
1240
|
-
client: runtime.client,
|
|
1241
|
-
config: runtime.config,
|
|
1242
|
-
projectConfig: runtime.projectConfig,
|
|
1243
|
-
store: runtime.store
|
|
1244
|
-
});
|
|
1245
|
-
continue;
|
|
1246
|
-
}
|
|
1247
|
-
const fusorEvents = runtime.subscribeEvent?.(eventName);
|
|
1248
|
-
if (fusorEvents) {
|
|
1249
|
-
eventProperties[eventName] = fusorEvents;
|
|
1250
|
-
}
|
|
1251
|
-
}
|
|
1252
|
-
let messagesIterable;
|
|
1253
|
-
Object.defineProperty(base, "messages", {
|
|
1254
|
-
enumerable: true,
|
|
1255
|
-
get() {
|
|
1256
|
-
messagesIterable ??= runtime.subscribeMessages();
|
|
1257
|
-
return messagesIterable;
|
|
1258
|
-
}
|
|
1259
|
-
});
|
|
1260
|
-
const instanceActions = buildInstanceActions(
|
|
1261
|
-
def.name,
|
|
1262
|
-
def.actions,
|
|
1263
|
-
/* @__PURE__ */ new Set([
|
|
1264
|
-
"user",
|
|
1265
|
-
"space",
|
|
1266
|
-
"messages",
|
|
1267
|
-
...Object.keys(customEvents)
|
|
1268
|
-
]),
|
|
1269
|
-
() => ({
|
|
1270
|
-
client: runtime.client,
|
|
1271
|
-
config: runtime.config,
|
|
1272
|
-
store: runtime.store
|
|
1273
|
-
})
|
|
1274
|
-
);
|
|
1275
|
-
return Object.assign(
|
|
1276
|
-
base,
|
|
1277
|
-
instanceActions,
|
|
1278
|
-
eventProperties
|
|
1279
|
-
);
|
|
1280
|
-
}
|
|
1281
|
-
function definePlatform(name, rawDef) {
|
|
1282
|
-
const def = rawDef;
|
|
1283
|
-
const fullDef = { ...def, name };
|
|
1284
|
-
const platformCache = /* @__PURE__ */ new WeakMap();
|
|
1285
|
-
const narrowSpectrum = (spectrum) => {
|
|
1286
|
-
const cached = platformCache.get(spectrum);
|
|
1287
|
-
if (cached) {
|
|
1288
|
-
return cached;
|
|
1289
|
-
}
|
|
1290
|
-
const runtime = spectrum.__internal.platforms.get(name);
|
|
1291
|
-
if (!runtime) {
|
|
1292
|
-
throw new Error(`Platform "${name}" is not registered`);
|
|
1293
|
-
}
|
|
1294
|
-
const instance = createPlatformInstance(
|
|
1295
|
-
fullDef,
|
|
1296
|
-
runtime
|
|
1297
|
-
);
|
|
1298
|
-
platformCache.set(spectrum, instance);
|
|
1299
|
-
return instance;
|
|
1300
|
-
};
|
|
1301
|
-
const narrowSpace = (input) => {
|
|
1302
|
-
if (input.__platform !== name) {
|
|
1303
|
-
platformLog2.warn("space platform mismatch; narrowing skipped", {
|
|
1304
|
-
expected: name,
|
|
1305
|
-
actual: input.__platform
|
|
1306
|
-
});
|
|
1307
|
-
}
|
|
1308
|
-
return input;
|
|
1309
|
-
};
|
|
1310
|
-
const narrowMessage = (input) => {
|
|
1311
|
-
if (input.platform !== name) {
|
|
1312
|
-
platformLog2.warn("message platform mismatch; narrowing skipped", {
|
|
1313
|
-
expected: name,
|
|
1314
|
-
actual: input.platform
|
|
1315
|
-
});
|
|
1316
|
-
}
|
|
1317
|
-
return input;
|
|
1318
|
-
};
|
|
1319
|
-
const narrower = ((input) => {
|
|
1320
|
-
if ("__providers" in input && "__internal" in input) {
|
|
1321
|
-
return narrowSpectrum(input);
|
|
1322
|
-
}
|
|
1323
|
-
if ("__platform" in input && "send" in input) {
|
|
1324
|
-
return narrowSpace(input);
|
|
1325
|
-
}
|
|
1326
|
-
if ("platform" in input && "sender" in input && "space" in input) {
|
|
1327
|
-
return narrowMessage(input);
|
|
1328
|
-
}
|
|
1329
|
-
throw new Error("Invalid input to platform narrowing function");
|
|
1330
|
-
});
|
|
1331
|
-
narrower.config = (config) => {
|
|
1332
|
-
const resolvedConfig = config ?? {};
|
|
1333
|
-
return {
|
|
1334
|
-
__tag: "PlatformProviderConfig",
|
|
1335
|
-
__def: void 0,
|
|
1336
|
-
__name: name,
|
|
1337
|
-
config: resolvedConfig,
|
|
1338
|
-
__definition: fullDef
|
|
1339
|
-
};
|
|
1340
|
-
};
|
|
1341
|
-
narrower.is = ((input) => {
|
|
1342
|
-
if (typeof input !== "object" || input === null) {
|
|
1343
|
-
return false;
|
|
1344
|
-
}
|
|
1345
|
-
if ("__platform" in input) {
|
|
1346
|
-
return input.__platform === name;
|
|
1347
|
-
}
|
|
1348
|
-
if ("platform" in input) {
|
|
1349
|
-
return input.platform === name;
|
|
1350
|
-
}
|
|
1351
|
-
return false;
|
|
1352
|
-
});
|
|
1353
|
-
if (def.static) {
|
|
1354
|
-
Object.assign(narrower, def.static);
|
|
1355
|
-
}
|
|
1356
|
-
return narrower;
|
|
1357
|
-
}
|
|
1358
|
-
|
|
1359
|
-
export {
|
|
1360
|
-
photoActionSchema,
|
|
1361
|
-
buildPhotoAction,
|
|
1362
|
-
avatar,
|
|
1363
|
-
edit,
|
|
1364
|
-
markdownSchema,
|
|
1365
|
-
asMarkdown,
|
|
1366
|
-
markdown,
|
|
1367
|
-
read,
|
|
1368
|
-
rename,
|
|
1369
|
-
reply,
|
|
1370
|
-
typing,
|
|
1371
|
-
unsend,
|
|
1372
|
-
UnsupportedError,
|
|
1373
|
-
renderInlineTokens,
|
|
1374
|
-
contentAttrs,
|
|
1375
|
-
senderAttrs,
|
|
1376
|
-
wrapProviderMessage,
|
|
1377
|
-
buildSpace,
|
|
1378
|
-
definePlatform
|
|
1379
|
-
};
|