@embeddables/cli 0.3.1 → 0.3.3

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.
@@ -1 +1 @@
1
- {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAuGA,wBAAsB,OAAO,CAAC,IAAI,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,OAAO,CAAA;CAAE,iBA0KxE"}
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAuGA,wBAAsB,OAAO,CAAC,IAAI,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,OAAO,CAAA;CAAE,iBA8LxE"}
@@ -216,6 +216,20 @@ export async function runInit(opts) {
216
216
  else {
217
217
  console.log(pc.gray(' ✓ tsconfig.json already exists'));
218
218
  }
219
+ // Migrate .types from old location (embeddables/.types/) to project root (.types/)
220
+ const oldTypesDir = path.join(embeddablesDir, '.types');
221
+ if (fs.existsSync(oldTypesDir)) {
222
+ fs.rmSync(oldTypesDir, { recursive: true });
223
+ console.log(pc.green(' ✓ Removed old embeddables/.types/'));
224
+ }
225
+ // Fix tsconfig paths if they still reference the old embeddables/.types/ location
226
+ if (fs.existsSync(tsconfigPath)) {
227
+ const tsconfigContent = fs.readFileSync(tsconfigPath, 'utf8');
228
+ if (tsconfigContent.includes('./embeddables/.types/')) {
229
+ fs.writeFileSync(tsconfigPath, tsconfigContent.replaceAll('./embeddables/.types/', './.types/'), 'utf8');
230
+ console.log(pc.green(' ✓ Updated tsconfig.json paths'));
231
+ }
232
+ }
219
233
  // Generate type declaration stubs for editor support (no npm install needed)
220
234
  writeTypeStubs(cwd);
221
235
  console.log(pc.green(' ✓ Generated type declarations'));
@@ -4,4 +4,6 @@ export declare const BranchStatus: {
4
4
  };
5
5
  export type BranchStatus = (typeof BranchStatus)[keyof typeof BranchStatus];
6
6
  export declare const WEB_APP_BASE_URL = "https://embeddables-web-fpnc2wscy-embeddables.vercel.app";
7
+ /** CDN origin that hosts the pre-built workbench assets (workbench.js / workbench.css). */
8
+ export declare const WORKBENCH_CDN_ORIGIN = "https://embeddables-workbench.heysavvy.workers.dev";
7
9
  //# sourceMappingURL=constants.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,YAAY;;;CAGf,CAAA;AAEV,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,OAAO,YAAY,CAAC,CAAA;AAG3E,eAAO,MAAM,gBAAgB,6DAA6D,CAAA"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,YAAY;;;CAGf,CAAA;AAEV,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,OAAO,YAAY,CAAC,CAAA;AAG3E,eAAO,MAAM,gBAAgB,6DAA6D,CAAA;AAG1F,2FAA2F;AAC3F,eAAO,MAAM,oBAAoB,uDAAuD,CAAA"}
package/dist/constants.js CHANGED
@@ -5,3 +5,5 @@ export const BranchStatus = {
5
5
  // export const WEB_APP_BASE_URL = 'https://app.embeddables.com'
6
6
  export const WEB_APP_BASE_URL = 'https://embeddables-web-fpnc2wscy-embeddables.vercel.app';
7
7
  // export const WEB_APP_BASE_URL = 'http://localhost:3000'
8
+ /** CDN origin that hosts the pre-built workbench assets (workbench.js / workbench.css). */
9
+ export const WORKBENCH_CDN_ORIGIN = 'https://embeddables-workbench.heysavvy.workers.dev';
@@ -1,4 +1,5 @@
1
1
  export declare function injectWorkbenchHtml(html: string, opts: {
2
2
  embeddableId: string;
3
+ workbenchOrigin?: string;
3
4
  }): string;
4
5
  //# sourceMappingURL=injectWorkbench.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"injectWorkbench.d.ts","sourceRoot":"","sources":["../../src/proxy/injectWorkbench.ts"],"names":[],"mappings":"AAAA,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE;IAAE,YAAY,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAsBxF"}
1
+ {"version":3,"file":"injectWorkbench.d.ts","sourceRoot":"","sources":["../../src/proxy/injectWorkbench.ts"],"names":[],"mappings":"AAAA,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAAE,GACvD,MAAM,CA8BR"}
@@ -1,12 +1,18 @@
1
1
  export function injectWorkbenchHtml(html, opts) {
2
2
  const lower = html.toLowerCase();
3
3
  const bodyCloseIndex = lower.lastIndexOf('</body>');
4
+ const isLocalDev = !opts.workbenchOrigin;
4
5
  const container = '<div id="__embeddables_workbench_root"></div>';
5
6
  const configScript = `<script>(function(){` +
6
7
  'window.__EMBEDDABLES_WORKBENCH = window.__EMBEDDABLES_WORKBENCH || {};' +
7
8
  `window.__EMBEDDABLES_WORKBENCH.embeddableId = ${JSON.stringify(opts.embeddableId)};` +
9
+ `window.__EMBEDDABLES_WORKBENCH.localDev = ${JSON.stringify(isLocalDev)};` +
8
10
  '})();</script>';
9
- const scriptTag = `<script src="/__embeddables_workbench.js" onload="if(typeof window.__embeddables_bootstrap_workbench==='function'){window.__embeddables_bootstrap_workbench({embeddableId:window.__EMBEDDABLES_WORKBENCH.embeddableId});}"></script>`;
11
+ // When a remote origin is provided, load from the CDN; otherwise use the local proxy path.
12
+ const jsUrl = opts.workbenchOrigin
13
+ ? `${opts.workbenchOrigin}/workbench.js`
14
+ : '/__embeddables_workbench.js';
15
+ const scriptTag = `<script src="${jsUrl}" onload="if(typeof window.__embeddables_bootstrap_workbench==='function'){window.__embeddables_bootstrap_workbench({embeddableId:window.__EMBEDDABLES_WORKBENCH.embeddableId,localDev:window.__EMBEDDABLES_WORKBENCH.localDev});}"></script>`;
10
16
  const injection = `\n${container}\n${configScript}\n${scriptTag}\n`;
11
17
  if (bodyCloseIndex === -1) {
12
18
  // No </body> tag found; append at the end
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/proxy/server.ts"],"names":[],"mappings":"AAqBA,wBAAsB,gBAAgB,CAAC,IAAI,EAAE;IAC3C,IAAI,EAAE,MAAM,CAAA;IACZ,YAAY,EAAE,MAAM,CAAA;IACpB,aAAa,EAAE,MAAM,CAAA;IACrB,iBAAiB,EAAE,MAAM,CAAA;IACzB,YAAY,EAAE,MAAM,CAAA;IACpB,cAAc,CAAC,EAAE,OAAO,CAAA;CACzB;;GAqRA"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/proxy/server.ts"],"names":[],"mappings":"AAmCA,wBAAsB,gBAAgB,CAAC,IAAI,EAAE;IAC3C,IAAI,EAAE,MAAM,CAAA;IACZ,YAAY,EAAE,MAAM,CAAA;IACpB,aAAa,EAAE,MAAM,CAAA;IACrB,iBAAiB,EAAE,MAAM,CAAA;IACzB,YAAY,EAAE,MAAM,CAAA;IACpB,cAAc,CAAC,EAAE,OAAO,CAAA;CACzB;;GA2SA"}
@@ -12,21 +12,44 @@ import { attachSse } from './sse.js';
12
12
  import { injectWorkbenchHtml } from './injectWorkbench.js';
13
13
  import { injectReloadScript } from './injectReload.js';
14
14
  import { injectApiInterceptor } from './injectApiInterceptor.js';
15
+ import { WORKBENCH_CDN_ORIGIN } from '../constants.js';
15
16
  // Resolve the CLI package root so workbench paths work regardless of cwd.
16
17
  // This file lives at src/proxy/server.ts (or dist/proxy/server.js when compiled),
17
18
  // so the package root is two directories up.
18
19
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
19
20
  const CLI_ROOT = path.resolve(__dirname, '..', '..');
21
+ /**
22
+ * Detect whether the CLI is running from a local development checkout
23
+ * (e.g. via `npm link` or `tsx`) rather than a regular `npm install -g`.
24
+ *
25
+ * Heuristic: the `src/workbench/` source directory only exists in the repo
26
+ * checkout — it is excluded from the published package (`files` in package.json
27
+ * only ships `bin/` and `dist/`).
28
+ */
29
+ function isLocalDev() {
30
+ const workbenchSrcDir = path.join(CLI_ROOT, 'src', 'workbench', 'index.tsx');
31
+ return fs.existsSync(workbenchSrcDir);
32
+ }
20
33
  export async function startProxyServer(opts) {
21
34
  const app = express();
35
+ const localDev = isLocalDev();
36
+ // ---------- Local-dev mode: build workbench from source ----------
22
37
  // Resolve workbench paths relative to the CLI package root, not process.cwd()
23
38
  const workbenchEntry = path.join(CLI_ROOT, 'src', 'workbench', 'index.tsx');
24
39
  const workbenchDir = path.join(CLI_ROOT, 'src', 'workbench');
25
40
  const workbenchCssEntry = path.join(CLI_ROOT, 'src', 'workbench', 'workbench.css');
26
- // Build the Workbench bundle. If it fails, we log the error and
27
- // keep the proxy running; HTML injection will still happen but the script may 404.
28
41
  let workbenchBundle = null;
29
42
  let workbenchCss = null;
43
+ // The origin used when injecting the workbench <script>/<link> tags.
44
+ // undefined → local proxy paths (/__embeddables_workbench.{js,css})
45
+ // string → remote CDN origin (e.g. https://embeddables-workbench.heysavvy.workers.dev)
46
+ const workbenchOrigin = localDev ? undefined : WORKBENCH_CDN_ORIGIN;
47
+ if (localDev) {
48
+ console.log('[Workbench] Local dev mode — building workbench from source');
49
+ }
50
+ else {
51
+ console.log(`[Workbench] Installed mode — loading workbench from CDN (${WORKBENCH_CDN_ORIGIN})`);
52
+ }
30
53
  const buildWorkbench = async () => {
31
54
  try {
32
55
  const result = await esbuild.build({
@@ -63,14 +86,16 @@ export async function startProxyServer(opts) {
63
86
  return false;
64
87
  }
65
88
  };
66
- // Initial build
67
- await buildWorkbench();
89
+ // Only build from source in local-dev mode
90
+ if (localDev) {
91
+ await buildWorkbench();
92
+ }
68
93
  // Add JSON body parsing middleware
69
94
  app.use(express.json());
70
95
  app.use(express.urlencoded({ extended: true }));
71
96
  const { broadcastReload } = attachSse(app);
72
- // Watch workbench files if enabled
73
- if (opts.watchWorkbench) {
97
+ // Watch workbench files if enabled (only meaningful in local-dev mode)
98
+ if (localDev && opts.watchWorkbench) {
74
99
  const workbenchGlob = path.join(workbenchDir, '**/*');
75
100
  const watcher = chokidar.watch(workbenchGlob, {
76
101
  ignoreInitial: true,
@@ -89,31 +114,33 @@ export async function startProxyServer(opts) {
89
114
  });
90
115
  console.log(`[Workbench] Watching ${workbenchGlob} for changes`);
91
116
  }
92
- // Serve the Workbench bundle as a static asset
93
- app.get('/__embeddables_workbench.js', (_req, res) => {
94
- if (!workbenchBundle) {
95
- res
96
- .status(503)
97
- .type('text/plain')
98
- .send('Workbench bundle is not available. Check dev server logs.');
99
- return;
100
- }
101
- res.setHeader('Content-Type', 'application/javascript; charset=utf-8');
102
- res.setHeader('Cache-Control', 'no-store');
103
- res.send(Buffer.from(workbenchBundle));
104
- });
105
- app.get('/__embeddables_workbench.css', (_req, res) => {
106
- if (!workbenchCss) {
107
- res
108
- .status(503)
109
- .type('text/plain')
110
- .send('Workbench CSS is not available. Check dev server logs.');
111
- return;
112
- }
113
- res.setHeader('Content-Type', 'text/css; charset=utf-8');
114
- res.setHeader('Cache-Control', 'no-store');
115
- res.send(workbenchCss);
116
- });
117
+ // Serve the Workbench bundle as a static asset (local-dev mode only)
118
+ if (localDev) {
119
+ app.get('/__embeddables_workbench.js', (_req, res) => {
120
+ if (!workbenchBundle) {
121
+ res
122
+ .status(503)
123
+ .type('text/plain')
124
+ .send('Workbench bundle is not available. Check dev server logs.');
125
+ return;
126
+ }
127
+ res.setHeader('Content-Type', 'application/javascript; charset=utf-8');
128
+ res.setHeader('Cache-Control', 'no-store');
129
+ res.send(Buffer.from(workbenchBundle));
130
+ });
131
+ app.get('/__embeddables_workbench.css', (_req, res) => {
132
+ if (!workbenchCss) {
133
+ res
134
+ .status(503)
135
+ .type('text/plain')
136
+ .send('Workbench CSS is not available. Check dev server logs.');
137
+ return;
138
+ }
139
+ res.setHeader('Content-Type', 'text/css; charset=utf-8');
140
+ res.setHeader('Cache-Control', 'no-store');
141
+ res.send(workbenchCss);
142
+ });
143
+ }
117
144
  // Intercept GET requests to overrideRoute: convert to POST and add generated JSON to body
118
145
  app.get(opts.overrideRoute, async (req, res) => {
119
146
  console.log(`[/init] Intercepted GET ${opts.overrideRoute}, query:`, req.query);
@@ -213,6 +240,7 @@ export async function startProxyServer(opts) {
213
240
  modifiedHtml = injectReloadScript(modifiedHtml);
214
241
  modifiedHtml = injectWorkbenchHtml(modifiedHtml, {
215
242
  embeddableId: opts.embeddableId,
243
+ workbenchOrigin,
216
244
  });
217
245
  console.log(`[Workbench] Injection complete (${modifiedHtml.length} chars), sending response`);
218
246
  res.setHeader('Content-Type', 'text/html; charset=utf-8');
@@ -247,6 +275,12 @@ export async function startProxyServer(opts) {
247
275
  console.log(`Override route: GET ${opts.overrideRoute}`);
248
276
  console.log(`Serving: ${opts.generatedJsonPath}`);
249
277
  console.log(`Reload SSE: /__embeddables_reload`);
278
+ if (localDev) {
279
+ console.log(`Workbench: built from local source`);
280
+ }
281
+ else {
282
+ console.log(`Workbench: served from ${WORKBENCH_CDN_ORIGIN}`);
283
+ }
250
284
  });
251
285
  return { broadcastReload };
252
286
  }
@@ -1,6 +1,7 @@
1
1
  type WorkbenchAppProps = {
2
2
  embeddableId: string;
3
+ localDev: boolean;
3
4
  };
4
- export declare function WorkbenchApp({ embeddableId }: WorkbenchAppProps): import("react/jsx-runtime").JSX.Element;
5
+ export declare function WorkbenchApp({ embeddableId, localDev }: WorkbenchAppProps): import("react/jsx-runtime").JSX.Element;
5
6
  export {};
6
7
  //# sourceMappingURL=WorkbenchApp.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"WorkbenchApp.d.ts","sourceRoot":"","sources":["../../src/workbench/WorkbenchApp.tsx"],"names":[],"mappings":"AAuBA,KAAK,iBAAiB,GAAG;IACvB,YAAY,EAAE,MAAM,CAAA;CACrB,CAAA;AAqCD,wBAAgB,YAAY,CAAC,EAAE,YAAY,EAAE,EAAE,iBAAiB,2CA6S/D"}
1
+ {"version":3,"file":"WorkbenchApp.d.ts","sourceRoot":"","sources":["../../src/workbench/WorkbenchApp.tsx"],"names":[],"mappings":"AAuBA,KAAK,iBAAiB,GAAG;IACvB,YAAY,EAAE,MAAM,CAAA;IACpB,QAAQ,EAAE,OAAO,CAAA;CAClB,CAAA;AAqCD,wBAAgB,YAAY,CAAC,EAAE,YAAY,EAAE,QAAQ,EAAE,EAAE,iBAAiB,2CA6SzE"}
@@ -34,7 +34,7 @@ function savePrefs(embeddableId, prefs) {
34
34
  // ignore
35
35
  }
36
36
  }
37
- export function WorkbenchApp({ embeddableId }) {
37
+ export function WorkbenchApp({ embeddableId, localDev }) {
38
38
  const [collapsed, setCollapsed] = useState(true);
39
39
  const [height, setHeight] = useState(260);
40
40
  const [activeTab, setActiveTab] = useState('user-data');
@@ -177,7 +177,7 @@ export function WorkbenchApp({ embeddableId }) {
177
177
  // Avoid toggling collapse when clicking the drag handle.
178
178
  event.preventDefault();
179
179
  event.stopPropagation();
180
- }, className: "absolute left-1/2 z-10 -translate-x-1/2 -top-1.5 h-3 w-16 cursor-ns-resize touch-none rounded-full border border-slate-500 bg-slate-400 shadow-md hover:bg-slate-300 transition-colors duration-200" })), _jsxs("div", { className: "relative flex items-center justify-between gap-3 border-t border-white/10 bg-slate-950/80 px-3 py-1.5 text-slate-200 shadow-2xl shadow-black/40 backdrop-blur supports-backdrop-filter:bg-slate-950/70", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsxs("div", { className: "flex cursor-pointer items-center gap-2", onClick: toggleCollapsed, children: [_jsx("div", { className: "text-xs font-semibold tracking-wide text-slate-100", children: "Embeddables Workbench" }), _jsx("div", { className: "rounded-full bg-white/10 px-2 py-0.5 text-[10px] font-medium text-slate-200 ring-1 ring-inset ring-white/10", children: "Beta" })] }), !collapsed && (_jsxs("div", { className: "ml-8 flex items-center gap-1", children: [_jsx("button", { type: "button", onClick: () => switchTab('user-data'), className: `cursor-pointer rounded-lg px-2.5 py-1 text-[11px] font-medium transition-colors ${activeTab === 'user-data'
180
+ }, className: "absolute left-1/2 z-10 -translate-x-1/2 -top-1.5 h-3 w-16 cursor-ns-resize touch-none rounded-full border border-slate-500 bg-slate-400 shadow-md hover:bg-slate-300 transition-colors duration-200" })), _jsxs("div", { className: "relative flex items-center justify-between gap-3 border-t border-white/10 bg-slate-950/80 px-3 py-1.5 text-slate-200 shadow-2xl shadow-black/40 backdrop-blur supports-backdrop-filter:bg-slate-950/70", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsxs("div", { className: "flex cursor-pointer items-center gap-2", onClick: toggleCollapsed, children: [_jsx("div", { className: "text-xs font-semibold tracking-wide text-slate-100", children: "Embeddables Workbench" }), _jsx("div", { className: "rounded-full bg-white/10 px-2 py-0.5 text-[10px] font-medium text-slate-200 ring-1 ring-inset ring-white/10", children: localDev ? 'Local' : 'Beta' })] }), !collapsed && (_jsxs("div", { className: "ml-8 flex items-center gap-1", children: [_jsx("button", { type: "button", onClick: () => switchTab('user-data'), className: `cursor-pointer rounded-lg px-2.5 py-1 text-[11px] font-medium transition-colors ${activeTab === 'user-data'
181
181
  ? 'bg-white/15 text-white ring-1 ring-inset ring-white/20'
182
182
  : 'text-slate-400 hover:bg-white/5 hover:text-slate-200'}`, children: "User Data" }), _jsx("button", { type: "button", onClick: () => switchTab('experiments'), className: `cursor-pointer rounded-lg px-2.5 py-1 text-[11px] font-medium transition-colors ${activeTab === 'experiments'
183
183
  ? 'bg-white/15 text-white ring-1 ring-inset ring-white/20'
@@ -2,6 +2,7 @@ declare global {
2
2
  interface Window {
3
3
  __embeddables_bootstrap_workbench?: (config: {
4
4
  embeddableId: string;
5
+ localDev?: boolean;
5
6
  }) => void;
6
7
  }
7
8
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/workbench/index.tsx"],"names":[],"mappings":"AAIA,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,iCAAiC,CAAC,EAAE,CAAC,MAAM,EAAE;YAAE,YAAY,EAAE,MAAM,CAAA;SAAE,KAAK,IAAI,CAAA;KAC/E;CACF"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/workbench/index.tsx"],"names":[],"mappings":"AAIA,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,iCAAiC,CAAC,EAAE,CAAC,MAAM,EAAE;YAC3C,YAAY,EAAE,MAAM,CAAA;YACpB,QAAQ,CAAC,EAAE,OAAO,CAAA;SACnB,KAAK,IAAI,CAAA;KACX;CACF"}
@@ -33,12 +33,12 @@ async function bootstrapWorkbench(config) {
33
33
  workbenchRoot = createRoot(mountPoint);
34
34
  }
35
35
  if (workbenchRoot) {
36
- workbenchRoot.render(_jsx(WorkbenchApp, { embeddableId: config.embeddableId }));
36
+ workbenchRoot.render(_jsx(WorkbenchApp, { embeddableId: config.embeddableId, localDev: config.localDev ?? false }));
37
37
  }
38
38
  }
39
39
  window.__embeddables_bootstrap_workbench = bootstrapWorkbench;
40
40
  // Auto-bootstrap if config is already available (fallback if onload handler doesn't fire)
41
41
  if (typeof window !== 'undefined' && window.__EMBEDDABLES_WORKBENCH?.embeddableId) {
42
42
  const config = window.__EMBEDDABLES_WORKBENCH;
43
- bootstrapWorkbench({ embeddableId: config.embeddableId });
43
+ bootstrapWorkbench({ embeddableId: config.embeddableId, localDev: config.localDev });
44
44
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@embeddables/cli",
3
- "version": "0.3.1",
3
+ "version": "0.3.3",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "embeddables": "./bin/embeddables.mjs"