editor-sdk 0.1.5 → 0.1.8
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.
- package/README.md +72 -5
- package/dist/SecureScene.js +112 -63
- package/dist/cli.js +85 -9
- package/package.json +38 -36
package/README.md
CHANGED
|
@@ -20,14 +20,39 @@ Authenticate with your registry API key.
|
|
|
20
20
|
npx editor-sdk login
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
You can specify a custom registry URL:
|
|
24
|
+
```bash
|
|
25
|
+
npx editor-sdk login --host https://your-registry.com
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### 2. Configure Registry URL (Optional)
|
|
29
|
+
Set a default registry URL to avoid specifying `--host` every time.
|
|
30
|
+
```bash
|
|
31
|
+
# View current configuration
|
|
32
|
+
npx editor-sdk config
|
|
33
|
+
|
|
34
|
+
# Set default registry URL
|
|
35
|
+
npx editor-sdk config --set-registry https://your-registry.com
|
|
36
|
+
|
|
37
|
+
# For production
|
|
38
|
+
npx editor-sdk config --set-registry https://api.yourdomain.com
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Configuration is saved to `~/.3d-editor/config.json`.
|
|
42
|
+
|
|
43
|
+
### 3. Add Component
|
|
24
44
|
Fetch a component by its ID.
|
|
25
45
|
```bash
|
|
26
|
-
npx editor-sdk add <component-id>
|
|
46
|
+
npx editor-sdk add <component-id>
|
|
27
47
|
```
|
|
28
|
-
|
|
48
|
+
|
|
49
|
+
*Examples:*
|
|
29
50
|
```bash
|
|
30
|
-
|
|
51
|
+
# Uses configured registry URL
|
|
52
|
+
npx editor-sdk add my-scene-123
|
|
53
|
+
|
|
54
|
+
# Override with --host flag
|
|
55
|
+
npx editor-sdk add my-scene-123 --host http://localhost:8080
|
|
31
56
|
```
|
|
32
57
|
|
|
33
58
|
---
|
|
@@ -53,10 +78,52 @@ export default function My3DComponent() {
|
|
|
53
78
|
| Prop | Type | Description |
|
|
54
79
|
|------|------|-------------|
|
|
55
80
|
| `id` | `string` | The unique ID of the component in the registry. |
|
|
56
|
-
| `registryUrl` | `string` | Base URL of the registry
|
|
81
|
+
| `registryUrl` | `string` | (Optional) Base URL of the registry. Auto-detected if not provided. |
|
|
57
82
|
| `token` | `string` | (Optional) Auth token for private components. |
|
|
58
83
|
| `onLoad` | `() => void` | Callback when the model finishes loading. |
|
|
59
84
|
| `onError` | `(err) => void` | Callback if loading fails. |
|
|
85
|
+
| `animation` | `object` | (Optional) Override animation settings. |
|
|
86
|
+
| `position` | `[x, y, z]` | (Optional) Override position. |
|
|
87
|
+
| `scale` | `[x, y, z]` | (Optional) Override scale. |
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## 🌐 Registry URL Configuration
|
|
92
|
+
|
|
93
|
+
The SDK automatically detects the registry URL using the following priority:
|
|
94
|
+
|
|
95
|
+
1. **Explicit prop**: `<SecureScene registryUrl="..." />`
|
|
96
|
+
2. **Environment variable**: `NEXT_PUBLIC_REGISTRY_URL`
|
|
97
|
+
3. **Auto-detect localhost**: `http://localhost:{current-port}` (works on any port!)
|
|
98
|
+
4. **Auto-detect domain**: Uses `window.location.origin` for deployed apps
|
|
99
|
+
5. **Fallback**: `http://localhost:3000`
|
|
100
|
+
|
|
101
|
+
### Development (Any Port)
|
|
102
|
+
The SDK automatically works on **any localhost port**:
|
|
103
|
+
```tsx
|
|
104
|
+
// Works on localhost:3000, localhost:8080, localhost:3001, etc.
|
|
105
|
+
<SecureScene id="my-component-123" />
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Production (Environment Variable)
|
|
109
|
+
Set the registry URL in your environment:
|
|
110
|
+
```bash
|
|
111
|
+
# .env.production
|
|
112
|
+
NEXT_PUBLIC_REGISTRY_URL=https://api.yourdomain.com
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
```tsx
|
|
116
|
+
// Component automatically uses NEXT_PUBLIC_REGISTRY_URL
|
|
117
|
+
<SecureScene id="my-component-123" />
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Override for Specific Component
|
|
121
|
+
```tsx
|
|
122
|
+
<SecureScene
|
|
123
|
+
id="my-component-123"
|
|
124
|
+
registryUrl="https://custom-registry.com"
|
|
125
|
+
/>
|
|
126
|
+
```
|
|
60
127
|
|
|
61
128
|
---
|
|
62
129
|
|
package/dist/SecureScene.js
CHANGED
|
@@ -1,102 +1,151 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect, useState, Suspense } from 'react';
|
|
2
|
+
import { useEffect, useState, Suspense, useRef } from 'react';
|
|
3
3
|
import { useGLTF, Html } from '@react-three/drei';
|
|
4
|
-
|
|
4
|
+
import { useFrame } from '@react-three/fiber';
|
|
5
5
|
// Default Registry URL (can be overridden)
|
|
6
|
-
|
|
7
|
-
const
|
|
6
|
+
// Supports environment variable or falls back to localhost detection
|
|
7
|
+
const getDefaultRegistryUrl = () => {
|
|
8
|
+
// 1. Check for environment variable (for production)
|
|
9
|
+
if (typeof process !== 'undefined' && process.env?.NEXT_PUBLIC_REGISTRY_URL) {
|
|
10
|
+
return process.env.NEXT_PUBLIC_REGISTRY_URL;
|
|
11
|
+
}
|
|
12
|
+
// 2. Auto-detect localhost with current port (for development)
|
|
13
|
+
if (typeof window !== 'undefined' && window.location.hostname === 'localhost') {
|
|
14
|
+
const port = window.location.port || '3000';
|
|
15
|
+
return `http://localhost:${port}`;
|
|
16
|
+
}
|
|
17
|
+
// 3. Use current origin if on a whitelisted domain
|
|
18
|
+
if (typeof window !== 'undefined') {
|
|
19
|
+
return window.location.origin;
|
|
20
|
+
}
|
|
21
|
+
// 4. Fallback
|
|
22
|
+
return 'http://localhost:3000';
|
|
23
|
+
};
|
|
24
|
+
const DEFAULT_REGISTRY_URL = getDefaultRegistryUrl();
|
|
25
|
+
const CACHE_DURATION_MS = 50 * 60 * 1000; // 50 minutes
|
|
26
|
+
// --- Internal Animator (Copied from Editor) ---
|
|
27
|
+
function SecureAnimator({ children, animation, ...props }) {
|
|
28
|
+
const ref = useRef(null);
|
|
29
|
+
useFrame((state, delta) => {
|
|
30
|
+
if (!ref.current || !animation || animation.type === 'none')
|
|
31
|
+
return;
|
|
32
|
+
const speed = animation.speed || 1;
|
|
33
|
+
const intensity = animation.intensity || 1;
|
|
34
|
+
const axis = animation.axis || 'y';
|
|
35
|
+
// Time based animations
|
|
36
|
+
const t = state.clock.getElapsedTime();
|
|
37
|
+
switch (animation.type) {
|
|
38
|
+
case 'rotate':
|
|
39
|
+
ref.current.rotation[axis] += delta * speed;
|
|
40
|
+
break;
|
|
41
|
+
case 'float':
|
|
42
|
+
const startY = 0; // Relative to parent
|
|
43
|
+
ref.current.position.y = startY + Math.sin(t * speed) * (0.5 * intensity);
|
|
44
|
+
break;
|
|
45
|
+
case 'pulse':
|
|
46
|
+
const scaleBase = 1;
|
|
47
|
+
const scale = scaleBase + Math.sin(t * speed * 2) * (0.1 * intensity);
|
|
48
|
+
ref.current.scale.set(scale, scale, scale);
|
|
49
|
+
break;
|
|
50
|
+
case 'bounce':
|
|
51
|
+
ref.current.position.y = Math.abs(Math.sin(t * speed * 2)) * intensity;
|
|
52
|
+
break;
|
|
53
|
+
case 'shake':
|
|
54
|
+
ref.current.position.x = Math.sin(t * speed * 10) * (0.05 * intensity);
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
return _jsx("group", { ref: ref, ...props, children: children });
|
|
59
|
+
}
|
|
60
|
+
// Loader wrapper
|
|
8
61
|
function SceneLoader({ url, ...props }) {
|
|
9
|
-
// useGLTF will suspend automatically
|
|
10
62
|
const { scene } = useGLTF(url);
|
|
11
|
-
|
|
63
|
+
// Clone scene to avoid shared state issues if reused
|
|
64
|
+
const cloned = scene.clone();
|
|
65
|
+
return _jsx("primitive", { object: cloned, ...props });
|
|
12
66
|
}
|
|
13
67
|
export function SecureScene({ id, registryUrl = DEFAULT_REGISTRY_URL, token, onLoad, onError, ...props }) {
|
|
14
|
-
const [
|
|
68
|
+
const [metadata, setMetadata] = useState(null);
|
|
15
69
|
const [error, setError] = useState(null);
|
|
16
70
|
const [isDev, setIsDev] = useState(false);
|
|
17
71
|
useEffect(() => {
|
|
18
72
|
let mounted = true;
|
|
19
|
-
// Check Dev Environment
|
|
20
73
|
if (typeof window !== 'undefined' && window.location.hostname === 'localhost') {
|
|
21
74
|
setIsDev(true);
|
|
22
75
|
}
|
|
23
|
-
async function fetchMetadata() {
|
|
76
|
+
async function fetchMetadata(force = false) {
|
|
24
77
|
try {
|
|
25
|
-
// 0. Check Cache (LocalStorage)
|
|
26
78
|
const cacheKey = `3d-editor:cache:${id}`;
|
|
27
|
-
|
|
28
|
-
if (
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
79
|
+
// 0. Check Cache (if not forced)
|
|
80
|
+
if (!force) {
|
|
81
|
+
const cached = localStorage.getItem(cacheKey);
|
|
82
|
+
if (cached) {
|
|
83
|
+
const data = JSON.parse(cached);
|
|
84
|
+
const age = Date.now() - (data.timestamp || 0);
|
|
85
|
+
if (age < CACHE_DURATION_MS) {
|
|
86
|
+
if (mounted) {
|
|
87
|
+
setMetadata(data);
|
|
88
|
+
onLoad?.();
|
|
89
|
+
}
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
localStorage.removeItem(cacheKey);
|
|
36
94
|
}
|
|
37
|
-
return; // Exit early
|
|
38
|
-
}
|
|
39
|
-
else {
|
|
40
|
-
console.log(`[SecureScene] Cache expired for ${id}`);
|
|
41
|
-
localStorage.removeItem(cacheKey);
|
|
42
95
|
}
|
|
43
96
|
}
|
|
44
|
-
// 1. Call
|
|
45
|
-
const res = await fetch(`${registryUrl}/api/registry/${id}
|
|
46
|
-
|
|
47
|
-
// Future: 'Authorization': `Bearer ${token}`
|
|
48
|
-
'Content-Type': 'application/json',
|
|
49
|
-
// Referer is sent automatically by browser
|
|
50
|
-
}
|
|
51
|
-
});
|
|
52
|
-
if (!res.ok) {
|
|
97
|
+
// 1. Call Registry
|
|
98
|
+
const res = await fetch(`${registryUrl}/api/registry/${id}`);
|
|
99
|
+
if (!res.ok)
|
|
53
100
|
throw new Error(`Registry Error: ${res.statusText}`);
|
|
54
|
-
}
|
|
55
101
|
const data = await res.json();
|
|
56
|
-
|
|
57
|
-
throw new Error("No model URL found for this component");
|
|
58
|
-
}
|
|
59
|
-
// 2. Cache it
|
|
102
|
+
// Cache it
|
|
60
103
|
try {
|
|
61
|
-
|
|
62
|
-
localStorage.setItem(cacheKey, JSON.stringify(cachePayload));
|
|
63
|
-
}
|
|
64
|
-
catch (e) {
|
|
65
|
-
// Ignore quota errors
|
|
104
|
+
localStorage.setItem(cacheKey, JSON.stringify({ ...data, timestamp: Date.now() }));
|
|
66
105
|
}
|
|
106
|
+
catch (e) { }
|
|
67
107
|
if (mounted) {
|
|
68
|
-
|
|
108
|
+
// Only update if changed? React handles basic diff, but for animations it's fine.
|
|
109
|
+
setMetadata(data);
|
|
69
110
|
onLoad?.();
|
|
70
111
|
}
|
|
71
112
|
}
|
|
72
113
|
catch (err) {
|
|
73
114
|
if (mounted) {
|
|
115
|
+
// Only log error if not polling (to avoid spam) or if critical?
|
|
116
|
+
// For now, log.
|
|
74
117
|
console.error("SecureScene Error:", err);
|
|
75
|
-
|
|
76
|
-
|
|
118
|
+
if (!force) {
|
|
119
|
+
setError(err.message);
|
|
120
|
+
onError?.(err);
|
|
121
|
+
}
|
|
77
122
|
}
|
|
78
123
|
}
|
|
79
124
|
}
|
|
80
125
|
fetchMetadata();
|
|
81
|
-
|
|
82
|
-
|
|
126
|
+
// Polling disabled to prevent infinite fetch loops
|
|
127
|
+
// Only fetch once on mount or when id/registryUrl changes
|
|
128
|
+
return () => {
|
|
129
|
+
mounted = false;
|
|
130
|
+
};
|
|
131
|
+
}, [id, registryUrl]); // Removed onLoad and onError from dependencies
|
|
83
132
|
if (error) {
|
|
84
|
-
return (
|
|
133
|
+
return (_jsx("group", { children: _jsx(Html, { position: [0, 1, 0], center: true, children: _jsxs("div", { style: { color: 'red', background: 'white', padding: 4, border: '1px solid red' }, children: ["Error: ", error] }) }) }));
|
|
85
134
|
}
|
|
86
|
-
if (!
|
|
87
|
-
// Loading State
|
|
135
|
+
if (!metadata)
|
|
88
136
|
return null;
|
|
137
|
+
// Render Logic
|
|
138
|
+
// Extract animation from props if it exists, to apply to the top-level container
|
|
139
|
+
const { animation, ...restProps } = props;
|
|
140
|
+
// 1. Multi-Model Support (sceneData)
|
|
141
|
+
if (metadata.sceneData && metadata.sceneData.objects) {
|
|
142
|
+
return (_jsx(SecureAnimator, { animation: animation, ...restProps, children: metadata.sceneData.objects.map((obj, index) => (_jsx("group", { position: obj.position || [0, 0, 0], rotation: obj.rotation || [0, 0, 0], scale: obj.scale || [1, 1, 1], children: _jsx(SecureAnimator, { animation: obj.animation, children: obj.type === 'imported' ? (_jsx(Suspense, { fallback: null, children: _jsx(SceneLoader, { url: obj.modelUrl || obj.modelPath }) })) : (
|
|
143
|
+
// Basic Primitive Support
|
|
144
|
+
_jsxs("mesh", { children: [_jsx("boxGeometry", { args: [1, 1, 1] }), _jsx("meshStandardMaterial", { color: obj.material?.color || "gray" })] })) }) }, `${obj.id}-${index}`))) }));
|
|
145
|
+
}
|
|
146
|
+
// 2. Legacy / Single Model Fallback
|
|
147
|
+
if (metadata.modelUrl && metadata.modelUrl !== 'none') {
|
|
148
|
+
return (_jsx(SecureAnimator, { animation: animation, ...restProps, children: _jsx(Suspense, { fallback: null, children: _jsx(SceneLoader, { url: metadata.modelUrl }) }) }));
|
|
89
149
|
}
|
|
90
|
-
return
|
|
91
|
-
position: 'absolute',
|
|
92
|
-
bottom: 10,
|
|
93
|
-
right: 10,
|
|
94
|
-
background: 'rgba(0,0,0,0.7)',
|
|
95
|
-
color: '#0f0',
|
|
96
|
-
padding: '4px 8px',
|
|
97
|
-
borderRadius: 4,
|
|
98
|
-
fontSize: '10px',
|
|
99
|
-
fontFamily: 'monospace',
|
|
100
|
-
border: '1px solid #0f0'
|
|
101
|
-
}, children: "DEV MODE: SECURE" }) }))] }));
|
|
150
|
+
return null;
|
|
102
151
|
}
|
package/dist/cli.js
CHANGED
|
@@ -36,32 +36,81 @@ program
|
|
|
36
36
|
program
|
|
37
37
|
.command('login')
|
|
38
38
|
.description('Authenticate with the 3D Editor Registry')
|
|
39
|
-
.
|
|
40
|
-
|
|
39
|
+
.option('--host <url>', 'Registry Host URL', 'http://localhost:3000')
|
|
40
|
+
.action(async (options) => {
|
|
41
|
+
console.log(chalk.blue('🔐 Authenticating with 3D Editor... (v0.1.6)'));
|
|
41
42
|
const response = await prompts({
|
|
42
43
|
type: 'password',
|
|
43
44
|
name: 'apiKey',
|
|
44
45
|
message: 'Enter your API Key:',
|
|
45
|
-
validate: value => value.length <
|
|
46
|
+
validate: value => value.length < 1 ? 'API Key cannot be empty' : true
|
|
46
47
|
});
|
|
47
48
|
if (response.apiKey) {
|
|
48
|
-
|
|
49
|
-
console.log(chalk.
|
|
49
|
+
const host = options.host;
|
|
50
|
+
console.log(chalk.gray(`Connecting to ${host}...`));
|
|
51
|
+
try {
|
|
52
|
+
const res = await fetch(`${host}/api/registry/verify`, {
|
|
53
|
+
headers: { 'Authorization': `Bearer ${response.apiKey}` }
|
|
54
|
+
});
|
|
55
|
+
if (!res.ok) {
|
|
56
|
+
console.error(chalk.red('\n❌ Login Failed: Invalid API Key.'));
|
|
57
|
+
console.error(chalk.yellow('Please check your key and try again.'));
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const data = await res.json();
|
|
61
|
+
console.log(chalk.green(`\n✓ Verified as ${data.user.email}`));
|
|
62
|
+
saveConfig({ apiKey: response.apiKey });
|
|
63
|
+
console.log(chalk.green('✓ Logged in successfully!'));
|
|
64
|
+
console.log(chalk.gray(`Credentials saved to ${CONFIG_FILE}`));
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
console.error(chalk.red('\n❌ Connection Failed:'), err.message);
|
|
68
|
+
console.error(chalk.yellow(`Could not reach ${host}`));
|
|
69
|
+
}
|
|
50
70
|
}
|
|
51
71
|
else {
|
|
52
72
|
console.log(chalk.yellow('Login cancelled.'));
|
|
53
73
|
}
|
|
54
74
|
});
|
|
75
|
+
// Config Command
|
|
76
|
+
program
|
|
77
|
+
.command('config')
|
|
78
|
+
.description('Configure registry settings')
|
|
79
|
+
.option('--set-registry <url>', 'Set the default registry URL')
|
|
80
|
+
.option('--show', 'Show current configuration')
|
|
81
|
+
.action(async (options) => {
|
|
82
|
+
const config = loadConfig();
|
|
83
|
+
if (options.setRegistry) {
|
|
84
|
+
config.registryUrl = options.setRegistry;
|
|
85
|
+
saveConfig(config);
|
|
86
|
+
console.log(chalk.green(`✓ Registry URL set to: ${options.setRegistry}`));
|
|
87
|
+
console.log(chalk.gray(`Saved to ${CONFIG_FILE}`));
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
if (options.show || (!options.setRegistry)) {
|
|
91
|
+
console.log(chalk.blue('📋 Current Configuration:'));
|
|
92
|
+
console.log(chalk.gray(`Config file: ${CONFIG_FILE}`));
|
|
93
|
+
console.log('');
|
|
94
|
+
console.log(`Registry URL: ${config.registryUrl || chalk.gray('(auto-detect)')}`);
|
|
95
|
+
console.log(`API Key: ${config.apiKey ? chalk.green('✓ Set') : chalk.yellow('✗ Not set')}`);
|
|
96
|
+
console.log('');
|
|
97
|
+
console.log(chalk.gray('Use --set-registry <url> to change the registry URL'));
|
|
98
|
+
console.log(chalk.gray('Example: npx editor-sdk config --set-registry https://your-domain.com'));
|
|
99
|
+
}
|
|
100
|
+
});
|
|
55
101
|
// Add Command
|
|
56
102
|
program
|
|
57
103
|
.command('add <componentId>')
|
|
58
104
|
.description('Add a component by ID')
|
|
59
105
|
.option('-p, --package', 'Install as a node_module package', false)
|
|
60
|
-
.option('--
|
|
106
|
+
.option('-f, --force', 'Force overwrite of existing files', false)
|
|
107
|
+
.option('--host <url>', 'Registry Host URL (overrides config)')
|
|
61
108
|
.action(async (componentId, options) => {
|
|
62
109
|
try {
|
|
63
110
|
const config = loadConfig();
|
|
64
111
|
const apiKey = config.apiKey;
|
|
112
|
+
// Use --host flag, then config, then default
|
|
113
|
+
const defaultHost = options.host || config.registryUrl || 'http://localhost:3000';
|
|
65
114
|
if (!apiKey) {
|
|
66
115
|
console.warn(chalk.yellow('⚠ You are not logged in. Public components only.'));
|
|
67
116
|
console.warn(chalk.yellow('Run `npx 3d-editor login` to access private components.'));
|
|
@@ -75,18 +124,32 @@ program
|
|
|
75
124
|
idFromArg = parts[parts.length - 1] || 'component';
|
|
76
125
|
}
|
|
77
126
|
else {
|
|
78
|
-
registryUrl = `${
|
|
127
|
+
registryUrl = `${defaultHost}/api/registry/${componentId}`;
|
|
79
128
|
}
|
|
80
129
|
console.log(chalk.blue(`📦 Fetching component from ${registryUrl}...`));
|
|
81
130
|
const res = await fetch(registryUrl, {
|
|
82
131
|
headers: apiKey ? { 'Authorization': `Bearer ${apiKey}` } : {}
|
|
83
132
|
});
|
|
84
133
|
if (!res.ok) {
|
|
134
|
+
const errorBody = await res.text();
|
|
135
|
+
try {
|
|
136
|
+
const json = JSON.parse(errorBody);
|
|
137
|
+
if (json.details) {
|
|
138
|
+
console.error(chalk.red(`Registry Error: ${json.error}`));
|
|
139
|
+
console.error(chalk.red(`Details: ${json.details}`));
|
|
140
|
+
if (json.stack)
|
|
141
|
+
console.error(chalk.gray(json.stack));
|
|
142
|
+
throw new Error("Remote operation failed");
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
catch (e) {
|
|
146
|
+
// Not JSON or parse error, ignore
|
|
147
|
+
}
|
|
85
148
|
if (res.status === 404)
|
|
86
149
|
throw new Error(`Component '${componentId}' not found.`);
|
|
87
150
|
if (res.status === 401)
|
|
88
151
|
throw new Error(`Unauthorized. Please login.`);
|
|
89
|
-
throw new Error(`Registry Error: ${res.statusText}`);
|
|
152
|
+
throw new Error(`Registry Error: ${res.statusText} - ${errorBody.slice(0, 100)}`);
|
|
90
153
|
}
|
|
91
154
|
const data = await res.json();
|
|
92
155
|
const { name, id } = data; // We don't need assets/code/etc anymore for the shell
|
|
@@ -146,7 +209,21 @@ program
|
|
|
146
209
|
else if (!fs.existsSync(path.dirname(targetFile))) {
|
|
147
210
|
fs.mkdirSync(path.dirname(targetFile), { recursive: true });
|
|
148
211
|
}
|
|
212
|
+
// Check if file exists and ask for permission if not forced
|
|
213
|
+
if (fs.existsSync(targetFile) && !options.force) {
|
|
214
|
+
const { overwrite } = await prompts({
|
|
215
|
+
type: 'confirm',
|
|
216
|
+
name: 'overwrite',
|
|
217
|
+
message: `File ${componentName}.tsx already exists. Overwrite?`,
|
|
218
|
+
initial: false
|
|
219
|
+
});
|
|
220
|
+
if (!overwrite) {
|
|
221
|
+
console.log(chalk.yellow('ℹ Action cancelled. File was not modified.'));
|
|
222
|
+
process.exit(0);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
149
225
|
// The Shell Code
|
|
226
|
+
// REMOVED registryUrl explicit prop to allow default or parent override
|
|
150
227
|
const shellCode = `import React from 'react';
|
|
151
228
|
import { SecureScene } from 'editor-sdk';
|
|
152
229
|
|
|
@@ -154,7 +231,6 @@ export default function ${componentName}(props: any) {
|
|
|
154
231
|
return (
|
|
155
232
|
<SecureScene
|
|
156
233
|
id="${id}"
|
|
157
|
-
registryUrl="${options.host}"
|
|
158
234
|
{...props}
|
|
159
235
|
/>
|
|
160
236
|
);
|
package/package.json
CHANGED
|
@@ -1,37 +1,39 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "editor-sdk",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"main": "dist/index.js",
|
|
5
|
-
"module": "dist/index.mjs",
|
|
6
|
-
"types": "dist/index.d.ts",
|
|
7
|
-
"bin": {
|
|
8
|
-
"editor-sdk": "./dist/cli.js"
|
|
9
|
-
},
|
|
10
|
-
"scripts": {
|
|
11
|
-
"build": "tsc",
|
|
12
|
-
"dev": "tsc --watch"
|
|
13
|
-
},
|
|
14
|
-
"peerDependencies": {
|
|
15
|
-
"@react-three/drei": ">=9.0.0",
|
|
16
|
-
"@react-three/fiber": ">=8.0.0",
|
|
17
|
-
"react": ">=18.0.0",
|
|
18
|
-
"react-dom": ">=18.0.0",
|
|
19
|
-
"three": ">=0.160.0"
|
|
20
|
-
},
|
|
21
|
-
"dependencies": {
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"prompts": "^2.4.2"
|
|
26
|
-
},
|
|
27
|
-
"files": [
|
|
28
|
-
"dist"
|
|
29
|
-
],
|
|
30
|
-
"devDependencies": {
|
|
31
|
-
"@
|
|
32
|
-
"@
|
|
33
|
-
"@types/node": "^20.0.0",
|
|
34
|
-
"@types/prompts": "^2.4.9",
|
|
35
|
-
"
|
|
36
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "editor-sdk",
|
|
3
|
+
"version": "0.1.8",
|
|
4
|
+
"main": "dist/index.js",
|
|
5
|
+
"module": "dist/index.mjs",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"bin": {
|
|
8
|
+
"editor-sdk": "./dist/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"dev": "tsc --watch"
|
|
13
|
+
},
|
|
14
|
+
"peerDependencies": {
|
|
15
|
+
"@react-three/drei": ">=9.0.0",
|
|
16
|
+
"@react-three/fiber": ">=8.0.0",
|
|
17
|
+
"react": ">=18.0.0",
|
|
18
|
+
"react-dom": ">=18.0.0",
|
|
19
|
+
"three": ">=0.160.0"
|
|
20
|
+
},
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"chalk": "^4.1.2",
|
|
23
|
+
"commander": "^11.1.0",
|
|
24
|
+
"node-fetch": "^3.3.2",
|
|
25
|
+
"prompts": "^2.4.2"
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"dist"
|
|
29
|
+
],
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@react-three/drei": ">=9.0.0",
|
|
32
|
+
"@react-three/fiber": ">=8.0.0",
|
|
33
|
+
"@types/node": "^20.0.0",
|
|
34
|
+
"@types/prompts": "^2.4.9",
|
|
35
|
+
"@types/react": "^18.3.27",
|
|
36
|
+
"@types/three": "^0.160.0",
|
|
37
|
+
"typescript": "^5.9.3"
|
|
38
|
+
}
|
|
37
39
|
}
|