editor-sdk 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.
- package/README.md +68 -0
- package/dist/SecureScene.d.ts +9 -0
- package/dist/SecureScene.js +102 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/package.json +26 -0
package/README.md
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# 3D Editor SDK
|
|
2
|
+
|
|
3
|
+
The secure runtime SDK for determining, fetching, and rendering components from the 3D Component Registry.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install editor-sdk-secure-demo
|
|
9
|
+
# or
|
|
10
|
+
pnpm add editor-sdk-secure-demo
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
*> Note: Ensure you are using the version that matches your registry configuration.*
|
|
14
|
+
|
|
15
|
+
## Features
|
|
16
|
+
|
|
17
|
+
- **Secure Loading**: Fetches signed URLs for assets, ensuring they are not publicly exposable.
|
|
18
|
+
- **Domain Locking**: Validates the request origin against the allowed domains list.
|
|
19
|
+
- **Caching**: Smart caching of model URLs in `localStorage` to minimize API costs and latency.
|
|
20
|
+
- **Dev Mode**: Shows a "DEV MODE: SECURE" badge when running on localhost.
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
This SDK is primarily used by the code generated via the CLI (`npx 3d-editor add`).
|
|
25
|
+
|
|
26
|
+
### Manual Usage
|
|
27
|
+
|
|
28
|
+
If you want to manually secure-load a scene:
|
|
29
|
+
|
|
30
|
+
```tsx
|
|
31
|
+
import { SecureScene } from 'editor-sdk-secure-demo';
|
|
32
|
+
|
|
33
|
+
function MyComponent() {
|
|
34
|
+
return (
|
|
35
|
+
<div style={{ width: '100vw', height: '100vh' }}>
|
|
36
|
+
<Canvas>
|
|
37
|
+
<SecureScene
|
|
38
|
+
id="your-component-id"
|
|
39
|
+
registryUrl="https://your-registry-api.com"
|
|
40
|
+
/>
|
|
41
|
+
</Canvas>
|
|
42
|
+
</div>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## CLI Authentication
|
|
48
|
+
|
|
49
|
+
To install **Private Components** from the registry, you must be authenticated in your terminal.
|
|
50
|
+
|
|
51
|
+
### How to Login
|
|
52
|
+
|
|
53
|
+
1. **Obtain an API Key**:
|
|
54
|
+
* *Self-Hosted*: Check your database `ApiKey` table or ask your administrator.
|
|
55
|
+
* *Development*: If you are running the registry locally, you can currently use any non-empty string (e.g., `dev-token`) as the system accepts preshared keys for testing.
|
|
56
|
+
|
|
57
|
+
2. **Run Login Command**:
|
|
58
|
+
```bash
|
|
59
|
+
npx 3d-editor login
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
3. **Enter Key**:
|
|
63
|
+
Paste your API key when prompted. It will be saved securely to `~/.3d-editor/config.json`.
|
|
64
|
+
|
|
65
|
+
### Troubleshooting
|
|
66
|
+
|
|
67
|
+
- **401 Unauthorized**: Run `npx 3d-editor login` again.
|
|
68
|
+
- **403 Forbidden**: Your domain might not be whitelisted. Check the Project settings in the Editor.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface SecureSceneProps {
|
|
2
|
+
id: string;
|
|
3
|
+
registryUrl?: string;
|
|
4
|
+
token?: string;
|
|
5
|
+
onLoad?: () => void;
|
|
6
|
+
onError?: (err: any) => void;
|
|
7
|
+
[key: string]: any;
|
|
8
|
+
}
|
|
9
|
+
export declare function SecureScene({ id, registryUrl, token, onLoad, onError, ...props }: SecureSceneProps): import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useState, Suspense } from 'react';
|
|
3
|
+
import { useGLTF, Html } from '@react-three/drei';
|
|
4
|
+
// import { PrimitiveProps } from '@react-three/fiber';
|
|
5
|
+
// Default Registry URL (can be overridden)
|
|
6
|
+
const DEFAULT_REGISTRY_URL = 'http://localhost:3000';
|
|
7
|
+
const CACHE_duration_MS = 50 * 60 * 1000; // 50 minutes (S3 URLs last 60m)
|
|
8
|
+
function SceneLoader({ url, ...props }) {
|
|
9
|
+
// useGLTF will suspend automatically
|
|
10
|
+
const { scene } = useGLTF(url);
|
|
11
|
+
return _jsx("primitive", { object: scene, ...props });
|
|
12
|
+
}
|
|
13
|
+
export function SecureScene({ id, registryUrl = DEFAULT_REGISTRY_URL, token, onLoad, onError, ...props }) {
|
|
14
|
+
const [modelUrl, setModelUrl] = useState(null);
|
|
15
|
+
const [error, setError] = useState(null);
|
|
16
|
+
const [isDev, setIsDev] = useState(false);
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
let mounted = true;
|
|
19
|
+
// Check Dev Environment
|
|
20
|
+
if (typeof window !== 'undefined' && window.location.hostname === 'localhost') {
|
|
21
|
+
setIsDev(true);
|
|
22
|
+
}
|
|
23
|
+
async function fetchMetadata() {
|
|
24
|
+
try {
|
|
25
|
+
// 0. Check Cache (LocalStorage)
|
|
26
|
+
const cacheKey = `3d-editor:cache:${id}`;
|
|
27
|
+
const cached = localStorage.getItem(cacheKey);
|
|
28
|
+
if (cached) {
|
|
29
|
+
const data = JSON.parse(cached);
|
|
30
|
+
const age = Date.now() - (data.timestamp || 0);
|
|
31
|
+
if (age < CACHE_duration_MS) {
|
|
32
|
+
if (mounted) {
|
|
33
|
+
console.log(`[SecureScene] Using cached URL for ${id}`);
|
|
34
|
+
setModelUrl(data.modelUrl);
|
|
35
|
+
onLoad?.();
|
|
36
|
+
}
|
|
37
|
+
return; // Exit early
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
console.log(`[SecureScene] Cache expired for ${id}`);
|
|
41
|
+
localStorage.removeItem(cacheKey);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
// 1. Call the Registry API
|
|
45
|
+
const res = await fetch(`${registryUrl}/api/registry/${id}`, {
|
|
46
|
+
headers: {
|
|
47
|
+
// Future: 'Authorization': `Bearer ${token}`
|
|
48
|
+
'Content-Type': 'application/json',
|
|
49
|
+
// Referer is sent automatically by browser
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
if (!res.ok) {
|
|
53
|
+
throw new Error(`Registry Error: ${res.statusText}`);
|
|
54
|
+
}
|
|
55
|
+
const data = await res.json();
|
|
56
|
+
if (!data.modelUrl || data.modelUrl === 'none') {
|
|
57
|
+
throw new Error("No model URL found for this component");
|
|
58
|
+
}
|
|
59
|
+
// 2. Cache it
|
|
60
|
+
try {
|
|
61
|
+
const cachePayload = { ...data, timestamp: Date.now() };
|
|
62
|
+
localStorage.setItem(cacheKey, JSON.stringify(cachePayload));
|
|
63
|
+
}
|
|
64
|
+
catch (e) {
|
|
65
|
+
// Ignore quota errors
|
|
66
|
+
}
|
|
67
|
+
if (mounted) {
|
|
68
|
+
setModelUrl(data.modelUrl);
|
|
69
|
+
onLoad?.();
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
if (mounted) {
|
|
74
|
+
console.error("SecureScene Error:", err);
|
|
75
|
+
setError(err.message);
|
|
76
|
+
onError?.(err);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
fetchMetadata();
|
|
81
|
+
return () => { mounted = false; };
|
|
82
|
+
}, [id, registryUrl, token, onLoad, onError]);
|
|
83
|
+
if (error) {
|
|
84
|
+
return (_jsxs("group", { children: [_jsxs("mesh", { children: [_jsx("boxGeometry", { args: [1, 1, 1] }), _jsx("meshStandardMaterial", { color: "red", wireframe: true })] }), _jsx(Html, { position: [0, 1, 0], center: true, children: _jsxs("div", { style: { color: 'red', background: 'white', padding: 4 }, children: ["Error: ", error] }) })] }));
|
|
85
|
+
}
|
|
86
|
+
if (!modelUrl) {
|
|
87
|
+
// Loading State
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
return (_jsxs("group", { children: [_jsx(Suspense, { fallback: null, children: _jsx(SceneLoader, { url: modelUrl, ...props }) }), isDev && (_jsx(Html, { position: [0, 0, 0], fullscreen: true, style: { pointerEvents: 'none' }, children: _jsx("div", { style: {
|
|
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" }) }))] }));
|
|
102
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { SecureScene } from './SecureScene';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { SecureScene } from './SecureScene';
|
package/package.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "editor-sdk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"main": "dist/index.js",
|
|
5
|
+
"module": "dist/index.mjs",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"dev": "tsc --watch"
|
|
10
|
+
},
|
|
11
|
+
"peerDependencies": {
|
|
12
|
+
"@react-three/drei": ">=9.0.0",
|
|
13
|
+
"@react-three/fiber": ">=8.0.0",
|
|
14
|
+
"react": ">=18.0.0",
|
|
15
|
+
"react-dom": ">=18.0.0",
|
|
16
|
+
"three": ">=0.160.0"
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist"
|
|
20
|
+
],
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"@types/react": "^18.3.27",
|
|
23
|
+
"@types/three": "^0.160.0",
|
|
24
|
+
"typescript": "^5.9.3"
|
|
25
|
+
}
|
|
26
|
+
}
|