@dipansrimany/mlink-sdk 0.3.1 → 0.4.2
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/adapters/express.d.mts +2 -2
- package/dist/adapters/express.d.ts +2 -2
- package/dist/adapters/express.js +117 -10
- package/dist/adapters/express.js.map +1 -1
- package/dist/adapters/express.mjs +117 -10
- package/dist/adapters/express.mjs.map +1 -1
- package/dist/adapters/next.d.mts +2 -2
- package/dist/adapters/next.d.ts +2 -2
- package/dist/adapters/next.js +117 -10
- package/dist/adapters/next.js.map +1 -1
- package/dist/adapters/next.mjs +117 -10
- package/dist/adapters/next.mjs.map +1 -1
- package/dist/builders-CLqoe2Kx.d.ts +245 -0
- package/dist/builders-pKj4NiIj.d.mts +245 -0
- package/dist/index.d.mts +1131 -24
- package/dist/index.d.ts +1131 -24
- package/dist/index.js +346 -20
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +317 -21
- package/dist/index.mjs.map +1 -1
- package/dist/react/index.d.mts +43 -5
- package/dist/react/index.d.ts +43 -5
- package/dist/react/index.js +522 -37
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.mjs +522 -37
- package/dist/react/index.mjs.map +1 -1
- package/dist/styles.css +248 -0
- package/dist/types-DD9rJ58Y.d.mts +214 -0
- package/dist/types-DD9rJ58Y.d.ts +214 -0
- package/package.json +2 -2
- package/dist/builders-CJNt88dM.d.ts +0 -19
- package/dist/builders-CN5ijFpW.d.mts +0 -19
- package/dist/types-CAnUIaVe.d.mts +0 -68
- package/dist/types-CAnUIaVe.d.ts +0 -68
package/dist/react/index.js
CHANGED
|
@@ -18,6 +18,8 @@ var MANTLE_SEPOLIA = {
|
|
|
18
18
|
};
|
|
19
19
|
var DEFAULT_CHAIN = MANTLE_SEPOLIA;
|
|
20
20
|
var ACTION_QUERY_PARAM = "action";
|
|
21
|
+
var REGISTRY_URL = "https://mlinks-fe.vercel.app";
|
|
22
|
+
var REGISTRY_VALIDATE_ENDPOINT = "/api/registry/validate";
|
|
21
23
|
|
|
22
24
|
// src/utils.ts
|
|
23
25
|
function parseBlinkUrl(blinkUrl) {
|
|
@@ -41,18 +43,113 @@ var ActionButtonSchema = zod.z.object({
|
|
|
41
43
|
placeholder: zod.z.string().optional(),
|
|
42
44
|
disabled: zod.z.boolean().optional()
|
|
43
45
|
});
|
|
46
|
+
zod.z.enum([
|
|
47
|
+
"text",
|
|
48
|
+
"number",
|
|
49
|
+
"email",
|
|
50
|
+
"url",
|
|
51
|
+
"date",
|
|
52
|
+
"datetime-local",
|
|
53
|
+
"textarea",
|
|
54
|
+
"select",
|
|
55
|
+
"radio",
|
|
56
|
+
"checkbox",
|
|
57
|
+
"address",
|
|
58
|
+
"token",
|
|
59
|
+
"amount"
|
|
60
|
+
]);
|
|
61
|
+
var ActionParameterOptionSchema = zod.z.object({
|
|
62
|
+
label: zod.z.string().min(1),
|
|
63
|
+
value: zod.z.string(),
|
|
64
|
+
selected: zod.z.boolean().optional()
|
|
65
|
+
});
|
|
66
|
+
var ActionParameterBaseSchema = zod.z.object({
|
|
67
|
+
name: zod.z.string().min(1).max(50),
|
|
68
|
+
label: zod.z.string().max(100).optional(),
|
|
69
|
+
required: zod.z.boolean().optional(),
|
|
70
|
+
pattern: zod.z.string().optional(),
|
|
71
|
+
patternDescription: zod.z.string().optional(),
|
|
72
|
+
min: zod.z.union([zod.z.string(), zod.z.number()]).optional(),
|
|
73
|
+
max: zod.z.union([zod.z.string(), zod.z.number()]).optional()
|
|
74
|
+
});
|
|
75
|
+
var ActionParameterSchema = ActionParameterBaseSchema.extend({
|
|
76
|
+
type: zod.z.enum([
|
|
77
|
+
"text",
|
|
78
|
+
"number",
|
|
79
|
+
"email",
|
|
80
|
+
"url",
|
|
81
|
+
"date",
|
|
82
|
+
"datetime-local",
|
|
83
|
+
"textarea",
|
|
84
|
+
"address",
|
|
85
|
+
"token",
|
|
86
|
+
"amount"
|
|
87
|
+
]).optional()
|
|
88
|
+
});
|
|
89
|
+
var ActionParameterSelectableSchema = ActionParameterBaseSchema.extend({
|
|
90
|
+
type: zod.z.enum(["select", "radio", "checkbox"]),
|
|
91
|
+
options: zod.z.array(ActionParameterOptionSchema).min(1)
|
|
92
|
+
});
|
|
93
|
+
var TypedActionParameterSchema = zod.z.union([
|
|
94
|
+
ActionParameterSelectableSchema,
|
|
95
|
+
ActionParameterSchema
|
|
96
|
+
]);
|
|
97
|
+
var LinkedActionTypeSchema = zod.z.enum(["transaction", "post", "external-link"]);
|
|
98
|
+
var LinkedActionSchema = zod.z.object({
|
|
99
|
+
type: LinkedActionTypeSchema.optional(),
|
|
100
|
+
href: zod.z.string().min(1),
|
|
101
|
+
label: zod.z.string().min(1).max(50),
|
|
102
|
+
disabled: zod.z.boolean().optional(),
|
|
103
|
+
parameters: zod.z.array(TypedActionParameterSchema).optional()
|
|
104
|
+
});
|
|
105
|
+
var ActionLinksSchema = zod.z.object({
|
|
106
|
+
actions: zod.z.array(LinkedActionSchema).min(1)
|
|
107
|
+
});
|
|
108
|
+
var PostNextActionLinkSchema = zod.z.object({
|
|
109
|
+
type: zod.z.literal("post"),
|
|
110
|
+
href: zod.z.string().min(1)
|
|
111
|
+
});
|
|
112
|
+
var InlineNextActionLinkSchema = zod.z.object({
|
|
113
|
+
type: zod.z.literal("inline"),
|
|
114
|
+
action: zod.z.lazy(() => ActionMetadataSchema)
|
|
115
|
+
});
|
|
116
|
+
var NextActionLinkSchema = zod.z.union([
|
|
117
|
+
PostNextActionLinkSchema,
|
|
118
|
+
InlineNextActionLinkSchema
|
|
119
|
+
]);
|
|
120
|
+
var iconSchema = zod.z.string().refine(
|
|
121
|
+
(val) => {
|
|
122
|
+
if (val.startsWith("http://") || val.startsWith("https://")) return true;
|
|
123
|
+
if (val.startsWith("data:image/")) return true;
|
|
124
|
+
if (val.startsWith("/")) return true;
|
|
125
|
+
return false;
|
|
126
|
+
},
|
|
127
|
+
{ message: "Icon must be a valid URL, base64 data URI, or relative path" }
|
|
128
|
+
);
|
|
44
129
|
var ActionMetadataSchema = zod.z.object({
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
130
|
+
type: zod.z.enum(["action", "completed"]).optional(),
|
|
131
|
+
title: zod.z.string().min(1).max(200),
|
|
132
|
+
icon: iconSchema,
|
|
133
|
+
description: zod.z.string().min(1).max(1e3),
|
|
134
|
+
label: zod.z.string().max(50).optional(),
|
|
135
|
+
// Legacy actions (optional)
|
|
136
|
+
actions: zod.z.array(ActionButtonSchema).optional(),
|
|
137
|
+
// Solana-style linked actions (optional)
|
|
138
|
+
links: ActionLinksSchema.optional(),
|
|
49
139
|
disabled: zod.z.boolean().optional(),
|
|
50
140
|
error: zod.z.object({ message: zod.z.string() }).optional()
|
|
51
|
-
})
|
|
141
|
+
}).refine(
|
|
142
|
+
(data) => {
|
|
143
|
+
return data.actions && data.actions.length > 0 || data.links?.actions && data.links.actions.length > 0;
|
|
144
|
+
},
|
|
145
|
+
{ message: "Must have at least one action or linked action" }
|
|
146
|
+
);
|
|
52
147
|
zod.z.object({
|
|
53
148
|
account: zod.z.string().regex(addressRegex, "Invalid Ethereum address"),
|
|
54
149
|
action: zod.z.string().min(1),
|
|
55
|
-
input: zod.z.string().optional()
|
|
150
|
+
input: zod.z.string().optional(),
|
|
151
|
+
// Solana-style parameter data
|
|
152
|
+
data: zod.z.record(zod.z.union([zod.z.string(), zod.z.array(zod.z.string())])).optional()
|
|
56
153
|
});
|
|
57
154
|
var EVMTransactionSchema = zod.z.object({
|
|
58
155
|
to: zod.z.string().regex(addressRegex, "Invalid to address"),
|
|
@@ -60,10 +157,22 @@ var EVMTransactionSchema = zod.z.object({
|
|
|
60
157
|
data: zod.z.string().regex(hexRegex, "Invalid hex data"),
|
|
61
158
|
chainId: zod.z.number().positive()
|
|
62
159
|
});
|
|
63
|
-
|
|
64
|
-
transaction
|
|
65
|
-
|
|
66
|
-
|
|
160
|
+
zod.z.object({
|
|
161
|
+
// Single transaction (legacy)
|
|
162
|
+
transaction: EVMTransactionSchema.optional(),
|
|
163
|
+
// Multiple transactions (batch)
|
|
164
|
+
transactions: zod.z.array(EVMTransactionSchema).optional(),
|
|
165
|
+
message: zod.z.string().optional(),
|
|
166
|
+
// Action chaining
|
|
167
|
+
links: zod.z.object({
|
|
168
|
+
next: NextActionLinkSchema.optional()
|
|
169
|
+
}).optional()
|
|
170
|
+
}).refine(
|
|
171
|
+
(data) => {
|
|
172
|
+
return data.transaction !== void 0 || data.transactions && data.transactions.length > 0;
|
|
173
|
+
},
|
|
174
|
+
{ message: "Must have either transaction or transactions" }
|
|
175
|
+
);
|
|
67
176
|
function validateActionMetadata(data) {
|
|
68
177
|
const result = ActionMetadataSchema.safeParse(data);
|
|
69
178
|
if (result.success) {
|
|
@@ -71,33 +180,77 @@ function validateActionMetadata(data) {
|
|
|
71
180
|
}
|
|
72
181
|
return { success: false, error: result.error.message };
|
|
73
182
|
}
|
|
74
|
-
function validateTransactionResponse(data) {
|
|
75
|
-
const result = TransactionResponseSchema.safeParse(data);
|
|
76
|
-
if (result.success) {
|
|
77
|
-
return { success: true, data: result.data };
|
|
78
|
-
}
|
|
79
|
-
return { success: false, error: result.error.message };
|
|
80
|
-
}
|
|
81
183
|
|
|
82
184
|
// src/react/useMlink.ts
|
|
83
185
|
var DEFAULT_REFRESH_INTERVAL = 10 * 60 * 1e3;
|
|
84
186
|
function useMlink(url, options = {}) {
|
|
85
|
-
const {
|
|
187
|
+
const {
|
|
188
|
+
refreshInterval = DEFAULT_REFRESH_INTERVAL,
|
|
189
|
+
enabled = true,
|
|
190
|
+
registryUrl = REGISTRY_URL,
|
|
191
|
+
requireRegistration = true,
|
|
192
|
+
allowPending = true,
|
|
193
|
+
allowBlocked = false
|
|
194
|
+
} = options;
|
|
86
195
|
const [status, setStatus] = react.useState("idle");
|
|
87
196
|
const [metadata, setMetadata] = react.useState(null);
|
|
88
197
|
const [error, setError] = react.useState(null);
|
|
198
|
+
const [registration, setRegistration] = react.useState(null);
|
|
89
199
|
const abortControllerRef = react.useRef(null);
|
|
90
200
|
const intervalRef = react.useRef(null);
|
|
91
201
|
const actionUrl = parseBlinkUrl(url) || url;
|
|
202
|
+
const validateRegistration = react.useCallback(async () => {
|
|
203
|
+
if (!actionUrl || !requireRegistration) return null;
|
|
204
|
+
try {
|
|
205
|
+
const validateUrl = `${registryUrl}${REGISTRY_VALIDATE_ENDPOINT}?url=${encodeURIComponent(actionUrl)}`;
|
|
206
|
+
const response = await fetch(validateUrl, {
|
|
207
|
+
method: "GET",
|
|
208
|
+
headers: {
|
|
209
|
+
Accept: "application/json"
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
const data = await response.json();
|
|
213
|
+
return data;
|
|
214
|
+
} catch (err) {
|
|
215
|
+
console.error("Registry validation error:", err);
|
|
216
|
+
return {
|
|
217
|
+
isRegistered: false,
|
|
218
|
+
status: null,
|
|
219
|
+
error: "Unable to validate against registry. Please try again later."
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
}, [actionUrl, registryUrl, requireRegistration]);
|
|
92
223
|
const fetchMetadata = react.useCallback(async () => {
|
|
93
224
|
if (!actionUrl || !enabled) return;
|
|
94
225
|
if (abortControllerRef.current) {
|
|
95
226
|
abortControllerRef.current.abort();
|
|
96
227
|
}
|
|
97
228
|
abortControllerRef.current = new AbortController();
|
|
98
|
-
setStatus("
|
|
229
|
+
setStatus("validating");
|
|
99
230
|
setError(null);
|
|
100
231
|
try {
|
|
232
|
+
if (requireRegistration) {
|
|
233
|
+
const registrationResult = await validateRegistration();
|
|
234
|
+
setRegistration(registrationResult);
|
|
235
|
+
if (registrationResult) {
|
|
236
|
+
if (!registrationResult.isRegistered) {
|
|
237
|
+
setError(registrationResult.error || "This MLink is not registered.");
|
|
238
|
+
setStatus("unregistered");
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
if (registrationResult.status === "blocked" && !allowBlocked) {
|
|
242
|
+
setError("This MLink has been blocked for policy violations.");
|
|
243
|
+
setStatus("blocked");
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
if (registrationResult.status === "pending" && !allowPending) {
|
|
247
|
+
setError("This MLink is pending review and not yet available.");
|
|
248
|
+
setStatus("error");
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
setStatus("loading");
|
|
101
254
|
const response = await fetch(actionUrl, {
|
|
102
255
|
method: "GET",
|
|
103
256
|
headers: {
|
|
@@ -123,7 +276,7 @@ function useMlink(url, options = {}) {
|
|
|
123
276
|
setError(errorMessage);
|
|
124
277
|
setStatus("error");
|
|
125
278
|
}
|
|
126
|
-
}, [actionUrl, enabled]);
|
|
279
|
+
}, [actionUrl, enabled, requireRegistration, validateRegistration, allowBlocked, allowPending]);
|
|
127
280
|
react.useEffect(() => {
|
|
128
281
|
if (enabled) {
|
|
129
282
|
fetchMetadata();
|
|
@@ -150,7 +303,9 @@ function useMlink(url, options = {}) {
|
|
|
150
303
|
metadata,
|
|
151
304
|
error,
|
|
152
305
|
url: actionUrl,
|
|
153
|
-
refresh: fetchMetadata
|
|
306
|
+
refresh: fetchMetadata,
|
|
307
|
+
registration,
|
|
308
|
+
isRegistered: registration?.isRegistered ?? false
|
|
154
309
|
};
|
|
155
310
|
}
|
|
156
311
|
function useExecuteMlink(options) {
|
|
@@ -164,7 +319,7 @@ function useExecuteMlink(options) {
|
|
|
164
319
|
setError(null);
|
|
165
320
|
}, []);
|
|
166
321
|
const execute = react.useCallback(
|
|
167
|
-
async (action, input) => {
|
|
322
|
+
async (action, input, data) => {
|
|
168
323
|
setStatus("executing");
|
|
169
324
|
setError(null);
|
|
170
325
|
setTxHash(null);
|
|
@@ -181,28 +336,33 @@ function useExecuteMlink(options) {
|
|
|
181
336
|
body: JSON.stringify({
|
|
182
337
|
account,
|
|
183
338
|
action,
|
|
184
|
-
input
|
|
339
|
+
input,
|
|
340
|
+
data
|
|
341
|
+
// Include parameter data
|
|
185
342
|
})
|
|
186
343
|
});
|
|
187
344
|
if (!response.ok) {
|
|
188
345
|
const errorData = await response.json().catch(() => ({}));
|
|
189
346
|
throw new Error(
|
|
190
|
-
errorData.message || `HTTP ${response.status}: ${response.statusText}`
|
|
347
|
+
errorData.error?.message || errorData.message || `HTTP ${response.status}: ${response.statusText}`
|
|
191
348
|
);
|
|
192
349
|
}
|
|
193
|
-
const
|
|
194
|
-
const
|
|
195
|
-
if (
|
|
196
|
-
throw new Error(
|
|
350
|
+
const responseData = await response.json();
|
|
351
|
+
const transactions = responseData.transactions ? responseData.transactions : responseData.transaction ? [responseData.transaction] : [];
|
|
352
|
+
if (transactions.length === 0) {
|
|
353
|
+
throw new Error("No transaction returned from action");
|
|
354
|
+
}
|
|
355
|
+
let lastHash = "";
|
|
356
|
+
for (const tx of transactions) {
|
|
357
|
+
lastHash = await adapter.signAndSendTransaction(tx);
|
|
197
358
|
}
|
|
198
|
-
|
|
199
|
-
setTxHash(hash);
|
|
359
|
+
setTxHash(lastHash);
|
|
200
360
|
setStatus("success");
|
|
201
|
-
onSuccess?.(
|
|
361
|
+
onSuccess?.(lastHash, action);
|
|
202
362
|
return {
|
|
203
363
|
success: true,
|
|
204
|
-
txHash:
|
|
205
|
-
message:
|
|
364
|
+
txHash: lastHash,
|
|
365
|
+
message: responseData.message
|
|
206
366
|
};
|
|
207
367
|
} catch (err) {
|
|
208
368
|
const errorMessage = err instanceof Error ? err.message : "Transaction failed";
|
|
@@ -348,6 +508,23 @@ function useMlinkContext() {
|
|
|
348
508
|
}
|
|
349
509
|
return context;
|
|
350
510
|
}
|
|
511
|
+
|
|
512
|
+
// src/builders.ts
|
|
513
|
+
function buildHref(template, params) {
|
|
514
|
+
let result = template;
|
|
515
|
+
for (const [key, value] of Object.entries(params)) {
|
|
516
|
+
const placeholder = `{${key}}`;
|
|
517
|
+
const replacement = Array.isArray(value) ? value.join(",") : value;
|
|
518
|
+
result = result.replace(new RegExp(placeholder, "g"), encodeURIComponent(replacement));
|
|
519
|
+
}
|
|
520
|
+
return result;
|
|
521
|
+
}
|
|
522
|
+
function hasParameters(action) {
|
|
523
|
+
return Boolean(action.parameters && action.parameters.length > 0);
|
|
524
|
+
}
|
|
525
|
+
function isSelectableParam(param) {
|
|
526
|
+
return param.type === "select" || param.type === "radio" || param.type === "checkbox";
|
|
527
|
+
}
|
|
351
528
|
function Mlink({
|
|
352
529
|
url,
|
|
353
530
|
adapter,
|
|
@@ -355,11 +532,20 @@ function Mlink({
|
|
|
355
532
|
onSuccess,
|
|
356
533
|
onError,
|
|
357
534
|
className = "",
|
|
358
|
-
stylePreset = "default"
|
|
535
|
+
stylePreset = "default",
|
|
536
|
+
registryUrl,
|
|
537
|
+
requireRegistration = true,
|
|
538
|
+
allowPending = true,
|
|
539
|
+
allowBlocked = false
|
|
359
540
|
}) {
|
|
360
541
|
const context = useMlinkContext();
|
|
361
542
|
const resolvedTheme = themeProp ? resolveTheme(themeProp) : context.theme;
|
|
362
|
-
const { status: fetchStatus, metadata, error: fetchError } = useMlink(url
|
|
543
|
+
const { status: fetchStatus, metadata, error: fetchError, registration } = useMlink(url, {
|
|
544
|
+
registryUrl,
|
|
545
|
+
requireRegistration,
|
|
546
|
+
allowPending,
|
|
547
|
+
allowBlocked
|
|
548
|
+
});
|
|
363
549
|
const {
|
|
364
550
|
execute,
|
|
365
551
|
status: execStatus,
|
|
@@ -373,9 +559,14 @@ function Mlink({
|
|
|
373
559
|
onError
|
|
374
560
|
});
|
|
375
561
|
const [inputValues, setInputValues] = react.useState({});
|
|
562
|
+
const [paramValues, setParamValues] = react.useState({});
|
|
563
|
+
const [selectedLinkedAction, setSelectedLinkedAction] = react.useState(null);
|
|
376
564
|
const handleInputChange = react.useCallback((actionValue, value) => {
|
|
377
565
|
setInputValues((prev) => ({ ...prev, [actionValue]: value }));
|
|
378
566
|
}, []);
|
|
567
|
+
const handleParamChange = react.useCallback((name, value) => {
|
|
568
|
+
setParamValues((prev) => ({ ...prev, [name]: value }));
|
|
569
|
+
}, []);
|
|
379
570
|
const handleAction = react.useCallback(
|
|
380
571
|
async (action) => {
|
|
381
572
|
const input = action.type === "input" ? inputValues[action.value] : void 0;
|
|
@@ -383,9 +574,55 @@ function Mlink({
|
|
|
383
574
|
},
|
|
384
575
|
[execute, inputValues]
|
|
385
576
|
);
|
|
386
|
-
|
|
577
|
+
const handleLinkedAction = react.useCallback(
|
|
578
|
+
async (action) => {
|
|
579
|
+
if (hasParameters(action)) {
|
|
580
|
+
setSelectedLinkedAction(action);
|
|
581
|
+
const defaults = {};
|
|
582
|
+
action.parameters?.forEach((param) => {
|
|
583
|
+
if (isSelectableParam(param)) {
|
|
584
|
+
const selectedOption = param.options.find((o) => o.selected);
|
|
585
|
+
if (selectedOption) {
|
|
586
|
+
defaults[param.name] = param.type === "checkbox" ? [selectedOption.value] : selectedOption.value;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
});
|
|
590
|
+
setParamValues(defaults);
|
|
591
|
+
} else {
|
|
592
|
+
await execute(action.href, void 0, {});
|
|
593
|
+
}
|
|
594
|
+
},
|
|
595
|
+
[execute]
|
|
596
|
+
);
|
|
597
|
+
const handleExecuteWithParams = react.useCallback(async () => {
|
|
598
|
+
if (!selectedLinkedAction) return;
|
|
599
|
+
const finalHref = buildHref(selectedLinkedAction.href, paramValues);
|
|
600
|
+
await execute(finalHref, void 0, paramValues);
|
|
601
|
+
setSelectedLinkedAction(null);
|
|
602
|
+
setParamValues({});
|
|
603
|
+
}, [selectedLinkedAction, paramValues, execute]);
|
|
604
|
+
const handleCancelParams = react.useCallback(() => {
|
|
605
|
+
setSelectedLinkedAction(null);
|
|
606
|
+
setParamValues({});
|
|
607
|
+
}, []);
|
|
608
|
+
const hasLinkedActions = react.useMemo(() => {
|
|
609
|
+
return metadata?.links?.actions && metadata.links.actions.length > 0;
|
|
610
|
+
}, [metadata]);
|
|
611
|
+
if (fetchStatus === "loading" || fetchStatus === "validating") {
|
|
387
612
|
return /* @__PURE__ */ jsxRuntime.jsx(MlinkContainer, { theme: resolvedTheme, className, preset: stylePreset, children: /* @__PURE__ */ jsxRuntime.jsx(MlinkSkeleton, {}) });
|
|
388
613
|
}
|
|
614
|
+
if (fetchStatus === "unregistered") {
|
|
615
|
+
return /* @__PURE__ */ jsxRuntime.jsx(MlinkContainer, { theme: resolvedTheme, className, preset: stylePreset, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
616
|
+
MlinkUnregistered,
|
|
617
|
+
{
|
|
618
|
+
message: fetchError || "This MLink is not registered.",
|
|
619
|
+
registryUrl
|
|
620
|
+
}
|
|
621
|
+
) });
|
|
622
|
+
}
|
|
623
|
+
if (fetchStatus === "blocked") {
|
|
624
|
+
return /* @__PURE__ */ jsxRuntime.jsx(MlinkContainer, { theme: resolvedTheme, className, preset: stylePreset, children: /* @__PURE__ */ jsxRuntime.jsx(MlinkBlocked, { message: fetchError || "This MLink has been blocked." }) });
|
|
625
|
+
}
|
|
389
626
|
if (fetchStatus === "error" || !metadata) {
|
|
390
627
|
return /* @__PURE__ */ jsxRuntime.jsx(MlinkContainer, { theme: resolvedTheme, className, preset: stylePreset, children: /* @__PURE__ */ jsxRuntime.jsx(MlinkError, { message: fetchError || "Failed to load action" }) });
|
|
391
628
|
}
|
|
@@ -399,6 +636,45 @@ function Mlink({
|
|
|
399
636
|
}
|
|
400
637
|
) });
|
|
401
638
|
}
|
|
639
|
+
if (selectedLinkedAction && hasParameters(selectedLinkedAction)) {
|
|
640
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(MlinkContainer, { theme: resolvedTheme, className, preset: stylePreset, children: [
|
|
641
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mlink-content", children: [
|
|
642
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "mlink-title", children: selectedLinkedAction.label }),
|
|
643
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mlink-description", children: "Fill in the details below" })
|
|
644
|
+
] }),
|
|
645
|
+
execError && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mlink-error-banner", children: execError }),
|
|
646
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "mlink-params", children: selectedLinkedAction.parameters?.map((param) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
647
|
+
ParameterInput,
|
|
648
|
+
{
|
|
649
|
+
param,
|
|
650
|
+
value: paramValues[param.name] || "",
|
|
651
|
+
onChange: (value) => handleParamChange(param.name, value),
|
|
652
|
+
disabled: execStatus === "executing"
|
|
653
|
+
},
|
|
654
|
+
param.name
|
|
655
|
+
)) }),
|
|
656
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mlink-actions", children: [
|
|
657
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
658
|
+
"button",
|
|
659
|
+
{
|
|
660
|
+
className: "mlink-button",
|
|
661
|
+
onClick: handleExecuteWithParams,
|
|
662
|
+
disabled: execStatus === "executing",
|
|
663
|
+
children: execStatus === "executing" ? /* @__PURE__ */ jsxRuntime.jsx(MlinkSpinner, {}) : selectedLinkedAction.label
|
|
664
|
+
}
|
|
665
|
+
),
|
|
666
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
667
|
+
"button",
|
|
668
|
+
{
|
|
669
|
+
className: "mlink-button mlink-button-secondary",
|
|
670
|
+
onClick: handleCancelParams,
|
|
671
|
+
disabled: execStatus === "executing",
|
|
672
|
+
children: "Back"
|
|
673
|
+
}
|
|
674
|
+
)
|
|
675
|
+
] })
|
|
676
|
+
] });
|
|
677
|
+
}
|
|
402
678
|
return /* @__PURE__ */ jsxRuntime.jsxs(MlinkContainer, { theme: resolvedTheme, className, preset: stylePreset, children: [
|
|
403
679
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "mlink-icon", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: metadata.icon, alt: metadata.title }) }),
|
|
404
680
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mlink-content", children: [
|
|
@@ -406,7 +682,29 @@ function Mlink({
|
|
|
406
682
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mlink-description", children: metadata.description })
|
|
407
683
|
] }),
|
|
408
684
|
execError && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mlink-error-banner", children: execError }),
|
|
409
|
-
/* @__PURE__ */ jsxRuntime.
|
|
685
|
+
hasLinkedActions && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mlink-actions", children: [
|
|
686
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "mlink-quick-actions", children: metadata.links.actions.filter((a) => !hasParameters(a)).map((action, index) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
687
|
+
"button",
|
|
688
|
+
{
|
|
689
|
+
className: "mlink-button mlink-button-quick",
|
|
690
|
+
onClick: () => handleLinkedAction(action),
|
|
691
|
+
disabled: metadata.disabled || action.disabled || execStatus === "executing",
|
|
692
|
+
children: execStatus === "executing" ? /* @__PURE__ */ jsxRuntime.jsx(MlinkSpinner, {}) : action.label
|
|
693
|
+
},
|
|
694
|
+
`${action.href}-${index}`
|
|
695
|
+
)) }),
|
|
696
|
+
metadata.links.actions.filter((a) => hasParameters(a)).map((action, index) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
697
|
+
"button",
|
|
698
|
+
{
|
|
699
|
+
className: "mlink-button mlink-button-custom",
|
|
700
|
+
onClick: () => handleLinkedAction(action),
|
|
701
|
+
disabled: metadata.disabled || action.disabled || execStatus === "executing",
|
|
702
|
+
children: action.label
|
|
703
|
+
},
|
|
704
|
+
`param-${action.href}-${index}`
|
|
705
|
+
))
|
|
706
|
+
] }),
|
|
707
|
+
!hasLinkedActions && metadata.actions && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mlink-actions", children: metadata.actions.map((action, index) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
410
708
|
ActionButtonComponent,
|
|
411
709
|
{
|
|
412
710
|
action,
|
|
@@ -420,6 +718,161 @@ function Mlink({
|
|
|
420
718
|
)) })
|
|
421
719
|
] });
|
|
422
720
|
}
|
|
721
|
+
function ParameterInput({ param, value, onChange, disabled }) {
|
|
722
|
+
const strValue = Array.isArray(value) ? value.join(",") : value;
|
|
723
|
+
if (isSelectableParam(param)) {
|
|
724
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
725
|
+
SelectableParamInput,
|
|
726
|
+
{
|
|
727
|
+
param,
|
|
728
|
+
value,
|
|
729
|
+
onChange,
|
|
730
|
+
disabled
|
|
731
|
+
}
|
|
732
|
+
);
|
|
733
|
+
}
|
|
734
|
+
const inputType = getInputType(param.type);
|
|
735
|
+
if (param.type === "textarea") {
|
|
736
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mlink-param-group", children: [
|
|
737
|
+
param.label && /* @__PURE__ */ jsxRuntime.jsx("label", { className: "mlink-param-label", children: param.label }),
|
|
738
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
739
|
+
"textarea",
|
|
740
|
+
{
|
|
741
|
+
className: "mlink-textarea",
|
|
742
|
+
value: strValue,
|
|
743
|
+
onChange: (e) => onChange(e.target.value),
|
|
744
|
+
disabled,
|
|
745
|
+
required: param.required
|
|
746
|
+
}
|
|
747
|
+
)
|
|
748
|
+
] });
|
|
749
|
+
}
|
|
750
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mlink-param-group", children: [
|
|
751
|
+
param.label && /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "mlink-param-label", children: [
|
|
752
|
+
param.label,
|
|
753
|
+
param.required && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mlink-required", children: "*" })
|
|
754
|
+
] }),
|
|
755
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
756
|
+
"input",
|
|
757
|
+
{
|
|
758
|
+
type: inputType,
|
|
759
|
+
className: "mlink-input",
|
|
760
|
+
value: strValue,
|
|
761
|
+
onChange: (e) => onChange(e.target.value),
|
|
762
|
+
disabled,
|
|
763
|
+
required: param.required,
|
|
764
|
+
pattern: param.pattern,
|
|
765
|
+
min: param.min?.toString(),
|
|
766
|
+
max: param.max?.toString(),
|
|
767
|
+
placeholder: param.label || param.name
|
|
768
|
+
}
|
|
769
|
+
),
|
|
770
|
+
param.patternDescription && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mlink-param-hint", children: param.patternDescription })
|
|
771
|
+
] });
|
|
772
|
+
}
|
|
773
|
+
function SelectableParamInput({
|
|
774
|
+
param,
|
|
775
|
+
value,
|
|
776
|
+
onChange,
|
|
777
|
+
disabled
|
|
778
|
+
}) {
|
|
779
|
+
const arrayValue = Array.isArray(value) ? value : value ? [value] : [];
|
|
780
|
+
if (param.type === "select") {
|
|
781
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mlink-param-group", children: [
|
|
782
|
+
param.label && /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "mlink-param-label", children: [
|
|
783
|
+
param.label,
|
|
784
|
+
param.required && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mlink-required", children: "*" })
|
|
785
|
+
] }),
|
|
786
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
787
|
+
"select",
|
|
788
|
+
{
|
|
789
|
+
className: "mlink-select",
|
|
790
|
+
value: arrayValue[0] || "",
|
|
791
|
+
onChange: (e) => onChange(e.target.value),
|
|
792
|
+
disabled,
|
|
793
|
+
required: param.required,
|
|
794
|
+
children: [
|
|
795
|
+
/* @__PURE__ */ jsxRuntime.jsxs("option", { value: "", children: [
|
|
796
|
+
"Select ",
|
|
797
|
+
param.label || param.name
|
|
798
|
+
] }),
|
|
799
|
+
param.options.map((opt) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: opt.value, children: opt.label }, opt.value))
|
|
800
|
+
]
|
|
801
|
+
}
|
|
802
|
+
)
|
|
803
|
+
] });
|
|
804
|
+
}
|
|
805
|
+
if (param.type === "radio") {
|
|
806
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mlink-param-group", children: [
|
|
807
|
+
param.label && /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "mlink-param-label", children: [
|
|
808
|
+
param.label,
|
|
809
|
+
param.required && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mlink-required", children: "*" })
|
|
810
|
+
] }),
|
|
811
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "mlink-radio-group", children: param.options.map((opt) => /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "mlink-radio-label", children: [
|
|
812
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
813
|
+
"input",
|
|
814
|
+
{
|
|
815
|
+
type: "radio",
|
|
816
|
+
name: param.name,
|
|
817
|
+
value: opt.value,
|
|
818
|
+
checked: arrayValue[0] === opt.value,
|
|
819
|
+
onChange: (e) => onChange(e.target.value),
|
|
820
|
+
disabled
|
|
821
|
+
}
|
|
822
|
+
),
|
|
823
|
+
opt.label
|
|
824
|
+
] }, opt.value)) })
|
|
825
|
+
] });
|
|
826
|
+
}
|
|
827
|
+
if (param.type === "checkbox") {
|
|
828
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mlink-param-group", children: [
|
|
829
|
+
param.label && /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "mlink-param-label", children: [
|
|
830
|
+
param.label,
|
|
831
|
+
param.required && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mlink-required", children: "*" })
|
|
832
|
+
] }),
|
|
833
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "mlink-checkbox-group", children: param.options.map((opt) => /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "mlink-checkbox-label", children: [
|
|
834
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
835
|
+
"input",
|
|
836
|
+
{
|
|
837
|
+
type: "checkbox",
|
|
838
|
+
value: opt.value,
|
|
839
|
+
checked: arrayValue.includes(opt.value),
|
|
840
|
+
onChange: (e) => {
|
|
841
|
+
if (e.target.checked) {
|
|
842
|
+
onChange([...arrayValue, opt.value]);
|
|
843
|
+
} else {
|
|
844
|
+
onChange(arrayValue.filter((v) => v !== opt.value));
|
|
845
|
+
}
|
|
846
|
+
},
|
|
847
|
+
disabled
|
|
848
|
+
}
|
|
849
|
+
),
|
|
850
|
+
opt.label
|
|
851
|
+
] }, opt.value)) })
|
|
852
|
+
] });
|
|
853
|
+
}
|
|
854
|
+
return null;
|
|
855
|
+
}
|
|
856
|
+
function getInputType(paramType) {
|
|
857
|
+
switch (paramType) {
|
|
858
|
+
case "number":
|
|
859
|
+
case "amount":
|
|
860
|
+
return "number";
|
|
861
|
+
case "email":
|
|
862
|
+
return "email";
|
|
863
|
+
case "url":
|
|
864
|
+
return "url";
|
|
865
|
+
case "date":
|
|
866
|
+
return "date";
|
|
867
|
+
case "datetime-local":
|
|
868
|
+
return "datetime-local";
|
|
869
|
+
case "address":
|
|
870
|
+
case "token":
|
|
871
|
+
case "text":
|
|
872
|
+
default:
|
|
873
|
+
return "text";
|
|
874
|
+
}
|
|
875
|
+
}
|
|
423
876
|
function MlinkContainer({ theme, className, preset, children }) {
|
|
424
877
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
425
878
|
"div",
|
|
@@ -446,6 +899,38 @@ function MlinkError({ message }) {
|
|
|
446
899
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mlink-error-message", children: message })
|
|
447
900
|
] });
|
|
448
901
|
}
|
|
902
|
+
function MlinkUnregistered({ message, registryUrl }) {
|
|
903
|
+
const registerUrl = registryUrl ? `${registryUrl}/dashboard/register` : "https://mlinks-fe.vercel.app/dashboard/register";
|
|
904
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mlink-unregistered", children: [
|
|
905
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "mlink-unregistered-icon", children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "48", height: "48", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
|
|
906
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "10" }),
|
|
907
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "12", y1: "8", x2: "12", y2: "12" }),
|
|
908
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "12", y1: "16", x2: "12.01", y2: "16" })
|
|
909
|
+
] }) }),
|
|
910
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "mlink-unregistered-title", children: "Unregistered MLink" }),
|
|
911
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mlink-unregistered-message", children: message }),
|
|
912
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
913
|
+
"a",
|
|
914
|
+
{
|
|
915
|
+
href: registerUrl,
|
|
916
|
+
target: "_blank",
|
|
917
|
+
rel: "noopener noreferrer",
|
|
918
|
+
className: "mlink-button",
|
|
919
|
+
children: "Register MLink"
|
|
920
|
+
}
|
|
921
|
+
)
|
|
922
|
+
] });
|
|
923
|
+
}
|
|
924
|
+
function MlinkBlocked({ message }) {
|
|
925
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mlink-blocked", children: [
|
|
926
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "mlink-blocked-icon", children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "48", height: "48", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
|
|
927
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "10" }),
|
|
928
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "4.93", y1: "4.93", x2: "19.07", y2: "19.07" })
|
|
929
|
+
] }) }),
|
|
930
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "mlink-blocked-title", children: "Blocked MLink" }),
|
|
931
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mlink-blocked-message", children: message })
|
|
932
|
+
] });
|
|
933
|
+
}
|
|
449
934
|
function MlinkSuccess({ message, txHash, onReset }) {
|
|
450
935
|
const shortHash = `${txHash.slice(0, 10)}...${txHash.slice(-8)}`;
|
|
451
936
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mlink-success", children: [
|