typebulb 0.2.1 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +2 -1
  2. package/dist/index.js +16 -16
  3. package/package.json +4 -4
package/README.md CHANGED
@@ -23,7 +23,7 @@ typebulb <file.bulb.md> Run a bulb
23
23
  typebulb . Find .bulb.md in current directory
24
24
  typebulb --watch <file> Watch for changes and reload
25
25
  typebulb --port 3333 <file> Custom port
26
- typebulb --headless <file> Serve without opening browser
26
+ typebulb --no-open <file> Don't auto-open browser
27
27
  typebulb --server <file> Run server.ts only, no web server
28
28
  ```
29
29
 
@@ -32,6 +32,7 @@ typebulb --server <file> Run server.ts only, no web server
32
32
  - **Hot reload** — `--watch` recompiles on save and refreshes the browser
33
33
  - **Filesystem access** — `tb.fs.read()` and `tb.fs.write()` for local files
34
34
  - **Server-side code** — Add a `**server.ts**` section; exported functions become callable from the browser via `tb.server.<name>()` (e.g., `export async function query(...)` → `await tb.server.query(...)`)
35
+ - **Built-in server functions** — `tb.server.log(...)` prints to the CLI's stdout, no `**server.ts**` needed. User exports override built-ins.
35
36
  - **Console bulbs** — Bulbs with only `**server.ts**` (no `**code.tsx**`) run directly in Node and print to stdout. Use `--server` to force this mode for any bulb.
36
37
  - **Auto-installs packages** — npm dependencies in server code are installed automatically
37
38
  - **Env files** — `.env` and `.env.local` auto-loaded from cwd
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import*as w from"fs/promises";import{readFileSync as Fe,existsSync as ie}from"fs";import*as g from"path";import{pathToFileURL as Ae}from"url";import{execFile as Be}from"child_process";import{promisify as Ve}from"util";import{EventEmitter as L}from"events";var me={code:{path:"code.tsx",language:"typescript"},css:{path:"styles.css",language:"css"},html:{path:"index.html",language:"html"},config:{path:"config.json",language:"json"},notes:{path:"notes.md",language:"markdown"},data:{path:"data.txt",language:"text"},infer:{path:"infer.md",language:"markdown"},insight:{path:"insight.json",language:"json"},server:{path:"server.ts",language:"typescript"}};function k(s){try{let e=s.split(`
3
- `),t=0;if(e[t]?.trim()!=="---")return null;t++;let r=[];for(;t<e.length&&e[t]?.trim()!=="---";)r.push(e[t]),t++;if(e[t]?.trim()!=="---")return null;t++;let n=ge(r);if(!n)return null;let o=new Map;for(;t<e.length;){let l=e[t]?.trim()?.match(/^\*\*(.+)\*\*$/);if(l){let u=l[1].trim();for(t++;t<e.length&&e[t]?.trim()==="";)t++;let c=e[t]?.match(/^(`{3,})(\w*)\s*$/);if(!c){t++;continue}let a=c[1];t++;let d=[];for(;t<e.length&&!e[t]?.match(new RegExp(`^${a}\\s*$`));)d.push(e[t]),t++;t++,o.set(u,d.join(`
4
- `))}else t++}return{frontmatter:n,files:o}}catch{return null}}function ge(s){let e={};for(let t of s){let r=t.indexOf(":");if(r===-1)continue;let n=t.slice(0,r).trim(),o=t.slice(r+1).trim();switch(n){case"format":e.format=o;break;case"name":e.name=we(o);break}}return!e.format?.startsWith("typebulb")||!e.name?null:e}function we(s){return s.startsWith('"')&&s.endsWith('"')?s.slice(1,-1).replace(/\\"/g,'"'):s.startsWith("'")&&s.endsWith("'")?s.slice(1,-1):s}function C(s){let e=t=>s.files.get(me[t].path)||"";return{name:s.frontmatter.name,code:e("code"),css:e("css"),html:e("html"),config:e("config"),notes:e("notes"),data:e("data"),infer:e("infer"),insight:e("insight"),server:e("server")}}function ve(s){try{return JSON.parse(s),!0}catch{}return!!(/^\s*<[\s\S]*>/.test(s)||/^---\s*$/m.test(s)||/^\w[\w\s]*:[ \t]/m.test(s))}function J(s){let e=s.trim();return e?ve(e)?[e]:s.split(/\n\n\n+/).map(t=>t.trim()).filter(Boolean):[]}function O(s){if(!s.trim())return{};try{return JSON.parse(s)}catch{return{}}}import{transform as q}from"sucrase";function be(s,e){try{let{code:t}=q(s,{transforms:["typescript","jsx"],jsxRuntime:"automatic",jsxImportSource:e.jsxImportSource||"react",production:!0});return{code:t}}catch(t){return{code:"",error:String(t)}}}function z(s,e={}){return be(s,e)}function G(s){try{let{code:e}=q(s,{transforms:["typescript"]});return{code:e}}catch(e){return{code:"",error:String(e)}}}var P="https://esm.sh",R="https://cdn.jsdelivr.net/npm/",F="https://data.jsdelivr.com/v1/package/npm/";function K(s){let e=(s||"").replace(/^\/+/,"").replace(/\/+$/,"");return e?e.split("/"):[]}var h=class s{constructor(e,t,r){let n=typeof e=="string"?s.parse(e):e;this.name=n.name,this.version=x(t??n.version),this.subpath=x(r??n.subpath)}static parse(e){let t=K(e||"");if(!t.length)return new s({name:""});if(t[0].startsWith("@")){let n=t[0],[o,i]=Y(t[1]??""),l=x(t.slice(2).join("/"));return new s({name:`${n}/${o}`,version:i,subpath:l})}else{let[n,o]=Y(t[0]),i=x(t.slice(1).join("/"));return new s({name:n,version:o,subpath:i})}}static fromUrl(e){try{let t=new URL(e),r=new URL(P).host,n=new URL(R).host;if(t.host===r){let o=K(t.pathname.replace(/^\/v\d+\//,"/"));if(!o.length)return;let i=o[0].startsWith("@")?`${o[0]}/${o[1]??""}`:o[0];return s.parse(i)}if(t.host===n){let o=t.pathname.split("/npm/")[1];if(!o)return;let i=o.split("/")[0]||"";return s.parse(i)}return}catch{return}}static versionFromUrl(e){return s.fromUrl(e)?.version}format(){let e=this.version?`${this.name}@${this.version}`:this.name;return this.subpath?`${e}/${this.subpath}`:e}root(){return this.name}static rootOf(e){return s.parse(e).name}withVersion(e){return new s({name:this.name,version:x(e),subpath:this.subpath})}withPreferredVersion(e,t){let r=e||t;return r?this.withVersion(r):this}static isBare(e){if(!e||e.startsWith(".")||e.startsWith("/"))return!1;let t=e.toLowerCase();return!t.startsWith("http://")&&!t.startsWith("https://")}},x=s=>s&&s.length?s:void 0,Y=s=>{let e=s.indexOf("@");return e<0?[s,void 0]:[s.slice(0,e),x(s.slice(e+1))]};async function v(s){try{return await s()}catch{return}}var I=class{constructor(e,t){this.cache=e,this.http=t,this.esmHost=P,this.jsDelivrBase=R,this.jsDelivrMeta=F,this.pinMs=1e4,this.versionsIndexMs=1440*60*1e3,this.metaTtlMs=10080*60*1e3,this.pinCache=new Map}normalizeRelative(e){let t=e||"";return t.startsWith("./")?t.slice(2):t.replace(/^\/+/,"")}ensureLeadingDotSlash(e){return e.startsWith("./")?e:`./${e}`}baseDir(e){let t=typeof e=="string"?new h(e):e;return`${this.jsDelivrBase}${t.name}${t.version?`@${t.version}`:""}/`}file(e,t){return new URL(this.normalizeRelative(t),this.baseDir(e)).toString()}packageJson(e){return this.file(e,"package.json")}buildEsmUrl(e,t={}){let{target:r="es2022",bundle:n=!1,external:o}=t,i=new URLSearchParams({target:r});return n&&i.append("bundle",""),o?.length&&i.append("external",o.join(",")),`${this.esmHost}/${e}?${i.toString()}`}async pinEsmUrl(e,t="es2022"){let r=this.buildEsmUrl(e,{target:t}),n=await v(()=>this.http.head(r));return n?.ok?n.url||r:void 0}async resolveExactVersion(e){let t=Date.now(),r=this.pinCache.get(e);if(r&&t-r.ts<this.pinMs)return r.value;let n=await this.tryResolveFromUrls([this.buildEsmUrl(e),`${this.esmHost}/${e}`]);return this.pinCache.set(e,{value:n,ts:t}),n}async tryResolveFromUrls(e){for(let t of e){let r=await v(()=>this.http.head(t)),n=this.parseVersionFromUrl(r?.url||t);if(n)return n}}async fetchVersionsIndex(e){if(await this.cache.isNegative(e))return;let t=await this.cache.getIndex(e);if(t&&Date.now()-t.updatedAt<this.versionsIndexMs)return{versions:t.versions,distTags:t.distTags};let r=await v(()=>this.http.getJson(`${this.jsDelivrMeta}${encodeURIComponent(e)}`));if(!r?.versions?.length){await this.cache.recordNegative(e);return}await this.cache.clearNegative(e);let n=r.distTags&&Object.keys(r.distTags).length?r.distTags:void 0;return await this.cache.setIndex(e,r.versions,n),r}parseVersionFromUrl(e){let t=h.fromUrl(e)?.version;return t&&/\d+\.\d+\.\d+/.test(t)?t:void 0}async fetchPackageMeta(e,t){let r=await this.cache.getMeta(e,t);if(r&&Date.now()-r.updatedAt<this.metaTtlMs){let{dependencies:a,peerDependencies:d,peerDependenciesMeta:p}=r;return{name:e,version:t,dependencies:a,peerDependencies:d,peerDependenciesMeta:p}}let n=this.packageJson(new h(`${e}@${t}`)),o=await v(()=>this.http.getJson(n));if(!o)return;let i=a=>a&&Object.keys(a).length?a:void 0,l=i(o.dependencies),u=i(o.peerDependencies),c=i(o.peerDependenciesMeta);return await this.cache.setMeta(e,t,l,u,c),{name:e,version:t,dependencies:l,peerDependencies:u,peerDependenciesMeta:c}}};var Z=s=>s.startsWith("@types/"),$=s=>Object.keys(s?.peerDependencies||{}).filter(e=>!Z(e)),A=s=>Object.keys(s?.dependencies||{}).filter(e=>!Z(e)),ye=s=>$(s).filter(e=>!s?.peerDependenciesMeta?.[e]?.optional),j=class{constructor(e){this.cdn=e}async resolve(e,t){let r=await this.fetchMeta(e),{allRoots:n,autoAddedPeers:o}=await this.expandWithPeers(r,t),i=this.computeFlags(n);return{allRoots:n,flags:i,autoAddedPeers:o}}async fetchMeta(e){return Promise.all(e.map(async({name:t,version:r})=>({name:t,version:r,meta:await this.cdn.fetchPackageMeta(t,r)})))}async expandWithPeers(e,t){let r=new Map(e.map(o=>[o.name,o])),n=[];for(let o of e)for(let i of ye(o.meta))!r.has(i)&&!n.some(l=>l.name===i)&&n.push({name:i,requiredBy:o.name});for(let{name:o,requiredBy:i}of n)try{let l=await t(o),u=await this.cdn.fetchPackageMeta(o,l);r.set(o,{name:o,version:l,meta:u})}catch(l){console.warn(`[typebulb] Failed to resolve peer "${o}" for "${i}":`,l)}return{allRoots:[...r.values()],autoAddedPeers:n.filter(o=>r.has(o.name))}}computeFlags(e){let t=new Set(e.flatMap(o=>$(o.meta))),r=new Map;for(let o of e)for(let i of A(o.meta))r.set(i,(r.get(i)||0)+1);let n=new Set([...r.entries()].filter(([,o])=>o>=2).map(([o])=>o));return new Map(e.map(o=>[o.name,{isPeerRoot:t.has(o.name),hasPeers:$(o.meta).length>0,isSharedDep:n.has(o.name)}]))}};var M=class{constructor(e,t,r){this.cache=e,this.cdn=t,this.semver=r}selectVersionFromIndex(e,t,r){return this.semver.selectBestVersion(e,{range:t,distTags:r})}async learnExactVersion(e){let t=await v(()=>this.cdn.fetchVersionsIndex(e));if(t?.versions?.length){let r=this.semver.selectBestVersion(t.versions,{distTags:t.distTags});if(r)return r}return this.cdn.resolveExactVersion(e)}async resolveExactForRoot(e,t){if(!t)return this.learnExactVersion(e);let r=await this.cache.getPinnedExact(e,t);if(r){if(this.semver.isExactVersion(r))return r;console.warn("[versionResolver] Rejecting invalid cached version for",e,":",r)}let n=await v(()=>this.cdn.fetchVersionsIndex(e));if(n?.versions?.length){let i=this.selectVersionFromIndex(n.versions,t,n.distTags);if(i){if(this.semver.isExactVersion(i))return await this.cache.setPinnedExact(e,t,i),i}else{console.warn("[versionResolver] Invalidating stale versions cache for",e,"- range",t,"not satisfied"),await this.cache.invalidateVersionsCache(e);let l=await v(()=>this.cdn.fetchVersionsIndex(e));if(l?.versions?.length){let u=this.selectVersionFromIndex(l.versions,t,l.distTags);if(u&&this.semver.isExactVersion(u))return await this.cache.setPinnedExact(e,t,u),u}}}let o=await this.cdn.resolveExactVersion(`${e}@${t}`);if(o&&this.semver.isExactVersion(o))return await this.cache.setPinnedExact(e,t,o),o}async effectivePackage(e,t){let r=new h(e),n=r.root(),o=t[n],i=o?await v(()=>this.cache.getPinnedExact(n,o))??await v(()=>this.resolveExactForRoot(n,o)):void 0;return{effectivePackage:i?r.withVersion(i).format():e,root:n,range:o,pinned:i}}};import{init as xe,parse as Se}from"es-module-lexer";var S=class s{constructor(e,t,r,n){this.version=e,this.cdn=t,this.peer=r,this.cache=n}extractImportsSync(e){let t=new Set;for(let r of s.importPatterns){r.lastIndex=0;for(let n of e.matchAll(r))h.isBare(n[1])&&t.add(n[1])}return Array.from(t)}async extractImports(e){let t=new Set,r=n=>{h.isBare(n)&&t.add(n)};try{await xe;let[n]=Se(e);n.forEach(o=>r(e.slice(o.s,o.e).trim()))}catch{return this.extractImportsSync(e)}return Array.from(t)}async buildImportMap(e,t){let r=await this.extractImports(e),n=[...new Set(r.map(h.rootOf))],o=await Promise.all(n.map(async a=>({name:a,version:await this.resolveVersion(a,t)}))),{allRoots:i,flags:l,autoAddedPeers:u}=await this.peer.resolve(o,a=>this.resolveVersion(a,t)),c=this.buildEntries([...r,...u.map(a=>a.name)],i,l,t);return{importMap:{imports:Object.fromEntries(c)},prefetchUrls:c.map(([,a])=>a)}}async resolveVersion(e,t){let r=t[e],n=await this.version.resolveExactForRoot(e,r);if(!n){let o=r?`${e}@${r}`:e,i=await v(()=>this.cdn.pinEsmUrl(o));if(!i)throw new Error(`Cannot resolve version for ${e} - network may be unavailable.`);n=h.versionFromUrl(i),n&&r&&await v(()=>this.cache.setPinnedExact(e,r,n))}if(!n)throw new Error(`Cannot resolve version for ${e}`);return n}buildEntries(e,t,r,n){let o=new Map(t.map(p=>[p.name,p])),i=p=>{let m=o.get(h.rootOf(p));return new h(p).withPreferredVersion(m.version,n[m.name]).format()},l=new Set([...r.entries()].filter(([,p])=>p.isPeerRoot||p.isSharedDep).map(([p])=>p)),u=new Set(e.filter(p=>p!==h.rootOf(p)).map(h.rootOf)),c=[],a=new Set,d=new Set(e.filter(p=>p===h.rootOf(p)));for(let p of e){let m=h.rootOf(p),f=o.get(m),{isPeerRoot:b,hasPeers:T,isSharedDep:pe}=r.get(m),de=u.has(m),ue=p!==m,fe=!(b||pe)&&(de||!T),D=this.singletonDepsOf(f,l),he=ue&&d.has(m)?[...D,m]:D.length?D:void 0;c.push([p,this.cdn.buildEsmUrl(i(p),{bundle:fe,external:he})]),a.add(p)}for(let p of l)o.has(p)&&(a.has(p)||c.push([p,this.cdn.buildEsmUrl(i(p),{})]),c.push([`${p}/`,`${this.cdn.esmHost}/${i(p)}/`]));return c}singletonDepsOf(e,t){return[...new Set([...$(e.meta),...A(e.meta)])].filter(r=>t.has(r))}};S.importPatterns=[/\bimport\s+(?:[^'";]*?from\s*)?['"]([^'"]+)['"]/g,/\bexport\s+[^'";]*?from\s*['"]([^'"]+)['"]/g];import{gt as X,satisfies as Ee,maxSatisfying as B,major as Pe,prerelease as Re,rsort as Ie,valid as $e}from"semver";var _=class{cmp(e,t){return e===t?0:X(e,t)?1:X(t,e)?-1:0}satisfies(e,t){return!e||!e.trim()?!0:!!Ee(t,e,{includePrerelease:!0})}pickMaxSatisfying(e,t){if(!e?.length)return;let r=B(e,t,{includePrerelease:!0});return r===null?void 0:r}pickLatest(e){return e?.length?Ie(e)[0]:void 0}selectBestVersion(e,t){if(!e?.length)return;let r=t?.range?.trim()||"*",n=t?.preferStable??!0,o=t?.distTags?.latest;if(o&&e.includes(o)&&this.satisfies(r,o))return o;if(n){let l=B(e,r,{includePrerelease:!1});if(l)return l}return B(e,r,{includePrerelease:!0})??void 0}majorOf(e){return Pe(e)}isPrerelease(e){return Re(e)!==null}isExactVersion(e){return $e(e)!==null}},V=new _;function Q(s,e){let t=new I(s,e),r=new j(t),n=new M(s,t,V);return{packageService:new S(n,t,r,s),versionResolver:n,cdnClient:t,peerResolver:r}}var W=class{pins=new Map;indexes=new Map;negatives=new Set;meta=new Map;async getPinnedExact(e,t){return this.pins.get(`${e}@${t}`)}async setPinnedExact(e,t,r){this.pins.set(`${e}@${t}`,r)}async getIndex(e){return this.indexes.get(e)}async setIndex(e,t,r){this.indexes.set(e,{versions:t,distTags:r,updatedAt:Date.now()})}async invalidateVersionsCache(e){this.indexes.delete(e)}async isNegative(e){return this.negatives.has(e)}async recordNegative(e){this.negatives.add(e)}async clearNegative(e){this.negatives.delete(e)}async getMeta(e,t){return this.meta.get(`${e}@${t}`)}async setMeta(e,t,r,n,o){this.meta.set(`${e}@${t}`,{dependencies:r,peerDependencies:n,peerDependenciesMeta:o,updatedAt:Date.now()})}},je={async getJson(s){try{let e=await fetch(s,{redirect:"follow"});return e.ok?await e.json():void 0}catch{return}},async head(s){try{let e=await fetch(s,{method:"HEAD",redirect:"follow"});return{ok:e.ok,url:e.url}}catch{return}}},Me=new W,{packageService:ee,versionResolver:_t,cdnClient:Dt,peerResolver:Ot}=Q(Me,je);var te=`
2
+ import*as v from"fs/promises";import{readFileSync as Fe,existsSync as ae}from"fs";import*as w from"path";import{pathToFileURL as Ae}from"url";import{execFile as Be}from"child_process";import{promisify as Ve}from"util";import{EventEmitter as H}from"events";var me={code:{path:"code.tsx",language:"typescript"},css:{path:"styles.css",language:"css"},html:{path:"index.html",language:"html"},config:{path:"config.json",language:"json"},notes:{path:"notes.md",language:"markdown"},data:{path:"data.txt",language:"text"},infer:{path:"infer.md",language:"markdown"},insight:{path:"insight.json",language:"json"},server:{path:"server.ts",language:"typescript"}};function O(s){try{let e=s.split(`
3
+ `),t=0;if(e[t]?.trim()!=="---")return null;t++;let r=[];for(;t<e.length&&e[t]?.trim()!=="---";)r.push(e[t]),t++;if(e[t]?.trim()!=="---")return null;t++;let n=ge(r);if(!n)return null;let o=new Map;for(;t<e.length;){let l=e[t]?.trim()?.match(/^\*\*(.+)\*\*$/);if(l){let d=l[1].trim();for(t++;t<e.length&&e[t]?.trim()==="";)t++;let u=e[t]?.match(/^(`{3,})(\w*)\s*$/);if(!u){t++;continue}let a=u[1];t++;let p=[];for(;t<e.length&&!e[t]?.match(new RegExp(`^${a}\\s*$`));)p.push(e[t]),t++;t++,o.set(d,p.join(`
4
+ `))}else t++}return{frontmatter:n,files:o}}catch{return null}}function ge(s){let e={};for(let t of s){let r=t.indexOf(":");if(r===-1)continue;let n=t.slice(0,r).trim(),o=t.slice(r+1).trim();switch(n){case"format":e.format=o;break;case"name":e.name=we(o);break}}return!e.format?.startsWith("typebulb")||!e.name?null:e}function we(s){return s.startsWith('"')&&s.endsWith('"')?s.slice(1,-1).replace(/\\"/g,'"'):s.startsWith("'")&&s.endsWith("'")?s.slice(1,-1):s}function k(s){let e=t=>s.files.get(me[t].path)||"";return{name:s.frontmatter.name,code:e("code"),css:e("css"),html:e("html"),config:e("config"),notes:e("notes"),data:e("data"),infer:e("infer"),insight:e("insight"),server:e("server")}}function ve(s){try{return JSON.parse(s),!0}catch{}return!!(/^\s*<[\s\S]*>/.test(s)||/^---\s*$/m.test(s)||/^\w[\w\s]*:[ \t]/m.test(s))}function q(s){let e=s.trim();return e?ve(e)?[e]:s.split(/\n\n\n+/).map(t=>t.trim()).filter(Boolean):[]}function F(s){if(!s.trim())return{};try{return JSON.parse(s)}catch{return{}}}import{transform as z}from"sucrase";function be(s,e){try{let{code:t}=z(s,{transforms:["typescript","jsx"],jsxRuntime:"automatic",jsxImportSource:e.jsxImportSource||"react",production:!0});return{code:t}}catch(t){return{code:"",error:String(t)}}}function G(s,e={}){return be(s,e)}function K(s){try{let{code:e}=z(s,{transforms:["typescript"]});return{code:e}}catch(e){return{code:"",error:String(e)}}}var R="https://esm.sh",I="https://cdn.jsdelivr.net/npm/",A="https://data.jsdelivr.com/v1/package/npm/";function Y(s){let e=(s||"").replace(/^\/+/,"").replace(/\/+$/,"");return e?e.split("/"):[]}var h=class s{constructor(e,t,r){let n=typeof e=="string"?s.parse(e):e;this.name=n.name,this.version=S(t??n.version),this.subpath=S(r??n.subpath)}static parse(e){let t=Y(e||"");if(!t.length)return new s({name:""});if(t[0].startsWith("@")){let n=t[0],[o,i]=Z(t[1]??""),l=S(t.slice(2).join("/"));return new s({name:`${n}/${o}`,version:i,subpath:l})}else{let[n,o]=Z(t[0]),i=S(t.slice(1).join("/"));return new s({name:n,version:o,subpath:i})}}static fromUrl(e){try{let t=new URL(e),r=new URL(R).host,n=new URL(I).host;if(t.host===r){let o=Y(t.pathname.replace(/^\/v\d+\//,"/"));if(!o.length)return;let i=o[0].startsWith("@")?`${o[0]}/${o[1]??""}`:o[0];return s.parse(i)}if(t.host===n){let o=t.pathname.split("/npm/")[1];if(!o)return;let i=o.split("/")[0]||"";return s.parse(i)}return}catch{return}}static versionFromUrl(e){return s.fromUrl(e)?.version}format(){let e=this.version?`${this.name}@${this.version}`:this.name;return this.subpath?`${e}/${this.subpath}`:e}root(){return this.name}static rootOf(e){return s.parse(e).name}withVersion(e){return new s({name:this.name,version:S(e),subpath:this.subpath})}withPreferredVersion(e,t){let r=e||t;return r?this.withVersion(r):this}static isBare(e){if(!e||e.startsWith(".")||e.startsWith("/"))return!1;let t=e.toLowerCase();return!t.startsWith("http://")&&!t.startsWith("https://")}},S=s=>s&&s.length?s:void 0,Z=s=>{let e=s.indexOf("@");return e<0?[s,void 0]:[s.slice(0,e),S(s.slice(e+1))]};async function b(s){try{return await s()}catch{return}}var $=class{constructor(e,t){this.cache=e,this.http=t,this.esmHost=R,this.jsDelivrBase=I,this.jsDelivrMeta=A,this.pinMs=1e4,this.versionsIndexMs=1440*60*1e3,this.metaTtlMs=10080*60*1e3,this.pinCache=new Map}normalizeRelative(e){let t=e||"";return t.startsWith("./")?t.slice(2):t.replace(/^\/+/,"")}ensureLeadingDotSlash(e){return e.startsWith("./")?e:`./${e}`}baseDir(e){let t=typeof e=="string"?new h(e):e;return`${this.jsDelivrBase}${t.name}${t.version?`@${t.version}`:""}/`}file(e,t){return new URL(this.normalizeRelative(t),this.baseDir(e)).toString()}packageJson(e){return this.file(e,"package.json")}buildEsmUrl(e,t={}){let{target:r="es2022",bundle:n=!1,external:o}=t,i=new URLSearchParams({target:r});return n&&i.append("bundle",""),o?.length&&i.append("external",o.join(",")),`${this.esmHost}/${e}?${i.toString()}`}async pinEsmUrl(e,t="es2022"){let r=this.buildEsmUrl(e,{target:t}),n=await b(()=>this.http.head(r));return n?.ok?n.url||r:void 0}async resolveExactVersion(e){let t=Date.now(),r=this.pinCache.get(e);if(r&&t-r.ts<this.pinMs)return r.value;let n=await this.tryResolveFromUrls([this.buildEsmUrl(e),`${this.esmHost}/${e}`]);return this.pinCache.set(e,{value:n,ts:t}),n}async tryResolveFromUrls(e){for(let t of e){let r=await b(()=>this.http.head(t)),n=this.parseVersionFromUrl(r?.url||t);if(n)return n}}async fetchVersionsIndex(e){if(await this.cache.isNegative(e))return;let t=await this.cache.getIndex(e);if(t&&Date.now()-t.updatedAt<this.versionsIndexMs)return{versions:t.versions,distTags:t.distTags};let r=await b(()=>this.http.getJson(`${this.jsDelivrMeta}${encodeURIComponent(e)}`));if(!r?.versions?.length){await this.cache.recordNegative(e);return}await this.cache.clearNegative(e);let n=r.distTags&&Object.keys(r.distTags).length?r.distTags:void 0;return await this.cache.setIndex(e,r.versions,n),r}parseVersionFromUrl(e){let t=h.fromUrl(e)?.version;return t&&/\d+\.\d+\.\d+/.test(t)?t:void 0}async fetchPackageMeta(e,t){let r=await this.cache.getMeta(e,t);if(r&&Date.now()-r.updatedAt<this.metaTtlMs){let{dependencies:a,peerDependencies:p,peerDependenciesMeta:c}=r;return{name:e,version:t,dependencies:a,peerDependencies:p,peerDependenciesMeta:c}}let n=this.packageJson(new h(`${e}@${t}`)),o=await b(()=>this.http.getJson(n));if(!o)return;let i=a=>a&&Object.keys(a).length?a:void 0,l=i(o.dependencies),d=i(o.peerDependencies),u=i(o.peerDependenciesMeta);return await this.cache.setMeta(e,t,l,d,u),{name:e,version:t,dependencies:l,peerDependencies:d,peerDependenciesMeta:u}}};var X=s=>s.startsWith("@types/"),j=s=>Object.keys(s?.peerDependencies||{}).filter(e=>!X(e)),B=s=>Object.keys(s?.dependencies||{}).filter(e=>!X(e)),ye=s=>j(s).filter(e=>!s?.peerDependenciesMeta?.[e]?.optional),M=class{constructor(e){this.cdn=e}async resolve(e,t){let r=await this.fetchMeta(e),{allRoots:n,autoAddedPeers:o}=await this.expandWithPeers(r,t),i=this.computeFlags(n);return{allRoots:n,flags:i,autoAddedPeers:o}}async fetchMeta(e){return Promise.all(e.map(async({name:t,version:r})=>({name:t,version:r,meta:await this.cdn.fetchPackageMeta(t,r)})))}async expandWithPeers(e,t){let r=new Map(e.map(o=>[o.name,o])),n=[];for(let o of e)for(let i of ye(o.meta))!r.has(i)&&!n.some(l=>l.name===i)&&n.push({name:i,requiredBy:o.name});for(let{name:o,requiredBy:i}of n)try{let l=await t(o),d=await this.cdn.fetchPackageMeta(o,l);r.set(o,{name:o,version:l,meta:d})}catch(l){console.warn(`[typebulb] Failed to resolve peer "${o}" for "${i}":`,l)}return{allRoots:[...r.values()],autoAddedPeers:n.filter(o=>r.has(o.name))}}computeFlags(e){let t=new Set(e.flatMap(o=>j(o.meta))),r=new Map;for(let o of e)for(let i of B(o.meta))r.set(i,(r.get(i)||0)+1);let n=new Set([...r.entries()].filter(([,o])=>o>=2).map(([o])=>o));return new Map(e.map(o=>[o.name,{isPeerRoot:t.has(o.name),hasPeers:j(o.meta).length>0,isSharedDep:n.has(o.name)}]))}};var T=class{constructor(e,t,r){this.cache=e,this.cdn=t,this.semver=r}selectVersionFromIndex(e,t,r){return this.semver.selectBestVersion(e,{range:t,distTags:r})}async learnExactVersion(e){let t=await b(()=>this.cdn.fetchVersionsIndex(e));if(t?.versions?.length){let r=this.semver.selectBestVersion(t.versions,{distTags:t.distTags});if(r)return r}return this.cdn.resolveExactVersion(e)}async resolveExactForRoot(e,t){if(!t)return this.learnExactVersion(e);let r=await this.cache.getPinnedExact(e,t);if(r){if(this.semver.isExactVersion(r))return r;console.warn("[versionResolver] Rejecting invalid cached version for",e,":",r)}let n=await b(()=>this.cdn.fetchVersionsIndex(e));if(n?.versions?.length){let i=this.selectVersionFromIndex(n.versions,t,n.distTags);if(i){if(this.semver.isExactVersion(i))return await this.cache.setPinnedExact(e,t,i),i}else{console.warn("[versionResolver] Invalidating stale versions cache for",e,"- range",t,"not satisfied"),await this.cache.invalidateVersionsCache(e);let l=await b(()=>this.cdn.fetchVersionsIndex(e));if(l?.versions?.length){let d=this.selectVersionFromIndex(l.versions,t,l.distTags);if(d&&this.semver.isExactVersion(d))return await this.cache.setPinnedExact(e,t,d),d}}}let o=await this.cdn.resolveExactVersion(`${e}@${t}`);if(o&&this.semver.isExactVersion(o))return await this.cache.setPinnedExact(e,t,o),o}async effectivePackage(e,t){let r=new h(e),n=r.root(),o=t[n],i=o?await b(()=>this.cache.getPinnedExact(n,o))??await b(()=>this.resolveExactForRoot(n,o)):void 0;return{effectivePackage:i?r.withVersion(i).format():e,root:n,range:o,pinned:i}}};import{init as xe,parse as Se}from"es-module-lexer";var E=class s{constructor(e,t,r,n){this.version=e,this.cdn=t,this.peer=r,this.cache=n}extractImportsSync(e){let t=new Set;for(let r of s.importPatterns){r.lastIndex=0;for(let n of e.matchAll(r))h.isBare(n[1])&&t.add(n[1])}return Array.from(t)}async extractImports(e){let t=new Set,r=n=>{h.isBare(n)&&t.add(n)};try{await xe;let[n]=Se(e);n.forEach(o=>r(e.slice(o.s,o.e).trim()))}catch{return this.extractImportsSync(e)}return Array.from(t)}async buildImportMap(e,t){let r=await this.extractImports(e),n=[...new Set(r.map(h.rootOf))],o=await Promise.all(n.map(async a=>({name:a,version:await this.resolveVersion(a,t)}))),{allRoots:i,flags:l,autoAddedPeers:d}=await this.peer.resolve(o,a=>this.resolveVersion(a,t)),u=this.buildEntries([...r,...d.map(a=>a.name)],i,l,t);return{importMap:{imports:Object.fromEntries(u)},prefetchUrls:u.map(([,a])=>a)}}async resolveVersion(e,t){let r=t[e],n=await this.version.resolveExactForRoot(e,r);if(!n){let o=r?`${e}@${r}`:e,i=await b(()=>this.cdn.pinEsmUrl(o));if(!i)throw new Error(`Cannot resolve version for ${e} - network may be unavailable.`);n=h.versionFromUrl(i),n&&r&&await b(()=>this.cache.setPinnedExact(e,r,n))}if(!n)throw new Error(`Cannot resolve version for ${e}`);return n}buildEntries(e,t,r,n){let o=new Map(t.map(c=>[c.name,c])),i=c=>{let f=o.get(h.rootOf(c));return new h(c).withPreferredVersion(f.version,n[f.name]).format()},l=new Set([...r.entries()].filter(([,c])=>c.isPeerRoot||c.isSharedDep).map(([c])=>c)),d=new Set(e.filter(c=>c!==h.rootOf(c)).map(h.rootOf)),u=[],a=new Set,p=new Set(e.filter(c=>c===h.rootOf(c)));for(let c of e){let f=h.rootOf(c),g=o.get(f),{isPeerRoot:m,hasPeers:x,isSharedDep:C}=r.get(f),de=d.has(f),ue=c!==f,fe=!(m||C)&&(de||!x),_=this.singletonDepsOf(g,l),he=ue&&p.has(f)?[..._,f]:_.length?_:void 0;u.push([c,this.cdn.buildEsmUrl(i(c),{bundle:fe,external:he})]),a.add(c)}for(let c of l)o.has(c)&&(a.has(c)||u.push([c,this.cdn.buildEsmUrl(i(c),{})]),u.push([`${c}/`,`${this.cdn.esmHost}/${i(c)}/`]));return u}singletonDepsOf(e,t){return[...new Set([...j(e.meta),...B(e.meta)])].filter(r=>t.has(r))}};E.importPatterns=[/\bimport\s+(?:[^'";]*?from\s*)?['"]([^'"]+)['"]/g,/\bexport\s+[^'";]*?from\s*['"]([^'"]+)['"]/g];import{gt as Q,satisfies as Ee,maxSatisfying as V,major as Pe,prerelease as Re,rsort as Ie,valid as $e}from"semver";var D=class{cmp(e,t){return e===t?0:Q(e,t)?1:Q(t,e)?-1:0}satisfies(e,t){return!e||!e.trim()?!0:!!Ee(t,e,{includePrerelease:!0})}pickMaxSatisfying(e,t){if(!e?.length)return;let r=V(e,t,{includePrerelease:!0});return r===null?void 0:r}pickLatest(e){return e?.length?Ie(e)[0]:void 0}selectBestVersion(e,t){if(!e?.length)return;let r=t?.range?.trim()||"*",n=t?.preferStable??!0,o=t?.distTags?.latest;if(o&&e.includes(o)&&this.satisfies(r,o))return o;if(n){let l=V(e,r,{includePrerelease:!1});if(l)return l}return V(e,r,{includePrerelease:!0})??void 0}majorOf(e){return Pe(e)}isPrerelease(e){return Re(e)!==null}isExactVersion(e){return $e(e)!==null}},W=new D;function ee(s,e){let t=new $(s,e),r=new M(t),n=new T(s,t,W);return{packageService:new E(n,t,r,s),versionResolver:n,cdnClient:t,peerResolver:r}}var U=class{pins=new Map;indexes=new Map;negatives=new Set;meta=new Map;async getPinnedExact(e,t){return this.pins.get(`${e}@${t}`)}async setPinnedExact(e,t,r){this.pins.set(`${e}@${t}`,r)}async getIndex(e){return this.indexes.get(e)}async setIndex(e,t,r){this.indexes.set(e,{versions:t,distTags:r,updatedAt:Date.now()})}async invalidateVersionsCache(e){this.indexes.delete(e)}async isNegative(e){return this.negatives.has(e)}async recordNegative(e){this.negatives.add(e)}async clearNegative(e){this.negatives.delete(e)}async getMeta(e,t){return this.meta.get(`${e}@${t}`)}async setMeta(e,t,r,n,o){this.meta.set(`${e}@${t}`,{dependencies:r,peerDependencies:n,peerDependenciesMeta:o,updatedAt:Date.now()})}},je={async getJson(s){try{let e=await fetch(s,{redirect:"follow"});return e.ok?await e.json():void 0}catch{return}},async head(s){try{let e=await fetch(s,{method:"HEAD",redirect:"follow"});return{ok:e.ok,url:e.url}}catch{return}}},Me=new U,{packageService:te,versionResolver:kt,cdnClient:Dt,peerResolver:_t}=ee(Me,je);var re=`
5
5
  (() => {
6
6
  // JSON parser (handles jsonish - unquoted keys)
7
7
  const parseJson = (str) => {
@@ -119,7 +119,7 @@ import*as w from"fs/promises";import{readFileSync as Fe,existsSync as ie}from"fs
119
119
  };
120
120
  }
121
121
  })();
122
- `;function re(s){let{name:e,code:t,css:r,html:n,data:o,insight:i,importMap:l,watch:u}=s,c=n.trim()||'<div id="app"></div>',a=d=>d.replace(/<\/script/gi,"<\\/script");return`<!DOCTYPE html>
122
+ `;function se(s){let{name:e,code:t,css:r,html:n,data:o,insight:i,importMap:l,watch:d}=s,u=n.trim()||'<div id="app"></div>',a=p=>p.replace(/<\/script/gi,"<\\/script");return`<!DOCTYPE html>
123
123
  <html>
124
124
  <head>
125
125
  <meta charset="utf-8">
@@ -138,21 +138,21 @@ ${r}
138
138
  </style>
139
139
  </head>
140
140
  <body>
141
- ${c}
141
+ ${u}
142
142
 
143
143
  ${o.length>0?`<script>window.__TB_DATA__ = ${a(JSON.stringify(o))};</script>`:""}
144
144
  ${i?`<script>window.__TB_INSIGHT__ = ${a(JSON.stringify(i))};</script>`:""}
145
- ${u?"<script>window.__TYPEBULB_WATCH__ = true;</script>":""}
145
+ ${d?"<script>window.__TYPEBULB_WATCH__ = true;</script>":""}
146
146
 
147
147
  <script>
148
- ${te}
148
+ ${re}
149
149
  </script>
150
150
 
151
151
  <script type="module">
152
152
  ${a(t)}
153
153
  </script>
154
154
  </body>
155
- </html>`}function Te(s){return s.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}import{Hono as ke}from"hono";import{serve as Ce}from"@hono/node-server";import{streamSSE as _e}from"hono/streaming";import*as E from"fs/promises";import*as y from"path";async function ne(s){let{getHtml:e,basePath:t,port:r,reloadEmitter:n,getServerExports:o}=s,i=new ke;i.get("/",c=>c.html(e())),i.post("/__fs/read",async c=>{try{let{path:a}=await c.req.json(),d=se(a,t),p=await E.readFile(d,"utf-8");return c.json({content:p})}catch(a){let d=a instanceof Error?a.message:"Unknown error";return c.json({error:d},400)}}),i.post("/__fs/write",async c=>{try{let{path:a,content:d}=await c.req.json(),p=se(a,t);return await E.mkdir(y.dirname(p),{recursive:!0}),await E.writeFile(p,d,"utf-8"),c.json({success:!0})}catch(a){let d=a instanceof Error?a.message:"Unknown error";return c.json({error:d},400)}}),i.post("/__api/:name",async c=>{try{let a=o?.(),d=c.req.param("name");if(!a||typeof a[d]!="function")return c.json({error:`API function '${d}' not found`},404);let{args:p}=await c.req.json(),m=await a[d](...p||[]);return c.json({result:m})}catch(a){let d=a instanceof Error?a.message:"Unknown error";return c.json({error:d},500)}});let l=["esm.sh","unpkg.com","cdn.jsdelivr.net","cdnjs.cloudflare.com"];i.get("/proxy/*",async c=>{let a=c.req.path.slice(7),d=a.lastIndexOf("https://");if(d===-1)return c.text("Invalid proxy URL",400);let p=a.slice(d),m;try{m=new URL(p)}catch{return c.text("Invalid URL",400)}if(m.protocol!=="https:")return c.text("HTTPS only",400);if(!l.includes(m.hostname))return c.text("Host not allowed",403);try{let f=await fetch(p,{headers:{Accept:c.req.header("Accept")||"*/*"},redirect:"follow"});if(!f.ok)return c.text(`Upstream ${f.status}`,f.status);let b=new Headers,T=f.headers.get("Content-Type");return T&&b.set("Content-Type",T),b.set("Access-Control-Allow-Origin","*"),b.set("Cross-Origin-Resource-Policy","cross-origin"),new Response(f.body,{status:f.status,headers:b})}catch(f){return c.text(`Proxy fetch failed: ${f instanceof Error?f.message:f}`,502)}}),n&&i.get("/__reload",c=>_e(c,async a=>{let d=()=>{a.writeSSE({event:"reload",data:""})};for(n.on("reload",d),a.onAbort(()=>{n.removeListener("reload",d)});;)await a.sleep(3e4)}));let u=Ce({fetch:i.fetch,port:r});return{port:r,close:()=>u.close()}}function se(s,e){let t=y.resolve(e,s),r=y.normalize(e);if(!y.normalize(t).startsWith(r))throw new Error("Path traversal detected - access denied");return t}async function U(s){let e=await import("net");return new Promise(t=>{let r=e.createServer();r.listen(s,()=>{let n=r.address(),o=typeof n=="object"&&n?n.port:s;r.close(()=>t(o))}),r.on("error",()=>{t(U(s+1))})})}import De from"open";async function oe(s){await De(s)}import Oe from"chokidar";function N(s){let{bulbPath:e,emitter:t}=s,r=Oe.watch(e,{persistent:!0,ignoreInitial:!0,awaitWriteFinish:{stabilityThreshold:100,pollInterval:50}});return r.on("change",()=>{t.emit("reload")}),()=>r.close()}var We=Ve(Be),Ue="0.1.3";function Ne(s){let e={file:"",port:3e3,watch:!0,open:!0,server:!1,help:!1,version:!1};for(let t=0;t<s.length;t++){let r=s[t];if(r==="--help"||r==="-h")e.help=!0;else if(r==="--version"||r==="-V")e.version=!0;else if(r==="--no-watch")e.watch=!1;else if(r==="--headless")e.open=!1;else if(r==="--server")e.server=!0;else if(r==="--port"||r==="-p"){let n=s[++t],o=parseInt(n,10);isNaN(o)&&(console.error(`Invalid port: ${n}`),process.exit(1)),e.port=o}else r.startsWith("-")||(e.file=r)}return e}function Le(){console.log(`
155
+ </html>`}function Te(s){return s.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}import{Hono as Ce}from"hono";import{serve as Oe}from"@hono/node-server";import{streamSSE as ke}from"hono/streaming";import*as P from"fs/promises";import*as y from"path";async function oe(s){let{getHtml:e,basePath:t,port:r,reloadEmitter:n,getServerExports:o}=s,i=new Ce;i.use("*",async(a,p)=>{await p(),a.res.headers.set("Cross-Origin-Opener-Policy","same-origin"),a.res.headers.set("Cross-Origin-Embedder-Policy","credentialless")}),i.get("/",a=>a.html(e())),i.post("/__fs/read",async a=>{try{let{path:p}=await a.req.json(),c=ne(p,t),f=await P.readFile(c,"utf-8");return a.json({content:f})}catch(p){let c=p instanceof Error?p.message:"Unknown error";return a.json({error:c},400)}}),i.post("/__fs/write",async a=>{try{let{path:p,content:c}=await a.req.json(),f=ne(p,t);return await P.mkdir(y.dirname(f),{recursive:!0}),await P.writeFile(f,c,"utf-8"),a.json({success:!0})}catch(p){let c=p instanceof Error?p.message:"Unknown error";return a.json({error:c},400)}});let l={log:console.log};i.post("/__api/:name",async a=>{try{let p=o?.(),c=a.req.param("name"),f=p?.[c]??l[c];if(!f||typeof f!="function")return a.json({error:`API function '${c}' not found`},404);let{args:g}=await a.req.json(),m=await f(...g||[]);return a.json({result:m})}catch(p){let c=p instanceof Error?p.message:"Unknown error";return a.json({error:c},500)}});let d=["esm.sh","unpkg.com","cdn.jsdelivr.net","cdnjs.cloudflare.com"];i.get("/proxy/*",async a=>{let p=a.req.path.slice(7),c=p.lastIndexOf("https://");if(c===-1)return a.text("Invalid proxy URL",400);let f=p.slice(c),g;try{g=new URL(f)}catch{return a.text("Invalid URL",400)}if(g.protocol!=="https:")return a.text("HTTPS only",400);if(!d.includes(g.hostname))return a.text("Host not allowed",403);try{let m=await fetch(f,{headers:{Accept:a.req.header("Accept")||"*/*"},redirect:"follow"});if(!m.ok)return a.text(`Upstream ${m.status}`,m.status);let x=new Headers,C=m.headers.get("Content-Type");return C&&x.set("Content-Type",C),x.set("Access-Control-Allow-Origin","*"),x.set("Cross-Origin-Resource-Policy","cross-origin"),new Response(m.body,{status:m.status,headers:x})}catch(m){return a.text(`Proxy fetch failed: ${m instanceof Error?m.message:m}`,502)}}),n&&i.get("/__reload",a=>ke(a,async p=>{let c=()=>{p.writeSSE({event:"reload",data:""})};for(n.on("reload",c),p.onAbort(()=>{n.removeListener("reload",c)});;)await p.sleep(3e4)}));let u=Oe({fetch:i.fetch,port:r});return{port:r,close:()=>u.close()}}function ne(s,e){let t=y.resolve(e,s),r=y.normalize(e);if(!y.normalize(t).startsWith(r))throw new Error("Path traversal detected - access denied");return t}async function N(s){let e=await import("net");return new Promise(t=>{let r=e.createServer();r.listen(s,()=>{let n=r.address(),o=typeof n=="object"&&n?n.port:s;r.close(()=>t(o))}),r.on("error",()=>{t(N(s+1))})})}import De from"open";async function ie(s){await De(s)}import _e from"chokidar";function L(s){let{bulbPath:e,emitter:t}=s,r=_e.watch(e,{persistent:!0,ignoreInitial:!0,awaitWriteFinish:{stabilityThreshold:100,pollInterval:50}});return r.on("change",()=>{t.emit("reload")}),()=>r.close()}var We=Ve(Be),Ue="0.1.3";function Ne(s){let e={file:"",port:3e3,watch:!0,open:!0,server:!1,help:!1,version:!1};for(let t=0;t<s.length;t++){let r=s[t];if(r==="--help"||r==="-h")e.help=!0;else if(r==="--version"||r==="-V")e.version=!0;else if(r==="--no-watch")e.watch=!1;else if(r==="--no-open")e.open=!1;else if(r==="--server")e.server=!0;else if(r==="--port"||r==="-p"){let n=s[++t],o=parseInt(n,10);isNaN(o)&&(console.error(`Invalid port: ${n}`),process.exit(1)),e.port=o}else r.startsWith("-")||(e.file=r)}return e}function Le(){console.log(`
156
156
  typebulb - Local bulb runner for Typebulb
157
157
 
158
158
  Usage:
@@ -162,7 +162,7 @@ Usage:
162
162
  Options:
163
163
  --no-watch Disable hot reload (watch is on by default)
164
164
  -p, --port <port> Use a specific port (default: 3000)
165
- --headless Serve without opening browser
165
+ --no-open Don't auto-open browser
166
166
  --server Run server.ts only, no web server
167
167
  -V, --version Show version number
168
168
  -h, --help Show this help message
@@ -183,11 +183,11 @@ Examples:
183
183
  typebulb my-editor.bulb.md
184
184
  typebulb --no-watch --port 8080 my-editor.bulb.md
185
185
  typebulb .
186
- `)}async function He(s){let t=(await w.readdir(s)).find(r=>r.endsWith(".bulb.md"));return t?g.join(s,t):null}function ce(){H([".env",".env.local"],process.cwd(),!0)}function H(s,e,t=!1){for(let r of s){let n=g.resolve(e,r);try{let o=Fe(n,"utf-8");for(let i of o.split(`
187
- `)){let l=i.trim();if(!l||l.startsWith("#"))continue;let u=l.indexOf("=");if(u===-1)continue;let c=l.slice(0,u).trim(),a=l.slice(u+1).trim();(a.startsWith('"')&&a.endsWith('"')||a.startsWith("'")&&a.endsWith("'"))&&(a=a.slice(1,-1)),process.env[c]??=a}}catch{t||console.warn(` Warning: env file not found: ${r}`)}}}function Je(s){let e=new Set,t=/\bimport\s+(?:[\s\S]*?\s+from\s+)?['"]([^./][^'"]*)['"]/g,r;for(;r=t.exec(s);){let n=r[1];if(n.startsWith("node:"))continue;let o=n.startsWith("@")?n.split("/").slice(0,2).join("/"):n.split("/")[0];e.add(o)}return[...e]}async function qe(s,e){let t=s.filter(n=>!ie(g.join(e,"node_modules",n)));if(t.length===0)return;let r=g.join(e,"package.json");ie(r)||await w.writeFile(r,JSON.stringify({name:"typebulb-server",private:!0})),console.log(` Installing: ${t.join(", ")}`),await We("npm",["install","--no-audit","--no-fund",...t],{cwd:e,shell:!0})}async function le(s,e){let t=G(s);if(t.error)throw new Error(`Server compilation error: ${t.error}`);let r=g.join(e,".typebulb");await w.mkdir(r,{recursive:!0});let n=Je(t.code);n.length>0&&await qe(n,r);let o=g.join(r,"server.mjs");return await w.writeFile(o,t.code,"utf-8"),await import(`${Ae(o).href}?t=${Date.now()}`)}async function ae(s,e){let t=await w.readFile(s,"utf-8"),r=k(t);if(!r)throw new Error("Invalid .bulb.md file format");let n=C(r),o=O(n.config),i=J(n.data),l=z(n.code,{jsxImportSource:o.jsxImportSource});l.error&&console.error("Compilation error:",l.error);let{importMap:u}=await ee.buildImportMap(l.code,o.dependencies??{}),c=re({name:n.name,code:l.code,css:n.css,html:n.html,data:i,insight:n.insight,importMap:u,watch:e}),a=g.dirname(s);o.env?.length&&H(o.env,a);let d=null;return n.server&&(d=await le(n.server,a)),{html:c,bulb:n,serverExports:d}}async function ze(s,e){ce();let t=async()=>{let r=await w.readFile(s,"utf-8"),n=k(r);if(!n)throw new Error("Invalid .bulb.md file format");let o=C(n),i=O(o.config),l=g.dirname(s);i.env?.length&&H(i.env,l),await le(o.server,l)};if(console.log(`Running ${g.basename(s)}...`),await t(),e){console.log(`Watching for changes...
188
- `);let r=new L;r.on("reload",async()=>{try{console.log("Re-running..."),await t()}catch(n){console.error("Error:",n)}}),N({bulbPath:s,emitter:r})}}async function Ge(){let s=Ne(process.argv.slice(2));s.version&&(console.log(`typebulb ${Ue}`),process.exit(0)),s.help&&(Le(),process.exit(0));let e;if(!s.file||s.file==="."){let f=await He(process.cwd());f||(console.error("No .bulb.md file found in current directory"),process.exit(1)),e=f}else e=g.resolve(s.file);try{await w.access(e)}catch{console.error(`File not found: ${e}`),process.exit(1)}e.endsWith(".bulb.md")||(console.error("File must have .bulb.md extension"),process.exit(1));let t=await w.readFile(e,"utf-8"),r=k(t);if(r){let f=C(r);if(f.server&&(!f.code||s.server)){await ze(e,s.watch);return}}let n=process.cwd(),o=s.watch?new L:void 0;ce(),console.log(`Loading ${g.basename(e)}...`);let{html:i,bulb:l,serverExports:u}=await ae(e,s.watch),c=await U(s.port),a=await ne({getHtml:()=>i,basePath:n,port:c,reloadEmitter:o,getServerExports:()=>u}),d=`http://localhost:${c}`;console.log(`
189
- ${l.name}`),console.log(` ${d}
186
+ `)}async function He(s){let t=(await v.readdir(s)).find(r=>r.endsWith(".bulb.md"));return t?w.join(s,t):null}function le(){J([".env",".env.local"],process.cwd(),!0)}function J(s,e,t=!1){for(let r of s){let n=w.resolve(e,r);try{let o=Fe(n,"utf-8");for(let i of o.split(`
187
+ `)){let l=i.trim();if(!l||l.startsWith("#"))continue;let d=l.indexOf("=");if(d===-1)continue;let u=l.slice(0,d).trim(),a=l.slice(d+1).trim();(a.startsWith('"')&&a.endsWith('"')||a.startsWith("'")&&a.endsWith("'"))&&(a=a.slice(1,-1)),process.env[u]??=a}}catch{t||console.warn(` Warning: env file not found: ${r}`)}}}function Je(s){let e=new Set,t=/\bimport\s+(?:[\s\S]*?\s+from\s+)?['"]([^./][^'"]*)['"]/g,r;for(;r=t.exec(s);){let n=r[1];if(n.startsWith("node:"))continue;let o=n.startsWith("@")?n.split("/").slice(0,2).join("/"):n.split("/")[0];e.add(o)}return[...e]}async function qe(s,e){let t=s.filter(n=>!ae(w.join(e,"node_modules",n)));if(t.length===0)return;let r=w.join(e,"package.json");ae(r)||await v.writeFile(r,JSON.stringify({name:"typebulb-server",private:!0})),console.log(` Installing: ${t.join(", ")}`),await We("npm",["install","--no-audit","--no-fund",...t],{cwd:e,shell:!0})}async function pe(s,e){let t=K(s);if(t.error)throw new Error(`Server compilation error: ${t.error}`);let r=w.join(e,".typebulb");await v.mkdir(r,{recursive:!0});let n=Je(t.code);n.length>0&&await qe(n,r);let o=w.join(r,"server.mjs");return await v.writeFile(o,t.code,"utf-8"),await import(`${Ae(o).href}?t=${Date.now()}`)}async function ce(s,e){let t=await v.readFile(s,"utf-8"),r=O(t);if(!r)throw new Error("Invalid .bulb.md file format");let n=k(r),o=F(n.config),i=q(n.data),l=G(n.code,{jsxImportSource:o.jsxImportSource});l.error&&console.error("Compilation error:",l.error);let{importMap:d}=await te.buildImportMap(l.code,o.dependencies??{}),u=se({name:n.name,code:l.code,css:n.css,html:n.html,data:i,insight:n.insight,importMap:d,watch:e}),a=w.dirname(s);o.env?.length&&J(o.env,a);let p=null;return n.server&&(p=await pe(n.server,a)),{html:u,bulb:n,serverExports:p}}async function ze(s,e){le();let t=async()=>{let r=await v.readFile(s,"utf-8"),n=O(r);if(!n)throw new Error("Invalid .bulb.md file format");let o=k(n),i=F(o.config),l=w.dirname(s);i.env?.length&&J(i.env,l),await pe(o.server,l)};if(console.log(`Running ${w.basename(s)}...`),await t(),e){console.log(`Watching for changes...
188
+ `);let r=new H;r.on("reload",async()=>{try{console.log("Re-running..."),await t()}catch(n){console.error("Error:",n)}}),L({bulbPath:s,emitter:r})}}async function Ge(){let s=Ne(process.argv.slice(2));s.version&&(console.log(`typebulb ${Ue}`),process.exit(0)),s.help&&(Le(),process.exit(0));let e;if(!s.file||s.file==="."){let g=await He(process.cwd());g||(console.error("No .bulb.md file found in current directory"),process.exit(1)),e=g}else e=w.resolve(s.file);try{await v.access(e)}catch{console.error(`File not found: ${e}`),process.exit(1)}e.endsWith(".bulb.md")||(console.error("File must have .bulb.md extension"),process.exit(1));let t=await v.readFile(e,"utf-8"),r=O(t);if(r){let g=k(r);if(g.server&&(!g.code||s.server)){await ze(e,s.watch);return}}let n=process.cwd(),o=s.watch?new H:void 0;le(),console.log(`Loading ${w.basename(e)}...`);let{html:i,bulb:l,serverExports:d}=await ce(e,s.watch),u=await N(s.port),a=await oe({getHtml:()=>i,basePath:n,port:u,reloadEmitter:o,getServerExports:()=>d}),p=`http://localhost:${u}`;console.log(`
189
+ ${l.name}`),console.log(` ${p}
190
190
  `),s.watch&&console.log(` Watching for changes...
191
- `);let p;if(s.watch&&o){let f=new L;f.on("reload",async()=>{try{console.log("Recompiling...");let b=await ae(e,!0);i=b.html,u=b.serverExports,o.emit("reload"),console.log(`Done. Browser reloading...
192
- `)}catch(b){console.error("Compile error:",b)}}),p=N({bulbPath:e,emitter:f})}s.open&&await oe(d);let m=async()=>{console.log(`
193
- Shutting down...`),a.close(),p?.();let f=g.join(g.dirname(e),".typebulb","server.mjs");await w.rm(f,{force:!0}).catch(()=>{}),process.exit(0)};process.on("SIGINT",m),process.on("SIGTERM",m)}Ge().catch(s=>{console.error("Error:",s.message),process.exit(1)});
191
+ `);let c;if(s.watch&&o){let g=new H;g.on("reload",async()=>{try{console.log("Recompiling...");let m=await ce(e,!0);i=m.html,d=m.serverExports,o.emit("reload"),console.log(`Done. Browser reloading...
192
+ `)}catch(m){console.error("Compile error:",m)}}),c=L({bulbPath:e,emitter:g})}s.open&&await ie(p);let f=async()=>{console.log(`
193
+ Shutting down...`),a.close(),c?.();let g=w.join(w.dirname(e),".typebulb","server.mjs");await v.rm(g,{force:!0}).catch(()=>{}),process.exit(0)};process.on("SIGINT",f),process.on("SIGTERM",f)}Ge().catch(s=>{console.error("Error:",s.message),process.exit(1)});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "typebulb",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "Local bulb runner CLI for Typebulb",
5
5
  "license": "MIT",
6
6
  "engines": { "node": ">=18" },
@@ -12,9 +12,9 @@
12
12
  "dist"
13
13
  ],
14
14
  "scripts": {
15
- "build": "tsc --build",
16
- "bundle": "node esbuild.config.mjs",
17
- "clean": "rimraf dist",
15
+ "typecheck": "tsc --build",
16
+ "build": "tsc --build && node esbuild.config.mjs",
17
+ "clean": "rimraf dist .tsbuild",
18
18
  "dev": "tsc --build && node dist/index.js",
19
19
  "prepublishOnly": "rimraf dist && node esbuild.config.mjs"
20
20
  },