@crossdelta/platform-sdk 0.3.24 → 0.3.26

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/README.md CHANGED
@@ -119,14 +119,14 @@ pf new hono-micro services/payments --ai \
119
119
 
120
120
  ```bash
121
121
  cd my-platform
122
- pf dev
122
+ bun dev
123
123
  ```
124
124
 
125
125
  This will:
126
126
  - 🔄 Auto-generate `.env.local` from infrastructure config
127
- - 🚀 Start all services in watch mode
127
+ - 🚀 Start NATS + all services in watch mode (via Turbo)
128
128
  - 🔌 Auto-assign unique ports per service
129
- - 📦 Install dependencies when `package.json` changes
129
+ - 📦 Monitor for file changes and hot-reload
130
130
 
131
131
  <br />
132
132
 
package/bin/cli.js CHANGED
@@ -241,7 +241,7 @@ ${Xe.default.bold(`cd ${r} && ${i} run dev`)}`).breakLine().log("Happy shipping
241
241
  \u274C Error: ${a.message}
242
242
  `)),process.exit(1)}}var L1=pr({name:"nest-micro",description:"Create a new NestJS microservice with the Crossdelta setup. Use --ai for AI-powered generation.",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"],["--ai","Use AI to generate service code based on a description"],["-d, --description <description>","Service description for AI generation (requires --ai)"]],exampleUsage:'platform new nest-micro my-service --ai -d "User authentication service"',buildContext:async(r,e)=>{if(e.ai)throw await jC(r.name,e),new Error("AI generation should have exited");let t=(r.name??"services/my-nest-service").trim(),s=de(t),i=e.packageManager??(e.yes&&Fe()),n=e.packageManager==="bun",o=gt(),a=e.yes?mi():[],c=!!r.name;return{cwd:s,projectName:t,shouldInstallBun:n,packageManager:i,availableIntegrations:o,selectedIntegrations:a,serviceType:"nest",elapsedTime:0,userConfirmed:!!e.yes||c,removeDir:!!e.yes}},prompts:[Ei,Ci,vi,ki,qa,yi],actions:[Si,Di,M1,Ua,j1,Ha,b1,Wa,Va],onComplete:({projectName:r,packageManager:e,selectedIntegrations:t,assignedPort:s})=>{O.breakLine().log(Qe.default.bold.green("NestJS Microservice created successfully!")),Pa(t),Ia((0,N1.basename)(r),s??3e3),O.breakLine().log(`To start the project, run:
243
243
  ${Qe.default.bold(`cd ${r} && ${e} run start:dev`)}`).breakLine().log("Happy coding! \u{1F389}").breakLine()}});var _t=_(require("chalk"));var Pe=require("node:fs"),Or=require("node:path"),W1=_(require("handlebars")),NC=()=>{let r=[(0,Or.join)(__dirname,"templates","workspace"),(0,Or.join)(__dirname,"..","workspace","templates")];for(let e of r)if((0,Pe.existsSync)(e))return e;throw new Error(`Workspace templates directory not found. Searched in: ${r.join(", ")}`)},LC=(r,e)=>{let t=(0,Pe.readFileSync)(r,"utf-8");return W1.default.compile(t)(e)},U1=r=>{r.endsWith(".sh")&&(0,Pe.chmodSync)(r,493)},UC=[{template:"package.json.hbs",output:"package.json"},{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:"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:"infra/services/nats.ts.hbs",output:"infra/services/nats.ts"},{template:"services/nats/package.json.hbs",output:"services/nats/package.json"},{template:"services/nats/nats.conf",output:"services/nats/nats.conf",isStatic:!0},{template:"services/nats/nats.prod.conf",output:"services/nats/nats.prod.conf",isStatic:!0},{template:"services/nats/README.md",output:"services/nats/README.md",isStatic:!0},{template:"services/nats/scripts/start-dev.sh.hbs",output:"services/nats/scripts/start-dev.sh"},{template:"apps/.gitkeep",output:"apps/.gitkeep",isStatic:!0},{template:"packages/.gitkeep",output:"packages/.gitkeep",isStatic:!0},{template:"docs/.gitkeep",output:"docs/.gitkeep",isStatic:!0},{template:".github/copilot-instructions.md.hbs",output:".github/copilot-instructions.md"},{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/check-path-changes/action.yml",output:".github/actions/check-path-changes/action.yml",isStatic:!0,isGitHubCI:!0},{template:".github/actions/check-path-changes/index.js",output:".github/actions/check-path-changes/index.js",isStatic:!0,isGitHubCI:!0},{template:".github/actions/detect-skipped-services/action.yml",output:".github/actions/detect-skipped-services/action.yml",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:".github/actions/resolve-scope-tags/action.yml",output:".github/actions/resolve-scope-tags/action.yml",isStatic:!0,isGitHubCI:!0},{template:".github/actions/resolve-scope-tags/index.js",output:".github/actions/resolve-scope-tags/index.js",isStatic:!0,isGitHubCI:!0}],q1={title:"Creating workspace structure",task:async(r,e)=>{if((0,Pe.existsSync)(r.cwd)){e.title="Workspace directory already exists, skipping...";return}let t=NC(),s=r.includeGitHubCI??!0,i={projectName:r.projectName,githubOwner:r.githubOwner||"",pulumiStackBase:r.pulumiStackBase||r.projectName},n=UC.filter(o=>!(o.isGitHubCI&&!s));for(let o of n){let a=(0,Or.join)(r.cwd,o.output),c=(0,Or.dirname)(a);if((0,Pe.existsSync)(c)||(0,Pe.mkdirSync)(c,{recursive:!0}),o.isStatic){let l=(0,Or.join)(t,o.template);(0,Pe.existsSync)(l)?(0,Pe.writeFileSync)(a,(0,Pe.readFileSync)(l)):(0,Pe.writeFileSync)(a,""),U1(a)}else{let l=(0,Or.join)(t,o.template),u=LC(l,i);(0,Pe.writeFileSync)(a,u),U1(a)}}e.title="Workspace structure created"}};var H1=pr({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"]],exampleUsage:"pf new workspace my-platform -o my-org",buildContext:(r,e)=>{let t=(r.name??"my-platform").trim(),s=de(t),i=e.packageManager??(e.yes?Fe():void 0),n=e.packageManager==="bun",o=!!e.yes,a=e.githubOwner??(o?"my-org":void 0),c=e.pulumiStack??(o?`${a}/${t}`:void 0),l=e.github!==void 0?e.github:o?!0:void 0,u=!!r.name;return{cwd:s,projectName:t,shouldInstallBun:n,packageManager:i,elapsedTime:0,userConfirmed:o||u,removeDir:o,githubOwner:a,pulumiStackBase:c,includeGitHubCI:l}},prompts:[Ei,Ci,w1,D1,v1,vi,ki,yi],actions:[Si,Di,q1,Ha,g1],onComplete:({projectName:r,includeGitHubCI:e})=>{O.breakLine().log(_t.default.bold.green("Workspace created successfully!")),O.breakLine().log(_t.default.bold("Next steps:")).log(` 1. ${_t.default.cyan(`cd ${r}`)}`).log(` 2. ${_t.default.cyan("bun pulumi login")}`).log(` 3. ${_t.default.cyan("bun pulumi up --stack dev")}`),e&&O.breakLine().log(_t.default.bold("GitHub Setup:")).log(" Configure these secrets in your GitHub repository:").log(` \u2022 ${_t.default.yellow("PULUMI_ACCESS_TOKEN")} - Pulumi Cloud access token`).log(` \u2022 ${_t.default.yellow("DIGITALOCEAN_TOKEN")} - DigitalOcean API token`).log(` \u2022 ${_t.default.yellow("NPM_TOKEN")} - (optional) NPM token for private packages`),O.breakLine().log(_t.default.bold("Structure created:")).log(" apps/ # Frontend applications").log(" services/ # Backend microservices").log(" packages/ # Shared libraries").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"),e&&O.log(" .github/").log(" \u251C\u2500\u2500 workflows/ # CI/CD workflows").log(" \u2514\u2500\u2500 actions/ # Custom actions"),O.breakLine().log("Happy coding! \u{1F389}").breakLine()}});var $n=[{name:"Hono Microservice",value:"hono-micro",command:T1,supportsAi:!0},{name:"Nest Microservice",value:"nest-micro",command:L1,supportsAi:!0},{name:"Workspace (Infrastructure)",value:"workspace",command:H1,supportsAi:!1}],V1=()=>{let r=pr({name:"new",description:"Create a new project",arguments:[["[type]","Type of the project (e.g., nest-micro, shopware-plugin)"]],exampleUsage:"platform new nest-micro test-project",buildContext:e=>({projectType:e.type??"",useAi:!1,aiDescription:"",elapsedTime:0,cwd:process.cwd()}),prompts:[{enabled:e=>!e.projectType,task:async(e,t)=>{let s=await t.prompt(oe).run({type:"select",message:"Select Project type",choices:$n});e.projectType=$n.find(i=>i.name===s)?.value||"",t.title=`Using ${Br.default.bold(s)}`}},{enabled:e=>!!e.projectType,task:async(e,t)=>{let i={"hono-micro":"services/my-hono-service","nest-micro":"services/my-nest-service",workspace:"my-platform"}[e.projectType]||"my-project",n=e.projectType==="workspace"?"\u{1F4E6} Workspace name:":"\u{1F4E6} Service path (e.g., services/my-service):",o=await t.prompt(oe).run({type:"input",message:n,initial:i});e.servicePath=o.trim(),t.title=`Path: ${Br.default.dim(o)}`}},{enabled:e=>!!$n.find(s=>s.value===e.projectType)?.supportsAi&&ai().valid,task:async(e,t)=>{let s=await t.prompt(oe).run({type:"toggle",message:"Use AI to generate service code?",enabled:"Yes",disabled:"No",initial:!1});e.useAi=s,t.title=s?Br.default.cyan("\u{1F916} AI-powered generation"):"Standard scaffolding"}},{enabled:e=>e.useAi,task:async(e,t)=>{let s=await t.prompt(oe).run({type:"input",message:"Describe what this service should do:"});e.aiDescription=s,t.title=`Description: ${Br.default.dim(s.slice(0,50))}${s.length>50?"...":""}`}},{enabled:e=>{if(!e.servicePath)return!1;let t=de(e.servicePath);return!ma(t)},task:async(e,t)=>{if(!await t.prompt(oe).run({type:"toggle",message:Br.default.bold("\u26A0\uFE0F Overwrite existing directory?"),enabled:"Yes",disabled:"No",initial:!1}))throw new Error("Project creation was canceled by the user.");e.shouldRemoveDir=!0,t.title=Br.default.yellow("Will overwrite existing directory")}}],actions:[],onComplete:async e=>{if($n.find(n=>n.value===e.projectType)||r.error(Br.default.red(`Project type ${e.projectType} is not supported.`)),e.shouldRemoveDir&&e.servicePath){let n=de(e.servicePath);ga(n)}let t=r.commands.find(n=>n.name()===e.projectType),s={},i=[];e.servicePath&&i.push("servicePath"),e.useAi&&(s.ai=!0,s.yes=!0,s.description=e.aiDescription),await f1(t,e,s,i)}});for(let e of $n)r.addCommand(e.command);return r};var Vt=require("node:fs"),Xa=require("node:path"),K1=_(require("chalk"));var Z1=300,WC=1e3,G1=["infra/services"],z1=["services","apps"],jn=null,Nn=!1,Pr=r=>{process.stdout.write(`${r}
244
- `)},qC=r=>new Promise(e=>setTimeout(e,r)),J1=(r,e)=>{let t=null;return(...s)=>{t&&clearTimeout(t),t=setTimeout(()=>r(...s),e)}},HC=()=>{process.stdout.write("\x1B[2J\x1B[0f")},VC=r=>{let e=(0,Xa.join)(r,"package.json");try{let t=(0,Vt.readFileSync)(e,"utf-8"),s=JSON.parse(t);return{watchDirs:s.pf?.dev?.watchDirs??G1,watchPackages:s.pf?.dev?.watchPackages??z1}}catch{return{watchDirs:G1,watchPackages:z1}}},Y1=async()=>{if(jn?.pid){try{process.kill(-jn.pid,"SIGTERM")}catch{try{jn.kill("SIGTERM")}catch{}}jn=null,await qC(WC)}},X1=r=>{jn=Og("turbo",["start:dev","--continue"],{cwd:r,envFile:".env.local",onStderr:e=>{Nn||process.stderr.write(e)},onExit:e=>{Nn||(Pr(`
244
+ `)},qC=r=>new Promise(e=>setTimeout(e,r)),J1=(r,e)=>{let t=null;return(...s)=>{t&&clearTimeout(t),t=setTimeout(()=>r(...s),e)}},HC=()=>{process.stdout.write("\x1B[2J\x1B[0f")},VC=r=>{let e=(0,Xa.join)(r,"package.json");try{let t=(0,Vt.readFileSync)(e,"utf-8"),s=JSON.parse(t);return{watchDirs:s.pf?.dev?.watchDirs??G1,watchPackages:s.pf?.dev?.watchPackages??z1}}catch{return{watchDirs:G1,watchPackages:z1}}},Y1=async()=>{if(jn?.pid){try{process.kill(-jn.pid,"SIGTERM")}catch{try{jn.kill("SIGTERM")}catch{}}jn=null,await qC(WC)}},X1=r=>{jn=Og("turbo",["run","start:dev","--continue"],{cwd:r,envFile:".env.local",onStderr:e=>{Nn||process.stderr.write(e)},onExit:e=>{Nn||(Pr(`
245
245
  Turbo exited with code ${e}`),process.exit(e??0))}})},Q1=async r=>{Pr("\u{1F4DD} Generating .env.local..."),await Je("bun",["run","generate-env"],{cwd:r,quiet:!0})},GC=async r=>{Pr("\u{1F4E6} Installing dependencies..."),await Je("bun",["install"],{cwd:r,quiet:!0})},zC=r=>{for(let e of Object.keys(require.cache))r.some(t=>e.includes(t))&&delete require.cache[e]},eb=async(r,e,t,s=!1)=>{if(!Nn){Nn=!0;try{zC(t),await Y1(),HC(),Pr(`\u{1F504} Detected change in ${K1.default.cyan(r.replace(/ /g,""))}, restarting...`),s&&await GC(e),await Q1(e),X1(e)}catch(i){Pr(`\u274C Failed to restart: ${i}`)}finally{Nn=!1}}},KC=(r,e)=>{for(let t of r){let s=(0,Xa.join)(e,t),i=J1(n=>eb(n,e,r),Z1);(0,Vt.watch)(s,{recursive:!0},(n,o)=>{o?.endsWith(".ts")&&i(o)})}},ZC=(r,e,t)=>{for(let s of r){let i=(0,Xa.join)(e,s);if(!(0,Vt.existsSync)(i))continue;let n=new Set((0,Vt.readdirSync)(i,{withFileTypes:!0}).filter(a=>a.isDirectory()).map(a=>a.name)),o=J1(async()=>{let a=new Set((0,Vt.readdirSync)(i,{withFileTypes:!0}).filter(u=>u.isDirectory()).map(u=>u.name)),c=[...a].filter(u=>!n.has(u)),l=[...n].filter(u=>!a.has(u));if(c.length>0||l.length>0){let u=[...c.map(h=>`+${h}`),...l.map(h=>`-${h}`)].join(", ");n=a,await eb(`${s}/ [${u}]`,e,t,!0)}},Z1);(0,Vt.watch)(i,{recursive:!1},()=>o())}},JC=()=>{let r=async()=>{Pr(`
246
246
  \u{1F44B} Shutting down...`),await Y1(),process.exit(0)};process.on("SIGINT",r),process.on("SIGTERM",r)},tb=new Ee("dev").description("Start development environment with hot reload").option("--no-watch","Disable watching for changes").action(async r=>{let e=En(),t=VC(e);if(await Q1(e),X1(e),!r.watch){Pr(`
247
247
  \u2590\u2590 Watch mode disabled`);return}let s=t.watchDirs??[],i=t.watchPackages??[];Pr(`
@@ -3,7 +3,7 @@
3
3
  # See: https://docs.nats.io/running-a-nats-service/configuration
4
4
 
5
5
  # Server identification
6
- server_name: {{workspaceName}}-nats
6
+ server_name: nats
7
7
 
8
8
  # Client connections
9
9
  listen: 0.0.0.0:4222
@@ -3,7 +3,7 @@
3
3
  # See: https://docs.nats.io/running-a-nats-service/configuration
4
4
 
5
5
  # Server identification
6
- server_name: {{workspaceName}}-nats
6
+ server_name: nats
7
7
 
8
8
  # Client connections
9
9
  listen: 0.0.0.0:4222
@@ -41,7 +41,11 @@ mkdir -p "$NATS_DATA_DIR"
41
41
  # Config file: services/nats/nats.conf (same as production)
42
42
  echo "[NATS] Starting with JetStream on ports ${NATS_PORT} (client) and ${NATS_HTTP_PORT} (http)..."
43
43
  echo "[NATS] Using config: $NATS_CONF"
44
- docker run -d \
44
+ echo "[NATS] Data directory: $NATS_DATA_DIR"
45
+ echo ""
46
+
47
+ # Start container in foreground (logs streaming) so Turbo keeps watching
48
+ docker run --rm \
45
49
  --name "$CONTAINER_NAME" \
46
50
  -p "${NATS_PORT}:4222" \
47
51
  -p "${NATS_HTTP_PORT}:8222" \
@@ -49,8 +53,3 @@ docker run -d \
49
53
  -v "${NATS_CONF}:/etc/nats/nats.conf:ro" \
50
54
  "$NATS_IMAGE" \
51
55
  -c /etc/nats/nats.conf
52
-
53
- echo "[NATS] Started successfully with JetStream"
54
- echo "[NATS] Data directory: $NATS_DATA_DIR"
55
- echo "[NATS] Health check: curl http://localhost:${NATS_HTTP_PORT}/healthz"
56
- echo "[NATS] JetStream info: curl http://localhost:${NATS_HTTP_PORT}/jsz"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crossdelta/platform-sdk",
3
- "version": "0.3.24",
3
+ "version": "0.3.26",
4
4
  "description": "CLI toolkit for scaffolding Turborepo workspaces with Pulumi infrastructure and Hono/NestJS microservices",
5
5
  "keywords": [
6
6
  "cli",