spectrum-ts 0.9.0 → 1.0.1
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-2Y5GBI6W.js +129 -0
- package/dist/chunk-7Q7KJKGL.js +117 -0
- package/dist/{chunk-6ZOLTQDN.js → chunk-LAGNM6I7.js} +39 -11
- package/dist/chunk-XMAI2AAN.js +1221 -0
- package/dist/index.d.ts +43 -7
- package/dist/index.js +49 -155
- package/dist/providers/imessage/index.d.ts +16 -7
- package/dist/providers/imessage/index.js +345 -60
- package/dist/providers/terminal/index.d.ts +109 -9
- package/dist/providers/terminal/index.js +813 -32
- package/dist/providers/whatsapp-business/index.d.ts +1 -1
- package/dist/providers/whatsapp-business/index.js +15 -10
- package/dist/{types-B8g0pvfg.d.ts → types-D5KhSXLy.d.ts} +27 -2
- package/package.json +1 -1
- package/dist/chunk-CZIWNTXP.js +0 -710
- package/dist/chunk-PLJI5FTO.js +0 -513
|
@@ -0,0 +1,1221 @@
|
|
|
1
|
+
// src/content/attachment.ts
|
|
2
|
+
import { createReadStream } from "fs";
|
|
3
|
+
import { readFile, stat } from "fs/promises";
|
|
4
|
+
import { basename } from "path";
|
|
5
|
+
import { Readable } from "stream";
|
|
6
|
+
import { lookup as lookupMimeType } from "mime-types";
|
|
7
|
+
import z2 from "zod";
|
|
8
|
+
|
|
9
|
+
// src/utils/io.ts
|
|
10
|
+
import z from "zod";
|
|
11
|
+
var readSchema = z.function({
|
|
12
|
+
input: [],
|
|
13
|
+
output: z.promise(z.instanceof(Buffer))
|
|
14
|
+
});
|
|
15
|
+
var streamSchema = z.function({
|
|
16
|
+
input: [],
|
|
17
|
+
output: z.promise(z.instanceof(ReadableStream))
|
|
18
|
+
});
|
|
19
|
+
var bufferToStream = (buf) => new ReadableStream({
|
|
20
|
+
start(controller) {
|
|
21
|
+
controller.enqueue(buf);
|
|
22
|
+
controller.close();
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// src/content/attachment.ts
|
|
27
|
+
var DEFAULT_ATTACHMENT_NAME = "attachment";
|
|
28
|
+
var attachmentSchema = z2.object({
|
|
29
|
+
type: z2.literal("attachment"),
|
|
30
|
+
name: z2.string().nonempty(),
|
|
31
|
+
mimeType: z2.string().nonempty(),
|
|
32
|
+
size: z2.number().int().nonnegative().optional(),
|
|
33
|
+
read: readSchema,
|
|
34
|
+
stream: streamSchema
|
|
35
|
+
});
|
|
36
|
+
var resolveAttachmentName = (input, name) => name || (typeof input === "string" ? basename(input) : DEFAULT_ATTACHMENT_NAME);
|
|
37
|
+
var resolveAttachmentMimeType = (name, mimeType) => {
|
|
38
|
+
if (mimeType) {
|
|
39
|
+
return mimeType;
|
|
40
|
+
}
|
|
41
|
+
const resolvedMimeType = lookupMimeType(name);
|
|
42
|
+
if (!resolvedMimeType) {
|
|
43
|
+
throw new Error(
|
|
44
|
+
`Unable to resolve MIME type for attachment "${name}". Pass options.mimeType explicitly.`
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
return resolvedMimeType;
|
|
48
|
+
};
|
|
49
|
+
var asAttachment = (input) => {
|
|
50
|
+
let cached;
|
|
51
|
+
const read = () => {
|
|
52
|
+
cached ??= input.read().catch((err) => {
|
|
53
|
+
cached = void 0;
|
|
54
|
+
throw err;
|
|
55
|
+
});
|
|
56
|
+
return cached;
|
|
57
|
+
};
|
|
58
|
+
const stream = input.stream ?? (async () => bufferToStream(await read()));
|
|
59
|
+
return attachmentSchema.parse({
|
|
60
|
+
type: "attachment",
|
|
61
|
+
name: input.name,
|
|
62
|
+
mimeType: input.mimeType,
|
|
63
|
+
size: input.size,
|
|
64
|
+
read,
|
|
65
|
+
stream
|
|
66
|
+
});
|
|
67
|
+
};
|
|
68
|
+
function attachment(input, options) {
|
|
69
|
+
return {
|
|
70
|
+
build: async () => {
|
|
71
|
+
const name = resolveAttachmentName(input, options?.name);
|
|
72
|
+
const mimeType = resolveAttachmentMimeType(name, options?.mimeType);
|
|
73
|
+
if (typeof input === "string") {
|
|
74
|
+
const stats = await stat(input);
|
|
75
|
+
return asAttachment({
|
|
76
|
+
name,
|
|
77
|
+
mimeType,
|
|
78
|
+
size: stats.size,
|
|
79
|
+
read: () => readFile(input),
|
|
80
|
+
stream: async () => Readable.toWeb(
|
|
81
|
+
createReadStream(input)
|
|
82
|
+
)
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
return asAttachment({
|
|
86
|
+
name,
|
|
87
|
+
mimeType,
|
|
88
|
+
size: input.byteLength,
|
|
89
|
+
read: async () => input,
|
|
90
|
+
stream: async () => bufferToStream(input)
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// src/utils/vcard.ts
|
|
97
|
+
import vCard from "vcf";
|
|
98
|
+
var asPropertyArray = (prop) => {
|
|
99
|
+
if (!prop) {
|
|
100
|
+
return [];
|
|
101
|
+
}
|
|
102
|
+
const arr = Array.isArray(prop) ? prop : [prop];
|
|
103
|
+
return arr;
|
|
104
|
+
};
|
|
105
|
+
var propString = (prop) => {
|
|
106
|
+
const [first] = asPropertyArray(prop);
|
|
107
|
+
const value = first?.valueOf().trim();
|
|
108
|
+
return value ? value : void 0;
|
|
109
|
+
};
|
|
110
|
+
var paramTypes = (prop) => {
|
|
111
|
+
const { type } = prop;
|
|
112
|
+
if (!type) {
|
|
113
|
+
return [];
|
|
114
|
+
}
|
|
115
|
+
return (Array.isArray(type) ? type : [type]).map((t) => t.toLowerCase());
|
|
116
|
+
};
|
|
117
|
+
var mapPhoneType = (prop) => {
|
|
118
|
+
const types = paramTypes(prop);
|
|
119
|
+
if (types.some((t) => t === "cell" || t === "mobile" || t === "iphone")) {
|
|
120
|
+
return "mobile";
|
|
121
|
+
}
|
|
122
|
+
if (types.includes("home")) {
|
|
123
|
+
return "home";
|
|
124
|
+
}
|
|
125
|
+
if (types.includes("work")) {
|
|
126
|
+
return "work";
|
|
127
|
+
}
|
|
128
|
+
if (types.length > 0) {
|
|
129
|
+
return "other";
|
|
130
|
+
}
|
|
131
|
+
return void 0;
|
|
132
|
+
};
|
|
133
|
+
var mapSimpleType = (prop) => {
|
|
134
|
+
const types = paramTypes(prop);
|
|
135
|
+
if (types.includes("home")) {
|
|
136
|
+
return "home";
|
|
137
|
+
}
|
|
138
|
+
if (types.includes("work")) {
|
|
139
|
+
return "work";
|
|
140
|
+
}
|
|
141
|
+
if (types.length > 0) {
|
|
142
|
+
return "other";
|
|
143
|
+
}
|
|
144
|
+
return void 0;
|
|
145
|
+
};
|
|
146
|
+
var splitStructured = (value) => value.split(";").map((part) => part.trim());
|
|
147
|
+
var extractName = (card) => {
|
|
148
|
+
const fn = propString(card.data.fn);
|
|
149
|
+
const n = propString(card.data.n);
|
|
150
|
+
if (!(fn || n)) {
|
|
151
|
+
return void 0;
|
|
152
|
+
}
|
|
153
|
+
const result = {};
|
|
154
|
+
if (fn) {
|
|
155
|
+
result.formatted = fn;
|
|
156
|
+
}
|
|
157
|
+
if (n) {
|
|
158
|
+
const [last, first, middle, prefix, suffix] = splitStructured(n);
|
|
159
|
+
if (first) {
|
|
160
|
+
result.first = first;
|
|
161
|
+
}
|
|
162
|
+
if (last) {
|
|
163
|
+
result.last = last;
|
|
164
|
+
}
|
|
165
|
+
if (middle) {
|
|
166
|
+
result.middle = middle;
|
|
167
|
+
}
|
|
168
|
+
if (prefix) {
|
|
169
|
+
result.prefix = prefix;
|
|
170
|
+
}
|
|
171
|
+
if (suffix) {
|
|
172
|
+
result.suffix = suffix;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return result;
|
|
176
|
+
};
|
|
177
|
+
var extractPhones = (card) => {
|
|
178
|
+
const props = asPropertyArray(card.data.tel);
|
|
179
|
+
if (props.length === 0) {
|
|
180
|
+
return void 0;
|
|
181
|
+
}
|
|
182
|
+
return props.map((p) => {
|
|
183
|
+
const entry = { value: p.valueOf().trim() };
|
|
184
|
+
const type = mapPhoneType(p);
|
|
185
|
+
if (type) {
|
|
186
|
+
entry.type = type;
|
|
187
|
+
}
|
|
188
|
+
return entry;
|
|
189
|
+
});
|
|
190
|
+
};
|
|
191
|
+
var extractEmails = (card) => {
|
|
192
|
+
const props = asPropertyArray(card.data.email);
|
|
193
|
+
if (props.length === 0) {
|
|
194
|
+
return void 0;
|
|
195
|
+
}
|
|
196
|
+
return props.map((p) => {
|
|
197
|
+
const entry = { value: p.valueOf().trim() };
|
|
198
|
+
const type = mapSimpleType(p);
|
|
199
|
+
if (type) {
|
|
200
|
+
entry.type = type;
|
|
201
|
+
}
|
|
202
|
+
return entry;
|
|
203
|
+
});
|
|
204
|
+
};
|
|
205
|
+
var extractAddresses = (card) => {
|
|
206
|
+
const props = asPropertyArray(card.data.adr);
|
|
207
|
+
if (props.length === 0) {
|
|
208
|
+
return void 0;
|
|
209
|
+
}
|
|
210
|
+
return props.map((p) => {
|
|
211
|
+
const [, , street, city, region, postalCode, country] = splitStructured(
|
|
212
|
+
p.valueOf()
|
|
213
|
+
);
|
|
214
|
+
const entry = {};
|
|
215
|
+
if (street) {
|
|
216
|
+
entry.street = street;
|
|
217
|
+
}
|
|
218
|
+
if (city) {
|
|
219
|
+
entry.city = city;
|
|
220
|
+
}
|
|
221
|
+
if (region) {
|
|
222
|
+
entry.region = region;
|
|
223
|
+
}
|
|
224
|
+
if (postalCode) {
|
|
225
|
+
entry.postalCode = postalCode;
|
|
226
|
+
}
|
|
227
|
+
if (country) {
|
|
228
|
+
entry.country = country;
|
|
229
|
+
}
|
|
230
|
+
const type = mapSimpleType(p);
|
|
231
|
+
if (type) {
|
|
232
|
+
entry.type = type;
|
|
233
|
+
}
|
|
234
|
+
return entry;
|
|
235
|
+
});
|
|
236
|
+
};
|
|
237
|
+
var extractOrg = (card) => {
|
|
238
|
+
const orgStr = propString(card.data.org);
|
|
239
|
+
const title = propString(card.data.title);
|
|
240
|
+
if (!(orgStr || title)) {
|
|
241
|
+
return void 0;
|
|
242
|
+
}
|
|
243
|
+
const result = {};
|
|
244
|
+
if (orgStr) {
|
|
245
|
+
const [name, department] = splitStructured(orgStr);
|
|
246
|
+
if (name) {
|
|
247
|
+
result.name = name;
|
|
248
|
+
}
|
|
249
|
+
if (department) {
|
|
250
|
+
result.department = department;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
if (title) {
|
|
254
|
+
result.title = title;
|
|
255
|
+
}
|
|
256
|
+
return result;
|
|
257
|
+
};
|
|
258
|
+
var extractUrls = (card) => {
|
|
259
|
+
const props = asPropertyArray(card.data.url);
|
|
260
|
+
if (props.length === 0) {
|
|
261
|
+
return void 0;
|
|
262
|
+
}
|
|
263
|
+
return props.map((p) => p.valueOf().trim());
|
|
264
|
+
};
|
|
265
|
+
var photoMimeFromType = (type) => {
|
|
266
|
+
if (!type) {
|
|
267
|
+
return "image/jpeg";
|
|
268
|
+
}
|
|
269
|
+
const lower = type.toLowerCase();
|
|
270
|
+
if (lower.startsWith("image/")) {
|
|
271
|
+
return lower;
|
|
272
|
+
}
|
|
273
|
+
return `image/${lower}`;
|
|
274
|
+
};
|
|
275
|
+
var DATA_URI_PATTERN = /^data:([^;,]+);base64,(.*)$/i;
|
|
276
|
+
var extractPhoto = (card) => {
|
|
277
|
+
const [prop] = asPropertyArray(card.data.photo);
|
|
278
|
+
if (!prop) {
|
|
279
|
+
return void 0;
|
|
280
|
+
}
|
|
281
|
+
const value = prop.valueOf();
|
|
282
|
+
const dataUriMatch = DATA_URI_PATTERN.exec(value);
|
|
283
|
+
if (dataUriMatch) {
|
|
284
|
+
const [, mimeType, base64] = dataUriMatch;
|
|
285
|
+
const buf2 = Buffer.from(base64 ?? "", "base64");
|
|
286
|
+
return {
|
|
287
|
+
mimeType: mimeType ?? "image/jpeg",
|
|
288
|
+
read: async () => buf2
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
const type = Array.isArray(prop.type) ? prop.type[0] : prop.type;
|
|
292
|
+
const buf = Buffer.from(value, "base64");
|
|
293
|
+
return {
|
|
294
|
+
mimeType: photoMimeFromType(type),
|
|
295
|
+
read: async () => buf
|
|
296
|
+
};
|
|
297
|
+
};
|
|
298
|
+
var normalizeVCardInput = (vcf) => {
|
|
299
|
+
const withoutBom = vcf.charCodeAt(0) === 65279 ? vcf.slice(1) : vcf;
|
|
300
|
+
return withoutBom.replace(/\r\n|\r|\n/g, "\r\n");
|
|
301
|
+
};
|
|
302
|
+
var fromVCard = (vcf) => {
|
|
303
|
+
const [card] = vCard.parse(normalizeVCardInput(vcf));
|
|
304
|
+
if (!card) {
|
|
305
|
+
throw new Error("Invalid vCard: no cards parsed");
|
|
306
|
+
}
|
|
307
|
+
const input = { raw: vcf };
|
|
308
|
+
const name = extractName(card);
|
|
309
|
+
if (name) {
|
|
310
|
+
input.name = name;
|
|
311
|
+
}
|
|
312
|
+
const phones = extractPhones(card);
|
|
313
|
+
if (phones) {
|
|
314
|
+
input.phones = phones;
|
|
315
|
+
}
|
|
316
|
+
const emails = extractEmails(card);
|
|
317
|
+
if (emails) {
|
|
318
|
+
input.emails = emails;
|
|
319
|
+
}
|
|
320
|
+
const addresses = extractAddresses(card);
|
|
321
|
+
if (addresses) {
|
|
322
|
+
input.addresses = addresses;
|
|
323
|
+
}
|
|
324
|
+
const org = extractOrg(card);
|
|
325
|
+
if (org) {
|
|
326
|
+
input.org = org;
|
|
327
|
+
}
|
|
328
|
+
const urls = extractUrls(card);
|
|
329
|
+
if (urls) {
|
|
330
|
+
input.urls = urls;
|
|
331
|
+
}
|
|
332
|
+
const birthday = propString(card.data.bday);
|
|
333
|
+
if (birthday) {
|
|
334
|
+
input.birthday = birthday;
|
|
335
|
+
}
|
|
336
|
+
const note = propString(card.data.note);
|
|
337
|
+
if (note) {
|
|
338
|
+
input.note = note;
|
|
339
|
+
}
|
|
340
|
+
const photo = extractPhoto(card);
|
|
341
|
+
if (photo) {
|
|
342
|
+
input.photo = photo;
|
|
343
|
+
}
|
|
344
|
+
return input;
|
|
345
|
+
};
|
|
346
|
+
var formattedNameFor = (name) => {
|
|
347
|
+
if (name?.formatted) {
|
|
348
|
+
return name.formatted;
|
|
349
|
+
}
|
|
350
|
+
const parts = [name?.first, name?.middle, name?.last].filter(
|
|
351
|
+
(p) => Boolean(p)
|
|
352
|
+
);
|
|
353
|
+
if (parts.length > 0) {
|
|
354
|
+
return parts.join(" ");
|
|
355
|
+
}
|
|
356
|
+
return "Unknown";
|
|
357
|
+
};
|
|
358
|
+
var phoneTypeParam = (type) => {
|
|
359
|
+
if (type === "mobile") {
|
|
360
|
+
return "CELL";
|
|
361
|
+
}
|
|
362
|
+
if (type === "home" || type === "work" || type === "other") {
|
|
363
|
+
return type.toUpperCase();
|
|
364
|
+
}
|
|
365
|
+
return void 0;
|
|
366
|
+
};
|
|
367
|
+
var simpleTypeParam = (type) => type ? type.toUpperCase() : void 0;
|
|
368
|
+
var photoTypeParam = (mimeType) => {
|
|
369
|
+
const sub = mimeType.split("/")[1] ?? "jpeg";
|
|
370
|
+
return sub.toUpperCase();
|
|
371
|
+
};
|
|
372
|
+
var writeName = (card, name) => {
|
|
373
|
+
card.set("fn", formattedNameFor(name));
|
|
374
|
+
if (!name) {
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
if (name.first || name.last || name.middle || name.prefix || name.suffix) {
|
|
378
|
+
card.set(
|
|
379
|
+
"n",
|
|
380
|
+
[
|
|
381
|
+
name.last ?? "",
|
|
382
|
+
name.first ?? "",
|
|
383
|
+
name.middle ?? "",
|
|
384
|
+
name.prefix ?? "",
|
|
385
|
+
name.suffix ?? ""
|
|
386
|
+
].join(";")
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
};
|
|
390
|
+
var writePhones = (card, phones) => {
|
|
391
|
+
for (const phone of phones ?? []) {
|
|
392
|
+
const type = phoneTypeParam(phone.type);
|
|
393
|
+
card.add("tel", phone.value, type ? { type } : void 0);
|
|
394
|
+
}
|
|
395
|
+
};
|
|
396
|
+
var writeEmails = (card, emails) => {
|
|
397
|
+
for (const email of emails ?? []) {
|
|
398
|
+
const type = simpleTypeParam(email.type);
|
|
399
|
+
card.add("email", email.value, type ? { type } : void 0);
|
|
400
|
+
}
|
|
401
|
+
};
|
|
402
|
+
var writeAddresses = (card, addresses) => {
|
|
403
|
+
for (const addr of addresses ?? []) {
|
|
404
|
+
const value = [
|
|
405
|
+
"",
|
|
406
|
+
"",
|
|
407
|
+
addr.street ?? "",
|
|
408
|
+
addr.city ?? "",
|
|
409
|
+
addr.region ?? "",
|
|
410
|
+
addr.postalCode ?? "",
|
|
411
|
+
addr.country ?? ""
|
|
412
|
+
].join(";");
|
|
413
|
+
const type = simpleTypeParam(addr.type);
|
|
414
|
+
card.add("adr", value, type ? { type } : void 0);
|
|
415
|
+
}
|
|
416
|
+
};
|
|
417
|
+
var writeOrg = (card, org) => {
|
|
418
|
+
if (!org) {
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
if (org.name || org.department) {
|
|
422
|
+
card.set("org", [org.name ?? "", org.department ?? ""].join(";"));
|
|
423
|
+
}
|
|
424
|
+
if (org.title) {
|
|
425
|
+
card.set("title", org.title);
|
|
426
|
+
}
|
|
427
|
+
};
|
|
428
|
+
var writeUrls = (card, urls) => {
|
|
429
|
+
for (const url of urls ?? []) {
|
|
430
|
+
card.add("url", url);
|
|
431
|
+
}
|
|
432
|
+
};
|
|
433
|
+
var writePhoto = async (card, photo) => {
|
|
434
|
+
if (!photo) {
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
const buf = await photo.read();
|
|
438
|
+
card.set("photo", buf.toString("base64"), {
|
|
439
|
+
encoding: "b",
|
|
440
|
+
type: photoTypeParam(photo.mimeType)
|
|
441
|
+
});
|
|
442
|
+
};
|
|
443
|
+
var toVCard = async (contact2) => {
|
|
444
|
+
if (typeof contact2.raw === "string" && contact2.raw.startsWith("BEGIN:VCARD")) {
|
|
445
|
+
return contact2.raw;
|
|
446
|
+
}
|
|
447
|
+
const card = new vCard();
|
|
448
|
+
writeName(card, contact2.name);
|
|
449
|
+
writePhones(card, contact2.phones);
|
|
450
|
+
writeEmails(card, contact2.emails);
|
|
451
|
+
writeAddresses(card, contact2.addresses);
|
|
452
|
+
writeOrg(card, contact2.org);
|
|
453
|
+
writeUrls(card, contact2.urls);
|
|
454
|
+
if (contact2.birthday) {
|
|
455
|
+
card.set("bday", contact2.birthday);
|
|
456
|
+
}
|
|
457
|
+
if (contact2.note) {
|
|
458
|
+
card.set("note", contact2.note);
|
|
459
|
+
}
|
|
460
|
+
await writePhoto(card, contact2.photo);
|
|
461
|
+
return card.toString();
|
|
462
|
+
};
|
|
463
|
+
|
|
464
|
+
// src/content/contact.ts
|
|
465
|
+
import vCard2 from "vcf";
|
|
466
|
+
import z3 from "zod";
|
|
467
|
+
var userRefSchema = z3.object({
|
|
468
|
+
__platform: z3.string(),
|
|
469
|
+
id: z3.string()
|
|
470
|
+
});
|
|
471
|
+
var nameSchema = z3.object({
|
|
472
|
+
formatted: z3.string().optional(),
|
|
473
|
+
first: z3.string().optional(),
|
|
474
|
+
last: z3.string().optional(),
|
|
475
|
+
middle: z3.string().optional(),
|
|
476
|
+
prefix: z3.string().optional(),
|
|
477
|
+
suffix: z3.string().optional()
|
|
478
|
+
});
|
|
479
|
+
var phoneTypeSchema = z3.enum(["mobile", "home", "work", "other"]);
|
|
480
|
+
var emailTypeSchema = z3.enum(["home", "work", "other"]);
|
|
481
|
+
var addressTypeSchema = z3.enum(["home", "work", "other"]);
|
|
482
|
+
var phoneSchema = z3.object({
|
|
483
|
+
value: z3.string(),
|
|
484
|
+
type: phoneTypeSchema.optional()
|
|
485
|
+
});
|
|
486
|
+
var emailSchema = z3.object({
|
|
487
|
+
value: z3.string(),
|
|
488
|
+
type: emailTypeSchema.optional()
|
|
489
|
+
});
|
|
490
|
+
var addressSchema = z3.object({
|
|
491
|
+
street: z3.string().optional(),
|
|
492
|
+
city: z3.string().optional(),
|
|
493
|
+
region: z3.string().optional(),
|
|
494
|
+
postalCode: z3.string().optional(),
|
|
495
|
+
country: z3.string().optional(),
|
|
496
|
+
type: addressTypeSchema.optional()
|
|
497
|
+
});
|
|
498
|
+
var orgSchema = z3.object({
|
|
499
|
+
name: z3.string().optional(),
|
|
500
|
+
title: z3.string().optional(),
|
|
501
|
+
department: z3.string().optional()
|
|
502
|
+
});
|
|
503
|
+
var photoSchema = z3.object({
|
|
504
|
+
mimeType: z3.string(),
|
|
505
|
+
read: readSchema
|
|
506
|
+
});
|
|
507
|
+
var contactSchema = z3.object({
|
|
508
|
+
type: z3.literal("contact"),
|
|
509
|
+
user: userRefSchema.optional(),
|
|
510
|
+
name: nameSchema.optional(),
|
|
511
|
+
phones: z3.array(phoneSchema).optional(),
|
|
512
|
+
emails: z3.array(emailSchema).optional(),
|
|
513
|
+
addresses: z3.array(addressSchema).optional(),
|
|
514
|
+
org: orgSchema.optional(),
|
|
515
|
+
urls: z3.array(z3.string()).optional(),
|
|
516
|
+
birthday: z3.string().optional(),
|
|
517
|
+
note: z3.string().optional(),
|
|
518
|
+
photo: photoSchema.optional(),
|
|
519
|
+
raw: z3.unknown().optional()
|
|
520
|
+
});
|
|
521
|
+
var asContact = (input) => contactSchema.parse({ type: "contact", ...input });
|
|
522
|
+
var isUser = (value) => typeof value === "object" && value !== null && "__platform" in value && "id" in value && typeof value.__platform === "string" && typeof value.id === "string";
|
|
523
|
+
function contact(input, details) {
|
|
524
|
+
return {
|
|
525
|
+
build: async () => {
|
|
526
|
+
if (typeof input === "string") {
|
|
527
|
+
return asContact(fromVCard(input));
|
|
528
|
+
}
|
|
529
|
+
if (input instanceof vCard2) {
|
|
530
|
+
return asContact(fromVCard(input.toString()));
|
|
531
|
+
}
|
|
532
|
+
if (isUser(input)) {
|
|
533
|
+
return asContact({
|
|
534
|
+
user: { __platform: input.__platform, id: input.id },
|
|
535
|
+
...details
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
return asContact(input);
|
|
539
|
+
}
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
// src/content/custom.ts
|
|
544
|
+
import z4 from "zod";
|
|
545
|
+
var customSchema = z4.object({
|
|
546
|
+
type: z4.literal("custom"),
|
|
547
|
+
raw: z4.unknown()
|
|
548
|
+
});
|
|
549
|
+
var asCustom = (raw) => customSchema.parse({ type: "custom", raw });
|
|
550
|
+
function custom(raw) {
|
|
551
|
+
return {
|
|
552
|
+
build: async () => asCustom(raw)
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
// src/content/text.ts
|
|
557
|
+
import z5 from "zod";
|
|
558
|
+
var textSchema = z5.object({
|
|
559
|
+
type: z5.literal("text"),
|
|
560
|
+
text: z5.string().nonempty()
|
|
561
|
+
});
|
|
562
|
+
var asText = (text2) => textSchema.parse({ type: "text", text: text2 });
|
|
563
|
+
function text(text2) {
|
|
564
|
+
return {
|
|
565
|
+
build: async () => asText(text2)
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
// src/content/resolve.ts
|
|
570
|
+
var resolveContents = (items) => Promise.all(
|
|
571
|
+
items.map((c) => typeof c === "string" ? text(c).build() : c.build())
|
|
572
|
+
);
|
|
573
|
+
|
|
574
|
+
// src/content/reaction.ts
|
|
575
|
+
import z6 from "zod";
|
|
576
|
+
var isMessage = (v) => typeof v === "object" && v !== null && "id" in v && "content" in v;
|
|
577
|
+
var reactionSchema = z6.object({
|
|
578
|
+
type: z6.literal("reaction"),
|
|
579
|
+
emoji: z6.string().min(1),
|
|
580
|
+
target: z6.custom(isMessage, {
|
|
581
|
+
message: "reaction target must be a Message"
|
|
582
|
+
})
|
|
583
|
+
});
|
|
584
|
+
var asReaction = (input) => reactionSchema.parse({ type: "reaction", ...input });
|
|
585
|
+
function reaction(emoji, target) {
|
|
586
|
+
return { build: async () => asReaction({ emoji, target }) };
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
// src/utils/errors.ts
|
|
590
|
+
var composeMessage = (opts) => {
|
|
591
|
+
const platform = opts.platform ?? "platform";
|
|
592
|
+
const subject = opts.kind === "content" ? `content type "${opts.contentType ?? "unknown"}"` : `action "${opts.action ?? "unknown"}"`;
|
|
593
|
+
const detail = opts.detail ? `: ${opts.detail}` : "";
|
|
594
|
+
return `${platform} does not support ${subject}${detail}`;
|
|
595
|
+
};
|
|
596
|
+
var UnsupportedError = class _UnsupportedError extends Error {
|
|
597
|
+
kind;
|
|
598
|
+
platform;
|
|
599
|
+
contentType;
|
|
600
|
+
action;
|
|
601
|
+
detail;
|
|
602
|
+
constructor(opts) {
|
|
603
|
+
super(composeMessage(opts));
|
|
604
|
+
this.name = "UnsupportedError";
|
|
605
|
+
this.kind = opts.kind;
|
|
606
|
+
this.platform = opts.platform;
|
|
607
|
+
this.contentType = opts.contentType;
|
|
608
|
+
this.action = opts.action;
|
|
609
|
+
this.detail = opts.detail;
|
|
610
|
+
}
|
|
611
|
+
static content(contentType, platform, detail) {
|
|
612
|
+
return new _UnsupportedError({
|
|
613
|
+
kind: "content",
|
|
614
|
+
contentType,
|
|
615
|
+
platform,
|
|
616
|
+
detail
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
static action(action, platform, detail) {
|
|
620
|
+
return new _UnsupportedError({ kind: "action", action, platform, detail });
|
|
621
|
+
}
|
|
622
|
+
withPlatform(platform) {
|
|
623
|
+
if (this.platform) {
|
|
624
|
+
return this;
|
|
625
|
+
}
|
|
626
|
+
return new _UnsupportedError({
|
|
627
|
+
kind: this.kind,
|
|
628
|
+
platform,
|
|
629
|
+
contentType: this.contentType,
|
|
630
|
+
action: this.action,
|
|
631
|
+
detail: this.detail
|
|
632
|
+
});
|
|
633
|
+
}
|
|
634
|
+
};
|
|
635
|
+
|
|
636
|
+
// src/platform/build.ts
|
|
637
|
+
var ANSI_YELLOW = "\x1B[33m";
|
|
638
|
+
var ANSI_RESET = "\x1B[0m";
|
|
639
|
+
var supportsAnsiColor = () => {
|
|
640
|
+
if (typeof process === "undefined") {
|
|
641
|
+
return false;
|
|
642
|
+
}
|
|
643
|
+
if (process.env.NO_COLOR) {
|
|
644
|
+
return false;
|
|
645
|
+
}
|
|
646
|
+
const force = process.env.FORCE_COLOR;
|
|
647
|
+
if (force !== void 0) {
|
|
648
|
+
return force !== "" && force !== "0" && force !== "false";
|
|
649
|
+
}
|
|
650
|
+
return Boolean(process.stderr?.isTTY);
|
|
651
|
+
};
|
|
652
|
+
var warnUnsupported = (err, fallbackPlatform) => {
|
|
653
|
+
const platform = err.platform ?? fallbackPlatform;
|
|
654
|
+
const subject = err.kind === "content" ? `content type "${err.contentType ?? "unknown"}"` : `action "${err.action ?? "unknown"}"`;
|
|
655
|
+
const detail = err.detail ? `: ${err.detail}` : "";
|
|
656
|
+
const body = `[spectrum-ts] ${platform} does not support ${subject}${detail}; skipping.`;
|
|
657
|
+
console.warn(
|
|
658
|
+
supportsAnsiColor() ? `${ANSI_YELLOW}${body}${ANSI_RESET}` : body
|
|
659
|
+
);
|
|
660
|
+
};
|
|
661
|
+
var providerMessageCoreKeys = /* @__PURE__ */ new Set([
|
|
662
|
+
"content",
|
|
663
|
+
"id",
|
|
664
|
+
"sender",
|
|
665
|
+
"space",
|
|
666
|
+
"timestamp"
|
|
667
|
+
]);
|
|
668
|
+
var extractExtras = (raw, definition) => {
|
|
669
|
+
const entries = Object.entries(raw).filter(
|
|
670
|
+
([key]) => !providerMessageCoreKeys.has(key)
|
|
671
|
+
);
|
|
672
|
+
const extra = Object.fromEntries(entries);
|
|
673
|
+
return definition.message?.schema ? definition.message.schema.parse(extra) : extra;
|
|
674
|
+
};
|
|
675
|
+
function wrapProviderMessage(raw, ctx) {
|
|
676
|
+
const wrappedContent = wrapNestedContent(raw.content, ctx);
|
|
677
|
+
return buildMessage({
|
|
678
|
+
id: raw.id,
|
|
679
|
+
content: wrappedContent,
|
|
680
|
+
sender: raw.sender,
|
|
681
|
+
timestamp: raw.timestamp ?? /* @__PURE__ */ new Date(),
|
|
682
|
+
extras: extractExtras(raw, ctx.definition),
|
|
683
|
+
spaceRef: ctx.spaceRef,
|
|
684
|
+
space: ctx.space,
|
|
685
|
+
definition: ctx.definition,
|
|
686
|
+
client: ctx.client,
|
|
687
|
+
config: ctx.config,
|
|
688
|
+
direction: "inbound"
|
|
689
|
+
});
|
|
690
|
+
}
|
|
691
|
+
var wrapNestedContent = (content, ctx) => {
|
|
692
|
+
if (content.type === "reaction") {
|
|
693
|
+
const target = content.target;
|
|
694
|
+
if (isRawProviderRecord(target)) {
|
|
695
|
+
return {
|
|
696
|
+
...content,
|
|
697
|
+
target: wrapProviderMessage(target, ctx)
|
|
698
|
+
};
|
|
699
|
+
}
|
|
700
|
+
return content;
|
|
701
|
+
}
|
|
702
|
+
if (content.type === "group") {
|
|
703
|
+
const items = content.items.map((item) => {
|
|
704
|
+
const raw = item;
|
|
705
|
+
return isRawProviderRecord(raw) ? wrapProviderMessage(raw, ctx) : item;
|
|
706
|
+
});
|
|
707
|
+
return { ...content, items };
|
|
708
|
+
}
|
|
709
|
+
return content;
|
|
710
|
+
};
|
|
711
|
+
var isRawProviderRecord = (v) => {
|
|
712
|
+
if (typeof v !== "object" || v === null) {
|
|
713
|
+
return false;
|
|
714
|
+
}
|
|
715
|
+
const record = v;
|
|
716
|
+
return "id" in record && "content" in record && typeof record.react !== "function" && typeof record.reply !== "function";
|
|
717
|
+
};
|
|
718
|
+
function buildSpace(params) {
|
|
719
|
+
const { spaceRef, extras, typingCtx, definition, client, config } = params;
|
|
720
|
+
let space;
|
|
721
|
+
async function dispatchReaction(item) {
|
|
722
|
+
try {
|
|
723
|
+
if (!definition.actions.reactToMessage) {
|
|
724
|
+
throw UnsupportedError.action("react", definition.name);
|
|
725
|
+
}
|
|
726
|
+
await definition.actions.reactToMessage({
|
|
727
|
+
space: spaceRef,
|
|
728
|
+
target: item.target,
|
|
729
|
+
reaction: item.emoji,
|
|
730
|
+
client,
|
|
731
|
+
config
|
|
732
|
+
});
|
|
733
|
+
} catch (err) {
|
|
734
|
+
if (err instanceof UnsupportedError) {
|
|
735
|
+
warnUnsupported(err, definition.name);
|
|
736
|
+
return;
|
|
737
|
+
}
|
|
738
|
+
throw err;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
async function dispatchSend(item) {
|
|
742
|
+
let sendResult;
|
|
743
|
+
try {
|
|
744
|
+
sendResult = await definition.actions.send({
|
|
745
|
+
...typingCtx,
|
|
746
|
+
content: item
|
|
747
|
+
});
|
|
748
|
+
} catch (err) {
|
|
749
|
+
if (err instanceof UnsupportedError) {
|
|
750
|
+
warnUnsupported(err, definition.name);
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
throw err;
|
|
754
|
+
}
|
|
755
|
+
if (!sendResult?.id) {
|
|
756
|
+
throw new Error(
|
|
757
|
+
`Platform "${definition.name}" send did not return a message id`
|
|
758
|
+
);
|
|
759
|
+
}
|
|
760
|
+
const outboundContent = item.type === "group" && sendResult.groupMembers ? {
|
|
761
|
+
...item,
|
|
762
|
+
items: item.items.map((stub, idx) => {
|
|
763
|
+
const member = sendResult?.groupMembers?.[idx];
|
|
764
|
+
if (!member?.id) {
|
|
765
|
+
return stub;
|
|
766
|
+
}
|
|
767
|
+
return buildMessage({
|
|
768
|
+
id: member.id,
|
|
769
|
+
content: stub.content,
|
|
770
|
+
sender: member.sender,
|
|
771
|
+
timestamp: member.timestamp ?? /* @__PURE__ */ new Date(),
|
|
772
|
+
extras: {},
|
|
773
|
+
spaceRef,
|
|
774
|
+
space,
|
|
775
|
+
definition,
|
|
776
|
+
client,
|
|
777
|
+
config,
|
|
778
|
+
direction: "outbound"
|
|
779
|
+
});
|
|
780
|
+
})
|
|
781
|
+
} : item;
|
|
782
|
+
return buildMessage({
|
|
783
|
+
id: sendResult.id,
|
|
784
|
+
content: outboundContent,
|
|
785
|
+
sender: sendResult.sender,
|
|
786
|
+
timestamp: sendResult.timestamp ?? /* @__PURE__ */ new Date(),
|
|
787
|
+
extras: {},
|
|
788
|
+
spaceRef,
|
|
789
|
+
space,
|
|
790
|
+
definition,
|
|
791
|
+
client,
|
|
792
|
+
config,
|
|
793
|
+
direction: "outbound"
|
|
794
|
+
});
|
|
795
|
+
}
|
|
796
|
+
async function sendImpl(...content) {
|
|
797
|
+
const resolved = await resolveContents(content);
|
|
798
|
+
const results = [];
|
|
799
|
+
for (const item of resolved) {
|
|
800
|
+
if (item.type === "reaction") {
|
|
801
|
+
await dispatchReaction(item);
|
|
802
|
+
continue;
|
|
803
|
+
}
|
|
804
|
+
const sent = await dispatchSend(item);
|
|
805
|
+
if (sent) {
|
|
806
|
+
results.push(sent);
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
if (content.length === 1) {
|
|
810
|
+
return results[0];
|
|
811
|
+
}
|
|
812
|
+
return results;
|
|
813
|
+
}
|
|
814
|
+
async function getMessageImpl(id) {
|
|
815
|
+
if (!definition.actions.getMessage) {
|
|
816
|
+
warnUnsupported(
|
|
817
|
+
UnsupportedError.action("getMessage", definition.name),
|
|
818
|
+
definition.name
|
|
819
|
+
);
|
|
820
|
+
return;
|
|
821
|
+
}
|
|
822
|
+
let raw;
|
|
823
|
+
try {
|
|
824
|
+
raw = await definition.actions.getMessage({
|
|
825
|
+
space: spaceRef,
|
|
826
|
+
messageId: id,
|
|
827
|
+
client,
|
|
828
|
+
config
|
|
829
|
+
});
|
|
830
|
+
} catch (err) {
|
|
831
|
+
if (err instanceof UnsupportedError) {
|
|
832
|
+
warnUnsupported(err, definition.name);
|
|
833
|
+
return;
|
|
834
|
+
}
|
|
835
|
+
throw err;
|
|
836
|
+
}
|
|
837
|
+
if (!raw) {
|
|
838
|
+
return;
|
|
839
|
+
}
|
|
840
|
+
return wrapProviderMessage(raw, {
|
|
841
|
+
client,
|
|
842
|
+
config,
|
|
843
|
+
definition,
|
|
844
|
+
space,
|
|
845
|
+
spaceRef
|
|
846
|
+
});
|
|
847
|
+
}
|
|
848
|
+
space = {
|
|
849
|
+
...extras,
|
|
850
|
+
...spaceRef,
|
|
851
|
+
send: sendImpl,
|
|
852
|
+
edit: async (message, newContent) => {
|
|
853
|
+
await message.edit(newContent);
|
|
854
|
+
},
|
|
855
|
+
getMessage: getMessageImpl,
|
|
856
|
+
startTyping: async () => {
|
|
857
|
+
await definition.actions.startTyping?.(typingCtx);
|
|
858
|
+
},
|
|
859
|
+
stopTyping: async () => {
|
|
860
|
+
await definition.actions.stopTyping?.(typingCtx);
|
|
861
|
+
},
|
|
862
|
+
responding: async (fn) => {
|
|
863
|
+
await definition.actions.startTyping?.(typingCtx);
|
|
864
|
+
try {
|
|
865
|
+
return await fn();
|
|
866
|
+
} finally {
|
|
867
|
+
await definition.actions.stopTyping?.(typingCtx).catch(() => {
|
|
868
|
+
});
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
};
|
|
872
|
+
return space;
|
|
873
|
+
}
|
|
874
|
+
function buildMessage(params) {
|
|
875
|
+
const { definition, client, config, spaceRef, space } = params;
|
|
876
|
+
let self;
|
|
877
|
+
const react = async (reaction2) => {
|
|
878
|
+
if (!definition.actions.reactToMessage) {
|
|
879
|
+
warnUnsupported(
|
|
880
|
+
UnsupportedError.action("react", definition.name),
|
|
881
|
+
definition.name
|
|
882
|
+
);
|
|
883
|
+
return;
|
|
884
|
+
}
|
|
885
|
+
if (!self) {
|
|
886
|
+
throw new Error(
|
|
887
|
+
"react() called before message construction completed (internal bug)"
|
|
888
|
+
);
|
|
889
|
+
}
|
|
890
|
+
try {
|
|
891
|
+
await definition.actions.reactToMessage({
|
|
892
|
+
space: spaceRef,
|
|
893
|
+
target: self,
|
|
894
|
+
reaction: reaction2,
|
|
895
|
+
client,
|
|
896
|
+
config
|
|
897
|
+
});
|
|
898
|
+
} catch (err) {
|
|
899
|
+
if (err instanceof UnsupportedError) {
|
|
900
|
+
warnUnsupported(err, definition.name);
|
|
901
|
+
return;
|
|
902
|
+
}
|
|
903
|
+
throw err;
|
|
904
|
+
}
|
|
905
|
+
};
|
|
906
|
+
async function reply(...content) {
|
|
907
|
+
if (!definition.actions.replyToMessage) {
|
|
908
|
+
warnUnsupported(
|
|
909
|
+
UnsupportedError.action("reply", definition.name),
|
|
910
|
+
definition.name
|
|
911
|
+
);
|
|
912
|
+
return content.length === 1 ? void 0 : [];
|
|
913
|
+
}
|
|
914
|
+
const resolved = await resolveContents(content);
|
|
915
|
+
const results = [];
|
|
916
|
+
for (const item of resolved) {
|
|
917
|
+
let sendResult;
|
|
918
|
+
try {
|
|
919
|
+
sendResult = await definition.actions.replyToMessage({
|
|
920
|
+
space: spaceRef,
|
|
921
|
+
messageId: params.id,
|
|
922
|
+
content: item,
|
|
923
|
+
client,
|
|
924
|
+
config
|
|
925
|
+
});
|
|
926
|
+
} catch (err) {
|
|
927
|
+
if (err instanceof UnsupportedError) {
|
|
928
|
+
warnUnsupported(err, definition.name);
|
|
929
|
+
continue;
|
|
930
|
+
}
|
|
931
|
+
throw err;
|
|
932
|
+
}
|
|
933
|
+
if (!sendResult?.id) {
|
|
934
|
+
throw new Error(
|
|
935
|
+
`Platform "${definition.name}" reply did not return a message id`
|
|
936
|
+
);
|
|
937
|
+
}
|
|
938
|
+
results.push(
|
|
939
|
+
buildMessage({
|
|
940
|
+
id: sendResult.id,
|
|
941
|
+
content: item,
|
|
942
|
+
sender: sendResult.sender,
|
|
943
|
+
timestamp: sendResult.timestamp ?? /* @__PURE__ */ new Date(),
|
|
944
|
+
extras: {},
|
|
945
|
+
spaceRef,
|
|
946
|
+
space,
|
|
947
|
+
definition,
|
|
948
|
+
client,
|
|
949
|
+
config,
|
|
950
|
+
direction: "outbound"
|
|
951
|
+
})
|
|
952
|
+
);
|
|
953
|
+
}
|
|
954
|
+
if (content.length === 1) {
|
|
955
|
+
return results[0];
|
|
956
|
+
}
|
|
957
|
+
return results;
|
|
958
|
+
}
|
|
959
|
+
const senderWithPlatform = params.sender === void 0 ? void 0 : { ...params.sender, __platform: definition.name };
|
|
960
|
+
if (params.direction === "outbound") {
|
|
961
|
+
const outbound = {
|
|
962
|
+
...params.extras,
|
|
963
|
+
id: params.id,
|
|
964
|
+
content: params.content,
|
|
965
|
+
direction: "outbound",
|
|
966
|
+
platform: definition.name,
|
|
967
|
+
react,
|
|
968
|
+
reply,
|
|
969
|
+
edit: async (newContent) => {
|
|
970
|
+
if (!definition.actions.editMessage) {
|
|
971
|
+
warnUnsupported(
|
|
972
|
+
UnsupportedError.action("edit", definition.name),
|
|
973
|
+
definition.name
|
|
974
|
+
);
|
|
975
|
+
return;
|
|
976
|
+
}
|
|
977
|
+
const [resolved] = await resolveContents([newContent]);
|
|
978
|
+
if (!resolved) {
|
|
979
|
+
return;
|
|
980
|
+
}
|
|
981
|
+
try {
|
|
982
|
+
await definition.actions.editMessage({
|
|
983
|
+
space: spaceRef,
|
|
984
|
+
messageId: params.id,
|
|
985
|
+
content: resolved,
|
|
986
|
+
client,
|
|
987
|
+
config
|
|
988
|
+
});
|
|
989
|
+
} catch (err) {
|
|
990
|
+
if (err instanceof UnsupportedError) {
|
|
991
|
+
warnUnsupported(err, definition.name);
|
|
992
|
+
return;
|
|
993
|
+
}
|
|
994
|
+
throw err;
|
|
995
|
+
}
|
|
996
|
+
},
|
|
997
|
+
sender: senderWithPlatform,
|
|
998
|
+
space,
|
|
999
|
+
timestamp: params.timestamp
|
|
1000
|
+
};
|
|
1001
|
+
self = outbound;
|
|
1002
|
+
return outbound;
|
|
1003
|
+
}
|
|
1004
|
+
const inbound = {
|
|
1005
|
+
...params.extras,
|
|
1006
|
+
id: params.id,
|
|
1007
|
+
content: params.content,
|
|
1008
|
+
direction: "inbound",
|
|
1009
|
+
platform: definition.name,
|
|
1010
|
+
react,
|
|
1011
|
+
reply,
|
|
1012
|
+
sender: senderWithPlatform,
|
|
1013
|
+
space,
|
|
1014
|
+
timestamp: params.timestamp
|
|
1015
|
+
};
|
|
1016
|
+
self = inbound;
|
|
1017
|
+
return inbound;
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
// src/platform/define.ts
|
|
1021
|
+
function createPlatformInstance(def, runtime) {
|
|
1022
|
+
const isPlatformUser = (value) => {
|
|
1023
|
+
return typeof value === "object" && value !== null && "__platform" in value && value.__platform === def.name;
|
|
1024
|
+
};
|
|
1025
|
+
const resolveUserID = async (userID) => {
|
|
1026
|
+
const resolved = await def.user.resolve({
|
|
1027
|
+
input: { userID },
|
|
1028
|
+
client: runtime.client,
|
|
1029
|
+
config: runtime.config
|
|
1030
|
+
});
|
|
1031
|
+
return {
|
|
1032
|
+
...resolved,
|
|
1033
|
+
__platform: def.name
|
|
1034
|
+
};
|
|
1035
|
+
};
|
|
1036
|
+
const resolveStringUsers = async (args) => {
|
|
1037
|
+
const convertArg = async (arg) => {
|
|
1038
|
+
if (typeof arg === "string") {
|
|
1039
|
+
return await resolveUserID(arg);
|
|
1040
|
+
}
|
|
1041
|
+
if (Array.isArray(arg)) {
|
|
1042
|
+
return await Promise.all(arg.map(convertArg));
|
|
1043
|
+
}
|
|
1044
|
+
return arg;
|
|
1045
|
+
};
|
|
1046
|
+
return await Promise.all(args.map(convertArg));
|
|
1047
|
+
};
|
|
1048
|
+
const normalizeSpaceArgs = (args) => {
|
|
1049
|
+
if (args.length === 0) {
|
|
1050
|
+
return { users: [], params: void 0 };
|
|
1051
|
+
}
|
|
1052
|
+
const [first, ...rest] = args;
|
|
1053
|
+
if (Array.isArray(first)) {
|
|
1054
|
+
return {
|
|
1055
|
+
users: first,
|
|
1056
|
+
params: rest[0]
|
|
1057
|
+
};
|
|
1058
|
+
}
|
|
1059
|
+
if (!isPlatformUser(first)) {
|
|
1060
|
+
return {
|
|
1061
|
+
users: [],
|
|
1062
|
+
params: first
|
|
1063
|
+
};
|
|
1064
|
+
}
|
|
1065
|
+
const last = args.at(-1);
|
|
1066
|
+
if (last !== void 0 && !isPlatformUser(last)) {
|
|
1067
|
+
return {
|
|
1068
|
+
users: args.slice(0, -1),
|
|
1069
|
+
params: last
|
|
1070
|
+
};
|
|
1071
|
+
}
|
|
1072
|
+
return {
|
|
1073
|
+
users: args,
|
|
1074
|
+
params: void 0
|
|
1075
|
+
};
|
|
1076
|
+
};
|
|
1077
|
+
const base = {
|
|
1078
|
+
async user(userID) {
|
|
1079
|
+
const resolved = await def.user.resolve({
|
|
1080
|
+
input: { userID },
|
|
1081
|
+
client: runtime.client,
|
|
1082
|
+
config: runtime.config
|
|
1083
|
+
});
|
|
1084
|
+
return {
|
|
1085
|
+
...resolved,
|
|
1086
|
+
__platform: def.name
|
|
1087
|
+
};
|
|
1088
|
+
},
|
|
1089
|
+
async space(...args) {
|
|
1090
|
+
const convertedArgs = await resolveStringUsers(args);
|
|
1091
|
+
const { users, params } = normalizeSpaceArgs(convertedArgs);
|
|
1092
|
+
let parsedParams = params;
|
|
1093
|
+
if (params !== void 0 && def.space.params) {
|
|
1094
|
+
parsedParams = def.space.params.parse(params);
|
|
1095
|
+
}
|
|
1096
|
+
const resolved = await def.space.resolve({
|
|
1097
|
+
input: { users, params: parsedParams },
|
|
1098
|
+
client: runtime.client,
|
|
1099
|
+
config: runtime.config
|
|
1100
|
+
});
|
|
1101
|
+
const parsedSpace = def.space.schema ? def.space.schema.parse(resolved) : resolved;
|
|
1102
|
+
const spaceRef = {
|
|
1103
|
+
id: parsedSpace.id,
|
|
1104
|
+
__platform: def.name
|
|
1105
|
+
};
|
|
1106
|
+
const typingCtx = {
|
|
1107
|
+
space: spaceRef,
|
|
1108
|
+
client: runtime.client,
|
|
1109
|
+
config: runtime.config
|
|
1110
|
+
};
|
|
1111
|
+
return buildSpace({
|
|
1112
|
+
spaceRef,
|
|
1113
|
+
extras: parsedSpace,
|
|
1114
|
+
typingCtx,
|
|
1115
|
+
definition: def,
|
|
1116
|
+
client: runtime.client,
|
|
1117
|
+
config: runtime.config
|
|
1118
|
+
});
|
|
1119
|
+
}
|
|
1120
|
+
};
|
|
1121
|
+
const eventProperties = {};
|
|
1122
|
+
for (const eventName of Object.keys(def.events)) {
|
|
1123
|
+
if (eventName === "messages") {
|
|
1124
|
+
continue;
|
|
1125
|
+
}
|
|
1126
|
+
const producer = def.events[eventName];
|
|
1127
|
+
if (producer) {
|
|
1128
|
+
eventProperties[eventName] = producer({
|
|
1129
|
+
client: runtime.client,
|
|
1130
|
+
config: runtime.config
|
|
1131
|
+
});
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
return Object.assign(base, eventProperties);
|
|
1135
|
+
}
|
|
1136
|
+
function definePlatform(name, def) {
|
|
1137
|
+
const fullDef = { name, ...def };
|
|
1138
|
+
const platformCache = /* @__PURE__ */ new WeakMap();
|
|
1139
|
+
const narrowSpectrum = (spectrum) => {
|
|
1140
|
+
const cached = platformCache.get(spectrum);
|
|
1141
|
+
if (cached) {
|
|
1142
|
+
return cached;
|
|
1143
|
+
}
|
|
1144
|
+
const runtime = spectrum.__internal.platforms.get(name);
|
|
1145
|
+
if (!runtime) {
|
|
1146
|
+
throw new Error(`Platform "${name}" is not registered`);
|
|
1147
|
+
}
|
|
1148
|
+
const instance = createPlatformInstance(
|
|
1149
|
+
fullDef,
|
|
1150
|
+
runtime
|
|
1151
|
+
);
|
|
1152
|
+
platformCache.set(spectrum, instance);
|
|
1153
|
+
return instance;
|
|
1154
|
+
};
|
|
1155
|
+
const narrowSpace = (input) => {
|
|
1156
|
+
if (input.__platform !== name) {
|
|
1157
|
+
throw new Error(
|
|
1158
|
+
`Expected space from "${name}", got "${input.__platform}"`
|
|
1159
|
+
);
|
|
1160
|
+
}
|
|
1161
|
+
return input;
|
|
1162
|
+
};
|
|
1163
|
+
const narrowMessage = (input) => {
|
|
1164
|
+
if (input.platform !== name) {
|
|
1165
|
+
throw new Error(
|
|
1166
|
+
`Expected message from "${name}", got "${input.platform}"`
|
|
1167
|
+
);
|
|
1168
|
+
}
|
|
1169
|
+
return input;
|
|
1170
|
+
};
|
|
1171
|
+
const narrower = ((input) => {
|
|
1172
|
+
if ("__providers" in input && "__internal" in input) {
|
|
1173
|
+
return narrowSpectrum(input);
|
|
1174
|
+
}
|
|
1175
|
+
if ("__platform" in input && "send" in input) {
|
|
1176
|
+
return narrowSpace(input);
|
|
1177
|
+
}
|
|
1178
|
+
if ("platform" in input && "sender" in input && "space" in input) {
|
|
1179
|
+
return narrowMessage(input);
|
|
1180
|
+
}
|
|
1181
|
+
throw new Error("Invalid input to platform narrowing function");
|
|
1182
|
+
});
|
|
1183
|
+
narrower.config = (config) => {
|
|
1184
|
+
const resolvedConfig = config ?? {};
|
|
1185
|
+
return {
|
|
1186
|
+
__tag: "PlatformProviderConfig",
|
|
1187
|
+
__def: void 0,
|
|
1188
|
+
__name: name,
|
|
1189
|
+
config: resolvedConfig,
|
|
1190
|
+
__definition: fullDef
|
|
1191
|
+
};
|
|
1192
|
+
};
|
|
1193
|
+
if (def.static) {
|
|
1194
|
+
Object.assign(narrower, def.static);
|
|
1195
|
+
}
|
|
1196
|
+
return narrower;
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
export {
|
|
1200
|
+
readSchema,
|
|
1201
|
+
streamSchema,
|
|
1202
|
+
bufferToStream,
|
|
1203
|
+
asAttachment,
|
|
1204
|
+
attachment,
|
|
1205
|
+
fromVCard,
|
|
1206
|
+
toVCard,
|
|
1207
|
+
asContact,
|
|
1208
|
+
contact,
|
|
1209
|
+
asCustom,
|
|
1210
|
+
custom,
|
|
1211
|
+
asText,
|
|
1212
|
+
text,
|
|
1213
|
+
resolveContents,
|
|
1214
|
+
reactionSchema,
|
|
1215
|
+
asReaction,
|
|
1216
|
+
reaction,
|
|
1217
|
+
UnsupportedError,
|
|
1218
|
+
wrapProviderMessage,
|
|
1219
|
+
buildSpace,
|
|
1220
|
+
definePlatform
|
|
1221
|
+
};
|