@embeddables/cli 0.6.11 → 0.7.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/.prompts/custom/build-funnel.md +1 -1
- package/dist/cli.js +7 -10
- package/dist/commands/branch.d.ts.map +1 -1
- package/dist/commands/branch.js +20 -15
- package/dist/commands/build-workbench.d.ts.map +1 -1
- package/dist/commands/build-workbench.js +37 -31
- package/dist/commands/build.d.ts.map +1 -1
- package/dist/commands/build.js +12 -3
- package/dist/commands/dev.d.ts.map +1 -1
- package/dist/commands/dev.js +34 -21
- package/dist/commands/experiments-connect.d.ts.map +1 -1
- package/dist/commands/experiments-connect.js +43 -30
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +49 -48
- package/dist/commands/login.d.ts.map +1 -1
- package/dist/commands/login.js +35 -25
- package/dist/commands/logout.d.ts.map +1 -1
- package/dist/commands/logout.js +10 -6
- package/dist/commands/pull.d.ts.map +1 -1
- package/dist/commands/pull.js +49 -36
- package/dist/commands/save.d.ts.map +1 -1
- package/dist/commands/save.js +79 -53
- package/dist/compiler/index.d.ts.map +1 -1
- package/dist/compiler/index.js +10 -9
- package/dist/compiler/reverse.d.ts.map +1 -1
- package/dist/compiler/reverse.js +18 -17
- package/dist/logger.d.ts +1 -0
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +4 -0
- package/dist/prompts/branches.d.ts.map +1 -1
- package/dist/prompts/branches.js +3 -2
- package/dist/prompts/embeddables.d.ts.map +1 -1
- package/dist/prompts/embeddables.js +8 -7
- package/dist/prompts/experiments.d.ts.map +1 -1
- package/dist/prompts/experiments.js +5 -4
- package/dist/prompts/projects.d.ts.map +1 -1
- package/dist/prompts/projects.js +4 -3
- package/dist/proxy/server.d.ts.map +1 -1
- package/dist/proxy/server.js +33 -32
- package/package.json +2 -1
|
@@ -2,7 +2,7 @@ We need to build the pages from the attached images. Please create a plan and hi
|
|
|
2
2
|
|
|
3
3
|
Embeddable ID: <EMBEDDABLE_ID>
|
|
4
4
|
|
|
5
|
-
Important: Always read the Embeddables CLI context before starting (e.g. @.cursor/rules/embeddables-cli.md or @.
|
|
5
|
+
Important: Always read the Embeddables CLI context before starting (e.g. @.cursor/rules/embeddables-cli.md or @.claude/embeddables-cli.md depending on your editor).
|
|
6
6
|
|
|
7
7
|
[KEEP / REMOVE]
|
|
8
8
|
Use the designs from the images but use the content from the attached document instead, and build out the pages based on that content. Therefore, when using the designs from the images, for each page just find the most relevant design for the page's content and design it based on that. However, don't be confined to the provided designs - just find the closest design and modify it to fit the content required.
|
package/dist/cli.js
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
|
+
import * as Sentry from '@sentry/node';
|
|
2
|
+
Sentry.init({
|
|
3
|
+
dsn: 'https://1660165be6d73c50814963e5c6bbd5dc@o4510046767742976.ingest.us.sentry.io/4510908525379584',
|
|
4
|
+
tracesSampleRate: 1.0,
|
|
5
|
+
enableLogs: true,
|
|
6
|
+
// debug: true,
|
|
7
|
+
});
|
|
1
8
|
import { createRequire } from 'node:module';
|
|
2
9
|
import path from 'node:path';
|
|
3
10
|
import { fileURLToPath } from 'node:url';
|
|
4
11
|
import { Command } from 'commander';
|
|
5
|
-
import pc from 'picocolors';
|
|
6
12
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
7
13
|
const require = createRequire(import.meta.url);
|
|
8
14
|
const pkg = require(path.join(__dirname, '..', 'package.json'));
|
|
@@ -16,15 +22,6 @@ import { runLogout } from './commands/logout.js';
|
|
|
16
22
|
import { runPull } from './commands/pull.js';
|
|
17
23
|
import { runSave } from './commands/save.js';
|
|
18
24
|
import { runUpgrade } from './commands/upgrade.js';
|
|
19
|
-
// Make all console.warn output yellow
|
|
20
|
-
const originalWarn = console.warn.bind(console);
|
|
21
|
-
console.warn = (...args) => {
|
|
22
|
-
originalWarn(...args.map((arg) => typeof arg === 'string' ? `${pc.bold(pc.bgYellow(' WARN '))} ${pc.yellow(arg)}` : arg));
|
|
23
|
-
};
|
|
24
|
-
const originalError = console.error.bind(console);
|
|
25
|
-
console.error = (...args) => {
|
|
26
|
-
originalError(...args.map((arg) => typeof arg === 'string' ? `${pc.bold(pc.bgRed(' ERROR '))} ${pc.red(arg)}` : arg));
|
|
27
|
-
};
|
|
28
25
|
const program = new Command();
|
|
29
26
|
program
|
|
30
27
|
.name('embeddables')
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"branch.d.ts","sourceRoot":"","sources":["../../src/commands/branch.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"branch.d.ts","sourceRoot":"","sources":["../../src/commands/branch.ts"],"names":[],"mappings":"AAQA,wBAAsB,SAAS,CAAC,IAAI,EAAE;IAAE,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,iBA4DpD"}
|
package/dist/commands/branch.js
CHANGED
|
@@ -2,13 +2,17 @@ import pc from 'picocolors';
|
|
|
2
2
|
import { isLoggedIn } from '../auth/index.js';
|
|
3
3
|
import { runPull } from './pull.js';
|
|
4
4
|
import { promptForLocalEmbeddable, fetchBranches, promptForBranch } from '../prompts/index.js';
|
|
5
|
+
import { createLogger, exit } from '../logger.js';
|
|
6
|
+
import * as stdout from '../stdout.js';
|
|
5
7
|
import { inferEmbeddableFromCwd } from '../helpers/utils.js';
|
|
6
8
|
export async function runBranch(opts) {
|
|
9
|
+
const logger = createLogger('runBranch');
|
|
7
10
|
// Check login status
|
|
8
11
|
if (!isLoggedIn()) {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
+
stdout.error(pc.red('Not logged in.'));
|
|
13
|
+
stdout.print(pc.gray('Run "embeddables login" first.'));
|
|
14
|
+
logger.error('not logged in');
|
|
15
|
+
await exit(1);
|
|
12
16
|
}
|
|
13
17
|
// Get embeddable ID
|
|
14
18
|
const inferred = inferEmbeddableFromCwd();
|
|
@@ -19,33 +23,34 @@ export async function runBranch(opts) {
|
|
|
19
23
|
if (!embeddableId) {
|
|
20
24
|
const selected = await promptForLocalEmbeddable();
|
|
21
25
|
if (!selected) {
|
|
22
|
-
|
|
26
|
+
await exit(1);
|
|
27
|
+
return;
|
|
23
28
|
}
|
|
24
29
|
embeddableId = selected;
|
|
25
30
|
}
|
|
26
|
-
|
|
27
|
-
|
|
31
|
+
stdout.print('');
|
|
32
|
+
stdout.print(pc.cyan('Fetching branches...'));
|
|
28
33
|
// Fetch branches for this embeddable
|
|
29
34
|
const branches = await fetchBranches(embeddableId);
|
|
30
35
|
if (branches.length === 0) {
|
|
31
|
-
|
|
32
|
-
|
|
36
|
+
stdout.print(pc.yellow('No branches found for this embeddable.'));
|
|
37
|
+
stdout.print(pc.gray('Branches are created in the Embeddables Builder.'));
|
|
33
38
|
return;
|
|
34
39
|
}
|
|
35
|
-
|
|
40
|
+
stdout.print('');
|
|
36
41
|
// Prompt for branch selection
|
|
37
42
|
const selectedBranch = await promptForBranch(branches);
|
|
38
|
-
|
|
43
|
+
stdout.print('');
|
|
39
44
|
// Pull the selected branch
|
|
40
45
|
if (selectedBranch === null) {
|
|
41
|
-
// User selected "main" - pull
|
|
42
|
-
|
|
43
|
-
|
|
46
|
+
// User selected "main" - pull without branch
|
|
47
|
+
stdout.print(pc.cyan('Switching to main (latest published version)...'));
|
|
48
|
+
stdout.print('');
|
|
44
49
|
await runPull({ id: embeddableId, useMain: true });
|
|
45
50
|
}
|
|
46
51
|
else {
|
|
47
|
-
|
|
48
|
-
|
|
52
|
+
stdout.print(pc.cyan(`Switching to branch: ${selectedBranch.name}...`));
|
|
53
|
+
stdout.print('');
|
|
49
54
|
await runPull({
|
|
50
55
|
id: embeddableId,
|
|
51
56
|
branch: selectedBranch.id,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build-workbench.d.ts","sourceRoot":"","sources":["../../src/commands/build-workbench.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"build-workbench.d.ts","sourceRoot":"","sources":["../../src/commands/build-workbench.ts"],"names":[],"mappings":"AAUA,wBAAsB,iBAAiB,CAAC,IAAI,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,iBA+H/E"}
|
|
@@ -5,29 +5,34 @@ import postcss from 'postcss';
|
|
|
5
5
|
import tailwindcss from '@tailwindcss/postcss';
|
|
6
6
|
import autoprefixer from 'autoprefixer';
|
|
7
7
|
import pc from 'picocolors';
|
|
8
|
+
import { createLogger, exit } from '../logger.js';
|
|
9
|
+
import * as stdout from '../stdout.js';
|
|
8
10
|
export async function runBuildWorkbench(opts) {
|
|
11
|
+
const logger = createLogger('runBuildWorkbench');
|
|
9
12
|
const outDir = opts.out || 'dist/workbench';
|
|
10
13
|
const shouldMinify = opts.minify ?? true;
|
|
11
14
|
// Check if wrangler config exists in outDir (native Cloudflare Git integration mode)
|
|
12
15
|
const hasWranglerConfig = fs.existsSync(path.join(outDir, 'wrangler.jsonc')) ||
|
|
13
16
|
fs.existsSync(path.join(outDir, 'wrangler.toml'));
|
|
14
|
-
|
|
15
|
-
|
|
17
|
+
stdout.print(pc.cyan('Building Workbench for CDN deployment...'));
|
|
18
|
+
stdout.print();
|
|
16
19
|
// Ensure output directory exists
|
|
17
20
|
fs.mkdirSync(outDir, { recursive: true });
|
|
18
21
|
const workbenchEntry = path.join(process.cwd(), 'src', 'workbench', 'index.tsx');
|
|
19
22
|
const workbenchCssEntry = path.join(process.cwd(), 'src', 'workbench', 'workbench.css');
|
|
20
23
|
// Check that source files exist
|
|
21
24
|
if (!fs.existsSync(workbenchEntry)) {
|
|
22
|
-
|
|
23
|
-
|
|
25
|
+
stdout.error(`Workbench entry not found: ${workbenchEntry}`);
|
|
26
|
+
logger.error('workbench entry not found', { path: workbenchEntry });
|
|
27
|
+
await exit(1);
|
|
24
28
|
}
|
|
25
29
|
if (!fs.existsSync(workbenchCssEntry)) {
|
|
26
|
-
|
|
27
|
-
|
|
30
|
+
stdout.error(`Workbench CSS not found: ${workbenchCssEntry}`);
|
|
31
|
+
logger.error('workbench css not found', { path: workbenchCssEntry });
|
|
32
|
+
await exit(1);
|
|
28
33
|
}
|
|
29
34
|
// Build JavaScript bundle
|
|
30
|
-
|
|
35
|
+
stdout.print(pc.dim(' Building JavaScript bundle...'));
|
|
31
36
|
const jsResult = await esbuild.build({
|
|
32
37
|
entryPoints: [workbenchEntry],
|
|
33
38
|
bundle: true,
|
|
@@ -41,11 +46,12 @@ export async function runBuildWorkbench(opts) {
|
|
|
41
46
|
});
|
|
42
47
|
const jsOutput = jsResult.outputFiles?.[0];
|
|
43
48
|
if (!jsOutput) {
|
|
44
|
-
|
|
45
|
-
|
|
49
|
+
stdout.error('Failed to build JavaScript bundle');
|
|
50
|
+
logger.error('workbench js bundle build failed');
|
|
51
|
+
await exit(1);
|
|
46
52
|
}
|
|
47
53
|
// Build CSS
|
|
48
|
-
|
|
54
|
+
stdout.print(pc.dim(' Building CSS...'));
|
|
49
55
|
const rawCss = fs.readFileSync(workbenchCssEntry, 'utf8');
|
|
50
56
|
const cssResult = await postcss([tailwindcss(), autoprefixer]).process(rawCss, {
|
|
51
57
|
from: workbenchCssEntry,
|
|
@@ -63,50 +69,50 @@ export async function runBuildWorkbench(opts) {
|
|
|
63
69
|
fs.mkdirSync(publicDir, { recursive: true });
|
|
64
70
|
fs.writeFileSync(path.join(publicDir, 'workbench.js'), jsOutput.contents);
|
|
65
71
|
const jsSize = (jsOutput.contents.length / 1024).toFixed(1);
|
|
66
|
-
|
|
72
|
+
stdout.print(pc.green(` ✓ ${publicDir}/workbench.js (${jsSize} KB)`));
|
|
67
73
|
fs.writeFileSync(path.join(publicDir, 'workbench.css'), cssResult.css);
|
|
68
74
|
const cssSize = (cssResult.css.length / 1024).toFixed(1);
|
|
69
|
-
|
|
75
|
+
stdout.print(pc.green(` ✓ ${publicDir}/workbench.css (${cssSize} KB)`));
|
|
70
76
|
fs.writeFileSync(path.join(publicDir, '_headers'), headersContent);
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
77
|
+
stdout.print(pc.green(` ✓ ${publicDir}/_headers`));
|
|
78
|
+
stdout.print();
|
|
79
|
+
stdout.print(pc.bold(pc.green('Build complete!')));
|
|
80
|
+
stdout.print();
|
|
81
|
+
stdout.print(pc.dim('Using existing wrangler.toml for Cloudflare Git integration'));
|
|
76
82
|
}
|
|
77
83
|
else {
|
|
78
84
|
// Standalone mode: output to outDir with nested cloudflare-worker folder
|
|
79
85
|
const jsPath = path.join(outDir, 'workbench.js');
|
|
80
86
|
fs.writeFileSync(jsPath, jsOutput.contents);
|
|
81
87
|
const jsSize = (jsOutput.contents.length / 1024).toFixed(1);
|
|
82
|
-
|
|
88
|
+
stdout.print(pc.green(` ✓ ${jsPath} (${jsSize} KB)`));
|
|
83
89
|
const cssPath = path.join(outDir, 'workbench.css');
|
|
84
90
|
fs.writeFileSync(cssPath, cssResult.css);
|
|
85
91
|
const cssSize = (cssResult.css.length / 1024).toFixed(1);
|
|
86
|
-
|
|
92
|
+
stdout.print(pc.green(` ✓ ${cssPath} (${cssSize} KB)`));
|
|
87
93
|
// Generate Cloudflare static site files
|
|
88
|
-
|
|
94
|
+
stdout.print(pc.dim(' Generating Cloudflare deployment files...'));
|
|
89
95
|
const workerDir = path.join(outDir, 'cloudflare');
|
|
90
96
|
fs.mkdirSync(workerDir, { recursive: true });
|
|
91
97
|
const wranglerConfig = generateWranglerConfig();
|
|
92
98
|
fs.writeFileSync(path.join(workerDir, 'wrangler.jsonc'), wranglerConfig);
|
|
93
|
-
|
|
99
|
+
stdout.print(pc.green(` ✓ ${workerDir}/wrangler.jsonc`));
|
|
94
100
|
const publicDir = path.join(workerDir, 'public');
|
|
95
101
|
fs.mkdirSync(publicDir, { recursive: true });
|
|
96
102
|
fs.copyFileSync(jsPath, path.join(publicDir, 'workbench.js'));
|
|
97
103
|
fs.copyFileSync(cssPath, path.join(publicDir, 'workbench.css'));
|
|
98
104
|
fs.writeFileSync(path.join(publicDir, '_headers'), headersContent);
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
+
stdout.print(pc.green(` ✓ ${publicDir}/ (assets + _headers)`));
|
|
106
|
+
stdout.print();
|
|
107
|
+
stdout.print(pc.bold(pc.green('Build complete!')));
|
|
108
|
+
stdout.print();
|
|
109
|
+
stdout.print(pc.cyan('Deploy to Cloudflare:'));
|
|
110
|
+
stdout.print(pc.dim(` cd ${workerDir} && npx wrangler deploy`));
|
|
105
111
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
112
|
+
stdout.print();
|
|
113
|
+
stdout.print(pc.cyan('Usage:'));
|
|
114
|
+
stdout.print(pc.dim(' Add ?workbench=true to any engine preview URL'));
|
|
115
|
+
stdout.print();
|
|
110
116
|
}
|
|
111
117
|
function generateWranglerConfig() {
|
|
112
118
|
return `{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/commands/build.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/commands/build.ts"],"names":[],"mappings":"AAQA,wBAAsB,QAAQ,CAAC,IAAI,EAAE;IACnC,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,OAAO,CAAA;IACb,WAAW,EAAE,UAAU,GAAG,QAAQ,CAAA;CACnC,iBA4CA"}
|
package/dist/commands/build.js
CHANGED
|
@@ -2,8 +2,11 @@ import path from 'node:path';
|
|
|
2
2
|
import { compileAllPages } from '../compiler/index.js';
|
|
3
3
|
import { formatError } from '../compiler/errors.js';
|
|
4
4
|
import { promptForLocalEmbeddable } from '../prompts/index.js';
|
|
5
|
+
import { captureException, createLogger, exit } from '../logger.js';
|
|
6
|
+
import * as stdout from '../stdout.js';
|
|
5
7
|
import { inferEmbeddableFromCwd } from '../helpers/utils.js';
|
|
6
8
|
export async function runBuild(opts) {
|
|
9
|
+
const logger = createLogger('runBuild');
|
|
7
10
|
const inferred = inferEmbeddableFromCwd();
|
|
8
11
|
let embeddableId = opts.id ?? inferred?.embeddableId;
|
|
9
12
|
if (inferred && !opts.id && embeddableId) {
|
|
@@ -14,10 +17,13 @@ export async function runBuild(opts) {
|
|
|
14
17
|
message: 'Select an embeddable to build:',
|
|
15
18
|
});
|
|
16
19
|
if (!selected) {
|
|
17
|
-
|
|
20
|
+
logger.error('no embeddable selected');
|
|
21
|
+
await exit(1);
|
|
22
|
+
return;
|
|
18
23
|
}
|
|
19
24
|
embeddableId = selected;
|
|
20
25
|
}
|
|
26
|
+
logger.info('build started', { embeddableId });
|
|
21
27
|
const pagesGlob = opts.pages || `embeddables/${embeddableId}/pages/**/*.page.tsx`;
|
|
22
28
|
const outPath = opts.out || path.join('embeddables', embeddableId, '.generated', 'embeddable.json');
|
|
23
29
|
const stylesDir = path.join('embeddables', embeddableId, 'styles');
|
|
@@ -32,7 +38,10 @@ export async function runBuild(opts) {
|
|
|
32
38
|
});
|
|
33
39
|
}
|
|
34
40
|
catch (e) {
|
|
35
|
-
|
|
36
|
-
|
|
41
|
+
captureException(e);
|
|
42
|
+
stdout.error(formatError(e));
|
|
43
|
+
logger.error('build failed', { embeddableId });
|
|
44
|
+
await exit(1);
|
|
37
45
|
}
|
|
46
|
+
logger.info('build complete', { embeddableId });
|
|
38
47
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"AAsHA,wBAAsB,MAAM,CAAC,IAAI,EAAE;IACjC,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,OAAO,CAAA;IACb,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,aAAa,EAAE,MAAM,CAAA;IACrB,WAAW,EAAE,UAAU,GAAG,QAAQ,CAAA;CACnC,iBAiIA"}
|
package/dist/commands/dev.js
CHANGED
|
@@ -7,6 +7,8 @@ import prompts from 'prompts';
|
|
|
7
7
|
import { compileAllPages } from '../compiler/index.js';
|
|
8
8
|
import { startProxyServer } from '../proxy/server.js';
|
|
9
9
|
import { formatError } from '../compiler/errors.js';
|
|
10
|
+
import { captureException, createLogger, exit } from '../logger.js';
|
|
11
|
+
import * as stdout from '../stdout.js';
|
|
10
12
|
import { inferEmbeddableFromCwd } from '../helpers/utils.js';
|
|
11
13
|
/**
|
|
12
14
|
* Check whether a port is available by attempting to listen on it.
|
|
@@ -72,8 +74,8 @@ async function discoverEmbeddables() {
|
|
|
72
74
|
async function promptForEmbeddable() {
|
|
73
75
|
const embeddables = await discoverEmbeddables();
|
|
74
76
|
if (embeddables.length === 0) {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
+
stdout.error('No embeddables found in the embeddables/ directory.');
|
|
78
|
+
stdout.error('Run `embeddables pull --id <id>` to pull an embeddable first.');
|
|
77
79
|
return null;
|
|
78
80
|
}
|
|
79
81
|
const choices = embeddables.map((e) => ({
|
|
@@ -93,6 +95,7 @@ async function promptForEmbeddable() {
|
|
|
93
95
|
return response.id || null;
|
|
94
96
|
}
|
|
95
97
|
export async function runDev(opts) {
|
|
98
|
+
const logger = createLogger('runDev');
|
|
96
99
|
const inferred = inferEmbeddableFromCwd();
|
|
97
100
|
let embeddableId = opts.id ?? inferred?.embeddableId;
|
|
98
101
|
if (inferred && !opts.id && embeddableId) {
|
|
@@ -101,10 +104,13 @@ export async function runDev(opts) {
|
|
|
101
104
|
if (!embeddableId) {
|
|
102
105
|
const selected = await promptForEmbeddable();
|
|
103
106
|
if (!selected) {
|
|
104
|
-
|
|
107
|
+
logger.error('no embeddable selected');
|
|
108
|
+
await exit(1);
|
|
109
|
+
return;
|
|
105
110
|
}
|
|
106
111
|
embeddableId = selected;
|
|
107
112
|
}
|
|
113
|
+
logger.info('dev started', { embeddableId, engine: opts.engine, port: opts.port });
|
|
108
114
|
const pagesGlob = opts.pages || `embeddables/${embeddableId}/pages/**/*.page.tsx`;
|
|
109
115
|
const outPath = opts.out || path.join('embeddables', embeddableId, '.generated', 'embeddable.json');
|
|
110
116
|
const stylesDir = path.join('embeddables', embeddableId, 'styles');
|
|
@@ -127,8 +133,10 @@ export async function runDev(opts) {
|
|
|
127
133
|
});
|
|
128
134
|
}
|
|
129
135
|
catch (e) {
|
|
130
|
-
|
|
131
|
-
|
|
136
|
+
captureException(e);
|
|
137
|
+
stdout.error(formatError(e));
|
|
138
|
+
logger.error('initial build failed', { embeddableId });
|
|
139
|
+
await exit(1);
|
|
132
140
|
}
|
|
133
141
|
// Start proxy — find an available port if the requested one is taken
|
|
134
142
|
const requestedPort = Number(opts.port);
|
|
@@ -137,12 +145,15 @@ export async function runDev(opts) {
|
|
|
137
145
|
port = await getAvailablePort(requestedPort);
|
|
138
146
|
}
|
|
139
147
|
catch (e) {
|
|
140
|
-
|
|
141
|
-
|
|
148
|
+
captureException(e);
|
|
149
|
+
stdout.error(pc.red(e.message));
|
|
150
|
+
logger.error('no available port', { requestedPort });
|
|
151
|
+
await exit(1);
|
|
152
|
+
return;
|
|
142
153
|
}
|
|
143
154
|
if (port !== requestedPort) {
|
|
144
|
-
|
|
145
|
-
|
|
155
|
+
stdout.warn(`Port ${requestedPort} is in use, using ${port} instead.`);
|
|
156
|
+
stdout.print('');
|
|
146
157
|
}
|
|
147
158
|
const proxy = await startProxyServer({
|
|
148
159
|
port,
|
|
@@ -152,6 +163,7 @@ export async function runDev(opts) {
|
|
|
152
163
|
embeddableId,
|
|
153
164
|
watchWorkbench: true,
|
|
154
165
|
});
|
|
166
|
+
logger.info('dev server running', { embeddableId, port });
|
|
155
167
|
// Watch all source files: pages, styles, config, global components, computed fields, and actions
|
|
156
168
|
const watcher = chokidar.watch([pagesGlob, stylesGlob, configPath, globalComponentsGlob, computedFieldsGlob, actionsGlob], {
|
|
157
169
|
ignoreInitial: true,
|
|
@@ -172,7 +184,8 @@ export async function runDev(opts) {
|
|
|
172
184
|
catch (e) {
|
|
173
185
|
// Keep dev server running; compiler errors show in console.
|
|
174
186
|
// (Proxy will still serve the last good JSON.)
|
|
175
|
-
|
|
187
|
+
captureException(e);
|
|
188
|
+
stdout.error(formatError(e));
|
|
176
189
|
}
|
|
177
190
|
};
|
|
178
191
|
let t;
|
|
@@ -180,21 +193,21 @@ export async function runDev(opts) {
|
|
|
180
193
|
clearTimeout(t);
|
|
181
194
|
t = setTimeout(rebuild, 80);
|
|
182
195
|
});
|
|
183
|
-
|
|
184
|
-
|
|
196
|
+
stdout.print(`Watching embeddables/${embeddableId}/ for changes...`);
|
|
197
|
+
stdout.print('');
|
|
185
198
|
const terminalWidth = process.stdout.columns || 80;
|
|
186
199
|
const separator = '━'.repeat(terminalWidth);
|
|
187
|
-
|
|
188
|
-
|
|
200
|
+
stdout.print(`${pc.bold(pc.cyan(separator))}`);
|
|
201
|
+
stdout.print('');
|
|
189
202
|
if (opts.local) {
|
|
190
|
-
|
|
203
|
+
stdout.print(`${pc.bold(pc.blue('Mode:'))} ${pc.blue('Local')} (using ${opts.engine})`);
|
|
191
204
|
}
|
|
192
205
|
else {
|
|
193
|
-
|
|
206
|
+
stdout.print(`${pc.bold(pc.yellow('Mode:'))} ${pc.yellow('Remote')} (using ${opts.engine})`);
|
|
194
207
|
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
208
|
+
stdout.print('');
|
|
209
|
+
stdout.print(`${pc.bold(pc.green('Preview URL:'))} ${pc.underline(pc.cyan(`http://localhost:${port}?id=${embeddableId}&version=latest`))}`);
|
|
210
|
+
stdout.print('');
|
|
211
|
+
stdout.print(`${pc.bold(pc.cyan(separator))}`);
|
|
212
|
+
stdout.print('');
|
|
200
213
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"experiments-connect.d.ts","sourceRoot":"","sources":["../../src/commands/experiments-connect.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"experiments-connect.d.ts","sourceRoot":"","sources":["../../src/commands/experiments-connect.ts"],"names":[],"mappings":"AAeA,wBAAsB,qBAAqB,CAAC,IAAI,EAAE;IAChD,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB,iBAeA"}
|
|
@@ -4,38 +4,44 @@ import pc from 'picocolors';
|
|
|
4
4
|
import { isLoggedIn } from '../auth/index.js';
|
|
5
5
|
import { getProjectId, writeProjectConfig } from '../config/index.js';
|
|
6
6
|
import { promptForProject, promptForLocalEmbeddable, promptForExperiment, } from '../prompts/index.js';
|
|
7
|
+
import { createLogger, exit } from '../logger.js';
|
|
8
|
+
import * as stdout from '../stdout.js';
|
|
7
9
|
import { inferEmbeddableFromCwd } from '../helpers/utils.js';
|
|
8
10
|
export async function runExperimentsConnect(opts) {
|
|
11
|
+
const logger = createLogger('runExperimentsConnect');
|
|
9
12
|
try {
|
|
10
13
|
await runExperimentsConnectInner(opts);
|
|
11
14
|
}
|
|
12
15
|
catch (error) {
|
|
13
16
|
if (error instanceof Error) {
|
|
14
|
-
|
|
17
|
+
stdout.error(pc.red(`Connect failed: ${error.message}`));
|
|
15
18
|
}
|
|
16
19
|
else {
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
process.exit(1);
|
|
20
|
-
if (error instanceof Error && error.message === 'exit') {
|
|
21
|
-
throw error;
|
|
20
|
+
stdout.error(pc.red('Connect failed with an unexpected error.'));
|
|
22
21
|
}
|
|
22
|
+
logger.error('experiments connect failed', {
|
|
23
|
+
message: error instanceof Error ? error.message : 'unexpected error',
|
|
24
|
+
});
|
|
25
|
+
await exit(1);
|
|
23
26
|
}
|
|
24
27
|
}
|
|
25
28
|
async function runExperimentsConnectInner(opts) {
|
|
29
|
+
const logger = createLogger('runExperimentsConnect');
|
|
26
30
|
// 1. Check login (needed for fetching experiments from Supabase)
|
|
27
31
|
if (!isLoggedIn()) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
32
|
+
stdout.error(pc.red('Not logged in.'));
|
|
33
|
+
stdout.print(pc.gray('Run "embeddables login" first.'));
|
|
34
|
+
logger.error('not logged in');
|
|
35
|
+
await exit(1);
|
|
31
36
|
}
|
|
32
37
|
// 2. Get project ID (needed for experiment list)
|
|
33
38
|
let projectId = getProjectId();
|
|
34
39
|
if (!projectId) {
|
|
35
|
-
|
|
40
|
+
stdout.print(pc.cyan('No project configured. Fetching projects...'));
|
|
36
41
|
const selectedProject = await promptForProject();
|
|
37
42
|
if (!selectedProject) {
|
|
38
|
-
|
|
43
|
+
await exit(1);
|
|
44
|
+
return;
|
|
39
45
|
}
|
|
40
46
|
projectId = selectedProject.id;
|
|
41
47
|
writeProjectConfig({
|
|
@@ -44,8 +50,8 @@ async function runExperimentsConnectInner(opts) {
|
|
|
44
50
|
project_id: projectId,
|
|
45
51
|
project_name: selectedProject.title || undefined,
|
|
46
52
|
});
|
|
47
|
-
|
|
48
|
-
|
|
53
|
+
stdout.print(pc.green('✓ Saved project to embeddables.json'));
|
|
54
|
+
stdout.print('');
|
|
49
55
|
}
|
|
50
56
|
// 4. Get embeddable ID (from option, cwd inference, or interactive prompt)
|
|
51
57
|
const inferred = inferEmbeddableFromCwd();
|
|
@@ -58,41 +64,46 @@ async function runExperimentsConnectInner(opts) {
|
|
|
58
64
|
message: 'Select an embeddable to connect the experiment to:',
|
|
59
65
|
});
|
|
60
66
|
if (!selected) {
|
|
61
|
-
|
|
67
|
+
await exit(1);
|
|
68
|
+
return;
|
|
62
69
|
}
|
|
63
70
|
embeddableId = selected;
|
|
64
|
-
|
|
71
|
+
stdout.print('');
|
|
65
72
|
}
|
|
66
73
|
// 5. Get experiment_id and experiment_key (from opts or interactive prompt)
|
|
67
74
|
let experimentId = opts.experimentId;
|
|
68
75
|
let experimentKey = opts.experimentKey;
|
|
69
76
|
if (experimentId && !experimentKey) {
|
|
70
|
-
|
|
71
|
-
|
|
77
|
+
stdout.error(pc.red('When using --experiment-id, --experiment-key is also required.'));
|
|
78
|
+
logger.error('missing experiment-key flag');
|
|
79
|
+
await exit(1);
|
|
72
80
|
}
|
|
73
81
|
if (experimentKey && !experimentId) {
|
|
74
|
-
|
|
75
|
-
|
|
82
|
+
stdout.error(pc.red('When using --experiment-key, --experiment-id is also required.'));
|
|
83
|
+
logger.error('missing experiment-id flag');
|
|
84
|
+
await exit(1);
|
|
76
85
|
}
|
|
77
86
|
if (!experimentId || !experimentKey) {
|
|
78
|
-
|
|
87
|
+
stdout.print(pc.cyan('Fetching experiments from project...'));
|
|
79
88
|
const selected = await promptForExperiment(projectId, {
|
|
80
89
|
message: 'Select an experiment to connect:',
|
|
81
90
|
excludeConnectedTo: embeddableId,
|
|
82
91
|
});
|
|
83
92
|
if (!selected) {
|
|
84
|
-
|
|
93
|
+
await exit(1);
|
|
94
|
+
return;
|
|
85
95
|
}
|
|
86
96
|
experimentId = selected.experiment_id;
|
|
87
97
|
experimentKey = selected.experiment_key;
|
|
88
|
-
|
|
98
|
+
stdout.print('');
|
|
89
99
|
}
|
|
90
100
|
// 6. Read config.json
|
|
91
101
|
const configPath = path.join('embeddables', embeddableId, 'config.json');
|
|
92
102
|
if (!fs.existsSync(configPath)) {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
103
|
+
stdout.error(pc.red(`No config.json found at ${configPath}`));
|
|
104
|
+
stdout.print(pc.gray('Run "embeddables pull" or "embeddables init" first.'));
|
|
105
|
+
logger.error('config not found', { configPath });
|
|
106
|
+
await exit(1);
|
|
96
107
|
}
|
|
97
108
|
let config;
|
|
98
109
|
try {
|
|
@@ -100,20 +111,22 @@ async function runExperimentsConnectInner(opts) {
|
|
|
100
111
|
config = JSON.parse(content);
|
|
101
112
|
}
|
|
102
113
|
catch {
|
|
103
|
-
|
|
104
|
-
|
|
114
|
+
stdout.error(pc.red('Failed to parse config.json.'));
|
|
115
|
+
logger.error('failed to parse config');
|
|
116
|
+
await exit(1);
|
|
117
|
+
return;
|
|
105
118
|
}
|
|
106
119
|
// 7. Append to connected_experiments
|
|
107
120
|
const connectedExperiments = Array.isArray(config.connected_experiments) ? [...config.connected_experiments] : [];
|
|
108
121
|
const alreadyConnected = connectedExperiments.some((e) => e.experiment_id === experimentId && e.experiment_key === experimentKey);
|
|
109
122
|
if (alreadyConnected) {
|
|
110
|
-
|
|
123
|
+
stdout.print(pc.yellow(`Experiment "${experimentKey}" is already connected to this embeddable.`));
|
|
111
124
|
return;
|
|
112
125
|
}
|
|
113
126
|
connectedExperiments.push({ experiment_id: experimentId, experiment_key: experimentKey });
|
|
114
127
|
config.connected_experiments = connectedExperiments;
|
|
115
128
|
// 8. Write the modified config back
|
|
116
129
|
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf8');
|
|
117
|
-
|
|
118
|
-
|
|
130
|
+
stdout.print(pc.green(`✓ Connected experiment "${experimentKey}" to embeddable (updated config.json)`));
|
|
131
|
+
stdout.print(pc.gray('Run "embeddables save" to persist to the cloud.'));
|
|
119
132
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAwHA,wBAAsB,OAAO,CAAC,IAAI,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,OAAO,CAAA;CAAE,iBAyOxE"}
|