@stardeck-customer-apps/testing 0.10.0 → 0.12.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/SKILL.md +32 -2
- package/dist/index.d.mts +10 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +26 -5
- package/dist/index.mjs +26 -5
- package/dist/setup.js +26 -5
- package/dist/setup.mjs +26 -5
- package/package.json +2 -2
package/SKILL.md
CHANGED
|
@@ -203,6 +203,26 @@ const integrations = createIntegrationsClient();
|
|
|
203
203
|
await integrations.line.push("U123", { type: "text", text: "Your order shipped" });
|
|
204
204
|
expect(app.messages.channel("line").to("U123")[0].body.text).toMatch(/shipped/i);
|
|
205
205
|
|
|
206
|
+
// LINE Flex card — the simulator preserves the complete LINE-native container.
|
|
207
|
+
const card = {
|
|
208
|
+
type: "bubble" as const,
|
|
209
|
+
body: {
|
|
210
|
+
type: "box",
|
|
211
|
+
layout: "vertical",
|
|
212
|
+
contents: [{ type: "text", text: "Order shipped" }],
|
|
213
|
+
},
|
|
214
|
+
};
|
|
215
|
+
await integrations.line.push("U123", {
|
|
216
|
+
type: "flex",
|
|
217
|
+
altText: "Your order shipped",
|
|
218
|
+
contents: card,
|
|
219
|
+
});
|
|
220
|
+
expect(app.messages.channel("line").to("U123")[1].body).toEqual({
|
|
221
|
+
type: "flex",
|
|
222
|
+
altText: "Your order shipped",
|
|
223
|
+
contents: card,
|
|
224
|
+
});
|
|
225
|
+
|
|
206
226
|
// Guest/counter identity resolution. `provenance` is selected by this trusted
|
|
207
227
|
// server route; never copy it from a browser form or request body.
|
|
208
228
|
const identity = await integrations.identities.resolveOrCreate({
|
|
@@ -265,7 +285,14 @@ expect(app.edge.latestDisplay()?.action).toBe("show");
|
|
|
265
285
|
- `app.issueSession(partial?)` — mint access/refresh tokens for cookie-based
|
|
266
286
|
auth flows (`__stardeck:sess`).
|
|
267
287
|
- `app.inbox` — emails sent through the email-sdk: `.latest(addr?)`,
|
|
268
|
-
`.to(addr)`, `.all()`, `.count`, `.clear()`.
|
|
288
|
+
`.to(addr)`, `.all()`, `.count`, `.clear()`. Each captured email carries
|
|
289
|
+
`headers` (`{}` when none were set), so a reply that should thread onto an
|
|
290
|
+
inbound conversation can be asserted:
|
|
291
|
+
|
|
292
|
+
```ts
|
|
293
|
+
expect(app.inbox.latest()?.headers["In-Reply-To"]).toBe("<original@mail.example>");
|
|
294
|
+
```
|
|
295
|
+
|
|
269
296
|
- `app.identities` — the platform-identity directory created through
|
|
270
297
|
integrations-sdk `client.identities`: `.get(id)`, `.links(id)`, `.all()`,
|
|
271
298
|
`.count`, `.clear()`. The test-only `.seedVerifiedLink(id, { kind,
|
|
@@ -288,7 +315,10 @@ externalId })` helper models a trusted platform/channel link, and
|
|
|
288
315
|
list `limit` 1–100, status filter values).
|
|
289
316
|
- `app.storage` — uploads through storage-sdk: `.uploads`, `.latest()`, `.count`, `.clear()`.
|
|
290
317
|
- `app.messages` — outbound Slack/LINE/Facebook sends:
|
|
291
|
-
`.all()`, `.latest()`, `.to(recipient)`, `.channel("line")`, `.count`, `.clear()`.
|
|
318
|
+
`.all()`, `.latest()`, `.to(recipient)`, `.channel("line")`, `.count`, `.clear()`. LINE Flex
|
|
319
|
+
captures expose `body.type`, `body.altText`, and the complete `body.contents` bubble/carousel.
|
|
320
|
+
The simulator validates the Flex envelope like production; LINE itself remains responsible for
|
|
321
|
+
validating nested component layout.
|
|
292
322
|
- `app.edge` — edge print/display/bindings from edge-sdk: `.prints`, `.displays`,
|
|
293
323
|
`.testPrints`, `.latestPrint()`, `.latestDisplay()`, `.bindings`,
|
|
294
324
|
`.seedDevices()`, `.seedPeripherals()`, `.seedBindings()`,
|
package/dist/index.d.mts
CHANGED
|
@@ -53,6 +53,11 @@ interface CapturedEmail {
|
|
|
53
53
|
html?: string;
|
|
54
54
|
text?: string;
|
|
55
55
|
replyTo?: string;
|
|
56
|
+
/**
|
|
57
|
+
* Raw headers the sender attached, e.g. the `In-Reply-To` / `References` pair
|
|
58
|
+
* that threads a reply onto an inbound conversation. Empty when none were set.
|
|
59
|
+
*/
|
|
60
|
+
headers: Record<string, string>;
|
|
56
61
|
attachments: Array<{
|
|
57
62
|
filename: string;
|
|
58
63
|
contentType?: string;
|
|
@@ -252,6 +257,7 @@ interface CapturedMessage {
|
|
|
252
257
|
channel: "slack" | "line" | "facebook";
|
|
253
258
|
recipient: string;
|
|
254
259
|
body: {
|
|
260
|
+
type?: string;
|
|
255
261
|
text?: string;
|
|
256
262
|
blocks?: unknown[];
|
|
257
263
|
threadTs?: string;
|
|
@@ -261,6 +267,10 @@ interface CapturedMessage {
|
|
|
261
267
|
originalContentUrl?: string;
|
|
262
268
|
/** LINE image push — HTTPS URL of the preview; may be omitted when not sent. */
|
|
263
269
|
previewImageUrl?: string;
|
|
270
|
+
/** LINE Flex fallback text shown when the rich card cannot be rendered. */
|
|
271
|
+
altText?: string;
|
|
272
|
+
/** Complete LINE Flex bubble or carousel payload. */
|
|
273
|
+
contents?: Record<string, unknown>;
|
|
264
274
|
};
|
|
265
275
|
connectionId?: string;
|
|
266
276
|
sentAt: Date;
|
package/dist/index.d.ts
CHANGED
|
@@ -53,6 +53,11 @@ interface CapturedEmail {
|
|
|
53
53
|
html?: string;
|
|
54
54
|
text?: string;
|
|
55
55
|
replyTo?: string;
|
|
56
|
+
/**
|
|
57
|
+
* Raw headers the sender attached, e.g. the `In-Reply-To` / `References` pair
|
|
58
|
+
* that threads a reply onto an inbound conversation. Empty when none were set.
|
|
59
|
+
*/
|
|
60
|
+
headers: Record<string, string>;
|
|
56
61
|
attachments: Array<{
|
|
57
62
|
filename: string;
|
|
58
63
|
contentType?: string;
|
|
@@ -252,6 +257,7 @@ interface CapturedMessage {
|
|
|
252
257
|
channel: "slack" | "line" | "facebook";
|
|
253
258
|
recipient: string;
|
|
254
259
|
body: {
|
|
260
|
+
type?: string;
|
|
255
261
|
text?: string;
|
|
256
262
|
blocks?: unknown[];
|
|
257
263
|
threadTs?: string;
|
|
@@ -261,6 +267,10 @@ interface CapturedMessage {
|
|
|
261
267
|
originalContentUrl?: string;
|
|
262
268
|
/** LINE image push — HTTPS URL of the preview; may be omitted when not sent. */
|
|
263
269
|
previewImageUrl?: string;
|
|
270
|
+
/** LINE Flex fallback text shown when the rich card cannot be rendered. */
|
|
271
|
+
altText?: string;
|
|
272
|
+
/** Complete LINE Flex bubble or carousel payload. */
|
|
273
|
+
contents?: Record<string, unknown>;
|
|
264
274
|
};
|
|
265
275
|
connectionId?: string;
|
|
266
276
|
sentAt: Date;
|
package/dist/index.js
CHANGED
|
@@ -660,6 +660,7 @@ async function handleEmailSend(request) {
|
|
|
660
660
|
html: body.html ? String(body.html) : void 0,
|
|
661
661
|
text: body.text ? String(body.text) : void 0,
|
|
662
662
|
replyTo: body.replyTo ? String(body.replyTo) : void 0,
|
|
663
|
+
headers: { ...body.headers ?? {} },
|
|
663
664
|
attachments: (body.attachments ?? []).map((a) => ({
|
|
664
665
|
filename: String(a.filename ?? ""),
|
|
665
666
|
contentType: a.contentType ? String(a.contentType) : void 0
|
|
@@ -2072,6 +2073,9 @@ function isValidLineImageUrl(url) {
|
|
|
2072
2073
|
return false;
|
|
2073
2074
|
}
|
|
2074
2075
|
}
|
|
2076
|
+
function isObject(value) {
|
|
2077
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2078
|
+
}
|
|
2075
2079
|
function captureMessage(channel, recipient, body, connectionId) {
|
|
2076
2080
|
const message = {
|
|
2077
2081
|
channel,
|
|
@@ -2083,7 +2087,8 @@ function captureMessage(channel, recipient, body, connectionId) {
|
|
|
2083
2087
|
messagingType: body.messagingType ? String(body.messagingType) : void 0,
|
|
2084
2088
|
tag: body.tag ? String(body.tag) : void 0,
|
|
2085
2089
|
originalContentUrl: body.originalContentUrl ? String(body.originalContentUrl) : void 0,
|
|
2086
|
-
previewImageUrl: body.previewImageUrl ? String(body.previewImageUrl) : void 0
|
|
2090
|
+
previewImageUrl: body.previewImageUrl ? String(body.previewImageUrl) : void 0,
|
|
2091
|
+
...body.type === "flex" && isObject(body.contents) ? { type: "flex", altText: String(body.altText), contents: body.contents } : {}
|
|
2087
2092
|
},
|
|
2088
2093
|
connectionId,
|
|
2089
2094
|
sentAt: /* @__PURE__ */ new Date()
|
|
@@ -2121,8 +2126,8 @@ async function handleMessagingRequest(request, channel, subPath) {
|
|
|
2121
2126
|
const userId = String(body.userId ?? "");
|
|
2122
2127
|
const message = body.message;
|
|
2123
2128
|
if (!userId) return failure("userId is required");
|
|
2124
|
-
if (!message || message.type !== "text" && message.type !== "image") {
|
|
2125
|
-
return failure("message must be { type: 'text' | 'image', ... }");
|
|
2129
|
+
if (!message || message.type !== "text" && message.type !== "image" && message.type !== "flex") {
|
|
2130
|
+
return failure("message must be { type: 'text' | 'image' | 'flex', ... }");
|
|
2126
2131
|
}
|
|
2127
2132
|
const connectionId = body.connectionId ? String(body.connectionId) : void 0;
|
|
2128
2133
|
if (message.type === "text") {
|
|
@@ -2133,7 +2138,7 @@ async function handleMessagingRequest(request, channel, subPath) {
|
|
|
2133
2138
|
return failure("message text exceeds maximum length");
|
|
2134
2139
|
}
|
|
2135
2140
|
captureMessage("line", userId, { text: message.text }, connectionId);
|
|
2136
|
-
} else {
|
|
2141
|
+
} else if (message.type === "image") {
|
|
2137
2142
|
const originalContentUrl = message.originalContentUrl;
|
|
2138
2143
|
if (!originalContentUrl || !isValidLineImageUrl(originalContentUrl)) {
|
|
2139
2144
|
return failure("originalContentUrl must be a valid HTTPS URL (max 2000 chars)");
|
|
@@ -2144,7 +2149,23 @@ async function handleMessagingRequest(request, channel, subPath) {
|
|
|
2144
2149
|
captureMessage(
|
|
2145
2150
|
"line",
|
|
2146
2151
|
userId,
|
|
2147
|
-
{
|
|
2152
|
+
{
|
|
2153
|
+
originalContentUrl,
|
|
2154
|
+
previewImageUrl: message.previewImageUrl ?? originalContentUrl
|
|
2155
|
+
},
|
|
2156
|
+
connectionId
|
|
2157
|
+
);
|
|
2158
|
+
} else {
|
|
2159
|
+
if (typeof message.altText !== "string" || !message.altText || message.altText.length > 400) {
|
|
2160
|
+
return failure("altText must be between 1 and 400 characters");
|
|
2161
|
+
}
|
|
2162
|
+
if (!isObject(message.contents) || message.contents.type !== "bubble" && message.contents.type !== "carousel") {
|
|
2163
|
+
return failure("contents.type must be 'bubble' or 'carousel'");
|
|
2164
|
+
}
|
|
2165
|
+
captureMessage(
|
|
2166
|
+
"line",
|
|
2167
|
+
userId,
|
|
2168
|
+
{ type: "flex", altText: message.altText, contents: message.contents },
|
|
2148
2169
|
connectionId
|
|
2149
2170
|
);
|
|
2150
2171
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -611,6 +611,7 @@ async function handleEmailSend(request) {
|
|
|
611
611
|
html: body.html ? String(body.html) : void 0,
|
|
612
612
|
text: body.text ? String(body.text) : void 0,
|
|
613
613
|
replyTo: body.replyTo ? String(body.replyTo) : void 0,
|
|
614
|
+
headers: { ...body.headers ?? {} },
|
|
614
615
|
attachments: (body.attachments ?? []).map((a) => ({
|
|
615
616
|
filename: String(a.filename ?? ""),
|
|
616
617
|
contentType: a.contentType ? String(a.contentType) : void 0
|
|
@@ -2023,6 +2024,9 @@ function isValidLineImageUrl(url) {
|
|
|
2023
2024
|
return false;
|
|
2024
2025
|
}
|
|
2025
2026
|
}
|
|
2027
|
+
function isObject(value) {
|
|
2028
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2029
|
+
}
|
|
2026
2030
|
function captureMessage(channel, recipient, body, connectionId) {
|
|
2027
2031
|
const message = {
|
|
2028
2032
|
channel,
|
|
@@ -2034,7 +2038,8 @@ function captureMessage(channel, recipient, body, connectionId) {
|
|
|
2034
2038
|
messagingType: body.messagingType ? String(body.messagingType) : void 0,
|
|
2035
2039
|
tag: body.tag ? String(body.tag) : void 0,
|
|
2036
2040
|
originalContentUrl: body.originalContentUrl ? String(body.originalContentUrl) : void 0,
|
|
2037
|
-
previewImageUrl: body.previewImageUrl ? String(body.previewImageUrl) : void 0
|
|
2041
|
+
previewImageUrl: body.previewImageUrl ? String(body.previewImageUrl) : void 0,
|
|
2042
|
+
...body.type === "flex" && isObject(body.contents) ? { type: "flex", altText: String(body.altText), contents: body.contents } : {}
|
|
2038
2043
|
},
|
|
2039
2044
|
connectionId,
|
|
2040
2045
|
sentAt: /* @__PURE__ */ new Date()
|
|
@@ -2072,8 +2077,8 @@ async function handleMessagingRequest(request, channel, subPath) {
|
|
|
2072
2077
|
const userId = String(body.userId ?? "");
|
|
2073
2078
|
const message = body.message;
|
|
2074
2079
|
if (!userId) return failure("userId is required");
|
|
2075
|
-
if (!message || message.type !== "text" && message.type !== "image") {
|
|
2076
|
-
return failure("message must be { type: 'text' | 'image', ... }");
|
|
2080
|
+
if (!message || message.type !== "text" && message.type !== "image" && message.type !== "flex") {
|
|
2081
|
+
return failure("message must be { type: 'text' | 'image' | 'flex', ... }");
|
|
2077
2082
|
}
|
|
2078
2083
|
const connectionId = body.connectionId ? String(body.connectionId) : void 0;
|
|
2079
2084
|
if (message.type === "text") {
|
|
@@ -2084,7 +2089,7 @@ async function handleMessagingRequest(request, channel, subPath) {
|
|
|
2084
2089
|
return failure("message text exceeds maximum length");
|
|
2085
2090
|
}
|
|
2086
2091
|
captureMessage("line", userId, { text: message.text }, connectionId);
|
|
2087
|
-
} else {
|
|
2092
|
+
} else if (message.type === "image") {
|
|
2088
2093
|
const originalContentUrl = message.originalContentUrl;
|
|
2089
2094
|
if (!originalContentUrl || !isValidLineImageUrl(originalContentUrl)) {
|
|
2090
2095
|
return failure("originalContentUrl must be a valid HTTPS URL (max 2000 chars)");
|
|
@@ -2095,7 +2100,23 @@ async function handleMessagingRequest(request, channel, subPath) {
|
|
|
2095
2100
|
captureMessage(
|
|
2096
2101
|
"line",
|
|
2097
2102
|
userId,
|
|
2098
|
-
{
|
|
2103
|
+
{
|
|
2104
|
+
originalContentUrl,
|
|
2105
|
+
previewImageUrl: message.previewImageUrl ?? originalContentUrl
|
|
2106
|
+
},
|
|
2107
|
+
connectionId
|
|
2108
|
+
);
|
|
2109
|
+
} else {
|
|
2110
|
+
if (typeof message.altText !== "string" || !message.altText || message.altText.length > 400) {
|
|
2111
|
+
return failure("altText must be between 1 and 400 characters");
|
|
2112
|
+
}
|
|
2113
|
+
if (!isObject(message.contents) || message.contents.type !== "bubble" && message.contents.type !== "carousel") {
|
|
2114
|
+
return failure("contents.type must be 'bubble' or 'carousel'");
|
|
2115
|
+
}
|
|
2116
|
+
captureMessage(
|
|
2117
|
+
"line",
|
|
2118
|
+
userId,
|
|
2119
|
+
{ type: "flex", altText: message.altText, contents: message.contents },
|
|
2099
2120
|
connectionId
|
|
2100
2121
|
);
|
|
2101
2122
|
}
|
package/dist/setup.js
CHANGED
|
@@ -607,6 +607,7 @@ async function handleEmailSend(request) {
|
|
|
607
607
|
html: body.html ? String(body.html) : void 0,
|
|
608
608
|
text: body.text ? String(body.text) : void 0,
|
|
609
609
|
replyTo: body.replyTo ? String(body.replyTo) : void 0,
|
|
610
|
+
headers: { ...body.headers ?? {} },
|
|
610
611
|
attachments: (body.attachments ?? []).map((a) => ({
|
|
611
612
|
filename: String(a.filename ?? ""),
|
|
612
613
|
contentType: a.contentType ? String(a.contentType) : void 0
|
|
@@ -1622,6 +1623,9 @@ function isValidLineImageUrl(url) {
|
|
|
1622
1623
|
return false;
|
|
1623
1624
|
}
|
|
1624
1625
|
}
|
|
1626
|
+
function isObject(value) {
|
|
1627
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1628
|
+
}
|
|
1625
1629
|
function captureMessage(channel, recipient, body, connectionId) {
|
|
1626
1630
|
const message = {
|
|
1627
1631
|
channel,
|
|
@@ -1633,7 +1637,8 @@ function captureMessage(channel, recipient, body, connectionId) {
|
|
|
1633
1637
|
messagingType: body.messagingType ? String(body.messagingType) : void 0,
|
|
1634
1638
|
tag: body.tag ? String(body.tag) : void 0,
|
|
1635
1639
|
originalContentUrl: body.originalContentUrl ? String(body.originalContentUrl) : void 0,
|
|
1636
|
-
previewImageUrl: body.previewImageUrl ? String(body.previewImageUrl) : void 0
|
|
1640
|
+
previewImageUrl: body.previewImageUrl ? String(body.previewImageUrl) : void 0,
|
|
1641
|
+
...body.type === "flex" && isObject(body.contents) ? { type: "flex", altText: String(body.altText), contents: body.contents } : {}
|
|
1637
1642
|
},
|
|
1638
1643
|
connectionId,
|
|
1639
1644
|
sentAt: /* @__PURE__ */ new Date()
|
|
@@ -1671,8 +1676,8 @@ async function handleMessagingRequest(request, channel, subPath) {
|
|
|
1671
1676
|
const userId = String(body.userId ?? "");
|
|
1672
1677
|
const message = body.message;
|
|
1673
1678
|
if (!userId) return failure("userId is required");
|
|
1674
|
-
if (!message || message.type !== "text" && message.type !== "image") {
|
|
1675
|
-
return failure("message must be { type: 'text' | 'image', ... }");
|
|
1679
|
+
if (!message || message.type !== "text" && message.type !== "image" && message.type !== "flex") {
|
|
1680
|
+
return failure("message must be { type: 'text' | 'image' | 'flex', ... }");
|
|
1676
1681
|
}
|
|
1677
1682
|
const connectionId = body.connectionId ? String(body.connectionId) : void 0;
|
|
1678
1683
|
if (message.type === "text") {
|
|
@@ -1683,7 +1688,7 @@ async function handleMessagingRequest(request, channel, subPath) {
|
|
|
1683
1688
|
return failure("message text exceeds maximum length");
|
|
1684
1689
|
}
|
|
1685
1690
|
captureMessage("line", userId, { text: message.text }, connectionId);
|
|
1686
|
-
} else {
|
|
1691
|
+
} else if (message.type === "image") {
|
|
1687
1692
|
const originalContentUrl = message.originalContentUrl;
|
|
1688
1693
|
if (!originalContentUrl || !isValidLineImageUrl(originalContentUrl)) {
|
|
1689
1694
|
return failure("originalContentUrl must be a valid HTTPS URL (max 2000 chars)");
|
|
@@ -1694,7 +1699,23 @@ async function handleMessagingRequest(request, channel, subPath) {
|
|
|
1694
1699
|
captureMessage(
|
|
1695
1700
|
"line",
|
|
1696
1701
|
userId,
|
|
1697
|
-
{
|
|
1702
|
+
{
|
|
1703
|
+
originalContentUrl,
|
|
1704
|
+
previewImageUrl: message.previewImageUrl ?? originalContentUrl
|
|
1705
|
+
},
|
|
1706
|
+
connectionId
|
|
1707
|
+
);
|
|
1708
|
+
} else {
|
|
1709
|
+
if (typeof message.altText !== "string" || !message.altText || message.altText.length > 400) {
|
|
1710
|
+
return failure("altText must be between 1 and 400 characters");
|
|
1711
|
+
}
|
|
1712
|
+
if (!isObject(message.contents) || message.contents.type !== "bubble" && message.contents.type !== "carousel") {
|
|
1713
|
+
return failure("contents.type must be 'bubble' or 'carousel'");
|
|
1714
|
+
}
|
|
1715
|
+
captureMessage(
|
|
1716
|
+
"line",
|
|
1717
|
+
userId,
|
|
1718
|
+
{ type: "flex", altText: message.altText, contents: message.contents },
|
|
1698
1719
|
connectionId
|
|
1699
1720
|
);
|
|
1700
1721
|
}
|
package/dist/setup.mjs
CHANGED
|
@@ -583,6 +583,7 @@ async function handleEmailSend(request) {
|
|
|
583
583
|
html: body.html ? String(body.html) : void 0,
|
|
584
584
|
text: body.text ? String(body.text) : void 0,
|
|
585
585
|
replyTo: body.replyTo ? String(body.replyTo) : void 0,
|
|
586
|
+
headers: { ...body.headers ?? {} },
|
|
586
587
|
attachments: (body.attachments ?? []).map((a) => ({
|
|
587
588
|
filename: String(a.filename ?? ""),
|
|
588
589
|
contentType: a.contentType ? String(a.contentType) : void 0
|
|
@@ -1598,6 +1599,9 @@ function isValidLineImageUrl(url) {
|
|
|
1598
1599
|
return false;
|
|
1599
1600
|
}
|
|
1600
1601
|
}
|
|
1602
|
+
function isObject(value) {
|
|
1603
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1604
|
+
}
|
|
1601
1605
|
function captureMessage(channel, recipient, body, connectionId) {
|
|
1602
1606
|
const message = {
|
|
1603
1607
|
channel,
|
|
@@ -1609,7 +1613,8 @@ function captureMessage(channel, recipient, body, connectionId) {
|
|
|
1609
1613
|
messagingType: body.messagingType ? String(body.messagingType) : void 0,
|
|
1610
1614
|
tag: body.tag ? String(body.tag) : void 0,
|
|
1611
1615
|
originalContentUrl: body.originalContentUrl ? String(body.originalContentUrl) : void 0,
|
|
1612
|
-
previewImageUrl: body.previewImageUrl ? String(body.previewImageUrl) : void 0
|
|
1616
|
+
previewImageUrl: body.previewImageUrl ? String(body.previewImageUrl) : void 0,
|
|
1617
|
+
...body.type === "flex" && isObject(body.contents) ? { type: "flex", altText: String(body.altText), contents: body.contents } : {}
|
|
1613
1618
|
},
|
|
1614
1619
|
connectionId,
|
|
1615
1620
|
sentAt: /* @__PURE__ */ new Date()
|
|
@@ -1647,8 +1652,8 @@ async function handleMessagingRequest(request, channel, subPath) {
|
|
|
1647
1652
|
const userId = String(body.userId ?? "");
|
|
1648
1653
|
const message = body.message;
|
|
1649
1654
|
if (!userId) return failure("userId is required");
|
|
1650
|
-
if (!message || message.type !== "text" && message.type !== "image") {
|
|
1651
|
-
return failure("message must be { type: 'text' | 'image', ... }");
|
|
1655
|
+
if (!message || message.type !== "text" && message.type !== "image" && message.type !== "flex") {
|
|
1656
|
+
return failure("message must be { type: 'text' | 'image' | 'flex', ... }");
|
|
1652
1657
|
}
|
|
1653
1658
|
const connectionId = body.connectionId ? String(body.connectionId) : void 0;
|
|
1654
1659
|
if (message.type === "text") {
|
|
@@ -1659,7 +1664,7 @@ async function handleMessagingRequest(request, channel, subPath) {
|
|
|
1659
1664
|
return failure("message text exceeds maximum length");
|
|
1660
1665
|
}
|
|
1661
1666
|
captureMessage("line", userId, { text: message.text }, connectionId);
|
|
1662
|
-
} else {
|
|
1667
|
+
} else if (message.type === "image") {
|
|
1663
1668
|
const originalContentUrl = message.originalContentUrl;
|
|
1664
1669
|
if (!originalContentUrl || !isValidLineImageUrl(originalContentUrl)) {
|
|
1665
1670
|
return failure("originalContentUrl must be a valid HTTPS URL (max 2000 chars)");
|
|
@@ -1670,7 +1675,23 @@ async function handleMessagingRequest(request, channel, subPath) {
|
|
|
1670
1675
|
captureMessage(
|
|
1671
1676
|
"line",
|
|
1672
1677
|
userId,
|
|
1673
|
-
{
|
|
1678
|
+
{
|
|
1679
|
+
originalContentUrl,
|
|
1680
|
+
previewImageUrl: message.previewImageUrl ?? originalContentUrl
|
|
1681
|
+
},
|
|
1682
|
+
connectionId
|
|
1683
|
+
);
|
|
1684
|
+
} else {
|
|
1685
|
+
if (typeof message.altText !== "string" || !message.altText || message.altText.length > 400) {
|
|
1686
|
+
return failure("altText must be between 1 and 400 characters");
|
|
1687
|
+
}
|
|
1688
|
+
if (!isObject(message.contents) || message.contents.type !== "bubble" && message.contents.type !== "carousel") {
|
|
1689
|
+
return failure("contents.type must be 'bubble' or 'carousel'");
|
|
1690
|
+
}
|
|
1691
|
+
captureMessage(
|
|
1692
|
+
"line",
|
|
1693
|
+
userId,
|
|
1694
|
+
{ type: "flex", altText: message.altText, contents: message.contents },
|
|
1674
1695
|
connectionId
|
|
1675
1696
|
);
|
|
1676
1697
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stardeck-customer-apps/testing",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "Vitest test harness for Stardeck customer apps — in-process Postgres (PGlite) plus a control-plane simulator so the real Stardeck SDKs run unmodified in tests",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
},
|
|
67
67
|
"peerDependencies": {
|
|
68
68
|
"@stardeck-customer-apps/edge-sdk": ">=1.4.0",
|
|
69
|
-
"@stardeck-customer-apps/integrations-sdk": ">=1.
|
|
69
|
+
"@stardeck-customer-apps/integrations-sdk": ">=1.13.0",
|
|
70
70
|
"@stardeck-customer-apps/payments-sdk": ">=0.1.0",
|
|
71
71
|
"@stardeck-customer-apps/storage-sdk": ">=0.1.0",
|
|
72
72
|
"next": "^14.0.0 || ^15.0.0 || ^16.0.0",
|