@stardeck-customer-apps/testing 0.10.0 → 0.11.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 +24 -1
- package/dist/index.d.mts +5 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +25 -5
- package/dist/index.mjs +25 -5
- package/dist/setup.js +25 -5
- package/dist/setup.mjs +25 -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({
|
|
@@ -288,7 +308,10 @@ externalId })` helper models a trusted platform/channel link, and
|
|
|
288
308
|
list `limit` 1–100, status filter values).
|
|
289
309
|
- `app.storage` — uploads through storage-sdk: `.uploads`, `.latest()`, `.count`, `.clear()`.
|
|
290
310
|
- `app.messages` — outbound Slack/LINE/Facebook sends:
|
|
291
|
-
`.all()`, `.latest()`, `.to(recipient)`, `.channel("line")`, `.count`, `.clear()`.
|
|
311
|
+
`.all()`, `.latest()`, `.to(recipient)`, `.channel("line")`, `.count`, `.clear()`. LINE Flex
|
|
312
|
+
captures expose `body.type`, `body.altText`, and the complete `body.contents` bubble/carousel.
|
|
313
|
+
The simulator validates the Flex envelope like production; LINE itself remains responsible for
|
|
314
|
+
validating nested component layout.
|
|
292
315
|
- `app.edge` — edge print/display/bindings from edge-sdk: `.prints`, `.displays`,
|
|
293
316
|
`.testPrints`, `.latestPrint()`, `.latestDisplay()`, `.bindings`,
|
|
294
317
|
`.seedDevices()`, `.seedPeripherals()`, `.seedBindings()`,
|
package/dist/index.d.mts
CHANGED
|
@@ -252,6 +252,7 @@ interface CapturedMessage {
|
|
|
252
252
|
channel: "slack" | "line" | "facebook";
|
|
253
253
|
recipient: string;
|
|
254
254
|
body: {
|
|
255
|
+
type?: string;
|
|
255
256
|
text?: string;
|
|
256
257
|
blocks?: unknown[];
|
|
257
258
|
threadTs?: string;
|
|
@@ -261,6 +262,10 @@ interface CapturedMessage {
|
|
|
261
262
|
originalContentUrl?: string;
|
|
262
263
|
/** LINE image push — HTTPS URL of the preview; may be omitted when not sent. */
|
|
263
264
|
previewImageUrl?: string;
|
|
265
|
+
/** LINE Flex fallback text shown when the rich card cannot be rendered. */
|
|
266
|
+
altText?: string;
|
|
267
|
+
/** Complete LINE Flex bubble or carousel payload. */
|
|
268
|
+
contents?: Record<string, unknown>;
|
|
264
269
|
};
|
|
265
270
|
connectionId?: string;
|
|
266
271
|
sentAt: Date;
|
package/dist/index.d.ts
CHANGED
|
@@ -252,6 +252,7 @@ interface CapturedMessage {
|
|
|
252
252
|
channel: "slack" | "line" | "facebook";
|
|
253
253
|
recipient: string;
|
|
254
254
|
body: {
|
|
255
|
+
type?: string;
|
|
255
256
|
text?: string;
|
|
256
257
|
blocks?: unknown[];
|
|
257
258
|
threadTs?: string;
|
|
@@ -261,6 +262,10 @@ interface CapturedMessage {
|
|
|
261
262
|
originalContentUrl?: string;
|
|
262
263
|
/** LINE image push — HTTPS URL of the preview; may be omitted when not sent. */
|
|
263
264
|
previewImageUrl?: string;
|
|
265
|
+
/** LINE Flex fallback text shown when the rich card cannot be rendered. */
|
|
266
|
+
altText?: string;
|
|
267
|
+
/** Complete LINE Flex bubble or carousel payload. */
|
|
268
|
+
contents?: Record<string, unknown>;
|
|
264
269
|
};
|
|
265
270
|
connectionId?: string;
|
|
266
271
|
sentAt: Date;
|
package/dist/index.js
CHANGED
|
@@ -2072,6 +2072,9 @@ function isValidLineImageUrl(url) {
|
|
|
2072
2072
|
return false;
|
|
2073
2073
|
}
|
|
2074
2074
|
}
|
|
2075
|
+
function isObject(value) {
|
|
2076
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2077
|
+
}
|
|
2075
2078
|
function captureMessage(channel, recipient, body, connectionId) {
|
|
2076
2079
|
const message = {
|
|
2077
2080
|
channel,
|
|
@@ -2083,7 +2086,8 @@ function captureMessage(channel, recipient, body, connectionId) {
|
|
|
2083
2086
|
messagingType: body.messagingType ? String(body.messagingType) : void 0,
|
|
2084
2087
|
tag: body.tag ? String(body.tag) : void 0,
|
|
2085
2088
|
originalContentUrl: body.originalContentUrl ? String(body.originalContentUrl) : void 0,
|
|
2086
|
-
previewImageUrl: body.previewImageUrl ? String(body.previewImageUrl) : void 0
|
|
2089
|
+
previewImageUrl: body.previewImageUrl ? String(body.previewImageUrl) : void 0,
|
|
2090
|
+
...body.type === "flex" && isObject(body.contents) ? { type: "flex", altText: String(body.altText), contents: body.contents } : {}
|
|
2087
2091
|
},
|
|
2088
2092
|
connectionId,
|
|
2089
2093
|
sentAt: /* @__PURE__ */ new Date()
|
|
@@ -2121,8 +2125,8 @@ async function handleMessagingRequest(request, channel, subPath) {
|
|
|
2121
2125
|
const userId = String(body.userId ?? "");
|
|
2122
2126
|
const message = body.message;
|
|
2123
2127
|
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', ... }");
|
|
2128
|
+
if (!message || message.type !== "text" && message.type !== "image" && message.type !== "flex") {
|
|
2129
|
+
return failure("message must be { type: 'text' | 'image' | 'flex', ... }");
|
|
2126
2130
|
}
|
|
2127
2131
|
const connectionId = body.connectionId ? String(body.connectionId) : void 0;
|
|
2128
2132
|
if (message.type === "text") {
|
|
@@ -2133,7 +2137,7 @@ async function handleMessagingRequest(request, channel, subPath) {
|
|
|
2133
2137
|
return failure("message text exceeds maximum length");
|
|
2134
2138
|
}
|
|
2135
2139
|
captureMessage("line", userId, { text: message.text }, connectionId);
|
|
2136
|
-
} else {
|
|
2140
|
+
} else if (message.type === "image") {
|
|
2137
2141
|
const originalContentUrl = message.originalContentUrl;
|
|
2138
2142
|
if (!originalContentUrl || !isValidLineImageUrl(originalContentUrl)) {
|
|
2139
2143
|
return failure("originalContentUrl must be a valid HTTPS URL (max 2000 chars)");
|
|
@@ -2144,7 +2148,23 @@ async function handleMessagingRequest(request, channel, subPath) {
|
|
|
2144
2148
|
captureMessage(
|
|
2145
2149
|
"line",
|
|
2146
2150
|
userId,
|
|
2147
|
-
{
|
|
2151
|
+
{
|
|
2152
|
+
originalContentUrl,
|
|
2153
|
+
previewImageUrl: message.previewImageUrl ?? originalContentUrl
|
|
2154
|
+
},
|
|
2155
|
+
connectionId
|
|
2156
|
+
);
|
|
2157
|
+
} else {
|
|
2158
|
+
if (typeof message.altText !== "string" || !message.altText || message.altText.length > 400) {
|
|
2159
|
+
return failure("altText must be between 1 and 400 characters");
|
|
2160
|
+
}
|
|
2161
|
+
if (!isObject(message.contents) || message.contents.type !== "bubble" && message.contents.type !== "carousel") {
|
|
2162
|
+
return failure("contents.type must be 'bubble' or 'carousel'");
|
|
2163
|
+
}
|
|
2164
|
+
captureMessage(
|
|
2165
|
+
"line",
|
|
2166
|
+
userId,
|
|
2167
|
+
{ type: "flex", altText: message.altText, contents: message.contents },
|
|
2148
2168
|
connectionId
|
|
2149
2169
|
);
|
|
2150
2170
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -2023,6 +2023,9 @@ function isValidLineImageUrl(url) {
|
|
|
2023
2023
|
return false;
|
|
2024
2024
|
}
|
|
2025
2025
|
}
|
|
2026
|
+
function isObject(value) {
|
|
2027
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2028
|
+
}
|
|
2026
2029
|
function captureMessage(channel, recipient, body, connectionId) {
|
|
2027
2030
|
const message = {
|
|
2028
2031
|
channel,
|
|
@@ -2034,7 +2037,8 @@ function captureMessage(channel, recipient, body, connectionId) {
|
|
|
2034
2037
|
messagingType: body.messagingType ? String(body.messagingType) : void 0,
|
|
2035
2038
|
tag: body.tag ? String(body.tag) : void 0,
|
|
2036
2039
|
originalContentUrl: body.originalContentUrl ? String(body.originalContentUrl) : void 0,
|
|
2037
|
-
previewImageUrl: body.previewImageUrl ? String(body.previewImageUrl) : void 0
|
|
2040
|
+
previewImageUrl: body.previewImageUrl ? String(body.previewImageUrl) : void 0,
|
|
2041
|
+
...body.type === "flex" && isObject(body.contents) ? { type: "flex", altText: String(body.altText), contents: body.contents } : {}
|
|
2038
2042
|
},
|
|
2039
2043
|
connectionId,
|
|
2040
2044
|
sentAt: /* @__PURE__ */ new Date()
|
|
@@ -2072,8 +2076,8 @@ async function handleMessagingRequest(request, channel, subPath) {
|
|
|
2072
2076
|
const userId = String(body.userId ?? "");
|
|
2073
2077
|
const message = body.message;
|
|
2074
2078
|
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', ... }");
|
|
2079
|
+
if (!message || message.type !== "text" && message.type !== "image" && message.type !== "flex") {
|
|
2080
|
+
return failure("message must be { type: 'text' | 'image' | 'flex', ... }");
|
|
2077
2081
|
}
|
|
2078
2082
|
const connectionId = body.connectionId ? String(body.connectionId) : void 0;
|
|
2079
2083
|
if (message.type === "text") {
|
|
@@ -2084,7 +2088,7 @@ async function handleMessagingRequest(request, channel, subPath) {
|
|
|
2084
2088
|
return failure("message text exceeds maximum length");
|
|
2085
2089
|
}
|
|
2086
2090
|
captureMessage("line", userId, { text: message.text }, connectionId);
|
|
2087
|
-
} else {
|
|
2091
|
+
} else if (message.type === "image") {
|
|
2088
2092
|
const originalContentUrl = message.originalContentUrl;
|
|
2089
2093
|
if (!originalContentUrl || !isValidLineImageUrl(originalContentUrl)) {
|
|
2090
2094
|
return failure("originalContentUrl must be a valid HTTPS URL (max 2000 chars)");
|
|
@@ -2095,7 +2099,23 @@ async function handleMessagingRequest(request, channel, subPath) {
|
|
|
2095
2099
|
captureMessage(
|
|
2096
2100
|
"line",
|
|
2097
2101
|
userId,
|
|
2098
|
-
{
|
|
2102
|
+
{
|
|
2103
|
+
originalContentUrl,
|
|
2104
|
+
previewImageUrl: message.previewImageUrl ?? originalContentUrl
|
|
2105
|
+
},
|
|
2106
|
+
connectionId
|
|
2107
|
+
);
|
|
2108
|
+
} else {
|
|
2109
|
+
if (typeof message.altText !== "string" || !message.altText || message.altText.length > 400) {
|
|
2110
|
+
return failure("altText must be between 1 and 400 characters");
|
|
2111
|
+
}
|
|
2112
|
+
if (!isObject(message.contents) || message.contents.type !== "bubble" && message.contents.type !== "carousel") {
|
|
2113
|
+
return failure("contents.type must be 'bubble' or 'carousel'");
|
|
2114
|
+
}
|
|
2115
|
+
captureMessage(
|
|
2116
|
+
"line",
|
|
2117
|
+
userId,
|
|
2118
|
+
{ type: "flex", altText: message.altText, contents: message.contents },
|
|
2099
2119
|
connectionId
|
|
2100
2120
|
);
|
|
2101
2121
|
}
|
package/dist/setup.js
CHANGED
|
@@ -1622,6 +1622,9 @@ function isValidLineImageUrl(url) {
|
|
|
1622
1622
|
return false;
|
|
1623
1623
|
}
|
|
1624
1624
|
}
|
|
1625
|
+
function isObject(value) {
|
|
1626
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1627
|
+
}
|
|
1625
1628
|
function captureMessage(channel, recipient, body, connectionId) {
|
|
1626
1629
|
const message = {
|
|
1627
1630
|
channel,
|
|
@@ -1633,7 +1636,8 @@ function captureMessage(channel, recipient, body, connectionId) {
|
|
|
1633
1636
|
messagingType: body.messagingType ? String(body.messagingType) : void 0,
|
|
1634
1637
|
tag: body.tag ? String(body.tag) : void 0,
|
|
1635
1638
|
originalContentUrl: body.originalContentUrl ? String(body.originalContentUrl) : void 0,
|
|
1636
|
-
previewImageUrl: body.previewImageUrl ? String(body.previewImageUrl) : void 0
|
|
1639
|
+
previewImageUrl: body.previewImageUrl ? String(body.previewImageUrl) : void 0,
|
|
1640
|
+
...body.type === "flex" && isObject(body.contents) ? { type: "flex", altText: String(body.altText), contents: body.contents } : {}
|
|
1637
1641
|
},
|
|
1638
1642
|
connectionId,
|
|
1639
1643
|
sentAt: /* @__PURE__ */ new Date()
|
|
@@ -1671,8 +1675,8 @@ async function handleMessagingRequest(request, channel, subPath) {
|
|
|
1671
1675
|
const userId = String(body.userId ?? "");
|
|
1672
1676
|
const message = body.message;
|
|
1673
1677
|
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', ... }");
|
|
1678
|
+
if (!message || message.type !== "text" && message.type !== "image" && message.type !== "flex") {
|
|
1679
|
+
return failure("message must be { type: 'text' | 'image' | 'flex', ... }");
|
|
1676
1680
|
}
|
|
1677
1681
|
const connectionId = body.connectionId ? String(body.connectionId) : void 0;
|
|
1678
1682
|
if (message.type === "text") {
|
|
@@ -1683,7 +1687,7 @@ async function handleMessagingRequest(request, channel, subPath) {
|
|
|
1683
1687
|
return failure("message text exceeds maximum length");
|
|
1684
1688
|
}
|
|
1685
1689
|
captureMessage("line", userId, { text: message.text }, connectionId);
|
|
1686
|
-
} else {
|
|
1690
|
+
} else if (message.type === "image") {
|
|
1687
1691
|
const originalContentUrl = message.originalContentUrl;
|
|
1688
1692
|
if (!originalContentUrl || !isValidLineImageUrl(originalContentUrl)) {
|
|
1689
1693
|
return failure("originalContentUrl must be a valid HTTPS URL (max 2000 chars)");
|
|
@@ -1694,7 +1698,23 @@ async function handleMessagingRequest(request, channel, subPath) {
|
|
|
1694
1698
|
captureMessage(
|
|
1695
1699
|
"line",
|
|
1696
1700
|
userId,
|
|
1697
|
-
{
|
|
1701
|
+
{
|
|
1702
|
+
originalContentUrl,
|
|
1703
|
+
previewImageUrl: message.previewImageUrl ?? originalContentUrl
|
|
1704
|
+
},
|
|
1705
|
+
connectionId
|
|
1706
|
+
);
|
|
1707
|
+
} else {
|
|
1708
|
+
if (typeof message.altText !== "string" || !message.altText || message.altText.length > 400) {
|
|
1709
|
+
return failure("altText must be between 1 and 400 characters");
|
|
1710
|
+
}
|
|
1711
|
+
if (!isObject(message.contents) || message.contents.type !== "bubble" && message.contents.type !== "carousel") {
|
|
1712
|
+
return failure("contents.type must be 'bubble' or 'carousel'");
|
|
1713
|
+
}
|
|
1714
|
+
captureMessage(
|
|
1715
|
+
"line",
|
|
1716
|
+
userId,
|
|
1717
|
+
{ type: "flex", altText: message.altText, contents: message.contents },
|
|
1698
1718
|
connectionId
|
|
1699
1719
|
);
|
|
1700
1720
|
}
|
package/dist/setup.mjs
CHANGED
|
@@ -1598,6 +1598,9 @@ function isValidLineImageUrl(url) {
|
|
|
1598
1598
|
return false;
|
|
1599
1599
|
}
|
|
1600
1600
|
}
|
|
1601
|
+
function isObject(value) {
|
|
1602
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1603
|
+
}
|
|
1601
1604
|
function captureMessage(channel, recipient, body, connectionId) {
|
|
1602
1605
|
const message = {
|
|
1603
1606
|
channel,
|
|
@@ -1609,7 +1612,8 @@ function captureMessage(channel, recipient, body, connectionId) {
|
|
|
1609
1612
|
messagingType: body.messagingType ? String(body.messagingType) : void 0,
|
|
1610
1613
|
tag: body.tag ? String(body.tag) : void 0,
|
|
1611
1614
|
originalContentUrl: body.originalContentUrl ? String(body.originalContentUrl) : void 0,
|
|
1612
|
-
previewImageUrl: body.previewImageUrl ? String(body.previewImageUrl) : void 0
|
|
1615
|
+
previewImageUrl: body.previewImageUrl ? String(body.previewImageUrl) : void 0,
|
|
1616
|
+
...body.type === "flex" && isObject(body.contents) ? { type: "flex", altText: String(body.altText), contents: body.contents } : {}
|
|
1613
1617
|
},
|
|
1614
1618
|
connectionId,
|
|
1615
1619
|
sentAt: /* @__PURE__ */ new Date()
|
|
@@ -1647,8 +1651,8 @@ async function handleMessagingRequest(request, channel, subPath) {
|
|
|
1647
1651
|
const userId = String(body.userId ?? "");
|
|
1648
1652
|
const message = body.message;
|
|
1649
1653
|
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', ... }");
|
|
1654
|
+
if (!message || message.type !== "text" && message.type !== "image" && message.type !== "flex") {
|
|
1655
|
+
return failure("message must be { type: 'text' | 'image' | 'flex', ... }");
|
|
1652
1656
|
}
|
|
1653
1657
|
const connectionId = body.connectionId ? String(body.connectionId) : void 0;
|
|
1654
1658
|
if (message.type === "text") {
|
|
@@ -1659,7 +1663,7 @@ async function handleMessagingRequest(request, channel, subPath) {
|
|
|
1659
1663
|
return failure("message text exceeds maximum length");
|
|
1660
1664
|
}
|
|
1661
1665
|
captureMessage("line", userId, { text: message.text }, connectionId);
|
|
1662
|
-
} else {
|
|
1666
|
+
} else if (message.type === "image") {
|
|
1663
1667
|
const originalContentUrl = message.originalContentUrl;
|
|
1664
1668
|
if (!originalContentUrl || !isValidLineImageUrl(originalContentUrl)) {
|
|
1665
1669
|
return failure("originalContentUrl must be a valid HTTPS URL (max 2000 chars)");
|
|
@@ -1670,7 +1674,23 @@ async function handleMessagingRequest(request, channel, subPath) {
|
|
|
1670
1674
|
captureMessage(
|
|
1671
1675
|
"line",
|
|
1672
1676
|
userId,
|
|
1673
|
-
{
|
|
1677
|
+
{
|
|
1678
|
+
originalContentUrl,
|
|
1679
|
+
previewImageUrl: message.previewImageUrl ?? originalContentUrl
|
|
1680
|
+
},
|
|
1681
|
+
connectionId
|
|
1682
|
+
);
|
|
1683
|
+
} else {
|
|
1684
|
+
if (typeof message.altText !== "string" || !message.altText || message.altText.length > 400) {
|
|
1685
|
+
return failure("altText must be between 1 and 400 characters");
|
|
1686
|
+
}
|
|
1687
|
+
if (!isObject(message.contents) || message.contents.type !== "bubble" && message.contents.type !== "carousel") {
|
|
1688
|
+
return failure("contents.type must be 'bubble' or 'carousel'");
|
|
1689
|
+
}
|
|
1690
|
+
captureMessage(
|
|
1691
|
+
"line",
|
|
1692
|
+
userId,
|
|
1693
|
+
{ type: "flex", altText: message.altText, contents: message.contents },
|
|
1674
1694
|
connectionId
|
|
1675
1695
|
);
|
|
1676
1696
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stardeck-customer-apps/testing",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.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",
|