@flisk-sh/flisk 1.4.2 → 1.4.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/README.md +148 -49
  3. package/cli/dist/backend-languages/bun.js +5 -0
  4. package/cli/dist/backend-languages/go.js +3 -1
  5. package/cli/dist/cli.js +9 -0
  6. package/cli/dist/commands/completion.js +8 -1
  7. package/cli/dist/commands/config.js +130 -55
  8. package/cli/dist/commands/doctor.js +66 -27
  9. package/cli/dist/commands/down.js +33 -1
  10. package/cli/dist/commands/logs.js +39 -1
  11. package/cli/dist/commands/project.js +12 -0
  12. package/cli/dist/commands/resource.js +39 -11
  13. package/cli/dist/commands/status.js +57 -19
  14. package/cli/dist/commands/up.js +221 -18
  15. package/cli/dist/commands/upgrade.js +2 -2
  16. package/cli/dist/frontend-frameworks/next.js +197 -0
  17. package/cli/dist/frontend-frameworks/react.js +81 -12
  18. package/cli/dist/frontend-frameworks/registry.js +5 -4
  19. package/cli/dist/frontend-frameworks/vue.js +82 -12
  20. package/cli/dist/generator/resource-templates.js +2 -4
  21. package/cli/dist/generator/template-engine.js +5 -3
  22. package/cli/dist/utils/bun-requirement.js +7 -4
  23. package/cli/dist/utils/cold-preflight.js +55 -34
  24. package/cli/dist/utils/config-explain.js +197 -0
  25. package/cli/dist/utils/demo-reset.js +22 -0
  26. package/cli/dist/utils/discovery-paths.js +7 -1
  27. package/cli/dist/utils/doctor-native.js +96 -0
  28. package/cli/dist/utils/host-port-plan.js +8 -3
  29. package/cli/dist/utils/native-project.js +41 -0
  30. package/cli/dist/utils/native-runtime.js +513 -0
  31. package/cli/dist/utils/native-service.js +95 -0
  32. package/cli/dist/utils/native-up.js +156 -0
  33. package/cli/dist/utils/platform.js +1 -1
  34. package/cli/dist/utils/port-conflict-fix.js +16 -14
  35. package/cli/dist/utils/port-occupant.js +35 -0
  36. package/cli/dist/utils/project-features.js +7 -16
  37. package/cli/dist/utils/project-templates.js +9 -9
  38. package/cli/dist/utils/resource-features.js +5 -5
  39. package/cli/dist/utils/service-manifest.js +20 -3
  40. package/cli/dist/utils/service-urls.js +11 -0
  41. package/cli/dist/utils/services.js +15 -2
  42. package/cli/dist/utils/startup-report.js +4 -3
  43. package/cli/dist/utils/tar.js +33 -28
  44. package/cli/dist/utils/tilt-logs.js +8 -0
  45. package/cli/dist/utils/tilt-process.js +4 -3
  46. package/cli/dist/utils/windows-doctor.js +18 -15
  47. package/cli/dist-bundle/chunk-BIVAAWBX.js +24 -0
  48. package/cli/dist-bundle/{chunk-YNCBBJ4V.js → chunk-C4UFBXRD.js} +1 -1
  49. package/cli/dist-bundle/{chunk-53L2JN7G.js → chunk-CAEMWNPF.js} +1 -1
  50. package/cli/dist-bundle/chunk-CDKEHFW6.js +35 -0
  51. package/cli/dist-bundle/{chunk-5QBYM33Q.js → chunk-DPQK5TIS.js} +1 -1
  52. package/cli/dist-bundle/chunk-ETXZ5LUG.js +18 -0
  53. package/cli/dist-bundle/chunk-GPUJ55MI.js +3 -0
  54. package/cli/dist-bundle/chunk-GT76VMUL.js +130 -0
  55. package/cli/dist-bundle/{chunk-IGN5UC3P.js → chunk-IDNES5PE.js} +14 -7
  56. package/cli/dist-bundle/{chunk-MWOVYIBB.js → chunk-MUDE4DZD.js} +5 -5
  57. package/cli/dist-bundle/chunk-QEBN6ZVQ.js +2 -0
  58. package/cli/dist-bundle/{chunk-I7TGIK5N.js → chunk-R7DWXISM.js} +1 -1
  59. package/cli/dist-bundle/{chunk-DU2BN46C.js → chunk-U5FCQF42.js} +1 -1
  60. package/cli/dist-bundle/{chunk-3XODDLRJ.js → chunk-WCGBVETR.js} +1 -1
  61. package/cli/dist-bundle/cli.js +626 -389
  62. package/cli/dist-bundle/cold-preflight-KUWOL2Q2.js +2 -0
  63. package/cli/dist-bundle/{demo-stage-OHNTP2UJ.js → demo-stage-QCC4IWXR.js} +1 -1
  64. package/cli/dist-bundle/{extension-fetch-VEFSCG4B.js → extension-fetch-KIPYDIRM.js} +1 -1
  65. package/cli/dist-bundle/{open-XROJ6YEY.js → open-AZNX4AC5.js} +1 -1
  66. package/cli/dist-bundle/project-5SWIGCNP.js +2 -0
  67. package/cli/dist-bundle/{project-templates-EYSQG2EO.js → project-templates-2VQPUBSQ.js} +1 -1
  68. package/cli/dist-bundle/{services-BIMF6MVU.js → services-EY5WKPHQ.js} +1 -1
  69. package/cli/dist-bundle/{tilt-port-owner-ZY5M2JC4.js → tilt-port-owner-UBU6JWOA.js} +1 -1
  70. package/cli/package.json +1 -1
  71. package/examples/{tdk-example → flisk-example}/README.md +2 -2
  72. package/package.json +2 -2
  73. package/runtime/discovery/AGENTS.md +1 -1
  74. package/runtime/discovery/TILT_DISCOVERY.star +1 -1
  75. package/runtime/discovery/deterministic.star +1 -1
  76. package/runtime/engine/AGENTS.md +3 -3
  77. package/runtime/engine/README.md +1 -1
  78. package/runtime/engine/REFACTORING_MANIFEST_SYSTEM.md +1 -1
  79. package/runtime/engine/assets/docker/wake-gateway/decide.ts +1 -1
  80. package/runtime/engine/assets/docker/wake-gateway/server.ts +2 -2
  81. package/runtime/engine/assets/scripts/mac-m1-resource-monitor.sh +6 -6
  82. package/runtime/engine/schemas/service-schema.json +99 -21
  83. package/runtime/engine/scripts/debug-container.sh +1 -1
  84. package/runtime/engine/scripts/generate-manifest.sh +2 -2
  85. package/runtime/engine/topologies/platform/docker/compose/compose.star +40 -4
  86. package/runtime/engine/topologies/platform/docker/compose/traefik_standalone.star +19 -4
  87. package/runtime/engine/topologies/platform/docker/constants.star +2 -2
  88. package/runtime/engine/topologies/platform/docker/dockerfile/language_dockerfile.star +6 -0
  89. package/runtime/engine/topologies/platform/docker/dockerfile/next_dockerfile.star +83 -0
  90. package/runtime/engine/topologies/platform/docker/index.star +4 -0
  91. package/runtime/engine/topologies/platform/docker/layers/prisma/filesystem_setup.star +7 -0
  92. package/runtime/engine/topologies/platform/docker/networking/traefik.star +85 -46
  93. package/runtime/engine/topologies/platform/docker/networking/traefik_static_routes.star +2 -2
  94. package/runtime/engine/topologies/tilt/common/utils_output.star +1 -0
  95. package/runtime/engine/topologies/tilt/generators/validators.star +1 -1
  96. package/runtime/engine/topologies/tilt/generators/vite/frontend.star +2 -2
  97. package/runtime/engine/topologies/tilt/generators/vite/generators.star +3 -0
  98. package/runtime/engine/topologies/tilt/manifest/constants.star +3 -0
  99. package/runtime/engine/topologies/tilt/manifest/mapper.star +1 -1
  100. package/runtime/engine/topologies/tilt/manifest/parser.star +9 -3
  101. package/runtime/engine/topologies/tilt/resources/orchestrator/apply_compose_resource_registration.star +9 -4
  102. package/runtime/engine/topologies/tilt/resources/orchestrator/generators/dependencies/resolver.star +50 -24
  103. package/runtime/engine/topologies/tilt/resources/orchestrator/generators/env.star +11 -2
  104. package/runtime/engine/topologies/tilt/resources/orchestrator/generators/manifest_resource.star +23 -9
  105. package/runtime/ext/ide-components/README.md +1 -1
  106. package/runtime/ext/ui-enhancements/extension.yaml +2 -2
  107. package/runtime/specs/specs/TILT_SERVICE_DEFAULTS.star +1 -1
  108. package/runtime/specs/specs/TILT_TECH_STACK.star +1 -1
  109. package/cli/dist-bundle/chunk-43XWL2PB.js +0 -61
  110. package/cli/dist-bundle/chunk-5F7YP5IR.js +0 -17
  111. package/cli/dist-bundle/chunk-ETKUVKJ2.js +0 -35
  112. package/cli/dist-bundle/chunk-K6SOCO6V.js +0 -3
  113. package/cli/dist-bundle/chunk-YHYRGWUH.js +0 -24
  114. package/cli/dist-bundle/chunk-YL56J2WX.js +0 -2
  115. package/cli/dist-bundle/cold-preflight-ZOPR7PWJ.js +0 -2
  116. package/cli/dist-bundle/project-7PTIOHGF.js +0 -2
  117. /package/examples/{tdk-example → flisk-example}/LICENSE +0 -0
  118. /package/examples/{tdk-example → flisk-example}/TILT_SERVICE_DEFAULTS.star +0 -0
  119. /package/examples/{tdk-example → flisk-example}/TILT_TECH_STACK.star +0 -0
  120. /package/examples/{tdk-example → flisk-example}/Tiltfile +0 -0
  121. /package/examples/{tdk-example → flisk-example}/services/platform/messaging/docker-compose.yml +0 -0
  122. /package/examples/{tdk-example → flisk-example}/services/shop/orders-api/package.json +0 -0
  123. /package/examples/{tdk-example → flisk-example}/services/shop/orders-api/service.json +0 -0
  124. /package/examples/{tdk-example → flisk-example}/services/shop/orders-api/src/index.ts +0 -0
  125. /package/examples/{tdk-example → flisk-example}/services/shop/orders-app/index.html +0 -0
  126. /package/examples/{tdk-example → flisk-example}/services/shop/orders-app/package.json +0 -0
  127. /package/examples/{tdk-example → flisk-example}/services/shop/orders-app/public/.gitkeep +0 -0
  128. /package/examples/{tdk-example → flisk-example}/services/shop/orders-app/service.json +0 -0
  129. /package/examples/{tdk-example → flisk-example}/services/shop/orders-app/src/main.tsx +0 -0
  130. /package/examples/{tdk-example → flisk-example}/services/shop/orders-app/src/orders.tsx +0 -0
  131. /package/examples/{tdk-example → flisk-example}/services/shop/orders-app/src/style.css +0 -0
  132. /package/examples/{tdk-example → flisk-example}/services/shop/orders-worker/package.json +0 -0
  133. /package/examples/{tdk-example → flisk-example}/services/shop/orders-worker/service.json +0 -0
  134. /package/examples/{tdk-example → flisk-example}/services/shop/orders-worker/src/index.ts +0 -0
@@ -16,6 +16,7 @@ OUTPUT = struct(AUTOGENERATED_FOLDER = ".autogenerated")
16
16
 
17
17
  load("../../../../platform/docker/auth/auth.star", "AuthConfig")
18
18
  load("../../../../platform/docker/networking/traefik_helpers.star", "get_api_path")
19
+ load("./dependencies/resolver.star", "resolve_backend_api_path")
19
20
  load("../../../../platform/docker/networking/api_path_constants.star", "get_frontend_base_path")
20
21
  load("../../../../platform/docker/constants.star", "PlatformDockerConstants")
21
22
 
@@ -154,9 +155,11 @@ def _generate_params_env(manifest, backend_manifest=None, write_fn=None):
154
155
  backend_port = backend_manifest.get('port', BASE_PORT_BACKEND) if backend_manifest else BASE_PORT_BACKEND
155
156
  backend_name = manifest.get('backendName', stack + '-backend')
156
157
  target_filename = '/' + OUTPUT.AUTOGENERATED_FOLDER + '/.env.frontend.autogenerated'
158
+ # The browser calls the frontend's own backend; the stack-management route only serves flisk's own API.
159
+ frontend_api_url = 'http://' + PlatformDockerConstants.API_LOCAL_DOMAIN + (resolve_backend_api_path(manifest) or api_path)
157
160
 
158
161
  # Shared with the Traefik route and the Vite `base` -- see get_frontend_base_path.
159
- base_path = get_frontend_base_path(manifest)
162
+ base_path = get_frontend_base_path(manifest, manifest.get('appName'))
160
163
  public_url = 'http://' + PlatformDockerConstants.LOCAL_DOMAIN + base_path
161
164
 
162
165
  content = """# Generated by Tilt SDK - DO NOT EDIT
@@ -167,10 +170,16 @@ VITE_BACKEND_NAME={backend_name}
167
170
  PUBLIC_URL={public_url}
168
171
  """.format(
169
172
  backend_port=backend_port,
170
- api_url=api_url,
173
+ api_url=frontend_api_url,
171
174
  backend_name=backend_name,
172
175
  public_url=public_url,
173
176
  )
177
+ if manifest.get('framework') == 'next':
178
+ # Next.js reads NEXT_PUBLIC_* (inlined into the browser bundle) and next.config.mjs reads FLISK_BASE_PATH.
179
+ # The generated Dockerfile copies this file to .env, which `next dev` and `next build` both load.
180
+ content += """NEXT_PUBLIC_API_URL={api_url}
181
+ FLISK_BASE_PATH={base_path}
182
+ """.format(api_url=frontend_api_url, base_path=base_path)
174
183
  content += _build_dependency_env_lines(manifest)
175
184
  else:
176
185
  db_name = manifest.get('databaseName', PlatformDockerConstants.get_db_name(stack))
@@ -132,9 +132,12 @@ def _build_generated_file_list(resource_config, resource_path, manifest):
132
132
  if app_type == 'bring-your-own':
133
133
  return []
134
134
  is_frontend = resource_config.get('frontend', False) or app_type == 'frontend'
135
+ is_next = is_frontend and manifest.get('framework') == 'next'
135
136
 
136
137
  # Vite
137
- if is_frontend:
138
+ if is_next:
139
+ pass
140
+ elif is_frontend:
138
141
  files.append(resource_path + '/' + OUTPUT.AUTOGENERATED_FOLDER + '/' + GENERATED_CONFIG_FILENAMES['vite_frontend'])
139
142
  files.append(resource_path + '/' + OUTPUT.AUTOGENERATED_FOLDER + '/' + GENERATED_CONFIG_FILENAMES['vite_build'])
140
143
  elif app_type == 'backend':
@@ -143,7 +146,7 @@ def _build_generated_file_list(resource_config, resource_path, manifest):
143
146
  files.append(resource_path + '/' + OUTPUT.AUTOGENERATED_FOLDER + '/' + GENERATED_CONFIG_FILENAMES['vite_sdk'])
144
147
 
145
148
  # Nginx (frontends)
146
- if is_frontend:
149
+ if is_frontend and not is_next:
147
150
  files.append(resource_path + '/' + OUTPUT.AUTOGENERATED_FOLDER + '/' + GENERATED_CONFIG_FILENAMES['nginx'])
148
151
 
149
152
  # Env
@@ -159,9 +162,10 @@ def _build_generated_file_list(resource_config, resource_path, manifest):
159
162
  dockerfile_path = resource_path + "/" + dockerfile_rel_path
160
163
  files.append(dockerfile_path + '.dockerignore')
161
164
 
162
- # TS config(s)
163
- files.append(resource_path + '/' + OUTPUT.AUTOGENERATED_FOLDER + '/' + GENERATED_CONFIG_FILENAMES['tsconfig_docker'])
164
- files.append(resource_path + '/' + OUTPUT.AUTOGENERATED_FOLDER + '/' + GENERATED_CONFIG_FILENAMES['tsconfig_local'])
165
+ # TS config(s): Next.js uses the app's own tsconfig.json
166
+ if not is_next:
167
+ files.append(resource_path + '/' + OUTPUT.AUTOGENERATED_FOLDER + '/' + GENERATED_CONFIG_FILENAMES['tsconfig_docker'])
168
+ files.append(resource_path + '/' + OUTPUT.AUTOGENERATED_FOLDER + '/' + GENERATED_CONFIG_FILENAMES['tsconfig_local'])
165
169
 
166
170
  # Prisma config (non-frontends)
167
171
  if not is_frontend:
@@ -313,6 +317,8 @@ def _generate_all_configs_for_resource(
313
317
 
314
318
  app_type = manifest.get('appType', 'backend')
315
319
  is_frontend = resource_config.get('frontend', False) or app_type == 'frontend'
320
+ # Next.js is a server framework: it owns its build and dev server, so the SPA outputs (Vite, nginx, api-client, tsconfig) are skipped.
321
+ is_next = is_frontend and manifest.get('framework') == 'next'
316
322
  # Provider-owned backends (Python, Go) own their runtime files (package/pyproject, Dockerfile,
317
323
  # reload command). Bun-only configs below (Vite, tsconfig, Prisma, bunfig) are skipped.
318
324
  provider_owned = Docker.is_provider_owned_language(manifest)
@@ -342,7 +348,7 @@ def _generate_all_configs_for_resource(
342
348
  # ==========================================================================
343
349
  # 2. NGINX CONFIG (for frontends)
344
350
  # ==========================================================================
345
- if is_frontend:
351
+ if is_frontend and not is_next:
346
352
  # Generate referer map using ApiGateway and pass to Nginx
347
353
  referer_map_block = ApiGateway.build_referer_map_block(manifest)
348
354
  Nginx.frontend(manifest, write_file, referer_map_block)
@@ -355,7 +361,7 @@ def _generate_all_configs_for_resource(
355
361
  # ==========================================================================
356
362
  # 3.5 FRONTEND API CLIENT FILES (for frontends only)
357
363
  # ==========================================================================
358
- if is_frontend:
364
+ if is_frontend and not is_next:
359
365
  FrontendGenerators.generate_all(manifest, write_file)
360
366
 
361
367
  # ==========================================================================
@@ -368,7 +374,13 @@ def _generate_all_configs_for_resource(
368
374
  # ==========================================================================
369
375
  # 5. DOCKERFILE
370
376
  # ==========================================================================
371
- if is_frontend:
377
+ if is_next:
378
+ dockerfile_content = Docker.next_frontend(
379
+ res_path=resource_path,
380
+ port=resource_config.get('port', manifest.get('port', BASE_PORT_FRONTEND)),
381
+ use_golden=should_enable('golden-image'),
382
+ )
383
+ elif is_frontend:
372
384
  target_path = resource_config.get('target_path', '/usr/share/nginx/html')
373
385
  use_nginx = 'nginx' in target_path or target_path == '/usr/share/nginx/html'
374
386
  port = resource_config.get('port', manifest.get('port', BASE_PORT_FRONTEND))
@@ -425,7 +437,9 @@ def _generate_all_configs_for_resource(
425
437
  # ==========================================================================
426
438
  internal_deps_map = Libs.get_deps_mapping(ctx, resource_path)
427
439
 
428
- if is_frontend:
440
+ if is_next:
441
+ pass
442
+ elif is_frontend:
429
443
  # CRITICAL FIX (v2.0): For Docker builds, we MUST NOT pass internal_deps
430
444
  # This prevents "Module not found" errors when path aliases point to
431
445
  # source files outside the Docker context.
@@ -1,6 +1,6 @@
1
1
  # Tilt IDE Components
2
2
 
3
- Web-based IDE features integrated into the Flisk Landscape Tilt development environment.
3
+ Web-based IDE features integrated into the Flisk Tilt development environment.
4
4
 
5
5
  ## Overview
6
6
 
@@ -1,9 +1,9 @@
1
- # UI Enhancements Extension for Flisk Landscape Tilt IDE
1
+ # UI Enhancements Extension for Flisk Tilt IDE
2
2
  # Provides tooltips, icons, help panels, and cron jobs monitoring
3
3
 
4
4
  name: ui-enhancements
5
5
  version: 1.0.0
6
- author: Flisk Landscape Platform Team
6
+ author: Flisk Platform Team
7
7
  description: |
8
8
  Enhances the Tilt web UI with:
9
9
  - Contextual tooltips for all resources
@@ -6,7 +6,7 @@
6
6
  # 🔧 TILT SERVICE DEFAULTS MASTER CONFIG
7
7
  # =============================================================================
8
8
  #
9
- # Defines standard configurations for all services in the Flisk Landscape platform.
9
+ # Defines standard configurations for all services in the Flisk platform.
10
10
  # This provides consistent port allocation, health checks, and build settings.
11
11
  #
12
12
  # See: docs/TILT_MASTER_CONFIGS.md
@@ -6,7 +6,7 @@
6
6
  # 🏗️ TILT TECH STACK MASTER CONFIG
7
7
  # =============================================================================
8
8
  #
9
- # Defines the approved technology choices for the Flisk Landscape platform.
9
+ # Defines the approved technology choices for the Flisk platform.
10
10
  # This is the single source of truth for tech stack validation.
11
11
  #
12
12
  # See: docs/TILT_MASTER_CONFIGS.md
@@ -1,61 +0,0 @@
1
- import{createRequire as __fliskRequire}from'node:module';const require=__fliskRequire(import.meta.url);
2
- import{o as mt,u as ht}from"./chunk-MWOVYIBB.js";import{f as O,g as gt,h as kt,i as St,j as se,k as yt,l as wt,m as xt,n as Pt,o as bt,p as ne,q as vt,s as Dt,t as $t}from"./chunk-IGN5UC3P.js";import{c as ft}from"./chunk-K6SOCO6V.js";import{b as W,c as Ct,d as K}from"./chunk-3J6XI3NX.js";import{a as dt,f as pt,g as te,j as Nt,k as Tt,l as Et,m as Rt,o as Ot,p as be,q as ve,r as _t,s as De,t as $e,u as jt,v as Ce,w as At}from"./chunk-YHYRGWUH.js";import{A as rt,B as we,C as Q,D as ot,E as Z,I as xe,J as it,K as ee,L as at,M as ct,O as lt,P as ut,Q as Pe,a as C,ga as w,t as st,x as nt}from"./chunk-5F7YP5IR.js";import{C as b,D as et,L as tt,b as h,d as v,j as A,m as H,n as G,o as Se,q as ye,x as g,y as Ze}from"./chunk-YNCBBJ4V.js";import{execFileSync as _s}from"node:child_process";import{readFileSync as Jr}from"node:fs";import{join as zr}from"node:path";import{execFileSync as sr,execSync as gs}from"node:child_process";import{existsSync as $,readdirSync as xs,readFileSync as B}from"node:fs";import{join as x,normalize as pe,relative as fe}from"node:path";import{Command as nr}from"commander";import{existsSync as Ms}from"node:fs";var re="docs/agent-hosts.md",Ne=`WebContainers cannot run Flisk: there is no Docker daemon or Tilt. Run \`flisk up\` on a machine with Docker. See ${re}.`,Ft=`Docker is not reachable from this Dev Container. Mount the host Docker socket (-v /var/run/docker.sock:/var/run/docker.sock) or add the docker-outside-of-docker feature to devcontainer.json, then retry. Docker-in-docker is not supported. See ${re}.`;function Hs(){return{env:process.env,versions:process.versions,platform:process.platform,fileExists:Ms}}function It(e=Hs()){let{env:t,versions:s,platform:n,fileExists:r}=e;return s.webcontainer?{kind:"webcontainer",canUp:!1}:t.CODESPACES==="true"?{kind:"codespaces",canUp:!0}:t.REMOTE_CONTAINERS||r("/.dockerenv")&&t.DEVCONTAINER?{kind:"devcontainer",canUp:!0}:n==="win32"?{kind:"native-windows",canUp:t.FLISK_ALLOW_NATIVE_WINDOWS==="1"}:n==="linux"&&t.WSL_DISTRO_NAME?{kind:"wsl2",canUp:!0}:{kind:"local",canUp:!0}}function Lt(e){return e==="devcontainer"||e==="codespaces"}function oe(e){return e.some(t=>st(t.config?.appType)||t.config?.appType==="frontend"||t.config?.appType==="worker")}import{existsSync as Ws,readFileSync as Bs}from"node:fs";import{join as Vs,relative as Mt,resolve as Ht,sep as Us}from"node:path";var Gs=2,Wt=["services/*/*"];function Bt(e){return e.split(Us).filter(Boolean)}function Ks(e,t){let s=0,n=0,r=-1,o=0;for(;n<t.length;)if(s<e.length&&e[s]!=="*"&&(e[s]==="?"||e[s]===t[n]))s++,n++;else if(s<e.length&&e[s]==="*")r=s++,o=n;else if(r!==-1)s=r+1,n=++o;else return!1;for(;s<e.length&&e[s]==="*";)s++;return s===e.length}function qs(e,t){return e.length===t.length&&e.every((s,n)=>Ks(s,t[n]))}function Te(e){let t=Vs(e,".flisk",H);if(!Ws(t))return Wt;try{let s=JSON.parse(Bs(t,"utf-8"))?.discovery?.paths;if(Array.isArray(s)&&s.every(n=>typeof n=="string"))return s}catch{}return Wt}function ie(e,t,s){let n=Bt(Mt(e,Ht(e,t)));return s.some(r=>{let o=Bt(Mt(e,Ht(e,r)));for(let i=0;i<=Gs;i++){let a=n.length-i;if(a>0&&qs(o,n.slice(0,a)))return!0}return!1})}function lo(e,t,s,n){let r=Te(e);if(ie(e,t,r))return{path:t};let o=`services/${s}/${n}`;return ie(e,o,r)?{path:o,adjustedFrom:t}:{path:t}}import{execFile as Js}from"node:child_process";import{promisify as zs}from"node:util";var Xs=zs(Js),Ys=1e4,Qs=5,Vt=10*6e4,Zs=3,en=200,tn=async e=>{let{stdout:t,stderr:s}=await Xs("docker",e,{encoding:"utf-8",timeout:Ys,windowsHide:!0});return`${t}${s}`};function sn(e,t){let s=Nt(t).toLowerCase(),n=e.replace(/-/g,"_").toLowerCase();return n===s||n.startsWith(`${s}_`)}function nn(e,t=Date.now()){if(e.status==="restarting")return!0;if(e.restartCount<Qs)return!1;if(e.status==="running"){let n=Date.parse(e.startedAt);return Number.isNaN(n)||t-n<Vt}let s=Date.parse(e.finishedAt??"");return e.exitCode!==0&&!Number.isNaN(s)&&t-s<Vt}function rn(e,t){let s=/database "([^"]+)" does not exist/i.exec(e);return s?`database "${s[1]}" does not exist: re-run the project's database provisioning (flisk up) or create it`:/Cannot find module|MODULE_NOT_FOUND|module not found/i.test(e)?"a module is missing from the image: rebuild it (flisk up --force) after checking the resource's dependencies":/EADDRINUSE|address already in use/i.test(e)?"the port inside the container is already in use: check for two processes started by the same command":/ECONNREFUSED|connection refused|could not connect/i.test(e)?"a dependency is not reachable: check that the service it connects to is running and healthy":/\b(?:environment variable|env var)\b.*\b(?:missing|not set|required|undefined)\b/i.test(e)||/\b[A-Z][A-Z0-9_]{2,}\b (?:is )?(?:not set|required|missing)\b/.test(e)?"a required environment variable is missing: check the project's .env":t.exitCode===137?"killed (exit 137), usually out of memory: raise the container's memory limit":null}function on(e){let t=[];for(let s of e.split(/\r?\n/)){let[n,r,o,i,a,c]=s.split(" ");!n||r===void 0||t.push({name:n.replace(/^\//,""),restartCount:Number(r)||0,status:o??"",startedAt:i??"",exitCode:Number(a)||0,finishedAt:c??""})}return t}async function Ut(e,t=[],s=tn,n=Date.now()){let r="Crash-looping containers",o;try{o=(await s(["ps","-a","--format",'{{.ID}} {{.Label "com.docker.compose.project"}}'])).split(/\r?\n/).map(m=>m.split(" ")).filter(([m,d])=>m&&d&&sn(d,e)).map(([m])=>m)}catch{return{name:r,didPass:!0,isSkipped:!0,message:"Docker not available - skipped crash-loop check"}}if(o.length===0)return{name:r,didPass:!0,isSkipped:!0,message:"No project containers to check"};let i;try{i=on(await s(["inspect","--format","{{.Name}} {{.RestartCount}} {{.State.Status}} {{.State.StartedAt}} {{.State.ExitCode}} {{.State.FinishedAt}}",...o]))}catch{return{name:r,didPass:!0,isSkipped:!0,message:"Containers changed while checking - skipped crash-loop check"}}let a=i.filter(f=>nn(f,n));if(a.length===0)return{name:r,didPass:!0,message:"No project container is crash-looping"};let c=[],l=new Set;for(let f of a){let m="";try{m=await s(["logs","--tail",String(Zs),f.name])}catch{}let d=Q(m,t).split(/\r?\n/).map(k=>k.trim().slice(0,en)).filter(Boolean),S=rn(m,f);S&&l.add(S),c.push([`${f.name}: ${f.status}, restarted ${f.restartCount} times`,...d.map(k=>` | ${k}`)].join(`
3
- `))}return{name:r,didPass:!1,message:`${a.length} container${a.length===1?" is":"s are"} crash-looping:
4
- ${c.join(`
5
- `)}`,fix:[...l,"Read the full log: docker logs <container>"].join("; ")}}import{execFile as yn,execFileSync as wn}from"node:child_process";import{existsSync as xn,realpathSync as Xt,statfsSync as Pn}from"node:fs";import{homedir as bn}from"node:os";import{join as vn}from"node:path";import{promisify as Dn}from"node:util";import{statfsSync as an}from"node:fs";import{homedir as cn}from"node:os";var ae="Disk Space",ln=1024**3,un=2,Ee=10,Gt=["Free disk space, then retry: flisk doctor"," docker system prune -af # unused images and build cache (needs Docker running)"," npm cache clean --force"," bun pm cache rm"," brew cleanup -s"].join(`
6
- `);function Kt(e=an,t=cn()){let s;try{let r=e(t);s=Number(r.bavail)*Number(r.bsize)/ln}catch{return{name:ae,didPass:!0,isSkipped:!0,message:"Could not read free disk space"}}let n=`${s.toFixed(1)} GB free`;return s<un?{name:ae,didPass:!1,message:`${n}: Docker cannot build or start containers`,fix:Gt}:s<Ee?{name:ae,didPass:!1,isWarning:!0,message:`${n}: image builds may fail`,fix:Gt}:{name:ae,didPass:!0,message:n}}var dn="Quit Docker Desktop completely (whale icon > Quit Docker Desktop), reopen it, and wait until it shows Running. If it keeps getting stuck, check free disk space. Then retry: flisk doctor";function qt(e,t=process.platform){return t!=="darwin"||!e?null:{name:"Container Runtime",didPass:!1,message:"Docker Desktop is open, but its engine is not responding",fix:dn}}import{execFileSync as pn}from"node:child_process";var Re=(e,t)=>pn(e,t,{encoding:"utf-8",stdio:"pipe",windowsHide:!0}),Jt=["traefik-public","traefik-private","backend","database","infisical-network","verdaccio-network","proxy"];function Oe(e){let t=e.replace(/-/g,"_");return Jt.map(s=>`${t}_${s}`)}function zt(e){let t=new Set;for(let s of e)for(let n of["traefik-public","traefik-private"])s.endsWith(`_${n}`)&&t.add(s.slice(0,-(n.length+1)));return t}function _e(e,t){for(let s of Jt)if(e.endsWith(`_${s}`)){let n=e.slice(0,-(s.length+1));if(t.has(n))return n}return null}function xo(e,t=Re){let s=new Set(t("docker",["network","ls","--format","{{.Name}}"]).split(/\r?\n/).map(r=>r.trim()).filter(Boolean)),n={removed:[],kept:[]};for(let r of Oe(e))if(s.has(r))try{t("docker",["network","rm",r]),n.removed.push(r)}catch(o){let i=String(o.stderr??o).trim();n.kept.push({name:r,reason:i.split(/\r?\n/)[0]??""})}return n}function ce(e,t=process.platform,s=Re){if(t==="win32")return[];let n;try{n=s("ps",["-axo","pid=,command="])}catch{return[]}let r=[];for(let o of n.split(/\r?\n/)){let i=/^\s*(\d+)\s+(.*)$/.exec(o);if(!i)continue;let a=i[2]??"",c=(a.split(/\s+/)[0]??"").split("/").at(-1),l=` -f ${e}`,f=a.indexOf(l),m=a[f+l.length];c==="tilt"&&/^\S+\s+up\s/.test(a)&&f>0&&(m===void 0||m===" ")&&r.push(Number(i[1]))}return r}function Po(e,t=process.platform,s=Re){let n=ce(e,t,s);for(let r of n)try{s("kill",["-TERM",String(r)])}catch{}return n}async function bo(e,t=8e3,s=ce){let n=Date.now()+t;for(;s(e).length>0;){if(Date.now()>=n)return!1;await new Promise(r=>setTimeout(r,250))}return!0}import{execFile as fn}from"node:child_process";function je(e){return new Promise(t=>{fn(b("tilt")??"tilt",["get","uiresources","-o","json","--port",String(e)],{timeout:15e3,maxBuffer:16*1024*1024},(s,n)=>t(s?null:n))})}function mn(e){let s=(JSON.parse(e).items??[]).filter(n=>n.status?.disableStatus?.state!=="Disabled").map(n=>n.status?.updateStatus==="not_applicable"?{...n,status:{...n.status,updateStatus:"ok"}}:n);return JSON.stringify({items:s})}function hn(e){return(JSON.parse(e).items??[]).filter(t=>t.status?.disableStatus?.state!=="Disabled").flatMap(t=>t.metadata?.name?[t.metadata.name]:[])}function gn(e,t){let s=JSON.parse(e).items??[],n=new Map(s.map(r=>[r.metadata?.name,r]));return{missing:t.filter(r=>!n.has(r)),disabled:t.filter(r=>n.get(r)?.status?.disableStatus?.state==="Disabled")}}function kn(e){return e.endsWith("-typecheck")}function Sn(e,t,s){let n=Rt(mn(e),t),r=n.failures.filter(i=>!kn(i.name)),o={ready:r.length===0&&n.pendingCount===0&&n.okCount>0,failures:r.map(i=>({name:i.name,message:i.error||`${i.updateStatus}/${i.runtimeStatus}`})),pending:n.pendingCount,timedOut:!1,enabled:hn(e)};if(s&&s.length>0){let{missing:i,disabled:a}=gn(e,s);if(i.length>0&&(o={...o,ready:!1,pending:o.pending+i.length}),a.length>0)return{settled:!0,result:{...o,ready:!1,failures:[...o.failures,...a.map(c=>({name:c,message:"disabled in Tilt"}))]}}}return{result:o,settled:o.ready||o.failures.length>0&&o.pending===0}}async function No(e,t={}){let s=t.timeoutMs??Number(process.env.FLISK_UP_READY_TIMEOUT_MS??9e5),n=t.intervalMs??2e3,r=t.fetchJson??je,o=t.deferred??Et(),i=Date.now()+s,a={ready:!1,failures:[],pending:0,timedOut:!1,enabled:[]};for(;;){let c=await r(e);if(c)try{let l=Sn(c,o,t.expected);if(a=l.result,l.settled||a.failures.length>0&&t.isStalled?.(c))return a}catch{}if(Date.now()>=i)return{...a,ready:!1,timedOut:!0};await new Promise(l=>setTimeout(l,n))}}var I=Dn(yn),$n=15e3,Cn=/^flisk_doctor_probe_(\d+)$/,Nn=async e=>{let{stdout:t}=await I("docker",e,{encoding:"utf-8",timeout:$n,windowsHide:!0});return t};function Tn(){try{return wn("docker",["info","--format","{{.ServerVersion}}"],{encoding:"utf-8",timeout:15e3,stdio:["ignore","pipe","ignore"]}),!0}catch{return!1}}function le(e){return String(e.stderr??e).trim().split(/\r?\n/)[0]??""}var En=120;function Rn(e){let t=/^\s*(?:(?:(\d+)-)?(\d+):)?(\d+):(\d+)\s*$/.exec(e);if(!t)return null;let[,s="0",n="0",r="0",o="0"]=t;return((Number(s)*24+Number(n))*60+Number(r))*60+Number(o)}async function On(e,t=je,s=2,n=r=>new Promise(o=>setTimeout(o,r))){for(let r=0;r<s;r++){r>0&&await n(1e3);for(let o of e)if(await t(o)!==null)return!0}return!1}async function _n(e){if(process.platform==="win32")return"unknown";try{let{stdout:t}=await I("ps",["-p",String(e),"-o","etime="],{encoding:"utf-8",timeout:5e3}),s=Rn(t);if(s===null||s<En)return"unknown";let{stdout:n}=await I("lsof",["-nP","-a","-p",String(e),"-iTCP","-sTCP:LISTEN","-Fn"],{encoding:"utf-8",timeout:5e3}),r=[...n.matchAll(/^n.*:(\d+)$/gm)].map(o=>Number(o[1]));return await On(r)?"healthy":"stale"}catch{return"unknown"}}function jn(e=Nn){return{colima:An,colimaStatus:()=>W(),dockerAnswers:Tn,async listNetworks(){let t=(await e(["network","ls","--format","{{.Name}}"])).split(/\r?\n/).map(n=>n.trim()).filter(Boolean);return(await Promise.all(t.map(async n=>{try{let r=await e(["network","inspect","--format","{{len .Containers}}",n]);return{name:n,containers:Number(r.trim())||0}}catch(r){if(/no such network|not found/i.test(String(r)))return null;throw r}}))).filter(n=>n!==null)},async removeNetwork(t){try{await e(["network","rm",t])}catch(s){throw new Error(le(s))}},freeDiskGib(){try{let t=Pn(bn());return Number(t.bavail)*Number(t.bsize)/1024**3}catch{return null}},async reclaimable(){let t=null;try{let n=await e(["system","df","--format","{{.Type}} {{.Reclaimable}}"]);for(let r of n.split(/\r?\n/)){let[o,i]=r.split(" ");o==="Build Cache"&&i&&(t=i.replace(/\s*\(.*\)/,"").trim())}}catch{}let s=0;try{s=(await e(["images","-q","-f","dangling=true"])).split(/\r?\n/).filter(Boolean).length}catch{}return{buildCache:t,danglingImages:s}},async prune(t){try{await e(t==="builder"?["builder","prune","-f"]:["image","prune","-f"])}catch(s){throw new Error(le(s))}},async tiltStatus(t){return _n(t)},stopProcess(t){process.kill(t,"SIGTERM")},isProcessAlive(t){try{return process.kill(t,0),!0}catch(s){return s.code==="EPERM"}}}}var Yt=jn();async function An(e){if(e==="native-lima"){let n=K();if(!n){let o=xn("/opt/homebrew/bin/brew")?"/opt/homebrew/bin/brew":"brew";if(await I(o,["reinstall","lima"],{encoding:"utf-8",timeout:3e5}),n=K(),!n)throw new Error("brew reinstall lima did not provide a native limactl")}let r=n?{...process.env,PATH:`${n}:${process.env.PATH??""}`}:process.env;await I("colima",["start"],{encoding:"utf-8",timeout:3e5,env:r});return}let t=K(),s=t?{...process.env,PATH:`${t}:${process.env.PATH??""}`}:process.env;await I("colima",[e],{encoding:"utf-8",timeout:3e5,env:s})}async function Qt(e,t=Yt,s={}){let n=[],r=[];for(let d of s.tiltPids??[])await t.tiltStatus(d)==="stale"?n.push({id:`stop-tilt:${d}`,check:"Tilt processes",scope:"project",command:`kill -TERM ${d}`,status:"planned",reason:"this project's Tilt is running but its UI does not answer"}):r.push(d);let o=r.length>0?`Tilt is running for this project (pid ${r.join(", ")}); run flisk down first`:void 0,i=t.colimaStatus(),a=Ct(i,t.dockerAnswers());if(a==="start")n.push({id:"colima:start",check:"Colima",scope:"host",command:"colima start",status:"planned",reason:i.problem??"the Colima VM is not running"});else if(a==="restart")n.push({id:"colima:restart",check:"Colima",scope:"host",command:"colima restart",status:"planned",reason:"the Colima VM is running but its Docker daemon does not answer"});else if(a==="native-lima"){let d=K();n.push({id:"colima:native-lima",check:"Colima",scope:"host",command:d?`PATH="${d}:$PATH" colima start`:"brew reinstall lima",status:"planned",reason:d?`the colima CLI on this PATH cannot see the VM; a native limactl is installed in ${d}`:"the colima CLI on this PATH cannot see the VM; lima is installed as an x86_64 build (brew reinstall lima)"})}let c=await t.listNetworks(),l=t.freeDiskGib();if(l!==null&&l<Ee){let{buildCache:d,danglingImages:S}=await t.reclaimable(),k=`${l.toFixed(1)} GB free`;d&&!/^0(\.0+)?\s*B$/i.test(d)&&n.push({id:"prune:builder",check:"Disk Space",scope:"host",command:"docker builder prune -f",status:"planned",reason:`${k}; ${d} of build cache reclaimable`}),S>0&&n.push({id:"prune:images",check:"Disk Space",scope:"host",command:"docker image prune -f",status:"planned",reason:`${k}; ${S} untagged image${S===1?"":"s"}`})}let f=new Set(e?Oe(e):[]),m=zt(c.map(d=>d.name));for(let d of c){let S=Cn.exec(d.name),k,y,p="unused network";if(S)k="probe",y="Orphaned probe networks";else if(f.has(d.name))k="project",y="Docker networks";else if(s.machine&&_e(d.name,m))k="machine",y="Docker networks",p=`unused network of project "${_e(d.name,m)}"`;else continue;let D={id:`remove-network:${d.name}`,check:y,scope:k,command:`docker network rm ${d.name}`,status:"planned",reason:p};d.containers>0?(D.status="skipped",D.reason=`${d.containers} container(s) still attached`):k==="project"&&o?(D.status="skipped",D.reason=o):S&&t.isProcessAlive(Number(S[1]))&&(D.status="skipped",D.reason=`probe of a running process (pid ${S[1]})`),n.push(D)}return n}async function Zt(e,t,s=Yt){let n=[];for(let r of e){if(r.status!=="planned"||t.dryRun){n.push(r);continue}if(!t.confirm){n.push({...r,status:"skipped",reason:"confirmation required; run in a terminal or pass --yes"});continue}let o=!1;try{o=await t.confirm(r)}catch{}if(!o){n.push({...r,status:"skipped",reason:"not confirmed"});continue}try{if(r.id.startsWith("prune:")){await s.prune(r.id==="prune:builder"?"builder":"images"),n.push({...r,status:"applied",reason:"pruned"});continue}if(r.id.startsWith("colima:")){let i=r.id.slice(7);if(i==="start"||i==="restart"){await s.colima(i),n.push({...r,status:"applied",reason:`colima ${i} ran`});continue}try{await s.colima("native-lima"),n.push({...r,status:"applied",reason:"native lima repaired and colima started"})}catch(a){n.push({...r,status:"failed",reason:le(a)})}continue}if(r.id.startsWith("stop-tilt:")){s.stopProcess(Number(r.id.slice(10))),n.push({...r,status:"applied",reason:"stopped"});continue}await s.removeNetwork(r.id.slice(15)),n.push({...r,status:"applied",reason:"removed"})}catch(i){n.push({...r,status:"failed",reason:le(i)})}}return n}function es(e){return e.some(t=>t.status==="failed")}async function Fn(e){try{let{stdout:t}=await I(e,["--version"],{encoding:"utf-8",timeout:5e3,windowsHide:!0});return t.trim().split(/\r?\n/)[0]||null}catch{return null}}async function In(e=et("flisk"),t=Fn){let s=new Set,n=[];for(let r of e){let o=r;try{o=Xt(r)}catch{}s.has(o)||(s.add(o),n.push({path:r,version:await t(r)}))}return n}async function ts(e){let t=e??await In();if(t.length<=1)return{name:"Flisk installs",didPass:!0,message:"One flisk install on PATH"};let s=t.map(r=>`${r.path} (${r.version??"version unknown"})`).join(", "),n=new Set(t.map(r=>r.version));return{name:"Flisk installs",didPass:!1,isWarning:!0,message:`${t.length} flisk installs on PATH${n.size>1?" with different versions":""}: ${s}`,fix:"Keep one: remove the others, or run flisk upgrade. The first on PATH is the one your shell runs; tools that launch /usr/local/bin/flisk directly may use another"}}function ss(e,t=ce){let s=new Set([e]);try{s.add(Xt(e))}catch{}let n=new Set;for(let r of s)for(let o of t(vn(r,".flisk",".flisk-out","Tiltfile")))n.add(o);return[...n]}var Ae=1;function Ln(e,t=[]){return{schemaVersion:Ae,data:e,errors:t}}function Mn(e){return e instanceof tt?{error:{code:"COMMAND_FAILED",message:e.message,...e.suggestions.length>0?{suggestions:e.suggestions}:{}},exitCode:e.exitCode}:{error:{code:"INTERNAL",message:e instanceof Error?e.message:String(e)},exitCode:2}}function Bo(e){let t=Mn(e);console.log(JSON.stringify(Ln(null,[t.error]))),console.error(t.error.message),process.exit(t.exitCode)}async function rs(e,t){let s=[],n=[],r=o=>{o.status==="fulfilled"?s.push(o.value):n.push({code:"INTERNAL",message:o.reason instanceof Error?o.reason.message:String(o.reason)})};for(let o of await Promise.allSettled(e.map(i=>Promise.resolve().then(i))))r(o);for(let o of t)for(let i of await Promise.allSettled([Promise.resolve().then(o)]))r(i);return{checks:s,errors:n}}function ns(e){let t=e.find(s=>s.name===v);return t?t.didPass:null}function F(e,t,s=[],n,r){return{schemaVersion:Ae,data:{ready:s.length===0&&e.every(o=>o.didPass||o.isSkipped||o.isWarning),inProject:t,checks:e,...r?{host:{...r,containerRuntimeReachable:ns(e),canUp:r.canUp&&ns(e)!==!1}}:{},...n!==void 0?{ports:{http:{requested:n?.requested.ingressHttp??80,chosen:n?.ingressHttp??null,explicit:n?.explicit.ingressHttp??!!process.env.FLISK_HTTP_PORT,reason:n?.reason.ingressHttp??"port planning failed"},https:{requested:n?.requested.ingressHttps??443,chosen:n?.ingressHttps??null,explicit:n?.explicit.ingressHttps??!!process.env.FLISK_HTTPS_PORT,reason:n?.reason.ingressHttps??"port planning failed"},postgres:{requested:n?.requested.postgres??5432,chosen:n?.postgres??null,explicit:n?.explicit.postgres??!!process.env.FLISK_POSTGRES_PORT,reason:n?.reason.postgres??"port planning failed"}}}:{}},errors:s}}function os(e){return e.errors.length>0?2:e.data.ready?0:1}import{existsSync as Ie,readFileSync as is}from"node:fs";import{dirname as Hn,join as Fe,normalize as Wn}from"node:path";function Bn(e){let t=new Set,s=/^load\s*\(([\s\S]*?)\)/gm;for(let n of e.matchAll(s)){let r=n[1]??"",o=/["']([^"']+)["']/g,i=Array.from(r.matchAll(o),a=>a[1]);for(let a of i.slice(1))a.startsWith("_")&&t.add(a)}return Array.from(t).sort()}function as(e,t,s){return t==="ext://flisk-cli"?Fe(s,".flisk",".flisk-out","flisk-cli-ext","Tiltfile"):t.startsWith(".")||!t.includes("://")&&!t.startsWith("@")?Wn(Fe(Hn(e),t)):null}function Vn(e,t,s){let n=[],r=/^load\s*\(([\s\S]*?)\)/gm;for(let o of t.matchAll(r)){let c=(o[1]??"").match(/["']([^"']+)["']/)?.[1];if(!c)continue;let l=as(e,c,s);l&&(Ie(l)||n.push(`${e}: ${c} -> ${l}`))}return n}function Un(e,t,s){let n=[],r=/^load\s*\(([\s\S]*?)\)/gm;for(let o of t.matchAll(r)){let c=(o[1]??"").match(/["']([^"']+)["']/)?.[1];if(c){let l=as(e,c,s);l&&n.push(l)}}return n}function Gn(e,t){let s=new Set,n=[e];for(;n.length>0;){let r=n.pop();if(!r||s.has(r)||!Ie(r))continue;s.add(r);let o=is(r,"utf-8");for(let i of Un(r,o,t))s.has(i)||n.push(i)}return Array.from(s).sort()}function cs(){let e=g()??process.cwd(),t=Fe(e,".flisk",".flisk-out","Tiltfile");if(!Ie(t))return{name:"Starlark Load Exports",didPass:!0,isSkipped:!0,message:"No generated Tiltfile to check"};let s=[],n=[],r=Gn(t,e);for(let i of r){let a=is(i,"utf-8"),c=Bn(a);c.length>0&&s.push(`${i}: ${c.join(", ")}`);for(let l of Vn(i,a,e))n.push(l)}if(s.length===0&&n.length===0)return{name:"Starlark Load Exports",didPass:!0,message:`${C(r.length,"reachable Starlark file")} ${r.length===1?"has":"have"} valid load exports and paths`};let o=[];return s.length>0&&o.push(`Private exported symbols:
7
- ${s.join(`
8
- `)}`),n.length>0&&o.push(`Missing relative load targets:
9
- ${n.join(`
10
- `)}`),{name:"Starlark Load Exports",didPass:!1,message:`Starlark load() issues will stop Tilt before services start:
11
- ${o.join(`
12
-
13
- `)}`,fix:"Regenerate with a current Flisk (`flisk config regenerate`). If the issue remains, rename loaded symbols without leading underscores and update stale load paths."}}import{readFileSync as Kn}from"node:fs";import{join as qn}from"node:path";function ls(e){let t=e.trim().match(/(\d+)\.(\d+)(?:\.(\d+))?/);return t?[Number(t[1]),Number(t[2]),Number(t[3]??0)]:null}function Jn(e){try{return JSON.parse(Kn(qn(e,".flisk",H),"utf-8"))?.minFliskVersion}catch{return}}function us(e,t){let s=Jn(e);if(s===void 0)return{status:"none"};let n=typeof s=="string"?ls(s):null;if(!n||!/^\d+\.\d+\.\d+$/.test(String(s).trim()))return{status:"malformed",message:`.flisk/project.json minFliskVersion ${JSON.stringify(s)} is not a version like "1.3.80"`,fix:'Set minFliskVersion to a MAJOR.MINOR.PATCH string, for example "1.3.80".'};let r=ls(t),o=r!==null&&(()=>{for(let[a,c]of n.entries()){let l=r[a]??0;if(l!==c)return l>c}return!0})(),i=n.join(".");return o?{status:"ok",required:i}:{status:"too-old",required:i,message:`flisk ${t} is older than the ${i} this project requires (minFliskVersion)`,fix:"Run: flisk upgrade"}}import{relative as zn}from"node:path";function ue(e,t){return zn(e,t.configPath)||t.configPath}function ds(e){let t=e.config?.dependsOn;return t===void 0?[]:Array.isArray(t)&&t.every(s=>typeof s=="string")?t:null}function Xn(e){let t=new Set(e.map(a=>a.name)),s=new Map(e.map(a=>[a.name,(ds(a)??[]).filter(c=>t.has(c))])),n=new Map,r=new Map,o=[],i=a=>{r.set(a,"visiting"),o.push(a);for(let c of s.get(a)??[])if(r.get(c)==="visiting"){let l=o.slice(o.indexOf(c)),f=l.indexOf([...l].sort()[0]??c),m=[...l.slice(f),...l.slice(0,f)];n.set(m.join(">"),[...m,m[0]??c])}else r.has(c)||i(c);o.pop(),r.set(a,"done")};for(let a of[...s.keys()].sort())r.has(a)||i(a);return[...n.values()]}function Le(e=g()??process.cwd(),t=w(e)){let s=t.flatMap(n=>ds(n)===null?[`${ue(e,n)} dependsOn ${JSON.stringify(n.config?.dependsOn)}: expected an array of service names`]:[]);return s.length===0?{name:"dependsOn",didPass:!0,message:"Every dependsOn is a list of service names"}:{name:"dependsOn",didPass:!1,message:`${C(s.length,"invalid dependsOn","invalid dependsOn")}:
14
- ${s.join(`
15
- `)}`,fix:'Write "dependsOn" as an array of service names, for example ["postgres", "api"]'}}function Me(e=g()??process.cwd(),t=w(e)){let s=new Map(t.map(o=>[o.name,o])),n=Xn(t);if(n.length===0)return{name:"Circular dependencies",didPass:!0,message:"No service depends on itself through dependsOn"};let r=n.map(o=>{let i=s.get(o[0]??""),a=o[1]??"";return`${i?ue(e,i):o[0]} dependsOn "${a}": circular dependency ${o.join(" -> ")}`});return{name:"Circular dependencies",didPass:!1,message:`${n.length} ${n.length===1?"circular dependency":"circular dependencies"}:
16
- ${r.join(`
17
- `)}`,fix:'Remove one "dependsOn" entry in each cycle so the services no longer wait on each other, or move what they share into a third service'}}function He(e=g()??process.cwd(),t=w(e)){let s=t.flatMap(n=>{let r=n.config?.port;return r===void 0?[]:typeof r=="number"&&Number.isInteger(r)&&r>=1&&r<=65535?[]:[`${ue(e,n)} port ${JSON.stringify(r)}: not an integer from 1 to 65535`]});return s.length===0?{name:"Service ports",didPass:!0,message:"Every declared port is valid"}:{name:"Service ports",didPass:!1,message:`${C(s.length,"invalid port")}:
18
- ${s.join(`
19
- `)}`,fix:'Set "port" to the port the service listens on, an integer from 1 to 65535'}}function We(e=g()??process.cwd(),t=!1,s=w(e)){let n=[],r=[];for(let i of s){let a=i.config?.schemaVersion;if(a===Pe)continue;let c=ue(e,i);a===void 0?n.push(`${c} schemaVersion: missing`):r.push(`${c} schemaVersion ${JSON.stringify(a)}: this flisk supports ${Pe}`)}if(n.length===0&&r.length===0)return{name:"schemaVersion",didPass:!0,message:"Every service.json has a supported schemaVersion"};let o=[...n.length>0?["Run `flisk config migrate` to add the missing schemaVersion"]:[],...r.length>0?["A version this flisk does not support cannot be migrated: upgrade flisk"]:[]];return{name:"schemaVersion",didPass:!1,isWarning:!t,message:`${C(n.length+r.length,"service.json file")} without a supported schemaVersion:
20
- ${[...n,...r].join(`
21
- `)}`,fix:o.join(". ")}}function li(e,t){let s=We(e,!1,t);s.didPass||(console.warn(h.yellow(`\u26A0 ${s.message}`)),s.fix&&console.warn(h.gray(` ${s.fix}`)))}function ui(e,t={},s=process.exit){let n=t.resources??w(e),r=[se(e,n),Le(e,n),Me(e,n),He(e,n)].filter(i=>!i.didPass);if(r.length===0)return;let o=r.map(i=>`${i.message}${i.fix?`
22
- Fix: ${i.fix}`:""}`).join(`
23
- `);t.onInvalid?.(o);for(let i of r)console.error(h.red(`\u2717 ${i.message}`)),i.fix&&console.error(h.gray(` Fix: ${i.fix}`));s(1)}import{execFileSync as q}from"node:child_process";import{lookup as Yn}from"node:dns/promises";var ps="Windows did not resolve *.flisk.localhost to 127.0.0.1. Browser URLs with hostnames will fail until you fix hosts or use the 127.0.0.1 URLs printed by flisk networks.";function Qn(e){return e.length>0&&e.every(t=>t==="127.0.0.1"||t==="::1")}function Zn(e){return e.split(/\r?\n/).flatMap(t=>{let s=t.match(/^\s*(\d+)\s+(\d+)(?:\s|$)/);return s?[[Number(s[1]),Number(s[2])]]:[]})}function er(e,t,s){let n=s.replace(/-/g,"_").toLowerCase();return e.filter(r=>{let o=t.find(a=>a.publishedPorts.includes(r));if(!o||!n)return!0;let i=o.name.toLowerCase();return!i.startsWith(`${n}_`)&&!i.startsWith(`${n}-`)})}function tr(){return b("docker")}function fs(){let e=tr();if(!e)return{name:"Windows Runtime Tools",didPass:!1,message:"Docker is not running. Start Docker Desktop, wait until it is Running, then retry flisk doctor.",fix:"Install Docker Desktop and start it in Linux container mode."};if(!b("tilt"))return{name:"Windows Runtime Tools",didPass:!1,message:"tilt.exe not found on PATH. Install Tilt from https://docs.tilt.dev/install.html"};let t=b("bun");return((process.argv[1]??"").toLowerCase().includes("node_modules")?b("node"):!0)?{name:"Windows Runtime Tools",didPass:!0,message:t?`Docker (${e}), Tilt, and Bun found on PATH`:`Docker (${e}) and Tilt found on PATH; \u26A0\uFE0F Bun was not found (generated services need Bun)`}:{name:"Windows Runtime Tools",didPass:!1,message:"node.exe not found on PATH for this npm installation."}}function ms(){try{return q(b("docker")??"docker",["info","--format","{{.OSType}}"],{encoding:"utf-8",timeout:1e4,windowsHide:!0}).trim().toLowerCase()==="windows"?{name:"Docker container mode",didPass:!1,message:"Docker Desktop is using Windows containers. Switch to Linux containers. Flisk stacks are Linux images.",fix:"Switch Docker Desktop to Linux containers. Flisk does not run Windows containers."}:{name:"Docker container mode",didPass:!0,message:"Docker Desktop is using Linux containers"}}catch{return{name:"Docker container mode",didPass:!1,message:"Docker is not running. Start Docker Desktop, wait until it is Running, then retry flisk doctor."}}}async function hs(){let e=[],t=[];try{let l=q("netsh",["interface","ipv4","show","excludedportrange","protocol=tcp"],{encoding:"utf-8",timeout:1e4,windowsHide:!0});t=Zn(l)}catch{e.push("Could not inspect Windows Hyper-V excluded TCP port ranges.")}let s=[80,443,5432],n=s.filter(l=>t.some(([f,m])=>l>=f&&l<=m));n.length>0&&e.push(`\u26A0\uFE0F Windows reserved port${n.length>1?"s":""} ${n.join(", ")} for Hyper-V/WSL. Fix: disable the conflicting Windows feature or change Flisk ports in project config. Common fix: run \`net stop winnat\` then \`net start winnat\` in an Administrator terminal, then retry \`flisk doctor\`.`);let r=[];for(let l of s)await _t(l)==="in-use"&&r.push(l);let o=[];try{let l=q(b("docker")??"docker",["ps","--format","{{.Names}}\\t{{.Ports}}"],{encoding:"utf-8",timeout:1e4,windowsHide:!0});o=Tt(l,s)}catch{}let i=g()?Z():"",a=er(r,o,i);if(a.length>0)try{let l=q("powershell.exe",["-NoProfile","-Command",`$ports = '${a.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 } } }`],{encoding:"utf-8",timeout:1e4,windowsHide:!0}).trim();e.push(`\u26A0\uFE0F Ports ${a.join(", ")} are already listening${l?`:
24
- ${l}`:"."}`)}catch{e.push(`\u26A0\uFE0F Ports ${a.join(", ")} are already in use.`)}let c=!0;for(let l of["flisk.localhost","example.flisk.localhost"])try{let f=await Yn(l,{all:!0,verbatim:!0});Qn(f.map(m=>m.address))||(c=!1)}catch{c=!1}c||e.push(`${ps}
25
- Add these lines to C:\\Windows\\System32\\drivers\\etc\\hosts (Administrator Notepad):
26
- 127.0.0.1 flisk.localhost
27
- 127.0.0.1 example.flisk.localhost`);try{q("git",["config","core.autocrlf"],{encoding:"utf-8",timeout:5e3,windowsHide:!0}).trim().toLowerCase()==="true"&&e.push("git core.autocrlf is true; generated files may get CRLF while Docker Linux containers expect LF. Recommended: git config core.autocrlf input in Flisk repos.")}catch{}return e.push("Docker Desktop Settings \u2192 Resources \u2192 File sharing must include this project directory."),{name:"Windows ports and DNS",didPass:c&&a.length===0,message:e.length>0?e.join(`
28
- `):"Required ports are available and Flisk localhost DNS resolves to loopback",...c?{}:{fix:ps}}}var R=1e4,E={dockerMissing:"See https://docs.docker.com/get-docker/",dockerDaemonDown:"Start Docker Desktop, OrbStack, or Colima, then retry: flisk doctor",tiltMissing:"curl -fsSL https://raw.githubusercontent.com/tilt-dev/tilt/master/scripts/install.sh | bash",bunMissing:"curl -fsSL https://bun.sh/install | bash",port80:"Stop the process bound to port 80, or stop local nginx/caddy. Then: flisk doctor",port5432:"Stop local Postgres or change the host port. Then: flisk doctor",notProject:"flisk project --yes"},ks="WSL2 detected. Use Docker Desktop WSL integration. Guide: docs/wsl2.md",Ss="Native Windows landscape boot is unsupported. Use WSL2 Ubuntu with Docker Desktop integration. Guide: docs/wsl2.md",Be=[0,25,0],L=[1,2,0];function me(e,t){let s=Ge(e);return s?Ke(s,t):!1}function rr(e,t,s=process.platform==="linux"&&!!process.env.WSL_DISTRO_NAME){if(!s)return{name:"WSL project location",didPass:!0,isSkipped:!0,message:"Not running in WSL2 - skipped project filesystem check"};let n=e.replace(/\\/g,"/").replace(/\/+$/,"").toLowerCase();if(n!=="/mnt/c"&&!n.startsWith("/mnt/c/"))return{name:"WSL project location",didPass:!0,message:`${ks} Project is on the WSL filesystem.`};let r=`${ks} Project is under /mnt/c (${e}); hot reload may be broken. Move it into the WSL filesystem, such as ~/projects.`;return{name:"WSL project location",didPass:!t,isWarning:!t,message:r,fix:"Move the repository under your WSL home directory (for example, ~/projects) and retry flisk doctor."}}function or(e){let t=e.replace(/^\[(.*)\]$/,"$1");return t==="0.0.0.0"||/^[0:]*:0\.0\.0\.0$/.test(t)?!0:t.includes(":")&&/^[0:]+$/.test(t)}function ir(e=process.env,t=!1){let s=(e.FLISK_BIND_ADDRESS??"").trim();return or(s)?{name:"Dev Port Bind Address",didPass:!t,isWarning:!t,message:`FLISK_BIND_ADDRESS is ${s}: Traefik and Postgres are reachable from other machines on the network.`,fix:"Unset FLISK_BIND_ADDRESS to keep dev ports on 127.0.0.1. Keep it only when you test from another device on purpose."}:{name:"Dev Port Bind Address",didPass:!0,message:s?`Dev ports bind to ${s}`:"Dev ports bind to 127.0.0.1 (this machine only)"}}function Ps(e){let t=e.filter(n=>!n.didPass&&!n.isSkipped&&!n.isWarning),s=e.filter(n=>n.didPass||n.isSkipped||n.isWarning);return t.sort((n,r)=>{let o=i=>i.name==="Host Ports"&&/\b5432\b/.test(i.message)?0:i.name==="Host Ports"?1:i.name==="Ingress Ports"?2:3;return o(n)-o(r)}),[...t,...s]}function ar(e,t){return t?e?"Doctor passed. Next: flisk up":"Doctor passed. Next: flisk project example":"Doctor failed. Fix the items above, then run: flisk doctor"}var Ue={checkDockerOperatingSystem:"Docker OS",checkDockerCompose:"Docker Compose",checkDockerVersions:"Docker Versions",checkDockerNetworkCapacity:"Docker Network Pool",checkContainerResourceHealth:"Container Resource Health",checkCrashLoopingContainers:"Crash-looping containers",checkTiltResourceHealth:"Tilt Resources",checkServiceHealth:"Service Health",checkServiceSmoke:"Service Smoke",checkAdvertisedEndpoints:"Advertised Endpoints"},ys=new Set(["Docker OS","Docker Compose","Docker Versions","Docker Network Pool","Container Resource Health","Crash-looping containers","Tilt Resources","Service Health","Service Smoke","Advertised Endpoints",...Object.keys(Ue)]);function cr(e){return!!(e.requiresContainerRuntime||e.checkName&&ys.has(e.checkName)||e.name&&ys.has(e.name))}function T(e,t){let s=()=>t();return s.requiresContainerRuntime=!0,s.checkName=e,s}async function lr(e,t={}){let s=[],n=!!t.containerRuntimeFailed;for(let r of e){if(n&&cr(r)){let i=r.checkName??(r.name&&Ue[r.name]?Ue[r.name]:r.name&&r.name!=="check"&&r.name!=="fn"?r.name:"Container check");s.push({name:i,didPass:!0,isSkipped:!0,message:`Docker not running - skipped ${i} check`});continue}let o=await r();if(s.push(o),o.name===v&&!o.didPass&&(n=!0),!t.all&&!o.didPass&&!o.isSkipped&&!o.isWarning)break}return s}function ur(e){let t=0,s=0,n=0,r=0;for(let o of e)o.isSkipped?n++:o.isWarning?r++:o.didPass?t++:s++;return r>0?`${t} passed, ${r} warned, ${s} failed, ${n} skipped`:`${t} passed, ${s} failed, ${n} skipped`}function Je(e){return gt(e)}function dr(e,t,s,n,r){return async(o=O)=>{try{return await o(t,R),{name:e,didPass:!0,message:s}}catch(i){return Je(i)?{name:e,didPass:!1,message:`${e} did not respond within ${R/1e3}s`,fix:r}:{name:e,didPass:!1,message:n,fix:r}}}}async function Ve(e){try{return await O(e,R),!0}catch{return!1}}async function pr(){let e=W();return e.isColima?e.running&&!e.problem?{name:"Colima",didPass:!0,message:`Colima VM is running${e.arch?` (${e.arch})`:""}`}:{name:"Colima",didPass:!1,message:e.problem??"The Colima VM is not running",fix:e.fix}:{name:"Colima",didPass:!0,message:"Docker does not run on Colima"}}async function ze(){if(process.platform==="win32")try{return await O("docker ps",R),{name:v,didPass:!0,message:"Docker Desktop Linux container engine is running"}}catch{return{name:v,didPass:!1,message:"Docker is not running",fix:E.dockerDaemonDown}}let e=!!b("docker");try{return await O("docker ps",R),{name:v,didPass:!0,message:"Docker daemon is running"}}catch(s){if(Je(s))return{name:v,didPass:!1,message:"Docker daemon is not responding",fix:E.dockerDaemonDown}}let t=qt(await Ve("pgrep -f com.docker.backend"));return t||(await Ve("colima status")?{name:v,didPass:!0,message:"Colima (Docker runtime) is running"}:b("colima")?{name:v,didPass:!1,message:"Colima is installed but not running",fix:E.dockerDaemonDown}:await Ve("podman ps")?{name:v,didPass:!0,message:"Podman is running"}:e?{name:v,didPass:!1,message:"Docker daemon is not running",fix:E.dockerDaemonDown}:{name:v,didPass:!1,message:"Docker is not running",fix:E.dockerMissing})}var J=dr("Docker Compose","docker compose version","Docker Compose plugin available","Docker Compose plugin not found","Install Docker Compose: https://docs.docker.com/compose/install/");J.requiresContainerRuntime=!0;J.checkName="Docker Compose";var fr=[25,0,0],mr=[2,20,2];function Ge(e){let t=e.trim().match(/(\d+)\.(\d+)(?:\.(\d+))?/);return t?[Number(t[1]),Number(t[2]),Number(t[3]??0)]:null}function Ke(e,t){for(let[s,n]of t.entries()){let r=e[s]??0;if(r!==n)return r>n}return!0}async function z(e=O){let t=async a=>String(await e(a,R)).trim(),s,n;try{[s,n]=await Promise.all([t("docker version --format '{{.Server.Version}}'"),t("docker compose version --short")])}catch{return{name:"Docker Versions",didPass:!0,isSkipped:!0,message:"Could not read Docker Engine/Compose versions - skipped version check"}}let r=Ge(s),o=Ge(n);if(!r||!o)return{name:"Docker Versions",didPass:!0,isSkipped:!0,message:`Unrecognized Docker version output (engine "${s}", compose "${n}") - skipped version check`};let i=[];return Ke(r,fr)||i.push(`Docker Engine ${s} (need 25.0+)`),Ke(o,mr)||i.push(`Docker Compose ${n} (need 2.20.2+)`),i.length===0?{name:"Docker Versions",didPass:!0,message:`Docker Engine ${s} and Compose ${n} support generated healthchecks`}:{name:"Docker Versions",didPass:!1,message:`Docker is too old for generated healthchecks (they use start_interval):
29
- ${i.join(`
30
- `)}`,fix:"Update Docker Desktop, or on Linux update docker-ce and the docker-compose-plugin package"}}z.requiresContainerRuntime=!0;z.checkName="Docker Versions";async function Xe(e=O){let t;try{t=String(await e("tilt version",R)).trim()}catch{return{name:"Tilt CLI",didPass:!1,message:"Tilt CLI not found",fix:E.tiltMissing}}return me(t,Be)?{name:"Tilt CLI",didPass:!0,message:`Tilt ${t} is available`}:{name:"Tilt CLI",didPass:!1,message:`Tilt ${t||"version unknown"} is below the required v${Be.join(".")} floor`,fix:`Install Tilt v${Be.join(".")} or newer: https://docs.tilt.dev/install.html`}}async function Ye(e=O){let t;try{t=String(await e("docker info --format '{{.OSType}}'",R)).trim().toLowerCase()}catch{return{name:"Docker OS",didPass:!0,isSkipped:!0,message:"Docker OS unavailable - daemon failure reported separately"}}return t!=="linux"?{name:"Docker OS",didPass:!1,message:`Docker is using the ${t||"unknown"} engine; Flisk requires Linux containers`,fix:"Switch Docker Desktop to Linux containers, enable WSL2 integration, then retry flisk doctor."}:{name:"Docker OS",didPass:!0,message:"Docker is using Linux containers"}}Ye.requiresContainerRuntime=!0;Ye.checkName="Docker OS";async function hr(e=O){let t=g();if(!t)return{name:"Bun",didPass:!0,isSkipped:!0,message:"Not in a project - skipping Bun check"};if(!oe(w(t)))return{name:"Bun",didPass:!0,isSkipped:!0,message:"No generated JS services - skipping Bun check"};try{let s=String(await e("bun --version",R)).trim();return me(s,L)?{name:"Bun",didPass:!0,message:`Bun ${s} is available`}:{name:"Bun",didPass:!1,message:`Bun ${s||"version unknown"} is below the required ${L.join(".")} floor`,fix:`Install Bun ${L.join(".")} or newer: https://bun.sh/install`}}catch{return{name:"Bun",didPass:!1,message:"Bun is not available",fix:E.bunMissing}}}function gr(){let e=g()??process.cwd(),t=nt(e);if(t.missing.length>0)return{name:"Environment Variables",didPass:!1,message:`Missing required env variables: ${t.missing.join(", ")}`,fix:$(x(e,".env"))?`Edit .env and set these values:
31
- ${t.missing.map(n=>`${n}=<value>`).join(`
32
- `)}`:"No .env file yet. Run `flisk project` to create one with working defaults."};if(t.invalid.length>0)return{name:"Environment Variables",didPass:!1,message:`Invalid env variables: ${t.invalid.join(", ")}`,fix:"Edit .env and provide values for empty variables"};let s=t.warnings.filter(n=>!n.startsWith(".env file not found"));return{name:"Environment Variables",didPass:!0,message:s.length>0?`All required environment variables are set (warning: ${s.join("; ")})`:"All required environment variables are set"}}function kr(){let e=g()??process.cwd(),t=x(e,".flisk",".flisk-out"),s=Se.filter(n=>!$(x(t,n)));return s.length===0?{name:"Master Configs",didPass:!0,message:`${Se.join(", ")} found`}:{name:"Master Configs",didPass:!1,message:`Master configs missing: ${s.join(", ")}`,fix:"Run: flisk project"}}function Sr(e=Ze()){let t=g();if(!t)return{name:"Flisk version",didPass:!0,isSkipped:!0,message:"Not in a project - skipping Flisk version check"};let s=us(t,e);return s.status==="none"?{name:"Flisk version",didPass:!0,isSkipped:!0,message:`flisk ${e} (no minFliskVersion in .flisk/project.json)`}:s.status==="malformed"||s.status==="too-old"?{name:"Flisk version",didPass:!1,message:s.message,fix:s.fix}:{name:"Flisk version",didPass:!0,message:`flisk ${e} meets minFliskVersion ${s.required}`}}function yr(){let e=g()??process.cwd(),t=Te(e),s=w(e).filter(r=>!ie(e,r.path,t));if(s.length===0)return{name:"Resource Discovery",didPass:!0,message:`All resources are covered by discovery.paths ${JSON.stringify(t)}`};let n=s.map(r=>pe(fe(e,r.path))).join(", ");return{name:"Resource Discovery",didPass:!1,message:`${C(s.length,"resource")} outside discovery.paths ${JSON.stringify(t)}: ${n}. \`flisk up\` will not start them`,fix:"Move each under services/<stack>/<name>, or add a matching pattern to discovery.paths in .flisk/project.json and run: flisk config regenerate"}}function wr(e){let t=[],s=(n,r)=>{for(let o of xs(n,{withFileTypes:!0})){if(r===""&&o.name===".flisk-contract")continue;let i=r===""?o.name:`${r}/${o.name}`;o.isDirectory()?s(x(n,o.name),i):o.isFile()&&t.push(i)}};return s(e,""),t.sort()}function xr(e=!1){let t=g()??process.cwd(),s=w(t).filter(o=>(o.config?.appType??o.type)!=="library");if(s.length===0)return{name:"Generated Files",didPass:!0,isSkipped:!0,message:"No resources to check"};let n=[],r=[];for(let o of s){let i=pe(fe(t,o.path)),a=x(o.path,A),c=$(a)?wr(a):[];c.length===0?(r.push(i),n.push(`${i}: no ${A}/ files`)):n.push(`${i}: ${c.join(", ")}`)}return r.length===0?{name:"Generated Files",didPass:!0,message:`${C(s.length,"resource")} with generated files:
33
- ${n.join(`
34
- `)}`}:{name:"Generated Files",didPass:!1,isWarning:!e,message:`${r.length} of ${C(s.length,"resource")} not generated yet:
35
- ${n.join(`
36
- `)}`,fix:"Run `flisk up` to generate them (it needs Docker, Compose and Tilt). `flisk config verify` checks the project-level files only."}}var Pr=["install-deps.sh","bun-hoisted-symlink-fix.sh","prisma-bun-client-link-fix.sh","prisma-normalize-client.sh","infisical-entrypoint.sh"],br=/shared-platform-engineering\/docker-templates\/([\w.-]+)/g;function vr(e){let t=new Set;for(let s of w(e)){let n=x(s.path,A);if($(n)){for(let r of xs(n))if(!(!r.startsWith("Dockerfile")||r.endsWith(".dockerignore")))for(let o of B(x(n,r),"utf-8").matchAll(br))t.add(o[1])}}return[...t]}function Dr(){let e=g()??process.cwd(),t=[];$(x(e,G))||t.push(`${G} (workspace root manifest)`);let s=x(e,"shared-platform-engineering","docker-templates"),n=[];try{n=vr(e)}catch{}for(let r of new Set([...Pr,...n]))$(x(s,r))||t.push(`shared-platform-engineering/docker-templates/${r}`);return t.length===0?{name:"Generated Runtime Assets",didPass:!0,message:"Generated Docker runtime assets are present"}:{name:"Generated Runtime Assets",didPass:!1,message:`Generated Dockerfiles reference missing project runtime assets:
37
- ${t.join(`
38
- `)}`,fix:"Run `flisk project --yes` (or `flisk config regenerate`) with an updated Flisk. `flisk up` also refreshes these assets before starting Tilt."}}function $r(){let e=g()??process.cwd(),t=w(e),s=[];for(let n of t){let r=x(n.path,G);if(!$(r))continue;let o=n.config?.appType??"backend",i=ye[o]??ye.backend,a;try{a=JSON.parse(B(r,"utf-8")).scripts??{}}catch{s.push(`${n.name} (package.json is invalid JSON)`);continue}let c=i.filter(l=>!a[l]);c.length>0&&s.push(`${n.name}: missing "${c.join('", "')}"`)}return s.length===0?{name:"Startup Scripts",didPass:!0,message:"All services have required package.json scripts"}:{name:"Startup Scripts",didPass:!1,message:`Services missing required package.json scripts:
39
- ${s.join(`
40
- `)}`,fix:`Add the missing scripts to each service's package.json, e.g.:
41
- "dev": "bun run src/index.ts",
42
- "build": "tsc",
43
- "start": "bun run dist/index.js"`}}function bs(e){if(e.startsWith("@"))return[e.split("/").slice(0,2).join("/")];let t=e.split("/")[0]??e;return[`@types/${t}`,t]}function vs(e,t,s=0){let n=e[t];if(typeof n!="string"||s>3)return!1;if(/(^|[\s;&|(])tsc(\s|$)/.test(n))return!0;for(let r of n.matchAll(/\b(?:bun|npm|pnpm|yarn)\s+run\s+([\w:.-]+)/g))if(vs(e,r[1],s+1))return!0;return!1}function ws(e){let t=bs(e);return e.includes("/")||e.startsWith("@")?t[t.length-1]:t[0]}var Cr={"@types/bun":"^1.4.2","@types/node":"^20.0.0"};function Nr(e,t){return(t.includes("bun")||e.has("@types/bun"))&&!e.has("@types/node")}function Tr(){let e=g()??process.cwd(),t=w(e),s=[],n=new Set;for(let o of t){let i=x(o.path,G),a=x(o.path,A,"tsconfig.docker.autogenerated.json");if(!$(i)||!$(a))continue;let f=qe(a)?.compilerOptions?.types;if(!Array.isArray(f))continue;let m=qe(i),d=m?.scripts??{};if(!vs(d,"build"))continue;let S=new Set([...Object.keys(m?.devDependencies??{}),...Object.keys(m?.dependencies??{})]),k=f.filter(y=>typeof y=="string").filter(y=>!bs(y).some(p=>S.has(p)));for(let y of k)n.add(ws(y));if(k.length>0){let y=k.map(p=>`"${p}" (needs ${ws(p)})`).join(", ");s.push(`${o.name}: "types" lists ${y}`)}Nr(S,f)&&(n.add("@types/node"),s.push(`${o.name}: uses @types/bun, which references node types internally, but @types/node is not a direct dependency`))}if(s.length===0)return{name:"TypeScript Type Dependencies",didPass:!0,message:`Every service's Docker tsconfig "types" entry has its @types package installed`};let r=[...n].map(o=>{let i=Cr[o];return i?`"${o}": "${i}"`:`${o} (bun add -d ${o})`});return{name:"TypeScript Type Dependencies",didPass:!1,message:`\`bun run build\` will fail with TS2688 (Cannot find type definition file) because a @types package it needs is not a direct dependency:
44
- ${s.join(`
45
- `)}`,fix:`Add to each service's package.json devDependencies: ${r.join(", ")}`}}function qe(e){try{return JSON.parse(B(e,"utf-8"))}catch{return null}}function Er(e){let s=qe(e)?.compilerOptions;return s?.allowSyntheticDefaultImports===!0||s?.esModuleInterop===!0}function Rr(){let e=g()??process.cwd(),t=w(e).filter(n=>n.config?.appType==="frontend");if(t.length===0)return{name:"Frontend Docker Preflight",didPass:!0,isSkipped:!0,message:"No frontend services to check"};let s=[];for(let n of t){$(x(n.path,"public"))||s.push(`${n.name}: no public/ folder, but the Docker build copies it. Create it: mkdir -p ${pe(fe(e,x(n.path,"public")))} && touch ${pe(fe(e,x(n.path,"public",".gitkeep")))}`);let r=x(n.path,A,"Dockerfile.app.autogenerated"),o=x(n.path,A,"tsconfig.docker.autogenerated.json");if($(r)){let i=B(r,"utf-8"),a=/\bEXPOSE\s+80\b/.test(i),c=n.config?.stack,l=c?x(e,"services",c,A,"docker-compose.app.autogenerated.yml"):"";if(a&&l&&$(l)){let f=B(l,"utf-8"),m=n.name.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),d=new RegExp(`traefik\\.http\\.services\\.${m}\\.loadbalancer\\.server\\.port=(\\d+)`),k=f.match(d)?.[1];k&&k!=="80"&&s.push(`${n.name}: nginx runtime exposes 80, but generated Traefik routes to ${k}`)}}if($(o)&&!Er(o)){let i=x(n.path,"src","main.tsx");if($(i)){let a=B(i,"utf-8"),c=[/import\s+\w+\s+from\s+["']react["']/,/import\s+\w+\s+from\s+["']react-dom\/client["']/].filter(l=>l.test(a)).map(l=>l.source.includes("react-dom")?"react-dom/client":"react");c.length>0&&s.push(`${n.name}: Docker tsconfig does not allow synthetic default imports, but src/main.tsx default-imports ${c.join(" and ")}`)}}}return s.length===0?{name:"Frontend Docker Preflight",didPass:!0,message:`${C(t.length,"frontend")} ${t.length===1?"has":"have"} compatible Docker build/routing metadata`}:{name:"Frontend Docker Preflight",didPass:!1,message:`Frontend Docker metadata has issues that will make \`flisk up\` fail or route 404:
46
- ${s.join(`
47
- `)}`,fix:'Regenerate with a current Flisk (`flisk config regenerate`) and ensure frontend nginx runtimes route to port 80. For React entrypoints, prefer named imports: `import { StrictMode } from "react"` and `import { createRoot } from "react-dom/client"`.'}}var Or=90,_r=80;function jr(e){return e.split(`
48
- `).map(t=>t.trim()).filter(Boolean).map(t=>{let[s,n,r]=t.split(",");return{name:s??"unknown",memPct:Number.parseFloat((n??"").replace(/%/g,"")),cpuPct:Number.parseFloat((r??"").replace(/%/g,""))}}).filter(t=>Number.isFinite(t.memPct)&&Number.isFinite(t.cpuPct))}var Ar=Object.assign(function(){let t;try{t=sr(b("docker")??"docker",["stats","--no-stream","--format","{{.Name}},{{.MemPerc}},{{.CPUPerc}}"],{stdio:"pipe",encoding:"utf-8",timeout:R,windowsHide:process.platform==="win32"})}catch(o){return{name:"Container Resource Health",didPass:!0,isSkipped:!0,message:Je(o)?"Docker not responding - skipped container resource check":"Docker not running - skipped container resource check"}}let s=jr(t);if(s.length===0)return{name:"Container Resource Health",didPass:!0,isSkipped:!0,message:"No running containers to check"};let n=s.filter(o=>o.memPct>=Or&&o.cpuPct>=_r);if(n.length===0)return{name:"Container Resource Health",didPass:!0,message:`${C(s.length,"container")} within normal resource usage`};let r=n.map(o=>`${o.name} (mem ${o.memPct.toFixed(0)}%, cpu ${o.cpuPct.toFixed(0)}%)`).join(`
49
- `);return{name:"Container Resource Health",didPass:!1,message:`${C(n.length,"container")} pinned near its memory limit while burning CPU -- likely stuck in a boot-time retry/fetch loop, not a normal crash:
50
- ${r}`,fix:"Check the container's boot logs for a network fetch loop (e.g. `bunx <pkg>` re-downloading a CLI on every start instead of using one already installed): docker logs <container>. If a Prisma auto-migration is the cause, set AUTO_MIGRATE=false in the project's .env as a temporary unblock."}},{requiresContainerRuntime:!0,checkName:"Container Resource Health"}),Fr=5e3;async function Ir(e,t){let s=g()??process.cwd(),n=xe(w(s),t);return n.length===0?{name:"Service Health",didPass:!0,isSkipped:!0,message:"No routable services to ping"}:De(await ee(n,e))}async function Lr(e,t){let s=g()??process.cwd(),n=w(s),r=ct(at(n,t),{timeoutSeconds:Math.ceil(e/1e3)});return r.length===0?$e([]):(await ee(xe(n,t),e)).some(i=>i.status!==void 0)?$e(await ut(r,lt(s))):{name:"Service Smoke",didPass:!0,isSkipped:!0,message:"No services responded - skipped smoke checks"}}async function Mr(e,t){let s=g()??process.cwd(),n=w(s),r=it(n,t);return r.length===0?{name:"Advertised Endpoints",didPass:!0,isSkipped:!0,message:"No routable services to check"}:jt(await ee(r,e))}function Hr(e,t){if(e.length===0){console.log(`${h.green("\u2713")} Nothing to repair
51
- `);return}for(let s of e){let n=s.status==="planned"?`${h.cyan("would run")}`:s.status==="applied"?h.green("ran"):s.status==="failed"?h.red("failed"):h.gray("skipped"),r=s.status==="planned"?"":` (${s.reason})`;console.log(`${n}: ${s.command}${r}`)}t&&console.log(h.gray(`
52
- Dry run: nothing was changed.`)),console.log("")}function de(e){for(let t of Ps(e))!t.didPass&&!t.isSkipped&&!t.isWarning?(console.log(`${h.red("\u2717")} ${t.message}`),t.fix&&console.log(`${h.blue("\u2139")} Fix: ${t.fix}`)):t.isWarning?(console.log(`${h.yellow("\u26A0")} ${h.yellow(t.message)}`),t.fix&&console.log(`${h.blue("\u2139")} Fix: ${t.fix}`)):t.isSkipped?(console.log(`${h.gray("\u25CB")} ${h.gray(t.message)}`),t.fix&&console.log(`${h.blue("\u2139")} ${t.fix}`)):console.log(`${h.green("\u2713")} ${t.message}`)}function Wr(e,t,s,n={},r){let o=r??[...e,...t];n.all?(console.log(h.bold("Machine")),de(e),console.log(`
53
- ${h.bold("Project")}`),s?t.length>0&&de(t):console.log(`${h.gray("\u25CB")} ${h.gray("Not in a Flisk project, so project checks were skipped")}`),console.log(""),console.log(ur(o))):(de(o),console.log(""),o.every(a=>a.didPass||a.isSkipped||a.isWarning)&&!s&&(console.log(`${h.gray("\u25CB")} ${h.gray("Not in a Flisk project, so project checks were skipped")}`),console.log("")))}var Br=new nr("doctor").description("Check environment readiness for Flisk").option("--all","Run every check and show Machine and Project sections with a summary",!1).option("--json","Output a versioned JSON readiness report",!1).exitOverride(e=>{e.exitCode===0&&process.exit(0),Br.opts().json&&console.log(JSON.stringify(F([],!1,[{code:"USAGE",message:e.message}]))),process.exit(2)}).option("--no-ping","Skip pinging running services' /health endpoints").option("--strict","Fail warnings such as a WSL project under /mnt/c").option("--fix","Repair this project's unused Docker networks and leftover doctor probes",!1).option("--dry-run","With --fix: list the repairs without changing anything",!1).option("--scope <scope>","With --fix: project (default) or machine, which also removes unused networks of other Flisk projects","project").option("-y, --yes","With --fix: apply every repair without asking",!1).option("--ping-timeout <ms>","Per-service ping timeout in milliseconds",String(Fr)).action(async e=>{let t=Number(e.pingTimeout);if(!/^\d+$/.test(e.pingTimeout)||!Number.isSafeInteger(t)||t<=0){let u="--ping-timeout must be a positive integer number of milliseconds";e.json&&console.log(JSON.stringify(F([],!1,[{code:"USAGE",message:u}]))),console.error(u),process.exit(2);return}let s=!["project","machine"].includes(e.scope),n=e.scope!=="project"&&!e.fix;if(s||n||(e.dryRun||e.yes)&&!e.fix){let u=s?`--scope must be project or machine, got "${e.scope}"`:n?"--scope only applies with --fix":`${e.dryRun?"--dry-run":"--yes"} only applies with --fix`;e.json&&console.log(JSON.stringify(F([],!1,[{code:"USAGE",message:u}]))),console.error(u),process.exit(2);return}e.json||console.log("Flisk doctor checks your local Docker + Tilt development environment; it does not check cluster deployments.");let r=It();if(process.platform==="win32"){let P=[{name:"Native Windows support",didPass:!1,message:Ss,fix:"Use WSL2 Ubuntu with Docker Desktop integration. Guide: docs/wsl2.md"},fs(),ms(),await hs()],M=null;try{M=await dt()}catch{}e.json?console.log(JSON.stringify(F(P,!!g(),[],M,{...r,canUp:!1}))):(de(P),console.log("")),console.error(Ss),process.exit(1);return}if(r.kind==="webcontainer"){let u=F([{name:"Host",didPass:!1,message:Ne,fix:`Use a machine with Docker. Guide: ${re}`}],!!g(),[],void 0,r);e.json&&console.log(JSON.stringify(u)),console.error(Ne),process.exit(1);return}e.json||(console.log(`
54
- ${h.bold("\u{1F50D} Flisk Doctor")}
55
- `),console.log(`Checking environment...
56
- `));let o=g(),i;if(e.fix){try{let u;if(o)try{u=mt(o).project.name}catch{}let P=o?ss(o):[],M=await Qt(u,void 0,{tiltPids:P,machine:e.scope==="machine"});i=await Zt(M,{dryRun:e.dryRun,confirm:e.yes||!e.json&&process.stdin.isTTY?async N=>e.yes||await ht({message:`Run \`${N.command}\` (${N.check}, ${N.reason})?`}):void 0})}catch(u){i=[{id:"plan-fixes",check:"Docker networks",scope:"project",command:"docker network ls",status:"failed",reason:`could not list Docker networks: ${(u instanceof Error?u.message:String(u)).split(/\r?\n/)[0]}`}]}e.json||Hr(i,e.dryRun)}let a,c;try{a=await te(o??process.cwd())}catch(u){a=null,c=u instanceof Error?u.message:String(u)}let l=[()=>a?{name:"Host Ports",didPass:!0,message:`HTTP ${a.ingressHttp}, HTTPS ${a.ingressHttps}, Postgres ${a.postgres}`}:{name:"Host Ports",didPass:!1,message:c??"Could not select host ports",fix:"Set FLISK_HTTP_PORT, FLISK_HTTPS_PORT, or FLISK_POSTGRES_PORT to available host ports."},()=>a?ve(gs,void 0,[a.ingressHttp,a.ingressHttps]):{name:"Ingress Ports",didPass:!1,message:"Ingress port plan is unavailable"},pr,ze,()=>Kt(),T("Docker OS",Ye),Xe,T("Docker Compose",J),T("Docker Versions",z),hr,()=>rr(g()??process.cwd(),e.strict),()=>ir(process.env,e.strict),T("Docker Network Pool",()=>$t()),()=>ts()],f=[()=>Sr(),kr,Dr,()=>xr(e.strict),cs,$r,()=>At(),Tr,Rr,yr,()=>St(),()=>se(),()=>Le(),()=>Me(),()=>He(),()=>We(void 0,e.strict),()=>yt(),()=>wt(),()=>Pt(),()=>xt(),()=>ne(),()=>kt(),()=>vt(),()=>bt(),()=>Dt(),gr,async()=>{let u=g()??process.cwd(),P=Ot(u)?await ft(u):!0;return be(gs,u,void 0,P)},T("Container Resource Health",Ar),T("Crash-looping containers",()=>{let u=g()??process.cwd(),P=[];try{P=we(u)}catch{return{name:"Crash-looping containers",didPass:!0,isSkipped:!0,message:"Could not read .env - skipped crash-loop check"}}return Ut(Z(),P)}),T("Tilt Resources",Ce)];if(e.ping){let u=o?pt(o,a?.ingressHttp):a?.ingressHttp;f.push(T("Service Health",()=>Ir(t,u))),f.push(T("Service Smoke",()=>Lr(t,u))),f.push(T("Advertised Endpoints",()=>Mr(t,u)))}let m=!!o,d=[],S=[],k,y=[];if(e.json||!e.all){let u=await rs(l,m?f:[]);k=u.checks,y=u.errors}else{let u=async(N,Is=!1)=>{let Ls=N.map(Y=>{let ge=async()=>{try{return await Y()}catch(ke){return y.push({code:"INTERNAL",message:ke instanceof Error?ke.message:String(ke)}),{name:Y.checkName??"Doctor check",didPass:!1,isSkipped:!1,message:"Check failed with an internal error"}}};return ge.requiresContainerRuntime=Y.requiresContainerRuntime,ge.checkName=Y.checkName,ge});return lr(Ls,{all:!!e.all,containerRuntimeFailed:Is})};d=await u(l);let P=d.some(N=>!N.didPass&&!N.isSkipped&&!N.isWarning),M=d.some(N=>N.name===v&&!N.didPass);m&&(e.all||!P)&&(S=await u(f,M)),k=[...d,...S]}if(Lt(r.kind))for(let u of k)u.name===v&&!u.didPass&&(u.fix=Ft);let p=F(Ps(k),m,y,a,r);i&&(p.data.actions=i),i&&es(i)&&(p.data.ready=!1);let j=os(p),U=p.data.ready;if(e.json){let u;try{u=we(g()??process.cwd())}catch(P){if(!(P instanceof rt))throw P;console.log(JSON.stringify(F([],m,[...y,{code:"ENV_UNREADABLE",message:P.message}]))),console.error(P.message),process.exit(1)}console.log(JSON.stringify(ot(p,u)));for(let P of y)console.error(Q(P.message,u));j!==0&&process.exit(j);return}for(let u of y)console.error(`${h.red("\u2717")} Doctor check failed: ${u.message}`);Wr(d,S,m,e,k);let Qe=ar(m,U);console.log(`${U?h.green(h.bold("\u2713")):h.red(h.bold("\u2717"))} ${Qe}`),j!==0&&process.exit(j)});function Ds(e){return me(e,L)}var X=`${L[0]}.${L[1]}+`;import{execFileSync as $s}from"node:child_process";import{accessSync as Cs,constants as Ns,readdirSync as Vr}from"node:fs";import{delimiter as Ur,join as V}from"node:path";var _="flisk",Ts="@flisk-sh/flisk",Gr="https://github.com/flisk-sh/flisk",Es="@tdk-landscape/tdk-cli-core";function Rs(e=_,t=process.env){let s=t.PATH??t.Path??"";for(let n of s.split(Ur)){if(!n)continue;let r=[V(n,e),V(n,`${e}.cmd`),V(n,`${e}.exe`)];for(let o of r)try{return Cs(o,Ns.X_OK),!0}catch{}}return!1}function Kr(e=process.env){let t=e.HOME??e.USERPROFILE??"";if(!t)return"flisk-workspace";let s=V(t,"Documents");try{return Cs(s,Ns.F_OK),V(s,"flisk")}catch{return V(t,"flisk")}}function qr(e){try{return Vr(e).every(t=>t.startsWith("."))}catch{return!0}}function Os(e=process.env,t=process.cwd()){if(!Rs(_,e))return`The \`${_}\` command is not on PATH (this run is not a global install). Install it with \`npm install -g ${Ts}\`, then run \`${_} project --yes\`. If npm reports EEXIST for \`flisk\`, the old package still owns that command: run \`npm uninstall -g ${Es}\` first. The current release is published from ${Gr}.`;if(qr(t))return`Next: ${_} project --yes`;let s=Kr(e);return`Next: mkdir -p ${s} && cd ${s} && ${_} project --yes
57
- (${_} project writes .flisk/ and the master configs into the current folder, so it starts clean in a folder of its own. Or clone a starter: ${_} project example)`}function aa(e=process.env){if(Rs(_,e))return"present";let t=e.npm_execpath?.includes("bun")?"bun":"npm",s=t==="bun"?"bun remove -g":"npm uninstall -g";try{$s(s,[Es],{stdio:"ignore",timeout:12e4})}catch{}try{return $s(t,["install","-g",Ts],{stdio:"ignore",timeout:3e5}),"installed"}catch{return"failed"}}var Xr=["docker","compose","tilt"],As=["node","bun","ports"],Fs=["node",...Xr,"bun","ports"];function Yr(e,t){return{id:e,ok:t.didPass||!!t.isSkipped,message:t.message,fix:t.fix}}function js(e){let[t=0,s=0]=String(e).split(".").map(n=>Number.parseInt(n,10));return!Number.isFinite(t)||!Number.isFinite(s)||t<22||t===22&&s<12}async function he(e,t,s){try{return Yr(e,await t())}catch{return{id:e,ok:!1,message:s}}}function Qr(e){return e.isSkipped?{...e,didPass:!1,isSkipped:!1,message:`Could not verify Docker Engine/Compose minimum versions: ${e.message}`,fix:e.fix??"Start Docker and ensure Docker Engine 25+ and Compose 2.20.2+ are installed."}:e}function Zr(e){try{return w(e).some(t=>t.config?.featuresEnabled?.includes("prisma")??!1)}catch{return!1}}async function eo(e={}){let t=e.cwd??process.cwd(),s=e.scope??"runtime",n=g(t),r=!!n,o=[],i=process.versions.node;if(o.push({id:"node",ok:!js(i),message:i,fix:js(i)?`Install Node.js 22.12+ (current: ${i}). https://nodejs.org`:void 0}),s==="runtime"){let p=W();p.isColima&&o.push({id:"colima",ok:p.running,message:p.running?`Colima VM is running${p.arch?` (${p.arch})`:""}`:p.problem??"The Colima VM is not running",fix:p.running?void 0:p.fix}),o.push(await he("docker",ze,"Docker daemon is not running")),o.push(await he("compose",async()=>{let[D,j]=await Promise.all([J(),z()]);return D.didPass?Qr(j):D},"Docker Engine or Compose version check failed")),o.push(await he("tilt",Xe,"Tilt CLI not found"))}let a=n?oe(w(n)):!0,c=!1,l=a?b("bun"):void 0;if(l)try{let p=_s(l,["--version"],{encoding:"utf-8",timeout:3e3}).trim();c=Ds(p)}catch{}o.push(a?{id:"bun",ok:c,message:c?`Bun ${X} is available on PATH`:`Bun ${X} not found on PATH`,fix:c?void 0:"curl -fsSL https://bun.sh/install | bash"}:{id:"bun",ok:!0,message:"Bun not needed: no generated JS services"});try{let p=await te(n??t,s==="scaffold"?{inspectDocker:!1}:{});o.push({id:"ports",ok:!0,message:`HTTP ${p.ingressHttp}, HTTPS ${p.ingressHttps}, Postgres ${p.postgres}`})}catch(p){o.push({id:"ports",ok:!1,message:p instanceof Error?p.message:String(p),fix:"Set FLISK_HTTP_PORT, FLISK_HTTPS_PORT, or FLISK_POSTGRES_PORT to available host ports."})}if(n&&s==="runtime"){let p=await he("nats",()=>ne(n),"NATS broker check failed");if(p.ok||o.push(p),Zr(n)){let D=!1;try{let j=JSON.parse(Jr(zr(n,".flisk",H),"utf-8")),U=String(j?.project?.name??"").replace(/-/g,"_");U&&(D=_s(b("docker")??"docker",["ps","--filter",`label=com.docker.compose.project=${U}`,"--filter","label=com.docker.compose.service=postgres","--filter","health=healthy","--format","{{.Names}}"],{encoding:"utf-8",timeout:3e3}).trim().length>0)}catch{}D||o.push({id:"prisma",ok:!1,message:"A Prisma resource is configured, but the project Postgres service is not healthy",fix:"Run `flisk up` to start the bundled Postgres, then check `flisk doctor`."})}}let f=o.filter(p=>!p.ok).slice(0,8),m=s==="scaffold"?As:Fs,d=f.some(p=>m.includes(p.id)),S=f.some(p=>p.id==="bun"),k=f.length?d?s==="scaffold"?S?`Project setup blocked. Bun ${X} must be installed on PATH.
58
- Fix the machine checks below.`:"Project setup blocked. Fix the machine checks below.":S?`Cold start blocked. Bun ${X} must be installed on PATH.
59
- Prisma/NATS configuration is project-specific and generated after \`flisk project\`. Fix the machine checks below.`:"Cold start blocked. Bun/Prisma/NATS are not the first failure.\nThey are generated after `flisk project`. Fix the machine checks below.":"Machine is ready. Project wiring is not.":"",y=f.length?"":r?"Environment ready for Flisk":`This machine is ready for Flisk.
60
- ${Os()}`;return{ok:f.length===0,inProject:r,items:f,header:k,footer:y}}function to(e,t={}){let s=[e.header];for(let n of e.items)s.push(`FAIL ${n.id.padEnd(7)} ${n.message}`),n.fix&&s.push(` ${n.fix}`);return e.footer&&(t.includeSuccessFooter??!0)&&s.push(e.footer),s.filter(Boolean).join(`
61
- `)}async function ba(e={}){let t=e.scope??"runtime",s=await eo({scope:t}),n=t==="scaffold"?As:Fs;s.items.some(o=>n.includes(o.id))&&(console.error(to(s)),process.exit(1))}function va(e=b){return{hasDockerCli:!!e("docker"),hasTiltCli:!!e("tilt")}}function Da(e){let t=[];return e.hasDockerCli||t.push(`Docker is not installed. ${E.dockerMissing}`),e.hasTiltCli||t.push(`Tilt is not installed. ${E.tiltMissing}`),t.push("Docker and Tilt are only needed to start the stack: start them, then run `flisk doctor` (readiness) and `flisk up`."),t}export{Ln as a,Mn as b,Bo as c,Ne as d,Ft as e,It as f,Lt as g,Te as h,ie as i,lo as j,xo as k,Po as l,bo as m,je as n,mn as o,Sn as p,No as q,us as r,li as s,ui as t,Br as u,Es as v,aa as w,Xr as x,As as y,Fs as z,js as A,Qr as B,eo as C,to as D,ba as E,va as F,Da as G};
@@ -1,17 +0,0 @@
1
- import{createRequire as __fliskRequire}from'node:module';const require=__fliskRequire(import.meta.url);
2
- import{N as q,b as f,e as G,h as xe,i as Ee,m as ke,n as we,r as z,u as ve,v as be,x as T}from"./chunk-YNCBBJ4V.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 K={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 H(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 K[H(e)].color}function xn(e){return K[H(e)].icon}function En(e,t){return K[H(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 J=62;function Tn(e="\u2500",t=J){return e.repeat(t)}function Ae(e,t=J-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=J){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 Ge}from"node:fs";import{join as _}from"node:path";function A(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 Z,readFileSync as Ce,writeFileSync as De}from"node:fs";import{join as Q}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&&lt(o,r),$t(o,t,"utf-8"),mt(o)}finally{dt(o)}gt(n,e)}catch(o){throw St(n,{force:!0}),o}}var b=[{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"),Gn=b.filter(e=>e.complete).map(e=>e.name);function Y(e,t){return e==="DB_PASSWORD"?t:e==="JWT_SECRET"?wt():e==="DATABASE_URL"?`postgresql://postgres:${t}@postgres:5432/app_dev`:b.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 b)t.push(...Ne(n)),t.push(`${n.name}=${Y(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 zn(e){let t=Q(e,".env"),n={missing:[],invalid:[],warnings:[]};if(!Z(t))return{missing:b.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 b)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=Q(e,".env");if(!Z(t))return De(t,je(),{mode:384}),b.map(u=>u.name);let n=Ce(t,"utf-8"),r=R(n),o=b.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,Y(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}=${Y(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=Q(e,".env");return Z(t)?!1:(De(t,je(),{mode:384}),!0)}var Ot="[REDACTED]",X=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 X(e,n instanceof Error?n.message:String(n))}return[...R(t).values()].filter(n=>n.length>=Ct)}function Me(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"?Me(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 Be(e,t=[]){let n;try{n=Dt(e,t)}catch(r){return()=>{throw r}}return r=>Me(r,n)}import{readFileSync as Ft}from"node:fs";import{join as Nt}from"node:path";function or(){return Ue(T())}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 ee(e,t){let n=Ue(t??T()),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 te(e){if(A(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){return Le(e,t,n=>jt(n))}function ar(e,t){return Le(e,t,n=>n)}function Le(e,t,n){let{appBase:r,apiBase:o}=ee(t);return e.filter(s=>s.config?.appType==="frontend"||A(s.config?.appType)).map(s=>{let a=A(s.config.appType)?o:r;return{name:s.name,appType:s.config.appType,url:`${a}${n(te(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 ir(e,t){return Promise.all(e.map(n=>Pt(n,t)))}var Ye=60,ze=1e3,qe=1e4,Ke=200,He=64*1024,Je=new Set([404,502,503,504]),Mt=new Set(["ECONNREFUSED","ENOTFOUND","EAI_AGAIN","EHOSTUNREACH","ENETUNREACH"]),Bt=new Set(["GET","HEAD","OPTIONS"]);function E(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function Ut(e,t){if(Bt.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"&&Mt.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 hr(e,t){let{appBase:n,apiBase:r}=ee(t),o=[];for(let s of e){let a=s.config;if(!a?.smoke)continue;let i=a.appType,c=A(i)||i===G;if(!c&&i!=="frontend"||a.exposeViaProxy===!1)continue;let d=c?r:n,p=a.healthCheckPath||(c&&i!==G?"/health":void 0);o.push({name:s.name,baseUrl:`${d}${te(s).replace(/\/+$/,"")}`,...p?.startsWith("/")?{readyPath:p}:{},smoke:a.smoke})}return o}var Lt=10;function gr(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 M(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 Gt(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}):Ge(i,r),Ge(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 zt(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(ze)}}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,L;for(;;){x=void 0,L=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;L=typeof v=="string"?v:void 0}if(!(x!==void 0&&g||S!==void 0&&S!==C&&Je.has(S))||r()>=a)break;await o(ze)}let ge=!1,V=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}}},W=V(me),y=S===void 0?void 0:V(D),Se=x===void 0?void 0:V(x),ye=x===void 0?void 0:L??Se,F=`${e.name}: ${h}: ${l} ${W??"(url withheld)"}`,$e;if(t.recordDir){let g=y===void 0?void 0:zt(y),$=x===void 0&&S===C&&(u.bodyContains===void 0||D.includes(u.bodyContains));$e=Gt(t.recordDir,Wt(d,h),{service:e.name,step:h,method:l,...W===void 0?{}:{url:W},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?`: ${M(y)}`:""}`);if(u.bodyContains!==void 0&&!D.includes(u.bodyContains))return N(`${F} returned ${S} but the body does not contain "${u.bodyContains}"${y?`: ${M(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?`: ${M(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?`: ${M(y)}`:""}`);i[$]=String(v)}}}return{name:e.name,ok:!0}}function Sr(e){return`Smoke check failed: ${e.failure}${e.recordPath?` (record: ${e.recordPath})`:""}`}function yr(e,t,n={}){return{redact:Be(e,n.envFiles??[]),...t?{warn:t}:{}}}async function $r(e,t={}){return Promise.all(e.map(n=>qt(n,t)))}var Xe=1,wr="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"}];function B(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(...B(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 vr(e,t){if(!et(e))return[];try{let n=JSON.parse(tt(e,"utf-8"));return!n||typeof n!="object"||Array.isArray(n)?[]:B(n,t)}catch{return[]}}function br(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 _r(e){let t=e==="resource"?en:tn;return n=>{let r=t(n);return r.valid?!0:r.error??"Invalid name"}}function Or(e){return ie(z,e)?{valid:!0}:{valid:!1,error:`Invalid service. Must be one of: ${z.join(", ")}`}}function Cr(e){return Number.isInteger(e)&&e>0&&e<=65535}function Dr(e){return!e.includes("\0")&&!/[<>:"|?*]/.test(e)}function Fr(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 zr(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 qr(){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=B(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 U(){let e=T();if(!e)throw q.notInProject();return it(e)}function Kr(e){let t=e;if(!t){let n=T();if(!n)throw q.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 Hr(e,t){let n=t??it(e);return pn(n)}function pn(e){let t=e||U(),n=new Set;for(let r of t)r.stack&&n.add(r.stack);return Array.from(n).sort()}function Jr(e=U()){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 Yr(e,t){return(t??U()).filter(r=>r.stack===e)}function Zr(e,t){return(t??U()).some(r=>r.stack===e)}var le=5e3,fe=new j({ttlMs:le}),pe=new j({ttlMs:le}),I=Te(le);function Qr(){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 Xr(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,J 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,A as t,_e as u,Gn as v,R as w,zn as x,qn as y,Kn as z,X as A,Dt as B,Me as C,Ie as D,or as E,ee as F,jt as G,te as H,sr as I,ar as J,ir as K,hr as L,gr as M,Sr as N,yr as O,$r as P,Xe as Q,wr as R,ae as S,vr as T,br as U,nt as V,en as W,tn as X,_r as Y,Or as Z,Cr as _,Dr as $,Fr as aa,ie as ba,zr as ca,qr as da,at as ea,fn as fa,it as ga,U as ha,Kr as ia,Hr as ja,pn as ka,Jr as la,Yr as ma,Zr as na,Qr as oa,ln as pa,Xr as qa};
@@ -1,35 +0,0 @@
1
- import{createRequire as __fliskRequire}from'node:module';const require=__fliskRequire(import.meta.url);
2
- import{a as C}from"./chunk-YL56J2WX.js";import{b as N,c as de,d as V,o as D,p as Y,t as J,u as L,w as O}from"./chunk-MWOVYIBB.js";import{$ as ie,ea as ae,fa as se,h as T,ha as ce,i as B,ja as le,ka as fe,la as pe,o as h,p as b,q as t,x as re,y as G,z as W}from"./chunk-5F7YP5IR.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-YNCBBJ4V.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 Oe}from"node:url";import{Command as Ce}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 p=I.get(l);if(p)return p}let{resources:s,problems:f}=o?ae(o):{resources:ce(),problems:[]},u=pe(s),n=fe(s);se(f);let r=s.filter(p=>!p.stack),d=new Map;for(let p of s)if(p.stack){let a=d.get(p.stack)||[];a.push(p),d.set(p.stack,a)}let m={resources:s,problems:f,stacks:u,stackNames:n,unassignedResources:r,resourcesByStack:d};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(f=>!l.has(f));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),f=Q(s,H(e,o));return f.length>0&&N(l,s),f}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||(C[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 f=C[e];if(!f){console.log(g.red(`
4
- \u274C Unknown template: "${e}"`)),console.log(g.yellow(`
5
- Available templates:`));for(let[r,d]of Object.entries(C))console.log(` ${g.cyan(r)} ${g.gray(`- ${d.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(`${f.description}
6
- `,0),t(`Source: ${f.bundledPath?"bundled Flisk example":f.repo}`,0),t(`Destination: ${n}
7
- `,0));try{if(f.bundledPath){let r=q(Pe(Oe(new URL("../dist/commands/project.js",import.meta.url).href)),"../../../");je(q(r,f.bundledPath),n,{recursive:!0});let d=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 p=_(n,m),a=K(p,"utf-8");we(p,a.replaceAll("flisk_example",d))}}else Se("git",["clone",f.repo,n],{stdio:["ignore","pipe","pipe"],encoding:"utf-8"})}catch(r){let d=r instanceof Error?r.message:String(r),m=f.bundledPath?`Bundled example copy failed (${ge(d)}). Reinstall the Flisk CLI package and try again.`:`git clone failed: ${ge(d)}. 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
- \u2705 Cloned "${e}" into ${u}/`)),t(`
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 Ce("project")).description("Initialize or validate project-level master configuration").argument("[template]",`Clone a starter example instead of a blank project (${Object.keys(C).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:f}=await import("./cold-preflight-ZOPR7PWJ.js");await l({scope:"scaffold"});let u=f(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
- `),t(`Location: ${r}
12
- `,0)));let d=_(r,".flisk"),m=_(d,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(d)||(de(d),n||h("Created: .flisk/ directory"));let p=S(m);if(p&&!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
- \u{1F4CB} Regenerating master configuration files...
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
- \u2705 Project configuration regenerated!`)),t(`
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(p&&o.force&&!o.yes&&(console.log(g.red(`
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 O({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 O({message:"Select Alpha enabled stacks (core business):",choices:[{title:"api (Backend API)",value:"api"},{title:"app (Frontend app)",value:"app"}]}),y=await O({message:"Select Beta enabled stacks (extended features):",choices:[{title:"worker (Background jobs)",value:"worker"},{title:"migrator (Database migrations)",value:"migrator"}]}),w=await O({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
- \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
- `);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
- \u{1F4CB} Creating project configuration...
24
- `),N(m,a),n||(h("Created: .flisk/project.json"),t(`\u2192 Project: ${a.project.name}`),b(`
25
- \u{1F4CB} Generating master configuration files...
26
- `)),await Y(r,{quiet:n}),n||b(`
27
- \u{1F527} Setting up environment variables...
28
- `),W(r))n||h("Created: .env (the defaults work as-is)");else{let c=G(r);c.length>0?n||h(`Updated: .env (added ${c.join(", ")}; existing values unchanged)`):n||h("Found: .env (environment already configured)")}let ee=re(r);if(ee.missing.length>0){console.log(g.yellow(`
29
- \u26A0\uFE0F Missing required environment variables:`));for(let c of ee.missing)t(`- ${c}`,1);t("\u2192 Edit .env and fill in these values before running `flisk up`",1)}let te=V(r);if(te.length>0&&!n&&h(`Added to .gitignore: ${te.join(", ")}`),!n){console.log(g.green(`
30
- \u2705 Project configuration complete!`)),t(`
31
- Generated files in .flisk/.flisk-out/:`,0);for(let c of P)t(`- ${c}`);t(`
32
- Environment configuration:`,0),t("- .env (contains all required variables)"),t(`
33
- Source file:`,0),t("- .flisk/project.json (edit this to change project structure)"),t(`
34
- Next steps:`,0),t("1. flisk resource api --type backend --stack app # scaffold a service"),t("2. flisk up app # start it"),t(`
35
- Runtime readiness:`,0);for(let c of u)t(c,0);t("\nAfter editing .flisk/project.json, run `flisk config regenerate`.",0)}})});export{me as a,Be as b,ue as c,Le as d,Ie as e,qe as f,gt as g,Re as h,Fe as i,ht as j};
@@ -1,3 +0,0 @@
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 P}from"node:os";import{join as a}from"node:path";import{spawnSync as _}from"node:child_process";import{existsSync as N,mkdirSync as w,mkdtempSync as C,readdirSync as $,renameSync as L,rmSync as v,statSync as j,writeFileSync as O}from"node:fs";import{tmpdir as D}from"node:os";import{join as c}from"node:path";function S(e,n){let r=C(c(D(),"flisk-ext-")),o=c(r,"bundle.tar.gz");O(o,e),w(n,{recursive:!0});let t=process.platform==="win32"?"tar.exe":"tar",i=_(t,["xzf",o,"-C",n,"--strip-components=1"],{stdio:"pipe",windowsHide:!0});if(i.status!==0){let s=c(r,"fallback");w(s,{recursive:!0});let f=_(t,["xzf",o,"-C",s],{stdio:"pipe",windowsHide:!0});if(f.status!==0)throw v(r,{recursive:!0,force:!0}),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&&N(c(s,u))&&j(c(s,u)).isDirectory()?c(s,u):s;for(let d of $(m))L(c(m,d),c(n,d))}v(r,{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,k={"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 I(){return process.env.FLISK_LICENSE_KEY?.trim()||null}function R(){return process.env.FLISK_PREMIUM_ENDPOINT?.trim()||U}function x(e){let n=a(e,".flisk",".project-id");if(p(n))return y(n,"utf-8").trim();let r=F();return g(a(e,".flisk"),{recursive:!0}),h(n,`${r}
3
- `),r}function B(e){let n=e.replace(/[^a-zA-Z0-9_-]/g,"_");return a(P(),".flisk","cache",`premium-${n}`,"premium.tar.gz")}function T(e){return p(e)?Date.now()-M(e).mtimeMs<z:!1}async function E(e,n,r){let o=`${R()}?resource=${encodeURIComponent(n)}&projectId=${encodeURIComponent(r)}`,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,n){let r=[];for(let o of A){let t;try{t=await E(e,o,n)}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=${r.join(", ")||"none"}`),console.info(`\u{1F50E} Premium bundle download: ${i.byteLength} bytes`),i}if(t.status===401||t.status===403){r.push(`${o}=${t.status}`);continue}return console.warn(`\u26A0\uFE0F Premium fetch failed: ${o} returned HTTP ${t.status}; prior entitlement probes: ${r.join(", ")||"none"}`),null}return console.warn(`\u26A0\uFE0F Premium entitlement probes denied: ${r.join(", ")}`),null}async function re(e,n){let r=I();if(!r)return!1;let o=B(r);if(T(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=x(e),i=await H(r,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}),S(y(o),t);let i=0,s=[];for(let[l,u]of Object.entries(k)){let m=a(t,l);if(!p(m)){s.push(l);continue}let d=a(n,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(k).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,n){let r=e.replace(/[^a-zA-Z0-9_-]/g,"_");return a(P(),".flisk","cache",`premium-${r}`,`${n}-access.json`)}async function b(e,n){let r=I();if(!r)return!1;let o=K(r,n);if(T(o))try{let s=JSON.parse(y(o,"utf-8"));return console.info(`\u{1F50E} ${n} entitlement probe: cache=${s.granted===!0?"granted":"denied"}`),s.granted===!0}catch{}let t=x(e),i=!1;try{let s=await E(r,n,t);i=s.ok,console.info(`\u{1F50E} ${n} entitlement probe: HTTP ${s.status} (${i?"granted":"denied"})`),s.body&&await s.arrayBuffer().catch(()=>{})}catch(s){return console.warn(`\u26A0\uFE0F ${n} 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{S as a,re as b,ne as c,oe as d,G as e};