@base44-preview/vite-plugin 0.2.20-pr.27.3c8770e → 0.2.20-pr.29.d7fd4ea
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/html-injections-plugin.d.ts +2 -4
- package/dist/html-injections-plugin.d.ts.map +1 -1
- package/dist/html-injections-plugin.js +65 -42
- package/dist/html-injections-plugin.js.map +1 -1
- package/dist/injections/analytics-tracker.js +0 -1
- package/dist/injections/analytics-tracker.js.map +1 -1
- package/dist/injections/navigation-notifier.d.ts +1 -2
- package/dist/injections/navigation-notifier.d.ts.map +1 -1
- package/dist/injections/navigation-notifier.js +2 -2
- package/dist/injections/navigation-notifier.js.map +1 -1
- package/dist/injections/sandbox-hmr-notifier.d.ts +1 -4
- package/dist/injections/sandbox-hmr-notifier.d.ts.map +1 -1
- package/dist/injections/sandbox-hmr-notifier.js +4 -4
- package/dist/injections/sandbox-hmr-notifier.js.map +1 -1
- package/dist/injections/sandbox-mount-observer.d.ts +1 -2
- package/dist/injections/sandbox-mount-observer.d.ts.map +1 -1
- package/dist/injections/sandbox-mount-observer.js +2 -2
- package/dist/injections/sandbox-mount-observer.js.map +1 -1
- package/dist/injections/unhandled-errors-handlers.d.ts +1 -4
- package/dist/injections/unhandled-errors-handlers.d.ts.map +1 -1
- package/dist/injections/unhandled-errors-handlers.js +67 -69
- package/dist/injections/unhandled-errors-handlers.js.map +1 -1
- package/dist/injections/visual-edit-agent.d.ts +1 -1
- package/dist/injections/visual-edit-agent.d.ts.map +1 -1
- package/dist/injections/visual-edit-agent.js +40 -1
- package/dist/injections/visual-edit-agent.js.map +1 -1
- package/package.json +2 -8
- package/src/html-injections-plugin.ts +88 -53
- package/src/injections/analytics-tracker.ts +0 -2
- package/src/injections/navigation-notifier.ts +1 -2
- package/src/injections/sandbox-hmr-notifier.ts +3 -4
- package/src/injections/sandbox-mount-observer.ts +2 -2
- package/src/injections/unhandled-errors-handlers.ts +80 -84
- package/src/injections/visual-edit-agent.ts +57 -2
- package/README.md +0 -93
- package/dist/bridge.d.ts +0 -20
- package/dist/bridge.d.ts.map +0 -1
- package/dist/bridge.js +0 -33
- package/dist/bridge.js.map +0 -1
- package/dist/statics/index.mjs +0 -2
- package/dist/statics/index.mjs.map +0 -1
- package/src/bridge.ts +0 -37
package/package.json
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@base44-preview/vite-plugin",
|
|
3
|
-
"version": "0.2.20-pr.
|
|
3
|
+
"version": "0.2.20-pr.29.d7fd4ea",
|
|
4
4
|
"description": "The Vite plugin for base44 based applications",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"exports": {
|
|
8
8
|
".": "./dist/index.js",
|
|
9
|
-
"./bridge": "./dist/bridge.js",
|
|
10
9
|
"./compat/*": "./compat/*"
|
|
11
10
|
},
|
|
12
11
|
"files": [
|
|
@@ -16,20 +15,15 @@
|
|
|
16
15
|
],
|
|
17
16
|
"scripts": {
|
|
18
17
|
"build": "tsc",
|
|
19
|
-
"build:bridge": "tsup",
|
|
20
18
|
"validate-exports": "node scripts/validate-exports.mjs",
|
|
21
|
-
"prepublishOnly": "rm -rf ./dist && npm run build && npm run
|
|
22
|
-
"dev": "node dev-server.js",
|
|
19
|
+
"prepublishOnly": "rm -rf ./dist && npm run build && npm run validate-exports",
|
|
23
20
|
"test:unit": "vitest run"
|
|
24
21
|
},
|
|
25
22
|
"devDependencies": {
|
|
26
23
|
"@types/babel__generator": "^7.27.0",
|
|
27
24
|
"@types/babel__traverse": "^7.28.0",
|
|
28
25
|
"@types/node": "^24.6.2",
|
|
29
|
-
"cors": "^2.8.5",
|
|
30
|
-
"express": "^4.21.2",
|
|
31
26
|
"jsdom": "^26.0.0",
|
|
32
|
-
"tsup": "^8.1.0",
|
|
33
27
|
"typescript": "^5.9.3",
|
|
34
28
|
"vite": "^7.1.9",
|
|
35
29
|
"vitest": "^3.0.0"
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import type { Plugin, HtmlTagDescriptor } from "vite";
|
|
2
2
|
import { loadEnv } from "vite";
|
|
3
3
|
|
|
4
|
+
type Injection = {
|
|
5
|
+
/** Script src path for dev mode (served from node_modules) */
|
|
6
|
+
src?: string;
|
|
7
|
+
injectTo: "head" | "body";
|
|
8
|
+
mode: "dev" | "production";
|
|
9
|
+
/** Inline content for production builds (src paths don't work in built output) */
|
|
10
|
+
inlineContent?: string;
|
|
11
|
+
};
|
|
12
|
+
|
|
4
13
|
const ANALYTICS_TRACKER_SCRIPT = `
|
|
5
14
|
let lastPath = "";
|
|
6
15
|
function getPageNameFromPath(path) {
|
|
@@ -33,19 +42,59 @@ window.addEventListener("popstate", trackPageView);
|
|
|
33
42
|
trackPageView();
|
|
34
43
|
`;
|
|
35
44
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
45
|
+
const INJECTIONS: Record<string, Injection> = {
|
|
46
|
+
unhandledErrors: {
|
|
47
|
+
src: "/node_modules/@base44/vite-plugin/dist/injections/unhandled-errors-handlers.js",
|
|
48
|
+
injectTo: "head",
|
|
49
|
+
mode: "dev",
|
|
50
|
+
},
|
|
51
|
+
sandboxMount: {
|
|
52
|
+
src: "/node_modules/@base44/vite-plugin/dist/injections/sandbox-mount-observer.js",
|
|
53
|
+
injectTo: "body",
|
|
54
|
+
mode: "dev",
|
|
55
|
+
},
|
|
56
|
+
hmrNotifier: {
|
|
57
|
+
src: "/node_modules/@base44/vite-plugin/dist/injections/sandbox-hmr-notifier.js",
|
|
58
|
+
injectTo: "head",
|
|
59
|
+
mode: "dev",
|
|
60
|
+
},
|
|
61
|
+
navigationNotifier: {
|
|
62
|
+
src: "/node_modules/@base44/vite-plugin/dist/injections/navigation-notifier.js",
|
|
63
|
+
injectTo: "head",
|
|
64
|
+
mode: "dev",
|
|
65
|
+
},
|
|
66
|
+
visualEditAgent: {
|
|
67
|
+
src: "/node_modules/@base44/vite-plugin/dist/injections/visual-edit-agent.js",
|
|
68
|
+
injectTo: "body",
|
|
69
|
+
mode: "dev",
|
|
70
|
+
},
|
|
71
|
+
analyticsTracker: {
|
|
72
|
+
injectTo: "head",
|
|
73
|
+
mode: "production",
|
|
74
|
+
inlineContent: ANALYTICS_TRACKER_SCRIPT,
|
|
75
|
+
},
|
|
76
|
+
};
|
|
42
77
|
|
|
43
78
|
export function htmlInjectionsPlugin({
|
|
44
79
|
hmrNotifier,
|
|
45
80
|
navigationNotifier,
|
|
46
81
|
visualEditAgent,
|
|
47
82
|
analyticsTracker,
|
|
48
|
-
}:
|
|
83
|
+
}: {
|
|
84
|
+
hmrNotifier: boolean;
|
|
85
|
+
navigationNotifier: boolean;
|
|
86
|
+
visualEditAgent: boolean;
|
|
87
|
+
analyticsTracker: boolean;
|
|
88
|
+
}) {
|
|
89
|
+
const enabledInjections: Record<string, boolean> = {
|
|
90
|
+
unhandledErrors: true,
|
|
91
|
+
sandboxMount: true,
|
|
92
|
+
hmrNotifier,
|
|
93
|
+
navigationNotifier,
|
|
94
|
+
visualEditAgent,
|
|
95
|
+
analyticsTracker,
|
|
96
|
+
};
|
|
97
|
+
|
|
49
98
|
let resolvedEnv: Record<string, string> = {};
|
|
50
99
|
|
|
51
100
|
return {
|
|
@@ -57,58 +106,44 @@ export function htmlInjectionsPlugin({
|
|
|
57
106
|
},
|
|
58
107
|
transformIndexHtml: {
|
|
59
108
|
handler(_html, ctx) {
|
|
60
|
-
const tags: HtmlTagDescriptor[] = [];
|
|
61
109
|
const currentMode = ctx.server ? "dev" : "production";
|
|
62
110
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
` try {`,
|
|
79
|
-
` const mode = new URLSearchParams(location.search).get("sandbox-bridge");`,
|
|
80
|
-
` const url = mode === "local"`,
|
|
81
|
-
` ? "https://localhost:3201/index.mjs"`,
|
|
82
|
-
` : "/node_modules/@base44/vite-plugin/dist/statics/index.mjs";`,
|
|
83
|
-
` const mod = await import(url);`,
|
|
84
|
-
` if (typeof mod.init === "function") mod.init(${options}, import.meta.hot);`,
|
|
85
|
-
` } catch (e) {`,
|
|
86
|
-
` console.error("[sandbox-bridge] Failed to load:", e);`,
|
|
87
|
-
` }`,
|
|
88
|
-
` })();`,
|
|
89
|
-
`}`,
|
|
90
|
-
].join("\n"),
|
|
91
|
-
injectTo: "head",
|
|
92
|
-
});
|
|
93
|
-
}
|
|
111
|
+
const tags = Object.entries(INJECTIONS)
|
|
112
|
+
.filter(
|
|
113
|
+
([key, injection]) =>
|
|
114
|
+
enabledInjections[key] && injection.mode === currentMode
|
|
115
|
+
)
|
|
116
|
+
.map(([, injection]): HtmlTagDescriptor => {
|
|
117
|
+
// Production injections use inline content (src paths don't work in built output)
|
|
118
|
+
if (injection.inlineContent) {
|
|
119
|
+
// Replace import.meta.env references with actual values
|
|
120
|
+
// Vite doesn't replace these in inline script content
|
|
121
|
+
let content = injection.inlineContent;
|
|
122
|
+
content = content.replace(
|
|
123
|
+
/import\.meta\.env\.(\w+)/g,
|
|
124
|
+
(_, envKey) => JSON.stringify(resolvedEnv[envKey] ?? "")
|
|
125
|
+
);
|
|
94
126
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
127
|
+
return {
|
|
128
|
+
tag: "script",
|
|
129
|
+
attrs: { type: "module" },
|
|
130
|
+
children: content,
|
|
131
|
+
injectTo: injection.injectTo,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
// Dev injections use src path (served from node_modules)
|
|
135
|
+
return {
|
|
136
|
+
tag: "script",
|
|
137
|
+
attrs: {
|
|
138
|
+
src: injection.src,
|
|
139
|
+
type: "module",
|
|
140
|
+
},
|
|
141
|
+
injectTo: injection.injectTo,
|
|
142
|
+
};
|
|
107
143
|
});
|
|
108
|
-
}
|
|
109
144
|
|
|
110
145
|
return tags;
|
|
111
146
|
},
|
|
112
147
|
},
|
|
113
|
-
};
|
|
148
|
+
} as Plugin;
|
|
114
149
|
}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
hmr.on("vite:beforeUpdate", () => {
|
|
1
|
+
if (import.meta.hot) {
|
|
2
|
+
import.meta.hot.on("vite:beforeUpdate", () => {
|
|
4
3
|
window.parent?.postMessage({ type: "sandbox:beforeUpdate" }, "*");
|
|
5
4
|
});
|
|
6
|
-
|
|
5
|
+
import.meta.hot.on("vite:afterUpdate", () => {
|
|
7
6
|
window.parent?.postMessage({ type: "sandbox:afterUpdate" }, "*");
|
|
8
7
|
});
|
|
9
8
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
|
|
2
|
+
if (window.self !== window.top) {
|
|
3
3
|
const observer = new MutationObserver((mutations) => {
|
|
4
4
|
const nodesAdded = mutations.some(
|
|
5
5
|
(mutation) => mutation.addedNodes.length > 0
|
|
@@ -1,97 +1,93 @@
|
|
|
1
|
-
|
|
1
|
+
/// <reference types="vite/client" />
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
window.removeEventListener("unhandledrejection", handleUnhandledRejection);
|
|
4
|
+
window.removeEventListener("error", handleWindowError);
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
window.removeEventListener("error", handleWindowError);
|
|
6
|
+
window.addEventListener("unhandledrejection", handleUnhandledRejection);
|
|
7
|
+
window.addEventListener("error", handleWindowError);
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
let shouldPropagateErrors = true;
|
|
10
|
+
let suppressionTimer: ReturnType<typeof setTimeout> | null = null;
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
if (import.meta.hot) {
|
|
13
|
+
import.meta.hot.on("vite:beforeUpdate", () => {
|
|
14
|
+
shouldPropagateErrors = false;
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
if (suppressionTimer) {
|
|
20
|
-
clearTimeout(suppressionTimer);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
suppressionTimer = setTimeout(() => {
|
|
24
|
-
shouldPropagateErrors = true;
|
|
25
|
-
suppressionTimer = null;
|
|
26
|
-
}, ERROR_SUPPRESSION_TIMEOUT);
|
|
27
|
-
});
|
|
28
|
-
hmr.on("vite:beforeFullReload", () => {
|
|
29
|
-
shouldPropagateErrors = false;
|
|
30
|
-
if (suppressionTimer) {
|
|
31
|
-
clearTimeout(suppressionTimer);
|
|
32
|
-
suppressionTimer = null;
|
|
33
|
-
}
|
|
34
|
-
});
|
|
35
|
-
}
|
|
16
|
+
if (suppressionTimer) {
|
|
17
|
+
clearTimeout(suppressionTimer);
|
|
18
|
+
}
|
|
36
19
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}) {
|
|
48
|
-
if (originalError?.response?.status === 402 || !shouldPropagateErrors) {
|
|
49
|
-
return;
|
|
20
|
+
suppressionTimer = setTimeout(() => {
|
|
21
|
+
shouldPropagateErrors = true;
|
|
22
|
+
suppressionTimer = null;
|
|
23
|
+
}, import.meta.env.VITE_HMR_ERROR_SUPPRESSION_DELAY ?? 10000);
|
|
24
|
+
});
|
|
25
|
+
import.meta.hot.on("vite:beforeFullReload", () => {
|
|
26
|
+
shouldPropagateErrors = false;
|
|
27
|
+
if (suppressionTimer) {
|
|
28
|
+
clearTimeout(suppressionTimer);
|
|
29
|
+
suppressionTimer = null;
|
|
50
30
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
type: "app_error",
|
|
54
|
-
error: {
|
|
55
|
-
title: title.toString(),
|
|
56
|
-
details: details?.toString(),
|
|
57
|
-
componentName: componentName?.toString(),
|
|
58
|
-
stack: originalError?.stack?.toString(),
|
|
59
|
-
},
|
|
60
|
-
},
|
|
61
|
-
"*"
|
|
62
|
-
);
|
|
63
|
-
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
64
33
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
34
|
+
function onAppError({
|
|
35
|
+
title,
|
|
36
|
+
details,
|
|
37
|
+
componentName,
|
|
38
|
+
originalError,
|
|
39
|
+
}: {
|
|
40
|
+
title: string;
|
|
41
|
+
details: string;
|
|
42
|
+
componentName: string;
|
|
43
|
+
originalError: any;
|
|
44
|
+
}) {
|
|
45
|
+
if (originalError?.response?.status === 402 || !shouldPropagateErrors) {
|
|
46
|
+
return;
|
|
78
47
|
}
|
|
48
|
+
window.parent?.postMessage(
|
|
49
|
+
{
|
|
50
|
+
type: "app_error",
|
|
51
|
+
error: {
|
|
52
|
+
title: title.toString(),
|
|
53
|
+
details: details?.toString(),
|
|
54
|
+
componentName: componentName?.toString(),
|
|
55
|
+
stack: originalError?.stack?.toString(),
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
"*"
|
|
59
|
+
);
|
|
60
|
+
}
|
|
79
61
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
62
|
+
function handleUnhandledRejection(event: any) {
|
|
63
|
+
const stack = event.reason.stack;
|
|
64
|
+
// extract function name from "at X (eval" where x is the function name
|
|
65
|
+
const functionName = stack.match(/at\s+(\w+)\s+\(eval/)?.[1];
|
|
66
|
+
const msg = functionName
|
|
67
|
+
? `Error in ${functionName}: ${event.reason.toString()}`
|
|
68
|
+
: event.reason.toString();
|
|
69
|
+
onAppError({
|
|
70
|
+
title: msg,
|
|
71
|
+
details: event.reason.toString(),
|
|
72
|
+
componentName: functionName,
|
|
73
|
+
originalError: event.reason,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
86
76
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
details: event.error.toString(),
|
|
93
|
-
componentName: functionName,
|
|
94
|
-
originalError: event.error,
|
|
95
|
-
});
|
|
77
|
+
function handleWindowError(event: any) {
|
|
78
|
+
const stack = event.error?.stack;
|
|
79
|
+
let functionName = stack.match(/at\s+(\w+)\s+\(eval/)?.[1];
|
|
80
|
+
if (functionName === "eval") {
|
|
81
|
+
functionName = null;
|
|
96
82
|
}
|
|
83
|
+
|
|
84
|
+
const msg = functionName
|
|
85
|
+
? `in ${functionName}: ${event.error.toString()}`
|
|
86
|
+
: event.error.toString();
|
|
87
|
+
onAppError({
|
|
88
|
+
title: msg,
|
|
89
|
+
details: event.error.toString(),
|
|
90
|
+
componentName: functionName,
|
|
91
|
+
originalError: event.error,
|
|
92
|
+
});
|
|
97
93
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { findElementsById, updateElementClasses } from "./utils.js";
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
if (window.self !== window.top) {
|
|
4
4
|
// State variables (replacing React useState/useRef)
|
|
5
5
|
let isVisualEditMode = false;
|
|
6
6
|
let isPopoverDragging = false;
|
|
@@ -216,6 +216,16 @@ export function setupVisualEditAgent() {
|
|
|
216
216
|
centerY: rect.top + rect.height / 2,
|
|
217
217
|
};
|
|
218
218
|
|
|
219
|
+
// Collect allowed element attributes
|
|
220
|
+
const ALLOWED_ATTRIBUTES: string[] = ["src"];
|
|
221
|
+
const attributes: Record<string, string> = {};
|
|
222
|
+
for (const attr of ALLOWED_ATTRIBUTES) {
|
|
223
|
+
const val = element.getAttribute(attr);
|
|
224
|
+
if (val !== null) {
|
|
225
|
+
attributes[attr] = val;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
219
229
|
// Send message to parent window with element info including position
|
|
220
230
|
const svgElement = element as SVGElement;
|
|
221
231
|
const elementData = {
|
|
@@ -232,6 +242,7 @@ export function setupVisualEditAgent() {
|
|
|
232
242
|
linenumber: htmlElement.dataset.linenumber,
|
|
233
243
|
filename: htmlElement.dataset.filename,
|
|
234
244
|
position: elementPosition,
|
|
245
|
+
attributes,
|
|
235
246
|
};
|
|
236
247
|
window.parent.postMessage(elementData, "*");
|
|
237
248
|
};
|
|
@@ -279,6 +290,31 @@ export function setupVisualEditAgent() {
|
|
|
279
290
|
}, 50);
|
|
280
291
|
};
|
|
281
292
|
|
|
293
|
+
// Update element attribute by visual selector ID
|
|
294
|
+
const updateElementAttribute = (
|
|
295
|
+
visualSelectorId: string,
|
|
296
|
+
attribute: string,
|
|
297
|
+
value: string
|
|
298
|
+
) => {
|
|
299
|
+
const elements = findElementsById(visualSelectorId);
|
|
300
|
+
if (elements.length === 0) return;
|
|
301
|
+
|
|
302
|
+
elements.forEach((element) => {
|
|
303
|
+
element.setAttribute(attribute, value);
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
// Reposition overlays after attribute change (e.g. image src swap can affect layout)
|
|
307
|
+
setTimeout(() => {
|
|
308
|
+
if (selectedElementId === visualSelectorId) {
|
|
309
|
+
selectedOverlays.forEach((overlay, index) => {
|
|
310
|
+
if (index < elements.length) {
|
|
311
|
+
positionOverlay(overlay, elements[index]!);
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
}, 50);
|
|
316
|
+
};
|
|
317
|
+
|
|
282
318
|
// Update element content by visual selector ID
|
|
283
319
|
const updateElementContent = (visualSelectorId: string, content: string) => {
|
|
284
320
|
const elements = findElementsById(visualSelectorId);
|
|
@@ -394,6 +430,25 @@ export function setupVisualEditAgent() {
|
|
|
394
430
|
}
|
|
395
431
|
break;
|
|
396
432
|
|
|
433
|
+
case "update-attribute":
|
|
434
|
+
if (
|
|
435
|
+
message.data &&
|
|
436
|
+
message.data.attribute !== undefined &&
|
|
437
|
+
message.data.value !== undefined
|
|
438
|
+
) {
|
|
439
|
+
updateElementAttribute(
|
|
440
|
+
message.data.visualSelectorId,
|
|
441
|
+
message.data.attribute,
|
|
442
|
+
message.data.value
|
|
443
|
+
);
|
|
444
|
+
} else {
|
|
445
|
+
console.warn(
|
|
446
|
+
"[VisualEditAgent] Invalid update-attribute message:",
|
|
447
|
+
message
|
|
448
|
+
);
|
|
449
|
+
}
|
|
450
|
+
break;
|
|
451
|
+
|
|
397
452
|
case "unselect-element":
|
|
398
453
|
unselectElement();
|
|
399
454
|
break;
|
|
@@ -558,4 +613,4 @@ export function setupVisualEditAgent() {
|
|
|
558
613
|
|
|
559
614
|
// Send ready message to parent
|
|
560
615
|
window.parent.postMessage({ type: "visual-edit-agent-ready" }, "*");
|
|
561
|
-
}
|
|
616
|
+
}
|
package/README.md
DELETED
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
# @base44/vite-plugin
|
|
2
|
-
|
|
3
|
-
Vite plugin for Base44 applications running in sandboxed iframes. Provides error tracking, HMR notifications, visual editing, navigation tracking, and legacy SDK compatibility.
|
|
4
|
-
|
|
5
|
-
## Installation
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
npm install @base44/vite-plugin
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
## Usage
|
|
12
|
-
|
|
13
|
-
```ts
|
|
14
|
-
// vite.config.ts
|
|
15
|
-
import base44 from "@base44/vite-plugin";
|
|
16
|
-
|
|
17
|
-
export default {
|
|
18
|
-
plugins: [
|
|
19
|
-
base44({
|
|
20
|
-
legacySDKImports: false,
|
|
21
|
-
hmrNotifier: true,
|
|
22
|
-
navigationNotifier: true,
|
|
23
|
-
visualEditAgent: true,
|
|
24
|
-
}),
|
|
25
|
-
],
|
|
26
|
-
};
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
### Options
|
|
30
|
-
|
|
31
|
-
| Option | Type | Default | Description |
|
|
32
|
-
| -------------------- | --------- | ------- | --------------------------------------------------- |
|
|
33
|
-
| `legacySDKImports` | `boolean` | `false` | Enable legacy SDK import resolution via compat layer |
|
|
34
|
-
| `hmrNotifier` | `boolean` | `false` | Notify parent window of HMR update lifecycle |
|
|
35
|
-
| `navigationNotifier` | `boolean` | `false` | Track URL changes and notify parent window |
|
|
36
|
-
| `visualEditAgent` | `boolean` | `false` | Enable interactive visual element editing |
|
|
37
|
-
|
|
38
|
-
## Architecture
|
|
39
|
-
|
|
40
|
-
### Plugin Composition
|
|
41
|
-
|
|
42
|
-
The plugin registers four sub-plugins when running in a sandbox (`MODAL_SANDBOX_ID` is set):
|
|
43
|
-
|
|
44
|
-
1. **base44** — Core configuration: path aliases (`@/` → `/src/`), environment variables, dependency optimization, and legacy SDK import resolution.
|
|
45
|
-
2. **iframe-hmr** — Sets CORS and `frame-ancestors` headers to allow iframe embedding.
|
|
46
|
-
3. **error-overlay** — Replaces Vite's default error overlay with a custom one that reports errors to the parent window.
|
|
47
|
-
4. **html-injections** — Injects the sandbox bridge script into the HTML.
|
|
48
|
-
|
|
49
|
-
Outside a sandbox, only the core plugin runs (with optional API proxy support via `VITE_BASE44_APP_BASE_URL`).
|
|
50
|
-
|
|
51
|
-
### Sandbox Bridge
|
|
52
|
-
|
|
53
|
-
The bridge (`src/bridge.ts`) is a single ES module that consolidates all sandbox-side features. Instead of injecting multiple individual `<script>` tags, the HTML injections plugin injects one inline script that dynamically imports the bridge and calls `init()` with the configured options.
|
|
54
|
-
|
|
55
|
-
The bridge conditionally sets up:
|
|
56
|
-
|
|
57
|
-
- **Error handlers** — Global `error` and `unhandledrejection` listeners that report to the parent via `postMessage`.
|
|
58
|
-
- **Mount observer** — `MutationObserver` that detects when instrumented elements (`data-source-location`) are rendered.
|
|
59
|
-
- **HMR notifier** — Forwards Vite's `beforeUpdate`/`afterUpdate` events to the parent.
|
|
60
|
-
- **Navigation notifier** — Intercepts `pushState`, `replaceState`, and `popstate` to track URL changes.
|
|
61
|
-
- **Visual edit agent** — Overlay system for selecting and editing elements with source location awareness.
|
|
62
|
-
|
|
63
|
-
### Local Development
|
|
64
|
-
|
|
65
|
-
For developing the bridge itself, you can load it from a local dev server instead of the installed package:
|
|
66
|
-
|
|
67
|
-
1. Generate local HTTPS certificates:
|
|
68
|
-
```bash
|
|
69
|
-
mkcert localhost
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
2. Start the dev server (with optional watch mode):
|
|
73
|
-
```bash
|
|
74
|
-
npm run dev # serve dist/statics/ on https://localhost:3201
|
|
75
|
-
npm run dev -- -w # same, with auto-rebuild on changes
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
3. Add `?sandbox-bridge=local` to your app's URL to load the bridge from `localhost:3201` instead of `node_modules`.
|
|
79
|
-
|
|
80
|
-
### Legacy SDK Compatibility
|
|
81
|
-
|
|
82
|
-
When `legacySDKImports` is enabled, imports of `/entities`, `/functions`, `/integrations`, and `/agents` are resolved to compatibility modules in `compat/`. These use `Proxy` objects to route calls to the Base44 SDK client.
|
|
83
|
-
|
|
84
|
-
## Building
|
|
85
|
-
|
|
86
|
-
```bash
|
|
87
|
-
npm run build # TypeScript compilation (plugin)
|
|
88
|
-
npm run build:bridge # tsup bundle (bridge ES module for browsers)
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
## License
|
|
92
|
-
|
|
93
|
-
MIT
|
package/dist/bridge.d.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Sandbox bridge entry point — dynamically imported by the inline script
|
|
3
|
-
* injected via html-injections-plugin. Conditionally initialises each
|
|
4
|
-
* sandbox-side feature based on the supplied options.
|
|
5
|
-
*/
|
|
6
|
-
/**
|
|
7
|
-
* Initialise the sandbox bridge. Called once from the injected inline script.
|
|
8
|
-
* @param options Feature flags — each defaults to enabled unless explicitly disabled.
|
|
9
|
-
* @param hmr Vite's HMR client (`import.meta.hot`), passed only in dev mode.
|
|
10
|
-
*/
|
|
11
|
-
export declare function init(options?: {
|
|
12
|
-
unhandledErrors?: boolean;
|
|
13
|
-
mountObserver?: boolean;
|
|
14
|
-
hmrNotifier?: boolean;
|
|
15
|
-
navigationNotifier?: boolean;
|
|
16
|
-
visualEditAgent?: boolean;
|
|
17
|
-
}, hmr?: {
|
|
18
|
-
on(event: string, cb: (...args: any[]) => void): void;
|
|
19
|
-
}): void;
|
|
20
|
-
//# sourceMappingURL=bridge.d.ts.map
|
package/dist/bridge.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAQH;;;;GAIG;AACH,wBAAgB,IAAI,CAClB,OAAO,GAAE;IACP,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,eAAe,CAAC,EAAE,OAAO,CAAC;CACtB,EACN,GAAG,CAAC,EAAE;IAAE,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,GAAG,IAAI,CAAA;CAAE,QAWhE"}
|