@flisk-sh/flisk 1.4.2 → 1.4.5
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 +4 -0
- package/README.md +119 -43
- package/cli/dist/backend-languages/bun.js +5 -0
- package/cli/dist/backend-languages/go.js +3 -1
- package/cli/dist/cli.js +9 -0
- package/cli/dist/commands/completion.js +8 -1
- package/cli/dist/commands/config.js +130 -55
- package/cli/dist/commands/doctor.js +16 -15
- package/cli/dist/commands/logs.js +1 -0
- package/cli/dist/commands/project.js +1 -0
- package/cli/dist/commands/resource.js +39 -11
- package/cli/dist/commands/status.js +18 -9
- package/cli/dist/commands/up.js +102 -17
- package/cli/dist/frontend-frameworks/next.js +197 -0
- package/cli/dist/frontend-frameworks/react.js +81 -12
- package/cli/dist/frontend-frameworks/registry.js +5 -4
- package/cli/dist/frontend-frameworks/vue.js +82 -12
- package/cli/dist/generator/resource-templates.js +2 -4
- package/cli/dist/generator/template-engine.js +5 -3
- package/cli/dist/utils/cold-preflight.js +3 -2
- package/cli/dist/utils/config-explain.js +197 -0
- package/cli/dist/utils/demo-reset.js +22 -0
- package/cli/dist/utils/discovery-paths.js +7 -1
- package/cli/dist/utils/host-port-plan.js +8 -3
- package/cli/dist/utils/port-conflict-fix.js +16 -14
- package/cli/dist/utils/port-occupant.js +35 -0
- package/cli/dist/utils/project-features.js +7 -16
- package/cli/dist/utils/project-templates.js +8 -8
- package/cli/dist/utils/resource-features.js +5 -5
- package/cli/dist/utils/service-manifest.js +1 -0
- package/cli/dist/utils/service-urls.js +11 -0
- package/cli/dist/utils/startup-report.js +4 -3
- package/cli/dist/utils/tar.js +33 -28
- package/cli/dist/utils/tilt-process.js +4 -3
- package/cli/dist/utils/windows-doctor.js +18 -15
- package/cli/dist-bundle/{chunk-IGN5UC3P.js → chunk-4J2FLFUA.js} +14 -7
- package/cli/dist-bundle/{chunk-ETKUVKJ2.js → chunk-BMHPPX2Q.js} +10 -10
- package/cli/dist-bundle/chunk-BP2M4OJ4.js +2 -0
- package/cli/dist-bundle/{chunk-53L2JN7G.js → chunk-CGTWXWBU.js} +1 -1
- package/cli/dist-bundle/chunk-GPUJ55MI.js +3 -0
- package/cli/dist-bundle/{chunk-5QBYM33Q.js → chunk-LUUJ4JB7.js} +1 -1
- package/cli/dist-bundle/chunk-PEZQEZVV.js +17 -0
- package/cli/dist-bundle/{chunk-MWOVYIBB.js → chunk-SFOFTVYM.js} +5 -5
- package/cli/dist-bundle/{chunk-3XODDLRJ.js → chunk-TIYTGN5R.js} +1 -1
- package/cli/dist-bundle/{chunk-YNCBBJ4V.js → chunk-U42XZWXB.js} +1 -1
- package/cli/dist-bundle/chunk-ULMQVD7J.js +122 -0
- package/cli/dist-bundle/chunk-V36PT2HF.js +24 -0
- package/cli/dist-bundle/{chunk-I7TGIK5N.js → chunk-VMVTECCK.js} +1 -1
- package/cli/dist-bundle/{chunk-DU2BN46C.js → chunk-Z5QEE4SW.js} +1 -1
- package/cli/dist-bundle/cli.js +607 -375
- package/cli/dist-bundle/cold-preflight-3BNDOLGS.js +2 -0
- package/cli/dist-bundle/{demo-stage-OHNTP2UJ.js → demo-stage-G4W23BXW.js} +1 -1
- package/cli/dist-bundle/{extension-fetch-VEFSCG4B.js → extension-fetch-KIPYDIRM.js} +1 -1
- package/cli/dist-bundle/{open-XROJ6YEY.js → open-HQFT6WDC.js} +1 -1
- package/cli/dist-bundle/project-JZO2CPZZ.js +2 -0
- package/cli/dist-bundle/{project-templates-EYSQG2EO.js → project-templates-IMJ3OX5I.js} +1 -1
- package/cli/dist-bundle/{services-BIMF6MVU.js → services-GLFKZBBZ.js} +1 -1
- package/cli/dist-bundle/{tilt-port-owner-ZY5M2JC4.js → tilt-port-owner-PJN4MPYE.js} +1 -1
- package/cli/package.json +1 -1
- package/package.json +1 -1
- package/runtime/engine/topologies/platform/docker/compose/compose.star +40 -4
- package/runtime/engine/topologies/platform/docker/dockerfile/language_dockerfile.star +6 -0
- package/runtime/engine/topologies/platform/docker/dockerfile/next_dockerfile.star +83 -0
- package/runtime/engine/topologies/platform/docker/index.star +4 -0
- package/runtime/engine/topologies/platform/docker/layers/prisma/filesystem_setup.star +7 -0
- package/runtime/engine/topologies/platform/docker/networking/traefik.star +85 -46
- package/runtime/engine/topologies/tilt/common/utils_output.star +1 -0
- package/runtime/engine/topologies/tilt/generators/validators.star +1 -1
- package/runtime/engine/topologies/tilt/generators/vite/frontend.star +2 -2
- package/runtime/engine/topologies/tilt/generators/vite/generators.star +3 -0
- package/runtime/engine/topologies/tilt/manifest/constants.star +3 -0
- package/runtime/engine/topologies/tilt/manifest/parser.star +9 -3
- package/runtime/engine/topologies/tilt/resources/orchestrator/apply_compose_resource_registration.star +8 -3
- package/runtime/engine/topologies/tilt/resources/orchestrator/generators/dependencies/resolver.star +50 -24
- package/runtime/engine/topologies/tilt/resources/orchestrator/generators/env.star +11 -2
- package/runtime/engine/topologies/tilt/resources/orchestrator/generators/manifest_resource.star +23 -9
- package/cli/dist-bundle/chunk-43XWL2PB.js +0 -61
- package/cli/dist-bundle/chunk-5F7YP5IR.js +0 -17
- package/cli/dist-bundle/chunk-K6SOCO6V.js +0 -3
- package/cli/dist-bundle/chunk-YHYRGWUH.js +0 -24
- package/cli/dist-bundle/chunk-YL56J2WX.js +0 -2
- package/cli/dist-bundle/cold-preflight-ZOPR7PWJ.js +0 -2
- package/cli/dist-bundle/project-7PTIOHGF.js +0 -2
|
@@ -36,15 +36,16 @@ export function checkWindowsRuntimeTools() {
|
|
|
36
36
|
return {
|
|
37
37
|
name: "Windows Runtime Tools",
|
|
38
38
|
didPass: false,
|
|
39
|
-
message: "Docker is not running
|
|
40
|
-
fix:
|
|
39
|
+
message: "Docker is not running or not installed.",
|
|
40
|
+
fix: '1. Install Docker Desktop: https://www.docker.com/products/docker-desktop/\n2. Start Docker Desktop and wait for "Running" status\n3. Enable WSL2 backend in Settings → General\n4. Enable WSL Integration for Ubuntu in Settings → Resources → WSL Integration\n5. Verify: docker version && docker compose version\nDocs: https://docs.docker.com/desktop/wsl/',
|
|
41
41
|
};
|
|
42
42
|
}
|
|
43
43
|
if (!findOnPath("tilt")) {
|
|
44
44
|
return {
|
|
45
45
|
name: "Windows Runtime Tools",
|
|
46
46
|
didPass: false,
|
|
47
|
-
message: "tilt.exe not found on PATH.
|
|
47
|
+
message: "tilt.exe not found on PATH.",
|
|
48
|
+
fix: "Install Tilt:\n curl -fsSL https://raw.githubusercontent.com/tilt-dev/tilt/master/scripts/install.sh | bash\nOr download from: https://docs.tilt.dev/install.html\nVerify: tilt version",
|
|
48
49
|
};
|
|
49
50
|
}
|
|
50
51
|
const bun = findOnPath("bun");
|
|
@@ -55,6 +56,7 @@ export function checkWindowsRuntimeTools() {
|
|
|
55
56
|
name: "Windows Runtime Tools",
|
|
56
57
|
didPass: false,
|
|
57
58
|
message: "node.exe not found on PATH for this npm installation.",
|
|
59
|
+
fix: "Install Node.js 22.12+: https://nodejs.org/\nOr use Bun (includes Node.js): https://bun.sh/install",
|
|
58
60
|
};
|
|
59
61
|
}
|
|
60
62
|
return {
|
|
@@ -62,7 +64,7 @@ export function checkWindowsRuntimeTools() {
|
|
|
62
64
|
didPass: true,
|
|
63
65
|
message: bun
|
|
64
66
|
? `Docker (${docker}), Tilt, and Bun found on PATH`
|
|
65
|
-
: `Docker (${docker}) and Tilt found on PATH; ⚠️ Bun was not found (generated services need Bun)`,
|
|
67
|
+
: `Docker (${docker}) and Tilt found on PATH; ⚠️ Bun was not found (generated services need Bun)\n Fix: curl -fsSL https://bun.sh/install | bash\n Verify: bun --version`,
|
|
66
68
|
};
|
|
67
69
|
}
|
|
68
70
|
export function checkWindowsDockerMode() {
|
|
@@ -76,8 +78,8 @@ export function checkWindowsDockerMode() {
|
|
|
76
78
|
return {
|
|
77
79
|
name: "Docker container mode",
|
|
78
80
|
didPass: false,
|
|
79
|
-
message: "Docker Desktop is using Windows containers.
|
|
80
|
-
fix:
|
|
81
|
+
message: "Docker Desktop is using Windows containers. Flisk requires Linux containers.",
|
|
82
|
+
fix: '1. Open Docker Desktop → Settings → General\n2. Ensure "Use WSL 2 instead of Hyper-V" is checked\n3. Click "Apply & Restart"\n4. Verify: docker info --format \'{{.OSType}}\' (should print "linux")\nDocs: https://docs.docker.com/desktop/wsl/',
|
|
81
83
|
};
|
|
82
84
|
}
|
|
83
85
|
return {
|
|
@@ -90,7 +92,8 @@ export function checkWindowsDockerMode() {
|
|
|
90
92
|
return {
|
|
91
93
|
name: "Docker container mode",
|
|
92
94
|
didPass: false,
|
|
93
|
-
message: "Docker is not
|
|
95
|
+
message: "Docker daemon is not responding.",
|
|
96
|
+
fix: '1. Start Docker Desktop and wait for "Running" status\n2. Verify: docker version\n3. If stuck: restart Docker Desktop or run `systemctl restart docker` in WSL2\nDocs: https://docs.docker.com/desktop/troubleshoot/',
|
|
94
97
|
};
|
|
95
98
|
}
|
|
96
99
|
}
|
|
@@ -102,12 +105,12 @@ export async function checkWindowsHostConfiguration() {
|
|
|
102
105
|
excludedRanges = parseExcludedPortRanges(output);
|
|
103
106
|
}
|
|
104
107
|
catch {
|
|
105
|
-
warnings.push("Could not inspect Windows Hyper-V excluded TCP port ranges.");
|
|
108
|
+
warnings.push("Could not inspect Windows Hyper-V excluded TCP port ranges.\n Fix: run as Administrator: netsh interface ipv4 show excludedportrange protocol=tcp\n If netsh fails, this check is skipped.");
|
|
106
109
|
}
|
|
107
110
|
const requiredPorts = [80, 443, 5432];
|
|
108
111
|
const excluded = requiredPorts.filter((port) => excludedRanges.some(([start, end]) => port >= start && port <= end));
|
|
109
112
|
if (excluded.length > 0) {
|
|
110
|
-
warnings.push(`⚠️ Windows reserved port${excluded.length > 1 ? "s" : ""} ${excluded.join(", ")} for Hyper-V/WSL
|
|
113
|
+
warnings.push(`⚠️ Windows reserved port${excluded.length > 1 ? "s" : ""} ${excluded.join(", ")} for Hyper-V/WSL.\n Fix 1 (reset reserved ranges): run as Administrator: net stop winnat && net start winnat\n Fix 2 (change Flisk ports): set FLISK_HTTP_PORT, FLISK_HTTPS_PORT, FLISK_POSTGRES_PORT in project config\n Verify: flisk doctor\n Docs: https://github.com/flisk-sh/flisk/blob/main/docs/wsl2.md#troubleshooting`);
|
|
111
114
|
}
|
|
112
115
|
const occupied = [];
|
|
113
116
|
for (const port of requiredPorts) {
|
|
@@ -131,10 +134,10 @@ export async function checkWindowsHostConfiguration() {
|
|
|
131
134
|
"-Command",
|
|
132
135
|
`$ports = '${externallyOccupied.join(",")}'.Split(','); foreach ($port in $ports) { Get-NetTCPConnection -LocalPort ([int]$port) -State Listen -ErrorAction SilentlyContinue | ForEach-Object { $connection = $_; $service = Get-CimInstance Win32_Service | Where-Object { $_.ProcessId -eq $connection.OwningProcess } | Select-Object -First 1; if ([int]$port -eq 80 -and ($service.Name -eq 'W3SVC' -or $connection.OwningProcess -eq 4)) { '80: IIS / World Wide Web Publishing (HTTP.sys)' } elseif ($service) { '{0}: {1}' -f $port, $service.DisplayName } else { '{0}: PID {1}' -f $port, $connection.OwningProcess } } }`,
|
|
133
136
|
], { encoding: "utf-8", timeout: 10_000, windowsHide: true }).trim();
|
|
134
|
-
warnings.push(`⚠️ Ports ${externallyOccupied.join(", ")} are already listening${owners ? `:\n${owners}` : "."}`);
|
|
137
|
+
warnings.push(`⚠️ Ports ${externallyOccupied.join(", ")} are already listening${owners ? `:\n${owners}` : "."}\n Fix: stop the conflicting service or change Flisk ports (FLISK_HTTP_PORT, etc.)\n Verify: flisk doctor\n Docs: https://github.com/flisk-sh/flisk/blob/main/docs/wsl2.md#troubleshooting`);
|
|
135
138
|
}
|
|
136
139
|
catch {
|
|
137
|
-
warnings.push(`⚠️ Ports ${externallyOccupied.join(", ")} are already in use
|
|
140
|
+
warnings.push(`⚠️ Ports ${externallyOccupied.join(", ")} are already in use.\n Fix: netstat -ano | findstr :${externallyOccupied.join(" :")}\n Verify: flisk doctor`);
|
|
138
141
|
}
|
|
139
142
|
}
|
|
140
143
|
let dnsOk = true;
|
|
@@ -149,7 +152,7 @@ export async function checkWindowsHostConfiguration() {
|
|
|
149
152
|
}
|
|
150
153
|
}
|
|
151
154
|
if (!dnsOk) {
|
|
152
|
-
warnings.push(`${WINDOWS_DNS_FAILURE}\
|
|
155
|
+
warnings.push(`${WINDOWS_DNS_FAILURE}\n Fix: add these lines to C:\\Windows\\System32\\drivers\\etc\\hosts (run Notepad as Administrator):\n 127.0.0.1 flisk.localhost\n 127.0.0.1 example.flisk.localhost\n Verify: ping flisk.localhost (should resolve to 127.0.0.1)\n Docs: https://github.com/flisk-sh/flisk/blob/main/docs/wsl2.md#troubleshooting`);
|
|
153
156
|
}
|
|
154
157
|
try {
|
|
155
158
|
const autocrlf = execFileSync("git", ["config", "core.autocrlf"], {
|
|
@@ -158,18 +161,18 @@ export async function checkWindowsHostConfiguration() {
|
|
|
158
161
|
windowsHide: true,
|
|
159
162
|
}).trim();
|
|
160
163
|
if (autocrlf.toLowerCase() === "true") {
|
|
161
|
-
warnings.push(
|
|
164
|
+
warnings.push('git core.autocrlf is true; generated files may get CRLF while Docker Linux containers expect LF.\n Fix: git config core.autocrlf input (run in Flisk repo root)\n Verify: git config core.autocrlf (should show "input")');
|
|
162
165
|
}
|
|
163
166
|
}
|
|
164
167
|
catch {
|
|
165
168
|
// No repository-local setting to report.
|
|
166
169
|
}
|
|
167
|
-
warnings.push("Docker Desktop Settings → Resources → File sharing must include this project directory
|
|
170
|
+
warnings.push("Docker Desktop Settings → Resources → File sharing must include this project directory.\n Fix: Docker Desktop → Settings → Resources → File sharing → add project path\n Verify: flisk doctor");
|
|
168
171
|
return {
|
|
169
172
|
name: "Windows ports and DNS",
|
|
170
173
|
didPass: dnsOk && externallyOccupied.length === 0,
|
|
171
174
|
message: warnings.length > 0
|
|
172
|
-
? warnings.join("\n")
|
|
175
|
+
? warnings.join("\n\n")
|
|
173
176
|
: "Required ports are available and Flisk localhost DNS resolves to loopback",
|
|
174
177
|
...(dnsOk ? {} : { fix: WINDOWS_DNS_FAILURE }),
|
|
175
178
|
};
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import{createRequire as __fliskRequire}from'node:module';const require=__fliskRequire(import.meta.url);
|
|
2
|
-
import{b as N}from"./chunk-
|
|
2
|
+
import{b as N}from"./chunk-TIYTGN5R.js";import{a as m,ha as f,t as T}from"./chunk-PEZQEZVV.js";import{C as I,L as O,e as $,f as C,i as F,j as E,m as U,n as P,x as u}from"./chunk-U42XZWXB.js";import{execFileSync as de,execSync as le}from"node:child_process";import{existsSync as A,readdirSync as V,readFileSync as y}from"node:fs";import{basename as ue,dirname as me,join as l,relative as k}from"node:path";function Oe(t){return M(t,!1)}function Ne(t){return M(t,!0)}function M(t,e){return`import { Hono } from 'hono';
|
|
3
|
+
import { cors } from 'hono/cors';
|
|
3
4
|
${e?`import { sql } from 'drizzle-orm';
|
|
4
5
|
import { db } from './db.js';
|
|
5
6
|
`:""}
|
|
6
7
|
const app = new Hono();
|
|
7
8
|
|
|
9
|
+
// The frontend runs on its own *.localhost origin (app.<project>.localhost), so the browser needs CORS to call this API.
|
|
10
|
+
const LOCAL_FRONTEND_ORIGIN = /^https?:\\/\\/([a-z0-9-]+\\.)+localhost(:\\d+)?$/;
|
|
11
|
+
app.use('*', cors({ origin: (origin) => (LOCAL_FRONTEND_ORIGIN.test(origin) ? origin : undefined) }));
|
|
12
|
+
|
|
8
13
|
// Health check endpoint (required by TILT_RESOURCE_DEFAULTS.star)
|
|
9
14
|
app.get('/health', (c) => {
|
|
10
15
|
return c.json({ status: 'ok', service: '${t}' });
|
|
@@ -52,7 +57,7 @@ export default {
|
|
|
52
57
|
port,
|
|
53
58
|
fetch: app.fetch,
|
|
54
59
|
};
|
|
55
|
-
`}var
|
|
60
|
+
`}var L={id:"bun",label:"Bun + Hono",installHint:"bun install"};var se="go run .",G={id:"go",label:"Go 1.23 (net/http)",devCommand:se,watch:["**/*.go","go.mod","go.sum"],installHint:"go mod tidy # optional: Docker builds the image for you",createFiles(t){return[{filename:"go.mod",content:`module ${t}
|
|
56
61
|
|
|
57
62
|
go 1.23
|
|
58
63
|
`,description:"Generating go.mod",emoji:"\u{1F4E6}"},{filename:"main.go",content:`package main
|
|
@@ -67,7 +72,9 @@ import (
|
|
|
67
72
|
func writeJSON(w http.ResponseWriter, status int, body any) {
|
|
68
73
|
w.Header().Set("Content-Type", "application/json")
|
|
69
74
|
w.WriteHeader(status)
|
|
70
|
-
|
|
75
|
+
if err := json.NewEncoder(w).Encode(body); err != nil {
|
|
76
|
+
log.Printf("failed to encode JSON response: %v", err)
|
|
77
|
+
}
|
|
71
78
|
}
|
|
72
79
|
|
|
73
80
|
func newMux() *http.ServeMux {
|
|
@@ -345,11 +352,11 @@ mod tests {
|
|
|
345
352
|
assert_eq!(body["status"], "ok");
|
|
346
353
|
}
|
|
347
354
|
}
|
|
348
|
-
`,description:"Generating backend source and smoke test",emoji:"\u{1F4BB}"}]}};var J="bun",h={bun:
|
|
355
|
+
`,description:"Generating backend source and smoke test",emoji:"\u{1F4BB}"}]}};var J="bun",h={bun:L,python:q,go:G,rust:H,node:B};function ae(t){let e=(t??J).trim().toLowerCase(),n=Object.hasOwn(h,e)?h[e]:void 0;if(!n){let s=Object.keys(h).join(", ");throw new O(`Unknown backend language "${t}". Supported languages: ${s}.`,[`Use one of: ${s}`,`Omit --language to use ${J}`])}return n}function qe(t,e){if(t!=="backend"){if(e!==void 0)throw new O("--language can only be used with --type backend.",["Add --type backend, or drop --language for this resource type"]);return}return ae(e)}import{exec as ie,execFile as ce}from"node:child_process";import{promisify as W}from"node:util";var pe=W(ie),z=async(t,e)=>{if(process.platform==="win32"){let s=t.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g)??[],[o,...r]=s.map(c=>c.startsWith('"')||c.startsWith("'")?c.slice(1,-1):c);if(!o)throw new Error("Command is empty");let a=I(o)??o,{stdout:i}=await W(ce)(a,r,{encoding:"utf-8",timeout:e,windowsHide:!0,shell:!1});return i}let{stdout:n}=await pe(t,{encoding:"utf-8",timeout:e});return n};function Ke(t){let e=t;return e?.code==="ETIMEDOUT"||e?.killed===!0}var x=1e4,fe=/\.(?:[cm]?[jt]sx?|vue)$/,he=/(?:\.test\.|\.spec\.|\/tests?\/|\/__tests__\/)/,ge=7,ke=new Set(["node_modules",E,"dist","tests","__tests__"]);function Y(t){try{let e=JSON.parse(y(l(t,".flisk",U),"utf-8"));return typeof e?.project?.name=="string"?e.project.name:void 0}catch{return}}function ye(t){try{let e=JSON.parse(y(t,"utf-8")).params;return e&&typeof e=="object"?e:{}}catch{return{}}}function R(t){try{let e=JSON.parse(y(l(t,P),"utf-8"));return e&&typeof e=="object"?e:{}}catch{return{}}}function K(t){if(typeof t!="string")return;let e=t.match(/(?:^|[^0-9])(\d+)(?:\.|$)/);return e?Number(e[1]):void 0}function be(t){let e=S(t);if(e===void 0)return["missing prisma/schema.prisma"];let n=e.replace(/\/\*[\s\S]*?\*\//g,"").replace(/^\s*\/\/.*$/gm,""),s=[],o=n.match(/datasource\s+\w+\s*\{([^}]*)\}/)?.[1]??"";return/provider\s*=\s*["']postgresql["']/.test(o)||s.push("schema provider is not postgresql"),/\b(?:url|directUrl)\s*=/.test(n)&&s.push("schema still declares url/directUrl"),s}function we(t){let e=l(t,E,"prisma.config.autogenerated.ts"),s=[l(t,"prisma.config.ts"),e].find(a=>A(a));if(!s)return["missing prisma.config.ts or generated Prisma config"];let r=(S(s)??"").match(/datasource\s*:\s*\{([^}]*)\}/)?.[1]??"";return/url\s*:\s*(?:env\(["']DATABASE_URL["']\)|process\.env\.DATABASE_URL)\s*,?/.test(r)?[]:['Prisma config datasource.url is not env("DATABASE_URL")']}function it(t=u()??process.cwd(),e=f(t)){let n=e.filter(r=>r.config?.featuresEnabled?.includes("prisma")),s=[],o=e.filter(r=>r.config?.appType==="migrator");for(let r of e){let a=r.config?.features;Array.isArray(a)&&a.includes("prisma")&&!r.config?.featuresEnabled?.includes("prisma")&&s.push(`${r.name}: use featuresEnabled, not features, for Prisma`)}if(n.length===0)return s.length===0?{name:"Prisma 7 consistency",didPass:!0,isSkipped:!0,message:"No resources enable Prisma; Prisma consistency checks were skipped"}:{name:"Prisma 7 consistency",didPass:!1,message:`${m(s.length,"Prisma consistency error")}:
|
|
349
356
|
${s.join(`
|
|
350
|
-
`)}`,fix:"Use featuresEnabled to opt into the validated Prisma 7 shape"};for(let r of n){let a=
|
|
357
|
+
`)}`,fix:"Use featuresEnabled to opt into the validated Prisma 7 shape"};for(let r of n){let a=R(r.path).dependencies,c={...R(r.path).devDependencies??{},...a??{}},d=K(c.prisma),p=K(c["@prisma/client"]);if(d===void 0||p===void 0?s.push(`${r.name}: prisma and @prisma/client are required`):(d!==p||d!==ge)&&s.push(`${r.name}: prisma packages must both be major 7`),s.push(...be(l(r.path,"prisma","schema.prisma")).map(g=>`${r.name}: ${g}`)),s.push(...we(r.path).map(g=>`${r.name}: ${g}`)),(r.config?.appType??r.type)==="migrator")r.config?.dependsOn?.some(w=>w==="postgres"||w===C)||s.push(`${r.name}: migrator must depend on postgres or database-management`);else{r.config?.dependsOn?.some(_=>o.some(v=>v.name===_))||s.push(`${r.name}: API must depend on its Prisma migrator, which depends on postgres or database-management`);let w=R(r.path).scripts;Object.entries(w??{}).some(([_,v])=>ee.has(_)&&typeof v=="string"&&/prisma\s+migrate/.test(v))&&s.push(`${r.name}: API start script must not run prisma migrate`);let te=l(r.path,r.config?.dockerfile??"Dockerfile");j(te,!0)&&s.push(`${r.name}: API image/startup must not run Prisma migrations`)}}return n.length>0&&!N(t,e).willStart&&s.push("shared platform Postgres will not start for the Prisma resources"),s.length===0?{name:"Prisma 7 consistency",didPass:!0,message:"Prisma resources use the validated Prisma 7 shape"}:{name:"Prisma 7 consistency",didPass:!1,message:`${m(s.length,"Prisma consistency error")}:
|
|
351
358
|
${s.join(`
|
|
352
|
-
`)}`,fix:"Use Prisma 7 packages and config, put migrations in a migrator, and make the API depend on that migrator"}}function X(t){let e=new Map;for(let n of t){let s=n.config?.port;T(n.config?.appType)&&typeof s=="number"&&e.set(n.name,s)}return e}function ve(t){let e=t.config?.language;return typeof e=="string"&&Object.hasOwn(h,e)&&h[e].createFiles!==void 0}function $e(t){return(t.config?.appType??t.type)===$}function ct(t=u()??process.cwd()){let e=f(t).filter(n=>!$e(n)&&!ve(n)&&!
|
|
359
|
+
`)}`,fix:"Use Prisma 7 packages and config, put migrations in a migrator, and make the API depend on that migrator"}}function X(t){let e=new Map;for(let n of t){let s=n.config?.port;T(n.config?.appType)&&typeof s=="number"&&e.set(n.name,s)}return e}function ve(t){let e=t.config?.language;return typeof e=="string"&&Object.hasOwn(h,e)&&h[e].createFiles!==void 0}function $e(t){return(t.config?.appType??t.type)===$}function ct(t=u()??process.cwd()){let e=f(t).filter(n=>!$e(n)&&!ve(n)&&!A(l(n.path,P))).map(n=>n.name);return e.length===0?{name:"Resource package.json",didPass:!0,message:"Every resource has a package.json"}:{name:"Resource package.json",didPass:!1,message:`${m(e.length,"resource")} without a package.json: ${e.join(", ")}. The image build stops at "COPY .../package.json: not found"`,fix:"Add a package.json next to each service.json (copy one from a working resource), or delete the folder's service.json if it is not a service"}}function pt(t=u()??process.cwd(),e=f(t)){let n=new Map;for(let r of e){let a=n.get(r.name)??[];a.push(k(t,r.configPath)||r.configPath),n.set(r.name,a)}let s=[...n].filter(([,r])=>r.length>1);if(s.length===0)return{name:"Duplicate resource names",didPass:!0,message:"Every resource has a unique appName"};let o=s.map(([r,a])=>`"${r}" in ${a.join(" and ")}`);return{name:"Duplicate resource names",didPass:!1,message:`${m(s.length,"appName")} used by more than one resource:
|
|
353
360
|
${o.join(`
|
|
354
361
|
`)}`,fix:'Give each resource its own "appName" in its service.json. The name becomes the Compose service and the Traefik router, so two resources with one name collide'}}function Pe(t){let e=t.config?.appType;return T(e)?!0:e===$&&t.config?.exposeViaProxy!==!1}function dt(t=u()??process.cwd()){let e=new Map;for(let o of f(t)){let r=o.config?.port;if(typeof r!="number"||!Pe(o))continue;let a=e.get(r)??[];a.push(o.name),e.set(r,a)}let n=[...e].filter(([,o])=>o.length>1);if(n.length===0)return{name:"Duplicate resource ports",didPass:!0,message:"No two routed resources use the same port"};let s=n.map(([o,r])=>`port ${o}: ${r.join(", ")}`);return{name:"Duplicate resource ports",didPass:!1,isWarning:!0,message:`${m(n.length,"port")} used by more than one routed resource:
|
|
355
362
|
${s.join(`
|
|
@@ -362,5 +369,5 @@ mod tests {
|
|
|
362
369
|
`)}`,fix:`Backends are only reachable through Traefik: http://api.${o}.localhost/api/<name without -api>/... (for example catalog-api is /api/catalog). Use that URL, or the generated .autogenerated/env.ts`}}var Z=/\b(?:prisma\s+(?:migrate\s+(?:deploy|dev|resolve)|db\s+push)|drizzle-kit\s+(?:migrate|push)|knex\s+migrate:\w+|typeorm\s+migration:run|mikro-orm\s+migration:up|sequelize(?:-cli)?\s+db:migrate|dbmate\s+(?:up|migrate)|atlas\s+migrate\s+apply|alembic\s+upgrade)\b/,ee=new Set(["start","dev","serve","prestart","predev","preserve"]),xe=/\.(?:sh|bash)$/,Se=/^\s*(?:CMD|ENTRYPOINT)\b/i;function S(t){try{return y(t,"utf-8")}catch{return}}function _e(t){let e=S(l(t,P));if(e===void 0)return[];let n={};try{n=JSON.parse(e)?.scripts??{}}catch{return[]}return Object.entries(n).filter(([s,o])=>ee.has(s)&&typeof o=="string"&&Z.test(o)).map(([s])=>`package.json "${s}" script`)}function j(t,e){let n=S(t);return n===void 0?!1:n.split(`
|
|
363
370
|
`).some(s=>Z.test(s.replace(/["'`,[\]]+/g," "))&&!Q.test(s)&&(!e||Se.test(s)))}function ft(t=u()??process.cwd()){let e=[];for(let n of f(t)){let s=n.config?.appType??n.type;if(s!=="backend"&&s!=="worker")continue;let o=n.path;for(let i of _e(o))e.push(`${n.name}: ${i}`);let r=l(o,n.config?.dockerfile??"Dockerfile");j(r,!0)&&e.push(`${n.name}: ${k(t,r)} CMD or ENTRYPOINT`);let a=[];try{a=V(o,{withFileTypes:!0})}catch{}for(let i of a){let c=l(o,i.name);i.isFile()&&xe.test(i.name)&&j(c,!1)&&e.push(`${n.name}: ${k(t,c)}`)}for(let i of D(l(o,"src")))j(i,!1)&&e.push(`${n.name}: ${k(t,i)}`)}return e.length===0?{name:"Migrations outside the API",didPass:!0,message:"No backend or worker runs a migration command from its own start-up"}:{name:"Migrations outside the API",didPass:!1,isWarning:!0,message:`${m(e.length,"migration command")} run from a backend or worker's own start-up:
|
|
364
371
|
${e.join(`
|
|
365
|
-
`)}`,fix:"Move the migration into a `migrator` resource (appType migrator) that the service depends on, so it runs once before the service starts instead of on every start and restart"}}function ht(t=u()??process.cwd()){let e=f(t).filter(s=>s.config?.featuresEnabled?.includes("nats")).map(s=>s.name);if(e.length===0)return{name:"NATS broker",didPass:!0,message:"No resource enables the nats feature",isSkipped:!0};if(
|
|
372
|
+
`)}`,fix:"Move the migration into a `migrator` resource (appType migrator) that the service depends on, so it runs once before the service starts instead of on every start and restart"}}function ht(t=u()??process.cwd()){let e=f(t).filter(s=>s.config?.featuresEnabled?.includes("nats")).map(s=>s.name);if(e.length===0)return{name:"NATS broker",didPass:!0,message:"No resource enables the nats feature",isSkipped:!0};if(A(l(t,"services","platform","messaging",F)))return{name:"NATS broker",didPass:!0,message:`${m(e.length,"resource")} enable nats and services/platform/messaging/docker-compose.yml is present`};let n=(Y(t)??"project").replace(/-/g,"_");return{name:"NATS broker",didPass:!1,message:`${e.join(", ")} enable the nats feature, but no NATS server will start: Flisk only starts one when services/platform/messaging/docker-compose.yml exists, and it does not generate that file`,fix:`Create it with a "nats" service (image nats with -js, container_name ${n}_nats, network ${n}_backend, external) and a "redis" service, which Flisk registers alongside it`}}function gt(t=u()??process.cwd()){let e=N(t),n=" (project resource set; flisk up may select a subset and not start Postgres)",s=e.focusWouldEnableDatabaseManagement?" Warning: generated Tiltfile default focus expands CORE_INFRA to database-management while project.json has it off; default flisk up takes the feature path.":"";if(e.unknownDependsOnNames.length>0){let r=e.unknownDependsOnNames.map(({resource:i,name:c})=>`"${c}" (from ${i})`).join(", "),a=e.willStart?` Postgres will start because ${e.reason==="feature"?"database-management is enabled":`resource(s) ${e.dependsOnUsers.join(", ")} depend on postgres/database-management`}${n}`:" Postgres will not start for this project.json/generated Tiltfile state.";return{name:"Shared platform Postgres",didPass:!1,message:`${m(e.unknownDependsOnNames.length,"unknown dependsOn name")}: ${r}. These are not known services or stacks, and are not the shared platform database names (postgres, database-management).${a}${s}`,fix:'Use "postgres" or "database-management" in dependsOn for the shared platform database, or fix the name to match an existing service'}}if(!e.willStart)return{name:"Shared platform Postgres",didPass:!0,isWarning:!!s,isSkipped:!s,message:s?s.trim():"Shared platform Postgres will not start: database-management is off and no resource depends on postgres or database-management."};if(e.reason==="feature"&&e.dependsOnUsers.length===0){let r=e.featureOnFromTiltfile||e.featureOnFromSpecMaster?" (generated Tiltfile/spec.master)":"";return{name:"Shared platform Postgres",didPass:!0,isWarning:!!s,message:`Shared platform Postgres will start because the database-management feature is enabled${r} (project-wide; not a flisk up selection).${s}`}}let o=e.reason==="feature"?`the database-management feature is enabled and resource(s) ${e.dependsOnUsers.join(", ")} depend on postgres/database-management`:`resource(s) ${e.dependsOnUsers.join(", ")} depend on postgres/database-management`;return{name:"Shared platform Postgres",didPass:!0,isWarning:!!s,message:`Shared platform Postgres will start because ${o}${n}${s}`}}function Ee(t){let e=[];for(let n of t.split(`
|
|
366
373
|
`)){let s=n.match(/^\s*(\d+)\s+.*\btilt up\b.*?\s-f\s+(\S+)/i);s&&e.push({pid:Number(s[1]),root:s[2].split(/[\\/]\.flisk[\\/]/i)[0]})}return e}function kt(t=u()??process.cwd(),e=le){let n;try{n=process.platform==="win32"?de("powershell.exe",["-NoProfile","-Command","Get-CimInstance Win32_Process | Where-Object { $_.Name -eq 'tilt.exe' } | ForEach-Object { '{0} {1}' -f $_.ProcessId, $_.CommandLine }"],{encoding:"utf-8",timeout:x,windowsHide:!0}):e("ps -Ao pid=,command=",{encoding:"utf-8",timeout:x})}catch{return{name:"Tilt processes",didPass:!0,isSkipped:!0,message:"Could not list processes"}}let s=Ee(n),o=s.filter(a=>a.root===t),r=s.filter(a=>a.root!==t);return o.length>1?{name:"Tilt processes",didPass:!1,message:`${o.length} Tilt processes are running for this project (pids ${o.map(a=>a.pid).join(", ")}). They fight over ports and containers`,fix:`Stop them all, then start one: flisk down && kill ${o.map(a=>a.pid).join(" ")} && flisk up`}:r.length>0?{name:"Tilt processes",didPass:!0,isSkipped:!0,message:`Another project's Tilt is running: ${r.map(i=>`${i.root} (pid ${i.pid})`).join(", ")}`,fix:"flisk up will use the next free Tilt UI port (10351, ...). If it is a leftover, run flisk down in that project"}:{name:"Tilt processes",didPass:!0,message:"No stray Tilt processes"}}async function yt(t=z){let e=`flisk_doctor_probe_${process.pid}`;try{await t(`docker network create ${e}`,x)}catch(n){let s=String(n.stderr??n);return/address pools/i.test(s)?{name:"Docker networks",didPass:!1,message:"Docker cannot create another network: all predefined address pools are in use. Each Flisk project needs several",fix:"run flisk down --prune-networks in projects you are not using, or docker network prune (removes all unused networks)"}:{name:"Docker networks",didPass:!0,isSkipped:!0,message:"Could not test network creation"}}try{await t(`docker network rm ${e}`,x)}catch{}return{name:"Docker networks",didPass:!0,message:"Docker can create networks"}}export{Oe as a,Ne as b,h as c,ae as d,qe as e,z as f,Ke as g,it as h,ct as i,pt as j,dt as k,lt as l,ut as m,mt as n,ft as o,ht as p,gt as q,Ee as r,kt as s,yt as t};
|
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
import{createRequire as __fliskRequire}from'node:module';const require=__fliskRequire(import.meta.url);
|
|
2
|
-
import{a as
|
|
3
|
-
`).map(l=>l.trim()).filter(Boolean).pop()??"unknown error"}async function Fe(e,o,l,s={}){let
|
|
2
|
+
import{a as O}from"./chunk-BP2M4OJ4.js";import{b as N,c as fe,d as V,p as D,q as Y,v as J,w as L,y as C}from"./chunk-SFOFTVYM.js";import{aa as ie,fa as ae,ga as se,h as T,i as B,ia as ce,ka as le,la as pe,ma as de,o as h,p as b,q as t,x as re,y as G,z as W}from"./chunk-PEZQEZVV.js";import{N as U,P as z,R as E,b as g,f as x,m as M,o as P,x as F}from"./chunk-U42XZWXB.js";import{execFileSync as Se}from"node:child_process";import{cpSync as je,existsSync as S,readdirSync as _e,readFileSync as K,writeFileSync as we}from"node:fs";import{basename as xe,dirname as Pe,isAbsolute as Ee,join as _,resolve as q}from"node:path";import{cwd as he}from"node:process";import{fileURLToPath as Ce}from"node:url";import{Command as Oe}from"commander";var ve="-y, --yes",ye="Skip prompts and confirmations; use defaults";function ue(e,o=ye){return e.option(ve,o,!1)}async function be(e,o=!0){let l=await L({message:e,initial:o});return l||T(),l}function Le(e,o=1){e.valid||E(e.error??"Validation failed",o)}function Ie(e,o,l,s){return e.dryRun?(s?s():(console.log(`Dry run - ${o}`),console.log(`Would run: ${l}`)),!0):!1}async function qe(e,o){return await be(e,!0)?!0:(o&&o(),!1)}var I=new Map;function me(e){e!==void 0?I.delete(e):I.clear()}function Be(e=!1){let o=F(),l=o??"";if(!e){let d=I.get(l);if(d)return d}let{resources:s,problems:p}=o?ae(o):{resources:ce(),problems:[]},u=de(s),n=pe(s);se(p);let r=s.filter(d=>!d.stack),f=new Map;for(let d of s)if(d.stack){let a=f.get(d.stack)||[];a.push(d),f.set(d.stack,a)}let m={resources:s,problems:p,stacks:u,stackNames:n,unassignedResources:r,resourcesByStack:f};return I.set(l,m),m}var A={proxy:{name:"proxy",description:"Traefik reverse proxy and API gateway for routing all services",category:"core",phase:"pre_alpha",enabled_by_default:!0,dependsOn:[]},verdaccio:{name:"verdaccio",description:"Verdaccio npm registry for package management and local publishing (Premium - requires FLISK_LICENSE_KEY)",category:"premium",phase:"pre_alpha",enabled_by_default:!1,dependsOn:[]},[x]:{name:x,description:"PostgreSQL database for data persistence and SQL operations",category:"core",phase:"pre_alpha",enabled_by_default:!0,dependsOn:[]},monitoring:{name:"monitoring",description:"SigNoz or SkyWalking observability platform for monitoring and tracing",category:"optional",phase:"pre_alpha",enabled_by_default:!1,dependsOn:[x]},elk:{name:"elk",description:"Elasticsearch, Logstash, Kibana stack for log aggregation and analysis",category:"optional",phase:"pre_alpha",enabled_by_default:!1,dependsOn:[]},debezium:{name:"debezium",description:"Change Data Capture for real-time data streaming from databases",category:"optional",phase:"pre_alpha",enabled_by_default:!1,dependsOn:[x]},"golden-image":{name:"golden-image",description:"Pre-built Docker image layer for faster builds and smaller deployments",category:"optional",phase:"pre_alpha",enabled_by_default:!0,dependsOn:[]}};function H(e,o){try{return le(e,o)}catch{return[]}}function Ae(e){return new Set([...e.phases.pre_alpha.enabledStacks,...e.phases.alpha.enabledStacks,...e.phases.beta.enabledStacks,...e.phases.out_of_scope.enabledStacks])}function Q(e,o){let l=Ae(e),s=o.filter(p=>!l.has(p));return s.length===0?(e.phases.pre_alpha.enabledStacks=Array.from(new Set(e.phases.pre_alpha.enabledStacks)),[]):(e.phases.pre_alpha.enabledStacks=Array.from(new Set([...e.phases.pre_alpha.enabledStacks,...s])),s)}function gt(e,o){let l=_(e,".flisk",M);if(!S(l))return[];let s=D(e),p=Q(s,H(e,o));return p.length>0&&N(l,s),p}function $e(){return Object.values(A).filter(e=>e.enabled_by_default&&e.category==="core").map(e=>e.name)}function Re(e,o){return o||(O[e]?.repo.split("/").pop()??e).replace(/\.git$/,"")}function ge(e){return e.split(`
|
|
3
|
+
`).map(l=>l.trim()).filter(Boolean).pop()??"unknown error"}async function Fe(e,o,l,s={}){let p=O[e];if(!p){console.log(g.red(`
|
|
4
4
|
\u274C Unknown template: "${e}"`)),console.log(g.yellow(`
|
|
5
|
-
Available templates:`));for(let[r,
|
|
6
|
-
`,0),t(`Source: ${
|
|
7
|
-
`,0));try{if(
|
|
5
|
+
Available templates:`));for(let[r,f]of Object.entries(O))console.log(` ${g.cyan(r)} ${g.gray(`- ${f.description}`)}`);console.log(g.gray("\nOr run `flisk project` with no template for a blank project.")),process.exit(1)}let u=Re(e,o);ie(u)||U.invalidPath(u).exit();let n=Ee(u)?u:q(he(),u);S(n)&&_e(n).length>0&&U.directoryExists(n).exit(),s.quiet||(B(`Cloning template: ${e}`),t(`${p.description}
|
|
6
|
+
`,0),t(`Source: ${p.bundledPath?"bundled Flisk example":p.repo}`,0),t(`Destination: ${n}
|
|
7
|
+
`,0));try{if(p.bundledPath){let r=q(Pe(Ce(new URL("../dist/commands/project.js",import.meta.url).href)),"../../../");je(q(r,p.bundledPath),n,{recursive:!0});let f=xe(n).replace(/-/g,"_");for(let m of["services/platform/messaging/docker-compose.yml","services/shop/orders-api/service.json","services/shop/orders-api/src/index.ts","services/shop/orders-worker/service.json","services/shop/orders-worker/src/index.ts"]){let d=_(n,m),a=K(d,"utf-8");we(d,a.replaceAll("flisk_example",f))}}else Se("git",["clone",p.repo,n],{stdio:["ignore","pipe","pipe"],encoding:"utf-8"})}catch(r){let f=r instanceof Error?r.message:String(r),m=p.bundledPath?`Bundled example copy failed (${ge(f)}). Reinstall the Flisk CLI package and try again.`:`git clone failed: ${ge(f)}. Check you have git installed and network access.`;if(s.quiet)throw new Error(m);E(m)}if(me(),!s.quiet){console.log(g.green(`
|
|
8
8
|
\u2705 Cloned "${e}" into ${u}/`)),t(`
|
|
9
9
|
Next steps:`,0),t(`1. cd ${u}`),t("2. flisk doctor # check Docker/Tilt are ready"),t("3. flisk up # start the stack"),t(`
|
|
10
|
-
Runtime readiness:`,0);for(let r of l)t(r,0)}}var $={version:"1.0",project:{name:"",version:"1.0.0"},phases:{pre_alpha:{name:"Pre-Alpha",description:"Core infrastructure and MVP services",enabledStacks:$e()},alpha:{name:"Alpha",description:"Essential business services",enabledStacks:[]},beta:{name:"Beta",description:"Extended features",enabledStacks:[]},out_of_scope:{name:"Out of Scope",description:"Future releases",enabledStacks:[]}},optional_infra:{monitoring:!1,elk:!1,debezium:!1,golden_image:!0,verdaccio:!1},discovery:{paths:["services/*/*"]},overrides:{}},ht=ue(new
|
|
10
|
+
Runtime readiness:`,0);for(let r of l)t(r,0)}}var $={version:"1.0",project:{name:"",version:"1.0.0"},phases:{pre_alpha:{name:"Pre-Alpha",description:"Core infrastructure and MVP services",enabledStacks:$e()},alpha:{name:"Alpha",description:"Essential business services",enabledStacks:[]},beta:{name:"Beta",description:"Extended features",enabledStacks:[]},out_of_scope:{name:"Out of Scope",description:"Future releases",enabledStacks:[]}},optional_infra:{monitoring:!1,elk:!1,debezium:!1,golden_image:!0,verdaccio:!1},discovery:{paths:["services/*/*"]},overrides:{}},ht=ue(new Oe("project")).alias("init").description("Initialize or validate project-level master configuration").argument("[template]",`Clone a starter example instead of a blank project (${Object.keys(O).join(", ")})`).option("--path <dir>","Directory to clone the template into (default: the template's repo name)").option("--check","Check if master configs exist and are in sync").option("--force","Overwrite existing configuration (dangerous)").option("--config-file <path>","Load project config from existing JSON file").option("-q, --quiet","Only print what fails; used by `flisk demo`",!1).action(async(e,o)=>{let{assertMachineReadyOrExit:l,detectRuntimePrerequisites:s,formatRuntimeNextSteps:p}=await import("./cold-preflight-3BNDOLGS.js");await l({scope:"scaffold"});let u=p(s()),n=!!o.quiet;if(e){await z(async()=>Fe(e,o.path,u,{quiet:n}));return}await z(async()=>{let r=F();r||(r=he(),n||(b(`\u{1F680} Initializing new Flisk project...
|
|
11
11
|
`),t(`Location: ${r}
|
|
12
|
-
`,0)));let
|
|
13
|
-
`,0)),S(
|
|
12
|
+
`,0)));let f=_(r,".flisk"),m=_(f,M);if(o.check){let c=P.every(k=>S(_(r,".flisk",".flisk-out",k))),v=S(m);if(c&&v){let k=D(r);h("Project configuration is valid"),t(`Project: ${k.project.name}`,3),t(`Phases: ${Object.keys(k.phases).join(", ")}`,3),process.exit(0)}else{if(console.log(g.yellow("\u26A0\uFE0F Project configuration incomplete:")),v||t(".flisk/project.json (not found)",3),!c)for(let k of P.filter(j=>!S(_(r,".flisk/.flisk-out",j))))t(`.flisk/.flisk-out/${k} (not found)`,3);t("Run `flisk project` to create them."),process.exit(1)}}n||(B("Project Configuration"),t(`Project root: ${r}
|
|
13
|
+
`,0)),S(f)||(fe(f),n||h("Created: .flisk/ directory"));let d=S(m);if(d&&!o.force){n||h(".flisk/project.json exists");let c=JSON.parse(K(m,"utf-8")),v=D(r),k=H(r),j=Q(v,k);if((j.length>0||!c.phases)&&N(m,v),j.length>0&&!n&&t(`Auto-enabled discovered service stacks: ${j.join(", ")}`,0),n||b(`
|
|
14
14
|
\u{1F4CB} Regenerating master configuration files...
|
|
15
15
|
`),await Y(r,{quiet:n}),W(r))n||h("Created: .env (the defaults work as-is)");else{let y=G(r);y.length>0&&!n&&h(`Updated: .env (added ${y.join(", ")}; existing values unchanged)`)}let R=V(r);if(R.length>0&&!n&&h(`Added to .gitignore: ${R.join(", ")}`),!n){console.log(g.green(`
|
|
16
16
|
\u2705 Project configuration regenerated!`)),t(`
|
|
17
17
|
Generated in .flisk/.flisk-out/:`,0);for(let y of P)t(`- ${y}`);t(`
|
|
18
|
-
Runtime readiness:`,0);for(let y of u)t(y,0)}return}if(
|
|
18
|
+
Runtime readiness:`,0);for(let y of u)t(y,0)}return}if(d&&o.force&&!o.yes&&(console.log(g.red(`
|
|
19
19
|
\u26A0\uFE0F WARNING: --force will overwrite .flisk/project.json!`)),!await L({message:"This will reset your project configuration. Continue?",initial:!1}))){T();return}let a,X=H(r);if(o.configFile){let c=q(o.configFile);S(c)||E(`Config file not found: ${c}`);let v=K(c,"utf-8");a=JSON.parse(v),n||h(`Loaded config from: ${c}`)}else if(o.yes)a=JSON.parse(JSON.stringify($)),a.project.name=r.split("/").pop()||"my-project",n||console.log(g.gray("Using default configuration (non-interactive mode)"));else{b(`\u{1F4DD} Project Setup Wizard
|
|
20
|
-
`);let c=await J({message:"Project name:",initial:r.split("/").pop()||"my-project",validate:i=>i.trim()!==""||"Project name is required"}),v=await J({message:"Project version:",initial:"1.0.0-alpha"}),k=Object.values(A).filter(i=>i.category==="core"&&i.phase==="pre_alpha"),j=new Set(k.map(i=>i.name)),oe=await
|
|
20
|
+
`);let c=await J({message:"Project name:",initial:r.split("/").pop()||"my-project",validate:i=>i.trim()!==""||"Project name is required"}),v=await J({message:"Project version:",initial:"1.0.0-alpha"}),k=Object.values(A).filter(i=>i.category==="core"&&i.phase==="pre_alpha"),j=new Set(k.map(i=>i.name)),oe=await C({message:"Select Pre-Alpha enabled stacks (core infrastructure):",choices:[...k.map(i=>({title:i.description,value:i.name,selected:i.enabled_by_default})),...X.filter(i=>!j.has(i)).map(i=>({title:`${i} (discovered service stack)`,value:i,selected:!0}))]}),R=await C({message:"Select Alpha enabled stacks (core business):",choices:[{title:"api (Backend API)",value:"api"},{title:"app (Frontend app)",value:"app"}]}),y=await C({message:"Select Beta enabled stacks (extended features):",choices:[{title:"worker (Background jobs)",value:"worker"},{title:"migrator (Database migrations)",value:"migrator"}]}),w=await C({message:"Enable optional infrastructure (high resource):",choices:Object.values(A).filter(i=>i.category==="optional").concat(Object.values(A).filter(i=>i.category==="premium")).map(i=>({title:i.description,value:i.name,selected:i.enabled_by_default}))});b(`
|
|
21
21
|
\u{1F4C2} Service discovery`),t("What: folder patterns Flisk scans for a service.json in each match."),t("Where: type one or more globs, comma-separated."),t("How: saved as discovery.paths in .flisk/project.json - editable later."),t(`Example: ${$.discovery.paths.join(", ")}
|
|
22
22
|
`);let ke=await J({message:"Folders to scan:",initial:$.discovery.paths.join(", ")});a=JSON.parse(JSON.stringify($)),a.project.name=c,a.project.version=v,a.phases.pre_alpha.enabledStacks=Array.from(new Set(oe)),a.phases.alpha.enabledStacks=Array.from(new Set(R)),a.phases.beta.enabledStacks=Array.from(new Set(y)),a.optional_infra.monitoring=w.includes("monitoring"),a.optional_infra.elk=w.includes("elk"),a.optional_infra.debezium=w.includes("debezium"),a.optional_infra.golden_image=w.includes("golden_image"),a.optional_infra.verdaccio=w.includes("verdaccio");let ne=ke.split(",").map(i=>i.trim()).filter(i=>i.length>0);a.discovery.paths=ne.length>0?ne:$.discovery.paths}let Z=Q(a,X);if(Z.length>0&&!n&&t(`Auto-enabled discovered service stacks: ${Z.join(", ")}`,0),n||b(`
|
|
23
23
|
\u{1F4CB} Creating project configuration...
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{createRequire as __fliskRequire}from'node:module';const require=__fliskRequire(import.meta.url);
|
|
2
|
+
var s={demo:{repo:"https://github.com/flisk-sh/flisk-restaurant-example.git",description:"The demo stack: restaurant ops with reservations, kitchen and floor control",fixedDir:"flisk-demo"},restaurant:{repo:"https://github.com/flisk-sh/flisk-restaurant-example.git",description:"Restaurant ops: reservations, kitchen, floor control (Hono + Vite + Bun)"},saas:{repo:"https://github.com/flisk-sh/flisk-saas-starter.git",description:"SaaS starter: account dashboard and a working checkout button (Hono + Vite + Bun)"},erp:{repo:"https://github.com/flisk-sh/flisk-erp-system.git",description:"Enterprise ERP: 100 microservices across 7 business domains"},"user-management":{repo:"https://github.com/flisk-sh/flisk-user-management.git",description:"User/auth management services"},ecommerce:{repo:"https://github.com/flisk-sh/flisk-ecommerce-example.git",description:"Vue 3 storefront and Hono catalog API (needs Flisk 1.3.75+ for --framework vue)"},example:{repo:"https://github.com/flisk-sh/flisk-example.git",bundledPath:"examples/tdk-example",description:"Hono + Postgres + NATS worker + Vite example with a routed write path"},"agent-stack":{repo:"https://github.com/flisk-sh/flisk-agent-stack.git",description:"AI agent stack: Bun agent + MCP tool server + Postgres + NATS"}};function i(t){let e=t[2];if(!(!e||e.startsWith("-")))return e in s?e:void 0}export{s as a,i as b};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import{createRequire as __fliskRequire}from'node:module';const require=__fliskRequire(import.meta.url);
|
|
2
|
-
import{f as A}from"./chunk-
|
|
2
|
+
import{f as A}from"./chunk-U42XZWXB.js";var o=[A,"proxy"];export{o as a};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import{createRequire as __fliskRequire}from'node:module';const require=__fliskRequire(import.meta.url);
|
|
2
|
+
import{randomUUID as F}from"node:crypto";import{existsSync as p,mkdirSync as g,readFileSync as y,statSync as M,writeFileSync as h}from"node:fs";import{homedir as k}from"node:os";import{join as a}from"node:path";import{spawnSync as _}from"node:child_process";import{existsSync as E,mkdirSync as w,mkdtempSync as N,readdirSync as $,renameSync as C,rmSync as L,statSync as j,writeFileSync as O}from"node:fs";import{tmpdir as D}from"node:os";import{join as c}from"node:path";function v(e,r){let n=N(c(D(),"flisk-ext-"));try{let o=c(n,"bundle.tar.gz");O(o,e),w(r,{recursive:!0});let t=process.platform==="win32"?"tar.exe":"tar",i=_(t,["xzf",o,"-C",r,"--strip-components=1"],{stdio:"pipe",windowsHide:!0});if(i.status!==0){let s=c(n,"fallback");w(s,{recursive:!0});let f=_(t,["xzf",o,"-C",s],{stdio:"pipe",windowsHide:!0});if(f.status!==0)throw new Error(`tar extraction failed: ${f.stderr?.toString()||i.stderr?.toString()||"unknown error"}`);let l=$(s),[u]=l,m=l.length===1&&u!==void 0&&E(c(s,u))&&j(c(s,u)).isDirectory()?c(s,u):s;for(let d of $(m))C(c(m,d),c(r,d))}}finally{L(n,{recursive:!0,force:!0})}}var U="https://tdk-extension-dist.oranguman.workers.dev/v1/premium/bundle.tar.gz",A=["playwright","c4-diagram","logging","agents-md","sablier","verdaccio"],z=720*60*1e3,S={"typescript/playwright_config.star":"engine/topologies/tilt/generators/typescript/playwright_config.star","generators/agents_md.star":"engine/topologies/tilt/generators/agents_md.star","c4_diagram.star":"engine/topologies/tilt/generators/c4_diagram.star","observability/logging.star":"engine/topologies/platform/observability/logging.star","generators/database_provisioner.star":"engine/topologies/tilt/generators/database_provisioner.star","generators/dependency_manifest.star":"engine/topologies/tilt/generators/dependency_manifest.star","generators/fixed_frontend_tsconfig.star":"engine/topologies/tilt/generators/typescript/fixed_frontend_tsconfig.star","synthetic_monitor.star":"engine/resources/synthetic_monitor.star","synthetic-monitor-topology.star":"engine/topologies/platform/services/synthetic-monitor.star","database-management-service.yaml":"platform/services/platform/database-management/service.yaml","networking/sablier_container_cycle.star":"engine/topologies/platform/docker/networking/sablier_container_cycle.star","registries/verdaccio_loader.star":"engine/topologies/platform/registries/verdaccio_loader.star"};function P(){return process.env.FLISK_LICENSE_KEY?.trim()||null}function R(){return process.env.FLISK_PREMIUM_ENDPOINT?.trim()||U}function I(e){let r=a(e,".flisk",".project-id");if(p(r))return y(r,"utf-8").trim();let n=F();return g(a(e,".flisk"),{recursive:!0}),h(r,`${n}
|
|
3
|
+
`),n}function B(e){let r=e.replace(/[^a-zA-Z0-9_-]/g,"_");return a(k(),".flisk","cache",`premium-${r}`,"premium.tar.gz")}function x(e){return p(e)?Date.now()-M(e).mtimeMs<z:!1}async function T(e,r,n){let o=`${R()}?resource=${encodeURIComponent(r)}&projectId=${encodeURIComponent(n)}`,t=await fetch(o,{headers:{Authorization:`Bearer ${e}`}});return t.status!==401?t:(await t.arrayBuffer().catch(()=>{}),fetch(`${o}&key=${encodeURIComponent(e)}`))}async function H(e,r){let n=[];for(let o of A){let t;try{t=await T(e,o,r)}catch(i){return console.warn(`\u26A0\uFE0F Premium fetch failed (network error): ${i.message}`),null}if(t.ok){let i=Buffer.from(await t.arrayBuffer());return console.info(`\u{1F50E} Premium entitlement probe: granted resource=${o} (HTTP ${t.status}); prior denials=${n.join(", ")||"none"}`),console.info(`\u{1F50E} Premium bundle download: ${i.byteLength} bytes`),i}if(t.status===401||t.status===403){n.push(`${o}=${t.status}`);continue}return console.warn(`\u26A0\uFE0F Premium fetch failed: ${o} returned HTTP ${t.status}; prior entitlement probes: ${n.join(", ")||"none"}`),null}return console.warn(`\u26A0\uFE0F Premium entitlement probes denied: ${n.join(", ")}`),null}async function re(e,r){let n=P();if(!n)return!1;let o=B(n);if(x(o))console.info("\u{1F50E} Premium bundle cache: hit (cached bundle reused)");else{console.info("\u{1F50E} Premium bundle cache: miss; requesting bundle from distribution service");let t=I(e),i=await H(n,t);if(!i)return console.warn("\u26A0\uFE0F No premium resources unlocked for this key - using free tier only."),!1;g(a(o,".."),{recursive:!0}),h(o,i)}try{let t=a(o,"..","extracted");g(t,{recursive:!0}),v(y(o),t);let i=0,s=[];for(let[l,u]of Object.entries(S)){let m=a(t,l);if(!p(m)){s.push(l);continue}let d=a(r,u);g(a(d,".."),{recursive:!0}),h(d,y(m)),i++}let f=a(t,"networking/sablier_container_cycle.star");if(console.info(`\u{1F50E} Premium bundle contents: mapped=${i}/${Object.keys(S).length}; sablier_overlay=${p(f)?"present":"MISSING"}`),s.length>0&&console.info(`\u{1F50E} Premium bundle missing mapped files: ${s.join(", ")}`),!p(f)&&process.env.FLISK_PREMIUM_DIAGNOSTICS==="1"){let l=await G(e);console.info(`\u{1F50E} Sablier entitlement check: ${l?"granted":"not confirmed"}; compare with sablier_overlay above to distinguish license access from bundle contents`)}return i>0?(console.log(`\u2713 Premium resources unlocked (${i} file${i===1?"":"s"})`),!0):!1}catch(t){return console.warn(`\u26A0\uFE0F Failed to apply premium overlay: ${t.message}`),!1}}function K(e,r){let n=e.replace(/[^a-zA-Z0-9_-]/g,"_");return a(k(),".flisk","cache",`premium-${n}`,`${r}-access.json`)}async function b(e,r){let n=P();if(!n)return!1;let o=K(n,r);if(x(o))try{let s=JSON.parse(y(o,"utf-8"));return console.info(`\u{1F50E} ${r} entitlement probe: cache=${s.granted===!0?"granted":"denied"}`),s.granted===!0}catch{}let t=I(e),i=!1;try{let s=await T(n,r,t);i=s.ok,console.info(`\u{1F50E} ${r} entitlement probe: HTTP ${s.status} (${i?"granted":"denied"})`),s.body&&await s.arrayBuffer().catch(()=>{})}catch(s){return console.warn(`\u26A0\uFE0F ${r} license check failed (network error): ${s.message}`),!1}try{g(a(o,".."),{recursive:!0}),h(o,JSON.stringify({granted:i,checked:new Date().toISOString()}))}catch{}return i}async function ne(e){return b(e,"verdaccio")}async function oe(e){return b(e,"ddd")}async function G(e){return b(e,"sablier")}export{v as a,re as b,ne as c,oe as d,G as e};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import{createRequire as __fliskRequire}from'node:module';const require=__fliskRequire(import.meta.url);
|
|
2
|
-
import{E as C,t as $}from"./chunk-
|
|
2
|
+
import{E as C,t as $}from"./chunk-U42XZWXB.js";var g=class extends Error{kind;constructor(i,a){super(i),this.kind=a,this.name="TiltEventsLoadError"}};function m(n){return typeof n=="object"&&n!==null&&!Array.isArray(n)?n:null}var w="\x1B",k="\x07",I=new RegExp(`${w}(?:\\[[0-?]*[ -/]*[@-~]|\\][^${k}${w}]*(?:${k}|${w}\\\\)?|[@-Z\\\\-_])`,"g"),O=new RegExp("[\\u0000-\\u0008\\u000b-\\u001f\\u007f-\\u009f]","g");function U(n){return n.replace(I,"").replace(O,"")}function r(n){return typeof n!="string"?void 0:U(n).trim()||void 0}function _(n){return Array.isArray(n)?n.flatMap(i=>{let a=r(i);return a?[a]:[]}):[]}function L(n){let i=m(n);if(!i||!Array.isArray(i.items))throw new Error("Tilt returned an invalid UIResource list");let a=[],e=[];for(let s of i.items){let u=m(s),N=m(u?.metadata),t=r(N?.name);if(!u||!t)continue;let c=m(u.status);a.push({name:t,runtimeStatus:r(c?.runtimeStatus),updateStatus:r(c?.updateStatus),hasPendingChanges:typeof c?.hasPendingChanges=="boolean"?c.hasPendingChanges:void 0});let x=Array.isArray(c?.buildHistory)?c.buildHistory:[];for(let[d,A]of x.entries()){let o=m(A);if(!o)continue;let f=r(o.error),l=_(o.warnings),p=[f,...l].filter(S=>!!S),h=r(o.spanID);e.push({id:`${t}:build:${h??String(d)}`,resourceName:t,kind:f?"failed":l.length?"warning":"success",title:f?"Build failed":l.length?"Build completed with warnings":"Build completed",occurredAt:r(o.finishTime)??r(o.startTime),details:p.length?p.join("; "):void 0})}let T=m(c?.currentBuild);T&&e.push({id:`${t}:current-build`,resourceName:t,kind:"running",title:"Build in progress",occurredAt:r(T.startTime)});let b=r(c?.pendingBuildSince);!T&&(c?.queued===!0||b)&&e.push({id:`${t}:queued`,resourceName:t,kind:"warning",title:"Update queued",occurredAt:b});let y=r(c?.lastDeployTime),E=y?Date.parse(y):Number.NaN,R=e.some(d=>d.resourceName===t&&d.kind==="success"&&d.occurredAt!==void 0&&Date.parse(d.occurredAt)===E);y&&!R&&e.push({id:`${t}:last-deploy`,resourceName:t,kind:"success",title:"Last deployed",occurredAt:y});let B=Array.isArray(c?.conditions)?c.conditions:[];for(let[d,A]of B.entries()){let o=m(A);if(!o)continue;let f=r(o.type),l=r(o.status),p=r(o.reason),h=r(o.message);if(!f&&!p&&!h||l==="True"&&!p&&!h)continue;let S=[p,h].filter(D=>!!D).join(": ");e.push({id:`${t}:condition:${d}:${f??"status"}`,resourceName:t,kind:l==="False"?"warning":"status",title:`Condition ${f??"status"}${l?`: ${l}`:""}`,occurredAt:r(o.lastTransitionTime),details:S||void 0})}}return e.sort((s,u)=>{if(s.kind==="running"&&u.kind!=="running")return-1;if(u.kind==="running"&&s.kind!=="running")return 1;let N=s.occurredAt?Date.parse(s.occurredAt):Number.NaN,t=u.occurredAt?Date.parse(u.occurredAt):Number.NaN;return Number.isNaN(N)?Number.isNaN(t)?s.resourceName.localeCompare(u.resourceName):1:Number.isNaN(t)?-1:t-N}),{resources:a,events:e}}async function q(){let n=process.env.TILT_PORT??String($.tiltUi),i;try{i=await C("get",["uiresources","-o","json","--port",n],{inheritStdio:!1,timeoutMs:1e4})}catch(e){let s=e instanceof Error?e.message:String(e),u=/ENOENT|not found|no such file|connection refused|failed to connect|could not connect|unable to connect|Tilt is not running/i.test(s)?"unavailable":"error";throw new g(s,u)}if(i.exitCode!==0){let e=i.stderr.trim()||`tilt get exited with code ${i.exitCode}`,s=/connection refused|failed to connect|could not connect|unable to connect|Tilt is not running|no running Tilt/i.test(e)?"unavailable":"error";throw new g(e,s)}let a;try{a=JSON.parse(i.stdout)}catch{throw new g("Tilt returned invalid UIResource JSON","error")}try{return L(a)}catch(e){throw new g(e instanceof Error?e.message:"Tilt returned an invalid UIResource list","error")}}export{g as a,U as b,q as c};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import{createRequire as __fliskRequire}from'node:module';const require=__fliskRequire(import.meta.url);
|
|
2
|
+
import{N as H,b as f,e as q,h as xe,i as Ee,m as ke,n as we,r as K,u as ve,v as be,x as A}from"./chunk-U42XZWXB.js";import st,{existsSync as O,readdirSync as nn,readFileSync as rn,statSync as ce}from"node:fs";import{dirname as on,join as w}from"node:path";var j=class{data;options;constructor(t){this.data=new Map,this.options=t}has(t){let n=this.data.get(t);return n?Date.now()-n.timestamp>this.options.ttlMs?(this.data.delete(t),!1):!0:!1}get(t){if(this.has(t))return this.data.get(t)?.value}set(t,n){this.data.set(t,{value:n,timestamp:Date.now()})}delete(t){this.data.delete(t)}clear(){this.data.clear()}keys(){let t=[];for(let n of this.data.keys())this.has(n)&&t.push(n);return t}size(){return this.keys().length}};function Te(e){let t=0;return{isValid(){return Date.now()-t<e},markUpdated(){t=Date.now()},reset(){t=0}}}function ct(e,t,n){return e===1?t:n||`${t}s`}function Re(e,t,n){return`${e} ${ct(e,t,n)}`}var ut={month:"short",day:"numeric",hour:"2-digit",minute:"2-digit"};function Sn(e){return new Date(e).toLocaleString("en-US",ut)}function yn(e){return new Date(e).toLocaleDateString("en-US",{month:"short",day:"numeric"})}var J={success:{color:"green",icon:"\u2713",chalkFn:f.green},error:{color:"red",icon:"\u2717",chalkFn:f.red},warning:{color:"yellow",icon:"\u25CB",chalkFn:f.yellow},unknown:{color:"gray",icon:"?",chalkFn:f.gray}};function Y(e){if(!e)return"unknown";let t=e.toLowerCase();return t==="ready"||t==="healthy"||t==="active"||t==="running"?"success":t==="error"||t==="failed"||t==="critical"||t==="stopped"?"error":t==="pending"||t==="starting"||t==="building"||t==="degraded"?"warning":"unknown"}function $n(e){return J[Y(e)].color}function xn(e){return J[Y(e)].icon}function En(e,t){return J[Y(t)].chalkFn(e)}var ft={resources:{singular:"resource",command:"flisk resource <name>",context:`
|
|
3
|
+
To create a resource:`},stacks:{singular:"stack",command:"flisk stack <stack-name>",context:`
|
|
4
|
+
To create a stack, use:`},services:{singular:"service",command:"flisk resource <name>",context:`
|
|
5
|
+
To create a service:`},"stack-services":{singular:"service",command:"flisk resource <name> --stack <stack-name>",context:`
|
|
6
|
+
To add services to this stack:`}};function kn(e,t){let n=ft[e];console.log(t?f.yellow(`No ${n.singular}s found${t}.`):f.yellow(`No ${n.singular}s found.`)),console.log(f.gray(n.context)),console.log(f.gray(` ${n.command}`)),e==="stacks"&&(console.log(f.gray(`
|
|
7
|
+
Or create a new resource with a stack:`)),console.log(f.gray(" flisk resource <name> --stack <stack-name>")))}function wn(e){console.log(f.yellow(e||"Cancelled."))}function vn(e){console.log(f.blue(`Flisk ${e}
|
|
8
|
+
`))}function bn(e,t){console.log(f.green(`All ${e} are ${t}!`))}var Z=62;function Tn(e="\u2500",t=Z){return e.repeat(t)}function Ae(e,t=Z-2){let n=Math.max(0,t-e.length),r=Math.floor(n/2),o=n-r;return" ".repeat(r)+e+" ".repeat(o)}function An(e,t){return e.length>t?`${e.slice(0,t-1)}\u2026`:e.padEnd(t)}function Rn(e,t){return e.length<=t?e:`${e.slice(0,t-3)}...`}function _n(e){console.log(f.green(`\u2713 ${e}`))}function On(e){console.log(f.blue(e))}function Cn(e,t=2){console.log(f.gray(`${" ".repeat(t)}${e}`))}function Dn(e){return e<1024?`${e} B`:e<1024*1024?`${(e/1024).toFixed(1)} KB`:`${(e/(1024*1024)).toFixed(1)} MB`}function Fn(e,t,n=Z){let r=n-2,o="\u2500".repeat(r);console.log(),console.log(f.cyan(`\u256D${o}\u256E`)),console.log(f.cyan("\u2502")+f.bold.white(Ae(e,r))+f.cyan("\u2502")),t&&(console.log(f.cyan(`\u251C${o}\u2524`)),console.log(f.cyan("\u2502")+f.gray(Ae(t,r))+f.cyan("\u2502"))),console.log(f.cyan(`\u2570${o}\u256F`))}import{existsSync as et,readFileSync as tt}from"node:fs";import{copyFileSync as Ve,mkdirSync as It,rmSync as We,writeFileSync as ze}from"node:fs";import{join as _}from"node:path";function b(e){return e==="backend"||e==="mcp"||e==="agent"}import{existsSync as At,readFileSync as Rt}from"node:fs";import{join as _t}from"node:path";import{randomBytes as Oe}from"node:crypto";import{existsSync as X,readFileSync as Ce,writeFileSync as De}from"node:fs";import{join as ee}from"node:path";import{randomBytes as pt}from"node:crypto";import{closeSync as dt,fchmodSync as lt,fsyncSync as mt,openSync as ht,renameSync as gt,rmSync as St,statSync as yt,writeFileSync as $t}from"node:fs";import{basename as xt,dirname as Et,join as kt}from"node:path";function _e(e,t){let n=kt(Et(e),`.${xt(e)}.${process.pid}.${pt(4).toString("hex")}.tmp`),r;try{r=yt(e).mode&511}catch{r=void 0}try{let o=ht(n,"wx");try{r!==void 0&<(o,r),$t(o,t,"utf-8"),mt(o)}finally{dt(o)}gt(n,e)}catch(o){throw St(n,{force:!0}),o}}var T=[{name:"VERDACCIO_URL_DOCKER",description:"Docker-internal URL of the private npm registry (Verdaccio, a paid feature)",required:!1,default:"http://verdaccio:4873",example:"http://verdaccio:4873"},{name:"VERDACCIO_URL",description:"Local URL of the private npm registry (Verdaccio, a paid feature)",required:!1,default:"http://localhost:4873"},{name:"TILT_ENV",description:"Tilt environment name",required:!0,default:"dev",example:"dev",complete:!0},{name:"DATABASE_URL",description:"PostgreSQL connection string (uses the generated DB_PASSWORD below)",required:!1},{name:"DB_PASSWORD",description:"PostgreSQL superuser password (docker-compose POSTGRES_PASSWORD)",required:!0,example:"postgres",complete:!0},{name:"JWT_SECRET",description:"Secret that signs local-jwt tokens (generated once per project; never shared between projects)",required:!1,complete:!0}],Fe=()=>Oe(16).toString("hex"),wt=()=>Oe(32).toString("hex"),zn=T.filter(e=>e.complete).map(e=>e.name);function Q(e,t){return e==="DB_PASSWORD"?t:e==="JWT_SECRET"?wt():e==="DATABASE_URL"?`postgresql://postgres:${t}@postgres:5432/app_dev`:T.find(n=>n.name===e)?.default??""}function vt(e){let t=e.trim(),n=t[0];if(n==='"'||n==="'"){let o=!1;for(let s=1;s<t.length;s+=1){let a=t[s];if(o){o=!1;continue}if(a==="\\"){o=!0;continue}if(a===n){let i=t.slice(1,s);if(n==='"'){let c={n:`
|
|
9
|
+
`,r:"\r",t:" ",'"':'"',"\\":"\\",$:"$"};return i.replace(/\\([nrt"\\$])/g,(d,p)=>c[p]??p)}return i.replace(/\\'/g,"'")}}return t}let r=e.search(/\s#/);return(r===-1?e:e.slice(0,r)).trim()}function R(e){let t=new Map,n=e.startsWith("\uFEFF")?e.slice(1):e;for(let r of n.split(/\r?\n/)){let o=r.trim();if(!o||o.startsWith("#"))continue;let s=o.startsWith("export ")?o.slice(7).trim():o,a=s.indexOf("=");if(a<=0)continue;let i=s.slice(0,a).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(i)&&t.set(i,vt(s.slice(a+1)))}return t}function Ne(e){let t=[`# ${e.description}`];return e.required&&t.push("# Required (the value below is a working default)"),e.example&&t.push(`# Example: ${e.example}`),t}function je(){let e=Fe(),t=["# \u{1F527} Flisk Environment Configuration","# Generated by `flisk project`. The defaults work as-is; edit only to change them.","# Keep this file out of git: it holds a generated database password and JWT secret.","#","# After editing, run: flisk config regenerate && flisk up",""];for(let n of T)t.push(...Ne(n)),t.push(`${n.name}=${Q(n.name,e)}`),t.push("");return t.join(`
|
|
10
|
+
`)}function bt(e){let t=e.match(/^[a-z][a-z0-9+.-]*:\/\/[^:/@\s]+:([^@\s]*)@/i);if(!(!t||t[1].includes("$")))return t[1]}function Gn(e){let t=ee(e,".env"),n={missing:[],invalid:[],warnings:[]};if(!X(t))return{missing:T.filter(a=>a.required).map(a=>a.name),invalid:[],warnings:[".env file not found - will be auto-generated"]};let r=R(Ce(t,"utf-8"));for(let a of T)a.required&&!r.has(a.name)&&n.missing.push(a.name),r.has(a.name)&&!(r.get(a.name)??"").trim()&&n.invalid.push(`${a.name} is set but empty`);let o=r.get("DATABASE_URL"),s=r.get("DB_PASSWORD");if(o&&s){let a=bt(o);a!==void 0&&a!==s&&n.warnings.push("DATABASE_URL carries a different password than DB_PASSWORD; the database container uses DB_PASSWORD")}return n}function Tt(e,t,n){if(!R(e).has(t))return e;let o=e.indexOf("=");if(o<0)return e;let s=e.slice(o+1),a=s.search(/\s#/),i=a<0?"":s.slice(a);return e.slice(0,o+1)+n+i}function qn(e){let t=ee(e,".env");if(!X(t))return De(t,je(),{mode:384}),T.map(u=>u.name);let n=Ce(t,"utf-8"),r=R(n),o=T.filter(u=>u.complete?r.has(u.name)?!(r.get(u.name)??"").trim():!0:!1);if(o.length===0)return[];let s=o.filter(u=>!r.has(u.name)),a=o.filter(u=>r.has(u.name)),i=r.get("DB_PASSWORD"),c=i?.trim()?i:Fe(),d=n.split(/(\r\n|\n|\r)/);for(let u of a){let h=-1;for(let l=0;l<d.length;l+=2)R(d[l]).has(u.name)&&(h=l);if(h<0)throw new Error(`Could not locate empty .env assignment for ${u.name}`);d[h]=Tt(d[h],u.name,Q(u.name,c))}let p=d.join("");if(s.length>0){let u=["","# Added by Flisk: keys this version expects. Existing values above were not changed.",""];for(let l of s)u.push(...Ne(l)),u.push(`${l.name}=${Q(l.name,c)}`),u.push("");let h=p.endsWith(`
|
|
11
|
+
`)||p.endsWith("\r")?"":`
|
|
12
|
+
`;p+=h+u.join(`
|
|
13
|
+
`)}return _e(t,p),o.map(u=>u.name)}function Kn(e){let t=ee(e,".env");return X(t)?!1:(De(t,je(),{mode:384}),!0)}var Ot="[REDACTED]",te=class extends Error{path;constructor(t,n){super(`Could not read ${t}: ${n}`),this.path=t,this.name="EnvUnreadableError"}},Ct=8;function Dt(e,t=[]){let n=[...Pe(_t(e,".env")),...t.flatMap(r=>Pe(r))];return[...new Set(n)].sort((r,o)=>o.length-r.length)}function Pe(e){if(!At(e))return[];let t;try{t=Rt(e,"utf-8")}catch(n){throw new te(e,n instanceof Error?n.message:String(n))}return[...R(t).values()].filter(n=>n.length>=Ct)}function Be(e,t){let n=e;for(let r of t)n=n.split(r).join(Ot);return n}function Ie(e,t){return t.length===0?e:typeof e=="string"?Be(e,t):Array.isArray(e)?e.map(n=>Ie(n,t)):e!==null&&typeof e=="object"?Object.fromEntries(Object.entries(e).map(([n,r])=>[n,Ie(r,t)])):e}function Me(e,t=[]){let n;try{n=Dt(e,t)}catch(r){return()=>{throw r}}return r=>Be(r,n)}import{readFileSync as Ft}from"node:fs";import{join as Nt}from"node:path";function or(){return Ue(A())}function Ue(e){if(e)try{let t=Ft(Nt(e,".flisk",ke),"utf-8"),n=JSON.parse(t);if(n?.project?.name)return n.project.name}catch{}return"flisk-project"}function B(e,t){let n=Ue(t??A()),r=process.env.FLISK_SERVICE_BASE_URL??`http://${n}.localhost${e?`:${e}`:""}`;try{let o=new URL(r.includes("://")?r:`http://${r}`),s=o.hostname,a=o.protocol==="https:"?"443":"80",i=o.port||(e?String(e):""),c=i===a?"":i;if(s==="localhost"||/^\d+\.\d+\.\d+\.\d+$/.test(s))return{appBase:`${o.protocol}//${s}${c?`:${c}`:""}`,apiBase:`${o.protocol}//${s}${c?`:${c}`:""}`};let d=s.replace(/^(app|api)\./,"");return{appBase:`${o.protocol}//app.${d}${c?`:${c}`:""}`,apiBase:`${o.protocol}//api.${d}${c?`:${c}`:""}`}}catch{return{appBase:`http://app.${n}.localhost`,apiBase:`http://api.${n}.localhost`}}}function jt(e){return`${e.replace(/\/+$/,"")}/health`}function M(e){if(b(e.config?.appType)){let t=e.name.replace(/-api$/,"");return e.config?.apiPath??`/api/${t}`}return e.config?.basePath??`/${e.name}`}function sr(e,t){let n=e.config?.appType;if(n!=="frontend"&&!b(n))return;let{appBase:r,apiBase:o}=B(t);return`${n==="frontend"?r:o}${M(e)}`}function ar(e,t){return Le(e,t,n=>jt(n))}function ir(e,t){return Le(e,t,n=>n)}function Le(e,t,n){let{appBase:r,apiBase:o}=B(t);return e.filter(s=>s.config?.appType==="frontend"||b(s.config?.appType)).map(s=>{let a=b(s.config.appType)?o:r;return{name:s.name,appType:s.config.appType,url:`${a}${n(M(s))}`}})}async function Pt(e,t){try{let n=await fetch(e.url,{method:"GET",redirect:"manual",signal:AbortSignal.timeout(t)});return{...e,ok:n.ok,status:n.status}}catch(n){let r=n instanceof Error&&n.name==="TimeoutError"?`no response within ${t}ms`:n instanceof Error?n.message:String(n);return{...e,ok:!1,error:r}}}async function cr(e,t){return Promise.all(e.map(n=>Pt(n,t)))}var Ye=60,Ge=1e3,qe=1e4,Ke=200,He=64*1024,Je=new Set([404,502,503,504]),Bt=new Set(["ECONNREFUSED","ENOTFOUND","EAI_AGAIN","EHOSTUNREACH","ENETUNREACH"]),Mt=new Set(["GET","HEAD","OPTIONS"]);function E(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function Ut(e,t){if(Mt.has(t))return!0;if(!E(e))return!1;let n=E(e.cause)?e.cause:void 0,r=e.code??n?.code;return typeof r=="string"&&Bt.has(r)}function re(e){if(!E(e))return['smoke: must be an object with "via" and "steps"'];let t=[];return e.via!=="proxy"&&t.push('smoke.via: only "proxy" is supported (the check must go through the public URL)'),e.timeoutSeconds!==void 0&&!(typeof e.timeoutSeconds=="number"&&e.timeoutSeconds>0)&&t.push("smoke.timeoutSeconds: must be a number greater than 0"),!Array.isArray(e.steps)||e.steps.length===0?(t.push("smoke.steps: needs at least one step"),t):(e.steps.forEach((n,r)=>{let o=`smoke.steps[${r}]`;if(!E(n)){t.push(`${o}: must be an object`);return}let s=n.path;(typeof s!="string"||!s.startsWith("/")||s.startsWith("//"))&&t.push(`${o}.path: must start with "/" and is appended to the public URL; a full URL or relative path would skip the proxy`),n.expect!==void 0&&!(typeof n.expect=="number"&&Number.isInteger(n.expect)&&n.expect>=100&&n.expect<=599)&&t.push(`${o}.expect: must be an HTTP status between 100 and 599`),n.bodyContains!==void 0&&typeof n.bodyContains!="string"&&t.push(`${o}.bodyContains: must be a string`),n.save!==void 0&&(E(n.save)&&Object.values(n.save).every(i=>typeof i=="string"&&i.startsWith("$."))||t.push(`${o}.save: must map names to paths such as "$.id"`))}),t)}function gr(e,t){let{appBase:n,apiBase:r}=B(t),o=[];for(let s of e){let a=s.config;if(!a?.smoke)continue;let i=a.appType,c=b(i)||i===q;if(!c&&i!=="frontend"||a.exposeViaProxy===!1)continue;let d=c?r:n,p=a.healthCheckPath||(c&&i!==q?"/health":void 0);o.push({name:s.name,baseUrl:`${d}${M(s).replace(/\/+$/,"")}`,...p?.startsWith("/")?{readyPath:p}:{},smoke:a.smoke})}return o}var Lt=10;function Sr(e,t={}){let n=t.timeoutSeconds??Lt;return e.flatMap(r=>{let o=r.smoke.steps.filter(a=>(a.method??"GET").toUpperCase()==="GET"&&a.body===void 0&&!a.path.includes("{{")).map(a=>({...a,bodyContains:void 0}));if(o.length===0)return[];let s=Math.min(r.smoke.timeoutSeconds??Ye,n);return[{name:r.name,baseUrl:r.baseUrl,smoke:{via:r.smoke.via,timeoutSeconds:s,steps:o}}]})}function Vt(e,t){let n=t.replace(/^\$\.?/,"").match(/[^.[\]]+/g)??[],r=e;for(let o of n){if(r==null||o==="__proto__"||o==="constructor"||o==="prototype"||!Object.hasOwn(r,o))return;let s=Object.getOwnPropertyDescriptor(r,o);if(!s||!("value"in s))return;r=s.value}return r}function Ze(e,t){return e.replace(/\{\{(\w+)\}\}/g,(n,r)=>t[r]??n)}function ne(e,t){return typeof e=="string"?Ze(e,t):Array.isArray(e)?e.map(n=>ne(n,t)):E(e)?Object.fromEntries(Object.entries(e).map(([n,r])=>[n,ne(r,t)])):e}function U(e){let t=e.replace(/\s+/g," ").trim();return t.length>Ke?`${t.slice(0,Ke)}...`:t}function Qe(e){return e.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")||"step"}function Wt(e,t){let n=Qe(t),r=(e.get(n)??0)+1;return e.set(n,r),r===1?n:`${n}-${r}`}function zt(e,t,n,r,o){try{let s=_(e,Qe(n.service),t);It(s,{recursive:!0});let a=_(s,"latest.json"),i=_(s,"body.txt");return r===void 0?We(i,{force:!0}):ze(i,r),ze(a,`${JSON.stringify({...n,bodyPath:r===void 0?null:"body.txt"},null,2)}
|
|
14
|
+
`),o&&(Ve(a,_(s,"last-success.json")),r===void 0?We(_(s,"last-success-body.txt"),{force:!0}):Ve(i,_(s,"last-success-body.txt"))),a}catch{return}}function Gt(e){let t=Buffer.from(e,"utf8");if(t.length<=He)return{body:e,truncated:!1,bytes:t.length};let n=t.subarray(0,He).toString("utf8").replace(/\uFFFD$/,"");return{body:n,truncated:!0,bytes:Buffer.byteLength(n,"utf8")}}async function qt(e,t={}){let n=t.fetch??((p,u)=>fetch(p,u)),r=t.now??Date.now,o=t.sleep??(p=>new Promise(u=>setTimeout(u,p))),s=(e.smoke.timeoutSeconds??Ye)*1e3,a=r()+s,i={},c=re(e.smoke);if(c.length>0)return{name:e.name,ok:!1,failure:`${e.name}: invalid smoke block: ${c.join("; ")}`};if(e.readyPath){let p=`${e.baseUrl}${e.readyPath}`;for(;;){try{let u=await n(p,{method:"GET",signal:AbortSignal.timeout(qe)});if(await u.text(),!Je.has(u.status)&&u.status<500)break}catch{}if(r()>=a)break;await o(Ge)}}a=r()+s;let d=new Map;for(let[p,u]of e.smoke.steps.entries()){let h=u.name??`step ${p+1}`,l=(u.method??"GET").toUpperCase(),me=`${e.baseUrl}${Ze(u.path,i)}`,C=u.expect??200,he=u.body!==void 0,S,D="",x,W;for(;;){x=void 0,W=void 0,S=void 0;let g=!1;try{let m=await n(me,{method:l,headers:he?{"content-type":"application/json"}:void 0,body:he?JSON.stringify(ne(u.body,i)):void 0,signal:AbortSignal.timeout(qe)});S=m.status,D=await m.text()}catch(m){g=Ut(m,l),x=m instanceof Error?m.message:String(m);let v=E(m)?m.code??(E(m.cause)?m.cause.code:void 0):void 0;W=typeof v=="string"?v:void 0}if(!(x!==void 0&&g||S!==void 0&&S!==C&&Je.has(S))||r()>=a)break;await o(Ge)}let ge=!1,z=g=>{if(!ge){if(!t.redact)return g;try{return t.redact(g)}catch($){ge=!0;let m=$ instanceof Error?$.message:String($);t.warn?.(`${e.name}: ${h}: the response text was neither shown nor stored: ${m}`);return}}},G=z(me),y=S===void 0?void 0:z(D),Se=x===void 0?void 0:z(x),ye=x===void 0?void 0:W??Se,F=`${e.name}: ${h}: ${l} ${G??"(url withheld)"}`,$e;if(t.recordDir){let g=y===void 0?void 0:Gt(y),$=x===void 0&&S===C&&(u.bodyContains===void 0||D.includes(u.bodyContains));$e=zt(t.recordDir,Wt(d,h),{service:e.name,step:h,method:l,...G===void 0?{}:{url:G},expectedStatus:C,status:S??null,...ye===void 0?{}:{error:ye},truncated:g?.truncated??!1,bodyBytes:g?.bytes??0},g?.body,$)}let N=g=>({name:e.name,ok:!1,failure:g,recordPath:$e});if(x!==void 0)return N(`${F} never answered (${Se??"the error was withheld"})`);if(S!==C)return N(`${F} returned ${S}, expected ${C}${y?`: ${U(y)}`:""}`);if(u.bodyContains!==void 0&&!D.includes(u.bodyContains))return N(`${F} returned ${S} but the body does not contain "${u.bodyContains}"${y?`: ${U(y)}`:""}`);if(u.save){let g;try{g=JSON.parse(D)}catch{return N(`${F} returned a body that is not JSON, cannot save${y?`: ${U(y)}`:""}`)}for(let[$,m]of Object.entries(u.save)){let v=Vt(g,m);if(v==null||typeof v=="object")return N(`${F} response has no ${m} to save as {{${$}}}${y?`: ${U(y)}`:""}`);i[$]=String(v)}}}return{name:e.name,ok:!0}}function yr(e){return`Smoke check failed: ${e.failure}${e.recordPath?` (record: ${e.recordPath})`:""}`}function $r(e,t,n={}){return{redact:Me(e,n.envFiles??[]),...t?{warn:t}:{}}}async function xr(e,t={}){return Promise.all(e.map(n=>qt(n,t)))}var Xe=1,vr="https://flisk.sh/schema.service.json",Kt={dependencies:"deprecated, use dependsOn (still read for now)",envVars:"deprecated, use params (still read when params is absent)",jwtSecret:"ignored. A secret must not be committed: remove it and set JWT_SECRET in the project .env"},Ht=new Set(["$schema","agent","apiBasePath","apiPath","appName","appType","backendName","basePath","build","buildContext","databaseName","dependencies","dev","dependsOn","dockerfile","enabled","exposeViaProxy","featuresEnabled","framework","healthCheckPath","image","language","name","nats","params","playwright","port","replicas","restart","runtime","sablier","schemaVersion","secrets","smoke","stack","syncs","traefik","type"]),Jt=[{path:["healthCheckPath"],pattern:/^\/[a-z0-9/-]*$/,expected:"a path such as /health"},{path:["traefik","host"],pattern:/^[A-Za-z0-9.-]+$/,expected:"a hostname such as shop.backend.myproject.local"},{path:["traefik","pathPrefix"],pattern:/^\/[a-z0-9/-]*$/,expected:"a path such as /api/v1/shop-management"},{path:["traefik","healthCheck"],pattern:/^\/[a-z0-9/-]*$/,expected:"a path such as /health"},{path:["nats","queueGroup"],pattern:/^[A-Za-z0-9_.-]+$/,expected:"letters, digits, '_', '.' or '-'"},{path:["databaseName"],pattern:/^[A-Za-z0-9_-]+$/,expected:"letters, digits, '_' or '-'"},{path:["stack"],pattern:/^[A-Za-z0-9_-]+$/,expected:"letters, digits, '_' or '-'"},{path:["image"],pattern:/^[A-Za-z0-9._/:@-]+$/,expected:"an image reference such as ghcr.io/acme/app:1.0"},{path:["basePath"],pattern:/^\/[a-z0-9/-]*$/,expected:"a path such as /storefront"}];function L(e,t){let n=[],r=new Set;for(let{path:o,pattern:s,expected:a}of Jt){let i=e;for(let d of o)i=i&&typeof i=="object"&&!Array.isArray(i)?i[d]:void 0;if(i==null||i==="")continue;let c=`${t}.${o.join(".")}`;(typeof i!="string"||!s.test(i))&&r.add(c),typeof i!="string"?n.push(`${c}: expected a string`):s.test(i)||n.push(`${c}: must be ${a} (no spaces, line breaks or YAML syntax)`)}for(let[o,s]of Yt(e,t))r.has(o)||n.push(`${o}: ${s}`);return n}function oe(e){for(let t=0;t<e.length;t++){let n=e.charCodeAt(t);if(n<32||n===127||n===133||n===8232||n===8233)return!0}return!1}function Yt(e,t){let n=[],r=Object.entries(e).filter(([o])=>o!=="smoke").map(([o,s])=>[s,`${t}.${o}`]);for(let o of Object.keys(e))oe(o)&&n.push([t,"a key contains a line break"]);for(;r.length>0;){let[o,s]=r.pop();if(typeof o=="string")oe(o)&&n.push([s,"must not contain line breaks or control characters"]);else if(Array.isArray(o))for(let[a,i]of o.entries())r.push([i,`${s}[${a}]`]);else if(o&&typeof o=="object")for(let[a,i]of Object.entries(o))oe(a)?n.push([s,"a key contains a line break"]):r.push([i,`${s}.${a}`])}return n.sort(([o],[s])=>o.localeCompare(s))}var Zt=["apiPath","basePath"];function se(e){if(!e||typeof e!="object"||Array.isArray(e))return[];let t=e;return Zt.filter(n=>n in t&&typeof t[n]!="string")}function ae(e,t){if(!e||typeof e!="object"||Array.isArray(e))return{errors:[`${t}: expected a JSON object`],warnings:[]};let n=e,r=[];for(let i of["appName","appType","stack","schemaVersion"])i in n||r.push(`${t}.${i}: required field is missing`);for(let i of["appName","appType","stack"])i in n&&typeof n[i]!="string"&&r.push(`${t}.${i}: expected a string`);"schemaVersion"in n&&n.schemaVersion!==Xe&&r.push(`${t}.schemaVersion: unsupported version ${String(n.schemaVersion)} (supported: ${Xe})`);for(let i of se(n))r.push(`${t}.${i}: expected a string`);let o=n.dev,s=o&&typeof o=="object"&&!Array.isArray(o)?o.liveReload:void 0;if(s!==void 0&&typeof s!="boolean"&&r.push(`${t}.dev.liveReload: expected true or false`),r.push(...L(n,t)),"smoke"in n)for(let i of re(n.smoke))r.push(`${t}.${i}`);let a=Object.keys(n).flatMap(i=>{let c=Kt[i];return c?[`${t}.${i}: ${c}`]:Ht.has(i)?[]:[`${t}.${i}: unknown field is preserved`]});return s===!0&&n.language!=="go"&&a.push(`${t}.dev.liveReload: only applies to Go services (language "go"); it is ignored here`),{errors:r,warnings:a,manifest:n}}function br(e,t){if(!et(e))return[];try{let n=JSON.parse(tt(e,"utf-8"));return!n||typeof n!="object"||Array.isArray(n)?[]:L(n,t)}catch{return[]}}function Tr(e,t){if(!et(e))return{errors:[`${t}: file is missing`],warnings:[]};try{let n=JSON.parse(tt(e,"utf-8"));return ae(n,t)}catch(n){let r=n instanceof Error?n.message:String(n);return{errors:[`${t}: invalid JSON (${r})`],warnings:[]}}}function nt(e){if(e.length===0||e.every(n=>n==="unknown"))return"unknown";let t=e.filter(n=>n==="ready").length/e.length;return t>.9?"healthy":t>.5?"degraded":"error"}var Qt=/^[a-z0-9-]+$/;function rt(e){return Qt.test(e)}var Xt="Use lowercase letters, numbers, and hyphens only";function ot(e,t){return`${e} "${t}" is not valid. ${Xt}, e.g. "${e==="Resource name"?"my-service":"my-stack"}".`}function en(e){return e.trim()?rt(e)?{valid:!0}:{valid:!1,error:ot("Resource name",e)}:{valid:!1,error:"Resource name is required"}}function tn(e){return e.trim()?rt(e)?{valid:!0}:{valid:!1,error:ot("Stack name",e)}:{valid:!1,error:"Stack name is required"}}function Or(e){let t=e==="resource"?en:tn;return n=>{let r=t(n);return r.valid?!0:r.error??"Invalid name"}}function Cr(e){return ie(K,e)?{valid:!0}:{valid:!1,error:`Invalid service. Must be one of: ${K.join(", ")}`}}function Dr(e){return Number.isInteger(e)&&e>0&&e<=65535}function Fr(e){return!e.includes("\0")&&!/[<>:"|?*]/.test(e)}function Nr(e,t="_"){return e.replace(/[^a-zA-Z0-9-]/g,t).substring(0,100)}function ie(e,t){return e.includes(t)}var sn=xe;function de(e,t=5,n=0){let r=[];if(n>t)return r;let o=st.readdirSync(e,{withFileTypes:!0});for(let s of o){let a=w(e,s.name);if(s.isDirectory()){if(an(s.name))continue;r.push(...de(a,t,n+1))}else s.isFile()&&s.name===sn&&r.push(a)}return r}function qr(e){return de(e)}function an(e){return ie(ve,e)||e.startsWith(".")}function cn(e){if(!e||typeof e!="object")return!1;let t=e,n=typeof t.runtime=="string"||typeof t.appType=="string"||typeof t.type=="string";return typeof t.appName=="string"&&n}var P=new Set;function Kr(){P.clear()}var k=class extends Error{reason;constructor(t){super(t),this.reason=t,this.name="InvalidServiceJsonError"}};function ue(e){return e instanceof Error?e.message:String(e)}function un(e){let t;try{t=st.readFileSync(e,"utf-8")}catch(c){throw new k(`could not read file: ${ue(c)}`)}let n;try{n=JSON.parse(t)}catch(c){throw new k(`invalid JSON: ${ue(c)}`)}if(!cn(n))throw new k("missing required fields (appName, runtime or appType)");let r=se(n);if(r.length>0)throw new k(r.map(c=>`${c}: expected a string`).join("; "));let o=L(n,e);if(o.length>0)throw new k(o.join("; "));let s=n;for(let c of ae(n,e).warnings)P.has(c)||(P.add(c),console.warn(`\u26A0\uFE0F ${c}`));let a={...s,appType:s.appType||s.type},i=on(e);return{name:a.appName,path:i,configPath:e,config:a,stack:a.stack,port:a.port,type:a.appType}}function at(e){let t=de(e),n=[],r=[];for(let o of t)try{n.push(un(o))}catch(s){r.push({path:o,reason:s instanceof k?s.reason:ue(s)})}return{resources:n,problems:r}}function fn(e){for(let t of e){let n=`skipped ${t.path}: ${t.reason}`;P.has(n)||(P.add(n),console.warn(`\u26A0\uFE0F ${n}`))}}function it(e){let{resources:t,problems:n}=at(e);return fn(n),t}function V(){let e=A();if(!e)throw H.notInProject();return it(e)}function Hr(e){let t=e;if(!t){let n=A();if(!n)throw H.notInProject();t=at(n)}if(t.problems.length>0)throw new Error(t.problems.map(n=>`Invalid service.json at ${n.path}: ${n.reason}`).join(`
|
|
15
|
+
`));return t.resources}function Jr(e,t){let n=t??it(e);return pn(n)}function pn(e){let t=e||V(),n=new Set;for(let r of t)r.stack&&n.add(r.stack);return Array.from(n).sort()}function Yr(e=V()){let t=new Map;for(let r of e)if(r.stack){let o=t.get(r.stack);o?o.push(r):t.set(r.stack,[r])}let n=[];for(let[r,o]of t)n.push({name:r,description:Re(o.length,"resource"),resources:o,resourceCount:o.length});return n.sort((r,o)=>r.name.localeCompare(o.name))}function Zr(e,t){return(t??V()).filter(r=>r.stack===e)}function Qr(e,t){return(t??V()).some(r=>r.stack===e)}var le=5e3,fe=new j({ttlMs:le}),pe=new j({ttlMs:le}),I=Te(le);function Xr(){fe.clear(),pe.clear(),I.reset()}function dn(e,t){if(e==="Dockerfile"||e.startsWith("docker-compose"))return"docker";if(e==="Tiltfile"||e.endsWith(".star"))return"tilt";if(e==="vite.config.ts"||e==="tsconfig.json"||e===we)return"config";if(e==="schema.prisma"||e.includes("migration"))return"prisma";if(t){let n=t.split(`
|
|
16
|
+
`).slice(0,10).join(`
|
|
17
|
+
`);if(n.includes("GENERATED BY Flisk")||n.includes("AUTOGENERATED"))return"generated"}return"unknown"}function ln(e){let t=e.configPath;if(I.isValid()){let l=fe.get(t);if(l)return l}let n=e.path,r=ce(e.configPath),o=r.mtime.toISOString(),s=r.mtime.toISOString(),a=mn(n),i=O(w(n,"Dockerfile")),c=O(w(n,"Tiltfile")),d=O(w(n,Ee))||O(w(n,"docker-compose.yaml")),p=e.config?.appType,u=p||"backend";p||(e.name.includes("frontend")?u="frontend":(e.name.includes("sdk")||e.name.includes("lib")||e.name.includes("infra"))&&(u="library"));let h={name:e.name,stack:e.stack,type:u,port:e.config?.port,createdAt:o,lastModified:s,dependencies:e.config?.dependsOn??[],hasDockerfile:i,hasTiltfile:c,hasDockerCompose:d,autogeneratedFiles:a,status:"unknown"};return fe.set(t,h),I.markUpdated(),h}function mn(e){let t=[];for(let r of be){let o=w(e,r);if(O(o)){let s=ce(o),a=rn(o,"utf-8");t.push({name:r,path:o,type:dn(r,a),size:s.size,lastModified:s.mtime.toISOString()})}}let n=w(e,"prisma");if(O(n)){let r=nn(n);for(let o of r)if(o==="schema.prisma"||o.includes("migration")){let s=w(n,o),a=ce(s);t.push({name:`prisma/${o}`,path:s,type:"prisma",size:a.size,lastModified:a.mtime.toISOString()})}}return t.sort((r,o)=>r.name.localeCompare(o.name))}function eo(e){let t=e.name;if(I.isValid()){let c=pe.get(t);if(c)return c}let n=e.resources.map(c=>ln(c)),r=n.map(c=>new Date(c.createdAt).getTime()),o=Math.min(...r),s=Math.max(...r),a=nt(n.map(c=>c.status)),i={name:e.name,resourceCount:n.length,createdAt:new Date(o).toISOString(),lastModified:new Date(s).toISOString(),resources:n,overallStatus:a};return pe.set(t,i),I.markUpdated(),i}export{Re as a,Sn as b,yn as c,$n as d,xn as e,En as f,kn as g,wn as h,vn as i,bn as j,Z as k,Tn as l,An as m,Rn as n,_n as o,On as p,Cn as q,Dn as r,Fn as s,b as t,_e as u,zn as v,R as w,Gn as x,qn as y,Kn as z,te as A,Dt as B,Be as C,Ie as D,or as E,B as F,jt as G,M as H,sr as I,ar as J,ir as K,cr as L,gr as M,Sr as N,yr as O,$r as P,xr as Q,Xe as R,vr as S,ae as T,br as U,Tr as V,nt as W,en as X,tn as Y,Or as Z,Cr as _,Dr as $,Fr as aa,Nr as ba,ie as ca,qr as da,Kr as ea,at as fa,fn as ga,it as ha,V as ia,Hr as ja,Jr as ka,pn as la,Yr as ma,Zr as na,Qr as oa,Xr as pa,ln as qa,eo as ra};
|