typebulb 0.9.4 → 0.9.6

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/dist/render.js CHANGED
@@ -439,17 +439,36 @@ ${A(s)}
439
439
  // viewport for a short bulb (so the frame shrinks to it), tracking the frame for a
440
440
  // full-bleed one (so it self-stabilises at the host's initial height).
441
441
  //
442
+ // Showing the Y scrollbar is DEFERRED; hiding it stays eager. Content grows (a new
443
+ // row, an expanded panel) synchronously, but the frame only grows a beat later, after
444
+ // the host receives the height message and resizes the iframe. In that gap body is
445
+ // taller than the still-old frame, so an eager overflow:auto flashes a scrollbar for
446
+ // exactly the round-trip. So when Y overflows we post the height and re-check after a
447
+ // short delay instead of flipping immediately: if the frame caught up the overflow is
448
+ // gone (no bar), and only an overflow that SURVIVES \u2014 a genuine host cap \u2014 gets auto.
449
+ // The host can't tell the iframe its cap (no host\u2192iframe channel), so this round-trip
450
+ // is how the embed distinguishes "frame about to grow" from "frame capped". X has no
451
+ // async resize (the host never grows width on content), so it flips inline.
452
+ //
442
453
  // Coalesce to one rAF and only write an overflow value that actually changed: setting
443
454
  // overflow adds/removes a scrollbar, which resizes the element and re-fires the
444
455
  // observer \u2014 writing unconditionally ping-pongs (the "ResizeObserver loop" warning).
445
- // The rAF + equality guard let it settle within a frame instead.
446
456
  var raf = 0;
457
+ var confirm = 0;
458
+ var showYScroll = function () {
459
+ confirm = 0;
460
+ if (de.scrollHeight > de.clientHeight + 1 && de.style.overflowY !== 'auto') de.style.overflowY = 'auto';
461
+ };
447
462
  var apply = function () {
448
463
  raf = 0;
449
- var oy = de.scrollHeight > de.clientHeight + 1 ? 'auto' : 'hidden';
450
- var ox = de.scrollWidth > de.clientWidth + 1 ? 'auto' : 'hidden';
451
- if (de.style.overflowY !== oy) de.style.overflowY = oy;
464
+ var ox = de.scrollWidth > de.clientWidth + 1 ? 'auto' : 'hidden';
452
465
  if (de.style.overflowX !== ox) de.style.overflowX = ox;
466
+ if (de.scrollHeight > de.clientHeight + 1) {
467
+ if (de.style.overflowY !== 'auto' && !confirm) confirm = setTimeout(showYScroll, 100);
468
+ } else {
469
+ if (confirm) { clearTimeout(confirm); confirm = 0; }
470
+ if (de.style.overflowY !== 'hidden') de.style.overflowY = 'hidden';
471
+ }
453
472
  post({ __typebulbEmbed: true, kind: 'height', height: Math.ceil(document.body.scrollHeight) });
454
473
  };
455
474
  var update = function () { if (!raf) raf = requestAnimationFrame(apply); };
package/dist/servers.d.ts CHANGED
@@ -11,6 +11,9 @@ export interface BulbServer {
11
11
  startedAt: number
12
12
  /** Launched with --trust (filesystem / AI / server.ts enabled)? */
13
13
  trust?: boolean
14
+ /** Human label of a capability the proactive scan predicts this untrusted bulb will need
15
+ * (set at register time; probabilistic, not enforcement). */
16
+ predicted?: string
14
17
  /** Human label of a privileged capability this untrusted server denied (set by the gate). */
15
18
  denied?: string
16
19
  }
@@ -46,6 +49,10 @@ export interface BulbFileInfo {
46
49
  * each with its frontmatter name (or filename stem) and mtime. */
47
50
  export declare function listBulbFiles(cwd: string): BulbFileInfo[]
48
51
 
52
+ /** Scan a `.bulb.md` and return the privileged capability it probably needs (or undefined),
53
+ * so a host can offer --trust before launching. Probabilistic hint, never enforcement. */
54
+ export declare function predictBulbTrust(file: string): Promise<string | undefined>
55
+
49
56
  /** The bulb's `name:` from its leading frontmatter, or undefined if absent. */
50
57
  export declare function bulbName(source: string): string | undefined
51
58
 
package/dist/servers.js CHANGED
@@ -1 +1,3 @@
1
- import{spawn as y}from"child_process";import{readdir as F,readFile as E,writeFile as tr,unlink as a,mkdir as nr}from"fs/promises";import{mkdirSync as sr,writeFileSync as or,appendFileSync as ar,readFileSync as L}from"fs";import*as s from"path";import{join as f,resolve as x}from"path";import{homedir as P}from"os";function c(){let r=process.env.TYPEBULB_SERVERS_DIR;return r?f(r,".."):f(P(),".typebulb")}function o(){return process.env.TYPEBULB_SERVERS_DIR||f(c(),"servers")}function m(r){let e=x(r);return(process.platform==="win32"?e.toLowerCase():e).replace(/\\/g,"/")}function N(r){return s.join(o(),`${r}.json`)}function l(r){return s.join(o(),`${r}.log`)}function T(r,e=0){try{let t=L(l(r)),n=e>=0&&e<=t.length?e:0;return{text:t.subarray(n).toString("utf8"),offset:t.length}}catch{return{text:"",offset:0}}}function D(r){try{return process.kill(r,0),!0}catch(e){return e.code==="EPERM"}}async function j(r){await a(N(r)).catch(()=>{}),await a(l(r)).catch(()=>{})}async function d(){let r;try{r=await F(o())}catch{return[]}let e=[];return await Promise.all(r.map(async t=>{if(!t.endsWith(".json"))return;let n=s.join(o(),t),i;try{i=JSON.parse(await E(n,"utf8"))}catch{await a(n).catch(()=>{});return}i&&typeof i.pid=="number"&&D(i.pid)?e.push(i):(await a(n).catch(()=>{}),i?.pid&&await a(l(i.pid)).catch(()=>{}))})),e.sort((t,n)=>t.startedAt-n.startedAt)}async function _(r){try{process.kill(r,"SIGTERM")}catch{}await j(r)}var A=r=>new Promise(e=>setTimeout(e,r));async function O(r,e={}){let t=e.cwd??process.cwd(),n=s.resolve(t,r),i=(await d()).find(u=>u.file===n);if(i)return i;let g=["typebulb",...e.trust?["--trust"]:[],r,...e.open===!1?["--no-open"]:[]];(process.platform==="win32"?y("cmd",["/c","start","","/b","npx",...g],{cwd:t,stdio:"ignore",windowsHide:!0}):y("npx",g,{cwd:t,detached:!0,stdio:"ignore"})).unref();let h=Date.now()+2e4;for(;Date.now()<h;){await A(150);let u=(await d()).find(B=>B.file===n);if(u)return u}throw new Error(`Launched ${s.basename(r)} but it did not register within 20s.`)}import{readdirSync as $,readFileSync as k,statSync as I}from"fs";import{join as w,basename as M}from"path";function p(r){let e=/^---[^\n]*\n([\s\S]*?)\n---/.exec(r),t=e?/^\s*name:\s*(.+?)\s*$/m.exec(e[1]):null;return t?t[1].replace(/^["']|["']$/g,""):void 0}function R(r){return(p(r)??"bulb").toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")||"bulb"}function J(r){return r.replace(/^---[^\n]*\r?\n[\s\S]*?\r?\n---[^\n]*\r?\n?/,"").replace(/^\r?\n+/,"")}function S(r,e,t){if(e>8)return t;let n;try{n=$(r,{withFileTypes:!0})}catch{return t}for(let i of n)if(i.isDirectory()){if(i.name.startsWith(".")||i.name==="node_modules")continue;S(w(r,i.name),e+1,t)}else i.isFile()&&i.name.endsWith(".bulb.md")&&t.push(w(r,i.name));return t}function z(r){return S(r,0,[]).map(e=>{let t=0;try{t=I(e).mtimeMs}catch{}let n;try{n=p(k(e,"utf8").slice(0,1024))}catch{}return{path:e,name:n??M(e).replace(/\.bulb\.md$/,""),mtime:t}})}import{readFileSync as C,writeFileSync as H,mkdirSync as U}from"fs";import{join as W}from"path";function v(){return W(c(),"trust.json")}function b(){try{let r=JSON.parse(C(v(),"utf8"));return new Set(Array.isArray(r)?r:[])}catch{return new Set}}function G(r){U(c(),{recursive:!0}),H(v(),JSON.stringify([...r]))}function V(r){return b().has(m(r))}function Y(r,e){let t=b(),n=m(r);(e?t.has(n):!t.has(n))||(e?t.add(n):t.delete(n),G(t))}function q(){return[...b()]}export{p as bulbName,V as isBulbTrusted,O as launchBulbServer,z as listBulbFiles,d as listBulbServers,q as listTrustedBulbs,T as readServerLog,l as serverLogPath,Y as setBulbTrusted,R as slugifyBulbName,_ as stopBulbServer,J as stripFrontmatter};
1
+ import{spawn as v}from"child_process";import{readdir as L,readFile as N,writeFile as pt,unlink as c,mkdir as mt}from"fs/promises";import{mkdirSync as gt,writeFileSync as bt,appendFileSync as ht,readFileSync as k}from"fs";import*as o from"path";import{join as g,resolve as _}from"path";import{homedir as E}from"os";function p(){let e=process.env.TYPEBULB_SERVERS_DIR;return e?g(e,".."):g(E(),".typebulb")}function u(){return process.env.TYPEBULB_SERVERS_DIR||g(p(),"servers")}function b(e){let t=_(e);return(process.platform==="win32"?t.toLowerCase():t).replace(/\\/g,"/")}function $(e){return o.join(u(),`${e}.json`)}function m(e){return o.join(u(),`${e}.log`)}function O(e,t=0){try{let r=k(m(e)),n=t>=0&&t<=r.length?t:0;return{text:r.subarray(n).toString("utf8"),offset:r.length}}catch{return{text:"",offset:0}}}function R(e){try{return process.kill(e,0),!0}catch(t){return t.code==="EPERM"}}async function A(e){await c($(e)).catch(()=>{}),await c(m(e)).catch(()=>{})}async function h(){let e;try{e=await L(u())}catch{return[]}let t=[];return await Promise.all(e.map(async r=>{if(!r.endsWith(".json"))return;let n=o.join(u(),r),s;try{s=JSON.parse(await N(n,"utf8"))}catch{await c(n).catch(()=>{});return}s&&typeof s.pid=="number"&&R(s.pid)?t.push(s):(await c(n).catch(()=>{}),s?.pid&&await c(m(s.pid)).catch(()=>{}))})),t.sort((r,n)=>r.startedAt-n.startedAt)}async function I(e){try{process.kill(e,"SIGTERM")}catch{}await A(e)}var J=e=>new Promise(t=>setTimeout(t,e));async function M(e,t={}){let r=t.cwd??process.cwd(),n=o.resolve(r,e),s=(await h()).find(a=>a.file===n);if(s)return s;let i=["typebulb",...t.trust?["--trust"]:[],e,...t.open===!1?["--no-open"]:[]];(process.platform==="win32"?v("cmd",["/c","start","","/b","npx",...i],{cwd:r,stdio:"ignore",windowsHide:!0}):v("npx",i,{cwd:r,detached:!0,stdio:"ignore"})).unref();let l=Date.now()+2e4;for(;Date.now()<l;){await J(150);let a=(await h()).find(f=>f.file===n);if(a)return a}throw new Error(`Launched ${o.basename(e)} but it did not register within 20s.`)}import{readdirSync as K,readFileSync as z,statSync as H}from"fs";import{join as S,basename as U}from"path";function d(e){let t=/^---[^\n]*\n([\s\S]*?)\n---/.exec(e),r=t?/^\s*name:\s*(.+?)\s*$/m.exec(t[1]):null;return r?r[1].replace(/^["']|["']$/g,""):void 0}function W(e){return(d(e)??"bulb").toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")||"bulb"}function C(e){return e.replace(/^---[^\n]*\r?\n[\s\S]*?\r?\n---[^\n]*\r?\n?/,"").replace(/^\r?\n+/,"")}function x(e,t,r){if(t>8)return r;let n;try{n=K(e,{withFileTypes:!0})}catch{return r}for(let s of n)if(s.isDirectory()){if(s.name.startsWith(".")||s.name==="node_modules")continue;x(S(e,s.name),t+1,r)}else s.isFile()&&s.name.endsWith(".bulb.md")&&r.push(S(e,s.name));return r}function Y(e){return x(e,0,[]).map(t=>{let r=0;try{r=H(t).mtimeMs}catch{}let n;try{n=d(z(t,"utf8").slice(0,1024))}catch{}return{path:t,name:n??U(t).replace(/\.bulb\.md$/,""),mtime:r}})}import{readFile as Q}from"fs/promises";var G={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 B(e){try{let t=e.split(`
2
+ `),r=0;if(t[r]?.trim()!=="---")return null;r++;let n=[];for(;r<t.length&&t[r]?.trim()!=="---";)n.push(t[r]),r++;if(t[r]?.trim()!=="---")return null;r++;let s=V(n);if(!s)return null;let i=new Map;for(;r<t.length;){let l=t[r]?.trim()?.match(/^\*\*(.+)\*\*$/);if(l){let a=l[1].trim();for(r++;r<t.length&&t[r]?.trim()==="";)r++;let f=t[r]?.match(/^(`{3,})(\w*)\s*$/);if(!f){r++;continue}let T=f[1];r++;let w=[];for(;r<t.length&&!t[r]?.match(new RegExp(`^${T}\\s*$`));)w.push(t[r]),r++;r++,i.set(a,w.join(`
3
+ `))}else r++}return{frontmatter:s,files:i}}catch{return null}}function V(e){let t={};for(let r of e){let n=r.indexOf(":");if(n===-1)continue;let s=r.slice(0,n).trim(),i=r.slice(n+1).trim();switch(s){case"format":t.format=i;break;case"name":t.name=q(i);break}}return!t.format?.startsWith("typebulb")||!t.name?null:t}function q(e){return e.startsWith('"')&&e.endsWith('"')?e.slice(1,-1).replace(/\\"/g,'"'):e.startsWith("'")&&e.endsWith("'")?e.slice(1,-1):e}function F(e){let t=r=>e.files.get(G[r].path)||"";return{name:e.frontmatter.name,code:t("code"),css:t("css"),html:t("html"),config:t("config"),notes:t("notes"),data:t("data"),infer:t("infer"),insight:t("insight"),server:t("server")}}function P(e){if(e.server.trim())return"server-side code (server.ts)";let t=e.code;if(/\btb\s*\.\s*fs\b/.test(t)||t.includes("/__fs"))return"the filesystem";if(/\btb\s*\.\s*ai\b/.test(t)||t.includes("/__ai"))return"AI (your API keys)";if(/\btb\s*\.\s*server\s*\.\s*(?!log\b)\w/.test(t)||t.includes("/__api"))return"server-side code (server.ts)"}async function X(e){try{let t=B(await Q(e,"utf-8"));return t?P(F(t)):void 0}catch{return}}import{readFileSync as Z,writeFileSync as tt,mkdirSync as et}from"fs";import{join as rt}from"path";function j(){return rt(p(),"trust.json")}function y(){try{let e=JSON.parse(Z(j(),"utf8"));return new Set(Array.isArray(e)?e:[])}catch{return new Set}}function nt(e){et(p(),{recursive:!0}),tt(j(),JSON.stringify([...e]))}function st(e){return y().has(b(e))}function it(e,t){let r=y(),n=b(e);(t?r.has(n):!r.has(n))||(t?r.add(n):r.delete(n),nt(r))}function ot(){return[...y()]}export{d as bulbName,st as isBulbTrusted,M as launchBulbServer,Y as listBulbFiles,h as listBulbServers,ot as listTrustedBulbs,X as predictBulbTrust,O as readServerLog,m as serverLogPath,it as setBulbTrusted,W as slugifyBulbName,I as stopBulbServer,C as stripFrontmatter};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "typebulb",
3
- "version": "0.9.4",
3
+ "version": "0.9.6",
4
4
  "description": "Local bulb runner CLI for Typebulb",
5
5
  "license": "MIT",
6
6
  "engines": { "node": ">=18" },