@embeddables/cli 0.1.0

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 (173) hide show
  1. package/README.md +116 -0
  2. package/bin/embeddables.mjs +2 -0
  3. package/dist/auth/index.d.ts +43 -0
  4. package/dist/auth/index.d.ts.map +1 -0
  5. package/dist/auth/index.js +100 -0
  6. package/dist/cli.d.ts +2 -0
  7. package/dist/cli.d.ts.map +1 -0
  8. package/dist/cli.js +75 -0
  9. package/dist/commands/build-workbench.d.ts +5 -0
  10. package/dist/commands/build-workbench.d.ts.map +1 -0
  11. package/dist/commands/build-workbench.js +122 -0
  12. package/dist/commands/build.d.ts +7 -0
  13. package/dist/commands/build.d.ts.map +1 -0
  14. package/dist/commands/build.js +22 -0
  15. package/dist/commands/dev.d.ts +11 -0
  16. package/dist/commands/dev.d.ts.map +1 -0
  17. package/dist/commands/dev.js +153 -0
  18. package/dist/commands/login.d.ts +2 -0
  19. package/dist/commands/login.d.ts.map +1 -0
  20. package/dist/commands/login.js +112 -0
  21. package/dist/commands/logout.d.ts +2 -0
  22. package/dist/commands/logout.d.ts.map +1 -0
  23. package/dist/commands/logout.js +18 -0
  24. package/dist/commands/pull.d.ts +7 -0
  25. package/dist/commands/pull.d.ts.map +1 -0
  26. package/dist/commands/pull.js +97 -0
  27. package/dist/compiler/errors.d.ts +20 -0
  28. package/dist/compiler/errors.d.ts.map +1 -0
  29. package/dist/compiler/errors.js +35 -0
  30. package/dist/compiler/evalStatic.d.ts +3 -0
  31. package/dist/compiler/evalStatic.d.ts.map +1 -0
  32. package/dist/compiler/evalStatic.js +57 -0
  33. package/dist/compiler/flatten.js +1 -0
  34. package/dist/compiler/helpers/duplicateIds.d.ts +9 -0
  35. package/dist/compiler/helpers/duplicateIds.d.ts.map +1 -0
  36. package/dist/compiler/helpers/duplicateIds.js +71 -0
  37. package/dist/compiler/index.d.ts +16 -0
  38. package/dist/compiler/index.d.ts.map +1 -0
  39. package/dist/compiler/index.js +934 -0
  40. package/dist/compiler/parsePage.d.ts +15 -0
  41. package/dist/compiler/parsePage.d.ts.map +1 -0
  42. package/dist/compiler/parsePage.js +562 -0
  43. package/dist/compiler/registry.d.ts +4 -0
  44. package/dist/compiler/registry.d.ts.map +1 -0
  45. package/dist/compiler/registry.js +44 -0
  46. package/dist/compiler/reverse.d.ts +17 -0
  47. package/dist/compiler/reverse.d.ts.map +1 -0
  48. package/dist/compiler/reverse.js +1632 -0
  49. package/dist/compiler/types.d.ts +21 -0
  50. package/dist/compiler/types.d.ts.map +1 -0
  51. package/dist/compiler/types.js +1 -0
  52. package/dist/components/index.d.ts +21 -0
  53. package/dist/components/index.d.ts.map +1 -0
  54. package/dist/components/index.js +21 -0
  55. package/dist/components/primitives/BaseComponent.d.ts +32 -0
  56. package/dist/components/primitives/BaseComponent.d.ts.map +1 -0
  57. package/dist/components/primitives/BaseComponent.js +26 -0
  58. package/dist/components/primitives/BookMeeting.d.ts +18 -0
  59. package/dist/components/primitives/BookMeeting.d.ts.map +1 -0
  60. package/dist/components/primitives/BookMeeting.js +5 -0
  61. package/dist/components/primitives/Chart.d.ts +41 -0
  62. package/dist/components/primitives/Chart.d.ts.map +1 -0
  63. package/dist/components/primitives/Chart.js +5 -0
  64. package/dist/components/primitives/Container.d.ts +8 -0
  65. package/dist/components/primitives/Container.d.ts.map +1 -0
  66. package/dist/components/primitives/Container.js +5 -0
  67. package/dist/components/primitives/CustomButton.d.ts +37 -0
  68. package/dist/components/primitives/CustomButton.d.ts.map +1 -0
  69. package/dist/components/primitives/CustomButton.js +10 -0
  70. package/dist/components/primitives/CustomHTML.d.ts +8 -0
  71. package/dist/components/primitives/CustomHTML.d.ts.map +1 -0
  72. package/dist/components/primitives/CustomHTML.js +5 -0
  73. package/dist/components/primitives/FileUpload.d.ts +18 -0
  74. package/dist/components/primitives/FileUpload.d.ts.map +1 -0
  75. package/dist/components/primitives/FileUpload.js +16 -0
  76. package/dist/components/primitives/InputBox.d.ts +34 -0
  77. package/dist/components/primitives/InputBox.d.ts.map +1 -0
  78. package/dist/components/primitives/InputBox.js +25 -0
  79. package/dist/components/primitives/Lottie.d.ts +11 -0
  80. package/dist/components/primitives/Lottie.d.ts.map +1 -0
  81. package/dist/components/primitives/Lottie.js +5 -0
  82. package/dist/components/primitives/MediaEmbed.d.ts +13 -0
  83. package/dist/components/primitives/MediaEmbed.d.ts.map +1 -0
  84. package/dist/components/primitives/MediaEmbed.js +6 -0
  85. package/dist/components/primitives/MediaImage.d.ts +8 -0
  86. package/dist/components/primitives/MediaImage.d.ts.map +1 -0
  87. package/dist/components/primitives/MediaImage.js +5 -0
  88. package/dist/components/primitives/OptionSelector.d.ts +35 -0
  89. package/dist/components/primitives/OptionSelector.d.ts.map +1 -0
  90. package/dist/components/primitives/OptionSelector.js +8 -0
  91. package/dist/components/primitives/PaypalCheckout.d.ts +25 -0
  92. package/dist/components/primitives/PaypalCheckout.d.ts.map +1 -0
  93. package/dist/components/primitives/PaypalCheckout.js +5 -0
  94. package/dist/components/primitives/PlainText.d.ts +6 -0
  95. package/dist/components/primitives/PlainText.d.ts.map +1 -0
  96. package/dist/components/primitives/PlainText.js +5 -0
  97. package/dist/components/primitives/ProgressBar.d.ts +15 -0
  98. package/dist/components/primitives/ProgressBar.d.ts.map +1 -0
  99. package/dist/components/primitives/ProgressBar.js +5 -0
  100. package/dist/components/primitives/RichText.d.ts +6 -0
  101. package/dist/components/primitives/RichText.d.ts.map +1 -0
  102. package/dist/components/primitives/RichText.js +5 -0
  103. package/dist/components/primitives/RichTextMarkdown.d.ts +6 -0
  104. package/dist/components/primitives/RichTextMarkdown.d.ts.map +1 -0
  105. package/dist/components/primitives/RichTextMarkdown.js +5 -0
  106. package/dist/components/primitives/Rive.d.ts +16 -0
  107. package/dist/components/primitives/Rive.d.ts.map +1 -0
  108. package/dist/components/primitives/Rive.js +8 -0
  109. package/dist/components/primitives/StripeCheckout.d.ts +52 -0
  110. package/dist/components/primitives/StripeCheckout.d.ts.map +1 -0
  111. package/dist/components/primitives/StripeCheckout.js +5 -0
  112. package/dist/components/primitives/StripeCheckout2.d.ts +30 -0
  113. package/dist/components/primitives/StripeCheckout2.d.ts.map +1 -0
  114. package/dist/components/primitives/StripeCheckout2.js +7 -0
  115. package/dist/proxy/injectApiInterceptor.d.ts +6 -0
  116. package/dist/proxy/injectApiInterceptor.d.ts.map +1 -0
  117. package/dist/proxy/injectApiInterceptor.js +66 -0
  118. package/dist/proxy/injectReload.d.ts +2 -0
  119. package/dist/proxy/injectReload.d.ts.map +1 -0
  120. package/dist/proxy/injectReload.js +14 -0
  121. package/dist/proxy/injectWorkbench.d.ts +4 -0
  122. package/dist/proxy/injectWorkbench.d.ts.map +1 -0
  123. package/dist/proxy/injectWorkbench.js +16 -0
  124. package/dist/proxy/server.d.ts +11 -0
  125. package/dist/proxy/server.d.ts.map +1 -0
  126. package/dist/proxy/server.js +246 -0
  127. package/dist/proxy/sse.d.ts +5 -0
  128. package/dist/proxy/sse.d.ts.map +1 -0
  129. package/dist/proxy/sse.js +17 -0
  130. package/dist/types-builder.d.ts +800 -0
  131. package/dist/types-builder.d.ts.map +1 -0
  132. package/dist/types-builder.js +20 -0
  133. package/dist/workbench/ActionsPanel.d.ts +6 -0
  134. package/dist/workbench/ActionsPanel.d.ts.map +1 -0
  135. package/dist/workbench/ActionsPanel.js +47 -0
  136. package/dist/workbench/AutofillPanel.d.ts +6 -0
  137. package/dist/workbench/AutofillPanel.d.ts.map +1 -0
  138. package/dist/workbench/AutofillPanel.js +543 -0
  139. package/dist/workbench/ComputedFieldsPanel.d.ts +6 -0
  140. package/dist/workbench/ComputedFieldsPanel.d.ts.map +1 -0
  141. package/dist/workbench/ComputedFieldsPanel.js +31 -0
  142. package/dist/workbench/ExperimentsPanel.d.ts +6 -0
  143. package/dist/workbench/ExperimentsPanel.d.ts.map +1 -0
  144. package/dist/workbench/ExperimentsPanel.js +182 -0
  145. package/dist/workbench/FieldEditorPanel.d.ts +9 -0
  146. package/dist/workbench/FieldEditorPanel.d.ts.map +1 -0
  147. package/dist/workbench/FieldEditorPanel.js +650 -0
  148. package/dist/workbench/InspectorPanel.d.ts +6 -0
  149. package/dist/workbench/InspectorPanel.d.ts.map +1 -0
  150. package/dist/workbench/InspectorPanel.js +341 -0
  151. package/dist/workbench/PageNavigator.d.ts +6 -0
  152. package/dist/workbench/PageNavigator.d.ts.map +1 -0
  153. package/dist/workbench/PageNavigator.js +123 -0
  154. package/dist/workbench/SchemaPanel.d.ts +6 -0
  155. package/dist/workbench/SchemaPanel.d.ts.map +1 -0
  156. package/dist/workbench/SchemaPanel.js +222 -0
  157. package/dist/workbench/UserDataPanel.d.ts +6 -0
  158. package/dist/workbench/UserDataPanel.d.ts.map +1 -0
  159. package/dist/workbench/UserDataPanel.js +350 -0
  160. package/dist/workbench/WorkbenchApp.d.ts +6 -0
  161. package/dist/workbench/WorkbenchApp.d.ts.map +1 -0
  162. package/dist/workbench/WorkbenchApp.js +193 -0
  163. package/dist/workbench/cloudflare-worker/README.md +31 -0
  164. package/dist/workbench/cloudflare-worker/public/workbench.css +1614 -0
  165. package/dist/workbench/cloudflare-worker/public/workbench.js +77 -0
  166. package/dist/workbench/cloudflare-worker/worker.js +40 -0
  167. package/dist/workbench/cloudflare-worker/wrangler.toml +10 -0
  168. package/dist/workbench/index.d.ts +9 -0
  169. package/dist/workbench/index.d.ts.map +1 -0
  170. package/dist/workbench/index.js +44 -0
  171. package/dist/workbench/workbench.css +1614 -0
  172. package/dist/workbench/workbench.js +77 -0
  173. package/package.json +79 -0
@@ -0,0 +1,15 @@
1
+ import type { ComponentJson, PageJson } from './types.js';
2
+ export declare function parsePageFromFile(args: {
3
+ code: string;
4
+ filePath: string;
5
+ pageKey: string;
6
+ }): PageJson;
7
+ /**
8
+ * Parses a global component file and returns the components.
9
+ * Similar to parsePageFromFile but simpler - no pageKey needed, just returns components.
10
+ */
11
+ export declare function parseGlobalComponentsFromFile(args: {
12
+ code: string;
13
+ filePath: string;
14
+ }): ComponentJson[];
15
+ //# sourceMappingURL=parsePage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parsePage.d.ts","sourceRoot":"","sources":["../../src/compiler/parsePage.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAezD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE;IACtC,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;CAChB,GAAG,QAAQ,CA+GX;AAED;;;GAGG;AACH,wBAAgB,6BAA6B,CAAC,IAAI,EAAE;IAClD,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;CACjB,GAAG,aAAa,EAAE,CAmGlB"}
@@ -0,0 +1,562 @@
1
+ import { parse } from '@babel/parser';
2
+ import traverseImport from '@babel/traverse';
3
+ import { ALLOWED_PRIMITIVES, TYPE_MAP, DEFAULTS_BY_TYPE } from './registry.js';
4
+ import { CompileError } from './errors.js';
5
+ import { evalStatic } from './evalStatic.js';
6
+ const traverse = (traverseImport.default ?? traverseImport);
7
+ export function parsePageFromFile(args) {
8
+ let ast;
9
+ try {
10
+ ast = parse(args.code, {
11
+ sourceType: 'module',
12
+ plugins: ['typescript', 'jsx'],
13
+ sourceFilename: args.filePath,
14
+ });
15
+ }
16
+ catch (error) {
17
+ // Babel parser errors have loc property with line/column
18
+ const line = error.loc?.line ?? error.loc?.start?.line;
19
+ const column = error.loc?.column ?? error.loc?.start?.column;
20
+ throw new CompileError(error.message || 'Syntax error', {
21
+ file: args.filePath,
22
+ line,
23
+ column,
24
+ pageKey: args.pageKey,
25
+ });
26
+ }
27
+ // collect static consts in module scope + inside page function (v1: module scope only is fine)
28
+ const constEnv = new Map();
29
+ traverse(ast, {
30
+ VariableDeclarator(path) {
31
+ const id = path.node.id;
32
+ const init = path.node.init;
33
+ if (id.type === 'Identifier' && init) {
34
+ // only accept if init is statically evaluable
35
+ try {
36
+ const v = evalStatic(init, constEnv, args.filePath);
37
+ constEnv.set(id.name, v);
38
+ }
39
+ catch {
40
+ // ignore non-static consts
41
+ }
42
+ }
43
+ },
44
+ });
45
+ // find exported function and its return JSX
46
+ // Supports both default exports (preferred) and named exports
47
+ let rootJsx = null;
48
+ let rootFragment = null;
49
+ // Helper to extract JSX from a function body
50
+ const extractJsxFromFunction = (fnBody) => {
51
+ const ret = fnBody.body.find((s) => s.type === 'ReturnStatement');
52
+ const arg = ret?.argument;
53
+ if (arg) {
54
+ if (arg.type === 'JSXElement') {
55
+ rootJsx = arg;
56
+ }
57
+ else if (arg.type === 'JSXFragment') {
58
+ rootFragment = arg;
59
+ }
60
+ }
61
+ };
62
+ traverse(ast, {
63
+ // Handle: export default function PageName() { ... }
64
+ ExportDefaultDeclaration(path) {
65
+ const decl = path.node.declaration;
66
+ if (decl.type === 'FunctionDeclaration') {
67
+ extractJsxFromFunction(decl.body);
68
+ }
69
+ },
70
+ // Handle: export function PageName() { ... } (legacy support)
71
+ ExportNamedDeclaration(path) {
72
+ const decl = path.node.declaration;
73
+ if (!decl)
74
+ return;
75
+ if (decl.type === 'FunctionDeclaration' && decl.id?.name) {
76
+ extractJsxFromFunction(decl.body);
77
+ }
78
+ },
79
+ });
80
+ const components = [];
81
+ if (rootJsx) {
82
+ // Single root element
83
+ const tree = jsxToTree(rootJsx, constEnv, args.filePath);
84
+ flatten(tree, null, components, args.pageKey, args.filePath);
85
+ }
86
+ else if (rootFragment) {
87
+ // Multiple root elements (fragment) - allow empty fragments for blank pages
88
+ const fragmentChildren = [];
89
+ const fragment = rootFragment;
90
+ for (const c of fragment.children) {
91
+ if (c.type === 'JSXElement') {
92
+ fragmentChildren.push(c);
93
+ }
94
+ }
95
+ // Allow blank pages - if fragment has no JSXElement children, components array stays empty
96
+ for (const child of fragmentChildren) {
97
+ const tree = jsxToTree(child, constEnv, args.filePath);
98
+ flatten(tree, null, components, args.pageKey, args.filePath);
99
+ }
100
+ }
101
+ else {
102
+ // Allow blank pages - if no JSX found, components array stays empty
103
+ // This handles pages that return null, undefined, or have no return statement
104
+ }
105
+ return {
106
+ key: args.pageKey,
107
+ showNav: true,
108
+ tags: [],
109
+ id: `page_${args.pageKey}`,
110
+ components,
111
+ };
112
+ }
113
+ /**
114
+ * Parses a global component file and returns the components.
115
+ * Similar to parsePageFromFile but simpler - no pageKey needed, just returns components.
116
+ */
117
+ export function parseGlobalComponentsFromFile(args) {
118
+ let ast;
119
+ try {
120
+ ast = parse(args.code, {
121
+ sourceType: 'module',
122
+ plugins: ['typescript', 'jsx'],
123
+ sourceFilename: args.filePath,
124
+ });
125
+ }
126
+ catch (error) {
127
+ // Babel parser errors have loc property with line/column
128
+ const line = error.loc?.line ?? error.loc?.start?.line;
129
+ const column = error.loc?.column ?? error.loc?.start?.column;
130
+ throw new CompileError(error.message || 'Syntax error', {
131
+ file: args.filePath,
132
+ line,
133
+ column,
134
+ });
135
+ }
136
+ // collect static consts in module scope
137
+ const constEnv = new Map();
138
+ traverse(ast, {
139
+ VariableDeclarator(path) {
140
+ const id = path.node.id;
141
+ const init = path.node.init;
142
+ if (id.type === 'Identifier' && init) {
143
+ try {
144
+ const v = evalStatic(init, constEnv, args.filePath);
145
+ constEnv.set(id.name, v);
146
+ }
147
+ catch {
148
+ // ignore non-static consts
149
+ }
150
+ }
151
+ },
152
+ });
153
+ // find exported function and its return JSX
154
+ // Supports both default exports (preferred) and named exports
155
+ let rootJsx = null;
156
+ let rootFragment = null;
157
+ // Helper to extract JSX from a function body
158
+ const extractJsxFromFunction = (fnBody) => {
159
+ const ret = fnBody.body.find((s) => s.type === 'ReturnStatement');
160
+ const arg = ret?.argument;
161
+ if (arg) {
162
+ if (arg.type === 'JSXElement') {
163
+ rootJsx = arg;
164
+ }
165
+ else if (arg.type === 'JSXFragment') {
166
+ rootFragment = arg;
167
+ }
168
+ }
169
+ };
170
+ traverse(ast, {
171
+ // Handle: export default function ComponentName() { ... }
172
+ ExportDefaultDeclaration(path) {
173
+ const decl = path.node.declaration;
174
+ if (decl.type === 'FunctionDeclaration') {
175
+ extractJsxFromFunction(decl.body);
176
+ }
177
+ },
178
+ // Handle: export function ComponentName() { ... } (legacy support)
179
+ ExportNamedDeclaration(path) {
180
+ const decl = path.node.declaration;
181
+ if (!decl)
182
+ return;
183
+ if (decl.type === 'FunctionDeclaration' && decl.id?.name) {
184
+ extractJsxFromFunction(decl.body);
185
+ }
186
+ },
187
+ });
188
+ const components = [];
189
+ if (rootJsx) {
190
+ // Single root element
191
+ const tree = jsxToTree(rootJsx, constEnv, args.filePath);
192
+ flatten(tree, null, components, '', args.filePath); // Empty pageKey for global components
193
+ }
194
+ else if (rootFragment) {
195
+ // Multiple root elements (fragment)
196
+ const fragmentChildren = [];
197
+ const fragment = rootFragment;
198
+ for (const c of fragment.children) {
199
+ if (c.type === 'JSXElement') {
200
+ fragmentChildren.push(c);
201
+ }
202
+ }
203
+ for (const child of fragmentChildren) {
204
+ const tree = jsxToTree(child, constEnv, args.filePath);
205
+ flatten(tree, null, components, '', args.filePath); // Empty pageKey for global components
206
+ }
207
+ }
208
+ return components;
209
+ }
210
+ function jsxToTree(el, constEnv, filePath) {
211
+ const opening = el.openingElement;
212
+ const tagName = getJsxTagName(opening.name);
213
+ if (!ALLOWED_PRIMITIVES.has(tagName)) {
214
+ throw new CompileError(`Unknown/unsupported primitive <${tagName}>.`, loc(filePath, opening));
215
+ }
216
+ const jsonType = TYPE_MAP[tagName];
217
+ const attrs = attrsToProps(opening.attributes, constEnv, filePath);
218
+ const id = requireString(attrs, 'id', filePath, opening);
219
+ const key = requireString(attrs, 'key', filePath, opening);
220
+ const tags = (attrs.tags ?? []);
221
+ if (!Array.isArray(tags) || tags.some((t) => typeof t !== 'string')) {
222
+ throw new CompileError(`Prop "tags" must be an array of strings.`, loc(filePath, opening));
223
+ }
224
+ delete attrs.id;
225
+ delete attrs.key;
226
+ delete attrs.tags;
227
+ // For CustomHTML, convert all children (including text) to HTML string
228
+ if (tagName === 'CustomHTML') {
229
+ // Check if there are any children (including text nodes and fragments)
230
+ const hasChildren = el.children.some((c) => c.type === 'JSXElement' ||
231
+ c.type === 'JSXFragment' ||
232
+ (c.type === 'JSXText' && c.value.trim().length > 0) ||
233
+ c.type === 'JSXExpressionContainer');
234
+ if (hasChildren) {
235
+ // Convert JSX children to HTML string
236
+ const htmlString = jsxChildrenToHTML(el.children, constEnv, filePath);
237
+ attrs.text = htmlString;
238
+ }
239
+ // Return with empty children array since we've converted them to HTML
240
+ return {
241
+ tag: tagName,
242
+ type: jsonType,
243
+ id,
244
+ key,
245
+ tags,
246
+ props: attrs,
247
+ children: [],
248
+ loc: loc(filePath, opening),
249
+ };
250
+ }
251
+ // children JSXElements only (ignore whitespace text nodes)
252
+ const children = el.children
253
+ .filter((c) => c.type === 'JSXElement')
254
+ .map((c) => jsxToTree(c, constEnv, filePath));
255
+ // v1 rule: only Container can have children (CustomHTML handled above)
256
+ if (children.length > 0 && tagName !== 'Container') {
257
+ throw new CompileError(`<${tagName}> cannot have children. Only <Container> and <CustomHTML> may contain children.`, loc(filePath, opening));
258
+ }
259
+ // enforce OptionSelector uses buttons
260
+ if (tagName === 'OptionSelector' && 'options' in attrs) {
261
+ throw new CompileError(`OptionSelector does not support prop "options". Use "buttons".`, loc(filePath, opening));
262
+ }
263
+ return {
264
+ tag: tagName,
265
+ type: jsonType,
266
+ id,
267
+ key,
268
+ tags,
269
+ props: attrs,
270
+ children,
271
+ loc: loc(filePath, opening),
272
+ };
273
+ }
274
+ function flatten(node, parentId, out, pageKey, filePath) {
275
+ const base = {
276
+ type: node.type,
277
+ id: node.id,
278
+ key: node.key,
279
+ tags: node.tags,
280
+ };
281
+ if (parentId)
282
+ base.parent_id = parentId;
283
+ const defaults = DEFAULTS_BY_TYPE[node.type] ?? {};
284
+ // Extract extra props from props={...} (round-trip from reverse compiler) and merge into component
285
+ const { props: extraProps, ...restProps } = node.props;
286
+ const mergedExtra = extraProps != null && typeof extraProps === 'object' && !Array.isArray(extraProps)
287
+ ? extraProps
288
+ : {};
289
+ const json = {
290
+ ...base,
291
+ ...defaults,
292
+ ...restProps,
293
+ ...mergedExtra,
294
+ };
295
+ // Helper to create errors with component context
296
+ const createError = (message, loc) => {
297
+ return new CompileError(message, {
298
+ ...loc,
299
+ pageKey,
300
+ componentId: node.id,
301
+ componentKey: node.key,
302
+ });
303
+ };
304
+ // Normalize OptionSelector buttons: generate IDs + defaults
305
+ if (json.type === 'OptionSelector') {
306
+ const buttons = json.buttons;
307
+ if (buttons != null) {
308
+ if (!Array.isArray(buttons)) {
309
+ throw createError(`OptionSelector requires "buttons" as an array. Got: ${typeof buttons}`, node.loc);
310
+ }
311
+ json.buttons = buttons.map((b) => {
312
+ if (!b || typeof b !== 'object') {
313
+ throw createError(`Each button must be an object.`, node.loc);
314
+ }
315
+ // key can be string or null, text is optional
316
+ if (b.key !== null && typeof b.key !== 'string') {
317
+ throw createError(`Each button requires "key" to be a string or null. Got: ${typeof b.key}`, node.loc);
318
+ }
319
+ if (b.text != null && typeof b.text !== 'string') {
320
+ throw createError(`Button "text" must be a string if provided. Got: ${typeof b.text}`, node.loc);
321
+ }
322
+ // Preserve all OptionSelectorButton properties from the input
323
+ // Required properties
324
+ const result = {
325
+ id: typeof b.id === 'string' ? b.id : `option_${b.key}`,
326
+ key: b.key,
327
+ };
328
+ // Optional properties - only include if present
329
+ if (b.text != null)
330
+ result.text = b.text;
331
+ if (b.description != null)
332
+ result.description = b.description;
333
+ // triggerEvent defaults to 'no-action' if not provided
334
+ result.triggerEvent = typeof b.triggerEvent === 'string' ? b.triggerEvent : 'no-action';
335
+ if (b.conditions != null)
336
+ result.conditions = b.conditions;
337
+ if (b.openUrlInNewTab != null)
338
+ result.openUrlInNewTab = b.openUrlInNewTab;
339
+ if (b.url != null)
340
+ result.url = b.url;
341
+ if (b.single_select != null)
342
+ result.single_select = b.single_select;
343
+ if (b.icon != null)
344
+ result.icon = b.icon;
345
+ if (b.emojiIcon != null)
346
+ result.emojiIcon = b.emojiIcon;
347
+ if (b.imageUrl != null)
348
+ result.imageUrl = b.imageUrl;
349
+ if (b.imageAltText != null)
350
+ result.imageAltText = b.imageAltText;
351
+ if (b.hide != null)
352
+ result.hide = b.hide;
353
+ if (b.is_repeatable_button != null)
354
+ result.is_repeatable_button = b.is_repeatable_button;
355
+ return result;
356
+ });
357
+ }
358
+ // If buttons is null/undefined, leave it as is (optional prop)
359
+ }
360
+ out.push(json);
361
+ for (const child of node.children) {
362
+ flatten(child, node.id, out, pageKey, filePath);
363
+ }
364
+ }
365
+ function attrsToProps(attrs, constEnv, filePath) {
366
+ const out = {};
367
+ for (const a of attrs) {
368
+ if (a.type === 'JSXSpreadAttribute') {
369
+ throw new CompileError('JSX spread props are not supported.', loc(filePath, a));
370
+ }
371
+ const name = a.name.type === 'JSXIdentifier' ? a.name.name : `${a.name.namespace.name}:${a.name.name.name}`;
372
+ if (!a.value) {
373
+ // <X disabled />
374
+ out[name] = true;
375
+ continue;
376
+ }
377
+ if (a.value.type === 'StringLiteral') {
378
+ out[name] = a.value.value;
379
+ continue;
380
+ }
381
+ if (a.value.type === 'JSXExpressionContainer') {
382
+ out[name] = evalStatic(a.value.expression, constEnv, filePath);
383
+ continue;
384
+ }
385
+ throw new CompileError(`Unsupported JSX attribute value for "${name}".`, loc(filePath, a));
386
+ }
387
+ return out;
388
+ }
389
+ function getJsxTagName(n) {
390
+ if (n.type === 'JSXIdentifier')
391
+ return n.name;
392
+ throw new CompileError('Only simple JSX identifiers are supported (no member expressions).');
393
+ }
394
+ function requireString(obj, prop, filePath, node) {
395
+ const v = obj[prop];
396
+ if (typeof v !== 'string' || v.length === 0) {
397
+ throw new CompileError(`Missing required string prop "${prop}".`, loc(filePath, node));
398
+ }
399
+ return v;
400
+ }
401
+ function loc(file, node) {
402
+ return { file, line: node.loc?.start?.line, column: node.loc?.start?.column };
403
+ }
404
+ /**
405
+ * Converts JSX children to HTML string.
406
+ * Handles JSXElements, JSXText, JSXExpressions, and JSXFragments.
407
+ * @param preserveWhitespace If true, preserves all whitespace (for <style> and <script> tags)
408
+ */
409
+ function jsxChildrenToHTML(children, constEnv, filePath, preserveWhitespace = false) {
410
+ let html = '';
411
+ for (const child of children) {
412
+ if (child.type === 'JSXElement') {
413
+ html += jsxElementToHTML(child, constEnv, filePath);
414
+ }
415
+ else if (child.type === 'JSXText') {
416
+ const text = child.value;
417
+ // For <style> and <script> tags, preserve all whitespace
418
+ // For other tags, skip whitespace-only text (it's likely indentation)
419
+ if (preserveWhitespace || text.trim().length > 0) {
420
+ html += escapeHTML(text);
421
+ }
422
+ }
423
+ else if (child.type === 'JSXExpressionContainer') {
424
+ // Try to evaluate static expressions
425
+ try {
426
+ const value = evalStatic(child.expression, constEnv, filePath);
427
+ if (typeof value === 'string') {
428
+ html += escapeHTML(value);
429
+ }
430
+ else if (value != null) {
431
+ html += escapeHTML(String(value));
432
+ }
433
+ }
434
+ catch {
435
+ throw new CompileError('CustomHTML children must be static. Dynamic expressions are not supported.', loc(filePath, child));
436
+ }
437
+ }
438
+ else if (child.type === 'JSXFragment') {
439
+ // Handle fragments by recursively processing their children
440
+ html += jsxChildrenToHTML(child.children, constEnv, filePath, preserveWhitespace);
441
+ }
442
+ // Ignore JSXSpreadChild and other unsupported types
443
+ }
444
+ return html;
445
+ }
446
+ /**
447
+ * HTML5 void elements - these are the only elements that can be self-closing in HTML.
448
+ * For all other elements, we must emit explicit closing tags even if they're self-closing in JSX.
449
+ * See: https://html.spec.whatwg.org/multipage/syntax.html#void-elements
450
+ */
451
+ const HTML_VOID_ELEMENTS = new Set([
452
+ 'area',
453
+ 'base',
454
+ 'br',
455
+ 'col',
456
+ 'embed',
457
+ 'hr',
458
+ 'img',
459
+ 'input',
460
+ 'link',
461
+ 'meta',
462
+ 'param',
463
+ 'source',
464
+ 'track',
465
+ 'wbr',
466
+ ]);
467
+ /**
468
+ * Converts a JSX element to HTML string.
469
+ */
470
+ function jsxElementToHTML(el, constEnv, filePath) {
471
+ const opening = el.openingElement;
472
+ const tagName = getJsxTagName(opening.name);
473
+ // Build attributes string
474
+ const attrs = [];
475
+ for (const attr of opening.attributes) {
476
+ if (attr.type === 'JSXAttribute') {
477
+ const name = attr.name.type === 'JSXIdentifier'
478
+ ? attr.name.name
479
+ : `${attr.name.namespace.name}:${attr.name.name.name}`;
480
+ if (!attr.value) {
481
+ // Boolean attribute
482
+ attrs.push(name);
483
+ }
484
+ else if (attr.value.type === 'StringLiteral') {
485
+ attrs.push(`${name}="${escapeHTMLAttribute(attr.value.value)}"`);
486
+ }
487
+ else if (attr.value.type === 'JSXExpressionContainer') {
488
+ try {
489
+ const value = evalStatic(attr.value.expression, constEnv, filePath);
490
+ if (name === 'style' && typeof value === 'object' && value !== null) {
491
+ // Convert style object to CSS string
492
+ const cssString = styleObjectToCSS(value);
493
+ attrs.push(`${name}="${escapeHTMLAttribute(cssString)}"`);
494
+ }
495
+ else if (typeof value === 'string') {
496
+ attrs.push(`${name}="${escapeHTMLAttribute(value)}"`);
497
+ }
498
+ else if (typeof value === 'number' || typeof value === 'boolean') {
499
+ attrs.push(`${name}="${String(value)}"`);
500
+ }
501
+ }
502
+ catch {
503
+ throw new CompileError('CustomHTML attribute values must be static. Dynamic expressions are not supported.', loc(filePath, attr));
504
+ }
505
+ }
506
+ }
507
+ }
508
+ const attrsStr = attrs.length > 0 ? ' ' + attrs.join(' ') : '';
509
+ // For <style> and <script> tags, preserve all whitespace to maintain CSS/JS formatting
510
+ const isRawTextTag = tagName.toLowerCase() === 'style' || tagName.toLowerCase() === 'script';
511
+ const childrenHTML = jsxChildrenToHTML(el.children, constEnv, filePath, isRawTextTag);
512
+ if (el.closingElement) {
513
+ return `<${tagName}${attrsStr}>${childrenHTML}</${tagName}>`;
514
+ }
515
+ else {
516
+ // Self-closing in JSX - check if it's an HTML void element
517
+ const isVoidElement = HTML_VOID_ELEMENTS.has(tagName.toLowerCase());
518
+ if (isVoidElement) {
519
+ // Void elements can be self-closing in HTML
520
+ return `<${tagName}${attrsStr} />`;
521
+ }
522
+ else {
523
+ // Non-void elements MUST have explicit closing tags in HTML
524
+ // Otherwise browsers interpret <div /> as <div> (unclosed)
525
+ return `<${tagName}${attrsStr}></${tagName}>`;
526
+ }
527
+ }
528
+ }
529
+ /**
530
+ * Escapes HTML entities in text content.
531
+ */
532
+ function escapeHTML(text) {
533
+ return text
534
+ .replace(/&/g, '&amp;')
535
+ .replace(/</g, '&lt;')
536
+ .replace(/>/g, '&gt;')
537
+ .replace(/"/g, '&quot;')
538
+ .replace(/'/g, '&#39;');
539
+ }
540
+ /**
541
+ * Escapes HTML entities in attribute values.
542
+ */
543
+ function escapeHTMLAttribute(text) {
544
+ return text
545
+ .replace(/&/g, '&amp;')
546
+ .replace(/</g, '&lt;')
547
+ .replace(/>/g, '&gt;')
548
+ .replace(/"/g, '&quot;');
549
+ }
550
+ /**
551
+ * Converts a JSX style object to CSS string.
552
+ * Example: { margin: 0, padding: 0, color: "#171717" } -> "margin: 0; padding: 0; color: #171717;"
553
+ */
554
+ function styleObjectToCSS(styleObj) {
555
+ const cssParts = [];
556
+ for (const [key, value] of Object.entries(styleObj)) {
557
+ // Convert camelCase to kebab-case (e.g., lineHeight -> line-height)
558
+ const cssKey = key.replace(/([A-Z])/g, '-$1').toLowerCase();
559
+ cssParts.push(`${cssKey}: ${value};`);
560
+ }
561
+ return cssParts.join(' ');
562
+ }
@@ -0,0 +1,4 @@
1
+ export declare const ALLOWED_PRIMITIVES: Set<string>;
2
+ export declare const TYPE_MAP: Record<string, string>;
3
+ export declare const DEFAULTS_BY_TYPE: Record<string, Record<string, any>>;
4
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/compiler/registry.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,kBAAkB,aAoB7B,CAAA;AAGF,eAAO,MAAM,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAoB3C,CAAA;AAED,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAM,CAAA"}
@@ -0,0 +1,44 @@
1
+ export const ALLOWED_PRIMITIVES = new Set([
2
+ 'Container',
3
+ 'PlainText',
4
+ 'RichText',
5
+ 'RichTextMarkdown',
6
+ 'CustomHTML',
7
+ 'InputBox',
8
+ 'OptionSelector',
9
+ 'CustomButton',
10
+ 'StripeCheckout',
11
+ 'StripeCheckout2',
12
+ 'PaypalCheckout',
13
+ 'MediaImage',
14
+ 'MediaEmbed',
15
+ 'Lottie',
16
+ 'Rive',
17
+ 'ProgressBar',
18
+ 'FileUpload',
19
+ 'BookMeeting',
20
+ 'Chart',
21
+ ]);
22
+ // TSX tag name -> JSON "type"
23
+ export const TYPE_MAP = {
24
+ Container: 'Container',
25
+ PlainText: 'PlainText',
26
+ RichText: 'RichText',
27
+ RichTextMarkdown: 'RichTextMarkdown',
28
+ CustomHTML: 'CustomHTML',
29
+ InputBox: 'InputBox',
30
+ OptionSelector: 'OptionSelector',
31
+ CustomButton: 'CustomButton',
32
+ StripeCheckout: 'StripeCheckout',
33
+ StripeCheckout2: 'StripeCheckout2',
34
+ PaypalCheckout: 'PaypalCheckout',
35
+ MediaImage: 'MediaImage',
36
+ MediaEmbed: 'MediaEmbed',
37
+ Lottie: 'Lottie',
38
+ Rive: 'Rive',
39
+ ProgressBar: 'ProgressBar',
40
+ FileUpload: 'FileUpload',
41
+ BookMeeting: 'BookMeeting',
42
+ Chart: 'Chart',
43
+ };
44
+ export const DEFAULTS_BY_TYPE = {};
@@ -0,0 +1,17 @@
1
+ import type { PageJson } from './types.js';
2
+ export declare function reverseCompile(embeddable: {
3
+ id?: string;
4
+ pages: PageJson[];
5
+ styles?: Record<string, any>;
6
+ computedFields?: any[];
7
+ dataOutputs?: any[];
8
+ components?: any[];
9
+ [key: string]: any;
10
+ }, embeddableId: string, opts?: {
11
+ fix?: boolean;
12
+ }): Promise<void>;
13
+ /**
14
+ * Sanitizes a string to be safe for use as a filename.
15
+ */
16
+ export declare function sanitizeFileName(str: string): string;
17
+ //# sourceMappingURL=reverse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reverse.d.ts","sourceRoot":"","sources":["../../src/compiler/reverse.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,QAAQ,EAAiB,MAAM,YAAY,CAAA;AAihBzD,wBAAsB,cAAc,CAClC,UAAU,EAAE;IACV,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,QAAQ,EAAE,CAAA;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAC5B,cAAc,CAAC,EAAE,GAAG,EAAE,CAAA;IACtB,WAAW,CAAC,EAAE,GAAG,EAAE,CAAA;IACnB,UAAU,CAAC,EAAE,GAAG,EAAE,CAAA;IAClB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;CACnB,EACD,YAAY,EAAE,MAAM,EACpB,IAAI,CAAC,EAAE;IAAE,GAAG,CAAC,EAAE,OAAO,CAAA;CAAE,iBAmDzB;AA0yCD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAKpD"}