spectrum-ts 1.17.0 → 1.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/authoring.js CHANGED
@@ -1,23 +1,24 @@
1
+ import { createRequire as __spectrumCreateRequire } from "node:module"; const require = __spectrumCreateRequire(import.meta.url);
1
2
  import {
2
3
  asGroup,
3
4
  asRichlink
4
- } from "./chunk-LJM5D2T5.js";
5
+ } from "./chunk-JQN6CRSC.js";
5
6
  import {
6
7
  asVoice
7
- } from "./chunk-5QSNQ6YN.js";
8
+ } from "./chunk-NNY6LMSC.js";
8
9
  import {
9
10
  asPoll,
10
11
  asPollOption
11
- } from "./chunk-KO67KDBD.js";
12
+ } from "./chunk-2D27WW5B.js";
12
13
  import {
13
14
  asContact
14
- } from "./chunk-RNGEA4UW.js";
15
+ } from "./chunk-QGJFZMD5.js";
15
16
  import {
16
17
  asAttachment,
17
18
  asCustom,
18
19
  asReaction,
19
20
  asText
20
- } from "./chunk-FTLJVVH7.js";
21
+ } from "./chunk-2ILTJC35.js";
21
22
  export {
22
23
  asAttachment,
23
24
  asContact,
@@ -1,3 +1,5 @@
1
+ import { createRequire as __spectrumCreateRequire } from "node:module"; const require = __spectrumCreateRequire(import.meta.url);
2
+
1
3
  // src/content/poll.ts
2
4
  import z from "zod";
3
5
  var pollChoiceSchema = z.object({
@@ -1,3 +1,4 @@
1
+ import { createRequire as __spectrumCreateRequire } from "node:module"; const require = __spectrumCreateRequire(import.meta.url);
1
2
  var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -1,18 +1,19 @@
1
+ import { createRequire as __spectrumCreateRequire } from "node:module"; const require = __spectrumCreateRequire(import.meta.url);
1
2
  import {
2
3
  cloud,
3
4
  mergeStreams,
4
5
  stream
5
- } from "./chunk-KYEHKHXT.js";
6
+ } from "./chunk-MC6ZKFSG.js";
6
7
  import {
7
8
  UnsupportedError,
8
9
  definePlatform
9
- } from "./chunk-32H3VVUE.js";
10
+ } from "./chunk-IPOFBAIM.js";
10
11
  import {
11
12
  asAttachment,
12
13
  asCustom,
13
14
  asReaction,
14
15
  asText
15
- } from "./chunk-FTLJVVH7.js";
16
+ } from "./chunk-2ILTJC35.js";
16
17
 
17
18
  // src/providers/slack/index.ts
18
19
  import { createClient as createClient2, staticTokens } from "@photon-ai/slack";
@@ -1,20 +1,21 @@
1
+ import { createRequire as __spectrumCreateRequire } from "node:module"; const require = __spectrumCreateRequire(import.meta.url);
1
2
  import {
2
3
  asVoice
3
- } from "./chunk-5QSNQ6YN.js";
4
+ } from "./chunk-NNY6LMSC.js";
4
5
  import {
5
6
  asContact,
6
7
  fromVCard,
7
8
  toVCard
8
- } from "./chunk-RNGEA4UW.js";
9
+ } from "./chunk-QGJFZMD5.js";
9
10
  import {
10
11
  UnsupportedError,
11
12
  definePlatform
12
- } from "./chunk-32H3VVUE.js";
13
+ } from "./chunk-IPOFBAIM.js";
13
14
  import {
14
15
  asAttachment,
15
16
  asCustom,
16
17
  reactionSchema
17
- } from "./chunk-FTLJVVH7.js";
18
+ } from "./chunk-2ILTJC35.js";
18
19
 
19
20
  // src/providers/terminal/index.ts
20
21
  import { spawn } from "child_process";
@@ -1,24 +1,25 @@
1
+ import { createRequire as __spectrumCreateRequire } from "node:module"; const require = __spectrumCreateRequire(import.meta.url);
1
2
  import {
2
3
  asPollOption
3
- } from "./chunk-KO67KDBD.js";
4
+ } from "./chunk-2D27WW5B.js";
4
5
  import {
5
6
  cloud,
6
7
  mergeStreams,
7
8
  stream
8
- } from "./chunk-KYEHKHXT.js";
9
+ } from "./chunk-MC6ZKFSG.js";
9
10
  import {
10
11
  asContact
11
- } from "./chunk-RNGEA4UW.js";
12
+ } from "./chunk-QGJFZMD5.js";
12
13
  import {
13
14
  UnsupportedError,
14
15
  definePlatform
15
- } from "./chunk-32H3VVUE.js";
16
+ } from "./chunk-IPOFBAIM.js";
16
17
  import {
17
18
  asAttachment,
18
19
  asCustom,
19
20
  asReaction,
20
21
  asText
21
- } from "./chunk-FTLJVVH7.js";
22
+ } from "./chunk-2ILTJC35.js";
22
23
 
23
24
  // src/providers/whatsapp-business/index.ts
24
25
  import { createClient as createClient2 } from "@photon-ai/whatsapp-business";
@@ -1,9 +1,10 @@
1
+ import { createRequire as __spectrumCreateRequire } from "node:module"; const require = __spectrumCreateRequire(import.meta.url);
1
2
  import {
2
3
  fetchUrlBytes,
3
4
  reaction,
4
5
  readSchema,
5
6
  resolveContents
6
- } from "./chunk-FTLJVVH7.js";
7
+ } from "./chunk-2ILTJC35.js";
7
8
 
8
9
  // src/content/avatar.ts
9
10
  import z2 from "zod";
@@ -1,10 +1,11 @@
1
+ import { createRequire as __spectrumCreateRequire } from "node:module"; const require = __spectrumCreateRequire(import.meta.url);
1
2
  import {
2
3
  bufferToStream,
3
4
  fetchUrlBytes,
4
5
  readSchema,
5
6
  resolveContents,
6
7
  streamSchema
7
- } from "./chunk-FTLJVVH7.js";
8
+ } from "./chunk-2ILTJC35.js";
8
9
 
9
10
  // src/content/group.ts
10
11
  import z from "zod";
@@ -1,3 +1,5 @@
1
+ import { createRequire as __spectrumCreateRequire } from "node:module"; const require = __spectrumCreateRequire(import.meta.url);
2
+
1
3
  // src/utils/cloud.ts
2
4
  var SPECTRUM_CLOUD_URL = process.env.SPECTRUM_CLOUD_URL ?? "https://spectrum.photon.codes";
3
5
  var SpectrumCloudError = class extends Error {
@@ -1,9 +1,10 @@
1
+ import { createRequire as __spectrumCreateRequire } from "node:module"; const require = __spectrumCreateRequire(import.meta.url);
1
2
  import {
2
3
  bufferToStream,
3
4
  fetchUrlBytes,
4
5
  readSchema,
5
6
  streamSchema
6
- } from "./chunk-FTLJVVH7.js";
7
+ } from "./chunk-2ILTJC35.js";
7
8
 
8
9
  // src/content/voice.ts
9
10
  import { createReadStream } from "fs";
@@ -1,6 +1,7 @@
1
+ import { createRequire as __spectrumCreateRequire } from "node:module"; const require = __spectrumCreateRequire(import.meta.url);
1
2
  import {
2
3
  readSchema
3
- } from "./chunk-FTLJVVH7.js";
4
+ } from "./chunk-2ILTJC35.js";
4
5
 
5
6
  // src/utils/vcard.ts
6
7
  import vCard from "vcf";
@@ -1,28 +1,29 @@
1
+ import { createRequire as __spectrumCreateRequire } from "node:module"; const require = __spectrumCreateRequire(import.meta.url);
1
2
  import {
2
3
  asGroup,
3
4
  asRichlink,
4
5
  groupSchema
5
- } from "./chunk-LJM5D2T5.js";
6
+ } from "./chunk-JQN6CRSC.js";
6
7
  import {
7
8
  asPoll,
8
9
  asPollOption
9
- } from "./chunk-KO67KDBD.js";
10
+ } from "./chunk-2D27WW5B.js";
10
11
  import {
11
12
  cloud,
12
13
  mergeStreams,
13
14
  stream
14
- } from "./chunk-KYEHKHXT.js";
15
+ } from "./chunk-MC6ZKFSG.js";
15
16
  import {
16
17
  asContact,
17
18
  fromVCard,
18
19
  toVCard
19
- } from "./chunk-RNGEA4UW.js";
20
+ } from "./chunk-QGJFZMD5.js";
20
21
  import {
21
22
  UnsupportedError,
22
23
  buildPhotoAction,
23
24
  definePlatform,
24
25
  photoActionSchema
25
- } from "./chunk-32H3VVUE.js";
26
+ } from "./chunk-IPOFBAIM.js";
26
27
  import {
27
28
  asAttachment,
28
29
  asCustom,
@@ -31,7 +32,7 @@ import {
31
32
  reactionSchema,
32
33
  text,
33
34
  textSchema
34
- } from "./chunk-FTLJVVH7.js";
35
+ } from "./chunk-2ILTJC35.js";
35
36
 
36
37
  // src/providers/imessage/index.ts
37
38
  import { createClient as createClient2, MessageEffect as MessageEffect2 } from "@photon-ai/advanced-imessage";
@@ -59,21 +60,79 @@ function background(input, options) {
59
60
  };
60
61
  }
61
62
 
63
+ // src/providers/imessage/content/customized-mini-app.ts
64
+ import z2 from "zod";
65
+ var layoutSchema = z2.object({
66
+ caption: z2.string().nonempty().optional(),
67
+ subcaption: z2.string().nonempty().optional(),
68
+ trailingCaption: z2.string().nonempty().optional(),
69
+ trailingSubcaption: z2.string().nonempty().optional(),
70
+ image: z2.instanceof(Uint8Array).optional(),
71
+ imageTitle: z2.string().nonempty().optional(),
72
+ imageSubtitle: z2.string().nonempty().optional(),
73
+ summary: z2.string().nonempty().optional()
74
+ }).refine(
75
+ (layout) => layout.caption !== void 0 || layout.subcaption !== void 0 || layout.trailingCaption !== void 0 || layout.trailingSubcaption !== void 0 || layout.image !== void 0,
76
+ {
77
+ message: "layout must set at least one of caption, subcaption, trailingCaption, trailingSubcaption, image"
78
+ }
79
+ ).refine(
80
+ (layout) => layout.image === void 0 === (layout.imageTitle === void 0),
81
+ {
82
+ message: "layout.image and layout.imageTitle must be set together",
83
+ path: ["imageTitle"]
84
+ }
85
+ ).refine(
86
+ (layout) => layout.imageSubtitle === void 0 || layout.image !== void 0,
87
+ {
88
+ message: "layout.imageSubtitle requires layout.image",
89
+ path: ["imageSubtitle"]
90
+ }
91
+ );
92
+ var customizedMiniAppSchema = z2.object({
93
+ type: z2.literal("customized-mini-app"),
94
+ __platform: z2.literal("iMessage"),
95
+ // Display name of the owning app, shown by Messages fallback UI.
96
+ appName: z2.string().nonempty(),
97
+ // Apple App Store numeric id of the owning app. Positive when set; omit to
98
+ // send a card whose extension is not published on the App Store.
99
+ appStoreId: z2.number().int().positive().optional(),
100
+ // Bundle identifier of the iMessage extension target.
101
+ extensionBundleId: z2.string().nonempty(),
102
+ // Visible card layout.
103
+ layout: layoutSchema,
104
+ // 10-character uppercase alphanumeric Apple Team ID.
105
+ teamId: z2.string(),
106
+ // Absolute URL delivered to the installed extension on tap.
107
+ url: z2.url()
108
+ });
109
+ var isCustomizedMiniApp = (v) => customizedMiniAppSchema.safeParse(v).success;
110
+ var asCustomizedMiniApp = (input) => customizedMiniAppSchema.parse({
111
+ type: "customized-mini-app",
112
+ __platform: "iMessage",
113
+ ...input
114
+ });
115
+ function customizedMiniApp(input) {
116
+ return {
117
+ build: async () => asCustomizedMiniApp(input)
118
+ };
119
+ }
120
+
62
121
  // src/providers/imessage/content/effect.ts
63
122
  import {
64
123
  MessageEffect
65
124
  } from "@photon-ai/advanced-imessage";
66
125
 
67
126
  // src/content/effect.ts
68
- import z2 from "zod";
69
- var effectInnerSchema = z2.discriminatedUnion("type", [
127
+ import z3 from "zod";
128
+ var effectInnerSchema = z3.discriminatedUnion("type", [
70
129
  textSchema,
71
130
  attachmentSchema
72
131
  ]);
73
- var messageEffectSchema = z2.object({
74
- type: z2.literal("effect"),
132
+ var messageEffectSchema = z3.object({
133
+ type: z3.literal("effect"),
75
134
  content: effectInnerSchema,
76
- effect: z2.string().nonempty()
135
+ effect: z3.string().nonempty()
77
136
  });
78
137
 
79
138
  // src/providers/imessage/content/effect.ts
@@ -103,13 +162,13 @@ function effect(input, messageEffect) {
103
162
  }
104
163
 
105
164
  // src/providers/imessage/content/read.ts
106
- import z3 from "zod";
165
+ import z4 from "zod";
107
166
  var isMessage = (v) => typeof v === "object" && v !== null && "id" in v && "content" in v;
108
- var readSchema = z3.object({
109
- type: z3.literal("read"),
110
- __platform: z3.literal("iMessage"),
111
- __fireAndForget: z3.literal(true),
112
- target: z3.custom(isMessage, {
167
+ var readSchema = z4.object({
168
+ type: z4.literal("read"),
169
+ __platform: z4.literal("iMessage"),
170
+ __fireAndForget: z4.literal(true),
171
+ target: z4.custom(isMessage, {
113
172
  message: "read target must be a Message"
114
173
  })
115
174
  });
@@ -137,33 +196,33 @@ import { createClient } from "@photon-ai/advanced-imessage";
137
196
 
138
197
  // src/providers/imessage/types.ts
139
198
  import { IMessageSDK } from "@photon-ai/imessage-kit";
140
- import z4 from "zod";
199
+ import z5 from "zod";
141
200
  var SHARED_PHONE = "shared";
142
201
  var isLocal = (client) => client instanceof IMessageSDK;
143
- var clientEntry = z4.object({
144
- address: z4.string(),
145
- token: z4.string(),
146
- phone: z4.string()
202
+ var clientEntry = z5.object({
203
+ address: z5.string(),
204
+ token: z5.string(),
205
+ phone: z5.string()
147
206
  });
148
- var configSchema = z4.union([
149
- z4.object({ local: z4.literal(true) }),
150
- z4.object({
151
- local: z4.literal(false).optional().default(false),
152
- clients: clientEntry.or(z4.array(clientEntry)).optional()
207
+ var configSchema = z5.union([
208
+ z5.object({ local: z5.literal(true) }),
209
+ z5.object({
210
+ local: z5.literal(false).optional().default(false),
211
+ clients: clientEntry.or(z5.array(clientEntry)).optional()
153
212
  })
154
213
  ]);
155
- var userSchema = z4.object({});
156
- var spaceSchema = z4.object({
157
- id: z4.string(),
158
- type: z4.enum(["dm", "group"]),
159
- phone: z4.string()
214
+ var userSchema = z5.object({});
215
+ var spaceSchema = z5.object({
216
+ id: z5.string(),
217
+ type: z5.enum(["dm", "group"]),
218
+ phone: z5.string()
160
219
  });
161
- var spaceParamsSchema = z4.object({
162
- phone: z4.string().optional()
220
+ var spaceParamsSchema = z5.object({
221
+ phone: z5.string().optional()
163
222
  });
164
- var messageSchema = z4.object({
165
- partIndex: z4.number().int().nonnegative().optional(),
166
- parentId: z4.string().optional()
223
+ var messageSchema = z5.object({
224
+ partIndex: z5.number().int().nonnegative().optional(),
225
+ parentId: z5.string().optional()
167
226
  });
168
227
 
169
228
  // src/providers/imessage/auth.ts
@@ -548,6 +607,18 @@ var setBackground = async (remote, spaceId, content) => {
548
607
  );
549
608
  };
550
609
 
610
+ // src/providers/imessage/remote/customized-mini-app.ts
611
+ var sendCustomizedMiniApp = async (remote, spaceId, content) => {
612
+ const chat = toChatGuid(spaceId);
613
+ const message = await remote.messages.sendCustomizedMiniApp(chat, content);
614
+ return {
615
+ id: message.guid,
616
+ content,
617
+ space: { id: spaceId },
618
+ timestamp: message.dateCreated
619
+ };
620
+ };
621
+
551
622
  // src/providers/imessage/remote/inbound.ts
552
623
  import {
553
624
  NotFoundError as NotFoundError2
@@ -909,7 +980,6 @@ var getMessage3 = async (remote, spaceId, msgId, phone) => {
909
980
  if (childRef) {
910
981
  try {
911
982
  const fetched = await remote.messages.get(
912
- toChatGuid(spaceId),
913
983
  toMessageGuid(childRef.parentGuid)
914
984
  );
915
985
  const parent = await rebuildFromAppleMessage(
@@ -932,10 +1002,7 @@ var getMessage3 = async (remote, spaceId, msgId, phone) => {
932
1002
  }
933
1003
  }
934
1004
  try {
935
- const fetched = await remote.messages.get(
936
- toChatGuid(spaceId),
937
- toMessageGuid(msgId)
938
- );
1005
+ const fetched = await remote.messages.get(toMessageGuid(msgId));
939
1006
  const rebuilt = await rebuildFromAppleMessage(
940
1007
  remote,
941
1008
  fetched,
@@ -974,10 +1041,7 @@ var resolveReactionTarget = async (client, cache, chat, targetGuid, partIndex, p
974
1041
  let candidate = cache.get(targetGuid);
975
1042
  if (!candidate) {
976
1043
  try {
977
- const fetched = await client.messages.get(
978
- toChatGuid(chat),
979
- toMessageGuid(targetGuid)
980
- );
1044
+ const fetched = await client.messages.get(toMessageGuid(targetGuid));
981
1045
  candidate = await rebuildFromAppleMessage(client, fetched, phone, chat);
982
1046
  cacheMessage(cache, candidate);
983
1047
  } catch {
@@ -1986,6 +2050,7 @@ var stopTyping = async (remote, spaceId) => {
1986
2050
  // src/providers/imessage/remote/api.ts
1987
2051
  var messages4 = (clients, projectConfig) => messages3(clients, projectConfig);
1988
2052
  var setBackground2 = async (remote, spaceId, content) => setBackground(remote, spaceId, content);
2053
+ var sendCustomizedMiniApp2 = async (remote, spaceId, content) => sendCustomizedMiniApp(remote, spaceId, content);
1989
2054
  var setDisplayName2 = async (remote, spaceId, content) => setDisplayName(remote, spaceId, content);
1990
2055
  var setIcon2 = async (remote, spaceId, content) => setIcon(remote, spaceId, content);
1991
2056
  var markRead2 = async (remote, spaceId) => {
@@ -2066,6 +2131,17 @@ var handleBackground = async (client, space, content) => {
2066
2131
  const remote = clientForPhone(client, space.phone);
2067
2132
  await setBackground2(remote, space.id, content);
2068
2133
  };
2134
+ var handleCustomizedMiniApp = async (client, space, content) => {
2135
+ if (isLocal(client)) {
2136
+ throw UnsupportedError.action(
2137
+ "customized-mini-app",
2138
+ "iMessage (local mode)",
2139
+ "mini app cards require remote iMessage"
2140
+ );
2141
+ }
2142
+ const remote = clientForPhone(client, space.phone);
2143
+ return await sendCustomizedMiniApp2(remote, space.id, content);
2144
+ };
2069
2145
  var handleRead = async (client, space) => {
2070
2146
  if (isLocal(client)) {
2071
2147
  throw UnsupportedError.action(
@@ -2290,6 +2366,9 @@ var imessage = definePlatform("iMessage", {
2290
2366
  await handleRead(client, space);
2291
2367
  return;
2292
2368
  }
2369
+ if (isCustomizedMiniApp(content)) {
2370
+ return await handleCustomizedMiniApp(client, space, content);
2371
+ }
2293
2372
  if (isLocal(client)) {
2294
2373
  return await send2(client, space.id, content);
2295
2374
  }
@@ -2350,6 +2429,7 @@ var imessage = definePlatform("iMessage", {
2350
2429
 
2351
2430
  export {
2352
2431
  background,
2432
+ customizedMiniApp,
2353
2433
  effect,
2354
2434
  read,
2355
2435
  imessage
package/dist/index.d.ts CHANGED
@@ -373,8 +373,8 @@ declare function rename(displayName: string): ContentBuilder;
373
373
  * `message.reply(content)` is sugar that delegates here. Providers see
374
374
  * `reply` like any other content type and route to a threaded send.
375
375
  *
376
- * Reply cannot wrap `reply`, `edit`, `reaction`, `group`, `typing`, `rename`,
377
- * or `avatar` content.
376
+ * Reply cannot wrap `reply`, `edit`, `reaction`, `group`, `typing`,
377
+ * `rename`, or `avatar` content.
378
378
  */
379
379
  declare const replySchema: z__default.ZodObject<{
380
380
  type: z__default.ZodLiteral<"reply">;
@@ -2645,13 +2645,14 @@ type WebhookHandler = (space: Space, message: Message) => void | Promise<void>;
2645
2645
  /**
2646
2646
  * Raw webhook input for HTTP servers without Web `Request`/`Response` (Express,
2647
2647
  * raw Node). `body` MUST be the exact bytes fusor POSTed — never a re-encoded
2648
- * JSON/text body — so the protobuf decode and the fusor-origin signature check
2649
- * (HMAC over the raw bytes) work. `headers` carry the `X-Spectrum-*` signature
2650
- * headers fusor sets on signed deliveries.
2648
+ * JSON/text body — so the protobuf decode works. `headers` are accepted (so the
2649
+ * natural `{ headers: req.headers, body: req.body }` shape keeps working) but are
2650
+ * not read: inbound authenticity is established by the per-platform `verify()`,
2651
+ * which reads the inner request reconstructed from the envelope.
2651
2652
  */
2652
2653
  interface WebhookRawRequest {
2653
2654
  body: Uint8Array | ArrayBuffer;
2654
- headers: Record<string, string>;
2655
+ headers?: Record<string, string>;
2655
2656
  }
2656
2657
  /** Raw webhook result, written back by the caller as the HTTP response. */
2657
2658
  interface WebhookRawResult {
@@ -2802,7 +2803,6 @@ declare function Spectrum<const Providers extends PlatformProviderConfig[]>(opti
2802
2803
  providers: [...Providers];
2803
2804
  options?: SpectrumOptions;
2804
2805
  telemetry?: boolean;
2805
- webhookSecret?: string;
2806
2806
  }): Promise<SpectrumInstance<Providers> & {
2807
2807
  readonly config: ProjectData;
2808
2808
  }>;
@@ -2812,7 +2812,6 @@ declare function Spectrum<const Providers extends PlatformProviderConfig[]>(opti
2812
2812
  providers: [...Providers];
2813
2813
  options?: SpectrumOptions;
2814
2814
  telemetry?: boolean;
2815
- webhookSecret?: string;
2816
2815
  }): Promise<SpectrumInstance<Providers>>;
2817
2816
 
2818
2817
  type UnsupportedKind = "content" | "action";
package/dist/index.js CHANGED
@@ -1,14 +1,15 @@
1
+ import { createRequire as __spectrumCreateRequire } from "node:module"; const require = __spectrumCreateRequire(import.meta.url);
1
2
  import {
2
3
  group,
3
4
  richlink
4
- } from "./chunk-LJM5D2T5.js";
5
+ } from "./chunk-JQN6CRSC.js";
5
6
  import {
6
7
  voice
7
- } from "./chunk-5QSNQ6YN.js";
8
+ } from "./chunk-NNY6LMSC.js";
8
9
  import {
9
10
  option,
10
11
  poll
11
- } from "./chunk-KO67KDBD.js";
12
+ } from "./chunk-2D27WW5B.js";
12
13
  import {
13
14
  SpectrumCloudError,
14
15
  broadcast,
@@ -16,12 +17,12 @@ import {
16
17
  createAsyncQueue,
17
18
  mergeStreams,
18
19
  stream
19
- } from "./chunk-KYEHKHXT.js";
20
+ } from "./chunk-MC6ZKFSG.js";
20
21
  import {
21
22
  contact,
22
23
  fromVCard,
23
24
  toVCard
24
- } from "./chunk-RNGEA4UW.js";
25
+ } from "./chunk-QGJFZMD5.js";
25
26
  import {
26
27
  UnsupportedError,
27
28
  avatar,
@@ -35,7 +36,7 @@ import {
35
36
  senderAttrs,
36
37
  typing,
37
38
  wrapProviderMessage
38
- } from "./chunk-32H3VVUE.js";
39
+ } from "./chunk-IPOFBAIM.js";
39
40
  import {
40
41
  __commonJS,
41
42
  __esm,
@@ -48,7 +49,7 @@ import {
48
49
  reaction,
49
50
  resolveContents,
50
51
  text
51
- } from "./chunk-FTLJVVH7.js";
52
+ } from "./chunk-2ILTJC35.js";
52
53
 
53
54
  // ../../node_modules/@grpc/grpc-js/build/src/constants.js
54
55
  var require_constants = __commonJS({
@@ -27500,33 +27501,6 @@ var FusorCore = class {
27500
27501
  }
27501
27502
  };
27502
27503
 
27503
- // src/fusor/origin.ts
27504
- import { createHmac, timingSafeEqual } from "crypto";
27505
- var SIGNATURE_HEADER = "x-spectrum-signature";
27506
- var TIMESTAMP_HEADER = "x-spectrum-timestamp";
27507
- var SIGNATURE_PREFIX = "v0=";
27508
- var stripPrefix = (value) => value.startsWith(SIGNATURE_PREFIX) ? value.slice(SIGNATURE_PREFIX.length) : value;
27509
- var safeEqualHex = (a, b) => {
27510
- const left = Buffer.from(a, "hex");
27511
- const right = Buffer.from(b, "hex");
27512
- if (left.length === 0 || left.length !== right.length) {
27513
- return false;
27514
- }
27515
- return timingSafeEqual(left, right);
27516
- };
27517
- var verifyFusorSignature = (secret, headers, body) => {
27518
- const timestamp = headers[TIMESTAMP_HEADER];
27519
- const signature = headers[SIGNATURE_HEADER];
27520
- if (!(timestamp && signature)) {
27521
- throw new Error("fusor webhook is missing X-Spectrum-* signature headers");
27522
- }
27523
- const base = Buffer.concat([Buffer.from(`v0:${timestamp}:`, "utf8"), body]);
27524
- const expected = SIGNATURE_PREFIX + createHmac("sha256", secret).update(base).digest("hex");
27525
- if (!safeEqualHex(stripPrefix(expected), stripPrefix(signature))) {
27526
- throw new Error("fusor webhook signature mismatch");
27527
- }
27528
- };
27529
-
27530
27504
  // src/utils/store.ts
27531
27505
  var isRecordObject = (value) => {
27532
27506
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
@@ -27595,16 +27569,14 @@ var spectrumConfigSchema = z.union([
27595
27569
  projectSecret: z.string().min(1),
27596
27570
  providers: z.array(z.custom()),
27597
27571
  options: spectrumOptionsSchema,
27598
- telemetry: z.boolean().optional(),
27599
- webhookSecret: z.string().min(1).optional()
27572
+ telemetry: z.boolean().optional()
27600
27573
  }),
27601
27574
  z.object({
27602
27575
  projectId: z.undefined().optional(),
27603
27576
  projectSecret: z.undefined().optional(),
27604
27577
  providers: z.array(z.custom()),
27605
27578
  options: spectrumOptionsSchema,
27606
- telemetry: z.boolean().optional(),
27607
- webhookSecret: z.string().min(1).optional()
27579
+ telemetry: z.boolean().optional()
27608
27580
  })
27609
27581
  ]);
27610
27582
  function bootstrapTelemetry(opts) {
@@ -27634,8 +27606,7 @@ async function Spectrum(options) {
27634
27606
  projectSecret,
27635
27607
  providers,
27636
27608
  options: runtimeOptions,
27637
- telemetry,
27638
- webhookSecret
27609
+ telemetry
27639
27610
  } = options;
27640
27611
  const flattenGroups = runtimeOptions?.flattenGroups ?? false;
27641
27612
  const otelHandle = telemetry ? bootstrapTelemetry({ projectId, projectSecret }) : void 0;
@@ -28002,23 +27973,14 @@ async function Spectrum(options) {
28002
27973
  };
28003
27974
  const readWebhookInput = async (request) => {
28004
27975
  if (typeof Request !== "undefined" && request instanceof Request) {
28005
- const headers2 = {};
28006
- request.headers.forEach((value, key) => {
28007
- headers2[key.toLowerCase()] = value;
28008
- });
28009
27976
  return {
28010
27977
  asWeb: true,
28011
- bodyBytes: new Uint8Array(await request.arrayBuffer()),
28012
- headers: headers2
27978
+ bodyBytes: new Uint8Array(await request.arrayBuffer())
28013
27979
  };
28014
27980
  }
28015
27981
  const raw = request;
28016
27982
  const bodyBytes = raw.body instanceof ArrayBuffer ? new Uint8Array(raw.body) : raw.body;
28017
- const headers = {};
28018
- for (const [key, value] of Object.entries(raw.headers ?? {})) {
28019
- headers[key.toLowerCase()] = value;
28020
- }
28021
- return { asWeb: false, bodyBytes, headers };
27983
+ return { asWeb: false, bodyBytes };
28022
27984
  };
28023
27985
  const deliverWebhookMessages = async (collected, runtime, handler, event) => {
28024
27986
  for (const record of collected) {
@@ -28040,12 +28002,6 @@ async function Spectrum(options) {
28040
28002
  }
28041
28003
  }
28042
28004
  };
28043
- const assertFusorOrigin = (bodyBytes, headers) => {
28044
- if (!webhookSecret) {
28045
- return;
28046
- }
28047
- verifyFusorSignature(webhookSecret, headers, bodyBytes);
28048
- };
28049
28005
  const decodeWebhookEvent = (bodyBytes) => {
28050
28006
  try {
28051
28007
  return RawInboundEvent2.decode(bodyBytes);
@@ -28096,19 +28052,7 @@ async function Spectrum(options) {
28096
28052
  "spectrum.webhook() requires at least one fusor provider; none are configured"
28097
28053
  );
28098
28054
  }
28099
- const { asWeb, bodyBytes, headers } = await readWebhookInput(request);
28100
- try {
28101
- assertFusorOrigin(bodyBytes, headers);
28102
- } catch (error) {
28103
- lifecycleLog.warn("spectrum.webhook: fusor origin verification failed", {
28104
- error: error instanceof Error ? error.message : String(error)
28105
- });
28106
- return buildWebhookResult(asWeb, {
28107
- status: 401,
28108
- headers: {},
28109
- body: new Uint8Array(0)
28110
- });
28111
- }
28055
+ const { asWeb, bodyBytes } = await readWebhookInput(request);
28112
28056
  const event = decodeWebhookEvent(bodyBytes);
28113
28057
  if (!event) {
28114
28058
  return buildWebhookResult(asWeb, {
@@ -39,6 +39,78 @@ declare function background(input: string | Buffer | URL, options?: {
39
39
  mimeType?: string;
40
40
  }): ContentBuilder;
41
41
 
42
+ /**
43
+ * Visible layout of a mini-app card. Mirrors Apple's
44
+ * `MSMessageTemplateLayout`. At least one of `caption`, `subcaption`,
45
+ * `trailingCaption`, `trailingSubcaption`, or `image` must be set so the
46
+ * bubble is not empty — `summary` is the fallback text shown on surfaces
47
+ * that cannot render the card (notifications, lock screen) and is not a
48
+ * visible slot on its own. `image` and `imageTitle` must be set together;
49
+ * `imageSubtitle` requires `image`.
50
+ */
51
+ declare const layoutSchema: z__default.ZodObject<{
52
+ caption: z__default.ZodOptional<z__default.ZodString>;
53
+ subcaption: z__default.ZodOptional<z__default.ZodString>;
54
+ trailingCaption: z__default.ZodOptional<z__default.ZodString>;
55
+ trailingSubcaption: z__default.ZodOptional<z__default.ZodString>;
56
+ image: z__default.ZodOptional<z__default.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>;
57
+ imageTitle: z__default.ZodOptional<z__default.ZodString>;
58
+ imageSubtitle: z__default.ZodOptional<z__default.ZodString>;
59
+ summary: z__default.ZodOptional<z__default.ZodString>;
60
+ }, z__default.core.$strip>;
61
+ /**
62
+ * iMessage-only mini-app card content. Lives entirely under the iMessage
63
+ * provider — never enters the universal `Content` discriminated union. The
64
+ * framework recognizes it via the generic content-level platform contract:
65
+ *
66
+ * - `__platform: "iMessage"` — `findUnsupportedPlatformContent` reads this tag
67
+ * and warns-and-skips when a different platform receives it.
68
+ *
69
+ * Unlike `background` / `read`, this content is **not** `__fireAndForget`: it
70
+ * produces a real outbound message, so the iMessage `send` handler narrows
71
+ * back to `CustomizedMiniApp` via the `isCustomizedMiniApp` guard and returns
72
+ * the resulting `ProviderMessageRecord` (rather than `void`).
73
+ */
74
+ declare const customizedMiniAppSchema: z__default.ZodObject<{
75
+ type: z__default.ZodLiteral<"customized-mini-app">;
76
+ __platform: z__default.ZodLiteral<"iMessage">;
77
+ appName: z__default.ZodString;
78
+ appStoreId: z__default.ZodOptional<z__default.ZodNumber>;
79
+ extensionBundleId: z__default.ZodString;
80
+ layout: z__default.ZodObject<{
81
+ caption: z__default.ZodOptional<z__default.ZodString>;
82
+ subcaption: z__default.ZodOptional<z__default.ZodString>;
83
+ trailingCaption: z__default.ZodOptional<z__default.ZodString>;
84
+ trailingSubcaption: z__default.ZodOptional<z__default.ZodString>;
85
+ image: z__default.ZodOptional<z__default.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>;
86
+ imageTitle: z__default.ZodOptional<z__default.ZodString>;
87
+ imageSubtitle: z__default.ZodOptional<z__default.ZodString>;
88
+ summary: z__default.ZodOptional<z__default.ZodString>;
89
+ }, z__default.core.$strip>;
90
+ teamId: z__default.ZodString;
91
+ url: z__default.ZodURL;
92
+ }, z__default.core.$strip>;
93
+ type CustomizedMiniApp = z__default.infer<typeof customizedMiniAppSchema>;
94
+ type CustomizedMiniAppLayout = z__default.infer<typeof layoutSchema>;
95
+ type CustomizedMiniAppInput = Omit<CustomizedMiniApp, "type" | "__platform">;
96
+ /**
97
+ * Construct a `customized-mini-app` content value. iMessage-only, remote-only.
98
+ *
99
+ * The layout is what recipients see in the bubble. `teamId` and
100
+ * `extensionBundleId` identify the iMessage extension that receives `url` when
101
+ * the recipient taps the card; the server constructs the matching
102
+ * `MSMessageExtensionBalloonPlugin` plugin id from these values. `appStoreId`
103
+ * is optional and only points recipients without the extension at its App
104
+ * Store entry.
105
+ *
106
+ * `space.send(customizedMiniApp(...))` is the canonical form.
107
+ *
108
+ * `CustomizedMiniApp` is intentionally not a member of the universal `Content`
109
+ * union — the `as unknown as Content` cast keeps the builder shape compatible
110
+ * with the framework's `ContentBuilder.build(): Promise<Content>` signature.
111
+ */
112
+ declare function customizedMiniApp(input: CustomizedMiniAppInput): ContentBuilder;
113
+
42
114
  type IMessageMessageEffect = MessageEffect;
43
115
  declare function effect(input: ContentInput, messageEffect: IMessageMessageEffect): ContentBuilder;
44
116
 
@@ -176,4 +248,4 @@ declare const imessage: Platform<PlatformDef<"iMessage", z.ZodUnion<readonly [z.
176
248
  };
177
249
  }>;
178
250
 
179
- export { type BackgroundInput, type IMessageMessageEffect, background, effect, imessage, read };
251
+ export { type BackgroundInput, type CustomizedMiniApp, type CustomizedMiniAppInput, type CustomizedMiniAppLayout, type IMessageMessageEffect, background, customizedMiniApp, effect, imessage, read };
@@ -1,17 +1,20 @@
1
+ import { createRequire as __spectrumCreateRequire } from "node:module"; const require = __spectrumCreateRequire(import.meta.url);
1
2
  import {
2
3
  background,
4
+ customizedMiniApp,
3
5
  effect,
4
6
  imessage,
5
7
  read
6
- } from "../../chunk-DK4P2SHC.js";
7
- import "../../chunk-LJM5D2T5.js";
8
- import "../../chunk-KO67KDBD.js";
9
- import "../../chunk-KYEHKHXT.js";
10
- import "../../chunk-RNGEA4UW.js";
11
- import "../../chunk-32H3VVUE.js";
12
- import "../../chunk-FTLJVVH7.js";
8
+ } from "../../chunk-YN6WOTBF.js";
9
+ import "../../chunk-JQN6CRSC.js";
10
+ import "../../chunk-2D27WW5B.js";
11
+ import "../../chunk-MC6ZKFSG.js";
12
+ import "../../chunk-QGJFZMD5.js";
13
+ import "../../chunk-IPOFBAIM.js";
14
+ import "../../chunk-2ILTJC35.js";
13
15
  export {
14
16
  background,
17
+ customizedMiniApp,
15
18
  effect,
16
19
  imessage,
17
20
  read
@@ -1,22 +1,23 @@
1
+ import { createRequire as __spectrumCreateRequire } from "node:module"; const require = __spectrumCreateRequire(import.meta.url);
1
2
  import {
2
3
  imessage
3
- } from "../chunk-DK4P2SHC.js";
4
- import "../chunk-LJM5D2T5.js";
4
+ } from "../chunk-YN6WOTBF.js";
5
+ import "../chunk-JQN6CRSC.js";
5
6
  import {
6
7
  slack
7
- } from "../chunk-Y4CZ5JFD.js";
8
+ } from "../chunk-3OTECDNH.js";
8
9
  import {
9
10
  terminal
10
- } from "../chunk-6NGVREPQ.js";
11
- import "../chunk-5QSNQ6YN.js";
11
+ } from "../chunk-5BKZJMZV.js";
12
+ import "../chunk-NNY6LMSC.js";
12
13
  import {
13
14
  whatsappBusiness
14
- } from "../chunk-GHERTIUM.js";
15
- import "../chunk-KO67KDBD.js";
16
- import "../chunk-KYEHKHXT.js";
17
- import "../chunk-RNGEA4UW.js";
18
- import "../chunk-32H3VVUE.js";
19
- import "../chunk-FTLJVVH7.js";
15
+ } from "../chunk-5TIF3FIE.js";
16
+ import "../chunk-2D27WW5B.js";
17
+ import "../chunk-MC6ZKFSG.js";
18
+ import "../chunk-QGJFZMD5.js";
19
+ import "../chunk-IPOFBAIM.js";
20
+ import "../chunk-2ILTJC35.js";
20
21
  export {
21
22
  imessage,
22
23
  slack,
@@ -1,9 +1,10 @@
1
+ import { createRequire as __spectrumCreateRequire } from "node:module"; const require = __spectrumCreateRequire(import.meta.url);
1
2
  import {
2
3
  slack
3
- } from "../../chunk-Y4CZ5JFD.js";
4
- import "../../chunk-KYEHKHXT.js";
5
- import "../../chunk-32H3VVUE.js";
6
- import "../../chunk-FTLJVVH7.js";
4
+ } from "../../chunk-3OTECDNH.js";
5
+ import "../../chunk-MC6ZKFSG.js";
6
+ import "../../chunk-IPOFBAIM.js";
7
+ import "../../chunk-2ILTJC35.js";
7
8
  export {
8
9
  slack
9
10
  };
@@ -1,10 +1,11 @@
1
+ import { createRequire as __spectrumCreateRequire } from "node:module"; const require = __spectrumCreateRequire(import.meta.url);
1
2
  import {
2
3
  terminal
3
- } from "../../chunk-6NGVREPQ.js";
4
- import "../../chunk-5QSNQ6YN.js";
5
- import "../../chunk-RNGEA4UW.js";
6
- import "../../chunk-32H3VVUE.js";
7
- import "../../chunk-FTLJVVH7.js";
4
+ } from "../../chunk-5BKZJMZV.js";
5
+ import "../../chunk-NNY6LMSC.js";
6
+ import "../../chunk-QGJFZMD5.js";
7
+ import "../../chunk-IPOFBAIM.js";
8
+ import "../../chunk-2ILTJC35.js";
8
9
  export {
9
10
  terminal
10
11
  };
@@ -1,11 +1,12 @@
1
+ import { createRequire as __spectrumCreateRequire } from "node:module"; const require = __spectrumCreateRequire(import.meta.url);
1
2
  import {
2
3
  whatsappBusiness
3
- } from "../../chunk-GHERTIUM.js";
4
- import "../../chunk-KO67KDBD.js";
5
- import "../../chunk-KYEHKHXT.js";
6
- import "../../chunk-RNGEA4UW.js";
7
- import "../../chunk-32H3VVUE.js";
8
- import "../../chunk-FTLJVVH7.js";
4
+ } from "../../chunk-5TIF3FIE.js";
5
+ import "../../chunk-2D27WW5B.js";
6
+ import "../../chunk-MC6ZKFSG.js";
7
+ import "../../chunk-QGJFZMD5.js";
8
+ import "../../chunk-IPOFBAIM.js";
9
+ import "../../chunk-2ILTJC35.js";
9
10
  export {
10
11
  whatsappBusiness
11
12
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spectrum-ts",
3
- "version": "1.17.0",
3
+ "version": "1.18.0",
4
4
  "description": "Bring agents to any interface — unified messaging SDK for TypeScript.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -38,7 +38,7 @@
38
38
  "./manifest.json": "./dist/manifest.json"
39
39
  },
40
40
  "dependencies": {
41
- "@photon-ai/advanced-imessage": "^0.10.0",
41
+ "@photon-ai/advanced-imessage": "^0.11.0",
42
42
  "@photon-ai/imessage-kit": "^3.0.0",
43
43
  "@photon-ai/otel": "^0.1.1",
44
44
  "@photon-ai/slack": "^0.2.0",