@lunatest/react 0.1.0 → 0.1.1
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/LunaTestProvider.js +3 -1
- package/dist/bootstrap.d.ts +19 -2
- package/dist/bootstrap.js +75 -22
- package/dist/browser.d.ts +5 -0
- package/dist/browser.js +4 -0
- package/dist/devtools/LunaDevtoolsPanel.d.ts +5 -0
- package/dist/devtools/LunaDevtoolsPanel.js +241 -5
- package/dist/devtools/mount.js +4 -1
- package/dist/hooks/useLunaProvider.js +3 -1
- package/dist/provider-options.d.ts +2 -0
- package/dist/provider-options.js +29 -0
- package/package.json +15 -4
package/dist/LunaTestProvider.js
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import React, { createContext, useMemo, useState } from "react";
|
|
2
2
|
import { createLunaProvider } from "./luna-provider.js";
|
|
3
|
+
import { createProviderOptionsKey } from "./provider-options.js";
|
|
3
4
|
export const LunaTestContext = createContext(null);
|
|
4
5
|
export function LunaTestProvider(props) {
|
|
5
6
|
const [scenarioId, setScenarioId] = useState(props.initialScenarioId);
|
|
7
|
+
const optionsKey = createProviderOptionsKey(props.options);
|
|
6
8
|
const provider = useMemo(() => {
|
|
7
9
|
if (props.provider) {
|
|
8
10
|
return props.provider;
|
|
9
11
|
}
|
|
10
12
|
return createLunaProvider(props.options ?? {});
|
|
11
|
-
}, [props.provider, props.options]);
|
|
13
|
+
}, [props.provider, optionsKey, props.options?.callHandler]);
|
|
12
14
|
const value = useMemo(() => ({
|
|
13
15
|
provider,
|
|
14
16
|
scenarioId,
|
package/dist/bootstrap.d.ts
CHANGED
|
@@ -1,15 +1,32 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type LunaWalletAssetState, type LunaWalletPermission } from "@lunatest/contracts";
|
|
2
|
+
import type { LuaConfig } from "@lunatest/core/browser";
|
|
3
|
+
import { type PresetRegistry, type ProjectPresetSources } from "@lunatest/core/browser";
|
|
2
4
|
import { type LunaRuntimeInterceptConfig } from "@lunatest/runtime-intercept";
|
|
3
5
|
export type LunaBootstrapOptions = {
|
|
6
|
+
enable?: boolean;
|
|
4
7
|
source?: string | URL;
|
|
5
8
|
nodeEnv?: string;
|
|
6
9
|
mountDevtools?: boolean;
|
|
7
10
|
devtoolsTargetId?: string;
|
|
11
|
+
presetRegistry?: PresetRegistry;
|
|
12
|
+
projectPresetSources?: ProjectPresetSources;
|
|
13
|
+
protocolPresetId?: string;
|
|
14
|
+
protocolPresetParams?: Record<string, unknown>;
|
|
15
|
+
walletPresetId?: string;
|
|
16
|
+
walletPresetParams?: Record<string, unknown>;
|
|
17
|
+
walletFallbackMode?: "off" | "manual-toggle";
|
|
18
|
+
walletPreset?: {
|
|
19
|
+
address: string;
|
|
20
|
+
chainId?: string;
|
|
21
|
+
permissions?: Array<LunaWalletPermission | string>;
|
|
22
|
+
assets?: Partial<LunaWalletAssetState>;
|
|
23
|
+
};
|
|
8
24
|
configOverride?: Partial<LunaRuntimeInterceptConfig>;
|
|
9
25
|
};
|
|
10
26
|
export type LunaBootstrapResult = {
|
|
11
27
|
enabled: boolean;
|
|
28
|
+
configLoaded: boolean;
|
|
12
29
|
unmountDevtools?: () => void;
|
|
13
|
-
config
|
|
30
|
+
config?: LuaConfig;
|
|
14
31
|
};
|
|
15
32
|
export declare function bootstrapLunaRuntime(options?: LunaBootstrapOptions): Promise<LunaBootstrapResult>;
|
package/dist/bootstrap.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { createLunaWalletAssetState, normalizeWalletPermissions, } from "@lunatest/contracts";
|
|
2
|
+
import { loadLunaConfig, createPresetRegistry, materializeProtocolPreset, materializeWalletPreset, } from "@lunatest/core/browser";
|
|
3
|
+
import { applyInterceptState, disableLunaRuntimeIntercept, enableLunaRuntimeIntercept, resolveEnabled, setWalletSession, setRouteMocks, } from "@lunatest/runtime-intercept";
|
|
3
4
|
import { mountLunaDevtools } from "./devtools/mount.js";
|
|
4
5
|
import { resolveNodeEnv } from "./node-env.js";
|
|
5
|
-
function toRuntimeConfig(config, configOverride) {
|
|
6
|
+
function toRuntimeConfig(config, enable, configOverride) {
|
|
6
7
|
return {
|
|
8
|
+
enable,
|
|
7
9
|
...configOverride,
|
|
8
10
|
intercept: {
|
|
9
11
|
...configOverride?.intercept,
|
|
@@ -15,34 +17,85 @@ function toRuntimeConfig(config, configOverride) {
|
|
|
15
17
|
},
|
|
16
18
|
};
|
|
17
19
|
}
|
|
20
|
+
function resolveBootstrapEnabled(options, nodeEnv) {
|
|
21
|
+
if (typeof options.enable === "boolean") {
|
|
22
|
+
return options.enable;
|
|
23
|
+
}
|
|
24
|
+
return resolveEnabled({
|
|
25
|
+
enable: options.configOverride?.enable,
|
|
26
|
+
}, nodeEnv);
|
|
27
|
+
}
|
|
18
28
|
export async function bootstrapLunaRuntime(options = {}) {
|
|
19
|
-
const config = await loadLunaConfig(options.source ?? "./lunatest.lua");
|
|
20
29
|
const nodeEnv = resolveNodeEnv(options.nodeEnv);
|
|
21
|
-
const
|
|
30
|
+
const bootstrapEnabled = resolveBootstrapEnabled(options, nodeEnv);
|
|
31
|
+
if (!bootstrapEnabled) {
|
|
32
|
+
return {
|
|
33
|
+
enabled: false,
|
|
34
|
+
configLoaded: false,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
const config = await loadLunaConfig(options.source ?? "./lunatest.lua");
|
|
38
|
+
const presetRegistry = options.presetRegistry ??
|
|
39
|
+
createPresetRegistry({
|
|
40
|
+
projectSources: options.projectPresetSources,
|
|
41
|
+
});
|
|
42
|
+
const runtimeConfig = toRuntimeConfig(config, options.enable ?? options.configOverride?.enable, options.configOverride);
|
|
22
43
|
const enabled = enableLunaRuntimeIntercept(runtimeConfig, nodeEnv);
|
|
23
44
|
if (!enabled) {
|
|
24
45
|
return {
|
|
25
46
|
enabled: false,
|
|
26
47
|
config,
|
|
48
|
+
configLoaded: true,
|
|
27
49
|
};
|
|
28
50
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
51
|
+
try {
|
|
52
|
+
const routeMocks = options.configOverride?.intercept?.routes ?? config.intercept?.routes ?? [];
|
|
53
|
+
setRouteMocks(routeMocks);
|
|
54
|
+
if (config.given) {
|
|
55
|
+
applyInterceptState(config.given);
|
|
56
|
+
}
|
|
57
|
+
if (config.intercept?.state) {
|
|
58
|
+
applyInterceptState(config.intercept.state);
|
|
59
|
+
}
|
|
60
|
+
if (options.protocolPresetId) {
|
|
61
|
+
const materialized = await materializeProtocolPreset(options.protocolPresetId, options.protocolPresetParams, presetRegistry);
|
|
62
|
+
setRouteMocks(materialized.routeMocks);
|
|
63
|
+
applyInterceptState(materialized.interceptState);
|
|
64
|
+
setWalletSession(materialized.walletSession);
|
|
65
|
+
}
|
|
66
|
+
if (options.walletPresetId) {
|
|
67
|
+
const materialized = await materializeWalletPreset(options.walletPresetId, options.walletPresetParams, presetRegistry);
|
|
68
|
+
setWalletSession(materialized.walletSession);
|
|
69
|
+
}
|
|
70
|
+
if (options.walletPreset) {
|
|
71
|
+
setWalletSession({
|
|
72
|
+
enabled: false,
|
|
73
|
+
connected: false,
|
|
74
|
+
chainId: options.walletPreset.chainId ?? "0x1",
|
|
75
|
+
accounts: [options.walletPreset.address],
|
|
76
|
+
permissions: normalizeWalletPermissions(options.walletPreset.permissions),
|
|
77
|
+
assets: createLunaWalletAssetState(options.walletPreset.assets),
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
const unmountDevtools = options.mountDevtools === false
|
|
81
|
+
? undefined
|
|
82
|
+
: mountLunaDevtools({
|
|
83
|
+
targetId: options.devtoolsTargetId,
|
|
84
|
+
nodeEnv,
|
|
85
|
+
panelProps: {
|
|
86
|
+
presetRegistry,
|
|
87
|
+
walletFallbackMode: options.walletFallbackMode ?? "off",
|
|
88
|
+
},
|
|
89
|
+
}) ?? undefined;
|
|
90
|
+
return {
|
|
91
|
+
enabled: true,
|
|
92
|
+
configLoaded: true,
|
|
93
|
+
unmountDevtools,
|
|
94
|
+
config,
|
|
95
|
+
};
|
|
33
96
|
}
|
|
34
|
-
|
|
35
|
-
|
|
97
|
+
catch (error) {
|
|
98
|
+
disableLunaRuntimeIntercept();
|
|
99
|
+
throw error;
|
|
36
100
|
}
|
|
37
|
-
const unmountDevtools = options.mountDevtools === false
|
|
38
|
-
? undefined
|
|
39
|
-
: mountLunaDevtools({
|
|
40
|
-
targetId: options.devtoolsTargetId,
|
|
41
|
-
nodeEnv,
|
|
42
|
-
}) ?? undefined;
|
|
43
|
-
return {
|
|
44
|
-
enabled: true,
|
|
45
|
-
unmountDevtools,
|
|
46
|
-
config,
|
|
47
|
-
};
|
|
48
101
|
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export type { LunaBootstrapOptions, LunaBootstrapResult } from "./bootstrap.js";
|
|
2
|
+
export { bootstrapLunaRuntime } from "./bootstrap.js";
|
|
3
|
+
export { enableLunaIntercept } from "./intercept.js";
|
|
4
|
+
export { LunaDevtoolsPanel } from "./devtools/LunaDevtoolsPanel.js";
|
|
5
|
+
export { mountLunaDevtools } from "./devtools/mount.js";
|
package/dist/browser.js
ADDED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { type PresetRegistry } from "@lunatest/core/browser";
|
|
2
|
+
import type { PresetDiagnostic } from "@lunatest/contracts";
|
|
1
3
|
import { type RouteMock } from "@lunatest/runtime-intercept";
|
|
2
4
|
type LunaDevtoolsPanelProps = {
|
|
3
5
|
title?: string;
|
|
@@ -15,6 +17,9 @@ type LunaDevtoolsPanelProps = {
|
|
|
15
17
|
};
|
|
16
18
|
onSetRouteMocks?: (routes: RouteMock[]) => void | Promise<void>;
|
|
17
19
|
onPatchState?: (patch: Record<string, unknown>) => void | Promise<void>;
|
|
20
|
+
initialPresetDiagnostics?: PresetDiagnostic[];
|
|
21
|
+
presetRegistry?: PresetRegistry;
|
|
22
|
+
walletFallbackMode?: "off" | "manual-toggle";
|
|
18
23
|
};
|
|
19
24
|
export declare function LunaDevtoolsPanel(props: LunaDevtoolsPanelProps): import("react/jsx-runtime").JSX.Element;
|
|
20
25
|
export {};
|
|
@@ -1,11 +1,39 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useMemo, useState } from "react";
|
|
3
|
-
import { executeLuaScenario, loadLunaConfig } from "@lunatest/core";
|
|
4
|
-
import { applyInterceptState, getInterceptState, setRouteMocks, } from "@lunatest/runtime-intercept";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useMemo, useState } from "react";
|
|
3
|
+
import { createPresetRegistry, executeLuaScenario, getPresetDiagnostics, listProtocolPresets, listWalletPresets, loadLunaConfig, materializeProtocolPreset, materializeWalletPreset, } from "@lunatest/core/browser";
|
|
4
|
+
import { applyInterceptState, connectWalletSession, disconnectWalletSession, getInterceptState, getWalletSession, setWalletSession, setRouteMocks, } from "@lunatest/runtime-intercept";
|
|
5
5
|
function toPrettyJson(input) {
|
|
6
6
|
return JSON.stringify(input, null, 2);
|
|
7
7
|
}
|
|
8
|
+
function buildDefaultParamValues(schema) {
|
|
9
|
+
return Object.fromEntries(schema.map((item) => [item.key, item.default === undefined ? "" : String(item.default)]));
|
|
10
|
+
}
|
|
11
|
+
function parsePresetValue(descriptor, value) {
|
|
12
|
+
if (descriptor.type === "number" || descriptor.type === "chainId") {
|
|
13
|
+
return Number(value);
|
|
14
|
+
}
|
|
15
|
+
if (descriptor.type === "boolean") {
|
|
16
|
+
return value === "true";
|
|
17
|
+
}
|
|
18
|
+
return value;
|
|
19
|
+
}
|
|
20
|
+
function resolveWalletSelectionId(walletPresets, candidateId) {
|
|
21
|
+
const exact = walletPresets.find((item) => item.qualifiedId === candidateId);
|
|
22
|
+
if (exact) {
|
|
23
|
+
return exact.qualifiedId;
|
|
24
|
+
}
|
|
25
|
+
const builtin = walletPresets.find((item) => item.qualifiedId === `builtin/${candidateId}`);
|
|
26
|
+
if (builtin) {
|
|
27
|
+
return builtin.qualifiedId;
|
|
28
|
+
}
|
|
29
|
+
const project = walletPresets.find((item) => item.qualifiedId === `project/${candidateId}`);
|
|
30
|
+
if (project) {
|
|
31
|
+
return project.qualifiedId;
|
|
32
|
+
}
|
|
33
|
+
return candidateId;
|
|
34
|
+
}
|
|
8
35
|
export function LunaDevtoolsPanel(props) {
|
|
36
|
+
const presetRegistry = useMemo(() => props.presetRegistry ?? createPresetRegistry(), [props.presetRegistry]);
|
|
9
37
|
const initialLuaScript = useMemo(() => props.initialLua ??
|
|
10
38
|
`scenario {
|
|
11
39
|
name = "swap-warning",
|
|
@@ -33,6 +61,114 @@ export function LunaDevtoolsPanel(props) {
|
|
|
33
61
|
const [status, setStatus] = useState("idle");
|
|
34
62
|
const [error, setError] = useState(null);
|
|
35
63
|
const [diff, setDiff] = useState(null);
|
|
64
|
+
const [protocolPresets, setProtocolPresets] = useState([]);
|
|
65
|
+
const [walletPresets, setWalletPresets] = useState([]);
|
|
66
|
+
const [selectedProtocolPresetId, setSelectedProtocolPresetId] = useState("");
|
|
67
|
+
const [selectedWalletPresetId, setSelectedWalletPresetId] = useState("");
|
|
68
|
+
const [selectedBuiltinScenarioId, setSelectedBuiltinScenarioId] = useState("");
|
|
69
|
+
const [presetParamValues, setPresetParamValues] = useState({});
|
|
70
|
+
const [presetParamsJson, setPresetParamsJson] = useState("{}");
|
|
71
|
+
const [materializedPreview, setMaterializedPreview] = useState("");
|
|
72
|
+
const [presetDiagnostics, setPresetDiagnostics] = useState(props.initialPresetDiagnostics ?? []);
|
|
73
|
+
const [walletSession, setWalletSessionState] = useState(() => {
|
|
74
|
+
try {
|
|
75
|
+
return getWalletSession();
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
const refreshWalletSession = () => {
|
|
82
|
+
try {
|
|
83
|
+
setWalletSessionState(getWalletSession());
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
setWalletSessionState(null);
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
const refreshPresetDiagnostics = async () => {
|
|
90
|
+
try {
|
|
91
|
+
const diagnostics = await getPresetDiagnostics(presetRegistry);
|
|
92
|
+
const projectDiagnostics = diagnostics.filter((item) => item.source === "project");
|
|
93
|
+
setPresetDiagnostics(projectDiagnostics.length > 0 ? projectDiagnostics : diagnostics);
|
|
94
|
+
}
|
|
95
|
+
catch (cause) {
|
|
96
|
+
setError(cause instanceof Error ? cause.message : String(cause));
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
useEffect(() => {
|
|
100
|
+
let cancelled = false;
|
|
101
|
+
async function loadBuiltIns() {
|
|
102
|
+
try {
|
|
103
|
+
const [protocols, wallets] = await Promise.all([
|
|
104
|
+
listProtocolPresets(presetRegistry),
|
|
105
|
+
listWalletPresets(presetRegistry),
|
|
106
|
+
]);
|
|
107
|
+
if (cancelled) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
setProtocolPresets(protocols);
|
|
111
|
+
setWalletPresets(wallets);
|
|
112
|
+
setSelectedProtocolPresetId((current) => current || protocols[0]?.qualifiedId || "");
|
|
113
|
+
setSelectedWalletPresetId((current) => current || wallets[0]?.qualifiedId || "");
|
|
114
|
+
await refreshPresetDiagnostics();
|
|
115
|
+
}
|
|
116
|
+
catch (cause) {
|
|
117
|
+
if (!cancelled) {
|
|
118
|
+
setError(cause instanceof Error ? cause.message : String(cause));
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
void loadBuiltIns();
|
|
123
|
+
return () => {
|
|
124
|
+
cancelled = true;
|
|
125
|
+
};
|
|
126
|
+
}, [presetRegistry]);
|
|
127
|
+
const selectedProtocolPreset = useMemo(() => protocolPresets.find((item) => item.qualifiedId === selectedProtocolPresetId) ?? null, [protocolPresets, selectedProtocolPresetId]);
|
|
128
|
+
const selectedWalletPreset = useMemo(() => walletPresets.find((item) => item.qualifiedId === selectedWalletPresetId) ?? null, [walletPresets, selectedWalletPresetId]);
|
|
129
|
+
const recommendedDescriptors = useMemo(() => {
|
|
130
|
+
const descriptors = new Map();
|
|
131
|
+
if (selectedProtocolPreset) {
|
|
132
|
+
const keys = new Set(selectedProtocolPreset.recommendedControls);
|
|
133
|
+
for (const descriptor of selectedProtocolPreset.paramsSchema) {
|
|
134
|
+
if (keys.has(descriptor.key)) {
|
|
135
|
+
descriptors.set(descriptor.key, descriptor);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
if (selectedWalletPreset?.paramsSchema) {
|
|
140
|
+
const keys = new Set(selectedWalletPreset.recommendedControls ?? []);
|
|
141
|
+
for (const descriptor of selectedWalletPreset.paramsSchema) {
|
|
142
|
+
if (keys.has(descriptor.key) && !descriptors.has(descriptor.key)) {
|
|
143
|
+
descriptors.set(descriptor.key, descriptor);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return Array.from(descriptors.values());
|
|
148
|
+
}, [selectedProtocolPreset, selectedWalletPreset]);
|
|
149
|
+
useEffect(() => {
|
|
150
|
+
if (!selectedProtocolPreset) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
setPresetParamValues(buildDefaultParamValues(selectedProtocolPreset.paramsSchema));
|
|
154
|
+
setSelectedBuiltinScenarioId(selectedProtocolPreset.builtinScenarios[0]?.id ?? "");
|
|
155
|
+
setSelectedWalletPresetId(resolveWalletSelectionId(walletPresets, selectedProtocolPreset.defaultWalletPreset.id));
|
|
156
|
+
}, [selectedProtocolPreset, walletPresets]);
|
|
157
|
+
const buildPresetParams = () => {
|
|
158
|
+
const params = {};
|
|
159
|
+
for (const descriptor of recommendedDescriptors) {
|
|
160
|
+
const current = presetParamValues[descriptor.key];
|
|
161
|
+
if (current === undefined || current === "") {
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
params[descriptor.key] = parsePresetValue(descriptor, current);
|
|
165
|
+
}
|
|
166
|
+
const advanced = JSON.parse(presetParamsJson);
|
|
167
|
+
return {
|
|
168
|
+
...params,
|
|
169
|
+
...advanced,
|
|
170
|
+
};
|
|
171
|
+
};
|
|
36
172
|
const handleRun = async () => {
|
|
37
173
|
setError(null);
|
|
38
174
|
setDiff(null);
|
|
@@ -76,6 +212,8 @@ export function LunaDevtoolsPanel(props) {
|
|
|
76
212
|
if (executed.diff) {
|
|
77
213
|
setDiff(executed.diff);
|
|
78
214
|
}
|
|
215
|
+
refreshWalletSession();
|
|
216
|
+
await refreshPresetDiagnostics();
|
|
79
217
|
}
|
|
80
218
|
catch (cause) {
|
|
81
219
|
setStatus("failed");
|
|
@@ -95,6 +233,8 @@ export function LunaDevtoolsPanel(props) {
|
|
|
95
233
|
setRouteMocks(parsed);
|
|
96
234
|
}
|
|
97
235
|
setStatus("routes-updated");
|
|
236
|
+
refreshWalletSession();
|
|
237
|
+
await refreshPresetDiagnostics();
|
|
98
238
|
}
|
|
99
239
|
catch (cause) {
|
|
100
240
|
setStatus("failed");
|
|
@@ -114,6 +254,7 @@ export function LunaDevtoolsPanel(props) {
|
|
|
114
254
|
applyInterceptState(parsed);
|
|
115
255
|
}
|
|
116
256
|
setStatus("state-patched");
|
|
257
|
+
refreshWalletSession();
|
|
117
258
|
}
|
|
118
259
|
catch (cause) {
|
|
119
260
|
setStatus("failed");
|
|
@@ -127,6 +268,60 @@ export function LunaDevtoolsPanel(props) {
|
|
|
127
268
|
setStatus("reset");
|
|
128
269
|
setError(null);
|
|
129
270
|
setDiff(null);
|
|
271
|
+
refreshWalletSession();
|
|
272
|
+
void refreshPresetDiagnostics();
|
|
273
|
+
};
|
|
274
|
+
const handleApplyProtocolPreset = async () => {
|
|
275
|
+
if (!selectedProtocolPresetId) {
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
setError(null);
|
|
279
|
+
setStatus("applying-protocol-preset");
|
|
280
|
+
try {
|
|
281
|
+
const params = buildPresetParams();
|
|
282
|
+
const materialized = await materializeProtocolPreset(selectedProtocolPresetId, params, presetRegistry);
|
|
283
|
+
let nextWalletSession = materialized.walletSession;
|
|
284
|
+
if (selectedWalletPresetId && selectedWalletPresetId !== materialized.walletPresetId) {
|
|
285
|
+
const walletMaterialized = await materializeWalletPreset(selectedWalletPresetId, params, presetRegistry);
|
|
286
|
+
nextWalletSession = walletMaterialized.walletSession;
|
|
287
|
+
}
|
|
288
|
+
setWalletSession(nextWalletSession);
|
|
289
|
+
setRouteMocks(materialized.routeMocks);
|
|
290
|
+
applyInterceptState(materialized.interceptState);
|
|
291
|
+
setMaterializedPreview(toPrettyJson(materialized));
|
|
292
|
+
setSelectedWalletPresetId(materialized.walletPresetId);
|
|
293
|
+
const scenario = materialized.builtinScenarios.find((item) => item.id === selectedBuiltinScenarioId) ??
|
|
294
|
+
materialized.builtinScenarios[0];
|
|
295
|
+
if (scenario) {
|
|
296
|
+
setLuaScript(scenario.lua);
|
|
297
|
+
}
|
|
298
|
+
refreshWalletSession();
|
|
299
|
+
await refreshPresetDiagnostics();
|
|
300
|
+
setStatus("protocol-preset-applied");
|
|
301
|
+
}
|
|
302
|
+
catch (cause) {
|
|
303
|
+
setStatus("failed");
|
|
304
|
+
setError(cause instanceof Error ? cause.message : String(cause));
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
const handleApplyWalletPreset = async () => {
|
|
308
|
+
if (!selectedWalletPresetId) {
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
setError(null);
|
|
312
|
+
setStatus("applying-wallet-preset");
|
|
313
|
+
try {
|
|
314
|
+
const materialized = await materializeWalletPreset(selectedWalletPresetId, buildPresetParams(), presetRegistry);
|
|
315
|
+
setWalletSession(materialized.walletSession);
|
|
316
|
+
setMaterializedPreview(toPrettyJson(materialized));
|
|
317
|
+
refreshWalletSession();
|
|
318
|
+
await refreshPresetDiagnostics();
|
|
319
|
+
setStatus("wallet-preset-applied");
|
|
320
|
+
}
|
|
321
|
+
catch (cause) {
|
|
322
|
+
setStatus("failed");
|
|
323
|
+
setError(cause instanceof Error ? cause.message : String(cause));
|
|
324
|
+
}
|
|
130
325
|
};
|
|
131
326
|
return (_jsxs("aside", { "data-lunatest-devtools": true, style: {
|
|
132
327
|
position: "fixed",
|
|
@@ -144,5 +339,46 @@ export function LunaDevtoolsPanel(props) {
|
|
|
144
339
|
fontSize: 12,
|
|
145
340
|
color: "#0f172a",
|
|
146
341
|
padding: 12,
|
|
147
|
-
}, children: [_jsx("h3", { style: { margin: 0, marginBottom: 8 }, children: props.title ?? "LunaTest Devtools" }), _jsx("p", { style: { margin: 0, marginBottom: 10, color: "#334155" }, children: "Lua DSL\uACFC \uC778\uD130\uC149\uD2B8 \uC0C1\uD0DC\uB97C \uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uBC14\uB85C \uC218\uC815\uD569\uB2C8\uB2E4." }), _jsx("label", { htmlFor: "lunatest-lua-script", children: "Lua Scenario" }), _jsx("textarea", { id: "lunatest-lua-script", value: luaScript, onChange: (event) => setLuaScript(event.currentTarget.value), style: { width: "100%", minHeight: 140, marginBottom: 8 } }), _jsx("button", { type: "button", onClick: handleRun, children: "Run Scenario" }), _jsx("hr", {}),
|
|
342
|
+
}, children: [_jsx("h3", { style: { margin: 0, marginBottom: 8 }, children: props.title ?? "LunaTest Devtools" }), _jsx("p", { style: { margin: 0, marginBottom: 10, color: "#334155" }, children: "Lua DSL\uACFC \uC778\uD130\uC149\uD2B8 \uC0C1\uD0DC\uB97C \uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uBC14\uB85C \uC218\uC815\uD569\uB2C8\uB2E4." }), _jsx("label", { htmlFor: "lunatest-lua-script", children: "Lua Scenario" }), _jsx("textarea", { id: "lunatest-lua-script", value: luaScript, onChange: (event) => setLuaScript(event.currentTarget.value), style: { width: "100%", minHeight: 140, marginBottom: 8 } }), _jsx("button", { type: "button", onClick: handleRun, children: "Run Scenario" }), _jsx("hr", {}), _jsxs("h4", { style: { margin: 0, marginBottom: 8 }, children: ["Diagnostics (", presetDiagnostics.length, ")"] }), presetDiagnostics.length === 0 ? (_jsx("p", { style: { margin: 0, marginBottom: 10, color: "#334155" }, children: "No preset diagnostics." })) : (_jsx("div", { style: { marginBottom: 12 }, children: presetDiagnostics.map((diagnostic) => (_jsxs("div", { style: {
|
|
343
|
+
border: "1px solid #cbd5e1",
|
|
344
|
+
borderRadius: 8,
|
|
345
|
+
padding: 8,
|
|
346
|
+
marginBottom: 8,
|
|
347
|
+
}, children: [_jsxs("p", { style: { margin: 0, marginBottom: 4 }, children: ["[", diagnostic.source, "] ", diagnostic.code] }), _jsx("p", { style: { margin: 0, marginBottom: 4 }, children: diagnostic.message }), diagnostic.qualifiedId ? (_jsxs("p", { style: { margin: 0, marginBottom: 4 }, children: ["preset: ", diagnostic.qualifiedId] })) : null, diagnostic.hint ? _jsxs("p", { style: { margin: 0 }, children: ["hint: ", diagnostic.hint] }) : null] }, `${diagnostic.code}:${diagnostic.qualifiedId ?? "none"}`))) })), _jsx("hr", {}), _jsx("h4", { style: { margin: 0, marginBottom: 8 }, children: "Protocol Preset" }), _jsx("label", { htmlFor: "lunatest-protocol-preset", children: "Protocol Preset" }), _jsx("select", { id: "lunatest-protocol-preset", value: selectedProtocolPresetId, onChange: (event) => setSelectedProtocolPresetId(event.currentTarget.value), style: { width: "100%", marginBottom: 8 }, children: protocolPresets.map((preset) => (_jsxs("option", { value: preset.qualifiedId, children: ["[", preset.source, "] ", preset.label] }, preset.qualifiedId))) }), _jsx("label", { htmlFor: "lunatest-wallet-preset", children: "Wallet Preset" }), _jsx("select", { id: "lunatest-wallet-preset", value: selectedWalletPresetId, onChange: (event) => setSelectedWalletPresetId(event.currentTarget.value), style: { width: "100%", marginBottom: 8 }, children: walletPresets.map((preset) => (_jsxs("option", { value: preset.qualifiedId, children: ["[", preset.source, "] ", preset.label] }, preset.qualifiedId))) }), recommendedDescriptors.length > 0 ? (_jsxs(_Fragment, { children: [_jsx("h4", { style: { margin: 0, marginBottom: 8 }, children: "Recommended Controls" }), recommendedDescriptors.map((descriptor) => (_jsxs("div", { style: { marginBottom: 8 }, children: [_jsx("label", { htmlFor: `preset-control-${descriptor.key}`, children: descriptor.label }), _jsx("input", { id: `preset-control-${descriptor.key}`, value: presetParamValues[descriptor.key] ?? "", onChange: (event) => setPresetParamValues((current) => ({
|
|
348
|
+
...current,
|
|
349
|
+
[descriptor.key]: event.currentTarget.value,
|
|
350
|
+
})), style: { width: "100%" } })] }, descriptor.key)))] })) : null, _jsx("label", { htmlFor: "lunatest-preset-params", children: "Advanced Preset Params (JSON)" }), _jsx("textarea", { id: "lunatest-preset-params", value: presetParamsJson, onChange: (event) => setPresetParamsJson(event.currentTarget.value), style: { width: "100%", minHeight: 90, marginBottom: 8 } }), selectedProtocolPreset?.builtinScenarios?.length ? (_jsxs(_Fragment, { children: [_jsx("label", { htmlFor: "lunatest-builtin-scenario", children: "Built-in Scenario" }), _jsx("select", { id: "lunatest-builtin-scenario", value: selectedBuiltinScenarioId, onChange: (event) => setSelectedBuiltinScenarioId(event.currentTarget.value), style: { width: "100%", marginBottom: 8 }, children: selectedProtocolPreset.builtinScenarios.map((scenario) => (_jsx("option", { value: scenario.id, children: scenario.label }, scenario.id))) })] })) : null, _jsx("button", { type: "button", onClick: handleApplyProtocolPreset, children: "Apply Protocol Preset" }), _jsx("button", { type: "button", style: { marginLeft: 8 }, onClick: handleApplyWalletPreset, children: "Apply Wallet Preset" }), materializedPreview ? (_jsxs(_Fragment, { children: [_jsx("p", { style: { marginTop: 10, marginBottom: 4 }, children: "materialized:" }), _jsx("pre", { style: { marginTop: 0, whiteSpace: "pre-wrap" }, children: materializedPreview })] })) : null, props.walletFallbackMode === "manual-toggle" ? (_jsxs(_Fragment, { children: [_jsx("hr", {}), _jsx("h4", { style: { margin: 0, marginBottom: 8 }, children: "Luna Wallet" }), _jsx("p", { style: { margin: 0, marginBottom: 8, color: "#334155" }, children: "\uC9C0\uAC11\uC774 \uC5C6\uC5B4\uB3C4 wallet RPC\uB97C Luna session\uC73C\uB85C \uD558\uC774\uC7AC\uD0B9\uD569\uB2C8\uB2E4." }), _jsxs("p", { style: { margin: 0, marginBottom: 8 }, children: ["mode: ", walletSession?.enabled ? "on" : "off", " / connected: ", walletSession?.connected ? "yes" : "no"] }), _jsxs("p", { style: { margin: 0, marginBottom: 8 }, children: ["account: ", walletSession?.accounts?.[0] ?? "n/a"] }), _jsxs("p", { style: { margin: 0, marginBottom: 8 }, children: ["chain: ", walletSession?.chainId ?? "n/a"] }), _jsxs("p", { style: { margin: 0, marginBottom: 8 }, children: ["permissions: ", walletSession?.permissions?.map((item) => item.parentCapability).join(", ") || "none"] }), _jsx("button", { type: "button", onClick: () => {
|
|
351
|
+
try {
|
|
352
|
+
if (walletSession?.enabled) {
|
|
353
|
+
disconnectWalletSession();
|
|
354
|
+
}
|
|
355
|
+
else {
|
|
356
|
+
connectWalletSession();
|
|
357
|
+
}
|
|
358
|
+
refreshWalletSession();
|
|
359
|
+
setStatus("wallet-updated");
|
|
360
|
+
setError(null);
|
|
361
|
+
}
|
|
362
|
+
catch (cause) {
|
|
363
|
+
setStatus("failed");
|
|
364
|
+
setError(cause instanceof Error ? cause.message : String(cause));
|
|
365
|
+
}
|
|
366
|
+
}, children: walletSession?.enabled ? "Disable Luna Wallet" : "Enable Luna Wallet" }), _jsx("button", { type: "button", style: { marginLeft: 8 }, onClick: () => {
|
|
367
|
+
try {
|
|
368
|
+
setWalletSession({
|
|
369
|
+
enabled: walletSession?.enabled ?? false,
|
|
370
|
+
connected: walletSession?.connected ?? false,
|
|
371
|
+
chainId: "0xaa36a7",
|
|
372
|
+
accounts: ["0x1111111111111111111111111111111111111111"],
|
|
373
|
+
permissions: walletSession?.permissions ?? [],
|
|
374
|
+
});
|
|
375
|
+
refreshWalletSession();
|
|
376
|
+
setStatus("wallet-updated");
|
|
377
|
+
setError(null);
|
|
378
|
+
}
|
|
379
|
+
catch (cause) {
|
|
380
|
+
setStatus("failed");
|
|
381
|
+
setError(cause instanceof Error ? cause.message : String(cause));
|
|
382
|
+
}
|
|
383
|
+
}, children: "Reset Session" })] })) : null, _jsx("hr", {}), _jsx("label", { htmlFor: "lunatest-routes", children: "Route Mocks (JSON array)" }), _jsx("textarea", { id: "lunatest-routes", value: routeJson, onChange: (event) => setRouteJson(event.currentTarget.value), style: { width: "100%", minHeight: 110, marginBottom: 8 } }), _jsx("button", { type: "button", onClick: handleApplyRoutes, children: "Apply Routes" }), _jsx("hr", {}), _jsx("label", { htmlFor: "lunatest-state", children: "Intercept State Patch (JSON object)" }), _jsx("textarea", { id: "lunatest-state", value: stateJson, onChange: (event) => setStateJson(event.currentTarget.value), style: { width: "100%", minHeight: 110, marginBottom: 8 } }), _jsx("button", { type: "button", onClick: handlePatchState, children: "Patch State" }), _jsx("button", { type: "button", onClick: handleReset, style: { marginLeft: 8 }, children: "Reset" }), _jsxs("p", { style: { marginTop: 10, marginBottom: 0 }, children: ["status: ", status] }), error ? (_jsxs("p", { style: { marginTop: 4, marginBottom: 0, color: "#b91c1c" }, children: ["error: ", error] })) : null, diff ? (_jsxs("pre", { style: { marginTop: 8, marginBottom: 0, color: "#7f1d1d", whiteSpace: "pre-wrap" }, children: ["diff: ", diff] })) : null] }));
|
|
148
384
|
}
|
package/dist/devtools/mount.js
CHANGED
|
@@ -5,6 +5,7 @@ import { resolveNodeEnv } from "../node-env.js";
|
|
|
5
5
|
const DEFAULT_TARGET_ID = "lunatest-devtools-root";
|
|
6
6
|
let activeRoot = null;
|
|
7
7
|
let activeTarget = null;
|
|
8
|
+
let activeTargetOwned = false;
|
|
8
9
|
export function mountLunaDevtools(options = {}) {
|
|
9
10
|
if (typeof document === "undefined") {
|
|
10
11
|
return null;
|
|
@@ -26,16 +27,18 @@ export function mountLunaDevtools(options = {}) {
|
|
|
26
27
|
}
|
|
27
28
|
activeRoot = createRoot(target);
|
|
28
29
|
activeTarget = target;
|
|
30
|
+
activeTargetOwned = !existing;
|
|
29
31
|
activeRoot.render(React.createElement(LunaDevtoolsPanel, options.panelProps));
|
|
30
32
|
return () => {
|
|
31
33
|
if (!activeRoot || !activeTarget) {
|
|
32
34
|
return;
|
|
33
35
|
}
|
|
34
36
|
activeRoot.unmount();
|
|
35
|
-
if (
|
|
37
|
+
if (activeTargetOwned) {
|
|
36
38
|
activeTarget.remove();
|
|
37
39
|
}
|
|
38
40
|
activeRoot = null;
|
|
39
41
|
activeTarget = null;
|
|
42
|
+
activeTargetOwned = false;
|
|
40
43
|
};
|
|
41
44
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { useMemo } from "react";
|
|
2
2
|
import { createLunaProvider } from "../luna-provider.js";
|
|
3
|
+
import { createProviderOptionsKey } from "../provider-options.js";
|
|
3
4
|
export function useLunaProvider(options) {
|
|
4
|
-
|
|
5
|
+
const optionsKey = createProviderOptionsKey(options);
|
|
6
|
+
return useMemo(() => createLunaProvider(options), [optionsKey, options.callHandler]);
|
|
5
7
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
function stableStringify(value) {
|
|
2
|
+
if (value === null || value === undefined) {
|
|
3
|
+
return String(value);
|
|
4
|
+
}
|
|
5
|
+
if (typeof value !== "object") {
|
|
6
|
+
return JSON.stringify(value);
|
|
7
|
+
}
|
|
8
|
+
if (Array.isArray(value)) {
|
|
9
|
+
return `[${value.map((item) => stableStringify(item)).join(",")}]`;
|
|
10
|
+
}
|
|
11
|
+
const entries = Object.entries(value)
|
|
12
|
+
.filter((entry) => entry[1] !== undefined)
|
|
13
|
+
.sort(([left], [right]) => left.localeCompare(right));
|
|
14
|
+
return `{${entries
|
|
15
|
+
.map(([key, nested]) => `${JSON.stringify(key)}:${stableStringify(nested)}`)
|
|
16
|
+
.join(",")}}`;
|
|
17
|
+
}
|
|
18
|
+
export function createProviderOptionsKey(options) {
|
|
19
|
+
if (!options) {
|
|
20
|
+
return "undefined";
|
|
21
|
+
}
|
|
22
|
+
const serializable = {
|
|
23
|
+
chainId: options.chainId,
|
|
24
|
+
accounts: options.accounts,
|
|
25
|
+
balances: options.balances,
|
|
26
|
+
wallet: options.wallet,
|
|
27
|
+
};
|
|
28
|
+
return stableStringify(serializable);
|
|
29
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lunatest/react",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -11,6 +11,11 @@
|
|
|
11
11
|
"import": "./dist/index.js",
|
|
12
12
|
"default": "./dist/index.js"
|
|
13
13
|
},
|
|
14
|
+
"./browser": {
|
|
15
|
+
"types": "./dist/browser.d.ts",
|
|
16
|
+
"import": "./dist/browser.js",
|
|
17
|
+
"default": "./dist/browser.js"
|
|
18
|
+
},
|
|
14
19
|
"./package.json": "./package.json"
|
|
15
20
|
},
|
|
16
21
|
"publishConfig": {
|
|
@@ -21,8 +26,9 @@
|
|
|
21
26
|
"dist"
|
|
22
27
|
],
|
|
23
28
|
"dependencies": {
|
|
24
|
-
"@lunatest/core": "0.1.
|
|
25
|
-
"@lunatest/runtime-intercept": "0.1.0"
|
|
29
|
+
"@lunatest/core": "0.1.1",
|
|
30
|
+
"@lunatest/runtime-intercept": "0.1.0",
|
|
31
|
+
"@lunatest/contracts": "0.1.0"
|
|
26
32
|
},
|
|
27
33
|
"peerDependencies": {
|
|
28
34
|
"react": "^18.3.1 || ^19.0.0",
|
|
@@ -34,9 +40,14 @@
|
|
|
34
40
|
"@types/react": "^18.3.12",
|
|
35
41
|
"@types/react-dom": "^18.3.1"
|
|
36
42
|
},
|
|
43
|
+
"repository": {
|
|
44
|
+
"type": "git",
|
|
45
|
+
"url": "https://github.com/songforthemute/lunatest",
|
|
46
|
+
"directory": "packages/react"
|
|
47
|
+
},
|
|
37
48
|
"scripts": {
|
|
38
49
|
"build": "tsc -p tsconfig.json",
|
|
39
50
|
"test": "vitest run",
|
|
40
|
-
"lint": "tsc -p tsconfig.json --
|
|
51
|
+
"lint": "tsc -p tsconfig.lint.json --pretty false"
|
|
41
52
|
}
|
|
42
53
|
}
|