spectrum-ts 1.17.1 → 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.
@@ -60,21 +60,79 @@ function background(input, options) {
60
60
  };
61
61
  }
62
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
+
63
121
  // src/providers/imessage/content/effect.ts
64
122
  import {
65
123
  MessageEffect
66
124
  } from "@photon-ai/advanced-imessage";
67
125
 
68
126
  // src/content/effect.ts
69
- import z2 from "zod";
70
- var effectInnerSchema = z2.discriminatedUnion("type", [
127
+ import z3 from "zod";
128
+ var effectInnerSchema = z3.discriminatedUnion("type", [
71
129
  textSchema,
72
130
  attachmentSchema
73
131
  ]);
74
- var messageEffectSchema = z2.object({
75
- type: z2.literal("effect"),
132
+ var messageEffectSchema = z3.object({
133
+ type: z3.literal("effect"),
76
134
  content: effectInnerSchema,
77
- effect: z2.string().nonempty()
135
+ effect: z3.string().nonempty()
78
136
  });
79
137
 
80
138
  // src/providers/imessage/content/effect.ts
@@ -104,13 +162,13 @@ function effect(input, messageEffect) {
104
162
  }
105
163
 
106
164
  // src/providers/imessage/content/read.ts
107
- import z3 from "zod";
165
+ import z4 from "zod";
108
166
  var isMessage = (v) => typeof v === "object" && v !== null && "id" in v && "content" in v;
109
- var readSchema = z3.object({
110
- type: z3.literal("read"),
111
- __platform: z3.literal("iMessage"),
112
- __fireAndForget: z3.literal(true),
113
- 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, {
114
172
  message: "read target must be a Message"
115
173
  })
116
174
  });
@@ -138,33 +196,33 @@ import { createClient } from "@photon-ai/advanced-imessage";
138
196
 
139
197
  // src/providers/imessage/types.ts
140
198
  import { IMessageSDK } from "@photon-ai/imessage-kit";
141
- import z4 from "zod";
199
+ import z5 from "zod";
142
200
  var SHARED_PHONE = "shared";
143
201
  var isLocal = (client) => client instanceof IMessageSDK;
144
- var clientEntry = z4.object({
145
- address: z4.string(),
146
- token: z4.string(),
147
- phone: z4.string()
202
+ var clientEntry = z5.object({
203
+ address: z5.string(),
204
+ token: z5.string(),
205
+ phone: z5.string()
148
206
  });
149
- var configSchema = z4.union([
150
- z4.object({ local: z4.literal(true) }),
151
- z4.object({
152
- local: z4.literal(false).optional().default(false),
153
- 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()
154
212
  })
155
213
  ]);
156
- var userSchema = z4.object({});
157
- var spaceSchema = z4.object({
158
- id: z4.string(),
159
- type: z4.enum(["dm", "group"]),
160
- 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()
161
219
  });
162
- var spaceParamsSchema = z4.object({
163
- phone: z4.string().optional()
220
+ var spaceParamsSchema = z5.object({
221
+ phone: z5.string().optional()
164
222
  });
165
- var messageSchema = z4.object({
166
- partIndex: z4.number().int().nonnegative().optional(),
167
- parentId: z4.string().optional()
223
+ var messageSchema = z5.object({
224
+ partIndex: z5.number().int().nonnegative().optional(),
225
+ parentId: z5.string().optional()
168
226
  });
169
227
 
170
228
  // src/providers/imessage/auth.ts
@@ -549,6 +607,18 @@ var setBackground = async (remote, spaceId, content) => {
549
607
  );
550
608
  };
551
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
+
552
622
  // src/providers/imessage/remote/inbound.ts
553
623
  import {
554
624
  NotFoundError as NotFoundError2
@@ -910,7 +980,6 @@ var getMessage3 = async (remote, spaceId, msgId, phone) => {
910
980
  if (childRef) {
911
981
  try {
912
982
  const fetched = await remote.messages.get(
913
- toChatGuid(spaceId),
914
983
  toMessageGuid(childRef.parentGuid)
915
984
  );
916
985
  const parent = await rebuildFromAppleMessage(
@@ -933,10 +1002,7 @@ var getMessage3 = async (remote, spaceId, msgId, phone) => {
933
1002
  }
934
1003
  }
935
1004
  try {
936
- const fetched = await remote.messages.get(
937
- toChatGuid(spaceId),
938
- toMessageGuid(msgId)
939
- );
1005
+ const fetched = await remote.messages.get(toMessageGuid(msgId));
940
1006
  const rebuilt = await rebuildFromAppleMessage(
941
1007
  remote,
942
1008
  fetched,
@@ -975,10 +1041,7 @@ var resolveReactionTarget = async (client, cache, chat, targetGuid, partIndex, p
975
1041
  let candidate = cache.get(targetGuid);
976
1042
  if (!candidate) {
977
1043
  try {
978
- const fetched = await client.messages.get(
979
- toChatGuid(chat),
980
- toMessageGuid(targetGuid)
981
- );
1044
+ const fetched = await client.messages.get(toMessageGuid(targetGuid));
982
1045
  candidate = await rebuildFromAppleMessage(client, fetched, phone, chat);
983
1046
  cacheMessage(cache, candidate);
984
1047
  } catch {
@@ -1987,6 +2050,7 @@ var stopTyping = async (remote, spaceId) => {
1987
2050
  // src/providers/imessage/remote/api.ts
1988
2051
  var messages4 = (clients, projectConfig) => messages3(clients, projectConfig);
1989
2052
  var setBackground2 = async (remote, spaceId, content) => setBackground(remote, spaceId, content);
2053
+ var sendCustomizedMiniApp2 = async (remote, spaceId, content) => sendCustomizedMiniApp(remote, spaceId, content);
1990
2054
  var setDisplayName2 = async (remote, spaceId, content) => setDisplayName(remote, spaceId, content);
1991
2055
  var setIcon2 = async (remote, spaceId, content) => setIcon(remote, spaceId, content);
1992
2056
  var markRead2 = async (remote, spaceId) => {
@@ -2067,6 +2131,17 @@ var handleBackground = async (client, space, content) => {
2067
2131
  const remote = clientForPhone(client, space.phone);
2068
2132
  await setBackground2(remote, space.id, content);
2069
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
+ };
2070
2145
  var handleRead = async (client, space) => {
2071
2146
  if (isLocal(client)) {
2072
2147
  throw UnsupportedError.action(
@@ -2291,6 +2366,9 @@ var imessage = definePlatform("iMessage", {
2291
2366
  await handleRead(client, space);
2292
2367
  return;
2293
2368
  }
2369
+ if (isCustomizedMiniApp(content)) {
2370
+ return await handleCustomizedMiniApp(client, space, content);
2371
+ }
2294
2372
  if (isLocal(client)) {
2295
2373
  return await send2(client, space.id, content);
2296
2374
  }
@@ -2351,6 +2429,7 @@ var imessage = definePlatform("iMessage", {
2351
2429
 
2352
2430
  export {
2353
2431
  background,
2432
+ customizedMiniApp,
2354
2433
  effect,
2355
2434
  read,
2356
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">;
@@ -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,10 +1,11 @@
1
1
  import { createRequire as __spectrumCreateRequire } from "node:module"; const require = __spectrumCreateRequire(import.meta.url);
2
2
  import {
3
3
  background,
4
+ customizedMiniApp,
4
5
  effect,
5
6
  imessage,
6
7
  read
7
- } from "../../chunk-YJMPSD3S.js";
8
+ } from "../../chunk-YN6WOTBF.js";
8
9
  import "../../chunk-JQN6CRSC.js";
9
10
  import "../../chunk-2D27WW5B.js";
10
11
  import "../../chunk-MC6ZKFSG.js";
@@ -13,6 +14,7 @@ import "../../chunk-IPOFBAIM.js";
13
14
  import "../../chunk-2ILTJC35.js";
14
15
  export {
15
16
  background,
17
+ customizedMiniApp,
16
18
  effect,
17
19
  imessage,
18
20
  read
@@ -1,7 +1,7 @@
1
1
  import { createRequire as __spectrumCreateRequire } from "node:module"; const require = __spectrumCreateRequire(import.meta.url);
2
2
  import {
3
3
  imessage
4
- } from "../chunk-YJMPSD3S.js";
4
+ } from "../chunk-YN6WOTBF.js";
5
5
  import "../chunk-JQN6CRSC.js";
6
6
  import {
7
7
  slack
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spectrum-ts",
3
- "version": "1.17.1",
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",