cmssy-cli 0.5.1 → 0.7.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 (37) hide show
  1. package/dist/cli.js +7 -1
  2. package/dist/cli.js.map +1 -1
  3. package/dist/commands/build.d.ts.map +1 -1
  4. package/dist/commands/build.js +67 -8
  5. package/dist/commands/build.js.map +1 -1
  6. package/dist/commands/create.d.ts.map +1 -1
  7. package/dist/commands/create.js +124 -70
  8. package/dist/commands/create.js.map +1 -1
  9. package/dist/commands/dev.d.ts.map +1 -1
  10. package/dist/commands/dev.js +178 -22
  11. package/dist/commands/dev.js.map +1 -1
  12. package/dist/commands/init.d.ts.map +1 -1
  13. package/dist/commands/init.js +80 -71
  14. package/dist/commands/init.js.map +1 -1
  15. package/dist/commands/migrate.d.ts +2 -0
  16. package/dist/commands/migrate.d.ts.map +1 -0
  17. package/dist/commands/migrate.js +225 -0
  18. package/dist/commands/migrate.js.map +1 -0
  19. package/dist/dev-ui/app.js +496 -0
  20. package/dist/dev-ui/index.html +443 -0
  21. package/dist/types/block-config.d.ts +40 -0
  22. package/dist/types/block-config.d.ts.map +1 -0
  23. package/dist/types/block-config.js +3 -0
  24. package/dist/types/block-config.js.map +1 -0
  25. package/dist/utils/block-config.d.ts +10 -0
  26. package/dist/utils/block-config.d.ts.map +1 -0
  27. package/dist/utils/block-config.js +191 -0
  28. package/dist/utils/block-config.js.map +1 -0
  29. package/dist/utils/field-schema.d.ts +10 -0
  30. package/dist/utils/field-schema.d.ts.map +1 -0
  31. package/dist/utils/field-schema.js +155 -0
  32. package/dist/utils/field-schema.js.map +1 -0
  33. package/dist/utils/type-generator.d.ts +3 -0
  34. package/dist/utils/type-generator.d.ts.map +1 -0
  35. package/dist/utils/type-generator.js +67 -0
  36. package/dist/utils/type-generator.js.map +1 -0
  37. package/package.json +8 -3
@@ -0,0 +1,443 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Cmssy Dev Server</title>
7
+ <style>
8
+ * {
9
+ margin: 0;
10
+ padding: 0;
11
+ box-sizing: border-box;
12
+ }
13
+
14
+ body {
15
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
16
+ background: #f5f5f5;
17
+ overflow: hidden;
18
+ height: 100vh;
19
+ }
20
+
21
+ .container {
22
+ display: grid;
23
+ grid-template-columns: 280px 1fr 400px;
24
+ height: 100vh;
25
+ gap: 0;
26
+ }
27
+
28
+ /* Left Panel - Blocks List */
29
+ .blocks-panel {
30
+ background: #ffffff;
31
+ border-right: 1px solid #e0e0e0;
32
+ overflow-y: auto;
33
+ }
34
+
35
+ .blocks-header {
36
+ padding: 20px;
37
+ border-bottom: 1px solid #e0e0e0;
38
+ background: #fafafa;
39
+ }
40
+
41
+ .blocks-header h1 {
42
+ font-size: 18px;
43
+ font-weight: 600;
44
+ color: #1a1a1a;
45
+ margin-bottom: 4px;
46
+ }
47
+
48
+ .blocks-header p {
49
+ font-size: 13px;
50
+ color: #666;
51
+ }
52
+
53
+ .blocks-list {
54
+ padding: 12px;
55
+ }
56
+
57
+ .block-item {
58
+ padding: 12px 16px;
59
+ margin-bottom: 4px;
60
+ border-radius: 8px;
61
+ cursor: pointer;
62
+ transition: all 0.2s;
63
+ border: 1px solid transparent;
64
+ }
65
+
66
+ .block-item:hover {
67
+ background: #f5f5f5;
68
+ }
69
+
70
+ .block-item.active {
71
+ background: #667eea;
72
+ color: white;
73
+ border-color: #667eea;
74
+ }
75
+
76
+ .block-item-name {
77
+ font-size: 14px;
78
+ font-weight: 500;
79
+ margin-bottom: 2px;
80
+ }
81
+
82
+ .block-item-type {
83
+ font-size: 12px;
84
+ opacity: 0.7;
85
+ }
86
+
87
+ /* Center Panel - Preview */
88
+ .preview-panel {
89
+ background: #fafafa;
90
+ display: flex;
91
+ flex-direction: column;
92
+ overflow: hidden;
93
+ }
94
+
95
+ .preview-header {
96
+ padding: 16px 24px;
97
+ background: white;
98
+ border-bottom: 1px solid #e0e0e0;
99
+ display: flex;
100
+ justify-content: space-between;
101
+ align-items: center;
102
+ }
103
+
104
+ .preview-title {
105
+ font-size: 16px;
106
+ font-weight: 600;
107
+ color: #1a1a1a;
108
+ }
109
+
110
+ .preview-badge {
111
+ font-size: 12px;
112
+ padding: 4px 12px;
113
+ background: #e8f5e9;
114
+ color: #2e7d32;
115
+ border-radius: 12px;
116
+ font-weight: 500;
117
+ }
118
+
119
+ .preview-content {
120
+ flex: 1;
121
+ padding: 24px;
122
+ overflow: auto;
123
+ display: flex;
124
+ align-items: center;
125
+ justify-content: center;
126
+ }
127
+
128
+ .preview-iframe-wrapper {
129
+ width: 100%;
130
+ height: 100%;
131
+ background: white;
132
+ border-radius: 12px;
133
+ box-shadow: 0 2px 8px rgba(0,0,0,0.1);
134
+ overflow: hidden;
135
+ }
136
+
137
+ .preview-iframe {
138
+ width: 100%;
139
+ height: 100%;
140
+ border: none;
141
+ }
142
+
143
+ .preview-empty {
144
+ text-align: center;
145
+ color: #999;
146
+ padding: 60px 20px;
147
+ }
148
+
149
+ .preview-empty-icon {
150
+ font-size: 48px;
151
+ margin-bottom: 16px;
152
+ opacity: 0.3;
153
+ }
154
+
155
+ /* Right Panel - Editor Drawer */
156
+ .editor-panel {
157
+ background: #ffffff;
158
+ border-left: 1px solid #e0e0e0;
159
+ display: flex;
160
+ flex-direction: column;
161
+ overflow: hidden;
162
+ }
163
+
164
+ .editor-header {
165
+ padding: 20px;
166
+ border-bottom: 1px solid #e0e0e0;
167
+ background: #fafafa;
168
+ }
169
+
170
+ .editor-header h2 {
171
+ font-size: 16px;
172
+ font-weight: 600;
173
+ color: #1a1a1a;
174
+ margin-bottom: 4px;
175
+ }
176
+
177
+ .editor-header p {
178
+ font-size: 13px;
179
+ color: #666;
180
+ }
181
+
182
+ .editor-content {
183
+ flex: 1;
184
+ overflow-y: auto;
185
+ padding: 20px;
186
+ }
187
+
188
+ .editor-empty {
189
+ text-align: center;
190
+ color: #999;
191
+ padding: 40px 20px;
192
+ }
193
+
194
+ /* Form Fields */
195
+ .field-group {
196
+ margin-bottom: 24px;
197
+ }
198
+
199
+ .field-label {
200
+ display: block;
201
+ font-size: 13px;
202
+ font-weight: 500;
203
+ color: #333;
204
+ margin-bottom: 8px;
205
+ }
206
+
207
+ .field-required {
208
+ color: #e53935;
209
+ margin-left: 2px;
210
+ }
211
+
212
+ .field-help {
213
+ font-size: 12px;
214
+ color: #666;
215
+ margin-top: 4px;
216
+ font-weight: 400;
217
+ }
218
+
219
+ .field-input {
220
+ width: 100%;
221
+ padding: 10px 12px;
222
+ border: 1px solid #ddd;
223
+ border-radius: 6px;
224
+ font-size: 14px;
225
+ font-family: inherit;
226
+ transition: all 0.2s;
227
+ }
228
+
229
+ .field-input:focus {
230
+ outline: none;
231
+ border-color: #667eea;
232
+ box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
233
+ }
234
+
235
+ .field-textarea {
236
+ min-height: 80px;
237
+ resize: vertical;
238
+ }
239
+
240
+ .field-select {
241
+ cursor: pointer;
242
+ }
243
+
244
+ .field-checkbox {
245
+ width: auto;
246
+ margin-right: 8px;
247
+ }
248
+
249
+ /* Repeater Fields */
250
+ .repeater-items {
251
+ border: 1px solid #e0e0e0;
252
+ border-radius: 8px;
253
+ padding: 12px;
254
+ background: #fafafa;
255
+ }
256
+
257
+ .repeater-item {
258
+ background: white;
259
+ border: 1px solid #e0e0e0;
260
+ border-radius: 6px;
261
+ padding: 16px;
262
+ margin-bottom: 12px;
263
+ }
264
+
265
+ .repeater-item:last-child {
266
+ margin-bottom: 0;
267
+ }
268
+
269
+ .repeater-item-header {
270
+ display: flex;
271
+ justify-content: space-between;
272
+ align-items: center;
273
+ margin-bottom: 12px;
274
+ padding-bottom: 12px;
275
+ border-bottom: 1px solid #f0f0f0;
276
+ }
277
+
278
+ .repeater-item-title {
279
+ font-size: 13px;
280
+ font-weight: 500;
281
+ color: #666;
282
+ }
283
+
284
+ .repeater-item-remove {
285
+ background: #ffebee;
286
+ color: #c62828;
287
+ border: none;
288
+ padding: 4px 12px;
289
+ border-radius: 4px;
290
+ font-size: 12px;
291
+ cursor: pointer;
292
+ font-weight: 500;
293
+ transition: all 0.2s;
294
+ }
295
+
296
+ .repeater-item-remove:hover {
297
+ background: #ffcdd2;
298
+ }
299
+
300
+ .repeater-add {
301
+ width: 100%;
302
+ padding: 10px;
303
+ background: white;
304
+ border: 1px dashed #667eea;
305
+ color: #667eea;
306
+ border-radius: 6px;
307
+ font-size: 13px;
308
+ font-weight: 500;
309
+ cursor: pointer;
310
+ transition: all 0.2s;
311
+ margin-top: 8px;
312
+ }
313
+
314
+ .repeater-add:hover {
315
+ background: #f5f7ff;
316
+ border-style: solid;
317
+ }
318
+
319
+ /* Loading State */
320
+ .loading {
321
+ display: flex;
322
+ align-items: center;
323
+ justify-content: center;
324
+ padding: 40px;
325
+ color: #999;
326
+ }
327
+
328
+ .spinner {
329
+ width: 20px;
330
+ height: 20px;
331
+ border: 2px solid #f3f3f3;
332
+ border-top: 2px solid #667eea;
333
+ border-radius: 50%;
334
+ animation: spin 1s linear infinite;
335
+ margin-right: 12px;
336
+ }
337
+
338
+ @keyframes spin {
339
+ 0% { transform: rotate(0deg); }
340
+ 100% { transform: rotate(360deg); }
341
+ }
342
+
343
+ /* Media Field */
344
+ .media-field {
345
+ display: flex;
346
+ gap: 12px;
347
+ align-items: flex-start;
348
+ }
349
+
350
+ .media-preview {
351
+ width: 80px;
352
+ height: 80px;
353
+ border: 1px solid #ddd;
354
+ border-radius: 6px;
355
+ overflow: hidden;
356
+ background: #fafafa;
357
+ display: flex;
358
+ align-items: center;
359
+ justify-content: center;
360
+ }
361
+
362
+ .media-preview img {
363
+ width: 100%;
364
+ height: 100%;
365
+ object-fit: cover;
366
+ }
367
+
368
+ .media-placeholder {
369
+ color: #999;
370
+ font-size: 12px;
371
+ }
372
+
373
+ .media-input-group {
374
+ flex: 1;
375
+ }
376
+
377
+ /* Color Field */
378
+ .color-field {
379
+ display: flex;
380
+ gap: 12px;
381
+ align-items: center;
382
+ }
383
+
384
+ .color-preview {
385
+ width: 40px;
386
+ height: 40px;
387
+ border: 1px solid #ddd;
388
+ border-radius: 6px;
389
+ cursor: pointer;
390
+ }
391
+
392
+ .color-input {
393
+ flex: 1;
394
+ }
395
+ </style>
396
+ </head>
397
+ <body>
398
+ <div class="container">
399
+ <!-- Left Panel: Blocks List -->
400
+ <div class="blocks-panel">
401
+ <div class="blocks-header">
402
+ <h1>📦 Blocks</h1>
403
+ <p id="blocks-count">Loading...</p>
404
+ </div>
405
+ <div class="blocks-list" id="blocks-list">
406
+ <div class="loading">
407
+ <div class="spinner"></div>
408
+ <span>Loading blocks...</span>
409
+ </div>
410
+ </div>
411
+ </div>
412
+
413
+ <!-- Center Panel: Preview -->
414
+ <div class="preview-panel">
415
+ <div class="preview-header">
416
+ <div class="preview-title" id="preview-title">Preview</div>
417
+ <div class="preview-badge" id="preview-status">Ready</div>
418
+ </div>
419
+ <div class="preview-content" id="preview-content">
420
+ <div class="preview-empty">
421
+ <div class="preview-empty-icon">👈</div>
422
+ <p>Select a block to preview</p>
423
+ </div>
424
+ </div>
425
+ </div>
426
+
427
+ <!-- Right Panel: Editor -->
428
+ <div class="editor-panel">
429
+ <div class="editor-header">
430
+ <h2>⚙️ Properties</h2>
431
+ <p id="editor-subtitle">No block selected</p>
432
+ </div>
433
+ <div class="editor-content" id="editor-content">
434
+ <div class="editor-empty">
435
+ Select a block to edit its properties
436
+ </div>
437
+ </div>
438
+ </div>
439
+ </div>
440
+
441
+ <script src="/dev-ui/app.js"></script>
442
+ </body>
443
+ </html>
@@ -0,0 +1,40 @@
1
+ export type FieldType = "singleLine" | "multiLine" | "richText" | "text" | "string" | "number" | "boolean" | "date" | "media" | "link" | "select" | "color" | "repeater";
2
+ export interface BaseFieldConfig {
3
+ type: FieldType;
4
+ label: string;
5
+ required?: boolean;
6
+ placeholder?: string;
7
+ defaultValue?: any;
8
+ helpText?: string;
9
+ }
10
+ export interface SelectFieldConfig extends BaseFieldConfig {
11
+ type: "select";
12
+ options: Array<{
13
+ label: string;
14
+ value: string;
15
+ }>;
16
+ }
17
+ export interface RepeaterFieldConfig extends BaseFieldConfig {
18
+ type: "repeater";
19
+ minItems?: number;
20
+ maxItems?: number;
21
+ schema: Record<string, FieldConfig>;
22
+ }
23
+ export type FieldConfig = BaseFieldConfig | SelectFieldConfig | RepeaterFieldConfig;
24
+ export interface BlockConfig {
25
+ name: string;
26
+ description?: string;
27
+ longDescription?: string;
28
+ category: string;
29
+ tags?: string[];
30
+ schema: Record<string, FieldConfig>;
31
+ pricing?: {
32
+ licenseType: "free" | "paid";
33
+ priceCents?: number;
34
+ };
35
+ }
36
+ export interface TemplateConfig extends Omit<BlockConfig, "category"> {
37
+ category?: string;
38
+ }
39
+ export type ResourceConfig = BlockConfig | TemplateConfig;
40
+ //# sourceMappingURL=block-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"block-config.d.ts","sourceRoot":"","sources":["../../src/types/block-config.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,SAAS,GACjB,YAAY,GACZ,WAAW,GACX,UAAU,GACV,MAAM,GACN,QAAQ,GACR,QAAQ,GACR,SAAS,GACT,MAAM,GACN,OAAO,GACP,MAAM,GACN,QAAQ,GACR,OAAO,GACP,UAAU,CAAC;AAEf,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,GAAG,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,iBAAkB,SAAQ,eAAe;IACxD,IAAI,EAAE,QAAQ,CAAC;IACf,OAAO,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAClD;AAED,MAAM,WAAW,mBAAoB,SAAQ,eAAe;IAC1D,IAAI,EAAE,UAAU,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;CACrC;AAED,MAAM,MAAM,WAAW,GACnB,eAAe,GACf,iBAAiB,GACjB,mBAAmB,CAAC;AAExB,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACpC,OAAO,CAAC,EAAE;QACR,WAAW,EAAE,MAAM,GAAG,MAAM,CAAC;QAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC;CACH;AAED,MAAM,WAAW,cAAe,SAAQ,IAAI,CAAC,WAAW,EAAE,UAAU,CAAC;IACnE,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,MAAM,cAAc,GAAG,WAAW,GAAG,cAAc,CAAC"}
@@ -0,0 +1,3 @@
1
+ // Type definitions for block.config.ts system
2
+ export {};
3
+ //# sourceMappingURL=block-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"block-config.js","sourceRoot":"","sources":["../../src/types/block-config.ts"],"names":[],"mappings":"AAAA,8CAA8C"}
@@ -0,0 +1,10 @@
1
+ import { BlockConfig, TemplateConfig, ResourceConfig, FieldConfig } from "../types/block-config.js";
2
+ export declare function defineBlock(config: BlockConfig): BlockConfig;
3
+ export declare function defineTemplate(config: TemplateConfig): TemplateConfig;
4
+ export declare function loadBlockConfig(blockPath: string): Promise<ResourceConfig | null>;
5
+ export declare function validateSchema(schema: Record<string, FieldConfig>, blockPath: string): Promise<{
6
+ valid: boolean;
7
+ errors: string[];
8
+ }>;
9
+ export declare function generatePackageJsonMetadata(config: ResourceConfig, packageType: "block" | "template"): any;
10
+ //# sourceMappingURL=block-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"block-config.d.ts","sourceRoot":"","sources":["../../src/utils/block-config.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,WAAW,EACX,cAAc,EACd,cAAc,EACd,WAAW,EAGZ,MAAM,0BAA0B,CAAC;AAIlC,wBAAgB,WAAW,CAAC,MAAM,EAAE,WAAW,GAAG,WAAW,CAE5D;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,cAAc,GAAG,cAAc,CAErE;AAGD,wBAAsB,eAAe,CACnC,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CA4EhC;AAGD,wBAAsB,cAAc,CAClC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,EACnC,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAqF/C;AAGD,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,cAAc,EACtB,WAAW,EAAE,OAAO,GAAG,UAAU,GAChC,GAAG,CAkBL"}
@@ -0,0 +1,191 @@
1
+ import fs from "fs-extra";
2
+ import path from "path";
3
+ import chalk from "chalk";
4
+ import { execSync } from "child_process";
5
+ import { getFieldTypes, isValidFieldType } from "./field-schema.js";
6
+ // Helper function for type-safe config authoring
7
+ export function defineBlock(config) {
8
+ return config;
9
+ }
10
+ export function defineTemplate(config) {
11
+ return config;
12
+ }
13
+ // Load block.config.ts dynamically
14
+ export async function loadBlockConfig(blockPath) {
15
+ const configPath = path.join(blockPath, "block.config.ts");
16
+ if (!fs.existsSync(configPath)) {
17
+ return null;
18
+ }
19
+ try {
20
+ // Find tsx binary - try multiple locations
21
+ const cliPath = path.dirname(path.dirname(new URL(import.meta.url).pathname));
22
+ // Possible locations for tsx binary
23
+ const possibleTsxPaths = [
24
+ path.join(cliPath, "node_modules", ".bin", "tsx"),
25
+ path.join(cliPath, "..", "..", "node_modules", ".bin", "tsx"), // If symlinked
26
+ path.join(process.cwd(), "node_modules", ".bin", "tsx"), // Project's node_modules
27
+ ];
28
+ let tsxBinary = possibleTsxPaths.find(p => fs.existsSync(p));
29
+ // If not found, use npx as fallback
30
+ if (!tsxBinary) {
31
+ tsxBinary = "npx -y tsx"; // Use npx with -y to auto-install if needed
32
+ }
33
+ const cacheDir = path.join(process.cwd(), ".cmssy", "cache");
34
+ fs.ensureDirSync(cacheDir);
35
+ // Create a mock cmssy-cli/config module in cache
36
+ const mockConfigPath = path.join(cacheDir, "cmssy-cli-config.mjs");
37
+ const mockConfig = `export const defineBlock = (config) => config;\nexport const defineTemplate = (config) => config;`;
38
+ fs.writeFileSync(mockConfigPath, mockConfig);
39
+ // Read original config and replace import path to point to mock
40
+ const configContent = fs.readFileSync(configPath, "utf-8");
41
+ const modifiedConfig = configContent.replace(/from\s+['"]cmssy-cli\/config['"]/g, `from '${mockConfigPath.replace(/\\/g, '/')}'`);
42
+ // Write modified config to temp file
43
+ const tempConfigPath = path.join(cacheDir, `temp-${path.basename(configPath)}`);
44
+ fs.writeFileSync(tempConfigPath, modifiedConfig);
45
+ // Execute with tsx - use --eval to import and output
46
+ const evalCode = `import cfg from '${tempConfigPath.replace(/\\/g, '/')}'; console.log(JSON.stringify(cfg.default || cfg));`;
47
+ // Build command - handle both direct binary path and npx
48
+ const command = tsxBinary.includes("npx")
49
+ ? `${tsxBinary} --eval "${evalCode}"`
50
+ : `"${tsxBinary}" --eval "${evalCode}"`;
51
+ const output = execSync(command, {
52
+ encoding: "utf-8",
53
+ cwd: process.cwd(),
54
+ env: {
55
+ ...process.env,
56
+ NODE_ENV: "development",
57
+ },
58
+ stdio: ["pipe", "pipe", "pipe"],
59
+ });
60
+ // Clean up
61
+ fs.removeSync(tempConfigPath);
62
+ fs.removeSync(mockConfigPath);
63
+ // Parse JSON output
64
+ const lines = output.trim().split("\n");
65
+ const jsonLine = lines[lines.length - 1];
66
+ const config = JSON.parse(jsonLine);
67
+ return config;
68
+ }
69
+ catch (error) {
70
+ throw new Error(`Failed to load block.config.ts at ${configPath}: ${error.message}`);
71
+ }
72
+ }
73
+ // Validate schema against backend field types
74
+ export async function validateSchema(schema, blockPath) {
75
+ const errors = [];
76
+ const fieldTypes = await getFieldTypes();
77
+ function validateField(key, field, parentPath = "") {
78
+ const fullPath = parentPath ? `${parentPath}.${key}` : key;
79
+ // Check if field type is valid
80
+ if (!isValidFieldType(field.type, fieldTypes)) {
81
+ errors.push(`Invalid field type "${field.type}" for field "${fullPath}". Valid types: ${fieldTypes.map((ft) => ft.type).join(", ")}`);
82
+ }
83
+ // Validate repeater nested schema
84
+ if (field.type === "repeater") {
85
+ const repeaterField = field;
86
+ if (!repeaterField.schema || typeof repeaterField.schema !== "object") {
87
+ errors.push(`Repeater field "${fullPath}" must have a "schema" property`);
88
+ }
89
+ else {
90
+ // Recursively validate nested schema
91
+ Object.entries(repeaterField.schema).forEach(([nestedKey, nestedField]) => {
92
+ validateField(nestedKey, nestedField, fullPath);
93
+ });
94
+ }
95
+ // Validate minItems/maxItems
96
+ if (repeaterField.minItems !== undefined && repeaterField.minItems < 0) {
97
+ errors.push(`Repeater field "${fullPath}" has invalid minItems (must be >= 0)`);
98
+ }
99
+ if (repeaterField.maxItems !== undefined && repeaterField.maxItems < 1) {
100
+ errors.push(`Repeater field "${fullPath}" has invalid maxItems (must be >= 1)`);
101
+ }
102
+ if (repeaterField.minItems &&
103
+ repeaterField.maxItems &&
104
+ repeaterField.minItems > repeaterField.maxItems) {
105
+ errors.push(`Repeater field "${fullPath}" has minItems > maxItems`);
106
+ }
107
+ }
108
+ // Validate select options
109
+ if (field.type === "select") {
110
+ const selectField = field;
111
+ if (!selectField.options ||
112
+ !Array.isArray(selectField.options) ||
113
+ selectField.options.length === 0) {
114
+ errors.push(`Select field "${fullPath}" must have at least one option`);
115
+ }
116
+ }
117
+ // Warn about required fields with default values
118
+ if (field.required && field.defaultValue !== undefined) {
119
+ console.warn(chalk.yellow(`Warning: Field "${fullPath}" is required but has a defaultValue. The defaultValue will be ignored.`));
120
+ }
121
+ }
122
+ Object.entries(schema).forEach(([key, field]) => {
123
+ validateField(key, field);
124
+ });
125
+ return { valid: errors.length === 0, errors };
126
+ }
127
+ // Generate package.json cmssy section from block.config.ts
128
+ export function generatePackageJsonMetadata(config, packageType) {
129
+ // Convert schema to legacy schemaFields format
130
+ const schemaFields = convertSchemaToLegacyFormat(config.schema);
131
+ // Extract default content from schema
132
+ const defaultContent = extractDefaultContent(config.schema);
133
+ return {
134
+ packageType,
135
+ displayName: config.name,
136
+ description: config.description,
137
+ longDescription: config.longDescription,
138
+ category: config.category || (packageType === "template" ? "pages" : "other"),
139
+ tags: config.tags || [],
140
+ pricing: config.pricing || { licenseType: "free" },
141
+ schemaFields,
142
+ defaultContent,
143
+ };
144
+ }
145
+ function convertSchemaToLegacyFormat(schema) {
146
+ const fields = [];
147
+ function convertField(key, field) {
148
+ const baseField = {
149
+ key,
150
+ type: field.type,
151
+ label: field.label,
152
+ required: field.required || false,
153
+ };
154
+ if (field.placeholder) {
155
+ baseField.placeholder = field.placeholder;
156
+ }
157
+ if (field.type === "select") {
158
+ const selectField = field;
159
+ baseField.options = selectField.options;
160
+ }
161
+ if (field.type === "repeater") {
162
+ const repeaterField = field;
163
+ const nestedFields = convertSchemaToLegacyFormat(repeaterField.schema);
164
+ baseField.minItems = repeaterField.minItems;
165
+ baseField.maxItems = repeaterField.maxItems;
166
+ baseField.itemSchema = {
167
+ type: "object",
168
+ fields: nestedFields,
169
+ };
170
+ }
171
+ return baseField;
172
+ }
173
+ Object.entries(schema).forEach(([key, field]) => {
174
+ fields.push(convertField(key, field));
175
+ });
176
+ return fields;
177
+ }
178
+ function extractDefaultContent(schema) {
179
+ const content = {};
180
+ Object.entries(schema).forEach(([key, field]) => {
181
+ if (field.defaultValue !== undefined) {
182
+ content[key] = field.defaultValue;
183
+ }
184
+ else if (field.type === "repeater") {
185
+ // Repeaters default to empty array
186
+ content[key] = [];
187
+ }
188
+ });
189
+ return content;
190
+ }
191
+ //# sourceMappingURL=block-config.js.map