spectrum-ts 2.0.0 → 3.1.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/dist/{attachment-B4nSrKVd.d.ts → attachment-CEpGtZLm.d.ts} +1 -1
- package/dist/{authoring-BjE5BvlO.d.ts → authoring-CP3vRza8.d.ts} +61 -26
- package/dist/authoring.d.ts +3 -3
- package/dist/authoring.js +5 -5
- package/dist/{chunk-NNY6LMSC.js → chunk-77U6SH5A.js} +1 -1
- package/dist/{chunk-WXY5QP3M.js → chunk-7ON5XHC2.js} +27 -21
- package/dist/{chunk-6BI4PFTP.js → chunk-CHY5YLLV.js} +1 -1
- package/dist/{chunk-Q537JPTG.js → chunk-FA7VA4XN.js} +10 -10
- package/dist/{chunk-NGC4DJIX.js → chunk-L3NUESOW.js} +425 -137
- package/dist/{chunk-2ILTJC35.js → chunk-LQMDV75O.js} +205 -11
- package/dist/{chunk-3B4QH4JG.js → chunk-MHGCPC2V.js} +1 -1
- package/dist/{chunk-U7AWXDH6.js → chunk-NZ5WCMTY.js} +1 -1
- package/dist/{chunk-5LT5J3NR.js → chunk-PSSWQBOH.js} +262 -30
- package/dist/{chunk-U3LXXT3W.js → chunk-Q44CIGG6.js} +20 -8
- package/dist/{chunk-ATNAE7OR.js → chunk-WMG36LHW.js} +676 -159
- package/dist/index.d.ts +107 -56
- package/dist/index.js +29 -182
- package/dist/providers/imessage/index.d.ts +7 -14
- package/dist/providers/imessage/index.js +6 -6
- package/dist/providers/index.d.ts +3 -3
- package/dist/providers/index.js +11 -11
- package/dist/providers/slack/index.d.ts +1 -2
- package/dist/providers/slack/index.js +3 -3
- package/dist/providers/telegram/index.d.ts +3 -5
- package/dist/providers/telegram/index.js +5 -5
- package/dist/providers/terminal/index.d.ts +2 -4
- package/dist/providers/terminal/index.js +5 -5
- package/dist/providers/whatsapp-business/index.d.ts +1 -1
- package/dist/providers/whatsapp-business/index.js +4 -4
- package/dist/{types-Bje8aq1k.d.ts → types-Be0T6E0e.d.ts} +172 -23
- package/dist/{types-BD0-kKyv.d.ts → types-CDYXH2R7.d.ts} +1 -1
- package/package.json +2 -1
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { createRequire as __spectrumCreateRequire } from "node:module"; const require = __spectrumCreateRequire(import.meta.url);
|
|
2
2
|
import {
|
|
3
|
+
StreamConsumedError,
|
|
4
|
+
asText,
|
|
5
|
+
drainStreamText,
|
|
3
6
|
fetchUrlBytes,
|
|
4
7
|
reaction,
|
|
5
8
|
readSchema,
|
|
6
|
-
resolveContents
|
|
7
|
-
|
|
9
|
+
resolveContents,
|
|
10
|
+
streamTextBuilder
|
|
11
|
+
} from "./chunk-LQMDV75O.js";
|
|
8
12
|
|
|
9
13
|
// src/content/avatar.ts
|
|
10
14
|
import z2 from "zod";
|
|
@@ -98,6 +102,11 @@ var asEdit = (input) => editSchema.parse({ type: "edit", ...input });
|
|
|
98
102
|
function edit(content, target) {
|
|
99
103
|
return {
|
|
100
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
|
+
}
|
|
101
110
|
if (target.direction !== "outbound") {
|
|
102
111
|
throw new Error(
|
|
103
112
|
`edit() target must be an outbound message (got direction "${target.direction}", message id "${target.id}")`
|
|
@@ -107,7 +116,7 @@ function edit(content, target) {
|
|
|
107
116
|
if (!resolved) {
|
|
108
117
|
throw new Error("edit() requires content");
|
|
109
118
|
}
|
|
110
|
-
if (resolved.type === "edit" || resolved.type === "reply" || resolved.type === "reaction" || resolved.type === "group" || resolved.type === "typing" || resolved.type === "rename" || resolved.type === "avatar") {
|
|
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") {
|
|
111
120
|
throw new Error(`edit() cannot wrap "${resolved.type}" content`);
|
|
112
121
|
}
|
|
113
122
|
return asEdit({ content: resolved, target });
|
|
@@ -115,11 +124,25 @@ function edit(content, target) {
|
|
|
115
124
|
};
|
|
116
125
|
}
|
|
117
126
|
|
|
118
|
-
// src/content/
|
|
127
|
+
// src/content/markdown.ts
|
|
119
128
|
import z4 from "zod";
|
|
120
|
-
var
|
|
121
|
-
type: z4.literal("
|
|
122
|
-
|
|
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/rename.ts
|
|
142
|
+
import z5 from "zod";
|
|
143
|
+
var renameSchema = z5.object({
|
|
144
|
+
type: z5.literal("rename"),
|
|
145
|
+
displayName: z5.string().min(1, "rename() displayName must be non-empty")
|
|
123
146
|
});
|
|
124
147
|
function rename(displayName) {
|
|
125
148
|
return {
|
|
@@ -128,15 +151,15 @@ function rename(displayName) {
|
|
|
128
151
|
}
|
|
129
152
|
|
|
130
153
|
// src/content/reply.ts
|
|
131
|
-
import
|
|
154
|
+
import z6 from "zod";
|
|
132
155
|
var isMessage2 = (v) => typeof v === "object" && v !== null && "id" in v && "content" in v;
|
|
133
156
|
var isContent2 = (v) => typeof v === "object" && v !== null && "type" in v && typeof v.type === "string";
|
|
134
|
-
var replySchema =
|
|
135
|
-
type:
|
|
136
|
-
content:
|
|
157
|
+
var replySchema = z6.object({
|
|
158
|
+
type: z6.literal("reply"),
|
|
159
|
+
content: z6.custom(isContent2, {
|
|
137
160
|
message: "reply content must be a Content value"
|
|
138
161
|
}),
|
|
139
|
-
target:
|
|
162
|
+
target: z6.custom(isMessage2, {
|
|
140
163
|
message: "reply target must be a Message"
|
|
141
164
|
})
|
|
142
165
|
});
|
|
@@ -144,11 +167,16 @@ var asReply = (input) => replySchema.parse({ type: "reply", ...input });
|
|
|
144
167
|
function reply(content, target) {
|
|
145
168
|
return {
|
|
146
169
|
build: async () => {
|
|
170
|
+
if (!target) {
|
|
171
|
+
throw new Error(
|
|
172
|
+
"reply() target is undefined \u2014 the targeted message was never sent (space.send resolves undefined when a platform skips unsupported content)"
|
|
173
|
+
);
|
|
174
|
+
}
|
|
147
175
|
const [resolved] = await resolveContents([content]);
|
|
148
176
|
if (!resolved) {
|
|
149
177
|
throw new Error("reply() requires content");
|
|
150
178
|
}
|
|
151
|
-
if (resolved.type === "reply" || resolved.type === "edit" || resolved.type === "reaction" || resolved.type === "group" || resolved.type === "typing" || resolved.type === "rename" || resolved.type === "avatar") {
|
|
179
|
+
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") {
|
|
152
180
|
throw new Error(`reply() cannot wrap "${resolved.type}" content`);
|
|
153
181
|
}
|
|
154
182
|
return asReply({ content: resolved, target });
|
|
@@ -157,10 +185,10 @@ function reply(content, target) {
|
|
|
157
185
|
}
|
|
158
186
|
|
|
159
187
|
// src/content/typing.ts
|
|
160
|
-
import
|
|
161
|
-
var typingSchema =
|
|
162
|
-
type:
|
|
163
|
-
state:
|
|
188
|
+
import z7 from "zod";
|
|
189
|
+
var typingSchema = z7.object({
|
|
190
|
+
type: z7.literal("typing"),
|
|
191
|
+
state: z7.enum(["start", "stop"])
|
|
164
192
|
});
|
|
165
193
|
function typing(state = "start") {
|
|
166
194
|
return {
|
|
@@ -168,6 +196,34 @@ function typing(state = "start") {
|
|
|
168
196
|
};
|
|
169
197
|
}
|
|
170
198
|
|
|
199
|
+
// src/content/unsend.ts
|
|
200
|
+
import z8 from "zod";
|
|
201
|
+
var isMessage3 = (v) => typeof v === "object" && v !== null && "id" in v && "content" in v;
|
|
202
|
+
var unsendSchema = z8.object({
|
|
203
|
+
type: z8.literal("unsend"),
|
|
204
|
+
target: z8.custom(isMessage3, {
|
|
205
|
+
message: "unsend target must be a Message"
|
|
206
|
+
})
|
|
207
|
+
});
|
|
208
|
+
var asUnsend = (input) => unsendSchema.parse({ type: "unsend", ...input });
|
|
209
|
+
function unsend(target) {
|
|
210
|
+
return {
|
|
211
|
+
build: async () => {
|
|
212
|
+
if (!target) {
|
|
213
|
+
throw new Error(
|
|
214
|
+
"unsend() target is undefined \u2014 the targeted message was never sent (space.send resolves undefined when a platform skips unsupported content)"
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
if (target.direction !== "outbound") {
|
|
218
|
+
throw new Error(
|
|
219
|
+
`unsend() target must be an outbound message (got direction "${target.direction}", message id "${target.id}")`
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
return asUnsend({ target });
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
|
|
171
227
|
// src/utils/errors.ts
|
|
172
228
|
var composeMessage = (opts) => {
|
|
173
229
|
const platform = opts.platform ?? "platform";
|
|
@@ -235,6 +291,133 @@ function classifyIdentifier(s) {
|
|
|
235
291
|
// src/platform/build.ts
|
|
236
292
|
import { createLogger, withSpan } from "@photon-ai/otel";
|
|
237
293
|
|
|
294
|
+
// src/utils/markdown.ts
|
|
295
|
+
import { Marked } from "marked";
|
|
296
|
+
var markdownLexer = new Marked();
|
|
297
|
+
var BULLET = "\u2022 ";
|
|
298
|
+
var HR_LINE = "\u2014\u2014\u2014";
|
|
299
|
+
var NESTED_LIST_INDENT = " ";
|
|
300
|
+
var BLOCK_SEPARATOR = "\n\n";
|
|
301
|
+
var TABLE_CELL_SEPARATOR = " | ";
|
|
302
|
+
var DEFAULT_LIST_START = 1;
|
|
303
|
+
var asMarkedToken = (token) => token;
|
|
304
|
+
var checkboxPrefix = (item) => {
|
|
305
|
+
if (!item.task) {
|
|
306
|
+
return "";
|
|
307
|
+
}
|
|
308
|
+
return item.checked ? "[x] " : "[ ] ";
|
|
309
|
+
};
|
|
310
|
+
var listMarker = (list, index) => {
|
|
311
|
+
if (!list.ordered) {
|
|
312
|
+
return BULLET;
|
|
313
|
+
}
|
|
314
|
+
const start = list.start === "" ? DEFAULT_LIST_START : list.start;
|
|
315
|
+
return `${start + index}. `;
|
|
316
|
+
};
|
|
317
|
+
var renderLink = (token) => {
|
|
318
|
+
if (token.text === token.href) {
|
|
319
|
+
return token.href;
|
|
320
|
+
}
|
|
321
|
+
return `${renderInlineTokens(token.tokens)} (${token.href})`;
|
|
322
|
+
};
|
|
323
|
+
var renderImage = (token) => token.text ? `${token.text} (${token.href})` : token.href;
|
|
324
|
+
var renderInlineToken = (token) => {
|
|
325
|
+
switch (token.type) {
|
|
326
|
+
case "strong":
|
|
327
|
+
case "em":
|
|
328
|
+
case "del":
|
|
329
|
+
return renderInlineTokens(token.tokens);
|
|
330
|
+
case "codespan":
|
|
331
|
+
return token.text;
|
|
332
|
+
case "br":
|
|
333
|
+
return "\n";
|
|
334
|
+
case "link":
|
|
335
|
+
return renderLink(token);
|
|
336
|
+
case "image":
|
|
337
|
+
return renderImage(token);
|
|
338
|
+
case "escape":
|
|
339
|
+
return token.text;
|
|
340
|
+
case "text":
|
|
341
|
+
return token.tokens ? renderInlineTokens(token.tokens) : token.text;
|
|
342
|
+
// Raw HTML in markdown source stays literal — plain text has no markup.
|
|
343
|
+
case "html":
|
|
344
|
+
return token.text;
|
|
345
|
+
// Task-item checkboxes are rendered from `ListItem.task`/`checked`.
|
|
346
|
+
case "checkbox":
|
|
347
|
+
return "";
|
|
348
|
+
default:
|
|
349
|
+
return "raw" in token ? String(token.raw) : "";
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
var renderInlineTokens = (tokens) => {
|
|
353
|
+
let out = "";
|
|
354
|
+
for (const token of tokens) {
|
|
355
|
+
out += renderInlineToken(asMarkedToken(token));
|
|
356
|
+
}
|
|
357
|
+
return out;
|
|
358
|
+
};
|
|
359
|
+
var renderBlockquote = (quote) => renderBlockTokens(quote.tokens).split("\n").map((line) => line ? `> ${line}` : ">").join("\n");
|
|
360
|
+
var renderList = (list) => {
|
|
361
|
+
const lines = [];
|
|
362
|
+
for (const [index, item] of list.items.entries()) {
|
|
363
|
+
const prefix = `${listMarker(list, index)}${checkboxPrefix(item)}`;
|
|
364
|
+
const blocks = [];
|
|
365
|
+
for (const token of item.tokens) {
|
|
366
|
+
const rendered = renderBlockToken(asMarkedToken(token));
|
|
367
|
+
if (rendered) {
|
|
368
|
+
blocks.push(rendered);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
const [first = "", ...rest] = blocks.join("\n").split("\n");
|
|
372
|
+
lines.push(`${prefix}${first}`);
|
|
373
|
+
for (const line of rest) {
|
|
374
|
+
lines.push(`${NESTED_LIST_INDENT}${line}`);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
return lines.join("\n");
|
|
378
|
+
};
|
|
379
|
+
var renderTable = (table) => {
|
|
380
|
+
const renderRow = (cells) => cells.map((cell) => renderInlineTokens(cell.tokens)).join(TABLE_CELL_SEPARATOR);
|
|
381
|
+
const lines = [renderRow(table.header)];
|
|
382
|
+
for (const row of table.rows) {
|
|
383
|
+
lines.push(renderRow(row));
|
|
384
|
+
}
|
|
385
|
+
return lines.join("\n");
|
|
386
|
+
};
|
|
387
|
+
var renderBlockToken = (token) => {
|
|
388
|
+
switch (token.type) {
|
|
389
|
+
case "heading":
|
|
390
|
+
case "paragraph":
|
|
391
|
+
return renderInlineTokens(token.tokens);
|
|
392
|
+
case "code":
|
|
393
|
+
return token.text;
|
|
394
|
+
case "blockquote":
|
|
395
|
+
return renderBlockquote(token);
|
|
396
|
+
case "list":
|
|
397
|
+
return renderList(token);
|
|
398
|
+
case "table":
|
|
399
|
+
return renderTable(token);
|
|
400
|
+
case "hr":
|
|
401
|
+
return HR_LINE;
|
|
402
|
+
case "space":
|
|
403
|
+
case "def":
|
|
404
|
+
return "";
|
|
405
|
+
default:
|
|
406
|
+
return renderInlineToken(token);
|
|
407
|
+
}
|
|
408
|
+
};
|
|
409
|
+
var renderBlockTokens = (tokens) => {
|
|
410
|
+
const blocks = [];
|
|
411
|
+
for (const token of tokens) {
|
|
412
|
+
const rendered = renderBlockToken(asMarkedToken(token));
|
|
413
|
+
if (rendered) {
|
|
414
|
+
blocks.push(rendered);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
return blocks.join(BLOCK_SEPARATOR);
|
|
418
|
+
};
|
|
419
|
+
var markdownToPlainText = (markdown2) => renderBlockTokens(markdownLexer.lexer(markdown2)).trim();
|
|
420
|
+
|
|
238
421
|
// src/utils/telemetry.ts
|
|
239
422
|
var targetId = (target) => {
|
|
240
423
|
const id = target?.id;
|
|
@@ -254,6 +437,13 @@ var replyOrEditAttrs = (content) => {
|
|
|
254
437
|
"spectrum.message.content.inner.type": typeof innerType === "string" ? innerType : void 0
|
|
255
438
|
};
|
|
256
439
|
};
|
|
440
|
+
var unsendAttrs = (content) => {
|
|
441
|
+
const target = content.target;
|
|
442
|
+
return {
|
|
443
|
+
"spectrum.message.content.target.id": targetId(target),
|
|
444
|
+
"spectrum.message.content.target.type": targetType(target)
|
|
445
|
+
};
|
|
446
|
+
};
|
|
257
447
|
var reactionAttrs = (content) => {
|
|
258
448
|
const target = content.target;
|
|
259
449
|
const emoji = content.emoji;
|
|
@@ -303,6 +493,7 @@ var voiceAttrs = (content) => {
|
|
|
303
493
|
var CONTENT_ATTR_HANDLERS = {
|
|
304
494
|
reply: replyOrEditAttrs,
|
|
305
495
|
edit: replyOrEditAttrs,
|
|
496
|
+
unsend: unsendAttrs,
|
|
306
497
|
reaction: reactionAttrs,
|
|
307
498
|
group: groupAttrs,
|
|
308
499
|
typing: typingAttrs,
|
|
@@ -350,11 +541,11 @@ var supportsAnsiColor = () => {
|
|
|
350
541
|
return Boolean(process.stderr?.isTTY);
|
|
351
542
|
};
|
|
352
543
|
var FIRE_AND_FORGET_TYPES = /* @__PURE__ */ new Set([
|
|
353
|
-
"reaction",
|
|
354
544
|
"typing",
|
|
355
545
|
"edit",
|
|
356
546
|
"rename",
|
|
357
|
-
"avatar"
|
|
547
|
+
"avatar",
|
|
548
|
+
"unsend"
|
|
358
549
|
]);
|
|
359
550
|
var isFireAndForget = (item) => FIRE_AND_FORGET_TYPES.has(item.type) || item.__fireAndForget === true;
|
|
360
551
|
var RESERVED_SPACE_KEYS = /* @__PURE__ */ new Set([
|
|
@@ -362,6 +553,7 @@ var RESERVED_SPACE_KEYS = /* @__PURE__ */ new Set([
|
|
|
362
553
|
"id",
|
|
363
554
|
"send",
|
|
364
555
|
"edit",
|
|
556
|
+
"unsend",
|
|
365
557
|
"getMessage",
|
|
366
558
|
"rename",
|
|
367
559
|
"avatar",
|
|
@@ -380,7 +572,8 @@ var RESERVED_MESSAGE_KEYS = /* @__PURE__ */ new Set([
|
|
|
380
572
|
"reply",
|
|
381
573
|
"sender",
|
|
382
574
|
"space",
|
|
383
|
-
"timestamp"
|
|
575
|
+
"timestamp",
|
|
576
|
+
"unsend"
|
|
384
577
|
]);
|
|
385
578
|
var scopeLabel = (scope) => {
|
|
386
579
|
if (scope === "space") {
|
|
@@ -451,8 +644,105 @@ var unsupportedPlatformContentError = (content, platform) => {
|
|
|
451
644
|
`requires ${requiredPlatform}`
|
|
452
645
|
);
|
|
453
646
|
};
|
|
647
|
+
var findStreamText = (item) => {
|
|
648
|
+
if (item.type === "streamText") {
|
|
649
|
+
return item;
|
|
650
|
+
}
|
|
651
|
+
if ((item.type === "reply" || item.type === "edit") && item.content.type === "streamText") {
|
|
652
|
+
return item.content;
|
|
653
|
+
}
|
|
654
|
+
return;
|
|
655
|
+
};
|
|
656
|
+
var replaceStreamText = (item, source, full) => {
|
|
657
|
+
const inner = source.format === "markdown" ? asMarkdown(full) : asText(full);
|
|
658
|
+
if (item.type === "reply" || item.type === "edit") {
|
|
659
|
+
return { ...item, content: inner };
|
|
660
|
+
}
|
|
661
|
+
return inner;
|
|
662
|
+
};
|
|
663
|
+
var downgradeMarkdown = (md) => {
|
|
664
|
+
const plain = markdownToPlainText(md.markdown);
|
|
665
|
+
return plain ? asText(plain) : void 0;
|
|
666
|
+
};
|
|
667
|
+
var replaceMarkdown = (item) => {
|
|
668
|
+
if (item.type === "markdown") {
|
|
669
|
+
return downgradeMarkdown(item) ?? item;
|
|
670
|
+
}
|
|
671
|
+
if ((item.type === "reply" || item.type === "edit") && item.content.type === "markdown") {
|
|
672
|
+
const downgraded = downgradeMarkdown(item.content);
|
|
673
|
+
return downgraded ? { ...item, content: downgraded } : item;
|
|
674
|
+
}
|
|
675
|
+
if (item.type === "group") {
|
|
676
|
+
let changed = false;
|
|
677
|
+
const items = item.items.map((member) => {
|
|
678
|
+
if (member.content.type !== "markdown") {
|
|
679
|
+
return member;
|
|
680
|
+
}
|
|
681
|
+
const downgraded = downgradeMarkdown(member.content);
|
|
682
|
+
if (!downgraded) {
|
|
683
|
+
return member;
|
|
684
|
+
}
|
|
685
|
+
changed = true;
|
|
686
|
+
return { ...member, content: downgraded };
|
|
687
|
+
});
|
|
688
|
+
return changed ? { ...item, items } : item;
|
|
689
|
+
}
|
|
690
|
+
return item;
|
|
691
|
+
};
|
|
692
|
+
async function resendDrainedStream(send, item, source, platform, unsupported) {
|
|
693
|
+
platformLog.info(
|
|
694
|
+
`${platform} does not support streaming text; waiting for the stream to finish to send the full text as one message.`,
|
|
695
|
+
{
|
|
696
|
+
"spectrum.provider": platform,
|
|
697
|
+
"spectrum.stream_text.fallback": true
|
|
698
|
+
}
|
|
699
|
+
);
|
|
700
|
+
let full;
|
|
701
|
+
try {
|
|
702
|
+
full = await drainStreamText(source);
|
|
703
|
+
} catch (drainErr) {
|
|
704
|
+
if (drainErr instanceof StreamConsumedError) {
|
|
705
|
+
throw unsupported;
|
|
706
|
+
}
|
|
707
|
+
throw drainErr;
|
|
708
|
+
}
|
|
709
|
+
if (!full) {
|
|
710
|
+
throw unsupported;
|
|
711
|
+
}
|
|
712
|
+
return await sendWithFallbacks(
|
|
713
|
+
send,
|
|
714
|
+
replaceStreamText(item, source, full),
|
|
715
|
+
platform
|
|
716
|
+
);
|
|
717
|
+
}
|
|
718
|
+
async function sendWithFallbacks(send, item, platform) {
|
|
719
|
+
try {
|
|
720
|
+
return await send(item);
|
|
721
|
+
} catch (err) {
|
|
722
|
+
if (!(err instanceof UnsupportedError)) {
|
|
723
|
+
throw err;
|
|
724
|
+
}
|
|
725
|
+
const source = findStreamText(item);
|
|
726
|
+
if (source) {
|
|
727
|
+
return await resendDrainedStream(send, item, source, platform, err);
|
|
728
|
+
}
|
|
729
|
+
const downgraded = replaceMarkdown(item);
|
|
730
|
+
if (downgraded === item) {
|
|
731
|
+
throw err;
|
|
732
|
+
}
|
|
733
|
+
platformLog.info(
|
|
734
|
+
`${platform} does not support markdown; sending the content as plain text instead.`,
|
|
735
|
+
{
|
|
736
|
+
"spectrum.provider": platform,
|
|
737
|
+
"spectrum.markdown.fallback": true
|
|
738
|
+
}
|
|
739
|
+
);
|
|
740
|
+
return await send(downgraded);
|
|
741
|
+
}
|
|
742
|
+
}
|
|
454
743
|
var providerMessageCoreKeys = /* @__PURE__ */ new Set([
|
|
455
744
|
"content",
|
|
745
|
+
"direction",
|
|
456
746
|
"id",
|
|
457
747
|
"sender",
|
|
458
748
|
"space",
|
|
@@ -465,8 +755,14 @@ var extractExtras = (raw, definition) => {
|
|
|
465
755
|
const extra = Object.fromEntries(entries);
|
|
466
756
|
return definition.message?.schema ? definition.message.schema.parse(extra) : extra;
|
|
467
757
|
};
|
|
758
|
+
var rawDirection = (raw) => raw.direction === "inbound" || raw.direction === "outbound" ? raw.direction : void 0;
|
|
468
759
|
function wrapProviderMessage(raw, ctx, direction) {
|
|
469
|
-
const
|
|
760
|
+
const effectiveDirection = rawDirection(raw) ?? direction;
|
|
761
|
+
const wrappedContent = wrapNestedContent(
|
|
762
|
+
raw.content,
|
|
763
|
+
ctx,
|
|
764
|
+
effectiveDirection
|
|
765
|
+
);
|
|
470
766
|
const base = {
|
|
471
767
|
id: raw.id,
|
|
472
768
|
content: wrappedContent,
|
|
@@ -479,7 +775,7 @@ function wrapProviderMessage(raw, ctx, direction) {
|
|
|
479
775
|
config: ctx.config,
|
|
480
776
|
store: ctx.store
|
|
481
777
|
};
|
|
482
|
-
if (
|
|
778
|
+
if (effectiveDirection === "inbound") {
|
|
483
779
|
return buildMessage({ ...base, sender: raw.sender, direction: "inbound" });
|
|
484
780
|
}
|
|
485
781
|
return buildMessage({ ...base, sender: raw.sender, direction: "outbound" });
|
|
@@ -537,19 +833,21 @@ function buildSpace(params) {
|
|
|
537
833
|
...contentAttrs(item)
|
|
538
834
|
},
|
|
539
835
|
async () => {
|
|
836
|
+
const platformError = unsupportedPlatformContentError(
|
|
837
|
+
item,
|
|
838
|
+
definition.name
|
|
839
|
+
);
|
|
840
|
+
if (platformError) {
|
|
841
|
+
warnUnsupported(platformError, definition.name);
|
|
842
|
+
return;
|
|
843
|
+
}
|
|
844
|
+
const providerSend = async (content) => await definition.send({
|
|
845
|
+
...actionCtx,
|
|
846
|
+
content
|
|
847
|
+
});
|
|
540
848
|
let raw;
|
|
541
849
|
try {
|
|
542
|
-
|
|
543
|
-
item,
|
|
544
|
-
definition.name
|
|
545
|
-
);
|
|
546
|
-
if (platformError) {
|
|
547
|
-
throw platformError;
|
|
548
|
-
}
|
|
549
|
-
raw = await definition.send({
|
|
550
|
-
...actionCtx,
|
|
551
|
-
content: item
|
|
552
|
-
});
|
|
850
|
+
raw = await sendWithFallbacks(providerSend, item, definition.name);
|
|
553
851
|
} catch (err) {
|
|
554
852
|
if (err instanceof UnsupportedError) {
|
|
555
853
|
warnUnsupported(err, definition.name);
|
|
@@ -637,6 +935,9 @@ function buildSpace(params) {
|
|
|
637
935
|
edit: async (message, newContent) => {
|
|
638
936
|
await space.send(edit(newContent, message));
|
|
639
937
|
},
|
|
938
|
+
unsend: async (message) => {
|
|
939
|
+
await space.send(unsend(message));
|
|
940
|
+
},
|
|
640
941
|
getMessage: getMessageImpl,
|
|
641
942
|
rename: async (displayName) => {
|
|
642
943
|
await space.send(rename(displayName));
|
|
@@ -684,7 +985,7 @@ function buildMessage(params) {
|
|
|
684
985
|
};
|
|
685
986
|
const react = async (emoji) => {
|
|
686
987
|
const target = requireBuiltMessage("react");
|
|
687
|
-
await space.send(reaction(emoji, target));
|
|
988
|
+
return await space.send(reaction(emoji, target));
|
|
688
989
|
};
|
|
689
990
|
async function reply2(...content) {
|
|
690
991
|
const target = requireBuiltMessage("reply");
|
|
@@ -700,6 +1001,15 @@ function buildMessage(params) {
|
|
|
700
1001
|
}
|
|
701
1002
|
await space.send(edit(newContent, target));
|
|
702
1003
|
};
|
|
1004
|
+
const unsend2 = async () => {
|
|
1005
|
+
const target = requireBuiltMessage("unsend");
|
|
1006
|
+
if (target.direction !== "outbound") {
|
|
1007
|
+
throw new Error(
|
|
1008
|
+
`cannot unsend message ${target.id}: only outbound messages can be unsent (direction: "${target.direction}")`
|
|
1009
|
+
);
|
|
1010
|
+
}
|
|
1011
|
+
await space.send(unsend(target));
|
|
1012
|
+
};
|
|
703
1013
|
const buildSenderWithPlatform = () => {
|
|
704
1014
|
if (params.sender === void 0) {
|
|
705
1015
|
return;
|
|
@@ -738,6 +1048,7 @@ function buildMessage(params) {
|
|
|
738
1048
|
react,
|
|
739
1049
|
reply: reply2,
|
|
740
1050
|
edit: edit2,
|
|
1051
|
+
unsend: unsend2,
|
|
741
1052
|
sender: senderWithPlatform,
|
|
742
1053
|
space,
|
|
743
1054
|
timestamp: params.timestamp
|
|
@@ -748,21 +1059,6 @@ function buildMessage(params) {
|
|
|
748
1059
|
|
|
749
1060
|
// src/platform/define.ts
|
|
750
1061
|
var platformLog2 = createLogger2("spectrum.platform");
|
|
751
|
-
function classifySpaceIdentifier(args) {
|
|
752
|
-
const stringArgs = args.filter((a) => typeof a === "string");
|
|
753
|
-
if (stringArgs.length > 1) {
|
|
754
|
-
return { kind: "group" };
|
|
755
|
-
}
|
|
756
|
-
const s = stringArgs[0];
|
|
757
|
-
if (!s) {
|
|
758
|
-
return { kind: "unknown" };
|
|
759
|
-
}
|
|
760
|
-
const { kind, identifier } = classifyIdentifier(s);
|
|
761
|
-
if (kind === "unknown") {
|
|
762
|
-
return { kind: "unknown" };
|
|
763
|
-
}
|
|
764
|
-
return { kind, identifier };
|
|
765
|
-
}
|
|
766
1062
|
function buildInstanceActions(platformName, declared, reservedKeys, buildCtx) {
|
|
767
1063
|
const out = {};
|
|
768
1064
|
for (const key of PLATFORM_WISE_ACTION_KEYS) {
|
|
@@ -794,7 +1090,6 @@ function buildInstanceActions(platformName, declared, reservedKeys, buildCtx) {
|
|
|
794
1090
|
return out;
|
|
795
1091
|
}
|
|
796
1092
|
function createPlatformInstance(def, runtime) {
|
|
797
|
-
const isPlatformUser = (value) => typeof value === "object" && value !== null && "__platform" in value && value.__platform === def.name;
|
|
798
1093
|
const resolveUserID = async (userID) => {
|
|
799
1094
|
const resolved = await def.user.resolve({
|
|
800
1095
|
input: { userID },
|
|
@@ -807,105 +1102,93 @@ function createPlatformInstance(def, runtime) {
|
|
|
807
1102
|
__platform: def.name
|
|
808
1103
|
};
|
|
809
1104
|
};
|
|
810
|
-
const
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
1105
|
+
const providerCtx = () => ({
|
|
1106
|
+
client: runtime.client,
|
|
1107
|
+
config: runtime.config,
|
|
1108
|
+
store: runtime.store
|
|
1109
|
+
});
|
|
1110
|
+
const parseSpaceParams = (params) => params !== void 0 && def.space.params ? def.space.params.parse(params) : params;
|
|
1111
|
+
const finalizeSpace = (resolved) => {
|
|
1112
|
+
const parsedSpace = def.space.schema ? def.space.schema.parse(resolved) : resolved;
|
|
1113
|
+
const spaceRef = {
|
|
1114
|
+
...parsedSpace,
|
|
1115
|
+
id: parsedSpace.id,
|
|
1116
|
+
__platform: def.name
|
|
819
1117
|
};
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
if (args.length === 0) {
|
|
824
|
-
return { users: [], params: void 0 };
|
|
825
|
-
}
|
|
826
|
-
const [first, ...rest] = args;
|
|
827
|
-
if (Array.isArray(first)) {
|
|
828
|
-
return {
|
|
829
|
-
users: first,
|
|
830
|
-
params: rest[0]
|
|
831
|
-
};
|
|
832
|
-
}
|
|
833
|
-
if (!isPlatformUser(first)) {
|
|
834
|
-
return {
|
|
835
|
-
users: [],
|
|
836
|
-
params: first
|
|
837
|
-
};
|
|
838
|
-
}
|
|
839
|
-
const last = args.at(-1);
|
|
840
|
-
if (last !== void 0 && !isPlatformUser(last)) {
|
|
841
|
-
return {
|
|
842
|
-
users: args.slice(0, -1),
|
|
843
|
-
params: last
|
|
844
|
-
};
|
|
845
|
-
}
|
|
846
|
-
return {
|
|
847
|
-
users: args,
|
|
848
|
-
params: void 0
|
|
1118
|
+
const actionCtx = {
|
|
1119
|
+
space: spaceRef,
|
|
1120
|
+
...providerCtx()
|
|
849
1121
|
};
|
|
1122
|
+
return buildSpace({
|
|
1123
|
+
spaceRef,
|
|
1124
|
+
extras: parsedSpace,
|
|
1125
|
+
actionCtx,
|
|
1126
|
+
definition: def,
|
|
1127
|
+
client: runtime.client,
|
|
1128
|
+
config: runtime.config,
|
|
1129
|
+
store: runtime.store
|
|
1130
|
+
});
|
|
850
1131
|
};
|
|
851
1132
|
const base = {
|
|
852
1133
|
async user(userID) {
|
|
853
|
-
|
|
854
|
-
input: { userID },
|
|
855
|
-
client: runtime.client,
|
|
856
|
-
config: runtime.config,
|
|
857
|
-
store: runtime.store
|
|
858
|
-
});
|
|
859
|
-
return {
|
|
860
|
-
...resolved,
|
|
861
|
-
__platform: def.name
|
|
862
|
-
};
|
|
1134
|
+
return await resolveUserID(userID);
|
|
863
1135
|
},
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
1136
|
+
space: {
|
|
1137
|
+
create: async (users, params) => {
|
|
1138
|
+
const userList = Array.isArray(users) ? users : [users];
|
|
1139
|
+
const first = userList.length === 1 ? userList[0] : void 0;
|
|
1140
|
+
const single = typeof first === "string" ? classifyIdentifier(first) : void 0;
|
|
1141
|
+
const kind = userList.length > 1 ? "group" : single?.kind ?? "unknown";
|
|
1142
|
+
return await withSpan2(
|
|
1143
|
+
"spectrum.space.create",
|
|
1144
|
+
{
|
|
1145
|
+
"spectrum.provider": def.name,
|
|
1146
|
+
"spectrum.space.user_count": userList.length,
|
|
1147
|
+
"spectrum.space.identifier_kind": kind,
|
|
1148
|
+
"spectrum.space.identifier": kind === "unknown" ? void 0 : single?.identifier
|
|
1149
|
+
},
|
|
1150
|
+
async () => {
|
|
1151
|
+
const resolvedUsers = await Promise.all(
|
|
1152
|
+
userList.map(
|
|
1153
|
+
(u) => typeof u === "string" ? resolveUserID(u) : u
|
|
1154
|
+
)
|
|
1155
|
+
);
|
|
1156
|
+
const resolved = await def.space.create({
|
|
1157
|
+
input: { users: resolvedUsers, params: parseSpaceParams(params) },
|
|
1158
|
+
...providerCtx()
|
|
1159
|
+
});
|
|
1160
|
+
return finalizeSpace(resolved);
|
|
1161
|
+
}
|
|
1162
|
+
);
|
|
1163
|
+
},
|
|
1164
|
+
get: async (id, params) => await withSpan2(
|
|
1165
|
+
"spectrum.space.get",
|
|
868
1166
|
{
|
|
869
1167
|
"spectrum.provider": def.name,
|
|
870
|
-
"spectrum.space.
|
|
871
|
-
"spectrum.space.identifier": identifier
|
|
1168
|
+
"spectrum.space.id": id
|
|
872
1169
|
},
|
|
873
1170
|
async () => {
|
|
874
|
-
const
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
1171
|
+
const parsedParams = parseSpaceParams(params);
|
|
1172
|
+
if (def.space.get) {
|
|
1173
|
+
const resolved = await def.space.get({
|
|
1174
|
+
input: { id, params: parsedParams },
|
|
1175
|
+
...providerCtx()
|
|
1176
|
+
});
|
|
1177
|
+
return finalizeSpace(resolved);
|
|
879
1178
|
}
|
|
880
|
-
const
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
};
|
|
892
|
-
const actionCtx = {
|
|
893
|
-
space: spaceRef,
|
|
894
|
-
client: runtime.client,
|
|
895
|
-
config: runtime.config,
|
|
896
|
-
store: runtime.store
|
|
897
|
-
};
|
|
898
|
-
return buildSpace({
|
|
899
|
-
spaceRef,
|
|
900
|
-
extras: parsedSpace,
|
|
901
|
-
actionCtx,
|
|
902
|
-
definition: def,
|
|
903
|
-
client: runtime.client,
|
|
904
|
-
config: runtime.config,
|
|
905
|
-
store: runtime.store
|
|
906
|
-
});
|
|
1179
|
+
const candidate = { id };
|
|
1180
|
+
if (def.space.schema) {
|
|
1181
|
+
const parsed = def.space.schema.safeParse(candidate);
|
|
1182
|
+
if (!parsed.success) {
|
|
1183
|
+
throw new Error(
|
|
1184
|
+
`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.`,
|
|
1185
|
+
{ cause: parsed.error }
|
|
1186
|
+
);
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
return finalizeSpace(candidate);
|
|
907
1190
|
}
|
|
908
|
-
)
|
|
1191
|
+
)
|
|
909
1192
|
}
|
|
910
1193
|
};
|
|
911
1194
|
const eventProperties = {};
|
|
@@ -1039,10 +1322,15 @@ export {
|
|
|
1039
1322
|
buildPhotoAction,
|
|
1040
1323
|
avatar,
|
|
1041
1324
|
edit,
|
|
1325
|
+
markdownSchema,
|
|
1326
|
+
asMarkdown,
|
|
1327
|
+
markdown,
|
|
1042
1328
|
rename,
|
|
1043
1329
|
reply,
|
|
1044
1330
|
typing,
|
|
1331
|
+
unsend,
|
|
1045
1332
|
UnsupportedError,
|
|
1333
|
+
renderInlineTokens,
|
|
1046
1334
|
contentAttrs,
|
|
1047
1335
|
senderAttrs,
|
|
1048
1336
|
wrapProviderMessage,
|