@spectrum-ts/core 12.0.0 → 12.2.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.
@@ -642,6 +642,22 @@ declare const editSchema: z.ZodObject<{
642
642
  read: z.core.$InferOuterFunctionType<z.ZodTuple<readonly [], null>, z.ZodPromise<z.ZodCustom<Buffer<ArrayBufferLike>, Buffer<ArrayBufferLike>>>>;
643
643
  stream: z.core.$InferOuterFunctionType<z.ZodTuple<readonly [], null>, z.ZodPromise<z.ZodCustom<ReadableStream<unknown>, ReadableStream<unknown>>>>;
644
644
  size?: number | undefined;
645
+ } | {
646
+ type: "group";
647
+ items: Message<string, User, Space<unknown>>[];
648
+ } | {
649
+ type: "custom";
650
+ raw: unknown;
651
+ } | {
652
+ type: "text";
653
+ text: string;
654
+ } | {
655
+ type: "markdown";
656
+ markdown: string;
657
+ } | {
658
+ type: "reaction";
659
+ emoji: string;
660
+ target: Message<string, User, Space<unknown>>;
645
661
  } | {
646
662
  type: "poll";
647
663
  title: string;
@@ -662,15 +678,6 @@ declare const editSchema: z.ZodObject<{
662
678
  };
663
679
  selected: boolean;
664
680
  title: string;
665
- } | {
666
- type: "custom";
667
- raw: unknown;
668
- } | {
669
- type: "text";
670
- text: string;
671
- } | {
672
- type: "markdown";
673
- markdown: string;
674
681
  } | {
675
682
  type: "streamText";
676
683
  stream: () => AsyncIterable<string>;
@@ -701,13 +708,6 @@ declare const editSchema: z.ZodObject<{
701
708
  summary: z.ZodOptional<z.ZodString>;
702
709
  }, z.core.$strip>>>;
703
710
  live?: boolean | undefined;
704
- } | {
705
- type: "reaction";
706
- emoji: string;
707
- target: Message<string, User, Space<unknown>>;
708
- } | {
709
- type: "group";
710
- items: Message<string, User, Space<unknown>>[];
711
711
  } | {
712
712
  type: "effect";
713
713
  content: {
@@ -800,6 +800,22 @@ declare const editSchema: z.ZodObject<{
800
800
  read: z.core.$InferOuterFunctionType<z.ZodTuple<readonly [], null>, z.ZodPromise<z.ZodCustom<Buffer<ArrayBufferLike>, Buffer<ArrayBufferLike>>>>;
801
801
  stream: z.core.$InferOuterFunctionType<z.ZodTuple<readonly [], null>, z.ZodPromise<z.ZodCustom<ReadableStream<unknown>, ReadableStream<unknown>>>>;
802
802
  size?: number | undefined;
803
+ } | {
804
+ type: "group";
805
+ items: Message<string, User, Space<unknown>>[];
806
+ } | {
807
+ type: "custom";
808
+ raw: unknown;
809
+ } | {
810
+ type: "text";
811
+ text: string;
812
+ } | {
813
+ type: "markdown";
814
+ markdown: string;
815
+ } | {
816
+ type: "reaction";
817
+ emoji: string;
818
+ target: Message<string, User, Space<unknown>>;
803
819
  } | {
804
820
  type: "poll";
805
821
  title: string;
@@ -820,15 +836,6 @@ declare const editSchema: z.ZodObject<{
820
836
  };
821
837
  selected: boolean;
822
838
  title: string;
823
- } | {
824
- type: "custom";
825
- raw: unknown;
826
- } | {
827
- type: "text";
828
- text: string;
829
- } | {
830
- type: "markdown";
831
- markdown: string;
832
839
  } | {
833
840
  type: "streamText";
834
841
  stream: () => AsyncIterable<string>;
@@ -859,13 +866,6 @@ declare const editSchema: z.ZodObject<{
859
866
  summary: z.ZodOptional<z.ZodString>;
860
867
  }, z.core.$strip>>>;
861
868
  live?: boolean | undefined;
862
- } | {
863
- type: "reaction";
864
- emoji: string;
865
- target: Message<string, User, Space<unknown>>;
866
- } | {
867
- type: "group";
868
- items: Message<string, User, Space<unknown>>[];
869
869
  } | {
870
870
  type: "effect";
871
871
  content: {
@@ -1188,6 +1188,22 @@ declare const replySchema: z.ZodObject<{
1188
1188
  read: z.core.$InferOuterFunctionType<z.ZodTuple<readonly [], null>, z.ZodPromise<z.ZodCustom<Buffer<ArrayBufferLike>, Buffer<ArrayBufferLike>>>>;
1189
1189
  stream: z.core.$InferOuterFunctionType<z.ZodTuple<readonly [], null>, z.ZodPromise<z.ZodCustom<ReadableStream<unknown>, ReadableStream<unknown>>>>;
1190
1190
  size?: number | undefined;
1191
+ } | {
1192
+ type: "group";
1193
+ items: Message<string, User, Space<unknown>>[];
1194
+ } | {
1195
+ type: "custom";
1196
+ raw: unknown;
1197
+ } | {
1198
+ type: "text";
1199
+ text: string;
1200
+ } | {
1201
+ type: "markdown";
1202
+ markdown: string;
1203
+ } | {
1204
+ type: "reaction";
1205
+ emoji: string;
1206
+ target: Message<string, User, Space<unknown>>;
1191
1207
  } | {
1192
1208
  type: "poll";
1193
1209
  title: string;
@@ -1208,15 +1224,6 @@ declare const replySchema: z.ZodObject<{
1208
1224
  };
1209
1225
  selected: boolean;
1210
1226
  title: string;
1211
- } | {
1212
- type: "custom";
1213
- raw: unknown;
1214
- } | {
1215
- type: "text";
1216
- text: string;
1217
- } | {
1218
- type: "markdown";
1219
- markdown: string;
1220
1227
  } | {
1221
1228
  type: "streamText";
1222
1229
  stream: () => AsyncIterable<string>;
@@ -1247,13 +1254,6 @@ declare const replySchema: z.ZodObject<{
1247
1254
  summary: z.ZodOptional<z.ZodString>;
1248
1255
  }, z.core.$strip>>>;
1249
1256
  live?: boolean | undefined;
1250
- } | {
1251
- type: "reaction";
1252
- emoji: string;
1253
- target: Message<string, User, Space<unknown>>;
1254
- } | {
1255
- type: "group";
1256
- items: Message<string, User, Space<unknown>>[];
1257
1257
  } | {
1258
1258
  type: "effect";
1259
1259
  content: {
@@ -1346,6 +1346,22 @@ declare const replySchema: z.ZodObject<{
1346
1346
  read: z.core.$InferOuterFunctionType<z.ZodTuple<readonly [], null>, z.ZodPromise<z.ZodCustom<Buffer<ArrayBufferLike>, Buffer<ArrayBufferLike>>>>;
1347
1347
  stream: z.core.$InferOuterFunctionType<z.ZodTuple<readonly [], null>, z.ZodPromise<z.ZodCustom<ReadableStream<unknown>, ReadableStream<unknown>>>>;
1348
1348
  size?: number | undefined;
1349
+ } | {
1350
+ type: "group";
1351
+ items: Message<string, User, Space<unknown>>[];
1352
+ } | {
1353
+ type: "custom";
1354
+ raw: unknown;
1355
+ } | {
1356
+ type: "text";
1357
+ text: string;
1358
+ } | {
1359
+ type: "markdown";
1360
+ markdown: string;
1361
+ } | {
1362
+ type: "reaction";
1363
+ emoji: string;
1364
+ target: Message<string, User, Space<unknown>>;
1349
1365
  } | {
1350
1366
  type: "poll";
1351
1367
  title: string;
@@ -1366,15 +1382,6 @@ declare const replySchema: z.ZodObject<{
1366
1382
  };
1367
1383
  selected: boolean;
1368
1384
  title: string;
1369
- } | {
1370
- type: "custom";
1371
- raw: unknown;
1372
- } | {
1373
- type: "text";
1374
- text: string;
1375
- } | {
1376
- type: "markdown";
1377
- markdown: string;
1378
1385
  } | {
1379
1386
  type: "streamText";
1380
1387
  stream: () => AsyncIterable<string>;
@@ -1405,13 +1412,6 @@ declare const replySchema: z.ZodObject<{
1405
1412
  summary: z.ZodOptional<z.ZodString>;
1406
1413
  }, z.core.$strip>>>;
1407
1414
  live?: boolean | undefined;
1408
- } | {
1409
- type: "reaction";
1410
- emoji: string;
1411
- target: Message<string, User, Space<unknown>>;
1412
- } | {
1413
- type: "group";
1414
- items: Message<string, User, Space<unknown>>[];
1415
1415
  } | {
1416
1416
  type: "effect";
1417
1417
  content: {
@@ -6786,7 +6786,7 @@ declare function definePlatform<_Name extends string, _ConfigSchema extends z.Zo
6786
6786
  id: string;
6787
6787
  }, _MessageSchema extends z.ZodType<object> | undefined = undefined, _MessageType extends ProviderMessage<_ResolvedUser, _ResolvedSpace, _MessageSchema extends z.ZodType<object> ? z.infer<_MessageSchema> : Record<never, never>> = ProviderMessage<_ResolvedUser, _ResolvedSpace, _MessageSchema extends z.ZodType<object> ? z.infer<_MessageSchema> : Record<never, never>>, _Events extends (Record<string, EventProducer<unknown, _Client, z.infer<_ConfigSchema>>> & {
6788
6788
  messages?: never;
6789
- }) | undefined = undefined, _Static extends Record<string, unknown> = Record<never, never>, _SpaceActions extends Record<string, SpaceActionFn> = Record<never, never>, _MessageActions extends Record<string, MessageActionFn> = Record<never, never>, _Actions extends Record<string, InstanceActionFn> = Record<never, never>>(name: _Name, def: {
6789
+ }) | undefined = undefined, _Static extends Record<string, unknown> = Record<never, never>, _SpaceActions extends Record<string, SpaceActionFn> = Record<never, never>, _MessageActions extends Record<string, MessageActionFn> = Record<never, never>, _Actions extends Record<string, InstanceActionFn> = Record<never, never>>(platformId: _Name, def: {
6790
6790
  lifecycle: {
6791
6791
  createClient: (ctx: CreateClientContext<_ConfigSchema>) => Promise<_Client>;
6792
6792
  destroyClient?: (ctx: {
@@ -6803,7 +6803,7 @@ declare function definePlatform<_Name extends string, _ConfigSchema extends z.Zo
6803
6803
  id: string;
6804
6804
  }, _MessageSchema extends z.ZodType<object> | undefined = undefined, _FusorEvents extends (Record<string, z.ZodType<object>> & {
6805
6805
  messages?: never;
6806
- }) | undefined = undefined, _Static extends Record<string, unknown> = Record<never, never>, _SpaceActions extends Record<string, SpaceActionFn> = Record<never, never>, _MessageActions extends Record<string, MessageActionFn> = Record<never, never>>(name: _Name, def: Omit<PlatformDef<_Name, _ConfigSchema, _UserSchema, _SpaceSchema, _SpaceParamsSchema, FusorClient<_TPayload>, _ResolvedUser, _ResolvedSpace, _MessageSchema, ProviderMessage<_ResolvedUser, _ResolvedSpace, _MessageSchema extends z.ZodType<object> ? z.infer<_MessageSchema> : Record<never, never>>, _FusorEvents, _SpaceActions, _MessageActions>, "lifecycle" | "name" | "messages"> & {
6806
+ }) | undefined = undefined, _Static extends Record<string, unknown> = Record<never, never>, _SpaceActions extends Record<string, SpaceActionFn> = Record<never, never>, _MessageActions extends Record<string, MessageActionFn> = Record<never, never>>(platformId: _Name, def: Omit<PlatformDef<_Name, _ConfigSchema, _UserSchema, _SpaceSchema, _SpaceParamsSchema, FusorClient<_TPayload>, _ResolvedUser, _ResolvedSpace, _MessageSchema, ProviderMessage<_ResolvedUser, _ResolvedSpace, _MessageSchema extends z.ZodType<object> ? z.infer<_MessageSchema> : Record<never, never>>, _FusorEvents, _SpaceActions, _MessageActions>, "lifecycle" | "name" | "messages"> & {
6807
6807
  lifecycle: {
6808
6808
  createClient: (ctx: CreateClientContext<_ConfigSchema>) => Promise<FusorClient<_TPayload>>;
6809
6809
  destroyClient?: (ctx: {
@@ -7333,6 +7333,22 @@ declare const contentSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
7333
7333
  read: z.core.$InferOuterFunctionType<z.ZodTuple<readonly [], null>, z.ZodPromise<z.ZodCustom<Buffer<ArrayBufferLike>, Buffer<ArrayBufferLike>>>>;
7334
7334
  stream: z.core.$InferOuterFunctionType<z.ZodTuple<readonly [], null>, z.ZodPromise<z.ZodCustom<ReadableStream<unknown>, ReadableStream<unknown>>>>;
7335
7335
  size?: number | undefined;
7336
+ } | {
7337
+ type: "group";
7338
+ items: Message<string, User, Space<unknown>>[];
7339
+ } | {
7340
+ type: "custom";
7341
+ raw: unknown;
7342
+ } | {
7343
+ type: "text";
7344
+ text: string;
7345
+ } | {
7346
+ type: "markdown";
7347
+ markdown: string;
7348
+ } | {
7349
+ type: "reaction";
7350
+ emoji: string;
7351
+ target: Message<string, User, Space<unknown>>;
7336
7352
  } | {
7337
7353
  type: "poll";
7338
7354
  title: string;
@@ -7353,15 +7369,6 @@ declare const contentSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
7353
7369
  };
7354
7370
  selected: boolean;
7355
7371
  title: string;
7356
- } | {
7357
- type: "custom";
7358
- raw: unknown;
7359
- } | {
7360
- type: "text";
7361
- text: string;
7362
- } | {
7363
- type: "markdown";
7364
- markdown: string;
7365
7372
  } | {
7366
7373
  type: "streamText";
7367
7374
  stream: () => AsyncIterable<string>;
@@ -7392,13 +7399,6 @@ declare const contentSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
7392
7399
  summary: z.ZodOptional<z.ZodString>;
7393
7400
  }, z.core.$strip>>>;
7394
7401
  live?: boolean | undefined;
7395
- } | {
7396
- type: "reaction";
7397
- emoji: string;
7398
- target: Message<string, User, Space<unknown>>;
7399
- } | {
7400
- type: "group";
7401
- items: Message<string, User, Space<unknown>>[];
7402
7402
  } | {
7403
7403
  type: "effect";
7404
7404
  content: {
@@ -7491,6 +7491,22 @@ declare const contentSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
7491
7491
  read: z.core.$InferOuterFunctionType<z.ZodTuple<readonly [], null>, z.ZodPromise<z.ZodCustom<Buffer<ArrayBufferLike>, Buffer<ArrayBufferLike>>>>;
7492
7492
  stream: z.core.$InferOuterFunctionType<z.ZodTuple<readonly [], null>, z.ZodPromise<z.ZodCustom<ReadableStream<unknown>, ReadableStream<unknown>>>>;
7493
7493
  size?: number | undefined;
7494
+ } | {
7495
+ type: "group";
7496
+ items: Message<string, User, Space<unknown>>[];
7497
+ } | {
7498
+ type: "custom";
7499
+ raw: unknown;
7500
+ } | {
7501
+ type: "text";
7502
+ text: string;
7503
+ } | {
7504
+ type: "markdown";
7505
+ markdown: string;
7506
+ } | {
7507
+ type: "reaction";
7508
+ emoji: string;
7509
+ target: Message<string, User, Space<unknown>>;
7494
7510
  } | {
7495
7511
  type: "poll";
7496
7512
  title: string;
@@ -7511,15 +7527,6 @@ declare const contentSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
7511
7527
  };
7512
7528
  selected: boolean;
7513
7529
  title: string;
7514
- } | {
7515
- type: "custom";
7516
- raw: unknown;
7517
- } | {
7518
- type: "text";
7519
- text: string;
7520
- } | {
7521
- type: "markdown";
7522
- markdown: string;
7523
7530
  } | {
7524
7531
  type: "streamText";
7525
7532
  stream: () => AsyncIterable<string>;
@@ -7550,13 +7557,6 @@ declare const contentSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
7550
7557
  summary: z.ZodOptional<z.ZodString>;
7551
7558
  }, z.core.$strip>>>;
7552
7559
  live?: boolean | undefined;
7553
- } | {
7554
- type: "reaction";
7555
- emoji: string;
7556
- target: Message<string, User, Space<unknown>>;
7557
- } | {
7558
- type: "group";
7559
- items: Message<string, User, Space<unknown>>[];
7560
7560
  } | {
7561
7561
  type: "effect";
7562
7562
  content: {
@@ -7653,6 +7653,22 @@ declare const contentSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
7653
7653
  read: z.core.$InferOuterFunctionType<z.ZodTuple<readonly [], null>, z.ZodPromise<z.ZodCustom<Buffer<ArrayBufferLike>, Buffer<ArrayBufferLike>>>>;
7654
7654
  stream: z.core.$InferOuterFunctionType<z.ZodTuple<readonly [], null>, z.ZodPromise<z.ZodCustom<ReadableStream<unknown>, ReadableStream<unknown>>>>;
7655
7655
  size?: number | undefined;
7656
+ } | {
7657
+ type: "group";
7658
+ items: Message<string, User, Space<unknown>>[];
7659
+ } | {
7660
+ type: "custom";
7661
+ raw: unknown;
7662
+ } | {
7663
+ type: "text";
7664
+ text: string;
7665
+ } | {
7666
+ type: "markdown";
7667
+ markdown: string;
7668
+ } | {
7669
+ type: "reaction";
7670
+ emoji: string;
7671
+ target: Message<string, User, Space<unknown>>;
7656
7672
  } | {
7657
7673
  type: "poll";
7658
7674
  title: string;
@@ -7673,15 +7689,6 @@ declare const contentSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
7673
7689
  };
7674
7690
  selected: boolean;
7675
7691
  title: string;
7676
- } | {
7677
- type: "custom";
7678
- raw: unknown;
7679
- } | {
7680
- type: "text";
7681
- text: string;
7682
- } | {
7683
- type: "markdown";
7684
- markdown: string;
7685
7692
  } | {
7686
7693
  type: "streamText";
7687
7694
  stream: () => AsyncIterable<string>;
@@ -7712,13 +7719,6 @@ declare const contentSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
7712
7719
  summary: z.ZodOptional<z.ZodString>;
7713
7720
  }, z.core.$strip>>>;
7714
7721
  live?: boolean | undefined;
7715
- } | {
7716
- type: "reaction";
7717
- emoji: string;
7718
- target: Message<string, User, Space<unknown>>;
7719
- } | {
7720
- type: "group";
7721
- items: Message<string, User, Space<unknown>>[];
7722
7722
  } | {
7723
7723
  type: "effect";
7724
7724
  content: {
@@ -7811,6 +7811,22 @@ declare const contentSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
7811
7811
  read: z.core.$InferOuterFunctionType<z.ZodTuple<readonly [], null>, z.ZodPromise<z.ZodCustom<Buffer<ArrayBufferLike>, Buffer<ArrayBufferLike>>>>;
7812
7812
  stream: z.core.$InferOuterFunctionType<z.ZodTuple<readonly [], null>, z.ZodPromise<z.ZodCustom<ReadableStream<unknown>, ReadableStream<unknown>>>>;
7813
7813
  size?: number | undefined;
7814
+ } | {
7815
+ type: "group";
7816
+ items: Message<string, User, Space<unknown>>[];
7817
+ } | {
7818
+ type: "custom";
7819
+ raw: unknown;
7820
+ } | {
7821
+ type: "text";
7822
+ text: string;
7823
+ } | {
7824
+ type: "markdown";
7825
+ markdown: string;
7826
+ } | {
7827
+ type: "reaction";
7828
+ emoji: string;
7829
+ target: Message<string, User, Space<unknown>>;
7814
7830
  } | {
7815
7831
  type: "poll";
7816
7832
  title: string;
@@ -7831,15 +7847,6 @@ declare const contentSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
7831
7847
  };
7832
7848
  selected: boolean;
7833
7849
  title: string;
7834
- } | {
7835
- type: "custom";
7836
- raw: unknown;
7837
- } | {
7838
- type: "text";
7839
- text: string;
7840
- } | {
7841
- type: "markdown";
7842
- markdown: string;
7843
7850
  } | {
7844
7851
  type: "streamText";
7845
7852
  stream: () => AsyncIterable<string>;
@@ -7870,13 +7877,6 @@ declare const contentSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
7870
7877
  summary: z.ZodOptional<z.ZodString>;
7871
7878
  }, z.core.$strip>>>;
7872
7879
  live?: boolean | undefined;
7873
- } | {
7874
- type: "reaction";
7875
- emoji: string;
7876
- target: Message<string, User, Space<unknown>>;
7877
- } | {
7878
- type: "group";
7879
- items: Message<string, User, Space<unknown>>[];
7880
7880
  } | {
7881
7881
  type: "effect";
7882
7882
  content: {
@@ -1,4 +1,4 @@
1
- import { Cn as reactionSchema, Dt as asRichlink, Gn as photoActionSchema, Gt as asPollOption, H as ProviderMessageRecord, Hn as avatarSchema, It as renameSchema, Nt as replySchema, Pn as asContact, Rt as asRead, U as ProviderUserRecord, Un as PhotoInput, Wn as buildPhotoAction, Wt as asPoll, Yt as asMarkdown, _t as asVoice, bt as asUnsend, gn as leaveSpaceSchema, in as groupSchema, jt as asReply, mn as addMemberSchema, nn as asGroup, q as ManagedStream, r as asAttachment, vn as removeMemberSchema, wn as asCustom, wt as asText, xn as asReaction } from "./attachment-DN82cCAP.js";
1
+ import { Cn as reactionSchema, Dt as asRichlink, Gn as photoActionSchema, Gt as asPollOption, H as ProviderMessageRecord, Hn as avatarSchema, It as renameSchema, Nt as replySchema, Pn as asContact, Rt as asRead, U as ProviderUserRecord, Un as PhotoInput, Wn as buildPhotoAction, Wt as asPoll, Yt as asMarkdown, _t as asVoice, bt as asUnsend, gn as leaveSpaceSchema, in as groupSchema, jt as asReply, mn as addMemberSchema, nn as asGroup, q as ManagedStream, r as asAttachment, vn as removeMemberSchema, wn as asCustom, wt as asText, xn as asReaction } from "./attachment-Wgb7-di3.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";
@@ -178,4 +178,18 @@ declare const resumableOrderedStream: <TLive, TMissed, TOutput>(options: Resumab
178
178
  */
179
179
  declare function errorAttrs(error: unknown, prefix?: string): LogAttrs$1;
180
180
  //#endregion
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, asUnsend, 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 };
181
+ //#region src/utils/token-renewal.d.ts
182
+ interface TokenRenewal {
183
+ dispose(): void;
184
+ forceRefresh(): Promise<void>;
185
+ invalidate(): void;
186
+ refreshIfNeeded(): Promise<void>;
187
+ }
188
+ interface TokenRenewalOptions {
189
+ expiresInSeconds(): number;
190
+ name: string;
191
+ refresh(): Promise<void>;
192
+ }
193
+ declare const createTokenRenewal: (options: TokenRenewalOptions) => TokenRenewal;
194
+ //#endregion
195
+ export { type CloseableAsyncIterable, type LogAttrs, type LogLevel, type PhotoInput, type PhotonLogger, type ProviderMessageRecord, type ProviderUserRecord, type ResumableStreamItem, type SanitizeUrlOptions, type TokenRenewal, type TokenRenewalOptions, addMemberSchema, asAttachment, asContact, asCustom, asGroup, asMarkdown, asPoll, asPollOption, asReaction, asRead, asReply, asRichlink, asText, asUnsend, asVoice, avatarSchema, buildPhotoAction, createLogger, createTokenRenewal, 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 { A as asPoll, B as asMarkdown, D as asReaction, F as addMemberSchema, G as groupSchema, H as markdownSchema, L as leaveSpaceSchema, Q as asCustom, S as replySchema, T as asRead, U as asGroup, Y as textSchema, at as avatarSchema, b as asReply, ct as asAttachment, d as envAwareConfig, et as asContact, f as envFor, ft as tracedFetch, g as asUnsend, i as stream, j as asPollOption, k as reactionSchema, l as renderInlineTokens, m as asVoice, o as errorAttrs, ot as buildPhotoAction, p as fromEnv, q as asText, st as photoActionSchema, ut as attachmentSchema, v as asRichlink, w as renameSchema, z as removeMemberSchema } from "./stream-B6kth7y9.js";
1
+ import { $ as asCustom, A as reactionSchema, B as removeMemberSchema, C as replySchema, E as asRead, I as addMemberSchema, J as asText, K as groupSchema, M as asPollOption, O as asReaction, R as leaveSpaceSchema, T as renameSchema, U as markdownSchema, V as asMarkdown, W as asGroup, X as textSchema, _ as asUnsend, a as createTokenRenewal, ct as photoActionSchema, dt as attachmentSchema, f as envAwareConfig, h as asVoice, i as stream, j as asPoll, lt as asAttachment, m as fromEnv, ot as avatarSchema, p as envFor, pt as tracedFetch, s as errorAttrs, st as buildPhotoAction, tt as asContact, u as renderInlineTokens, x as asReply, y as asRichlink } from "./stream-4VQBWgZQ.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, asUnsend, 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 };
425
+ export { addMemberSchema, asAttachment, asContact, asCustom, asGroup, asMarkdown, asPoll, asPollOption, asReaction, asRead, asReply, asRichlink, asText, asUnsend, asVoice, avatarSchema, buildPhotoAction, createLogger, createTokenRenewal, 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 StreamTextSource, A as isFusorEvent, An as ContactInput, At as Reply, B as PlatformUser, Bn as AvatarInput, Bt as Poll, C as FusorVerify, Ct as typing, D as WebhookRawResult, Dn as ContactAddress, E as WebhookRawRequest, En as Contact, Et as Richlink, F as PlatformInstance, Fn as contact, Ft as rename, G as SpaceNamespace, Ht as PollChoiceInput, I as PlatformMessage, In as AgentSender, J as broadcast, Jn as AppOptions, Jt as Markdown, K as Broadcaster, Kn as App, Kt as option, L as PlatformProviderConfig, Ln as User, Lt as Read, M as EventProducer, Mn as ContactOrg, Mt as reply, N as Platform, Nn as ContactPhone, O as FusorEvent, On as ContactDetails, Ot as richlink, P as PlatformDef, Pt as Rename, Q as CloudPlatform, Qt as StreamText, R as PlatformRuntime, Rn as Avatar, S as FusorRespond, Sn as reaction, St as Typing, T as WebhookHandler, Tn as custom, Tt as text, Ut as PollOption, V as ProviderMessage, Vn as avatar, Vt as PollChoice, W as SchemaMessage, X as stream, Xn as app, Xt as markdown, Y as mergeStreams, Yn as AppUrl, Z as Store, Zn as appLayoutSchema, Zt as DeltaExtractor, _ as FusorClient, _n as removeMember, a as Content, an as Edit, at as ProjectProfile, b as FusorMessagesReturn, bn as ReactionBuilder, c as fromVCard, cn as Space, ct as SlackTokenData, d as UnsupportedKind, dn as MemberInput, dt as SubscriptionStatus, en as TextStreamOptions, et as FusorTokenData, f as Spectrum, fn as RemoveMember, ft as TokenData, g as isFusorClient, gt as Voice, h as fusor, hn as leaveSpace, ht as EmojiKey, i as attachment, it as ProjectData, j as AnyPlatformDef, jn as ContactName, k as fusorEvent, kn as ContactEmail, kt as resolveContents, l as toVCard, ln as AddMember, lt as SpectrumCloudError, m as definePlatform, mt as Emoji, n as AttachmentInput, nt as PlatformStatus, o as ContentBuilder, on as edit, ot as SharedTokenData, p as SpectrumInstance, pn as addMember, pt as cloud, q as ManagedStream, qn as AppLayout, qt as poll, rn as group, rt as PlatformsData, s as ContentInput, sn as Message, st as SlackTeamMeta, t as Attachment, tn as Group, tt as ImessageInfoData, u as UnsupportedError, un as LeaveSpace, ut as SubscriptionData, v as FusorMessages, vt as voice, w as FusorVerifyRequest, x as FusorReply, xt as unsend, y as FusorMessagesCtx, yn as Reaction, yt as Unsend, z as PlatformSpace, zn as AvatarData, zt as read } from "./attachment-DN82cCAP.js";
1
+ import { $ as DedicatedTokenData, $t as StreamTextSource, A as isFusorEvent, An as ContactInput, At as Reply, B as PlatformUser, Bn as AvatarInput, Bt as Poll, C as FusorVerify, Ct as typing, D as WebhookRawResult, Dn as ContactAddress, E as WebhookRawRequest, En as Contact, Et as Richlink, F as PlatformInstance, Fn as contact, Ft as rename, G as SpaceNamespace, Ht as PollChoiceInput, I as PlatformMessage, In as AgentSender, J as broadcast, Jn as AppOptions, Jt as Markdown, K as Broadcaster, Kn as App, Kt as option, L as PlatformProviderConfig, Ln as User, Lt as Read, M as EventProducer, Mn as ContactOrg, Mt as reply, N as Platform, Nn as ContactPhone, O as FusorEvent, On as ContactDetails, Ot as richlink, P as PlatformDef, Pt as Rename, Q as CloudPlatform, Qt as StreamText, R as PlatformRuntime, Rn as Avatar, S as FusorRespond, Sn as reaction, St as Typing, T as WebhookHandler, Tn as custom, Tt as text, Ut as PollOption, V as ProviderMessage, Vn as avatar, Vt as PollChoice, W as SchemaMessage, X as stream, Xn as app, Xt as markdown, Y as mergeStreams, Yn as AppUrl, Z as Store, Zn as appLayoutSchema, Zt as DeltaExtractor, _ as FusorClient, _n as removeMember, a as Content, an as Edit, at as ProjectProfile, b as FusorMessagesReturn, bn as ReactionBuilder, c as fromVCard, cn as Space, ct as SlackTokenData, d as UnsupportedKind, dn as MemberInput, dt as SubscriptionStatus, en as TextStreamOptions, et as FusorTokenData, f as Spectrum, fn as RemoveMember, ft as TokenData, g as isFusorClient, gt as Voice, h as fusor, hn as leaveSpace, ht as EmojiKey, i as attachment, it as ProjectData, j as AnyPlatformDef, jn as ContactName, k as fusorEvent, kn as ContactEmail, kt as resolveContents, l as toVCard, ln as AddMember, lt as SpectrumCloudError, m as definePlatform, mt as Emoji, n as AttachmentInput, nt as PlatformStatus, o as ContentBuilder, on as edit, ot as SharedTokenData, p as SpectrumInstance, pn as addMember, pt as cloud, q as ManagedStream, qn as AppLayout, qt as poll, rn as group, rt as PlatformsData, s as ContentInput, sn as Message, st as SlackTeamMeta, t as Attachment, tn as Group, tt as ImessageInfoData, u as UnsupportedError, un as LeaveSpace, ut as SubscriptionData, v as FusorMessages, vt as voice, w as FusorVerifyRequest, x as FusorReply, xt as unsend, y as FusorMessagesCtx, yn as Reaction, yt as Unsend, z as PlatformSpace, zn as AvatarData, zt as read } from "./attachment-Wgb7-di3.js";
2
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 custom, B as asMarkdown, C as rename, E as read, F as addMemberSchema, I as leaveSpace, J as text, K as resolveContents, L as leaveSpaceSchema, M as option, N as poll, O as reaction, P as addMember, Q as asCustom, R as removeMember, V as markdown, W as group, X as StreamConsumedError, Z as drainStreamText, _ as unsend, a as contentAttrs, at as avatarSchema, c as markdownToPlainText, ct as asAttachment, d as envAwareConfig, dt as fetchUrlBytes, et as asContact, f as envFor, ft as tracedFetch, h as voice, i as stream, it as avatar, lt as attachment, m as asVoice, n as createAsyncQueue, nt as fromVCard, o as errorAttrs, q as asText, r as mergeStreams, rt as toVCard, s as senderAttrs, t as broadcast, tt as contact, u as classifyIdentifier, w as renameSchema, x as reply, y as richlink, z as removeMemberSchema } from "./stream-B6kth7y9.js";
1
+ import { $ as asCustom, B as removeMemberSchema, D as read, F as addMember, G as group, H as markdown, I as addMemberSchema, J as asText, L as leaveSpace, N as option, P as poll, Q as drainStreamText, R as leaveSpaceSchema, S as reply, T as renameSchema, V as asMarkdown, Y as text, Z as StreamConsumedError, a as createTokenRenewal, at as avatar, b as richlink, c as senderAttrs, d as classifyIdentifier, et as custom, f as envAwareConfig, ft as fetchUrlBytes, g as voice, h as asVoice, i as stream, it as toVCard, k as reaction, l as markdownToPlainText, lt as asAttachment, n as createAsyncQueue, nt as contact, o as contentAttrs, ot as avatarSchema, p as envFor, pt as tracedFetch, q as resolveContents, r as mergeStreams, rt as fromVCard, s as errorAttrs, t as broadcast, tt as asContact, ut as attachment, v as unsend, w as rename, z as removeMember } from "./stream-4VQBWgZQ.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";
@@ -2806,6 +2806,11 @@ function buildMessage(params) {
2806
2806
  //#endregion
2807
2807
  //#region src/platform/define.ts
2808
2808
  const platformLog = createLogger("spectrum.platform");
2809
+ const PLATFORM_ID_PATTERN = /^[a-z][a-z0-9]*(?:_[a-z0-9]+)*$/;
2810
+ const assertValidPlatformId = (platformId) => {
2811
+ if (PLATFORM_ID_PATTERN.test(platformId)) return;
2812
+ throw new Error(`Invalid platform id "${platformId}". Platform ids must use lowercase snake_case (for example, "my_platform").`);
2813
+ };
2809
2814
  function buildInstanceActions(platformName, declared, reservedKeys, buildCtx) {
2810
2815
  const out = {};
2811
2816
  for (const key of PLATFORM_WISE_ACTION_KEYS) {
@@ -2948,33 +2953,34 @@ function createPlatformInstance(def, runtime) {
2948
2953
  }));
2949
2954
  return Object.assign(base, instanceActions, eventProperties);
2950
2955
  }
2951
- function definePlatform(name, rawDef) {
2956
+ function definePlatform(platformId, rawDef) {
2957
+ assertValidPlatformId(platformId);
2952
2958
  const def = rawDef;
2953
2959
  const fullDef = {
2954
2960
  ...def,
2955
- name,
2956
- config: envAwareConfig(name, def.config)
2961
+ name: platformId,
2962
+ config: envAwareConfig(platformId, def.config)
2957
2963
  };
2958
2964
  const platformCache = /* @__PURE__ */ new WeakMap();
2959
2965
  const narrowSpectrum = (spectrum) => {
2960
2966
  const cached = platformCache.get(spectrum);
2961
2967
  if (cached) return cached;
2962
- const runtime = spectrum.__internal.platforms.get(name);
2963
- if (!runtime) throw new Error(`Platform "${name}" is not registered`);
2968
+ const runtime = spectrum.__internal.platforms.get(platformId);
2969
+ if (!runtime) throw new Error(`Platform "${platformId}" is not registered`);
2964
2970
  const instance = createPlatformInstance(fullDef, runtime);
2965
2971
  platformCache.set(spectrum, instance);
2966
2972
  return instance;
2967
2973
  };
2968
2974
  const narrowSpace = (input) => {
2969
- if (input.__platform !== name) platformLog.warn("space platform mismatch; narrowing skipped", {
2970
- "spectrum.platform.expected": name,
2975
+ if (input.__platform !== platformId) platformLog.warn("space platform mismatch; narrowing skipped", {
2976
+ "spectrum.platform.expected": platformId,
2971
2977
  "spectrum.platform.actual": input.__platform
2972
2978
  });
2973
2979
  return input;
2974
2980
  };
2975
2981
  const narrowMessage = (input) => {
2976
- if (input.platform !== name) platformLog.warn("message platform mismatch; narrowing skipped", {
2977
- "spectrum.platform.expected": name,
2982
+ if (input.platform !== platformId) platformLog.warn("message platform mismatch; narrowing skipped", {
2983
+ "spectrum.platform.expected": platformId,
2978
2984
  "spectrum.platform.actual": input.platform
2979
2985
  });
2980
2986
  return input;
@@ -2989,15 +2995,15 @@ function definePlatform(name, rawDef) {
2989
2995
  return {
2990
2996
  __tag: "PlatformProviderConfig",
2991
2997
  __def: void 0,
2992
- __name: name,
2998
+ __name: platformId,
2993
2999
  config: config ?? {},
2994
3000
  __definition: fullDef
2995
3001
  };
2996
3002
  };
2997
3003
  narrower.is = ((input) => {
2998
3004
  if (typeof input !== "object" || input === null) return false;
2999
- if ("__platform" in input) return input.__platform === name;
3000
- if ("platform" in input) return input.platform === name;
3005
+ if ("__platform" in input) return input.__platform === platformId;
3006
+ if ("platform" in input) return input.platform === platformId;
3001
3007
  return false;
3002
3008
  });
3003
3009
  if (def.static) Object.assign(narrower, def.static);
@@ -3005,18 +3011,19 @@ function definePlatform(name, rawDef) {
3005
3011
  }
3006
3012
  //#endregion
3007
3013
  //#region src/build-env.ts
3008
- const SPECTRUM_SDK_VERSION = "12.0.0";
3014
+ const SPECTRUM_SDK_VERSION = "12.2.0";
3009
3015
  //#endregion
3010
3016
  //#region src/utils/provider-packages.ts
3011
3017
  const OFFICIAL_PROVIDER_PACKAGES = {
3012
3018
  imessage: "@spectrum-ts/imessage",
3019
+ local_imessage: "@spectrum-ts/imessage-local",
3013
3020
  slack: "@spectrum-ts/slack",
3014
3021
  telegram: "@spectrum-ts/telegram",
3015
3022
  terminal: "@spectrum-ts/terminal",
3016
- "whatsapp-business": "@spectrum-ts/whatsapp-business"
3023
+ whatsapp_business: "@spectrum-ts/whatsapp-business"
3017
3024
  };
3018
- const SEPARATORS = /[\s_]+/g;
3019
- const normalizePlatformKey = (platform) => platform.trim().toLowerCase().replace(SEPARATORS, "-");
3025
+ const LEGACY_SEPARATORS = /[\s-]+/g;
3026
+ const normalizePlatformKey = (platform) => platform.trim().toLowerCase().replace(LEGACY_SEPARATORS, "_");
3020
3027
  const officialProviderPackage = (platform) => OFFICIAL_PROVIDER_PACKAGES[normalizePlatformKey(platform)];
3021
3028
  const installCommand = (pkg) => process.versions.bun ? `bun add ${pkg}` : `npm install ${pkg}`;
3022
3029
  /**
@@ -3095,10 +3102,6 @@ const cloud = {
3095
3102
  };
3096
3103
  //#endregion
3097
3104
  //#region src/fusor/auth.ts
3098
- const log$2 = createLogger("spectrum.fusor.auth");
3099
- const RENEWAL_RATIO = .8;
3100
- const EXPIRY_BUFFER_MS = 3e4;
3101
- const RETRY_DELAY_MS = 3e4;
3102
3105
  /**
3103
3106
  * Single-token provider for the fusor stream. Mirrors the renewal cadence
3104
3107
  * of the slack provider package's auth but without per-team bookkeeping —
@@ -3107,83 +3110,23 @@ const RETRY_DELAY_MS = 3e4;
3107
3110
  function createFusorTokenProvider(projectId, projectSecret) {
3108
3111
  return (async () => {
3109
3112
  let tokenData = await cloud.issueFusorToken(projectId, projectSecret);
3110
- let tokenExpiresAt = Date.now() + tokenData.expiresIn * 1e3;
3111
- let disposed = false;
3112
- let renewalTimer;
3113
- let refreshFailures = 0;
3114
- const clearRenewalTimer = () => {
3115
- if (renewalTimer !== void 0) {
3116
- clearTimeout(renewalTimer);
3117
- renewalTimer = void 0;
3118
- }
3119
- };
3120
- const refresh = async () => {
3121
- tokenData = await cloud.issueFusorToken(projectId, projectSecret);
3122
- tokenExpiresAt = Date.now() + tokenData.expiresIn * 1e3;
3123
- };
3124
- const onRefreshSuccess = () => {
3125
- if (refreshFailures > 0) {
3126
- log$2.info("fusor token refresh recovered", { "spectrum.fusor.auth.attempt": refreshFailures });
3127
- refreshFailures = 0;
3113
+ const renewal = createTokenRenewal({
3114
+ expiresInSeconds: () => tokenData.expiresIn,
3115
+ name: "fusor",
3116
+ refresh: async () => {
3117
+ tokenData = await cloud.issueFusorToken(projectId, projectSecret);
3128
3118
  }
3129
- };
3130
- const onRefreshFailure = (error) => {
3131
- refreshFailures += 1;
3132
- log$2.warn("fusor token refresh failed; retrying", {
3133
- "spectrum.fusor.auth.attempt": refreshFailures,
3134
- "spectrum.fusor.auth.retry_in_ms": RETRY_DELAY_MS,
3135
- ...errorAttrs(error)
3136
- }, error);
3137
- };
3138
- const scheduleRetry = () => {
3139
- if (disposed) return;
3140
- clearRenewalTimer();
3141
- renewalTimer = setTimeout(async () => {
3142
- if (disposed) return;
3143
- try {
3144
- await refresh();
3145
- onRefreshSuccess();
3146
- scheduleRenewal();
3147
- } catch (retryErr) {
3148
- onRefreshFailure(retryErr);
3149
- scheduleRetry();
3150
- }
3151
- }, RETRY_DELAY_MS);
3152
- renewalTimer?.unref?.();
3153
- };
3154
- const scheduleRenewal = () => {
3155
- if (disposed) return;
3156
- clearRenewalTimer();
3157
- const ttlMs = tokenData.expiresIn * 1e3;
3158
- const renewInMs = Math.max(ttlMs * RENEWAL_RATIO, 5e3);
3159
- renewalTimer = setTimeout(async () => {
3160
- try {
3161
- await refresh();
3162
- onRefreshSuccess();
3163
- scheduleRenewal();
3164
- } catch (err) {
3165
- onRefreshFailure(err);
3166
- scheduleRetry();
3167
- }
3168
- }, renewInMs);
3169
- renewalTimer?.unref?.();
3170
- };
3171
- scheduleRenewal();
3119
+ });
3172
3120
  return {
3173
3121
  async getToken() {
3174
- if (Date.now() >= tokenExpiresAt - EXPIRY_BUFFER_MS) {
3175
- await refresh();
3176
- onRefreshSuccess();
3177
- scheduleRenewal();
3178
- }
3122
+ await renewal.refreshIfNeeded();
3179
3123
  return tokenData.token;
3180
3124
  },
3181
3125
  invalidate() {
3182
- tokenExpiresAt = 0;
3126
+ renewal.invalidate();
3183
3127
  },
3184
3128
  async dispose() {
3185
- disposed = true;
3186
- clearRenewalTimer();
3129
+ renewal.dispose();
3187
3130
  }
3188
3131
  };
3189
3132
  })();
@@ -3653,6 +3596,16 @@ var FusorCore = class {
3653
3596
  }
3654
3597
  };
3655
3598
  //#endregion
3599
+ //#region src/platform/unique-names.ts
3600
+ const assertUniquePlatformNames = (providers) => {
3601
+ const names = /* @__PURE__ */ new Set();
3602
+ for (const provider of providers) {
3603
+ const name = provider.__definition.name;
3604
+ if (names.has(name)) throw new Error(`Spectrum received multiple providers for platform "${name}". Register exactly one provider per platform.`);
3605
+ names.add(name);
3606
+ }
3607
+ };
3608
+ //#endregion
3656
3609
  //#region src/utils/store.ts
3657
3610
  const isRecordObject = (value) => {
3658
3611
  if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
@@ -4077,6 +4030,7 @@ async function Spectrum(options) {
4077
4030
  projectId,
4078
4031
  projectSecret
4079
4032
  });
4033
+ assertUniquePlatformNames(options.providers);
4080
4034
  const { options: runtimeOptions, providers, telemetry, webhookSecret } = options;
4081
4035
  const flattenGroups = runtimeOptions?.flattenGroups ?? false;
4082
4036
  applyLogLevel(runtimeOptions?.logLevel);
@@ -1,5 +1,5 @@
1
1
  import z from "zod";
2
- import { createInstrumentedFetch, sanitizeEmail, sanitizeErrorMessage, sanitizePhone, sanitizeUrl } from "@photon-ai/otel";
2
+ import { createInstrumentedFetch, createLogger, sanitizeEmail, sanitizeErrorMessage, sanitizePhone, sanitizeUrl } from "@photon-ai/otel";
3
3
  import { randomUUID } from "node:crypto";
4
4
  import { createReadStream } from "node:fs";
5
5
  import { readFile, stat } from "node:fs/promises";
@@ -1249,8 +1249,8 @@ const fromEnv = (envKey, schema) => z.preprocess((value) => {
1249
1249
  * Normalize a platform id into the env-var prefix segment: upper-case, with any
1250
1250
  * run of non-alphanumeric characters collapsed to a single `_`. This is a
1251
1251
  * whole-name transform, NOT camelCase splitting, so it reproduces the prefixes
1252
- * the adapters used by hand `"telegram"` → `TELEGRAM`,
1253
- * `"WhatsApp Business"` `WHATSAPP_BUSINESS`, `"Slack"` `SLACK`.
1252
+ * canonical platform ids use lowercase snake_case, so `"telegram"` becomes
1253
+ * `TELEGRAM` and `"whatsapp_business"` becomes `WHATSAPP_BUSINESS`.
1254
1254
  */
1255
1255
  const NON_ALPHANUMERIC_RUN = /[^A-Z0-9]+/;
1256
1256
  const normalizePlatformName = (name) => name.toUpperCase().split(NON_ALPHANUMERIC_RUN).filter(Boolean).join("_");
@@ -1576,6 +1576,85 @@ function errorAttrs(error, prefix = "spectrum.error") {
1576
1576
  };
1577
1577
  }
1578
1578
  //#endregion
1579
+ //#region src/utils/token-renewal.ts
1580
+ const RENEWAL_RATIO = .8;
1581
+ const EXPIRY_BUFFER_MS = 3e4;
1582
+ const MIN_RENEWAL_DELAY_MS = 5e3;
1583
+ const RETRY_DELAY_MS = 3e4;
1584
+ const createTokenRenewal = (options) => {
1585
+ const telemetryPrefix = `spectrum.${options.name}.auth`;
1586
+ const log = createLogger(telemetryPrefix);
1587
+ let disposed = false;
1588
+ let refreshFailures = 0;
1589
+ let refreshInFlight;
1590
+ let renewalTimer;
1591
+ let tokenExpiresAt = Date.now() + options.expiresInSeconds() * 1e3;
1592
+ const clearRenewalTimer = () => {
1593
+ if (renewalTimer !== void 0) {
1594
+ clearTimeout(renewalTimer);
1595
+ renewalTimer = void 0;
1596
+ }
1597
+ };
1598
+ const onRefreshSuccess = () => {
1599
+ if (refreshFailures > 0) {
1600
+ log.info(`${options.name} token refresh recovered`, { [`${telemetryPrefix}.attempt`]: refreshFailures });
1601
+ refreshFailures = 0;
1602
+ }
1603
+ };
1604
+ const onRefreshFailure = (error) => {
1605
+ refreshFailures += 1;
1606
+ log.warn(`${options.name} token refresh failed; retrying`, {
1607
+ [`${telemetryPrefix}.attempt`]: refreshFailures,
1608
+ [`${telemetryPrefix}.retry_in_ms`]: RETRY_DELAY_MS,
1609
+ ...errorAttrs(error)
1610
+ }, error);
1611
+ };
1612
+ const refreshNow = async () => {
1613
+ await options.refresh();
1614
+ tokenExpiresAt = Date.now() + options.expiresInSeconds() * 1e3;
1615
+ onRefreshSuccess();
1616
+ scheduleRenewal();
1617
+ };
1618
+ const coalescedRefresh = () => {
1619
+ if (!refreshInFlight) refreshInFlight = refreshNow().finally(() => {
1620
+ refreshInFlight = void 0;
1621
+ });
1622
+ return refreshInFlight;
1623
+ };
1624
+ const runScheduledRefresh = () => {
1625
+ if (disposed) return;
1626
+ coalescedRefresh().catch((error) => {
1627
+ onRefreshFailure(error);
1628
+ if (disposed) return;
1629
+ renewalTimer = setTimeout(runScheduledRefresh, RETRY_DELAY_MS);
1630
+ renewalTimer?.unref?.();
1631
+ });
1632
+ };
1633
+ const scheduleRenewal = () => {
1634
+ if (disposed) return;
1635
+ clearRenewalTimer();
1636
+ const ttlMs = options.expiresInSeconds() * 1e3;
1637
+ const renewInMs = Math.max(ttlMs * RENEWAL_RATIO, MIN_RENEWAL_DELAY_MS);
1638
+ renewalTimer = setTimeout(runScheduledRefresh, renewInMs);
1639
+ renewalTimer?.unref?.();
1640
+ };
1641
+ scheduleRenewal();
1642
+ return {
1643
+ dispose() {
1644
+ disposed = true;
1645
+ clearRenewalTimer();
1646
+ },
1647
+ forceRefresh: coalescedRefresh,
1648
+ invalidate() {
1649
+ tokenExpiresAt = 0;
1650
+ },
1651
+ async refreshIfNeeded() {
1652
+ if (Date.now() < tokenExpiresAt - EXPIRY_BUFFER_MS) return;
1653
+ await coalescedRefresh();
1654
+ }
1655
+ };
1656
+ };
1657
+ //#endregion
1579
1658
  //#region src/utils/stream.ts
1580
1659
  /**
1581
1660
  * Unbounded FIFO queue with `AsyncIterable` consumer. Used by FusorCore to feed
@@ -1746,4 +1825,4 @@ function broadcast(source) {
1746
1825
  };
1747
1826
  }
1748
1827
  //#endregion
1749
- export { custom as $, asPoll as A, asMarkdown as B, rename as C, asReaction as D, read as E, addMemberSchema as F, groupSchema as G, markdownSchema as H, leaveSpace as I, text as J, resolveContents as K, leaveSpaceSchema as L, option as M, poll as N, reaction as O, addMember as P, asCustom as Q, removeMember as R, replySchema as S, asRead as T, asGroup as U, markdown as V, group as W, StreamConsumedError as X, textSchema as Y, drainStreamText as Z, unsend as _, contentAttrs as a, avatarSchema as at, asReply as b, markdownToPlainText as c, asAttachment as ct, envAwareConfig as d, fetchUrlBytes as dt, asContact as et, envFor as f, tracedFetch as ft, asUnsend as g, voice as h, stream as i, avatar as it, asPollOption as j, reactionSchema as k, renderInlineTokens as l, attachment as lt, asVoice as m, createAsyncQueue as n, fromVCard as nt, errorAttrs as o, buildPhotoAction as ot, fromEnv as p, asText as q, mergeStreams as r, toVCard as rt, senderAttrs as s, photoActionSchema as st, broadcast as t, contact as tt, classifyIdentifier as u, attachmentSchema as ut, asRichlink as v, renameSchema as w, reply as x, richlink as y, removeMemberSchema as z };
1828
+ export { asCustom as $, reactionSchema as A, removeMemberSchema as B, replySchema as C, read as D, asRead as E, addMember as F, group as G, markdown as H, addMemberSchema as I, asText as J, groupSchema as K, leaveSpace as L, asPollOption as M, option as N, asReaction as O, poll as P, drainStreamText as Q, leaveSpaceSchema as R, reply as S, renameSchema as T, markdownSchema as U, asMarkdown as V, asGroup as W, textSchema as X, text as Y, StreamConsumedError as Z, asUnsend as _, createTokenRenewal as a, avatar as at, richlink as b, senderAttrs as c, photoActionSchema as ct, classifyIdentifier as d, attachmentSchema as dt, custom as et, envAwareConfig as f, fetchUrlBytes as ft, voice as g, asVoice as h, stream as i, toVCard as it, asPoll as j, reaction as k, markdownToPlainText as l, asAttachment as lt, fromEnv as m, createAsyncQueue as n, contact as nt, contentAttrs as o, avatarSchema as ot, envFor as p, tracedFetch as pt, resolveContents as q, mergeStreams as r, fromVCard as rt, errorAttrs as s, buildPhotoAction as st, broadcast as t, asContact as tt, renderInlineTokens as u, attachment as ut, unsend as v, rename as w, asReply as x, asRichlink as y, removeMember as z };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spectrum-ts/core",
3
- "version": "12.0.0",
3
+ "version": "12.2.0",
4
4
  "description": "The spectrum-ts runtime — Spectrum, content builders, fusor, and the provider authoring API.",
5
5
  "repository": {
6
6
  "type": "git",