@spectrum-ts/core 9.1.0 → 9.3.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/authoring.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { At as asReply, Cn as asCustom, Ct as asText, Et as asRichlink, Ft as renameSchema, H as ProviderMessageRecord, Hn as PhotoInput, Jt as asMarkdown, Lt as asRead, Mt as replySchema, Nn as asContact, Sn as reactionSchema, U as ProviderUserRecord, Un as buildPhotoAction, Ut as asPoll, Vn as avatarSchema, Wn as photoActionSchema, Wt as asPollOption, _n as removeMemberSchema, _t as asVoice, bn as asReaction, hn as leaveSpaceSchema, pn as addMemberSchema, q as ManagedStream, r as asAttachment, rn as groupSchema, tn as asGroup } from "./attachment-
|
|
1
|
+
import { At as asReply, Cn as asCustom, Ct as asText, Et as asRichlink, Ft as renameSchema, H as ProviderMessageRecord, Hn as PhotoInput, Jt as asMarkdown, Lt as asRead, Mt as replySchema, Nn as asContact, Sn as reactionSchema, U as ProviderUserRecord, Un as buildPhotoAction, Ut as asPoll, Vn as avatarSchema, Wn as photoActionSchema, Wt as asPollOption, _n as removeMemberSchema, _t as asVoice, bn as asReaction, hn as leaveSpaceSchema, pn as addMemberSchema, q as ManagedStream, r as asAttachment, rn as groupSchema, tn as asGroup } from "./attachment-ChqzKngn.js";
|
|
2
2
|
import z from "zod";
|
|
3
3
|
import { FetchSpanOptions, LogAttrs, LogAttrs as LogAttrs$1, LogLevel, PhotonLogger, SanitizeUrlOptions, createLogger, sanitizeEmail, sanitizeErrorMessage, sanitizePhone, sanitizeUrl, setLogLevel } from "@photon-ai/otel";
|
|
4
4
|
import { Token } from "marked";
|
|
@@ -30,6 +30,61 @@ declare const ensureM4a: (buffer: Buffer, mimeType: string) => Promise<{
|
|
|
30
30
|
duration?: number;
|
|
31
31
|
}>;
|
|
32
32
|
//#endregion
|
|
33
|
+
//#region src/utils/env.d.ts
|
|
34
|
+
/**
|
|
35
|
+
* Build a Spectrum config env-var name from a channel and a key, centralizing
|
|
36
|
+
* the `SPECTRUM_<CHANNEL>_<KEY>` convention so the prefix can't drift or be
|
|
37
|
+
* mistyped per field. Both segments are joined verbatim (callers pass them
|
|
38
|
+
* already upper-snake-cased), e.g. `envFor("TELEGRAM", "BOT_TOKEN")` →
|
|
39
|
+
* `"SPECTRUM_TELEGRAM_BOT_TOKEN"`.
|
|
40
|
+
*/
|
|
41
|
+
declare const envFor: (channel: string, key: string) => string;
|
|
42
|
+
/**
|
|
43
|
+
* Wrap a config-field schema so it falls back to an environment variable when
|
|
44
|
+
* the field is omitted. Precedence is **explicit value > env var > the inner
|
|
45
|
+
* schema's own default/required check**:
|
|
46
|
+
*
|
|
47
|
+
* - An explicit value (anything other than `undefined`) is passed straight
|
|
48
|
+
* through — a caller-supplied config always wins over the environment.
|
|
49
|
+
* - When the field is `undefined`, `process.env[envKey]` is substituted. An
|
|
50
|
+
* env var set to the empty string is treated as unset (a common deploy
|
|
51
|
+
* footgun) so it doesn't satisfy a `.min(1)` by accident.
|
|
52
|
+
* - The inner `schema` then validates the resolved value, keeping its exact
|
|
53
|
+
* semantics — regex, `.min(1)`, `.url()`, `.optional()`, `.default(...)`.
|
|
54
|
+
* When both the field and the env var are absent, a required inner schema
|
|
55
|
+
* raises its normal "required" error.
|
|
56
|
+
*
|
|
57
|
+
* The output type is the inner schema's output type, so call sites are
|
|
58
|
+
* unchanged. Because substitution only happens on `undefined`, wrapping a
|
|
59
|
+
* field inside a `z.object` leaves it a plain key on the parsed result — union
|
|
60
|
+
* discriminators like `"accessToken" in config` keep working.
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```ts
|
|
64
|
+
* botToken: fromEnv("SPECTRUM_TELEGRAM_BOT_TOKEN", z.string().regex(BOT_TOKEN_PATTERN)),
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
declare const fromEnv: <T extends z.ZodType>(envKey: string, schema: T) => z.ZodPreprocess<T>;
|
|
68
|
+
/**
|
|
69
|
+
* Rewrite a platform's config schema so every string-leaf field falls back to
|
|
70
|
+
* `SPECTRUM_<PLATFORM>_<KEY>` when omitted which is the equivalent of
|
|
71
|
+
* hand-wrapping each field with {@link fromEnv}. Called once by
|
|
72
|
+
* `definePlatform`, so adapters declare plain Zod and get env fallback for free.
|
|
73
|
+
*
|
|
74
|
+
* - **object**: each string-leaf field is wrapped with `fromEnv`; non-string
|
|
75
|
+
* fields (records, arrays, nested objects, booleans, numbers) are left as-is.
|
|
76
|
+
* The object is only reconstructed when a field actually changed, and
|
|
77
|
+
* `.strict()` is preserved so an empty strict "cloud" branch is untouched.
|
|
78
|
+
* - **union**: each option is rewritten independently, so an env value only
|
|
79
|
+
* satisfies the branch that declares that field (a direct/cloud union stays
|
|
80
|
+
* correctly discriminated).
|
|
81
|
+
* - anything else is returned unchanged.
|
|
82
|
+
*
|
|
83
|
+
* The output type is identical to the input schema's. Only omitted fields gain
|
|
84
|
+
* an env fallback which means that `z.infer` and downstream contexts are unaffected.
|
|
85
|
+
*/
|
|
86
|
+
declare const envAwareConfig: <T extends z.ZodType>(name: string, schema: T) => T;
|
|
87
|
+
//#endregion
|
|
33
88
|
//#region src/utils/instrumented-fetch.d.ts
|
|
34
89
|
/**
|
|
35
90
|
* Build a fetch that traces spectrum's OWN outbound HTTP: each request through
|
|
@@ -123,4 +178,4 @@ declare const resumableOrderedStream: <TLive, TMissed, TOutput>(options: Resumab
|
|
|
123
178
|
*/
|
|
124
179
|
declare function errorAttrs(error: unknown, prefix?: string): LogAttrs$1;
|
|
125
180
|
//#endregion
|
|
126
|
-
export { type CloseableAsyncIterable, type LogAttrs, type LogLevel, type PhotoInput, type PhotonLogger, type ProviderMessageRecord, type ProviderUserRecord, type ResumableStreamItem, type SanitizeUrlOptions, addMemberSchema, asAttachment, asContact, asCustom, asGroup, asMarkdown, asPoll, asPollOption, asReaction, asRead, asReply, asRichlink, asText, asVoice, avatarSchema, buildPhotoAction, createLogger, ensureM4a, errorAttrs, groupSchema, leaveSpaceSchema, messageEffectSchema, photoActionSchema, reactionSchema, removeMemberSchema, renameSchema, renderInlineTokens, replySchema, resumableOrderedStream, sanitizeEmail, sanitizeErrorMessage, sanitizePhone, sanitizeUrl, setLogLevel, tracedFetch };
|
|
181
|
+
export { type CloseableAsyncIterable, type LogAttrs, type LogLevel, type PhotoInput, type PhotonLogger, type ProviderMessageRecord, type ProviderUserRecord, type ResumableStreamItem, type SanitizeUrlOptions, addMemberSchema, asAttachment, asContact, asCustom, asGroup, asMarkdown, asPoll, asPollOption, asReaction, asRead, asReply, asRichlink, asText, asVoice, avatarSchema, buildPhotoAction, createLogger, ensureM4a, envAwareConfig, envFor, errorAttrs, fromEnv, groupSchema, leaveSpaceSchema, messageEffectSchema, photoActionSchema, reactionSchema, removeMemberSchema, renameSchema, renderInlineTokens, replySchema, resumableOrderedStream, sanitizeEmail, sanitizeErrorMessage, sanitizePhone, sanitizeUrl, setLogLevel, tracedFetch };
|
package/dist/authoring.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { B as markdownSchema, C as asRead, D as reactionSchema, F as leaveSpaceSchema, G as asText, L as removeMemberSchema, N as addMemberSchema, O as asPoll, Q as asContact, R as asMarkdown, S as renameSchema, T as asReaction, U as groupSchema, V as asGroup, X as asCustom, at as photoActionSchema, b as replySchema, ct as attachmentSchema, d as envAwareConfig, f as envFor, g as asRichlink, i as stream, it as buildPhotoAction, k as asPollOption, l as renderInlineTokens, m as asVoice, o as errorAttrs, ot as asAttachment, p as fromEnv, q as textSchema, rt as avatarSchema, ut as tracedFetch, v as asReply } from "./stream-DAfZ5DFR.js";
|
|
2
2
|
import z from "zod";
|
|
3
3
|
import { createLogger, createLogger as createLogger$1, sanitizeEmail, sanitizeErrorMessage, sanitizePhone, sanitizeUrl, setLogLevel } from "@photon-ai/otel";
|
|
4
4
|
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
|
@@ -422,4 +422,4 @@ const resumableOrderedStream = (options) => stream((emit, end) => {
|
|
|
422
422
|
};
|
|
423
423
|
});
|
|
424
424
|
//#endregion
|
|
425
|
-
export { addMemberSchema, asAttachment, asContact, asCustom, asGroup, asMarkdown, asPoll, asPollOption, asReaction, asRead, asReply, asRichlink, asText, asVoice, avatarSchema, buildPhotoAction, createLogger, ensureM4a, errorAttrs, groupSchema, leaveSpaceSchema, messageEffectSchema, photoActionSchema, reactionSchema, removeMemberSchema, renameSchema, renderInlineTokens, replySchema, resumableOrderedStream, sanitizeEmail, sanitizeErrorMessage, sanitizePhone, sanitizeUrl, setLogLevel, tracedFetch };
|
|
425
|
+
export { addMemberSchema, asAttachment, asContact, asCustom, asGroup, asMarkdown, asPoll, asPollOption, asReaction, asRead, asReply, asRichlink, asText, asVoice, avatarSchema, buildPhotoAction, createLogger, ensureM4a, envAwareConfig, envFor, errorAttrs, fromEnv, groupSchema, leaveSpaceSchema, messageEffectSchema, photoActionSchema, reactionSchema, removeMemberSchema, renameSchema, renderInlineTokens, replySchema, resumableOrderedStream, sanitizeEmail, sanitizeErrorMessage, sanitizePhone, sanitizeUrl, setLogLevel, tracedFetch };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { $ as DedicatedTokenData, $t as TextStreamOptions, A as isFusorEvent, An as ContactName, B as PlatformUser, Bn as avatar, Bt as PollChoice, C as FusorVerify, D as WebhookRawResult, Dn as ContactDetails, Dt as richlink, E as WebhookRawRequest, En as ContactAddress, F as PlatformInstance, Fn as AgentSender, G as SpaceNamespace, Gn as App, Gt as option, Ht as PollOption, I as PlatformMessage, In as User, It as Read, J as broadcast, Jn as
|
|
2
|
-
export { type AddMember, type AgentSender, type AnyPlatformDef, type App, type AppLayout, type AppUrl, type Attachment, type AttachmentInput, type Avatar, type AvatarData, type AvatarInput, type Broadcaster, type CloudPlatform, type Contact, type ContactAddress, type ContactDetails, type ContactEmail, type ContactInput, type ContactName, type ContactOrg, type ContactPhone, type Content, type ContentBuilder, type ContentInput, type DedicatedTokenData, type DeltaExtractor, type Edit, Emoji, type EmojiKey, type EventProducer, type FusorClient, type FusorEvent, type FusorMessages, type FusorMessagesCtx, type FusorMessagesReturn, type FusorReply, type FusorRespond, type FusorTokenData, type FusorVerify, type FusorVerifyRequest, type Group, type ImessageInfoData, type LeaveSpace, type ManagedStream, type Markdown, type MemberInput, type Message, type Platform, type PlatformDef, type PlatformInstance, type PlatformMessage, type PlatformProviderConfig, type PlatformRuntime, type PlatformSpace, type PlatformStatus, type PlatformUser, type PlatformsData, type Poll, type PollChoice, type PollChoiceInput, type PollOption, type ProjectData, type ProjectProfile, type ProviderMessage, type Reaction, type ReactionBuilder, type Read, type RemoveMember, type Rename, type Reply, type Richlink, type SchemaMessage, type SharedTokenData, type SlackTeamMeta, type SlackTokenData, type Space, type SpaceNamespace, Spectrum, SpectrumCloudError, type SpectrumInstance, type Store, type StreamText, type StreamTextSource, type SubscriptionData, type SubscriptionStatus, type TextStreamOptions, type TokenData, type Typing, type Unsend, UnsupportedError, type UnsupportedKind, type User, type Voice, type WebhookHandler, type WebhookRawRequest, type WebhookRawResult, addMember, app, appLayoutSchema, attachment, avatar, broadcast, cloud, contact, custom, definePlatform, edit, fromVCard, fusor, fusorEvent, group, isFusorClient, isFusorEvent, leaveSpace, markdown, mergeStreams, option, poll, reaction, read, removeMember, rename, reply, resolveContents, richlink, stream, text, toVCard, typing, unsend, voice };
|
|
1
|
+
import { $ as DedicatedTokenData, $t as TextStreamOptions, A as isFusorEvent, An as ContactName, B as PlatformUser, Bn as avatar, Bt as PollChoice, C as FusorVerify, D as WebhookRawResult, Dn as ContactDetails, Dt as richlink, E as WebhookRawRequest, En as ContactAddress, F as PlatformInstance, Fn as AgentSender, G as SpaceNamespace, Gn as App, Gt as option, Ht as PollOption, I as PlatformMessage, In as User, It as Read, J as broadcast, Jn as AppUrl, K as Broadcaster, Kn as AppLayout, Kt as poll, L as PlatformProviderConfig, Ln as Avatar, M as EventProducer, Mn as ContactPhone, N as Platform, Nt as Rename, O as FusorEvent, On as ContactEmail, Ot as resolveContents, P as PlatformDef, Pn as contact, Pt as rename, Q as CloudPlatform, Qt as StreamTextSource, R as PlatformRuntime, Rn as AvatarData, Rt as read, S as FusorRespond, St as typing, T as WebhookHandler, Tn as Contact, Tt as Richlink, V as ProviderMessage, Vt as PollChoiceInput, W as SchemaMessage, X as stream, Xn as appLayoutSchema, Xt as DeltaExtractor, Y as mergeStreams, Yn as app, Yt as markdown, Z as Store, Zt as StreamText, _ as FusorClient, a as Content, an as edit, at as ProjectProfile, b as FusorMessagesReturn, bt as unsend, c as fromVCard, cn as AddMember, ct as SlackTokenData, d as UnsupportedKind, dn as RemoveMember, dt as SubscriptionStatus, en as Group, et as FusorTokenData, f as Spectrum, fn as addMember, ft as TokenData, g as isFusorClient, gn as removeMember, gt as Voice, h as fusor, ht as EmojiKey, i as attachment, in as Edit, it as ProjectData, j as AnyPlatformDef, jn as ContactOrg, jt as reply, k as fusorEvent, kn as ContactInput, kt as Reply, l as toVCard, ln as LeaveSpace, lt as SpectrumCloudError, m as definePlatform, mn as leaveSpace, mt as Emoji, n as AttachmentInput, nn as group, nt as PlatformStatus, o as ContentBuilder, on as Message, ot as SharedTokenData, p as SpectrumInstance, pt as cloud, q as ManagedStream, qn as AppOptions, qt as Markdown, rt as PlatformsData, s as ContentInput, sn as Space, st as SlackTeamMeta, t as Attachment, tt as ImessageInfoData, u as UnsupportedError, un as MemberInput, ut as SubscriptionData, v as FusorMessages, vn as Reaction, vt as voice, w as FusorVerifyRequest, wn as custom, wt as text, x as FusorReply, xn as reaction, xt as Typing, y as FusorMessagesCtx, yn as ReactionBuilder, yt as Unsend, z as PlatformSpace, zn as AvatarInput, zt as Poll } from "./attachment-ChqzKngn.js";
|
|
2
|
+
export { type AddMember, type AgentSender, type AnyPlatformDef, type App, type AppLayout, type AppOptions, type AppUrl, type Attachment, type AttachmentInput, type Avatar, type AvatarData, type AvatarInput, type Broadcaster, type CloudPlatform, type Contact, type ContactAddress, type ContactDetails, type ContactEmail, type ContactInput, type ContactName, type ContactOrg, type ContactPhone, type Content, type ContentBuilder, type ContentInput, type DedicatedTokenData, type DeltaExtractor, type Edit, Emoji, type EmojiKey, type EventProducer, type FusorClient, type FusorEvent, type FusorMessages, type FusorMessagesCtx, type FusorMessagesReturn, type FusorReply, type FusorRespond, type FusorTokenData, type FusorVerify, type FusorVerifyRequest, type Group, type ImessageInfoData, type LeaveSpace, type ManagedStream, type Markdown, type MemberInput, type Message, type Platform, type PlatformDef, type PlatformInstance, type PlatformMessage, type PlatformProviderConfig, type PlatformRuntime, type PlatformSpace, type PlatformStatus, type PlatformUser, type PlatformsData, type Poll, type PollChoice, type PollChoiceInput, type PollOption, type ProjectData, type ProjectProfile, type ProviderMessage, type Reaction, type ReactionBuilder, type Read, type RemoveMember, type Rename, type Reply, type Richlink, type SchemaMessage, type SharedTokenData, type SlackTeamMeta, type SlackTokenData, type Space, type SpaceNamespace, Spectrum, SpectrumCloudError, type SpectrumInstance, type Store, type StreamText, type StreamTextSource, type SubscriptionData, type SubscriptionStatus, type TextStreamOptions, type TokenData, type Typing, type Unsend, UnsupportedError, type UnsupportedKind, type User, type Voice, type WebhookHandler, type WebhookRawRequest, type WebhookRawResult, addMember, app, appLayoutSchema, attachment, avatar, broadcast, cloud, contact, custom, definePlatform, edit, fromVCard, fusor, fusorEvent, group, isFusorClient, isFusorEvent, leaveSpace, markdown, mergeStreams, option, poll, reaction, read, removeMember, rename, reply, resolveContents, richlink, stream, text, toVCard, typing, unsend, voice };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as
|
|
1
|
+
import { $ as contact, A as option, E as reaction, F as leaveSpaceSchema, G as asText, H as group, I as removeMember, J as StreamConsumedError, K as text, L as removeMemberSchema, M as addMember, N as addMemberSchema, P as leaveSpace, Q as asContact, R as asMarkdown, S as renameSchema, W as resolveContents, X as asCustom, Y as drainStreamText, Z as custom, _ as richlink, a as contentAttrs, c as markdownToPlainText, d as envAwareConfig, et as fromVCard, f as envFor, h as voice, i as stream, j as poll, lt as fetchUrlBytes, n as createAsyncQueue, nt as avatar, o as errorAttrs, ot as asAttachment, r as mergeStreams, rt as avatarSchema, s as senderAttrs, st as attachment, t as broadcast, tt as toVCard, u as classifyIdentifier, ut as tracedFetch, w as read, x as rename, y as reply, z as markdown } from "./stream-DAfZ5DFR.js";
|
|
2
2
|
import z from "zod";
|
|
3
3
|
import ogs from "open-graph-scraper";
|
|
4
4
|
import { createLogger, setLogLevel, setupOtel, withSpan } from "@photon-ai/otel";
|
|
@@ -89,7 +89,8 @@ const layoutAccessor = z.function({
|
|
|
89
89
|
const appSchema = z.object({
|
|
90
90
|
type: z.literal("app"),
|
|
91
91
|
url: urlAccessor,
|
|
92
|
-
layout: layoutAccessor
|
|
92
|
+
layout: layoutAccessor,
|
|
93
|
+
live: z.boolean().optional()
|
|
93
94
|
});
|
|
94
95
|
const memoize = (factory) => {
|
|
95
96
|
let cached;
|
|
@@ -136,7 +137,7 @@ const buildLayout = (metadata, url, image) => {
|
|
|
136
137
|
* across `url()` / `layout()`; fetch / parse failures resolve to a host-only
|
|
137
138
|
* caption (no throw, no retry).
|
|
138
139
|
*/
|
|
139
|
-
const asApp = (url) => {
|
|
140
|
+
const asApp = (url, options = {}) => {
|
|
140
141
|
const getUrl = resolveUrl(url);
|
|
141
142
|
const getMetadata = memoize(async () => fetchLinkMetadata(await getUrl()));
|
|
142
143
|
const getLayout = memoize(async () => {
|
|
@@ -154,11 +155,18 @@ const asApp = (url) => {
|
|
|
154
155
|
return appSchema.parse({
|
|
155
156
|
type: "app",
|
|
156
157
|
url: getUrl,
|
|
157
|
-
layout: getLayout
|
|
158
|
+
layout: getLayout,
|
|
159
|
+
...options
|
|
158
160
|
});
|
|
159
161
|
};
|
|
160
|
-
|
|
161
|
-
|
|
162
|
+
/**
|
|
163
|
+
* Construct an app card from a URL.
|
|
164
|
+
*
|
|
165
|
+
* Pass `{ live: true }` to ask supported platforms to render the installed
|
|
166
|
+
* app extension's live UI. Other platforms retain their normal URL fallback.
|
|
167
|
+
*/
|
|
168
|
+
function app(url, options = {}) {
|
|
169
|
+
return { build: async () => asApp(url, options) };
|
|
162
170
|
}
|
|
163
171
|
//#endregion
|
|
164
172
|
//#region src/content/edit.ts
|
|
@@ -2990,7 +2998,8 @@ function definePlatform(name, rawDef) {
|
|
|
2990
2998
|
const def = rawDef;
|
|
2991
2999
|
const fullDef = {
|
|
2992
3000
|
...def,
|
|
2993
|
-
name
|
|
3001
|
+
name,
|
|
3002
|
+
config: envAwareConfig(name, def.config)
|
|
2994
3003
|
};
|
|
2995
3004
|
const platformCache = /* @__PURE__ */ new WeakMap();
|
|
2996
3005
|
const narrowSpectrum = (spectrum) => {
|
|
@@ -3042,7 +3051,7 @@ function definePlatform(name, rawDef) {
|
|
|
3042
3051
|
}
|
|
3043
3052
|
//#endregion
|
|
3044
3053
|
//#region src/build-env.ts
|
|
3045
|
-
const SPECTRUM_SDK_VERSION = "9.1
|
|
3054
|
+
const SPECTRUM_SDK_VERSION = "9.3.1";
|
|
3046
3055
|
//#endregion
|
|
3047
3056
|
//#region src/utils/provider-packages.ts
|
|
3048
3057
|
const OFFICIAL_PROVIDER_PACKAGES = {
|
|
@@ -4082,12 +4091,27 @@ function bootstrapTelemetry(opts) {
|
|
|
4082
4091
|
function applyLogLevel(level) {
|
|
4083
4092
|
if (level) setLogLevel(level);
|
|
4084
4093
|
}
|
|
4094
|
+
function envValue(key) {
|
|
4095
|
+
const value = process.env[key];
|
|
4096
|
+
return value === "" ? void 0 : value;
|
|
4097
|
+
}
|
|
4098
|
+
function resolveProjectCredentials(options) {
|
|
4099
|
+
return {
|
|
4100
|
+
projectId: options.projectId ?? envValue(envFor("PROJECT", "ID")),
|
|
4101
|
+
projectSecret: options.projectSecret ?? envValue(envFor("PROJECT", "SECRET"))
|
|
4102
|
+
};
|
|
4103
|
+
}
|
|
4085
4104
|
async function Spectrum(options) {
|
|
4086
|
-
|
|
4087
|
-
|
|
4105
|
+
const { projectId, projectSecret } = resolveProjectCredentials(options);
|
|
4106
|
+
spectrumConfigSchema.parse({
|
|
4107
|
+
...options,
|
|
4108
|
+
projectId,
|
|
4109
|
+
projectSecret
|
|
4110
|
+
});
|
|
4111
|
+
const { providers, options: runtimeOptions, telemetry, webhookSecret } = options;
|
|
4088
4112
|
const flattenGroups = runtimeOptions?.flattenGroups ?? false;
|
|
4089
4113
|
applyLogLevel(runtimeOptions?.logLevel);
|
|
4090
|
-
const resolvedWebhookSecret = webhookSecret ?? process.env
|
|
4114
|
+
const resolvedWebhookSecret = webhookSecret ?? process.env[envFor("WEBHOOK", "SECRET")];
|
|
4091
4115
|
const otelHandle = telemetry ? bootstrapTelemetry({
|
|
4092
4116
|
projectId,
|
|
4093
4117
|
projectSecret
|
|
@@ -1158,6 +1158,128 @@ function voice(input, options) {
|
|
|
1158
1158
|
} };
|
|
1159
1159
|
}
|
|
1160
1160
|
//#endregion
|
|
1161
|
+
//#region src/utils/env.ts
|
|
1162
|
+
/**
|
|
1163
|
+
* Build a Spectrum config env-var name from a channel and a key, centralizing
|
|
1164
|
+
* the `SPECTRUM_<CHANNEL>_<KEY>` convention so the prefix can't drift or be
|
|
1165
|
+
* mistyped per field. Both segments are joined verbatim (callers pass them
|
|
1166
|
+
* already upper-snake-cased), e.g. `envFor("TELEGRAM", "BOT_TOKEN")` →
|
|
1167
|
+
* `"SPECTRUM_TELEGRAM_BOT_TOKEN"`.
|
|
1168
|
+
*/
|
|
1169
|
+
const envFor = (channel, key) => `SPECTRUM_${channel}_${key}`;
|
|
1170
|
+
/**
|
|
1171
|
+
* Wrap a config-field schema so it falls back to an environment variable when
|
|
1172
|
+
* the field is omitted. Precedence is **explicit value > env var > the inner
|
|
1173
|
+
* schema's own default/required check**:
|
|
1174
|
+
*
|
|
1175
|
+
* - An explicit value (anything other than `undefined`) is passed straight
|
|
1176
|
+
* through — a caller-supplied config always wins over the environment.
|
|
1177
|
+
* - When the field is `undefined`, `process.env[envKey]` is substituted. An
|
|
1178
|
+
* env var set to the empty string is treated as unset (a common deploy
|
|
1179
|
+
* footgun) so it doesn't satisfy a `.min(1)` by accident.
|
|
1180
|
+
* - The inner `schema` then validates the resolved value, keeping its exact
|
|
1181
|
+
* semantics — regex, `.min(1)`, `.url()`, `.optional()`, `.default(...)`.
|
|
1182
|
+
* When both the field and the env var are absent, a required inner schema
|
|
1183
|
+
* raises its normal "required" error.
|
|
1184
|
+
*
|
|
1185
|
+
* The output type is the inner schema's output type, so call sites are
|
|
1186
|
+
* unchanged. Because substitution only happens on `undefined`, wrapping a
|
|
1187
|
+
* field inside a `z.object` leaves it a plain key on the parsed result — union
|
|
1188
|
+
* discriminators like `"accessToken" in config` keep working.
|
|
1189
|
+
*
|
|
1190
|
+
* @example
|
|
1191
|
+
* ```ts
|
|
1192
|
+
* botToken: fromEnv("SPECTRUM_TELEGRAM_BOT_TOKEN", z.string().regex(BOT_TOKEN_PATTERN)),
|
|
1193
|
+
* ```
|
|
1194
|
+
*/
|
|
1195
|
+
const fromEnv = (envKey, schema) => z.preprocess((value) => {
|
|
1196
|
+
if (value !== void 0) return value;
|
|
1197
|
+
const envValue = process.env[envKey];
|
|
1198
|
+
return envValue === "" ? void 0 : envValue;
|
|
1199
|
+
}, schema);
|
|
1200
|
+
/**
|
|
1201
|
+
* Normalize a platform id into the env-var prefix segment: upper-case, with any
|
|
1202
|
+
* run of non-alphanumeric characters collapsed to a single `_`. This is a
|
|
1203
|
+
* whole-name transform, NOT camelCase splitting, so it reproduces the prefixes
|
|
1204
|
+
* the adapters used by hand — `"telegram"` → `TELEGRAM`,
|
|
1205
|
+
* `"WhatsApp Business"` → `WHATSAPP_BUSINESS`, `"Slack"` → `SLACK`.
|
|
1206
|
+
*/
|
|
1207
|
+
const NON_ALPHANUMERIC_RUN = /[^A-Z0-9]+/;
|
|
1208
|
+
const normalizePlatformName = (name) => name.toUpperCase().split(NON_ALPHANUMERIC_RUN).filter(Boolean).join("_");
|
|
1209
|
+
const CAMEL_BOUNDARY = /([a-z0-9])([A-Z])/g;
|
|
1210
|
+
/** Convert a camelCase config field name into its UPPER_SNAKE env-var suffix. */
|
|
1211
|
+
const toEnvKey = (field) => field.replace(CAMEL_BOUNDARY, "$1_$2").toUpperCase();
|
|
1212
|
+
const INNER_TYPE_WRAPPERS = new Set([
|
|
1213
|
+
"optional",
|
|
1214
|
+
"nullable",
|
|
1215
|
+
"default",
|
|
1216
|
+
"readonly",
|
|
1217
|
+
"catch",
|
|
1218
|
+
"nonoptional",
|
|
1219
|
+
"prefault"
|
|
1220
|
+
]);
|
|
1221
|
+
const asInternal = (schema) => schema;
|
|
1222
|
+
const unwrapSchema = (schema) => {
|
|
1223
|
+
let current = asInternal(schema);
|
|
1224
|
+
for (let depth = 0; depth < 20; depth++) {
|
|
1225
|
+
const { type } = current.def;
|
|
1226
|
+
if (INNER_TYPE_WRAPPERS.has(type) && current.def.innerType) {
|
|
1227
|
+
current = current.def.innerType;
|
|
1228
|
+
continue;
|
|
1229
|
+
}
|
|
1230
|
+
if (type === "pipe" && (current.def.out || current.def.in)) {
|
|
1231
|
+
current = current.def.out ?? current.def.in;
|
|
1232
|
+
continue;
|
|
1233
|
+
}
|
|
1234
|
+
break;
|
|
1235
|
+
}
|
|
1236
|
+
return current;
|
|
1237
|
+
};
|
|
1238
|
+
const isStringLeaf = (schema) => unwrapSchema(schema).def.type === "string";
|
|
1239
|
+
const isStrictObject = (def) => def.catchall?.def.type === "never";
|
|
1240
|
+
/**
|
|
1241
|
+
* Rewrite a platform's config schema so every string-leaf field falls back to
|
|
1242
|
+
* `SPECTRUM_<PLATFORM>_<KEY>` when omitted which is the equivalent of
|
|
1243
|
+
* hand-wrapping each field with {@link fromEnv}. Called once by
|
|
1244
|
+
* `definePlatform`, so adapters declare plain Zod and get env fallback for free.
|
|
1245
|
+
*
|
|
1246
|
+
* - **object**: each string-leaf field is wrapped with `fromEnv`; non-string
|
|
1247
|
+
* fields (records, arrays, nested objects, booleans, numbers) are left as-is.
|
|
1248
|
+
* The object is only reconstructed when a field actually changed, and
|
|
1249
|
+
* `.strict()` is preserved so an empty strict "cloud" branch is untouched.
|
|
1250
|
+
* - **union**: each option is rewritten independently, so an env value only
|
|
1251
|
+
* satisfies the branch that declares that field (a direct/cloud union stays
|
|
1252
|
+
* correctly discriminated).
|
|
1253
|
+
* - anything else is returned unchanged.
|
|
1254
|
+
*
|
|
1255
|
+
* The output type is identical to the input schema's. Only omitted fields gain
|
|
1256
|
+
* an env fallback which means that `z.infer` and downstream contexts are unaffected.
|
|
1257
|
+
*/
|
|
1258
|
+
const envAwareConfig = (name, schema) => {
|
|
1259
|
+
return rewrite(`SPECTRUM_${normalizePlatformName(name)}`, schema);
|
|
1260
|
+
};
|
|
1261
|
+
const rewrite = (prefix, schema) => {
|
|
1262
|
+
const { def } = asInternal(schema);
|
|
1263
|
+
if (def.type === "object" && def.shape) return rewriteObject(prefix, def, schema);
|
|
1264
|
+
if (def.type === "union" && def.options) {
|
|
1265
|
+
const options = def.options.map((option) => rewrite(prefix, option));
|
|
1266
|
+
return z.union(options);
|
|
1267
|
+
}
|
|
1268
|
+
return schema;
|
|
1269
|
+
};
|
|
1270
|
+
const rewriteObject = (prefix, def, original) => {
|
|
1271
|
+
const shape = def.shape;
|
|
1272
|
+
const nextShape = {};
|
|
1273
|
+
let changed = false;
|
|
1274
|
+
for (const [key, field] of Object.entries(shape)) if (isStringLeaf(field)) {
|
|
1275
|
+
nextShape[key] = fromEnv(`${prefix}_${toEnvKey(key)}`, field);
|
|
1276
|
+
changed = true;
|
|
1277
|
+
} else nextShape[key] = field;
|
|
1278
|
+
if (!changed) return original;
|
|
1279
|
+
const rebuilt = z.object(nextShape);
|
|
1280
|
+
return isStrictObject(def) ? rebuilt.strict() : rebuilt;
|
|
1281
|
+
};
|
|
1282
|
+
//#endregion
|
|
1161
1283
|
//#region src/utils/identifier.ts
|
|
1162
1284
|
const PHONE_LIKE = /^\+?[\d\s()\-.]{7,}$/;
|
|
1163
1285
|
const EMAIL_LIKE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
@@ -1576,4 +1698,4 @@ function broadcast(source) {
|
|
|
1576
1698
|
};
|
|
1577
1699
|
}
|
|
1578
1700
|
//#endregion
|
|
1579
|
-
export {
|
|
1701
|
+
export { contact as $, option as A, markdownSchema as B, asRead as C, reactionSchema as D, reaction as E, leaveSpaceSchema as F, asText as G, group as H, removeMember as I, StreamConsumedError as J, text as K, removeMemberSchema as L, addMember as M, addMemberSchema as N, asPoll as O, leaveSpace as P, asContact as Q, asMarkdown as R, renameSchema as S, asReaction as T, groupSchema as U, asGroup as V, resolveContents as W, asCustom as X, drainStreamText as Y, custom as Z, richlink as _, contentAttrs as a, photoActionSchema as at, replySchema as b, markdownToPlainText as c, attachmentSchema as ct, envAwareConfig as d, fromVCard as et, envFor as f, asRichlink as g, voice as h, stream as i, buildPhotoAction as it, poll as j, asPollOption as k, renderInlineTokens as l, fetchUrlBytes as lt, asVoice as m, createAsyncQueue as n, avatar as nt, errorAttrs as o, asAttachment as ot, fromEnv as p, textSchema as q, mergeStreams as r, avatarSchema as rt, senderAttrs as s, attachment as st, broadcast as t, toVCard as tt, classifyIdentifier as u, tracedFetch as ut, asReply as v, read as w, rename as x, reply as y, markdown as z };
|