@spikard/node 0.11.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/dist/index.d.mts +16 -1
- package/dist/index.d.ts +16 -1
- package/dist/index.js +131 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +131 -0
- package/dist/index.mjs.map +1 -1
- package/index.js +52 -52
- package/package.json +4 -4
- package/spikard-node.linux-x64-gnu.node +0 -0
package/dist/index.mjs
CHANGED
|
@@ -903,6 +903,36 @@ import fs from "fs/promises";
|
|
|
903
903
|
import { createRequire as createRequire3 } from "module";
|
|
904
904
|
import path from "path";
|
|
905
905
|
import { gunzipSync, gzipSync } from "zlib";
|
|
906
|
+
var GRAPHQL_WS_TIMEOUT_MS = 2e3;
|
|
907
|
+
var GRAPHQL_WS_MAX_CONTROL_MESSAGES = 32;
|
|
908
|
+
var withTimeout = async (promise, timeoutMs, context) => {
|
|
909
|
+
let timer;
|
|
910
|
+
try {
|
|
911
|
+
return await Promise.race([
|
|
912
|
+
promise,
|
|
913
|
+
new Promise((_resolve, reject) => {
|
|
914
|
+
timer = setTimeout(() => reject(new Error(`Timed out waiting for ${context}`)), timeoutMs);
|
|
915
|
+
})
|
|
916
|
+
]);
|
|
917
|
+
} finally {
|
|
918
|
+
if (timer) {
|
|
919
|
+
clearTimeout(timer);
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
};
|
|
923
|
+
var decodeGraphqlWsMessage = (value) => {
|
|
924
|
+
if (typeof value === "string") {
|
|
925
|
+
const parsed = JSON.parse(value);
|
|
926
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
927
|
+
return parsed;
|
|
928
|
+
}
|
|
929
|
+
throw new Error("Expected GraphQL WebSocket JSON object message");
|
|
930
|
+
}
|
|
931
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
932
|
+
return value;
|
|
933
|
+
}
|
|
934
|
+
throw new Error("Expected GraphQL WebSocket message object");
|
|
935
|
+
};
|
|
906
936
|
var MockWebSocketConnection = class {
|
|
907
937
|
handler;
|
|
908
938
|
queue = [];
|
|
@@ -1616,6 +1646,107 @@ var TestClient = class {
|
|
|
1616
1646
|
bodyText: response.text()
|
|
1617
1647
|
};
|
|
1618
1648
|
}
|
|
1649
|
+
/**
|
|
1650
|
+
* Send a GraphQL subscription over WebSocket and return the first event payload.
|
|
1651
|
+
*/
|
|
1652
|
+
async graphqlSubscription(query, variables, operationName, path2 = "/graphql") {
|
|
1653
|
+
const operationId = "spikard-subscription-1";
|
|
1654
|
+
const subscriptionPayload = { query };
|
|
1655
|
+
if (variables !== null && variables !== void 0) {
|
|
1656
|
+
subscriptionPayload.variables = variables;
|
|
1657
|
+
}
|
|
1658
|
+
if (operationName !== null && operationName !== void 0) {
|
|
1659
|
+
subscriptionPayload.operationName = operationName;
|
|
1660
|
+
}
|
|
1661
|
+
const ws = await this.websocketConnect(path2);
|
|
1662
|
+
try {
|
|
1663
|
+
await ws.sendJson({ type: "connection_init" });
|
|
1664
|
+
let acknowledged = false;
|
|
1665
|
+
for (let i = 0; i < GRAPHQL_WS_MAX_CONTROL_MESSAGES; i++) {
|
|
1666
|
+
const message = decodeGraphqlWsMessage(
|
|
1667
|
+
await withTimeout(ws.receiveJson(), GRAPHQL_WS_TIMEOUT_MS, "GraphQL connection_ack")
|
|
1668
|
+
);
|
|
1669
|
+
const messageType = typeof message.type === "string" ? message.type : "";
|
|
1670
|
+
if (messageType === "connection_ack") {
|
|
1671
|
+
acknowledged = true;
|
|
1672
|
+
break;
|
|
1673
|
+
}
|
|
1674
|
+
if (messageType === "ping") {
|
|
1675
|
+
const pong = { type: "pong" };
|
|
1676
|
+
if ("payload" in message) {
|
|
1677
|
+
pong.payload = message.payload;
|
|
1678
|
+
}
|
|
1679
|
+
await ws.sendJson(pong);
|
|
1680
|
+
continue;
|
|
1681
|
+
}
|
|
1682
|
+
if (messageType === "connection_error" || messageType === "error") {
|
|
1683
|
+
throw new Error(`GraphQL subscription rejected during init: ${JSON.stringify(message)}`);
|
|
1684
|
+
}
|
|
1685
|
+
}
|
|
1686
|
+
if (!acknowledged) {
|
|
1687
|
+
throw new Error("No GraphQL connection_ack received");
|
|
1688
|
+
}
|
|
1689
|
+
await ws.sendJson({
|
|
1690
|
+
id: operationId,
|
|
1691
|
+
type: "subscribe",
|
|
1692
|
+
payload: subscriptionPayload
|
|
1693
|
+
});
|
|
1694
|
+
let event = null;
|
|
1695
|
+
const errors = [];
|
|
1696
|
+
let completeReceived = false;
|
|
1697
|
+
for (let i = 0; i < GRAPHQL_WS_MAX_CONTROL_MESSAGES; i++) {
|
|
1698
|
+
const message = decodeGraphqlWsMessage(
|
|
1699
|
+
await withTimeout(ws.receiveJson(), GRAPHQL_WS_TIMEOUT_MS, "GraphQL subscription message")
|
|
1700
|
+
);
|
|
1701
|
+
const messageType = typeof message.type === "string" ? message.type : "";
|
|
1702
|
+
const messageId = typeof message.id === "string" ? message.id : void 0;
|
|
1703
|
+
const idMatches = messageId === void 0 || messageId === operationId;
|
|
1704
|
+
if (messageType === "next" && idMatches) {
|
|
1705
|
+
event = "payload" in message ? message.payload : null;
|
|
1706
|
+
await ws.sendJson({ id: operationId, type: "complete" });
|
|
1707
|
+
try {
|
|
1708
|
+
const maybeComplete = decodeGraphqlWsMessage(
|
|
1709
|
+
await withTimeout(ws.receiveJson(), GRAPHQL_WS_TIMEOUT_MS, "GraphQL complete message")
|
|
1710
|
+
);
|
|
1711
|
+
const completeType = typeof maybeComplete.type === "string" ? maybeComplete.type : "";
|
|
1712
|
+
const completeId = typeof maybeComplete.id === "string" ? maybeComplete.id : void 0;
|
|
1713
|
+
if (completeType === "complete" && (completeId === void 0 || completeId === operationId)) {
|
|
1714
|
+
completeReceived = true;
|
|
1715
|
+
}
|
|
1716
|
+
} catch {
|
|
1717
|
+
}
|
|
1718
|
+
break;
|
|
1719
|
+
}
|
|
1720
|
+
if (messageType === "error") {
|
|
1721
|
+
errors.push("payload" in message ? message.payload : message);
|
|
1722
|
+
break;
|
|
1723
|
+
}
|
|
1724
|
+
if (messageType === "complete" && idMatches) {
|
|
1725
|
+
completeReceived = true;
|
|
1726
|
+
break;
|
|
1727
|
+
}
|
|
1728
|
+
if (messageType === "ping") {
|
|
1729
|
+
const pong = { type: "pong" };
|
|
1730
|
+
if ("payload" in message) {
|
|
1731
|
+
pong.payload = message.payload;
|
|
1732
|
+
}
|
|
1733
|
+
await ws.sendJson(pong);
|
|
1734
|
+
}
|
|
1735
|
+
}
|
|
1736
|
+
if (event === null && errors.length === 0 && !completeReceived) {
|
|
1737
|
+
throw new Error("No GraphQL subscription event received before timeout");
|
|
1738
|
+
}
|
|
1739
|
+
return {
|
|
1740
|
+
operationId,
|
|
1741
|
+
acknowledged,
|
|
1742
|
+
event,
|
|
1743
|
+
errors,
|
|
1744
|
+
completeReceived
|
|
1745
|
+
};
|
|
1746
|
+
} finally {
|
|
1747
|
+
await ws.close();
|
|
1748
|
+
}
|
|
1749
|
+
}
|
|
1619
1750
|
/**
|
|
1620
1751
|
* Cleanup resources when test client is done
|
|
1621
1752
|
*/
|