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 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
+ }
@@ -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
+ }