spectrum-ts 1.17.1 → 2.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-B4nSrKVd.d.ts} +1 -1
- package/dist/{authoring-C9uDdZ2F.d.ts → authoring-BjE5BvlO.d.ts} +2 -2
- package/dist/authoring.d.ts +3 -3
- package/dist/authoring.js +6 -3
- package/dist/chunk-34FQGGD7.js +34 -0
- package/dist/chunk-3B4QH4JG.js +35 -0
- package/dist/chunk-3GEJYGZK.js +84 -0
- package/dist/chunk-5LT5J3NR.js +695 -0
- package/dist/{chunk-MC6ZKFSG.js → chunk-5XEFJBN2.js} +25 -103
- package/dist/{chunk-JQN6CRSC.js → chunk-6BI4PFTP.js} +10 -39
- package/dist/{chunk-QGJFZMD5.js → chunk-6UZFVXQF.js} +17 -101
- package/dist/{chunk-YJMPSD3S.js → chunk-ATNAE7OR.js} +196 -47
- package/dist/{chunk-IPOFBAIM.js → chunk-NGC4DJIX.js} +23 -19
- package/dist/{chunk-5TIF3FIE.js → chunk-Q537JPTG.js} +8 -6
- package/dist/{chunk-5BKZJMZV.js → chunk-U3LXXT3W.js} +61 -32
- package/dist/chunk-U7AWXDH6.js +91 -0
- package/dist/{chunk-3OTECDNH.js → chunk-WXY5QP3M.js} +5 -3
- package/dist/index.d.ts +71 -126
- package/dist/index.js +350 -90
- package/dist/manifest.json +6 -0
- package/dist/providers/imessage/index.d.ts +75 -3
- package/dist/providers/imessage/index.js +10 -5
- package/dist/providers/index.d.ts +5 -2
- package/dist/providers/index.js +16 -8
- package/dist/providers/slack/index.d.ts +1 -1
- package/dist/providers/slack/index.js +4 -3
- package/dist/providers/telegram/index.d.ts +47 -0
- package/dist/providers/telegram/index.js +13 -0
- package/dist/providers/terminal/index.d.ts +17 -419
- package/dist/providers/terminal/index.js +5 -3
- package/dist/providers/whatsapp-business/index.d.ts +1 -1
- package/dist/providers/whatsapp-business/index.js +6 -4
- package/dist/types-BD0-kKyv.d.ts +82 -0
- package/dist/{types-DcQ5a7PK.d.ts → types-Bje8aq1k.d.ts} +34 -4
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
import { createRequire as __spectrumCreateRequire } from "node:module"; const require = __spectrumCreateRequire(import.meta.url);
|
|
2
2
|
import {
|
|
3
|
-
group,
|
|
4
3
|
richlink
|
|
5
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-6BI4PFTP.js";
|
|
5
|
+
import {
|
|
6
|
+
FUSOR_MESSAGES_CHANNEL,
|
|
7
|
+
fusor,
|
|
8
|
+
fusorEvent,
|
|
9
|
+
isFusorClient,
|
|
10
|
+
isFusorEvent
|
|
11
|
+
} from "./chunk-34FQGGD7.js";
|
|
12
|
+
import {
|
|
13
|
+
group
|
|
14
|
+
} from "./chunk-3B4QH4JG.js";
|
|
6
15
|
import {
|
|
7
16
|
voice
|
|
8
17
|
} from "./chunk-NNY6LMSC.js";
|
|
@@ -12,23 +21,26 @@ import {
|
|
|
12
21
|
} from "./chunk-2D27WW5B.js";
|
|
13
22
|
import {
|
|
14
23
|
SpectrumCloudError,
|
|
24
|
+
cloud
|
|
25
|
+
} from "./chunk-3GEJYGZK.js";
|
|
26
|
+
import {
|
|
27
|
+
contact
|
|
28
|
+
} from "./chunk-U7AWXDH6.js";
|
|
29
|
+
import {
|
|
15
30
|
broadcast,
|
|
16
|
-
cloud,
|
|
17
31
|
createAsyncQueue,
|
|
18
32
|
mergeStreams,
|
|
19
33
|
stream
|
|
20
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-5XEFJBN2.js";
|
|
21
35
|
import {
|
|
22
|
-
contact,
|
|
23
36
|
fromVCard,
|
|
24
37
|
toVCard
|
|
25
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-6UZFVXQF.js";
|
|
26
39
|
import {
|
|
27
40
|
UnsupportedError,
|
|
28
41
|
avatar,
|
|
29
42
|
buildSpace,
|
|
30
43
|
contentAttrs,
|
|
31
|
-
defineFusorPlatform,
|
|
32
44
|
definePlatform,
|
|
33
45
|
edit,
|
|
34
46
|
rename,
|
|
@@ -36,7 +48,7 @@ import {
|
|
|
36
48
|
senderAttrs,
|
|
37
49
|
typing,
|
|
38
50
|
wrapProviderMessage
|
|
39
|
-
} from "./chunk-
|
|
51
|
+
} from "./chunk-NGC4DJIX.js";
|
|
40
52
|
import {
|
|
41
53
|
__commonJS,
|
|
42
54
|
__esm,
|
|
@@ -6444,13 +6456,13 @@ var require_path = __commonJS({
|
|
|
6444
6456
|
return /^(?:\/|\w+:)/.test(path2);
|
|
6445
6457
|
}
|
|
6446
6458
|
);
|
|
6447
|
-
var
|
|
6459
|
+
var normalize2 = (
|
|
6448
6460
|
/**
|
|
6449
6461
|
* Normalizes the specified path.
|
|
6450
6462
|
* @param {string} path Path to normalize
|
|
6451
6463
|
* @returns {string} Normalized path
|
|
6452
6464
|
*/
|
|
6453
|
-
path.normalize = function
|
|
6465
|
+
path.normalize = function normalize3(path2) {
|
|
6454
6466
|
path2 = path2.replace(/\\/g, "/").replace(/\/{2,}/g, "/");
|
|
6455
6467
|
var parts = path2.split("/"), absolute = isAbsolute(path2), prefix = "";
|
|
6456
6468
|
if (absolute)
|
|
@@ -6473,12 +6485,12 @@ var require_path = __commonJS({
|
|
|
6473
6485
|
);
|
|
6474
6486
|
path.resolve = function resolve(originPath, includePath, alreadyNormalized) {
|
|
6475
6487
|
if (!alreadyNormalized)
|
|
6476
|
-
includePath =
|
|
6488
|
+
includePath = normalize2(includePath);
|
|
6477
6489
|
if (isAbsolute(includePath))
|
|
6478
6490
|
return includePath;
|
|
6479
6491
|
if (!alreadyNormalized)
|
|
6480
|
-
originPath =
|
|
6481
|
-
return (originPath = originPath.replace(/(?:\/|^)[^/]+$/, "")).length ?
|
|
6492
|
+
originPath = normalize2(originPath);
|
|
6493
|
+
return (originPath = originPath.replace(/(?:\/|^)[^/]+$/, "")).length ? normalize2(originPath + "/" + includePath) : includePath;
|
|
6482
6494
|
};
|
|
6483
6495
|
}
|
|
6484
6496
|
});
|
|
@@ -24195,6 +24207,162 @@ var require_src3 = __commonJS({
|
|
|
24195
24207
|
}
|
|
24196
24208
|
});
|
|
24197
24209
|
|
|
24210
|
+
// src/content/stream-text.ts
|
|
24211
|
+
import z from "zod";
|
|
24212
|
+
var streamTextSchema = z.object({
|
|
24213
|
+
type: z.literal("streamText"),
|
|
24214
|
+
// A single-consumption producer of normalized text deltas. The builder
|
|
24215
|
+
// closes over the normalized source; the platform driver calls it once.
|
|
24216
|
+
// Kept opaque to Zod via `z.custom` (same approach as `attachment.read`).
|
|
24217
|
+
stream: z.custom(
|
|
24218
|
+
(v) => typeof v === "function",
|
|
24219
|
+
{
|
|
24220
|
+
message: "streamText.stream must be a function returning AsyncIterable<string>"
|
|
24221
|
+
}
|
|
24222
|
+
)
|
|
24223
|
+
});
|
|
24224
|
+
var asRecord = (value) => typeof value === "object" && value !== null ? value : void 0;
|
|
24225
|
+
var SKIP_EVENT_TYPES = /* @__PURE__ */ new Set([
|
|
24226
|
+
"message_start",
|
|
24227
|
+
"message_delta",
|
|
24228
|
+
"message_stop",
|
|
24229
|
+
"content_block_start",
|
|
24230
|
+
"content_block_stop",
|
|
24231
|
+
"ping"
|
|
24232
|
+
]);
|
|
24233
|
+
var fromOpenAIResponses = (obj) => {
|
|
24234
|
+
const type = obj.type;
|
|
24235
|
+
if (typeof type !== "string" || !type.startsWith("response.")) {
|
|
24236
|
+
return;
|
|
24237
|
+
}
|
|
24238
|
+
if (type === "response.output_text.delta" && typeof obj.delta === "string") {
|
|
24239
|
+
return obj.delta;
|
|
24240
|
+
}
|
|
24241
|
+
return null;
|
|
24242
|
+
};
|
|
24243
|
+
var fromAnthropicDelta = (obj) => {
|
|
24244
|
+
if (obj.type !== "content_block_delta") {
|
|
24245
|
+
return;
|
|
24246
|
+
}
|
|
24247
|
+
const delta = asRecord(obj.delta);
|
|
24248
|
+
if (delta?.type === "text_delta" && typeof delta.text === "string") {
|
|
24249
|
+
return delta.text;
|
|
24250
|
+
}
|
|
24251
|
+
return null;
|
|
24252
|
+
};
|
|
24253
|
+
var fromAiSdkPart = (obj) => {
|
|
24254
|
+
if (obj.type !== "text-delta") {
|
|
24255
|
+
return;
|
|
24256
|
+
}
|
|
24257
|
+
if (typeof obj.textDelta === "string") {
|
|
24258
|
+
return obj.textDelta;
|
|
24259
|
+
}
|
|
24260
|
+
return typeof obj.text === "string" ? obj.text : null;
|
|
24261
|
+
};
|
|
24262
|
+
var fromOpenAIChat = (obj) => {
|
|
24263
|
+
if (!Array.isArray(obj.choices)) {
|
|
24264
|
+
return;
|
|
24265
|
+
}
|
|
24266
|
+
const delta = asRecord(asRecord(obj.choices[0])?.delta);
|
|
24267
|
+
const content = delta?.content;
|
|
24268
|
+
return typeof content === "string" ? content : null;
|
|
24269
|
+
};
|
|
24270
|
+
var fromControlEvent = (obj) => typeof obj.type === "string" && SKIP_EVENT_TYPES.has(obj.type) ? null : void 0;
|
|
24271
|
+
var OBJECT_EXTRACTORS = [
|
|
24272
|
+
fromOpenAIResponses,
|
|
24273
|
+
fromAnthropicDelta,
|
|
24274
|
+
fromAiSdkPart,
|
|
24275
|
+
fromOpenAIChat,
|
|
24276
|
+
fromControlEvent
|
|
24277
|
+
];
|
|
24278
|
+
var defaultExtract = (chunk) => {
|
|
24279
|
+
if (typeof chunk === "string") {
|
|
24280
|
+
return chunk;
|
|
24281
|
+
}
|
|
24282
|
+
const record = asRecord(chunk);
|
|
24283
|
+
if (!record) {
|
|
24284
|
+
throw new Error(
|
|
24285
|
+
`streamText: cannot extract a text delta from a ${typeof chunk} chunk. Pass { extract } to map your stream's chunks to text.`
|
|
24286
|
+
);
|
|
24287
|
+
}
|
|
24288
|
+
for (const extractor of OBJECT_EXTRACTORS) {
|
|
24289
|
+
const result = extractor(record);
|
|
24290
|
+
if (result !== void 0) {
|
|
24291
|
+
return result;
|
|
24292
|
+
}
|
|
24293
|
+
}
|
|
24294
|
+
throw new Error(
|
|
24295
|
+
`streamText: unrecognized chunk shape (type=${String(record.type)}). Pass an { extract } function to map your provider's chunk to a text delta.`
|
|
24296
|
+
);
|
|
24297
|
+
};
|
|
24298
|
+
var isReadableStream = (value) => typeof value?.getReader === "function";
|
|
24299
|
+
var isAsyncIterable = (value) => typeof value?.[Symbol.asyncIterator] === "function";
|
|
24300
|
+
async function* readableToAsync(source) {
|
|
24301
|
+
if (isAsyncIterable(source)) {
|
|
24302
|
+
yield* source;
|
|
24303
|
+
return;
|
|
24304
|
+
}
|
|
24305
|
+
const reader = source.getReader();
|
|
24306
|
+
try {
|
|
24307
|
+
while (true) {
|
|
24308
|
+
const { done, value } = await reader.read();
|
|
24309
|
+
if (done) {
|
|
24310
|
+
return;
|
|
24311
|
+
}
|
|
24312
|
+
yield value;
|
|
24313
|
+
}
|
|
24314
|
+
} finally {
|
|
24315
|
+
reader.releaseLock();
|
|
24316
|
+
}
|
|
24317
|
+
}
|
|
24318
|
+
var resolveChunkIterable = (source) => {
|
|
24319
|
+
const textStream = source.textStream;
|
|
24320
|
+
if (textStream != null) {
|
|
24321
|
+
if (isReadableStream(textStream)) {
|
|
24322
|
+
return readableToAsync(textStream);
|
|
24323
|
+
}
|
|
24324
|
+
if (isAsyncIterable(textStream)) {
|
|
24325
|
+
return textStream;
|
|
24326
|
+
}
|
|
24327
|
+
throw new Error(
|
|
24328
|
+
"streamText: `.textStream` must be an AsyncIterable or a ReadableStream."
|
|
24329
|
+
);
|
|
24330
|
+
}
|
|
24331
|
+
if (isReadableStream(source)) {
|
|
24332
|
+
return readableToAsync(source);
|
|
24333
|
+
}
|
|
24334
|
+
if (isAsyncIterable(source)) {
|
|
24335
|
+
return source;
|
|
24336
|
+
}
|
|
24337
|
+
throw new Error(
|
|
24338
|
+
"streamText: source must be an AsyncIterable, a ReadableStream, or an object with a `.textStream` (e.g. the AI SDK streamText() result)."
|
|
24339
|
+
);
|
|
24340
|
+
};
|
|
24341
|
+
var normalize = (source, options) => {
|
|
24342
|
+
const extract = options?.extract ? options.extract : defaultExtract;
|
|
24343
|
+
let consumed = false;
|
|
24344
|
+
return async function* normalized() {
|
|
24345
|
+
if (consumed) {
|
|
24346
|
+
throw new Error(
|
|
24347
|
+
"streamText: this source has already been consumed \u2014 a stream can only be sent once."
|
|
24348
|
+
);
|
|
24349
|
+
}
|
|
24350
|
+
consumed = true;
|
|
24351
|
+
for await (const chunk of resolveChunkIterable(source)) {
|
|
24352
|
+
const delta = extract(chunk);
|
|
24353
|
+
if (delta) {
|
|
24354
|
+
yield delta;
|
|
24355
|
+
}
|
|
24356
|
+
}
|
|
24357
|
+
};
|
|
24358
|
+
};
|
|
24359
|
+
var asStreamText = (input) => streamTextSchema.parse({ type: "streamText", stream: input.stream });
|
|
24360
|
+
function streamText(source, options) {
|
|
24361
|
+
return {
|
|
24362
|
+
build: async () => asStreamText({ stream: normalize(source, options) })
|
|
24363
|
+
};
|
|
24364
|
+
}
|
|
24365
|
+
|
|
24198
24366
|
// src/emoji/generated.ts
|
|
24199
24367
|
var GeneratedEmoji = {
|
|
24200
24368
|
_1stPlaceMedal: "\u{1F947}",
|
|
@@ -26124,21 +26292,6 @@ var aliases = {
|
|
|
26124
26292
|
};
|
|
26125
26293
|
var Emoji = { ...GeneratedEmoji, ...aliases };
|
|
26126
26294
|
|
|
26127
|
-
// src/fusor/types.ts
|
|
26128
|
-
var FUSOR_BRAND = /* @__PURE__ */ Symbol.for("spectrum.fusor.client");
|
|
26129
|
-
|
|
26130
|
-
// src/fusor/index.ts
|
|
26131
|
-
function fusor(platform, verify) {
|
|
26132
|
-
return {
|
|
26133
|
-
[FUSOR_BRAND]: true,
|
|
26134
|
-
platform,
|
|
26135
|
-
verify
|
|
26136
|
-
};
|
|
26137
|
-
}
|
|
26138
|
-
function isFusorClient(value) {
|
|
26139
|
-
return typeof value === "object" && value !== null && value[FUSOR_BRAND] === true;
|
|
26140
|
-
}
|
|
26141
|
-
|
|
26142
26295
|
// src/spectrum.ts
|
|
26143
26296
|
import {
|
|
26144
26297
|
createLogger as createLogger2,
|
|
@@ -26146,7 +26299,7 @@ import {
|
|
|
26146
26299
|
withSpan
|
|
26147
26300
|
} from "@photon-ai/otel";
|
|
26148
26301
|
import { RawInboundEvent as RawInboundEvent2 } from "@photon-ai/proto/photon/fusor/v1/inbound";
|
|
26149
|
-
import
|
|
26302
|
+
import z2 from "zod";
|
|
26150
26303
|
|
|
26151
26304
|
// src/build-env.ts
|
|
26152
26305
|
var SPECTRUM_SDK_VERSION = "local";
|
|
@@ -27282,6 +27435,23 @@ function combineReplies(outcomes) {
|
|
|
27282
27435
|
body
|
|
27283
27436
|
};
|
|
27284
27437
|
}
|
|
27438
|
+
function routeHandlerResult(result, handler, deliver) {
|
|
27439
|
+
if (result === void 0) {
|
|
27440
|
+
return;
|
|
27441
|
+
}
|
|
27442
|
+
const items = Array.isArray(result) ? result : [result];
|
|
27443
|
+
for (const item of items) {
|
|
27444
|
+
if (!isFusorEvent(item)) {
|
|
27445
|
+
deliver(item);
|
|
27446
|
+
continue;
|
|
27447
|
+
}
|
|
27448
|
+
if (item.name === FUSOR_MESSAGES_CHANNEL) {
|
|
27449
|
+
deliver(item.data);
|
|
27450
|
+
} else {
|
|
27451
|
+
handler.pushEvent(item.name, item.data);
|
|
27452
|
+
}
|
|
27453
|
+
}
|
|
27454
|
+
}
|
|
27285
27455
|
function runHandlerOnce(handler, parsedRequest, deliver = handler.pushMessage) {
|
|
27286
27456
|
return (async () => {
|
|
27287
27457
|
try {
|
|
@@ -27302,12 +27472,7 @@ function runHandlerOnce(handler, parsedRequest, deliver = handler.pushMessage) {
|
|
|
27302
27472
|
};
|
|
27303
27473
|
const result = await handler.messages({ payload, respond });
|
|
27304
27474
|
returned = true;
|
|
27305
|
-
|
|
27306
|
-
const records = Array.isArray(result) ? result : [result];
|
|
27307
|
-
for (const record of records) {
|
|
27308
|
-
deliver(record);
|
|
27309
|
-
}
|
|
27310
|
-
}
|
|
27475
|
+
routeHandlerResult(result, handler, deliver);
|
|
27311
27476
|
return { ok: true, reply: reply2 };
|
|
27312
27477
|
} catch (error) {
|
|
27313
27478
|
const errorReason = error instanceof Error ? error.message : String(error);
|
|
@@ -27403,7 +27568,7 @@ var FusorCore = class {
|
|
|
27403
27568
|
const { iterable: requestIterable, sink } = createRequestSink();
|
|
27404
27569
|
this.requestSink = sink;
|
|
27405
27570
|
sink.push({ init: { startSeq: 0 }, reply: void 0 });
|
|
27406
|
-
const metadata = Metadata().set("
|
|
27571
|
+
const metadata = Metadata().set("access_token", token);
|
|
27407
27572
|
const stream2 = client.subscribe(requestIterable, { metadata });
|
|
27408
27573
|
try {
|
|
27409
27574
|
for await (const response of stream2) {
|
|
@@ -27558,25 +27723,26 @@ function createStore() {
|
|
|
27558
27723
|
|
|
27559
27724
|
// src/spectrum.ts
|
|
27560
27725
|
var PHOTON_OTEL_ENDPOINT = "https://otlp.photon.codes";
|
|
27726
|
+
var STREAM_CLOSE_TIMEOUT_MS = 5e3;
|
|
27561
27727
|
var lifecycleLog = createLogger2("spectrum.lifecycle");
|
|
27562
27728
|
var ignoreCleanupError = () => void 0;
|
|
27563
|
-
var spectrumOptionsSchema =
|
|
27564
|
-
flattenGroups:
|
|
27729
|
+
var spectrumOptionsSchema = z2.object({
|
|
27730
|
+
flattenGroups: z2.boolean().optional()
|
|
27565
27731
|
}).optional();
|
|
27566
|
-
var spectrumConfigSchema =
|
|
27567
|
-
|
|
27568
|
-
projectId:
|
|
27569
|
-
projectSecret:
|
|
27570
|
-
providers:
|
|
27732
|
+
var spectrumConfigSchema = z2.union([
|
|
27733
|
+
z2.object({
|
|
27734
|
+
projectId: z2.string().min(1),
|
|
27735
|
+
projectSecret: z2.string().min(1),
|
|
27736
|
+
providers: z2.array(z2.custom()),
|
|
27571
27737
|
options: spectrumOptionsSchema,
|
|
27572
|
-
telemetry:
|
|
27738
|
+
telemetry: z2.boolean().optional()
|
|
27573
27739
|
}),
|
|
27574
|
-
|
|
27575
|
-
projectId:
|
|
27576
|
-
projectSecret:
|
|
27577
|
-
providers:
|
|
27740
|
+
z2.object({
|
|
27741
|
+
projectId: z2.undefined().optional(),
|
|
27742
|
+
projectSecret: z2.undefined().optional(),
|
|
27743
|
+
providers: z2.array(z2.custom()),
|
|
27578
27744
|
options: spectrumOptionsSchema,
|
|
27579
|
-
telemetry:
|
|
27745
|
+
telemetry: z2.boolean().optional()
|
|
27580
27746
|
})
|
|
27581
27747
|
]);
|
|
27582
27748
|
function bootstrapTelemetry(opts) {
|
|
@@ -27614,15 +27780,21 @@ async function Spectrum(options) {
|
|
|
27614
27780
|
const platformStates = /* @__PURE__ */ new Map();
|
|
27615
27781
|
const fusorMessageSources = /* @__PURE__ */ new Map();
|
|
27616
27782
|
const messageBroadcasters = /* @__PURE__ */ new Map();
|
|
27783
|
+
const fusorEventSources = /* @__PURE__ */ new Map();
|
|
27784
|
+
const eventBroadcasters = /* @__PURE__ */ new Map();
|
|
27617
27785
|
const customEventStreams = /* @__PURE__ */ new Map();
|
|
27618
27786
|
let stopped = false;
|
|
27619
|
-
const adaptIterable = (iterable) => stream((emit, end) => {
|
|
27787
|
+
const adaptIterable = (iterable, project) => stream((emit, end) => {
|
|
27620
27788
|
const iterator = iterable[Symbol.asyncIterator]();
|
|
27621
27789
|
const pump = (async () => {
|
|
27622
27790
|
try {
|
|
27623
27791
|
let result = await iterator.next();
|
|
27624
27792
|
while (!result.done) {
|
|
27625
|
-
|
|
27793
|
+
if (project) {
|
|
27794
|
+
await project(result.value, emit);
|
|
27795
|
+
} else {
|
|
27796
|
+
await emit(result.value);
|
|
27797
|
+
}
|
|
27626
27798
|
result = await iterator.next();
|
|
27627
27799
|
}
|
|
27628
27800
|
end();
|
|
@@ -27694,20 +27866,20 @@ async function Spectrum(options) {
|
|
|
27694
27866
|
projectConfig,
|
|
27695
27867
|
store
|
|
27696
27868
|
});
|
|
27697
|
-
|
|
27698
|
-
|
|
27699
|
-
|
|
27869
|
+
return adaptIterable(
|
|
27870
|
+
raw,
|
|
27871
|
+
async (record, emit) => {
|
|
27872
|
+
const tuples = await resolveRecordToMessages(record, {
|
|
27700
27873
|
client,
|
|
27701
27874
|
config,
|
|
27702
27875
|
definition,
|
|
27703
27876
|
store
|
|
27704
27877
|
});
|
|
27705
27878
|
for (const tuple of tuples) {
|
|
27706
|
-
|
|
27879
|
+
await emit(tuple);
|
|
27707
27880
|
}
|
|
27708
27881
|
}
|
|
27709
|
-
|
|
27710
|
-
return adaptIterable(bindSend());
|
|
27882
|
+
);
|
|
27711
27883
|
};
|
|
27712
27884
|
const getOrCreateMessageBroadcast = (state) => {
|
|
27713
27885
|
if (stopped) {
|
|
@@ -27723,6 +27895,24 @@ async function Spectrum(options) {
|
|
|
27723
27895
|
}
|
|
27724
27896
|
return broadcaster;
|
|
27725
27897
|
};
|
|
27898
|
+
const getOrCreateEventBroadcast = (platform, channel) => {
|
|
27899
|
+
const queue = fusorEventSources.get(platform)?.get(channel);
|
|
27900
|
+
if (!queue) {
|
|
27901
|
+
return;
|
|
27902
|
+
}
|
|
27903
|
+
if (stopped) {
|
|
27904
|
+
throw new Error(
|
|
27905
|
+
`Spectrum instance has been stopped; cannot subscribe to "${platform}" event "${channel}"`
|
|
27906
|
+
);
|
|
27907
|
+
}
|
|
27908
|
+
const key = `${platform}\0${channel}`;
|
|
27909
|
+
let broadcaster = eventBroadcasters.get(key);
|
|
27910
|
+
if (!broadcaster) {
|
|
27911
|
+
broadcaster = broadcast(adaptIterable(queue.iterable));
|
|
27912
|
+
eventBroadcasters.set(key, broadcaster);
|
|
27913
|
+
}
|
|
27914
|
+
return broadcaster;
|
|
27915
|
+
};
|
|
27726
27916
|
await withSpan(
|
|
27727
27917
|
"spectrum.init",
|
|
27728
27918
|
{
|
|
@@ -27744,6 +27934,7 @@ async function Spectrum(options) {
|
|
|
27744
27934
|
config: userConfig,
|
|
27745
27935
|
projectId,
|
|
27746
27936
|
projectSecret,
|
|
27937
|
+
projectConfig,
|
|
27747
27938
|
store
|
|
27748
27939
|
})
|
|
27749
27940
|
);
|
|
@@ -27755,7 +27946,13 @@ async function Spectrum(options) {
|
|
|
27755
27946
|
};
|
|
27756
27947
|
platformStates.set(def.name, {
|
|
27757
27948
|
...state,
|
|
27758
|
-
|
|
27949
|
+
projectConfig,
|
|
27950
|
+
subscribeMessages: () => getOrCreateMessageBroadcast(state).subscribe(),
|
|
27951
|
+
// Fanout subscription to a fusor event channel. Returns undefined for
|
|
27952
|
+
// regular platforms (no per-channel queue) — callers fall back to the
|
|
27953
|
+
// producer path. Resolved lazily, after the fusor bootstrap below has
|
|
27954
|
+
// created the per-(platform, channel) queues.
|
|
27955
|
+
subscribeEvent: (channel) => getOrCreateEventBroadcast(def.name, channel)?.subscribe()
|
|
27759
27956
|
});
|
|
27760
27957
|
}
|
|
27761
27958
|
}
|
|
@@ -27778,10 +27975,36 @@ async function Spectrum(options) {
|
|
|
27778
27975
|
continue;
|
|
27779
27976
|
}
|
|
27780
27977
|
const userMessages = runtime.definition.messages;
|
|
27978
|
+
const declaredEvents = runtime.definition.events ?? {};
|
|
27979
|
+
const eventQueues = /* @__PURE__ */ new Map();
|
|
27980
|
+
for (const channel of Object.keys(declaredEvents)) {
|
|
27981
|
+
eventQueues.set(channel, createAsyncQueue());
|
|
27982
|
+
}
|
|
27983
|
+
fusorEventSources.set(name, eventQueues);
|
|
27781
27984
|
const handler = {
|
|
27782
27985
|
verify: client.verify,
|
|
27783
|
-
|
|
27784
|
-
|
|
27986
|
+
// Enrich the transport-level `{ payload, respond }` ctx with the same
|
|
27987
|
+
// runtime context every other platform callback receives, so fusor
|
|
27988
|
+
// handlers can read config/store/projectConfig directly instead of
|
|
27989
|
+
// smuggling state through the payload.
|
|
27990
|
+
messages: async (ctx) => userMessages({
|
|
27991
|
+
...ctx,
|
|
27992
|
+
config: runtime.config,
|
|
27993
|
+
store: runtime.store,
|
|
27994
|
+
projectConfig: runtime.projectConfig
|
|
27995
|
+
}),
|
|
27996
|
+
pushMessage: (record) => queue.push(record),
|
|
27997
|
+
pushEvent: (channel, data) => {
|
|
27998
|
+
const eventQueue = eventQueues.get(channel);
|
|
27999
|
+
if (!eventQueue) {
|
|
28000
|
+
lifecycleLog.warn(
|
|
28001
|
+
`spectrum: fusorEvent("${channel}", \u2026) names a channel not declared in "${name}".events; dropping`,
|
|
28002
|
+
{ platform: name, channel }
|
|
28003
|
+
);
|
|
28004
|
+
return;
|
|
28005
|
+
}
|
|
28006
|
+
eventQueue.push(data);
|
|
28007
|
+
}
|
|
27785
28008
|
};
|
|
27786
28009
|
fusorCore.register(client.platform, handler);
|
|
27787
28010
|
}
|
|
@@ -27828,30 +28051,35 @@ async function Spectrum(options) {
|
|
|
27828
28051
|
const providerStreams = [];
|
|
27829
28052
|
for (const state of platformStates.values()) {
|
|
27830
28053
|
const { client, config, definition, store } = state;
|
|
27831
|
-
|
|
27832
|
-
if (!
|
|
27833
|
-
|
|
27834
|
-
|
|
27835
|
-
|
|
27836
|
-
client,
|
|
27837
|
-
config,
|
|
27838
|
-
projectConfig,
|
|
27839
|
-
store
|
|
27840
|
-
});
|
|
27841
|
-
const annotatePlatform = async function* () {
|
|
27842
|
-
for await (const value of providerEvents) {
|
|
27843
|
-
const annotated = await withSpan(
|
|
27844
|
-
"spectrum.event",
|
|
27845
|
-
{
|
|
27846
|
-
"spectrum.provider": definition.name,
|
|
27847
|
-
"spectrum.event.name": eventName
|
|
27848
|
-
},
|
|
27849
|
-
() => ({ ...value, platform: definition.name })
|
|
27850
|
-
);
|
|
27851
|
-
yield annotated;
|
|
28054
|
+
let source = state.subscribeEvent?.(eventName);
|
|
28055
|
+
if (!source) {
|
|
28056
|
+
const producer = definition.events?.[eventName];
|
|
28057
|
+
if (typeof producer !== "function") {
|
|
28058
|
+
continue;
|
|
27852
28059
|
}
|
|
27853
|
-
|
|
27854
|
-
|
|
28060
|
+
source = producer({ client, config, projectConfig, store });
|
|
28061
|
+
}
|
|
28062
|
+
const providerEvents = source;
|
|
28063
|
+
providerStreams.push(
|
|
28064
|
+
adaptIterable(
|
|
28065
|
+
providerEvents,
|
|
28066
|
+
async (value, emit2) => {
|
|
28067
|
+
const annotated = await withSpan(
|
|
28068
|
+
"spectrum.event",
|
|
28069
|
+
{
|
|
28070
|
+
"spectrum.provider": definition.name,
|
|
28071
|
+
"spectrum.event.name": eventName
|
|
28072
|
+
},
|
|
28073
|
+
// Object payloads are flattened and tagged with `platform`. A
|
|
28074
|
+
// primitive/null payload can't be spread (a string would mangle
|
|
28075
|
+
// into indexed chars, a number/bool would vanish), so wrap it
|
|
28076
|
+
// under `payload` instead.
|
|
28077
|
+
() => typeof value === "object" && value !== null ? { ...value, platform: definition.name } : { platform: definition.name, payload: value }
|
|
28078
|
+
);
|
|
28079
|
+
await emit2(annotated);
|
|
28080
|
+
}
|
|
28081
|
+
)
|
|
28082
|
+
);
|
|
27855
28083
|
}
|
|
27856
28084
|
const merged = mergeStreams(providerStreams);
|
|
27857
28085
|
const pump = (async () => {
|
|
@@ -27870,6 +28098,18 @@ async function Spectrum(options) {
|
|
|
27870
28098
|
};
|
|
27871
28099
|
});
|
|
27872
28100
|
const messagesStream = createMessagesStream();
|
|
28101
|
+
const closeFusorSources = () => {
|
|
28102
|
+
for (const queue of fusorMessageSources.values()) {
|
|
28103
|
+
queue.close();
|
|
28104
|
+
}
|
|
28105
|
+
fusorMessageSources.clear();
|
|
28106
|
+
for (const queues of fusorEventSources.values()) {
|
|
28107
|
+
for (const queue of queues.values()) {
|
|
28108
|
+
queue.close();
|
|
28109
|
+
}
|
|
28110
|
+
}
|
|
28111
|
+
fusorEventSources.clear();
|
|
28112
|
+
};
|
|
27873
28113
|
const stopOnce = async () => {
|
|
27874
28114
|
if (stopped) {
|
|
27875
28115
|
return;
|
|
@@ -27884,13 +28124,31 @@ async function Spectrum(options) {
|
|
|
27884
28124
|
...Array.from(
|
|
27885
28125
|
messageBroadcasters.values(),
|
|
27886
28126
|
(broadcaster) => broadcaster.close()
|
|
28127
|
+
),
|
|
28128
|
+
...Array.from(
|
|
28129
|
+
eventBroadcasters.values(),
|
|
28130
|
+
(broadcaster) => broadcaster.close()
|
|
27887
28131
|
)
|
|
27888
28132
|
];
|
|
27889
28133
|
process.off("SIGINT", handleSignal);
|
|
27890
28134
|
process.off("SIGTERM", handleSignal);
|
|
27891
28135
|
const streamCloseStart = performance.now();
|
|
27892
|
-
|
|
27893
|
-
|
|
28136
|
+
const streamSettled = Promise.allSettled(streamShutdowns);
|
|
28137
|
+
let streamTimedOut = false;
|
|
28138
|
+
await Promise.race([
|
|
28139
|
+
streamSettled,
|
|
28140
|
+
new Promise((resolve) => {
|
|
28141
|
+
setTimeout(() => {
|
|
28142
|
+
streamTimedOut = true;
|
|
28143
|
+
resolve();
|
|
28144
|
+
}, STREAM_CLOSE_TIMEOUT_MS).unref();
|
|
28145
|
+
})
|
|
28146
|
+
]);
|
|
28147
|
+
if (streamTimedOut) {
|
|
28148
|
+
lifecycleLog.warn("stream close timed out; proceeding to teardown", {
|
|
28149
|
+
timeoutMs: STREAM_CLOSE_TIMEOUT_MS
|
|
28150
|
+
});
|
|
28151
|
+
}
|
|
27894
28152
|
let fusorCloseMs = 0;
|
|
27895
28153
|
if (fusorCore) {
|
|
27896
28154
|
const fusorCloseStart = performance.now();
|
|
@@ -27901,10 +28159,7 @@ async function Spectrum(options) {
|
|
|
27901
28159
|
lifecycleLog.warn("fusor core close failed", { error });
|
|
27902
28160
|
});
|
|
27903
28161
|
fusorCloseMs = Math.round(performance.now() - fusorCloseStart);
|
|
27904
|
-
|
|
27905
|
-
queue.close();
|
|
27906
|
-
}
|
|
27907
|
-
fusorMessageSources.clear();
|
|
28162
|
+
closeFusorSources();
|
|
27908
28163
|
}
|
|
27909
28164
|
const clientShutdowns = [];
|
|
27910
28165
|
for (const state of platformStates.values()) {
|
|
@@ -27928,8 +28183,11 @@ async function Spectrum(options) {
|
|
|
27928
28183
|
const clientCloseStart = performance.now();
|
|
27929
28184
|
await Promise.allSettled(clientShutdowns);
|
|
27930
28185
|
const clientCloseMs = Math.round(performance.now() - clientCloseStart);
|
|
28186
|
+
await streamSettled.catch(() => void 0);
|
|
28187
|
+
const streamCloseMs = Math.round(performance.now() - streamCloseStart);
|
|
27931
28188
|
customEventStreams.clear();
|
|
27932
28189
|
messageBroadcasters.clear();
|
|
28190
|
+
eventBroadcasters.clear();
|
|
27933
28191
|
platformStates.clear();
|
|
27934
28192
|
lifecycleLog.info("Spectrum stopped", {
|
|
27935
28193
|
providers: providerNames,
|
|
@@ -28102,13 +28360,14 @@ export {
|
|
|
28102
28360
|
cloud,
|
|
28103
28361
|
contact,
|
|
28104
28362
|
custom,
|
|
28105
|
-
defineFusorPlatform,
|
|
28106
28363
|
definePlatform,
|
|
28107
28364
|
edit,
|
|
28108
28365
|
fromVCard,
|
|
28109
28366
|
fusor,
|
|
28367
|
+
fusorEvent,
|
|
28110
28368
|
group,
|
|
28111
28369
|
isFusorClient,
|
|
28370
|
+
isFusorEvent,
|
|
28112
28371
|
mergeStreams,
|
|
28113
28372
|
option,
|
|
28114
28373
|
poll,
|
|
@@ -28118,6 +28377,7 @@ export {
|
|
|
28118
28377
|
resolveContents,
|
|
28119
28378
|
richlink,
|
|
28120
28379
|
stream,
|
|
28380
|
+
streamText,
|
|
28121
28381
|
text,
|
|
28122
28382
|
toVCard,
|
|
28123
28383
|
typing,
|
package/dist/manifest.json
CHANGED