@jxrstudios/jxr 1.0.3 → 1.0.5

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 (41) hide show
  1. package/README.md +6 -2
  2. package/bin/jxr.js +60 -0
  3. package/dist/deployer.d.ts +8 -12
  4. package/dist/deployer.d.ts.map +1 -1
  5. package/dist/deployer.js +69 -106
  6. package/dist/deployer.js.map +1 -1
  7. package/dist/enhanced-transpiler.d.ts +36 -0
  8. package/dist/enhanced-transpiler.d.ts.map +1 -0
  9. package/dist/enhanced-transpiler.js +272 -0
  10. package/dist/enhanced-transpiler.js.map +1 -0
  11. package/dist/entry-point-detection.d.ts +22 -0
  12. package/dist/entry-point-detection.d.ts.map +1 -0
  13. package/dist/entry-point-detection.js +415 -0
  14. package/dist/entry-point-detection.js.map +1 -0
  15. package/dist/index.d.ts +19 -12
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +2119 -16
  18. package/dist/index.js.map +1 -1
  19. package/dist/jxr-server-manager.d.ts +32 -0
  20. package/dist/jxr-server-manager.d.ts.map +1 -0
  21. package/dist/jxr-server-manager.js +353 -0
  22. package/dist/jxr-server-manager.js.map +1 -0
  23. package/dist/runtime.d.ts +9 -9
  24. package/dist/runtime.d.ts.map +1 -1
  25. package/dist/runtime.js +3 -3
  26. package/package.json +15 -4
  27. package/src/deployer.ts +231 -0
  28. package/src/enhanced-transpiler.ts +331 -0
  29. package/src/entry-point-detection.ts +470 -0
  30. package/src/index.ts +63 -0
  31. package/src/jxr-server-manager.ts +410 -0
  32. package/src/module-resolver.ts +520 -0
  33. package/src/moq-transport.ts +267 -0
  34. package/src/runtime.ts +188 -0
  35. package/src/web-crypto.ts +279 -0
  36. package/src/worker-pool.ts +321 -0
  37. package/zzz_react_template/App.tsx +160 -0
  38. package/zzz_react_template/index.css +16 -0
  39. package/zzz_react_template/index.html +12 -0
  40. package/zzz_react_template/main.tsx +10 -0
  41. package/zzz_react_template/package.json +25 -0
@@ -0,0 +1,470 @@
1
+ /**
2
+ * Entry point detection utility for the preview system
3
+ * Automatically finds or creates appropriate entry points for preview rendering
4
+ */
5
+
6
+ export interface ProjectFile {
7
+ id: string
8
+ path: string
9
+ content: string
10
+ language: string
11
+ createdAt: number
12
+ updatedAt: number
13
+ }
14
+
15
+ export interface EntryPointDetection {
16
+ entryPoint: string
17
+ files: ProjectFile[]
18
+ createdEntry: boolean
19
+ }
20
+
21
+ function generateId(): string {
22
+ return Date.now().toString(36) + Math.random().toString(36).substr(2)
23
+ }
24
+
25
+ // Ordered: prefer component files over bootstrap files.
26
+ // The preview engine already handles React mounting — it needs a *component* entry,
27
+ // not a bootstrap file that calls createRoot() itself.
28
+ const COMMON_ENTRY_POINTS = [
29
+ 'src/App.tsx',
30
+ 'src/App.ts',
31
+ 'App.tsx',
32
+ 'App.ts',
33
+ 'app/page.tsx', // Next.js
34
+ 'app/page.ts',
35
+ 'src/main.tsx',
36
+ 'src/main.ts',
37
+ 'main.tsx',
38
+ 'main.ts',
39
+ 'index.tsx',
40
+ 'index.ts',
41
+ 'src/index.tsx',
42
+ 'src/index.ts'
43
+ ]
44
+
45
+ const COMPONENT_INDICATORS = [
46
+ 'App',
47
+ 'Main',
48
+ 'Page',
49
+ 'Index',
50
+ 'Home'
51
+ ]
52
+
53
+ /**
54
+ * Finds or creates an entry point for preview rendering
55
+ */
56
+ export function findOrCreateEntryPoint(files: ProjectFile[]): EntryPointDetection {
57
+ console.log('🔍 Entry point detection: Starting with', files.length, 'files')
58
+ console.log('📁 Available files:', files.map(f => f.path))
59
+
60
+ // Try to find existing entry point
61
+ const existingEntry = findExistingEntryPoint(files)
62
+ if (existingEntry) {
63
+ console.log('🎯 Found existing entry point:', existingEntry)
64
+ return {
65
+ entryPoint: existingEntry,
66
+ files,
67
+ createdEntry: false
68
+ }
69
+ }
70
+
71
+ // Try to find a component that could serve as entry point
72
+ const componentEntry = findComponentEntryPoint(files)
73
+ if (componentEntry) {
74
+ console.log('🔧 Found component that could be entry point:', componentEntry)
75
+
76
+ // Create an app wrapper for the component
77
+ const componentFile = files.find(f => f.path === componentEntry)
78
+ if (componentFile) {
79
+ console.log('📝 Creating app wrapper for component:', componentEntry)
80
+ const appWrapperFile = createAppWrapperForComponent(componentFile)
81
+ const updatedFiles = [...files, appWrapperFile]
82
+
83
+ return {
84
+ entryPoint: appWrapperFile.path,
85
+ files: updatedFiles,
86
+ createdEntry: true
87
+ }
88
+ }
89
+ }
90
+
91
+ // Create a default entry point
92
+ console.log('📝 Creating default entry point')
93
+ const entryPointFile = createDefaultEntryPoint(files)
94
+ const updatedFiles = [...files, entryPointFile]
95
+
96
+ return {
97
+ entryPoint: entryPointFile.path,
98
+ files: updatedFiles,
99
+ createdEntry: true
100
+ }
101
+ }
102
+
103
+ /**
104
+ * Detects whether a file is a bootstrap/mount file (calls createRoot, render, hydrateRoot)
105
+ * rather than a renderable component. The preview engine handles mounting itself,
106
+ * so these files should be skipped as entry points.
107
+ */
108
+ function isBootstrapFile(file: ProjectFile): boolean {
109
+ const c = file.content
110
+ return (
111
+ /\bcreateRoot\s*\(/.test(c) ||
112
+ /ReactDOM\.render\s*\(/.test(c) ||
113
+ /\bhydrateRoot\s*\(/.test(c)
114
+ )
115
+ }
116
+
117
+ /**
118
+ * Given a bootstrap file like `src/main.tsx`, extract the component it imports.
119
+ * e.g. `import App from './App'` → resolve to `src/App.tsx`.
120
+ * Returns null if no component import is found.
121
+ */
122
+ function extractComponentFromBootstrap(bootstrapFile: ProjectFile, allFiles: ProjectFile[]): string | null {
123
+ // Match default imports: import Foo from './path'
124
+ const importRe = /import\s+(\w+)\s+from\s+['"]([^'"]+)['"]/g
125
+ let match: RegExpExecArray | null
126
+ while ((match = importRe.exec(bootstrapFile.content)) !== null) {
127
+ const importPath = match[2]
128
+ // Skip external packages
129
+ if (!importPath.startsWith('.') && !importPath.startsWith('@/')) continue
130
+ // Resolve relative to the bootstrap file's directory
131
+ const bootstrapDir = bootstrapFile.path.replace(/\/[^/]+$/, '')
132
+ let resolvedBase: string
133
+ if (importPath.startsWith('@/')) {
134
+ resolvedBase = 'src/' + importPath.slice(2)
135
+ } else {
136
+ // Resolve relative import
137
+ const parts = bootstrapDir.split('/').filter(Boolean)
138
+ for (const seg of importPath.split('/')) {
139
+ if (seg === '..') parts.pop()
140
+ else if (seg !== '.') parts.push(seg)
141
+ }
142
+ resolvedBase = parts.join('/')
143
+ }
144
+ // Try to find the file with extensions
145
+ const extensions = ['.tsx', '.ts', '.jsx', '.js', '']
146
+ for (const ext of extensions) {
147
+ const candidate = resolvedBase + ext
148
+ const found = allFiles.find(f =>
149
+ f.path === candidate ||
150
+ f.path === '/' + candidate ||
151
+ f.path.replace(/^\//, '') === candidate
152
+ )
153
+ if (found) {
154
+ console.log(`🔗 Bootstrap ${bootstrapFile.path} imports component from ${found.path}`)
155
+ return found.path
156
+ }
157
+ }
158
+ }
159
+ return null
160
+ }
161
+
162
+ /**
163
+ * Searches for existing entry points in the file list.
164
+ * Skips bootstrap files and resolves to the component they import instead.
165
+ */
166
+ function findExistingEntryPoint(files: ProjectFile[]): string | null {
167
+ for (const entryPath of COMMON_ENTRY_POINTS) {
168
+ const found = files.find(f =>
169
+ f.path === entryPath ||
170
+ f.path.endsWith(entryPath) ||
171
+ f.path.replace(/^\//, '') === entryPath.replace(/^\//, '')
172
+ )
173
+ if (found) {
174
+ // If this is a bootstrap file (calls createRoot/render), skip it
175
+ // and try to resolve the component it imports instead
176
+ if (isBootstrapFile(found)) {
177
+ console.log(`⚡ Skipping bootstrap file as entry point: ${found.path}`)
178
+ const componentPath = extractComponentFromBootstrap(found, files)
179
+ if (componentPath) {
180
+ return componentPath
181
+ }
182
+ // If we can't extract a component, continue searching
183
+ continue
184
+ }
185
+ return found.path
186
+ }
187
+ }
188
+ return null
189
+ }
190
+
191
+ /**
192
+ * Finds a component that could serve as entry point
193
+ */
194
+ function findComponentEntryPoint(files: ProjectFile[]): string | null {
195
+ // Look for files with component indicators
196
+ for (const file of files) {
197
+ const fileName = file.path.split('/').pop()?.replace(/\.(tsx?|jsx?)$/, '')
198
+ if (fileName && COMPONENT_INDICATORS.includes(fileName)) {
199
+ return file.path
200
+ }
201
+ }
202
+
203
+ // Look for the first React component (contains JSX/TSX)
204
+ const jsxFiles = files.filter(f =>
205
+ f.path.endsWith('.tsx') || f.path.endsWith('.jsx')
206
+ )
207
+
208
+ if (jsxFiles.length > 0) {
209
+ // Prefer files with "export default" or component patterns
210
+ for (const file of jsxFiles) {
211
+ if (file.content.includes('export default') ||
212
+ file.content.includes('function') ||
213
+ file.content.includes('const')) {
214
+ return file.path
215
+ }
216
+ }
217
+ // Fallback to first JSX file
218
+ return jsxFiles[0].path
219
+ }
220
+
221
+ // Look for TypeScript files
222
+ const tsFiles = files.filter(f => f.path.endsWith('.ts'))
223
+ if (tsFiles.length > 0) {
224
+ return tsFiles[0].path
225
+ }
226
+
227
+ return null
228
+ }
229
+
230
+ /**
231
+ * Converts a string to a valid PascalCase JavaScript identifier
232
+ * e.g., "html-landing" → "HtmlLanding", "my-component" → "MyComponent"
233
+ */
234
+ function toValidIdentifier(name: string): string {
235
+ // Replace hyphens and underscores with spaces, then convert to PascalCase
236
+ return name
237
+ .replace(/[-_]/g, ' ')
238
+ .replace(/\s+/g, ' ')
239
+ .split(' ')
240
+ .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
241
+ .join('')
242
+ // Remove any remaining invalid characters
243
+ .replace(/[^a-zA-Z0-9_$]/g, '')
244
+ // Ensure it starts with a letter or underscore (prepend underscore if it starts with a number)
245
+ .replace(/^[0-9]/, '_$&')
246
+ || 'Component' // fallback if empty
247
+ }
248
+
249
+ /**
250
+ * Creates an app wrapper for a component file
251
+ */
252
+ function createAppWrapperForComponent(componentFile: ProjectFile): ProjectFile {
253
+ const rawComponentName = componentFile.path.split('/').pop()?.replace(/\.(tsx?|jsx?)$/, '') || 'Component'
254
+ const componentName = toValidIdentifier(rawComponentName)
255
+ const componentPath = componentFile.path.replace(/\.(tsx?|jsx?)$/, '')
256
+
257
+ const content = `import React from 'react'
258
+ import ${componentName} from '${componentPath}'
259
+
260
+ // App wrapper created by preview system for component: ${componentName}
261
+ export default function App() {
262
+ return (
263
+ <div style={{
264
+ padding: '20px',
265
+ fontFamily: 'system-ui, sans-serif',
266
+ minHeight: '100vh',
267
+ background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)'
268
+ }}>
269
+ <div style={{
270
+ maxWidth: '800px',
271
+ margin: '0 auto',
272
+ background: 'white',
273
+ borderRadius: '12px',
274
+ padding: '30px',
275
+ boxShadow: '0 10px 30px rgba(0,0,0,0.1)'
276
+ }}>
277
+ <h1 style={{
278
+ color: '#333',
279
+ marginBottom: '10px',
280
+ fontSize: '28px',
281
+ fontWeight: '700'
282
+ }}>
283
+ DamascusAI Preview
284
+ </h1>
285
+ <p style={{
286
+ color: '#666',
287
+ marginBottom: '30px',
288
+ fontSize: '16px'
289
+ }}>
290
+ Sovereign generation system active - Component: ${componentName}
291
+ </p>
292
+
293
+ <div style={{
294
+ border: '2px dashed #e2e8f0',
295
+ borderRadius: '8px',
296
+ padding: '20px',
297
+ background: '#f8fafc'
298
+ }}>
299
+ <h2 style={{
300
+ color: '#4a5568',
301
+ marginBottom: '15px',
302
+ fontSize: '18px'
303
+ }}>
304
+ Component: <code>${componentName}</code>
305
+ </h2>
306
+ <${componentName} />
307
+ </div>
308
+
309
+ <div style={{
310
+ marginTop: '20px',
311
+ padding: '15px',
312
+ background: '#edf2f7',
313
+ borderRadius: '6px',
314
+ fontSize: '14px',
315
+ color: '#4a5568'
316
+ }}>
317
+ <strong>Generated by:</strong> DamascusAI Sovereign Stack
318
+ </div>
319
+ </div>
320
+ </div>
321
+ )
322
+ }
323
+ `
324
+
325
+ const now = Date.now()
326
+ return {
327
+ id: generateId(),
328
+ path: '/App.tsx',
329
+ content,
330
+ language: 'typescript',
331
+ createdAt: now,
332
+ updatedAt: now
333
+ }
334
+ }
335
+
336
+ /**
337
+ * Creates a default entry point that imports and renders available components
338
+ */
339
+ function createDefaultEntryPoint(files: ProjectFile[]): ProjectFile {
340
+ console.log('📝 Creating default App.tsx entry point')
341
+
342
+ // Find components to import
343
+ const components = findAvailableComponents(files)
344
+
345
+ // Sanitize component names to be valid JavaScript identifiers
346
+ const sanitizedComponents = components.map(comp => ({
347
+ ...comp,
348
+ name: toValidIdentifier(comp.name)
349
+ }))
350
+
351
+ const imports = sanitizedComponents.map(comp =>
352
+ `import ${comp.name} from '${comp.path.replace(/\.(tsx?|jsx?)$/, '')}'`
353
+ ).join('\n')
354
+
355
+ const componentUsage = sanitizedComponents.map(comp =>
356
+ ` <${comp.name} />`
357
+ ).join('\n')
358
+
359
+ const content = `${imports}
360
+ import React from 'react'
361
+
362
+ // Default entry point created by preview system
363
+ export default function App() {
364
+ return (
365
+ <div style={{
366
+ padding: '20px',
367
+ fontFamily: 'system-ui, sans-serif',
368
+ minHeight: '100vh',
369
+ background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)'
370
+ }}>
371
+ <div style={{
372
+ maxWidth: '800px',
373
+ margin: '0 auto',
374
+ background: 'white',
375
+ borderRadius: '12px',
376
+ padding: '30px',
377
+ boxShadow: '0 10px 30px rgba(0,0,0,0.1)'
378
+ }}>
379
+ <h1 style={{
380
+ color: '#333',
381
+ marginBottom: '10px',
382
+ fontSize: '28px',
383
+ fontWeight: '700'
384
+ }}>
385
+ 🚀 Damascus AI Preview
386
+ </h1>
387
+ <p style={{
388
+ color: '#666',
389
+ marginBottom: '30px',
390
+ fontSize: '16px'
391
+ }}>
392
+ Sovereign generation system active
393
+ </p>
394
+
395
+ <div style={{
396
+ border: '2px dashed #e2e8f0',
397
+ borderRadius: '8px',
398
+ padding: '20px',
399
+ background: '#f8fafc'
400
+ }}>
401
+ <h2 style={{
402
+ color: '#4a5568',
403
+ marginBottom: '15px',
404
+ fontSize: '18px'
405
+ }}>
406
+ Generated Components:
407
+ </h2>
408
+ <div style={{ marginTop: '20px' }}>
409
+ ${componentUsage}
410
+ </div>
411
+ </div>
412
+
413
+ <div style={{
414
+ marginTop: '20px',
415
+ padding: '15px',
416
+ background: '#edf2f7',
417
+ borderRadius: '6px',
418
+ fontSize: '14px',
419
+ color: '#4a5568'
420
+ }}>
421
+ <strong>Generated by:</strong> DamascusAI Sovereign Stack
422
+ </div>
423
+ </div>
424
+ </div>
425
+ )
426
+ }
427
+ `
428
+
429
+ const now = Date.now()
430
+ return {
431
+ id: generateId(),
432
+ path: '/App.tsx',
433
+ content,
434
+ language: 'typescript',
435
+ createdAt: now,
436
+ updatedAt: now
437
+ }
438
+ }
439
+
440
+ /**
441
+ * Finds available components in the file list
442
+ */
443
+ function findAvailableComponents(files: ProjectFile[]): Array<{name: string, path: string}> {
444
+ const components: Array<{name: string, path: string}> = []
445
+
446
+ for (const file of files) {
447
+ if (file.path.endsWith('.tsx') || file.path.endsWith('.jsx')) {
448
+ const fileName = file.path.split('/').pop()?.replace(/\.(tsx?|jsx?)$/, '')
449
+ if (fileName && fileName !== 'App' && fileName !== 'index') {
450
+ // Check if it's likely a component (has JSX or export)
451
+ if (file.content.includes('export') || file.content.includes('return')) {
452
+ components.push({
453
+ name: fileName,
454
+ path: file.path
455
+ })
456
+ }
457
+ }
458
+ }
459
+ }
460
+
461
+ // If no components found, create a simple placeholder
462
+ if (components.length === 0) {
463
+ components.push({
464
+ name: 'GeneratedContent',
465
+ path: '/placeholder'
466
+ })
467
+ }
468
+
469
+ return components.slice(0, 3) // Limit to first 3 components
470
+ }
package/src/index.ts ADDED
@@ -0,0 +1,63 @@
1
+ /**
2
+ * JXR.js — Edge OS Runtime Framework
3
+ * A runtime for executing JavaScript at the edge with:
4
+ * - Virtual File System
5
+ * - JSX Transformation (zero-build)
6
+ * - Worker Pool orchestration
7
+ * - MoQ Transport streaming
8
+ * - Web Crypto module integrity
9
+ * - Server with HMR
10
+ */
11
+
12
+ // Core runtime exports
13
+ export { WorkerPool } from './worker-pool.ts';
14
+ export type {
15
+ WorkerMetrics,
16
+ PoolMetrics,
17
+ WorkerStatus,
18
+ TaskPriority,
19
+ } from './worker-pool.ts';
20
+
21
+ export { MoQTransport } from './moq-transport.ts';
22
+ export type {
23
+ MoQStreamMetrics,
24
+ MoQConnectionState,
25
+ MoQObject,
26
+ MoQTrackNamespace,
27
+ } from './moq-transport.ts';
28
+
29
+ export { JXRCrypto, jxrCrypto } from './web-crypto.ts';
30
+ export type { ModuleHash, SignedManifest } from './web-crypto.ts';
31
+
32
+ export {
33
+ VirtualFS,
34
+ JSXTransformer,
35
+ ImportMapBuilder,
36
+ ModuleCache,
37
+ DEFAULT_PROJECT_FILES,
38
+ } from './module-resolver.ts';
39
+ export type {
40
+ VirtualFile,
41
+ VirtualDirectory,
42
+ ResolvedModule,
43
+ ImportMap,
44
+ } from './module-resolver.ts';
45
+
46
+ export { JXRRuntime, jxrRuntime } from './runtime.ts';
47
+ export type { JXRRuntimeConfig, JXRRuntimeMetrics } from './runtime.ts';
48
+
49
+ // Transpiler
50
+ export { EnhancedTranspiler } from './enhanced-transpiler.ts';
51
+ export type { TranspilerOptions, TranspilationResult } from './enhanced-transpiler.ts';
52
+
53
+ // Entry point detection
54
+ export { findOrCreateEntryPoint } from './entry-point-detection.ts';
55
+ export type { EntryPointDetection, ProjectFile } from './entry-point-detection.ts';
56
+
57
+ // Server manager
58
+ export { JXRServerManager } from './jxr-server-manager.ts';
59
+ export type { JXRServerConfig } from './jxr-server-manager.ts';
60
+
61
+ // Deployer
62
+ export { JXRDeployer, jxrDeployer } from './deployer.ts';
63
+ export type { DeployConfig, DeployResult, DeploymentStatus } from './deployer.ts';