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