@embeddables/cli 0.8.0 → 0.8.2

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 (235) hide show
  1. package/dist/auth/index.d.ts +43 -0
  2. package/dist/auth/index.d.ts.map +1 -0
  3. package/dist/auth/index.js +102 -0
  4. package/dist/cli.d.ts +2 -0
  5. package/dist/cli.d.ts.map +1 -0
  6. package/dist/cli.js +210 -0
  7. package/dist/commands/branch.d.ts +4 -0
  8. package/dist/commands/branch.d.ts.map +1 -0
  9. package/dist/commands/branch.js +67 -0
  10. package/dist/commands/build-workbench.d.ts +5 -0
  11. package/dist/commands/build-workbench.d.ts.map +1 -0
  12. package/dist/commands/build-workbench.js +116 -0
  13. package/dist/commands/build.d.ts +8 -0
  14. package/dist/commands/build.d.ts.map +1 -0
  15. package/dist/commands/build.js +60 -0
  16. package/dist/commands/builder-open.d.ts +4 -0
  17. package/dist/commands/builder-open.d.ts.map +1 -0
  18. package/dist/commands/builder-open.js +74 -0
  19. package/dist/commands/dev.d.ts +12 -0
  20. package/dist/commands/dev.d.ts.map +1 -0
  21. package/dist/commands/dev.js +226 -0
  22. package/dist/commands/experiments-connect.d.ts +6 -0
  23. package/dist/commands/experiments-connect.d.ts.map +1 -0
  24. package/dist/commands/experiments-connect.js +140 -0
  25. package/dist/commands/init.d.ts +5 -0
  26. package/dist/commands/init.d.ts.map +1 -0
  27. package/dist/commands/init.js +384 -0
  28. package/dist/commands/inspect.d.ts +9 -0
  29. package/dist/commands/inspect.d.ts.map +1 -0
  30. package/dist/commands/inspect.js +293 -0
  31. package/dist/commands/login.d.ts +2 -0
  32. package/dist/commands/login.d.ts.map +1 -0
  33. package/dist/commands/login.js +117 -0
  34. package/dist/commands/logout.d.ts +2 -0
  35. package/dist/commands/logout.d.ts.map +1 -0
  36. package/dist/commands/logout.js +19 -0
  37. package/dist/commands/pull.d.ts +16 -0
  38. package/dist/commands/pull.d.ts.map +1 -0
  39. package/dist/commands/pull.js +395 -0
  40. package/dist/commands/save.d.ts +30 -0
  41. package/dist/commands/save.d.ts.map +1 -0
  42. package/dist/commands/save.js +597 -0
  43. package/dist/commands/upgrade.d.ts +2 -0
  44. package/dist/commands/upgrade.d.ts.map +1 -0
  45. package/dist/commands/upgrade.js +50 -0
  46. package/dist/compiler/errors.d.ts +20 -0
  47. package/dist/compiler/errors.d.ts.map +1 -0
  48. package/dist/compiler/errors.js +35 -0
  49. package/dist/compiler/evalStatic.d.ts +3 -0
  50. package/dist/compiler/evalStatic.d.ts.map +1 -0
  51. package/dist/compiler/evalStatic.js +57 -0
  52. package/dist/compiler/helpers/duplicateIds.d.ts +9 -0
  53. package/dist/compiler/helpers/duplicateIds.d.ts.map +1 -0
  54. package/dist/compiler/helpers/duplicateIds.js +71 -0
  55. package/dist/compiler/helpers/numericLeadingKeys.d.ts +8 -0
  56. package/dist/compiler/helpers/numericLeadingKeys.d.ts.map +1 -0
  57. package/dist/compiler/helpers/numericLeadingKeys.js +17 -0
  58. package/dist/compiler/index.d.ts +18 -0
  59. package/dist/compiler/index.d.ts.map +1 -0
  60. package/dist/compiler/index.js +1272 -0
  61. package/dist/compiler/parsePage.d.ts +15 -0
  62. package/dist/compiler/parsePage.d.ts.map +1 -0
  63. package/dist/compiler/parsePage.js +654 -0
  64. package/dist/compiler/registry.d.ts +4 -0
  65. package/dist/compiler/registry.d.ts.map +1 -0
  66. package/dist/compiler/registry.js +44 -0
  67. package/dist/compiler/reverse.d.ts +23 -0
  68. package/dist/compiler/reverse.d.ts.map +1 -0
  69. package/dist/compiler/reverse.js +1938 -0
  70. package/dist/compiler/types.d.ts +21 -0
  71. package/dist/compiler/types.d.ts.map +1 -0
  72. package/dist/compiler/types.js +1 -0
  73. package/dist/components/index.d.ts +21 -0
  74. package/dist/components/index.d.ts.map +1 -0
  75. package/dist/components/index.js +21 -0
  76. package/dist/components/primitives/BaseComponent.d.ts +33 -0
  77. package/dist/components/primitives/BaseComponent.d.ts.map +1 -0
  78. package/dist/components/primitives/BaseComponent.js +26 -0
  79. package/dist/components/primitives/BookMeeting.d.ts +18 -0
  80. package/dist/components/primitives/BookMeeting.d.ts.map +1 -0
  81. package/dist/components/primitives/BookMeeting.js +5 -0
  82. package/dist/components/primitives/Chart.d.ts +41 -0
  83. package/dist/components/primitives/Chart.d.ts.map +1 -0
  84. package/dist/components/primitives/Chart.js +5 -0
  85. package/dist/components/primitives/Container.d.ts +8 -0
  86. package/dist/components/primitives/Container.d.ts.map +1 -0
  87. package/dist/components/primitives/Container.js +5 -0
  88. package/dist/components/primitives/CustomButton.d.ts +37 -0
  89. package/dist/components/primitives/CustomButton.d.ts.map +1 -0
  90. package/dist/components/primitives/CustomButton.js +10 -0
  91. package/dist/components/primitives/CustomHTML.d.ts +8 -0
  92. package/dist/components/primitives/CustomHTML.d.ts.map +1 -0
  93. package/dist/components/primitives/CustomHTML.js +5 -0
  94. package/dist/components/primitives/FileUpload.d.ts +18 -0
  95. package/dist/components/primitives/FileUpload.d.ts.map +1 -0
  96. package/dist/components/primitives/FileUpload.js +16 -0
  97. package/dist/components/primitives/InputBox.d.ts +34 -0
  98. package/dist/components/primitives/InputBox.d.ts.map +1 -0
  99. package/dist/components/primitives/InputBox.js +25 -0
  100. package/dist/components/primitives/Lottie.d.ts +11 -0
  101. package/dist/components/primitives/Lottie.d.ts.map +1 -0
  102. package/dist/components/primitives/Lottie.js +5 -0
  103. package/dist/components/primitives/MediaEmbed.d.ts +13 -0
  104. package/dist/components/primitives/MediaEmbed.d.ts.map +1 -0
  105. package/dist/components/primitives/MediaEmbed.js +6 -0
  106. package/dist/components/primitives/MediaImage.d.ts +8 -0
  107. package/dist/components/primitives/MediaImage.d.ts.map +1 -0
  108. package/dist/components/primitives/MediaImage.js +5 -0
  109. package/dist/components/primitives/OptionSelector.d.ts +38 -0
  110. package/dist/components/primitives/OptionSelector.d.ts.map +1 -0
  111. package/dist/components/primitives/OptionSelector.js +8 -0
  112. package/dist/components/primitives/PaypalCheckout.d.ts +25 -0
  113. package/dist/components/primitives/PaypalCheckout.d.ts.map +1 -0
  114. package/dist/components/primitives/PaypalCheckout.js +5 -0
  115. package/dist/components/primitives/PlainText.d.ts +6 -0
  116. package/dist/components/primitives/PlainText.d.ts.map +1 -0
  117. package/dist/components/primitives/PlainText.js +5 -0
  118. package/dist/components/primitives/ProgressBar.d.ts +15 -0
  119. package/dist/components/primitives/ProgressBar.d.ts.map +1 -0
  120. package/dist/components/primitives/ProgressBar.js +5 -0
  121. package/dist/components/primitives/RichText.d.ts +6 -0
  122. package/dist/components/primitives/RichText.d.ts.map +1 -0
  123. package/dist/components/primitives/RichText.js +5 -0
  124. package/dist/components/primitives/RichTextMarkdown.d.ts +6 -0
  125. package/dist/components/primitives/RichTextMarkdown.d.ts.map +1 -0
  126. package/dist/components/primitives/RichTextMarkdown.js +5 -0
  127. package/dist/components/primitives/Rive.d.ts +16 -0
  128. package/dist/components/primitives/Rive.d.ts.map +1 -0
  129. package/dist/components/primitives/Rive.js +8 -0
  130. package/dist/components/primitives/StripeCheckout.d.ts +52 -0
  131. package/dist/components/primitives/StripeCheckout.d.ts.map +1 -0
  132. package/dist/components/primitives/StripeCheckout.js +5 -0
  133. package/dist/components/primitives/StripeCheckout2.d.ts +30 -0
  134. package/dist/components/primitives/StripeCheckout2.d.ts.map +1 -0
  135. package/dist/components/primitives/StripeCheckout2.js +7 -0
  136. package/dist/config/index.d.ts +23 -0
  137. package/dist/config/index.d.ts.map +1 -0
  138. package/dist/config/index.js +42 -0
  139. package/dist/constants.d.ts +9 -0
  140. package/dist/constants.d.ts.map +1 -0
  141. package/dist/constants.js +9 -0
  142. package/dist/helpers/dates.d.ts +5 -0
  143. package/dist/helpers/dates.d.ts.map +1 -0
  144. package/dist/helpers/dates.js +7 -0
  145. package/dist/helpers/json.d.ts +47 -0
  146. package/dist/helpers/json.d.ts.map +1 -0
  147. package/dist/helpers/json.js +622 -0
  148. package/dist/helpers/prompt.d.ts +15 -0
  149. package/dist/helpers/prompt.d.ts.map +1 -0
  150. package/dist/helpers/prompt.js +35 -0
  151. package/dist/helpers/utils.d.ts +13 -0
  152. package/dist/helpers/utils.d.ts.map +1 -0
  153. package/dist/helpers/utils.js +28 -0
  154. package/dist/logger.d.ts +11 -0
  155. package/dist/logger.d.ts.map +1 -0
  156. package/dist/logger.js +21 -0
  157. package/dist/patches/prompts-escape.d.ts +14 -0
  158. package/dist/patches/prompts-escape.d.ts.map +1 -0
  159. package/dist/patches/prompts-escape.js +23 -0
  160. package/dist/prompts/branches.d.ts +20 -0
  161. package/dist/prompts/branches.d.ts.map +1 -0
  162. package/dist/prompts/branches.js +86 -0
  163. package/dist/prompts/embeddables.d.ts +43 -0
  164. package/dist/prompts/embeddables.d.ts.map +1 -0
  165. package/dist/prompts/embeddables.js +200 -0
  166. package/dist/prompts/experiments.d.ts +28 -0
  167. package/dist/prompts/experiments.d.ts.map +1 -0
  168. package/dist/prompts/experiments.js +89 -0
  169. package/dist/prompts/index.d.ts +11 -0
  170. package/dist/prompts/index.d.ts.map +1 -0
  171. package/dist/prompts/index.js +6 -0
  172. package/dist/prompts/projects.d.ts +22 -0
  173. package/dist/prompts/projects.d.ts.map +1 -0
  174. package/dist/prompts/projects.js +92 -0
  175. package/dist/prompts/versions.d.ts +18 -0
  176. package/dist/prompts/versions.d.ts.map +1 -0
  177. package/dist/prompts/versions.js +95 -0
  178. package/dist/proxy/injectApiInterceptor.d.ts +6 -0
  179. package/dist/proxy/injectApiInterceptor.d.ts.map +1 -0
  180. package/dist/proxy/injectApiInterceptor.js +66 -0
  181. package/dist/proxy/injectReload.d.ts +2 -0
  182. package/dist/proxy/injectReload.d.ts.map +1 -0
  183. package/dist/proxy/injectReload.js +14 -0
  184. package/dist/proxy/injectWorkbench.d.ts +5 -0
  185. package/dist/proxy/injectWorkbench.d.ts.map +1 -0
  186. package/dist/proxy/injectWorkbench.js +22 -0
  187. package/dist/proxy/server.d.ts +11 -0
  188. package/dist/proxy/server.d.ts.map +1 -0
  189. package/dist/proxy/server.js +304 -0
  190. package/dist/proxy/sse.d.ts +5 -0
  191. package/dist/proxy/sse.d.ts.map +1 -0
  192. package/dist/proxy/sse.js +17 -0
  193. package/dist/sentry-context.d.ts +48 -0
  194. package/dist/sentry-context.d.ts.map +1 -0
  195. package/dist/sentry-context.js +156 -0
  196. package/dist/stdout.d.ts +61 -0
  197. package/dist/stdout.d.ts.map +1 -0
  198. package/dist/stdout.js +163 -0
  199. package/dist/types-builder.d.ts +800 -0
  200. package/dist/types-builder.d.ts.map +1 -0
  201. package/dist/types-builder.js +20 -0
  202. package/dist/workbench/ActionsPanel.d.ts +6 -0
  203. package/dist/workbench/ActionsPanel.d.ts.map +1 -0
  204. package/dist/workbench/ActionsPanel.js +47 -0
  205. package/dist/workbench/AutofillPanel.d.ts +6 -0
  206. package/dist/workbench/AutofillPanel.d.ts.map +1 -0
  207. package/dist/workbench/AutofillPanel.js +543 -0
  208. package/dist/workbench/ComputedFieldsPanel.d.ts +6 -0
  209. package/dist/workbench/ComputedFieldsPanel.d.ts.map +1 -0
  210. package/dist/workbench/ComputedFieldsPanel.js +31 -0
  211. package/dist/workbench/ExperimentsPanel.d.ts +6 -0
  212. package/dist/workbench/ExperimentsPanel.d.ts.map +1 -0
  213. package/dist/workbench/ExperimentsPanel.js +182 -0
  214. package/dist/workbench/FieldEditorPanel.d.ts +9 -0
  215. package/dist/workbench/FieldEditorPanel.d.ts.map +1 -0
  216. package/dist/workbench/FieldEditorPanel.js +650 -0
  217. package/dist/workbench/InspectorPanel.d.ts +6 -0
  218. package/dist/workbench/InspectorPanel.d.ts.map +1 -0
  219. package/dist/workbench/InspectorPanel.js +341 -0
  220. package/dist/workbench/PageNavigator.d.ts +6 -0
  221. package/dist/workbench/PageNavigator.d.ts.map +1 -0
  222. package/dist/workbench/PageNavigator.js +123 -0
  223. package/dist/workbench/SchemaPanel.d.ts +6 -0
  224. package/dist/workbench/SchemaPanel.d.ts.map +1 -0
  225. package/dist/workbench/SchemaPanel.js +222 -0
  226. package/dist/workbench/UserDataPanel.d.ts +6 -0
  227. package/dist/workbench/UserDataPanel.d.ts.map +1 -0
  228. package/dist/workbench/UserDataPanel.js +350 -0
  229. package/dist/workbench/WorkbenchApp.d.ts +7 -0
  230. package/dist/workbench/WorkbenchApp.d.ts.map +1 -0
  231. package/dist/workbench/WorkbenchApp.js +193 -0
  232. package/dist/workbench/index.d.ts +10 -0
  233. package/dist/workbench/index.d.ts.map +1 -0
  234. package/dist/workbench/index.js +44 -0
  235. package/package.json +2 -1
@@ -0,0 +1,395 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { reverseCompile } from '../compiler/reverse.js';
4
+ import { getAccessToken, isLoggedIn } from '../auth/index.js';
5
+ import { getProjectId, writeProjectConfig } from '../config/index.js';
6
+ import { promptForProject, promptForEmbeddable, fetchEmbeddableMetadata, fetchRecentVersions, promptForVersion, } from '../prompts/index.js';
7
+ import { captureException, createLogger, exit } from '../logger.js';
8
+ import { getSentryContextFromEmbeddableConfig, getSentryContextFromProjectConfig, setSentryContext, } from '../sentry-context.js';
9
+ import * as stdout from '../stdout.js';
10
+ import { inferEmbeddableFromCwd } from '../helpers/utils.js';
11
+ /**
12
+ * Normalize custom_validation_function strings so literal "\\n" (backslash-n) becomes real newline.
13
+ * The API sometimes returns these double-escaped; dataOutputs/computedFields code is written
14
+ * as raw file content so they don't get double-escaped when written to embeddable.json.
15
+ */
16
+ function normalizeCustomValidationFunctionsInFlow(flow) {
17
+ function walkComponent(comp) {
18
+ const v = comp.custom_validation_function;
19
+ if (typeof v === 'string') {
20
+ comp.custom_validation_function = v.replace(/\\n/g, '\n');
21
+ }
22
+ const children = comp.components ?? comp.buttons;
23
+ if (Array.isArray(children)) {
24
+ for (const c of children) {
25
+ if (c && typeof c === 'object')
26
+ walkComponent(c);
27
+ }
28
+ }
29
+ }
30
+ const pages = flow.pages;
31
+ if (Array.isArray(pages)) {
32
+ for (const page of pages) {
33
+ if (page && typeof page === 'object' && Array.isArray(page.components)) {
34
+ for (const comp of page.components) {
35
+ if (comp && typeof comp === 'object')
36
+ walkComponent(comp);
37
+ }
38
+ }
39
+ }
40
+ }
41
+ const components = flow.components;
42
+ if (Array.isArray(components)) {
43
+ for (const comp of components) {
44
+ if (comp && typeof comp === 'object')
45
+ walkComponent(comp);
46
+ }
47
+ }
48
+ }
49
+ /** Slug for branch name/id for use in filenames (e.g. "my branch" -> "my_branch"). */
50
+ function slugForBranch(nameOrId) {
51
+ return (String(nameOrId)
52
+ .replace(/[^a-zA-Z0-9_.-]/g, '_')
53
+ .replace(/_+/g, '_') || 'main');
54
+ }
55
+ /** Versioned embeddable filename: embeddable-{branchSlug}@{version}.json */
56
+ function getVersionedBasename(version, branchSlug) {
57
+ const v = typeof version === 'number' ? version : String(version);
58
+ return `embeddable-${branchSlug}@${v}.json`;
59
+ }
60
+ /** Read current _branch_id and _branch_name from config (set when on a branch via `embeddables branch`). */
61
+ function getCurrentBranchFromConfig(embeddableId) {
62
+ const configPath = path.join('embeddables', embeddableId, 'config.json');
63
+ if (!fs.existsSync(configPath))
64
+ return null;
65
+ try {
66
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
67
+ const branchId = config._branch_id;
68
+ if (typeof branchId !== 'string' || !branchId)
69
+ return null;
70
+ const branchName = config._branch_name;
71
+ return { branchId, branchName: typeof branchName === 'string' ? branchName : undefined };
72
+ }
73
+ catch {
74
+ return null;
75
+ }
76
+ }
77
+ /** Write _version, _branch_id, _branch_name to config.json. Called early and in finally so it always runs. */
78
+ function writePullMetadataToConfig(embeddableId, version, branch, branchName) {
79
+ const configPath = path.join('embeddables', embeddableId, 'config.json');
80
+ let config = {};
81
+ try {
82
+ if (fs.existsSync(configPath)) {
83
+ try {
84
+ const content = fs.readFileSync(configPath, 'utf8');
85
+ config = JSON.parse(content);
86
+ }
87
+ catch {
88
+ /* use empty config */
89
+ }
90
+ }
91
+ if (version != null) {
92
+ const n = typeof version === 'number' ? version : parseInt(String(version), 10);
93
+ if (!isNaN(n))
94
+ config._version = n;
95
+ }
96
+ if (branch) {
97
+ config._branch_id = branch;
98
+ if (branchName)
99
+ config._branch_name = branchName;
100
+ else
101
+ delete config._branch_name;
102
+ }
103
+ else {
104
+ delete config._branch_id;
105
+ delete config._branch_name;
106
+ }
107
+ fs.mkdirSync(path.dirname(configPath), { recursive: true });
108
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n', 'utf8');
109
+ }
110
+ catch (err) {
111
+ stdout.warn(`Could not write config.json: ${err instanceof Error ? err.message : err}`);
112
+ }
113
+ }
114
+ export async function runPull(opts) {
115
+ const logger = createLogger('runPull');
116
+ const totalTimer = stdout.timer();
117
+ const inferred = inferEmbeddableFromCwd();
118
+ let embeddableId = opts.id ?? inferred?.embeddableId;
119
+ if (inferred && !opts.id && embeddableId) {
120
+ process.chdir(inferred.projectRoot);
121
+ }
122
+ // Re-apply project/org from config after chdir so embeddables.json in project root is used
123
+ setSentryContext(getSentryContextFromProjectConfig());
124
+ // If no ID provided, try to get it interactively
125
+ if (!embeddableId) {
126
+ if (!isLoggedIn()) {
127
+ stdout.warn('No embeddable ID provided and not logged in.');
128
+ stdout.dim('Use --id <embeddable-id> or run "embeddables login" for interactive selection.');
129
+ logger.error('not logged in and no embeddable id');
130
+ await exit(1);
131
+ return;
132
+ }
133
+ let projectId = getProjectId();
134
+ // If no project ID configured, prompt for project selection
135
+ if (!projectId) {
136
+ const selectedProject = await stdout.withSpinner('Fetching projects…', async () => {
137
+ return promptForProject();
138
+ }, { successText: 'Projects loaded' });
139
+ if (!selectedProject) {
140
+ logger.error('project selection failed');
141
+ await exit(1);
142
+ return;
143
+ }
144
+ projectId = selectedProject.id;
145
+ // Save the selected project to config
146
+ writeProjectConfig({
147
+ org_id: selectedProject.org_id || undefined,
148
+ org_title: selectedProject.org_title || undefined,
149
+ project_id: projectId,
150
+ project_name: selectedProject.title || undefined,
151
+ });
152
+ stdout.success('Saved project to embeddables.json');
153
+ stdout.gap();
154
+ }
155
+ stdout.step('Fetching embeddables from project…');
156
+ const selected = await promptForEmbeddable(projectId, {
157
+ message: 'Select an embeddable to pull:',
158
+ prioritizeLocal: true,
159
+ });
160
+ if (!selected) {
161
+ logger.error('embeddable selection failed');
162
+ await exit(1);
163
+ return;
164
+ }
165
+ embeddableId = selected;
166
+ stdout.gap();
167
+ }
168
+ if (embeddableId) {
169
+ setSentryContext({
170
+ embeddable: { id: embeddableId },
171
+ ...getSentryContextFromEmbeddableConfig(embeddableId),
172
+ });
173
+ }
174
+ // When useMain, pull main and ignore/clear saved branch. Otherwise stay on current branch when no --branch.
175
+ const currentFromConfig = opts.useMain || opts.branch != null ? null : getCurrentBranchFromConfig(embeddableId);
176
+ const effectiveBranch = opts.useMain ? undefined : (opts.branch ?? currentFromConfig?.branchId);
177
+ const effectiveBranchName = opts.useMain
178
+ ? undefined
179
+ : (opts.branchName ?? currentFromConfig?.branchName);
180
+ const outPath = opts.out || path.join('embeddables', embeddableId, '.generated', 'embeddable.json');
181
+ const branchLabel = effectiveBranch
182
+ ? effectiveBranchName
183
+ ? `${effectiveBranchName} (${effectiveBranch})`
184
+ : effectiveBranch
185
+ : 'main';
186
+ if (effectiveBranch) {
187
+ setSentryContext({ branch: { id: effectiveBranch, name: effectiveBranchName ?? null } });
188
+ }
189
+ // Resolve version:
190
+ // --version (no value) → interactive selector
191
+ // --version <n> → specific version
192
+ // (omitted) → latest
193
+ let versionParam;
194
+ if (opts.selectVersion) {
195
+ stdout.step('Fetching recent versions…');
196
+ const recentVersions = await fetchRecentVersions(embeddableId, effectiveBranch ?? null);
197
+ const selected = await promptForVersion(recentVersions, {
198
+ message: 'Select a version to pull:',
199
+ });
200
+ versionParam = selected === null ? 'latest' : String(selected);
201
+ stdout.gap();
202
+ }
203
+ else if (opts.version !== undefined && opts.version !== '') {
204
+ const v = String(opts.version).toLowerCase();
205
+ versionParam = v === 'latest' ? 'latest' : v;
206
+ }
207
+ else {
208
+ versionParam = 'latest';
209
+ }
210
+ let url = `https://engine.embeddables.com/${embeddableId}?version=${versionParam}`;
211
+ if (effectiveBranch)
212
+ url += `&embeddable_branch=${effectiveBranch}`;
213
+ logger.info('pull started');
214
+ let pullVersion;
215
+ try {
216
+ // Fetch embeddable
217
+ const fetchResult = await stdout.withSpinner(`Fetching embeddable (branch: ${branchLabel})…`, async () => {
218
+ // Add authentication header if available
219
+ const accessToken = getAccessToken();
220
+ const headers = {};
221
+ if (accessToken) {
222
+ headers['Authorization'] = `Bearer ${accessToken}`;
223
+ }
224
+ const response = await fetch(url, { headers });
225
+ if (!response.ok) {
226
+ throw new Error(`Failed to fetch embeddable: ${response.status} ${response.statusText}`);
227
+ }
228
+ const data = await response.json();
229
+ const flow = data.flow;
230
+ if (!flow) {
231
+ throw new Error("Response does not contain a 'flow' property");
232
+ }
233
+ // Normalize custom_validation_function: literal \n (backslash-n) -> real newline (so embeddable.json is correct)
234
+ normalizeCustomValidationFunctionsInFlow(flow);
235
+ return { flow, data };
236
+ }, { successText: 'Fetched embeddable' });
237
+ const { flow, data } = fetchResult;
238
+ // Save to embeddables/<id>/.generated/embeddable.json
239
+ fs.mkdirSync(path.dirname(outPath), { recursive: true });
240
+ const flowJson = JSON.stringify(flow, null, 2);
241
+ fs.writeFileSync(outPath, flowJson, 'utf8');
242
+ pullVersion =
243
+ data.version ?? data.embeddable_version ?? flow.version;
244
+ const version = pullVersion;
245
+ const branchSlug = slugForBranch(effectiveBranchName ?? effectiveBranch ?? 'main');
246
+ // Also save version-specific file (e.g. embeddable-main@135.json or embeddable-my_branch@135.json)
247
+ let filesWritten = 1;
248
+ if (version != null) {
249
+ const versionStr = typeof version === 'string' ? version : String(version);
250
+ const versionedBasename = getVersionedBasename(versionStr, branchSlug);
251
+ const versionedPath = path.join(path.dirname(outPath), versionedBasename);
252
+ fs.writeFileSync(versionedPath, flowJson, 'utf8');
253
+ filesWritten++;
254
+ }
255
+ // Persist _version and _branch_id in config.json immediately so they survive reverseCompile
256
+ writePullMetadataToConfig(embeddableId, version, effectiveBranch, effectiveBranchName);
257
+ // Fetch and save flow metadata from DB (title, archived, created_at)
258
+ const metadata = await fetchEmbeddableMetadata(embeddableId);
259
+ if (metadata) {
260
+ const metadataPath = path.join('embeddables', embeddableId, 'metadata.json');
261
+ fs.mkdirSync(path.dirname(metadataPath), { recursive: true });
262
+ fs.writeFileSync(metadataPath, JSON.stringify(metadata, null, 2) + '\n', 'utf8');
263
+ filesWritten++;
264
+ }
265
+ const versionForSentry = pullVersion != null && !isNaN(Number(pullVersion)) ? Number(pullVersion) : undefined;
266
+ setSentryContext({
267
+ embeddable: { id: embeddableId, title: metadata?.title ?? null },
268
+ versionNumber: versionForSentry,
269
+ });
270
+ stdout.infoBox([
271
+ ['ID', embeddableId],
272
+ ['Branch', branchLabel],
273
+ ['Version', String(pullVersion ?? 'unknown')],
274
+ ['Pages', String(flow.pages?.length ?? 0)],
275
+ ]
276
+ .map(([k, v]) => `${k}: ${v}`)
277
+ .join(' · '));
278
+ // Clear existing pages, styles, computed-fields, actions, and global-components before generating new ones
279
+ const pagesDir = path.join('embeddables', embeddableId, 'pages');
280
+ const stylesDir = path.join('embeddables', embeddableId, 'styles');
281
+ const computedFieldsDir = path.join('embeddables', embeddableId, 'computed-fields');
282
+ const actionsDir = path.join('embeddables', embeddableId, 'actions');
283
+ const globalComponentsDir = path.join('embeddables', embeddableId, 'global-components');
284
+ let clearedCount = 0;
285
+ for (const [dir, ext] of [
286
+ [pagesDir, '.page.tsx'],
287
+ [stylesDir, '.css'],
288
+ [computedFieldsDir, '.js'],
289
+ [actionsDir, '.js'],
290
+ [globalComponentsDir, '.location.tsx'],
291
+ ]) {
292
+ if (fs.existsSync(dir)) {
293
+ const files = fs.readdirSync(dir).filter((f) => f.endsWith(ext));
294
+ for (const file of files) {
295
+ fs.unlinkSync(path.join(dir, file));
296
+ }
297
+ clearedCount += files.length;
298
+ }
299
+ }
300
+ if (clearedCount > 0) {
301
+ stdout.dim(`Cleaned up ${clearedCount} existing files`);
302
+ }
303
+ // Run reverse compiler (pass pullMetadata so config.json gets _version and _branch_id even on fix retry)
304
+ const versionNum = version != null && !isNaN(Number(version)) ? Number(version) : undefined;
305
+ const pullMetadata = {
306
+ version: versionNum,
307
+ branchId: effectiveBranch,
308
+ branchName: effectiveBranchName,
309
+ };
310
+ let usedFix = opts.fix;
311
+ try {
312
+ await stdout.withSpinner('Reverse-compiling…', async () => {
313
+ stdout.mute();
314
+ try {
315
+ await reverseCompile(flow, embeddableId, {
316
+ fix: opts.fix,
317
+ preserve: opts.preserve,
318
+ pullMetadata,
319
+ });
320
+ }
321
+ finally {
322
+ stdout.unmute();
323
+ }
324
+ }, {
325
+ successText: 'Reverse compile complete',
326
+ });
327
+ }
328
+ catch (compileError) {
329
+ // If fix mode wasn't already enabled, offer to retry with fix mode
330
+ if (!opts.fix && compileError instanceof Error) {
331
+ stdout.gap();
332
+ stdout.warn('Reverse compile issue:');
333
+ stdout.warn(` ${compileError.message}`);
334
+ stdout.gap();
335
+ const { prompt: promptWithCancel } = await import('../helpers/prompt.js');
336
+ const response = await promptWithCancel({
337
+ type: 'confirm',
338
+ name: 'fix',
339
+ message: 'Would you like to retry with auto-fix enabled? (removes problematic components)',
340
+ initial: true,
341
+ }, 1);
342
+ if (response.fix) {
343
+ usedFix = true;
344
+ await stdout.withSpinner('Retrying with auto-fix…', async () => {
345
+ stdout.mute();
346
+ try {
347
+ await reverseCompile(flow, embeddableId, {
348
+ fix: true,
349
+ preserve: opts.preserve,
350
+ pullMetadata,
351
+ });
352
+ }
353
+ finally {
354
+ stdout.unmute();
355
+ }
356
+ }, {
357
+ successText: 'Reverse compile complete (with fixes)',
358
+ });
359
+ }
360
+ else {
361
+ await exit(1);
362
+ }
363
+ }
364
+ else {
365
+ throw compileError;
366
+ }
367
+ }
368
+ // Persist mutated flow when fixes were applied (e.g. parent_key -> parent_id)
369
+ if (usedFix) {
370
+ const flowJson = JSON.stringify(flow, null, 2);
371
+ fs.writeFileSync(outPath, flowJson, 'utf8');
372
+ if (version != null) {
373
+ const versionStr = typeof version === 'string' ? version : String(version);
374
+ const versionedBasename = getVersionedBasename(versionStr, branchSlug);
375
+ const versionedPath = path.join(path.dirname(outPath), versionedBasename);
376
+ fs.writeFileSync(versionedPath, flowJson, 'utf8');
377
+ }
378
+ }
379
+ stdout.gap();
380
+ stdout.successBox(`Pulled in ${totalTimer()} ${stdout.symbols.bolt} ${stdout.randomMessage('pull')}`);
381
+ logger.info('pull complete', { version: String(pullVersion ?? 'unknown') });
382
+ }
383
+ catch (error) {
384
+ captureException(error);
385
+ stdout.error(`Error pulling embeddable: ${error instanceof Error ? error.message : String(error)}`);
386
+ logger.error('pull failed', { message: error instanceof Error ? error.message : String(error) });
387
+ await exit(1);
388
+ }
389
+ finally {
390
+ // Always persist _version and _branch_id once we've fetched (so config is correct even if reverseCompile failed or was retried with fix)
391
+ if (embeddableId != null && pullVersion !== undefined) {
392
+ writePullMetadataToConfig(embeddableId, pullVersion, effectiveBranch, effectiveBranchName);
393
+ }
394
+ }
395
+ }
@@ -0,0 +1,30 @@
1
+ import type { HistoryCommand } from '../types-builder.js';
2
+ export type EditHistoryDescription = {
3
+ origin: string;
4
+ description: string;
5
+ } | {
6
+ overflowed: boolean;
7
+ };
8
+ export type SaveEmbeddableVersionParams = {
9
+ embeddableId: string;
10
+ jsonString: string;
11
+ userId: string;
12
+ projectId: string;
13
+ label?: string;
14
+ fromVersionNumber: number;
15
+ editHistoryLength?: number;
16
+ editHistoryDescriptions?: EditHistoryDescription[];
17
+ workingDraftId?: string;
18
+ editHistoryStorageKey?: string;
19
+ editHistory?: HistoryCommand[];
20
+ branchId?: string | null;
21
+ branchMergedId?: string | null;
22
+ };
23
+ export declare function runSave(opts: {
24
+ id?: string;
25
+ label?: string;
26
+ branch?: string;
27
+ skipBuild?: boolean;
28
+ fromVersion?: string;
29
+ }): Promise<void>;
30
+ //# sourceMappingURL=save.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"save.d.ts","sourceRoot":"","sources":["../../src/commands/save.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAGV,cAAc,EACf,MAAM,qBAAqB,CAAA;AAuC5B,MAAM,MAAM,sBAAsB,GAC9B;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GACvC;IAAE,UAAU,EAAE,OAAO,CAAA;CAAE,CAAA;AAE3B,MAAM,MAAM,2BAA2B,GAAG;IACxC,YAAY,EAAE,MAAM,CAAA;IACpB,UAAU,EAAE,MAAM,CAAA;IAClB,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,iBAAiB,EAAE,MAAM,CAAA;IACzB,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,uBAAuB,CAAC,EAAE,sBAAsB,EAAE,CAAA;IAClD,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,WAAW,CAAC,EAAE,cAAc,EAAE,CAAA;IAC9B,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAC/B,CAAA;AAkND,wBAAsB,OAAO,CAAC,IAAI,EAAE;IAClC,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,iBAqBA"}