@crossdelta/platform-sdk 0.26.8 → 0.26.10

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/CHANGELOG.md CHANGED
@@ -5,6 +5,27 @@ All notable changes to `@crossdelta/platform-sdk` will be documented in this fil
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.26.10] - 2026-10-27
9
+
10
+ ### Fixed
11
+ - **The generated `setup-bun-install` action no longer caches the Bun binary.** Its default
12
+ `cache-paths` held all of `~/.bun`, so a cache restored after `setup-bun` put the previously
13
+ cached Bun back over the pinned one. Host steps then ran the old Bun, and the lockfile it wrote
14
+ for the Docker build context was refused as changed by the pinned Bun in the image. The default
15
+ is now `~/.bun/install/cache`, and a new step fails the job when `bun --revision` differs from
16
+ the revision `setup-bun` installed.
17
+
18
+ ## [0.26.9] - 2026-10-27
19
+
20
+ ### Security
21
+ - **Generated Hono and NestJS service Dockerfiles default to Bun `1.4.4`, and the NestJS one to
22
+ Node `24.22.1`.** Bun `1.4.3` and earlier bundle a brotli decoder with an out-of-bounds write
23
+ (CVE-2026-111497, GHSA-663w-cv8g-jj2p); `1.4.4` ships the fixed brotli `1.2.1`. The previous
24
+ defaults were Bun `1.2.23` and the floating Node tag `24`.
25
+ - **A new workspace falls back to `packageManager: bun@1.4.4` when `bun --version` cannot be read**,
26
+ instead of `bun@1.2.7`. The generated `build-and-deploy.yml` passes that value to every image as
27
+ the `BUN_VERSION` build argument, so the old fallback overrode the Dockerfile defaults above.
28
+
8
29
  ## [0.26.8] - 2026-10-25
9
30
 
10
31
  ### Changed
package/bin/cli.mjs CHANGED
@@ -312,7 +312,7 @@ export default handleEvent(${Zt(o)}Contract, async (data) => {
312
312
  console.log('Processing ${o}:', data)
313
313
  // TODO: Implement handler logic
314
314
  })
315
- `.trim();return{success:!0,message:`Would create handler for ${o}`,changes:[{type:"create",path:n,description:`Create event handler for ${o}`,preview:i.slice(0,500)}]}}return t.logger.debug(`Handler created: ${o} at ${n} (service: ${s})`),{success:!0}},qa={"stream.wired":Ja,"contract.created":za,"handler.created":Ka},Xt=(e,t,r)=>e.map(n=>{let o=qa[n.kind];return o?o(n,t,r):(t.logger.warn(`Unknown effect kind: ${n.kind}`),{success:!1,message:`Unknown effect kind: ${n.kind}`})});import{join as rt}from"path";import{deriveEventNames as Lc}from"@crossdelta/cloudevents";var Qt=(e,t,r={})=>({ok:!0,operation:e,summary:t,artifacts:r.artifacts??[],changes:r.changes??[],diagnostics:r.diagnostics??[],next:r.next,data:r.data});v();import{join as tr}from"path";import{readFileSync as Ya}from"fs";import{join as Za}from"path";import Xa from"handlebars";var Q=(e,t)=>Sn(e,t??`Templates directory not found. Searched in: ${e.join(", ")}`),er=(e,t={})=>{let r=Ya(e,"utf-8");return Xa.compile(r)(t)},Xe=e=>(t,r={})=>{let n=Za(e,t);return er(n,r)};var D=e=>e.toUpperCase().replace(/-/g,"_");var Yn=e=>e.split("-").map(t=>t.charAt(0).toUpperCase()+t.slice(1)).join(" ");var Qa=()=>typeof import.meta?.url=="string"?L(import.meta.url):typeof __dirname=="string"?__dirname:process.cwd(),ec=()=>{let e=Qa(),t=H();return Q([tr(e,"templates","hono-microservice"),tr(e,"..","hono-microservice","templates"),tr(t,"bin","templates","hono-microservice")],"Hono templates directory not found.")},Qe=(e,t={})=>Xe(ec())(e,t),Zn=()=>Qe("tsconfig.json.hbs"),Xn=(e="1.2.23")=>Qe("Dockerfile.hbs",{bunVersion:e}),Qn=()=>Qe("biome.json.hbs");var eo=(e,t=[])=>Qe("src/config/env.ts.hbs",{envKey:D(e),envVars:t});var tc=e=>{let t=["import './config/env'","import '@crossdelta/telemetry'",""];return e&&t.push("import { consumeJetStreams, isConsumerConnected } from '@crossdelta/cloudevents'"),t.push("import { Hono } from 'hono'"),t},rc=(e,t)=>{let r=[`const port = Number(process.env.${e}_PORT) || 8080`,"const app = new Hono()","","app.get('/health', (c) => {"," return c.json({ status: 'ok' })","})"];return t&&r.push("","app.get('/health/ready', (c) => {"," const ready = isConsumerConnected()"," return c.json({ status: ready ? 'ok' : 'not-ready' }, ready ? 200 : 503)","})"),r},nc=(e,t)=>["","// Start NATS JetStream consumer","consumeJetStreams({",` streams: [${t.map(r=>`'${r}'`).join(", ")}],`,` consumer: '${e}',`," discover: './src/events/**/*.handler.ts',","})"],oc=()=>["","Bun.serve({"," port,"," fetch: app.fetch,","})","","console.log(`\u{1F680} Service ready at http://localhost:${port}`)"],sc=e=>{let t=D(e.serviceName);return`${[...tc(e.hasEvents),"",...rc(t,e.hasEvents),...e.hasEvents?nc(e.serviceName,e.streams):[],...oc()].join(`
315
+ `.trim();return{success:!0,message:`Would create handler for ${o}`,changes:[{type:"create",path:n,description:`Create event handler for ${o}`,preview:i.slice(0,500)}]}}return t.logger.debug(`Handler created: ${o} at ${n} (service: ${s})`),{success:!0}},qa={"stream.wired":Ja,"contract.created":za,"handler.created":Ka},Xt=(e,t,r)=>e.map(n=>{let o=qa[n.kind];return o?o(n,t,r):(t.logger.warn(`Unknown effect kind: ${n.kind}`),{success:!1,message:`Unknown effect kind: ${n.kind}`})});import{join as rt}from"path";import{deriveEventNames as Lc}from"@crossdelta/cloudevents";var Qt=(e,t,r={})=>({ok:!0,operation:e,summary:t,artifacts:r.artifacts??[],changes:r.changes??[],diagnostics:r.diagnostics??[],next:r.next,data:r.data});v();import{join as tr}from"path";import{readFileSync as Ya}from"fs";import{join as Za}from"path";import Xa from"handlebars";var Q=(e,t)=>Sn(e,t??`Templates directory not found. Searched in: ${e.join(", ")}`),er=(e,t={})=>{let r=Ya(e,"utf-8");return Xa.compile(r)(t)},Xe=e=>(t,r={})=>{let n=Za(e,t);return er(n,r)};var D=e=>e.toUpperCase().replace(/-/g,"_");var Yn=e=>e.split("-").map(t=>t.charAt(0).toUpperCase()+t.slice(1)).join(" ");var Qa=()=>typeof import.meta?.url=="string"?L(import.meta.url):typeof __dirname=="string"?__dirname:process.cwd(),ec=()=>{let e=Qa(),t=H();return Q([tr(e,"templates","hono-microservice"),tr(e,"..","hono-microservice","templates"),tr(t,"bin","templates","hono-microservice")],"Hono templates directory not found.")},Qe=(e,t={})=>Xe(ec())(e,t),Zn=()=>Qe("tsconfig.json.hbs"),Xn=(e="1.4.4")=>Qe("Dockerfile.hbs",{bunVersion:e}),Qn=()=>Qe("biome.json.hbs");var eo=(e,t=[])=>Qe("src/config/env.ts.hbs",{envKey:D(e),envVars:t});var tc=e=>{let t=["import './config/env'","import '@crossdelta/telemetry'",""];return e&&t.push("import { consumeJetStreams, isConsumerConnected } from '@crossdelta/cloudevents'"),t.push("import { Hono } from 'hono'"),t},rc=(e,t)=>{let r=[`const port = Number(process.env.${e}_PORT) || 8080`,"const app = new Hono()","","app.get('/health', (c) => {"," return c.json({ status: 'ok' })","})"];return t&&r.push("","app.get('/health/ready', (c) => {"," const ready = isConsumerConnected()"," return c.json({ status: ready ? 'ok' : 'not-ready' }, ready ? 200 : 503)","})"),r},nc=(e,t)=>["","// Start NATS JetStream consumer","consumeJetStreams({",` streams: [${t.map(r=>`'${r}'`).join(", ")}],`,` consumer: '${e}',`," discover: './src/events/**/*.handler.ts',","})"],oc=()=>["","Bun.serve({"," port,"," fetch: app.fetch,","})","","console.log(`\u{1F680} Service ready at http://localhost:${port}`)"],sc=e=>{let t=D(e.serviceName);return`${[...tc(e.hasEvents),"",...rc(t,e.hasEvents),...e.hasEvents?nc(e.serviceName,e.streams):[],...oc()].join(`
316
316
  `)}
317
317
  `},ic=e=>{let t={hono:"^4.6.14","@crossdelta/telemetry":"workspace:*",zod:"^4.1.0",[`${e.workspaceScope}/contracts`]:"workspace:*"};e.hasEvents&&(t["@crossdelta/cloudevents"]="workspace:*");let r={name:e.packageName,version:"0.0.1",type:"module",scripts:{"start:dev":"bun --watch src/index.ts",start:"bun src/index.ts",test:"bun test"},dependencies:t,devDependencies:{"@types/bun":"^1.2.23"}};return JSON.stringify(r,null,2)},ac=e=>`# ${e.serviceName}
318
318
 
@@ -334,7 +334,7 @@ ${e.hasEvents?`
334
334
  This service consumes from:
335
335
  ${e.streams.map(t=>`- ${t}`).join(`
336
336
  `)}`:""}
337
- `,cc=()=>"",to={name:"hono-bun",runtime:"bun",framework:"hono",files:[{path:"src/index.ts",content:sc},{path:"src/config/env.ts",content:e=>eo(e.serviceName,e.envVars)},{path:"package.json",content:ic},{path:"tsconfig.json",content:()=>Zn()},{path:"Dockerfile",content:()=>Xn()},{path:"biome.json",content:()=>Qn()},{path:"README.md",content:ac},{path:"src/events/.gitkeep",content:cc,skip:e=>!e.hasEvents}]};import{join as rr}from"path";var pc=()=>typeof import.meta?.url=="string"?L(import.meta.url):typeof __dirname=="string"?__dirname:process.cwd(),lc=()=>{let e=pc(),t=H();return Q([rr(e,"templates","nest-microservice"),rr(e,"..","nest-microservice","templates"),rr(t,"bin","templates","nest-microservice")],"NestJS templates directory not found.")},G=(e,t={})=>Xe(lc())(e,t),ro=(e,t=3e3)=>G("src/main.ts.hbs",{serviceName:e,envKey:D(e),displayName:Yn(e),defaultPort:t}),no=()=>G("src/app.context.ts.hbs",{}),oo=()=>G("src/events/events.module.ts.hbs",{}),so=e=>G("src/events/events.service.ts.hbs",{serviceName:e}),io=(e="24",t="1.2.23")=>G("Dockerfile.hbs",{nodeVersion:e,bunVersion:t}),ao=()=>G("biome.json.hbs",{}),co=(e,t=[])=>G("src/config/env.ts.hbs",{envKey:D(e),envVars:t});var mc=e=>{let t={"@nestjs/common":"^11.0.0","@nestjs/core":"^11.0.0","@nestjs/platform-express":"^11.0.0","@crossdelta/telemetry":"workspace:*","reflect-metadata":"^0.2.0",rxjs:"^7.8.0",zod:"^4.1.0",[`${e.workspaceScope}/contracts`]:"workspace:*"};e.hasEvents&&(t["@crossdelta/cloudevents"]="workspace:*");let r={name:e.packageName,version:"0.0.1",scripts:{"start:dev":"bun --watch src/main.ts",start:"node dist/main.js",build:"tsc",test:"bun test"},dependencies:t,devDependencies:{"@nestjs/cli":"^11.0.0","@types/node":"^24.0.0",typescript:"^5.7.0"}};return JSON.stringify(r,null,2)},dc=()=>JSON.stringify({extends:"../../../../tsconfig.json",compilerOptions:{outDir:"./dist",rootDir:"./src",experimentalDecorators:!0,emitDecoratorMetadata:!0},include:["src/**/*"],exclude:["node_modules","dist","**/*.test.ts"]},null,2),uc=e=>{let t=["Module","HttpModule"],r=["HttpModule"];return e.hasEvents&&(t.push("OnModuleInit"),r.push("EventsModule")),`import { ${t.join(", ")} } from '@nestjs/common'
337
+ `,cc=()=>"",to={name:"hono-bun",runtime:"bun",framework:"hono",files:[{path:"src/index.ts",content:sc},{path:"src/config/env.ts",content:e=>eo(e.serviceName,e.envVars)},{path:"package.json",content:ic},{path:"tsconfig.json",content:()=>Zn()},{path:"Dockerfile",content:()=>Xn()},{path:"biome.json",content:()=>Qn()},{path:"README.md",content:ac},{path:"src/events/.gitkeep",content:cc,skip:e=>!e.hasEvents}]};import{join as rr}from"path";var pc=()=>typeof import.meta?.url=="string"?L(import.meta.url):typeof __dirname=="string"?__dirname:process.cwd(),lc=()=>{let e=pc(),t=H();return Q([rr(e,"templates","nest-microservice"),rr(e,"..","nest-microservice","templates"),rr(t,"bin","templates","nest-microservice")],"NestJS templates directory not found.")},G=(e,t={})=>Xe(lc())(e,t),ro=(e,t=3e3)=>G("src/main.ts.hbs",{serviceName:e,envKey:D(e),displayName:Yn(e),defaultPort:t}),no=()=>G("src/app.context.ts.hbs",{}),oo=()=>G("src/events/events.module.ts.hbs",{}),so=e=>G("src/events/events.service.ts.hbs",{serviceName:e}),io=(e="24.22.1",t="1.4.4")=>G("Dockerfile.hbs",{nodeVersion:e,bunVersion:t}),ao=()=>G("biome.json.hbs",{}),co=(e,t=[])=>G("src/config/env.ts.hbs",{envKey:D(e),envVars:t});var mc=e=>{let t={"@nestjs/common":"^11.0.0","@nestjs/core":"^11.0.0","@nestjs/platform-express":"^11.0.0","@crossdelta/telemetry":"workspace:*","reflect-metadata":"^0.2.0",rxjs:"^7.8.0",zod:"^4.1.0",[`${e.workspaceScope}/contracts`]:"workspace:*"};e.hasEvents&&(t["@crossdelta/cloudevents"]="workspace:*");let r={name:e.packageName,version:"0.0.1",scripts:{"start:dev":"bun --watch src/main.ts",start:"node dist/main.js",build:"tsc",test:"bun test"},dependencies:t,devDependencies:{"@nestjs/cli":"^11.0.0","@types/node":"^24.0.0",typescript:"^5.7.0"}};return JSON.stringify(r,null,2)},dc=()=>JSON.stringify({extends:"../../../../tsconfig.json",compilerOptions:{outDir:"./dist",rootDir:"./src",experimentalDecorators:!0,emitDecoratorMetadata:!0},include:["src/**/*"],exclude:["node_modules","dist","**/*.test.ts"]},null,2),uc=e=>{let t=["Module","HttpModule"],r=["HttpModule"];return e.hasEvents&&(t.push("OnModuleInit"),r.push("EventsModule")),`import { ${t.join(", ")} } from '@nestjs/common'
338
338
  ${e.hasEvents?"import { EventsModule } from './events/events.module'":""}
339
339
 
340
340
  @Module({
@@ -464,7 +464,7 @@ ${e.message}`)}},te=()=>N().reduce((t,r)=>(r.initial&&t.push(r.name),t),[]),dp=e
464
464
  `);throw new Error(`Policy violation(s) detected:
465
465
  ${o}`)}e.forEach(Wp)},Hp=async(e,t,r)=>{let{projectName:n,serviceType:o,hasEvents:s,streams:i}=e,a=n.split("/").pop()||n,p=ko({type:o==="hono"?"hono-micro":"nest",name:a,path:n,hasEvents:s,streams:i}),l=p.effects.filter(T=>T.kind==="file:write"),d=Fp(o);Bp(l,d),e.assignedPort=p.port,e.elapsedTime+=r(),t.title=`Generated ${a} (port ${p.port}, ${e.elapsedTime}s)`},ut={title:"Generating service",task:async(e,t)=>R(t,async r=>{await Hp(e,t,r)})};var Go=M({name:"hono-micro",description:"Create a new Hono microservice with the Crossdelta setup.",arguments:[["[name]","Name or path of the service"]],options:[["-P, --package-manager <manager>","Specify the package manager (e.g., npm, yarn, pnpm, bun)"],["-y, --yes","Skip all prompts and use default values"],["--remove-dir","Remove existing directory before creating (used internally)"]],exampleUsage:"platform new hono-micro my-service",buildContext:async(e,t)=>{let r=(e.name??"my-hono-service").trim();!r.includes("/")&&!r.startsWith(".")&&(r=`${I().services}/${r}`);let n=_(r),o=t.packageManager??(t.yes&&w()),s=N(),i=t.yes?te():[],a=t.packageManager==="bun",c=!!t.yes||!!e.name&&e.name!=="my-hono-service";return{cwd:n,projectName:r,packageManager:o,shouldInstallBun:a,availableIntegrations:s,selectedIntegrations:i,serviceType:"hono",elapsedTime:0,userConfirmed:!!t.yes,skipInputPrompts:c,removeDir:!!t.removeDir||!!t.yes}},prompts:[ae,se,oe,ce,pt,re],actions:[ie,ne,ut,it,ct,mt],onComplete:({projectName:e,packageManager:t,selectedIntegrations:r,assignedPort:n})=>{m.breakLine().log(Ho.bold.green("Hono Microservice created successfully!")),ot(r),Ro(Gp(e),n??8080);let o=t??w();m.breakLine().log(`To start the project, run:
466
466
  ${Ho.bold(`cd ${e} && ${o} run dev`)}`).breakLine().log("Happy shipping with Hono! \u26A1\uFE0F").breakLine()}});import Uo from"chalk";v();h();var Vo=M({name:"nest-micro",description:"Create a new NestJS microservice with the Crossdelta setup.",arguments:[["[name]","Name or path of the service"]],options:[["-P, --package-manager <manager>","Specify the package manager (e.g., npm, yarn, pnpm, bun)"],["-y, --yes","Skip all prompts and use default values"],["--remove-dir","Remove existing directory before creating (used internally)"]],exampleUsage:"platform new nest-micro my-service",buildContext:async(e,t)=>{let r=(e.name??"my-nest-service").trim();!r.includes("/")&&!r.startsWith(".")&&(r=`${I().services}/${r}`);let n=_(r),o=t.packageManager??(t.yes&&w()),s=t.packageManager==="bun",i=N(),a=t.yes?te():[],c=!!t.yes||!!e.name;return{cwd:n,projectName:r,shouldInstallBun:s,packageManager:o,availableIntegrations:i,selectedIntegrations:a,serviceType:"nest",elapsedTime:0,userConfirmed:!!t.yes,skipInputPrompts:c,removeDir:!!t.removeDir||!!t.yes}},prompts:[ae,se,oe,ce,pt,re],actions:[ie,ne,ut,it,lt,jo,ct,mt],onComplete:({projectName:e,packageManager:t,selectedIntegrations:r})=>{m.breakLine().log(Uo.bold.green("NestJS Microservice created successfully!")),ot(r),m.breakLine().log(`To start the project, run:
467
- ${Uo.bold(`cd ${e} && ${t} run start:dev`)}`).breakLine().log("Happy coding! \u{1F389}").breakLine()}});import O from"chalk";h();import{spawnSync as Jp}from"child_process";import{chmodSync as zp,existsSync as gr,mkdirSync as Kp,readFileSync as qp,writeFileSync as fr}from"fs";import{dirname as Yp,join as hr}from"path";import{existsSync as Up}from"fs";import{join as gt}from"path";var Vp=()=>typeof import.meta?.url=="string"?L(import.meta.url):typeof __dirname=="string"?__dirname:process.cwd(),ur=()=>{let e=Vp(),t=H();return Q([gt(e,"templates","workspace"),gt(e,"..","..","..","..","templates","workspace"),gt(t,"bin","templates","workspace")],"Workspace templates directory not found.")},Jo=()=>{let e=ur(),t=gt(e,"packages","contracts");if(!Up(t))throw new Error(`Contracts templates directory not found at: ${t}`);return t},ft=(e,t)=>er(e,t);var zo=e=>{e.endsWith(".sh")&&zp(e,493)},Zp=[{template:"package.json.hbs",output:"package.json"},{template:"tsconfig.json.hbs",output:"tsconfig.json"},{template:"pnpm-workspace.yaml.hbs",output:"pnpm-workspace.yaml",forPackageManager:"pnpm"},{template:"biome.json.hbs",output:"biome.json"},{template:"turbo.json.hbs",output:"turbo.json"},{template:"bunfig.toml.hbs",output:"bunfig.toml"},{template:"gitignore.hbs",output:".gitignore"},{template:"editorconfig.hbs",output:".editorconfig"},{template:"npmrc.hbs",output:".npmrc"},{template:".vscode/settings.json",output:".vscode/settings.json",isStatic:!0},{template:".vscode/extensions.json",output:".vscode/extensions.json",isStatic:!0},{template:"infra/package.json.hbs",output:"infra/package.json"},{template:"infra/index.ts.hbs",output:"infra/index.ts"},{template:"infra/tsconfig.json.hbs",output:"infra/tsconfig.json"},{template:"infra/Pulumi.yaml.hbs",output:"infra/Pulumi.yaml"},{template:"infra/Pulumi.dev.yaml.hbs",output:"infra/Pulumi.dev.yaml"},{template:"apps/.gitkeep",output:"apps/.gitkeep",isStatic:!0},{template:"services/.gitkeep",output:"services/.gitkeep",isStatic:!0},{template:"packages/.gitkeep",output:"packages/.gitkeep",isStatic:!0},{template:"docs/.gitkeep",output:"docs/.gitkeep",isStatic:!0},{template:"infra/services/.gitkeep",output:"infra/services/.gitkeep",isStatic:!0},{template:".github/README.md",output:".github/README.md",isStatic:!0,isGitHubCI:!0},{template:".github/dependabot.yml",output:".github/dependabot.yml",isStatic:!0,isGitHubCI:!0},{template:".github/workflows/lint-and-tests.yml.hbs",output:".github/workflows/lint-and-tests.yml",isGitHubCI:!0},{template:".github/workflows/build-and-deploy.yml.hbs",output:".github/workflows/build-and-deploy.yml",isGitHubCI:!0},{template:".github/workflows/publish-packages.yml",output:".github/workflows/publish-packages.yml",isStatic:!0,isGitHubCI:!0},{template:".github/actions/setup-bun-install/action.yml.hbs",output:".github/actions/setup-bun-install/action.yml",isGitHubCI:!0},{template:".github/actions/check-image-tag-exists/action.yml",output:".github/actions/check-image-tag-exists/action.yml",isStatic:!0,isGitHubCI:!0},{template:".github/actions/check-image-tag-exists/index.js",output:".github/actions/check-image-tag-exists/index.js",isStatic:!0,isGitHubCI:!0},{template:".github/actions/generate-scope-matrix/action.yml",output:".github/actions/generate-scope-matrix/action.yml",isStatic:!0,isGitHubCI:!0},{template:".github/actions/generate-scope-matrix/index.js",output:".github/actions/generate-scope-matrix/index.js",isStatic:!0,isGitHubCI:!0},{template:".github/actions/prepare-build-context/action.yml",output:".github/actions/prepare-build-context/action.yml",isStatic:!0,isGitHubCI:!0},{template:"CLAUDE.md",output:"CLAUDE.md",isStatic:!0},{template:".claude/rules/cloudevents.md",output:".claude/rules/cloudevents.md",isStatic:!0}],Ko={title:"Creating workspace structure",task:async(e,t)=>{if(gr(e.cwd)){t.title="Workspace directory already exists, skipping...";return}let r=ur(),n=e.includeGitHubCI??!0,o=e.packageManager||"bun",s=c=>{try{let d=Jp(c,["--version"],{encoding:"utf-8",timeout:5e3}).stdout?.trim();if(d&&/^\d+\.\d+/.test(d))return`${c}@${d}`}catch{}return{bun:"bun@1.2.7",npm:"npm@10.9.2",yarn:"yarn@4.6.0",pnpm:"pnpm@9.15.2"}[c]||`${c}@latest`},i={projectName:e.projectName,scope:`@${e.projectName}`,githubOwner:e.githubOwner||"",pulumiStackBase:e.pulumiStackBase||e.projectName,packageManager:o,packageManagerVersion:s(o)},a=Zp.filter(c=>!(c.isGitHubCI&&!n||c.forPackageManager&&c.forPackageManager!==o));for(let c of a){let p=hr(e.cwd,c.output),l=Yp(p);if(gr(l)||Kp(l,{recursive:!0}),c.isStatic){let d=hr(r,c.template);gr(d)?fr(p,qp(d)):fr(p,""),zo(p)}else{let d=hr(r,c.template),T=ft(d,i);fr(p,T),zo(p)}}t.title="Workspace structure created"}};v();import{existsSync as Yo,mkdirSync as qo,readFileSync as Xp,writeFileSync as Qp}from"fs";import{join as ht}from"path";import el from"chalk";var tl=()=>{let e=ht(process.cwd(),"package.json");if(!Yo(e))throw new Error("Root package.json not found. Please run this command from the workspace root.");let r=JSON.parse(Xp(e,"utf-8")).name;if(!r)throw new Error('package.json must have a "name" field');return r.startsWith("@")?r:`@${r}`},rl=()=>{let e=tl(),{relativePath:t,packagePath:r}=x();if(Yo(r))throw new Error(`${t} already exists. Remove it first if you want to regenerate.`);let n=Jo(),o={scope:e,workspaceName:e.replace("@","").replace("/","-")};qo(r,{recursive:!0}),qo(ht(r,"src","events"),{recursive:!0});let s=[{template:"package.json.hbs",output:"package.json"},{template:"tsconfig.json.hbs",output:"tsconfig.json"},{template:"tsup.config.ts",output:"tsup.config.ts"},{template:"README.md.hbs",output:"README.md"},{template:"src/index.ts",output:"src/index.ts"},{template:"src/stream-policies.ts.hbs",output:"src/stream-policies.ts"},{template:"src/events/index.ts",output:"src/events/index.ts"}];for(let i of s){let a=ht(n,i.template),c=ht(r,i.output),p=ft(a,o);Qp(c,p,"utf-8")}},Zo={title:"Initializing contracts package",task:(e,t)=>{let r=process.cwd();try{process.chdir(e.cwd),rl(),t.title="Contracts package initialized"}catch(n){throw t.title=el.red(`Contracts package failed: ${n.message}`),n}finally{process.chdir(r)}}};import{spawnSync as nl}from"child_process";import ol from"chalk";var sl=["bunx","jackherd","init"],il=([e,...t],r)=>{let n=nl(e,t,{cwd:r,encoding:"utf-8"}),o=`${n.error?.message??""} ${n.stdout??""}${n.stderr??""}`.trim();return{exitCode:n.status??1,output:o}},al=(e,t=il)=>{let{exitCode:r,output:n}=t(sl,e);if(r!==0)throw new Error(`jackherd init failed (exit ${r}): ${n}`)},Xo={title:"Installing agent skills and rules",task:(e,t)=>{try{al(e.cwd),t.title="Agent skills and rules installed"}catch(r){throw t.title=ol.red(r.message),r}}};import cl from"chalk";var Qo={title:"Registering shell completion",task:async(e,t)=>{let r=process.cwd();try{process.chdir(e.cwd),await xe(),t.title="Shell completion registered"}catch{t.title=cl.dim("Shell completion skipped")}finally{process.chdir(r)}}};var es=M({name:"workspace",description:"Create a new workspace with infrastructure setup",arguments:[["[name]","Name or path of the workspace"]],options:[["-P, --package-manager <manager>","Specify the package manager (e.g., npm, yarn, pnpm, bun)"],["-y, --yes","Skip all prompts and use default values"],["-o, --github-owner <owner>","GitHub organization or username"],["-s, --pulumi-stack <name>","Pulumi stack base name (org/project)"],["--no-github","Skip GitHub CI/CD workflows"],["--remove-dir","Remove existing directory before creating (internal use)"]],exampleUsage:"pf new workspace my-platform -o my-org",buildContext:(e,t)=>{let r=(e.name??"my-platform").trim(),n=_(r),o=!!t.yes,s=t.packageManager??(o?w():void 0),i=t.packageManager==="bun",a=t.githubOwner??(o?"my-org":void 0),c=t.pulumiStack??(o?`${a}/${r}`:void 0),p=t.github===!1?!1:o?!0:void 0,l=!!e.name,d=t.removeDir||o;return{cwd:n,projectName:r,shouldInstallBun:i,packageManager:s,elapsedTime:0,userConfirmed:o,skipInputPrompts:l&&!o,removeDir:d,githubOwner:a,pulumiStackBase:c,includeGitHubCI:p}},prompts:[{enabled:e=>!e.userConfirmed,task:async(e,t)=>{try{let r=u();if(!await y(t).toggle({message:`\u26A0\uFE0F You are inside an existing workspace (${r}). Create a nested workspace anyway?`,initial:!1}))throw new Error("Workspace creation cancelled - already inside a workspace");t.title=O.yellow("Warning: Creating nested workspace")}catch(r){if(Ie(r)&&r.message?.includes("cancelled"))throw r;t.title="No existing workspace detected"}}},ae,se,Fo,Wo,Bo,oe,ce,re],actions:[ie,ne,Ko,Zo,lt,Xo,_o,Qo],onComplete:({projectName:e,includeGitHubCI:t})=>{m.breakLine().log(O.bold.green("Workspace created successfully!")),m.breakLine().log(O.bold("Next steps:")).log(` 1. ${O.cyan(`cd ${e}`)}`).log(` 2. ${O.cyan("exec $SHELL")} ${O.dim("(enable tab completion)")}`),t&&m.breakLine().log(O.bold("GitHub Setup:")).log(" Configure these secrets in your GitHub repository:").log(` \u2022 ${O.yellow("PULUMI_ACCESS_TOKEN")} - Pulumi Cloud access token`).log(` \u2022 ${O.yellow("DIGITALOCEAN_TOKEN")} - DigitalOcean API token`).log(` \u2022 ${O.yellow("NPM_TOKEN")} - (optional) NPM token for private packages`),m.breakLine().log(O.bold("Structure created:")).log(" apps/ # Frontend applications").log(" services/ # Backend microservices").log(" packages/").log(" \u2514\u2500\u2500 contracts/ # Shared event contracts").log(" infra/").log(" \u251C\u2500\u2500 index.ts # Pulumi entry point").log(" \u251C\u2500\u2500 services/ # Service configs").log(" \u251C\u2500\u2500 Pulumi.yaml # Pulumi project config").log(" \u2514\u2500\u2500 Pulumi.dev.yaml # Dev stack config"),t&&m.log(" .github/").log(" \u251C\u2500\u2500 workflows/ # CI/CD workflows").log(" \u2514\u2500\u2500 actions/ # Custom actions"),m.breakLine().log("Happy coding! \u{1F389}").breakLine()}});var yt=[{name:"Hono Microservice",value:"hono-micro",command:Go},{name:"Nest Microservice",value:"nest-micro",command:Vo},{name:"Workspace (Infrastructure)",value:"workspace",command:es}],ts=()=>{let e=M({name:"new",description:"Create a new project",arguments:[["[type]","Type of the project (e.g., nest-micro, hono-micro, workspace)"]],exampleUsage:"platform new hono-micro test-service",buildContext:t=>({projectType:t.type??"",elapsedTime:0,cwd:process.cwd()}),prompts:[{enabled:t=>!t.projectType,task:async(t,r)=>{let o=await y(r).select({message:"Select Project type",choices:yt});t.projectType=yt.find(s=>s.name===o)?.value||"",r.title=`Using ${pe.bold(o)}`}},{enabled:t=>t.projectType!=="workspace"&&t.projectType!=="",task:async(t,r)=>{try{u(),r.title="Workspace detected"}catch{let n=["Not in a workspace directory","",`Current directory: ${process.cwd()}`,"","This command must be run from within a workspace created with pf new workspace","","To create a new workspace, run:",""," pf new workspace my-platform"].join(`
467
+ ${Uo.bold(`cd ${e} && ${t} run start:dev`)}`).breakLine().log("Happy coding! \u{1F389}").breakLine()}});import O from"chalk";h();import{spawnSync as Jp}from"child_process";import{chmodSync as zp,existsSync as gr,mkdirSync as Kp,readFileSync as qp,writeFileSync as fr}from"fs";import{dirname as Yp,join as hr}from"path";import{existsSync as Up}from"fs";import{join as gt}from"path";var Vp=()=>typeof import.meta?.url=="string"?L(import.meta.url):typeof __dirname=="string"?__dirname:process.cwd(),ur=()=>{let e=Vp(),t=H();return Q([gt(e,"templates","workspace"),gt(e,"..","..","..","..","templates","workspace"),gt(t,"bin","templates","workspace")],"Workspace templates directory not found.")},Jo=()=>{let e=ur(),t=gt(e,"packages","contracts");if(!Up(t))throw new Error(`Contracts templates directory not found at: ${t}`);return t},ft=(e,t)=>er(e,t);var zo=e=>{e.endsWith(".sh")&&zp(e,493)},Zp=[{template:"package.json.hbs",output:"package.json"},{template:"tsconfig.json.hbs",output:"tsconfig.json"},{template:"pnpm-workspace.yaml.hbs",output:"pnpm-workspace.yaml",forPackageManager:"pnpm"},{template:"biome.json.hbs",output:"biome.json"},{template:"turbo.json.hbs",output:"turbo.json"},{template:"bunfig.toml.hbs",output:"bunfig.toml"},{template:"gitignore.hbs",output:".gitignore"},{template:"editorconfig.hbs",output:".editorconfig"},{template:"npmrc.hbs",output:".npmrc"},{template:".vscode/settings.json",output:".vscode/settings.json",isStatic:!0},{template:".vscode/extensions.json",output:".vscode/extensions.json",isStatic:!0},{template:"infra/package.json.hbs",output:"infra/package.json"},{template:"infra/index.ts.hbs",output:"infra/index.ts"},{template:"infra/tsconfig.json.hbs",output:"infra/tsconfig.json"},{template:"infra/Pulumi.yaml.hbs",output:"infra/Pulumi.yaml"},{template:"infra/Pulumi.dev.yaml.hbs",output:"infra/Pulumi.dev.yaml"},{template:"apps/.gitkeep",output:"apps/.gitkeep",isStatic:!0},{template:"services/.gitkeep",output:"services/.gitkeep",isStatic:!0},{template:"packages/.gitkeep",output:"packages/.gitkeep",isStatic:!0},{template:"docs/.gitkeep",output:"docs/.gitkeep",isStatic:!0},{template:"infra/services/.gitkeep",output:"infra/services/.gitkeep",isStatic:!0},{template:".github/README.md",output:".github/README.md",isStatic:!0,isGitHubCI:!0},{template:".github/dependabot.yml",output:".github/dependabot.yml",isStatic:!0,isGitHubCI:!0},{template:".github/workflows/lint-and-tests.yml.hbs",output:".github/workflows/lint-and-tests.yml",isGitHubCI:!0},{template:".github/workflows/build-and-deploy.yml.hbs",output:".github/workflows/build-and-deploy.yml",isGitHubCI:!0},{template:".github/workflows/publish-packages.yml",output:".github/workflows/publish-packages.yml",isStatic:!0,isGitHubCI:!0},{template:".github/actions/setup-bun-install/action.yml.hbs",output:".github/actions/setup-bun-install/action.yml",isGitHubCI:!0},{template:".github/actions/check-image-tag-exists/action.yml",output:".github/actions/check-image-tag-exists/action.yml",isStatic:!0,isGitHubCI:!0},{template:".github/actions/check-image-tag-exists/index.js",output:".github/actions/check-image-tag-exists/index.js",isStatic:!0,isGitHubCI:!0},{template:".github/actions/generate-scope-matrix/action.yml",output:".github/actions/generate-scope-matrix/action.yml",isStatic:!0,isGitHubCI:!0},{template:".github/actions/generate-scope-matrix/index.js",output:".github/actions/generate-scope-matrix/index.js",isStatic:!0,isGitHubCI:!0},{template:".github/actions/prepare-build-context/action.yml",output:".github/actions/prepare-build-context/action.yml",isStatic:!0,isGitHubCI:!0},{template:"CLAUDE.md",output:"CLAUDE.md",isStatic:!0},{template:".claude/rules/cloudevents.md",output:".claude/rules/cloudevents.md",isStatic:!0}],Ko={title:"Creating workspace structure",task:async(e,t)=>{if(gr(e.cwd)){t.title="Workspace directory already exists, skipping...";return}let r=ur(),n=e.includeGitHubCI??!0,o=e.packageManager||"bun",s=c=>{try{let d=Jp(c,["--version"],{encoding:"utf-8",timeout:5e3}).stdout?.trim();if(d&&/^\d+\.\d+/.test(d))return`${c}@${d}`}catch{}return{bun:"bun@1.4.4",npm:"npm@10.9.2",yarn:"yarn@4.6.0",pnpm:"pnpm@9.15.2"}[c]||`${c}@latest`},i={projectName:e.projectName,scope:`@${e.projectName}`,githubOwner:e.githubOwner||"",pulumiStackBase:e.pulumiStackBase||e.projectName,packageManager:o,packageManagerVersion:s(o)},a=Zp.filter(c=>!(c.isGitHubCI&&!n||c.forPackageManager&&c.forPackageManager!==o));for(let c of a){let p=hr(e.cwd,c.output),l=Yp(p);if(gr(l)||Kp(l,{recursive:!0}),c.isStatic){let d=hr(r,c.template);gr(d)?fr(p,qp(d)):fr(p,""),zo(p)}else{let d=hr(r,c.template),T=ft(d,i);fr(p,T),zo(p)}}t.title="Workspace structure created"}};v();import{existsSync as Yo,mkdirSync as qo,readFileSync as Xp,writeFileSync as Qp}from"fs";import{join as ht}from"path";import el from"chalk";var tl=()=>{let e=ht(process.cwd(),"package.json");if(!Yo(e))throw new Error("Root package.json not found. Please run this command from the workspace root.");let r=JSON.parse(Xp(e,"utf-8")).name;if(!r)throw new Error('package.json must have a "name" field');return r.startsWith("@")?r:`@${r}`},rl=()=>{let e=tl(),{relativePath:t,packagePath:r}=x();if(Yo(r))throw new Error(`${t} already exists. Remove it first if you want to regenerate.`);let n=Jo(),o={scope:e,workspaceName:e.replace("@","").replace("/","-")};qo(r,{recursive:!0}),qo(ht(r,"src","events"),{recursive:!0});let s=[{template:"package.json.hbs",output:"package.json"},{template:"tsconfig.json.hbs",output:"tsconfig.json"},{template:"tsup.config.ts",output:"tsup.config.ts"},{template:"README.md.hbs",output:"README.md"},{template:"src/index.ts",output:"src/index.ts"},{template:"src/stream-policies.ts.hbs",output:"src/stream-policies.ts"},{template:"src/events/index.ts",output:"src/events/index.ts"}];for(let i of s){let a=ht(n,i.template),c=ht(r,i.output),p=ft(a,o);Qp(c,p,"utf-8")}},Zo={title:"Initializing contracts package",task:(e,t)=>{let r=process.cwd();try{process.chdir(e.cwd),rl(),t.title="Contracts package initialized"}catch(n){throw t.title=el.red(`Contracts package failed: ${n.message}`),n}finally{process.chdir(r)}}};import{spawnSync as nl}from"child_process";import ol from"chalk";var sl=["bunx","jackherd","init"],il=([e,...t],r)=>{let n=nl(e,t,{cwd:r,encoding:"utf-8"}),o=`${n.error?.message??""} ${n.stdout??""}${n.stderr??""}`.trim();return{exitCode:n.status??1,output:o}},al=(e,t=il)=>{let{exitCode:r,output:n}=t(sl,e);if(r!==0)throw new Error(`jackherd init failed (exit ${r}): ${n}`)},Xo={title:"Installing agent skills and rules",task:(e,t)=>{try{al(e.cwd),t.title="Agent skills and rules installed"}catch(r){throw t.title=ol.red(r.message),r}}};import cl from"chalk";var Qo={title:"Registering shell completion",task:async(e,t)=>{let r=process.cwd();try{process.chdir(e.cwd),await xe(),t.title="Shell completion registered"}catch{t.title=cl.dim("Shell completion skipped")}finally{process.chdir(r)}}};var es=M({name:"workspace",description:"Create a new workspace with infrastructure setup",arguments:[["[name]","Name or path of the workspace"]],options:[["-P, --package-manager <manager>","Specify the package manager (e.g., npm, yarn, pnpm, bun)"],["-y, --yes","Skip all prompts and use default values"],["-o, --github-owner <owner>","GitHub organization or username"],["-s, --pulumi-stack <name>","Pulumi stack base name (org/project)"],["--no-github","Skip GitHub CI/CD workflows"],["--remove-dir","Remove existing directory before creating (internal use)"]],exampleUsage:"pf new workspace my-platform -o my-org",buildContext:(e,t)=>{let r=(e.name??"my-platform").trim(),n=_(r),o=!!t.yes,s=t.packageManager??(o?w():void 0),i=t.packageManager==="bun",a=t.githubOwner??(o?"my-org":void 0),c=t.pulumiStack??(o?`${a}/${r}`:void 0),p=t.github===!1?!1:o?!0:void 0,l=!!e.name,d=t.removeDir||o;return{cwd:n,projectName:r,shouldInstallBun:i,packageManager:s,elapsedTime:0,userConfirmed:o,skipInputPrompts:l&&!o,removeDir:d,githubOwner:a,pulumiStackBase:c,includeGitHubCI:p}},prompts:[{enabled:e=>!e.userConfirmed,task:async(e,t)=>{try{let r=u();if(!await y(t).toggle({message:`\u26A0\uFE0F You are inside an existing workspace (${r}). Create a nested workspace anyway?`,initial:!1}))throw new Error("Workspace creation cancelled - already inside a workspace");t.title=O.yellow("Warning: Creating nested workspace")}catch(r){if(Ie(r)&&r.message?.includes("cancelled"))throw r;t.title="No existing workspace detected"}}},ae,se,Fo,Wo,Bo,oe,ce,re],actions:[ie,ne,Ko,Zo,lt,Xo,_o,Qo],onComplete:({projectName:e,includeGitHubCI:t})=>{m.breakLine().log(O.bold.green("Workspace created successfully!")),m.breakLine().log(O.bold("Next steps:")).log(` 1. ${O.cyan(`cd ${e}`)}`).log(` 2. ${O.cyan("exec $SHELL")} ${O.dim("(enable tab completion)")}`),t&&m.breakLine().log(O.bold("GitHub Setup:")).log(" Configure these secrets in your GitHub repository:").log(` \u2022 ${O.yellow("PULUMI_ACCESS_TOKEN")} - Pulumi Cloud access token`).log(` \u2022 ${O.yellow("DIGITALOCEAN_TOKEN")} - DigitalOcean API token`).log(` \u2022 ${O.yellow("NPM_TOKEN")} - (optional) NPM token for private packages`),m.breakLine().log(O.bold("Structure created:")).log(" apps/ # Frontend applications").log(" services/ # Backend microservices").log(" packages/").log(" \u2514\u2500\u2500 contracts/ # Shared event contracts").log(" infra/").log(" \u251C\u2500\u2500 index.ts # Pulumi entry point").log(" \u251C\u2500\u2500 services/ # Service configs").log(" \u251C\u2500\u2500 Pulumi.yaml # Pulumi project config").log(" \u2514\u2500\u2500 Pulumi.dev.yaml # Dev stack config"),t&&m.log(" .github/").log(" \u251C\u2500\u2500 workflows/ # CI/CD workflows").log(" \u2514\u2500\u2500 actions/ # Custom actions"),m.breakLine().log("Happy coding! \u{1F389}").breakLine()}});var yt=[{name:"Hono Microservice",value:"hono-micro",command:Go},{name:"Nest Microservice",value:"nest-micro",command:Vo},{name:"Workspace (Infrastructure)",value:"workspace",command:es}],ts=()=>{let e=M({name:"new",description:"Create a new project",arguments:[["[type]","Type of the project (e.g., nest-micro, hono-micro, workspace)"]],exampleUsage:"platform new hono-micro test-service",buildContext:t=>({projectType:t.type??"",elapsedTime:0,cwd:process.cwd()}),prompts:[{enabled:t=>!t.projectType,task:async(t,r)=>{let o=await y(r).select({message:"Select Project type",choices:yt});t.projectType=yt.find(s=>s.name===o)?.value||"",r.title=`Using ${pe.bold(o)}`}},{enabled:t=>t.projectType!=="workspace"&&t.projectType!=="",task:async(t,r)=>{try{u(),r.title="Workspace detected"}catch{let n=["Not in a workspace directory","",`Current directory: ${process.cwd()}`,"","This command must be run from within a workspace created with pf new workspace","","To create a new workspace, run:",""," pf new workspace my-platform"].join(`
468
468
  `);throw new Error(n)}}},{enabled:t=>t.projectType==="workspace",task:async(t,r)=>{try{let n=u();if(!await y(r).toggle({message:`\u26A0\uFE0F You are inside an existing workspace (${n}). Create a nested workspace anyway?`,initial:!1}))throw new Error("Workspace creation cancelled - already inside a workspace");r.title=pe.yellow("Warning: Creating nested workspace")}catch(n){if(Ie(n)&&n.message?.includes("cancelled"))throw n;r.title="No existing workspace detected"}}},{enabled:t=>!!t.projectType,task:async(t,r)=>{let o={"hono-micro":"services/my-hono-service","nest-micro":"services/my-nest-service",workspace:"my-platform"}[t.projectType]||"my-project",s=t.projectType==="workspace"?"\u{1F4E6} Workspace name:":"\u{1F4E6} Service path (e.g., services/my-service):",a=await y(r).input({message:s,initial:o});t.servicePath=a.trim(),r.title=`Path: ${pe.dim(a)}`}},{enabled:t=>{if(!t.servicePath)return!1;let r=_(t.servicePath);return!De(r)},task:async(t,r)=>{if(!await y(r).toggle({message:pe.bold("\u26A0\uFE0F Overwrite existing directory?"),initial:!1}))throw new Error("Project creation was canceled by the user.");t.shouldRemoveDir=!0,r.title=pe.yellow("Will overwrite existing directory")}},{title:"Confirm creation",enabled:t=>t.projectType!=="workspace",task:async(t,r)=>{if(!await y(r).toggle({message:"\u{1F680} Ready to launch your project?",initial:!0}))throw new Error("Project creation was canceled by the user.")}}],actions:[],onComplete:async t=>{yt.find(s=>s.value===t.projectType)||e.error(pe.red(`Project type ${t.projectType} is not supported.`));let r=e.commands.find(s=>s.name()===t.projectType),n={};t.projectType!=="workspace"&&(n.yes=!0);let o=[];t.servicePath&&o.push("servicePath"),t.shouldRemoveDir&&(n.removeDir=!0),await Ao(r,t,n,o)}});for(let t of yt)e.addCommand(t.command);return e};import{Command as Cl}from"commander";h();import{createHash as pl}from"crypto";import{existsSync as V,readdirSync as rs,readFileSync as ns,unlinkSync as ll,writeFileSync as ml}from"fs";import{join as F}from"path";import dl from"chokidar";v();h();var ul=e=>{let t=F(e,"infra","services");if(!V(t))return[];let r=rs(t).filter(a=>a.endsWith(".ts")&&a!=="index.ts").map(a=>a.replace(".ts","")),n=I(e),o=a=>{let c=F(e,a);return V(c)?rs(c).filter(p=>V(F(c,p,"package.json"))):[]},s=[...o(n.services),...o(n.apps)],i=r.filter(a=>!s.includes(a));for(let a of i)ll(F(t,`${a}.ts`));return i},os=".install-hash",ss=e=>{let t=ns(e);return pl("sha256").update(t).digest("hex")},gl=e=>{let t=F(e,"node_modules",os),r=F(e,"bun.lock");return!V(r)||!V(t)?!0:ns(t,"utf-8")!==ss(r)},fl=e=>{let t=F(e,"bun.lock"),r=F(e,"node_modules",os);V(t)&&ml(r,ss(t),"utf-8")},yr=async e=>{let t=ul(e);t.length>0&&console.log(`\u{1F9F9} Cleaned ${t.length} orphaned infra config(s): ${t.join(", ")}`),gl(e)&&(await j({cwd:e,quiet:!0}),fl(e)),await ye("generate-env",{cwd:e})},vr=async e=>{await xo()&&await pr(e)},is=e=>{let{eventsPath:t}=x(e);if(!V(t))return async()=>{};let r=z(async()=>{console.log(`
469
469
  \u{1F4E6} Contracts changed, updating streams...`),await pr(e)},500),n=dl.watch(`${t}/**/*.ts`,{ignoreInitial:!0,ignored:["**/index.ts","**/*.test.ts","**/*.spec.ts"]});return n.on("add",r).on("change",r),async()=>{await n.close()}};Oe();import cs from"chalk";v();le();var hl=["SIGINT","SIGTERM"],yl=[k.SUCCESS,k.GENERAL_ERROR,k.CANCELLED],vl=({code:e,signal:t})=>t?hl.includes(t)?k.SUCCESS:k.GENERAL_ERROR:e===null||yl.includes(e)?k.SUCCESS:e,as=e=>!e.isCurrentProcess||e.isRestarting||e.isShuttingDown?{action:"continue"}:{action:"shutdown",exitCode:vl(e)};var kr=e=>new Promise(t=>setTimeout(t,e)),ps=e=>process.stdout.write(`${e}
470
470
  `),kl=()=>{process.stdout.write("\x1B[2J\x1B[3J\x1B[H"),process.stdout.isTTY&&process.stdout.write("\x1Bc")},ls=()=>({turboProcess:null,isRestarting:!1,isShuttingDown:!1}),ms=async(e,t,r,n)=>{let{dev:o}=fe(t),s=r.length>0?[...r]:o.filter,i=on(nn(".env.local",t),s,process.env.PORT);i.length>0&&(await pn(i),await kr(50));let a=s.flatMap(p=>["--filter",p]);ps(cs.cyan("\u{1F525} Starting services...")),ps(cs.dim(` Command: turbo run ${Pt} --continue${a.length?` ${a.join(" ")}`:""}`)),e.isShuttingDown=!1,e.isRestarting=!1;let c=sn("turbo",["run",Pt,"--continue","--no-daemon",...a],{cwd:t,envFile:".env.local",onExit:(p,l)=>{let d=as({code:p,signal:l,isRestarting:e.isRestarting,isShuttingDown:e.isShuttingDown,isCurrentProcess:e.turboProcess===c});d.action==="shutdown"&&n(d.exitCode)}});e.turboProcess=c},vt=async e=>{let t=e.turboProcess?.pid;if(t){e.isShuttingDown=!0;try{try{process.kill(-t,"SIGTERM")}catch{e.turboProcess?.kill("SIGTERM")}await kr(200);try{process.kill(-t,"SIGKILL")}catch{}await kr(30)}catch{}e.turboProcess=null,kl()}};var ds=new Cl("dev").description("Start development mode with auto-reload").option("-f, --filter <packages...>","Only start these workspace packages (e.g. @orderboss/website). Overrides pf.dev.filter, and narrows the ports freed on start so a second worktree does not kill the first one.").action(async e=>{let t=e.filter??[],r;try{r=u()}catch(d){A(d);return}let n=ls(),o,s,i=async(d=k.SUCCESS)=>{n.isShuttingDown||(console.log(`
@@ -19,9 +19,11 @@ inputs:
19
19
  description: Restore keys for the cache step.
20
20
  default: ''
21
21
  cache-paths:
22
- description: Newline-delimited paths to include in the cache.
22
+ description: >-
23
+ Newline-delimited paths to include in the cache. Never ~/.bun as a whole: it holds the Bun
24
+ binary, and restoring it after setup-bun swaps the pinned Bun for the cached one.
23
25
  default: |
24
- ~/.bun
26
+ ~/.bun/install/cache
25
27
  node_modules
26
28
  bun-version-file:
27
29
  description: Path to a file containing the Bun version (passed to setup-bun).
@@ -35,6 +37,7 @@ runs:
35
37
  using: composite
36
38
  steps:
37
39
  - name: Install Bun
40
+ id: setup-bun
38
41
  uses: oven-sh/setup-bun@v2
39
42
  with:
40
43
  bun-version-file: $\{{ inputs.bun-version-file }}
@@ -47,6 +50,19 @@ runs:
47
50
  key: $\{{ inputs.cache-key }}
48
51
  restore-keys: $\{{ inputs.cache-restore-keys }}
49
52
 
53
+ # A Bun other than the pinned one writes a lockfile the pinned Bun in the Docker image refuses
54
+ # as changed, so a drifted binary fails here instead of in the image build.
55
+ - name: Verify Bun version
56
+ shell: bash
57
+ env:
58
+ EXPECTED_REVISION: $\{{ steps.setup-bun.outputs.bun-revision }}
59
+ run: |
60
+ ACTUAL_REVISION=$(bun --revision)
61
+ if [ "$ACTUAL_REVISION" != "$EXPECTED_REVISION" ]; then
62
+ echo "::error::bun on PATH is $ACTUAL_REVISION, setup-bun installed $EXPECTED_REVISION"
63
+ exit 1
64
+ fi
65
+
50
66
  - name: Install dependencies
51
67
  shell: bash
52
68
  env:
package/dist/facade.js CHANGED
@@ -1331,7 +1331,7 @@ var renderTemplate2 = (templateName, data = {}) => createTemplateRenderer(getTem
1331
1331
  var generateHonoTsConfig = () => {
1332
1332
  return renderTemplate2("tsconfig.json.hbs");
1333
1333
  };
1334
- var generateHonoDockerfile = (bunVersion = "1.2.23") => {
1334
+ var generateHonoDockerfile = (bunVersion = "1.4.4") => {
1335
1335
  return renderTemplate2("Dockerfile.hbs", { bunVersion });
1336
1336
  };
1337
1337
  var generateHonoBiomeJson = () => {
@@ -1539,7 +1539,7 @@ var generateEventsModule = () => {
1539
1539
  var generateEventsService = (serviceName) => {
1540
1540
  return renderTemplate3("src/events/events.service.ts.hbs", { serviceName });
1541
1541
  };
1542
- var generateNestDockerfile = (nodeVersion = "24", bunVersion = "1.2.23") => {
1542
+ var generateNestDockerfile = (nodeVersion = "24.22.1", bunVersion = "1.4.4") => {
1543
1543
  return renderTemplate3("Dockerfile.hbs", { nodeVersion, bunVersion });
1544
1544
  };
1545
1545
  var generateNestBiomeJson = () => {
package/dist/facade.mjs CHANGED
@@ -1261,7 +1261,7 @@ var renderTemplate2 = (templateName, data = {}) => createTemplateRenderer(getTem
1261
1261
  var generateHonoTsConfig = () => {
1262
1262
  return renderTemplate2("tsconfig.json.hbs");
1263
1263
  };
1264
- var generateHonoDockerfile = (bunVersion = "1.2.23") => {
1264
+ var generateHonoDockerfile = (bunVersion = "1.4.4") => {
1265
1265
  return renderTemplate2("Dockerfile.hbs", { bunVersion });
1266
1266
  };
1267
1267
  var generateHonoBiomeJson = () => {
@@ -1468,7 +1468,7 @@ var generateEventsModule = () => {
1468
1468
  var generateEventsService = (serviceName) => {
1469
1469
  return renderTemplate3("src/events/events.service.ts.hbs", { serviceName });
1470
1470
  };
1471
- var generateNestDockerfile = (nodeVersion = "24", bunVersion = "1.2.23") => {
1471
+ var generateNestDockerfile = (nodeVersion = "24.22.1", bunVersion = "1.4.4") => {
1472
1472
  return renderTemplate3("Dockerfile.hbs", { nodeVersion, bunVersion });
1473
1473
  };
1474
1474
  var generateNestBiomeJson = () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crossdelta/platform-sdk",
3
- "version": "0.26.8",
3
+ "version": "0.26.10",
4
4
  "description": "Platform toolkit for event-driven microservices — keeping code and infrastructure in lockstep.",
5
5
  "keywords": [
6
6
  "cli",