spectrum-ts 1.18.0 → 3.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 +11 -1
- package/dist/{attachment-DfWSZS5L.d.ts → attachment-WePAHfcH.d.ts} +1 -1
- package/dist/{authoring-C9uDdZ2F.d.ts → authoring-DDh3muGT.d.ts} +61 -26
- package/dist/authoring.d.ts +3 -3
- package/dist/authoring.js +8 -5
- package/dist/chunk-34FQGGD7.js +34 -0
- package/dist/chunk-3GEJYGZK.js +84 -0
- package/dist/{chunk-MC6ZKFSG.js → chunk-5XEFJBN2.js} +25 -103
- package/dist/{chunk-QGJFZMD5.js → chunk-6UZFVXQF.js} +17 -101
- package/dist/{chunk-NNY6LMSC.js → chunk-77U6SH5A.js} +1 -1
- package/dist/{chunk-YN6WOTBF.js → chunk-AYCMTRVC.js} +622 -79
- package/dist/{chunk-JQN6CRSC.js → chunk-CHY5YLLV.js} +11 -40
- package/dist/{chunk-5BKZJMZV.js → chunk-EZ5SNNFS.js} +79 -38
- package/dist/{chunk-3OTECDNH.js → chunk-FULEQIRQ.js} +31 -23
- package/dist/{chunk-2ILTJC35.js → chunk-LQMDV75O.js} +205 -11
- package/dist/{chunk-IPOFBAIM.js → chunk-LX437ZTY.js} +439 -154
- package/dist/chunk-MHGCPC2V.js +35 -0
- package/dist/chunk-NZ5WCMTY.js +91 -0
- package/dist/chunk-TXRWKSNH.js +927 -0
- package/dist/{chunk-5TIF3FIE.js → chunk-UXJ5OO6P.js} +16 -14
- package/dist/index.d.ts +125 -129
- package/dist/index.js +180 -73
- package/dist/manifest.json +6 -0
- package/dist/providers/imessage/index.d.ts +6 -14
- package/dist/providers/imessage/index.js +9 -6
- package/dist/providers/index.d.ts +5 -2
- package/dist/providers/index.js +18 -10
- package/dist/providers/slack/index.d.ts +1 -2
- package/dist/providers/slack/index.js +5 -4
- package/dist/providers/telegram/index.d.ts +45 -0
- package/dist/providers/telegram/index.js +13 -0
- package/dist/providers/terminal/index.d.ts +18 -422
- package/dist/providers/terminal/index.js +7 -5
- package/dist/providers/whatsapp-business/index.d.ts +1 -1
- package/dist/providers/whatsapp-business/index.js +7 -5
- package/dist/types-BujGKBin.d.ts +82 -0
- package/dist/{types-DcQ5a7PK.d.ts → types-YqCNUDIt.d.ts} +204 -26
- package/package.json +3 -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";
|
|
@@ -216,7 +272,7 @@ var UnsupportedError = class _UnsupportedError extends Error {
|
|
|
216
272
|
};
|
|
217
273
|
|
|
218
274
|
// src/platform/define.ts
|
|
219
|
-
import { withSpan as withSpan2 } from "@photon-ai/otel";
|
|
275
|
+
import { createLogger as createLogger2, withSpan as withSpan2 } from "@photon-ai/otel";
|
|
220
276
|
|
|
221
277
|
// src/utils/identifier.ts
|
|
222
278
|
import { sanitizeEmail, sanitizePhone } from "@photon-ai/otel";
|
|
@@ -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,6 +644,102 @@ 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",
|
|
456
745
|
"id",
|
|
@@ -537,19 +826,21 @@ function buildSpace(params) {
|
|
|
537
826
|
...contentAttrs(item)
|
|
538
827
|
},
|
|
539
828
|
async () => {
|
|
829
|
+
const platformError = unsupportedPlatformContentError(
|
|
830
|
+
item,
|
|
831
|
+
definition.name
|
|
832
|
+
);
|
|
833
|
+
if (platformError) {
|
|
834
|
+
warnUnsupported(platformError, definition.name);
|
|
835
|
+
return;
|
|
836
|
+
}
|
|
837
|
+
const providerSend = async (content) => await definition.send({
|
|
838
|
+
...actionCtx,
|
|
839
|
+
content
|
|
840
|
+
});
|
|
540
841
|
let raw;
|
|
541
842
|
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
|
-
});
|
|
843
|
+
raw = await sendWithFallbacks(providerSend, item, definition.name);
|
|
553
844
|
} catch (err) {
|
|
554
845
|
if (err instanceof UnsupportedError) {
|
|
555
846
|
warnUnsupported(err, definition.name);
|
|
@@ -637,6 +928,9 @@ function buildSpace(params) {
|
|
|
637
928
|
edit: async (message, newContent) => {
|
|
638
929
|
await space.send(edit(newContent, message));
|
|
639
930
|
},
|
|
931
|
+
unsend: async (message) => {
|
|
932
|
+
await space.send(unsend(message));
|
|
933
|
+
},
|
|
640
934
|
getMessage: getMessageImpl,
|
|
641
935
|
rename: async (displayName) => {
|
|
642
936
|
await space.send(rename(displayName));
|
|
@@ -684,7 +978,7 @@ function buildMessage(params) {
|
|
|
684
978
|
};
|
|
685
979
|
const react = async (emoji) => {
|
|
686
980
|
const target = requireBuiltMessage("react");
|
|
687
|
-
await space.send(reaction(emoji, target));
|
|
981
|
+
return await space.send(reaction(emoji, target));
|
|
688
982
|
};
|
|
689
983
|
async function reply2(...content) {
|
|
690
984
|
const target = requireBuiltMessage("reply");
|
|
@@ -700,6 +994,15 @@ function buildMessage(params) {
|
|
|
700
994
|
}
|
|
701
995
|
await space.send(edit(newContent, target));
|
|
702
996
|
};
|
|
997
|
+
const unsend2 = async () => {
|
|
998
|
+
const target = requireBuiltMessage("unsend");
|
|
999
|
+
if (target.direction !== "outbound") {
|
|
1000
|
+
throw new Error(
|
|
1001
|
+
`cannot unsend message ${target.id}: only outbound messages can be unsent (direction: "${target.direction}")`
|
|
1002
|
+
);
|
|
1003
|
+
}
|
|
1004
|
+
await space.send(unsend(target));
|
|
1005
|
+
};
|
|
703
1006
|
const buildSenderWithPlatform = () => {
|
|
704
1007
|
if (params.sender === void 0) {
|
|
705
1008
|
return;
|
|
@@ -738,6 +1041,7 @@ function buildMessage(params) {
|
|
|
738
1041
|
react,
|
|
739
1042
|
reply: reply2,
|
|
740
1043
|
edit: edit2,
|
|
1044
|
+
unsend: unsend2,
|
|
741
1045
|
sender: senderWithPlatform,
|
|
742
1046
|
space,
|
|
743
1047
|
timestamp: params.timestamp
|
|
@@ -747,21 +1051,7 @@ function buildMessage(params) {
|
|
|
747
1051
|
}
|
|
748
1052
|
|
|
749
1053
|
// src/platform/define.ts
|
|
750
|
-
|
|
751
|
-
const stringArgs = args.filter((a) => typeof a === "string");
|
|
752
|
-
if (stringArgs.length > 1) {
|
|
753
|
-
return { kind: "group" };
|
|
754
|
-
}
|
|
755
|
-
const s = stringArgs[0];
|
|
756
|
-
if (!s) {
|
|
757
|
-
return { kind: "unknown" };
|
|
758
|
-
}
|
|
759
|
-
const { kind, identifier } = classifyIdentifier(s);
|
|
760
|
-
if (kind === "unknown") {
|
|
761
|
-
return { kind: "unknown" };
|
|
762
|
-
}
|
|
763
|
-
return { kind, identifier };
|
|
764
|
-
}
|
|
1054
|
+
var platformLog2 = createLogger2("spectrum.platform");
|
|
765
1055
|
function buildInstanceActions(platformName, declared, reservedKeys, buildCtx) {
|
|
766
1056
|
const out = {};
|
|
767
1057
|
for (const key of PLATFORM_WISE_ACTION_KEYS) {
|
|
@@ -793,7 +1083,6 @@ function buildInstanceActions(platformName, declared, reservedKeys, buildCtx) {
|
|
|
793
1083
|
return out;
|
|
794
1084
|
}
|
|
795
1085
|
function createPlatformInstance(def, runtime) {
|
|
796
|
-
const isPlatformUser = (value) => typeof value === "object" && value !== null && "__platform" in value && value.__platform === def.name;
|
|
797
1086
|
const resolveUserID = async (userID) => {
|
|
798
1087
|
const resolved = await def.user.resolve({
|
|
799
1088
|
input: { userID },
|
|
@@ -806,117 +1095,112 @@ function createPlatformInstance(def, runtime) {
|
|
|
806
1095
|
__platform: def.name
|
|
807
1096
|
};
|
|
808
1097
|
};
|
|
809
|
-
const
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
1098
|
+
const providerCtx = () => ({
|
|
1099
|
+
client: runtime.client,
|
|
1100
|
+
config: runtime.config,
|
|
1101
|
+
store: runtime.store
|
|
1102
|
+
});
|
|
1103
|
+
const parseSpaceParams = (params) => params !== void 0 && def.space.params ? def.space.params.parse(params) : params;
|
|
1104
|
+
const finalizeSpace = (resolved) => {
|
|
1105
|
+
const parsedSpace = def.space.schema ? def.space.schema.parse(resolved) : resolved;
|
|
1106
|
+
const spaceRef = {
|
|
1107
|
+
...parsedSpace,
|
|
1108
|
+
id: parsedSpace.id,
|
|
1109
|
+
__platform: def.name
|
|
818
1110
|
};
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
if (args.length === 0) {
|
|
823
|
-
return { users: [], params: void 0 };
|
|
824
|
-
}
|
|
825
|
-
const [first, ...rest] = args;
|
|
826
|
-
if (Array.isArray(first)) {
|
|
827
|
-
return {
|
|
828
|
-
users: first,
|
|
829
|
-
params: rest[0]
|
|
830
|
-
};
|
|
831
|
-
}
|
|
832
|
-
if (!isPlatformUser(first)) {
|
|
833
|
-
return {
|
|
834
|
-
users: [],
|
|
835
|
-
params: first
|
|
836
|
-
};
|
|
837
|
-
}
|
|
838
|
-
const last = args.at(-1);
|
|
839
|
-
if (last !== void 0 && !isPlatformUser(last)) {
|
|
840
|
-
return {
|
|
841
|
-
users: args.slice(0, -1),
|
|
842
|
-
params: last
|
|
843
|
-
};
|
|
844
|
-
}
|
|
845
|
-
return {
|
|
846
|
-
users: args,
|
|
847
|
-
params: void 0
|
|
1111
|
+
const actionCtx = {
|
|
1112
|
+
space: spaceRef,
|
|
1113
|
+
...providerCtx()
|
|
848
1114
|
};
|
|
1115
|
+
return buildSpace({
|
|
1116
|
+
spaceRef,
|
|
1117
|
+
extras: parsedSpace,
|
|
1118
|
+
actionCtx,
|
|
1119
|
+
definition: def,
|
|
1120
|
+
client: runtime.client,
|
|
1121
|
+
config: runtime.config,
|
|
1122
|
+
store: runtime.store
|
|
1123
|
+
});
|
|
849
1124
|
};
|
|
850
1125
|
const base = {
|
|
851
1126
|
async user(userID) {
|
|
852
|
-
|
|
853
|
-
input: { userID },
|
|
854
|
-
client: runtime.client,
|
|
855
|
-
config: runtime.config,
|
|
856
|
-
store: runtime.store
|
|
857
|
-
});
|
|
858
|
-
return {
|
|
859
|
-
...resolved,
|
|
860
|
-
__platform: def.name
|
|
861
|
-
};
|
|
1127
|
+
return await resolveUserID(userID);
|
|
862
1128
|
},
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
1129
|
+
space: {
|
|
1130
|
+
create: async (users, params) => {
|
|
1131
|
+
const userList = Array.isArray(users) ? users : [users];
|
|
1132
|
+
const first = userList.length === 1 ? userList[0] : void 0;
|
|
1133
|
+
const single = typeof first === "string" ? classifyIdentifier(first) : void 0;
|
|
1134
|
+
const kind = userList.length > 1 ? "group" : single?.kind ?? "unknown";
|
|
1135
|
+
return await withSpan2(
|
|
1136
|
+
"spectrum.space.create",
|
|
1137
|
+
{
|
|
1138
|
+
"spectrum.provider": def.name,
|
|
1139
|
+
"spectrum.space.user_count": userList.length,
|
|
1140
|
+
"spectrum.space.identifier_kind": kind,
|
|
1141
|
+
"spectrum.space.identifier": kind === "unknown" ? void 0 : single?.identifier
|
|
1142
|
+
},
|
|
1143
|
+
async () => {
|
|
1144
|
+
const resolvedUsers = await Promise.all(
|
|
1145
|
+
userList.map(
|
|
1146
|
+
(u) => typeof u === "string" ? resolveUserID(u) : u
|
|
1147
|
+
)
|
|
1148
|
+
);
|
|
1149
|
+
const resolved = await def.space.create({
|
|
1150
|
+
input: { users: resolvedUsers, params: parseSpaceParams(params) },
|
|
1151
|
+
...providerCtx()
|
|
1152
|
+
});
|
|
1153
|
+
return finalizeSpace(resolved);
|
|
1154
|
+
}
|
|
1155
|
+
);
|
|
1156
|
+
},
|
|
1157
|
+
get: async (id, params) => await withSpan2(
|
|
1158
|
+
"spectrum.space.get",
|
|
867
1159
|
{
|
|
868
1160
|
"spectrum.provider": def.name,
|
|
869
|
-
"spectrum.space.
|
|
870
|
-
"spectrum.space.identifier": identifier
|
|
1161
|
+
"spectrum.space.id": id
|
|
871
1162
|
},
|
|
872
1163
|
async () => {
|
|
873
|
-
const
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
1164
|
+
const parsedParams = parseSpaceParams(params);
|
|
1165
|
+
if (def.space.get) {
|
|
1166
|
+
const resolved = await def.space.get({
|
|
1167
|
+
input: { id, params: parsedParams },
|
|
1168
|
+
...providerCtx()
|
|
1169
|
+
});
|
|
1170
|
+
return finalizeSpace(resolved);
|
|
1171
|
+
}
|
|
1172
|
+
const candidate = { id };
|
|
1173
|
+
if (def.space.schema) {
|
|
1174
|
+
const parsed = def.space.schema.safeParse(candidate);
|
|
1175
|
+
if (!parsed.success) {
|
|
1176
|
+
throw new Error(
|
|
1177
|
+
`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.`,
|
|
1178
|
+
{ cause: parsed.error }
|
|
1179
|
+
);
|
|
1180
|
+
}
|
|
878
1181
|
}
|
|
879
|
-
|
|
880
|
-
input: { users, params: parsedParams },
|
|
881
|
-
client: runtime.client,
|
|
882
|
-
config: runtime.config,
|
|
883
|
-
store: runtime.store
|
|
884
|
-
});
|
|
885
|
-
const parsedSpace = def.space.schema ? def.space.schema.parse(resolved) : resolved;
|
|
886
|
-
const spaceRef = {
|
|
887
|
-
...parsedSpace,
|
|
888
|
-
id: parsedSpace.id,
|
|
889
|
-
__platform: def.name
|
|
890
|
-
};
|
|
891
|
-
const actionCtx = {
|
|
892
|
-
space: spaceRef,
|
|
893
|
-
client: runtime.client,
|
|
894
|
-
config: runtime.config,
|
|
895
|
-
store: runtime.store
|
|
896
|
-
};
|
|
897
|
-
return buildSpace({
|
|
898
|
-
spaceRef,
|
|
899
|
-
extras: parsedSpace,
|
|
900
|
-
actionCtx,
|
|
901
|
-
definition: def,
|
|
902
|
-
client: runtime.client,
|
|
903
|
-
config: runtime.config,
|
|
904
|
-
store: runtime.store
|
|
905
|
-
});
|
|
1182
|
+
return finalizeSpace(candidate);
|
|
906
1183
|
}
|
|
907
|
-
)
|
|
1184
|
+
)
|
|
908
1185
|
}
|
|
909
1186
|
};
|
|
910
1187
|
const eventProperties = {};
|
|
911
1188
|
const customEvents = def.events ?? {};
|
|
912
1189
|
for (const eventName of Object.keys(customEvents)) {
|
|
913
|
-
const
|
|
914
|
-
if (
|
|
1190
|
+
const declared = customEvents[eventName];
|
|
1191
|
+
if (typeof declared === "function") {
|
|
1192
|
+
const producer = declared;
|
|
915
1193
|
eventProperties[eventName] = producer({
|
|
916
1194
|
client: runtime.client,
|
|
917
1195
|
config: runtime.config,
|
|
1196
|
+
projectConfig: runtime.projectConfig,
|
|
918
1197
|
store: runtime.store
|
|
919
1198
|
});
|
|
1199
|
+
continue;
|
|
1200
|
+
}
|
|
1201
|
+
const fusorEvents = runtime.subscribeEvent?.(eventName);
|
|
1202
|
+
if (fusorEvents) {
|
|
1203
|
+
eventProperties[eventName] = fusorEvents;
|
|
920
1204
|
}
|
|
921
1205
|
}
|
|
922
1206
|
let messagesIterable;
|
|
@@ -948,8 +1232,9 @@ function createPlatformInstance(def, runtime) {
|
|
|
948
1232
|
eventProperties
|
|
949
1233
|
);
|
|
950
1234
|
}
|
|
951
|
-
function definePlatform(name,
|
|
952
|
-
const
|
|
1235
|
+
function definePlatform(name, rawDef) {
|
|
1236
|
+
const def = rawDef;
|
|
1237
|
+
const fullDef = { ...def, name };
|
|
953
1238
|
const platformCache = /* @__PURE__ */ new WeakMap();
|
|
954
1239
|
const narrowSpectrum = (spectrum) => {
|
|
955
1240
|
const cached = platformCache.get(spectrum);
|
|
@@ -969,17 +1254,19 @@ function definePlatform(name, def) {
|
|
|
969
1254
|
};
|
|
970
1255
|
const narrowSpace = (input) => {
|
|
971
1256
|
if (input.__platform !== name) {
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
1257
|
+
platformLog2.warn("space platform mismatch; narrowing skipped", {
|
|
1258
|
+
expected: name,
|
|
1259
|
+
actual: input.__platform
|
|
1260
|
+
});
|
|
975
1261
|
}
|
|
976
1262
|
return input;
|
|
977
1263
|
};
|
|
978
1264
|
const narrowMessage = (input) => {
|
|
979
1265
|
if (input.platform !== name) {
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
1266
|
+
platformLog2.warn("message platform mismatch; narrowing skipped", {
|
|
1267
|
+
expected: name,
|
|
1268
|
+
actual: input.platform
|
|
1269
|
+
});
|
|
983
1270
|
}
|
|
984
1271
|
return input;
|
|
985
1272
|
};
|
|
@@ -1022,26 +1309,24 @@ function definePlatform(name, def) {
|
|
|
1022
1309
|
}
|
|
1023
1310
|
return narrower;
|
|
1024
1311
|
}
|
|
1025
|
-
function defineFusorPlatform(name, def) {
|
|
1026
|
-
return definePlatform(
|
|
1027
|
-
name,
|
|
1028
|
-
def
|
|
1029
|
-
);
|
|
1030
|
-
}
|
|
1031
1312
|
|
|
1032
1313
|
export {
|
|
1033
1314
|
photoActionSchema,
|
|
1034
1315
|
buildPhotoAction,
|
|
1035
1316
|
avatar,
|
|
1036
1317
|
edit,
|
|
1318
|
+
markdownSchema,
|
|
1319
|
+
asMarkdown,
|
|
1320
|
+
markdown,
|
|
1037
1321
|
rename,
|
|
1038
1322
|
reply,
|
|
1039
1323
|
typing,
|
|
1324
|
+
unsend,
|
|
1040
1325
|
UnsupportedError,
|
|
1326
|
+
renderInlineTokens,
|
|
1041
1327
|
contentAttrs,
|
|
1042
1328
|
senderAttrs,
|
|
1043
1329
|
wrapProviderMessage,
|
|
1044
1330
|
buildSpace,
|
|
1045
|
-
definePlatform
|
|
1046
|
-
defineFusorPlatform
|
|
1331
|
+
definePlatform
|
|
1047
1332
|
};
|