@inodra/sui 0.1.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.
Files changed (91) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +237 -0
  3. package/bin/inodra-sui.mjs +2 -0
  4. package/dist/chunk-3CBLVI6J.js +426 -0
  5. package/dist/chunk-3CBLVI6J.js.map +1 -0
  6. package/dist/chunk-4HKKW5QN.js +80 -0
  7. package/dist/chunk-4HKKW5QN.js.map +1 -0
  8. package/dist/chunk-74XGJJJI.js +33 -0
  9. package/dist/chunk-74XGJJJI.js.map +1 -0
  10. package/dist/chunk-D6FZYWG7.js +99 -0
  11. package/dist/chunk-D6FZYWG7.js.map +1 -0
  12. package/dist/chunk-LZZMTSLH.js +31 -0
  13. package/dist/chunk-LZZMTSLH.js.map +1 -0
  14. package/dist/chunk-M3M2L6BW.js +29 -0
  15. package/dist/chunk-M3M2L6BW.js.map +1 -0
  16. package/dist/chunk-NSXRC5XW.js +298 -0
  17. package/dist/chunk-NSXRC5XW.js.map +1 -0
  18. package/dist/chunk-OGMBYWTG.js +537 -0
  19. package/dist/chunk-OGMBYWTG.js.map +1 -0
  20. package/dist/chunk-U6D5LGNF.js +111 -0
  21. package/dist/chunk-U6D5LGNF.js.map +1 -0
  22. package/dist/chunk-ZSW3EWGF.js +539 -0
  23. package/dist/chunk-ZSW3EWGF.js.map +1 -0
  24. package/dist/cli.js +665 -0
  25. package/dist/cli.js.map +1 -0
  26. package/dist/events.cjs +3273 -0
  27. package/dist/events.cjs.map +1 -0
  28. package/dist/events.d.cts +47 -0
  29. package/dist/events.d.ts +47 -0
  30. package/dist/events.js +2578 -0
  31. package/dist/events.js.map +1 -0
  32. package/dist/graphql.cjs +129 -0
  33. package/dist/graphql.cjs.map +1 -0
  34. package/dist/graphql.d.cts +36 -0
  35. package/dist/graphql.d.ts +36 -0
  36. package/dist/graphql.js +10 -0
  37. package/dist/graphql.js.map +1 -0
  38. package/dist/grpc-web.cjs +129 -0
  39. package/dist/grpc-web.cjs.map +1 -0
  40. package/dist/grpc-web.d.cts +31 -0
  41. package/dist/grpc-web.d.ts +31 -0
  42. package/dist/grpc-web.js +10 -0
  43. package/dist/grpc-web.js.map +1 -0
  44. package/dist/grpc.cjs +131 -0
  45. package/dist/grpc.cjs.map +1 -0
  46. package/dist/grpc.d.cts +34 -0
  47. package/dist/grpc.d.ts +34 -0
  48. package/dist/grpc.js +10 -0
  49. package/dist/grpc.js.map +1 -0
  50. package/dist/index-CGBN_EI5.d.ts +4800 -0
  51. package/dist/index-CVO2Kj8a.d.cts +4800 -0
  52. package/dist/index.cjs +4723 -0
  53. package/dist/index.cjs.map +1 -0
  54. package/dist/index.d.cts +504 -0
  55. package/dist/index.d.ts +504 -0
  56. package/dist/index.js +59 -0
  57. package/dist/index.js.map +1 -0
  58. package/dist/manage.cjs +376 -0
  59. package/dist/manage.cjs.map +1 -0
  60. package/dist/manage.d.cts +2 -0
  61. package/dist/manage.d.ts +2 -0
  62. package/dist/manage.js +8 -0
  63. package/dist/manage.js.map +1 -0
  64. package/dist/network-CEg2MJY3.d.cts +60 -0
  65. package/dist/network-CEg2MJY3.d.ts +60 -0
  66. package/dist/node.cjs +4759 -0
  67. package/dist/node.cjs.map +1 -0
  68. package/dist/node.d.cts +28 -0
  69. package/dist/node.d.ts +28 -0
  70. package/dist/node.js +73 -0
  71. package/dist/node.js.map +1 -0
  72. package/dist/reflection-Bx44-Fs1.d.ts +4639 -0
  73. package/dist/reflection-Cf7WFH6H.d.cts +4639 -0
  74. package/dist/reflection-FEGYA7DU.js +9 -0
  75. package/dist/reflection-FEGYA7DU.js.map +1 -0
  76. package/dist/runtime-OWKF3LZ7.js +37 -0
  77. package/dist/runtime-OWKF3LZ7.js.map +1 -0
  78. package/dist/warp.cjs +633 -0
  79. package/dist/warp.cjs.map +1 -0
  80. package/dist/warp.d.cts +195 -0
  81. package/dist/warp.d.ts +195 -0
  82. package/dist/warp.js +12 -0
  83. package/dist/warp.js.map +1 -0
  84. package/dist/webhooks.cjs +104 -0
  85. package/dist/webhooks.cjs.map +1 -0
  86. package/dist/webhooks.d.cts +35 -0
  87. package/dist/webhooks.d.ts +35 -0
  88. package/dist/webhooks.js +7 -0
  89. package/dist/webhooks.js.map +1 -0
  90. package/llms.txt +189 -0
  91. package/package.json +119 -0
@@ -0,0 +1,111 @@
1
+ // src/core/auth.ts
2
+ function resolveAuth(auth) {
3
+ if (typeof auth === "string") {
4
+ return staticAuth(apiKeyCredentials(auth));
5
+ }
6
+ if ("apiKey" in auth) {
7
+ return staticAuth(apiKeyCredentials(auth.apiKey));
8
+ }
9
+ if ("sessionToken" in auth) {
10
+ return staticAuth({
11
+ headers: { authorization: `Bearer ${auth.sessionToken}` }
12
+ });
13
+ }
14
+ return {
15
+ credentials: async () => auth.getCredentials(),
16
+ staticCredentials: () => void 0
17
+ };
18
+ }
19
+ function authFromOptions(options, context) {
20
+ if (options.auth !== void 0) {
21
+ return resolveAuth(options.auth);
22
+ }
23
+ if (options.apiKey) {
24
+ return resolveAuth(options.apiKey);
25
+ }
26
+ throw new Error(`${context} requires auth (or the apiKey shorthand)`);
27
+ }
28
+ function staticAuthHeaders(options, context) {
29
+ const credentials = authFromOptions(options, context).staticCredentials();
30
+ if (!credentials) {
31
+ throw new Error(
32
+ `${context} does not support dynamic credential providers \u2014 pass an apiKey or sessionToken`
33
+ );
34
+ }
35
+ return credentials.headers;
36
+ }
37
+ function apiKeyCredentials(apiKey) {
38
+ if (!apiKey) {
39
+ throw new Error("Inodra auth: apiKey must be a non-empty string");
40
+ }
41
+ return {
42
+ headers: { "x-api-key": apiKey },
43
+ queryParams: { api_key: apiKey }
44
+ };
45
+ }
46
+ function staticAuth(credentials) {
47
+ return {
48
+ credentials: async () => credentials,
49
+ staticCredentials: () => credentials
50
+ };
51
+ }
52
+
53
+ // src/core/network.ts
54
+ var INODRA_ENDPOINTS = {
55
+ mainnet: {
56
+ rest: "https://mainnet-api.inodra.com/v1",
57
+ graphql: "https://mainnet-api.inodra.com/v1/graphql",
58
+ /** host:port for native gRPC transports */
59
+ grpc: "mainnet-grpc.inodra.com:443",
60
+ /** base URL for gRPC-Web transports */
61
+ grpcWeb: "https://mainnet-grpc.inodra.com",
62
+ warpWs: "wss://mainnet-api.inodra.com/v1"
63
+ },
64
+ testnet: {
65
+ rest: "https://testnet-api.inodra.com/v1",
66
+ graphql: "https://testnet-api.inodra.com/v1/graphql",
67
+ grpc: "testnet-grpc.inodra.com:443",
68
+ grpcWeb: "https://testnet-grpc.inodra.com",
69
+ warpWs: "wss://testnet-api.inodra.com/v1"
70
+ }
71
+ };
72
+ function restBaseUrl(network) {
73
+ return INODRA_ENDPOINTS[network].rest;
74
+ }
75
+ function graphqlUrl(network) {
76
+ return INODRA_ENDPOINTS[network].graphql;
77
+ }
78
+ function grpcBaseUrl(network) {
79
+ return INODRA_ENDPOINTS[network].grpcWeb;
80
+ }
81
+ function grpcHost(network) {
82
+ return INODRA_ENDPOINTS[network].grpc;
83
+ }
84
+ function warpStreamUrl(network, subscriptionId) {
85
+ return `${INODRA_ENDPOINTS[network].rest}/warp/${subscriptionId}/stream`;
86
+ }
87
+ function warpWebSocketUrl(network, subscriptionId) {
88
+ return `${INODRA_ENDPOINTS[network].warpWs}/warp/${subscriptionId}/ws`;
89
+ }
90
+
91
+ // src/core/version.ts
92
+ var SDK_VERSION = "0.1.0";
93
+ var SDK_HEADER_NAME = "x-inodra-sdk";
94
+ var SDK_HEADER_VALUE = `inodra-sdk-ts/${SDK_VERSION}`;
95
+
96
+ export {
97
+ resolveAuth,
98
+ authFromOptions,
99
+ staticAuthHeaders,
100
+ INODRA_ENDPOINTS,
101
+ restBaseUrl,
102
+ graphqlUrl,
103
+ grpcBaseUrl,
104
+ grpcHost,
105
+ warpStreamUrl,
106
+ warpWebSocketUrl,
107
+ SDK_VERSION,
108
+ SDK_HEADER_NAME,
109
+ SDK_HEADER_VALUE
110
+ };
111
+ //# sourceMappingURL=chunk-U6D5LGNF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/auth.ts","../src/core/network.ts","../src/core/version.ts"],"sourcesContent":["/**\n * Pluggable authentication for every Inodra transport.\n *\n * Most users pass an API key. The provider shape exists so future credential\n * types (x402 wallet session tokens, refreshing vault-issued keys) slot in\n * without changing any client constructor.\n */\n\nexport interface AuthCredentials {\n\t/** Sent as HTTP headers (REST, SSE, GraphQL) and gRPC metadata. */\n\theaders: Record<string, string>;\n\t/**\n\t * Sent as URL query parameters on transports that cannot carry headers\n\t * (WebSocket upgrades in browsers). Omit to disallow such transports.\n\t */\n\tqueryParams?: Record<string, string>;\n}\n\nexport type InodraAuth =\n\t| string\n\t| { apiKey: string }\n\t| { sessionToken: string }\n\t| {\n\t\t\tgetCredentials: () => AuthCredentials | Promise<AuthCredentials>;\n\t };\n\n/** Normalized auth handle used internally by every transport. */\nexport interface ResolvedAuth {\n\t/** Resolve credentials. Called per request on REST/SSE, per connect on WS. */\n\tcredentials(): Promise<AuthCredentials>;\n\t/**\n\t * Resolve credentials synchronously, or undefined for dynamic providers.\n\t * Used by clients whose underlying transport takes static headers\n\t * (gRPC, GraphQL).\n\t */\n\tstaticCredentials(): AuthCredentials | undefined;\n}\n\nexport function resolveAuth(auth: InodraAuth): ResolvedAuth {\n\tif (typeof auth === \"string\") {\n\t\treturn staticAuth(apiKeyCredentials(auth));\n\t}\n\tif (\"apiKey\" in auth) {\n\t\treturn staticAuth(apiKeyCredentials(auth.apiKey));\n\t}\n\tif (\"sessionToken\" in auth) {\n\t\treturn staticAuth({\n\t\t\theaders: { authorization: `Bearer ${auth.sessionToken}` },\n\t\t});\n\t}\n\treturn {\n\t\tcredentials: async () => auth.getCredentials(),\n\t\tstaticCredentials: () => undefined,\n\t};\n}\n\n/**\n * Resolve auth from an options bag that accepts both the modern `auth` field\n * and the original `apiKey` shorthand.\n */\nexport function authFromOptions(\n\toptions: { auth?: InodraAuth; apiKey?: string },\n\tcontext: string,\n): ResolvedAuth {\n\tif (options.auth !== undefined) {\n\t\treturn resolveAuth(options.auth);\n\t}\n\tif (options.apiKey) {\n\t\treturn resolveAuth(options.apiKey);\n\t}\n\tthrow new Error(`${context} requires auth (or the apiKey shorthand)`);\n}\n\n/**\n * Headers for clients whose transports take static headers (gRPC, GraphQL).\n * Dynamic providers are rejected: the underlying Mysten clients cannot\n * re-resolve credentials per call, so a snapshot would silently go stale.\n */\nexport function staticAuthHeaders(\n\toptions: { auth?: InodraAuth; apiKey?: string },\n\tcontext: string,\n): Record<string, string> {\n\tconst credentials = authFromOptions(options, context).staticCredentials();\n\tif (!credentials) {\n\t\tthrow new Error(\n\t\t\t`${context} does not support dynamic credential providers — pass an apiKey or sessionToken`,\n\t\t);\n\t}\n\treturn credentials.headers;\n}\n\nfunction apiKeyCredentials(apiKey: string): AuthCredentials {\n\tif (!apiKey) {\n\t\tthrow new Error(\"Inodra auth: apiKey must be a non-empty string\");\n\t}\n\treturn {\n\t\theaders: { \"x-api-key\": apiKey },\n\t\tqueryParams: { api_key: apiKey },\n\t};\n}\n\nfunction staticAuth(credentials: AuthCredentials): ResolvedAuth {\n\treturn {\n\t\tcredentials: async () => credentials,\n\t\tstaticCredentials: () => credentials,\n\t};\n}\n","export type InodraNetwork = \"mainnet\" | \"testnet\";\n\n/**\n * Canonical Inodra endpoints per network and protocol.\n */\nexport const INODRA_ENDPOINTS = {\n\tmainnet: {\n\t\trest: \"https://mainnet-api.inodra.com/v1\",\n\t\tgraphql: \"https://mainnet-api.inodra.com/v1/graphql\",\n\t\t/** host:port for native gRPC transports */\n\t\tgrpc: \"mainnet-grpc.inodra.com:443\",\n\t\t/** base URL for gRPC-Web transports */\n\t\tgrpcWeb: \"https://mainnet-grpc.inodra.com\",\n\t\twarpWs: \"wss://mainnet-api.inodra.com/v1\",\n\t},\n\ttestnet: {\n\t\trest: \"https://testnet-api.inodra.com/v1\",\n\t\tgraphql: \"https://testnet-api.inodra.com/v1/graphql\",\n\t\tgrpc: \"testnet-grpc.inodra.com:443\",\n\t\tgrpcWeb: \"https://testnet-grpc.inodra.com\",\n\t\twarpWs: \"wss://testnet-api.inodra.com/v1\",\n\t},\n} as const satisfies Record<\n\tInodraNetwork,\n\t{ rest: string; graphql: string; grpc: string; grpcWeb: string; warpWs: string }\n>;\n\nexport function restBaseUrl(network: InodraNetwork): string {\n\treturn INODRA_ENDPOINTS[network].rest;\n}\n\nexport function graphqlUrl(network: InodraNetwork): string {\n\treturn INODRA_ENDPOINTS[network].graphql;\n}\n\n/** Base URL for gRPC-Web / Connect transports. */\nexport function grpcBaseUrl(network: InodraNetwork): string {\n\treturn INODRA_ENDPOINTS[network].grpcWeb;\n}\n\n/** host:port for native gRPC transports. */\nexport function grpcHost(network: InodraNetwork): string {\n\treturn INODRA_ENDPOINTS[network].grpc;\n}\n\nexport function warpStreamUrl(\n\tnetwork: InodraNetwork,\n\tsubscriptionId: string,\n): string {\n\treturn `${INODRA_ENDPOINTS[network].rest}/warp/${subscriptionId}/stream`;\n}\n\nexport function warpWebSocketUrl(\n\tnetwork: InodraNetwork,\n\tsubscriptionId: string,\n): string {\n\treturn `${INODRA_ENDPOINTS[network].warpWs}/warp/${subscriptionId}/ws`;\n}\n","export const SDK_VERSION = \"0.1.0\";\n\n/**\n * Identifies the SDK to Inodra on every request, alongside the API key.\n * Sent on REST, GraphQL, SSE (headers) and gRPC (metadata). Browsers cannot\n * attach headers to WebSocket upgrades, so the Warp WS client omits it.\n */\nexport const SDK_HEADER_NAME = \"x-inodra-sdk\";\nexport const SDK_HEADER_VALUE = `inodra-sdk-ts/${SDK_VERSION}`;\n"],"mappings":";AAsCO,SAAS,YAAY,MAAgC;AAC3D,MAAI,OAAO,SAAS,UAAU;AAC7B,WAAO,WAAW,kBAAkB,IAAI,CAAC;AAAA,EAC1C;AACA,MAAI,YAAY,MAAM;AACrB,WAAO,WAAW,kBAAkB,KAAK,MAAM,CAAC;AAAA,EACjD;AACA,MAAI,kBAAkB,MAAM;AAC3B,WAAO,WAAW;AAAA,MACjB,SAAS,EAAE,eAAe,UAAU,KAAK,YAAY,GAAG;AAAA,IACzD,CAAC;AAAA,EACF;AACA,SAAO;AAAA,IACN,aAAa,YAAY,KAAK,eAAe;AAAA,IAC7C,mBAAmB,MAAM;AAAA,EAC1B;AACD;AAMO,SAAS,gBACf,SACA,SACe;AACf,MAAI,QAAQ,SAAS,QAAW;AAC/B,WAAO,YAAY,QAAQ,IAAI;AAAA,EAChC;AACA,MAAI,QAAQ,QAAQ;AACnB,WAAO,YAAY,QAAQ,MAAM;AAAA,EAClC;AACA,QAAM,IAAI,MAAM,GAAG,OAAO,0CAA0C;AACrE;AAOO,SAAS,kBACf,SACA,SACyB;AACzB,QAAM,cAAc,gBAAgB,SAAS,OAAO,EAAE,kBAAkB;AACxE,MAAI,CAAC,aAAa;AACjB,UAAM,IAAI;AAAA,MACT,GAAG,OAAO;AAAA,IACX;AAAA,EACD;AACA,SAAO,YAAY;AACpB;AAEA,SAAS,kBAAkB,QAAiC;AAC3D,MAAI,CAAC,QAAQ;AACZ,UAAM,IAAI,MAAM,gDAAgD;AAAA,EACjE;AACA,SAAO;AAAA,IACN,SAAS,EAAE,aAAa,OAAO;AAAA,IAC/B,aAAa,EAAE,SAAS,OAAO;AAAA,EAChC;AACD;AAEA,SAAS,WAAW,aAA4C;AAC/D,SAAO;AAAA,IACN,aAAa,YAAY;AAAA,IACzB,mBAAmB,MAAM;AAAA,EAC1B;AACD;;;ACrGO,IAAM,mBAAmB;AAAA,EAC/B,SAAS;AAAA,IACR,MAAM;AAAA,IACN,SAAS;AAAA;AAAA,IAET,MAAM;AAAA;AAAA,IAEN,SAAS;AAAA,IACT,QAAQ;AAAA,EACT;AAAA,EACA,SAAS;AAAA,IACR,MAAM;AAAA,IACN,SAAS;AAAA,IACT,MAAM;AAAA,IACN,SAAS;AAAA,IACT,QAAQ;AAAA,EACT;AACD;AAKO,SAAS,YAAY,SAAgC;AAC3D,SAAO,iBAAiB,OAAO,EAAE;AAClC;AAEO,SAAS,WAAW,SAAgC;AAC1D,SAAO,iBAAiB,OAAO,EAAE;AAClC;AAGO,SAAS,YAAY,SAAgC;AAC3D,SAAO,iBAAiB,OAAO,EAAE;AAClC;AAGO,SAAS,SAAS,SAAgC;AACxD,SAAO,iBAAiB,OAAO,EAAE;AAClC;AAEO,SAAS,cACf,SACA,gBACS;AACT,SAAO,GAAG,iBAAiB,OAAO,EAAE,IAAI,SAAS,cAAc;AAChE;AAEO,SAAS,iBACf,SACA,gBACS;AACT,SAAO,GAAG,iBAAiB,OAAO,EAAE,MAAM,SAAS,cAAc;AAClE;;;ACzDO,IAAM,cAAc;AAOpB,IAAM,kBAAkB;AACxB,IAAM,mBAAmB,iBAAiB,WAAW;","names":[]}
@@ -0,0 +1,539 @@
1
+ import {
2
+ SDK_HEADER_NAME,
3
+ SDK_HEADER_VALUE,
4
+ authFromOptions,
5
+ warpStreamUrl,
6
+ warpWebSocketUrl
7
+ } from "./chunk-U6D5LGNF.js";
8
+
9
+ // src/warp/iterate.ts
10
+ async function* iterateStream(stream, options = {}) {
11
+ const queue = [];
12
+ let notify;
13
+ let closed = false;
14
+ let failure;
15
+ const unsubscribes = [
16
+ stream.on("message", (event) => {
17
+ queue.push(event);
18
+ notify?.();
19
+ }),
20
+ stream.on("close", ({ reason }) => {
21
+ closed = true;
22
+ if (reason !== "client closed") {
23
+ failure = new Error(`Warp stream closed: ${reason}`);
24
+ }
25
+ notify?.();
26
+ })
27
+ ];
28
+ try {
29
+ for (; ; ) {
30
+ while (queue.length > 0) {
31
+ yield queue.shift();
32
+ }
33
+ if (closed) {
34
+ if (failure) {
35
+ throw failure;
36
+ }
37
+ return;
38
+ }
39
+ await new Promise((resolve) => {
40
+ notify = resolve;
41
+ });
42
+ notify = void 0;
43
+ }
44
+ } finally {
45
+ for (const unsubscribe of unsubscribes) {
46
+ unsubscribe();
47
+ }
48
+ if (options.closeOnReturn ?? true) {
49
+ stream.close();
50
+ }
51
+ }
52
+ }
53
+
54
+ // src/warp/emitter.ts
55
+ var Emitter = class {
56
+ handlers = /* @__PURE__ */ new Map();
57
+ on(event, handler) {
58
+ let set = this.handlers.get(event);
59
+ if (!set) {
60
+ set = /* @__PURE__ */ new Set();
61
+ this.handlers.set(event, set);
62
+ }
63
+ set.add(handler);
64
+ return () => set?.delete(handler);
65
+ }
66
+ off(event, handler) {
67
+ this.handlers.get(event)?.delete(handler);
68
+ }
69
+ /** Returns false if any handler threw. */
70
+ emit(event, payload) {
71
+ let ok = true;
72
+ for (const handler of this.handlers.get(event) ?? []) {
73
+ try {
74
+ handler(payload);
75
+ } catch {
76
+ ok = false;
77
+ }
78
+ }
79
+ return ok;
80
+ }
81
+ };
82
+
83
+ // src/warp/sse-parser.ts
84
+ var SseParser = class {
85
+ buffer = "";
86
+ eventName = "";
87
+ dataLines = [];
88
+ id;
89
+ retryMs;
90
+ feed(chunk) {
91
+ this.buffer += chunk;
92
+ const messages = [];
93
+ for (; ; ) {
94
+ const newline = this.buffer.search(/\r\n|\r|\n/);
95
+ if (newline === -1) {
96
+ break;
97
+ }
98
+ const line = this.buffer.slice(0, newline);
99
+ const sep = this.buffer.slice(newline, newline + 2);
100
+ this.buffer = this.buffer.slice(newline + (sep === "\r\n" ? 2 : 1));
101
+ if (line === "") {
102
+ const message = this.flush();
103
+ if (message) {
104
+ messages.push(message);
105
+ }
106
+ continue;
107
+ }
108
+ this.parseLine(line);
109
+ }
110
+ return messages;
111
+ }
112
+ parseLine(line) {
113
+ if (line.startsWith(":")) {
114
+ return;
115
+ }
116
+ const colon = line.indexOf(":");
117
+ const field = colon === -1 ? line : line.slice(0, colon);
118
+ let value = colon === -1 ? "" : line.slice(colon + 1);
119
+ if (value.startsWith(" ")) {
120
+ value = value.slice(1);
121
+ }
122
+ switch (field) {
123
+ case "event":
124
+ this.eventName = value;
125
+ break;
126
+ case "data":
127
+ this.dataLines.push(value);
128
+ break;
129
+ case "id":
130
+ if (!value.includes("\0")) {
131
+ this.id = value;
132
+ }
133
+ break;
134
+ case "retry": {
135
+ const ms = Number(value);
136
+ if (Number.isInteger(ms) && ms >= 0) {
137
+ this.retryMs = ms;
138
+ }
139
+ break;
140
+ }
141
+ default:
142
+ break;
143
+ }
144
+ }
145
+ flush() {
146
+ if (this.dataLines.length === 0 && this.eventName === "") {
147
+ const retryOnly = this.retryMs;
148
+ this.retryMs = void 0;
149
+ return retryOnly === void 0 ? void 0 : { event: "", data: "", retryMs: retryOnly };
150
+ }
151
+ const message = {
152
+ event: this.eventName || "message",
153
+ data: this.dataLines.join("\n"),
154
+ id: this.id,
155
+ retryMs: this.retryMs
156
+ };
157
+ this.eventName = "";
158
+ this.dataLines = [];
159
+ this.retryMs = void 0;
160
+ return message;
161
+ }
162
+ };
163
+
164
+ // src/warp/types.ts
165
+ var CONTROL_EVENTS = /* @__PURE__ */ new Set(["connected", "error", "quota_exceeded"]);
166
+ function backoffDelayMs(attempt) {
167
+ return Math.min(1e3 * 2 ** (attempt - 1), 3e4) + Math.random() * 250;
168
+ }
169
+
170
+ // src/warp/sse.ts
171
+ var WarpSseStream = class extends Emitter {
172
+ lastEventId;
173
+ options;
174
+ auth;
175
+ url;
176
+ fetchImpl;
177
+ abort;
178
+ reconnectAttempt = 0;
179
+ serverRetryMs;
180
+ closed = false;
181
+ closeEmitted = false;
182
+ constructor(options) {
183
+ super();
184
+ this.options = options;
185
+ this.auth = authFromOptions(options, "WarpSseStream");
186
+ const base = options.url ?? warpStreamUrl(options.network ?? "mainnet", options.subscriptionId);
187
+ if (options.mode !== void 0) {
188
+ const url = new URL(base);
189
+ url.searchParams.set("mode", options.mode);
190
+ this.url = url.toString();
191
+ } else {
192
+ this.url = base;
193
+ }
194
+ this.fetchImpl = options.fetch ?? globalThis.fetch;
195
+ this.lastEventId = options.lastEventId;
196
+ }
197
+ connect() {
198
+ if (this.abort) {
199
+ return;
200
+ }
201
+ this.closed = false;
202
+ this.closeEmitted = false;
203
+ this.abort = new AbortController();
204
+ void this.run(this.abort.signal);
205
+ }
206
+ close() {
207
+ const wasActive = this.abort !== void 0;
208
+ this.closed = true;
209
+ this.abort?.abort();
210
+ this.abort = void 0;
211
+ if (wasActive) {
212
+ this.emitClose("client closed");
213
+ }
214
+ }
215
+ /** Emit "close" exactly once per connection lifecycle. */
216
+ emitClose(reason) {
217
+ if (this.closeEmitted) {
218
+ return;
219
+ }
220
+ this.closeEmitted = true;
221
+ this.emit("close", { reason });
222
+ }
223
+ /** Iterate data events; ends when the stream closes (throws unless client-initiated). */
224
+ [Symbol.asyncIterator]() {
225
+ return iterateStream(this);
226
+ }
227
+ async run(signal) {
228
+ for (; ; ) {
229
+ try {
230
+ await this.consume(signal);
231
+ if (this.closed || signal.aborted) {
232
+ return;
233
+ }
234
+ } catch (error) {
235
+ if (this.closed || signal.aborted) {
236
+ return;
237
+ }
238
+ if (error instanceof TerminalStreamError) {
239
+ this.emit("error", error);
240
+ this.emitClose(error.message);
241
+ this.close();
242
+ return;
243
+ }
244
+ this.emit(
245
+ "error",
246
+ error instanceof Error ? error : new Error(String(error))
247
+ );
248
+ }
249
+ if (!(this.options.reconnect ?? true)) {
250
+ this.emitClose("disconnected");
251
+ this.close();
252
+ return;
253
+ }
254
+ this.reconnectAttempt += 1;
255
+ if (this.reconnectAttempt > (this.options.maxReconnectAttempts ?? 10)) {
256
+ this.emitClose("max reconnect attempts exceeded");
257
+ this.close();
258
+ return;
259
+ }
260
+ const delayMs = this.serverRetryMs ?? backoffDelayMs(this.reconnectAttempt);
261
+ this.emit("reconnecting", { attempt: this.reconnectAttempt, delayMs });
262
+ await sleep(delayMs);
263
+ if (this.closed || signal.aborted) {
264
+ return;
265
+ }
266
+ }
267
+ }
268
+ async consume(signal) {
269
+ const { headers: authHeaders } = await this.auth.credentials();
270
+ const headers = {
271
+ accept: "text/event-stream",
272
+ ...authHeaders,
273
+ [SDK_HEADER_NAME]: SDK_HEADER_VALUE
274
+ };
275
+ if (this.lastEventId !== void 0) {
276
+ headers["Last-Event-ID"] = this.lastEventId;
277
+ }
278
+ const response = await this.fetchImpl(this.url, { headers, signal });
279
+ if (!response.ok || !response.body) {
280
+ const retryable = response.status >= 500;
281
+ const message = `SSE connection failed with status ${response.status}`;
282
+ if (retryable) {
283
+ throw new Error(message);
284
+ }
285
+ throw new TerminalStreamError(message);
286
+ }
287
+ const parser = new SseParser();
288
+ const decoder = new TextDecoder();
289
+ const reader = response.body.getReader();
290
+ for (; ; ) {
291
+ const { done, value } = await reader.read();
292
+ if (done) {
293
+ return;
294
+ }
295
+ for (const message of parser.feed(decoder.decode(value, { stream: true }))) {
296
+ if (message.retryMs !== void 0) {
297
+ this.serverRetryMs = message.retryMs;
298
+ }
299
+ if (message.event === "" && message.data === "") {
300
+ continue;
301
+ }
302
+ this.dispatch(message.event, message.id, message.data);
303
+ }
304
+ }
305
+ }
306
+ dispatch(event, id, raw) {
307
+ let data = raw;
308
+ try {
309
+ data = JSON.parse(raw);
310
+ } catch {
311
+ }
312
+ if (id !== void 0) {
313
+ this.lastEventId = id;
314
+ }
315
+ switch (event) {
316
+ case "connected":
317
+ this.reconnectAttempt = 0;
318
+ this.emit("connected", data);
319
+ return;
320
+ case "quota_exceeded":
321
+ this.emit("quota_exceeded", data);
322
+ this.emitClose("quota_exceeded");
323
+ this.close();
324
+ return;
325
+ case "error": {
326
+ const message = typeof data === "object" && data !== null && "error" in data ? String(data.error) : raw;
327
+ this.emit("error", new Error(message));
328
+ return;
329
+ }
330
+ default:
331
+ if (!CONTROL_EVENTS.has(event)) {
332
+ this.emit("message", { type: event, id, data });
333
+ }
334
+ }
335
+ }
336
+ };
337
+ var TerminalStreamError = class extends Error {
338
+ };
339
+ function sleep(ms) {
340
+ return new Promise((resolve) => setTimeout(resolve, ms));
341
+ }
342
+
343
+ // src/warp/ws.ts
344
+ var WarpWebSocketStream = class extends Emitter {
345
+ lastEventId;
346
+ options;
347
+ auth;
348
+ wsCtor;
349
+ ws;
350
+ reconnectAttempt = 0;
351
+ closed = false;
352
+ constructor(options) {
353
+ super();
354
+ this.options = options;
355
+ this.auth = authFromOptions(options, "WarpWebSocketStream");
356
+ const ctor = options.webSocket ?? globalThis.WebSocket;
357
+ if (!ctor) {
358
+ throw new Error(
359
+ "No WebSocket implementation available \u2014 pass one via the webSocket option"
360
+ );
361
+ }
362
+ this.wsCtor = ctor;
363
+ this.lastEventId = options.lastEventId;
364
+ }
365
+ connect() {
366
+ if (this.ws) {
367
+ return;
368
+ }
369
+ this.closed = false;
370
+ this.open();
371
+ }
372
+ close() {
373
+ this.closed = true;
374
+ this.ws?.close(1e3, "client closed");
375
+ this.ws = void 0;
376
+ }
377
+ /** Acknowledge an event by ID (only needed with autoAck: false). */
378
+ ack(id) {
379
+ this.ws?.send(JSON.stringify({ type: "ack", id }));
380
+ }
381
+ /** Iterate data events; ends when the stream closes (throws unless client-initiated). */
382
+ [Symbol.asyncIterator]() {
383
+ return iterateStream(this);
384
+ }
385
+ buildUrl(authParams) {
386
+ const base = this.options.url ?? warpWebSocketUrl(
387
+ this.options.network ?? "mainnet",
388
+ this.options.subscriptionId
389
+ );
390
+ const url = new URL(base);
391
+ for (const [key, value] of Object.entries(authParams)) {
392
+ url.searchParams.set(key, value);
393
+ }
394
+ if (this.options.mode !== void 0) {
395
+ url.searchParams.set("mode", this.options.mode);
396
+ }
397
+ if (this.lastEventId !== void 0) {
398
+ url.searchParams.set("lastEventId", this.lastEventId);
399
+ }
400
+ return url.toString();
401
+ }
402
+ open() {
403
+ const credentials = this.auth.staticCredentials();
404
+ if (credentials) {
405
+ this.openWith(credentials.queryParams);
406
+ return;
407
+ }
408
+ void this.auth.credentials().then(
409
+ (resolved) => {
410
+ if (!this.closed && !this.ws) {
411
+ this.openWith(resolved.queryParams);
412
+ }
413
+ },
414
+ (error) => {
415
+ this.failTerminally(
416
+ error instanceof Error ? error : new Error(String(error)),
417
+ "credential resolution failed"
418
+ );
419
+ }
420
+ );
421
+ }
422
+ failTerminally(error, reason) {
423
+ this.closed = true;
424
+ this.emit("error", error);
425
+ this.emit("close", { reason });
426
+ }
427
+ openWith(authParams) {
428
+ if (!authParams) {
429
+ this.failTerminally(
430
+ new Error(
431
+ "These credentials cannot authenticate a WebSocket (no header support on upgrades) \u2014 use an API key or the SSE stream"
432
+ ),
433
+ "unsupported credentials"
434
+ );
435
+ return;
436
+ }
437
+ const ws = new this.wsCtor(this.buildUrl(authParams));
438
+ this.ws = ws;
439
+ ws.addEventListener("message", (event) => {
440
+ this.handleMessage(event);
441
+ });
442
+ ws.addEventListener("error", () => {
443
+ this.emit("error", new Error("WebSocket transport error"));
444
+ });
445
+ ws.addEventListener("close", (event) => {
446
+ if (this.ws !== ws) {
447
+ return;
448
+ }
449
+ this.ws = void 0;
450
+ this.handleClose(event.code, event.reason);
451
+ });
452
+ }
453
+ handleMessage(event) {
454
+ let parsed;
455
+ try {
456
+ parsed = JSON.parse(
457
+ typeof event.data === "string" ? event.data : String(event.data)
458
+ );
459
+ } catch {
460
+ this.emit("error", new Error("Received malformed WebSocket message"));
461
+ return;
462
+ }
463
+ const type = typeof parsed.event === "string" ? parsed.event : "message";
464
+ const id = typeof parsed.id === "string" ? parsed.id : void 0;
465
+ switch (type) {
466
+ case "connected":
467
+ this.reconnectAttempt = 0;
468
+ this.emit("connected", parsed.data);
469
+ return;
470
+ case "quota_exceeded":
471
+ this.emit("quota_exceeded", parsed.data);
472
+ return;
473
+ // server closes with 1008; handleClose makes it terminal
474
+ case "error": {
475
+ const message = typeof parsed.data === "object" && parsed.data !== null && "error" in parsed.data ? String(parsed.data.error) : "Server error";
476
+ this.emit("error", new Error(message));
477
+ return;
478
+ }
479
+ default: {
480
+ if (id !== void 0) {
481
+ this.lastEventId = id;
482
+ }
483
+ const handlersSucceeded = this.emit("message", {
484
+ type,
485
+ id,
486
+ data: parsed.data
487
+ });
488
+ if ((this.options.autoAck ?? true) && id !== void 0) {
489
+ if (handlersSucceeded) {
490
+ this.ack(id);
491
+ } else {
492
+ this.emit(
493
+ "error",
494
+ new Error(
495
+ `Message handler threw; event ${id} not acked and will be redelivered`
496
+ )
497
+ );
498
+ }
499
+ }
500
+ }
501
+ }
502
+ }
503
+ handleClose(code, reason) {
504
+ if (this.closed) {
505
+ this.emit("close", { reason: "client closed" });
506
+ return;
507
+ }
508
+ if (code === 1008) {
509
+ this.emit("close", { reason: reason || "policy violation" });
510
+ this.closed = true;
511
+ return;
512
+ }
513
+ if (!(this.options.reconnect ?? true)) {
514
+ this.emit("close", { reason: reason || "disconnected" });
515
+ this.closed = true;
516
+ return;
517
+ }
518
+ this.reconnectAttempt += 1;
519
+ if (this.reconnectAttempt > (this.options.maxReconnectAttempts ?? 10)) {
520
+ this.emit("close", { reason: "max reconnect attempts exceeded" });
521
+ this.closed = true;
522
+ return;
523
+ }
524
+ const delayMs = backoffDelayMs(this.reconnectAttempt);
525
+ this.emit("reconnecting", { attempt: this.reconnectAttempt, delayMs });
526
+ setTimeout(() => {
527
+ if (!this.closed && !this.ws) {
528
+ this.open();
529
+ }
530
+ }, delayMs);
531
+ }
532
+ };
533
+
534
+ export {
535
+ iterateStream,
536
+ WarpSseStream,
537
+ WarpWebSocketStream
538
+ };
539
+ //# sourceMappingURL=chunk-ZSW3EWGF.js.map