@memberjunction/react-runtime 2.74.0 → 2.76.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 (54) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +27 -0
  3. package/README.md +96 -4
  4. package/dist/compiler/component-compiler.d.ts +0 -1
  5. package/dist/compiler/component-compiler.d.ts.map +1 -1
  6. package/dist/compiler/component-compiler.js +34 -25
  7. package/dist/index.d.ts +2 -2
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +3 -6
  10. package/dist/registry/component-resolver.d.ts +1 -6
  11. package/dist/registry/component-resolver.d.ts.map +1 -1
  12. package/dist/registry/component-resolver.js +19 -19
  13. package/dist/registry/index.d.ts +2 -1
  14. package/dist/registry/index.d.ts.map +1 -1
  15. package/dist/registry/index.js +3 -1
  16. package/dist/runtime/component-hierarchy.d.ts +1 -1
  17. package/dist/runtime/component-hierarchy.d.ts.map +1 -1
  18. package/dist/runtime/component-hierarchy.js +25 -25
  19. package/dist/runtime/index.d.ts +1 -1
  20. package/dist/runtime/index.d.ts.map +1 -1
  21. package/dist/runtime/index.js +1 -2
  22. package/dist/runtime/prop-builder.d.ts +1 -2
  23. package/dist/runtime/prop-builder.d.ts.map +1 -1
  24. package/dist/runtime/prop-builder.js +4 -76
  25. package/dist/types/index.d.ts +2 -0
  26. package/dist/types/index.d.ts.map +1 -1
  27. package/dist/types/index.js +15 -0
  28. package/dist/types/library-config.d.ts +32 -0
  29. package/dist/types/library-config.d.ts.map +1 -0
  30. package/dist/types/library-config.js +2 -0
  31. package/dist/utilities/core-libraries.d.ts +5 -0
  32. package/dist/utilities/core-libraries.d.ts.map +1 -0
  33. package/dist/utilities/core-libraries.js +52 -0
  34. package/dist/utilities/library-loader.d.ts +3 -2
  35. package/dist/utilities/library-loader.d.ts.map +1 -1
  36. package/dist/utilities/library-loader.js +65 -76
  37. package/dist/utilities/standard-libraries.d.ts +13 -24
  38. package/dist/utilities/standard-libraries.d.ts.map +1 -1
  39. package/dist/utilities/standard-libraries.js +58 -47
  40. package/package.json +4 -4
  41. package/samples/entities-1.js +493 -0
  42. package/src/compiler/component-compiler.ts +64 -35
  43. package/src/index.ts +1 -5
  44. package/src/registry/component-resolver.ts +21 -30
  45. package/src/registry/index.ts +2 -1
  46. package/src/runtime/component-hierarchy.ts +26 -26
  47. package/src/runtime/index.ts +0 -1
  48. package/src/runtime/prop-builder.ts +5 -112
  49. package/src/types/index.ts +6 -1
  50. package/src/types/library-config.ts +75 -0
  51. package/src/utilities/core-libraries.ts +61 -0
  52. package/src/utilities/library-loader.ts +113 -93
  53. package/src/utilities/standard-libraries.ts +104 -71
  54. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,493 @@
1
+ function EntityFieldSummaryTable({ data, utilities, styles, components, onStateChanged, filters, sortBy, sortDirection }) {
2
+ const { TableComponent, FiltersPanel } = components;
3
+
4
+ const handleSort = (column, direction) => {
5
+ onStateChanged?.({ sortBy: column, sortDirection: direction });
6
+ };
7
+
8
+ const handleFiltersChange = (updatedFilters) => {
9
+ onStateChanged?.({ filters: updatedFilters });
10
+ };
11
+
12
+ const columns = [
13
+ {
14
+ key: 'EntityName',
15
+ title: 'Entity Name',
16
+ dataIndex: 'EntityName',
17
+ width: 200,
18
+ ellipsis: true,
19
+ sortable: true,
20
+ render: (text) => (
21
+ <div style={{
22
+ maxWidth: '200px',
23
+ whiteSpace: 'nowrap',
24
+ overflow: 'hidden',
25
+ textOverflow: 'ellipsis',
26
+ cursor: 'pointer',
27
+ color: styles.colors.primary
28
+ }} title={text}>
29
+ {text}
30
+ </div>
31
+ )
32
+ },
33
+ {
34
+ key: 'EntityDescription',
35
+ title: 'Description',
36
+ dataIndex: 'EntityDescription',
37
+ width: 300,
38
+ ellipsis: true,
39
+ sortable: false,
40
+ render: (text) => (
41
+ <div style={{
42
+ maxWidth: '300px',
43
+ whiteSpace: 'nowrap',
44
+ overflow: 'hidden',
45
+ textOverflow: 'ellipsis',
46
+ color: styles.colors.textSecondary
47
+ }} title={text}>
48
+ {text || 'No description'}
49
+ </div>
50
+ )
51
+ },
52
+ {
53
+ key: 'TotalFields',
54
+ title: 'Total Fields',
55
+ dataIndex: 'TotalFields',
56
+ width: 100,
57
+ sortable: true,
58
+ align: 'right',
59
+ render: (value) => (
60
+ <span style={{
61
+ fontWeight: styles.typography.fontWeight?.medium || '500',
62
+ color: styles.colors.text
63
+ }}>
64
+ {value || 0}
65
+ </span>
66
+ )
67
+ },
68
+ {
69
+ key: 'TextFields',
70
+ title: 'Text',
71
+ dataIndex: 'TextFields',
72
+ width: 80,
73
+ sortable: true,
74
+ align: 'right',
75
+ render: (value) => (
76
+ <span style={{ color: styles.colors.info }}>
77
+ {value || 0}
78
+ </span>
79
+ )
80
+ },
81
+ {
82
+ key: 'NumberFields',
83
+ title: 'Number',
84
+ dataIndex: 'NumberFields',
85
+ width: 80,
86
+ sortable: true,
87
+ align: 'right',
88
+ render: (value) => (
89
+ <span style={{ color: styles.colors.success }}>
90
+ {value || 0}
91
+ </span>
92
+ )
93
+ },
94
+ {
95
+ key: 'DateFields',
96
+ title: 'Date',
97
+ dataIndex: 'DateFields',
98
+ width: 80,
99
+ sortable: true,
100
+ align: 'right',
101
+ render: (value) => (
102
+ <span style={{ color: styles.colors.warning }}>
103
+ {value || 0}
104
+ </span>
105
+ )
106
+ },
107
+ {
108
+ key: 'BooleanFields',
109
+ title: 'Boolean',
110
+ dataIndex: 'BooleanFields',
111
+ width: 80,
112
+ sortable: true,
113
+ align: 'right',
114
+ render: (value) => (
115
+ <span style={{ color: styles.colors.error }}>
116
+ {value || 0}
117
+ </span>
118
+ )
119
+ },
120
+ {
121
+ key: 'GuidFields',
122
+ title: 'GUID',
123
+ dataIndex: 'GuidFields',
124
+ width: 80,
125
+ sortable: true,
126
+ align: 'right',
127
+ render: (value) => (
128
+ <span style={{ color: styles.colors.textSecondary }}>
129
+ {value || 0}
130
+ </span>
131
+ )
132
+ },
133
+ {
134
+ key: 'BinaryFields',
135
+ title: 'Binary',
136
+ dataIndex: 'BinaryFields',
137
+ width: 80,
138
+ sortable: true,
139
+ align: 'right',
140
+ render: (value) => (
141
+ <span style={{ color: styles.colors.textTertiary }}>
142
+ {value || 0}
143
+ </span>
144
+ )
145
+ },
146
+ {
147
+ key: 'EntityRowCount',
148
+ title: 'Rows',
149
+ dataIndex: 'EntityRowCount',
150
+ width: 100,
151
+ sortable: true,
152
+ align: 'right',
153
+ render: (value) => (
154
+ <span style={{ fontSize: styles.typography.fontSize.sm }}>
155
+ {value?.toLocaleString() || '0'}
156
+ </span>
157
+ )
158
+ },
159
+ {
160
+ key: 'EntityStatus',
161
+ title: 'Status',
162
+ dataIndex: 'EntityStatus',
163
+ width: 100,
164
+ sortable: true,
165
+ render: (value) => (
166
+ <span style={{
167
+ padding: '4px 8px',
168
+ borderRadius: styles.borders.radius,
169
+ fontSize: styles.typography.fontSize.xs,
170
+ backgroundColor: value === 'Active' ? styles.colors.successLight :
171
+ value === 'Deprecated' ? styles.colors.warningLight :
172
+ styles.colors.errorLight,
173
+ color: value === 'Active' ? styles.colors.success :
174
+ value === 'Deprecated' ? styles.colors.warning :
175
+ styles.colors.error
176
+ }}>
177
+ {value}
178
+ </span>
179
+ )
180
+ },
181
+ {
182
+ key: 'LastFieldUpdate',
183
+ title: 'Last Updated',
184
+ dataIndex: 'LastFieldUpdate',
185
+ width: 150,
186
+ sortable: true,
187
+ render: (value) => (
188
+ <div title={value ? new Date(value).toLocaleString() : ''}>
189
+ <span style={{
190
+ fontSize: styles.typography.fontSize.sm,
191
+ color: styles.colors.textSecondary
192
+ }}>
193
+ {value ? new Date(value).toLocaleDateString() : 'Never'}
194
+ </span>
195
+ </div>
196
+ )
197
+ }
198
+ ];
199
+
200
+ const containerStyle = {
201
+ display: 'flex',
202
+ flexDirection: 'column',
203
+ height: '100%',
204
+ backgroundColor: styles.colors.background,
205
+ fontFamily: styles.typography.fontFamily
206
+ };
207
+
208
+ const filterContainerStyle = {
209
+ padding: styles.spacing.md,
210
+ borderBottom: `1px solid ${styles.colors.border}`,
211
+ backgroundColor: styles.colors.surface
212
+ };
213
+
214
+ const tableContainerStyle = {
215
+ flex: 1,
216
+ overflow: 'auto',
217
+ padding: styles.spacing.md
218
+ };
219
+
220
+ return (
221
+ <div style={containerStyle}>
222
+ <div style={filterContainerStyle}>
223
+ <FiltersPanel
224
+ searchTerm={filters?.searchTerm || ''}
225
+ minFields={filters?.minFields || 0}
226
+ maxFields={filters?.maxFields || 9999}
227
+ selectedStatuses={filters?.selectedStatuses || ['Active']}
228
+ typeFilters={filters?.typeFilters || {}}
229
+ onFiltersChange={handleFiltersChange}
230
+ styles={styles}
231
+ utilities={utilities}
232
+ components={components}
233
+ />
234
+ </div>
235
+ <div style={tableContainerStyle}>
236
+ <TableComponent
237
+ columns={columns}
238
+ data={data || []}
239
+ sortColumn={sortBy}
240
+ sortDirection={sortDirection}
241
+ onSort={handleSort}
242
+ styles={styles}
243
+ utilities={utilities}
244
+ components={components}
245
+ />
246
+ </div>
247
+ </div>
248
+ );
249
+ }
250
+
251
+ /*******************************************************
252
+ TableComponent
253
+ undefined
254
+ *******************************************************/
255
+ function TableComponent({ columns = [], data = [], sortColumn, sortDirection, styles = {}, callbacks = {}, ...userState }) {
256
+ const updateState = (newState) => {
257
+ callbacks?.UpdateUserState?.({ ...userState, ...newState });
258
+ };
259
+
260
+ const handleSort = (columnName) => {
261
+ const newDirection = sortColumn === columnName && sortDirection === 'asc' ? 'desc' : 'asc';
262
+ updateState({ sortColumn: columnName, sortDirection: newDirection });
263
+ };
264
+
265
+ const containerStyle = {
266
+ height: '100%',
267
+ overflow: 'auto',
268
+ backgroundColor: styles.colors?.background || '#ffffff',
269
+ border: `1px solid ${styles.colors?.border || '#e0e0e0'}`,
270
+ borderRadius: styles.borders?.radius || '4px',
271
+ fontFamily: styles.typography?.fontFamily || 'Arial, sans-serif'
272
+ };
273
+
274
+ const tableStyle = {
275
+ width: '100%',
276
+ borderCollapse: 'collapse',
277
+ fontSize: styles.typography?.fontSize?.md || '14px'
278
+ };
279
+
280
+ const headerStyle = {
281
+ backgroundColor: styles.colors?.surface || '#f5f5f5',
282
+ borderBottom: `1px solid ${styles.colors?.border || '#e0e0e0'}`,
283
+ padding: styles.spacing?.md || '12px',
284
+ textAlign: 'left',
285
+ fontWeight: styles.typography?.fontWeight?.semibold || '600',
286
+ color: styles.colors?.text || '#333333',
287
+ cursor: 'pointer',
288
+ userSelect: 'none'
289
+ };
290
+
291
+ const cellStyle = {
292
+ padding: styles.spacing?.md || '12px',
293
+ borderBottom: `1px solid ${styles.colors?.borderLight || styles.colors?.border || '#e0e0e0'}`,
294
+ color: styles.colors?.textSecondary || '#666666'
295
+ };
296
+
297
+ const sortIconStyle = {
298
+ marginLeft: styles.spacing?.xs || '4px',
299
+ fontSize: styles.typography?.fontSize?.sm || '12px'
300
+ };
301
+
302
+ return (
303
+ <div style={containerStyle}>
304
+ <table style={tableStyle}>
305
+ <thead>
306
+ <tr>
307
+ {columns.map(column => (
308
+ <th
309
+ key={column.field}
310
+ style={{
311
+ ...headerStyle,
312
+ ...(sortColumn === column.field
313
+ ? { backgroundColor: styles.colors?.primaryLight || styles.colors?.primary || '#e3f2fd' }
314
+ : {})
315
+ }}
316
+ onClick={() => handleSort(column.field)}
317
+ >
318
+ {column.title}
319
+ {sortColumn === column.field && (
320
+ <span style={sortIconStyle}>
321
+ {sortDirection === 'asc' ? '↑' : '↓'}
322
+ </span>
323
+ )}
324
+ </th>
325
+ ))}
326
+ </tr>
327
+ </thead>
328
+ <tbody>
329
+ {data.map((row, index) => (
330
+ <tr key={index}>
331
+ {columns.map(column => (
332
+ <td
333
+ key={`${index}-${column.field}`}
334
+ style={cellStyle}
335
+ >
336
+ {row[column.field] || '-'}
337
+ </td>
338
+ ))}
339
+ </tr>
340
+ ))}
341
+ </tbody>
342
+ </table>
343
+ </div>
344
+ );
345
+ }
346
+
347
+ /*******************************************************
348
+ FiltersPanel
349
+ undefined
350
+ *******************************************************/
351
+ function FiltersPanel({ data, utilities, styles, components, searchTerm, minFields, maxFields, selectedStatuses, typeFilters, onStateChanged }) {
352
+ const handleSearchChange = (e) => {
353
+ onStateChanged?.({ searchTerm: e.target.value });
354
+ };
355
+
356
+ const handleMinFieldsChange = (e) => {
357
+ const value = parseInt(e.target.value) || 0;
358
+ onStateChanged?.({ minFields: value });
359
+ };
360
+
361
+ const handleMaxFieldsChange = (e) => {
362
+ const value = parseInt(e.target.value) || 0;
363
+ onStateChanged?.({ maxFields: value });
364
+ };
365
+
366
+ const handleStatusToggle = (status) => {
367
+ const updatedStatuses = selectedStatuses.includes(status)
368
+ ? selectedStatuses.filter(s => s !== status)
369
+ : [...selectedStatuses, status];
370
+ onStateChanged?.({ selectedStatuses: updatedStatuses });
371
+ };
372
+
373
+ const handleTypeChange = (typeName, value) => {
374
+ const updatedTypeFilters = { ...typeFilters, [typeName]: value };
375
+ onStateChanged?.({ typeFilters: updatedTypeFilters });
376
+ };
377
+
378
+ const containerStyle = {
379
+ padding: styles.spacing.md,
380
+ backgroundColor: styles.colors.surface,
381
+ border: `1px solid ${styles.colors.border}`,
382
+ borderRadius: styles.borders.radius,
383
+ height: '100%',
384
+ overflow: 'auto'
385
+ };
386
+
387
+ const inputStyle = {
388
+ width: '100%',
389
+ padding: styles.spacing.sm,
390
+ border: `1px solid ${styles.colors.border}`,
391
+ borderRadius: styles.borders.radius,
392
+ fontSize: styles.typography.fontSize.sm,
393
+ marginBottom: styles.spacing.sm
394
+ };
395
+
396
+ const labelStyle = {
397
+ display: 'block',
398
+ marginBottom: styles.spacing.xs,
399
+ fontSize: styles.typography.fontSize.sm,
400
+ color: styles.colors.textSecondary,
401
+ fontWeight: styles.typography.fontWeight?.medium || '500'
402
+ };
403
+
404
+ const statusButtonStyle = (isActive) => ({
405
+ padding: `${styles.spacing.xs} ${styles.spacing.sm}`,
406
+ margin: `0 ${styles.spacing.xs} ${styles.spacing.xs} 0`,
407
+ border: `1px solid ${isActive ? styles.colors.primary : styles.colors.border}`,
408
+ borderRadius: styles.borders.radius,
409
+ backgroundColor: isActive ? styles.colors.primary : 'transparent',
410
+ color: isActive ? styles.colors.textInverse || styles.colors.background : styles.colors.text,
411
+ cursor: 'pointer',
412
+ fontSize: styles.typography.fontSize.xs
413
+ });
414
+
415
+ const numberInputStyle = {
416
+ width: '80px',
417
+ padding: styles.spacing.xs,
418
+ border: `1px solid ${styles.colors.border}`,
419
+ borderRadius: styles.borders.radius,
420
+ fontSize: styles.typography.fontSize.sm,
421
+ margin: `0 ${styles.spacing.sm}`
422
+ };
423
+
424
+ return (
425
+ <div style={containerStyle}>
426
+ <div style={{ marginBottom: styles.spacing.lg }}>
427
+ <label style={labelStyle}>Entity Name Search</label>
428
+ <input
429
+ type="text"
430
+ value={searchTerm || ''}
431
+ onChange={handleSearchChange}
432
+ placeholder="Search entities..."
433
+ style={inputStyle}
434
+ />
435
+ </div>
436
+
437
+ <div style={{ marginBottom: styles.spacing.lg }}>
438
+ <label style={labelStyle}>Field Count Range</label>
439
+ <div style={{ display: 'flex', alignItems: 'center', gap: styles.spacing.sm }}>
440
+ <span>Min:</span>
441
+ <input
442
+ type="number"
443
+ value={minFields || 0}
444
+ onChange={handleMinFieldsChange}
445
+ min="0"
446
+ style={numberInputStyle}
447
+ />
448
+ <span>Max:</span>
449
+ <input
450
+ type="number"
451
+ value={maxFields || 0}
452
+ onChange={handleMaxFieldsChange}
453
+ min="0"
454
+ style={numberInputStyle}
455
+ />
456
+ </div>
457
+ </div>
458
+
459
+ <div style={{ marginBottom: styles.spacing.lg }}>
460
+ <label style={labelStyle}>Status Filters</label>
461
+ <div>
462
+ {['Active', 'Deprecated', 'Disabled'].map(status => (
463
+ <button
464
+ key={status}
465
+ onClick={() => handleStatusToggle(status)}
466
+ style={statusButtonStyle(selectedStatuses.includes(status))}
467
+ >
468
+ {status}
469
+ </button>
470
+ ))}
471
+ </div>
472
+ </div>
473
+
474
+ <div>
475
+ <label style={labelStyle}>Type Distribution Threshold</label>
476
+ {Object.entries(typeFilters || {}).map(([typeName, currentValue]) => (
477
+ <div key={typeName} style={{ marginBottom: styles.spacing.sm }}>
478
+ <label style={{ ...labelStyle, fontSize: styles.typography.fontSize.xs }}>
479
+ {typeName}:
480
+ </label>
481
+ <input
482
+ type="number"
483
+ value={currentValue || 0}
484
+ onChange={(e) => handleTypeChange(typeName, parseInt(e.target.value) || 0)}
485
+ min="0"
486
+ style={numberInputStyle}
487
+ />
488
+ </div>
489
+ ))}
490
+ </div>
491
+ </div>
492
+ );
493
+ }
@@ -161,7 +161,11 @@ export class ComponentCompiler {
161
161
  */
162
162
  private wrapComponentCode(componentCode: string, componentName: string): string {
163
163
  return `
164
- function createComponent(React, ReactDOM, useState, useEffect, useCallback, createStateUpdater, libraries, styles, console) {
164
+ function createComponent(
165
+ React, ReactDOM,
166
+ useState, useEffect, useCallback, useMemo, useRef, useContext, useReducer, useLayoutEffect,
167
+ libraries, styles, console
168
+ ) {
165
169
  ${componentCode}
166
170
 
167
171
  // Ensure the component exists
@@ -193,19 +197,17 @@ export class ComponentCompiler {
193
197
  */
194
198
  private createComponentFactory(transpiledCode: string, componentName: string): Function {
195
199
  try {
196
- // Create the factory function
200
+ // Create the factory function with all React hooks
197
201
  const factoryCreator = new Function(
198
- 'React', 'ReactDOM', 'useState', 'useEffect', 'useCallback',
199
- 'createStateUpdater', 'libraries', 'styles', 'console',
202
+ 'React', 'ReactDOM',
203
+ 'useState', 'useEffect', 'useCallback', 'useMemo', 'useRef', 'useContext', 'useReducer', 'useLayoutEffect',
204
+ 'libraries', 'styles', 'console',
200
205
  `${transpiledCode}; return createComponent;`
201
206
  );
202
207
 
203
208
  // Return a function that executes the factory with runtime context
204
209
  return (context: RuntimeContext, styles: any = {}) => {
205
210
  const { React, ReactDOM, libraries = {} } = context;
206
-
207
- // Create state updater utility
208
- const createStateUpdater = this.createStateUpdaterUtility();
209
211
 
210
212
  // Execute the factory creator to get the createComponent function
211
213
  const createComponentFn = factoryCreator(
@@ -214,7 +216,11 @@ export class ComponentCompiler {
214
216
  React.useState,
215
217
  React.useEffect,
216
218
  React.useCallback,
217
- createStateUpdater,
219
+ React.useMemo,
220
+ React.useRef,
221
+ React.useContext,
222
+ React.useReducer,
223
+ React.useLayoutEffect,
218
224
  libraries,
219
225
  styles,
220
226
  console
@@ -227,7 +233,11 @@ export class ComponentCompiler {
227
233
  React.useState,
228
234
  React.useEffect,
229
235
  React.useCallback,
230
- createStateUpdater,
236
+ React.useMemo,
237
+ React.useRef,
238
+ React.useContext,
239
+ React.useReducer,
240
+ React.useLayoutEffect,
231
241
  libraries,
232
242
  styles,
233
243
  console
@@ -238,28 +248,6 @@ export class ComponentCompiler {
238
248
  }
239
249
  }
240
250
 
241
- /**
242
- * Creates the state updater utility function for nested components
243
- * @returns State updater function
244
- */
245
- private createStateUpdaterUtility(): Function {
246
- return (statePath: string, parentStateUpdater: Function) => {
247
- return (componentStateUpdate: any) => {
248
- if (!statePath) {
249
- // Root component - pass through directly
250
- parentStateUpdater(componentStateUpdate);
251
- } else {
252
- // Sub-component - bubble up with path context
253
- const pathParts = statePath.split('.');
254
- const componentKey = pathParts[pathParts.length - 1];
255
-
256
- parentStateUpdater({
257
- [componentKey]: componentStateUpdate
258
- });
259
- }
260
- };
261
- };
262
- }
263
251
 
264
252
  /**
265
253
  * Validates compilation options
@@ -267,21 +255,62 @@ export class ComponentCompiler {
267
255
  * @throws Error if validation fails
268
256
  */
269
257
  private validateCompileOptions(options: CompileOptions): void {
258
+ // Check if options object exists
259
+ if (!options) {
260
+ throw new Error(
261
+ 'Component compilation failed: No options provided.\n' +
262
+ 'Expected an object with componentName and componentCode properties.\n' +
263
+ 'Example: { componentName: "MyComponent", componentCode: "function MyComponent() { ... }" }'
264
+ );
265
+ }
266
+
267
+ // Check component name
270
268
  if (!options.componentName) {
271
- throw new Error('Component name is required');
269
+ const providedKeys = Object.keys(options).join(', ');
270
+ throw new Error(
271
+ 'Component compilation failed: Component name is required.\n' +
272
+ `Received options with keys: [${providedKeys}]\n` +
273
+ 'Please ensure your component spec includes a "name" property.\n' +
274
+ 'Example: { name: "MyComponent", code: "..." }'
275
+ );
272
276
  }
273
277
 
278
+ // Check component code
274
279
  if (!options.componentCode) {
275
- throw new Error('Component code is required');
280
+ throw new Error(
281
+ `Component compilation failed: Component code is required for "${options.componentName}".\n` +
282
+ 'Please ensure your component spec includes a "code" property with the component source code.\n' +
283
+ 'Example: { name: "MyComponent", code: "function MyComponent() { return <div>Hello</div>; }" }'
284
+ );
276
285
  }
277
286
 
287
+ // Check code type
278
288
  if (typeof options.componentCode !== 'string') {
279
- throw new Error('Component code must be a string');
289
+ const actualType = typeof options.componentCode;
290
+ throw new Error(
291
+ `Component compilation failed: Component code must be a string for "${options.componentName}".\n` +
292
+ `Received type: ${actualType}\n` +
293
+ `Received value: ${JSON.stringify(options.componentCode).substring(0, 100)}...\n` +
294
+ 'Please ensure the code property contains a string of JavaScript/JSX code.'
295
+ );
296
+ }
297
+
298
+ // Check if code is empty or whitespace only
299
+ if (options.componentCode.trim().length === 0) {
300
+ throw new Error(
301
+ `Component compilation failed: Component code is empty for "${options.componentName}".\n` +
302
+ 'The code property must contain valid JavaScript/JSX code defining a React component.'
303
+ );
280
304
  }
281
305
 
282
306
  // Basic syntax check
283
307
  if (!options.componentCode.includes(options.componentName)) {
284
- throw new Error(`Component code must define a component named "${options.componentName}"`);
308
+ throw new Error(
309
+ `Component compilation failed: Component code must define a component named "${options.componentName}".\n` +
310
+ 'The function/component name in the code must match the componentName property.\n' +
311
+ `Expected to find: function ${options.componentName}(...) or const ${options.componentName} = ...\n` +
312
+ 'Code preview: ' + options.componentCode.substring(0, 200) + '...'
313
+ );
285
314
  }
286
315
  }
287
316
 
package/src/index.ts CHANGED
@@ -53,7 +53,6 @@ export {
53
53
 
54
54
  export {
55
55
  buildComponentProps,
56
- cleanupPropBuilder,
57
56
  normalizeCallbacks,
58
57
  normalizeStyles,
59
58
  validateComponentProps,
@@ -87,11 +86,8 @@ export {
87
86
  } from './utilities/component-styles';
88
87
 
89
88
  export {
90
- STANDARD_LIBRARY_URLS,
91
89
  StandardLibraries,
92
- getCoreLibraryUrls,
93
- getUILibraryUrls,
94
- getCSSUrls,
90
+ StandardLibraryManager,
95
91
  createStandardLibraries
96
92
  } from './utilities/standard-libraries';
97
93