feat: add optional extension hooks for private overlays
Three small, backwards-compatible hooks let anyone with private features
(velxio.dev's analytics, custom integrations, paid tiers, …) layer them
on top of the open-source build without forking files.
Backend (app/main.py):
- After standard router registration, try-import an optional `app.pro`
module exposing `register_pro(app)`. ImportError is silently swallowed
(the OSS image doesn't ship `app.pro`, so this is a no-op there).
Frontend:
- EditorToolbar: new optional `rightSlot` prop renders extra elements
after the built-in right-group buttons (mirrors the existing
`centerSlot` pattern).
- main.tsx: dynamic `import('@pro/index')` gated by VITE_PRO_BUILD env.
When unset (OSS build), the branch is dead-code-eliminated and no pro
chunk is emitted.
- vite.config.ts: `@pro` alias resolves to `src/__pro_stub__/` by default.
Private builds set `VITE_PRO_BUILD=true` and `PRO_OVERLAY_PATH=<path>`
to point at their real overlay tree.
- src/__pro_stub__/index.ts: 1-line no-op `mountPro` so TypeScript and
Vite resolvers stay happy in OSS builds.
Verified: `npm run build:docker` succeeds; `npm test` passes 1161/1162;
the OSS bundle (43 MB) contains zero references to `__pro_stub__`,
`@pro`, or `pro/index` (verified via `grep dist/`).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
156d89c61d
commit
edd2ac32d5
|
|
@ -126,6 +126,17 @@ app.include_router(simulation.router, prefix="/api/simulation", tags=["simulatio
|
||||||
from app.api.routes import iot_gateway
|
from app.api.routes import iot_gateway
|
||||||
app.include_router(iot_gateway.router, prefix="/api/gateway", tags=["iot-gateway"])
|
app.include_router(iot_gateway.router, prefix="/api/gateway", tags=["iot-gateway"])
|
||||||
|
|
||||||
|
# Optional pro extension. The `app.pro` package only exists in private builds
|
||||||
|
# (overlaid at Docker build time by an external repo) — its absence in the
|
||||||
|
# open-source image is expected and silently ignored. Anyone with private
|
||||||
|
# extensions can drop a package at `backend/app/pro/` exposing
|
||||||
|
# `register_pro(app)` and have it auto-loaded here without further edits.
|
||||||
|
try:
|
||||||
|
from app.pro import register_pro # type: ignore[import-not-found]
|
||||||
|
register_pro(app)
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
@app.get("/")
|
@app.get("/")
|
||||||
def root():
|
def root():
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,7 @@
|
||||||
|
// Open-source no-op stub for the @pro alias (see vite.config.ts).
|
||||||
|
// Private overlays (e.g. velxio-prod) replace this at build time by setting
|
||||||
|
// VITE_PRO_BUILD=true and PRO_OVERLAY_PATH to their real pro/frontend/src/pro
|
||||||
|
// directory. The dynamic `import('@pro/index')` in main.tsx is gated by
|
||||||
|
// VITE_PRO_BUILD, so this stub is unreachable in OSS builds — it exists only
|
||||||
|
// to keep the TypeScript and Vite resolvers happy.
|
||||||
|
export const mountPro = () => {};
|
||||||
|
|
@ -33,6 +33,12 @@ interface EditorToolbarProps {
|
||||||
* regardless of how narrow the editor pane gets.
|
* regardless of how narrow the editor pane gets.
|
||||||
*/
|
*/
|
||||||
centerSlot?: React.ReactNode;
|
centerSlot?: React.ReactNode;
|
||||||
|
/**
|
||||||
|
* Optional extra elements rendered after the built-in right-group buttons
|
||||||
|
* (Libraries / Import-Export / Output Console). Used by private overlays
|
||||||
|
* to add deployment-specific actions without forking the toolbar.
|
||||||
|
*/
|
||||||
|
rightSlot?: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
const BOARD_PILL_ICON: Record<BoardKind, string> = {
|
const BOARD_PILL_ICON: Record<BoardKind, string> = {
|
||||||
|
|
@ -63,6 +69,7 @@ export const EditorToolbar = ({
|
||||||
compileLogs: _compileLogs,
|
compileLogs: _compileLogs,
|
||||||
setCompileLogs,
|
setCompileLogs,
|
||||||
centerSlot,
|
centerSlot,
|
||||||
|
rightSlot,
|
||||||
}: EditorToolbarProps) => {
|
}: EditorToolbarProps) => {
|
||||||
const { files, codeChangedSinceLastCompile, markCompiled } = useEditorStore();
|
const { files, codeChangedSinceLastCompile, markCompiled } = useEditorStore();
|
||||||
const {
|
const {
|
||||||
|
|
@ -973,6 +980,7 @@ export const EditorToolbar = ({
|
||||||
<line x1="12" y1="19" x2="20" y2="19" />
|
<line x1="12" y1="19" x2="20" y2="19" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
|
{rightSlot}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -15,3 +15,13 @@ import './components/velxio-components/EPaperElement';
|
||||||
import App from './App.tsx';
|
import App from './App.tsx';
|
||||||
|
|
||||||
createRoot(document.getElementById('root')!).render(<App />);
|
createRoot(document.getElementById('root')!).render(<App />);
|
||||||
|
|
||||||
|
// Optional pro overlay. The `@pro` import resolves to a no-op stub in the
|
||||||
|
// open-source build (see vite.config.ts) and to the real overlay only when
|
||||||
|
// VITE_PRO_BUILD=true at build time. The dynamic import keeps the pro chunk
|
||||||
|
// out of the OSS bundle entirely (Vite tree-shakes the never-taken branch).
|
||||||
|
if (import.meta.env.VITE_PRO_BUILD) {
|
||||||
|
import('@pro/index')
|
||||||
|
.then((m) => m.mountPro?.())
|
||||||
|
.catch((err) => console.warn('[pro] failed to load overlay:', err));
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,27 @@
|
||||||
import { defineConfig } from 'vite'
|
import { defineConfig } from 'vite'
|
||||||
import react from '@vitejs/plugin-react'
|
import react from '@vitejs/plugin-react'
|
||||||
|
import path from 'path'
|
||||||
|
|
||||||
// https://vite.dev/config/
|
// https://vite.dev/config/
|
||||||
// avr8js / rp2040js / @wokwi/elements are resolved from npm via package.json.
|
// avr8js / rp2040js / @wokwi/elements are resolved from npm via package.json.
|
||||||
// (The third-party/ clones are reference-only — keep them updated for credits.)
|
// (The third-party/ clones are reference-only — keep them updated for credits.)
|
||||||
|
//
|
||||||
|
// The `@pro` alias resolves to a no-op stub by default. Private overlays
|
||||||
|
// (e.g. velxio-prod) set VITE_PRO_BUILD=true and PRO_OVERLAY_PATH at build
|
||||||
|
// time to point at their actual pro source tree. See README's "Pro overlay"
|
||||||
|
// section.
|
||||||
|
const proOverlayPath =
|
||||||
|
process.env.VITE_PRO_BUILD && process.env.PRO_OVERLAY_PATH
|
||||||
|
? path.resolve(process.env.PRO_OVERLAY_PATH)
|
||||||
|
: path.resolve(__dirname, 'src/__pro_stub__')
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [react()],
|
plugins: [react()],
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'@pro': proOverlayPath,
|
||||||
|
},
|
||||||
|
},
|
||||||
server: {
|
server: {
|
||||||
proxy: {
|
proxy: {
|
||||||
'/api': {
|
'/api': {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue