spectrum-ts 1.13.1 → 1.15.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/{chunk-4TXLNBGE.js → chunk-2B76JFHX.js} +23 -2
- package/dist/{chunk-FPYXHZZA.js → chunk-BGV4SFVJ.js} +116 -48
- package/dist/{chunk-L3VXHUVY.js → chunk-J2ANGYYW.js} +1 -1
- package/dist/{chunk-UFJZIZDO.js → chunk-JBMQ5GEW.js} +6 -19
- package/dist/{chunk-NBXNTQIQ.js → chunk-TSJKUFNE.js} +3 -3
- package/dist/{chunk-E7AXYNOZ.js → chunk-VOAKCXBF.js} +2 -1
- package/dist/{chunk-VQOW7X7U.js → chunk-YEQPATHB.js} +3 -2
- package/dist/{chunk-YDHES53X.js → chunk-YFCDKZ6W.js} +126 -27
- package/dist/index.d.ts +23 -14
- package/dist/index.js +13 -5
- package/dist/photo-content-Deq7T2tO.d.ts +33 -0
- package/dist/providers/imessage/index.d.ts +40 -3
- package/dist/providers/imessage/index.js +4 -4
- package/dist/providers/index.d.ts +2 -2
- package/dist/providers/index.js +8 -8
- package/dist/providers/slack/index.d.ts +2 -2
- package/dist/providers/slack/index.js +2 -2
- package/dist/providers/terminal/index.d.ts +28 -22
- package/dist/providers/terminal/index.js +4 -4
- package/dist/providers/whatsapp-business/index.d.ts +2 -2
- package/dist/providers/whatsapp-business/index.js +3 -3
- package/dist/{types-BzW4gInA.d.ts → types-kk0L8kFG.d.ts} +100 -23
- package/package.json +2 -2
- package/dist/photo-content-BQF42prd.d.ts +0 -13
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
bufferToStream,
|
|
3
|
+
fetchUrlBytes,
|
|
3
4
|
readSchema,
|
|
4
5
|
streamSchema
|
|
5
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-YFCDKZ6W.js";
|
|
6
7
|
|
|
7
8
|
// src/content/voice.ts
|
|
8
9
|
import { createReadStream } from "fs";
|
|
@@ -26,11 +27,23 @@ var resolveVoiceName = (input, name) => {
|
|
|
26
27
|
if (name) {
|
|
27
28
|
return name;
|
|
28
29
|
}
|
|
30
|
+
if (input instanceof URL) {
|
|
31
|
+
return basename(input.pathname) || void 0;
|
|
32
|
+
}
|
|
29
33
|
if (typeof input === "string") {
|
|
30
34
|
return basename(input);
|
|
31
35
|
}
|
|
32
36
|
return;
|
|
33
37
|
};
|
|
38
|
+
var resolveVoiceMimeHint = (input, name) => {
|
|
39
|
+
if (input instanceof URL) {
|
|
40
|
+
return basename(input.pathname) || void 0;
|
|
41
|
+
}
|
|
42
|
+
if (typeof input === "string") {
|
|
43
|
+
return basename(input);
|
|
44
|
+
}
|
|
45
|
+
return name;
|
|
46
|
+
};
|
|
34
47
|
var resolveVoiceMimeType = (name, mimeType) => {
|
|
35
48
|
if (mimeType) {
|
|
36
49
|
if (!AUDIO_MIME_PATTERN.test(mimeType)) {
|
|
@@ -79,8 +92,16 @@ function voice(input, options) {
|
|
|
79
92
|
return {
|
|
80
93
|
build: async () => {
|
|
81
94
|
const name = resolveVoiceName(input, options?.name);
|
|
82
|
-
const mimeHint =
|
|
95
|
+
const mimeHint = resolveVoiceMimeHint(input, name);
|
|
83
96
|
const mimeType = resolveVoiceMimeType(mimeHint, options?.mimeType);
|
|
97
|
+
if (input instanceof URL) {
|
|
98
|
+
return asVoice({
|
|
99
|
+
name,
|
|
100
|
+
mimeType,
|
|
101
|
+
duration: options?.duration,
|
|
102
|
+
read: async () => (await fetchUrlBytes(input)).data
|
|
103
|
+
});
|
|
104
|
+
}
|
|
84
105
|
if (typeof input === "string") {
|
|
85
106
|
const stats = await stat(input);
|
|
86
107
|
if (!stats.isFile()) {
|
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
asGroup,
|
|
3
3
|
asRichlink,
|
|
4
4
|
groupSchema
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-JBMQ5GEW.js";
|
|
6
6
|
import {
|
|
7
7
|
asPoll,
|
|
8
8
|
asPollOption
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
asContact,
|
|
17
17
|
fromVCard,
|
|
18
18
|
toVCard
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-J2ANGYYW.js";
|
|
20
20
|
import {
|
|
21
21
|
UnsupportedError,
|
|
22
22
|
asAttachment,
|
|
@@ -29,11 +29,12 @@ import {
|
|
|
29
29
|
reactionSchema,
|
|
30
30
|
text,
|
|
31
31
|
textSchema
|
|
32
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-YFCDKZ6W.js";
|
|
33
33
|
|
|
34
34
|
// src/providers/imessage/index.ts
|
|
35
35
|
import { createClient as createClient2, MessageEffect as MessageEffect2 } from "@photon-ai/advanced-imessage";
|
|
36
36
|
import { IMessageSDK as IMessageSDK2 } from "@photon-ai/imessage-kit";
|
|
37
|
+
import { withSpan } from "@photon-ai/otel";
|
|
37
38
|
|
|
38
39
|
// src/providers/imessage/content/background.ts
|
|
39
40
|
import z from "zod";
|
|
@@ -322,6 +323,7 @@ var readLocalAttachment = async (att) => {
|
|
|
322
323
|
var toAttachmentContent = (att) => {
|
|
323
324
|
const { localPath } = att;
|
|
324
325
|
return asAttachment({
|
|
326
|
+
id: att.id,
|
|
325
327
|
name: att.fileName ?? DEFAULT_ATTACHMENT_NAME,
|
|
326
328
|
mimeType: att.mimeType,
|
|
327
329
|
size: att.sizeBytes,
|
|
@@ -546,7 +548,7 @@ var setBackground = async (remote, spaceId, content) => {
|
|
|
546
548
|
|
|
547
549
|
// src/providers/imessage/remote/inbound.ts
|
|
548
550
|
import {
|
|
549
|
-
NotFoundError
|
|
551
|
+
NotFoundError as NotFoundError2
|
|
550
552
|
} from "@photon-ai/advanced-imessage";
|
|
551
553
|
|
|
552
554
|
// src/providers/imessage/cache.ts
|
|
@@ -620,45 +622,10 @@ var getPollCache = (owner) => {
|
|
|
620
622
|
return cache;
|
|
621
623
|
};
|
|
622
624
|
|
|
623
|
-
// src/providers/imessage/remote/
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
var resolveChatGuid = (message, hint) => {
|
|
628
|
-
if (hint) {
|
|
629
|
-
return hint;
|
|
630
|
-
}
|
|
631
|
-
const first = message.chatGuids?.[0];
|
|
632
|
-
return first ?? "";
|
|
633
|
-
};
|
|
634
|
-
var resolveSenderId = (message) => message.sender?.address ?? "";
|
|
635
|
-
var isIMessageMessage = (value) => {
|
|
636
|
-
if (typeof value !== "object" || value === null) {
|
|
637
|
-
return false;
|
|
638
|
-
}
|
|
639
|
-
const record = value;
|
|
640
|
-
return typeof record.id === "string" && record.id.length > 0 && typeof record.content === "object" && record.content !== null && typeof record.sender === "object" && record.sender !== null && typeof record.space === "object" && record.space !== null;
|
|
641
|
-
};
|
|
642
|
-
var asProviderGroup = (items) => groupSchema.parse({ type: "group", items });
|
|
643
|
-
var buildMessageBase = (message, chatGuidHint, timestamp, phone) => {
|
|
644
|
-
const chat = resolveChatGuid(message, chatGuidHint);
|
|
645
|
-
return {
|
|
646
|
-
sender: { id: resolveSenderId(message) },
|
|
647
|
-
space: {
|
|
648
|
-
id: chat,
|
|
649
|
-
type: chat.includes(";+;") ? "group" : "dm",
|
|
650
|
-
phone
|
|
651
|
-
},
|
|
652
|
-
timestamp
|
|
653
|
-
};
|
|
654
|
-
};
|
|
655
|
-
var toAttachmentContent2 = (client, info) => asAttachment({
|
|
656
|
-
name: info.fileName,
|
|
657
|
-
mimeType: info.mimeType,
|
|
658
|
-
size: info.totalBytes,
|
|
659
|
-
read: async () => await downloadPrimaryAttachment(client, info.guid),
|
|
660
|
-
stream: async () => downloadPrimaryAttachmentStream(client, info.guid)
|
|
661
|
-
});
|
|
625
|
+
// src/providers/imessage/remote/attachments.ts
|
|
626
|
+
import {
|
|
627
|
+
NotFoundError
|
|
628
|
+
} from "@photon-ai/advanced-imessage";
|
|
662
629
|
var downloadPrimaryAttachmentStream = (client, attachmentGuid) => {
|
|
663
630
|
const frames = client.attachments.downloadStream(attachmentGuid);
|
|
664
631
|
const iterator = frames[Symbol.asyncIterator]();
|
|
@@ -713,6 +680,66 @@ var downloadPrimaryAttachment = async (client, attachmentGuid) => {
|
|
|
713
680
|
}
|
|
714
681
|
return Buffer.concat(chunks);
|
|
715
682
|
};
|
|
683
|
+
var getRemoteAttachment = async (client, guid) => {
|
|
684
|
+
let info;
|
|
685
|
+
try {
|
|
686
|
+
info = await client.attachments.get(guid);
|
|
687
|
+
} catch (err) {
|
|
688
|
+
if (err instanceof NotFoundError) {
|
|
689
|
+
return;
|
|
690
|
+
}
|
|
691
|
+
throw err;
|
|
692
|
+
}
|
|
693
|
+
return asAttachment({
|
|
694
|
+
id: info.guid,
|
|
695
|
+
name: info.fileName,
|
|
696
|
+
mimeType: info.mimeType,
|
|
697
|
+
size: info.totalBytes,
|
|
698
|
+
read: () => downloadPrimaryAttachment(client, info.guid),
|
|
699
|
+
stream: async () => downloadPrimaryAttachmentStream(client, info.guid)
|
|
700
|
+
});
|
|
701
|
+
};
|
|
702
|
+
|
|
703
|
+
// src/providers/imessage/remote/inbound.ts
|
|
704
|
+
var URL_BALLOON_BUNDLE_ID = "com.apple.messages.URLBalloonProvider";
|
|
705
|
+
var getBalloonBundleId = (message) => message.content.balloonBundleId;
|
|
706
|
+
var messageAttachments = (message) => message.content.attachments;
|
|
707
|
+
var resolveChatGuid = (message, hint) => {
|
|
708
|
+
if (hint) {
|
|
709
|
+
return hint;
|
|
710
|
+
}
|
|
711
|
+
const first = message.chatGuids?.[0];
|
|
712
|
+
return first ?? "";
|
|
713
|
+
};
|
|
714
|
+
var resolveSenderId = (message) => message.sender?.address ?? "";
|
|
715
|
+
var isIMessageMessage = (value) => {
|
|
716
|
+
if (typeof value !== "object" || value === null) {
|
|
717
|
+
return false;
|
|
718
|
+
}
|
|
719
|
+
const record = value;
|
|
720
|
+
return typeof record.id === "string" && record.id.length > 0 && typeof record.content === "object" && record.content !== null && typeof record.sender === "object" && record.sender !== null && typeof record.space === "object" && record.space !== null;
|
|
721
|
+
};
|
|
722
|
+
var asProviderGroup = (items) => groupSchema.parse({ type: "group", items });
|
|
723
|
+
var buildMessageBase = (message, chatGuidHint, timestamp, phone) => {
|
|
724
|
+
const chat = resolveChatGuid(message, chatGuidHint);
|
|
725
|
+
return {
|
|
726
|
+
sender: { id: resolveSenderId(message) },
|
|
727
|
+
space: {
|
|
728
|
+
id: chat,
|
|
729
|
+
type: chat.includes(";+;") ? "group" : "dm",
|
|
730
|
+
phone
|
|
731
|
+
},
|
|
732
|
+
timestamp
|
|
733
|
+
};
|
|
734
|
+
};
|
|
735
|
+
var toAttachmentContent2 = (client, info) => asAttachment({
|
|
736
|
+
id: info.guid,
|
|
737
|
+
name: info.fileName,
|
|
738
|
+
mimeType: info.mimeType,
|
|
739
|
+
size: info.totalBytes,
|
|
740
|
+
read: async () => await downloadPrimaryAttachment(client, info.guid),
|
|
741
|
+
stream: async () => downloadPrimaryAttachmentStream(client, info.guid)
|
|
742
|
+
});
|
|
716
743
|
var toVCardContent2 = async (client, info) => {
|
|
717
744
|
try {
|
|
718
745
|
const buf = await downloadPrimaryAttachment(client, info.guid);
|
|
@@ -896,7 +923,7 @@ var getMessage3 = async (remote, spaceId, msgId, phone) => {
|
|
|
896
923
|
const item = parent.content.items[childRef.partIndex];
|
|
897
924
|
return isIMessageMessage(item) ? item : void 0;
|
|
898
925
|
} catch (err) {
|
|
899
|
-
if (err instanceof
|
|
926
|
+
if (err instanceof NotFoundError2) {
|
|
900
927
|
return;
|
|
901
928
|
}
|
|
902
929
|
throw err;
|
|
@@ -916,7 +943,7 @@ var getMessage3 = async (remote, spaceId, msgId, phone) => {
|
|
|
916
943
|
cacheMessage(cache, rebuilt);
|
|
917
944
|
return rebuilt;
|
|
918
945
|
} catch (err) {
|
|
919
|
-
if (err instanceof
|
|
946
|
+
if (err instanceof NotFoundError2) {
|
|
920
947
|
return;
|
|
921
948
|
}
|
|
922
949
|
throw err;
|
|
@@ -1375,7 +1402,7 @@ var editMessage = async (remote, spaceId, msgId, content) => {
|
|
|
1375
1402
|
import {
|
|
1376
1403
|
AuthenticationError,
|
|
1377
1404
|
IMessageError,
|
|
1378
|
-
NotFoundError as
|
|
1405
|
+
NotFoundError as NotFoundError3,
|
|
1379
1406
|
ValidationError
|
|
1380
1407
|
} from "@photon-ai/advanced-imessage";
|
|
1381
1408
|
|
|
@@ -1751,7 +1778,7 @@ var toPollDeltaMessages = async (client, pollCache, event, phone) => {
|
|
|
1751
1778
|
|
|
1752
1779
|
// src/providers/imessage/remote/stream.ts
|
|
1753
1780
|
var isRetryableIMessageStreamError = (error) => {
|
|
1754
|
-
if (error instanceof AuthenticationError || error instanceof
|
|
1781
|
+
if (error instanceof AuthenticationError || error instanceof NotFoundError3 || error instanceof ValidationError) {
|
|
1755
1782
|
return false;
|
|
1756
1783
|
}
|
|
1757
1784
|
if (error instanceof IMessageError) {
|
|
@@ -2202,12 +2229,53 @@ var imessage = definePlatform("iMessage", {
|
|
|
2202
2229
|
return await send4(remote, space.id, content);
|
|
2203
2230
|
},
|
|
2204
2231
|
actions: {
|
|
2205
|
-
getMessage: async ({ space, messageId
|
|
2232
|
+
getMessage: async ({ client }, space, messageId) => {
|
|
2206
2233
|
if (isLocal(client)) {
|
|
2207
2234
|
return getMessage2(client, messageId);
|
|
2208
2235
|
}
|
|
2209
2236
|
const remote = clientForPhone(client, space.phone);
|
|
2210
2237
|
return getMessage4(remote, space.id, messageId, space.phone);
|
|
2238
|
+
},
|
|
2239
|
+
// Fetch an attachment by GUID. Returns a spectrum `Attachment` whose
|
|
2240
|
+
// `.read()` / `.stream()` lazily download the bytes — calling both
|
|
2241
|
+
// issues two independent gRPC downloads, so cache `.read()` if you
|
|
2242
|
+
// need the bytes more than once. Returns `undefined` for unknown
|
|
2243
|
+
// GUIDs. Local-mode iMessage is not supported.
|
|
2244
|
+
getAttachment: async ({ client }, guid, phone) => {
|
|
2245
|
+
if (isLocal(client)) {
|
|
2246
|
+
throw UnsupportedError.action(
|
|
2247
|
+
"getAttachment",
|
|
2248
|
+
"iMessage (local mode)",
|
|
2249
|
+
"fetching attachments by GUID requires remote iMessage"
|
|
2250
|
+
);
|
|
2251
|
+
}
|
|
2252
|
+
if (client.length === 0) {
|
|
2253
|
+
throw new Error("No iMessage clients configured");
|
|
2254
|
+
}
|
|
2255
|
+
const routedPhone = (() => {
|
|
2256
|
+
if (isSharedMode(client)) {
|
|
2257
|
+
return SHARED_PHONE;
|
|
2258
|
+
}
|
|
2259
|
+
if (phone) {
|
|
2260
|
+
return phone;
|
|
2261
|
+
}
|
|
2262
|
+
if (client.length === 1) {
|
|
2263
|
+
return client[0].phone;
|
|
2264
|
+
}
|
|
2265
|
+
throw new Error(
|
|
2266
|
+
`imessage.getAttachment requires a phone in multi-phone mode. Available: ${availablePhones(client).join(", ")}`
|
|
2267
|
+
);
|
|
2268
|
+
})();
|
|
2269
|
+
const remote = clientForPhone(client, routedPhone);
|
|
2270
|
+
return withSpan(
|
|
2271
|
+
"spectrum.imessage.getAttachment",
|
|
2272
|
+
{
|
|
2273
|
+
"spectrum.provider": "iMessage",
|
|
2274
|
+
"spectrum.imessage.attachment.guid": guid,
|
|
2275
|
+
"spectrum.imessage.phone": routedPhone
|
|
2276
|
+
},
|
|
2277
|
+
() => getRemoteAttachment(remote, guid)
|
|
2278
|
+
);
|
|
2211
2279
|
}
|
|
2212
2280
|
}
|
|
2213
2281
|
});
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
bufferToStream,
|
|
3
|
+
fetchUrlBytes,
|
|
3
4
|
readSchema,
|
|
4
5
|
resolveContents,
|
|
5
6
|
streamSchema
|
|
6
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-YFCDKZ6W.js";
|
|
7
8
|
|
|
8
9
|
// src/content/group.ts
|
|
9
10
|
import z from "zod";
|
|
@@ -82,24 +83,10 @@ var fetchLinkMetadata = async (url) => {
|
|
|
82
83
|
return {};
|
|
83
84
|
}
|
|
84
85
|
};
|
|
85
|
-
var fetchImage =
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
const res = await fetch(url, {
|
|
90
|
-
signal: controller.signal,
|
|
91
|
-
headers: { "User-Agent": USER_AGENT }
|
|
92
|
-
});
|
|
93
|
-
if (!res.ok) {
|
|
94
|
-
throw new Error(`image fetch ${url} returned ${res.status}`);
|
|
95
|
-
}
|
|
96
|
-
const data = Buffer.from(await res.arrayBuffer());
|
|
97
|
-
const mimeType = res.headers.get("content-type") ?? void 0;
|
|
98
|
-
return { data, mimeType };
|
|
99
|
-
} finally {
|
|
100
|
-
clearTimeout(timer);
|
|
101
|
-
}
|
|
102
|
-
};
|
|
86
|
+
var fetchImage = (url) => fetchUrlBytes(new URL(url), {
|
|
87
|
+
timeoutMs: DEFAULT_TIMEOUT_MS,
|
|
88
|
+
headers: { "User-Agent": USER_AGENT }
|
|
89
|
+
});
|
|
103
90
|
|
|
104
91
|
// src/content/richlink.ts
|
|
105
92
|
var richlinkCoverSchema = z2.object({
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
2
|
asVoice
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-2B76JFHX.js";
|
|
4
4
|
import {
|
|
5
5
|
asContact,
|
|
6
6
|
fromVCard,
|
|
7
7
|
toVCard
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-J2ANGYYW.js";
|
|
9
9
|
import {
|
|
10
10
|
UnsupportedError,
|
|
11
11
|
asAttachment,
|
|
12
12
|
asCustom,
|
|
13
13
|
definePlatform,
|
|
14
14
|
reactionSchema
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-YFCDKZ6W.js";
|
|
16
16
|
|
|
17
17
|
// src/providers/terminal/index.ts
|
|
18
18
|
import { spawn } from "child_process";
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
asReaction,
|
|
11
11
|
asText,
|
|
12
12
|
definePlatform
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-YFCDKZ6W.js";
|
|
14
14
|
|
|
15
15
|
// src/providers/slack/index.ts
|
|
16
16
|
import { createClient as createClient2, staticTokens } from "@photon-ai/slack";
|
|
@@ -169,6 +169,7 @@ var tsToDate = (ts) => {
|
|
|
169
169
|
return new Date(seconds * 1e3);
|
|
170
170
|
};
|
|
171
171
|
var lazySlackFile = (client, teamId, file) => asAttachment({
|
|
172
|
+
id: file.id,
|
|
172
173
|
name: file.name,
|
|
173
174
|
mimeType: file.mimeType,
|
|
174
175
|
size: file.size,
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
} from "./chunk-YKWKZ2PZ.js";
|
|
9
9
|
import {
|
|
10
10
|
asContact
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-J2ANGYYW.js";
|
|
12
12
|
import {
|
|
13
13
|
UnsupportedError,
|
|
14
14
|
asAttachment,
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
asReaction,
|
|
17
17
|
asText,
|
|
18
18
|
definePlatform
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-YFCDKZ6W.js";
|
|
20
20
|
|
|
21
21
|
// src/providers/whatsapp-business/index.ts
|
|
22
22
|
import { createClient as createClient2 } from "@photon-ai/whatsapp-business";
|
|
@@ -508,6 +508,7 @@ var fetchMedia = async (client, mediaId) => {
|
|
|
508
508
|
return response;
|
|
509
509
|
};
|
|
510
510
|
var lazyMedia = (client, media) => asAttachment({
|
|
511
|
+
id: media.id,
|
|
511
512
|
name: media.filename ?? `media-${media.id}`,
|
|
512
513
|
mimeType: media.mimeType,
|
|
513
514
|
read: async () => Buffer.from(await (await fetchMedia(client, media.id)).arrayBuffer()),
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// src/content/attachment.ts
|
|
2
|
+
import { randomUUID } from "crypto";
|
|
2
3
|
import { createReadStream } from "fs";
|
|
3
4
|
import { readFile, stat } from "fs/promises";
|
|
4
5
|
import { basename } from "path";
|
|
@@ -22,18 +23,52 @@ var bufferToStream = (buf) => new ReadableStream({
|
|
|
22
23
|
controller.close();
|
|
23
24
|
}
|
|
24
25
|
});
|
|
26
|
+
var DEFAULT_FETCH_TIMEOUT_MS = 1e4;
|
|
27
|
+
var fetchUrlBytes = async (url, options) => {
|
|
28
|
+
const controller = new AbortController();
|
|
29
|
+
const timer = setTimeout(
|
|
30
|
+
() => controller.abort(),
|
|
31
|
+
options?.timeoutMs ?? DEFAULT_FETCH_TIMEOUT_MS
|
|
32
|
+
);
|
|
33
|
+
try {
|
|
34
|
+
const res = await fetch(url, {
|
|
35
|
+
signal: controller.signal,
|
|
36
|
+
headers: options?.headers
|
|
37
|
+
});
|
|
38
|
+
if (!res.ok) {
|
|
39
|
+
throw new Error(`URL fetch ${url.toString()} returned ${res.status}`);
|
|
40
|
+
}
|
|
41
|
+
const data = Buffer.from(await res.arrayBuffer());
|
|
42
|
+
const mimeType = res.headers.get("content-type") ?? void 0;
|
|
43
|
+
return { data, mimeType };
|
|
44
|
+
} finally {
|
|
45
|
+
clearTimeout(timer);
|
|
46
|
+
}
|
|
47
|
+
};
|
|
25
48
|
|
|
26
49
|
// src/content/attachment.ts
|
|
27
50
|
var DEFAULT_ATTACHMENT_NAME = "attachment";
|
|
28
51
|
var attachmentSchema = z2.object({
|
|
29
52
|
type: z2.literal("attachment"),
|
|
53
|
+
id: z2.string().nonempty(),
|
|
30
54
|
name: z2.string().nonempty(),
|
|
31
55
|
mimeType: z2.string().nonempty(),
|
|
32
56
|
size: z2.number().int().nonnegative().optional(),
|
|
33
57
|
read: readSchema,
|
|
34
58
|
stream: streamSchema
|
|
35
59
|
});
|
|
36
|
-
var resolveAttachmentName = (input, name) =>
|
|
60
|
+
var resolveAttachmentName = (input, name) => {
|
|
61
|
+
if (name) {
|
|
62
|
+
return name;
|
|
63
|
+
}
|
|
64
|
+
if (input instanceof URL) {
|
|
65
|
+
return basename(input.pathname) || DEFAULT_ATTACHMENT_NAME;
|
|
66
|
+
}
|
|
67
|
+
if (typeof input === "string") {
|
|
68
|
+
return basename(input);
|
|
69
|
+
}
|
|
70
|
+
return DEFAULT_ATTACHMENT_NAME;
|
|
71
|
+
};
|
|
37
72
|
var resolveAttachmentMimeType = (name, mimeType) => {
|
|
38
73
|
if (mimeType) {
|
|
39
74
|
return mimeType;
|
|
@@ -58,6 +93,7 @@ var asAttachment = (input) => {
|
|
|
58
93
|
const stream = input.stream ?? (async () => bufferToStream(await read()));
|
|
59
94
|
return attachmentSchema.parse({
|
|
60
95
|
type: "attachment",
|
|
96
|
+
id: input.id ?? randomUUID(),
|
|
61
97
|
name: input.name,
|
|
62
98
|
mimeType: input.mimeType,
|
|
63
99
|
size: input.size,
|
|
@@ -68,11 +104,21 @@ var asAttachment = (input) => {
|
|
|
68
104
|
function attachment(input, options) {
|
|
69
105
|
return {
|
|
70
106
|
build: async () => {
|
|
107
|
+
const id = options?.id;
|
|
71
108
|
const name = resolveAttachmentName(input, options?.name);
|
|
72
109
|
const mimeType = resolveAttachmentMimeType(name, options?.mimeType);
|
|
110
|
+
if (input instanceof URL) {
|
|
111
|
+
return asAttachment({
|
|
112
|
+
id,
|
|
113
|
+
name,
|
|
114
|
+
mimeType,
|
|
115
|
+
read: async () => (await fetchUrlBytes(input)).data
|
|
116
|
+
});
|
|
117
|
+
}
|
|
73
118
|
if (typeof input === "string") {
|
|
74
119
|
const stats = await stat(input);
|
|
75
120
|
return asAttachment({
|
|
121
|
+
id,
|
|
76
122
|
name,
|
|
77
123
|
mimeType,
|
|
78
124
|
size: stats.size,
|
|
@@ -83,6 +129,7 @@ function attachment(input, options) {
|
|
|
83
129
|
});
|
|
84
130
|
}
|
|
85
131
|
return asAttachment({
|
|
132
|
+
id,
|
|
86
133
|
name,
|
|
87
134
|
mimeType,
|
|
88
135
|
size: input.byteLength,
|
|
@@ -114,7 +161,12 @@ var resolveMimeType = (input, mimeType, contentLabel) => {
|
|
|
114
161
|
if (mimeType) {
|
|
115
162
|
return mimeType;
|
|
116
163
|
}
|
|
117
|
-
if (
|
|
164
|
+
if (input instanceof URL) {
|
|
165
|
+
const resolved = lookupMimeType2(basename2(input.pathname));
|
|
166
|
+
if (resolved) {
|
|
167
|
+
return resolved;
|
|
168
|
+
}
|
|
169
|
+
} else if (typeof input === "string") {
|
|
118
170
|
const resolved = lookupMimeType2(basename2(input));
|
|
119
171
|
if (resolved) {
|
|
120
172
|
return resolved;
|
|
@@ -140,7 +192,9 @@ var buildPhotoAction = (input, options, contentLabel) => {
|
|
|
140
192
|
}
|
|
141
193
|
const mimeType = resolveMimeType(input, options?.mimeType, contentLabel);
|
|
142
194
|
let read;
|
|
143
|
-
if (
|
|
195
|
+
if (input instanceof URL) {
|
|
196
|
+
read = cachedRead(async () => (await fetchUrlBytes(input)).data);
|
|
197
|
+
} else if (typeof input === "string") {
|
|
144
198
|
read = cachedRead(() => readFile2(input));
|
|
145
199
|
} else {
|
|
146
200
|
const snapshot = Buffer.from(input);
|
|
@@ -502,6 +556,7 @@ var RESERVED_SPACE_KEYS = /* @__PURE__ */ new Set([
|
|
|
502
556
|
"stopTyping",
|
|
503
557
|
"responding"
|
|
504
558
|
]);
|
|
559
|
+
var PLATFORM_WISE_ACTION_KEYS = /* @__PURE__ */ new Set(["getMessage"]);
|
|
505
560
|
var RESERVED_MESSAGE_KEYS = /* @__PURE__ */ new Set([
|
|
506
561
|
"content",
|
|
507
562
|
"direction",
|
|
@@ -514,8 +569,17 @@ var RESERVED_MESSAGE_KEYS = /* @__PURE__ */ new Set([
|
|
|
514
569
|
"space",
|
|
515
570
|
"timestamp"
|
|
516
571
|
]);
|
|
572
|
+
var scopeLabel = (scope) => {
|
|
573
|
+
if (scope === "space") {
|
|
574
|
+
return "Space";
|
|
575
|
+
}
|
|
576
|
+
if (scope === "message") {
|
|
577
|
+
return "Message";
|
|
578
|
+
}
|
|
579
|
+
return "PlatformInstance";
|
|
580
|
+
};
|
|
517
581
|
var warnReservedAction = (scope, name, platform) => {
|
|
518
|
-
const body = `[spectrum-ts] ${platform} declared ${scope} action "${name}" which collides with a reserved ${scope
|
|
582
|
+
const body = `[spectrum-ts] ${platform} declared ${scope} action "${name}" which collides with a reserved ${scopeLabel(scope)} key; skipping.`;
|
|
519
583
|
console.warn(
|
|
520
584
|
supportsAnsiColor() ? `${ANSI_YELLOW}${body}${ANSI_RESET}` : body
|
|
521
585
|
);
|
|
@@ -718,11 +782,7 @@ function buildSpace(params) {
|
|
|
718
782
|
async function getMessageImpl(id) {
|
|
719
783
|
const getMessage = definition.actions?.getMessage;
|
|
720
784
|
if (!getMessage) {
|
|
721
|
-
|
|
722
|
-
UnsupportedError.action("getMessage", definition.name),
|
|
723
|
-
definition.name
|
|
724
|
-
);
|
|
725
|
-
return;
|
|
785
|
+
throw UnsupportedError.action("getMessage", definition.name);
|
|
726
786
|
}
|
|
727
787
|
return withSpan(
|
|
728
788
|
"spectrum.message.get",
|
|
@@ -732,22 +792,11 @@ function buildSpace(params) {
|
|
|
732
792
|
"spectrum.message.id": id
|
|
733
793
|
},
|
|
734
794
|
async () => {
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
client,
|
|
741
|
-
config,
|
|
742
|
-
store
|
|
743
|
-
});
|
|
744
|
-
} catch (err) {
|
|
745
|
-
if (err instanceof UnsupportedError) {
|
|
746
|
-
warnUnsupported(err, definition.name);
|
|
747
|
-
return;
|
|
748
|
-
}
|
|
749
|
-
throw err;
|
|
750
|
-
}
|
|
795
|
+
const raw = await getMessage(
|
|
796
|
+
{ client, config, store },
|
|
797
|
+
spaceRef,
|
|
798
|
+
id
|
|
799
|
+
);
|
|
751
800
|
if (!raw) {
|
|
752
801
|
return;
|
|
753
802
|
}
|
|
@@ -785,7 +834,7 @@ function buildSpace(params) {
|
|
|
785
834
|
await space.send(rename(displayName));
|
|
786
835
|
},
|
|
787
836
|
avatar: (async (input, options) => {
|
|
788
|
-
if (typeof input === "string") {
|
|
837
|
+
if (typeof input === "string" || input instanceof URL) {
|
|
789
838
|
await space.send(avatar(input, options));
|
|
790
839
|
return;
|
|
791
840
|
}
|
|
@@ -905,6 +954,36 @@ function classifySpaceIdentifier(args) {
|
|
|
905
954
|
}
|
|
906
955
|
return { kind, identifier };
|
|
907
956
|
}
|
|
957
|
+
function buildInstanceActions(platformName, declared, reservedKeys, buildCtx) {
|
|
958
|
+
const out = {};
|
|
959
|
+
for (const key of PLATFORM_WISE_ACTION_KEYS) {
|
|
960
|
+
const override = declared?.[key];
|
|
961
|
+
if (override && typeof override === "function") {
|
|
962
|
+
out[key] = (...args) => override(buildCtx(), ...args);
|
|
963
|
+
} else {
|
|
964
|
+
out[key] = () => {
|
|
965
|
+
throw UnsupportedError.action(key, platformName);
|
|
966
|
+
};
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
if (!declared) {
|
|
970
|
+
return out;
|
|
971
|
+
}
|
|
972
|
+
for (const [name, factory] of Object.entries(declared)) {
|
|
973
|
+
if (PLATFORM_WISE_ACTION_KEYS.has(name)) {
|
|
974
|
+
continue;
|
|
975
|
+
}
|
|
976
|
+
if (reservedKeys.has(name)) {
|
|
977
|
+
warnReservedAction("instance", name, platformName);
|
|
978
|
+
continue;
|
|
979
|
+
}
|
|
980
|
+
if (typeof factory !== "function") {
|
|
981
|
+
continue;
|
|
982
|
+
}
|
|
983
|
+
out[name] = (...args) => factory(buildCtx(), ...args);
|
|
984
|
+
}
|
|
985
|
+
return out;
|
|
986
|
+
}
|
|
908
987
|
function createPlatformInstance(def, runtime) {
|
|
909
988
|
const isPlatformUser = (value) => typeof value === "object" && value !== null && "__platform" in value && value.__platform === def.name;
|
|
910
989
|
const resolveUserID = async (userID) => {
|
|
@@ -1040,7 +1119,26 @@ function createPlatformInstance(def, runtime) {
|
|
|
1040
1119
|
return messagesIterable;
|
|
1041
1120
|
}
|
|
1042
1121
|
});
|
|
1043
|
-
|
|
1122
|
+
const instanceActions = buildInstanceActions(
|
|
1123
|
+
def.name,
|
|
1124
|
+
def.actions,
|
|
1125
|
+
/* @__PURE__ */ new Set([
|
|
1126
|
+
"user",
|
|
1127
|
+
"space",
|
|
1128
|
+
"messages",
|
|
1129
|
+
...Object.keys(customEvents)
|
|
1130
|
+
]),
|
|
1131
|
+
() => ({
|
|
1132
|
+
client: runtime.client,
|
|
1133
|
+
config: runtime.config,
|
|
1134
|
+
store: runtime.store
|
|
1135
|
+
})
|
|
1136
|
+
);
|
|
1137
|
+
return Object.assign(
|
|
1138
|
+
base,
|
|
1139
|
+
instanceActions,
|
|
1140
|
+
eventProperties
|
|
1141
|
+
);
|
|
1044
1142
|
}
|
|
1045
1143
|
function definePlatform(name, def) {
|
|
1046
1144
|
const fullDef = { name, ...def };
|
|
@@ -1121,6 +1219,7 @@ export {
|
|
|
1121
1219
|
readSchema,
|
|
1122
1220
|
streamSchema,
|
|
1123
1221
|
bufferToStream,
|
|
1222
|
+
fetchUrlBytes,
|
|
1124
1223
|
attachmentSchema,
|
|
1125
1224
|
asAttachment,
|
|
1126
1225
|
attachment,
|