@lmjs/cli 2.5.9 → 2.6.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/LICENSE CHANGED
@@ -1,201 +1,21 @@
1
- Apache License
2
- Version 2.0, January 2004
3
- http://www.apache.org/licenses/
4
-
5
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
-
7
- 1. Definitions.
8
-
9
- "License" shall mean the terms and conditions for use, reproduction,
10
- and distribution as defined by Sections 1 through 9 of this document.
11
-
12
- "Licensor" shall mean the copyright owner or entity authorized by
13
- the copyright owner that is granting the License.
14
-
15
- "Legal Entity" shall mean the union of the acting entity and all
16
- other entities that control, are controlled by, or are under common
17
- control with that entity. For the purposes of this definition,
18
- "control" means (i) the power, direct or indirect, to cause the
19
- direction or management of such entity, whether by contract or
20
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
- outstanding shares, or (iii) beneficial ownership of such entity.
22
-
23
- "You" (or "Your") shall mean an individual or Legal Entity
24
- exercising permissions granted by this License.
25
-
26
- "Source" form shall mean the preferred form for making modifications,
27
- including but not limited to software source code, documentation
28
- source, and configuration files.
29
-
30
- "Object" form shall mean any form resulting from mechanical
31
- transformation or translation of a Source form, including but
32
- not limited to compiled object code, generated documentation,
33
- and conversions to other media types.
34
-
35
- "Work" shall mean the work of authorship, whether in Source or
36
- Object form, made available under the License, as indicated by a
37
- copyright notice that is included in or attached to the work
38
- (an example is provided in the Appendix below).
39
-
40
- "Derivative Works" shall mean any work, whether in Source or Object
41
- form, that is based on (or derived from) the Work and for which the
42
- editorial revisions, annotations, elaborations, or other modifications
43
- represent, as a whole, an original work of authorship. For the purposes
44
- of this License, Derivative Works shall not include works that remain
45
- separable from, or merely link (or bind by name) to the interfaces of,
46
- the Work and Derivative Works thereof.
47
-
48
- "Contribution" shall mean any work of authorship, including
49
- the original version of the Work and any modifications or additions
50
- to that Work or Derivative Works thereof, that is intentionally
51
- submitted to Licensor for inclusion in the Work by the copyright owner
52
- or by an individual or Legal Entity authorized to submit on behalf of
53
- the copyright owner. For the purposes of this definition, "submitted"
54
- means any form of electronic, verbal, or written communication sent
55
- to the Licensor or its representatives, including but not limited to
56
- communication on electronic mailing lists, source code control systems,
57
- and issue tracking systems that are managed by, or on behalf of, the
58
- Licensor for the purpose of discussing and improving the Work, but
59
- excluding communication that is conspicuously marked or otherwise
60
- designated in writing by the copyright owner as "Not a Contribution."
61
-
62
- "Contributor" shall mean Licensor and any individual or Legal Entity
63
- on behalf of whom a Contribution has been received by Licensor and
64
- subsequently incorporated within the Work.
65
-
66
- 2. Grant of Copyright License. Subject to the terms and conditions of
67
- this License, each Contributor hereby grants to You a perpetual,
68
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
- copyright license to reproduce, prepare Derivative Works of,
70
- publicly display, publicly perform, sublicense, and distribute the
71
- Work and such Derivative Works in Source or Object form.
72
-
73
- 3. Grant of Patent License. Subject to the terms and conditions of
74
- this License, each Contributor hereby grants to You a perpetual,
75
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
- (except as stated in this section) patent license to make, have made,
77
- use, offer to sell, sell, import, and otherwise transfer the Work,
78
- where such license applies only to those patent claims licensable
79
- by such Contributor that are necessarily infringed by their
80
- Contribution(s) alone or by combination of their Contribution(s)
81
- with the Work to which such Contribution(s) was submitted. If You
82
- institute patent litigation against any entity (including a
83
- cross-claim or counterclaim in a lawsuit) alleging that the Work
84
- or a Contribution incorporated within the Work constitutes direct
85
- or contributory patent infringement, then any patent licenses
86
- granted to You under this License for that Work shall terminate
87
- as of the date such litigation is filed.
88
-
89
- 4. Redistribution. You may reproduce and distribute copies of the
90
- Work or Derivative Works thereof in any medium, with or without
91
- modifications, and in Source or Object form, provided that You
92
- meet the following conditions:
93
-
94
- (a) You must give any other recipients of the Work or
95
- Derivative Works a copy of this License; and
96
-
97
- (b) You must cause any modified files to carry prominent notices
98
- stating that You changed the files; and
99
-
100
- (c) You must retain, in the Source form of any Derivative Works
101
- that You distribute, all copyright, patent, trademark, and
102
- attribution notices from the Source form of the Work,
103
- excluding those notices that do not pertain to any part of
104
- the Derivative Works; and
105
-
106
- (d) If the Work includes a "NOTICE" text file as part of its
107
- distribution, then any Derivative Works that You distribute must
108
- include a readable copy of the attribution notices contained
109
- within such NOTICE file, excluding those notices that do not
110
- pertain to any part of the Derivative Works, in at least one
111
- of the following places: within a NOTICE text file distributed
112
- as part of the Derivative Works; within the Source form or
113
- documentation, if provided along with the Derivative Works; or,
114
- within a display generated by the Derivative Works, if and
115
- wherever such third-party notices normally appear. The contents
116
- of the NOTICE file are for informational purposes only and
117
- do not modify the License. You may add Your own attribution
118
- notices within Derivative Works that You distribute, alongside
119
- or as an addendum to the NOTICE text from the Work, provided
120
- that such additional attribution notices cannot be construed
121
- as modifying the License.
122
-
123
- You may add Your own copyright statement to Your modifications and
124
- may provide additional or different license terms and conditions
125
- for use, reproduction, or distribution of Your modifications, or
126
- for any such Derivative Works as a whole, provided Your use,
127
- reproduction, and distribution of the Work otherwise complies with
128
- the conditions stated in this License.
129
-
130
- 5. Submission of Contributions. Unless You explicitly state otherwise,
131
- any Contribution intentionally submitted for inclusion in the Work
132
- by You to the Licensor shall be under the terms and conditions of
133
- this License, without any additional terms or conditions.
134
- Notwithstanding the above, nothing herein shall supersede or modify
135
- the terms of any separate license agreement you may have executed
136
- with Licensor regarding such Contributions.
137
-
138
- 6. Trademarks. This License does not grant permission to use the trade
139
- names, trademarks, service marks, or product names of the Licensor,
140
- except as required for reasonable and customary use in describing the
141
- origin of the Work and reproducing the content of the NOTICE file.
142
-
143
- 7. Disclaimer of Warranty. Unless required by applicable law or
144
- agreed to in writing, Licensor provides the Work (and each
145
- Contributor provides its Contributions) on an "AS IS" BASIS,
146
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
- implied, including, without limitation, any warranties or conditions
148
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
- PARTICULAR PURPOSE. You are solely responsible for determining the
150
- appropriateness of using or redistributing the Work and assume any
151
- risks associated with Your exercise of permissions under this License.
152
-
153
- 8. Limitation of Liability. In no event and under no legal theory,
154
- whether in tort (including negligence), contract, or otherwise,
155
- unless required by applicable law (such as deliberate and grossly
156
- negligent acts) or agreed to in writing, shall any Contributor be
157
- liable to You for damages, including any direct, indirect, special,
158
- incidental, or consequential damages of any character arising as a
159
- result of this License or out of the use or inability to use the
160
- Work (including but not limited to damages for loss of goodwill,
161
- work stoppage, computer failure or malfunction, or any and all
162
- other commercial damages or losses), even if such Contributor
163
- has been advised of the possibility of such damages.
164
-
165
- 9. Accepting Warranty or Additional Liability. While redistributing
166
- the Work or Derivative Works thereof, You may choose to offer,
167
- and charge a fee for, acceptance of support, warranty, indemnity,
168
- or other liability obligations and/or rights consistent with this
169
- License. However, in accepting such obligations, You may act only
170
- on Your own behalf and on Your sole responsibility, not on behalf
171
- of any other Contributor, and only if You agree to indemnify,
172
- defend, and hold each Contributor harmless for any liability
173
- incurred by, or claims asserted against, such Contributor by reason
174
- of your accepting any such warranty or additional liability.
175
-
176
- END OF TERMS AND CONDITIONS
177
-
178
- APPENDIX: How to apply the Apache License to your work.
179
-
180
- To apply the Apache License to your work, attach the following
181
- boilerplate notice, with the fields enclosed by brackets "[]"
182
- replaced with your own identifying information. (Don't include
183
- the brackets!) The text should be enclosed in the appropriate
184
- comment syntax for the file format. We also recommend that a
185
- file or class name and description of purpose be included on the
186
- same "printed page" as the copyright notice for easier
187
- identification within third-party archives.
188
-
189
- Copyright [yyyy] [name of copyright owner]
190
-
191
- Licensed under the Apache License, Version 2.0 (the "License");
192
- you may not use this file except in compliance with the License.
193
- You may obtain a copy of the License at
194
-
195
- http://www.apache.org/licenses/LICENSE-2.0
196
-
197
- Unless required by applicable law or agreed to in writing, software
198
- distributed under the License is distributed on an "AS IS" BASIS,
199
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
- See the License for the specific language governing permissions and
201
- limitations under the License.
1
+ MIT License
2
+
3
+ Copyright (c) 2025-2026 BEA Technologies
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/dist/lib/app.js CHANGED
@@ -1,2 +1,2 @@
1
- var fs=require("fs");const path=require("path");const chokidar=require("chokidar");const express=require("express");var compression=require("compression");const app=express();const server=require("http").createServer(app);var WebSocketServer=require("ws").Server;var wss=new WebSocketServer({server,path:"/"});const fcs=require("./fcs.js");var cl=console.log;var cookieParser=require("cookie-parser");var expressSession=require("express-session");const bodyParser=require("body-parser");var multer=require("multer");var upload=multer();let session=expressSession({secret:"keyboardcat",resave:true,saveUninitialized:true,cookie:{maxAge:9999e3}});var config=fcs.getConfig();var _views={};var _layouts={};var _tpls={};function readTpls(){return fcs.readTemplates(`${process.cwd()}`)}function readLayouts(){let dir=`${process.cwd()}`+path.sep+`src`+path.sep+`layouts`+path.sep;return fs.existsSync(dir)?fcs.linkTemplateReactiveVars(fcs.annotateLayoutRegions(fcs.getAllFiles(dir,{},false,true)),_tpls):{}}function readViews(){let dir=`${process.cwd()}`+path.sep+`src`+path.sep+`views`+path.sep;return fs.existsSync(dir)?fcs.linkTemplateReactiveVars(fcs.getAllFiles(dir,{},false,true),_tpls):{}}var _compiledScripts={};(function buildCompiledScriptsMap(){try{var projectRoot=`${process.cwd()}`;var srcRoot=projectRoot+path.sep+`src`;var hasOwnDeps=fcs.hasOwnDependencies(projectRoot);var indexJsPath=srcRoot+path.sep+"index.js";if(fs.existsSync(indexJsPath)){var indexJsSrc=hasOwnDeps?fcs.buildEntryWithImports(indexJsPath,projectRoot):fs.readFileSync(indexJsPath,"utf8");_compiledScripts["/index.js"]=fcs.compileProjectScript(indexJsSrc,"index")}var indexHtmlPath=srcRoot+path.sep+"index.html";if(fs.existsSync(indexHtmlPath)){var localSrcs=fcs.detectLocalProjectScripts(fs.readFileSync(indexHtmlPath,"utf8"));for(const src of localSrcs){var scriptPath=srcRoot+src.split("/").join(path.sep);if(fs.existsSync(scriptPath)){var scriptSrc=hasOwnDeps?fcs.buildEntryWithImports(scriptPath,projectRoot):fs.readFileSync(scriptPath,"utf8");_compiledScripts[src]=fcs.compileProjectScript(scriptSrc,src)}}}}catch(e){cl("Failed to compile index.js / project scripts for reactivity \u2014 serving them raw. "+e.message)}})();var _pluginsJs=null;var _pluginsCss=null;var _pluginsBuildPromise=null;_pluginsBuildPromise=(async function buildSelectedPluginsBundle(){try{var projectRoot=`${process.cwd()}`;var selected=fcs.selectedProjectPlugins(projectRoot);if(!selected.length)return;var pluginsEsbuild=require(path.join(__dirname,"plugins-esbuild-plugin.js"));_pluginsJs=await pluginsEsbuild.buildSelectedPluginsBundle(selected,projectRoot);_pluginsCss=await pluginsEsbuild.buildSelectedCss(selected,projectRoot+path.sep+"node_modules")}catch(e){cl("Failed to build the selective plugins bundle \u2014 "+e.message)}})();app.use(compression());app.use(cookieParser());app.use(session);async function sendIndexHtml(res){let file=`${process.cwd()}`+path.sep+`src`+path.sep+`index.html`;let html=fcs.v1CompatIndexHtml(fs.readFileSync(file,"utf8"));if(_pluginsBuildPromise){try{await _pluginsBuildPromise}catch(e){}}let tags="";if(_pluginsJs!=null&&html.indexOf("/js/lumenjs-plugins.js")==-1){tags+=`
2
- <script src="/js/lumenjs-plugins.js" type="text/javascript"><\/script>`}if(tags)html=html.split("</head>").join(tags+"\n</head>");if(_pluginsCss&&_pluginsCss.trim()!=""&&html.indexOf("/css/lumenjs-plugins.css")==-1){html=fcs.placePluginsCss(html,`<link href="/css/lumenjs-plugins.css" rel="stylesheet" type="text/css" />`)}res.type("text/html").send(html)}app.use("/",async function(req,res,next){if(req.path==="/"||req.path==="/index.html"){sendIndexHtml(res);return}if(req.path==="/js/lm.js"&&!fs.existsSync(`${process.cwd()}`+path.sep+`src`+path.sep+`js`+path.sep+`lm.js`)){res.type("application/javascript").sendFile(path.join(__dirname,"files","libs","lm.js"));return}if(Object.prototype.hasOwnProperty.call(_compiledScripts,req.path)){res.type("application/javascript").send(_compiledScripts[req.path]);return}if(req.path==="/js/lumenjs-plugins.js"||req.path==="/css/lumenjs-plugins.css"){if(_pluginsBuildPromise){try{await _pluginsBuildPromise}catch(e){}}if(req.path==="/js/lumenjs-plugins.js"&&_pluginsJs!=null){res.type("application/javascript").send(_pluginsJs);return}if(req.path==="/css/lumenjs-plugins.css"&&_pluginsCss!=null){res.type("text/css").send(_pluginsCss);return}}next()});app.use("/",express.static(`${process.cwd()}`+path.sep+`src`));app.set("etag",false);app.use(bodyParser.urlencoded({extended:true}));app.use(bodyParser.json());app.use(bodyParser.raw());app.use(upload.array());wss.on("connection",ws=>{ws.on("message",async message=>{try{const buffer=Buffer.from(message);const data=JSON.parse(buffer.toString());if(data.hasOwnProperty("e")){var _data={"e":data.e,"_iter":data._iter,"payload":{}};if(data.e=="getfiles"){_data["payload"]={"hstFormatVersion":fcs.HST_FORMAT_VERSION,"views":_views,"layouts":_layouts,"tpls":_tpls}}else if(data.e=="getlayout"){_data.payload=_layouts[data.payload]}else if(data.e=="getview"){_data.payload=_views[data.payload]}else if(data.e=="gettpl"){_data.payload=_tpls[data.payload]}else if(data.e=="request"){try{let pata=data.payload;let __data=pata["data"];let _body=pata["body"];var resa=await API.req(_body["path"],__data,_body["headers"],_body["method"],_body["api"],true);let res=await resa.text();try{res=JSON.parse(res)}catch(e){}_data.payload=_body["full"]?{"headers":resa.headers,"data":res}:res;ws.send(JSON.stringify(_data))}catch(e){_data.payload={"code":108,"error":e.message,"type":"RequestError"};ws.send(JSON.stringify(_data))}return}ws.send(JSON.stringify(_data))}}catch(e){}})}).on("error",e=>{});global.app=app;var fetch=fcs.fetch;global.fetch=fetch;global.wss=wss;global.fs=fs;global.path=path;var API=fcs.API;global.API=API;if(fs.existsSync(`${process.cwd()}`+path.sep+`src`+path.sep+`app.js`)){require(`${process.cwd()}`+path.sep+`src`+path.sep+`app.js`)}app.get("*",function(req,res){let nodes=req.params[0].split("/");if(nodes.length)nodes.shift();if(fs.existsSync(`${process.cwd()}`+path.sep+`src`+path.sep+`views`+path.sep+nodes[0]+`.view`))sendIndexHtml(res);else res.sendFile(`${process.cwd()}`+path.sep+`src`+path.sep+`404.html`);return});app.post("*",function(req,res){let nodes=req.params[0].split("/");if(nodes.length)nodes.shift();if(fs.existsSync(`${process.cwd()}`+path.sep+`src`+path.sep+`views`+path.sep+nodes[0]+`.view`))sendIndexHtml(res);else res.sendFile(`${process.cwd()}`+path.sep+`src`+path.sep+`404.html`);return});function broadcastFilesChanged(){wss.clients.forEach(function(client){if(client.readyState===1){client.send(JSON.stringify({e:"files-changed"}))}})}chokidar.watch(`${process.cwd()}`+path.sep+`src`+path.sep+`views`,{ignoreInitial:true}).on("all",function(){_views=readViews();broadcastFilesChanged()});chokidar.watch(`${process.cwd()}`+path.sep+`src`+path.sep+`layouts`,{ignoreInitial:true}).on("all",function(){_layouts=readLayouts();broadcastFilesChanged()});chokidar.watch([`${process.cwd()}`+path.sep+`src`+path.sep+`tpls`,`${process.cwd()}`+path.sep+`src`+path.sep+`comps`],{ignoreInitial:true}).on("all",function(){_tpls=readTpls();_views=readViews();_layouts=readLayouts();broadcastFilesChanged()});var _lmBasePort=Number(config.server.port)>0?Number(config.server.port):0;var _lmOriginalPortRetries=0;var _LM_MAX_ORIGINAL_PORT_RETRIES=10;var _lmFallbackAttempt=0;var _LM_MAX_FALLBACK_ATTEMPTS=20;function lmStartListening(port){server.listen(port)}server.on("listening",function(){cl(`LumenJS server running at http://${config.server.host}:${server.address().port}/`);fcs.checkProjectRuntime(process.cwd());config.server.port=server.address().port;_tpls=readTpls();_views=readViews();_layouts=readLayouts()});server.on("error",e=>{if(e.code!=="EADDRINUSE")throw e;if(_lmBasePort>0&&_lmOriginalPortRetries<_LM_MAX_ORIGINAL_PORT_RETRIES){_lmOriginalPortRetries++;cl("Port "+_lmBasePort+" is in use (likely still being released by a previous run) \u2014 retrying ("+_lmOriginalPortRetries+"/"+_LM_MAX_ORIGINAL_PORT_RETRIES+")...");setTimeout(()=>lmStartListening(_lmBasePort),300);return}if(_lmFallbackAttempt<_LM_MAX_FALLBACK_ATTEMPTS){_lmFallbackAttempt++;let fallbackPort=(_lmBasePort>0?_lmBasePort:3e3)+_lmFallbackAttempt;cl("Port "+_lmBasePort+" is already in use \u2014 falling back to "+fallbackPort+"..");lmStartListening(fallbackPort);return}cl("Could not find a free port after "+_LM_MAX_FALLBACK_ATTEMPTS+" attempts starting from "+_lmBasePort+".")});lmStartListening(_lmBasePort);
1
+ var fs=require("fs");const path=require("path");const chokidar=require("chokidar");const express=require("express");var compression=require("compression");const app=express();const server=require("http").createServer(app);var WebSocketServer=require("ws").Server;var wss=new WebSocketServer({server,path:"/"});const fcs=require("./fcs.js");var cl=console.log;var cookieParser=require("cookie-parser");var expressSession=require("express-session");const bodyParser=require("body-parser");var multer=require("multer");var upload=multer();let session=expressSession({secret:"keyboardcat",resave:true,saveUninitialized:true,cookie:{maxAge:9999e3}});var config=fcs.getConfig();var _views={};var _layouts={};var _tpls={};function readTpls(){return fcs.readTemplates(`${process.cwd()}`)}function readLayouts(){let dir=`${process.cwd()}`+path.sep+`src`+path.sep+`layouts`+path.sep;return fs.existsSync(dir)?fcs.linkTemplateReactiveVars(fcs.annotateLayoutRegions(fcs.getAllFiles(dir,{},false,true)),_tpls):{}}function readViews(){let dir=`${process.cwd()}`+path.sep+`src`+path.sep+`views`+path.sep;return fs.existsSync(dir)?fcs.linkTemplateReactiveVars(fcs.getAllFiles(dir,{},false,true),_tpls):{}}var _lmServerErrors=[];var _compiledScripts={};(function buildCompiledScriptsMap(){try{var projectRoot=`${process.cwd()}`;var srcRoot=projectRoot+path.sep+`src`;var hasOwnDeps=fcs.hasOwnDependencies(projectRoot);var indexJsPath=srcRoot+path.sep+"index.js";if(fs.existsSync(indexJsPath)){var indexJsSrc=hasOwnDeps?fcs.buildEntryWithImports(indexJsPath,projectRoot):fs.readFileSync(indexJsPath,"utf8");_compiledScripts["/index.js"]=fcs.compileProjectScript(indexJsSrc,"index")}var indexHtmlPath=srcRoot+path.sep+"index.html";if(fs.existsSync(indexHtmlPath)){var localSrcs=fcs.detectLocalProjectScripts(fs.readFileSync(indexHtmlPath,"utf8"));for(const src of localSrcs){var scriptPath=srcRoot+src.split("/").join(path.sep);if(fs.existsSync(scriptPath)){var scriptSrc=hasOwnDeps?fcs.buildEntryWithImports(scriptPath,projectRoot):fs.readFileSync(scriptPath,"utf8");_compiledScripts[src]=fcs.compileProjectScript(scriptSrc,src)}}}}catch(e){cl("Failed to compile index.js / project scripts for reactivity \u2014 serving them raw. "+e.message);_lmServerErrors.push("Failed to compile index.js / project scripts: "+e.message)}})();var _pluginsJs=null;var _pluginsCss=null;var _pluginsBuildPromise=null;_pluginsBuildPromise=(async function buildSelectedPluginsBundle(){try{var projectRoot=`${process.cwd()}`;var selected=fcs.selectedProjectPlugins(projectRoot);if(!selected.length)return;var pluginsEsbuild=require(path.join(__dirname,"plugins-esbuild-plugin.js"));_pluginsJs=await pluginsEsbuild.buildSelectedPluginsBundle(selected,projectRoot);_pluginsCss=await pluginsEsbuild.buildSelectedCss(selected,projectRoot+path.sep+"node_modules")}catch(e){cl("Failed to build the selective plugins bundle \u2014 "+e.message)}})();app.use(compression());app.use(cookieParser());app.use(session);var _copilot=null;try{const _copilotServer=require(path.join(__dirname,"copilot","server.js"));if(_copilotServer.isEnabled(config)){_copilot=_copilotServer.mount(app,{root:`${process.cwd()}`,token:process.env.LM_COPILOT_TOKEN||require("crypto").randomBytes(24).toString("hex"),getPort:function(){var a=server.address();return a?a.port:0}})}}catch(e){cl("Lumen Copilot is off: "+e.message);_copilot=null}async function sendIndexHtml(res,req){let file=`${process.cwd()}`+path.sep+`src`+path.sep+`index.html`;let html=fcs.v1CompatIndexHtml(fs.readFileSync(file,"utf8"));if(_pluginsBuildPromise){try{await _pluginsBuildPromise}catch(e){}}let tags="";if(_pluginsJs!=null&&html.indexOf("/js/lumenjs-plugins.js")==-1){tags+=`
2
+ <script src="/js/lumenjs-plugins.js" type="text/javascript"><\/script>`}if(tags)html=html.split("</head>").join(tags+"\n</head>");if(_pluginsCss&&_pluginsCss.trim()!=""&&html.indexOf("/css/lumenjs-plugins.css")==-1){html=fcs.placePluginsCss(html,`<link href="/css/lumenjs-plugins.css" rel="stylesheet" type="text/css" />`)}if(_copilot)html=_copilot.inject(html,req);res.type("text/html").send(html)}app.use("/",async function(req,res,next){if(req.path==="/"||req.path==="/index.html"){sendIndexHtml(res,req);return}if(req.path==="/js/lm.js"&&!fs.existsSync(`${process.cwd()}`+path.sep+`src`+path.sep+`js`+path.sep+`lm.js`)){res.type("application/javascript").sendFile(path.join(__dirname,"files","libs","lm.js"));return}if(Object.prototype.hasOwnProperty.call(_compiledScripts,req.path)){res.type("application/javascript").send(_compiledScripts[req.path]);return}if(req.path==="/js/lumenjs-plugins.js"||req.path==="/css/lumenjs-plugins.css"){if(_pluginsBuildPromise){try{await _pluginsBuildPromise}catch(e){}}if(req.path==="/js/lumenjs-plugins.js"&&_pluginsJs!=null){res.type("application/javascript").send(_pluginsJs);return}if(req.path==="/css/lumenjs-plugins.css"&&_pluginsCss!=null){res.type("text/css").send(_pluginsCss);return}}next()});app.use("/",express.static(`${process.cwd()}`+path.sep+`src`));app.set("etag",false);app.use(bodyParser.urlencoded({extended:true}));app.use(bodyParser.json());app.use(bodyParser.raw());app.use(upload.array());wss.on("connection",ws=>{ws.on("message",async message=>{try{const buffer=Buffer.from(message);const data=JSON.parse(buffer.toString());if(data.hasOwnProperty("e")){var _data={"e":data.e,"_iter":data._iter,"payload":{}};if(data.e=="getfiles"){_data["payload"]={"hstFormatVersion":fcs.HST_FORMAT_VERSION,"views":_views,"layouts":_layouts,"tpls":_tpls}}else if(data.e=="getlayout"){_data.payload=_layouts[data.payload]}else if(data.e=="getview"){_data.payload=_views[data.payload]}else if(data.e=="gettpl"){_data.payload=_tpls[data.payload]}else if(data.e=="request"){try{let pata=data.payload;let __data=pata["data"];let _body=pata["body"];var resa=await API.req(_body["path"],__data,_body["headers"],_body["method"],_body["api"],true);let res=await resa.text();try{res=JSON.parse(res)}catch(e){}_data.payload=_body["full"]?{"headers":resa.headers,"data":res}:res;ws.send(JSON.stringify(_data))}catch(e){_data.payload={"code":108,"error":e.message,"type":"RequestError"};ws.send(JSON.stringify(_data))}return}ws.send(JSON.stringify(_data))}}catch(e){}})}).on("error",e=>{});global.app=app;var fetch=fcs.fetch;global.fetch=fetch;global.wss=wss;global.fs=fs;global.path=path;var API=fcs.API;global.API=API;if(fs.existsSync(`${process.cwd()}`+path.sep+`src`+path.sep+`app.js`)){require(`${process.cwd()}`+path.sep+`src`+path.sep+`app.js`)}app.get("*",function(req,res){let nodes=req.params[0].split("/");if(nodes.length)nodes.shift();if(fs.existsSync(`${process.cwd()}`+path.sep+`src`+path.sep+`views`+path.sep+nodes[0]+`.view`))sendIndexHtml(res,req);else res.sendFile(`${process.cwd()}`+path.sep+`src`+path.sep+`404.html`);return});app.post("*",function(req,res){let nodes=req.params[0].split("/");if(nodes.length)nodes.shift();if(fs.existsSync(`${process.cwd()}`+path.sep+`src`+path.sep+`views`+path.sep+nodes[0]+`.view`))sendIndexHtml(res,req);else res.sendFile(`${process.cwd()}`+path.sep+`src`+path.sep+`404.html`);return});function broadcastFilesChanged(){wss.clients.forEach(function(client){if(client.readyState===1){client.send(JSON.stringify({e:"files-changed"}))}})}chokidar.watch(`${process.cwd()}`+path.sep+`src`+path.sep+`views`,{ignoreInitial:true}).on("all",function(){_views=readViews();broadcastFilesChanged()});chokidar.watch(`${process.cwd()}`+path.sep+`src`+path.sep+`layouts`,{ignoreInitial:true}).on("all",function(){_layouts=readLayouts();broadcastFilesChanged()});chokidar.watch([`${process.cwd()}`+path.sep+`src`+path.sep+`tpls`,`${process.cwd()}`+path.sep+`src`+path.sep+`comps`],{ignoreInitial:true}).on("all",function(){_tpls=readTpls();_views=readViews();_layouts=readLayouts();broadcastFilesChanged()});var _lmBasePort=Number(config.server.port)>0?Number(config.server.port):0;var _lmOriginalPortRetries=0;var _LM_MAX_ORIGINAL_PORT_RETRIES=10;var _lmFallbackAttempt=0;var _LM_MAX_FALLBACK_ATTEMPTS=20;function lmStartListening(port){server.listen(port)}server.on("listening",function(){cl(`LumenJS server running at http://${config.server.host}:${server.address().port}/`);if(_copilot){_lmServerErrors.splice(0).forEach(function(m){_copilot.noteServerError(m)});if(!require(path.join(__dirname,"copilot","auth.js")).hasCredentials()){cl("Lumen Copilot (AI help on this page): run `lm login` to turn it on. New Roxyon accounts get $0.50 of free credit.")}}fcs.checkProjectRuntime(process.cwd());config.server.port=server.address().port;_tpls=readTpls();_views=readViews();_layouts=readLayouts()});server.on("error",e=>{if(e.code!=="EADDRINUSE")throw e;if(_lmBasePort>0&&_lmOriginalPortRetries<_LM_MAX_ORIGINAL_PORT_RETRIES){_lmOriginalPortRetries++;cl("Port "+_lmBasePort+" is in use (likely still being released by a previous run) \u2014 retrying ("+_lmOriginalPortRetries+"/"+_LM_MAX_ORIGINAL_PORT_RETRIES+")...");setTimeout(()=>lmStartListening(_lmBasePort),300);return}if(_lmFallbackAttempt<_LM_MAX_FALLBACK_ATTEMPTS){_lmFallbackAttempt++;let fallbackPort=(_lmBasePort>0?_lmBasePort:3e3)+_lmFallbackAttempt;cl("Port "+_lmBasePort+" is already in use \u2014 falling back to "+fallbackPort+"..");lmStartListening(fallbackPort);return}cl("Could not find a free port after "+_LM_MAX_FALLBACK_ATTEMPTS+" attempts starting from "+_lmBasePort+".")});lmStartListening(_lmBasePort);
@@ -0,0 +1 @@
1
+ const fs=require("fs");const os=require("os");const path=require("path");const http=require("http");const crypto=require("crypto");const{spawn}=require("child_process");const fetch=require("node-fetch");const DEFAULT_CONSOLE_URL="https://console.roxyon.com";const SCOPE="read deploy copilot";const CLIENT_NAME="LumenJS CLI (lm)";const LOGIN_TIMEOUT_MS=5*60*1e3;function consoleUrl(){return(process.env.ROXYON_CONSOLE_URL||DEFAULT_CONSOLE_URL).replace(/\/+$/,"")}function oauthResource(){return process.env.ROXYON_OAUTH_RESOURCE||DEFAULT_CONSOLE_URL}function credentialsPath(){if(process.env.LM_ROXYON_CONFIG)return process.env.LM_ROXYON_CONFIG;return path.join(os.homedir(),".roxyon","config.json")}function readCredentials(){try{const c=JSON.parse(fs.readFileSync(credentialsPath(),"utf8"));return c&&typeof c==="object"?c:null}catch(e){return null}}function writeCredentials(creds){const p=credentialsPath();fs.mkdirSync(path.dirname(p),{recursive:true,mode:448});fs.writeFileSync(p,JSON.stringify(creds,null,2)+"\n",{mode:384});try{fs.chmodSync(p,384)}catch(e){}return p}function b64url(buf){return buf.toString("base64").replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function pkcePair(){const verifier=b64url(crypto.randomBytes(32));const challenge=b64url(crypto.createHash("sha256").update(verifier).digest());return{verifier,challenge}}function hasScope(scope,s){return String(scope||"").split(/\s+/).indexOf(s)!==-1}function lmVersion(){for(const p of[path.join(__dirname,"..","..","package.json"),path.join(__dirname,"..","..","..","package.json")]){try{const j=JSON.parse(fs.readFileSync(p,"utf8"));if(j.name==="@lmjs/cli")return j.version}catch(e){}}return"0.0.0"}function clientHeader(){return"lm/"+lmVersion()}async function postForm(url,fields){const body=Object.keys(fields).filter(k=>fields[k]!==void 0&&fields[k]!==null).map(k=>encodeURIComponent(k)+"="+encodeURIComponent(fields[k])).join("&");const res=await fetch(url,{method:"POST",headers:{"content-type":"application/x-www-form-urlencoded","x-roxyon-client":clientHeader()},body});let json=null;try{json=await res.json()}catch(e){}return{status:res.status,json:json||{}}}let _refreshing=null;async function refreshLumen(){if(_refreshing)return _refreshing;_refreshing=(async()=>{const creds=readCredentials();const l=creds&&creds.lumen;if(!l||!l.refreshToken)return null;if(l.accessToken&&Number(l.expiresAt||0)>Date.now()+6e4)return l;const r=await postForm(consoleUrl()+"/oauth/token",{grant_type:"refresh_token",refresh_token:l.refreshToken,client_id:l.clientId});if(r.status!==200||!r.json.access_token){if(r.status===400){const fresh2=readCredentials()||{};if(fresh2.lumen){delete fresh2.lumen.accessToken;delete fresh2.lumen.refreshToken;writeCredentials(fresh2)}}return null}const fresh=readCredentials()||{};const wasTopLevel=fresh.kind==="pat"&&fresh.lumen&&fresh.sessionToken===fresh.lumen.accessToken;fresh.lumen=Object.assign({},fresh.lumen||{},{accessToken:r.json.access_token,refreshToken:r.json.refresh_token||l.refreshToken,expiresAt:Date.now()+Number(r.json.expires_in||3600)*1e3,scope:r.json.scope||l.scope});if(wasTopLevel)fresh.sessionToken=fresh.lumen.accessToken;writeCredentials(fresh);return fresh.lumen})();try{return await _refreshing}finally{_refreshing=null}}async function getAuth(){const env=(process.env.ROXYON_TOKEN||"").trim();if(env)return authFromToken(env,"ROXYON_TOKEN");const creds=readCredentials();if(!creds)return null;let l=creds.lumen;if(l&&(l.accessToken||l.refreshToken)){if(!l.accessToken||Number(l.expiresAt||0)<=Date.now()+6e4){try{l=await refreshLumen()}catch(e){l=null}}if(l&&l.accessToken){return{headers:{authorization:"Bearer "+l.accessToken},source:"lm login",email:l.email||"",scope:l.scope||""}}}if(creds.sessionToken&&creds.kind!=="pat"){const a=authFromToken(String(creds.sessionToken),"roxyon login");a.email=creds.email||"";return a}return null}function authFromToken(tok,source){if(tok.indexOf("roxp_")===0)return{headers:{authorization:"Bearer "+tok},source,email:"",scope:""};return{headers:{"x-bea-session-token":tok},source,email:"",scope:"*"}}function hasCredentials(){if((process.env.ROXYON_TOKEN||"").trim())return true;const c=readCredentials();if(!c)return false;if(c.lumen&&(c.lumen.refreshToken||c.lumen.accessToken))return true;return!!(c.sessionToken&&c.kind!=="pat")}function storedPat(){const c=readCredentials();if(!c)return null;if(c.lumen&&c.lumen.accessToken&&Number(c.lumen.expiresAt||0)>Date.now()+6e4)return c.lumen.accessToken;if(typeof c.sessionToken==="string"&&c.sessionToken.indexOf("roxp_")===0)return c.sessionToken;return null}async function freshPat(){const c=readCredentials();if(c&&c.lumen&&c.lumen.refreshToken&&!(Number(c.lumen.expiresAt||0)>Date.now()+6e4)){try{await refreshLumen()}catch(e){}}return storedPat()}function openBrowser(url){const cmd=process.platform==="darwin"?"open":process.platform==="win32"?"cmd":"xdg-open";const args=process.platform==="win32"?["/c","start","",url]:[url];try{const p=spawn(cmd,args,{stdio:"ignore",detached:true});p.on("error",()=>{});p.unref();return true}catch(e){return false}}function listen(port){return new Promise((resolve,reject)=>{const srv=http.createServer();srv.once("error",reject);srv.listen(port,"127.0.0.1",()=>resolve(srv))})}function page(title,body){const esc=s=>String(s).replace(/[&<>"]/g,c=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;"})[c]);return`<!doctype html><meta charset="utf-8"><title>${esc(title)}</title><body style="font:15px/1.6 -apple-system,Segoe UI,Roboto,sans-serif;background:#0f0f10;color:#e6e6e6;display:flex;min-height:100vh;margin:0;align-items:center;justify-content:center"><div style="max-width:420px;padding:32px;background:#1a1a1c;border:1px solid #2a2a2e;border-radius:14px"><h1 style="font-size:18px;margin:0 0 10px">${esc(title)}</h1><p>${esc(body)}</p></div>`}async function registerClient(redirectUri){const res=await fetch(consoleUrl()+"/oauth/register",{method:"POST",headers:{"content-type":"application/json","x-roxyon-client":clientHeader()},body:JSON.stringify({client_name:CLIENT_NAME,redirect_uris:[redirectUri],grant_types:["authorization_code","refresh_token"],token_endpoint_auth_method:"none",scope:SCOPE,software_id:"lmjs-cli"})});const j=await res.json().catch(()=>({}));if(res.status!==201||!j.client_id)throw new Error("could not register with "+consoleUrl()+" ("+res.status+" "+(j.error_description||j.error||"")+")");return j.client_id}async function login(opts){opts=opts||{};const log=opts.log||console.log;const argv=opts.argv||[];const force=argv.indexOf("--force")!==-1;const noBrowser=argv.indexOf("--no-browser")!==-1;if(!force){const existing=readCredentials();if(existing&&existing.lumen&&existing.lumen.refreshToken){const l=Number(existing.lumen.expiresAt||0)>Date.now()+6e4?existing.lumen:await refreshLumen().catch(()=>null);if(l&&l.accessToken){log("Already signed in to Roxyon"+(l.email?" as "+l.email:"")+". (`lm login --force` signs in again, `lm logout` signs out.)");if(!hasScope(l.scope,"copilot"))log("This sign-in has no Copilot access yet; run `lm login --force`.");return l}}}const prev=(readCredentials()||{}).lumen||{};let srv=null;if(prev.clientId&&prev.redirectUri){const m=/^http:\/\/127\.0\.0\.1:(\d+)\/callback$/.exec(prev.redirectUri);if(m){try{srv=await listen(Number(m[1]))}catch(e){srv=null}}}let clientId=prev.clientId;let redirectUri=prev.redirectUri;if(!srv){srv=await listen(0);redirectUri="http://127.0.0.1:"+srv.address().port+"/callback";clientId=null}try{if(!clientId)clientId=await registerClient(redirectUri);const{verifier,challenge}=pkcePair();const state=b64url(crypto.randomBytes(16));const q={response_type:"code",client_id:clientId,redirect_uri:redirectUri,scope:SCOPE,state,code_challenge:challenge,code_challenge_method:"S256",resource:oauthResource()};const url=consoleUrl()+"/oauth/authorize?"+Object.keys(q).map(k=>k+"="+encodeURIComponent(q[k])).join("&");const codeP=new Promise((resolve,reject)=>{const timer=setTimeout(()=>reject(new Error("timed out waiting for the browser sign-in")),opts.timeoutMs||LOGIN_TIMEOUT_MS);srv.on("request",(req,res)=>{const u=new URL(req.url,"http://127.0.0.1");if(u.pathname!=="/callback"){res.writeHead(404);res.end();return}const p=u.searchParams;if(p.get("state")!==state){res.writeHead(400,{"content-type":"text/html; charset=utf-8"});res.end(page("Sign-in failed","This sign-in link does not match the one lm started. Run lm login again."));return}clearTimeout(timer);if(p.get("error")){res.writeHead(200,{"content-type":"text/html; charset=utf-8"});res.end(page("Not signed in","Access was not granted ("+p.get("error")+"). You can close this tab."));reject(new Error("access was not granted ("+p.get("error")+")"));return}res.writeHead(200,{"content-type":"text/html; charset=utf-8"});res.end(page("Signed in","LumenJS is now connected to your Roxyon account. You can close this tab and go back to the terminal."));resolve(p.get("code"))})});log("Sign in to Roxyon (free; new accounts get $0.50 of Copilot credit).");const opener=opts.open||openBrowser;if(noBrowser||!opener(url))log("Open this link in a browser on this computer:\n "+url);else log("Opening your browser... If it did not open, use this link:\n "+url);const code=await codeP;const tok=await postForm(consoleUrl()+"/oauth/token",{grant_type:"authorization_code",code,redirect_uri:redirectUri,client_id:clientId,code_verifier:verifier,resource:oauthResource()});if(tok.status!==200||!tok.json.access_token){throw new Error("token exchange failed ("+tok.status+" "+(tok.json.error_description||tok.json.error||"")+")")}let userId="",email="";try{const me=await fetch(consoleUrl()+"/account/context",{headers:{authorization:"Bearer "+tok.json.access_token,"x-roxyon-client":clientHeader()}});const j=await me.json();const u=j&&(j.user||j.data&&j.data.user)||{};userId=String(u.objectId||u.id||"");email=String(u.Email||u.email||"")}catch(e){}const lumen={accessToken:tok.json.access_token,refreshToken:tok.json.refresh_token,expiresAt:Date.now()+Number(tok.json.expires_in||3600)*1e3,scope:tok.json.scope||"",clientId,redirectUri,userId,email,savedAt:new Date().toISOString()};const creds=readCredentials()||{};creds.lumen=lumen;if(!creds.sessionToken||creds.kind==="pat"){creds.sessionToken=lumen.accessToken;creds.kind="pat";creds.userId=userId;creds.email=email;creds.savedAt=lumen.savedAt}const where=writeCredentials(creds);log("Signed in"+(email?" as "+email:"")+". Saved to "+where+".");if(!hasScope(lumen.scope,"copilot")){log("Note: the Roxyon console did not grant the copilot scope (granted: "+(lumen.scope||"none")+"). Lumen Copilot will not work until it does.")}else{log("Lumen Copilot is on: run `lm serve` and open the Copilot button on the page.")}return lumen}finally{try{srv.close()}catch(e){}}}async function loginCommand(argv){try{const r=await login({argv:argv||[]});if(!r)process.exitCode=1}catch(e){console.log("lm login failed: "+e.message);process.exitCode=1}}async function logout(){const creds=readCredentials();if(!creds||!creds.lumen){console.log("Not signed in with lm.");return}for(const t of[creds.lumen.refreshToken,creds.lumen.accessToken]){if(!t)continue;try{await postForm(consoleUrl()+"/oauth/revoke",{token:t})}catch(e){}}const keepClient={clientId:creds.lumen.clientId,redirectUri:creds.lumen.redirectUri};if(creds.kind==="pat"&&creds.sessionToken===creds.lumen.accessToken){delete creds.sessionToken;delete creds.kind;delete creds.userId;delete creds.email;delete creds.savedAt}creds.lumen=keepClient;writeCredentials(creds);console.log("Signed out.")}module.exports={consoleUrl,credentialsPath,readCredentials,writeCredentials,getAuth,hasCredentials,storedPat,freshPat,login,loginCommand,logout,pkcePair,clientHeader,lmVersion,_refreshLumen:refreshLumen};
@@ -0,0 +1 @@
1
+ const fs=require("fs");const path=require("path");const crypto=require("crypto");const fetch=require("node-fetch");const auth=require("./auth.js");const MAX_FILES_BYTES=60*1024;const MAX_TREE=200;const MAX_EDIT_BYTES=200*1024;const MAX_ANSWERS=30;const EDIT_EXTS=[".view",".layout",".js",".css",".less",".html",".json",".md",".svg",".txt",".tpl"];const TOPUP_URL="https://console.roxyon.com/account/billing?topup=1";function sha1(s){return crypto.createHash("sha1").update(s).digest("hex")}function isEnabled(config){const v=String(process.env.LM_COPILOT||"").trim().toLowerCase();if(v==="0"||v==="false"||v==="off")return false;if(config&&config.copilot===false)return false;if(v==="1"||v==="true"||v==="on")return true;if(process.env.LM_COPILOT_AUTO==="0")return false;if(process.env.LM_COPILOT_AUTO==="1")return true;if(process.env.CI)return false;return!!process.stdout.isTTY}const LOOPBACK_NAMES=["127.0.0.1","localhost","[::1]"];function isLoopbackAddr(a){a=String(a||"");return a==="127.0.0.1"||a==="::1"||a==="::ffff:127.0.0.1"}function loopbackHost(hostHeader,port){const h=String(hostHeader||"").toLowerCase();const m=/^(\[::1\]|127\.0\.0\.1|localhost):(\d+)$/.exec(h);if(!m||LOOPBACK_NAMES.indexOf(m[1])===-1)return null;if(port&&Number(m[2])!==Number(port))return null;return h}function tokenOk(given,token){const a=Buffer.from(String(given||""));const b=Buffer.from(String(token||""));return a.length===b.length&&b.length>0&&crypto.timingSafeEqual(a,b)}function checkRequest(req,token,port,needOrigin){const remote=req.socket&&req.socket.remoteAddress;if(!isLoopbackAddr(remote))return"not from this computer";const host=loopbackHost(req.headers.host,port);if(!host)return"bad host";const origin=req.headers.origin;if(origin!==void 0||needOrigin){if(origin!=="http://"+host)return"bad origin"}const site=req.headers["sec-fetch-site"];if(site!==void 0&&site!=="same-origin")return"not same-origin";if(!tokenOk(req.headers["x-lm-copilot"],token))return"bad token";return null}function resolveEditPath(root,rel){if(typeof rel!=="string"||rel===""||rel.length>240)return null;if(/[\0\\]/.test(rel)||path.isAbsolute(rel)||/^[a-zA-Z]:/.test(rel))return null;const norm=path.posix.normalize(rel.replace(/^\.\//,""));if(norm!==rel.replace(/^\.\//,""))return null;const segs=norm.split("/");if(segs[0]!=="src"||segs.length<2)return null;for(const s of segs){if(s===""||s==="."||s===".."||s[0]===".")return null;const l=s.toLowerCase();if(l==="node_modules"||l==="dist"||l==="config.json"||l.endsWith(".local")||l.startsWith(".env"))return null}if(EDIT_EXTS.indexOf(path.posix.extname(norm).toLowerCase())===-1)return null;if(norm==="src/js/lm.js")return null;let srcReal;try{srcReal=fs.realpathSync(path.join(root,"src"))}catch(e){return null}const abs=path.join(root,...segs);let p=abs;while(!fs.existsSync(p)){const up=path.dirname(p);if(up===p)return null;p=up}let real;try{real=fs.realpathSync(p)}catch(e){return null}if(real!==srcReal&&real.indexOf(srcReal+path.sep)!==0)return null;if(fs.existsSync(abs)){const st=fs.lstatSync(abs);if(st.isSymbolicLink()||!st.isFile())return null}return abs}function readIf(abs){try{return fs.readFileSync(abs,"utf8")}catch(e){return null}}function listTree(root){const out=[];const src=path.join(root,"src");(function walk(dir,rel){if(out.length>=MAX_TREE)return;let ents=[];try{ents=fs.readdirSync(dir,{withFileTypes:true})}catch(e){return}ents.sort((a,b)=>a.name.localeCompare(b.name));for(const e of ents){if(out.length>=MAX_TREE)return;if(e.name[0]==="."||e.name==="node_modules")continue;const r=rel?rel+"/"+e.name:e.name;if(e.isDirectory())walk(path.join(dir,e.name),r);else out.push("src/"+r)}})(src,"");return out}function defaultView(root){const idx=readIf(path.join(root,"src","index.js"))||"";const m=/["']?defaultView["']?\s*:\s*["']([\w\/-]+)["']/.exec(idx);return m?m[1]:"home"}function pickFiles(root,wanted){const files=[];let total=0;const seen={};for(const rel of wanted){if(!rel||seen[rel]||rel==="src/js/lm.js")continue;seen[rel]=1;const abs=path.join(root,...rel.split("/"));const c=readIf(abs);if(c==null)continue;const n=Buffer.byteLength(c);if(total+n>MAX_FILES_BYTES)continue;total+=n;files.push({path:rel,content:c})}return files}function viewsFromText(text,tree){const out=[];const views=tree.filter(t=>/^src\/(views|layouts|tpls|comps)\//.test(t));for(const v of views){const name=v.replace(/^src\/(views|layouts|tpls|comps)\//,"").replace(/\.(view|layout|tpl)$/,"");if(name&&new RegExp("(^|[^\\w-])"+name.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")+"([^\\w-]|$)").test(text))out.push(v)}return out}function routeView(root,route,tree){let seg=String(route||"/").split("?")[0].split("#")[0].replace(/^\/+/,"").split("/")[0];if(!seg||seg==="index.html")seg=defaultView(root);const v="src/views/"+seg+".view";return tree.indexOf(v)!==-1?v:null}function buildContext(root,body,serverErrors){const tree=listTree(root);const wanted=[];if(body.mode==="explain"){const e=body.error||{};const text=[e.message,e.stack,e.source].join("\n");const rv=routeView(root,e.route,tree);if(rv)wanted.push(rv);wanted.push(...viewsFromText(text,tree));const re=/https?:\/\/[^/\s)]+\/([^\s):?#]+\.js)/g;let m;while(m=re.exec(text)){const rel="src/"+m[1];if(tree.indexOf(rel)!==-1)wanted.push(rel)}wanted.push("src/index.js");wanted.push(...tree.filter(t=>/^src\/layouts\//.test(t)))}else{const name=sanitizeName(body.name);if(name)wanted.push("src/views/"+name+".view");wanted.push("src/index.js");wanted.push(...tree.filter(t=>/^src\/layouts\//.test(t)));const views=tree.filter(t=>/^src\/views\/[^/]+\.view$/.test(t)).map(t=>({t,n:(readIf(path.join(root,...t.split("/")))||"").length})).filter(x=>x.n>0).sort((a,b)=>a.n-b.n).slice(0,2).map(x=>x.t);wanted.push(...views)}return{tree,files:pickFiles(root,wanted),serverErrors:(serverErrors||[]).slice(-5)}}function sanitizeName(n){n=String(n||"").trim().toLowerCase().replace(/\.view$/,"");return/^[a-z][a-z0-9_-]{0,40}$/.test(n)?n:""}function unifiedDiff(before,after,file){const a=before==null?[]:before.replace(/\n$/,"").split("\n");const b=after.replace(/\n$/,"").split("\n");const head="--- "+(before==null?"/dev/null":"a/"+file)+"\n+++ b/"+file+"\n";if(a.length*b.length>4e6){return head+"@@ -1,"+a.length+" +1,"+b.length+" @@\n"+a.map(l=>"-"+l).join("\n")+(a.length?"\n":"")+b.map(l=>"+"+l).join("\n")+"\n"}const n=a.length,m=b.length;const dp=new Array(n+1);for(let i2=0;i2<=n;i2++)dp[i2]=new Int32Array(m+1);for(let i2=n-1;i2>=0;i2--)for(let j2=m-1;j2>=0;j2--)dp[i2][j2]=a[i2]===b[j2]?dp[i2+1][j2+1]+1:Math.max(dp[i2+1][j2],dp[i2][j2+1]);const ops=[];let i=0,j=0;while(i<n||j<m){if(i<n&&j<m&&a[i]===b[j]){ops.push([" ",a[i],i,j]);i++;j++}else if(i<n&&(j>=m||dp[i+1][j]>=dp[i][j+1])){ops.push(["-",a[i],i,j]);i++}else{ops.push(["+",b[j],i,j]);j++}}const changed=ops.map(o=>o[0]!==" ");if(changed.indexOf(true)===-1)return"";let out=head;let k=0;while(k<ops.length){if(!changed[k]){k++;continue}const start=Math.max(0,k-3);let last=k;for(let x=k;x<ops.length;x++){if(changed[x])last=x;else if(x-last>6)break}const end=Math.min(ops.length-1,last+3);const hunk=ops.slice(start,end+1);const aStart=hunk[0][2]+1,bStart=hunk[0][3]+1;const aLen=hunk.filter(o=>o[0]!=="+").length;const bLen=hunk.filter(o=>o[0]!=="-").length;out+="@@ -"+(aLen?aStart:0)+","+aLen+" +"+(bLen?bStart:0)+","+bLen+" @@\n";out+=hunk.map(o=>o[0]+o[1]).join("\n")+"\n";k=end+1}return out}function mount(app,opts){const express=require("express");const root=fs.realpathSync(opts.root);const token=opts.token;const getPort=opts.getPort||(()=>0);const doFetch=opts.fetch||fetch;const answers=new Map;const serverErrors=[];const json=express.json({limit:"1mb"});function guard(needOrigin){return function(req,res,next){const why=checkRequest(req,token,getPort(),needOrigin);if(why){res.status(403).json({ok:false,error:"Lumen Copilot refused this request ("+why+")."});return}next()}}app.get("/__lm/copilot.js",function(req,res){res.set("Cache-Control","no-store");res.type("application/javascript").sendFile(path.join(__dirname,"widget.js"))});app.get("/__lm/copilot/status",guard(false),async function(req,res){const a=await auth.getAuth().catch(()=>null);res.set("Cache-Control","no-store");res.json({ok:true,signedIn:!!a,email:a&&a.email||"",serverErrors:serverErrors.slice(-10),lmVersion:auth.lmVersion()})});app.post("/__lm/copilot/turn",guard(true),json,async function(req,res){const b=req.body||{};const mode=b.mode==="create"?"create":b.mode==="explain"?"explain":null;const prompt=String(b.prompt||"").slice(0,4e3);if(!mode)return res.status(400).json({ok:false,error:"mode must be explain or create"});if(mode==="create"&&!prompt.trim())return res.status(400).json({ok:false,error:"Describe the view you want."});const a=await auth.getAuth().catch(()=>null);if(!a)return res.json({ok:false,signin:true,error:"Sign in to use Lumen Copilot: run `lm login` in a terminal."});const error=mode==="explain"?{message:String(b.error&&b.error.message||"").slice(0,4e3),stack:String(b.error&&b.error.stack||"").slice(0,8e3),route:String(b.error&&b.error.route||"").slice(0,300),source:String(b.error&&b.error.source||"").slice(0,300)}:void 0;const ctx=buildContext(root,{mode,error,name:b.name},serverErrors);const payload={mode,prompt,name:sanitizeName(b.name)||void 0,think:!!b.think,error,files:ctx.files,tree:ctx.tree,serverErrors:ctx.serverErrors,lmVersion:auth.lmVersion()};let r,j;try{r=await doFetch((opts.consoleUrl||auth.consoleUrl())+"/copilot/v1/turn",{method:"POST",headers:Object.assign({"content-type":"application/json","x-roxyon-client":auth.clientHeader()},a.headers),body:JSON.stringify(payload),timeout:18e4});j=await r.json().catch(()=>null)}catch(e){return res.json({ok:false,error:"Could not reach Roxyon ("+e.message+"). Check your connection and try again."})}if(!r||!j)return res.json({ok:false,error:"Roxyon answered with something unexpected (HTTP "+(r&&r.status)+"). Try again."});if(r.status===401)return res.json({ok:false,signin:true,error:"Your Roxyon sign-in has expired. Run `lm login` again."});if(r.status===402||j.code==="insufficient_funds"){return res.json({ok:false,topup:j.topupUrl||TOPUP_URL,error:j.error||"Your Roxyon wallet is empty. Add funds to keep using Lumen Copilot.",usage:j.usage||null})}if(!j.ok)return res.json({ok:false,error:j.error||"Lumen Copilot could not answer (HTTP "+r.status+").",usage:j.usage||null});const edits=[];const dropped=[];for(const e of Array.isArray(j.edits)?j.edits.slice(0,10):[]){const rel=e&&typeof e.path==="string"?e.path:"";const content=e&&typeof e.content==="string"?e.content:null;const abs=resolveEditPath(root,rel);if(!abs||content==null||Buffer.byteLength(content)>MAX_EDIT_BYTES){dropped.push(rel||"(no path)");continue}const before=readIf(abs);if(before===content)continue;edits.push({path:rel,abs,content,before_sha1:before==null?null:sha1(before),isNew:before==null,diff:unifiedDiff(before,content,rel)})}const id=crypto.randomBytes(9).toString("hex");if(edits.length){answers.set(id,{edits,createdAt:Date.now()});while(answers.size>MAX_ANSWERS)answers.delete(answers.keys().next().value)}res.json({ok:true,id:edits.length?id:null,text:String(j.text||""),edits:edits.map(e=>({path:e.path,isNew:e.isNew,diff:e.diff})),dropped,hint:j.hint||"",usage:j.usage||null})});app.post("/__lm/copilot/apply",guard(true),json,function(req,res){const ans=answers.get(String(req.body&&req.body.id||""));if(!ans)return res.status(404).json({ok:false,error:"That answer is no longer available (the dev server restarted?). Ask again."});for(const e of ans.edits){const abs=resolveEditPath(root,e.path);if(!abs||abs!==e.abs)return res.status(403).json({ok:false,error:"Refused to write "+e.path+"."});const now=readIf(abs);const nowSha=now==null?null:sha1(now);if(nowSha!==e.before_sha1){return res.status(409).json({ok:false,error:e.path+" changed since this answer was made. Nothing was written; ask again."})}}const applyId=new Date().toISOString().replace(/[:.]/g,"-")+"-"+crypto.randomBytes(3).toString("hex");const dir=path.join(root,".lumen","copilot-undo",applyId);fs.mkdirSync(dir,{recursive:true});const manifest={applyId,answerId:req.body.id,at:new Date().toISOString(),files:[]};ans.edits.forEach((e,i)=>{const before=readIf(e.abs);const entry={path:e.path,existed:before!=null,after_sha1:sha1(e.content)};if(before!=null){entry.backup="f"+i;fs.writeFileSync(path.join(dir,entry.backup),before)}manifest.files.push(entry)});fs.writeFileSync(path.join(dir,"manifest.json"),JSON.stringify(manifest,null,2));for(const e of ans.edits){fs.mkdirSync(path.dirname(e.abs),{recursive:true});fs.writeFileSync(e.abs,e.content)}answers.delete(req.body.id);res.json({ok:true,applyId,files:ans.edits.map(e=>e.path)})});app.post("/__lm/copilot/undo",guard(true),json,function(req,res){const applyId=String(req.body&&req.body.applyId||"");if(!/^[\w-]{10,80}$/.test(applyId))return res.status(400).json({ok:false,error:"bad applyId"});const dir=path.join(root,".lumen","copilot-undo",applyId);let manifest;try{manifest=JSON.parse(fs.readFileSync(path.join(dir,"manifest.json"),"utf8"))}catch(e){return res.status(404).json({ok:false,error:"Nothing to undo for that change."})}if(manifest.undone)return res.json({ok:true,files:[],already:true});for(const f of manifest.files){const abs=resolveEditPath(root,f.path);const now=abs?readIf(abs):null;if(!abs||now==null||sha1(now)!==f.after_sha1){return res.status(409).json({ok:false,error:f.path+" was changed after Copilot wrote it, so Undo was not done. Its old version is in .lumen/copilot-undo/"+applyId+"/."})}}for(const f of manifest.files){const abs=resolveEditPath(root,f.path);if(f.existed)fs.writeFileSync(abs,fs.readFileSync(path.join(dir,f.backup)));else fs.unlinkSync(abs)}manifest.undone=new Date().toISOString();fs.writeFileSync(path.join(dir,"manifest.json"),JSON.stringify(manifest,null,2));res.json({ok:true,files:manifest.files.map(f=>f.path)})});return{inject(html,req){if(!req||!isLoopbackAddr(req.socket&&req.socket.remoteAddress)||!loopbackHost(req.headers.host,getPort()))return html;if(!auth.hasCredentials())return html;const tags=`<script>window.__lmCopilot=${JSON.stringify({t:token})};<\/script><script src="/__lm/copilot.js"><\/script>`;const m=/<head(\s[^>]*)?>/i.exec(html);if(m)return html.slice(0,m.index+m[0].length)+tags+html.slice(m.index+m[0].length);return tags+html},noteServerError(msg){serverErrors.push({message:String(msg).slice(0,2e3),at:Date.now()});if(serverErrors.length>20)serverErrors.shift()}}}module.exports={isEnabled,mount,checkRequest,resolveEditPath,unifiedDiff,buildContext,loopbackHost,sha1,sanitizeName};
@@ -0,0 +1 @@
1
+ (function(){"use strict";if(window.__lmCopilotLoaded)return;window.__lmCopilotLoaded=true;var cfg=window.__lmCopilot||{};var TOKEN=cfg.t||"";try{delete window.__lmCopilot}catch(e){window.__lmCopilot=void 0}if(!TOKEN)return;var errors=[];var listeners=[];var inOwnCode=false;function addError(kind,message,stack,source){if(inOwnCode)return;message=String(message||"").slice(0,2e3);if(!message)return;var last=errors[errors.length-1];if(last&&last.message===message&&last.kind===kind){last.count++;changed();return}errors.push({kind,message,stack:String(stack||"").slice(0,6e3),source:String(source||""),route:location.pathname+location.search+location.hash,at:Date.now(),count:1});if(errors.length>20)errors.shift();changed()}function changed(){for(var i=0;i<listeners.length;i++){try{listeners[i]()}catch(e){}}}window.addEventListener("error",function(ev){if(!ev||!ev.message)return;var src=ev.filename?ev.filename+":"+ev.lineno+":"+ev.colno:"";addError("error",ev.message,ev.error&&ev.error.stack,src)});window.addEventListener("unhandledrejection",function(ev){var r=ev&&ev.reason;addError("promise",r&&r.message||String(r),r&&r.stack,"")});var origError=console.error;console.error=function(){try{var parts=[],stack="";for(var i=0;i<arguments.length;i++){var a=arguments[i];if(a instanceof Error){parts.push(a.message);stack=stack||a.stack}else if(typeof a==="string")parts.push(a);else{try{parts.push(JSON.stringify(a))}catch(e){parts.push(String(a))}}}addError("console",parts.join(" "),stack||new Error().stack,"")}catch(e){}return origError.apply(console,arguments)};function call(method,url,body){inOwnCode=true;var p=fetch(url,{method,credentials:"same-origin",headers:body?{"content-type":"application/json","x-lm-copilot":TOKEN}:{"x-lm-copilot":TOKEN},body:body?JSON.stringify(body):void 0}).then(function(r){return r.json().catch(function(){return{ok:false,error:"HTTP "+r.status}})},function(e){return{ok:false,error:"The dev server did not answer ("+e.message+")."}});inOwnCode=false;return p}function el(tag,attrs,kids){var n=document.createElement(tag);if(attrs)for(var k in attrs){if(attrs[k]==null)continue;if(k==="text")n.textContent=attrs[k];else if(k.indexOf("on")===0)n.addEventListener(k.slice(2),attrs[k]);else n.setAttribute(k,attrs[k])}(kids||[]).forEach(function(c){if(c)n.appendChild(typeof c==="string"?document.createTextNode(c):c)});return n}function money(v){var n=Number(v);if(!isFinite(n))return"";return"$"+(n!==0&&Math.abs(n)<.01?n.toFixed(4):n.toFixed(2))}var CSS=[":host{all:initial}","*{box-sizing:border-box;font-family:-apple-system,Segoe UI,Roboto,sans-serif}",".fab{position:fixed;right:18px;bottom:18px;z-index:2147483646;border:0;border-radius:22px;padding:10px 16px;background:#111827;color:#fff;font-size:13px;font-weight:600;cursor:pointer;box-shadow:0 6px 20px rgba(0,0,0,.25)}",".fab .n{display:inline-block;min-width:18px;margin-left:8px;padding:0 6px;border-radius:9px;background:#ef4444;font-size:11px;line-height:18px}",".panel{position:fixed;right:18px;bottom:70px;z-index:2147483647;width:440px;max-width:calc(100vw - 36px);height:620px;max-height:calc(100vh - 100px);display:flex;flex-direction:column;background:#fff;color:#111827;border:1px solid #e5e7eb;border-radius:14px;box-shadow:0 18px 50px rgba(0,0,0,.25);overflow:hidden;font-size:13px}",".panel[hidden]{display:none}",".hd{display:flex;align-items:center;gap:8px;padding:10px 12px;border-bottom:1px solid #e5e7eb;background:#f9fafb}",".hd b{flex:1;font-size:14px}",".x{border:0;background:none;font-size:18px;cursor:pointer;color:#6b7280}",".tabs{display:flex;border-bottom:1px solid #e5e7eb}",".tabs button{flex:1;border:0;background:none;padding:9px;cursor:pointer;font-size:13px;color:#6b7280;border-bottom:2px solid transparent}",".tabs button.on{color:#111827;border-bottom-color:#2563eb;font-weight:600}",".body{flex:1;overflow:auto;padding:12px}",".err{border:1px solid #fecaca;background:#fef2f2;border-radius:8px;padding:8px;margin-bottom:8px}",".err .m{font-family:ui-monospace,Menlo,monospace;font-size:12px;white-space:pre-wrap;word-break:break-word;max-height:80px;overflow:hidden}",".err .k{color:#991b1b;font-size:11px;margin-bottom:4px}",".btn{border:1px solid #d1d5db;background:#fff;border-radius:7px;padding:6px 10px;cursor:pointer;font-size:12px}",".btn.p{background:#2563eb;border-color:#2563eb;color:#fff}",".btn[disabled]{opacity:.5;cursor:default}","textarea,input[type=text]{width:100%;border:1px solid #d1d5db;border-radius:8px;padding:8px;font-size:13px;margin-bottom:8px}","textarea{height:90px;resize:vertical}",".row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}",".muted{color:#6b7280;font-size:12px}",".ans{margin-top:12px;border-top:1px solid #e5e7eb;padding-top:12px}",".txt{white-space:pre-wrap;word-break:break-word;line-height:1.5}","pre{background:#0b1020;color:#e5e7eb;border-radius:8px;padding:8px;overflow:auto;font-size:11.5px;line-height:1.45;font-family:ui-monospace,Menlo,monospace;margin:6px 0}",".add{color:#86efac}.del{color:#fca5a5}.hunk{color:#93c5fd}",".file{font-weight:600;margin-top:10px}",".price{margin-top:10px;padding:6px 8px;background:#f3f4f6;border-radius:6px;font-size:12px;color:#374151}",".warn{color:#92400e;background:#fffbeb;border:1px solid #fde68a;border-radius:8px;padding:8px;margin-top:8px}","a{color:#2563eb}"].join("\n");var host,root,fab,panel,bodyEl,tab="errors",serverErrors=[],busy=false,lastAnswer=null;function build(){host=el("div",{id:"lm-copilot-host"});root=host.attachShadow({mode:"open"});root.appendChild(el("style",{text:CSS}));fab=el("button",{"class":"fab",title:"Lumen Copilot (lm serve only)",onclick:toggle});panel=el("div",{"class":"panel",hidden:""});var tabs=el("div",{"class":"tabs"},[el("button",{"data-t":"errors",text:"Explain an error",onclick:function(){setTab("errors")}}),el("button",{"data-t":"create",text:"Create a view",onclick:function(){setTab("create")}})]);bodyEl=el("div",{"class":"body"});panel.appendChild(el("div",{"class":"hd"},[el("b",{text:"Lumen Copilot"}),el("span",{"class":"muted",text:"dev only"}),el("button",{"class":"x",title:"Close",text:"\xD7",onclick:toggle})]));panel.appendChild(tabs);panel.appendChild(bodyEl);root.appendChild(fab);root.appendChild(panel);document.documentElement.appendChild(host);listeners.push(function(){renderFab();if(!panel.hidden&&tab==="errors"&&!busy&&!lastAnswer)render()});renderFab()}function renderFab(){fab.textContent="Copilot";var n=errors.length+serverErrors.length;if(n)fab.appendChild(el("span",{"class":"n",text:String(n)}))}function toggle(){if(panel.hidden){panel.hidden=false;refreshStatus();render()}else panel.hidden=true}function setTab(t){tab=t;lastAnswer=null;render()}function refreshStatus(){call("GET","/__lm/copilot/status").then(function(s){if(s&&s.ok){serverErrors=(s.serverErrors||[]).map(function(e){return{kind:"server",message:e.message,stack:"",source:"lm serve",route:location.pathname,count:1}});if(!s.signedIn){lastAnswer={signin:true,error:"Sign in to Roxyon to use Lumen Copilot: run `lm login` in a terminal."}}renderFab();if(!busy)render()}})}function render(){var tb=root.querySelectorAll(".tabs button");for(var i=0;i<tb.length;i++)tb[i].className=tb[i].getAttribute("data-t")===tab?"on":"";bodyEl.textContent="";if(tab==="errors")renderErrors();else renderCreate();if(busy)bodyEl.appendChild(el("div",{"class":"ans muted",id:"busy",text:"Thinking..."}));if(lastAnswer)bodyEl.appendChild(renderAnswer(lastAnswer))}function renderErrors(){var all=serverErrors.concat(errors);if(!all.length){bodyEl.appendChild(el("p",{"class":"muted",text:"No errors on this page yet. When the page or the dev server reports one, it shows here with an Explain button."}));return}all.slice().reverse().forEach(function(e){bodyEl.appendChild(el("div",{"class":"err"},[el("div",{"class":"k",text:(e.kind==="server"?"dev server":e.kind)+(e.count>1?" (x"+e.count+")":"")+" \xB7 "+e.route}),el("div",{"class":"m",text:e.message}),el("div",{"class":"row",style:"margin-top:6px"},[el("button",{"class":"btn p",text:"Explain this error",disabled:busy?"":null,onclick:function(){explain(e,false)}}),el("button",{"class":"btn",text:"Think harder",title:"Uses a stronger model; costs more",disabled:busy?"":null,onclick:function(){explain(e,true)}})])]))})}var draft={prompt:"",name:"",think:false};function renderCreate(){var ta=el("textarea",{placeholder:'Describe the view, e.g. "a pricing page with three plans and a monthly/yearly switch"'});ta.value=draft.prompt;ta.addEventListener("input",function(){draft.prompt=ta.value});var nm=el("input",{type:"text",placeholder:"Name (optional), e.g. pricing -> src/views/pricing.view"});nm.value=draft.name;nm.addEventListener("input",function(){draft.name=nm.value});var th=el("input",{type:"checkbox"});th.checked=draft.think;th.addEventListener("change",function(){draft.think=th.checked});var go=el("button",{"class":"btn p",text:"Create view",onclick:function(){if(!draft.prompt.trim()){ta.focus();return}turn({mode:"create",prompt:draft.prompt,name:draft.name,think:draft.think})}});if(busy)go.setAttribute("disabled","");bodyEl.appendChild(ta);bodyEl.appendChild(nm);bodyEl.appendChild(el("div",{"class":"row"},[go,el("label",{"class":"muted"},[th," Think harder (stronger model, costs more)"])]));bodyEl.appendChild(el("p",{"class":"muted",text:"Nothing is written until you click Apply. The price of each answer is shown under it."}))}function explain(e,think){turn({mode:"explain",think,prompt:"",error:{message:e.message,stack:e.stack,route:e.route,source:e.source}})}function turn(body){if(busy)return;busy=true;lastAnswer=null;render();var t0=Date.now();var tick=setInterval(function(){var b=root.getElementById?root.getElementById("busy"):null;if(b)b.textContent="Thinking... "+Math.round((Date.now()-t0)/1e3)+"s"},1e3);call("POST","/__lm/copilot/turn",body).then(function(r){clearInterval(tick);busy=false;lastAnswer=r||{ok:false,error:"No answer."};render()})}function textBlock(s){var wrap=el("div",{"class":"txt"});String(s||"").split(/```[\w-]*\n?/).forEach(function(part,i){if(i%2)wrap.appendChild(el("pre",{text:part.replace(/\n$/,"")}));else if(part)wrap.appendChild(document.createTextNode(part))});return wrap}function diffBlock(d){var pre=el("pre");String(d||"").split("\n").forEach(function(line){var c=line[0]==="+"&&line.indexOf("+++")!==0?"add":line[0]==="-"&&line.indexOf("---")!==0?"del":line.indexOf("@@")===0?"hunk":"";pre.appendChild(el("span",c?{"class":c,text:line+"\n"}:{text:line+"\n"}))});return pre}function renderAnswer(a){var box=el("div",{"class":"ans"});if(!a.ok){box.appendChild(el("div",{"class":"warn",text:a.error||"Something went wrong."}));if(a.topup)box.appendChild(el("p",{},[el("a",{href:a.topup,target:"_blank",rel:"noopener",text:"Add funds to your Roxyon wallet"})]));if(a.usage)box.appendChild(priceLine(a.usage));return box}box.appendChild(textBlock(a.text));if(a.hint)box.appendChild(el("p",{"class":"muted",text:a.hint}));(a.edits||[]).forEach(function(e){box.appendChild(el("div",{"class":"file",text:(e.isNew?"New file: ":"Change: ")+e.path}));box.appendChild(diffBlock(e.diff))});if(a.dropped&&a.dropped.length)box.appendChild(el("div",{"class":"warn",text:"Not offered (outside src/ or not allowed): "+a.dropped.join(", ")}));if(a.id&&!a.applied){var apply=el("button",{"class":"btn p",text:"Apply "+a.edits.length+" file"+(a.edits.length>1?"s":""),onclick:function(){apply.setAttribute("disabled","");call("POST","/__lm/copilot/apply",{id:a.id}).then(function(r){if(r&&r.ok){a.applied=r.applyId;a.appliedFiles=r.files}else a.applyError=r&&r.error||"Apply failed.";render()})}});box.appendChild(el("div",{"class":"row",style:"margin-top:8px"},[apply,el("span",{"class":"muted",text:"Writes to your project; Undo is available."})]))}if(a.applyError)box.appendChild(el("div",{"class":"warn",text:a.applyError}));if(a.applied&&!a.undone){var undo=el("button",{"class":"btn",text:"Undo",onclick:function(){undo.setAttribute("disabled","");call("POST","/__lm/copilot/undo",{applyId:a.applied}).then(function(r){if(r&&r.ok)a.undone=true;else a.applyError=r&&r.error||"Undo failed.";render()})}});box.appendChild(el("div",{"class":"row",style:"margin-top:8px"},[el("span",{text:"Applied: "+(a.appliedFiles||[]).join(", ")}),undo]))}if(a.undone)box.appendChild(el("p",{"class":"muted",text:"Undone: the files are back to how they were."}));if(a.usage)box.appendChild(priceLine(a.usage));return box}function priceLine(u){var parts=[];if(u.chargeUSD!=null)parts.push("This answer: "+money(u.chargeUSD)+(u.model?" ("+u.model+")":""));if(u.walletUSD!=null)parts.push("Wallet: "+money(u.walletUSD));if(u.note)parts.push(u.note);return el("div",{"class":"price",text:parts.join(" \xB7 ")})}if(document.readyState==="loading")document.addEventListener("DOMContentLoaded",build);else build()})();
package/dist/lib/fcs.js CHANGED
@@ -1,4 +1,4 @@
1
- const fetch=require("node-fetch");const path=require("path");const fs=require("fs");const nodemon=require("nodemon");var _acorn=require("acorn");var acorn=require("acorn-loose");const _hstLib=require("./hst.js");const{parseScriptJs,scriptPayload,stripAstPositions,annotateLayoutRegions,hst_markSvg,hst_orderIfFor,_hst}=_hstLib;exports.scriptPayload=scriptPayload;exports.stripAstPositions=stripAstPositions;exports.annotateLayoutRegions=annotateLayoutRegions;exports.hst_orderIfFor=hst_orderIfFor;function precompileViewScripts(groups){let failed=0;function visit(nodes,vars,inSection,file){if(!Array.isArray(nodes))return;for(const n of nodes){if(!n||n.type==="text")continue;const sectioned=inSection||n.type==="section"||n.type==="sections";const nm=String(n.name||"").toLowerCase();if((nm==="script"||nm==="js")&&Array.isArray(n.children)&&n.children[0]&&n.children[0].type==="text"){const t=n.children[0];if(t.content||t.jst){try{const ast=t.jst||_acorn.parse(t.content,{ecmaVersion:"latest",sourceType:"script"});const code=_walk.getWatcher(ast,{name:file},sectioned?void 0:(vars||[]).slice(),["__LMROOT__"]).code;new Function(code);t.pre=code;delete t.content;delete t.jst}catch(e){failed++;console.warn("\x1B[33mLumenJS: could not precompile a <script> in "+file+": "+(e&&e.message)+" (it will be compiled in the browser)\x1B[0m")}}}visit(n.children,vars,sectioned,file)}}for(const group of groups){const isTpl=group.kind==="tpls";for(const key in group.files){const entry=group.files[key];if(!entry||!Array.isArray(entry.hst))continue;const file=Buffer.from(key,"base64").toString().split(/[\\/]/).slice(-2).join("/");visit(entry.hst,entry.reactiveVars,isTpl,file)}}return failed}exports.precompileViewScripts=precompileViewScripts;function stripDevOnlyRuntime(src){const a=src.indexOf("var __lmDevOnlyStart__ = 1;");const b=src.indexOf("var __lmDevOnlyEnd__ = 1;");if(a<0||b<a)return src;return src.slice(0,a)+src.slice(b+"var __lmDevOnlyEnd__ = 1;".length)}exports.stripDevOnlyRuntime=stripDevOnlyRuntime;const _HST_EMPTY_OBJ={attrs:1,ax:1,evs:1,ex:1};function compactHstReplacer(key,value){if(_HST_EMPTY_OBJ[key]===1&&value&&typeof value==="object"&&!Array.isArray(value)&&Object.keys(value).length===0)return void 0;if(key==="isV"&&value===false)return void 0;if((key==="mss"||key==="children")&&Array.isArray(value)&&value.length===0)return void 0;return value}exports.compactHstReplacer=compactHstReplacer;var _min=require("html-minifier").minify;var he=require("he");const _FD=require("ftp-deploy");const _fD=new _FD;global.astring=global.astring||require("astring");const _corePackageDir=path.dirname(require.resolve("@lmjs/core/package.json"));const _walk=require(path.join(_corePackageDir,"src","walk.js"));const _npmBundle=require(path.join(__dirname,"npm-bundle.js"));exports.hasOwnDependencies=_npmBundle.hasOwnDependencies;exports.buildEntryWithImports=_npmBundle.buildEntryWithImports;const _pluginsBundle=require(path.join(__dirname,"plugins-esbuild-plugin.js"));const _vault=require(path.join(__dirname,"vault.js"));exports.login=_vault.login;exports.apiSet=_vault.apiSet;exports.apiUpdate=_vault.apiUpdate;exports.apiGet=_vault.apiGet;exports.apiList=_vault.apiList;exports.apiRemove=_vault.apiRemove;function selectedProjectPlugins(projectRoot){try{let deps={};const pkgPath=path.join(projectRoot,"package.json");if(fs.existsSync(pkgPath)){const pkg=JSON.parse(fs.readFileSync(pkgPath,"utf8"));Object.assign(deps,pkg.dependencies||{})}for(const key of detectUsedPlugins(projectRoot))if(!deps.hasOwnProperty(key))deps[key]="*";if(!Object.keys(deps).length)return[];return _pluginsBundle.selectedRegistryEntries(deps)}catch(e){return[]}}exports.selectedProjectPlugins=selectedProjectPlugins;const PLUGIN_SIGNATURES={"select2":[/\.select2\s*\(/,/<[a-z][^>]*\s(?:sl|slct)(?=[\s>=\/])/i],"flickity":[/\.flickity\s*\(/,/\bFlickity\b/,/<[a-z][^>]*\scrslf(?=[\s>=\/])/i],"bootstrap-colorpicker":[/\.colorpicker\s*\(/,/<input[^>]*\scolor(?=[\s>=\/])/i],"bootstrap-datetimepicker":[/\.datetimepicker\s*\(/,/<input[^>]*\s(?:date|time|datetime)(?=[\s>=\/])/i],"jquery-ui-dist":[/\.(?:sortable|draggable|droppable|resizable|datepicker|autocomplete|slider|accordion)\s*\(/,/<[a-z][^>]*\s(?:sort|drag|drop)(?=[\s>=\/])/i],"jquery.easing":[/\beas(?:e|ing)(?:In|Out)[A-Z]\w*/],"nestedSortable":[/\.nestedSortable\s*\(/],"jquery.numeric":[/\.numeric\s*\(/,/<input[^>]*\s(?:numeric|integer|decimal)(?=[\s>=\/])/i],"timeago":[/\.timeago\s*\(/,/\$\.timeago\b/],"slick-carousel":[/\.(?:crsl|slick)\s*\(/,/<[a-z][^>]*\scrsl(?=[\s>=\/])/i],"@iconify/iconify":[/class=["'][^"']*\biconify\b/,/\bIconify\./],"rellax":[/\bRellax\b/],"headroom.js":[/\bHeadroom\b/,/<[a-z][^>]*\spinnable(?=[\s>=\/])/i],"in-view":[/\binView\b/],"gsap":[/\bgsap\b/,/\bTween(?:Max|Lite)\b/,/\bTimeline(?:Max|Lite)\b/],"jquery.caret":[/\.caret\s*\(/],"jquery-tageditor":[/\.tagEditor\s*\(/,/<[a-z][^>]*\stags(?=[\s>=\/])/i],"jquery-lazy":[/\.lazy\s*\(/,/<img[^>]*\slazy(?=[\s>=\/])/i]};function detectUsedPlugins(projectRoot){const src=path.join(projectRoot,"src");if(!fs.existsSync(src))return[];let indexHtml="";try{indexHtml=fs.readFileSync(path.join(src,"index.html"),"utf8")}catch(e){}const texts=[];(function walk(dir){let entries;try{entries=fs.readdirSync(dir,{withFileTypes:true})}catch(e){return}for(const e of entries){const p=path.join(dir,e.name);if(e.isDirectory()){if(e.name==="node_modules"||e.name==="js"||e.name==="css"||e.name==="images")continue;walk(p)}else if(/\.(view|tpl|layout)$/.test(e.name)){try{texts.push(fs.readFileSync(p,"utf8"))}catch(x){}}}})(src);try{texts.push(fs.readFileSync(path.join(src,"index.js"),"utf8"))}catch(e){}for(const s of detectLocalProjectScripts(indexHtml)){try{texts.push(fs.readFileSync(path.join(src,s.replace(/^\//,"")),"utf8"))}catch(e){}}const all=texts.join("\n");const loadedByPage=key=>new RegExp(`<script[^>]+src=["'][^"']*`+key.replace(/^@[^/]+\//,"").replace(/\.js$/,"").replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),"i").test(indexHtml);const found=[];for(const key in PLUGIN_SIGNATURES){if(loadedByPage(key))continue;if(PLUGIN_SIGNATURES[key].some(re=>re.test(all)))found.push(key)}return found}exports.detectUsedPlugins=detectUsedPlugins;function compileProjectScript(source,name){let ast=_acorn.parse(source,{ecmaVersion:"latest",sourceType:"script"});let topLevelNames=[];for(const stmt of ast.body){if(stmt.type==="VariableDeclaration"){for(const d of stmt.declarations){if(d.id&&d.id.type==="Identifier")topLevelNames.push(d.id.name)}}}return _walk.getWatcher(ast,{name:name||"script"},topLevelNames,"Global").code}exports.compileProjectScript=compileProjectScript;function placePluginsCss(html,cssTag){html=String(html);let at=html.search(/<link\b[^>]*href=["']\/css\//i);return at>=0?html.slice(0,at)+cssTag+"\n "+html.slice(at):html.split("</head>").join(" "+cssTag+"\n</head>")}exports.placePluginsCss=placePluginsCss;function detectLocalProjectScripts(indexHtmlSource){let found=[];let re=/<script\b[^>]*\bsrc\s*=\s*["']([^"']+)["'][^>]*>/gi;let m;indexHtmlSource=String(indexHtmlSource).replace(/<!--[\s\S]*?-->/g,"");while(m=re.exec(indexHtmlSource)){let src=m[1];if(src.indexOf("/")!==0)continue;if(src==="/js/lm.js"||src==="/index.js")continue;if(/^\/js\/bea\.js(\?|$)/.test(src))continue;found.push(src)}return found}exports.detectLocalProjectScripts=detectLocalProjectScripts;const CORE_Libs={BEACSS:"https://s.beacdn.com/beacss/latest",COREJS:"https://lmjs.beacdn.com/latest"};const HST_FORMAT_VERSION=3;exports.HST_FORMAT_VERSION=HST_FORMAT_VERSION;function checkProjectRuntime(projectPath){try{let lm=fs.readFileSync(path.join(projectPath,"src","js","lm.js"),"utf8");let m=/EXPECTED_HST_FORMAT_VERSION\s*=\s*(\d+)/.exec(lm);let accepts=/_HST_FORMATS_ACCEPTED\s*=\s*\[([\d,\s]+)\]/.exec(lm);let ok=accepts?accepts[1].split(",").map(Number).indexOf(HST_FORMAT_VERSION)>-1:m&&Number(m[1])===HST_FORMAT_VERSION;if(m&&!ok){console.warn("\n\x1B[33mLumenJS: src/js/lm.js is older than this CLI (it expects HST v"+m[1]+", this CLI emits v"+HST_FORMAT_VERSION+"). Run `lm update-core` to update the project's runtime.\x1B[0m\n");return false}}catch(e){}return true}exports.checkProjectRuntime=checkProjectRuntime;exports.getAllFiles=getAllFiles;exports.ucwords=ucwords;exports.fetch=fetch;exports.getConfig=getConfig;function getConfig(){config={version:"1.0.0.1",server:{host:"127.0.0.1",port:"3000",ssl:{enabled:false,key:"/key.pem",cert:"/certificate.cer"}},apis:[{baseURL:"https://www.beaapis.com/1",headers:{"X-BEA-Application-ID":"YOUR_APPLICATION_ID","X-BEA-Authorization":"YOUR_REST_API_KEY"}}],ftp:{host:"ftp_host",username:"ftp_username",password:"ftp_password",remotePath:"/",concurrency:4,protocol:"ftp",port:21,ignore:[],syncOption:{delete:false,ignoreExisting:false,skipCreate:false,update:true},connectTimeout:3e4}};let configPath=process.cwd()+path.sep+`config.json`;if(fs.existsSync(configPath)){config=JSON.parse(fs.readFileSync(configPath,{encoding:"utf8"}))}return config}var config=getConfig();function ucwords(str){return(str+"").replace(/^([a-z])|\s+([a-z])/g,function($1){return $1.toUpperCase()})}function lw(s){return s.toString().toLowerCase()}exports.lw=lw;function cd(d){if(!fs.existsSync(d))fs.mkdirSync(d)}exports.cd=cd;function rd(d){if(fs.existsSync(d))fs.rmSync(d,{recursive:true,force:true})}exports.rd=rd;function create(conf){let t=conf.type=="v"||conf.type=="s"||conf.type=="t"?conf.type:"p";let n=conf.type=="v"||conf.type=="s"||conf.type=="t"?lw(conf.name[0]):conf.name.join(" ");if(t=="p"){var libsPath=`${__dirname}`+path.sep+`files`+path.sep+`libs`;var getLibs=false;var _beacssMissing=false;var _lmMissing=false;require("dns").resolve("www.google.com",function(err){if(!err){var _mobiuscssPath=libsPath+path.sep+"mobius.css";_beacssMissing=!fs.existsSync(_mobiuscssPath)||fs.statSync(_mobiuscssPath).size===0;_lmMissing=!fs.existsSync(libsPath+path.sep+"lm.js");if(_lmMissing||_beacssMissing)getLibs=true}if(getLibs){API.get(CORE_Libs.COREJS,{}).then(corejs=>{API.get(CORE_Libs.BEACSS,{}).then(mobiuscss=>{var libsPath2=`${__dirname}`+path.sep+`files`+path.sep+`libs`;cd(libsPath2);if(_lmMissing){fs.writeFileSync(libsPath2+path.sep+"lm.js",String(corejs))}if(_beacssMissing){fs.writeFileSync(libsPath2+path.sep+"mobius.css",String(mobiuscss))}var filePath3=`${process.cwd()}`+path.sep;copyDir(`${__dirname}`+path.sep+`files`+path.sep+`project`,filePath3,n);cd(filePath3+path.sep+"src");cd(filePath3+path.sep+"src"+path.sep+"css");cd(filePath3+path.sep+"src"+path.sep+"images");cd(filePath3+path.sep+"src"+path.sep+"js");let stylesFile=filePath3+path.sep+"src"+path.sep+"css"+path.sep+`styles.css`;let faviconFile=filePath3+path.sep+"src"+path.sep+`favicon.ico`;if(!fs.existsSync(stylesFile))fs.writeFileSync(stylesFile,String(""));if(!fs.existsSync(faviconFile))fs.writeFileSync(faviconFile,String(""));fs.writeFileSync(filePath3+path.sep+"src"+path.sep+"js"+path.sep+"lm.js",String(corejs));fs.writeFileSync(filePath3+path.sep+"src"+path.sep+"css"+path.sep+"mobius.css",String(mobiuscss))})}).catch(err2=>{})}else{var filePath2=`${process.cwd()}`+path.sep;copyDir(`${__dirname}`+path.sep+`files`+path.sep+`project`,filePath2,n);cd(filePath2+path.sep+"src");cd(filePath2+path.sep+"src"+path.sep+"css");cd(filePath2+path.sep+"src"+path.sep+"images");cd(filePath2+path.sep+"src"+path.sep+"js");let stylesFile=filePath2+path.sep+"src"+path.sep+"css"+path.sep+`styles.css`;let faviconFile=filePath2+path.sep+"src"+path.sep+`favicon.ico`;if(!fs.existsSync(stylesFile))fs.writeFileSync(stylesFile,String(""));if(!fs.existsSync(faviconFile))fs.writeFileSync(faviconFile,String(""));let corejs=Buffer.from(fs.readFileSync(libsPath+path.sep+"lm.js").toString());let mobiuscss=Buffer.from(fs.readFileSync(libsPath+path.sep+"mobius.css").toString());fs.writeFileSync(filePath2+path.sep+"src"+path.sep+"js"+path.sep+"lm.js",String(corejs));fs.writeFileSync(filePath2+path.sep+"src"+path.sep+"css"+path.sep+"mobius.css",String(mobiuscss))}})}else{let file=t=="s"?"sub_view.view":t=="t"?"layout.layout":"view.view";let ident=t=="s"?"SubView":t=="t"?"Template":"View";let dir=t=="t"?"layouts":"views";let ext=t=="t"?"layout":"view";let html5=Buffer.from(fs.readFileSync(`${__dirname}`+path.sep+`files`+path.sep+file).toString().split("["+ident+"Name]").join(ucwords(n)));var filePath=`${process.cwd()}`+path.sep+`src`+path.sep+dir+path.sep+`${n}.${ext}`;if(fs.existsSync(filePath)){console.log(ident+" already exists:",filePath)}else{fs.writeFileSync(filePath,html5)}}}function getAllFiles(dirPath,arrayOfFiles={},mini=false,hst=true){files=fs.readdirSync(dirPath);files.forEach(function(file){if(fs.statSync(dirPath+path.sep+file).isDirectory()){arrayOfFiles=getAllFiles(dirPath+path.sep+file,arrayOfFiles,mini,hst)}else{let filePath=path.join(dirPath,path.sep,file);let fileKey=Buffer.from(filePath.split(`${process.cwd()}`+path.sep).join("").split("\\").join("/")).toString("base64");if(mini){var result=_min(fs.readFileSync(filePath,{encoding:"utf8"}),{removeAttributeQuotes:false,caseSensitive:true,collapseWhitespace:true,collapseInlineTagWhitespace:true,conservativeCollapse:true,removeComments:true,minifyJS:false,minifyCSS:false,processConditionalComments:true,collapseBooleanAttributes:false,decodeEntities:false,html5:true,includeAutoGeneratedTags:true,keepClosingSlash:true,maxLineLength:null,minifyURLs:false,preserveLineBreaks:false,preventAttributesEscaping:false,processScripts:["text/html"],quoteCharacter:"",removeEmptyAttributes:true,removeEmptyElements:false,removeOptionalTags:true,removeRedundantAttributes:true,removeScriptTypeAttributes:false,removeStyleLinkTypeAttributes:true,removeTagWhitespace:false,sortAttributes:false,sortClassName:false,trimCustomFragments:true,useShortDoctype:true,continueOnParseError:true})}else if(hst){var result=_hst(fs.readFileSync(filePath,{encoding:"utf8"}))}else{var result=fs.readFileSync(filePath,{encoding:"utf8"})}arrayOfFiles[fileKey]=result}});return arrayOfFiles}function v1CompatIndexHtml(html){return String(html).replace(/(<script\b[^>]*\bsrc=["'])\/?js\/bea\.js(?:\?[^"']*)?(["'])/gi,"$1/js/lm.js$2").replace(/<script\b[^>]*\bsrc=["']\/?index\.js(?:\?[^"']*)?["'][^>]*>\s*<\/script>\s*/gi,"").replace(/<script\b[^>]*\bsrc=["'](?:https?:)?\/\/(?:s\.|www\.)?beacdn\.com\/beajs\/[^"']*["'][^>]*>\s*<\/script>\s*/gi,"").replace(/<script\b[^>]*\bsrc=["'](?:https?:)?\/\/lmjs\.beacdn\.com\/[^"']*["'][^>]*>\s*<\/script>\s*/gi,"")}exports.v1CompatIndexHtml=v1CompatIndexHtml;function readTemplates(root){let out={};for(const folder of["tpls","comps"]){let dir=root+path.sep+"src"+path.sep+folder+path.sep;if(fs.existsSync(dir))Object.assign(out,getAllFiles(dir,{},false,true))}return out}exports.readTemplates=readTemplates;function linkTemplateReactiveVars(files2,templates){function templateFor(attrs){let name=String(attrs.tpl).replace(/^\/+/,"");for(const f of["tpls","comps"]){let key=Buffer.from("src/"+f+"/"+name+".tpl").toString("base64");if(templates[key])return templates[key]}return null}function scan(nodes,add){if(!Array.isArray(nodes))return;for(const node of nodes){if(!node)continue;let a=node.attrs;if(a&&node.type==="sections"&&a.hasOwnProperty("tpl")){let t=templateFor(a);if(t)(t.reactiveVars||[]).forEach(add)}if(a&&a.hasOwnProperty("view")){let sv=viewFor(a.view);if(sv)(sv.reactiveVars||[]).forEach(add)}if(node.children)scan(node.children,add);if(Array.isArray(node.content))scan(node.content,add)}}let viewByPath={};for(const key in files2){let p=Buffer.from(key,"base64").toString().replace(/\\/g,"/");let m=p.match(/(?:^|\/)views\/(.+)\.view$/);if(m)viewByPath[m[1]]=files2[key]}function viewFor(v){let p=String(v).trim().replace(/^\/+/,"").replace(/\.view$/,"");if(!p||/\{\{/.test(p))return null;return viewByPath[p]||null}for(let pass=0;pass<10;pass++){let grew=false;for(const key in files2){let entry=files2[key];if(!entry||!Array.isArray(entry.hst))continue;if(!Array.isArray(entry.reactiveVars))entry.reactiveVars=[];scan(entry.hst,function(n){if(entry.reactiveVars.indexOf(n)===-1){entry.reactiveVars.push(n);grew=true}})}if(!grew)break}for(const key in files2){let entry=files2[key];if(!entry||!Array.isArray(entry.hst))continue;if(!Array.isArray(entry.reactiveVars))entry.reactiveVars=[];scan(entry.hst,function(n){if(entry.reactiveVars.indexOf(n)===-1)entry.reactiveVars.push(n)})}return files2}exports.linkTemplateReactiveVars=linkTemplateReactiveVars;function copyDir(src,dest,projectName){try{const entries=fs.readdirSync(src,{encoding:"utf8",withFileTypes:true});cd(dest);for(let entry of entries){const srcPath=path.join(src.toString(),entry.name.toString());const destPath=path.join(dest.toString(),entry.name.toString());if(entry.isDirectory()){if(projectName=="_beajsBuild"){if(entry.name!="layouts"&&entry.name!="views"&&entry.name!="css"&&entry.name!="js")copyDir(srcPath,destPath,projectName)}else copyDir(srcPath,destPath,projectName)}else{if(projectName=="_beajsBuild"){if(projectName=="_beajsBuild"&&entry.name!="index.js"&&entry.name!="index.html")fs.copyFileSync(srcPath,destPath)}else{if(!fs.existsSync(destPath)){if(projectName){if(entry.name=="index.html"){let html5=Buffer.from(fs.readFileSync(srcPath).toString().split("[ProjectName]").join(ucwords(projectName)));fs.writeFileSync(destPath,html5)}else if(entry.name=="package.json"){let slug=lw(projectName).replace(/[^a-z0-9._-]+/g,"-").replace(/^[-.]+|[-.]+$/g,"")||"lumenjs-project";let pkgJson=Buffer.from(fs.readFileSync(srcPath).toString().split("[ProjectName]").join(slug));fs.writeFileSync(destPath,pkgJson)}else fs.copyFileSync(srcPath,destPath)}else fs.copyFileSync(srcPath,destPath)}else{console.log("File already exists:",destPath)}}}}}catch(e){console.log(e)}}exports.copyDir=copyDir;function validURL(string){let url;try{url=new URL(string)}catch(_){return false}return url.protocol==="http:"||url.protocol==="https:"}exports.validURL=validURL;let getPairs=(obj,keys=[])=>Object.entries(obj).reduce((pairs,[key,value])=>{if(typeof value==="object")pairs.push(...getPairs(value,[...keys,key]));else pairs.push([[...keys,key],value]);return pairs},[]);function pairIt(obj){return getPairs(obj).map(([[key0,...keysRest],value])=>`${key0}${keysRest.map(a=>`[${a}]`).join("")}=${value}`).join("&")}var API={get:async function(u,d,h,a){return await this.req(u,d,h,"GET",a)},put:async function(u,d,h,a){return await this.req(u,d,h,"PUT",a)},post:async function(u,d,h,a){return await this.req(u,d,h,"POST",a)},delete:async function(u,d,h,a){return await this.req(u,d,h,"DELETE",a)},patch:async function(u,d,h,a){return await this.req(u,d,h,"PATCH",a)},req:async function(u,d,h,m,a,f){var urled=validURL(u)?u:config.apis[a??0].baseURL+u;if(m.toString().toLowerCase()=="get"){if(urled.indexOf("?")>-1)urled=urled+"&"+pairIt(d??{});else urled=urled+"?"+pairIt(d??{})}let resa=await fetch(urled,{method:m.toString().toLowerCase(),body:m.toString().toLowerCase()=="get"?null:JSON.stringify(d??{}),headers:h??config.apis[a??0].headers});if(f)return resa;let res=await resa.text();try{return JSON.parse(res)}catch(e){return res}}};exports.API=API;function updateCore(){var libsPath=`${__dirname}`+path.sep+`files`+path.sep+`libs`;var projectJsPath=process.cwd()+path.sep+"src"+path.sep+"js"+path.sep+"lm.js";var projectCssPath=process.cwd()+path.sep+"src"+path.sep+"css"+path.sep+"mobius.css";if(!fs.existsSync(process.cwd()+path.sep+"config.json")||!fs.existsSync(process.cwd()+path.sep+"src")){console.log("This doesn't look like a LumenJS project (no config.json/src here) \u2014 run this from your project's root.");return}if(!fs.existsSync(libsPath+path.sep+"lm.js")){console.log("This CLI install has no bundled runtime to copy from \u2014 reinstall @lmjs/cli.");return}addAgentFiles(process.cwd());var oldSize=fs.existsSync(projectJsPath)?fs.statSync(projectJsPath).size:null;var newJs=fs.readFileSync(libsPath+path.sep+"lm.js");var newSize=newJs.length;if(oldSize===newSize){console.log("Already up to date \u2014 this project's src/js/lm.js already matches the installed @lmjs/cli's runtime ("+newSize+" bytes). Nothing to do.");return}fs.writeFileSync(projectJsPath,newJs);if(fs.existsSync(libsPath+path.sep+"mobius.css")){fs.writeFileSync(projectCssPath,fs.readFileSync(libsPath+path.sep+"mobius.css"))}console.log("Updated src/js/lm.js"+(oldSize!==null?" (was "+oldSize+" bytes, now "+newSize+" bytes)":" (was missing, now "+newSize+" bytes)")+" \u2014 matches the currently installed @lmjs/cli.")}exports.updateCore=updateCore;function addAgentFiles(projectPath){var tplDir=path.join(__dirname,"files","project");var added=[];["AGENTS.md","CLAUDE.md"].forEach(function(f){var dest=path.join(projectPath,f);if(!fs.existsSync(dest)&&fs.existsSync(path.join(tplDir,f))){fs.copyFileSync(path.join(tplDir,f),dest);added.push(f)}});if(added.length)console.log("Added "+added.join(" + ")+" (points AI coding agents at the LumenJS reference).");return added}exports.addAgentFiles=addAgentFiles;exports.processCommand=processCommands;exports.startServer=startServer;exports.create=create;function processCommands(cmd){var command=cmd?cmd:process.argv[2];if(command=="run"||command=="serve"){startServer()}else if(command=="create"||command=="update"){let c=process.argv.slice(3,process.argv.length);let t=lw(c[0]);create({type:t[0]=="-"?t.split("-").join(""):t,name:t[0]=="-"?c.slice(1,c.length):c})}else if(command=="update-core"){updateCore()}else if(command=="purge"){console.log("(lm purge now does the same thing as `lm update-core` \u2014 prefer that name going forward.)");updateCore()}else if(command=="build"){let cmds=process.argv;_bp(cmds)}else if(command=="login"){_vault.login()}else if(command=="api"){let sub=process.argv[3];let name=process.argv[4];if(sub=="set")_vault.apiSet(name);else if(sub=="update")_vault.apiUpdate(name);else if(sub=="get")_vault.apiGet(name);else if(sub=="list")_vault.apiList();else if(sub=="remove")_vault.apiRemove(name);else console.log("Usage: lm api <set|get|update|list|remove> [name]")}else if(command=="-v"||command=="-version"){let _pkgJsonPath=fs.existsSync(path.join(__dirname,"..","..","package.json"))?path.join(__dirname,"..","..","package.json"):path.join(__dirname,"..","package.json");console.log("LumenJS version "+require(_pkgJsonPath).version)}else{console.log("LumenJS CLI's command not found!")}}async function _bp(cmds){import("ora").then(async({default:ora,oraPromise})=>{const spinner=ora({color:"green"});spinner.succeed("Started Building Project For Production").start("Gathering Required Resources..");var projectPath=`${process.cwd()}`;if(!checkProjectRuntime(projectPath)){spinner.fail("Build stopped: run `lm update-core` first, then build again.");process.exit(1)}var dest=projectPath+path.sep+"dist";let _views=getAllFiles(`${process.cwd()}`+path.sep+`src`+path.sep+`views`+path.sep,{},false,true);let _layoutsDir=`${process.cwd()}`+path.sep+`src`+path.sep+`layouts`+path.sep;let _layouts=fs.existsSync(_layoutsDir)?annotateLayoutRegions(getAllFiles(_layoutsDir,{},false,true)):{};let _tpls=readTemplates(`${process.cwd()}`);linkTemplateReactiveVars(_views,_tpls);linkTemplateReactiveVars(_layouts,_tpls);var indexCode=fs.readFileSync(projectPath+path.sep+`src`+path.sep+"index.html","utf8");let _cssDir=projectPath+path.sep+`src`+path.sep+"css"+path.sep;let _mainCssName=["mobius.css","bea.css"].find(f=>fs.existsSync(_cssDir+f))||null;var beaCSSCode=_mainCssName?fs.readFileSync(_cssDir+_mainCssName,"utf8"):"";var indexJs=fs.readFileSync(projectPath+path.sep+`src`+path.sep+"index.js","utf8");let _hasOwnDeps=_npmBundle.hasOwnDependencies(projectPath);let _localScriptSrcs=detectLocalProjectScripts(String(indexCode));let _compiledScripts="";for(const src of _localScriptSrcs){let scriptPath=projectPath+path.sep+"src"+path.sep+src.split("/").join(path.sep).replace(/^\\|^\//,"");try{let raw=_hasOwnDeps?_npmBundle.buildEntryWithImports(scriptPath,projectPath):fs.readFileSync(scriptPath,"utf8");_compiledScripts+="\n"+compileProjectScript(raw,src);indexCode=indexCode.split(new RegExp(`<script\\b[^>]*\\bsrc\\s*=\\s*["']`+src.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")+`["'][^>]*>\\s*<\\/script>`,"i")).join("")}catch(e){}}let _uncompiledScripts=precompileViewScripts([{kind:"views",files:_views},{kind:"layouts",files:_layouts},{kind:"tpls",files:_tpls}]);let vt=`
1
+ const fetch=require("node-fetch");const path=require("path");const fs=require("fs");const nodemon=require("nodemon");var _acorn=require("acorn");var acorn=require("acorn-loose");const _hstLib=require("./hst.js");const{parseScriptJs,scriptPayload,stripAstPositions,annotateLayoutRegions,hst_markSvg,hst_orderIfFor,_hst}=_hstLib;exports.scriptPayload=scriptPayload;exports.stripAstPositions=stripAstPositions;exports.annotateLayoutRegions=annotateLayoutRegions;exports.hst_orderIfFor=hst_orderIfFor;function precompileViewScripts(groups){let failed=0;function visit(nodes,vars,inSection,file){if(!Array.isArray(nodes))return;for(const n of nodes){if(!n||n.type==="text")continue;const sectioned=inSection||n.type==="section"||n.type==="sections";const nm=String(n.name||"").toLowerCase();if((nm==="script"||nm==="js")&&Array.isArray(n.children)&&n.children[0]&&n.children[0].type==="text"){const t=n.children[0];if(t.content||t.jst){try{const ast=t.jst||_acorn.parse(t.content,{ecmaVersion:"latest",sourceType:"script"});const code=_walk.getWatcher(ast,{name:file},sectioned?void 0:(vars||[]).slice(),["__LMROOT__"]).code;new Function(code);t.pre=code;delete t.content;delete t.jst}catch(e){failed++;console.warn("\x1B[33mLumenJS: could not precompile a <script> in "+file+": "+(e&&e.message)+" (it will be compiled in the browser)\x1B[0m")}}}visit(n.children,vars,sectioned,file)}}for(const group of groups){const isTpl=group.kind==="tpls";for(const key in group.files){const entry=group.files[key];if(!entry||!Array.isArray(entry.hst))continue;const file=Buffer.from(key,"base64").toString().split(/[\\/]/).slice(-2).join("/");visit(entry.hst,entry.reactiveVars,isTpl,file)}}return failed}exports.precompileViewScripts=precompileViewScripts;function stripDevOnlyRuntime(src){const a=src.indexOf("var __lmDevOnlyStart__ = 1;");const b=src.indexOf("var __lmDevOnlyEnd__ = 1;");if(a<0||b<a)return src;return src.slice(0,a)+src.slice(b+"var __lmDevOnlyEnd__ = 1;".length)}exports.stripDevOnlyRuntime=stripDevOnlyRuntime;const _HST_EMPTY_OBJ={attrs:1,ax:1,evs:1,ex:1};function compactHstReplacer(key,value){if(_HST_EMPTY_OBJ[key]===1&&value&&typeof value==="object"&&!Array.isArray(value)&&Object.keys(value).length===0)return void 0;if(key==="isV"&&value===false)return void 0;if((key==="mss"||key==="children")&&Array.isArray(value)&&value.length===0)return void 0;return value}exports.compactHstReplacer=compactHstReplacer;var _min=require("html-minifier").minify;var he=require("he");const _FD=require("ftp-deploy");const _fD=new _FD;global.astring=global.astring||require("astring");const _corePackageDir=path.dirname(require.resolve("@lmjs/core/package.json"));const _walk=require(path.join(_corePackageDir,"src","walk.js"));const _npmBundle=require(path.join(__dirname,"npm-bundle.js"));exports.hasOwnDependencies=_npmBundle.hasOwnDependencies;exports.buildEntryWithImports=_npmBundle.buildEntryWithImports;const _pluginsBundle=require(path.join(__dirname,"plugins-esbuild-plugin.js"));const _vault=require(path.join(__dirname,"vault.js"));exports.login=_vault.login;exports.apiSet=_vault.apiSet;exports.apiUpdate=_vault.apiUpdate;exports.apiGet=_vault.apiGet;exports.apiList=_vault.apiList;exports.apiRemove=_vault.apiRemove;function selectedProjectPlugins(projectRoot){try{let deps={};const pkgPath=path.join(projectRoot,"package.json");if(fs.existsSync(pkgPath)){const pkg=JSON.parse(fs.readFileSync(pkgPath,"utf8"));Object.assign(deps,pkg.dependencies||{})}for(const key of detectUsedPlugins(projectRoot))if(!deps.hasOwnProperty(key))deps[key]="*";if(!Object.keys(deps).length)return[];return _pluginsBundle.selectedRegistryEntries(deps)}catch(e){return[]}}exports.selectedProjectPlugins=selectedProjectPlugins;const PLUGIN_SIGNATURES={"select2":[/\.select2\s*\(/,/<[a-z][^>]*\s(?:sl|slct)(?=[\s>=\/])/i],"flickity":[/\.flickity\s*\(/,/\bFlickity\b/,/<[a-z][^>]*\scrslf(?=[\s>=\/])/i],"bootstrap-colorpicker":[/\.colorpicker\s*\(/,/<input[^>]*\scolor(?=[\s>=\/])/i],"bootstrap-datetimepicker":[/\.datetimepicker\s*\(/,/<input[^>]*\s(?:date|time|datetime)(?=[\s>=\/])/i],"jquery-ui-dist":[/\.(?:sortable|draggable|droppable|resizable|datepicker|autocomplete|slider|accordion)\s*\(/,/<[a-z][^>]*\s(?:sort|drag|drop)(?=[\s>=\/])/i],"jquery.easing":[/\beas(?:e|ing)(?:In|Out)[A-Z]\w*/],"nestedSortable":[/\.nestedSortable\s*\(/],"jquery.numeric":[/\.numeric\s*\(/,/<input[^>]*\s(?:numeric|integer|decimal)(?=[\s>=\/])/i],"timeago":[/\.timeago\s*\(/,/\$\.timeago\b/],"slick-carousel":[/\.(?:crsl|slick)\s*\(/,/<[a-z][^>]*\scrsl(?=[\s>=\/])/i],"@iconify/iconify":[/class=["'][^"']*\biconify\b/,/\bIconify\./],"rellax":[/\bRellax\b/],"headroom.js":[/\bHeadroom\b/,/<[a-z][^>]*\spinnable(?=[\s>=\/])/i],"in-view":[/\binView\b/],"gsap":[/\bgsap\b/,/\bTween(?:Max|Lite)\b/,/\bTimeline(?:Max|Lite)\b/],"jquery.caret":[/\.caret\s*\(/],"jquery-tageditor":[/\.tagEditor\s*\(/,/<[a-z][^>]*\stags(?=[\s>=\/])/i],"jquery-lazy":[/\.lazy\s*\(/,/<img[^>]*\slazy(?=[\s>=\/])/i]};function detectUsedPlugins(projectRoot){const src=path.join(projectRoot,"src");if(!fs.existsSync(src))return[];let indexHtml="";try{indexHtml=fs.readFileSync(path.join(src,"index.html"),"utf8")}catch(e){}const texts=[];(function walk(dir){let entries;try{entries=fs.readdirSync(dir,{withFileTypes:true})}catch(e){return}for(const e of entries){const p=path.join(dir,e.name);if(e.isDirectory()){if(e.name==="node_modules"||e.name==="js"||e.name==="css"||e.name==="images")continue;walk(p)}else if(/\.(view|tpl|layout)$/.test(e.name)){try{texts.push(fs.readFileSync(p,"utf8"))}catch(x){}}}})(src);try{texts.push(fs.readFileSync(path.join(src,"index.js"),"utf8"))}catch(e){}for(const s of detectLocalProjectScripts(indexHtml)){try{texts.push(fs.readFileSync(path.join(src,s.replace(/^\//,"")),"utf8"))}catch(e){}}const all=texts.join("\n");const loadedByPage=key=>new RegExp(`<script[^>]+src=["'][^"']*`+key.replace(/^@[^/]+\//,"").replace(/\.js$/,"").replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),"i").test(indexHtml);const found=[];for(const key in PLUGIN_SIGNATURES){if(loadedByPage(key))continue;if(PLUGIN_SIGNATURES[key].some(re=>re.test(all)))found.push(key)}return found}exports.detectUsedPlugins=detectUsedPlugins;function compileProjectScript(source,name){let ast=_acorn.parse(source,{ecmaVersion:"latest",sourceType:"script"});let topLevelNames=[];for(const stmt of ast.body){if(stmt.type==="VariableDeclaration"){for(const d of stmt.declarations){if(d.id&&d.id.type==="Identifier")topLevelNames.push(d.id.name)}}}return _walk.getWatcher(ast,{name:name||"script"},topLevelNames,"Global").code}exports.compileProjectScript=compileProjectScript;function placePluginsCss(html,cssTag){html=String(html);let at=html.search(/<link\b[^>]*href=["']\/css\//i);return at>=0?html.slice(0,at)+cssTag+"\n "+html.slice(at):html.split("</head>").join(" "+cssTag+"\n</head>")}exports.placePluginsCss=placePluginsCss;function detectLocalProjectScripts(indexHtmlSource){let found=[];let re=/<script\b[^>]*\bsrc\s*=\s*["']([^"']+)["'][^>]*>/gi;let m;indexHtmlSource=String(indexHtmlSource).replace(/<!--[\s\S]*?-->/g,"");while(m=re.exec(indexHtmlSource)){let src=m[1];if(src.indexOf("/")!==0)continue;if(src==="/js/lm.js"||src==="/index.js")continue;if(/^\/js\/bea\.js(\?|$)/.test(src))continue;found.push(src)}return found}exports.detectLocalProjectScripts=detectLocalProjectScripts;const CORE_Libs={BEACSS:"https://s.beacdn.com/beacss/latest",COREJS:"https://lmjs.beacdn.com/latest"};const HST_FORMAT_VERSION=3;exports.HST_FORMAT_VERSION=HST_FORMAT_VERSION;function checkProjectRuntime(projectPath){try{let lm=fs.readFileSync(path.join(projectPath,"src","js","lm.js"),"utf8");let m=/EXPECTED_HST_FORMAT_VERSION\s*=\s*(\d+)/.exec(lm);let accepts=/_HST_FORMATS_ACCEPTED\s*=\s*\[([\d,\s]+)\]/.exec(lm);let ok=accepts?accepts[1].split(",").map(Number).indexOf(HST_FORMAT_VERSION)>-1:m&&Number(m[1])===HST_FORMAT_VERSION;if(m&&!ok){console.warn("\n\x1B[33mLumenJS: src/js/lm.js is older than this CLI (it expects HST v"+m[1]+", this CLI emits v"+HST_FORMAT_VERSION+"). Run `lm update-core` to update the project's runtime.\x1B[0m\n");return false}}catch(e){}return true}exports.checkProjectRuntime=checkProjectRuntime;exports.getAllFiles=getAllFiles;exports.ucwords=ucwords;exports.fetch=fetch;exports.getConfig=getConfig;function getConfig(){config={version:"1.0.0.1",server:{host:"127.0.0.1",port:"3000",ssl:{enabled:false,key:"/key.pem",cert:"/certificate.cer"}},apis:[{baseURL:"https://www.beaapis.com/1",headers:{"X-BEA-Application-ID":"YOUR_APPLICATION_ID","X-BEA-Authorization":"YOUR_REST_API_KEY"}}],ftp:{host:"ftp_host",username:"ftp_username",password:"ftp_password",remotePath:"/",concurrency:4,protocol:"ftp",port:21,ignore:[],syncOption:{delete:false,ignoreExisting:false,skipCreate:false,update:true},connectTimeout:3e4}};let configPath=process.cwd()+path.sep+`config.json`;if(fs.existsSync(configPath)){config=JSON.parse(fs.readFileSync(configPath,{encoding:"utf8"}))}return config}var config=getConfig();function ucwords(str){return(str+"").replace(/^([a-z])|\s+([a-z])/g,function($1){return $1.toUpperCase()})}function lw(s){return s.toString().toLowerCase()}exports.lw=lw;function cd(d){if(!fs.existsSync(d))fs.mkdirSync(d)}exports.cd=cd;function rd(d){if(fs.existsSync(d))fs.rmSync(d,{recursive:true,force:true})}exports.rd=rd;function create(conf){let t=conf.type=="v"||conf.type=="s"||conf.type=="t"?conf.type:"p";let n=conf.type=="v"||conf.type=="s"||conf.type=="t"?lw(conf.name[0]):conf.name.join(" ");if(t=="p"){var libsPath=`${__dirname}`+path.sep+`files`+path.sep+`libs`;var getLibs=false;var _beacssMissing=false;var _lmMissing=false;require("dns").resolve("www.google.com",function(err){if(!err){var _mobiuscssPath=libsPath+path.sep+"mobius.css";_beacssMissing=!fs.existsSync(_mobiuscssPath)||fs.statSync(_mobiuscssPath).size===0;_lmMissing=!fs.existsSync(libsPath+path.sep+"lm.js");if(_lmMissing||_beacssMissing)getLibs=true}if(getLibs){API.get(CORE_Libs.COREJS,{}).then(corejs=>{API.get(CORE_Libs.BEACSS,{}).then(mobiuscss=>{var libsPath2=`${__dirname}`+path.sep+`files`+path.sep+`libs`;cd(libsPath2);if(_lmMissing){fs.writeFileSync(libsPath2+path.sep+"lm.js",String(corejs))}if(_beacssMissing){fs.writeFileSync(libsPath2+path.sep+"mobius.css",String(mobiuscss))}var filePath3=`${process.cwd()}`+path.sep;copyDir(`${__dirname}`+path.sep+`files`+path.sep+`project`,filePath3,n);cd(filePath3+path.sep+"src");cd(filePath3+path.sep+"src"+path.sep+"css");cd(filePath3+path.sep+"src"+path.sep+"images");cd(filePath3+path.sep+"src"+path.sep+"js");let stylesFile=filePath3+path.sep+"src"+path.sep+"css"+path.sep+`styles.css`;let faviconFile=filePath3+path.sep+"src"+path.sep+`favicon.ico`;if(!fs.existsSync(stylesFile))fs.writeFileSync(stylesFile,String(""));if(!fs.existsSync(faviconFile))fs.writeFileSync(faviconFile,String(""));fs.writeFileSync(filePath3+path.sep+"src"+path.sep+"js"+path.sep+"lm.js",String(corejs));fs.writeFileSync(filePath3+path.sep+"src"+path.sep+"css"+path.sep+"mobius.css",String(mobiuscss))})}).catch(err2=>{})}else{var filePath2=`${process.cwd()}`+path.sep;copyDir(`${__dirname}`+path.sep+`files`+path.sep+`project`,filePath2,n);cd(filePath2+path.sep+"src");cd(filePath2+path.sep+"src"+path.sep+"css");cd(filePath2+path.sep+"src"+path.sep+"images");cd(filePath2+path.sep+"src"+path.sep+"js");let stylesFile=filePath2+path.sep+"src"+path.sep+"css"+path.sep+`styles.css`;let faviconFile=filePath2+path.sep+"src"+path.sep+`favicon.ico`;if(!fs.existsSync(stylesFile))fs.writeFileSync(stylesFile,String(""));if(!fs.existsSync(faviconFile))fs.writeFileSync(faviconFile,String(""));let corejs=Buffer.from(fs.readFileSync(libsPath+path.sep+"lm.js").toString());let mobiuscss=Buffer.from(fs.readFileSync(libsPath+path.sep+"mobius.css").toString());fs.writeFileSync(filePath2+path.sep+"src"+path.sep+"js"+path.sep+"lm.js",String(corejs));fs.writeFileSync(filePath2+path.sep+"src"+path.sep+"css"+path.sep+"mobius.css",String(mobiuscss))}})}else{let file=t=="s"?"sub_view.view":t=="t"?"layout.layout":"view.view";let ident=t=="s"?"SubView":t=="t"?"Template":"View";let dir=t=="t"?"layouts":"views";let ext=t=="t"?"layout":"view";let html5=Buffer.from(fs.readFileSync(`${__dirname}`+path.sep+`files`+path.sep+file).toString().split("["+ident+"Name]").join(ucwords(n)));var filePath=`${process.cwd()}`+path.sep+`src`+path.sep+dir+path.sep+`${n}.${ext}`;if(fs.existsSync(filePath)){console.log(ident+" already exists:",filePath)}else{fs.writeFileSync(filePath,html5)}}}function getAllFiles(dirPath,arrayOfFiles={},mini=false,hst=true){files=fs.readdirSync(dirPath);files.forEach(function(file){if(fs.statSync(dirPath+path.sep+file).isDirectory()){arrayOfFiles=getAllFiles(dirPath+path.sep+file,arrayOfFiles,mini,hst)}else{let filePath=path.join(dirPath,path.sep,file);let fileKey=Buffer.from(filePath.split(`${process.cwd()}`+path.sep).join("").split("\\").join("/")).toString("base64");if(mini){var result=_min(fs.readFileSync(filePath,{encoding:"utf8"}),{removeAttributeQuotes:false,caseSensitive:true,collapseWhitespace:true,collapseInlineTagWhitespace:true,conservativeCollapse:true,removeComments:true,minifyJS:false,minifyCSS:false,processConditionalComments:true,collapseBooleanAttributes:false,decodeEntities:false,html5:true,includeAutoGeneratedTags:true,keepClosingSlash:true,maxLineLength:null,minifyURLs:false,preserveLineBreaks:false,preventAttributesEscaping:false,processScripts:["text/html"],quoteCharacter:"",removeEmptyAttributes:true,removeEmptyElements:false,removeOptionalTags:true,removeRedundantAttributes:true,removeScriptTypeAttributes:false,removeStyleLinkTypeAttributes:true,removeTagWhitespace:false,sortAttributes:false,sortClassName:false,trimCustomFragments:true,useShortDoctype:true,continueOnParseError:true})}else if(hst){var result=_hst(fs.readFileSync(filePath,{encoding:"utf8"}))}else{var result=fs.readFileSync(filePath,{encoding:"utf8"})}arrayOfFiles[fileKey]=result}});return arrayOfFiles}function v1CompatIndexHtml(html){return String(html).replace(/(<script\b[^>]*\bsrc=["'])\/?js\/bea\.js(?:\?[^"']*)?(["'])/gi,"$1/js/lm.js$2").replace(/<script\b[^>]*\bsrc=["']\/?index\.js(?:\?[^"']*)?["'][^>]*>\s*<\/script>\s*/gi,"").replace(/<script\b[^>]*\bsrc=["'](?:https?:)?\/\/(?:s\.|www\.)?beacdn\.com\/beajs\/[^"']*["'][^>]*>\s*<\/script>\s*/gi,"").replace(/<script\b[^>]*\bsrc=["'](?:https?:)?\/\/lmjs\.beacdn\.com\/[^"']*["'][^>]*>\s*<\/script>\s*/gi,"")}exports.v1CompatIndexHtml=v1CompatIndexHtml;function readTemplates(root){let out={};for(const folder of["tpls","comps"]){let dir=root+path.sep+"src"+path.sep+folder+path.sep;if(fs.existsSync(dir))Object.assign(out,getAllFiles(dir,{},false,true))}return out}exports.readTemplates=readTemplates;function linkTemplateReactiveVars(files2,templates){function templateFor(attrs){let name=String(attrs.tpl).replace(/^\/+/,"");for(const f of["tpls","comps"]){let key=Buffer.from("src/"+f+"/"+name+".tpl").toString("base64");if(templates[key])return templates[key]}return null}function scan(nodes,add){if(!Array.isArray(nodes))return;for(const node of nodes){if(!node)continue;let a=node.attrs;if(a&&node.type==="sections"&&a.hasOwnProperty("tpl")){let t=templateFor(a);if(t)(t.reactiveVars||[]).forEach(add)}if(a&&a.hasOwnProperty("view")){let sv=viewFor(a.view);if(sv)(sv.reactiveVars||[]).forEach(add)}if(node.children)scan(node.children,add);if(Array.isArray(node.content))scan(node.content,add)}}let viewByPath={};for(const key in files2){let p=Buffer.from(key,"base64").toString().replace(/\\/g,"/");let m=p.match(/(?:^|\/)views\/(.+)\.view$/);if(m)viewByPath[m[1]]=files2[key]}function viewFor(v){let p=String(v).trim().replace(/^\/+/,"").replace(/\.view$/,"");if(!p||/\{\{/.test(p))return null;return viewByPath[p]||null}for(let pass=0;pass<10;pass++){let grew=false;for(const key in files2){let entry=files2[key];if(!entry||!Array.isArray(entry.hst))continue;if(!Array.isArray(entry.reactiveVars))entry.reactiveVars=[];scan(entry.hst,function(n){if(entry.reactiveVars.indexOf(n)===-1){entry.reactiveVars.push(n);grew=true}})}if(!grew)break}for(const key in files2){let entry=files2[key];if(!entry||!Array.isArray(entry.hst))continue;if(!Array.isArray(entry.reactiveVars))entry.reactiveVars=[];scan(entry.hst,function(n){if(entry.reactiveVars.indexOf(n)===-1)entry.reactiveVars.push(n)})}return files2}exports.linkTemplateReactiveVars=linkTemplateReactiveVars;function copyDir(src,dest,projectName){try{const entries=fs.readdirSync(src,{encoding:"utf8",withFileTypes:true});cd(dest);for(let entry of entries){const srcPath=path.join(src.toString(),entry.name.toString());const destPath=path.join(dest.toString(),entry.name.toString());if(entry.isDirectory()){if(projectName=="_beajsBuild"){if(entry.name!="layouts"&&entry.name!="views"&&entry.name!="css"&&entry.name!="js")copyDir(srcPath,destPath,projectName)}else copyDir(srcPath,destPath,projectName)}else{if(projectName=="_beajsBuild"){if(projectName=="_beajsBuild"&&entry.name!="index.js"&&entry.name!="index.html")fs.copyFileSync(srcPath,destPath)}else{if(!fs.existsSync(destPath)){if(projectName){if(entry.name=="index.html"){let html5=Buffer.from(fs.readFileSync(srcPath).toString().split("[ProjectName]").join(ucwords(projectName)));fs.writeFileSync(destPath,html5)}else if(entry.name=="package.json"){let slug=lw(projectName).replace(/[^a-z0-9._-]+/g,"-").replace(/^[-.]+|[-.]+$/g,"")||"lumenjs-project";let pkgJson=Buffer.from(fs.readFileSync(srcPath).toString().split("[ProjectName]").join(slug));fs.writeFileSync(destPath,pkgJson)}else fs.copyFileSync(srcPath,destPath)}else fs.copyFileSync(srcPath,destPath)}else{console.log("File already exists:",destPath)}}}}}catch(e){console.log(e)}}exports.copyDir=copyDir;function validURL(string){let url;try{url=new URL(string)}catch(_){return false}return url.protocol==="http:"||url.protocol==="https:"}exports.validURL=validURL;let getPairs=(obj,keys=[])=>Object.entries(obj).reduce((pairs,[key,value])=>{if(typeof value==="object")pairs.push(...getPairs(value,[...keys,key]));else pairs.push([[...keys,key],value]);return pairs},[]);function pairIt(obj){return getPairs(obj).map(([[key0,...keysRest],value])=>`${key0}${keysRest.map(a=>`[${a}]`).join("")}=${value}`).join("&")}var API={get:async function(u,d,h,a){return await this.req(u,d,h,"GET",a)},put:async function(u,d,h,a){return await this.req(u,d,h,"PUT",a)},post:async function(u,d,h,a){return await this.req(u,d,h,"POST",a)},delete:async function(u,d,h,a){return await this.req(u,d,h,"DELETE",a)},patch:async function(u,d,h,a){return await this.req(u,d,h,"PATCH",a)},req:async function(u,d,h,m,a,f){var urled=validURL(u)?u:config.apis[a??0].baseURL+u;if(m.toString().toLowerCase()=="get"){if(urled.indexOf("?")>-1)urled=urled+"&"+pairIt(d??{});else urled=urled+"?"+pairIt(d??{})}let resa=await fetch(urled,{method:m.toString().toLowerCase(),body:m.toString().toLowerCase()=="get"?null:JSON.stringify(d??{}),headers:h??config.apis[a??0].headers});if(f)return resa;let res=await resa.text();try{return JSON.parse(res)}catch(e){return res}}};exports.API=API;function updateCore(){var libsPath=`${__dirname}`+path.sep+`files`+path.sep+`libs`;var projectJsPath=process.cwd()+path.sep+"src"+path.sep+"js"+path.sep+"lm.js";var projectCssPath=process.cwd()+path.sep+"src"+path.sep+"css"+path.sep+"mobius.css";if(!fs.existsSync(process.cwd()+path.sep+"config.json")||!fs.existsSync(process.cwd()+path.sep+"src")){console.log("This doesn't look like a LumenJS project (no config.json/src here) \u2014 run this from your project's root.");return}if(!fs.existsSync(libsPath+path.sep+"lm.js")){console.log("This CLI install has no bundled runtime to copy from \u2014 reinstall @lmjs/cli.");return}addAgentFiles(process.cwd());var oldSize=fs.existsSync(projectJsPath)?fs.statSync(projectJsPath).size:null;var newJs=fs.readFileSync(libsPath+path.sep+"lm.js");var newSize=newJs.length;if(oldSize===newSize){console.log("Already up to date \u2014 this project's src/js/lm.js already matches the installed @lmjs/cli's runtime ("+newSize+" bytes). Nothing to do.");return}fs.writeFileSync(projectJsPath,newJs);if(fs.existsSync(libsPath+path.sep+"mobius.css")){fs.writeFileSync(projectCssPath,fs.readFileSync(libsPath+path.sep+"mobius.css"))}console.log("Updated src/js/lm.js"+(oldSize!==null?" (was "+oldSize+" bytes, now "+newSize+" bytes)":" (was missing, now "+newSize+" bytes)")+" \u2014 matches the currently installed @lmjs/cli.")}exports.updateCore=updateCore;function addAgentFiles(projectPath){var tplDir=path.join(__dirname,"files","project");var added=[];["AGENTS.md","CLAUDE.md"].forEach(function(f){var dest=path.join(projectPath,f);if(!fs.existsSync(dest)&&fs.existsSync(path.join(tplDir,f))){fs.copyFileSync(path.join(tplDir,f),dest);added.push(f)}});if(added.length)console.log("Added "+added.join(" + ")+" (points AI coding agents at the LumenJS reference).");return added}exports.addAgentFiles=addAgentFiles;exports.processCommand=processCommands;exports.startServer=startServer;exports.create=create;function processCommands(cmd){var command=cmd?cmd:process.argv[2];if(command=="run"||command=="serve"){startServer()}else if(command=="create"||command=="update"){let c=process.argv.slice(3,process.argv.length);let t=lw(c[0]);create({type:t[0]=="-"?t.split("-").join(""):t,name:t[0]=="-"?c.slice(1,c.length):c})}else if(command=="update-core"){updateCore()}else if(command=="purge"){console.log("(lm purge now does the same thing as `lm update-core` \u2014 prefer that name going forward.)");updateCore()}else if(command=="build"){let cmds=process.argv;_bp(cmds)}else if(command=="login"){require(path.join(__dirname,"copilot","auth.js")).loginCommand(process.argv.slice(3))}else if(command=="logout"){require(path.join(__dirname,"copilot","auth.js")).logout()}else if(command=="api"){let sub=process.argv[3];let name=process.argv[4];if(sub=="set")_vault.apiSet(name);else if(sub=="update")_vault.apiUpdate(name);else if(sub=="get")_vault.apiGet(name);else if(sub=="list")_vault.apiList();else if(sub=="remove")_vault.apiRemove(name);else console.log("Usage: lm api <set|get|update|list|remove> [name]")}else if(command=="-v"||command=="-version"){let _pkgJsonPath=fs.existsSync(path.join(__dirname,"..","..","package.json"))?path.join(__dirname,"..","..","package.json"):path.join(__dirname,"..","package.json");console.log("LumenJS version "+require(_pkgJsonPath).version)}else{console.log("LumenJS CLI's command not found!")}}async function _bp(cmds){import("ora").then(async({default:ora,oraPromise})=>{const spinner=ora({color:"green"});spinner.succeed("Started Building Project For Production").start("Gathering Required Resources..");var projectPath=`${process.cwd()}`;if(!checkProjectRuntime(projectPath)){spinner.fail("Build stopped: run `lm update-core` first, then build again.");process.exit(1)}var dest=projectPath+path.sep+"dist";let _views=getAllFiles(`${process.cwd()}`+path.sep+`src`+path.sep+`views`+path.sep,{},false,true);let _layoutsDir=`${process.cwd()}`+path.sep+`src`+path.sep+`layouts`+path.sep;let _layouts=fs.existsSync(_layoutsDir)?annotateLayoutRegions(getAllFiles(_layoutsDir,{},false,true)):{};let _tpls=readTemplates(`${process.cwd()}`);linkTemplateReactiveVars(_views,_tpls);linkTemplateReactiveVars(_layouts,_tpls);var indexCode=fs.readFileSync(projectPath+path.sep+`src`+path.sep+"index.html","utf8");let _cssDir=projectPath+path.sep+`src`+path.sep+"css"+path.sep;let _mainCssName=["mobius.css","bea.css"].find(f=>fs.existsSync(_cssDir+f))||null;var beaCSSCode=_mainCssName?fs.readFileSync(_cssDir+_mainCssName,"utf8"):"";var indexJs=fs.readFileSync(projectPath+path.sep+`src`+path.sep+"index.js","utf8");let _hasOwnDeps=_npmBundle.hasOwnDependencies(projectPath);let _localScriptSrcs=detectLocalProjectScripts(String(indexCode));let _compiledScripts="";for(const src of _localScriptSrcs){let scriptPath=projectPath+path.sep+"src"+path.sep+src.split("/").join(path.sep).replace(/^\\|^\//,"");try{let raw=_hasOwnDeps?_npmBundle.buildEntryWithImports(scriptPath,projectPath):fs.readFileSync(scriptPath,"utf8");_compiledScripts+="\n"+compileProjectScript(raw,src);indexCode=indexCode.split(new RegExp(`<script\\b[^>]*\\bsrc\\s*=\\s*["']`+src.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")+`["'][^>]*>\\s*<\\/script>`,"i")).join("")}catch(e){}}let _uncompiledScripts=precompileViewScripts([{kind:"views",files:_views},{kind:"layouts",files:_layouts},{kind:"tpls",files:_tpls}]);let vt=`
2
2
  var _vcData = {
3
3
  "hstFormatVersion": `+HST_FORMAT_VERSION+`,
4
4
  "views": `+JSON.stringify(_views,compactHstReplacer)+`,
@@ -8,4 +8,4 @@ var _vcData = {
8
8
  `;try{let _indexJsSrc=_hasOwnDeps?_npmBundle.buildEntryWithImports(projectPath+path.sep+"src"+path.sep+"index.js",projectPath):String(indexJs);vt+=_compiledScripts+"\n"+compileProjectScript(_indexJsSrc,"index")}catch(e){spinner.fail("Error compiling index.js: "+(e&&e.message?e.message:e));spinner.fail("Build Failed! Fix index.js and try again.");return}indexCode=v1CompatIndexHtml(indexCode);spinner.succeed("Gathering Required Resources").start("Signing & Securing App..");let vaultApis=await _vault.resolveVaultApis(projectPath);let effectiveApis=(config.apis||[]).concat(vaultApis);let baasEntry=(config.apis||[]).find(a=>a&&typeof a.baseURL==="string"&&a.baseURL.indexOf("beaapis.com")>-1);let beajsAppId=baasEntry&&baasEntry.headers&&baasEntry.headers["X-BEA-Application-ID"];let beajsAuth=baasEntry&&baasEntry.headers&&baasEntry.headers["X-BEA-Authorization"];const keyUnset=v=>!v||/^YOUR_/i.test(String(v).trim());let token_="";if(!baasEntry||keyUnset(beajsAppId)||keyUnset(beajsAuth)){spinner.warn("No Roxyon BaaS keys in config.json, so this build has no backend token. The app works as a static site; to use the Roxyon BaaS (data, auth, files), add your app's keys from https://console.roxyon.com to config.json's apis[] and build again.");spinner.start("Signing & Securing App..")}else{let beajsRawRes;try{beajsRawRes=await API.req("https://www.beaapis.com/1/beajs",{data:{apis:JSON.stringify(effectiveApis)}},{"X-BEA-Application-ID":beajsAppId,"X-BEA-Authorization":beajsAuth},"POST",void 0,true)}catch(e){spinner.fail("Error getting a unique token for production! "+(e&&e.message?e.message:e));spinner.fail("Build Failed! Try again later.");return}if(!beajsRawRes.ok){spinner.fail("Error getting a unique token for production! HTTP "+beajsRawRes.status+(beajsRawRes.status===401||beajsRawRes.status===403?" (the Roxyon BaaS keys in config.json were rejected: check them in https://console.roxyon.com)":""));spinner.fail("Build Failed!");return}let r={};try{r=JSON.parse(await beajsRawRes.text())}catch(e){r={}}token_=r.token;if(!token_){spinner.fail("Error getting a unique token for production!");spinner.fail("Build Failed! Try again later.");return}}let js_="";js_+=`
9
9
  var _beaTn = "`+token_+`";
10
10
  `+(token_?"":"var _lmStatic = true;\n");let _projectRuntime=projectPath+path.sep+"src"+path.sep+"js"+path.sep+"lm.js";let ownRuntime=fs.readFileSync(fs.existsSync(_projectRuntime)?_projectRuntime:path.join(__dirname,"files","libs","lm.js"),"utf8");js_+=(_uncompiledScripts===0?stripDevOnlyRuntime(ownRuntime):ownRuntime)+"\n"+vt;spinner.succeed("Signing & Securing App").start("Packing & Cleaning...");rd(dest);spinner.succeed("Packing & Cleaning").start("Writing Files...");let buildPath=`build_serverless`;copyDir(`${__dirname}`+path.sep+`files`+path.sep+buildPath,dest);copyDir(projectPath+path.sep+"src",dest,"_beajsBuild");cd(dest+path.sep+"js");cd(dest+path.sep+"css");cd(dest+path.sep+"images");copyDir(projectPath+path.sep+"src"+path.sep+"js",dest+path.sep+"js","_beajsBuild");copyDir(projectPath+path.sep+"src"+path.sep+"css",dest+path.sep+"css","_beajsBuild");copyDir(projectPath+path.sep+"src"+path.sep+"images",dest+path.sep+"images","_beajsBuild");fs.writeFileSync(dest+path.sep+"js"+path.sep+"lm.js",String(js_));let _dT=new Date().getTime();fs.renameSync(dest+path.sep+"js"+path.sep+"lm.js",dest+path.sep+"js"+path.sep+"app."+_dT+".js");if(!fs.existsSync(dest+path.sep+"css"))fs.mkdirSync(dest+path.sep+"css",{recursive:true});if(_mainCssName){let _copied=dest+path.sep+"css"+path.sep+_mainCssName;if(fs.existsSync(_copied))fs.unlinkSync(_copied);fs.writeFileSync(dest+path.sep+"css"+path.sep+"app."+_dT+".css",String(beaCSSCode))}let _hasStyles=fs.existsSync(dest+path.sep+"css"+path.sep+"styles.css");if(_hasStyles){fs.renameSync(dest+path.sep+"css"+path.sep+"styles.css",dest+path.sep+"css"+path.sep+"styles."+_dT+".css")}let _selectedPlugins=selectedProjectPlugins(projectPath);let _pluginsTags="";let _pluginsCssTag="";if(_selectedPlugins.length){let _pluginsJs=await _pluginsBundle.buildSelectedPluginsBundle(_selectedPlugins,projectPath,{minify:true});let _pluginsCss=await _pluginsBundle.buildSelectedCss(_selectedPlugins,projectPath+path.sep+"node_modules");fs.writeFileSync(dest+path.sep+"js"+path.sep+"plugins."+_dT+".js",String(_pluginsJs));_pluginsTags+=`
11
- <script src="/js/plugins.`+_dT+`.js" type="text/javascript"><\/script>`;if(_pluginsCss&&_pluginsCss.trim()!=""){fs.writeFileSync(dest+path.sep+"css"+path.sep+"plugins."+_dT+".css",String(_pluginsCss));_pluginsCssTag=`<link href="/css/plugins.`+_dT+`.css" rel="stylesheet" type="text/css" />`}}let _indexOut=String(indexCode).split("/js/lm.js").join("/js/app."+_dT+".js").split("/css/styles.css").join(_hasStyles?"/css/styles."+_dT+".css":"/css/styles.css").split("/css/"+(_mainCssName||"mobius.css")).join(_mainCssName?"/css/app."+_dT+".css":"/css/mobius.css").split("</head>").join(_pluginsTags+"\n</head>");if(_pluginsCssTag)_indexOut=placePluginsCss(_indexOut,_pluginsCssTag);fs.writeFileSync(dest+path.sep+"index.html",_indexOut);if(cmds.indexOf("--deploy")>=0){spinner.succeed("Writing Files").start("Uploading To Server..");const _config={user:config.ftp.username,password:config.ftp.password,host:config.ftp.host,port:config.ftp.port,localRoot:dest,remoteRoot:config.ftp.remotePath,include:["*","**/*",".*"],exclude:config.ftp.ignore,deleteRemote:false,forcePasv:true,sftp:config.ftp.protocol=="sftp"?true:false};_fD.deploy(_config,function(err,res){if(err){console.log(err)}else{spinner.succeed("Finished Uploading To Server");spinner.succeed("Done")}});_fD.on("uploading",function(data){spinner.start(parseFloat((data.transferredFileCount*100/data.totalFilesCount).toFixed(2))+"% Uploading "+data.filename+" ")})}else spinner.succeed("Writing Files").succeed("Done")})}function clear(isSoft){process.stdout.write(isSoft?"\x1B[H\x1B[2J":"\x1B[2J\x1B[3J\x1B[H\x1Bc")}function startServer(){clear();nodemon({script:path.join(__dirname.toString(),"app.js"),ext:"view,js,css,json,layout,html",watch:["src/","config.json"],ignore:["src/views/**","src/layouts/**"]});nodemon.on("restart",files2=>{console.log(files2)})}
11
+ <script src="/js/plugins.`+_dT+`.js" type="text/javascript"><\/script>`;if(_pluginsCss&&_pluginsCss.trim()!=""){fs.writeFileSync(dest+path.sep+"css"+path.sep+"plugins."+_dT+".css",String(_pluginsCss));_pluginsCssTag=`<link href="/css/plugins.`+_dT+`.css" rel="stylesheet" type="text/css" />`}}let _indexOut=String(indexCode).split("/js/lm.js").join("/js/app."+_dT+".js").split("/css/styles.css").join(_hasStyles?"/css/styles."+_dT+".css":"/css/styles.css").split("/css/"+(_mainCssName||"mobius.css")).join(_mainCssName?"/css/app."+_dT+".css":"/css/mobius.css").split("</head>").join(_pluginsTags+"\n</head>");if(_pluginsCssTag)_indexOut=placePluginsCss(_indexOut,_pluginsCssTag);fs.writeFileSync(dest+path.sep+"index.html",_indexOut);if(cmds.indexOf("--deploy")>=0){spinner.succeed("Writing Files").start("Uploading To Server..");const _config={user:config.ftp.username,password:config.ftp.password,host:config.ftp.host,port:config.ftp.port,localRoot:dest,remoteRoot:config.ftp.remotePath,include:["*","**/*",".*"],exclude:config.ftp.ignore,deleteRemote:false,forcePasv:true,sftp:config.ftp.protocol=="sftp"?true:false};_fD.deploy(_config,function(err,res){if(err){console.log(err)}else{spinner.succeed("Finished Uploading To Server");spinner.succeed("Done")}});_fD.on("uploading",function(data){spinner.start(parseFloat((data.transferredFileCount*100/data.totalFilesCount).toFixed(2))+"% Uploading "+data.filename+" ")})}else spinner.succeed("Writing Files").succeed("Done")})}function clear(isSoft){process.stdout.write(isSoft?"\x1B[H\x1B[2J":"\x1B[2J\x1B[3J\x1B[H\x1Bc")}function startServer(){clear();if(!process.env.LM_COPILOT_TOKEN)process.env.LM_COPILOT_TOKEN=require("crypto").randomBytes(24).toString("hex");if(!process.env.LM_COPILOT_AUTO)process.env.LM_COPILOT_AUTO=process.stdout.isTTY&&!process.env.CI?"1":"0";nodemon({script:path.join(__dirname.toString(),"app.js"),ext:"view,js,css,json,layout,html",watch:["src/","config.json"],ignore:["src/views/**","src/layouts/**"]});nodemon.on("restart",files2=>{console.log(files2)})}
package/dist/lib/vault.js CHANGED
@@ -1 +1 @@
1
- const fs=require("fs");const path=require("path");const readline=require("readline");const{execSync,spawnSync}=require("child_process");const fetch=require("node-fetch");const LUMEN_SECRETS_BASE="https://www.beaapis.com/1/_lumen_secrets";const TOKEN_FILE_NAME=".roxyon-token.local";const VAULT_FILE_NAME=".lumen-vault.local";function tokenFilePath(projectRoot){return path.join(projectRoot,TOKEN_FILE_NAME)}function vaultFilePath(projectRoot){return path.join(projectRoot,VAULT_FILE_NAME)}function getStoredToken(projectRoot){if(process.env.ROXYON_TOKEN)return process.env.ROXYON_TOKEN.trim();const p=tokenFilePath(projectRoot);if(fs.existsSync(p)){const t=fs.readFileSync(p,"utf8").trim();if(t)return t}return null}function hasRoxyonCli(){try{execSync(process.platform==="win32"?"where roxyon":"which roxyon",{stdio:"ignore"});return true}catch(e){return false}}async function login(){const projectRoot=process.cwd();const existing=getStoredToken(projectRoot);if(existing){console.log("Already logged in (found an existing ROXYON_TOKEN).");return existing}if(!hasRoxyonCli()){console.log("No ROXYON_TOKEN found, and the `roxyon` CLI isn't on your PATH.\nInstall it (`npm install -g @roxyon/cli`), run `roxyon login` once,\nthen re-run `lm login` \u2014 it reuses whatever `roxyon login` already set up.");return null}console.log("No ROXYON_TOKEN found. Launching `roxyon login`...");const res=spawnSync("roxyon",["login"],{stdio:"inherit"});if(res.status!==0){console.log("`roxyon login` did not complete successfully.");return null}const after=getStoredToken(projectRoot);if(!after){console.log("`roxyon login` finished, but no ROXYON_TOKEN turned up. Set it manually\n(export ROXYON_TOKEN=... or write it to ./"+TOKEN_FILE_NAME+") and try again.");return null}console.log("Logged in.");return after}exports.login=login;exports.getStoredToken=getStoredToken;function prompt(question){const rl=readline.createInterface({input:process.stdin,output:process.stdout});return new Promise(resolve=>rl.question(question,answer=>{rl.close();resolve(answer)}))}async function promptHeaders(){const headers={};console.log("Enter headers one at a time as `Name: value` (blank line to finish):");for(;;){const line=(await prompt(" header: ")).trim();if(line==="")break;const idx=line.indexOf(":");if(idx===-1){console.log(" (expected `Name: value`, skipped)");continue}headers[line.slice(0,idx).trim()]=line.slice(idx+1).trim()}return headers}async function lumenSecretsCall(pathSeg,method,token,body,query){let url=LUMEN_SECRETS_BASE+"/"+pathSeg;if(query){const qs=Object.keys(query).map(k=>encodeURIComponent(k)+"="+encodeURIComponent(query[k])).join("&");if(qs)url+="?"+qs}const res=await fetch(url,{method,headers:{Authorization:"Bearer "+token,"Content-Type":"application/json"},body:method==="GET"?void 0:JSON.stringify(body||{})});const text=await res.text();try{return JSON.parse(text)}catch(e){return{ok:false,error:"non-JSON response: "+text.slice(0,200)}}}function readVaultFile(projectRoot){const p=vaultFilePath(projectRoot);if(!fs.existsSync(p))return[];try{const parsed=JSON.parse(fs.readFileSync(p,"utf8"));return Array.isArray(parsed)?parsed:[]}catch(e){return[]}}function writeVaultFile(projectRoot,entries){fs.writeFileSync(vaultFilePath(projectRoot),JSON.stringify(entries,null,2)+"\n")}async function apiSet(name){const projectRoot=process.cwd();const token=await login();if(!token)return;if(!name){name=(await prompt("name: ")).trim();if(!name)return console.log("A name is required.")}const url=(await prompt("url: ")).trim();const headers=await promptHeaders();const service=(await prompt('service (optional, e.g. "stripe"): ')).trim();const notes=(await prompt("notes (optional): ")).trim();const r=await lumenSecretsCall("set","POST",token,{name,service,label:name,value:JSON.stringify({url,headers}),notes});if(!r.ok){console.log("Failed: "+(r.error||"unknown error"));return}const entries=readVaultFile(projectRoot).filter(e=>e.name!==name);entries.push({name,objectId:r.objectId});writeVaultFile(projectRoot,entries);console.log(`Saved "${name}" to the vault (api_index ${entries.length-1}). Nothing sensitive was written to config.json.`)}exports.apiSet=apiSet;async function apiUpdate(name){const projectRoot=process.cwd();const token=await login();if(!token)return;if(!name){name=(await prompt("name: ")).trim()}const entries=readVaultFile(projectRoot);const entry=entries.find(e=>e.name===name);if(!entry){console.log(`No vault entry named "${name}" in this project.`);return}const url=(await prompt("new url (blank to keep current): ")).trim();const changeHeaders=(await prompt("update headers? (y/N): ")).trim().toLowerCase()==="y";const headers=changeHeaders?await promptHeaders():void 0;if(!url&&!changeHeaders){console.log("Nothing to update.");return}const current=await lumenSecretsCall("resolve","GET",token,null,{id:entry.objectId});if(!current.ok){console.log("Could not read the current value: "+(current.error||"unknown error"));return}let currentParsed;try{currentParsed=JSON.parse(current.value)}catch(e){currentParsed={url:"",headers:{}}}const merged={url:url||currentParsed.url,headers:headers||currentParsed.headers};const r=await lumenSecretsCall("update","POST",token,{objectId:entry.objectId,value:JSON.stringify(merged)});console.log(r.ok?`Updated "${name}".`:"Failed: "+(r.error||"unknown error"))}exports.apiUpdate=apiUpdate;async function apiGet(name){const projectRoot=process.cwd();const token=await login();if(!token)return;if(!name)name=(await prompt("name: ")).trim();const entries=readVaultFile(projectRoot);const entry=entries.find(e=>e.name===name);if(!entry){console.log(`No vault entry named "${name}" in this project.`);return}const r=await lumenSecretsCall("resolve","GET",token,null,{id:entry.objectId});if(!r.ok){console.log("Failed: "+(r.error||"unknown error"));return}let parsed;try{parsed=JSON.parse(r.value)}catch(e){parsed={url:r.value,headers:{}}}console.log(JSON.stringify({name,url:parsed.url,headers:parsed.headers},null,2))}exports.apiGet=apiGet;async function apiList(){const projectRoot=process.cwd();const token=await login();if(!token)return;const entries=readVaultFile(projectRoot);if(entries.length===0){console.log("No vault-managed APIs in this project yet \u2014 try `lm api set`.");return}entries.forEach((e,i)=>console.log(`${i}: ${e.name} (${e.objectId})`))}exports.apiList=apiList;async function apiRemove(name){const projectRoot=process.cwd();const token=await login();if(!token)return;if(!name)name=(await prompt("name: ")).trim();const entries=readVaultFile(projectRoot);const entry=entries.find(e=>e.name===name);if(!entry){console.log(`No vault entry named "${name}" in this project.`);return}const r=await lumenSecretsCall("remove","POST",token,{objectId:entry.objectId});if(!r.ok){console.log("Failed: "+(r.error||"unknown error"));return}writeVaultFile(projectRoot,entries.filter(e=>e.name!==name));console.log(`Removed "${name}".`)}exports.apiRemove=apiRemove;async function resolveVaultApis(projectRoot){const entries=readVaultFile(projectRoot);if(entries.length===0)return[];const token=getStoredToken(projectRoot);if(!token){console.log("Vault entries exist but no ROXYON_TOKEN is available \u2014 run `lm login`. Skipping vault-managed APIs for this build.");return[]}const resolved=[];for(const entry of entries){try{const r=await lumenSecretsCall("resolve","GET",token,null,{id:entry.objectId});if(!r.ok){console.log(`Could not resolve vault entry "${entry.name}": ${r.error||"unknown error"} \u2014 skipping it for this build.`);continue}const parsed=JSON.parse(r.value);resolved.push({baseURL:parsed.url,headers:parsed.headers})}catch(e){console.log(`Could not resolve vault entry "${entry.name}": ${e.message} \u2014 skipping it for this build.`)}}return resolved}exports.resolveVaultApis=resolveVaultApis;
1
+ const fs=require("fs");const path=require("path");const readline=require("readline");const fetch=require("node-fetch");const LUMEN_SECRETS_BASE="https://www.beaapis.com/1/_lumen_secrets";const TOKEN_FILE_NAME=".roxyon-token.local";const VAULT_FILE_NAME=".lumen-vault.local";function tokenFilePath(projectRoot){return path.join(projectRoot,TOKEN_FILE_NAME)}function vaultFilePath(projectRoot){return path.join(projectRoot,VAULT_FILE_NAME)}function getStoredToken(projectRoot){if(process.env.ROXYON_TOKEN)return process.env.ROXYON_TOKEN.trim();const p=tokenFilePath(projectRoot);if(fs.existsSync(p)){const t=fs.readFileSync(p,"utf8").trim();if(t)return t}return _auth().storedPat()}async function getToken(projectRoot){const t=getStoredToken(projectRoot);if(t)return t;return _auth().freshPat()}function _auth(){return require(path.join(__dirname,"copilot","auth.js"))}async function login(){const projectRoot=process.cwd();const existing=await getToken(projectRoot);if(existing)return existing;console.log("Not signed in to Roxyon yet. Starting `lm login`...");try{await _auth().login({argv:process.argv.slice(3)})}catch(e){console.log("lm login failed: "+e.message);return null}const after=await getToken(projectRoot);if(!after)console.log("Sign-in did not complete. Run `lm login` and try again.");return after}exports.login=login;exports.getStoredToken=getStoredToken;function prompt(question){const rl=readline.createInterface({input:process.stdin,output:process.stdout});return new Promise(resolve=>rl.question(question,answer=>{rl.close();resolve(answer)}))}async function promptHeaders(){const headers={};console.log("Enter headers one at a time as `Name: value` (blank line to finish):");for(;;){const line=(await prompt(" header: ")).trim();if(line==="")break;const idx=line.indexOf(":");if(idx===-1){console.log(" (expected `Name: value`, skipped)");continue}headers[line.slice(0,idx).trim()]=line.slice(idx+1).trim()}return headers}async function lumenSecretsCall(pathSeg,method,token,body,query){let url=LUMEN_SECRETS_BASE+"/"+pathSeg;if(query){const qs=Object.keys(query).map(k=>encodeURIComponent(k)+"="+encodeURIComponent(query[k])).join("&");if(qs)url+="?"+qs}const res=await fetch(url,{method,headers:{Authorization:"Bearer "+token,"Content-Type":"application/json"},body:method==="GET"?void 0:JSON.stringify(body||{})});const text=await res.text();try{return JSON.parse(text)}catch(e){return{ok:false,error:"non-JSON response: "+text.slice(0,200)}}}function readVaultFile(projectRoot){const p=vaultFilePath(projectRoot);if(!fs.existsSync(p))return[];try{const parsed=JSON.parse(fs.readFileSync(p,"utf8"));return Array.isArray(parsed)?parsed:[]}catch(e){return[]}}function writeVaultFile(projectRoot,entries){fs.writeFileSync(vaultFilePath(projectRoot),JSON.stringify(entries,null,2)+"\n")}async function apiSet(name){const projectRoot=process.cwd();const token=await login();if(!token)return;if(!name){name=(await prompt("name: ")).trim();if(!name)return console.log("A name is required.")}const url=(await prompt("url: ")).trim();const headers=await promptHeaders();const service=(await prompt('service (optional, e.g. "stripe"): ')).trim();const notes=(await prompt("notes (optional): ")).trim();const r=await lumenSecretsCall("set","POST",token,{name,service,label:name,value:JSON.stringify({url,headers}),notes});if(!r.ok){console.log("Failed: "+(r.error||"unknown error"));return}const entries=readVaultFile(projectRoot).filter(e=>e.name!==name);entries.push({name,objectId:r.objectId});writeVaultFile(projectRoot,entries);console.log(`Saved "${name}" to the vault (api_index ${entries.length-1}). Nothing sensitive was written to config.json.`)}exports.apiSet=apiSet;async function apiUpdate(name){const projectRoot=process.cwd();const token=await login();if(!token)return;if(!name){name=(await prompt("name: ")).trim()}const entries=readVaultFile(projectRoot);const entry=entries.find(e=>e.name===name);if(!entry){console.log(`No vault entry named "${name}" in this project.`);return}const url=(await prompt("new url (blank to keep current): ")).trim();const changeHeaders=(await prompt("update headers? (y/N): ")).trim().toLowerCase()==="y";const headers=changeHeaders?await promptHeaders():void 0;if(!url&&!changeHeaders){console.log("Nothing to update.");return}const current=await lumenSecretsCall("resolve","GET",token,null,{id:entry.objectId});if(!current.ok){console.log("Could not read the current value: "+(current.error||"unknown error"));return}let currentParsed;try{currentParsed=JSON.parse(current.value)}catch(e){currentParsed={url:"",headers:{}}}const merged={url:url||currentParsed.url,headers:headers||currentParsed.headers};const r=await lumenSecretsCall("update","POST",token,{objectId:entry.objectId,value:JSON.stringify(merged)});console.log(r.ok?`Updated "${name}".`:"Failed: "+(r.error||"unknown error"))}exports.apiUpdate=apiUpdate;async function apiGet(name){const projectRoot=process.cwd();const token=await login();if(!token)return;if(!name)name=(await prompt("name: ")).trim();const entries=readVaultFile(projectRoot);const entry=entries.find(e=>e.name===name);if(!entry){console.log(`No vault entry named "${name}" in this project.`);return}const r=await lumenSecretsCall("resolve","GET",token,null,{id:entry.objectId});if(!r.ok){console.log("Failed: "+(r.error||"unknown error"));return}let parsed;try{parsed=JSON.parse(r.value)}catch(e){parsed={url:r.value,headers:{}}}console.log(JSON.stringify({name,url:parsed.url,headers:parsed.headers},null,2))}exports.apiGet=apiGet;async function apiList(){const projectRoot=process.cwd();const token=await login();if(!token)return;const entries=readVaultFile(projectRoot);if(entries.length===0){console.log("No vault-managed APIs in this project yet \u2014 try `lm api set`.");return}entries.forEach((e,i)=>console.log(`${i}: ${e.name} (${e.objectId})`))}exports.apiList=apiList;async function apiRemove(name){const projectRoot=process.cwd();const token=await login();if(!token)return;if(!name)name=(await prompt("name: ")).trim();const entries=readVaultFile(projectRoot);const entry=entries.find(e=>e.name===name);if(!entry){console.log(`No vault entry named "${name}" in this project.`);return}const r=await lumenSecretsCall("remove","POST",token,{objectId:entry.objectId});if(!r.ok){console.log("Failed: "+(r.error||"unknown error"));return}writeVaultFile(projectRoot,entries.filter(e=>e.name!==name));console.log(`Removed "${name}".`)}exports.apiRemove=apiRemove;async function resolveVaultApis(projectRoot){const entries=readVaultFile(projectRoot);if(entries.length===0)return[];const token=await getToken(projectRoot);if(!token){console.log("Vault entries exist but no ROXYON_TOKEN is available \u2014 run `lm login`. Skipping vault-managed APIs for this build.");return[]}const resolved=[];for(const entry of entries){try{const r=await lumenSecretsCall("resolve","GET",token,null,{id:entry.objectId});if(!r.ok){console.log(`Could not resolve vault entry "${entry.name}": ${r.error||"unknown error"} \u2014 skipping it for this build.`);continue}const parsed=JSON.parse(r.value);resolved.push({baseURL:parsed.url,headers:parsed.headers})}catch(e){console.log(`Could not resolve vault entry "${entry.name}": ${e.message} \u2014 skipping it for this build.`)}}return resolved}exports.resolveVaultApis=resolveVaultApis;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lmjs/cli",
3
- "version": "2.5.9",
3
+ "version": "2.6.0",
4
4
  "description": "Command line interface for LumenJS — scaffold, develop, and build LumenJS applications. LumenJS is a tiny, fast reactive JavaScript framework for building web applications and interfaces with standard HTML, CSS, and JavaScript.",
5
5
  "bin": {
6
6
  "lm": "./dist/bin/commands.js",
@@ -52,7 +52,7 @@
52
52
  "ora": "^6.1.2",
53
53
  "semver": "^7.3.7",
54
54
  "ws": "^8.8.1",
55
- "@lmjs/core": "2.5.9"
55
+ "@lmjs/core": "2.6.0"
56
56
  },
57
57
  "engines": {
58
58
  "node": ">=18.0.0"
@@ -66,7 +66,7 @@
66
66
  "access": "public"
67
67
  },
68
68
  "bugs": {
69
- "url": "https://github.com/bea-technology/lumenjs/issues"
69
+ "url": "https://github.com/bea-technology/lumen.js/issues"
70
70
  },
71
71
  "license": "MIT",
72
72
  "repository": {