@embeddable.com/init 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.
Files changed (2) hide show
  1. package/dist/index.js +132 -55
  2. package/package.json +3 -2
package/dist/index.js CHANGED
@@ -7,45 +7,65 @@ import path from "path";
7
7
  import chalk from "chalk";
8
8
  import degit from "degit";
9
9
  import prompts from "prompts";
10
+ import terminalLink from "terminal-link";
10
11
  var REPO_URL = "embeddable-hq/remarkable-pro-boilerplate";
11
12
  var DEFAULT_FOLDER = "embeddable-repo";
12
- var SIGNUP_URLS = {
13
- US: "https://app.us.embeddable.com/auth/login?screen_hint=signup",
14
- EU: "https://app.eu.embeddable.com/auth/login?screen_hint=signup"
13
+ var WORKSPACE_URLS = {
14
+ US: "https://app.us.embeddable.com",
15
+ EU: "https://app.eu.embeddable.com"
15
16
  };
17
+ var INVALID_FOLDER_CHARS = /[<>:"/\\|?*\x00-\x1f]/;
16
18
  function exit(message) {
17
19
  if (message) console.log(message);
18
20
  process.exit(0);
19
21
  }
22
+ function validateFolderName(value, cwd) {
23
+ const trimmed = value.trim();
24
+ if (!trimmed) return "Folder name is required";
25
+ if (INVALID_FOLDER_CHARS.test(trimmed)) {
26
+ return "Folder name contains invalid characters";
27
+ }
28
+ if (trimmed.startsWith(".") || trimmed.startsWith("-")) {
29
+ return "Folder name should not start with . or -";
30
+ }
31
+ if (fs.existsSync(path.join(cwd, trimmed))) {
32
+ return `Folder "${trimmed}" already exists`;
33
+ }
34
+ return true;
35
+ }
36
+ function isValidUuid(value) {
37
+ const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
38
+ return uuidRegex.test(value.trim());
39
+ }
20
40
  async function main() {
21
- prompts.override({ onCancel: () => exit("\nSetup cancelled.\n") });
41
+ const onCancel = () => exit("\nSetup cancelled.\n");
22
42
  console.log(chalk.bold("\nWelcome to Embeddable!\n"));
43
+ console.log(`Embeddable stores your ${chalk.bold("charting components")}, ${chalk.bold("data models")} and ${chalk.bold("themes")} in code, so you have full control over the look-and-feel and the data powering your dashboards.
44
+ `);
45
+ console.log("This tool helps you get set up to create your first Embeddable dashboard.\n");
23
46
  const cwd = process.cwd();
24
- const { confirmDir } = await prompts({
25
- type: "confirm",
26
- name: "confirmDir",
27
- message: `Create a new project in ${chalk.cyan(cwd)}?`,
28
- initial: true
29
- });
30
- if (confirmDir === void 0) {
31
- exit("\nSetup cancelled.\n");
32
- }
47
+ const { confirmDir } = await prompts(
48
+ {
49
+ type: "confirm",
50
+ name: "confirmDir",
51
+ message: `Ok to create a new Project subfolder in your current directory ${chalk.cyan(cwd)}?`,
52
+ initial: true
53
+ },
54
+ { onCancel }
55
+ );
33
56
  if (!confirmDir) {
34
57
  exit("\nRun this command from the directory where you'd like to create your project.\n");
35
58
  }
36
- const { folderName } = await prompts({
37
- type: "text",
38
- name: "folderName",
39
- message: "Project folder name:",
40
- initial: DEFAULT_FOLDER,
41
- validate: (value) => {
42
- if (!value.trim()) return "Folder name is required";
43
- if (fs.existsSync(path.join(cwd, value))) {
44
- return `Folder "${value}" already exists`;
45
- }
46
- return true;
47
- }
48
- });
59
+ const { folderName } = await prompts(
60
+ {
61
+ type: "text",
62
+ name: "folderName",
63
+ message: "Project folder name:",
64
+ initial: DEFAULT_FOLDER,
65
+ validate: (value) => validateFolderName(value, cwd)
66
+ },
67
+ { onCancel }
68
+ );
49
69
  if (!folderName) {
50
70
  exit("\nSetup cancelled.\n");
51
71
  }
@@ -71,54 +91,111 @@ Failed to download boilerplate: ${message}
71
91
  chalk.yellow("\nWarning: Could not initialize git repository. Is git installed?")
72
92
  );
73
93
  }
74
- const { region: selectedRegion } = await prompts({
75
- type: "select",
76
- name: "region",
77
- message: "Select your region:",
78
- choices: [
79
- { title: "US", value: "US" },
80
- { title: "EU", value: "EU" }
81
- ],
82
- initial: 0
83
- });
84
- if (!selectedRegion) {
94
+ console.log(chalk.dim("Installing dependencies...\n"));
95
+ try {
96
+ execSync("npm install", { cwd: projectPath, stdio: "inherit" });
97
+ } catch {
98
+ console.log(
99
+ chalk.yellow("\nWarning: npm install failed. You can run it manually.")
100
+ );
101
+ }
102
+ const { apiKey } = await prompts(
103
+ {
104
+ type: "password",
105
+ name: "apiKey",
106
+ message: `
107
+ Grab your API key (to create an account, head to ${chalk.cyan("https://embeddable.com/select-region")}) and paste it here:`,
108
+ validate: (value) => {
109
+ const trimmed = value.trim();
110
+ if (!trimmed) return "API key is required";
111
+ if (!isValidUuid(trimmed)) return "API key must be a valid UUID";
112
+ return true;
113
+ }
114
+ },
115
+ { onCancel }
116
+ );
117
+ if (!apiKey) {
118
+ exit("\nSetup cancelled.\n");
119
+ }
120
+ const embeddableDir = path.join(projectPath, ".embeddable");
121
+ fs.mkdirSync(embeddableDir, { recursive: true });
122
+ fs.writeFileSync(path.join(embeddableDir, ".api-key"), apiKey.trim());
123
+ const regionResponse = await prompts(
124
+ {
125
+ type: "select",
126
+ name: "region",
127
+ message: "Confirm your workspace region:",
128
+ choices: [
129
+ { title: "US", value: "US" },
130
+ { title: "EU", value: "EU" }
131
+ ],
132
+ initial: 0
133
+ },
134
+ { onCancel }
135
+ );
136
+ const region = regionResponse.region;
137
+ if (!region) {
85
138
  exit("\nSetup cancelled.\n");
86
139
  }
87
- const region = selectedRegion;
88
140
  if (region === "EU") {
89
141
  const configPath = path.join(projectPath, "embeddable.config.ts");
90
- if (!fs.existsSync(configPath)) {
91
- console.log(
92
- chalk.yellow("\nWarning: Could not find embeddable.config.ts to set region.")
93
- );
94
- } else {
95
- let configContent = fs.readFileSync(configPath, "utf-8");
96
- const originalContent = configContent;
97
- configContent = configContent.replace(
142
+ try {
143
+ const configContent = fs.readFileSync(configPath, "utf-8");
144
+ let updatedContent = configContent.replace(
98
145
  /^(\s*)region:\s*["']US["'],?/m,
99
146
  "$1// region: 'US',"
100
147
  );
101
- configContent = configContent.replace(
148
+ updatedContent = updatedContent.replace(
102
149
  /^(\s*)\/\/\s*region:\s*["']EU["'],?/m,
103
150
  "$1region: 'EU',"
104
151
  );
105
- if (configContent === originalContent) {
152
+ if (updatedContent === configContent) {
106
153
  console.log(
107
154
  chalk.yellow("\nWarning: Could not update region in config file. Please set it manually.")
108
155
  );
109
156
  } else {
110
- fs.writeFileSync(configPath, configContent);
157
+ fs.writeFileSync(configPath, updatedContent);
111
158
  }
159
+ } catch {
160
+ console.log(
161
+ chalk.yellow("\nWarning: Could not find embeddable.config.ts to set region.")
162
+ );
112
163
  }
113
164
  }
165
+ console.log(chalk.dim("\nBuilding bundle...\n"));
166
+ try {
167
+ execSync("npm run embeddable:build", { cwd: projectPath, stdio: "inherit" });
168
+ } catch {
169
+ console.log(
170
+ chalk.yellow("\nWarning: embeddable:build failed. You can run it manually.")
171
+ );
172
+ }
173
+ console.log(chalk.dim("\nPushing to workspace...\n"));
174
+ try {
175
+ execSync(
176
+ `npm run embeddable:push -- --api-key ${apiKey.trim()} --email "no-reply@embeddable.com" --message "npx @embeddable.com/init"`,
177
+ { cwd: projectPath, stdio: "inherit" }
178
+ );
179
+ } catch {
180
+ console.log(
181
+ chalk.yellow("\nWarning: embeddable:push failed. You can run it manually.")
182
+ );
183
+ }
184
+ const workspaceUrl = WORKSPACE_URLS[region];
185
+ const workspaceLink = terminalLink(workspaceUrl, workspaceUrl, {
186
+ fallback: (text) => text
187
+ });
188
+ const docsUrl = "https://docs.embeddable.com/getting-started/first-embeddable";
189
+ const docsLink = terminalLink(docsUrl, docsUrl, {
190
+ fallback: (text) => text
191
+ });
114
192
  console.log(chalk.green(`
115
- \u2713 Created ${chalk.bold(folderName)}!
193
+ Success!
116
194
  `));
117
- console.log("Next steps:\n");
118
- console.log(chalk.cyan(` cd ${folderName}`));
119
- console.log(chalk.cyan(" npm install"));
120
- console.log(chalk.cyan(" npm run dev\n"));
121
- console.log(`Sign up at: ${chalk.underline(SIGNUP_URLS[region])}
195
+ console.log(`You have pushed your ${chalk.bold("components")} and ${chalk.bold("data models")} to workspace.`);
196
+ console.log(`Head there now to build your first Embeddable: ${chalk.cyan(workspaceLink)}
197
+ `);
198
+ console.log(`Follow the guide here to create your first Embeddable dashboard: ${chalk.cyan(docsLink)}
122
199
  `);
123
200
  }
124
201
  main().catch((error) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@embeddable.com/init",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "CLI tool for Embeddable",
5
5
  "type": "module",
6
6
  "bin": {
@@ -25,7 +25,8 @@
25
25
  "dependencies": {
26
26
  "chalk": "^5.3.0",
27
27
  "degit": "^2.8.4",
28
- "prompts": "^2.4.2"
28
+ "prompts": "^2.4.2",
29
+ "terminal-link": "^5.0.0"
29
30
  },
30
31
  "devDependencies": {
31
32
  "@types/degit": "^2.8.6",