@process.co/element-dev-server 0.0.1-test-3
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 +108 -0
- package/dist/chunk-NT4V5NUJ.js +1 -0
- package/dist/chunk-QPYNK3X7.js +1 -0
- package/dist/cli.js +18 -0
- package/dist/logo.js +1 -0
- package/dist/ui.js +1 -0
- package/dist/version.js +1 -0
- package/package.json +79 -0
- package/src/cli.tsx +56 -0
- package/src/logo.tsx +31 -0
- package/src/ui.tsx +651 -0
- package/src/version.json +1 -0
package/README.md
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# Process.co Element Dev Server
|
|
2
|
+
|
|
3
|
+
A CLI tool for developing Process.co elements with an interactive development server.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm install
|
|
9
|
+
pnpm build
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Usage
|
|
13
|
+
|
|
14
|
+
The CLI accepts an optional path argument to specify which directory to scan for Process.co element modules:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# Use current directory
|
|
18
|
+
process-element
|
|
19
|
+
|
|
20
|
+
# Use a specific path
|
|
21
|
+
process-element /path/to/your/elements
|
|
22
|
+
|
|
23
|
+
# Use relative path
|
|
24
|
+
process-element ../my-elements
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Features
|
|
28
|
+
|
|
29
|
+
- **Interactive UI**: Uses Ink to provide a beautiful terminal interface
|
|
30
|
+
- **Path Validation**: Automatically validates that the provided path exists and is a directory
|
|
31
|
+
- **Element Discovery**: Uses the @process.co/elements library to discover and load element modules
|
|
32
|
+
- **Dev Server Launch**: Launches a Vite development server for the selected element
|
|
33
|
+
- **Smart Directory Detection**: Automatically finds the appropriate dev directory for each element
|
|
34
|
+
|
|
35
|
+
## How It Works
|
|
36
|
+
|
|
37
|
+
The CLI uses the `@process.co/elements` library to:
|
|
38
|
+
|
|
39
|
+
1. **Scan for Elements**: Looks for `.mjs` or `.mts` files in the specified directory
|
|
40
|
+
2. **Load Modules**: Imports each element module using the elements library
|
|
41
|
+
3. **Display Information**: Shows element names and types in an interactive list
|
|
42
|
+
4. **Launch Dev Server**: Starts a Vite server in the appropriate directory
|
|
43
|
+
|
|
44
|
+
## Development
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
# Build the project
|
|
48
|
+
pnpm build
|
|
49
|
+
|
|
50
|
+
# Run in development mode
|
|
51
|
+
pnpm cli
|
|
52
|
+
|
|
53
|
+
# Watch for changes
|
|
54
|
+
pnpm dev
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Requirements
|
|
58
|
+
|
|
59
|
+
- Node.js 18+
|
|
60
|
+
- A directory containing Process.co element modules (`.mjs` or `.mts` files)
|
|
61
|
+
- Each element should have proper exports with `name` and `type` properties
|
|
62
|
+
- Elements should have a `dev/` directory with a `vite.config.js` file (optional)
|
|
63
|
+
|
|
64
|
+
## Element Module Format
|
|
65
|
+
|
|
66
|
+
Elements should be exported as modules with the following structure:
|
|
67
|
+
|
|
68
|
+
```javascript
|
|
69
|
+
// example-element.mjs
|
|
70
|
+
export default {
|
|
71
|
+
name: 'Example Element',
|
|
72
|
+
type: 'app', // or 'action', 'signal', 'credential'
|
|
73
|
+
description: 'An example element',
|
|
74
|
+
// ... other properties
|
|
75
|
+
};
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Error Handling
|
|
79
|
+
|
|
80
|
+
The CLI provides clear error messages for common issues:
|
|
81
|
+
|
|
82
|
+
- **Path doesn't exist**: Shows the full path that was not found
|
|
83
|
+
- **Path is not a directory**: Validates that the path points to a directory
|
|
84
|
+
- **No elements found**: Provides guidance when no valid element modules are found
|
|
85
|
+
- **Module loading errors**: Shows specific errors when element modules fail to load
|
|
86
|
+
|
|
87
|
+
## Examples
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
# From a project root with element modules
|
|
91
|
+
process-element
|
|
92
|
+
|
|
93
|
+
# From anywhere, pointing to a specific project
|
|
94
|
+
process-element ~/projects/my-process-app
|
|
95
|
+
|
|
96
|
+
# Using relative paths
|
|
97
|
+
process-element ./elements
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Element Discovery
|
|
101
|
+
|
|
102
|
+
The CLI will automatically discover elements that:
|
|
103
|
+
|
|
104
|
+
- Are `.mjs` or `.mts` files in the specified directory
|
|
105
|
+
- Have proper exports with `name` and `type` properties
|
|
106
|
+
- Can be successfully imported by the `@process.co/elements` library
|
|
107
|
+
|
|
108
|
+
Elements are displayed with their name and type for easy identification.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{useState as y,useEffect as F,useRef as ie}from"react";import{Box as m,Text as e}from"ink";import W from"ink-select-input";import E from"fs";import u from"path";import{createServer as se}from"vite";import{fileURLToPath as le}from"url";import v from"@process.co/element-dev-support/dist/index.js";import{Fragment as ce,jsx as o,jsxs as r}from"react/jsx-runtime";var he=({rootDir:g})=>{let[P,V]=y([]),[T,z]=y(null),[k,q]=y([]),[_,Q]=y(null),[Y,Z]=y([]),[ee,b]=y(null),[K,te]=y(null),[x,c]=y("loading"),[R,H]=y("elements"),[C,G]=y([]),w=ie(G);w.current=G;let[U,ne]=y(null),[ue,re]=y(null),oe=async(a,t,i)=>{try{let l=E.statSync(a).isDirectory()?a:u.dirname(a),s=i==="action"?"actions":"sources",n=u.join(l,s);if(!E.existsSync(n))return null;let f=(await v.importFolderModulesOfType(s,i,l,["common"])).find(p=>p.key===t);if(!f)return null;let j=E.readdirSync(n).filter(p=>{let h=u.join(n,p);return E.statSync(h).isDirectory()}),S=null,B=null;for(let p of j){let h=u.join(n,p),J=[`${p}.mjs`,`${p}.mts`,`${p}.js`,`${p}.ts`,"index.mjs","index.mts","index.js","index.ts"];for(let L of J){let d=u.join(h,L);if(E.existsSync(d))try{let{module:N}=await v.importFromPath(d);if(N.key===t){S=d,B=h;break}}catch{continue}}if(S)break}if(!S)return null;let A=u.join(l,"ui");return f&&f.ui&&(A=u.join(l,"ui",f.ui)),{modulePath:S,uiDir:A}}catch{return null}};F(()=>{(async()=>{try{try{let s=await v.autoDetectElement(g);if(s!=="pipedream"){let n=await v.loadElementPointers(g,s);V([{label:`${n.name} (${n.elementType})`,value:n.name,info:n,path:g}]),c("ready");return}}catch{}let t=E.readdirSync(g,{withFileTypes:!0}),i=[];for(let s of t){let n=u.join(g,s.name);if(s.isDirectory())try{E.readdirSync(n).some(f=>f.endsWith(".mjs")||f.endsWith(".mts")||f.endsWith(".js")||f.endsWith(".ts")||f.endsWith(".tsx")||f.endsWith(".jsx"))&&i.push(n)}catch{}}if(i.length===0){c("no-elements");return}let l=[];for(let s of i)try{let n=await v.loadElementPointers(s,"auto");l.push({label:`${n.name} (${n.elementType})`,value:n.name,info:n,path:s})}catch{}if(l.length>0){V(l),c("ready");return}else c("no-elements")}catch{c("error")}})()},[g]),F(()=>{U&&x==="launching"&&c("dev-server-running")},[U,x]),F(()=>{},[x]);let M=async(a,t,i)=>{if(!a)return;let l=await oe(a.path,t.value,t.type);if(!l){c("error");return}let s=E.statSync(a.path).isDirectory()?a.path:u.dirname(a.path),n=u.join(s,"dev"),D=E.existsSync(n)?n:s,f=le(import.meta.url),j=u.dirname(f),S=u.resolve(j,"../.process/vite.config.cjs"),B=u.join(D,"vite.config.js"),A=u.join(D,"vite.config.ts"),p=S;E.existsSync(B)?p=B:E.existsSync(A)&&(p=A);let h=await v.loadElementPointers(a.path,"auto"),J=h.actions?.find(d=>d.key===t.value)||h.signals?.find(d=>d.key===t.value);process.env.VITE_ELEMENT_PATH=a.path,process.env.VITE_ELEMENT_TYPE=t.type,process.env.VITE_ELEMENT_NAME=a.info.name,process.env.VITE_ACTION_SIGNAL_KEY=t.value,process.env.VITE_PROPERTY_KEY=i?.propertyKey||"",process.env.VITE_PROPERTY_TYPE=i?.type||"",process.env.VITE_PROPERTY_UI_PATH=i?.uiPath||"",process.env.VITE_MODULE_PATH=l.modulePath,process.env.VITE_UI_DIRECTORY=l.uiDir;let L=1;try{let d=await se({configFile:p,root:u.resolve(j,"../.process"),optimizeDeps:{include:["react","react-dom"],exclude:[a.path]},define:{"import.meta.env.VITE_ELEMENT_PATH":JSON.stringify(a.path),"import.meta.env.VITE_ELEMENT_TYPE":JSON.stringify(t.type),"import.meta.env.VITE_ELEMENT_NAME":JSON.stringify(a.info.name),"import.meta.env.VITE_ACTION_SIGNAL_KEY":JSON.stringify(t.value),"import.meta.env.VITE_PROPERTY_KEY":JSON.stringify(i?.propertyKey||null),"import.meta.env.VITE_PROPERTY_TYPE":JSON.stringify(i?.type||null),"import.meta.env.VITE_PROPERTY_UI_PATH":JSON.stringify(i?.uiPath||null),"import.meta.env.VITE_MODULE_PATH":JSON.stringify(l.modulePath),"import.meta.env.VITE_UI_DIRECTORY":JSON.stringify(l.uiDir),"import.meta.env.VITE_ELEMENT_MODULE":JSON.stringify(h),"import.meta.env.VITE_CURRENT_ACTION_SIGNAL":JSON.stringify(J),"import.meta.env.VITE_SELECTED_PROPERTY":JSON.stringify(i)},logLevel:"info",customLogger:{info(I){w.current(O=>[...O,I].slice(-L))},warn(I){w.current(O=>[...O,`[warn] ${I}`].slice(-L))},error(I){w.current(O=>[...O,`[error] ${I}`].slice(-L))},clearScreen(){},hasWarned:!1,warnOnce(I){},hasErrorLogged:()=>!1}});await d.listen();let N=`http://localhost:${d.config.server?.port||5173}`;ne(N),re(`Dev server running at ${N}`);let X=()=>{d.close()};process.on("SIGINT",X),process.on("SIGTERM",X),d.watcher.on("change",I=>{}),c("dev-server-running"),process.stdin.resume()}catch{c("error")}},$=async a=>{if(R==="elements"){let t=P.find(i=>i.value===a.value);if(!t)return;z(t),c("loading-actions-signals");try{let i=await v.loadElementPointers(t.path,"auto"),l=[];i.actions&&Array.isArray(i.actions)&&i.actions.forEach((s,n)=>{l.push({label:`Action: ${s.name||s.key}`,value:s.key,type:"action",path:t.path,description:s.description})}),i.signals&&Array.isArray(i.signals)&&i.signals.forEach((s,n)=>{l.push({label:`Signal: ${s.name||s.key}`,value:s.key,type:"signal",path:t.path,description:s.description})}),l.length===0&&l.push({label:"Default Component",value:"default",type:"action",path:t.path,description:"Default component from element"}),q(l),H("actions-signals"),c("ready")}catch{c("error")}}else if(R==="actions-signals"){let t=k.find(i=>i.value===a.value);if(!t||!T)return;Q(t),c("loading-properties");try{let i=await v.loadElementPointers(T.path,"auto"),l=i.actions?.find(n=>n.key===t.value)||i.signals?.find(n=>n.key===t.value);if(l&&l.ui){b(t.value),c("launching"),M(T,t,null).catch(n=>{c("error")});return}if(!l||!l.props){b(t.value),c("launching");try{await M(T,t,null)}catch{c("error")}return}let s=[];if(l.props.forEach(n=>{n.ui&&s.push({label:`\u{1F3A8} ${n.key} (${n.ui})`,value:`ui-${n.key}`,propertyKey:n.key,type:"ui-variant",uiPath:n.ui,description:n.description,propertyData:n})}),s.length===0){b(t.value),c("launching"),await M(T,t,null);return}Z(s),H("properties"),c("ready")}catch{c("error")}}else{let t=Y.find(i=>i.value===a.value);if(!t||!_||!T)return;te(t),b(t.value),c("launching"),await M(T,_,t)}};return x==="loading"?r(m,{flexDirection:"column",marginTop:2,children:[r(e,{children:["\u{1F50D} Loading elements from: ",o(e,{color:"cyan",children:g})]}),o(e,{children:"Loading..."})]}):x==="no-elements"?r(m,{flexDirection:"column",marginTop:2,children:[r(e,{color:"red",children:["\u274C No valid elements found in: ",o(e,{color:"cyan",children:g})]}),o(e,{color:"yellow",children:"Make sure the directory contains valid element modules."}),o(e,{color:"yellow",children:"Supported formats: Pipedream, n8n, Doflo, Process.co"}),o(e,{color:"yellow",children:"Supported file types: .js, .ts, .mjs, .mts, .jsx, .tsx"})]}):x==="error"?r(m,{flexDirection:"column",marginTop:2,children:[r(e,{color:"red",children:["\u274C Error loading elements from: ",o(e,{color:"cyan",children:g})]}),o(e,{color:"yellow",children:"Check that the directory contains valid element modules."})]}):x==="loading-actions-signals"?r(m,{flexDirection:"column",marginTop:2,children:[r(e,{children:["\u{1F50D} Loading actions and signals for '",o(e,{color:"cyan",children:T?.info.name}),"'..."]}),o(e,{children:"Loading..."})]}):x==="loading-properties"?r(m,{flexDirection:"column",marginTop:2,children:[r(e,{children:["\u{1F50D} Loading properties for '",o(e,{color:"cyan",children:_?.label}),"'..."]}),o(e,{children:"Loading..."})]}):x==="launching"?r(m,{flexDirection:"column",marginTop:2,children:[r(e,{color:"green",children:["\u{1F680} Launching dev server for '",o(e,{color:"cyan",children:ee}),"'..."]}),r(e,{children:["Directory: ",o(e,{color:"cyan",children:g})]})]}):x==="dev-server-running"?r(ce,{children:[r(m,{flexDirection:"column",flexShrink:1,borderStyle:"round",margin:2,marginBottom:1,paddingX:2,paddingY:1,borderColor:"#8759F2",children:[r(e,{children:["Server Running: ",o(e,{color:"#01D4E7",underline:!0,children:U})]}),r(e,{children:["Element: ",o(e,{color:"#01D4E7",children:T?.info.name})]}),r(e,{children:["Action/Signal: ",o(e,{color:"#01D4E7",children:_?.label})]}),K?r(e,{children:["Property UI: ",o(e,{color:"#01D4E7",children:K.label})]}):r(e,{children:["UI Mode: ",o(e,{color:"#01D4E7",children:"Action/Signal Level"})]}),o(e,{color:"yellow",children:"Press Ctrl+C to stop the server"})]}),r(m,{marginTop:1,marginLeft:4,flexDirection:"column",children:[o(e,{color:"gray",children:"Server Status:"}),C.length>0?C.map((a,t)=>o(e,{color:"gray",children:a},t)):o(e,{color:"gray",children:"Waiting for activity..."})]})]}):R==="properties"?r(m,{flexDirection:"column",children:[r(e,{children:["\u{1F4C1} Element: ",o(e,{color:"cyan",children:T?.info.name})]}),r(e,{children:["\u{1F4CB} Action/Signal: ",o(e,{color:"#01D4E7",children:_?.label})]}),r(e,{children:["Found ",Y.length," property(ies):"]}),o(W,{items:Y,onSelect:$})]}):R==="actions-signals"?r(m,{flexDirection:"column",marginTop:2,marginBottom:2,children:[r(e,{children:["\u{1F4C1} Element: ",o(e,{color:"#01D4E7",children:T?.info.name})]}),r(m,{flexDirection:"column",marginTop:1,children:[r(e,{children:["Found ",k.length," action(s)/signal(s):"]}),o(W,{items:k,onSelect:$})]})]}):r(m,{flexDirection:"column",marginTop:2,marginBottom:2,children:[r(m,{flexDirection:"column",children:[r(e,{children:["\u{1F4C1} Directory: ",o(e,{color:"#01D4E7",children:g})]}),r(m,{flexDirection:"column",marginTop:1,children:[r(e,{children:["Found ",P.length," element(s):"]}),o(W,{items:P,onSelect:$})]})]}),o(ae,{logs:C})]})},ae=({logs:g})=>g.length?r(m,{flexDirection:"column",marginTop:1,children:[o(e,{color:"gray",children:"Output:"}),g.map((P,V)=>o(e,{color:"gray",children:P},V))]}):null;export{he as a};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{Box as t,Text as o}from"ink";import r from"path";import{fileURLToPath as f}from"url";import{readFileSync as n}from"fs";import{Fragment as h,jsx as _,jsxs as e}from"react/jsx-runtime";var x=f(import.meta.url),a=r.dirname(x),c=r.resolve(r.join(a,"../package.json")),i=JSON.parse(n(c,"utf8"));function d(l){return new Array(42-l.length).fill(" ").join("")}function B(){return _(h,{children:e(t,{flexDirection:"column",display:"flex",alignSelf:"flex-start",marginLeft:3,marginTop:1,marginBottom:1,children:[e(t,{flexGrow:1,children:[_(o,{color:"whiteBright",bold:!0,children:" _ __ _ __ ___ ___ ___ ___ ___"}),_(o,{color:"#dffff",children:" ___ ___ "})]}),e(t,{flexGrow:1,children:[_(o,{color:"whiteBright",bold:!0,children:" | '_ \\| '__/ _ \\ / __/ _ \\/ __/ __|"}),_(o,{color:"#dffff",children:" / __/ _ \\"})]}),e(t,{flexGrow:1,children:[_(o,{color:"whiteBright",bold:!0,children:" | |_) | | | (_) | (_| __/\\__ \\__ \\"}),_(o,{color:"#dffff",children:"| (_| (_) | "})]}),e(t,{flexGrow:1,children:[_(o,{color:"whiteBright",bold:!0,children:" | .__/|_| \\___/ \\___\\___||___/___"}),_(o,{color:"#FF3C82",children:"(_)"}),_(o,{color:"#dffff",children:"___\\___/"})]}),e(t,{flexGrow:1,children:[e(o,{color:"whiteBright",bold:!0,children:[" |_|",d(i.version)]}),e(o,{color:"whiteBright",bold:!0,children:["v",i.version]})]}),_(t,{flexGrow:1,paddingTop:1,children:_(o,{color:"whiteBright",bold:!0,children:"a CLI to aid in the building of process.co elements"})})]})})}export{d as a,B as b};
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{b as t}from"./chunk-QPYNK3X7.js";import{a as r}from"./chunk-NT4V5NUJ.js";import{render as p}from"ink";import c from"meow";import m from"path";import i from"fs";import{Fragment as f,jsx as o,jsxs as d}from"react/jsx-runtime";process.stdout.write("\x1Bc");var s=c(`
|
|
3
|
+
Usage
|
|
4
|
+
$ process-element [path]
|
|
5
|
+
|
|
6
|
+
Options
|
|
7
|
+
--help Show help
|
|
8
|
+
--version Show version
|
|
9
|
+
|
|
10
|
+
Examples
|
|
11
|
+
$ process-element # Use current directory
|
|
12
|
+
$ process-element /path/to/elements # Use specified path
|
|
13
|
+
$ process-element ./elements # Use relative path
|
|
14
|
+
|
|
15
|
+
Description
|
|
16
|
+
Scans the specified directory for Process.co element modules (.mjs/.mts files)
|
|
17
|
+
and provides an interactive interface to launch development servers.
|
|
18
|
+
`,{importMeta:import.meta}),e=s.input[0]?m.resolve(s.input[0]):process.cwd();i.existsSync(e)||(console.error(`\u274C Error: Path '${e}' does not exist.`),process.exit(1));var a=i.statSync(e);a.isDirectory()||(console.error(`\u274C Error: '${e}' is not a directory.`),process.exit(1));var l=({rootDir:n})=>d(f,{children:[o(t,{}),o(r,{rootDir:n})]});p(o(l,{rootDir:e}));
|
package/dist/logo.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{a,b}from"./chunk-QPYNK3X7.js";export{b as default,a as lengthAsSpace};
|
package/dist/ui.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{a}from"./chunk-NT4V5NUJ.js";export{a as DevUI};
|
package/dist/version.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var o="0.0.0-development",v={version:o};export{v as default,o as version};
|
package/package.json
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@process.co/element-dev-server",
|
|
3
|
+
"version": "0.0.1-test-3",
|
|
4
|
+
"description": "Helper Library for developing elements for Process.co",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": "./dist/index.js"
|
|
10
|
+
},
|
|
11
|
+
"bin": {
|
|
12
|
+
"proc-dev": "tsx ./dist/cli.js"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist/*",
|
|
16
|
+
"src/*",
|
|
17
|
+
"bundle/*"
|
|
18
|
+
],
|
|
19
|
+
"publishConfig": {
|
|
20
|
+
"access": "public",
|
|
21
|
+
"registry": "https://registry.npmjs.org",
|
|
22
|
+
"scope": "@process.co"
|
|
23
|
+
},
|
|
24
|
+
"release": {
|
|
25
|
+
"branches": [
|
|
26
|
+
"main"
|
|
27
|
+
]
|
|
28
|
+
},
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "rm -rf ./dist && echo '{\"version\":'$(jq '.version' ./package.json)'}' > ./src/version.json && tsup && chmod +x ./dist/cli.js",
|
|
31
|
+
"dev": "tsx src/cli.tsx",
|
|
32
|
+
"cli": "tsx dist/cli.js",
|
|
33
|
+
"test": "jest"
|
|
34
|
+
},
|
|
35
|
+
"keywords": [
|
|
36
|
+
"process",
|
|
37
|
+
"element",
|
|
38
|
+
"cli",
|
|
39
|
+
"validation",
|
|
40
|
+
"compatibility"
|
|
41
|
+
],
|
|
42
|
+
"author": {
|
|
43
|
+
"name": "Process.co Team",
|
|
44
|
+
"email": "developers@process.co"
|
|
45
|
+
},
|
|
46
|
+
"license": "ISC",
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"@process.co/element-dev-support": "github:process-co/npm-element-dev-support#main",
|
|
49
|
+
"@vitejs/plugin-react": "^4.3.4",
|
|
50
|
+
"axios": "^1.8.4",
|
|
51
|
+
"ink": "^6.0.1",
|
|
52
|
+
"ink-select-input": "^6.2.0",
|
|
53
|
+
"io-ts": "^2.2.22",
|
|
54
|
+
"meow": "^13.2.0",
|
|
55
|
+
"querystring": "^0.2.1",
|
|
56
|
+
"react": "^19.1.0",
|
|
57
|
+
"react-dom": "^19.1.0",
|
|
58
|
+
"tsup": "^8.5.0",
|
|
59
|
+
"tsx": "^4.19.1",
|
|
60
|
+
"type-fest": "^4.21.0",
|
|
61
|
+
"vite": "^7.0.2"
|
|
62
|
+
},
|
|
63
|
+
"devDependencies": {
|
|
64
|
+
"@babel/core": "^7.26.9",
|
|
65
|
+
"@babel/preset-env": "^7.26.9",
|
|
66
|
+
"@babel/preset-typescript": "^7.26.0",
|
|
67
|
+
"@repo/config-jest": "workspace:^",
|
|
68
|
+
"@repo/config-typescript": "workspace:^",
|
|
69
|
+
"@types/jest": "^29.5.14",
|
|
70
|
+
"@types/node": "22.13.17",
|
|
71
|
+
"@types/react": "19.1.0",
|
|
72
|
+
"@vercel/ncc": "^0.38.1",
|
|
73
|
+
"babel-jest": "^29.7.0",
|
|
74
|
+
"jest": "^29.7.0",
|
|
75
|
+
"ts-jest": "^29.2.6",
|
|
76
|
+
"typescript": "^5.7.3"
|
|
77
|
+
},
|
|
78
|
+
"registry": "https://registry.npmjs.org"
|
|
79
|
+
}
|
package/src/cli.tsx
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
process.stdout.write('\x1Bc'); // Clear the terminal
|
|
4
|
+
|
|
5
|
+
import { render } from 'ink';
|
|
6
|
+
import meow from 'meow';
|
|
7
|
+
import React from 'react';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import fs from 'fs';
|
|
10
|
+
import { DevUI } from './ui.js';
|
|
11
|
+
import Logo from './logo.js';
|
|
12
|
+
|
|
13
|
+
const cli = meow(`
|
|
14
|
+
Usage
|
|
15
|
+
$ process-element [path]
|
|
16
|
+
|
|
17
|
+
Options
|
|
18
|
+
--help Show help
|
|
19
|
+
--version Show version
|
|
20
|
+
|
|
21
|
+
Examples
|
|
22
|
+
$ process-element # Use current directory
|
|
23
|
+
$ process-element /path/to/elements # Use specified path
|
|
24
|
+
$ process-element ./elements # Use relative path
|
|
25
|
+
|
|
26
|
+
Description
|
|
27
|
+
Scans the specified directory for Process.co element modules (.mjs/.mts files)
|
|
28
|
+
and provides an interactive interface to launch development servers.
|
|
29
|
+
`, {
|
|
30
|
+
importMeta: import.meta,
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// Get the path from arguments or use current directory
|
|
34
|
+
const targetPath = cli.input[0] ? path.resolve(cli.input[0]) : process.cwd();
|
|
35
|
+
|
|
36
|
+
// Validate that the path exists
|
|
37
|
+
if (!fs.existsSync(targetPath)) {
|
|
38
|
+
console.error(`❌ Error: Path '${targetPath}' does not exist.`);
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Check if it's a directory
|
|
43
|
+
const stats = fs.statSync(targetPath);
|
|
44
|
+
if (!stats.isDirectory()) {
|
|
45
|
+
console.error(`❌ Error: '${targetPath}' is not a directory.`);
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const App = ({ rootDir }: { rootDir: string }) => (
|
|
50
|
+
<>
|
|
51
|
+
<Logo />
|
|
52
|
+
<DevUI rootDir={rootDir} />
|
|
53
|
+
</>
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
render(<App rootDir={targetPath} />);
|
package/src/logo.tsx
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
import { readFileSync } from 'fs';
|
|
6
|
+
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
const __dirname = path.dirname(__filename);
|
|
9
|
+
const packageJsonPath = path.resolve(path.join(__dirname, "../package.json"));
|
|
10
|
+
const pack = JSON.parse(readFileSync(packageJsonPath, 'utf8')) as { version: string };
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
export function lengthAsSpace(version:string){
|
|
14
|
+
return new Array(42 - version.length).fill(" ").join("")
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export default function (){
|
|
18
|
+
|
|
19
|
+
// const version = pack.version
|
|
20
|
+
|
|
21
|
+
return (<>
|
|
22
|
+
<Box flexDirection="column" display="flex" alignSelf="flex-start" marginLeft={3} marginTop={1} marginBottom={1} >
|
|
23
|
+
<Box flexGrow={1} ><Text color="whiteBright" bold={true} > _ __ _ __ ___ ___ ___ ___ ___</Text><Text color="#dffff"> ___ ___ </Text></Box>
|
|
24
|
+
<Box flexGrow={1}><Text color="whiteBright" bold={true} > | '_ \| '__/ _ \ / __/ _ \/ __/ __|</Text><Text color="#dffff"> / __/ _ \</Text></Box>
|
|
25
|
+
<Box flexGrow={1}><Text color="whiteBright" bold={true}> | |_) | | | (_) | (_| __/\__ \__ \</Text><Text color="#dffff">| (_| (_) | </Text></Box>
|
|
26
|
+
<Box flexGrow={1}><Text color="whiteBright" bold={true}> | .__/|_| \___/ \___\___||___/___</Text><Text color="#FF3C82">(_)</Text><Text color="#dffff">___\___/</Text></Box>
|
|
27
|
+
<Box flexGrow={1}><Text color="whiteBright" bold={true}> |_|{lengthAsSpace(pack.version)}</Text><Text color="whiteBright" bold={true} >v{pack.version}</Text></Box>
|
|
28
|
+
<Box flexGrow={1} paddingTop={1} ><Text color="whiteBright" bold={true} >a CLI to aid in the building of process.co elements</Text></Box>
|
|
29
|
+
</Box>
|
|
30
|
+
</>)
|
|
31
|
+
}
|
package/src/ui.tsx
ADDED
|
@@ -0,0 +1,651 @@
|
|
|
1
|
+
// src/DevUI.js
|
|
2
|
+
import React, { useState, useEffect, useRef } from 'react';
|
|
3
|
+
import { Box, Text } from 'ink';
|
|
4
|
+
import SelectInput from 'ink-select-input';
|
|
5
|
+
import fs from 'fs';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
import { createServer } from 'vite';
|
|
8
|
+
import { fileURLToPath } from 'url';
|
|
9
|
+
import dev_support from '@process.co/element-dev-support/dist/index.js';
|
|
10
|
+
|
|
11
|
+
interface ElementItem {
|
|
12
|
+
label: string;
|
|
13
|
+
value: string;
|
|
14
|
+
info: dev_support.IProcessDefinitionUIInfo;
|
|
15
|
+
path: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface ActionSignalItem {
|
|
19
|
+
label: string;
|
|
20
|
+
value: string;
|
|
21
|
+
type: 'action' | 'signal';
|
|
22
|
+
path: string;
|
|
23
|
+
description?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface PropertyItem {
|
|
27
|
+
label: string;
|
|
28
|
+
value: string;
|
|
29
|
+
propertyKey: string;
|
|
30
|
+
type: 'standard' | 'ui-variant';
|
|
31
|
+
uiPath?: string;
|
|
32
|
+
description?: string;
|
|
33
|
+
propertyData?: any;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const DevUI = ({ rootDir }: { rootDir: string }) => {
|
|
37
|
+
const [elements, setElements] = useState<ElementItem[]>([]);
|
|
38
|
+
const [selectedElement, setSelectedElement] = useState<ElementItem | null>(null);
|
|
39
|
+
const [actionsSignals, setActionsSignals] = useState<ActionSignalItem[]>([]);
|
|
40
|
+
const [selectedActionSignal, setSelectedActionSignal] = useState<ActionSignalItem | null>(null);
|
|
41
|
+
const [properties, setProperties] = useState<PropertyItem[]>([]);
|
|
42
|
+
const [selected, setSelected] = useState<string | null>(null);
|
|
43
|
+
const [selectedProperty, setSelectedProperty] = useState<PropertyItem | null>(null);
|
|
44
|
+
const [status, setStatus] = useState<'loading' | 'ready' | 'no-elements' | 'error' | 'loading-actions-signals' | 'loading-properties' | 'launching' | 'dev-server-running'>('loading');
|
|
45
|
+
const [step, setStep] = useState<'elements' | 'actions-signals' | 'properties'>('elements');
|
|
46
|
+
const [viteLogs, setViteLogs] = useState<string[]>([]);
|
|
47
|
+
const viteLogsRef = useRef(setViteLogs);
|
|
48
|
+
viteLogsRef.current = setViteLogs;
|
|
49
|
+
const [viteUrl, setViteUrl] = useState<string | null>(null);
|
|
50
|
+
// Add a serverMessage state for display messages
|
|
51
|
+
const [serverMessage, setServerMessage] = useState<string | null>(null);
|
|
52
|
+
|
|
53
|
+
// Function to get the actual module path for an action/signal
|
|
54
|
+
const getModulePath = async (elementPath: string, actionSignalKey: string, type: 'action' | 'signal'): Promise<{ modulePath: string; uiDir: string } | null> => {
|
|
55
|
+
try {
|
|
56
|
+
// Get the base directory of the element
|
|
57
|
+
const elementDir = fs.statSync(elementPath).isDirectory()
|
|
58
|
+
? elementPath
|
|
59
|
+
: path.dirname(elementPath);
|
|
60
|
+
|
|
61
|
+
// Determine the folder to look in based on type
|
|
62
|
+
const folderName = type === 'action' ? 'actions' : 'sources';
|
|
63
|
+
const folderPath = path.join(elementDir, folderName);
|
|
64
|
+
|
|
65
|
+
// Check if the folder exists
|
|
66
|
+
if (!fs.existsSync(folderPath)) {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Get all modules of the specific type to find the one with matching key
|
|
71
|
+
const modules = await dev_support.importFolderModulesOfType(folderName, type, elementDir, ['common']);
|
|
72
|
+
|
|
73
|
+
const targetModule = modules.find(module => module.key === actionSignalKey);
|
|
74
|
+
|
|
75
|
+
if (!targetModule) {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// The importFolderModulesOfType function already found the module, so we need to get its path
|
|
80
|
+
// We can do this by looking at the module's structure or by scanning the folder
|
|
81
|
+
const subfolders = fs.readdirSync(folderPath).filter(item => {
|
|
82
|
+
const itemPath = path.join(folderPath, item);
|
|
83
|
+
return fs.statSync(itemPath).isDirectory();
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
let modulePath = null;
|
|
87
|
+
let moduleDir = null;
|
|
88
|
+
|
|
89
|
+
// Look through each subfolder to find the one that contains our module
|
|
90
|
+
for (const subfolder of subfolders) {
|
|
91
|
+
const subfolderPath = path.join(folderPath, subfolder);
|
|
92
|
+
|
|
93
|
+
// Look for the module file in this subfolder
|
|
94
|
+
const possibleFiles = [
|
|
95
|
+
`${subfolder}.mjs`,
|
|
96
|
+
`${subfolder}.mts`,
|
|
97
|
+
`${subfolder}.js`,
|
|
98
|
+
`${subfolder}.ts`,
|
|
99
|
+
'index.mjs',
|
|
100
|
+
'index.mts',
|
|
101
|
+
'index.js',
|
|
102
|
+
'index.ts'
|
|
103
|
+
];
|
|
104
|
+
|
|
105
|
+
for (const file of possibleFiles) {
|
|
106
|
+
const filePath = path.join(subfolderPath, file);
|
|
107
|
+
if (fs.existsSync(filePath)) {
|
|
108
|
+
// Import this file to check if it's the right module
|
|
109
|
+
try {
|
|
110
|
+
const { module } = await dev_support.importFromPath(filePath);
|
|
111
|
+
|
|
112
|
+
// Check if this module has the right key
|
|
113
|
+
if (module.key === actionSignalKey) {
|
|
114
|
+
modulePath = filePath;
|
|
115
|
+
moduleDir = subfolderPath;
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
} catch (importError) {
|
|
119
|
+
// Continue to next file if import fails
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (modulePath) break;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (!modulePath) {
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Determine the UI directory
|
|
133
|
+
let uiDir = path.join(elementDir, 'ui');
|
|
134
|
+
if (targetModule && targetModule.ui) {
|
|
135
|
+
uiDir = path.join(elementDir, 'ui', targetModule.ui);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return { modulePath, uiDir };
|
|
139
|
+
} catch (error) {
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
useEffect(() => {
|
|
145
|
+
const loadElements = async () => {
|
|
146
|
+
try {
|
|
147
|
+
// First, check if the target directory itself is a valid element
|
|
148
|
+
try {
|
|
149
|
+
const elementType = await dev_support.autoDetectElement(rootDir);
|
|
150
|
+
if (elementType !== 'pipedream') { // If it's a valid element type
|
|
151
|
+
const elementInfo = await dev_support.loadElementPointers(rootDir, elementType);
|
|
152
|
+
setElements([{
|
|
153
|
+
label: `${elementInfo.name} (${elementInfo.elementType})`,
|
|
154
|
+
value: elementInfo.name,
|
|
155
|
+
info: elementInfo,
|
|
156
|
+
path: rootDir
|
|
157
|
+
}]);
|
|
158
|
+
setStatus('ready');
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
} catch (error) {
|
|
162
|
+
// Target directory is not a valid element, continue to scan subdirectories
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Scan for potential element directories/files
|
|
166
|
+
const entries = fs.readdirSync(rootDir, { withFileTypes: true });
|
|
167
|
+
const potentialElements: string[] = [];
|
|
168
|
+
|
|
169
|
+
// Look for directories only (elements are directories, not individual files)
|
|
170
|
+
for (const entry of entries) {
|
|
171
|
+
const entryPath = path.join(rootDir, entry.name);
|
|
172
|
+
|
|
173
|
+
if (entry.isDirectory()) {
|
|
174
|
+
// Check if directory contains element files
|
|
175
|
+
try {
|
|
176
|
+
const subEntries = fs.readdirSync(entryPath);
|
|
177
|
+
if (subEntries.some(file =>
|
|
178
|
+
file.endsWith('.mjs') ||
|
|
179
|
+
file.endsWith('.mts') ||
|
|
180
|
+
file.endsWith('.js') ||
|
|
181
|
+
file.endsWith('.ts') ||
|
|
182
|
+
file.endsWith('.tsx') ||
|
|
183
|
+
file.endsWith('.jsx')
|
|
184
|
+
)) {
|
|
185
|
+
potentialElements.push(entryPath);
|
|
186
|
+
}
|
|
187
|
+
} catch (error) {
|
|
188
|
+
// Skip directories we can't read
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
// Skip individual files - elements are always directories
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (potentialElements.length === 0) {
|
|
195
|
+
setStatus('no-elements');
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Try to load each potential element
|
|
200
|
+
const loadedElements: ElementItem[] = [];
|
|
201
|
+
|
|
202
|
+
for (const elementPath of potentialElements) {
|
|
203
|
+
try {
|
|
204
|
+
const elementInfo = await dev_support.loadElementPointers(elementPath, 'auto');
|
|
205
|
+
loadedElements.push({
|
|
206
|
+
label: `${elementInfo.name} (${elementInfo.elementType})`,
|
|
207
|
+
value: elementInfo.name,
|
|
208
|
+
info: elementInfo,
|
|
209
|
+
path: elementPath
|
|
210
|
+
});
|
|
211
|
+
} catch (error) {
|
|
212
|
+
// Skip elements that can't be loaded
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (loadedElements.length > 0) {
|
|
217
|
+
setElements(loadedElements);
|
|
218
|
+
setStatus('ready');
|
|
219
|
+
return;
|
|
220
|
+
} else {
|
|
221
|
+
setStatus('no-elements');
|
|
222
|
+
}
|
|
223
|
+
} catch (error) {
|
|
224
|
+
setStatus('error');
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
loadElements();
|
|
229
|
+
}, [rootDir]);
|
|
230
|
+
|
|
231
|
+
// React to viteUrl changes to show the banner
|
|
232
|
+
useEffect(() => {
|
|
233
|
+
if (viteUrl && status === 'launching') {
|
|
234
|
+
setStatus('dev-server-running');
|
|
235
|
+
}
|
|
236
|
+
}, [viteUrl, status]);
|
|
237
|
+
|
|
238
|
+
// Debug: log all status changes
|
|
239
|
+
useEffect(() => {
|
|
240
|
+
// console.log(`Status changed to: ${status}`);
|
|
241
|
+
}, [status]);
|
|
242
|
+
|
|
243
|
+
const launchDevServer = async (element: ElementItem, actionSignal: ActionSignalItem, property: PropertyItem | null) => {
|
|
244
|
+
if (!element) return;
|
|
245
|
+
|
|
246
|
+
// Get the actual module path and UI directory for the action/signal
|
|
247
|
+
const moduleInfo = await getModulePath(element.path, actionSignal.value, actionSignal.type);
|
|
248
|
+
|
|
249
|
+
if (!moduleInfo) {
|
|
250
|
+
setStatus('error');
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Try to find a dev directory or use the element's directory
|
|
255
|
+
const elementDir = fs.statSync(element.path).isDirectory()
|
|
256
|
+
? element.path
|
|
257
|
+
: path.dirname(element.path);
|
|
258
|
+
|
|
259
|
+
const devDir = path.join(elementDir, 'dev');
|
|
260
|
+
|
|
261
|
+
// Check if dev directory exists, otherwise use the element directory
|
|
262
|
+
const workingDir = fs.existsSync(devDir) ? devDir : elementDir;
|
|
263
|
+
|
|
264
|
+
// Use the internal Vite config from .process directory
|
|
265
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
266
|
+
const __dirname = path.dirname(__filename);
|
|
267
|
+
const internalViteConfig = path.resolve(__dirname, '../.process/vite.config.cjs');
|
|
268
|
+
|
|
269
|
+
// Check if user has their own vite config, otherwise use internal one
|
|
270
|
+
const userViteConfig = path.join(workingDir, 'vite.config.js');
|
|
271
|
+
const userViteConfigTs = path.join(workingDir, 'vite.config.ts');
|
|
272
|
+
|
|
273
|
+
let configToUse = internalViteConfig;
|
|
274
|
+
if (fs.existsSync(userViteConfig)) {
|
|
275
|
+
configToUse = userViteConfig;
|
|
276
|
+
} else if (fs.existsSync(userViteConfigTs)) {
|
|
277
|
+
configToUse = userViteConfigTs;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Load the complete element data from compatibility module
|
|
281
|
+
const elementModule = await dev_support.loadElementPointers(element.path, 'auto');
|
|
282
|
+
const currentActionSignal = (elementModule.actions as any[])?.find(action => action.key === actionSignal.value) ||
|
|
283
|
+
(elementModule.signals as any[])?.find(signal => signal.key === actionSignal.value);
|
|
284
|
+
|
|
285
|
+
// Set process.env variables for Vite config
|
|
286
|
+
process.env.VITE_ELEMENT_PATH = element.path;
|
|
287
|
+
process.env.VITE_ELEMENT_TYPE = actionSignal.type;
|
|
288
|
+
process.env.VITE_ELEMENT_NAME = element.info.name;
|
|
289
|
+
process.env.VITE_ACTION_SIGNAL_KEY = actionSignal.value;
|
|
290
|
+
process.env.VITE_PROPERTY_KEY = property?.propertyKey || '';
|
|
291
|
+
process.env.VITE_PROPERTY_TYPE = property?.type || '';
|
|
292
|
+
process.env.VITE_PROPERTY_UI_PATH = property?.uiPath || '';
|
|
293
|
+
process.env.VITE_MODULE_PATH = moduleInfo.modulePath;
|
|
294
|
+
process.env.VITE_UI_DIRECTORY = moduleInfo.uiDir;
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
const viteLogLength = 1;
|
|
298
|
+
// Create Vite server programmatically
|
|
299
|
+
try {
|
|
300
|
+
const vite = await createServer({
|
|
301
|
+
configFile: configToUse,
|
|
302
|
+
root: path.resolve(__dirname, '../.process'), // Use .process directory as root
|
|
303
|
+
optimizeDeps: {
|
|
304
|
+
include: ['react', 'react-dom'],
|
|
305
|
+
exclude: [element.path] // Exclude the external element from optimization
|
|
306
|
+
},
|
|
307
|
+
define: {
|
|
308
|
+
// Pass complete element data directly to Vite
|
|
309
|
+
'import.meta.env.VITE_ELEMENT_PATH': JSON.stringify(element.path),
|
|
310
|
+
'import.meta.env.VITE_ELEMENT_TYPE': JSON.stringify(actionSignal.type),
|
|
311
|
+
'import.meta.env.VITE_ELEMENT_NAME': JSON.stringify(element.info.name),
|
|
312
|
+
'import.meta.env.VITE_ACTION_SIGNAL_KEY': JSON.stringify(actionSignal.value),
|
|
313
|
+
'import.meta.env.VITE_PROPERTY_KEY': JSON.stringify(property?.propertyKey || null),
|
|
314
|
+
'import.meta.env.VITE_PROPERTY_TYPE': JSON.stringify(property?.type || null),
|
|
315
|
+
'import.meta.env.VITE_PROPERTY_UI_PATH': JSON.stringify(property?.uiPath || null),
|
|
316
|
+
// Add the actual module paths
|
|
317
|
+
'import.meta.env.VITE_MODULE_PATH': JSON.stringify(moduleInfo.modulePath),
|
|
318
|
+
'import.meta.env.VITE_UI_DIRECTORY': JSON.stringify(moduleInfo.uiDir),
|
|
319
|
+
// Pass the complete element data from compatibility module
|
|
320
|
+
'import.meta.env.VITE_ELEMENT_MODULE': JSON.stringify(elementModule),
|
|
321
|
+
'import.meta.env.VITE_CURRENT_ACTION_SIGNAL': JSON.stringify(currentActionSignal),
|
|
322
|
+
'import.meta.env.VITE_SELECTED_PROPERTY': JSON.stringify(property)
|
|
323
|
+
},
|
|
324
|
+
logLevel: 'info',
|
|
325
|
+
customLogger: {
|
|
326
|
+
info(msg) { viteLogsRef.current(logs => [...logs, msg].slice(-viteLogLength)); },
|
|
327
|
+
warn(msg) { viteLogsRef.current(logs => [...logs, `[warn] ${msg}`].slice(-viteLogLength)); },
|
|
328
|
+
error(msg) { viteLogsRef.current(logs => [...logs, `[error] ${msg}`].slice(-viteLogLength)); },
|
|
329
|
+
clearScreen() { },
|
|
330
|
+
hasWarned: false,
|
|
331
|
+
warnOnce(msg) { },
|
|
332
|
+
hasErrorLogged: () => false,
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
await vite.listen();
|
|
337
|
+
const serverUrl = `http://localhost:${vite.config.server?.port || 5173}`;
|
|
338
|
+
setViteUrl(serverUrl); // <-- set the state
|
|
339
|
+
setServerMessage(`Dev server running at ${serverUrl}`);
|
|
340
|
+
|
|
341
|
+
// Keep the server running and handle cleanup
|
|
342
|
+
const cleanup = () => {
|
|
343
|
+
vite.close();
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
// Handle process termination
|
|
347
|
+
process.on('SIGINT', cleanup);
|
|
348
|
+
process.on('SIGTERM', cleanup);
|
|
349
|
+
|
|
350
|
+
// Handle file changes
|
|
351
|
+
vite.watcher.on('change', (file: string) => {
|
|
352
|
+
// console.log(`File changed: ${file}`);
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
// Keep the process alive
|
|
356
|
+
setStatus('dev-server-running');
|
|
357
|
+
|
|
358
|
+
// Don't exit - keep the server running
|
|
359
|
+
process.stdin.resume();
|
|
360
|
+
|
|
361
|
+
} catch (error: any) {
|
|
362
|
+
setStatus('error');
|
|
363
|
+
}
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
const handleSelect = async (item: { value: string }) => {
|
|
367
|
+
if (step === 'elements') {
|
|
368
|
+
// First step: select an element
|
|
369
|
+
const element = elements.find(el => el.value === item.value);
|
|
370
|
+
if (!element) return;
|
|
371
|
+
|
|
372
|
+
setSelectedElement(element);
|
|
373
|
+
setStatus('loading-actions-signals');
|
|
374
|
+
|
|
375
|
+
try {
|
|
376
|
+
// Load the element to get its actions and signals
|
|
377
|
+
const elementModule = await dev_support.loadElementPointers(element.path, 'auto');
|
|
378
|
+
|
|
379
|
+
const items: ActionSignalItem[] = [];
|
|
380
|
+
|
|
381
|
+
// Add actions
|
|
382
|
+
if (elementModule.actions && Array.isArray(elementModule.actions)) {
|
|
383
|
+
elementModule.actions.forEach((action, index) => {
|
|
384
|
+
items.push({
|
|
385
|
+
label: `Action: ${action.name || action.key}`,
|
|
386
|
+
value: action.key,
|
|
387
|
+
type: 'action',
|
|
388
|
+
path: element.path,
|
|
389
|
+
description: action.description
|
|
390
|
+
});
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// Add signals
|
|
395
|
+
if (elementModule.signals && Array.isArray(elementModule.signals)) {
|
|
396
|
+
elementModule.signals.forEach((signal, index) => {
|
|
397
|
+
items.push({
|
|
398
|
+
label: `Signal: ${signal.name || signal.key}`,
|
|
399
|
+
value: signal.key,
|
|
400
|
+
type: 'signal',
|
|
401
|
+
path: element.path,
|
|
402
|
+
description: signal.description
|
|
403
|
+
});
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
if (items.length === 0) {
|
|
408
|
+
// If no actions/signals found, create a default item
|
|
409
|
+
items.push({
|
|
410
|
+
label: 'Default Component',
|
|
411
|
+
value: 'default',
|
|
412
|
+
type: 'action',
|
|
413
|
+
path: element.path,
|
|
414
|
+
description: 'Default component from element'
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
setActionsSignals(items);
|
|
419
|
+
setStep('actions-signals');
|
|
420
|
+
setStatus('ready');
|
|
421
|
+
} catch (error) {
|
|
422
|
+
setStatus('error');
|
|
423
|
+
}
|
|
424
|
+
} else if (step === 'actions-signals') {
|
|
425
|
+
// Second step: select an action/signal
|
|
426
|
+
const selectedActionSignal = actionsSignals.find(i => i.value === item.value);
|
|
427
|
+
if (!selectedActionSignal || !selectedElement) return;
|
|
428
|
+
|
|
429
|
+
setSelectedActionSignal(selectedActionSignal);
|
|
430
|
+
setStatus('loading-properties');
|
|
431
|
+
|
|
432
|
+
try {
|
|
433
|
+
// Load the element to get its properties
|
|
434
|
+
const elementModule = await dev_support.loadElementPointers(selectedElement.path, 'auto');
|
|
435
|
+
|
|
436
|
+
const currentActionSignal = (elementModule.actions as any[])?.find(action => action.key === selectedActionSignal.value) ||
|
|
437
|
+
(elementModule.signals as any[])?.find(signal => signal.key === selectedActionSignal.value);
|
|
438
|
+
|
|
439
|
+
// Check if the action/signal has its own UI
|
|
440
|
+
if (currentActionSignal && currentActionSignal.ui) {
|
|
441
|
+
setSelected(selectedActionSignal.value);
|
|
442
|
+
setStatus('launching');
|
|
443
|
+
// Launch server without awaiting - let it run in background
|
|
444
|
+
launchDevServer(selectedElement, selectedActionSignal, null).catch(error => {
|
|
445
|
+
setStatus('error');
|
|
446
|
+
});
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// No UI found, check for properties with UI
|
|
451
|
+
if (!currentActionSignal || !currentActionSignal.props) {
|
|
452
|
+
setSelected(selectedActionSignal.value);
|
|
453
|
+
setStatus('launching');
|
|
454
|
+
try {
|
|
455
|
+
await launchDevServer(selectedElement, selectedActionSignal, null);
|
|
456
|
+
} catch (error) {
|
|
457
|
+
setStatus('error');
|
|
458
|
+
}
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
const propertyItems: PropertyItem[] = [];
|
|
463
|
+
|
|
464
|
+
// Add properties that have UI components
|
|
465
|
+
// props is an array, not an object
|
|
466
|
+
currentActionSignal.props.forEach((prop: any) => {
|
|
467
|
+
if (prop.ui) {
|
|
468
|
+
// Property has a UI variant
|
|
469
|
+
propertyItems.push({
|
|
470
|
+
label: `🎨 ${prop.key} (${prop.ui})`,
|
|
471
|
+
value: `ui-${prop.key}`,
|
|
472
|
+
propertyKey: prop.key,
|
|
473
|
+
type: 'ui-variant',
|
|
474
|
+
uiPath: prop.ui,
|
|
475
|
+
description: prop.description,
|
|
476
|
+
propertyData: prop
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
if (propertyItems.length === 0) {
|
|
482
|
+
setSelected(selectedActionSignal.value);
|
|
483
|
+
setStatus('launching');
|
|
484
|
+
await launchDevServer(selectedElement, selectedActionSignal, null);
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
setProperties(propertyItems);
|
|
489
|
+
setStep('properties');
|
|
490
|
+
setStatus('ready');
|
|
491
|
+
} catch (error) {
|
|
492
|
+
setStatus('error');
|
|
493
|
+
}
|
|
494
|
+
} else {
|
|
495
|
+
// Third step: select a property (or launch directly)
|
|
496
|
+
const selectedProperty = properties.find(i => i.value === item.value);
|
|
497
|
+
if (!selectedProperty || !selectedActionSignal || !selectedElement) return;
|
|
498
|
+
|
|
499
|
+
setSelectedProperty(selectedProperty);
|
|
500
|
+
setSelected(selectedProperty.value);
|
|
501
|
+
setStatus('launching');
|
|
502
|
+
await launchDevServer(selectedElement, selectedActionSignal, selectedProperty);
|
|
503
|
+
}
|
|
504
|
+
};
|
|
505
|
+
|
|
506
|
+
if (status === 'loading') {
|
|
507
|
+
return (
|
|
508
|
+
<Box flexDirection="column" marginTop={2}>
|
|
509
|
+
<Text>🔍 Loading elements from: <Text color="cyan">{rootDir}</Text></Text>
|
|
510
|
+
<Text>Loading...</Text>
|
|
511
|
+
</Box>
|
|
512
|
+
);
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
if (status === 'no-elements') {
|
|
516
|
+
return (
|
|
517
|
+
<Box flexDirection="column" marginTop={2}>
|
|
518
|
+
<Text color="red">❌ No valid elements found in: <Text color="cyan">{rootDir}</Text></Text>
|
|
519
|
+
<Text color="yellow">Make sure the directory contains valid element modules.</Text>
|
|
520
|
+
<Text color="yellow">Supported formats: Pipedream, n8n, Doflo, Process.co</Text>
|
|
521
|
+
<Text color="yellow">Supported file types: .js, .ts, .mjs, .mts, .jsx, .tsx</Text>
|
|
522
|
+
</Box>
|
|
523
|
+
);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
if (status === 'error') {
|
|
527
|
+
return (
|
|
528
|
+
<Box flexDirection="column" marginTop={2}>
|
|
529
|
+
<Text color="red">❌ Error loading elements from: <Text color="cyan">{rootDir}</Text></Text>
|
|
530
|
+
<Text color="yellow">Check that the directory contains valid element modules.</Text>
|
|
531
|
+
</Box>
|
|
532
|
+
);
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
if (status === 'loading-actions-signals') {
|
|
536
|
+
return (
|
|
537
|
+
<Box flexDirection="column" marginTop={2}>
|
|
538
|
+
<Text>🔍 Loading actions and signals for '<Text color="cyan">{selectedElement?.info.name}</Text>'...</Text>
|
|
539
|
+
<Text>Loading...</Text>
|
|
540
|
+
</Box>
|
|
541
|
+
);
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
if (status === 'loading-properties') {
|
|
545
|
+
return (
|
|
546
|
+
<Box flexDirection="column" marginTop={2}>
|
|
547
|
+
<Text>🔍 Loading properties for '<Text color="cyan">{selectedActionSignal?.label}</Text>'...</Text>
|
|
548
|
+
<Text>Loading...</Text>
|
|
549
|
+
</Box>
|
|
550
|
+
);
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
if (status === 'launching') {
|
|
554
|
+
return (
|
|
555
|
+
<Box flexDirection="column" marginTop={2}>
|
|
556
|
+
<Text color="green">🚀 Launching dev server for '<Text color="cyan">{selected}</Text>'...</Text>
|
|
557
|
+
<Text>Directory: <Text color="cyan">{rootDir}</Text></Text>
|
|
558
|
+
</Box>
|
|
559
|
+
);
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
if (status === 'dev-server-running') {
|
|
563
|
+
return (
|
|
564
|
+
<>
|
|
565
|
+
<Box flexDirection="column" flexShrink={1} borderStyle={"round"} margin={2} marginBottom={1} paddingX={2} paddingY={1} borderColor="#8759F2" >
|
|
566
|
+
{/* <ViteBanner url={viteUrl} /> */}
|
|
567
|
+
{/* <Text color="green">✅ Dev server is running!</Text> */}
|
|
568
|
+
{/* {serverMessage && <Text>{serverMessage}</Text>} */}
|
|
569
|
+
<Text>Server Running: <Text color="#01D4E7" underline>{viteUrl}</Text></Text>
|
|
570
|
+
<Text>Element: <Text color="#01D4E7">{selectedElement?.info.name}</Text></Text>
|
|
571
|
+
<Text>Action/Signal: <Text color="#01D4E7">{selectedActionSignal?.label}</Text></Text>
|
|
572
|
+
{selectedProperty ? (
|
|
573
|
+
<Text>Property UI: <Text color="#01D4E7">{selectedProperty.label}</Text></Text>
|
|
574
|
+
) : (
|
|
575
|
+
<Text>UI Mode: <Text color="#01D4E7">Action/Signal Level</Text></Text>
|
|
576
|
+
)}
|
|
577
|
+
<Text color="yellow">Press Ctrl+C to stop the server</Text>
|
|
578
|
+
</Box>
|
|
579
|
+
<Box marginTop={1} marginLeft={4} flexDirection="column">
|
|
580
|
+
<Text color="gray">Server Status:</Text>
|
|
581
|
+
{viteLogs.length > 0 ? (
|
|
582
|
+
viteLogs.map((line, i) => (
|
|
583
|
+
<Text key={i} color="gray">{line}</Text>
|
|
584
|
+
))
|
|
585
|
+
) : (
|
|
586
|
+
<Text color="gray">Waiting for activity...</Text>
|
|
587
|
+
)}
|
|
588
|
+
</Box></>
|
|
589
|
+
|
|
590
|
+
);
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
if (step === 'properties') {
|
|
594
|
+
return (
|
|
595
|
+
<Box flexDirection="column">
|
|
596
|
+
<Text>📁 Element: <Text color="cyan">{selectedElement?.info.name}</Text></Text>
|
|
597
|
+
<Text>📋 Action/Signal: <Text color="#01D4E7">{selectedActionSignal?.label}</Text></Text>
|
|
598
|
+
<Text>Found {properties.length} property(ies):</Text>
|
|
599
|
+
<SelectInput items={properties} onSelect={handleSelect} />
|
|
600
|
+
</Box>
|
|
601
|
+
);
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
if (step === 'actions-signals') {
|
|
605
|
+
return (
|
|
606
|
+
<Box flexDirection="column" marginTop={2} marginBottom={2}>
|
|
607
|
+
{/* <Text>Status: {step}</Text> */}
|
|
608
|
+
<Text>📁 Element: <Text color="#01D4E7">{selectedElement?.info.name}</Text></Text>
|
|
609
|
+
<Box flexDirection="column" marginTop={1}>
|
|
610
|
+
<Text>Found {actionsSignals.length} action(s)/signal(s):</Text>
|
|
611
|
+
<SelectInput items={actionsSignals} onSelect={handleSelect} />
|
|
612
|
+
</Box>
|
|
613
|
+
</Box>
|
|
614
|
+
);
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
return (
|
|
618
|
+
<Box flexDirection="column" marginTop={2} marginBottom={2}>
|
|
619
|
+
{/* <ViteBanner url={viteUrl} /> */}
|
|
620
|
+
<Box flexDirection="column">
|
|
621
|
+
<Text>📁 Directory: <Text color="#01D4E7">{rootDir}</Text></Text>
|
|
622
|
+
<Box flexDirection="column" marginTop={1}>
|
|
623
|
+
<Text>Found {elements.length} element(s):</Text>
|
|
624
|
+
<SelectInput items={elements} onSelect={handleSelect} /></Box>
|
|
625
|
+
</Box>
|
|
626
|
+
<ViteLogPanel logs={viteLogs} />
|
|
627
|
+
</Box>
|
|
628
|
+
);
|
|
629
|
+
};
|
|
630
|
+
|
|
631
|
+
// Vite log panel
|
|
632
|
+
const ViteLogPanel = ({ logs }: { logs: string[] }) => {
|
|
633
|
+
if (!logs.length) return null;
|
|
634
|
+
return (
|
|
635
|
+
<Box flexDirection="column" marginTop={1}>
|
|
636
|
+
<Text color="gray">Output:</Text>
|
|
637
|
+
{logs.map((line, i) => (
|
|
638
|
+
<Text key={i} color="gray">{line}</Text>
|
|
639
|
+
))}
|
|
640
|
+
</Box>
|
|
641
|
+
);
|
|
642
|
+
};
|
|
643
|
+
|
|
644
|
+
// Vite banner component
|
|
645
|
+
const ViteBanner = ({ url }: { url: string | null }) =>
|
|
646
|
+
url ? (
|
|
647
|
+
<Box marginBottom={1} justifyContent="center" borderStyle="double" borderColor="#8759F2" marginX={10}>
|
|
648
|
+
<Text color="green">🚀 Process Element Server running at: </Text>
|
|
649
|
+
<Text color="cyan" underline>{url}</Text>
|
|
650
|
+
</Box>
|
|
651
|
+
) : null;
|
package/src/version.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":"0.0.0-development"}
|