@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.
- package/LICENSE +202 -0
- package/README.md +237 -0
- package/bin/inodra-sui.mjs +2 -0
- package/dist/chunk-3CBLVI6J.js +426 -0
- package/dist/chunk-3CBLVI6J.js.map +1 -0
- package/dist/chunk-4HKKW5QN.js +80 -0
- package/dist/chunk-4HKKW5QN.js.map +1 -0
- package/dist/chunk-74XGJJJI.js +33 -0
- package/dist/chunk-74XGJJJI.js.map +1 -0
- package/dist/chunk-D6FZYWG7.js +99 -0
- package/dist/chunk-D6FZYWG7.js.map +1 -0
- package/dist/chunk-LZZMTSLH.js +31 -0
- package/dist/chunk-LZZMTSLH.js.map +1 -0
- package/dist/chunk-M3M2L6BW.js +29 -0
- package/dist/chunk-M3M2L6BW.js.map +1 -0
- package/dist/chunk-NSXRC5XW.js +298 -0
- package/dist/chunk-NSXRC5XW.js.map +1 -0
- package/dist/chunk-OGMBYWTG.js +537 -0
- package/dist/chunk-OGMBYWTG.js.map +1 -0
- package/dist/chunk-U6D5LGNF.js +111 -0
- package/dist/chunk-U6D5LGNF.js.map +1 -0
- package/dist/chunk-ZSW3EWGF.js +539 -0
- package/dist/chunk-ZSW3EWGF.js.map +1 -0
- package/dist/cli.js +665 -0
- package/dist/cli.js.map +1 -0
- package/dist/events.cjs +3273 -0
- package/dist/events.cjs.map +1 -0
- package/dist/events.d.cts +47 -0
- package/dist/events.d.ts +47 -0
- package/dist/events.js +2578 -0
- package/dist/events.js.map +1 -0
- package/dist/graphql.cjs +129 -0
- package/dist/graphql.cjs.map +1 -0
- package/dist/graphql.d.cts +36 -0
- package/dist/graphql.d.ts +36 -0
- package/dist/graphql.js +10 -0
- package/dist/graphql.js.map +1 -0
- package/dist/grpc-web.cjs +129 -0
- package/dist/grpc-web.cjs.map +1 -0
- package/dist/grpc-web.d.cts +31 -0
- package/dist/grpc-web.d.ts +31 -0
- package/dist/grpc-web.js +10 -0
- package/dist/grpc-web.js.map +1 -0
- package/dist/grpc.cjs +131 -0
- package/dist/grpc.cjs.map +1 -0
- package/dist/grpc.d.cts +34 -0
- package/dist/grpc.d.ts +34 -0
- package/dist/grpc.js +10 -0
- package/dist/grpc.js.map +1 -0
- package/dist/index-CGBN_EI5.d.ts +4800 -0
- package/dist/index-CVO2Kj8a.d.cts +4800 -0
- package/dist/index.cjs +4723 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +504 -0
- package/dist/index.d.ts +504 -0
- package/dist/index.js +59 -0
- package/dist/index.js.map +1 -0
- package/dist/manage.cjs +376 -0
- package/dist/manage.cjs.map +1 -0
- package/dist/manage.d.cts +2 -0
- package/dist/manage.d.ts +2 -0
- package/dist/manage.js +8 -0
- package/dist/manage.js.map +1 -0
- package/dist/network-CEg2MJY3.d.cts +60 -0
- package/dist/network-CEg2MJY3.d.ts +60 -0
- package/dist/node.cjs +4759 -0
- package/dist/node.cjs.map +1 -0
- package/dist/node.d.cts +28 -0
- package/dist/node.d.ts +28 -0
- package/dist/node.js +73 -0
- package/dist/node.js.map +1 -0
- package/dist/reflection-Bx44-Fs1.d.ts +4639 -0
- package/dist/reflection-Cf7WFH6H.d.cts +4639 -0
- package/dist/reflection-FEGYA7DU.js +9 -0
- package/dist/reflection-FEGYA7DU.js.map +1 -0
- package/dist/runtime-OWKF3LZ7.js +37 -0
- package/dist/runtime-OWKF3LZ7.js.map +1 -0
- package/dist/warp.cjs +633 -0
- package/dist/warp.cjs.map +1 -0
- package/dist/warp.d.cts +195 -0
- package/dist/warp.d.ts +195 -0
- package/dist/warp.js +12 -0
- package/dist/warp.js.map +1 -0
- package/dist/webhooks.cjs +104 -0
- package/dist/webhooks.cjs.map +1 -0
- package/dist/webhooks.d.cts +35 -0
- package/dist/webhooks.d.ts +35 -0
- package/dist/webhooks.js +7 -0
- package/dist/webhooks.js.map +1 -0
- package/llms.txt +189 -0
- package/package.json +119 -0
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
import {
|
|
2
|
+
baseEventType,
|
|
3
|
+
moveAddress,
|
|
4
|
+
moveBalance,
|
|
5
|
+
moveBool,
|
|
6
|
+
moveBytes,
|
|
7
|
+
moveSmallUint,
|
|
8
|
+
moveString,
|
|
9
|
+
moveTypeName,
|
|
10
|
+
moveUint
|
|
11
|
+
} from "./chunk-D6FZYWG7.js";
|
|
12
|
+
import {
|
|
13
|
+
SDK_HEADER_NAME,
|
|
14
|
+
SDK_HEADER_VALUE,
|
|
15
|
+
authFromOptions,
|
|
16
|
+
graphqlUrl
|
|
17
|
+
} from "./chunk-U6D5LGNF.js";
|
|
18
|
+
|
|
19
|
+
// src/events/move-types.ts
|
|
20
|
+
import { z } from "zod";
|
|
21
|
+
var PRIMITIVES = /* @__PURE__ */ new Set([
|
|
22
|
+
"u8",
|
|
23
|
+
"u16",
|
|
24
|
+
"u32",
|
|
25
|
+
"u64",
|
|
26
|
+
"u128",
|
|
27
|
+
"u256",
|
|
28
|
+
"bool",
|
|
29
|
+
"address",
|
|
30
|
+
"signer"
|
|
31
|
+
]);
|
|
32
|
+
function splitArgs(inner) {
|
|
33
|
+
const parts = [];
|
|
34
|
+
let depth = 0;
|
|
35
|
+
let current = "";
|
|
36
|
+
for (const char of inner) {
|
|
37
|
+
if (char === "<") depth++;
|
|
38
|
+
if (char === ">") depth--;
|
|
39
|
+
if (char === "," && depth === 0) {
|
|
40
|
+
parts.push(current.trim());
|
|
41
|
+
current = "";
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
current += char;
|
|
45
|
+
}
|
|
46
|
+
if (current.trim()) parts.push(current.trim());
|
|
47
|
+
return parts;
|
|
48
|
+
}
|
|
49
|
+
function parseMoveType(input) {
|
|
50
|
+
const type = input.trim();
|
|
51
|
+
if (PRIMITIVES.has(type)) {
|
|
52
|
+
return { kind: "prim", prim: type };
|
|
53
|
+
}
|
|
54
|
+
if (type.startsWith("vector<") && type.endsWith(">")) {
|
|
55
|
+
return { kind: "vector", inner: parseMoveType(type.slice(7, -1)) };
|
|
56
|
+
}
|
|
57
|
+
const param = /^\$(\d+)$/.exec(type);
|
|
58
|
+
if (param) {
|
|
59
|
+
return { kind: "param", index: Number(param[1]) };
|
|
60
|
+
}
|
|
61
|
+
const generic = type.indexOf("<");
|
|
62
|
+
const base = generic === -1 ? type : type.slice(0, generic);
|
|
63
|
+
const args = generic === -1 || !type.endsWith(">") ? [] : splitArgs(type.slice(generic + 1, -1)).map(parseMoveType);
|
|
64
|
+
const [address, module, name, ...rest] = base.split("::");
|
|
65
|
+
if (address?.startsWith("0x") && module && name && rest.length === 0) {
|
|
66
|
+
return {
|
|
67
|
+
kind: "struct",
|
|
68
|
+
address: normalizeAddress(address),
|
|
69
|
+
module,
|
|
70
|
+
name,
|
|
71
|
+
args
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
return { kind: "opaque", raw: type };
|
|
75
|
+
}
|
|
76
|
+
function normalizeAddress(address) {
|
|
77
|
+
return address.replace(/^0x0+(?=[0-9a-fA-F])/, "0x");
|
|
78
|
+
}
|
|
79
|
+
function formatMoveType(node) {
|
|
80
|
+
switch (node.kind) {
|
|
81
|
+
case "prim":
|
|
82
|
+
return node.prim;
|
|
83
|
+
case "vector":
|
|
84
|
+
return `vector<${formatMoveType(node.inner)}>`;
|
|
85
|
+
case "param":
|
|
86
|
+
return `$${node.index}`;
|
|
87
|
+
case "opaque":
|
|
88
|
+
return node.raw;
|
|
89
|
+
case "struct": {
|
|
90
|
+
const base = `${node.address}::${node.module}::${node.name}`;
|
|
91
|
+
return node.args.length === 0 ? base : `${base}<${node.args.map(formatMoveType).join(", ")}>`;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function substituteTypeParams(node, args) {
|
|
96
|
+
switch (node.kind) {
|
|
97
|
+
case "param":
|
|
98
|
+
return args[node.index] ?? node;
|
|
99
|
+
case "vector":
|
|
100
|
+
return { kind: "vector", inner: substituteTypeParams(node.inner, args) };
|
|
101
|
+
case "struct":
|
|
102
|
+
return {
|
|
103
|
+
...node,
|
|
104
|
+
args: node.args.map((a) => substituteTypeParams(a, args))
|
|
105
|
+
};
|
|
106
|
+
default:
|
|
107
|
+
return node;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
var MAX_DEPTH = 6;
|
|
111
|
+
async function resolveShape(node, resolveStruct, depth = 0, stack = []) {
|
|
112
|
+
switch (node.kind) {
|
|
113
|
+
case "prim":
|
|
114
|
+
switch (node.prim) {
|
|
115
|
+
case "u8":
|
|
116
|
+
case "u16":
|
|
117
|
+
case "u32":
|
|
118
|
+
return { kind: "smallUint" };
|
|
119
|
+
case "u64":
|
|
120
|
+
case "u128":
|
|
121
|
+
case "u256":
|
|
122
|
+
return { kind: "uint" };
|
|
123
|
+
case "bool":
|
|
124
|
+
return { kind: "bool" };
|
|
125
|
+
case "address":
|
|
126
|
+
case "signer":
|
|
127
|
+
return { kind: "address" };
|
|
128
|
+
default:
|
|
129
|
+
return { kind: "unknown" };
|
|
130
|
+
}
|
|
131
|
+
case "vector":
|
|
132
|
+
if (node.inner.kind === "prim" && node.inner.prim === "u8") {
|
|
133
|
+
return { kind: "bytes" };
|
|
134
|
+
}
|
|
135
|
+
return {
|
|
136
|
+
kind: "vector",
|
|
137
|
+
inner: await resolveShape(node.inner, resolveStruct, depth, stack)
|
|
138
|
+
};
|
|
139
|
+
case "param":
|
|
140
|
+
case "opaque":
|
|
141
|
+
return { kind: "unknown" };
|
|
142
|
+
case "struct": {
|
|
143
|
+
const base = `${node.address}::${node.module}::${node.name}`;
|
|
144
|
+
switch (base) {
|
|
145
|
+
case "0x1::string::String":
|
|
146
|
+
case "0x1::ascii::String":
|
|
147
|
+
case "0x2::url::Url":
|
|
148
|
+
return { kind: "string" };
|
|
149
|
+
case "0x1::type_name::TypeName":
|
|
150
|
+
return { kind: "typeName" };
|
|
151
|
+
case "0x2::object::ID":
|
|
152
|
+
return { kind: "address" };
|
|
153
|
+
case "0x2::balance::Balance":
|
|
154
|
+
return { kind: "balance" };
|
|
155
|
+
case "0x1::option::Option": {
|
|
156
|
+
const inner = node.args[0];
|
|
157
|
+
if (node.args.length === 1 && inner) {
|
|
158
|
+
return {
|
|
159
|
+
kind: "option",
|
|
160
|
+
inner: await resolveShape(inner, resolveStruct, depth, stack)
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
return { kind: "unknown" };
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
const key = formatMoveType(node);
|
|
167
|
+
if (depth >= MAX_DEPTH || stack.includes(key)) {
|
|
168
|
+
return { kind: "unknown" };
|
|
169
|
+
}
|
|
170
|
+
const definition = await resolveStruct(node);
|
|
171
|
+
if (!definition) {
|
|
172
|
+
return { kind: "unknown" };
|
|
173
|
+
}
|
|
174
|
+
const fields = await Promise.all(
|
|
175
|
+
definition.map(async (field) => ({
|
|
176
|
+
name: field.name,
|
|
177
|
+
value: await resolveShape(
|
|
178
|
+
substituteTypeParams(field.type, node.args),
|
|
179
|
+
resolveStruct,
|
|
180
|
+
depth + 1,
|
|
181
|
+
[...stack, key]
|
|
182
|
+
)
|
|
183
|
+
}))
|
|
184
|
+
);
|
|
185
|
+
return { kind: "object", fields };
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
function shapeToZod(shape) {
|
|
190
|
+
switch (shape.kind) {
|
|
191
|
+
case "smallUint":
|
|
192
|
+
return moveSmallUint;
|
|
193
|
+
case "uint":
|
|
194
|
+
return moveUint;
|
|
195
|
+
case "bool":
|
|
196
|
+
return moveBool;
|
|
197
|
+
case "address":
|
|
198
|
+
return moveAddress;
|
|
199
|
+
case "string":
|
|
200
|
+
return moveString;
|
|
201
|
+
case "typeName":
|
|
202
|
+
return moveTypeName;
|
|
203
|
+
case "balance":
|
|
204
|
+
return moveBalance;
|
|
205
|
+
case "bytes":
|
|
206
|
+
return moveBytes;
|
|
207
|
+
case "option":
|
|
208
|
+
return shapeToZod(shape.inner).nullable();
|
|
209
|
+
case "vector":
|
|
210
|
+
return z.array(shapeToZod(shape.inner));
|
|
211
|
+
case "object":
|
|
212
|
+
return z.looseObject(
|
|
213
|
+
Object.fromEntries(
|
|
214
|
+
shape.fields.map((f) => [f.name, shapeToZod(f.value)])
|
|
215
|
+
)
|
|
216
|
+
);
|
|
217
|
+
case "unknown":
|
|
218
|
+
return z.unknown();
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
var PRIMITIVE_EXPRS = {
|
|
222
|
+
smallUint: "moveSmallUint",
|
|
223
|
+
uint: "moveUint",
|
|
224
|
+
bool: "moveBool",
|
|
225
|
+
address: "moveAddress",
|
|
226
|
+
string: "moveString",
|
|
227
|
+
typeName: "moveTypeName",
|
|
228
|
+
balance: "moveBalance",
|
|
229
|
+
bytes: "moveBytes"
|
|
230
|
+
};
|
|
231
|
+
function shapeToExpr(shape, used, indent = "") {
|
|
232
|
+
const primitive = PRIMITIVE_EXPRS[shape.kind];
|
|
233
|
+
if (primitive) {
|
|
234
|
+
used.add(primitive);
|
|
235
|
+
return primitive;
|
|
236
|
+
}
|
|
237
|
+
switch (shape.kind) {
|
|
238
|
+
case "option":
|
|
239
|
+
return `${shapeToExpr(shape.inner, used, indent)}.nullable()`;
|
|
240
|
+
case "vector":
|
|
241
|
+
return `z.array(${shapeToExpr(shape.inner, used, indent)})`;
|
|
242
|
+
case "object": {
|
|
243
|
+
const lines = ["z.looseObject({"];
|
|
244
|
+
for (const field of shape.fields) {
|
|
245
|
+
lines.push(
|
|
246
|
+
`${indent} ${JSON.stringify(field.name)}: ${shapeToExpr(field.value, used, `${indent} `)},`
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
lines.push(`${indent}})`);
|
|
250
|
+
return lines.join("\n");
|
|
251
|
+
}
|
|
252
|
+
default:
|
|
253
|
+
return "z.unknown()";
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// src/events/reflection.ts
|
|
258
|
+
var STRUCT_QUERY = `query($pkg: SuiAddress!, $module: String!, $struct: String!) {
|
|
259
|
+
package(address: $pkg) {
|
|
260
|
+
module(name: $module) { struct(name: $struct) { fields { name type { repr } } } }
|
|
261
|
+
}
|
|
262
|
+
}`;
|
|
263
|
+
var EventSchemaReflector = class {
|
|
264
|
+
url;
|
|
265
|
+
headers;
|
|
266
|
+
fetchImpl;
|
|
267
|
+
/** Struct-definition cache, keyed by `address::module::name`. */
|
|
268
|
+
structs = /* @__PURE__ */ new Map();
|
|
269
|
+
/** Resolved shape cache, keyed by the canonical concrete type string. */
|
|
270
|
+
shapes = /* @__PURE__ */ new Map();
|
|
271
|
+
schemas = /* @__PURE__ */ new Map();
|
|
272
|
+
constructor(options = {}) {
|
|
273
|
+
this.url = options.url ?? graphqlUrl(options.network ?? "mainnet");
|
|
274
|
+
this.fetchImpl = options.fetch ?? globalThis.fetch;
|
|
275
|
+
const headers = {
|
|
276
|
+
"content-type": "application/json",
|
|
277
|
+
[SDK_HEADER_NAME]: SDK_HEADER_VALUE
|
|
278
|
+
};
|
|
279
|
+
if (options.auth !== void 0 || options.apiKey) {
|
|
280
|
+
const credentials = authFromOptions(
|
|
281
|
+
options,
|
|
282
|
+
"EventSchemaReflector"
|
|
283
|
+
).staticCredentials();
|
|
284
|
+
if (!credentials) {
|
|
285
|
+
throw new Error(
|
|
286
|
+
"EventSchemaReflector does not support dynamic credential providers"
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
Object.assign(headers, credentials.headers);
|
|
290
|
+
}
|
|
291
|
+
this.headers = headers;
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Resolved schema shape for a concrete Move type (cached). Useful for
|
|
295
|
+
* codegen and introspection; most callers want {@link schemaFor}.
|
|
296
|
+
*/
|
|
297
|
+
shapeFor(type) {
|
|
298
|
+
const node = parseMoveType(type);
|
|
299
|
+
const key = formatMoveType(node);
|
|
300
|
+
let pending = this.shapes.get(key);
|
|
301
|
+
if (!pending) {
|
|
302
|
+
pending = resolveShape(node, (struct) => this.structFields(struct));
|
|
303
|
+
this.shapes.set(key, pending);
|
|
304
|
+
pending.catch(() => this.shapes.delete(key));
|
|
305
|
+
}
|
|
306
|
+
return pending;
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Build (and cache) a loose, normalizing zod schema for any concrete Move
|
|
310
|
+
* event type. Same encoding rules as the generated catalog schemas.
|
|
311
|
+
*/
|
|
312
|
+
schemaFor(type) {
|
|
313
|
+
const key = formatMoveType(parseMoveType(type));
|
|
314
|
+
let pending = this.schemas.get(key);
|
|
315
|
+
if (!pending) {
|
|
316
|
+
pending = this.shapeFor(type).then((shape) => {
|
|
317
|
+
if (shape.kind !== "object") {
|
|
318
|
+
throw new Error(
|
|
319
|
+
`${type} did not resolve to a struct \u2014 is it a Move event type on this network?`
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
return shapeToZod(shape);
|
|
323
|
+
});
|
|
324
|
+
this.schemas.set(key, pending);
|
|
325
|
+
pending.catch(() => this.schemas.delete(key));
|
|
326
|
+
}
|
|
327
|
+
return pending;
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Validate + normalize an event payload against the reflected schema.
|
|
331
|
+
* Accepts a Warp stream message, a bare `{type, data}` event payload, or
|
|
332
|
+
* a webhook body. Returns undefined when the message carries a different
|
|
333
|
+
* event type; throws when the payload doesn't fit the reflected schema.
|
|
334
|
+
*
|
|
335
|
+
* Matching: exact when `type` is fully instantiated; base-type when
|
|
336
|
+
* `type` has no generics (all instantiations match).
|
|
337
|
+
*/
|
|
338
|
+
async parseEvent(message, type) {
|
|
339
|
+
const payload = extractEventPayload(message);
|
|
340
|
+
if (!payload) {
|
|
341
|
+
return void 0;
|
|
342
|
+
}
|
|
343
|
+
const canonical = (t) => formatMoveType(parseMoveType(t));
|
|
344
|
+
const requested = canonical(type);
|
|
345
|
+
const matches = type.includes("<") ? canonical(payload.type) === requested : canonical(baseEventType(payload.type)) === requested;
|
|
346
|
+
if (!matches) {
|
|
347
|
+
return void 0;
|
|
348
|
+
}
|
|
349
|
+
const schema = await this.schemaFor(
|
|
350
|
+
type.includes("<") ? type : payload.type
|
|
351
|
+
);
|
|
352
|
+
return schema.parse(payload.data);
|
|
353
|
+
}
|
|
354
|
+
structFields(struct) {
|
|
355
|
+
const key = `${struct.address}::${struct.module}::${struct.name}`;
|
|
356
|
+
let pending = this.structs.get(key);
|
|
357
|
+
if (!pending) {
|
|
358
|
+
pending = this.fetchStruct(struct);
|
|
359
|
+
this.structs.set(key, pending);
|
|
360
|
+
pending.catch(() => this.structs.delete(key));
|
|
361
|
+
}
|
|
362
|
+
return pending;
|
|
363
|
+
}
|
|
364
|
+
async fetchStruct(struct) {
|
|
365
|
+
let lastError;
|
|
366
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
367
|
+
try {
|
|
368
|
+
const response = await this.fetchImpl(this.url, {
|
|
369
|
+
method: "POST",
|
|
370
|
+
headers: this.headers,
|
|
371
|
+
body: JSON.stringify({
|
|
372
|
+
query: STRUCT_QUERY,
|
|
373
|
+
variables: {
|
|
374
|
+
pkg: struct.address,
|
|
375
|
+
module: struct.module,
|
|
376
|
+
struct: struct.name
|
|
377
|
+
}
|
|
378
|
+
})
|
|
379
|
+
});
|
|
380
|
+
if (!response.ok) {
|
|
381
|
+
throw new Error(`GraphQL reflection failed: HTTP ${response.status}`);
|
|
382
|
+
}
|
|
383
|
+
const body = await response.json();
|
|
384
|
+
if (body.errors) {
|
|
385
|
+
throw new Error(
|
|
386
|
+
`GraphQL reflection errors: ${JSON.stringify(body.errors)}`
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
const fields = body.data?.package?.module?.struct?.fields;
|
|
390
|
+
if (!fields) {
|
|
391
|
+
return void 0;
|
|
392
|
+
}
|
|
393
|
+
return fields.map((field) => ({
|
|
394
|
+
name: field.name,
|
|
395
|
+
type: parseMoveType(field.type.repr)
|
|
396
|
+
}));
|
|
397
|
+
} catch (error) {
|
|
398
|
+
lastError = error;
|
|
399
|
+
await new Promise((r) => setTimeout(r, 300 * 2 ** attempt));
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
throw lastError;
|
|
403
|
+
}
|
|
404
|
+
};
|
|
405
|
+
function extractEventPayload(input, depth = 0) {
|
|
406
|
+
if (depth > 3 || typeof input !== "object" || input === null) {
|
|
407
|
+
return void 0;
|
|
408
|
+
}
|
|
409
|
+
const record = input;
|
|
410
|
+
if (typeof record.type === "string" && record.type.includes("::") && "data" in record) {
|
|
411
|
+
return { type: record.type, data: record.data };
|
|
412
|
+
}
|
|
413
|
+
return extractEventPayload(record.payload, depth + 1) ?? extractEventPayload(record.data, depth + 1);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
export {
|
|
417
|
+
parseMoveType,
|
|
418
|
+
normalizeAddress,
|
|
419
|
+
formatMoveType,
|
|
420
|
+
substituteTypeParams,
|
|
421
|
+
resolveShape,
|
|
422
|
+
shapeToZod,
|
|
423
|
+
shapeToExpr,
|
|
424
|
+
EventSchemaReflector
|
|
425
|
+
};
|
|
426
|
+
//# sourceMappingURL=chunk-3CBLVI6J.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/events/move-types.ts","../src/events/reflection.ts"],"sourcesContent":["/**\n * Move type-string parsing and the shared Move→schema mapping used by both\n * runtime reflection (build zod schemas for arbitrary events on the fly) and\n * the codegen CLI (emit source for them).\n *\n * Encoding rules match the generated catalog schemas — see runtime.ts.\n */\nimport { z } from \"zod\";\nimport {\n\tmoveAddress,\n\tmoveBalance,\n\tmoveBool,\n\tmoveBytes,\n\tmoveSmallUint,\n\tmoveString,\n\tmoveTypeName,\n\tmoveUint,\n} from \"./runtime.js\";\n\n// ---------------------------------------------------------------------------\n// Parsing\n// ---------------------------------------------------------------------------\n\nexport type MoveTypeNode =\n\t| { kind: \"prim\"; prim: string }\n\t| { kind: \"vector\"; inner: MoveTypeNode }\n\t| {\n\t\t\tkind: \"struct\";\n\t\t\taddress: string;\n\t\t\tmodule: string;\n\t\t\tname: string;\n\t\t\targs: MoveTypeNode[];\n\t }\n\t/** `$0`-style placeholder in a struct definition (GraphQL OpenMoveType). */\n\t| { kind: \"param\"; index: number }\n\t/** Anything unparseable (bare `T` in hand-written data, malformed input). */\n\t| { kind: \"opaque\"; raw: string };\n\nconst PRIMITIVES = new Set([\n\t\"u8\",\n\t\"u16\",\n\t\"u32\",\n\t\"u64\",\n\t\"u128\",\n\t\"u256\",\n\t\"bool\",\n\t\"address\",\n\t\"signer\",\n]);\n\n/** Split `a, b<c, d>, e` at top-level commas. */\nfunction splitArgs(inner: string): string[] {\n\tconst parts: string[] = [];\n\tlet depth = 0;\n\tlet current = \"\";\n\tfor (const char of inner) {\n\t\tif (char === \"<\") depth++;\n\t\tif (char === \">\") depth--;\n\t\tif (char === \",\" && depth === 0) {\n\t\t\tparts.push(current.trim());\n\t\t\tcurrent = \"\";\n\t\t\tcontinue;\n\t\t}\n\t\tcurrent += char;\n\t}\n\tif (current.trim()) parts.push(current.trim());\n\treturn parts;\n}\n\nexport function parseMoveType(input: string): MoveTypeNode {\n\tconst type = input.trim();\n\tif (PRIMITIVES.has(type)) {\n\t\treturn { kind: \"prim\", prim: type };\n\t}\n\tif (type.startsWith(\"vector<\") && type.endsWith(\">\")) {\n\t\treturn { kind: \"vector\", inner: parseMoveType(type.slice(7, -1)) };\n\t}\n\tconst param = /^\\$(\\d+)$/.exec(type);\n\tif (param) {\n\t\treturn { kind: \"param\", index: Number(param[1]) };\n\t}\n\tconst generic = type.indexOf(\"<\");\n\tconst base = generic === -1 ? type : type.slice(0, generic);\n\tconst args =\n\t\tgeneric === -1 || !type.endsWith(\">\")\n\t\t\t? []\n\t\t\t: splitArgs(type.slice(generic + 1, -1)).map(parseMoveType);\n\tconst [address, module, name, ...rest] = base.split(\"::\");\n\tif (address?.startsWith(\"0x\") && module && name && rest.length === 0) {\n\t\treturn {\n\t\t\tkind: \"struct\",\n\t\t\taddress: normalizeAddress(address),\n\t\t\tmodule,\n\t\t\tname,\n\t\t\targs,\n\t\t};\n\t}\n\treturn { kind: \"opaque\", raw: type };\n}\n\n/** 0x000…002 → 0x2 */\nexport function normalizeAddress(address: string): string {\n\treturn address.replace(/^0x0+(?=[0-9a-fA-F])/, \"0x\");\n}\n\nexport function formatMoveType(node: MoveTypeNode): string {\n\tswitch (node.kind) {\n\t\tcase \"prim\":\n\t\t\treturn node.prim;\n\t\tcase \"vector\":\n\t\t\treturn `vector<${formatMoveType(node.inner)}>`;\n\t\tcase \"param\":\n\t\t\treturn `$${node.index}`;\n\t\tcase \"opaque\":\n\t\t\treturn node.raw;\n\t\tcase \"struct\": {\n\t\t\tconst base = `${node.address}::${node.module}::${node.name}`;\n\t\t\treturn node.args.length === 0\n\t\t\t\t? base\n\t\t\t\t: `${base}<${node.args.map(formatMoveType).join(\", \")}>`;\n\t\t}\n\t}\n}\n\n/** Replace `$n` placeholders with the instantiation's concrete type args. */\nexport function substituteTypeParams(\n\tnode: MoveTypeNode,\n\targs: MoveTypeNode[],\n): MoveTypeNode {\n\tswitch (node.kind) {\n\t\tcase \"param\":\n\t\t\treturn args[node.index] ?? node;\n\t\tcase \"vector\":\n\t\t\treturn { kind: \"vector\", inner: substituteTypeParams(node.inner, args) };\n\t\tcase \"struct\":\n\t\t\treturn {\n\t\t\t\t...node,\n\t\t\t\targs: node.args.map((a) => substituteTypeParams(a, args)),\n\t\t\t};\n\t\tdefault:\n\t\t\treturn node;\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Schema shapes — the resolved, encoding-aware intermediate form\n// ---------------------------------------------------------------------------\n\nexport type SchemaShape =\n\t| { kind: \"smallUint\" }\n\t| { kind: \"uint\" }\n\t| { kind: \"bool\" }\n\t| { kind: \"address\" }\n\t| { kind: \"string\" }\n\t| { kind: \"typeName\" }\n\t| { kind: \"balance\" }\n\t| { kind: \"bytes\" }\n\t| { kind: \"option\"; inner: SchemaShape }\n\t| { kind: \"vector\"; inner: SchemaShape }\n\t| { kind: \"object\"; fields: { name: string; value: SchemaShape }[] }\n\t| { kind: \"unknown\" };\n\nexport interface StructField {\n\tname: string;\n\t/** Field type as written in the struct definition (may contain `$n`). */\n\ttype: MoveTypeNode;\n}\n\n/** Fetches the field layout of a struct definition (no type args applied). */\nexport type StructResolver = (struct: {\n\taddress: string;\n\tmodule: string;\n\tname: string;\n}) => Promise<StructField[] | undefined>;\n\nconst MAX_DEPTH = 6;\n\n/**\n * Resolve a concrete Move type into a SchemaShape, recursing into nested\n * user structs via `resolveStruct`. Depth-capped and cycle-guarded — anything\n * beyond becomes `unknown` (schemas are loose, so this stays safe).\n */\nexport async function resolveShape(\n\tnode: MoveTypeNode,\n\tresolveStruct: StructResolver,\n\tdepth = 0,\n\tstack: string[] = [],\n): Promise<SchemaShape> {\n\tswitch (node.kind) {\n\t\tcase \"prim\":\n\t\t\tswitch (node.prim) {\n\t\t\t\tcase \"u8\":\n\t\t\t\tcase \"u16\":\n\t\t\t\tcase \"u32\":\n\t\t\t\t\treturn { kind: \"smallUint\" };\n\t\t\t\tcase \"u64\":\n\t\t\t\tcase \"u128\":\n\t\t\t\tcase \"u256\":\n\t\t\t\t\treturn { kind: \"uint\" };\n\t\t\t\tcase \"bool\":\n\t\t\t\t\treturn { kind: \"bool\" };\n\t\t\t\tcase \"address\":\n\t\t\t\tcase \"signer\":\n\t\t\t\t\treturn { kind: \"address\" };\n\t\t\t\tdefault:\n\t\t\t\t\treturn { kind: \"unknown\" };\n\t\t\t}\n\t\tcase \"vector\":\n\t\t\tif (node.inner.kind === \"prim\" && node.inner.prim === \"u8\") {\n\t\t\t\treturn { kind: \"bytes\" };\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tkind: \"vector\",\n\t\t\t\tinner: await resolveShape(node.inner, resolveStruct, depth, stack),\n\t\t\t};\n\t\tcase \"param\":\n\t\tcase \"opaque\":\n\t\t\treturn { kind: \"unknown\" };\n\t\tcase \"struct\": {\n\t\t\tconst base = `${node.address}::${node.module}::${node.name}`;\n\t\t\tswitch (base) {\n\t\t\t\tcase \"0x1::string::String\":\n\t\t\t\tcase \"0x1::ascii::String\":\n\t\t\t\tcase \"0x2::url::Url\":\n\t\t\t\t\treturn { kind: \"string\" };\n\t\t\t\tcase \"0x1::type_name::TypeName\":\n\t\t\t\t\treturn { kind: \"typeName\" };\n\t\t\t\tcase \"0x2::object::ID\":\n\t\t\t\t\treturn { kind: \"address\" };\n\t\t\t\tcase \"0x2::balance::Balance\":\n\t\t\t\t\treturn { kind: \"balance\" };\n\t\t\t\tcase \"0x1::option::Option\": {\n\t\t\t\t\tconst inner = node.args[0];\n\t\t\t\t\tif (node.args.length === 1 && inner) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tkind: \"option\",\n\t\t\t\t\t\t\tinner: await resolveShape(inner, resolveStruct, depth, stack),\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t\treturn { kind: \"unknown\" };\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst key = formatMoveType(node);\n\t\t\tif (depth >= MAX_DEPTH || stack.includes(key)) {\n\t\t\t\treturn { kind: \"unknown\" };\n\t\t\t}\n\t\t\tconst definition = await resolveStruct(node);\n\t\t\tif (!definition) {\n\t\t\t\treturn { kind: \"unknown\" };\n\t\t\t}\n\t\t\tconst fields = await Promise.all(\n\t\t\t\tdefinition.map(async (field) => ({\n\t\t\t\t\tname: field.name,\n\t\t\t\t\tvalue: await resolveShape(\n\t\t\t\t\t\tsubstituteTypeParams(field.type, node.args),\n\t\t\t\t\t\tresolveStruct,\n\t\t\t\t\t\tdepth + 1,\n\t\t\t\t\t\t[...stack, key],\n\t\t\t\t\t),\n\t\t\t\t})),\n\t\t\t);\n\t\t\treturn { kind: \"object\", fields };\n\t\t}\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Consumers: runtime zod builder + codegen expression emitter\n// ---------------------------------------------------------------------------\n\nexport function shapeToZod(shape: SchemaShape): z.ZodTypeAny {\n\tswitch (shape.kind) {\n\t\tcase \"smallUint\":\n\t\t\treturn moveSmallUint;\n\t\tcase \"uint\":\n\t\t\treturn moveUint;\n\t\tcase \"bool\":\n\t\t\treturn moveBool;\n\t\tcase \"address\":\n\t\t\treturn moveAddress;\n\t\tcase \"string\":\n\t\t\treturn moveString;\n\t\tcase \"typeName\":\n\t\t\treturn moveTypeName;\n\t\tcase \"balance\":\n\t\t\treturn moveBalance;\n\t\tcase \"bytes\":\n\t\t\treturn moveBytes;\n\t\tcase \"option\":\n\t\t\treturn shapeToZod(shape.inner).nullable();\n\t\tcase \"vector\":\n\t\t\treturn z.array(shapeToZod(shape.inner));\n\t\tcase \"object\":\n\t\t\treturn z.looseObject(\n\t\t\t\tObject.fromEntries(\n\t\t\t\t\tshape.fields.map((f) => [f.name, shapeToZod(f.value)]),\n\t\t\t\t),\n\t\t\t);\n\t\tcase \"unknown\":\n\t\t\treturn z.unknown();\n\t}\n}\n\nconst PRIMITIVE_EXPRS: Record<string, string> = {\n\tsmallUint: \"moveSmallUint\",\n\tuint: \"moveUint\",\n\tbool: \"moveBool\",\n\taddress: \"moveAddress\",\n\tstring: \"moveString\",\n\ttypeName: \"moveTypeName\",\n\tbalance: \"moveBalance\",\n\tbytes: \"moveBytes\",\n};\n\n/**\n * Emit a zod source expression for a shape (codegen CLI). `used` collects\n * the runtime primitive names the emitted code imports.\n */\nexport function shapeToExpr(\n\tshape: SchemaShape,\n\tused: Set<string>,\n\tindent = \"\",\n): string {\n\tconst primitive = PRIMITIVE_EXPRS[shape.kind];\n\tif (primitive) {\n\t\tused.add(primitive);\n\t\treturn primitive;\n\t}\n\tswitch (shape.kind) {\n\t\tcase \"option\":\n\t\t\treturn `${shapeToExpr(shape.inner, used, indent)}.nullable()`;\n\t\tcase \"vector\":\n\t\t\treturn `z.array(${shapeToExpr(shape.inner, used, indent)})`;\n\t\tcase \"object\": {\n\t\t\tconst lines = [\"z.looseObject({\"];\n\t\t\tfor (const field of shape.fields) {\n\t\t\t\tlines.push(\n\t\t\t\t\t`${indent}\\t${JSON.stringify(field.name)}: ${shapeToExpr(field.value, used, `${indent}\\t`)},`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tlines.push(`${indent}})`);\n\t\t\treturn lines.join(\"\\n\");\n\t\t}\n\t\tdefault:\n\t\t\treturn \"z.unknown()\";\n\t}\n}\n","/**\n * Runtime schema reflection for ARBITRARY Move event types — not just the\n * curated catalog. Fetches struct layouts from Sui GraphQL (Inodra's gateway\n * with your API key, or any Sui GraphQL endpoint), substitutes concrete\n * generics, and builds the same loose, normalizing zod schemas the generated\n * catalog uses.\n *\n * ```ts\n * import { EventSchemaReflector } from \"@inodra/sui/events\";\n *\n * const reflector = new EventSchemaReflector({ apiKey, network: \"mainnet\" });\n * const schema = await reflector.schemaFor(\"0xpkg::vault::Deposit<0x2::sui::SUI>\");\n * const payload = schema.parse(event.data);\n * ```\n */\nimport type { z } from \"zod\";\nimport { authFromOptions, type InodraAuth } from \"../core/auth.js\";\nimport { graphqlUrl, type InodraNetwork } from \"../core/network.js\";\nimport { SDK_HEADER_NAME, SDK_HEADER_VALUE } from \"../core/version.js\";\nimport {\n\tformatMoveType,\n\ttype MoveTypeNode,\n\tparseMoveType,\n\tresolveShape,\n\ttype SchemaShape,\n\tshapeToZod,\n\ttype StructField,\n} from \"./move-types.js\";\nimport { baseEventType } from \"./runtime.js\";\n\nexport interface EventSchemaReflectorOptions {\n\t/** Credentials for Inodra's GraphQL gateway. Omit when using a public url. */\n\tauth?: InodraAuth;\n\t/** Shorthand for `auth: { apiKey }`. */\n\tapiKey?: string;\n\t/** Defaults to \"mainnet\". Ignored when url is set. */\n\tnetwork?: InodraNetwork;\n\t/** GraphQL endpoint override (e.g. a public Sui GraphQL for keyless use). */\n\turl?: string;\n\t/** Custom fetch implementation. */\n\tfetch?: typeof globalThis.fetch;\n}\n\nconst STRUCT_QUERY = `query($pkg: SuiAddress!, $module: String!, $struct: String!) {\n\tpackage(address: $pkg) {\n\t\tmodule(name: $module) { struct(name: $struct) { fields { name type { repr } } } }\n\t}\n}`;\n\ninterface StructQueryResult {\n\tpackage: {\n\t\tmodule: {\n\t\t\tstruct: { fields: { name: string; type: { repr: string } }[] } | null;\n\t\t} | null;\n\t} | null;\n}\n\nexport class EventSchemaReflector {\n\tprivate readonly url: string;\n\tprivate readonly headers: Record<string, string>;\n\tprivate readonly fetchImpl: typeof globalThis.fetch;\n\t/** Struct-definition cache, keyed by `address::module::name`. */\n\tprivate readonly structs = new Map<\n\t\tstring,\n\t\tPromise<StructField[] | undefined>\n\t>();\n\t/** Resolved shape cache, keyed by the canonical concrete type string. */\n\tprivate readonly shapes = new Map<string, Promise<SchemaShape>>();\n\tprivate readonly schemas = new Map<string, Promise<z.ZodTypeAny>>();\n\n\tconstructor(options: EventSchemaReflectorOptions = {}) {\n\t\tthis.url = options.url ?? graphqlUrl(options.network ?? \"mainnet\");\n\t\tthis.fetchImpl = options.fetch ?? globalThis.fetch;\n\t\tconst headers: Record<string, string> = {\n\t\t\t\"content-type\": \"application/json\",\n\t\t\t[SDK_HEADER_NAME]: SDK_HEADER_VALUE,\n\t\t};\n\t\tif (options.auth !== undefined || options.apiKey) {\n\t\t\tconst credentials = authFromOptions(\n\t\t\t\toptions,\n\t\t\t\t\"EventSchemaReflector\",\n\t\t\t).staticCredentials();\n\t\t\tif (!credentials) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"EventSchemaReflector does not support dynamic credential providers\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tObject.assign(headers, credentials.headers);\n\t\t}\n\t\tthis.headers = headers;\n\t}\n\n\t/**\n\t * Resolved schema shape for a concrete Move type (cached). Useful for\n\t * codegen and introspection; most callers want {@link schemaFor}.\n\t */\n\tshapeFor(type: string): Promise<SchemaShape> {\n\t\tconst node = parseMoveType(type);\n\t\tconst key = formatMoveType(node);\n\t\tlet pending = this.shapes.get(key);\n\t\tif (!pending) {\n\t\t\tpending = resolveShape(node, (struct) => this.structFields(struct));\n\t\t\tthis.shapes.set(key, pending);\n\t\t\tpending.catch(() => this.shapes.delete(key));\n\t\t}\n\t\treturn pending;\n\t}\n\n\t/**\n\t * Build (and cache) a loose, normalizing zod schema for any concrete Move\n\t * event type. Same encoding rules as the generated catalog schemas.\n\t */\n\tschemaFor(type: string): Promise<z.ZodTypeAny> {\n\t\tconst key = formatMoveType(parseMoveType(type));\n\t\tlet pending = this.schemas.get(key);\n\t\tif (!pending) {\n\t\t\tpending = this.shapeFor(type).then((shape) => {\n\t\t\t\tif (shape.kind !== \"object\") {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`${type} did not resolve to a struct — is it a Move event type on this network?`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn shapeToZod(shape);\n\t\t\t});\n\t\t\tthis.schemas.set(key, pending);\n\t\t\tpending.catch(() => this.schemas.delete(key));\n\t\t}\n\t\treturn pending;\n\t}\n\n\t/**\n\t * Validate + normalize an event payload against the reflected schema.\n\t * Accepts a Warp stream message, a bare `{type, data}` event payload, or\n\t * a webhook body. Returns undefined when the message carries a different\n\t * event type; throws when the payload doesn't fit the reflected schema.\n\t *\n\t * Matching: exact when `type` is fully instantiated; base-type when\n\t * `type` has no generics (all instantiations match).\n\t */\n\tasync parseEvent(\n\t\tmessage: unknown,\n\t\ttype: string,\n\t): Promise<Record<string, unknown> | undefined> {\n\t\tconst payload = extractEventPayload(message);\n\t\tif (!payload) {\n\t\t\treturn undefined;\n\t\t}\n\t\t// Canonicalize both sides — on-chain type strings use full-width\n\t\t// addresses (0x000…2::…), user input is usually short-form (0x2::…).\n\t\tconst canonical = (t: string) => formatMoveType(parseMoveType(t));\n\t\tconst requested = canonical(type);\n\t\tconst matches = type.includes(\"<\")\n\t\t\t? canonical(payload.type) === requested\n\t\t\t: canonical(baseEventType(payload.type)) === requested;\n\t\tif (!matches) {\n\t\t\treturn undefined;\n\t\t}\n\t\tconst schema = await this.schemaFor(\n\t\t\ttype.includes(\"<\") ? type : payload.type,\n\t\t);\n\t\treturn schema.parse(payload.data) as Record<string, unknown>;\n\t}\n\n\tprivate structFields(struct: {\n\t\taddress: string;\n\t\tmodule: string;\n\t\tname: string;\n\t}): Promise<StructField[] | undefined> {\n\t\tconst key = `${struct.address}::${struct.module}::${struct.name}`;\n\t\tlet pending = this.structs.get(key);\n\t\tif (!pending) {\n\t\t\tpending = this.fetchStruct(struct);\n\t\t\tthis.structs.set(key, pending);\n\t\t\tpending.catch(() => this.structs.delete(key));\n\t\t}\n\t\treturn pending;\n\t}\n\n\tprivate async fetchStruct(struct: {\n\t\taddress: string;\n\t\tmodule: string;\n\t\tname: string;\n\t}): Promise<StructField[] | undefined> {\n\t\tlet lastError: unknown;\n\t\tfor (let attempt = 0; attempt < 3; attempt++) {\n\t\t\ttry {\n\t\t\t\tconst response = await this.fetchImpl(this.url, {\n\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\theaders: this.headers,\n\t\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\t\tquery: STRUCT_QUERY,\n\t\t\t\t\t\tvariables: {\n\t\t\t\t\t\t\tpkg: struct.address,\n\t\t\t\t\t\t\tmodule: struct.module,\n\t\t\t\t\t\t\tstruct: struct.name,\n\t\t\t\t\t\t},\n\t\t\t\t\t}),\n\t\t\t\t});\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\tthrow new Error(`GraphQL reflection failed: HTTP ${response.status}`);\n\t\t\t\t}\n\t\t\t\tconst body = (await response.json()) as {\n\t\t\t\t\tdata?: StructQueryResult;\n\t\t\t\t\terrors?: unknown;\n\t\t\t\t};\n\t\t\t\tif (body.errors) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`GraphQL reflection errors: ${JSON.stringify(body.errors)}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst fields = body.data?.package?.module?.struct?.fields;\n\t\t\t\tif (!fields) {\n\t\t\t\t\treturn undefined; // unknown struct — caller maps to z.unknown()\n\t\t\t\t}\n\t\t\t\treturn fields.map((field) => ({\n\t\t\t\t\tname: field.name,\n\t\t\t\t\ttype: parseMoveType(field.type.repr) as MoveTypeNode,\n\t\t\t\t}));\n\t\t\t} catch (error) {\n\t\t\t\tlastError = error;\n\t\t\t\tawait new Promise((r) => setTimeout(r, 300 * 2 ** attempt));\n\t\t\t}\n\t\t}\n\t\tthrow lastError;\n\t}\n}\n\n// Local copy of the delivery-shape extractor (index.ts owns the catalog-typed\n// variant; this one is untyped by design).\nfunction extractEventPayload(\n\tinput: unknown,\n\tdepth = 0,\n): { type: string; data: unknown } | undefined {\n\tif (depth > 3 || typeof input !== \"object\" || input === null) {\n\t\treturn undefined;\n\t}\n\tconst record = input as Record<string, unknown>;\n\tif (\n\t\ttypeof record.type === \"string\" &&\n\t\trecord.type.includes(\"::\") &&\n\t\t\"data\" in record\n\t) {\n\t\treturn { type: record.type, data: record.data };\n\t}\n\treturn (\n\t\textractEventPayload(record.payload, depth + 1) ??\n\t\textractEventPayload(record.data, depth + 1)\n\t);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAOA,SAAS,SAAS;AA+BlB,IAAM,aAAa,oBAAI,IAAI;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD,CAAC;AAGD,SAAS,UAAU,OAAyB;AAC3C,QAAM,QAAkB,CAAC;AACzB,MAAI,QAAQ;AACZ,MAAI,UAAU;AACd,aAAW,QAAQ,OAAO;AACzB,QAAI,SAAS,IAAK;AAClB,QAAI,SAAS,IAAK;AAClB,QAAI,SAAS,OAAO,UAAU,GAAG;AAChC,YAAM,KAAK,QAAQ,KAAK,CAAC;AACzB,gBAAU;AACV;AAAA,IACD;AACA,eAAW;AAAA,EACZ;AACA,MAAI,QAAQ,KAAK,EAAG,OAAM,KAAK,QAAQ,KAAK,CAAC;AAC7C,SAAO;AACR;AAEO,SAAS,cAAc,OAA6B;AAC1D,QAAM,OAAO,MAAM,KAAK;AACxB,MAAI,WAAW,IAAI,IAAI,GAAG;AACzB,WAAO,EAAE,MAAM,QAAQ,MAAM,KAAK;AAAA,EACnC;AACA,MAAI,KAAK,WAAW,SAAS,KAAK,KAAK,SAAS,GAAG,GAAG;AACrD,WAAO,EAAE,MAAM,UAAU,OAAO,cAAc,KAAK,MAAM,GAAG,EAAE,CAAC,EAAE;AAAA,EAClE;AACA,QAAM,QAAQ,YAAY,KAAK,IAAI;AACnC,MAAI,OAAO;AACV,WAAO,EAAE,MAAM,SAAS,OAAO,OAAO,MAAM,CAAC,CAAC,EAAE;AAAA,EACjD;AACA,QAAM,UAAU,KAAK,QAAQ,GAAG;AAChC,QAAM,OAAO,YAAY,KAAK,OAAO,KAAK,MAAM,GAAG,OAAO;AAC1D,QAAM,OACL,YAAY,MAAM,CAAC,KAAK,SAAS,GAAG,IACjC,CAAC,IACD,UAAU,KAAK,MAAM,UAAU,GAAG,EAAE,CAAC,EAAE,IAAI,aAAa;AAC5D,QAAM,CAAC,SAAS,QAAQ,MAAM,GAAG,IAAI,IAAI,KAAK,MAAM,IAAI;AACxD,MAAI,SAAS,WAAW,IAAI,KAAK,UAAU,QAAQ,KAAK,WAAW,GAAG;AACrE,WAAO;AAAA,MACN,MAAM;AAAA,MACN,SAAS,iBAAiB,OAAO;AAAA,MACjC;AAAA,MACA;AAAA,MACA;AAAA,IACD;AAAA,EACD;AACA,SAAO,EAAE,MAAM,UAAU,KAAK,KAAK;AACpC;AAGO,SAAS,iBAAiB,SAAyB;AACzD,SAAO,QAAQ,QAAQ,wBAAwB,IAAI;AACpD;AAEO,SAAS,eAAe,MAA4B;AAC1D,UAAQ,KAAK,MAAM;AAAA,IAClB,KAAK;AACJ,aAAO,KAAK;AAAA,IACb,KAAK;AACJ,aAAO,UAAU,eAAe,KAAK,KAAK,CAAC;AAAA,IAC5C,KAAK;AACJ,aAAO,IAAI,KAAK,KAAK;AAAA,IACtB,KAAK;AACJ,aAAO,KAAK;AAAA,IACb,KAAK,UAAU;AACd,YAAM,OAAO,GAAG,KAAK,OAAO,KAAK,KAAK,MAAM,KAAK,KAAK,IAAI;AAC1D,aAAO,KAAK,KAAK,WAAW,IACzB,OACA,GAAG,IAAI,IAAI,KAAK,KAAK,IAAI,cAAc,EAAE,KAAK,IAAI,CAAC;AAAA,IACvD;AAAA,EACD;AACD;AAGO,SAAS,qBACf,MACA,MACe;AACf,UAAQ,KAAK,MAAM;AAAA,IAClB,KAAK;AACJ,aAAO,KAAK,KAAK,KAAK,KAAK;AAAA,IAC5B,KAAK;AACJ,aAAO,EAAE,MAAM,UAAU,OAAO,qBAAqB,KAAK,OAAO,IAAI,EAAE;AAAA,IACxE,KAAK;AACJ,aAAO;AAAA,QACN,GAAG;AAAA,QACH,MAAM,KAAK,KAAK,IAAI,CAAC,MAAM,qBAAqB,GAAG,IAAI,CAAC;AAAA,MACzD;AAAA,IACD;AACC,aAAO;AAAA,EACT;AACD;AAiCA,IAAM,YAAY;AAOlB,eAAsB,aACrB,MACA,eACA,QAAQ,GACR,QAAkB,CAAC,GACI;AACvB,UAAQ,KAAK,MAAM;AAAA,IAClB,KAAK;AACJ,cAAQ,KAAK,MAAM;AAAA,QAClB,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AACJ,iBAAO,EAAE,MAAM,YAAY;AAAA,QAC5B,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AACJ,iBAAO,EAAE,MAAM,OAAO;AAAA,QACvB,KAAK;AACJ,iBAAO,EAAE,MAAM,OAAO;AAAA,QACvB,KAAK;AAAA,QACL,KAAK;AACJ,iBAAO,EAAE,MAAM,UAAU;AAAA,QAC1B;AACC,iBAAO,EAAE,MAAM,UAAU;AAAA,MAC3B;AAAA,IACD,KAAK;AACJ,UAAI,KAAK,MAAM,SAAS,UAAU,KAAK,MAAM,SAAS,MAAM;AAC3D,eAAO,EAAE,MAAM,QAAQ;AAAA,MACxB;AACA,aAAO;AAAA,QACN,MAAM;AAAA,QACN,OAAO,MAAM,aAAa,KAAK,OAAO,eAAe,OAAO,KAAK;AAAA,MAClE;AAAA,IACD,KAAK;AAAA,IACL,KAAK;AACJ,aAAO,EAAE,MAAM,UAAU;AAAA,IAC1B,KAAK,UAAU;AACd,YAAM,OAAO,GAAG,KAAK,OAAO,KAAK,KAAK,MAAM,KAAK,KAAK,IAAI;AAC1D,cAAQ,MAAM;AAAA,QACb,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AACJ,iBAAO,EAAE,MAAM,SAAS;AAAA,QACzB,KAAK;AACJ,iBAAO,EAAE,MAAM,WAAW;AAAA,QAC3B,KAAK;AACJ,iBAAO,EAAE,MAAM,UAAU;AAAA,QAC1B,KAAK;AACJ,iBAAO,EAAE,MAAM,UAAU;AAAA,QAC1B,KAAK,uBAAuB;AAC3B,gBAAM,QAAQ,KAAK,KAAK,CAAC;AACzB,cAAI,KAAK,KAAK,WAAW,KAAK,OAAO;AACpC,mBAAO;AAAA,cACN,MAAM;AAAA,cACN,OAAO,MAAM,aAAa,OAAO,eAAe,OAAO,KAAK;AAAA,YAC7D;AAAA,UACD;AACA,iBAAO,EAAE,MAAM,UAAU;AAAA,QAC1B;AAAA,MACD;AACA,YAAM,MAAM,eAAe,IAAI;AAC/B,UAAI,SAAS,aAAa,MAAM,SAAS,GAAG,GAAG;AAC9C,eAAO,EAAE,MAAM,UAAU;AAAA,MAC1B;AACA,YAAM,aAAa,MAAM,cAAc,IAAI;AAC3C,UAAI,CAAC,YAAY;AAChB,eAAO,EAAE,MAAM,UAAU;AAAA,MAC1B;AACA,YAAM,SAAS,MAAM,QAAQ;AAAA,QAC5B,WAAW,IAAI,OAAO,WAAW;AAAA,UAChC,MAAM,MAAM;AAAA,UACZ,OAAO,MAAM;AAAA,YACZ,qBAAqB,MAAM,MAAM,KAAK,IAAI;AAAA,YAC1C;AAAA,YACA,QAAQ;AAAA,YACR,CAAC,GAAG,OAAO,GAAG;AAAA,UACf;AAAA,QACD,EAAE;AAAA,MACH;AACA,aAAO,EAAE,MAAM,UAAU,OAAO;AAAA,IACjC;AAAA,EACD;AACD;AAMO,SAAS,WAAW,OAAkC;AAC5D,UAAQ,MAAM,MAAM;AAAA,IACnB,KAAK;AACJ,aAAO;AAAA,IACR,KAAK;AACJ,aAAO;AAAA,IACR,KAAK;AACJ,aAAO;AAAA,IACR,KAAK;AACJ,aAAO;AAAA,IACR,KAAK;AACJ,aAAO;AAAA,IACR,KAAK;AACJ,aAAO;AAAA,IACR,KAAK;AACJ,aAAO;AAAA,IACR,KAAK;AACJ,aAAO;AAAA,IACR,KAAK;AACJ,aAAO,WAAW,MAAM,KAAK,EAAE,SAAS;AAAA,IACzC,KAAK;AACJ,aAAO,EAAE,MAAM,WAAW,MAAM,KAAK,CAAC;AAAA,IACvC,KAAK;AACJ,aAAO,EAAE;AAAA,QACR,OAAO;AAAA,UACN,MAAM,OAAO,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,WAAW,EAAE,KAAK,CAAC,CAAC;AAAA,QACtD;AAAA,MACD;AAAA,IACD,KAAK;AACJ,aAAO,EAAE,QAAQ;AAAA,EACnB;AACD;AAEA,IAAM,kBAA0C;AAAA,EAC/C,WAAW;AAAA,EACX,MAAM;AAAA,EACN,MAAM;AAAA,EACN,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,SAAS;AAAA,EACT,OAAO;AACR;AAMO,SAAS,YACf,OACA,MACA,SAAS,IACA;AACT,QAAM,YAAY,gBAAgB,MAAM,IAAI;AAC5C,MAAI,WAAW;AACd,SAAK,IAAI,SAAS;AAClB,WAAO;AAAA,EACR;AACA,UAAQ,MAAM,MAAM;AAAA,IACnB,KAAK;AACJ,aAAO,GAAG,YAAY,MAAM,OAAO,MAAM,MAAM,CAAC;AAAA,IACjD,KAAK;AACJ,aAAO,WAAW,YAAY,MAAM,OAAO,MAAM,MAAM,CAAC;AAAA,IACzD,KAAK,UAAU;AACd,YAAM,QAAQ,CAAC,iBAAiB;AAChC,iBAAW,SAAS,MAAM,QAAQ;AACjC,cAAM;AAAA,UACL,GAAG,MAAM,IAAK,KAAK,UAAU,MAAM,IAAI,CAAC,KAAK,YAAY,MAAM,OAAO,MAAM,GAAG,MAAM,GAAI,CAAC;AAAA,QAC3F;AAAA,MACD;AACA,YAAM,KAAK,GAAG,MAAM,IAAI;AACxB,aAAO,MAAM,KAAK,IAAI;AAAA,IACvB;AAAA,IACA;AACC,aAAO;AAAA,EACT;AACD;;;AC/SA,IAAM,eAAe;AAAA;AAAA;AAAA;AAAA;AAcd,IAAM,uBAAN,MAA2B;AAAA,EAChB;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA,UAAU,oBAAI,IAG7B;AAAA;AAAA,EAEe,SAAS,oBAAI,IAAkC;AAAA,EAC/C,UAAU,oBAAI,IAAmC;AAAA,EAElE,YAAY,UAAuC,CAAC,GAAG;AACtD,SAAK,MAAM,QAAQ,OAAO,WAAW,QAAQ,WAAW,SAAS;AACjE,SAAK,YAAY,QAAQ,SAAS,WAAW;AAC7C,UAAM,UAAkC;AAAA,MACvC,gBAAgB;AAAA,MAChB,CAAC,eAAe,GAAG;AAAA,IACpB;AACA,QAAI,QAAQ,SAAS,UAAa,QAAQ,QAAQ;AACjD,YAAM,cAAc;AAAA,QACnB;AAAA,QACA;AAAA,MACD,EAAE,kBAAkB;AACpB,UAAI,CAAC,aAAa;AACjB,cAAM,IAAI;AAAA,UACT;AAAA,QACD;AAAA,MACD;AACA,aAAO,OAAO,SAAS,YAAY,OAAO;AAAA,IAC3C;AACA,SAAK,UAAU;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,SAAS,MAAoC;AAC5C,UAAM,OAAO,cAAc,IAAI;AAC/B,UAAM,MAAM,eAAe,IAAI;AAC/B,QAAI,UAAU,KAAK,OAAO,IAAI,GAAG;AACjC,QAAI,CAAC,SAAS;AACb,gBAAU,aAAa,MAAM,CAAC,WAAW,KAAK,aAAa,MAAM,CAAC;AAClE,WAAK,OAAO,IAAI,KAAK,OAAO;AAC5B,cAAQ,MAAM,MAAM,KAAK,OAAO,OAAO,GAAG,CAAC;AAAA,IAC5C;AACA,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,UAAU,MAAqC;AAC9C,UAAM,MAAM,eAAe,cAAc,IAAI,CAAC;AAC9C,QAAI,UAAU,KAAK,QAAQ,IAAI,GAAG;AAClC,QAAI,CAAC,SAAS;AACb,gBAAU,KAAK,SAAS,IAAI,EAAE,KAAK,CAAC,UAAU;AAC7C,YAAI,MAAM,SAAS,UAAU;AAC5B,gBAAM,IAAI;AAAA,YACT,GAAG,IAAI;AAAA,UACR;AAAA,QACD;AACA,eAAO,WAAW,KAAK;AAAA,MACxB,CAAC;AACD,WAAK,QAAQ,IAAI,KAAK,OAAO;AAC7B,cAAQ,MAAM,MAAM,KAAK,QAAQ,OAAO,GAAG,CAAC;AAAA,IAC7C;AACA,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,WACL,SACA,MAC+C;AAC/C,UAAM,UAAU,oBAAoB,OAAO;AAC3C,QAAI,CAAC,SAAS;AACb,aAAO;AAAA,IACR;AAGA,UAAM,YAAY,CAAC,MAAc,eAAe,cAAc,CAAC,CAAC;AAChE,UAAM,YAAY,UAAU,IAAI;AAChC,UAAM,UAAU,KAAK,SAAS,GAAG,IAC9B,UAAU,QAAQ,IAAI,MAAM,YAC5B,UAAU,cAAc,QAAQ,IAAI,CAAC,MAAM;AAC9C,QAAI,CAAC,SAAS;AACb,aAAO;AAAA,IACR;AACA,UAAM,SAAS,MAAM,KAAK;AAAA,MACzB,KAAK,SAAS,GAAG,IAAI,OAAO,QAAQ;AAAA,IACrC;AACA,WAAO,OAAO,MAAM,QAAQ,IAAI;AAAA,EACjC;AAAA,EAEQ,aAAa,QAIkB;AACtC,UAAM,MAAM,GAAG,OAAO,OAAO,KAAK,OAAO,MAAM,KAAK,OAAO,IAAI;AAC/D,QAAI,UAAU,KAAK,QAAQ,IAAI,GAAG;AAClC,QAAI,CAAC,SAAS;AACb,gBAAU,KAAK,YAAY,MAAM;AACjC,WAAK,QAAQ,IAAI,KAAK,OAAO;AAC7B,cAAQ,MAAM,MAAM,KAAK,QAAQ,OAAO,GAAG,CAAC;AAAA,IAC7C;AACA,WAAO;AAAA,EACR;AAAA,EAEA,MAAc,YAAY,QAIa;AACtC,QAAI;AACJ,aAAS,UAAU,GAAG,UAAU,GAAG,WAAW;AAC7C,UAAI;AACH,cAAM,WAAW,MAAM,KAAK,UAAU,KAAK,KAAK;AAAA,UAC/C,QAAQ;AAAA,UACR,SAAS,KAAK;AAAA,UACd,MAAM,KAAK,UAAU;AAAA,YACpB,OAAO;AAAA,YACP,WAAW;AAAA,cACV,KAAK,OAAO;AAAA,cACZ,QAAQ,OAAO;AAAA,cACf,QAAQ,OAAO;AAAA,YAChB;AAAA,UACD,CAAC;AAAA,QACF,CAAC;AACD,YAAI,CAAC,SAAS,IAAI;AACjB,gBAAM,IAAI,MAAM,mCAAmC,SAAS,MAAM,EAAE;AAAA,QACrE;AACA,cAAM,OAAQ,MAAM,SAAS,KAAK;AAIlC,YAAI,KAAK,QAAQ;AAChB,gBAAM,IAAI;AAAA,YACT,8BAA8B,KAAK,UAAU,KAAK,MAAM,CAAC;AAAA,UAC1D;AAAA,QACD;AACA,cAAM,SAAS,KAAK,MAAM,SAAS,QAAQ,QAAQ;AACnD,YAAI,CAAC,QAAQ;AACZ,iBAAO;AAAA,QACR;AACA,eAAO,OAAO,IAAI,CAAC,WAAW;AAAA,UAC7B,MAAM,MAAM;AAAA,UACZ,MAAM,cAAc,MAAM,KAAK,IAAI;AAAA,QACpC,EAAE;AAAA,MACH,SAAS,OAAO;AACf,oBAAY;AACZ,cAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,MAAM,KAAK,OAAO,CAAC;AAAA,MAC3D;AAAA,IACD;AACA,UAAM;AAAA,EACP;AACD;AAIA,SAAS,oBACR,OACA,QAAQ,GACsC;AAC9C,MAAI,QAAQ,KAAK,OAAO,UAAU,YAAY,UAAU,MAAM;AAC7D,WAAO;AAAA,EACR;AACA,QAAM,SAAS;AACf,MACC,OAAO,OAAO,SAAS,YACvB,OAAO,KAAK,SAAS,IAAI,KACzB,UAAU,QACT;AACD,WAAO,EAAE,MAAM,OAAO,MAAM,MAAM,OAAO,KAAK;AAAA,EAC/C;AACA,SACC,oBAAoB,OAAO,SAAS,QAAQ,CAAC,KAC7C,oBAAoB,OAAO,MAAM,QAAQ,CAAC;AAE5C;","names":[]}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// src/webhooks/index.ts
|
|
2
|
+
async function verifyWebhookSignature(options) {
|
|
3
|
+
const parsed = parseSignatureHeader(options.signature);
|
|
4
|
+
if (!parsed) {
|
|
5
|
+
return false;
|
|
6
|
+
}
|
|
7
|
+
const now = options.now ?? Math.floor(Date.now() / 1e3);
|
|
8
|
+
const tolerance = options.toleranceSeconds ?? 300;
|
|
9
|
+
if (Math.abs(now - parsed.timestamp) > tolerance) {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
const expected = await hmacSha256Hex(
|
|
13
|
+
options.secret,
|
|
14
|
+
`${parsed.timestamp}.${options.payload}`
|
|
15
|
+
);
|
|
16
|
+
return parsed.signatures.some(
|
|
17
|
+
(candidate) => timingSafeEqualHex(candidate, expected)
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
function parseSignatureHeader(header) {
|
|
21
|
+
if (typeof header !== "string" || header.length === 0) {
|
|
22
|
+
return void 0;
|
|
23
|
+
}
|
|
24
|
+
let timestamp;
|
|
25
|
+
const signatures = [];
|
|
26
|
+
for (const part of header.split(",")) {
|
|
27
|
+
const eq = part.indexOf("=");
|
|
28
|
+
if (eq === -1) {
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
const key = part.slice(0, eq).trim();
|
|
32
|
+
const value = part.slice(eq + 1).trim();
|
|
33
|
+
if (key === "t") {
|
|
34
|
+
const ts = Number(value);
|
|
35
|
+
if (Number.isInteger(ts)) {
|
|
36
|
+
timestamp = ts;
|
|
37
|
+
}
|
|
38
|
+
} else if (key === "v1" && /^[0-9a-f]+$/i.test(value)) {
|
|
39
|
+
signatures.push(value.toLowerCase());
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (timestamp === void 0 || signatures.length === 0) {
|
|
43
|
+
return void 0;
|
|
44
|
+
}
|
|
45
|
+
return { timestamp, signatures };
|
|
46
|
+
}
|
|
47
|
+
async function hmacSha256Hex(secret, message) {
|
|
48
|
+
const encoder = new TextEncoder();
|
|
49
|
+
const key = await crypto.subtle.importKey(
|
|
50
|
+
"raw",
|
|
51
|
+
encoder.encode(secret),
|
|
52
|
+
{ name: "HMAC", hash: "SHA-256" },
|
|
53
|
+
false,
|
|
54
|
+
["sign"]
|
|
55
|
+
);
|
|
56
|
+
const signature = await crypto.subtle.sign(
|
|
57
|
+
"HMAC",
|
|
58
|
+
key,
|
|
59
|
+
encoder.encode(message)
|
|
60
|
+
);
|
|
61
|
+
return Array.from(
|
|
62
|
+
new Uint8Array(signature),
|
|
63
|
+
(byte) => byte.toString(16).padStart(2, "0")
|
|
64
|
+
).join("");
|
|
65
|
+
}
|
|
66
|
+
function timingSafeEqualHex(a, b) {
|
|
67
|
+
if (a.length !== b.length) {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
let diff = 0;
|
|
71
|
+
for (let i = 0; i < a.length; i++) {
|
|
72
|
+
diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
73
|
+
}
|
|
74
|
+
return diff === 0;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export {
|
|
78
|
+
verifyWebhookSignature
|
|
79
|
+
};
|
|
80
|
+
//# sourceMappingURL=chunk-4HKKW5QN.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/webhooks/index.ts"],"sourcesContent":["/**\n * Verification for Inodra webhook signatures.\n *\n * Inodra signs webhook deliveries with HMAC-SHA256 when the webhook has a\n * secret configured. The signature arrives in the `X-Inodra-Signature`\n * header as `t=<unix-seconds>,v1=<hex-hmac>` where the HMAC is computed\n * over `<timestamp>.<raw-body>`.\n */\n\nexport interface VerifyWebhookSignatureOptions {\n\t/** Raw request body, exactly as received (before any JSON parsing). */\n\tpayload: string;\n\t/** Value of the X-Inodra-Signature header. */\n\tsignature: string;\n\t/** The webhook's secret. */\n\tsecret: string;\n\t/** Max allowed age of the signature. Defaults to 300 seconds. */\n\ttoleranceSeconds?: number;\n\t/** Current unix time in seconds (for testing). */\n\tnow?: number;\n}\n\n/**\n * Verify a webhook delivery. Returns false for malformed, mismatched, or\n * expired signatures — never throws on bad input.\n *\n * ```ts\n * const valid = await verifyWebhookSignature({\n * payload: rawBody,\n * signature: req.headers[\"x-inodra-signature\"],\n * secret: process.env.INODRA_WEBHOOK_SECRET,\n * });\n * ```\n */\nexport async function verifyWebhookSignature(\n\toptions: VerifyWebhookSignatureOptions,\n): Promise<boolean> {\n\tconst parsed = parseSignatureHeader(options.signature);\n\tif (!parsed) {\n\t\treturn false;\n\t}\n\n\tconst now = options.now ?? Math.floor(Date.now() / 1000);\n\tconst tolerance = options.toleranceSeconds ?? 300;\n\tif (Math.abs(now - parsed.timestamp) > tolerance) {\n\t\treturn false;\n\t}\n\n\tconst expected = await hmacSha256Hex(\n\t\toptions.secret,\n\t\t`${parsed.timestamp}.${options.payload}`,\n\t);\n\treturn parsed.signatures.some((candidate) =>\n\t\ttimingSafeEqualHex(candidate, expected),\n\t);\n}\n\nfunction parseSignatureHeader(\n\theader: string,\n): { timestamp: number; signatures: string[] } | undefined {\n\tif (typeof header !== \"string\" || header.length === 0) {\n\t\treturn undefined;\n\t}\n\tlet timestamp: number | undefined;\n\tconst signatures: string[] = [];\n\n\tfor (const part of header.split(\",\")) {\n\t\tconst eq = part.indexOf(\"=\");\n\t\tif (eq === -1) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst key = part.slice(0, eq).trim();\n\t\tconst value = part.slice(eq + 1).trim();\n\t\tif (key === \"t\") {\n\t\t\tconst ts = Number(value);\n\t\t\tif (Number.isInteger(ts)) {\n\t\t\t\ttimestamp = ts;\n\t\t\t}\n\t\t} else if (key === \"v1\" && /^[0-9a-f]+$/i.test(value)) {\n\t\t\tsignatures.push(value.toLowerCase());\n\t\t}\n\t}\n\n\tif (timestamp === undefined || signatures.length === 0) {\n\t\treturn undefined;\n\t}\n\treturn { timestamp, signatures };\n}\n\nasync function hmacSha256Hex(secret: string, message: string): Promise<string> {\n\tconst encoder = new TextEncoder();\n\tconst key = await crypto.subtle.importKey(\n\t\t\"raw\",\n\t\tencoder.encode(secret),\n\t\t{ name: \"HMAC\", hash: \"SHA-256\" },\n\t\tfalse,\n\t\t[\"sign\"],\n\t);\n\tconst signature = await crypto.subtle.sign(\n\t\t\"HMAC\",\n\t\tkey,\n\t\tencoder.encode(message),\n\t);\n\treturn Array.from(new Uint8Array(signature), (byte) =>\n\t\tbyte.toString(16).padStart(2, \"0\"),\n\t).join(\"\");\n}\n\nfunction timingSafeEqualHex(a: string, b: string): boolean {\n\tif (a.length !== b.length) {\n\t\treturn false;\n\t}\n\tlet diff = 0;\n\tfor (let i = 0; i < a.length; i++) {\n\t\tdiff |= a.charCodeAt(i) ^ b.charCodeAt(i);\n\t}\n\treturn diff === 0;\n}\n"],"mappings":";AAkCA,eAAsB,uBACrB,SACmB;AACnB,QAAM,SAAS,qBAAqB,QAAQ,SAAS;AACrD,MAAI,CAAC,QAAQ;AACZ,WAAO;AAAA,EACR;AAEA,QAAM,MAAM,QAAQ,OAAO,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AACvD,QAAM,YAAY,QAAQ,oBAAoB;AAC9C,MAAI,KAAK,IAAI,MAAM,OAAO,SAAS,IAAI,WAAW;AACjD,WAAO;AAAA,EACR;AAEA,QAAM,WAAW,MAAM;AAAA,IACtB,QAAQ;AAAA,IACR,GAAG,OAAO,SAAS,IAAI,QAAQ,OAAO;AAAA,EACvC;AACA,SAAO,OAAO,WAAW;AAAA,IAAK,CAAC,cAC9B,mBAAmB,WAAW,QAAQ;AAAA,EACvC;AACD;AAEA,SAAS,qBACR,QAC0D;AAC1D,MAAI,OAAO,WAAW,YAAY,OAAO,WAAW,GAAG;AACtD,WAAO;AAAA,EACR;AACA,MAAI;AACJ,QAAM,aAAuB,CAAC;AAE9B,aAAW,QAAQ,OAAO,MAAM,GAAG,GAAG;AACrC,UAAM,KAAK,KAAK,QAAQ,GAAG;AAC3B,QAAI,OAAO,IAAI;AACd;AAAA,IACD;AACA,UAAM,MAAM,KAAK,MAAM,GAAG,EAAE,EAAE,KAAK;AACnC,UAAM,QAAQ,KAAK,MAAM,KAAK,CAAC,EAAE,KAAK;AACtC,QAAI,QAAQ,KAAK;AAChB,YAAM,KAAK,OAAO,KAAK;AACvB,UAAI,OAAO,UAAU,EAAE,GAAG;AACzB,oBAAY;AAAA,MACb;AAAA,IACD,WAAW,QAAQ,QAAQ,eAAe,KAAK,KAAK,GAAG;AACtD,iBAAW,KAAK,MAAM,YAAY,CAAC;AAAA,IACpC;AAAA,EACD;AAEA,MAAI,cAAc,UAAa,WAAW,WAAW,GAAG;AACvD,WAAO;AAAA,EACR;AACA,SAAO,EAAE,WAAW,WAAW;AAChC;AAEA,eAAe,cAAc,QAAgB,SAAkC;AAC9E,QAAM,UAAU,IAAI,YAAY;AAChC,QAAM,MAAM,MAAM,OAAO,OAAO;AAAA,IAC/B;AAAA,IACA,QAAQ,OAAO,MAAM;AAAA,IACrB,EAAE,MAAM,QAAQ,MAAM,UAAU;AAAA,IAChC;AAAA,IACA,CAAC,MAAM;AAAA,EACR;AACA,QAAM,YAAY,MAAM,OAAO,OAAO;AAAA,IACrC;AAAA,IACA;AAAA,IACA,QAAQ,OAAO,OAAO;AAAA,EACvB;AACA,SAAO,MAAM;AAAA,IAAK,IAAI,WAAW,SAAS;AAAA,IAAG,CAAC,SAC7C,KAAK,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG;AAAA,EAClC,EAAE,KAAK,EAAE;AACV;AAEA,SAAS,mBAAmB,GAAW,GAAoB;AAC1D,MAAI,EAAE,WAAW,EAAE,QAAQ;AAC1B,WAAO;AAAA,EACR;AACA,MAAI,OAAO;AACX,WAAS,IAAI,GAAG,IAAI,EAAE,QAAQ,KAAK;AAClC,YAAQ,EAAE,WAAW,CAAC,IAAI,EAAE,WAAW,CAAC;AAAA,EACzC;AACA,SAAO,SAAS;AACjB;","names":[]}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SDK_HEADER_NAME,
|
|
3
|
+
SDK_HEADER_VALUE,
|
|
4
|
+
graphqlUrl,
|
|
5
|
+
staticAuthHeaders
|
|
6
|
+
} from "./chunk-U6D5LGNF.js";
|
|
7
|
+
|
|
8
|
+
// src/graphql/index.ts
|
|
9
|
+
import {
|
|
10
|
+
SuiGraphQLClient
|
|
11
|
+
} from "@mysten/sui/graphql";
|
|
12
|
+
import {
|
|
13
|
+
SuiGraphQLClient as SuiGraphQLClient2
|
|
14
|
+
} from "@mysten/sui/graphql";
|
|
15
|
+
function createInodraGraphQLClient(options) {
|
|
16
|
+
const network = options.network ?? "mainnet";
|
|
17
|
+
return new SuiGraphQLClient({
|
|
18
|
+
network,
|
|
19
|
+
url: options.url ?? graphqlUrl(network),
|
|
20
|
+
headers: {
|
|
21
|
+
...staticAuthHeaders(options, "createInodraGraphQLClient"),
|
|
22
|
+
[SDK_HEADER_NAME]: SDK_HEADER_VALUE,
|
|
23
|
+
...options.headers
|
|
24
|
+
},
|
|
25
|
+
queries: options.queries
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export {
|
|
30
|
+
createInodraGraphQLClient,
|
|
31
|
+
SuiGraphQLClient2 as SuiGraphQLClient
|
|
32
|
+
};
|
|
33
|
+
//# sourceMappingURL=chunk-74XGJJJI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/graphql/index.ts"],"sourcesContent":["import {\n\ttype GraphQLDocument,\n\tSuiGraphQLClient,\n} from \"@mysten/sui/graphql\";\nimport { type InodraAuth, staticAuthHeaders } from \"../core/auth.js\";\nimport { graphqlUrl, type InodraNetwork } from \"../core/network.js\";\nimport { SDK_HEADER_NAME, SDK_HEADER_VALUE } from \"../core/version.js\";\n\nexport interface InodraGraphQLClientOptions<\n\tQueries extends Record<string, GraphQLDocument>,\n> {\n\t/** Credentials: an API key string, {apiKey}, or {sessionToken}. */\n\tauth?: InodraAuth;\n\t/** Shorthand for `auth: { apiKey }`. */\n\tapiKey?: string;\n\t/** Defaults to \"mainnet\". Ignored when url is set. */\n\tnetwork?: InodraNetwork;\n\t/** Overrides the network-derived GraphQL endpoint. */\n\turl?: string;\n\t/** Extra headers sent with every request. */\n\theaders?: Record<string, string>;\n\t/** Named, typed queries usable via client.execute(name, ...). */\n\tqueries?: Queries;\n}\n\n/**\n * GraphQL client for Inodra's Sui GraphQL endpoint.\n *\n * Returns a fully featured `SuiGraphQLClient` from @mysten/sui,\n * authenticated against Inodra.\n *\n * ```ts\n * import { graphql } from \"@mysten/sui/graphql/schemas/latest\";\n *\n * const client = createInodraGraphQLClient({ apiKey });\n * const result = await client.query({\n * query: graphql(`query { chainIdentifier }`),\n * });\n * ```\n */\nexport function createInodraGraphQLClient<\n\tQueries extends Record<string, GraphQLDocument> = Record<\n\t\tstring,\n\t\tGraphQLDocument\n\t>,\n>(options: InodraGraphQLClientOptions<Queries>): SuiGraphQLClient<Queries> {\n\tconst network = options.network ?? \"mainnet\";\n\treturn new SuiGraphQLClient({\n\t\tnetwork,\n\t\turl: options.url ?? graphqlUrl(network),\n\t\theaders: {\n\t\t\t...staticAuthHeaders(options, \"createInodraGraphQLClient\"),\n\t\t\t[SDK_HEADER_NAME]: SDK_HEADER_VALUE,\n\t\t\t...options.headers,\n\t\t},\n\t\tqueries: options.queries,\n\t});\n}\n\nexport {\n\ttype GraphQLDocument,\n\ttype GraphQLQueryOptions,\n\ttype GraphQLQueryResult,\n\tSuiGraphQLClient,\n} from \"@mysten/sui/graphql\";\n"],"mappings":";;;;;;;;AAAA;AAAA,EAEC;AAAA,OACM;AAwDP;AAAA,EAIC,oBAAAA;AAAA,OACM;AAxBA,SAAS,0BAKd,SAAyE;AAC1E,QAAM,UAAU,QAAQ,WAAW;AACnC,SAAO,IAAI,iBAAiB;AAAA,IAC3B;AAAA,IACA,KAAK,QAAQ,OAAO,WAAW,OAAO;AAAA,IACtC,SAAS;AAAA,MACR,GAAG,kBAAkB,SAAS,2BAA2B;AAAA,MACzD,CAAC,eAAe,GAAG;AAAA,MACnB,GAAG,QAAQ;AAAA,IACZ;AAAA,IACA,SAAS,QAAQ;AAAA,EAClB,CAAC;AACF;","names":["SuiGraphQLClient"]}
|