@stacksjs/actions 0.70.87 → 0.70.90

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 (159) hide show
  1. package/dist/action.js +78 -2
  2. package/dist/actions.js +2 -2
  3. package/dist/add.js +20 -2
  4. package/dist/ai/request-model-access.js +10 -2
  5. package/dist/auth/client.js +44 -3
  6. package/dist/auth/prune.js +35 -2
  7. package/dist/auth/setup.js +152 -36
  8. package/dist/auth/token.js +10 -2
  9. package/dist/blog-admin.d.ts +55 -0
  10. package/dist/blog-admin.js +156 -0
  11. package/dist/blog.d.ts +11 -0
  12. package/dist/blog.js +306 -56
  13. package/dist/build/cli.js +6 -2
  14. package/dist/build/component-libs.js +7 -2
  15. package/dist/build/core.js +55 -2
  16. package/dist/build/desktop.js +5 -2
  17. package/dist/build/docs.js +5 -2
  18. package/dist/build/frontend-static.js +123 -8
  19. package/dist/build/server.js +6 -2
  20. package/dist/build/stacks.js +8 -2
  21. package/dist/build/views.js +11 -2
  22. package/dist/build.js +64 -2
  23. package/dist/bump.js +162 -7
  24. package/dist/cdn/invalidate-cache.js +0 -1
  25. package/dist/changelog.js +27 -3
  26. package/dist/check/ports.js +16 -2
  27. package/dist/clean.js +4 -2
  28. package/dist/commit.js +12 -2
  29. package/dist/copy-types.js +10 -3
  30. package/dist/database/seed.js +26 -2
  31. package/dist/deploy/create-receipt-rule.js +36 -2
  32. package/dist/deploy/index.js +1318 -44
  33. package/dist/dev/api.js +94 -6
  34. package/dist/dev/components.js +7 -2
  35. package/dist/dev/dashboard-globals.js +89 -3
  36. package/dist/dev/dashboard-utils.js +380 -2
  37. package/dist/dev/dashboard.js +435 -8
  38. package/dist/dev/desktop.js +5 -2
  39. package/dist/dev/docs.js +24 -2
  40. package/dist/dev/index.js +29 -2
  41. package/dist/dev/migrate-watcher.js +74 -2
  42. package/dist/dev/system-tray.js +5 -2
  43. package/dist/dev/views.js +147 -2
  44. package/dist/discover-packages.js +32 -2
  45. package/dist/domains/add.js +61 -2
  46. package/dist/domains/purchase.js +66 -2
  47. package/dist/domains/remove.js +24 -2
  48. package/dist/examples.js +34 -2
  49. package/dist/find-projects.js +16 -2
  50. package/dist/fresh.js +17 -2
  51. package/dist/generate/action-types.js +9 -4
  52. package/dist/generate/component-meta.js +9 -3
  53. package/dist/generate/custom-cli-file.js +20 -3
  54. package/dist/generate/env-files.js +46 -6
  55. package/dist/generate/ide-helpers.js +8 -3
  56. package/dist/generate/index.js +158 -2
  57. package/dist/generate/lib-entries.js +16 -3
  58. package/dist/generate/pantry-file.js +46 -2
  59. package/dist/generate/symlink-core.js +2 -2
  60. package/dist/generate/types.js +3 -2
  61. package/dist/generate/vscode-custom-data.js +9 -3
  62. package/dist/generate/web-types.js +0 -1
  63. package/dist/helpers/component-meta.js +295 -2
  64. package/dist/helpers/index.js +1 -2
  65. package/dist/helpers/lib-entries.js +73 -2
  66. package/dist/helpers/package-json.js +45 -14
  67. package/dist/helpers/utils.js +105 -2
  68. package/dist/helpers/vscode-custom-data.js +30 -2
  69. package/dist/index.d.ts +13 -0
  70. package/dist/index.js +50 -2
  71. package/dist/key-generate.js +24 -2
  72. package/dist/lint/fix.js +4 -3
  73. package/dist/lint/index.js +4 -3
  74. package/dist/lint/lint.js +34 -2
  75. package/dist/make-command.js +64 -14
  76. package/dist/make-job.js +41 -16
  77. package/dist/make-policy.js +74 -26
  78. package/dist/make-resource.js +50 -21
  79. package/dist/make.js +328 -2
  80. package/dist/migrate/database.js +16 -2
  81. package/dist/migrate/fresh.js +31 -2
  82. package/dist/orm/base.js +0 -2
  83. package/dist/orm/test.js +0 -1
  84. package/dist/prepublish.js +16 -2
  85. package/dist/queue/clear.js +42 -2
  86. package/dist/queue/dlq-purge.js +28 -2
  87. package/dist/queue/dlq-retry.js +35 -2
  88. package/dist/queue/dlq.js +55 -5
  89. package/dist/queue/failed.js +69 -5
  90. package/dist/queue/flush.js +49 -3
  91. package/dist/queue/inspect.js +154 -8
  92. package/dist/queue/list.js +69 -5
  93. package/dist/queue/monitor.js +94 -6
  94. package/dist/queue/pause.js +28 -2
  95. package/dist/queue/quarantine.js +42 -3
  96. package/dist/queue/resume.js +27 -2
  97. package/dist/queue/retry.js +41 -2
  98. package/dist/queue/schedule-list.js +33 -5
  99. package/dist/queue/schedule.js +12 -2
  100. package/dist/queue/status.js +68 -5
  101. package/dist/queue/table.js +10 -2
  102. package/dist/queue/unquarantine.js +27 -2
  103. package/dist/queue/work.js +57 -2
  104. package/dist/release.js +21 -3
  105. package/dist/route/list.js +78 -3
  106. package/dist/run-action.js +1 -2
  107. package/dist/saas/setup.js +9 -2
  108. package/dist/scaffold-crud.js +164 -40
  109. package/dist/schedule/run.js +7 -2
  110. package/dist/search/flush.js +10 -2
  111. package/dist/search/import.js +10 -2
  112. package/dist/search/settings-list.js +10 -2
  113. package/dist/search/settings.js +9 -2
  114. package/dist/serve/api.js +35 -2
  115. package/dist/setup/index.js +1 -2
  116. package/dist/setup/ssl.js +271 -2
  117. package/dist/stacks.js +265 -2
  118. package/dist/templates.js +301 -2
  119. package/dist/test/feature.js +11 -2
  120. package/dist/test/index.js +11 -2
  121. package/dist/test/ui.js +4 -2
  122. package/dist/test/unit.js +11 -2
  123. package/dist/typecheck.js +4 -2
  124. package/dist/types.js +16 -2
  125. package/dist/upgrade/binary.js +29 -2
  126. package/dist/upgrade/bun.js +0 -1
  127. package/dist/upgrade/dependencies.js +0 -2
  128. package/dist/upgrade/framework-utils.js +193 -2
  129. package/dist/upgrade/framework.js +310 -7
  130. package/dist/upgrade/index.js +17 -2
  131. package/dist/upgrade/packages.js +99 -12
  132. package/dist/upgrade/shell.js +40 -4
  133. package/dist/upgrade.js +38 -2
  134. package/package.json +16 -16
  135. package/dist/chunk-0c8sd1t6.js +0 -2
  136. package/dist/chunk-0g2nzhx9.js +0 -240
  137. package/dist/chunk-0gadhvn7.js +0 -3
  138. package/dist/chunk-0h27aa4r.js +0 -93
  139. package/dist/chunk-3peh8kqs.js +0 -851
  140. package/dist/chunk-62gdfav8.js +0 -3
  141. package/dist/chunk-6bk53y02.js +0 -3
  142. package/dist/chunk-78c5n3s2.js +0 -3
  143. package/dist/chunk-c74qv38e.js +0 -17
  144. package/dist/chunk-cdk2r2jd.js +0 -1
  145. package/dist/chunk-dbz9dp2h.js +0 -3
  146. package/dist/chunk-e142dheq.js +0 -3
  147. package/dist/chunk-ec4gky40.js +0 -3
  148. package/dist/chunk-h3d81wrg.js +0 -3
  149. package/dist/chunk-m3q0x4h0.js +0 -688
  150. package/dist/chunk-mqgbyhph.js +0 -5
  151. package/dist/chunk-n24m8prb.js +0 -3
  152. package/dist/chunk-n5eqwhck.js +0 -7
  153. package/dist/chunk-nsvnqv8y.js +0 -5
  154. package/dist/chunk-nty6mmx4.js +0 -283
  155. package/dist/chunk-s735m9mg.js +0 -1
  156. package/dist/chunk-tpb8jgsk.js +0 -7
  157. package/dist/chunk-tsbk5vne.js +0 -3
  158. package/dist/chunk-xg9xscqz.js +0 -1
  159. package/dist/chunk-y6e448he.js +0 -7
@@ -1,8 +1,435 @@
1
- // @bun
2
- import{bb as m,cb as u,eb as b,fb as n}from"../chunk-ec4gky40.js";import{Lb as A}from"../chunk-h3d81wrg.js";import $ from"process";import{existsSync as j,readFileSync as o,writeFileSync as c}from"fs";import{bold as B,cyan as x,dim as Y,green as k}from"@stacksjs/cli";import{projectPath as T,storagePath as D}from"@stacksjs/path";var p=$.argv.indexOf("--verbose"),F=p!==-1&&$.argv[p+1]!=="false",s=Bun.nanoseconds(),i=console.log,e=console.warn,P=[];console.log=(...X)=>{P.push(X.map(String).join(" "))};console.warn=(...X)=>{P.push(X.map(String).join(" "))};var g=D("framework/defaults/views/dashboard"),EE=T("resources/views/dashboard"),h=Number($.env.PORT_ADMIN)||3002,V=await b(h),S=$.env.APP_URL||"",XE=S!==""&&S!=="localhost"&&!S.includes("localhost:"),y=XE?S.replace(/^https?:\/\//,""):null,C=y?`dashboard.${y}`:null,l=`${$.env.HOME}/.stacks/ssl`;function d(){console.log=i,console.warn=(...X)=>{let O=X.map(String).join(" ");if(O.includes("[STX] DOM API violation")||O.includes("unsafe expression"))return;e(...X)}}async function YE(){try{await import("@stacksjs/orm")}catch(E){if(F)console.warn("[dashboard] orm preload failed:",E?.message||E)}try{let{hoistDashboardGlobals:E}=await import("./dashboard-globals.js");await E({verbose:F})}catch(E){if(F)console.warn("[dashboard] globals preload failed:",E?.message||E)}let X;try{X=(await import("../chunk-0g2nzhx9.js")).serve}catch{X=(await import(T("pantry/bun-plugin-stx/dist/serve.js"))).serve}let O;try{let E=$.env.STX_SRC||`${$.env.HOME}/Code/Tools/stx/packages/stx/src/index.ts`;if(await Bun.file(E).exists())O=await import(E)}catch{}if(O&&F)console.log("[Dashboard] Using local stx checkout");if(!O)try{let E=T("pantry/@stacksjs/stx/dist/index.js");if(await Bun.file(E).exists())O=await import(E)}catch{}let{listConfigFiles:G,readConfig:K,updateConfigKey:Q}=await import(D("framework/defaults/resources/functions/dashboard/config-io.ts")),N={"/__deps/charts.js":D("framework/core/charts/dist/index.js")},U={...Object.fromEntries(Object.entries(N).map(([E,z])=>[E,async()=>{let W=Bun.file(z);if(!await W.exists())return new Response(`// dependency dist missing: ${z}
3
- // rebuild with: cd ${z.replace(/\/dist\/[^/]+$/,"")} && bun build.ts`,{status:500,headers:{"content-type":"text/javascript; charset=utf-8"}});return new Response(W,{headers:{"content-type":"text/javascript; charset=utf-8","cache-control":"no-cache"}})}])),"/api/config/list":async()=>{try{return Response.json({ok:!0,files:G()})}catch(E){return Response.json({ok:!1,error:E?.message},{status:500})}},"/api/config/read":async(E)=>{try{let W=new URL(E.url).searchParams.get("name")||"";if(!/^[\w-]+$/.test(W))return Response.json({ok:!1,error:"Invalid config name"},{status:400});let Z=await K(W);if(!Z)return Response.json({ok:!1,error:"Not found"},{status:404});return Response.json({ok:!0,name:W,fields:Z.fields,values:Z.values})}catch(z){return Response.json({ok:!1,error:z?.message},{status:500})}},"/api/config/source":async(E)=>{try{let W=new URL(E.url).searchParams.get("name")||"";if(!/^[\w-]+$/.test(W))return Response.json({ok:!1,error:"Invalid config name"},{status:400});let Z=await K(W);if(!Z)return Response.json({ok:!1,error:"Not found"},{status:404});return new Response(Z.source,{headers:{"content-type":"text/plain; charset=utf-8"}})}catch(z){return Response.json({ok:!1,error:z?.message},{status:500})}},"/api/config/update":async(E)=>{if(E.method!=="POST")return Response.json({ok:!1,error:"Method not allowed"},{status:405});try{let z=await E.json(),W=z.file?.replace(/\.ts$/,"");if(!W||!/^[\w-]+$/.test(W))return Response.json({ok:!1,error:"Invalid file"},{status:400});let Z=[];if(Array.isArray(z.updates))for(let _ of z.updates){let I=_.key??_.path;if(typeof I==="string")Z.push({key:I,value:_.value})}else if(z.key)Z.push({key:z.key,value:z.value});if(Z.length===0)return Response.json({ok:!1,error:"No updates supplied"},{status:400});let R=[];for(let _ of Z)try{let I=await Q(W,_.key,H(_.value));R.push({key:_.key,ok:!0,newValue:I.newValue})}catch(I){R.push({key:_.key,ok:!1,error:I?.message})}let a=R.every((_)=>_.ok);return Response.json({ok:a,file:W,results:R})}catch(z){return Response.json({ok:!1,error:z?.message},{status:500})}}};function H(E){if(typeof E==="boolean"||typeof E==="number")return E;if(E==="true")return!0;if(E==="false")return!1;if(E===""||E==null)return"";if(typeof E==="string"&&/^-?\d+(?:\.\d+)?$/.test(E))return Number(E);return String(E)}let L=null;try{let E=await import("@stacksjs/router"),z=(await import(T("app/Routes.ts"))).default;await E.loadRoutes(z),L=E.route}catch(E){if(F)console.warn("[dashboard] Stacks router init failed:",E?.message||E)}X({patterns:[EE,g],port:V,componentsDir:D("framework/defaults/resources/components/Dashboard"),layoutsDir:g,partialsDir:g,quiet:!0,routes:U,onRequest:L?async(E)=>{let W=new URL(E.url).pathname;if(W.startsWith("/api/config/")||W.startsWith("/__deps/"))return null;if(W.startsWith("/api/"))return L.handleRequest(E);if(E.method.toUpperCase()!=="GET")return L.handleRequest(E);return null}:void 0,auth:!1,...O&&{stxModule:O}}).catch((E)=>{d(),console.error(`
4
- Failed to start dashboard server: ${E?.message||E}
5
- `),$.exit(1)})}async function $E(){if(!C)return!1;if($.env.STACKS_PROXY_MANAGED)return!1;try{let{startProxies:X}=await import("@stacksjs/rpx");return await X({proxies:[{from:`localhost:${V}`,to:C,cleanUrls:!1}],https:{basePath:l,validityDays:825},regenerateUntrustedCerts:!1,verbose:F}),!0}catch(X){if(F)i(` ${Y(`Proxy: ${X}`)}`);return!1}}var zE=V+1;function J(X,O=200,G={}){return new Response(JSON.stringify(X),{status:O,headers:{"Content-Type":"application/json","Access-Control-Allow-Origin":"*","Access-Control-Allow-Methods":"POST, OPTIONS","Access-Control-Allow-Headers":"Content-Type",...G}})}function GE(){Bun.serve({port:zE,fetch:async(X)=>{if(X.method==="OPTIONS")return new Response(null,{headers:{"Access-Control-Allow-Origin":"*","Access-Control-Allow-Methods":"POST, OPTIONS","Access-Control-Allow-Headers":"Content-Type"}});if(new URL(X.url).pathname==="/api/config/update"&&X.method==="POST")try{let{file:G,updates:K}=await X.json();if(!G||G.includes("..")||!G.match(/^[\w.-]+\.ts$/))return J({error:"Invalid file name"},400);let Q=T(`config/${G}`),N=o(Q,"utf-8");for(let{path:U,value:H}of K){let w=U.split(".").pop().replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),E=new RegExp(`(${w}\\s*:\\s*)(?:'[^']*'|"[^"]*"|\\d+(?:\\.\\d+)?|true|false)`);if(E.test(N)){let z=/^\d+(?:\.\d+)?$/.test(H),W=H==="true"||H==="false",Z=H.replace(/'/g,"\\'").replace(/\$/g,"$$$$"),R=z||W?H:`'${Z}'`;N=N.replace(E,`$1${R}`)}}return c(Q,N,"utf-8"),J({success:!0})}catch(G){return J({error:G.message},500)}return J({error:"Not found"},404)}})}GE();var[,t]=await Promise.all([YE(),m(T("app/Models"),D("framework/defaults/app/Models"))]);async function OE(){let X={library:!0,content:!0,commerce:!0,marketing:!0,analytics:!0,management:!0,utilities:!0,ci:!1,data:{dashboard:!0,activity:!0,users:!0,teams:!0,subscribers:!0,allModels:!0}};try{let O=await import(T("config/dashboard.ts")),G=O.default?.sections??{},K=G.data??{};return{library:G.library?.enabled??!0,content:G.content?.enabled??!0,commerce:G.commerce?.enabled??!0,marketing:G.marketing?.enabled??!0,analytics:G.analytics?.enabled??!0,management:G.management?.enabled??!0,utilities:G.utilities?.enabled??!0,ci:O.default?.ci?.enabled??!1,data:{dashboard:K.dashboard?.enabled??!0,activity:K.activity?.enabled??!0,users:K.users?.enabled??!0,teams:K.teams?.enabled??!0,subscribers:K.subscribers?.enabled??!0,allModels:K.allModels?.enabled??!0}}}catch{return X}}var WE=await OE(),KE=D("framework/defaults/views/dashboard/.discovered-models.json"),ZE={models:u(t),sections:WE};c(KE,JSON.stringify(ZE,null,2));var NE=await n(V);d();var HE=!1;$E().then((X)=>{HE=X}).catch((X)=>{if(F)console.warn("[Dashboard] Reverse proxy failed:",X)});var v=C?`https://${C}`:null,q=`http://localhost:${V}`,r=`http://localhost:${V}/`,_E=(Bun.nanoseconds()-s)/1e6;console.log();console.log(` ${B(x("stacks dashboard"))}`);console.log();if(v)console.log(` ${k("\u279C")} ${B("Local")}: ${x(v)}`),console.log(` ${Y("\u279C")} ${Y("Origin")}: ${Y(q)}`);else console.log(` ${k("\u279C")} ${B("Local")}: ${x(q)}`);console.log(` ${k("\u279C")} ${B("Window")}: ${Y("Stacks Dashboard")} ${Y("1400\xD7900")}`);console.log(` ${k("\u279C")} ${B("Models")}: ${Y(`${t.length} discovered`)}`);if(V!==h)console.log(` ${Y("\u279C")} ${Y(`Port ${h} in use, using ${V}`)}`);if(!NE)console.log(` ${Y("\u26A0")} ${Y("Dev server may not be ready yet")}`);console.log();console.log(` ${Y(`ready in ${_E.toFixed(0)} ms`)}`);if(F){if(console.log(),console.log(` ${Y("\u279C")} ${Y("Sidebar")}: ${Y(`${sidebarConfig.sections.length} sections, 240px`)}`),console.log(` ${Y("\u279C")} ${Y("URL")}: ${Y(r)}`),C)console.log(` ${Y("\u279C")} ${Y("SSL")}: ${Y(l)}`),console.log(` ${Y("\u279C")} ${Y("Proxy")}: ${Y(`localhost:${V} \u2192 ${C}`)}`);if(P.length>0){console.log();for(let X of P)console.log(` ${Y(X)}`)}}console.log();var M,f=$.env.HOME?`${$.env.HOME}/Code/Tools/craft/packages/typescript/src/index.ts`:void 0;if(f&&j(f))try{({createApp:M}=await import(f))}catch{}if(!M){let X=["craft-native","@craft-native/craft","@stacksjs/ts-craft"];for(let O of X)try{if({createApp:M}=await import(O),M)break}catch{}}if(M){let X=T("resources/assets/images/app-icon.png"),O=D("framework/defaults/resources/assets/images/app-icon.png"),G=await Bun.file(X).exists()?X:await Bun.file(O).exists()?O:void 0,K=(()=>{let N=$.env.CRAFT_BIN;if(N)return j(N)?N:void 0;let U=`${$.env.HOME}/Code/Tools/craft/craft`;if(j(U))return U;let H=`${$.env.HOME}/Code/Tools/craft/bin/craft`;if(j(H))return H;let L=`${$.env.HOME}/Code/Tools/craft/packages/zig/zig-out/bin/craft`;if(j(L))return L;let w=`${$.env.HOME}/Documents/Projects/craft/packages/zig/zig-out/bin/craft`;if(j(w))return w;return})();if(!K)M=void 0;if(!M)console.log(` ${Y("Native window unavailable. Set CRAFT_BIN to a craft binary, or open the URL above in a browser.")}
6
- `),await new Promise(()=>{});let Q=M({url:r,quiet:!F,...K&&{craftPath:K},window:{title:"Stacks Dashboard",width:1400,height:900,titlebarHidden:!0,webSidebarMaterial:!0,webSidebarWidth:250,...G&&{icon:G}}});$.on("SIGINT",()=>{Q.close(),$.exit(0)}),$.on("SIGTERM",()=>{Q.close(),$.exit(0)});try{await Q.show(),$.exit(0)}catch{let N=v||q;console.log(` ${Y("Dashboard available at:")} ${x(N)}
7
- `),await new Promise(()=>{})}}else console.log(` ${Y("Native window unavailable. Set CRAFT_BIN to a craft binary, or open the URL above in a browser.")}
8
- `),$.on("SIGINT",()=>$.exit(0)),$.on("SIGTERM",()=>$.exit(0)),await new Promise(()=>{});
1
+ import process from "node:process";
2
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
3
+ import { bold, cyan, dim, green, red } from "@stacksjs/cli";
4
+ import { projectPath, storagePath } from "@stacksjs/path";
5
+ import { buildDashboardUrl, buildManifest, discoverModels, findAvailablePort, waitForServer } from "./dashboard-utils";
6
+ const verboseIdx = process.argv.indexOf("--verbose"), verbose = verboseIdx !== -1 && process.argv[verboseIdx + 1] !== "false", startTime = Bun.nanoseconds(), originalConsoleLog = console.log, originalConsoleWarn = console.warn, bufferedLogs = [];
7
+ console.log = (...args) => {
8
+ bufferedLogs.push(args.map(String).join(" "));
9
+ };
10
+ console.warn = (...args) => {
11
+ bufferedLogs.push(args.map(String).join(" "));
12
+ };
13
+ const dashboardPath = storagePath("framework/defaults/views/dashboard"), userDashboardPath = projectPath("resources/views/dashboard"), preferredPort = Number(process.env.PORT_ADMIN) || 3002, dashboardPort = await findAvailablePort(preferredPort), appUrl = process.env.APP_URL || "", hasCustomDomain = appUrl !== "" && appUrl !== "localhost" && !appUrl.includes("localhost:"), domain = hasCustomDomain ? appUrl.replace(/^https?:\/\//, "") : null, dashboardDomain = domain ? `dashboard.${domain}` : null, sslBasePath = `${process.env.HOME}/.stacks/ssl`;
14
+ function restoreConsole() {
15
+ console.log = originalConsoleLog;
16
+ console.warn = (...args) => {
17
+ const msg = args.map(String).join(" ");
18
+ if (msg.includes("[STX] DOM API violation") || msg.includes("unsafe expression"))
19
+ return;
20
+ originalConsoleWarn(...args);
21
+ };
22
+ }
23
+ async function startStxServer() {
24
+ try {
25
+ await import("@stacksjs/orm");
26
+ } catch (err) {
27
+ if (verbose)
28
+ console.warn("[dashboard] orm preload failed:", err?.message || err);
29
+ }
30
+ try {
31
+ const { hoistDashboardGlobals } = await import("./dashboard-globals");
32
+ await hoistDashboardGlobals({ verbose });
33
+ } catch (err) {
34
+ if (verbose)
35
+ console.warn("[dashboard] globals preload failed:", err?.message || err);
36
+ }
37
+ let serve;
38
+ try {
39
+ serve = (await import("bun-plugin-stx/serve")).serve;
40
+ } catch {
41
+ serve = (await import(projectPath("pantry/bun-plugin-stx/dist/serve.js"))).serve;
42
+ }
43
+ let stxModule;
44
+ try {
45
+ const localStx = process.env.STX_SRC || `${process.env.HOME}/Code/Tools/stx/packages/stx/src/index.ts`;
46
+ if (await Bun.file(localStx).exists())
47
+ stxModule = await import(localStx);
48
+ } catch {}
49
+ if (stxModule && verbose)
50
+ console.log("[Dashboard] Using local stx checkout");
51
+ if (!stxModule)
52
+ try {
53
+ const vendoredStx = projectPath("pantry/@stacksjs/stx/dist/index.js");
54
+ if (await Bun.file(vendoredStx).exists())
55
+ stxModule = await import(vendoredStx);
56
+ } catch {}
57
+ const { listConfigFiles, readConfig, updateConfigKey } = await import(storagePath("framework/defaults/resources/functions/dashboard/config-io.ts")), depRoutes = {
58
+ "/__deps/charts.js": storagePath("framework/core/charts/dist/index.js")
59
+ }, configRoutes = {
60
+ ...Object.fromEntries(Object.entries(depRoutes).map(([url, file]) => [
61
+ url,
62
+ async () => {
63
+ const f = Bun.file(file);
64
+ if (!await f.exists())
65
+ return new Response(`// dependency dist missing: ${file}
66
+ // rebuild with: cd ${file.replace(/\/dist\/[^/]+$/, "")} && bun build.ts`, { status: 500, headers: { "content-type": "text/javascript; charset=utf-8" } });
67
+ return new Response(f, {
68
+ headers: {
69
+ "content-type": "text/javascript; charset=utf-8",
70
+ "cache-control": "no-cache"
71
+ }
72
+ });
73
+ }
74
+ ])),
75
+ "/api/config/list": async () => {
76
+ try {
77
+ return Response.json({ ok: !0, files: listConfigFiles() });
78
+ } catch (e) {
79
+ return Response.json({ ok: !1, error: e?.message }, { status: 500 });
80
+ }
81
+ },
82
+ "/api/config/read": async (req) => {
83
+ try {
84
+ const name = new URL(req.url).searchParams.get("name") || "";
85
+ if (!/^[\w-]+$/.test(name))
86
+ return Response.json({ ok: !1, error: "Invalid config name" }, { status: 400 });
87
+ const result = await readConfig(name);
88
+ if (!result)
89
+ return Response.json({ ok: !1, error: "Not found" }, { status: 404 });
90
+ return Response.json({ ok: !0, name, fields: result.fields, values: result.values });
91
+ } catch (e) {
92
+ return Response.json({ ok: !1, error: e?.message }, { status: 500 });
93
+ }
94
+ },
95
+ "/api/config/source": async (req) => {
96
+ try {
97
+ const name = new URL(req.url).searchParams.get("name") || "";
98
+ if (!/^[\w-]+$/.test(name))
99
+ return Response.json({ ok: !1, error: "Invalid config name" }, { status: 400 });
100
+ const result = await readConfig(name);
101
+ if (!result)
102
+ return Response.json({ ok: !1, error: "Not found" }, { status: 404 });
103
+ return new Response(result.source, { headers: { "content-type": "text/plain; charset=utf-8" } });
104
+ } catch (e) {
105
+ return Response.json({ ok: !1, error: e?.message }, { status: 500 });
106
+ }
107
+ },
108
+ "/api/config/update": async (req) => {
109
+ if (req.method !== "POST")
110
+ return Response.json({ ok: !1, error: "Method not allowed" }, { status: 405 });
111
+ try {
112
+ const body = await req.json(), file = body.file?.replace(/\.ts$/, "");
113
+ if (!file || !/^[\w-]+$/.test(file))
114
+ return Response.json({ ok: !1, error: "Invalid file" }, { status: 400 });
115
+ const updates = [];
116
+ if (Array.isArray(body.updates))
117
+ for (const u of body.updates) {
118
+ const k = u.key ?? u.path;
119
+ if (typeof k === "string")
120
+ updates.push({ key: k, value: u.value });
121
+ }
122
+ else if (body.key)
123
+ updates.push({ key: body.key, value: body.value });
124
+ if (updates.length === 0)
125
+ return Response.json({ ok: !1, error: "No updates supplied" }, { status: 400 });
126
+ const results = [];
127
+ for (const u of updates)
128
+ try {
129
+ const r = await updateConfigKey(file, u.key, coerce(u.value));
130
+ results.push({ key: u.key, ok: !0, newValue: r.newValue });
131
+ } catch (err) {
132
+ results.push({ key: u.key, ok: !1, error: err?.message });
133
+ }
134
+ const allOk = results.every((r) => r.ok);
135
+ return Response.json({ ok: allOk, file, results });
136
+ } catch (e) {
137
+ return Response.json({ ok: !1, error: e?.message }, { status: 500 });
138
+ }
139
+ }
140
+ };
141
+ function coerce(v) {
142
+ if (typeof v === "boolean" || typeof v === "number")
143
+ return v;
144
+ if (v === "true")
145
+ return !0;
146
+ if (v === "false")
147
+ return !1;
148
+ if (v === "" || v == null)
149
+ return "";
150
+ if (typeof v === "string" && /^-?\d+(?:\.\d+)?$/.test(v))
151
+ return Number(v);
152
+ return String(v);
153
+ }
154
+ let stacksRoute = null;
155
+ try {
156
+ const router = await import("@stacksjs/router"), routeRegistry = (await import(projectPath("app/Routes.ts"))).default;
157
+ await router.loadRoutes(routeRegistry);
158
+ stacksRoute = router.route;
159
+ } catch (err) {
160
+ const detail = err?.message || String(err);
161
+ console.error(`
162
+ ${red(bold(" Dashboard API disabled: Stacks router init failed."))}`);
163
+ console.error(` ${detail}`);
164
+ console.error(dim(" Every /api/dashboard/* endpoint will return 404; pages render but show no data."));
165
+ console.error(dim(` Re-run with --verbose for the full stack trace.
166
+ `));
167
+ if (verbose)
168
+ console.error(err);
169
+ }
170
+ serve({
171
+ patterns: [userDashboardPath, dashboardPath],
172
+ port: dashboardPort,
173
+ componentsDir: storagePath("framework/defaults/resources/components/Dashboard"),
174
+ layoutsDir: dashboardPath,
175
+ partialsDir: dashboardPath,
176
+ quiet: !0,
177
+ routes: configRoutes,
178
+ onRequest: stacksRoute ? async (req) => {
179
+ const pathname = new URL(req.url).pathname;
180
+ if (pathname.startsWith("/api/config/") || pathname.startsWith("/__deps/"))
181
+ return null;
182
+ if (pathname.startsWith("/api/"))
183
+ return stacksRoute.handleRequest(req);
184
+ if (req.method.toUpperCase() !== "GET")
185
+ return stacksRoute.handleRequest(req);
186
+ return null;
187
+ } : void 0,
188
+ auth: !1,
189
+ ...stxModule && { stxModule }
190
+ }).catch((err) => {
191
+ restoreConsole();
192
+ console.error(`
193
+ Failed to start dashboard server: ${err?.message || err}
194
+ `);
195
+ process.exit(1);
196
+ });
197
+ }
198
+ async function startReverseProxy() {
199
+ if (!dashboardDomain)
200
+ return !1;
201
+ if (process.env.STACKS_PROXY_MANAGED)
202
+ return !1;
203
+ try {
204
+ const { startProxies } = await import("@stacksjs/rpx");
205
+ await startProxies({
206
+ proxies: [
207
+ { from: `localhost:${dashboardPort}`, to: dashboardDomain, cleanUrls: !1 }
208
+ ],
209
+ https: {
210
+ basePath: sslBasePath,
211
+ validityDays: 825
212
+ },
213
+ regenerateUntrustedCerts: !1,
214
+ verbose
215
+ });
216
+ return !0;
217
+ } catch (error) {
218
+ if (verbose)
219
+ originalConsoleLog(` ${dim(`Proxy: ${error}`)}`);
220
+ return !1;
221
+ }
222
+ }
223
+ const configApiPort = dashboardPort + 1;
224
+ function jsonResponse(data, status = 200, extraHeaders = {}) {
225
+ return new Response(JSON.stringify(data), {
226
+ status,
227
+ headers: {
228
+ "Content-Type": "application/json",
229
+ "Access-Control-Allow-Origin": "*",
230
+ "Access-Control-Allow-Methods": "POST, OPTIONS",
231
+ "Access-Control-Allow-Headers": "Content-Type",
232
+ ...extraHeaders
233
+ }
234
+ });
235
+ }
236
+ function startConfigApi() {
237
+ Bun.serve({
238
+ port: configApiPort,
239
+ fetch: async (req) => {
240
+ if (req.method === "OPTIONS")
241
+ return new Response(null, {
242
+ headers: {
243
+ "Access-Control-Allow-Origin": "*",
244
+ "Access-Control-Allow-Methods": "POST, OPTIONS",
245
+ "Access-Control-Allow-Headers": "Content-Type"
246
+ }
247
+ });
248
+ if (new URL(req.url).pathname === "/api/config/update" && req.method === "POST")
249
+ try {
250
+ const { file, updates } = await req.json();
251
+ if (!file || file.includes("..") || !file.match(/^[\w.-]+\.ts$/))
252
+ return jsonResponse({ error: "Invalid file name" }, 400);
253
+ const filePath = projectPath(`config/${file}`);
254
+ let content = readFileSync(filePath, "utf-8");
255
+ for (const { path: keyPath, value } of updates) {
256
+ const escapedKey = keyPath.split(".").pop().replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), pattern = new RegExp(`(${escapedKey}\\s*:\\s*)(?:'[^']*'|"[^"]*"|\\d+(?:\\.\\d+)?|true|false)`);
257
+ if (pattern.test(content)) {
258
+ const isNum = /^\d+(?:\.\d+)?$/.test(value), isBool = value === "true" || value === "false", sanitizedValue = value.replace(/'/g, "\\'").replace(/\$/g, "$$$$"), replacement = isNum || isBool ? value : `'${sanitizedValue}'`;
259
+ content = content.replace(pattern, `$1${replacement}`);
260
+ }
261
+ }
262
+ writeFileSync(filePath, content, "utf-8");
263
+ return jsonResponse({ success: !0 });
264
+ } catch (err) {
265
+ return jsonResponse({ error: err.message }, 500);
266
+ }
267
+ return jsonResponse({ error: "Not found" }, 404);
268
+ }
269
+ });
270
+ }
271
+ startConfigApi();
272
+ const [, discoveredModels] = await Promise.all([
273
+ startStxServer(),
274
+ discoverModels(projectPath("app/Models"), storagePath("framework/defaults/app/Models"))
275
+ ]);
276
+ async function loadDashboardToggles() {
277
+ const fallback = {
278
+ library: !0,
279
+ content: !0,
280
+ commerce: !0,
281
+ marketing: !0,
282
+ analytics: !0,
283
+ management: !0,
284
+ utilities: !0,
285
+ ci: !1,
286
+ data: { dashboard: !0, activity: !0, users: !0, teams: !0, subscribers: !0, allModels: !0 }
287
+ };
288
+ try {
289
+ const mod = await import(projectPath("config/dashboard.ts")), sections = mod.default?.sections ?? {}, data = sections.data ?? {};
290
+ return {
291
+ library: sections.library?.enabled ?? !0,
292
+ content: sections.content?.enabled ?? !0,
293
+ commerce: sections.commerce?.enabled ?? !0,
294
+ marketing: sections.marketing?.enabled ?? !0,
295
+ analytics: sections.analytics?.enabled ?? !0,
296
+ management: sections.management?.enabled ?? !0,
297
+ utilities: sections.utilities?.enabled ?? !0,
298
+ ci: mod.default?.ci?.enabled ?? !1,
299
+ data: {
300
+ dashboard: data.dashboard?.enabled ?? !0,
301
+ activity: data.activity?.enabled ?? !0,
302
+ users: data.users?.enabled ?? !0,
303
+ teams: data.teams?.enabled ?? !0,
304
+ subscribers: data.subscribers?.enabled ?? !0,
305
+ allModels: data.allModels?.enabled ?? !0
306
+ }
307
+ };
308
+ } catch {
309
+ return fallback;
310
+ }
311
+ }
312
+ const dashboardToggles = await loadDashboardToggles(), manifestPath = storagePath("framework/defaults/views/dashboard/.discovered-models.json"), manifestPayload = {
313
+ models: buildManifest(discoveredModels),
314
+ sections: dashboardToggles
315
+ };
316
+ writeFileSync(manifestPath, JSON.stringify(manifestPayload, null, 2));
317
+ const serverReady = await waitForServer(dashboardPort);
318
+ restoreConsole();
319
+ let proxyStarted = !1;
320
+ startReverseProxy().then((ok) => {
321
+ proxyStarted = ok;
322
+ }).catch((err) => {
323
+ if (verbose)
324
+ console.warn("[Dashboard] Reverse proxy failed:", err);
325
+ });
326
+ const dashboardHttpsUrl = dashboardDomain ? `https://${dashboardDomain}` : null, dashboardLocalUrl = `http://localhost:${dashboardPort}`, initialUrl = `http://localhost:${dashboardPort}/`, elapsedMs = (Bun.nanoseconds() - startTime) / 1e6;
327
+ console.log();
328
+ console.log(` ${bold(cyan("stacks dashboard"))}`);
329
+ console.log();
330
+ if (dashboardHttpsUrl) {
331
+ console.log(` ${green("\u279C")} ${bold("Local")}: ${cyan(dashboardHttpsUrl)}`);
332
+ console.log(` ${dim("\u279C")} ${dim("Origin")}: ${dim(dashboardLocalUrl)}`);
333
+ } else
334
+ console.log(` ${green("\u279C")} ${bold("Local")}: ${cyan(dashboardLocalUrl)}`);
335
+ console.log(` ${green("\u279C")} ${bold("Window")}: ${dim("Stacks Dashboard")} ${dim("1400\xD7900")}`);
336
+ console.log(` ${green("\u279C")} ${bold("Models")}: ${dim(`${discoveredModels.length} discovered`)}`);
337
+ if (dashboardPort !== preferredPort)
338
+ console.log(` ${dim("\u279C")} ${dim(`Port ${preferredPort} in use, using ${dashboardPort}`)}`);
339
+ if (!serverReady)
340
+ console.log(` ${dim("\u26A0")} ${dim("Dev server may not be ready yet")}`);
341
+ console.log();
342
+ console.log(` ${dim(`ready in ${elapsedMs.toFixed(0)} ms`)}`);
343
+ if (verbose) {
344
+ console.log();
345
+ console.log(` ${dim("\u279C")} ${dim("URL")}: ${dim(initialUrl)}`);
346
+ if (dashboardDomain) {
347
+ console.log(` ${dim("\u279C")} ${dim("SSL")}: ${dim(sslBasePath)}`);
348
+ console.log(` ${dim("\u279C")} ${dim("Proxy")}: ${dim(`localhost:${dashboardPort} \u2192 ${dashboardDomain}`)}`);
349
+ }
350
+ if (bufferedLogs.length > 0) {
351
+ console.log();
352
+ for (const line of bufferedLogs)
353
+ console.log(` ${dim(line)}`);
354
+ }
355
+ }
356
+ console.log();
357
+ let createApp;
358
+ const localCraftSdk = process.env.HOME ? `${process.env.HOME}/Code/Tools/craft/packages/typescript/src/index.ts` : void 0;
359
+ if (localCraftSdk && existsSync(localCraftSdk))
360
+ try {
361
+ ({ createApp } = await import(localCraftSdk));
362
+ } catch {}
363
+ if (!createApp) {
364
+ const packageNames = ["craft-native", "@craft-native/craft", "@stacksjs/ts-craft"];
365
+ for (const packageName of packageNames)
366
+ try {
367
+ ({ createApp } = await import(packageName));
368
+ if (createApp)
369
+ break;
370
+ } catch {}
371
+ }
372
+ if (createApp) {
373
+ const userIconPath = projectPath("resources/assets/images/app-icon.png"), defaultIconPath = storagePath("framework/defaults/resources/assets/images/app-icon.png"), appIconPath = await Bun.file(userIconPath).exists() ? userIconPath : await Bun.file(defaultIconPath).exists() ? defaultIconPath : void 0, craftBinaryPath = (() => {
374
+ const explicit = process.env.CRAFT_BIN;
375
+ if (explicit)
376
+ return existsSync(explicit) ? explicit : void 0;
377
+ const codeTools = `${process.env.HOME}/Code/Tools/craft/craft`;
378
+ if (existsSync(codeTools))
379
+ return codeTools;
380
+ const codeToolsBin = `${process.env.HOME}/Code/Tools/craft/bin/craft`;
381
+ if (existsSync(codeToolsBin))
382
+ return codeToolsBin;
383
+ const codeToolsZig = `${process.env.HOME}/Code/Tools/craft/packages/zig/zig-out/bin/craft`;
384
+ if (existsSync(codeToolsZig))
385
+ return codeToolsZig;
386
+ const homeRel = `${process.env.HOME}/Documents/Projects/craft/packages/zig/zig-out/bin/craft`;
387
+ if (existsSync(homeRel))
388
+ return homeRel;
389
+ return;
390
+ })();
391
+ if (!craftBinaryPath)
392
+ createApp = void 0;
393
+ if (!createApp) {
394
+ console.log(` ${dim("Native window unavailable. Set CRAFT_BIN to a craft binary, or open the URL above in a browser.")}
395
+ `);
396
+ await new Promise(() => {});
397
+ }
398
+ const app = createApp({
399
+ url: initialUrl,
400
+ quiet: !verbose,
401
+ ...craftBinaryPath && { craftPath: craftBinaryPath },
402
+ window: {
403
+ title: "Stacks Dashboard",
404
+ width: 1400,
405
+ height: 900,
406
+ titlebarHidden: !0,
407
+ webSidebarMaterial: !0,
408
+ webSidebarWidth: 250,
409
+ ...appIconPath && { icon: appIconPath }
410
+ }
411
+ });
412
+ process.on("SIGINT", () => {
413
+ app.close();
414
+ process.exit(0);
415
+ });
416
+ process.on("SIGTERM", () => {
417
+ app.close();
418
+ process.exit(0);
419
+ });
420
+ try {
421
+ await app.show();
422
+ process.exit(0);
423
+ } catch {
424
+ const fallbackUrl = dashboardHttpsUrl || dashboardLocalUrl;
425
+ console.log(` ${dim("Dashboard available at:")} ${cyan(fallbackUrl)}
426
+ `);
427
+ await new Promise(() => {});
428
+ }
429
+ } else {
430
+ console.log(` ${dim("Native window unavailable. Set CRAFT_BIN to a craft binary, or open the URL above in a browser.")}
431
+ `);
432
+ process.on("SIGINT", () => process.exit(0));
433
+ process.on("SIGTERM", () => process.exit(0));
434
+ await new Promise(() => {});
435
+ }
@@ -1,2 +1,5 @@
1
- // @bun
2
- import{runCommand as r}from"@stacksjs/cli";import{frameworkPath as m}from"@stacksjs/path";await r("bun run dev",{cwd:m("views/desktop")});
1
+ import { runCommand } from "@stacksjs/cli";
2
+ import { frameworkPath } from "@stacksjs/path";
3
+ await runCommand("bun run dev", {
4
+ cwd: frameworkPath("views/desktop")
5
+ });
package/dist/dev/docs.js CHANGED
@@ -1,2 +1,24 @@
1
- // @bun
2
- import{overridesReady as n,config as t,docs as o}from"@stacksjs/config";import{projectPath as e}from"@stacksjs/path";import{startServer as a}from"@stacksjs/bunpress";await n;var c=Number(process.env.PORT_DOCS)||t.ports?.docs||3006,i=String(process.env.APP_URL||t.app?.url||""),s=i.replace(/^https?:\/\//,"").replace(/\/$/,""),p=s&&s!=="localhost"&&!s.includes(":"),r=p?`https://${s}/docs`:void 0,d={verbose:!1,docsDir:e("docs"),outDir:e("dist/docs"),...o,markdown:o.markdown??{},...r&&{sitemap:{...o.sitemap,baseUrl:r}}};await a({port:c,root:e("docs"),watch:!0,quiet:!0,config:d});
1
+ import { overridesReady, config as appConfig, docs } from "@stacksjs/config";
2
+ import { projectPath } from "@stacksjs/path";
3
+ import { startServer } from "@stacksjs/bunpress";
4
+ await overridesReady;
5
+ const port = Number(process.env.PORT_DOCS) || appConfig.ports?.docs || 3006, appUrl = String(process.env.APP_URL || appConfig.app?.url || ""), domain = appUrl.replace(/^https?:\/\//, "").replace(/\/$/, ""), pathMountedDocs = domain && domain !== "localhost" && !domain.includes(":"), devBaseUrl = pathMountedDocs ? `https://${domain}/docs` : void 0, bunPressConfig = {
6
+ verbose: !1,
7
+ docsDir: projectPath("docs"),
8
+ outDir: projectPath("dist/docs"),
9
+ ...docs,
10
+ markdown: docs.markdown ?? {},
11
+ ...devBaseUrl && {
12
+ sitemap: {
13
+ ...docs.sitemap,
14
+ baseUrl: devBaseUrl
15
+ }
16
+ }
17
+ };
18
+ await startServer({
19
+ port,
20
+ root: projectPath("docs"),
21
+ watch: !0,
22
+ quiet: !0,
23
+ config: bunPressConfig
24
+ });
package/dist/dev/index.js CHANGED
@@ -1,2 +1,29 @@
1
- // @bun
2
- import{ib as a,jb as b,kb as c,lb as d,mb as e,nb as f,ob as g,pb as h,qb as i}from"../chunk-62gdfav8.js";import"../chunk-s735m9mg.js";import"../chunk-0gadhvn7.js";import"../chunk-h3d81wrg.js";export{g as runSystemTrayDevServer,b as runFrontendDevServer,i as runDocsDevServer,a as runDevServer,h as runDesktopDevServer,f as runDashboardDevServer,e as runComponentsDevServer,c as runBackendDevServer,d as runApiDevServer};
1
+ import { Action } from "@stacksjs/enums";
2
+ import { runAction } from "../helpers";
3
+ export async function runDevServer(options) {
4
+ await runAction(Action.Dev, options);
5
+ }
6
+ export async function runFrontendDevServer(options) {
7
+ await runAction(Action.Dev, options);
8
+ }
9
+ export async function runBackendDevServer(options) {
10
+ await runApiDevServer(options);
11
+ }
12
+ export async function runApiDevServer(options) {
13
+ await runAction(Action.DevApi, options);
14
+ }
15
+ export async function runComponentsDevServer(options) {
16
+ await runAction(Action.DevComponents, options);
17
+ }
18
+ export async function runDashboardDevServer(options) {
19
+ await runAction(Action.DevDashboard, options);
20
+ }
21
+ export async function runSystemTrayDevServer(options) {
22
+ await runAction(Action.DevSystemTray, options);
23
+ }
24
+ export async function runDesktopDevServer(options) {
25
+ await runAction(Action.DevDesktop, options);
26
+ }
27
+ export async function runDocsDevServer(options) {
28
+ await runAction(Action.DevDocs, options);
29
+ }
@@ -1,2 +1,74 @@
1
- // @bun
2
- import{Lb as R}from"../chunk-h3d81wrg.js";import{existsSync as T,watch as U}from"fs";import V from"process";import{log as E}from"@stacksjs/logging";import{path as N}from"@stacksjs/path";var F,J=!1,K=!1;function _(){if(["0","false","off"].includes(String(V.env.STACKS_DEV_AUTO_MIGRATE).toLowerCase()))return;let A=[N.userModelsPath(""),N.storagePath("framework/models")].filter((z)=>{try{return T(z)}catch{return!1}});if(A.length===0)return;let I=(z)=>{if(z&&!z.endsWith(".ts"))return;if(F)clearTimeout(F);F=setTimeout(()=>void O(),400)},G=0;for(let z of A)try{U(z,{recursive:!0},(B,H)=>I(H)),G+=1}catch(B){E.debug(`[dev] model watch failed for ${z}: ${B instanceof Error?B.message:String(B)}`)}if(G>0)E.debug("[dev] auto-migrate watcher active \u2014 model edits will sync the database")}async function O(){if(J){K=!0;return}J=!0;try{let{previewPendingMigrations:A,generateMigrations:I,runDatabaseMigration:G}=await import("@stacksjs/database"),z=await A();if(z.length===0)return;let B=z.filter((Q)=>Q.destructive);if(B.length>0){E.warn(`[dev] ${B.length} destructive schema change${B.length===1?"":"s"} detected \u2014 not auto-applied. Run \`buddy migrate\` to review and apply.`);return}E.info(`[dev] Syncing database to models (${z.length} change${z.length===1?"":"s"})...`);let H=await I();if(H.isErr){E.error("[dev] auto-migrate: generation failed",H.error);return}let L=await G();if(L.isErr){E.error("[dev] auto-migrate: apply failed",L.error);return}E.success("[dev] Database is in sync with your models.")}catch(A){E.warn(`[dev] auto-migrate skipped: ${A instanceof Error?A.message:String(A)}`)}finally{if(J=!1,K){if(K=!1,F)clearTimeout(F);F=setTimeout(()=>void O(),400)}}}export{_ as startModelMigrationWatcher};
1
+ import { existsSync, watch } from "node:fs";
2
+ import process from "node:process";
3
+ import { log } from "@stacksjs/logging";
4
+ import { path } from "@stacksjs/path";
5
+ let debounce, running = !1, queued = !1;
6
+ export function startModelMigrationWatcher() {
7
+ if (["0", "false", "off"].includes(String(process.env.STACKS_DEV_AUTO_MIGRATE).toLowerCase()))
8
+ return;
9
+ const dirs = [path.userModelsPath(""), path.storagePath("framework/models")].filter((d) => {
10
+ try {
11
+ return existsSync(d);
12
+ } catch {
13
+ return !1;
14
+ }
15
+ });
16
+ if (dirs.length === 0)
17
+ return;
18
+ const schedule = (filename) => {
19
+ if (filename && !filename.endsWith(".ts"))
20
+ return;
21
+ if (debounce)
22
+ clearTimeout(debounce);
23
+ debounce = setTimeout(() => void reconcile(), 400);
24
+ };
25
+ let watching = 0;
26
+ for (const dir of dirs)
27
+ try {
28
+ watch(dir, { recursive: !0 }, (_event, filename) => schedule(filename));
29
+ watching += 1;
30
+ } catch (err) {
31
+ log.debug(`[dev] model watch failed for ${dir}: ${err instanceof Error ? err.message : String(err)}`);
32
+ }
33
+ if (watching > 0)
34
+ log.debug("[dev] auto-migrate watcher active \u2014 model edits will sync the database");
35
+ }
36
+ async function reconcile() {
37
+ if (running) {
38
+ queued = !0;
39
+ return;
40
+ }
41
+ running = !0;
42
+ try {
43
+ const { previewPendingMigrations, generateMigrations, runDatabaseMigration } = await import("@stacksjs/database"), ops = await previewPendingMigrations();
44
+ if (ops.length === 0)
45
+ return;
46
+ const destructive = ops.filter((o) => o.destructive);
47
+ if (destructive.length > 0) {
48
+ log.warn(`[dev] ${destructive.length} destructive schema change${destructive.length === 1 ? "" : "s"} detected \u2014 not auto-applied. Run \`buddy migrate\` to review and apply.`);
49
+ return;
50
+ }
51
+ log.info(`[dev] Syncing database to models (${ops.length} change${ops.length === 1 ? "" : "s"})...`);
52
+ const gen = await generateMigrations();
53
+ if (gen.isErr) {
54
+ log.error("[dev] auto-migrate: generation failed", gen.error);
55
+ return;
56
+ }
57
+ const run = await runDatabaseMigration();
58
+ if (run.isErr) {
59
+ log.error("[dev] auto-migrate: apply failed", run.error);
60
+ return;
61
+ }
62
+ log.success("[dev] Database is in sync with your models.");
63
+ } catch (err) {
64
+ log.warn(`[dev] auto-migrate skipped: ${err instanceof Error ? err.message : String(err)}`);
65
+ } finally {
66
+ running = !1;
67
+ if (queued) {
68
+ queued = !1;
69
+ if (debounce)
70
+ clearTimeout(debounce);
71
+ debounce = setTimeout(() => void reconcile(), 400);
72
+ }
73
+ }
74
+ }
@@ -1,2 +1,5 @@
1
- // @bun
2
- import{runCommand as r}from"@stacksjs/cli";import{frameworkPath as m}from"@stacksjs/path";await r("bun run dev",{cwd:m("system-tray")});
1
+ import { runCommand } from "@stacksjs/cli";
2
+ import { frameworkPath } from "@stacksjs/path";
3
+ await runCommand("bun run dev", {
4
+ cwd: frameworkPath("system-tray")
5
+ });